MetaLlamaStyles.stylex",[],(function(t,n,r,o,a,i,l){"use strict";var e={metaBlue:"#0064E0",white:"#FFFFFF",blue500:"#47A5FA",blue600:"#0082FB",blue550:"#5AB8FE",blue800:"#004BB9",gray100:"#F1F4F7",gray150:"#E6EBEF",gray200:"#DEE3E9",gray250:"#D6DDE4",gray300:"#CBD2D9",gray400:"#A7B3BF",gray600:"#67788A",gray700:"#465A69",gray800:"#344854",gray900:"#283943",gray1000:"#1C2B33",gray1050:"#152127",gray1100:"#0F191E",gray1200:"#0A1317",green600:"#28D232",teal600:"#00D2BE",duration200ms:"200ms",cubicBezier4011:"cubic-bezier(.4,0,1,1)"},s={animationDuration:"var(--x1weifbk)",animationTiming:"var(--x14hvzkc)",__varGroupHash__:"xmopozj"},u={accordionIcon:"var(--x6r2aoj)",backgroundHeavy:"var(--x1cix6a3)",backgroundSurface1:"var(--xxqu37j)",backgroundSurface2:"var(--xz1cps4)",backgroundSurface3:"var(--x1uqmqip)",bannerBackground:"var(--x13xh4bl)",bannerColor:"var(--x14y9034)",borderHeavy:"var(--x18jq40y)",borderLight:"var(--x1ui6vop)",hoverWrapperBackground:"var(--xtopsle)",hoverWrapperColor:"var(--x1w7l2c6)",interactiveDisabled:"var(--x1j0o1lg)",interactiveHover:"var(--xb9oy6o)",interactivePressed:"var(--xgq499o)",interactivePrimary:"var(--x14w6lj)",textDisabled:"var(--x13f2gul)",textPrimary:"var(--xsqyi4y)",textSecondary:"var(--x125rdix)",__varGroupHash__:"x1dw1mgp"},c={accordionIcon:"var(--x13i12v9)",backgroundHeavy:"var(--xh62zee)",backgroundSurface1:"var(--x4q3eum)",backgroundSurface2:"var(--x11wfri4)",backgroundSurface3:"var(--xagq3df)",bannerBackground:"var(--x45zfrs)",bannerColor:"var(--xgyo8xs)",borderHeavy:"var(--x1aiv0oh)",borderLight:"var(--xu2fda)",hoverWrapperBackground:"var(--xuoq941)",hoverWrapperColor:"var(--x1m5sfx6)",interactiveDisabled:"var(--x1bh52m)",interactiveHover:"var(--x1n95m3h)",interactivePressed:"var(--xd2zy2m)",interactivePrimary:"var(--x1bvtcs1)",textDisabled:"var(--x1jg916q)",textPrimary:"var(--x7ltdba)",textSecondary:"var(--x1m7q81a)",__varGroupHash__:"x1bdozbk"};l.commonVars=s,l.lightMode=u,l.darkMode=c}),98);
-----
StdlibWebBloksActions",["WebBloksAnimatedAddOnCompleteListener","WebBloksAnimatedBuild","WebBloksAnimatedCancel","WebBloksAnimatedCancelToken","WebBloksAnimatedCreate","WebBloksAnimatedCreateColor","WebBloksAnimatedCreateCubicBezier","WebBloksAnimatedCreateDimension","WebBloksAnimatedDestroy","WebBloksAnimatedGetCurrentColorValue","WebBloksAnimatedGetCurrentDimensionValue","WebBloksAnimatedGetCurrentPlayTime","WebBloksAnimatedGetCurrentValue","WebBloksAnimatedGetTotalDuration","WebBloksAnimatedIsInitialized","WebBloksAnimatedLoop","WebBloksAnimatedParallel","WebBloksAnimatedPause","WebBloksAnimatedResume","WebBloksAnimatedSequence","WebBloksAnimatedSetCurrentPlayTime","WebBloksAnimatedStagger","WebBloksAnimatedStart","WebBloksAnimatedStartToken","WebBloksCollectionSetIndex","WebBloksDummy","WebBloksScreenClose","WebBloksScreenOpen","WebBloksTooltipHide","WebBloksTooltipShow"],(function(a,b,c,d,e,f,g){a={"bk.action.animated.AddOnCompleteListener":c("WebBloksAnimatedAddOnCompleteListener"),"bk.action.animated.Build":c("WebBloksAnimatedBuild"),"bk.action.animated.Cancel":c("WebBloksAnimatedCancel"),"bk.action.animated.CancelToken":c("WebBloksAnimatedCancelToken"),"bk.action.animated.CancelWithToken":c("WebBloksDummy"),"bk.action.animated.Create":c("WebBloksAnimatedCreate"),"bk.action.animated.CreateColor":c("WebBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("WebBloksAnimatedCreateDimension"),"bk.action.animated.Destroy":c("WebBloksAnimatedDestroy"),"bk.action.animated.GetCurrentColorValue":c("WebBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("WebBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentPlayTime":c("WebBloksAnimatedGetCurrentPlayTime"),"bk.action.animated.GetCurrentValue":c("WebBloksAnimatedGetCurrentValue"),"bk.action.animated.GetTotalDuration":c("WebBloksAnimatedGetTotalDuration"),"bk.action.animated.IsInitialized":c("WebBloksAnimatedIsInitialized"),"bk.action.animated.Loop":c("WebBloksAnimatedLoop"),"bk.action.animated.Parallel":c("WebBloksAnimatedParallel"),"bk.action.animated.Pause":c("WebBloksAnimatedPause"),"bk.action.animated.Resume":c("WebBloksAnimatedResume"),"bk.action.animated.Sequence":c("WebBloksAnimatedSequence"),"bk.action.animated.SetCurrentPlayTime":c("WebBloksAnimatedSetCurrentPlayTime"),"bk.action.animated.Stagger":c("WebBloksAnimatedStagger"),"bk.action.animated.Start":c("WebBloksAnimatedStart"),"bk.action.animated.StartToken":c("WebBloksAnimatedStartToken"),"bk.action.animated.StartWithToken":c("WebBloksDummy"),"bk.action.animated.easing.CreateCubicBezier":c("WebBloksAnimatedCreateCubicBezier"),"bk.action.collection.SetIndex":c("WebBloksCollectionSetIndex"),"bk.action.screen.Close":c("WebBloksScreenClose"),"bk.action.screen.Open":c("WebBloksScreenOpen"),"bk.action.tooltip.Hide":c("WebBloksTooltipHide"),"bk.action.tooltip.Show":c("WebBloksTooltipShow")};g.ACTIONS=a}),98);
-----
FDSProgressRingUtils",["ix"],(function(a,b,c,d,e,f,g,h){"use strict";function a(a,b,c,d){function e(a,b){return 1-3*b+3*a}function f(a,b){return 3*b-6*a}function g(a){return 3*a}function h(a,b,c){return((e(b,c)*a+f(b,c))*a+g(b))*a}function i(a,b,c){return 3*e(b,c)*a*a+2*f(b,c)*a+g(b)}function j(b){var d=b;for(var e=0;e<4;++e){var f=i(d,a,c);if(f===0)return d;var g=h(d,a,c)-b;d-=g/f}return d}return function(e){return a===b&&c===d?e:h(j(e),b,d)}}function b(a,b,c){switch(b){case"12":switch(c){case"dark":switch(a){case"blue":return h("1876411");case"disabled":return h("1876443");case"dark":return h("1876427");case"light":return h("1876427");default:return h("1876427")}case"light":switch(a){case"blue":return h("1876419");case"disabled":return h("1876451");case"dark":return h("1876435");case"light":return h("1876427");default:return h("1876435")}default:return h("1876435")}case"16":switch(c){case"dark":switch(a){case"blue":return h("1876412");case"disabled":return h("1876444");case"dark":return h("1876428");case"light":return h("1876428");default:return h("1876428")}case"light":switch(a){case"blue":return h("1876420");case"disabled":return h("1876452");case"dark":return h("1876436");case"light":return h("1876428");default:return h("1876436")}default:return h("1876436")}case"20":switch(c){case"dark":switch(a){case"blue":return h("1876413");case"disabled":return h("1876445");case"dark":return h("1876429");case"light":return h("1876429");default:return h("1876429")}case"light":switch(a){case"blue":return h("1876421");case"disabled":return h("1876453");case"dark":return h("1876437");case"light":return h("1876429");default:return h("1876437")}default:return h("1876437")}case"24":switch(c){case"dark":switch(a){case"blue":return h("1876414");case"disabled":return h("1876446");case"dark":return h("1876430");case"light":return h("1876430");default:return h("1876430")}case"light":switch(a){case"blue":return h("1876422");case"disabled":return h("1876454");case"dark":return h("1876438");case"light":return h("1876430");default:return h("1876438")}default:return h("1876438")}case"32":switch(c){case"dark":switch(a){case"blue":return h("1876415");case"disabled":return h("1876447");case"dark":return h("1876431");case"light":return h("1876431");default:return h("1876431")}case"light":switch(a){case"blue":return h("1876423");case"disabled":return h("1876455");case"dark":return h("1876439");case"light":return h("1876431");default:return h("1876439")}default:return h("1876439")}case"48":switch(c){case"dark":switch(a){case"blue":return h("1876416");case"disabled":return h("1876448");case"dark":return h("1876432");case"light":return h("1876432");default:return h("1876432")}case"light":switch(a){case"blue":return h("1876424");case"disabled":return h("1876456");case"dark":return h("1876440");case"light":return h("1876432");default:return h("1876440")}default:return h("1876440")}case"60":switch(c){case"dark":switch(a){case"blue":return h("1940508");case"disabled":return h("1940512");case"dark":return h("1940510");case"light":return h("1940510");default:return h("1940510")}case"light":switch(a){case"blue":return h("1940509");case"disabled":return h("1940513");case"dark":return h("1940511");case"light":return h("1940510");default:return h("1940511")}default:return h("1940511")}case"72":switch(c){case"dark":switch(a){case"blue":return h("1876418");case"disabled":return h("1876450");case"dark":return h("1876434");case"light":return h("1876434");default:return h("1876434")}case"light":switch(a){case"blue":return h("1876426");case"disabled":return h("1876458");case"dark":return h("1876442");case"light":return h("1876434");default:return h("1876442")}default:return h("1876442")}default:return h("1876439")}}function c(a){switch(a){case"dark":return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"};case"light":return{backgroundColor:"var(--progress-ring-on-media-background)",foregroundColor:"var(--progress-ring-on-media-foreground)"};case"blue":return{backgroundColor:"var(--progress-ring-blue-background)",foregroundColor:"var(--progress-ring-blue-foreground)"};case"disabled":return{backgroundColor:"var(--progress-ring-disabled-background)",foregroundColor:"var(--progress-ring-disabled-foreground)"};default:return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"}}}g.getCubicBezierPercentageFunc=a;g.getRingGifUrl=b;g.getRingColor=c}),98);/*FB_PKG_DELIM*/
-----
konva-7.2.3",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g};function i(){Object.defineProperty(g,"__esModule",{value:!0});g._registerNode=g._NODES_REGISTRY=g.Konva=g.glob=g._parseUA=void 0;var b=Math.PI/180;function c(){return typeof window!=="undefined"&&({}.toString.call(window)==="[object Window]"||{}.toString.call(window)==="[object global]")}var d=function(a){var b=a.indexOf("msie ");if(b>0)return parseInt(a.substring(b+5,a.indexOf(".",b)),10);b=a.indexOf("trident/");if(b>0){b=a.indexOf("rv:");return parseInt(a.substring(b+3,a.indexOf(".",b)),10)}b=a.indexOf("edge/");return b>0?parseInt(a.substring(b+5,a.indexOf(".",b)),10):!1},e=function(a){var b=a.toLowerCase(),c=/(chrome)[ /]([w.]+)/.exec(b)||/(webkit)[ /]([w.]+)/.exec(b)||/(opera)(?:.*version|)[ /]([w.]+)/.exec(b)||/(msie) ([w.]+)/.exec(b)||b.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([w.]+)|)/.exec(b)||[],e=!!a.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);a=!!a.match(/IEMobile/i);return{browser:c[1]||"",version:c[2]||"0",isIE:d(b),mobile:e,ieMobile:a}};g._parseUA=e;g.glob=typeof a!=="undefined"?a:typeof window!=="undefined"?window:typeof WorkerGlobalScope!=="undefined"?self:{};g.Konva={_global:g.glob,version:"7.2.3",isBrowser:c(),isUnminified:/param/.test(function(a){}.toString()),dblClickWindow:400,getAngle:function(a){return g.Konva.angleDeg?a*b:a},enableTrace:!1,_pointerEventsEnabled:!1,hitOnDragEnabled:!1,captureTouchEventsEnabled:!1,listenClickTap:!1,inDblClickWindow:!1,pixelRatio:void 0,dragDistance:3,angleDeg:!0,showWarnings:!0,dragButtons:[0,1],isDragging:function(){return g.Konva.DD.isDragging},isDragReady:function(){return!!g.Konva.DD.node},UA:g._parseUA(g.glob.navigator&&g.glob.navigator.userAgent||""),document:g.glob.document,_injectGlobal:function(a){g.glob.Konva=a},_parseUA:g._parseUA};g._NODES_REGISTRY={};e=function(a){g._NODES_REGISTRY[a.prototype.getClassName()]=a,g.Konva[a.prototype.getClassName()]=a};g._registerNode=e}var j=!1;function k(){j||(j=!0,i());return h.exports}var l={},m={exports:l};function n(){Object.defineProperty(l,"__esModule",{value:!0});l.Util=l.Transform=l.Collection=void 0;var a=k(),b=function(){function a(){}a.toCollection=function(b){var c=new a(),d=b.length,e;for(e=0;e0?Math.acos(a/f):-Math.acos(a/f);e.scaleX=f;e.scaleY=g/f;e.skewX=(a*c+b*d)/g;e.skewY=0}else if(c!=0||d!=0){f=Math.sqrt(c*c+d*d);e.rotation=Math.PI/2-(d>0?Math.acos(-c/f):-Math.acos(c/f));e.scaleX=g/f;e.scaleY=f;e.skewX=0;e.skewY=(a*c+b*d)/g}e.rotation=l.Util._getRotation(e.rotation);return e};return a}();l.Transform=b;var c="[object Array]",d="[object Number]",e="[object String]",f="[object Boolean]",g=Math.PI/180,h=180/Math.PI,i="#",j="",m="0";b="Konva warning: ";var n="Konva error: ",o="rgb(",p={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,132,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,255,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,203],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[119,128,144],slategrey:[119,128,144],snow:[255,255,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],transparent:[255,255,255,0],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,5]},q=/rgb((d{1,3}),(d{1,3}),(d{1,3}))/,r=[];l.Util={_isElement:function(a){return!!(a&&a.nodeType==1)},_isFunction:function(a){return!!(a&&a.constructor&&a.call&&a.apply)},_isPlainObject:function(a){return!!a&&a.constructor===Object},_isArray:function(a){return Object.prototype.toString.call(a)===c},_isNumber:function(a){return Object.prototype.toString.call(a)===d&&!isNaN(a)&&isFinite(a)},_isString:function(a){return Object.prototype.toString.call(a)===e},_isBoolean:function(a){return Object.prototype.toString.call(a)===f},isObject:function(a){return a instanceof Object},isValidSelector:function(a){if(typeof a!=="string")return!1;a=a[0];return a==="#"||a==="."||a===a.toUpperCase()},_sign:function(a){if(a===0)return 1;if(a>0)return 1;else return-1},requestAnimFrame:function(a){r.push(a),r.length===1&&requestAnimationFrame(function(){var a=r;r=[];a.forEach(function(a){a()})})},createCanvasElement:function(){var a=document.createElement("canvas");try{a.style=a.style||{}}catch(a){}return a},createImageElement:function(){return document.createElement("img")},_isInDocument:function(a){while(a=a.parentNode)if(a==document)return!0;return!1},_simplifyArray:function(a){var b=[],c=a.length,d=l.Util,e,f;for(e=0;e>16&255,g:a>>8&255,b:a&255}},getRandomColor:function(){var a=(Math.random()*16777215<<0).toString(16);while(a.length<6)a=m+a;return i+a},get:function(a,b){if(a===void 0)return b;else return a},getRGB:function(a){var b;if(a in p){b=p[a];return{r:b[0],g:b[1],b:b[2]}}else if(a[0]===i)return this._hexToRgb(a.substring(1));else if(a.substr(0,4)===o){b=q.exec(a.replace(/ /g,""));return{r:parseInt(b[1],10),g:parseInt(b[2],10),b:parseInt(b[3],10)}}else return{r:0,g:0,b:0}},colorToRGBA:function(a){a=a||"black";return l.Util._namedColorToRBA(a)||l.Util._hex3ColorToRGBA(a)||l.Util._hex6ColorToRGBA(a)||l.Util._rgbColorToRGBA(a)||l.Util._rgbaColorToRGBA(a)||l.Util._hslColorToRGBA(a)},_namedColorToRBA:function(a){a=p[a.toLowerCase()];return!a?null:{r:a[0],g:a[1],b:a[2],a:1}},_rgbColorToRGBA:function(a){if(a.indexOf("rgb(")===0){a=a.match(/rgb(([^)]+))/)[1];a=a.split(/ *, */).map(Number);return{r:a[0],g:a[1],b:a[2],a:1}}},_rgbaColorToRGBA:function(a){if(a.indexOf("rgba(")===0){a=a.match(/rgba(([^)]+))/)[1];a=a.split(/ *, */).map(Number);return{r:a[0],g:a[1],b:a[2],a:a[3]}}},_hex6ColorToRGBA:function(a){if(a[0]==="#"&&a.length===7)return{r:parseInt(a.slice(1,3),16),g:parseInt(a.slice(3,5),16),b:parseInt(a.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(a){if(a[0]==="#"&&a.length===4)return{r:parseInt(a[1]+a[1],16),g:parseInt(a[2]+a[2],16),b:parseInt(a[3]+a[3],16),a:1}},_hslColorToRGBA:function(a){if(/hsl((d+),s*([d.]+)%,s*([d.]+)%)/g.test(a)){a=/hsl((d+),s*([d.]+)%,s*([d.]+)%)/g.exec(a);a[0];a=a.slice(1);var b=Number(a[0])/360,c=Number(a[1])/100;a=Number(a[2])/100;var d,e,f=void 0;if(c===0){f=a*255;return{r:Math.round(f),g:Math.round(f),b:Math.round(f),a:1}}a<.5?d=a*(1+c):d=a+c-a*c;c=2*a-d;a=[0,0,0];for(var g=0;g<3;g++)e=b+1/3*-(g-1),e<0&&e++,e>1&&e--,6*e<1?f=c+(d-c)*6*e:2*e<1?f=d:3*e<2?f=c+(d-c)*(2/3-e)*6:f=c,a[g]=f*255;return{r:Math.round(a[0]),g:Math.round(a[1]),b:Math.round(a[2]),a:1}}},haveIntersection:function(a,b){return!(b.x>a.x+a.width||b.x+b.widtha.y+a.height||b.y+b.height1?(g=c,h=d,i=(c-e)*(c-e)+(d-f)*(d-f)):(g=a+j*(c-a),h=b+j*(d-b),i=(g-e)*(g-e)+(h-f)*(h-f))}return[g,h,i]},_getProjectionToLine:function(a,b,c){var d=l.Util.cloneObject(a),e=Number.MAX_VALUE;b.forEach(function(f,g){if(!c&&g===b.length-1)return;g=b[(g+1)%b.length];f=l.Util._getProjectionToSegment(f.x,f.y,g.x,g.y,a.x,a.y);g=f[0];var h=f[1];f=f[2];fb.length){var f=b;b=a;a=f}for(f=0;f255)return 255;else if(a<0)return 0;return Math.round(a)}q.RGBComponent=d;function e(a){if(a>1)return 1;else if(a<1e-4)return 1e-4;return a}q.alphaComponent=e;function f(){if(a.Konva.isUnminified)return function(a,d){b.Util._isNumber(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a number.");return a}}q.getNumberValidator=f;function g(d){if(a.Konva.isUnminified)return function(a,e){var f=b.Util._isNumber(a),g=b.Util._isArray(a)&&a.length==d;!f&&!g&&b.Util.warn(c(a)+" is a not valid value for ""+e+"" attribute. The value should be a number or Array("+d+")");return a}}q.getNumberOrArrayOfNumbersValidator=g;function h(){if(a.Konva.isUnminified)return function(a,d){var e=b.Util._isNumber(a),f=a==="auto";e||f||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a number or "auto".");return a}}q.getNumberOrAutoValidator=h;function i(){if(a.Konva.isUnminified)return function(a,d){b.Util._isString(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a string.");return a}}q.getStringValidator=i;function j(){if(a.Konva.isUnminified)return function(a,d){var e=b.Util._isString(a),f=Object.prototype.toString.call(a)==="[object CanvasGradient]";e||f||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a string or a native gradient.");return a}}q.getStringOrGradientValidator=j;function l(){if(a.Konva.isUnminified)return function(a,d){b.Util._isFunction(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a function.");return a}}q.getFunctionValidator=l;function m(){if(a.Konva.isUnminified)return function(a,d){!b.Util._isArray(a)?b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a array of numbers."):a.forEach(function(a){b.Util._isNumber(a)||b.Util.warn("""+d+"" attribute has non numeric element "+a+". Make sure that all elements are numbers.")});return a}}q.getNumberArrayValidator=m;function n(){if(a.Konva.isUnminified)return function(a,d){var e=a===!0||a===!1;e||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a boolean.");return a}}q.getBooleanValidator=n;function o(d){if(a.Konva.isUnminified)return function(a,e){b.Util.isObject(a)||b.Util.warn(c(a)+" is a not valid value for ""+e+"" attribute. The value should be an object with properties "+d);return a}}q.getComponentValidator=o}var t=!1;function u(){t||(t=!0,s());return r.exports}var v={},w={exports:v};function x(){Object.defineProperty(v,"__esModule",{value:!0});v.Factory=void 0;var a=p(),b=u(),c="get",d="set";v.Factory={addGetterSetter:function(a,b,c,d,e){v.Factory.addGetter(a,b,c),v.Factory.addSetter(a,b,d,e),v.Factory.addOverloadedGetterSetter(a,b)},addGetter:function(b,d,e){var f=c+a.Util._capitalize(d);b.prototype[f]=b.prototype[f]||function(){var a=this.attrs[d];return a===void 0?e:a}},addSetter:function(b,c,e,f){var g=d+a.Util._capitalize(c);b.prototype[g]||v.Factory.overWriteSetter(b,c,e,f)},overWriteSetter:function(b,c,e,f){var g=d+a.Util._capitalize(c);b.prototype[g]=function(a){e&&a!==void 0&&a!==null&&(a=e.call(this,a,c));this._setAttr(c,a);f&&f.call(this);return this}},addComponentsGetterSetter:function(e,f,g,h,i){var j=g.length,k=a.Util._capitalize,l=c+k(f),m=d+k(f),n;e.prototype[l]=function(){var a={};for(l=0;l=o&&b.shift()};a.prototype.reset=function(){var a=this.getCanvas().getPixelRatio();this.setTransform(1*a,0,0,1*a,0,0)};a.prototype.getCanvas=function(){return this.canvas};a.prototype.clear=function(a){var b=this.getCanvas();a?this.clearRect(a.x||0,a.y||0,a.width||0,a.height||0):this.clearRect(0,0,b.getWidth()/b.pixelRatio,b.getHeight()/b.pixelRatio)};a.prototype._applyLineCap=function(a){a=a.getLineCap();a&&this.setAttr("lineCap",a)};a.prototype._applyOpacity=function(a){a=a.getAbsoluteOpacity();a!==1&&this.setAttr("globalAlpha",a)};a.prototype._applyLineJoin=function(a){a=a.attrs.lineJoin;a&&this.setAttr("lineJoin",a)};a.prototype.setAttr=function(a,b){this._context[a]=b};a.prototype.arc=function(a,b,c,d,e,f){this._context.arc(a,b,c,d,e,f)};a.prototype.arcTo=function(a,b,c,d,e){this._context.arcTo(a,b,c,d,e)};a.prototype.beginPath=function(){this._context.beginPath()};a.prototype.bezierCurveTo=function(a,b,c,d,e,f){this._context.bezierCurveTo(a,b,c,d,e,f)};a.prototype.clearRect=function(a,b,c,d){this._context.clearRect(a,b,c,d)};a.prototype.clip=function(){this._context.clip()};a.prototype.closePath=function(){this._context.closePath()};a.prototype.createImageData=function(a,b){var c=arguments;if(c.length===2)return this._context.createImageData(a,b);else if(c.length===1)return this._context.createImageData(a)};a.prototype.createLinearGradient=function(a,b,c,d){return this._context.createLinearGradient(a,b,c,d)};a.prototype.createPattern=function(a,b){return this._context.createPattern(a,b)};a.prototype.createRadialGradient=function(a,b,c,d,e,f){return this._context.createRadialGradient(a,b,c,d,e,f)};a.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){var j=arguments,k=this._context;j.length===3?k.drawImage(a,b,c):j.length===5?k.drawImage(a,b,c,d,e):j.length===9&&k.drawImage(a,b,c,d,e,f,g,h,i)};a.prototype.ellipse=function(a,b,c,d,e,f,g,h){this._context.ellipse(a,b,c,d,e,f,g,h)};a.prototype.isPointInPath=function(a,b){return this._context.isPointInPath(a,b)};a.prototype.fill=function(){this._context.fill()};a.prototype.fillRect=function(a,b,c,d){this._context.fillRect(a,b,c,d)};a.prototype.strokeRect=function(a,b,c,d){this._context.strokeRect(a,b,c,d)};a.prototype.fillText=function(a,b,c){this._context.fillText(a,b,c)};a.prototype.measureText=function(a){return this._context.measureText(a)};a.prototype.getImageData=function(a,b,c,d){return this._context.getImageData(a,b,c,d)};a.prototype.lineTo=function(a,b){this._context.lineTo(a,b)};a.prototype.moveTo=function(a,b){this._context.moveTo(a,b)};a.prototype.rect=function(a,b,c,d){this._context.rect(a,b,c,d)};a.prototype.putImageData=function(a,b,c){this._context.putImageData(a,b,c)};a.prototype.quadraticCurveTo=function(a,b,c,d){this._context.quadraticCurveTo(a,b,c,d)};a.prototype.restore=function(){this._context.restore()};a.prototype.rotate=function(a){this._context.rotate(a)};a.prototype.save=function(){this._context.save()};a.prototype.scale=function(a,b){this._context.scale(a,b)};a.prototype.setLineDash=function(a){this._context.setLineDash?this._context.setLineDash(a):"mozDash"in this._context?this._context.mozDash=a:"webkitLineDash"in this._context&&(this._context.webkitLineDash=a)};a.prototype.getLineDash=function(){return this._context.getLineDash()};a.prototype.setTransform=function(a,b,c,d,e,f){this._context.setTransform(a,b,c,d,e,f)};a.prototype.stroke=function(){this._context.stroke()};a.prototype.strokeText=function(a,b,c,d){this._context.strokeText(a,b,c,d)};a.prototype.transform=function(a,b,c,d,e,f){this._context.transform(a,b,c,d,e,f)};a.prototype.translate=function(a,b){this._context.translate(a,b)};a.prototype._enableTrace=function(){var a=this,c=m.length,d=b.Util._simplifyArray,e=this.setAttr,f,g,h=function(b){var c=a[b],e;a[b]=function(){g=d(Array.prototype.slice.call(arguments,0));e=c.apply(a,arguments);a._trace({method:b,args:g});return e}};for(f=0;f0&&d[0].getDepth()<=a&&h(d)}b.nodeType!==D&&h(b.getStage().getChildren());return c};b.prototype.getDepth=function(){var a=0,b=this.parent;while(b)a++,b=b.parent;return a};b.prototype._batchTransformChanges=function(a){this._batchingTransformChange=!0,a(),this._batchingTransformChange=!1,this._needClearTransformCache&&(this._clearCache(C),this._clearSelfAndDescendantCache(l,!0)),this._needClearTransformCache=!1};b.prototype.setPosition=function(a){var b=this;this._batchTransformChanges(function(){b.x(a.x),b.y(a.y)});return this};b.prototype.getPosition=function(){return{x:this.x(),y:this.y()}};b.prototype.getAbsolutePosition=function(b){var c=!1,d=this.parent;while(d){if(d.isCached()){c=!0;break}d=d.parent}c&&!b&&(b=!0);d=this.getAbsoluteTransform(b).getMatrix();c=new a.Transform();b=this.offset();c.m=d.slice();c.translate(b.x,b.y);return c.getTranslation()};b.prototype.setAbsolutePosition=function(a){var b=this._clearTransform();this.attrs.x=b.x;this.attrs.y=b.y;delete b.x;delete b.y;this._clearCache(C);var c=this._getAbsoluteTransform().copy();c.invert();c.translate(a.x,a.y);a={x:this.attrs.x+c.getTranslation().x,y:this.attrs.y+c.getTranslation().y};this._setTransform(b);this.setPosition({x:a.x,y:a.y});this._clearCache(C);this._clearSelfAndDescendantCache(l);return this};b.prototype._setTransform=function(a){var b;for(b in a)this.attrs[b]=a[b]};b.prototype._clearTransform=function(){var a={x:this.x(),y:this.y(),rotation:this.rotation(),scaleX:this.scaleX(),scaleY:this.scaleY(),offsetX:this.offsetX(),offsetY:this.offsetY(),skewX:this.skewX(),skewY:this.skewY()};this.attrs.x=0;this.attrs.y=0;this.attrs.rotation=0;this.attrs.scaleX=1;this.attrs.scaleY=1;this.attrs.offsetX=0;this.attrs.offsetY=0;this.attrs.skewX=0;this.attrs.skewY=0;return a};b.prototype.move=function(a){var b=a.x;a=a.y;var c=this.x(),d=this.y();b!==void 0&&(c+=b);a!==void 0&&(d+=a);this.setPosition({x:c,y:d});return this};b.prototype._eachAncestorReverse=function(a,b){var c=[],d=this.getParent();if(b&&b._id===this._id)return;c.unshift(this);while(d&&(!b||d._id!==b._id))c.unshift(d),d=d.parent;d=c.length;for(b=0;b0){this.parent.children.splice(b,1);this.parent.children.splice(b-1,0,this);this.parent._setChildrenIndices();return!0}return!1};b.prototype.moveToBottom=function(){if(!this.parent){a.Util.warn("Node has no parent. moveToBottom function is ignored.");return!1}var b=this.index;if(b>0){this.parent.children.splice(b,1);this.parent.children.unshift(this);this.parent._setChildrenIndices();return!0}return!1};b.prototype.setZIndex=function(b){if(!this.parent){a.Util.warn("Node has no parent. zIndex parameter is ignored.");return this}(b<0||b>=this.parent.children.length)&&a.Util.warn("Unexpected value "+b+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var c=this.index;this.parent.children.splice(c,1);this.parent.children.splice(b,0,this);this.parent._setChildrenIndices();return this};b.prototype.getAbsoluteOpacity=function(){return this._getCache(i,this._getAbsoluteOpacity)};b.prototype._getAbsoluteOpacity=function(){var a=this.opacity(),b=this.getParent();b&&!b._isUnderCache&&(a*=b.getAbsoluteOpacity());return a};b.prototype.moveTo=function(a){this.getParent()!==a&&(this._remove(),a.add(this));return this};b.prototype.toObject=function(){var b={},c=this.getAttrs(),d,e,f;b.attrs={};for(d in c){e=c[d];f=a.Util.isObject(e)&&!a.Util._isPlainObject(e)&&!a.Util._isArray(e);if(f)continue;f=typeof this[d]==="function"&&this[d];delete c[d];f=f?f.call(this):null;c[d]=e;f!==e&&(b.attrs[d]=e)}b.className=this.getClassName();return a.Util._prepareToStringify(b)};b.prototype.toJSON=function(){return JSON.stringify(this.toObject())};b.prototype.getParent=function(){return this.parent};b.prototype.findAncestors=function(a,b,c){var d=[];b&&this._isMatch(a)&&d.push(this);b=this.parent;while(b){if(b===c)return d;b._isMatch(a)&&d.push(b);b=b.parent}return d};b.prototype.isAncestorOf=function(a){return!1};b.prototype.findAncestor=function(a,b,c){return this.findAncestors(a,b,c)[0]};b.prototype._isMatch=function(b){if(!b)return!1;if(typeof b==="function")return b(this);b=b.replace(/ /g,"").split(",");var c=b.length,d,e;for(d=0;d=0;if(!c)return;if(this.isDragging())return;c=!1;e.DD._dragElements.forEach(function(a){b.isAncestorOf(a.node)&&(c=!0)});c||this._createDragElement(a)})};b.prototype._dragChange=function(){if(this.attrs.draggable)this._listenDrag();else{this._dragCleanup();var a=this.getStage();if(!a)return;a=e.DD._dragElements.get(this._id);var b=a&&a.dragStatus==="dragging";a=a&&a.dragStatus==="ready";b?this.stopDrag():a&&e.DD._dragElements["delete"](this._id)}};b.prototype._dragCleanup=function(){this.off("mousedown.konva"),this.off("touchstart.konva")};b.create=function(b,c){a.Util._isString(b)&&(b=JSON.parse(b));return this._createNode(b,c)};b._createNode=function(c,e){var f=b.prototype.getClassName.call(c),g=c.children;e&&(c.attrs.container=e);d._NODES_REGISTRY[f]||(a.Util.warn("Can not find a node with class name ""+f+"". Fallback to "Shape"."),f="Shape");e=d._NODES_REGISTRY[f];f=new e(c.attrs);if(g){e=g.length;for(c=0;c0};c.prototype.removeChildren=function(){var a;for(var c=0;c1){for(var c=0;c0?a[0]:void 0};c.prototype._generalFind=function(a,c){var d=[];this._descendants(function(b){var e=b._isMatch(a);e&&d.push(b);return e&&c?!0:!1});return b.Collection.toCollection(d)};c.prototype._descendants=function(a){var b;for(var c=0;c-1&&V.stages.splice(a,1);return this};h.prototype.getPointerPosition=function(){var a=this._pointerPositions[0]||this._changedPointerPositions[0];if(!a){b.Util.warn(pa);return null}return{x:a.x,y:a.y}};h.prototype._getPointerById=function(a){return this._pointerPositions.find(function(b){return b.id===a})};h.prototype.getPointersPositions=function(){return this._pointerPositions};h.prototype.getStage=function(){return this};h.prototype.getContent=function(){return this.content};h.prototype._toKonvaCanvas=function(a){a=a||{};a.x=a.x||0;a.y=a.y||0;a.width=a.width||this.width();a.height=a.height||this.height();var b=new f.SceneCanvas({width:a.width,height:a.height,pixelRatio:a.pixelRatio||1}),c=b.getContext()._context,d=this.children;(a.x||a.y)&&c.translate(-1*a.x,-1*a.y);d.each(function(b){if(!b.isVisible())return;b=b._toKonvaCanvas(a);c.drawImage(b._canvas,a.x,a.y,b.getWidth()/b.getPixelRatio(),b.getHeight()/b.getPixelRatio())});return b};h.prototype.getIntersection=function(a,b){if(!a)return null;var c=this.children,d=c.length;d=d-1;var e;for(d=d;d>=0;d--){e=c[d].getIntersection(a,b);if(e)return e}return null};h.prototype._resizeDOM=function(){var a=this.width(),b=this.height();this.content&&(this.content.style.width=a+m,this.content.style.height=b+m);this.bufferCanvas.setSize(a,b);this.bufferHitCanvas.setSize(a,b);this.children.each(function(c){c.setSize({width:a,height:b}),c.draw()})};h.prototype.add=function(a){if(arguments.length>1){for(var d=0;dla&&b.Util.warn("The stage has "+f+" layers. Recommended maximum number of layers is 3-5. Adding more layers into the stage may drop the performance. Rethink your tree structure, you can use Konva.Group.");a.setSize({width:this.width(),height:this.height()});a.draw();e.Konva.isBrowser&&this.content.appendChild(a.canvas._canvas);return this};h.prototype.getParent=function(){return null};h.prototype.getLayer=function(){return null};h.prototype.hasPointerCapture=function(a){return i.hasPointerCapture(a,this)};h.prototype.setPointerCapture=function(a){i.setPointerCapture(a,this)};h.prototype.releaseCapture=function(a){i.releaseCapture(a,this)};h.prototype.getLayers=function(){return this.getChildren()};h.prototype._bindContentEvents=function(){if(!e.Konva.isBrowser)return;for(var a=0;a0};c.prototype.destroy=function(){d.Node.prototype.destroy.call(this);delete W.shapes[this.colorKey];delete this.colorKey;return this};c.prototype._useBufferCanvas=function(a){var b;if(!this.getStage())return!1;b=(b=this.attrs.perfectDrawEnabled)!==null&&b!==void 0?b:!0;if(!b)return!1;b=a||this.hasFill();a=this.hasStroke();var c=this.getAbsoluteOpacity()!==1;if(b&&a&&c)return!0;c=this.hasShadow();var d=this.shadowForStrokeEnabled();return b&&a&&c&&d?!0:!1};c.prototype.setStrokeHitEnabled=function(a){b.Util.warn("strokeHitEnabled property is deprecated. Please use hitStrokeWidth instead."),a?this.hitStrokeWidth("auto"):this.hitStrokeWidth(0)};c.prototype.getStrokeHitEnabled=function(){if(this.hitStrokeWidth()===0)return!1;else return!0};c.prototype.getSelfRect=function(){var a=this.size();return{x:this._centroid?-a.width/2:0,y:this._centroid?-a.height/2:0,width:a.width,height:a.height}};c.prototype.getClientRect=function(a){a===void 0&&(a={});var b=a.skipTransform,c=a.relativeTo,d=this.getSelfRect(),e=!a.skipStroke&&this.hasStroke();e=e&&this.strokeWidth()||0;var f=d.width+e,g=d.height+e;a=!a.skipShadow&&this.hasShadow();var h=a?this.shadowOffsetX():0,i=a?this.shadowOffsetY():0;f=f+Math.abs(h);g=g+Math.abs(i);a=a&&this.shadowBlur()||0;f=f+a*2;g=g+a*2;var j=0;Math.round(e/2)!==e/2&&(j=1);f={width:f+j,height:g+j,x:-Math.round(e/2+a)+Math.min(h,0)+d.x,y:-Math.round(e/2+a)+Math.min(i,0)+d.y};return!b?this._transformedRect(f,c):f};c.prototype.drawScene=function(a,b){var c=this.getLayer();a=a||c.getCanvas();c=a.getContext();var d=this._getCanvasCache(),e=this.getSceneFunc(),f=this.hasShadow(),g=a.isCache;a=a.isCache;var h=b===this;if(!this.isVisible()&&!g)return this;if(d){c.save();g=this.getAbsoluteTransform(b).getMatrix();c.transform(g[0],g[1],g[2],g[3],g[4],g[5]);this._drawCachedSceneCanvas(c);c.restore();return this}if(!e)return this;c.save();if(this._useBufferCanvas()&&!a){d=this.getStage();g=d.bufferCanvas;a=g.getContext();a.clear();a.save();a._applyLineJoin(this);d=this.getAbsoluteTransform(b).getMatrix();a.transform(d[0],d[1],d[2],d[3],d[4],d[5]);e.call(this,a,this);a.restore();a=g.pixelRatio;f&&c._applyShadow(this);c._applyOpacity(this);c._applyGlobalCompositeOperation(this);c.drawImage(g._canvas,0,0,g.width/a,g.height/a)}else{c._applyLineJoin(this);if(!h){d=this.getAbsoluteTransform(b).getMatrix();c.transform(d[0],d[1],d[2],d[3],d[4],d[5]);c._applyOpacity(this);c._applyGlobalCompositeOperation(this)}f&&c._applyShadow(this);e.call(this,c,this)}c.restore();return this};c.prototype.drawHit=function(a,c,d){d===void 0&&(d=!1);if(!this.shouldDrawHit(c,d))return this;d=this.getLayer();a=a||d.hitCanvas;d=a&&a.getContext();a=this.hitFunc()||this.sceneFunc();var e=this._getCanvasCache();e=e&&e.hit;this.colorKey||b.Util.warn("Looks like your canvas has a destroyed shape in it. Do not reuse shape after you destroyed it. See the shape in logs above. If you want to reuse shape you should call remove() instead of destroy()");if(e){d.save();e=this.getAbsoluteTransform(c).getMatrix();d.transform(e[0],e[1],e[2],e[3],e[4],e[5]);this._drawCachedHitCanvas(d);d.restore();return this}if(!a)return this;d.save();d._applyLineJoin(this);e=this===c;if(!e){e=this.getAbsoluteTransform(c).getMatrix();d.transform(e[0],e[1],e[2],e[3],e[4],e[5])}a.call(this,d,this);d.restore();return this};c.prototype.drawHitFromCache=function(a){a===void 0&&(a=0);var c=this._getCanvasCache(),d=this._getCachedSceneCanvas();c=c.hit;var e=c.getContext(),f=c.getWidth();c=c.getHeight();var g,h,i;e.clear();e.drawImage(d._canvas,0,0,f,c);try{d=e.getImageData(0,0,f,c);f=d.data;c=f.length;g=b.Util._hexToRgb(this.colorKey);for(h=0;ha?(f[h]=g.r,f[h+1]=g.g,f[h+2]=g.b,f[h+3]=255):f[h+3]=0;e.putImageData(d,0,0)}catch(a){b.Util.error("Unable to draw hit graph from cached scene canvas. "+a.message)}return this};c.prototype.hasPointerCapture=function(a){return g.hasPointerCapture(a,this)};c.prototype.setPointerCapture=function(a){g.setPointerCapture(a,this)};c.prototype.releaseCapture=function(a){g.releaseCapture(a,this)};return c}(d.Node);W.Shape=B;B.prototype._fillFunc=q;B.prototype._strokeFunc=r;B.prototype._fillFuncHit=s;B.prototype._strokeFuncHit=t;B.prototype._centroid=!1;B.prototype.nodeType="Shape";f._registerNode(B);B.prototype.eventListeners={};B.prototype.on.call(B.prototype,"shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva",v);B.prototype.on.call(B.prototype,"shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva",w);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva",x);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva",y);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva",A);c.Factory.addGetterSetter(B,"stroke",void 0,e.getStringOrGradientValidator());c.Factory.addGetterSetter(B,"strokeWidth",2,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillAfterStrokeEnabled",!1);c.Factory.addGetterSetter(B,"hitStrokeWidth","auto",e.getNumberOrAutoValidator());c.Factory.addGetterSetter(B,"strokeHitEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"perfectDrawEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"shadowForStrokeEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"lineJoin");c.Factory.addGetterSetter(B,"lineCap");c.Factory.addGetterSetter(B,"sceneFunc");c.Factory.addGetterSetter(B,"hitFunc");c.Factory.addGetterSetter(B,"dash");c.Factory.addGetterSetter(B,"dashOffset",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowColor",void 0,e.getStringValidator());c.Factory.addGetterSetter(B,"shadowBlur",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowOpacity",1,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"shadowOffset",["x","y"]);c.Factory.addGetterSetter(B,"shadowOffsetX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowOffsetY",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternImage");c.Factory.addGetterSetter(B,"fill",void 0,e.getStringOrGradientValidator());c.Factory.addGetterSetter(B,"fillPatternX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternY",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillLinearGradientColorStops");c.Factory.addGetterSetter(B,"strokeLinearGradientColorStops");c.Factory.addGetterSetter(B,"fillRadialGradientStartRadius",0);c.Factory.addGetterSetter(B,"fillRadialGradientEndRadius",0);c.Factory.addGetterSetter(B,"fillRadialGradientColorStops");c.Factory.addGetterSetter(B,"fillPatternRepeat","repeat");c.Factory.addGetterSetter(B,"fillEnabled",!0);c.Factory.addGetterSetter(B,"strokeEnabled",!0);c.Factory.addGetterSetter(B,"shadowEnabled",!0);c.Factory.addGetterSetter(B,"dashEnabled",!0);c.Factory.addGetterSetter(B,"strokeScaleEnabled",!0);c.Factory.addGetterSetter(B,"fillPriority","color");c.Factory.addComponentsGetterSetter(B,"fillPatternOffset",["x","y"]);c.Factory.addGetterSetter(B,"fillPatternOffsetX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternOffsetY",0,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"fillPatternScale",["x","y"]);c.Factory.addGetterSetter(B,"fillPatternScaleX",1,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternScaleY",1,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"fillLinearGradientStartPoint",["x","y"]);c.Factory.addComponentsGetterSetter(B,"strokeLinearGradientStartPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillLinearGradientStartPointX",0);c.Factory.addGetterSetter(B,"strokeLinearGradientStartPointX",0);c.Factory.addGetterSetter(B,"fillLinearGradientStartPointY",0);c.Factory.addGetterSetter(B,"strokeLinearGradientStartPointY",0);c.Factory.addComponentsGetterSetter(B,"fillLinearGradientEndPoint",["x","y"]);c.Factory.addComponentsGetterSetter(B,"strokeLinearGradientEndPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillLinearGradientEndPointX",0);c.Factory.addGetterSetter(B,"strokeLinearGradientEndPointX",0);c.Factory.addGetterSetter(B,"fillLinearGradientEndPointY",0);c.Factory.addGetterSetter(B,"strokeLinearGradientEndPointY",0);c.Factory.addComponentsGetterSetter(B,"fillRadialGradientStartPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillRadialGradientStartPointX",0);c.Factory.addGetterSetter(B,"fillRadialGradientStartPointY",0);c.Factory.addComponentsGetterSetter(B,"fillRadialGradientEndPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillRadialGradientEndPointX",0);c.Factory.addGetterSetter(B,"fillRadialGradientEndPointY",0);c.Factory.addGetterSetter(B,"fillPatternRotation",0);c.Factory.backCompat(B,{dashArray:"dash",getDashArray:"getDash",setDashArray:"getDash",drawFunc:"sceneFunc",getDrawFunc:"getSceneFunc",setDrawFunc:"setSceneFunc",drawHitFunc:"hitFunc",getDrawHitFunc:"getHitFunc",setDrawHitFunc:"setHitFunc"});b.Collection.mapMethods(B)}var pa=!1;function X(){pa||(pa=!0,oa());return na.exports}var qa={},ra={exports:qa};function sa(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(qa,"__esModule",{value:!0});qa.Layer=void 0;var b=p(),c=ea(),d=R(),e=z(),f=J(),g=u(),h=X(),i=k(),j="#",l="beforeDraw",m="draw",n=[{x:0,y:0},{x:-1,y:-1},{x:1,y:-1},{x:1,y:1},{x:-1,y:1}],o=n.length,q=function(g){a(e,g);function e(a){a=g.call(this,a)||this;a.canvas=new f.SceneCanvas();a.hitCanvas=new f.HitCanvas({pixelRatio:1});a._waitingForDraw=!1;a.on("visibleChange.konva",a._checkVisibility);a._checkVisibility();a.on("imageSmoothingEnabledChange.konva",a._setSmoothEnabled);a._setSmoothEnabled();return a}e.prototype.createPNGStream=function(){var a=this.canvas._canvas;return a.createPNGStream()};e.prototype.getCanvas=function(){return this.canvas};e.prototype.getHitCanvas=function(){return this.hitCanvas};e.prototype.getContext=function(){return this.getCanvas().getContext()};e.prototype.clear=function(a){this.getContext().clear(a);this.getHitCanvas().getContext().clear(a);return this};e.prototype.setZIndex=function(a){g.prototype.setZIndex.call(this,a);var b=this.getStage();b&&(b.content.removeChild(this.getCanvas()._canvas),a0)return{antialiased:!0};return{}};e.prototype.drawScene=function(a,b){var d=this.getLayer();a=a||d&&d.getCanvas();this._fire(l,{node:this});this.clearBeforeDraw()&&a.getContext().clear();c.Container.prototype.drawScene.call(this,a,b);this._fire(m,{node:this});return this};e.prototype.drawHit=function(a,b){var d=this.getLayer();a=a||d&&d.hitCanvas;d&&d.clearBeforeDraw()&&d.getHitCanvas().getContext().clear();c.Container.prototype.drawHit.call(this,a,b);return this};e.prototype.enableHitGraph=function(){this.hitGraphEnabled(!0);return this};e.prototype.disableHitGraph=function(){this.hitGraphEnabled(!1);return this};e.prototype.setHitGraphEnabled=function(a){b.Util.warn("hitGraphEnabled method is deprecated. Please use layer.listening() instead."),this.listening(a)};e.prototype.getHitGraphEnabled=function(a){b.Util.warn("hitGraphEnabled method is deprecated. Please use layer.listening() instead.");return this.listening()};e.prototype.toggleHitCanvas=function(){if(!this.parent)return;var a=this.parent,b=!!this.hitCanvas._canvas.parentNode;b?a.content.removeChild(this.hitCanvas._canvas):a.content.appendChild(this.hitCanvas._canvas)};return e}(c.Container);qa.Layer=q;q.prototype.nodeType="Layer";i._registerNode(q);e.Factory.addGetterSetter(q,"imageSmoothingEnabled",!0);e.Factory.addGetterSetter(q,"clearBeforeDraw",!0);e.Factory.addGetterSetter(q,"hitGraphEnabled",!0,g.getBooleanValidator());b.Collection.mapMethods(q)}var ta=!1;function ua(){ta||(ta=!0,sa());return ra.exports}var va={},wa={exports:va};function xa(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(va,"__esModule",{value:!0});va.FastLayer=void 0;var b=p(),c=ua(),d=k();c=function(c){a(d,c);function d(a){a=c.call(this,a)||this;a.listening(!1);b.Util.warn("Konva.Fast layer is deprecated. Please use "new Konva.Layer({ listening: false })" instead.");return a}return d}(c.Layer);va.FastLayer=c;c.prototype.nodeType="FastLayer";d._registerNode(c);b.Collection.mapMethods(c)}var ya=!1;function za(){ya||(ya=!0,xa());return wa.exports}var Aa={},Ba={exports:Aa};function Ca(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Aa,"__esModule",{value:!0});Aa.Group=void 0;var b=p(),c=ea(),d=k();c=function(c){a(d,c);function d(){return c!==null&&c.apply(this,arguments)||this}d.prototype._validateAdd=function(a){a=a.getType();a!=="Group"&&a!=="Shape"&&b.Util["throw"]("You may only add groups and shapes to groups.")};return d}(c.Container);Aa.Group=c;c.prototype.nodeType="Group";d._registerNode(c);b.Collection.mapMethods(c)}var Da=!1;function Ea(){Da||(Da=!0,Ca());return Ba.exports}var Fa={},Ga={exports:Fa};function Ha(){Object.defineProperty(Fa,"__esModule",{value:!0});Fa.Animation=void 0;var a=k(),b=function(){return a.glob.performance&&a.glob.performance.now?function(){return a.glob.performance.now()}:function(){return new Date().getTime()}}(),c=function(){function a(c,d){this.id=a.animIdCounter++,this.frame={time:0,timeDiff:0,lastTime:b(),frameRate:0},this.func=c,this.setLayers(d)}a.prototype.setLayers=function(a){var b;!a?b=[]:a.length>0?b=a:b=[a];this.layers=b;return this};a.prototype.getLayers=function(){return this.layers};a.prototype.addLayer=function(a){var b=this.layers,c=b.length,d;for(d=0;dthis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():a<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=a,this.update())};a.prototype.getTime=function(){return this._time};a.prototype.setPosition=function(a){this.prevPos=this._pos,this.propFunc(a),this._pos=a};a.prototype.getPosition=function(a){a===void 0&&(a=this._time);return this.func(a,this.begin,this._change,this.duration)};a.prototype.play=function(){this.state=g,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")};a.prototype.reverse=function(){this.state=h,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")};a.prototype.seek=function(a){this.pause(),this._time=a,this.update(),this.fire("onSeek")};a.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")};a.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")};a.prototype.update=function(){this.setPosition(this.getPosition(this._time)),this.fire("onUpdate")};a.prototype.onEnterFrame=function(){var a=this.getTimer()-this._startTime;this.state===g?this.setTime(a):this.state===h&&this.setTime(this.duration-a)};a.prototype.pause=function(){this.state=f,this.fire("onPause")};a.prototype.getTimer=function(){return new Date().getTime()};return a}(),m=function(){function c(f){var g=this,h=f.node,j=h._id,k,m=f.easing||Y.Easings.Linear,n=!!f.yoyo,o;typeof f.duration==="undefined"?k=.3:f.duration===0?k=.001:k=f.duration;this.node=h;this._id=i++;h=h.getLayer()||(h instanceof d.Konva.Stage?h.getLayers():null);h||a.Util.error("Tween constructor have `node` that is not in a layer. Please add node into layer first.");this.anim=new b.Animation(function(){g.tween.onEnterFrame()},h);this.tween=new l(o,function(a){g._tweenFunc(a)},m,0,1,k*1e3,n);this._addListeners();c.attrs[j]||(c.attrs[j]={});c.attrs[j][this._id]||(c.attrs[j][this._id]={});c.tweens[j]||(c.tweens[j]={});for(o in f)e[o]===void 0&&this._addAttr(o,f[o]);this.reset();this.onFinish=f.onFinish;this.onReset=f.onReset;this.onUpdate=f.onUpdate}c.prototype._addAttr=function(b,d){var e=this.node,f=e._id,g,h,i,k,l,m;h=c.tweens[f][b];h&&delete c.attrs[f][h][b];h=e.getAttr(b);if(a.Util._isArray(d)){g=[];i=Math.max(d.length,h.length);b==="points"&&d.length!==h.length&&(d.length>h.length?(l=h,h=a.Util._prepareArrayForTween(h,d,e.closed())):(k=d,d=a.Util._prepareArrayForTween(d,h,e.closed())));if(b.indexOf("fill")===0)for(e=0;e4){d=this.getTensionPoints();g=d.length;h=e?0:4;e||a.quadraticCurveTo(d[0],d[1],d[2],d[3]);while(h4;f&&(e=this.getTensionPoints());var g=d.length,h,i;f?(h=d[g-2]-(e[e.length-2]+e[e.length-4])/2,i=d[g-1]-(e[e.length-1]+e[e.length-3])/2):(h=d[g-2]-d[g-4],i=d[g-1]-d[g-3]);var j=(Math.atan2(i,h)+c)%c,k=this.pointerLength(),l=this.pointerWidth();a.save();a.beginPath();a.translate(d[g-2],d[g-1]);a.rotate(j);a.moveTo(0,0);a.lineTo(-k,l/2);a.lineTo(-k,-l/2);a.closePath();a.restore();this.pointerAtBeginning()&&(a.save(),a.translate(d[0],d[1]),f?(h=(e[0]+e[2])/2-d[0],i=(e[1]+e[3])/2-d[1]):(h=d[2]-d[0],i=d[3]-d[1]),a.rotate((Math.atan2(-i,-h)+c)%c),a.moveTo(0,0),a.lineTo(-k,l/2),a.lineTo(-k,-l/2),a.closePath(),a.restore());g=this.dashEnabled();g&&(this.attrs.dashEnabled=!1,a.setLineDash([]));a.fillStrokeShape(this);g&&(this.attrs.dashEnabled=!0)};c.prototype.getSelfRect=function(){var a=b.prototype.getSelfRect.call(this),c=this.pointerWidth()/2;return{x:a.x-c,y:a.y-c,width:a.width+c*2,height:a.height+c*2}};return c}(d.Line);cb.Arrow=d;d.prototype.className="Arrow";f._registerNode(d);c.Factory.addGetterSetter(d,"pointerLength",10,e.getNumberValidator());c.Factory.addGetterSetter(d,"pointerWidth",10,e.getNumberValidator());c.Factory.addGetterSetter(d,"pointerAtBeginning",!1);b.Collection.mapMethods(d)}var fb=!1;function gb(){fb||(fb=!0,eb());return db.exports}var hb={},ib={exports:hb};function jb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(hb,"__esModule",{value:!0});hb.Circle=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){a.beginPath(),a.arc(0,0,this.attrs.radius||0,0,Math.PI*2,!1),a.closePath(),a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radius()*2};c.prototype.getHeight=function(){return this.radius()*2};c.prototype.setWidth=function(a){this.radius()!==a/2&&this.radius(a/2)};c.prototype.setHeight=function(a){this.radius()!==a/2&&this.radius(a/2)};return c}(d.Shape);hb.Circle=d;d.prototype._centroid=!0;d.prototype.className="Circle";d.prototype._attrsAffectingSize=["radius"];f._registerNode(d);c.Factory.addGetterSetter(d,"radius",0,e.getNumberValidator());b.Collection.mapMethods(d)}var kb=!1;function lb(){kb||(kb=!0,jb());return ib.exports}var mb={},nb={exports:mb};function ob(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(mb,"__esModule",{value:!0});mb.Ellipse=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this.radiusX(),c=this.radiusY();a.beginPath();a.save();b!==c&&a.scale(1,c/b);a.arc(0,0,b,0,Math.PI*2,!1);a.restore();a.closePath();a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radiusX()*2};c.prototype.getHeight=function(){return this.radiusY()*2};c.prototype.setWidth=function(a){this.radiusX(a/2)};c.prototype.setHeight=function(a){this.radiusY(a/2)};return c}(d.Shape);mb.Ellipse=d;d.prototype.className="Ellipse";d.prototype._centroid=!0;d.prototype._attrsAffectingSize=["radiusX","radiusY"];f._registerNode(d);c.Factory.addComponentsGetterSetter(d,"radius",["x","y"]);c.Factory.addGetterSetter(d,"radiusX",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"radiusY",0,e.getNumberValidator());b.Collection.mapMethods(d)}var pb=!1;function qb(){pb||(pb=!0,ob());return nb.exports}var rb={},sb={exports:rb};function tb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(rb,"__esModule",{value:!0});rb.Image=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(c){a(d,c);function d(){return c!==null&&c.apply(this,arguments)||this}d.prototype._useBufferCanvas=function(){return c.prototype._useBufferCanvas.call(this,!0)};d.prototype._sceneFunc=function(a){var b=this.getWidth(),c=this.getHeight(),d=this.attrs.image,e;if(d){var f=this.attrs.cropWidth,g=this.attrs.cropHeight;f&&g?e=[d,this.cropX(),this.cropY(),f,g,0,0,b,c]:e=[d,0,0,b,c]}(this.hasFill()||this.hasStroke())&&(a.beginPath(),a.rect(0,0,b,c),a.closePath(),a.fillStrokeShape(this));d&&a.drawImage.apply(a,e)};d.prototype._hitFunc=function(a){var b=this.width(),c=this.height();a.beginPath();a.rect(0,0,b,c);a.closePath();a.fillStrokeShape(this)};d.prototype.getWidth=function(){var a;return(a=this.attrs.width)!==null&&a!==void 0?a:((a=this.image())===null||a===void 0?void 0:a.width)||0};d.prototype.getHeight=function(){var a;return(a=this.attrs.height)!==null&&a!==void 0?a:((a=this.image())===null||a===void 0?void 0:a.height)||0};d.fromURL=function(a,c){var e=b.Util.createImageElement();e.onload=function(){var a=new d({image:e});c(a)};e.crossOrigin="Anonymous";e.src=a};return d}(d.Shape);rb.Image=d;d.prototype.className="Image";f._registerNode(d);c.Factory.addGetterSetter(d,"image");c.Factory.addComponentsGetterSetter(d,"crop",["x","y","width","height"]);c.Factory.addGetterSetter(d,"cropX",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropY",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropWidth",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropHeight",0,e.getNumberValidator());b.Collection.mapMethods(d)}var ub=!1;function vb(){ub||(ub=!0,tb());return sb.exports}var Z={},wb={exports:Z};function xb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Z,"__esModule",{value:!0});Z.Tag=Z.Label=void 0;var b=p(),c=z(),d=X(),e=Ea(),f=u(),g=k(),h=["fontFamily","fontSize","fontStyle","padding","lineHeight","text","width","height"],i="Change.konva",j="none",l="up",m="right",n="down",o="left",q=h.length;e=function(b){a(c,b);function c(a){a=b.call(this,a)||this;a.on("add.konva",function(a){this._addListeners(a.child),this._sync()});return a}c.prototype.getText=function(){return this.find("Text")[0]};c.prototype.getTag=function(){return this.find("Tag")[0]};c.prototype._addListeners=function(a){var b=this,c,d=function(){b._sync()};for(c=0;ci?h:i,n=h>i?1:h/i;i=h>i?i/h:1;a.translate(e,g);a.rotate(l);a.scale(n,i);a.arc(0,0,m,j,j+k,1-f);a.scale(1/n,1/i);a.rotate(-l);a.translate(-e,-g);break;case"z":c=!0;a.closePath();break}}!c&&!this.hasFill()?a.strokeShape(this):a.fillStrokeShape(this)};c.prototype.getSelfRect=function(){var a=[];this.dataArray.forEach(function(b){if(b.command==="A"){var d=b.points[4],e=b.points[5],f=b.points[4]+e,g=Math.PI/180;Math.abs(d-f)f;e-=g){var h=c.getPointOnEllipticalArc(b.points[0],b.points[1],b.points[2],b.points[3],e,0);a.push(h.x,h.y)}else for(e=d+g;ethis.dataArray[b].pathLength)a-=this.dataArray[b].pathLength,++b;if(b===d){d=this.dataArray[b-1].points.slice(-2);return{x:d[0],y:d[1]}}if(a<.01){d=this.dataArray[b].points.slice(0,2);return{x:d[0],y:d[1]}}d=this.dataArray[b];b=d.points;switch(d.command){case"L":return c.getPointOnLine(a,d.start.x,d.start.y,b[0],b[1]);case"C":return c.getPointOnCubicBezier(a/d.pathLength,d.start.x,d.start.y,b[0],b[1],b[2],b[3],b[4],b[5]);case"Q":return c.getPointOnQuadraticBezier(a/d.pathLength,d.start.x,d.start.y,b[0],b[1],b[2],b[3]);case"A":var e=b[0],f=b[1],g=b[2],h=b[3],i=b[4],j=b[5];b=b[6];i+=j*a/d.pathLength;return c.getPointOnEllipticalArc(e,f,g,h,i,b)}return null};c.getLineLength=function(a,b,c,d){return Math.sqrt((c-a)*(c-a)+(d-b)*(d-b))};c.getPointOnLine=function(a,b,c,d,e,f,g){f===void 0&&(f=b);g===void 0&&(g=c);var h=(e-c)/(d-b+1e-8),i=Math.sqrt(a*a/(1+h*h));d0){if(isNaN(h[0]))break;l=null;i=[];k=e;var m=f,n,o,p,q;switch(j){case"l":e+=h.shift();f+=h.shift();l="L";i.push(e,f);break;case"L":e=h.shift();f=h.shift();i.push(e,f);break;case"m":var r=h.shift(),s=h.shift();e+=r;f+=s;l="M";if(a.length>2&&a[a.length-1].command==="z")for(var t=a.length-2;t>=0;t--)if(a[t].command==="M"){e=a[t].points[0]+r;f=a[t].points[1]+s;break}i.push(e,f);j="l";break;case"M":e=h.shift();f=h.shift();l="M";i.push(e,f);j="L";break;case"h":e+=h.shift();l="L";i.push(e,f);break;case"H":e=h.shift();l="L";i.push(e,f);break;case"v":f+=h.shift();l="L";i.push(e,f);break;case"V":f=h.shift();l="L";i.push(e,f);break;case"C":i.push(h.shift(),h.shift(),h.shift(),h.shift());e=h.shift();f=h.shift();i.push(e,f);break;case"c":i.push(e+h.shift(),f+h.shift(),e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="C";i.push(e,f);break;case"S":r=e;s=f;t=a[a.length-1];t.command==="C"&&(r=e+(e-t.points[2]),s=f+(f-t.points[3]));i.push(r,s,h.shift(),h.shift());e=h.shift();f=h.shift();l="C";i.push(e,f);break;case"s":r=e;s=f;t=a[a.length-1];t.command==="C"&&(r=e+(e-t.points[2]),s=f+(f-t.points[3]));i.push(r,s,e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="C";i.push(e,f);break;case"Q":i.push(h.shift(),h.shift());e=h.shift();f=h.shift();i.push(e,f);break;case"q":i.push(e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="Q";i.push(e,f);break;case"T":r=e;s=f;t=a[a.length-1];t.command==="Q"&&(r=e+(e-t.points[0]),s=f+(f-t.points[1]));e=h.shift();f=h.shift();l="Q";i.push(r,s,e,f);break;case"t":r=e;s=f;t=a[a.length-1];t.command==="Q"&&(r=e+(e-t.points[0]),s=f+(f-t.points[1]));e+=h.shift();f+=h.shift();l="Q";i.push(r,s,e,f);break;case"A":t=h.shift();r=h.shift();s=h.shift();n=h.shift();o=h.shift();p=e;q=f;e=h.shift();f=h.shift();l="A";i=this.convertEndpointToCenterParameterization(p,q,e,f,n,o,t,r,s);break;case"a":t=h.shift();r=h.shift();s=h.shift();n=h.shift();o=h.shift();p=e;q=f;e+=h.shift();f+=h.shift();l="A";i=this.convertEndpointToCenterParameterization(p,q,e,f,n,o,t,r,s);break}a.push({command:l||j,points:i,start:{x:k,y:m},pathLength:this.calcLength(k,m,l||j,i)})}(j==="z"||j==="Z")&&a.push({command:"z",points:[],start:void 0,pathLength:0})}return a};c.calcLength=function(a,b,d,e){var f,g,h,i=c;switch(d){case"L":return i.getLineLength(a,b,e[0],e[1]);case"C":d=0;f=i.getPointOnCubicBezier(0,a,b,e[0],e[1],e[2],e[3],e[4],e[5]);for(h=.01;h<=1;h+=.01)g=i.getPointOnCubicBezier(h,a,b,e[0],e[1],e[2],e[3],e[4],e[5]),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;return d;case"Q":d=0;f=i.getPointOnQuadraticBezier(0,a,b,e[0],e[1],e[2],e[3]);for(h=.01;h<=1;h+=.01)g=i.getPointOnQuadraticBezier(h,a,b,e[0],e[1],e[2],e[3]),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;return d;case"A":d=0;a=e[4];b=e[5];var j=e[4]+b,k=Math.PI/180;Math.abs(a-j)j;h-=k)g=i.getPointOnEllipticalArc(e[0],e[1],e[2],e[3],h,0),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;else for(h=a+k;h1&&(g*=Math.sqrt(l),h*=Math.sqrt(l));l=Math.sqrt((g*g*(h*h)-g*g*(k*k)-h*h*(j*j))/(g*g*(k*k)+h*h*(j*j)));e===f&&(l*=-1);isNaN(l)&&(l=0);e=l*g*k/h;l=l*-h*j/g;a=(a+c)/2+Math.cos(i)*e-Math.sin(i)*l;c=(b+d)/2+Math.sin(i)*e+Math.cos(i)*l;var m=function(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1])},n=function(a,b){return(a[0]*b[0]+a[1]*b[1])/(m(a)*m(b))};b=function(a,b){return(a[0]*b[1]=1&&(e=0);f===0&&e>0&&(e=e-2*Math.PI);f===1&&e<0&&(e=e+2*Math.PI);return[a,c,g,h,d,e,i,f]};return c}(d.Shape);Ab.Path=d;d.prototype.className="Path";d.prototype._attrsAffectingSize=["data"];e._registerNode(d);c.Factory.addGetterSetter(d,"data");b.Collection.mapMethods(d)}var Db=!1;function Eb(){Db||(Db=!0,Cb());return Bb.exports}var Fb={},Gb={exports:Fb};function Hb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Fb,"__esModule",{value:!0});Fb.Rect=void 0;var b=p(),c=z(),d=X(),e=k(),f=u();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this.cornerRadius(),c=this.width(),d=this.height();a.beginPath();if(!b)a.rect(0,0,c,d);else{var e=0,f=0,g=0,h=0;typeof b==="number"?e=f=g=h=Math.min(b,c/2,d/2):(e=Math.min(b[0]||0,c/2,d/2),f=Math.min(b[1]||0,c/2,d/2),h=Math.min(b[2]||0,c/2,d/2),g=Math.min(b[3]||0,c/2,d/2));a.moveTo(e,0);a.lineTo(c-f,0);a.arc(c-f,f,f,Math.PI*3/2,0,!1);a.lineTo(c,d-h);a.arc(c-h,d-h,h,0,Math.PI/2,!1);a.lineTo(g,d);a.arc(g,d-g,g,Math.PI/2,Math.PI,!1);a.lineTo(0,e);a.arc(e,e,e,Math.PI,Math.PI*3/2,!1)}a.closePath();a.fillStrokeShape(this)};return c}(d.Shape);Fb.Rect=d;d.prototype.className="Rect";e._registerNode(d);c.Factory.addGetterSetter(d,"cornerRadius",0,f.getNumberOrArrayOfNumbersValidator(4));b.Collection.mapMethods(d)}var Ib=!1;function Jb(){Ib||(Ib=!0,Hb());return Gb.exports}var Kb={},Lb={exports:Kb};function Mb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Kb,"__esModule",{value:!0});Kb.RegularPolygon=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this._getPoints();a.beginPath();a.moveTo(b[0].x,b[0].y);for(var c=1;c=0,c=a.indexOf(""")>=0||a.indexOf(""")>=0;b&&!c&&(a="""+a+""");return a}).join(", ")}var J;function K(){if(J)return J;J=b.Util.createCanvasElement().getContext(n);return J}function L(a){a.fillText(this._partialText,this._partialTextX,this._partialTextY)}function M(a){a.strokeText(this._partialText,this._partialTextX,this._partialTextY)}function N(a){a=a||{};!a.fillLinearGradientColorStops&&!a.fillRadialGradientColorStops&&!a.fillPatternImage&&(a.fill=a.fill||"black");return a}d=function(c){a(d,c);function d(a){a=c.call(this,N(a))||this;a._partialTextX=0;a._partialTextY=0;for(var b=0;b1&&(s+=f)}};d.prototype._hitFunc=function(a){var b=this.getWidth(),c=this.getHeight();a.beginPath();a.rect(0,0,b,c);a.closePath();a.fillStrokeShape(this)};d.prototype.setText=function(a){a=b.Util._isString(a)?a:a===null||a===void 0?"":a+"";this._setAttr(r,a);return this};d.prototype.getWidth=function(){var a=this.attrs.width===i||this.attrs.width===void 0;return a?this.getTextWidth()+this.padding()*2:this.attrs.width};d.prototype.getHeight=function(){var a=this.attrs.height===i||this.attrs.height===void 0;return a?this.fontSize()*this.textArr.length*this.lineHeight()+this.padding()*2:this.attrs.height};d.prototype.getTextWidth=function(){return this.textWidth};d.prototype.getTextHeight=function(){b.Util.warn("text.getTextHeight() method is deprecated. Use text.height() - for full height and text.fontSize() - for one line height.");return this.textHeight};d.prototype.measureSize=function(a){var b=K(),c=this.fontSize();b.save();b.font=this._getContextFont();a=b.measureText(a);b.restore();return{width:a.width,height:c}};d.prototype._getContextFont=function(){return e.Konva.UA.isIE?this.fontStyle()+A+this.fontSize()+y+this.fontFamily():this.fontStyle()+A+this.fontVariant()+A+(this.fontSize()+y)+I(this.fontFamily())};d.prototype._addTextLine=function(a){this.align()===l&&(a=a.trim());var b=this._getTextWidth(a);return this.textArr.push({text:a,width:b})};d.prototype._getTextWidth=function(a){var b=this.letterSpacing(),c=a.length;return K().measureText(a).width+(c?b*(c-1):0)};d.prototype._setTextData=function(){var a=this.text().split(" "),b=+this.fontSize(),c=0,d=this.lineHeight()*b,e=this.attrs.width,f=this.attrs.height,g=e!==i&&e!==void 0,h=f!==i&&f!==void 0,j=this.padding();e=e-j*2;f=f-j*2;j=0;var k=this.wrap(),l=k!==E;k=k!==D&&l;var m=this.ellipsis();this.textArr=[];K().font=this._getContextFont();var n=m?this._getTextWidth(F):0;for(var p=0,q=a.length;pe)while(r.length>0){var t=0,u=r.length,v="",w=0;while(t>>1,y=r.slice(0,x+1),z=this._getTextWidth(y)+n;z<=e?(t=x+1,v=y,w=z):u=x}if(v){if(k){y=r[v.length];z=y===A||y===o;z&&w<=e?x=v.length:x=Math.max(v.lastIndexOf(A),v.lastIndexOf(o))+1;x>0&&(t=x,v=v.slice(0,t),w=this._getTextWidth(v))}v=v.trimRight();this._addTextLine(v);c=Math.max(c,w);j+=d;if(!l||h&&j+d>f){u=this.textArr[this.textArr.length-1];if(u&&m){y=this._getTextWidth(u.text+F)0){s=this._getTextWidth(r);if(s<=e){this._addTextLine(r);j+=d;c=Math.max(c,s);break}}}else break}else this._addTextLine(r),j+=d,c=Math.max(c,s);if(h&&j+d>f)break}this.textHeight=b;this.textWidth=c};d.prototype.getStrokeScaleEnabled=function(){return!0};return d}(d.Shape);$.Text=d;d.prototype._fillFunc=L;d.prototype._strokeFunc=M;d.prototype.className=s;d.prototype._attrsAffectingSize=["text","fontSize","padding","wrap","lineHeight"];g._registerNode(d);c.Factory.overWriteSetter(d,"width",f.getNumberOrAutoValidator());c.Factory.overWriteSetter(d,"height",f.getNumberOrAutoValidator());c.Factory.addGetterSetter(d,"fontFamily","Arial");c.Factory.addGetterSetter(d,"fontSize",12,f.getNumberValidator());c.Factory.addGetterSetter(d,"fontStyle",x);c.Factory.addGetterSetter(d,"fontVariant",x);c.Factory.addGetterSetter(d,"padding",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"align",q);c.Factory.addGetterSetter(d,"verticalAlign",t);c.Factory.addGetterSetter(d,"lineHeight",1,f.getNumberValidator());c.Factory.addGetterSetter(d,"wrap",C);c.Factory.addGetterSetter(d,"ellipsis",!1,f.getBooleanValidator());c.Factory.addGetterSetter(d,"letterSpacing",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"text","",f.getStringValidator());c.Factory.addGetterSetter(d,"textDecoration","");b.Collection.mapMethods(d)}var fc=!1;function gc(){fc||(fc=!0,ec());return dc.exports}var hc={},ic={exports:hc};function jc(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(hc,"__esModule",{value:!0});hc.TextPath=void 0;var b=p(),c=z(),d=X(),e=Eb(),f=gc(),g=u(),h=k(),i="",j="normal";function l(a){a.fillText(this.partialText,0,0)}function m(a){a.strokeText(this.partialText,0,0)}d=function(c){a(d,c);function d(a){var d=c.call(this,a)||this;d.dummyCanvas=b.Util.createCanvasElement();d.dataArray=[];d.dataArray=e.Path.parsePathData(d.attrs.data);d.on("dataChange.konva",function(){this.dataArray=e.Path.parsePathData(this.attrs.data),this._setTextData()});d.on("textChange.konva alignChange.konva letterSpacingChange.konva kerningFuncChange.konva",d._setTextData);a&&a.getKerning&&(b.Util.warn("getKerning TextPath API is deprecated. Please use "kerningFunc" instead."),d.kerningFunc(a.getKerning));d._setTextData();return d}d.prototype._sceneFunc=function(a){a.setAttr("font",this._getContextFont());a.setAttr("textBaseline",this.textBaseline());a.setAttr("textAlign","left");a.save();var b=this.textDecoration(),c=this.fill(),d=this.fontSize(),e=this.glyphInfo;b==="underline"&&a.beginPath();for(var f=0;f=1){var c=b[0].p0;a.moveTo(c.x,c.y)}for(c=0;c0&&(i+=a.dataArray[b].pathLength);b=0;d==="center"&&(b=Math.max(0,i/2-h/2));d==="right"&&(b=Math.max(0,i-h));var j=f.stringToArray(this.text()),k=this.text().split(" ").length-1,l,m,n,o=-1,p=0,q=function(){p=0;var b=a.dataArray;for(var c=o+1;c0){o=c;return b[c]}else b[c].command==="M"&&(l={x:b[c].points[0],y:b[c].points[1]});return{}},r=function(f){var b=a._getTextSize(f).width+c;f===" "&&d==="justify"&&(b+=(i-h)/k);f=0;var g=0;m=void 0;while(Math.abs(b-f)/b>.01&&g<20){g++;var j=f;while(n===void 0)n=q(),n&&j+n.pathLengthb?m=e.Path.getPointOnLine(b,l.x,l.y,n.points[0],n.points[1],l.x,l.y):n=void 0;break;case"A":var o=n.points[4],r=n.points[5],s=n.points[4]+r;p===0?p=o+1e-8:b>f?p+=Math.PI/180*r/Math.abs(r):p-=Math.PI/360*r/Math.abs(r);(r<0&&p=0&&p>s)&&(p=s,j=!0);m=e.Path.getPointOnEllipticalArc(n.points[0],n.points[1],n.points[2],n.points[3],p,n.points[6]);break;case"C":p===0?b>n.pathLength?p=1e-8:p=b/n.pathLength:b>f?p+=(b-f)/n.pathLength/2:p=Math.max(p-(f-b)/n.pathLength/2,0);p>1&&(p=1,j=!0);m=e.Path.getPointOnCubicBezier(p,n.start.x,n.start.y,n.points[0],n.points[1],n.points[2],n.points[3],n.points[4],n.points[5]);break;case"Q":p===0?p=b/n.pathLength:b>f?p+=(b-f)/n.pathLength:p-=(f-b)/n.pathLength;p>1&&(p=1,j=!0);m=e.Path.getPointOnQuadraticBezier(p,n.start.x,n.start.y,n.points[0],n.points[1],n.points[2],n.points[3]);break}m!==void 0&&(f=e.Path.getLineLength(l.x,l.y,m.x,m.y));j&&(j=!1,n=void 0)}},s="C",t=a._getTextSize(s).width+c;b=b/t-1;for(t=0;th.x?-1:1,k=this.findOne(".top-left").y()>h.y?-1:1;e=c*this.cos*j;b=c*this.sin*k;this.findOne(".top-left").x(h.x-e);this.findOne(".top-left").y(h.y-b)}}else if(this._movingAnchorName==="top-center")this.findOne(".top-left").y(d.y());else if(this._movingAnchorName==="top-right"){if(f){h=g?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()};c=Math.sqrt(Math.pow(d.x()-h.x,2)+Math.pow(h.y-d.y(),2));var j=this.findOne(".top-right").x()h.y?-1:1;e=c*this.cos*j;b=c*this.sin*k;this.findOne(".top-right").x(h.x+e);this.findOne(".top-right").y(h.y-b)}var l=d.position();this.findOne(".top-left").y(l.y);this.findOne(".bottom-right").x(l.x)}else if(this._movingAnchorName==="middle-left")this.findOne(".top-left").x(d.x());else if(this._movingAnchorName==="middle-right")this.findOne(".bottom-right").x(d.x());else if(this._movingAnchorName==="bottom-left"){if(f){h=g?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()};c=Math.sqrt(Math.pow(h.x-d.x(),2)+Math.pow(d.y()-h.y,2));var j=h.x=0){var g=f.point({x:-this.padding()*2,y:0});a.x+=g.x;a.y+=g.y;a.width+=this.padding()*2;this._movingAnchorName=this._movingAnchorName.replace("left","right");this._anchorDragOffset.x-=g.x;this._anchorDragOffset.y-=g.y}else if(this._movingAnchorName&&a.width<0&&this._movingAnchorName.indexOf("right")>=0){var g=f.point({x:this.padding()*2,y:0});this._movingAnchorName=this._movingAnchorName.replace("right","left");this._anchorDragOffset.x-=g.x;this._anchorDragOffset.y-=g.y;a.width+=this.padding()*2}if(this._movingAnchorName&&a.height<0&&this._movingAnchorName.indexOf("top")>=0){var g=f.point({x:0,y:-this.padding()*2});a.x+=g.x;a.y+=g.y;this._movingAnchorName=this._movingAnchorName.replace("top","bottom");this._anchorDragOffset.x-=g.x;this._anchorDragOffset.y-=g.y;a.height+=this.padding()*2}else if(this._movingAnchorName&&a.height<0&&this._movingAnchorName.indexOf("bottom")>=0){var g=f.point({x:0,y:this.padding()*2});this._movingAnchorName=this._movingAnchorName.replace("bottom","top");this._anchorDragOffset.x-=g.x;this._anchorDragOffset.y-=g.y;a.height+=this.padding()*2}if(this.boundBoxFunc()){f=this.boundBoxFunc()(e,a);f?a=f:c.Util.warn("boundBoxFunc returned falsy. You should return new bound rect from it!")}g=1e7;f=new c.Transform();f.translate(e.x,e.y);f.rotate(e.rotation);f.scale(e.width/g,e.height/g);e=new c.Transform();e.translate(a.x,a.y);e.rotate(a.rotation);e.scale(a.width/g,a.height/g);var h=e.multiply(f.invert());this._nodes.forEach(function(a){var e=a.getParent().getAbsoluteTransform(),f=a.getTransform().copy();f.translate(a.offsetX(),a.offsetY());var g=new c.Transform();g.multiply(e.copy().invert()).multiply(h).multiply(e).multiply(f);e=g.decompose();a.setAttrs(e);d._fire("transform",{evt:b,target:a});a._fire("transform",{evt:b,target:a});(f=a.getLayer())===null||f===void 0?void 0:f.batchDraw()});this.rotation(c.Util._getRotation(a.rotation));this._resetTransformCache();this.update();this.getLayer().batchDraw()};b.prototype.forceUpdate=function(){this._resetTransformCache(),this.update()};b.prototype._batchChangeChild=function(a,b){a=this.findOne(a);a.setAttrs(b)};b.prototype.update=function(){var a=this,b=this._getNodeRect();this.rotation(c.Util._getRotation(b.rotation));var d=b.width;b=b.height;var e=this.enabledAnchors(),f=this.resizeEnabled(),g=this.padding(),h=this.anchorSize();this.find("._anchor").each(function(b){b.setAttrs({width:h,height:h,offsetX:h/2,offsetY:h/2,stroke:a.anchorStroke(),strokeWidth:a.anchorStrokeWidth(),fill:a.anchorFill(),cornerRadius:a.anchorCornerRadius()})});this._batchChangeChild(".top-left",{x:0,y:0,offsetX:h/2+g,offsetY:h/2+g,visible:f&&e.indexOf("top-left")>=0});this._batchChangeChild(".top-center",{x:d/2,y:0,offsetY:h/2+g,visible:f&&e.indexOf("top-center")>=0});this._batchChangeChild(".top-right",{x:d,y:0,offsetX:h/2-g,offsetY:h/2+g,visible:f&&e.indexOf("top-right")>=0});this._batchChangeChild(".middle-left",{x:0,y:b/2,offsetX:h/2+g,visible:f&&e.indexOf("middle-left")>=0});this._batchChangeChild(".middle-right",{x:d,y:b/2,offsetX:h/2-g,visible:f&&e.indexOf("middle-right")>=0});this._batchChangeChild(".bottom-left",{x:0,y:b,offsetX:h/2+g,offsetY:h/2-g,visible:f&&e.indexOf("bottom-left")>=0});this._batchChangeChild(".bottom-center",{x:d/2,y:b,offsetY:h/2-g,visible:f&&e.indexOf("bottom-center")>=0});this._batchChangeChild(".bottom-right",{x:d,y:b,offsetX:h/2-g,offsetY:h/2-g,visible:f&&e.indexOf("bottom-right")>=0});this._batchChangeChild(".rotater",{x:d/2,y:-this.rotateAnchorOffset()*c.Util._sign(b)-g,visible:this.rotateEnabled()});this._batchChangeChild(".back",{width:d,height:b,visible:this.borderEnabled(),stroke:this.borderStroke(),strokeWidth:this.borderStrokeWidth(),dash:this.borderDash(),x:0,y:0});(f=this.getLayer())===null||f===void 0?void 0:f.batchDraw()};b.prototype.isTransforming=function(){return this._transforming};b.prototype.stopTransform=function(){if(this._transforming){this._removeEvents();var a=this.findOne("."+this._movingAnchorName);a&&a.stopDrag()}};b.prototype.destroy=function(){this.getStage()&&this._cursorChange&&(this.getStage().content.style.cursor="");h.Group.prototype.destroy.call(this);this.detach();this._removeEvents();return this};b.prototype.toObject=function(){return e.Node.prototype.toObject.call(this)};return b}(h.Group);mc.Transformer=C;function D(a){a instanceof Array||c.Util.warn("enabledAnchors value should be an array");a instanceof Array&&a.forEach(function(a){v.indexOf(a)===-1&&c.Util.warn("Unknown anchor name: "+a+". Available names are: "+v.join(", "))});return a||[]}C.prototype.className="Transformer";l._registerNode(C);d.Factory.addGetterSetter(C,"enabledAnchors",v,D);d.Factory.addGetterSetter(C,"resizeEnabled",!0);d.Factory.addGetterSetter(C,"anchorSize",10,j.getNumberValidator());d.Factory.addGetterSetter(C,"rotateEnabled",!0);d.Factory.addGetterSetter(C,"rotationSnaps",[]);d.Factory.addGetterSetter(C,"rotateAnchorOffset",50,j.getNumberValidator());d.Factory.addGetterSetter(C,"rotationSnapTolerance",5,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderEnabled",!0);d.Factory.addGetterSetter(C,"anchorStroke","rgb(0, 161, 255)");d.Factory.addGetterSetter(C,"anchorStrokeWidth",1,j.getNumberValidator());d.Factory.addGetterSetter(C,"anchorFill","white");d.Factory.addGetterSetter(C,"anchorCornerRadius",0,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderStroke","rgb(0, 161, 255)");d.Factory.addGetterSetter(C,"borderStrokeWidth",1,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderDash");d.Factory.addGetterSetter(C,"keepRatio",!0);d.Factory.addGetterSetter(C,"centeredScaling",!1);d.Factory.addGetterSetter(C,"ignoreStroke",!1);d.Factory.addGetterSetter(C,"padding",0,j.getNumberValidator());d.Factory.addGetterSetter(C,"node");d.Factory.addGetterSetter(C,"nodes");d.Factory.addGetterSetter(C,"boundBoxFunc");d.Factory.addGetterSetter(C,"shouldOverdrawWholeArea",!1);d.Factory.backCompat(C,{lineEnabled:"borderEnabled",rotateHandlerOffset:"rotateAnchorOffset",enabledHandlers:"enabledAnchors"});c.Collection.mapMethods(C)}var pc=!1;function qc(){pc||(pc=!0,oc());return nc.exports}var rc={},sc={exports:rc};function tc(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(rc,"__esModule",{value:!0});rc.Wedge=void 0;var b=p(),c=z(),d=X(),e=k(),f=u(),g=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){a.beginPath(),a.arc(0,0,this.radius(),0,e.Konva.getAngle(this.angle()),this.clockwise()),a.lineTo(0,0),a.closePath(),a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radius()*2};c.prototype.getHeight=function(){return this.radius()*2};c.prototype.setWidth=function(a){this.radius(a/2)};c.prototype.setHeight=function(a){this.radius(a/2)};return c}(d.Shape);rc.Wedge=d;d.prototype.className="Wedge";d.prototype._centroid=!0;d.prototype._attrsAffectingSize=["radius"];g._registerNode(d);c.Factory.addGetterSetter(d,"radius",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"angle",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"clockwise",!1);c.Factory.backCompat(d,{angleDeg:"angle",getAngleDeg:"getAngle",setAngleDeg:"setAngle"});b.Collection.mapMethods(d)}var uc=!1;function vc(){uc||(uc=!0,tc());return sc.exports}var wc={},xc={exports:wc};function yc(){Object.defineProperty(wc,"__esModule",{value:!0});wc.Blur=void 0;var a=z(),b=R(),c=u();function d(){this.r=0,this.g=0,this.b=0,this.a=0,this.next=null}var e=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],f=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function g(a,b){var c=a.data,g=a.width;a=a.height;var h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=b+b+1,E=g-1,F=a-1,G=b+1,H=G*(G+1)/2,I=new d(),J=null,K=I,L,M,N=e[b],O=f[b];for(j=1;j>O,B!==0?(B=255/B,c[D]=(m*N>>O)*B,c[D+1]=(n*N>>O)*B,c[D+2]=(o*N>>O)*B):c[D]=c[D+1]=c[D+2]=0,m-=q,n-=r,o-=s,p-=t,q-=L.r,r-=L.g,s-=L.b,t-=L.a,k=l+((k=h+b+1)>O,B>0?(B=255/B,c[k]=(m*N>>O)*B,c[k+1]=(n*N>>O)*B,c[k+2]=(o*N>>O)*B):c[k]=c[k+1]=c[k+2]=0,m-=q,n-=r,o-=s,p-=t,q-=L.r,r-=L.g,s-=L.b,t-=L.a,k=h+((k=i+G)0&&g(a,b)};wc.Blur=h;a.Factory.addGetterSetter(b.Node,"blurRadius",0,c.getNumberValidator(),a.Factory.afterSetFilter)}var zc=!1;function Ac(){zc||(zc=!0,yc());return xc.exports}var Bc={},Cc={exports:Bc};function Dc(){Object.defineProperty(Bc,"__esModule",{value:!0});Bc.Brighten=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=this.brightness()*255;a=a.data;var c=a.length,d;for(d=0;d255?255:d,e=e<0?0:e>255?255:e,f=f<0?0:f>255?255:f,a[g]=d,a[g+1]=e,a[g+2]=f};Gc.Contrast=d;a.Factory.addGetterSetter(b.Node,"contrast",0,c.getNumberValidator(),a.Factory.afterSetFilter)}var Jc=!1;function Kc(){Jc||(Jc=!0,Ic());return Hc.exports}var Lc={},Mc={exports:Lc};function Nc(){Object.defineProperty(Lc,"__esModule",{value:!0});Lc.Emboss=void 0;var a=z(),b=R(),c=p(),d=u(),e=function(a){var b=this.embossStrength()*10,d=this.embossWhiteLevel()*255,e=this.embossDirection(),f=this.embossBlend(),g=0,h=0,i=a.data,j=a.width;a=a.height;var k=j*4,l=a;switch(e){case"top-left":g=-1;h=-1;break;case"top":g=-1;h=0;break;case"top-right":g=-1;h=1;break;case"right":g=0;h=1;break;case"bottom-right":g=1;h=1;break;case"bottom":g=1;h=0;break;case"bottom-left":g=1;h=-1;break;case"left":g=0;h=-1;break;default:c.Util.error("Unknown emboss direction: "+e)}do{e=(l-1)*k;var m=g;l+m<1&&(m=0);l+m>a&&(m=0);m=(l-1+m)*j*4;var n=j;do{var o=e+(n-1)*4,p=h;n+p<1&&(p=0);n+p>j&&(p=0);p=m+(n-1+p)*4;var q=i[o]-i[p],r=i[o+1]-i[p+1];p=i[o+2]-i[p+2];q=q;var s=q>0?q:-q,t=r>0?r:-r,u=p>0?p:-p;t>s&&(q=r);u>s&&(q=p);q*=b;if(f){t=i[o]+q;r=i[o+1]+q;u=i[o+2]+q;i[o]=t>255?255:t<0?0:t;i[o+1]=r>255?255:r<0?0:r;i[o+2]=u>255?255:u<0?0:u}else{s=d-q;s<0?s=0:s>255&&(s=255);i[o]=i[o+1]=i[o+2]=s}}while(--n)}while(--l)};Lc.Emboss=e;a.Factory.addGetterSetter(b.Node,"embossStrength",.5,d.getNumberValidator(),a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossWhiteLevel",.5,d.getNumberValidator(),a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossDirection","top-left",null,a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossBlend",!1,null,a.Factory.afterSetFilter)}var Oc=!1;function Pc(){Oc||(Oc=!0,Nc());return Mc.exports}var Qc={},Rc={exports:Qc};function Sc(){Object.defineProperty(Qc,"__esModule",{value:!0});Qc.Enhance=void 0;var a=z(),b=R(),c=u();function d(a,b,c,d,e){c=c-b;e=e-d;if(c===0)return d+e/2;if(e===0)return d;a=(a-b)/c;a=e*a+d;return a}var e=function(a){a=a.data;var b=a.length,c=a[0],e=c,f,g=a[1],h=g,i=a[2],j=i,k,l=this.enhance();if(l===0)return;for(k=0;ke&&(e=f),f=a[k+1],fh&&(h=f),f=a[k+2],fj&&(j=f);e===c&&(e=255,c=0);h===g&&(h=255,g=0);j===i&&(j=255,i=0);var m,n,o,p,q,r;l>0?(f=e+l*(255-e),n=c-l*(c-0),o=h+l*(255-h),p=g-l*(g-0),q=j+l*(255-j),r=i-l*(i-0)):(m=(e+c)*.5,f=e+l*(e-m),n=c+l*(c-m),m=(h+g)*.5,o=h+l*(h-m),p=g+l*(g-m),m=(j+i)*.5,q=j+l*(j-m),r=i+l*(i-m));for(k=0;kn?m:n;m=a;a=e;var o,p,q=360/a*Math.PI/180,r,s;for(p=0;po?n:o;n=a;var p=e,q;c=c.polarRotation||0;for(h=0;hb&&(t=s,u=0,v=-1);for(h=0;h=0&&n=0&&o=0&&n=0&&o=255*4?255:0}return g}function k(a,b,c){var d=[1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9],e=Math.round(Math.sqrt(d.length)),f=Math.floor(e/2),g=[];for(var h=0;h=0&&n=0&&o=d)continue;for(g=q;g=e)continue;h=(d*g+f)*4;i+=a[h+0];j+=a[h+1];k+=a[h+2];l+=a[h+3];u+=1}}i=i/u;j=j/u;k=k/u;l=l/u;for(f=o;f=d)continue;for(g=q;g=e)continue;h=(d*g+f)*4;a[h+0]=i;a[h+1]=j;a[h+2]=k;a[h+3]=l}}}};Dd.Pixelate=e;a.Factory.addGetterSetter(c.Node,"pixelSize",8,d.getNumberValidator(),a.Factory.afterSetFilter)}var Gd=!1;function Hd(){Gd||(Gd=!0,Fd());return Ed.exports}var Id={},Jd={exports:Id};function Kd(){Object.defineProperty(Id,"__esModule",{value:!0});Id.Posterize=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=Math.round(this.levels()*254)+1;a=a.data;var c=a.length;b=255/b;var d;for(d=0;d255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"green",0,function(a){this._filterUpToDate=!1;if(a>255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"blue",0,c.RGBComponent,a.Factory.afterSetFilter)}var Qd=!1;function Rd(){Qd||(Qd=!0,Pd());return Od.exports}var Sd={},Td={exports:Sd};function Ud(){Object.defineProperty(Sd,"__esModule",{value:!0});Sd.RGBA=void 0;var a=z(),b=R(),c=u(),d=function(a){a=a.data;var b=a.length,c=this.red(),d=this.green(),e=this.blue(),f=this.alpha(),g,h;for(g=0;g255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"green",0,function(a){this._filterUpToDate=!1;if(a>255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"blue",0,c.RGBComponent,a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"alpha",1,function(a){this._filterUpToDate=!1;if(a>1)return 1;else if(a<0)return 0;else return a})}var Vd=!1;function Wd(){Vd||(Vd=!0,Ud());return Td.exports}var Xd={},Yd={exports:Xd};function Zd(){Object.defineProperty(Xd,"__esModule",{value:!0});Xd.Sepia=void 0;var a=function(a){a=a.data;var b=a.length,c,d,e,f;for(c=0;c127&&(h=255-h);i>127&&(i=255-i);j>127&&(j=255-j);b[g]=h;b[g+1]=i;b[g+2]=j}while(--f)}while(--a)};be.Solarize=a}var ee=!1;function fe(){ee||(ee=!0,de());return ce.exports}var ge={},he={exports:ge};function ie(){Object.defineProperty(ge,"__esModule",{value:!0});ge.Threshold=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=this.threshold()*255;a=a.data;var c=a.length,d;for(d=0;d-----
three.r137_mod",[],(function $module_three_r137_mod(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "137"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.image = image; this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { 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 (this.image !== undefined) { // TODO: Move to THREE.Image const image = this.image; if (image.uuid === undefined) { image.uuid = generateUUID(); // UGH } if (!isRootObject && meta.images[image.uuid] === undefined) { let url; if (Array.isArray(image)) { // process array of images e.g. CubeTexture url = []; for (let i = 0, l = image.length; i < l; i++) { // check cube texture with data textures if (image[i].isDataTexture) { url.push(serializeImage(image[i].image)); } else { url.push(serializeImage(image[i])); } } } else { // process single image url = serializeImage(image); } meta.images[image.uuid] = { uuid: image.uuid, url: url }; } output.image = image.uuid; } if (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) this.version++; } } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); this.texture = new Texture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.image = { width: width, height: height, depth: 1 }; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } setTexture(texture) { texture.image = { width: this.width, height: this.height, depth: this.depth }; this.texture = texture; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count) { super(width, height); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class WebGLMultisampleRenderTarget extends WebGLRenderTarget { constructor(width, height, options = {}) { super(width, height, options); this.samples = 4; this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true; this.useRenderToTexture = options.useRenderToTexture !== undefined ? options.useRenderToTexture : false; this.useRenderbuffer = this.useRenderToTexture === false; } copy(source) { super.copy.call(this, source); this.samples = source.samples; this.useRenderToTexture = source.useRenderToTexture; this.useRenderbuffer = source.useRenderbuffer; return this; } } WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } toVector3(optionalResult) { if (optionalResult) { return optionalResult.set(this._x, this._y, this._z); } else { return new Vector3(this._x, this._y, this._z); } } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRoot = meta === undefined || typeof meta === "string"; if (isRoot) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRoot) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (attributes.tangent === undefined) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = attributes.tangent.array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options, dummy) { if (Number.isInteger(options)) { console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"); options = dummy; } super(size, size, options); options = options || {}; // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture = new CubeTexture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type = gl.FLOAT; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Float64Array) { console.warn("THREE.WebGLAttributes: Unsupported data buffer format: Float64Array."); } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { console.warn("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_maxMipLevel 8.0 #define cubeUV_minMipLevel 4.0 #define cubeUV_maxTileSize 256.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); float texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; if ( mipInt < cubeUV_maxMipLevel ) { uv.y += 2.0 * cubeUV_maxTileSize; } uv.y += filterInt * 2.0 * cubeUV_minTileSize; uv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize ); uv *= texelSize; return texture2D( envMap, uv ).rgb; } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1, 2 ) * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec3 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset, const in int stride ) { float texelIndex = float( vertexIndex * stride + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ).xyz; } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #ifndef USE_MORPHNORMALS if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 1 ) * morphTargetInfluences[ i ]; #else if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 2 ) * morphTargetInfluences[ i ]; #endif } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef TEXTURE_LOD_EXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; var uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data && data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; const LOD_MIN = 4; const LOD_MAX = 8; const SIZE_MAX = Math.pow(2, LOD_MAX); // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/_createPlanes(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._blurMaterial = _getBlurShader(MAX_SAMPLES); this._equirectShader = null; this._cubemapShader = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = this._allocateTargets(); this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); this._compileMaterial(this._cubemapShader); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); this._compileMaterial(this._equirectShader); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); if (this._cubemapShader !== null) this._cubemapShader.dispose(); if (this._equirectShader !== null) this._equirectShader.dispose(); for (let i = 0; i < _lodPlanes.length; i++) { _lodPlanes[i].dispose(); } } // private interface _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(texture); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets(texture) { // warning: null texture is valid const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(params); cubeUVRenderTarget.depthBuffer = texture ? false : true; if (this._pingPongRenderTarget === null) { this._pingPongRenderTarget = _createRenderTarget(params); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(_lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } _setViewport(cubeUVRenderTarget, col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); } this._cubemapShader.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); } } const material = isCubeTexture ? this._cubemapShader : this._equirectShader; const mesh = new Mesh(_lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; if (!isCubeTexture) { uniforms["texelSize"].value.set(1.0 / texture.image.width, 1.0 / texture.image.height); } _setViewport(cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < TOTAL_LODS; i++) { const sigma = Math.sqrt(_sigmas[i] * _sigmas[i] - _sigmas[i - 1] * _sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(_lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = _sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = LOD_MAX - lodIn; const outputSize = _sizeLods[lodOut]; const x = 3 * Math.max(0, SIZE_MAX - 2 * outputSize); const y = (lodOut === 0 ? 0 : 2 * SIZE_MAX) + 2 * outputSize * (lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes() { const _lodPlanes = []; const _sizeLods = []; const _sigmas = []; let lod = LOD_MAX; for (let i = 0; i < TOTAL_LODS; i++) { const sizeLod = Math.pow(2, lod); _sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > LOD_MAX - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - LOD_MAX + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } _sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); _lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { _lodPlanes, _sizeLods, _sigmas }; } function _createRenderTarget(params) { const cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(maxSamples) { const weights = new Float32Array(maxSamples); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new RawShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": maxSamples }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectShader() { const texelSize = new Vector2(1, 1); const shaderMaterial = new RawShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null }, "texelSize": { value: texelSize } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; #include void main() { gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); vec2 f = fract( uv / texelSize - 0.5 ); uv -= f * texelSize; vec3 tl = texture2D ( envMap, uv ).rgb; uv.x += texelSize.x; vec3 tr = texture2D ( envMap, uv ).rgb; uv.y += texelSize.y; vec3 br = texture2D ( envMap, uv ).rgb; uv.x -= texelSize.x; vec3 bl = texture2D ( envMap, uv ).rgb; vec3 tm = mix( tl, tr, f.x ); vec3 bm = mix( bl, br, f.x ); gl_FragColor.rgb = mix( tm, bm, f.y ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCubemapShader() { const shaderMaterial = new RawShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } class DataTexture2DArray extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture2DArray.prototype.isDataTexture2DArray = true; function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector3(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const numberOfMorphTargets = geometry.morphAttributes.position.length; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== numberOfMorphTargets) { if (entry !== undefined) entry.texture.dispose(); const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal || []; const numberOfVertices = geometry.attributes.position.count; const numberOfVertexData = hasMorphNormals === true ? 2 : 1; // (v,n) vs. (v) let width = numberOfVertices * numberOfVertexData; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * numberOfMorphTargets); const texture = new DataTexture2DArray(buffer, width, height, numberOfMorphTargets); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = numberOfVertexData * 4; for (let i = 0; i < numberOfMorphTargets; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); const stride = j * vertexDataStride; buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } } } entry = { count: numberOfMorphTargets, texture: texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update: update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose }; } class DataTexture3D extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture3D.prototype.isDataTexture3D = true; /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyTexture2dArray = new DataTexture2DArray(); const emptyTexture3d = new DataTexture3D(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || emptyTexture3d, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyTexture2dArray, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || emptyTexture3d, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyTexture2dArray, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.transparent ? "" : "#define OPAQUE", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2: isWebGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUV: !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping), lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, transparent: material.transparent, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!object.geometry && !!object.geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!object.geometry && !!object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones: maxBones, useVertexTexture: floatVertexTextures, morphTargets: !!object.geometry && !!object.geometry.morphAttributes.position, morphNormals: !!object.geometry && !!object.geometry.morphAttributes.normal, morphTargetsCount: !!object.geometry && !!object.geometry.morphAttributes.position ? object.geometry.morphAttributes.position.length : 0, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.envMapCubeUV) _programLayers.enable(7); if (parameters.lightMap) _programLayers.enable(8); if (parameters.aoMap) _programLayers.enable(9); if (parameters.emissiveMap) _programLayers.enable(10); if (parameters.bumpMap) _programLayers.enable(11); if (parameters.normalMap) _programLayers.enable(12); if (parameters.objectSpaceNormalMap) _programLayers.enable(13); if (parameters.tangentSpaceNormalMap) _programLayers.enable(14); if (parameters.clearcoat) _programLayers.enable(15); if (parameters.clearcoatMap) _programLayers.enable(16); if (parameters.clearcoatRoughnessMap) _programLayers.enable(17); if (parameters.clearcoatNormalMap) _programLayers.enable(18); if (parameters.displacementMap) _programLayers.enable(19); if (parameters.specularMap) _programLayers.enable(20); if (parameters.roughnessMap) _programLayers.enable(21); if (parameters.metalnessMap) _programLayers.enable(22); if (parameters.gradientMap) _programLayers.enable(23); if (parameters.alphaMap) _programLayers.enable(24); if (parameters.alphaTest) _programLayers.enable(25); if (parameters.vertexColors) _programLayers.enable(26); if (parameters.vertexAlphas) _programLayers.enable(27); if (parameters.vertexUvs) _programLayers.enable(28); if (parameters.vertexTangents) _programLayers.enable(29); if (parameters.uvsVertexOnly) _programLayers.enable(30); if (parameters.fog) _programLayers.enable(31); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.premultipliedAlpha) _programLayers.enable(7); if (parameters.shadowMapEnabled) _programLayers.enable(8); if (parameters.physicallyCorrectLights) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.depthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.specularIntensityMap) _programLayers.enable(14); if (parameters.specularColorMap) _programLayers.enable(15); if (parameters.transmission) _programLayers.enable(16); if (parameters.transmissionMap) _programLayers.enable(17); if (parameters.thicknessMap) _programLayers.enable(18); if (parameters.sheen) _programLayers.enable(19); if (parameters.sheenColorMap) _programLayers.enable(20); if (parameters.sheenRoughnessMap) _programLayers.enable(21); if (parameters.decodeVideoTexture) _programLayers.enable(22); if (parameters.transparent) _programLayers.enable(23); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, texImage3D: texImage3D, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const hasMultisampledRenderToTexture = extensions.has("WEBGL_multisampled_render_to_texture"); const MultisampledRenderToTextureExtension = hasMultisampledRenderToTexture ? extensions.get("WEBGL_multisampled_render_to_texture") : undefined; const _videoTextures = new WeakMap(); let _canvas; // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } info.memory.textures--; } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; _gl.deleteTexture(textureProperties.__webglTexture); properties.remove(texture); } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (!renderTarget) return; if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === undefined) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined"); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataTexture2DArray) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isDataTexture3D) textureType = _gl.TEXTURE_3D; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataTexture2DArray) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isDataTexture3D) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture && (texture.isCompressedTexture || texture.image[0].isCompressedTexture); const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || renderTarget.useRenderToTexture) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (renderTarget.useRenderbuffer) { if (isWebGL2) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (isRenderTarget3D) { // Render targets containing layers, i.e: Texture 3D and 2d arrays if (isWebGL2) { const isTexture3D = texture.isDataTexture3D; glTextureType = isTexture3D ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.useRenderbuffer) { if (isWebGL2) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } if (!renderTarget.ignoreDepthForMultisampleCopy) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } const renderTargetProperties = properties.get(renderTarget); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (renderTarget.ignoreDepthForMultisampleCopy) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } function getRenderTargetSamples(renderTarget) { return isWebGL2 && (renderTarget.useRenderbuffer || renderTarget.useRenderToTexture) ? Math.min(maxSamples, renderTarget.samples) : 0; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // backwards compatibility let warnedTexture2D = false; let warnedTextureCube = false; function safeSetTexture2D(texture, slot) { if (texture && texture.isWebGLRenderTarget) { if (warnedTexture2D === false) { console.warn("THREE.WebGLTextures.safeSetTexture2D: don"t use render targets as textures. Use their .texture property instead."); warnedTexture2D = true; } texture = texture.texture; } setTexture2D(texture, slot); } function safeSetTextureCube(texture, slot) { if (texture && texture.isWebGLCubeRenderTarget) { if (warnedTextureCube === false) { console.warn("THREE.WebGLTextures.safeSetTextureCube: don"t use cube render targets as textures. Use their .texture property instead."); warnedTextureCube = true; } texture = texture.texture; } setTextureCube(texture, slot); } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.safeSetTexture2D = safeSetTexture2D; this.safeSetTextureCube = safeSetTextureCube; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; const hasMultisampledRenderToTexture = renderer.extensions.has("WEBGL_multisampled_render_to_texture"); let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let isMultisample = false; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { isMultisample = attributes.antialias; let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); if (isMultisample) { newRenderTarget = new WebGLMultisampleRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, useRenderToTexture: hasMultisampledRenderToTexture, encoding: renderer.outputEncoding }); } else { newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, encoding: renderer.outputEncoding }); } } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; // ***Custom Modification*** // Needed to get references to GL Buffers that will be used with // compute shaders run in WebGL"s TransformFeedback _this.attributes = attributes; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color, depth, stencil) { let bits = 0; if (color === undefined || color) bits |= _gl.COLOR_BUFFER_BIT; if (depth === undefined || depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil === undefined || stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); } function renderTransmissionPass(opaqueObjects, scene, camera) { if (_transmissionRenderTarget === null) { const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; _transmissionRenderTarget = new renderTargetType(1024, 1024, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, magFilter: NearestFilter, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, useRenderToTexture: extensions.has("WEBGL_multisampled_render_to_texture") }); } const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphTargetsCount = !!geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } if (!!geometry && (geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined)) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (renderTarget.useRenderToTexture) { console.warn("render-to-texture extension was disabled because an external texture was provided"); renderTarget.useRenderToTexture = false; renderTarget.useRenderbuffer = true; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isDataTexture3D || texture.isDataTexture2DArray) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (renderTarget.useRenderbuffer) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isDataTexture3D) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataTexture2DArray) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isDataTexture3D) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, object: object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter; this.minFilter = minFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV: function (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1, hash2); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; var Materials = /*#__PURE__*/Object.freeze({ __proto__: null, ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, MeshMatcapMaterial: MeshMatcapMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray: function (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray: function (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder: function (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip: function (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive: function (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * (fps || 1.0); } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } if (typeof ReadableStream === "undefined" || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.substr(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = new Materials[json.type](); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.substr(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = await deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } let texture; const image = images[data.image]; if (Array.isArray(image)) { texture = new CubeTexture(image); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(image.data, image.width, image.height); } else { texture = new Texture(image); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext: function () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext: function (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (!matches) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (!nodeName || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera && camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera && camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object && object.isBone) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; const toChange = []; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange.push({ froms: sIdx, tos: s2Idx, hole: hIdx }); if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } // console.log("ambiguous: ", ambiguous); if (toChange.length > 0) { // console.log("to change: ", toChange); if (!ambiguous) newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add: function /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get: function /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get: function () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set: function (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, 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."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get: function () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, 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."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set: function () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set: function (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set: function (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set: function (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get: function () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get: function () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set: function /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get: function () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get: function () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.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 () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get: function () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set: function (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get: function () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set: function (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get: function () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set: function (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get: function () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get: function () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set: function (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* cullFace */ () { 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."); return undefined; }, 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."); return undefined; }, set: function () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get: function () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get: function () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get: function () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject: function /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach: function /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach: function /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
three.r137",[],(function $module_three_r137(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "137"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.image = image; this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { 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 (this.image !== undefined) { // TODO: Move to THREE.Image const image = this.image; if (image.uuid === undefined) { image.uuid = generateUUID(); // UGH } if (!isRootObject && meta.images[image.uuid] === undefined) { let url; if (Array.isArray(image)) { // process array of images e.g. CubeTexture url = []; for (let i = 0, l = image.length; i < l; i++) { // check cube texture with data textures if (image[i].isDataTexture) { url.push(serializeImage(image[i].image)); } else { url.push(serializeImage(image[i])); } } } else { // process single image url = serializeImage(image); } meta.images[image.uuid] = { uuid: image.uuid, url: url }; } output.image = image.uuid; } if (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) this.version++; } } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); this.texture = new Texture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.image = { width: width, height: height, depth: 1 }; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } setTexture(texture) { texture.image = { width: this.width, height: this.height, depth: this.depth }; this.texture = texture; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count) { super(width, height); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class WebGLMultisampleRenderTarget extends WebGLRenderTarget { constructor(width, height, options = {}) { super(width, height, options); this.samples = 4; this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true; this.useRenderToTexture = options.useRenderToTexture !== undefined ? options.useRenderToTexture : false; this.useRenderbuffer = this.useRenderToTexture === false; } copy(source) { super.copy.call(this, source); this.samples = source.samples; this.useRenderToTexture = source.useRenderToTexture; this.useRenderbuffer = source.useRenderbuffer; return this; } } WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } toVector3(optionalResult) { if (optionalResult) { return optionalResult.set(this._x, this._y, this._z); } else { return new Vector3(this._x, this._y, this._z); } } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRoot = meta === undefined || typeof meta === "string"; if (isRoot) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRoot) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (attributes.tangent === undefined) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = attributes.tangent.array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options, dummy) { if (Number.isInteger(options)) { console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"); options = dummy; } super(size, size, options); options = options || {}; // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture = new CubeTexture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type = gl.FLOAT; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Float64Array) { console.warn("THREE.WebGLAttributes: Unsupported data buffer format: Float64Array."); } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { console.warn("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_maxMipLevel 8.0 #define cubeUV_minMipLevel 4.0 #define cubeUV_maxTileSize 256.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); float texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; if ( mipInt < cubeUV_maxMipLevel ) { uv.y += 2.0 * cubeUV_maxTileSize; } uv.y += filterInt * 2.0 * cubeUV_minTileSize; uv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize ); uv *= texelSize; return texture2D( envMap, uv ).rgb; } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1, 2 ) * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec3 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset, const in int stride ) { float texelIndex = float( vertexIndex * stride + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ).xyz; } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #ifndef USE_MORPHNORMALS if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 1 ) * morphTargetInfluences[ i ]; #else if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 2 ) * morphTargetInfluences[ i ]; #endif } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef TEXTURE_LOD_EXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; var uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data && data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; const LOD_MIN = 4; const LOD_MAX = 8; const SIZE_MAX = Math.pow(2, LOD_MAX); // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/_createPlanes(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._blurMaterial = _getBlurShader(MAX_SAMPLES); this._equirectShader = null; this._cubemapShader = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = this._allocateTargets(); this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); this._compileMaterial(this._cubemapShader); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); this._compileMaterial(this._equirectShader); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); if (this._cubemapShader !== null) this._cubemapShader.dispose(); if (this._equirectShader !== null) this._equirectShader.dispose(); for (let i = 0; i < _lodPlanes.length; i++) { _lodPlanes[i].dispose(); } } // private interface _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(texture); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets(texture) { // warning: null texture is valid const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(params); cubeUVRenderTarget.depthBuffer = texture ? false : true; if (this._pingPongRenderTarget === null) { this._pingPongRenderTarget = _createRenderTarget(params); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(_lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } _setViewport(cubeUVRenderTarget, col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); } this._cubemapShader.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); } } const material = isCubeTexture ? this._cubemapShader : this._equirectShader; const mesh = new Mesh(_lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; if (!isCubeTexture) { uniforms["texelSize"].value.set(1.0 / texture.image.width, 1.0 / texture.image.height); } _setViewport(cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < TOTAL_LODS; i++) { const sigma = Math.sqrt(_sigmas[i] * _sigmas[i] - _sigmas[i - 1] * _sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(_lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = _sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = LOD_MAX - lodIn; const outputSize = _sizeLods[lodOut]; const x = 3 * Math.max(0, SIZE_MAX - 2 * outputSize); const y = (lodOut === 0 ? 0 : 2 * SIZE_MAX) + 2 * outputSize * (lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes() { const _lodPlanes = []; const _sizeLods = []; const _sigmas = []; let lod = LOD_MAX; for (let i = 0; i < TOTAL_LODS; i++) { const sizeLod = Math.pow(2, lod); _sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > LOD_MAX - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - LOD_MAX + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } _sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); _lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { _lodPlanes, _sizeLods, _sigmas }; } function _createRenderTarget(params) { const cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(maxSamples) { const weights = new Float32Array(maxSamples); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new RawShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": maxSamples }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectShader() { const texelSize = new Vector2(1, 1); const shaderMaterial = new RawShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null }, "texelSize": { value: texelSize } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; #include void main() { gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); vec2 f = fract( uv / texelSize - 0.5 ); uv -= f * texelSize; vec3 tl = texture2D ( envMap, uv ).rgb; uv.x += texelSize.x; vec3 tr = texture2D ( envMap, uv ).rgb; uv.y += texelSize.y; vec3 br = texture2D ( envMap, uv ).rgb; uv.x -= texelSize.x; vec3 bl = texture2D ( envMap, uv ).rgb; vec3 tm = mix( tl, tr, f.x ); vec3 bm = mix( bl, br, f.x ); gl_FragColor.rgb = mix( tm, bm, f.y ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCubemapShader() { const shaderMaterial = new RawShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } class DataTexture2DArray extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture2DArray.prototype.isDataTexture2DArray = true; function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector3(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const numberOfMorphTargets = geometry.morphAttributes.position.length; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== numberOfMorphTargets) { if (entry !== undefined) entry.texture.dispose(); const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal || []; const numberOfVertices = geometry.attributes.position.count; const numberOfVertexData = hasMorphNormals === true ? 2 : 1; // (v,n) vs. (v) let width = numberOfVertices * numberOfVertexData; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * numberOfMorphTargets); const texture = new DataTexture2DArray(buffer, width, height, numberOfMorphTargets); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = numberOfVertexData * 4; for (let i = 0; i < numberOfMorphTargets; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); const stride = j * vertexDataStride; buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } } } entry = { count: numberOfMorphTargets, texture: texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update: update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose }; } class DataTexture3D extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture3D.prototype.isDataTexture3D = true; /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyTexture2dArray = new DataTexture2DArray(); const emptyTexture3d = new DataTexture3D(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || emptyTexture3d, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyTexture2dArray, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || emptyTexture3d, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyTexture2dArray, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.transparent ? "" : "#define OPAQUE", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2: isWebGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUV: !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping), lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, transparent: material.transparent, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!object.geometry && !!object.geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!object.geometry && !!object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones: maxBones, useVertexTexture: floatVertexTextures, morphTargets: !!object.geometry && !!object.geometry.morphAttributes.position, morphNormals: !!object.geometry && !!object.geometry.morphAttributes.normal, morphTargetsCount: !!object.geometry && !!object.geometry.morphAttributes.position ? object.geometry.morphAttributes.position.length : 0, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.envMapCubeUV) _programLayers.enable(7); if (parameters.lightMap) _programLayers.enable(8); if (parameters.aoMap) _programLayers.enable(9); if (parameters.emissiveMap) _programLayers.enable(10); if (parameters.bumpMap) _programLayers.enable(11); if (parameters.normalMap) _programLayers.enable(12); if (parameters.objectSpaceNormalMap) _programLayers.enable(13); if (parameters.tangentSpaceNormalMap) _programLayers.enable(14); if (parameters.clearcoat) _programLayers.enable(15); if (parameters.clearcoatMap) _programLayers.enable(16); if (parameters.clearcoatRoughnessMap) _programLayers.enable(17); if (parameters.clearcoatNormalMap) _programLayers.enable(18); if (parameters.displacementMap) _programLayers.enable(19); if (parameters.specularMap) _programLayers.enable(20); if (parameters.roughnessMap) _programLayers.enable(21); if (parameters.metalnessMap) _programLayers.enable(22); if (parameters.gradientMap) _programLayers.enable(23); if (parameters.alphaMap) _programLayers.enable(24); if (parameters.alphaTest) _programLayers.enable(25); if (parameters.vertexColors) _programLayers.enable(26); if (parameters.vertexAlphas) _programLayers.enable(27); if (parameters.vertexUvs) _programLayers.enable(28); if (parameters.vertexTangents) _programLayers.enable(29); if (parameters.uvsVertexOnly) _programLayers.enable(30); if (parameters.fog) _programLayers.enable(31); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.premultipliedAlpha) _programLayers.enable(7); if (parameters.shadowMapEnabled) _programLayers.enable(8); if (parameters.physicallyCorrectLights) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.depthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.specularIntensityMap) _programLayers.enable(14); if (parameters.specularColorMap) _programLayers.enable(15); if (parameters.transmission) _programLayers.enable(16); if (parameters.transmissionMap) _programLayers.enable(17); if (parameters.thicknessMap) _programLayers.enable(18); if (parameters.sheen) _programLayers.enable(19); if (parameters.sheenColorMap) _programLayers.enable(20); if (parameters.sheenRoughnessMap) _programLayers.enable(21); if (parameters.decodeVideoTexture) _programLayers.enable(22); if (parameters.transparent) _programLayers.enable(23); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, texImage3D: texImage3D, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const hasMultisampledRenderToTexture = extensions.has("WEBGL_multisampled_render_to_texture"); const MultisampledRenderToTextureExtension = hasMultisampledRenderToTexture ? extensions.get("WEBGL_multisampled_render_to_texture") : undefined; const _videoTextures = new WeakMap(); let _canvas; // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } info.memory.textures--; } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; _gl.deleteTexture(textureProperties.__webglTexture); properties.remove(texture); } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (!renderTarget) return; if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === undefined) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined"); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataTexture2DArray) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isDataTexture3D) textureType = _gl.TEXTURE_3D; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataTexture2DArray) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isDataTexture3D) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture && (texture.isCompressedTexture || texture.image[0].isCompressedTexture); const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || renderTarget.useRenderToTexture) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (renderTarget.useRenderbuffer) { if (isWebGL2) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (isRenderTarget3D) { // Render targets containing layers, i.e: Texture 3D and 2d arrays if (isWebGL2) { const isTexture3D = texture.isDataTexture3D; glTextureType = isTexture3D ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.useRenderbuffer) { if (isWebGL2) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } if (!renderTarget.ignoreDepthForMultisampleCopy) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } const renderTargetProperties = properties.get(renderTarget); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (renderTarget.ignoreDepthForMultisampleCopy) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } function getRenderTargetSamples(renderTarget) { return isWebGL2 && (renderTarget.useRenderbuffer || renderTarget.useRenderToTexture) ? Math.min(maxSamples, renderTarget.samples) : 0; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // backwards compatibility let warnedTexture2D = false; let warnedTextureCube = false; function safeSetTexture2D(texture, slot) { if (texture && texture.isWebGLRenderTarget) { if (warnedTexture2D === false) { console.warn("THREE.WebGLTextures.safeSetTexture2D: don"t use render targets as textures. Use their .texture property instead."); warnedTexture2D = true; } texture = texture.texture; } setTexture2D(texture, slot); } function safeSetTextureCube(texture, slot) { if (texture && texture.isWebGLCubeRenderTarget) { if (warnedTextureCube === false) { console.warn("THREE.WebGLTextures.safeSetTextureCube: don"t use cube render targets as textures. Use their .texture property instead."); warnedTextureCube = true; } texture = texture.texture; } setTextureCube(texture, slot); } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.safeSetTexture2D = safeSetTexture2D; this.safeSetTextureCube = safeSetTextureCube; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; const hasMultisampledRenderToTexture = renderer.extensions.has("WEBGL_multisampled_render_to_texture"); let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let isMultisample = false; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { isMultisample = attributes.antialias; let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); if (isMultisample) { newRenderTarget = new WebGLMultisampleRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, useRenderToTexture: hasMultisampledRenderToTexture, encoding: renderer.outputEncoding }); } else { newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, encoding: renderer.outputEncoding }); } } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color, depth, stencil) { let bits = 0; if (color === undefined || color) bits |= _gl.COLOR_BUFFER_BIT; if (depth === undefined || depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil === undefined || stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); } function renderTransmissionPass(opaqueObjects, scene, camera) { if (_transmissionRenderTarget === null) { const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; _transmissionRenderTarget = new renderTargetType(1024, 1024, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, magFilter: NearestFilter, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, useRenderToTexture: extensions.has("WEBGL_multisampled_render_to_texture") }); } const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphTargetsCount = !!geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } if (!!geometry && (geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined)) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (renderTarget.useRenderToTexture) { console.warn("render-to-texture extension was disabled because an external texture was provided"); renderTarget.useRenderToTexture = false; renderTarget.useRenderbuffer = true; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isDataTexture3D || texture.isDataTexture2DArray) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (renderTarget.useRenderbuffer) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isDataTexture3D) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataTexture2DArray) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isDataTexture3D) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, object: object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter; this.minFilter = minFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV: function (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1, hash2); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; var Materials = /*#__PURE__*/Object.freeze({ __proto__: null, ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, MeshMatcapMaterial: MeshMatcapMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray: function (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray: function (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder: function (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip: function (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive: function (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * (fps || 1.0); } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } if (typeof ReadableStream === "undefined" || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.substr(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = new Materials[json.type](); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.substr(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = await deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } let texture; const image = images[data.image]; if (Array.isArray(image)) { texture = new CubeTexture(image); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(image.data, image.width, image.height); } else { texture = new Texture(image); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext: function () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext: function (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (!matches) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (!nodeName || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera && camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera && camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object && object.isBone) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; const toChange = []; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange.push({ froms: sIdx, tos: s2Idx, hole: hIdx }); if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } // console.log("ambiguous: ", ambiguous); if (toChange.length > 0) { // console.log("to change: ", toChange); if (!ambiguous) newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add: function /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get: function /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get: function () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set: function (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, 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."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get: function () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, 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."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set: function () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set: function (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set: function (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set: function (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get: function () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get: function () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set: function /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get: function () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get: function () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.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 () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get: function () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set: function (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get: function () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set: function (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get: function () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set: function (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get: function () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get: function () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set: function (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* cullFace */ () { 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."); return undefined; }, 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."); return undefined; }, set: function () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get: function () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get: function () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get: function () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject: function /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach: function /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach: function /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
three.r155",[],(function $module_three_r155(global,require,requireDynamic,requireLazy,module,exports){ "use strict" console.warn( "Scripts "build/three.js" and "build/three.min.js" are deprecated with r150+, and will be removed with r160. Please use ES Modules or alternatives: https://threejs.org/docs/index.html#manual/en/introduction/Installation" ); /** * @license * Copyright 2010-2023 Three.js Authors * SPDX-License-Identifier: MIT */ (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "155"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const TwoPassDoubleSide = 2; // r149 const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; /** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */ const LinearEncoding = 3000; /** @deprecated Use SRGBColorSpace in three.js r152+. */ const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ""; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const DisplayP3ColorSpace = "display-p3"; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const NeverCompare = 512; const LessCompare = 513; const EqualCompare = 514; const LessEqualCompare = 515; const GreaterCompare = 516; const NotEqualCompare = 517; const GreaterEqualCompare = 518; const AlwaysCompare = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 const WebGLCoordinateSystem = 2000; const WebGPUCoordinateSystem = 2001; /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; const listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } } hasEventListener( type, listener ) { if ( this._listeners === undefined ) return false; const listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; } removeEventListener( type, listener ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { const index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } } dispatchEvent( event ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice( 0 ); for ( let i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" ]; let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + "-" + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + "-" + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + "-" + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + "-" + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp( value, min, max ) { return Math.max( min, Math.min( max, value ) ); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { return ( ( n % m ) + m ) % m; } // Linear mapping from range to range function mapLinear( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp( x, y, value ) { if ( x !== y ) { return ( value - x ) / ( y - x ); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp( x, y, t ) { return ( 1 - t ) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp( x, y, lambda, dt ) { return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong( x, length = 1 ) { return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); } function smootherstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); } // Random integer from interval function randInt( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); } // Random float from interval function randFloat( low, high ) { return low + Math.random() * ( high - low ); } // Random float from <-range/2, range/2> interval function randFloatSpread( range ) { return range * ( 0.5 - Math.random() ); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { if ( s !== undefined ) _seed = s; // Mulberry32 generator let t = _seed += 0x6D2B79F5; t = Math.imul( t ^ t >>> 15, t | 1 ); t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } function degToRad( degrees ) { return degrees * DEG2RAD; } function radToDeg( radians ) { return radians * RAD2DEG; } function isPowerOfTwo( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; } function ceilPowerOfTwo( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); } function floorPowerOfTwo( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } function setQuaternionFromProperEuler( q, a, b, c, order ) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos( b / 2 ); const s2 = sin( b / 2 ); const c13 = cos( ( a + c ) / 2 ); const s13 = sin( ( a + c ) / 2 ); const c1_3 = cos( ( a - c ) / 2 ); const s1_3 = sin( ( a - c ) / 2 ); const c3_1 = cos( ( c - a ) / 2 ); const s3_1 = sin( ( c - a ) / 2 ); switch ( order ) { case "XYX": q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); break; case "YZY": q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); break; case "ZXZ": q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); break; case "XZX": q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); break; case "YXY": q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); break; case "ZYZ": q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order ); } } function denormalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint32Array: return value / 4294967295.0; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int32Array: return Math.max( value / 2147483647.0, - 1.0 ); case Int16Array: return Math.max( value / 32767.0, - 1.0 ); case Int8Array: return Math.max( value / 127.0, - 1.0 ); default: throw new Error( "Invalid component type." ); } } function normalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint32Array: return Math.round( value * 4294967295.0 ); case Uint16Array: return Math.round( value * 65535.0 ); case Uint8Array: return Math.round( value * 255.0 ); case Int32Array: return Math.round( value * 2147483647.0 ); case Int16Array: return Math.round( value * 32767.0 ); case Int8Array: return Math.round( value * 127.0 ); default: throw new Error( "Invalid component type." ); } } const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize }; class Vector2 { constructor( x = 0, y = 0 ) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width( value ) { this.x = value; } get height() { return this.y; } set height( value ) { this.y = value; } set( x, y ) { this.x = x; this.y = y; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y ); } copy( v ) { this.x = v.x; this.y = v.y; return this; } add( v ) { this.x += v.x; this.y += v.y; return this; } addScalar( s ) { this.x += s; this.y += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; return this; } subScalar( s ) { this.x -= s; this.y -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; return this; } divide( v ) { this.x /= v.x; this.y /= v.y; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } applyMatrix3( m ) { const x = this.x, y = this.y; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; } negate() { this.x = - this.x; this.y = - this.y; return this; } dot( v ) { return this.x * v.x + this.y * v.y; } cross( v ) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt( this.x * this.x + this.y * this.y ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ); } normalize() { return this.divideScalar( this.length() || 1 ); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2( - this.y, - this.x ) + Math.PI; return angle; } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; } rotateAround( center, angle ) { const c = Math.cos( angle ), s = Math.sin( angle ); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; } } class Matrix3 { constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { Matrix3.prototype.isMatrix3 = true; this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( n11 !== undefined ) { this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ); } } set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { const te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; } identity() { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrix3Column( this, 0 ); yAxis.setFromMatrix3Column( this, 1 ); zAxis.setFromMatrix3Column( this, 2 ); return this; } setFromMatrix4( m ) { const me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; } determinant() { const te = this.elements; const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; } getNormalMatrix( matrix4 ) { return this.setFromMatrix4( matrix4 ).invert().transpose(); } transposeIntoArray( r ) { const m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; } setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { const c = Math.cos( rotation ); const s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); return this; } // scale( sx, sy ) { this.premultiply( _m3.makeScale( sx, sy ) ); return this; } rotate( theta ) { this.premultiply( _m3.makeRotation( - theta ) ); return this; } translate( tx, ty ) { this.premultiply( _m3.makeTranslation( tx, ty ) ); return this; } // for 2D Transforms makeTranslation( x, y ) { if ( x.isVector2 ) { this.set( 1, 0, x.x, 0, 1, x.y, 0, 0, 1 ); } else { this.set( 1, 0, x, 0, 1, y, 0, 0, 1 ); } return this; } makeRotation( theta ) { // counterclockwise const c = Math.cos( theta ); const s = Math.sin( theta ); this.set( c, - s, 0, s, c, 0, 0, 0, 1 ); return this; } makeScale( x, y ) { this.set( x, 0, 0, 0, y, 0, 0, 0, 1 ); return this; } // equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } clone() { return new this.constructor().fromArray( this.elements ); } } const _m3 = /*@__PURE__*/ new Matrix3(); function arrayNeedsUint32( array ) { // assumes larger values usually on last for ( let i = array.length - 1; i >= 0; -- i ) { if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray( type, buffer ) { return new TYPED_ARRAYS[ type ]( buffer ); } function createElementNS( name ) { return document.createElementNS( "http://www.w3.org/1999/xhtml", name ); } const _cache = {}; function warnOnce( message ) { if ( message in _cache ) return; _cache[ message ] = true; console.warn( message ); } function SRGBToLinear( c ) { return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); } function LinearToSRGB( c ) { return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().fromArray( [ 0.8224621, 0.0331941, 0.0170827, 0.1775380, 0.9668058, 0.0723974, - 0.0000001, 0.0000001, 0.9105199 ] ); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().fromArray( [ 1.2249401, - 0.0420569, - 0.0196376, - 0.2249404, 1.0420571, - 0.0786361, 0.0000001, 0.0000000, 1.0982735 ] ); function DisplayP3ToLinearSRGB( color ) { // Display P3 uses the sRGB transfer functions return color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ); } function LinearSRGBToDisplayP3( color ) { // Display P3 uses the sRGB transfer functions return color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(); } // Conversions from to Linear-sRGB reference space. const TO_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertSRGBToLinear(), [ DisplayP3ColorSpace ]: DisplayP3ToLinearSRGB, }; // Conversions to from Linear-sRGB reference space. const FROM_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertLinearToSRGB(), [ DisplayP3ColorSpace ]: LinearSRGBToDisplayP3, }; const ColorManagement = { enabled: true, get legacyMode() { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); return ! this.enabled; }, set legacyMode( legacyMode ) { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); this.enabled = ! legacyMode; }, get workingColorSpace() { return LinearSRGBColorSpace; }, set workingColorSpace( colorSpace ) { console.warn( "THREE.ColorManagement: .workingColorSpace is readonly." ); }, convert: function ( color, sourceColorSpace, targetColorSpace ) { if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { return color; } const sourceToLinear = TO_LINEAR[ sourceColorSpace ]; const targetFromLinear = FROM_LINEAR[ targetColorSpace ]; if ( sourceToLinear === undefined || targetFromLinear === undefined ) { throw new Error( `Unsupported color space conversion, "${ sourceColorSpace }" to "${ targetColorSpace }".` ); } return targetFromLinear( sourceToLinear( color ) ); }, fromWorkingColorSpace: function ( color, targetColorSpace ) { return this.convert( color, this.workingColorSpace, targetColorSpace ); }, toWorkingColorSpace: function ( color, sourceColorSpace ) { return this.convert( color, sourceColorSpace, this.workingColorSpace ); }, }; let _canvas; class ImageUtils { static getDataURL( image ) { if ( /^data:/i.test( image.src ) ) { return image.src; } if ( typeof HTMLCanvasElement === "undefined" ) { return image.src; } let canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { if ( _canvas === undefined ) _canvas = createElementNS( "canvas" ); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } canvas = _canvas; } if ( canvas.width > 2048 || canvas.height > 2048 ) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image ); return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } static sRGBToLinear( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const canvas = createElementNS( "canvas" ); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height ); const imageData = context.getImageData( 0, 0, image.width, image.height ); const data = imageData.data; for ( let i = 0; i < data.length; i ++ ) { data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; } context.putImageData( imageData, 0, 0 ); return canvas; } else if ( image.data ) { const data = image.data.slice( 0 ); for ( let i = 0; i < data.length; i ++ ) { if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); } else { // assuming float data[ i ] = SRGBToLinear( data[ i ] ); } } return { data: data, width: image.width, height: image.height }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied." ); return image; } } } let sourceId = 0; class Source { constructor( data = null ) { this.isSource = true; Object.defineProperty( this, "id", { value: sourceId ++ } ); this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { return meta.images[ this.uuid ]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if ( data !== null ) { let url; if ( Array.isArray( data ) ) { // cube texture url = []; for ( let i = 0, l = data.length; i < l; i ++ ) { if ( data[ i ].isDataTexture ) { url.push( serializeImage( data[ i ].image ) ); } else { url.push( serializeImage( data[ i ] ) ); } } } else { // texture url = serializeImage( data ); } output.url = url; } if ( ! isRootObject ) { meta.images[ this.uuid ] = output; } return output; } } function serializeImage( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { // default images return ImageUtils.getDataURL( image ); } else { if ( image.data ) { // images of DataTexture return { data: Array.from( image.data ), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn( "THREE.Texture: Unable to serialize Texture." ); return {}; } } } let textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { super(); this.isTexture = true; Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = generateUUID(); this.name = ""; this.source = new Source( image ); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) if ( typeof colorSpace === "string" ) { this.colorSpace = colorSpace; } else { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image( value = null ) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.needsUpdate = true; return this; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } const output = { metadata: { version: 4.6, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON( meta ).uuid, mapping: this.mapping, channel: this.channel, 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, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment }; if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; } dispose() { this.dispatchEvent( { type: "dispose" } ); } transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } return uv; } set needsUpdate( value ) { if ( value === true ) { this.version ++; this.source.needsUpdate = true; } } get encoding() { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set encoding( encoding ) { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor( x = 0, y = 0, z = 0, w = 1 ) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width( value ) { this.z = value; } get height() { return this.w; } set height( value ) { this.w = value; } set( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setW( w ) { this.w = w; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z, this.w ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } setAxisAngleFromQuaternion( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); const s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = ( m11 + 1 ) / 2; const yy = ( m22 + 1 ) / 2; const zz = ( m33 + 1 ) / 2; const xy = ( m12 + m21 ) / 4; const xz = ( m13 + m31 ) / 4; const yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class RenderTarget extends EventDispatcher { constructor( width = 1, height = 1, options = {} ) { super(); this.isRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); const image = { width: width, height: height, depth: 1 }; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); this.texture.isRenderTargetTexture = true; this.texture.flipY = false; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy( source.scissor ); this.scissorTest = source.scissorTest; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.texture.isRenderTargetTexture = true; // ensure image object is not shared, see #20328 const image = Object.assign( {}, source.texture.image ); this.texture.source = new Source( image ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class WebGLRenderTarget extends RenderTarget { constructor( width = 1, height = 1, options = {} ) { super( width, height, options ); this.isWebGLRenderTarget = true; } } class DataArrayTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { super( null ); this.isDataArrayTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor( width = 1, height = 1, depth = 1 ) { super( width, height ); this.isWebGLArrayRenderTarget = true; this.depth = depth; this.texture = new DataArrayTexture( null, width, height, depth ); this.texture.isRenderTargetTexture = true; } } class Data3DTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super( null ); this.isData3DTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGL3DRenderTarget extends WebGLRenderTarget { constructor( width = 1, height = 1, depth = 1 ) { super( width, height ); this.isWebGL3DRenderTarget = true; this.depth = depth; this.texture = new Data3DTexture( null, width, height, depth ); this.texture.isRenderTargetTexture = true; } } class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor( width = 1, height = 1, count = 1, options = {} ) { super( width, height, options ); this.isWebGLMultipleRenderTargets = true; const texture = this.texture; this.texture = []; for ( let i = 0; i < count; i ++ ) { this.texture[ i ] = texture.clone(); this.texture[ i ].isRenderTargetTexture = true; } } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; for ( let i = 0, il = this.texture.length; i < il; i ++ ) { this.texture[ i ].image.width = width; this.texture[ i ].image.height = height; this.texture[ i ].image.depth = depth; } this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } copy( source ) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy( source.scissor ); this.scissorTest = source.scissorTest; this.viewport.copy( source.viewport ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.texture.length = 0; for ( let i = 0, il = source.texture.length; i < il; i ++ ) { this.texture[ i ] = source.texture[ i ].clone(); this.texture[ i ].isRenderTargetTexture = true; } return this; } } class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( t === 0 ) { dst[ dstOffset + 0 ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; return; } if ( t === 1 ) { dst[ dstOffset + 0 ] = x1; dst[ dstOffset + 1 ] = y1; dst[ dstOffset + 2 ] = z1; dst[ dstOffset + 3 ] = w1; return; } if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { const sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { const x0 = src0[ srcOffset0 ]; const y0 = src0[ srcOffset0 + 1 ]; const z0 = src0[ srcOffset0 + 2 ]; const w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 ]; const y1 = src1[ srcOffset1 + 1 ]; const z1 = src1[ srcOffset1 + 2 ]; const w1 = src1[ srcOffset1 + 3 ]; dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w( value ) { this._w = value; this._onChangeCallback(); } set( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._w ); } copy( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler( euler, update ) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos( x / 2 ); const c2 = cos( y / 2 ); const c3 = cos( z / 2 ); const s1 = sin( x / 2 ); const s2 = sin( y / 2 ); const s3 = sin( z / 2 ); switch ( order ) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order ); } if ( update !== false ) this._onChangeCallback(); return this; } setFromAxisAngle( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this._onChangeCallback(); return this; } setFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33; if ( trace > 0 ) { const s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors( vFrom, vTo ) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot( vTo ) + 1; if ( r < Number.EPSILON ) { // vFrom and vTo point in opposite directions r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { this._x = - vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = - vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo( q ) { return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); } rotateTowards( q, step ) { const angle = this.angleTo( q ); if ( angle === 0 ) return this; const t = Math.min( 1, step / angle ); this.slerp( q, t ); return this; } identity() { return this.set( 0, 0, 0, 1 ); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= - 1; this._y *= - 1; this._z *= - 1; this._onChangeCallback(); return this; } dot( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } normalize() { let l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply( q ) { return this.multiplyQuaternions( this, q ); } premultiply( q ) { return this.multiplyQuaternions( q, this ); } multiplyQuaternions( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if ( sqrSinHalfTheta <= Number.EPSILON ) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this._onChangeCallback(); return this; } slerpQuaternions( qa, qb, t ) { return this.copy( qa ).slerp( qb, t ); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt( 1 - u1 ); const sqrtu1 = Math.sqrt( u1 ); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set( sqrt1u1 * Math.cos( u2 ), sqrtu1 * Math.sin( u3 ), sqrtu1 * Math.cos( u3 ), sqrt1u1 * Math.sin( u2 ), ); } equals( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } fromArray( array, offset = 0 ) { this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; } fromBufferAttribute( attribute, index ) { this._x = attribute.getX( index ); this._y = attribute.getY( index ); this._z = attribute.getZ( index ); this._w = attribute.getW( index ); return this; } toJSON() { return this.toArray(); } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor( x = 0, y = 0, z = 0 ) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set( x, y, z ) { if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler( euler ) { return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } applyAxisAngle( axis, angle ) { return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); } applyMatrix3( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; } applyNormalMatrix( m ) { return this.applyMatrix3( m ).normalize(); } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; } applyQuaternion( q ) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; } project( camera ) { return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } unproject( camera ) { return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); } transformDirection( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); } divide( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; return this; } cross( v ) { return this.crossVectors( this, v ); } crossVectors( a, b ) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector( v ) { const denominator = v.lengthSq(); if ( denominator === 0 ) return this.set( 0, 0, 0 ); const scalar = v.dot( this ) / denominator; return this.copy( v ).multiplyScalar( scalar ); } projectOnPlane( planeNormal ) { _vector$b.copy( this ).projectOnVector( planeNormal ); return this.sub( _vector$b ); } reflect( normal ) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$b.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); } setFromSpherical( s ) { return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } setFromSphericalCoords( radius, phi, theta ) { const sinPhiRadius = Math.sin( phi ) * radius; this.x = sinPhiRadius * Math.sin( theta ); this.y = Math.cos( phi ) * radius; this.z = sinPhiRadius * Math.cos( theta ); return this; } setFromCylindrical( c ) { return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); } setFromCylindricalCoords( radius, theta, y ) { this.x = radius * Math.sin( theta ); this.y = y; this.z = radius * Math.cos( theta ); return this; } setFromMatrixPosition( m ) { const e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; } setFromMatrixScale( m ) { const sx = this.setFromMatrixColumn( m, 0 ).length(); const sy = this.setFromMatrixColumn( m, 1 ).length(); const sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn( m, index ) { return this.fromArray( m.elements, index * 4 ); } setFromMatrix3Column( m, index ) { return this.fromArray( m.elements, index * 3 ); } setFromEuler( e ) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor( c ) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = ( Math.random() - 0.5 ) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt( 1 - u ** 2 ); this.x = f * Math.cos( t ); this.y = f * Math.sin( t ); this.z = u; return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; } } const _vector$b = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { this.isBox3 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromArray( array ) { this.makeEmpty(); for ( let i = 0, il = array.length; i < il; i += 3 ) { this.expandByPoint( _vector$a.fromArray( array, i ) ); } return this; } setFromBufferAttribute( attribute ) { this.makeEmpty(); for ( let i = 0, il = attribute.count; i < il; i ++ ) { this.expandByPoint( _vector$a.fromBufferAttribute( attribute, i ) ); } return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$a.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } setFromObject( object, precise = false ) { this.makeEmpty(); return this.expandByObject( object, precise ); } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } expandByObject( object, precise = false ) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix( false, false ); if ( object.boundingBox !== undefined ) { if ( object.boundingBox === null ) { object.computeBoundingBox(); } _box$3.copy( object.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } else { const geometry = object.geometry; if ( geometry !== undefined ) { if ( precise && geometry.attributes !== undefined && geometry.attributes.position !== undefined ) { const position = geometry.attributes.position; for ( let i = 0, l = position.count; i < l; i ++ ) { _vector$a.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); this.expandByPoint( _vector$a ); } } else { if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } _box$3.copy( geometry.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { this.expandByObject( children[ i ], precise ); } return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); } intersectsBox( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, _vector$a ); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$a.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); } intersectsPlane( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= - plane.constant && max >= - plane.constant ); } intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( _center ); _extents.subVectors( this.max, _center ); // translate triangle to aabb origin _v0$2.subVectors( triangle.a, _center ); _v1$7.subVectors( triangle.b, _center ); _v2$4.subVectors( triangle.c, _center ); // compute edge vectors for triangle _f0.subVectors( _v1$7, _v0$2 ); _f1.subVectors( _v2$4, _v1$7 ); _f2.subVectors( _v0$2, _v2$4 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors( _f0, _f1 ); axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; return satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ); } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { return this.clampPoint( point, _vector$a ).distanceTo( point ); } getBoundingSphere( target ) { if ( this.isEmpty() ) { target.makeEmpty(); } else { this.getCenter( target.center ); target.radius = this.getSize( _vector$a ).length() * 0.5; } return target; } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } applyMatrix4( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( _points ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3() ]; const _vector$a = /*@__PURE__*/ new Vector3(); const _box$3 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$4 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes( axes, v0, v1, v2, extents ) { for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { _testAxis.fromArray( axes, i ); // project the aabb onto the separating axis const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot( _testAxis ); const p1 = v1.dot( _testAxis ); const p2 = v2.dot( _testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); class Sphere { constructor( center = new Vector3(), radius = - 1 ) { this.center = center; this.radius = radius; } set( center, radius ) { this.center.copy( center ); this.radius = radius; return this; } setFromPoints( points, optionalCenter ) { const center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { _box$2.setFromPoints( points ).getCenter( center ); } let maxRadiusSq = 0; for ( let i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; } copy( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; } isEmpty() { return ( this.radius < 0 ); } makeEmpty() { this.center.set( 0, 0, 0 ); this.radius = - 1; return this; } containsPoint( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); } distanceToPoint( point ) { return ( point.distanceTo( this.center ) - this.radius ); } intersectsSphere( sphere ) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } intersectsBox( box ) { return box.intersectsSphere( this ); } intersectsPlane( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } clampPoint( point, target ) { const deltaLengthSq = this.center.distanceToSquared( point ); target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; } getBoundingBox( target ) { if ( this.isEmpty() ) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; } applyMatrix4( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate( offset ) { this.center.add( offset ); return this; } expandByPoint( point ) { if ( this.isEmpty() ) { this.center.copy( point ); this.radius = 0; return this; } _v1$6.subVectors( point, this.center ); const lengthSq = _v1$6.lengthSq(); if ( lengthSq > ( this.radius * this.radius ) ) { // calculate the minimal sphere const length = Math.sqrt( lengthSq ); const delta = ( length - this.radius ) * 0.5; this.center.addScaledVector( _v1$6, delta / length ); this.radius += delta; } return this; } union( sphere ) { if ( sphere.isEmpty() ) { return this; } if ( this.isEmpty() ) { this.copy( sphere ); return this; } if ( this.center.equals( sphere.center ) === true ) { this.radius = Math.max( this.radius, sphere.radius ); } else { _v2$3.subVectors( sphere.center, this.center ).setLength( sphere.radius ); this.expandByPoint( _v1$6.copy( sphere.center ).add( _v2$3 ) ); this.expandByPoint( _v1$6.copy( sphere.center ).sub( _v2$3 ) ); } return this; } equals( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } clone() { return new this.constructor().copy( this ); } } const _vector$9 = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { this.origin = origin; this.direction = direction; } set( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; } copy( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; } at( t, target ) { return target.copy( this.origin ).addScaledVector( this.direction, t ); } lookAt( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; } recast( t ) { this.origin.copy( this.at( t, _vector$9 ) ); return this; } closestPointToPoint( point, target ) { target.subVectors( point, this.origin ); const directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.origin ).addScaledVector( this.direction, directionDistance ); } distanceToPoint( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); } distanceSqToPoint( point ) { const directionDistance = _vector$9.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } _vector$9.copy( this.origin ).addScaledVector( this.direction, directionDistance ); return _vector$9.distanceToSquared( point ); } distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); _segDir.copy( v1 ).sub( v0 ).normalize(); _diff.copy( this.origin ).sub( _segCenter ); const segExtent = v0.distanceTo( v1 ) * 0.5; const a01 = - this.direction.dot( _segDir ); const b0 = _diff.dot( this.direction ); const b1 = - _diff.dot( _segDir ); const c = _diff.lengthSq(); const det = Math.abs( 1 - a01 * a01 ); let s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 ); } return sqrDist; } intersectSphere( sphere, target ) { _vector$9.subVectors( sphere.center, this.origin ); const tca = _vector$9.dot( this.direction ); const d2 = _vector$9.dot( _vector$9 ) - tca * tca; const radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; const thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if ( t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); } intersectsSphere( sphere ) { return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); } distanceToPlane( plane ) { const denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane( plane, target ) { const t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); } intersectsPlane( plane ) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } const denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox( box, target ) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin; if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); } intersectsBox( box ) { return this.intersectBox( box, _vector$9 ) !== null; } intersectTriangle( a, b, c, backfaceCulling, target ) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors( b, a ); _edge2.subVectors( c, a ); _normal$1.crossVectors( _edge1, _edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot( _normal$1 ); let sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } _diff.subVectors( this.origin, a ); const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. const QdN = - sign * _diff.dot( _normal$1 ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); } applyMatrix4( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; } equals( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } clone() { return new this.constructor().copy( this ); } } class Matrix4 { constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { Matrix4.prototype.isMatrix4 = true; this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( n11 !== undefined ) { this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ); } } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { const te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; } identity() { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } clone() { return new Matrix4().fromArray( this.elements ); } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; } copyPosition( m ) { const te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; } setFromMatrix3( m ) { const me = m.elements; this.set( me[ 0 ], me[ 3 ], me[ 6 ], 0, me[ 1 ], me[ 4 ], me[ 7 ], 0, me[ 2 ], me[ 5 ], me[ 8 ], 0, 0, 0, 0, 1 ); return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; } makeBasis( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; } extractRotation( m ) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromEuler( euler ) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos( x ), b = Math.sin( x ); const c = Math.cos( y ), d = Math.sin( y ); const e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromQuaternion( q ) { return this.compose( _zero, q, _one ); } lookAt( eye, target, up ) { const te = this.elements; _z.subVectors( eye, target ); if ( _z.lengthSq() === 0 ) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors( up, _z ); if ( _x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors( up, _z ); } _x.normalize(); _y.crossVectors( _z, _x ); te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; } determinant() { const te = this.elements; const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); } transpose() { const te = this.elements; let tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; } setPosition( x, y, z ) { const te = this.elements; if ( x.isVector3 ) { te[ 12 ] = x.x; te[ 13 ] = x.y; te[ 14 ] = x.z; } else { te[ 12 ] = x; te[ 13 ] = y; te[ 14 ] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; } scale( v ) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); } makeTranslation( x, y, z ) { if ( x.isVector3 ) { this.set( 1, 0, 0, x.x, 0, 1, 0, x.y, 0, 0, 1, x.z, 0, 0, 0, 1 ); } else { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); } return this; } makeRotationX( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; } makeRotationY( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; } makeRotationZ( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } makeRotationAxis( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos( angle ); const s = Math.sin( angle ); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; } makeScale( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; } makeShear( xy, xz, yx, yz, zx, zy ) { this.set( 1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1 ); return this; } compose( position, quaternion, scale ) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; } decompose( position, quaternion, scale ) { const te = this.elements; let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part _m1$2.copy( this ); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[ 0 ] *= invSX; _m1$2.elements[ 1 ] *= invSX; _m1$2.elements[ 2 ] *= invSX; _m1$2.elements[ 4 ] *= invSY; _m1$2.elements[ 5 ] *= invSY; _m1$2.elements[ 6 ] *= invSY; _m1$2.elements[ 8 ] *= invSZ; _m1$2.elements[ 9 ] *= invSZ; _m1$2.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( _m1$2 ); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { const te = this.elements; const x = 2 * near / ( right - left ); const y = 2 * near / ( top - bottom ); const a = ( right + left ) / ( right - left ); const b = ( top + bottom ) / ( top - bottom ); let c, d; if ( coordinateSystem === WebGLCoordinateSystem ) { c = - ( far + near ) / ( far - near ); d = ( - 2 * far * near ) / ( far - near ); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { c = - far / ( far - near ); d = ( - far * near ) / ( far - near ); } else { throw new Error( "THREE.Matrix4.makePerspective(): Invalid coordinate system: " + coordinateSystem ); } te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { const te = this.elements; const w = 1.0 / ( right - left ); const h = 1.0 / ( top - bottom ); const p = 1.0 / ( far - near ); const x = ( right + left ) * w; const y = ( top + bottom ) * h; let z, zInv; if ( coordinateSystem === WebGLCoordinateSystem ) { z = ( far + near ) * p; zInv = - 2 * p; } else if ( coordinateSystem === WebGPUCoordinateSystem ) { z = near * p; zInv = - 1 * p; } else { throw new Error( "THREE.Matrix4.makeOrthographic(): Invalid coordinate system: " + coordinateSystem ); } te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = zInv; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; } equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$2 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order( value ) { this._order = value; this._onChangeCallback(); } set( x, y, z, order = this._order ) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._order ); } copy( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix( m, order = this._order, update = true ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; switch ( order ) { case "XYZ": this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } break; case "YXZ": this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.9999999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } break; case "ZXY": this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.9999999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } break; case "ZYX": this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.9999999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } break; case "YZX": this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } break; case "XZY": this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.9999999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order ); } this._order = order; if ( update === true ) this._onChangeCallback(); return this; } setFromQuaternion( q, order, update ) { _matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( _matrix, order, update ); } setFromVector3( v, order = this._order ) { return this.set( v.x, v.y, v.z, order ); } reorder( newOrder ) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler( this ); return this.setFromQuaternion( _quaternion$3, newOrder ); } equals( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); } fromArray( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = "XYZ"; class Layers { constructor() { this.mask = 1 | 0; } set( channel ) { this.mask = ( 1 << channel | 0 ) >>> 0; } enable( channel ) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle( channel ) { this.mask ^= 1 << channel | 0; } disable( channel ) { this.mask &= ~ ( 1 << channel | 0 ); } disableAll() { this.mask = 0; } test( layers ) { return ( this.mask & layers.mask ) !== 0; } isEnabled( channel ) { return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$1 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty( this, "id", { value: _object3DId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation._onChange( onRotationChange ); quaternion._onChange( onQuaternionChange ); Object.defineProperties( this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldNeedsUpdate = false; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} applyMatrix4( matrix ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); this.matrix.premultiply( matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); } applyQuaternion( q ) { this.quaternion.premultiply( q ); return this; } setRotationFromAxisAngle( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); } setRotationFromEuler( euler ) { this.quaternion.setFromEuler( euler, true ); } setRotationFromMatrix( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); } setRotationFromQuaternion( q ) { // assumes q is normalized this.quaternion.copy( q ); } rotateOnAxis( axis, angle ) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( _q1 ); return this; } rotateOnWorldAxis( axis, angle ) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( _q1 ); return this; } rotateX( angle ) { return this.rotateOnAxis( _xAxis, angle ); } rotateY( angle ) { return this.rotateOnAxis( _yAxis, angle ); } rotateZ( angle ) { return this.rotateOnAxis( _zAxis, angle ); } translateOnAxis( axis, distance ) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( _v1$4.multiplyScalar( distance ) ); return this; } translateX( distance ) { return this.translateOnAxis( _xAxis, distance ); } translateY( distance ) { return this.translateOnAxis( _yAxis, distance ); } translateZ( distance ) { return this.translateOnAxis( _zAxis, distance ); } localToWorld( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( this.matrixWorld ); } worldToLocal( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } lookAt( x, y, z ) { // This method does not support objects having non-uniformly-scaled parent(s) if ( x.isVector3 ) { _target.copy( x ); } else { _target.set( x, y, z ); } const parent = this.parent; this.updateWorldMatrix( true, false ); _position$3.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { _m1$1.lookAt( _position$3, _target, this.up ); } else { _m1$1.lookAt( _target, _position$3, this.up ); } this.quaternion.setFromRotationMatrix( _m1$1 ); if ( parent ) { _m1$1.extractRotation( parent.matrixWorld ); _q1.setFromRotationMatrix( _m1$1 ); this.quaternion.premultiply( _q1.invert() ); } } add( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( object && object.isObject3D ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; this.children.push( object ); object.dispatchEvent( _addedEvent ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; } remove( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } const index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; this.children.splice( index, 1 ); object.dispatchEvent( _removedEvent ); } return this; } removeFromParent() { const parent = this.parent; if ( parent !== null ) { parent.remove( this ); } return this; } clear() { for ( let i = 0; i < this.children.length; i ++ ) { const object = this.children[ i ]; object.parent = null; object.dispatchEvent( _removedEvent ); } this.children.length = 0; return this; } attach( object ) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix( true, false ); _m1$1.copy( this.matrixWorld ).invert(); if ( object.parent !== null ) { object.parent.updateWorldMatrix( true, false ); _m1$1.multiply( object.parent.matrixWorld ); } object.applyMatrix4( _m1$1 ); this.add( object ); object.updateWorldMatrix( false, true ); return this; } getObjectById( id ) { return this.getObjectByProperty( "id", id ); } getObjectByName( name ) { return this.getObjectByProperty( "name", name ); } getObjectByProperty( name, value ) { if ( this[ name ] === value ) return this; for ( let i = 0, l = this.children.length; i < l; i ++ ) { const child = this.children[ i ]; const object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; } getObjectsByProperty( name, value ) { let result = []; if ( this[ name ] === value ) result.push( this ); for ( let i = 0, l = this.children.length; i < l; i ++ ) { const childResult = this.children[ i ].getObjectsByProperty( name, value ); if ( childResult.length > 0 ) { result = result.concat( childResult ); } } return result; } getWorldPosition( target ) { this.updateWorldMatrix( true, false ); return target.setFromMatrixPosition( this.matrixWorld ); } getWorldQuaternion( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, target, _scale$2 ); return target; } getWorldScale( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, _quaternion$2, target ); return target; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); } raycast( /* raycaster, intersects */ ) {} traverse( callback ) { callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } } traverseVisible( callback ) { if ( this.visible === false ) return; callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } } traverseAncestors( callback ) { const parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } } updateMatrix() { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true || force === true ) { child.updateMatrixWorld( force ); } } } updateWorldMatrix( updateParents, updateChildren ) { const parent = this.parent; if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { parent.updateWorldMatrix( true, false ); } if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } // update children if ( updateChildren === true ) { const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true ) { child.updateWorldMatrix( false, true ); } } } } toJSON( meta ) { // meta is a string when called from JSON.stringify const isRootObject = ( meta === undefined || typeof meta === "string" ); const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.6, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // object specific properties if ( this.isInstancedMesh ) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); } // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.isScene ) { if ( this.background ) { if ( this.background.isColor ) { object.background = this.background.toJSON(); } else if ( this.background.isTexture ) { object.background = this.background.toJSON( meta ).uuid; } } if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { object.environment = this.environment.toJSON( meta ).uuid; } } else if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); const parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { const shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.isSkinnedMesh ) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if ( this.skeleton !== undefined ) { serialize( meta.skeletons, this.skeleton ); object.skeleton = this.skeleton.uuid; } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { const uuids = []; for ( let i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( let i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } // if ( this.animations.length > 0 ) { object.animations = []; for ( let i = 0; i < this.animations.length; i ++ ) { const animation = this.animations[ i ]; object.animations.push( serialize( meta.animations, animation ) ); } } if ( isRootObject ) { const geometries = extractFromCache( meta.geometries ); const materials = extractFromCache( meta.materials ); const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); const shapes = extractFromCache( meta.shapes ); const skeletons = extractFromCache( meta.skeletons ); const animations = extractFromCache( meta.animations ); const nodes = extractFromCache( meta.nodes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; if ( skeletons.length > 0 ) output.skeletons = skeletons; if ( animations.length > 0 ) output.animations = animations; if ( nodes.length > 0 ) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } } clone( recursive ) { return new this.constructor().copy( this, recursive ); } copy( source, recursive = true ) { this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.rotation.order = source.rotation.order; this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations.slice(); this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( let i = 0; i < source.children.length; i ++ ) { const child = source.children[ i ]; this.add( child.clone() ); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 ); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$1 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); let warnedGetUV = false; class Triangle { constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { this.a = a; this.b = b; this.c = c; } static getNormal( a, b, c, target ) { target.subVectors( c, b ); _v0$1.subVectors( a, b ); target.cross( _v0$1 ); const targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord( point, a, b, c, target ) { _v0$1.subVectors( c, a ); _v1$3.subVectors( b, a ); _v2$2.subVectors( point, a ); const dot00 = _v0$1.dot( _v0$1 ); const dot01 = _v0$1.dot( _v1$3 ); const dot02 = _v0$1.dot( _v2$2 ); const dot11 = _v1$3.dot( _v1$3 ); const dot12 = _v1$3.dot( _v2$2 ); const denom = ( dot00 * dot11 - dot01 * dot01 ); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } const invDenom = 1 / denom; const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); } static containsPoint( point, a, b, c ) { this.getBarycoord( point, a, b, c, _v3$1 ); return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target ); } static getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) { this.getBarycoord( point, p1, p2, p3, _v3$1 ); target.setScalar( 0 ); target.addScaledVector( v1, _v3$1.x ); target.addScaledVector( v2, _v3$1.y ); target.addScaledVector( v3, _v3$1.z ); return target; } static isFrontFacing( a, b, c, direction ) { _v0$1.subVectors( c, b ); _v1$3.subVectors( a, b ); // strictly front facing return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; } set( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; } setFromPointsAndIndices( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; } setFromAttributeAndIndices( attribute, i0, i1, i2 ) { this.a.fromBufferAttribute( attribute, i0 ); this.b.fromBufferAttribute( attribute, i1 ); this.c.fromBufferAttribute( attribute, i2 ); return this; } clone() { return new this.constructor().copy( this ); } copy( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; } getArea() { _v0$1.subVectors( this.c, this.b ); _v1$3.subVectors( this.a, this.b ); return _v0$1.cross( _v1$3 ).length() * 0.5; } getMidpoint( target ) { return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } getNormal( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); } getPlane( target ) { return target.setFromCoplanarPoints( this.a, this.b, this.c ); } getBarycoord( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); } getUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } getInterpolation( point, v1, v2, v3, target ) { return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target ); } containsPoint( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); } isFrontFacing( direction ) { return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); } intersectsBox( box ) { return box.intersectsTriangle( this ); } closestPointToPoint( p, target ) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors( b, a ); _vac.subVectors( c, a ); _vap.subVectors( p, a ); const d1 = _vab.dot( _vap ); const d2 = _vac.dot( _vap ); if ( d1 <= 0 && d2 <= 0 ) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy( a ); } _vbp.subVectors( p, b ); const d3 = _vab.dot( _vbp ); const d4 = _vac.dot( _vbp ); if ( d3 >= 0 && d4 <= d3 ) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy( b ); } const vc = d1 * d4 - d3 * d2; if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { v = d1 / ( d1 - d3 ); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy( a ).addScaledVector( _vab, v ); } _vcp.subVectors( p, c ); const d5 = _vab.dot( _vcp ); const d6 = _vac.dot( _vcp ); if ( d6 >= 0 && d5 <= d6 ) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy( c ); } const vb = d5 * d2 - d1 * d6; if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { w = d2 / ( d2 - d6 ); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy( a ).addScaledVector( _vac, w ); } const va = d3 * d6 - d5 * d4; if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { _vbc.subVectors( c, b ); w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC } // face region const denom = 1 / ( va + vb + vc ); // u = va * denom v = vb * denom; w = vc * denom; return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); } equals( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest( value ) { if ( this._alphaTest > 0 !== value > 0 ) { this.version ++; } this._alphaTest = value; } onBuild( /* shaderobject, renderer */ ) {} onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} onBeforeCompile( /* shaderobject, renderer */ ) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues( values ) { if ( values === undefined ) return; for ( const key in values ) { const newValue = values[ key ]; if ( newValue === undefined ) { console.warn( `THREE.Material: parameter "${ key }" has value of undefined.` ); continue; } const currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( `THREE.Material: "${ key }" is not a property of THREE.${ this.type }.` ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else { this[ key ] = newValue; } } } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( isRootObject ) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.6, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.sheen !== undefined ) data.sheen = this.sheen; if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; } if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; } if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; } if ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy; if ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation; if ( this.anisotropyMap && this.anisotropyMap.isTexture ) { data.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid; } if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) { data.lightMap = this.lightMap.toJSON( meta ).uuid; data.lightMapIntensity = this.lightMapIntensity; } if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; if ( this.combine !== undefined ) data.combine = this.combine; } if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.transmission !== undefined ) data.transmission = this.transmission; if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; if ( this.thickness !== undefined ) data.thickness = this.thickness; if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); if ( this.size !== undefined ) data.size = this.size; if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors ) data.vertexColors = true; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; if ( this.polygonOffset === true ) data.polygonOffset = true; if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.alphaHash === true ) data.alphaHash = this.alphaHash; if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.forceSinglePass === true ) data.forceSinglePass = this.forceSinglePass; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.visible === false ) data.visible = false; if ( this.toneMapped === false ) data.toneMapped = false; if ( this.fog === false ) data.fog = false; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRootObject ) { const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if ( srcPlanes !== null ) { const n = srcPlanes.length; dstPlanes = new Array( n ); for ( let i = 0; i !== n; ++ i ) { dstPlanes[ i ] = srcPlanes[ i ].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaHash = source.alphaHash; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse( JSON.stringify( source.userData ) ); return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } set needsUpdate( value ) { if ( value === true ) this.version ++; } } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } class Color { constructor( r, g, b ) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; return this.set( r, g, b ); } set( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string const value = r; if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } } else { this.setRGB( r, g, b ); } return this; } setScalar( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex( hex, colorSpace = SRGBColorSpace ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo( h, 1 ); s = clamp( s, 0, 1 ); l = clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); const q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setStyle( style, colorSpace = SRGBColorSpace ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } let m; if ( m = /^(w+)(([^)]*))/.exec( style ) ) { // rgb / hsl let color; const name = m[ 1 ]; const components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255, colorSpace ); } if ( color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100, colorSpace ); } break; case "hsl": case "hsla": if ( color = /^s*(d*.?d+)s*,s*(d*.?d+)\%s*,s*(d*.?d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha( color[ 4 ] ); return this.setHSL( parseFloat( color[ 1 ] ) / 360, parseFloat( color[ 2 ] ) / 100, parseFloat( color[ 3 ] ) / 100, colorSpace ); } break; default: console.warn( "THREE.Color: Unknown color model " + style ); } } else if ( m = /^#([A-Fa-fd]+)$/.exec( style ) ) { // hex color const hex = m[ 1 ]; const size = hex.length; if ( size === 3 ) { // #ff0 return this.setRGB( parseInt( hex.charAt( 0 ), 16 ) / 15, parseInt( hex.charAt( 1 ), 16 ) / 15, parseInt( hex.charAt( 2 ), 16 ) / 15, colorSpace ); } else if ( size === 6 ) { // #ff0000 return this.setHex( parseInt( hex, 16 ), colorSpace ); } else { console.warn( "THREE.Color: Invalid hex color " + style ); } } else if ( style && style.length > 0 ) { return this.setColorName( style, colorSpace ); } return this; } setColorName( style, colorSpace = SRGBColorSpace ) { // color keywords const hex = _colorKeywords[ style.toLowerCase() ]; if ( hex !== undefined ) { // red this.setHex( hex, colorSpace ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } return this; } clone() { return new this.constructor( this.r, this.g, this.b ); } copy( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear( color ) { this.r = SRGBToLinear( color.r ); this.g = SRGBToLinear( color.g ); this.b = SRGBToLinear( color.b ); return this; } copyLinearToSRGB( color ) { this.r = LinearToSRGB( color.r ); this.g = LinearToSRGB( color.g ); this.b = LinearToSRGB( color.b ); return this; } convertSRGBToLinear() { this.copySRGBToLinear( this ); return this; } convertLinearToSRGB() { this.copyLinearToSRGB( this ); return this; } getHex( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); return Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) ); } getHexString( colorSpace = SRGBColorSpace ) { return ( "000000" + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } getHSL( target, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max( r, g, b ); const min = Math.min( r, g, b ); let hue, saturation; const lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB( target, colorSpace = ColorManagement.workingColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; if ( colorSpace !== SRGBColorSpace ) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`; } return `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`; } offsetHSL( h, s, l ) { this.getHSL( _hslA ); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } add( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar( s ) { this.r += s; this.g += s; this.b += s; return this; } sub( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; } multiply( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar( s ) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; } lerpColors( color1, color2, alpha ) { this.r = color1.r + ( color2.r - color1.r ) * alpha; this.g = color1.g + ( color2.g - color1.g ) * alpha; this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } lerpHSL( color, alpha ) { this.getHSL( _hslA ); color.getHSL( _hslB ); const h = lerp( _hslA.h, _hslB.h, alpha ); const s = lerp( _hslA.s, _hslB.s, alpha ); const l = lerp( _hslA.l, _hslB.l, alpha ); this.setHSL( h, s, l ); return this; } setFromVector3( v ) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3( m ) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b; this.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b; this.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b; return this; } equals( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); } fromArray( array, offset = 0 ) { this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; } fromBufferAttribute( attribute, index ) { this.r = attribute.getX( index ); this.g = attribute.getY( index ); this.b = attribute.getZ( index ); return this; } toJSON() { return this.getHex(); } *[ Symbol.iterator ]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/ new Color(); Color.NAMES = _colorKeywords; class MeshBasicMaterial extends Material { constructor( parameters ) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } // Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf const _tables = /*@__PURE__*/ _generateTables(); function _generateTables() { // float32 to float16 helpers const buffer = new ArrayBuffer( 4 ); const floatView = new Float32Array( buffer ); const uint32View = new Uint32Array( buffer ); const baseTable = new Uint32Array( 512 ); const shiftTable = new Uint32Array( 512 ); for ( let i = 0; i < 256; ++ i ) { const e = i - 127; // very small number (0, -0) if ( e < - 27 ) { baseTable[ i ] = 0x0000; baseTable[ i | 0x100 ] = 0x8000; shiftTable[ i ] = 24; shiftTable[ i | 0x100 ] = 24; // small number (denorm) } else if ( e < - 14 ) { baseTable[ i ] = 0x0400 >> ( - e - 14 ); baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; shiftTable[ i ] = - e - 1; shiftTable[ i | 0x100 ] = - e - 1; // normal number } else if ( e <= 15 ) { baseTable[ i ] = ( e + 15 ) << 10; baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; shiftTable[ i ] = 13; shiftTable[ i | 0x100 ] = 13; // large number (Infinity, -Infinity) } else if ( e < 128 ) { baseTable[ i ] = 0x7c00; baseTable[ i | 0x100 ] = 0xfc00; shiftTable[ i ] = 24; shiftTable[ i | 0x100 ] = 24; // stay (NaN, Infinity, -Infinity) } else { baseTable[ i ] = 0x7c00; baseTable[ i | 0x100 ] = 0xfc00; shiftTable[ i ] = 13; shiftTable[ i | 0x100 ] = 13; } } // float16 to float32 helpers const mantissaTable = new Uint32Array( 2048 ); const exponentTable = new Uint32Array( 64 ); const offsetTable = new Uint32Array( 64 ); for ( let i = 1; i < 1024; ++ i ) { let m = i << 13; // zero pad mantissa bits let e = 0; // zero exponent // normalized while ( ( m & 0x00800000 ) === 0 ) { m <<= 1; e -= 0x00800000; // decrement exponent } m &= ~ 0x00800000; // clear leading 1 bit e += 0x38800000; // adjust bias mantissaTable[ i ] = m | e; } for ( let i = 1024; i < 2048; ++ i ) { mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); } for ( let i = 1; i < 31; ++ i ) { exponentTable[ i ] = i << 23; } exponentTable[ 31 ] = 0x47800000; exponentTable[ 32 ] = 0x80000000; for ( let i = 33; i < 63; ++ i ) { exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); } exponentTable[ 63 ] = 0xc7800000; for ( let i = 1; i < 64; ++ i ) { if ( i !== 32 ) { offsetTable[ i ] = 1024; } } return { floatView: floatView, uint32View: uint32View, baseTable: baseTable, shiftTable: shiftTable, mantissaTable: mantissaTable, exponentTable: exponentTable, offsetTable: offsetTable }; } // float32 to float16 function toHalfFloat( val ) { if ( Math.abs( val ) > 65504 ) console.warn( "THREE.DataUtils.toHalfFloat(): Value out of range." ); val = clamp( val, - 65504, 65504 ); _tables.floatView[ 0 ] = val; const f = _tables.uint32View[ 0 ]; const e = ( f >> 23 ) & 0x1ff; return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] ); } // float16 to float32 function fromHalfFloat( val ) { const m = val >> 10; _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ]; return _tables.floatView[ 0 ]; } const DataUtils = { toHalfFloat: toHalfFloat, fromHalfFloat: fromHalfFloat, }; const _vector$8 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor( array, itemSize, normalized = false ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.gpuType = FloatType; this.version = 0; } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; this.gpuType = source.gpuType; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( let i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } copyArray( array ) { this.array.set( array ); return this; } applyMatrix3( m ) { if ( this.itemSize === 2 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector2$1.fromBufferAttribute( this, i ); _vector2$1.applyMatrix3( m ); this.setXY( i, _vector2$1.x, _vector2$1.y ); } } else if ( this.itemSize === 3 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix3( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } } return this; } applyMatrix4( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix4( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyNormalMatrix( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.transformDirection( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } set( value, offset = 0 ) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set( value, offset ); return this; } getComponent( index, component ) { let value = this.array[ index * this.itemSize + component ]; if ( this.normalized ) value = denormalize( value, this.array ); return value; } setComponent( index, component, value ) { if ( this.normalized ) value = normalize( value, this.array ); this.array[ index * this.itemSize + component ] = value; return this; } getX( index ) { let x = this.array[ index * this.itemSize ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.array[ index * this.itemSize ] = x; return this; } getY( index ) { let y = this.array[ index * this.itemSize + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.array[ index * this.itemSize + 1 ] = y; return this; } getZ( index ) { let z = this.array[ index * this.itemSize + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.array[ index * this.itemSize + 2 ] = z; return this; } getW( index ) { let w = this.array[ index * this.itemSize + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.array[ index * this.itemSize + 3 ] = w; return this; } setXY( index, x, y ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; } onUpload( callback ) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor( this.array, this.itemSize ).copy( this ); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from( this.array ), normalized: this.normalized }; if ( this.name !== "" ) data.name = this.name; if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; return data; } } // class Int8BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int8Array( array ), itemSize, normalized ); } } class Uint8BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint8Array( array ), itemSize, normalized ); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint8ClampedArray( array ), itemSize, normalized ); } } class Int16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int16Array( array ), itemSize, normalized ); } } class Uint16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); } } class Int32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int32Array( array ), itemSize, normalized ); } } class Uint32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint32Array( array ), itemSize, normalized ); } } class Float16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); this.isFloat16BufferAttribute = true; } getX( index ) { let x = fromHalfFloat( this.array[ index * this.itemSize ] ); if ( this.normalized ) x = denormalize( x, this.array ); return x; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.array[ index * this.itemSize ] = toHalfFloat( x ); return this; } getY( index ) { let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] ); if ( this.normalized ) y = denormalize( y, this.array ); return y; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.array[ index * this.itemSize + 1 ] = toHalfFloat( y ); return this; } getZ( index ) { let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] ); if ( this.normalized ) z = denormalize( z, this.array ); return z; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.array[ index * this.itemSize + 2 ] = toHalfFloat( z ); return this; } getW( index ) { let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] ); if ( this.normalized ) w = denormalize( w, this.array ); return w; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.array[ index * this.itemSize + 3 ] = toHalfFloat( w ); return this; } setXY( index, x, y ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); this.array[ index + 2 ] = toHalfFloat( z ); return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); this.array[ index + 2 ] = toHalfFloat( z ); this.array[ index + 3 ] = toHalfFloat( w ); return this; } } class Float32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float32Array( array ), itemSize, normalized ); } } class Float64BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float64Array( array ), itemSize, normalized ); } } let _id$1 = 0; const _m1 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$1 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$7 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty( this, "id", { value: _id$1 ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } return this; } getAttribute( name ) { return this.attributes[ name ]; } setAttribute( name, attribute ) { this.attributes[ name ] = attribute; return this; } deleteAttribute( name ) { delete this.attributes[ name ]; return this; } hasAttribute( name ) { return this.attributes[ name ] !== undefined; } addGroup( start, count, materialIndex = 0 ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex } ); } clearGroups() { this.groups = []; } setDrawRange( start, count ) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4( matrix ) { const position = this.attributes.position; if ( position !== undefined ) { position.applyMatrix4( matrix ); position.needsUpdate = true; } const normal = this.attributes.normal; if ( normal !== undefined ) { const normalMatrix = new Matrix3().getNormalMatrix( matrix ); normal.applyNormalMatrix( normalMatrix ); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if ( tangent !== undefined ) { tangent.transformDirection( matrix ); tangent.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; } applyQuaternion( q ) { _m1.makeRotationFromQuaternion( q ); this.applyMatrix4( _m1 ); return this; } rotateX( angle ) { // rotate geometry around world x-axis _m1.makeRotationX( angle ); this.applyMatrix4( _m1 ); return this; } rotateY( angle ) { // rotate geometry around world y-axis _m1.makeRotationY( angle ); this.applyMatrix4( _m1 ); return this; } rotateZ( angle ) { // rotate geometry around world z-axis _m1.makeRotationZ( angle ); this.applyMatrix4( _m1 ); return this; } translate( x, y, z ) { // translate geometry _m1.makeTranslation( x, y, z ); this.applyMatrix4( _m1 ); return this; } scale( x, y, z ) { // scale geometry _m1.makeScale( x, y, z ); this.applyMatrix4( _m1 ); return this; } lookAt( vector ) { _obj.lookAt( vector ); _obj.updateMatrix(); this.applyMatrix4( _obj.matrix ); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter( _offset ).negate(); this.translate( _offset.x, _offset.y, _offset.z ); return this; } setFromPoints( points ) { const position = []; for ( let i = 0, l = points.length; i < l; i ++ ) { const point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.setAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; } computeBoundingBox() { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingBox.set( new Vector3( - Infinity, - Infinity, - Infinity ), new Vector3( + Infinity, + Infinity, + Infinity ) ); return; } if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _box$1.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( this.boundingBox.min, _box$1.min ); this.boundingBox.expandByPoint( _vector$7 ); _vector$7.addVectors( this.boundingBox.max, _box$1.max ); this.boundingBox.expandByPoint( _vector$7 ); } else { this.boundingBox.expandByPoint( _box$1.min ); this.boundingBox.expandByPoint( _box$1.max ); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } } computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingSphere.set( new Vector3(), Infinity ); return; } if ( position ) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _boxMorphTargets.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( _box$1.min, _boxMorphTargets.min ); _box$1.expandByPoint( _vector$7 ); _vector$7.addVectors( _box$1.max, _boxMorphTargets.max ); _box$1.expandByPoint( _vector$7 ); } else { _box$1.expandByPoint( _boxMorphTargets.min ); _box$1.expandByPoint( _boxMorphTargets.max ); } } } _box$1.getCenter( center ); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for ( let i = 0, il = position.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( position, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; const morphTargetsRelative = this.morphTargetsRelative; for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { _vector$7.fromBufferAttribute( morphAttribute, j ); if ( morphTargetsRelative ) { _offset.fromBufferAttribute( position, j ); _vector$7.add( _offset ); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } } } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)" ); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if ( this.hasAttribute( "tangent" ) === false ) { this.setAttribute( "tangent", new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } const tangents = this.getAttribute( "tangent" ).array; const tan1 = [], tan2 = []; for ( let i = 0; i < nVertices; i ++ ) { tan1[ i ] = new Vector3(); tan2[ i ] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle( a, b, c ) { vA.fromArray( positions, a * 3 ); vB.fromArray( positions, b * 3 ); vC.fromArray( positions, c * 3 ); uvA.fromArray( uvs, a * 2 ); uvB.fromArray( uvs, b * 2 ); uvC.fromArray( uvs, c * 2 ); vB.sub( vA ); vC.sub( vA ); uvB.sub( uvA ); uvC.sub( uvA ); const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); // silently ignore degenerate uv triangles having coincident or colinear vertices if ( ! isFinite( r ) ) return; sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); } let groups = this.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.length } ]; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleTriangle( indices[ j + 0 ], indices[ j + 1 ], indices[ j + 2 ] ); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex( v ) { n.fromArray( normals, v * 3 ); n2.copy( n ); const t = tan1[ v ]; // Gram-Schmidt orthogonalize tmp.copy( t ); tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2.crossVectors( n2, t ); const test = tmp2.dot( tan2[ v ] ); const w = ( test < 0.0 ) ? - 1.0 : 1.0; tangents[ v * 4 ] = tmp.x; tangents[ v * 4 + 1 ] = tmp.y; tangents[ v * 4 + 2 ] = tmp.z; tangents[ v * 4 + 3 ] = w; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleVertex( indices[ j + 0 ] ); handleVertex( indices[ j + 1 ] ); handleVertex( indices[ j + 2 ] ); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute( "position" ); if ( positionAttribute !== undefined ) { let normalAttribute = this.getAttribute( "normal" ); if ( normalAttribute === undefined ) { normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); this.setAttribute( "normal", normalAttribute ); } else { // reset existing normals to zero for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { normalAttribute.setXYZ( i, 0, 0, 0 ); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { for ( let i = 0, il = index.count; i < il; i += 3 ) { const vA = index.getX( i + 0 ); const vB = index.getX( i + 1 ); const vC = index.getX( i + 2 ); pA.fromBufferAttribute( positionAttribute, vA ); pB.fromBufferAttribute( positionAttribute, vB ); pC.fromBufferAttribute( positionAttribute, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); nA.fromBufferAttribute( normalAttribute, vA ); nB.fromBufferAttribute( normalAttribute, vB ); nC.fromBufferAttribute( normalAttribute, vC ); nA.add( cb ); nB.add( cb ); nC.add( cb ); normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); } } else { // non-indexed elements (unconnected triangle soup) for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { pA.fromBufferAttribute( positionAttribute, i + 0 ); pB.fromBufferAttribute( positionAttribute, i + 1 ); pC.fromBufferAttribute( positionAttribute, i + 2 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } normalizeNormals() { const normals = this.attributes.normal; for ( let i = 0, il = normals.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( normals, i ); _vector$7.normalize(); normals.setXYZ( i, _vector$7.x, _vector$7.y, _vector$7.z ); } } toNonIndexed() { function convertBufferAttribute( attribute, indices ) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor( indices.length * itemSize ); let index = 0, index2 = 0; for ( let i = 0, l = indices.length; i < l; i ++ ) { if ( attribute.isInterleavedBufferAttribute ) { index = indices[ i ] * attribute.data.stride + attribute.offset; } else { index = indices[ i ] * itemSize; } for ( let j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } return new BufferAttribute( array2, itemSize, normalized ); } // if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed." ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for ( const name in attributes ) { const attribute = attributes[ name ]; const newAttribute = convertBufferAttribute( attribute, indices ); geometry2.setAttribute( name, newAttribute ); } // morph attributes const morphAttributes = this.morphAttributes; for ( const name in morphAttributes ) { const morphArray = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { const attribute = morphAttribute[ i ]; const newAttribute = convertBufferAttribute( attribute, indices ); morphArray.push( newAttribute ); } geometry2.morphAttributes[ name ] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; } toJSON() { const data = { metadata: { version: 4.6, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { const parameters = this.parameters; for ( const key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if ( index !== null ) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call( index.array ) }; } const attributes = this.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; data.data.attributes[ key ] = attribute.toJSON( data.data ); } const morphAttributes = {}; let hasMorphAttributes = false; for ( const key in this.morphAttributes ) { const attributeArray = this.morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; array.push( attribute.toJSON( data.data ) ); } if ( array.length > 0 ) { morphAttributes[ key ] = array; hasMorphAttributes = true; } } if ( hasMorphAttributes ) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } const boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if ( index !== null ) { this.setIndex( index.clone( data ) ); } // attributes const attributes = source.attributes; for ( const name in attributes ) { const attribute = attributes[ name ]; this.setAttribute( name, attribute.clone( data ) ); } // morph attributes const morphAttributes = source.morphAttributes; for ( const name in morphAttributes ) { const array = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone( data ) ); } this.morphAttributes[ name ] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box const boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); const _ray$3 = /*@__PURE__*/ new Ray(); const _sphere$5 = /*@__PURE__*/ new Sphere(); const _sphereHitAt = /*@__PURE__*/ new Vector3(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _normalA = /*@__PURE__*/ new Vector3(); const _normalB = /*@__PURE__*/ new Vector3(); const _normalC = /*@__PURE__*/ new Vector3(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } getVertexPosition( index, target ) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute( position, index ); const morphInfluences = this.morphTargetInfluences; if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { const influence = morphInfluences[ i ]; const morphAttribute = morphPosition[ i ]; if ( influence === 0 ) continue; _tempA.fromBufferAttribute( morphAttribute, index ); if ( morphTargetsRelative ) { _morphA.addScaledVector( _tempA, influence ); } else { _morphA.addScaledVector( _tempA.sub( target ), influence ); } } target.add( _morphA ); } return target; } raycast( raycaster, intersects ) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // test with bounding sphere in world space if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$5.copy( geometry.boundingSphere ); _sphere$5.applyMatrix4( matrixWorld ); // check distance from ray origin to bounding sphere _ray$3.copy( raycaster.ray ).recast( raycaster.near ); if ( _sphere$5.containsPoint( _ray$3.origin ) === false ) { if ( _ray$3.intersectSphere( _sphere$5, _sphereHitAt ) === null ) return; if ( _ray$3.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return; } // convert ray to local space of mesh _inverseMatrix$3.copy( matrixWorld ).invert(); _ray$3.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$3 ); // test with bounding box in local space if ( geometry.boundingBox !== null ) { if ( _ray$3.intersectsBox( geometry.boundingBox ) === false ) return; } // test for intersections with geometry this._computeIntersections( raycaster, intersects, _ray$3 ); } _computeIntersections( raycaster, intersects, rayLocalSpace ) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if ( index !== null ) { // indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = index.getX( j ); const b = index.getX( j + 1 ); const c = index.getX( j + 2 ); intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = index.getX( i ); const b = index.getX( i + 1 ); const c = index.getX( i + 2 ); intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } } else if ( position !== undefined ) { // non-indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } } } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { let intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point ); } if ( intersect === null ) return null; _intersectionPointWorld.copy( point ); _intersectionPointWorld.applyMatrix4( object.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) { object.getVertexPosition( a, _vA$1 ); object.getVertexPosition( b, _vB$1 ); object.getVertexPosition( c, _vC$1 ); const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); if ( intersection ) { if ( uv ) { _uvA$1.fromBufferAttribute( uv, a ); _uvB$1.fromBufferAttribute( uv, b ); _uvC$1.fromBufferAttribute( uv, c ); intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } if ( uv1 ) { _uvA$1.fromBufferAttribute( uv1, a ); _uvB$1.fromBufferAttribute( uv1, b ); _uvC$1.fromBufferAttribute( uv1, c ); intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); intersection.uv2 = intersection.uv1; // @deprecated, r152 } if ( normal ) { _normalA.fromBufferAttribute( normal, a ); _normalB.fromBufferAttribute( normal, b ); _normalC.fromBufferAttribute( normal, c ); intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3() ); if ( intersection.normal.dot( ray.direction ) > 0 ) { intersection.normal.multiplyScalar( - 1 ); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor( widthSegments ); heightSegments = Math.floor( heightSegments ); depthSegments = Math.floor( depthSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segmentHeight - heightHalf; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); } } /** * Uniform Utilities */ function cloneUniforms( src ) { const dst = {}; for ( const u in src ) { dst[ u ] = {}; for ( const p in src[ u ] ) { const property = src[ u ][ p ]; if ( property && ( property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion ) ) { if ( property.isRenderTargetTexture ) { console.warn( "UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms()." ); dst[ u ][ p ] = null; } else { dst[ u ][ p ] = property.clone(); } } else if ( Array.isArray( property ) ) { dst[ u ][ p ] = property.slice(); } else { dst[ u ][ p ] = property; } } } return dst; } function mergeUniforms( uniforms ) { const merged = {}; for ( let u = 0; u < uniforms.length; u ++ ) { const tmp = cloneUniforms( uniforms[ u ] ); for ( const p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; } function cloneUniformsGroups( src ) { const dst = []; for ( let u = 0; u < src.length; u ++ ) { dst.push( src[ u ].clone() ); } return dst; } function getUnlitUniformColorSpace( renderer ) { if ( renderer.getRenderTarget() === null ) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } return LinearSRGBColorSpace; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor( parameters ) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv1": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if ( parameters !== undefined ) { this.setValues( parameters ); } } copy( source ) { super.copy( source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms( source.uniforms ); this.uniformsGroups = cloneUniformsGroups( source.uniformsGroups ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.glslVersion = this.glslVersion; data.uniforms = {}; for ( const name in this.uniforms ) { const uniform = this.uniforms[ name ]; const value = uniform.value; if ( value && value.isTexture ) { data.uniforms[ name ] = { type: "t", value: value.toJSON( meta ).uuid }; } else if ( value && value.isColor ) { data.uniforms[ name ] = { type: "c", value: value.getHex() }; } else if ( value && value.isVector2 ) { data.uniforms[ name ] = { type: "v2", value: value.toArray() }; } else if ( value && value.isVector3 ) { data.uniforms[ name ] = { type: "v3", value: value.toArray() }; } else if ( value && value.isVector4 ) { data.uniforms[ name ] = { type: "v4", value: value.toArray() }; } else if ( value && value.isMatrix3 ) { data.uniforms[ name ] = { type: "m3", value: value.toArray() }; } else if ( value && value.isMatrix4 ) { data.uniforms[ name ] = { type: "m4", value: value.toArray() }; } else { data.uniforms[ name ] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for ( const key in this.extensions ) { if ( this.extensions[ key ] === true ) extensions[ key ] = true; } if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; return data; } } class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); this.coordinateSystem = WebGLCoordinateSystem; } copy( source, recursive ) { super.copy( source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); this.coordinateSystem = source.coordinateSystem; return this; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } updateWorldMatrix( updateParents, updateChildren ) { super.updateWorldMatrix( updateParents, updateChildren ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } clone() { return new this.constructor().copy( this ); } } class PerspectiveCamera extends Camera { constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength( focalLength ) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan( Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = - 0.5 * width; const view = this.view; if ( this.view !== null && this.view.enabled ) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = - 90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor( near, far, renderTarget ) { super(); this.type = "CubeCamera"; this.renderTarget = renderTarget; this.coordinateSystem = null; const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.layers = this.layers; this.add( cameraPX ); const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.layers = this.layers; this.add( cameraNX ); const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.layers = this.layers; this.add( cameraPY ); const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.layers = this.layers; this.add( cameraNY ); const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.layers = this.layers; this.add( cameraPZ ); const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.layers = this.layers; this.add( cameraNZ ); } updateCoordinateSystem() { const coordinateSystem = this.coordinateSystem; const cameras = this.children.concat(); const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; for ( const camera of cameras ) this.remove( camera ); if ( coordinateSystem === WebGLCoordinateSystem ) { cameraPX.up.set( 0, 1, 0 ); cameraPX.lookAt( 1, 0, 0 ); cameraNX.up.set( 0, 1, 0 ); cameraNX.lookAt( - 1, 0, 0 ); cameraPY.up.set( 0, 0, - 1 ); cameraPY.lookAt( 0, 1, 0 ); cameraNY.up.set( 0, 0, 1 ); cameraNY.lookAt( 0, - 1, 0 ); cameraPZ.up.set( 0, 1, 0 ); cameraPZ.lookAt( 0, 0, 1 ); cameraNZ.up.set( 0, 1, 0 ); cameraNZ.lookAt( 0, 0, - 1 ); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( - 1, 0, 0 ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( 1, 0, 0 ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( 0, 1, 0 ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( 0, - 1, 0 ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( 0, 0, 1 ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( 0, 0, - 1 ); } else { throw new Error( "THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: " + coordinateSystem ); } for ( const camera of cameras ) { this.add( camera ); camera.updateMatrixWorld(); } } update( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); const renderTarget = this.renderTarget; if ( this.coordinateSystem !== renderer.coordinateSystem ) { this.coordinateSystem = renderer.coordinateSystem; this.updateCoordinateSystem(); } const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentXrEnabled = renderer.xr.enabled; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget( renderTarget, 0 ); renderer.render( scene, cameraPX ); renderer.setRenderTarget( renderTarget, 1 ); renderer.render( scene, cameraNX ); renderer.setRenderTarget( renderTarget, 2 ); renderer.render( scene, cameraPY ); renderer.setRenderTarget( renderTarget, 3 ); renderer.render( scene, cameraNY ); renderer.setRenderTarget( renderTarget, 4 ); renderer.render( scene, cameraPZ ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget( renderTarget, 5 ); renderer.render( scene, cameraNZ ); renderer.setRenderTarget( currentRenderTarget ); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images( value ) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor( size = 1, options = {} ) { super( size, size, options ); this.isWebGLCubeRenderTarget = true; const image = { width: size, height: size, depth: 1 }; const images = [ image, image, image, image, image, image ]; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null }, }, vertexShader: /* glsl */` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry( 5, 5, 5 ); const material = new ShaderMaterial( { name: "CubemapFromEquirect", uniforms: cloneUniforms( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending } ); material.uniforms.tEquirect.value = texture; const mesh = new Mesh( geometry, material ); const currentMinFilter = texture.minFilter; // Avoid blurred poles if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; const camera = new CubeCamera( 1, 10, this ); camera.update( renderer, mesh ); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear( renderer, color, depth, stencil ) { const currentRenderTarget = renderer.getRenderTarget(); for ( let i = 0; i < 6; i ++ ) { renderer.setRenderTarget( this, i ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( currentRenderTarget ); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; } setComponents( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; } setFromNormalAndCoplanarPoint( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; } setFromCoplanarPoints( a, b, c ) { const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; } copy( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= - 1; this.normal.negate(); return this; } distanceToPoint( point ) { return this.normal.dot( point ) + this.constant; } distanceToSphere( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; } projectPoint( point, target ) { return target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) ); } intersectLine( line, target ) { const direction = line.delta( _vector1 ); const denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return null; } const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return null; } return target.copy( line.start ).addScaledVector( direction, t ); } intersectsLine( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint( line.start ); const endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); } intersectsBox( box ) { return box.intersectsPlane( this ); } intersectsSphere( sphere ) { return sphere.intersectsPlane( this ); } coplanarPoint( target ) { return target.copy( this.normal ).multiplyScalar( - this.constant ); } applyMatrix4( matrix, optionalNormalMatrix ) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; } translate( offset ) { this.constant -= offset.dot( this.normal ); return this; } equals( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } clone() { return new this.constructor().copy( this ); } } const _sphere$4 = /*@__PURE__*/ new Sphere(); const _vector$6 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { this.planes = [ p0, p1, p2, p3, p4, p5 ]; } set( p0, p1, p2, p3, p4, p5 ) { const planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; } copy( frustum ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; } setFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem ) { const planes = this.planes; const me = m.elements; const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); if ( coordinateSystem === WebGLCoordinateSystem ) { planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); } else { throw new Error( "THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: " + coordinateSystem ); } return this; } intersectsObject( object ) { if ( object.boundingSphere !== undefined ) { if ( object.boundingSphere === null ) object.computeBoundingSphere(); _sphere$4.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld ); } else { const geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$4.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); } return this.intersectsSphere( _sphere$4 ); } intersectsSprite( sprite ) { _sphere$4.center.set( 0, 0, 0 ); _sphere$4.radius = 0.7071067811865476; _sphere$4.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( _sphere$4 ); } intersectsSphere( sphere ) { const planes = this.planes; const center = sphere.center; const negRadius = - sphere.radius; for ( let i = 0; i < 6; i ++ ) { const distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; } intersectsBox( box ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { const plane = planes[ i ]; // corner at max distance _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; if ( plane.distanceToPoint( _vector$6 ) < 0 ) { return false; } } return true; } containsPoint( point ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } clone() { return new this.constructor().copy( this ); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame( time, frame ) { animationLoop( time, frame ); requestId = context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; requestId = context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { context.cancelAnimationFrame( requestId ); isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } function WebGLAttributes( gl, capabilities ) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); let type; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { if ( isWebGL2 ) { type = gl.HALF_FLOAT; } else { throw new Error( "THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2." ); } } else { type = gl.UNSIGNED_SHORT; } } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } else if ( array instanceof Uint8ClampedArray ) { type = gl.UNSIGNED_BYTE; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else { if ( isWebGL2 ) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); } updateRange.count = - 1; // reset range } attribute.onUploadCallback(); } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isGLBufferAttribute ) { const cached = buffers.get( attribute ); if ( ! cached || cached.version < attribute.version ) { buffers.set( attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version } ); } return; } if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor( widthSegments ); const gridY = Math.floor( heightSegments ); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segment_height - height_half; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = ix + gridX1 * iy; const b = ix + gridX1 * ( iy + 1 ); const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = ( ix + 1 ) + gridX1 * iy; indices.push( a, b, d ); indices.push( b, c, d ); } } this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); } } var alphahash_fragment = "#ifdef USE_ALPHAHASH if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; #endif"; var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH const float ALPHA_HASH_SCALE = 0.05; float hash2D( vec2 value ) { return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); } float hash3D( vec3 value ) { return hash2D( vec2( hash2D( value.xy ), value.z ) ); } float getAlphaHashThreshold( vec3 position ) { float maxDeriv = max( length( dFdx( position.xyz ) ), length( dFdy( position.xyz ) ) ); float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); vec2 pixScales = vec2( exp2( floor( log2( pixScale ) ) ), exp2( ceil( log2( pixScale ) ) ) ); vec2 alpha = vec2( hash3D( floor( pixScales.x * position.xyz ) ), hash3D( floor( pixScales.y * position.xyz ) ) ); float lerpFactor = fract( log2( pixScale ) ); float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; float a = min( lerpFactor, 1.0 - lerpFactor ); vec3 cases = vec3( x * x / ( 2.0 * a * ( 1.0 - a ) ), ( x - 0.5 * a ) / ( 1.0 - a ), 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) ); float threshold = ( x < ( 1.0 - a ) ) ? ( ( x < a ) ? cases.x : cases.y ) : cases.z; return clamp( threshold , 1.0e-6, 1.0 ); } #endif"; var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position ); #ifdef USE_ALPHAHASH vPosition = vec3( position ); #endif"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 rgb = XYZ_TO_REC709 * xyz; return rgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++ m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vBumpMapUv ); vec2 dSTdy = dFdy( vBumpMapUv ); float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = dFdx( surf_pos.xyz ); vec3 vSigmaY = dFdy( surf_pos.xyz ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; #ifdef USE_ALPHAHASH varying vec3 vPosition; #endif vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float luminance( const in vec3 rgb ) { const vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 ); return dot( weights, rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); } vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define cubeUV_r0 1.0 #define cubeUV_v0 0.339 #define cubeUV_m0 - 2.0 #define cubeUV_r1 0.8 #define cubeUV_v1 0.276 #define cubeUV_m1 - 1.0 #define cubeUV_r4 0.4 #define cubeUV_v4 0.046 #define cubeUV_m4 2.0 #define cubeUV_r5 0.305 #define cubeUV_v5 0.016 #define cubeUV_m5 3.0 #define cubeUV_r6 0.21 #define cubeUV_v6 0.0038 #define cubeUV_m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= cubeUV_r1 ) { mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; } else if ( roughness >= cubeUV_r4 ) { mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; } else if ( roughness >= cubeUV_r5 ) { mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; } else if ( roughness >= cubeUV_r6 ) { mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_fragment = "LambertMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; }; void RE_Direct_Lambert( const in IncidentLight directLight, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert #define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( LEGACY_LIGHTS ) if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #else float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP vec3 getIBLIrradiance( const in vec3 normal ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #ifdef USE_ANISOTROPY vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 bentNormal = cross( bitangent, viewDir ); bentNormal = normalize( cross( bentNormal, bitangent ) ); bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); return getIBLRadiance( viewDir, bentNormal, roughness ); #else return vec3( 0.0 ); #endif } #endif #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR material.ior = ior; #ifdef USE_SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULAR_COLORMAP specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; #endif #ifdef USE_SPECULAR_INTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEEN_COLORMAP material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEEN_ROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; #endif #endif #ifdef USE_ANISOTROPY #ifdef USE_ANISOTROPYMAP mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; #else vec2 anisotropyV = anisotropyVector; #endif material.anisotropy = length( anisotropyV ); anisotropyV /= material.anisotropy; material.anisotropy = saturate( material.anisotropy ); material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); material.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y; material.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y; #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif #ifdef IOR float ior; #endif #ifdef USE_TRANSMISSION float transmission; float transmissionAlpha; float thickness; float attenuationDistance; vec3 attenuationColor; #endif #ifdef USE_ANISOTROPY float anisotropy; float alphaT; vec3 anisotropyT; vec3 anisotropyB; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } #ifdef USE_ANISOTROPY float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); float v = 0.5 / ( gv + gl ); return saturate(v); } float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { float a2 = alphaT * alphaB; highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); highp float v2 = dot( v, v ); float w2 = a2 / v2; return RECIPROCAL_PI * a2 * pow2 ( w2 ); } #endif #ifdef USE_CLEARCOAT vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { vec3 f0 = material.clearcoatF0; float f90 = material.clearcoatF90; float roughness = material.clearcoatRoughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { vec3 f0 = material.specularColor; float f90 = material.specularF90; float roughness = material.roughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); #ifdef USE_IRIDESCENCE F = mix( F, material.iridescenceFresnel, material.iridescence ); #endif #ifdef USE_ANISOTROPY float dotTL = dot( material.anisotropyT, lightDir ); float dotTV = dot( material.anisotropyT, viewDir ); float dotTH = dot( material.anisotropyT, halfDir ); float dotBL = dot( material.anisotropyB, lightDir ); float dotBV = dot( material.anisotropyB, viewDir ); float dotBH = dot( material.anisotropyB, halfDir ); float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); #else float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); #endif return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometry.viewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; vec4 spotColor; vec3 spotLightCoord; bool inSpotLightMap; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS #else #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #endif #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; #endif #undef SPOT_LIGHT_MAP_INDEX #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) #ifdef USE_ANISOTROPY radiance += getIBLAnisotropyRadiance( geometry.viewDir, geometry.normal, material.roughness, material.anisotropyB, material.anisotropy ); #else radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #endif #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP diffuseColor *= texture2D( map, vMapUv ); #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal *= faceDirection; #endif #endif #if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) #ifdef USE_TANGENT mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn = getTangentFrame( - vViewPosition, normal, #if defined( USE_NORMALMAP ) vNormalMapUv #elif defined( USE_CLEARCOAT_NORMALMAP ) vClearcoatNormalMapUv #else vUv #endif ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn[0] *= faceDirection; tbn[1] *= faceDirection; #endif #endif #ifdef USE_CLEARCOAT_NORMALMAP #ifdef USE_TANGENT mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn2[0] *= faceDirection; tbn2[1] *= faceDirection; #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( USE_NORMALMAP_TANGENTSPACE ) vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef USE_NORMALMAP_OBJECTSPACE uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { vec3 q0 = dFdx( eye_pos.xyz ); vec3 q1 = dFdy( eye_pos.xyz ); vec2 st0 = dFdx( uv.st ); vec2 st1 = dFdy( uv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var opaque_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec2 packDepthToRG( in highp float v ) { return packDepthToRGBA( v ).yx; } float unpackRGToDepth( const in highp vec2 v ) { return unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { return depth * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * depth - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; bool frustumTest = inFrustum && shadowCoord.z <= 1.0; if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if defined( USE_SHADOWMAP ) #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif #if NUM_SPOT_LIGHT_COORDS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { shadowWorldPosition = worldPosition; #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; #endif vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return saturate( toneMappingExposure * color ); } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; material.attenuationDistance = attenuationDistance; material.attenuationColor = attenuationColor; #ifdef USE_TRANSMISSIONMAP material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; #endif #ifdef USE_THICKNESSMAP material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmitted = getIBLVolumeRefraction( n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness, material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; float w0( float a ) { return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); } float w1( float a ) { return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); } float w2( float a ){ return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); } float w3( float a ) { return ( 1.0 / 6.0 ) * ( a * a * a ); } float g0( float a ) { return w0( a ) + w1( a ); } float g1( float a ) { return w2( a ) + w3( a ); } float h0( float a ) { return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); } float h1( float a ) { return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); } vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { uv = uv * texelSize.zw + 0.5; vec2 iuv = floor( uv ); vec2 fuv = fract( uv ); float g0x = g0( fuv.x ); float g1x = g1( fuv.x ); float h0x = h0( fuv.x ); float h1x = h1( fuv.x ); float h0y = h0( fuv.y ); float h1y = h1( fuv.y ); vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); } vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); vec2 fLodSizeInv = 1.0 / fLodSize; vec2 cLodSizeInv = 1.0 / cLodSize; vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); return mix( fSample, cSample, fract( lod ) ); } vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); } vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( isinf( attenuationDistance ) ) { return vec3( 1.0 ); } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 attenuatedColor = transmittance * transmittedLight.rgb; vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); } #endif"; var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP varying vec2 vNormalMapUv; #endif #ifdef USE_EMISSIVEMAP varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SHEEN_COLORMAP varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_SPECULARMAP varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP uniform mat3 mapTransform; varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP uniform mat3 alphaMapTransform; varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP uniform mat3 lightMapTransform; varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP uniform mat3 aoMapTransform; varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP uniform mat3 bumpMapTransform; varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP uniform mat3 normalMapTransform; varying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP uniform mat3 displacementMapTransform; varying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP uniform mat3 emissiveMapTransform; varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP uniform mat3 metalnessMapTransform; varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP uniform mat3 roughnessMapTransform; varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP uniform mat3 anisotropyMapTransform; varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP uniform mat3 clearcoatMapTransform; varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform mat3 clearcoatNormalMapTransform; varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform mat3 clearcoatRoughnessMapTransform; varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP uniform mat3 sheenColorMapTransform; varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform mat3 sheenRoughnessMapTransform; varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP uniform mat3 iridescenceMapTransform; varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform mat3 iridescenceThicknessMapTransform; varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP uniform mat3 specularMapTransform; varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP uniform mat3 specularColorMapTransform; varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform mat3 specularIntensityMapTransform; varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ANISOTROPYMAP vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$h = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$h = "uniform sampler2D t2D; uniform float backgroundIntensity; varying vec2 vUv; void main() { vec4 texColor = texture2D( t2D, vUv ); texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$g = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #elif defined( ENVMAP_TYPE_CUBE_UV ) uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform float backgroundBlurriness; uniform float backgroundIntensity; varying vec3 vWorldDirection; #include void main() { #ifdef ENVMAP_TYPE_CUBE vec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness ); #else vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldDirection; void main() { vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); gl_FragColor = texColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$9 = "#define LAMBERT uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif #ifdef USE_ANISOTROPY uniform vec2 anisotropyVector; #ifdef USE_ANISOTROPYMAP uniform sampler2D anisotropyMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include #ifdef USE_POINTS_UV varying vec2 vUv; uniform mat3 uvTransform; #endif void main() { #ifdef USE_POINTS_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphahash_fragment: alphahash_fragment, alphahash_pars_fragment: alphahash_pars_fragment, alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, colorspace_fragment: colorspace_fragment, colorspace_pars_fragment: colorspace_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, opaque_fragment: opaque_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null }, specularMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // physical refractionRatio: { value: 0.98 }, // basic, lambert, phong }, aomap: { aoMap: { value: null }, aoMapIntensity: { value: 1 }, aoMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, lightmap: { lightMap: { value: null }, lightMapIntensity: { value: 1 }, lightMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, bumpmap: { bumpMap: { value: null }, bumpMapTransform: { value: /*@__PURE__*/ new Matrix3() }, bumpScale: { value: 1 } }, normalmap: { normalMap: { value: null }, normalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, normalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) } }, displacementmap: { displacementMap: { value: null }, displacementMapTransform: { value: /*@__PURE__*/ new Matrix3() }, displacementScale: { value: 1 }, displacementBias: { value: 0 } }, emissivemap: { emissiveMap: { value: null }, emissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, metalnessmap: { metalnessMap: { value: null }, metalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, roughnessmap: { roughnessMap: { value: null }, roughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, gradientmap: { gradientMap: { value: null } }, fog: { fogDensity: { value: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) } }, 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: {} } }, spotLightMap: { value: [] }, spotShadowMap: { value: [] }, spotLightMatrix: { 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/ new Matrix3() } }, sprite: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 } } }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specular: { value: /*@__PURE__*/ new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } } ] ), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.sprite, UniformsLib.fog ] ), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: /*@__PURE__*/ new Matrix3() }, t2D: { value: null }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, backgroundCube: { uniforms: { envMap: { value: null }, flipEnvMap: { value: - 1 }, backgroundBlurriness: { value: 0 }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.backgroundCube_vert, fragmentShader: ShaderChunk.backgroundCube_frag }, cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: /*@__PURE__*/ new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.lights, UniformsLib.fog, { color: { value: /*@__PURE__*/ new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms( [ ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalMap: { value: null }, clearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescenceIOR: { value: 1.3 }, iridescenceThicknessMinimum: { value: 100 }, iridescenceThicknessMaximum: { value: 400 }, iridescenceThicknessMap: { value: null }, iridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheen: { value: 0 }, sheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, sheenColorMap: { value: null }, sheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, sheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, thicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, attenuationDistance: { value: 0 }, attenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) }, specularColorMap: { value: null }, specularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() }, anisotropyVector: { value: /*@__PURE__*/ new Vector2() }, anisotropyMap: { value: null }, anisotropyMapTransform: { value: /*@__PURE__*/ new Matrix3() }, } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; const _rgb = { r: 0, b: 0, g: 0 }; function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render( renderList, scene ) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = ( usePMREM ? cubeuvmaps : cubemaps ).get( background ); } if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } const xr = renderer.xr; const environmentBlendMode = xr.getEnvironmentBlendMode(); switch ( environmentBlendMode ) { case "opaque": forceClear = true; break; case "additive": state.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha ); forceClear = true; break; case "alpha-blend": state.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha ); forceClear = true; break; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), new ShaderMaterial( { name: "BackgroundCubeMaterial", uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false } ) ); boxMesh.geometry.deleteAttribute( "normal" ); boxMesh.geometry.deleteAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } } ); objects.update( boxMesh ); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); } else if ( background && background.isTexture ) { if ( planeMesh === undefined ) { planeMesh = new Mesh( new PlaneGeometry( 2, 2 ), new ShaderMaterial( { name: "BackgroundMaterial", uniforms: cloneUniforms( ShaderLib.background.uniforms ), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false } ) ); planeMesh.geometry.deleteAttribute( "normal" ); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } } ); objects.update( planeMesh ); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( background.matrixAutoUpdate === true ) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); } } function setClear( color, alpha ) { color.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) ); state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha = 1 ) { clearColor.set( color ); clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const extension = capabilities.isWebGL2 ? null : extensions.get( "OES_vertex_array_object" ); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState( null ); let currentState = defaultState; let forceUpdate = false; function setup( object, material, program, geometry, index ) { let updateBuffers = false; if ( vaoAvailable ) { const state = getBindingState( geometry, program, material ); if ( currentState !== state ) { currentState = state; bindVertexArrayObject( currentState.object ); } updateBuffers = needsUpdate( object, geometry, program, index ); if ( updateBuffers ) saveCache( object, geometry, program, index ); } else { const wireframe = ( material.wireframe === true ); if ( currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe ) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } if ( updateBuffers || forceUpdate ) { forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); if ( index !== null ) { gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer ); } } } function createVertexArrayObject() { if ( capabilities.isWebGL2 ) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); return extension.bindVertexArrayOES( vao ); } function deleteVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); return extension.deleteVertexArrayOES( vao ); } function getBindingState( geometry, program, material ) { const wireframe = ( material.wireframe === true ); let programMap = bindingStates[ geometry.id ]; if ( programMap === undefined ) { programMap = {}; bindingStates[ geometry.id ] = programMap; } let stateMap = programMap[ program.id ]; if ( stateMap === undefined ) { stateMap = {}; programMap[ program.id ] = stateMap; } let state = stateMap[ wireframe ]; if ( state === undefined ) { state = createBindingState( createVertexArrayObject() ); stateMap[ wireframe ] = state; } return state; } function createBindingState( vao ) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for ( let i = 0; i < maxVertexAttributes; i ++ ) { newAttributes[ i ] = 0; enabledAttributes[ i ] = 0; attributeDivisors[ i ] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate( object, geometry, program, index ) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { const cachedAttribute = cachedAttributes[ name ]; let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( cachedAttribute === undefined ) return true; if ( cachedAttribute.attribute !== geometryAttribute ) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum ++; } } if ( currentState.attributesNum !== attributesNum ) return true; if ( currentState.index !== index ) return true; return false; } function saveCache( object, geometry, program, index ) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let attribute = attributes[ name ]; if ( attribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) attribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if ( attribute && attribute.data ) { data.data = attribute.data; } cache[ name ] = data; attributesNum ++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { const extension = capabilities.isWebGL2 ? gl : extensions.get( "ANGLE_instanced_arrays" ); extension[ capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE" ]( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset, integer ) { if ( integer === true ) { gl.vertexAttribIPointer( index, size, type, stride, offset ); } else { gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); } } function setupVertexAttributes( object, material, program, geometry ) { if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( geometryAttribute !== undefined ) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; // check for integer attributes (WebGL 2 only) const integer = ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType ) ); if ( geometryAttribute.isInterleavedBufferAttribute ) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if ( data.isInstancedInterleavedBuffer ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement, integer ); } } else { if ( geometryAttribute.isInstancedBufferAttribute ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, ( size / programAttribute.locationSize ) * i * bytesPerElement, integer ); } } } else if ( materialDefaultAttributeValues !== undefined ) { const value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: gl.vertexAttrib4fv( programAttribute.location, value ); break; default: gl.vertexAttrib1fv( programAttribute.location, value ); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometryId ]; } } function releaseStatesOfGeometry( geometry ) { if ( bindingStates[ geometry.id ] === undefined ) return; const programMap = bindingStates[ geometry.id ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometry.id ]; } function releaseStatesOfProgram( program ) { for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; if ( programMap[ program.id ] === undefined ) continue; const stateMap = programMap[ program.id ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ program.id ]; } } function reset() { resetDefaultState(); forceUpdate = true; if ( currentState === defaultState ) return; currentState = defaultState; bindVertexArrayObject( currentState.object ); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawArraysInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, start, count, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities( gl, extensions, parameters ) { let maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl.constructor.name === "WebGL2RenderingContext"; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has( "WEBGL_draw_buffers" ); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); const maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); const maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); const maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); const maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has( "OES_texture_float" ); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping( properties ) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping ) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function ( planes, camera ) { globalState = projectPlanes( planes, camera, 0 ); }; this.setState = function ( material, camera, useCache ) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get( material ); if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, useCache ); for ( let i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps( renderer ) { let cubemaps = new WeakMap(); function mapTextureMapping( texture, mapping ) { if ( mapping === EquirectangularReflectionMapping ) { texture.mapping = CubeReflectionMapping; } else if ( mapping === EquirectangularRefractionMapping ) { texture.mapping = CubeRefractionMapping; } return texture; } function get( texture ) { if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if ( cubemaps.has( texture ) ) { const cubemap = cubemaps.get( texture ).texture; return mapTextureMapping( cubemap, texture.mapping ); } else { const image = texture.image; if ( image && image.height > 0 ) { const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemap = cubemaps.get( texture ); if ( cubemap !== undefined ) { cubemaps.delete( texture ); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; } setViewOffset( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = ( this.right - this.left ) / ( 2 * this.zoom ); const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); const cx = ( this.right + this.left ) / 2; const cy = ( this.top + this.bottom ) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; // Golden Ratio const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3( 1, 1, 1 ), /*@__PURE__*/ new Vector3( - 1, 1, 1 ), /*@__PURE__*/ new Vector3( 1, 1, - 1 ), /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor( renderer ) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial( this._blurMaterial ); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { _oldTarget = this._renderer.getRenderTarget(); this._setSize( 256 ); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); if ( sigma > 0 ) { this._blur( cubeUVRenderTarget, 0, 0, sigma ); } this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular( equirectangular, renderTarget = null ) { return this._fromTexture( equirectangular, renderTarget ); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap( cubemap, renderTarget = null ) { return this._fromTexture( cubemap, renderTarget ); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial( this._cubemapMaterial ); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial( this._equirectMaterial ); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } // private interface _setSize( cubeSize ) { this._lodMax = Math.floor( Math.log2( cubeSize ) ); this._cubeSize = Math.pow( 2, this._lodMax ); } _dispose() { if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); for ( let i = 0; i < this._lodPlanes.length; i ++ ) { this._lodPlanes[ i ].dispose(); } } _cleanup( outputTarget ) { this._renderer.setRenderTarget( _oldTarget ); outputTarget.scissorTest = false; _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); } _fromTexture( texture, renderTarget ) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); } else { // Equirectangular this._setSize( texture.image.width / 4 ); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV( texture, cubeUVRenderTarget ); this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max( this._cubeSize, 16 * 7 ); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget( width, height, params ); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { if ( this._pingPongRenderTarget !== null ) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget( width, height, params ); const { _lodMax } = this; ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); this._blurMaterial = _getBlurShader( _lodMax, width, height ); } return cubeUVRenderTarget; } _compileMaterial( material ) { const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); this._renderer.compile( tmpMesh, _flatCamera ); } _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); const upSign = [ 1, - 1, 1, 1, 1, 1 ]; const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor( _clearColor ); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial( { name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, } ); const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); let useSolidColor = false; const background = scene.background; if ( background ) { if ( background.isColor ) { backgroundMaterial.color.copy( background ); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy( _clearColor ); useSolidColor = true; } for ( let i = 0; i < 6; i ++ ) { const col = i % 3; if ( col === 0 ) { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); } else if ( col === 1 ) { cubeCamera.up.set( 0, 0, upSign[ i ] ); cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); } else { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); renderer.setRenderTarget( cubeUVRenderTarget ); if ( useSolidColor ) { renderer.render( backgroundBox, cubeCamera ); } renderer.render( scene, cubeCamera ); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV( texture, cubeUVRenderTarget ) { const renderer = this._renderer; const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); if ( isCubeTexture ) { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; } else { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh( this._lodPlanes[ 0 ], material ); const uniforms = material.uniforms; uniforms[ "envMap" ].value = texture; const size = this._cubeSize; _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); renderer.setRenderTarget( cubeUVRenderTarget ); renderer.render( mesh, _flatCamera ); } _applyPMREM( cubeUVRenderTarget ) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for ( let i = 1; i < this._lodPlanes.length; i ++ ) { const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if ( direction !== "latitudinal" && direction !== "longitudinal" ) { console.error( "blur direction must be either latitudinal or longitudinal!" ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[ lodIn ] - 1; const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; if ( samples > MAX_SAMPLES ) { console.warn( `sigmaRadians, ${ sigmaRadians}, is too large and will clip, as it requested ${ samples} samples when the maximum is set to ${MAX_SAMPLES}` ); } const weights = []; let sum = 0; for ( let i = 0; i < MAX_SAMPLES; ++ i ) { const x = i / sigmaPixels; const weight = Math.exp( - x * x / 2 ); weights.push( weight ); if ( i === 0 ) { sum += weight; } else if ( i < samples ) { sum += 2 * weight; } } for ( let i = 0; i < weights.length; i ++ ) { weights[ i ] = weights[ i ] / sum; } blurUniforms[ "envMap" ].value = targetIn.texture; blurUniforms[ "samples" ].value = samples; blurUniforms[ "weights" ].value = weights; blurUniforms[ "latitudinal" ].value = direction === "latitudinal"; if ( poleAxis ) { blurUniforms[ "poleAxis" ].value = poleAxis; } const { _lodMax } = this; blurUniforms[ "dTheta" ].value = radiansPerPixel; blurUniforms[ "mipInt" ].value = _lodMax - lodIn; const outputSize = this._sizeLods[ lodOut ]; const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); const y = 4 * ( this._cubeSize - outputSize ); _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); renderer.setRenderTarget( targetOut ); renderer.render( blurMesh, _flatCamera ); } } function _createPlanes( lodMax ) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for ( let i = 0; i < totalLods; i ++ ) { const sizeLod = Math.pow( 2, lod ); sizeLods.push( sizeLod ); let sigma = 1.0 / sizeLod; if ( i > lodMax - LOD_MIN ) { sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; } else if ( i === 0 ) { sigma = 0; } sigmas.push( sigma ); const texelSize = 1.0 / ( sizeLod - 2 ); const min = - texelSize; const max = 1 + texelSize; const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array( positionSize * vertices * cubeFaces ); const uv = new Float32Array( uvSize * vertices * cubeFaces ); const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); for ( let face = 0; face < cubeFaces; face ++ ) { const x = ( face % 3 ) * 2 / 3 - 1; const y = face > 2 ? 0 : - 1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0 ]; position.set( coordinates, positionSize * vertices * face ); uv.set( uv1, uvSize * vertices * face ); const fill = [ face, face, face, face, face, face ]; faceIndex.set( fill, faceIndexSize * vertices * face ); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute( position, positionSize ) ); planes.setAttribute( "uv", new BufferAttribute( uv, uvSize ) ); planes.setAttribute( "faceIndex", new BufferAttribute( faceIndex, faceIndexSize ) ); lodPlanes.push( planes ); if ( lod > LOD_MIN ) { lod --; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget( width, height, params ) { const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport( target, x, y, width, height ) { target.viewport.set( x, y, width, height ); target.scissor.set( x, y, width, height ); } function _getBlurShader( lodMax, width, height ) { const weights = new Float32Array( MAX_SAMPLES ); const poleAxis = new Vector3( 0, 1, 0 ); const shaderMaterial = new ShaderMaterial( { name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0`, }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false } ); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial( { name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCubemapMaterial() { return new ShaderMaterial( { name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: - 1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCommonVertexShader() { return /* glsl */` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps( renderer ) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get( texture ) { if ( texture && texture.isTexture ) { const mapping = texture.mapping; const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); // equirect/cube map to cubeUV conversion if ( isEquirectMap || isCubeMap ) { if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get( texture ); if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); cubeUVmaps.set( texture, renderTarget ); return renderTarget.texture; } else { if ( cubeUVmaps.has( texture ) ) { return cubeUVmaps.get( texture ).texture; } else { const image = texture.image; if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); cubeUVmaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete( image ) { let count = 0; const length = 6; for ( let i = 0; i < length; i ++ ) { if ( image[ i ] !== undefined ) count ++; } return count === length; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemapUV = cubeUVmaps.get( texture ); if ( cubemapUV !== undefined ) { cubeUVmaps.delete( texture ); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if ( pmremGenerator !== null ) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions( gl ) { const extensions = {}; function getExtension( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } let extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } extensions[ name ] = extension; return extension; } return { has: function ( name ) { return getExtension( name ) !== null; }, init: function ( capabilities ) { if ( capabilities.isWebGL2 ) { getExtension( "EXT_color_buffer_float" ); } else { getExtension( "WEBGL_depth_texture" ); getExtension( "OES_texture_float" ); getExtension( "OES_texture_half_float" ); getExtension( "OES_texture_half_float_linear" ); getExtension( "OES_standard_derivatives" ); getExtension( "OES_element_index_uint" ); getExtension( "OES_vertex_array_object" ); getExtension( "ANGLE_instanced_arrays" ); } getExtension( "OES_texture_float_linear" ); getExtension( "EXT_color_buffer_half_float" ); getExtension( "WEBGL_multisampled_render_to_texture" ); }, get: function ( name ) { const extension = getExtension( name ); if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } return extension; } }; } function WebGLGeometries( gl, attributes, info, bindingStates ) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose( event ) { const geometry = event.target; if ( geometry.index !== null ) { attributes.remove( geometry.index ); } for ( const name in geometry.attributes ) { attributes.remove( geometry.attributes[ name ] ); } for ( const name in geometry.morphAttributes ) { const array = geometry.morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.remove( array[ i ] ); } } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; const attribute = wireframeAttributes.get( geometry ); if ( attribute ) { attributes.remove( attribute ); wireframeAttributes.delete( geometry ); } bindingStates.releaseStatesOfGeometry( geometry ); if ( geometry.isInstancedBufferGeometry === true ) { delete geometry._maxInstanceCount; } // info.memory.geometries --; } function get( object, geometry ) { if ( geometries[ geometry.id ] === true ) return geometry; geometry.addEventListener( "dispose", onGeometryDispose ); geometries[ geometry.id ] = true; info.memory.geometries ++; return geometry; } function update( geometry ) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for ( const name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets const morphAttributes = geometry.morphAttributes; for ( const name in morphAttributes ) { const array = morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function updateWireframeAttribute( geometry ) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if ( geometryIndex !== null ) { const array = geometryIndex.array; version = geometryIndex.version; for ( let i = 0, l = array.length; i < l; i += 3 ) { const a = array[ i + 0 ]; const b = array[ i + 1 ]; const c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else if ( geometryPosition !== undefined ) { const array = geometryPosition.array; version = geometryPosition.version; for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push( a, b, b, c, c, a ); } } else { return; } const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get( geometry ); if ( previousAttribute ) attributes.remove( previousAttribute ); // wireframeAttributes.set( geometry, attribute ); } function getWireframeAttribute( geometry ) { const currentAttribute = wireframeAttributes.get( geometry ); if ( currentAttribute ) { const geometryIndex = geometry.index; if ( geometryIndex !== null ) { // if the attribute is obsolete, create a new one if ( currentAttribute.version < geometryIndex.version ) { updateWireframeAttribute( geometry ); } } } else { updateWireframeAttribute( geometry ); } return wireframeAttributes.get( geometry ); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } let type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawElementsInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo( gl ) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { render.calls ++; switch ( mode ) { case gl.TRIANGLES: render.triangles += instanceCount * ( count / 3 ); break; case gl.LINES: render.lines += instanceCount * ( count / 2 ); break; case gl.LINE_STRIP: render.lines += instanceCount * ( count - 1 ); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } function numericalSort( a, b ) { return a[ 0 ] - b[ 0 ]; } function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl, capabilities, textures ) { const influencesList = {}; const morphInfluences = new Float32Array( 8 ); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for ( let i = 0; i < 8; i ++ ) { workInfluences[ i ] = [ i, 0 ]; } function update( object, geometry, program ) { const objectInfluences = object.morphTargetInfluences; if ( capabilities.isWebGL2 === true ) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let entry = morphTextures.get( geometry ); if ( entry === undefined || entry.count !== morphTargetsCount ) { if ( entry !== undefined ) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if ( hasMorphPosition === true ) vertexDataCount = 1; if ( hasMorphNormals === true ) vertexDataCount = 2; if ( hasMorphColors === true ) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if ( width > capabilities.maxTextureSize ) { height = Math.ceil( width / capabilities.maxTextureSize ); width = capabilities.maxTextureSize; } const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for ( let i = 0; i < morphTargetsCount; i ++ ) { const morphTarget = morphTargets[ i ]; const morphNormal = morphNormals[ i ]; const morphColor = morphColors[ i ]; const offset = width * height * 4 * i; for ( let j = 0; j < morphTarget.count; j ++ ) { const stride = j * vertexDataStride; if ( hasMorphPosition === true ) { morph.fromBufferAttribute( morphTarget, j ); buffer[ offset + stride + 0 ] = morph.x; buffer[ offset + stride + 1 ] = morph.y; buffer[ offset + stride + 2 ] = morph.z; buffer[ offset + stride + 3 ] = 0; } if ( hasMorphNormals === true ) { morph.fromBufferAttribute( morphNormal, j ); buffer[ offset + stride + 4 ] = morph.x; buffer[ offset + stride + 5 ] = morph.y; buffer[ offset + stride + 6 ] = morph.z; buffer[ offset + stride + 7 ] = 0; } if ( hasMorphColors === true ) { morph.fromBufferAttribute( morphColor, j ); buffer[ offset + stride + 8 ] = morph.x; buffer[ offset + stride + 9 ] = morph.y; buffer[ offset + stride + 10 ] = morph.z; buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2( width, height ) }; morphTextures.set( geometry, entry ); function disposeTexture() { texture.dispose(); morphTextures.delete( geometry ); geometry.removeEventListener( "dispose", disposeTexture ); } geometry.addEventListener( "dispose", disposeTexture ); } // let morphInfluencesSum = 0; for ( let i = 0; i < objectInfluences.length; i ++ ) { morphInfluencesSum += objectInfluences[ i ]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", objectInfluences ); program.getUniforms().setValue( gl, "morphTargetsTexture", entry.texture, textures ); program.getUniforms().setValue( gl, "morphTargetsTextureSize", entry.size ); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[ geometry.id ]; if ( influences === undefined || influences.length !== length ) { // initialise list influences = []; for ( let i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } // Collect influences for ( let i = 0; i < length; i ++ ) { const influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); for ( let i = 0; i < 8; i ++ ) { if ( i < length && influences[ i ][ 1 ] ) { workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; } else { workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; workInfluences[ i ][ 1 ] = 0; } } workInfluences.sort( numericalSort ); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for ( let i = 0; i < 8; i ++ ) { const influence = workInfluences[ i ]; const index = influence[ 0 ]; const value = influence[ 1 ]; if ( index !== Number.MAX_SAFE_INTEGER && value ) { if ( morphTargets && geometry.getAttribute( "morphTarget" + i ) !== morphTargets[ index ] ) { geometry.setAttribute( "morphTarget" + i, morphTargets[ index ] ); } if ( morphNormals && geometry.getAttribute( "morphNormal" + i ) !== morphNormals[ index ] ) { geometry.setAttribute( "morphNormal" + i, morphNormals[ index ] ); } morphInfluences[ i ] = value; morphInfluencesSum += value; } else { if ( morphTargets && geometry.hasAttribute( "morphTarget" + i ) === true ) { geometry.deleteAttribute( "morphTarget" + i ); } if ( morphNormals && geometry.hasAttribute( "morphNormal" + i ) === true ) { geometry.deleteAttribute( "morphNormal" + i ); } morphInfluences[ i ] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } } return { update: update }; } function WebGLObjects( gl, geometries, attributes, info ) { let updateMap = new WeakMap(); function update( object ) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateMap.get( buffergeometry ) !== frame ) { geometries.update( buffergeometry ); updateMap.set( buffergeometry, frame ); } if ( object.isInstancedMesh ) { if ( object.hasEventListener( "dispose", onInstancedMeshDispose ) === false ) { object.addEventListener( "dispose", onInstancedMeshDispose ); } if ( updateMap.get( object ) !== frame ) { attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); if ( object.instanceColor !== null ) { attributes.update( object.instanceColor, gl.ARRAY_BUFFER ); } updateMap.set( object, frame ); } } if ( object.isSkinnedMesh ) { const skeleton = object.skeleton; if ( updateMap.get( skeleton ) !== frame ) { skeleton.update(); updateMap.set( skeleton, frame ); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose( event ) { const instancedMesh = event.target; instancedMesh.removeEventListener( "dispose", onInstancedMeshDispose ); attributes.remove( instancedMesh.instanceMatrix ); if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); } return { update: update, dispose: dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array( 16 ); const mat3array = new Float32Array( 9 ); const mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { const firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( let i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( let i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( textures, n ) { let r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( let i = 0; i !== n; ++ i ) { r[ i ] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValueV3f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValueV4f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValueM3( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValueM4( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single integer / boolean function setValueV1i( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2i( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } } function setValueV3i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3i( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } } function setValueV4i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4i( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } } // Single unsigned integer function setValueV1ui( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1ui( this.addr, v ); cache[ 0 ] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2ui( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV3ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3ui( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV4ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4ui( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4uiv( this.addr, v ); copyArray( cache, v ); } } // Single texture (2D / Cube) function setValueT1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2D( v || emptyTexture, unit ); } function setValueT3D1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture3D( v || empty3dTexture, unit ); } function setValueT6( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTextureCube( v || emptyCubeTexture, unit ); } function setValueT2DArray1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2DArray( v || emptyArrayTexture, unit ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray( gl, v ) { gl.uniform1fv( this.addr, v ); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray( gl, v ) { const data = flatten( v, this.size, 2 ); gl.uniform2fv( this.addr, data ); } function setValueV3fArray( gl, v ) { const data = flatten( v, this.size, 3 ); gl.uniform3fv( this.addr, data ); } function setValueV4fArray( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniform4fv( this.addr, data ); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniformMatrix2fv( this.addr, false, data ); } function setValueM3Array( gl, v ) { const data = flatten( v, this.size, 9 ); gl.uniformMatrix3fv( this.addr, false, data ); } function setValueM4Array( gl, v ) { const data = flatten( v, this.size, 16 ); gl.uniformMatrix4fv( this.addr, false, data ); } // Array of integer / boolean function setValueV1iArray( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValueV3iArray( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValueV4iArray( gl, v ) { gl.uniform4iv( this.addr, v ); } // Array of unsigned integer function setValueV1uiArray( gl, v ) { gl.uniform1uiv( this.addr, v ); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray( gl, v ) { gl.uniform2uiv( this.addr, v ); } function setValueV3uiArray( gl, v ) { gl.uniform3uiv( this.addr, v ); } function setValueV4uiArray( gl, v ) { gl.uniform4uiv( this.addr, v ); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT3DArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); } } function setValueT6Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } function setValueT2DArrayArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor( id ) { this.id = id; this.seq = []; this.map = {}; } setValue( gl, value, textures ) { const seq = this.seq; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; u.setValue( gl, value[ u.id ], textures ); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { const match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex; let id = match[ 1 ]; const idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container class WebGLUniforms { constructor( gl, program ) { this.seq = []; this.map = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( let i = 0; i < n; ++ i ) { const info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } setValue( gl, name, value, textures ) { const u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, textures ); } setOptional( gl, object, name ) { const v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); } static upload( gl, seq, values, textures ) { for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, textures ); } } } static seqWithValue( seq, values ) { const r = []; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; } } function WebGLShader( gl, type, string ) { const shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); return shader; } let programIdCount = 0; function handleSource( string, errorLine ) { const lines = string.split( " " ); const lines2 = []; const from = Math.max( errorLine - 6, 0 ); const to = Math.min( errorLine + 6, lines.length ); for ( let i = from; i < to; i ++ ) { const line = i + 1; lines2.push( `${line === errorLine ? ">" : " "} ${line}: ${lines[ i ]}` ); } return lines2.join( " " ); } function getEncodingComponents( colorSpace ) { switch ( colorSpace ) { case LinearSRGBColorSpace: return [ "Linear", "( value )" ]; case SRGBColorSpace: return [ "sRGB", "( value )" ]; default: console.warn( "THREE.WebGLProgram: Unsupported color space:", colorSpace ); return [ "Linear", "( value )" ]; } } function getShaderErrors( gl, shader, type ) { const status = gl.getShaderParameter( shader, gl.COMPILE_STATUS ); const errors = gl.getShaderInfoLog( shader ).trim(); if ( status && errors === "" ) return ""; const errorMatches = /ERROR: 0:(d+)/.exec( errors ); if ( errorMatches ) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt( errorMatches[ 1 ] ); return type.toUpperCase() + " " + errors + " " + handleSource( gl.getShaderSource( shader ), errorLine ); } else { return errors; } } function getTexelEncodingFunction( functionName, colorSpace ) { const components = getEncodingComponents( colorSpace ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { let toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn( "THREE.WebGLProgram: Unsupported toneMapping:", toneMapping ); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( parameters ) { const chunks = [ ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? "#extension GL_EXT_draw_buffers : require" : "", ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { const chunks = []; for ( const name in defines ) { const value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { const attributes = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( let i = 0; i < n; i ++ ) { const info = gl.getActiveAttrib( program, i ); const name = info.name; let locationSize = 1; if ( info.type === gl.FLOAT_MAT2 ) locationSize = 2; if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3; if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = { type: info.type, location: gl.getAttribLocation( program, name ), locationSize: locationSize }; } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps ) .replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps ) .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes( string ) { return string.replace( includePattern, includeReplacer ); } const shaderChunkMap = new Map( [ [ "encodings_fragment", "colorspace_fragment" ], // @deprecated, r154 [ "encodings_pars_fragment", "colorspace_pars_fragment" ], // @deprecated, r154 [ "output_fragment", "opaque_fragment" ], // @deprecated, r154 ] ); function includeReplacer( match, include ) { let string = ShaderChunk[ include ]; if ( string === undefined ) { const newInclude = shaderChunkMap.get( include ); if ( newInclude !== undefined ) { string = ShaderChunk[ newInclude ]; console.warn( "THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.", include, newInclude ); } else { throw new Error( "Can not resolve #include <" + include + ">" ); } } return resolveIncludes( string ); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision( parameters ), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.displacementMap ? "#define USE_DISPLACEMENTMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", // parameters.mapUv ? "#define MAP_UV " + parameters.mapUv : "", parameters.alphaMapUv ? "#define ALPHAMAP_UV " + parameters.alphaMapUv : "", parameters.lightMapUv ? "#define LIGHTMAP_UV " + parameters.lightMapUv : "", parameters.aoMapUv ? "#define AOMAP_UV " + parameters.aoMapUv : "", parameters.emissiveMapUv ? "#define EMISSIVEMAP_UV " + parameters.emissiveMapUv : "", parameters.bumpMapUv ? "#define BUMPMAP_UV " + parameters.bumpMapUv : "", parameters.normalMapUv ? "#define NORMALMAP_UV " + parameters.normalMapUv : "", parameters.displacementMapUv ? "#define DISPLACEMENTMAP_UV " + parameters.displacementMapUv : "", parameters.metalnessMapUv ? "#define METALNESSMAP_UV " + parameters.metalnessMapUv : "", parameters.roughnessMapUv ? "#define ROUGHNESSMAP_UV " + parameters.roughnessMapUv : "", parameters.anisotropyMapUv ? "#define ANISOTROPYMAP_UV " + parameters.anisotropyMapUv : "", parameters.clearcoatMapUv ? "#define CLEARCOATMAP_UV " + parameters.clearcoatMapUv : "", parameters.clearcoatNormalMapUv ? "#define CLEARCOAT_NORMALMAP_UV " + parameters.clearcoatNormalMapUv : "", parameters.clearcoatRoughnessMapUv ? "#define CLEARCOAT_ROUGHNESSMAP_UV " + parameters.clearcoatRoughnessMapUv : "", parameters.iridescenceMapUv ? "#define IRIDESCENCEMAP_UV " + parameters.iridescenceMapUv : "", parameters.iridescenceThicknessMapUv ? "#define IRIDESCENCE_THICKNESSMAP_UV " + parameters.iridescenceThicknessMapUv : "", parameters.sheenColorMapUv ? "#define SHEEN_COLORMAP_UV " + parameters.sheenColorMapUv : "", parameters.sheenRoughnessMapUv ? "#define SHEEN_ROUGHNESSMAP_UV " + parameters.sheenRoughnessMapUv : "", parameters.specularMapUv ? "#define SPECULARMAP_UV " + parameters.specularMapUv : "", parameters.specularColorMapUv ? "#define SPECULAR_COLORMAP_UV " + parameters.specularColorMapUv : "", parameters.specularIntensityMapUv ? "#define SPECULAR_INTENSITYMAP_UV " + parameters.specularIntensityMapUv : "", parameters.transmissionMapUv ? "#define TRANSMISSIONMAP_UV " + parameters.transmissionMapUv : "", parameters.thicknessMapUv ? "#define THICKNESSMAP_UV " + parameters.thicknessMapUv : "", // parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", ( parameters.morphColors && parameters.isWebGL2 ) ? "#define USE_MORPHCOLORS" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.useLegacyLights ? "#define LEGACY_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_UV1", " attribute vec2 uv1;", "#endif", "#ifdef USE_UV2", " attribute vec2 uv2;", "#endif", "#ifdef USE_UV3", " attribute vec2 uv3;", "#endif", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, generatePrecision( parameters ), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.useLegacyLights ? "#define LEGACY_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk[ "colorspace_pars_fragment" ], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputColorSpace ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = resolveIncludes( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = resolveIncludes( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ "precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture" ].join( " " ) + " " + prefixVertex; prefixFragment = [ "#define varying in", ( parameters.glslVersion === GLSL3 ) ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", ( parameters.glslVersion === GLSL3 ) ? "" : "#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( " " ) + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( parameters.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); // check for link errors if ( renderer.debug.checkShaderErrors ) { const programLog = gl.getProgramInfoLog( program ).trim(); const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); let runnable = true; let haveDiagnostics = true; if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; if ( typeof renderer.debug.onShaderError === "function" ) { renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader ); } else { // default error reporting const vertexErrors = getShaderErrors( gl, glVertexShader, "vertex" ); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment" ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors ); } } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: Program Info Log:", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program ); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram( this ); gl.deleteProgram( program ); this.program = undefined; }; // this.type = parameters.shaderType; this.name = parameters.shaderName; this.id = programIdCount ++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update( material ) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage( vertexShader ); const fragmentShaderStage = this._getShaderStage( fragmentShader ); const materialShaders = this._getShaderCacheForMaterial( material ); if ( materialShaders.has( vertexShaderStage ) === false ) { materialShaders.add( vertexShaderStage ); vertexShaderStage.usedTimes ++; } if ( materialShaders.has( fragmentShaderStage ) === false ) { materialShaders.add( fragmentShaderStage ); fragmentShaderStage.usedTimes ++; } return this; } remove( material ) { const materialShaders = this.materialCache.get( material ); for ( const shaderStage of materialShaders ) { shaderStage.usedTimes --; if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); } this.materialCache.delete( material ); return this; } getVertexShaderID( material ) { return this._getShaderStage( material.vertexShader ).id; } getFragmentShaderID( material ) { return this._getShaderStage( material.fragmentShader ).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial( material ) { const cache = this.materialCache; let set = cache.get( material ); if ( set === undefined ) { set = new Set(); cache.set( material, set ); } return set; } _getShaderStage( code ) { const cache = this.shaderCache; let stage = cache.get( code ); if ( stage === undefined ) { stage = new WebGLShaderStage( code ); cache.set( code, stage ); } return stage; } } class WebGLShaderStage { constructor( code ) { this.id = _id ++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const IS_WEBGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getChannel( value ) { if ( value === 0 ) return "uv"; return `uv${ value }`; } function getParameters( material, lights, shadows, scene, object ) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; const shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let morphTextureStride = 0; if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update( material ); customVertexShaderID = _customShaders.getVertexShaderID( material ); customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const HAS_MAP = !! material.map; const HAS_MATCAP = !! material.matcap; const HAS_ENVMAP = !! envMap; const HAS_AOMAP = !! material.aoMap; const HAS_LIGHTMAP = !! material.lightMap; const HAS_BUMPMAP = !! material.bumpMap; const HAS_NORMALMAP = !! material.normalMap; const HAS_DISPLACEMENTMAP = !! material.displacementMap; const HAS_EMISSIVEMAP = !! material.emissiveMap; const HAS_METALNESSMAP = !! material.metalnessMap; const HAS_ROUGHNESSMAP = !! material.roughnessMap; const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap; const HAS_SPECULARMAP = !! material.specularMap; const HAS_SPECULAR_COLORMAP = !! material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap; const HAS_GRADIENTMAP = !! material.gradientMap; const HAS_ALPHAMAP = !! material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_ALPHAHASH = !! material.alphaHash; const HAS_EXTENSIONS = !! material.extensions; const HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1; const HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2; const HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3; let toneMapping = NoToneMapping; if ( material.toneMapped ) { if ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) { toneMapping = renderer.toneMapping; } } const parameters = { isWebGL2: IS_WEBGL2, shaderID: shaderID, shaderType: material.type, shaderName: material.name, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, anisotropy: HAS_ANISOTROPY, anisotropyMap: HAS_ANISOTROPYMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, alphaHash: HAS_ALPHAHASH, combine: material.combine, // mapUv: HAS_MAP && getChannel( material.map.channel ), aoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ), lightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ), bumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ), normalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ), emissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ), metalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ), anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ), specularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ), thicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ), alphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ), // vertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUv1s: HAS_ATTRIBUTE_UV1, vertexUv2s: HAS_ATTRIBUTE_UV2, vertexUv3s: HAS_ATTRIBUTE_UV3, pointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ), fog: !! fog, useFog: material.fog === true, fogExp2: ( fog && fog.isFogExp2 ), flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: toneMapping, useLegacyLights: renderer._useLegacyLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true, extensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true, extensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true, extensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true, rendererExtensionFragDepth: IS_WEBGL2 || extensions.has( "EXT_frag_depth" ), rendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( "WEBGL_draw_buffers" ), rendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( "EXT_shader_texture_lod" ), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey( parameters ) { const array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( parameters.customVertexShaderID ); array.push( parameters.customFragmentShaderID ); } if ( parameters.defines !== undefined ) { for ( const name in parameters.defines ) { array.push( name ); array.push( parameters.defines[ name ] ); } } if ( parameters.isRawShaderMaterial === false ) { getProgramCacheKeyParameters( array, parameters ); getProgramCacheKeyBooleans( array, parameters ); array.push( renderer.outputColorSpace ); } array.push( parameters.customProgramCacheKey ); return array.join(); } function getProgramCacheKeyParameters( array, parameters ) { array.push( parameters.precision ); array.push( parameters.outputColorSpace ); array.push( parameters.envMapMode ); array.push( parameters.envMapCubeUVHeight ); array.push( parameters.mapUv ); array.push( parameters.alphaMapUv ); array.push( parameters.lightMapUv ); array.push( parameters.aoMapUv ); array.push( parameters.bumpMapUv ); array.push( parameters.normalMapUv ); array.push( parameters.displacementMapUv ); array.push( parameters.emissiveMapUv ); array.push( parameters.metalnessMapUv ); array.push( parameters.roughnessMapUv ); array.push( parameters.anisotropyMapUv ); array.push( parameters.clearcoatMapUv ); array.push( parameters.clearcoatNormalMapUv ); array.push( parameters.clearcoatRoughnessMapUv ); array.push( parameters.iridescenceMapUv ); array.push( parameters.iridescenceThicknessMapUv ); array.push( parameters.sheenColorMapUv ); array.push( parameters.sheenRoughnessMapUv ); array.push( parameters.specularMapUv ); array.push( parameters.specularColorMapUv ); array.push( parameters.specularIntensityMapUv ); array.push( parameters.transmissionMapUv ); array.push( parameters.thicknessMapUv ); array.push( parameters.combine ); array.push( parameters.fogExp2 ); array.push( parameters.sizeAttenuation ); array.push( parameters.morphTargetsCount ); array.push( parameters.morphAttributeCount ); array.push( parameters.numDirLights ); array.push( parameters.numPointLights ); array.push( parameters.numSpotLights ); array.push( parameters.numSpotLightMaps ); array.push( parameters.numHemiLights ); array.push( parameters.numRectAreaLights ); array.push( parameters.numDirLightShadows ); array.push( parameters.numPointLightShadows ); array.push( parameters.numSpotLightShadows ); array.push( parameters.numSpotLightShadowsWithMaps ); array.push( parameters.shadowMapType ); array.push( parameters.toneMapping ); array.push( parameters.numClippingPlanes ); array.push( parameters.numClipIntersection ); array.push( parameters.depthPacking ); } function getProgramCacheKeyBooleans( array, parameters ) { _programLayers.disableAll(); if ( parameters.isWebGL2 ) _programLayers.enable( 0 ); if ( parameters.supportsVertexTextures ) _programLayers.enable( 1 ); if ( parameters.instancing ) _programLayers.enable( 2 ); if ( parameters.instancingColor ) _programLayers.enable( 3 ); if ( parameters.matcap ) _programLayers.enable( 4 ); if ( parameters.envMap ) _programLayers.enable( 5 ); if ( parameters.normalMapObjectSpace ) _programLayers.enable( 6 ); if ( parameters.normalMapTangentSpace ) _programLayers.enable( 7 ); if ( parameters.clearcoat ) _programLayers.enable( 8 ); if ( parameters.iridescence ) _programLayers.enable( 9 ); if ( parameters.alphaTest ) _programLayers.enable( 10 ); if ( parameters.vertexColors ) _programLayers.enable( 11 ); if ( parameters.vertexAlphas ) _programLayers.enable( 12 ); if ( parameters.vertexUv1s ) _programLayers.enable( 13 ); if ( parameters.vertexUv2s ) _programLayers.enable( 14 ); if ( parameters.vertexUv3s ) _programLayers.enable( 15 ); if ( parameters.vertexTangents ) _programLayers.enable( 16 ); if ( parameters.anisotropy ) _programLayers.enable( 17 ); array.push( _programLayers.mask ); _programLayers.disableAll(); if ( parameters.fog ) _programLayers.enable( 0 ); if ( parameters.useFog ) _programLayers.enable( 1 ); if ( parameters.flatShading ) _programLayers.enable( 2 ); if ( parameters.logarithmicDepthBuffer ) _programLayers.enable( 3 ); if ( parameters.skinning ) _programLayers.enable( 4 ); if ( parameters.morphTargets ) _programLayers.enable( 5 ); if ( parameters.morphNormals ) _programLayers.enable( 6 ); if ( parameters.morphColors ) _programLayers.enable( 7 ); if ( parameters.premultipliedAlpha ) _programLayers.enable( 8 ); if ( parameters.shadowMapEnabled ) _programLayers.enable( 9 ); if ( parameters.useLegacyLights ) _programLayers.enable( 10 ); if ( parameters.doubleSided ) _programLayers.enable( 11 ); if ( parameters.flipSided ) _programLayers.enable( 12 ); if ( parameters.useDepthPacking ) _programLayers.enable( 13 ); if ( parameters.dithering ) _programLayers.enable( 14 ); if ( parameters.transmission ) _programLayers.enable( 15 ); if ( parameters.sheen ) _programLayers.enable( 16 ); if ( parameters.opaque ) _programLayers.enable( 17 ); if ( parameters.pointsUvs ) _programLayers.enable( 18 ); array.push( _programLayers.mask ); } function getUniforms( material ) { const shaderID = shaderIDs[ material.type ]; let uniforms; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; uniforms = UniformsUtils.clone( shader.uniforms ); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram( parameters, cacheKey ) { let program; // Check if code has been already compiled for ( let p = 0, pl = programs.length; p < pl; p ++ ) { const preexistingProgram = programs[ p ]; if ( preexistingProgram.cacheKey === cacheKey ) { program = preexistingProgram; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); programs.push( program ); } return program; } function releaseProgram( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set const i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache( material ) { _customShaders.remove( material ); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get( object ) { let map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { let renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex ++; return renderItem; } function push( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.push( renderItem ); } else if ( material.transparent === true ) { transparent.push( renderItem ); } else { opaque.push( renderItem ); } } function unshift( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.unshift( renderItem ); } else if ( material.transparent === true ) { transparent.unshift( renderItem ); } else { opaque.unshift( renderItem ); } } function sort( customOpaqueSort, customTransparentSort ) { if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); } function finish() { // Clear references from inactive renderItems in the list for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { const renderItem = renderItems[ i ]; if ( renderItem.id === null ) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get( scene, renderCallDepth ) { const listArray = lists.get( scene ); let list; if ( listArray === undefined ) { list = new WebGLRenderList(); lists.set( scene, [ list ] ); } else { if ( renderCallDepth >= listArray.length ) { list = new WebGLRenderList(); listArray.push( list ); } else { list = listArray[ renderCallDepth ]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[ light.id ] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst( lightA, lightB ) { return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 ); } function WebGLLights( extensions, capabilities ) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: - 1, pointLength: - 1, spotLength: - 1, rectAreaLength: - 1, hemiLength: - 1, numDirectionalShadows: - 1, numPointShadows: - 1, numSpotShadows: - 1, numSpotMaps: - 1 }, ambient: [ 0, 0, 0 ], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0 }; for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup( lights, useLegacyLights ) { let r = 0, g = 0, b = 0; for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort( shadowCastingAndTexturingLightsFirst ); // artist-friendly light intensity scaling factor const scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if ( light.isLightProbe ) { for ( let j = 0; j < 9; j ++ ) { state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); } } else if ( light.isDirectionalLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[ directionalLength ] = shadowUniforms; state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; numDirectionalShadows ++; } state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); uniforms.distance = distance; uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = light.decay; state.spot[ spotLength ] = uniforms; const shadow = light.shadow; if ( light.map ) { state.spotLightMap[ numSpotMaps ] = light.map; numSpotMaps ++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices( light ); if ( light.castShadow ) numSpotShadowsWithMaps ++; } state.spotLightMatrix[ spotLength ] = shadow.matrix; if ( light.castShadow ) { const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[ spotLength ] = shadowUniforms; state.spotShadowMap[ spotLength ] = shadowMap; numSpotShadows ++; } spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = cache.get( light ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); uniforms.distance = light.distance; uniforms.decay = light.decay; if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[ pointLength ] = shadowUniforms; state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; numPointShadows ++; } state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = cache.get( light ); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } if ( rectAreaLength > 0 ) { if ( capabilities.isWebGL2 ) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if ( extensions.has( "OES_texture_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if ( extensions.has( "OES_texture_half_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error( "THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions." ); } } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; state.version = nextVersion ++; } } function setupView( lights, camera ) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; if ( light.isDirectionalLight ) { const uniforms = state.directional[ directionalLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = state.spot[ spotLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = state.rectArea[ rectAreaLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = state.point[ pointLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = state.hemi[ hemiLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); hemiLength ++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState( extensions, capabilities ) { const lights = new WebGLLights( extensions, capabilities ); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function setupLights( useLegacyLights ) { lights.setup( lightsArray, useLegacyLights ); } function setupLightsView( camera ) { lights.setupView( lightsArray, camera ); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates( extensions, capabilities ) { let renderStates = new WeakMap(); function get( scene, renderCallDepth = 0 ) { const renderStateArray = renderStates.get( scene ); let renderState; if ( renderStateArray === undefined ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStates.set( scene, [ renderState ] ); } else { if ( renderCallDepth >= renderStateArray.length ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStateArray.push( renderState ); } else { renderState = renderStateArray[ renderCallDepth ]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } class MeshDepthMaterial extends Material { constructor( parameters ) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor( parameters ) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap( _renderer, _objects, _capabilities ) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial( { defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment } ); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), 3 ) ); const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending( NoBlending ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // check for shadow map type changes const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap ); const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap ); // render depth map for ( let i = 0, il = lights.length; i < il; i ++ ) { const light = lights[ i ]; const shadow = light.shadow; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; _shadowMapSize.copy( shadow.mapSize ); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply( shadowFrameExtents ); _viewportSize.copy( shadow.mapSize ); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if ( _shadowMapSize.x > _maxTextureSize ) { _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if ( _shadowMapSize.y > _maxTextureSize ) { _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if ( shadow.map === null || toVSM === true || fromVSM === true ) { const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; if ( shadow.map !== null ) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget( shadow.map ); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for ( let vp = 0; vp < viewportCount; vp ++ ) { const viewport = shadow.getViewport( vp ); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w ); _state.viewport( _viewport ); shadow.updateMatrices( light, vp ); _frustum = shadow.getFrustum(); renderObject( scene, camera, shadow.camera, light, this.type ); } // do blur pass for VSM if ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) { VSMPass( shadow, camera ); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); }; function VSMPass( shadow, camera ) { const geometry = _objects.update( fullScreenMesh ); if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if ( shadow.mapPass === null ) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.mapPass ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.map ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); } function getDepthMaterial( object, material, light, type ) { let result = null; const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; if ( customMaterial !== undefined ) { result = customMaterial; } else { result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; if ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) || ( material.displacementMap && material.displacementScale !== 0 ) || ( material.alphaMap && material.alphaTest > 0 ) || ( material.map && material.alphaTest > 0 ) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } let cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if ( type === VSMShadowMap ) { result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; } else { result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { const materialProperties = _renderer.properties.get( result ); materialProperties.light = light; } return result; } function renderObject( object, camera, shadowCamera, light, type ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); const geometry = _objects.update( object ); const material = object.material; if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let k = 0, kl = groups.length; k < kl; k ++ ) { const group = groups[ k ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { const depthMaterial = getDepthMaterial( object, material, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, light, type ); } } } function WebGLState( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( ! locked ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter( gl.VERSION ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); const viewportParam = gl.getParameter( gl.VIEWPORT ); const currentScissor = new Vector4().fromArray( scissorParam ); const currentViewport = new Vector4().fromArray( viewportParam ); function createTexture( type, target, count, dimensions ) { const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( let i = 0; i < count; i ++ ) { if ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) { gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } else { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } } return texture; } const emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); if ( isWebGL2 ) { emptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 ); emptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 ); } // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); setBlending( NoBlending ); // function enable( id ) { if ( enabledCapabilities[ id ] !== true ) { gl.enable( id ); enabledCapabilities[ id ] = true; } } function disable( id ) { if ( enabledCapabilities[ id ] !== false ) { gl.disable( id ); enabledCapabilities[ id ] = false; } } function bindFramebuffer( target, framebuffer ) { if ( currentBoundFramebuffers[ target ] !== framebuffer ) { gl.bindFramebuffer( target, framebuffer ); currentBoundFramebuffers[ target ] = framebuffer; if ( isWebGL2 ) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if ( target === gl.DRAW_FRAMEBUFFER ) { currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer; } if ( target === gl.FRAMEBUFFER ) { currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer; } } return true; } return false; } function drawBuffers( renderTarget, framebuffer ) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if ( renderTarget ) { drawBuffers = currentDrawbuffers.get( framebuffer ); if ( drawBuffers === undefined ) { drawBuffers = []; currentDrawbuffers.set( framebuffer, drawBuffers ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { const textures = renderTarget.texture; if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { for ( let i = 0, il = textures.length; i < il; i ++ ) { drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { drawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if ( drawBuffers[ 0 ] !== gl.BACK ) { drawBuffers[ 0 ] = gl.BACK; needsUpdate = true; } } if ( needsUpdate ) { if ( capabilities.isWebGL2 ) { gl.drawBuffers( drawBuffers ); } else { extensions.get( "WEBGL_draw_buffers" ).drawBuffersWEBGL( drawBuffers ); } } } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } const equationToGL = { [ AddEquation ]: gl.FUNC_ADD, [ SubtractEquation ]: gl.FUNC_SUBTRACT, [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT }; if ( isWebGL2 ) { equationToGL[ MinEquation ] = gl.MIN; equationToGL[ MaxEquation ] = gl.MAX; } else { const extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { equationToGL[ MinEquation ] = extension.MIN_EXT; equationToGL[ MaxEquation ] = extension.MAX_EXT; } } const factorToGL = { [ ZeroFactor ]: gl.ZERO, [ OneFactor ]: gl.ONE, [ SrcColorFactor ]: gl.SRC_COLOR, [ SrcAlphaFactor ]: gl.SRC_ALPHA, [ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE, [ DstColorFactor ]: gl.DST_COLOR, [ DstAlphaFactor ]: gl.DST_ALPHA, [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending === NoBlending ) { if ( currentBlendingEnabled === true ) { disable( gl.BLEND ); currentBlendingEnabled = false; } return; } if ( currentBlendingEnabled === false ) { enable( gl.BLEND ); currentBlendingEnabled = true; } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation( gl.FUNC_ADD ); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if ( premultipliedAlpha ) { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.ONE, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } else { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); let flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); ( material.blending === NormalBlending && material.transparent === false ) ? setBlending( NoBlending ) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); const stencilWrite = material.stencilWrite; stencilBuffer.setTest( stencilWrite ); if ( stencilWrite ) { stencilBuffer.setMask( material.stencilWriteMask ); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); material.alphaToCoverage === true ? enable( gl.SAMPLE_ALPHA_TO_COVERAGE ) : disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture, webglSlot ) { if ( webglSlot === undefined ) { if ( currentTextureSlot === null ) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[ webglSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ webglSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture !== undefined && boundTexture.type !== undefined ) { gl.bindTexture( boundTexture.type, null ); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage2D() { try { gl.texSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage3D() { try { gl.texSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage2D() { try { gl.texStorage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage3D() { try { gl.texStorage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage3D() { try { gl.texImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } function updateUBOMapping( uniformsGroup, program ) { let mapping = uboProgramMap.get( program ); if ( mapping === undefined ) { mapping = new WeakMap(); uboProgramMap.set( program, mapping ); } let blockIndex = mapping.get( uniformsGroup ); if ( blockIndex === undefined ) { blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name ); mapping.set( uniformsGroup, blockIndex ); } } function uniformBlockBinding( uniformsGroup, program ) { const mapping = uboProgramMap.get( program ); const blockIndex = mapping.get( uniformsGroup ); if ( uboBindings.get( program ) !== blockIndex ) { // bind shader specific block index to global block point gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex ); uboBindings.set( program, blockIndex ); } } // function reset() { // reset state gl.disable( gl.BLEND ); gl.disable( gl.CULL_FACE ); gl.disable( gl.DEPTH_TEST ); gl.disable( gl.POLYGON_OFFSET_FILL ); gl.disable( gl.SCISSOR_TEST ); gl.disable( gl.STENCIL_TEST ); gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ONE, gl.ZERO ); gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); gl.colorMask( true, true, true, true ); gl.clearColor( 0, 0, 0, 0 ); gl.depthMask( true ); gl.depthFunc( gl.LESS ); gl.clearDepth( 1 ); gl.stencilMask( 0xffffffff ); gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); gl.clearStencil( 0 ); gl.cullFace( gl.BACK ); gl.frontFace( gl.CCW ); gl.polygonOffset( 0, 0 ); gl.activeTexture( gl.TEXTURE0 ); gl.bindFramebuffer( gl.FRAMEBUFFER, null ); if ( isWebGL2 === true ) { gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); } gl.useProgram( null ); gl.lineWidth( 1 ); gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture" ) ? extensions.get( "WEBGL_multisampled_render_to_texture" ) : null; const supportsInvalidateFramebuffer = typeof navigator === "undefined" ? false : /OculusBrowser/g.test( navigator.userAgent ); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" // eslint-disable-next-line compat/compat && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; } catch ( err ) { // Ignore any errors } function createCanvas( width, height ) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas( width, height ) : createElementNS( "canvas" ); } function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { let scale = 1; // handle case if texture exceeds max size if ( image.width > maxSize || image.height > maxSize ) { scale = maxSize / Math.max( image.width, image.height ); } // only perform resize if necessary if ( scale < 1 || needsPowerOfTwo === true ) { // only perform resize for certain image types if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor( scale * image.width ); const height = floor( scale * image.height ); if ( _canvas === undefined ) _canvas = createCanvas( width, height ); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, width, height ); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")." ); return canvas; } else { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); } return image; } } return image; } function isPowerOfTwo$1( image ) { return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); } function textureNeedsPowerOfTwo( texture ) { if ( isWebGL2 ) return false; return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, supportsMips ) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target ) { _gl.generateMipmap( target ); } function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) { if ( isWebGL2 === false ) return glFormat; if ( internalFormatName !== null ) { if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """ ); } let internalFormat = glFormat; if ( glFormat === _gl.RED ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.R32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.R16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8; } if ( glFormat === _gl.RED_INTEGER ) { if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8UI; if ( glType === _gl.UNSIGNED_SHORT ) internalFormat = _gl.R16UI; if ( glType === _gl.UNSIGNED_INT ) internalFormat = _gl.R32UI; if ( glType === _gl.BYTE ) internalFormat = _gl.R8I; if ( glType === _gl.SHORT ) internalFormat = _gl.R16I; if ( glType === _gl.INT ) internalFormat = _gl.R32I; } if ( glFormat === _gl.RG ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8; } if ( glFormat === _gl.RGBA ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if ( glType === _gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = _gl.RGBA4; if ( glType === _gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = _gl.RGB5_A1; } if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { extensions.get( "EXT_color_buffer_float" ); } return internalFormat; } function getMipLevels( texture, image, supportsMips ) { if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { return Math.log2( Math.max( image.width, image.height ) ) + 1; } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { // user-defined mipmaps return texture.mipmaps.length; } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { _videoTextures.delete( texture ); } } function onRenderTargetDispose( event ) { const renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); } // function deallocateTexture( texture ) { const textureProperties = properties.get( texture ); if ( textureProperties.__webglInit === undefined ) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get( source ); if ( webglTextures ) { const webglTexture = webglTextures[ textureProperties.__cacheKey ]; webglTexture.usedTimes --; // the WebGLTexture object is not used anymore, remove it if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } // remove the weak map entry if no WebGLTexture uses the source anymore if ( Object.keys( webglTextures ).length === 0 ) { _sources.delete( source ); } } properties.remove( texture ); } function deleteTexture( texture ) { const textureProperties = properties.get( texture ); _gl.deleteTexture( textureProperties.__webglTexture ); const source = texture.source; const webglTextures = _sources.get( source ); delete webglTextures[ textureProperties.__cacheKey ]; info.memory.textures --; } function deallocateRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); info.memory.textures --; } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLCubeRenderTarget ) { for ( let i = 0; i < 6; i ++ ) { if ( Array.isArray( renderTargetProperties.__webglFramebuffer[ i ] ) ) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer[ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ][ level ] ); } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); } if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { if ( Array.isArray( renderTargetProperties.__webglFramebuffer ) ) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ level ] ); } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); } if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); if ( renderTargetProperties.__webglColorRenderbuffer ) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) { if ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] ); } } if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { for ( let i = 0, il = texture.length; i < il; i ++ ) { const attachmentProperties = properties.get( texture[ i ] ); if ( attachmentProperties.__webglTexture ) { _gl.deleteTexture( attachmentProperties.__webglTexture ); info.memory.textures --; } properties.remove( texture[ i ] ); } } properties.remove( texture ); properties.remove( renderTarget ); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if ( textureUnit >= maxTextures ) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey( texture ) { const array = []; array.push( texture.wrapS ); array.push( texture.wrapT ); array.push( texture.wrapR || 0 ); array.push( texture.magFilter ); array.push( texture.minFilter ); array.push( texture.anisotropy ); array.push( texture.internalFormat ); array.push( texture.format ); array.push( texture.type ); array.push( texture.generateMipmaps ); array.push( texture.premultiplyAlpha ); array.push( texture.flipY ); array.push( texture.unpackAlignment ); array.push( texture.colorSpace ); return array.join(); } // function setTexture2D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if ( image === null ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found." ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete" ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture2DArray( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture3D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTextureCube( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } const wrappingToGL = { [ RepeatWrapping ]: _gl.REPEAT, [ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE, [ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT }; const filterToGL = { [ NearestFilter ]: _gl.NEAREST, [ NearestMipmapNearestFilter ]: _gl.NEAREST_MIPMAP_NEAREST, [ NearestMipmapLinearFilter ]: _gl.NEAREST_MIPMAP_LINEAR, [ LinearFilter ]: _gl.LINEAR, [ LinearMipmapNearestFilter ]: _gl.LINEAR_MIPMAP_NEAREST, [ LinearMipmapLinearFilter ]: _gl.LINEAR_MIPMAP_LINEAR }; const compareToGL = { [ NeverCompare ]: _gl.NEVER, [ AlwaysCompare ]: _gl.ALWAYS, [ LessCompare ]: _gl.LESS, [ LessEqualCompare ]: _gl.LEQUAL, [ EqualCompare ]: _gl.EQUAL, [ GreaterEqualCompare ]: _gl.GEQUAL, [ GreaterCompare ]: _gl.GREATER, [ NotEqualCompare ]: _gl.NOTEQUAL }; function setTextureParameters( textureType, texture, supportsMips ) { if ( supportsMips ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE ); } if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping." ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter." ); } } if ( texture.compareFunction ) { _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE ); _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] ); } if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( texture.magFilter === NearestFilter ) return; if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; if ( texture.type === FloatType && extensions.has( "OES_texture_float_linear" ) === false ) return; // verify extension for WebGL 1 and WebGL 2 if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( "OES_texture_half_float_linear" ) === false ) ) return; // verify extension for WebGL 1 only if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function initTexture( textureProperties, texture ) { let forceUpload = false; if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get( source ); if ( webglTextures === undefined ) { webglTextures = {}; _sources.set( source, webglTextures ); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey( texture ); if ( textureCacheKey !== textureProperties.__cacheKey ) { // if not, create a new instance of WebGLTexture if ( webglTextures[ textureCacheKey ] === undefined ) { // create new entry webglTextures[ textureCacheKey ] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures ++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[ textureCacheKey ].usedTimes ++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[ textureProperties.__cacheKey ]; if ( webglTexture !== undefined ) { webglTextures[ textureProperties.__cacheKey ].usedTimes --; if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; } return forceUpload; } function uploadTexture( textureProperties, texture, slot ) { let textureType = _gl.TEXTURE_2D; if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY; if ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); image = verifyColorSpace( texture, image ); const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ); let glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); setTextureParameters( textureType, texture, supportsMips ); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); const levels = getMipLevels( texture, image, supportsMips ); if ( texture.isDepthTexture ) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if ( isWebGL2 ) { if ( texture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( texture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if ( texture.type === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if ( texture.type === FloatType ) { console.error( "WebGLRenderer: Floating point depth texture requires WebGL2." ); } } // validation checks for WebGL 1 if ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedIntType; glType = utils.convert( texture.type ); } } if ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } // if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } } } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } } else if ( texture.isCompressedTexture ) { if ( texture.isCompressedArrayTexture ) { if ( useTexStorage && allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 ); } else { state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data ); } } } } else { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else if ( texture.isDataArrayTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isData3DTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isFramebufferTexture ) { if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } else { let width = image.width, height = image.height; for ( let i = 0; i < levels; i ++ ) { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image ); } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( textureType ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } function uploadCubeTexture( textureProperties, texture, slot ) { if ( texture.image.length !== 6 ) return; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); const cubeImage = []; for ( let i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); } const image = cubeImage[ 0 ], supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ), glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); let levels = getMipLevels( texture, image, supportsMips ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); let mipmaps; if ( isCompressed ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height ); } for ( let i = 0; i < 6; i ++ ) { mipmaps = cubeImage[ i ].mipmaps; for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else { mipmaps = texture.mipmaps; if ( useTexStorage && allocateMemory ) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if ( mipmaps.length > 0 ) levels ++; state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); } for ( let i = 0; i < 6; i ++ ) { if ( isDataTexture ) { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; const mipmapImage = mipmap.image[ i ].image; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); } } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); } } } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { // We assume images for cube map have the same size. generateMipmap( _gl.TEXTURE_CUBE_MAP ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level ) { const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const renderTargetProperties = properties.get( renderTarget ); if ( ! renderTargetProperties.__hasExternalTextures ) { const width = Math.max( 1, renderTarget.width >> level ); const height = Math.max( 1, renderTarget.height >> level ); if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null ); } else { state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); } else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753 _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, level ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if ( isMultisample || useMultisampledRTT( renderTarget ) ) { const depthTexture = renderTarget.depthTexture; if ( depthTexture && depthTexture.isDepthTexture ) { if ( depthTexture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( depthTexture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples( renderTarget ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } } } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; const samples = getRenderTargetSamples( renderTarget ); if ( renderTarget.depthTexture.format === DepthFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( let i = 0; i < 6; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); } } else { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // rebind framebuffer with external textures function rebindTextures( renderTarget, colorTexture, depthTexture ) { const renderTargetProperties = properties.get( renderTarget ); if ( colorTexture !== undefined ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0 ); } if ( depthTexture !== undefined ) { setupDepthRenderbuffer( renderTarget ); } } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { if ( textureProperties.__webglTexture === undefined ) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures ++; } const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( let i = 0; i < 6; i ++ ) { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { renderTargetProperties.__webglFramebuffer[ i ] = []; for ( let level = 0; level < texture.mipmaps.length; level ++ ) { renderTargetProperties.__webglFramebuffer[ i ][ level ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } } else { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { renderTargetProperties.__webglFramebuffer = []; for ( let level = 0; level < texture.mipmaps.length; level ++ ) { renderTargetProperties.__webglFramebuffer[ level ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } if ( isMultipleRenderTargets ) { if ( capabilities.drawBuffers ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachmentProperties = properties.get( textures[ i ] ); if ( attachmentProperties.__webglTexture === undefined ) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } } } else { console.warn( "THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension." ); } } if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = isMultipleRenderTargets ? texture : [ texture ]; renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; renderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true ); const samples = getRenderTargetSamples( renderTarget ); _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); if ( renderTarget.depthBuffer ) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); for ( let i = 0; i < 6; i ++ ) { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { for ( let level = 0; level < texture.mipmaps.length; level ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ][ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 ); } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( _gl.TEXTURE_CUBE_MAP ); } state.unbindTexture(); } else if ( isMultipleRenderTargets ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachment = textures[ i ]; const attachmentProperties = properties.get( attachment ); state.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0 ); if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { generateMipmap( _gl.TEXTURE_2D ); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { if ( isWebGL2 ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error( "THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2." ); } } state.bindTexture( glTextureType, textureProperties.__webglTexture ); setTextureParameters( glTextureType, texture, supportsMips ); if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { for ( let level = 0; level < texture.mipmaps.length; level ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0 ); } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( glTextureType ); } state.unbindTexture(); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0, il = textures.length; i < il; i ++ ) { const texture = textures[ i ]; if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target ); state.unbindTexture(); } } } function updateMultisampleRenderTarget( renderTarget ) { if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ]; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = []; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get( renderTarget ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); // If MRT we need to remove FBO attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null ); state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { invalidationArray.push( _gl.COLOR_ATTACHMENT0 + i ); if ( renderTarget.depthBuffer ) { invalidationArray.push( depthStyle ); } const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; if ( ignoreDepthValues === false ) { if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT; if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT; } if ( isMultipleRenderTargets ) { _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } if ( ignoreDepthValues === true ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] ); _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] ); } if ( isMultipleRenderTargets ) { const webglTexture = properties.get( textures[ i ] ).__webglTexture; _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST ); if ( supportsInvalidateFramebuffer ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null ); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const webglTexture = properties.get( textures[ i ] ).__webglTexture; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 ); } } state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); } } function getRenderTargetSamples( renderTarget ) { return Math.min( maxSamples, renderTarget.samples ); } function useMultisampledRTT( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); return isWebGL2 && renderTarget.samples > 0 && extensions.has( "WEBGL_multisampled_render_to_texture" ) === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture( texture ) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures.get( texture ) !== frame ) { _videoTextures.set( texture, frame ); texture.update(); } } function verifyColorSpace( texture, image ) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if ( texture.isCompressedTexture === true || texture.format === _SRGBAFormat ) return image; if ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) { // sRGB if ( colorSpace === SRGBColorSpace ) { if ( isWebGL2 === false ) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if ( extensions.has( "EXT_sRGB" ) === true && format === RGBAFormat ) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear( image ); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if ( format !== RGBAFormat || type !== UnsignedByteType ) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType." ); } } } else { console.error( "THREE.WebGLTextures: Unsupported texture color space:", colorSpace ); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function convert( p, colorSpace = NoColorSpace ) { let extension; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { if ( isWebGL2 ) return gl.HALF_FLOAT; extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) { return extension.HALF_FLOAT_OES; } else { return null; } } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; // WebGL 1 sRGB fallback if ( p === _SRGBAFormat ) { extension = extensions.get( "EXT_sRGB" ); if ( extension !== null ) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if ( p === RedFormat ) return gl.RED; if ( p === RedIntegerFormat ) return gl.RED_INTEGER; if ( p === RGFormat ) return gl.RG; if ( p === RGIntegerFormat ) return gl.RG_INTEGER; if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if ( colorSpace === SRGBColorSpace ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc_srgb" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc" ); if ( extension !== null ) { if ( p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format ) { extension = extensions.get( "EXT_texture_compression_bptc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // RGTC if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { extension = extensions.get( "EXT_texture_compression_rgtc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT; if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if ( p === UnsignedInt248Type ) { if ( isWebGL2 ) return gl.UNSIGNED_INT_24_8; extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return ( gl[ p ] !== undefined ) ? gl[ p ] : null; } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor( array = [] ) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if ( this._hand === null ) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if ( this._targetRay === null ) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if ( this._grip === null ) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent( event ) { if ( this._targetRay !== null ) { this._targetRay.dispatchEvent( event ); } if ( this._grip !== null ) { this._grip.dispatchEvent( event ); } if ( this._hand !== null ) { this._hand.dispatchEvent( event ); } return this; } connect( inputSource ) { if ( inputSource && inputSource.hand ) { const hand = this._hand; if ( hand ) { for ( const inputjoint of inputSource.hand.values() ) { // Initialize hand with joints when connected this._getHandJoint( hand, inputjoint ); } } } this.dispatchEvent( { type: "connected", data: inputSource } ); return this; } disconnect( inputSource ) { this.dispatchEvent( { type: "disconnected", data: inputSource } ); if ( this._targetRay !== null ) { this._targetRay.visible = false; } if ( this._grip !== null ) { this._grip.visible = false; } if ( this._hand !== null ) { this._hand.visible = false; } return this; } update( inputSource, frame, referenceSpace ) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if ( inputSource && frame.session.visibilityState !== "visible-blurred" ) { if ( hand && inputSource.hand ) { handPose = true; for ( const inputjoint of inputSource.hand.values() ) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose( inputjoint, referenceSpace ); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint( hand, inputjoint ); if ( jointPose !== null ) { joint.matrix.fromArray( jointPose.transform.matrix ); joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints[ "index-finger-tip" ]; const thumbTip = hand.joints[ "thumb-tip" ]; const distance = indexTip.position.distanceTo( thumbTip.position ); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent( { type: "pinchend", handedness: inputSource.handedness, target: this } ); } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent( { type: "pinchstart", handedness: inputSource.handedness, target: this } ); } } else { if ( grip !== null && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { grip.matrix.fromArray( gripPose.transform.matrix ); grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); grip.matrixWorldNeedsUpdate = true; if ( gripPose.linearVelocity ) { grip.hasLinearVelocity = true; grip.linearVelocity.copy( gripPose.linearVelocity ); } else { grip.hasLinearVelocity = false; } if ( gripPose.angularVelocity ) { grip.hasAngularVelocity = true; grip.angularVelocity.copy( gripPose.angularVelocity ); } else { grip.hasAngularVelocity = false; } } } } if ( targetRay !== null ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if ( inputPose === null && gripPose !== null ) { inputPose = gripPose; } if ( inputPose !== null ) { targetRay.matrix.fromArray( inputPose.transform.matrix ); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); targetRay.matrixWorldNeedsUpdate = true; if ( inputPose.linearVelocity ) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy( inputPose.linearVelocity ); } else { targetRay.hasLinearVelocity = false; } if ( inputPose.angularVelocity ) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy( inputPose.angularVelocity ); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent( _moveEvent ); } } } if ( targetRay !== null ) { targetRay.visible = ( inputPose !== null ); } if ( grip !== null ) { grip.visible = ( gripPose !== null ); } if ( hand !== null ) { hand.visible = ( handPose !== null ); } return this; } // private method _getHandJoint( hand, inputjoint ) { if ( hand.joints[ inputjoint.jointName ] === undefined ) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[ inputjoint.jointName ] = joint; hand.add( joint ); } return hand.joints[ inputjoint.jointName ]; } } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedIntType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isDepthTexture = true; this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; this.compareFunction = null; } copy( source ) { super.copy( source ); this.compareFunction = source.compareFunction; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.compareFunction !== null ) data.compareFunction = this.compareFunction; return data; } } class WebXRManager extends EventDispatcher { constructor( renderer, gl ) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; // const cameraL = new PerspectiveCamera(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); const cameras = [ cameraL, cameraR ]; const cameraXR = new ArrayCamera(); cameraXR.layers.enable( 1 ); cameraXR.layers.enable( 2 ); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getGripSpace(); }; this.getHand = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getHandSpace(); }; // function onSessionEvent( event ) { const controllerIndex = controllerInputSources.indexOf( event.inputSource ); if ( controllerIndex === - 1 ) { return; } const controller = controllers[ controllerIndex ]; if ( controller !== undefined ) { controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace ); controller.dispatchEvent( { type: event.type, data: event.inputSource } ); } } function onSessionEnd() { session.removeEventListener( "select", onSessionEvent ); session.removeEventListener( "selectstart", onSessionEvent ); session.removeEventListener( "selectend", onSessionEvent ); session.removeEventListener( "squeeze", onSessionEvent ); session.removeEventListener( "squeezestart", onSessionEvent ); session.removeEventListener( "squeezeend", onSessionEvent ); session.removeEventListener( "end", onSessionEnd ); session.removeEventListener( "inputsourceschange", onInputSourcesChange ); for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; if ( inputSource === null ) continue; controllerInputSources[ i ] = null; controllers[ i ].disconnect( inputSource ); } _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget( initialRenderTarget ); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting." ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting." ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function ( space ) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function ( value ) { session = value; if ( session !== null ) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); session.addEventListener( "inputsourceschange", onInputSourcesChange ); if ( attributes.xrCompatible !== true ) { await gl.makeXRCompatible(); } if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { const layerInit = { antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: glBaseLayer } ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil } ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if ( attributes.depth ) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding( session, gl ); glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); session.updateRenderState( { layers: [ glProjLayer ] } ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0 } ); const renderTargetProperties = renderer.properties.get( newRenderTarget ); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation( foveation ); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); animation.setContext( session ); animation.start(); scope.isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } }; this.getEnvironmentBlendMode = function () { if ( session !== null ) { return session.environmentBlendMode; } }; function onInputSourcesChange( event ) { // Notify disconnected for ( let i = 0; i < event.removed.length; i ++ ) { const inputSource = event.removed[ i ]; const index = controllerInputSources.indexOf( inputSource ); if ( index >= 0 ) { controllerInputSources[ index ] = null; controllers[ index ].disconnect( inputSource ); } } // Notify connected for ( let i = 0; i < event.added.length; i ++ ) { const inputSource = event.added[ i ]; let controllerIndex = controllerInputSources.indexOf( inputSource ); if ( controllerIndex === - 1 ) { // Assign input source a controller that currently has no input source for ( let i = 0; i < controllers.length; i ++ ) { if ( i >= controllerInputSources.length ) { controllerInputSources.push( inputSource ); controllerIndex = i; break; } else if ( controllerInputSources[ i ] === null ) { controllerInputSources[ i ] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if ( controllerIndex === - 1 ) break; } const controller = controllers[ controllerIndex ]; if ( controller ) { controller.connect( inputSource ); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); const ipd = cameraLPos.distanceTo( cameraRPos ); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / ( - leftFov + rightFov ); const xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + ( ipd - xOffset ); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); } this.updateCamera = function ( camera ) { if ( session === null ) return; cameraXR.near = cameraR.near = cameraL.near = camera.near; cameraXR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraXR.near, depthFar: cameraXR.far } ); _currentDepthNear = cameraXR.near; _currentDepthFar = cameraXR.far; } const parent = camera.parent; const cameras = cameraXR.cameras; updateCamera( cameraXR, parent ); for ( let i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } // update projection matrix for proper view frustum culling if ( cameras.length === 2 ) { setProjectionFromUnion( cameraXR, cameraL, cameraR ); } else { // assume single camera setup (AR) cameraXR.projectionMatrix.copy( cameraL.projectionMatrix ); } // update user camera and its children updateUserCamera( camera, cameraXR, parent ); }; function updateUserCamera( camera, cameraXR, parent ) { if ( parent === null ) { camera.matrix.copy( cameraXR.matrixWorld ); } else { camera.matrix.copy( parent.matrixWorld ); camera.matrix.invert(); camera.matrix.multiply( cameraXR.matrixWorld ); } camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.updateMatrixWorld( true ); const children = camera.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } camera.projectionMatrix.copy( cameraXR.projectionMatrix ); camera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse ); if ( camera.isPerspectiveCamera ) { camera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] ); camera.zoom = 1; } } this.getCamera = function () { return cameraXR; }; this.getFoveation = function () { if ( glProjLayer === null && glBaseLayer === null ) { return undefined; } return foveation; }; this.setFoveation = function ( value ) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if ( glProjLayer !== null ) { glProjLayer.fixedFoveation = value; } if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { glBaseLayer.fixedFoveation = value; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); xrFrame = frame; if ( pose !== null ) { const views = pose.views; if ( glBaseLayer !== null ) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); renderer.setRenderTarget( newRenderTarget ); } let cameraXRNeedsUpdate = false; // check if it"s necessary to rebuild cameraXR"s camera list if ( views.length !== cameraXR.cameras.length ) { cameraXR.cameras.length = 0; cameraXRNeedsUpdate = true; } for ( let i = 0; i < views.length; i ++ ) { const view = views[ i ]; let viewport = null; if ( glBaseLayer !== null ) { viewport = glBaseLayer.getViewport( view ); } else { const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if ( i === 0 ) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); renderer.setRenderTarget( newRenderTarget ); } } let camera = cameras[ i ]; if ( camera === undefined ) { camera = new PerspectiveCamera(); camera.layers.enable( i ); camera.viewport = new Vector4(); cameras[ i ] = camera; } camera.matrix.fromArray( view.transform.matrix ); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraXR.matrix.copy( camera.matrix ); cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale ); } if ( cameraXRNeedsUpdate === true ) { cameraXR.cameras.push( camera ); } } } // for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; const controller = controllers[ i ]; if ( inputSource !== null && controller !== undefined ) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace ); } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); if ( frame.detectedPlanes ) { scope.dispatchEvent( { type: "planesdetected", data: frame } ); } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials( renderer, properties ) { function refreshTransformUniform( map, uniform ) { if ( map.matrixAutoUpdate === true ) { map.updateMatrix(); } uniform.value.copy( map.matrix ); } function refreshFogUniforms( uniforms, fog ) { fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) ); if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshToonMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsToon( uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsPhong( uniforms, material ); } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsStandard( uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); } } else if ( material.isMeshMatcapMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsMatcap( uniforms, material ); } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsDistance( uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( uniforms, material, pixelRatio, height ); } else if ( material.isSpriteMaterial ) { refreshUniformsSprites( uniforms, material ); } else if ( material.isShadowMaterial ) { uniforms.color.value.copy( material.color ); uniforms.opacity.value = material.opacity; } else if ( material.isShaderMaterial ) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value.copy( material.color ); } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform ); uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform( material.normalMap, uniforms.normalMapTransform ); uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform ); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform ); } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform( material.specularMap, uniforms.specularMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get( material ).envMap; if ( envMap ) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; // artist-friendly light intensity scaling factor const scaleFactor = ( renderer._useLegacyLights === true ) ? Math.PI : 1; uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; refreshTransformUniform( material.lightMap, uniforms.lightMapTransform ); } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform( material.aoMap, uniforms.aoMapTransform ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.uvTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value.copy( material.specular ); uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon( uniforms, material ) { if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.metalness.value = material.metalness; if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform ); } uniforms.roughness.value = material.roughness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform ); } const envMap = properties.get( material ).envMap; if ( envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { uniforms.ior.value = material.ior; // also part of uniforms common if ( material.sheen > 0 ) { uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); uniforms.sheenRoughness.value = material.sheenRoughness; if ( material.sheenColorMap ) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform ); } if ( material.sheenRoughnessMap ) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform ); } } if ( material.clearcoat > 0 ) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if ( material.clearcoatMap ) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform ); } if ( material.clearcoatRoughnessMap ) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform ); } if ( material.clearcoatNormalMap ) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform ); uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); if ( material.side === BackSide ) { uniforms.clearcoatNormalScale.value.negate(); } } } if ( material.iridescence > 0 ) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ]; if ( material.iridescenceMap ) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform ); } if ( material.iridescenceThicknessMap ) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform ); } } if ( material.transmission > 0 ) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); if ( material.transmissionMap ) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform ); } uniforms.thickness.value = material.thickness; if ( material.thicknessMap ) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform ); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy( material.attenuationColor ); } if ( material.anisotropy > 0 ) { uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) ); if ( material.anisotropyMap ) { uniforms.anisotropyMap.value = material.anisotropyMap; refreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform ); } } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy( material.specularColor ); if ( material.specularColorMap ) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform ); } if ( material.specularIntensityMap ) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform ); } } function refreshUniformsMatcap( uniforms, material ) { if ( material.matcap ) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance( uniforms, material ) { const light = properties.get( material ).light; uniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld ); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function WebGLUniformsGroups( gl, info, capabilities, state ) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program function bind( uniformsGroup, program ) { const webglProgram = program.program; state.uniformBlockBinding( uniformsGroup, webglProgram ); } function update( uniformsGroup, program ) { let buffer = buffers[ uniformsGroup.id ]; if ( buffer === undefined ) { prepareUniformsGroup( uniformsGroup ); buffer = createBuffer( uniformsGroup ); buffers[ uniformsGroup.id ] = buffer; uniformsGroup.addEventListener( "dispose", onUniformsGroupsDispose ); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping( uniformsGroup, webglProgram ); // update UBO once per frame const frame = info.render.frame; if ( updateList[ uniformsGroup.id ] !== frame ) { updateBufferData( uniformsGroup ); updateList[ uniformsGroup.id ] = frame; } } function createBuffer( uniformsGroup ) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); gl.bufferData( gl.UNIFORM_BUFFER, size, usage ); gl.bindBuffer( gl.UNIFORM_BUFFER, null ); gl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer ); return buffer; } function allocateBindingPointIndex() { for ( let i = 0; i < maxBindingPoints; i ++ ) { if ( allocatedBindingPoints.indexOf( i ) === - 1 ) { allocatedBindingPoints.push( i ); return i; } } console.error( "THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached." ); return 0; } function updateBufferData( uniformsGroup ) { const buffer = buffers[ uniformsGroup.id ]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); for ( let i = 0, il = uniforms.length; i < il; i ++ ) { const uniform = uniforms[ i ]; // partly update the buffer if necessary if ( hasUniformChanged( uniform, i, cache ) === true ) { const offset = uniform.__offset; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; let arrayOffset = 0; for ( let i = 0; i < values.length; i ++ ) { const value = values[ i ]; const info = getUniformSize( value ); if ( typeof value === "number" ) { uniform.__data[ 0 ] = value; gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data ); } else if ( value.isMatrix3 ) { // manually converting 3x3 to 3x4 uniform.__data[ 0 ] = value.elements[ 0 ]; uniform.__data[ 1 ] = value.elements[ 1 ]; uniform.__data[ 2 ] = value.elements[ 2 ]; uniform.__data[ 3 ] = value.elements[ 0 ]; uniform.__data[ 4 ] = value.elements[ 3 ]; uniform.__data[ 5 ] = value.elements[ 4 ]; uniform.__data[ 6 ] = value.elements[ 5 ]; uniform.__data[ 7 ] = value.elements[ 0 ]; uniform.__data[ 8 ] = value.elements[ 6 ]; uniform.__data[ 9 ] = value.elements[ 7 ]; uniform.__data[ 10 ] = value.elements[ 8 ]; uniform.__data[ 11 ] = value.elements[ 0 ]; } else { value.toArray( uniform.__data, arrayOffset ); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data ); } } gl.bindBuffer( gl.UNIFORM_BUFFER, null ); } function hasUniformChanged( uniform, index, cache ) { const value = uniform.value; if ( cache[ index ] === undefined ) { // cache entry does not exist so far if ( typeof value === "number" ) { cache[ index ] = value; } else { const values = Array.isArray( value ) ? value : [ value ]; const tempValues = []; for ( let i = 0; i < values.length; i ++ ) { tempValues.push( values[ i ].clone() ); } cache[ index ] = tempValues; } return true; } else { // compare current value with cached entry if ( typeof value === "number" ) { if ( cache[ index ] !== value ) { cache[ index ] = value; return true; } } else { const cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ]; const values = Array.isArray( value ) ? value : [ value ]; for ( let i = 0; i < cachedObjects.length; i ++ ) { const cachedObject = cachedObjects[ i ]; if ( cachedObject.equals( values[ i ] ) === false ) { cachedObject.copy( values[ i ] ); return true; } } } } return false; } function prepareUniformsGroup( uniformsGroup ) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes let chunkOffset = 0; // offset within a single chunk in bytes for ( let i = 0, l = uniforms.length; i < l; i ++ ) { const uniform = uniforms[ i ]; const infos = { boundary: 0, // bytes storage: 0 // bytes }; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; for ( let j = 0, jl = values.length; j < jl; j ++ ) { const value = values[ j ]; const info = getUniformSize( value ); infos.boundary += info.boundary; infos.storage += info.storage; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT ); uniform.__offset = offset; // if ( i > 0 ) { chunkOffset = offset % chunkSize; const remainingSizeInChunk = chunkSize - chunkOffset; // check for chunk overflow if ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) { // add padding and adjust offset offset += ( chunkSize - chunkOffset ); uniform.__offset = offset; } } offset += infos.storage; } // ensure correct final padding chunkOffset = offset % chunkSize; if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset ); // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize( value ) { const info = { boundary: 0, // bytes storage: 0 // bytes }; // determine sizes according to STD140 if ( typeof value === "number" ) { // float/int info.boundary = 4; info.storage = 4; } else if ( value.isVector2 ) { // vec2 info.boundary = 8; info.storage = 8; } else if ( value.isVector3 || value.isColor ) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if ( value.isVector4 ) { // vec4 info.boundary = 16; info.storage = 16; } else if ( value.isMatrix3 ) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if ( value.isMatrix4 ) { // mat4 info.boundary = 64; info.storage = 64; } else if ( value.isTexture ) { console.warn( "THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group." ); } else { console.warn( "THREE.WebGLRenderer: Unsupported uniform value type.", value ); } return info; } function onUniformsGroupsDispose( event ) { const uniformsGroup = event.target; uniformsGroup.removeEventListener( "dispose", onUniformsGroupsDispose ); const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex ); allocatedBindingPoints.splice( index, 1 ); gl.deleteBuffer( buffers[ uniformsGroup.id ] ); delete buffers[ uniformsGroup.id ]; delete updateList[ uniformsGroup.id ]; } function dispose() { for ( const id in buffers ) { gl.deleteBuffer( buffers[ id ] ); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose }; } function createCanvasElement() { const canvas = createElementNS( "canvas" ); canvas.style.display = "block"; return canvas; } class WebGLRenderer { constructor( parameters = {} ) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = true, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = "default", failIfMajorPerformanceCaveat = false, } = parameters; this.isWebGLRenderer = true; let _alpha; if ( context !== null ) { _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } const uintClearColor = new Uint32Array( 4 ); const intClearColor = new Int32Array( 4 ); let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputColorSpace = SRGBColorSpace; // physical lights this._useLegacyLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = - 1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; const _currentClearColor = new Color( 0x000000 ); let _currentClearAlpha = 0; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4( 0, 0, _width, _height ); const _scissor = new Vector4( 0, 0, _width, _height ); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext( contextNames, contextAttributes ) { for ( let i = 0; i < contextNames.length; i ++ ) { const contextName = contextNames[ i ]; const context = canvas.getContext( contextName, contextAttributes ); if ( context !== null ) return context; } return null; } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat, }; // OffscreenCanvas does not have setAttribute, see #22811 if ( "setAttribute" in canvas ) canvas.setAttribute( "data-engine", `three.js r${REVISION}` ); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener( "webglcontextlost", onContextLost, false ); canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false ); if ( _gl === null ) { const contextNames = [ "webgl2", "webgl", "experimental-webgl" ]; if ( _this.isWebGL1Renderer === true ) { contextNames.shift(); } _gl = getContext( contextNames, contextAttributes ); if ( _gl === null ) { if ( getContext( contextNames ) ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } } if ( typeof WebGLRenderingContext !== "undefined" && _gl instanceof WebGLRenderingContext ) { // @deprecated, r153 console.warn( "THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163." ); } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions( _gl ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); extensions.init( capabilities ); utils = new WebGLUtils( _gl, extensions, capabilities ); state = new WebGLState( _gl, extensions, capabilities ); info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); cubemaps = new WebGLCubeMaps( _this ); cubeuvmaps = new WebGLCubeUVMaps( _this ); attributes = new WebGLAttributes( _gl, capabilities ); bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); objects = new WebGLObjects( _gl, geometries, attributes, info ); morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); clipping = new WebGLClipping( properties ); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); materials = new WebGLMaterials( _this, properties ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions, capabilities ); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager( _this, _gl ); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function ( target ) { return target.set( _width, _height ); }; this.setSize = function ( width, height, updateStyle = true ) { if ( xr.isPresenting ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; canvas.width = Math.floor( width * _pixelRatio ); canvas.height = Math.floor( height * _pixelRatio ); if ( updateStyle === true ) { canvas.style.width = width + "px"; canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function ( target ) { return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor( width * pixelRatio ); canvas.height = Math.floor( height * pixelRatio ); this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function ( target ) { return target.copy( _currentViewport ); }; this.getViewport = function ( target ) { return target.copy( _viewport ); }; this.setViewport = function ( x, y, width, height ) { if ( x.isVector4 ) { _viewport.set( x.x, x.y, x.z, x.w ); } else { _viewport.set( x, y, width, height ); } state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissor = function ( target ) { return target.copy( _scissor ); }; this.setScissor = function ( x, y, width, height ) { if ( x.isVector4 ) { _scissor.set( x.x, x.y, x.z, x.w ); } else { _scissor.set( x, y, width, height ); } state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; this.setOpaqueSort = function ( method ) { _opaqueSort = method; }; this.setTransparentSort = function ( method ) { _transparentSort = method; }; // Clearing this.getClearColor = function ( target ) { return target.copy( background.getClearColor() ); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color = true, depth = true, stencil = true ) { let bits = 0; if ( color ) { // check if we"re trying to clear an integer target let isIntegerFormat = false; if ( _currentRenderTarget !== null ) { const targetFormat = _currentRenderTarget.texture.format; isIntegerFormat = targetFormat === RGBAIntegerFormat || targetFormat === RGIntegerFormat || targetFormat === RedIntegerFormat; } // use the appropriate clear functions to clear the target if it"s a signed // or unsigned integer target if ( isIntegerFormat ) { const targetType = _currentRenderTarget.texture.type; const isUnsignedType = targetType === UnsignedByteType || targetType === UnsignedIntType || targetType === UnsignedShortType || targetType === UnsignedInt248Type || targetType === UnsignedShort4444Type || targetType === UnsignedShort5551Type; const clearColor = background.getClearColor(); const a = background.getClearAlpha(); const r = clearColor.r; const g = clearColor.g; const b = clearColor.b; if ( isUnsignedType ) { uintClearColor[ 0 ] = r; uintClearColor[ 1 ] = g; uintClearColor[ 2 ] = b; uintClearColor[ 3 ] = a; _gl.clearBufferuiv( _gl.COLOR, 0, uintClearColor ); } else { intClearColor[ 0 ] = r; intClearColor[ 1 ] = g; intClearColor[ 2 ] = b; intClearColor[ 3 ] = a; _gl.clearBufferiv( _gl.COLOR, 0, intClearColor ); } } else { bits |= _gl.COLOR_BUFFER_BIT; } } if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; // this.dispose = function () { canvas.removeEventListener( "webglcontextlost", onContextLost, false ); canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener( "sessionstart", onXRSessionStart ); xr.removeEventListener( "sessionend", onXRSessionEnd ); if ( _transmissionRenderTarget ) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError( event ) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage ); } function onMaterialDispose( event ) { const material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReferences( material ); properties.remove( material ); } function releaseMaterialProgramReferences( material ) { const programs = properties.get( material ).programs; if ( programs !== undefined ) { programs.forEach( function ( program ) { programCache.releaseProgram( program ); } ); if ( material.isShaderMaterial ) { programCache.releaseShaderCache( material ); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); const program = setProgram( camera, scene, geometry, material, object ); state.setMaterial( material, frontFaceCW ); // let index = geometry.index; let rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); if ( index === undefined ) return; rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; if ( group !== null ) { drawStart = Math.max( drawStart, group.start * rangeFactor ); drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); } if ( index !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, index.count ); } else if ( position !== undefined && position !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, position.count ); } const drawCount = drawEnd - drawStart; if ( drawCount < 0 || drawCount === Infinity ) return; // bindingStates.setup( object, material, program, geometry, index ); let attribute; let renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { renderer.setMode( _gl.TRIANGLES ); } } else if ( object.isLine ) { let lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } else if ( object.isSprite ) { renderer.setMode( _gl.TRIANGLES ); } if ( object.isInstancedMesh ) { renderer.renderInstances( drawStart, drawCount, object.count ); } else if ( geometry.isInstancedBufferGeometry ) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); renderer.renderInstances( drawStart, drawCount, instanceCount ); } else { renderer.render( drawStart, drawCount ); } }; // Compile this.compile = function ( scene, camera ) { function prepare( material, scene, object ) { if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = FrontSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = DoubleSide; } else { getProgram( material, scene, object ); } } currentRenderState = renderStates.get( scene ); currentRenderState.init(); renderStateStack.push( currentRenderState ); scene.traverseVisible( function ( object ) { if ( object.isLight && object.layers.test( camera.layers ) ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( _this._useLegacyLights ); scene.traverse( function ( object ) { const material = object.material; if ( material ) { if ( Array.isArray( material ) ) { for ( let i = 0; i < material.length; i ++ ) { const material2 = material[ i ]; prepare( material2, scene, object ); } } else { prepare( material, scene, object ); } } } ); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time ) { if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); if ( typeof self !== "undefined" ) animation.setContext( self ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; xr.setAnimationLoop( callback ); ( callback === null ) ? animation.stop() : animation.start(); }; xr.addEventListener( "sessionstart", onXRSessionStart ); xr.addEventListener( "sessionend", onXRSessionEnd ); // Rendering this.render = function ( scene, camera ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost === true ) return; // update scene graph if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); if ( xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering } // if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); currentRenderState = renderStates.get( scene, renderStateStack.length ); currentRenderState.init(); renderStateStack.push( currentRenderState ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromProjectionMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); currentRenderList = renderLists.get( scene, renderListStack.length ); currentRenderList.init(); renderListStack.push( currentRenderList ); projectObject( scene, camera, 0, _this.sortObjects ); currentRenderList.finish(); if ( _this.sortObjects === true ) { currentRenderList.sort( _opaqueSort, _transparentSort ); } // this.info.render.frame ++; if ( _clippingEnabled === true ) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); if ( _clippingEnabled === true ) clipping.endShadows(); // if ( this.info.autoReset === true ) this.info.reset(); // background.render( currentRenderList, scene ); // render scene currentRenderState.setupLights( _this._useLegacyLights ); if ( camera.isArrayCamera ) { const cameras = camera.cameras; for ( let i = 0, l = cameras.length; i < l; i ++ ) { const camera2 = cameras[ i ]; renderScene( currentRenderList, scene, camera2, camera2.viewport ); } } else { renderScene( currentRenderList, scene, camera ); } // if ( _currentRenderTarget !== null ) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget( _currentRenderTarget ); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap( _currentRenderTarget ); } // if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = - 1; _currentCamera = null; renderStateStack.pop(); if ( renderStateStack.length > 0 ) { currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; } else { currentRenderState = null; } renderListStack.pop(); if ( renderListStack.length > 0 ) { currentRenderList = renderListStack[ renderListStack.length - 1 ]; } else { currentRenderList = null; } }; function projectObject( object, camera, groupOrder, sortObjects ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isGroup ) { groupOrder = object.renderOrder; } else if ( object.isLOD ) { if ( object.autoUpdate === true ) object.update( camera ); } else if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } const geometry = objects.update( object ); const material = object.material; if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { const geometry = objects.update( object ); const material = object.material; if ( sortObjects ) { if ( object.boundingSphere !== undefined ) { if ( object.boundingSphere === null ) object.computeBoundingSphere(); _vector3.copy( object.boundingSphere.center ); } else { if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _vector3.copy( geometry.boundingSphere.center ); } _vector3 .applyMatrix4( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, groupOrder, sortObjects ); } } function renderScene( currentRenderList, scene, camera, viewport ) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView( camera ); if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); } function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { const isWebGL2 = capabilities.isWebGL2; if ( _transmissionRenderTarget === null ) { _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { generateMipmaps: true, type: extensions.has( "EXT_color_buffer_half_float" ) ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: ( isWebGL2 ) ? 4 : 0 } ); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } _this.getDrawingBufferSize( _vector2 ); if ( isWebGL2 ) { _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); } else { _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget( _transmissionRenderTarget ); _this.getClearColor( _currentClearColor ); _currentClearAlpha = _this.getClearAlpha(); if ( _currentClearAlpha < 1 ) _this.setClearColor( 0xffffff, 0.5 ); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects( opaqueObjects, scene, camera ); textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); let renderTargetNeedsUpdate = false; for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { const renderItem = transmissiveObjects[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject( object, scene, camera, geometry, material, group ); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if ( renderTargetNeedsUpdate === true ) { textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); } _this.setRenderTarget( currentRenderTarget ); _this.setClearColor( _currentClearColor, _currentClearAlpha ); _this.toneMapping = currentToneMapping; } function renderObjects( renderList, scene, camera ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for ( let i = 0, l = renderList.length; i < l; i ++ ) { const renderItem = renderList[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if ( object.layers.test( camera.layers ) ) { renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); material.onBeforeRender( _this, scene, camera, geometry, object, group ); if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } function getProgram( material, scene, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); const programCacheKey = programCache.getProgramCacheKey( parameters ); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); if ( programs === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); programs = new Map(); materialProperties.programs = programs; } let program = programs.get( programCacheKey ); if ( program !== undefined ) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties( material, parameters ); return program; } } else { parameters.uniforms = programCache.getUniforms( material ); material.onBuild( object, parameters, _this ); material.onBeforeCompile( parameters, _this ); program = programCache.acquireProgram( parameters, programCacheKey ); programs.set( programCacheKey, program ); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties( material, parameters ); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights( material ); materialProperties.lightsStateVersion = lightsStateVersion; if ( materialProperties.needsLights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties( material, parameters ) { const materialProperties = properties.get( material ); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.instancing = parameters.instancing; materialProperties.instancingColor = parameters.instancingColor; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram( camera, scene, geometry, material, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ); const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 ); const morphTargets = !! geometry.morphAttributes.position; const morphNormals = !! geometry.morphAttributes.normal; const morphColors = !! geometry.morphAttributes.color; let toneMapping = NoToneMapping; if ( material.toneMapped ) { if ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) { toneMapping = _this.toneMapping; } } const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; if ( _clippingEnabled === true ) { if ( _localClippingEnabled === true || camera !== _currentCamera ) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState( material, camera, useCache ); } } // let needsProgramChange = false; if ( material.version === materialProperties.__version ) { if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { needsProgramChange = true; } else if ( materialProperties.outputColorSpace !== colorSpace ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) { needsProgramChange = true; } else if ( materialProperties.envMap !== envMap ) { needsProgramChange = true; } else if ( material.fog === true && materialProperties.fog !== fog ) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection ) ) { needsProgramChange = true; } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { needsProgramChange = true; } else if ( materialProperties.vertexTangents !== vertexTangents ) { needsProgramChange = true; } else if ( materialProperties.morphTargets !== morphTargets ) { needsProgramChange = true; } else if ( materialProperties.morphNormals !== morphNormals ) { needsProgramChange = true; } else if ( materialProperties.morphColors !== morphColors ) { needsProgramChange = true; } else if ( materialProperties.toneMapping !== toneMapping ) { needsProgramChange = true; } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if ( needsProgramChange === true ) { program = getProgram( material, scene, object ); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || _currentCamera !== camera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( _currentCamera !== camera ) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap ) { const uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true ); } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if ( object.isSkinnedMesh ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); const skeleton = object.skeleton; if ( skeleton ) { if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { console.warn( "THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required." ); } } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { morphtargets.update( object, geometry, program ); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue( _gl, "receiveShadow", object.receiveShadow ); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if ( material.isMeshGouraudMaterial && material.envMap !== null ) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); if ( materialProperties.needsLights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog === true ) { materials.refreshFogUniforms( m_uniforms, fog ); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); material.uniformsNeedUpdate = false; } if ( material.isSpriteMaterial ) { p_uniforms.setValue( _gl, "center", object.center ); } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); // UBOs if ( material.isShaderMaterial || material.isRawShaderMaterial ) { const groups = material.uniformsGroups; for ( let i = 0, l = groups.length; i < l; i ++ ) { if ( capabilities.isWebGL2 ) { const group = groups[ i ]; uniformsGroups.update( group, program ); uniformsGroups.bind( group, program ); } else { console.warn( "THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2." ); } } } return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights( material ) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || ( material.isShaderMaterial && material.lights === true ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { properties.get( renderTarget.texture ).__webglTexture = colorTexture; properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__hasExternalTextures = true; if ( renderTargetProperties.__hasExternalTextures ) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if ( extensions.has( "WEBGL_multisampled_render_to_texture" ) === true ) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided" ); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if ( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer( _gl.FRAMEBUFFER, null ); useDefaultFramebuffer = false; } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } else if ( renderTargetProperties.__hasExternalTextures ) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); } const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget ) { if ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) { framebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ]; } else { framebuffer = __webglFramebuffer[ activeCubeFace ]; } isCube = true; } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; } else { if ( Array.isArray( __webglFramebuffer ) ) { framebuffer = __webglFramebuffer[ activeMipmapLevel ]; } else { framebuffer = __webglFramebuffer; } } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { state.drawBuffers( renderTarget, framebuffer ); } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { const textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); } else if ( isRenderTarget3D ) { const textureProperties = properties.get( renderTarget.texture ); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); } _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } let framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[ activeCubeFaceIndex ]; } if ( framebuffer ) { state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( "EXT_color_buffer_half_float" ) || ( capabilities.isWebGL2 && extensions.has( "EXT_color_buffer_float" ) ) ); if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( "OES_texture_float" ) || extensions.has( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! halfFloatSupportedByExt ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); } } }; this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { const levelScale = Math.pow( 2, - level ); const width = Math.floor( texture.image.width * levelScale ); const height = Math.floor( texture.image.height * levelScale ); textures.setTexture2D( texture, 0 ); _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); state.unbindTexture(); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); textures.setTexture2D( dstTexture, 0 ); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { if ( srcTexture.isCompressedTexture ) { _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); } } // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { if ( _this.isWebGL1Renderer ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2." ); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); let glTarget; if ( dstTexture.isData3DTexture ) { textures.setTexture3D( dstTexture, 0 ); glTarget = _gl.TEXTURE_3D; } else if ( dstTexture.isDataArrayTexture ) { textures.setTexture2DArray( dstTexture, 0 ); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray." ); return; } _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); } else { if ( srcTexture.isCompressedArrayTexture ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture." ); _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); } else { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } } _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); state.unbindTexture(); }; this.initTexture = function ( texture ) { if ( texture.isCubeTexture ) { textures.setTextureCube( texture, 0 ); } else if ( texture.isData3DTexture ) { textures.setTexture3D( texture, 0 ); } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { textures.setTexture2DArray( texture, 0 ); } else { textures.setTexture2D( texture, 0 ); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } get coordinateSystem() { return WebGLCoordinateSystem; } get physicallyCorrectLights() { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); return ! this.useLegacyLights; } set physicallyCorrectLights( value ) { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); this.useLegacyLights = ! value; } get outputEncoding() { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); return this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set outputEncoding( encoding ) { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); this.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace; } get useLegacyLights() { // @deprecated, r155 console.warn( "THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733." ); return this._useLegacyLights; } set useLegacyLights( value ) { // @deprecated, r155 console.warn( "THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733." ); this._useLegacyLights = value; } } class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor( color, density = 0.00025 ) { this.isFogExp2 = true; this.name = ""; this.color = new Color( color ); this.density = density; } clone() { return new FogExp2( this.color, this.density ); } toJSON( /* meta */ ) { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } class Fog { constructor( color, near = 1, far = 1000 ) { this.isFog = true; this.name = ""; this.color = new Color( color ); this.near = near; this.far = far; } clone() { return new Fog( this.color, this.near, this.far ); } toJSON( /* meta */ ) { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.overrideMaterial = null; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } copy( source, recursive ) { super.copy( source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.environment !== null ) this.environment = source.environment.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); if ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness; if ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity; return data; } } class InterleavedBuffer { constructor( array, stride ) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( let i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } set( value, offset = 0 ) { this.array.set( value, offset ); return this; } clone( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; } const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); const ib = new this.constructor( array, this.stride ); ib.setUsage( this.usage ); return ib; } onUpload( callback ) { this.onUploadCallback = callback; return this; } toJSON( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } const _vector$5 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor( interleavedBuffer, itemSize, offset, normalized = false ) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate( value ) { this.data.needsUpdate = value; } applyMatrix4( m ) { for ( let i = 0, l = this.data.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyMatrix4( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyNormalMatrix( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.transformDirection( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.data.array[ index * this.data.stride + this.offset ] = x; return this; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; } getX( index ) { let x = this.data.array[ index * this.data.stride + this.offset ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } getY( index ) { let y = this.data.array[ index * this.data.stride + this.offset + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } getZ( index ) { let z = this.data.array[ index * this.data.stride + this.offset + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } getW( index ) { let w = this.data.array[ index * this.data.stride + this.offset + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setXY( index, x, y ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } clone( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); } return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); } } toJSON( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interleaved attribute if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } class SpriteMaterial extends Material { constructor( parameters ) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor( material ) { super(); this.isSprite = true; this.type = "Sprite"; if ( _geometry === undefined ) { _geometry = new BufferGeometry(); const float32Array = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0, 0.5, - 0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, - 0.5, 0.5, 0, 0, 1 ] ); const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); _geometry.setAttribute( "position", new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); } this.geometry = _geometry; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } raycast( raycaster, intersects ) { if ( raycaster.camera === null ) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites." ); } _worldScale.setFromMatrixScale( this.matrixWorld ); _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar( - _mvPosition.z ); } const rotation = this.material.rotation; let sin, cos; if ( rotation !== 0 ) { cos = Math.cos( rotation ); sin = Math.sin( rotation ); } const center = this.center; transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvA.set( 0, 0 ); _uvB.set( 1, 0 ); _uvC.set( 1, 1 ); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); if ( intersect === null ) { // check second triangle transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvB.set( 0, 1 ); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); if ( intersect === null ) { return; } } const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), face: null, object: this } ); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.center !== undefined ) this.center.copy( source.center ); this.material = source.material; return this; } } function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { // compute position in camera space _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); // to check if rotation is not zero if ( sin !== undefined ) { _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); } else { _rotatedPosition.copy( _alignedPosition ); } vertexPosition.copy( mvPosition ); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4( _viewWorldMatrix ); } const _v1$2 = /*@__PURE__*/ new Vector3(); const _v2$1 = /*@__PURE__*/ new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties( this, { levels: { enumerable: true, value: [] }, isLOD: { value: true, } } ); this.autoUpdate = true; } copy( source ) { super.copy( source, false ); const levels = source.levels; for ( let i = 0, l = levels.length; i < l; i ++ ) { const level = levels[ i ]; this.addLevel( level.object.clone(), level.distance, level.hysteresis ); } this.autoUpdate = source.autoUpdate; return this; } addLevel( object, distance = 0, hysteresis = 0 ) { distance = Math.abs( distance ); const levels = this.levels; let l; for ( l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } ); this.add( object ); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance( distance ) { const levels = this.levels; if ( levels.length > 0 ) { let i, l; for ( i = 1, l = levels.length; i < l; i ++ ) { let levelDistance = levels[ i ].distance; if ( levels[ i ].object.visible ) { levelDistance -= levelDistance * levels[ i ].hysteresis; } if ( distance < levelDistance ) { break; } } return levels[ i - 1 ].object; } return null; } raycast( raycaster, intersects ) { const levels = this.levels; if ( levels.length > 0 ) { _v1$2.setFromMatrixPosition( this.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _v1$2 ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); } } update( camera ) { const levels = this.levels; if ( levels.length > 1 ) { _v1$2.setFromMatrixPosition( camera.matrixWorld ); _v2$1.setFromMatrixPosition( this.matrixWorld ); const distance = _v1$2.distanceTo( _v2$1 ) / camera.zoom; levels[ 0 ].object.visible = true; let i, l; for ( i = 1, l = levels.length; i < l; i ++ ) { let levelDistance = levels[ i ].distance; if ( levels[ i ].object.visible ) { levelDistance -= levelDistance * levels[ i ].hysteresis; } if ( distance >= levelDistance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } this._currentLevel = i - 1; for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.autoUpdate === false ) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for ( let i = 0, l = levels.length; i < l; i ++ ) { const level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance, hysteresis: level.hysteresis } ); } return data; } } const _basePosition = /*@__PURE__*/ new Vector3(); const _skinIndex = /*@__PURE__*/ new Vector4(); const _skinWeight = /*@__PURE__*/ new Vector4(); const _vector3 = /*@__PURE__*/ new Vector3(); const _matrix4 = /*@__PURE__*/ new Matrix4(); const _vertex = /*@__PURE__*/ new Vector3(); const _sphere$3 = /*@__PURE__*/ new Sphere(); const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); class SkinnedMesh extends Mesh { constructor( geometry, material ) { super( geometry, material ); this.isSkinnedMesh = true; this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute( "position" ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingBox.expandByPoint( _vertex ); } } computeBoundingSphere() { const geometry = this.geometry; if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute( "position" ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingSphere.expandByPoint( _vertex ); } } copy( source, recursive ) { super.copy( source, recursive ); this.bindMode = source.bindMode; this.bindMatrix.copy( source.bindMatrix ); this.bindMatrixInverse.copy( source.bindMatrixInverse ); this.skeleton = source.skeleton; if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast( raycaster, intersects ) { const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // test with bounding sphere in world space if ( this.boundingSphere === null ) this.computeBoundingSphere(); _sphere$3.copy( this.boundingSphere ); _sphere$3.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; // convert ray to local space of skinned mesh _inverseMatrix$2.copy( matrixWorld ).invert(); _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); // test with bounding box in local space if ( this.boundingBox !== null ) { if ( _ray$2.intersectsBox( this.boundingBox ) === false ) return; } // test for intersections with geometry this._computeIntersections( raycaster, intersects, _ray$2 ); } getVertexPosition( index, target ) { super.getVertexPosition( index, target ); this.applyBoneTransform( index, target ); return target; } bind( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.copy( bindMatrix ).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { vector.fromBufferAttribute( skinWeight, i ); const scale = 1.0 / vector.manhattanLength(); if ( scale !== Infinity ) { vector.multiplyScalar( scale ); } else { vector.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); } } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.copy( this.matrixWorld ).invert(); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.copy( this.bindMatrix ).invert(); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode ); } } applyBoneTransform( index, vector ) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); _basePosition.copy( vector ).applyMatrix4( this.bindMatrix ); vector.set( 0, 0, 0 ); for ( let i = 0; i < 4; i ++ ) { const weight = _skinWeight.getComponent( i ); if ( weight !== 0 ) { const boneIndex = _skinIndex.getComponent( i ); _matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); vector.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight ); } } return vector.applyMatrix4( this.bindMatrixInverse ); } boneTransform( index, vector ) { // @deprecated, r151 console.warn( "THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151." ); return this.applyBoneTransform( index, vector ); } } class Bone extends Object3D { constructor() { super(); this.isBone = true; this.type = "Bone"; } } class DataTexture extends Texture { constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isDataTexture = true; this.image = { data: data, width: width, height: height }; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } const _offsetMatrix = /*@__PURE__*/ new Matrix4(); const _identityMatrix = /*@__PURE__*/ new Matrix4(); class Skeleton { constructor( bones = [], boneInverses = [] ) { this.uuid = generateUUID(); this.bones = bones.slice( 0 ); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array( bones.length * 16 ); // calculate inverse bone matrices if necessary if ( boneInverses.length === 0 ) { this.calculateInverses(); } else { // handle special case if ( bones.length !== boneInverses.length ) { console.warn( "THREE.Skeleton: Number of inverse bone matrices does not match amount of bones." ); this.boneInverses = []; for ( let i = 0, il = this.bones.length; i < il; i ++ ) { this.boneInverses.push( new Matrix4() ); } } } } calculateInverses() { this.boneInverses.length = 0; for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const inverse = new Matrix4(); if ( this.bones[ i ] ) { inverse.copy( this.bones[ i ].matrixWorld ).invert(); } this.boneInverses.push( inverse ); } } pose() { // recover the bind-time world matrices for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone ) { bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); } } // compute the local matrices, positions, rotations and scales for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.copy( bone.parent.matrixWorld ).invert(); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for ( let i = 0, il = bones.length; i < il; i ++ ) { // compute the offset between the current and the original transform const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); _offsetMatrix.toArray( boneMatrices, i * 16 ); } if ( boneTexture !== null ) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton( this.bones, this.boneInverses ); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo( size ); size = Math.max( size, 4 ); const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( this.boneMatrices ); // copy current values const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName( name ) { for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone.name === name ) { return bone; } } return undefined; } dispose( ) { if ( this.boneTexture !== null ) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON( json, bones ) { this.uuid = json.uuid; for ( let i = 0, l = json.bones.length; i < l; i ++ ) { const uuid = json.bones[ i ]; let bone = bones[ uuid ]; if ( bone === undefined ) { console.warn( "THREE.Skeleton: No bone found with UUID:", uuid ); bone = new Bone(); } this.bones.push( bone ); this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for ( let i = 0, l = bones.length; i < l; i ++ ) { const bone = bones[ i ]; data.bones.push( bone.uuid ); const boneInverse = boneInverses[ i ]; data.boneInverses.push( boneInverse.toArray() ); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { super( array, itemSize, normalized ); this.isInstancedBufferAttribute = true; this.meshPerAttribute = meshPerAttribute; } copy( source ) { super.copy( source ); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); const _instanceIntersects = []; const _box3 = /*@__PURE__*/ new Box3(); const _identity = /*@__PURE__*/ new Matrix4(); const _mesh = /*@__PURE__*/ new Mesh(); const _sphere$2 = /*@__PURE__*/ new Sphere(); class InstancedMesh extends Mesh { constructor( geometry, material, count ) { super( geometry, material ); this.isInstancedMesh = true; this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); this.instanceColor = null; this.count = count; this.boundingBox = null; this.boundingSphere = null; for ( let i = 0; i < count; i ++ ) { this.setMatrixAt( i, _identity ); } } computeBoundingBox() { const geometry = this.geometry; const count = this.count; if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } this.boundingBox.makeEmpty(); for ( let i = 0; i < count; i ++ ) { this.getMatrixAt( i, _instanceLocalMatrix ); _box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix ); this.boundingBox.union( _box3 ); } } computeBoundingSphere() { const geometry = this.geometry; const count = this.count; if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } this.boundingSphere.makeEmpty(); for ( let i = 0; i < count; i ++ ) { this.getMatrixAt( i, _instanceLocalMatrix ); _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix ); this.boundingSphere.union( _sphere$2 ); } } copy( source, recursive ) { super.copy( source, recursive ); this.instanceMatrix.copy( source.instanceMatrix ); if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); this.count = source.count; if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; } getColorAt( index, color ) { color.fromArray( this.instanceColor.array, index * 3 ); } getMatrixAt( index, matrix ) { matrix.fromArray( this.instanceMatrix.array, index * 16 ); } raycast( raycaster, intersects ) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if ( _mesh.material === undefined ) return; // test with bounding sphere first if ( this.boundingSphere === null ) this.computeBoundingSphere(); _sphere$2.copy( this.boundingSphere ); _sphere$2.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; // now test each instance for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { // calculate the world matrix for each instance this.getMatrixAt( instanceId, _instanceLocalMatrix ); _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast( raycaster, _instanceIntersects ); // process the result of raycast for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { const intersect = _instanceIntersects[ i ]; intersect.instanceId = instanceId; intersect.object = this; intersects.push( intersect ); } _instanceIntersects.length = 0; } } setColorAt( index, color ) { if ( this.instanceColor === null ) { this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); } color.toArray( this.instanceColor.array, index * 3 ); } setMatrixAt( index, matrix ) { matrix.toArray( this.instanceMatrix.array, index * 16 ); } updateMorphTargets() { } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class LineBasicMaterial extends Material { constructor( parameters ) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _start$1 = /*@__PURE__*/ new Vector3(); const _end$1 = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = [ 0 ]; for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { _start$1.fromBufferAttribute( positionAttribute, i - 1 ); _end$1.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += _start$1.distanceTo( _end$1 ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$1.copy( geometry.boundingSphere ); _sphere$1.applyMatrix4( matrixWorld ); _sphere$1.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; // _inverseMatrix$1.copy( matrixWorld ).invert(); _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { const a = index.getX( i ); const b = index.getX( i + 1 ); vStart.fromBufferAttribute( positionAttribute, a ); vEnd.fromBufferAttribute( positionAttribute, b ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { vStart.fromBufferAttribute( positionAttribute, i ); vEnd.fromBufferAttribute( positionAttribute, i + 1 ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { _start.fromBufferAttribute( positionAttribute, i ); _end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } } class LineLoop extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineLoop = true; this.type = "LineLoop"; } } class PointsMaterial extends Material { constructor( parameters ) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ); _sphere.applyMatrix4( matrixWorld ); _sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; // _inverseMatrix.copy( matrixWorld ).invert(); _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i ++ ) { const a = index.getX( i ); _position$2.fromBufferAttribute( positionAttribute, a ); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end; i < l; i ++ ) { _position$2.fromBufferAttribute( positionAttribute, i ); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { const rayPointDistanceSq = _ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { const intersectPoint = new Vector3(); _ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); const distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint, index: index, face: null, object: object } ); } } class VideoTexture extends Texture { constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isVideoTexture = true; this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback( updateVideo ); } if ( "requestVideoFrameCallback" in video ) { video.requestVideoFrameCallback( updateVideo ); } } clone() { return new this.constructor( this.image ).copy( this ); } update() { const video = this.image; const hasVideoFrameCallback = "requestVideoFrameCallback" in video; if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } class FramebufferTexture extends Texture { constructor( width, height ) { super( { width, height } ); this.isFramebufferTexture = true; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } class CompressedTexture extends Texture { constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isCompressedTexture = true; this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } class CompressedArrayTexture extends CompressedTexture { constructor( mipmaps, width, height, depth, format, type ) { super( mipmaps, width, height, format, type ); this.isCompressedArrayTexture = true; this.image.depth = depth; this.wrapR = ClampToEdgeWrapping; } } class CompressedCubeTexture extends CompressedTexture { constructor( images, format, type ) { super( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping ); this.isCompressedCubeTexture = true; this.isCubeTexture = true; this.image = images; } } class CanvasTexture extends Texture { constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isCanvasTexture = true; this.needsUpdate = true; } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); } // Get sequence of points using getPoint( t ) getPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; } // Get list of cumulative segment lengths getLengths( divisions = this.arcLengthDivisions ) { if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint( 0 ); let sum = 0; cache.push( 0 ); for ( let p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping( u, distance ) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[ i ]; const lengthAfter = arcLengths[ i + 1 ]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t const t = ( i + segmentFraction ) / ( il - 1 ); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent( t, optionalTarget ) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; const pt1 = this.getPoint( t1 ); const pt2 = this.getPoint( t2 ); const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); tangent.copy( pt2 ).sub( pt1 ).normalize(); return tangent; } getTangentAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getTangent( t, optionalTarget ); } computeFrenetFrames( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for ( let i = 0; i <= segments; i ++ ) { const u = i / segments; tangents[ i ] = this.getTangentAt( u, new Vector3() ); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs( tangents[ 0 ].x ); const ty = Math.abs( tangents[ 0 ].y ); const tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( let i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( let i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { super(); this.isEllipseCurve = true; this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint( t, optionalTarget ) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos( angle ); let y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { const cos = Math.cos( this.aRotation ); const sin = Math.sin( this.aRotation ); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set( x, y ); } copy( source ) { super.copy( source ); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON( json ) { super.fromJSON( json ); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } class ArcCurve extends EllipseCurve { constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); this.isArcCurve = true; this.type = "ArcCurve"; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5 ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const points = this.points; const l = points.length; const p = ( l - ( this.closed ? 0 : 1 ) ) * t; let intPoint = Math.floor( p ); let weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } const p1 = points[ intPoint % l ]; const p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom( t, p0, p1, p2, p3 ) { const v0 = ( p2 - p0 ) * 0.5; const v1 = ( p3 - p1 ) * 0.5; const t2 = t * t; const t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } // function QuadraticBezierP0( t, p ) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1( t, p ) { return 2 * ( 1 - t ) * t * p; } function QuadraticBezierP2( t, p ) { return t * t * p; } function QuadraticBezier( t, p0, p1, p2 ) { return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + QuadraticBezierP2( t, p2 ); } // function CubicBezierP0( t, p ) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + CubicBezierP3( t, p3 ); } class CubicBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { super(); this.isCubicBezierCurve = true; this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; } } class CubicBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { super(); this.isCubicBezierCurve3 = true; this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; } } class LineCurve extends Curve { constructor( v1 = new Vector2(), v2 = new Vector2() ) { super(); this.isLineCurve = true; this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); } getTangent( t, optionalTarget = new Vector2() ) { return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); } getTangentAt( u, optionalTarget ) { return this.getTangent( u, optionalTarget ); } copy( source ) { super.copy( source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class LineCurve3 extends Curve { constructor( v1 = new Vector3(), v2 = new Vector3() ) { super(); this.isLineCurve3 = true; this.type = "LineCurve3"; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); } getTangent( t, optionalTarget = new Vector3() ) { return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); } getTangentAt( u, optionalTarget ) { return this.getTangent( u, optionalTarget ); } copy( source ) { super.copy( source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class QuadraticBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { super(); this.isQuadraticBezierCurve = true; this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class QuadraticBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { super(); this.isQuadraticBezierCurve3 = true; this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ), QuadraticBezier( t, v0.z, v1.z, v2.z ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class SplineCurve extends Curve { constructor( points = [] ) { super(); this.isSplineCurve = true; this.type = "SplineCurve"; this.points = points; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const points = this.points; const p = ( points.length - 1 ) * t; const intPoint = Math.floor( p ); const weight = p - intPoint; const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; const p1 = points[ intPoint ]; const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; point.set( CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector2().fromArray( point ) ); } return this; } } var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add( curve ) { this.curves.push( curve ); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[ 0 ].getPoint( 0 ); const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint( t, optionalTarget ) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { const diff = curveLengths[ i ] - d; const curve = this.curves[ i ]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u, optionalTarget ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; } copy( source ) { super.copy( source ); this.curves = []; for ( let i = 0, l = source.curves.length; i < l; i ++ ) { const curve = source.curves[ i ]; this.curves.push( curve.clone() ); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for ( let i = 0, l = this.curves.length; i < l; i ++ ) { const curve = this.curves[ i ]; data.curves.push( curve.toJSON() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.autoClose = json.autoClose; this.curves = []; for ( let i = 0, l = json.curves.length; i < l; i ++ ) { const curve = json.curves[ i ]; this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } return this; } } class Path extends CurvePath { constructor( points ) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if ( points ) { this.setFromPoints( points ); } } setFromPoints( points ) { this.moveTo( points[ 0 ].x, points[ 0 ].y ); for ( let i = 1, l = points.length; i < l; i ++ ) { this.lineTo( points[ i ].x, points[ i ].y ); } return this; } moveTo( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? return this; } lineTo( x, y ) { const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); return this; } quadraticCurveTo( aCPx, aCPy, aX, aY ) { const curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); return this; } bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { const curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); return this; } splineThru( pts /*Array of Vector*/ ) { const npts = [ this.currentPoint.clone() ].concat( pts ); const curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); return this; } arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); return this; } absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); return this; } ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); return this; } absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); const lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); return this; } copy( source ) { super.copy( source ); this.currentPoint.copy( source.currentPoint ); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.currentPoint.fromArray( json.currentPoint ); return this; } } class LatheGeometry extends BufferGeometry { constructor( points = [ new Vector2( 0, - 0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp( phiLength, 0, Math.PI * 2 ); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { switch ( j ) { case 0: // special handling for 1st vertex on path dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; prevNormal.copy( normal ); normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); break; case ( points.length - 1 ): // special handling for last Vertex on path initNormals.push( prevNormal.x, prevNormal.y, prevNormal.z ); break; default: // default handling for all vertices in between dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; curNormal.copy( normal ); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); prevNormal.copy( curNormal ); } } // generate vertices, uvs and normals for ( let i = 0; i <= segments; i ++ ) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin( phi ); const cos = Math.cos( phi ); for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); // normal const x = initNormals[ 3 * j + 0 ] * sin; const y = initNormals[ 3 * j + 1 ]; const z = initNormals[ 3 * j + 0 ] * cos; normals.push( x, y, z ); } } // indices for ( let i = 0; i < segments; i ++ ) { for ( let j = 0; j < ( points.length - 1 ); j ++ ) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push( a, b, d ); indices.push( c, d, b ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength ); } } class CapsuleGeometry extends LatheGeometry { constructor( radius = 1, length = 1, capSegments = 4, radialSegments = 8 ) { const path = new Path(); path.absarc( 0, - length / 2, radius, Math.PI * 1.5, 0 ); path.absarc( 0, length / 2, radius, 0, Math.PI * 0.5 ); super( path.getPoints( capSegments ), radialSegments ); this.type = "CapsuleGeometry"; this.parameters = { radius: radius, length: length, capSegments: capSegments, radialSegments: radialSegments, }; } static fromJSON( data ) { return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments ); } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max( 3, segments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( let i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); } } class CylinderGeometry extends BufferGeometry { constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor( radialSegments ); heightSegments = Math.floor( heightSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( let y = 0; y <= heightSegments; y ++ ) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin( theta ); const cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { for ( let y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices const a = indexArray[ y ][ x ]; const b = indexArray[ y + 1 ][ x ]; const c = indexArray[ y + 1 ][ x + 1 ]; const d = indexArray[ y ][ x + 1 ]; // faces indices.push( a, b, d ); indices.push( b, c, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = ( top === true ) ? radiusTop : radiusBottom; const sign = ( top === true ) ? 1 : - 1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( let x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, halfHeight * sign, 0 ); // normal normals.push( 0, sign, 0 ); // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos( theta ); const sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); } } class ConeGeometry extends CylinderGeometry { constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON( data ) { return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); } } class PolyhedronGeometry extends BufferGeometry { constructor( vertices = [], indices = [], radius = 1, detail = 0 ) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius applyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute( "position", new Float32BufferAttribute( vertexBuffer, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvBuffer, 2 ) ); if ( detail === 0 ) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide( detail ) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivision with the given detail value for ( let i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for ( let i = 0; i <= cols; i ++ ) { v[ i ] = []; const aj = a.clone().lerp( c, i / cols ); const bj = b.clone().lerp( c, i / cols ); const rows = cols - i; for ( let j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( let i = 0; i < cols; i ++ ) { for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { const k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function applyRadius( radius ) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( let i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for ( let i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; const u = azimuth( vertex ) / 2 / Math.PI + 0.5; const v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( let i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face const x0 = uvBuffer[ i + 0 ]; const x1 = uvBuffer[ i + 2 ]; const x2 = uvBuffer[ i + 4 ]; const max = Math.max( x0, x1, x2 ); const min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { const stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); const azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details ); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const t = ( 1 + Math.sqrt( 5 ) ) / 2; const r = 1 / t; const vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; const indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; super( vertices, indices, radius, detail ); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new DodecahedronGeometry( data.radius, data.detail ); } } const _v0 = /*@__PURE__*/ new Vector3(); const _v1$1 = /*@__PURE__*/ new Vector3(); const _normal = /*@__PURE__*/ new Vector3(); const _triangle = /*@__PURE__*/ new Triangle(); class EdgesGeometry extends BufferGeometry { constructor( geometry = null, thresholdAngle = 1 ) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if ( geometry !== null ) { const precisionPoints = 4; const precision = Math.pow( 10, precisionPoints ); const thresholdDot = Math.cos( DEG2RAD * thresholdAngle ); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute( "position" ); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [ 0, 0, 0 ]; const vertKeys = [ "a", "b", "c" ]; const hashes = new Array( 3 ); const edgeData = {}; const vertices = []; for ( let i = 0; i < indexCount; i += 3 ) { if ( indexAttr ) { indexArr[ 0 ] = indexAttr.getX( i ); indexArr[ 1 ] = indexAttr.getX( i + 1 ); indexArr[ 2 ] = indexAttr.getX( i + 2 ); } else { indexArr[ 0 ] = i; indexArr[ 1 ] = i + 1; indexArr[ 2 ] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute( positionAttr, indexArr[ 0 ] ); b.fromBufferAttribute( positionAttr, indexArr[ 1 ] ); c.fromBufferAttribute( positionAttr, indexArr[ 2 ] ); _triangle.getNormal( _normal ); // create hashes for the edge from the vertices hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`; hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`; hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`; // skip degenerate triangles if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) { continue; } // iterate over every edge for ( let j = 0; j < 3; j ++ ) { // get the first and next vertex making up the edge const jNext = ( j + 1 ) % 3; const vecHash0 = hashes[ j ]; const vecHash1 = hashes[ jNext ]; const v0 = _triangle[ vertKeys[ j ] ]; const v1 = _triangle[ vertKeys[ jNext ] ]; const hash = `${ vecHash0 }_${ vecHash1 }`; const reverseHash = `${ vecHash1 }_${ vecHash0 }`; if ( reverseHash in edgeData && edgeData[ reverseHash ] ) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) { vertices.push( v0.x, v0.y, v0.z ); vertices.push( v1.x, v1.y, v1.z ); } edgeData[ reverseHash ] = null; } else if ( ! ( hash in edgeData ) ) { // if we"ve already got an edge here then skip adding a new one edgeData[ hash ] = { index0: indexArr[ j ], index1: indexArr[ jNext ], normal: _normal.clone(), }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for ( const key in edgeData ) { if ( edgeData[ key ] ) { const { index0, index1 } = edgeData[ key ]; _v0.fromBufferAttribute( positionAttr, index0 ); _v1$1.fromBufferAttribute( positionAttr, index1 ); vertices.push( _v0.x, _v0.y, _v0.z ); vertices.push( _v1$1.x, _v1$1.y, _v1$1.z ); } } this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } } class Shape extends Path { constructor( points ) { super( points ); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles( divisions ) { const holesPts = []; for ( let i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; } copy( source ) { super.copy( source ); this.holes = []; for ( let i = 0, l = source.holes.length; i < l; i ++ ) { const hole = source.holes[ i ]; this.holes.push( hole.clone() ); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for ( let i = 0, l = this.holes.length; i < l; i ++ ) { const hole = this.holes[ i ]; data.holes.push( hole.toJSON() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.uuid = json.uuid; this.holes = []; for ( let i = 0, l = json.holes.length; i < l; i ++ ) { const hole = json.holes[ i ]; this.holes.push( new Path().fromJSON( hole ) ); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.4) */ const Earcut = { triangulate: function ( data, holeIndices, dim = 2 ) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; let outerNode = linkedList( data, 0, outerLen, dim, true ); const triangles = []; if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if ( data.length > 80 * dim ) { minX = maxX = data[ 0 ]; minY = maxY = data[ 1 ]; for ( let i = dim; i < outerLen; i += dim ) { x = data[ i ]; y = data[ i + 1 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max( maxX - minX, maxY - minY ); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked( outerNode, triangles, dim, minX, minY, invSize, 0 ); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList( data, start, end, dim, clockwise ) { let i, last; if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } else { for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } if ( last && equals( last, last.next ) ) { removeNode( last ); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints( start, end ) { if ( ! start ) return start; if ( ! end ) end = start; let p = start, again; do { again = false; if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { removeNode( p ); p = end = p.prev; if ( p === p.next ) break; again = true; } else { p = p.next; } } while ( again || p !== end ); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { if ( ! ear ) return; // interlink polygon nodes in z-order if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); let stop = ear, prev, next; // iterate through ears, slicing them one by one while ( ear.prev !== ear.next ) { prev = ear.prev; next = ear.next; if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { // cut off the triangle triangles.push( prev.i / dim | 0 ); triangles.push( ear.i / dim | 0 ); triangles.push( next.i / dim | 0 ); removeNode( ear ); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if ( ear === stop ) { // try filtering points and slicing again if ( ! pass ) { earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); // if this didn"t work, try curing all small self-intersections locally } else if ( pass === 1 ) { ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { splitEarcut( ear, triangles, dim, minX, minY, invSize ); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar( ear ) { const a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); let p = c.next; while ( p !== a ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.next; } return true; } function isEarHashed( ear, minX, minY, invSize ) { const a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); // z-order range for the current triangle bbox; const minZ = zOrder( x0, y0, minX, minY, invSize ), maxZ = zOrder( x1, y1, minX, minY, invSize ); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while ( p && p.z >= minZ && n && n.z <= maxZ ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while ( p && p.z >= minZ ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; } // look for remaining points in increasing z-order while ( n && n.z <= maxZ ) { if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections( start, triangles, dim ) { let p = start; do { const a = p.prev, b = p.next.next; if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { triangles.push( a.i / dim | 0 ); triangles.push( p.i / dim | 0 ); triangles.push( b.i / dim | 0 ); // remove two nodes involved removeNode( p ); removeNode( p.next ); p = start = b; } p = p.next; } while ( p !== start ); return filterPoints( p ); } // try splitting polygon into two and triangulate them independently function splitEarcut( start, triangles, dim, minX, minY, invSize ) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while ( b !== a.prev ) { if ( a.i !== b.i && isValidDiagonal( a, b ) ) { // split the polygon in two by the diagonal let c = splitPolygon( a, b ); // filter colinear points around the cuts a = filterPoints( a, a.next ); c = filterPoints( c, c.next ); // run earcut on each half earcutLinked( a, triangles, dim, minX, minY, invSize, 0 ); earcutLinked( c, triangles, dim, minX, minY, invSize, 0 ); return; } b = b.next; } a = a.next; } while ( a !== start ); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles( data, holeIndices, outerNode, dim ) { const queue = []; let i, len, start, end, list; for ( i = 0, len = holeIndices.length; i < len; i ++ ) { start = holeIndices[ i ] * dim; end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; list = linkedList( data, start, end, dim, false ); if ( list === list.next ) list.steiner = true; queue.push( getLeftmost( list ) ); } queue.sort( compareX ); // process holes from left to right for ( i = 0; i < queue.length; i ++ ) { outerNode = eliminateHole( queue[ i ], outerNode ); } return outerNode; } function compareX( a, b ) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole( hole, outerNode ) { const bridge = findHoleBridge( hole, outerNode ); if ( ! bridge ) { return outerNode; } const bridgeReverse = splitPolygon( bridge, hole ); // filter collinear points around the cuts filterPoints( bridgeReverse, bridgeReverse.next ); return filterPoints( bridge, bridge.next ); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge( hole, outerNode ) { let p = outerNode, qx = - Infinity, m; const hx = hole.x, hy = hole.y; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); if ( x <= hx && x > qx ) { qx = x; m = p.x < p.next.x ? p : p.next; if ( x === hx ) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while ( p !== outerNode ); if ( ! m ) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { m = p; tanMin = tan; } } p = p.next; } while ( p !== stop ); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector( m, p ) { return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; } // interlink polygon nodes in z-order function indexCurve( start, minX, minY, invSize ) { let p = start; do { if ( p.z === 0 ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while ( p !== start ); p.prevZ.nextZ = null; p.prevZ = null; sortLinked( p ); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked( list ) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while ( p ) { numMerges ++; q = p; pSize = 0; for ( i = 0; i < inSize; i ++ ) { pSize ++; q = q.nextZ; if ( ! q ) break; } qSize = inSize; while ( pSize > 0 || ( qSize > 0 && q ) ) { if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { e = p; p = p.nextZ; pSize --; } else { e = q; q = q.nextZ; qSize --; } if ( tail ) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while ( numMerges > 1 ); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder( x, y, minX, minY, invSize ) { // coords are transformed into non-negative 15-bit integer range x = ( x - minX ) * invSize | 0; y = ( y - minY ) * invSize | 0; x = ( x | ( x << 8 ) ) & 0x00FF00FF; x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; x = ( x | ( x << 2 ) ) & 0x33333333; x = ( x | ( x << 1 ) ) & 0x55555555; y = ( y | ( y << 8 ) ) & 0x00FF00FF; y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; y = ( y | ( y << 2 ) ) & 0x33333333; y = ( y | ( y << 1 ) ) & 0x55555555; return x | ( y << 1 ); } // find the leftmost node of a polygon ring function getLeftmost( start ) { let p = start, leftmost = start; do { if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; p = p.next; } while ( p !== start ); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { return ( cx - px ) * ( ay - py ) >= ( ax - px ) * ( cy - py ) && ( ax - px ) * ( by - py ) >= ( bx - px ) * ( ay - py ) && ( bx - px ) * ( cy - py ) >= ( cx - px ) * ( by - py ); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal( a, b ) { return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones"t intersect other edges ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case } // signed area of a triangle function area( p, q, r ) { return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); } // check if two points are equal function equals( p1, p2 ) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects( p1, q1, p2, q2 ) { const o1 = sign( area( p1, q1, p2 ) ); const o2 = sign( area( p1, q1, q2 ) ); const o3 = sign( area( p2, q2, p1 ) ); const o4 = sign( area( p2, q2, q1 ) ); if ( o1 !== o2 && o3 !== o4 ) return true; // general case if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment( p, q, r ) { return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); } function sign( num ) { return num > 0 ? 1 : num < 0 ? - 1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon( a, b ) { let p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects( p, p.next, a, b ) ) return true; p = p.next; } while ( p !== a ); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside( a, b ) { return area( a.prev, a, a.next ) < 0 ? area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside( a, b ) { let p = a, inside = false; const px = ( a.x + b.x ) / 2, py = ( a.y + b.y ) / 2; do { if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) inside = ! inside; p = p.next; } while ( p !== a ); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon( a, b ) { const a2 = new Node( a.i, a.x, a.y ), b2 = new Node( b.i, b.x, b.y ), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode( i, x, y, last ) { const p = new Node( i, x, y ); if ( ! last ) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode( p ) { p.next.prev = p.prev; p.prev.next = p.next; if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; } function Node( i, x, y ) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea( data, start, end, dim ) { let sum = 0; for ( let i = start, j = end - dim; i < end; i += dim ) { sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area( contour ) { const n = contour.length; let a = 0.0; for ( let p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; } static isClockWise( pts ) { return ShapeUtils.area( pts ) < 0; } static triangulateShape( contour, holes ) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts( contour ); addContour( vertices, contour ); // let holeIndex = contour.length; holes.forEach( removeDupEndPts ); for ( let i = 0; i < holes.length; i ++ ) { holeIndices.push( holeIndex ); holeIndex += holes[ i ].length; addContour( vertices, holes[ i ] ); } // const triangles = Earcut.triangulate( vertices, holeIndices ); // for ( let i = 0; i < triangles.length; i += 3 ) { faces.push( triangles.slice( i, i + 3 ) ); } return faces; } } function removeDupEndPts( points ) { const l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } function addContour( vertices, contour ) { for ( let i = 0; i < contour.length; i ++ ) { vertices.push( contour[ i ].x ); vertices.push( contour[ i ].y ); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; const scope = this; const verticesArray = []; const uvArray = []; for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; addShape( shape ); } // build geometry this.setAttribute( "position", new Float32BufferAttribute( verticesArray, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvArray, 2 ) ); this.computeVertexNormals(); // functions function addShape( shape ) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; const depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints( curveSegments ); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return pt.clone().addScaledVector( vec, size ); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt( v_prev_lensq ); const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } const contourMovements = []; for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = []; for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( let b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos( t * Math.PI / 2 ); const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape for ( let i = 0, il = contour.length; i < il; i ++ ) { const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( let i = 0, il = ahole.length; i < il; i ++ ) { const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for ( let i = 0; i < vlen; i ++ ) { const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices for ( let s = 1; s <= steps; s ++ ) { for ( let i = 0; i < vlen; i ++ ) { const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, depth / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( let b = bevelSegments - 1; b >= 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos( t * Math.PI / 2 ); const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape for ( let i = 0, il = contour.length; i < il; i ++ ) { const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, depth + z ); } // expand holes for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( let i = 0, il = ahole.length; i < il; i ++ ) { const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, depth + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if ( bevelEnabled ) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } scope.addGroup( start, verticesArray.length / 3 - start, 0 ); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } scope.addGroup( start, verticesArray.length / 3 - start, 1 ); } function sidewalls( contour, layeroffset ) { let i = contour.length; while ( -- i >= 0 ) { const j = i; let k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { const slen1 = vlen * s; const slen2 = vlen * ( s + 1 ); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d ); } } } function v( x, y, z ) { placeholder.push( x ); placeholder.push( y ); placeholder.push( z ); } function f3( a, b, c ) { addVertex( a ); addVertex( b ); addVertex( c ); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); } function f4( a, b, c, d ) { addVertex( a ); addVertex( b ); addVertex( d ); addVertex( b ); addVertex( c ); addVertex( d ); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 3 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); addUV( uvs[ 3 ] ); } function addVertex( index ) { verticesArray.push( placeholder[ index * 3 + 0 ] ); verticesArray.push( placeholder[ index * 3 + 1 ] ); verticesArray.push( placeholder[ index * 3 + 2 ] ); } function addUV( vector2 ) { uvArray.push( vector2.x ); uvArray.push( vector2.y ); } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1( shapes, options, data ); } static fromJSON( data, shapes ) { const geometryShapes = []; for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { const shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } const extrudePath = data.options.extrudePath; if ( extrudePath !== undefined ) { data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); } return new ExtrudeGeometry( geometryShapes, data.options ); } } const WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { const a_x = vertices[ indexA * 3 ]; const a_y = vertices[ indexA * 3 + 1 ]; const b_x = vertices[ indexB * 3 ]; const b_y = vertices[ indexB * 3 + 1 ]; const c_x = vertices[ indexC * 3 ]; const c_y = vertices[ indexC * 3 + 1 ]; return [ new Vector2( a_x, a_y ), new Vector2( b_x, b_y ), new Vector2( c_x, c_y ) ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { const a_x = vertices[ indexA * 3 ]; const a_y = vertices[ indexA * 3 + 1 ]; const a_z = vertices[ indexA * 3 + 2 ]; const b_x = vertices[ indexB * 3 ]; const b_y = vertices[ indexB * 3 + 1 ]; const b_z = vertices[ indexB * 3 + 2 ]; const c_x = vertices[ indexC * 3 ]; const c_y = vertices[ indexC * 3 + 1 ]; const c_z = vertices[ indexC * 3 + 2 ]; const d_x = vertices[ indexD * 3 ]; const d_y = vertices[ indexD * 3 + 1 ]; const d_z = vertices[ indexD * 3 + 2 ]; if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { return [ new Vector2( a_x, 1 - a_z ), new Vector2( b_x, 1 - b_z ), new Vector2( c_x, 1 - c_z ), new Vector2( d_x, 1 - d_z ) ]; } else { return [ new Vector2( a_y, 1 - a_z ), new Vector2( b_y, 1 - b_z ), new Vector2( c_y, 1 - c_z ), new Vector2( d_y, 1 - d_z ) ]; } } }; function toJSON$1( shapes, options, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } data.options = Object.assign( {}, options ); if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const t = ( 1 + Math.sqrt( 5 ) ) / 2; const vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; const indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; super( vertices, indices, radius, detail ); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new IcosahedronGeometry( data.radius, data.detail ); } } class OctahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; const indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; super( vertices, indices, radius, detail ); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new OctahedronGeometry( data.radius, data.detail ); } } class RingGeometry extends BufferGeometry { constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max( 3, thetaSegments ); phiSegments = Math.max( 1, phiSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for ( let j = 0; j <= phiSegments; j ++ ) { for ( let i = 0; i <= thetaSegments; i ++ ) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // increase the radius for next row of vertices radius += radiusStep; } // indices for ( let j = 0; j < phiSegments; j ++ ) { const thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( let i = 0; i < thetaSegments; i ++ ) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); } } class ShapeGeometry extends BufferGeometry { constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( let i = 0; i < shapes.length; i ++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { const indexOffset = vertices.length / 3; const points = shape.extractPoints( curveSegments ); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); } for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { const shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { const shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { const vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // indices for ( let i = 0, l = faces.length; i < l; i ++ ) { const face = faces[ i ]; const a = face[ 0 ] + indexOffset; const b = face[ 1 ] + indexOffset; const c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON( shapes, data ); } static fromJSON( data, shapes ) { const geometryShapes = []; for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { const shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } return new ShapeGeometry( geometryShapes, data.curveSegments ); } } function toJSON( shapes, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } return data; } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max( 3, Math.floor( widthSegments ) ); heightSegments = Math.max( 2, Math.floor( heightSegments ) ); const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for ( let iy = 0; iy <= heightSegments; iy ++ ) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if ( iy === 0 && thetaStart === 0 ) { uOffset = 0.5 / widthSegments; } else if ( iy === heightSegments && thetaEnd === Math.PI ) { uOffset = - 0.5 / widthSegments; } for ( let ix = 0; ix <= widthSegments; ix ++ ) { const u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.copy( vertex ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u + uOffset, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( let iy = 0; iy < heightSegments; iy ++ ) { for ( let ix = 0; ix < widthSegments; ix ++ ) { const a = grid[ iy ][ ix + 1 ]; const b = grid[ iy ][ ix ]; const c = grid[ iy + 1 ][ ix ]; const d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; const indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; super( vertices, indices, radius, detail ); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new TetrahedronGeometry( data.radius, data.detail ); } } class TorusGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2 ) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor( radialSegments ); tubularSegments = Math.floor( tubularSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for ( let j = 0; j <= radialSegments; j ++ ) { for ( let i = 0; i <= tubularSegments; i ++ ) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); normal.subVectors( vertex, center ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( let j = 1; j <= radialSegments; j ++ ) { for ( let i = 1; i <= tubularSegments; i ++ ) { // indices const a = ( tubularSegments + 1 ) * j + i - 1; const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; const d = ( tubularSegments + 1 ) * j + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); } } class TorusKnotGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor( tubularSegments ); radialSegments = Math.floor( radialSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for ( let i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segment const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for ( let j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = - tube * Math.cos( v ); const cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( let j = 1; j <= tubularSegments; j ++ ) { for ( let i = 1; i <= radialSegments; i ++ ) { // indices const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); const b = ( radialSegments + 1 ) * j + ( i - 1 ); const c = ( radialSegments + 1 ) * j + i; const d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { const cu = Math.cos( u ); const su = Math.sin( u ); const quOverP = q / p * u; const cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); } } class TubeGeometry extends BufferGeometry { constructor( path = new QuadraticBezierCurve3( new Vector3( - 1, - 1, 0 ), new Vector3( - 1, 1, 0 ), new Vector3( 1, 1, 0 ) ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( let i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt( i / tubularSegments, P ); // retrieve corresponding normal and binormal const N = frames.normals[ i ]; const B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( let j = 0; j <= radialSegments; j ++ ) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin( v ); const cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( let j = 1; j <= tubularSegments; j ++ ) { for ( let i = 1; i <= radialSegments; i ++ ) { const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); const b = ( radialSegments + 1 ) * j + ( i - 1 ); const c = ( radialSegments + 1 ) * j + i; const d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( let i = 0; i <= tubularSegments; i ++ ) { for ( let j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON( data ) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry( new Curves[ data.path.type ]().fromJSON( data.path ), data.tubularSegments, data.radius, data.radialSegments, data.closed ); } } class WireframeGeometry extends BufferGeometry { constructor( geometry = null ) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if ( geometry !== null ) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if ( geometry.index !== null ) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; } // create a data structure that contains all edges without duplicates for ( let o = 0, ol = groups.length; o < ol; ++ o ) { const group = groups[ o ]; const groupStart = group.start; const groupCount = group.count; for ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) { for ( let j = 0; j < 3; j ++ ) { const index1 = indices.getX( i + j ); const index2 = indices.getX( i + ( j + 1 ) % 3 ); start.fromBufferAttribute( position, index1 ); end.fromBufferAttribute( position, index2 ); if ( isUniqueEdge( start, end, edges ) === true ) { vertices.push( start.x, start.y, start.z ); vertices.push( end.x, end.y, end.z ); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { for ( let j = 0; j < 3; j ++ ) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + ( ( j + 1 ) % 3 ); start.fromBufferAttribute( position, index1 ); end.fromBufferAttribute( position, index2 ); if ( isUniqueEdge( start, end, edges ) === true ) { vertices.push( start.x, start.y, start.z ); vertices.push( end.x, end.y, end.z ); } } } } // build geometry this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } } function isUniqueEdge( start, end, edges ) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) { return false; } else { edges.add( hash1 ); edges.add( hash2 ); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, CapsuleGeometry: CapsuleGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); class ShadowMaterial extends Material { constructor( parameters ) { super(); this.isShadowMaterial = true; this.type = "ShadowMaterial"; this.color = new Color( 0x000000 ); this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.fog = source.fog; return this; } } class RawShaderMaterial extends ShaderMaterial { constructor( parameters ) { super( parameters ); this.isRawShaderMaterial = true; this.type = "RawShaderMaterial"; } } class MeshStandardMaterial extends Material { constructor( parameters ) { super(); this.isMeshStandardMaterial = true; this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color( 0xffffff ); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.defines = { "STANDARD": "" }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshPhysicalMaterial extends MeshStandardMaterial { constructor( parameters ) { super(); this.isMeshPhysicalMaterial = true; this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.anisotropyRotation = 0; this.anisotropyMap = null; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2( 1, 1 ); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty( this, "reflectivity", { get: function () { return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); }, set: function ( reflectivity ) { this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); } } ); this.iridescenceMap = null; this.iridescenceIOR = 1.3; this.iridescenceThicknessRange = [ 100, 400 ]; this.iridescenceThicknessMap = null; this.sheenColor = new Color( 0x000000 ); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = Infinity; this.attenuationColor = new Color( 1, 1, 1 ); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color( 1, 1, 1 ); this.specularColorMap = null; this._anisotropy = 0; this._clearcoat = 0; this._iridescence = 0; this._sheen = 0.0; this._transmission = 0; this.setValues( parameters ); } get anisotropy() { return this._anisotropy; } set anisotropy( value ) { if ( this._anisotropy > 0 !== value > 0 ) { this.version ++; } this._anisotropy = value; } get clearcoat() { return this._clearcoat; } set clearcoat( value ) { if ( this._clearcoat > 0 !== value > 0 ) { this.version ++; } this._clearcoat = value; } get iridescence() { return this._iridescence; } set iridescence( value ) { if ( this._iridescence > 0 !== value > 0 ) { this.version ++; } this._iridescence = value; } get sheen() { return this._sheen; } set sheen( value ) { if ( this._sheen > 0 !== value > 0 ) { this.version ++; } this._sheen = value; } get transmission() { return this._transmission; } set transmission( value ) { if ( this._transmission > 0 !== value > 0 ) { this.version ++; } this._transmission = value; } copy( source ) { super.copy( source ); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.anisotropy = source.anisotropy; this.anisotropyRotation = source.anisotropyRotation; this.anisotropyMap = source.anisotropyMap; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); this.ior = source.ior; this.iridescence = source.iridescence; this.iridescenceMap = source.iridescenceMap; this.iridescenceIOR = source.iridescenceIOR; this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; this.sheen = source.sheen; this.sheenColor.copy( source.sheenColor ); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy( source.attenuationColor ); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy( source.specularColor ); this.specularColorMap = source.specularColorMap; return this; } } class MeshPhongMaterial extends Material { constructor( parameters ) { super(); this.isMeshPhongMaterial = true; this.type = "MeshPhongMaterial"; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshToonMaterial extends Material { constructor( parameters ) { super(); this.isMeshToonMaterial = true; this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } class MeshNormalMaterial extends Material { constructor( parameters ) { super(); this.isMeshNormalMaterial = true; this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.flatShading = false; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } class MeshLambertMaterial extends Material { constructor( parameters ) { super(); this.isMeshLambertMaterial = true; this.type = "MeshLambertMaterial"; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshMatcapMaterial extends Material { constructor( parameters ) { super(); this.isMeshMatcapMaterial = true; this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color( 0xffffff ); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.defines = { "MATCAP": "" }; this.color.copy( source.color ); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class LineDashedMaterial extends LineBasicMaterial { constructor( parameters ) { super(); this.isLineDashedMaterial = true; this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } // same as Array.prototype.slice, but also works on typed arrays function arraySlice( array, from, to ) { if ( isTypedArray( array ) ) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } return array.slice( from, to ); } // converts an array to a specific type function convertArray( array, type, forceClone ) { if ( ! array || // let "undefined" and "null" pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === "number" ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array } function isTypedArray( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); } // returns an array by which times and values can be sorted function getKeyframeOrder( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } const n = times.length; const result = new Array( n ); for ( let i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; } // uses the array previously returned by "getKeyframeOrder" to sort data function sortedArray( values, stride, order ) { const nValues = values.length; const result = new values.constructor( nValues ); for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { const srcOffset = order[ i ] * stride; for ( let j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; } // function for parsing AOS keyframe formats function flattenJSON( jsonKeys, times, values, valuePropertyName ) { let i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data let value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } function subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for ( let i = 0; i < clip.tracks.length; ++ i ) { const track = clip.tracks[ i ]; const valueSize = track.getValueSize(); const times = []; const values = []; for ( let j = 0; j < track.times.length; ++ j ) { const frame = track.times[ j ] * fps; if ( frame < startFrame || frame >= endFrame ) continue; times.push( track.times[ j ] ); for ( let k = 0; k < valueSize; ++ k ) { values.push( track.values[ j * valueSize + k ] ); } } if ( times.length === 0 ) continue; track.times = convertArray( times, track.times.constructor ); track.values = convertArray( values, track.values.constructor ); tracks.push( track ); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for ( let i = 0; i < clip.tracks.length; ++ i ) { if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { minStartTime = clip.tracks[ i ].times[ 0 ]; } } // shift all tracks such that clip begins at t=0 for ( let i = 0; i < clip.tracks.length; ++ i ) { clip.tracks[ i ].shift( - 1 * minStartTime ); } clip.resetDuration(); return clip; } function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { if ( fps <= 0 ) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for ( let i = 0; i < numTracks; ++ i ) { const referenceTrack = referenceClip.tracks[ i ]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if ( referenceTrackType === "bool" || referenceTrackType === "string" ) continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find( function ( track ) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; } ); if ( targetTrack === undefined ) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if ( referenceTime <= referenceTrack.times[ 0 ] ) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = arraySlice( referenceTrack.values, startIndex, endIndex ); } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = arraySlice( referenceTrack.values, startIndex, endIndex ); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate( referenceTime ); referenceValue = arraySlice( interpolant.resultBuffer, startIndex, endIndex ); } // Conjugate the quaternion if ( referenceTrackType === "quaternion" ) { const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); referenceQuat.toArray( referenceValue ); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for ( let j = 0; j < numTimes; ++ j ) { const valueStart = j * targetValueSize + targetOffset; if ( referenceTrackType === "quaternion" ) { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat( targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart ); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for ( let k = 0; k < valueEnd; ++ k ) { targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } const AnimationUtils = { arraySlice: arraySlice, convertArray: convertArray, isTypedArray: isTypedArray, getKeyframeOrder: getKeyframeOrder, sortedArray: sortedArray, flattenJSON: flattenJSON, subclip: subclip, makeClipAdditive: makeClipAdditive }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate( t ) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( let giveUpAt = i1 + 2; ; ) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_( i1 - 1 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? const t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( let giveUpAt = i1 - 2; ; ) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.copySampleValue_( 0 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { const mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.copySampleValue_( 0 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_( i1 - 1 ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_( index ) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( let i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; } // Template methods for derived classes: interpolate_( /* i1, t0, t, t1 */ ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer } intervalChanged_( /* i1, t0, t1 */ ) { // empty } } /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = - 0; this._offsetPrev = - 0; this._weightNext = - 0; this._offsetNext = - 0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_( i1, t0, t1 ) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = - wP * ppp + 2 * wP * pp - wP * p; const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for ( let i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } class LinearInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( let i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1 /*, t0, t, t1 */ ) { return this.copySampleValue_( i1 - 1 ); } } class KeyframeTrack { constructor( name, times, values, interpolation ) { if ( name === undefined ) throw new Error( "THREE.KeyframeTrack: track name is undefined" ); if ( times === undefined || times.length === 0 ) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name ); this.name = name; this.times = convertArray( times, this.TimeBufferType ); this.values = convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON( track ) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if ( trackType.toJSON !== this.toJSON ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": convertArray( track.times, Array ), "values": convertArray( track.values, Array ) }; const interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); } InterpolantFactoryMethodLinear( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); } InterpolantFactoryMethodSmooth( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); } setInterpolation( interpolation ) { let factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( "THREE.KeyframeTrack:", message ); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift( timeOffset ) { if ( timeOffset !== 0.0 ) { const times = this.times; for ( let i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale( timeScale ) { if ( timeScale !== 1.0 ) { const times = this.times; for ( let i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim( startTime, endTime ) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) { ++ from; } while ( to !== - 1 && times[ to ] > endTime ) { -- to; } ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) { to = Math.max( to, 1 ); from = to - 1; } const stride = this.getValueSize(); this.times = arraySlice( times, from, to ); this.values = arraySlice( this.values, from * stride, to * stride ); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "THREE.KeyframeTrack: Invalid value size in track.", this ); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "THREE.KeyframeTrack: Track is empty.", this ); valid = false; } let prevTime = null; for ( let i = 0; i !== nKeys; i ++ ) { const currTime = times[ i ]; if ( typeof currTime === "number" && isNaN( currTime ) ) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( isTypedArray( values ) ) { for ( let i = 0, n = values.length; i !== n; ++ i ) { const value = values[ i ]; if ( isNaN( value ) ) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value ); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = arraySlice( this.times ), values = arraySlice( this.values ), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for ( let i = 1; i < lastIndex; ++ i ) { let keep = false; const time = times[ i ]; const timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( let j = 0; j !== stride; ++ j ) { const value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; const readOffset = i * stride, writeOffset = writeIndex * stride; for ( let j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = arraySlice( times, 0, writeIndex ); this.values = arraySlice( values, 0, writeIndex * stride ); } else { this.times = times; this.values = values; } return this; } clone() { const times = arraySlice( this.times, 0 ); const values = arraySlice( this.values, 0 ); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack( this.name, times, values ); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = ( t - t0 ) / ( t1 - t0 ); let offset = i1 * stride; for ( let end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; class AnimationClip { constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } } static parse( json ) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); } const clip = new this( json.name, json.duration, tracks, json.blendMode ); clip.uuid = json.uuid; return clip; } static toJSON( clip ) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; } static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for ( let i = 0; i < numMorphTargets; i ++ ) { let times = []; let values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); const order = getKeyframeOrder( times ); times = sortedArray( times, 1, order ); values = sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[ i ].name + "]", times, values ).scale( 1.0 / fps ) ); } return new this( name, - 1, tracks ); } static findByName( objectOrClipArray, name ) { let clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( let i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; } static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { const morphTarget = morphTargets[ i ]; const parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { const name = parts[ 1 ]; let animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } const clips = []; for ( const name in animationToMorphTargets ) { clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; } // parse the animation.hierarchy format static parseAnimation( animation, bones ) { if ( ! animation ) { console.error( "THREE.AnimationClip: No animation in JSONLoader data." ); return null; } const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { const times = []; const values = []; flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || - 1; const hierarchyTracks = animation.hierarchy || []; for ( let h = 0; h < hierarchyTracks.length; h ++ ) { const animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets if ( animationKeys[ 0 ].morphTargets ) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for ( k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[ k ].morphTargets ) { for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( const morphTargetName in morphTargetNames ) { const times = []; const values = []; for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { const animationKey = animationKeys[ k ]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values ) ); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[ h ].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks ); } } if ( tracks.length === 0 ) { return null; } const clip = new this( clipName, duration, tracks, blendMode ); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for ( let i = 0, n = tracks.length; i !== n; ++ i ) { const track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; return this; } trim() { for ( let i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; } validate() { let valid = true; for ( let i = 0; i < this.tracks.length; i ++ ) { valid = valid && this.tracks[ i ].validate(); } return valid; } optimize() { for ( let i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } clone() { const tracks = []; for ( let i = 0; i < this.tracks.length; i ++ ) { tracks.push( this.tracks[ i ].clone() ); } return new this.constructor( this.name, this.duration, tracks, this.blendMode ); } toJSON() { return this.constructor.toJSON( this ); } } function getTrackTypeForValueTypeName( typeName ) { switch ( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "THREE.KeyframeTrack: Unsupported typeName: " + typeName ); } function parseKeyframeTrack( json ) { if ( json.type === undefined ) { throw new Error( "THREE.KeyframeTrack: track type undefined, can not parse" ); } const trackType = getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { const times = [], values = []; flattenJSON( json.keys, times, values, "value" ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } } const Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor( onLoad, onProgress, onError ) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; this.resolveURL = function ( url ) { if ( urlModifier ) { return urlModifier( url ); } return url; }; this.setURLModifier = function ( transform ) { urlModifier = transform; return this; }; this.addHandler = function ( regex, loader ) { handlers.push( regex, loader ); return this; }; this.removeHandler = function ( regex ) { const index = handlers.indexOf( regex ); if ( index !== - 1 ) { handlers.splice( index, 2 ); } return this; }; this.getHandler = function ( file ) { for ( let i = 0, l = handlers.length; i < l; i += 2 ) { const regex = handlers[ i ]; const loader = handlers[ i + 1 ]; if ( regex.global ) regex.lastIndex = 0; // see #17920 if ( regex.test( file ) ) { return loader; } } return null; }; } } const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); class Loader { constructor( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load( /* url, onLoad, onProgress, onError */ ) {} loadAsync( url, onProgress ) { const scope = this; return new Promise( function ( resolve, reject ) { scope.load( url, resolve, onProgress, reject ); } ); } parse( /* data */ ) {} setCrossOrigin( crossOrigin ) { this.crossOrigin = crossOrigin; return this; } setWithCredentials( value ) { this.withCredentials = value; return this; } setPath( path ) { this.path = path; return this; } setResourcePath( resourcePath ) { this.resourcePath = resourcePath; return this; } setRequestHeader( requestHeader ) { this.requestHeader = requestHeader; return this; } } Loader.DEFAULT_MATERIAL_NAME = "__DEFAULT"; const loading = {}; class HttpError extends Error { constructor( message, response ) { super( message ); this.response = response; } } class FileLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const cached = Cache.get( url ); if ( cached !== undefined ) { this.manager.itemStart( url ); setTimeout( () => { if ( onLoad ) onLoad( cached ); this.manager.itemEnd( url ); }, 0 ); return cached; } // Check if request is duplicate if ( loading[ url ] !== undefined ) { loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); return; } // Initialise array for duplicate requests loading[ url ] = []; loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError, } ); // create request const req = new Request( url, { headers: new Headers( this.requestHeader ), credentials: this.withCredentials ? "include" : "same-origin", // An abort controller could be added within a future PR } ); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch( req ) .then( response => { if ( response.status === 200 || response.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if ( response.status === 0 ) { console.warn( "THREE.FileLoader: HTTP Status 0 received." ); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if ( typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined ) { return response; } const callbacks = loading[ url ]; const reader = response.body.getReader(); // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get( "Content-Length" ) || response.headers.get( "X-File-Size" ); const total = contentLength ? parseInt( contentLength ) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream( { start( controller ) { readData(); function readData() { reader.read().then( ( { done, value } ) => { if ( done ) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent( "progress", { lengthComputable, loaded, total } ); for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onProgress ) callback.onProgress( event ); } controller.enqueue( value ); readData(); } } ); } } } ); return new Response( stream ); } else { throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response ); } } ) .then( response => { switch ( responseType ) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text() .then( text => { const parser = new DOMParser(); return parser.parseFromString( text, mimeType ); } ); case "json": return response.json(); default: if ( mimeType === undefined ) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec( mimeType ); const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; const decoder = new TextDecoder( label ); return response.arrayBuffer().then( ab => decoder.decode( ab ) ); } } } ) .then( data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add( url, data ); const callbacks = loading[ url ]; delete loading[ url ]; for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( data ); } } ) .catch( err => { // Abort errors and other errors are handled the same const callbacks = loading[ url ]; if ( callbacks === undefined ) { // When onLoad was called and url was deleted in `loading` this.manager.itemError( url ); throw err; } delete loading[ url ]; for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onError ) callback.onError( err ); } this.manager.itemError( url ); } ) .finally( () => { this.manager.itemEnd( url ); } ); this.manager.itemStart( url ); } setResponseType( value ) { this.responseType = value; return this; } setMimeType( value ) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const animations = []; for ( let i = 0; i < json.length; i ++ ) { const clip = AnimationClip.parse( json[ i ] ); animations.push( clip ); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( "arraybuffer" ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( scope.withCredentials ); let loaded = 0; function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { const texDatas = scope.parse( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { for ( let i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { const texDatas = scope.parse( buffer, true ); if ( texDatas.isCubemap ) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( let f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps: [] }; for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; } } class ImageLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const scope = this; const cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } const image = createElementNS( "img" ); function onImageLoad() { removeEventListeners(); Cache.add( url, this ); if ( onLoad ) onLoad( this ); scope.manager.itemEnd( url ); } function onImageError( event ) { removeEventListeners(); if ( onError ) onError( event ); scope.manager.itemError( url ); scope.manager.itemEnd( url ); } function removeEventListeners() { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); } image.addEventListener( "load", onImageLoad, false ); image.addEventListener( "error", onImageError, false ); if ( url.slice( 0, 5 ) !== "data:" ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart( url ); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( urls, onLoad, onProgress, onError ) { const texture = new CubeTexture(); texture.colorSpace = SRGBColorSpace; const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); let loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( let i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.setRequestHeader( this.requestHeader ); loader.setPath( this.path ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( buffer ) { let texData; try { texData = scope.parse( buffer ); } catch ( error ) { if ( onError !== undefined ) { onError( error ); } else { console.error( error ); return; } } if ( ! texData ) return onError(); // TODO: Remove this when all loaders properly throw errors if ( texData.image !== undefined ) { texture.image = texData.image; } else if ( texData.data !== undefined ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if ( texData.colorSpace !== undefined ) { texture.colorSpace = texData.colorSpace; } else if ( texData.encoding !== undefined ) { // @deprecated, r152 texture.encoding = texData.encoding; } if ( texData.flipY !== undefined ) { texture.flipY = texData.flipY; } if ( texData.format !== undefined ) { texture.format = texData.format; } if ( texData.type !== undefined ) { texture.type = texData.type; } if ( texData.mipmaps !== undefined ) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if ( texData.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } if ( texData.generateMipmaps !== undefined ) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } class TextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const texture = new Texture(); const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); loader.load( url, function ( image ) { texture.image = image; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; } } class Light extends Object3D { constructor( color, intensity = 1 ) { super(); this.isLight = true; this.type = "Light"; this.color = new Color( color ); this.intensity = intensity; } dispose() { // Empty here in base class; some subclasses override. } copy( source, recursive ) { super.copy( source, recursive ); this.color.copy( source.color ); this.intensity = source.intensity; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } class HemisphereLight extends Light { constructor( skyColor, groundColor, intensity ) { super( skyColor, intensity ); this.isHemisphereLight = true; this.type = "HemisphereLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } copy( source, recursive ) { super.copy( source, recursive ); this.groundColor.copy( source.groundColor ); return this; } } const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); const _lookTarget$1 = /*@__PURE__*/ new Vector3(); class LightShadow { constructor( camera ) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2( 1, 1 ); this._viewportCount = 1; this._viewports = [ new Vector4( 0, 0, 1, 1 ) ]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices( light ) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld$1 ); _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget$1 ); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( _projScreenMatrix$1 ); } getViewport( viewportIndex ) { return this._viewports[ viewportIndex ]; } getFrameExtents() { return this._frameExtents; } dispose() { if ( this.map ) { this.map.dispose(); } if ( this.mapPass ) { this.mapPass.dispose(); } } copy( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; } clone() { return new this.constructor().copy( this ); } toJSON() { const object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); this.isSpotLightShadow = true; this.focus = 1; } updateMatrices( light ) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices( light ); } copy( source ) { super.copy( source ); this.focus = source.focus; return this; } } class SpotLight extends Light { constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2 ) { super( color, intensity ); this.isSpotLight = true; this.type = "SpotLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; this.map = null; this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power( power ) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy( source, recursive ) { super.copy( source, recursive ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld = /*@__PURE__*/ new Vector3(); const _lookTarget = /*@__PURE__*/ new Vector3(); class PointLightShadow extends LightShadow { constructor() { super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); this.isPointLightShadow = true; this._frameExtents = new Vector2( 4, 2 ); this._viewportCount = 6; this._viewports = [ // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4( 2, 1, 1, 1 ), // negative X new Vector4( 0, 1, 1, 1 ), // positive Z new Vector4( 3, 1, 1, 1 ), // negative Z new Vector4( 1, 1, 1, 1 ), // positive Y new Vector4( 3, 0, 1, 1 ), // negative Y new Vector4( 1, 0, 1, 1 ) ]; this._cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; this._cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; } updateMatrices( light, viewportIndex = 0 ) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if ( far !== camera.far ) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); camera.position.copy( _lightPositionWorld ); _lookTarget.copy( camera.position ); _lookTarget.add( this._cubeDirections[ viewportIndex ] ); camera.up.copy( this._cubeUps[ viewportIndex ] ); camera.lookAt( _lookTarget ); camera.updateMatrixWorld(); shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); this._frustum.setFromProjectionMatrix( _projScreenMatrix ); } } class PointLight extends Light { constructor( color, intensity, distance = 0, decay = 2 ) { super( color, intensity ); this.isPointLight = true; this.type = "PointLight"; this.distance = distance; this.decay = decay; this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power( power ) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / ( 4 * Math.PI ); } dispose() { this.shadow.dispose(); } copy( source, recursive ) { super.copy( source, recursive ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } class DirectionalLightShadow extends LightShadow { constructor() { super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); this.isDirectionalLightShadow = true; } } class DirectionalLight extends Light { constructor( color, intensity ) { super( color, intensity ); this.isDirectionalLight = true; this.type = "DirectionalLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy( source ) { super.copy( source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } class AmbientLight extends Light { constructor( color, intensity ) { super( color, intensity ); this.isAmbientLight = true; this.type = "AmbientLight"; } } class RectAreaLight extends Light { constructor( color, intensity, width = 10, height = 10 ) { super( color, intensity ); this.isRectAreaLight = true; this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power( power ) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / ( this.width * this.height * Math.PI ); } copy( source ) { super.copy( source ); this.width = source.width; this.height = source.height; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.width = this.width; data.object.height = this.height; return data; } } /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.isSphericalHarmonics3 = true; this.coefficients = []; for ( let i = 0; i < 9; i ++ ) { this.coefficients.push( new Vector3() ); } } set( coefficients ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].copy( coefficients[ i ] ); } return this; } zero() { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].set( 0, 0, 0 ); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt( normal, target ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); // band 1 target.addScaledVector( coeff[ 1 ], 0.488603 * y ); target.addScaledVector( coeff[ 2 ], 0.488603 * z ); target.addScaledVector( coeff[ 3 ], 0.488603 * x ); // band 2 target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt( normal, target ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 // band 1 target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); // band 2 target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 return target; } add( sh ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].add( sh.coefficients[ i ] ); } return this; } addScaledSH( sh, s ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); } return this; } scale( s ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].multiplyScalar( s ); } return this; } lerp( sh, alpha ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); } return this; } equals( sh ) { for ( let i = 0; i < 9; i ++ ) { if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { return false; } } return true; } copy( sh ) { return this.set( sh.coefficients ); } clone() { return new this.constructor().copy( this ); } fromArray( array, offset = 0 ) { const coefficients = this.coefficients; for ( let i = 0; i < 9; i ++ ) { coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); } return this; } toArray( array = [], offset = 0 ) { const coefficients = this.coefficients; for ( let i = 0; i < 9; i ++ ) { coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt( normal, shBasis ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[ 0 ] = 0.282095; // band 1 shBasis[ 1 ] = 0.488603 * y; shBasis[ 2 ] = 0.488603 * z; shBasis[ 3 ] = 0.488603 * x; // band 2 shBasis[ 4 ] = 1.092548 * x * y; shBasis[ 5 ] = 1.092548 * y * z; shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); shBasis[ 7 ] = 1.092548 * x * z; shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); } } class LightProbe extends Light { constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { super( undefined, intensity ); this.isLightProbe = true; this.sh = sh; } copy( source ) { super.copy( source ); this.sh.copy( source.sh ); return this; } fromJSON( json ) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray( json.sh ); return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.sh = this.sh.toArray(); return data; } } class MaterialLoader extends Loader { constructor( manager ) { super( manager ); this.textures = {}; } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( scope.manager ); loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( "THREE.MaterialLoader: Undefined texture", name ); } return textures[ name ]; } const material = MaterialLoader.createMaterialFromType( json.type ); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.sheen !== undefined ) material.sheen = json.sheen; if ( json.sheenColor !== undefined ) material.sheenColor = new Color().setHex( json.sheenColor ); if ( json.sheenRoughness !== undefined ) material.sheenRoughness = json.sheenRoughness; if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity; if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex( json.specularColor ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; if ( json.iridescence !== undefined ) material.iridescence = json.iridescence; if ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR; if ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange; if ( json.transmission !== undefined ) material.transmission = json.transmission; if ( json.thickness !== undefined ) material.thickness = json.thickness; if ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance; if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor ); if ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy; if ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.combine !== undefined ) material.combine = json.combine; if ( json.side !== undefined ) material.side = json.side; if ( json.shadowSide !== undefined ) material.shadowSide = json.shadowSide; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.alphaHash !== undefined ) material.alphaHash = json.alphaHash; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.rotation !== undefined ) material.rotation = json.rotation; if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; if ( json.scale !== undefined ) material.scale = json.scale; if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; if ( json.dithering !== undefined ) material.dithering = json.dithering; if ( json.alphaToCoverage !== undefined ) material.alphaToCoverage = json.alphaToCoverage; if ( json.premultipliedAlpha !== undefined ) material.premultipliedAlpha = json.premultipliedAlpha; if ( json.forceSinglePass !== undefined ) material.forceSinglePass = json.forceSinglePass; if ( json.visible !== undefined ) material.visible = json.visible; if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; if ( json.userData !== undefined ) material.userData = json.userData; if ( json.vertexColors !== undefined ) { if ( typeof json.vertexColors === "number" ) { material.vertexColors = ( json.vertexColors > 0 ) ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if ( json.uniforms !== undefined ) { for ( const name in json.uniforms ) { const uniform = json.uniforms[ name ]; material.uniforms[ name ] = {}; switch ( uniform.type ) { case "t": material.uniforms[ name ].value = getTexture( uniform.value ); break; case "c": material.uniforms[ name ].value = new Color().setHex( uniform.value ); break; case "v2": material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); break; case "v3": material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); break; case "v4": material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); break; case "m3": material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); break; case "m4": material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); break; default: material.uniforms[ name ].value = uniform.value; } } } if ( json.defines !== undefined ) material.defines = json.defines; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.glslVersion !== undefined ) material.glslVersion = json.glslVersion; if ( json.extensions !== undefined ) { for ( const key in json.extensions ) { material.extensions[ key ] = json.extensions[ key ]; } } if ( json.lights !== undefined ) material.lights = json.lights; if ( json.clipping !== undefined ) material.clipping = json.clipping; // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; if ( json.normalScale !== undefined ) { let normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap ); if ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); if ( json.iridescenceMap !== undefined ) material.iridescenceMap = getTexture( json.iridescenceMap ); if ( json.iridescenceThicknessMap !== undefined ) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap ); if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); if ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap ); if ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap ); if ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap ); if ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap ); return material; } setTextures( value ) { this.textures = value; return this; } static createMaterialFromType( type ) { const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material }; return new materialLib[ type ](); } } class LoaderUtils { static decodeText( array ) { if ( typeof TextDecoder !== "undefined" ) { return new TextDecoder().decode( array ); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for ( let i = 0, il = array.length; i < il; i ++ ) { // Implicitly assumes little-endian. s += String.fromCharCode( array[ i ] ); } try { // merges multi-byte utf-8 characters. return decodeURIComponent( escape( s ) ); } catch ( e ) { // see #16358 return s; } } static extractUrlBase( url ) { const index = url.lastIndexOf( "/" ); if ( index === - 1 ) return "./"; return url.slice( 0, index + 1 ); } static resolveURL( url, path ) { // Invalid URL if ( typeof url !== "string" || url === "" ) return ""; // Host Relative URL if ( /^https?:///i.test( path ) && /^//.test( url ) ) { path = path.replace( /(^https?://[^/]+).*/i, "$1" ); } // Absolute URL http://,https://,// if ( /^(https?:)?///i.test( url ) ) return url; // Data URI if ( /^data:.*,.*$/i.test( url ) ) return url; // Blob URL if ( /^blob:.*$/i.test( url ) ) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.isInstancedBufferGeometry = true; this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy( source ) { super.copy( source ); this.instanceCount = source.instanceCount; return this; } toJSON() { const data = super.toJSON(); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } class BufferGeometryLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( scope.manager ); loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer( json, uuid ) { if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[ uuid ]; const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); const array = getTypedArray( interleavedBuffer.type, buffer ); const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[ uuid ] = ib; return ib; } function getArrayBuffer( json, uuid ) { if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[ uuid ]; const ab = new Uint32Array( arrayBuffer ).buffer; arrayBufferMap[ uuid ] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if ( index !== undefined ) { const typedArray = getTypedArray( index.type, index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } const attributes = json.data.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; let bufferAttribute; if ( attribute.isInterleavedBufferAttribute ) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); } else { const typedArray = getTypedArray( attribute.type, attribute.array ); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); } if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage ); if ( attribute.updateRange !== undefined ) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute( key, bufferAttribute ); } const morphAttributes = json.data.morphAttributes; if ( morphAttributes ) { for ( const key in morphAttributes ) { const attributeArray = morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; let bufferAttribute; if ( attribute.isInterleavedBufferAttribute ) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); } else { const typedArray = getTypedArray( attribute.type, attribute.array ); bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); } if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; array.push( bufferAttribute ); } geometry.morphAttributes[ key ] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if ( morphTargetsRelative ) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( let i = 0, n = groups.length; i !== n; ++ i ) { const group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } const boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { const center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } if ( json.name ) geometry.name = json.name; if ( json.userData ) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const path = ( this.path === "" ) ? LoaderUtils.extractUrlBase( url ) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { let json = null; try { json = JSON.parse( text ); } catch ( error ) { if ( onError !== undefined ) onError( error ); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message ); return; } const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { if ( onError !== undefined ) onError( new Error( "THREE.ObjectLoader: Can"t load " + url ) ); console.error( "THREE.ObjectLoader: Can"t load " + url ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); } async loadAsync( url, onProgress ) { const scope = this; const path = ( this.path === "" ) ? LoaderUtils.extractUrlBase( url ) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); const text = await loader.loadAsync( url, onProgress ); const json = JSON.parse( text ); const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { throw new Error( "THREE.ObjectLoader: Can"t load " + url ); } return await scope.parseAsync( json ); } parse( json, onLoad ) { const animations = this.parseAnimations( json.animations ); const shapes = this.parseShapes( json.shapes ); const geometries = this.parseGeometries( json.geometries, shapes ); const images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); const textures = this.parseTextures( json.textures, images ); const materials = this.parseMaterials( json.materials, textures ); const object = this.parseObject( json.object, geometries, materials, textures, animations ); const skeletons = this.parseSkeletons( json.skeletons, object ); this.bindSkeletons( object, skeletons ); // if ( onLoad !== undefined ) { let hasImages = false; for ( const uuid in images ) { if ( images[ uuid ].data instanceof HTMLImageElement ) { hasImages = true; break; } } if ( hasImages === false ) onLoad( object ); } return object; } async parseAsync( json ) { const animations = this.parseAnimations( json.animations ); const shapes = this.parseShapes( json.shapes ); const geometries = this.parseGeometries( json.geometries, shapes ); const images = await this.parseImagesAsync( json.images ); const textures = this.parseTextures( json.textures, images ); const materials = this.parseMaterials( json.materials, textures ); const object = this.parseObject( json.object, geometries, materials, textures, animations ); const skeletons = this.parseSkeletons( json.skeletons, object ); this.bindSkeletons( object, skeletons ); return object; } parseShapes( json ) { const shapes = {}; if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const shape = new Shape().fromJSON( json[ i ] ); shapes[ shape.uuid ] = shape; } } return shapes; } parseSkeletons( json, object ) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse( function ( child ) { if ( child.isBone ) bones[ child.uuid ] = child; } ); // create skeletons if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const skeleton = new Skeleton().fromJSON( json[ i ], bones ); skeletons[ skeleton.uuid ] = skeleton; } } return skeletons; } parseGeometries( json, shapes ) { const geometries = {}; if ( json !== undefined ) { const bufferGeometryLoader = new BufferGeometryLoader(); for ( let i = 0, l = json.length; i < l; i ++ ) { let geometry; const data = json[ i ]; switch ( data.type ) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse( data ); break; default: if ( data.type in Geometries ) { geometry = Geometries[ data.type ].fromJSON( data, shapes ); } else { console.warn( `THREE.ObjectLoader: Unsupported geometry type "${ data.type }"` ); } } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; if ( data.userData !== undefined ) geometry.userData = data.userData; geometries[ data.uuid ] = geometry; } } return geometries; } parseMaterials( json, textures ) { const cache = {}; // MultiMaterial const materials = {}; if ( json !== undefined ) { const loader = new MaterialLoader(); loader.setTextures( textures ); for ( let i = 0, l = json.length; i < l; i ++ ) { const data = json[ i ]; if ( cache[ data.uuid ] === undefined ) { cache[ data.uuid ] = loader.parse( data ); } materials[ data.uuid ] = cache[ data.uuid ]; } } return materials; } parseAnimations( json ) { const animations = {}; if ( json !== undefined ) { for ( let i = 0; i < json.length; i ++ ) { const data = json[ i ]; const clip = AnimationClip.parse( data ); animations[ clip.uuid ] = clip; } } return animations; } parseImages( json, onLoad ) { const scope = this; const images = {}; let loader; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemError( url ); scope.manager.itemEnd( url ); } ); } function deserializeImage( image ) { if ( typeof image === "string" ) { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test( url ) ? url : scope.resourcePath + url; return loadImage( path ); } else { if ( image.data ) { return { data: getTypedArray( image.type, image.data ), width: image.width, height: image.height }; } else { return null; } } } if ( json !== undefined && json.length > 0 ) { const manager = new LoadingManager( onLoad ); loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( let i = 0, il = json.length; i < il; i ++ ) { const image = json[ i ]; const url = image.url; if ( Array.isArray( url ) ) { // load array of images e.g CubeTexture const imageArray = []; for ( let j = 0, jl = url.length; j < jl; j ++ ) { const currentUrl = url[ j ]; const deserializedImage = deserializeImage( currentUrl ); if ( deserializedImage !== null ) { if ( deserializedImage instanceof HTMLImageElement ) { imageArray.push( deserializedImage ); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); } } } images[ image.uuid ] = new Source( imageArray ); } else { // load single image const deserializedImage = deserializeImage( image.url ); images[ image.uuid ] = new Source( deserializedImage ); } } } return images; } async parseImagesAsync( json ) { const scope = this; const images = {}; let loader; async function deserializeImage( image ) { if ( typeof image === "string" ) { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test( url ) ? url : scope.resourcePath + url; return await loader.loadAsync( path ); } else { if ( image.data ) { return { data: getTypedArray( image.type, image.data ), width: image.width, height: image.height }; } else { return null; } } } if ( json !== undefined && json.length > 0 ) { loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); for ( let i = 0, il = json.length; i < il; i ++ ) { const image = json[ i ]; const url = image.url; if ( Array.isArray( url ) ) { // load array of images e.g CubeTexture const imageArray = []; for ( let j = 0, jl = url.length; j < jl; j ++ ) { const currentUrl = url[ j ]; const deserializedImage = await deserializeImage( currentUrl ); if ( deserializedImage !== null ) { if ( deserializedImage instanceof HTMLImageElement ) { imageArray.push( deserializedImage ); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); } } } images[ image.uuid ] = new Source( imageArray ); } else { // load single image const deserializedImage = await deserializeImage( image.url ); images[ image.uuid ] = new Source( deserializedImage ); } } } return images; } parseTextures( json, images ) { function parseConstant( value, type ) { if ( typeof value === "number" ) return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value ); return type[ value ]; } const textures = {}; if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const data = json[ i ]; if ( data.image === undefined ) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined image", data.image ); } const source = images[ data.image ]; const image = source.data; let texture; if ( Array.isArray( image ) ) { texture = new CubeTexture(); if ( image.length === 6 ) texture.needsUpdate = true; } else { if ( image && image.data ) { texture = new DataTexture(); } else { texture = new Texture(); } if ( image ) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); if ( data.channel !== undefined ) texture.channel = data.channel; if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.center !== undefined ) texture.center.fromArray( data.center ); if ( data.rotation !== undefined ) texture.rotation = data.rotation; if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); } if ( data.format !== undefined ) texture.format = data.format; if ( data.internalFormat !== undefined ) texture.internalFormat = data.internalFormat; if ( data.type !== undefined ) texture.type = data.type; if ( data.colorSpace !== undefined ) texture.colorSpace = data.colorSpace; if ( data.encoding !== undefined ) texture.encoding = data.encoding; // @deprecated, r152 if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; if ( data.generateMipmaps !== undefined ) texture.generateMipmaps = data.generateMipmaps; if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; if ( data.compareFunction !== undefined ) texture.compareFunction = data.compareFunction; if ( data.userData !== undefined ) texture.userData = data.userData; textures[ data.uuid ] = texture; } } return textures; } parseObject( data, geometries, materials, textures, animations ) { let object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined geometry", name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( Array.isArray( name ) ) { const array = []; for ( let i = 0, l = name.length; i < l; i ++ ) { const uuid = name[ i ]; if ( materials[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", uuid ); } array.push( materials[ uuid ] ); } return array; } if ( materials[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", name ); } return materials[ name ]; } function getTexture( uuid ) { if ( textures[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined texture", uuid ); } return textures[ uuid ]; } let geometry, material; switch ( data.type ) { case "Scene": object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } else { object.background = getTexture( data.background ); } } if ( data.environment !== undefined ) { object.environment = getTexture( data.environment ); } if ( data.fog !== undefined ) { if ( data.fog.type === "Fog" ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === "FogExp2" ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } if ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness; if ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity; break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "AmbientLight": object = new AmbientLight( data.color, data.intensity ); break; case "DirectionalLight": object = new DirectionalLight( data.color, data.intensity ); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case "LightProbe": object = new LightProbe().fromJSON( data ); break; case "SkinnedMesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); object = new SkinnedMesh( geometry, material ); if ( data.bindMode !== undefined ) object.bindMode = data.bindMode; if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix ); if ( data.skeleton !== undefined ) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); object = new Mesh( geometry, material ); break; case "InstancedMesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh( geometry, material, count ); object.instanceMatrix = new InstancedBufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); if ( instanceColor !== undefined ) object.instanceColor = new InstancedBufferAttribute( new Float32Array( instanceColor.array ), instanceColor.itemSize ); break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineLoop": object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineSegments": object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "PointCloud": case "Points": object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "Sprite": object = new Sprite( getMaterial( data.material ) ); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { object.matrix.fromArray( data.matrix ); if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.up !== undefined ) object.up.fromArray( data.up ); if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.layers !== undefined ) object.layers.mask = data.layers; if ( data.children !== undefined ) { const children = data.children; for ( let i = 0; i < children.length; i ++ ) { object.add( this.parseObject( children[ i ], geometries, materials, textures, animations ) ); } } if ( data.animations !== undefined ) { const objectAnimations = data.animations; for ( let i = 0; i < objectAnimations.length; i ++ ) { const uuid = objectAnimations[ i ]; object.animations.push( animations[ uuid ] ); } } if ( data.type === "LOD" ) { if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; const levels = data.levels; for ( let l = 0; l < levels.length; l ++ ) { const level = levels[ l ]; const child = object.getObjectByProperty( "uuid", level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance, level.hysteresis ); } } } return object; } bindSkeletons( object, skeletons ) { if ( Object.keys( skeletons ).length === 0 ) return; object.traverse( function ( child ) { if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) { const skeleton = skeletons[ child.skeleton ]; if ( skeleton === undefined ) { console.warn( "THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton ); } else { child.bind( skeleton, child.bindMatrix ); } } } ); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor( manager ) { super( manager ); this.isImageBitmapLoader = true; if ( typeof createImageBitmap === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported." ); } if ( typeof fetch === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: fetch() not supported." ); } this.options = { premultiplyAlpha: "none" }; } setOptions( options ) { this.options = options; return this; } load( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const scope = this; const cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } const fetchOptions = {}; fetchOptions.credentials = ( this.crossOrigin === "anonymous" ) ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch( url, fetchOptions ).then( function ( res ) { return res.blob(); } ).then( function ( blob ) { return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: "none" } ) ); } ).then( function ( imageBitmap ) { Cache.add( url, imageBitmap ); if ( onLoad ) onLoad( imageBitmap ); scope.manager.itemEnd( url ); } ).catch( function ( e ) { if ( onError ) onError( e ); scope.manager.itemError( url ); scope.manager.itemEnd( url ); } ); scope.manager.itemStart( url ); } } let _context; class AudioContext { static getContext() { if ( _context === undefined ) { _context = new ( window.AudioContext || window.webkitAudioContext )(); } return _context; } static setContext( value ) { _context = value; } } class AudioLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( buffer ) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice( 0 ); const context = AudioContext.getContext(); context.decodeAudioData( bufferCopy, function ( audioBuffer ) { onLoad( audioBuffer ); }, handleError ); } catch ( e ) { handleError( e ); } }, onProgress, onError ); function handleError( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } } } class HemisphereLightProbe extends LightProbe { constructor( skyColor, groundColor, intensity = 1 ) { super( undefined, intensity ); this.isHemisphereLightProbe = true; const color1 = new Color().set( skyColor ); const color2 = new Color().set( groundColor ); const sky = new Vector3( color1.r, color1.g, color1.b ); const ground = new Vector3( color2.r, color2.g, color2.b ); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt( Math.PI ); const c1 = c0 * Math.sqrt( 0.75 ); this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); } } class AmbientLightProbe extends LightProbe { constructor( color, intensity = 1 ) { super( undefined, intensity ); this.isAmbientLightProbe = true; const color1 = new Color().set( color ); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); } } const _eyeRight = /*@__PURE__*/ new Matrix4(); const _eyeLeft = /*@__PURE__*/ new Matrix4(); const _projectionMatrix = /*@__PURE__*/ new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update( camera ) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if ( needsUpdate ) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy( camera.projectionMatrix ); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = ( cache.near * Math.tan( DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[ 12 ] = - eyeSepHalf; _eyeRight.elements[ 12 ] = eyeSepHalf; // for left eye xmin = - ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( _projectionMatrix ); // for right eye xmin = - ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( _projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); } } class Clock { constructor( autoStart = true ) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { const newTime = now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 } const _position$1 = /*@__PURE__*/ new Vector3(); const _quaternion$1 = /*@__PURE__*/ new Quaternion(); const _scale$1 = /*@__PURE__*/ new Vector3(); const _orientation$1 = /*@__PURE__*/ new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose( _position$1, _quaternion$1, _scale$1 ); _orientation$1.set( 0, 0, - 1 ).applyQuaternion( _quaternion$1 ); if ( listener.positionX ) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime( _position$1.x, endTime ); listener.positionY.linearRampToValueAtTime( _position$1.y, endTime ); listener.positionZ.linearRampToValueAtTime( _position$1.z, endTime ); listener.forwardX.linearRampToValueAtTime( _orientation$1.x, endTime ); listener.forwardY.linearRampToValueAtTime( _orientation$1.y, endTime ); listener.forwardZ.linearRampToValueAtTime( _orientation$1.z, endTime ); listener.upX.linearRampToValueAtTime( up.x, endTime ); listener.upY.linearRampToValueAtTime( up.y, endTime ); listener.upZ.linearRampToValueAtTime( up.z, endTime ); } else { listener.setPosition( _position$1.x, _position$1.y, _position$1.z ); listener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z ); } } } class Audio extends Object3D { constructor( listener ) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource( audioNode ) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource( mediaElement ) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource( mediaElement ); this.connect(); return this; } setMediaStreamSource( mediaStream ) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource( mediaStream ); this.connect(); return this; } setBuffer( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = "buffer"; if ( this.autoplay ) this.play(); return this; } play( delay = 0 ) { if ( this.isPlaying === true ) { console.warn( "THREE.Audio: Audio is already playing." ); return; } if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind( this ); source.start( this._startedAt, this._progress + this.offset, this.duration ); this.isPlaying = true; this.source = source; this.setDetune( this.detune ); this.setPlaybackRate( this.playbackRate ); return this.connect(); } pause() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } if ( this.isPlaying === true ) { // update current progress this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; if ( this.loop === true ) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % ( this.duration || this.buffer.duration ); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this._progress = 0; if ( this.source !== null ) { this.source.stop(); this.source.onended = null; } this.isPlaying = false; return this; } connect() { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( let i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } this._connected = true; return this; } disconnect() { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( let i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters( value ) { if ( ! value ) value = []; if ( this._connected === true ) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune( value ) { this.detune = value; if ( this.source.detune === undefined ) return; // only set detune when available if ( this.isPlaying === true ) { this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[ 0 ]; } setFilter( filter ) { return this.setFilters( filter ? [ filter ] : [] ); } setPlaybackRate( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return false; } return this.loop; } setLoop( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; } setLoopStart( value ) { this.loopStart = value; return this; } setLoopEnd( value ) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } } const _position = /*@__PURE__*/ new Vector3(); const _quaternion = /*@__PURE__*/ new Quaternion(); const _scale = /*@__PURE__*/ new Vector3(); const _orientation = /*@__PURE__*/ new Vector3(); class PositionalAudio extends Audio { constructor( listener ) { super( listener ); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect( this.gain ); } connect() { super.connect(); this.panner.connect( this.gain ); } disconnect() { super.disconnect(); this.panner.disconnect( this.gain ); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance( value ) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor( value ) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel( value ) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance( value ) { this.panner.maxDistance = value; return this; } setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; this.matrixWorld.decompose( _position, _quaternion, _scale ); _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion ); const panner = this.panner; if ( panner.positionX ) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime( _position.x, endTime ); panner.positionY.linearRampToValueAtTime( _position.y, endTime ); panner.positionZ.linearRampToValueAtTime( _position.z, endTime ); panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime ); panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime ); panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime ); } else { panner.setPosition( _position.x, _position.y, _position.z ); panner.setOrientation( _orientation.x, _orientation.y, _orientation.z ); } } } class AudioAnalyser { constructor( audio, fftSize = 2048 ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } getFrequencyData() { this.analyser.getByteFrequencyData( this.data ); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for ( let i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } class PropertyMixer { constructor( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch ( typeName ) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array( valueSize * 6 ); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array( valueSize * 5 ); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array( valueSize * 5 ); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( let i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive( weight ) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if ( this.cumulativeWeightAdditive === 0 ) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply( accuIndex ) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } if ( weightAdditive > 0 ) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); } for ( let i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for ( let i = startIndex; i < endIndex; i ++ ) { this.buffer[ i ] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for ( let i = 0; i < this.valueSize; i ++ ) { this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; } } // mix functions _select( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( let i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } } _slerp( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); } _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); // Slerp to the intermediate result Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); } _lerp( buffer, dstOffset, srcOffset, t, stride ) { const s = 1 - t; for ( let i = 0; i !== stride; ++ i ) { const j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { for ( let i = 0; i !== stride; ++ i ) { const j = dstOffset + i; buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp( "[" + _RESERVED_CHARS_RE + "]", "g" ); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace( "\.", "" ) + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /*@__PURE__*/ /((?:WC+[/:])*)/.source.replace( "WC", _wordChar ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace( "WCOD", _wordCharOrDot ); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /*@__PURE__*/ /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", _wordChar ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /*@__PURE__*/ /.(WC+)(?:[(.+)])?/.source.replace( "WC", _wordChar ); const _trackRe = new RegExp( "" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$" ); const _supportedObjectNames = [ "material", "materials", "bones", "map" ]; class Composite { constructor( targetGroup, path, optionalParsedPath ) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); } getValue( array, offset ) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); } setValue( array, offset ) { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } } bind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } } unbind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ); this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName( name ) { return name.replace( /s/g, "_" ).replace( _reservedRe, "" ); } static parseTrackName( trackName ) { const matches = _trackRe.exec( trackName ); if ( matches === null ) { throw new Error( "PropertyBinding: Cannot parse trackName: " + trackName ); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], // required propertyIndex: matches[ 6 ] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf( "." ); if ( lastDot !== undefined && lastDot !== - 1 ) { const objectName = results.nodeName.substring( lastDot + 1 ); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { results.nodeName = results.nodeName.substring( 0, lastDot ); results.objectName = objectName; } } if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName ); } return results; } static findNode( root, nodeName ) { if ( nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { const bone = root.skeleton.getBoneByName( nodeName ); if ( bone !== undefined ) { return bone; } } // search into node subtree. if ( root.children ) { const searchNodeSubtree = function ( children ) { for ( let i = 0; i < children.length; i ++ ) { const childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } const result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; const subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct( buffer, offset ) { buffer[ offset ] = this.targetObject[ this.propertyName ]; } _getValue_array( buffer, offset ) { const source = this.resolvedProperty; for ( let i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } } _getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; } _getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } // Direct _setValue_direct( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; } _setValue_direct_setNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } } _setValue_array_setNeedsUpdate( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; } _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); } _setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); } _setValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ); this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.warn( "THREE.PropertyBinding: No target node found for track: " + this.path + "." ); return; } if ( objectName ) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case "materials": if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.materials ) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this ); return; } targetObject = targetObject.material.materials; break; case "bones": if ( ! targetObject.skeleton ) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( let i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; case "map": if ( "map" in targetObject ) { targetObject = targetObject.map; break; } if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.map ) { console.error( "THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.", this ); return; } targetObject = targetObject.material.map; break; default: if ( targetObject[ objectName ] === undefined ) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property const nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { const nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject ); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this ); return; } if ( ! targetObject.geometry.morphAttributes ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this ); return; } if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( Array.isArray( nodeProperty ) ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [ PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray, ]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ [ // Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, ], [ // EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, ], [ // ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, ], [ // HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, ] ]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.isAnimationObjectGroup = true; this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( let i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid; let index = indicesByUUID[ uuid ]; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region const firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ]; let binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject ) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region const firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object"s place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; if ( lastIndex > 0 ) { indicesByUUID[ lastObject.uuid ] = index; } objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[ path ]; const bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { const object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; } unsubscribe_( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } } class AnimationAction { constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( let i = 0; i !== nTracks; ++ i ) { const interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = - 1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false;// keep feeding the last frame? this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate this.zeroSlopeAtEnd = true;// clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction( this ); return this; } stop() { this._mixer._deactivateAction( this ); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = - 1;// forget previous loops this._startTime = null;// forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction( this ); } startAt( time ) { this._startTime = time; return this; } setLoop( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn( duration ) { return this._scheduleFading( duration, 0, 1 ); } fadeOut( duration ) { return this._scheduleFading( duration, 1, 0 ); } crossFadeFrom( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if ( warp ) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; } crossFadeTo( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); } stopFading() { const weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); } warp( startTimeScale, endTimeScale, duration ) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer if ( ! this.enabled ) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight( time ); return; } const startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action const timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { deltaTime = 0; } else { this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); const clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight( time ); if ( weight > 0 ) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch ( this.blendMode ) { case AdditiveAnimationBlendMode: for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulateAdditive( weight ); } break; case NormalAnimationBlendMode: default: for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } } } _updateWeight( time ) { let weight = 0; if ( this.enabled ) { weight = this.weight; const interpolant = this._weightInterpolant; if ( interpolant !== null ) { const interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale( time ) { let timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { const interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime( deltaTime ) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = ( loop === LoopPingPong ); if ( deltaTime === 0 ) { if ( loopCount === - 1 ) return time; return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; } if ( loop === LoopOnce ) { if ( loopCount === - 1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else { this.time = time; break handle_stop; } if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this.time = time; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime < 0 ? - 1 : 1 } ); } } else { // repetitive Repeat or PingPong if ( loopCount === - 1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around const loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); const pending = this.repetitions - loopCount; if ( pending <= 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime > 0 ? 1 : - 1 } ); } else { // keep running if ( pending === 1 ) { // entering the last round const atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent( { type: "loop", action: this, loopDelta: loopDelta } ); } } else { this.time = time; } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings( atStart, atEnd, pingPong ) { const settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading( duration, weightNow, weightThen ) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } } const _controlInterpolantsResultBuffer = new Float32Array( 1 ); class AnimationMixer extends EventDispatcher { constructor( root ) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction( action, prototypeAction ) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( let i = 0; i !== nTracks; ++ i ) { const track = tracks[ i ], trackName = track.name; let binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { ++ binding.referenceCount; bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } const path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } } _activateAction( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } const bindings = action._propertyBindings; // increment reference counts / sort out state for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } } _deactivateAction( action ) { if ( this._isActiveAction( action ) ) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction( action ) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction( action, clipUuid, rootUuid ) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; } _removeInactiveAction( action ) { const actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = ( action._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); } _removeInactiveBindingsForAction( action ) { const bindings = action._propertyBindings; for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } } _lendAction( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; } _takeBackAction( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding( binding, rootUuid, trackName ) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); } _removeInactiveBinding( binding ) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; if ( Object.keys( bindingByName ).length === 0 ) { delete bindingsByRoot[ rootUuid ]; } } _lendBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; } _takeBackBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++; let interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, _controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; } _takeBackControlInterpolant( interpolant ) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction( clip, optionalRoot, blendMode ) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[ clipUuid ]; let prototypeAction = null; if ( blendMode === undefined ) { if ( clipObject !== null ) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if ( actionsForClip !== undefined ) { const existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; } // get an existing action existingAction( clip, optionalRoot ) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for ( let i = nActions - 1; i >= 0; -- i ) { actions[ i ].stop(); } return this; } // advance the time and update apply the animation update( deltaTime ) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( let i = 0; i !== nActions; ++ i ) { const action = actions[ i ]; action._update( time, deltaTime, timeDirection, accuIndex ); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for ( let i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; } // Allows you to seek to a specific time in an animation. setTime( timeInSeconds ) { this.time = 0; // Zero out time attribute for AnimationMixer object; for ( let i = 0; i < this._actions.length; i ++ ) { this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip( clip ) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { const action = actionsToRemove[ i ]; this._deactivateAction( action ); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } } // free all resources specific to a particular root target object uncacheRoot( root ) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( const clipUuid in actionsByClip ) { const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( const trackName in bindingByName ) { const binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } } // remove a targeted clip from the cache uncacheAction( clip, optionalRoot ) { const action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } class Uniform { constructor( value ) { this.value = value; } clone() { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); } } let id = 0; class UniformsGroup extends EventDispatcher { constructor() { super(); this.isUniformsGroup = true; Object.defineProperty( this, "id", { value: id ++ } ); this.name = ""; this.usage = StaticDrawUsage; this.uniforms = []; } add( uniform ) { this.uniforms.push( uniform ); return this; } remove( uniform ) { const index = this.uniforms.indexOf( uniform ); if ( index !== - 1 ) this.uniforms.splice( index, 1 ); return this; } setName( name ) { this.name = name; return this; } setUsage( value ) { this.usage = value; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); return this; } copy( source ) { this.name = source.name; this.usage = source.usage; const uniformsSource = source.uniforms; this.uniforms.length = 0; for ( let i = 0, l = uniformsSource.length; i < l; i ++ ) { this.uniforms.push( uniformsSource[ i ].clone() ); } return this; } clone() { return new this.constructor().copy( this ); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor( array, stride, meshPerAttribute = 1 ) { super( array, stride ); this.isInstancedInterleavedBuffer = true; this.meshPerAttribute = meshPerAttribute; } copy( source ) { super.copy( source ); this.meshPerAttribute = source.meshPerAttribute; return this; } clone( data ) { const ib = super.clone( data ); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON( data ) { const json = super.toJSON( data ); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } class GLBufferAttribute { constructor( buffer, type, itemSize, elementSize, count ) { this.isGLBufferAttribute = true; this.name = ""; this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } setBuffer( buffer ) { this.buffer = buffer; return this; } setType( type, elementSize ) { this.type = type; this.elementSize = elementSize; return this; } setItemSize( itemSize ) { this.itemSize = itemSize; return this; } setCount( count ) { this.count = count; return this; } } class Raycaster { constructor( origin, direction, near = 0, far = Infinity ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); } setFromCamera( coords, camera ) { if ( camera.isPerspectiveCamera ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); this.camera = camera; } else if ( camera.isOrthographicCamera ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); this.camera = camera; } else { console.error( "THREE.Raycaster: Unsupported camera type: " + camera.type ); } } intersectObject( object, recursive = true, intersects = [] ) { intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; } intersectObjects( objects, recursive = true, intersects = [] ) { for ( let i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.layers.test( raycaster.layers ) ) { object.raycast( raycaster, intersects ); } if ( recursive === true ) { const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor( radius = 1, phi = 0, theta = 0 ) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be between EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; } setFromVector3( v ) { return this.setFromCartesianCoords( v.x, v.y, v.z ); } setFromCartesianCoords( x, y, z ) { this.radius = Math.sqrt( x * x + y * y + z * z ); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( x, z ); this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); } return this; } clone() { return new this.constructor().copy( this ); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor( radius = 1, theta = 0, y = 0 ) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3( v ) { return this.setFromCartesianCoords( v.x, v.y, v.z ); } setFromCartesianCoords( x, y, z ) { this.radius = Math.sqrt( x * x + z * z ); this.theta = Math.atan2( x, z ); this.y = y; return this; } clone() { return new this.constructor().copy( this ); } } const _vector$4 = /*@__PURE__*/ new Vector2(); class Box2 { constructor( min = new Vector2( + Infinity, + Infinity ), max = new Vector2( - Infinity, - Infinity ) ) { this.isBox2 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$4.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); } intersectsBox( box ) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { return this.clampPoint( point, _vector$4 ).distanceTo( point ); } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _startP = /*@__PURE__*/ new Vector3(); const _startEnd = /*@__PURE__*/ new Vector3(); class Line3 { constructor( start = new Vector3(), end = new Vector3() ) { this.start = start; this.end = end; } set( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; } copy( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; } getCenter( target ) { return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); } delta( target ) { return target.subVectors( this.end, this.start ); } distanceSq() { return this.start.distanceToSquared( this.end ); } distance() { return this.start.distanceTo( this.end ); } at( t, target ) { return this.delta( target ).multiplyScalar( t ).add( this.start ); } closestPointToPointParameter( point, clampToLine ) { _startP.subVectors( point, this.start ); _startEnd.subVectors( this.end, this.start ); const startEnd2 = _startEnd.dot( _startEnd ); const startEnd_startP = _startEnd.dot( _startP ); let t = startEnd_startP / startEnd2; if ( clampToLine ) { t = clamp( t, 0, 1 ); } return t; } closestPointToPoint( point, clampToLine, target ) { const t = this.closestPointToPointParameter( point, clampToLine ); return this.delta( target ).multiplyScalar( t ).add( this.start ); } applyMatrix4( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; } equals( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } clone() { return new this.constructor().copy( this ); } } const _vector$3 = /*@__PURE__*/ new Vector3(); class SpotLightHelper extends Object3D { constructor( light, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "SpotLightHelper"; const geometry = new BufferGeometry(); const positions = [ 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 ]; for ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { const p1 = ( i / l ) * Math.PI * 2; const p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); this.light.target.updateWorldMatrix( true, false ); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); _vector$3.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( _vector$3 ); if ( this.color !== undefined ) { this.cone.material.color.set( this.color ); } else { this.cone.material.color.copy( this.light.color ); } } } const _vector$2 = /*@__PURE__*/ new Vector3(); const _boneMatrix = /*@__PURE__*/ new Matrix4(); const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); class SkeletonHelper extends LineSegments { constructor( object ) { const bones = getBoneList( object ); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color( 0, 0, 1 ); const color2 = new Color( 0, 1, 0 ); for ( let i = 0; i < bones.length; i ++ ) { const bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); super( geometry, material ); this.isSkeletonHelper = true; this.type = "SkeletonHelper"; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld( force ) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute( "position" ); _matrixWorldInv.copy( this.root.matrixWorld ).invert(); for ( let i = 0, j = 0; i < bones.length; i ++ ) { const bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); _vector$2.setFromMatrixPosition( _boneMatrix ); position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); _vector$2.setFromMatrixPosition( _boneMatrix ); position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); j += 2; } } geometry.getAttribute( "position" ).needsUpdate = true; super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); } } function getBoneList( object ) { const boneList = []; if ( object.isBone === true ) { boneList.push( object ); } for ( let i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } return boneList; } class PointLightHelper extends Mesh { constructor( light, sphereSize, color ) { const geometry = new SphereGeometry( sphereSize, 4, 2 ); const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); super( geometry, material ); this.light = light; this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { this.material.color.copy( this.light.color ); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/ new Vector3(); const _color1 = /*@__PURE__*/ new Color(); const _color2 = /*@__PURE__*/ new Color(); class HemisphereLightHelper extends Object3D { constructor( light, size, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "HemisphereLightHelper"; const geometry = new OctahedronGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); if ( this.color === undefined ) this.material.vertexColors = true; const position = geometry.getAttribute( "position" ); const colors = new Float32Array( position.count * 3 ); geometry.setAttribute( "color", new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, this.material ) ); this.update(); } dispose() { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); } update() { const mesh = this.children[ 0 ]; if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { const colors = mesh.geometry.getAttribute( "color" ); _color1.copy( this.light.color ); _color2.copy( this.light.groundColor ); for ( let i = 0, l = colors.count; i < l; i ++ ) { const color = ( i < ( l / 2 ) ) ? _color1 : _color2; colors.setXYZ( i, color.r, color.g, color.b ); } colors.needsUpdate = true; } this.light.updateWorldMatrix( true, false ); mesh.lookAt( _vector$1.setFromMatrixPosition( this.light.matrixWorld ).negate() ); } } class GridHelper extends LineSegments { constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { color1 = new Color( color1 ); color2 = new Color( color2 ); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { vertices.push( - halfSize, 0, k, halfSize, 0, k ); vertices.push( k, 0, - halfSize, k, 0, halfSize ); const color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "GridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PolarGridHelper extends LineSegments { constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) { color1 = new Color( color1 ); color2 = new Color( color2 ); const vertices = []; const colors = []; // create the sectors if ( sectors > 1 ) { for ( let i = 0; i < sectors; i ++ ) { const v = ( i / sectors ) * ( Math.PI * 2 ); const x = Math.sin( v ) * radius; const z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); const color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } } // create the rings for ( let i = 0; i < rings; i ++ ) { const color = ( i & 1 ) ? color1 : color2; const r = radius - ( radius / rings * i ); for ( let j = 0; j < divisions; j ++ ) { // first vertex let v = ( j / divisions ) * ( Math.PI * 2 ); let x = Math.sin( v ) * r; let z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "PolarGridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } const _v1 = /*@__PURE__*/ new Vector3(); const _v2 = /*@__PURE__*/ new Vector3(); const _v3 = /*@__PURE__*/ new Vector3(); class DirectionalLightHelper extends Object3D { constructor( light, size, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "DirectionalLightHelper"; if ( size === undefined ) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); this.lightPlane = new Line( geometry, material ); this.add( this.lightPlane ); geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.targetLine = new Line( geometry, material ); this.add( this.targetLine ); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); this.light.target.updateWorldMatrix( true, false ); _v1.setFromMatrixPosition( this.light.matrixWorld ); _v2.setFromMatrixPosition( this.light.target.matrixWorld ); _v3.subVectors( _v2, _v1 ); this.lightPlane.lookAt( _v2 ); if ( this.color !== undefined ) { this.lightPlane.material.color.set( this.color ); this.targetLine.material.color.set( this.color ); } else { this.lightPlane.material.color.copy( this.light.color ); this.targetLine.material.color.copy( this.light.color ); } this.targetLine.lookAt( _v2 ); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/ new Vector3(); const _camera = /*@__PURE__*/ new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor( camera ) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } ); const vertices = []; const colors = []; const pointMap = {}; // near addLine( "n1", "n2" ); addLine( "n2", "n4" ); addLine( "n4", "n3" ); addLine( "n3", "n1" ); // far addLine( "f1", "f2" ); addLine( "f2", "f4" ); addLine( "f4", "f3" ); addLine( "f3", "f1" ); // sides addLine( "n1", "f1" ); addLine( "n2", "f2" ); addLine( "n3", "f3" ); addLine( "n4", "f4" ); // cone addLine( "p", "n1" ); addLine( "p", "n2" ); addLine( "p", "n3" ); addLine( "p", "n4" ); // up addLine( "u1", "u2" ); addLine( "u2", "u3" ); addLine( "u3", "u1" ); // target addLine( "c", "t" ); addLine( "p", "c" ); // cross addLine( "cn1", "cn2" ); addLine( "cn3", "cn4" ); addLine( "cf1", "cf2" ); addLine( "cf3", "cf4" ); function addLine( a, b ) { addPoint( a ); addPoint( b ); } function addPoint( id ) { vertices.push( 0, 0, 0 ); colors.push( 0, 0, 0 ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); super( geometry, material ); this.type = "CameraHelper"; this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); // colors const colorFrustum = new Color( 0xffaa00 ); const colorCone = new Color( 0xff0000 ); const colorUp = new Color( 0x00aaff ); const colorTarget = new Color( 0xffffff ); const colorCross = new Color( 0x333333 ); this.setColors( colorFrustum, colorCone, colorUp, colorTarget, colorCross ); } setColors( frustum, cone, up, target, cross ) { const geometry = this.geometry; const colorAttribute = geometry.getAttribute( "color" ); // near colorAttribute.setXYZ( 0, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 1, frustum.r, frustum.g, frustum.b ); // n1, n2 colorAttribute.setXYZ( 2, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 3, frustum.r, frustum.g, frustum.b ); // n2, n4 colorAttribute.setXYZ( 4, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 5, frustum.r, frustum.g, frustum.b ); // n4, n3 colorAttribute.setXYZ( 6, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 7, frustum.r, frustum.g, frustum.b ); // n3, n1 // far colorAttribute.setXYZ( 8, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 9, frustum.r, frustum.g, frustum.b ); // f1, f2 colorAttribute.setXYZ( 10, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 11, frustum.r, frustum.g, frustum.b ); // f2, f4 colorAttribute.setXYZ( 12, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 13, frustum.r, frustum.g, frustum.b ); // f4, f3 colorAttribute.setXYZ( 14, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 15, frustum.r, frustum.g, frustum.b ); // f3, f1 // sides colorAttribute.setXYZ( 16, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 17, frustum.r, frustum.g, frustum.b ); // n1, f1 colorAttribute.setXYZ( 18, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 19, frustum.r, frustum.g, frustum.b ); // n2, f2 colorAttribute.setXYZ( 20, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 21, frustum.r, frustum.g, frustum.b ); // n3, f3 colorAttribute.setXYZ( 22, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 23, frustum.r, frustum.g, frustum.b ); // n4, f4 // cone colorAttribute.setXYZ( 24, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 25, cone.r, cone.g, cone.b ); // p, n1 colorAttribute.setXYZ( 26, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 27, cone.r, cone.g, cone.b ); // p, n2 colorAttribute.setXYZ( 28, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 29, cone.r, cone.g, cone.b ); // p, n3 colorAttribute.setXYZ( 30, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 31, cone.r, cone.g, cone.b ); // p, n4 // up colorAttribute.setXYZ( 32, up.r, up.g, up.b ); colorAttribute.setXYZ( 33, up.r, up.g, up.b ); // u1, u2 colorAttribute.setXYZ( 34, up.r, up.g, up.b ); colorAttribute.setXYZ( 35, up.r, up.g, up.b ); // u2, u3 colorAttribute.setXYZ( 36, up.r, up.g, up.b ); colorAttribute.setXYZ( 37, up.r, up.g, up.b ); // u3, u1 // target colorAttribute.setXYZ( 38, target.r, target.g, target.b ); colorAttribute.setXYZ( 39, target.r, target.g, target.b ); // c, t colorAttribute.setXYZ( 40, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 41, cross.r, cross.g, cross.b ); // p, c // cross colorAttribute.setXYZ( 42, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 43, cross.r, cross.g, cross.b ); // cn1, cn2 colorAttribute.setXYZ( 44, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 45, cross.r, cross.g, cross.b ); // cn3, cn4 colorAttribute.setXYZ( 46, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 47, cross.r, cross.g, cross.b ); // cf1, cf2 colorAttribute.setXYZ( 48, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 49, cross.r, cross.g, cross.b ); // cf3, cf4 colorAttribute.needsUpdate = true; } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); // center / target setPoint( "c", pointMap, geometry, _camera, 0, 0, - 1 ); setPoint( "t", pointMap, geometry, _camera, 0, 0, 1 ); // near setPoint( "n1", pointMap, geometry, _camera, - w, - h, - 1 ); setPoint( "n2", pointMap, geometry, _camera, w, - h, - 1 ); setPoint( "n3", pointMap, geometry, _camera, - w, h, - 1 ); setPoint( "n4", pointMap, geometry, _camera, w, h, - 1 ); // far setPoint( "f1", pointMap, geometry, _camera, - w, - h, 1 ); setPoint( "f2", pointMap, geometry, _camera, w, - h, 1 ); setPoint( "f3", pointMap, geometry, _camera, - w, h, 1 ); setPoint( "f4", pointMap, geometry, _camera, w, h, 1 ); // up setPoint( "u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 ); setPoint( "u2", pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", pointMap, geometry, _camera, 0, h * 2, - 1 ); // cross setPoint( "cf1", pointMap, geometry, _camera, - w, 0, 1 ); setPoint( "cf2", pointMap, geometry, _camera, w, 0, 1 ); setPoint( "cf3", pointMap, geometry, _camera, 0, - h, 1 ); setPoint( "cf4", pointMap, geometry, _camera, 0, h, 1 ); setPoint( "cn1", pointMap, geometry, _camera, - w, 0, - 1 ); setPoint( "cn2", pointMap, geometry, _camera, w, 0, - 1 ); setPoint( "cn3", pointMap, geometry, _camera, 0, - h, - 1 ); setPoint( "cn4", pointMap, geometry, _camera, 0, h, - 1 ); geometry.getAttribute( "position" ).needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint( point, pointMap, geometry, camera, x, y, z ) { _vector.set( x, y, z ).unproject( camera ); const points = pointMap[ point ]; if ( points !== undefined ) { const position = geometry.getAttribute( "position" ); for ( let i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], _vector.x, _vector.y, _vector.z ); } } } const _box = /*@__PURE__*/ new Box3(); class BoxHelper extends LineSegments { constructor( object, color = 0xffff00 ) { const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); const positions = new Float32Array( 8 * 3 ); const geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.setAttribute( "position", new BufferAttribute( positions, 3 ) ); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update( object ) { if ( object !== undefined ) { console.warn( "THREE.BoxHelper: .update() has no longer arguments." ); } if ( this.object !== undefined ) { _box.setFromObject( this.object ); } if ( _box.isEmpty() ) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject( object ) { this.object = object; this.update(); return this; } copy( source, recursive ) { super.copy( source, recursive ); this.object = source.object; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class Box3Helper extends LineSegments { constructor( box, color = 0xffff00 ) { const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; const geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld( force ) { const box = this.box; if ( box.isEmpty() ) return; box.getCenter( this.position ); box.getSize( this.scale ); this.scale.multiplyScalar( 0.5 ); super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PlaneHelper extends Line { constructor( plane, size = 1, hex = 0xffff00 ) { const color = hex; const positions = [ 1, - 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ]; const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); geometry.computeBoundingSphere(); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ]; const geometry2 = new BufferGeometry(); geometry2.setAttribute( "position", new Float32BufferAttribute( positions2, 3 ) ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) ); } updateMatrixWorld( force ) { this.position.set( 0, 0, 0 ); this.scale.set( 0.5 * this.size, 0.5 * this.size, 1 ); this.lookAt( this.plane.normal ); this.translateZ( - this.plane.constant ); super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); } } const _axis = /*@__PURE__*/ new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) { super(); this.type = "ArrowHelper"; if ( _lineGeometry === undefined ) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); _coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 ); _coneGeometry.translate( 0, - 0.5, 0 ); } this.position.copy( origin ); this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { _axis.set( dir.z, 0, - dir.x ).normalize(); const radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( _axis, radians ); } } setLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) { this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458 this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); } setColor( color ) { this.line.material.color.set( color ); this.cone.material.color.set( color ); } copy( source ) { super.copy( source, false ); this.line.copy( source.line ); this.cone.copy( source.cone ); return this; } dispose() { this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); this.cone.material.dispose(); } } class AxesHelper extends LineSegments { constructor( size = 1 ) { const vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; const colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "AxesHelper"; } setColors( xAxisColor, yAxisColor, zAxisColor ) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set( xAxisColor ); color.toArray( array, 0 ); color.toArray( array, 3 ); color.set( yAxisColor ); color.toArray( array, 6 ); color.toArray( array, 9 ); color.set( zAxisColor ); color.toArray( array, 12 ); color.toArray( array, 15 ); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo( x, y ) { this.currentPath = new Path(); this.subPaths.push( this.currentPath ); this.currentPath.moveTo( x, y ); return this; } lineTo( x, y ) { this.currentPath.lineTo( x, y ); return this; } quadraticCurveTo( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); return this; } bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); return this; } splineThru( pts ) { this.currentPath.splineThru( pts ); return this; } toShapes( isCCW ) { function toShapesNoHoles( inSubpaths ) { const shapes = []; for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { const tmpPath = inSubpaths[ i ]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { let edgeLowPt = inPolygon[ p ]; let edgeHighPt = inPolygon[ q ]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; let solid, tmpPath, tmpShape; const shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( let i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log("cw", i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { let ambiguous = false; let toChange = 0; for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { const sho = newShapeHoles[ sIdx ]; for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { const ho = sho[ hIdx ]; let hole_unassigned = true; for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange ++; if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } if ( toChange > 0 && ambiguous === false ) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for ( let i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } } if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "register", { detail: { revision: REVISION, } } ) ); } if ( typeof window !== "undefined" ) { if ( window.__THREE__ ) { console.warn( "WARNING: Multiple instances of Three.js being imported." ); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysCompare = AlwaysCompare; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationAction = AnimationAction; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasTexture = CanvasTexture; exports.CapsuleGeometry = CapsuleGeometry; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.ColorManagement = ColorManagement; exports.CompressedArrayTexture = CompressedArrayTexture; exports.CompressedCubeTexture = CompressedCubeTexture; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DisplayP3ColorSpace = DisplayP3ColorSpace; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EllipseCurve = EllipseCurve; exports.EqualCompare = EqualCompare; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FileLoader = FileLoader; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterCompare = GreaterCompare; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualCompare = GreaterEqualCompare; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LessCompare = LessCompare; exports.LessDepth = LessDepth; exports.LessEqualCompare = LessEqualCompare; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LineSegments = LineSegments; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearSRGBColorSpace = LinearSRGBColorSpace; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverCompare = NeverCompare; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColorSpace = NoColorSpace; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualCompare = NotEqualCompare; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.RED_GREEN_RGTC2_Format = RED_GREEN_RGTC2_Format; exports.RED_RGTC1_Format = RED_RGTC1_Format; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RenderTarget = RenderTarget; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingGeometry = RingGeometry; exports.SIGNED_RED_GREEN_RGTC2_Format = SIGNED_RED_GREEN_RGTC2_Format; exports.SIGNED_RED_RGTC1_Format = SIGNED_RED_RGTC1_Format; exports.SRGBColorSpace = SRGBColorSpace; exports.Scene = Scene; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.Source = Source; exports.Sphere = Sphere; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronGeometry = TetrahedronGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusGeometry = TorusGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeGeometry = TubeGeometry; exports.TwoPassDoubleSide = TwoPassDoubleSide; exports.UVMapping = UVMapping; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsGroup = UniformsGroup; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCoordinateSystem = WebGLCoordinateSystem; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WebGPUCoordinateSystem = WebGPUCoordinateSystem; exports.WireframeGeometry = WireframeGeometry; exports.WrapAroundEnding = WrapAroundEnding; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; })); /* */}),null);
-----
mapillary",[],(function $module_mapillary(global,require,requireDynamic,requireLazy,module,exports){ (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.mapillary = {})); })(this, (function (exports) { "use strict"; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } function isFunction(value) { return typeof value === "function"; } function createErrorClass(createImpl) { var _super = function (instance) { Error.call(instance); instance.stack = new Error().stack; }; var ctorFunc = createImpl(_super); ctorFunc.prototype = Object.create(Error.prototype); ctorFunc.prototype.constructor = ctorFunc; return ctorFunc; } var UnsubscriptionError = createErrorClass(function (_super) { return function UnsubscriptionErrorImpl(errors) { _super(this); this.message = errors ? errors.length + " errors occurred during unsubscription: " + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join(" ") : ""; this.name = "UnsubscriptionError"; this.errors = errors; }; }); function arrRemove(arr, item) { if (arr) { var index = arr.indexOf(item); 0 <= index && arr.splice(index, 1); } } var Subscription = (function () { function Subscription(initialTeardown) { this.initialTeardown = initialTeardown; this.closed = false; this._parentage = null; this._finalizers = null; } Subscription.prototype.unsubscribe = function () { var e_1, _a, e_2, _b; var errors; if (!this.closed) { this.closed = true; var _parentage = this._parentage; if (_parentage) { this._parentage = null; if (Array.isArray(_parentage)) { try { for (var _parentage_1 = __values(_parentage), _parentage_1_1 = _parentage_1.next(); !_parentage_1_1.done; _parentage_1_1 = _parentage_1.next()) { var parent_1 = _parentage_1_1.value; parent_1.remove(this); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_parentage_1_1 && !_parentage_1_1.done && (_a = _parentage_1.return)) _a.call(_parentage_1); } finally { if (e_1) throw e_1.error; } } } else { _parentage.remove(this); } } var initialFinalizer = this.initialTeardown; if (isFunction(initialFinalizer)) { try { initialFinalizer(); } catch (e) { errors = e instanceof UnsubscriptionError ? e.errors : [e]; } } var _finalizers = this._finalizers; if (_finalizers) { this._finalizers = null; try { for (var _finalizers_1 = __values(_finalizers), _finalizers_1_1 = _finalizers_1.next(); !_finalizers_1_1.done; _finalizers_1_1 = _finalizers_1.next()) { var finalizer = _finalizers_1_1.value; try { execFinalizer(finalizer); } catch (err) { errors = errors !== null && errors !== void 0 ? errors : []; if (err instanceof UnsubscriptionError) { errors = __spreadArray(__spreadArray([], __read(errors)), __read(err.errors)); } else { errors.push(err); } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_finalizers_1_1 && !_finalizers_1_1.done && (_b = _finalizers_1.return)) _b.call(_finalizers_1); } finally { if (e_2) throw e_2.error; } } } if (errors) { throw new UnsubscriptionError(errors); } } }; Subscription.prototype.add = function (teardown) { var _a; if (teardown && teardown !== this) { if (this.closed) { execFinalizer(teardown); } else { if (teardown instanceof Subscription) { if (teardown.closed || teardown._hasParent(this)) { return; } teardown._addParent(this); } (this._finalizers = (_a = this._finalizers) !== null && _a !== void 0 ? _a : []).push(teardown); } } }; Subscription.prototype._hasParent = function (parent) { var _parentage = this._parentage; return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent)); }; Subscription.prototype._addParent = function (parent) { var _parentage = this._parentage; this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent; }; Subscription.prototype._removeParent = function (parent) { var _parentage = this._parentage; if (_parentage === parent) { this._parentage = null; } else if (Array.isArray(_parentage)) { arrRemove(_parentage, parent); } }; Subscription.prototype.remove = function (teardown) { var _finalizers = this._finalizers; _finalizers && arrRemove(_finalizers, teardown); if (teardown instanceof Subscription) { teardown._removeParent(this); } }; Subscription.EMPTY = (function () { var empty = new Subscription(); empty.closed = true; return empty; })(); return Subscription; }()); var EMPTY_SUBSCRIPTION = Subscription.EMPTY; function isSubscription(value) { return (value instanceof Subscription || (value && "closed" in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))); } function execFinalizer(finalizer) { if (isFunction(finalizer)) { finalizer(); } else { finalizer.unsubscribe(); } } var config = { onUnhandledError: null, onStoppedNotification: null, Promise: undefined, useDeprecatedSynchronousErrorHandling: false, useDeprecatedNextContext: false, }; var timeoutProvider = { setTimeout: function (handler, timeout) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } var delegate = timeoutProvider.delegate; if (delegate === null || delegate === void 0 ? void 0 : delegate.setTimeout) { return delegate.setTimeout.apply(delegate, __spreadArray([handler, timeout], __read(args))); } return setTimeout.apply(void 0, __spreadArray([handler, timeout], __read(args))); }, clearTimeout: function (handle) { var delegate = timeoutProvider.delegate; return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearTimeout) || clearTimeout)(handle); }, delegate: undefined, }; function reportUnhandledError(err) { timeoutProvider.setTimeout(function () { { throw err; } }); } function noop() { } function errorContext(cb) { { cb(); } } var Subscriber = (function (_super) { __extends(Subscriber, _super); function Subscriber(destination) { var _this = _super.call(this) || this; _this.isStopped = false; if (destination) { _this.destination = destination; if (isSubscription(destination)) { destination.add(_this); } } else { _this.destination = EMPTY_OBSERVER; } return _this; } Subscriber.create = function (next, error, complete) { return new SafeSubscriber(next, error, complete); }; Subscriber.prototype.next = function (value) { if (this.isStopped) ; else { this._next(value); } }; Subscriber.prototype.error = function (err) { if (this.isStopped) ; else { this.isStopped = true; this._error(err); } }; Subscriber.prototype.complete = function () { if (this.isStopped) ; else { this.isStopped = true; this._complete(); } }; Subscriber.prototype.unsubscribe = function () { if (!this.closed) { this.isStopped = true; _super.prototype.unsubscribe.call(this); this.destination = null; } }; Subscriber.prototype._next = function (value) { this.destination.next(value); }; Subscriber.prototype._error = function (err) { try { this.destination.error(err); } finally { this.unsubscribe(); } }; Subscriber.prototype._complete = function () { try { this.destination.complete(); } finally { this.unsubscribe(); } }; return Subscriber; }(Subscription)); var _bind = Function.prototype.bind; function bind(fn, thisArg) { return _bind.call(fn, thisArg); } var ConsumerObserver = (function () { function ConsumerObserver(partialObserver) { this.partialObserver = partialObserver; } ConsumerObserver.prototype.next = function (value) { var partialObserver = this.partialObserver; if (partialObserver.next) { try { partialObserver.next(value); } catch (error) { handleUnhandledError(error); } } }; ConsumerObserver.prototype.error = function (err) { var partialObserver = this.partialObserver; if (partialObserver.error) { try { partialObserver.error(err); } catch (error) { handleUnhandledError(error); } } else { handleUnhandledError(err); } }; ConsumerObserver.prototype.complete = function () { var partialObserver = this.partialObserver; if (partialObserver.complete) { try { partialObserver.complete(); } catch (error) { handleUnhandledError(error); } } }; return ConsumerObserver; }()); var SafeSubscriber = (function (_super) { __extends(SafeSubscriber, _super); function SafeSubscriber(observerOrNext, error, complete) { var _this = _super.call(this) || this; var partialObserver; if (isFunction(observerOrNext) || !observerOrNext) { partialObserver = { next: (observerOrNext !== null && observerOrNext !== void 0 ? observerOrNext : undefined), error: error !== null && error !== void 0 ? error : undefined, complete: complete !== null && complete !== void 0 ? complete : undefined, }; } else { var context_1; if (_this && config.useDeprecatedNextContext) { context_1 = Object.create(observerOrNext); context_1.unsubscribe = function () { return _this.unsubscribe(); }; partialObserver = { next: observerOrNext.next && bind(observerOrNext.next, context_1), error: observerOrNext.error && bind(observerOrNext.error, context_1), complete: observerOrNext.complete && bind(observerOrNext.complete, context_1), }; } else { partialObserver = observerOrNext; } } _this.destination = new ConsumerObserver(partialObserver); return _this; } return SafeSubscriber; }(Subscriber)); function handleUnhandledError(error) { { reportUnhandledError(error); } } function defaultErrorHandler(err) { throw err; } var EMPTY_OBSERVER = { closed: true, next: noop, error: defaultErrorHandler, complete: noop, }; var observable = (function () { return (typeof Symbol === "function" && Symbol.observable) || "@@observable"; })(); function identity(x) { return x; } function pipeFromArray(fns) { if (fns.length === 0) { return identity; } if (fns.length === 1) { return fns[0]; } return function piped(input) { return fns.reduce(function (prev, fn) { return fn(prev); }, input); }; } var Observable = (function () { function Observable(subscribe) { if (subscribe) { this._subscribe = subscribe; } } Observable.prototype.lift = function (operator) { var observable = new Observable(); observable.source = this; observable.operator = operator; return observable; }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { var _this = this; var subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete); errorContext(function () { var _a = _this, operator = _a.operator, source = _a.source; subscriber.add(operator ? operator.call(subscriber, source) : source ? _this._subscribe(subscriber) : _this._trySubscribe(subscriber)); }); return subscriber; }; Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe(sink); } catch (err) { sink.error(err); } }; Observable.prototype.forEach = function (next, promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var subscriber = new SafeSubscriber({ next: function (value) { try { next(value); } catch (err) { reject(err); subscriber.unsubscribe(); } }, error: reject, complete: resolve, }); _this.subscribe(subscriber); }); }; Observable.prototype._subscribe = function (subscriber) { var _a; return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber); }; Observable.prototype[observable] = function () { return this; }; Observable.prototype.pipe = function () { var operations = []; for (var _i = 0; _i < arguments.length; _i++) { operations[_i] = arguments[_i]; } return pipeFromArray(operations)(this); }; Observable.prototype.toPromise = function (promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var value; _this.subscribe(function (x) { return (value = x); }, function (err) { return reject(err); }, function () { return resolve(value); }); }); }; Observable.create = function (subscribe) { return new Observable(subscribe); }; return Observable; }()); function getPromiseCtor(promiseCtor) { var _a; return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : config.Promise) !== null && _a !== void 0 ? _a : Promise; } function isObserver(value) { return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete); } function isSubscriber(value) { return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value)); } function hasLift(source) { return isFunction(source === null || source === void 0 ? void 0 : source.lift); } function operate(init) { return function (source) { if (hasLift(source)) { return source.lift(function (liftedSource) { try { return init(liftedSource, this); } catch (err) { this.error(err); } }); } throw new TypeError("Unable to lift unknown Observable type"); }; } function createOperatorSubscriber(destination, onNext, onComplete, onError, onFinalize) { return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize); } var OperatorSubscriber = (function (_super) { __extends(OperatorSubscriber, _super); function OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize, shouldUnsubscribe) { var _this = _super.call(this, destination) || this; _this.onFinalize = onFinalize; _this.shouldUnsubscribe = shouldUnsubscribe; _this._next = onNext ? function (value) { try { onNext(value); } catch (err) { destination.error(err); } } : _super.prototype._next; _this._error = onError ? function (err) { try { onError(err); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : _super.prototype._error; _this._complete = onComplete ? function () { try { onComplete(); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : _super.prototype._complete; return _this; } OperatorSubscriber.prototype.unsubscribe = function () { var _a; if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { var closed_1 = this.closed; _super.prototype.unsubscribe.call(this); !closed_1 && ((_a = this.onFinalize) === null || _a === void 0 ? void 0 : _a.call(this)); } }; return OperatorSubscriber; }(Subscriber)); function refCount() { return operate(function (source, subscriber) { var connection = null; source._refCount++; var refCounter = createOperatorSubscriber(subscriber, undefined, undefined, undefined, function () { if (!source || source._refCount <= 0 || 0 < --source._refCount) { connection = null; return; } var sharedConnection = source._connection; var conn = connection; connection = null; if (sharedConnection && (!conn || sharedConnection === conn)) { sharedConnection.unsubscribe(); } subscriber.unsubscribe(); }); source.subscribe(refCounter); if (!refCounter.closed) { connection = source.connect(); } }); } var ConnectableObservable = (function (_super) { __extends(ConnectableObservable, _super); function ConnectableObservable(source, subjectFactory) { var _this = _super.call(this) || this; _this.source = source; _this.subjectFactory = subjectFactory; _this._subject = null; _this._refCount = 0; _this._connection = null; if (hasLift(source)) { _this.lift = source.lift; } return _this; } ConnectableObservable.prototype._subscribe = function (subscriber) { return this.getSubject().subscribe(subscriber); }; ConnectableObservable.prototype.getSubject = function () { var subject = this._subject; if (!subject || subject.isStopped) { this._subject = this.subjectFactory(); } return this._subject; }; ConnectableObservable.prototype._teardown = function () { this._refCount = 0; var _connection = this._connection; this._subject = this._connection = null; _connection === null || _connection === void 0 ? void 0 : _connection.unsubscribe(); }; ConnectableObservable.prototype.connect = function () { var _this = this; var connection = this._connection; if (!connection) { connection = this._connection = new Subscription(); var subject_1 = this.getSubject(); connection.add(this.source.subscribe(createOperatorSubscriber(subject_1, undefined, function () { _this._teardown(); subject_1.complete(); }, function (err) { _this._teardown(); subject_1.error(err); }, function () { return _this._teardown(); }))); if (connection.closed) { this._connection = null; connection = Subscription.EMPTY; } } return connection; }; ConnectableObservable.prototype.refCount = function () { return refCount()(this); }; return ConnectableObservable; }(Observable)); var ObjectUnsubscribedError = createErrorClass(function (_super) { return function ObjectUnsubscribedErrorImpl() { _super(this); this.name = "ObjectUnsubscribedError"; this.message = "object unsubscribed"; }; }); var Subject = (function (_super) { __extends(Subject, _super); function Subject() { var _this = _super.call(this) || this; _this.closed = false; _this.currentObservers = null; _this.observers = []; _this.isStopped = false; _this.hasError = false; _this.thrownError = null; return _this; } Subject.prototype.lift = function (operator) { var subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; }; Subject.prototype._throwIfClosed = function () { if (this.closed) { throw new ObjectUnsubscribedError(); } }; Subject.prototype.next = function (value) { var _this = this; errorContext(function () { var e_1, _a; _this._throwIfClosed(); if (!_this.isStopped) { if (!_this.currentObservers) { _this.currentObservers = Array.from(_this.observers); } try { for (var _b = __values(_this.currentObservers), _c = _b.next(); !_c.done; _c = _b.next()) { var observer = _c.value; observer.next(value); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } } }); }; Subject.prototype.error = function (err) { var _this = this; errorContext(function () { _this._throwIfClosed(); if (!_this.isStopped) { _this.hasError = _this.isStopped = true; _this.thrownError = err; var observers = _this.observers; while (observers.length) { observers.shift().error(err); } } }); }; Subject.prototype.complete = function () { var _this = this; errorContext(function () { _this._throwIfClosed(); if (!_this.isStopped) { _this.isStopped = true; var observers = _this.observers; while (observers.length) { observers.shift().complete(); } } }); }; Subject.prototype.unsubscribe = function () { this.isStopped = this.closed = true; this.observers = this.currentObservers = null; }; Object.defineProperty(Subject.prototype, "observed", { get: function () { var _a; return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0; }, enumerable: false, configurable: true }); Subject.prototype._trySubscribe = function (subscriber) { this._throwIfClosed(); return _super.prototype._trySubscribe.call(this, subscriber); }; Subject.prototype._subscribe = function (subscriber) { this._throwIfClosed(); this._checkFinalizedStatuses(subscriber); return this._innerSubscribe(subscriber); }; Subject.prototype._innerSubscribe = function (subscriber) { var _this = this; var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers; if (hasError || isStopped) { return EMPTY_SUBSCRIPTION; } this.currentObservers = null; observers.push(subscriber); return new Subscription(function () { _this.currentObservers = null; arrRemove(observers, subscriber); }); }; Subject.prototype._checkFinalizedStatuses = function (subscriber) { var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped; if (hasError) { subscriber.error(thrownError); } else if (isStopped) { subscriber.complete(); } }; Subject.prototype.asObservable = function () { var observable = new Observable(); observable.source = this; return observable; }; Subject.create = function (destination, source) { return new AnonymousSubject(destination, source); }; return Subject; }(Observable)); var AnonymousSubject = (function (_super) { __extends(AnonymousSubject, _super); function AnonymousSubject(destination, source) { var _this = _super.call(this) || this; _this.destination = destination; _this.source = source; return _this; } AnonymousSubject.prototype.next = function (value) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value); }; AnonymousSubject.prototype.error = function (err) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err); }; AnonymousSubject.prototype.complete = function () { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a); }; AnonymousSubject.prototype._subscribe = function (subscriber) { var _a, _b; return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : EMPTY_SUBSCRIPTION; }; return AnonymousSubject; }(Subject)); var BehaviorSubject = (function (_super) { __extends(BehaviorSubject, _super); function BehaviorSubject(_value) { var _this = _super.call(this) || this; _this._value = _value; return _this; } Object.defineProperty(BehaviorSubject.prototype, "value", { get: function () { return this.getValue(); }, enumerable: false, configurable: true }); BehaviorSubject.prototype._subscribe = function (subscriber) { var subscription = _super.prototype._subscribe.call(this, subscriber); !subscription.closed && subscriber.next(this._value); return subscription; }; BehaviorSubject.prototype.getValue = function () { var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, _value = _a._value; if (hasError) { throw thrownError; } this._throwIfClosed(); return _value; }; BehaviorSubject.prototype.next = function (value) { _super.prototype.next.call(this, (this._value = value)); }; return BehaviorSubject; }(Subject)); var dateTimestampProvider = { now: function () { return (dateTimestampProvider.delegate || Date).now(); }, delegate: undefined, }; var ReplaySubject = (function (_super) { __extends(ReplaySubject, _super); function ReplaySubject(_bufferSize, _windowTime, _timestampProvider) { if (_bufferSize === void 0) { _bufferSize = Infinity; } if (_windowTime === void 0) { _windowTime = Infinity; } if (_timestampProvider === void 0) { _timestampProvider = dateTimestampProvider; } var _this = _super.call(this) || this; _this._bufferSize = _bufferSize; _this._windowTime = _windowTime; _this._timestampProvider = _timestampProvider; _this._buffer = []; _this._infiniteTimeWindow = true; _this._infiniteTimeWindow = _windowTime === Infinity; _this._bufferSize = Math.max(1, _bufferSize); _this._windowTime = Math.max(1, _windowTime); return _this; } ReplaySubject.prototype.next = function (value) { var _a = this, isStopped = _a.isStopped, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow, _timestampProvider = _a._timestampProvider, _windowTime = _a._windowTime; if (!isStopped) { _buffer.push(value); !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime); } this._trimBuffer(); _super.prototype.next.call(this, value); }; ReplaySubject.prototype._subscribe = function (subscriber) { this._throwIfClosed(); this._trimBuffer(); var subscription = this._innerSubscribe(subscriber); var _a = this, _infiniteTimeWindow = _a._infiniteTimeWindow, _buffer = _a._buffer; var copy = _buffer.slice(); for (var i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) { subscriber.next(copy[i]); } this._checkFinalizedStatuses(subscriber); return subscription; }; ReplaySubject.prototype._trimBuffer = function () { var _a = this, _bufferSize = _a._bufferSize, _timestampProvider = _a._timestampProvider, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow; var adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize; _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize); if (!_infiniteTimeWindow) { var now = _timestampProvider.now(); var last = 0; for (var i = 1; i < _buffer.length && _buffer[i] <= now; i += 2) { last = i; } last && _buffer.splice(0, last + 1); } }; return ReplaySubject; }(Subject)); var Action = (function (_super) { __extends(Action, _super); function Action(scheduler, work) { return _super.call(this) || this; } Action.prototype.schedule = function (state, delay) { return this; }; return Action; }(Subscription)); var intervalProvider = { setInterval: function (handler, timeout) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } var delegate = intervalProvider.delegate; if (delegate === null || delegate === void 0 ? void 0 : delegate.setInterval) { return delegate.setInterval.apply(delegate, __spreadArray([handler, timeout], __read(args))); } return setInterval.apply(void 0, __spreadArray([handler, timeout], __read(args))); }, clearInterval: function (handle) { var delegate = intervalProvider.delegate; return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearInterval) || clearInterval)(handle); }, delegate: undefined, }; var AsyncAction = (function (_super) { __extends(AsyncAction, _super); function AsyncAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; _this.pending = false; return _this; } AsyncAction.prototype.schedule = function (state, delay) { var _a; if (delay === void 0) { delay = 0; } if (this.closed) { return this; } this.state = state; var id = this.id; var scheduler = this.scheduler; if (id != null) { this.id = this.recycleAsyncId(scheduler, id, delay); } this.pending = true; this.delay = delay; this.id = (_a = this.id) !== null && _a !== void 0 ? _a : this.requestAsyncId(scheduler, this.id, delay); return this; }; AsyncAction.prototype.requestAsyncId = function (scheduler, _id, delay) { if (delay === void 0) { delay = 0; } return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay); }; AsyncAction.prototype.recycleAsyncId = function (_scheduler, id, delay) { if (delay === void 0) { delay = 0; } if (delay != null && this.delay === delay && this.pending === false) { return id; } if (id != null) { intervalProvider.clearInterval(id); } return undefined; }; AsyncAction.prototype.execute = function (state, delay) { if (this.closed) { return new Error("executing a cancelled action"); } this.pending = false; var error = this._execute(state, delay); if (error) { return error; } else if (this.pending === false && this.id != null) { this.id = this.recycleAsyncId(this.scheduler, this.id, null); } }; AsyncAction.prototype._execute = function (state, _delay) { var errored = false; var errorValue; try { this.work(state); } catch (e) { errored = true; errorValue = e ? e : new Error("Scheduled action threw falsy error"); } if (errored) { this.unsubscribe(); return errorValue; } }; AsyncAction.prototype.unsubscribe = function () { if (!this.closed) { var _a = this, id = _a.id, scheduler = _a.scheduler; var actions = scheduler.actions; this.work = this.state = this.scheduler = null; this.pending = false; arrRemove(actions, this); if (id != null) { this.id = this.recycleAsyncId(scheduler, id, null); } this.delay = null; _super.prototype.unsubscribe.call(this); } }; return AsyncAction; }(Action)); var Scheduler = (function () { function Scheduler(schedulerActionCtor, now) { if (now === void 0) { now = Scheduler.now; } this.schedulerActionCtor = schedulerActionCtor; this.now = now; } Scheduler.prototype.schedule = function (work, delay, state) { if (delay === void 0) { delay = 0; } return new this.schedulerActionCtor(this, work).schedule(state, delay); }; Scheduler.now = dateTimestampProvider.now; return Scheduler; }()); var AsyncScheduler = (function (_super) { __extends(AsyncScheduler, _super); function AsyncScheduler(SchedulerAction, now) { if (now === void 0) { now = Scheduler.now; } var _this = _super.call(this, SchedulerAction, now) || this; _this.actions = []; _this._active = false; return _this; } AsyncScheduler.prototype.flush = function (action) { var actions = this.actions; if (this._active) { actions.push(action); return; } var error; this._active = true; do { if ((error = action.execute(action.state, action.delay))) { break; } } while ((action = actions.shift())); this._active = false; if (error) { while ((action = actions.shift())) { action.unsubscribe(); } throw error; } }; return AsyncScheduler; }(Scheduler)); var asyncScheduler = new AsyncScheduler(AsyncAction); var async = asyncScheduler; var EMPTY$1 = new Observable(function (subscriber) { return subscriber.complete(); }); function empty(scheduler) { return scheduler ? emptyScheduled(scheduler) : EMPTY$1; } function emptyScheduled(scheduler) { return new Observable(function (subscriber) { return scheduler.schedule(function () { return subscriber.complete(); }); }); } function isScheduler(value) { return value && isFunction(value.schedule); } function last$1(arr) { return arr[arr.length - 1]; } function popResultSelector(args) { return isFunction(last$1(args)) ? args.pop() : undefined; } function popScheduler(args) { return isScheduler(last$1(args)) ? args.pop() : undefined; } function popNumber(args, defaultValue) { return typeof last$1(args) === "number" ? args.pop() : defaultValue; } var isArrayLike = (function (x) { return x && typeof x.length === "number" && typeof x !== "function"; }); function isPromise(value) { return isFunction(value === null || value === void 0 ? void 0 : value.then); } function isInteropObservable(input) { return isFunction(input[observable]); } function isAsyncIterable(obj) { return Symbol.asyncIterator && isFunction(obj === null || obj === void 0 ? void 0 : obj[Symbol.asyncIterator]); } function createInvalidObservableTypeError(input) { return new TypeError("You provided " + (input !== null && typeof input === "object" ? "an invalid object" : """ + input + """) + " where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable."); } function getSymbolIterator() { if (typeof Symbol !== "function" || !Symbol.iterator) { return "@@iterator"; } return Symbol.iterator; } var iterator = getSymbolIterator(); function isIterable(input) { return isFunction(input === null || input === void 0 ? void 0 : input[iterator]); } function readableStreamLikeToAsyncGenerator(readableStream) { return __asyncGenerator(this, arguments, function readableStreamLikeToAsyncGenerator_1() { var reader, _a, value, done; return __generator(this, function (_b) { switch (_b.label) { case 0: reader = readableStream.getReader(); _b.label = 1; case 1: _b.trys.push([1, , 9, 10]); _b.label = 2; case 2: return [4, __await(reader.read())]; case 3: _a = _b.sent(), value = _a.value, done = _a.done; if (!done) return [3, 5]; return [4, __await(void 0)]; case 4: return [2, _b.sent()]; case 5: return [4, __await(value)]; case 6: return [4, _b.sent()]; case 7: _b.sent(); return [3, 2]; case 8: return [3, 10]; case 9: reader.releaseLock(); return [7]; case 10: return [2]; } }); }); } function isReadableStreamLike(obj) { return isFunction(obj === null || obj === void 0 ? void 0 : obj.getReader); } function innerFrom(input) { if (input instanceof Observable) { return input; } if (input != null) { if (isInteropObservable(input)) { return fromInteropObservable(input); } if (isArrayLike(input)) { return fromArrayLike(input); } if (isPromise(input)) { return fromPromise(input); } if (isAsyncIterable(input)) { return fromAsyncIterable(input); } if (isIterable(input)) { return fromIterable(input); } if (isReadableStreamLike(input)) { return fromReadableStreamLike(input); } } throw createInvalidObservableTypeError(input); } function fromInteropObservable(obj) { return new Observable(function (subscriber) { var obs = obj[observable](); if (isFunction(obs.subscribe)) { return obs.subscribe(subscriber); } throw new TypeError("Provided object does not correctly implement Symbol.observable"); }); } function fromArrayLike(array) { return new Observable(function (subscriber) { for (var i = 0; i < array.length && !subscriber.closed; i++) { subscriber.next(array[i]); } subscriber.complete(); }); } function fromPromise(promise) { return new Observable(function (subscriber) { promise .then(function (value) { if (!subscriber.closed) { subscriber.next(value); subscriber.complete(); } }, function (err) { return subscriber.error(err); }) .then(null, reportUnhandledError); }); } function fromIterable(iterable) { return new Observable(function (subscriber) { var e_1, _a; try { for (var iterable_1 = __values(iterable), iterable_1_1 = iterable_1.next(); !iterable_1_1.done; iterable_1_1 = iterable_1.next()) { var value = iterable_1_1.value; subscriber.next(value); if (subscriber.closed) { return; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (iterable_1_1 && !iterable_1_1.done && (_a = iterable_1.return)) _a.call(iterable_1); } finally { if (e_1) throw e_1.error; } } subscriber.complete(); }); } function fromAsyncIterable(asyncIterable) { return new Observable(function (subscriber) { process(asyncIterable, subscriber).catch(function (err) { return subscriber.error(err); }); }); } function fromReadableStreamLike(readableStream) { return fromAsyncIterable(readableStreamLikeToAsyncGenerator(readableStream)); } function process(asyncIterable, subscriber) { var asyncIterable_1, asyncIterable_1_1; var e_2, _a; return __awaiter(this, void 0, void 0, function () { var value, e_2_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 5, 6, 11]); asyncIterable_1 = __asyncValues(asyncIterable); _b.label = 1; case 1: return [4, asyncIterable_1.next()]; case 2: if (!(asyncIterable_1_1 = _b.sent(), !asyncIterable_1_1.done)) return [3, 4]; value = asyncIterable_1_1.value; subscriber.next(value); if (subscriber.closed) { return [2]; } _b.label = 3; case 3: return [3, 1]; case 4: return [3, 11]; case 5: e_2_1 = _b.sent(); e_2 = { error: e_2_1 }; return [3, 11]; case 6: _b.trys.push([6, , 9, 10]); if (!(asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return))) return [3, 8]; return [4, _a.call(asyncIterable_1)]; case 7: _b.sent(); _b.label = 8; case 8: return [3, 10]; case 9: if (e_2) throw e_2.error; return [7]; case 10: return [7]; case 11: subscriber.complete(); return [2]; } }); }); } function executeSchedule(parentSubscription, scheduler, work, delay, repeat) { if (delay === void 0) { delay = 0; } if (repeat === void 0) { repeat = false; } var scheduleSubscription = scheduler.schedule(function () { work(); if (repeat) { parentSubscription.add(this.schedule(null, delay)); } else { this.unsubscribe(); } }, delay); parentSubscription.add(scheduleSubscription); if (!repeat) { return scheduleSubscription; } } function observeOn(scheduler, delay) { if (delay === void 0) { delay = 0; } return operate(function (source, subscriber) { source.subscribe(createOperatorSubscriber(subscriber, function (value) { return executeSchedule(subscriber, scheduler, function () { return subscriber.next(value); }, delay); }, function () { return executeSchedule(subscriber, scheduler, function () { return subscriber.complete(); }, delay); }, function (err) { return executeSchedule(subscriber, scheduler, function () { return subscriber.error(err); }, delay); })); }); } function subscribeOn(scheduler, delay) { if (delay === void 0) { delay = 0; } return operate(function (source, subscriber) { subscriber.add(scheduler.schedule(function () { return source.subscribe(subscriber); }, delay)); }); } function scheduleObservable(input, scheduler) { return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler)); } function schedulePromise(input, scheduler) { return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler)); } function scheduleArray(input, scheduler) { return new Observable(function (subscriber) { var i = 0; return scheduler.schedule(function () { if (i === input.length) { subscriber.complete(); } else { subscriber.next(input[i++]); if (!subscriber.closed) { this.schedule(); } } }); }); } function scheduleIterable(input, scheduler) { return new Observable(function (subscriber) { var iterator$1; executeSchedule(subscriber, scheduler, function () { iterator$1 = input[iterator](); executeSchedule(subscriber, scheduler, function () { var _a; var value; var done; try { (_a = iterator$1.next(), value = _a.value, done = _a.done); } catch (err) { subscriber.error(err); return; } if (done) { subscriber.complete(); } else { subscriber.next(value); } }, 0, true); }); return function () { return isFunction(iterator$1 === null || iterator$1 === void 0 ? void 0 : iterator$1.return) && iterator$1.return(); }; }); } function scheduleAsyncIterable(input, scheduler) { if (!input) { throw new Error("Iterable cannot be null"); } return new Observable(function (subscriber) { executeSchedule(subscriber, scheduler, function () { var iterator = input[Symbol.asyncIterator](); executeSchedule(subscriber, scheduler, function () { iterator.next().then(function (result) { if (result.done) { subscriber.complete(); } else { subscriber.next(result.value); } }); }, 0, true); }); }); } function scheduleReadableStreamLike(input, scheduler) { return scheduleAsyncIterable(readableStreamLikeToAsyncGenerator(input), scheduler); } function scheduled(input, scheduler) { if (input != null) { if (isInteropObservable(input)) { return scheduleObservable(input, scheduler); } if (isArrayLike(input)) { return scheduleArray(input, scheduler); } if (isPromise(input)) { return schedulePromise(input, scheduler); } if (isAsyncIterable(input)) { return scheduleAsyncIterable(input, scheduler); } if (isIterable(input)) { return scheduleIterable(input, scheduler); } if (isReadableStreamLike(input)) { return scheduleReadableStreamLike(input, scheduler); } } throw createInvalidObservableTypeError(input); } function from(input, scheduler) { return scheduler ? scheduled(input, scheduler) : innerFrom(input); } function of() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); return from(args, scheduler); } function throwError(errorOrErrorFactory, scheduler) { var errorFactory = isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function () { return errorOrErrorFactory; }; var init = function (subscriber) { return subscriber.error(errorFactory()); }; return new Observable(scheduler ? function (subscriber) { return scheduler.schedule(init, 0, subscriber); } : init); } var EmptyError = createErrorClass(function (_super) { return function EmptyErrorImpl() { _super(this); this.name = "EmptyError"; this.message = "no elements in sequence"; }; }); function isValidDate(value) { return value instanceof Date && !isNaN(value); } var TimeoutError = createErrorClass(function (_super) { return function TimeoutErrorImpl(info) { if (info === void 0) { info = null; } _super(this); this.message = "Timeout has occurred"; this.name = "TimeoutError"; this.info = info; }; }); function timeout(config, schedulerArg) { var _a = (isValidDate(config) ? { first: config } : typeof config === "number" ? { each: config } : config), first = _a.first, each = _a.each, _b = _a.with, _with = _b === void 0 ? timeoutErrorFactory : _b, _c = _a.scheduler, scheduler = _c === void 0 ? schedulerArg !== null && schedulerArg !== void 0 ? schedulerArg : asyncScheduler : _c, _d = _a.meta, meta = _d === void 0 ? null : _d; if (first == null && each == null) { throw new TypeError("No timeout provided."); } return operate(function (source, subscriber) { var originalSourceSubscription; var timerSubscription; var lastValue = null; var seen = 0; var startTimer = function (delay) { timerSubscription = executeSchedule(subscriber, scheduler, function () { try { originalSourceSubscription.unsubscribe(); innerFrom(_with({ meta: meta, lastValue: lastValue, seen: seen, })).subscribe(subscriber); } catch (err) { subscriber.error(err); } }, delay); }; originalSourceSubscription = source.subscribe(createOperatorSubscriber(subscriber, function (value) { timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); seen++; subscriber.next((lastValue = value)); each > 0 && startTimer(each); }, undefined, undefined, function () { if (!(timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.closed)) { timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); } lastValue = null; })); !seen && startTimer(first != null ? (typeof first === "number" ? first : +first - scheduler.now()) : each); }); } function timeoutErrorFactory(info) { throw new TimeoutError(info); } function map(project, thisArg) { return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { subscriber.next(project.call(thisArg, value, index++)); })); }); } var isArray$6 = Array.isArray; function callOrApply(fn, args) { return isArray$6(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args); } function mapOneOrManyArgs(fn) { return map(function (args) { return callOrApply(fn, args); }); } var isArray$5 = Array.isArray; var getPrototypeOf = Object.getPrototypeOf, objectProto = Object.prototype, getKeys = Object.keys; function argsArgArrayOrObject(args) { if (args.length === 1) { var first_1 = args[0]; if (isArray$5(first_1)) { return { args: first_1, keys: null }; } if (isPOJO(first_1)) { var keys = getKeys(first_1); return { args: keys.map(function (key) { return first_1[key]; }), keys: keys, }; } } return { args: args, keys: null }; } function isPOJO(obj) { return obj && typeof obj === "object" && getPrototypeOf(obj) === objectProto; } function createObject(keys, values) { return keys.reduce(function (result, key, i) { return ((result[key] = values[i]), result); }, {}); } function combineLatest() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); var resultSelector = popResultSelector(args); var _a = argsArgArrayOrObject(args), observables = _a.args, keys = _a.keys; if (observables.length === 0) { return from([], scheduler); } var result = new Observable(combineLatestInit(observables, scheduler, keys ? function (values) { return createObject(keys, values); } : identity)); return resultSelector ? result.pipe(mapOneOrManyArgs(resultSelector)) : result; } function combineLatestInit(observables, scheduler, valueTransform) { if (valueTransform === void 0) { valueTransform = identity; } return function (subscriber) { maybeSchedule(scheduler, function () { var length = observables.length; var values = new Array(length); var active = length; var remainingFirstValues = length; var _loop_1 = function (i) { maybeSchedule(scheduler, function () { var source = from(observables[i], scheduler); var hasFirstValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { values[i] = value; if (!hasFirstValue) { hasFirstValue = true; remainingFirstValues--; } if (!remainingFirstValues) { subscriber.next(valueTransform(values.slice())); } }, function () { if (!--active) { subscriber.complete(); } })); }, subscriber); }; for (var i = 0; i < length; i++) { _loop_1(i); } }, subscriber); }; } function maybeSchedule(scheduler, execute, subscription) { if (scheduler) { executeSchedule(subscription, scheduler, execute); } else { execute(); } } function mergeInternals(source, subscriber, project, concurrent, onBeforeNext, expand, innerSubScheduler, additionalFinalizer) { var buffer = []; var active = 0; var index = 0; var isComplete = false; var checkComplete = function () { if (isComplete && !buffer.length && !active) { subscriber.complete(); } }; var outerNext = function (value) { return (active < concurrent ? doInnerSub(value) : buffer.push(value)); }; var doInnerSub = function (value) { expand && subscriber.next(value); active++; var innerComplete = false; innerFrom(project(value, index++)).subscribe(createOperatorSubscriber(subscriber, function (innerValue) { onBeforeNext === null || onBeforeNext === void 0 ? void 0 : onBeforeNext(innerValue); if (expand) { outerNext(innerValue); } else { subscriber.next(innerValue); } }, function () { innerComplete = true; }, undefined, function () { if (innerComplete) { try { active--; var _loop_1 = function () { var bufferedValue = buffer.shift(); if (innerSubScheduler) { executeSchedule(subscriber, innerSubScheduler, function () { return doInnerSub(bufferedValue); }); } else { doInnerSub(bufferedValue); } }; while (buffer.length && active < concurrent) { _loop_1(); } checkComplete(); } catch (err) { subscriber.error(err); } } })); }; source.subscribe(createOperatorSubscriber(subscriber, outerNext, function () { isComplete = true; checkComplete(); })); return function () { additionalFinalizer === null || additionalFinalizer === void 0 ? void 0 : additionalFinalizer(); }; } function mergeMap(project, resultSelector, concurrent) { if (concurrent === void 0) { concurrent = Infinity; } if (isFunction(resultSelector)) { return mergeMap(function (a, i) { return map(function (b, ii) { return resultSelector(a, b, i, ii); })(innerFrom(project(a, i))); }, concurrent); } else if (typeof resultSelector === "number") { concurrent = resultSelector; } return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent); }); } function mergeAll(concurrent) { if (concurrent === void 0) { concurrent = Infinity; } return mergeMap(identity, concurrent); } function concatAll() { return mergeAll(1); } function concat() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return concatAll()(from(args, popScheduler(args))); } var nodeEventEmitterMethods = ["addListener", "removeListener"]; var eventTargetMethods = ["addEventListener", "removeEventListener"]; var jqueryMethods = ["on", "off"]; function fromEvent(target, eventName, options, resultSelector) { if (isFunction(options)) { resultSelector = options; options = undefined; } if (resultSelector) { return fromEvent(target, eventName, options).pipe(mapOneOrManyArgs(resultSelector)); } var _a = __read(isEventTarget(target) ? eventTargetMethods.map(function (methodName) { return function (handler) { return target[methodName](eventName, handler, options); }; }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add = _a[0], remove = _a[1]; if (!add) { if (isArrayLike(target)) { return mergeMap(function (subTarget) { return fromEvent(subTarget, eventName, options); })(innerFrom(target)); } } if (!add) { throw new TypeError("Invalid event target"); } return new Observable(function (subscriber) { var handler = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return subscriber.next(1 < args.length ? args : args[0]); }; add(handler); return function () { return remove(handler); }; }); } function toCommonHandlerRegistry(target, eventName) { return function (methodName) { return function (handler) { return target[methodName](eventName, handler); }; }; } function isNodeStyleEventEmitter(target) { return isFunction(target.addListener) && isFunction(target.removeListener); } function isJQueryStyleEventEmitter(target) { return isFunction(target.on) && isFunction(target.off); } function isEventTarget(target) { return isFunction(target.addEventListener) && isFunction(target.removeEventListener); } function timer(dueTime, intervalOrScheduler, scheduler) { if (dueTime === void 0) { dueTime = 0; } if (scheduler === void 0) { scheduler = async; } var intervalDuration = -1; if (intervalOrScheduler != null) { if (isScheduler(intervalOrScheduler)) { scheduler = intervalOrScheduler; } else { intervalDuration = intervalOrScheduler; } } return new Observable(function (subscriber) { var due = isValidDate(dueTime) ? +dueTime - scheduler.now() : dueTime; if (due < 0) { due = 0; } var n = 0; return scheduler.schedule(function () { if (!subscriber.closed) { subscriber.next(n++); if (0 <= intervalDuration) { this.schedule(undefined, intervalDuration); } else { subscriber.complete(); } } }, due); }); } function merge() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); var concurrent = popNumber(args, Infinity); var sources = args; return !sources.length ? EMPTY$1 : sources.length === 1 ? innerFrom(sources[0]) : mergeAll(concurrent)(from(sources, scheduler)); } var isArray$4 = Array.isArray; function argsOrArgArray(args) { return args.length === 1 && isArray$4(args[0]) ? args[0] : args; } function filter(predicate, thisArg) { return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { return predicate.call(thisArg, value, index++) && subscriber.next(value); })); }); } function zip() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var resultSelector = popResultSelector(args); var sources = argsOrArgArray(args); return sources.length ? new Observable(function (subscriber) { var buffers = sources.map(function () { return []; }); var completed = sources.map(function () { return false; }); subscriber.add(function () { buffers = completed = null; }); var _loop_1 = function (sourceIndex) { innerFrom(sources[sourceIndex]).subscribe(createOperatorSubscriber(subscriber, function (value) { buffers[sourceIndex].push(value); if (buffers.every(function (buffer) { return buffer.length; })) { var result = buffers.map(function (buffer) { return buffer.shift(); }); subscriber.next(resultSelector ? resultSelector.apply(void 0, __spreadArray([], __read(result))) : result); if (buffers.some(function (buffer, i) { return !buffer.length && completed[i]; })) { subscriber.complete(); } } }, function () { completed[sourceIndex] = true; !buffers[sourceIndex].length && subscriber.complete(); })); }; for (var sourceIndex = 0; !subscriber.closed && sourceIndex < sources.length; sourceIndex++) { _loop_1(sourceIndex); } return function () { buffers = completed = null; }; }) : EMPTY$1; } function audit(durationSelector) { return operate(function (source, subscriber) { var hasValue = false; var lastValue = null; var durationSubscriber = null; var isComplete = false; var endDuration = function () { durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe(); durationSubscriber = null; if (hasValue) { hasValue = false; var value = lastValue; lastValue = null; subscriber.next(value); } isComplete && subscriber.complete(); }; var cleanupDuration = function () { durationSubscriber = null; isComplete && subscriber.complete(); }; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; lastValue = value; if (!durationSubscriber) { innerFrom(durationSelector(value)).subscribe((durationSubscriber = createOperatorSubscriber(subscriber, endDuration, cleanupDuration))); } }, function () { isComplete = true; (!hasValue || !durationSubscriber || durationSubscriber.closed) && subscriber.complete(); })); }); } function auditTime(duration, scheduler) { if (scheduler === void 0) { scheduler = asyncScheduler; } return audit(function () { return timer(duration, scheduler); }); } function bufferCount(bufferSize, startBufferEvery) { if (startBufferEvery === void 0) { startBufferEvery = null; } startBufferEvery = startBufferEvery !== null && startBufferEvery !== void 0 ? startBufferEvery : bufferSize; return operate(function (source, subscriber) { var buffers = []; var count = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var e_1, _a, e_2, _b; var toEmit = null; if (count++ % startBufferEvery === 0) { buffers.push([]); } try { for (var buffers_1 = __values(buffers), buffers_1_1 = buffers_1.next(); !buffers_1_1.done; buffers_1_1 = buffers_1.next()) { var buffer = buffers_1_1.value; buffer.push(value); if (bufferSize <= buffer.length) { toEmit = toEmit !== null && toEmit !== void 0 ? toEmit : []; toEmit.push(buffer); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (buffers_1_1 && !buffers_1_1.done && (_a = buffers_1.return)) _a.call(buffers_1); } finally { if (e_1) throw e_1.error; } } if (toEmit) { try { for (var toEmit_1 = __values(toEmit), toEmit_1_1 = toEmit_1.next(); !toEmit_1_1.done; toEmit_1_1 = toEmit_1.next()) { var buffer = toEmit_1_1.value; arrRemove(buffers, buffer); subscriber.next(buffer); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (toEmit_1_1 && !toEmit_1_1.done && (_b = toEmit_1.return)) _b.call(toEmit_1); } finally { if (e_2) throw e_2.error; } } } }, function () { var e_3, _a; try { for (var buffers_2 = __values(buffers), buffers_2_1 = buffers_2.next(); !buffers_2_1.done; buffers_2_1 = buffers_2.next()) { var buffer = buffers_2_1.value; subscriber.next(buffer); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (buffers_2_1 && !buffers_2_1.done && (_a = buffers_2.return)) _a.call(buffers_2); } finally { if (e_3) throw e_3.error; } } subscriber.complete(); }, undefined, function () { buffers = null; })); }); } function bufferWhen(closingSelector) { return operate(function (source, subscriber) { var buffer = null; var closingSubscriber = null; var openBuffer = function () { closingSubscriber === null || closingSubscriber === void 0 ? void 0 : closingSubscriber.unsubscribe(); var b = buffer; buffer = []; b && subscriber.next(b); innerFrom(closingSelector()).subscribe((closingSubscriber = createOperatorSubscriber(subscriber, openBuffer, noop))); }; openBuffer(); source.subscribe(createOperatorSubscriber(subscriber, function (value) { return buffer === null || buffer === void 0 ? void 0 : buffer.push(value); }, function () { buffer && subscriber.next(buffer); subscriber.complete(); }, undefined, function () { return (buffer = closingSubscriber = null); })); }); } function catchError(selector) { return operate(function (source, subscriber) { var innerSub = null; var syncUnsub = false; var handledResult; innerSub = source.subscribe(createOperatorSubscriber(subscriber, undefined, undefined, function (err) { handledResult = innerFrom(selector(err, catchError(selector)(source))); if (innerSub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } else { syncUnsub = true; } })); if (syncUnsub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } }); } function scanInternals(accumulator, seed, hasSeed, emitOnNext, emitBeforeComplete) { return function (source, subscriber) { var hasState = hasSeed; var state = seed; var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var i = index++; state = hasState ? accumulator(state, value, i) : ((hasState = true), value); emitOnNext && subscriber.next(state); }, emitBeforeComplete && (function () { hasState && subscriber.next(state); subscriber.complete(); }))); }; } function reduce(accumulator, seed) { return operate(scanInternals(accumulator, seed, arguments.length >= 2, false, true)); } function concatMap(project, resultSelector) { return isFunction(resultSelector) ? mergeMap(project, resultSelector, 1) : mergeMap(project, 1); } function fromSubscribable(subscribable) { return new Observable(function (subscriber) { return subscribable.subscribe(subscriber); }); } var DEFAULT_CONFIG = { connector: function () { return new Subject(); }, }; function connect(selector, config) { if (config === void 0) { config = DEFAULT_CONFIG; } var connector = config.connector; return operate(function (source, subscriber) { var subject = connector(); innerFrom(selector(fromSubscribable(subject))).subscribe(subscriber); subscriber.add(source.subscribe(subject)); }); } function debounceTime(dueTime, scheduler) { if (scheduler === void 0) { scheduler = asyncScheduler; } return operate(function (source, subscriber) { var activeTask = null; var lastValue = null; var lastTime = null; var emit = function () { if (activeTask) { activeTask.unsubscribe(); activeTask = null; var value = lastValue; lastValue = null; subscriber.next(value); } }; function emitWhenIdle() { var targetTime = lastTime + dueTime; var now = scheduler.now(); if (now < targetTime) { activeTask = this.schedule(undefined, targetTime - now); subscriber.add(activeTask); return; } emit(); } source.subscribe(createOperatorSubscriber(subscriber, function (value) { lastValue = value; lastTime = scheduler.now(); if (!activeTask) { activeTask = scheduler.schedule(emitWhenIdle, dueTime); subscriber.add(activeTask); } }, function () { emit(); subscriber.complete(); }, undefined, function () { lastValue = activeTask = null; })); }); } function defaultIfEmpty(defaultValue) { return operate(function (source, subscriber) { var hasValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; subscriber.next(value); }, function () { if (!hasValue) { subscriber.next(defaultValue); } subscriber.complete(); })); }); } function take(count) { return count <= 0 ? function () { return EMPTY$1; } : operate(function (source, subscriber) { var seen = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (++seen <= count) { subscriber.next(value); if (count <= seen) { subscriber.complete(); } } })); }); } function distinctUntilChanged(comparator, keySelector) { if (keySelector === void 0) { keySelector = identity; } comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare$3; return operate(function (source, subscriber) { var previousKey; var first = true; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var currentKey = keySelector(value); if (first || !comparator(previousKey, currentKey)) { first = false; previousKey = currentKey; subscriber.next(value); } })); }); } function defaultCompare$3(a, b) { return a === b; } function throwIfEmpty(errorFactory) { if (errorFactory === void 0) { errorFactory = defaultErrorFactory; } return operate(function (source, subscriber) { var hasValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; subscriber.next(value); }, function () { return (hasValue ? subscriber.complete() : subscriber.error(errorFactory())); })); }); } function defaultErrorFactory() { return new EmptyError(); } function expand(project, concurrent, scheduler) { if (concurrent === void 0) { concurrent = Infinity; } concurrent = (concurrent || 0) < 1 ? Infinity : concurrent; return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent, undefined, true, scheduler); }); } function finalize(callback) { return operate(function (source, subscriber) { try { source.subscribe(subscriber); } finally { subscriber.add(callback); } }); } function first(predicate, defaultValue) { var hasDefaultValue = arguments.length >= 2; return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, take(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; } function takeLast(count) { return count <= 0 ? function () { return EMPTY$1; } : operate(function (source, subscriber) { var buffer = []; source.subscribe(createOperatorSubscriber(subscriber, function (value) { buffer.push(value); count < buffer.length && buffer.shift(); }, function () { var e_1, _a; try { for (var buffer_1 = __values(buffer), buffer_1_1 = buffer_1.next(); !buffer_1_1.done; buffer_1_1 = buffer_1.next()) { var value = buffer_1_1.value; subscriber.next(value); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (buffer_1_1 && !buffer_1_1.done && (_a = buffer_1.return)) _a.call(buffer_1); } finally { if (e_1) throw e_1.error; } } subscriber.complete(); }, undefined, function () { buffer = null; })); }); } function last(predicate, defaultValue) { var hasDefaultValue = arguments.length >= 2; return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, takeLast(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; } function multicast(subjectOrSubjectFactory, selector) { var subjectFactory = isFunction(subjectOrSubjectFactory) ? subjectOrSubjectFactory : function () { return subjectOrSubjectFactory; }; if (isFunction(selector)) { return connect(selector, { connector: subjectFactory, }); } return function (source) { return new ConnectableObservable(source, subjectFactory); }; } function pairwise() { return operate(function (source, subscriber) { var prev; var hasPrev = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var p = prev; prev = value; hasPrev && subscriber.next([p, value]); hasPrev = true; })); }); } function pluck() { var properties = []; for (var _i = 0; _i < arguments.length; _i++) { properties[_i] = arguments[_i]; } var length = properties.length; if (length === 0) { throw new Error("list of properties cannot be empty."); } return map(function (x) { var currentProp = x; for (var i = 0; i < length; i++) { var p = currentProp === null || currentProp === void 0 ? void 0 : currentProp[properties[i]]; if (typeof p !== "undefined") { currentProp = p; } else { return undefined; } } return currentProp; }); } function publish(selector) { return selector ? function (source) { return connect(selector)(source); } : function (source) { return multicast(new Subject())(source); }; } function publishReplay(bufferSize, windowTime, selectorOrScheduler, timestampProvider) { if (selectorOrScheduler && !isFunction(selectorOrScheduler)) { timestampProvider = selectorOrScheduler; } var selector = isFunction(selectorOrScheduler) ? selectorOrScheduler : undefined; return function (source) { return multicast(new ReplaySubject(bufferSize, windowTime, timestampProvider), selector)(source); }; } function retry(configOrCount) { if (configOrCount === void 0) { configOrCount = Infinity; } var config; if (configOrCount && typeof configOrCount === "object") { config = configOrCount; } else { config = { count: configOrCount, }; } var _a = config.count, count = _a === void 0 ? Infinity : _a, delay = config.delay, _b = config.resetOnSuccess, resetOnSuccess = _b === void 0 ? false : _b; return count <= 0 ? identity : operate(function (source, subscriber) { var soFar = 0; var innerSub; var subscribeForRetry = function () { var syncUnsub = false; innerSub = source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (resetOnSuccess) { soFar = 0; } subscriber.next(value); }, undefined, function (err) { if (soFar++ < count) { var resub_1 = function () { if (innerSub) { innerSub.unsubscribe(); innerSub = null; subscribeForRetry(); } else { syncUnsub = true; } }; if (delay != null) { var notifier = typeof delay === "number" ? timer(delay) : innerFrom(delay(err, soFar)); var notifierSubscriber_1 = createOperatorSubscriber(subscriber, function () { notifierSubscriber_1.unsubscribe(); resub_1(); }, function () { subscriber.complete(); }); notifier.subscribe(notifierSubscriber_1); } else { resub_1(); } } else { subscriber.error(err); } })); if (syncUnsub) { innerSub.unsubscribe(); innerSub = null; subscribeForRetry(); } }; subscribeForRetry(); }); } function sample(notifier) { return operate(function (source, subscriber) { var hasValue = false; var lastValue = null; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; lastValue = value; })); innerFrom(notifier).subscribe(createOperatorSubscriber(subscriber, function () { if (hasValue) { hasValue = false; var value = lastValue; lastValue = null; subscriber.next(value); } }, noop)); }); } function scan(accumulator, seed) { return operate(scanInternals(accumulator, seed, arguments.length >= 2, true)); } function share(options) { if (options === void 0) { options = {}; } var _a = options.connector, connector = _a === void 0 ? function () { return new Subject(); } : _a, _b = options.resetOnError, resetOnError = _b === void 0 ? true : _b, _c = options.resetOnComplete, resetOnComplete = _c === void 0 ? true : _c, _d = options.resetOnRefCountZero, resetOnRefCountZero = _d === void 0 ? true : _d; return function (wrapperSource) { var connection; var resetConnection; var subject; var refCount = 0; var hasCompleted = false; var hasErrored = false; var cancelReset = function () { resetConnection === null || resetConnection === void 0 ? void 0 : resetConnection.unsubscribe(); resetConnection = undefined; }; var reset = function () { cancelReset(); connection = subject = undefined; hasCompleted = hasErrored = false; }; var resetAndUnsubscribe = function () { var conn = connection; reset(); conn === null || conn === void 0 ? void 0 : conn.unsubscribe(); }; return operate(function (source, subscriber) { refCount++; if (!hasErrored && !hasCompleted) { cancelReset(); } var dest = (subject = subject !== null && subject !== void 0 ? subject : connector()); subscriber.add(function () { refCount--; if (refCount === 0 && !hasErrored && !hasCompleted) { resetConnection = handleReset(resetAndUnsubscribe, resetOnRefCountZero); } }); dest.subscribe(subscriber); if (!connection && refCount > 0) { connection = new SafeSubscriber({ next: function (value) { return dest.next(value); }, error: function (err) { hasErrored = true; cancelReset(); resetConnection = handleReset(reset, resetOnError, err); dest.error(err); }, complete: function () { hasCompleted = true; cancelReset(); resetConnection = handleReset(reset, resetOnComplete); dest.complete(); }, }); innerFrom(source).subscribe(connection); } })(wrapperSource); }; } function handleReset(reset, on) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } if (on === true) { reset(); return; } if (on === false) { return; } var onSubscriber = new SafeSubscriber({ next: function () { onSubscriber.unsubscribe(); reset(); }, }); return innerFrom(on.apply(void 0, __spreadArray([], __read(args)))).subscribe(onSubscriber); } function skip(count) { return filter(function (_, index) { return count <= index; }); } function skipWhile(predicate) { return operate(function (source, subscriber) { var taking = false; var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { return (taking || (taking = !predicate(value, index++))) && subscriber.next(value); })); }); } function startWith() { var values = []; for (var _i = 0; _i < arguments.length; _i++) { values[_i] = arguments[_i]; } var scheduler = popScheduler(values); return operate(function (source, subscriber) { (scheduler ? concat(values, source, scheduler) : concat(values, source)).subscribe(subscriber); }); } function switchMap(project, resultSelector) { return operate(function (source, subscriber) { var innerSubscriber = null; var index = 0; var isComplete = false; var checkComplete = function () { return isComplete && !innerSubscriber && subscriber.complete(); }; source.subscribe(createOperatorSubscriber(subscriber, function (value) { innerSubscriber === null || innerSubscriber === void 0 ? void 0 : innerSubscriber.unsubscribe(); var innerIndex = 0; var outerIndex = index++; innerFrom(project(value, outerIndex)).subscribe((innerSubscriber = createOperatorSubscriber(subscriber, function (innerValue) { return subscriber.next(resultSelector ? resultSelector(value, innerValue, outerIndex, innerIndex++) : innerValue); }, function () { innerSubscriber = null; checkComplete(); }))); }, function () { isComplete = true; checkComplete(); })); }); } function takeUntil(notifier) { return operate(function (source, subscriber) { innerFrom(notifier).subscribe(createOperatorSubscriber(subscriber, function () { return subscriber.complete(); }, noop)); !subscriber.closed && source.subscribe(subscriber); }); } function takeWhile(predicate, inclusive) { if (inclusive === void 0) { inclusive = false; } return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var result = predicate(value, index++); (result || inclusive) && subscriber.next(value); !result && subscriber.complete(); })); }); } function tap(observerOrNext, error, complete) { var tapObserver = isFunction(observerOrNext) || error || complete ? { next: observerOrNext, error: error, complete: complete } : observerOrNext; return tapObserver ? operate(function (source, subscriber) { var _a; (_a = tapObserver.subscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); var isUnsub = true; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var _a; (_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value); subscriber.next(value); }, function () { var _a; isUnsub = false; (_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver); subscriber.complete(); }, function (err) { var _a; isUnsub = false; (_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err); subscriber.error(err); }, function () { var _a, _b; if (isUnsub) { (_a = tapObserver.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); } (_b = tapObserver.finalize) === null || _b === void 0 ? void 0 : _b.call(tapObserver); })); }) : identity; } function withLatestFrom() { var inputs = []; for (var _i = 0; _i < arguments.length; _i++) { inputs[_i] = arguments[_i]; } var project = popResultSelector(inputs); return operate(function (source, subscriber) { var len = inputs.length; var otherValues = new Array(len); var hasValue = inputs.map(function () { return false; }); var ready = false; var _loop_1 = function (i) { innerFrom(inputs[i]).subscribe(createOperatorSubscriber(subscriber, function (value) { otherValues[i] = value; if (!ready && !hasValue[i]) { hasValue[i] = true; (ready = hasValue.every(identity)) && (hasValue = null); } }, noop)); }; for (var i = 0; i < len; i++) { _loop_1(i); } source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (ready) { var values = __spreadArray([value], __read(otherValues)); subscriber.next(project ? project.apply(void 0, __spreadArray([], __read(values))) : values); } })); }); } /** * @class Filter * * @classdesc Represents a class for creating image filters. Implementation and * definitions based on https://github.com/mapbox/feature-filter. */ class FilterCreator { /** * Create a filter from a filter expression. * * @description The following filters are supported: * * Comparison * `==` * `!=` * `<` * `<=` * `>` * `>=` * * Set membership * `in` * `!in` * * Combining * `all` * * @param {FilterExpression} filter - Comparison, set membership or combinding filter * expression. * @returns {FilterFunction} Function taking a image and returning a boolean that * indicates whether the image passed the test or not. */ createFilter(filter) { return new Function("node", "return " + this._compile(filter) + ";"); } _compile(filter) { if (filter == null || filter.length <= 1) { return "true"; } const operator = filter[0]; const operation = operator === "==" ? this._compileComparisonOp("===", filter[1], filter[2], false) : operator === "!=" ? this._compileComparisonOp("!==", filter[1], filter[2], false) : operator === ">" || operator === ">=" || operator === "<" || operator === "<=" ? this._compileComparisonOp(operator, filter[1], filter[2], true) : operator === "in" ? this._compileInOp(filter[1], filter.slice(2)) : operator === "!in" ? this._compileNegation(this._compileInOp(filter[1], filter.slice(2))) : operator === "all" ? this._compileLogicalOp(filter.slice(1), "&&") : "true"; return "(" + operation + ")"; } _compare(a, b) { return a < b ? -1 : a > b ? 1 : 0; } _compileComparisonOp(operator, property, value, checkType) { const left = this._compilePropertyReference(property); const right = JSON.stringify(value); return (checkType ? "typeof " + left + "===typeof " + right + "&&" : "") + left + operator + right; } _compileInOp(property, values) { const compare = this._compare; const left = JSON.stringify(values.sort(compare)); const right = this._compilePropertyReference(property); return left + ".indexOf(" + right + ")!==-1"; } _compileLogicalOp(filters, operator) { const compile = this._compile.bind(this); return filters.map(compile).join(operator); } _compileNegation(expression) { return "!(" + expression + ")"; } _compilePropertyReference(property) { return "node[" + JSON.stringify(property) + "]"; } } /** * @license * Copyright 2010-2023 Three.js Authors * SPDX-License-Identifier: MIT */ const REVISION = "152"; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; /** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */ const LinearEncoding = 3000; /** @deprecated Use SRGBColorSpace in three.js r152+. */ const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ""; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const DisplayP3ColorSpace = "display-p3"; const KeepStencilOp = 7680; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; const listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } } hasEventListener( type, listener ) { if ( this._listeners === undefined ) return false; const listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; } removeEventListener( type, listener ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { const index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } } dispatchEvent( event ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice( 0 ); for ( let i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" ]; let _seed = 1234567; const DEG2RAD$1 = Math.PI / 180; const RAD2DEG$1 = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + "-" + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + "-" + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + "-" + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + "-" + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp$1( value, min, max ) { return Math.max( min, Math.min( max, value ) ); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { return ( ( n % m ) + m ) % m; } // Linear mapping from range to range function mapLinear( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp( x, y, value ) { if ( x !== y ) { return ( value - x ) / ( y - x ); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp( x, y, t ) { return ( 1 - t ) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp( x, y, lambda, dt ) { return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong( x, length = 1 ) { return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); } function smootherstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); } // Random integer from interval function randInt( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); } // Random float from interval function randFloat( low, high ) { return low + Math.random() * ( high - low ); } // Random float from <-range/2, range/2> interval function randFloatSpread( range ) { return range * ( 0.5 - Math.random() ); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { if ( s !== undefined ) _seed = s; // Mulberry32 generator let t = _seed += 0x6D2B79F5; t = Math.imul( t ^ t >>> 15, t | 1 ); t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } function degToRad( degrees ) { return degrees * DEG2RAD$1; } function radToDeg( radians ) { return radians * RAD2DEG$1; } function isPowerOfTwo( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; } function ceilPowerOfTwo( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); } function floorPowerOfTwo( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } function setQuaternionFromProperEuler( q, a, b, c, order ) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos( b / 2 ); const s2 = sin( b / 2 ); const c13 = cos( ( a + c ) / 2 ); const s13 = sin( ( a + c ) / 2 ); const c1_3 = cos( ( a - c ) / 2 ); const s1_3 = sin( ( a - c ) / 2 ); const c3_1 = cos( ( c - a ) / 2 ); const s3_1 = sin( ( c - a ) / 2 ); switch ( order ) { case "XYX": q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); break; case "YZY": q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); break; case "ZXZ": q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); break; case "XZX": q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); break; case "YXY": q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); break; case "ZYZ": q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order ); } } function denormalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int16Array: return Math.max( value / 32767.0, - 1.0 ); case Int8Array: return Math.max( value / 127.0, - 1.0 ); default: throw new Error( "Invalid component type." ); } } function normalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint16Array: return Math.round( value * 65535.0 ); case Uint8Array: return Math.round( value * 255.0 ); case Int16Array: return Math.round( value * 32767.0 ); case Int8Array: return Math.round( value * 127.0 ); default: throw new Error( "Invalid component type." ); } } const MathUtils = { DEG2RAD: DEG2RAD$1, RAD2DEG: RAD2DEG$1, generateUUID: generateUUID, clamp: clamp$1, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize }; class Vector2 { constructor( x = 0, y = 0 ) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width( value ) { this.x = value; } get height() { return this.y; } set height( value ) { this.y = value; } set( x, y ) { this.x = x; this.y = y; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y ); } copy( v ) { this.x = v.x; this.y = v.y; return this; } add( v ) { this.x += v.x; this.y += v.y; return this; } addScalar( s ) { this.x += s; this.y += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; return this; } subScalar( s ) { this.x -= s; this.y -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; return this; } divide( v ) { this.x /= v.x; this.y /= v.y; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } applyMatrix3( m ) { const x = this.x, y = this.y; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; } negate() { this.x = - this.x; this.y = - this.y; return this; } dot( v ) { return this.x * v.x + this.y * v.y; } cross( v ) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt( this.x * this.x + this.y * this.y ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ); } normalize() { return this.divideScalar( this.length() || 1 ); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2( - this.y, - this.x ) + Math.PI; return angle; } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp$1( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; } rotateAround( center, angle ) { const c = Math.cos( angle ), s = Math.sin( angle ); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; } } class Matrix3 { constructor() { Matrix3.prototype.isMatrix3 = true; this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; } set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { const te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; } identity() { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrix3Column( this, 0 ); yAxis.setFromMatrix3Column( this, 1 ); zAxis.setFromMatrix3Column( this, 2 ); return this; } setFromMatrix4( m ) { const me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; } determinant() { const te = this.elements; const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; } getNormalMatrix( matrix4 ) { return this.setFromMatrix4( matrix4 ).invert().transpose(); } transposeIntoArray( r ) { const m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; } setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { const c = Math.cos( rotation ); const s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); return this; } // scale( sx, sy ) { this.premultiply( _m3.makeScale( sx, sy ) ); return this; } rotate( theta ) { this.premultiply( _m3.makeRotation( - theta ) ); return this; } translate( tx, ty ) { this.premultiply( _m3.makeTranslation( tx, ty ) ); return this; } // for 2D Transforms makeTranslation( x, y ) { this.set( 1, 0, x, 0, 1, y, 0, 0, 1 ); return this; } makeRotation( theta ) { // counterclockwise const c = Math.cos( theta ); const s = Math.sin( theta ); this.set( c, - s, 0, s, c, 0, 0, 0, 1 ); return this; } makeScale( x, y ) { this.set( x, 0, 0, 0, y, 0, 0, 0, 1 ); return this; } // equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } clone() { return new this.constructor().fromArray( this.elements ); } } const _m3 = /*@__PURE__*/ new Matrix3(); function arrayNeedsUint32( array ) { // assumes larger values usually on last for ( let i = array.length - 1; i >= 0; -- i ) { if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } function createElementNS( name ) { return document.createElementNS( "http://www.w3.org/1999/xhtml", name ); } const _cache = {}; function warnOnce( message ) { if ( message in _cache ) return; _cache[ message ] = true; console.warn( message ); } function SRGBToLinear( c ) { return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); } function LinearToSRGB( c ) { return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().fromArray( [ 0.8224621, 0.0331941, 0.0170827, 0.1775380, 0.9668058, 0.0723974, - 0.0000001, 0.0000001, 0.9105199 ] ); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().fromArray( [ 1.2249401, - 0.0420569, - 0.0196376, - 0.2249404, 1.0420571, - 0.0786361, 0.0000001, 0.0000000, 1.0982735 ] ); function DisplayP3ToLinearSRGB( color ) { // Display P3 uses the sRGB transfer functions return color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ); } function LinearSRGBToDisplayP3( color ) { // Display P3 uses the sRGB transfer functions return color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(); } // Conversions from to Linear-sRGB reference space. const TO_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertSRGBToLinear(), [ DisplayP3ColorSpace ]: DisplayP3ToLinearSRGB, }; // Conversions to from Linear-sRGB reference space. const FROM_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertLinearToSRGB(), [ DisplayP3ColorSpace ]: LinearSRGBToDisplayP3, }; const ColorManagement = { enabled: true, get legacyMode() { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); return ! this.enabled; }, set legacyMode( legacyMode ) { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); this.enabled = ! legacyMode; }, get workingColorSpace() { return LinearSRGBColorSpace; }, set workingColorSpace( colorSpace ) { console.warn( "THREE.ColorManagement: .workingColorSpace is readonly." ); }, convert: function ( color, sourceColorSpace, targetColorSpace ) { if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { return color; } const sourceToLinear = TO_LINEAR[ sourceColorSpace ]; const targetFromLinear = FROM_LINEAR[ targetColorSpace ]; if ( sourceToLinear === undefined || targetFromLinear === undefined ) { throw new Error( `Unsupported color space conversion, "${ sourceColorSpace }" to "${ targetColorSpace }".` ); } return targetFromLinear( sourceToLinear( color ) ); }, fromWorkingColorSpace: function ( color, targetColorSpace ) { return this.convert( color, this.workingColorSpace, targetColorSpace ); }, toWorkingColorSpace: function ( color, sourceColorSpace ) { return this.convert( color, sourceColorSpace, this.workingColorSpace ); }, }; let _canvas; class ImageUtils { static getDataURL( image ) { if ( /^data:/i.test( image.src ) ) { return image.src; } if ( typeof HTMLCanvasElement === "undefined" ) { return image.src; } let canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { if ( _canvas === undefined ) _canvas = createElementNS( "canvas" ); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } canvas = _canvas; } if ( canvas.width > 2048 || canvas.height > 2048 ) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image ); return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } static sRGBToLinear( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const canvas = createElementNS( "canvas" ); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height ); const imageData = context.getImageData( 0, 0, image.width, image.height ); const data = imageData.data; for ( let i = 0; i < data.length; i ++ ) { data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; } context.putImageData( imageData, 0, 0 ); return canvas; } else if ( image.data ) { const data = image.data.slice( 0 ); for ( let i = 0; i < data.length; i ++ ) { if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); } else { // assuming float data[ i ] = SRGBToLinear( data[ i ] ); } } return { data: data, width: image.width, height: image.height }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied." ); return image; } } } class Source { constructor( data = null ) { this.isSource = true; this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { return meta.images[ this.uuid ]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if ( data !== null ) { let url; if ( Array.isArray( data ) ) { // cube texture url = []; for ( let i = 0, l = data.length; i < l; i ++ ) { if ( data[ i ].isDataTexture ) { url.push( serializeImage( data[ i ].image ) ); } else { url.push( serializeImage( data[ i ] ) ); } } } else { // texture url = serializeImage( data ); } output.url = url; } if ( ! isRootObject ) { meta.images[ this.uuid ] = output; } return output; } } function serializeImage( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { // default images return ImageUtils.getDataURL( image ); } else { if ( image.data ) { // images of DataTexture return { data: Array.from( image.data ), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn( "THREE.Texture: Unable to serialize Texture." ); return {}; } } } let textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { super(); this.isTexture = true; Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = generateUUID(); this.name = ""; this.source = new Source( image ); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) if ( typeof colorSpace === "string" ) { this.colorSpace = colorSpace; } else { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image( value = null ) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.needsUpdate = true; return this; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } const output = { metadata: { version: 4.5, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON( meta ).uuid, mapping: this.mapping, channel: this.channel, 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, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment }; if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; } dispose() { this.dispatchEvent( { type: "dispose" } ); } transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } return uv; } set needsUpdate( value ) { if ( value === true ) { this.version ++; this.source.needsUpdate = true; } } get encoding() { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set encoding( encoding ) { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor( x = 0, y = 0, z = 0, w = 1 ) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width( value ) { this.z = value; } get height() { return this.w; } set height( value ) { this.w = value; } set( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setW( w ) { this.w = w; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z, this.w ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } setAxisAngleFromQuaternion( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); const s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = ( m11 + 1 ) / 2; const yy = ( m22 + 1 ) / 2; const zz = ( m33 + 1 ) / 2; const xy = ( m12 + m21 ) / 4; const xz = ( m13 + m31 ) / 4; const yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor( width = 1, height = 1, options = {} ) { super(); this.isWebGLRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); const image = { width: width, height: height, depth: 1 }; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); this.texture.isRenderTargetTexture = true; this.texture.flipY = false; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy( source.scissor ); this.scissorTest = source.scissorTest; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.texture.isRenderTargetTexture = true; // ensure image object is not shared, see #20328 const image = Object.assign( {}, source.texture.image ); this.texture.source = new Source( image ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class DataArrayTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { super( null ); this.isDataArrayTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class Data3DTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super( null ); this.isData3DTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( t === 0 ) { dst[ dstOffset + 0 ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; return; } if ( t === 1 ) { dst[ dstOffset + 0 ] = x1; dst[ dstOffset + 1 ] = y1; dst[ dstOffset + 2 ] = z1; dst[ dstOffset + 3 ] = w1; return; } if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { const sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { const x0 = src0[ srcOffset0 ]; const y0 = src0[ srcOffset0 + 1 ]; const z0 = src0[ srcOffset0 + 2 ]; const w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 ]; const y1 = src1[ srcOffset1 + 1 ]; const z1 = src1[ srcOffset1 + 2 ]; const w1 = src1[ srcOffset1 + 3 ]; dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w( value ) { this._w = value; this._onChangeCallback(); } set( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._w ); } copy( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler( euler, update ) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos( x / 2 ); const c2 = cos( y / 2 ); const c3 = cos( z / 2 ); const s1 = sin( x / 2 ); const s2 = sin( y / 2 ); const s3 = sin( z / 2 ); switch ( order ) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order ); } if ( update !== false ) this._onChangeCallback(); return this; } setFromAxisAngle( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this._onChangeCallback(); return this; } setFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33; if ( trace > 0 ) { const s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors( vFrom, vTo ) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot( vTo ) + 1; if ( r < Number.EPSILON ) { // vFrom and vTo point in opposite directions r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { this._x = - vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = - vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo( q ) { return 2 * Math.acos( Math.abs( clamp$1( this.dot( q ), - 1, 1 ) ) ); } rotateTowards( q, step ) { const angle = this.angleTo( q ); if ( angle === 0 ) return this; const t = Math.min( 1, step / angle ); this.slerp( q, t ); return this; } identity() { return this.set( 0, 0, 0, 1 ); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= - 1; this._y *= - 1; this._z *= - 1; this._onChangeCallback(); return this; } dot( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } normalize() { let l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply( q ) { return this.multiplyQuaternions( this, q ); } premultiply( q ) { return this.multiplyQuaternions( q, this ); } multiplyQuaternions( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if ( sqrSinHalfTheta <= Number.EPSILON ) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this._onChangeCallback(); return this; } slerpQuaternions( qa, qb, t ) { return this.copy( qa ).slerp( qb, t ); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt( 1 - u1 ); const sqrtu1 = Math.sqrt( u1 ); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set( sqrt1u1 * Math.cos( u2 ), sqrtu1 * Math.sin( u3 ), sqrtu1 * Math.cos( u3 ), sqrt1u1 * Math.sin( u2 ), ); } equals( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } fromArray( array, offset = 0 ) { this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; } fromBufferAttribute( attribute, index ) { this._x = attribute.getX( index ); this._y = attribute.getY( index ); this._z = attribute.getZ( index ); this._w = attribute.getW( index ); return this; } toJSON() { return this.toArray(); } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor( x = 0, y = 0, z = 0 ) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set( x, y, z ) { if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler( euler ) { return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } applyAxisAngle( axis, angle ) { return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); } applyMatrix3( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; } applyNormalMatrix( m ) { return this.applyMatrix3( m ).normalize(); } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; } applyQuaternion( q ) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; } project( camera ) { return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } unproject( camera ) { return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); } transformDirection( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); } divide( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; return this; } cross( v ) { return this.crossVectors( this, v ); } crossVectors( a, b ) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector( v ) { const denominator = v.lengthSq(); if ( denominator === 0 ) return this.set( 0, 0, 0 ); const scalar = v.dot( this ) / denominator; return this.copy( v ).multiplyScalar( scalar ); } projectOnPlane( planeNormal ) { _vector$b.copy( this ).projectOnVector( planeNormal ); return this.sub( _vector$b ); } reflect( normal ) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$b.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp$1( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); } setFromSpherical( s ) { return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } setFromSphericalCoords( radius, phi, theta ) { const sinPhiRadius = Math.sin( phi ) * radius; this.x = sinPhiRadius * Math.sin( theta ); this.y = Math.cos( phi ) * radius; this.z = sinPhiRadius * Math.cos( theta ); return this; } setFromCylindrical( c ) { return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); } setFromCylindricalCoords( radius, theta, y ) { this.x = radius * Math.sin( theta ); this.y = y; this.z = radius * Math.cos( theta ); return this; } setFromMatrixPosition( m ) { const e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; } setFromMatrixScale( m ) { const sx = this.setFromMatrixColumn( m, 0 ).length(); const sy = this.setFromMatrixColumn( m, 1 ).length(); const sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn( m, index ) { return this.fromArray( m.elements, index * 4 ); } setFromMatrix3Column( m, index ) { return this.fromArray( m.elements, index * 3 ); } setFromEuler( e ) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor( c ) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = ( Math.random() - 0.5 ) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt( 1 - u ** 2 ); this.x = f * Math.cos( t ); this.y = f * Math.sin( t ); this.z = u; return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; } } const _vector$b = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { this.isBox3 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromArray( array ) { this.makeEmpty(); for ( let i = 0, il = array.length; i < il; i += 3 ) { this.expandByPoint( _vector$a.fromArray( array, i ) ); } return this; } setFromBufferAttribute( attribute ) { this.makeEmpty(); for ( let i = 0, il = attribute.count; i < il; i ++ ) { this.expandByPoint( _vector$a.fromBufferAttribute( attribute, i ) ); } return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$a.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } setFromObject( object, precise = false ) { this.makeEmpty(); return this.expandByObject( object, precise ); } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } expandByObject( object, precise = false ) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix( false, false ); if ( object.boundingBox !== undefined ) { if ( object.boundingBox === null ) { object.computeBoundingBox(); } _box$3.copy( object.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } else { const geometry = object.geometry; if ( geometry !== undefined ) { if ( precise && geometry.attributes !== undefined && geometry.attributes.position !== undefined ) { const position = geometry.attributes.position; for ( let i = 0, l = position.count; i < l; i ++ ) { _vector$a.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); this.expandByPoint( _vector$a ); } } else { if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } _box$3.copy( geometry.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { this.expandByObject( children[ i ], precise ); } return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); } intersectsBox( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, _vector$a ); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$a.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); } intersectsPlane( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= - plane.constant && max >= - plane.constant ); } intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( _center ); _extents.subVectors( this.max, _center ); // translate triangle to aabb origin _v0$2.subVectors( triangle.a, _center ); _v1$7.subVectors( triangle.b, _center ); _v2$4.subVectors( triangle.c, _center ); // compute edge vectors for triangle _f0.subVectors( _v1$7, _v0$2 ); _f1.subVectors( _v2$4, _v1$7 ); _f2.subVectors( _v0$2, _v2$4 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors( _f0, _f1 ); axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; return satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ); } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { return this.clampPoint( point, _vector$a ).distanceTo( point ); } getBoundingSphere( target ) { if ( this.isEmpty() ) { target.makeEmpty(); } else { this.getCenter( target.center ); target.radius = this.getSize( _vector$a ).length() * 0.5; } return target; } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } applyMatrix4( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( _points ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3() ]; const _vector$a = /*@__PURE__*/ new Vector3(); const _box$3 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$4 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes( axes, v0, v1, v2, extents ) { for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { _testAxis.fromArray( axes, i ); // project the aabb onto the separating axis const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot( _testAxis ); const p1 = v1.dot( _testAxis ); const p2 = v2.dot( _testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); class Sphere { constructor( center = new Vector3(), radius = - 1 ) { this.center = center; this.radius = radius; } set( center, radius ) { this.center.copy( center ); this.radius = radius; return this; } setFromPoints( points, optionalCenter ) { const center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { _box$2.setFromPoints( points ).getCenter( center ); } let maxRadiusSq = 0; for ( let i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; } copy( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; } isEmpty() { return ( this.radius < 0 ); } makeEmpty() { this.center.set( 0, 0, 0 ); this.radius = - 1; return this; } containsPoint( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); } distanceToPoint( point ) { return ( point.distanceTo( this.center ) - this.radius ); } intersectsSphere( sphere ) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } intersectsBox( box ) { return box.intersectsSphere( this ); } intersectsPlane( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } clampPoint( point, target ) { const deltaLengthSq = this.center.distanceToSquared( point ); target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; } getBoundingBox( target ) { if ( this.isEmpty() ) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; } applyMatrix4( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate( offset ) { this.center.add( offset ); return this; } expandByPoint( point ) { if ( this.isEmpty() ) { this.center.copy( point ); this.radius = 0; return this; } _v1$6.subVectors( point, this.center ); const lengthSq = _v1$6.lengthSq(); if ( lengthSq > ( this.radius * this.radius ) ) { // calculate the minimal sphere const length = Math.sqrt( lengthSq ); const delta = ( length - this.radius ) * 0.5; this.center.addScaledVector( _v1$6, delta / length ); this.radius += delta; } return this; } union( sphere ) { if ( sphere.isEmpty() ) { return this; } if ( this.isEmpty() ) { this.copy( sphere ); return this; } if ( this.center.equals( sphere.center ) === true ) { this.radius = Math.max( this.radius, sphere.radius ); } else { _v2$3.subVectors( sphere.center, this.center ).setLength( sphere.radius ); this.expandByPoint( _v1$6.copy( sphere.center ).add( _v2$3 ) ); this.expandByPoint( _v1$6.copy( sphere.center ).sub( _v2$3 ) ); } return this; } equals( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } clone() { return new this.constructor().copy( this ); } } const _vector$9 = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { this.origin = origin; this.direction = direction; } set( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; } copy( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; } at( t, target ) { return target.copy( this.origin ).addScaledVector( this.direction, t ); } lookAt( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; } recast( t ) { this.origin.copy( this.at( t, _vector$9 ) ); return this; } closestPointToPoint( point, target ) { target.subVectors( point, this.origin ); const directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.origin ).addScaledVector( this.direction, directionDistance ); } distanceToPoint( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); } distanceSqToPoint( point ) { const directionDistance = _vector$9.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } _vector$9.copy( this.origin ).addScaledVector( this.direction, directionDistance ); return _vector$9.distanceToSquared( point ); } distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); _segDir.copy( v1 ).sub( v0 ).normalize(); _diff.copy( this.origin ).sub( _segCenter ); const segExtent = v0.distanceTo( v1 ) * 0.5; const a01 = - this.direction.dot( _segDir ); const b0 = _diff.dot( this.direction ); const b1 = - _diff.dot( _segDir ); const c = _diff.lengthSq(); const det = Math.abs( 1 - a01 * a01 ); let s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 ); } return sqrDist; } intersectSphere( sphere, target ) { _vector$9.subVectors( sphere.center, this.origin ); const tca = _vector$9.dot( this.direction ); const d2 = _vector$9.dot( _vector$9 ) - tca * tca; const radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; const thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if ( t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); } intersectsSphere( sphere ) { return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); } distanceToPlane( plane ) { const denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane( plane, target ) { const t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); } intersectsPlane( plane ) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } const denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox( box, target ) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin; if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); } intersectsBox( box ) { return this.intersectBox( box, _vector$9 ) !== null; } intersectTriangle( a, b, c, backfaceCulling, target ) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors( b, a ); _edge2.subVectors( c, a ); _normal$1.crossVectors( _edge1, _edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot( _normal$1 ); let sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } _diff.subVectors( this.origin, a ); const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. const QdN = - sign * _diff.dot( _normal$1 ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); } applyMatrix4( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; } equals( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } clone() { return new this.constructor().copy( this ); } } class Matrix4 { constructor() { Matrix4.prototype.isMatrix4 = true; this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { const te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; } identity() { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } clone() { return new Matrix4().fromArray( this.elements ); } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; } copyPosition( m ) { const te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; } setFromMatrix3( m ) { const me = m.elements; this.set( me[ 0 ], me[ 3 ], me[ 6 ], 0, me[ 1 ], me[ 4 ], me[ 7 ], 0, me[ 2 ], me[ 5 ], me[ 8 ], 0, 0, 0, 0, 1 ); return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; } makeBasis( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; } extractRotation( m ) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromEuler( euler ) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos( x ), b = Math.sin( x ); const c = Math.cos( y ), d = Math.sin( y ); const e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromQuaternion( q ) { return this.compose( _zero, q, _one ); } lookAt( eye, target, up ) { const te = this.elements; _z.subVectors( eye, target ); if ( _z.lengthSq() === 0 ) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors( up, _z ); if ( _x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors( up, _z ); } _x.normalize(); _y.crossVectors( _z, _x ); te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; } determinant() { const te = this.elements; const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); } transpose() { const te = this.elements; let tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; } setPosition( x, y, z ) { const te = this.elements; if ( x.isVector3 ) { te[ 12 ] = x.x; te[ 13 ] = x.y; te[ 14 ] = x.z; } else { te[ 12 ] = x; te[ 13 ] = y; te[ 14 ] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; } scale( v ) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); } makeTranslation( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; } makeRotationX( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; } makeRotationY( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; } makeRotationZ( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } makeRotationAxis( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos( angle ); const s = Math.sin( angle ); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; } makeScale( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; } makeShear( xy, xz, yx, yz, zx, zy ) { this.set( 1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1 ); return this; } compose( position, quaternion, scale ) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; } decompose( position, quaternion, scale ) { const te = this.elements; let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part _m1$2.copy( this ); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[ 0 ] *= invSX; _m1$2.elements[ 1 ] *= invSX; _m1$2.elements[ 2 ] *= invSX; _m1$2.elements[ 4 ] *= invSY; _m1$2.elements[ 5 ] *= invSY; _m1$2.elements[ 6 ] *= invSY; _m1$2.elements[ 8 ] *= invSZ; _m1$2.elements[ 9 ] *= invSZ; _m1$2.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( _m1$2 ); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far ) { const te = this.elements; const x = 2 * near / ( right - left ); const y = 2 * near / ( top - bottom ); const a = ( right + left ) / ( right - left ); const b = ( top + bottom ) / ( top - bottom ); const c = - ( far + near ) / ( far - near ); const d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far ) { const te = this.elements; const w = 1.0 / ( right - left ); const h = 1.0 / ( top - bottom ); const p = 1.0 / ( far - near ); const x = ( right + left ) * w; const y = ( top + bottom ) * h; const z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; } equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$2 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order( value ) { this._order = value; this._onChangeCallback(); } set( x, y, z, order = this._order ) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._order ); } copy( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix( m, order = this._order, update = true ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; switch ( order ) { case "XYZ": this._y = Math.asin( clamp$1( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } break; case "YXZ": this._x = Math.asin( - clamp$1( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.9999999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } break; case "ZXY": this._x = Math.asin( clamp$1( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.9999999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } break; case "ZYX": this._y = Math.asin( - clamp$1( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.9999999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } break; case "YZX": this._z = Math.asin( clamp$1( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } break; case "XZY": this._z = Math.asin( - clamp$1( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.9999999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order ); } this._order = order; if ( update === true ) this._onChangeCallback(); return this; } setFromQuaternion( q, order, update ) { _matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( _matrix, order, update ); } setFromVector3( v, order = this._order ) { return this.set( v.x, v.y, v.z, order ); } reorder( newOrder ) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler( this ); return this.setFromQuaternion( _quaternion$3, newOrder ); } equals( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); } fromArray( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = "XYZ"; class Layers { constructor() { this.mask = 1 | 0; } set( channel ) { this.mask = ( 1 << channel | 0 ) >>> 0; } enable( channel ) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle( channel ) { this.mask ^= 1 << channel | 0; } disable( channel ) { this.mask &= ~ ( 1 << channel | 0 ); } disableAll() { this.mask = 0; } test( layers ) { return ( this.mask & layers.mask ) !== 0; } isEnabled( channel ) { return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$1 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty( this, "id", { value: _object3DId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation._onChange( onRotationChange ); quaternion._onChange( onQuaternionChange ); Object.defineProperties( this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldNeedsUpdate = false; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} applyMatrix4( matrix ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); this.matrix.premultiply( matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); } applyQuaternion( q ) { this.quaternion.premultiply( q ); return this; } setRotationFromAxisAngle( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); } setRotationFromEuler( euler ) { this.quaternion.setFromEuler( euler, true ); } setRotationFromMatrix( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); } setRotationFromQuaternion( q ) { // assumes q is normalized this.quaternion.copy( q ); } rotateOnAxis( axis, angle ) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( _q1 ); return this; } rotateOnWorldAxis( axis, angle ) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( _q1 ); return this; } rotateX( angle ) { return this.rotateOnAxis( _xAxis, angle ); } rotateY( angle ) { return this.rotateOnAxis( _yAxis, angle ); } rotateZ( angle ) { return this.rotateOnAxis( _zAxis, angle ); } translateOnAxis( axis, distance ) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( _v1$4.multiplyScalar( distance ) ); return this; } translateX( distance ) { return this.translateOnAxis( _xAxis, distance ); } translateY( distance ) { return this.translateOnAxis( _yAxis, distance ); } translateZ( distance ) { return this.translateOnAxis( _zAxis, distance ); } localToWorld( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( this.matrixWorld ); } worldToLocal( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } lookAt( x, y, z ) { // This method does not support objects having non-uniformly-scaled parent(s) if ( x.isVector3 ) { _target.copy( x ); } else { _target.set( x, y, z ); } const parent = this.parent; this.updateWorldMatrix( true, false ); _position$3.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { _m1$1.lookAt( _position$3, _target, this.up ); } else { _m1$1.lookAt( _target, _position$3, this.up ); } this.quaternion.setFromRotationMatrix( _m1$1 ); if ( parent ) { _m1$1.extractRotation( parent.matrixWorld ); _q1.setFromRotationMatrix( _m1$1 ); this.quaternion.premultiply( _q1.invert() ); } } add( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( object && object.isObject3D ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; this.children.push( object ); object.dispatchEvent( _addedEvent ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; } remove( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } const index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; this.children.splice( index, 1 ); object.dispatchEvent( _removedEvent ); } return this; } removeFromParent() { const parent = this.parent; if ( parent !== null ) { parent.remove( this ); } return this; } clear() { for ( let i = 0; i < this.children.length; i ++ ) { const object = this.children[ i ]; object.parent = null; object.dispatchEvent( _removedEvent ); } this.children.length = 0; return this; } attach( object ) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix( true, false ); _m1$1.copy( this.matrixWorld ).invert(); if ( object.parent !== null ) { object.parent.updateWorldMatrix( true, false ); _m1$1.multiply( object.parent.matrixWorld ); } object.applyMatrix4( _m1$1 ); this.add( object ); object.updateWorldMatrix( false, true ); return this; } getObjectById( id ) { return this.getObjectByProperty( "id", id ); } getObjectByName( name ) { return this.getObjectByProperty( "name", name ); } getObjectByProperty( name, value ) { if ( this[ name ] === value ) return this; for ( let i = 0, l = this.children.length; i < l; i ++ ) { const child = this.children[ i ]; const object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; } getObjectsByProperty( name, value ) { let result = []; if ( this[ name ] === value ) result.push( this ); for ( let i = 0, l = this.children.length; i < l; i ++ ) { const childResult = this.children[ i ].getObjectsByProperty( name, value ); if ( childResult.length > 0 ) { result = result.concat( childResult ); } } return result; } getWorldPosition( target ) { this.updateWorldMatrix( true, false ); return target.setFromMatrixPosition( this.matrixWorld ); } getWorldQuaternion( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, target, _scale$2 ); return target; } getWorldScale( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, _quaternion$2, target ); return target; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); } raycast( /* raycaster, intersects */ ) {} traverse( callback ) { callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } } traverseVisible( callback ) { if ( this.visible === false ) return; callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } } traverseAncestors( callback ) { const parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } } updateMatrix() { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true || force === true ) { child.updateMatrixWorld( force ); } } } updateWorldMatrix( updateParents, updateChildren ) { const parent = this.parent; if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { parent.updateWorldMatrix( true, false ); } if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } // update children if ( updateChildren === true ) { const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true ) { child.updateWorldMatrix( false, true ); } } } } toJSON( meta ) { // meta is a string when called from JSON.stringify const isRootObject = ( meta === undefined || typeof meta === "string" ); const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // object specific properties if ( this.isInstancedMesh ) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); } // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.isScene ) { if ( this.background ) { if ( this.background.isColor ) { object.background = this.background.toJSON(); } else if ( this.background.isTexture ) { object.background = this.background.toJSON( meta ).uuid; } } if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { object.environment = this.environment.toJSON( meta ).uuid; } } else if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); const parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { const shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.isSkinnedMesh ) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if ( this.skeleton !== undefined ) { serialize( meta.skeletons, this.skeleton ); object.skeleton = this.skeleton.uuid; } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { const uuids = []; for ( let i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( let i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } // if ( this.animations.length > 0 ) { object.animations = []; for ( let i = 0; i < this.animations.length; i ++ ) { const animation = this.animations[ i ]; object.animations.push( serialize( meta.animations, animation ) ); } } if ( isRootObject ) { const geometries = extractFromCache( meta.geometries ); const materials = extractFromCache( meta.materials ); const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); const shapes = extractFromCache( meta.shapes ); const skeletons = extractFromCache( meta.skeletons ); const animations = extractFromCache( meta.animations ); const nodes = extractFromCache( meta.nodes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; if ( skeletons.length > 0 ) output.skeletons = skeletons; if ( animations.length > 0 ) output.animations = animations; if ( nodes.length > 0 ) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } } clone( recursive ) { return new this.constructor().copy( this, recursive ); } copy( source, recursive = true ) { this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.rotation.order = source.rotation.order; this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( let i = 0; i < source.children.length; i ++ ) { const child = source.children[ i ]; this.add( child.clone() ); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 ); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$1 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); let warnedGetUV = false; class Triangle { constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { this.a = a; this.b = b; this.c = c; } static getNormal( a, b, c, target ) { target.subVectors( c, b ); _v0$1.subVectors( a, b ); target.cross( _v0$1 ); const targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord( point, a, b, c, target ) { _v0$1.subVectors( c, a ); _v1$3.subVectors( b, a ); _v2$2.subVectors( point, a ); const dot00 = _v0$1.dot( _v0$1 ); const dot01 = _v0$1.dot( _v1$3 ); const dot02 = _v0$1.dot( _v2$2 ); const dot11 = _v1$3.dot( _v1$3 ); const dot12 = _v1$3.dot( _v2$2 ); const denom = ( dot00 * dot11 - dot01 * dot01 ); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } const invDenom = 1 / denom; const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); } static containsPoint( point, a, b, c ) { this.getBarycoord( point, a, b, c, _v3$1 ); return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target ); } static getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) { this.getBarycoord( point, p1, p2, p3, _v3$1 ); target.setScalar( 0 ); target.addScaledVector( v1, _v3$1.x ); target.addScaledVector( v2, _v3$1.y ); target.addScaledVector( v3, _v3$1.z ); return target; } static isFrontFacing( a, b, c, direction ) { _v0$1.subVectors( c, b ); _v1$3.subVectors( a, b ); // strictly front facing return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; } set( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; } setFromPointsAndIndices( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; } setFromAttributeAndIndices( attribute, i0, i1, i2 ) { this.a.fromBufferAttribute( attribute, i0 ); this.b.fromBufferAttribute( attribute, i1 ); this.c.fromBufferAttribute( attribute, i2 ); return this; } clone() { return new this.constructor().copy( this ); } copy( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; } getArea() { _v0$1.subVectors( this.c, this.b ); _v1$3.subVectors( this.a, this.b ); return _v0$1.cross( _v1$3 ).length() * 0.5; } getMidpoint( target ) { return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } getNormal( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); } getPlane( target ) { return target.setFromCoplanarPoints( this.a, this.b, this.c ); } getBarycoord( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); } getUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } getInterpolation( point, v1, v2, v3, target ) { return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target ); } containsPoint( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); } isFrontFacing( direction ) { return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); } intersectsBox( box ) { return box.intersectsTriangle( this ); } closestPointToPoint( p, target ) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors( b, a ); _vac.subVectors( c, a ); _vap.subVectors( p, a ); const d1 = _vab.dot( _vap ); const d2 = _vac.dot( _vap ); if ( d1 <= 0 && d2 <= 0 ) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy( a ); } _vbp.subVectors( p, b ); const d3 = _vab.dot( _vbp ); const d4 = _vac.dot( _vbp ); if ( d3 >= 0 && d4 <= d3 ) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy( b ); } const vc = d1 * d4 - d3 * d2; if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { v = d1 / ( d1 - d3 ); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy( a ).addScaledVector( _vab, v ); } _vcp.subVectors( p, c ); const d5 = _vab.dot( _vcp ); const d6 = _vac.dot( _vcp ); if ( d6 >= 0 && d5 <= d6 ) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy( c ); } const vb = d5 * d2 - d1 * d6; if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { w = d2 / ( d2 - d6 ); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy( a ).addScaledVector( _vac, w ); } const va = d3 * d6 - d5 * d4; if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { _vbc.subVectors( c, b ); w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC } // face region const denom = 1 / ( va + vb + vc ); // u = va * denom v = vb * denom; w = vc * denom; return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); } equals( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest( value ) { if ( this._alphaTest > 0 !== value > 0 ) { this.version ++; } this._alphaTest = value; } onBuild( /* shaderobject, renderer */ ) {} onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} onBeforeCompile( /* shaderobject, renderer */ ) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues( values ) { if ( values === undefined ) return; for ( const key in values ) { const newValue = values[ key ]; if ( newValue === undefined ) { console.warn( `THREE.Material: parameter "${ key }" has value of undefined.` ); continue; } const currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( `THREE.Material: "${ key }" is not a property of THREE.${ this.type }.` ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else { this[ key ] = newValue; } } } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( isRootObject ) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.sheen !== undefined ) data.sheen = this.sheen; if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; } if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; } if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; } if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) { data.lightMap = this.lightMap.toJSON( meta ).uuid; data.lightMapIntensity = this.lightMapIntensity; } if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; if ( this.combine !== undefined ) data.combine = this.combine; } if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.transmission !== undefined ) data.transmission = this.transmission; if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; if ( this.thickness !== undefined ) data.thickness = this.thickness; if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); if ( this.size !== undefined ) data.size = this.size; if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors ) data.vertexColors = true; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; if ( this.polygonOffset === true ) data.polygonOffset = true; if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.forceSinglePass === true ) data.forceSinglePass = this.forceSinglePass; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.visible === false ) data.visible = false; if ( this.toneMapped === false ) data.toneMapped = false; if ( this.fog === false ) data.fog = false; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRootObject ) { const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if ( srcPlanes !== null ) { const n = srcPlanes.length; dstPlanes = new Array( n ); for ( let i = 0; i !== n; ++ i ) { dstPlanes[ i ] = srcPlanes[ i ].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse( JSON.stringify( source.userData ) ); return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } set needsUpdate( value ) { if ( value === true ) this.version ++; } } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } class Color { constructor( r, g, b ) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } set( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } return this; } setScalar( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex( hex, colorSpace = SRGBColorSpace ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo( h, 1 ); s = clamp$1( s, 0, 1 ); l = clamp$1( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); const q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setStyle( style, colorSpace = SRGBColorSpace ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } let m; if ( m = /^(w+)(([^)]*))/.exec( style ) ) { // rgb / hsl let color; const name = m[ 1 ]; const components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255, colorSpace ); } if ( color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100, colorSpace ); } break; case "hsl": case "hsla": if ( color = /^s*(d*.?d+)s*,s*(d*.?d+)\%s*,s*(d*.?d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha( color[ 4 ] ); return this.setHSL( parseFloat( color[ 1 ] ) / 360, parseFloat( color[ 2 ] ) / 100, parseFloat( color[ 3 ] ) / 100, colorSpace ); } break; default: console.warn( "THREE.Color: Unknown color model " + style ); } } else if ( m = /^#([A-Fa-fd]+)$/.exec( style ) ) { // hex color const hex = m[ 1 ]; const size = hex.length; if ( size === 3 ) { // #ff0 return this.setRGB( parseInt( hex.charAt( 0 ), 16 ) / 15, parseInt( hex.charAt( 1 ), 16 ) / 15, parseInt( hex.charAt( 2 ), 16 ) / 15, colorSpace ); } else if ( size === 6 ) { // #ff0000 return this.setHex( parseInt( hex, 16 ), colorSpace ); } else { console.warn( "THREE.Color: Invalid hex color " + style ); } } else if ( style && style.length > 0 ) { return this.setColorName( style, colorSpace ); } return this; } setColorName( style, colorSpace = SRGBColorSpace ) { // color keywords const hex = _colorKeywords[ style.toLowerCase() ]; if ( hex !== undefined ) { // red this.setHex( hex, colorSpace ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } return this; } clone() { return new this.constructor( this.r, this.g, this.b ); } copy( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear( color ) { this.r = SRGBToLinear( color.r ); this.g = SRGBToLinear( color.g ); this.b = SRGBToLinear( color.b ); return this; } copyLinearToSRGB( color ) { this.r = LinearToSRGB( color.r ); this.g = LinearToSRGB( color.g ); this.b = LinearToSRGB( color.b ); return this; } convertSRGBToLinear() { this.copySRGBToLinear( this ); return this; } convertLinearToSRGB() { this.copyLinearToSRGB( this ); return this; } getHex( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); return Math.round( clamp$1( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp$1( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp$1( _color.b * 255, 0, 255 ) ); } getHexString( colorSpace = SRGBColorSpace ) { return ( "000000" + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } getHSL( target, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max( r, g, b ); const min = Math.min( r, g, b ); let hue, saturation; const lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB( target, colorSpace = ColorManagement.workingColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; if ( colorSpace !== SRGBColorSpace ) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`; } return `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`; } offsetHSL( h, s, l ) { this.getHSL( _hslA ); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } add( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar( s ) { this.r += s; this.g += s; this.b += s; return this; } sub( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; } multiply( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar( s ) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; } lerpColors( color1, color2, alpha ) { this.r = color1.r + ( color2.r - color1.r ) * alpha; this.g = color1.g + ( color2.g - color1.g ) * alpha; this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } lerpHSL( color, alpha ) { this.getHSL( _hslA ); color.getHSL( _hslB ); const h = lerp( _hslA.h, _hslB.h, alpha ); const s = lerp( _hslA.s, _hslB.s, alpha ); const l = lerp( _hslA.l, _hslB.l, alpha ); this.setHSL( h, s, l ); return this; } setFromVector3( v ) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3( m ) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b; this.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b; this.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b; return this; } equals( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); } fromArray( array, offset = 0 ) { this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; } fromBufferAttribute( attribute, index ) { this.r = attribute.getX( index ); this.g = attribute.getY( index ); this.b = attribute.getZ( index ); return this; } toJSON() { return this.getHex(); } *[ Symbol.iterator ]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/ new Color(); Color.NAMES = _colorKeywords; class MeshBasicMaterial extends Material { constructor( parameters ) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } const _vector$8 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor( array, itemSize, normalized = false ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( let i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } copyArray( array ) { this.array.set( array ); return this; } applyMatrix3( m ) { if ( this.itemSize === 2 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector2$1.fromBufferAttribute( this, i ); _vector2$1.applyMatrix3( m ); this.setXY( i, _vector2$1.x, _vector2$1.y ); } } else if ( this.itemSize === 3 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix3( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } } return this; } applyMatrix4( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix4( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyNormalMatrix( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.transformDirection( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } set( value, offset = 0 ) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set( value, offset ); return this; } getX( index ) { let x = this.array[ index * this.itemSize ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.array[ index * this.itemSize ] = x; return this; } getY( index ) { let y = this.array[ index * this.itemSize + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.array[ index * this.itemSize + 1 ] = y; return this; } getZ( index ) { let z = this.array[ index * this.itemSize + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.array[ index * this.itemSize + 2 ] = z; return this; } getW( index ) { let w = this.array[ index * this.itemSize + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.array[ index * this.itemSize + 3 ] = w; return this; } setXY( index, x, y ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; } onUpload( callback ) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor( this.array, this.itemSize ).copy( this ); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from( this.array ), normalized: this.normalized }; if ( this.name !== "" ) data.name = this.name; if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; return data; } copyColorsArray() { // @deprecated, r144 console.error( "THREE.BufferAttribute: copyColorsArray() was removed in r144." ); } copyVector2sArray() { // @deprecated, r144 console.error( "THREE.BufferAttribute: copyVector2sArray() was removed in r144." ); } copyVector3sArray() { // @deprecated, r144 console.error( "THREE.BufferAttribute: copyVector3sArray() was removed in r144." ); } copyVector4sArray() { // @deprecated, r144 console.error( "THREE.BufferAttribute: copyVector4sArray() was removed in r144." ); } } class Uint16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); } } class Uint32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint32Array( array ), itemSize, normalized ); } } class Float32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float32Array( array ), itemSize, normalized ); } } let _id$1 = 0; const _m1 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$1 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$7 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty( this, "id", { value: _id$1 ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } return this; } getAttribute( name ) { return this.attributes[ name ]; } setAttribute( name, attribute ) { this.attributes[ name ] = attribute; return this; } deleteAttribute( name ) { delete this.attributes[ name ]; return this; } hasAttribute( name ) { return this.attributes[ name ] !== undefined; } addGroup( start, count, materialIndex = 0 ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex } ); } clearGroups() { this.groups = []; } setDrawRange( start, count ) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4( matrix ) { const position = this.attributes.position; if ( position !== undefined ) { position.applyMatrix4( matrix ); position.needsUpdate = true; } const normal = this.attributes.normal; if ( normal !== undefined ) { const normalMatrix = new Matrix3().getNormalMatrix( matrix ); normal.applyNormalMatrix( normalMatrix ); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if ( tangent !== undefined ) { tangent.transformDirection( matrix ); tangent.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; } applyQuaternion( q ) { _m1.makeRotationFromQuaternion( q ); this.applyMatrix4( _m1 ); return this; } rotateX( angle ) { // rotate geometry around world x-axis _m1.makeRotationX( angle ); this.applyMatrix4( _m1 ); return this; } rotateY( angle ) { // rotate geometry around world y-axis _m1.makeRotationY( angle ); this.applyMatrix4( _m1 ); return this; } rotateZ( angle ) { // rotate geometry around world z-axis _m1.makeRotationZ( angle ); this.applyMatrix4( _m1 ); return this; } translate( x, y, z ) { // translate geometry _m1.makeTranslation( x, y, z ); this.applyMatrix4( _m1 ); return this; } scale( x, y, z ) { // scale geometry _m1.makeScale( x, y, z ); this.applyMatrix4( _m1 ); return this; } lookAt( vector ) { _obj.lookAt( vector ); _obj.updateMatrix(); this.applyMatrix4( _obj.matrix ); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter( _offset ).negate(); this.translate( _offset.x, _offset.y, _offset.z ); return this; } setFromPoints( points ) { const position = []; for ( let i = 0, l = points.length; i < l; i ++ ) { const point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.setAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; } computeBoundingBox() { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingBox.set( new Vector3( - Infinity, - Infinity, - Infinity ), new Vector3( + Infinity, + Infinity, + Infinity ) ); return; } if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _box$1.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( this.boundingBox.min, _box$1.min ); this.boundingBox.expandByPoint( _vector$7 ); _vector$7.addVectors( this.boundingBox.max, _box$1.max ); this.boundingBox.expandByPoint( _vector$7 ); } else { this.boundingBox.expandByPoint( _box$1.min ); this.boundingBox.expandByPoint( _box$1.max ); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } } computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingSphere.set( new Vector3(), Infinity ); return; } if ( position ) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _boxMorphTargets.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( _box$1.min, _boxMorphTargets.min ); _box$1.expandByPoint( _vector$7 ); _vector$7.addVectors( _box$1.max, _boxMorphTargets.max ); _box$1.expandByPoint( _vector$7 ); } else { _box$1.expandByPoint( _boxMorphTargets.min ); _box$1.expandByPoint( _boxMorphTargets.max ); } } } _box$1.getCenter( center ); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for ( let i = 0, il = position.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( position, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; const morphTargetsRelative = this.morphTargetsRelative; for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { _vector$7.fromBufferAttribute( morphAttribute, j ); if ( morphTargetsRelative ) { _offset.fromBufferAttribute( position, j ); _vector$7.add( _offset ); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } } } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)" ); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if ( this.hasAttribute( "tangent" ) === false ) { this.setAttribute( "tangent", new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } const tangents = this.getAttribute( "tangent" ).array; const tan1 = [], tan2 = []; for ( let i = 0; i < nVertices; i ++ ) { tan1[ i ] = new Vector3(); tan2[ i ] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle( a, b, c ) { vA.fromArray( positions, a * 3 ); vB.fromArray( positions, b * 3 ); vC.fromArray( positions, c * 3 ); uvA.fromArray( uvs, a * 2 ); uvB.fromArray( uvs, b * 2 ); uvC.fromArray( uvs, c * 2 ); vB.sub( vA ); vC.sub( vA ); uvB.sub( uvA ); uvC.sub( uvA ); const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); // silently ignore degenerate uv triangles having coincident or colinear vertices if ( ! isFinite( r ) ) return; sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); } let groups = this.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.length } ]; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleTriangle( indices[ j + 0 ], indices[ j + 1 ], indices[ j + 2 ] ); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex( v ) { n.fromArray( normals, v * 3 ); n2.copy( n ); const t = tan1[ v ]; // Gram-Schmidt orthogonalize tmp.copy( t ); tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2.crossVectors( n2, t ); const test = tmp2.dot( tan2[ v ] ); const w = ( test < 0.0 ) ? - 1.0 : 1.0; tangents[ v * 4 ] = tmp.x; tangents[ v * 4 + 1 ] = tmp.y; tangents[ v * 4 + 2 ] = tmp.z; tangents[ v * 4 + 3 ] = w; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleVertex( indices[ j + 0 ] ); handleVertex( indices[ j + 1 ] ); handleVertex( indices[ j + 2 ] ); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute( "position" ); if ( positionAttribute !== undefined ) { let normalAttribute = this.getAttribute( "normal" ); if ( normalAttribute === undefined ) { normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); this.setAttribute( "normal", normalAttribute ); } else { // reset existing normals to zero for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { normalAttribute.setXYZ( i, 0, 0, 0 ); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { for ( let i = 0, il = index.count; i < il; i += 3 ) { const vA = index.getX( i + 0 ); const vB = index.getX( i + 1 ); const vC = index.getX( i + 2 ); pA.fromBufferAttribute( positionAttribute, vA ); pB.fromBufferAttribute( positionAttribute, vB ); pC.fromBufferAttribute( positionAttribute, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); nA.fromBufferAttribute( normalAttribute, vA ); nB.fromBufferAttribute( normalAttribute, vB ); nC.fromBufferAttribute( normalAttribute, vC ); nA.add( cb ); nB.add( cb ); nC.add( cb ); normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); } } else { // non-indexed elements (unconnected triangle soup) for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { pA.fromBufferAttribute( positionAttribute, i + 0 ); pB.fromBufferAttribute( positionAttribute, i + 1 ); pC.fromBufferAttribute( positionAttribute, i + 2 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge() { // @deprecated, r144 console.error( "THREE.BufferGeometry.merge() has been removed. Use THREE.BufferGeometryUtils.mergeGeometries() instead." ); return this; } normalizeNormals() { const normals = this.attributes.normal; for ( let i = 0, il = normals.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( normals, i ); _vector$7.normalize(); normals.setXYZ( i, _vector$7.x, _vector$7.y, _vector$7.z ); } } toNonIndexed() { function convertBufferAttribute( attribute, indices ) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor( indices.length * itemSize ); let index = 0, index2 = 0; for ( let i = 0, l = indices.length; i < l; i ++ ) { if ( attribute.isInterleavedBufferAttribute ) { index = indices[ i ] * attribute.data.stride + attribute.offset; } else { index = indices[ i ] * itemSize; } for ( let j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } return new BufferAttribute( array2, itemSize, normalized ); } // if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed." ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for ( const name in attributes ) { const attribute = attributes[ name ]; const newAttribute = convertBufferAttribute( attribute, indices ); geometry2.setAttribute( name, newAttribute ); } // morph attributes const morphAttributes = this.morphAttributes; for ( const name in morphAttributes ) { const morphArray = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { const attribute = morphAttribute[ i ]; const newAttribute = convertBufferAttribute( attribute, indices ); morphArray.push( newAttribute ); } geometry2.morphAttributes[ name ] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { const parameters = this.parameters; for ( const key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if ( index !== null ) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call( index.array ) }; } const attributes = this.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; data.data.attributes[ key ] = attribute.toJSON( data.data ); } const morphAttributes = {}; let hasMorphAttributes = false; for ( const key in this.morphAttributes ) { const attributeArray = this.morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; array.push( attribute.toJSON( data.data ) ); } if ( array.length > 0 ) { morphAttributes[ key ] = array; hasMorphAttributes = true; } } if ( hasMorphAttributes ) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } const boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if ( index !== null ) { this.setIndex( index.clone( data ) ); } // attributes const attributes = source.attributes; for ( const name in attributes ) { const attribute = attributes[ name ]; this.setAttribute( name, attribute.clone( data ) ); } // morph attributes const morphAttributes = source.morphAttributes; for ( const name in morphAttributes ) { const array = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone( data ) ); } this.morphAttributes[ name ] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box const boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); const _sphere$4 = /*@__PURE__*/ new Sphere(); const _sphereHitAt = /*@__PURE__*/ new Vector3(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _normalA = /*@__PURE__*/ new Vector3(); const _normalB = /*@__PURE__*/ new Vector3(); const _normalC = /*@__PURE__*/ new Vector3(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } getVertexPosition( index, target ) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute( position, index ); const morphInfluences = this.morphTargetInfluences; if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { const influence = morphInfluences[ i ]; const morphAttribute = morphPosition[ i ]; if ( influence === 0 ) continue; _tempA.fromBufferAttribute( morphAttribute, index ); if ( morphTargetsRelative ) { _morphA.addScaledVector( _tempA, influence ); } else { _morphA.addScaledVector( _tempA.sub( target ), influence ); } } target.add( _morphA ); } return target; } raycast( raycaster, intersects ) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$4.copy( geometry.boundingSphere ); _sphere$4.applyMatrix4( matrixWorld ); _ray$2.copy( raycaster.ray ).recast( raycaster.near ); if ( _sphere$4.containsPoint( _ray$2.origin ) === false ) { if ( _ray$2.intersectSphere( _sphere$4, _sphereHitAt ) === null ) return; if ( _ray$2.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return; } // _inverseMatrix$2.copy( matrixWorld ).invert(); _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( _ray$2.intersectsBox( geometry.boundingBox ) === false ) return; } this._computeIntersections( raycaster, intersects ); } _computeIntersections( raycaster, intersects ) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if ( index !== null ) { // indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = index.getX( j ); const b = index.getX( j + 1 ); const c = index.getX( j + 2 ); intersection = checkGeometryIntersection( this, groupMaterial, raycaster, _ray$2, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = index.getX( i ); const b = index.getX( i + 1 ); const c = index.getX( i + 2 ); intersection = checkGeometryIntersection( this, material, raycaster, _ray$2, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } } else if ( position !== undefined ) { // non-indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection( this, groupMaterial, raycaster, _ray$2, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection( this, material, raycaster, _ray$2, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } } } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { let intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point ); } if ( intersect === null ) return null; _intersectionPointWorld.copy( point ); _intersectionPointWorld.applyMatrix4( object.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) { object.getVertexPosition( a, _vA$1 ); object.getVertexPosition( b, _vB$1 ); object.getVertexPosition( c, _vC$1 ); const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); if ( intersection ) { if ( uv ) { _uvA$1.fromBufferAttribute( uv, a ); _uvB$1.fromBufferAttribute( uv, b ); _uvC$1.fromBufferAttribute( uv, c ); intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } if ( uv1 ) { _uvA$1.fromBufferAttribute( uv1, a ); _uvB$1.fromBufferAttribute( uv1, b ); _uvC$1.fromBufferAttribute( uv1, c ); intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); intersection.uv2 = intersection.uv1; // Backwards compatibility } if ( normal ) { _normalA.fromBufferAttribute( normal, a ); _normalB.fromBufferAttribute( normal, b ); _normalC.fromBufferAttribute( normal, c ); intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3() ); if ( intersection.normal.dot( ray.direction ) > 0 ) { intersection.normal.multiplyScalar( - 1 ); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor( widthSegments ); heightSegments = Math.floor( heightSegments ); depthSegments = Math.floor( depthSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segmentHeight - heightHalf; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); } } /** * Uniform Utilities */ function cloneUniforms( src ) { const dst = {}; for ( const u in src ) { dst[ u ] = {}; for ( const p in src[ u ] ) { const property = src[ u ][ p ]; if ( property && ( property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion ) ) { if ( property.isRenderTargetTexture ) { console.warn( "UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms()." ); dst[ u ][ p ] = null; } else { dst[ u ][ p ] = property.clone(); } } else if ( Array.isArray( property ) ) { dst[ u ][ p ] = property.slice(); } else { dst[ u ][ p ] = property; } } } return dst; } function mergeUniforms( uniforms ) { const merged = {}; for ( let u = 0; u < uniforms.length; u ++ ) { const tmp = cloneUniforms( uniforms[ u ] ); for ( const p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; } function cloneUniformsGroups( src ) { const dst = []; for ( let u = 0; u < src.length; u ++ ) { dst.push( src[ u ].clone() ); } return dst; } function getUnlitUniformColorSpace( renderer ) { if ( renderer.getRenderTarget() === null ) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } return LinearSRGBColorSpace; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor( parameters ) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv1": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if ( parameters !== undefined ) { this.setValues( parameters ); } } copy( source ) { super.copy( source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms( source.uniforms ); this.uniformsGroups = cloneUniformsGroups( source.uniformsGroups ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.glslVersion = this.glslVersion; data.uniforms = {}; for ( const name in this.uniforms ) { const uniform = this.uniforms[ name ]; const value = uniform.value; if ( value && value.isTexture ) { data.uniforms[ name ] = { type: "t", value: value.toJSON( meta ).uuid }; } else if ( value && value.isColor ) { data.uniforms[ name ] = { type: "c", value: value.getHex() }; } else if ( value && value.isVector2 ) { data.uniforms[ name ] = { type: "v2", value: value.toArray() }; } else if ( value && value.isVector3 ) { data.uniforms[ name ] = { type: "v3", value: value.toArray() }; } else if ( value && value.isVector4 ) { data.uniforms[ name ] = { type: "v4", value: value.toArray() }; } else if ( value && value.isMatrix3 ) { data.uniforms[ name ] = { type: "m3", value: value.toArray() }; } else if ( value && value.isMatrix4 ) { data.uniforms[ name ] = { type: "m4", value: value.toArray() }; } else { data.uniforms[ name ] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for ( const key in this.extensions ) { if ( this.extensions[ key ] === true ) extensions[ key ] = true; } if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; return data; } } let Camera$2 = class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy( source, recursive ) { super.copy( source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); return this; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } updateWorldMatrix( updateParents, updateChildren ) { super.updateWorldMatrix( updateParents, updateChildren ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } clone() { return new this.constructor().copy( this ); } }; let PerspectiveCamera$1 = class PerspectiveCamera extends Camera$2 { constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength( focalLength ) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG$1 * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan( DEG2RAD$1 * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG$1 * 2 * Math.atan( Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = - 0.5 * width; const view = this.view; if ( this.view !== null && this.view.enabled ) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } }; const fov = - 90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor( near, far, renderTarget ) { super(); this.type = "CubeCamera"; this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPX.layers = this.layers; cameraPX.up.set( 0, 1, 0 ); cameraPX.lookAt( 1, 0, 0 ); this.add( cameraPX ); const cameraNX = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNX.layers = this.layers; cameraNX.up.set( 0, 1, 0 ); cameraNX.lookAt( - 1, 0, 0 ); this.add( cameraNX ); const cameraPY = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPY.layers = this.layers; cameraPY.up.set( 0, 0, - 1 ); cameraPY.lookAt( 0, 1, 0 ); this.add( cameraPY ); const cameraNY = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNY.layers = this.layers; cameraNY.up.set( 0, 0, 1 ); cameraNY.lookAt( 0, - 1, 0 ); this.add( cameraNY ); const cameraPZ = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPZ.layers = this.layers; cameraPZ.up.set( 0, 1, 0 ); cameraPZ.lookAt( 0, 0, 1 ); this.add( cameraPZ ); const cameraNZ = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNZ.layers = this.layers; cameraNZ.up.set( 0, 1, 0 ); cameraNZ.lookAt( 0, 0, - 1 ); this.add( cameraNZ ); } update( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentToneMapping = renderer.toneMapping; const currentXrEnabled = renderer.xr.enabled; renderer.toneMapping = NoToneMapping; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget( renderTarget, 0 ); renderer.render( scene, cameraPX ); renderer.setRenderTarget( renderTarget, 1 ); renderer.render( scene, cameraNX ); renderer.setRenderTarget( renderTarget, 2 ); renderer.render( scene, cameraPY ); renderer.setRenderTarget( renderTarget, 3 ); renderer.render( scene, cameraNY ); renderer.setRenderTarget( renderTarget, 4 ); renderer.render( scene, cameraPZ ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget( renderTarget, 5 ); renderer.render( scene, cameraNZ ); renderer.setRenderTarget( currentRenderTarget ); renderer.toneMapping = currentToneMapping; renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images( value ) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor( size = 1, options = {} ) { super( size, size, options ); this.isWebGLCubeRenderTarget = true; const image = { width: size, height: size, depth: 1 }; const images = [ image, image, image, image, image, image ]; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null }, }, vertexShader: /* glsl */` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry( 5, 5, 5 ); const material = new ShaderMaterial( { name: "CubemapFromEquirect", uniforms: cloneUniforms( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending } ); material.uniforms.tEquirect.value = texture; const mesh = new Mesh( geometry, material ); const currentMinFilter = texture.minFilter; // Avoid blurred poles if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; const camera = new CubeCamera( 1, 10, this ); camera.update( renderer, mesh ); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear( renderer, color, depth, stencil ) { const currentRenderTarget = renderer.getRenderTarget(); for ( let i = 0; i < 6; i ++ ) { renderer.setRenderTarget( this, i ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( currentRenderTarget ); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; } setComponents( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; } setFromNormalAndCoplanarPoint( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; } setFromCoplanarPoints( a, b, c ) { const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; } copy( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= - 1; this.normal.negate(); return this; } distanceToPoint( point ) { return this.normal.dot( point ) + this.constant; } distanceToSphere( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; } projectPoint( point, target ) { return target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) ); } intersectLine( line, target ) { const direction = line.delta( _vector1 ); const denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return null; } const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return null; } return target.copy( line.start ).addScaledVector( direction, t ); } intersectsLine( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint( line.start ); const endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); } intersectsBox( box ) { return box.intersectsPlane( this ); } intersectsSphere( sphere ) { return sphere.intersectsPlane( this ); } coplanarPoint( target ) { return target.copy( this.normal ).multiplyScalar( - this.constant ); } applyMatrix4( matrix, optionalNormalMatrix ) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; } translate( offset ) { this.constant -= offset.dot( this.normal ); return this; } equals( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } clone() { return new this.constructor().copy( this ); } } const _sphere$3 = /*@__PURE__*/ new Sphere(); const _vector$6 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { this.planes = [ p0, p1, p2, p3, p4, p5 ]; } set( p0, p1, p2, p3, p4, p5 ) { const planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; } copy( frustum ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; } setFromProjectionMatrix( m ) { const planes = this.planes; const me = m.elements; const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; } intersectsObject( object ) { if ( object.boundingSphere !== undefined ) { if ( object.boundingSphere === null ) object.computeBoundingSphere(); _sphere$3.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld ); } else { const geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$3.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); } return this.intersectsSphere( _sphere$3 ); } intersectsSprite( sprite ) { _sphere$3.center.set( 0, 0, 0 ); _sphere$3.radius = 0.7071067811865476; _sphere$3.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( _sphere$3 ); } intersectsSphere( sphere ) { const planes = this.planes; const center = sphere.center; const negRadius = - sphere.radius; for ( let i = 0; i < 6; i ++ ) { const distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; } intersectsBox( box ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { const plane = planes[ i ]; // corner at max distance _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; if ( plane.distanceToPoint( _vector$6 ) < 0 ) { return false; } } return true; } containsPoint( point ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } clone() { return new this.constructor().copy( this ); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame( time, frame ) { animationLoop( time, frame ); requestId = context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; requestId = context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { context.cancelAnimationFrame( requestId ); isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } function WebGLAttributes( gl, capabilities ) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); let type; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { if ( isWebGL2 ) { type = gl.HALF_FLOAT; } else { throw new Error( "THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2." ); } } else { type = gl.UNSIGNED_SHORT; } } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } else if ( array instanceof Uint8ClampedArray ) { type = gl.UNSIGNED_BYTE; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else { if ( isWebGL2 ) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); } updateRange.count = - 1; // reset range } attribute.onUploadCallback(); } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isGLBufferAttribute ) { const cached = buffers.get( attribute ); if ( ! cached || cached.version < attribute.version ) { buffers.set( attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version } ); } return; } if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor( widthSegments ); const gridY = Math.floor( heightSegments ); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segment_height - height_half; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = ix + gridX1 * iy; const b = ix + gridX1 * ( iy + 1 ); const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = ( ix + 1 ) + gridX1 * iy; indices.push( a, b, d ); indices.push( b, c, d ); } } this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 rgb = XYZ_TO_REC709 * xyz; return rgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float R21 = R12; float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++ m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vBumpMapUv ); vec2 dSTdy = dFdy( vBumpMapUv ); float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = dFdx( surf_pos.xyz ); vec3 vSigmaY = dFdy( surf_pos.xyz ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common$2 = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float luminance( const in vec3 rgb ) { const vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 ); return dot( weights, rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); } vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define cubeUV_r0 1.0 #define cubeUV_v0 0.339 #define cubeUV_m0 - 2.0 #define cubeUV_r1 0.8 #define cubeUV_v1 0.276 #define cubeUV_m1 - 1.0 #define cubeUV_r4 0.4 #define cubeUV_v4 0.046 #define cubeUV_m4 2.0 #define cubeUV_r5 0.305 #define cubeUV_v5 0.016 #define cubeUV_m5 3.0 #define cubeUV_r6 0.21 #define cubeUV_v6 0.0038 #define cubeUV_m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= cubeUV_r1 ) { mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; } else if ( roughness >= cubeUV_r4 ) { mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; } else if ( roughness >= cubeUV_r5 ) { mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; } else if ( roughness >= cubeUV_r6 ) { mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_fragment = "LambertMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; }; void RE_Direct_Lambert( const in IncidentLight directLight, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert #define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( LEGACY_LIGHTS ) if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #else float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR material.ior = ior; #ifdef USE_SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULAR_COLORMAP specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; #endif #ifdef USE_SPECULAR_INTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEEN_COLORMAP material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEEN_ROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif #ifdef IOR float ior; #endif #ifdef USE_TRANSMISSION float transmission; float transmissionAlpha; float thickness; float attenuationDistance; vec3 attenuationColor; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } #ifdef USE_CLEARCOAT vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { vec3 f0 = material.clearcoatF0; float f90 = material.clearcoatF90; float roughness = material.clearcoatRoughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { vec3 f0 = material.specularColor; float f90 = material.specularF90; float roughness = material.roughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); #ifdef USE_IRIDESCENCE F = mix( F, material.iridescenceFresnel, material.iridescence ); #endif float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometry.viewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; vec4 spotColor; vec3 spotLightCoord; bool inSpotLightMap; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS #else #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #endif #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; #endif #undef SPOT_LIGHT_MAP_INDEX #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP diffuseColor *= texture2D( map, vMapUv ); #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal *= faceDirection; #endif #endif #ifdef USE_NORMALMAP_TANGENTSPACE #ifdef USE_TANGENT mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn = getTangentFrame( - vViewPosition, normal, vNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn[0] *= faceDirection; tbn[1] *= faceDirection; #endif #endif #ifdef USE_CLEARCOAT_NORMALMAP #ifdef USE_TANGENT mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn2[0] *= faceDirection; tbn2[1] *= faceDirection; #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( USE_NORMALMAP_TANGENTSPACE ) vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef USE_NORMALMAP_OBJECTSPACE uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { vec3 q0 = dFdx( eye_pos.xyz ); vec3 q1 = dFdy( eye_pos.xyz ); vec2 st0 = dFdx( uv.st ); vec2 st1 = dFdy( uv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec2 packDepthToRG( in highp float v ) { return packDepthToRGBA( v ).yx; } float unpackRGToDepth( const in highp vec2 v ) { return unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { return depth * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * depth - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; bool frustumTest = inFrustum && shadowCoord.z <= 1.0; if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if defined( USE_SHADOWMAP ) #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif #if NUM_SPOT_LIGHT_COORDS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { shadowWorldPosition = worldPosition; #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; #endif vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; material.attenuationDistance = attenuationDistance; material.attenuationColor = attenuationColor; #ifdef USE_TRANSMISSIONMAP material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; #endif #ifdef USE_THICKNESSMAP material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness, material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmission.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmission.rgb, material.transmission ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; float w0( float a ) { return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); } float w1( float a ) { return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); } float w2( float a ){ return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); } float w3( float a ) { return ( 1.0 / 6.0 ) * ( a * a * a ); } float g0( float a ) { return w0( a ) + w1( a ); } float g1( float a ) { return w2( a ) + w3( a ); } float h0( float a ) { return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); } float h1( float a ) { return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); } vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { uv = uv * texelSize.zw + 0.5; vec2 iuv = floor( uv ); vec2 fuv = fract( uv ); float g0x = g0( fuv.x ); float g1x = g1( fuv.x ); float h0x = h0( fuv.x ); float h1x = h1( fuv.x ); float h0y = h0( fuv.y ); float h1y = h1( fuv.y ); vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); } vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); vec2 fLodSizeInv = 1.0 / fLodSize; vec2 cLodSizeInv = 1.0 / cLodSize; vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); return mix( fSample, cSample, fract( lod ) ); } vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( isinf( attenuationDistance ) ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#ifdef USE_UV varying vec2 vUv; #endif #ifdef USE_MAP varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP varying vec2 vNormalMapUv; #endif #ifdef USE_EMISSIVEMAP varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP varying vec2 vRoughnessMapUv; #endif #ifdef USE_CLEARCOATMAP varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SHEEN_COLORMAP varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_SPECULARMAP varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV varying vec2 vUv; #endif #ifdef USE_MAP uniform mat3 mapTransform; varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP uniform mat3 alphaMapTransform; varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP uniform mat3 lightMapTransform; varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP uniform mat3 aoMapTransform; varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP uniform mat3 bumpMapTransform; varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP uniform mat3 normalMapTransform; varying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP uniform mat3 displacementMapTransform; varying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP uniform mat3 emissiveMapTransform; varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP uniform mat3 metalnessMapTransform; varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP uniform mat3 roughnessMapTransform; varying vec2 vRoughnessMapUv; #endif #ifdef USE_CLEARCOATMAP uniform mat3 clearcoatMapTransform; varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform mat3 clearcoatNormalMapTransform; varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform mat3 clearcoatRoughnessMapTransform; varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP uniform mat3 sheenColorMapTransform; varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform mat3 sheenRoughnessMapTransform; varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP uniform mat3 iridescenceMapTransform; varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform mat3 iridescenceThicknessMapTransform; varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP uniform mat3 specularMapTransform; varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP uniform mat3 specularColorMapTransform; varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform mat3 specularIntensityMapTransform; varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_vertex = "#ifdef USE_UV vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$h = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$h = "uniform sampler2D t2D; uniform float backgroundIntensity; varying vec2 vUv; void main() { vec4 texColor = texture2D( t2D, vUv ); texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$g = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #elif defined( ENVMAP_TYPE_CUBE_UV ) uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform float backgroundBlurriness; uniform float backgroundIntensity; varying vec3 vWorldDirection; #include void main() { #ifdef ENVMAP_TYPE_CUBE vec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness ); #else vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldDirection; void main() { vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); gl_FragColor = texColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$9 = "#define LAMBERT uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include #ifdef USE_POINTS_UV varying vec2 vUv; uniform mat3 uvTransform; #endif void main() { #ifdef USE_POINTS_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk$1 = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common$2, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null }, specularMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // physical refractionRatio: { value: 0.98 }, // basic, lambert, phong }, aomap: { aoMap: { value: null }, aoMapIntensity: { value: 1 }, aoMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, lightmap: { lightMap: { value: null }, lightMapIntensity: { value: 1 }, lightMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, bumpmap: { bumpMap: { value: null }, bumpMapTransform: { value: /*@__PURE__*/ new Matrix3() }, bumpScale: { value: 1 } }, normalmap: { normalMap: { value: null }, normalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, normalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) } }, displacementmap: { displacementMap: { value: null }, displacementMapTransform: { value: /*@__PURE__*/ new Matrix3() }, displacementScale: { value: 1 }, displacementBias: { value: 0 } }, emissivemap: { emissiveMap: { value: null }, emissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, metalnessmap: { metalnessMap: { value: null }, metalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, roughnessmap: { roughnessMap: { value: null }, roughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, gradientmap: { gradientMap: { value: null } }, fog: { fogDensity: { value: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) } }, 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: {} } }, spotLightMap: { value: [] }, spotShadowMap: { value: [] }, spotLightMatrix: { 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/ new Matrix3() } }, sprite: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } } }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.meshbasic_vert, fragmentShader: ShaderChunk$1.meshbasic_frag }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk$1.meshlambert_vert, fragmentShader: ShaderChunk$1.meshlambert_frag }, phong: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specular: { value: /*@__PURE__*/ new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk$1.meshphong_vert, fragmentShader: ShaderChunk$1.meshphong_frag }, standard: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk$1.meshphysical_vert, fragmentShader: ShaderChunk$1.meshphysical_frag }, toon: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk$1.meshtoon_vert, fragmentShader: ShaderChunk$1.meshtoon_frag }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } } ] ), vertexShader: ShaderChunk$1.meshmatcap_vert, fragmentShader: ShaderChunk$1.meshmatcap_frag }, points: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.points_vert, fragmentShader: ShaderChunk$1.points_frag }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk$1.linedashed_vert, fragmentShader: ShaderChunk$1.linedashed_frag }, depth: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk$1.depth_vert, fragmentShader: ShaderChunk$1.depth_frag }, normal: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk$1.meshnormal_vert, fragmentShader: ShaderChunk$1.meshnormal_frag }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.sprite, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.sprite_vert, fragmentShader: ShaderChunk$1.sprite_frag }, background: { uniforms: { uvTransform: { value: /*@__PURE__*/ new Matrix3() }, t2D: { value: null }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk$1.background_vert, fragmentShader: ShaderChunk$1.background_frag }, backgroundCube: { uniforms: { envMap: { value: null }, flipEnvMap: { value: - 1 }, backgroundBlurriness: { value: 0 }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk$1.backgroundCube_vert, fragmentShader: ShaderChunk$1.backgroundCube_frag }, cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk$1.cube_vert, fragmentShader: ShaderChunk$1.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk$1.equirect_vert, fragmentShader: ShaderChunk$1.equirect_frag }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: /*@__PURE__*/ new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk$1.distanceRGBA_vert, fragmentShader: ShaderChunk$1.distanceRGBA_frag }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.lights, UniformsLib.fog, { color: { value: /*@__PURE__*/ new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk$1.shadow_vert, fragmentShader: ShaderChunk$1.shadow_frag } }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms( [ ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalMap: { value: null }, clearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescenceIOR: { value: 1.3 }, iridescenceThicknessMinimum: { value: 100 }, iridescenceThicknessMaximum: { value: 400 }, iridescenceThicknessMap: { value: null }, iridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheen: { value: 0 }, sheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, sheenColorMap: { value: null }, sheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, sheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, thicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, attenuationDistance: { value: 0 }, attenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) }, specularColorMap: { value: null }, specularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() } } ] ), vertexShader: ShaderChunk$1.meshphysical_vert, fragmentShader: ShaderChunk$1.meshphysical_frag }; const _rgb = { r: 0, b: 0, g: 0 }; function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render( renderList, scene ) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = ( usePMREM ? cubeuvmaps : cubemaps ).get( background ); } if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } const xr = renderer.xr; const environmentBlendMode = xr.getEnvironmentBlendMode(); switch ( environmentBlendMode ) { case "opaque": forceClear = true; break; case "additive": state.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha ); forceClear = true; break; case "alpha-blend": state.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha ); forceClear = true; break; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), new ShaderMaterial( { name: "BackgroundCubeMaterial", uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false } ) ); boxMesh.geometry.deleteAttribute( "normal" ); boxMesh.geometry.deleteAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } } ); objects.update( boxMesh ); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); } else if ( background && background.isTexture ) { if ( planeMesh === undefined ) { planeMesh = new Mesh( new PlaneGeometry( 2, 2 ), new ShaderMaterial( { name: "BackgroundMaterial", uniforms: cloneUniforms( ShaderLib.background.uniforms ), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false } ) ); planeMesh.geometry.deleteAttribute( "normal" ); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } } ); objects.update( planeMesh ); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( background.matrixAutoUpdate === true ) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); } } function setClear( color, alpha ) { color.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) ); state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha = 1 ) { clearColor.set( color ); clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const extension = capabilities.isWebGL2 ? null : extensions.get( "OES_vertex_array_object" ); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState( null ); let currentState = defaultState; let forceUpdate = false; function setup( object, material, program, geometry, index ) { let updateBuffers = false; if ( vaoAvailable ) { const state = getBindingState( geometry, program, material ); if ( currentState !== state ) { currentState = state; bindVertexArrayObject( currentState.object ); } updateBuffers = needsUpdate( object, geometry, program, index ); if ( updateBuffers ) saveCache( object, geometry, program, index ); } else { const wireframe = ( material.wireframe === true ); if ( currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe ) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } if ( updateBuffers || forceUpdate ) { forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); if ( index !== null ) { gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer ); } } } function createVertexArrayObject() { if ( capabilities.isWebGL2 ) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); return extension.bindVertexArrayOES( vao ); } function deleteVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); return extension.deleteVertexArrayOES( vao ); } function getBindingState( geometry, program, material ) { const wireframe = ( material.wireframe === true ); let programMap = bindingStates[ geometry.id ]; if ( programMap === undefined ) { programMap = {}; bindingStates[ geometry.id ] = programMap; } let stateMap = programMap[ program.id ]; if ( stateMap === undefined ) { stateMap = {}; programMap[ program.id ] = stateMap; } let state = stateMap[ wireframe ]; if ( state === undefined ) { state = createBindingState( createVertexArrayObject() ); stateMap[ wireframe ] = state; } return state; } function createBindingState( vao ) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for ( let i = 0; i < maxVertexAttributes; i ++ ) { newAttributes[ i ] = 0; enabledAttributes[ i ] = 0; attributeDivisors[ i ] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate( object, geometry, program, index ) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { const cachedAttribute = cachedAttributes[ name ]; let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( cachedAttribute === undefined ) return true; if ( cachedAttribute.attribute !== geometryAttribute ) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum ++; } } if ( currentState.attributesNum !== attributesNum ) return true; if ( currentState.index !== index ) return true; return false; } function saveCache( object, geometry, program, index ) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let attribute = attributes[ name ]; if ( attribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) attribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if ( attribute && attribute.data ) { data.data = attribute.data; } cache[ name ] = data; attributesNum ++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { const extension = capabilities.isWebGL2 ? gl : extensions.get( "ANGLE_instanced_arrays" ); extension[ capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE" ]( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset ) { if ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT ) ) { gl.vertexAttribIPointer( index, size, type, stride, offset ); } else { gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); } } function setupVertexAttributes( object, material, program, geometry ) { if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( geometryAttribute !== undefined ) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if ( data.isInstancedInterleavedBuffer ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement ); } } else { if ( geometryAttribute.isInstancedBufferAttribute ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, ( size / programAttribute.locationSize ) * i * bytesPerElement ); } } } else if ( materialDefaultAttributeValues !== undefined ) { const value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: gl.vertexAttrib4fv( programAttribute.location, value ); break; default: gl.vertexAttrib1fv( programAttribute.location, value ); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometryId ]; } } function releaseStatesOfGeometry( geometry ) { if ( bindingStates[ geometry.id ] === undefined ) return; const programMap = bindingStates[ geometry.id ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometry.id ]; } function releaseStatesOfProgram( program ) { for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; if ( programMap[ program.id ] === undefined ) continue; const stateMap = programMap[ program.id ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ program.id ]; } } function reset() { resetDefaultState(); forceUpdate = true; if ( currentState === defaultState ) return; currentState = defaultState; bindVertexArrayObject( currentState.object ); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawArraysInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, start, count, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities( gl, extensions, parameters ) { let maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl.constructor.name === "WebGL2RenderingContext"; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has( "WEBGL_draw_buffers" ); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); const maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); const maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); const maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); const maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has( "OES_texture_float" ); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping( properties ) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping ) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function ( planes, camera ) { globalState = projectPlanes( planes, camera, 0 ); }; this.setState = function ( material, camera, useCache ) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get( material ); if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, useCache ); for ( let i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps( renderer ) { let cubemaps = new WeakMap(); function mapTextureMapping( texture, mapping ) { if ( mapping === EquirectangularReflectionMapping ) { texture.mapping = CubeReflectionMapping; } else if ( mapping === EquirectangularRefractionMapping ) { texture.mapping = CubeRefractionMapping; } return texture; } function get( texture ) { if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if ( cubemaps.has( texture ) ) { const cubemap = cubemaps.get( texture ).texture; return mapTextureMapping( cubemap, texture.mapping ); } else { const image = texture.image; if ( image && image.height > 0 ) { const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemap = cubemaps.get( texture ); if ( cubemap !== undefined ) { cubemaps.delete( texture ); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera$2 { constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; } setViewOffset( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = ( this.right - this.left ) / ( 2 * this.zoom ); const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); const cx = ( this.right + this.left ) / 2; const cy = ( this.top + this.bottom ) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; // Golden Ratio const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3( 1, 1, 1 ), /*@__PURE__*/ new Vector3( - 1, 1, 1 ), /*@__PURE__*/ new Vector3( 1, 1, - 1 ), /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor( renderer ) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial( this._blurMaterial ); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { _oldTarget = this._renderer.getRenderTarget(); this._setSize( 256 ); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); if ( sigma > 0 ) { this._blur( cubeUVRenderTarget, 0, 0, sigma ); } this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular( equirectangular, renderTarget = null ) { return this._fromTexture( equirectangular, renderTarget ); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap( cubemap, renderTarget = null ) { return this._fromTexture( cubemap, renderTarget ); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial( this._cubemapMaterial ); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial( this._equirectMaterial ); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } // private interface _setSize( cubeSize ) { this._lodMax = Math.floor( Math.log2( cubeSize ) ); this._cubeSize = Math.pow( 2, this._lodMax ); } _dispose() { if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); for ( let i = 0; i < this._lodPlanes.length; i ++ ) { this._lodPlanes[ i ].dispose(); } } _cleanup( outputTarget ) { this._renderer.setRenderTarget( _oldTarget ); outputTarget.scissorTest = false; _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); } _fromTexture( texture, renderTarget ) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); } else { // Equirectangular this._setSize( texture.image.width / 4 ); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV( texture, cubeUVRenderTarget ); this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max( this._cubeSize, 16 * 7 ); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget( width, height, params ); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { if ( this._pingPongRenderTarget !== null ) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget( width, height, params ); const { _lodMax } = this; ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); this._blurMaterial = _getBlurShader( _lodMax, width, height ); } return cubeUVRenderTarget; } _compileMaterial( material ) { const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); this._renderer.compile( tmpMesh, _flatCamera ); } _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera$1( fov, aspect, near, far ); const upSign = [ 1, - 1, 1, 1, 1, 1 ]; const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor( _clearColor ); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial( { name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, } ); const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); let useSolidColor = false; const background = scene.background; if ( background ) { if ( background.isColor ) { backgroundMaterial.color.copy( background ); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy( _clearColor ); useSolidColor = true; } for ( let i = 0; i < 6; i ++ ) { const col = i % 3; if ( col === 0 ) { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); } else if ( col === 1 ) { cubeCamera.up.set( 0, 0, upSign[ i ] ); cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); } else { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); renderer.setRenderTarget( cubeUVRenderTarget ); if ( useSolidColor ) { renderer.render( backgroundBox, cubeCamera ); } renderer.render( scene, cubeCamera ); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV( texture, cubeUVRenderTarget ) { const renderer = this._renderer; const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); if ( isCubeTexture ) { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; } else { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh( this._lodPlanes[ 0 ], material ); const uniforms = material.uniforms; uniforms[ "envMap" ].value = texture; const size = this._cubeSize; _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); renderer.setRenderTarget( cubeUVRenderTarget ); renderer.render( mesh, _flatCamera ); } _applyPMREM( cubeUVRenderTarget ) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for ( let i = 1; i < this._lodPlanes.length; i ++ ) { const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if ( direction !== "latitudinal" && direction !== "longitudinal" ) { console.error( "blur direction must be either latitudinal or longitudinal!" ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[ lodIn ] - 1; const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; if ( samples > MAX_SAMPLES ) { console.warn( `sigmaRadians, ${ sigmaRadians}, is too large and will clip, as it requested ${ samples} samples when the maximum is set to ${MAX_SAMPLES}` ); } const weights = []; let sum = 0; for ( let i = 0; i < MAX_SAMPLES; ++ i ) { const x = i / sigmaPixels; const weight = Math.exp( - x * x / 2 ); weights.push( weight ); if ( i === 0 ) { sum += weight; } else if ( i < samples ) { sum += 2 * weight; } } for ( let i = 0; i < weights.length; i ++ ) { weights[ i ] = weights[ i ] / sum; } blurUniforms[ "envMap" ].value = targetIn.texture; blurUniforms[ "samples" ].value = samples; blurUniforms[ "weights" ].value = weights; blurUniforms[ "latitudinal" ].value = direction === "latitudinal"; if ( poleAxis ) { blurUniforms[ "poleAxis" ].value = poleAxis; } const { _lodMax } = this; blurUniforms[ "dTheta" ].value = radiansPerPixel; blurUniforms[ "mipInt" ].value = _lodMax - lodIn; const outputSize = this._sizeLods[ lodOut ]; const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); const y = 4 * ( this._cubeSize - outputSize ); _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); renderer.setRenderTarget( targetOut ); renderer.render( blurMesh, _flatCamera ); } } function _createPlanes( lodMax ) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for ( let i = 0; i < totalLods; i ++ ) { const sizeLod = Math.pow( 2, lod ); sizeLods.push( sizeLod ); let sigma = 1.0 / sizeLod; if ( i > lodMax - LOD_MIN ) { sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; } else if ( i === 0 ) { sigma = 0; } sigmas.push( sigma ); const texelSize = 1.0 / ( sizeLod - 2 ); const min = - texelSize; const max = 1 + texelSize; const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array( positionSize * vertices * cubeFaces ); const uv = new Float32Array( uvSize * vertices * cubeFaces ); const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); for ( let face = 0; face < cubeFaces; face ++ ) { const x = ( face % 3 ) * 2 / 3 - 1; const y = face > 2 ? 0 : - 1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0 ]; position.set( coordinates, positionSize * vertices * face ); uv.set( uv1, uvSize * vertices * face ); const fill = [ face, face, face, face, face, face ]; faceIndex.set( fill, faceIndexSize * vertices * face ); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute( position, positionSize ) ); planes.setAttribute( "uv", new BufferAttribute( uv, uvSize ) ); planes.setAttribute( "faceIndex", new BufferAttribute( faceIndex, faceIndexSize ) ); lodPlanes.push( planes ); if ( lod > LOD_MIN ) { lod --; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget( width, height, params ) { const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport( target, x, y, width, height ) { target.viewport.set( x, y, width, height ); target.scissor.set( x, y, width, height ); } function _getBlurShader( lodMax, width, height ) { const weights = new Float32Array( MAX_SAMPLES ); const poleAxis = new Vector3( 0, 1, 0 ); const shaderMaterial = new ShaderMaterial( { name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0`, }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false } ); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial( { name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCubemapMaterial() { return new ShaderMaterial( { name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: - 1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCommonVertexShader() { return /* glsl */` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps( renderer ) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get( texture ) { if ( texture && texture.isTexture ) { const mapping = texture.mapping; const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); // equirect/cube map to cubeUV conversion if ( isEquirectMap || isCubeMap ) { if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get( texture ); if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); cubeUVmaps.set( texture, renderTarget ); return renderTarget.texture; } else { if ( cubeUVmaps.has( texture ) ) { return cubeUVmaps.get( texture ).texture; } else { const image = texture.image; if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); cubeUVmaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete( image ) { let count = 0; const length = 6; for ( let i = 0; i < length; i ++ ) { if ( image[ i ] !== undefined ) count ++; } return count === length; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemapUV = cubeUVmaps.get( texture ); if ( cubemapUV !== undefined ) { cubeUVmaps.delete( texture ); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if ( pmremGenerator !== null ) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions( gl ) { const extensions = {}; function getExtension( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } let extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } extensions[ name ] = extension; return extension; } return { has: function ( name ) { return getExtension( name ) !== null; }, init: function ( capabilities ) { if ( capabilities.isWebGL2 ) { getExtension( "EXT_color_buffer_float" ); } else { getExtension( "WEBGL_depth_texture" ); getExtension( "OES_texture_float" ); getExtension( "OES_texture_half_float" ); getExtension( "OES_texture_half_float_linear" ); getExtension( "OES_standard_derivatives" ); getExtension( "OES_element_index_uint" ); getExtension( "OES_vertex_array_object" ); getExtension( "ANGLE_instanced_arrays" ); } getExtension( "OES_texture_float_linear" ); getExtension( "EXT_color_buffer_half_float" ); getExtension( "WEBGL_multisampled_render_to_texture" ); }, get: function ( name ) { const extension = getExtension( name ); if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } return extension; } }; } function WebGLGeometries( gl, attributes, info, bindingStates ) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose( event ) { const geometry = event.target; if ( geometry.index !== null ) { attributes.remove( geometry.index ); } for ( const name in geometry.attributes ) { attributes.remove( geometry.attributes[ name ] ); } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; const attribute = wireframeAttributes.get( geometry ); if ( attribute ) { attributes.remove( attribute ); wireframeAttributes.delete( geometry ); } bindingStates.releaseStatesOfGeometry( geometry ); if ( geometry.isInstancedBufferGeometry === true ) { delete geometry._maxInstanceCount; } // info.memory.geometries --; } function get( object, geometry ) { if ( geometries[ geometry.id ] === true ) return geometry; geometry.addEventListener( "dispose", onGeometryDispose ); geometries[ geometry.id ] = true; info.memory.geometries ++; return geometry; } function update( geometry ) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for ( const name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets const morphAttributes = geometry.morphAttributes; for ( const name in morphAttributes ) { const array = morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function updateWireframeAttribute( geometry ) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if ( geometryIndex !== null ) { const array = geometryIndex.array; version = geometryIndex.version; for ( let i = 0, l = array.length; i < l; i += 3 ) { const a = array[ i + 0 ]; const b = array[ i + 1 ]; const c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { const array = geometryPosition.array; version = geometryPosition.version; for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push( a, b, b, c, c, a ); } } const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get( geometry ); if ( previousAttribute ) attributes.remove( previousAttribute ); // wireframeAttributes.set( geometry, attribute ); } function getWireframeAttribute( geometry ) { const currentAttribute = wireframeAttributes.get( geometry ); if ( currentAttribute ) { const geometryIndex = geometry.index; if ( geometryIndex !== null ) { // if the attribute is obsolete, create a new one if ( currentAttribute.version < geometryIndex.version ) { updateWireframeAttribute( geometry ); } } } else { updateWireframeAttribute( geometry ); } return wireframeAttributes.get( geometry ); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } let type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawElementsInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo( gl ) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { render.calls ++; switch ( mode ) { case gl.TRIANGLES: render.triangles += instanceCount * ( count / 3 ); break; case gl.LINES: render.lines += instanceCount * ( count / 2 ); break; case gl.LINE_STRIP: render.lines += instanceCount * ( count - 1 ); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.frame ++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } function numericalSort( a, b ) { return a[ 0 ] - b[ 0 ]; } function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl, capabilities, textures ) { const influencesList = {}; const morphInfluences = new Float32Array( 8 ); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for ( let i = 0; i < 8; i ++ ) { workInfluences[ i ] = [ i, 0 ]; } function update( object, geometry, program ) { const objectInfluences = object.morphTargetInfluences; if ( capabilities.isWebGL2 === true ) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let entry = morphTextures.get( geometry ); if ( entry === undefined || entry.count !== morphTargetsCount ) { if ( entry !== undefined ) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if ( hasMorphPosition === true ) vertexDataCount = 1; if ( hasMorphNormals === true ) vertexDataCount = 2; if ( hasMorphColors === true ) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if ( width > capabilities.maxTextureSize ) { height = Math.ceil( width / capabilities.maxTextureSize ); width = capabilities.maxTextureSize; } const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for ( let i = 0; i < morphTargetsCount; i ++ ) { const morphTarget = morphTargets[ i ]; const morphNormal = morphNormals[ i ]; const morphColor = morphColors[ i ]; const offset = width * height * 4 * i; for ( let j = 0; j < morphTarget.count; j ++ ) { const stride = j * vertexDataStride; if ( hasMorphPosition === true ) { morph.fromBufferAttribute( morphTarget, j ); buffer[ offset + stride + 0 ] = morph.x; buffer[ offset + stride + 1 ] = morph.y; buffer[ offset + stride + 2 ] = morph.z; buffer[ offset + stride + 3 ] = 0; } if ( hasMorphNormals === true ) { morph.fromBufferAttribute( morphNormal, j ); buffer[ offset + stride + 4 ] = morph.x; buffer[ offset + stride + 5 ] = morph.y; buffer[ offset + stride + 6 ] = morph.z; buffer[ offset + stride + 7 ] = 0; } if ( hasMorphColors === true ) { morph.fromBufferAttribute( morphColor, j ); buffer[ offset + stride + 8 ] = morph.x; buffer[ offset + stride + 9 ] = morph.y; buffer[ offset + stride + 10 ] = morph.z; buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2( width, height ) }; morphTextures.set( geometry, entry ); function disposeTexture() { texture.dispose(); morphTextures.delete( geometry ); geometry.removeEventListener( "dispose", disposeTexture ); } geometry.addEventListener( "dispose", disposeTexture ); } // let morphInfluencesSum = 0; for ( let i = 0; i < objectInfluences.length; i ++ ) { morphInfluencesSum += objectInfluences[ i ]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", objectInfluences ); program.getUniforms().setValue( gl, "morphTargetsTexture", entry.texture, textures ); program.getUniforms().setValue( gl, "morphTargetsTextureSize", entry.size ); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[ geometry.id ]; if ( influences === undefined || influences.length !== length ) { // initialise list influences = []; for ( let i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } // Collect influences for ( let i = 0; i < length; i ++ ) { const influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); for ( let i = 0; i < 8; i ++ ) { if ( i < length && influences[ i ][ 1 ] ) { workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; } else { workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; workInfluences[ i ][ 1 ] = 0; } } workInfluences.sort( numericalSort ); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for ( let i = 0; i < 8; i ++ ) { const influence = workInfluences[ i ]; const index = influence[ 0 ]; const value = influence[ 1 ]; if ( index !== Number.MAX_SAFE_INTEGER && value ) { if ( morphTargets && geometry.getAttribute( "morphTarget" + i ) !== morphTargets[ index ] ) { geometry.setAttribute( "morphTarget" + i, morphTargets[ index ] ); } if ( morphNormals && geometry.getAttribute( "morphNormal" + i ) !== morphNormals[ index ] ) { geometry.setAttribute( "morphNormal" + i, morphNormals[ index ] ); } morphInfluences[ i ] = value; morphInfluencesSum += value; } else { if ( morphTargets && geometry.hasAttribute( "morphTarget" + i ) === true ) { geometry.deleteAttribute( "morphTarget" + i ); } if ( morphNormals && geometry.hasAttribute( "morphNormal" + i ) === true ) { geometry.deleteAttribute( "morphNormal" + i ); } morphInfluences[ i ] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } } return { update: update }; } function WebGLObjects( gl, geometries, attributes, info ) { let updateMap = new WeakMap(); function update( object ) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateMap.get( buffergeometry ) !== frame ) { geometries.update( buffergeometry ); updateMap.set( buffergeometry, frame ); } if ( object.isInstancedMesh ) { if ( object.hasEventListener( "dispose", onInstancedMeshDispose ) === false ) { object.addEventListener( "dispose", onInstancedMeshDispose ); } attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); if ( object.instanceColor !== null ) { attributes.update( object.instanceColor, gl.ARRAY_BUFFER ); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose( event ) { const instancedMesh = event.target; instancedMesh.removeEventListener( "dispose", onInstancedMeshDispose ); attributes.remove( instancedMesh.instanceMatrix ); if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); } return { update: update, dispose: dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array( 16 ); const mat3array = new Float32Array( 9 ); const mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { const firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( let i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( let i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( textures, n ) { let r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( let i = 0; i !== n; ++ i ) { r[ i ] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValueV3f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValueV4f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValueM3( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValueM4( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single integer / boolean function setValueV1i( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2i( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } } function setValueV3i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3i( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } } function setValueV4i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4i( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } } // Single unsigned integer function setValueV1ui( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1ui( this.addr, v ); cache[ 0 ] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2ui( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV3ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3ui( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV4ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4ui( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4uiv( this.addr, v ); copyArray( cache, v ); } } // Single texture (2D / Cube) function setValueT1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2D( v || emptyTexture, unit ); } function setValueT3D1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture3D( v || empty3dTexture, unit ); } function setValueT6( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTextureCube( v || emptyCubeTexture, unit ); } function setValueT2DArray1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2DArray( v || emptyArrayTexture, unit ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray( gl, v ) { gl.uniform1fv( this.addr, v ); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray( gl, v ) { const data = flatten( v, this.size, 2 ); gl.uniform2fv( this.addr, data ); } function setValueV3fArray( gl, v ) { const data = flatten( v, this.size, 3 ); gl.uniform3fv( this.addr, data ); } function setValueV4fArray( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniform4fv( this.addr, data ); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniformMatrix2fv( this.addr, false, data ); } function setValueM3Array( gl, v ) { const data = flatten( v, this.size, 9 ); gl.uniformMatrix3fv( this.addr, false, data ); } function setValueM4Array( gl, v ) { const data = flatten( v, this.size, 16 ); gl.uniformMatrix4fv( this.addr, false, data ); } // Array of integer / boolean function setValueV1iArray( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValueV3iArray( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValueV4iArray( gl, v ) { gl.uniform4iv( this.addr, v ); } // Array of unsigned integer function setValueV1uiArray( gl, v ) { gl.uniform1uiv( this.addr, v ); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray( gl, v ) { gl.uniform2uiv( this.addr, v ); } function setValueV3uiArray( gl, v ) { gl.uniform3uiv( this.addr, v ); } function setValueV4uiArray( gl, v ) { gl.uniform4uiv( this.addr, v ); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT3DArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); } } function setValueT6Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } function setValueT2DArrayArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor( id ) { this.id = id; this.seq = []; this.map = {}; } setValue( gl, value, textures ) { const seq = this.seq; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; u.setValue( gl, value[ u.id ], textures ); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { const match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex; let id = match[ 1 ]; const idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container class WebGLUniforms { constructor( gl, program ) { this.seq = []; this.map = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( let i = 0; i < n; ++ i ) { const info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } setValue( gl, name, value, textures ) { const u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, textures ); } setOptional( gl, object, name ) { const v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); } static upload( gl, seq, values, textures ) { for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, textures ); } } } static seqWithValue( seq, values ) { const r = []; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; } } function WebGLShader( gl, type, string ) { const shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); return shader; } let programIdCount = 0; function handleSource( string, errorLine ) { const lines = string.split( " " ); const lines2 = []; const from = Math.max( errorLine - 6, 0 ); const to = Math.min( errorLine + 6, lines.length ); for ( let i = from; i < to; i ++ ) { const line = i + 1; lines2.push( `${line === errorLine ? ">" : " "} ${line}: ${lines[ i ]}` ); } return lines2.join( " " ); } function getEncodingComponents( colorSpace ) { switch ( colorSpace ) { case LinearSRGBColorSpace: return [ "Linear", "( value )" ]; case SRGBColorSpace: return [ "sRGB", "( value )" ]; default: console.warn( "THREE.WebGLProgram: Unsupported color space:", colorSpace ); return [ "Linear", "( value )" ]; } } function getShaderErrors( gl, shader, type ) { const status = gl.getShaderParameter( shader, gl.COMPILE_STATUS ); const errors = gl.getShaderInfoLog( shader ).trim(); if ( status && errors === "" ) return ""; const errorMatches = /ERROR: 0:(d+)/.exec( errors ); if ( errorMatches ) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt( errorMatches[ 1 ] ); return type.toUpperCase() + " " + errors + " " + handleSource( gl.getShaderSource( shader ), errorLine ); } else { return errors; } } function getTexelEncodingFunction( functionName, colorSpace ) { const components = getEncodingComponents( colorSpace ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { let toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn( "THREE.WebGLProgram: Unsupported toneMapping:", toneMapping ); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( parameters ) { const chunks = [ ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? "#extension GL_EXT_draw_buffers : require" : "", ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { const chunks = []; for ( const name in defines ) { const value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { const attributes = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( let i = 0; i < n; i ++ ) { const info = gl.getActiveAttrib( program, i ); const name = info.name; let locationSize = 1; if ( info.type === gl.FLOAT_MAT2 ) locationSize = 2; if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3; if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = { type: info.type, location: gl.getAttribLocation( program, name ), locationSize: locationSize }; } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps ) .replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps ) .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } // Resolve Includes const includePattern$1 = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes$1( string ) { return string.replace( includePattern$1, includeReplacer$1 ); } function includeReplacer$1( match, include ) { const string = ShaderChunk$1[ include ]; if ( string === undefined ) { throw new Error( "Can not resolve #include <" + include + ">" ); } return resolveIncludes$1( string ); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision( parameters ), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.displacementMap ? "#define USE_DISPLACEMENTMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", // parameters.mapUv ? "#define MAP_UV " + parameters.mapUv : "", parameters.alphaMapUv ? "#define ALPHAMAP_UV " + parameters.alphaMapUv : "", parameters.lightMapUv ? "#define LIGHTMAP_UV " + parameters.lightMapUv : "", parameters.aoMapUv ? "#define AOMAP_UV " + parameters.aoMapUv : "", parameters.emissiveMapUv ? "#define EMISSIVEMAP_UV " + parameters.emissiveMapUv : "", parameters.bumpMapUv ? "#define BUMPMAP_UV " + parameters.bumpMapUv : "", parameters.normalMapUv ? "#define NORMALMAP_UV " + parameters.normalMapUv : "", parameters.displacementMapUv ? "#define DISPLACEMENTMAP_UV " + parameters.displacementMapUv : "", parameters.metalnessMapUv ? "#define METALNESSMAP_UV " + parameters.metalnessMapUv : "", parameters.roughnessMapUv ? "#define ROUGHNESSMAP_UV " + parameters.roughnessMapUv : "", parameters.clearcoatMapUv ? "#define CLEARCOATMAP_UV " + parameters.clearcoatMapUv : "", parameters.clearcoatNormalMapUv ? "#define CLEARCOAT_NORMALMAP_UV " + parameters.clearcoatNormalMapUv : "", parameters.clearcoatRoughnessMapUv ? "#define CLEARCOAT_ROUGHNESSMAP_UV " + parameters.clearcoatRoughnessMapUv : "", parameters.iridescenceMapUv ? "#define IRIDESCENCEMAP_UV " + parameters.iridescenceMapUv : "", parameters.iridescenceThicknessMapUv ? "#define IRIDESCENCE_THICKNESSMAP_UV " + parameters.iridescenceThicknessMapUv : "", parameters.sheenColorMapUv ? "#define SHEEN_COLORMAP_UV " + parameters.sheenColorMapUv : "", parameters.sheenRoughnessMapUv ? "#define SHEEN_ROUGHNESSMAP_UV " + parameters.sheenRoughnessMapUv : "", parameters.specularMapUv ? "#define SPECULARMAP_UV " + parameters.specularMapUv : "", parameters.specularColorMapUv ? "#define SPECULAR_COLORMAP_UV " + parameters.specularColorMapUv : "", parameters.specularIntensityMapUv ? "#define SPECULAR_INTENSITYMAP_UV " + parameters.specularIntensityMapUv : "", parameters.transmissionMapUv ? "#define TRANSMISSIONMAP_UV " + parameters.transmissionMapUv : "", parameters.thicknessMapUv ? "#define THICKNESSMAP_UV " + parameters.thicknessMapUv : "", // parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", ( parameters.morphColors && parameters.isWebGL2 ) ? "#define USE_MORPHCOLORS" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_UV1", " attribute vec2 uv1;", "#endif", "#ifdef USE_UV2", " attribute vec2 uv2;", "#endif", "#ifdef USE_UV3", " attribute vec2 uv3;", "#endif", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, generatePrecision( parameters ), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.useLegacyLights ? "#define LEGACY_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk$1[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk$1[ "encodings_pars_fragment" ], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputColorSpace ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = resolveIncludes$1( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = resolveIncludes$1( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ "precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture" ].join( " " ) + " " + prefixVertex; prefixFragment = [ "#define varying in", ( parameters.glslVersion === GLSL3 ) ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", ( parameters.glslVersion === GLSL3 ) ? "" : "#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( " " ) + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( parameters.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); // check for link errors if ( renderer.debug.checkShaderErrors ) { const programLog = gl.getProgramInfoLog( program ).trim(); const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); let runnable = true; let haveDiagnostics = true; if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; if ( typeof renderer.debug.onShaderError === "function" ) { renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader ); } else { // default error reporting const vertexErrors = getShaderErrors( gl, glVertexShader, "vertex" ); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment" ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors ); } } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: Program Info Log:", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program ); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram( this ); gl.deleteProgram( program ); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount ++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update( material ) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage( vertexShader ); const fragmentShaderStage = this._getShaderStage( fragmentShader ); const materialShaders = this._getShaderCacheForMaterial( material ); if ( materialShaders.has( vertexShaderStage ) === false ) { materialShaders.add( vertexShaderStage ); vertexShaderStage.usedTimes ++; } if ( materialShaders.has( fragmentShaderStage ) === false ) { materialShaders.add( fragmentShaderStage ); fragmentShaderStage.usedTimes ++; } return this; } remove( material ) { const materialShaders = this.materialCache.get( material ); for ( const shaderStage of materialShaders ) { shaderStage.usedTimes --; if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); } this.materialCache.delete( material ); return this; } getVertexShaderID( material ) { return this._getShaderStage( material.vertexShader ).id; } getFragmentShaderID( material ) { return this._getShaderStage( material.fragmentShader ).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial( material ) { const cache = this.materialCache; let set = cache.get( material ); if ( set === undefined ) { set = new Set(); cache.set( material, set ); } return set; } _getShaderStage( code ) { const cache = this.shaderCache; let stage = cache.get( code ); if ( stage === undefined ) { stage = new WebGLShaderStage( code ); cache.set( code, stage ); } return stage; } } class WebGLShaderStage { constructor( code ) { this.id = _id ++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const IS_WEBGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getChannel( value ) { if ( value === 1 ) return "uv1"; if ( value === 2 ) return "uv2"; if ( value === 3 ) return "uv3"; return "uv"; } function getParameters( material, lights, shadows, scene, object ) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; const shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let morphTextureStride = 0; if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update( material ); customVertexShaderID = _customShaders.getVertexShaderID( material ); customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const HAS_MAP = !! material.map; const HAS_MATCAP = !! material.matcap; const HAS_ENVMAP = !! envMap; const HAS_AOMAP = !! material.aoMap; const HAS_LIGHTMAP = !! material.lightMap; const HAS_BUMPMAP = !! material.bumpMap; const HAS_NORMALMAP = !! material.normalMap; const HAS_DISPLACEMENTMAP = !! material.displacementMap; const HAS_EMISSIVEMAP = !! material.emissiveMap; const HAS_METALNESSMAP = !! material.metalnessMap; const HAS_ROUGHNESSMAP = !! material.roughnessMap; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap; const HAS_SPECULARMAP = !! material.specularMap; const HAS_SPECULAR_COLORMAP = !! material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap; const HAS_GRADIENTMAP = !! material.gradientMap; const HAS_ALPHAMAP = !! material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_EXTENSIONS = !! material.extensions; const HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1; const HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2; const HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3; const parameters = { isWebGL2: IS_WEBGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, combine: material.combine, // mapUv: HAS_MAP && getChannel( material.map.channel ), aoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ), lightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ), bumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ), normalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ), emissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ), metalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ), specularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ), thicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ), alphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ), // vertexTangents: HAS_NORMALMAP && !! geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUv1s: HAS_ATTRIBUTE_UV1, vertexUv2s: HAS_ATTRIBUTE_UV2, vertexUv3s: HAS_ATTRIBUTE_UV3, pointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ), fog: !! fog, useFog: material.fog === true, fogExp2: ( fog && fog.isFogExp2 ), flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, useLegacyLights: renderer.useLegacyLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true, extensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true, extensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true, extensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true, rendererExtensionFragDepth: IS_WEBGL2 || extensions.has( "EXT_frag_depth" ), rendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( "WEBGL_draw_buffers" ), rendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( "EXT_shader_texture_lod" ), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey( parameters ) { const array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( parameters.customVertexShaderID ); array.push( parameters.customFragmentShaderID ); } if ( parameters.defines !== undefined ) { for ( const name in parameters.defines ) { array.push( name ); array.push( parameters.defines[ name ] ); } } if ( parameters.isRawShaderMaterial === false ) { getProgramCacheKeyParameters( array, parameters ); getProgramCacheKeyBooleans( array, parameters ); array.push( renderer.outputColorSpace ); } array.push( parameters.customProgramCacheKey ); return array.join(); } function getProgramCacheKeyParameters( array, parameters ) { array.push( parameters.precision ); array.push( parameters.outputColorSpace ); array.push( parameters.envMapMode ); array.push( parameters.envMapCubeUVHeight ); array.push( parameters.mapUv ); array.push( parameters.alphaMapUv ); array.push( parameters.lightMapUv ); array.push( parameters.aoMapUv ); array.push( parameters.bumpMapUv ); array.push( parameters.normalMapUv ); array.push( parameters.displacementMapUv ); array.push( parameters.emissiveMapUv ); array.push( parameters.metalnessMapUv ); array.push( parameters.roughnessMapUv ); array.push( parameters.clearcoatMapUv ); array.push( parameters.clearcoatNormalMapUv ); array.push( parameters.clearcoatRoughnessMapUv ); array.push( parameters.iridescenceMapUv ); array.push( parameters.iridescenceThicknessMapUv ); array.push( parameters.sheenColorMapUv ); array.push( parameters.sheenRoughnessMapUv ); array.push( parameters.specularMapUv ); array.push( parameters.specularColorMapUv ); array.push( parameters.specularIntensityMapUv ); array.push( parameters.transmissionMapUv ); array.push( parameters.thicknessMapUv ); array.push( parameters.combine ); array.push( parameters.fogExp2 ); array.push( parameters.sizeAttenuation ); array.push( parameters.morphTargetsCount ); array.push( parameters.morphAttributeCount ); array.push( parameters.numDirLights ); array.push( parameters.numPointLights ); array.push( parameters.numSpotLights ); array.push( parameters.numSpotLightMaps ); array.push( parameters.numHemiLights ); array.push( parameters.numRectAreaLights ); array.push( parameters.numDirLightShadows ); array.push( parameters.numPointLightShadows ); array.push( parameters.numSpotLightShadows ); array.push( parameters.numSpotLightShadowsWithMaps ); array.push( parameters.shadowMapType ); array.push( parameters.toneMapping ); array.push( parameters.numClippingPlanes ); array.push( parameters.numClipIntersection ); array.push( parameters.depthPacking ); } function getProgramCacheKeyBooleans( array, parameters ) { _programLayers.disableAll(); if ( parameters.isWebGL2 ) _programLayers.enable( 0 ); if ( parameters.supportsVertexTextures ) _programLayers.enable( 1 ); if ( parameters.instancing ) _programLayers.enable( 2 ); if ( parameters.instancingColor ) _programLayers.enable( 3 ); if ( parameters.matcap ) _programLayers.enable( 4 ); if ( parameters.envMap ) _programLayers.enable( 5 ); if ( parameters.normalMapObjectSpace ) _programLayers.enable( 6 ); if ( parameters.normalMapTangentSpace ) _programLayers.enable( 7 ); if ( parameters.clearcoat ) _programLayers.enable( 8 ); if ( parameters.iridescence ) _programLayers.enable( 9 ); if ( parameters.alphaTest ) _programLayers.enable( 10 ); if ( parameters.vertexColors ) _programLayers.enable( 11 ); if ( parameters.vertexAlphas ) _programLayers.enable( 12 ); if ( parameters.vertexUv1s ) _programLayers.enable( 13 ); if ( parameters.vertexUv2s ) _programLayers.enable( 14 ); if ( parameters.vertexUv3s ) _programLayers.enable( 15 ); if ( parameters.vertexTangents ) _programLayers.enable( 16 ); array.push( _programLayers.mask ); _programLayers.disableAll(); if ( parameters.fog ) _programLayers.enable( 0 ); if ( parameters.useFog ) _programLayers.enable( 1 ); if ( parameters.flatShading ) _programLayers.enable( 2 ); if ( parameters.logarithmicDepthBuffer ) _programLayers.enable( 3 ); if ( parameters.skinning ) _programLayers.enable( 4 ); if ( parameters.morphTargets ) _programLayers.enable( 5 ); if ( parameters.morphNormals ) _programLayers.enable( 6 ); if ( parameters.morphColors ) _programLayers.enable( 7 ); if ( parameters.premultipliedAlpha ) _programLayers.enable( 8 ); if ( parameters.shadowMapEnabled ) _programLayers.enable( 9 ); if ( parameters.useLegacyLights ) _programLayers.enable( 10 ); if ( parameters.doubleSided ) _programLayers.enable( 11 ); if ( parameters.flipSided ) _programLayers.enable( 12 ); if ( parameters.useDepthPacking ) _programLayers.enable( 13 ); if ( parameters.dithering ) _programLayers.enable( 14 ); if ( parameters.transmission ) _programLayers.enable( 15 ); if ( parameters.sheen ) _programLayers.enable( 16 ); if ( parameters.opaque ) _programLayers.enable( 17 ); if ( parameters.pointsUvs ) _programLayers.enable( 18 ); array.push( _programLayers.mask ); } function getUniforms( material ) { const shaderID = shaderIDs[ material.type ]; let uniforms; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; uniforms = UniformsUtils.clone( shader.uniforms ); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram( parameters, cacheKey ) { let program; // Check if code has been already compiled for ( let p = 0, pl = programs.length; p < pl; p ++ ) { const preexistingProgram = programs[ p ]; if ( preexistingProgram.cacheKey === cacheKey ) { program = preexistingProgram; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); programs.push( program ); } return program; } function releaseProgram( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set const i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache( material ) { _customShaders.remove( material ); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get( object ) { let map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { let renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex ++; return renderItem; } function push( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.push( renderItem ); } else if ( material.transparent === true ) { transparent.push( renderItem ); } else { opaque.push( renderItem ); } } function unshift( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.unshift( renderItem ); } else if ( material.transparent === true ) { transparent.unshift( renderItem ); } else { opaque.unshift( renderItem ); } } function sort( customOpaqueSort, customTransparentSort ) { if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); } function finish() { // Clear references from inactive renderItems in the list for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { const renderItem = renderItems[ i ]; if ( renderItem.id === null ) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get( scene, renderCallDepth ) { const listArray = lists.get( scene ); let list; if ( listArray === undefined ) { list = new WebGLRenderList(); lists.set( scene, [ list ] ); } else { if ( renderCallDepth >= listArray.length ) { list = new WebGLRenderList(); listArray.push( list ); } else { list = listArray[ renderCallDepth ]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[ light.id ] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst( lightA, lightB ) { return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 ); } function WebGLLights( extensions, capabilities ) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: - 1, pointLength: - 1, spotLength: - 1, rectAreaLength: - 1, hemiLength: - 1, numDirectionalShadows: - 1, numPointShadows: - 1, numSpotShadows: - 1, numSpotMaps: - 1 }, ambient: [ 0, 0, 0 ], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0 }; for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup( lights, useLegacyLights ) { let r = 0, g = 0, b = 0; for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort( shadowCastingAndTexturingLightsFirst ); // artist-friendly light intensity scaling factor const scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if ( light.isLightProbe ) { for ( let j = 0; j < 9; j ++ ) { state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); } } else if ( light.isDirectionalLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[ directionalLength ] = shadowUniforms; state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; numDirectionalShadows ++; } state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); uniforms.distance = distance; uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = light.decay; state.spot[ spotLength ] = uniforms; const shadow = light.shadow; if ( light.map ) { state.spotLightMap[ numSpotMaps ] = light.map; numSpotMaps ++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices( light ); if ( light.castShadow ) numSpotShadowsWithMaps ++; } state.spotLightMatrix[ spotLength ] = shadow.matrix; if ( light.castShadow ) { const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[ spotLength ] = shadowUniforms; state.spotShadowMap[ spotLength ] = shadowMap; numSpotShadows ++; } spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = cache.get( light ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); uniforms.distance = light.distance; uniforms.decay = light.decay; if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[ pointLength ] = shadowUniforms; state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; numPointShadows ++; } state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = cache.get( light ); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } if ( rectAreaLength > 0 ) { if ( capabilities.isWebGL2 ) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if ( extensions.has( "OES_texture_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if ( extensions.has( "OES_texture_half_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error( "THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions." ); } } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; state.version = nextVersion ++; } } function setupView( lights, camera ) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; if ( light.isDirectionalLight ) { const uniforms = state.directional[ directionalLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = state.spot[ spotLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = state.rectArea[ rectAreaLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = state.point[ pointLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = state.hemi[ hemiLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); hemiLength ++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState( extensions, capabilities ) { const lights = new WebGLLights( extensions, capabilities ); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function setupLights( useLegacyLights ) { lights.setup( lightsArray, useLegacyLights ); } function setupLightsView( camera ) { lights.setupView( lightsArray, camera ); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates( extensions, capabilities ) { let renderStates = new WeakMap(); function get( scene, renderCallDepth = 0 ) { const renderStateArray = renderStates.get( scene ); let renderState; if ( renderStateArray === undefined ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStates.set( scene, [ renderState ] ); } else { if ( renderCallDepth >= renderStateArray.length ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStateArray.push( renderState ); } else { renderState = renderStateArray[ renderCallDepth ]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } class MeshDepthMaterial extends Material { constructor( parameters ) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor( parameters ) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex$i = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment$i = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap( _renderer, _objects, _capabilities ) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial( { defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex$i, fragmentShader: fragment$i } ); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), 3 ) ); const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending( NoBlending ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // check for shadow map type changes const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap ); const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap ); // render depth map for ( let i = 0, il = lights.length; i < il; i ++ ) { const light = lights[ i ]; const shadow = light.shadow; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; _shadowMapSize.copy( shadow.mapSize ); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply( shadowFrameExtents ); _viewportSize.copy( shadow.mapSize ); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if ( _shadowMapSize.x > _maxTextureSize ) { _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if ( _shadowMapSize.y > _maxTextureSize ) { _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if ( shadow.map === null || toVSM === true || fromVSM === true ) { const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; if ( shadow.map !== null ) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget( shadow.map ); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for ( let vp = 0; vp < viewportCount; vp ++ ) { const viewport = shadow.getViewport( vp ); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w ); _state.viewport( _viewport ); shadow.updateMatrices( light, vp ); _frustum = shadow.getFrustum(); renderObject( scene, camera, shadow.camera, light, this.type ); } // do blur pass for VSM if ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) { VSMPass( shadow, camera ); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); }; function VSMPass( shadow, camera ) { const geometry = _objects.update( fullScreenMesh ); if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if ( shadow.mapPass === null ) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.mapPass ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.map ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); } function getDepthMaterial( object, material, light, type ) { let result = null; const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; if ( customMaterial !== undefined ) { result = customMaterial; } else { result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; if ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) || ( material.displacementMap && material.displacementScale !== 0 ) || ( material.alphaMap && material.alphaTest > 0 ) || ( material.map && material.alphaTest > 0 ) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } let cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if ( type === VSMShadowMap ) { result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; } else { result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { const materialProperties = _renderer.properties.get( result ); materialProperties.light = light; } return result; } function renderObject( object, camera, shadowCamera, light, type ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); const geometry = _objects.update( object ); const material = object.material; if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let k = 0, kl = groups.length; k < kl; k ++ ) { const group = groups[ k ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { const depthMaterial = getDepthMaterial( object, material, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, light, type ); } } } function WebGLState( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( ! locked ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter( gl.VERSION ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); const viewportParam = gl.getParameter( gl.VIEWPORT ); const currentScissor = new Vector4().fromArray( scissorParam ); const currentViewport = new Vector4().fromArray( viewportParam ); function createTexture( type, target, count, dimensions ) { const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( let i = 0; i < count; i ++ ) { if ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) { gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } else { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } } return texture; } const emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); if ( isWebGL2 ) { emptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 ); emptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 ); } // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); setBlending( NoBlending ); // function enable( id ) { if ( enabledCapabilities[ id ] !== true ) { gl.enable( id ); enabledCapabilities[ id ] = true; } } function disable( id ) { if ( enabledCapabilities[ id ] !== false ) { gl.disable( id ); enabledCapabilities[ id ] = false; } } function bindFramebuffer( target, framebuffer ) { if ( currentBoundFramebuffers[ target ] !== framebuffer ) { gl.bindFramebuffer( target, framebuffer ); currentBoundFramebuffers[ target ] = framebuffer; if ( isWebGL2 ) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if ( target === gl.DRAW_FRAMEBUFFER ) { currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer; } if ( target === gl.FRAMEBUFFER ) { currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer; } } return true; } return false; } function drawBuffers( renderTarget, framebuffer ) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if ( renderTarget ) { drawBuffers = currentDrawbuffers.get( framebuffer ); if ( drawBuffers === undefined ) { drawBuffers = []; currentDrawbuffers.set( framebuffer, drawBuffers ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { const textures = renderTarget.texture; if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { for ( let i = 0, il = textures.length; i < il; i ++ ) { drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { drawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if ( drawBuffers[ 0 ] !== gl.BACK ) { drawBuffers[ 0 ] = gl.BACK; needsUpdate = true; } } if ( needsUpdate ) { if ( capabilities.isWebGL2 ) { gl.drawBuffers( drawBuffers ); } else { extensions.get( "WEBGL_draw_buffers" ).drawBuffersWEBGL( drawBuffers ); } } } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } const equationToGL = { [ AddEquation ]: gl.FUNC_ADD, [ SubtractEquation ]: gl.FUNC_SUBTRACT, [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT }; if ( isWebGL2 ) { equationToGL[ MinEquation ] = gl.MIN; equationToGL[ MaxEquation ] = gl.MAX; } else { const extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { equationToGL[ MinEquation ] = extension.MIN_EXT; equationToGL[ MaxEquation ] = extension.MAX_EXT; } } const factorToGL = { [ ZeroFactor ]: gl.ZERO, [ OneFactor ]: gl.ONE, [ SrcColorFactor ]: gl.SRC_COLOR, [ SrcAlphaFactor ]: gl.SRC_ALPHA, [ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE, [ DstColorFactor ]: gl.DST_COLOR, [ DstAlphaFactor ]: gl.DST_ALPHA, [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending === NoBlending ) { if ( currentBlendingEnabled === true ) { disable( gl.BLEND ); currentBlendingEnabled = false; } return; } if ( currentBlendingEnabled === false ) { enable( gl.BLEND ); currentBlendingEnabled = true; } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation( gl.FUNC_ADD ); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if ( premultipliedAlpha ) { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.ONE, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } else { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); let flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); ( material.blending === NormalBlending && material.transparent === false ) ? setBlending( NoBlending ) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); const stencilWrite = material.stencilWrite; stencilBuffer.setTest( stencilWrite ); if ( stencilWrite ) { stencilBuffer.setMask( material.stencilWriteMask ); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); material.alphaToCoverage === true ? enable( gl.SAMPLE_ALPHA_TO_COVERAGE ) : disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture, webglSlot ) { if ( webglSlot === undefined ) { if ( currentTextureSlot === null ) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[ webglSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ webglSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture !== undefined && boundTexture.type !== undefined ) { gl.bindTexture( boundTexture.type, null ); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage2D() { try { gl.texSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage3D() { try { gl.texSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage2D() { try { gl.texStorage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage3D() { try { gl.texStorage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage3D() { try { gl.texImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } function updateUBOMapping( uniformsGroup, program ) { let mapping = uboProgramMap.get( program ); if ( mapping === undefined ) { mapping = new WeakMap(); uboProgramMap.set( program, mapping ); } let blockIndex = mapping.get( uniformsGroup ); if ( blockIndex === undefined ) { blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name ); mapping.set( uniformsGroup, blockIndex ); } } function uniformBlockBinding( uniformsGroup, program ) { const mapping = uboProgramMap.get( program ); const blockIndex = mapping.get( uniformsGroup ); if ( uboBindings.get( program ) !== blockIndex ) { // bind shader specific block index to global block point gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex ); uboBindings.set( program, blockIndex ); } } // function reset() { // reset state gl.disable( gl.BLEND ); gl.disable( gl.CULL_FACE ); gl.disable( gl.DEPTH_TEST ); gl.disable( gl.POLYGON_OFFSET_FILL ); gl.disable( gl.SCISSOR_TEST ); gl.disable( gl.STENCIL_TEST ); gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ONE, gl.ZERO ); gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); gl.colorMask( true, true, true, true ); gl.clearColor( 0, 0, 0, 0 ); gl.depthMask( true ); gl.depthFunc( gl.LESS ); gl.clearDepth( 1 ); gl.stencilMask( 0xffffffff ); gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); gl.clearStencil( 0 ); gl.cullFace( gl.BACK ); gl.frontFace( gl.CCW ); gl.polygonOffset( 0, 0 ); gl.activeTexture( gl.TEXTURE0 ); gl.bindFramebuffer( gl.FRAMEBUFFER, null ); if ( isWebGL2 === true ) { gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); } gl.useProgram( null ); gl.lineWidth( 1 ); gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture" ) ? extensions.get( "WEBGL_multisampled_render_to_texture" ) : null; const supportsInvalidateFramebuffer = typeof navigator === "undefined" ? false : /OculusBrowser/g.test( navigator.userAgent ); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" // eslint-disable-next-line compat/compat && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; } catch ( err ) { // Ignore any errors } function createCanvas( width, height ) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas( width, height ) : createElementNS( "canvas" ); } function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { let scale = 1; // handle case if texture exceeds max size if ( image.width > maxSize || image.height > maxSize ) { scale = maxSize / Math.max( image.width, image.height ); } // only perform resize if necessary if ( scale < 1 || needsPowerOfTwo === true ) { // only perform resize for certain image types if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor( scale * image.width ); const height = floor( scale * image.height ); if ( _canvas === undefined ) _canvas = createCanvas( width, height ); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, width, height ); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")." ); return canvas; } else { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); } return image; } } return image; } function isPowerOfTwo$1( image ) { return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); } function textureNeedsPowerOfTwo( texture ) { if ( isWebGL2 ) return false; return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, supportsMips ) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target ) { _gl.generateMipmap( target ); } function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) { if ( isWebGL2 === false ) return glFormat; if ( internalFormatName !== null ) { if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """ ); } let internalFormat = glFormat; if ( glFormat === _gl.RED ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.R32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.R16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8; } if ( glFormat === _gl.RG ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8; } if ( glFormat === _gl.RGBA ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if ( glType === _gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = _gl.RGBA4; if ( glType === _gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = _gl.RGB5_A1; } if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { extensions.get( "EXT_color_buffer_float" ); } return internalFormat; } function getMipLevels( texture, image, supportsMips ) { if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { return Math.log2( Math.max( image.width, image.height ) ) + 1; } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { // user-defined mipmaps return texture.mipmaps.length; } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { _videoTextures.delete( texture ); } } function onRenderTargetDispose( event ) { const renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); } // function deallocateTexture( texture ) { const textureProperties = properties.get( texture ); if ( textureProperties.__webglInit === undefined ) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get( source ); if ( webglTextures ) { const webglTexture = webglTextures[ textureProperties.__cacheKey ]; webglTexture.usedTimes --; // the WebGLTexture object is not used anymore, remove it if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } // remove the weak map entry if no WebGLTexture uses the source anymore if ( Object.keys( webglTextures ).length === 0 ) { _sources.delete( source ); } } properties.remove( texture ); } function deleteTexture( texture ) { const textureProperties = properties.get( texture ); _gl.deleteTexture( textureProperties.__webglTexture ); const source = texture.source; const webglTextures = _sources.get( source ); delete webglTextures[ textureProperties.__cacheKey ]; info.memory.textures --; } function deallocateRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); info.memory.textures --; } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLCubeRenderTarget ) { for ( let i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); if ( renderTargetProperties.__webglColorRenderbuffer ) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) { if ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] ); } } if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { for ( let i = 0, il = texture.length; i < il; i ++ ) { const attachmentProperties = properties.get( texture[ i ] ); if ( attachmentProperties.__webglTexture ) { _gl.deleteTexture( attachmentProperties.__webglTexture ); info.memory.textures --; } properties.remove( texture[ i ] ); } } properties.remove( texture ); properties.remove( renderTarget ); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if ( textureUnit >= maxTextures ) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey( texture ) { const array = []; array.push( texture.wrapS ); array.push( texture.wrapT ); array.push( texture.wrapR || 0 ); array.push( texture.magFilter ); array.push( texture.minFilter ); array.push( texture.anisotropy ); array.push( texture.internalFormat ); array.push( texture.format ); array.push( texture.type ); array.push( texture.generateMipmaps ); array.push( texture.premultiplyAlpha ); array.push( texture.flipY ); array.push( texture.unpackAlignment ); array.push( texture.colorSpace ); return array.join(); } // function setTexture2D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if ( image === null ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found." ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete" ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture2DArray( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture3D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTextureCube( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } const wrappingToGL = { [ RepeatWrapping ]: _gl.REPEAT, [ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE, [ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT }; const filterToGL = { [ NearestFilter ]: _gl.NEAREST, [ NearestMipmapNearestFilter ]: _gl.NEAREST_MIPMAP_NEAREST, [ NearestMipmapLinearFilter ]: _gl.NEAREST_MIPMAP_LINEAR, [ LinearFilter ]: _gl.LINEAR, [ LinearMipmapNearestFilter ]: _gl.LINEAR_MIPMAP_NEAREST, [ LinearMipmapLinearFilter ]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters( textureType, texture, supportsMips ) { if ( supportsMips ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE ); } if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping." ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter." ); } } if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( texture.magFilter === NearestFilter ) return; if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; if ( texture.type === FloatType && extensions.has( "OES_texture_float_linear" ) === false ) return; // verify extension for WebGL 1 and WebGL 2 if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( "OES_texture_half_float_linear" ) === false ) ) return; // verify extension for WebGL 1 only if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function initTexture( textureProperties, texture ) { let forceUpload = false; if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get( source ); if ( webglTextures === undefined ) { webglTextures = {}; _sources.set( source, webglTextures ); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey( texture ); if ( textureCacheKey !== textureProperties.__cacheKey ) { // if not, create a new instance of WebGLTexture if ( webglTextures[ textureCacheKey ] === undefined ) { // create new entry webglTextures[ textureCacheKey ] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures ++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[ textureCacheKey ].usedTimes ++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[ textureProperties.__cacheKey ]; if ( webglTexture !== undefined ) { webglTextures[ textureProperties.__cacheKey ].usedTimes --; if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; } return forceUpload; } function uploadTexture( textureProperties, texture, slot ) { let textureType = _gl.TEXTURE_2D; if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY; if ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); image = verifyColorSpace( texture, image ); const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ); let glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); setTextureParameters( textureType, texture, supportsMips ); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); const levels = getMipLevels( texture, image, supportsMips ); if ( texture.isDepthTexture ) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if ( isWebGL2 ) { if ( texture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( texture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if ( texture.type === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if ( texture.type === FloatType ) { console.error( "WebGLRenderer: Floating point depth texture requires WebGL2." ); } } // validation checks for WebGL 1 if ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedIntType; glType = utils.convert( texture.type ); } } if ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } // if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } } } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } } else if ( texture.isCompressedTexture ) { if ( texture.isCompressedArrayTexture ) { if ( useTexStorage && allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 ); } else { state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data ); } } } } else { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else if ( texture.isDataArrayTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isData3DTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isFramebufferTexture ) { if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } else { let width = image.width, height = image.height; for ( let i = 0; i < levels; i ++ ) { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image ); } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( textureType ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } function uploadCubeTexture( textureProperties, texture, slot ) { if ( texture.image.length !== 6 ) return; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); const cubeImage = []; for ( let i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); } const image = cubeImage[ 0 ], supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ), glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); let levels = getMipLevels( texture, image, supportsMips ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); let mipmaps; if ( isCompressed ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height ); } for ( let i = 0; i < 6; i ++ ) { mipmaps = cubeImage[ i ].mipmaps; for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else { mipmaps = texture.mipmaps; if ( useTexStorage && allocateMemory ) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if ( mipmaps.length > 0 ) levels ++; state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); } for ( let i = 0; i < 6; i ++ ) { if ( isDataTexture ) { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; const mipmapImage = mipmap.image[ i ].image; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); } } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); } } } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { // We assume images for cube map have the same size. generateMipmap( _gl.TEXTURE_CUBE_MAP ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const renderTargetProperties = properties.get( renderTarget ); if ( ! renderTargetProperties.__hasExternalTextures ) { if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); } else { state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); } else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753 _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if ( isMultisample || useMultisampledRTT( renderTarget ) ) { const depthTexture = renderTarget.depthTexture; if ( depthTexture && depthTexture.isDepthTexture ) { if ( depthTexture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( depthTexture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples( renderTarget ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } } } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; const samples = getRenderTargetSamples( renderTarget ); if ( renderTarget.depthTexture.format === DepthFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( let i = 0; i < 6; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); } } else { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // rebind framebuffer with external textures function rebindTextures( renderTarget, colorTexture, depthTexture ) { const renderTargetProperties = properties.get( renderTarget ); if ( colorTexture !== undefined ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); } if ( depthTexture !== undefined ) { setupDepthRenderbuffer( renderTarget ); } } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { if ( textureProperties.__webglTexture === undefined ) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures ++; } const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( let i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if ( isMultipleRenderTargets ) { if ( capabilities.drawBuffers ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachmentProperties = properties.get( textures[ i ] ); if ( attachmentProperties.__webglTexture === undefined ) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } } } else { console.warn( "THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension." ); } } if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = isMultipleRenderTargets ? texture : [ texture ]; renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; renderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true ); const samples = getRenderTargetSamples( renderTarget ); _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); if ( renderTarget.depthBuffer ) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); for ( let i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( _gl.TEXTURE_CUBE_MAP ); } state.unbindTexture(); } else if ( isMultipleRenderTargets ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachment = textures[ i ]; const attachmentProperties = properties.get( attachment ); state.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D ); if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { generateMipmap( _gl.TEXTURE_2D ); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { if ( isWebGL2 ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error( "THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2." ); } } state.bindTexture( glTextureType, textureProperties.__webglTexture ); setTextureParameters( glTextureType, texture, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType ); if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( glTextureType ); } state.unbindTexture(); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0, il = textures.length; i < il; i ++ ) { const texture = textures[ i ]; if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target ); state.unbindTexture(); } } } function updateMultisampleRenderTarget( renderTarget ) { if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ]; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = []; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get( renderTarget ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); // If MRT we need to remove FBO attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null ); state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { invalidationArray.push( _gl.COLOR_ATTACHMENT0 + i ); if ( renderTarget.depthBuffer ) { invalidationArray.push( depthStyle ); } const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; if ( ignoreDepthValues === false ) { if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT; if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT; } if ( isMultipleRenderTargets ) { _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } if ( ignoreDepthValues === true ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] ); _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] ); } if ( isMultipleRenderTargets ) { const webglTexture = properties.get( textures[ i ] ).__webglTexture; _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST ); if ( supportsInvalidateFramebuffer ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null ); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const webglTexture = properties.get( textures[ i ] ).__webglTexture; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 ); } } state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); } } function getRenderTargetSamples( renderTarget ) { return Math.min( maxSamples, renderTarget.samples ); } function useMultisampledRTT( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); return isWebGL2 && renderTarget.samples > 0 && extensions.has( "WEBGL_multisampled_render_to_texture" ) === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture( texture ) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures.get( texture ) !== frame ) { _videoTextures.set( texture, frame ); texture.update(); } } function verifyColorSpace( texture, image ) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if ( texture.isCompressedTexture === true || texture.format === _SRGBAFormat ) return image; if ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) { // sRGB if ( colorSpace === SRGBColorSpace ) { if ( isWebGL2 === false ) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if ( extensions.has( "EXT_sRGB" ) === true && format === RGBAFormat ) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear( image ); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if ( format !== RGBAFormat || type !== UnsignedByteType ) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType." ); } } } else { console.error( "THREE.WebGLTextures: Unsupported texture color space:", colorSpace ); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function convert( p, colorSpace = NoColorSpace ) { let extension; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { if ( isWebGL2 ) return gl.HALF_FLOAT; extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) { return extension.HALF_FLOAT_OES; } else { return null; } } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; // WebGL 1 sRGB fallback if ( p === _SRGBAFormat ) { extension = extensions.get( "EXT_sRGB" ); if ( extension !== null ) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if ( p === RedFormat ) return gl.RED; if ( p === RedIntegerFormat ) return gl.RED_INTEGER; if ( p === RGFormat ) return gl.RG; if ( p === RGIntegerFormat ) return gl.RG_INTEGER; if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if ( colorSpace === SRGBColorSpace ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc_srgb" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc" ); if ( extension !== null ) { if ( p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format ) { extension = extensions.get( "EXT_texture_compression_bptc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // RGTC if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { extension = extensions.get( "EXT_texture_compression_rgtc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT; if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if ( p === UnsignedInt248Type ) { if ( isWebGL2 ) return gl.UNSIGNED_INT_24_8; extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return ( gl[ p ] !== undefined ) ? gl[ p ] : null; } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera$1 { constructor( array = [] ) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if ( this._hand === null ) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if ( this._targetRay === null ) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if ( this._grip === null ) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent( event ) { if ( this._targetRay !== null ) { this._targetRay.dispatchEvent( event ); } if ( this._grip !== null ) { this._grip.dispatchEvent( event ); } if ( this._hand !== null ) { this._hand.dispatchEvent( event ); } return this; } connect( inputSource ) { if ( inputSource && inputSource.hand ) { const hand = this._hand; if ( hand ) { for ( const inputjoint of inputSource.hand.values() ) { // Initialize hand with joints when connected this._getHandJoint( hand, inputjoint ); } } } this.dispatchEvent( { type: "connected", data: inputSource } ); return this; } disconnect( inputSource ) { this.dispatchEvent( { type: "disconnected", data: inputSource } ); if ( this._targetRay !== null ) { this._targetRay.visible = false; } if ( this._grip !== null ) { this._grip.visible = false; } if ( this._hand !== null ) { this._hand.visible = false; } return this; } update( inputSource, frame, referenceSpace ) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if ( inputSource && frame.session.visibilityState !== "visible-blurred" ) { if ( hand && inputSource.hand ) { handPose = true; for ( const inputjoint of inputSource.hand.values() ) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose( inputjoint, referenceSpace ); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint( hand, inputjoint ); if ( jointPose !== null ) { joint.matrix.fromArray( jointPose.transform.matrix ); joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints[ "index-finger-tip" ]; const thumbTip = hand.joints[ "thumb-tip" ]; const distance = indexTip.position.distanceTo( thumbTip.position ); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent( { type: "pinchend", handedness: inputSource.handedness, target: this } ); } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent( { type: "pinchstart", handedness: inputSource.handedness, target: this } ); } } else { if ( grip !== null && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { grip.matrix.fromArray( gripPose.transform.matrix ); grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); grip.matrixWorldNeedsUpdate = true; if ( gripPose.linearVelocity ) { grip.hasLinearVelocity = true; grip.linearVelocity.copy( gripPose.linearVelocity ); } else { grip.hasLinearVelocity = false; } if ( gripPose.angularVelocity ) { grip.hasAngularVelocity = true; grip.angularVelocity.copy( gripPose.angularVelocity ); } else { grip.hasAngularVelocity = false; } } } } if ( targetRay !== null ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if ( inputPose === null && gripPose !== null ) { inputPose = gripPose; } if ( inputPose !== null ) { targetRay.matrix.fromArray( inputPose.transform.matrix ); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); targetRay.matrixWorldNeedsUpdate = true; if ( inputPose.linearVelocity ) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy( inputPose.linearVelocity ); } else { targetRay.hasLinearVelocity = false; } if ( inputPose.angularVelocity ) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy( inputPose.angularVelocity ); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent( _moveEvent ); } } } if ( targetRay !== null ) { targetRay.visible = ( inputPose !== null ); } if ( grip !== null ) { grip.visible = ( gripPose !== null ); } if ( hand !== null ) { hand.visible = ( handPose !== null ); } return this; } // private method _getHandJoint( hand, inputjoint ) { if ( hand.joints[ inputjoint.jointName ] === undefined ) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[ inputjoint.jointName ] = joint; hand.add( joint ); } return hand.joints[ inputjoint.jointName ]; } } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedIntType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isDepthTexture = true; this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } class WebXRManager extends EventDispatcher { constructor( renderer, gl ) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; const planes = new Set(); const planesLastChangedTimes = new Map(); // const cameraL = new PerspectiveCamera$1(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera$1(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); const cameras = [ cameraL, cameraR ]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getGripSpace(); }; this.getHand = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getHandSpace(); }; // function onSessionEvent( event ) { const controllerIndex = controllerInputSources.indexOf( event.inputSource ); if ( controllerIndex === - 1 ) { return; } const controller = controllers[ controllerIndex ]; if ( controller !== undefined ) { controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace ); controller.dispatchEvent( { type: event.type, data: event.inputSource } ); } } function onSessionEnd() { session.removeEventListener( "select", onSessionEvent ); session.removeEventListener( "selectstart", onSessionEvent ); session.removeEventListener( "selectend", onSessionEvent ); session.removeEventListener( "squeeze", onSessionEvent ); session.removeEventListener( "squeezestart", onSessionEvent ); session.removeEventListener( "squeezeend", onSessionEvent ); session.removeEventListener( "end", onSessionEnd ); session.removeEventListener( "inputsourceschange", onInputSourcesChange ); for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; if ( inputSource === null ) continue; controllerInputSources[ i ] = null; controllers[ i ].disconnect( inputSource ); } _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget( initialRenderTarget ); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting." ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting." ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function ( space ) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function ( value ) { session = value; if ( session !== null ) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); session.addEventListener( "inputsourceschange", onInputSourcesChange ); if ( attributes.xrCompatible !== true ) { await gl.makeXRCompatible(); } if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { const layerInit = { antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: glBaseLayer } ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil } ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if ( attributes.depth ) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding( session, gl ); glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); session.updateRenderState( { layers: [ glProjLayer ] } ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0 } ); const renderTargetProperties = renderer.properties.get( newRenderTarget ); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation( foveation ); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); animation.setContext( session ); animation.start(); scope.isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } }; this.getEnvironmentBlendMode = function () { if ( session !== null ) { return session.environmentBlendMode; } }; function onInputSourcesChange( event ) { // Notify disconnected for ( let i = 0; i < event.removed.length; i ++ ) { const inputSource = event.removed[ i ]; const index = controllerInputSources.indexOf( inputSource ); if ( index >= 0 ) { controllerInputSources[ index ] = null; controllers[ index ].disconnect( inputSource ); } } // Notify connected for ( let i = 0; i < event.added.length; i ++ ) { const inputSource = event.added[ i ]; let controllerIndex = controllerInputSources.indexOf( inputSource ); if ( controllerIndex === - 1 ) { // Assign input source a controller that currently has no input source for ( let i = 0; i < controllers.length; i ++ ) { if ( i >= controllerInputSources.length ) { controllerInputSources.push( inputSource ); controllerIndex = i; break; } else if ( controllerInputSources[ i ] === null ) { controllerInputSources[ i ] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if ( controllerIndex === - 1 ) break; } const controller = controllers[ controllerIndex ]; if ( controller ) { controller.connect( inputSource ); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); const ipd = cameraLPos.distanceTo( cameraRPos ); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / ( - leftFov + rightFov ); const xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + ( ipd - xOffset ); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); } this.updateCamera = function ( camera ) { if ( session === null ) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraVR.near, depthFar: cameraVR.far } ); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera( cameraVR, parent ); for ( let i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } // update projection matrix for proper view frustum culling if ( cameras.length === 2 ) { setProjectionFromUnion( cameraVR, cameraL, cameraR ); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); } // update user camera and its children updateUserCamera( camera, cameraVR, parent ); }; function updateUserCamera( camera, cameraVR, parent ) { if ( parent === null ) { camera.matrix.copy( cameraVR.matrixWorld ); } else { camera.matrix.copy( parent.matrixWorld ); camera.matrix.invert(); camera.matrix.multiply( cameraVR.matrixWorld ); } camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.updateMatrixWorld( true ); const children = camera.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } camera.projectionMatrix.copy( cameraVR.projectionMatrix ); camera.projectionMatrixInverse.copy( cameraVR.projectionMatrixInverse ); if ( camera.isPerspectiveCamera ) { camera.fov = RAD2DEG$1 * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] ); camera.zoom = 1; } } this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if ( glProjLayer === null && glBaseLayer === null ) { return undefined; } return foveation; }; this.setFoveation = function ( value ) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if ( glProjLayer !== null ) { glProjLayer.fixedFoveation = value; } if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { glBaseLayer.fixedFoveation = value; } }; this.getPlanes = function () { return planes; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); xrFrame = frame; if ( pose !== null ) { const views = pose.views; if ( glBaseLayer !== null ) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); renderer.setRenderTarget( newRenderTarget ); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if ( views.length !== cameraVR.cameras.length ) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for ( let i = 0; i < views.length; i ++ ) { const view = views[ i ]; let viewport = null; if ( glBaseLayer !== null ) { viewport = glBaseLayer.getViewport( view ); } else { const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if ( i === 0 ) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); renderer.setRenderTarget( newRenderTarget ); } } let camera = cameras[ i ]; if ( camera === undefined ) { camera = new PerspectiveCamera$1(); camera.layers.enable( i ); camera.viewport = new Vector4(); cameras[ i ] = camera; } camera.matrix.fromArray( view.transform.matrix ); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraVR.matrix.copy( camera.matrix ); cameraVR.matrix.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); } if ( cameraVRNeedsUpdate === true ) { cameraVR.cameras.push( camera ); } } } // for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; const controller = controllers[ i ]; if ( inputSource !== null && controller !== undefined ) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace ); } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); if ( frame.detectedPlanes ) { scope.dispatchEvent( { type: "planesdetected", data: frame.detectedPlanes } ); let planesToRemove = null; for ( const plane of planes ) { if ( ! frame.detectedPlanes.has( plane ) ) { if ( planesToRemove === null ) { planesToRemove = []; } planesToRemove.push( plane ); } } if ( planesToRemove !== null ) { for ( const plane of planesToRemove ) { planes.delete( plane ); planesLastChangedTimes.delete( plane ); scope.dispatchEvent( { type: "planeremoved", data: plane } ); } } for ( const plane of frame.detectedPlanes ) { if ( ! planes.has( plane ) ) { planes.add( plane ); planesLastChangedTimes.set( plane, frame.lastChangedTime ); scope.dispatchEvent( { type: "planeadded", data: plane } ); } else { const lastKnownTime = planesLastChangedTimes.get( plane ); if ( plane.lastChangedTime > lastKnownTime ) { planesLastChangedTimes.set( plane, plane.lastChangedTime ); scope.dispatchEvent( { type: "planechanged", data: plane } ); } } } } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials( renderer, properties ) { function refreshTransformUniform( map, uniform ) { if ( map.matrixAutoUpdate === true ) { map.updateMatrix(); } uniform.value.copy( map.matrix ); } function refreshFogUniforms( uniforms, fog ) { fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) ); if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshToonMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsToon( uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsPhong( uniforms, material ); } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsStandard( uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); } } else if ( material.isMeshMatcapMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsMatcap( uniforms, material ); } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsDistance( uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( uniforms, material, pixelRatio, height ); } else if ( material.isSpriteMaterial ) { refreshUniformsSprites( uniforms, material ); } else if ( material.isShadowMaterial ) { uniforms.color.value.copy( material.color ); uniforms.opacity.value = material.opacity; } else if ( material.isShaderMaterial ) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value.copy( material.color ); } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform ); uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform( material.normalMap, uniforms.normalMapTransform ); uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform ); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform ); } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform( material.specularMap, uniforms.specularMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get( material ).envMap; if ( envMap ) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; // artist-friendly light intensity scaling factor const scaleFactor = ( renderer.useLegacyLights === true ) ? Math.PI : 1; uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; refreshTransformUniform( material.lightMap, uniforms.lightMapTransform ); } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform( material.aoMap, uniforms.aoMapTransform ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.uvTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value.copy( material.specular ); uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon( uniforms, material ) { if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.metalness.value = material.metalness; if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform ); } uniforms.roughness.value = material.roughness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform ); } const envMap = properties.get( material ).envMap; if ( envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { uniforms.ior.value = material.ior; // also part of uniforms common if ( material.sheen > 0 ) { uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); uniforms.sheenRoughness.value = material.sheenRoughness; if ( material.sheenColorMap ) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform ); } if ( material.sheenRoughnessMap ) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform ); } } if ( material.clearcoat > 0 ) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if ( material.clearcoatMap ) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform ); } if ( material.clearcoatRoughnessMap ) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform ); } if ( material.clearcoatNormalMap ) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform ); uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); if ( material.side === BackSide ) { uniforms.clearcoatNormalScale.value.negate(); } } } if ( material.iridescence > 0 ) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ]; if ( material.iridescenceMap ) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform ); } if ( material.iridescenceThicknessMap ) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform ); } } if ( material.transmission > 0 ) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); if ( material.transmissionMap ) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform ); } uniforms.thickness.value = material.thickness; if ( material.thicknessMap ) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform ); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy( material.attenuationColor ); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy( material.specularColor ); if ( material.specularColorMap ) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform ); } if ( material.specularIntensityMap ) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform ); } } function refreshUniformsMatcap( uniforms, material ) { if ( material.matcap ) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance( uniforms, material ) { const light = properties.get( material ).light; uniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld ); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function WebGLUniformsGroups( gl, info, capabilities, state ) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program function bind( uniformsGroup, program ) { const webglProgram = program.program; state.uniformBlockBinding( uniformsGroup, webglProgram ); } function update( uniformsGroup, program ) { let buffer = buffers[ uniformsGroup.id ]; if ( buffer === undefined ) { prepareUniformsGroup( uniformsGroup ); buffer = createBuffer( uniformsGroup ); buffers[ uniformsGroup.id ] = buffer; uniformsGroup.addEventListener( "dispose", onUniformsGroupsDispose ); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping( uniformsGroup, webglProgram ); // update UBO once per frame const frame = info.render.frame; if ( updateList[ uniformsGroup.id ] !== frame ) { updateBufferData( uniformsGroup ); updateList[ uniformsGroup.id ] = frame; } } function createBuffer( uniformsGroup ) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); gl.bufferData( gl.UNIFORM_BUFFER, size, usage ); gl.bindBuffer( gl.UNIFORM_BUFFER, null ); gl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer ); return buffer; } function allocateBindingPointIndex() { for ( let i = 0; i < maxBindingPoints; i ++ ) { if ( allocatedBindingPoints.indexOf( i ) === - 1 ) { allocatedBindingPoints.push( i ); return i; } } console.error( "THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached." ); return 0; } function updateBufferData( uniformsGroup ) { const buffer = buffers[ uniformsGroup.id ]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); for ( let i = 0, il = uniforms.length; i < il; i ++ ) { const uniform = uniforms[ i ]; // partly update the buffer if necessary if ( hasUniformChanged( uniform, i, cache ) === true ) { const offset = uniform.__offset; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; let arrayOffset = 0; for ( let i = 0; i < values.length; i ++ ) { const value = values[ i ]; const info = getUniformSize( value ); if ( typeof value === "number" ) { uniform.__data[ 0 ] = value; gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data ); } else if ( value.isMatrix3 ) { // manually converting 3x3 to 3x4 uniform.__data[ 0 ] = value.elements[ 0 ]; uniform.__data[ 1 ] = value.elements[ 1 ]; uniform.__data[ 2 ] = value.elements[ 2 ]; uniform.__data[ 3 ] = value.elements[ 0 ]; uniform.__data[ 4 ] = value.elements[ 3 ]; uniform.__data[ 5 ] = value.elements[ 4 ]; uniform.__data[ 6 ] = value.elements[ 5 ]; uniform.__data[ 7 ] = value.elements[ 0 ]; uniform.__data[ 8 ] = value.elements[ 6 ]; uniform.__data[ 9 ] = value.elements[ 7 ]; uniform.__data[ 10 ] = value.elements[ 8 ]; uniform.__data[ 11 ] = value.elements[ 0 ]; } else { value.toArray( uniform.__data, arrayOffset ); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data ); } } gl.bindBuffer( gl.UNIFORM_BUFFER, null ); } function hasUniformChanged( uniform, index, cache ) { const value = uniform.value; if ( cache[ index ] === undefined ) { // cache entry does not exist so far if ( typeof value === "number" ) { cache[ index ] = value; } else { const values = Array.isArray( value ) ? value : [ value ]; const tempValues = []; for ( let i = 0; i < values.length; i ++ ) { tempValues.push( values[ i ].clone() ); } cache[ index ] = tempValues; } return true; } else { // compare current value with cached entry if ( typeof value === "number" ) { if ( cache[ index ] !== value ) { cache[ index ] = value; return true; } } else { const cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ]; const values = Array.isArray( value ) ? value : [ value ]; for ( let i = 0; i < cachedObjects.length; i ++ ) { const cachedObject = cachedObjects[ i ]; if ( cachedObject.equals( values[ i ] ) === false ) { cachedObject.copy( values[ i ] ); return true; } } } } return false; } function prepareUniformsGroup( uniformsGroup ) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes let chunkOffset = 0; // offset within a single chunk in bytes for ( let i = 0, l = uniforms.length; i < l; i ++ ) { const uniform = uniforms[ i ]; const infos = { boundary: 0, // bytes storage: 0 // bytes }; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; for ( let j = 0, jl = values.length; j < jl; j ++ ) { const value = values[ j ]; const info = getUniformSize( value ); infos.boundary += info.boundary; infos.storage += info.storage; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT ); uniform.__offset = offset; // if ( i > 0 ) { chunkOffset = offset % chunkSize; const remainingSizeInChunk = chunkSize - chunkOffset; // check for chunk overflow if ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) { // add padding and adjust offset offset += ( chunkSize - chunkOffset ); uniform.__offset = offset; } } offset += infos.storage; } // ensure correct final padding chunkOffset = offset % chunkSize; if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset ); // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize( value ) { const info = { boundary: 0, // bytes storage: 0 // bytes }; // determine sizes according to STD140 if ( typeof value === "number" ) { // float/int info.boundary = 4; info.storage = 4; } else if ( value.isVector2 ) { // vec2 info.boundary = 8; info.storage = 8; } else if ( value.isVector3 || value.isColor ) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if ( value.isVector4 ) { // vec4 info.boundary = 16; info.storage = 16; } else if ( value.isMatrix3 ) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if ( value.isMatrix4 ) { // mat4 info.boundary = 64; info.storage = 64; } else if ( value.isTexture ) { console.warn( "THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group." ); } else { console.warn( "THREE.WebGLRenderer: Unsupported uniform value type.", value ); } return info; } function onUniformsGroupsDispose( event ) { const uniformsGroup = event.target; uniformsGroup.removeEventListener( "dispose", onUniformsGroupsDispose ); const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex ); allocatedBindingPoints.splice( index, 1 ); gl.deleteBuffer( buffers[ uniformsGroup.id ] ); delete buffers[ uniformsGroup.id ]; delete updateList[ uniformsGroup.id ]; } function dispose() { for ( const id in buffers ) { gl.deleteBuffer( buffers[ id ] ); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose }; } function createCanvasElement() { const canvas = createElementNS( "canvas" ); canvas.style.display = "block"; return canvas; } class WebGLRenderer { constructor( parameters = {} ) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = true, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = "default", failIfMajorPerformanceCaveat = false, } = parameters; this.isWebGLRenderer = true; let _alpha; if ( context !== null ) { _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputColorSpace = SRGBColorSpace; // physical lights this.useLegacyLights = true; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = - 1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4( 0, 0, _width, _height ); const _scissor = new Vector4( 0, 0, _width, _height ); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext( contextNames, contextAttributes ) { for ( let i = 0; i < contextNames.length; i ++ ) { const contextName = contextNames[ i ]; const context = canvas.getContext( contextName, contextAttributes ); if ( context !== null ) return context; } return null; } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat, }; // OffscreenCanvas does not have setAttribute, see #22811 if ( "setAttribute" in canvas ) canvas.setAttribute( "data-engine", `three.js r${REVISION}` ); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener( "webglcontextlost", onContextLost, false ); canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false ); if ( _gl === null ) { const contextNames = [ "webgl2", "webgl", "experimental-webgl" ]; if ( _this.isWebGL1Renderer === true ) { contextNames.shift(); } _gl = getContext( contextNames, contextAttributes ); if ( _gl === null ) { if ( getContext( contextNames ) ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions( _gl ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); extensions.init( capabilities ); utils = new WebGLUtils( _gl, extensions, capabilities ); state = new WebGLState( _gl, extensions, capabilities ); info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); cubemaps = new WebGLCubeMaps( _this ); cubeuvmaps = new WebGLCubeUVMaps( _this ); attributes = new WebGLAttributes( _gl, capabilities ); bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); objects = new WebGLObjects( _gl, geometries, attributes, info ); morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); clipping = new WebGLClipping( properties ); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); materials = new WebGLMaterials( _this, properties ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions, capabilities ); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager( _this, _gl ); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function ( target ) { return target.set( _width, _height ); }; this.setSize = function ( width, height, updateStyle = true ) { if ( xr.isPresenting ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; canvas.width = Math.floor( width * _pixelRatio ); canvas.height = Math.floor( height * _pixelRatio ); if ( updateStyle === true ) { canvas.style.width = width + "px"; canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function ( target ) { return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor( width * pixelRatio ); canvas.height = Math.floor( height * pixelRatio ); this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function ( target ) { return target.copy( _currentViewport ); }; this.getViewport = function ( target ) { return target.copy( _viewport ); }; this.setViewport = function ( x, y, width, height ) { if ( x.isVector4 ) { _viewport.set( x.x, x.y, x.z, x.w ); } else { _viewport.set( x, y, width, height ); } state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissor = function ( target ) { return target.copy( _scissor ); }; this.setScissor = function ( x, y, width, height ) { if ( x.isVector4 ) { _scissor.set( x.x, x.y, x.z, x.w ); } else { _scissor.set( x, y, width, height ); } state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; this.setOpaqueSort = function ( method ) { _opaqueSort = method; }; this.setTransparentSort = function ( method ) { _transparentSort = method; }; // Clearing this.getClearColor = function ( target ) { return target.copy( background.getClearColor() ); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color = true, depth = true, stencil = true ) { let bits = 0; if ( color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; // this.dispose = function () { canvas.removeEventListener( "webglcontextlost", onContextLost, false ); canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener( "sessionstart", onXRSessionStart ); xr.removeEventListener( "sessionend", onXRSessionEnd ); if ( _transmissionRenderTarget ) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError( event ) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage ); } function onMaterialDispose( event ) { const material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReferences( material ); properties.remove( material ); } function releaseMaterialProgramReferences( material ) { const programs = properties.get( material ).programs; if ( programs !== undefined ) { programs.forEach( function ( program ) { programCache.releaseProgram( program ); } ); if ( material.isShaderMaterial ) { programCache.releaseShaderCache( material ); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); const program = setProgram( camera, scene, geometry, material, object ); state.setMaterial( material, frontFaceCW ); // let index = geometry.index; let rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; if ( group !== null ) { drawStart = Math.max( drawStart, group.start * rangeFactor ); drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); } if ( index !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, index.count ); } else if ( position !== undefined && position !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, position.count ); } const drawCount = drawEnd - drawStart; if ( drawCount < 0 || drawCount === Infinity ) return; // bindingStates.setup( object, material, program, geometry, index ); let attribute; let renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { renderer.setMode( _gl.TRIANGLES ); } } else if ( object.isLine ) { let lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } else if ( object.isSprite ) { renderer.setMode( _gl.TRIANGLES ); } if ( object.isInstancedMesh ) { renderer.renderInstances( drawStart, drawCount, object.count ); } else if ( geometry.isInstancedBufferGeometry ) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); renderer.renderInstances( drawStart, drawCount, instanceCount ); } else { renderer.render( drawStart, drawCount ); } }; // Compile this.compile = function ( scene, camera ) { function prepare( material, scene, object ) { if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = FrontSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = DoubleSide; } else { getProgram( material, scene, object ); } } currentRenderState = renderStates.get( scene ); currentRenderState.init(); renderStateStack.push( currentRenderState ); scene.traverseVisible( function ( object ) { if ( object.isLight && object.layers.test( camera.layers ) ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( _this.useLegacyLights ); scene.traverse( function ( object ) { const material = object.material; if ( material ) { if ( Array.isArray( material ) ) { for ( let i = 0; i < material.length; i ++ ) { const material2 = material[ i ]; prepare( material2, scene, object ); } } else { prepare( material, scene, object ); } } } ); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time ) { if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); if ( typeof self !== "undefined" ) animation.setContext( self ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; xr.setAnimationLoop( callback ); ( callback === null ) ? animation.stop() : animation.start(); }; xr.addEventListener( "sessionstart", onXRSessionStart ); xr.addEventListener( "sessionend", onXRSessionEnd ); // Rendering this.render = function ( scene, camera ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost === true ) return; // update scene graph if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); if ( xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering } // if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); currentRenderState = renderStates.get( scene, renderStateStack.length ); currentRenderState.init(); renderStateStack.push( currentRenderState ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromProjectionMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); currentRenderList = renderLists.get( scene, renderListStack.length ); currentRenderList.init(); renderListStack.push( currentRenderList ); projectObject( scene, camera, 0, _this.sortObjects ); currentRenderList.finish(); if ( _this.sortObjects === true ) { currentRenderList.sort( _opaqueSort, _transparentSort ); } // if ( _clippingEnabled === true ) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); if ( _clippingEnabled === true ) clipping.endShadows(); // if ( this.info.autoReset === true ) this.info.reset(); // background.render( currentRenderList, scene ); // render scene currentRenderState.setupLights( _this.useLegacyLights ); if ( camera.isArrayCamera ) { const cameras = camera.cameras; for ( let i = 0, l = cameras.length; i < l; i ++ ) { const camera2 = cameras[ i ]; renderScene( currentRenderList, scene, camera2, camera2.viewport ); } } else { renderScene( currentRenderList, scene, camera ); } // if ( _currentRenderTarget !== null ) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget( _currentRenderTarget ); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap( _currentRenderTarget ); } // if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = - 1; _currentCamera = null; renderStateStack.pop(); if ( renderStateStack.length > 0 ) { currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; } else { currentRenderState = null; } renderListStack.pop(); if ( renderListStack.length > 0 ) { currentRenderList = renderListStack[ renderListStack.length - 1 ]; } else { currentRenderList = null; } }; function projectObject( object, camera, groupOrder, sortObjects ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isGroup ) { groupOrder = object.renderOrder; } else if ( object.isLOD ) { if ( object.autoUpdate === true ) object.update( camera ); } else if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } const geometry = objects.update( object ); const material = object.material; if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { if ( object.isSkinnedMesh ) { // update skeleton only once in a frame if ( object.skeleton.frame !== info.render.frame ) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } const geometry = objects.update( object ); const material = object.material; if ( sortObjects ) { if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _vector3 .copy( geometry.boundingSphere.center ) .applyMatrix4( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, groupOrder, sortObjects ); } } function renderScene( currentRenderList, scene, camera, viewport ) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView( camera ); if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); } function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { if ( _transmissionRenderTarget === null ) { const isWebGL2 = capabilities.isWebGL2; _transmissionRenderTarget = new WebGLRenderTarget( 1024, 1024, { generateMipmaps: true, type: extensions.has( "EXT_color_buffer_half_float" ) ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: ( isWebGL2 && antialias === true ) ? 4 : 0 } ); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget( _transmissionRenderTarget ); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects( opaqueObjects, scene, camera ); textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); let renderTargetNeedsUpdate = false; for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { const renderItem = transmissiveObjects[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject( object, scene, camera, geometry, material, group ); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if ( renderTargetNeedsUpdate === true ) { textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); } _this.setRenderTarget( currentRenderTarget ); _this.toneMapping = currentToneMapping; } function renderObjects( renderList, scene, camera ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for ( let i = 0, l = renderList.length; i < l; i ++ ) { const renderItem = renderList[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if ( object.layers.test( camera.layers ) ) { renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); material.onBeforeRender( _this, scene, camera, geometry, object, group ); if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } function getProgram( material, scene, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); const programCacheKey = programCache.getProgramCacheKey( parameters ); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); if ( programs === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); programs = new Map(); materialProperties.programs = programs; } let program = programs.get( programCacheKey ); if ( program !== undefined ) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties( material, parameters ); return program; } } else { parameters.uniforms = programCache.getUniforms( material ); material.onBuild( object, parameters, _this ); material.onBeforeCompile( parameters, _this ); program = programCache.acquireProgram( parameters, programCacheKey ); programs.set( programCacheKey, program ); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties( material, parameters ); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights( material ); materialProperties.lightsStateVersion = lightsStateVersion; if ( materialProperties.needsLights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties( material, parameters ) { const materialProperties = properties.get( material ); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram( camera, scene, geometry, material, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ); const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; const morphTargets = !! geometry.morphAttributes.position; const morphNormals = !! geometry.morphAttributes.normal; const morphColors = !! geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; if ( _clippingEnabled === true ) { if ( _localClippingEnabled === true || camera !== _currentCamera ) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState( material, camera, useCache ); } } // let needsProgramChange = false; if ( material.version === materialProperties.__version ) { if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { needsProgramChange = true; } else if ( materialProperties.outputColorSpace !== colorSpace ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( materialProperties.envMap !== envMap ) { needsProgramChange = true; } else if ( material.fog === true && materialProperties.fog !== fog ) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection ) ) { needsProgramChange = true; } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { needsProgramChange = true; } else if ( materialProperties.vertexTangents !== vertexTangents ) { needsProgramChange = true; } else if ( materialProperties.morphTargets !== morphTargets ) { needsProgramChange = true; } else if ( materialProperties.morphNormals !== morphNormals ) { needsProgramChange = true; } else if ( materialProperties.morphColors !== morphColors ) { needsProgramChange = true; } else if ( materialProperties.toneMapping !== toneMapping ) { needsProgramChange = true; } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if ( needsProgramChange === true ) { program = getProgram( material, scene, object ); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || _currentCamera !== camera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( _currentCamera !== camera ) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap ) { const uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true ); } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if ( object.isSkinnedMesh ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); const skeleton = object.skeleton; if ( skeleton ) { if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { console.warn( "THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required." ); } } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { morphtargets.update( object, geometry, program ); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue( _gl, "receiveShadow", object.receiveShadow ); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if ( material.isMeshGouraudMaterial && material.envMap !== null ) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); if ( materialProperties.needsLights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog === true ) { materials.refreshFogUniforms( m_uniforms, fog ); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); material.uniformsNeedUpdate = false; } if ( material.isSpriteMaterial ) { p_uniforms.setValue( _gl, "center", object.center ); } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); // UBOs if ( material.isShaderMaterial || material.isRawShaderMaterial ) { const groups = material.uniformsGroups; for ( let i = 0, l = groups.length; i < l; i ++ ) { if ( capabilities.isWebGL2 ) { const group = groups[ i ]; uniformsGroups.update( group, program ); uniformsGroups.bind( group, program ); } else { console.warn( "THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2." ); } } } return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights( material ) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || ( material.isShaderMaterial && material.lights === true ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { properties.get( renderTarget.texture ).__webglTexture = colorTexture; properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__hasExternalTextures = true; if ( renderTargetProperties.__hasExternalTextures ) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if ( extensions.has( "WEBGL_multisampled_render_to_texture" ) === true ) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided" ); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if ( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer( _gl.FRAMEBUFFER, null ); useDefaultFramebuffer = false; } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } else if ( renderTargetProperties.__hasExternalTextures ) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); } const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget ) { framebuffer = __webglFramebuffer[ activeCubeFace ]; isCube = true; } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { state.drawBuffers( renderTarget, framebuffer ); } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { const textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); } else if ( isRenderTarget3D ) { const textureProperties = properties.get( renderTarget.texture ); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); } _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } let framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[ activeCubeFaceIndex ]; } if ( framebuffer ) { state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( "EXT_color_buffer_half_float" ) || ( capabilities.isWebGL2 && extensions.has( "EXT_color_buffer_float" ) ) ); if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( "OES_texture_float" ) || extensions.has( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! halfFloatSupportedByExt ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); } } }; this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { const levelScale = Math.pow( 2, - level ); const width = Math.floor( texture.image.width * levelScale ); const height = Math.floor( texture.image.height * levelScale ); textures.setTexture2D( texture, 0 ); _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); state.unbindTexture(); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); textures.setTexture2D( dstTexture, 0 ); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { if ( srcTexture.isCompressedTexture ) { _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); } } // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { if ( _this.isWebGL1Renderer ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2." ); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); let glTarget; if ( dstTexture.isData3DTexture ) { textures.setTexture3D( dstTexture, 0 ); glTarget = _gl.TEXTURE_3D; } else if ( dstTexture.isDataArrayTexture ) { textures.setTexture2DArray( dstTexture, 0 ); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray." ); return; } _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); } else { if ( srcTexture.isCompressedArrayTexture ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture." ); _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); } else { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } } _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); state.unbindTexture(); }; this.initTexture = function ( texture ) { if ( texture.isCubeTexture ) { textures.setTextureCube( texture, 0 ); } else if ( texture.isData3DTexture ) { textures.setTexture3D( texture, 0 ); } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { textures.setTexture2DArray( texture, 0 ); } else { textures.setTexture2D( texture, 0 ); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } get physicallyCorrectLights() { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); return ! this.useLegacyLights; } set physicallyCorrectLights( value ) { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); this.useLegacyLights = ! value; } get outputEncoding() { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); return this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set outputEncoding( encoding ) { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); this.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace; } } class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.overrideMaterial = null; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } copy( source, recursive ) { super.copy( source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.environment !== null ) this.environment = source.environment.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); if ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness; if ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity; return data; } get autoUpdate() { // @deprecated, r144 console.warn( "THREE.Scene: autoUpdate was renamed to matrixWorldAutoUpdate in r144." ); return this.matrixWorldAutoUpdate; } set autoUpdate( value ) { // @deprecated, r144 console.warn( "THREE.Scene: autoUpdate was renamed to matrixWorldAutoUpdate in r144." ); this.matrixWorldAutoUpdate = value; } } class InterleavedBuffer { constructor( array, stride ) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( let i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } set( value, offset = 0 ) { this.array.set( value, offset ); return this; } clone( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; } const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); const ib = new this.constructor( array, this.stride ); ib.setUsage( this.usage ); return ib; } onUpload( callback ) { this.onUploadCallback = callback; return this; } toJSON( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } const _vector$5 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor( interleavedBuffer, itemSize, offset, normalized = false ) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate( value ) { this.data.needsUpdate = value; } applyMatrix4( m ) { for ( let i = 0, l = this.data.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyMatrix4( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyNormalMatrix( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.transformDirection( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.data.array[ index * this.data.stride + this.offset ] = x; return this; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; } getX( index ) { let x = this.data.array[ index * this.data.stride + this.offset ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } getY( index ) { let y = this.data.array[ index * this.data.stride + this.offset + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } getZ( index ) { let z = this.data.array[ index * this.data.stride + this.offset + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } getW( index ) { let w = this.data.array[ index * this.data.stride + this.offset + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setXY( index, x, y ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } clone( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); } return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); } } toJSON( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interleaved attribute if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } class SpriteMaterial extends Material { constructor( parameters ) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor( material ) { super(); this.isSprite = true; this.type = "Sprite"; if ( _geometry === undefined ) { _geometry = new BufferGeometry(); const float32Array = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0, 0.5, - 0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, - 0.5, 0.5, 0, 0, 1 ] ); const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); _geometry.setAttribute( "position", new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); } this.geometry = _geometry; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } raycast( raycaster, intersects ) { if ( raycaster.camera === null ) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites." ); } _worldScale.setFromMatrixScale( this.matrixWorld ); _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar( - _mvPosition.z ); } const rotation = this.material.rotation; let sin, cos; if ( rotation !== 0 ) { cos = Math.cos( rotation ); sin = Math.sin( rotation ); } const center = this.center; transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvA.set( 0, 0 ); _uvB.set( 1, 0 ); _uvC.set( 1, 1 ); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); if ( intersect === null ) { // check second triangle transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvB.set( 0, 1 ); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); if ( intersect === null ) { return; } } const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), face: null, object: this } ); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.center !== undefined ) this.center.copy( source.center ); this.material = source.material; return this; } } function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { // compute position in camera space _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); // to check if rotation is not zero if ( sin !== undefined ) { _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); } else { _rotatedPosition.copy( _alignedPosition ); } vertexPosition.copy( mvPosition ); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4( _viewWorldMatrix ); } class LineBasicMaterial extends Material { constructor( parameters ) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _start$1 = /*@__PURE__*/ new Vector3(); const _end$1 = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = [ 0 ]; for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { _start$1.fromBufferAttribute( positionAttribute, i - 1 ); _end$1.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += _start$1.distanceTo( _end$1 ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$1.copy( geometry.boundingSphere ); _sphere$1.applyMatrix4( matrixWorld ); _sphere$1.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; // _inverseMatrix$1.copy( matrixWorld ).invert(); _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { const a = index.getX( i ); const b = index.getX( i + 1 ); vStart.fromBufferAttribute( positionAttribute, a ); vEnd.fromBufferAttribute( positionAttribute, b ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { vStart.fromBufferAttribute( positionAttribute, i ); vEnd.fromBufferAttribute( positionAttribute, i + 1 ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { _start.fromBufferAttribute( positionAttribute, i ); _end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } } class PointsMaterial extends Material { constructor( parameters ) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ); _sphere.applyMatrix4( matrixWorld ); _sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; // _inverseMatrix.copy( matrixWorld ).invert(); _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i ++ ) { const a = index.getX( i ); _position$2.fromBufferAttribute( positionAttribute, a ); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end; i < l; i ++ ) { _position$2.fromBufferAttribute( positionAttribute, i ); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { const rayPointDistanceSq = _ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { const intersectPoint = new Vector3(); _ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); const distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint, index: index, face: null, object: object } ); } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); } // Get sequence of points using getPoint( t ) getPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; } // Get list of cumulative segment lengths getLengths( divisions = this.arcLengthDivisions ) { if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint( 0 ); let sum = 0; cache.push( 0 ); for ( let p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping( u, distance ) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[ i ]; const lengthAfter = arcLengths[ i + 1 ]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t const t = ( i + segmentFraction ) / ( il - 1 ); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent( t, optionalTarget ) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; const pt1 = this.getPoint( t1 ); const pt2 = this.getPoint( t2 ); const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); tangent.copy( pt2 ).sub( pt1 ).normalize(); return tangent; } getTangentAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getTangent( t, optionalTarget ); } computeFrenetFrames( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for ( let i = 0; i <= segments; i ++ ) { const u = i / segments; tangents[ i ] = this.getTangentAt( u, new Vector3() ); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs( tangents[ 0 ].x ); const ty = Math.abs( tangents[ 0 ].y ); const tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( let i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); const theta = Math.acos( clamp$1( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { let theta = Math.acos( clamp$1( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( let i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5 ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const points = this.points; const l = points.length; const p = ( l - ( this.closed ? 0 : 1 ) ) * t; let intPoint = Math.floor( p ); let weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } const p1 = points[ intPoint % l ]; const p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max( 3, segments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( let i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); } } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max( 3, Math.floor( widthSegments ) ); heightSegments = Math.max( 2, Math.floor( heightSegments ) ); const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for ( let iy = 0; iy <= heightSegments; iy ++ ) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if ( iy === 0 && thetaStart === 0 ) { uOffset = 0.5 / widthSegments; } else if ( iy === heightSegments && thetaEnd === Math.PI ) { uOffset = - 0.5 / widthSegments; } for ( let ix = 0; ix <= widthSegments; ix ++ ) { const u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.copy( vertex ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u + uOffset, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( let iy = 0; iy < heightSegments; iy ++ ) { for ( let ix = 0; ix < widthSegments; ix ++ ) { const a = grid[ iy ][ ix + 1 ]; const b = grid[ iy ][ ix ]; const c = grid[ iy + 1 ][ ix ]; const d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); } } class Clock { constructor( autoStart = true ) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { const newTime = now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 } class Raycaster { constructor( origin, direction, near = 0, far = Infinity ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); } setFromCamera( coords, camera ) { if ( camera.isPerspectiveCamera ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); this.camera = camera; } else if ( camera.isOrthographicCamera ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); this.camera = camera; } else { console.error( "THREE.Raycaster: Unsupported camera type: " + camera.type ); } } intersectObject( object, recursive = true, intersects = [] ) { intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; } intersectObjects( objects, recursive = true, intersects = [] ) { for ( let i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.layers.test( raycaster.layers ) ) { object.raycast( raycaster, intersects ); } if ( recursive === true ) { const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "register", { detail: { revision: REVISION, } } ) ); } if ( typeof window !== "undefined" ) { if ( window.__THREE__ ) { console.warn( "WARNING: Multiple instances of Three.js being imported." ); } else { window.__THREE__ = REVISION; } } const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; const WGS84A = 6378137.0; const WGS84B = 6356752.31424518; /** * Convert coordinates from geodetic (WGS84) reference to local topocentric * (ENU) reference. * * @param {number} lng Longitude in degrees. * @param {number} lat Latitude in degrees. * @param {number} alt Altitude in meters. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The x, y, z local topocentric ENU coordinates. */ function geodeticToEnu(lng, lat, alt, refLng, refLat, refAlt) { const ecef = geodeticToEcef(lng, lat, alt); return ecefToEnu(ecef[0], ecef[1], ecef[2], refLng, refLat, refAlt); } /** * Convert coordinates from local topocentric (ENU) reference to * geodetic (WGS84) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The longitude, latitude in degrees * and altitude in meters. */ function enuToGeodetic(x, y, z, refLng, refLat, refAlt) { const ecef = enuToEcef(x, y, z, refLng, refLat, refAlt); return ecefToGeodetic(ecef[0], ecef[1], ecef[2]); } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to local topocentric (ENU) reference. * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The x, y, z topocentric ENU coordinates in East, North * and Up directions respectively. */ function ecefToEnu(X, Y, Z, refLng, refLat, refAlt) { const refEcef = geodeticToEcef(refLng, refLat, refAlt); const V = [ X - refEcef[0], Y - refEcef[1], Z - refEcef[2], ]; refLng = refLng * DEG2RAD; refLat = refLat * DEG2RAD; const cosLng = Math.cos(refLng); const sinLng = Math.sin(refLng); const cosLat = Math.cos(refLat); const sinLat = Math.sin(refLat); const x = -sinLng * V[0] + cosLng * V[1]; const y = -sinLat * cosLng * V[0] - sinLat * sinLng * V[1] + cosLat * V[2]; const z = cosLat * cosLng * V[0] + cosLat * sinLng * V[1] + sinLat * V[2]; return [x, y, z]; } /** * Convert coordinates from local topocentric (ENU) reference * to Earth-Centered, Earth-Fixed (ECEF) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The X, Y, Z ECEF coordinates. */ function enuToEcef(x, y, z, refLng, refLat, refAlt) { const refEcef = geodeticToEcef(refLng, refLat, refAlt); refLng = refLng * DEG2RAD; refLat = refLat * DEG2RAD; const cosLng = Math.cos(refLng); const sinLng = Math.sin(refLng); const cosLat = Math.cos(refLat); const sinLat = Math.sin(refLat); const X = -sinLng * x - sinLat * cosLng * y + cosLat * cosLng * z + refEcef[0]; const Y = cosLng * x - sinLat * sinLng * y + cosLat * sinLng * z + refEcef[1]; const Z = cosLat * y + sinLat * z + refEcef[2]; return [X, Y, Z]; } /** * Convert coordinates from geodetic reference (WGS84) to Earth-Centered, * Earth-Fixed (ECEF) reference. * * @param {number} lng Longitude in degrees. * @param {number} lat Latitude in degrees. * @param {number} alt Altitude in meters. * @returns {Array} The X, Y, Z ECEF coordinates. */ function geodeticToEcef(lng, lat, alt) { const a = WGS84A; const b = WGS84B; lng = lng * DEG2RAD; lat = lat * DEG2RAD; const cosLng = Math.cos(lng); const sinLng = Math.sin(lng); const cosLat = Math.cos(lat); const sinLat = Math.sin(lat); const a2 = a * a; const b2 = b * b; const L = 1.0 / Math.sqrt(a2 * cosLat * cosLat + b2 * sinLat * sinLat); const nhcl = (a2 * L + alt) * cosLat; const X = nhcl * cosLng; const Y = nhcl * sinLng; const Z = (b2 * L + alt) * sinLat; return [X, Y, Z]; } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to geodetic reference (WGS84). * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @returns {Array} The longitude, latitude in degrees * and altitude in meters. */ function ecefToGeodetic(X, Y, Z) { const a = WGS84A; const b = WGS84B; const a2 = a * a; const b2 = b * b; const a2mb2 = a2 - b2; const ea = Math.sqrt(a2mb2 / a2); const eb = Math.sqrt(a2mb2 / b2); const p = Math.sqrt(X * X + Y * Y); const theta = Math.atan2(Z * a, p * b); const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); const lng = Math.atan2(Y, X); const lat = Math.atan2(Z + eb * eb * b * sinTheta * sinTheta * sinTheta, p - ea * ea * a * cosTheta * cosTheta * cosTheta); const sinLat = Math.sin(lat); const cosLat = Math.cos(lat); const N = a / Math.sqrt(1 - ea * ea * sinLat * sinLat); const alt = p / cosLat - N; return [ lng * RAD2DEG, lat * RAD2DEG, alt ]; } /** * @class GraphCalculator * * @classdesc Represents a calculator for graph entities. */ class GraphCalculator { /** * Get the bounding box corners for a circle with radius of a threshold * with center in a geodetic position. * * @param {LngLat} lngLat - Longitude, latitude to encode. * @param {number} threshold - Threshold distance from the position in meters. * * @returns {Array} The south west and north east corners of the * bounding box. */ boundingBoxCorners(lngLat, threshold) { const sw = enuToGeodetic(-threshold, -threshold, 0, lngLat.lng, lngLat.lat, 0); const ne = enuToGeodetic(threshold, threshold, 0, lngLat.lng, lngLat.lat, 0); return [ { lat: sw[1], lng: sw[0] }, { lat: ne[1], lng: ne[0] }, ]; } /** * Convert a compass angle to an angle axis rotation vector. * * @param {number} compassAngle - The compass angle in degrees. * @param {number} orientation - The orientation of the original image. * * @returns {Array} Angle axis rotation vector. */ rotationFromCompass(compassAngle, orientation) { let x = 0; let y = 0; let z = 0; switch (orientation) { case 1: x = Math.PI / 2; break; case 3: x = -Math.PI / 2; z = Math.PI; break; case 6: y = -Math.PI / 2; z = -Math.PI / 2; break; case 8: y = Math.PI / 2; z = Math.PI / 2; break; } const rz = new Matrix4() .makeRotationZ(z); const euler = new Euler(x, y, compassAngle * Math.PI / 180, "XYZ"); const re = new Matrix4() .makeRotationFromEuler(euler); const rotation = new Vector4() .setAxisAngleFromRotationMatrix(re.multiply(rz)); return rotation .multiplyScalar(rotation.w) .toArray() .slice(0, 3); } } /** * @class Image * * @classdesc Represents a image in the navigation graph. * * Explanation of position and bearing properties: * * When images are uploaded they will have GPS information in the EXIF, this is what * is called `originalLngLat` {@link Image.originalLngLat}. * * When Structure from Motions has been run for a image a `computedLngLat` that * differs from the `originalLngLat` will be created. It is different because * GPS positions are not very exact and SfM aligns the camera positions according * to the 3D reconstruction {@link Image.computedLngLat}. * * At last there exist a `lngLat` property which evaluates to * the `computedLngLat` from SfM if it exists but falls back * to the `originalLngLat` from the EXIF GPS otherwise {@link Image.lngLat}. * * Everything that is done in in the Viewer is based on the SfM positions, * i.e. `computedLngLat`. That is why the smooth transitions go in the right * direction (nd not in strange directions because of bad GPS). * * E.g. when placing a marker in the Viewer it is relative to the SfM * position i.e. the `computedLngLat`. * * The same concept as above also applies to the compass angle (or bearing) properties * `originalCa`, `computedCa` and `ca`. */ let Image$1 = class Image { /** * Create a new image instance. * * @description Images are always created internally by the library. * Images can not be added to the library through any API method. * * @param {CoreImageEnt} core- Raw core image data. * @ignore */ constructor(core) { if (!core) { throw new Error(`Incorrect core image data ${core}`); } this._cache = null; this._core = core; this._spatial = null; } /** * Get assets cached. * * @description The assets that need to be cached for this property * to report true are the following: fill properties, image and mesh. * The library ensures that the current image will always have the * assets cached. * * @returns {boolean} Value indicating whether all assets have been * cached. * * @ignore */ get assetsCached() { return this._core != null && this._spatial != null && this._cache != null && this._cache.camera != null && this._cache.image != null && this._cache.mesh != null; } /** * Get camera. * * @returns {ICamera} Camera of the image * * @ignore */ get camera() { return this._cache.camera; } /** * Get cameraParameters. * * @description Will be undefined if SfM has * not been run. * * Camera type dependent parameters. * * For perspective and fisheye camera types, * the camera parameters array should be * constructed according to * * `[focal, k1, k2]` * * where focal is the camera focal length, * and k1, k2 are radial distortion parameters. * * For spherical camera type the camera * parameters are unset or emtpy array. * * @returns {Array} The parameters * related to the camera type. */ get cameraParameters() { return this._spatial.camera_parameters; } /** * Get cameraType. * * @description Will be undefined if SfM has not been run. * * @returns {string} The camera type that captured the image. */ get cameraType() { return this._spatial.camera_type; } /** * Get capturedAt. * * @description Timestamp of the image capture date * and time represented as a Unix epoch timestamp in milliseconds. * * @returns {number} Timestamp when the image was captured. */ get capturedAt() { return this._spatial.captured_at; } /** * Get clusterId. * * @returns {string} Globally unique id of the SfM cluster to which * the image belongs. */ get clusterId() { return !!this._spatial.cluster ? this._spatial.cluster.id : null; } /** * Get clusterUrl. * * @returns {string} Url to the cluster reconstruction file. * * @ignore */ get clusterUrl() { return !!this._spatial.cluster ? this._spatial.cluster.url : null; } /** * Get compassAngle. * * @description If the SfM computed compass angle exists it will * be returned, otherwise the original EXIF compass angle. * * @returns {number} Compass angle, measured in degrees * clockwise with respect to north. */ get compassAngle() { return this._spatial.computed_compass_angle != null ? this._spatial.computed_compass_angle : this._spatial.compass_angle; } /** * Get complete. * * @description The library ensures that the current image will * always be full. * * @returns {boolean} Value indicating whether the image has all * properties filled. * * @ignore */ get complete() { return this._spatial != null; } /** * Get computedAltitude. * * @description If SfM has not been run the computed altitude is * set to a default value of two meters. * * @returns {number} Altitude, in meters. */ get computedAltitude() { return this._spatial.computed_altitude; } /** * Get computedCompassAngle. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed compass angle, measured * in degrees clockwise with respect to north. */ get computedCompassAngle() { return this._spatial.computed_compass_angle; } /** * Get computedLngLat. * * @description Will not be set if SfM has not been run. * * @returns {LngLat} SfM computed longitude, latitude in WGS84 datum, * measured in degrees. */ get computedLngLat() { return this._core.computed_geometry; } /** * Get creatorId. * * @description Note that the creator ID will not be set when using * the Mapillary API. * * @returns {string} Globally unique id of the user who uploaded * the image. */ get creatorId() { return this._spatial.creator.id; } /** * Get creatorUsername. * * @description Note that the creator username will not be set when * using the Mapillary API. * * @returns {string} Username of the creator who uploaded * the image. */ get creatorUsername() { return this._spatial.creator.username; } /** * Get exifOrientation. * * @returns {number} EXIF orientation of original image. */ get exifOrientation() { return this._spatial.exif_orientation; } /** * Get height. * * @returns {number} Height of original image, not adjusted * for orientation. */ get height() { return this._spatial.height; } /** * Get image. * * @description The image will always be set on the current image. * * @returns {HTMLImageElement} Cached image element of the image. */ get image() { return this._cache.image; } /** * Get image$. * * @returns {Observable} Observable emitting * the cached image when it is updated. * * @ignore */ get image$() { return this._cache.image$; } /** * Get id. * * @returns {string} Globally unique id of the image. */ get id() { return this._core.id; } /** * Get is disposed. * * @description If the image has been disposed no properties * are acessible. Image are disposed when the {@link Viewer} * is reset or a new data provider is set. * * @returns {boolean} Value indicating that this image * has been disposed. */ get isDisposed() { return !this._core; } /** * Get is complete. * * @returns {boolean} Value indicating that this image * is complete. * * @ignore */ get isComplete() { return !!this._core && !!this._spatial; } /** * Get lngLat. * * @description If the SfM computed longitude, latitude exist * it will be returned, otherwise the original EXIF latitude * longitude. * * @returns {LngLat} Longitude, latitude in WGS84 datum, * measured in degrees. */ get lngLat() { return this._core.computed_geometry != null ? this._core.computed_geometry : this._core.geometry; } /** * Get merged. * * @returns {boolean} Value indicating whether SfM has been * run on the image and the image has been merged into a * connected component. */ get merged() { return this._spatial != null && this._spatial.merge_id != null; } /** * Get mergeId. * * @description Will not be set if SfM has not yet been run on * image. * * @returns {stirng} Id of connected component to which image * belongs after the aligning merge. */ get mergeId() { return this._spatial.merge_id; } /** * Get mesh. * * @description The mesh will always be set on the current image. * * @returns {MeshContract} SfM triangulated mesh of reconstructed * atomic 3D points. */ get mesh() { return this._cache.mesh; } /** * Get originalAltitude. * * @returns {number} EXIF altitude, in meters, if available. */ get originalAltitude() { return this._spatial.altitude; } /** * Get originalCompassAngle. * * @returns {number} Original EXIF compass angle, measured in * degrees. */ get originalCompassAngle() { return this._spatial.compass_angle; } /** * Get originalLngLat. * * @returns {LngLat} Original EXIF longitude, latitude in * WGS84 datum, measured in degrees. */ get originalLngLat() { return this._core.geometry; } /** * Get ownerId. * * @returns {string} Globally unique id of the owner to which * the image belongs. If the image does not belong to an * owner the owner id will be undefined. */ get ownerId() { return !!this._spatial.owner ? this._spatial.owner.id : null; } /** * Get private. * * @returns {boolean} Value specifying if image is accessible to * organization members only or to everyone. */ get private() { return this._spatial.private; } /** * Get qualityScore. * * @returns {number} A number between zero and one * determining the quality of the image. Blurriness * (motion blur / out-of-focus), occlusion (camera * mount, ego vehicle, water-drops), windshield * reflections, bad illumination (exposure, glare), * and bad weather condition (fog, rain, snow) * affect the quality score. * * @description Value should be on the interval [0, 1]. */ get qualityScore() { return this._spatial.quality_score; } /** * Get rotation. * * @description Will not be set if SfM has not been run. * * @returns {Array} Rotation vector in angle axis representation. */ get rotation() { return this._spatial.computed_rotation; } /** * Get scale. * * @description Will not be set if SfM has not been run. * * @returns {number} Scale of reconstruction the image * belongs to. */ get scale() { return this._spatial.atomic_scale; } /** * Get sequenceId. * * @returns {string} Globally unique id of the sequence * to which the image belongs. */ get sequenceId() { return !!this._core.sequence ? this._core.sequence.id : null; } /** * Get sequenceEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * sequence edges. * * @ignore */ get sequenceEdges() { return this._cache.sequenceEdges; } /** * Get sequenceEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable} Observable emitting * values describing the status of the sequence edges. * * @ignore */ get sequenceEdges$() { return this._cache.sequenceEdges$; } /** * Get spatialEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * spatial edges. * * @ignore */ get spatialEdges() { return this._cache.spatialEdges; } /** * Get spatialEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable} Observable emitting * values describing the status of the spatial edges. * * @ignore */ get spatialEdges$() { return this._cache.spatialEdges$; } /** * Get width. * * @returns {number} Width of original image, not * adjusted for orientation. */ get width() { return this._spatial.width; } /** * Cache the image and mesh assets. * * @description The assets are always cached internally by the * library prior to setting a image as the current image. * * @returns {Observable} Observable emitting this image whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ cacheAssets$(factory) { this.cacheCamera(factory); return this._cache .cacheAssets$(this._spatial, this.merged) .pipe(map(() => { return this; })); } /** * Cache the image camera. * * @ignore */ cacheCamera(factory) { this._cache.cacheCamera(factory.makeCamera(this._spatial.camera_type, this._spatial.camera_parameters)); } /** * Cache the image asset. * * @description Use for caching a differently sized image than * the one currently held by the image. * * @returns {Observable} Observable emitting this image whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ cacheImage$() { return this._cache .cacheImage$(this._spatial) .pipe(map(() => { return this; })); } /** * Cache the sequence edges. * * @description The sequence edges are cached asynchronously * internally by the library. * * @param {Array} edges - Sequence edges to cache. * @ignore */ cacheSequenceEdges(edges) { this._cache.cacheSequenceEdges(edges); } /** * Cache the spatial edges. * * @description The spatial edges are cached asynchronously * internally by the library. * * @param {Array} edges - Spatial edges to cache. * @ignore */ cacheSpatialEdges(edges) { this._cache.cacheSpatialEdges(edges); } /** * Dispose the image. * * @description Disposes all cached assets. * @ignore */ dispose() { if (this._cache != null) { this._cache.dispose(); this._cache = null; } this._core = null; this._spatial = null; } /** * Returns a value indicating if the image has an initialized * image cache. * * @description The image cache is initialized internally by * the library. * * @ignore */ hasInitializedCache() { return this._cache != null; } /** * Initialize the image cache. * * @description The image cache is initialized internally by * the library. * * @param {ImageCache} cache - The image cache to set as cache. * @ignore */ initializeCache(cache) { if (this._cache != null) { throw new Error(`Image cache already initialized (${this.id}).`); } this._cache = cache; } /** * Complete an image with spatial properties. * * @description The image is completed internally by * the library. * * @param {SpatialImageEnt} fill - The spatial image struct. * @ignore */ makeComplete(fill) { if (fill == null) { throw new Error("Fill can not be null."); } this._spatial = fill; } /** * Reset the sequence edges. * * @ignore */ resetSequenceEdges() { this._cache.resetSequenceEdges(); } /** * Reset the spatial edges. * * @ignore */ resetSpatialEdges() { this._cache.resetSpatialEdges(); } /** * Clears the image and mesh assets, aborts * any outstanding requests and resets edges. * * @ignore */ uncache() { if (this._cache == null) { return; } this._cache.dispose(); this._cache = null; } }; /** * @class ImageCache * * @classdesc Represents the cached properties of a image. */ class ImageCache { /** * Create a new image cache instance. */ constructor(provider) { this._disposed = false; this._provider = provider; this._camera = null; this._image = null; this._mesh = null; this._sequenceEdges = { cached: false, edges: [] }; this._spatialEdges = { cached: false, edges: [] }; this._imageChanged$ = new Subject(); this._image$ = this._imageChanged$.pipe(startWith(null), publishReplay(1), refCount()); this._iamgeSubscription = this._image$.subscribe(); this._sequenceEdgesChanged$ = new Subject(); this._sequenceEdges$ = this._sequenceEdgesChanged$.pipe(startWith(this._sequenceEdges), publishReplay(1), refCount()); this._sequenceEdgesSubscription = this._sequenceEdges$.subscribe(() => { }); this._spatialEdgesChanged$ = new Subject(); this._spatialEdges$ = this._spatialEdgesChanged$.pipe(startWith(this._spatialEdges), publishReplay(1), refCount()); this._spatialEdgesSubscription = this._spatialEdges$.subscribe(() => { }); this._cachingAssets$ = null; } get camera() { return this._camera; } /** * Get image. * * @description Will not be set when assets have not been cached * or when the object has been disposed. * * @returns {HTMLImageElement} Cached image element of the image. */ get image() { return this._image; } /** * Get image$. * * @returns {Observable} Observable emitting * the cached image when it is updated. */ get image$() { return this._image$; } /** * Get mesh. * * @description Will not be set when assets have not been cached * or when the object has been disposed. * * @returns {MeshContract} SfM triangulated mesh of reconstructed * atomic 3D points. */ get mesh() { return this._mesh; } /** * Get sequenceEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * sequence edges. */ get sequenceEdges() { return this._sequenceEdges; } /** * Get sequenceEdges$. * * @returns {Observable} Observable emitting * values describing the status of the sequence edges. */ get sequenceEdges$() { return this._sequenceEdges$; } /** * Get spatialEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * spatial edges. */ get spatialEdges() { return this._spatialEdges; } /** * Get spatialEdges$. * * @returns {Observable} Observable emitting * values describing the status of the spatial edges. */ get spatialEdges$() { return this._spatialEdges$; } cacheCamera(camera) { this._camera = camera; } /** * Cache the image and mesh assets. * * @param {SpatialImageEnt} spatial - Spatial props of the image to cache. * @param {boolean} spherical - Value indicating whether image is a spherical. * @param {boolean} merged - Value indicating whether image is merged. * @returns {Observable} Observable emitting this image * cache whenever the load status has changed and when the mesh or image * has been fully loaded. */ cacheAssets$(spatial, merged) { if (this._cachingAssets$ != null) { return this._cachingAssets$; } const cachingAssets$ = combineLatest(this._cacheImage$(spatial), this._cacheMesh$(spatial, merged)).pipe(map(([image, mesh]) => { this._image = image; this._mesh = mesh; return this; }), finalize(() => { this._cachingAssets$ = null; }), publishReplay(1), refCount()); this._cachingAssets$ = cachingAssets$; cachingAssets$.pipe(first((imageCache) => { return !!imageCache._image; })) .subscribe(() => { this._imageChanged$.next(this._image); }, () => { }); return cachingAssets$; } /** * Cache an image with a higher resolution than the current one. * * @param {SpatialImageEnt} spatial - Spatial props. * @returns {Observable} Observable emitting a single item, * the image cache, when the image has been cached. If supplied image * size is not larger than the current image size the image cache is * returned immediately. */ cacheImage$(spatial) { if (this._image != null) { return of(this); } const cacheImage$ = this._cacheImage$(spatial) .pipe(first((image) => { return !!image; }), tap((image) => { this._disposeImage(); this._image = image; }), map(() => { return this; }), publishReplay(1), refCount()); cacheImage$ .subscribe(() => { this._imageChanged$.next(this._image); }, () => { }); return cacheImage$; } /** * Cache the sequence edges. * * @param {Array} edges - Sequence edges to cache. */ cacheSequenceEdges(edges) { this._sequenceEdges = { cached: true, edges: edges }; this._sequenceEdgesChanged$.next(this._sequenceEdges); } /** * Cache the spatial edges. * * @param {Array} edges - Spatial edges to cache. */ cacheSpatialEdges(edges) { this._spatialEdges = { cached: true, edges: edges }; this._spatialEdgesChanged$.next(this._spatialEdges); } /** * Dispose the image cache. * * @description Disposes all cached assets and unsubscribes to * all streams. */ dispose() { this._iamgeSubscription.unsubscribe(); this._sequenceEdgesSubscription.unsubscribe(); this._spatialEdgesSubscription.unsubscribe(); this._disposeImage(); this._camera = null; this._mesh = null; this._sequenceEdges = { cached: false, edges: [] }; this._spatialEdges = { cached: false, edges: [] }; this._imageChanged$.next(null); this._sequenceEdgesChanged$.next(this._sequenceEdges); this._spatialEdgesChanged$.next(this._spatialEdges); this._disposed = true; if (this._imageAborter != null) { this._imageAborter(); this._imageAborter = null; } if (this._meshAborter != null) { this._meshAborter(); this._meshAborter = null; } } /** * Reset the sequence edges. */ resetSequenceEdges() { this._sequenceEdges = { cached: false, edges: [] }; this._sequenceEdgesChanged$.next(this._sequenceEdges); } /** * Reset the spatial edges. */ resetSpatialEdges() { this._spatialEdges = { cached: false, edges: [] }; this._spatialEdgesChanged$.next(this._spatialEdges); } /** * Cache the image. * * @param {SpatialImageEnt} spatial - Spatial image. * @param {boolean} spherical - Value indicating whether image is a spherical. * @returns {Observable>} Observable * emitting a load status object every time the load status changes * and completes when the image is fully loaded. */ _cacheImage$(spatial) { return Observable.create((subscriber) => { const abort = new Promise((_, reject) => { this._imageAborter = reject; }); const url = spatial.thumb.url; if (!url) { const thumbId = spatial.thumb.id; const message = `Incorrect thumb URL for ${spatial.id} ` + `(${thumbId}, ${url})`; subscriber.error(new Error(message)); return; } this._provider.getImageBuffer(url, abort) .then((buffer) => { this._imageAborter = null; const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { if (this._disposed) { window.URL.revokeObjectURL(image.src); const message = `Image load was aborted (${url})`; subscriber.error(new Error(message)); return; } subscriber.next(image); subscriber.complete(); }; image.onerror = () => { this._imageAborter = null; subscriber.error(new Error(`Failed to load image (${url})`)); }; const blob = new Blob([buffer]); image.src = window.URL.createObjectURL(blob); }, (error) => { this._imageAborter = null; subscriber.error(error); }); }); } /** * Cache the mesh. * * @param {SpatialImageEnt} spatial - Spatial props. * @param {boolean} merged - Value indicating whether image is merged. * @returns {Observable>} Observable emitting * a load status object every time the load status changes and completes * when the mesh is fully loaded. */ _cacheMesh$(spatial, merged) { return Observable.create((subscriber) => { if (!merged) { subscriber.next(this._createEmptyMesh()); subscriber.complete(); return; } const url = spatial.mesh.url; if (!url) { const meshId = spatial.mesh.id; const message = `Incorrect mesh URL for ${spatial.id} ` + `(${meshId}, ${url})`; console.warn(message); subscriber.next(this._createEmptyMesh()); subscriber.complete(); return; } const abort = new Promise((_, reject) => { this._meshAborter = reject; }); this._provider.getMesh(url, abort) .then((mesh) => { this._meshAborter = null; if (this._disposed) { return; } subscriber.next(mesh); subscriber.complete(); }, (error) => { this._meshAborter = null; console.error(error); subscriber.next(this._createEmptyMesh()); subscriber.complete(); }); }); } /** * Create a load status object with an empty mesh. * * @returns {ILoadStatusObject} Load status object * with empty mesh. */ _createEmptyMesh() { return { faces: [], vertices: [] }; } _disposeImage() { if (this._image != null) { window.URL.revokeObjectURL(this._image.src); } this._image = null; } } /** * @class Sequence * * @classdesc Represents a sequence of ordered images. */ class Sequence { /** * Create a new sequene instance. * * @param {SequenceEnt} sequence - Raw sequence data. */ constructor(sequence) { this._id = sequence.id; this._imageIds = sequence.image_ids; } /** * Get id. * * @returns {string} Unique sequence id. */ get id() { return this._id; } /** * Get ids. * * @returns {Array} Array of ordered image ids in the sequence. */ get imageIds() { return this._imageIds; } /** * Dispose the sequence. * * @description Disposes all cached assets. */ dispose() { this._id = null; this._imageIds = null; } /** * Find the next image id in the sequence with respect to * the provided image id. * * @param {string} id - Reference image id. * @returns {string} Next id in sequence if it exists, null otherwise. */ findNext(id) { let i = this._imageIds.indexOf(id); if ((i + 1) >= this._imageIds.length || i === -1) { return null; } else { return this._imageIds[i + 1]; } } /** * Find the previous image id in the sequence with respect to * the provided image id. * * @param {string} id - Reference image id. * @returns {string} Previous id in sequence if it exists, null otherwise. */ findPrev(id) { let i = this._imageIds.indexOf(id); if (i === 0 || i === -1) { return null; } else { return this._imageIds[i - 1]; } } } class EdgeCalculatorCoefficients { constructor() { this.sphericalPreferredDistance = 2; this.sphericalMotion = 2; this.sphericalSequencePenalty = 1; this.sphericalMergeCCPenalty = 4; this.stepPreferredDistance = 4; this.stepMotion = 3; this.stepRotation = 4; this.stepSequencePenalty = 2; this.stepMergeCCPenalty = 6; this.similarDistance = 2; this.similarRotation = 3; this.turnDistance = 4; this.turnMotion = 2; this.turnSequencePenalty = 1; this.turnMergeCCPenalty = 4; } } /** * Enumeration for edge directions * @enum {number} * @readonly * @description Directions for edges in image graph describing * sequence, spatial and image type relations between nodes. */ exports.NavigationDirection = void 0; (function (NavigationDirection) { /** * Next image in the sequence. */ NavigationDirection[NavigationDirection["Next"] = 0] = "Next"; /** * Previous image in the sequence. */ NavigationDirection[NavigationDirection["Prev"] = 1] = "Prev"; /** * Step to the left keeping viewing direction. */ NavigationDirection[NavigationDirection["StepLeft"] = 2] = "StepLeft"; /** * Step to the right keeping viewing direction. */ NavigationDirection[NavigationDirection["StepRight"] = 3] = "StepRight"; /** * Step forward keeping viewing direction. */ NavigationDirection[NavigationDirection["StepForward"] = 4] = "StepForward"; /** * Step backward keeping viewing direction. */ NavigationDirection[NavigationDirection["StepBackward"] = 5] = "StepBackward"; /** * Turn 90 degrees counter clockwise. */ NavigationDirection[NavigationDirection["TurnLeft"] = 6] = "TurnLeft"; /** * Turn 90 degrees clockwise. */ NavigationDirection[NavigationDirection["TurnRight"] = 7] = "TurnRight"; /** * Turn 180 degrees. */ NavigationDirection[NavigationDirection["TurnU"] = 8] = "TurnU"; /** * Spherical in general direction. */ NavigationDirection[NavigationDirection["Spherical"] = 9] = "Spherical"; /** * Looking in roughly the same direction at rougly the same position. */ NavigationDirection[NavigationDirection["Similar"] = 10] = "Similar"; })(exports.NavigationDirection || (exports.NavigationDirection = {})); class EdgeCalculatorDirections { constructor() { this.steps = {}; this.turns = {}; this.spherical = {}; this.steps[exports.NavigationDirection.StepForward] = { direction: exports.NavigationDirection.StepForward, motionChange: 0, useFallback: true, }; this.steps[exports.NavigationDirection.StepBackward] = { direction: exports.NavigationDirection.StepBackward, motionChange: Math.PI, useFallback: true, }; this.steps[exports.NavigationDirection.StepLeft] = { direction: exports.NavigationDirection.StepLeft, motionChange: Math.PI / 2, useFallback: false, }; this.steps[exports.NavigationDirection.StepRight] = { direction: exports.NavigationDirection.StepRight, motionChange: -Math.PI / 2, useFallback: false, }; this.turns[exports.NavigationDirection.TurnLeft] = { direction: exports.NavigationDirection.TurnLeft, directionChange: Math.PI / 2, motionChange: Math.PI / 4, }; this.turns[exports.NavigationDirection.TurnRight] = { direction: exports.NavigationDirection.TurnRight, directionChange: -Math.PI / 2, motionChange: -Math.PI / 4, }; this.turns[exports.NavigationDirection.TurnU] = { direction: exports.NavigationDirection.TurnU, directionChange: Math.PI, motionChange: null, }; this.spherical[exports.NavigationDirection.StepForward] = { direction: exports.NavigationDirection.StepForward, directionChange: 0, next: exports.NavigationDirection.StepLeft, prev: exports.NavigationDirection.StepRight, }; this.spherical[exports.NavigationDirection.StepBackward] = { direction: exports.NavigationDirection.StepBackward, directionChange: Math.PI, next: exports.NavigationDirection.StepRight, prev: exports.NavigationDirection.StepLeft, }; this.spherical[exports.NavigationDirection.StepLeft] = { direction: exports.NavigationDirection.StepLeft, directionChange: Math.PI / 2, next: exports.NavigationDirection.StepBackward, prev: exports.NavigationDirection.StepForward, }; this.spherical[exports.NavigationDirection.StepRight] = { direction: exports.NavigationDirection.StepRight, directionChange: -Math.PI / 2, next: exports.NavigationDirection.StepForward, prev: exports.NavigationDirection.StepBackward, }; } } class EdgeCalculatorSettings { constructor() { this.sphericalMinDistance = 0.1; this.sphericalMaxDistance = 20; this.sphericalPreferredDistance = 5; this.sphericalMaxItems = 4; this.sphericalMaxStepTurnChange = Math.PI / 8; this.rotationMaxDistance = this.turnMaxRigDistance; this.rotationMaxDirectionChange = Math.PI / 6; this.rotationMaxVerticalDirectionChange = Math.PI / 8; this.similarMaxDirectionChange = Math.PI / 8; this.similarMaxDistance = 12; this.similarMinTimeDifference = 12 * 3600 * 1000; this.stepMaxDistance = 20; this.stepMaxDirectionChange = Math.PI / 6; this.stepMaxDrift = Math.PI / 6; this.stepPreferredDistance = 4; this.turnMaxDistance = 15; this.turnMaxDirectionChange = 2 * Math.PI / 9; this.turnMaxRigDistance = 0.65; this.turnMinRigDirectionChange = Math.PI / 6; } get maxDistance() { return Math.max(this.sphericalMaxDistance, this.similarMaxDistance, this.stepMaxDistance, this.turnMaxDistance); } } /** * @class MapillaryError * * @classdesc Generic Mapillary error. */ class MapillaryError extends Error { constructor(message) { super(message); Object.setPrototypeOf(this, MapillaryError.prototype); this.name = "MapillaryError"; } } class ArgumentMapillaryError extends MapillaryError { constructor(message) { super(message != null ? message : "The argument is not valid."); Object.setPrototypeOf(this, ArgumentMapillaryError.prototype); this.name = "ArgumentMapillaryError"; } } /** * @class Spatial * * @classdesc Provides methods for scalar, vector and matrix calculations. */ class Spatial { constructor() { this._epsilon = 1e-9; } /** * Converts azimuthal phi rotation (counter-clockwise with origin on X-axis) to * bearing (clockwise with origin at north or Y-axis). * * @param {number} phi - Azimuthal phi angle in radians. * @returns {number} Bearing in radians. */ azimuthalToBearing(phi) { return -phi + Math.PI / 2; } /** * Converts degrees to radians. * * @param {number} deg - Degrees. * @returns {number} Radians. */ degToRad(deg) { return Math.PI * deg / 180; } /** * Converts radians to degrees. * * @param {number} rad - Radians. * @returns {number} Degrees. */ radToDeg(rad) { return 180 * rad / Math.PI; } /** * Creates a rotation matrix from an angle-axis vector. * * @param {Array} angleAxis - Angle-axis representation of a rotation. * @returns {THREE.Matrix4} Rotation matrix. */ rotationMatrix(angleAxis) { let axis = new Vector3(angleAxis[0], angleAxis[1], angleAxis[2]); let angle = axis.length(); if (angle > 0) { axis.normalize(); } return new Matrix4().makeRotationAxis(axis, angle); } /** * Rotates a vector according to a angle-axis rotation vector. * * @param {Array} vector - Vector to rotate. * @param {Array} angleAxis - Angle-axis representation of a rotation. * @returns {THREE.Vector3} Rotated vector. */ rotate(vector, angleAxis) { let v = new Vector3(vector[0], vector[1], vector[2]); let rotationMatrix = this.rotationMatrix(angleAxis); v.applyMatrix4(rotationMatrix); return v; } /** * Calculates the optical center from a rotation vector * on the angle-axis representation and a translation vector * according to C = -R^T t. * * @param {Array} rotation - Angle-axis representation of a rotation. * @param {Array} translation - Translation vector. * @returns {THREE.Vector3} Optical center. */ opticalCenter(rotation, translation) { let angleAxis = [-rotation[0], -rotation[1], -rotation[2]]; let vector = [-translation[0], -translation[1], -translation[2]]; return this.rotate(vector, angleAxis); } /** * Calculates the viewing direction from a rotation vector * on the angle-axis representation. * * @param {number[]} rotation - Angle-axis representation of a rotation. * @returns {THREE.Vector3} Viewing direction. */ viewingDirection(rotation) { let angleAxis = [-rotation[0], -rotation[1], -rotation[2]]; return this.rotate([0, 0, 1], angleAxis); } /** * Wrap a number on the interval [min, max]. * * @param {number} value - Value to wrap. * @param {number} min - Lower endpoint of interval. * @param {number} max - Upper endpoint of interval. * @returns {number} The wrapped number. */ wrap(value, min, max) { if (max < min) { throw new Error("Invalid arguments: max must be larger than min."); } let interval = (max - min); while (value > max || value < min) { if (value > max) { value = value - interval; } else if (value < min) { value = value + interval; } } return value; } /** * Wrap an angle on the interval [-Pi, Pi]. * * @param {number} angle - Value to wrap. * @returns {number} Wrapped angle. */ wrapAngle(angle) { return this.wrap(angle, -Math.PI, Math.PI); } /** * Limit the value to the interval [min, max] by changing the value to * the nearest available one when it is outside the interval. * * @param {number} value - Value to clamp. * @param {number} min - Minimum of the interval. * @param {number} max - Maximum of the interval. * @returns {number} Clamped value. */ clamp(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } /** * Calculates the counter-clockwise angle from the first * vector (x1, y1)^T to the second (x2, y2)^T. * * @param {number} x1 - X coordinate of first vector. * @param {number} y1 - Y coordinate of first vector. * @param {number} x2 - X coordinate of second vector. * @param {number} y2 - Y coordinate of second vector. * @returns {number} Counter clockwise angle between the vectors. */ angleBetweenVector2(x1, y1, x2, y2) { let angle = Math.atan2(y2, x2) - Math.atan2(y1, x1); return this.wrapAngle(angle); } /** * Calculates the minimum (absolute) angle change for rotation * from one angle to another on the [-Pi, Pi] interval. * * @param {number} angle1 - Start angle. * @param {number} angle2 - Destination angle. * @returns {number} Absolute angle change between angles. */ angleDifference(angle1, angle2) { let angle = angle2 - angle1; return this.wrapAngle(angle); } /** * Calculates the relative rotation angle between two * angle-axis vectors. * * @param {number} rotation1 - First angle-axis vector. * @param {number} rotation2 - Second angle-axis vector. * @returns {number} Relative rotation angle. */ relativeRotationAngle(rotation1, rotation2) { let R1T = this.rotationMatrix([-rotation1[0], -rotation1[1], -rotation1[2]]); let R2 = this.rotationMatrix(rotation2); let R = R1T.multiply(R2); let elements = R.elements; // from Tr(R) = 1 + 2 * cos(theta) let tr = elements[0] + elements[5] + elements[10]; let theta = Math.acos(Math.max(Math.min((tr - 1) / 2, 1), -1)); return theta; } /** * Calculates the angle from a vector to a plane. * * @param {Array} vector - The vector. * @param {Array} planeNormal - Normal of the plane. * @returns {number} Angle from between plane and vector. */ angleToPlane(vector, planeNormal) { let v = new Vector3().fromArray(vector); let norm = v.length(); if (norm < this._epsilon) { return 0; } let projection = v.dot(new Vector3().fromArray(planeNormal)); return Math.asin(projection / norm); } /** * Calculates the projection of a vector onto a plane. * * @param {Array} vector - The vector. * @param {Array} planeNormal - Normal of the plane. * @returns {number} Projection of vector onto plane. */ projectToPlane(vector, planeNormal) { const directionVector = new Vector3().fromArray(vector); const normalVector = new Vector3().fromArray(planeNormal); const normalProjection = directionVector.clone().dot(normalVector); const planeProjection = directionVector .clone().sub(normalVector.clone().multiplyScalar(normalProjection)); return planeProjection.toArray(); } azimuthal(direction, up) { const directionVector = new Vector3().fromArray(direction); const upVector = new Vector3().fromArray(up); const upProjection = directionVector.clone().dot(upVector); const planeProjection = directionVector.clone().sub(upVector.clone().multiplyScalar(upProjection)); return Math.atan2(planeProjection.y, planeProjection.x); } /** * Calculates the distance between two coordinates * (longitude, latitude pairs) in meters according to * the haversine formula. * * @param {number} lat1 - Latitude of the first coordinate in degrees. * @param {number} lng1 - Longitude of the first coordinate in degrees. * @param {number} lat2 - Latitude of the second coordinate in degrees. * @param {number} lng2 - Longitude of the second coordinate in degrees. * @returns {number} Distance between lat lon positions in meters. */ distanceFromLngLat(lng1, lat1, lng2, lat2) { let r = 6371000; let dLat = this.degToRad(lat2 - lat1); let dLng = this.degToRad(lng2 - lng1); let hav = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.degToRad(lat1)) * Math.cos(this.degToRad(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2); let d = 2 * r * Math.atan2(Math.sqrt(hav), Math.sqrt(1 - hav)); return d; } } const spatial = new Spatial(); function isSpherical(cameraType) { return cameraType === "spherical"; } function isFisheye(cameraType) { return cameraType === "fisheye"; } function computeTranslation(position, rotation, reference) { const C = geodeticToEnu(position.lng, position.lat, position.alt, reference.lng, reference.lat, reference.alt); const RC = spatial.rotate(C, rotation); const translation = [-RC.x, -RC.y, -RC.z]; return translation; } function computeBearings(transform, basicVertices, basicDirections, pointsPerLine, viewportCoords) { // @ts-ignore const camera = new Camera$2(); camera.up.copy(transform.upVector()); camera.position.copy(new Vector3().fromArray(transform.unprojectSfM([0, 0], 0))); camera.lookAt(new Vector3().fromArray(transform.unprojectSfM([0, 0], 10))); return computeCameraBearings(camera, transform, basicVertices, basicDirections, pointsPerLine, viewportCoords); } function computeCameraBearings(camera, transform, basicVertices, basicDirections, pointsPerLine, viewportCoords) { const basicPoints = []; for (let side = 0; side < basicVertices.length; ++side) { const v = basicVertices[side]; const d = basicDirections[side]; for (let i = 0; i <= pointsPerLine; ++i) { basicPoints.push([ v[0] + d[0] * i / pointsPerLine, v[1] + d[1] * i / pointsPerLine, ]); } } camera.updateMatrix(); camera.updateMatrixWorld(true); const bearings = []; for (const basicPoint of basicPoints) { const worldPoint = transform.unprojectBasic(basicPoint, 10000); const cameraPoint = new Vector3() .fromArray(viewportCoords.worldToCamera(worldPoint, camera)); cameraPoint.normalize(); bearings.push(cameraPoint.toArray()); } return bearings; } /** * @class EdgeCalculator * * @classdesc Represents a class for calculating node edges. */ class EdgeCalculator { /** * Create a new edge calculator instance. * * @param {EdgeCalculatorSettings} settings - Settings struct. * @param {EdgeCalculatorDirections} directions - Directions struct. * @param {EdgeCalculatorCoefficients} coefficients - Coefficients struct. */ constructor(settings, directions, coefficients) { this._spatial = new Spatial(); this._settings = settings != null ? settings : new EdgeCalculatorSettings(); this._directions = directions != null ? directions : new EdgeCalculatorDirections(); this._coefficients = coefficients != null ? coefficients : new EdgeCalculatorCoefficients(); } /** * Returns the potential edges to destination nodes for a set * of nodes with respect to a source node. * * @param {Image} node - Source node. * @param {Array} nodes - Potential destination nodes. * @param {Array} fallbackIds - Ids for destination nodes * that should be returned even if they do not meet the * criteria for a potential edge. * @throws {ArgumentMapillaryError} If node is not full. */ getPotentialEdges(node, potentialImages, fallbackIds) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (!node.merged) { return []; } let currentDirection = this._spatial.viewingDirection(node.rotation); let currentVerticalDirection = this._spatial.angleToPlane(currentDirection.toArray(), [0, 0, 1]); let potentialEdges = []; for (let potential of potentialImages) { if (!potential.merged || potential.id === node.id) { continue; } let enu = geodeticToEnu(potential.lngLat.lng, potential.lngLat.lat, potential.computedAltitude, node.lngLat.lng, node.lngLat.lat, node.computedAltitude); let motion = new Vector3(enu[0], enu[1], enu[2]); let distance = motion.length(); if (distance > this._settings.maxDistance && fallbackIds.indexOf(potential.id) < 0) { continue; } let motionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, motion.x, motion.y); let verticalMotion = this._spatial.angleToPlane(motion.toArray(), [0, 0, 1]); let direction = this._spatial.viewingDirection(potential.rotation); let directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); let verticalDirection = this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); let verticalDirectionChange = verticalDirection - currentVerticalDirection; let rotation = this._spatial.relativeRotationAngle(node.rotation, potential.rotation); let worldMotionAzimuth = this._spatial.angleBetweenVector2(1, 0, motion.x, motion.y); let sameSequence = potential.sequenceId != null && node.sequenceId != null && potential.sequenceId === node.sequenceId; let sameMergeCC = potential.mergeId === node.mergeId; let sameUser = potential.creatorId === node.creatorId; let potentialEdge = { capturedAt: potential.capturedAt, directionChange: directionChange, distance: distance, spherical: isSpherical(potential.cameraType), id: potential.id, motionChange: motionChange, rotation: rotation, sameMergeCC: sameMergeCC, sameSequence: sameSequence, sameUser: sameUser, sequenceId: potential.sequenceId, verticalDirectionChange: verticalDirectionChange, verticalMotion: verticalMotion, worldMotionAzimuth: worldMotionAzimuth, }; potentialEdges.push(potentialEdge); } return potentialEdges; } /** * Computes the sequence edges for a node. * * @param {Image} node - Source node. * @throws {ArgumentMapillaryError} If node is not full. */ computeSequenceEdges(node, sequence) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (node.sequenceId !== sequence.id) { throw new ArgumentMapillaryError("Image and sequence does not correspond."); } let edges = []; let nextId = sequence.findNext(node.id); if (nextId != null) { edges.push({ data: { direction: exports.NavigationDirection.Next, worldMotionAzimuth: Number.NaN, }, source: node.id, target: nextId, }); } let prevId = sequence.findPrev(node.id); if (prevId != null) { edges.push({ data: { direction: exports.NavigationDirection.Prev, worldMotionAzimuth: Number.NaN, }, source: node.id, target: prevId, }); } return edges; } /** * Computes the similar edges for a node. * * @description Similar edges for perspective images * look roughly in the same direction and are positioned closed to the node. * Similar edges for spherical only target other spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeSimilarEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let nodeSpherical = isSpherical(node.cameraType); let sequenceGroups = {}; for (let potentialEdge of potentialEdges) { if (potentialEdge.sequenceId == null) { continue; } if (potentialEdge.sameSequence) { continue; } if (nodeSpherical) { if (!potentialEdge.spherical) { continue; } } else { if (!potentialEdge.spherical && Math.abs(potentialEdge.directionChange) > this._settings.similarMaxDirectionChange) { continue; } } if (potentialEdge.distance > this._settings.similarMaxDistance) { continue; } if (potentialEdge.sameUser && Math.abs(potentialEdge.capturedAt - node.capturedAt) < this._settings.similarMinTimeDifference) { continue; } if (sequenceGroups[potentialEdge.sequenceId] == null) { sequenceGroups[potentialEdge.sequenceId] = []; } sequenceGroups[potentialEdge.sequenceId].push(potentialEdge); } let similarEdges = []; let calculateScore = isSpherical(node.cameraType) ? (potentialEdge) => { return potentialEdge.distance; } : (potentialEdge) => { return this._coefficients.similarDistance * potentialEdge.distance + this._coefficients.similarRotation * potentialEdge.rotation; }; for (let sequenceId in sequenceGroups) { if (!sequenceGroups.hasOwnProperty(sequenceId)) { continue; } let lowestScore = Number.MAX_VALUE; let similarEdge = null; for (let potentialEdge of sequenceGroups[sequenceId]) { let score = calculateScore(potentialEdge); if (score < lowestScore) { lowestScore = score; similarEdge = potentialEdge; } } if (similarEdge == null) { continue; } similarEdges.push(similarEdge); } return similarEdges .map((potentialEdge) => { return { data: { direction: exports.NavigationDirection.Similar, worldMotionAzimuth: potentialEdge.worldMotionAzimuth, }, source: node.id, target: potentialEdge.id, }; }); } /** * Computes the step edges for a perspective node. * * @description Step edge targets can only be other perspective nodes. * Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @param {string} prevId - Id of previous node in sequence. * @param {string} nextId - Id of next node in sequence. * @throws {ArgumentMapillaryError} If node is not full. */ computeStepEdges(node, potentialEdges, prevId, nextId) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let edges = []; if (isSpherical(node.cameraType)) { return edges; } for (let k in this._directions.steps) { if (!this._directions.steps.hasOwnProperty(k)) { continue; } let step = this._directions.steps[k]; let lowestScore = Number.MAX_VALUE; let edge = null; let fallback = null; for (let potential of potentialEdges) { if (potential.spherical) { continue; } if (Math.abs(potential.directionChange) > this._settings.stepMaxDirectionChange) { continue; } let motionDifference = this._spatial.angleDifference(step.motionChange, potential.motionChange); let directionMotionDifference = this._spatial.angleDifference(potential.directionChange, motionDifference); let drift = Math.max(Math.abs(motionDifference), Math.abs(directionMotionDifference)); if (Math.abs(drift) > this._settings.stepMaxDrift) { continue; } let potentialId = potential.id; if (step.useFallback && (potentialId === prevId || potentialId === nextId)) { fallback = potential; } if (potential.distance > this._settings.stepMaxDistance) { continue; } motionDifference = Math.sqrt(motionDifference * motionDifference + potential.verticalMotion * potential.verticalMotion); let score = this._coefficients.stepPreferredDistance * Math.abs(potential.distance - this._settings.stepPreferredDistance) / this._settings.stepMaxDistance + this._coefficients.stepMotion * motionDifference / this._settings.stepMaxDrift + this._coefficients.stepRotation * potential.rotation / this._settings.stepMaxDirectionChange + this._coefficients.stepSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.stepMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } edge = edge == null ? fallback : edge; if (edge != null) { edges.push({ data: { direction: step.direction, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } } return edges; } /** * Computes the turn edges for a perspective node. * * @description Turn edge targets can only be other perspective images. * Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeTurnEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let edges = []; if (isSpherical(node.cameraType)) { return edges; } for (let k in this._directions.turns) { if (!this._directions.turns.hasOwnProperty(k)) { continue; } let turn = this._directions.turns[k]; let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialEdges) { if (potential.spherical) { continue; } if (potential.distance > this._settings.turnMaxDistance) { continue; } let rig = turn.direction !== exports.NavigationDirection.TurnU && potential.distance < this._settings.turnMaxRigDistance && Math.abs(potential.directionChange) > this._settings.turnMinRigDirectionChange; let directionDifference = this._spatial.angleDifference(turn.directionChange, potential.directionChange); let score; if (rig && potential.directionChange * turn.directionChange > 0 && Math.abs(potential.directionChange) < Math.abs(turn.directionChange)) { score = -Math.PI / 2 + Math.abs(potential.directionChange); } else { if (Math.abs(directionDifference) > this._settings.turnMaxDirectionChange) { continue; } let motionDifference = turn.motionChange ? this._spatial.angleDifference(turn.motionChange, potential.motionChange) : 0; motionDifference = Math.sqrt(motionDifference * motionDifference + potential.verticalMotion * potential.verticalMotion); score = this._coefficients.turnDistance * potential.distance / this._settings.turnMaxDistance + this._coefficients.turnMotion * motionDifference / Math.PI + this._coefficients.turnSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.turnMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); } if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { edges.push({ data: { direction: turn.direction, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } } return edges; } /** * Computes the spherical edges for a perspective node. * * @description Perspective to spherical edge targets can only be * spherical nodes. Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computePerspectiveToSphericalEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (isSpherical(node.cameraType)) { return []; } let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialEdges) { if (!potential.spherical) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential.distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(potential.motionChange) / Math.PI + this._coefficients.sphericalMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge == null) { return []; } return [ { data: { direction: exports.NavigationDirection.Spherical, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }, ]; } /** * Computes the spherical and step edges for a spherical node. * * @description Spherical to spherical edge targets can only be * spherical nodes. spherical to step edge targets can only be perspective * nodes. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeSphericalEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (!isSpherical(node.cameraType)) { return []; } let sphericalEdges = []; let potentialSpherical = []; let potentialSteps = []; for (let potential of potentialEdges) { if (potential.distance > this._settings.sphericalMaxDistance) { continue; } if (potential.spherical) { if (potential.distance < this._settings.sphericalMinDistance) { continue; } potentialSpherical.push(potential); } else { for (let k in this._directions.spherical) { if (!this._directions.spherical.hasOwnProperty(k)) { continue; } let spherical = this._directions.spherical[k]; let turn = this._spatial.angleDifference(potential.directionChange, potential.motionChange); let turnChange = this._spatial.angleDifference(spherical.directionChange, turn); if (Math.abs(turnChange) > this._settings.sphericalMaxStepTurnChange) { continue; } potentialSteps.push([spherical.direction, potential]); // break if step direction found break; } } } let maxRotationDifference = Math.PI / this._settings.sphericalMaxItems; let occupiedAngles = []; let stepAngles = []; for (let index = 0; index < this._settings.sphericalMaxItems; index++) { let rotation = index / this._settings.sphericalMaxItems * 2 * Math.PI; let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialSpherical) { let motionDifference = this._spatial.angleDifference(rotation, potential.motionChange); if (Math.abs(motionDifference) > maxRotationDifference) { continue; } let occupiedDifference = Number.MAX_VALUE; for (let occupiedAngle of occupiedAngles) { let difference = Math.abs(this._spatial.angleDifference(occupiedAngle, potential.motionChange)); if (difference < occupiedDifference) { occupiedDifference = difference; } } if (occupiedDifference <= maxRotationDifference) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential.distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(motionDifference) / maxRotationDifference + this._coefficients.sphericalSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.sphericalMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { occupiedAngles.push(edge.motionChange); sphericalEdges.push({ data: { direction: exports.NavigationDirection.Spherical, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } else { stepAngles.push(rotation); } } let occupiedStepAngles = {}; occupiedStepAngles[exports.NavigationDirection.Spherical] = occupiedAngles; occupiedStepAngles[exports.NavigationDirection.StepForward] = []; occupiedStepAngles[exports.NavigationDirection.StepLeft] = []; occupiedStepAngles[exports.NavigationDirection.StepBackward] = []; occupiedStepAngles[exports.NavigationDirection.StepRight] = []; for (let stepAngle of stepAngles) { let occupations = []; for (let k in this._directions.spherical) { if (!this._directions.spherical.hasOwnProperty(k)) { continue; } let spherical = this._directions.spherical[k]; let allOccupiedAngles = occupiedStepAngles[exports.NavigationDirection.Spherical] .concat(occupiedStepAngles[spherical.direction]) .concat(occupiedStepAngles[spherical.prev]) .concat(occupiedStepAngles[spherical.next]); let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialSteps) { if (potential[0] !== spherical.direction) { continue; } let motionChange = this._spatial.angleDifference(stepAngle, potential[1].motionChange); if (Math.abs(motionChange) > maxRotationDifference) { continue; } let minOccupiedDifference = Number.MAX_VALUE; for (let occupiedAngle of allOccupiedAngles) { let occupiedDifference = Math.abs(this._spatial.angleDifference(occupiedAngle, potential[1].motionChange)); if (occupiedDifference < minOccupiedDifference) { minOccupiedDifference = occupiedDifference; } } if (minOccupiedDifference <= maxRotationDifference) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential[1].distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(motionChange) / maxRotationDifference + this._coefficients.sphericalMergeCCPenalty * (potential[1].sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { occupations.push(edge); sphericalEdges.push({ data: { direction: edge[0], worldMotionAzimuth: edge[1].worldMotionAzimuth, }, source: node.id, target: edge[1].id, }); } } for (let occupation of occupations) { occupiedStepAngles[occupation[0]].push(occupation[1].motionChange); } } return sphericalEdges; } } class GraphMapillaryError extends MapillaryError { constructor(message) { super(message); Object.setPrototypeOf(this, GraphMapillaryError.prototype); this.name = "GraphMapillaryError"; } } /** * @class CancelMapillaryError * * @classdesc Error thrown when a move to request has been * cancelled before completing because of a subsequent request. */ class CancelMapillaryError extends MapillaryError { constructor(message) { super(message != null ? message : "The request was cancelled."); Object.setPrototypeOf(this, CancelMapillaryError.prototype); this.name = "CancelMapillaryError"; } } /** * @class Graph * * @classdesc Represents a graph of nodes with edges. */ class Graph { /** * Create a new graph instance. * * @param {APIWrapper} [api] - API instance for retrieving data. * @param {rbush.RBush} [nodeIndex] - Node index for fast spatial retreival. * @param {GraphCalculator} [graphCalculator] - Instance for graph calculations. * @param {EdgeCalculator} [edgeCalculator] - Instance for edge calculations. * @param {FilterCreator} [filterCreator] - Instance for filter creation. * @param {GraphConfiguration} [configuration] - Configuration struct. */ constructor(api, options, nodeIndex, graphCalculator, edgeCalculator, filterCreator, configuration) { var _a; this._api = api; this._computedGraph = (_a = options === null || options === void 0 ? void 0 : options.computedGraph) !== null && _a !== void 0 ? _a : false; this._cachedNodes = {}; this._cachedNodeTiles = {}; this._cachedSequenceNodes = {}; this._cachedSpatialEdges = {}; this._cachedTiles = {}; this._cachingFill$ = {}; this._cachingFull$ = {}; this._cachingSequenceNodes$ = {}; this._cachingSequences$ = {}; this._cachingSpatialArea$ = {}; this._cachingTiles$ = {}; this._changed$ = new Subject(); this._filterCreator = filterCreator !== null && filterCreator !== void 0 ? filterCreator : new FilterCreator(); this._filter = this._filterCreator.createFilter(undefined); this._filterSubject$ = new Subject(); this._filter$ = concat(of(this._filter), this._filterSubject$).pipe(publishReplay(1), refCount()); this._filterSubscription = this._filter$.subscribe(() => { }); this._defaultAlt = 2; this._edgeCalculator = edgeCalculator !== null && edgeCalculator !== void 0 ? edgeCalculator : new EdgeCalculator(); this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); this._configuration = configuration !== null && configuration !== void 0 ? configuration : { maxSequences: 50, maxUnusedImages: 100, maxUnusedPreStoredImages: 30, maxUnusedTiles: 20, }; this._clusterNodes = new Map(); this._nodes = {}; this._nodeIndex = nodeIndex !== null && nodeIndex !== void 0 ? nodeIndex : new Graph._spatialIndex(16); this._nodeIndexNodes = new Map(); this._nodeIndexTiles = {}; this._nodeToTile = {}; this._preStored = {}; this._preDeletedNodes = new Map(); this._requiredNodeTiles = {}; this._requiredSpatialArea = {}; this._sequences = {}; this._tileThreshold = 20; } static register(spatialIndex) { Graph._spatialIndex = spatialIndex; } /** * Get api. * * @returns {APIWrapper} The API instance used by * the graph. */ get api() { return this._api; } /** * Get changed$. * * @returns {Observable} Observable emitting * the graph every time it has changed. */ get changed$() { return this._changed$; } /** * Get filter$. * * @returns {Observable} Observable emitting * the filter every time it has changed. */ get filter$() { return this._filter$; } /** * Caches the full node data for all images within a bounding * box. * * @description The node assets are not cached. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * @returns {Observable>} Observable emitting * the full nodes in the bounding box. */ cacheBoundingBox$(sw, ne) { const cacheTiles$ = this._api.data.geometry.bboxToCellIds(sw, ne) .filter((h) => { return !(h in this._cachedTiles); }) .map((h) => { return h in this._cachingTiles$ ? this._cachingTiles$[h] : this._cacheTile$(h); }); if (cacheTiles$.length === 0) { cacheTiles$.push(of(this)); } return from(cacheTiles$).pipe(mergeAll(), last(), mergeMap(() => { const nodes = this._nodeIndex .search({ maxX: ne.lng, maxY: ne.lat, minX: sw.lng, minY: sw.lat, }) .map((item) => { return item.node; }); const fullNodes = []; const coreNodes = []; for (const node of nodes) { if (node.complete) { fullNodes.push(node); } else { coreNodes.push(node.id); } } const coreNodeBatches = []; const batchSize = 200; while (coreNodes.length > 0) { coreNodeBatches.push(coreNodes.splice(0, batchSize)); } const fullNodes$ = of(fullNodes); const fillNodes$ = coreNodeBatches .map((batch) => { return this._api .getSpatialImages$(batch) .pipe(map((items) => { const result = []; for (const item of items) { const exists = this .hasNode(item.node_id); if (!exists) { continue; } const node = this .getNode(item.node_id); if (!node.complete) { this._makeFull(node, item.node); } result.push(node); } return result; })); }); return merge(fullNodes$, from(fillNodes$).pipe(mergeAll())); }), reduce((acc, value) => { return acc.concat(value); })); } /** * Caches the full node data for all images of a cell. * * @description The node assets are not cached. * * @param {string} cellId - Cell id. * @returns {Observable>} Observable * emitting the full nodes of the cell. */ cacheCell$(cellId) { const cacheCell$ = cellId in this._cachedTiles ? of(this) : cellId in this._cachingTiles$ ? this._cachingTiles$[cellId] : this._cacheTile$(cellId); return cacheCell$.pipe(mergeMap(() => { const cachedCell = this._cachedTiles[cellId]; cachedCell.accessed = new Date().getTime(); const cellNodes = cachedCell.nodes; const fullNodes = []; const coreNodes = []; for (const node of cellNodes) { if (node.complete) { fullNodes.push(node); } else { coreNodes.push(node.id); } } const coreNodeBatches = []; const batchSize = 200; while (coreNodes.length > 0) { coreNodeBatches.push(coreNodes.splice(0, batchSize)); } const fullNodes$ = of(fullNodes); const fillNodes$ = coreNodeBatches .map((batch) => { return this._api.getSpatialImages$(batch).pipe(map((items) => { const filled = []; for (const item of items) { if (!item.node) { console.warn(`Image is empty (${item.node})`); continue; } const id = item.node_id; if (!this.hasNode(id)) { continue; } const node = this.getNode(id); if (!node.complete) { this._makeFull(node, item.node); } filled.push(node); } return filled; })); }); return merge(fullNodes$, from(fillNodes$).pipe(mergeAll())); }), reduce((acc, value) => { return acc.concat(value); })); } /** * Retrieve and cache node fill properties. * * @param {string} key - Key of node to fill. * @returns {Observable} Observable emitting the graph * when the node has been updated. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheFill$(key) { if (key in this._cachingFull$) { throw new GraphMapillaryError(`Cannot fill node while caching full (${key}).`); } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot fill node that does not exist in graph (${key}).`); } if (key in this._cachingFill$) { return this._cachingFill$[key]; } const node = this.getNode(key); if (node.complete) { throw new GraphMapillaryError(`Cannot fill node that is already full (${key}).`); } this._cachingFill$[key] = this._api.getSpatialImages$([key]).pipe(tap((items) => { for (const item of items) { if (!item.node) { console.warn(`Image is empty ${item.node_id}`); } if (!node.complete) { this._makeFull(node, item.node); } delete this._cachingFill$[item.node_id]; } }), map(() => { return this; }), finalize(() => { if (key in this._cachingFill$) { delete this._cachingFill$[key]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingFill$[key]; } /** * Retrieve and cache full node properties. * * @param {string} key - Key of node to fill. * @returns {Observable} Observable emitting the graph * when the node has been updated. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheFull$(key) { if (key in this._cachingFull$) { return this._cachingFull$[key]; } if (this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache full node that already exist in graph (${key}).`); } this._cachingFull$[key] = this._api.getImages$([key]).pipe(tap((items) => { for (const item of items) { if (!item.node) { throw new GraphMapillaryError(`Image does not exist (${key}, ${item.node}).`); } const id = item.node_id; if (this.hasNode(id)) { const node = this.getNode(key); if (!node.complete) { this._makeFull(node, item.node); } } else { if (item.node.sequence.id == null) { throw new GraphMapillaryError(`Image has no sequence key (${key}).`); } let node = null; if (this._preDeletedNodes.has(id)) { node = this._unDeleteNode(id); } else { node = new Image$1(item.node); } this._makeFull(node, item.node); const lngLat = this._getNodeLngLat(node); const cellId = this._api.data.geometry .lngLatToCellId(lngLat); this._preStore(cellId, node); this._setNode(node); delete this._cachingFull$[id]; } } }), map(() => this), finalize(() => { if (key in this._cachingFull$) { delete this._cachingFull$[key]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingFull$[key]; } /** * Retrieve and cache a node sequence. * * @param {string} key - Key of node for which to retrieve sequence. * @returns {Observable} Observable emitting the graph * when the sequence has been retrieved. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheNodeSequence$(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache sequence edges of node that does not exist in graph (${key}).`); } let node = this.getNode(key); if (node.sequenceId in this._sequences) { throw new GraphMapillaryError(`Sequence already cached (${key}), (${node.sequenceId}).`); } return this._cacheSequence$(node.sequenceId); } /** * Retrieve and cache a sequence. * * @param {string} sequenceKey - Key of sequence to cache. * @returns {Observable} Observable emitting the graph * when the sequence has been retrieved. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSequence$(sequenceKey) { if (sequenceKey in this._sequences) { throw new GraphMapillaryError(`Sequence already cached (${sequenceKey})`); } return this._cacheSequence$(sequenceKey); } /** * Cache sequence edges for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSequenceEdges(key) { let node = this.getNode(key); if (!(node.sequenceId in this._sequences)) { throw new GraphMapillaryError(`Sequence is not cached (${key}), (${node.sequenceId})`); } let sequence = this._sequences[node.sequenceId].sequence; let edges = this._edgeCalculator.computeSequenceEdges(node, sequence); node.cacheSequenceEdges(edges); } /** * Retrieve and cache full nodes for all keys in a sequence. * * @param {string} sequenceKey - Key of sequence. * @param {string} referenceNodeKey - Key of node to use as reference * for optimized caching. * @returns {Observable} Observable emitting the graph * when the nodes of the sequence has been cached. */ cacheSequenceNodes$(sequenceKey, referenceNodeKey) { if (!this.hasSequence(sequenceKey)) { throw new GraphMapillaryError(`Cannot cache sequence nodes of sequence that does not exist in graph (${sequenceKey}).`); } if (this.hasSequenceNodes(sequenceKey)) { throw new GraphMapillaryError(`Sequence nodes already cached (${sequenceKey}).`); } const sequence = this.getSequence(sequenceKey); if (sequence.id in this._cachingSequenceNodes$) { return this._cachingSequenceNodes$[sequence.id]; } const batches = []; const keys = sequence.imageIds.slice(); const referenceBatchSize = 50; if (!!referenceNodeKey && keys.length > referenceBatchSize) { const referenceIndex = keys.indexOf(referenceNodeKey); const startIndex = Math.max(0, Math.min(referenceIndex - referenceBatchSize / 2, keys.length - referenceBatchSize)); batches.push(keys.splice(startIndex, referenceBatchSize)); } const batchSize = 200; while (keys.length > 0) { batches.push(keys.splice(0, batchSize)); } let batchesToCache = batches.length; const sequenceNodes$ = from(batches).pipe(mergeMap((batch) => { return this._api.getImages$(batch).pipe(tap((items) => { for (const item of items) { if (!item.node) { console.warn(`Image empty (${item.node_id})`); continue; } const id = item.node_id; if (this.hasNode(id)) { const node = this.getNode(id); if (!node.complete) { this._makeFull(node, item.node); } } else { if (item.node.sequence.id == null) { console.warn(`Sequence missing, discarding node (${item.node_id})`); } let node = null; if (this._preDeletedNodes.has(id)) { node = this._unDeleteNode(id); } else { node = new Image$1(item.node); } this._makeFull(node, item.node); const lngLat = this._getNodeLngLat(node); const cellId = this._api.data.geometry .lngLatToCellId(lngLat); this._preStore(cellId, node); this._setNode(node); } } batchesToCache--; }), map(() => this)); }, 6), last(), finalize(() => { delete this._cachingSequenceNodes$[sequence.id]; if (batchesToCache === 0) { this._cachedSequenceNodes[sequence.id] = true; } }), publish(), refCount()); this._cachingSequenceNodes$[sequence.id] = sequenceNodes$; return sequenceNodes$; } /** * Retrieve and cache full nodes for a node spatial area. * * @param {string} key - Key of node for which to retrieve sequence. * @returns {Observable} Observable emitting the graph * when the nodes in the spatial area has been made full. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSpatialArea$(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache spatial area of node that does not exist in graph (${key}).`); } if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Image already spatially cached (${key}).`); } if (!(key in this._requiredSpatialArea)) { throw new GraphMapillaryError(`Spatial area not determined (${key}).`); } let spatialArea = this._requiredSpatialArea[key]; if (Object.keys(spatialArea.cacheNodes).length === 0) { throw new GraphMapillaryError(`Spatial nodes already cached (${key}).`); } if (key in this._cachingSpatialArea$) { return this._cachingSpatialArea$[key]; } let batches = []; while (spatialArea.cacheKeys.length > 0) { batches.push(spatialArea.cacheKeys.splice(0, 200)); } let batchesToCache = batches.length; let spatialArea$ = []; for (let batch of batches) { let spatialNodeBatch$ = this._api.getSpatialImages$(batch).pipe(tap((items) => { const currentSpatialArea = this._requiredSpatialArea[key]; if (currentSpatialArea == null || spatialArea !== currentSpatialArea) { throw new CancelMapillaryError("Required spatial area changed."); } const currentCaching = this._cachingSpatialArea$[key]; if (currentCaching == null || spatialArea$ !== currentCaching) { throw new CancelMapillaryError("Spatial area caching changed."); } for (const item of items) { if (!item.node) { console.warn(`Image is empty (${item.node_id})`); continue; } const id = item.node_id; const spatialNode = spatialArea.cacheNodes[id]; if (spatialNode.complete) { delete spatialArea.cacheNodes[id]; continue; } this._makeFull(spatialNode, item.node); delete spatialArea.cacheNodes[id]; } if (--batchesToCache === 0) { delete this._cachingSpatialArea$[key]; } }), map(() => { return this; }), catchError((error) => { for (let batchKey of batch) { if (batchKey in spatialArea.all) { delete spatialArea.all[batchKey]; } if (batchKey in spatialArea.cacheNodes) { delete spatialArea.cacheNodes[batchKey]; } } const currentCaching = this._cachingSpatialArea$[key]; if (spatialArea$ === currentCaching) { delete this._cachingSpatialArea$[key]; } throw error; }), finalize(() => { if (Object.keys(spatialArea.cacheNodes).length === 0) { this._changed$.next(this); } }), publish(), refCount()); spatialArea$.push(spatialNodeBatch$); } this._cachingSpatialArea$[key] = spatialArea$; return spatialArea$; } /** * Cache spatial edges for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSpatialEdges(key) { if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Spatial edges already cached (${key}).`); } let node = this.getNode(key); let sequence = this._sequences[node.sequenceId].sequence; let fallbackKeys = []; let prevKey = sequence.findPrev(node.id); if (prevKey != null) { fallbackKeys.push(prevKey); } let nextKey = sequence.findNext(node.id); if (nextKey != null) { fallbackKeys.push(nextKey); } let allSpatialNodes = this._requiredSpatialArea[key].all; let potentialNodes = []; let filter = this._filter; for (let spatialNodeKey in allSpatialNodes) { if (!allSpatialNodes.hasOwnProperty(spatialNodeKey)) { continue; } let spatialNode = allSpatialNodes[spatialNodeKey]; if (spatialNode.complete && filter(spatialNode)) { potentialNodes.push(spatialNode); } } let potentialEdges = this._edgeCalculator.getPotentialEdges(node, potentialNodes, fallbackKeys); let edges = this._edgeCalculator.computeStepEdges(node, potentialEdges, prevKey, nextKey); edges = edges.concat(this._edgeCalculator.computeTurnEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computeSphericalEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computePerspectiveToSphericalEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computeSimilarEdges(node, potentialEdges)); node.cacheSpatialEdges(edges); this._cachedSpatialEdges[key] = node; delete this._requiredSpatialArea[key]; delete this._cachedNodeTiles[key]; } /** * Retrieve and cache tiles for a node. * * @param {string} key - Key of node for which to retrieve tiles. * @returns {Array>} Array of observables emitting * the graph for each tile required for the node has been cached. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheTiles$(key) { if (key in this._cachedNodeTiles) { throw new GraphMapillaryError(`Tiles already cached (${key}).`); } if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Spatial edges already cached so tiles considered cached (${key}).`); } if (!(key in this._requiredNodeTiles)) { throw new GraphMapillaryError(`Tiles have not been determined (${key}).`); } let nodeTiles = this._requiredNodeTiles[key]; if (nodeTiles.cache.length === 0 && nodeTiles.caching.length === 0) { throw new GraphMapillaryError(`Tiles already cached (${key}).`); } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache tiles of node that does not exist in graph (${key}).`); } let hs = nodeTiles.cache.slice(); nodeTiles.caching = this._requiredNodeTiles[key].caching.concat(hs); nodeTiles.cache = []; let cacheTiles$ = []; for (let h of nodeTiles.caching) { const cacheTile$ = h in this._cachingTiles$ ? this._cachingTiles$[h] : this._cacheTile$(h); cacheTiles$.push(cacheTile$.pipe(tap((graph) => { let index = nodeTiles.caching.indexOf(h); if (index > -1) { nodeTiles.caching.splice(index, 1); } if (nodeTiles.caching.length === 0 && nodeTiles.cache.length === 0) { delete this._requiredNodeTiles[key]; this._cachedNodeTiles[key] = true; } }), catchError((error) => { let index = nodeTiles.caching.indexOf(h); if (index > -1) { nodeTiles.caching.splice(index, 1); } if (nodeTiles.caching.length === 0 && nodeTiles.cache.length === 0) { delete this._requiredNodeTiles[key]; this._cachedNodeTiles[key] = true; } throw error; }), finalize(() => { this._changed$.next(this); }), publish(), refCount())); } return cacheTiles$; } /** * Initialize the cache for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ initializeCache(key) { if (key in this._cachedNodes) { throw new GraphMapillaryError(`Image already in cache (${key}).`); } const node = this.getNode(key); const provider = this._api.data; node.initializeCache(new ImageCache(provider)); const accessed = new Date().getTime(); this._cachedNodes[key] = { accessed: accessed, node: node }; this._updateCachedTileAccess(key, accessed); } /** * Get a value indicating if the graph is fill caching a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the node is being fill cached. */ isCachingFill(key) { return key in this._cachingFill$; } /** * Get a value indicating if the graph is fully caching a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the node is being fully cached. */ isCachingFull(key) { return key in this._cachingFull$; } /** * Get a value indicating if the graph is caching a sequence of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the sequence of a node is * being cached. */ isCachingNodeSequence(key) { let node = this.getNode(key); return node.sequenceId in this._cachingSequences$; } /** * Get a value indicating if the graph is caching a sequence. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if the sequence is * being cached. */ isCachingSequence(sequenceKey) { return sequenceKey in this._cachingSequences$; } /** * Get a value indicating if the graph is caching sequence nodes. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if the sequence nodes are * being cached. */ isCachingSequenceNodes(sequenceKey) { return sequenceKey in this._cachingSequenceNodes$; } /** * Get a value indicating if the graph is caching the tiles * required for calculating spatial edges of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the tiles of * a node are being cached. */ isCachingTiles(key) { return key in this._requiredNodeTiles && this._requiredNodeTiles[key].cache.length === 0 && this._requiredNodeTiles[key].caching.length > 0; } /** * Get a value indicating if the cache has been initialized * for a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the cache has been * initialized for a node. */ hasInitializedCache(key) { return key in this._cachedNodes; } /** * Get a value indicating if a node exist in the graph. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if a node exist in the graph. */ hasNode(key) { let accessed = new Date().getTime(); this._updateCachedNodeAccess(key, accessed); this._updateCachedTileAccess(key, accessed); return key in this._nodes; } /** * Get a value indicating if a node sequence exist in the graph. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if a node sequence exist * in the graph. */ hasNodeSequence(key) { let node = this.getNode(key); let sequenceKey = node.sequenceId; let hasNodeSequence = sequenceKey in this._sequences; if (hasNodeSequence) { this._sequences[sequenceKey].accessed = new Date().getTime(); } return hasNodeSequence; } /** * Get a value indicating if a sequence exist in the graph. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if a sequence exist * in the graph. */ hasSequence(sequenceKey) { let hasSequence = sequenceKey in this._sequences; if (hasSequence) { this._sequences[sequenceKey].accessed = new Date().getTime(); } return hasSequence; } /** * Get a value indicating if sequence nodes has been cached in the graph. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if a sequence nodes has been * cached in the graph. */ hasSequenceNodes(sequenceKey) { return sequenceKey in this._cachedSequenceNodes; } /** * Get a value indicating if the graph has fully cached * all nodes in the spatial area of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the spatial area * of a node has been cached. */ hasSpatialArea(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Spatial area nodes cannot be determined if node not in graph (${key}).`); } if (key in this._cachedSpatialEdges) { return true; } if (key in this._requiredSpatialArea) { return Object .keys(this._requiredSpatialArea[key].cacheNodes) .length === 0; } let node = this.getNode(key); let bbox = this._graphCalculator .boundingBoxCorners(node.lngLat, this._tileThreshold); let spatialItems = this._nodeIndex .search({ maxX: bbox[1].lng, maxY: bbox[1].lat, minX: bbox[0].lng, minY: bbox[0].lat, }); let spatialNodes = { all: {}, cacheKeys: [], cacheNodes: {}, }; for (let spatialItem of spatialItems) { spatialNodes.all[spatialItem.node.id] = spatialItem.node; if (!spatialItem.node.complete) { spatialNodes.cacheKeys.push(spatialItem.node.id); spatialNodes.cacheNodes[spatialItem.node.id] = spatialItem.node; } } this._requiredSpatialArea[key] = spatialNodes; return spatialNodes.cacheKeys.length === 0; } /** * Get a value indicating if the graph has a tiles required * for a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the the tiles required * by a node has been cached. */ hasTiles(key) { if (key in this._cachedNodeTiles) { return true; } if (key in this._cachedSpatialEdges) { return true; } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Image does not exist in graph (${key}).`); } let nodeTiles = { cache: [], caching: [] }; if (!(key in this._requiredNodeTiles)) { const node = this.getNode(key); const [sw, ne] = this._graphCalculator .boundingBoxCorners(node.lngLat, this._tileThreshold); nodeTiles.cache = this._api.data.geometry .bboxToCellIds(sw, ne) .filter((h) => { return !(h in this._cachedTiles); }); if (nodeTiles.cache.length > 0) { this._requiredNodeTiles[key] = nodeTiles; } } else { nodeTiles = this._requiredNodeTiles[key]; } return nodeTiles.cache.length === 0 && nodeTiles.caching.length === 0; } /** * Get a node. * * @param {string} key - Key of node. * @returns {Image} Retrieved node. */ getNode(key) { let accessed = new Date().getTime(); this._updateCachedNodeAccess(key, accessed); this._updateCachedTileAccess(key, accessed); return this._nodes[key]; } /** * Get a sequence. * * @param {string} sequenceKey - Key of sequence. * @returns {Image} Retrieved sequence. */ getSequence(sequenceKey) { let sequenceAccess = this._sequences[sequenceKey]; sequenceAccess.accessed = new Date().getTime(); return sequenceAccess.sequence; } /** * Reset all spatial edges of the graph nodes. */ resetSpatialEdges() { for (const nodeId of Object.keys(this._cachedSpatialEdges)) { const node = this._cachedSpatialEdges[nodeId]; node.resetSpatialEdges(); } this._cachedSpatialEdges = {}; } /** * Reset all spatial areas of the graph nodes. */ resetSpatialArea() { this._requiredSpatialArea = {}; this._cachingSpatialArea$ = {}; } /** * Reset the complete graph and disposed all nodes. */ reset() { for (let cachedKey of Object.keys(this._cachedNodes)) { this._cachedNodes[cachedKey].node.dispose(); } this._cachedNodes = {}; this._cachedNodeTiles = {}; this._cachedSpatialEdges = {}; this._cachedTiles = {}; this._cachingFill$ = {}; this._cachingFull$ = {}; this._cachingSequences$ = {}; this._cachingSpatialArea$ = {}; this._cachingTiles$ = {}; this._clusterNodes = new Map(); this._nodes = {}; this._nodeToTile = {}; this._preStored = {}; this._preDeletedNodes = new Map(); this._requiredNodeTiles = {}; this._requiredSpatialArea = {}; this._sequences = {}; this._nodeIndexNodes = new Map(); this._nodeIndexTiles = {}; this._nodeIndex.clear(); } /** * Set the spatial node filter. * * @emits FilterFunction The filter function to the {@link Graph.filter$} * observable. * * @param {FilterExpression} filter - Filter expression to be applied * when calculating spatial edges. */ setFilter(filter) { this._filter = this._filterCreator.createFilter(filter); this._filterSubject$.next(this._filter); } /** * Uncache the graph according to the graph configuration. * * @description Uncaches unused tiles, unused nodes and * sequences according to the numbers specified in the * graph configuration. Sequences does not have a direct * reference to either tiles or nodes and may be uncached * even if they are related to the nodes that should be kept. * * @param {Array} keepIds - Ids of nodes to keep in * graph unrelated to last access. Tiles related to those keys * will also be kept in graph. * @param {Array} keepCellIds - Ids of cells to keep in * graph unrelated to last access. The nodes of the cells may * still be uncached if not specified in the keep ids param * but are guaranteed to not be disposed. * @param {string} keepSequenceId - Optional id of sequence * for which the belonging nodes should not be disposed or * removed from the graph. These nodes may still be uncached if * not specified in keep ids param but are guaranteed to not * be disposed. */ uncache(keepIds, keepCellIds, keepSequenceId) { const idsInUse = {}; this._addNewKeys(idsInUse, this._cachingFull$); this._addNewKeys(idsInUse, this._cachingFill$); this._addNewKeys(idsInUse, this._cachingSpatialArea$); this._addNewKeys(idsInUse, this._requiredNodeTiles); this._addNewKeys(idsInUse, this._requiredSpatialArea); for (const key of keepIds) { if (key in idsInUse) { continue; } idsInUse[key] = true; } const geometry = this._api.data.geometry; const keepCells = new Set(keepCellIds); const potentialCells = []; for (const cellId in this._cachedTiles) { if (!this._cachedTiles.hasOwnProperty(cellId) || keepCells.has(cellId)) { continue; } potentialCells.push([cellId, this._cachedTiles[cellId]]); } const sortedPotentialCells = potentialCells .sort((h1, h2) => { return h2[1].accessed - h1[1].accessed; }); const keepPotentialCells = sortedPotentialCells .slice(0, this._configuration.maxUnusedTiles) .map((h) => { return h[0]; }); for (const potentialCell of keepPotentialCells) { keepCells.add(potentialCell); } for (const id in idsInUse) { if (!idsInUse.hasOwnProperty(id)) { continue; } if (!this.hasNode(id)) { continue; } const node = this._nodes[id]; const lngLat = this._getNodeLngLat(node); const nodeCellId = geometry.lngLatToCellId(lngLat); if (!keepCells.has(nodeCellId)) { if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._nodeToTile) { delete this._nodeToTile[id]; } if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[id]; } if (node.hasInitializedCache()) { node.resetSpatialEdges(); } if (nodeCellId in this._cachedTiles) { const index = this._cachedTiles[nodeCellId].nodes .findIndex(n => n.id === id); if (index !== -1) { this._cachedTiles[nodeCellId].nodes.splice(index, 1); } } this._preStore(nodeCellId, node); } } const uncacheCells = sortedPotentialCells .slice(this._configuration.maxUnusedTiles) .map((h) => { return h[0]; }); for (let uncacheCell of uncacheCells) { this._uncacheTile(uncacheCell, keepSequenceId); } const potentialPreStored = []; const nonCachedPreStored = []; for (let cellId in this._preStored) { if (!this._preStored.hasOwnProperty(cellId) || cellId in this._cachingTiles$) { continue; } const prestoredNodes = this._preStored[cellId]; for (let id in prestoredNodes) { if (!prestoredNodes.hasOwnProperty(id) || id in idsInUse) { continue; } if (prestoredNodes[id].sequenceId === keepSequenceId) { continue; } if (id in this._cachedNodes) { potentialPreStored.push([this._cachedNodes[id], cellId]); } else { nonCachedPreStored.push([id, cellId]); } } } const uncachePreStored = potentialPreStored .sort(([na1], [na2]) => { return na2.accessed - na1.accessed; }) .slice(this._configuration.maxUnusedPreStoredImages) .map(([na, h]) => { return [na.node.id, h]; }); this._uncachePreStored(nonCachedPreStored); this._uncachePreStored(uncachePreStored); const potentialNodes = []; for (let id in this._cachedNodes) { if (!this._cachedNodes.hasOwnProperty(id) || id in idsInUse) { continue; } potentialNodes.push(this._cachedNodes[id]); } const uncacheNodes = potentialNodes .sort((n1, n2) => { return n2.accessed - n1.accessed; }) .slice(this._configuration.maxUnusedImages); for (const nodeAccess of uncacheNodes) { nodeAccess.node.uncache(); const id = nodeAccess.node.id; delete this._cachedNodes[id]; if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[id]; } } for (const [nodeId, node] of this._preDeletedNodes.entries()) { if (nodeId in idsInUse) { continue; } if (nodeId in this._cachedNodes) { delete this._cachedNodes[nodeId]; } this._preDeletedNodes.delete(nodeId); node.dispose(); } const potentialSequences = []; for (let sequenceId in this._sequences) { if (!this._sequences.hasOwnProperty(sequenceId) || sequenceId in this._cachingSequences$ || sequenceId === keepSequenceId) { continue; } potentialSequences.push(this._sequences[sequenceId]); } const uncacheSequences = potentialSequences .sort((s1, s2) => { return s2.accessed - s1.accessed; }) .slice(this._configuration.maxSequences); for (const sequenceAccess of uncacheSequences) { const sequenceId = sequenceAccess.sequence.id; delete this._sequences[sequenceId]; if (sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[sequenceId]; } sequenceAccess.sequence.dispose(); } } /** * Updates existing cells with new core nodes. * * @description Non-existing cells are discarded * and not requested at all. * * Existing nodes are not changed. * * New nodes are not made full or getting assets * cached. * * @param {Array} cellIds - Cell ids. * @returns {Observable>} Observable * emitting the updated cells. */ updateCells$(cellIds) { const cachedCells = this._cachedTiles; const cachingCells = this._cachingTiles$; return from(cellIds) .pipe(mergeMap((cellId) => { if (cellId in cachedCells) { return this._updateCell$(cellId); } if (cellId in cachingCells) { return cachingCells[cellId] .pipe(catchError(() => { return of(this); }), mergeMap(() => this._updateCell$(cellId))); } return empty(); })); } /** * Deletes clusters. * * @description Existing nodes for the clusters are deleted * and placed in a deleted store. The deleted store will be * purged during uncaching if the nodes are no longer in use. * * Nodes in the deleted store are always removed on reset. * * @param {Array} clusterIds - Cluster ids. * @returns {Observable>} Observable * emitting the IDs for the deleted clusters. */ deleteClusters$(clusterIds) { if (!clusterIds.length) { return empty(); } return from(clusterIds) .pipe(map((clusterId) => { if (!this._clusterNodes.has(clusterId)) { return null; } const clusterNodes = this._clusterNodes.get(clusterId); for (const nodeId of clusterNodes.values()) { const node = this._nodes[nodeId]; delete this._nodes[nodeId]; if (nodeId in this._cachedNodeTiles) { delete this._cachedNodeTiles[nodeId]; } if (nodeId in this._cachedNodeTiles) { delete this._cachedNodeTiles[nodeId]; } if (nodeId in this._nodeToTile) { const nodeCellId = this._nodeToTile[nodeId]; if (nodeCellId in this._cachedTiles) { const tileIndex = this._cachedTiles[nodeCellId].nodes .findIndex(n => n.id === nodeId); if (tileIndex !== -1) { this._cachedTiles[nodeCellId].nodes.splice(tileIndex, 1); } } delete this._nodeToTile[nodeId]; } const item = this._nodeIndexNodes.get(nodeId); this._nodeIndex.remove(item); this._nodeIndexNodes.delete(nodeId); const cell = this._nodeIndexTiles[item.cellId]; const nodeIndex = cell.indexOf(item); if (nodeIndex === -1) { throw new GraphMapillaryError(`Corrupt graph index cell (${nodeId})`); } cell.splice(nodeIndex, 1); this._preDeletedNodes.set(nodeId, node); } this._clusterNodes.delete(clusterId); return clusterId; }), filter((clusterId) => { return clusterId != null; })); } /** * Unsubscribes all subscriptions. * * @description Afterwards, you must not call any other methods * on the graph instance. */ unsubscribe() { this._filterSubscription.unsubscribe(); } _addNewKeys(keys, dict) { for (let key in dict) { if (!dict.hasOwnProperty(key) || !this.hasNode(key)) { continue; } if (!(key in keys)) { keys[key] = true; } } } _cacheSequence$(sequenceId) { if (sequenceId in this._cachingSequences$) { return this._cachingSequences$[sequenceId]; } this._cachingSequences$[sequenceId] = this._api .getSequence$(sequenceId) .pipe(tap((sequence) => { if (!sequence) { console.warn(`Sequence does not exist ` + `(${sequenceId})`); } else { if (!(sequence.id in this._sequences)) { this._sequences[sequence.id] = { accessed: new Date().getTime(), sequence: new Sequence(sequence), }; } delete this._cachingSequences$[sequenceId]; } }), map(() => { return this; }), finalize(() => { if (sequenceId in this._cachingSequences$) { delete this._cachingSequences$[sequenceId]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingSequences$[sequenceId]; } _cacheTile$(cellId) { this._cachingTiles$[cellId] = this._api .getCoreImages$(cellId) .pipe(tap((contract) => { if (cellId in this._cachedTiles) { return; } const cores = contract.images; this._nodeIndexTiles[cellId] = []; this._cachedTiles[cellId] = { accessed: new Date().getTime(), nodes: [], }; const hCache = this._cachedTiles[cellId].nodes; const preStored = this._removeFromPreStore(cellId); const preDeleted = this._preDeletedNodes; for (const core of cores) { if (!core) { break; } if (core.sequence.id == null) { console.warn(`Sequence missing, discarding ` + `node (${core.id})`); continue; } if (preStored != null && core.id in preStored) { const preStoredNode = preStored[core.id]; delete preStored[core.id]; hCache.push(preStoredNode); const preStoredNodeIndexItem = { cellId, id: core.id, lat: preStoredNode.lngLat.lat, lng: preStoredNode.lngLat.lng, node: preStoredNode, }; this._nodeIndex.insert(preStoredNodeIndexItem); this._nodeIndexNodes.set(core.id, preStoredNodeIndexItem); this._nodeIndexTiles[cellId] .push(preStoredNodeIndexItem); this._nodeToTile[preStoredNode.id] = cellId; continue; } let node = null; if (preDeleted.has(core.id)) { node = preDeleted.get(core.id); preDeleted.delete(core.id); } else { node = new Image$1(core); } hCache.push(node); const nodeIndexItem = { cellId, id: node.id, lat: node.lngLat.lat, lng: node.lngLat.lng, node: node, }; this._nodeIndex.insert(nodeIndexItem); this._nodeIndexNodes.set(node.id, nodeIndexItem); this._nodeIndexTiles[cellId].push(nodeIndexItem); this._nodeToTile[node.id] = cellId; this._setNode(node); } delete this._cachingTiles$[cellId]; }), map(() => this), catchError((error) => { delete this._cachingTiles$[cellId]; throw error; }), publish(), refCount()); return this._cachingTiles$[cellId]; } _addClusterNode(node) { const clusterId = node.clusterId; if (clusterId == null) { console.warn(`Cannot set cluster node, cluster ID is undefined for node ${node.id}.`); return; } if (!this._clusterNodes.has(clusterId)) { this._clusterNodes.set(clusterId, new Set()); } const clusterNodes = this._clusterNodes.get(clusterId); if (clusterNodes.has(node.id)) { throw new GraphMapillaryError(`Cluster has image (${clusterId}, ${node.id}).`); } clusterNodes.add(node.id); } _removeClusterNode(node) { if (!node.isComplete) { return; } const clusterId = node.clusterId; if (clusterId == null || !this._clusterNodes.has(clusterId)) { return; } const clusterNodes = this._clusterNodes.get(clusterId); clusterNodes.delete(node.id); if (!clusterNodes.size) { this._clusterNodes.delete(clusterId); } } _makeFull(node, fillNode) { if (fillNode.computed_altitude == null) { fillNode.computed_altitude = this._defaultAlt; } if (fillNode.computed_rotation == null) { fillNode.computed_rotation = this._graphCalculator.rotationFromCompass(fillNode.compass_angle, fillNode.exif_orientation); } node.makeComplete(fillNode); this._addClusterNode(node); } _disposeNode(node) { this._removeClusterNode(node); node.dispose(); } _getNodeLngLat(node) { if (!this._computedGraph) { return node.originalLngLat; } return node.lngLat; } _preStore(h, node) { if (!(h in this._preStored)) { this._preStored[h] = {}; } this._preStored[h][node.id] = node; } _removeFromPreStore(h) { let preStored = null; if (h in this._preStored) { preStored = this._preStored[h]; delete this._preStored[h]; } return preStored; } _setNode(node) { let key = node.id; if (this.hasNode(key)) { throw new GraphMapillaryError(`Image already exist (${key}).`); } this._nodes[key] = node; } _uncacheTile(h, keepSequenceKey) { for (let node of this._cachedTiles[h].nodes) { let key = node.id; delete this._nodeToTile[key]; if (key in this._cachedNodes) { delete this._cachedNodes[key]; } if (key in this._cachedNodeTiles) { delete this._cachedNodeTiles[key]; } if (key in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[key]; } if (node.sequenceId === keepSequenceKey) { this._preStore(h, node); node.uncache(); } else { delete this._nodes[key]; if (node.sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[node.sequenceId]; } this._disposeNode(node); } } for (let nodeIndexItem of this._nodeIndexTiles[h]) { this._nodeIndex.remove(nodeIndexItem); this._nodeIndexNodes.delete(nodeIndexItem.id); } delete this._nodeIndexTiles[h]; delete this._cachedTiles[h]; } _uncachePreStored(preStored) { let hs = {}; for (let [key, h] of preStored) { if (key in this._nodes) { delete this._nodes[key]; } if (key in this._cachedNodes) { delete this._cachedNodes[key]; } const node = this._preStored[h][key]; if (node.sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[node.sequenceId]; } delete this._preStored[h][key]; this._disposeNode(node); hs[h] = true; } for (let h in hs) { if (!hs.hasOwnProperty(h)) { continue; } if (Object.keys(this._preStored[h]).length === 0) { delete this._preStored[h]; } } } _updateCachedTileAccess(key, accessed) { if (key in this._nodeToTile) { this._cachedTiles[this._nodeToTile[key]].accessed = accessed; } } _updateCachedNodeAccess(key, accessed) { if (key in this._cachedNodes) { this._cachedNodes[key].accessed = accessed; } } _updateCell$(cellId) { return this._api.getCoreImages$(cellId).pipe(mergeMap((contract) => { if (!(cellId in this._cachedTiles)) { return empty(); } const nodeIndex = this._nodeIndex; const nodeIndexCell = this._nodeIndexTiles[cellId]; const nodeIndexNodes = this._nodeIndexNodes; const nodeToCell = this._nodeToTile; const cell = this._cachedTiles[cellId]; cell.accessed = new Date().getTime(); const cellNodes = cell.nodes; const cores = contract.images; for (const core of cores) { if (core == null) { break; } if (this.hasNode(core.id)) { continue; } if (core.sequence.id == null) { console.warn(`Sequence missing, discarding ` + `node (${core.id})`); continue; } let node = null; if (this._preDeletedNodes.has(core.id)) { node = this._unDeleteNode(core.id); } else { node = new Image$1(core); } cellNodes.push(node); const nodeIndexItem = { cellId, id: node.id, lat: node.lngLat.lat, lng: node.lngLat.lng, node: node, }; nodeIndex.insert(nodeIndexItem); nodeIndexCell.push(nodeIndexItem); nodeIndexNodes.set(node.id, nodeIndexItem); nodeToCell[node.id] = cellId; this._setNode(node); } return of(cellId); }), catchError((error) => { console.error(error); return empty(); })); } _unDeleteNode(id) { if (!this._preDeletedNodes.has(id)) { throw new GraphMapillaryError(`Pre-deleted node does not exist ${id}`); } const node = this._preDeletedNodes.get(id); this._preDeletedNodes.delete(id); if (node.isComplete) { this._addClusterNode(node); } return node; } } class MarkerSet { constructor() { this._hash = {}; this._index = new MarkerSet._spatialIndex(16); this._indexChanged$ = new Subject(); this._updated$ = new Subject(); } static register(spatialIndex) { MarkerSet._spatialIndex = spatialIndex; } get changed$() { return this._indexChanged$; } get updated$() { return this._updated$; } add(markers) { const updated = []; const hash = this._hash; const index = this._index; for (const marker of markers) { const id = marker.id; if (id in hash) { index.remove(hash[id]); updated.push(marker); } const item = { lat: marker.lngLat.lat, lng: marker.lngLat.lng, marker: marker, }; hash[id] = item; index.insert(item); } if (updated.length > 0) { this._updated$.next(updated); } if (markers.length > updated.length) { this._indexChanged$.next(this); } } has(id) { return id in this._hash; } get(id) { return this.has(id) ? this._hash[id].marker : undefined; } getAll() { return this._index .all() .map((indexItem) => { return indexItem.marker; }); } remove(ids) { const hash = this._hash; const index = this._index; let changed = false; for (const id of ids) { if (!(id in hash)) { continue; } const item = hash[id]; index.remove(item); delete hash[id]; changed = true; } if (changed) { this._indexChanged$.next(this); } } removeAll() { this._hash = {}; this._index.clear(); this._indexChanged$.next(this); } search([sw, ne]) { return this._index .search({ maxX: ne.lng, maxY: ne.lat, minX: sw.lng, minY: sw.lat, }) .map((indexItem) => { return indexItem.marker; }); } update(marker) { const hash = this._hash; const index = this._index; const id = marker.id; if (!(id in hash)) { return; } index.remove(hash[id]); const item = { lat: marker.lngLat.lat, lng: marker.lngLat.lng, marker: marker, }; hash[id] = item; index.insert(item); } } function quickselect(arr, k, left, right, compare) { quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare$2); } function quickselectStep(arr, k, left, right, compare) { while (right > left) { if (right - left > 600) { var n = right - left + 1; var m = k - left + 1; var z = Math.log(n); var s = 0.5 * Math.exp(2 * z / 3); var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); quickselectStep(arr, k, newLeft, newRight, compare); } var t = arr[k]; var i = left; var j = right; swap(arr, left, k); if (compare(arr[right], t) > 0) swap(arr, left, right); while (i < j) { swap(arr, i, j); i++; j--; while (compare(arr[i], t) < 0) i++; while (compare(arr[j], t) > 0) j--; } if (compare(arr[left], t) === 0) swap(arr, left, j); else { j++; swap(arr, j, right); } if (j <= k) left = j + 1; if (k <= j) right = j - 1; } } function swap(arr, i, j) { var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } function defaultCompare$2(a, b) { return a < b ? -1 : a > b ? 1 : 0; } class RBush { constructor(maxEntries = 9) { // max entries in a node is 9 by default; min node fill is 40% for best performance this._maxEntries = Math.max(4, maxEntries); this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); this.clear(); } all() { return this._all(this.data, []); } search(bbox) { let node = this.data; const result = []; if (!intersects$1(bbox, node)) return result; const toBBox = this.toBBox; const nodesToSearch = []; while (node) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const childBBox = node.leaf ? toBBox(child) : child; if (intersects$1(bbox, childBBox)) { if (node.leaf) result.push(child); else if (contains(bbox, childBBox)) this._all(child, result); else nodesToSearch.push(child); } } node = nodesToSearch.pop(); } return result; } collides(bbox) { let node = this.data; if (!intersects$1(bbox, node)) return false; const nodesToSearch = []; while (node) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const childBBox = node.leaf ? this.toBBox(child) : child; if (intersects$1(bbox, childBBox)) { if (node.leaf || contains(bbox, childBBox)) return true; nodesToSearch.push(child); } } node = nodesToSearch.pop(); } return false; } load(data) { if (!(data && data.length)) return this; if (data.length < this._minEntries) { for (let i = 0; i < data.length; i++) { this.insert(data[i]); } return this; } // recursively build the tree with the given data from scratch using OMT algorithm let node = this._build(data.slice(), 0, data.length - 1, 0); if (!this.data.children.length) { // save as is if tree is empty this.data = node; } else if (this.data.height === node.height) { // split root if trees have the same height this._splitRoot(this.data, node); } else { if (this.data.height < node.height) { // swap trees if inserted one is bigger const tmpNode = this.data; this.data = node; node = tmpNode; } // insert the small tree into the large tree at appropriate level this._insert(node, this.data.height - node.height - 1, true); } return this; } insert(item) { if (item) this._insert(item, this.data.height - 1); return this; } clear() { this.data = createNode([]); return this; } remove(item, equalsFn) { if (!item) return this; let node = this.data; const bbox = this.toBBox(item); const path = []; const indexes = []; let i, parent, goingUp; // depth-first iterative tree traversal while (node || path.length) { if (!node) { // go up node = path.pop(); parent = path[path.length - 1]; i = indexes.pop(); goingUp = true; } if (node.leaf) { // check current node const index = findItem(item, node.children, equalsFn); if (index !== -1) { // item found, remove the item and condense tree upwards node.children.splice(index, 1); path.push(node); this._condense(path); return this; } } if (!goingUp && !node.leaf && contains(node, bbox)) { // go down path.push(node); indexes.push(i); i = 0; parent = node; node = node.children[0]; } else if (parent) { // go right i++; node = parent.children[i]; goingUp = false; } else node = null; // nothing found } return this; } toBBox(item) { return item; } compareMinX(a, b) { return a.minX - b.minX; } compareMinY(a, b) { return a.minY - b.minY; } toJSON() { return this.data; } fromJSON(data) { this.data = data; return this; } _all(node, result) { const nodesToSearch = []; while (node) { if (node.leaf) result.push(...node.children); else nodesToSearch.push(...node.children); node = nodesToSearch.pop(); } return result; } _build(items, left, right, height) { const N = right - left + 1; let M = this._maxEntries; let node; if (N <= M) { // reached leaf level; return leaf node = createNode(items.slice(left, right + 1)); calcBBox(node, this.toBBox); return node; } if (!height) { // target height of the bulk-loaded tree height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization M = Math.ceil(N / Math.pow(M, height - 1)); } node = createNode([]); node.leaf = false; node.height = height; // split the items into M mostly square tiles const N2 = Math.ceil(N / M); const N1 = N2 * Math.ceil(Math.sqrt(M)); multiSelect(items, left, right, N1, this.compareMinX); for (let i = left; i <= right; i += N1) { const right2 = Math.min(i + N1 - 1, right); multiSelect(items, i, right2, N2, this.compareMinY); for (let j = i; j <= right2; j += N2) { const right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively node.children.push(this._build(items, j, right3, height - 1)); } } calcBBox(node, this.toBBox); return node; } _chooseSubtree(bbox, node, level, path) { while (true) { path.push(node); if (node.leaf || path.length - 1 === level) break; let minArea = Infinity; let minEnlargement = Infinity; let targetNode; for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const area = bboxArea(child); const enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement if (enlargement < minEnlargement) { minEnlargement = enlargement; minArea = area < minArea ? area : minArea; targetNode = child; } else if (enlargement === minEnlargement) { // otherwise choose one with the smallest area if (area < minArea) { minArea = area; targetNode = child; } } } node = targetNode || node.children[0]; } return node; } _insert(item, level, isNode) { const bbox = isNode ? item : this.toBBox(item); const insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too const node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node node.children.push(item); extend(node, bbox); // split on node overflow; propagate upwards if necessary while (level >= 0) { if (insertPath[level].children.length > this._maxEntries) { this._split(insertPath, level); level--; } else break; } // adjust bboxes along the insertion path this._adjustParentBBoxes(bbox, insertPath, level); } // split overflowed node into two _split(insertPath, level) { const node = insertPath[level]; const M = node.children.length; const m = this._minEntries; this._chooseSplitAxis(node, m, M); const splitIndex = this._chooseSplitIndex(node, m, M); const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); newNode.height = node.height; newNode.leaf = node.leaf; calcBBox(node, this.toBBox); calcBBox(newNode, this.toBBox); if (level) insertPath[level - 1].children.push(newNode); else this._splitRoot(node, newNode); } _splitRoot(node, newNode) { // split root node this.data = createNode([node, newNode]); this.data.height = node.height + 1; this.data.leaf = false; calcBBox(this.data, this.toBBox); } _chooseSplitIndex(node, m, M) { let index; let minOverlap = Infinity; let minArea = Infinity; for (let i = m; i <= M - m; i++) { const bbox1 = distBBox(node, 0, i, this.toBBox); const bbox2 = distBBox(node, i, M, this.toBBox); const overlap = intersectionArea(bbox1, bbox2); const area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap if (overlap < minOverlap) { minOverlap = overlap; index = i; minArea = area < minArea ? area : minArea; } else if (overlap === minOverlap) { // otherwise choose distribution with minimum area if (area < minArea) { minArea = area; index = i; } } } return index || M - m; } // sorts node children by the best axis for split _chooseSplitAxis(node, m, M) { const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; const xMargin = this._allDistMargin(node, m, M, compareMinX); const yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX, // otherwise it"s already sorted by minY if (xMargin < yMargin) node.children.sort(compareMinX); } // total margin of all possible split distributions where each node is at least m full _allDistMargin(node, m, M, compare) { node.children.sort(compare); const toBBox = this.toBBox; const leftBBox = distBBox(node, 0, m, toBBox); const rightBBox = distBBox(node, M - m, M, toBBox); let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); for (let i = m; i < M - m; i++) { const child = node.children[i]; extend(leftBBox, node.leaf ? toBBox(child) : child); margin += bboxMargin(leftBBox); } for (let i = M - m - 1; i >= m; i--) { const child = node.children[i]; extend(rightBBox, node.leaf ? toBBox(child) : child); margin += bboxMargin(rightBBox); } return margin; } _adjustParentBBoxes(bbox, path, level) { // adjust bboxes along the given tree path for (let i = level; i >= 0; i--) { extend(path[i], bbox); } } _condense(path) { // go through the path, removing empty nodes and updating bboxes for (let i = path.length - 1, siblings; i >= 0; i--) { if (path[i].children.length === 0) { if (i > 0) { siblings = path[i - 1].children; siblings.splice(siblings.indexOf(path[i]), 1); } else this.clear(); } else calcBBox(path[i], this.toBBox); } } } function findItem(item, items, equalsFn) { if (!equalsFn) return items.indexOf(item); for (let i = 0; i < items.length; i++) { if (equalsFn(item, items[i])) return i; } return -1; } // calculate node"s bbox from bboxes of its children function calcBBox(node, toBBox) { distBBox(node, 0, node.children.length, toBBox, node); } // min bounding rectangle of node children from k to p-1 function distBBox(node, k, p, toBBox, destNode) { if (!destNode) destNode = createNode(null); destNode.minX = Infinity; destNode.minY = Infinity; destNode.maxX = -Infinity; destNode.maxY = -Infinity; for (let i = k; i < p; i++) { const child = node.children[i]; extend(destNode, node.leaf ? toBBox(child) : child); } return destNode; } function extend(a, b) { a.minX = Math.min(a.minX, b.minX); a.minY = Math.min(a.minY, b.minY); a.maxX = Math.max(a.maxX, b.maxX); a.maxY = Math.max(a.maxY, b.maxY); return a; } function compareNodeMinX(a, b) { return a.minX - b.minX; } function compareNodeMinY(a, b) { return a.minY - b.minY; } function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } function enlargedArea(a, b) { return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); } function intersectionArea(a, b) { const minX = Math.max(a.minX, b.minX); const minY = Math.max(a.minY, b.minY); const maxX = Math.min(a.maxX, b.maxX); const maxY = Math.min(a.maxY, b.maxY); return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); } function contains(a, b) { return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY; } function intersects$1(a, b) { return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY; } function createNode(children) { return { children, height: 1, leaf: true, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; // combines selection algorithm with binary divide & conquer approach function multiSelect(arr, left, right, n, compare) { const stack = [left, right]; while (stack.length) { right = stack.pop(); left = stack.pop(); if (right - left <= n) continue; const mid = left + Math.ceil((right - left) / n / 2) * n; quickselect(arr, mid, left, right, compare); stack.push(left, mid, mid, right); } } class GeoRBush extends RBush { compareMinX(a, b) { return a.lng - b.lng; } compareMinY(a, b) { return a.lat - b.lat; } toBBox(item) { return { minX: item.lng, minY: item.lat, maxX: item.lng, maxY: item.lat, }; } } class ComponentService { constructor(container, navigator) { this._components = {}; for (const componentName in ComponentService.registeredComponents) { if (!ComponentService.registeredComponents.hasOwnProperty(componentName)) { continue; } const component = ComponentService.registeredComponents[componentName]; this._components[componentName] = { active: false, component: new component(componentName, container, navigator), }; } this._coverComponent = new ComponentService.registeredCoverComponent("cover", container, navigator); this._coverComponent.activate(); this._coverActivated = true; } static register(component) { if (ComponentService.registeredComponents[component.componentName] === undefined) { ComponentService.registeredComponents[component.componentName] = component; } } static registerCover(coverComponent) { ComponentService.registeredCoverComponent = coverComponent; } get coverActivated() { return this._coverActivated; } activateCover() { if (this._coverActivated) { return; } this._coverActivated = true; for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } const component = this._components[componentName]; if (component.active) { component.component.deactivate(); } } } deactivateCover() { if (!this._coverActivated) { return; } this._coverActivated = false; for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } const component = this._components[componentName]; if (component.active) { component.component.activate(); } } } activate(name) { this._checkName(name); this._components[name].active = true; if (!this._coverActivated) { this.get(name).activate(); } } configure(name, conf) { this._checkName(name); this.get(name).configure(conf); } deactivate(name) { this._checkName(name); this._components[name].active = false; if (!this._coverActivated) { this.get(name).deactivate(); } } get(name) { return this._components[name].component; } getCover() { return this._coverComponent; } remove() { this._coverComponent.deactivate(); for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } this._components[componentName].component.deactivate(); } } _checkName(name) { if (!(name in this._components)) { throw new ArgumentMapillaryError(`Component does not exist: ${name}`); } } } ComponentService.registeredComponents = {}; var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; } function getAugmentedNamespace(n) { if (n.__esModule) return n; var f = n.default; if (typeof f == "function") { var a = function a () { if (this instanceof a) { var args = [null]; args.push.apply(args, arguments); var Ctor = Function.bind.apply(f, args); return new Ctor(); } return f.apply(this, arguments); }; a.prototype = f.prototype; } else a = {}; Object.defineProperty(a, "__esModule", {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } var nativeIsArray = Array.isArray; var toString$2 = Object.prototype.toString; var xIsArray = nativeIsArray || isArray$3; function isArray$3(obj) { return toString$2.call(obj) === "[object Array]" } var version$5 = "2"; var version$4 = version$5; VirtualPatch.NONE = 0; VirtualPatch.VTEXT = 1; VirtualPatch.VNODE = 2; VirtualPatch.WIDGET = 3; VirtualPatch.PROPS = 4; VirtualPatch.ORDER = 5; VirtualPatch.INSERT = 6; VirtualPatch.REMOVE = 7; VirtualPatch.THUNK = 8; var vpatch = VirtualPatch; function VirtualPatch(type, vNode, patch) { this.type = Number(type); this.vNode = vNode; this.patch = patch; } VirtualPatch.prototype.version = version$4; VirtualPatch.prototype.type = "VirtualPatch"; var version$3 = version$5; var isVnode = isVirtualNode; function isVirtualNode(x) { return x && x.type === "VirtualNode" && x.version === version$3 } var version$2 = version$5; var isVtext = isVirtualText; function isVirtualText(x) { return x && x.type === "VirtualText" && x.version === version$2 } var isWidget_1 = isWidget$7; function isWidget$7(w) { return w && w.type === "Widget" } var isThunk_1 = isThunk$3; function isThunk$3(t) { return t && t.type === "Thunk" } var isVNode$4 = isVnode; var isVText$3 = isVtext; var isWidget$6 = isWidget_1; var isThunk$2 = isThunk_1; var handleThunk_1 = handleThunk$2; function handleThunk$2(a, b) { var renderedA = a; var renderedB = b; if (isThunk$2(b)) { renderedB = renderThunk(b, a); } if (isThunk$2(a)) { renderedA = renderThunk(a, null); } return { a: renderedA, b: renderedB } } function renderThunk(thunk, previous) { var renderedThunk = thunk.vnode; if (!renderedThunk) { renderedThunk = thunk.vnode = thunk.render(previous); } if (!(isVNode$4(renderedThunk) || isVText$3(renderedThunk) || isWidget$6(renderedThunk))) { throw new Error("thunk did not return a valid node"); } return renderedThunk } var isObject$2 = function isObject(x) { return typeof x === "object" && x !== null; }; var isVhook = isHook$3; function isHook$3(hook) { return hook && (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) } var isObject$1 = isObject$2; var isHook$2 = isVhook; var diffProps_1 = diffProps$1; function diffProps$1(a, b) { var diff; for (var aKey in a) { if (!(aKey in b)) { diff = diff || {}; diff[aKey] = undefined; } var aValue = a[aKey]; var bValue = b[aKey]; if (aValue === bValue) { continue } else if (isObject$1(aValue) && isObject$1(bValue)) { if (getPrototype$1(bValue) !== getPrototype$1(aValue)) { diff = diff || {}; diff[aKey] = bValue; } else if (isHook$2(bValue)) { diff = diff || {}; diff[aKey] = bValue; } else { var objectDiff = diffProps$1(aValue, bValue); if (objectDiff) { diff = diff || {}; diff[aKey] = objectDiff; } } } else { diff = diff || {}; diff[aKey] = bValue; } } for (var bKey in b) { if (!(bKey in a)) { diff = diff || {}; diff[bKey] = b[bKey]; } } return diff } function getPrototype$1(value) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(value) } else if (value.__proto__) { return value.__proto__ } else if (value.constructor) { return value.constructor.prototype } } var isArray$2 = xIsArray; var VPatch$1 = vpatch; var isVNode$3 = isVnode; var isVText$2 = isVtext; var isWidget$5 = isWidget_1; var isThunk$1 = isThunk_1; var handleThunk$1 = handleThunk_1; var diffProps = diffProps_1; var diff_1$1 = diff$2; function diff$2(a, b) { var patch = { a: a }; walk(a, b, patch, 0); return patch } function walk(a, b, patch, index) { if (a === b) { return } var apply = patch[index]; var applyClear = false; if (isThunk$1(a) || isThunk$1(b)) { thunks(a, b, patch, index); } else if (b == null) { // If a is a widget we will add a remove patch for it // Otherwise any child widgets/hooks must be destroyed. // This prevents adding two remove patches for a widget. if (!isWidget$5(a)) { clearState(a, patch, index); apply = patch[index]; } apply = appendPatch(apply, new VPatch$1(VPatch$1.REMOVE, a, b)); } else if (isVNode$3(b)) { if (isVNode$3(a)) { if (a.tagName === b.tagName && a.namespace === b.namespace && a.key === b.key) { var propsPatch = diffProps(a.properties, b.properties); if (propsPatch) { apply = appendPatch(apply, new VPatch$1(VPatch$1.PROPS, a, propsPatch)); } apply = diffChildren(a, b, patch, apply, index); } else { apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); applyClear = true; } } else { apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); applyClear = true; } } else if (isVText$2(b)) { if (!isVText$2(a)) { apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); applyClear = true; } else if (a.text !== b.text) { apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); } } else if (isWidget$5(b)) { if (!isWidget$5(a)) { applyClear = true; } apply = appendPatch(apply, new VPatch$1(VPatch$1.WIDGET, a, b)); } if (apply) { patch[index] = apply; } if (applyClear) { clearState(a, patch, index); } } function diffChildren(a, b, patch, apply, index) { var aChildren = a.children; var orderedSet = reorder(aChildren, b.children); var bChildren = orderedSet.children; var aLen = aChildren.length; var bLen = bChildren.length; var len = aLen > bLen ? aLen : bLen; for (var i = 0; i < len; i++) { var leftNode = aChildren[i]; var rightNode = bChildren[i]; index += 1; if (!leftNode) { if (rightNode) { // Excess nodes in b need to be added apply = appendPatch(apply, new VPatch$1(VPatch$1.INSERT, null, rightNode)); } } else { walk(leftNode, rightNode, patch, index); } if (isVNode$3(leftNode) && leftNode.count) { index += leftNode.count; } } if (orderedSet.moves) { // Reorder nodes last apply = appendPatch(apply, new VPatch$1( VPatch$1.ORDER, a, orderedSet.moves )); } return apply } function clearState(vNode, patch, index) { // TODO: Make this a single walk, not two unhook(vNode, patch, index); destroyWidgets(vNode, patch, index); } // Patch records for all destroyed widgets must be added because we need // a DOM node reference for the destroy function function destroyWidgets(vNode, patch, index) { if (isWidget$5(vNode)) { if (typeof vNode.destroy === "function") { patch[index] = appendPatch( patch[index], new VPatch$1(VPatch$1.REMOVE, vNode, null) ); } } else if (isVNode$3(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { var children = vNode.children; var len = children.length; for (var i = 0; i < len; i++) { var child = children[i]; index += 1; destroyWidgets(child, patch, index); if (isVNode$3(child) && child.count) { index += child.count; } } } else if (isThunk$1(vNode)) { thunks(vNode, null, patch, index); } } // Create a sub-patch for thunks function thunks(a, b, patch, index) { var nodes = handleThunk$1(a, b); var thunkPatch = diff$2(nodes.a, nodes.b); if (hasPatches(thunkPatch)) { patch[index] = new VPatch$1(VPatch$1.THUNK, null, thunkPatch); } } function hasPatches(patch) { for (var index in patch) { if (index !== "a") { return true } } return false } // Execute hooks when two nodes are identical function unhook(vNode, patch, index) { if (isVNode$3(vNode)) { if (vNode.hooks) { patch[index] = appendPatch( patch[index], new VPatch$1( VPatch$1.PROPS, vNode, undefinedKeys(vNode.hooks) ) ); } if (vNode.descendantHooks || vNode.hasThunks) { var children = vNode.children; var len = children.length; for (var i = 0; i < len; i++) { var child = children[i]; index += 1; unhook(child, patch, index); if (isVNode$3(child) && child.count) { index += child.count; } } } } else if (isThunk$1(vNode)) { thunks(vNode, null, patch, index); } } function undefinedKeys(obj) { var result = {}; for (var key in obj) { result[key] = undefined; } return result } // List diff, naive left to right reordering function reorder(aChildren, bChildren) { // O(M) time, O(M) memory var bChildIndex = keyIndex(bChildren); var bKeys = bChildIndex.keys; var bFree = bChildIndex.free; if (bFree.length === bChildren.length) { return { children: bChildren, moves: null } } // O(N) time, O(N) memory var aChildIndex = keyIndex(aChildren); var aKeys = aChildIndex.keys; var aFree = aChildIndex.free; if (aFree.length === aChildren.length) { return { children: bChildren, moves: null } } // O(MAX(N, M)) memory var newChildren = []; var freeIndex = 0; var freeCount = bFree.length; var deletedItems = 0; // Iterate through a and match a node in b // O(N) time, for (var i = 0 ; i < aChildren.length; i++) { var aItem = aChildren[i]; var itemIndex; if (aItem.key) { if (bKeys.hasOwnProperty(aItem.key)) { // Match up the old keys itemIndex = bKeys[aItem.key]; newChildren.push(bChildren[itemIndex]); } else { // Remove old keyed items itemIndex = i - deletedItems++; newChildren.push(null); } } else { // Match the item in a with the next free item in b if (freeIndex < freeCount) { itemIndex = bFree[freeIndex++]; newChildren.push(bChildren[itemIndex]); } else { // There are no free items in b to match with // the free items in a, so the extra free nodes // are deleted. itemIndex = i - deletedItems++; newChildren.push(null); } } } var lastFreeIndex = freeIndex >= bFree.length ? bChildren.length : bFree[freeIndex]; // Iterate through b and append any new keys // O(M) time for (var j = 0; j < bChildren.length; j++) { var newItem = bChildren[j]; if (newItem.key) { if (!aKeys.hasOwnProperty(newItem.key)) { // Add any new keyed items // We are adding new items to the end and then sorting them // in place. In future we should insert new items in place. newChildren.push(newItem); } } else if (j >= lastFreeIndex) { // Add any leftover non-keyed items newChildren.push(newItem); } } var simulate = newChildren.slice(); var simulateIndex = 0; var removes = []; var inserts = []; var simulateItem; for (var k = 0; k < bChildren.length;) { var wantedItem = bChildren[k]; simulateItem = simulate[simulateIndex]; // remove items while (simulateItem === null && simulate.length) { removes.push(remove(simulate, simulateIndex, null)); simulateItem = simulate[simulateIndex]; } if (!simulateItem || simulateItem.key !== wantedItem.key) { // if we need a key in this position... if (wantedItem.key) { if (simulateItem && simulateItem.key) { // if an insert doesn"t put this key in place, it needs to move if (bKeys[simulateItem.key] !== k + 1) { removes.push(remove(simulate, simulateIndex, simulateItem.key)); simulateItem = simulate[simulateIndex]; // if the remove didn"t put the wanted item in place, we need to insert it if (!simulateItem || simulateItem.key !== wantedItem.key) { inserts.push({key: wantedItem.key, to: k}); } // items are matching, so skip ahead else { simulateIndex++; } } else { inserts.push({key: wantedItem.key, to: k}); } } else { inserts.push({key: wantedItem.key, to: k}); } k++; } // a key in simulate has no matching wanted key, remove it else if (simulateItem && simulateItem.key) { removes.push(remove(simulate, simulateIndex, simulateItem.key)); } } else { simulateIndex++; k++; } } // remove all the remaining nodes from simulate while(simulateIndex < simulate.length) { simulateItem = simulate[simulateIndex]; removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)); } // If the only moves we have are deletes then we can just // let the delete patch remove these items. if (removes.length === deletedItems && !inserts.length) { return { children: newChildren, moves: null } } return { children: newChildren, moves: { removes: removes, inserts: inserts } } } function remove(arr, index, key) { arr.splice(index, 1); return { from: index, key: key } } function keyIndex(children) { var keys = {}; var free = []; var length = children.length; for (var i = 0; i < length; i++) { var child = children[i]; if (child.key) { keys[child.key] = i; } else { free.push(i); } } return { keys: keys, // A hash of key name to index free: free // An array of unkeyed item indices } } function appendPatch(apply, patch) { if (apply) { if (isArray$2(apply)) { apply.push(patch); } else { apply = [apply, patch]; } return apply } else { return patch } } var diff$1 = diff_1$1; var diff_1 = diff$1; var slice = Array.prototype.slice; var domWalk$2 = iterativelyWalk; function iterativelyWalk(nodes, cb) { if (!("length" in nodes)) { nodes = [nodes]; } nodes = slice.call(nodes); while(nodes.length) { var node = nodes.shift(), ret = cb(node); if (ret) { return ret } if (node.childNodes && node.childNodes.length) { nodes = slice.call(node.childNodes).concat(nodes); } } } var domComment = Comment$1; function Comment$1(data, owner) { if (!(this instanceof Comment$1)) { return new Comment$1(data, owner) } this.data = data; this.nodeValue = data; this.length = data.length; this.ownerDocument = owner || null; } Comment$1.prototype.nodeType = 8; Comment$1.prototype.nodeName = "#comment"; Comment$1.prototype.toString = function _Comment_toString() { return "[object Comment]" }; var domText = DOMText$1; function DOMText$1(value, owner) { if (!(this instanceof DOMText$1)) { return new DOMText$1(value) } this.data = value || ""; this.length = this.data.length; this.ownerDocument = owner || null; } DOMText$1.prototype.type = "DOMTextNode"; DOMText$1.prototype.nodeType = 3; DOMText$1.prototype.nodeName = "#text"; DOMText$1.prototype.toString = function _Text_toString() { return this.data }; DOMText$1.prototype.replaceData = function replaceData(index, length, value) { var current = this.data; var left = current.substring(0, index); var right = current.substring(index + length, current.length); this.data = left + value + right; this.length = this.data.length; }; var dispatchEvent_1 = dispatchEvent$2; function dispatchEvent$2(ev) { var elem = this; var type = ev.type; if (!ev.target) { ev.target = elem; } if (!elem.listeners) { elem.listeners = {}; } var listeners = elem.listeners[type]; if (listeners) { return listeners.forEach(function (listener) { ev.currentTarget = elem; if (typeof listener === "function") { listener(ev); } else { listener.handleEvent(ev); } }) } if (elem.parentNode) { elem.parentNode.dispatchEvent(ev); } } var addEventListener_1 = addEventListener$2; function addEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { elem.listeners = {}; } if (!elem.listeners[type]) { elem.listeners[type] = []; } if (elem.listeners[type].indexOf(listener) === -1) { elem.listeners[type].push(listener); } } var removeEventListener_1 = removeEventListener$2; function removeEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { return } if (!elem.listeners[type]) { return } var list = elem.listeners[type]; var index = list.indexOf(listener); if (index !== -1) { list.splice(index, 1); } } var serialize = serializeNode$1; var voidElements = ["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]; function serializeNode$1(node) { switch (node.nodeType) { case 3: return escapeText(node.data) case 8: return "" default: return serializeElement(node) } } function serializeElement(elem) { var strings = []; var tagname = elem.tagName; if (elem.namespaceURI === "http://www.w3.org/1999/xhtml") { tagname = tagname.toLowerCase(); } strings.push("<" + tagname + properties(elem) + datasetify(elem)); if (voidElements.indexOf(tagname) > -1) { strings.push(" />"); } else { strings.push(">"); if (elem.childNodes.length) { strings.push.apply(strings, elem.childNodes.map(serializeNode$1)); } else if (elem.textContent || elem.innerText) { strings.push(escapeText(elem.textContent || elem.innerText)); } else if (elem.innerHTML) { strings.push(elem.innerHTML); } strings.push(""); } return strings.join("") } function isProperty(elem, key) { var type = typeof elem[key]; if (key === "style" && Object.keys(elem.style).length > 0) { return true } return elem.hasOwnProperty(key) && (type === "string" || type === "boolean" || type === "number") && key !== "nodeName" && key !== "className" && key !== "tagName" && key !== "textContent" && key !== "innerText" && key !== "namespaceURI" && key !== "innerHTML" } function stylify(styles) { if (typeof styles === "string") return styles var attr = ""; Object.keys(styles).forEach(function (key) { var value = styles[key]; key = key.replace(/[A-Z]/g, function(c) { return "-" + c.toLowerCase(); }); attr += key + ":" + value + ";"; }); return attr } function datasetify(elem) { var ds = elem.dataset; var props = []; for (var key in ds) { props.push({ name: "data-" + key, value: ds[key] }); } return props.length ? stringify(props) : "" } function stringify(list) { var attributes = []; list.forEach(function (tuple) { var name = tuple.name; var value = tuple.value; if (name === "style") { value = stylify(value); } attributes.push(name + "=" + """ + escapeAttributeValue(value) + """); }); return attributes.length ? " " + attributes.join(" ") : "" } function properties(elem) { var props = []; for (var key in elem) { if (isProperty(elem, key)) { props.push({ name: key, value: elem[key] }); } } for (var ns in elem._attributes) { for (var attribute in elem._attributes[ns]) { var prop = elem._attributes[ns][attribute]; var name = (prop.prefix ? prop.prefix + ":" : "") + attribute; props.push({ name: name, value: prop.value }); } } if (elem.className) { props.push({ name: "class", value: elem.className }); } return props.length ? stringify(props) : "" } function escapeText(s) { var str = ""; if (typeof(s) === "string") { str = s; } else if (s) { str = s.toString(); } return str .replace(/&/g, "&") .replace(//g, ">") } function escapeAttributeValue(str) { return escapeText(str).replace(/"/g, """) } var domWalk$1 = domWalk$2; var dispatchEvent$1 = dispatchEvent_1; var addEventListener$1 = addEventListener_1; var removeEventListener$1 = removeEventListener_1; var serializeNode = serialize; var htmlns = "http://www.w3.org/1999/xhtml"; var domElement = DOMElement$2; function DOMElement$2(tagName, owner, namespace) { if (!(this instanceof DOMElement$2)) { return new DOMElement$2(tagName) } var ns = namespace === undefined ? htmlns : (namespace || null); this.tagName = ns === htmlns ? String(tagName).toUpperCase() : tagName; this.nodeName = this.tagName; this.className = ""; this.dataset = {}; this.childNodes = []; this.parentNode = null; this.style = {}; this.ownerDocument = owner || null; this.namespaceURI = ns; this._attributes = {}; if (this.tagName === "INPUT") { this.type = "text"; } } DOMElement$2.prototype.type = "DOMElement"; DOMElement$2.prototype.nodeType = 1; DOMElement$2.prototype.appendChild = function _Element_appendChild(child) { if (child.parentNode) { child.parentNode.removeChild(child); } this.childNodes.push(child); child.parentNode = this; return child }; DOMElement$2.prototype.replaceChild = function _Element_replaceChild(elem, needle) { // TODO: Throw NotFoundError if needle.parentNode !== this if (elem.parentNode) { elem.parentNode.removeChild(elem); } var index = this.childNodes.indexOf(needle); needle.parentNode = null; this.childNodes[index] = elem; elem.parentNode = this; return needle }; DOMElement$2.prototype.removeChild = function _Element_removeChild(elem) { // TODO: Throw NotFoundError if elem.parentNode !== this var index = this.childNodes.indexOf(elem); this.childNodes.splice(index, 1); elem.parentNode = null; return elem }; DOMElement$2.prototype.insertBefore = function _Element_insertBefore(elem, needle) { // TODO: Throw NotFoundError if referenceElement is a dom node // and parentNode !== this if (elem.parentNode) { elem.parentNode.removeChild(elem); } var index = needle === null || needle === undefined ? -1 : this.childNodes.indexOf(needle); if (index > -1) { this.childNodes.splice(index, 0, elem); } else { this.childNodes.push(elem); } elem.parentNode = this; return elem }; DOMElement$2.prototype.setAttributeNS = function _Element_setAttributeNS(namespace, name, value) { var prefix = null; var localName = name; var colonPosition = name.indexOf(":"); if (colonPosition > -1) { prefix = name.substr(0, colonPosition); localName = name.substr(colonPosition + 1); } if (this.tagName === "INPUT" && name === "type") { this.type = value; } else { var attributes = this._attributes[namespace] || (this._attributes[namespace] = {}); attributes[localName] = {value: value, prefix: prefix}; } }; DOMElement$2.prototype.getAttributeNS = function _Element_getAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; var value = attributes && attributes[name] && attributes[name].value; if (this.tagName === "INPUT" && name === "type") { return this.type; } if (typeof value !== "string") { return null } return value }; DOMElement$2.prototype.removeAttributeNS = function _Element_removeAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; if (attributes) { delete attributes[name]; } }; DOMElement$2.prototype.hasAttributeNS = function _Element_hasAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; return !!attributes && name in attributes; }; DOMElement$2.prototype.setAttribute = function _Element_setAttribute(name, value) { return this.setAttributeNS(null, name, value) }; DOMElement$2.prototype.getAttribute = function _Element_getAttribute(name) { return this.getAttributeNS(null, name) }; DOMElement$2.prototype.removeAttribute = function _Element_removeAttribute(name) { return this.removeAttributeNS(null, name) }; DOMElement$2.prototype.hasAttribute = function _Element_hasAttribute(name) { return this.hasAttributeNS(null, name) }; DOMElement$2.prototype.removeEventListener = removeEventListener$1; DOMElement$2.prototype.addEventListener = addEventListener$1; DOMElement$2.prototype.dispatchEvent = dispatchEvent$1; // Un-implemented DOMElement$2.prototype.focus = function _Element_focus() { return void 0 }; DOMElement$2.prototype.toString = function _Element_toString() { return serializeNode(this) }; DOMElement$2.prototype.getElementsByClassName = function _Element_getElementsByClassName(classNames) { var classes = classNames.split(" "); var elems = []; domWalk$1(this, function (node) { if (node.nodeType === 1) { var nodeClassName = node.className || ""; var nodeClasses = nodeClassName.split(" "); if (classes.every(function (item) { return nodeClasses.indexOf(item) !== -1 })) { elems.push(node); } } }); return elems }; DOMElement$2.prototype.getElementsByTagName = function _Element_getElementsByTagName(tagName) { tagName = tagName.toLowerCase(); var elems = []; domWalk$1(this.childNodes, function (node) { if (node.nodeType === 1 && (tagName === "*" || node.tagName.toLowerCase() === tagName)) { elems.push(node); } }); return elems }; DOMElement$2.prototype.contains = function _Element_contains(element) { return domWalk$1(this, function (node) { return element === node }) || false }; var DOMElement$1 = domElement; var domFragment = DocumentFragment$1; function DocumentFragment$1(owner) { if (!(this instanceof DocumentFragment$1)) { return new DocumentFragment$1() } this.childNodes = []; this.parentNode = null; this.ownerDocument = owner || null; } DocumentFragment$1.prototype.type = "DocumentFragment"; DocumentFragment$1.prototype.nodeType = 11; DocumentFragment$1.prototype.nodeName = "#document-fragment"; DocumentFragment$1.prototype.appendChild = DOMElement$1.prototype.appendChild; DocumentFragment$1.prototype.replaceChild = DOMElement$1.prototype.replaceChild; DocumentFragment$1.prototype.removeChild = DOMElement$1.prototype.removeChild; DocumentFragment$1.prototype.toString = function _DocumentFragment_toString() { return this.childNodes.map(function (node) { return String(node) }).join("") }; var event = Event$1; function Event$1(family) {} Event$1.prototype.initEvent = function _Event_initEvent(type, bubbles, cancelable) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; }; Event$1.prototype.preventDefault = function _Event_preventDefault() { }; var domWalk = domWalk$2; var Comment = domComment; var DOMText = domText; var DOMElement = domElement; var DocumentFragment = domFragment; var Event = event; var dispatchEvent = dispatchEvent_1; var addEventListener = addEventListener_1; var removeEventListener = removeEventListener_1; var document$3 = Document$1; function Document$1() { if (!(this instanceof Document$1)) { return new Document$1(); } this.head = this.createElement("head"); this.body = this.createElement("body"); this.documentElement = this.createElement("html"); this.documentElement.appendChild(this.head); this.documentElement.appendChild(this.body); this.childNodes = [this.documentElement]; this.nodeType = 9; } var proto = Document$1.prototype; proto.createTextNode = function createTextNode(value) { return new DOMText(value, this) }; proto.createElementNS = function createElementNS(namespace, tagName) { var ns = namespace === null ? null : String(namespace); return new DOMElement(tagName, this, ns) }; proto.createElement = function createElement(tagName) { return new DOMElement(tagName, this) }; proto.createDocumentFragment = function createDocumentFragment() { return new DocumentFragment(this) }; proto.createEvent = function createEvent(family) { return new Event() }; proto.createComment = function createComment(data) { return new Comment(data, this) }; proto.getElementById = function getElementById(id) { id = String(id); var result = domWalk(this.childNodes, function (node) { if (String(node.id) === id) { return node } }); return result || null }; proto.getElementsByClassName = DOMElement.prototype.getElementsByClassName; proto.getElementsByTagName = DOMElement.prototype.getElementsByTagName; proto.contains = DOMElement.prototype.contains; proto.removeEventListener = removeEventListener; proto.addEventListener = addEventListener; proto.dispatchEvent = dispatchEvent; var Document = document$3; var minDocument = new Document(); var topLevel = typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof window !== "undefined" ? window : {}; var minDoc = minDocument; var doccy; if (typeof document !== "undefined") { doccy = document; } else { doccy = topLevel["__GLOBAL_DOCUMENT_CACHE@4"]; if (!doccy) { doccy = topLevel["__GLOBAL_DOCUMENT_CACHE@4"] = minDoc; } } var document_1 = doccy; var isObject = isObject$2; var isHook$1 = isVhook; var applyProperties_1 = applyProperties$2; function applyProperties$2(node, props, previous) { for (var propName in props) { var propValue = props[propName]; if (propValue === undefined) { removeProperty(node, propName, propValue, previous); } else if (isHook$1(propValue)) { removeProperty(node, propName, propValue, previous); if (propValue.hook) { propValue.hook(node, propName, previous ? previous[propName] : undefined); } } else { if (isObject(propValue)) { patchObject(node, props, previous, propName, propValue); } else { node[propName] = propValue; } } } } function removeProperty(node, propName, propValue, previous) { if (previous) { var previousValue = previous[propName]; if (!isHook$1(previousValue)) { if (propName === "attributes") { for (var attrName in previousValue) { node.removeAttribute(attrName); } } else if (propName === "style") { for (var i in previousValue) { node.style[i] = ""; } } else if (typeof previousValue === "string") { node[propName] = ""; } else { node[propName] = null; } } else if (previousValue.unhook) { previousValue.unhook(node, propName, propValue); } } } function patchObject(node, props, previous, propName, propValue) { var previousValue = previous ? previous[propName] : undefined; // Set attributes if (propName === "attributes") { for (var attrName in propValue) { var attrValue = propValue[attrName]; if (attrValue === undefined) { node.removeAttribute(attrName); } else { node.setAttribute(attrName, attrValue); } } return } if(previousValue && isObject(previousValue) && getPrototype(previousValue) !== getPrototype(propValue)) { node[propName] = propValue; return } if (!isObject(node[propName])) { node[propName] = {}; } var replacer = propName === "style" ? "" : undefined; for (var k in propValue) { var value = propValue[k]; node[propName][k] = (value === undefined) ? replacer : value; } } function getPrototype(value) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(value) } else if (value.__proto__) { return value.__proto__ } else if (value.constructor) { return value.constructor.prototype } } var document$2 = document_1; var applyProperties$1 = applyProperties_1; var isVNode$2 = isVnode; var isVText$1 = isVtext; var isWidget$4 = isWidget_1; var handleThunk = handleThunk_1; var createElement_1$1 = createElement$1; function createElement$1(vnode, opts) { var doc = opts ? opts.document || document$2 : document$2; var warn = opts ? opts.warn : null; vnode = handleThunk(vnode).a; if (isWidget$4(vnode)) { return vnode.init() } else if (isVText$1(vnode)) { return doc.createTextNode(vnode.text) } else if (!isVNode$2(vnode)) { if (warn) { warn("Item is not a valid virtual dom node", vnode); } return null } var node = (vnode.namespace === null) ? doc.createElement(vnode.tagName) : doc.createElementNS(vnode.namespace, vnode.tagName); var props = vnode.properties; applyProperties$1(node, props); var children = vnode.children; for (var i = 0; i < children.length; i++) { var childNode = createElement$1(children[i], opts); if (childNode) { node.appendChild(childNode); } } return node } // Maps a virtual DOM tree onto a real DOM tree in an efficient manner. // We don"t want to read all of the DOM nodes in the tree so we use // the in-order tree indexing to eliminate recursion down certain branches. // We only recurse into a DOM node if we know that it contains a child of // interest. var noChild = {}; var domIndex_1 = domIndex$1; function domIndex$1(rootNode, tree, indices, nodes) { if (!indices || indices.length === 0) { return {} } else { indices.sort(ascending); return recurse(rootNode, tree, indices, nodes, 0) } } function recurse(rootNode, tree, indices, nodes, rootIndex) { nodes = nodes || {}; if (rootNode) { if (indexInRange(indices, rootIndex, rootIndex)) { nodes[rootIndex] = rootNode; } var vChildren = tree.children; if (vChildren) { var childNodes = rootNode.childNodes; for (var i = 0; i < tree.children.length; i++) { rootIndex += 1; var vChild = vChildren[i] || noChild; var nextIndex = rootIndex + (vChild.count || 0); // skip recursion down the tree if there are no nodes down here if (indexInRange(indices, rootIndex, nextIndex)) { recurse(childNodes[i], vChild, indices, nodes, rootIndex); } rootIndex = nextIndex; } } } return nodes } // Binary search for an index in the interval [left, right] function indexInRange(indices, left, right) { if (indices.length === 0) { return false } var minIndex = 0; var maxIndex = indices.length - 1; var currentIndex; var currentItem; while (minIndex <= maxIndex) { currentIndex = ((maxIndex + minIndex) / 2) >> 0; currentItem = indices[currentIndex]; if (minIndex === maxIndex) { return currentItem >= left && currentItem <= right } else if (currentItem < left) { minIndex = currentIndex + 1; } else if (currentItem > right) { maxIndex = currentIndex - 1; } else { return true } } return false; } function ascending(a, b) { return a > b ? 1 : -1 } var isWidget$3 = isWidget_1; var updateWidget_1 = updateWidget$1; function updateWidget$1(a, b) { if (isWidget$3(a) && isWidget$3(b)) { if ("name" in a && "name" in b) { return a.id === b.id } else { return a.init === b.init } } return false } var applyProperties = applyProperties_1; var isWidget$2 = isWidget_1; var VPatch = vpatch; var updateWidget = updateWidget_1; var patchOp$1 = applyPatch$1; function applyPatch$1(vpatch, domNode, renderOptions) { var type = vpatch.type; var vNode = vpatch.vNode; var patch = vpatch.patch; switch (type) { case VPatch.REMOVE: return removeNode$1(domNode, vNode) case VPatch.INSERT: return insertNode$1(domNode, patch, renderOptions) case VPatch.VTEXT: return stringPatch(domNode, vNode, patch, renderOptions) case VPatch.WIDGET: return widgetPatch(domNode, vNode, patch, renderOptions) case VPatch.VNODE: return vNodePatch(domNode, vNode, patch, renderOptions) case VPatch.ORDER: reorderChildren(domNode, patch); return domNode case VPatch.PROPS: applyProperties(domNode, patch, vNode.properties); return domNode case VPatch.THUNK: return replaceRoot(domNode, renderOptions.patch(domNode, patch, renderOptions)) default: return domNode } } function removeNode$1(domNode, vNode) { var parentNode = domNode.parentNode; if (parentNode) { parentNode.removeChild(domNode); } destroyWidget(domNode, vNode); return null } function insertNode$1(parentNode, vNode, renderOptions) { var newNode = renderOptions.render(vNode, renderOptions); if (parentNode) { parentNode.appendChild(newNode); } return parentNode } function stringPatch(domNode, leftVNode, vText, renderOptions) { var newNode; if (domNode.nodeType === 3) { domNode.replaceData(0, domNode.length, vText.text); newNode = domNode; } else { var parentNode = domNode.parentNode; newNode = renderOptions.render(vText, renderOptions); if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } } return newNode } function widgetPatch(domNode, leftVNode, widget, renderOptions) { var updating = updateWidget(leftVNode, widget); var newNode; if (updating) { newNode = widget.update(leftVNode, domNode) || domNode; } else { newNode = renderOptions.render(widget, renderOptions); } var parentNode = domNode.parentNode; if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } if (!updating) { destroyWidget(domNode, leftVNode); } return newNode } function vNodePatch(domNode, leftVNode, vNode, renderOptions) { var parentNode = domNode.parentNode; var newNode = renderOptions.render(vNode, renderOptions); if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } return newNode } function destroyWidget(domNode, w) { if (typeof w.destroy === "function" && isWidget$2(w)) { w.destroy(domNode); } } function reorderChildren(domNode, moves) { var childNodes = domNode.childNodes; var keyMap = {}; var node; var remove; var insert; for (var i = 0; i < moves.removes.length; i++) { remove = moves.removes[i]; node = childNodes[remove.from]; if (remove.key) { keyMap[remove.key] = node; } domNode.removeChild(node); } var length = childNodes.length; for (var j = 0; j < moves.inserts.length; j++) { insert = moves.inserts[j]; node = keyMap[insert.key]; // this is the weirdest bug i"ve ever seen in webkit domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]); } } function replaceRoot(oldRoot, newRoot) { if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) { oldRoot.parentNode.replaceChild(newRoot, oldRoot); } return newRoot; } var document$1 = document_1; var isArray$1 = xIsArray; var render = createElement_1$1; var domIndex = domIndex_1; var patchOp = patchOp$1; var patch_1$1 = patch$2; function patch$2(rootNode, patches, renderOptions) { renderOptions = renderOptions || {}; renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch$2 ? renderOptions.patch : patchRecursive; renderOptions.render = renderOptions.render || render; return renderOptions.patch(rootNode, patches, renderOptions) } function patchRecursive(rootNode, patches, renderOptions) { var indices = patchIndices(patches); if (indices.length === 0) { return rootNode } var index = domIndex(rootNode, patches.a, indices); var ownerDocument = rootNode.ownerDocument; if (!renderOptions.document && ownerDocument !== document$1) { renderOptions.document = ownerDocument; } for (var i = 0; i < indices.length; i++) { var nodeIndex = indices[i]; rootNode = applyPatch(rootNode, index[nodeIndex], patches[nodeIndex], renderOptions); } return rootNode } function applyPatch(rootNode, domNode, patchList, renderOptions) { if (!domNode) { return rootNode } var newNode; if (isArray$1(patchList)) { for (var i = 0; i < patchList.length; i++) { newNode = patchOp(patchList[i], domNode, renderOptions); if (domNode === rootNode) { rootNode = newNode; } } } else { newNode = patchOp(patchList, domNode, renderOptions); if (domNode === rootNode) { rootNode = newNode; } } return rootNode } function patchIndices(patches) { var indices = []; for (var key in patches) { if (key !== "a") { indices.push(Number(key)); } } return indices } var patch$1 = patch_1$1; var patch_1 = patch$1; var version$1 = version$5; var isVNode$1 = isVnode; var isWidget$1 = isWidget_1; var isThunk = isThunk_1; var isVHook = isVhook; var vnode = VirtualNode; var noProperties = {}; var noChildren = []; function VirtualNode(tagName, properties, children, key, namespace) { this.tagName = tagName; this.properties = properties || noProperties; this.children = children || noChildren; this.key = key != null ? String(key) : undefined; this.namespace = (typeof namespace === "string") ? namespace : null; var count = (children && children.length) || 0; var descendants = 0; var hasWidgets = false; var hasThunks = false; var descendantHooks = false; var hooks; for (var propName in properties) { if (properties.hasOwnProperty(propName)) { var property = properties[propName]; if (isVHook(property) && property.unhook) { if (!hooks) { hooks = {}; } hooks[propName] = property; } } } for (var i = 0; i < count; i++) { var child = children[i]; if (isVNode$1(child)) { descendants += child.count || 0; if (!hasWidgets && child.hasWidgets) { hasWidgets = true; } if (!hasThunks && child.hasThunks) { hasThunks = true; } if (!descendantHooks && (child.hooks || child.descendantHooks)) { descendantHooks = true; } } else if (!hasWidgets && isWidget$1(child)) { if (typeof child.destroy === "function") { hasWidgets = true; } } else if (!hasThunks && isThunk(child)) { hasThunks = true; } } this.count = count + descendants; this.hasWidgets = hasWidgets; this.hasThunks = hasThunks; this.hooks = hooks; this.descendantHooks = descendantHooks; } VirtualNode.prototype.version = version$1; VirtualNode.prototype.type = "VirtualNode"; var version = version$5; var vtext = VirtualText; function VirtualText(text) { this.text = String(text); } VirtualText.prototype.version = version; VirtualText.prototype.type = "VirtualText"; /*! * Cross-Browser Split 1.1.1 * Copyright 2007-2012 Steven Levithan * Available under the MIT License * ECMAScript compliant, uniform cross-browser split method */ /** * Splits a string into an array of strings using a regex or string separator. Matches of the * separator are not included in the result array. However, if `separator` is a regex that contains * capturing groups, backreferences are spliced into the result each time `separator` is matched. * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably * cross-browser. * @param {String} str String to split. * @param {RegExp|String} separator Regex or string to use for separating the string. * @param {Number} [limit] Maximum number of items to include in the result array. * @returns {Array} Array of substrings. * @example * * // Basic use * split("a b c d", " "); * // -> ["a", "b", "c", "d"] * * // With limit * split("a b c d", " ", 2); * // -> ["a", "b"] * * // Backreferences in result array * split("..word1 word2..", /([a-z]+)(d+)/i); * // -> ["..", "word", "1", " ", "word", "2", ".."] */ var browserSplit = (function split(undef) { var nativeSplit = String.prototype.split, compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group self; self = function(str, separator, limit) { // If `separator` is not a regex, use `nativeSplit` if (Object.prototype.toString.call(separator) !== "[object RegExp]") { return nativeSplit.call(str, separator, limit); } var output = [], flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6 (separator.sticky ? "y" : ""), // Firefox 3+ lastLastIndex = 0, // Make `global` and avoid `lastIndex` issues by working with a copy separator = new RegExp(separator.source, flags + "g"), separator2, match, lastIndex, lastLength; str += ""; // Type-convert if (!compliantExecNpcg) { // Doesn"t need flags gy, but they don"t hurt separator2 = new RegExp("^" + separator.source + "$(?!\s)", flags); } /* Values for `limit`, per the spec: * If undefined: 4294967295 // Math.pow(2, 32) - 1 * If 0, Infinity, or NaN: 0 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; * If negative number: 4294967296 - Math.floor(Math.abs(limit)) * If other: Type-convert, then use the above rules */ limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1 limit >>> 0; // ToUint32(limit) while (match = separator.exec(str)) { // `separator.lastIndex` is not reliable cross-browser lastIndex = match.index + match[0].length; if (lastIndex > lastLastIndex) { output.push(str.slice(lastLastIndex, match.index)); // Fix browsers whose `exec` methods don"t consistently return `undefined` for // nonparticipating capturing groups if (!compliantExecNpcg && match.length > 1) { match[0].replace(separator2, function() { for (var i = 1; i < arguments.length - 2; i++) { if (arguments[i] === undef) { match[i] = undef; } } }); } if (match.length > 1 && match.index < str.length) { Array.prototype.push.apply(output, match.slice(1)); } lastLength = match[0].length; lastLastIndex = lastIndex; if (output.length >= limit) { break; } } if (separator.lastIndex === match.index) { separator.lastIndex++; // Avoid an infinite loop } } if (lastLastIndex === str.length) { if (lastLength || !separator.test("")) { output.push(""); } } else { output.push(str.slice(lastLastIndex)); } return output.length > limit ? output.slice(0, limit) : output; }; return self; })(); var split = browserSplit; var classIdSplit = /([.#]?[a-zA-Z0-9u007F-uFFFF_:-]+)/; var notClassId = /^.|#/; var parseTag_1 = parseTag$1; function parseTag$1(tag, props) { if (!tag) { return "DIV"; } var noId = !(props.hasOwnProperty("id")); var tagParts = split(tag, classIdSplit); var tagName = null; if (notClassId.test(tagParts[1])) { tagName = "DIV"; } var classes, part, type, i; for (i = 0; i < tagParts.length; i++) { part = tagParts[i]; if (!part) { continue; } type = part.charAt(0); if (!tagName) { tagName = part; } else if (type === ".") { classes = classes || []; classes.push(part.substring(1, part.length)); } else if (type === "#" && noId) { props.id = part.substring(1, part.length); } } if (classes) { if (props.className) { classes.push(props.className); } props.className = classes.join(" "); } return props.namespace ? tagName : tagName.toUpperCase(); } var softSetHook$1 = SoftSetHook; function SoftSetHook(value) { if (!(this instanceof SoftSetHook)) { return new SoftSetHook(value); } this.value = value; } SoftSetHook.prototype.hook = function (node, propertyName) { if (node[propertyName] !== this.value) { node[propertyName] = this.value; } }; /*global window, global*/ var root = typeof window !== "undefined" ? window : typeof commonjsGlobal !== "undefined" ? commonjsGlobal : {}; var individual = Individual$1; function Individual$1(key, value) { if (key in root) { return root[key]; } root[key] = value; return value; } var Individual = individual; var oneVersion = OneVersion; function OneVersion(moduleName, version, defaultValue) { var key = "__INDIVIDUAL_ONE_VERSION_" + moduleName; var enforceKey = key + "_ENFORCE_SINGLETON"; var versionValue = Individual(enforceKey, version); if (versionValue !== version) { throw new Error("Can only have one copy of " + moduleName + ". " + "You already have version " + versionValue + " installed. " + "This means you cannot install version " + version); } return Individual(key, defaultValue); } var OneVersionConstraint = oneVersion; var MY_VERSION = "7"; OneVersionConstraint("ev-store", MY_VERSION); var hashKey = "__EV_STORE_KEY@" + MY_VERSION; var evStore = EvStore$1; function EvStore$1(elem) { var hash = elem[hashKey]; if (!hash) { hash = elem[hashKey] = {}; } return hash; } var EvStore = evStore; var evHook$1 = EvHook; function EvHook(value) { if (!(this instanceof EvHook)) { return new EvHook(value); } this.value = value; } EvHook.prototype.hook = function (node, propertyName) { var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = this.value; }; EvHook.prototype.unhook = function(node, propertyName) { var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = undefined; }; var isArray = xIsArray; var VNode$1 = vnode; var VText$1 = vtext; var isVNode = isVnode; var isVText = isVtext; var isWidget = isWidget_1; var isHook = isVhook; var isVThunk = isThunk_1; var parseTag = parseTag_1; var softSetHook = softSetHook$1; var evHook = evHook$1; var virtualHyperscript = h$2; function h$2(tagName, properties, children) { var childNodes = []; var tag, props, key, namespace; if (!children && isChildren(properties)) { children = properties; props = {}; } props = props || properties || {}; tag = parseTag(tagName, props); // support keys if (props.hasOwnProperty("key")) { key = props.key; props.key = undefined; } // support namespace if (props.hasOwnProperty("namespace")) { namespace = props.namespace; props.namespace = undefined; } // fix cursor bug if (tag === "INPUT" && !namespace && props.hasOwnProperty("value") && props.value !== undefined && !isHook(props.value) ) { props.value = softSetHook(props.value); } transformProperties(props); if (children !== undefined && children !== null) { addChild(children, childNodes, tag, props); } return new VNode$1(tag, props, childNodes, key, namespace); } function addChild(c, childNodes, tag, props) { if (typeof c === "string") { childNodes.push(new VText$1(c)); } else if (typeof c === "number") { childNodes.push(new VText$1(String(c))); } else if (isChild(c)) { childNodes.push(c); } else if (isArray(c)) { for (var i = 0; i < c.length; i++) { addChild(c[i], childNodes, tag, props); } } else if (c === null || c === undefined) { return; } else { throw UnexpectedVirtualElement({ foreignObject: c, parentVnode: { tagName: tag, properties: props } }); } } function transformProperties(props) { for (var propName in props) { if (props.hasOwnProperty(propName)) { var value = props[propName]; if (isHook(value)) { continue; } if (propName.substr(0, 3) === "ev-") { // add ev-foo support props[propName] = evHook(value); } } } } function isChild(x) { return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x); } function isChildren(x) { return typeof x === "string" || isArray(x) || isChild(x); } function UnexpectedVirtualElement(data) { var err = new Error(); err.type = "virtual-hyperscript.unexpected.virtual-element"; err.message = "Unexpected virtual child passed to h(). " + "Expected a VNode / Vthunk / VWidget / string but: " + "got: " + errorString(data.foreignObject) + ". " + "The parent vnode is: " + errorString(data.parentVnode); err.foreignObject = data.foreignObject; err.parentVnode = data.parentVnode; return err; } function errorString(obj) { try { return JSON.stringify(obj, null, " "); } catch (e) { return String(obj); } } var h$1 = virtualHyperscript; var h_1 = h$1; var createElement = createElement_1$1; var createElement_1 = createElement; var diff = diff_1; var patch = patch_1; var h = h_1; var create = createElement_1; var VNode = vnode; var VText = vtext; var virtualDom = { diff: diff, patch: patch, h: h, create: create, VNode: VNode, VText: VText }; class EventEmitter { constructor() { this._events = {}; } /** * @ignore */ fire(type, event) { if (!this._listens(type)) { return; } for (const handler of this._events[type]) { handler(event); } } /** * Unsubscribe from an event by its name. * @param {string} type - The name of the event * to unsubscribe from. * @param {(event: T) => void} handler - The * handler to remove. */ off(type, handler) { if (!type) { this._events = {}; return; } if (this._listens(type)) { const index = this._events[type].indexOf(handler); if (index >= 0) { this._events[type].splice(index, 1); } if (!this._events[type].length) { delete this._events[type]; } } } /** * Subscribe to an event by its name. * @param {string} type - The name of the event * to subscribe to. * @param {(event: T) => void} handler - The * handler called when the event occurs. */ on(type, handler) { this._events[type] = this._events[type] || []; this._events[type].push(handler); } _listens(eventType) { return eventType in this._events; } } class SubscriptionHolder { constructor() { this._subscriptions = []; } push(subscription) { this._subscriptions.push(subscription); } unsubscribe() { for (const sub of this._subscriptions) { sub.unsubscribe(); } this._subscriptions = []; } } class Component extends EventEmitter { constructor(name, container, navigator) { super(); this._activated$ = new BehaviorSubject(false); this._configurationSubject$ = new Subject(); this._activated = false; this._container = container; this._name = name; this._navigator = navigator; this._subscriptions = new SubscriptionHolder(); this._configuration$ = this._configurationSubject$.pipe(startWith(this.defaultConfiguration), scan((conf, newConf) => { for (let key in newConf) { if (newConf.hasOwnProperty(key)) { conf[key] = newConf[key]; } } return conf; }), publishReplay(1), refCount()); this._configuration$.subscribe(() => { }); } /** * Get activated. * * @returns {boolean} Value indicating if the component is * currently active. */ get activated() { return this._activated; } /** @ignore */ get activated$() { return this._activated$; } /** * Get default configuration. * * @returns {TConfiguration} Default configuration for component. */ get defaultConfiguration() { return this._getDefaultConfiguration(); } /** @ignore */ get configuration$() { return this._configuration$; } /** * Get name. * * @description The name of the component. Used when interacting with the * component through the Viewer"s API. */ get name() { return this._name; } /** @ignore */ activate(conf) { if (this._activated) { return; } if (conf !== undefined) { this._configurationSubject$.next(conf); } this._activated = true; this._activate(); this._activated$.next(true); } /** * Configure the component. * * @param configuration Component configuration. */ configure(configuration) { this._configurationSubject$.next(configuration); } /** @ignore */ deactivate() { if (!this._activated) { return; } this._activated = false; this._deactivate(); this._container.domRenderer.clear(this._name); this._container.glRenderer.clear(this._name); this._activated$.next(false); } /** @inheritdoc */ fire(type, event) { super.fire(type, event); } /** @inheritdoc */ off(type, handler) { super.off(type, handler); } /** @inheritdoc */ on(type, handler) { super.on(type, handler); } /** * Detect the viewer"s new width and height and resize the component"s * rendered elements accordingly if applicable. * * @ignore */ resize() { return; } } var CoverState; (function (CoverState) { CoverState[CoverState["Hidden"] = 0] = "Hidden"; CoverState[CoverState["Loading"] = 1] = "Loading"; CoverState[CoverState["Visible"] = 2] = "Visible"; })(CoverState || (CoverState = {})); class CoverComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); } _activate() { const originalSrc$ = this.configuration$.pipe(first((c) => { return !!c.id; }), filter((c) => { return !c.src; }), switchMap((c) => { return this._getImageSrc$(c.id).pipe(catchError((error) => { console.error(error); return empty(); })); }), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(originalSrc$.pipe(map((src) => { return { src: src }; })) .subscribe((c) => { this._configurationSubject$.next(c); })); subs.push(combineLatest(this.configuration$, originalSrc$).pipe(filter(([c, src]) => { return !!c.src && c.src !== src; }), first()) .subscribe(([, src]) => { window.URL.revokeObjectURL(src); })); subs.push(this._configuration$.pipe(distinctUntilChanged(undefined, (configuration) => { return configuration.state; }), switchMap((configuration) => { return combineLatest(of(configuration.state), this._navigator.stateService.currentImage$); }), switchMap(([state, image]) => { const keySrc$ = combineLatest(of(image.id), image.image$.pipe(filter((imageElement) => { return !!imageElement; }), map((imageElement) => { return imageElement.src; }))); return state === CoverState.Visible ? keySrc$.pipe(first()) : keySrc$; }), distinctUntilChanged(([k1, s1], [k2, s2]) => { return k1 === k2 && s1 === s2; }), map(([key, src]) => { return { id: key, src: src }; })) .subscribe(this._configurationSubject$)); subs.push(combineLatest(this._configuration$, this._container.configurationService.exploreUrl$, this._container.renderService.size$).pipe(map(([configuration, exploreUrl, size]) => { if (!configuration.src) { return { name: this._name, vNode: virtualDom.h("div", []) }; } const compactClass = size.width <= 640 || size.height <= 480 ? ".mapillary-cover-compact" : ""; if (configuration.state === CoverState.Hidden) { const doneContainer = virtualDom.h("div.mapillary-cover-container.mapillary-cover-done" + compactClass, [this._getCoverBackgroundVNode(configuration)]); return { name: this._name, vNode: doneContainer }; } const container = virtualDom.h("div.mapillary-cover-container" + compactClass, [this._getCoverButtonVNode(configuration, exploreUrl)]); return { name: this._name, vNode: container }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { state: CoverState.Visible }; } _getCoverButtonVNode(configuration, exploreUrl) { const cover = configuration.state === CoverState.Loading ? "div.mapillary-cover.mapillary-cover-loading" : "div.mapillary-cover"; const coverButton = virtualDom.h("div.mapillary-cover-button", [virtualDom.h("div.mapillary-cover-button-icon", [])]); const coverLogo = virtualDom.h("a.mapillary-cover-logo", { href: exploreUrl, target: "_blank" }, []); const coverIndicator = virtualDom.h("div.mapillary-cover-indicator", { onclick: () => { this.configure({ state: CoverState.Loading }); } }, []); return virtualDom.h(cover, [ this._getCoverBackgroundVNode(configuration), coverIndicator, coverButton, coverLogo, ]); } _getCoverBackgroundVNode(conf) { const properties = { style: { backgroundImage: `url(${conf.src})` }, }; const children = []; if (conf.state === CoverState.Loading) { children.push(virtualDom.h("div.mapillary-cover-spinner", {}, [])); } return virtualDom.h("div.mapillary-cover-background", properties, children); } _getImageSrc$(id) { return Observable.create((subscriber) => { this._navigator.api.getImages$([id]) .subscribe((items) => { for (const item of items) { const imageId = typeof id === "number" ? id.toString() : id; if (item.node_id !== imageId) { continue; } this._navigator.api.data .getImageBuffer(item.node.thumb.url) .then((buffer) => { const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { subscriber.next(image.src); subscriber.complete(); }; image.onerror = () => { subscriber.error(new Error(`Failed to load cover ` + `image (${id})`)); }; const blob = new Blob([buffer]); image.src = window.URL .createObjectURL(blob); }, (error) => { subscriber.error(error); }); return; } subscriber.error(new MapillaryError(`Non existent cover key: ${id}`)); }, (error) => { subscriber.error(error); }); }); } } CoverComponent.componentName = "cover"; class AttributionComponent extends Component { _activate() { this._subscriptions.push(combineLatest(this._container.configurationService.exploreUrl$, this._navigator.stateService.currentImage$, this._container.renderService.size$).pipe(map(([exploreUrl, image, size]) => { const attribution = this._makeAttribution(image.creatorUsername, exploreUrl, image.id, image.capturedAt, size.width); return { name: this._name, vNode: attribution, }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } makeImageUrl(exploreUrl, id) { return `${exploreUrl}/app/?pKey=${id}&focus=photo`; } _makeAttribution(creatorUsername, exploreUrl, imageId, capturedAt, viewportWidth) { const compact = viewportWidth <= 640; const date = this._makeDate(capturedAt, compact); const by = this._makeBy(creatorUsername, exploreUrl, imageId, compact); const compactClass = compact ? ".mapillary-attribution-compact" : ""; return virtualDom.h("div.mapillary-attribution-container" + compactClass, {}, [...by, date]); } _makeBy(creatorUsername, exploreUrl, imageId, compact) { const icon = virtualDom.h("div.mapillary-attribution-logo", []); return creatorUsername ? this._makeCreatorBy(icon, creatorUsername, exploreUrl, imageId, compact) : this._makeGeneralBy(icon, exploreUrl, imageId, compact); } _makeCreatorBy(icon, creatorUsername, exploreUrl, imageId, compact) { const mapillary = virtualDom.h("a.mapillary-attribution-icon-container", { href: exploreUrl, rel: "noreferrer", target: "_blank" }, [icon]); const content = compact ? `${creatorUsername}` : `image by ${creatorUsername}`; const imageBy = virtualDom.h("div.mapillary-attribution-username", { textContent: content }, []); const image = virtualDom.h("a.mapillary-attribution-image-container", { href: this.makeImageUrl(exploreUrl, imageId), rel: "noreferrer", target: "_blank", }, [imageBy]); return [mapillary, image]; } _makeGeneralBy(icon, exploreUrl, imageId, compact) { const imagesBy = virtualDom.h("div.mapillary-attribution-username", { textContent: "images by" }, []); const mapillary = virtualDom.h("div.mapillary-attribution-icon-container", {}, [icon]); const contributors = virtualDom.h("div.mapillary-attribution-username", { textContent: "contributors" }, []); const children = [mapillary, contributors]; if (!compact) { children.unshift(imagesBy); } const image = virtualDom.h("a.mapillary-attribution-image-container", { href: this.makeImageUrl(exploreUrl, imageId), rel: "noreferrer", target: "_blank", }, children); return [image]; } _makeDate(capturedAt, compact) { const date = new Date(capturedAt) .toDateString() .split(" "); const formatted = (date.length > 3 ? compact ? [date[3]] : [date[1], date[2] + ",", date[3]] : date).join(" "); return virtualDom.h("div.mapillary-attribution-date", { textContent: formatted }, []); } } AttributionComponent.componentName = "attribution"; /** * @class ViewportCoords * * @classdesc Provides methods for calculating 2D coordinate conversions * as well as 3D projection and unprojection. * * Basic coordinates are 2D coordinates on the [0, 1] interval and * have the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * Viewport coordinates are 2D coordinates on the [-1, 1] interval and * have the origin point in the center. The bottom left corner point is * (-1, -1) and the top right corner point is (1, 1). * * Canvas coordiantes are 2D pixel coordinates on the [0, canvasWidth] and * [0, canvasHeight] intervals. The origin point (0, 0) is in the top left * corner and the maximum value is (canvasWidth, canvasHeight) is in the * bottom right corner. * * 3D coordinates are in the topocentric world reference frame. */ class ViewportCoords { constructor() { this._unprojectDepth = 200; } /** * Convert basic coordinates to canvas coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ basicToCanvas(basicX, basicY, container, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const canvas = this.projectToCanvas(point3d, container, camera); return canvas; } /** * Convert basic coordinates to canvas coordinates safely. If 3D point is * behind camera null will be returned. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates if the basic point represents a 3D point * in front of the camera, otherwise null. */ basicToCanvasSafe(basicX, basicY, container, transform, camera) { const viewport = this.basicToViewportSafe(basicX, basicY, transform, camera); if (viewport === null) { return null; } const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Convert basic coordinates to viewport coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ basicToViewport(basicX, basicY, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const viewport = this.projectToViewport(point3d, camera); return viewport; } /** * Convert basic coordinates to viewport coordinates safely. If 3D point is * behind camera null will be returned. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ basicToViewportSafe(basicX, basicY, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const pointCamera = this.worldToCamera(point3d, camera); if (pointCamera[2] > 0) { return null; } const viewport = this.projectToViewport(point3d, camera); return viewport; } /** * Convert camera 3D coordinates to viewport coordinates. * * @param {number} pointCamera - 3D point in camera coordinate system. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ cameraToViewport(pointCamera, camera) { const viewport = new Vector3().fromArray(pointCamera) .applyMatrix4(camera.projectionMatrix); return [viewport.x, viewport.y]; } /** * Get canvas pixel position from event. * * @param {Event} event - Event containing clientX and clientY properties. * @param {HTMLElement} element - HTML element. * @returns {Array} 2D canvas coordinates. */ canvasPosition(event, element) { const clientRect = element.getBoundingClientRect(); const canvasX = event.clientX - clientRect.left - element.clientLeft; const canvasY = event.clientY - clientRect.top - element.clientTop; return [canvasX, canvasY]; } /** * Convert canvas coordinates to basic coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D basic coordinates. */ canvasToBasic(canvasX, canvasY, container, transform, camera) { const point3d = this.unprojectFromCanvas(canvasX, canvasY, container, camera) .toArray(); const basic = transform.projectBasic(point3d); return basic; } /** * Convert canvas coordinates to viewport coordinates. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D viewport coordinates. */ canvasToViewport(canvasX, canvasY, container) { const [canvasWidth, canvasHeight] = this.containerToCanvas(container); const viewportX = 2 * canvasX / canvasWidth - 1; const viewportY = 1 - 2 * canvasY / canvasHeight; return [viewportX, viewportY]; } /** * Determines the width and height of the container in canvas coordinates. * * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D canvas coordinates. */ containerToCanvas(container) { return [container.offsetWidth, container.offsetHeight]; } /** * Determine basic distances from image to canvas corners. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * Determines the smallest basic distance for every side of the canvas. * * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} Array of basic distances as [top, right, bottom, left]. */ getBasicDistances(transform, camera) { const topLeftBasic = this.viewportToBasic(-1, 1, transform, camera); const topRightBasic = this.viewportToBasic(1, 1, transform, camera); const bottomRightBasic = this.viewportToBasic(1, -1, transform, camera); const bottomLeftBasic = this.viewportToBasic(-1, -1, transform, camera); let topBasicDistance = 0; let rightBasicDistance = 0; let bottomBasicDistance = 0; let leftBasicDistance = 0; if (topLeftBasic[1] < 0 && topRightBasic[1] < 0) { topBasicDistance = topLeftBasic[1] > topRightBasic[1] ? -topLeftBasic[1] : -topRightBasic[1]; } if (topRightBasic[0] > 1 && bottomRightBasic[0] > 1) { rightBasicDistance = topRightBasic[0] < bottomRightBasic[0] ? topRightBasic[0] - 1 : bottomRightBasic[0] - 1; } if (bottomRightBasic[1] > 1 && bottomLeftBasic[1] > 1) { bottomBasicDistance = bottomRightBasic[1] < bottomLeftBasic[1] ? bottomRightBasic[1] - 1 : bottomLeftBasic[1] - 1; } if (bottomLeftBasic[0] < 0 && topLeftBasic[0] < 0) { leftBasicDistance = bottomLeftBasic[0] > topLeftBasic[0] ? -bottomLeftBasic[0] : -topLeftBasic[0]; } return [topBasicDistance, rightBasicDistance, bottomBasicDistance, leftBasicDistance]; } /** * Determine pixel distances from image to canvas corners. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * Determines the smallest pixel distance for every side of the canvas. * * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} Array of pixel distances as [top, right, bottom, left]. */ getPixelDistances(container, transform, camera) { const topLeftBasic = this.viewportToBasic(-1, 1, transform, camera); const topRightBasic = this.viewportToBasic(1, 1, transform, camera); const bottomRightBasic = this.viewportToBasic(1, -1, transform, camera); const bottomLeftBasic = this.viewportToBasic(-1, -1, transform, camera); let topPixelDistance = 0; let rightPixelDistance = 0; let bottomPixelDistance = 0; let leftPixelDistance = 0; const [canvasWidth, canvasHeight] = this.containerToCanvas(container); if (topLeftBasic[1] < 0 && topRightBasic[1] < 0) { const basicX = topLeftBasic[1] > topRightBasic[1] ? topLeftBasic[0] : topRightBasic[0]; const canvas = this.basicToCanvas(basicX, 0, container, transform, camera); topPixelDistance = canvas[1] > 0 ? canvas[1] : 0; } if (topRightBasic[0] > 1 && bottomRightBasic[0] > 1) { const basicY = topRightBasic[0] < bottomRightBasic[0] ? topRightBasic[1] : bottomRightBasic[1]; const canvas = this.basicToCanvas(1, basicY, container, transform, camera); rightPixelDistance = canvas[0] < canvasWidth ? canvasWidth - canvas[0] : 0; } if (bottomRightBasic[1] > 1 && bottomLeftBasic[1] > 1) { const basicX = bottomRightBasic[1] < bottomLeftBasic[1] ? bottomRightBasic[0] : bottomLeftBasic[0]; const canvas = this.basicToCanvas(basicX, 1, container, transform, camera); bottomPixelDistance = canvas[1] < canvasHeight ? canvasHeight - canvas[1] : 0; } if (bottomLeftBasic[0] < 0 && topLeftBasic[0] < 0) { const basicY = bottomLeftBasic[0] > topLeftBasic[0] ? bottomLeftBasic[1] : topLeftBasic[1]; const canvas = this.basicToCanvas(0, basicY, container, transform, camera); leftPixelDistance = canvas[0] > 0 ? canvas[0] : 0; } return [topPixelDistance, rightPixelDistance, bottomPixelDistance, leftPixelDistance]; } /** * Determine if an event occured inside an element. * * @param {Event} event - Event containing clientX and clientY properties. * @param {HTMLElement} element - HTML element. * @returns {boolean} Value indicating if the event occured inside the element or not. */ insideElement(event, element) { const clientRect = element.getBoundingClientRect(); const minX = clientRect.left + element.clientLeft; const maxX = minX + element.clientWidth; const minY = clientRect.top + element.clientTop; const maxY = minY + element.clientHeight; return event.clientX > minX && event.clientX < maxX && event.clientY > minY && event.clientY < maxY; } /** * Project 3D world coordinates to canvas coordinates. * * @param {Array} point3D - 3D world coordinates. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ projectToCanvas(point3d, container, camera) { const viewport = this.projectToViewport(point3d, camera); const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Project 3D world coordinates to canvas coordinates safely. If 3D * point is behind camera null will be returned. * * @param {Array} point3D - 3D world coordinates. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ projectToCanvasSafe(point3d, container, camera) { const pointCamera = this.worldToCamera(point3d, camera); if (pointCamera[2] > 0) { return null; } const viewport = this.projectToViewport(point3d, camera); const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Project 3D world coordinates to viewport coordinates. * * @param {Array} point3D - 3D world coordinates. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ projectToViewport(point3d, camera) { const viewport = new Vector3(point3d[0], point3d[1], point3d[2]) .project(camera); return [viewport.x, viewport.y]; } /** * Uproject canvas coordinates to 3D world coordinates. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D world coordinates. */ unprojectFromCanvas(canvasX, canvasY, container, camera) { const viewport = this.canvasToViewport(canvasX, canvasY, container); const point3d = this.unprojectFromViewport(viewport[0], viewport[1], camera); return point3d; } /** * Unproject viewport coordinates to 3D world coordinates. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D world coordinates. */ unprojectFromViewport(viewportX, viewportY, camera) { const point3d = new Vector3(viewportX, viewportY, 1) .unproject(camera); return point3d; } /** * Convert viewport coordinates to basic coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D basic coordinates. */ viewportToBasic(viewportX, viewportY, transform, camera) { const point3d = new Vector3(viewportX, viewportY, 1) .unproject(camera) .toArray(); const basic = transform.projectBasic(point3d); return basic; } /** * Convert viewport coordinates to canvas coordinates. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D canvas coordinates. */ viewportToCanvas(viewportX, viewportY, container) { const [canvasWidth, canvasHeight] = this.containerToCanvas(container); const canvasX = canvasWidth * (viewportX + 1) / 2; const canvasY = -canvasHeight * (viewportY - 1) / 2; return [canvasX, canvasY]; } /** * Convert 3D world coordinates to 3D camera coordinates. * * @param {number} point3D - 3D point in world coordinate system. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D camera coordinates. */ worldToCamera(point3d, camera) { const pointCamera = new Vector3(point3d[0], point3d[1], point3d[2]) .applyMatrix4(camera.matrixWorldInverse); return pointCamera.toArray(); } } /** * Enumeration for component size. * @enum {number} * @readonly * @description May be used by a component to allow for resizing * of the UI elements rendered by the component. */ exports.ComponentSize = void 0; (function (ComponentSize) { /** * Automatic size. The size of the elements will automatically * change at a predefined threshold. */ ComponentSize[ComponentSize["Automatic"] = 0] = "Automatic"; /** * Large size. The size of the elements will be fixed until another * component size is configured. */ ComponentSize[ComponentSize["Large"] = 1] = "Large"; /** * Small size. The size of the elements will be fixed until another * component size is configured. */ ComponentSize[ComponentSize["Small"] = 2] = "Small"; })(exports.ComponentSize || (exports.ComponentSize = {})); /** * @class BearingComponent * * @classdesc Component for indicating bearing and field of view. * * @example * ```js * var viewer = new Viewer({ ... }); * var bearingComponent = viewer.getComponent("bearing"); * bearingComponent.configure({ size: ComponentSize.Small }); * ``` */ class BearingComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._spatial = new Spatial(); this._viewportCoords = new ViewportCoords(); this._svgNamespace = "http://www.w3.org/2000/svg"; this._distinctThreshold = Math.PI / 360; this._animationSpeed = 0.075; } _activate() { const subs = this._subscriptions; const cameraBearingFov$ = this._container.renderService.renderCamera$.pipe(map((rc) => { let vFov = this._spatial.degToRad(rc.perspective.fov); let hFov = rc.perspective.aspect === Number.POSITIVE_INFINITY ? Math.PI : Math.atan(rc.perspective.aspect * Math.tan(0.5 * vFov)) * 2; return [this._spatial.azimuthalToBearing(rc.rotation.phi), hFov]; }), distinctUntilChanged((a1, a2) => { return Math.abs(a2[0] - a1[0]) < this._distinctThreshold && Math.abs(a2[1] - a1[1]) < this._distinctThreshold; })); const imageFov$ = combineLatest(this._navigator.stateService.currentState$.pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; })), this._navigator.panService.panImages$).pipe(map(([frame, panImages]) => { const image = frame.state.currentImage; const transform = frame.state.currentTransform; if (isSpherical(image.cameraType)) { return [Math.PI, Math.PI]; } const hFov = this._computeHorizontalFov(transform); let hFovLeft = hFov / 2; let hFovRight = hFov / 2; for (const [n, , f] of panImages) { const diff = this._spatial.wrap(n.compassAngle - image.compassAngle, -180, 180); if (diff < 0) { hFovLeft = this._spatial.degToRad(Math.abs(diff)) + f / 2; } else { hFovRight = this._spatial.degToRad(Math.abs(diff)) + f / 2; } } return [hFovLeft, hFovRight]; }), distinctUntilChanged(([hFovLeft1, hFovRight1], [hFovLeft2, hFovRight2]) => { return Math.abs(hFovLeft2 - hFovLeft1) < this._distinctThreshold && Math.abs(hFovRight2 - hFovRight1) < this._distinctThreshold; })); const offset$ = combineLatest(this._navigator.stateService.currentState$.pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; })), this._container.renderService.bearing$).pipe(map(([frame, bearing]) => { const offset = this._spatial.degToRad(frame.state.currentImage.compassAngle - bearing); return offset; })); const imageFovOperation$ = new Subject(); const smoothImageFov$ = imageFovOperation$.pipe(scan((state, operation) => { return operation(state); }, { alpha: 0, curr: [0, 0, 0], prev: [0, 0, 0] }), map((state) => { const alpha = MathUtils.smootherstep(state.alpha, 0, 1); const curr = state.curr; const prev = state.prev; return [ this._interpolate(prev[0], curr[0], alpha), this._interpolate(prev[1], curr[1], alpha), ]; })); subs.push(imageFov$.pipe(map((nbf) => { return (state) => { const a = MathUtils.smootherstep(state.alpha, 0, 1); const c = state.curr; const p = state.prev; const prev = [ this._interpolate(p[0], c[0], a), this._interpolate(p[1], c[1], a), ]; const curr = nbf.slice(); return { alpha: 0, curr: curr, prev: prev, }; }; })) .subscribe(imageFovOperation$)); subs.push(imageFov$.pipe(switchMap(() => { return this._container.renderService.renderCameraFrame$.pipe(skip(1), scan((alpha) => { return alpha + this._animationSpeed; }, 0), takeWhile((alpha) => { return alpha <= 1 + this._animationSpeed; }), map((alpha) => { return Math.min(alpha, 1); })); }), map((alpha) => { return (nbfState) => { return { alpha: alpha, curr: nbfState.curr.slice(), prev: nbfState.prev.slice(), }; }; })) .subscribe(imageFovOperation$)); const imageBearingFov$ = combineLatest(offset$, smoothImageFov$).pipe(map(([offset, fov]) => { return [offset, fov[0], fov[1]]; })); subs.push(combineLatest(cameraBearingFov$, imageBearingFov$, this._configuration$, this._container.renderService.size$).pipe(map(([[cb, cf], [no, nfl, nfr], configuration, size]) => { const background = this._createBackground(cb); const fovIndicator = this._createFovIndicator(nfl, nfr, no); const north = this._createNorth(cb); const cameraSector = this._createCircleSectorCompass(this._createCircleSector(Math.max(Math.PI / 20, cf), "#FFF")); const compact = configuration.size === exports.ComponentSize.Small || configuration.size === exports.ComponentSize.Automatic && size.width < 640 ? ".mapillary-bearing-compact" : ""; return { name: this._name, vNode: virtualDom.h("div.mapillary-bearing-indicator-container" + compact, { oncontextmenu: (event) => { event.preventDefault(); } }, [ background, fovIndicator, north, cameraSector, ]), }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { size: exports.ComponentSize.Automatic }; } _createFovIndicator(fovLeft, fovRigth, offset) { const arc = this._createFovArc(fovLeft, fovRigth); const group = virtualDom.h("g", { attributes: { transform: "translate(18,18)" }, namespace: this._svgNamespace, }, [arc]); const svg = virtualDom.h("svg", { attributes: { viewBox: "0 0 36 36" }, namespace: this._svgNamespace, style: { height: "36px", left: "2px", position: "absolute", top: "2px", transform: `rotateZ(${this._spatial.radToDeg(offset)}deg)`, width: "36px", }, }, [group]); return svg; } _createFovArc(fovLeft, fovRigth) { const radius = 16.75; const strokeWidth = 2.5; const fov = fovLeft + fovRigth; if (fov > 2 * Math.PI - Math.PI / 90) { return virtualDom.h("circle", { attributes: { cx: "0", cy: "0", "fill-opacity": "0", r: `${radius}`, stroke: "#FFF", "stroke-width": `${strokeWidth}`, }, namespace: this._svgNamespace, }, []); } let arcStart = -Math.PI / 2 - fovLeft; let arcEnd = arcStart + fov; let startX = radius * Math.cos(arcStart); let startY = radius * Math.sin(arcStart); let endX = radius * Math.cos(arcEnd); let endY = radius * Math.sin(arcEnd); let largeArc = fov >= Math.PI ? 1 : 0; let description = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArc} 1 ${endX} ${endY}`; return virtualDom.h("path", { attributes: { d: description, "fill-opacity": "0", stroke: "#FFF", "stroke-width": `${strokeWidth}`, }, namespace: this._svgNamespace, }, []); } _createCircleSectorCompass(cameraSector) { let group = virtualDom.h("g", { attributes: { transform: "translate(1,1)" }, namespace: this._svgNamespace, }, [cameraSector]); let svg = virtualDom.h("svg", { attributes: { viewBox: "0 0 2 2" }, namespace: this._svgNamespace, style: { height: "26px", left: "7px", position: "absolute", top: "7px", width: "26px", }, }, [group]); return svg; } _createCircleSector(fov, fill) { if (fov > 2 * Math.PI - Math.PI / 90) { return virtualDom.h("circle", { attributes: { cx: "0", cy: "0", fill: fill, r: "1" }, namespace: this._svgNamespace, }, []); } let arcStart = -Math.PI / 2 - fov / 2; let arcEnd = arcStart + fov; let startX = Math.cos(arcStart); let startY = Math.sin(arcStart); let endX = Math.cos(arcEnd); let endY = Math.sin(arcEnd); let largeArc = fov >= Math.PI ? 1 : 0; let description = `M 0 0 ${startX} ${startY} A 1 1 0 ${largeArc} 1 ${endX} ${endY}`; return virtualDom.h("path", { attributes: { d: description, fill: fill }, namespace: this._svgNamespace, }, []); } _createNorth(bearing) { const north = virtualDom.h("div.mapillary-bearing-north", []); const container = virtualDom.h("div.mapillary-bearing-north-container", { style: { transform: `rotateZ(${this._spatial.radToDeg(-bearing)}deg)` } }, [north]); return container; } _createBackground(bearing) { return virtualDom.h("div.mapillary-bearing-indicator-background", { style: { transform: `rotateZ(${this._spatial.radToDeg(-bearing)}deg)` } }, [ virtualDom.h("div.mapillary-bearing-indicator-background-circle", []), virtualDom.h("div.mapillary-bearing-indicator-background-arrow-container", [ virtualDom.h("div.mapillary-bearing-indicator-background-arrow", []), ]), ]); } _computeHorizontalFov(transform) { const vertices = [[1, 0]]; const directions = [[0, 0.5]]; const pointsPerLine = 12; const bearings = computeBearings(transform, vertices, directions, pointsPerLine, this._viewportCoords); const projections = bearings .map(b => this._spatial.projectToPlane(b, [0, 1, 0])) .map(p => [p[0], -p[2]]); const angles = projections.map(p => Math.abs(Math.atan2(p[0], p[1]))); const fov = 2 * Math.min(...angles); return fov; } _interpolate(x1, x2, alpha) { return (1 - alpha) * x1 + alpha * x2; } } BearingComponent.componentName = "bearing"; class CacheComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); } _activate() { const subs = this._subscriptions; subs.push(combineLatest(this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; }), filter((status) => { return status.cached; })), this._configuration$).pipe(switchMap((nc) => { let status = nc[0]; let configuration = nc[1]; let sequenceDepth = Math.max(0, Math.min(4, configuration.depth.sequence)); let next$ = this._cache$(status.edges, exports.NavigationDirection.Next, sequenceDepth); let prev$ = this._cache$(status.edges, exports.NavigationDirection.Prev, sequenceDepth); return merge(next$, prev$).pipe(catchError((error) => { console.error("Failed to cache sequence edges.", error); return empty(); })); })) .subscribe(() => { })); subs.push(combineLatest(this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return combineLatest(of(image), image.spatialEdges$.pipe(filter((status) => { return status.cached; }))); })), this._configuration$).pipe(switchMap(([[image, edgeStatus], configuration]) => { let edges = edgeStatus.edges; let depth = configuration.depth; let sphericalDepth = Math.max(0, Math.min(2, depth.spherical)); let stepDepth = isSpherical(image.cameraType) ? 0 : Math.max(0, Math.min(3, depth.step)); let turnDepth = isSpherical(image.cameraType) ? 0 : Math.max(0, Math.min(1, depth.turn)); let spherical$ = this._cache$(edges, exports.NavigationDirection.Spherical, sphericalDepth); let forward$ = this._cache$(edges, exports.NavigationDirection.StepForward, stepDepth); let backward$ = this._cache$(edges, exports.NavigationDirection.StepBackward, stepDepth); let left$ = this._cache$(edges, exports.NavigationDirection.StepLeft, stepDepth); let right$ = this._cache$(edges, exports.NavigationDirection.StepRight, stepDepth); let turnLeft$ = this._cache$(edges, exports.NavigationDirection.TurnLeft, turnDepth); let turnRight$ = this._cache$(edges, exports.NavigationDirection.TurnRight, turnDepth); let turnU$ = this._cache$(edges, exports.NavigationDirection.TurnU, turnDepth); return merge(forward$, backward$, left$, right$, spherical$, turnLeft$, turnRight$, turnU$).pipe(catchError((error) => { console.error("Failed to cache spatial edges.", error); return empty(); })); })) .subscribe(() => { })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { depth: { spherical: 1, sequence: 2, step: 1, turn: 0 } }; } _cache$(edges, direction, depth) { return zip(of(edges), of(depth)).pipe(expand((ed) => { let es = ed[0]; let d = ed[1]; let edgesDepths$ = []; if (d > 0) { for (let edge of es) { if (edge.data.direction === direction) { edgesDepths$.push(zip(this._navigator.graphService.cacheImage$(edge.target).pipe(mergeMap((n) => { return this._imageToEdges$(n, direction); })), of(d - 1))); } } } return from(edgesDepths$).pipe(mergeAll()); }), skip(1)); } _imageToEdges$(image, direction) { return ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), map((status) => { return status.edges; })); } } CacheComponent.componentName = "cache"; /** * @class DirectionDOMCalculator * @classdesc Helper class for calculating DOM CSS properties. */ class DirectionDOMCalculator { constructor(configuration, size) { this._spatial = new Spatial(); this._minThresholdWidth = 320; this._maxThresholdWidth = 1480; this._minThresholdHeight = 240; this._maxThresholdHeight = 820; this._configure(configuration); this._resize(size); this._reset(); } get minWidth() { return this._minWidth; } get maxWidth() { return this._maxWidth; } get containerWidth() { return this._containerWidth; } get containerWidthCss() { return this._containerWidthCss; } get containerMarginCss() { return this._containerMarginCss; } get containerLeftCss() { return this._containerLeftCss; } get containerHeight() { return this._containerHeight; } get containerHeightCss() { return this._containerHeightCss; } get containerBottomCss() { return this._containerBottomCss; } get stepCircleSize() { return this._stepCircleSize; } get stepCircleSizeCss() { return this._stepCircleSizeCss; } get stepCircleMarginCss() { return this._stepCircleMarginCss; } get turnCircleSize() { return this._turnCircleSize; } get turnCircleSizeCss() { return this._turnCircleSizeCss; } get outerRadius() { return this._outerRadius; } get innerRadius() { return this._innerRadius; } get shadowOffset() { return this._shadowOffset; } /** * Configures the min and max width values. * * @param {DirectionConfiguration} configuration Configuration * with min and max width values. */ configure(configuration) { this._configure(configuration); this._reset(); } /** * Resizes all properties according to the width and height * of the size object. * * @param {ViewportSize} size The size of the container element. */ resize(size) { this._resize(size); this._reset(); } /** * Calculates the coordinates on the unit circle for an angle. * * @param {number} angle Angle in radians. * @returns {Array} The x and y coordinates on the unit circle. */ angleToCoordinates(angle) { return [Math.cos(angle), Math.sin(angle)]; } /** * Calculates the coordinates on the unit circle for the * relative angle between the first and second angle. * * @param {number} first Angle in radians. * @param {number} second Angle in radians. * @returns {Array} The x and y coordinates on the unit circle * for the relative angle between the first and second angle. */ relativeAngleToCoordiantes(first, second) { let relativeAngle = this._spatial.wrapAngle(first - second); return this.angleToCoordinates(relativeAngle); } _configure(configuration) { this._minWidth = configuration.minWidth; this._maxWidth = this._getMaxWidth(configuration.minWidth, configuration.maxWidth); } _resize(size) { this._elementWidth = size.width; this._elementHeight = size.height; } _reset() { this._containerWidth = this._getContainerWidth(this._elementWidth, this._elementHeight); this._containerHeight = this._getContainerHeight(this.containerWidth); this._stepCircleSize = this._getStepCircleDiameter(this._containerHeight); this._turnCircleSize = this._getTurnCircleDiameter(this.containerHeight); this._outerRadius = this._getOuterRadius(this._containerHeight); this._innerRadius = this._getInnerRadius(this._containerHeight); this._shadowOffset = 3; this._containerWidthCss = this._numberToCssPixels(this._containerWidth); this._containerMarginCss = this._numberToCssPixels(-0.5 * this._containerWidth); this._containerLeftCss = this._numberToCssPixels(Math.floor(0.5 * this._elementWidth)); this._containerHeightCss = this._numberToCssPixels(this._containerHeight); this._containerBottomCss = this._numberToCssPixels(Math.floor(-0.08 * this._containerHeight)); this._stepCircleSizeCss = this._numberToCssPixels(this._stepCircleSize); this._stepCircleMarginCss = this._numberToCssPixels(-0.5 * this._stepCircleSize); this._turnCircleSizeCss = this._numberToCssPixels(this._turnCircleSize); } _getContainerWidth(elementWidth, elementHeight) { let relativeWidth = (elementWidth - this._minThresholdWidth) / (this._maxThresholdWidth - this._minThresholdWidth); let relativeHeight = (elementHeight - this._minThresholdHeight) / (this._maxThresholdHeight - this._minThresholdHeight); let coeff = Math.max(0, Math.min(1, Math.min(relativeWidth, relativeHeight))); coeff = 0.04 * Math.round(25 * coeff); return this._minWidth + coeff * (this._maxWidth - this._minWidth); } _getContainerHeight(containerWidth) { return 0.77 * containerWidth; } _getStepCircleDiameter(containerHeight) { return 0.34 * containerHeight; } _getTurnCircleDiameter(containerHeight) { return 0.3 * containerHeight; } _getOuterRadius(containerHeight) { return 0.31 * containerHeight; } _getInnerRadius(containerHeight) { return 0.125 * containerHeight; } _numberToCssPixels(value) { return value + "px"; } _getMaxWidth(value, minWidth) { return value > minWidth ? value : minWidth; } } /** * @class DirectionDOMRenderer * @classdesc DOM renderer for direction arrows. */ class DirectionDOMRenderer { constructor(configuration, size) { this._isEdge = false; this._spatial = new Spatial(); this._calculator = new DirectionDOMCalculator(configuration, size); this._image = null; this._rotation = { phi: 0, theta: 0 }; this._epsilon = 0.5 * Math.PI / 180; this._highlightKey = null; this._distinguishSequence = false; this._needsRender = false; this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; this._stepDirections = [ exports.NavigationDirection.StepForward, exports.NavigationDirection.StepBackward, exports.NavigationDirection.StepLeft, exports.NavigationDirection.StepRight, ]; this._turnDirections = [ exports.NavigationDirection.TurnLeft, exports.NavigationDirection.TurnRight, exports.NavigationDirection.TurnU, ]; this._turnNames = {}; this._turnNames[exports.NavigationDirection.TurnLeft] = "mapillary-direction-turn-left"; this._turnNames[exports.NavigationDirection.TurnRight] = "mapillary-direction-turn-right"; this._turnNames[exports.NavigationDirection.TurnU] = "mapillary-direction-turn-around"; // detects IE 8-11, then Edge 20+. let isIE = !!document.documentMode; this._isEdge = !isIE && !!window.StyleMedia; } /** * Get needs render. * * @returns {boolean} Value indicating whether render should be called. */ get needsRender() { return this._needsRender; } /** * Renders virtual DOM elements. * * @description Calling render resets the needs render property. */ render(navigator) { this._needsRender = false; let rotation = this._rotation; let steps = []; let turns = []; if (isSpherical(this._image.cameraType)) { steps = steps.concat(this._createSphericalArrows(navigator, rotation)); } else { steps = steps.concat(this._createPerspectiveToSphericalArrows(navigator, rotation)); steps = steps.concat(this._createStepArrows(navigator, rotation)); turns = turns.concat(this._createTurnArrows(navigator)); } return this._getContainer(steps, turns, rotation); } setEdges(edgeStatus, sequence) { this._setEdges(edgeStatus, sequence); this._setNeedsRender(); } /** * Set image for which to show edges. * * @param {Image} image */ setImage(image) { this._image = image; this._clearEdges(); this._setNeedsRender(); } /** * Set the render camera to use for calculating rotations. * * @param {RenderCamera} renderCamera */ setRenderCamera(renderCamera) { let rotation = renderCamera.rotation; if (Math.abs(rotation.phi - this._rotation.phi) < this._epsilon) { return; } this._rotation = rotation; this._setNeedsRender(); } /** * Set configuration values. * * @param {DirectionConfiguration} configuration */ setConfiguration(configuration) { let needsRender = false; if (this._highlightKey !== configuration.highlightId || this._distinguishSequence !== configuration.distinguishSequence) { this._highlightKey = configuration.highlightId; this._distinguishSequence = configuration.distinguishSequence; needsRender = true; } if (this._calculator.minWidth !== configuration.minWidth || this._calculator.maxWidth !== configuration.maxWidth) { this._calculator.configure(configuration); needsRender = true; } if (needsRender) { this._setNeedsRender(); } } /** * Detect the element"s width and height and resize * elements accordingly. * * @param {ViewportSize} size Size of vßiewer container element. */ resize(size) { this._calculator.resize(size); this._setNeedsRender(); } _setNeedsRender() { if (this._image != null) { this._needsRender = true; } } _clearEdges() { this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; } _setEdges(edgeStatus, sequence) { this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; for (let edge of edgeStatus.edges) { let direction = edge.data.direction; if (this._stepDirections.indexOf(direction) > -1) { this._stepEdges.push(edge); continue; } if (this._turnDirections.indexOf(direction) > -1) { this._turnEdges.push(edge); continue; } if (edge.data.direction === exports.NavigationDirection.Spherical) { this._sphericalEdges.push(edge); } } if (this._distinguishSequence && sequence != null) { let edges = this._sphericalEdges .concat(this._stepEdges) .concat(this._turnEdges); for (let edge of edges) { let edgeKey = edge.target; for (let sequenceKey of sequence.imageIds) { if (sequenceKey === edgeKey) { this._sequenceEdgeKeys.push(edgeKey); break; } } } } } _createSphericalArrows(navigator, rotation) { let arrows = []; for (let sphericalEdge of this._sphericalEdges) { arrows.push(this._createVNodeByKey(navigator, sphericalEdge.target, sphericalEdge.data.worldMotionAzimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-spherical")); } for (let stepEdge of this._stepEdges) { arrows.push(this._createSphericalToPerspectiveArrow(navigator, stepEdge.target, stepEdge.data.worldMotionAzimuth, rotation, stepEdge.data.direction)); } return arrows; } _createSphericalToPerspectiveArrow(navigator, key, azimuth, rotation, direction) { let threshold = Math.PI / 8; let relativePhi = rotation.phi; switch (direction) { case exports.NavigationDirection.StepBackward: relativePhi = rotation.phi - Math.PI; break; case exports.NavigationDirection.StepLeft: relativePhi = rotation.phi + Math.PI / 2; break; case exports.NavigationDirection.StepRight: relativePhi = rotation.phi - Math.PI / 2; break; } if (Math.abs(this._spatial.wrapAngle(azimuth - relativePhi)) < threshold) { return this._createVNodeByKey(navigator, key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-step"); } return this._createVNodeInactive(key, azimuth, rotation); } _createPerspectiveToSphericalArrows(navigator, rotation) { let arrows = []; for (let sphericalEdge of this._sphericalEdges) { arrows.push(this._createVNodeByKey(navigator, sphericalEdge.target, sphericalEdge.data.worldMotionAzimuth, rotation, this._calculator.innerRadius, "mapillary-direction-arrow-spherical", true)); } return arrows; } _createStepArrows(navigator, rotation) { let arrows = []; for (let stepEdge of this._stepEdges) { arrows.push(this._createVNodeByDirection(navigator, stepEdge.target, stepEdge.data.worldMotionAzimuth, rotation, stepEdge.data.direction)); } return arrows; } _createTurnArrows(navigator) { let turns = []; for (let turnEdge of this._turnEdges) { let direction = turnEdge.data.direction; let name = this._turnNames[direction]; turns.push(this._createVNodeByTurn(navigator, turnEdge.target, name, direction)); } return turns; } _createVNodeByKey(navigator, key, azimuth, rotation, offset, className, shiftVertically) { let onClick = (e) => { navigator.moveTo$(key) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; return this._createVNode(key, azimuth, rotation, offset, className, "mapillary-direction-circle", onClick, shiftVertically); } _createVNodeByDirection(navigator, key, azimuth, rotation, direction) { let onClick = (e) => { navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; return this._createVNode(key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-step", "mapillary-direction-circle", onClick); } _createVNodeByTurn(navigator, key, className, direction) { let onClick = (e) => { navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; let style = { height: this._calculator.turnCircleSizeCss, transform: "rotate(0)", width: this._calculator.turnCircleSizeCss, }; switch (direction) { case exports.NavigationDirection.TurnLeft: style.left = "5px"; style.top = "5px"; break; case exports.NavigationDirection.TurnRight: style.right = "5px"; style.top = "5px"; break; case exports.NavigationDirection.TurnU: style.left = "5px"; style.bottom = "5px"; break; } let circleProperties = { attributes: { "data-id": key, }, onclick: onClick, style: style, }; let circleClassName = "mapillary-direction-turn-circle"; if (this._sequenceEdgeKeys.indexOf(key) > -1) { circleClassName += "-sequence"; } if (this._highlightKey === key) { circleClassName += "-highlight"; } let turn = virtualDom.h(`div.${className}`, {}, []); return virtualDom.h("div." + circleClassName, circleProperties, [turn]); } _createVNodeInactive(key, azimuth, rotation) { return this._createVNode(key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-inactive", "mapillary-direction-circle-inactive"); } _createVNode(key, azimuth, rotation, radius, className, circleClassName, onClick, shiftVertically) { let translation = this._calculator.angleToCoordinates(azimuth - rotation.phi); // rotate 90 degrees clockwise and flip over X-axis let translationX = Math.round(-radius * translation[1] + 0.5 * this._calculator.containerWidth); let translationY = Math.round(-radius * translation[0] + 0.5 * this._calculator.containerHeight); let shadowTranslation = this._calculator.relativeAngleToCoordiantes(azimuth, rotation.phi); let shadowOffset = this._calculator.shadowOffset; let shadowTranslationX = -shadowOffset * shadowTranslation[1]; let shadowTranslationY = shadowOffset * shadowTranslation[0]; let filter = `drop-shadow(${shadowTranslationX}px ${shadowTranslationY}px 1px rgba(0,0,0,0.8))`; let properties = { style: { "-webkit-filter": filter, filter: filter, }, }; let chevron = virtualDom.h("div." + className, properties, []); let azimuthDeg = -this._spatial.radToDeg(azimuth - rotation.phi); let circleTransform = shiftVertically ? `translate(${translationX}px, ${translationY}px) rotate(${azimuthDeg}deg) translateZ(-0.01px)` : `translate(${translationX}px, ${translationY}px) rotate(${azimuthDeg}deg)`; let circleProperties = { attributes: { "data-id": key }, onclick: onClick, style: { height: this._calculator.stepCircleSizeCss, marginLeft: this._calculator.stepCircleMarginCss, marginTop: this._calculator.stepCircleMarginCss, transform: circleTransform, width: this._calculator.stepCircleSizeCss, }, }; if (this._sequenceEdgeKeys.indexOf(key) > -1) { circleClassName += "-sequence"; } if (this._highlightKey === key) { circleClassName += "-highlight"; } return virtualDom.h("div." + circleClassName, circleProperties, [chevron]); } _getContainer(steps, turns, rotation) { // edge does not handle hover on perspective transforms. let transform = this._isEdge ? "rotateX(60deg)" : `perspective(${this._calculator.containerWidthCss}) rotateX(60deg)`; let properties = { oncontextmenu: (event) => { event.preventDefault(); }, style: { bottom: this._calculator.containerBottomCss, height: this._calculator.containerHeightCss, left: this._calculator.containerLeftCss, marginLeft: this._calculator.containerMarginCss, transform: transform, width: this._calculator.containerWidthCss, }, }; return virtualDom.h("div.mapillary-direction-perspective", properties, turns.concat(steps)); } } /** * @class DirectionComponent * @classdesc Component showing navigation arrows for steps and turns. */ class DirectionComponent extends Component { /** @ignore */ constructor(name, container, navigator, directionDOMRenderer) { super(name, container, navigator); this._renderer = !!directionDOMRenderer ? directionDOMRenderer : new DirectionDOMRenderer(this.defaultConfiguration, { height: container.container.offsetHeight, width: container.container.offsetWidth }); this._hoveredIdSubject$ = new Subject(); this._hoveredId$ = this._hoveredIdSubject$.pipe(share()); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } _activate() { const subs = this._subscriptions; subs.push(this._configuration$ .subscribe((configuration) => { this._renderer.setConfiguration(configuration); })); subs.push(this._container.renderService.size$ .subscribe((size) => { this._renderer.resize(size); })); subs.push(this._navigator.stateService.currentImage$.pipe(tap((image) => { this._container.domRenderer.render$.next({ name: this._name, vNode: virtualDom.h("div", {}, []) }); this._renderer.setImage(image); }), withLatestFrom(this._configuration$), switchMap(([image, configuration]) => { return combineLatest(image.spatialEdges$, configuration.distinguishSequence ? this._navigator.graphService .cacheSequence$(image.sequenceId).pipe(catchError((error) => { console.error(`Failed to cache sequence (${image.sequenceId})`, error); return of(null); })) : of(null)); })) .subscribe(([edgeStatus, sequence]) => { this._renderer.setEdges(edgeStatus, sequence); })); subs.push(this._container.renderService.renderCameraFrame$.pipe(tap((renderCamera) => { this._renderer.setRenderCamera(renderCamera); }), map(() => { return this._renderer; }), filter((renderer) => { return renderer.needsRender; }), map((renderer) => { return { name: this._name, vNode: renderer.render(this._navigator) }; })) .subscribe(this._container.domRenderer.render$)); subs.push(combineLatest(this._container.domRenderer.element$, this._container.renderService.renderCamera$, this._container.mouseService.mouseMove$.pipe(startWith(null)), this._container.mouseService.mouseUp$.pipe(startWith(null))).pipe(map(([element]) => { let elements = element.getElementsByClassName("mapillary-direction-perspective"); for (let i = 0; i < elements.length; i++) { let hovered = elements.item(i).querySelector(":hover"); if (hovered != null && hovered.hasAttribute("data-id")) { return hovered.getAttribute("data-id"); } } return null; }), distinctUntilChanged()) .subscribe(this._hoveredIdSubject$)); subs.push(this._hoveredId$ .subscribe((id) => { const type = "hover"; const event = { id, target: this, type, }; this.fire(type, event); })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { distinguishSequence: false, maxWidth: 460, minWidth: 260, }; } } /** @inheritdoc */ DirectionComponent.componentName = "direction"; var common$1 = /* glsl */ ` #define PI 3.141592653589793 #define PI2 6.283185307179586 #define POSITIVE_INFINITY 3.402823466e+38 `; var coordinates = /* glsl */ ` vec2 sfmToUv(const in vec2 sfm, const in vec2 scale) { float u = scale.x * sfm.x + 0.5; float v = - scale.y * sfm.y + 0.5; return vec2(u, v); } `; var bearing_fragment = /* glsl */ ` vec3 bearing = normalize(positionExtrinsic.xyz); `; var map_color_fragment = /* glsl */ ` vec2 uv = sfmToUv(sfm, scale); float u = uv.x; float v = uv.y; vec4 mapColor; if (u >= 0. && u <= 1. && v >= 0. && v <= 1.) { mapColor = texture2D(map, vec2(u, v)); mapColor.a = opacity; } else { mapColor = vec4(0.0, 0.0, 0.0, 0.0); } `; var gl_frag_color_fragment = /* glsl */ ` gl_FragColor = mapColor; `; var precision_fragment = /* glsl */ ` #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif `; var uniforms_fragment = /* glsl */ ` uniform sampler2D map; uniform float opacity; uniform vec2 scale; `; var varyings_fragment = /* glsl */ ` varying vec4 positionExtrinsic; `; var extrinsic_vertex = /* glsl */ ` positionExtrinsic = extrinsicMatrix * vec4(position, 1.0); `; var gl_position_vertex = /* glsl */ ` gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); `; var uniforms_vertex = /* glsl */ ` uniform mat4 extrinsicMatrix; `; var varyings_vertex = /* glsl */ ` varying vec4 positionExtrinsic; `; // tslint:disable-next-line:variable-name const ShaderChunk = { // Definitions and functions common: common$1, coordinates, // Fragment bearing_fragment, map_color_fragment, gl_frag_color_fragment, precision_fragment, uniforms_fragment, varyings_fragment, // Vertex extrinsic_vertex, gl_position_vertex, uniforms_vertex, varyings_vertex, }; const expandPattern = /^[ ]*#expand +<([wd./]+)>/gm; const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function expandParameters(parameters) { const keys = Object.keys(parameters); if (keys.length === 0) { return ""; } const variables = keys .map(key => `float ${key};`); const expansion = ` ${variables.map(v => `uniform ${v}`).join(" ")} struct Parameters { ${variables.map(v => ` ${v}`).join(" ")} }; `; return expansion; } function expandUniforms(uniforms) { const keys = Object.keys(uniforms); if (keys.length === 0) { return ""; } const variables = []; for (const key of keys) { const value = uniforms[key]; if (typeof value === "boolean") { variables.push(`bool ${key};`); } else if (typeof value === "number") { variables.push(`float ${key};`); } else if (value instanceof Array) { switch (value.length) { case 2: variables.push(`vec2 ${key};`); break; case 3: variables.push(`vec3 ${key};`); break; case 4: variables.push(`vec4 ${key};`); break; case 9: variables.push(`mat3 ${key};`); break; case 16: variables.push(`mat4 ${key};`); break; default: throw new Error("Can not #expand vector of length <" + value.length + ">"); } } else { throw new Error("Can not #expand instance <" + value + ">"); } } const expansion = ` ${variables.map(v => `uniform ${v}`).join(" ")} struct Uniforms { ${variables.map(v => ` ${v}`).join(" ")} }; `; return expansion; } function expandProjectToSfmDefinition(definition) { return definition; } function expandProjectToSfmInvocation(parameters, uniforms) { const parameterKeys = Object.keys(parameters); const uniformKeys = Object.keys(uniforms); const p = parameterKeys.length > 0 ? `Parameters parameters = Parameters(${parameterKeys.join(", ")});` : ""; const u = uniformKeys.length > 0 ? `Uniforms uniforms = Uniforms(${uniformKeys.join(", ")});` : ""; const project = `vec2 sfm = projectToSfm(bearing${parameterKeys.length > 0 ? ", parameters" : ""}${uniformKeys.length > 0 ? ", uniforms" : ""});`; const expansion = ` ${p} ${u} ${project} `; return expansion; } function includeReplacer(_match, include) { const chunk = ShaderChunk[include]; if (chunk === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(chunk); } function resolveIncludes(shader) { return shader.replace(includePattern, includeReplacer); } function resolveExpands(shader, projectToSfmFunction, parameters, uniforms) { function expandReplacer(_match, expand) { switch (expand) { case "parameters": return expandParameters(parameters); case "uniforms": return expandUniforms(uniforms); case "project_to_sfm_definition": return expandProjectToSfmDefinition(projectToSfmFunction); case "project_to_sfm_invocation": return expandProjectToSfmInvocation(parameters, uniforms); default: throw new Error("Can not resolve #expand <" + expand + ">"); } } return shader.replace(expandPattern, expandReplacer); } function resolveShader(shader, camera) { return resolveExpands(resolveIncludes(shader), camera.projectToSfmFunction, camera.parameters, camera.uniforms); } function makeCameraUniforms(camera) { const cameraUniforms = {}; const { parameters, uniforms } = camera; for (const key in parameters) { if (parameters.hasOwnProperty(key)) { cameraUniforms[key] = { value: parameters[key] }; } } for (const key in uniforms) { if (!uniforms.hasOwnProperty(key)) { continue; } const value = uniforms[key]; if (value instanceof Array) { switch (value.length) { case 2: cameraUniforms[key] = { value: new Vector2().fromArray(value), }; break; case 3: cameraUniforms[key] = { value: new Vector3().fromArray(value), }; break; case 4: cameraUniforms[key] = { value: new Vector4().fromArray(value), }; break; case 9: cameraUniforms[key] = { value: new Matrix3().fromArray(value), }; break; case 16: cameraUniforms[key] = { value: new Matrix4().fromArray(value), }; break; default: throw new Error("Uniform vector of length <" + value.length + "> not supported"); } } else { cameraUniforms[key] = { value: uniforms[key] }; } } return cameraUniforms; } class MeshFactory { constructor(imagePlaneDepth, imageSphereRadius) { this._imagePlaneDepth = imagePlaneDepth != null ? imagePlaneDepth : 200; this._imageSphereRadius = imageSphereRadius != null ? imageSphereRadius : 200; } createMesh(image, transform, shader) { const texture = this._createTexture(image.image); const materialParameters = this._createMaterialParameters(transform, texture, shader); const material = new ShaderMaterial(materialParameters); if (isSpherical(transform.cameraType)) { return this._createImageSphere(image, transform, material); } else if (isFisheye(transform.cameraType)) { return this._createImagePlaneFisheye(image, transform, material); } else { return this._createImagePlane(image, transform, material); } } _createImageSphere(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImageSphereGeo(transform, image) : this._getFlatImageSphereGeo(transform); return new Mesh(geometry, material); } _createImagePlane(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImagePlaneGeo(transform, image) : this._getRegularFlatImagePlaneGeo(transform); return new Mesh(geometry, material); } _createImagePlaneFisheye(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImagePlaneGeoFisheye(transform, image) : this._getRegularFlatImagePlaneGeoFisheye(transform); return new Mesh(geometry, material); } _createMaterialParameters(transform, texture, shader) { const scaleX = Math.max(transform.basicHeight, transform.basicWidth) / transform.basicWidth; const scaleY = Math.max(transform.basicWidth, transform.basicHeight) / transform.basicHeight; return { depthWrite: false, fragmentShader: resolveShader(shader.fragment, transform.camera), side: DoubleSide, transparent: true, uniforms: Object.assign({ extrinsicMatrix: { value: transform.basicRt }, map: { value: texture }, opacity: { value: 1.0 }, scale: { value: new Vector2(scaleX, scaleY) } }, makeCameraUniforms(transform.camera)), vertexShader: resolveShader(shader.vertex, transform.camera), }; } _createTexture(image) { let texture = new Texture(image); texture.minFilter = LinearFilter; texture.needsUpdate = true; return texture; } _useMesh(transform, image) { return image.mesh.vertices.length && transform.hasValidScale; } _getImageSphereGeo(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getImagePlaneGeo(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getImagePlaneGeoFisheye(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getFlatImageSphereGeo(transform) { const geometry = new SphereGeometry(this._imageSphereRadius, 20, 40); const t = transform.rt .clone() .invert(); geometry.applyMatrix4(t); return geometry; } _getRegularFlatImagePlaneGeo(transform) { let width = transform.width; let height = transform.height; let size = Math.max(width, height); let dx = width / 2.0 / size; let dy = height / 2.0 / size; return this._getFlatImagePlaneGeo(transform, dx, dy); } _getFlatImagePlaneGeo(transform, dx, dy) { let vertices = []; vertices.push(transform.unprojectSfM([-dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([-dx, dy], this._imagePlaneDepth)); return this._createFlatGeometry(vertices); } _getRegularFlatImagePlaneGeoFisheye(transform) { let width = transform.width; let height = transform.height; let size = Math.max(width, height); let dx = width / 2.0 / size; let dy = height / 2.0 / size; return this._getFlatImagePlaneGeoFisheye(transform, dx, dy); } _getFlatImagePlaneGeoFisheye(transform, dx, dy) { let vertices = []; vertices.push(transform.unprojectSfM([-dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([-dx, dy], this._imagePlaneDepth)); return this._createFlatGeometry(vertices); } _createFlatGeometry(vertices) { let positions = new Float32Array(12); for (let i = 0; i < vertices.length; i++) { let index = 3 * i; positions[index + 0] = vertices[i][0]; positions[index + 1] = vertices[i][1]; positions[index + 2] = vertices[i][2]; } let indices = new Uint16Array(6); indices[0] = 0; indices[1] = 1; indices[2] = 3; indices[3] = 1; indices[4] = 2; indices[5] = 3; let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } } class MeshScene { constructor() { this._planes = []; this._planesOld = []; this._planesPeriphery = []; this._scene = new Scene(); this._sceneOld = new Scene(); this._scenePeriphery = new Scene(); } get planes() { return this._planes; } get planesOld() { return this._planesOld; } get planesPeriphery() { return this._planesPeriphery; } get scene() { return this._scene; } get sceneOld() { return this._sceneOld; } get scenePeriphery() { return this._scenePeriphery; } updateImagePlanes(planes) { this._dispose(this._planesOld, this.sceneOld); for (const plane of this._planes) { this._scene.remove(plane.mesh); this._sceneOld.add(plane.mesh); } for (const plane of planes) { this._scene.add(plane.mesh); } this._planesOld = this._planes; this._planes = planes; } addImagePlanes(planes) { for (const plane of planes) { this._scene.add(plane.mesh); this._planes.push(plane); } } addImagePlanesOld(planes) { for (const plane of planes) { this._sceneOld.add(plane.mesh); this._planesOld.push(plane); } } setImagePlanes(planes) { this._clear(); this.addImagePlanes(planes); } addPeripheryPlanes(planes) { for (const plane of planes) { this._scenePeriphery.add(plane.mesh); this._planesPeriphery.push(plane); } } setPeripheryPlanes(planes) { this._clearPeriphery(); this.addPeripheryPlanes(planes); } setImagePlanesOld(planes) { this._clearOld(); this.addImagePlanesOld(planes); } clear() { this._clear(); this._clearOld(); } _clear() { this._dispose(this._planes, this._scene); this._planes = []; } _clearOld() { this._dispose(this._planesOld, this._sceneOld); this._planesOld = []; } _clearPeriphery() { this._dispose(this._planesPeriphery, this._scenePeriphery); this._planesPeriphery = []; } _dispose(planes, scene) { for (const plane of planes) { const { mesh } = plane; scene.remove(mesh); mesh.geometry.dispose(); mesh.material.dispose(); const texture = mesh.material .uniforms.map.value; if (texture != null) { texture.dispose(); } } } } class ImageGLRenderer { constructor() { this._factory = new MeshFactory(); this._scene = new MeshScene(); this._alpha = 0; this._alphaOld = 0; this._fadeOutSpeed = 0.05; this._currentKey = null; this._previousKey = null; this._providerDisposers = {}; this._frameId = 0; this._needsRender = false; } get frameId() { return this._frameId; } get needsRender() { return this._needsRender; } indicateNeedsRender() { this._needsRender = true; } addPeripheryPlane(image, transform, shader) { const mesh = this._factory.createMesh(image, transform, shader); const plane = { mesh, imageId: image.id, camera: image.camera, }; this._scene.addPeripheryPlanes([plane]); this._needsRender = true; } clearPeripheryPlanes() { this._scene.setPeripheryPlanes([]); this._needsRender = true; } setShader(shader) { const planes = [ ...this._scene.planes, ...this._scene.planesOld, ...this._scene.planesPeriphery, ]; this._setShader(shader, planes); this._needsRender = true; } updateFrame(frame, shader) { this._updateFrameId(frame.id); this._needsRender = this._updateAlpha(frame.state.alpha) || this._needsRender; this._needsRender = this._updateAlphaOld(frame.state.alpha) || this._needsRender; this._needsRender = this._updateImagePlanes(frame.state, shader) || this._needsRender; } setTextureProvider(key, provider) { if (key !== this._currentKey) { return; } const createdSubscription = provider.textureCreated$ .subscribe((texture) => { this._updateTexture(texture); }); const updatedSubscription = provider.textureUpdated$ .subscribe((updated) => { this._needsRender = true; }); const dispose = () => { createdSubscription.unsubscribe(); updatedSubscription.unsubscribe(); provider.dispose(); }; if (key in this._providerDisposers) { const disposeProvider = this._providerDisposers[key]; disposeProvider(); delete this._providerDisposers[key]; } this._providerDisposers[key] = dispose; } updateTextureImage(imageElement, image) { this._needsRender = true; const planes = [ ...this._scene.planes, ...this._scene.planesOld, ...this._scene.planesPeriphery, ]; for (const plane of planes) { if (plane.imageId !== image.id) { continue; } const material = plane.mesh.material; const texture = material.uniforms.map.value; texture.image = imageElement; texture.needsUpdate = true; } } render(perspectiveCamera, renderer) { const planes = this._scene.planes; const planesOld = this._scene.planesOld; const planesPeriphery = this._scene.planesPeriphery; const planeAlpha = Object.keys(planesOld).length ? 1 : this._alpha; const peripheryAlpha = Object.keys(planesOld).length ? 1 : Math.floor(this._alpha); for (const plane of planes) { plane.mesh.material.uniforms.opacity.value = planeAlpha; } for (const plane of planesOld) { plane.mesh.material.uniforms.opacity.value = this._alphaOld; } for (const plane of planesPeriphery) { plane.mesh.material.uniforms.opacity.value = peripheryAlpha; } renderer.render(this._scene.scenePeriphery, perspectiveCamera); renderer.render(this._scene.scene, perspectiveCamera); renderer.render(this._scene.sceneOld, perspectiveCamera); for (const plane of planes) { plane.mesh.material.uniforms.opacity.value = this._alpha; } renderer.render(this._scene.scene, perspectiveCamera); } clearNeedsRender() { this._needsRender = false; } reset() { this._scene.clear(); for (const disposeProvider of Object.values(this._providerDisposers)) { disposeProvider(); } this._needsRender = true; } _setShader(shader, planes) { for (const plane of planes) { const material = plane.mesh.material; material.fragmentShader = resolveShader(shader.fragment, plane.camera); material.vertexShader = resolveShader(shader.vertex, plane.camera); } } _updateFrameId(frameId) { this._frameId = frameId; } _updateAlpha(alpha) { if (alpha === this._alpha) { return false; } this._alpha = alpha; return true; } _updateAlphaOld(alpha) { if (alpha < 1 || this._alphaOld === 0) { return false; } this._alphaOld = Math.max(0, this._alphaOld - this._fadeOutSpeed); return true; } _updateImagePlanes(state, shader) { if (state.currentImage == null || state.currentImage.id === this._currentKey) { return false; } const previousKey = state.previousImage != null ? state.previousImage.id : null; const currentKey = state.currentImage.id; if (this._previousKey !== previousKey && this._previousKey !== currentKey && this._previousKey in this._providerDisposers) { const disposeProvider = this._providerDisposers[this._previousKey]; disposeProvider(); delete this._providerDisposers[this._previousKey]; } if (previousKey != null) { if (previousKey !== this._currentKey && previousKey !== this._previousKey) { const previousMesh = this._factory.createMesh(state.previousImage, state.previousTransform, shader); const previousPlane = { mesh: previousMesh, imageId: previousKey, camera: state.previousImage.camera, }; this._scene.updateImagePlanes([previousPlane]); } this._previousKey = previousKey; } this._currentKey = currentKey; const currentMesh = this._factory.createMesh(state.currentImage, state.currentTransform, shader); const plane = { mesh: currentMesh, imageId: currentKey, camera: state.currentImage.camera, }; this._scene.updateImagePlanes([plane]); this._alphaOld = 1; return true; } _updateTexture(texture) { this._needsRender = true; const planes = this._scene.planes; for (const plane of planes) { const material = plane.mesh.material; const oldTexture = material.uniforms.map.value; material.uniforms.map.value = null; oldTexture.dispose(); material.uniforms.map.value = texture; } } } var RenderPass; (function (RenderPass) { RenderPass[RenderPass["Background"] = 0] = "Background"; RenderPass[RenderPass["Opaque"] = 1] = "Opaque"; })(RenderPass || (RenderPass = {})); /** * @class ImageTileLoader * * @classdesc Represents a loader of image tiles. */ class TileLoader { /** * Create a new image image tile loader instance. * * @param {APIWrapper} _api - API wrapper. */ constructor(_api) { this._api = _api; this._urls$ = new Map(); } /** * Retrieve an image tile. * * @param {string} url - URL to the image tile resource */ getImage$(url) { let aborter; const abort = new Promise((_, reject) => { aborter = reject; }); return [Observable.create((subscriber) => { this._api.data .getImageBuffer(url, abort) .then((buffer) => { aborter = null; const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { window.URL.revokeObjectURL(image.src); subscriber.next(image); subscriber.complete(); }; image.onerror = () => { aborter = null; window.URL.revokeObjectURL(image.src); subscriber.error(new Error(`Failed to load image tile`)); }; const blob = new Blob([buffer]); image.src = window.URL.createObjectURL(blob); }, (error) => { aborter = null; subscriber.error(error); }); }), () => { if (!!aborter) { aborter(); } }]; } getURLs$(imageId, level) { const uniqueId = this._inventId(imageId, level); if (this._urls$.has(uniqueId)) { return this._urls$.get(uniqueId); } const request = { imageId, z: level }; const urls$ = this._api .getImageTiles$(request) .pipe(retry(1), map(contract => contract.node), finalize(() => { this._urls$.delete(uniqueId); }), publish(), refCount()); this._urls$.set(uniqueId, urls$); return urls$; } _inventId(imageId, level) { return `${imageId}-${level}`; } } /** * @class ImageTileStore * * @classdesc Represents a store for image tiles. */ class TileStore { /** * Create a new image image tile store instance. */ constructor() { this._tiles = new Map(); this._urlLevels = new Set(); this._urls = new Map(); } /** * Add an image tile to the store. * * @param {string} id - The identifier for the image tile. * @param {HTMLImageElement} image - The image tile. */ add(id, image) { if (this._tiles.has(id)) { throw new Error(`Image tile already stored (${id})`); } this._tiles.set(id, image); } addURLs(level, ents) { const urls = this._urls; for (const ent of ents) { const id = this.inventId(ent); if (this._urls.has(id)) { throw new Error(`URL already stored (${id})`); } urls.set(id, ent.url); } this._urlLevels.add(level); } /** * Dispose the store. * * @description Disposes all cached assets. */ dispose() { this._tiles .forEach(image => window.URL.revokeObjectURL(image.src)); this._tiles.clear(); this._urls.clear(); this._urlLevels.clear(); } /** * Get an image tile from the store. * * @param {string} id - The identifier for the tile. * @param {number} level - The level of the tile. */ get(id) { return this._tiles.get(id); } getURL(id) { return this._urls.get(id); } /** * Check if an image tile exist in the store. * * @param {string} id - The identifier for the tile. * @param {number} level - The level of the tile. */ has(id) { return this._tiles.has(id); } hasURL(id) { return this._urls.has(id); } hasURLLevel(level) { return this._urlLevels.has(level); } /** * Create a unique tile id from a tile. * * @description Tile ids are used as a hash for * storing the tile in a dictionary. * * @param {ImageTileEnt} tile - The tile. * @returns {string} Unique id. */ inventId(tile) { return `${tile.z}-${tile.x}-${tile.y}`; } } /** * @class RegionOfInterestCalculator * * @classdesc Represents a calculator for regions of interest. */ class RegionOfInterestCalculator { constructor() { this._viewportCoords = new ViewportCoords(); } /** * Compute a region of interest based on the current render camera * and the viewport size. * * @param {RenderCamera} renderCamera - Render camera used for unprojections. * @param {ViewportSize} size - Viewport size in pixels. * @param {Transform} transform - Transform used for projections. * * @returns {TileRegionOfInterest} A region of interest. */ computeRegionOfInterest(renderCamera, size, transform) { const viewportBoundaryPoints = this._viewportBoundaryPoints(4); const bbox = this._viewportPointsBoundingBox(viewportBoundaryPoints, renderCamera, transform); this._clipBoundingBox(bbox); const viewportPixelWidth = 2 / size.width; const viewportPixelHeight = 2 / size.height; const centralViewportPixel = [ [-0.5 * viewportPixelWidth, 0.5 * viewportPixelHeight], [0.5 * viewportPixelWidth, 0.5 * viewportPixelHeight], [0.5 * viewportPixelWidth, -0.5 * viewportPixelHeight], [-0.5 * viewportPixelWidth, -0.5 * viewportPixelHeight], ]; const cpbox = this._viewportPointsBoundingBox(centralViewportPixel, renderCamera, transform); const inverted = cpbox.minX < cpbox.maxX; return { bbox: bbox, pixelHeight: cpbox.maxY - cpbox.minY, pixelWidth: cpbox.maxX - cpbox.minX + (inverted ? 0 : 1), }; } _viewportBoundaryPoints(pointsPerSide) { const points = []; const os = [[-1, 1], [1, 1], [1, -1], [-1, -1]]; const ds = [[2, 0], [0, -2], [-2, 0], [0, 2]]; for (let side = 0; side < 4; ++side) { const o = os[side]; const d = ds[side]; for (let i = 0; i < pointsPerSide; ++i) { points.push([o[0] + d[0] * i / pointsPerSide, o[1] + d[1] * i / pointsPerSide]); } } return points; } _viewportPointsBoundingBox(viewportPoints, renderCamera, transform) { const basicPoints = viewportPoints .map((point) => { return this._viewportCoords .viewportToBasic(point[0], point[1], transform, renderCamera.perspective); }); if (isSpherical(transform.cameraType)) { return this._boundingBoxSpherical(basicPoints); } else { return this._boundingBox(basicPoints); } } _boundingBox(points) { const bbox = { maxX: Number.NEGATIVE_INFINITY, maxY: Number.NEGATIVE_INFINITY, minX: Number.POSITIVE_INFINITY, minY: Number.POSITIVE_INFINITY, }; for (let i = 0; i < points.length; ++i) { bbox.minX = Math.min(bbox.minX, points[i][0]); bbox.maxX = Math.max(bbox.maxX, points[i][0]); bbox.minY = Math.min(bbox.minY, points[i][1]); bbox.maxY = Math.max(bbox.maxY, points[i][1]); } return bbox; } _boundingBoxSpherical(points) { const xs = []; const ys = []; for (let i = 0; i < points.length; ++i) { xs.push(points[i][0]); ys.push(points[i][1]); } xs.sort((a, b) => { return this._sign(a - b); }); ys.sort((a, b) => { return this._sign(a - b); }); const intervalX = this._intervalSpherical(xs); return { maxX: intervalX[1], maxY: ys[ys.length - 1], minX: intervalX[0], minY: ys[0], }; } /** * Find the max interval between consecutive numbers. * Assumes numbers are between 0 and 1, sorted and that * x is equivalent to x + 1. */ _intervalSpherical(xs) { let maxdx = 0; let maxi = -1; for (let i = 0; i < xs.length - 1; ++i) { const dx = xs[i + 1] - xs[i]; if (dx > maxdx) { maxdx = dx; maxi = i; } } const loopdx = xs[0] + 1 - xs[xs.length - 1]; if (loopdx > maxdx) { return [xs[0], xs[xs.length - 1]]; } else { return [xs[maxi + 1], xs[maxi]]; } } _clipBoundingBox(bbox) { bbox.minX = Math.max(0, Math.min(1, bbox.minX)); bbox.maxX = Math.max(0, Math.min(1, bbox.maxX)); bbox.minY = Math.max(0, Math.min(1, bbox.minY)); bbox.maxY = Math.max(0, Math.min(1, bbox.maxY)); } _sign(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; } } const TILE_MIN_REQUEST_LEVEL = 11; const TILE_SIZE = 1024; function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function levelTilePixelSize(level) { return TILE_SIZE / levelScale(level); } function levelScale(level) { return Math.pow(2, level.z - level.max); } function rawImageLevel(size) { const s = Math.max(size.w, size.h); return Math.log(s) / Math.log(2); } function baseImageLevel(size) { return Math.ceil(rawImageLevel(size)); } function clampedImageLevel(size, min, max) { return Math.max(min, Math.min(max, baseImageLevel(size))); } function basicToTileCoords2D(basic, size, level) { const tilePixelSize = levelTilePixelSize(level); const w = size.w; const h = size.h; const maxX = Math.ceil(w / tilePixelSize) - 1; const maxY = Math.ceil(h / tilePixelSize) - 1; const x = clamp(Math.floor(w * basic[0] / tilePixelSize), 0, maxX); const y = clamp(Math.floor(h * basic[1] / tilePixelSize), 0, maxY); return { x, y }; } function tileToPixelCoords2D(tile, size, level) { const scale = 1 / levelScale(level); const scaledTS = scale * TILE_SIZE; const x = scaledTS * tile.x; const y = scaledTS * tile.y; const w = Math.min(scaledTS, size.w - x); const h = Math.min(scaledTS, size.h - y); return { h, x, y, w }; } function hasOverlap1D(low, base, scale) { return (scale * low <= base && base < scale * (low + 1)); } function hasOverlap2D(tile1, tile2) { if (tile1.z === tile2.z) { return tile1.x === tile2.x && tile1.y === tile2.y; } const low = tile1.z < tile2.z ? tile1 : tile2; const base = tile1.z < tile2.z ? tile2 : tile1; const scale = 1 / levelScale({ max: base.z, z: low.z }); const overlapX = hasOverlap1D(low.x, base.x, scale); const overlapY = hasOverlap1D(low.y, base.y, scale); return overlapX && overlapY; } function cornersToTilesCoords2D(topLeft, bottomRight, size, level) { const xs = []; if (topLeft.x > bottomRight.x) { const tilePixelSize = levelTilePixelSize(level); const maxX = Math.ceil(size.w / tilePixelSize) - 1; for (let x = topLeft.x; x <= maxX; x++) { xs.push(x); } for (let x = 0; x <= bottomRight.x; x++) { xs.push(x); } } else { for (let x = topLeft.x; x <= bottomRight.x; x++) { xs.push(x); } } const tiles = []; for (const x of xs) { for (let y = topLeft.y; y <= bottomRight.y; y++) { tiles.push({ x, y }); } } return tiles; } function verifySize(size) { return size.w > 0 && size.h > 0; } /** * @class TextureProvider * * @classdesc Represents a provider of textures. */ class TextureProvider { /** * Create a new image texture provider instance. * * @param {string} imageId - The identifier of the image for which to request tiles. * @param {number} width - The full width of the original image. * @param {number} height - The full height of the original image. * @param {HTMLImageElement} background - Image to use as background. * @param {TileLoader} loader - Loader for retrieving tiles. * @param {TileStore} store - Store for saving tiles. * @param {THREE.WebGLRenderer} renderer - Renderer used for rendering tiles to texture. */ constructor(imageId, width, height, background, loader, store, renderer) { const size = { h: height, w: width }; if (!verifySize(size)) { console.warn(`Original image size (${width}, ${height}) ` + `is invalid (${imageId}). Tiles will not be loaded.`); } this._imageId = imageId; this._size = size; this._level = { max: baseImageLevel(this._size), z: -1, }; this._holder = new SubscriptionHolder(); this._updated$ = new Subject(); this._createdSubject$ = new Subject(); this._created$ = this._createdSubject$ .pipe(publishReplay(1), refCount()); this._holder.push(this._created$.subscribe(() => { })); this._hasSubject$ = new Subject(); this._has$ = this._hasSubject$ .pipe(startWith(false), publishReplay(1), refCount()); this._holder.push(this._has$.subscribe(() => { })); this._renderedLevel = new Set(); this._rendered = new Map(); this._subscriptions = new Map(); this._urlSubscriptions = new Map(); this._failedLevels = new Set(); this._loader = loader; this._store = store; this._background = background; this._renderer = renderer; this._aborts = []; this._render = null; this._disposed = false; } /** * Get disposed. * * @returns {boolean} Value indicating whether provider has * been disposed. */ get disposed() { return this._disposed; } /** * Get hasTexture$. * * @returns {Observable} Observable emitting * values indicating when the existance of a texture * changes. */ get hasTexture$() { return this._has$; } /** * Get id. * * @returns {boolean} The identifier of the image for * which to render textures. */ get id() { return this._imageId; } /** * Get textureUpdated$. * * @returns {Observable} Observable emitting * values when an existing texture has been updated. */ get textureUpdated$() { return this._updated$; } /** * Get textureCreated$. * * @returns {Observable} Observable emitting * values when a new texture has been created. */ get textureCreated$() { return this._created$; } /** * Abort all outstanding image tile requests. */ abort() { this._subscriptions.forEach(sub => sub.unsubscribe()); this._subscriptions.clear(); for (const abort of this._aborts) { abort(); } this._aborts = []; } /** * Dispose the provider. * * @description Disposes all cached assets and * aborts all outstanding image tile requests. */ dispose() { if (this._disposed) { console.warn(`Texture already disposed (${this._imageId})`); return; } this._urlSubscriptions.forEach(sub => sub.unsubscribe()); this._urlSubscriptions.clear(); this.abort(); if (this._render != null) { this._render.target.dispose(); this._render.target = null; this._render.camera = null; this._render = null; } this._store.dispose(); this._holder.unsubscribe(); this._renderedLevel.clear(); this._background = null; this._renderer = null; this._disposed = true; } /** * Set the region of interest. * * @description When the region of interest is set the * the tile level is determined and tiles for the region * are fetched from the store or the loader and renderedLevel * to the texture. * * @param {TileRegionOfInterest} roi - Spatial edges to cache. */ setRegionOfInterest(roi) { if (!verifySize(this._size)) { return; } const virtualWidth = 1 / roi.pixelWidth; const virtualHeight = 1 / roi.pixelHeight; const level = clampedImageLevel({ h: virtualHeight, w: virtualWidth }, TILE_MIN_REQUEST_LEVEL, this._level.max); if (level !== this._level.z) { this.abort(); this._level.z = level; this._renderedLevel.clear(); this._rendered .forEach((tile, id) => { if (tile.z !== level) { return; } this._renderedLevel.add(id); }); } if (this._render == null) { this._initRender(); } const topLeft = basicToTileCoords2D([roi.bbox.minX, roi.bbox.minY], this._size, this._level); const bottomRight = basicToTileCoords2D([roi.bbox.maxX, roi.bbox.maxY], this._size, this._level); const tiles = cornersToTilesCoords2D(topLeft, bottomRight, this._size, this._level); this._fetchTiles(level, tiles); } /** * Retrieve an image tile. * * @description Retrieve an image tile and render it to the * texture. Add the tile to the store and emit to the updated * observable. * * @param {ImageTileEnt} tile - The tile ent. */ _fetchTile(tile) { const getTile = this._loader.getImage$(tile.url); const tile$ = getTile[0]; const abort = getTile[1]; this._aborts.push(abort); const tileId = this._store.inventId(tile); const subscription = tile$.subscribe((image) => { const pixels = tileToPixelCoords2D(tile, this._size, this._level); this._renderToTarget(pixels, image); this._subscriptions.delete(tileId); this._removeFromArray(abort, this._aborts); this._markRendered(tile); this._store.add(tileId, image); this._updated$.next(true); }, (error) => { this._subscriptions.delete(tileId); this._removeFromArray(abort, this._aborts); console.error(error); }); if (!subscription.closed) { this._subscriptions.set(tileId, subscription); } } /** * Fetch image tiles. * * @description Retrieve a image tiles and render them to the * texture. Retrieve from store if it exists, otherwise retrieve * from loader. * * @param {Array} tiles - Array of tile coordinates to * retrieve. */ _fetchTiles(level, tiles) { if (this._failedLevels.has(level)) { return; } const urls$ = this._store.hasURLLevel(level) ? of(undefined) : this._loader .getURLs$(this._imageId, level) .pipe(tap(ents => { if (!this._store.hasURLLevel(level)) { this._store.addURLs(level, ents); } if (this._failedLevels.size > 0) { // tslint:disable-next-line:no-console console.debug(`Tile URL fetch succeeded for level ${level}, resetting failed levels ${[...this._failedLevels.keys()]}`); this._failedLevels.clear(); } })); const subscription = urls$.subscribe(() => { if (level !== this._level.z) { return; } for (const tile of tiles) { const ent = { x: tile.x, y: tile.y, z: level, url: null, }; const id = this._store.inventId(ent); if (this._renderedLevel.has(id) || this._subscriptions.has(id)) { continue; } if (this._store.has(id)) { const pixels = tileToPixelCoords2D(tile, this._size, this._level); this._renderToTarget(pixels, this._store.get(id)); this._markRendered(ent); this._updated$.next(true); continue; } ent.url = this._store.getURL(id); this._fetchTile(ent); } this._urlSubscriptions.delete(level); }, (error) => { this._failedLevels.add(level); this._urlSubscriptions.delete(level); // tslint:disable-next-line:no-console console.debug(error); }); if (!subscription.closed) { this._urlSubscriptions.set(level, subscription); } } _initRender() { const dx = this._size.w / 2; const dy = this._size.h / 2; const near = -1; const far = 1; const camera = new OrthographicCamera(-dx, dx, dy, -dy, near, far); camera.position.z = 1; const gl = this._renderer.getContext(); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const backgroundSize = Math.max(this._size.w, this._size.h); const scale = maxTextureSize > backgroundSize ? 1 : maxTextureSize / backgroundSize; const targetWidth = Math.floor(scale * this._size.w); const targetHeight = Math.floor(scale * this._size.h); const target = new WebGLRenderTarget(targetWidth, targetHeight, { depthBuffer: false, format: RGBAFormat, magFilter: LinearFilter, minFilter: LinearFilter, stencilBuffer: false, }); this._render = { camera, target }; const pixels = tileToPixelCoords2D({ x: 0, y: 0 }, this._size, { max: this._level.max, z: 0 }); this._renderToTarget(pixels, this._background); this._createdSubject$.next(target.texture); this._hasSubject$.next(true); } /** * Mark a tile as rendered. * * @description Clears tiles marked as rendered in other * levels of the tile pyramid if they overlap the * newly rendered tile. * * @param {Arrary} tile - The tile ent. */ _markRendered(tile) { const others = Array.from(this._rendered.entries()) .filter(([_, t]) => { return t.z !== tile.z; }); for (const [otherId, other] of others) { if (hasOverlap2D(tile, other)) { this._rendered.delete(otherId); } } const id = this._store.inventId(tile); this._rendered.set(id, tile); this._renderedLevel.add(id); } /** * Remove an item from an array if it exists in array. * * @param {T} item - Item to remove. * @param {Array} array - Array from which item should be removed. */ _removeFromArray(item, array) { const index = array.indexOf(item); if (index !== -1) { array.splice(index, 1); } } /** * Render an image tile to the target texture. * * @param {ImageTileEnt} tile - Tile ent. * @param {HTMLImageElement} image - The image tile to render. */ _renderToTarget(pixel, image) { const texture = new Texture(image); texture.minFilter = LinearFilter; texture.needsUpdate = true; const geometry = new PlaneGeometry(pixel.w, pixel.h); const material = new MeshBasicMaterial({ map: texture, side: FrontSide, }); const mesh = new Mesh(geometry, material); mesh.position.x = -this._size.w / 2 + pixel.x + pixel.w / 2; mesh.position.y = this._size.h / 2 - pixel.y - pixel.h / 2; const scene = new Scene(); scene.add(mesh); const target = this._renderer.getRenderTarget(); this._renderer.resetState(); this._renderer.setRenderTarget(this._render.target); this._renderer.render(scene, this._render.camera); this._renderer.setRenderTarget(target); scene.remove(mesh); geometry.dispose(); material.dispose(); texture.dispose(); } } var State; (function (State) { State[State["Custom"] = 0] = "Custom"; State[State["Earth"] = 1] = "Earth"; State[State["GravityTraversing"] = 2] = "GravityTraversing"; State[State["Traversing"] = 3] = "Traversing"; State[State["Waiting"] = 4] = "Waiting"; State[State["WaitingInteractively"] = 5] = "WaitingInteractively"; })(State || (State = {})); class ImageComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); this._imageTileLoader = new TileLoader(navigator.api); this._roiCalculator = new RegionOfInterestCalculator(); this._rendererOperation$ = new Subject(); this._rendererCreator$ = new Subject(); this._rendererDisposer$ = new Subject(); this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { return operation(renderer); }, null), filter((renderer) => { return renderer != null; }), distinctUntilChanged(undefined, (renderer) => { return renderer.frameId; })); this._rendererCreator$.pipe(map(() => { return (renderer) => { if (renderer != null) { throw new Error("Multiple image plane states can not be created at the same time"); } return new ImageGLRenderer(); }; })) .subscribe(this._rendererOperation$); this._rendererDisposer$.pipe(map(() => { return (renderer) => { renderer.reset(); renderer.clearNeedsRender(); return null; }; })) .subscribe(this._rendererOperation$); } _activate() { const subs = this._subscriptions; subs.push(this._renderer$.pipe(map((renderer) => { const renderHash = { name: this._name, renderer: { frameId: renderer.frameId, needsRender: renderer.needsRender, render: renderer.render.bind(renderer), pass: RenderPass.Background, }, }; renderer.clearNeedsRender(); return renderHash; })) .subscribe(this._container.glRenderer.render$)); this._rendererCreator$.next(null); subs.push(this._navigator.graphService.dataReset$.pipe(map(() => { return (renderer) => { renderer.reset(); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(this._navigator.stateService.currentState$.pipe(withLatestFrom(this._navigator.projectionService.shader$), map(([frame, shader]) => { return (renderer) => { renderer.updateFrame(frame, shader); return renderer; }; })) .subscribe(this._rendererOperation$)); const textureProvider$ = this._container.configurationService.imageTiling$.pipe(switchMap((active) => { return active ? this._navigator.stateService.currentState$ : new Subject(); }), distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; }), withLatestFrom(this._container.glRenderer.webGLRenderer$), map(([frame, renderer]) => { const state = frame.state; const currentNode = state.currentImage; const currentTransform = state.currentTransform; return new TextureProvider(currentNode.id, currentTransform.basicWidth, currentTransform.basicHeight, currentNode.image, this._imageTileLoader, new TileStore(), renderer); }), publishReplay(1), refCount()); subs.push(this._navigator.projectionService.shader$.pipe(map((shader) => { return (renderer) => { renderer.setShader(shader); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(textureProvider$.subscribe(() => { })); subs.push(textureProvider$.pipe(map((provider) => { return (renderer) => { renderer.setTextureProvider(provider.id, provider); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(textureProvider$.pipe(pairwise()) .subscribe((pair) => { const previous = pair[0]; previous.abort(); })); const roiTrigger$ = this._container.configurationService.imageTiling$.pipe(switchMap((active) => { return active ? combineLatest(this._navigator.stateService.state$, this._navigator.stateService.inTranslation$) : new Subject(); }), switchMap(([state, inTranslation]) => { const streetState = state === State.GravityTraversing || state === State.Traversing || state === State.Waiting || state === State.WaitingInteractively; const active = streetState && !inTranslation; return active ? this._container.renderService.renderCameraFrame$ : empty(); }), map((camera) => { return { camera, height: camera.size.height.valueOf(), lookat: camera.camera.lookat.clone(), width: camera.size.width.valueOf(), zoom: camera.zoom.valueOf(), }; }), pairwise(), map(([pl0, pl1]) => { const stalled = pl0.width === pl1.width && pl0.height === pl1.height && pl0.zoom === pl1.zoom && pl0.lookat.equals(pl1.lookat); return { camera: pl1.camera, stalled }; }), distinctUntilChanged((x, y) => { return x.stalled === y.stalled; }), filter((camera) => { return camera.stalled; }), withLatestFrom(this._container.renderService.size$, this._navigator.stateService.currentTransform$)); subs.push(textureProvider$.pipe(switchMap((provider) => { return roiTrigger$.pipe(map(([stalled, size, transform]) => { const camera = stalled.camera; const basic = new ViewportCoords() .viewportToBasic(0, 0, transform, camera.perspective); if (basic[0] < 0 || basic[1] < 0 || basic[0] > 1 || basic[1] > 1) { return undefined; } return [ this._roiCalculator .computeRegionOfInterest(camera, size, transform), provider, ]; }), filter((args) => { return !!args; })); }), filter((args) => { return !args[1].disposed; })) .subscribe(([roi, provider]) => { provider.setRegionOfInterest(roi); })); const hasTexture$ = textureProvider$ .pipe(switchMap((provider) => { return provider.hasTexture$; }), startWith(false), publishReplay(1), refCount()); subs.push(hasTexture$.subscribe(() => { })); subs.push(this._navigator.panService.panImages$.pipe(map(() => { return (renderer) => { renderer.clearPeripheryPlanes(); return renderer; }; })) .subscribe(this._rendererOperation$)); const cachedPanNodes$ = this._navigator.panService.panImages$.pipe(switchMap((nts) => { return from(nts).pipe(mergeMap(([n, t]) => { return combineLatest(this._navigator.graphService.cacheImage$(n.id).pipe(catchError((error) => { console.error(`Failed to cache periphery image (${n.id})`, error); return empty(); })), of(t)); })); }), share()); subs.push(cachedPanNodes$.pipe(withLatestFrom(this._navigator.projectionService.shader$), map(([[n, t], s]) => { return (renderer) => { renderer.addPeripheryPlane(n, t, s); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(cachedPanNodes$.pipe(mergeMap(([n]) => { return n.cacheImage$().pipe(catchError(() => { return empty(); })); }), map((n) => { return (renderer) => { renderer.updateTextureImage(n.image, n); return renderer; }; })) .subscribe(this._rendererOperation$)); const inTransition$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.alpha < 1; }), distinctUntilChanged()); const panTrigger$ = combineLatest(this._container.mouseService.active$, this._container.touchService.active$, this._navigator.stateService.inMotion$, inTransition$).pipe(map(([mouseActive, touchActive, inMotion, inTransition]) => { return !(mouseActive || touchActive || inMotion || inTransition); }), filter((trigger) => { return trigger; })); subs.push(this._navigator.panService.panImages$ .pipe(switchMap((nts) => { return nts.length === 0 ? empty() : panTrigger$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentImage$, this._navigator.stateService.currentTransform$), mergeMap(([, renderCamera, currentNode, currentTransform]) => { return of([ renderCamera, currentNode, currentTransform, nts, ]); })); }), switchMap(([camera, cn, ct, nts]) => { const direction = camera.camera.lookat.clone().sub(camera.camera.position); const cd = new Spatial().viewingDirection(cn.rotation); const ca = cd.angleTo(direction); const closest = [ca, undefined]; const basic = new ViewportCoords().viewportToBasic(0, 0, ct, camera.perspective); if (basic[0] >= 0 && basic[0] <= 1 && basic[1] >= 0 && basic[1] <= 1) { closest[0] = Number.NEGATIVE_INFINITY; } for (const [n] of nts) { const d = new Spatial().viewingDirection(n.rotation); const a = d.angleTo(direction); if (a < closest[0]) { closest[0] = a; closest[1] = n.id; } } if (!closest[1]) { return empty(); } return this._navigator.moveTo$(closest[1]).pipe(catchError(() => { return empty(); })); })) .subscribe()); } _deactivate() { this._rendererDisposer$.next(null); this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } } ImageComponent.componentName = "image"; class HandlerBase { /** @ignore */ constructor(component, container, navigator) { this._component = component; this._container = container; this._navigator = navigator; this._enabled = false; } /** * Returns a Boolean indicating whether the interaction is enabled. * * @returns {boolean} `true` if the interaction is enabled. */ get isEnabled() { return this._enabled; } /** * Enables the interaction. * * @example * ```js * ..enable(); * ``` */ enable() { if (this._enabled || !this._component.activated) { return; } this._enable(); this._enabled = true; this._component.configure(this._getConfiguration(true)); } /** * Disables the interaction. * * @example * ```js * ..disable(); * ``` */ disable() { if (!this._enabled) { return; } this._disable(); this._enabled = false; if (this._component.activated) { this._component.configure(this._getConfiguration(false)); } } } /** * The `KeySequenceNavigationHandler` allows the user to navigate through a sequence using the * following key commands: * * `ALT` + `Up Arrow`: Navigate to next image in the sequence. * `ALT` + `Down Arrow`: Navigate to previous image in sequence. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keySequenceNavigation.disable(); * keyboardComponent.keySequenceNavigation.enable(); * * var isEnabled = keyboardComponent.keySequenceNavigation.isEnabled; * ``` */ class KeySequenceNavigationHandler extends HandlerBase { _enable() { const sequenceEdges$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; })); this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(sequenceEdges$)) .subscribe(([event, edgeStatus]) => { let direction = null; switch (event.keyCode) { case 38: // up direction = exports.NavigationDirection.Next; break; case 40: // down direction = exports.NavigationDirection.Prev; break; default: return; } event.preventDefault(); if (!event.altKey || event.shiftKey || !edgeStatus.cached) { return; } for (const edge of edgeStatus.edges) { if (edge.data.direction === direction) { this._navigator.moveTo$(edge.target) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); return; } } }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keySequenceNavigation: enable }; } } /** * The `KeySpatialNavigationHandler` allows the user to navigate through a sequence using the * following key commands: * * `Up Arrow`: Step forward. * `Down Arrow`: Step backward. * `Left Arrow`: Step to the left. * `Rigth Arrow`: Step to the right. * `SHIFT` + `Down Arrow`: Turn around. * `SHIFT` + `Left Arrow`: Turn to the left. * `SHIFT` + `Rigth Arrow`: Turn to the right. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keySpatialNavigation.disable(); * keyboardComponent.keySpatialNavigation.enable(); * * var isEnabled = keyboardComponent.keySpatialNavigation.isEnabled; * ``` */ class KeySpatialNavigationHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, spatial) { super(component, container, navigator); this._spatial = spatial; } _enable() { const spatialEdges$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.spatialEdges$; })); this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(spatialEdges$, this._navigator.stateService.currentState$)) .subscribe(([event, edgeStatus, frame]) => { let spherical = isSpherical(frame.state.currentImage.cameraType); let direction = null; switch (event.keyCode) { case 37: // left direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnLeft : exports.NavigationDirection.StepLeft; break; case 38: // up direction = event.shiftKey && !spherical ? exports.NavigationDirection.Spherical : exports.NavigationDirection.StepForward; break; case 39: // right direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnRight : exports.NavigationDirection.StepRight; break; case 40: // down direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnU : exports.NavigationDirection.StepBackward; break; default: return; } event.preventDefault(); if (event.altKey || !edgeStatus.cached || (event.shiftKey && spherical)) { return; } if (!spherical) { this._moveDir(direction, edgeStatus); } else { const shifts = {}; shifts[exports.NavigationDirection.StepBackward] = Math.PI; shifts[exports.NavigationDirection.StepForward] = 0; shifts[exports.NavigationDirection.StepLeft] = Math.PI / 2; shifts[exports.NavigationDirection.StepRight] = -Math.PI / 2; const phi = this._rotationFromCamera(frame.state.camera).phi; const navigationAngle = this._spatial.wrapAngle(phi + shifts[direction]); const threshold = Math.PI / 4; const edges = edgeStatus.edges.filter((e) => { return e.data.direction === exports.NavigationDirection.Spherical || e.data.direction === direction; }); let smallestAngle = Number.MAX_VALUE; let toKey = null; for (const edge of edges) { const angle = Math.abs(this._spatial.wrapAngle(edge.data.worldMotionAzimuth - navigationAngle)); if (angle < Math.min(smallestAngle, threshold)) { smallestAngle = angle; toKey = edge.target; } } if (toKey == null) { return; } this._moveTo(toKey); } }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keySpatialNavigation: enable }; } _moveDir(direction, edgeStatus) { for (const edge of edgeStatus.edges) { if (edge.data.direction === direction) { this._moveTo(edge.target); return; } } } _moveTo(id) { this._navigator.moveTo$(id) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } _rotationFromCamera(camera) { let direction = camera.lookat.clone().sub(camera.position); let upProjection = direction.clone().dot(camera.up); let planeProjection = direction.clone().sub(camera.up.clone().multiplyScalar(upProjection)); let phi = Math.atan2(planeProjection.y, planeProjection.x); let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); return { phi: phi, theta: theta }; } } /** * The `KeyZoomHandler` allows the user to zoom in and out using the * following key commands: * * `+`: Zoom in. * `-`: Zoom out. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keyZoom.disable(); * keyboardComponent.keyZoom.enable(); * * var isEnabled = keyboardComponent.keyZoom.isEnabled; * ``` */ class KeyZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([event, render, transform]) => { if (event.altKey || event.ctrlKey || event.metaKey) { return; } let delta = 0; switch (event.key) { case "+": delta = 1; break; case "-": delta = -1; break; default: return; } event.preventDefault(); const unprojected = this._viewportCoords.unprojectFromViewport(0, 0, render.perspective); const reference = transform.projectBasic(unprojected.toArray()); this._navigator.stateService.zoomIn(delta, reference); }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keyZoom: enable }; } } /** * The `KeyPlayHandler` allows the user to control the play behavior * using the following key commands: * * `Spacebar`: Start or stop playing. * `SHIFT` + `D`: Switch direction. * `<`: Decrease speed. * `>`: Increase speed. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keyPlay.disable(); * keyboardComponent.keyPlay.enable(); * * var isEnabled = keyboardComponent.keyPlay.isEnabled; * ``` */ class KeyPlayHandler extends HandlerBase { _enable() { this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(this._navigator.playService.playing$, this._navigator.playService.direction$, this._navigator.playService.speed$, this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; })), this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged()))) .subscribe(([event, playing, direction, speed, status, earth]) => { if (event.altKey || event.ctrlKey || event.metaKey) { return; } switch (event.key) { case "D": if (!event.shiftKey) { return; } const newDirection = playing ? null : direction === exports.NavigationDirection.Next ? exports.NavigationDirection.Prev : direction === exports.NavigationDirection.Prev ? exports.NavigationDirection.Next : null; if (newDirection != null) { this._navigator.playService.setDirection(newDirection); } break; case " ": if (event.shiftKey) { return; } if (!earth) { if (playing) { this._navigator.playService.stop(); } else { for (let edge of status.edges) { if (edge.data.direction === direction) { this._navigator.playService.play(); } } } } break; case "<": this._navigator.playService.setSpeed(speed - 0.05); break; case ">": this._navigator.playService.setSpeed(speed + 0.05); break; default: return; } event.preventDefault(); }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keyPlay: enable }; } } /** * @class KeyboardComponent * * @classdesc Component for keyboard event handling. * * To retrive and use the keyboard component * * @example * ```js * var viewer = new Viewer({ ... }); * * var keyboardComponent = viewer.getComponent("keyboard"); * ``` */ class KeyboardComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._keyPlayHandler = new KeyPlayHandler(this, container, navigator); this._keySequenceNavigationHandler = new KeySequenceNavigationHandler(this, container, navigator); this._keySpatialNavigationHandler = new KeySpatialNavigationHandler(this, container, navigator, new Spatial()); this._keyZoomHandler = new KeyZoomHandler(this, container, navigator, new ViewportCoords()); } /** * Get key play. * * @returns {KeyPlayHandler} The key play handler. */ get keyPlay() { return this._keyPlayHandler; } /** * Get key sequence navigation. * * @returns {KeySequenceNavigationHandler} The key sequence navigation handler. */ get keySequenceNavigation() { return this._keySequenceNavigationHandler; } /** * Get spatial. * * @returns {KeySpatialNavigationHandler} The spatial handler. */ get keySpatialNavigation() { return this._keySpatialNavigationHandler; } /** * Get key zoom. * * @returns {KeyZoomHandler} The key zoom handler. */ get keyZoom() { return this._keyZoomHandler; } _activate() { this._subscriptions.push(this._configuration$ .subscribe((configuration) => { if (configuration.keyPlay) { this._keyPlayHandler.enable(); } else { this._keyPlayHandler.disable(); } if (configuration.keySequenceNavigation) { this._keySequenceNavigationHandler.enable(); } else { this._keySequenceNavigationHandler.disable(); } if (configuration.keySpatialNavigation) { this._keySpatialNavigationHandler.enable(); } else { this._keySpatialNavigationHandler.disable(); } if (configuration.keyZoom) { this._keyZoomHandler.enable(); } else { this._keyZoomHandler.disable(); } })); } _deactivate() { this._subscriptions.unsubscribe(); this._keyPlayHandler.disable(); this._keySequenceNavigationHandler.disable(); this._keySpatialNavigationHandler.disable(); this._keyZoomHandler.disable(); } _getDefaultConfiguration() { return { keyPlay: true, keySequenceNavigation: true, keySpatialNavigation: true, keyZoom: true }; } } KeyboardComponent.componentName = "keyboard"; class MarkerScene { constructor(scene, raycaster) { this._needsRender = false; this._interactiveObjects = []; this._markers = {}; this._objectMarkers = {}; this._raycaster = !!raycaster ? raycaster : new Raycaster(); this._scene = !!scene ? scene : new Scene(); } get markers() { return this._markers; } get needsRender() { return this._needsRender; } add(marker, position) { if (marker.id in this._markers) { this._dispose(marker.id); } marker.createGeometry(position); this._scene.add(marker.geometry); this._markers[marker.id] = marker; for (let interactiveObject of marker.getInteractiveObjects()) { this._interactiveObjects.push(interactiveObject); this._objectMarkers[interactiveObject.uuid] = marker.id; } this._needsRender = true; } clear() { for (const id in this._markers) { if (!this._markers.hasOwnProperty) { continue; } this._dispose(id); } this._needsRender = true; } get(id) { return this._markers[id]; } getAll() { return Object .keys(this._markers) .map((id) => { return this._markers[id]; }); } has(id) { return id in this._markers; } intersectObjects([viewportX, viewportY], camera) { this._raycaster.setFromCamera(new Vector2(viewportX, viewportY), camera); const intersects = this._raycaster.intersectObjects(this._interactiveObjects); for (const intersect of intersects) { if (intersect.object.uuid in this._objectMarkers) { return this._objectMarkers[intersect.object.uuid]; } } return null; } lerpAltitude(id, alt, alpha) { if (!(id in this._markers)) { return; } this._markers[id].lerpAltitude(alt, alpha); this._needsRender = true; } remove(id) { if (!(id in this._markers)) { return; } this._dispose(id); this._needsRender = true; } render(perspectiveCamera, renderer) { renderer.render(this._scene, perspectiveCamera); this._needsRender = false; } update(id, position, lngLat) { if (!(id in this._markers)) { return; } const marker = this._markers[id]; marker.updatePosition(position, lngLat); this._needsRender = true; } _dispose(id) { const marker = this._markers[id]; this._scene.remove(marker.geometry); for (let interactiveObject of marker.getInteractiveObjects()) { const index = this._interactiveObjects.indexOf(interactiveObject); if (index !== -1) { this._interactiveObjects.splice(index, 1); } else { console.warn(`Object does not exist (${interactiveObject.id}) for ${id}`); } delete this._objectMarkers[interactiveObject.uuid]; } marker.disposeGeometry(); delete this._markers[id]; } } /** * @class MarkerComponent * * @classdesc Component for showing and editing 3D marker objects. * * The `add` method is used for adding new markers or replacing * markers already in the set. * * If a marker already in the set has the same * id as one of the markers added, the old marker will be removed and * the added marker will take its place. * * It is not possible to update markers in the set by updating any properties * directly on the marker object. Markers need to be replaced by * re-adding them for updates to geographic position or configuration * to be reflected. * * Markers added to the marker component can be either interactive * or non-interactive. Different marker types define their behavior. * Markers with interaction support can be configured with options * to respond to dragging inside the viewer and be detected when * retrieving markers from pixel points with the `getMarkerIdAt` method. * * To retrive and use the marker component * * @example * ```js * var viewer = new Viewer({ component: { marker: true }, ... }); * * var markerComponent = viewer.getComponent("marker"); * ``` */ class MarkerComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._graphCalculator = new GraphCalculator(); this._markerScene = new MarkerScene(); this._markerSet = new MarkerSet(); this._viewportCoords = new ViewportCoords(); this._relativeGroundAltitude = -2; } /** * Add markers to the marker set or replace markers in the marker set. * * @description If a marker already in the set has the same * id as one of the markers added, the old marker will be removed * the added marker will take its place. * * Any marker inside the visible bounding bbox * will be initialized and placed in the viewer. * * @param {Array} markers - Markers to add. * * @example * ```js * markerComponent.add([marker1, marker2]); * ``` */ add(markers) { this._markerSet.add(markers); } fire(type, event) { super.fire(type, event); } /** * Returns the marker in the marker set with the specified id, or * undefined if the id matches no marker. * * @param {string} markerId - Id of the marker. * * @example * ```js * var marker = markerComponent.get("markerId"); * ``` * */ get(markerId) { return this._markerSet.get(markerId); } /** * Returns an array of all markers. * * @example * ```js * var markers = markerComponent.getAll(); * ``` */ getAll() { return this._markerSet.getAll(); } /** * Returns the id of the interactive marker closest to the current camera * position at the specified point. * * @description Notice that the pixelPoint argument requires x, y * coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the marker component. * * If no interactive geometry of an interactive marker exist at the pixel * point, `null` will be returned. * * @param {Array} pixelPoint - Pixel coordinates on the viewer element. * @returns {string} Id of the interactive marker closest to the camera. If no * interactive marker exist at the pixel point, `null` will be returned. * * @example * ```js * markerComponent.getMarkerIdAt([100, 100]) * .then((markerId) => { console.log(markerId); }); * ``` */ getMarkerIdAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const id = this._markerScene.intersectObjects(viewport, render.perspective); return id; })) .subscribe((id) => { resolve(id); }, (error) => { reject(error); }); }); } /** * Check if a marker exist in the marker set. * * @param {string} markerId - Id of the marker. * * @example * ```js * var markerExists = markerComponent.has("markerId"); * ``` */ has(markerId) { return this._markerSet.has(markerId); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Remove markers with the specified ids from the marker set. * * @param {Array} markerIds - Ids for markers to remove. * * @example * ```js * markerComponent.remove(["id-1", "id-2"]); * ``` */ remove(markerIds) { this._markerSet.remove(markerIds); } /** * Remove all markers from the marker set. * * @example * ```js * markerComponent.removeAll(); * ``` */ removeAll() { this._markerSet.removeAll(); } _activate() { const groundAltitude$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.camera.position.z + this._relativeGroundAltitude; }), distinctUntilChanged((a1, a2) => { return Math.abs(a1 - a2) < 0.01; }), publishReplay(1), refCount()); const geoInitiated$ = combineLatest(groundAltitude$, this._navigator.stateService.reference$).pipe(first(), map(() => { }), publishReplay(1), refCount()); const clampedConfiguration$ = this._configuration$.pipe(map((configuration) => { return { visibleBBoxSize: Math.max(1, Math.min(200, configuration.visibleBBoxSize)) }; })); const currentLngLat$ = this._navigator.stateService.currentImage$.pipe(map((image) => { return image.lngLat; }), publishReplay(1), refCount()); const visibleBBox$ = combineLatest(clampedConfiguration$, currentLngLat$).pipe(map(([configuration, lngLat]) => { return this._graphCalculator .boundingBoxCorners(lngLat, configuration.visibleBBoxSize / 2); }), publishReplay(1), refCount()); const visibleMarkers$ = combineLatest(concat(of(this._markerSet), this._markerSet.changed$), visibleBBox$).pipe(map(([set, bbox]) => { return set.search(bbox); })); const subs = this._subscriptions; subs.push(geoInitiated$.pipe(switchMap(() => { return visibleMarkers$.pipe(withLatestFrom(this._navigator.stateService.reference$, groundAltitude$)); })) .subscribe(([markers, reference, alt]) => { const markerScene = this._markerScene; const sceneMarkers = markerScene.markers; const markersToRemove = Object.assign({}, sceneMarkers); for (const marker of markers) { if (marker.id in sceneMarkers) { delete markersToRemove[marker.id]; } else { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.add(marker, point3d); } } for (const id in markersToRemove) { if (!markersToRemove.hasOwnProperty(id)) { continue; } markerScene.remove(id); } })); subs.push(geoInitiated$.pipe(switchMap(() => { return this._markerSet.updated$.pipe(withLatestFrom(visibleBBox$, this._navigator.stateService.reference$, groundAltitude$)); })) .subscribe(([markers, [sw, ne], reference, alt]) => { const markerScene = this._markerScene; for (const marker of markers) { const exists = markerScene.has(marker.id); const visible = marker.lngLat.lat > sw.lat && marker.lngLat.lat < ne.lat && marker.lngLat.lng > sw.lng && marker.lngLat.lng < ne.lng; if (visible) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.add(marker, point3d); } else if (!visible && exists) { markerScene.remove(marker.id); } } })); subs.push(this._navigator.stateService.reference$.pipe(skip(1), withLatestFrom(groundAltitude$)) .subscribe(([reference, alt]) => { const markerScene = this._markerScene; for (const marker of markerScene.getAll()) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.update(marker.id, point3d); } })); subs.push(groundAltitude$.pipe(skip(1), withLatestFrom(this._navigator.stateService.reference$, currentLngLat$)) .subscribe(([alt, reference, lngLat]) => { const markerScene = this._markerScene; const position = geodeticToEnu(lngLat.lng, lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); for (const marker of markerScene.getAll()) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); const distanceX = point3d[0] - position[0]; const distanceY = point3d[1] - position[1]; const groundDistance = Math .sqrt(distanceX * distanceX + distanceY * distanceY); if (groundDistance > 50) { continue; } markerScene.lerpAltitude(marker.id, alt, Math.min(1, Math.max(0, 1.2 - 1.2 * groundDistance / 50))); } })); subs.push(this._navigator.stateService.currentState$ .pipe(map((frame) => { const scene = this._markerScene; return { name: this._name, renderer: { frameId: frame.id, needsRender: scene.needsRender, render: scene.render.bind(scene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); const hoveredMarkerId$ = combineLatest(this._container.renderService.renderCamera$, this._container.mouseService.mouseMove$) .pipe(map(([render, event]) => { const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const viewport = this._viewportCoords .canvasToViewport(canvasX, canvasY, element); const markerId = this._markerScene.intersectObjects(viewport, render.perspective); return markerId; }), publishReplay(1), refCount()); const draggingStarted$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragStart$).pipe(map(() => { return true; })); const draggingStopped$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragEnd$).pipe(map(() => { return false; })); const filteredDragging$ = merge(draggingStarted$, draggingStopped$) .pipe(startWith(false)); subs.push(merge(draggingStarted$.pipe(withLatestFrom(hoveredMarkerId$)), combineLatest(draggingStopped$, of(null))).pipe(startWith([false, null]), pairwise()) .subscribe(([previous, current]) => { const dragging = current[0]; const type = dragging ? "markerdragstart" : "markerdragend"; const id = dragging ? current[1] : previous[1]; const marker = this._markerScene.get(id); const event = { marker, target: this, type, }; this.fire(type, event); })); const mouseDown$ = merge(this._container.mouseService.mouseDown$.pipe(map(() => { return true; })), this._container.mouseService.documentMouseUp$.pipe(map(() => { return false; }))).pipe(startWith(false)); subs.push(combineLatest(this._container.mouseService.active$, hoveredMarkerId$.pipe(distinctUntilChanged()), mouseDown$, filteredDragging$) .pipe(map(([active, markerId, mouseDown, filteredDragging]) => { return (!active && markerId != null && mouseDown) || filteredDragging; }), distinctUntilChanged()) .subscribe((claim) => { if (claim) { this._container.mouseService.claimMouse(this._name, 1); this._container.mouseService.claimWheel(this._name, 1); } else { this._container.mouseService.unclaimMouse(this._name); this._container.mouseService.unclaimWheel(this._name); } })); const offset$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragStart$).pipe(withLatestFrom(hoveredMarkerId$, this._container.renderService.renderCamera$), map(([e, id, r]) => { const marker = this._markerScene.get(id); const element = this._container.container; const [groundCanvasX, groundCanvasY] = this._viewportCoords .projectToCanvas(marker.geometry.position .toArray(), element, r.perspective); const [canvasX, canvasY] = this._viewportCoords .canvasPosition(e, element); const offset = [canvasX - groundCanvasX, canvasY - groundCanvasY]; return [marker, offset, r]; }), publishReplay(1), refCount()); subs.push(this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDrag$) .pipe(withLatestFrom(offset$, this._navigator.stateService.reference$, clampedConfiguration$)) .subscribe(([event, [marker, offset, render], reference, configuration]) => { if (!this._markerScene.has(marker.id)) { return; } const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords .canvasPosition(event, element); const groundX = canvasX - offset[0]; const groundY = canvasY - offset[1]; const [viewportX, viewportY] = this._viewportCoords .canvasToViewport(groundX, groundY, element); const direction = new Vector3(viewportX, viewportY, 1) .unproject(render.perspective) .sub(render.perspective.position) .normalize(); const distance = Math.min(this._relativeGroundAltitude / direction.z, configuration.visibleBBoxSize / 2 - 0.1); if (distance < 0) { return; } const intersection = direction .clone() .multiplyScalar(distance) .add(render.perspective.position); intersection.z = render.perspective.position.z + this._relativeGroundAltitude; const [lng, lat] = enuToGeodetic(intersection.x, intersection.y, intersection.z, reference.lng, reference.lat, reference.alt); this._markerScene .update(marker.id, intersection.toArray(), { lat, lng }); this._markerSet.update(marker); const type = "markerposition"; const markerEvent = { marker, target: this, type, }; this.fire(type, markerEvent); })); } _deactivate() { this._subscriptions.unsubscribe(); this._markerScene.clear(); } _getDefaultConfiguration() { return { visibleBBoxSize: 100 }; } } MarkerComponent.componentName = "marker"; function sign$1(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; } function colinearPointOnSegment(p, s) { return p.x <= Math.max(s.p1.x, s.p2.x) && p.x >= Math.min(s.p1.x, s.p2.x) && p.y >= Math.max(s.p1.y, s.p2.y) && p.y >= Math.min(s.p1.y, s.p2.y); } function parallel(s1, s2) { const ux = s1.p2.x - s1.p1.x; const uy = s1.p2.y - s1.p1.y; const vx = s2.p2.x - s2.p1.x; const vy = s2.p2.y - s2.p1.y; const cross = ux * vy - uy * vx; const u2 = ux * ux + uy * uy; const v2 = vx * vx + vy * vy; const epsilon2 = 1e-10; return cross * cross < epsilon2 * u2 * v2; } function tripletOrientation(p1, p2, p3) { const orientation = (p2.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p2.x - p1.x); return sign$1(orientation); } function segmentsIntersect(s1, s2) { if (parallel(s1, s2)) { return false; } const o1 = tripletOrientation(s1.p1, s1.p2, s2.p1); const o2 = tripletOrientation(s1.p1, s1.p2, s2.p2); const o3 = tripletOrientation(s2.p1, s2.p2, s1.p1); const o4 = tripletOrientation(s2.p1, s2.p2, s1.p2); if (o1 !== o2 && o3 !== o4) { return true; } if (o1 === 0 && colinearPointOnSegment(s2.p1, s1)) { return true; } if (o2 === 0 && colinearPointOnSegment(s2.p2, s1)) { return true; } if (o3 === 0 && colinearPointOnSegment(s1.p1, s2)) { return true; } if (o4 === 0 && colinearPointOnSegment(s1.p2, s2)) { return true; } return false; } function segmentIntersection(s1, s2) { if (parallel(s1, s2)) { return undefined; } const x1 = s1.p1.x; const x2 = s1.p2.x; const y1 = s1.p1.y; const y2 = s1.p2.y; const x3 = s2.p1.x; const x4 = s2.p2.x; const y3 = s2.p1.y; const y4 = s2.p2.y; const den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); const xNum = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4); const yNum = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4); return { x: xNum / den, y: yNum / den }; } function basicBoundaryPoints(pointsPerSide) { let points = []; let os = [[0, 0], [1, 0], [1, 1], [0, 1]]; let ds = [[1, 0], [0, 1], [-1, 0], [0, -1]]; for (let side = 0; side < 4; ++side) { let o = os[side]; let d = ds[side]; for (let i = 0; i < pointsPerSide; ++i) { points.push([o[0] + d[0] * i / pointsPerSide, o[1] + d[1] * i / pointsPerSide]); } } return points; } function insideViewport(x, y) { return x >= -1 && x <= 1 && y >= -1 && y <= 1; } function insideBasic(x, y) { return x >= 0 && x <= 1 && y >= 0 && y <= 1; } function viewportDistances(transform, perspective, viewportCoords) { const boundaryPointsBasic = basicBoundaryPoints(100); const boundaryPointsViewport = boundaryPointsBasic .map((basic) => { return viewportCoords.basicToViewportSafe(basic[0], basic[1], transform, perspective); }); const visibleBoundaryPoints = []; const viewportSides = [ { x: -1, y: 1 }, { x: 1, y: 1 }, { x: 1, y: -1 }, { x: -1, y: -1 } ]; const intersections = [false, false, false, false]; for (let i = 0; i < boundaryPointsViewport.length; i++) { const p1 = boundaryPointsViewport[i]; const p2 = boundaryPointsViewport[(i + 1) % boundaryPointsViewport.length]; if (p1 === null) { continue; } if (p2 === null) { if (insideViewport(p1[0], p1[1])) { visibleBoundaryPoints.push(p1); } continue; } const [x1, y1] = p1; const [x2, y2] = p2; if (insideViewport(x1, y1)) { if (insideViewport(x2, y2)) { visibleBoundaryPoints.push(p1); } else { for (let side = 0; side < 4; side++) { const s1 = { p1: { x: x1, y: y1 }, p2: { x: x2, y: y2 } }; const s2 = { p1: viewportSides[side], p2: viewportSides[(side + 1) % 4] }; const intersecting = segmentsIntersect(s1, s2); if (intersecting) { const intersection = segmentIntersection(s1, s2); visibleBoundaryPoints.push(p1, [intersection.x, intersection.y]); intersections[side] = true; } } } } } const [topLeftBasicX, topLeftBasicY] = viewportCoords.viewportToBasic(-1, 1, transform, perspective); const [topRightBasicX, topRightBasicY] = viewportCoords.viewportToBasic(1, 1, transform, perspective); const [bottomRightBasicX, bottomRightBasicY] = viewportCoords.viewportToBasic(1, -1, transform, perspective); const [bottomLeftBasicX, bottomLeftBasicY] = viewportCoords.viewportToBasic(-1, -1, transform, perspective); if (insideBasic(topLeftBasicX, topLeftBasicY)) { intersections[3] = intersections[0] = true; } if (insideBasic(topRightBasicX, topRightBasicY)) { intersections[0] = intersections[1] = true; } if (insideBasic(bottomRightBasicX, bottomRightBasicY)) { intersections[1] = intersections[2] = true; } if (insideBasic(bottomLeftBasicX, bottomLeftBasicY)) { intersections[2] = intersections[3] = true; } const maximums = [-1, -1, 1, 1]; for (let visibleBoundaryPoint of visibleBoundaryPoints) { const x = visibleBoundaryPoint[0]; const y = visibleBoundaryPoint[1]; if (x > maximums[1]) { maximums[1] = x; } if (x < maximums[3]) { maximums[3] = x; } if (y > maximums[0]) { maximums[0] = y; } if (y < maximums[2]) { maximums[2] = y; } } const boundary = [1, 1, -1, -1]; const distances = []; for (let side = 0; side < 4; side++) { if (intersections[side]) { distances.push(0); continue; } distances.push(Math.abs(boundary[side] - maximums[side])); } return distances; } /** * The `BounceHandler` ensures that the viewer bounces back to the image * when drag panning outside of the image edge. */ class BounceHandler extends HandlerBase { constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; } _enable() { const inTransition$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.alpha < 1; }), distinctUntilChanged()); this._bounceSubscription = combineLatest(inTransition$, this._navigator.stateService.inTranslation$, this._container.mouseService.active$, this._container.touchService.active$).pipe(map((noForce) => { return noForce[0] || noForce[1] || noForce[2] || noForce[3]; }), distinctUntilChanged(), switchMap((noForce) => { return noForce ? empty() : combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$.pipe(first())); }), withLatestFrom(this._navigator.panService.panImages$)) .subscribe(([[render, transform], nts]) => { if (!transform.hasValidScale && render.camera.focal < 0.1) { return; } if (render.perspective.aspect === 0 || render.perspective.aspect === Number.POSITIVE_INFINITY) { return; } const distances = viewportDistances(transform, render.perspective, this._viewportCoords); const basic = this._viewportCoords.viewportToBasic(0, 0, transform, render.perspective); if ((basic[0] < 0 || basic[0] > 1) && nts.length > 0) { distances[0] = distances[2] = 0; } for (const [, t] of nts) { const d = viewportDistances(t, render.perspective, this._viewportCoords); for (let i = 1; i < distances.length; i += 2) { if (d[i] < distances[i]) { distances[i] = d[i]; } } } if (Math.max(...distances) < 0.01) { return; } const horizontalDistance = distances[1] - distances[3]; const verticalDistance = distances[0] - distances[2]; const currentDirection = this._viewportCoords .unprojectFromViewport(0, 0, render.perspective) .sub(render.perspective.position); const directionPhi = this._viewportCoords .unprojectFromViewport(horizontalDistance, 0, render.perspective) .sub(render.perspective.position); const directionTheta = this._viewportCoords .unprojectFromViewport(0, verticalDistance, render.perspective) .sub(render.perspective.position); let phi = (horizontalDistance > 0 ? 1 : -1) * directionPhi.angleTo(currentDirection); let theta = (verticalDistance > 0 ? 1 : -1) * directionTheta.angleTo(currentDirection); const threshold = Math.PI / 60; const coeff = 1e-1; phi = this._spatial.clamp(coeff * phi, -threshold, threshold); theta = this._spatial.clamp(coeff * theta, -threshold, threshold); this._navigator.stateService.rotateUnbounded({ phi: phi, theta: theta }); }); } _disable() { this._bounceSubscription.unsubscribe(); } _getConfiguration() { return {}; } } class MouseOperator { static filteredPairwiseMouseDrag$(name, mouseService) { return this._filteredPairwiseMouseDrag$(name, mouseService, mouseService.mouseDragStart$, mouseService.mouseDrag$, mouseService.mouseDragEnd$); } static filteredPairwiseMouseRightDrag$(name, mouseService) { return this._filteredPairwiseMouseDrag$(name, mouseService, mouseService.mouseRightDragStart$, mouseService.mouseRightDrag$, mouseService.mouseRightDragEnd$); } static _filteredPairwiseMouseDrag$(name, mouseService, mouseDragStart$, mouseDrag$, mouseDragEnd$) { return mouseService .filtered$(name, mouseDragStart$).pipe(switchMap((mouseDragStart) => { const dragging$ = concat(of(mouseDragStart), mouseService .filtered$(name, mouseDrag$)); const dragEnd$ = mouseService .filtered$(name, mouseDragEnd$).pipe(map(() => { return null; })); return merge(dragging$, dragEnd$).pipe(takeWhile((e) => { return !!e; }), startWith(null)); }), pairwise(), filter((pair) => { return pair[0] != null && pair[1] != null; })); } } /** * The `DragPanHandler` allows the user to pan the viewer image by clicking and dragging the cursor. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.dragPan.disable(); * pointerComponent.dragPan.enable(); * * var isEnabled = pointerComponent.dragPan.isEnabled; * ``` */ class DragPanHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; } _enable() { let draggingStarted$ = this._container.mouseService .filtered$(this._component.name, this._container.mouseService.mouseDragStart$).pipe(map(() => { return true; }), share()); let draggingStopped$ = this._container.mouseService .filtered$(this._component.name, this._container.mouseService.mouseDragEnd$).pipe(map(() => { return false; }), share()); this._activeMouseSubscription = merge(draggingStarted$, draggingStopped$) .subscribe(this._container.mouseService.activate$); const documentMouseMove$ = merge(draggingStarted$, draggingStopped$).pipe(switchMap((dragging) => { return dragging ? this._container.mouseService.documentMouseMove$ : empty(); })); this._preventDefaultSubscription = merge(documentMouseMove$, this._container.touchService.touchMove$) .subscribe((event) => { event.preventDefault(); // prevent selection of content outside the viewer }); let touchMovingStarted$ = this._container.touchService.singleTouchDragStart$.pipe(map(() => { return true; })); let touchMovingStopped$ = this._container.touchService.singleTouchDragEnd$.pipe(map(() => { return false; })); this._activeTouchSubscription = merge(touchMovingStarted$, touchMovingStopped$) .subscribe(this._container.touchService.activate$); const rotation$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return isSpherical(frame.state.currentImage.cameraType) || frame.state.imagesAhead < 1; }), distinctUntilChanged(), switchMap((enable) => { if (!enable) { return empty(); } const mouseDrag$ = MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService); const singleTouchDrag$ = merge(this._container.touchService.singleTouchDragStart$, this._container.touchService.singleTouchDrag$, this._container.touchService.singleTouchDragEnd$.pipe(map(() => { return null; }))).pipe(map((event) => { return event != null && event.touches.length > 0 ? event.touches[0] : null; }), pairwise(), filter((pair) => { return pair[0] != null && pair[1] != null; })); return merge(mouseDrag$, singleTouchDrag$); }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$, this._navigator.panService.panImages$), map(([events, render, transform, nts]) => { let previousEvent = events[0]; let event = events[1]; let movementX = event.clientX - previousEvent.clientX; let movementY = event.clientY - previousEvent.clientY; let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); let currentDirection = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective) .sub(render.perspective.position); let directionX = this._viewportCoords.unprojectFromCanvas(canvasX - movementX, canvasY, element, render.perspective) .sub(render.perspective.position); let directionY = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY - movementY, element, render.perspective) .sub(render.perspective.position); let phi = (movementX > 0 ? 1 : -1) * directionX.angleTo(currentDirection); let theta = (movementY > 0 ? -1 : 1) * directionY.angleTo(currentDirection); const distances = viewportDistances(transform, render.perspective, this._viewportCoords); for (const [, t] of nts) { const d = viewportDistances(t, render.perspective, this._viewportCoords); for (let i = 0; i < distances.length; i++) { if (d[i] < distances[i]) { distances[i] = d[i]; } } } if (distances[0] > 0 && theta < 0) { theta /= Math.max(1, 2e2 * distances[0]); } if (distances[2] > 0 && theta > 0) { theta /= Math.max(1, 2e2 * distances[2]); } if (distances[1] > 0 && phi < 0) { phi /= Math.max(1, 2e2 * distances[1]); } if (distances[3] > 0 && phi > 0) { phi /= Math.max(1, 2e2 * distances[3]); } return { phi: phi, theta: theta }; }), share()); this._rotateWithoutInertiaSubscription = rotation$ .subscribe((rotation) => { this._navigator.stateService.rotateWithoutInertia(rotation); }); this._rotateSubscription = rotation$.pipe(scan((rotationBuffer, rotation) => { this._drainBuffer(rotationBuffer); rotationBuffer.push([Date.now(), rotation]); return rotationBuffer; }, []), sample(merge(this._container.mouseService.filtered$(this._component.name, this._container.mouseService.mouseDragEnd$), this._container.touchService.singleTouchDragEnd$)), map((rotationBuffer) => { const drainedBuffer = this._drainBuffer(rotationBuffer.slice()); const rotation = { phi: 0, theta: 0 }; for (const bufferedRotation of drainedBuffer) { rotation.phi += bufferedRotation[1].phi; rotation.theta += bufferedRotation[1].theta; } const count = drainedBuffer.length; if (count > 0) { rotation.phi /= count; rotation.theta /= count; } const threshold = Math.PI / 18; rotation.phi = this._spatial.clamp(rotation.phi, -threshold, threshold); rotation.theta = this._spatial.clamp(rotation.theta, -threshold, threshold); return rotation; })) .subscribe((rotation) => { this._navigator.stateService.rotate(rotation); }); } _disable() { this._activeMouseSubscription.unsubscribe(); this._activeTouchSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._rotateSubscription.unsubscribe(); this._rotateWithoutInertiaSubscription.unsubscribe(); this._activeMouseSubscription = null; this._activeTouchSubscription = null; this._preventDefaultSubscription = null; this._rotateSubscription = null; } _getConfiguration(enable) { return { dragPan: enable }; } _drainBuffer(buffer) { const cutoff = 50; const now = Date.now(); while (buffer.length > 0 && now - buffer[0][0] > cutoff) { buffer.shift(); } return buffer; } } class EarthControlHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; this._subscriptions = new SubscriptionHolder(); } _enable() { const earth$ = this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(earth$.pipe(switchMap((earth) => { return earth ? this._container.mouseService.mouseWheel$ : empty(); })) .subscribe((event) => { event.preventDefault(); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return !(e1.ctrlKey && e2.ctrlKey); })); }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$), map(([[previous, current], render, transform]) => { const planeNormal = [0, 0, 1]; const planePoint = [0, 0, -2]; const currentIntersection = this._planeIntersection(current, planeNormal, planePoint, render.perspective, this._container.container); const previousIntersection = this._planeIntersection(previous, planeNormal, planePoint, render.perspective, this._container.container); if (!currentIntersection || !previousIntersection) { return null; } const direction = new Vector3() .subVectors(currentIntersection, previousIntersection) .multiplyScalar(-1) .toArray(); return direction; }), filter((direction) => { return !!direction; })) .subscribe((direction) => { this._navigator.stateService.truck(direction); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return e1.ctrlKey && e2.ctrlKey; })); }), map(([previous, current]) => { return this._mousePairToRotation(previous, current); })) .subscribe((rotation) => { this._navigator.stateService.orbit(rotation); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseRightDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return !e1.ctrlKey && !e2.ctrlKey; })); }), map(([previous, current]) => { return this._mousePairToRotation(previous, current); })) .subscribe((rotation) => { this._navigator.stateService.orbit(rotation); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return this._container.mouseService .filteredWheel$(this._component.name, this._container.mouseService.mouseWheel$); }), map((event) => { let delta = event.deltaY; if (event.deltaMode === 1) { delta = 40 * delta; } else if (event.deltaMode === 2) { delta = 800 * delta; } const canvasSize = this._viewportCoords.containerToCanvas(this._container.container); return -delta / canvasSize[1]; })) .subscribe((delta) => { this._navigator.stateService.dolly(delta); })); } _disable() { this._subscriptions.unsubscribe(); } _getConfiguration() { return {}; } _eventToViewport(event, element) { const previousCanvas = this._viewportCoords.canvasPosition(event, element); return this._viewportCoords.canvasToViewport(previousCanvas[0], previousCanvas[1], element); } _mousePairToRotation(previous, current) { const [currentX, currentY] = this._eventToViewport(current, this._container.container); const [previousX, previousY] = this._eventToViewport(previous, this._container.container); const phi = (previousX - currentX) * Math.PI; const theta = (currentY - previousY) * Math.PI / 2; return { phi: phi, theta: theta }; } _planeIntersection(event, planeNormal, planePoint, camera, element) { const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const direction = this._viewportCoords .unprojectFromCanvas(canvasX, canvasY, element, camera) .sub(camera.position) .normalize(); if (Math.abs(this._spatial.angleToPlane(direction.toArray(), planeNormal)) < Math.PI / 90) { return null; } const l0 = camera.position.clone(); const n = new Vector3().fromArray(planeNormal); const p0 = new Vector3().fromArray(planePoint); const d = new Vector3().subVectors(p0, l0).dot(n) / direction.clone().dot(n); const intersection = new Vector3().addVectors(l0, direction.multiplyScalar(d)); if (this._viewportCoords.worldToCamera(intersection.toArray(), camera)[2] > 0) { return null; } return intersection; } } /** * The `ScrollZoomHandler` allows the user to zoom the viewer image by scrolling. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.scrollZoom.disable(); * pointerComponent.scrollZoom.enable(); * * var isEnabled = pointerComponent.scrollZoom.isEnabled; * ``` */ class ScrollZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._container.mouseService.claimWheel(this._component.name, 0); this._preventDefaultSubscription = this._container.mouseService.mouseWheel$ .subscribe((event) => { event.preventDefault(); }); this._zoomSubscription = this._container.mouseService .filteredWheel$(this._component.name, this._container.mouseService.mouseWheel$).pipe(withLatestFrom(this._navigator.stateService.currentState$, (w, f) => { return [w, f]; }), filter((args) => { let state = args[1].state; return isSpherical(state.currentImage.cameraType) || state.imagesAhead < 1; }), map((args) => { return args[0]; }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$, (w, r, t) => { return [w, r, t]; })) .subscribe((args) => { let event = args[0]; let render = args[1]; let transform = args[2]; let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); let unprojected = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective); let reference = transform.projectBasic(unprojected.toArray()); let deltaY = event.deltaY; if (event.deltaMode === 1) { deltaY = 40 * deltaY; } else if (event.deltaMode === 2) { deltaY = 800 * deltaY; } const canvasSize = this._viewportCoords.containerToCanvas(element); let zoom = -3 * deltaY / canvasSize[1]; this._navigator.stateService.zoomIn(zoom, reference); }); } _disable() { this._container.mouseService.unclaimWheel(this._component.name); this._preventDefaultSubscription.unsubscribe(); this._zoomSubscription.unsubscribe(); this._preventDefaultSubscription = null; this._zoomSubscription = null; } _getConfiguration(enable) { return { scrollZoom: enable }; } } /** * The `TouchZoomHandler` allows the user to zoom the viewer image by pinching on a touchscreen. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.touchZoom.disable(); * pointerComponent.touchZoom.enable(); * * var isEnabled = pointerComponent.touchZoom.isEnabled; * ``` */ class TouchZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._preventDefaultSubscription = this._container.touchService.pinch$ .subscribe((pinch) => { pinch.originalEvent.preventDefault(); }); let pinchStarted$ = this._container.touchService.pinchStart$.pipe(map((event) => { return true; })); let pinchStopped$ = this._container.touchService.pinchEnd$.pipe(map((event) => { return false; })); this._activeSubscription = merge(pinchStarted$, pinchStopped$) .subscribe(this._container.touchService.activate$); this._zoomSubscription = this._container.touchService.pinch$.pipe(withLatestFrom(this._navigator.stateService.currentState$), filter((args) => { let state = args[1].state; return isSpherical(state.currentImage.cameraType) || state.imagesAhead < 1; }), map((args) => { return args[0]; }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([pinch, render, transform]) => { let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(pinch, element); let unprojected = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective); let reference = transform.projectBasic(unprojected.toArray()); const [canvasWidth, canvasHeight] = this._viewportCoords.containerToCanvas(element); let zoom = 3 * pinch.distanceChange / Math.min(canvasWidth, canvasHeight); this._navigator.stateService.zoomIn(zoom, reference); }); } _disable() { this._activeSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._zoomSubscription.unsubscribe(); this._preventDefaultSubscription = null; this._zoomSubscription = null; } _getConfiguration(enable) { return { touchZoom: enable }; } } /** * @class PointerComponent * * @classdesc Component handling mouse, pen, and touch events for camera movement. * * To retrive and use the mouse component * * @example * ```js * var viewer = new Viewer({ ... }); * * var pointerComponent = viewer.getComponent("pointer"); * ``` */ class PointerComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); const spatial = new Spatial(); const viewportCoords = new ViewportCoords(); this._bounceHandler = new BounceHandler(this, container, navigator, viewportCoords, spatial); this._dragPanHandler = new DragPanHandler(this, container, navigator, viewportCoords, spatial); this._earthControlHandler = new EarthControlHandler(this, container, navigator, viewportCoords, spatial); this._scrollZoomHandler = new ScrollZoomHandler(this, container, navigator, viewportCoords); this._touchZoomHandler = new TouchZoomHandler(this, container, navigator, viewportCoords); } /** * Get drag pan. * * @returns {DragPanHandler} The drag pan handler. */ get dragPan() { return this._dragPanHandler; } /** * Get earth control. * * @returns {EarthControlHandler} The earth control handler. */ get earthControl() { return this._earthControlHandler; } /** * Get scroll zoom. * * @returns {ScrollZoomHandler} The scroll zoom handler. */ get scrollZoom() { return this._scrollZoomHandler; } /** * Get touch zoom. * * @returns {TouchZoomHandler} The touch zoom handler. */ get touchZoom() { return this._touchZoomHandler; } _activate() { this._bounceHandler.enable(); this._subscriptions.push(this._configuration$ .subscribe((configuration) => { if (configuration.dragPan) { this._dragPanHandler.enable(); } else { this._dragPanHandler.disable(); } if (configuration.earthControl) { this._earthControlHandler.enable(); } else { this._earthControlHandler.disable(); } if (configuration.scrollZoom) { this._scrollZoomHandler.enable(); } else { this._scrollZoomHandler.disable(); } if (configuration.touchZoom) { this._touchZoomHandler.enable(); } else { this._touchZoomHandler.disable(); } })); this._container.mouseService.claimMouse(this._name, 0); } _deactivate() { this._container.mouseService.unclaimMouse(this._name); this._subscriptions.unsubscribe(); this._bounceHandler.disable(); this._dragPanHandler.disable(); this._earthControlHandler.disable(); this._scrollZoomHandler.disable(); this._touchZoomHandler.disable(); } _getDefaultConfiguration() { return { dragPan: true, earthControl: true, scrollZoom: true, touchZoom: true, }; } } /** @inheritdoc */ PointerComponent.componentName = "pointer"; class DOM { constructor(doc) { this._document = !!doc ? doc : document; } get document() { return this._document; } createElement(tagName, className, container) { const element = this._document.createElement(tagName); if (!!className) { element.className = className; } if (!!container) { container.appendChild(element); } return element; } } /** * @class PopupComponent * * @classdesc Component for showing HTML popup objects. * * The `add` method is used for adding new popups. Popups are removed by reference. * * It is not possible to update popups in the set by updating any properties * directly on the popup object. Popups need to be replaced by * removing them and creating new ones with relevant changed properties and * adding those instead. * * Popups are only relevant to a single image because they are based on * 2D basic image coordinates. Popups related to a certain image should * be removed when the viewer is moved to another image. * * To retrive and use the popup component * * @example * ```js * var viewer = new Viewer({ component: { popup: true }, ... }); * * var popupComponent = viewer.getComponent("popup"); * ``` */ class PopupComponent extends Component { /** @ignore */ constructor(name, container, navigator, dom) { super(name, container, navigator); this._dom = !!dom ? dom : new DOM(); this._popups = []; this._added$ = new Subject(); this._popups$ = new Subject(); } /** * Add popups to the popups set. * * @description Adding a new popup never replaces an old one * because they are stored by reference. Adding an already * existing popup has no effect. * * @param {Array} popups - Popups to add. * * @example * ```js * popupComponent.add([popup1, popup2]); * ``` */ add(popups) { for (const popup of popups) { if (this._popups.indexOf(popup) !== -1) { continue; } this._popups.push(popup); if (this._activated) { popup.setParentContainer(this._popupContainer); } } this._added$.next(popups); this._popups$.next(this._popups); } /** * Returns an array of all popups. * * @example * ```js * var popups = popupComponent.getAll(); * ``` */ getAll() { return this._popups.slice(); } /** * Remove popups based on reference from the popup set. * * @param {Array} popups - Popups to remove. * * @example * ```js * popupComponent.remove([popup1, popup2]); * ``` */ remove(popups) { for (const popup of popups) { this._remove(popup); } this._popups$.next(this._popups); } /** * Remove all popups from the popup set. * * @example * ```js * popupComponent.removeAll(); * ``` */ removeAll() { for (const popup of this._popups.slice()) { this._remove(popup); } this._popups$.next(this._popups); } _activate() { this._popupContainer = this._dom.createElement("div", "mapillary-popup-container", this._container.container); for (const popup of this._popups) { popup.setParentContainer(this._popupContainer); } const subs = this._subscriptions; subs.push(combineLatest(this._container.renderService.renderCamera$, this._container.renderService.size$, this._navigator.stateService.currentTransform$) .subscribe(([renderCamera, size, transform]) => { for (const popup of this._popups) { popup.update(renderCamera, size, transform); } })); const changed$ = this._popups$.pipe(startWith(this._popups), switchMap((popups) => { return from(popups).pipe(mergeMap((popup) => { return popup.changed$; })); }), map((popup) => { return [popup]; })); subs.push(merge(this._added$, changed$).pipe(withLatestFrom(this._container.renderService.renderCamera$, this._container.renderService.size$, this._navigator.stateService.currentTransform$)) .subscribe(([popups, renderCamera, size, transform]) => { for (const popup of popups) { popup.update(renderCamera, size, transform); } })); } _deactivate() { this._subscriptions.unsubscribe(); for (const popup of this._popups) { popup.remove(); } this._container.container.removeChild(this._popupContainer); delete this._popupContainer; } _getDefaultConfiguration() { return {}; } _remove(popup) { const index = this._popups.indexOf(popup); if (index === -1) { return; } const removed = this._popups.splice(index, 1)[0]; if (this._activated) { removed.remove(); } } } PopupComponent.componentName = "popup"; /** * Enumeration for graph modes. * @enum {number} * @readonly * @description Modes for the retrieval and caching performed * by the graph service on the graph. */ var GraphMode; (function (GraphMode) { /** * Caching is performed on sequences only and sequence edges are * calculated. Spatial tiles * are not retrieved and spatial edges are not calculated when * caching nodes. Complete sequences are being cached for requested * nodes within the graph. */ GraphMode[GraphMode["Sequence"] = 0] = "Sequence"; /** * Caching is performed with emphasis on spatial data. Sequence edges * as well as spatial edges are cached. Sequence data * is still requested but complete sequences are not being cached * for requested nodes. * * This is the initial mode of the graph service. */ GraphMode[GraphMode["Spatial"] = 1] = "Spatial"; })(GraphMode || (GraphMode = {})); var SequenceMode; (function (SequenceMode) { SequenceMode[SequenceMode["Default"] = 0] = "Default"; SequenceMode[SequenceMode["Playback"] = 1] = "Playback"; SequenceMode[SequenceMode["Timeline"] = 2] = "Timeline"; })(SequenceMode || (SequenceMode = {})); class SequenceDOMRenderer { constructor(container) { this._container = container; this._minThresholdWidth = 320; this._maxThresholdWidth = 1480; this._minThresholdHeight = 240; this._maxThresholdHeight = 820; this._stepperDefaultWidth = 108; this._controlsDefaultWidth = 88; this._defaultHeight = 30; this._expandControls = false; this._mode = SequenceMode.Default; this._speed = 0.5; this._changingSpeed = false; this._index = null; this._changingPosition = false; this._mouseEnterDirection$ = new Subject(); this._mouseLeaveDirection$ = new Subject(); this._notifyChanged$ = new Subject(); this._notifyChangingPositionChanged$ = new Subject(); this._notifySpeedChanged$ = new Subject(); this._notifyIndexChanged$ = new Subject(); } get changed$() { return this._notifyChanged$; } get changingPositionChanged$() { return this._notifyChangingPositionChanged$; } get speed$() { return this._notifySpeedChanged$; } get index$() { return this._notifyIndexChanged$; } get mouseEnterDirection$() { return this._mouseEnterDirection$; } get mouseLeaveDirection$() { return this._mouseLeaveDirection$; } activate() { if (!!this._changingSubscription) { return; } this._changingSubscription = merge(this._container.mouseService.documentMouseUp$, this._container.touchService.touchEnd$.pipe(filter((touchEvent) => { return touchEvent.touches.length === 0; }))) .subscribe(() => { if (this._changingSpeed) { this._changingSpeed = false; } if (this._changingPosition) { this._setChangingPosition(false); } }); } deactivate() { if (!this._changingSubscription) { return; } this._changingSpeed = false; this._changingPosition = false; this._expandControls = false; this._mode = SequenceMode.Default; this._changingSubscription.unsubscribe(); this._changingSubscription = null; } render(edgeStatus, configuration, containerWidth, speed, index, max, playEnabled, component, navigator) { if (configuration.visible === false) { return virtualDom.h("div.mapillary-sequence-container", {}, []); } const stepper = this._createStepper(edgeStatus, configuration, playEnabled, containerWidth, component, navigator); const controls = this._createSequenceControls(containerWidth); const playback = this._createPlaybackControls(containerWidth, speed, component, configuration); const timeline = this._createTimelineControls(containerWidth, index, max); return virtualDom.h("div.mapillary-sequence-container", [stepper, controls, playback, timeline]); } getContainerWidth(size, configuration) { let minWidth = configuration.minWidth; let maxWidth = configuration.maxWidth; if (maxWidth < minWidth) { maxWidth = minWidth; } let relativeWidth = (size.width - this._minThresholdWidth) / (this._maxThresholdWidth - this._minThresholdWidth); let relativeHeight = (size.height - this._minThresholdHeight) / (this._maxThresholdHeight - this._minThresholdHeight); let coeff = Math.max(0, Math.min(1, Math.min(relativeWidth, relativeHeight))); return minWidth + coeff * (maxWidth - minWidth); } _createPositionInput(index, max) { this._index = index; const onPosition = (e) => { this._index = Number(e.target.value); this._notifyIndexChanged$.next(this._index); }; const boundingRect = this._container.domContainer.getBoundingClientRect(); const width = Math.max(276, Math.min(410, 5 + 0.8 * boundingRect.width)) - 65; const onStart = (e) => { e.stopPropagation(); this._setChangingPosition(true); }; const onMove = (e) => { if (this._changingPosition === true) { e.stopPropagation(); } }; const onKeyDown = (e) => { if (e.key === "ArrowDown" || e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); } }; const positionInputProperties = { max: max != null ? max : 1, min: 0, onchange: onPosition, oninput: onPosition, onkeydown: onKeyDown, onpointerdown: onStart, onpointermove: onMove, ontouchmove: onMove, ontouchstart: onStart, style: { width: `${width}px`, }, type: "range", value: index != null ? index : 0, }; const disabled = index == null || max == null || max <= 1; if (disabled) { positionInputProperties.disabled = "true"; } const positionInput = virtualDom.h("input.mapillary-sequence-position", positionInputProperties, []); const positionContainerClass = disabled ? ".mapillary-sequence-position-container-inactive" : ".mapillary-sequence-position-container"; return virtualDom.h("div" + positionContainerClass, [positionInput]); } _createSpeedInput(speed) { this._speed = speed; const onSpeed = (e) => { this._speed = Number(e.target.value) / 1000; this._notifySpeedChanged$.next(this._speed); }; const boundingRect = this._container.domContainer.getBoundingClientRect(); const width = Math.max(276, Math.min(410, 5 + 0.8 * boundingRect.width)) - 160; const onStart = (e) => { this._changingSpeed = true; e.stopPropagation(); }; const onMove = (e) => { if (this._changingSpeed === true) { e.stopPropagation(); } }; const onKeyDown = (e) => { if (e.key === "ArrowDown" || e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); } }; const speedInput = virtualDom.h("input.mapillary-sequence-speed", { max: 1000, min: 0, onchange: onSpeed, oninput: onSpeed, onkeydown: onKeyDown, onpointerdown: onStart, onpointermove: onMove, ontouchmove: onMove, ontouchstart: onStart, style: { width: `${width}px`, }, type: "range", value: 1000 * speed, }, []); return virtualDom.h("div.mapillary-sequence-speed-container", [speedInput]); } _createPlaybackControls(containerWidth, speed, component, configuration) { if (this._mode !== SequenceMode.Playback) { return virtualDom.h("div.mapillary-sequence-playback", []); } const switchIcon = virtualDom.h("div.mapillary-sequence-switch-icon.mapillary-sequence-icon-visible", []); const direction = configuration.direction === exports.NavigationDirection.Next ? exports.NavigationDirection.Prev : exports.NavigationDirection.Next; const playing = configuration.playing; const switchButtonProperties = { onclick: () => { if (!playing) { component.configure({ direction }); } }, }; const switchButtonClassName = configuration.playing ? ".mapillary-sequence-switch-button-inactive" : ".mapillary-sequence-switch-button"; const switchButton = virtualDom.h("div" + switchButtonClassName, switchButtonProperties, [switchIcon]); const slowIcon = virtualDom.h("div.mapillary-sequence-slow-icon.mapillary-sequence-icon-visible", []); const slowContainer = virtualDom.h("div.mapillary-sequence-slow-container", [slowIcon]); const fastIcon = virtualDom.h("div.mapillary-sequence-fast-icon.mapillary-sequence-icon-visible", []); const fastContainer = virtualDom.h("div.mapillary-sequence-fast-container", [fastIcon]); const closeIcon = virtualDom.h("div.mapillary-sequence-close-icon.mapillary-sequence-icon-visible", []); const closeButtonProperties = { onclick: () => { this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, }; const closeButton = virtualDom.h("div.mapillary-sequence-close-button", closeButtonProperties, [closeIcon]); const speedInput = this._createSpeedInput(speed); const playbackChildren = [switchButton, slowContainer, speedInput, fastContainer, closeButton]; const top = Math.round(containerWidth / this._stepperDefaultWidth * this._defaultHeight + 10); const playbackProperties = { style: { top: `${top}px` } }; return virtualDom.h("div.mapillary-sequence-playback", playbackProperties, playbackChildren); } _createPlayingButton(nextId, prevId, playEnabled, configuration, component) { let canPlay = (configuration.direction === exports.NavigationDirection.Next && nextId != null) || (configuration.direction === exports.NavigationDirection.Prev && prevId != null); canPlay = canPlay && playEnabled; let onclick = configuration.playing ? () => { component.stop(); } : canPlay ? () => { component.play(); } : null; let buttonProperties = { onclick: onclick }; let iconProperties = {}; if (configuration.direction === exports.NavigationDirection.Prev) { iconProperties.style = { transform: "rotate(180deg) translate(50%, 50%)", }; } let icon = virtualDom.h("div.mapillary-sequence-icon", iconProperties, []); let buttonClass = configuration.playing ? "mapillary-sequence-stop" : canPlay ? "mapillary-sequence-play" : "mapillary-sequence-play-inactive"; return virtualDom.h("div." + buttonClass, buttonProperties, [icon]); } _createSequenceControls(containerWidth) { const borderRadius = Math.round(8 / this._stepperDefaultWidth * containerWidth); const expanderProperties = { onclick: () => { this._expandControls = !this._expandControls; this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, style: { "border-bottom-right-radius": `${borderRadius}px`, "border-top-right-radius": `${borderRadius}px`, }, }; const expanderBar = virtualDom.h("div.mapillary-sequence-expander-bar", []); const expander = virtualDom.h("div.mapillary-sequence-expander-button", expanderProperties, [expanderBar]); const fastIconClassName = this._mode === SequenceMode.Playback ? ".mapillary-sequence-fast-icon-gray.mapillary-sequence-icon-visible" : ".mapillary-sequence-fast-icon"; const fastIcon = virtualDom.h("div" + fastIconClassName, []); const playbackProperties = { onclick: () => { this._mode = this._mode === SequenceMode.Playback ? SequenceMode.Default : SequenceMode.Playback; this._notifyChanged$.next(this); }, }; const playback = virtualDom.h("div.mapillary-sequence-playback-button", playbackProperties, [fastIcon]); const timelineIconClassName = this._mode === SequenceMode.Timeline ? ".mapillary-sequence-timeline-icon-gray.mapillary-sequence-icon-visible" : ".mapillary-sequence-timeline-icon"; const timelineIcon = virtualDom.h("div" + timelineIconClassName, []); const timelineProperties = { onclick: () => { this._mode = this._mode === SequenceMode.Timeline ? SequenceMode.Default : SequenceMode.Timeline; this._notifyChanged$.next(this); }, }; const timeline = virtualDom.h("div.mapillary-sequence-timeline-button", timelineProperties, [timelineIcon]); const properties = { style: { height: (this._defaultHeight / this._stepperDefaultWidth * containerWidth) + "px", transform: `translate(${containerWidth / 2 + 2}px, 0)`, width: (this._controlsDefaultWidth / this._stepperDefaultWidth * containerWidth) + "px", }, }; const className = ".mapillary-sequence-controls" + (this._expandControls ? ".mapillary-sequence-controls-expanded" : ""); return virtualDom.h("div" + className, properties, [playback, timeline, expander]); } _createSequenceArrows(nextId, prevId, containerWidth, configuration, navigator) { let nextProperties = { onclick: nextId != null ? () => { navigator.moveDir$(exports.NavigationDirection.Next) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } : null, onpointerenter: () => { this._mouseEnterDirection$.next(exports.NavigationDirection.Next); }, onpointerleave: () => { this._mouseLeaveDirection$.next(exports.NavigationDirection.Next); }, }; const borderRadius = Math.round(8 / this._stepperDefaultWidth * containerWidth); let prevProperties = { onclick: prevId != null ? () => { navigator.moveDir$(exports.NavigationDirection.Prev) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } : null, onpointerenter: () => { this._mouseEnterDirection$.next(exports.NavigationDirection.Prev); }, onpointerleave: () => { this._mouseLeaveDirection$.next(exports.NavigationDirection.Prev); }, style: { "border-bottom-left-radius": `${borderRadius}px`, "border-top-left-radius": `${borderRadius}px`, }, }; let nextClass = this._getStepClassName(exports.NavigationDirection.Next, nextId, configuration.highlightId); let prevClass = this._getStepClassName(exports.NavigationDirection.Prev, prevId, configuration.highlightId); let nextIcon = virtualDom.h("div.mapillary-sequence-icon", []); let prevIcon = virtualDom.h("div.mapillary-sequence-icon", []); return [ virtualDom.h("div." + prevClass, prevProperties, [prevIcon]), virtualDom.h("div." + nextClass, nextProperties, [nextIcon]), ]; } _createStepper(edgeStatus, configuration, playEnabled, containerWidth, component, navigator) { let nextId = null; let prevId = null; for (let edge of edgeStatus.edges) { if (edge.data.direction === exports.NavigationDirection.Next) { nextId = edge.target; } if (edge.data.direction === exports.NavigationDirection.Prev) { prevId = edge.target; } } const playingButton = this._createPlayingButton(nextId, prevId, playEnabled, configuration, component); const buttons = this._createSequenceArrows(nextId, prevId, containerWidth, configuration, navigator); buttons.splice(1, 0, playingButton); const containerProperties = { oncontextmenu: (event) => { event.preventDefault(); }, style: { height: (this._defaultHeight / this._stepperDefaultWidth * containerWidth) + "px", width: containerWidth + "px", }, }; return virtualDom.h("div.mapillary-sequence-stepper", containerProperties, buttons); } _createTimelineControls(containerWidth, index, max) { if (this._mode !== SequenceMode.Timeline) { return virtualDom.h("div.mapillary-sequence-timeline", []); } const positionInput = this._createPositionInput(index, max); const closeIcon = virtualDom.h("div.mapillary-sequence-close-icon.mapillary-sequence-icon-visible", []); const closeButtonProperties = { onclick: () => { this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, }; const closeButton = virtualDom.h("div.mapillary-sequence-close-button", closeButtonProperties, [closeIcon]); const top = Math.round(containerWidth / this._stepperDefaultWidth * this._defaultHeight + 10); const playbackProperties = { style: { top: `${top}px` } }; return virtualDom.h("div.mapillary-sequence-timeline", playbackProperties, [positionInput, closeButton]); } _getStepClassName(direction, imageId, highlightId) { let className = direction === exports.NavigationDirection.Next ? "mapillary-sequence-step-next" : "mapillary-sequence-step-prev"; if (imageId == null) { className += "-inactive"; } else { if (highlightId === imageId) { className += "-highlight"; } } return className; } _setChangingPosition(value) { this._changingPosition = value; this._notifyChangingPositionChanged$.next(value); } } /** * @class DataProviderBase * * @classdesc Base class to extend if implementing a data provider * class. * * @fires datacreate * * @example * ```js * class MyDataProvider extends DataProviderBase { * constructor() { * super(new S2GeometryProvider()); * } * ... * } * ``` */ class DataProviderBase extends EventEmitter { /** * Create a new data provider base instance. * * @param {IGeometryProvider} geometry - Geometry * provider instance. */ constructor(_geometry) { super(); this._geometry = _geometry; } /** * Get geometry property. * * @returns {IGeometryProvider} Geometry provider instance. */ get geometry() { return this._geometry; } fire(type, event) { super.fire(type, event); } /** * Get core images in a geometry cell. * * @param {string} cellId - The id of the geometry cell. * @returns {Promise} Promise to * the core images of the requested geometry cell id. * @throws Rejects the promise on errors. */ getCoreImages(cellId) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get a cluster reconstruction. * * @param {string} url - URL for the cluster reconstruction * to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the * cluster reconstruction. * @throws Rejects the promise on errors. */ getCluster(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get spatial images. * * @param {Array} imageIds - The ids for the * images to retrieve. * @returns {Promise} Promise to * the spatial images of the requested image ids. * @throws Rejects the promise on errors. */ getSpatialImages(imageIds) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get complete images. * * @param {Array} imageIds - The ids for the * images to retrieve. * @returns {Promise} Promise to the images of the * requested image ids. * @throws Rejects the promise on errors. */ getImages(imageIds) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get an image as an array buffer. * * @param {string} url - URL for image to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the array * buffer containing the image. * @throws Rejects the promise on errors. */ getImageBuffer(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get image tiles urls for a tile level. * * @param {ImageTilesRequestContract} tiles - Tiles to request * @returns {Promise} Promise to the * image tiles response contract * * @throws Rejects the promise on errors. * * @example * ```js * var tileRequest = { imageId: "image-id", z: 12 }; * provider.getImageTiles(tileRequest) * .then((response) => console.log(response)); * ``` */ getImageTiles(tiles) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get a mesh. * * @param {string} url - URL for mesh to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the mesh. * @throws Rejects the promise on errors. */ getMesh(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get sequence. * * @param {Array} sequenceId - The id for the * sequence to retrieve. * @returns {Promise} Promise to the sequences of the * requested image ids. * @throws Rejects the promise on errors. */ getSequence(sequenceId) { return Promise.reject(new MapillaryError("Not implemented")); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Set an access token for authenticated API requests of * protected resources. * * @param {string} [accessToken] accessToken - User access * token or client access token. */ setAccessToken(accessToken) { throw new MapillaryError("Not implemented"); } } var s2geometry = {exports: {}}; function commonjsRequire(path) { throw new Error("Could not dynamically require "" + path + "". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work."); } var long = {exports: {}}; /* Copyright 2013 Daniel Wirtz Copyright 2009 The Closure Library Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ var hasRequiredLong; function requireLong () { if (hasRequiredLong) return long.exports; hasRequiredLong = 1; (function (module) { /** * @license long.js (c) 2013 Daniel Wirtz * Released under the Apache License, Version 2.0 * see: https://github.com/dcodeIO/long.js for details */ (function(global, factory) { /* AMD */ if (typeof commonjsRequire === "function" && "object" === "object" && module && module["exports"]) module["exports"] = factory(); /* Global */ else (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = factory(); })(commonjsGlobal, function() { /** * Constructs a 64 bit two"s-complement integer, given its low and high 32 bit values as *signed* integers. * See the from* functions below for more convenient ways of constructing Longs. * @exports Long * @class A Long class for representing a 64 bit two"s-complement integer value. * @param {number} low The low (signed) 32 bits of the long * @param {number} high The high (signed) 32 bits of the long * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @constructor */ function Long(low, high, unsigned) { /** * The low 32 bits as a signed value. * @type {number} */ this.low = low | 0; /** * The high 32 bits as a signed value. * @type {number} */ this.high = high | 0; /** * Whether unsigned or not. * @type {boolean} */ this.unsigned = !!unsigned; } // The internal representation of a long is the two given signed, 32-bit values. // We use 32-bit pieces because these are the size of integers on which // Javascript performs bit-operations. For operations like addition and // multiplication, we split each number into 16 bit pieces, which can easily be // multiplied within Javascript"s floating-point representation without overflow // or change in sign. // // In the algorithms below, we frequently reduce the negative case to the // positive case by negating the input(s) and then post-processing the result. // Note that we must ALWAYS check specially whether those values are MIN_VALUE // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as // a positive number, it overflows back into a negative). Not handling this // case would often result in infinite recursion. // // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* // methods on which they depend. /** * An indicator used to reliably determine if an object is a Long or not. * @type {boolean} * @const * @private */ Long.prototype.__isLong__; Object.defineProperty(Long.prototype, "__isLong__", { value: true, enumerable: false, configurable: false }); /** * @function * @param {*} obj Object * @returns {boolean} * @inner */ function isLong(obj) { return (obj && obj["__isLong__"]) === true; } /** * Tests if the specified object is a Long. * @function * @param {*} obj Object * @returns {boolean} */ Long.isLong = isLong; /** * A cache of the Long representations of small integer values. * @type {!Object} * @inner */ var INT_CACHE = {}; /** * A cache of the Long representations of small unsigned integer values. * @type {!Object} * @inner */ var UINT_CACHE = {}; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromInt(value, unsigned) { var obj, cachedObj, cache; if (unsigned) { value >>>= 0; if (cache = (0 <= value && value < 256)) { cachedObj = UINT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); if (cache) UINT_CACHE[value] = obj; return obj; } else { value |= 0; if (cache = (-128 <= value && value < 128)) { cachedObj = INT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, value < 0 ? -1 : 0, false); if (cache) INT_CACHE[value] = obj; return obj; } } /** * Returns a Long representing the given 32 bit integer value. * @function * @param {number} value The 32 bit integer in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromInt = fromInt; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromNumber(value, unsigned) { if (isNaN(value) || !isFinite(value)) return unsigned ? UZERO : ZERO; if (unsigned) { if (value < 0) return UZERO; if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE; } else { if (value <= -TWO_PWR_63_DBL) return MIN_VALUE; if (value + 1 >= TWO_PWR_63_DBL) return MAX_VALUE; } if (value < 0) return fromNumber(-value, unsigned).neg(); return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); } /** * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. * @function * @param {number} value The number in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromNumber = fromNumber; /** * @param {number} lowBits * @param {number} highBits * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromBits(lowBits, highBits, unsigned) { return new Long(lowBits, highBits, unsigned); } /** * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is * assumed to use 32 bits. * @function * @param {number} lowBits The low 32 bits * @param {number} highBits The high 32 bits * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromBits = fromBits; /** * @function * @param {number} base * @param {number} exponent * @returns {number} * @inner */ var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4) /** * @param {string} str * @param {(boolean|number)=} unsigned * @param {number=} radix * @returns {!Long} * @inner */ function fromString(str, unsigned, radix) { if (str.length === 0) throw Error("empty string"); if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") return ZERO; if (typeof unsigned === "number") { // For goog.math.long compatibility radix = unsigned, unsigned = false; } else { unsigned = !! unsigned; } radix = radix || 10; if (radix < 2 || 36 < radix) throw RangeError("radix"); var p; if ((p = str.indexOf("-")) > 0) throw Error("interior hyphen"); else if (p === 0) { return fromString(str.substring(1), unsigned, radix).neg(); } // Do several (8) digits each time through the loop, so as to // minimize the calls to the very expensive emulated div. var radixToPower = fromNumber(pow_dbl(radix, 8)); var result = ZERO; for (var i = 0; i < str.length; i += 8) { var size = Math.min(8, str.length - i), value = parseInt(str.substring(i, i + size), radix); if (size < 8) { var power = fromNumber(pow_dbl(radix, size)); result = result.mul(power).add(fromNumber(value)); } else { result = result.mul(radixToPower); result = result.add(fromNumber(value)); } } result.unsigned = unsigned; return result; } /** * Returns a Long representation of the given string, written using the specified radix. * @function * @param {string} str The textual representation of the Long * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 * @returns {!Long} The corresponding Long value */ Long.fromString = fromString; /** * @function * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val * @returns {!Long} * @inner */ function fromValue(val) { if (val /* is compatible */ instanceof Long) return val; if (typeof val === "number") return fromNumber(val); if (typeof val === "string") return fromString(val); // Throws for non-objects, converts non-instanceof Long: return fromBits(val.low, val.high, val.unsigned); } /** * Converts the specified value to a Long. * @function * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value * @returns {!Long} */ Long.fromValue = fromValue; // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be // no runtime penalty for these. /** * @type {number} * @const * @inner */ var TWO_PWR_16_DBL = 1 << 16; /** * @type {number} * @const * @inner */ var TWO_PWR_24_DBL = 1 << 24; /** * @type {number} * @const * @inner */ var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; /** * @type {number} * @const * @inner */ var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; /** * @type {number} * @const * @inner */ var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; /** * @type {!Long} * @const * @inner */ var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL); /** * @type {!Long} * @inner */ var ZERO = fromInt(0); /** * Signed zero. * @type {!Long} */ Long.ZERO = ZERO; /** * @type {!Long} * @inner */ var UZERO = fromInt(0, true); /** * Unsigned zero. * @type {!Long} */ Long.UZERO = UZERO; /** * @type {!Long} * @inner */ var ONE = fromInt(1); /** * Signed one. * @type {!Long} */ Long.ONE = ONE; /** * @type {!Long} * @inner */ var UONE = fromInt(1, true); /** * Unsigned one. * @type {!Long} */ Long.UONE = UONE; /** * @type {!Long} * @inner */ var NEG_ONE = fromInt(-1); /** * Signed negative one. * @type {!Long} */ Long.NEG_ONE = NEG_ONE; /** * @type {!Long} * @inner */ var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); /** * Maximum signed value. * @type {!Long} */ Long.MAX_VALUE = MAX_VALUE; /** * @type {!Long} * @inner */ var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); /** * Maximum unsigned value. * @type {!Long} */ Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE; /** * @type {!Long} * @inner */ var MIN_VALUE = fromBits(0, 0x80000000|0, false); /** * Minimum signed value. * @type {!Long} */ Long.MIN_VALUE = MIN_VALUE; /** * @alias Long.prototype * @inner */ var LongPrototype = Long.prototype; /** * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. * @returns {number} */ LongPrototype.toInt = function toInt() { return this.unsigned ? this.low >>> 0 : this.low; }; /** * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). * @returns {number} */ LongPrototype.toNumber = function toNumber() { if (this.unsigned) return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); return this.high * TWO_PWR_32_DBL + (this.low >>> 0); }; /** * Converts the Long to a string written in the specified radix. * @param {number=} radix Radix (2-36), defaults to 10 * @returns {string} * @override * @throws {RangeError} If `radix` is out of range */ LongPrototype.toString = function toString(radix) { radix = radix || 10; if (radix < 2 || 36 < radix) throw RangeError("radix"); if (this.isZero()) return "0"; if (this.isNegative()) { // Unsigned Longs are never negative if (this.eq(MIN_VALUE)) { // We need to change the Long value before it can be negated, so we remove // the bottom-most digit in this base and then recurse to do the rest. var radixLong = fromNumber(radix), div = this.div(radixLong), rem1 = div.mul(radixLong).sub(this); return div.toString(radix) + rem1.toInt().toString(radix); } else return "-" + this.neg().toString(radix); } // Do several (6) digits each time through the loop, so as to // minimize the calls to the very expensive emulated div. var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned), rem = this; var result = ""; while (true) { var remDiv = rem.div(radixToPower), intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0, digits = intval.toString(radix); rem = remDiv; if (rem.isZero()) return digits + result; else { while (digits.length < 6) digits = "0" + digits; result = "" + digits + result; } } }; /** * Gets the high 32 bits as a signed integer. * @returns {number} Signed high bits */ LongPrototype.getHighBits = function getHighBits() { return this.high; }; /** * Gets the high 32 bits as an unsigned integer. * @returns {number} Unsigned high bits */ LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() { return this.high >>> 0; }; /** * Gets the low 32 bits as a signed integer. * @returns {number} Signed low bits */ LongPrototype.getLowBits = function getLowBits() { return this.low; }; /** * Gets the low 32 bits as an unsigned integer. * @returns {number} Unsigned low bits */ LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() { return this.low >>> 0; }; /** * Gets the number of bits needed to represent the absolute value of this Long. * @returns {number} */ LongPrototype.getNumBitsAbs = function getNumBitsAbs() { if (this.isNegative()) // Unsigned Longs are never negative return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs(); var val = this.high != 0 ? this.high : this.low; for (var bit = 31; bit > 0; bit--) if ((val & (1 << bit)) != 0) break; return this.high != 0 ? bit + 33 : bit + 1; }; /** * Tests if this Long"s value equals zero. * @returns {boolean} */ LongPrototype.isZero = function isZero() { return this.high === 0 && this.low === 0; }; /** * Tests if this Long"s value is negative. * @returns {boolean} */ LongPrototype.isNegative = function isNegative() { return !this.unsigned && this.high < 0; }; /** * Tests if this Long"s value is positive. * @returns {boolean} */ LongPrototype.isPositive = function isPositive() { return this.unsigned || this.high >= 0; }; /** * Tests if this Long"s value is odd. * @returns {boolean} */ LongPrototype.isOdd = function isOdd() { return (this.low & 1) === 1; }; /** * Tests if this Long"s value is even. * @returns {boolean} */ LongPrototype.isEven = function isEven() { return (this.low & 1) === 0; }; /** * Tests if this Long"s value equals the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.equals = function equals(other) { if (!isLong(other)) other = fromValue(other); if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) return false; return this.high === other.high && this.low === other.low; }; /** * Tests if this Long"s value equals the specified"s. This is an alias of {@link Long#equals}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.eq = LongPrototype.equals; /** * Tests if this Long"s value differs from the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.notEquals = function notEquals(other) { return !this.eq(/* validates */ other); }; /** * Tests if this Long"s value differs from the specified"s. This is an alias of {@link Long#notEquals}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.neq = LongPrototype.notEquals; /** * Tests if this Long"s value is less than the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lessThan = function lessThan(other) { return this.comp(/* validates */ other) < 0; }; /** * Tests if this Long"s value is less than the specified"s. This is an alias of {@link Long#lessThan}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lt = LongPrototype.lessThan; /** * Tests if this Long"s value is less than or equal the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) { return this.comp(/* validates */ other) <= 0; }; /** * Tests if this Long"s value is less than or equal the specified"s. This is an alias of {@link Long#lessThanOrEqual}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lte = LongPrototype.lessThanOrEqual; /** * Tests if this Long"s value is greater than the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.greaterThan = function greaterThan(other) { return this.comp(/* validates */ other) > 0; }; /** * Tests if this Long"s value is greater than the specified"s. This is an alias of {@link Long#greaterThan}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.gt = LongPrototype.greaterThan; /** * Tests if this Long"s value is greater than or equal the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) { return this.comp(/* validates */ other) >= 0; }; /** * Tests if this Long"s value is greater than or equal the specified"s. This is an alias of {@link Long#greaterThanOrEqual}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.gte = LongPrototype.greaterThanOrEqual; /** * Compares this Long"s value with the specified"s. * @param {!Long|number|string} other Other value * @returns {number} 0 if they are the same, 1 if the this is greater and -1 * if the given one is greater */ LongPrototype.compare = function compare(other) { if (!isLong(other)) other = fromValue(other); if (this.eq(other)) return 0; var thisNeg = this.isNegative(), otherNeg = other.isNegative(); if (thisNeg && !otherNeg) return -1; if (!thisNeg && otherNeg) return 1; // At this point the sign bits are the same if (!this.unsigned) return this.sub(other).isNegative() ? -1 : 1; // Both are positive if at least one is unsigned return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; }; /** * Compares this Long"s value with the specified"s. This is an alias of {@link Long#compare}. * @function * @param {!Long|number|string} other Other value * @returns {number} 0 if they are the same, 1 if the this is greater and -1 * if the given one is greater */ LongPrototype.comp = LongPrototype.compare; /** * Negates this Long"s value. * @returns {!Long} Negated Long */ LongPrototype.negate = function negate() { if (!this.unsigned && this.eq(MIN_VALUE)) return MIN_VALUE; return this.not().add(ONE); }; /** * Negates this Long"s value. This is an alias of {@link Long#negate}. * @function * @returns {!Long} Negated Long */ LongPrototype.neg = LongPrototype.negate; /** * Returns the sum of this and the specified Long. * @param {!Long|number|string} addend Addend * @returns {!Long} Sum */ LongPrototype.add = function add(addend) { if (!isLong(addend)) addend = fromValue(addend); // Divide each number into 4 chunks of 16 bits, and then sum the chunks. var a48 = this.high >>> 16; var a32 = this.high & 0xFFFF; var a16 = this.low >>> 16; var a00 = this.low & 0xFFFF; var b48 = addend.high >>> 16; var b32 = addend.high & 0xFFFF; var b16 = addend.low >>> 16; var b00 = addend.low & 0xFFFF; var c48 = 0, c32 = 0, c16 = 0, c00 = 0; c00 += a00 + b00; c16 += c00 >>> 16; c00 &= 0xFFFF; c16 += a16 + b16; c32 += c16 >>> 16; c16 &= 0xFFFF; c32 += a32 + b32; c48 += c32 >>> 16; c32 &= 0xFFFF; c48 += a48 + b48; c48 &= 0xFFFF; return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); }; /** * Returns the difference of this and the specified Long. * @param {!Long|number|string} subtrahend Subtrahend * @returns {!Long} Difference */ LongPrototype.subtract = function subtract(subtrahend) { if (!isLong(subtrahend)) subtrahend = fromValue(subtrahend); return this.add(subtrahend.neg()); }; /** * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}. * @function * @param {!Long|number|string} subtrahend Subtrahend * @returns {!Long} Difference */ LongPrototype.sub = LongPrototype.subtract; /** * Returns the product of this and the specified Long. * @param {!Long|number|string} multiplier Multiplier * @returns {!Long} Product */ LongPrototype.multiply = function multiply(multiplier) { if (this.isZero()) return ZERO; if (!isLong(multiplier)) multiplier = fromValue(multiplier); if (multiplier.isZero()) return ZERO; if (this.eq(MIN_VALUE)) return multiplier.isOdd() ? MIN_VALUE : ZERO; if (multiplier.eq(MIN_VALUE)) return this.isOdd() ? MIN_VALUE : ZERO; if (this.isNegative()) { if (multiplier.isNegative()) return this.neg().mul(multiplier.neg()); else return this.neg().mul(multiplier).neg(); } else if (multiplier.isNegative()) return this.mul(multiplier.neg()).neg(); // If both longs are small, use float multiplication if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24)) return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. // We can skip products that would overflow. var a48 = this.high >>> 16; var a32 = this.high & 0xFFFF; var a16 = this.low >>> 16; var a00 = this.low & 0xFFFF; var b48 = multiplier.high >>> 16; var b32 = multiplier.high & 0xFFFF; var b16 = multiplier.low >>> 16; var b00 = multiplier.low & 0xFFFF; var c48 = 0, c32 = 0, c16 = 0, c00 = 0; c00 += a00 * b00; c16 += c00 >>> 16; c00 &= 0xFFFF; c16 += a16 * b00; c32 += c16 >>> 16; c16 &= 0xFFFF; c16 += a00 * b16; c32 += c16 >>> 16; c16 &= 0xFFFF; c32 += a32 * b00; c48 += c32 >>> 16; c32 &= 0xFFFF; c32 += a16 * b16; c48 += c32 >>> 16; c32 &= 0xFFFF; c32 += a00 * b32; c48 += c32 >>> 16; c32 &= 0xFFFF; c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; c48 &= 0xFFFF; return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); }; /** * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}. * @function * @param {!Long|number|string} multiplier Multiplier * @returns {!Long} Product */ LongPrototype.mul = LongPrototype.multiply; /** * Returns this Long divided by the specified. The result is signed if this Long is signed or * unsigned if this Long is unsigned. * @param {!Long|number|string} divisor Divisor * @returns {!Long} Quotient */ LongPrototype.divide = function divide(divisor) { if (!isLong(divisor)) divisor = fromValue(divisor); if (divisor.isZero()) throw Error("division by zero"); if (this.isZero()) return this.unsigned ? UZERO : ZERO; var approx, rem, res; if (!this.unsigned) { // This section is only relevant for signed longs and is derived from the // closure library as a whole. if (this.eq(MIN_VALUE)) { if (divisor.eq(ONE) || divisor.eq(NEG_ONE)) return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE else if (divisor.eq(MIN_VALUE)) return ONE; else { // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. var halfThis = this.shr(1); approx = halfThis.div(divisor).shl(1); if (approx.eq(ZERO)) { return divisor.isNegative() ? ONE : NEG_ONE; } else { rem = this.sub(divisor.mul(approx)); res = approx.add(rem.div(divisor)); return res; } } } else if (divisor.eq(MIN_VALUE)) return this.unsigned ? UZERO : ZERO; if (this.isNegative()) { if (divisor.isNegative()) return this.neg().div(divisor.neg()); return this.neg().div(divisor).neg(); } else if (divisor.isNegative()) return this.div(divisor.neg()).neg(); res = ZERO; } else { // The algorithm below has not been made for unsigned longs. It"s therefore // required to take special care of the MSB prior to running it. if (!divisor.unsigned) divisor = divisor.toUnsigned(); if (divisor.gt(this)) return UZERO; if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true return UONE; res = UZERO; } // Repeat the following until the remainder is less than other: find a // floating-point that approximates remainder / other *from below*, add this // into the result, and subtract it from the remainder. It is critical that // the approximate value is less than or equal to the real value so that the // remainder never becomes negative. rem = this; while (rem.gte(divisor)) { // Approximate the result of division. This may be a little greater or // smaller than the actual value. approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); // We will tweak the approximate result by changing it in the 48-th digit or // the smallest non-fractional digit, whichever is larger. var log2 = Math.ceil(Math.log(approx) / Math.LN2), delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48), // Decrease the approximation until it is smaller than the remainder. Note // that if it is too large, the product overflows and is negative. approxRes = fromNumber(approx), approxRem = approxRes.mul(divisor); while (approxRem.isNegative() || approxRem.gt(rem)) { approx -= delta; approxRes = fromNumber(approx, this.unsigned); approxRem = approxRes.mul(divisor); } // We know the answer can"t be zero... and actually, zero would cause // infinite recursion since we would make no progress. if (approxRes.isZero()) approxRes = ONE; res = res.add(approxRes); rem = rem.sub(approxRem); } return res; }; /** * Returns this Long divided by the specified. This is an alias of {@link Long#divide}. * @function * @param {!Long|number|string} divisor Divisor * @returns {!Long} Quotient */ LongPrototype.div = LongPrototype.divide; /** * Returns this Long modulo the specified. * @param {!Long|number|string} divisor Divisor * @returns {!Long} Remainder */ LongPrototype.modulo = function modulo(divisor) { if (!isLong(divisor)) divisor = fromValue(divisor); return this.sub(this.div(divisor).mul(divisor)); }; /** * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. * @function * @param {!Long|number|string} divisor Divisor * @returns {!Long} Remainder */ LongPrototype.mod = LongPrototype.modulo; /** * Returns the bitwise NOT of this Long. * @returns {!Long} */ LongPrototype.not = function not() { return fromBits(~this.low, ~this.high, this.unsigned); }; /** * Returns the bitwise AND of this Long and the specified. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.and = function and(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low & other.low, this.high & other.high, this.unsigned); }; /** * Returns the bitwise OR of this Long and the specified. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.or = function or(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low | other.low, this.high | other.high, this.unsigned); }; /** * Returns the bitwise XOR of this Long and the given one. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.xor = function xor(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); }; /** * Returns this Long with bits shifted to the left by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftLeft = function shiftLeft(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); if ((numBits &= 63) === 0) return this; else if (numBits < 32) return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); else return fromBits(0, this.low << (numBits - 32), this.unsigned); }; /** * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shl = LongPrototype.shiftLeft; /** * Returns this Long with bits arithmetically shifted to the right by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftRight = function shiftRight(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); if ((numBits &= 63) === 0) return this; else if (numBits < 32) return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); else return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); }; /** * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shr = LongPrototype.shiftRight; /** * Returns this Long with bits logically shifted to the right by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); numBits &= 63; if (numBits === 0) return this; else { var high = this.high; if (numBits < 32) { var low = this.low; return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); } else if (numBits === 32) return fromBits(high, 0, this.unsigned); else return fromBits(high >>> (numBits - 32), 0, this.unsigned); } }; /** * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shru = LongPrototype.shiftRightUnsigned; /** * Converts this Long to signed. * @returns {!Long} Signed long */ LongPrototype.toSigned = function toSigned() { if (!this.unsigned) return this; return fromBits(this.low, this.high, false); }; /** * Converts this Long to unsigned. * @returns {!Long} Unsigned long */ LongPrototype.toUnsigned = function toUnsigned() { if (this.unsigned) return this; return fromBits(this.low, this.high, true); }; /** * Converts this Long to its byte representation. * @param {boolean=} le Whether little or big endian, defaults to big endian * @returns {!Array.} Byte representation */ LongPrototype.toBytes = function(le) { return le ? this.toBytesLE() : this.toBytesBE(); }; /** * Converts this Long to its little endian byte representation. * @returns {!Array.} Little endian byte representation */ LongPrototype.toBytesLE = function() { var hi = this.high, lo = this.low; return [ lo & 0xff, (lo >>> 8) & 0xff, (lo >>> 16) & 0xff, (lo >>> 24) & 0xff, hi & 0xff, (hi >>> 8) & 0xff, (hi >>> 16) & 0xff, (hi >>> 24) & 0xff ]; }; /** * Converts this Long to its big endian byte representation. * @returns {!Array.} Big endian byte representation */ LongPrototype.toBytesBE = function() { var hi = this.high, lo = this.low; return [ (hi >>> 24) & 0xff, (hi >>> 16) & 0xff, (hi >>> 8) & 0xff, hi & 0xff, (lo >>> 24) & 0xff, (lo >>> 16) & 0xff, (lo >>> 8) & 0xff, lo & 0xff ]; }; return Long; }); } (long)); return long.exports; } (function (module) { /// S2 Geometry functions // the regional scoreboard is based on a level 6 S2 Cell // - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22 // at the time of writing there"s no actual API for the intel map to retrieve scoreboard data, // but it"s still useful to plot the score cells on the intel map // the S2 geometry is based on projecting the earth sphere onto a cube, with some scaling of face coordinates to // keep things close to approximate equal area for adjacent cells // to convert a lat,lng into a cell id: // - convert lat,lng to x,y,z // - convert x,y,z into face,u,v // - u,v scaled to s,t with quadratic formula // - s,t converted to integer i,j offsets // - i,j converted to a position along a Hubbert space-filling curve // - combine face,position to get the cell id //NOTE: compared to the google S2 geometry library, we vary from their code in the following ways // - cell IDs: they combine face and the hilbert curve position into a single 64 bit number. this gives efficient space // and speed. javascript doesn"t have appropriate data types, and speed is not cricical, so we use // as [face,[bitpair,bitpair,...]] instead // - i,j: they always use 30 bits, adjusting as needed. we use 0 to (1< temp[1]) { if (temp[0] > temp[2]) { return 0; } else { return 2; } } else { if (temp[1] > temp[2]) { return 1; } else { return 2; } } }; var faceXYZToUV = function(face,xyz) { var u,v; switch (face) { case 0: u = xyz[1]/xyz[0]; v = xyz[2]/xyz[0]; break; case 1: u = -xyz[0]/xyz[1]; v = xyz[2]/xyz[1]; break; case 2: u = -xyz[0]/xyz[2]; v = -xyz[1]/xyz[2]; break; case 3: u = xyz[2]/xyz[0]; v = xyz[1]/xyz[0]; break; case 4: u = xyz[2]/xyz[1]; v = -xyz[0]/xyz[1]; break; case 5: u = -xyz[1]/xyz[2]; v = -xyz[0]/xyz[2]; break; default: throw {error: "Invalid face"}; } return [u,v]; }; S2.XYZToFaceUV = function(xyz) { var face = largestAbsComponent(xyz); if (xyz[face] < 0) { face += 3; } var uv = faceXYZToUV (face,xyz); return [face, uv]; }; S2.FaceUVToXYZ = function(face,uv) { var u = uv[0]; var v = uv[1]; switch (face) { case 0: return [ 1, u, v]; case 1: return [-u, 1, v]; case 2: return [-u,-v, 1]; case 3: return [-1,-v,-u]; case 4: return [ v,-1,-u]; case 5: return [ v, u,-1]; default: throw {error: "Invalid face"}; } }; var singleSTtoUV = function(st) { if (st >= 0.5) { return (1/3.0) * (4*st*st - 1); } else { return (1/3.0) * (1 - (4*(1-st)*(1-st))); } }; S2.STToUV = function(st) { return [singleSTtoUV(st[0]), singleSTtoUV(st[1])]; }; var singleUVtoST = function(uv) { if (uv >= 0) { return 0.5 * Math.sqrt (1 + 3*uv); } else { return 1 - 0.5 * Math.sqrt (1 - 3*uv); } }; S2.UVToST = function(uv) { return [singleUVtoST(uv[0]), singleUVtoST(uv[1])]; }; S2.STToIJ = function(st,order) { var maxSize = (1<=0; i--) { var mask = 1<= 0; i--) { level = maxLevel - i; bit = position[i]; rx = 0; ry = 0; if (bit === "1") { ry = 1; } else if (bit === "2") { rx = 1; ry = 1; } else if (bit === "3") { rx = 1; } val = Math.pow(2, level - 1); rotateAndFlipQuadrant(val, point, rx, ry); point.x += val * rx; point.y += val * ry; } if (face % 2 === 1) { var t = point.x; point.x = point.y; point.y = t; } return S2.S2Cell.FromFaceIJ(parseInt(face), [point.x, point.y], level); }; //static method to construct S2.S2Cell.FromLatLng = function(latLng, level) { if ((!latLng.lat && latLng.lat !== 0) || (!latLng.lng && latLng.lng !== 0)) { throw new Error("Pass { lat: lat, lng: lng } to S2.S2Cell.FromLatLng"); } var xyz = S2.LatLngToXYZ(latLng); var faceuv = S2.XYZToFaceUV(xyz); var st = S2.UVToST(faceuv[1]); var ij = S2.STToIJ(st,level); return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level); }; /* S2.faceIjLevelToXyz = function (face, ij, level) { var st = S2.IJToST(ij, level, [0.5, 0.5]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(face, uv); return S2.XYZToLatLng(xyz); return xyz; }; */ S2.S2Cell.FromFaceIJ = function(face,ij,level) { var cell = new S2.S2Cell(); cell.face = face; cell.ij = ij; cell.level = level; return cell; }; S2.S2Cell.prototype.toString = function() { return "F"+this.face+"ij["+this.ij[0]+","+this.ij[1]+"]@"+this.level; }; S2.S2Cell.prototype.getLatLng = function() { var st = S2.IJToST(this.ij,this.level, [0.5,0.5]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(this.face, uv); return S2.XYZToLatLng(xyz); }; S2.S2Cell.prototype.getCornerLatLngs = function() { var result = []; var offsets = [ [ 0.0, 0.0 ], [ 0.0, 1.0 ], [ 1.0, 1.0 ], [ 1.0, 0.0 ] ]; for (var i=0; i<4; i++) { var st = S2.IJToST(this.ij, this.level, offsets[i]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(this.face, uv); result.push ( S2.XYZToLatLng(xyz) ); } return result; }; S2.S2Cell.prototype.getFaceAndQuads = function () { var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face); return [this.face,quads]; }; S2.S2Cell.prototype.toHilbertQuadkey = function () { var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face); return this.face.toString(10) + "/" + quads.join(""); }; S2.latLngToNeighborKeys = S2.S2Cell.latLngToNeighborKeys = function (lat, lng, level) { return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).getNeighbors().map(function (cell) { return cell.toHilbertQuadkey(); }); }; S2.S2Cell.prototype.getNeighbors = function() { var fromFaceIJWrap = function(face,ij,level) { var maxSize = (1<=0 && ij[1]>=0 && ij[0] levelN) { posS = posS.substr(0, levelN); } // 3-bit face value faceB = Long.fromString(faceN.toString(10), true, 10).toString(2); while (faceB.length < S2.FACE_BITS) { faceB = "0" + faceB; } // 60-bit position value posB = Long.fromString(posS, true, 4).toString(2); while (posB.length < (2 * levelN)) { posB = "0" + posB; } bin = faceB + posB; // 1-bit lsb marker bin += "1"; // n-bit padding to 64-bits while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { bin += "0"; } return Long.fromString(bin, true, 2).toString(10); }; S2.keyToId = S2.S2Cell.keyToId = S2.toId = S2.toCellId = S2.fromKey = function (key) { var parts = key.split("/"); return S2.fromFacePosLevel(parts[0], parts[1], parts[1].length); }; S2.idToKey = S2.S2Cell.idToKey = S2.S2Cell.toKey = S2.toKey = S2.fromId = S2.fromCellId = S2.S2Cell.toHilbertQuadkey = S2.toHilbertQuadkey = function (idS) { var Long = exports.dcodeIO && exports.dcodeIO.Long || requireLong(); var bin = Long.fromString(idS, true, 10).toString(2); while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { bin = "0" + bin; } // MUST come AFTER binstr has been left-padded with "0"s var lsbIndex = bin.lastIndexOf("1"); // substr(start, len) // substring(start, end) // includes start, does not include end var faceB = bin.substring(0, 3); // posB will always be a multiple of 2 (or it"s invalid) var posB = bin.substring(3, lsbIndex); var levelN = posB.length / 2; var faceS = Long.fromString(faceB, true, 2).toString(10); var posS = Long.fromString(posB, true, 2).toString(4); while (posS.length < levelN) { posS = "0" + posS; } return faceS + "/" + posS; }; S2.keyToLatLng = S2.S2Cell.keyToLatLng = function (key) { var cell2 = S2.S2Cell.FromHilbertQuadKey(key); return cell2.getLatLng(); }; S2.idToLatLng = S2.S2Cell.idToLatLng = function (id) { var key = S2.idToKey(id); return S2.keyToLatLng(key); }; S2.S2Cell.latLngToKey = S2.latLngToKey = S2.latLngToQuadkey = function (lat, lng, level) { if (isNaN(level) || level < 1 || level > 30) { throw new Error(""level" is not a number between 1 and 30 (but it should be)"); } // TODO // // S2.idToLatLng(id) // S2.keyToLatLng(key) // S2.nextFace(key) // prevent wrapping on nextKey // S2.prevFace(key) // prevent wrapping on prevKey // // .toKeyArray(id) // face,quadtree // .toKey(id) // hilbert // .toPoint(id) // ij // .toId(key) // uint64 (as string) // .toLong(key) // long.js // .toLatLng(id) // object? or array?, or string (with comma)? // // maybe S2.HQ.x, S2.GPS.x, S2.CI.x? return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).toHilbertQuadkey(); }; S2.stepKey = function (key, num) { var Long = exports.dcodeIO && exports.dcodeIO.Long || requireLong(); var parts = key.split("/"); var faceS = parts[0]; var posS = parts[1]; var level = parts[1].length; var posL = Long.fromString(posS, true, 4); // TODO handle wrapping (0 === pos + 1) // (only on the 12 edges of the globe) var otherL; if (num > 0) { otherL = posL.add(Math.abs(num)); } else if (num < 0) { otherL = posL.subtract(Math.abs(num)); } var otherS = otherL.toString(4); if ("0" === otherS) { console.warning(new Error("face/position wrapping is not yet supported")); } while (otherS.length < level) { otherS = "0" + otherS; } return faceS + "/" + otherS; }; S2.S2Cell.prevKey = S2.prevKey = function (key) { return S2.stepKey(key, -1); }; S2.S2Cell.nextKey = S2.nextKey = function (key) { return S2.stepKey(key, 1); }; })(module.exports ); } (s2geometry)); var s2geometryExports = s2geometry.exports; /** * @class GeometryProviderBase * * @classdesc Base class to extend if implementing a geometry * provider class. * * @example * ```js * class MyGeometryProvider extends GeometryProviderBase { * ... * } * ``` */ class GeometryProviderBase { /** * Create a new geometry provider base instance. */ constructor() { } /** * Convert a geodetic bounding box to the the minimum set * of cell ids containing the bounding box. * * @description The bounding box needs * to be sufficiently small to be contained in an area with the size * of maximally four tiles. Up to nine adjacent tiles may be returned. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * * @returns {Array} Array of cell ids. */ bboxToCellIds(sw, ne) { throw new MapillaryError("Not implemented"); } /** * Get the cell ids of all adjacent cells. * * @description In the case of approximately rectangular cells * this is typically the eight orthogonally and diagonally adjacent * cells. * * @param {string} cellId - Id of cell. * @returns {Array} Array of cell ids. No specific * order is guaranteed. */ getAdjacent(cellId) { throw new MapillaryError("Not implemented"); } /** * Get the vertices of a cell. * * @description The vertices form an unclosed * clockwise polygon in the 2D longitude, latitude * space. No assumption on the position of the first * vertex relative to the others can be made. * * @param {string} cellId - Id of cell. * @returns {Array} Unclosed clockwise polygon. */ getVertices(cellId) { throw new MapillaryError("Not implemented"); } /** * Convert geodetic coordinates to a cell id. * * @param {LngLat} lngLat - Longitude, latitude to convert. * @returns {string} Cell id for the longitude, latitude. */ lngLatToCellId(lngLat) { throw new MapillaryError("Not implemented"); } /** @ignore */ _approxBboxToCellIds(sw, ne) { if (ne.lat <= sw.lat || ne.lng <= sw.lng) { throw new MapillaryError("North east needs to be top right of south west"); } const centerLat = (sw.lat + ne.lat) / 2; const centerLng = (sw.lng + ne.lng) / 2; const enu = geodeticToEnu(ne.lng, ne.lat, 0, centerLng, centerLat, 0); const threshold = Math.max(enu[0], enu[1]); return this._lngLatToCellIds({ lat: centerLat, lng: centerLng }, threshold); } /** @ignore */ _enuToGeodetic(point, reference) { const [lng, lat] = enuToGeodetic(point[0], point[1], point[2], reference.lng, reference.lat, 0); return { lat, lng }; } /** @ignore */ _getLngLatBoundingBoxCorners(lngLat, threshold) { return [ [-threshold, threshold, 0], [threshold, threshold, 0], [threshold, -threshold, 0], [-threshold, -threshold, 0], ].map((point) => { return this._enuToGeodetic(point, lngLat); }); } /** * Convert a geodetic square to cell ids. * * The square is specified as a longitude, latitude * and a threshold from the position using Manhattan distance. * * @param {LngLat} lngLat - Longitude, latitude. * @param {number} threshold - Threshold of the conversion in meters. * * @returns {Array} Array of cell ids reachable within * the threshold. * * @ignore */ _lngLatToCellIds(lngLat, threshold) { const cellId = this.lngLatToCellId(lngLat); const bboxCorners = this._getLngLatBoundingBoxCorners(lngLat, threshold); for (const corner of bboxCorners) { const cid = this.lngLatToCellId(corner); if (cid !== cellId) { return [cellId, ...this.getAdjacent(cellId)]; } } return [cellId]; } } /** * @class S2GeometryProvider * * @classdesc Geometry provider based on S2 cells. * * @example * ```js * class MyDataProvider extends DataProviderBase { * ... * } * * const geometryProvider = new S2GeometryProvider(); * const dataProvider = new MyDataProvider(geometryProvider); * ``` */ class S2GeometryProvider extends GeometryProviderBase { /** * Create a new S2 geometry provider instance. */ constructor(_level = 17) { super(); this._level = _level; } /** @inheritdoc */ bboxToCellIds(sw, ne) { return this._approxBboxToCellIds(sw, ne); } /** @inheritdoc */ getAdjacent(cellId) { const k = s2geometryExports.S2.idToKey(cellId); const position = k.split("/")[1]; const level = position.length; const [a0, a1, a2, a3] = this._getNeighbors(k, level); const existing = [k, a0, a1, a2, a3]; const others = Array .from(new Set([ ...this._getNeighbors(a0, level), ...this._getNeighbors(a1, level), ...this._getNeighbors(a2, level), ...this._getNeighbors(a3, level), ].filter((o) => { return !existing.includes(o); }))); const adjacent = [a0, a1, a2, a3]; for (const other of others) { let count = 0; for (const n of this._getNeighbors(other, level)) { if (existing.includes(n)) { count++; } } if (count === 2) { adjacent.push(other); } } return adjacent.map((a) => s2geometryExports.S2.keyToId(a)); } /** @inheritdoc */ getVertices(cellId) { const key = s2geometryExports.S2.idToKey(cellId); const cell = s2geometryExports.S2.S2Cell.FromHilbertQuadKey(key); return cell .getCornerLatLngs() .map((c) => { return { lat: c.lat, lng: c.lng }; }); } /** @inheritdoc */ lngLatToCellId(lngLat) { return this._lngLatToId(lngLat, this._level); } _getNeighbors(s2key, level) { const latlng = s2geometryExports.S2.keyToLatLng(s2key); const neighbors = s2geometryExports.S2.latLngToNeighborKeys(latlng.lat, latlng.lng, level); return neighbors; } _lngLatToId(lngLat, level) { const s2key = s2geometryExports.S2.latLngToKey(lngLat.lat, lngLat.lng, level); return s2geometryExports.S2.keyToId(s2key); } } let Camera$1 = class Camera { constructor(type, projectToSfmFunction) { this.type = type; this.projectToSfmFunction = projectToSfmFunction; this.parameters = {}; this.uniforms = {}; } }; /** * Compute distortion given the distorted radius. * * Solves for d in the equation * * y = d(x, k1, k2) * x * * given the distorted radius, y. */ function distortionFromDistortedRadius(distortedRadius, k1, k2, radialPeak) { let d = 1.0; for (let i = 0; i < 10; i++) { let radius = distortedRadius / d; if (radius > radialPeak) { radius = radialPeak; } d = 1 + k1 * Math.pow(radius, 2) + k2 * Math.pow(radius, 4); } return d; } function makeRadialPeak(k1, k2) { const a = 5 * k2; const b = 3 * k1; const c = 1; const d = Math.pow(b, 2) - 4 * a * c; if (d < 0) { return Number.POSITIVE_INFINITY; } const root1 = (-b - Math.sqrt(d)) / 2 / a; const root2 = (-b + Math.sqrt(d)) / 2 / a; const minRoot = Math.min(root1, root2); const maxRoot = Math.max(root1, root2); return minRoot > 0 ? Math.sqrt(minRoot) : maxRoot > 0 ? Math.sqrt(maxRoot) : Number.POSITIVE_INFINITY; } function bearing$2(point, parameters, uniforms) { const [x, y] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Transformation const [xd, yd] = [x / focal, y / focal]; // Undistortion const dr = Math.sqrt(xd * xd + yd * yd); const d = distortionFromDistortedRadius(dr, k1, k2, radialPeak); const xp = xd / d; const yp = yd / d; // Unprojection const zp = 1; const length = Math.sqrt(xp * xp + yp * yp + zp * zp); const xb = xp / length; const yb = yp / length; const zb = zp / length; return [xb, yb, zb]; } function project$2(point, parameters, uniforms) { const [x, y, z] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Projection if (z <= 0) { return [ x < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, y < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, ]; } const xp = x / z; const yp = y / z; // Distortion let r2 = xp * xp + yp * yp; if (r2 > radialPeak * Math.sqrt(r2)) { r2 = Math.pow(radialPeak, 2); } const distortion = 1 + r2 * (k1 + r2 * k2); const xd = xp * distortion; const yd = yp * distortion; // Transformation const xt = focal * xd; const yt = focal * yd; return [xt, yt]; } const PERSPECTIVE_CAMERA_TYPE = "perspective"; const PERSPECTIVE_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing, Parameters parameters, Uniforms uniforms) { float x = bearing.x; float y = bearing.y; float z = bearing.z; float focal = parameters.focal; float k1 = parameters.k1; float k2 = parameters.k2; float radialPeak = uniforms.radialPeak; // Projection if (z < 0.) { return vec2(POSITIVE_INFINITY, POSITIVE_INFINITY); } float xp = x / z; float yp = y / z; // Distortion float r2 = xp * xp + yp * yp; if (r2 > radialPeak * sqrt(r2)) { r2 = radialPeak * radialPeak; } float distortion = 1.0 + r2 * (k1 + r2 * k2); float xd = xp * distortion; float yd = yp * distortion; // Transformation float xt = focal * xd; float yt = focal * yd; return vec2(xt, yt); } `; class PerspectiveCamera extends Camera$1 { constructor(parameters) { super(PERSPECTIVE_CAMERA_TYPE, PERSPECTIVE_PROJECT_FUNCTION); const [focal, k1, k2] = parameters; this.parameters.focal = focal; this.parameters.k1 = k1; this.parameters.k2 = k2; const radialPeak = makeRadialPeak(k1, k2); this.uniforms.radialPeak = radialPeak; } bearingFromSfm(point) { return bearing$2(point, this.parameters, this.uniforms); } projectToSfm(point) { return project$2(point, this.parameters, this.uniforms); } } const NULL_IMAGE_ID = "null-image-id"; const NULL_SEQUENCE_ID = "null-sequence-id"; class NullCameraFactory { makeCamera(_type, _parameters) { return new PerspectiveCamera([0.85, 0, 0]); } } class NullImageDataProvider extends DataProviderBase { constructor() { super(new S2GeometryProvider()); } getImageBuffer() { return generateImageBuffer(); } getMesh() { return Promise.resolve({ faces: [], vertices: [] }); } } function generateImageBuffer() { const canvas = document.createElement("canvas"); const w = 1; const h = 1; canvas.width = w; canvas.height = h; const ctx = canvas.getContext("2d"); ctx.fillStyle = `rgb(0 0 0)`; ctx.fillRect(0, 0, w, h); return new Promise((resolve) => { canvas.toBlob((blob) => { blob.arrayBuffer() .then(buffer => resolve(buffer)); }, "image/jpeg", 1); }); } function isNullImageId(imageId) { return imageId === NULL_IMAGE_ID; } function isNullSequenceId(sequenceId) { return sequenceId === NULL_SEQUENCE_ID; } function makeNullImage$() { const core = { computed_geometry: null, geometry: { lat: 90, lng: 0 }, id: NULL_IMAGE_ID, sequence: { id: NULL_SEQUENCE_ID, }, }; const image = new Image$1(core); const spatial = { altitude: 0, camera_parameters: [], camera_type: "null-camera-type", captured_at: 0, cluster: { id: "null-cluster-id", url: "null-cluster-url" }, compass_angle: 0, creator: { id: "null-creator-id", username: "null-creator-username" }, exif_orientation: 0, height: 0, id: NULL_IMAGE_ID, mesh: { id: "null-mesh-id", url: "null-mesh-url" }, owner: { id: "null-owner-id" }, thumb: { id: "null-thumb-id", url: "null-thumb-url" }, width: 0, atomic_scale: 0, computed_altitude: 0, computed_compass_angle: 0, computed_rotation: [0, 0, 0], merge_id: "null-merge-id", private: false, quality_score: 0, }; image.makeComplete(spatial); image.initializeCache(new ImageCache(new NullImageDataProvider())); image.cacheSequenceEdges([]); image.cacheSpatialEdges([]); return image.cacheAssets$(new NullCameraFactory()); } /** * @class SequenceComponent * @classdesc Component showing navigation arrows for sequence directions * as well as playing button. Exposes an API to start and stop play. */ class SequenceComponent extends Component { constructor(name, container, navigator, renderer, scheduler) { super(name, container, navigator); this._sequenceDOMRenderer = !!renderer ? renderer : new SequenceDOMRenderer(container); this._scheduler = scheduler; this._containerWidth$ = new Subject(); this._hoveredIdSubject$ = new Subject(); this._hoveredId$ = this._hoveredIdSubject$.pipe(share()); this._navigator.playService.playing$.pipe(skip(1), withLatestFrom(this._configuration$)) .subscribe(([playing, configuration]) => { const type = "playing"; const event = { playing, target: this, type, }; this.fire(type, event); if (playing === configuration.playing) { return; } if (playing) { this.play(); } else { this.stop(); } }); this._navigator.playService.direction$.pipe(skip(1), withLatestFrom(this._configuration$)) .subscribe(([direction, configuration]) => { if (direction !== configuration.direction) { this.configure({ direction }); } }); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Start playing. * * @fires playing */ play() { this.configure({ playing: true }); } /** * Stop playing. * * @fires playing */ stop() { this.configure({ playing: false }); } _activate() { this._sequenceDOMRenderer.activate(); const edgeStatus$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; }), publishReplay(1), refCount()); const sequence$ = this._navigator.stateService.currentImage$.pipe(distinctUntilChanged(undefined, (image) => { return image.sequenceId; }), switchMap((image) => { return concat(of(null), this._navigator.graphService.cacheSequence$(image.sequenceId).pipe(retry(3), catchError((e) => { if (!isNullSequenceId(image.sequenceId)) { console.error("Failed to cache sequence", e); } return of(null); }))); }), startWith(null), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(sequence$.subscribe()); const rendererId$ = this._sequenceDOMRenderer.index$.pipe(withLatestFrom(sequence$), map(([index, sequence]) => { return sequence != null && !isNullSequenceId(sequence.id) ? sequence.imageIds[index] : null; }), filter((id) => { return !!id; }), distinctUntilChanged(), publish(), refCount()); subs.push(merge(rendererId$.pipe(debounceTime(100, this._scheduler)), rendererId$.pipe(auditTime(400, this._scheduler))).pipe(distinctUntilChanged(), switchMap((id) => { return this._navigator.moveTo$(id).pipe(catchError(() => { return empty(); })); })) .subscribe()); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return changing; })) .subscribe(() => { this._navigator.graphService.setGraphMode(GraphMode.Sequence); })); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return !changing; })) .subscribe(() => { this._navigator.graphService.setGraphMode(GraphMode.Spatial); })); this._navigator.graphService.graphMode$.pipe(switchMap((mode) => { return mode === GraphMode.Spatial ? this._navigator.stateService.currentImage$.pipe(take(2)) : empty(); }), filter((image) => { return !image.spatialEdges.cached; }), switchMap((image) => { return this._navigator.graphService.cacheImage$(image.id).pipe(catchError(() => { return empty(); })); })) .subscribe(); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return changing; })) .subscribe(() => { this._navigator.playService.stop(); })); subs.push(combineLatest(this._navigator.graphService.graphMode$, this._sequenceDOMRenderer.changingPositionChanged$.pipe(startWith(false), distinctUntilChanged())).pipe(withLatestFrom(this._navigator.stateService.currentImage$), switchMap(([[mode, changing], image]) => { return changing && mode === GraphMode.Sequence ? this._navigator.graphService.cacheSequenceImages$(image.sequenceId, image.id).pipe(retry(3), catchError((error) => { console.error("Failed to cache sequence images.", error); return empty(); })) : empty(); })) .subscribe()); const position$ = sequence$.pipe(switchMap((sequence) => { if (!sequence) { return of({ index: null, max: null }); } let firstCurrentId = true; return this._sequenceDOMRenderer.changingPositionChanged$.pipe(startWith(false), distinctUntilChanged(), switchMap((changingPosition) => { const skipCount = !changingPosition && firstCurrentId ? 0 : 1; firstCurrentId = false; return changingPosition ? rendererId$ : this._navigator.stateService.currentImage$.pipe(map((image) => { return image.id; }), distinctUntilChanged(), skip(skipCount)); }), map((imageId) => { if (isNullSequenceId(sequence.id)) { return { index: null, max: null }; } const index = sequence.imageIds.indexOf(imageId); if (index === -1) { return { index: null, max: null }; } return { index: index, max: sequence.imageIds.length - 1 }; })); })); const earth$ = this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged()); subs.push(combineLatest(edgeStatus$, this._configuration$, this._containerWidth$, this._sequenceDOMRenderer.changed$.pipe(startWith(this._sequenceDOMRenderer)), this._navigator.playService.speed$, position$, earth$).pipe(map(([edgeStatus, configuration, containerWidth, , speed, position, earth]) => { const vNode = this._sequenceDOMRenderer .render(edgeStatus, configuration, containerWidth, speed, position.index, position.max, !earth, this, this._navigator); return { name: this._name, vNode: vNode }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._sequenceDOMRenderer.speed$ .subscribe((speed) => { this._navigator.playService.setSpeed(speed); })); subs.push(this._configuration$.pipe(map((configuration) => { return configuration.direction; }), distinctUntilChanged()) .subscribe((direction) => { this._navigator.playService.setDirection(direction); })); subs.push(combineLatest(this._container.renderService.size$, this._configuration$.pipe(distinctUntilChanged((value1, value2) => { return value1[0] === value2[0] && value1[1] === value2[1]; }, (configuration) => { return [configuration.minWidth, configuration.maxWidth]; }))).pipe(map(([size, configuration]) => { return this._sequenceDOMRenderer.getContainerWidth(size, configuration); })) .subscribe(this._containerWidth$)); subs.push(this._configuration$.pipe(map((configuration) => { return configuration.playing; }), distinctUntilChanged()) .subscribe((playing) => { if (playing) { this._navigator.playService.play(); } else { this._navigator.playService.stop(); } })); subs.push(this._sequenceDOMRenderer.mouseEnterDirection$.pipe(switchMap((direction) => { const edgeTo$ = edgeStatus$.pipe(map((edgeStatus) => { for (let edge of edgeStatus.edges) { if (edge.data.direction === direction) { return edge.target; } } return null; }), takeUntil(this._sequenceDOMRenderer.mouseLeaveDirection$)); return concat(edgeTo$, of(null)); }), distinctUntilChanged()) .subscribe(this._hoveredIdSubject$)); subs.push(this._hoveredId$ .subscribe((id) => { const type = "hover"; const event = { id, target: this, type, }; this.fire(type, event); })); } _deactivate() { this._subscriptions.unsubscribe(); this._sequenceDOMRenderer.deactivate(); } _getDefaultConfiguration() { return { direction: exports.NavigationDirection.Next, maxWidth: 108, minWidth: 70, playing: false, visible: true, }; } } /** @inheritdoc */ SequenceComponent.componentName = "sequence"; /** * @class Transform * * @classdesc Class used for calculating coordinate transformations * and projections. */ class Transform { /** * Create a new transform instance. * @param {number} orientation - Image orientation. * @param {number} width - Image height. * @param {number} height - Image width. * @param {number} focal - Focal length. * @param {number} scale - Atomic scale. * @param {Array} rotation - Rotation vector in three dimensions. * @param {Array} translation - Translation vector in three dimensions. * @param {HTMLImageElement} image - Image for fallback size calculations. */ constructor(orientation, width, height, scale, rotation, translation, image, camera) { this.camera = camera; this._orientation = this._getValue(orientation, 1); let imageWidth = image != null ? image.width : 4; let imageHeight = image != null ? image.height : 3; let keepOrientation = this._orientation < 5; this._width = this._getValue(width, keepOrientation ? imageWidth : imageHeight); this._height = this._getValue(height, keepOrientation ? imageHeight : imageWidth); this._basicAspect = keepOrientation ? this._width / this._height : this._height / this._width; this._basicWidth = keepOrientation ? width : height; this._basicHeight = keepOrientation ? height : width; this._focal = this._getValue(camera.parameters.focal, 1); this._scale = this._getValue(scale, 0); this._worldToCamera = this.createWorldToCamera(rotation, translation); this._worldToCameraInverse = new Matrix4() .copy(this._worldToCamera) .invert(); this._scaledWorldToCamera = this._createScaledWorldToCamera(this._worldToCamera, this._scale); this._scaledWorldToCameraInverse = new Matrix4() .copy(this._scaledWorldToCamera) .invert(); this._basicWorldToCamera = this._createBasicWorldToCamera(this._worldToCamera, orientation); } get cameraType() { return this.camera.type; } /** * Get basic aspect. * @returns {number} The orientation adjusted aspect ratio. */ get basicAspect() { return this._basicAspect; } /** * Get basic height. * * @description Does not fall back to image image height but * uses original value from API so can be faulty. * * @returns {number} The height of the basic version image * (adjusted for orientation). */ get basicHeight() { return this._basicHeight; } get basicRt() { return this._basicWorldToCamera; } /** * Get basic width. * * @description Does not fall back to image image width but * uses original value from API so can be faulty. * * @returns {number} The width of the basic version image * (adjusted for orientation). */ get basicWidth() { return this._basicWidth; } /** * Get focal. * @returns {number} The image focal length. */ get focal() { return this._focal; } /** * Get height. * * @description Falls back to the image image height if * the API data is faulty. * * @returns {number} The orientation adjusted image height. */ get height() { return this._height; } /** * Get orientation. * @returns {number} The image orientation. */ get orientation() { return this._orientation; } /** * Get rt. * @returns {THREE.Matrix4} The extrinsic camera matrix. */ get rt() { return this._worldToCamera; } /** * Get rtInverse. * @returns {THREE.Matrix4} The inverse of the extrinsic camera matrix. */ get rtInverse() { return this._worldToCameraInverse; } /** * Get srt. * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. */ get srt() { return this._scaledWorldToCamera; } /** * Get srtInverse. * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. */ get srtInverse() { return this._scaledWorldToCameraInverse; } /** * Get scale. * @returns {number} The image atomic reconstruction scale. */ get scale() { return this._scale; } /** * Get has valid scale. * @returns {boolean} Value indicating if the scale of the transform is valid. */ get hasValidScale() { return this._scale > 1e-2 && this._scale < 50; } /** * Get width. * * @description Falls back to the image image width if * the API data is faulty. * * @returns {number} The orientation adjusted image width. */ get width() { return this._width; } /** * Calculate the up vector for the image transform. * * @returns {THREE.Vector3} Normalized and orientation adjusted up vector. */ upVector() { let rte = this._worldToCamera.elements; switch (this._orientation) { case 1: return new Vector3(-rte[1], -rte[5], -rte[9]); case 3: return new Vector3(rte[1], rte[5], rte[9]); case 6: return new Vector3(-rte[0], -rte[4], -rte[8]); case 8: return new Vector3(rte[0], rte[4], rte[8]); default: return new Vector3(-rte[1], -rte[5], -rte[9]); } } /** * Project 3D world coordinates to basic coordinates. * * @param {Array} point3d - 3D world coordinates. * @return {Array} 2D basic coordinates. */ projectBasic(point3d) { let sfm = this.projectSfM(point3d); return this._sfmToBasic(sfm); } /** * Unproject basic coordinates to 3D world coordinates. * * @param {Array} basic - 2D basic coordinates. * @param {Array} distance - Distance to unproject from camera center. * @param {boolean} [depth] - Treat the distance value as depth from camera center. * Only applicable for perspective images. Will be * ignored for spherical. * @returns {Array} Unprojected 3D world coordinates. */ unprojectBasic(basic, distance, depth) { let sfm = this._basicToSfm(basic); return this.unprojectSfM(sfm, distance, depth); } /** * Project 3D world coordinates to SfM coordinates. * * @param {Array} point3d - 3D world coordinates. * @return {Array} 2D SfM coordinates. */ projectSfM(point3d) { let v = new Vector4(point3d[0], point3d[1], point3d[2], 1); v.applyMatrix4(this._worldToCamera); return this._bearingToSfm([v.x, v.y, v.z]); } /** * Unproject SfM coordinates to a 3D world coordinates. * * @param {Array} sfm - 2D SfM coordinates. * @param {Array} distance - Distance to unproject * from camera center. * @param {boolean} [depth] - Treat the distance value as * depth from camera center. Only applicable for perspective * images. Will be ignored for spherical. * @returns {Array} Unprojected 3D world coordinates. */ unprojectSfM(sfm, distance, depth) { const bearing = this._sfmToBearing(sfm); const unprojectedCamera = depth && !isSpherical(this.camera.type) ? new Vector4(distance * bearing[0] / bearing[2], distance * bearing[1] / bearing[2], distance, 1) : new Vector4(distance * bearing[0], distance * bearing[1], distance * bearing[2], 1); const unprojectedWorld = unprojectedCamera .applyMatrix4(this._worldToCameraInverse); return [ unprojectedWorld.x / unprojectedWorld.w, unprojectedWorld.y / unprojectedWorld.w, unprojectedWorld.z / unprojectedWorld.w, ]; } /** * Transform SfM coordinates to bearing vector (3D cartesian * coordinates on the unit sphere). * * @param {Array} sfm - 2D SfM coordinates. * @returns {Array} Bearing vector (3D cartesian coordinates * on the unit sphere). */ _sfmToBearing(sfm) { return this.camera.bearingFromSfm(sfm); } /** * Transform bearing vector (3D cartesian coordiantes on the unit sphere) to * SfM coordinates. * * @param {Array} bearing - Bearing vector (3D cartesian coordinates on the * unit sphere). * @returns {Array} 2D SfM coordinates. */ _bearingToSfm(bearing) { return this.camera.projectToSfm(bearing); } /** * Convert basic coordinates to SfM coordinates. * * @param {Array} basic - 2D basic coordinates. * @returns {Array} 2D SfM coordinates. */ _basicToSfm(basic) { let rotatedX; let rotatedY; switch (this._orientation) { case 1: rotatedX = basic[0]; rotatedY = basic[1]; break; case 3: rotatedX = 1 - basic[0]; rotatedY = 1 - basic[1]; break; case 6: rotatedX = basic[1]; rotatedY = 1 - basic[0]; break; case 8: rotatedX = 1 - basic[1]; rotatedY = basic[0]; break; default: rotatedX = basic[0]; rotatedY = basic[1]; break; } let w = this._width; let h = this._height; let s = Math.max(w, h); let sfmX = rotatedX * w / s - w / s / 2; let sfmY = rotatedY * h / s - h / s / 2; return [sfmX, sfmY]; } /** * Convert SfM coordinates to basic coordinates. * * @param {Array} sfm - 2D SfM coordinates. * @returns {Array} 2D basic coordinates. */ _sfmToBasic(sfm) { let w = this._width; let h = this._height; let s = Math.max(w, h); let rotatedX = (sfm[0] + w / s / 2) / w * s; let rotatedY = (sfm[1] + h / s / 2) / h * s; let basicX; let basicY; switch (this._orientation) { case 1: basicX = rotatedX; basicY = rotatedY; break; case 3: basicX = 1 - rotatedX; basicY = 1 - rotatedY; break; case 6: basicX = 1 - rotatedY; basicY = rotatedX; break; case 8: basicX = rotatedY; basicY = 1 - rotatedX; break; default: basicX = rotatedX; basicY = rotatedY; break; } return [basicX, basicY]; } /** * Checks a value and returns it if it exists and is larger than 0. * Fallbacks if it is null. * * @param {number} value - Value to check. * @param {number} fallback - Value to fall back to. * @returns {number} The value or its fallback value if it is not defined or negative. */ _getValue(value, fallback) { return value != null && value > 0 ? value : fallback; } _getCameraParameters(value, cameraType) { if (isSpherical(cameraType)) { return []; } if (!value || value.length === 0) { return [1, 0, 0]; } const padding = 3 - value.length; if (padding <= 0) { return value; } return value .concat(new Array(padding) .fill(0)); } /** * Creates the extrinsic camera matrix [ R | t ]. * * @param {Array} rotation - Rotation vector in angle axis representation. * @param {Array} translation - Translation vector. * @returns {THREE.Matrix4} Extrisic camera matrix. */ createWorldToCamera(rotation, translation) { const axis = new Vector3(rotation[0], rotation[1], rotation[2]); const angle = axis.length(); if (angle > 0) { axis.normalize(); } const worldToCamera = new Matrix4(); worldToCamera.makeRotationAxis(axis, angle); worldToCamera.setPosition(new Vector3(translation[0], translation[1], translation[2])); return worldToCamera; } /** * Calculates the scaled extrinsic camera matrix scale * [ R | t ]. * * @param {THREE.Matrix4} worldToCamera - Extrisic camera matrix. * @param {number} scale - Scale factor. * @returns {THREE.Matrix4} Scaled extrisic camera matrix. */ _createScaledWorldToCamera(worldToCamera, scale) { const scaledWorldToCamera = worldToCamera.clone(); const elements = scaledWorldToCamera.elements; elements[12] = scale * elements[12]; elements[13] = scale * elements[13]; elements[14] = scale * elements[14]; scaledWorldToCamera.scale(new Vector3(scale, scale, scale)); return scaledWorldToCamera; } _createBasicWorldToCamera(rt, orientation) { const axis = new Vector3(0, 0, 1); let angle = 0; switch (orientation) { case 3: angle = Math.PI; break; case 6: angle = Math.PI / 2; break; case 8: angle = 3 * Math.PI / 2; break; } return new Matrix4() .makeRotationAxis(axis, angle) .multiply(rt); } /** * Calculate a transformation matrix from normalized coordinates for * texture map coordinates. * * @returns {THREE.Matrix4} Normalized coordinates to texture map * coordinates transformation matrix. */ _normalizedToTextureMatrix() { const size = Math.max(this._width, this._height); const w = size / this._width; const h = size / this._height; switch (this._orientation) { case 1: return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 3: return new Matrix4().set(-w, 0, 0, 0.5, 0, h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 6: return new Matrix4().set(0, -h, 0, 0.5, -w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 8: return new Matrix4().set(0, h, 0, 0.5, w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); default: return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); } } } class PlayService { constructor(graphService, stateService) { this._subscriptions = new SubscriptionHolder(); this._graphService = graphService; this._stateService = stateService; const subs = this._subscriptions; this._directionSubject$ = new Subject(); this._direction$ = this._directionSubject$.pipe(startWith(exports.NavigationDirection.Next), publishReplay(1), refCount()); subs.push(this._direction$.subscribe()); this._playing = false; this._playingSubject$ = new Subject(); this._playing$ = this._playingSubject$.pipe(startWith(this._playing), publishReplay(1), refCount()); subs.push(this._playing$.subscribe()); this._speed = 0.5; this._speedSubject$ = new Subject(); this._speed$ = this._speedSubject$.pipe(startWith(this._speed), publishReplay(1), refCount()); subs.push(this._speed$.subscribe()); this._imagesAhead = this._mapImagesAhead(this._mapSpeed(this._speed)); this._bridging$ = null; } get playing() { return this._playing; } get direction$() { return this._direction$; } get playing$() { return this._playing$; } get speed$() { return this._speed$; } play() { if (this._playing) { return; } this._stateService.cutImages(); const stateSpeed = this._setSpeed(this._speed); this._stateService.setSpeed(stateSpeed); this._graphModeSubscription = this._speed$.pipe(map((speed) => { return speed > PlayService.sequenceSpeed ? GraphMode.Sequence : GraphMode.Spatial; }), distinctUntilChanged()) .subscribe((mode) => { this._graphService.setGraphMode(mode); }); this._cacheSubscription = combineLatest(this._stateService.currentImage$.pipe(map((image) => { return [image.sequenceId, image.id]; }), distinctUntilChanged(undefined, ([sequenceId]) => { return sequenceId; })), this._graphService.graphMode$, this._direction$).pipe(switchMap(([[sequenceId, imageId], mode, direction]) => { if (direction !== exports.NavigationDirection.Next && direction !== exports.NavigationDirection.Prev) { return of([undefined, direction]); } const sequence$ = (mode === GraphMode.Sequence ? this._graphService.cacheSequenceImages$(sequenceId, imageId) : this._graphService.cacheSequence$(sequenceId)).pipe(retry(3), catchError((error) => { console.error(error); return of(undefined); })); return combineLatest(sequence$, of(direction)); }), switchMap(([sequence, direction]) => { if (sequence === undefined) { return empty(); } const imageIds = sequence.imageIds.slice(); if (direction === exports.NavigationDirection.Prev) { imageIds.reverse(); } return this._stateService.currentState$.pipe(map((frame) => { return [frame.state.trajectory[frame.state.trajectory.length - 1].id, frame.state.imagesAhead]; }), scan(([lastRequestKey, previousRequestKeys], [lastTrajectoryKey, imagesAhead]) => { if (lastRequestKey === undefined) { lastRequestKey = lastTrajectoryKey; } const lastIndex = imageIds.length - 1; if (imagesAhead >= this._imagesAhead || imageIds[lastIndex] === lastRequestKey) { return [lastRequestKey, []]; } const current = imageIds.indexOf(lastTrajectoryKey); const start = imageIds.indexOf(lastRequestKey) + 1; const end = Math.min(lastIndex, current + this._imagesAhead - imagesAhead) + 1; if (end <= start) { return [lastRequestKey, []]; } return [imageIds[end - 1], imageIds.slice(start, end)]; }, [undefined, []]), mergeMap(([lastRequestKey, newRequestKeys]) => { return from(newRequestKeys); })); }), mergeMap((key) => { return this._graphService.cacheImage$(key).pipe(catchError(() => { return empty(); })); }, 6)) .subscribe(); this._playingSubscription = this._stateService.currentState$.pipe(filter((frame) => { return frame.state.imagesAhead < this._imagesAhead; }), distinctUntilChanged(undefined, (frame) => { return frame.state.lastImage.id; }), map((frame) => { const lastImage = frame.state.lastImage; const trajectory = frame.state.trajectory; let increasingTime = undefined; for (let i = trajectory.length - 2; i >= 0; i--) { const image = trajectory[i]; if (image.sequenceId !== lastImage.sequenceId) { break; } if (image.capturedAt !== lastImage.capturedAt) { increasingTime = image.capturedAt < lastImage.capturedAt; break; } } return [frame.state.lastImage, increasingTime]; }), withLatestFrom(this._direction$), switchMap(([[image, increasingTime], direction]) => { return zip(([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), timeout(15000)), of(direction)).pipe(map(([s, d]) => { for (let edge of s.edges) { if (edge.data.direction === d) { return edge.target; } } return null; }), switchMap((key) => { return key != null ? this._graphService.cacheImage$(key) : empty(); })); })) .subscribe((image) => { this._stateService.appendImagess([image]); }, (error) => { console.error(error); this.stop(); }); this._clearSubscription = this._stateService.currentImage$.pipe(bufferCount(1, 10)) .subscribe((images) => { this._stateService.clearPriorImages(); }); this._setPlaying(true); const currentLastImages$ = this._stateService.currentState$.pipe(map((frame) => { return frame.state; }), distinctUntilChanged(([kc1, kl1], [kc2, kl2]) => { return kc1 === kc2 && kl1 === kl2; }, (state) => { return [state.currentImage.id, state.lastImage.id]; }), filter((state) => { return state.currentImage.id === state.lastImage.id && state.currentIndex === state.trajectory.length - 1; }), map((state) => { return state.currentImage; })); this._stopSubscription = combineLatest(currentLastImages$, this._direction$).pipe(switchMap(([image, direction]) => { const edgeStatus$ = ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), timeout(15000), catchError((error) => { console.error(error); return of({ cached: false, edges: [] }); })); return combineLatest(of(direction), edgeStatus$).pipe(map(([d, es]) => { for (const edge of es.edges) { if (edge.data.direction === d) { return true; } } return false; })); }), mergeMap((hasEdge) => { if (hasEdge || !this._bridging$) { return of(hasEdge); } return this._bridging$.pipe(map((image) => { return image != null; }), catchError((error) => { console.error(error); return of(false); })); }), first((hasEdge) => { return !hasEdge; })) .subscribe(undefined, undefined, () => { this.stop(); }); if (this._stopSubscription.closed) { this._stopSubscription = null; } this._earthSubscription = this._stateService.state$ .pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged(), first((earth) => { return earth; })) .subscribe(undefined, undefined, () => { this.stop(); }); if (this._earthSubscription.closed) { this._earthSubscription = null; } } dispose() { this.stop(); this._subscriptions.unsubscribe(); } setDirection(direction) { this._directionSubject$.next(direction); } setSpeed(speed) { speed = Math.max(0, Math.min(1, speed)); if (speed === this._speed) { return; } const stateSpeed = this._setSpeed(speed); if (this._playing) { this._stateService.setSpeed(stateSpeed); } this._speedSubject$.next(this._speed); } stop() { if (!this._playing) { return; } if (!!this._stopSubscription) { if (!this._stopSubscription.closed) { this._stopSubscription.unsubscribe(); } this._stopSubscription = null; } if (!!this._earthSubscription) { if (!this._earthSubscription.closed) { this._earthSubscription.unsubscribe(); } this._earthSubscription = null; } this._graphModeSubscription.unsubscribe(); this._graphModeSubscription = null; this._cacheSubscription.unsubscribe(); this._cacheSubscription = null; this._playingSubscription.unsubscribe(); this._playingSubscription = null; this._clearSubscription.unsubscribe(); this._clearSubscription = null; this._stateService.setSpeed(1); this._stateService.cutImages(); this._graphService.setGraphMode(GraphMode.Spatial); this._setPlaying(false); } _mapSpeed(speed) { const x = 2 * speed - 1; return Math.pow(10, x) - 0.2 * x; } _mapImagesAhead(stateSpeed) { return Math.round(Math.max(10, Math.min(50, 8 + 6 * stateSpeed))); } _setPlaying(playing) { this._playing = playing; this._playingSubject$.next(playing); } _setSpeed(speed) { this._speed = speed; const stateSpeed = this._mapSpeed(this._speed); this._imagesAhead = this._mapImagesAhead(stateSpeed); return stateSpeed; } } PlayService.sequenceSpeed = 0.54; const MAX_CAMERA_SIZE = 1; const MAX_POINT_SIZE = 1; const MIN_CAMERA_SIZE = 1e-3; const MIN_POINT_SIZE = 1e-3; exports.CameraVisualizationMode = void 0; (function (CameraVisualizationMode) { /** * Cameras are hidden. */ CameraVisualizationMode[CameraVisualizationMode["Hidden"] = 0] = "Hidden"; /** * Cameras are shown, all with the same color. */ CameraVisualizationMode[CameraVisualizationMode["Homogeneous"] = 1] = "Homogeneous"; /** * Cameras are shown with colors based on the * their clusters. */ CameraVisualizationMode[CameraVisualizationMode["Cluster"] = 2] = "Cluster"; /** * Cameras are shown with colors based on the * their connected components. */ CameraVisualizationMode[CameraVisualizationMode["ConnectedComponent"] = 3] = "ConnectedComponent"; /** * Cameras are shown, with colors based on the * their sequence. */ CameraVisualizationMode[CameraVisualizationMode["Sequence"] = 4] = "Sequence"; })(exports.CameraVisualizationMode || (exports.CameraVisualizationMode = {})); exports.OriginalPositionMode = void 0; (function (OriginalPositionMode) { /** * Original positions are hidden. */ OriginalPositionMode[OriginalPositionMode["Hidden"] = 0] = "Hidden"; /** * Visualize original positions with altitude change. */ OriginalPositionMode[OriginalPositionMode["Altitude"] = 1] = "Altitude"; /** * Visualize original positions without altitude change, * i.e. as flat lines from the camera origin. */ OriginalPositionMode[OriginalPositionMode["Flat"] = 2] = "Flat"; })(exports.OriginalPositionMode || (exports.OriginalPositionMode = {})); class ClusterPoints extends Points { constructor(parameters) { super(); this._originalSize = parameters.originalSize; const { cluster, color, scale, translation } = parameters; this._makeAttributes(cluster); this.material.size = scale * this._originalSize; this.setColor(color); this.matrixAutoUpdate = false; this.position.fromArray(translation); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setColor(color) { this.material.vertexColors = color == null; this.material.color = new Color(color); this.material.needsUpdate = true; } resize(scale) { this.material.size = scale * this._originalSize; this.material.needsUpdate = true; } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(cluster) { const geometry = this.geometry; geometry.setAttribute("position", new BufferAttribute(new Float32Array(cluster.coordinates), 3)); const colorSize = cluster.colors.length > 0 ? cluster.colors.length / cluster.pointIds.length : 3; geometry.setAttribute("color", new BufferAttribute(new Float32Array(cluster.colors), colorSize)); } } class CellLine extends Line { constructor(vertices) { super(); this._makeAttributes(vertices); this.matrixAutoUpdate = false; this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(vertices) { const closedPolygon = vertices.slice(); closedPolygon.push(vertices[0]); let index = 0; const positions = new Float32Array(3 * (vertices.length + 1)); for (const vertex of closedPolygon) { positions[index++] = vertex[0]; positions[index++] = vertex[1]; positions[index++] = vertex[2]; } this.geometry.setAttribute("position", new BufferAttribute(positions, 3)); } } // Level 0: 1 x 1 x 1 meter cubes const OCTREE_ROOT_LEVEL = 14; // 16384 meters const OCTREE_LEAF_LEVEL = 6; // 64 meters function isLeafLevel(level, leafLevel) { return level === leafLevel; } function levelToSize(level) { return Math.pow(2, level); } function levelToRootBoundingBox(level) { const size = levelToSize(level); const half = size / 2; const min = [-half, -half, -half]; const max = [half, half, half]; return { min, max }; } class SpatialOctreeNode { constructor(level, leafLevel, boundingBox, parent) { this.level = level; this.leafLevel = leafLevel; this.boundingBox = boundingBox; this.parent = parent; this.children = []; this.items = []; if (parent) { parent.children.push(this); } } get isEmpty() { return !(this.children.length || this.items.length); } add(object) { const self = this; if (!self.boundingBox.containsPoint(object.position)) { throw new Error(`Item not contained in node`); } if (isLeafLevel(self.level, self.leafLevel)) { self.items.push(object); return this; } for (const child of self.children) { if (child.boundingBox.containsPoint(object.position)) { return child.add(object); } } for (const boundingBox of self._generateBoundingBoxes()) { if (boundingBox.containsPoint(object.position)) { const child = new SpatialOctreeNode(self.level - 1, self.leafLevel, boundingBox, self); return child.add(object); } } throw new Error(`Item not contained in children`); } intersect(ray, target, nodes) { if (!ray.intersectBox(this.boundingBox, target)) { return; } if (isLeafLevel(this.level, this.leafLevel)) { nodes.push(this); return; } for (const child of this.children) { child.intersect(ray, target, nodes); } } remove(object) { const index = this.items.indexOf(object); if (index < 0) { throw new Error(`Item does not exist ${object.uuid}`); } this.items.splice(index, 1); } traverse() { const self = this; if (!self.isEmpty) { return; } const parent = self.parent; if (!parent) { return; } const index = parent.children.indexOf(self); if (index < 0) { throw new Error(`Corrupt octree`); } parent.children.splice(index, 1); this.parent = null; parent.traverse(); } _generateBoundingBoxes() { const self = this; const min = self.boundingBox.min; const max = self.boundingBox.max; const size = (max.x - min.x) / 2; const mins = [ [min.x, min.y + size, min.z + size], [min.x + size, min.y + size, min.z + size], [min.x, min.y, min.z + size], [min.x + size, min.y, min.z + size], [min.x, min.y + size, min.z], [min.x + size, min.y + size, min.z], [min.x, min.y, min.z], [min.x + size, min.y, min.z], ]; const boundingBoxes = []; for (const [minX, minY, minZ] of mins) { boundingBoxes.push(new Box3(new Vector3(minX, minY, minZ), new Vector3(minX + size, minY + size, minZ + size))); } return boundingBoxes; } } class SpatialOctree { constructor(rootLevel, leafLevel) { this.rootLevel = rootLevel; this.leafLevel = leafLevel; if (leafLevel > rootLevel) { throw new Error(); } this._index = new Map(); this._root = this._makeRoot(); } get root() { return this._root; } add(object) { if (!this.root.boundingBox.containsPoint(object.position)) { console.warn(`Object outside bounding box ${object.uuid}`); return false; } const leaf = this._root.add(object); this._index.set(object.uuid, leaf); return true; } has(object) { return this._index.has(object.uuid); } intersect(ray) { const leaves = []; const target = new Vector3(); this._root.intersect(ray, target, leaves); return leaves .map(leaf => leaf.items) .reduce((acc, items) => { acc.push(...items); return acc; }, []); } reset() { this._root = this._makeRoot(); this._index.clear(); } remove(object) { if (!this.has(object)) { throw new Error(`Frame does not exist ${object.uuid}`); } const leaf = this._index.get(object.uuid); leaf.remove(object); leaf.traverse(); this._index.delete(object.uuid); } _makeRoot() { const level = this.rootLevel; const bbox = levelToRootBoundingBox(level); const box = new Box3(new Vector3().fromArray(bbox.min), new Vector3().fromArray(bbox.max)); return new SpatialOctreeNode(level, this.leafLevel, box); } } const MAX_THRESHOLD = 5e-2; const MIN_THRESHOLD = 5e-3; class SpatialIntersection { constructor(octree, raycaster) { this._objects = []; this._objectImageMap = new Map(); this._octree = octree !== null && octree !== void 0 ? octree : new SpatialOctree(OCTREE_ROOT_LEVEL, OCTREE_LEAF_LEVEL); this._raycaster = raycaster !== null && raycaster !== void 0 ? raycaster : new Raycaster(); this._interactiveLayer = 1; this._raycaster = !!raycaster ? raycaster : new Raycaster(undefined, undefined, 1, 10000); this._raycaster.layers.set(this._interactiveLayer); } get interactiveLayer() { return this._interactiveLayer; } get octree() { return this._octree; } get raycaster() { return this._raycaster; } add(object, imageId) { const uuid = object.uuid; const success = this._octree.add(object); if (success) { this._objectImageMap.set(uuid, imageId); this._objects.push(object); } } intersectObjects(viewport, camera) { this._raycaster.setFromCamera(new Vector2().fromArray(viewport), camera); const objects = this._octree.intersect(this.raycaster.ray); const intersects = this._raycaster.intersectObjects(objects); const onMap = this._objectImageMap; for (const intersect of intersects) { const uuid = intersect.object.uuid; if (!onMap.has(uuid)) { continue; } return onMap.get(uuid); } return null; } remove(object) { const objects = this._objects; const index = objects.indexOf(object); if (index !== -1) { const deleted = objects.splice(index, 1); for (const d of deleted) { this._objectImageMap.delete(d.uuid); } this._octree.remove(object); } else { console.warn(`Object does not exist`); } } setIntersectionThreshold(cameraSize) { const threshold = Math.min(Math.max(MIN_THRESHOLD, 2e-1 * cameraSize), MAX_THRESHOLD); this._raycaster.params.Line.threshold = threshold; } } class PositionLine extends Line { constructor(parameters) { super(parameters.geometry, parameters.material); const mode = parameters.mode; const originalOrigin = parameters.originalOrigin; const transform = parameters.transform; const origin = transform.unprojectBasic([0, 0], 0); this._relativeAltitude = originalOrigin[2] - origin[2]; this._makeAttributes(origin, originalOrigin, mode); this.matrixAutoUpdate = false; this.position.fromArray(origin); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setMode(mode) { const positionAttribute = this.geometry.attributes.position; const positions = positionAttribute.array; positions[5] = this._modeToAltitude(mode); positionAttribute.needsUpdate = true; this.geometry.computeBoundingSphere(); } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(origin, originalOrigin, mode) { const positions = new Float32Array(6); positions[0] = 0; positions[1] = 0; positions[2] = 0; positions[3] = originalOrigin[0] - origin[0]; positions[4] = originalOrigin[1] - origin[1]; positions[5] = this._modeToAltitude(mode); const attribute = new BufferAttribute(positions, 3); this.geometry.setAttribute("position", attribute); attribute.needsUpdate = true; this.geometry.computeBoundingSphere(); } _modeToAltitude(mode) { return mode === exports.OriginalPositionMode.Altitude ? this._relativeAltitude : 0; } } class CameraFrameBase extends LineSegments { constructor(parameters) { super(parameters.geometry, parameters.material); const color = parameters.color; const size = parameters.size; const scale = parameters.scale; const transform = parameters.transform; const origin = transform.unprojectBasic([0, 0], 0); const positions = this._makePositions(size, transform, origin); this._makeAttributes(positions, color); this.geometry.computeBoundingSphere(); this.geometry.computeBoundingBox(); this.matrixAutoUpdate = false; this.position.fromArray(origin); this.scale.set(scale, scale, scale); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setColor(color) { this._updateColorAttribute(color); return this; } resize(scale) { this.scale.set(scale, scale, scale); this.updateMatrix(); this.updateMatrixWorld(false); return this; } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(positions, color) { const geometry = this.geometry; const positionAttribute = new BufferAttribute(new Float32Array(positions), 3); geometry.setAttribute("position", positionAttribute); positionAttribute.needsUpdate = true; const colorAttribute = new BufferAttribute(new Float32Array(positions.length), 3); geometry.setAttribute("color", colorAttribute); this._updateColorAttribute(color); } _updateColorAttribute(color) { const [r, g, b] = new Color(color).toArray(); const colorAttribute = this.geometry.attributes.color; const colors = colorAttribute.array; const length = colors.length; let index = 0; for (let i = 0; i < length; i++) { colors[index++] = r; colors[index++] = g; colors[index++] = b; } colorAttribute.needsUpdate = true; } } class SphericalCameraFrame extends CameraFrameBase { _makePositions(size, transform, origin) { const vs = 10; const positions = []; positions.push(...this._makeAxis(size, transform, origin)); positions.push(...this._makeLat(0.5, vs, size, transform, origin)); for (const lat of [0, 0.25, 0.5, 0.75]) { positions .push(...this._makeLng(lat, vs, size, transform, origin)); } return positions; } _makeAxis(size, transform, origin) { const south = transform.unprojectBasic([0.5, 1], 0.8 * size); const north = transform.unprojectBasic([0.5, 0], 1.2 * size); return [ south[0] - origin[0], south[1] - origin[1], south[2] - origin[2], north[0] - origin[0], north[1] - origin[1], north[2] - origin[2], ]; } _makeLat(basicY, numVertices, size, transform, origin) { const dist = 0.8 * size; const [originX, originY, originZ] = origin; const positions = []; const first = transform.unprojectBasic([0, basicY], dist); first[0] -= originX; first[1] -= originY; first[2] -= originZ; positions.push(...first); for (let i = 1; i <= numVertices; i++) { const position = transform.unprojectBasic([i / numVertices, basicY], dist); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position, ...position); } positions.push(...first); return positions; } _makeLng(basicX, numVertices, size, transform, origin) { const dist = 0.8 * size; const [originX, originY, originZ] = origin; const positions = []; const first = transform.unprojectBasic([basicX, 0], dist); first[0] -= originX; first[1] -= originY; first[2] -= originZ; positions.push(...first); for (let i = 0; i <= numVertices; i++) { const position = transform.unprojectBasic([basicX, i / numVertices], dist); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position, ...position); } positions.push(...first); return positions; } } class PerspectiveCameraFrame extends CameraFrameBase { _makePositions(size, transform, origin) { const samples = 8; const positions = []; positions.push(...this._makeDiags(size, transform, origin)); positions.push(...this._makeFrame(size, samples, transform, origin)); return positions; } _makeDiags(size, transform, origin) { const [originX, originY, originZ] = origin; const cameraCenter = [0, 0, 0]; const positions = []; for (const vertex2d of [[0, 0], [1, 0], [1, 1], [0, 1]]) { const corner = transform.unprojectBasic(vertex2d, size); corner[0] -= originX; corner[1] -= originY; corner[2] -= originZ; positions.push(...cameraCenter, ...corner); } return positions; } _makeFrame(size, samples, transform, origin) { const vertices2d = []; vertices2d.push(...this._subsample([0, 1], [0, 0], samples)); vertices2d.push(...this._subsample([0, 0], [1, 0], samples)); vertices2d.push(...this._subsample([1, 0], [1, 1], samples)); const [originX, originY, originZ] = origin; const positions = []; for (const vertex2d of vertices2d) { const position = transform.unprojectBasic(vertex2d, size); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position); } return positions; } _interpolate(a, b, alpha) { return a + alpha * (b - a); } _subsample(p1, p2, subsamples) { if (subsamples < 1) { return [p1, p2]; } const samples = []; samples.push(p1); for (let i = 0; i <= subsamples; i++) { const p = []; for (let j = 0; j < 3; j++) { p.push(this._interpolate(p1[j], p2[j], i / (subsamples + 1))); } samples.push(p); samples.push(p); } samples.push(p2); return samples; } } const SPATIAL_DEFAULT_COLOR = 0xFFFFFF; function resetEnu(reference, prevEnu, prevReference) { const [prevX, prevY, prevZ] = prevEnu; const [lng, lat, alt] = enuToGeodetic(prevX, prevY, prevZ, prevReference.lng, prevReference.lat, prevReference.alt); return geodeticToEnu(lng, lat, alt, reference.lng, reference.lat, reference.alt); } const DEFAULT_ID = exports.CameraVisualizationMode[exports.CameraVisualizationMode.Homogeneous]; class SpatialCell { constructor(id, _scene, _intersection) { this.id = id; this._scene = _scene; this._intersection = _intersection; this.cameras = new Object3D(); this.keys = []; this._positionLines = {}; this._positions = new Object3D(); this._cameraFrames = {}; this._clusters = new Map(); this._connectedComponents = new Map(); this._defaults = new Map(); this._sequences = new Map(); this._props = {}; this.clusterVisibles = {}; this._frameMaterial = new LineBasicMaterial({ vertexColors: true, }); this._positionMaterial = new LineBasicMaterial({ color: 0xff0000, }); this._clusterImages = new Map(); this._scene.add(this.cameras, this._positions); } addImage(props) { const image = props.image; const id = image.id; if (this.hasImage(id)) { throw new Error(`Image exists ${id}`); } const cId = props.idMap.clusterId; if (!this._clusters.has(cId)) { this._clusters.set(cId, []); } const ccId = props.idMap.ccId; if (!(this._connectedComponents.has(ccId))) { this._connectedComponents.set(ccId, []); } if (!(this._defaults.has(DEFAULT_ID))) { this._defaults.set(DEFAULT_ID, []); } const sId = props.idMap.sequenceId; if (!this._sequences.has(sId)) { this._sequences.set(sId, []); } this._props[id] = { image: image, ids: { ccId, clusterId: cId, sequenceId: sId }, }; this.keys.push(id); if (!this._clusterImages.has(cId)) { this._clusterImages.set(cId, []); } this._clusterImages.get(cId).push(id); } applyCameraColor(imageId, color) { this._cameraFrames[imageId].setColor(color); } applyCameraSize(size) { for (const camera of this.cameras.children) { camera.resize(size); } } applyFilter(filter) { var _a; const clusterVisibles = this.clusterVisibles; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; } clusterVisibles[clusterId] = false; } const cameraFrames = this._cameraFrames; const positionLines = this._positionLines; const interactiveLayer = this._intersection.interactiveLayer; for (const props of Object.values(this._props)) { const image = props.image; const visible = filter(image); const key = image.id; positionLines[key].visible = visible; const camera = cameraFrames[key]; this._setCameraVisibility(camera, visible, interactiveLayer); clusterVisibles[_a = props.ids.clusterId] || (clusterVisibles[_a] = visible); } } applyPositionMode(mode) { this._positions.visible = mode !== exports.OriginalPositionMode.Hidden; for (const position of this._positions.children) { position.setMode(mode); } } dispose() { this._disposeCameras(); this._disposePositions(); this._scene = null; this._intersection = null; } disposeCluster(clusterId) { if (!this._clusterImages.has(clusterId)) { return; } const { _cameraFrames, _intersection, _positionLines, _positions, _props, cameras, keys, } = this; const imageIds = this._clusterImages.get(clusterId); for (const imageId of imageIds) { this._disposeCamera(_cameraFrames[imageId], cameras, _intersection); this._disposePosition(_positionLines[imageId], _positions); delete _cameraFrames[imageId]; delete _positionLines[imageId]; const index = keys.indexOf(imageId); if (index !== -1) { keys.splice(index, 1); } delete _props[imageId]; } this._clusterImages.delete(clusterId); this._clusters.delete(clusterId); delete this.clusterVisibles[clusterId]; } getCamerasByMode(mode) { switch (mode) { case exports.CameraVisualizationMode.Cluster: return this._clusters; case exports.CameraVisualizationMode.ConnectedComponent: return this._connectedComponents; case exports.CameraVisualizationMode.Sequence: return this._sequences; default: return this._defaults; } } getColorId(imageId, mode) { const props = this._props[imageId]; const cvm = exports.CameraVisualizationMode; switch (mode) { case cvm.Cluster: return props.ids.clusterId; case cvm.ConnectedComponent: return props.ids.ccId; case cvm.Sequence: return props.ids.sequenceId; default: return DEFAULT_ID; } } hasImage(key) { return this.keys.indexOf(key) !== -1; } getCluster(imageId) { if (!this.hasImage(imageId)) { throw new Error(`Image does not exist (${imageId})`); } return this._props[imageId].ids.clusterId; } resetReference(reference, prevReference) { const frames = this._cameraFrames; for (const frameId in frames) { if (!frames.hasOwnProperty(frameId)) { continue; } const frame = frames[frameId]; frame.position.fromArray(resetEnu(reference, frame.position.toArray(), prevReference)); frame.update(); } const lines = this._positionLines; for (const lineId in lines) { if (!lines.hasOwnProperty(lineId)) { continue; } const line = lines[lineId]; line.position.fromArray(resetEnu(reference, line.position.toArray(), prevReference)); line.update(); } } visualize(props) { var _a, _b; const id = props.id; const visible = props.visible; const transform = props.transform; const cameraParameters = { color: props.color, material: this._frameMaterial, scale: props.scale, size: props.maxSize, transform, }; const camera = isSpherical(transform.cameraType) ? new SphericalCameraFrame(cameraParameters) : new PerspectiveCameraFrame(cameraParameters); const interactiveLayer = this._intersection.interactiveLayer; this._setCameraVisibility(camera, visible, interactiveLayer); this.cameras.add(camera); this._cameraFrames[id] = camera; const intersection = this._intersection; intersection.add(camera, id); const ids = this._props[id].ids; (_a = this.clusterVisibles)[_b = ids.clusterId] || (_a[_b] = visible); const idCamera = { camera, clusterId: ids.clusterId }; this._clusters.get(ids.clusterId).push(idCamera); this._connectedComponents.get(ids.ccId).push(idCamera); this._defaults.get(DEFAULT_ID).push(idCamera); this._sequences.get(ids.sequenceId).push(idCamera); const positionParameters = { material: this._positionMaterial, mode: props.positionMode, originalOrigin: props.originalPosition, transform, }; const position = new PositionLine(positionParameters); position.visible = visible; this._positions.add(position); this._positionLines[id] = position; } _disposeCamera(camera, cameras, intersection) { camera.dispose(); intersection.remove(camera); cameras.remove(camera); } _disposeCameras() { const intersection = this._intersection; const cameras = this.cameras; for (const camera of cameras.children.slice()) { this._disposeCamera(camera, cameras, intersection); } this._scene.remove(this.cameras); } _disposePosition(position, positions) { position.dispose(); positions.remove(position); } _disposePositions() { const positions = this._positions; for (const position of positions.children.slice()) { this._disposePosition(position, positions); } this._scene.remove(this._positions); } _setCameraVisibility(camera, visible, layer) { camera.visible = visible; if (visible) { camera.layers.enable(layer); } else { camera.layers.disable(layer); } } } class SpatialAssets { constructor() { this._colors = new Map(); const cvm = exports.CameraVisualizationMode; this._colors.set(cvm[cvm.Homogeneous], "#FFFFFF"); } getColor(id) { const colors = this._colors; if (!colors.has(id)) { colors.set(id, this._randomColor()); } return colors.get(id); } _randomColor() { return `hsl(${Math.floor(360 * Math.random())}, 100%, 60%)`; } } function isModeVisible(mode) { return mode !== exports.CameraVisualizationMode.Hidden; } function isOverviewState(state) { return state === State.Custom || state === State.Earth; } exports.PointVisualizationMode = void 0; (function (PointVisualizationMode) { /** * Points are hidden. */ PointVisualizationMode[PointVisualizationMode["Hidden"] = 0] = "Hidden"; /** * Visualize points with original colors. */ PointVisualizationMode[PointVisualizationMode["Original"] = 1] = "Original"; /** * Paint all points belonging to a specific * cluster with the same random color. */ PointVisualizationMode[PointVisualizationMode["Cluster"] = 2] = "Cluster"; })(exports.PointVisualizationMode || (exports.PointVisualizationMode = {})); const NO_CLUSTER_ID = "NO_CLUSTER_ID"; const NO_MERGE_ID = "NO_MERGE_ID"; const NO_SEQUENCE_ID = "NO_SEQUENCE_ID"; const RAY_NEAR_SCALE = 1.2; const ORIGINAL_CAMERA_SIZE = 1; const ORIGINAL_POINT_SIZE = 2; class SpatialScene { constructor(configuration, scene) { this._imageCellMap = new Map(); this._clusterCellMap = new Map(); this._clusterImageMap = new Map(); this._scene = !!scene ? scene : new Scene(); this._scene.matrixWorldAutoUpdate = false; this._intersection = new SpatialIntersection(); this._assets = new SpatialAssets(); this._needsRender = false; this._images = {}; this._cells = {}; this._cellClusters = {}; this._clusters = {}; this._cameraVisualizationMode = !!configuration.cameraVisualizationMode ? configuration.cameraVisualizationMode : exports.CameraVisualizationMode.Homogeneous; this._cameraSize = configuration.cameraSize; this._pointSize = configuration.pointSize; this._pointVisualizationMode = !!configuration.pointVisualizationMode ? configuration.pointVisualizationMode : exports.PointVisualizationMode.Original; this._positionMode = configuration.originalPositionMode; this._cellsVisible = configuration.cellsVisible; this._hoveredId = null; this._selectedId = null; this._colors = { hover: "#FF0000", select: "#FF8000" }; this._cameraOverrideColors = new Map(); this._pointOverrideColors = new Map(); this._filter = () => true; } get needsRender() { return this._needsRender; } get intersection() { return this._intersection; } addCluster(reconstruction, translation, cellId) { if (this.hasCluster(reconstruction.id, cellId)) { return; } const clusterId = reconstruction.id; if (!(clusterId in this._clusters)) { const color = this._getPointColor(clusterId); const points = new ClusterPoints({ cluster: reconstruction, color, originalSize: ORIGINAL_POINT_SIZE, scale: this._pointSize, translation, }); points.visible = this._getClusterVisible(clusterId); this._scene.add(points); this._clusters[clusterId] = { cellIds: [], points: points, }; } if (this._clusters[clusterId].cellIds.indexOf(cellId) === -1) { this._clusters[clusterId].cellIds.push(cellId); } if (!(cellId in this._cellClusters)) { this._cellClusters[cellId] = { keys: [] }; } if (this._cellClusters[cellId].keys.indexOf(clusterId) === -1) { this._cellClusters[cellId].keys.push(clusterId); } if (!this._clusterImageMap.has(clusterId)) { this._clusterImageMap.set(clusterId, new Set()); } this._needsRender = true; } addImage(image, transform, originalPosition, cellId) { var _a, _b, _c; const imageId = image.id; const idMap = { clusterId: (_a = image.clusterId) !== null && _a !== void 0 ? _a : NO_CLUSTER_ID, sequenceId: (_b = image.sequenceId) !== null && _b !== void 0 ? _b : NO_SEQUENCE_ID, ccId: (_c = image.mergeId) !== null && _c !== void 0 ? _c : NO_MERGE_ID, }; if (!(cellId in this._images)) { const created = new SpatialCell(cellId, this._scene, this._intersection); created.cameras.visible = isModeVisible(this._cameraVisualizationMode); created.applyPositionMode(this._positionMode); this._images[cellId] = created; } const cell = this._images[cellId]; if (cell.hasImage(imageId)) { return; } cell.addImage({ idMap, image: image }); const colorId = cell.getColorId(imageId, this._cameraVisualizationMode); let color = this._cameraOverrideColors.has(idMap.clusterId) ? this._cameraOverrideColors.get(idMap.clusterId) : this._assets.getColor(colorId); const visible = this._filter(image); cell.visualize({ id: imageId, color, positionMode: this._positionMode, scale: this._cameraSize, transform, visible, maxSize: ORIGINAL_CAMERA_SIZE, originalPosition }); if (!this._clusterCellMap.has(idMap.clusterId)) { this._clusterCellMap.set(idMap.clusterId, new Set()); } const clusterCells = this._clusterCellMap.get(idMap.clusterId); if (!clusterCells.has(cellId)) { clusterCells.add(cellId); } this._imageCellMap.set(imageId, cellId); if (!this._clusterImageMap.has(idMap.clusterId)) { this._clusterImageMap.set(idMap.clusterId, new Set()); } this._clusterImageMap.get(idMap.clusterId).add(imageId); if (imageId === this._selectedId) { this._highlight(imageId, this._colors.select, this._cameraVisualizationMode); } if (idMap.clusterId in this._clusters) { const clusterVisible = this._getClusterVisible(idMap.clusterId); this._clusters[idMap.clusterId].points.visible = clusterVisible; } this._needsRender = true; } addCell(vertices, cellId) { if (this.hasCell(cellId)) { return; } const cell = new CellLine(vertices); this._cells[cellId] = cell; this._cells[cellId].visible = this._cellsVisible; this._scene.add(this._cells[cellId]); this._needsRender = true; } deactivate() { this._filter = () => true; this._selectedId = null; this._hoveredId = null; this.uncache(); } getCameraOverrideColor(clusterId) { return this._cameraOverrideColors.get(clusterId); } getPointOverrideColor(clusterId) { return this._pointOverrideColors.get(clusterId); } hasCell(cellId) { return cellId in this._cells; } hasCluster(clusterId, cellId) { return clusterId in this._clusters && this._clusters[clusterId].cellIds.indexOf(cellId) !== -1; } hasImage(imageId, cellId) { return cellId in this._images && this._images[cellId].hasImage(imageId); } render(camera, renderer) { renderer.render(this._scene, camera); this._needsRender = false; } resetReference(reference, prevReference) { const clusters = this._clusters; for (const clusterId in clusters) { if (!clusters.hasOwnProperty(clusterId)) { continue; } const cluster = clusters[clusterId]; cluster.points.position.fromArray(resetEnu(reference, cluster.points.position.toArray(), prevReference)); cluster.points.update(); } const cells = this._cells; for (const cellId in cells) { if (!cells.hasOwnProperty(cellId)) { continue; } const cell = cells[cellId]; cell.position.clone(); cell.position.fromArray(resetEnu(reference, cell.position.toArray(), prevReference)); cell.update(); } const images = this._images; for (const cellId in images) { if (!images.hasOwnProperty(cellId)) { continue; } const spatialCell = images[cellId]; spatialCell.resetReference(reference, prevReference); } } setCameraOverrideColor(clusterId, color) { if (color != null) { this._cameraOverrideColors.set(clusterId, color); } else { this._cameraOverrideColors.delete(clusterId); } if (!this._clusterCellMap.has(clusterId)) { return; } const cellIds = this._clusterCellMap.get(clusterId); this._applyCameraColor([...cellIds.keys()]); this._needsRender = true; } setCameraSize(cameraSize) { if (Math.abs(cameraSize - this._cameraSize) < 1e-4) { return; } const imageCells = this._images; for (const cellId of Object.keys(imageCells)) { imageCells[cellId].applyCameraSize(cameraSize); } this._intersection.raycaster.near = this._getNear(cameraSize); this._intersection.setIntersectionThreshold(cameraSize); this._cameraSize = cameraSize; this._needsRender = true; } setCameraVisualizationMode(mode) { if (mode === this._cameraVisualizationMode) { return; } this._cameraVisualizationMode = mode; this._applyCameraColor(Object.keys(this._images)); this._needsRender = true; } setCellVisibility(visible) { if (visible === this._cellsVisible) { return; } for (const cellId in this._cells) { if (!this._cells.hasOwnProperty(cellId)) { continue; } this._cells[cellId].visible = visible; } this._cellsVisible = visible; this._needsRender = true; } setFilter(filter) { this._filter = filter; const clusterVisibles = {}; for (const imageCell of Object.values(this._images)) { imageCell.applyFilter(filter); const imageCV = imageCell.clusterVisibles; for (const clusterId in imageCV) { if (!imageCV.hasOwnProperty(clusterId)) { continue; } if (!(clusterId in clusterVisibles)) { clusterVisibles[clusterId] = false; } clusterVisibles[clusterId] || (clusterVisibles[clusterId] = imageCV[clusterId]); } } const pointsVisible = this._pointVisualizationMode !== exports.PointVisualizationMode.Hidden; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; } clusterVisibles[clusterId] && (clusterVisibles[clusterId] = pointsVisible); const visible = clusterVisibles[clusterId]; if (clusterId in this._clusters) { this._clusters[clusterId].points.visible = visible; } } this._needsRender = true; } setHoveredImage(imageId) { if (imageId != null && !this._imageCellMap.has(imageId)) { throw new MapillaryError(`Image does not exist: ${imageId}`); } if (this._hoveredId === imageId) { return; } this._needsRender = true; if (this._hoveredId != null) { if (this._hoveredId === this._selectedId) { this._highlight(this._hoveredId, this._colors.select, this._cameraVisualizationMode); } else { this._resetCameraColor(this._hoveredId); } } this._highlight(imageId, this._colors.hover, this._cameraVisualizationMode); this._hoveredId = imageId; } setPointOverrideColor(clusterId, color) { if (color != null) { this._pointOverrideColors.set(clusterId, color); } else { this._pointOverrideColors.delete(clusterId); } this._applyPointColor(clusterId); this._needsRender = true; } setPointSize(pointSize) { if (Math.abs(pointSize - this._pointSize) < 1e-4) { return; } const clusters = this._clusters; for (const key in clusters) { if (!clusters.hasOwnProperty(key)) { continue; } clusters[key].points.resize(pointSize); } this._pointSize = pointSize; this._needsRender = true; } setPointVisualizationMode(mode) { if (mode === this._pointVisualizationMode) { return; } this._pointVisualizationMode = mode; for (const clusterId in this._clusters) { if (!this._clusters.hasOwnProperty(clusterId)) { continue; } this._applyPointColor(clusterId); } this._needsRender = true; } setPositionMode(mode) { if (mode === this._positionMode) { return; } for (const cell of Object.values(this._images)) { cell.applyPositionMode(mode); } this._positionMode = mode; this._needsRender = true; } setSelectedImage(id) { if (this._selectedId === id) { return; } if (this._selectedId != null) { this._resetCameraColor(this._selectedId); } this._highlight(id, this._colors.select, this._cameraVisualizationMode); this._selectedId = id; this._needsRender = true; } uncache(keepCellIds) { for (const cellId of Object.keys(this._cellClusters)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._disposeReconstruction(cellId); } for (const cellId of Object.keys(this._images)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } const nceMap = this._imageCellMap; const keys = this._images[cellId].keys; for (const key of keys) { nceMap.delete(key); } this._images[cellId].dispose(); delete this._images[cellId]; } for (const cellId of Object.keys(this._cells)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._disposeCell(cellId); } this._needsRender = true; } uncacheCluster(clusterId) { var _a; const cellIds = new Set(); if (clusterId in this._clusters) { const cluster = this._clusters[clusterId]; for (const cellId of cluster.cellIds) { cellIds.add(cellId); } this._scene.remove(cluster.points); cluster.points.dispose(); delete this._clusters[clusterId]; } for (const cellId of (_a = this._clusterCellMap.get(clusterId)) !== null && _a !== void 0 ? _a : []) { cellIds.add(cellId); } this._clusterCellMap.delete(clusterId); for (const cellId of cellIds.values()) { if (!(cellId in this._cellClusters)) { continue; } const cellClusters = this._cellClusters[cellId]; const index = cellClusters.keys.indexOf(clusterId); if (index !== -1) { cellClusters.keys.splice(index, 1); } } for (const cellId of cellIds.values()) { if (!(cellId in this._images)) { continue; } const cell = this._images[cellId]; cell.disposeCluster(clusterId); } if (this._clusterImageMap.has(clusterId)) { const imageCellMap = this._imageCellMap; const imageIds = this._clusterImageMap.get(clusterId).values(); for (const imageId of imageIds) { imageCellMap.delete(imageId); } this._clusterImageMap.delete(clusterId); } this._needsRender = true; } _applyCameraColor(cellIds) { const mode = this._cameraVisualizationMode; const visible = isModeVisible(mode); const assets = this._assets; const overrides = this._cameraOverrideColors; const images = this._images; for (const cellId of cellIds) { if (!(cellId in images)) { continue; } const cell = images[cellId]; cell.cameras.visible = visible; const cameraMap = cell.getCamerasByMode(mode); cameraMap.forEach((cameras, colorId) => { let color = assets.getColor(colorId); for (const camera of cameras) { if (overrides.has(camera.clusterId)) { camera.camera.setColor(overrides.get(camera.clusterId)); } else { camera.camera.setColor(color); } } }); } this._highlight(this._hoveredId, this._colors.hover, mode); this._highlight(this._selectedId, this._colors.select, mode); } _applyPointColor(clusterId) { if (!(clusterId in this._clusters)) { return; } const cluster = this._clusters[clusterId]; cluster.points.visible = this._getClusterVisible(clusterId); const color = this._getPointColor(clusterId); cluster.points.setColor(color); } _getClusterVisible(clusterId) { if (this._pointVisualizationMode === exports.PointVisualizationMode.Hidden) { return false; } let visible = false; for (const imageCell of Object.values(this._images)) { const imageCV = imageCell.clusterVisibles; if (!(clusterId in imageCV)) { continue; } visible || (visible = imageCV[clusterId]); } return visible; } _disposeCell(cellId) { const cell = this._cells[cellId]; for (const line of cell.children.slice()) { line.dispose(); cell.remove(line); } this._scene.remove(cell); delete this._cells[cellId]; } _disposePoints(cellId) { for (const clusterId of this._cellClusters[cellId].keys) { if (!(clusterId in this._clusters)) { continue; } const index = this._clusters[clusterId].cellIds.indexOf(cellId); if (index === -1) { continue; } const cluster = this._clusters[clusterId]; cluster.cellIds.splice(index, 1); if (cluster.cellIds.length > 0) { continue; } this._scene.remove(cluster.points); cluster.points.dispose(); delete this._clusters[clusterId]; } } _disposeReconstruction(cellId) { this._disposePoints(cellId); delete this._cellClusters[cellId]; } _getNear(cameraSize) { const near = RAY_NEAR_SCALE * ORIGINAL_CAMERA_SIZE * cameraSize; return Math.max(0.01, near); } _getPointColor(clusterId) { let color = null; if (this._pointVisualizationMode === exports.PointVisualizationMode.Cluster) { color = this._assets.getColor(clusterId); } if (this._pointOverrideColors.has(clusterId)) { color = this._pointOverrideColors.get(clusterId); } return color; } _highlight(imageId, color, mode) { const nceMap = this._imageCellMap; if (imageId == null || !nceMap.has(imageId)) { return; } const cellId = nceMap.get(imageId); const cell = this._images[cellId]; const clusterId = cell.getCluster(imageId); const overridden = this._cameraOverrideColors.get(clusterId); color = mode === exports.CameraVisualizationMode.Homogeneous && !overridden ? color : SPATIAL_DEFAULT_COLOR; this._images[cellId].applyCameraColor(imageId, color); } _resetCameraColor(imageId) { const nceMap = this._imageCellMap; if (imageId == null || !nceMap.has(imageId)) { return; } const cellId = nceMap.get(imageId); const cell = this._images[cellId]; const colorId = cell.getColorId(imageId, this._cameraVisualizationMode); let color = this._assets.getColor(colorId); const clusterId = cell.getCluster(imageId); if (this._cameraOverrideColors.has(clusterId)) { color = this._cameraOverrideColors.get(clusterId); } cell.applyCameraColor(imageId, color); } } class SpatialCache { constructor(graphService, api) { this._graphService = graphService; this._api = api; this._cells = new Map(); this._clusters = {}; this._cellClusters = {}; this._cellImageRequests = {}; this._cellClusterRequests = {}; this._clusterRequests = new Set(); } cacheClusters$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot cache reconstructions of a non-existing cell."); } if (this.hasClusters(cellId)) { throw new Error("Cannot cache reconstructions that already exists."); } if (this.isCachingClusters(cellId)) { return this._cellClusterRequests[cellId].request; } const duplicatedClusters = this.getCell(cellId) .filter((n) => { return !!n.clusterId && !!n.clusterUrl; }) .map((n) => { return { key: n.clusterId, url: n.clusterUrl }; }); const clusters = Array .from(new Map(duplicatedClusters.map((cd) => { return [cd.key, cd]; })) .values()); this._cellClusters[cellId] = clusters; let cancel; const cancellationToken = new Promise((_, reject) => { cancel = reject; }); this._cellClusterRequests[cellId] = { cancel, request: this._cacheClusters$(clusters, cellId, cancellationToken).pipe(finalize(() => { if (cellId in this._cellClusterRequests) { delete this._cellClusterRequests[cellId]; } }), publish(), refCount()) }; return this._cellClusterRequests[cellId].request; } cacheCell$(cellId) { if (this.hasCell(cellId)) { throw new Error("Cannot cache cell that already exists."); } if (this.isCachingCell(cellId)) { return this._cellImageRequests[cellId]; } this._cellImageRequests[cellId] = this._graphService.cacheCell$(cellId).pipe(catchError((error) => { console.error(error); return empty(); }), filter(() => { return !this._cells.has(cellId); }), tap((images) => { const cell = { clusters: new Map(), images: new Map(), }; this._cells.set(cellId, cell); for (const image of images) { cell.images.set(image.id, image); const clusterId = image.clusterId; if (!cell.clusters.has(clusterId)) { cell.clusters.set(clusterId, []); } const clusterImageIds = cell.clusters.get(clusterId); clusterImageIds.push(image.id); } delete this._cellImageRequests[cellId]; }), finalize(() => { if (cellId in this._cellImageRequests) { delete this._cellImageRequests[cellId]; } }), publish(), refCount()); return this._cellImageRequests[cellId]; } isCachingClusters(cellId) { return cellId in this._cellClusterRequests; } isCachingCell(cellId) { return cellId in this._cellImageRequests; } hasClusters(cellId) { if (cellId in this._cellClusterRequests || !(cellId in this._cellClusters)) { return false; } for (const cd of this._cellClusters[cellId]) { if (!(cd.key in this._clusters)) { return false; } } return true; } hasCell(cellId) { return !(cellId in this._cellImageRequests) && this._cells.has(cellId); } getClusters(cellId) { return cellId in this._cellClusters ? this._cellClusters[cellId] .map((cd) => { const cluster = this._clusters[cd.key]; return cluster ? cluster.contract : null; }) .filter((reconstruction) => { return !!reconstruction; }) : []; } getCell(cellId) { return this._cells.has(cellId) ? Array.from(this._cells.get(cellId).images.values()) : []; } removeCluster(clusterId) { var _a; this._clusterRequests.delete(clusterId); if (clusterId in this._clusters) { delete this._clusters[clusterId]; } const cellIds = []; for (const [cellId, cell] of this._cells.entries()) { if (cell.clusters.has(clusterId)) { cellIds.push(cellId); } } for (const cellId of cellIds) { if (!this._cells.has(cellId)) { continue; } const cell = this._cells.get(cellId); const clusterImages = (_a = cell.clusters.get(clusterId)) !== null && _a !== void 0 ? _a : []; for (const imageId of clusterImages) { cell.images.delete(imageId); } cell.clusters.delete(clusterId); if (cellId in this._cellClusters) { const cellClusters = this._cellClusters[cellId]; const index = cellClusters.findIndex(cd => cd.key === clusterId); if (index !== -1) { cellClusters.splice(index, 1); } } } } uncache(keepCellIds) { for (const cellId of Object.keys(this._cellClusterRequests)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._cellClusterRequests[cellId].cancel(); delete this._cellClusterRequests[cellId]; } for (let cellId of Object.keys(this._cellClusters)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } for (const cd of this._cellClusters[cellId]) { if (!(cd.key in this._clusters)) { continue; } const { cellIds } = this._clusters[cd.key]; cellIds.delete(cellId); if (cellIds.size > 0) { continue; } delete this._clusters[cd.key]; } delete this._cellClusters[cellId]; } for (let cellId of this._cells.keys()) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._cells.delete(cellId); } } updateCell$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot update cell that does not exists."); } return this._graphService.cacheCell$(cellId).pipe(catchError((error) => { console.error(error); return empty(); }), filter(() => { return this._cells.has(cellId); }), tap((images) => { const cell = this._cells.get(cellId); for (const image of images) { cell.images.set(image.id, image); const clusterId = image.clusterId; if (!cell.clusters.has(clusterId)) { cell.clusters.set(clusterId, []); } const clusterImageIds = cell.clusters.get(clusterId); clusterImageIds.push(image.id); } }), publish(), refCount()); } updateClusters$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot update reconstructions of a non-existing cell."); } if (!this.hasClusters(cellId)) { throw new Error("Cannot update reconstructions for cell that is not cached."); } const duplicatedClusters = this.getCell(cellId) .filter((n) => { return !!n.clusterId && !!n.clusterUrl; }) .map((n) => { return { key: n.clusterId, url: n.clusterUrl }; }); const clusters = Array .from(new Map(duplicatedClusters.map((cd) => { return [cd.key, cd]; })) .values()) .filter(cd => { return !(cd.key in this._clusters); }); this._cellClusters[cellId].push(...clusters); return this._cacheClusters$(clusters, cellId, null); } _cacheClusters$(clusters, cellId, cancellation) { return from(clusters).pipe(mergeMap((cd) => { if (this._hasCluster(cd.key)) { return of(this._getCluster(cd.key)); } this._clusterRequests.add(cd.key); return this._getCluster$(cd.url, cd.key, cancellation) .pipe(catchError((error) => { if (error instanceof CancelMapillaryError) { return empty(); } console.error(error); return empty(); })); }, 6), filter((cluster) => { return cellId in this._cellClusters && this._clusterRequests.has(cluster.id); }), tap((cluster) => { if (!this._hasCluster(cluster.id)) { this._clusters[cluster.id] = { cellIds: new Set(), contract: cluster, }; } const { cellIds } = this._clusters[cluster.id]; cellIds.add(cellId); this._clusterRequests.delete(cluster.id); })); } _getCluster(id) { return this._clusters[id].contract; } _getCluster$(url, clusterId, abort) { return Observable.create((subscriber) => { this._api.data.getCluster(url, abort) .then((reconstruction) => { reconstruction.id = clusterId; subscriber.next(reconstruction); subscriber.complete(); }, (error) => { subscriber.error(error); }); }); } _hasCluster(id) { return id in this._clusters; } } function connectedComponent(cellId, depth, geometry) { const cells = new Set(); cells.add(cellId); connectedComponentRecursive(cells, [cellId], 0, depth, geometry); return Array.from(cells); } function connectedComponentRecursive(cells, current, currentDepth, maxDepth, geometry) { if (currentDepth >= maxDepth) { return; } const adjacent = []; for (const cellId of current) { const aCells = geometry.getAdjacent(cellId); adjacent.push(...aCells); } const newCells = []; for (const a of adjacent) { if (cells.has(a)) { continue; } cells.add(a); newCells.push(a); } connectedComponentRecursive(cells, newCells, currentDepth + 1, maxDepth, geometry); } class SpatialComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._cache = new SpatialCache(navigator.graphService, navigator.api); this._scene = new SpatialScene(this._getDefaultConfiguration()); this._viewportCoords = new ViewportCoords(); this._spatial = new Spatial(); } /** * Get the currently set camera frame override color, or null if * no color is set. * * @param {string} clusterId - Id of the cluster. * * @returns {string | number | null} The current override color * for the cluster. */ getCameraOverrideColor(clusterId) { return this._scene.getCameraOverrideColor(clusterId); } /** * Returns the image id of the camera frame closest to the current * render camera position at the specified point. * * @description Notice that the pixelPoint argument requires x, y * coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the spatial component. * * If no camera frame exist at the pixel * point, `null` will be returned. * * @param {Array} pixelPoint - Pixel coordinates on * the viewer element. * @returns {string} Image id of the camera frame closest to * the camera. If no camera frame is intersected at the * pixel point, `null` will be returned. * * @example * ```js * spatialComponent.getFrameIdAt([100, 125]) * .then((imageId) => { console.log(imageId); }); * ``` */ getFrameIdAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const id = this._scene.intersection .intersectObjects(viewport, render.perspective); return id; })) .subscribe((id) => { resolve(id); }, (error) => { reject(error); }); }); } /** * Get the currently set point override color, or null if * no color is set. * * @param {string} clusterId - Id of the cluster. * * @returns {string | number | null} The current override color * for the cluster. */ getPointOverrideColor(clusterId) { return this._scene.getPointOverrideColor(clusterId); } /** * Override the camera color for a cluster. * * @description The configured color is applied for all visible * visualization modes, overriding the color of the currently * selected mode. * * @param {string} clusterId - Id of the cluster to configure. * @param {number | string} color - The color to paint the cameras with. * * @example * ```js * spatialComponent.setCameraOverrideColor("my-cluster-id", 0x00ff00); * ``` */ setCameraOverrideColor(clusterId, color) { this._scene.setCameraOverrideColor(clusterId, color); } /** * Override the point color for a cluster. * * @description The configured color is applied for all visible * visualization modes, overriding the color of the currently * selected mode. * * @param {string} clusterId - Id of the cluster to configure. * @param {number | string} color - The color to paint the points with. * * @example * ```js * spatialComponent.setPointOverrideColor("my-cluster-id", 0x00ff00); * ``` */ setPointOverrideColor(clusterId, color) { this._scene.setPointOverrideColor(clusterId, color); } _activate() { this._navigator.cacheService.configure({ cellDepth: 3 }); const subs = this._subscriptions; subs.push(this._configuration$ .pipe(map((c) => { var _a; c.cameraSize = this._spatial.clamp(c.cameraSize, MIN_CAMERA_SIZE, MAX_CAMERA_SIZE); c.pointSize = this._spatial.clamp(c.pointSize, MIN_POINT_SIZE, MAX_POINT_SIZE); c.cellGridDepth = this._spatial.clamp(c.cellGridDepth, 1, 3); const pointVisualizationMode = c.pointsVisible ? (_a = c.pointVisualizationMode) !== null && _a !== void 0 ? _a : exports.PointVisualizationMode.Original : exports.PointVisualizationMode.Hidden; return { cameraSize: c.cameraSize, cameraVisualizationMode: c.cameraVisualizationMode, cellsVisible: c.cellsVisible, originalPositionMode: c.originalPositionMode, pointSize: c.pointSize, pointVisualizationMode, }; }), distinctUntilChanged((c1, c2) => { return c1.cameraSize === c2.cameraSize && c1.cameraVisualizationMode === c2.cameraVisualizationMode && c1.cellsVisible === c2.cellsVisible && c1.originalPositionMode === c2.originalPositionMode && c1.pointSize === c2.pointSize && c1.pointVisualizationMode === c2.pointVisualizationMode; })) .subscribe((c) => { this._scene.setCameraSize(c.cameraSize); const cvm = c.cameraVisualizationMode; this._scene.setCameraVisualizationMode(cvm); this._scene.setCellVisibility(c.cellsVisible); this._scene.setPointSize(c.pointSize); const pvm = c.pointVisualizationMode; this._scene.setPointVisualizationMode(pvm); const opm = c.originalPositionMode; this._scene.setPositionMode(opm); })); subs.push(this._navigator.graphService.dataReset$ .subscribe(() => { this._cache.uncache(); this._scene.uncache(); })); subs.push(this._navigator.stateService.reference$ .pipe(pairwise()) .subscribe(([prevReference, reference]) => { this._scene.resetReference(reference, prevReference); })); subs.push(this._navigator.graphService.filter$ .subscribe(imageFilter => { this._scene.setFilter(imageFilter); })); const bearing$ = this._container.renderService.bearing$.pipe(map((bearing) => { const interval = 6; const discrete = interval * Math.floor(bearing / interval); return discrete; }), distinctUntilChanged(), publishReplay(1), refCount()); const currentImage$ = this._navigator.stateService.currentImage$; const cellId$ = currentImage$ .pipe(map((image) => { return this._navigator.api.data.geometry .lngLatToCellId(image.originalLngLat); }), distinctUntilChanged(), publishReplay(1), refCount()); const cellGridDepth$ = this._configuration$ .pipe(map((c) => { return c.cellGridDepth; }), distinctUntilChanged(), publishReplay(1), refCount()); const sequencePlay$ = combineLatest(this._navigator.playService.playing$, this._navigator.playService.speed$).pipe(map(([playing, speed]) => { return playing && speed > PlayService.sequenceSpeed; }), distinctUntilChanged(), publishReplay(1), refCount()); const isOverview$ = this._navigator.stateService.state$.pipe(map((state) => { return isOverviewState(state); }), distinctUntilChanged(), publishReplay(1), refCount()); const cell$ = combineLatest(isOverview$, sequencePlay$, bearing$, cellGridDepth$, currentImage$) .pipe(distinctUntilChanged(([o1, s1, b1, d1, i1], [o2, s2, b2, d2, i2]) => { if (!i1.assetsCached) { return false; } if (o1 !== o2) { return false; } const isd = i1.id === i2.id && s1 === s2 && d1 === d2; if (o1) { return isd; } return isd && b1 === b2; }), concatMap(([isOverview, sequencePlay, bearing, depth, image]) => { if (isOverview) { const geometry = this._navigator.api.data.geometry; const cellId = geometry .lngLatToCellId(image.originalLngLat); const cells = sequencePlay ? [cellId] : connectedComponent(cellId, depth, geometry); return of(cells); } const fov = sequencePlay ? 30 : 90; return of(this._cellsInFov(image, bearing, fov)); }), switchMap((cellIds) => { return from(cellIds).pipe(mergeMap((cellId) => { const t$ = this._cache.hasCell(cellId) ? of(this._cache.getCell(cellId)) : this._cache.cacheCell$(cellId); return t$.pipe(map((images) => ({ id: cellId, images }))); }, 6)); })); subs.push(cell$.pipe(withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([cell, reference]) => { if (this._scene.hasCell(cell.id)) { return; } this._scene.addCell(this._cellToTopocentric(cell.id, reference), cell.id); })); subs.push(cell$.pipe(withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([cell, reference]) => { this._addSceneImages(cell, reference); })); subs.push(cell$.pipe(concatMap((cell) => { const cellId = cell.id; let reconstructions$; if (this._cache.hasClusters(cellId)) { reconstructions$ = from(this._cache.getClusters(cellId)); } else if (this._cache.isCachingClusters(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId).pipe(last(null, {}), switchMap(() => { return from(this._cache.getClusters(cellId)); })); } else if (this._cache.hasCell(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId); } else { reconstructions$ = empty(); } return combineLatest(of(cellId), reconstructions$); }), withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([[cellId, reconstruction], reference]) => { if (this._scene .hasCluster(reconstruction.id, cellId)) { return; } this._scene.addCluster(reconstruction, this._computeTranslation(reconstruction, reference), cellId); })); subs.push(combineLatest(cellId$, cellGridDepth$) .subscribe(([cellId, depth]) => { const keepCells = connectedComponent(cellId, depth, this._navigator.api.data.geometry); this._scene.uncache(keepCells); this._cache.uncache(keepCells); })); subs.push(this._navigator.playService.playing$.pipe(switchMap((playing) => { return playing ? empty() : this._container.mouseService.dblClick$; }), withLatestFrom(this._container.renderService.renderCamera$), switchMap(([event, render]) => { const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords .canvasPosition(event, element); const viewport = this._viewportCoords.canvasToViewport(canvasX, canvasY, element); const id = this._scene.intersection .intersectObjects(viewport, render.perspective); return !!id ? this._navigator.moveTo$(id).pipe(catchError(() => { return empty(); })) : empty(); })) .subscribe()); const intersectChange$ = combineLatest(this._configuration$, this._navigator.stateService.state$).pipe(map(([c, state]) => { return { size: c.cameraSize, visible: isModeVisible(c.cameraVisualizationMode), state, }; }), distinctUntilChanged((c1, c2) => { return c1.size === c2.size && c1.visible === c2.visible && c1.state === c2.state; })); const mouseMove$ = this._container.mouseService.mouseMove$.pipe(publishReplay(1), refCount()); subs.push(mouseMove$.subscribe()); const mouseHover$ = merge(this._container.mouseService.mouseEnter$, this._container.mouseService.mouseLeave$, this._container.mouseService.windowBlur$); subs.push(combineLatest(this._navigator.playService.playing$, mouseHover$, isOverview$, this._navigator.graphService.filter$) .pipe(switchMap(([playing, mouseHover]) => { return !playing && mouseHover.type === "pointerenter" ? combineLatest(concat(mouseMove$.pipe(take(1)), this._container.mouseService.mouseMove$), this._container.renderService.renderCamera$, intersectChange$) : combineLatest(of(mouseHover), of(null), of(null)); })) .subscribe(([event, render]) => { if (event.type !== "pointermove") { this._scene.setHoveredImage(null); return; } const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const viewport = this._viewportCoords.canvasToViewport(canvasX, canvasY, element); const key = this._scene.intersection .intersectObjects(viewport, render.perspective); this._scene.setHoveredImage(key); })); subs.push(this._navigator.stateService.currentId$ .subscribe((id) => { this._scene.setSelectedImage(id); })); subs.push(this._navigator.stateService.currentState$ .pipe(map((frame) => { const scene = this._scene; return { name: this._name, renderer: { frameId: frame.id, needsRender: scene.needsRender, render: scene.render.bind(scene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); subs.push(this._navigator.graphService.dataDeleted$ .subscribe((clusterIds) => { for (const clusterId of clusterIds) { this._cache.removeCluster(clusterId); this._scene.uncacheCluster(clusterId); } })); const updatedCell$ = this._navigator.graphService.dataAdded$ .pipe(filter((cellId) => { return this._cache.hasCell(cellId); }), mergeMap((cellId) => { return this._cache.updateCell$(cellId).pipe(map((images) => ({ id: cellId, images })), withLatestFrom(this._navigator.stateService.reference$)); }), publish(), refCount()); subs.push(updatedCell$ .subscribe(([cell, reference]) => { this._addSceneImages(cell, reference); })); subs.push(updatedCell$ .pipe(concatMap(([cell]) => { const cellId = cell.id; const cache = this._cache; let reconstructions$; if (cache.hasClusters(cellId)) { reconstructions$ = cache.updateClusters$(cellId); } else if (cache.isCachingClusters(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId).pipe(last(null, {}), switchMap(() => { return from(cache.updateClusters$(cellId)); })); } else { reconstructions$ = empty(); } return combineLatest(of(cellId), reconstructions$); }), withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([[cellId, reconstruction], reference]) => { if (this._scene.hasCluster(reconstruction.id, cellId)) { return; } this._scene.addCluster(reconstruction, this._computeTranslation(reconstruction, reference), cellId); })); } _deactivate() { this._subscriptions.unsubscribe(); this._cache.uncache(); this._scene.deactivate(); this._navigator.cacheService.configure(); } _getDefaultConfiguration() { return { cameraSize: 0.1, cameraVisualizationMode: exports.CameraVisualizationMode.Homogeneous, cellGridDepth: 1, originalPositionMode: exports.OriginalPositionMode.Hidden, pointSize: 0.05, pointsVisible: true, pointVisualizationMode: exports.PointVisualizationMode.Original, cellsVisible: false, }; } _addSceneImages(cell, reference) { const cellId = cell.id; const images = cell.images; for (const image of images) { if (this._scene.hasImage(image.id, cellId)) { continue; } this._scene.addImage(image, this._createTransform(image, reference), this._computeOriginalPosition(image, reference), cellId); } } _cellsInFov(image, bearing, fov) { const spatial = this._spatial; const geometry = this._navigator.api.data.geometry; const cell = geometry.lngLatToCellId(image.originalLngLat); const cells = [cell]; const threshold = fov / 2; const adjacent = geometry.getAdjacent(cell); for (const a of adjacent) { const vertices = geometry.getVertices(a); for (const vertex of vertices) { const [x, y] = geodeticToEnu(vertex.lng, vertex.lat, 0, image.lngLat.lng, image.lngLat.lat, 0); const azimuthal = Math.atan2(y, x); const vertexBearing = spatial.radToDeg(spatial.azimuthalToBearing(azimuthal)); if (Math.abs(vertexBearing - bearing) < threshold) { cells.push(a); } } } return cells; } _computeOriginalPosition(image, reference) { return geodeticToEnu(image.originalLngLat.lng, image.originalLngLat.lat, image.originalAltitude != null ? image.originalAltitude : image.computedAltitude, reference.lng, reference.lat, reference.alt); } _cellToTopocentric(cellId, reference) { const vertices = this._navigator.api.data.geometry .getVertices(cellId) .map((vertex) => { return geodeticToEnu(vertex.lng, vertex.lat, -2, reference.lng, reference.lat, reference.alt); }); return vertices; } _computeTranslation(reconstruction, reference) { return geodeticToEnu(reconstruction.reference.lng, reconstruction.reference.lat, reconstruction.reference.alt, reference.lng, reference.lat, reference.alt); } _createTransform(image, reference) { const translation = computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); const transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, undefined, image.assetsCached ? image.camera : this._navigator.projectionService.makeCamera(image.cameraType, image.cameraParameters)); return transform; } } SpatialComponent.componentName = "spatial"; /** * @class Geometry * @abstract * @classdesc Represents a geometry. */ class Geometry { /** * Create a geometry. * * @constructor * @ignore */ constructor() { this._notifyChanged$ = new Subject(); } /** * Get changed observable. * * @description Emits the geometry itself every time the geometry * has changed. * * @returns {Observable} Observable emitting the geometry instance. * @ignore */ get changed$() { return this._notifyChanged$; } } class GeometryTagError extends MapillaryError { constructor(message) { super(message != null ? message : "The provided geometry value is incorrect"); Object.setPrototypeOf(this, GeometryTagError.prototype); this.name = "GeometryTagError"; } } /** * @class PointsGeometry * * @classdesc Represents a point set in the 2D basic image coordinate system. * * @example * ```js * var points = [[0.5, 0.3], [0.7, 0.3], [0.6, 0.5]]; * var pointsGeometry = new PointsGeometry(points); * ``` */ class PointsGeometry extends Geometry { /** * Create a points geometry. * * @constructor * @param {Array>} points - Array of 2D points on the basic coordinate * system. The number of points must be greater than or equal to two. * * @throws {GeometryTagError} Point coordinates must be valid basic coordinates. */ constructor(points) { super(); const pointsLength = points.length; if (pointsLength < 2) { throw new GeometryTagError("A points geometry must have two or more positions."); } this._points = []; for (const point of points) { if (point[0] < 0 || point[0] > 1 || point[1] < 0 || point[1] > 1) { throw new GeometryTagError("Basic coordinates of points must be on the interval [0, 1]."); } this._points.push(point.slice()); } } /** * Get points property. * @returns {Array>} Array of 2d points. */ get points() { return this._points; } /** * Add a point to the point set. * * @param {Array} point - Point to add. * @ignore */ addPoint2d(point) { const clamped = [ Math.max(0, Math.min(1, point[0])), Math.max(0, Math.min(1, point[1])), ]; this._points.push(clamped); this._notifyChanged$.next(this); } /** * Get the coordinates of a point from the point set representation of the geometry. * * @param {number} index - Point index. * @returns {Array} Array representing the 2D basic coordinates of the point. * @ignore */ getPoint2d(index) { return this._points[index].slice(); } /** * Remove a point from the point set. * * @param {number} index - The index of the point to remove. * @ignore */ removePoint2d(index) { if (index < 0 || index >= this._points.length || this._points.length < 3) { throw new GeometryTagError("Index for removed point must be valid."); } this._points.splice(index, 1); this._notifyChanged$.next(this); } /** @ignore */ setVertex2d(index, value, transform) { this.setPoint2d(index, value, transform); } /** @ignore */ setPoint2d(index, value, transform) { const changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; this._points[index] = changed; this._notifyChanged$.next(this); } /** @ignore */ getPoints3d(transform) { return this._getPoints3d(this._points, transform); } /** @ignore */ getPoint3d(index, transform) { return transform.unprojectBasic(this._points[index], 200); } /** @ignore */ getPoints2d() { return this._points.slice(); } /** @ignore */ getCentroid2d(transform) { if (!transform) { throw new GeometryTagError("Get centroid must be called with a transform for points geometries."); } const [minX, minY, maxX, maxY] = this.getRect2d(transform); const centroidX = minX < maxX ? (minX + maxX) / 2 : ((minX + maxX + 1) / 2) % 1; const centroidY = (minY + maxY) / 2; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { let centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** @ignore */ getRect2d(transform) { let minX = 1; let maxX = 0; let minY = 1; let maxY = 0; const points = this._points; for (const point of points) { if (point[0] < minX) { minX = point[0]; } if (point[0] > maxX) { maxX = point[0]; } if (point[1] < minY) { minY = point[1]; } if (point[1] > maxY) { maxY = point[1]; } } if (isSpherical(transform.cameraType)) { const indices = []; for (let i = 0; i < points.length; i++) { indices[i] = i; } indices.sort((a, b) => { return points[a][0] < points[b][0] ? -1 : points[a][0] > points[b][0] ? 1 : a < b ? -1 : 1; }); let maxDistanceX = points[indices[0]][0] + 1 - points[indices[indices.length - 1]][0]; let leftMostIndex = 0; for (let i = 0; i < indices.length - 1; i++) { const index1 = indices[i]; const index2 = indices[i + 1]; const distanceX = points[index2][0] - points[index1][0]; if (distanceX > maxDistanceX) { maxDistanceX = distanceX; leftMostIndex = i + 1; } } if (leftMostIndex > 0) { minX = points[indices[leftMostIndex]][0]; maxX = points[indices[leftMostIndex - 1]][0]; } } return [minX, minY, maxX, maxY]; } /** @ignore */ setCentroid2d(value, transform) { throw new Error("Not implemented"); } _getPoints3d(points2d, transform) { return points2d .map((point) => { return transform.unprojectBasic(point, 200); }); } } class CreateTag { constructor(geometry, transform, viewportCoords) { this._geometry = geometry; this._transform = transform; this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._aborted$ = new Subject(); this._created$ = new Subject(); this._glObjectsChanged$ = new Subject(); this._geometryChangedSubscription = this._geometry.changed$ .subscribe(() => { this._onGeometryChanged(); this._glObjectsChanged$.next(this); }); } get geometry() { return this._geometry; } get glObjects() { return this._glObjects; } get aborted$() { return this._aborted$; } get created$() { return this._created$; } get glObjectsChanged$() { return this._glObjectsChanged$; } get geometryChanged$() { return this._geometry.changed$.pipe(map(() => { return this; })); } dispose() { this._geometryChangedSubscription.unsubscribe(); } _canvasToTransform(canvas) { const canvasX = Math.round(canvas[0]); const canvasY = Math.round(canvas[1]); const transform = `translate(-50%,-50%) translate(${canvasX}px,${canvasY}px)`; return transform; } _colorToBackground(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _createOutine(polygon3d, color) { const positions = this._getLinePositions(polygon3d); const geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ color: color, linewidth: 1, }); return new Line(geometry, material); } _disposeLine(line) { if (line == null) { return; } line.geometry.dispose(); line.material.dispose(); } _getLinePositions(polygon3d) { const length = polygon3d.length; const positions = new Float32Array(length * 3); for (let i = 0; i < length; ++i) { const index = 3 * i; const position = polygon3d[i]; positions[index] = position[0]; positions[index + 1] = position[1]; positions[index + 2] = position[2]; } return positions; } } var earcut$2 = {exports: {}}; earcut$2.exports = earcut; earcut$2.exports.default = earcut; function earcut(data, holeIndices, dim) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, outerNode = linkedList(data, 0, outerLen, dim, true), triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (var i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); return triangles; } // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { var i, last; if (clockwise === (signedArea$1(data, start, end, dim) > 0)) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals$1(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; var p = start, again; do { again = false; if (!p.steiner && (equals$1(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); var stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim | 0); triangles.push(ear.i / dim | 0); triangles.push(next.i / dim | 0); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); var p = c.next; while (p !== a) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); // z-order range for the current triangle bbox; var minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); var p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { var p = start; do { var a = p.prev, b = p.next.next; if (!equals$1(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim | 0); triangles.push(p.i / dim | 0); triangles.push(b.i / dim | 0); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal var c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize, 0); earcutLinked(c, triangles, dim, minX, minY, invSize, 0); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { var queue = [], i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { outerNode = eliminateHole(queue[i], outerNode); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { var bridge = findHoleBridge(hole, outerNode); if (!bridge) { return outerNode; } var bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts filterPoints(bridgeReverse, bridgeReverse.next); return filterPoints(bridge, bridge.next); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { var p = outerNode, hx = hole.x, hy = hole.y, qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while (p !== outerNode); if (!m) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { var p = start; do { if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || (qSize > 0 && q)) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = (x - minX) * invSize | 0; y = (y - minY) * invSize | 0; x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; y = (y | (y << 8)) & 0x00FF00FF; y = (y | (y << 4)) & 0x0F0F0F0F; y = (y | (y << 2)) & 0x33333333; y = (y | (y << 1)) & 0x55555555; return x | (y << 1); } // find the leftmost node of a polygon ring function getLeftmost(start) { var p = start, leftmost = start; do { if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones"t intersect other edges (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals$1(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals$1(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { var o1 = sign(area(p1, q1, p2)); var o2 = sign(area(p1, q1, q2)); var o3 = sign(area(p2, q2, p1)); var o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { var p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { var p = a, inside = false, px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { var a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { var p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } // return a percentage difference between the polygon area and its triangulation area; // used to verify correctness of triangulation earcut.deviation = function (data, holeIndices, dim, triangles) { var hasHoles = holeIndices && holeIndices.length; var outerLen = hasHoles ? holeIndices[0] * dim : data.length; var polygonArea = Math.abs(signedArea$1(data, 0, outerLen, dim)); if (hasHoles) { for (var i = 0, len = holeIndices.length; i < len; i++) { var start = holeIndices[i] * dim; var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; polygonArea -= Math.abs(signedArea$1(data, start, end, dim)); } } var trianglesArea = 0; for (i = 0; i < triangles.length; i += 3) { var a = triangles[i] * dim; var b = triangles[i + 1] * dim; var c = triangles[i + 2] * dim; trianglesArea += Math.abs( (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1])); } return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea); }; function signedArea$1(data, start, end, dim) { var sum = 0; for (var i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts earcut.flatten = function (data) { var dim = data[0][0].length, result = {vertices: [], holes: [], dimensions: dim}, holeIndex = 0; for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j++) { for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); } if (i > 0) { holeIndex += data[i - 1].length; result.holes.push(holeIndex); } } return result; }; var earcutExports = earcut$2.exports; var earcut$1 = /*@__PURE__*/getDefaultExportFromCjs(earcutExports); var polylabel$2 = {exports: {}}; let TinyQueue$1 = class TinyQueue { constructor(data = [], compare = defaultCompare$1) { this.data = data; this.length = this.data.length; this.compare = compare; if (this.length > 0) { for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i); } } push(item) { this.data.push(item); this.length++; this._up(this.length - 1); } pop() { if (this.length === 0) return undefined; const top = this.data[0]; const bottom = this.data.pop(); this.length--; if (this.length > 0) { this.data[0] = bottom; this._down(0); } return top; } peek() { return this.data[0]; } _up(pos) { const {data, compare} = this; const item = data[pos]; while (pos > 0) { const parent = (pos - 1) >> 1; const current = data[parent]; if (compare(item, current) >= 0) break; data[pos] = current; pos = parent; } data[pos] = item; } _down(pos) { const {data, compare} = this; const halfLength = this.length >> 1; const item = data[pos]; while (pos < halfLength) { let left = (pos << 1) + 1; let best = data[left]; const right = left + 1; if (right < this.length && compare(data[right], best) < 0) { left = right; best = data[right]; } if (compare(best, item) >= 0) break; data[pos] = best; pos = left; } data[pos] = item; } }; function defaultCompare$1(a, b) { return a < b ? -1 : a > b ? 1 : 0; } var tinyqueue$1 = /*#__PURE__*/Object.freeze({ __proto__: null, default: TinyQueue$1 }); var require$$0 = /*@__PURE__*/getAugmentedNamespace(tinyqueue$1); var Queue$1 = require$$0; if (Queue$1.default) Queue$1 = Queue$1.default; // temporary webpack fix polylabel$2.exports = polylabel; polylabel$2.exports.default = polylabel; function polylabel(polygon, precision, debug) { precision = precision || 1.0; // find the bounding box of the outer ring var minX, minY, maxX, maxY; for (var i = 0; i < polygon[0].length; i++) { var p = polygon[0][i]; if (!i || p[0] < minX) minX = p[0]; if (!i || p[1] < minY) minY = p[1]; if (!i || p[0] > maxX) maxX = p[0]; if (!i || p[1] > maxY) maxY = p[1]; } var width = maxX - minX; var height = maxY - minY; var cellSize = Math.min(width, height); var h = cellSize / 2; if (cellSize === 0) { var degeneratePoleOfInaccessibility = [minX, minY]; degeneratePoleOfInaccessibility.distance = 0; return degeneratePoleOfInaccessibility; } // a priority queue of cells in order of their "potential" (max distance to polygon) var cellQueue = new Queue$1(undefined, compareMax); // cover polygon with initial cells for (var x = minX; x < maxX; x += cellSize) { for (var y = minY; y < maxY; y += cellSize) { cellQueue.push(new Cell(x + h, y + h, h, polygon)); } } // take centroid as the first best guess var bestCell = getCentroidCell(polygon); // special case for rectangular polygons var bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, polygon); if (bboxCell.d > bestCell.d) bestCell = bboxCell; var numProbes = cellQueue.length; while (cellQueue.length) { // pick the most promising cell from the queue var cell = cellQueue.pop(); // update the best cell if we found a better one if (cell.d > bestCell.d) { bestCell = cell; if (debug) console.log("found best %d after %d probes", Math.round(1e4 * cell.d) / 1e4, numProbes); } // do not drill down further if there"s no chance of a better solution if (cell.max - bestCell.d <= precision) continue; // split the cell into four cells h = cell.h / 2; cellQueue.push(new Cell(cell.x - h, cell.y - h, h, polygon)); cellQueue.push(new Cell(cell.x + h, cell.y - h, h, polygon)); cellQueue.push(new Cell(cell.x - h, cell.y + h, h, polygon)); cellQueue.push(new Cell(cell.x + h, cell.y + h, h, polygon)); numProbes += 4; } if (debug) { console.log("num probes: " + numProbes); console.log("best distance: " + bestCell.d); } var poleOfInaccessibility = [bestCell.x, bestCell.y]; poleOfInaccessibility.distance = bestCell.d; return poleOfInaccessibility; } function compareMax(a, b) { return b.max - a.max; } function Cell(x, y, h, polygon) { this.x = x; // cell center x this.y = y; // cell center y this.h = h; // half the cell size this.d = pointToPolygonDist(x, y, polygon); // distance from cell center to polygon this.max = this.d + this.h * Math.SQRT2; // max distance to polygon within a cell } // signed distance from point to polygon outline (negative if point is outside) function pointToPolygonDist(x, y, polygon) { var inside = false; var minDistSq = Infinity; for (var k = 0; k < polygon.length; k++) { var ring = polygon[k]; for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) { var a = ring[i]; var b = ring[j]; if ((a[1] > y !== b[1] > y) && (x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0])) inside = !inside; minDistSq = Math.min(minDistSq, getSegDistSq(x, y, a, b)); } } return minDistSq === 0 ? 0 : (inside ? 1 : -1) * Math.sqrt(minDistSq); } // get polygon centroid function getCentroidCell(polygon) { var area = 0; var x = 0; var y = 0; var points = polygon[0]; for (var i = 0, len = points.length, j = len - 1; i < len; j = i++) { var a = points[i]; var b = points[j]; var f = a[0] * b[1] - b[0] * a[1]; x += (a[0] + b[0]) * f; y += (a[1] + b[1]) * f; area += f * 3; } if (area === 0) return new Cell(points[0][0], points[0][1], 0, polygon); return new Cell(x / area, y / area, 0, polygon); } // get squared distance from a point to a segment function getSegDistSq(px, py, a, b) { var x = a[0]; var y = a[1]; var dx = b[0] - x; var dy = b[1] - y; if (dx !== 0 || dy !== 0) { var t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); if (t > 1) { x = b[0]; y = b[1]; } else if (t > 0) { x += dx * t; y += dy * t; } } dx = px - x; dy = py - y; return dx * dx + dy * dy; } var polylabelExports = polylabel$2.exports; var polylabel$1 = /*@__PURE__*/getDefaultExportFromCjs(polylabelExports); function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } class SplayTree { constructor(compare = DEFAULT_COMPARE, noDuplicates = false) { this._compare = compare; this._root = null; this._size = 0; this._noDuplicates = !!noDuplicates; } rotateLeft(x) { var y = x.right; if (y) { x.right = y.left; if (y.left) y.left.parent = x; y.parent = x.parent; } if (!x.parent) this._root = y; else if (x === x.parent.left) x.parent.left = y; else x.parent.right = y; if (y) y.left = x; x.parent = y; } rotateRight(x) { var y = x.left; if (y) { x.left = y.right; if (y.right) y.right.parent = x; y.parent = x.parent; } if (!x.parent) this._root = y; else if(x === x.parent.left) x.parent.left = y; else x.parent.right = y; if (y) y.right = x; x.parent = y; } _splay(x) { while (x.parent) { var p = x.parent; if (!p.parent) { if (p.left === x) this.rotateRight(p); else this.rotateLeft(p); } else if (p.left === x && p.parent.left === p) { this.rotateRight(p.parent); this.rotateRight(p); } else if (p.right === x && p.parent.right === p) { this.rotateLeft(p.parent); this.rotateLeft(p); } else if (p.left === x && p.parent.right === p) { this.rotateRight(p); this.rotateLeft(p); } else { this.rotateLeft(p); this.rotateRight(p); } } } splay(x) { var p, gp, ggp, l, r; while (x.parent) { p = x.parent; gp = p.parent; if (gp && gp.parent) { ggp = gp.parent; if (ggp.left === gp) ggp.left = x; else ggp.right = x; x.parent = ggp; } else { x.parent = null; this._root = x; } l = x.left; r = x.right; if (x === p.left) { // left if (gp) { if (gp.left === p) { /* zig-zig */ if (p.right) { gp.left = p.right; gp.left.parent = gp; } else gp.left = null; p.right = gp; gp.parent = p; } else { /* zig-zag */ if (l) { gp.right = l; l.parent = gp; } else gp.right = null; x.left = gp; gp.parent = x; } } if (r) { p.left = r; r.parent = p; } else p.left = null; x.right = p; p.parent = x; } else { // right if (gp) { if (gp.right === p) { /* zig-zig */ if (p.left) { gp.right = p.left; gp.right.parent = gp; } else gp.right = null; p.left = gp; gp.parent = p; } else { /* zig-zag */ if (r) { gp.left = r; r.parent = gp; } else gp.left = null; x.right = gp; gp.parent = x; } } if (l) { p.right = l; l.parent = p; } else p.right = null; x.left = p; p.parent = x; } } } replace(u, v) { if (!u.parent) this._root = v; else if (u === u.parent.left) u.parent.left = v; else u.parent.right = v; if (v) v.parent = u.parent; } minNode(u = this._root) { if (u) while (u.left) u = u.left; return u; } maxNode(u = this._root) { if (u) while (u.right) u = u.right; return u; } insert(key, data) { var z = this._root; var p = null; var comp = this._compare; var cmp; if (this._noDuplicates) { while (z) { p = z; cmp = comp(z.key, key); if (cmp === 0) return; else if (comp(z.key, key) < 0) z = z.right; else z = z.left; } } else { while (z) { p = z; if (comp(z.key, key) < 0) z = z.right; else z = z.left; } } z = { key, data, left: null, right: null, parent: p }; if (!p) this._root = z; else if (comp(p.key, z.key) < 0) p.right = z; else p.left = z; this.splay(z); this._size++; return z; } find (key) { var z = this._root; var comp = this._compare; while (z) { var cmp = comp(z.key, key); if (cmp < 0) z = z.right; else if (cmp > 0) z = z.left; else return z; } return null; } /** * Whether the tree contains a node with the given key * @param {Key} key * @return {boolean} true/false */ contains (key) { var node = this._root; var comparator = this._compare; while (node) { var cmp = comparator(key, node.key); if (cmp === 0) return true; else if (cmp < 0) node = node.left; else node = node.right; } return false; } remove (key) { var z = this.find(key); if (!z) return false; this.splay(z); if (!z.left) this.replace(z, z.right); else if (!z.right) this.replace(z, z.left); else { var y = this.minNode(z.right); if (y.parent !== z) { this.replace(y, y.right); y.right = z.right; y.right.parent = y; } this.replace(z, y); y.left = z.left; y.left.parent = y; } this._size--; return true; } removeNode(z) { if (!z) return false; this.splay(z); if (!z.left) this.replace(z, z.right); else if (!z.right) this.replace(z, z.left); else { var y = this.minNode(z.right); if (y.parent !== z) { this.replace(y, y.right); y.right = z.right; y.right.parent = y; } this.replace(z, y); y.left = z.left; y.left.parent = y; } this._size--; return true; } erase (key) { var z = this.find(key); if (!z) return; this.splay(z); var s = z.left; var t = z.right; var sMax = null; if (s) { s.parent = null; sMax = this.maxNode(s); this.splay(sMax); this._root = sMax; } if (t) { if (s) sMax.right = t; else this._root = t; t.parent = sMax; } this._size--; } /** * Removes and returns the node with smallest key * @return {?Node} */ pop () { var node = this._root, returnValue = null; if (node) { while (node.left) node = node.left; returnValue = { key: node.key, data: node.data }; this.remove(node.key); } return returnValue; } /* eslint-disable class-methods-use-this */ /** * Successor node * @param {Node} node * @return {?Node} */ next (node) { var successor = node; if (successor) { if (successor.right) { successor = successor.right; while (successor && successor.left) successor = successor.left; } else { successor = node.parent; while (successor && successor.right === node) { node = successor; successor = successor.parent; } } } return successor; } /** * Predecessor node * @param {Node} node * @return {?Node} */ prev (node) { var predecessor = node; if (predecessor) { if (predecessor.left) { predecessor = predecessor.left; while (predecessor && predecessor.right) predecessor = predecessor.right; } else { predecessor = node.parent; while (predecessor && predecessor.left === node) { node = predecessor; predecessor = predecessor.parent; } } } return predecessor; } /* eslint-enable class-methods-use-this */ /** * @param {forEachCallback} callback * @return {SplayTree} */ forEach(callback) { var current = this._root; var s = [], done = false, i = 0; while (!done) { // Reach the left most Node of the current Node if (current) { // Place pointer to a tree node on the stack // before traversing the node"s left subtree s.push(current); current = current.left; } else { // BackTrack from the empty subtree and visit the Node // at the top of the stack; however, if the stack is // empty you are done if (s.length > 0) { current = s.pop(); callback(current, i++); // We have visited the node and its left // subtree. Now, it"s right subtree"s turn current = current.right; } else done = true; } } return this; } /** * Walk key range from `low` to `high`. Stops if `fn` returns a value. * @param {Key} low * @param {Key} high * @param {Function} fn * @param {*?} ctx * @return {SplayTree} */ range(low, high, fn, ctx) { const Q = []; const compare = this._compare; let node = this._root, cmp; while (Q.length !== 0 || node) { if (node) { Q.push(node); node = node.left; } else { node = Q.pop(); cmp = compare(node.key, high); if (cmp > 0) { break; } else if (compare(node.key, low) >= 0) { if (fn.call(ctx, node)) return this; // stop if smth is returned } node = node.right; } } return this; } /** * Returns all keys in order * @return {Array} */ keys () { var current = this._root; var s = [], r = [], done = false; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); r.push(current.key); current = current.right; } else done = true; } } return r; } /** * Returns `data` fields of all nodes in order. * @return {Array} */ values () { var current = this._root; var s = [], r = [], done = false; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); r.push(current.data); current = current.right; } else done = true; } } return r; } /** * Returns node at given index * @param {number} index * @return {?Node} */ at (index) { // removed after a consideration, more misleading than useful // index = index % this.size; // if (index < 0) index = this.size - index; var current = this._root; var s = [], done = false, i = 0; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); if (i === index) return current; i++; current = current.right; } else done = true; } } return null; } /** * Bulk-load items. Both array have to be same size * @param {Array} keys * @param {Array} [values] * @param {Boolean} [presort=false] Pre-sort keys and values, using * tree"s comparator. Sorting is done * in-place * @return {AVLTree} */ load(keys = [], values = [], presort = false) { if (this._size !== 0) throw new Error("bulk-load: tree is not empty"); const size = keys.length; if (presort) sort(keys, values, 0, size - 1, this._compare); this._root = loadRecursive(null, keys, values, 0, size); this._size = size; return this; } min() { var node = this.minNode(this._root); if (node) return node.key; else return null; } max() { var node = this.maxNode(this._root); if (node) return node.key; else return null; } isEmpty() { return this._root === null; } get size() { return this._size; } /** * Create a tree and load it with items * @param {Array} keys * @param {Array?} [values] * @param {Function?} [comparator] * @param {Boolean?} [presort=false] Pre-sort keys and values, using * tree"s comparator. Sorting is done * in-place * @param {Boolean?} [noDuplicates=false] Allow duplicates * @return {SplayTree} */ static createTree(keys, values, comparator, presort, noDuplicates) { return new SplayTree(comparator, noDuplicates).load(keys, values, presort); } } function loadRecursive (parent, keys, values, start, end) { const size = end - start; if (size > 0) { const middle = start + Math.floor(size / 2); const key = keys[middle]; const data = values[middle]; const node = { key, data, parent }; node.left = loadRecursive(node, keys, values, start, middle); node.right = loadRecursive(node, keys, values, middle + 1, end); return node; } return null; } function sort(keys, values, left, right, compare) { if (left >= right) return; const pivot = keys[(left + right) >> 1]; let i = left - 1; let j = right + 1; while (true) { do i++; while (compare(keys[i], pivot) < 0); do j--; while (compare(keys[j], pivot) > 0); if (i >= j) break; let tmp = keys[i]; keys[i] = keys[j]; keys[j] = tmp; tmp = values[i]; values[i] = values[j]; values[j] = tmp; } sort(keys, values, left, j, compare); sort(keys, values, j + 1, right, compare); } const NORMAL = 0; const NON_CONTRIBUTING = 1; const SAME_TRANSITION = 2; const DIFFERENT_TRANSITION = 3; const INTERSECTION = 0; const UNION = 1; const DIFFERENCE = 2; const XOR = 3; /** * @param {SweepEvent} event * @param {SweepEvent} prev * @param {Operation} operation */ function computeFields (event, prev, operation) { // compute inOut and otherInOut fields if (prev === null) { event.inOut = false; event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon } else { if (event.isSubject === prev.isSubject) { event.inOut = !prev.inOut; event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon } else { event.inOut = !prev.otherInOut; event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut; } // compute prevInResult field if (prev) { event.prevInResult = (!inResult(prev, operation) || prev.isVertical()) ? prev.prevInResult : prev; } } // check if the line segment belongs to the Boolean operation let isInResult = inResult(event, operation); if (isInResult) { event.resultTransition = determineResultTransition(event, operation); } else { event.resultTransition = 0; } } /* eslint-disable indent */ function inResult(event, operation) { switch (event.type) { case NORMAL: switch (operation) { case INTERSECTION: return !event.otherInOut; case UNION: return event.otherInOut; case DIFFERENCE: // return (event.isSubject && !event.otherInOut) || // (!event.isSubject && event.otherInOut); return (event.isSubject && event.otherInOut) || (!event.isSubject && !event.otherInOut); case XOR: return true; } break; case SAME_TRANSITION: return operation === INTERSECTION || operation === UNION; case DIFFERENT_TRANSITION: return operation === DIFFERENCE; case NON_CONTRIBUTING: return false; } return false; } /* eslint-enable indent */ function determineResultTransition(event, operation) { let thisIn = !event.inOut; let thatIn = !event.otherInOut; let isIn; switch (operation) { case INTERSECTION: isIn = thisIn && thatIn; break; case UNION: isIn = thisIn || thatIn; break; case XOR: isIn = thisIn ^ thatIn; break; case DIFFERENCE: if (event.isSubject) { isIn = thisIn && !thatIn; } else { isIn = thatIn && !thisIn; } break; } return isIn ? +1 : -1; } class SweepEvent { /** * Sweepline event * * @class {SweepEvent} * @param {Array.} point * @param {Boolean} left * @param {SweepEvent=} otherEvent * @param {Boolean} isSubject * @param {Number} edgeType */ constructor (point, left, otherEvent, isSubject, edgeType) { /** * Is left endpoint? * @type {Boolean} */ this.left = left; /** * @type {Array.} */ this.point = point; /** * Other edge reference * @type {SweepEvent} */ this.otherEvent = otherEvent; /** * Belongs to source or clipping polygon * @type {Boolean} */ this.isSubject = isSubject; /** * Edge contribution type * @type {Number} */ this.type = edgeType || NORMAL; /** * In-out transition for the sweepline crossing polygon * @type {Boolean} */ this.inOut = false; /** * @type {Boolean} */ this.otherInOut = false; /** * Previous event in result? * @type {SweepEvent} */ this.prevInResult = null; /** * Type of result transition (0 = not in result, +1 = out-in, -1, in-out) * @type {Number} */ this.resultTransition = 0; // connection step /** * @type {Number} */ this.otherPos = -1; /** * @type {Number} */ this.outputContourId = -1; this.isExteriorRing = true; // TODO: Looks unused, remove? } /** * @param {Array.} p * @return {Boolean} */ isBelow (p) { const p0 = this.point, p1 = this.otherEvent.point; return this.left ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0 // signedArea(this.point, this.otherEvent.point, p) > 0 : : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0; //signedArea(this.otherEvent.point, this.point, p) > 0; } /** * @param {Array.} p * @return {Boolean} */ isAbove (p) { return !this.isBelow(p); } /** * @return {Boolean} */ isVertical () { return this.point[0] === this.otherEvent.point[0]; } /** * Does event belong to result? * @return {Boolean} */ get inResult() { return this.resultTransition !== 0; } clone () { const copy = new SweepEvent( this.point, this.left, this.otherEvent, this.isSubject, this.type); copy.contourId = this.contourId; copy.resultTransition = this.resultTransition; copy.prevInResult = this.prevInResult; copy.isExteriorRing = this.isExteriorRing; copy.inOut = this.inOut; copy.otherInOut = this.otherInOut; return copy; } } function equals(p1, p2) { if (p1[0] === p2[0]) { if (p1[1] === p2[1]) { return true; } else { return false; } } return false; } // const EPSILON = 1e-9; // const abs = Math.abs; // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164 // Precision problem. // // module.exports = function equals(p1, p2) { // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON; // }; const epsilon = 1.1102230246251565e-16; const splitter = 134217729; const resulterrbound = (3 + 8 * epsilon) * epsilon; // fast_expansion_sum_zeroelim routine from oritinal code function sum(elen, e, flen, f, h) { let Q, Qnew, hh, bvirt; let enow = e[0]; let fnow = f[0]; let eindex = 0; let findex = 0; if ((fnow > enow) === (fnow > -enow)) { Q = enow; enow = e[++eindex]; } else { Q = fnow; fnow = f[++findex]; } let hindex = 0; if (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = enow + Q; hh = Q - (Qnew - enow); enow = e[++eindex]; } else { Qnew = fnow + Q; hh = Q - (Qnew - fnow); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } while (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; } else { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } } while (eindex < elen) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } while (findex < flen) { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } if (Q !== 0 || hindex === 0) { h[hindex++] = Q; } return hindex; } function estimate(elen, e) { let Q = e[0]; for (let i = 1; i < elen; i++) Q += e[i]; return Q; } function vec(n) { return new Float64Array(n); } const ccwerrboundA = (3 + 16 * epsilon) * epsilon; const ccwerrboundB = (2 + 12 * epsilon) * epsilon; const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon; const B = vec(4); const C1 = vec(8); const C2 = vec(12); const D = vec(16); const u = vec(4); function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) { let acxtail, acytail, bcxtail, bcytail; let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3; const acx = ax - cx; const bcx = bx - cx; const acy = ay - cy; const bcy = by - cy; s1 = acx * bcy; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcx; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; B[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; B[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; B[2] = _j - (u3 - bvirt) + (_i - bvirt); B[3] = u3; let det = estimate(4, B); let errbound = ccwerrboundB * detsum; if (det >= errbound || -det >= errbound) { return det; } bvirt = ax - acx; acxtail = ax - (acx + bvirt) + (bvirt - cx); bvirt = bx - bcx; bcxtail = bx - (bcx + bvirt) + (bvirt - cx); bvirt = ay - acy; acytail = ay - (acy + bvirt) + (bvirt - cy); bvirt = by - bcy; bcytail = by - (bcy + bvirt) + (bvirt - cy); if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) { return det; } errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det); det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); if (det >= errbound || -det >= errbound) return det; s1 = acxtail * bcy; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcx; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C1len = sum(4, B, 4, u, C1); s1 = acx * bcytail; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcxtail; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C2len = sum(C1len, C1, 4, u, C2); s1 = acxtail * bcytail; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcxtail; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const Dlen = sum(C2len, C2, 4, u, D); return D[Dlen - 1]; } function orient2d(ax, ay, bx, by, cx, cy) { const detleft = (ay - cy) * (bx - cx); const detright = (ax - cx) * (by - cy); const det = detleft - detright; if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det; const detsum = Math.abs(detleft + detright); if (Math.abs(det) >= ccwerrboundA * detsum) return det; return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum); } /** * Signed area of the triangle (p0, p1, p2) * @param {Array.} p0 * @param {Array.} p1 * @param {Array.} p2 * @return {Number} */ function signedArea(p0, p1, p2) { const res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]); if (res > 0) return -1; if (res < 0) return 1; return 0; } /** * @param {SweepEvent} e1 * @param {SweepEvent} e2 * @return {Number} */ function compareEvents(e1, e2) { const p1 = e1.point; const p2 = e2.point; // Different x-coordinate if (p1[0] > p2[0]) return 1; if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate // Event with lower y-coordinate is processed first if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1; return specialCases(e1, e2, p1); } /* eslint-disable no-unused-vars */ function specialCases(e1, e2, p1, p2) { // Same coordinates, but one is a left endpoint and the other is // a right endpoint. The right endpoint is processed first if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point; // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) // Same coordinates, both events // are left endpoints or right endpoints. // not collinear if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) { // the event associate to the bottom segment is processed first return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1; } return (!e1.isSubject && e2.isSubject) ? 1 : -1; } /* eslint-enable no-unused-vars */ /** * @param {SweepEvent} se * @param {Array.} p * @param {Queue} queue * @return {Queue} */ function divideSegment(se, p, queue) { const r = new SweepEvent(p, false, se, se.isSubject); const l = new SweepEvent(p, true, se.otherEvent, se.isSubject); /* eslint-disable no-console */ if (equals(se.point, se.otherEvent.point)) { console.warn("what is that, a collapsed segment?", se); } /* eslint-enable no-console */ r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event if (compareEvents(l, se.otherEvent) > 0) { se.otherEvent.left = true; l.left = false; } // avoid a rounding error. The left event would be processed after the right event // if (compareEvents(se, r) > 0) {} se.otherEvent.otherEvent = l; se.otherEvent = r; queue.push(l); queue.push(r); return queue; } //const EPS = 1e-9; /** * Finds the magnitude of the cross product of two vectors (if we pretend * they"re in three dimensions) * * @param {Object} a First vector * @param {Object} b Second vector * @private * @returns {Number} The magnitude of the cross product */ function crossProduct(a, b) { return (a[0] * b[1]) - (a[1] * b[0]); } /** * Finds the dot product of two vectors. * * @param {Object} a First vector * @param {Object} b Second vector * @private * @returns {Number} The dot product */ function dotProduct(a, b) { return (a[0] * b[0]) + (a[1] * b[1]); } /** * Finds the intersection (if any) between two line segments a and b, given the * line segments" end points a1, a2 and b1, b2. * * This algorithm is based on Schneider and Eberly. * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf * Page 244. * * @param {Array.} a1 point of first line * @param {Array.} a2 point of first line * @param {Array.} b1 point of second line * @param {Array.} b2 point of second line * @param {Boolean=} noEndpointTouch whether to skip single touchpoints * (meaning connected segments) as * intersections * @returns {Array.>|Null} If the lines intersect, the point of * intersection. If they overlap, the two end points of the overlapping segment. * Otherwise, null. */ function intersection$1 (a1, a2, b1, b2, noEndpointTouch) { // The algorithm expects our lines in the form P + sd, where P is a point, // s is on the interval [0, 1], and d is a vector. // We are passed two points. P can be the first point of each pair. The // vector, then, could be thought of as the distance (in x and y components) // from the first point to the second point. // So first, let"s make our vectors: const va = [a2[0] - a1[0], a2[1] - a1[1]]; const vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form: /* eslint-disable arrow-body-style */ function toPoint(p, s, d) { return [ p[0] + s * d[0], p[1] + s * d[1] ]; } /* eslint-enable arrow-body-style */ // The rest is pretty much a straight port of the algorithm. const e = [b1[0] - a1[0], b1[1] - a1[1]]; let kross = crossProduct(va, vb); let sqrKross = kross * kross; const sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb); // Check for line intersection. This works because of the properties of the // cross product -- specifically, two vectors are parallel if and only if the // cross product is the 0 vector. The full calculation involves relative error // to account for possible very small line segments. See Schneider & Eberly // for details. if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) { // If they"re not parallel, then (because these are line segments) they // still might not actually intersect. This code checks that the // intersection point of the lines is actually on both line segments. const s = crossProduct(e, vb) / kross; if (s < 0 || s > 1) { // not on line segment a return null; } const t = crossProduct(e, va) / kross; if (t < 0 || t > 1) { // not on line segment b return null; } if (s === 0 || s === 1) { // on an endpoint of line segment a return noEndpointTouch ? null : [toPoint(a1, s, va)]; } if (t === 0 || t === 1) { // on an endpoint of line segment b return noEndpointTouch ? null : [toPoint(b1, t, vb)]; } return [toPoint(a1, s, va)]; } // If we"ve reached this point, then the lines are either parallel or the // same, but the segments could overlap partially or fully, or not at all. // So we need to find the overlap, if any. To do that, we can use e, which is // the (vector) difference between the two initial points. If this is parallel // with the line itself, then the two lines are the same line, and there will // be overlap. //const sqrLenE = dotProduct(e, e); kross = crossProduct(e, va); sqrKross = kross * kross; if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) { // Lines are just parallel, not the same. No overlap. return null; } const sa = dotProduct(va, e) / sqrLenA; const sb = sa + dotProduct(va, vb) / sqrLenA; const smin = Math.min(sa, sb); const smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from // Schneider & Eberly, just inlined into this function. if (smin <= 1 && smax >= 0) { // overlap on an end point if (smin === 1) { return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)]; } if (smax === 0) { return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)]; } if (noEndpointTouch && smin === 0 && smax === 1) return null; // There"s overlap on a segment -- two points of intersection. Return both. return [ toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va) ]; } return null; } /** * @param {SweepEvent} se1 * @param {SweepEvent} se2 * @param {Queue} queue * @return {Number} */ function possibleIntersection (se1, se2, queue) { // that disallows self-intersecting polygons, // did cost us half a day, so I"ll leave it // out of respect // if (se1.isSubject === se2.isSubject) return; const inter = intersection$1( se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point ); const nintersections = inter ? inter.length : 0; if (nintersections === 0) return 0; // no intersection // the line segments intersect at an endpoint of both line segments if ((nintersections === 1) && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) { return 0; } if (nintersections === 2 && se1.isSubject === se2.isSubject) { // if(se1.contourId === se2.contourId){ // console.warn("Edges of the same polygon overlap", // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); // } //throw new Error("Edges of the same polygon overlap"); return 0; } // The line segments associated to se1 and se2 intersect if (nintersections === 1) { // if the intersection point is not an endpoint of se1 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) { divideSegment(se1, inter[0], queue); } // if the intersection point is not an endpoint of se2 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) { divideSegment(se2, inter[0], queue); } return 1; } // The line segments associated to se1 and se2 overlap const events = []; let leftCoincide = false; let rightCoincide = false; if (equals(se1.point, se2.point)) { leftCoincide = true; // linked } else if (compareEvents(se1, se2) === 1) { events.push(se2, se1); } else { events.push(se1, se2); } if (equals(se1.otherEvent.point, se2.otherEvent.point)) { rightCoincide = true; } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) { events.push(se2.otherEvent, se1.otherEvent); } else { events.push(se1.otherEvent, se2.otherEvent); } if ((leftCoincide && rightCoincide) || leftCoincide) { // both line segments are equal or share the left endpoint se2.type = NON_CONTRIBUTING; se1.type = (se2.inOut === se1.inOut) ? SAME_TRANSITION : DIFFERENT_TRANSITION; if (leftCoincide && !rightCoincide) { // honestly no idea, but changing events selection from [2, 1] // to [0, 1] fixes the overlapping self-intersecting polygons issue divideSegment(events[1].otherEvent, events[0].point, queue); } return 2; } // the line segments share the right endpoint if (rightCoincide) { divideSegment(events[0], events[1].point, queue); return 3; } // no line segment includes totally the other one if (events[0] !== events[3].otherEvent) { divideSegment(events[0], events[1].point, queue); divideSegment(events[1], events[2].point, queue); return 3; } // one line segment includes the other one divideSegment(events[0], events[1].point, queue); divideSegment(events[3].otherEvent, events[2].point, queue); return 3; } /** * @param {SweepEvent} le1 * @param {SweepEvent} le2 * @return {Number} */ function compareSegments(le1, le2) { if (le1 === le2) return 0; // Segments are not collinear if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) { // If they share their left endpoint use the right endpoint to sort if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1; // has the line segment associated to e1 been inserted // into S after the line segment associated to e2 ? if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted // into S after the line segment associated to e1 return le1.isBelow(le2.point) ? -1 : 1; } if (le1.isSubject === le2.isSubject) { // same polygon let p1 = le1.point, p2 = le2.point; if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) { p1 = le1.otherEvent.point; p2 = le2.otherEvent.point; if (p1[0] === p2[0] && p1[1] === p2[1]) return 0; else return le1.contourId > le2.contourId ? 1 : -1; } } else { // Segments are collinear, but belong to separate polygons return le1.isSubject ? -1 : 1; } return compareEvents(le1, le2) === 1 ? 1 : -1; } function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) { const sweepLine = new SplayTree(compareSegments); const sortedEvents = []; const rightbound = Math.min(sbbox[2], cbbox[2]); let prev, next, begin; while (eventQueue.length !== 0) { let event = eventQueue.pop(); sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here if ((operation === INTERSECTION && event.point[0] > rightbound) || (operation === DIFFERENCE && event.point[0] > sbbox[2])) { break; } if (event.left) { next = prev = sweepLine.insert(event); begin = sweepLine.minNode(); if (prev !== begin) prev = sweepLine.prev(prev); else prev = null; next = sweepLine.next(next); const prevEvent = prev ? prev.key : null; let prevprevEvent; computeFields(event, prevEvent, operation); if (next) { if (possibleIntersection(event, next.key, eventQueue) === 2) { computeFields(event, prevEvent, operation); computeFields(next.key, event, operation); } } if (prev) { if (possibleIntersection(prev.key, event, eventQueue) === 2) { let prevprev = prev; if (prevprev !== begin) prevprev = sweepLine.prev(prevprev); else prevprev = null; prevprevEvent = prevprev ? prevprev.key : null; computeFields(prevEvent, prevprevEvent, operation); computeFields(event, prevEvent, operation); } } } else { event = event.otherEvent; next = prev = sweepLine.find(event); if (prev && next) { if (prev !== begin) prev = sweepLine.prev(prev); else prev = null; next = sweepLine.next(next); sweepLine.remove(event); if (next && prev) { possibleIntersection(prev.key, next.key, eventQueue); } } } } return sortedEvents; } class Contour { /** * Contour * * @class {Contour} */ constructor() { this.points = []; this.holeIds = []; this.holeOf = null; this.depth = null; } isExterior() { return this.holeOf == null; } } /** * @param {Array.} sortedEvents * @return {Array.} */ function orderEvents(sortedEvents) { let event, i, len, tmp; const resultEvents = []; for (i = 0, len = sortedEvents.length; i < len; i++) { event = sortedEvents[i]; if ((event.left && event.inResult) || (!event.left && event.otherEvent.inResult)) { resultEvents.push(event); } } // Due to overlapping edges the resultEvents array can be not wholly sorted let sorted = false; while (!sorted) { sorted = true; for (i = 0, len = resultEvents.length; i < len; i++) { if ((i + 1) < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) { tmp = resultEvents[i]; resultEvents[i] = resultEvents[i + 1]; resultEvents[i + 1] = tmp; sorted = false; } } } for (i = 0, len = resultEvents.length; i < len; i++) { event = resultEvents[i]; event.otherPos = i; } // imagine, the right event is found in the beginning of the queue, // when his left counterpart is not marked yet for (i = 0, len = resultEvents.length; i < len; i++) { event = resultEvents[i]; if (!event.left) { tmp = event.otherPos; event.otherPos = event.otherEvent.otherPos; event.otherEvent.otherPos = tmp; } } return resultEvents; } /** * @param {Number} pos * @param {Array.} resultEvents * @param {Object>} processed * @return {Number} */ function nextPos(pos, resultEvents, processed, origPos) { let newPos = pos + 1, p = resultEvents[pos].point, p1; const length = resultEvents.length; if (newPos < length) p1 = resultEvents[newPos].point; while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) { if (!processed[newPos]) { return newPos; } else { newPos++; } if (newPos < length) { p1 = resultEvents[newPos].point; } } newPos = pos - 1; while (processed[newPos] && newPos > origPos) { newPos--; } return newPos; } function initializeContourFromContext(event, contours, contourId) { const contour = new Contour(); if (event.prevInResult != null) { const prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id, // because we must have already processed it (i.e., assigned an output contour id) // in an earlier iteration, otherwise it wouldn"t be possible that it is "previous in // result". const lowerContourId = prevInResult.outputContourId; const lowerResultTransition = prevInResult.resultTransition; if (lowerResultTransition > 0) { // We are inside. Now we have to check if the thing below us is another hole or // an exterior contour. const lowerContour = contours[lowerContourId]; if (lowerContour.holeOf != null) { // The lower contour is a hole => Connect the new contour as a hole to its parent, // and use same depth. const parentContourId = lowerContour.holeOf; contours[parentContourId].holeIds.push(contourId); contour.holeOf = parentContourId; contour.depth = contours[lowerContourId].depth; } else { // The lower contour is an exterior contour => Connect the new contour as a hole, // and increment depth. contours[lowerContourId].holeIds.push(contourId); contour.holeOf = lowerContourId; contour.depth = contours[lowerContourId].depth + 1; } } else { // We are outside => this contour is an exterior contour of same depth. contour.holeOf = null; contour.depth = contours[lowerContourId].depth; } } else { // There is no lower/previous contour => this contour is an exterior contour of depth 0. contour.holeOf = null; contour.depth = 0; } return contour; } /** * @param {Array.} sortedEvents * @return {Array.<*>} polygons */ function connectEdges(sortedEvents) { let i, len; const resultEvents = orderEvents(sortedEvents); // "false"-filled array const processed = {}; const contours = []; for (i = 0, len = resultEvents.length; i < len; i++) { if (processed[i]) { continue; } const contourId = contours.length; const contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID const markAsProcessed = (pos) => { processed[pos] = true; if (pos < resultEvents.length && resultEvents[pos]) { resultEvents[pos].outputContourId = contourId; } }; let pos = i; let origPos = i; const initial = resultEvents[i].point; contour.points.push(initial); /* eslint no-constant-condition: "off" */ while (true) { markAsProcessed(pos); pos = resultEvents[pos].otherPos; markAsProcessed(pos); contour.points.push(resultEvents[pos].point); pos = nextPos(pos, resultEvents, processed, origPos); if (pos == origPos || pos >= resultEvents.length || !resultEvents[pos]) { break; } } contours.push(contour); } return contours; } var tinyqueue = {exports: {}}; tinyqueue.exports = TinyQueue; tinyqueue.exports.default = TinyQueue; function TinyQueue(data, compare) { if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare); this.data = data || []; this.length = this.data.length; this.compare = compare || defaultCompare; if (this.length > 0) { for (var i = (this.length >> 1) - 1; i >= 0; i--) this._down(i); } } function defaultCompare(a, b) { return a < b ? -1 : a > b ? 1 : 0; } TinyQueue.prototype = { push: function (item) { this.data.push(item); this.length++; this._up(this.length - 1); }, pop: function () { if (this.length === 0) return undefined; var top = this.data[0]; this.length--; if (this.length > 0) { this.data[0] = this.data[this.length]; this._down(0); } this.data.pop(); return top; }, peek: function () { return this.data[0]; }, _up: function (pos) { var data = this.data; var compare = this.compare; var item = data[pos]; while (pos > 0) { var parent = (pos - 1) >> 1; var current = data[parent]; if (compare(item, current) >= 0) break; data[pos] = current; pos = parent; } data[pos] = item; }, _down: function (pos) { var data = this.data; var compare = this.compare; var halfLength = this.length >> 1; var item = data[pos]; while (pos < halfLength) { var left = (pos << 1) + 1; var right = left + 1; var best = data[left]; if (right < this.length && compare(data[right], best) < 0) { left = right; best = data[right]; } if (compare(best, item) >= 0) break; data[pos] = best; pos = left; } data[pos] = item; } }; var tinyqueueExports = tinyqueue.exports; var Queue = /*@__PURE__*/getDefaultExportFromCjs(tinyqueueExports); const max = Math.max; const min = Math.min; let contourId = 0; function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) { let i, len, s1, s2, e1, e2; for (i = 0, len = contourOrHole.length - 1; i < len; i++) { s1 = contourOrHole[i]; s2 = contourOrHole[i + 1]; e1 = new SweepEvent(s1, false, undefined, isSubject); e2 = new SweepEvent(s2, false, e1, isSubject); e1.otherEvent = e2; if (s1[0] === s2[0] && s1[1] === s2[1]) { continue; // skip collapsed edges, or it breaks } e1.contourId = e2.contourId = depth; if (!isExteriorRing) { e1.isExteriorRing = false; e2.isExteriorRing = false; } if (compareEvents(e1, e2) > 0) { e2.left = true; } else { e1.left = true; } const x = s1[0], y = s1[1]; bbox[0] = min(bbox[0], x); bbox[1] = min(bbox[1], y); bbox[2] = max(bbox[2], x); bbox[3] = max(bbox[3], y); // Pushing it so the queue is sorted from left to right, // with object on the left having the highest priority. Q.push(e1); Q.push(e2); } } function fillQueue(subject, clipping, sbbox, cbbox, operation) { const eventQueue = new Queue(null, compareEvents); let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk; for (i = 0, ii = subject.length; i < ii; i++) { polygonSet = subject[i]; for (j = 0, jj = polygonSet.length; j < jj; j++) { isExteriorRing = j === 0; if (isExteriorRing) contourId++; processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing); } } for (i = 0, ii = clipping.length; i < ii; i++) { polygonSet = clipping[i]; for (j = 0, jj = polygonSet.length; j < jj; j++) { isExteriorRing = j === 0; if (operation === DIFFERENCE) isExteriorRing = false; if (isExteriorRing) contourId++; processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing); } } return eventQueue; } const EMPTY = []; function trivialOperation(subject, clipping, operation) { let result = null; if (subject.length * clipping.length === 0) { if (operation === INTERSECTION) { result = EMPTY; } else if (operation === DIFFERENCE) { result = subject; } else if (operation === UNION || operation === XOR) { result = (subject.length === 0) ? clipping : subject; } } return result; } function compareBBoxes(subject, clipping, sbbox, cbbox, operation) { let result = null; if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) { if (operation === INTERSECTION) { result = EMPTY; } else if (operation === DIFFERENCE) { result = subject; } else if (operation === UNION || operation === XOR) { result = subject.concat(clipping); } } return result; } function boolean(subject, clipping, operation) { if (typeof subject[0][0][0] === "number") { subject = [subject]; } if (typeof clipping[0][0][0] === "number") { clipping = [clipping]; } let trivial = trivialOperation(subject, clipping, operation); if (trivial) { return trivial === EMPTY ? null : trivial; } const sbbox = [Infinity, Infinity, -Infinity, -Infinity]; const cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time("fill queue"); const eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd("fill queue"); trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation); if (trivial) { return trivial === EMPTY ? null : trivial; } // console.time("subdivide edges"); const sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd("subdivide edges"); // console.time("connect vertices"); const contours = connectEdges(sortedEvents); //console.timeEnd("connect vertices"); // Convert contours to polygons const polygons = []; for (let i = 0; i < contours.length; i++) { let contour = contours[i]; if (contour.isExterior()) { // The exterior ring goes first let rings = [contour.points]; // Followed by holes if any for (let j = 0; j < contour.holeIds.length; j++) { let holeId = contour.holeIds[j]; rings.push(contours[holeId].points); } polygons.push(rings); } } return polygons; } function intersection (subject, clipping) { return boolean(subject, clipping, INTERSECTION); } /** * @class VertexGeometry * @abstract * @classdesc Represents a vertex geometry. */ class VertexGeometry extends Geometry { /** * Create a vertex geometry. * * @constructor * @ignore */ constructor() { super(); this._subsampleThreshold = 0.005; } /** * Finds the polygon pole of inaccessibility, the most distant internal * point from the polygon outline. * * @param {Array>} points2d - 2d points of outline to triangulate. * @returns {Array} Point of inaccessibility. * @ignore */ _getPoleOfInaccessibility2d(points2d) { let pole2d = polylabel$1([points2d], 3e-2); return pole2d; } _project(points2d, transform) { const camera = this._createCamera(transform.upVector().toArray(), transform.unprojectSfM([0, 0], 0), transform.unprojectSfM([0, 0], 10)); return this._deunproject(points2d, transform, camera); } _subsample(points2d, threshold = this._subsampleThreshold) { const subsampled = []; const length = points2d.length; for (let index = 0; index < length; index++) { const p1 = points2d[index]; const p2 = points2d[(index + 1) % length]; subsampled.push(p1); const dist = Math.sqrt(Math.pow((p2[0] - p1[0]), 2) + Math.pow((p2[1] - p1[1]), 2)); const subsamples = Math.floor(dist / threshold); const coeff = 1 / (subsamples + 1); for (let i = 1; i <= subsamples; i++) { const alpha = i * coeff; const subsample = [ (1 - alpha) * p1[0] + alpha * p2[0], (1 - alpha) * p1[1] + alpha * p2[1], ]; subsampled.push(subsample); } } return subsampled; } /** * Triangulates a 2d polygon and returns the triangle * representation as a flattened array of 3d points. * * @param {Array>} points2d - 2d points of outline to triangulate. * @param {Array>} points3d - 3d points of outline corresponding to the 2d points. * @param {Array>>} [holes2d] - 2d points of holes to triangulate. * @param {Array>>} [holes3d] - 3d points of holes corresponding to the 2d points. * @returns {Array} Flattened array of 3d points ordered based on the triangles. * @ignore */ _triangulate(points2d, points3d, holes2d, holes3d) { let data = [points2d.slice(0, -1)]; for (let hole2d of holes2d != null ? holes2d : []) { data.push(hole2d.slice(0, -1)); } let points = points3d.slice(0, -1); for (let hole3d of holes3d != null ? holes3d : []) { points = points.concat(hole3d.slice(0, -1)); } let flattened = earcut$1.flatten(data); let indices = earcut$1(flattened.vertices, flattened.holes, flattened.dimensions); let triangles = []; for (let i = 0; i < indices.length; ++i) { let point = points[indices[i]]; triangles.push(point[0]); triangles.push(point[1]); triangles.push(point[2]); } return triangles; } _triangulateSpherical(points2d, holes2d, transform) { const triangles = []; const epsilon = 1e-9; const subareasX = 3; const subareasY = 3; for (let x = 0; x < subareasX; x++) { for (let y = 0; y < subareasY; y++) { const epsilonX0 = x === 0 ? -epsilon : epsilon; const epsilonY0 = y === 0 ? -epsilon : epsilon; const x0 = x / subareasX + epsilonX0; const y0 = y / subareasY + epsilonY0; const x1 = (x + 1) / subareasX + epsilon; const y1 = (y + 1) / subareasY + epsilon; const bbox2d = [ [x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0], ]; const lookat2d = [ (2 * x + 1) / (2 * subareasX), (2 * y + 1) / (2 * subareasY), ]; triangles.push(...this._triangulateSubarea(points2d, holes2d, bbox2d, lookat2d, transform)); } } return triangles; } _unproject(points2d, transform, distance = 200) { return points2d .map((point) => { return transform.unprojectBasic(point, distance); }); } _createCamera(upVector, position, lookAt) { // @ts-ignore const camera = new Camera$2(); camera.up.copy(new Vector3().fromArray(upVector)); camera.position.copy(new Vector3().fromArray(position)); camera.lookAt(new Vector3().fromArray(lookAt)); camera.updateMatrix(); camera.updateMatrixWorld(true); return camera; } _deunproject(points2d, transform, camera) { return points2d .map((point2d) => { const pointWorld = transform.unprojectBasic(point2d, 10000); const pointCamera = new Vector3(pointWorld[0], pointWorld[1], pointWorld[2]) .applyMatrix4(camera.matrixWorldInverse); return [pointCamera.x / pointCamera.z, pointCamera.y / pointCamera.z]; }); } _triangulateSubarea(points2d, holes2d, bbox2d, lookat2d, transform) { const intersections = intersection([points2d, ...holes2d], [bbox2d]); if (!intersections) { return []; } const triangles = []; const threshold = this._subsampleThreshold; const camera = this._createCamera(transform.upVector().toArray(), transform.unprojectSfM([0, 0], 0), transform.unprojectBasic(lookat2d, 10)); for (const intersection of intersections) { const subsampledPolygon2d = this._subsample(intersection[0], threshold); const polygon2d = this._deunproject(subsampledPolygon2d, transform, camera); const polygon3d = this._unproject(subsampledPolygon2d, transform); const polygonHoles2d = []; const polygonHoles3d = []; for (let i = 1; i < intersection.length; i++) { let subsampledHole2d = this._subsample(intersection[i], threshold); const hole2d = this._deunproject(subsampledHole2d, transform, camera); const hole3d = this._unproject(subsampledHole2d, transform); polygonHoles2d.push(hole2d); polygonHoles3d.push(hole3d); } triangles.push(...this._triangulate(polygon2d, polygon3d, polygonHoles2d, polygonHoles3d)); } return triangles; } } /** * @class RectGeometry * * @classdesc Represents a rectangle geometry in the 2D basic image coordinate system. * * @example * ```js * var basicRect = [0.5, 0.3, 0.7, 0.4]; * var rectGeometry = new RectGeometry(basicRect); * ``` */ class RectGeometry extends VertexGeometry { /** * Create a rectangle geometry. * * @constructor * @param {Array} rect - An array representing the top-left and bottom-right * corners of the rectangle in basic coordinates. Ordered according to [x0, y0, x1, y1]. * * @throws {GeometryTagError} Rectangle coordinates must be valid basic coordinates. */ constructor(rect) { super(); if (rect.length !== 4) { throw new GeometryTagError("Rectangle needs to have four values."); } if (rect[1] > rect[3]) { throw new GeometryTagError("Basic Y coordinates values can not be inverted."); } for (let coord of rect) { if (coord < 0 || coord > 1) { throw new GeometryTagError("Basic coordinates must be on the interval [0, 1]."); } } this._anchorIndex = undefined; this._rect = rect.slice(0, 4); this._inverted = this._rect[0] > this._rect[2]; } /** * Get anchor index property. * * @returns {number} Index representing the current anchor property if * achoring indexing has been initialized. If anchor indexing has not been * initialized or has been terminated undefined will be returned. * @ignore */ get anchorIndex() { return this._anchorIndex; } /** * Get inverted property. * * @returns {boolean} Boolean determining whether the rect geometry is * inverted. For spherical the rect geometrye may be inverted. * @ignore */ get inverted() { return this._inverted; } /** * Get rect property. * * @returns {Array} Array representing the top-left and bottom-right * corners of the rectangle in basic coordinates. */ get rect() { return this._rect; } /** * Initialize anchor indexing to enable setting opposite vertex. * * @param {number} [index] - The index of the vertex to use as anchor. * * @throws {GeometryTagError} If anchor indexing has already been initialized. * @throws {GeometryTagError} If index is not valid (0 to 3). * @ignore */ initializeAnchorIndexing(index) { if (this._anchorIndex !== undefined) { throw new GeometryTagError("Anchor indexing is already initialized."); } if (index < 0 || index > 3) { throw new GeometryTagError(`Invalid anchor index: ${index}.`); } this._anchorIndex = index === undefined ? 0 : index; } /** * Terminate anchor indexing to disable setting pposite vertex. * @ignore */ terminateAnchorIndexing() { this._anchorIndex = undefined; } /** * Set the value of the vertex opposite to the anchor in the polygon * representation of the rectangle. * * @description Setting the opposite vertex may change the anchor index. * * @param {Array} opposite - The new value of the vertex opposite to the anchor. * @param {Transform} transform - The transform of the image related to the rectangle. * * @throws {GeometryTagError} When anchor indexing has not been initialized. * @ignore */ setOppositeVertex2d(opposite, transform) { if (this._anchorIndex === undefined) { throw new GeometryTagError("Anchor indexing needs to be initialized."); } const changed = [ Math.max(0, Math.min(1, opposite[0])), Math.max(0, Math.min(1, opposite[1])), ]; const original = this._rect.slice(); const anchor = this._anchorIndex === 0 ? [original[0], original[3]] : this._anchorIndex === 1 ? [original[0], original[1]] : this._anchorIndex === 2 ? [original[2], original[1]] : [original[2], original[3]]; if (isSpherical(transform.cameraType)) { const deltaX = this._anchorIndex < 2 ? changed[0] - original[2] : changed[0] - original[0]; if (!this._inverted && this._anchorIndex < 2 && changed[0] < 0.25 && original[2] > 0.75 && deltaX < -0.5) { // right side passes boundary rightward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (!this._inverted && this._anchorIndex >= 2 && changed[0] < 0.25 && original[2] > 0.75 && deltaX < -0.5) { // left side passes right side and boundary rightward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (this._inverted && this._anchorIndex >= 2 && changed[0] < 0.25 && original[0] > 0.75 && deltaX < -0.5) { this._inverted = false; if (anchor[0] > changed[0]) { // left side passes boundary rightward this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else { // left side passes right side and boundary rightward this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } } else if (!this._inverted && this._anchorIndex >= 2 && changed[0] > 0.75 && original[0] < 0.25 && deltaX > 0.5) { // left side passes boundary leftward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (!this._inverted && this._anchorIndex < 2 && changed[0] > 0.75 && original[0] < 0.25 && deltaX > 0.5) { // right side passes left side and boundary leftward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (this._inverted && this._anchorIndex < 2 && changed[0] > 0.75 && original[2] < 0.25 && deltaX > 0.5) { this._inverted = false; if (anchor[0] > changed[0]) { // right side passes boundary leftward this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else { // right side passes left side and boundary leftward this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } } else if (this._inverted && this._anchorIndex < 2 && changed[0] > original[0]) { // inverted and right side passes left side completing a loop this._inverted = false; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (this._inverted && this._anchorIndex >= 2 && changed[0] < original[2]) { // inverted and left side passes right side completing a loop this._inverted = false; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (this._inverted) { // if still inverted only top and bottom can switch if (this._anchorIndex < 2) { this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else { this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } } else { // if still not inverted treat as non spherical if (anchor[0] <= changed[0] && anchor[1] > changed[1]) { this._anchorIndex = 0; } else if (anchor[0] <= changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 1; } else if (anchor[0] > changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 2; } else { this._anchorIndex = 3; } } const rect = []; if (this._anchorIndex === 0) { rect[0] = anchor[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = anchor[1]; } else if (this._anchorIndex === 1) { rect[0] = anchor[0]; rect[1] = anchor[1]; rect[2] = changed[0]; rect[3] = changed[1]; } else if (this._anchorIndex === 2) { rect[0] = changed[0]; rect[1] = anchor[1]; rect[2] = anchor[0]; rect[3] = changed[1]; } else { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = anchor[0]; rect[3] = anchor[1]; } if (!this._inverted && rect[0] > rect[2] || this._inverted && rect[0] < rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; } else { if (anchor[0] <= changed[0] && anchor[1] > changed[1]) { this._anchorIndex = 0; } else if (anchor[0] <= changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 1; } else if (anchor[0] > changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 2; } else { this._anchorIndex = 3; } const rect = []; if (this._anchorIndex === 0) { rect[0] = anchor[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = anchor[1]; } else if (this._anchorIndex === 1) { rect[0] = anchor[0]; rect[1] = anchor[1]; rect[2] = changed[0]; rect[3] = changed[1]; } else if (this._anchorIndex === 2) { rect[0] = changed[0]; rect[1] = anchor[1]; rect[2] = anchor[0]; rect[3] = changed[1]; } else { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = anchor[0]; rect[3] = anchor[1]; } if (rect[0] > rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; } this._notifyChanged$.next(this); } /** * Set the value of a vertex in the polygon representation of the rectangle. * * @description The polygon is defined to have the first vertex at the * bottom-left corner with the rest of the vertices following in clockwise order. * * @param {number} index - The index of the vertex to be set. * @param {Array} value - The new value of the vertex. * @param {Transform} transform - The transform of the image related to the rectangle. * @ignore */ setVertex2d(index, value, transform) { let original = this._rect.slice(); let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; let rect = []; if (index === 0) { rect[0] = changed[0]; rect[1] = original[1]; rect[2] = original[2]; rect[3] = changed[1]; } else if (index === 1) { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = original[2]; rect[3] = original[3]; } else if (index === 2) { rect[0] = original[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = original[3]; } else if (index === 3) { rect[0] = original[0]; rect[1] = original[1]; rect[2] = changed[0]; rect[3] = changed[1]; } if (isSpherical(transform.cameraType)) { let passingBoundaryLeftward = index < 2 && changed[0] > 0.75 && original[0] < 0.25 || index >= 2 && this._inverted && changed[0] > 0.75 && original[2] < 0.25; let passingBoundaryRightward = index < 2 && this._inverted && changed[0] < 0.25 && original[0] > 0.75 || index >= 2 && changed[0] < 0.25 && original[2] > 0.75; if (passingBoundaryLeftward || passingBoundaryRightward) { this._inverted = !this._inverted; } else { if (rect[0] - original[0] < -0.25) { rect[0] = original[0]; } if (rect[2] - original[2] > 0.25) { rect[2] = original[2]; } } if (!this._inverted && rect[0] > rect[2] || this._inverted && rect[0] < rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } } else { if (rect[0] > rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; this._notifyChanged$.next(this); } /** @ignore */ setCentroid2d(value, transform) { let original = this._rect.slice(); let x0 = original[0]; let x1 = this._inverted ? original[2] + 1 : original[2]; let y0 = original[1]; let y1 = original[3]; let centerX = x0 + (x1 - x0) / 2; let centerY = y0 + (y1 - y0) / 2; let translationX = 0; if (isSpherical(transform.cameraType)) { translationX = this._inverted ? value[0] + 1 - centerX : value[0] - centerX; } else { let minTranslationX = -x0; let maxTranslationX = 1 - x1; translationX = Math.max(minTranslationX, Math.min(maxTranslationX, value[0] - centerX)); } let minTranslationY = -y0; let maxTranslationY = 1 - y1; let translationY = Math.max(minTranslationY, Math.min(maxTranslationY, value[1] - centerY)); this._rect[0] = original[0] + translationX; this._rect[1] = original[1] + translationY; this._rect[2] = original[2] + translationX; this._rect[3] = original[3] + translationY; if (this._rect[0] < 0) { this._rect[0] += 1; this._inverted = !this._inverted; } else if (this._rect[0] > 1) { this._rect[0] -= 1; this._inverted = !this._inverted; } if (this._rect[2] < 0) { this._rect[2] += 1; this._inverted = !this._inverted; } else if (this._rect[2] > 1) { this._rect[2] -= 1; this._inverted = !this._inverted; } this._notifyChanged$.next(this); } /** * Get the 3D coordinates for the vertices of the rectangle with * interpolated points along the lines. * * @param {Transform} transform - The transform of the image related to * the rectangle. * @returns {Array>} Polygon array of 3D world coordinates * representing the rectangle. * @ignore */ getPoints3d(transform) { return this._getPoints2d() .map((point) => { return transform.unprojectBasic(point, 200); }); } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The method shifts the right side * coordinates of the rectangle by one unit to ensure that the vertices are ordered * clockwise. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getVertex2d(index) { return this._rectToVertices2d(this._rect)[index]; } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The coordinates will not be shifted * so they may not appear in clockwise order when layed out on the plane. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getNonAdjustedVertex2d(index) { return this._rectToNonAdjustedVertices2d(this._rect)[index]; } /** * Get a vertex from the polygon representation of the 3D coordinates for the * vertices of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @param {number} index - Vertex index. * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>} Polygon array of 3D world coordinates representing * the vertices of the geometry. * @ignore */ getVertex3d(index, transform) { return transform.unprojectBasic(this._rectToVertices2d(this._rect)[index], 200); } /** * Get a polygon representation of the 2D basic coordinates for the vertices of the rectangle. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @returns {Array>} Polygon array of 2D basic coordinates representing * the rectangle vertices. * @ignore */ getVertices2d() { return this._rectToVertices2d(this._rect); } /** * Get a polygon representation of the 3D coordinates for the vertices of the rectangle. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @param {Transform} transform - The transform of the image related to the rectangle. * @returns {Array>} Polygon array of 3D world coordinates representing * the rectangle vertices. * @ignore */ getVertices3d(transform) { return this._rectToVertices2d(this._rect) .map((vertex) => { return transform.unprojectBasic(vertex, 200); }); } /** @ignore */ getCentroid2d() { const rect = this._rect; const x0 = rect[0]; const x1 = this._inverted ? rect[2] + 1 : rect[2]; const y0 = rect[1]; const y1 = rect[3]; const centroidX = (x0 + x1) / 2; const centroidY = (y0 + y1) / 2; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { const centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** * @ignore */ getPoleOfInaccessibility2d() { return this._getPoleOfInaccessibility2d(this._rectToVertices2d(this._rect)); } /** @ignore */ getPoleOfInaccessibility3d(transform) { let pole2d = this._getPoleOfInaccessibility2d(this._rectToVertices2d(this._rect)); return transform.unprojectBasic(pole2d, 200); } /** @ignore */ getTriangles3d(transform) { return isSpherical(transform.cameraType) ? [] : this._triangulate(this._project(this._getPoints2d(), transform), this.getPoints3d(transform)); } /** * Check if a particular bottom-right value is valid according to the current * rectangle coordinates. * * @param {Array} bottomRight - The bottom-right coordinates to validate * @returns {boolean} Value indicating whether the provided bottom-right coordinates * are valid. * @ignore */ validate(bottomRight) { let rect = this._rect; if (!this._inverted && bottomRight[0] < rect[0] || bottomRight[0] - rect[2] > 0.25 || bottomRight[1] < rect[1]) { return false; } return true; } /** * Get the 2D coordinates for the vertices of the rectangle with * interpolated points along the lines. * * @returns {Array>} Polygon array of 2D basic coordinates * representing the rectangle. */ _getPoints2d() { let vertices2d = this._rectToVertices2d(this._rect); let sides = vertices2d.length - 1; let sections = 10; let points2d = []; for (let i = 0; i < sides; ++i) { let startX = vertices2d[i][0]; let startY = vertices2d[i][1]; let endX = vertices2d[i + 1][0]; let endY = vertices2d[i + 1][1]; let intervalX = (endX - startX) / (sections - 1); let intervalY = (endY - startY) / (sections - 1); for (let j = 0; j < sections; ++j) { let point = [ startX + j * intervalX, startY + j * intervalY, ]; points2d.push(point); } } return points2d; } /** * Convert the top-left, bottom-right representation of a rectangle to a polygon * representation of the vertices starting at the bottom-left corner going * clockwise. * * @description The method shifts the right side coordinates of the rectangle * by one unit to ensure that the vertices are ordered clockwise. * * @param {Array} rect - Top-left, bottom-right representation of a * rectangle. * @returns {Array>} Polygon representation of the vertices of the * rectangle. */ _rectToVertices2d(rect) { return [ [rect[0], rect[3]], [rect[0], rect[1]], [this._inverted ? rect[2] + 1 : rect[2], rect[1]], [this._inverted ? rect[2] + 1 : rect[2], rect[3]], [rect[0], rect[3]], ]; } /** * Convert the top-left, bottom-right representation of a rectangle to a polygon * representation of the vertices starting at the bottom-left corner going * clockwise. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The coordinates will not be shifted * to ensure that the vertices are ordered clockwise when layed out on the plane. * * @param {Array} rect - Top-left, bottom-right representation of a * rectangle. * @returns {Array>} Polygon representation of the vertices of the * rectangle. */ _rectToNonAdjustedVertices2d(rect) { return [ [rect[0], rect[3]], [rect[0], rect[1]], [rect[2], rect[1]], [rect[2], rect[3]], [rect[0], rect[3]], ]; } } class ExtremePointCreateTag extends CreateTag { constructor(geometry, options, transform, viewportCoords) { super(geometry, transform, viewportCoords); this._options = { color: options.color == null ? 0xFFFFFF : options.color, indicateCompleter: options.indicateCompleter == null ? true : options.indicateCompleter, }; this._rectGeometry = new RectGeometry(this._geometry.getRect2d(transform)); this._createGlObjects(); } create() { if (this._geometry.points.length < 3) { return; } this._geometry.removePoint2d(this._geometry.points.length - 1); this._created$.next(this); } dispose() { super.dispose(); this._disposeObjects(); } getDOMObjects(camera, size) { const container = { offsetHeight: size.height, offsetWidth: size.width, }; const vNodes = []; const points2d = this._geometry.getPoints2d(); const length = points2d.length; for (let index = 0; index < length - 1; index++) { const nonModifiedIndex = index; const [pointX, pointY] = points2d[index]; const pointCanvas = this._viewportCoords.basicToCanvasSafe(pointX, pointY, container, this._transform, camera); if (!pointCanvas) { continue; } const abort = (e) => { e.stopPropagation(); this._aborted$.next(this); }; const remove = (e) => { e.stopPropagation(); this._geometry.removePoint2d(nonModifiedIndex); }; const transform = this._canvasToTransform(pointCanvas); const completerProperties = { onclick: index === 0 && length < 3 ? abort : remove, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); const background = this._colorToBackground(this._options.color); const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } if (length > 2 && this._options.indicateCompleter === true) { const [centroidX, centroidY] = this._geometry.getCentroid2d(this._transform); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidX, centroidY, container, this._transform, camera); if (!!centroidCanvas) { const complete = (e) => { e.stopPropagation(); this._geometry.removePoint2d(this._geometry.points.length - 1); this._created$.next(this); }; const transform = this._canvasToTransform(centroidCanvas); const completerProperties = { onclick: complete, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-completer.mapillary-tag-larger", completerProperties, [])); const pointProperties = { style: { background: this._colorToBackground(this._options.color), transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex.mapillary-tag-larger", pointProperties, [])); const dotProperties = { style: { transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-dot", dotProperties, [])); } } return vNodes; } _onGeometryChanged() { this._disposeObjects(); this._rectGeometry = new RectGeometry(this._geometry.getRect2d(this._transform)); this._createGlObjects(); } _createGlObjects() { this._glObjects = []; const polygon3d = this._rectGeometry.getPoints3d(this._transform); this._outline = this._createOutine(polygon3d, this._options.color); this._glObjects.push(this._outline); } _disposeObjects() { this._disposeLine(this._outline); this._outline = null; this._glObjects = null; } } /** * @class PolygonGeometry * * @classdesc Represents a polygon geometry in the 2D basic image coordinate system. * All polygons and holes provided to the constructor needs to be closed. * * @example * ```js * var basicPolygon = [[0.5, 0.3], [0.7, 0.3], [0.6, 0.5], [0.5, 0.3]]; * var polygonGeometry = new PolygonGeometry(basicPolygon); * ``` */ class PolygonGeometry extends VertexGeometry { /** * Create a polygon geometry. * * @constructor * @param {Array>} polygon - Array of polygon vertices. Must be closed. * @param {Array>>} [holes] - Array of arrays of hole vertices. * Each array of holes vertices must be closed. * * @throws {GeometryTagError} Polygon coordinates must be valid basic coordinates. */ constructor(polygon, holes) { super(); let polygonLength = polygon.length; if (polygonLength < 3) { throw new GeometryTagError("A polygon must have three or more positions."); } if (polygon[0][0] !== polygon[polygonLength - 1][0] || polygon[0][1] !== polygon[polygonLength - 1][1]) { throw new GeometryTagError("First and last positions must be equivalent."); } this._polygon = []; for (let vertex of polygon) { if (vertex[0] < 0 || vertex[0] > 1 || vertex[1] < 0 || vertex[1] > 1) { throw new GeometryTagError("Basic coordinates of polygon must be on the interval [0, 1]."); } this._polygon.push(vertex.slice()); } this._holes = []; if (holes == null) { return; } for (let i = 0; i < holes.length; i++) { let hole = holes[i]; let holeLength = hole.length; if (holeLength < 3) { throw new GeometryTagError("A polygon hole must have three or more positions."); } if (hole[0][0] !== hole[holeLength - 1][0] || hole[0][1] !== hole[holeLength - 1][1]) { throw new GeometryTagError("First and last positions of hole must be equivalent."); } this._holes.push([]); for (let vertex of hole) { if (vertex[0] < 0 || vertex[0] > 1 || vertex[1] < 0 || vertex[1] > 1) { throw new GeometryTagError("Basic coordinates of hole must be on the interval [0, 1]."); } this._holes[i].push(vertex.slice()); } } } /** * Get polygon property. * @returns {Array>} Closed 2d polygon. */ get polygon() { return this._polygon; } /** * Get holes property. * @returns {Array>>} Holes of 2d polygon. */ get holes() { return this._holes; } /** * Add a vertex to the polygon by appending it after the last vertex. * * @param {Array} vertex - Vertex to add. * @ignore */ addVertex2d(vertex) { let clamped = [ Math.max(0, Math.min(1, vertex[0])), Math.max(0, Math.min(1, vertex[1])), ]; this._polygon.splice(this._polygon.length - 1, 0, clamped); this._notifyChanged$.next(this); } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getVertex2d(index) { return this._polygon[index].slice(); } /** * Remove a vertex from the polygon. * * @param {number} index - The index of the vertex to remove. * @ignore */ removeVertex2d(index) { if (index < 0 || index >= this._polygon.length || this._polygon.length < 4) { throw new GeometryTagError("Index for removed vertex must be valid."); } if (index > 0 && index < this._polygon.length - 1) { this._polygon.splice(index, 1); } else { this._polygon.splice(0, 1); this._polygon.pop(); let closing = this._polygon[0].slice(); this._polygon.push(closing); } this._notifyChanged$.next(this); } /** @ignore */ setVertex2d(index, value, transform) { let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; if (index === 0 || index === this._polygon.length - 1) { this._polygon[0] = changed.slice(); this._polygon[this._polygon.length - 1] = changed.slice(); } else { this._polygon[index] = changed.slice(); } this._notifyChanged$.next(this); } /** @ignore */ setCentroid2d(value, transform) { let xs = this._polygon.map((point) => { return point[0]; }); let ys = this._polygon.map((point) => { return point[1]; }); let minX = Math.min.apply(Math, xs); let maxX = Math.max.apply(Math, xs); let minY = Math.min.apply(Math, ys); let maxY = Math.max.apply(Math, ys); let centroid = this.getCentroid2d(); let minTranslationX = -minX; let maxTranslationX = 1 - maxX; let minTranslationY = -minY; let maxTranslationY = 1 - maxY; let translationX = Math.max(minTranslationX, Math.min(maxTranslationX, value[0] - centroid[0])); let translationY = Math.max(minTranslationY, Math.min(maxTranslationY, value[1] - centroid[1])); for (let point of this._polygon) { point[0] += translationX; point[1] += translationY; } this._notifyChanged$.next(this); } /** @ignore */ getPoints3d(transform) { return this._getPoints3d(this._subsample(this._polygon), transform); } /** @ignore */ getVertex3d(index, transform) { return transform.unprojectBasic(this._polygon[index], 200); } /** @ignore */ getVertices2d() { return this._polygon.slice(); } /** @ignore */ getVertices3d(transform) { return this._getPoints3d(this._polygon, transform); } /** * Get a polygon representation of the 3D coordinates for the vertices of each hole * of the geometry. Line segments between vertices will possibly be subsampled * resulting in a larger number of points than the total number of vertices. * * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>>} Array of hole polygons in 3D world coordinates * representing the vertices of each hole of the geometry. * @ignore */ getHolePoints3d(transform) { return this._holes .map((hole2d) => { return this._getPoints3d(this._subsample(hole2d), transform); }); } /** * Get a polygon representation of the 3D coordinates for the vertices of each hole * of the geometry. * * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>>} Array of hole polygons in 3D world coordinates * representing the vertices of each hole of the geometry. * @ignore */ getHoleVertices3d(transform) { return this._holes .map((hole2d) => { return this._getPoints3d(hole2d, transform); }); } /** @ignore */ getCentroid2d() { let polygon = this._polygon; let area = 0; let centroidX = 0; let centroidY = 0; for (let i = 0; i < polygon.length - 1; i++) { let xi = polygon[i][0]; let yi = polygon[i][1]; let xi1 = polygon[i + 1][0]; let yi1 = polygon[i + 1][1]; let a = xi * yi1 - xi1 * yi; area += a; centroidX += (xi + xi1) * a; centroidY += (yi + yi1) * a; } area /= 2; centroidX /= 6 * area; centroidY /= 6 * area; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { let centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** @ignore */ get3dDomainTriangles3d(transform) { return this._triangulate(this._project(this._polygon, transform), this.getVertices3d(transform), this._holes .map((hole2d) => { return this._project(hole2d, transform); }), this.getHoleVertices3d(transform)); } /** @ignore */ getTriangles3d(transform) { if (isSpherical(transform.cameraType)) { return this._triangulateSpherical(this._polygon.slice(), this.holes.slice(), transform); } const points2d = this._project(this._subsample(this._polygon), transform); const points3d = this.getPoints3d(transform); const holes2d = this._holes .map((hole) => { return this._project(this._subsample(hole), transform); }); const holes3d = this.getHolePoints3d(transform); return this._triangulate(points2d, points3d, holes2d, holes3d); } /** @ignore */ getPoleOfInaccessibility2d() { return this._getPoleOfInaccessibility2d(this._polygon.slice()); } /** @ignore */ getPoleOfInaccessibility3d(transform) { let pole2d = this._getPoleOfInaccessibility2d(this._polygon.slice()); return transform.unprojectBasic(pole2d, 200); } _getPoints3d(points2d, transform) { return points2d .map((point) => { return transform.unprojectBasic(point, 200); }); } } class OutlineCreateTag extends CreateTag { constructor(geometry, options, transform, viewportCoords) { super(geometry, transform, viewportCoords); this._options = { color: options.color == null ? 0xFFFFFF : options.color }; this._createGlObjects(); } create() { if (this._geometry instanceof RectGeometry) { this._created$.next(this); } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 2); this._created$.next(this); } } dispose() { super.dispose(); this._disposeLine(this._outline); this._disposeObjects(); } getDOMObjects(camera, size) { const vNodes = []; const container = { offsetHeight: size.height, offsetWidth: size.width, }; const abort = (e) => { e.stopPropagation(); this._aborted$.next(this); }; if (this._geometry instanceof RectGeometry) { const anchorIndex = this._geometry.anchorIndex; const vertexIndex = anchorIndex === undefined ? 1 : anchorIndex; const [basicX, basicY] = this._geometry.getVertex2d(vertexIndex); const canvasPoint = this._viewportCoords.basicToCanvasSafe(basicX, basicY, container, this._transform, camera); if (canvasPoint != null) { const background = this._colorToBackground(this._options.color); const transform = this._canvasToTransform(canvasPoint); const pointProperties = { style: { background: background, transform: transform }, }; const completerProperties = { onclick: abort, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; const [firstVertexBasicX, firstVertexBasicY] = polygonGeometry.getVertex2d(0); const firstVertexCanvas = this._viewportCoords.basicToCanvasSafe(firstVertexBasicX, firstVertexBasicY, container, this._transform, camera); if (firstVertexCanvas != null) { const firstOnclick = polygonGeometry.polygon.length > 4 ? (e) => { e.stopPropagation(); polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 2); this._created$.next(this); } : abort; const transform = this._canvasToTransform(firstVertexCanvas); const completerProperties = { onclick: firstOnclick, style: { transform: transform }, }; const firstClass = polygonGeometry.polygon.length > 4 ? "mapillary-tag-completer" : "mapillary-tag-interactor"; vNodes.push(virtualDom.h("div." + firstClass, completerProperties, [])); } if (polygonGeometry.polygon.length > 3) { const [lastVertexBasicX, lastVertexBasicY] = polygonGeometry.getVertex2d(polygonGeometry.polygon.length - 3); const lastVertexCanvas = this._viewportCoords.basicToCanvasSafe(lastVertexBasicX, lastVertexBasicY, container, this._transform, camera); if (lastVertexCanvas != null) { const remove = (e) => { e.stopPropagation(); polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 3); }; const transform = this._canvasToTransform(lastVertexCanvas); const completerProperties = { onclick: remove, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); } } const verticesBasic = polygonGeometry.polygon.slice(); verticesBasic.splice(-2, 2); for (const vertexBasic of verticesBasic) { const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasic[0], vertexBasic[1], container, this._transform, camera); if (vertexCanvas != null) { const background = this._colorToBackground(this._options.color); const transform = this._canvasToTransform(vertexCanvas); const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } } } return vNodes; } addPoint(point) { if (this._geometry instanceof RectGeometry) { const rectGeometry = this._geometry; if (!rectGeometry.validate(point)) { return; } this._created$.next(this); } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; polygonGeometry.addVertex2d(point); } } _onGeometryChanged() { this._disposeLine(this._outline); this._disposeObjects(); this._createGlObjects(); } _disposeObjects() { this._outline = null; this._glObjects = []; } _createGlObjects() { const polygon3d = this._geometry instanceof RectGeometry ? this._geometry.getPoints3d(this._transform) : this._geometry.getVertices3d(this._transform); this._outline = this._createOutine(polygon3d, this._options.color); this._glObjects = [this._outline]; } } class TagCreator { constructor(component, navigator) { this._component = component; this._navigator = navigator; this._tagOperation$ = new Subject(); this._createPoints$ = new Subject(); this._createPolygon$ = new Subject(); this._createRect$ = new Subject(); this._delete$ = new Subject(); this._tag$ = this._tagOperation$.pipe(scan((tag, operation) => { return operation(tag); }, null), share()); this._replayedTag$ = this._tag$.pipe(publishReplay(1), refCount()); this._replayedTag$.subscribe(); this._createPoints$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new PointsGeometry([ [coord[0], coord[1]], [coord[0], coord[1]], ]); return new ExtremePointCreateTag(geometry, { color: conf.createColor, indicateCompleter: conf.indicatePointsCompleter, }, transform); }; })) .subscribe(this._tagOperation$); this._createRect$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new RectGeometry([ coord[0], coord[1], coord[0], coord[1], ]); return new OutlineCreateTag(geometry, { color: conf.createColor }, transform); }; })) .subscribe(this._tagOperation$); this._createPolygon$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new PolygonGeometry([ [coord[0], coord[1]], [coord[0], coord[1]], [coord[0], coord[1]], ]); return new OutlineCreateTag(geometry, { color: conf.createColor }, transform); }; })) .subscribe(this._tagOperation$); this._delete$.pipe(map(() => { return () => { return null; }; })) .subscribe(this._tagOperation$); } get createRect$() { return this._createRect$; } get createPolygon$() { return this._createPolygon$; } get createPoints$() { return this._createPoints$; } get delete$() { return this._delete$; } get tag$() { return this._tag$; } get replayedTag$() { return this._replayedTag$; } } class TagDOMRenderer { render(tags, createTag, atlas, camera, size) { let vNodes = []; for (const tag of tags) { vNodes = vNodes.concat(tag.getDOMObjects(atlas, camera, size)); } if (createTag != null) { vNodes = vNodes.concat(createTag.getDOMObjects(camera, size)); } return virtualDom.h("div.mapillary-tag-container", {}, vNodes); } clear() { return virtualDom.h("div", {}, []); } } class TagScene { constructor(scene, raycaster) { this._createTag = null; this._needsRender = false; this._raycaster = !!raycaster ? raycaster : new Raycaster(); this._scene = !!scene ? scene : new Scene(); this._objectTags = {}; this._retrievableObjects = []; this._tags = {}; } get needsRender() { return this._needsRender; } add(tags) { for (let tag of tags) { if (tag.tag.id in this._tags) { this._remove(tag.tag.id); } this._add(tag); } this._needsRender = true; } addCreateTag(tag) { for (const object of tag.glObjects) { this._scene.add(object); } this._createTag = { tag: tag, objects: tag.glObjects }; this._needsRender = true; } clear() { for (const id of Object.keys(this._tags)) { this._remove(id); } this._needsRender = false; } get(id) { return this.has(id) ? this._tags[id].tag : undefined; } has(id) { return id in this._tags; } hasCreateTag() { return this._createTag != null; } intersectObjects([viewportX, viewportY], camera) { this._raycaster.setFromCamera(new Vector2(viewportX, viewportY), camera); const intersects = this._raycaster.intersectObjects(this._retrievableObjects); const intersectedIds = []; for (const intersect of intersects) { if (intersect.object.uuid in this._objectTags) { intersectedIds.push(this._objectTags[intersect.object.uuid]); } } return intersectedIds; } remove(ids) { for (const id of ids) { this._remove(id); } this._needsRender = true; } removeAll() { for (const id of Object.keys(this._tags)) { this._remove(id); } this._needsRender = true; } removeCreateTag() { if (this._createTag == null) { return; } for (const object of this._createTag.objects) { this._scene.remove(object); } this._createTag.tag.dispose(); this._createTag = null; this._needsRender = true; } render(perspectiveCamera, renderer) { renderer.render(this._scene, perspectiveCamera); this._needsRender = false; } update() { this._needsRender = true; } updateCreateTagObjects(tag) { if (this._createTag.tag !== tag) { throw new Error("Create tags do not have the same reference."); } for (let object of this._createTag.objects) { this._scene.remove(object); } for (const object of tag.glObjects) { this._scene.add(object); } this._createTag.objects = tag.glObjects; this._needsRender = true; } updateObjects(tag) { const id = tag.tag.id; if (this._tags[id].tag !== tag) { throw new Error("Tags do not have the same reference."); } const tagObjects = this._tags[id]; this._removeObjects(tagObjects); delete this._tags[id]; this._add(tag); this._needsRender = true; } _add(tag) { const id = tag.tag.id; const tagObjects = { tag: tag, objects: [], retrievableObjects: [] }; this._tags[id] = tagObjects; for (const object of tag.getGLObjects()) { tagObjects.objects.push(object); this._scene.add(object); } for (const retrievableObject of tag.getRetrievableObjects()) { tagObjects.retrievableObjects.push(retrievableObject); this._retrievableObjects.push(retrievableObject); this._objectTags[retrievableObject.uuid] = tag.tag.id; } } _remove(id) { const tagObjects = this._tags[id]; this._removeObjects(tagObjects); tagObjects.tag.dispose(); delete this._tags[id]; } _removeObjects(tagObjects) { for (const object of tagObjects.objects) { this._scene.remove(object); } for (const retrievableObject of tagObjects.retrievableObjects) { const index = this._retrievableObjects.indexOf(retrievableObject); if (index !== -1) { this._retrievableObjects.splice(index, 1); } } } } /** * Enumeration for tag modes * @enum {number} * @readonly * @description Modes for the interaction in the tag component. */ exports.TagMode = void 0; (function (TagMode) { /** * Disables creating tags. */ TagMode[TagMode["Default"] = 0] = "Default"; /** * Create a point geometry through a click. */ TagMode[TagMode["CreatePoint"] = 1] = "CreatePoint"; /** * Create a points geometry through clicks. */ TagMode[TagMode["CreatePoints"] = 2] = "CreatePoints"; /** * Create a polygon geometry through clicks. */ TagMode[TagMode["CreatePolygon"] = 3] = "CreatePolygon"; /** * Create a rect geometry through clicks. */ TagMode[TagMode["CreateRect"] = 4] = "CreateRect"; /** * Create a rect geometry through drag. * * @description Claims the mouse which results in mouse handlers like * drag pan and scroll zoom becoming inactive. */ TagMode[TagMode["CreateRectDrag"] = 5] = "CreateRectDrag"; })(exports.TagMode || (exports.TagMode = {})); var TagOperation; (function (TagOperation) { TagOperation[TagOperation["None"] = 0] = "None"; TagOperation[TagOperation["Centroid"] = 1] = "Centroid"; TagOperation[TagOperation["Vertex"] = 2] = "Vertex"; })(TagOperation || (TagOperation = {})); class RenderTag { constructor(tag, transform, viewportCoords) { this._tag = tag; this._transform = transform; this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._glObjectsChanged$ = new Subject(); this._interact$ = new Subject(); } get glObjectsChanged$() { return this._glObjectsChanged$; } get interact$() { return this._interact$; } get tag() { return this._tag; } } class OutlineRenderTagBase extends RenderTag { constructor(tag, transform) { super(tag, transform); this._geometryChangedSubscription = this._tag.geometry.changed$ .subscribe(() => { this._onGeometryChanged(); }); this._changedSubscription = this._tag.changed$ .subscribe(() => { const glObjectsChanged = this._onTagChanged(); if (glObjectsChanged) { this._glObjectsChanged$.next(this); } }); } dispose() { this._changedSubscription.unsubscribe(); this._geometryChangedSubscription.unsubscribe(); } _colorToCss(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _createFill() { let triangles = this._getTriangles(); let positions = new Float32Array(triangles); let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); let material = new MeshBasicMaterial({ side: DoubleSide, transparent: true }); this._updateFillMaterial(material); return new Mesh(geometry, material); } _createLine(points3d) { let positions = this._getLinePositions(points3d); let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); let material = new LineBasicMaterial(); this._updateLineBasicMaterial(material); const line = new Line(geometry, material); line.renderOrder = 1; return line; } _createOutline() { return this._createLine(this._getPoints3d()); } _disposeFill() { if (this._fill == null) { return; } this._fill.geometry.dispose(); this._fill.material.dispose(); this._fill = null; } _disposeOutline() { if (this._outline == null) { return; } this._outline.geometry.dispose(); this._outline.material.dispose(); this._outline = null; } _getLinePositions(points3d) { let length = points3d.length; let positions = new Float32Array(length * 3); for (let i = 0; i < length; ++i) { let index = 3 * i; let position = points3d[i]; positions[index + 0] = position[0]; positions[index + 1] = position[1]; positions[index + 2] = position[2]; } return positions; } _interact(operation, cursor, vertexIndex) { return (e) => { let offsetX = e.offsetX - e.target.offsetWidth / 2; let offsetY = e.offsetY - e.target.offsetHeight / 2; this._interact$.next({ cursor: cursor, offsetX: offsetX, offsetY: offsetY, operation: operation, tag: this._tag, vertexIndex: vertexIndex, }); }; } _updateFillGeometry() { let triangles = this._getTriangles(); let positions = new Float32Array(triangles); let geometry = this._fill.geometry; let attribute = geometry.getAttribute("position"); if (attribute.array.length === positions.length) { attribute.set(positions); attribute.needsUpdate = true; } else { geometry.deleteAttribute("position"); geometry.setAttribute("position", new BufferAttribute(positions, 3)); } geometry.computeBoundingSphere(); } _updateLine(line, points3d) { let positions = this._getLinePositions(points3d); let geometry = line.geometry; let attribute = geometry.getAttribute("position"); attribute.set(positions); attribute.needsUpdate = true; geometry.computeBoundingSphere(); } _updateOulineGeometry() { this._updateLine(this._outline, this._getPoints3d()); } } /** * @class OutlineRenderTag * @classdesc Tag visualizing the properties of an OutlineTag. */ class ExtremePointRenderTag extends OutlineRenderTagBase { constructor(tag, transform) { super(tag, transform); this._rectGeometry = new RectGeometry(this._tag.geometry.getRect2d(transform)); this._fill = !isSpherical(transform.cameraType) ? this._createFill() : null; this._outline = this._tag.lineWidth >= 1 ? this._createOutline() : null; } dispose() { super.dispose(); this._disposeFill(); this._disposeOutline(); } getDOMObjects(atlas, camera, size) { const vNodes = []; const container = { offsetHeight: size.height, offsetWidth: size.width, }; if (!this._tag.editable) { return vNodes; } const lineColor = this._colorToCss(this._tag.lineColor); const points2d = this._tag.geometry.getPoints2d(); for (let i = 0; i < points2d.length; i++) { const [vertexBasicX, vertexBasicY] = points2d[i]; const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasicX, vertexBasicY, container, this._transform, camera); if (vertexCanvas == null) { continue; } const cursor = "crosshair"; const interact = this._interact(TagOperation.Vertex, cursor, i); const vertexCanvasX = Math.round(vertexCanvas[0]); const vertexCanvasY = Math.round(vertexCanvas[1]); const transform = `translate(-50%, -50%) translate(${vertexCanvasX}px,${vertexCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform, cursor: cursor }, }; vNodes.push(virtualDom.h("div.mapillary-tag-resizer", properties, [])); if (!this._tag.indicateVertices) { continue; } const pointProperties = { style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { const glObjects = []; if (this._fill != null) { glObjects.push(this._fill); } if (this._outline != null) { glObjects.push(this._outline); } return glObjects; } getRetrievableObjects() { return this._fill != null ? [this._fill] : []; } _onGeometryChanged() { this._rectGeometry = new RectGeometry(this._tag.geometry.getRect2d(this._transform)); if (this._fill != null) { this._updateFillGeometry(); } if (this._outline != null) { this._updateOulineGeometry(); } } _onTagChanged() { let glObjectsChanged = false; if (this._fill != null) { this._updateFillMaterial(this._fill.material); } if (this._outline == null) { if (this._tag.lineWidth >= 1) { this._outline = this._createOutline(); glObjectsChanged = true; } } else { this._updateOutlineMaterial(); } return glObjectsChanged; } _getPoints3d() { return this._rectGeometry.getPoints3d(this._transform); } _getTriangles() { return this._rectGeometry.getTriangles3d(this._transform); } _updateFillMaterial(material) { material.color = new Color(this._tag.fillColor); material.opacity = this._tag.fillOpacity; material.needsUpdate = true; } _updateLineBasicMaterial(material) { material.color = new Color(this._tag.lineColor); material.linewidth = Math.max(this._tag.lineWidth, 1); material.visible = this._tag.lineWidth >= 1 && this._tag.lineOpacity > 0; material.opacity = this._tag.lineOpacity; material.transparent = this._tag.lineOpacity < 1; material.needsUpdate = true; } _updateOutlineMaterial() { let material = this._outline.material; this._updateLineBasicMaterial(material); } } /** * @class Tag * @abstract * @classdesc Abstract class representing the basic functionality of for a tag. */ class Tag extends EventEmitter { /** * Create a tag. * * @constructor * @param {string} id * @param {Geometry} geometry */ constructor(id, geometry) { super(); this._id = id; this._geometry = geometry; this._notifyChanged$ = new Subject(); this._notifyChanged$ .subscribe((t) => { const type = "tag"; const event = { target: this, type, }; this.fire(type, event); }); this._geometry.changed$ .subscribe((g) => { const type = "geometry"; const event = { target: this, type, }; this.fire(type, event); }); } /** * Get id property. * @returns {string} */ get id() { return this._id; } /** * Get geometry property. * @returns {Geometry} The geometry of the tag. */ get geometry() { return this._geometry; } /** * Get changed observable. * @returns {Observable} * @ignore */ get changed$() { return this._notifyChanged$; } /** * Get geometry changed observable. * @returns {Observable} * @ignore */ get geometryChanged$() { return this._geometry.changed$.pipe(map(() => { return this; }), share()); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } } /** * @class ExtremePointTag * * @classdesc Tag holding properties for visualizing a extreme points * and their outline. * * @example * ```js * var geometry = new PointsGeometry([[0.3, 0.3], [0.5, 0.4]]); * var tag = new ExtremePointTag( * "id-1", * geometry * { editable: true, lineColor: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class ExtremePointTag extends Tag { /** * Create an extreme point tag. * * @override * @constructor * @param {string} id - Unique identifier of the tag. * @param {PointsGeometry} geometry - Geometry defining points of tag. * @param {ExtremePointTagOptions} options - Options defining the visual appearance and * behavior of the extreme point tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; this._editable = options.editable == null ? false : options.editable; this._fillColor = options.fillColor == null ? 0xFFFFFF : options.fillColor; this._fillOpacity = options.fillOpacity == null ? 0.0 : options.fillOpacity; this._indicateVertices = options.indicateVertices == null ? true : options.indicateVertices; this._lineColor = options.lineColor == null ? 0xFFFFFF : options.lineColor; this._lineOpacity = options.lineOpacity == null ? 1 : options.lineOpacity; this._lineWidth = options.lineWidth == null ? 1 : options.lineWidth; } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { this._editable = value; this._notifyChanged$.next(this); } /** * Get fill color property. * @returns {number} */ get fillColor() { return this._fillColor; } /** * Set fill color property. * @param {number} * * @fires changed */ set fillColor(value) { this._fillColor = value; this._notifyChanged$.next(this); } /** * Get fill opacity property. * @returns {number} */ get fillOpacity() { return this._fillOpacity; } /** * Set fill opacity property. * @param {number} * * @fires changed */ set fillOpacity(value) { this._fillOpacity = value; this._notifyChanged$.next(this); } /** @inheritdoc */ get geometry() { return this._geometry; } /** * Get indicate vertices property. * @returns {boolean} Value indicating if vertices should be indicated * when tag is editable. */ get indicateVertices() { return this._indicateVertices; } /** * Set indicate vertices property. * @param {boolean} * * @fires changed */ set indicateVertices(value) { this._indicateVertices = value; this._notifyChanged$.next(this); } /** * Get line color property. * @returns {number} */ get lineColor() { return this._lineColor; } /** * Set line color property. * @param {number} * * @fires changed */ set lineColor(value) { this._lineColor = value; this._notifyChanged$.next(this); } /** * Get line opacity property. * @returns {number} */ get lineOpacity() { return this._lineOpacity; } /** * Set line opacity property. * @param {number} * * @fires changed */ set lineOpacity(value) { this._lineOpacity = value; this._notifyChanged$.next(this); } /** * Get line width property. * @returns {number} */ get lineWidth() { return this._lineWidth; } /** * Set line width property. * @param {number} * * @fires changed */ set lineWidth(value) { this._lineWidth = value; this._notifyChanged$.next(this); } /** * Set options for tag. * * @description Sets all the option properties provided and keeps * the rest of the values as is. * * @param {ExtremePointTagOptions} options - Extreme point tag options * * @fires changed */ setOptions(options) { this._editable = options.editable == null ? this._editable : options.editable; this._indicateVertices = options.indicateVertices == null ? this._indicateVertices : options.indicateVertices; this._lineColor = options.lineColor == null ? this._lineColor : options.lineColor; this._lineWidth = options.lineWidth == null ? this._lineWidth : options.lineWidth; this._fillColor = options.fillColor == null ? this._fillColor : options.fillColor; this._fillOpacity = options.fillOpacity == null ? this._fillOpacity : options.fillOpacity; this._notifyChanged$.next(this); } } /** * Enumeration for tag domains. * @enum {number} * @readonly * @description Defines where lines between two vertices are treated * as straight. * * Only applicable for polygons. For rectangles lines between * vertices are always treated as straight in the distorted 2D * projection and bended in the undistorted 3D space. */ exports.TagDomain = void 0; (function (TagDomain) { /** * Treats lines between two vertices as straight in the * distorted 2D projection, i.e. on the image. If the image * is distorted this will result in bended lines when rendered * in the undistorted 3D space. */ TagDomain[TagDomain["TwoDimensional"] = 0] = "TwoDimensional"; /** * Treats lines as straight in the undistorted 3D space. If the * image is distorted this will result in bended lines when rendered * on the distorted 2D projection of the image. */ TagDomain[TagDomain["ThreeDimensional"] = 1] = "ThreeDimensional"; })(exports.TagDomain || (exports.TagDomain = {})); /** * @class OutlineRenderTag * @classdesc Tag visualizing the properties of an OutlineTag. */ class OutlineRenderTag extends OutlineRenderTagBase { constructor(tag, transform) { super(tag, transform); this._fill = !isSpherical(transform.cameraType) ? this._createFill() : tag.domain === exports.TagDomain.TwoDimensional && tag.geometry instanceof PolygonGeometry ? this._createFill() : null; this._holes = this._tag.lineWidth >= 1 ? this._createHoles() : []; this._outline = this._tag.lineWidth >= 1 ? this._createOutline() : null; } dispose() { super.dispose(); this._disposeFill(); this._disposeHoles(); this._disposeOutline(); } getDOMObjects(atlas, camera, size) { const vNodes = []; const isRect = this._tag.geometry instanceof RectGeometry; const isPerspective = !isSpherical(this._transform.cameraType); const container = { offsetHeight: size.height, offsetWidth: size.width, }; if (this._tag.icon != null && (isRect || isPerspective)) { const [iconBasicX, iconBasicY] = this._tag.geometry instanceof RectGeometry ? this._tag.geometry.getVertex2d(this._tag.iconIndex) : this._tag.geometry.getPoleOfInaccessibility2d(); const iconCanvas = this._viewportCoords.basicToCanvasSafe(iconBasicX, iconBasicY, container, this._transform, camera); if (iconCanvas != null) { const interact = () => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: this._tag }); }; if (atlas.loaded) { const sprite = atlas.getDOMSprite(this._tag.icon, this._tag.iconFloat); const iconCanvasX = Math.round(iconCanvas[0]); const iconCanvasY = Math.round(iconCanvas[1]); const transform = `translate(${iconCanvasX}px,${iconCanvasY}px)`; const click = (e) => { e.stopPropagation(); this._tag.click$.next(this._tag); }; const properties = { onclick: click, onpointerdown: interact, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-symbol", properties, [sprite])); } } } else if (this._tag.text != null && (isRect || isPerspective)) { const [textBasicX, textBasicY] = this._tag.geometry instanceof RectGeometry ? this._tag.geometry.getVertex2d(3) : this._tag.geometry.getPoleOfInaccessibility2d(); const textCanvas = this._viewportCoords.basicToCanvasSafe(textBasicX, textBasicY, container, this._transform, camera); if (textCanvas != null) { const textCanvasX = Math.round(textCanvas[0]); const textCanvasY = Math.round(textCanvas[1]); const transform = this._tag.geometry instanceof RectGeometry ? `translate(${textCanvasX}px,${textCanvasY}px)` : `translate(-50%, -50%) translate(${textCanvasX}px,${textCanvasY}px)`; const interact = () => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: this._tag }); }; const properties = { onpointerdown: interact, style: { color: this._colorToCss(this._tag.textColor), transform: transform, }, textContent: this._tag.text, }; vNodes.push(virtualDom.h("span.mapillary-tag-symbol", properties, [])); } } if (!this._tag.editable) { return vNodes; } const lineColor = this._colorToCss(this._tag.lineColor); if (this._tag.geometry instanceof RectGeometry) { const [centroidBasicX, centroidBasicY] = this._tag.geometry.getCentroid2d(); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidBasicX, centroidBasicY, container, this._transform, camera); if (centroidCanvas != null) { const interact = this._interact(TagOperation.Centroid, "move"); const centroidCanvasX = Math.round(centroidCanvas[0]); const centroidCanvasY = Math.round(centroidCanvas[1]); const transform = `translate(-50%, -50%) translate(${centroidCanvasX}px,${centroidCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-mover", properties, [])); } } const vertices2d = this._tag.geometry.getVertices2d(); for (let i = 0; i < vertices2d.length - 1; i++) { if (isRect && ((this._tag.icon != null && i === this._tag.iconIndex) || (this._tag.icon == null && this._tag.text != null && i === 3))) { continue; } const [vertexBasicX, vertexBasicY] = vertices2d[i]; const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasicX, vertexBasicY, container, this._transform, camera); if (vertexCanvas == null) { continue; } const cursor = isRect ? i % 2 === 0 ? "nesw-resize" : "nwse-resize" : "crosshair"; const interact = this._interact(TagOperation.Vertex, cursor, i); const vertexCanvasX = Math.round(vertexCanvas[0]); const vertexCanvasY = Math.round(vertexCanvas[1]); const transform = `translate(-50%, -50%) translate(${vertexCanvasX}px,${vertexCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform, cursor: cursor }, }; vNodes.push(virtualDom.h("div.mapillary-tag-resizer", properties, [])); if (!this._tag.indicateVertices) { continue; } const pointProperties = { style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { const glObjects = []; if (this._fill != null) { glObjects.push(this._fill); } for (const hole of this._holes) { glObjects.push(hole); } if (this._outline != null) { glObjects.push(this._outline); } return glObjects; } getRetrievableObjects() { return this._fill != null ? [this._fill] : []; } _onGeometryChanged() { if (this._fill != null) { this._updateFillGeometry(); } if (this._holes.length > 0) { this._updateHoleGeometries(); } if (this._outline != null) { this._updateOulineGeometry(); } } _onTagChanged() { let glObjectsChanged = false; if (this._fill != null) { this._updateFillMaterial(this._fill.material); } if (this._outline == null) { if (this._tag.lineWidth >= 1) { this._holes = this._createHoles(); this._outline = this._createOutline(); glObjectsChanged = true; } } else { this._updateHoleMaterials(); this._updateOutlineMaterial(); } return glObjectsChanged; } _getPoints3d() { return this._in3dDomain() ? this._tag.geometry.getVertices3d(this._transform) : this._tag.geometry.getPoints3d(this._transform); } _getTriangles() { return this._in3dDomain() ? this._tag.geometry.get3dDomainTriangles3d(this._transform) : this._tag.geometry.getTriangles3d(this._transform); } _updateFillMaterial(material) { material.color = new Color(this._tag.fillColor); material.opacity = this._tag.fillOpacity; material.needsUpdate = true; } _updateLineBasicMaterial(material) { material.color = new Color(this._tag.lineColor); material.linewidth = Math.max(this._tag.lineWidth, 1); material.visible = this._tag.lineWidth >= 1 && this._tag.lineOpacity > 0; material.opacity = this._tag.lineOpacity; material.transparent = this._tag.lineOpacity < 1; material.needsUpdate = true; } _createHoles() { let holes = []; if (this._tag.geometry instanceof PolygonGeometry) { let holes3d = this._getHoles3d(); for (let holePoints3d of holes3d) { let hole = this._createLine(holePoints3d); holes.push(hole); } } return holes; } _disposeHoles() { for (let hole of this._holes) { hole.geometry.dispose(); hole.material.dispose(); } this._holes = []; } _getHoles3d() { const polygonGeometry = this._tag.geometry; return this._in3dDomain() ? polygonGeometry.getHoleVertices3d(this._transform) : polygonGeometry.getHolePoints3d(this._transform); } _in3dDomain() { return this._tag.geometry instanceof PolygonGeometry && this._tag.domain === exports.TagDomain.ThreeDimensional; } _updateHoleGeometries() { let holes3d = this._getHoles3d(); if (holes3d.length !== this._holes.length) { throw new Error("Changing the number of holes is not supported."); } for (let i = 0; i < this._holes.length; i++) { let holePoints3d = holes3d[i]; let hole = this._holes[i]; this._updateLine(hole, holePoints3d); } } _updateHoleMaterials() { for (const hole of this._holes) { this._updateLineBasicMaterial(hole.material); } } _updateOutlineMaterial() { this._updateLineBasicMaterial(this._outline.material); } } /** * Enumeration for alignments * @enum {number} * @readonly */ exports.Alignment = void 0; (function (Alignment) { /** * Align to bottom */ Alignment[Alignment["Bottom"] = 0] = "Bottom"; /** * Align to bottom left */ Alignment[Alignment["BottomLeft"] = 1] = "BottomLeft"; /** * Align to bottom right */ Alignment[Alignment["BottomRight"] = 2] = "BottomRight"; /** * Align to center */ Alignment[Alignment["Center"] = 3] = "Center"; /** * Align to left */ Alignment[Alignment["Left"] = 4] = "Left"; /** * Align to right */ Alignment[Alignment["Right"] = 5] = "Right"; /** * Align to top */ Alignment[Alignment["Top"] = 6] = "Top"; /** * Align to top left */ Alignment[Alignment["TopLeft"] = 7] = "TopLeft"; /** * Align to top right */ Alignment[Alignment["TopRight"] = 8] = "TopRight"; })(exports.Alignment || (exports.Alignment = {})); /** * @class OutlineTag * * @classdesc Tag holding properties for visualizing a geometry outline. * * @example * ```js * var geometry = new RectGeometry([0.3, 0.3, 0.5, 0.4]); * var tag = new OutlineTag( * "id-1", * geometry * { editable: true, lineColor: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class OutlineTag extends Tag { /** * Create an outline tag. * * @override * @constructor * @param {string} id - Unique identifier of the tag. * @param {VertexGeometry} geometry - Geometry defining vertices of tag. * @param {OutlineTagOptions} options - Options defining the visual appearance and * behavior of the outline tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; const domain = options.domain != null && geometry instanceof PolygonGeometry ? options.domain : exports.TagDomain.TwoDimensional; const twoDimensionalPolygon = this._twoDimensionalPolygon(domain, geometry); this._domain = domain; this._editable = options.editable == null || twoDimensionalPolygon ? false : options.editable; this._fillColor = options.fillColor == null ? 0xFFFFFF : options.fillColor; this._fillOpacity = options.fillOpacity == null ? 0.0 : options.fillOpacity; this._icon = options.icon === undefined ? null : options.icon; this._iconFloat = options.iconFloat == null ? exports.Alignment.Center : options.iconFloat; this._iconIndex = options.iconIndex == null ? 3 : options.iconIndex; this._indicateVertices = options.indicateVertices == null ? true : options.indicateVertices; this._lineColor = options.lineColor == null ? 0xFFFFFF : options.lineColor; this._lineOpacity = options.lineOpacity == null ? 1 : options.lineOpacity; this._lineWidth = options.lineWidth == null ? 1 : options.lineWidth; this._text = options.text === undefined ? null : options.text; this._textColor = options.textColor == null ? 0xFFFFFF : options.textColor; this._click$ = new Subject(); this._click$ .subscribe(() => { const type = "click"; const event = { target: this, type, }; this.fire(type, event); }); } /** * Click observable. * * @description An observable emitting the tag when the icon of the * tag has been clicked. * * @returns {Observable} */ get click$() { return this._click$; } /** * Get domain property. * * @description Readonly property that can only be set in constructor. * * @returns Value indicating the domain of the tag. */ get domain() { return this._domain; } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { if (this._twoDimensionalPolygon(this._domain, this._geometry)) { return; } this._editable = value; this._notifyChanged$.next(this); } /** * Get fill color property. * @returns {number} */ get fillColor() { return this._fillColor; } /** * Set fill color property. * @param {number} * * @fires changed */ set fillColor(value) { this._fillColor = value; this._notifyChanged$.next(this); } /** * Get fill opacity property. * @returns {number} */ get fillOpacity() { return this._fillOpacity; } /** * Set fill opacity property. * @param {number} * * @fires changed */ set fillOpacity(value) { this._fillOpacity = value; this._notifyChanged$.next(this); } /** @inheritdoc */ get geometry() { return this._geometry; } /** * Get icon property. * @returns {string} */ get icon() { return this._icon; } /** * Set icon property. * @param {string} * * @fires changed */ set icon(value) { this._icon = value; this._notifyChanged$.next(this); } /** * Get icon float property. * @returns {Alignment} */ get iconFloat() { return this._iconFloat; } /** * Set icon float property. * @param {Alignment} * * @fires changed */ set iconFloat(value) { this._iconFloat = value; this._notifyChanged$.next(this); } /** * Get icon index property. * @returns {number} */ get iconIndex() { return this._iconIndex; } /** * Set icon index property. * @param {number} * * @fires changed */ set iconIndex(value) { this._iconIndex = value; this._notifyChanged$.next(this); } /** * Get indicate vertices property. * @returns {boolean} Value indicating if vertices should be indicated * when tag is editable. */ get indicateVertices() { return this._indicateVertices; } /** * Set indicate vertices property. * @param {boolean} * * @fires changed */ set indicateVertices(value) { this._indicateVertices = value; this._notifyChanged$.next(this); } /** * Get line color property. * @returns {number} */ get lineColor() { return this._lineColor; } /** * Set line color property. * @param {number} * * @fires changed */ set lineColor(value) { this._lineColor = value; this._notifyChanged$.next(this); } /** * Get line opacity property. * @returns {number} */ get lineOpacity() { return this._lineOpacity; } /** * Set line opacity property. * @param {number} * * @fires changed */ set lineOpacity(value) { this._lineOpacity = value; this._notifyChanged$.next(this); } /** * Get line width property. * @returns {number} */ get lineWidth() { return this._lineWidth; } /** * Set line width property. * @param {number} * * @fires changed */ set lineWidth(value) { this._lineWidth = value; this._notifyChanged$.next(this); } /** * Get text property. * @returns {string} */ get text() { return this._text; } /** * Set text property. * @param {string} * * @fires changed */ set text(value) { this._text = value; this._notifyChanged$.next(this); } /** * Get text color property. * @returns {number} */ get textColor() { return this._textColor; } /** * Set text color property. * @param {number} * * @fires changed */ set textColor(value) { this._textColor = value; this._notifyChanged$.next(this); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Set options for tag. * * @description Sets all the option properties provided and keeps * the rest of the values as is. * * @param {OutlineTagOptions} options - Outline tag options * * @fires changed */ setOptions(options) { const twoDimensionalPolygon = this._twoDimensionalPolygon(this._domain, this._geometry); this._editable = twoDimensionalPolygon || options.editable == null ? this._editable : options.editable; this._icon = options.icon === undefined ? this._icon : options.icon; this._iconFloat = options.iconFloat == null ? this._iconFloat : options.iconFloat; this._iconIndex = options.iconIndex == null ? this._iconIndex : options.iconIndex; this._indicateVertices = options.indicateVertices == null ? this._indicateVertices : options.indicateVertices; this._lineColor = options.lineColor == null ? this._lineColor : options.lineColor; this._lineWidth = options.lineWidth == null ? this._lineWidth : options.lineWidth; this._fillColor = options.fillColor == null ? this._fillColor : options.fillColor; this._fillOpacity = options.fillOpacity == null ? this._fillOpacity : options.fillOpacity; this._text = options.text === undefined ? this._text : options.text; this._textColor = options.textColor == null ? this._textColor : options.textColor; this._notifyChanged$.next(this); } _twoDimensionalPolygon(domain, geometry) { return domain !== exports.TagDomain.ThreeDimensional && geometry instanceof PolygonGeometry; } } /** * @class SpotRenderTag * @classdesc Tag visualizing the properties of a SpotTag. */ class SpotRenderTag extends RenderTag { dispose() { } getDOMObjects(atlas, camera, size) { const tag = this._tag; const container = { offsetHeight: size.height, offsetWidth: size.width, }; const vNodes = []; const [centroidBasicX, centroidBasicY] = tag.geometry.getCentroid2d(); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidBasicX, centroidBasicY, container, this._transform, camera); if (centroidCanvas != null) { const interactNone = (e) => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: tag }); }; const canvasX = Math.round(centroidCanvas[0]); const canvasY = Math.round(centroidCanvas[1]); if (tag.icon != null) { if (atlas.loaded) { const sprite = atlas.getDOMSprite(tag.icon, exports.Alignment.Bottom); const iconTransform = `translate(${canvasX}px,${canvasY + 8}px)`; const properties = { onpointerdown: interactNone, style: { pointerEvents: "all", transform: iconTransform, }, }; vNodes.push(virtualDom.h("div", properties, [sprite])); } } else if (tag.text != null) { const textTransform = `translate(-50%,0%) translate(${canvasX}px,${canvasY + 8}px)`; const properties = { onpointerdown: interactNone, style: { color: this._colorToCss(tag.textColor), transform: textTransform, }, textContent: tag.text, }; vNodes.push(virtualDom.h("span.mapillary-tag-symbol", properties, [])); } const interact = this._interact(TagOperation.Centroid, tag, "move"); const background = this._colorToCss(tag.color); const transform = `translate(-50%,-50%) translate(${canvasX}px,${canvasY}px)`; if (tag.editable) { let interactorProperties = { onpointerdown: interact, style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-spot-interactor", interactorProperties, [])); } const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { return []; } getRetrievableObjects() { return []; } _colorToCss(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _interact(operation, tag, cursor, vertexIndex) { return (e) => { const offsetX = e.offsetX - e.target.offsetWidth / 2; const offsetY = e.offsetY - e.target.offsetHeight / 2; this._interact$.next({ cursor: cursor, offsetX: offsetX, offsetY: offsetY, operation: operation, tag: tag, vertexIndex: vertexIndex, }); }; } } /** * @class SpotTag * * @classdesc Tag holding properties for visualizing the centroid of a geometry. * * @example * ```js * var geometry = new PointGeometry([0.3, 0.3]); * var tag = new SpotTag( * "id-1", * geometry * { editable: true, color: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class SpotTag extends Tag { /** * Create a spot tag. * * @override * @constructor * @param {string} id * @param {Geometry} geometry * @param {IOutlineTagOptions} options - Options defining the visual appearance and * behavior of the spot tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; this._color = options.color == null ? 0xFFFFFF : options.color; this._editable = options.editable == null ? false : options.editable; this._icon = options.icon === undefined ? null : options.icon; this._text = options.text === undefined ? null : options.text; this._textColor = options.textColor == null ? 0xFFFFFF : options.textColor; } /** * Get color property. * @returns {number} The color of the spot as a hexagonal number; */ get color() { return this._color; } /** * Set color property. * @param {number} * * @fires changed */ set color(value) { this._color = value; this._notifyChanged$.next(this); } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { this._editable = value; this._notifyChanged$.next(this); } /** * Get icon property. * @returns {string} */ get icon() { return this._icon; } /** * Set icon property. * @param {string} * * @fires changed */ set icon(value) { this._icon = value; this._notifyChanged$.next(this); } /** * Get text property. * @returns {string} */ get text() { return this._text; } /** * Set text property. * @param {string} * * @fires changed */ set text(value) { this._text = value; this._notifyChanged$.next(this); } /** * Get text color property. * @returns {number} */ get textColor() { return this._textColor; } /** * Set text color property. * @param {number} * * @fires changed */ set textColor(value) { this._textColor = value; this._notifyChanged$.next(this); } /** * Set options for tag. * * @description Sets all the option properties provided and keps * the rest of the values as is. * * @param {SpotTagOptions} options - Spot tag options * * @fires changed */ setOptions(options) { this._color = options.color == null ? this._color : options.color; this._editable = options.editable == null ? this._editable : options.editable; this._icon = options.icon === undefined ? this._icon : options.icon; this._text = options.text === undefined ? this._text : options.text; this._textColor = options.textColor == null ? this._textColor : options.textColor; this._notifyChanged$.next(this); } } class TagSet { constructor() { this._active = false; this._hash = {}; this._hashDeactivated = {}; this._notifyChanged$ = new Subject(); } get active() { return this._active; } get changed$() { return this._notifyChanged$; } activate(transform) { if (this._active) { return; } for (const id in this._hashDeactivated) { if (!this._hashDeactivated.hasOwnProperty(id)) { continue; } const tag = this._hashDeactivated[id]; this._add(tag, transform); } this._hashDeactivated = {}; this._active = true; this._notifyChanged$.next(this); } deactivate() { if (!this._active) { return; } for (const id in this._hash) { if (!this._hash.hasOwnProperty(id)) { continue; } this._hashDeactivated[id] = this._hash[id].tag; } this._hash = {}; this._active = false; } add(tags, transform) { this._assertActivationState(true); for (const tag of tags) { this._add(tag, transform); } this._notifyChanged$.next(this); } addDeactivated(tags) { this._assertActivationState(false); for (const tag of tags) { if (!(tag instanceof OutlineTag || tag instanceof SpotTag || tag instanceof ExtremePointTag)) { throw new Error("Tag type not supported"); } this._hashDeactivated[tag.id] = tag; } } get(id) { return this.has(id) ? this._hash[id] : undefined; } getAll() { const hash = this._hash; return Object.keys(hash) .map((id) => { return hash[id]; }); } getAllDeactivated() { const hashDeactivated = this._hashDeactivated; return Object.keys(hashDeactivated) .map((id) => { return hashDeactivated[id]; }); } getDeactivated(id) { return this.hasDeactivated(id) ? this._hashDeactivated[id] : undefined; } has(id) { return id in this._hash; } hasDeactivated(id) { return id in this._hashDeactivated; } remove(ids) { this._assertActivationState(true); const hash = this._hash; for (const id of ids) { if (!(id in hash)) { continue; } delete hash[id]; } this._notifyChanged$.next(this); } removeAll() { this._assertActivationState(true); this._hash = {}; this._notifyChanged$.next(this); } removeAllDeactivated() { this._assertActivationState(false); this._hashDeactivated = {}; } removeDeactivated(ids) { this._assertActivationState(false); const hashDeactivated = this._hashDeactivated; for (const id of ids) { if (!(id in hashDeactivated)) { continue; } delete hashDeactivated[id]; } } _add(tag, transform) { if (tag instanceof OutlineTag) { this._hash[tag.id] = new OutlineRenderTag(tag, transform); } else if (tag instanceof SpotTag) { this._hash[tag.id] = new SpotRenderTag(tag, transform); } else if (tag instanceof ExtremePointTag) { this._hash[tag.id] = new ExtremePointRenderTag(tag, transform); } else { throw new Error("Tag type not supported"); } } _assertActivationState(should) { if (should !== this._active) { throw new Error("Tag set not in correct state for operation."); } } } /** * @class PointGeometry * * @classdesc Represents a point geometry in the 2D basic image coordinate system. * * @example * ```js * var basicPoint = [0.5, 0.7]; * var pointGeometry = new PointGeometry(basicPoint); * ``` */ class PointGeometry extends Geometry { /** * Create a point geometry. * * @constructor * @param {Array} point - An array representing the basic coordinates of * the point. * * @throws {GeometryTagError} Point coordinates must be valid basic coordinates. */ constructor(point) { super(); let x = point[0]; let y = point[1]; if (x < 0 || x > 1 || y < 0 || y > 1) { throw new GeometryTagError("Basic coordinates must be on the interval [0, 1]."); } this._point = point.slice(); } /** * Get point property. * @returns {Array} Array representing the basic coordinates of the point. */ get point() { return this._point; } /** * Get the 2D basic coordinates for the centroid of the point, i.e. the 2D * basic coordinates of the point itself. * * @returns {Array} 2D basic coordinates representing the centroid. * @ignore */ getCentroid2d() { return this._point.slice(); } /** * Get the 3D world coordinates for the centroid of the point, i.e. the 3D * world coordinates of the point itself. * * @param {Transform} transform - The transform of the image related to the point. * @returns {Array} 3D world coordinates representing the centroid. * @ignore */ getCentroid3d(transform) { return transform.unprojectBasic(this._point, 200); } /** * Set the centroid of the point, i.e. the point coordinates. * * @param {Array} value - The new value of the centroid. * @param {Transform} transform - The transform of the image related to the point. * @ignore */ setCentroid2d(value, transform) { let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; this._point[0] = changed[0]; this._point[1] = changed[1]; this._notifyChanged$.next(this); } } class TagHandlerBase extends HandlerBase { constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._name = `${this._component.name}-${this._getNameExtension()}`; this._viewportCoords = viewportCoords; } _getConfiguration(enable) { return {}; } _mouseEventToBasic(event, element, camera, transform, offsetX, offsetY) { offsetX = offsetX != null ? offsetX : 0; offsetY = offsetY != null ? offsetY : 0; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const basic = this._viewportCoords.canvasToBasic(canvasX - offsetX, canvasY - offsetY, element, transform, camera.perspective); return basic; } } class CreateHandlerBase extends TagHandlerBase { constructor(component, container, navigator, viewportCoords, tagCreator) { super(component, container, navigator, viewportCoords); this._tagCreator = tagCreator; this._geometryCreated$ = new Subject(); } get geometryCreated$() { return this._geometryCreated$; } _enable() { this._enableCreate(); this._container.container.classList.add("component-tag-create"); } _disable() { this._container.container.classList.remove("component-tag-create"); this._disableCreate(); } _validateBasic(basic) { const x = basic[0]; const y = basic[1]; return 0 <= x && x <= 1 && 0 <= y && y <= 1; } _mouseEventToBasic$(mouseEvent$) { return mouseEvent$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$), map(([event, camera, transform]) => { return this._mouseEventToBasic(event, this._container.container, camera, transform); })); } } class CreatePointHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.deferPixels(this._name, 4); this._geometryCreatedSubscription = this._mouseEventToBasic$(this._container.mouseService.proximateClick$).pipe(filter(this._validateBasic), map((basic) => { return new PointGeometry(basic); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.undeferPixels(this._name); this._geometryCreatedSubscription.unsubscribe(); } _getNameExtension() { return "create-point"; } } class CreateVertexHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.deferPixels(this._name, 4); const transformChanged$ = this._navigator.stateService.currentTransform$.pipe(map(() => { }), publishReplay(1), refCount()); this._deleteSubscription = transformChanged$.pipe(skip(1)) .subscribe(this._tagCreator.delete$); const basicClick$ = this._mouseEventToBasic$(this._container.mouseService.proximateClick$).pipe(share()); this._createSubscription = transformChanged$.pipe(switchMap(() => { return basicClick$.pipe(filter(this._validateBasic), take(1)); })) .subscribe(this._create$); this._setVertexSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), merge(this._container.mouseService.mouseMove$, this._container.mouseService.domMouseMove$), this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$) : empty(); })) .subscribe(([tag, event, camera, transform]) => { const basicPoint = this._mouseEventToBasic(event, this._container.container, camera, transform); this._setVertex2d(tag, basicPoint, transform); }); this._addPointSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicClick$) : empty(); })) .subscribe(([tag, basicPoint]) => { this._addPoint(tag, basicPoint); }); this._geometryCreateSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? tag.created$.pipe(map((t) => { return t.geometry; })) : empty(); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.undeferPixels(this._name); this._tagCreator.delete$.next(null); this._addPointSubscription.unsubscribe(); this._createSubscription.unsubscribe(); this._deleteSubscription.unsubscribe(); this._geometryCreateSubscription.unsubscribe(); this._setVertexSubscription.unsubscribe(); } } class CreatePointsHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createPoints$; } _addPoint(tag, basicPoint) { tag.geometry.addPoint2d(basicPoint); } _getNameExtension() { return "create-points"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setPoint2d((tag.geometry).points.length - 1, basicPoint, transform); } } class CreatePolygonHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createPolygon$; } _addPoint(tag, basicPoint) { tag.addPoint(basicPoint); } _getNameExtension() { return "create-polygon"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setVertex2d(tag.geometry.polygon.length - 2, basicPoint, transform); } } class CreateRectHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createRect$; } _addPoint(tag, basicPoint) { const rectGeometry = tag.geometry; if (!rectGeometry.validate(basicPoint)) { basicPoint = rectGeometry.getNonAdjustedVertex2d(3); } tag.addPoint(basicPoint); } _enable() { super._enable(); this._initializeAnchorIndexingSubscription = this._tagCreator.tag$.pipe(filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.geometry.initializeAnchorIndexing(); }); } _disable() { super._disable(); this._initializeAnchorIndexingSubscription.unsubscribe(); } _getNameExtension() { return "create-rect"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setOppositeVertex2d(basicPoint, transform); } } class CreateRectDragHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.claimMouse(this._name, 2); this._deleteSubscription = this._navigator.stateService.currentTransform$.pipe(map((transform) => { return null; }), skip(1)) .subscribe(this._tagCreator.delete$); this._createSubscription = this._mouseEventToBasic$(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseDragStart$)).pipe(filter(this._validateBasic)) .subscribe(this._tagCreator.createRect$); this._initializeAnchorIndexingSubscription = this._tagCreator.tag$.pipe(filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.geometry.initializeAnchorIndexing(); }); const basicMouse$ = combineLatest(merge(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseMove$), this._container.mouseService.filtered$(this._name, this._container.mouseService.domMouseMove$)), this._container.renderService.renderCamera$).pipe(withLatestFrom(this._navigator.stateService.currentTransform$), map(([[event, camera], transform]) => { return this._mouseEventToBasic(event, this._container.container, camera, transform); })); this._setVertexSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicMouse$, this._navigator.stateService.currentTransform$) : empty(); })) .subscribe(([tag, basicPoint, transform]) => { tag.geometry.setOppositeVertex2d(basicPoint, transform); }); const basicMouseDragEnd$ = this._container.mouseService.mouseDragEnd$.pipe(withLatestFrom(this._mouseEventToBasic$(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseDrag$)).pipe(filter(this._validateBasic)), (event, basicPoint) => { return basicPoint; }), share()); this._addPointSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicMouseDragEnd$) : empty(); })) .subscribe(([tag, basicPoint]) => { const rectGeometry = tag.geometry; if (!rectGeometry.validate(basicPoint)) { basicPoint = rectGeometry.getNonAdjustedVertex2d(3); } tag.addPoint(basicPoint); }); this._geometryCreatedSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? tag.created$.pipe(map((t) => { return t.geometry; })) : empty(); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.unclaimMouse(this._name); this._tagCreator.delete$.next(null); this._addPointSubscription.unsubscribe(); this._createSubscription.unsubscribe(); this._deleteSubscription.unsubscribe(); this._geometryCreatedSubscription.unsubscribe(); this._initializeAnchorIndexingSubscription.unsubscribe(); this._setVertexSubscription.unsubscribe(); } _getNameExtension() { return "create-rect-drag"; } } class EditVertexHandler extends TagHandlerBase { constructor(component, container, navigator, viewportCoords, tagSet) { super(component, container, navigator, viewportCoords); this._tagSet = tagSet; } _enable() { const interaction$ = this._tagSet.changed$.pipe(map((tagSet) => { return tagSet.getAll(); }), switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return tag.interact$; })); }), switchMap((interaction) => { return concat(of(interaction), this._container.mouseService.documentMouseUp$.pipe(map(() => { return { offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: null }; }), first())); }), share()); merge(this._container.mouseService.mouseMove$, this._container.mouseService.domMouseMove$).pipe(share()); this._claimMouseSubscription = interaction$.pipe(switchMap((interaction) => { return !!interaction.tag ? this._container.mouseService.domMouseDragStart$ : empty(); })) .subscribe(() => { this._container.mouseService.claimMouse(this._name, 3); }); this._cursorSubscription = interaction$.pipe(map((interaction) => { return interaction.cursor; }), distinctUntilChanged()) .subscribe((cursor) => { const interactionCursors = ["crosshair", "move", "nesw-resize", "nwse-resize"]; for (const interactionCursor of interactionCursors) { this._container.container.classList.remove(`component-tag-edit-${interactionCursor}`); } if (!!cursor) { this._container.container.classList.add(`component-tag-edit-${cursor}`); } }); this._unclaimMouseSubscription = this._container.mouseService .filtered$(this._name, this._container.mouseService.domMouseDragEnd$) .subscribe((e) => { this._container.mouseService.unclaimMouse(this._name); }); this._preventDefaultSubscription = interaction$.pipe(switchMap((interaction) => { return !!interaction.tag ? this._container.mouseService.documentMouseMove$ : empty(); })) .subscribe((event) => { event.preventDefault(); // prevent selection of content outside the viewer }); this._updateGeometrySubscription = interaction$.pipe(switchMap((interaction) => { if (interaction.operation === TagOperation.None || !interaction.tag) { return empty(); } const mouseDrag$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.domMouseDrag$).pipe(filter((event) => { return this._viewportCoords.insideElement(event, this._container.container); })); return combineLatest(mouseDrag$, this._container.renderService.renderCamera$).pipe(withLatestFrom(of(interaction), this._navigator.stateService.currentTransform$, ([event, render], i, transform) => { return [event, render, i, transform]; })); })) .subscribe(([mouseEvent, renderCamera, interaction, transform]) => { const basic = this._mouseEventToBasic(mouseEvent, this._container.container, renderCamera, transform, interaction.offsetX, interaction.offsetY); const geometry = interaction.tag.geometry; if (interaction.operation === TagOperation.Centroid) { geometry.setCentroid2d(basic, transform); } else if (interaction.operation === TagOperation.Vertex) { geometry.setVertex2d(interaction.vertexIndex, basic, transform); } }); } _disable() { this._claimMouseSubscription.unsubscribe(); this._cursorSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._unclaimMouseSubscription.unsubscribe(); this._updateGeometrySubscription.unsubscribe(); } _getNameExtension() { return "edit-vertex"; } } /** * @class TagComponent * * @classdesc Component for showing and editing tags with different * geometries composed from 2D basic image coordinates (see the * {@link Viewer} class documentation for more information about coordinate * systems). * * The `add` method is used for adding new tags or replacing * tags already in the set. Tags are removed by id. * * If a tag already in the set has the same * id as one of the tags added, the old tag will be removed and * the added tag will take its place. * * The tag component mode can be set to either be non interactive or * to be in creating mode of a certain geometry type. * * The tag properties can be updated at any time and the change will * be visibile immediately. * * Tags are only relevant to a single image because they are based on * 2D basic image coordinates. Tags related to a certain image should * be removed when the viewer is moved to another image. * * To retrive and use the tag component * * @example * ```js * var viewer = new Viewer({ component: { tag: true } }, ...); * * var tagComponent = viewer.getComponent("tag"); * ``` */ class TagComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._tagDomRenderer = new TagDOMRenderer(); this._tagScene = new TagScene(); this._tagSet = new TagSet(); this._tagCreator = new TagCreator(this, navigator); this._viewportCoords = new ViewportCoords(); this._createHandlers = { "CreatePoint": new CreatePointHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreatePoints": new CreatePointsHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreatePolygon": new CreatePolygonHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreateRect": new CreateRectHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreateRectDrag": new CreateRectDragHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "Default": undefined, }; this._editVertexHandler = new EditVertexHandler(this, container, navigator, this._viewportCoords, this._tagSet); this._renderTags$ = this._tagSet.changed$.pipe(map((tagSet) => { const tags = tagSet.getAll(); // ensure that tags are always rendered in the same order // to avoid hover tracking problems on first resize. tags.sort((t1, t2) => { const id1 = t1.tag.id; const id2 = t2.tag.id; if (id1 < id2) { return -1; } if (id1 > id2) { return 1; } return 0; }); return tags; }), share()); this._tagChanged$ = this._renderTags$.pipe(switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return merge(tag.tag.changed$, tag.tag.geometryChanged$); })); }), share()); this._renderTagGLChanged$ = this._renderTags$.pipe(switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return tag.glObjectsChanged$; })); }), share()); this._createGeometryChanged$ = this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.geometryChanged$ : empty(); }), share()); this._createGLObjectsChanged$ = this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.glObjectsChanged$ : empty(); }), share()); this._creatingConfiguration$ = this._configuration$.pipe(distinctUntilChanged((c1, c2) => { return c1.mode === c2.mode; }, (configuration) => { return { createColor: configuration.createColor, mode: configuration.mode, }; }), publishReplay(1), refCount()); this._creatingConfiguration$ .subscribe((configuration) => { const type = "tagmode"; const event = { mode: configuration.mode, target: this, type, }; this.fire(type, event); }); } /** * Add tags to the tag set or replace tags in the tag set. * * @description If a tag already in the set has the same * id as one of the tags added, the old tag will be removed * the added tag will take its place. * * @param {Array} tags - Tags to add. * * @example * ```js * tagComponent.add([tag1, tag2]); * ``` */ add(tags) { if (this._activated) { this._navigator.stateService.currentTransform$.pipe(first()) .subscribe((transform) => { this._tagSet.add(tags, transform); const renderTags = tags .map((tag) => { return this._tagSet.get(tag.id); }); this._tagScene.add(renderTags); }); } else { this._tagSet.addDeactivated(tags); } } /** * Calculate the smallest rectangle containing all the points * in the points geometry. * * @description The result may be different depending on if the * current image is an spherical or not. If the * current image is an spherical the rectangle may * wrap the horizontal border of the image. * * @returns {Promise>} Promise to the rectangle * on the format specified for the {@link RectGeometry} in basic * coordinates. */ calculateRect(geometry) { return new Promise((resolve, reject) => { this._navigator.stateService.currentTransform$.pipe(first(), map((transform) => { return geometry.getRect2d(transform); })) .subscribe((rect) => { resolve(rect); }, (error) => { reject(error); }); }); } /** * Force the creation of a geometry programatically using its * current vertices. * * @description The method only has an effect when the tag * mode is either of the following modes: * * {@link TagMode.CreatePoints} * {@link TagMode.CreatePolygon} * {@link TagMode.CreateRect} * {@link TagMode.CreateRectDrag} * * In the case of points or polygon creation, only the created * vertices are used, i.e. the mouse position is disregarded. * * In the case of rectangle creation the position of the mouse * at the time of the method call is used as one of the vertices * defining the rectangle. * * @fires geometrycreate * * @example * ```js * tagComponent.on("geometrycreate", function(geometry) { * console.log(geometry); * }); * * tagComponent.create(); * ``` */ create() { this._tagCreator.replayedTag$.pipe(first(), filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.create(); }); } /** * Change the current tag mode. * * @description Change the tag mode to one of the create modes for creating new geometries. * * @param {TagMode} mode - New tag mode. * * @fires tagmode * * @example * ```js * tagComponent.changeMode(TagMode.CreateRect); * ``` */ changeMode(mode) { this.configure({ mode: mode }); } fire(type, event) { super.fire(type, event); } /** * Returns the tag in the tag set with the specified id, or * undefined if the id matches no tag. * * @param {string} tagId - Id of the tag. * * @example * ```js * var tag = tagComponent.get("tagId"); * ``` */ get(tagId) { if (this._activated) { const renderTag = this._tagSet.get(tagId); return renderTag !== undefined ? renderTag.tag : undefined; } else { return this._tagSet.getDeactivated(tagId); } } /** * Returns an array of all tags. * * @example * ```js * var tags = tagComponent.getAll(); * ``` */ getAll() { if (this.activated) { return this._tagSet .getAll() .map((renderTag) => { return renderTag.tag; }); } else { return this._tagSet.getAllDeactivated(); } } /** * Returns an array of tag ids for tags that contain the specified point. * * @description The pixel point must lie inside the polygon or rectangle * of an added tag for the tag id to be returned. Tag ids for * tags that do not have a fill will also be returned if the point is inside * the geometry of the tag. Tags with point geometries can not be retrieved. * * No tag ids will be returned for polygons rendered in cropped spherical or * rectangles rendered in spherical. * * Notice that the pixelPoint argument requires x, y coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the tag component. * * If no tag at exist the pixel point, an empty array will be returned. * * @param {Array} pixelPoint - Pixel coordinates on the viewer element. * @returns {Promise>} Promise to the ids of the tags that * contain the specified pixel point. * * @example * ```js * tagComponent.getTagIdsAt([100, 100]) * .then((tagIds) => { console.log(tagIds); }); * ``` */ getTagIdsAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const ids = this._tagScene.intersectObjects(viewport, render.perspective); return ids; })) .subscribe((ids) => { resolve(ids); }, (error) => { reject(error); }); }); } /** * Check if a tag exist in the tag set. * * @param {string} tagId - Id of the tag. * * @example * ```js * var tagExists = tagComponent.has("tagId"); * ``` */ has(tagId) { return this._activated ? this._tagSet.has(tagId) : this._tagSet.hasDeactivated(tagId); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Remove tags with the specified ids from the tag set. * * @param {Array} tagIds - Ids for tags to remove. * * @example * ```js * tagComponent.remove(["id-1", "id-2"]); * ``` */ remove(tagIds) { if (this._activated) { this._tagSet.remove(tagIds); this._tagScene.remove(tagIds); } else { this._tagSet.removeDeactivated(tagIds); } } /** * Remove all tags from the tag set. * * @example * ```js * tagComponent.removeAll(); * ``` */ removeAll() { if (this._activated) { this._tagSet.removeAll(); this._tagScene.removeAll(); } else { this._tagSet.removeAllDeactivated(); } } _activate() { this._editVertexHandler.enable(); const handlerGeometryCreated$ = from(Object.keys(this._createHandlers)).pipe(map((key) => { return this._createHandlers[key]; }), filter((handler) => { return !!handler; }), mergeMap((handler) => { return handler.geometryCreated$; }), share()); const subs = this._subscriptions; subs.push(handlerGeometryCreated$ .subscribe((geometry) => { const type = "geometrycreate"; const event = { geometry, target: this, type, }; this.fire(type, event); })); subs.push(this._tagCreator.tag$.pipe(skipWhile((tag) => { return tag == null; }), distinctUntilChanged()) .subscribe((tag) => { const type = tag != null ? "tagcreatestart" : "tagcreateend"; const event = { target: this, type, }; this.fire(type, event); })); subs.push(handlerGeometryCreated$ .subscribe(() => { this.changeMode(exports.TagMode.Default); })); subs.push(this._creatingConfiguration$ .subscribe((configuration) => { this._disableCreateHandlers(); const mode = exports.TagMode[configuration.mode]; const handler = this._createHandlers[mode]; if (!!handler) { handler.enable(); } })); subs.push(this._renderTags$ .subscribe(() => { const type = "tags"; const event = { target: this, type, }; this.fire(type, event); })); subs.push(this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.aborted$.pipe(map(() => { return null; })) : empty(); })) .subscribe(() => { this.changeMode(exports.TagMode.Default); })); subs.push(this._tagCreator.tag$ .subscribe((tag) => { if (this._tagScene.hasCreateTag()) { this._tagScene.removeCreateTag(); } if (tag != null) { this._tagScene.addCreateTag(tag); } })); subs.push(this._createGLObjectsChanged$ .subscribe((tag) => { this._tagScene.updateCreateTagObjects(tag); })); subs.push(this._renderTagGLChanged$ .subscribe((tag) => { this._tagScene.updateObjects(tag); })); subs.push(this._tagChanged$ .subscribe(() => { this._tagScene.update(); })); subs.push(combineLatest(this._renderTags$.pipe(startWith([]), tap(() => { this._container.domRenderer.render$.next({ name: this._name, vNode: this._tagDomRenderer.clear(), }); })), this._container.renderService.renderCamera$, this._container.spriteService.spriteAtlas$, this._container.renderService.size$, this._tagChanged$.pipe(startWith(null)), merge(this._tagCreator.tag$, this._createGeometryChanged$).pipe(startWith(null))).pipe(map(([renderTags, rc, atlas, size, , ct]) => { return { name: this._name, vNode: this._tagDomRenderer.render(renderTags, ct, atlas, rc.perspective, size), }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._navigator.stateService.currentState$.pipe(map((frame) => { const tagScene = this._tagScene; return { name: this._name, renderer: { frameId: frame.id, needsRender: tagScene.needsRender, render: tagScene.render.bind(tagScene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); this._navigator.stateService.currentTransform$.pipe(first()) .subscribe((transform) => { this._tagSet.activate(transform); this._tagScene.add(this._tagSet.getAll()); }); } _deactivate() { this._editVertexHandler.disable(); this._disableCreateHandlers(); this._tagScene.clear(); this._tagSet.deactivate(); this._tagCreator.delete$.next(null); this._subscriptions.unsubscribe(); this._container.container.classList.remove("component-tag-create"); } _getDefaultConfiguration() { return { createColor: 0xFFFFFF, indicatePointsCompleter: true, mode: exports.TagMode.Default, }; } _disableCreateHandlers() { const createHandlers = this._createHandlers; for (const key in createHandlers) { if (!createHandlers.hasOwnProperty(key)) { continue; } const handler = createHandlers[key]; if (!!handler) { handler.disable(); } } } } /** @inheritdoc */ TagComponent.componentName = "tag"; /** * @class ZoomComponent * * @classdesc Component rendering UI elements used for zooming. * * @example * ```js * var viewer = new Viewer({ ... }); * * var zoomComponent = viewer.getComponent("zoom"); * zoomComponent.configure({ size: ComponentSize.Small }); * ``` */ class ZoomComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); this._viewportCoords = new ViewportCoords(); this._zoomDelta$ = new Subject(); } _activate() { const subs = this._subscriptions; subs.push(combineLatest(this._navigator.stateService.currentState$, this._navigator.stateService.state$, this._configuration$, this._container.renderService.size$).pipe(map(([frame, state, configuration, size]) => { const zoom = frame.state.zoom; const zoomInIcon = virtualDom.h("div.mapillary-zoom-in-icon", []); const zoomInButton = zoom >= 3 || state === State.Waiting ? virtualDom.h("div.mapillary-zoom-in-button-inactive", [zoomInIcon]) : virtualDom.h("div.mapillary-zoom-in-button", { onclick: () => { this._zoomDelta$.next(1); } }, [zoomInIcon]); const zoomOutIcon = virtualDom.h("div.mapillary-zoom-out-icon", []); const zoomOutButton = zoom <= 0 || state === State.Waiting ? virtualDom.h("div.mapillary-zoom-out-button-inactive", [zoomOutIcon]) : virtualDom.h("div.mapillary-zoom-out-button", { onclick: () => { this._zoomDelta$.next(-1); } }, [zoomOutIcon]); const compact = configuration.size === exports.ComponentSize.Small || configuration.size === exports.ComponentSize.Automatic && size.width < 640 ? ".mapillary-zoom-compact" : ""; return { name: this._name, vNode: virtualDom.h("div.mapillary-zoom-container" + compact, { oncontextmenu: (event) => { event.preventDefault(); } }, [zoomInButton, zoomOutButton]), }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._zoomDelta$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([zoomDelta, render, transform]) => { const unprojected = this._viewportCoords.unprojectFromViewport(0, 0, render.perspective); const reference = transform.projectBasic(unprojected.toArray()); this._navigator.stateService.zoomIn(zoomDelta, reference); })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { size: exports.ComponentSize.Automatic }; } } ZoomComponent.componentName = "zoom"; class ImageFallbackComponent extends Component { constructor(name, container, navigator, dom) { super(name, container, navigator); this._canvasId = `${container.id}-${this._name}`; this._dom = !!dom ? dom : new DOM(); } _activate() { const canvasSize$ = this._container.domRenderer.element$.pipe(map(() => { return this._dom.document.getElementById(this._canvasId); }), filter((canvas) => { return !!canvas; }), map((canvas) => { const adaptableDomRenderer = canvas.parentElement; const width = adaptableDomRenderer.offsetWidth; const height = adaptableDomRenderer.offsetHeight; return [canvas, { height: height, width: width }]; }), distinctUntilChanged((s1, s2) => { return s1.height === s2.height && s1.width === s2.width; }, ([, size]) => { return size; })); this._subscriptions.push(combineLatest(canvasSize$, this._navigator.stateService.currentImage$) .subscribe(([[canvas, size], image]) => { canvas.width = size.width; canvas.height = size.height; canvas .getContext("2d") .drawImage(image.image, 0, 0, size.width, size.height); })); this._container.domRenderer.renderAdaptive$.next({ name: this._name, vNode: virtualDom.h(`canvas#${this._canvasId}`, []) }); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } } ImageFallbackComponent.componentName = "imagefallback"; /** * @class NavigationFallbackComponent * * @classdesc Fallback navigation component for environments without WebGL support. * * Replaces the functionality in the Direction and Sequence components. */ class NavigationFallbackComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._seqNames = {}; this._seqNames[exports.NavigationDirection[exports.NavigationDirection.Prev]] = "-prev"; this._seqNames[exports.NavigationDirection[exports.NavigationDirection.Next]] = "-next"; this._spaTopNames = {}; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.TurnLeft]] = "-turn-left"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepLeft]] = "-left"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepForward]] = "-forward"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepRight]] = "-right"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.TurnRight]] = "-turn-right"; this._spaBottomNames = {}; this._spaBottomNames[exports.NavigationDirection[exports.NavigationDirection.TurnU]] = "-turn-around"; this._spaBottomNames[exports.NavigationDirection[exports.NavigationDirection.StepBackward]] = "-backward"; } _activate() { this._subscriptions.push(combineLatest(this._navigator.stateService.currentImage$, this._configuration$).pipe(switchMap(([image, configuration]) => { const sequenceEdges$ = configuration.sequence ? image.sequenceEdges$.pipe(map((status) => { return status.edges .map((edge) => { return edge.data.direction; }); })) : of([]); const spatialEdges$ = !isSpherical(image.cameraType) && configuration.spatial ? image.spatialEdges$.pipe(map((status) => { return status.edges .map((edge) => { return edge.data.direction; }); })) : of([]); return combineLatest(sequenceEdges$, spatialEdges$).pipe(map(([seq, spa]) => { return seq.concat(spa); })); }), map((edgeDirections) => { const seqs = this._createArrowRow(this._seqNames, edgeDirections); const spaTops = this._createArrowRow(this._spaTopNames, edgeDirections); const spaBottoms = this._createArrowRow(this._spaBottomNames, edgeDirections); const seqContainer = virtualDom.h(`div.mapillary-navigation-sequence`, seqs); const spaTopContainer = virtualDom.h(`div.NavigationSpatialTop`, spaTops); const spaBottomContainer = virtualDom.h(`div.mapillary-navigation-spatial-bottom`, spaBottoms); const spaContainer = virtualDom.h(`div.mapillary-navigation-spatial`, [spaTopContainer, spaBottomContainer]); return { name: this._name, vNode: virtualDom.h(`div.NavigationContainer`, [seqContainer, spaContainer]) }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { sequence: true, spatial: true }; } _createArrowRow(arrowNames, edgeDirections) { const arrows = []; for (const arrowName in arrowNames) { if (!(arrowNames.hasOwnProperty(arrowName))) { continue; } const direction = exports.NavigationDirection[arrowName]; if (edgeDirections.indexOf(direction) !== -1) { arrows.push(this._createVNode(direction, arrowNames[arrowName], "visible")); } else { arrows.push(this._createVNode(direction, arrowNames[arrowName], "hidden")); } } return arrows; } _createVNode(direction, name, visibility) { return virtualDom.h(`span.mapillary-navigation-button.mapillary-navigation${name}`, { onclick: () => { this._navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }, style: { visibility: visibility, }, }, []); } } NavigationFallbackComponent.componentName = "navigationfallback"; /*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */ // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. /* eslint-disable space-unary-ops */ /* Public constants ==========================================================*/ /* ===========================================================================*/ //const Z_FILTERED = 1; //const Z_HUFFMAN_ONLY = 2; //const Z_RLE = 3; const Z_FIXED$1 = 4; //const Z_DEFAULT_STRATEGY = 0; /* Possible values of the data_type field (though see inflate()) */ const Z_BINARY = 0; const Z_TEXT = 1; //const Z_ASCII = 1; // = Z_TEXT const Z_UNKNOWN$1 = 2; /*============================================================================*/ function zero$1(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } // From zutil.h const STORED_BLOCK = 0; const STATIC_TREES = 1; const DYN_TREES = 2; /* The three kinds of block type */ const MIN_MATCH$1 = 3; const MAX_MATCH$1 = 258; /* The minimum and maximum match lengths */ // From deflate.h /* =========================================================================== * Internal compression state. */ const LENGTH_CODES$1 = 29; /* number of length codes, not counting the special END_BLOCK code */ const LITERALS$1 = 256; /* number of literal bytes 0..255 */ const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; /* number of Literal or Length codes, including the END_BLOCK code */ const D_CODES$1 = 30; /* number of distance codes */ const BL_CODES$1 = 19; /* number of codes used to transfer the bit lengths */ const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; /* maximum heap size */ const MAX_BITS$1 = 15; /* All codes must not exceed MAX_BITS bits */ const Buf_size = 16; /* size of bit buffer in bi_buf */ /* =========================================================================== * Constants */ const MAX_BL_BITS = 7; /* Bit length codes must not exceed MAX_BL_BITS bits */ const END_BLOCK = 256; /* end of block literal code */ const REP_3_6 = 16; /* repeat previous bit length 3-6 times (2 bits of repeat count) */ const REPZ_3_10 = 17; /* repeat a zero length 3-10 times (3 bits of repeat count) */ const REPZ_11_138 = 18; /* repeat a zero length 11-138 times (7 bits of repeat count) */ /* eslint-disable comma-spacing,array-bracket-spacing */ const extra_lbits = /* extra bits for each length code */ new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]); const extra_dbits = /* extra bits for each distance code */ new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]); const extra_blbits = /* extra bits for each bit length code */ new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]); const bl_order = new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]); /* eslint-enable comma-spacing,array-bracket-spacing */ /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ // We pre-fill arrays with 0 to avoid uninitialized gaps const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 const static_ltree = new Array((L_CODES$1 + 2) * 2); zero$1(static_ltree); /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ const static_dtree = new Array(D_CODES$1 * 2); zero$1(static_dtree); /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ const _dist_code = new Array(DIST_CODE_LEN); zero$1(_dist_code); /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1); zero$1(_length_code); /* length code for each normalized match length (0 == MIN_MATCH) */ const base_length = new Array(LENGTH_CODES$1); zero$1(base_length); /* First normalized length for each code (0 = MIN_MATCH) */ const base_dist = new Array(D_CODES$1); zero$1(base_dist); /* First normalized distance for each code (0 = distance of 1) */ function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { this.static_tree = static_tree; /* static tree or NULL */ this.extra_bits = extra_bits; /* extra bits for each code or NULL */ this.extra_base = extra_base; /* base index for extra_bits */ this.elems = elems; /* max number of elements in the tree */ this.max_length = max_length; /* max bit length for the codes */ // show if `static_tree` has data or dummy - needed for monomorphic objects this.has_stree = static_tree && static_tree.length; } let static_l_desc; let static_d_desc; let static_bl_desc; function TreeDesc(dyn_tree, stat_desc) { this.dyn_tree = dyn_tree; /* the dynamic tree */ this.max_code = 0; /* largest code with non zero frequency */ this.stat_desc = stat_desc; /* the corresponding static tree */ } const d_code = (dist) => { return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; }; /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ const put_short = (s, w) => { // put_byte(s, (uch)((w) & 0xff)); // put_byte(s, (uch)((ush)(w) >> 8)); s.pending_buf[s.pending++] = (w) & 0xff; s.pending_buf[s.pending++] = (w >>> 8) & 0xff; }; /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ const send_bits = (s, value, length) => { if (s.bi_valid > (Buf_size - length)) { s.bi_buf |= (value << s.bi_valid) & 0xffff; put_short(s, s.bi_buf); s.bi_buf = value >> (Buf_size - s.bi_valid); s.bi_valid += length - Buf_size; } else { s.bi_buf |= (value << s.bi_valid) & 0xffff; s.bi_valid += length; } }; const send_code = (s, c, tree) => { send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); }; /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ const bi_reverse = (code, len) => { let res = 0; do { res |= code & 1; code >>>= 1; res <<= 1; } while (--len > 0); return res >>> 1; }; /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ const bi_flush = (s) => { if (s.bi_valid === 16) { put_short(s, s.bi_buf); s.bi_buf = 0; s.bi_valid = 0; } else if (s.bi_valid >= 8) { s.pending_buf[s.pending++] = s.bi_buf & 0xff; s.bi_buf >>= 8; s.bi_valid -= 8; } }; /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ const gen_bitlen = (s, desc) => { // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ const tree = desc.dyn_tree; const max_code = desc.max_code; const stree = desc.stat_desc.static_tree; const has_stree = desc.stat_desc.has_stree; const extra = desc.stat_desc.extra_bits; const base = desc.stat_desc.extra_base; const max_length = desc.stat_desc.max_length; let h; /* heap index */ let n, m; /* iterate over the tree elements */ let bits; /* bit length */ let xbits; /* extra bits */ let f; /* frequency */ let overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS$1; bits++) { s.bl_count[bits] = 0; } /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) { n = s.heap[h]; bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; if (bits > max_length) { bits = max_length; overflow++; } tree[n * 2 + 1]/*.Len*/ = bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) { continue; } /* not a leaf node */ s.bl_count[bits]++; xbits = 0; if (n >= base) { xbits = extra[n - base]; } f = tree[n * 2]/*.Freq*/; s.opt_len += f * (bits + xbits); if (has_stree) { s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); } } if (overflow === 0) { return; } // Tracev((stderr," bit length overflow ")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length - 1; while (s.bl_count[bits] === 0) { bits--; } s.bl_count[bits]--; /* move one leaf down the tree */ s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ s.bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from "ar" written by Haruhiko Okumura.) */ for (bits = max_length; bits !== 0; bits--) { n = s.bl_count[bits]; while (n !== 0) { m = s.heap[--h]; if (m > max_code) { continue; } if (tree[m * 2 + 1]/*.Len*/ !== bits) { // Tracev((stderr,"code %d bits %d->%d ", m, tree[m].Len, bits)); s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; tree[m * 2 + 1]/*.Len*/ = bits; } n--; } } }; /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ const gen_codes = (tree, max_code, bl_count) => { // ct_data *tree; /* the tree to decorate */ // int max_code; /* largest code with non zero frequency */ // ushf *bl_count; /* number of codes at each bit length */ const next_code = new Array(MAX_BITS$1 + 1); /* next code value for each bit length */ let code = 0; /* running code value */ let bits; /* bit index */ let n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS$1; bits++) { code = (code + bl_count[bits - 1]) << 1; next_code[bits] = code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ //Assert (code + bl_count[MAX_BITS]-1 == (1< { let n; /* iterates over tree elements */ let bits; /* bit counter */ let length; /* length value */ let code; /* code value */ let dist; /* distance index */ const bl_count = new Array(MAX_BITS$1 + 1); /* number of codes at each bit length for an optimal tree */ // do check in _tr_init() //if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ /*#ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif*/ /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES$1 - 1; code++) { base_length[code] = length; for (n = 0; n < (1 << extra_lbits[code]); n++) { _length_code[length++] = code; } } //Assert (length == 256, "tr_static_init: length != 256"); /* Note that the length 255 (match length 258) can be represented * in two different ways: code 284 + 5 bits or code 285, so we * overwrite length_code[255] to use the best encoding: */ _length_code[length - 1] = code; /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ dist = 0; for (code = 0; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1 << extra_dbits[code]); n++) { _dist_code[dist++] = code; } } //Assert (dist == 256, "tr_static_init: dist != 256"); dist >>= 7; /* from now on, all distances are divided by 128 */ for (; code < D_CODES$1; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = code; } } //Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS$1; bits++) { bl_count[bits] = 0; } n = 0; while (n <= 143) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } while (n <= 255) { static_ltree[n * 2 + 1]/*.Len*/ = 9; n++; bl_count[9]++; } while (n <= 279) { static_ltree[n * 2 + 1]/*.Len*/ = 7; n++; bl_count[7]++; } while (n <= 287) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes(static_ltree, L_CODES$1 + 1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES$1; n++) { static_dtree[n * 2 + 1]/*.Len*/ = 5; static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); } // Now data ready and we can init static trees static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1); static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1); static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS); //static_init_done = true; }; /* =========================================================================== * Initialize a new block. */ const init_block = (s) => { let n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES$1; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < D_CODES$1; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < BL_CODES$1; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; s.opt_len = s.static_len = 0; s.sym_next = s.matches = 0; }; /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ const bi_windup = (s) => { if (s.bi_valid > 8) { put_short(s, s.bi_buf); } else if (s.bi_valid > 0) { //put_byte(s, (Byte)s->bi_buf); s.pending_buf[s.pending++] = s.bi_buf; } s.bi_buf = 0; s.bi_valid = 0; }; /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ const smaller = (tree, n, m, depth) => { const _n2 = n * 2; const _m2 = m * 2; return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); }; /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ const pqdownheap = (s, tree, k) => { // deflate_state *s; // ct_data *tree; /* the tree to restore */ // int k; /* node to move down */ const v = s.heap[k]; let j = k << 1; /* left son of k */ while (j <= s.heap_len) { /* Set j to the smallest of the two sons: */ if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s.heap[j], s.depth)) { break; } /* Exchange v with the smallest son */ s.heap[k] = s.heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s.heap[k] = v; }; // inlined manually // const SMALLEST = 1; /* =========================================================================== * Send the block data compressed using the given Huffman trees */ const compress_block = (s, ltree, dtree) => { // deflate_state *s; // const ct_data *ltree; /* literal tree */ // const ct_data *dtree; /* distance tree */ let dist; /* distance of matched string */ let lc; /* match length or unmatched char (if dist == 0) */ let sx = 0; /* running index in sym_buf */ let code; /* the code to send */ let extra; /* number of extra bits to send */ if (s.sym_next !== 0) { do { dist = s.pending_buf[s.sym_buf + sx++] & 0xff; dist += (s.pending_buf[s.sym_buf + sx++] & 0xff) << 8; lc = s.pending_buf[s.sym_buf + sx++]; if (dist === 0) { send_code(s, lc, ltree); /* send a literal byte */ //Tracecv(isgraph(lc), (stderr," "%c" ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra !== 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); //Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra !== 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and sym_buf is ok: */ //Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); } while (sx < s.sym_next); } send_code(s, END_BLOCK, ltree); }; /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. * Update the total bit length for the current block. * IN assertion: the field freq is set for all tree elements. * OUT assertions: the fields len and code are set to the optimal bit length * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ const build_tree = (s, desc) => { // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ const tree = desc.dyn_tree; const stree = desc.stat_desc.static_tree; const has_stree = desc.stat_desc.has_stree; const elems = desc.stat_desc.elems; let n, m; /* iterate over heap elements */ let max_code = -1; /* largest code with non zero frequency */ let node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s.heap_len = 0; s.heap_max = HEAP_SIZE$1; for (n = 0; n < elems; n++) { if (tree[n * 2]/*.Freq*/ !== 0) { s.heap[++s.heap_len] = max_code = n; s.depth[n] = 0; } else { tree[n * 2 + 1]/*.Len*/ = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s.heap_len < 2) { node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); tree[node * 2]/*.Freq*/ = 1; s.depth[node] = 0; s.opt_len--; if (has_stree) { s.static_len -= stree[node * 2 + 1]/*.Len*/; } /* node is 0 or 1 so it does not have extra bits */ } desc.max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { //pqremove(s, tree, n); /* n = node of least frequency */ /*** pqremove ***/ n = s.heap[1/*SMALLEST*/]; s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; pqdownheap(s, tree, 1/*SMALLEST*/); /***/ m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ s.heap[--s.heap_max] = m; /* Create a new node father of n and m */ tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; /* and insert the new node in the heap */ s.heap[1/*SMALLEST*/] = node++; pqdownheap(s, tree, 1/*SMALLEST*/); } while (s.heap_len >= 2); s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, desc); /* The field len is now set, we can generate the bit codes */ gen_codes(tree, max_code, s.bl_count); }; /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ const scan_tree = (s, tree, max_code) => { // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ let n; /* iterates over all tree elements */ let prevlen = -1; /* last emitted length */ let curlen; /* length of current code */ let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ let count = 0; /* repeat count of the current code */ let max_count = 7; /* max repeat count */ let min_count = 4; /* min repeat count */ if (nextlen === 0) { max_count = 138; min_count = 3; } tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { s.bl_tree[curlen * 2]/*.Freq*/ += count; } else if (curlen !== 0) { if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } s.bl_tree[REP_3_6 * 2]/*.Freq*/++; } else if (count <= 10) { s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; } else { s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } }; /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ const send_tree = (s, tree, max_code) => { // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ let n; /* iterates over all tree elements */ let prevlen = -1; /* last emitted length */ let curlen; /* length of current code */ let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ let count = 0; /* repeat count of the current code */ let max_count = 7; /* max repeat count */ let min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen === 0) { max_count = 138; min_count = 3; } for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); } else if (curlen !== 0) { if (curlen !== prevlen) { send_code(s, curlen, s.bl_tree); count--; } //Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s.bl_tree); send_bits(s, count - 3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s.bl_tree); send_bits(s, count - 3, 3); } else { send_code(s, REPZ_11_138, s.bl_tree); send_bits(s, count - 11, 7); } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } }; /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ const build_bl_tree = (s) => { let max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, s.dyn_ltree, s.l_desc.max_code); scan_tree(s, s.dyn_dtree, s.d_desc.max_code); /* Build the bit length tree: */ build_tree(s, s.bl_desc); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) { if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { break; } } /* Update opt_len to include the bit length tree and counts */ s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; //Tracev((stderr, " dyn trees: dyn %ld, stat %ld", // s->opt_len, s->static_len)); return max_blindex; }; /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ const send_all_trees = (s, lcodes, dcodes, blcodes) => { // deflate_state *s; // int lcodes, dcodes, blcodes; /* number of codes for each tree */ let rank; /* index in bl_order */ //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, // "too many codes"); //Tracev((stderr, " bl counts: ")); send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes - 1, 5); send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { //Tracev((stderr, " bl code %2d ", bl_order[rank])); send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); } //Tracev((stderr, " bl tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ //Tracev((stderr, " lit tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ //Tracev((stderr, " dist tree: sent %ld", s->bits_sent)); }; /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "block list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ const detect_data_type = (s) => { /* block_mask is the bit mask of block-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ let block_mask = 0xf3ffc07f; let n; /* Check for non-textual ("block-listed") bytes. */ for (n = 0; n <= 31; n++, block_mask >>>= 1) { if ((block_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { return Z_BINARY; } } /* Check for textual ("allow-listed") bytes. */ if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { return Z_TEXT; } for (n = 32; n < LITERALS$1; n++) { if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { return Z_TEXT; } } /* There are no "block-listed" or "allow-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; }; let static_init_done = false; /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ const _tr_init$1 = (s) => { if (!static_init_done) { tr_static_init(); static_init_done = true; } s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); s.bi_buf = 0; s.bi_valid = 0; /* Initialize the first block of the first file: */ init_block(s); }; /* =========================================================================== * Send a stored block */ const _tr_stored_block$1 = (s, buf, stored_len, last) => { //DeflateState *s; //charf *buf; /* input block */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, stored_len); put_short(s, ~stored_len); if (stored_len) { s.pending_buf.set(s.window.subarray(buf, buf + stored_len), s.pending); } s.pending += stored_len; }; /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ const _tr_align$1 = (s) => { send_bits(s, STATIC_TREES << 1, 3); send_code(s, END_BLOCK, static_ltree); bi_flush(s); }; /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ const _tr_flush_block$1 = (s, buf, stored_len, last) => { //DeflateState *s; //charf *buf; /* input block, or NULL if too old */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ let opt_lenb, static_lenb; /* opt_len and static_len in bytes */ let max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s.level > 0) { /* Check if the file is binary or text */ if (s.strm.data_type === Z_UNKNOWN$1) { s.strm.data_type = detect_data_type(s); } /* Construct the literal and distance trees */ build_tree(s, s.l_desc); // Tracev((stderr, " lit data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); build_tree(s, s.d_desc); // Tracev((stderr, " dist data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s.opt_len + 3 + 7) >>> 3; static_lenb = (s.static_len + 3 + 7) >>> 3; // Tracev((stderr, " opt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, // s->sym_next / 3)); if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } } else { // Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { /* 4: two words for the lengths */ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can"t have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block$1(s, buf, stored_len, last); } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) { send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); compress_block(s, static_ltree, static_dtree); } else { send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); compress_block(s, s.dyn_ltree, s.dyn_dtree); } // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); } // Tracev((stderr," comprlen %lu(%lu) ", s->compressed_len>>3, // s->compressed_len-7*last)); }; /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ const _tr_tally$1 = (s, dist, lc) => { // deflate_state *s; // unsigned dist; /* distance of matched string */ // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ s.pending_buf[s.sym_buf + s.sym_next++] = dist; s.pending_buf[s.sym_buf + s.sym_next++] = dist >> 8; s.pending_buf[s.sym_buf + s.sym_next++] = lc; if (dist === 0) { /* lc is the unmatched char */ s.dyn_ltree[lc * 2]/*.Freq*/++; } else { s.matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ //Assert((ush)dist < (ush)MAX_DIST(s) && // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2]/*.Freq*/++; s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; } return (s.sym_next === s.sym_end); }; var _tr_init_1 = _tr_init$1; var _tr_stored_block_1 = _tr_stored_block$1; var _tr_flush_block_1 = _tr_flush_block$1; var _tr_tally_1 = _tr_tally$1; var _tr_align_1 = _tr_align$1; var trees = { _tr_init: _tr_init_1, _tr_stored_block: _tr_stored_block_1, _tr_flush_block: _tr_flush_block_1, _tr_tally: _tr_tally_1, _tr_align: _tr_align_1 }; // Note: adler32 takes 12% for level 0 and 2% for level 6. // It isn"t worth it to make additional optimizations as in original. // Small size is preferable. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const adler32 = (adler, buf, len, pos) => { let s1 = (adler & 0xffff) |0, s2 = ((adler >>> 16) & 0xffff) |0, n = 0; while (len !== 0) { // Set limit ~ twice less than 5552, to keep // s2 in 31-bits, because we force signed ints. // in other case %= will fail. n = len > 2000 ? 2000 : len; len -= n; do { s1 = (s1 + buf[pos++]) |0; s2 = (s2 + s1) |0; } while (--n); s1 %= 65521; s2 %= 65521; } return (s1 | (s2 << 16)) |0; }; var adler32_1 = adler32; // Note: we can"t get significant speed boost here. // So write code to minimize size - no pregenerated tables // and array tools dependencies. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // Use ordinary array, since untyped makes no boost here const makeTable = () => { let c, table = []; for (var n = 0; n < 256; n++) { c = n; for (var k = 0; k < 8; k++) { c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); } table[n] = c; } return table; }; // Create table on load. Just 255 signed longs. Not a problem. const crcTable = new Uint32Array(makeTable()); const crc32 = (crc, buf, len, pos) => { const t = crcTable; const end = pos + len; crc ^= -1; for (let i = pos; i < end; i++) { crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; } return (crc ^ (-1)); // >>> 0; }; var crc32_1 = crc32; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var messages = { 2: "need dictionary", /* Z_NEED_DICT 2 */ 1: "stream end", /* Z_STREAM_END 1 */ 0: "", /* Z_OK 0 */ "-1": "file error", /* Z_ERRNO (-1) */ "-2": "stream error", /* Z_STREAM_ERROR (-2) */ "-3": "data error", /* Z_DATA_ERROR (-3) */ "-4": "insufficient memory", /* Z_MEM_ERROR (-4) */ "-5": "buffer error", /* Z_BUF_ERROR (-5) */ "-6": "incompatible version" /* Z_VERSION_ERROR (-6) */ }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var constants$2 = { /* Allowed flush values; see deflate() and inflate() below for details */ Z_NO_FLUSH: 0, Z_PARTIAL_FLUSH: 1, Z_SYNC_FLUSH: 2, Z_FULL_FLUSH: 3, Z_FINISH: 4, Z_BLOCK: 5, Z_TREES: 6, /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ Z_OK: 0, Z_STREAM_END: 1, Z_NEED_DICT: 2, Z_ERRNO: -1, Z_STREAM_ERROR: -2, Z_DATA_ERROR: -3, Z_MEM_ERROR: -4, Z_BUF_ERROR: -5, //Z_VERSION_ERROR: -6, /* compression levels */ Z_NO_COMPRESSION: 0, Z_BEST_SPEED: 1, Z_BEST_COMPRESSION: 9, Z_DEFAULT_COMPRESSION: -1, Z_FILTERED: 1, Z_HUFFMAN_ONLY: 2, Z_RLE: 3, Z_FIXED: 4, Z_DEFAULT_STRATEGY: 0, /* Possible values of the data_type field (though see inflate()) */ Z_BINARY: 0, Z_TEXT: 1, //Z_ASCII: 1, // = Z_TEXT (deprecated) Z_UNKNOWN: 2, /* The deflate compression method */ Z_DEFLATED: 8 //Z_NULL: null // Use -1 or null inline, depending on var type }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } = trees; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH: Z_NO_FLUSH$2, Z_PARTIAL_FLUSH, Z_FULL_FLUSH: Z_FULL_FLUSH$1, Z_FINISH: Z_FINISH$3, Z_BLOCK: Z_BLOCK$1, Z_OK: Z_OK$3, Z_STREAM_END: Z_STREAM_END$3, Z_STREAM_ERROR: Z_STREAM_ERROR$2, Z_DATA_ERROR: Z_DATA_ERROR$2, Z_BUF_ERROR: Z_BUF_ERROR$1, Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, Z_UNKNOWN, Z_DEFLATED: Z_DEFLATED$2 } = constants$2; /*============================================================================*/ const MAX_MEM_LEVEL = 9; /* Maximum value for memLevel in deflateInit2 */ const MAX_WBITS$1 = 15; /* 32K LZ77 window */ const DEF_MEM_LEVEL = 8; const LENGTH_CODES = 29; /* number of length codes, not counting the special END_BLOCK code */ const LITERALS = 256; /* number of literal bytes 0..255 */ const L_CODES = LITERALS + 1 + LENGTH_CODES; /* number of Literal or Length codes, including the END_BLOCK code */ const D_CODES = 30; /* number of distance codes */ const BL_CODES = 19; /* number of codes used to transfer the bit lengths */ const HEAP_SIZE = 2 * L_CODES + 1; /* maximum heap size */ const MAX_BITS = 15; /* All codes must not exceed MAX_BITS bits */ const MIN_MATCH = 3; const MAX_MATCH = 258; const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); const PRESET_DICT = 0x20; const INIT_STATE = 42; /* zlib header -> BUSY_STATE */ //#ifdef GZIP const GZIP_STATE = 57; /* gzip header -> BUSY_STATE | EXTRA_STATE */ //#endif const EXTRA_STATE = 69; /* gzip extra block -> NAME_STATE */ const NAME_STATE = 73; /* gzip file name -> COMMENT_STATE */ const COMMENT_STATE = 91; /* gzip comment -> HCRC_STATE */ const HCRC_STATE = 103; /* gzip header CRC -> BUSY_STATE */ const BUSY_STATE = 113; /* deflate -> FINISH_STATE */ const FINISH_STATE = 666; /* stream complete */ const BS_NEED_MORE = 1; /* block not completed, need more input or more output */ const BS_BLOCK_DONE = 2; /* block flush performed */ const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ const OS_CODE = 0x03; // Unix :) . Don"t detect, use this default. const err = (strm, errorCode) => { strm.msg = messages[errorCode]; return errorCode; }; const rank = (f) => { return ((f) * 2) - ((f) > 4 ? 9 : 0); }; const zero = (buf) => { let len = buf.length; while (--len >= 0) { buf[len] = 0; } }; /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ const slide_hash = (s) => { let n, m; let p; let wsize = s.w_size; n = s.hash_size; p = n; do { m = s.head[--p]; s.head[p] = (m >= wsize ? m - wsize : 0); } while (--n); n = wsize; //#ifndef FASTEST p = n; do { m = s.prev[--p]; s.prev[p] = (m >= wsize ? m - wsize : 0); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); //#endif }; /* eslint-disable new-cap */ let HASH_ZLIB = (s, prev, data) => ((prev << s.hash_shift) ^ data) & s.hash_mask; // This hash causes less collisions, https://github.com/nodeca/pako/issues/135 // But breaks binary compatibility //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask; let HASH = HASH_ZLIB; /* ========================================================================= * Flush as much pending output as possible. All deflate() output, except for * some deflate_stored() output, goes through this function so some * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ const flush_pending = (strm) => { const s = strm.state; //_tr_flush_bits(s); let len = s.pending; if (len > strm.avail_out) { len = strm.avail_out; } if (len === 0) { return; } strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out); strm.next_out += len; s.pending_out += len; strm.total_out += len; strm.avail_out -= len; s.pending -= len; if (s.pending === 0) { s.pending_out = 0; } }; const flush_block_only = (s, last) => { _tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); s.block_start = s.strstart; flush_pending(s.strm); }; const put_byte = (s, b) => { s.pending_buf[s.pending++] = b; }; /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ const putShortMSB = (s, b) => { // put_byte(s, (Byte)(b >> 8)); // put_byte(s, (Byte)(b & 0xff)); s.pending_buf[s.pending++] = (b >>> 8) & 0xff; s.pending_buf[s.pending++] = b & 0xff; }; /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->input buffer and copying from it. * (See also flush_pending()). */ const read_buf = (strm, buf, start, size) => { let len = strm.avail_in; if (len > size) { len = size; } if (len === 0) { return 0; } strm.avail_in -= len; // zmemcpy(buf, strm->next_in, len); buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start); if (strm.state.wrap === 1) { strm.adler = adler32_1(strm.adler, buf, len, start); } else if (strm.state.wrap === 2) { strm.adler = crc32_1(strm.adler, buf, len, start); } strm.next_in += len; strm.total_in += len; return len; }; /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ const longest_match = (s, cur_match) => { let chain_length = s.max_chain_length; /* max hash chain length */ let scan = s.strstart; /* current string */ let match; /* matched string */ let len; /* length of current match */ let best_len = s.prev_length; /* best match length so far */ let nice_match = s.nice_match; /* stop if match long enough */ const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; const _win = s.window; // shortcut const wmask = s.w_mask; const prev = s.prev; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ const strend = s.strstart + MAX_MATCH; let scan_end1 = _win[scan + best_len - 1]; let scan_end = _win[scan + best_len]; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s.prev_length >= s.good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if (nice_match > s.lookahead) { nice_match = s.lookahead; } // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { // Assert(cur_match < s->strstart, "no future"); match = cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) { continue; } /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2; match++; // Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { /*jshint noempty:false*/ } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend); // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (strend - scan); scan = strend - MAX_MATCH; if (len > best_len) { s.match_start = cur_match; best_len = len; if (len >= nice_match) { break; } scan_end1 = _win[scan + best_len - 1]; scan_end = _win[scan + best_len]; } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); if (best_len <= s.lookahead) { return best_len; } return s.lookahead; }; /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ const fill_window = (s) => { const _w_size = s.w_size; let n, more, str; //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = s.window_size - s.lookahead - s.strstart; // JS ints have 32 bit, block below not needed /* Deal with !@#$% 64K limit: */ //if (sizeof(int) <= 2) { // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { // more = wsize; // // } else if (more == (unsigned)(-1)) { // /* Very unlikely, but possible on 16 bit machine if // * strstart == 0 && lookahead == 1 (input done a byte at time) // */ // more--; // } //} /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { s.window.set(s.window.subarray(_w_size, _w_size + _w_size - more), 0); s.match_start -= _w_size; s.strstart -= _w_size; /* we now have strstart >= MAX_DIST */ s.block_start -= _w_size; if (s.insert > s.strstart) { s.insert = s.strstart; } slide_hash(s); more += _w_size; } if (s.strm.avail_in === 0) { break; } /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ //Assert(more >= 2, "more < 2"); n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); s.lookahead += n; /* Initialize the hash value now that we have some input: */ if (s.lookahead + s.insert >= MIN_MATCH) { str = s.strstart - s.insert; s.ins_h = s.window[str]; /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); //#if MIN_MATCH != 3 // Call update_hash() MIN_MATCH-3 more times //#endif while (s.insert) { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; s.insert--; if (s.lookahead + s.insert < MIN_MATCH) { break; } } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ // if (s.high_water < s.window_size) { // const curr = s.strstart + s.lookahead; // let init = 0; // // if (s.high_water < curr) { // /* Previous high water mark below current data -- zero WIN_INIT // * bytes or up to end of window, whichever is less. // */ // init = s.window_size - curr; // if (init > WIN_INIT) // init = WIN_INIT; // zmemzero(s->window + curr, (unsigned)init); // s->high_water = curr + init; // } // else if (s->high_water < (ulg)curr + WIN_INIT) { // /* High water mark at or above current data, but below current data // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up // * to end of window, whichever is less. // */ // init = (ulg)curr + WIN_INIT - s->high_water; // if (init > s->window_size - s->high_water) // init = s->window_size - s->high_water; // zmemzero(s->window + s->high_water, (unsigned)init); // s->high_water += init; // } // } // // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, // "not enough room for search"); }; /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * * In case deflateParams() is used to later switch to a non-zero compression * level, s->matches (otherwise unused when storing) keeps track of the number * of hash table slides to perform. If s->matches is 1, then one hash table * slide will be done when switching. If s->matches is 2, the maximum value * allowed here, then the hash table will be cleared, since two or more slides * is the same as a clear. * * deflate_stored() is written to minimize the number of times an input byte is * copied. It is most efficient with large input and output buffers, which * maximizes the opportunites to have a single copy from next_in to next_out. */ const deflate_stored = (s, flush) => { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. */ let min_block = s.pending_buf_size - 5 > s.w_size ? s.w_size : s.pending_buf_size - 5; /* Copy as many min_block or larger stored blocks directly to next_out as * possible. If flushing, copy the remaining available input to next_out as * stored blocks, if there is enough space. */ let len, left, have, last = 0; let used = s.strm.avail_in; do { /* Set len to the maximum size block that we can copy directly with the * available input data and output space. Set left to how much of that * would be copied from what"s left in the window. */ len = 65535/* MAX_STORED */; /* maximum deflate stored block length */ have = (s.bi_valid + 42) >> 3; /* number of header bytes */ if (s.strm.avail_out < have) { /* need room for header */ break; } /* maximum stored block length that will fit in avail_out: */ have = s.strm.avail_out - have; left = s.strstart - s.block_start; /* bytes left in window */ if (len > left + s.strm.avail_in) { len = left + s.strm.avail_in; /* limit len to the input */ } if (len > have) { len = have; /* limit len to the output */ } /* If the stored block would be less than min_block in length, or if * unable to copy all of the available input when flushing, then try * copying to the window and the pending buffer instead. Also don"t * write an empty block when flushing -- deflate() does that. */ if (len < min_block && ((len === 0 && flush !== Z_FINISH$3) || flush === Z_NO_FLUSH$2 || len !== left + s.strm.avail_in)) { break; } /* Make a dummy stored block in pending to get the header bytes, * including any pending bits. This also updates the debugging counts. */ last = flush === Z_FINISH$3 && len === left + s.strm.avail_in ? 1 : 0; _tr_stored_block(s, 0, 0, last); /* Replace the lengths in the dummy stored block with len. */ s.pending_buf[s.pending - 4] = len; s.pending_buf[s.pending - 3] = len >> 8; s.pending_buf[s.pending - 2] = ~len; s.pending_buf[s.pending - 1] = ~len >> 8; /* Write the stored block header bytes. */ flush_pending(s.strm); //#ifdef ZLIB_DEBUG // /* Update debugging counts for the data about to be copied. */ // s->compressed_len += len << 3; // s->bits_sent += len << 3; //#endif /* Copy uncompressed bytes from the window to next_out. */ if (left) { if (left > len) { left = len; } //zmemcpy(s->strm->next_out, s->window + s->block_start, left); s.strm.output.set(s.window.subarray(s.block_start, s.block_start + left), s.strm.next_out); s.strm.next_out += left; s.strm.avail_out -= left; s.strm.total_out += left; s.block_start += left; len -= left; } /* Copy uncompressed bytes directly from next_in to next_out, updating * the check value. */ if (len) { read_buf(s.strm, s.strm.output, s.strm.next_out, len); s.strm.next_out += len; s.strm.avail_out -= len; s.strm.total_out += len; } } while (last === 0); /* Update the sliding window with the last s->w_size bytes of the copied * data, or append all of the copied data to the existing window if less * than s->w_size bytes were copied. Also update the number of bytes to * insert in the hash tables, in the event that deflateParams() switches to * a non-zero compression level. */ used -= s.strm.avail_in; /* number of input bytes directly copied */ if (used) { /* If any input was used, then no unused input remains in the window, * therefore s->block_start == s->strstart. */ if (used >= s.w_size) { /* supplant the previous history */ s.matches = 2; /* clear hash */ //zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s.window.set(s.strm.input.subarray(s.strm.next_in - s.w_size, s.strm.next_in), 0); s.strstart = s.w_size; s.insert = s.strstart; } else { if (s.window_size - s.strstart <= used) { /* Slide the window down. */ s.strstart -= s.w_size; //zmemcpy(s->window, s->window + s->w_size, s->strstart); s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0); if (s.matches < 2) { s.matches++; /* add a pending slide_hash() */ } if (s.insert > s.strstart) { s.insert = s.strstart; } } //zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s.window.set(s.strm.input.subarray(s.strm.next_in - used, s.strm.next_in), s.strstart); s.strstart += used; s.insert += used > s.w_size - s.insert ? s.w_size - s.insert : used; } s.block_start = s.strstart; } if (s.high_water < s.strstart) { s.high_water = s.strstart; } /* If the last block was written to next_out, then done. */ if (last) { return BS_FINISH_DONE; } /* If flushing and all input has been consumed, then done. */ if (flush !== Z_NO_FLUSH$2 && flush !== Z_FINISH$3 && s.strm.avail_in === 0 && s.strstart === s.block_start) { return BS_BLOCK_DONE; } /* Fill the window with any remaining input. */ have = s.window_size - s.strstart; if (s.strm.avail_in > have && s.block_start >= s.w_size) { /* Slide the window down. */ s.block_start -= s.w_size; s.strstart -= s.w_size; //zmemcpy(s->window, s->window + s->w_size, s->strstart); s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0); if (s.matches < 2) { s.matches++; /* add a pending slide_hash() */ } have += s.w_size; /* more space now */ if (s.insert > s.strstart) { s.insert = s.strstart; } } if (have > s.strm.avail_in) { have = s.strm.avail_in; } if (have) { read_buf(s.strm, s.window, s.strstart, have); s.strstart += have; s.insert += have > s.w_size - s.insert ? s.w_size - s.insert : have; } if (s.high_water < s.strstart) { s.high_water = s.strstart; } /* There was not enough avail_out to write a complete worthy or flushed * stored block to next_out. Write a stored block to pending instead, if we * have enough input for a worthy block, or if flushing and there is enough * room for the remaining input as a stored block in the pending buffer. */ have = (s.bi_valid + 42) >> 3; /* number of header bytes */ /* maximum stored block length that will fit in pending: */ have = s.pending_buf_size - have > 65535/* MAX_STORED */ ? 65535/* MAX_STORED */ : s.pending_buf_size - have; min_block = have > s.w_size ? s.w_size : have; left = s.strstart - s.block_start; if (left >= min_block || ((left || flush === Z_FINISH$3) && flush !== Z_NO_FLUSH$2 && s.strm.avail_in === 0 && left <= have)) { len = left > have ? have : left; last = flush === Z_FINISH$3 && s.strm.avail_in === 0 && len === left ? 1 : 0; _tr_stored_block(s, s.block_start, len, last); s.block_start += len; flush_pending(s.strm); } /* We"ve done all we can with the available input and output. */ return last ? BS_FINISH_STARTED : BS_NEED_MORE; }; /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ const deflate_fast = (s, flush) => { let hash_head; /* head of the hash chain */ let bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; /* flush the current block */ } } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ } if (s.match_length >= MIN_MATCH) { // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only /*** _tr_tally_dist(s, s.strstart - s.match_start, s.match_length - MIN_MATCH, bflush); ***/ bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { s.match_length--; /* string at strstart already in table */ do { s.strstart++; /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s.match_length !== 0); s.strstart++; } else { s.strstart += s.match_length; s.match_length = 0; s.ins_h = s.window[s.strstart]; /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); //#if MIN_MATCH != 3 // Call UPDATE_HASH() MIN_MATCH-3 more times //#endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s.window[s.strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.sym_next) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ const deflate_slow = (s, flush) => { let hash_head; /* head of hash chain */ let bflush; /* set if current block must be flushed */ let max_insert; /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. */ s.prev_length = s.match_length; s.prev_match = s.match_start; s.match_length = MIN_MATCH - 1; if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ if (s.match_length <= 5 && (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s.match_length = MIN_MATCH - 1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { max_insert = s.strstart + s.lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ //check_match(s, s.strstart-1, s.prev_match, s.prev_length); /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH, bflush);***/ bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s.lookahead -= s.prev_length - 1; s.prev_length -= 2; do { if (++s.strstart <= max_insert) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } } while (--s.prev_length !== 0); s.match_available = 0; s.match_length = MIN_MATCH - 1; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } else if (s.match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); if (bflush) { /*** FLUSH_BLOCK_ONLY(s, 0) ***/ flush_block_only(s, false); /***/ } s.strstart++; s.lookahead--; if (s.strm.avail_out === 0) { return BS_NEED_MORE; } } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s.match_available = 1; s.strstart++; s.lookahead--; } } //Assert (flush != Z_NO_FLUSH, "no flush?"); if (s.match_available) { //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); s.match_available = 0; } s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.sym_next) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ const deflate_rle = (s, flush) => { let bflush; /* set if current block must be flushed */ let prev; /* byte at distance one to match */ let scan, strend; /* scan goes up to strend for length of run */ const _win = s.window; for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s.lookahead <= MAX_MATCH) { fill_window(s); if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* See how many times the previous byte repeats */ s.match_length = 0; if (s.lookahead >= MIN_MATCH && s.strstart > 0) { scan = s.strstart - 1; prev = _win[scan]; if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { strend = s.strstart + MAX_MATCH; do { /*jshint noempty:false*/ } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend); s.match_length = MAX_MATCH - (strend - scan); if (s.match_length > s.lookahead) { s.match_length = s.lookahead; } } //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s.match_length >= MIN_MATCH) { //check_match(s, s.strstart, s.strstart - 1, s.match_length); /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; s.strstart += s.match_length; s.match_length = 0; } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.sym_next) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ const deflate_huff = (s, flush) => { let bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s.lookahead === 0) { fill_window(s); if (s.lookahead === 0) { if (flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } break; /* flush the current block */ } } /* Output a literal byte */ s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.sym_next) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ function Config(good_length, max_lazy, nice_length, max_chain, func) { this.good_length = good_length; this.max_lazy = max_lazy; this.nice_length = nice_length; this.max_chain = max_chain; this.func = func; } const configuration_table = [ /* good lazy nice chain */ new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ new Config(4, 5, 16, 8, deflate_fast), /* 2 */ new Config(4, 6, 32, 32, deflate_fast), /* 3 */ new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ new Config(8, 16, 32, 32, deflate_slow), /* 5 */ new Config(8, 16, 128, 128, deflate_slow), /* 6 */ new Config(8, 32, 128, 256, deflate_slow), /* 7 */ new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ ]; /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ const lm_init = (s) => { s.window_size = 2 * s.w_size; /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); /* Set the default configuration parameters: */ s.max_lazy_match = configuration_table[s.level].max_lazy; s.good_match = configuration_table[s.level].good_length; s.nice_match = configuration_table[s.level].nice_length; s.max_chain_length = configuration_table[s.level].max_chain; s.strstart = 0; s.block_start = 0; s.lookahead = 0; s.insert = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; s.ins_h = 0; }; function DeflateState() { this.strm = null; /* pointer back to this zlib stream */ this.status = 0; /* as the name implies */ this.pending_buf = null; /* output still pending */ this.pending_buf_size = 0; /* size of pending_buf */ this.pending_out = 0; /* next pending byte to output to the stream */ this.pending = 0; /* nb of bytes in the pending buffer */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.gzhead = null; /* gzip header information to write */ this.gzindex = 0; /* where in extra, name, or comment */ this.method = Z_DEFLATED$2; /* can only be DEFLATED */ this.last_flush = -1; /* value of flush param for previous deflate call */ this.w_size = 0; /* LZ77 window size (32K by default) */ this.w_bits = 0; /* log2(w_size) (8..16) */ this.w_mask = 0; /* w_size - 1 */ this.window = null; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. */ this.window_size = 0; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ this.prev = null; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ this.head = null; /* Heads of the hash chains or NIL. */ this.ins_h = 0; /* hash index of string to be inserted */ this.hash_size = 0; /* number of elements in hash table */ this.hash_bits = 0; /* log2(hash_size) */ this.hash_mask = 0; /* hash_size-1 */ this.hash_shift = 0; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ this.block_start = 0; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ this.match_length = 0; /* length of best match */ this.prev_match = 0; /* previous match */ this.match_available = 0; /* set if previous match exists */ this.strstart = 0; /* start of string to insert */ this.match_start = 0; /* start of matching string */ this.lookahead = 0; /* number of valid bytes ahead in window */ this.prev_length = 0; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ this.max_chain_length = 0; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ this.max_lazy_match = 0; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ // That"s alias to max_lazy_match, don"t use directly //this.max_insert_length = 0; /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ this.level = 0; /* compression level (1..9) */ this.strategy = 0; /* favor or force Huffman coding*/ this.good_match = 0; /* Use a faster search when the previous match is longer than this */ this.nice_match = 0; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn"t use ct_data typedef below to suppress compiler warning */ // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ // Use flat array of DOUBLE size, with interleaved fata, // because JS does not support effective this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2); this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2); this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2); zero(this.dyn_ltree); zero(this.dyn_dtree); zero(this.bl_tree); this.l_desc = null; /* desc. for literal tree */ this.d_desc = null; /* desc. for distance tree */ this.bl_desc = null; /* desc. for bit length tree */ //ush bl_count[MAX_BITS+1]; this.bl_count = new Uint16Array(MAX_BITS + 1); /* number of codes at each bit length for an optimal tree */ //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */ zero(this.heap); this.heap_len = 0; /* number of elements in the heap */ this.heap_max = 0; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; zero(this.depth); /* Depth of each subtree used as tie breaker for trees of equal frequency */ this.sym_buf = 0; /* buffer for distances and literals/lengths */ this.lit_bufsize = 0; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can"t count above 4 */ this.sym_next = 0; /* running index in sym_buf */ this.sym_end = 0; /* symbol table full when sym_next reaches this */ this.opt_len = 0; /* bit length of current block with optimal trees */ this.static_len = 0; /* bit length of current block with static trees */ this.matches = 0; /* number of string matches in current block */ this.insert = 0; /* bytes at end of window left to insert */ this.bi_buf = 0; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ this.bi_valid = 0; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ // Used for window memory init. We safely ignore it for JS. That makes // sense only for pointers and memory check tools. //this.high_water = 0; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ const deflateStateCheck = (strm) => { if (!strm) { return 1; } const s = strm.state; if (!s || s.strm !== strm || (s.status !== INIT_STATE && //#ifdef GZIP s.status !== GZIP_STATE && //#endif s.status !== EXTRA_STATE && s.status !== NAME_STATE && s.status !== COMMENT_STATE && s.status !== HCRC_STATE && s.status !== BUSY_STATE && s.status !== FINISH_STATE)) { return 1; } return 0; }; const deflateResetKeep = (strm) => { if (deflateStateCheck(strm)) { return err(strm, Z_STREAM_ERROR$2); } strm.total_in = strm.total_out = 0; strm.data_type = Z_UNKNOWN; const s = strm.state; s.pending = 0; s.pending_out = 0; if (s.wrap < 0) { s.wrap = -s.wrap; /* was made negative by deflate(..., Z_FINISH); */ } s.status = //#ifdef GZIP s.wrap === 2 ? GZIP_STATE : //#endif s.wrap ? INIT_STATE : BUSY_STATE; strm.adler = (s.wrap === 2) ? 0 // crc32(0, Z_NULL, 0) : 1; // adler32(0, Z_NULL, 0) s.last_flush = -2; _tr_init(s); return Z_OK$3; }; const deflateReset = (strm) => { const ret = deflateResetKeep(strm); if (ret === Z_OK$3) { lm_init(strm.state); } return ret; }; const deflateSetHeader = (strm, head) => { if (deflateStateCheck(strm) || strm.state.wrap !== 2) { return Z_STREAM_ERROR$2; } strm.state.gzhead = head; return Z_OK$3; }; const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { if (!strm) { // === Z_NULL return Z_STREAM_ERROR$2; } let wrap = 1; if (level === Z_DEFAULT_COMPRESSION$1) { level = 6; } if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || (windowBits === 8 && wrap !== 1)) { return err(strm, Z_STREAM_ERROR$2); } if (windowBits === 8) { windowBits = 9; } /* until 256-byte window bug fixed */ const s = new DeflateState(); strm.state = s; s.strm = strm; s.status = INIT_STATE; /* to pass state test in deflateReset() */ s.wrap = wrap; s.gzhead = null; s.w_bits = windowBits; s.w_size = 1 << s.w_bits; s.w_mask = s.w_size - 1; s.hash_bits = memLevel + 7; s.hash_size = 1 << s.hash_bits; s.hash_mask = s.hash_size - 1; s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); s.window = new Uint8Array(s.w_size * 2); s.head = new Uint16Array(s.hash_size); s.prev = new Uint16Array(s.w_size); // Don"t need mem init magic for JS. //s.high_water = 0; /* nothing written to s->window yet */ s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ /* We overlay pending_buf and sym_buf. This works since the average size * for length/distance pairs over any compressed block is assured to be 31 * bits or less. * * Analysis: The longest fixed codes are a length code of 8 bits plus 5 * extra bits, for lengths 131 to 257. The longest fixed distance codes are * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest * possible fixed-codes length/distance pair is then 31 bits total. * * sym_buf starts one-fourth of the way into pending_buf. So there are * three bytes in sym_buf for every four bytes in pending_buf. Each symbol * in sym_buf is three bytes -- two for the distance and one for the * literal/length. As each symbol is consumed, the pointer to the next * sym_buf value to read moves forward three bytes. From that symbol, up to * 31 bits are written to pending_buf. The closest the written pending_buf * bits gets to the next sym_buf symbol to read is just before the last * code is written. At that time, 31*(n-2) bits have been written, just * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1 * symbols are written.) The closest the writing gets to what is unread is * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and * can range from 128 to 32768. * * Therefore, at a minimum, there are 142 bits of space between what is * written and what is read in the overlain buffers, so the symbols cannot * be overwritten by the compressed data. That space is actually 139 bits, * due to the three-bit fixed-code block header. * * That covers the case where either Z_FIXED is specified, forcing fixed * codes, or when the use of fixed codes is chosen, because that choice * results in a smaller compressed block than dynamic codes. That latter * condition then assures that the above analysis also covers all dynamic * blocks. A dynamic-code block will only be chosen to be emitted if it has * fewer bits than a fixed-code block would for the same set of symbols. * Therefore its average symbol length is assured to be less than 31. So * the compressed data for a dynamic block also cannot overwrite the * symbols from which it is being constructed. */ s.pending_buf_size = s.lit_bufsize * 4; s.pending_buf = new Uint8Array(s.pending_buf_size); // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) //s->sym_buf = s->pending_buf + s->lit_bufsize; s.sym_buf = s.lit_bufsize; //s->sym_end = (s->lit_bufsize - 1) * 3; s.sym_end = (s.lit_bufsize - 1) * 3; /* We avoid equality with lit_bufsize*3 because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ s.level = level; s.strategy = strategy; s.method = method; return deflateReset(strm); }; const deflateInit = (strm, level) => { return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1); }; /* ========================================================================= */ const deflate$2 = (strm, flush) => { if (deflateStateCheck(strm) || flush > Z_BLOCK$1 || flush < 0) { return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2; } const s = strm.state; if (!strm.output || (strm.avail_in !== 0 && !strm.input) || (s.status === FINISH_STATE && flush !== Z_FINISH$3)) { return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2); } const old_flush = s.last_flush; s.last_flush = flush; /* Flush as much pending output as possible */ if (s.pending !== 0) { flush_pending(strm); if (strm.avail_out === 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won"t be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s.last_flush = -1; return Z_OK$3; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) { return err(strm, Z_BUF_ERROR$1); } /* User must not provide more input after the first FINISH: */ if (s.status === FINISH_STATE && strm.avail_in !== 0) { return err(strm, Z_BUF_ERROR$1); } /* Write the header */ if (s.status === INIT_STATE && s.wrap === 0) { s.status = BUSY_STATE; } if (s.status === INIT_STATE) { /* zlib header */ let header = (Z_DEFLATED$2 + ((s.w_bits - 8) << 4)) << 8; let level_flags = -1; if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { level_flags = 0; } else if (s.level < 6) { level_flags = 1; } else if (s.level === 6) { level_flags = 2; } else { level_flags = 3; } header |= (level_flags << 6); if (s.strstart !== 0) { header |= PRESET_DICT; } header += 31 - (header % 31); putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s.strstart !== 0) { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } strm.adler = 1; // adler32(0L, Z_NULL, 0); s.status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } } //#ifdef GZIP if (s.status === GZIP_STATE) { /* gzip header */ strm.adler = 0; //crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (!s.gzhead) { // s->gzhead == Z_NULL put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s.status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } } else { put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16) ); put_byte(s, s.gzhead.time & 0xff); put_byte(s, (s.gzhead.time >> 8) & 0xff); put_byte(s, (s.gzhead.time >> 16) & 0xff); put_byte(s, (s.gzhead.time >> 24) & 0xff); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, s.gzhead.os & 0xff); if (s.gzhead.extra && s.gzhead.extra.length) { put_byte(s, s.gzhead.extra.length & 0xff); put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); } if (s.gzhead.hcrc) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0); } s.gzindex = 0; s.status = EXTRA_STATE; } } if (s.status === EXTRA_STATE) { if (s.gzhead.extra/* != Z_NULL*/) { let beg = s.pending; /* start of bytes to update crc */ let left = (s.gzhead.extra.length & 0xffff) - s.gzindex; while (s.pending + left > s.pending_buf_size) { let copy = s.pending_buf_size - s.pending; // zmemcpy(s.pending_buf + s.pending, // s.gzhead.extra + s.gzindex, copy); s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending); s.pending = s.pending_buf_size; //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// s.gzindex += copy; flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } beg = 0; left -= copy; } // JS specific: s.gzhead.extra may be TypedArray or Array for backward compatibility // TypedArray.slice and TypedArray.from don"t exist in IE10-IE11 let gzhead_extra = new Uint8Array(s.gzhead.extra); // zmemcpy(s->pending_buf + s->pending, // s->gzhead->extra + s->gzindex, left); s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending); s.pending += left; //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// s.gzindex = 0; } s.status = NAME_STATE; } if (s.status === NAME_STATE) { if (s.gzhead.name/* != Z_NULL*/) { let beg = s.pending; /* start of bytes to update crc */ let val; do { if (s.pending === s.pending_buf_size) { //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } beg = 0; } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.name.length) { val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// s.gzindex = 0; } s.status = COMMENT_STATE; } if (s.status === COMMENT_STATE) { if (s.gzhead.comment/* != Z_NULL*/) { let beg = s.pending; /* start of bytes to update crc */ let val; do { if (s.pending === s.pending_buf_size) { //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } beg = 0; } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.comment.length) { val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); //--- HCRC_UPDATE(beg) ---// if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } //---// } s.status = HCRC_STATE; } if (s.status === HCRC_STATE) { if (s.gzhead.hcrc) { if (s.pending + 2 > s.pending_buf_size) { flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } } put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); strm.adler = 0; //crc32(0L, Z_NULL, 0); } s.status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s.pending !== 0) { s.last_flush = -1; return Z_OK$3; } } //#endif /* Start a new block or continue the current one. */ if (strm.avail_in !== 0 || s.lookahead !== 0 || (flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE)) { let bstate = s.level === 0 ? deflate_stored(s, flush) : s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush); if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { s.status = FINISH_STATE; } if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK$3; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don"t have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate === BS_BLOCK_DONE) { if (flush === Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush !== Z_BLOCK$1) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, 0, 0, false); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush === Z_FULL_FLUSH$1) { /*** CLEAR_HASH(s); ***/ /* forget history */ zero(s.head); // Fill with NIL (= 0); if (s.lookahead === 0) { s.strstart = 0; s.block_start = 0; s.insert = 0; } } } flush_pending(strm); if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK$3; } } } if (flush !== Z_FINISH$3) { return Z_OK$3; } if (s.wrap <= 0) { return Z_STREAM_END$3; } /* Write the trailer */ if (s.wrap === 2) { put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); put_byte(s, (strm.adler >> 16) & 0xff); put_byte(s, (strm.adler >> 24) & 0xff); put_byte(s, strm.total_in & 0xff); put_byte(s, (strm.total_in >> 8) & 0xff); put_byte(s, (strm.total_in >> 16) & 0xff); put_byte(s, (strm.total_in >> 24) & 0xff); } else { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s.wrap > 0) { s.wrap = -s.wrap; } /* write the trailer only once! */ return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3; }; const deflateEnd = (strm) => { if (deflateStateCheck(strm)) { return Z_STREAM_ERROR$2; } const status = strm.state.status; strm.state = null; return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3; }; /* ========================================================================= * Initializes the compression dictionary from the given byte * sequence without producing any compressed output. */ const deflateSetDictionary = (strm, dictionary) => { let dictLength = dictionary.length; if (deflateStateCheck(strm)) { return Z_STREAM_ERROR$2; } const s = strm.state; const wrap = s.wrap; if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { return Z_STREAM_ERROR$2; } /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap === 1) { /* adler32(strm->adler, dictionary, dictLength); */ strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0); } s.wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s.w_size) { if (wrap === 0) { /* already empty otherwise */ /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); s.strstart = 0; s.block_start = 0; s.insert = 0; } /* use the tail */ // dictionary = dictionary.slice(dictLength - s.w_size); let tmpDict = new Uint8Array(s.w_size); tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0); dictionary = tmpDict; dictLength = s.w_size; } /* insert dictionary into window and hash */ const avail = strm.avail_in; const next = strm.next_in; const input = strm.input; strm.avail_in = dictLength; strm.next_in = 0; strm.input = dictionary; fill_window(s); while (s.lookahead >= MIN_MATCH) { let str = s.strstart; let n = s.lookahead - (MIN_MATCH - 1); do { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; } while (--n); s.strstart = str; s.lookahead = MIN_MATCH - 1; fill_window(s); } s.strstart += s.lookahead; s.block_start = s.strstart; s.insert = s.lookahead; s.lookahead = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; strm.next_in = next; strm.input = input; strm.avail_in = avail; s.wrap = wrap; return Z_OK$3; }; var deflateInit_1 = deflateInit; var deflateInit2_1 = deflateInit2; var deflateReset_1 = deflateReset; var deflateResetKeep_1 = deflateResetKeep; var deflateSetHeader_1 = deflateSetHeader; var deflate_2$1 = deflate$2; var deflateEnd_1 = deflateEnd; var deflateSetDictionary_1 = deflateSetDictionary; var deflateInfo = "pako deflate (from Nodeca project)"; /* Not implemented module.exports.deflateBound = deflateBound; module.exports.deflateCopy = deflateCopy; module.exports.deflateGetDictionary = deflateGetDictionary; module.exports.deflateParams = deflateParams; module.exports.deflatePending = deflatePending; module.exports.deflatePrime = deflatePrime; module.exports.deflateTune = deflateTune; */ var deflate_1$2 = { deflateInit: deflateInit_1, deflateInit2: deflateInit2_1, deflateReset: deflateReset_1, deflateResetKeep: deflateResetKeep_1, deflateSetHeader: deflateSetHeader_1, deflate: deflate_2$1, deflateEnd: deflateEnd_1, deflateSetDictionary: deflateSetDictionary_1, deflateInfo: deflateInfo }; const _has = (obj, key) => { return Object.prototype.hasOwnProperty.call(obj, key); }; var assign = function (obj /*from1, from2, from3, ...*/) { const sources = Array.prototype.slice.call(arguments, 1); while (sources.length) { const source = sources.shift(); if (!source) { continue; } if (typeof source !== "object") { throw new TypeError(source + "must be non-object"); } for (const p in source) { if (_has(source, p)) { obj[p] = source[p]; } } } return obj; }; // Join array of chunks to single array. var flattenChunks = (chunks) => { // calculate data length let len = 0; for (let i = 0, l = chunks.length; i < l; i++) { len += chunks[i].length; } // join chunks const result = new Uint8Array(len); for (let i = 0, pos = 0, l = chunks.length; i < l; i++) { let chunk = chunks[i]; result.set(chunk, pos); pos += chunk.length; } return result; }; var common = { assign: assign, flattenChunks: flattenChunks }; // String encode/decode helpers // Quick check if we can use fast array to bin string conversion // // - apply(Array) can fail on Android 2.2 // - apply(Uint8Array) can fail on iOS 5.1 Safari // let STR_APPLY_UIA_OK = true; try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; } // Table with utf8 lengths (calculated by first byte of sequence) // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, // because max possible codepoint is 0x10ffff const _utf8len = new Uint8Array(256); for (let q = 0; q < 256; q++) { _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); } _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start // convert string to array (typed, when possible) var string2buf = (str) => { if (typeof TextEncoder === "function" && TextEncoder.prototype.encode) { return new TextEncoder().encode(str); } let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; // count binary size for (m_pos = 0; m_pos < str_len; m_pos++) { c = str.charCodeAt(m_pos); if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { c2 = str.charCodeAt(m_pos + 1); if ((c2 & 0xfc00) === 0xdc00) { c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); m_pos++; } } buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; } // allocate buffer buf = new Uint8Array(buf_len); // convert for (i = 0, m_pos = 0; i < buf_len; m_pos++) { c = str.charCodeAt(m_pos); if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { c2 = str.charCodeAt(m_pos + 1); if ((c2 & 0xfc00) === 0xdc00) { c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); m_pos++; } } if (c < 0x80) { /* one byte */ buf[i++] = c; } else if (c < 0x800) { /* two bytes */ buf[i++] = 0xC0 | (c >>> 6); buf[i++] = 0x80 | (c & 0x3f); } else if (c < 0x10000) { /* three bytes */ buf[i++] = 0xE0 | (c >>> 12); buf[i++] = 0x80 | (c >>> 6 & 0x3f); buf[i++] = 0x80 | (c & 0x3f); } else { /* four bytes */ buf[i++] = 0xf0 | (c >>> 18); buf[i++] = 0x80 | (c >>> 12 & 0x3f); buf[i++] = 0x80 | (c >>> 6 & 0x3f); buf[i++] = 0x80 | (c & 0x3f); } } return buf; }; // Helper const buf2binstring = (buf, len) => { // On Chrome, the arguments in a function call that are allowed is `65534`. // If the length of the buffer is smaller than that, we can use this optimization, // otherwise we will take a slower path. if (len < 65534) { if (buf.subarray && STR_APPLY_UIA_OK) { return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len)); } } let result = ""; for (let i = 0; i < len; i++) { result += String.fromCharCode(buf[i]); } return result; }; // convert array to string var buf2string = (buf, max) => { const len = max || buf.length; if (typeof TextDecoder === "function" && TextDecoder.prototype.decode) { return new TextDecoder().decode(buf.subarray(0, max)); } let i, out; // Reserve max possible length (2 words per char) // NB: by unknown reasons, Array is significantly faster for // String.fromCharCode.apply than Uint16Array. const utf16buf = new Array(len * 2); for (out = 0, i = 0; i < len;) { let c = buf[i++]; // quick process ascii if (c < 0x80) { utf16buf[out++] = c; continue; } let c_len = _utf8len[c]; // skip 5 & 6 byte codes if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; } // apply mask on first byte c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; // join the rest while (c_len > 1 && i < len) { c = (c << 6) | (buf[i++] & 0x3f); c_len--; } // terminated by end of string? if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } if (c < 0x10000) { utf16buf[out++] = c; } else { c -= 0x10000; utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); utf16buf[out++] = 0xdc00 | (c & 0x3ff); } } return buf2binstring(utf16buf, out); }; // Calculate max possible position in utf8 buffer, // that will not break sequence. If that"s not possible // - (very small limits) return max size as is. // // buf[] - utf8 bytes array // max - length limit (mandatory); var utf8border = (buf, max) => { max = max || buf.length; if (max > buf.length) { max = buf.length; } // go back from last position, until start of sequence found let pos = max - 1; while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } // Very small and broken sequence, // return max, because we should return something anyway. if (pos < 0) { return max; } // If we came to start of buffer - that means buffer is too small, // return max too. if (pos === 0) { return max; } return (pos + _utf8len[buf[pos]] > max) ? pos : max; }; var strings = { string2buf: string2buf, buf2string: buf2string, utf8border: utf8border }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function ZStream() { /* next input byte */ this.input = null; // JS specific, because we have no pointers this.next_in = 0; /* number of bytes available at input */ this.avail_in = 0; /* total number of input bytes read so far */ this.total_in = 0; /* next output byte should be put there */ this.output = null; // JS specific, because we have no pointers this.next_out = 0; /* remaining free space at output */ this.avail_out = 0; /* total number of bytes output so far */ this.total_out = 0; /* last error message, NULL if no error */ this.msg = ""/*Z_NULL*/; /* not visible by applications */ this.state = null; /* best guess about the data type: binary or text */ this.data_type = 2/*Z_UNKNOWN*/; /* adler32 value of the uncompressed data */ this.adler = 0; } var zstream = ZStream; const toString$1 = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH: Z_NO_FLUSH$1, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH: Z_FINISH$2, Z_OK: Z_OK$2, Z_STREAM_END: Z_STREAM_END$2, Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY, Z_DEFLATED: Z_DEFLATED$1 } = constants$2; /* ===========================================================================*/ /** * class Deflate * * Generic JS-style wrapper for zlib calls. If you don"t need * streaming behaviour - use more simple functions: [[deflate]], * [[deflateRaw]] and [[gzip]]. **/ /* internal * Deflate.chunks -> Array * * Chunks of output data, if [[Deflate#onData]] not overridden. **/ /** * Deflate.result -> Uint8Array * * Compressed result, generated by default [[Deflate#onData]] * and [[Deflate#onEnd]] handlers. Filled after you push last chunk * (call [[Deflate#push]] with `Z_FINISH` / `true` param). **/ /** * Deflate.err -> Number * * Error code after deflate finished. 0 (Z_OK) on success. * You will not need it in real life, because deflate errors * are possible only on wrong options or bad `onData` / `onEnd` * custom handlers. **/ /** * Deflate.msg -> String * * Error message, if [[Deflate.err]] != 0 **/ /** * new Deflate(options) * - options (Object): zlib deflate options. * * Creates new deflator instance with specified params. Throws exception * on bad params. Supported options: * * - `level` * - `windowBits` * - `memLevel` * - `strategy` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw deflate * - `gzip` (Boolean) - create gzip wrapper * - `header` (Object) - custom header for gzip * - `text` (Boolean) - true if compressed data believed to be text * - `time` (Number) - modification time, unix timestamp * - `os` (Number) - operation system code * - `extra` (Array) - array of bytes with extra data (max 65536) * - `name` (String) - file name (binary string) * - `comment` (String) - comment (binary string) * - `hcrc` (Boolean) - true if header crc should be added * * ##### Example: * * ```javascript * const pako = require("pako") * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * const deflate = new pako.Deflate({ level: 3}); * * deflate.push(chunk1, false); * deflate.push(chunk2, true); // true -> last chunk * * if (deflate.err) { throw new Error(deflate.err); } * * console.log(deflate.result); * ``` **/ function Deflate$1(options) { this.options = common.assign({ level: Z_DEFAULT_COMPRESSION, method: Z_DEFLATED$1, chunkSize: 16384, windowBits: 15, memLevel: 8, strategy: Z_DEFAULT_STRATEGY }, options || {}); let opt = this.options; if (opt.raw && (opt.windowBits > 0)) { opt.windowBits = -opt.windowBits; } else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { opt.windowBits += 16; } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ""; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new zstream(); this.strm.avail_out = 0; let status = deflate_1$2.deflateInit2( this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy ); if (status !== Z_OK$2) { throw new Error(messages[status]); } if (opt.header) { deflate_1$2.deflateSetHeader(this.strm, opt.header); } if (opt.dictionary) { let dict; // Convert data if needed if (typeof opt.dictionary === "string") { // If we need to compress text, change encoding to utf8. dict = strings.string2buf(opt.dictionary); } else if (toString$1.call(opt.dictionary) === "[object ArrayBuffer]") { dict = new Uint8Array(opt.dictionary); } else { dict = opt.dictionary; } status = deflate_1$2.deflateSetDictionary(this.strm, dict); if (status !== Z_OK$2) { throw new Error(messages[status]); } this._dict_set = true; } } /** * Deflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be * converted to utf8 byte sequence. * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. * * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with * new compressed chunks. Returns `true` on success. The last data block must * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending * buffers and call [[Deflate#onEnd]]. * * On fail call [[Deflate#onEnd]] with error code and return false. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Deflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; let status, _flush_mode; if (this.ended) { return false; } if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; // Convert data if needed if (typeof data === "string") { // If we need to compress text, change encoding to utf8. strm.input = strings.string2buf(data); } else if (toString$1.call(data) === "[object ArrayBuffer]") { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } // Make sure avail_out > 6 to avoid repeating markers if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) { this.onData(strm.output.subarray(0, strm.next_out)); strm.avail_out = 0; continue; } status = deflate_1$2.deflate(strm, _flush_mode); // Ended => flush and finish if (status === Z_STREAM_END$2) { if (strm.next_out > 0) { this.onData(strm.output.subarray(0, strm.next_out)); } status = deflate_1$2.deflateEnd(this.strm); this.onEnd(status); this.ended = true; return status === Z_OK$2; } // Flush if out buffer full if (strm.avail_out === 0) { this.onData(strm.output); continue; } // Flush if requested and has data if (_flush_mode > 0 && strm.next_out > 0) { this.onData(strm.output.subarray(0, strm.next_out)); strm.avail_out = 0; continue; } if (strm.avail_in === 0) break; } return true; }; /** * Deflate#onData(chunk) -> Void * - chunk (Uint8Array): output data. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Deflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Deflate#onEnd(status) -> Void * - status (Number): deflate status. 0 (Z_OK) on success, * other if not. * * Called once after you tell deflate that the input stream is * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Deflate$1.prototype.onEnd = function (status) { // On success - join if (status === Z_OK$2) { this.result = common.flattenChunks(this.chunks); } this.chunks = []; this.err = status; this.msg = this.strm.msg; }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // See state defs from inflate.js const BAD$1 = 16209; /* got a data error -- remain here until reset */ const TYPE$1 = 16191; /* i: waiting for type bits, including last-flag bit */ /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state.mode === LEN strm.avail_in >= 6 strm.avail_out >= 258 start >= strm.avail_out state.bits < 8 On return, state.mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm.avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm.avail_out >= 258 for each loop to avoid checking for output space. */ var inffast = function inflate_fast(strm, start) { let _in; /* local strm.input */ let last; /* have enough input while in < last */ let _out; /* local strm.output */ let beg; /* inflate()"s initial strm.output */ let end; /* while out < end, enough space available */ //#ifdef INFLATE_STRICT let dmax; /* maximum distance from zlib header */ //#endif let wsize; /* window size or zero if not using window */ let whave; /* valid bytes in the window */ let wnext; /* window write index */ // Use `s_window` instead `window`, avoid conflict with instrumentation tools let s_window; /* allocated sliding window, if wsize != 0 */ let hold; /* local strm.hold */ let bits; /* local strm.bits */ let lcode; /* local strm.lencode */ let dcode; /* local strm.distcode */ let lmask; /* mask for first level of length codes */ let dmask; /* mask for first level of distance codes */ let here; /* retrieved table entry */ let op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ let len; /* match length, unused bytes */ let dist; /* match distance */ let from; /* where to copy match from */ let from_source; let input, output; // JS specific, because we have no pointers /* copy state to local variables */ const state = strm.state; //here = state.here; _in = strm.next_in; input = strm.input; last = _in + (strm.avail_in - 5); _out = strm.next_out; output = strm.output; beg = _out - (start - strm.avail_out); end = _out + (strm.avail_out - 257); //#ifdef INFLATE_STRICT dmax = state.dmax; //#endif wsize = state.wsize; whave = state.whave; wnext = state.wnext; s_window = state.window; hold = state.hold; bits = state.bits; lcode = state.lencode; dcode = state.distcode; lmask = (1 << state.lenbits) - 1; dmask = (1 << state.distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ top: do { if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = lcode[hold & lmask]; dolen: for (;;) { // Goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op === 0) { /* literal */ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal "%c" " : // "inflate: literal 0x%02x ", here.val)); output[_out++] = here & 0xffff/*here.val*/; } else if (op & 16) { /* length base */ len = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += input[_in++] << bits; bits += 8; } len += hold & ((1 << op) - 1); hold >>>= op; bits -= op; } //Tracevv((stderr, "inflate: length %u ", len)); if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = dcode[hold & dmask]; dodist: for (;;) { // goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op & 16) { /* distance base */ dist = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (bits < op) { hold += input[_in++] << bits; bits += 8; if (bits < op) { hold += input[_in++] << bits; bits += 8; } } dist += hold & ((1 << op) - 1); //#ifdef INFLATE_STRICT if (dist > dmax) { strm.msg = "invalid distance too far back"; state.mode = BAD$1; break top; } //#endif hold >>>= op; bits -= op; //Tracevv((stderr, "inflate: distance %u ", dist)); op = _out - beg; /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state.sane) { strm.msg = "invalid distance too far back"; state.mode = BAD$1; break top; } // (!) This block is disabled in zlib defaults, // don"t enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // if (len <= op - whave) { // do { // output[_out++] = 0; // } while (--len); // continue top; // } // len -= op - whave; // do { // output[_out++] = 0; // } while (--op > whave); // if (op === 0) { // from = _out - dist; // do { // output[_out++] = output[from++]; // } while (--len); // continue top; // } //#endif } from = 0; // window index from_source = s_window; if (wnext === 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = 0; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } while (len > 2) { output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; len -= 3; } if (len) { output[_out++] = from_source[from++]; if (len > 1) { output[_out++] = from_source[from++]; } } } else { from = _out - dist; /* copy direct from output */ do { /* minimum length is three */ output[_out++] = output[from++]; output[_out++] = output[from++]; output[_out++] = output[from++]; len -= 3; } while (len > 2); if (len) { output[_out++] = output[from++]; if (len > 1) { output[_out++] = output[from++]; } } } } else if ((op & 64) === 0) { /* 2nd level distance code */ here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dodist; } else { strm.msg = "invalid distance code"; state.mode = BAD$1; break top; } break; // need to emulate goto via "continue" } } else if ((op & 64) === 0) { /* 2nd level length code */ here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dolen; } else if (op & 32) { /* end-of-block */ //Tracevv((stderr, "inflate: end of block ")); state.mode = TYPE$1; break top; } else { strm.msg = "invalid literal/length code"; state.mode = BAD$1; break top; } break; // need to emulate goto via "continue" } } while (_in < last && _out < end); /* return unused bytes (on entry, bits < 8, so in won"t go too far back) */ len = bits >> 3; _in -= len; bits -= len << 3; hold &= (1 << bits) - 1; /* update state and return */ strm.next_in = _in; strm.next_out = _out; strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); state.hold = hold; state.bits = bits; return; }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const MAXBITS = 15; const ENOUGH_LENS$1 = 852; const ENOUGH_DISTS$1 = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); const CODES$1 = 0; const LENS$1 = 1; const DISTS$1 = 2; const lbase = new Uint16Array([ /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ]); const lext = new Uint8Array([ /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 ]); const dbase = new Uint16Array([ /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 ]); const dext = new Uint8Array([ /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64 ]); const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => { const bits = opts.bits; //here = opts.here; /* table entry for duplication */ let len = 0; /* a code"s length in bits */ let sym = 0; /* index of code symbols */ let min = 0, max = 0; /* minimum and maximum code lengths */ let root = 0; /* number of index bits for root table */ let curr = 0; /* number of index bits for current table */ let drop = 0; /* code bits to drop for sub-table */ let left = 0; /* number of prefix codes available */ let used = 0; /* code entries in table used */ let huff = 0; /* Huffman code */ let incr; /* for incrementing code, index */ let fill; /* index for replicating entries */ let low; /* low bits for current root entry */ let mask; /* mask for low root bits */ let next; /* next available space in table */ let base = null; /* base value table to use */ // let shoextra; /* extra bits table to use */ let match; /* use base and extra for symbol >= match */ const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ let extra = null; let here_bits, here_op, here_val; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) { count[len] = 0; } for (sym = 0; sym < codes; sym++) { count[lens[lens_index + sym]]++; } /* bound code lengths, force root to be within code lengths */ root = bits; for (max = MAXBITS; max >= 1; max--) { if (count[max] !== 0) { break; } } if (root > max) { root = max; } if (max === 0) { /* no symbols to code at all */ //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ //table.bits[opts.table_index] = 1; //here.bits = (var char)1; //table.val[opts.table_index++] = 0; //here.val = (var short)0; table[table_index++] = (1 << 24) | (64 << 16) | 0; //table.op[opts.table_index] = 64; //table.bits[opts.table_index] = 1; //table.val[opts.table_index++] = 0; table[table_index++] = (1 << 24) | (64 << 16) | 0; opts.bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) { if (count[min] !== 0) { break; } } if (root < min) { root = min; } /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) { return -1; } /* over-subscribed */ } if (left > 0 && (type === CODES$1 || max !== 1)) { return -1; /* incomplete set */ } /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) { offs[len + 1] = offs[len] + count[len]; } /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) { if (lens[lens_index + sym] !== 0) { work[offs[lens[lens_index + sym]]++] = sym; } } /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ // poor man optimization - use if-else instead of switch, // to avoid deopts in old v8 if (type === CODES$1) { base = extra = work; /* dummy value--not used */ match = 20; } else if (type === LENS$1) { base = lbase; extra = lext; match = 257; } else { /* DISTS */ base = dbase; extra = dext; match = 0; } /* initialize opts for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = table_index; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = -1; /* trigger new sub-table when len > root */ used = 1 << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type === LENS$1 && used > ENOUGH_LENS$1) || (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } /* process all codes and make table entries */ for (;;) { /* create table entry */ here_bits = len - drop; if (work[sym] + 1 < match) { here_op = 0; here_val = work[sym]; } else if (work[sym] >= match) { here_op = extra[work[sym] - match]; here_val = base[work[sym] - match]; } else { here_op = 32 + 64; /* end of block */ here_val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1 << (len - drop); fill = 1 << curr; min = fill; /* save offset to next table */ do { fill -= incr; table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; } while (fill !== 0); /* backwards increment the len-bit code huff */ incr = 1 << (len - 1); while (huff & incr) { incr >>= 1; } if (incr !== 0) { huff &= incr - 1; huff += incr; } else { huff = 0; } /* go to next symbol, update count, len */ sym++; if (--count[len] === 0) { if (len === max) { break; } len = lens[lens_index + work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) !== low) { /* if first time, transition to sub-tables */ if (drop === 0) { drop = root; } /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = 1 << curr; while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) { break; } curr++; left <<= 1; } /* check for enough space */ used += 1 << curr; if ((type === LENS$1 && used > ENOUGH_LENS$1) || (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } /* point entry in root table to sub-table */ low = huff & mask; /*table.op[low] = curr; table.bits[low] = root; table.val[low] = next - opts.table_index;*/ table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff !== 0) { //table.op[next + huff] = 64; /* invalid code marker */ //table.bits[next + huff] = len - drop; //table.val[next + huff] = 0; table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; } /* set return parameters */ //opts.table_index += used; opts.bits = root; return 0; }; var inftrees = inflate_table; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const CODES = 0; const LENS = 1; const DISTS = 2; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_FINISH: Z_FINISH$1, Z_BLOCK, Z_TREES, Z_OK: Z_OK$1, Z_STREAM_END: Z_STREAM_END$1, Z_NEED_DICT: Z_NEED_DICT$1, Z_STREAM_ERROR: Z_STREAM_ERROR$1, Z_DATA_ERROR: Z_DATA_ERROR$1, Z_MEM_ERROR: Z_MEM_ERROR$1, Z_BUF_ERROR, Z_DEFLATED } = constants$2; /* STATES ====================================================================*/ /* ===========================================================================*/ const HEAD = 16180; /* i: waiting for magic header */ const FLAGS = 16181; /* i: waiting for method and flags (gzip) */ const TIME = 16182; /* i: waiting for modification time (gzip) */ const OS = 16183; /* i: waiting for extra flags and operating system (gzip) */ const EXLEN = 16184; /* i: waiting for extra length (gzip) */ const EXTRA = 16185; /* i: waiting for extra bytes (gzip) */ const NAME = 16186; /* i: waiting for end of file name (gzip) */ const COMMENT = 16187; /* i: waiting for end of comment (gzip) */ const HCRC = 16188; /* i: waiting for header crc (gzip) */ const DICTID = 16189; /* i: waiting for dictionary check value */ const DICT = 16190; /* waiting for inflateSetDictionary() call */ const TYPE = 16191; /* i: waiting for type bits, including last-flag bit */ const TYPEDO = 16192; /* i: same, but skip check to exit inflate on new block */ const STORED = 16193; /* i: waiting for stored size (length and complement) */ const COPY_ = 16194; /* i/o: same as COPY below, but only first time in */ const COPY = 16195; /* i/o: waiting for input or output to copy stored block */ const TABLE = 16196; /* i: waiting for dynamic block table lengths */ const LENLENS = 16197; /* i: waiting for code length code lengths */ const CODELENS = 16198; /* i: waiting for length/lit and distance code lengths */ const LEN_ = 16199; /* i: same as LEN below, but only first time in */ const LEN = 16200; /* i: waiting for length/lit/eob code */ const LENEXT = 16201; /* i: waiting for length extra bits */ const DIST = 16202; /* i: waiting for distance code */ const DISTEXT = 16203; /* i: waiting for distance extra bits */ const MATCH = 16204; /* o: waiting for output space to copy string */ const LIT = 16205; /* o: waiting for output space to write literal */ const CHECK = 16206; /* i: waiting for 32-bit check value */ const LENGTH = 16207; /* i: waiting for 32-bit length (gzip) */ const DONE = 16208; /* finished check, done -- remain here until reset */ const BAD = 16209; /* got a data error -- remain here until reset */ const MEM = 16210; /* got an inflate() memory error -- remain here until reset */ const SYNC = 16211; /* looking for synchronization bytes to restart inflate() */ /* ===========================================================================*/ const ENOUGH_LENS = 852; const ENOUGH_DISTS = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); const MAX_WBITS = 15; /* 32K LZ77 window */ const DEF_WBITS = MAX_WBITS; const zswap32 = (q) => { return (((q >>> 24) & 0xff) + ((q >>> 8) & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24)); }; function InflateState() { this.strm = null; /* pointer back to this zlib stream */ this.mode = 0; /* current inflate mode */ this.last = false; /* true if processing last block */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip, bit 2 true to validate check value */ this.havedict = false; /* true if dictionary provided */ this.flags = 0; /* gzip header method and flags (0 if zlib), or -1 if raw or no header yet */ this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ this.check = 0; /* protected copy of check value */ this.total = 0; /* protected copy of output count */ // TODO: may be {} this.head = null; /* where to save gzip header information */ /* sliding window */ this.wbits = 0; /* log base 2 of requested window size */ this.wsize = 0; /* window size or zero if not using window */ this.whave = 0; /* valid bytes in the window */ this.wnext = 0; /* window write index */ this.window = null; /* allocated sliding window, if needed */ /* bit accumulator */ this.hold = 0; /* input bit accumulator */ this.bits = 0; /* number of bits in "in" */ /* for string and stored block copying */ this.length = 0; /* literal or length of data to copy */ this.offset = 0; /* distance back to copy string from */ /* for table and code decoding */ this.extra = 0; /* extra bits needed */ /* fixed and dynamic code tables */ this.lencode = null; /* starting table for length/literal codes */ this.distcode = null; /* starting table for distance codes */ this.lenbits = 0; /* index bits for lencode */ this.distbits = 0; /* index bits for distcode */ /* dynamic table building */ this.ncode = 0; /* number of code length code lengths */ this.nlen = 0; /* number of length code lengths */ this.ndist = 0; /* number of distance code lengths */ this.have = 0; /* number of code lengths in lens[] */ this.next = null; /* next available space in codes[] */ this.lens = new Uint16Array(320); /* temporary storage for code lengths */ this.work = new Uint16Array(288); /* work area for code table building */ /* because we don"t have pointers in js, we use lencode and distcode directly as buffers so we don"t need codes */ //this.codes = new Int32Array(ENOUGH); /* space for code tables */ this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ this.distdyn = null; /* dynamic table for distance codes (JS specific) */ this.sane = 0; /* if false, allow invalid distance too far */ this.back = 0; /* bits back of last unprocessed length/lit */ this.was = 0; /* initial length of match */ } const inflateStateCheck = (strm) => { if (!strm) { return 1; } const state = strm.state; if (!state || state.strm !== strm || state.mode < HEAD || state.mode > SYNC) { return 1; } return 0; }; const inflateResetKeep = (strm) => { if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } const state = strm.state; strm.total_in = strm.total_out = state.total = 0; strm.msg = ""; /*Z_NULL*/ if (state.wrap) { /* to support ill-conceived Java test suite */ strm.adler = state.wrap & 1; } state.mode = HEAD; state.last = 0; state.havedict = 0; state.flags = -1; state.dmax = 32768; state.head = null/*Z_NULL*/; state.hold = 0; state.bits = 0; //state.lencode = state.distcode = state.next = state.codes; state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); state.sane = 1; state.back = -1; //Tracev((stderr, "inflate: reset ")); return Z_OK$1; }; const inflateReset = (strm) => { if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } const state = strm.state; state.wsize = 0; state.whave = 0; state.wnext = 0; return inflateResetKeep(strm); }; const inflateReset2 = (strm, windowBits) => { let wrap; /* get the state */ if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } const state = strm.state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 5; if (windowBits < 48) { windowBits &= 15; } } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) { return Z_STREAM_ERROR$1; } if (state.window !== null && state.wbits !== windowBits) { state.window = null; } /* update state and reset the rest of it */ state.wrap = wrap; state.wbits = windowBits; return inflateReset(strm); }; const inflateInit2 = (strm, windowBits) => { if (!strm) { return Z_STREAM_ERROR$1; } //strm.msg = Z_NULL; /* in case we return an error */ const state = new InflateState(); //if (state === Z_NULL) return Z_MEM_ERROR; //Tracev((stderr, "inflate: allocated ")); strm.state = state; state.strm = strm; state.window = null/*Z_NULL*/; state.mode = HEAD; /* to pass state test in inflateReset2() */ const ret = inflateReset2(strm, windowBits); if (ret !== Z_OK$1) { strm.state = null/*Z_NULL*/; } return ret; }; const inflateInit = (strm) => { return inflateInit2(strm, DEF_WBITS); }; /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it"s called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ let virgin = true; let lenfix, distfix; // We have no pointers in JS, so keep tables separate const fixedtables = (state) => { /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { lenfix = new Int32Array(512); distfix = new Int32Array(32); /* literal/length table */ let sym = 0; while (sym < 144) { state.lens[sym++] = 8; } while (sym < 256) { state.lens[sym++] = 9; } while (sym < 280) { state.lens[sym++] = 7; } while (sym < 288) { state.lens[sym++] = 8; } inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); /* distance table */ sym = 0; while (sym < 32) { state.lens[sym++] = 5; } inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); /* do this just once */ virgin = false; } state.lencode = lenfix; state.lenbits = 9; state.distcode = distfix; state.distbits = 5; }; /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor"s data caches. */ const updatewindow = (strm, src, end, copy) => { let dist; const state = strm.state; /* if it hasn"t been done already, allocate space for the window */ if (state.window === null) { state.wsize = 1 << state.wbits; state.wnext = 0; state.whave = 0; state.window = new Uint8Array(state.wsize); } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state.wsize) { state.window.set(src.subarray(end - state.wsize, end), 0); state.wnext = 0; state.whave = state.wsize; } else { dist = state.wsize - state.wnext; if (dist > copy) { dist = copy; } //zmemcpy(state->window + state->wnext, end - copy, dist); state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); copy -= dist; if (copy) { //zmemcpy(state->window, end - copy, copy); state.window.set(src.subarray(end - copy, end), 0); state.wnext = copy; state.whave = state.wsize; } else { state.wnext += dist; if (state.wnext === state.wsize) { state.wnext = 0; } if (state.whave < state.wsize) { state.whave += dist; } } } return 0; }; const inflate$2 = (strm, flush) => { let state; let input, output; // input/output buffers let next; /* next input INDEX */ let put; /* next output INDEX */ let have, left; /* available input and output */ let hold; /* bit buffer */ let bits; /* bits in bit buffer */ let _in, _out; /* save starting available input and output */ let copy; /* number of stored or match bytes to copy */ let from; /* where to copy match bytes from */ let from_source; let here = 0; /* current decoding table entry */ let here_bits, here_op, here_val; // paked "here" denormalized (JS specific) //let last; /* parent table entry */ let last_bits, last_op, last_val; // paked "last" denormalized (JS specific) let len; /* length to copy for repeats, bits to drop */ let ret; /* return code */ const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */ let opts; let n; // temporary variable for NEED_BITS const order = /* permutation of code lengths */ new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); if (inflateStateCheck(strm) || !strm.output || (!strm.input && strm.avail_in !== 0)) { return Z_STREAM_ERROR$1; } state = strm.state; if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- _in = have; _out = left; ret = Z_OK$1; inf_leave: // goto emulation for (;;) { switch (state.mode) { case HEAD: if (state.wrap === 0) { state.mode = TYPEDO; break; } //=== NEEDBITS(16); while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ if (state.wbits === 0) { state.wbits = 15; } state.check = 0/*crc32(0L, Z_NULL, 0)*/; //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = FLAGS; break; } if (state.head) { state.head.done = false; } if (!(state.wrap & 1) || /* check if zlib header allowed */ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { strm.msg = "incorrect header check"; state.mode = BAD; break; } if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { strm.msg = "unknown compression method"; state.mode = BAD; break; } //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// len = (hold & 0x0f)/*BITS(4)*/ + 8; if (state.wbits === 0) { state.wbits = len; } if (len > 15 || len > state.wbits) { strm.msg = "invalid window size"; state.mode = BAD; break; } // !!! pako patch. Force use `options.windowBits` if passed. // Required to always use max window size by default. state.dmax = 1 << state.wbits; //state.dmax = 1 << len; state.flags = 0; /* indicate zlib header */ //Tracev((stderr, "inflate: zlib header ok ")); strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS(); hold = 0; bits = 0; //===// break; case FLAGS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.flags = hold; if ((state.flags & 0xff) !== Z_DEFLATED) { strm.msg = "unknown compression method"; state.mode = BAD; break; } if (state.flags & 0xe000) { strm.msg = "unknown header flags set"; state.mode = BAD; break; } if (state.head) { state.head.text = ((hold >> 8) & 1); } if ((state.flags & 0x0200) && (state.wrap & 4)) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = TIME; /* falls through */ case TIME: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.time = hold; } if ((state.flags & 0x0200) && (state.wrap & 4)) { //=== CRC4(state.check, hold) hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; hbuf[2] = (hold >>> 16) & 0xff; hbuf[3] = (hold >>> 24) & 0xff; state.check = crc32_1(state.check, hbuf, 4, 0); //=== } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = OS; /* falls through */ case OS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.xflags = (hold & 0xff); state.head.os = (hold >> 8); } if ((state.flags & 0x0200) && (state.wrap & 4)) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = EXLEN; /* falls through */ case EXLEN: if (state.flags & 0x0400) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length = hold; if (state.head) { state.head.extra_len = hold; } if ((state.flags & 0x0200) && (state.wrap & 4)) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// } else if (state.head) { state.head.extra = null/*Z_NULL*/; } state.mode = EXTRA; /* falls through */ case EXTRA: if (state.flags & 0x0400) { copy = state.length; if (copy > have) { copy = have; } if (copy) { if (state.head) { len = state.head.extra_len - state.length; if (!state.head.extra) { // Use untyped array for more convenient processing later state.head.extra = new Uint8Array(state.head.extra_len); } state.head.extra.set( input.subarray( next, // extra field is limited to 65536 bytes // - no need for additional size check next + copy ), /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ len ); //zmemcpy(state.head.extra + len, next, // len + copy > state.head.extra_max ? // state.head.extra_max - len : copy); } if ((state.flags & 0x0200) && (state.wrap & 4)) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; state.length -= copy; } if (state.length) { break inf_leave; } } state.length = 0; state.mode = NAME; /* falls through */ case NAME: if (state.flags & 0x0800) { if (have === 0) { break inf_leave; } copy = 0; do { // TODO: 2 or 1 bytes? len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.name_max*/)) { state.head.name += String.fromCharCode(len); } } while (len && copy < have); if ((state.flags & 0x0200) && (state.wrap & 4)) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.name = null; } state.length = 0; state.mode = COMMENT; /* falls through */ case COMMENT: if (state.flags & 0x1000) { if (have === 0) { break inf_leave; } copy = 0; do { len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.comm_max*/)) { state.head.comment += String.fromCharCode(len); } } while (len && copy < have); if ((state.flags & 0x0200) && (state.wrap & 4)) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.comment = null; } state.mode = HCRC; /* falls through */ case HCRC: if (state.flags & 0x0200) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((state.wrap & 4) && hold !== (state.check & 0xffff)) { strm.msg = "header crc mismatch"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// } if (state.head) { state.head.hcrc = ((state.flags >> 9) & 1); state.head.done = true; } strm.adler = state.check = 0; state.mode = TYPE; break; case DICTID: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// strm.adler = state.check = zswap32(hold); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = DICT; /* falls through */ case DICT: if (state.havedict === 0) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- return Z_NEED_DICT$1; } strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = TYPE; /* falls through */ case TYPE: if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } /* falls through */ case TYPEDO: if (state.last) { //--- BYTEBITS() ---// hold >>>= bits & 7; bits -= bits & 7; //---// state.mode = CHECK; break; } //=== NEEDBITS(3); */ while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.last = (hold & 0x01)/*BITS(1)*/; //--- DROPBITS(1) ---// hold >>>= 1; bits -= 1; //---// switch ((hold & 0x03)/*BITS(2)*/) { case 0: /* stored block */ //Tracev((stderr, "inflate: stored block%s ", // state.last ? " (last)" : "")); state.mode = STORED; break; case 1: /* fixed block */ fixedtables(state); //Tracev((stderr, "inflate: fixed codes block%s ", // state.last ? " (last)" : "")); state.mode = LEN_; /* decode codes */ if (flush === Z_TREES) { //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break inf_leave; } break; case 2: /* dynamic block */ //Tracev((stderr, "inflate: dynamic codes block%s ", // state.last ? " (last)" : "")); state.mode = TABLE; break; case 3: strm.msg = "invalid block type"; state.mode = BAD; } //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break; case STORED: //--- BYTEBITS() ---// /* go to byte boundary */ hold >>>= bits & 7; bits -= bits & 7; //---// //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { strm.msg = "invalid stored block lengths"; state.mode = BAD; break; } state.length = hold & 0xffff; //Tracev((stderr, "inflate: stored length %u ", // state.length)); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = COPY_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case COPY_: state.mode = COPY; /* falls through */ case COPY: copy = state.length; if (copy) { if (copy > have) { copy = have; } if (copy > left) { copy = left; } if (copy === 0) { break inf_leave; } //--- zmemcpy(put, next, copy); --- output.set(input.subarray(next, next + copy), put); //---// have -= copy; next += copy; left -= copy; put += copy; state.length -= copy; break; } //Tracev((stderr, "inflate: stored end ")); state.mode = TYPE; break; case TABLE: //=== NEEDBITS(14); */ while (bits < 14) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// //#ifndef PKZIP_BUG_WORKAROUND if (state.nlen > 286 || state.ndist > 30) { strm.msg = "too many length or distance symbols"; state.mode = BAD; break; } //#endif //Tracev((stderr, "inflate: table sizes ok ")); state.have = 0; state.mode = LENLENS; /* falls through */ case LENLENS: while (state.have < state.ncode) { //=== NEEDBITS(3); while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } while (state.have < 19) { state.lens[order[state.have++]] = 0; } // We have separate tables & no pointers. 2 commented lines below not needed. //state.next = state.codes; //state.lencode = state.next; // Switch to use dynamic table state.lencode = state.lendyn; state.lenbits = 7; opts = { bits: state.lenbits }; ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); state.lenbits = opts.bits; if (ret) { strm.msg = "invalid code lengths set"; state.mode = BAD; break; } //Tracev((stderr, "inflate: code lengths ok ")); state.have = 0; state.mode = CODELENS; /* falls through */ case CODELENS: while (state.have < state.nlen + state.ndist) { for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_val < 16) { //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.lens[state.have++] = here_val; } else { if (here_val === 16) { //=== NEEDBITS(here.bits + 2); n = here_bits + 2; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// if (state.have === 0) { strm.msg = "invalid bit length repeat"; state.mode = BAD; break; } len = state.lens[state.have - 1]; copy = 3 + (hold & 0x03);//BITS(2); //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// } else if (here_val === 17) { //=== NEEDBITS(here.bits + 3); n = here_bits + 3; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 3 + (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } else { //=== NEEDBITS(here.bits + 7); n = here_bits + 7; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 11 + (hold & 0x7f);//BITS(7); //--- DROPBITS(7) ---// hold >>>= 7; bits -= 7; //---// } if (state.have + copy > state.nlen + state.ndist) { strm.msg = "invalid bit length repeat"; state.mode = BAD; break; } while (copy--) { state.lens[state.have++] = len; } } } /* handle error breaks in while */ if (state.mode === BAD) { break; } /* check for end-of-block code (better have one) */ if (state.lens[256] === 0) { strm.msg = "invalid code -- missing end-of-block"; state.mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state.lenbits = 9; opts = { bits: state.lenbits }; ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.lenbits = opts.bits; // state.lencode = state.next; if (ret) { strm.msg = "invalid literal/lengths set"; state.mode = BAD; break; } state.distbits = 6; //state.distcode.copy(state.codes); // Switch to use dynamic table state.distcode = state.distdyn; opts = { bits: state.distbits }; ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.distbits = opts.bits; // state.distcode = state.next; if (ret) { strm.msg = "invalid distances set"; state.mode = BAD; break; } //Tracev((stderr, "inflate: codes ok ")); state.mode = LEN_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case LEN_: state.mode = LEN; /* falls through */ case LEN: if (have >= 6 && left >= 258) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- inffast(strm, _out); //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- if (state.mode === TYPE) { state.back = -1; } break; } state.back = 0; for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if (here_bits <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_op && (here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.lencode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; state.length = here_val; if (here_op === 0) { //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal "%c" " : // "inflate: literal 0x%02x ", here.val)); state.mode = LIT; break; } if (here_op & 32) { //Tracevv((stderr, "inflate: end of block ")); state.back = -1; state.mode = TYPE; break; } if (here_op & 64) { strm.msg = "invalid literal/length code"; state.mode = BAD; break; } state.extra = here_op & 15; state.mode = LENEXT; /* falls through */ case LENEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //Tracevv((stderr, "inflate: length %u ", state.length)); state.was = state.length; state.mode = DIST; /* falls through */ case DIST: for (;;) { here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if ((here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.distcode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; if (here_op & 64) { strm.msg = "invalid distance code"; state.mode = BAD; break; } state.offset = here_val; state.extra = (here_op) & 15; state.mode = DISTEXT; /* falls through */ case DISTEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //#ifdef INFLATE_STRICT if (state.offset > state.dmax) { strm.msg = "invalid distance too far back"; state.mode = BAD; break; } //#endif //Tracevv((stderr, "inflate: distance %u ", state.offset)); state.mode = MATCH; /* falls through */ case MATCH: if (left === 0) { break inf_leave; } copy = _out - left; if (state.offset > copy) { /* copy from window */ copy = state.offset - copy; if (copy > state.whave) { if (state.sane) { strm.msg = "invalid distance too far back"; state.mode = BAD; break; } // (!) This block is disabled in zlib defaults, // don"t enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // Trace((stderr, "inflate.c too far ")); // copy -= state.whave; // if (copy > state.length) { copy = state.length; } // if (copy > left) { copy = left; } // left -= copy; // state.length -= copy; // do { // output[put++] = 0; // } while (--copy); // if (state.length === 0) { state.mode = LEN; } // break; //#endif } if (copy > state.wnext) { copy -= state.wnext; from = state.wsize - copy; } else { from = state.wnext - copy; } if (copy > state.length) { copy = state.length; } from_source = state.window; } else { /* copy from output */ from_source = output; from = put - state.offset; copy = state.length; } if (copy > left) { copy = left; } left -= copy; state.length -= copy; do { output[put++] = from_source[from++]; } while (--copy); if (state.length === 0) { state.mode = LEN; } break; case LIT: if (left === 0) { break inf_leave; } output[put++] = state.length; left--; state.mode = LEN; break; case CHECK: if (state.wrap) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; // Use "|" instead of "+" to make sure that result is signed hold |= input[next++] << bits; bits += 8; } //===// _out -= left; strm.total_out += _out; state.total += _out; if ((state.wrap & 4) && _out) { strm.adler = state.check = /*UPDATE_CHECK(state.check, put - _out, _out);*/ (state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out)); } _out = left; // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too if ((state.wrap & 4) && (state.flags ? hold : zswap32(hold)) !== state.check) { strm.msg = "incorrect data check"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: check matches trailer ")); } state.mode = LENGTH; /* falls through */ case LENGTH: if (state.wrap && state.flags) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((state.wrap & 4) && hold !== (state.total & 0xffffffff)) { strm.msg = "incorrect length check"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: length matches trailer ")); } state.mode = DONE; /* falls through */ case DONE: ret = Z_STREAM_END$1; break inf_leave; case BAD: ret = Z_DATA_ERROR$1; break inf_leave; case MEM: return Z_MEM_ERROR$1; case SYNC: /* falls through */ default: return Z_STREAM_ERROR$1; } } // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1))) { if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ; } _in -= strm.avail_in; _out -= strm.avail_out; strm.total_in += _in; strm.total_out += _out; state.total += _out; if ((state.wrap & 4) && _out) { strm.adler = state.check = /*UPDATE_CHECK(state.check, strm.next_out - _out, _out);*/ (state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out)); } strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); if (((_in === 0 && _out === 0) || flush === Z_FINISH$1) && ret === Z_OK$1) { ret = Z_BUF_ERROR; } return ret; }; const inflateEnd = (strm) => { if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } let state = strm.state; if (state.window) { state.window = null; } strm.state = null; return Z_OK$1; }; const inflateGetHeader = (strm, head) => { /* check state */ if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } const state = strm.state; if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR$1; } /* save header structure */ state.head = head; head.done = false; return Z_OK$1; }; const inflateSetDictionary = (strm, dictionary) => { const dictLength = dictionary.length; let state; let dictid; let ret; /* check state */ if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1; } state = strm.state; if (state.wrap !== 0 && state.mode !== DICT) { return Z_STREAM_ERROR$1; } /* check for correct dictionary identifier */ if (state.mode === DICT) { dictid = 1; /* adler32(0, null, 0)*/ /* dictid = adler32(dictid, dictionary, dictLength); */ dictid = adler32_1(dictid, dictionary, dictLength, 0); if (dictid !== state.check) { return Z_DATA_ERROR$1; } } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary, dictLength, dictLength); if (ret) { state.mode = MEM; return Z_MEM_ERROR$1; } state.havedict = 1; // Tracev((stderr, "inflate: dictionary set ")); return Z_OK$1; }; var inflateReset_1 = inflateReset; var inflateReset2_1 = inflateReset2; var inflateResetKeep_1 = inflateResetKeep; var inflateInit_1 = inflateInit; var inflateInit2_1 = inflateInit2; var inflate_2$1 = inflate$2; var inflateEnd_1 = inflateEnd; var inflateGetHeader_1 = inflateGetHeader; var inflateSetDictionary_1 = inflateSetDictionary; var inflateInfo = "pako inflate (from Nodeca project)"; /* Not implemented module.exports.inflateCodesUsed = inflateCodesUsed; module.exports.inflateCopy = inflateCopy; module.exports.inflateGetDictionary = inflateGetDictionary; module.exports.inflateMark = inflateMark; module.exports.inflatePrime = inflatePrime; module.exports.inflateSync = inflateSync; module.exports.inflateSyncPoint = inflateSyncPoint; module.exports.inflateUndermine = inflateUndermine; module.exports.inflateValidate = inflateValidate; */ var inflate_1$2 = { inflateReset: inflateReset_1, inflateReset2: inflateReset2_1, inflateResetKeep: inflateResetKeep_1, inflateInit: inflateInit_1, inflateInit2: inflateInit2_1, inflate: inflate_2$1, inflateEnd: inflateEnd_1, inflateGetHeader: inflateGetHeader_1, inflateSetDictionary: inflateSetDictionary_1, inflateInfo: inflateInfo }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function GZheader() { /* true if compressed data believed to be text */ this.text = 0; /* modification time */ this.time = 0; /* extra flags (not used when writing a gzip file) */ this.xflags = 0; /* operating system */ this.os = 0; /* pointer to extra field or Z_NULL if none */ this.extra = null; /* extra field length (valid if extra != Z_NULL) */ this.extra_len = 0; // Actually, we don"t need it in JS, // but leave for few code modifications // // Setup limits is not necessary because in js we should not preallocate memory // for inflate use constant limit in 65536 bytes // /* space at extra (only when reading header) */ // this.extra_max = 0; /* pointer to zero-terminated file name or Z_NULL */ this.name = ""; /* space at name (only when reading header) */ // this.name_max = 0; /* pointer to zero-terminated comment or Z_NULL */ this.comment = ""; /* space at comment (only when reading header) */ // this.comm_max = 0; /* true if there was or will be a header crc */ this.hcrc = 0; /* true when done reading gzip header (not used when writing a gzip file) */ this.done = false; } var gzheader = GZheader; const toString = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH, Z_FINISH, Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR } = constants$2; /* ===========================================================================*/ /** * class Inflate * * Generic JS-style wrapper for zlib calls. If you don"t need * streaming behaviour - use more simple functions: [[inflate]] * and [[inflateRaw]]. **/ /* internal * inflate.chunks -> Array * * Chunks of output data, if [[Inflate#onData]] not overridden. **/ /** * Inflate.result -> Uint8Array|String * * Uncompressed result, generated by default [[Inflate#onData]] * and [[Inflate#onEnd]] handlers. Filled after you push last chunk * (call [[Inflate#push]] with `Z_FINISH` / `true` param). **/ /** * Inflate.err -> Number * * Error code after inflate finished. 0 (Z_OK) on success. * Should be checked if broken data possible. **/ /** * Inflate.msg -> String * * Error message, if [[Inflate.err]] != 0 **/ /** * new Inflate(options) * - options (Object): zlib inflate options. * * Creates new inflator instance with specified params. Throws exception * on bad params. Supported options: * * - `windowBits` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw inflate * - `to` (String) - if equal to "string", then result will be converted * from utf8 to utf16 (javascript) string. When string output requested, * chunk length can differ from `chunkSize`, depending on content. * * By default, when no options set, autodetect deflate/gzip data format via * wrapper header. * * ##### Example: * * ```javascript * const pako = require("pako") * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * const inflate = new pako.Inflate({ level: 3}); * * inflate.push(chunk1, false); * inflate.push(chunk2, true); // true -> last chunk * * if (inflate.err) { throw new Error(inflate.err); } * * console.log(inflate.result); * ``` **/ function Inflate$1(options) { this.options = common.assign({ chunkSize: 1024 * 64, windowBits: 15, to: "" }, options || {}); const opt = this.options; // Force window size for `raw` data, if not set directly, // because we have no header for autodetect. if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { opt.windowBits = -opt.windowBits; if (opt.windowBits === 0) { opt.windowBits = -15; } } // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate if ((opt.windowBits >= 0) && (opt.windowBits < 16) && !(options && options.windowBits)) { opt.windowBits += 32; } // Gzip header has no info about windows size, we can do autodetect only // for deflate. So, if window size not set, force it to max when gzip possible if ((opt.windowBits > 15) && (opt.windowBits < 48)) { // bit 3 (16) -> gzipped data // bit 4 (32) -> autodetect gzip/deflate if ((opt.windowBits & 15) === 0) { opt.windowBits |= 15; } } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ""; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new zstream(); this.strm.avail_out = 0; let status = inflate_1$2.inflateInit2( this.strm, opt.windowBits ); if (status !== Z_OK) { throw new Error(messages[status]); } this.header = new gzheader(); inflate_1$2.inflateGetHeader(this.strm, this.header); // Setup dictionary if (opt.dictionary) { // Convert data if needed if (typeof opt.dictionary === "string") { opt.dictionary = strings.string2buf(opt.dictionary); } else if (toString.call(opt.dictionary) === "[object ArrayBuffer]") { opt.dictionary = new Uint8Array(opt.dictionary); } if (opt.raw) { //In raw mode we need to set the dictionary early status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary); if (status !== Z_OK) { throw new Error(messages[status]); } } } } /** * Inflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer): input data * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, * `true` means Z_FINISH. * * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with * new output chunks. Returns `true` on success. If end of stream detected, * [[Inflate#onEnd]] will be called. * * `flush_mode` is not needed for normal operation, because end of stream * detected automatically. You may try to use it for advanced things, but * this functionality was not tested. * * On fail call [[Inflate#onEnd]] with error code and return false. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Inflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; const dictionary = this.options.dictionary; let status, _flush_mode, last_avail_out; if (this.ended) return false; if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed if (toString.call(data) === "[object ArrayBuffer]") { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } status = inflate_1$2.inflate(strm, _flush_mode); if (status === Z_NEED_DICT && dictionary) { status = inflate_1$2.inflateSetDictionary(strm, dictionary); if (status === Z_OK) { status = inflate_1$2.inflate(strm, _flush_mode); } else if (status === Z_DATA_ERROR) { // Replace code with more verbose status = Z_NEED_DICT; } } // Skip snyc markers if more data follows and not raw mode while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) { inflate_1$2.inflateReset(strm); status = inflate_1$2.inflate(strm, _flush_mode); } switch (status) { case Z_STREAM_ERROR: case Z_DATA_ERROR: case Z_NEED_DICT: case Z_MEM_ERROR: this.onEnd(status); this.ended = true; return false; } // Remember real `avail_out` value, because we may patch out buffer content // to align utf8 strings boundaries. last_avail_out = strm.avail_out; if (strm.next_out) { if (strm.avail_out === 0 || status === Z_STREAM_END) { if (this.options.to === "string") { let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); let tail = strm.next_out - next_out_utf8; let utf8str = strings.buf2string(strm.output, next_out_utf8); // move tail & realign counters strm.next_out = tail; strm.avail_out = chunkSize - tail; if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); this.onData(utf8str); } else { this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out)); } } } // Must repeat iteration if out buffer is full if (status === Z_OK && last_avail_out === 0) continue; // Finalize if end of stream reached. if (status === Z_STREAM_END) { status = inflate_1$2.inflateEnd(this.strm); this.onEnd(status); this.ended = true; return true; } if (strm.avail_in === 0) break; } return true; }; /** * Inflate#onData(chunk) -> Void * - chunk (Uint8Array|String): output data. When string output requested, * each chunk will be string. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Inflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Inflate#onEnd(status) -> Void * - status (Number): inflate status. 0 (Z_OK) on success, * other if not. * * Called either after you tell inflate that the input stream is * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Inflate$1.prototype.onEnd = function (status) { // On success - join if (status === Z_OK) { if (this.options.to === "string") { this.result = this.chunks.join(""); } else { this.result = common.flattenChunks(this.chunks); } } this.chunks = []; this.err = status; this.msg = this.strm.msg; }; /** * inflate(data[, options]) -> Uint8Array|String * - data (Uint8Array|ArrayBuffer): input data to decompress. * - options (Object): zlib inflate options. * * Decompress `data` with inflate/ungzip and `options`. Autodetect * format via wrapper header by default. That"s why we don"t provide * separate `ungzip` method. * * Supported options are: * * - windowBits * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information. * * Sugar (options): * * - `raw` (Boolean) - say that we work with raw stream, if you don"t wish to specify * negative windowBits implicitly. * - `to` (String) - if equal to "string", then result will be converted * from utf8 to utf16 (javascript) string. When string output requested, * chunk length can differ from `chunkSize`, depending on content. * * * ##### Example: * * ```javascript * const pako = require("pako"); * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9])); * let output; * * try { * output = pako.inflate(input); * } catch (err) { * console.log(err); * } * ``` **/ function inflate$1(input, options) { const inflator = new Inflate$1(options); inflator.push(input); // That will never happens, if you don"t cheat with options :) if (inflator.err) throw inflator.msg || messages[inflator.err]; return inflator.result; } /** * inflateRaw(data[, options]) -> Uint8Array|String * - data (Uint8Array|ArrayBuffer): input data to decompress. * - options (Object): zlib inflate options. * * The same as [[inflate]], but creates raw data, without wrapper * (header and adler32 crc). **/ function inflateRaw$1(input, options) { options = options || {}; options.raw = true; return inflate$1(input, options); } /** * ungzip(data[, options]) -> Uint8Array|String * - data (Uint8Array|ArrayBuffer): input data to decompress. * - options (Object): zlib inflate options. * * Just shortcut to [[inflate]], because it autodetects format * by header.content. Done for convenience. **/ var Inflate_1$1 = Inflate$1; var inflate_2 = inflate$1; var inflateRaw_1$1 = inflateRaw$1; var ungzip$1 = inflate$1; var constants = constants$2; var inflate_1$1 = { Inflate: Inflate_1$1, inflate: inflate_2, inflateRaw: inflateRaw_1$1, ungzip: ungzip$1, constants: constants }; const { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1; var inflate_1 = inflate; var ieee754$1 = {}; /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ ieee754$1.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; var eBias = eMax >> 1; var nBits = -7; var i = isLE ? (nBytes - 1) : 0; var d = isLE ? -1 : 1; var s = buffer[offset + i]; i += d; e = s & ((1 << (-nBits)) - 1); s >>= (-nBits); nBits += eLen; for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} m = e & ((1 << (-nBits)) - 1); e >>= (-nBits); nBits += mLen; for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} if (e === 0) { e = 1 - eBias; } else if (e === eMax) { return m ? NaN : ((s ? -1 : 1) * Infinity) } else { m = m + Math.pow(2, mLen); e = e - eBias; } return (s ? -1 : 1) * m * Math.pow(2, e - mLen) }; ieee754$1.write = function (buffer, value, offset, isLE, mLen, nBytes) { var e, m, c; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; var eBias = eMax >> 1; var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0); var i = isLE ? 0 : (nBytes - 1); var d = isLE ? 1 : -1; var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; value = Math.abs(value); if (isNaN(value) || value === Infinity) { m = isNaN(value) ? 1 : 0; e = eMax; } else { e = Math.floor(Math.log(value) / Math.LN2); if (value * (c = Math.pow(2, -e)) < 1) { e--; c *= 2; } if (e + eBias >= 1) { value += rt / c; } else { value += rt * Math.pow(2, 1 - eBias); } if (value * c >= 2) { e++; c /= 2; } if (e + eBias >= eMax) { m = 0; e = eMax; } else if (e + eBias >= 1) { m = ((value * c) - 1) * Math.pow(2, mLen); e = e + eBias; } else { m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); e = 0; } } for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} e = (e << mLen) | m; eLen += mLen; for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} buffer[offset + i - d] |= s * 128; }; var pbf = Pbf; var ieee754 = ieee754$1; function Pbf(buf) { this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); this.pos = 0; this.type = 0; this.length = this.buf.length; } Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string // data structures (which currently switch structure types at 12 bytes or more) var TEXT_DECODER_MIN_LENGTH = 12; var utf8TextDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf8"); Pbf.prototype = { destroy: function() { this.buf = null; }, // === READING ================================================================= readFields: function(readField, result, end) { end = end || this.length; while (this.pos < end) { var val = this.readVarint(), tag = val >> 3, startPos = this.pos; this.type = val & 0x7; readField(tag, result, this); if (this.pos === startPos) this.skip(val); } return result; }, readMessage: function(readField, result) { return this.readFields(readField, result, this.readVarint() + this.pos); }, readFixed32: function() { var val = readUInt32(this.buf, this.pos); this.pos += 4; return val; }, readSFixed32: function() { var val = readInt32(this.buf, this.pos); this.pos += 4; return val; }, // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed) readFixed64: function() { var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; this.pos += 8; return val; }, readSFixed64: function() { var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; this.pos += 8; return val; }, readFloat: function() { var val = ieee754.read(this.buf, this.pos, true, 23, 4); this.pos += 4; return val; }, readDouble: function() { var val = ieee754.read(this.buf, this.pos, true, 52, 8); this.pos += 8; return val; }, readVarint: function(isSigned) { var buf = this.buf, val, b; b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val; b = buf[this.pos]; val |= (b & 0x0f) << 28; return readVarintRemainder(val, isSigned, this); }, readVarint64: function() { // for compatibility with v2.0.1 return this.readVarint(true); }, readSVarint: function() { var num = this.readVarint(); return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding }, readBoolean: function() { return Boolean(this.readVarint()); }, readString: function() { var end = this.readVarint() + this.pos; var pos = this.pos; this.pos = end; if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) { // longer strings are fast with the built-in browser TextDecoder API return readUtf8TextDecoder(this.buf, pos, end); } // short strings are fast with our custom implementation return readUtf8(this.buf, pos, end); }, readBytes: function() { var end = this.readVarint() + this.pos, buffer = this.buf.subarray(this.pos, end); this.pos = end; return buffer; }, // verbose for performance reasons; doesn"t affect gzipped size readPackedVarint: function(arr, isSigned) { if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned)); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readVarint(isSigned)); return arr; }, readPackedSVarint: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSVarint()); return arr; }, readPackedBoolean: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readBoolean()); return arr; }, readPackedFloat: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFloat()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFloat()); return arr; }, readPackedDouble: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readDouble()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readDouble()); return arr; }, readPackedFixed32: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFixed32()); return arr; }, readPackedSFixed32: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSFixed32()); return arr; }, readPackedFixed64: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFixed64()); return arr; }, readPackedSFixed64: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSFixed64()); return arr; }, skip: function(val) { var type = val & 0x7; if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; else if (type === Pbf.Fixed32) this.pos += 4; else if (type === Pbf.Fixed64) this.pos += 8; else throw new Error("Unimplemented type: " + type); }, // === WRITING ================================================================= writeTag: function(tag, type) { this.writeVarint((tag << 3) | type); }, realloc: function(min) { var length = this.length || 16; while (length < this.pos + min) length *= 2; if (length !== this.length) { var buf = new Uint8Array(length); buf.set(this.buf); this.buf = buf; this.length = length; } }, finish: function() { this.length = this.pos; this.pos = 0; return this.buf.subarray(0, this.length); }, writeFixed32: function(val) { this.realloc(4); writeInt32(this.buf, val, this.pos); this.pos += 4; }, writeSFixed32: function(val) { this.realloc(4); writeInt32(this.buf, val, this.pos); this.pos += 4; }, writeFixed64: function(val) { this.realloc(8); writeInt32(this.buf, val & -1, this.pos); writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); this.pos += 8; }, writeSFixed64: function(val) { this.realloc(8); writeInt32(this.buf, val & -1, this.pos); writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); this.pos += 8; }, writeVarint: function(val) { val = +val || 0; if (val > 0xfffffff || val < 0) { writeBigVarint(val, this); return; } this.realloc(4); this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = (val >>> 7) & 0x7f; }, writeSVarint: function(val) { this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); }, writeBoolean: function(val) { this.writeVarint(Boolean(val)); }, writeString: function(str) { str = String(str); this.realloc(str.length * 4); this.pos++; // reserve 1 byte for short string length var startPos = this.pos; // write the string directly to the buffer and see how much was written this.pos = writeUtf8(this.buf, str, this.pos); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position this.pos = startPos - 1; this.writeVarint(len); this.pos += len; }, writeFloat: function(val) { this.realloc(4); ieee754.write(this.buf, val, this.pos, true, 23, 4); this.pos += 4; }, writeDouble: function(val) { this.realloc(8); ieee754.write(this.buf, val, this.pos, true, 52, 8); this.pos += 8; }, writeBytes: function(buffer) { var len = buffer.length; this.writeVarint(len); this.realloc(len); for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i]; }, writeRawMessage: function(fn, obj) { this.pos++; // reserve 1 byte for short message length // write the message directly to the buffer and see how much was written var startPos = this.pos; fn(obj, this); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position this.pos = startPos - 1; this.writeVarint(len); this.pos += len; }, writeMessage: function(tag, fn, obj) { this.writeTag(tag, Pbf.Bytes); this.writeRawMessage(fn, obj); }, writePackedVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr); }, writePackedSVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr); }, writePackedBoolean: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr); }, writePackedFloat: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr); }, writePackedDouble: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr); }, writePackedFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr); }, writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); }, writePackedFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr); }, writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); }, writeBytesField: function(tag, buffer) { this.writeTag(tag, Pbf.Bytes); this.writeBytes(buffer); }, writeFixed32Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeFixed32(val); }, writeSFixed32Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeSFixed32(val); }, writeFixed64Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeFixed64(val); }, writeSFixed64Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeSFixed64(val); }, writeVarintField: function(tag, val) { this.writeTag(tag, Pbf.Varint); this.writeVarint(val); }, writeSVarintField: function(tag, val) { this.writeTag(tag, Pbf.Varint); this.writeSVarint(val); }, writeStringField: function(tag, str) { this.writeTag(tag, Pbf.Bytes); this.writeString(str); }, writeFloatField: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeFloat(val); }, writeDoubleField: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeDouble(val); }, writeBooleanField: function(tag, val) { this.writeVarintField(tag, Boolean(val)); } }; function readVarintRemainder(l, s, p) { var buf = p.buf, h, b; b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s); throw new Error("Expected varint not more than 10 bytes"); } function readPackedEnd(pbf) { return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; } function toNum(low, high, isSigned) { if (isSigned) { return high * 0x100000000 + (low >>> 0); } return ((high >>> 0) * 0x100000000) + (low >>> 0); } function writeBigVarint(val, pbf) { var low, high; if (val >= 0) { low = (val % 0x100000000) | 0; high = (val / 0x100000000) | 0; } else { low = ~(-val % 0x100000000); high = ~(-val / 0x100000000); if (low ^ 0xffffffff) { low = (low + 1) | 0; } else { low = 0; high = (high + 1) | 0; } } if (val >= 0x10000000000000000 || val < -0x10000000000000000) { throw new Error("Given varint doesn"t fit into 10 bytes"); } pbf.realloc(10); writeBigVarintLow(low, high, pbf); writeBigVarintHigh(high, pbf); } function writeBigVarintLow(low, high, pbf) { pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos] = low & 0x7f; } function writeBigVarintHigh(high, pbf) { var lsb = (high & 0x07) << 4; pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f; } function makeRoomForExtraLength(startPos, len, pbf) { var extraLen = len <= 0x3fff ? 1 : len <= 0x1fffff ? 2 : len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7)); // if 1 byte isn"t enough for encoding message length, shift the data to the right pbf.realloc(extraLen); for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i]; } function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); } function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); } function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); } function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); } function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); } function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); } function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); } function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); } function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); } // Buffer code below from https://github.com/feross/buffer, MIT-licensed function readUInt32(buf, pos) { return ((buf[pos]) | (buf[pos + 1] << 8) | (buf[pos + 2] << 16)) + (buf[pos + 3] * 0x1000000); } function writeInt32(buf, val, pos) { buf[pos] = val; buf[pos + 1] = (val >>> 8); buf[pos + 2] = (val >>> 16); buf[pos + 3] = (val >>> 24); } function readInt32(buf, pos) { return ((buf[pos]) | (buf[pos + 1] << 8) | (buf[pos + 2] << 16)) + (buf[pos + 3] << 24); } function readUtf8(buf, pos, end) { var str = ""; var i = pos; while (i < end) { var b0 = buf[i]; var c = null; // codepoint var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1; if (i + bytesPerSequence > end) break; var b1, b2, b3; if (bytesPerSequence === 1) { if (b0 < 0x80) { c = b0; } } else if (bytesPerSequence === 2) { b1 = buf[i + 1]; if ((b1 & 0xC0) === 0x80) { c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F); if (c <= 0x7F) { c = null; } } } else if (bytesPerSequence === 3) { b1 = buf[i + 1]; b2 = buf[i + 2]; if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) { c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F); if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) { c = null; } } } else if (bytesPerSequence === 4) { b1 = buf[i + 1]; b2 = buf[i + 2]; b3 = buf[i + 3]; if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F); if (c <= 0xFFFF || c >= 0x110000) { c = null; } } } if (c === null) { c = 0xFFFD; bytesPerSequence = 1; } else if (c > 0xFFFF) { c -= 0x10000; str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800); c = 0xDC00 | c & 0x3FF; } str += String.fromCharCode(c); i += bytesPerSequence; } return str; } function readUtf8TextDecoder(buf, pos, end) { return utf8TextDecoder.decode(buf.subarray(pos, end)); } function writeUtf8(buf, str, pos) { for (var i = 0, c, lead; i < str.length; i++) { c = str.charCodeAt(i); // code point if (c > 0xD7FF && c < 0xE000) { if (lead) { if (c < 0xDC00) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; lead = c; continue; } else { c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; lead = null; } } else { if (c > 0xDBFF || (i + 1 === str.length)) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; } else { lead = c; } continue; } } else if (lead) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; lead = null; } if (c < 0x80) { buf[pos++] = c; } else { if (c < 0x800) { buf[pos++] = c >> 0x6 | 0xC0; } else { if (c < 0x10000) { buf[pos++] = c >> 0xC | 0xE0; } else { buf[pos++] = c >> 0x12 | 0xF0; buf[pos++] = c >> 0xC & 0x3F | 0x80; } buf[pos++] = c >> 0x6 & 0x3F | 0x80; } buf[pos++] = c & 0x3F | 0x80; } } return pos; } var Pbf$1 = /*@__PURE__*/getDefaultExportFromCjs(pbf); /** * Decompress and parse an array buffer containing zipped * json data and return as a json object. * * @description Handles array buffers continaing zipped json * data. * * @param {ArrayBuffer} buffer - Array buffer to decompress. * @returns {Object} Parsed object. */ function decompress(buffer) { const inflated = inflate_1(buffer, { to: "string" }); return JSON.parse(inflated); } /** * Retrieves a resource as an array buffer and returns a promise * to the buffer. * * @description Rejects the promise on request failure. * * @param {string} url - URL for resource to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the array buffer * resource. */ function fetchArrayBuffer(url, abort) { const method = "GET"; const responseType = "arraybuffer"; return xhrFetch(url, method, responseType, [], null, abort); } function xhrFetch(url, method, responseType, headers, body, abort) { const xhr = new XMLHttpRequest(); const promise = new Promise((resolve, reject) => { xhr.open(method, url, true); for (const header of headers) { xhr.setRequestHeader(header.name, header.value); } xhr.responseType = responseType; xhr.timeout = 15000; xhr.onload = () => { var _a; if (xhr.status !== 200) { const error = (_a = xhr.response) !== null && _a !== void 0 ? _a : new MapillaryError(`Response status error: ${url}`); reject(error); } if (!xhr.response) { reject(new MapillaryError(`Response empty: ${url}`)); } resolve(xhr.response); }; xhr.onerror = () => { reject(new MapillaryError(`Request error: ${url}`)); }; xhr.ontimeout = () => { reject(new MapillaryError(`Request timeout: ${url}`)); }; xhr.onabort = () => { reject(new MapillaryError(`Request aborted: ${url}`)); }; xhr.send(method === "POST" ? body : null); }); if (!!abort) { abort.catch(() => { xhr.abort(); }); } return promise; } /** * Read the fields of a protobuf array buffer into a mesh * object. * * @param {ArrayBuffer} buffer - Protobuf array buffer * to read from. * @returns {MeshContract} Mesh object. */ function readMeshPbf(buffer) { const pbf = new Pbf$1(buffer); const mesh = { faces: [], vertices: [] }; return pbf.readFields(readMeshPbfField, mesh); } function readMeshPbfField(tag, mesh, pbf) { if (tag === 1) { mesh.vertices.push(pbf.readFloat()); } else if (tag === 2) { mesh.faces.push(pbf.readVarint()); } else { console.warn(`Unsupported pbf tag (${tag})`); } } const UNDISTORTION_MARGIN_FACTOR = 3; const MIN_DEPTH = 5; const MAX_DEPTH = 200; function convertCameraType(graphCameraType) { switch (graphCameraType) { case "equirectangular": case "spherical": return "spherical"; case "fisheye": return "fisheye"; default: return "perspective"; } } class GraphConverter { clusterReconstruction(source) { const id = null; const colors = []; const coordinates = []; const pointIds = []; const points = source.points; const normalizer = 1 / 255; for (const pointId in points) { if (!points.hasOwnProperty(pointId)) { continue; } pointIds.push(pointId); const point = points[pointId]; const color = point.color; colors.push(color[0] * normalizer); colors.push(color[1] * normalizer); colors.push(color[2] * normalizer); coordinates.push(...point.coordinates); } const lla = source.reference_lla; const reference = { alt: lla.altitude, lat: lla.latitude, lng: lla.longitude, }; return { colors, coordinates, id, pointIds, reference, rotation: [0, 0, 0], }; } coreImage(source) { const geometry = this._geometry(source.geometry); const computedGeometry = this._geometry(source.computed_geometry); const sequence = { id: source.sequence }; const id = source.id; return { computed_geometry: computedGeometry, geometry, id, sequence, }; } /** * Clamps the depth of the points to the [5, 200] meters interval to avoid * strange appearance. * * @param source Source mesh. * @param params Parameters. * @returns Converted mesh. */ mesh(source, params) { const { vertices } = source; const scale = params && params.scale != null ? params.scale : 1; const scaleInv = 1 / scale; const perspective = params ? params.perspective : true; const zMin = scale * MIN_DEPTH; const zMax = scale * MAX_DEPTH; const numVertices = vertices.length / 3; for (let i = 0; i < numVertices; ++i) { const index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; if (perspective) { // Workaround for corner points not being undistorted // during processing for perspective cameras. if (i < 4) { x *= UNDISTORTION_MARGIN_FACTOR; y *= UNDISTORTION_MARGIN_FACTOR; } const zBounded = Math.max(zMin, Math.min(z, zMax)); const factor = zBounded / z; vertices[index + 0] = factor * x * scaleInv; vertices[index + 1] = factor * y * scaleInv; vertices[index + 2] = zBounded * scaleInv; } else { const l = Math.sqrt(x * x + y * y + z * z); const lBounded = Math.max(zMin, Math.min(l, zMax)); const factor = lBounded / l; vertices[index + 0] = factor * x * scaleInv; vertices[index + 1] = factor * y * scaleInv; vertices[index + 2] = factor * z * scaleInv; } } return source; } spatialImage(source) { var _a, _b, _c, _d, _e, _f; source.camera_type = convertCameraType(source.camera_type); source.camera_parameters = (_a = source.camera_parameters) !== null && _a !== void 0 ? _a : []; source.merge_id = source.merge_cc ? source.merge_cc.toString() : null; source.private = null; const thumbUrl = source.camera_type === "spherical" ? source.thumb_2048_url : source.thumb_1024_url; source.thumb = (_b = source.thumb) !== null && _b !== void 0 ? _b : { id: null, url: thumbUrl }; source.cluster = (_c = source.sfm_cluster) !== null && _c !== void 0 ? _c : { id: null, url: null }; source.creator = (_d = source.creator) !== null && _d !== void 0 ? _d : { id: null, username: null }; source.owner = (_e = source.organization) !== null && _e !== void 0 ? _e : { id: null }; source.mesh = (_f = source.mesh) !== null && _f !== void 0 ? _f : { id: null, url: null }; return source; } _geometry(geometry) { const coords = geometry === null || geometry === void 0 ? void 0 : geometry.coordinates; const lngLat = coords ? { lat: coords[1], lng: coords[0], } : null; return lngLat; } } class GraphQueryCreator { constructor() { this.imagesPath = "images"; this.sequencePath = "image_ids"; this._imageTilesPath = "tiles"; this.coreFields = ["computed_geometry", "geometry", "sequence"]; this.idFields = ["id"]; this.spatialFields = [ "altitude", "atomic_scale", "camera_parameters", "camera_type", "captured_at", "compass_angle", "computed_altitude", "computed_compass_angle", "computed_rotation", "creator", "exif_orientation", "height", "merge_cc", "mesh", "organization", "quality_score", "sfm_cluster", "thumb_1024_url", "thumb_2048_url", "width", ]; this.imageTileFields = ["url", "z", "x", "y"]; } images(imageIds, fields) { return `image_ids=${imageIds.join(",")}&fields=${fields.join(",")}`; } imagesS2(cellId, fields) { return `s2=${cellId}&fields=${fields.join(",")}`; } imageTiles(z, fields) { return `z=${z}&fields=${fields.join(",")}`; } imageTilesPath(imageId) { return `${imageId}/${this._imageTilesPath}`; } sequence(sequenceId) { return `sequence_id=${sequenceId}`; } } class GraphDataProvider extends DataProviderBase { constructor(options, geometry, converter, queryCreator) { var _a; super(geometry !== null && geometry !== void 0 ? geometry : new S2GeometryProvider()); this._convert = converter !== null && converter !== void 0 ? converter : new GraphConverter(); this._query = queryCreator !== null && queryCreator !== void 0 ? queryCreator : new GraphQueryCreator(); this._meshParameters = new Map(); this._method = "GET"; const opts = options !== null && options !== void 0 ? options : {}; this._endpoint = (_a = opts.endpoint) !== null && _a !== void 0 ? _a : "https://graph.mapillary.com"; this._accessToken = opts.accessToken; } getCluster(url, abort) { return fetchArrayBuffer(url, abort) .then((buffer) => { const reconstructions = decompress(buffer); if (reconstructions.length < 1) { throw new Error("Cluster reconstruction empty"); } return this._convert .clusterReconstruction(reconstructions[0]); }); } getCoreImages(cellId) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ]; const query = this._query.imagesS2(cellId, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { cell_id: cellId, images: [], }; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); result.images.push(coreImage); } return result; }); } getImageBuffer(url, abort) { return fetchArrayBuffer(url, abort); } getImages(imageIds) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = []; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); const spatialImage = this._convert.spatialImage(item); this._setMeshParameters(spatialImage); const image = Object.assign({}, spatialImage, coreImage); const contract = { node: image, node_id: item.id, }; result.push(contract); } return result; }); } getImageTiles(request) { const fields = [ ...this._query.imageTileFields, ]; const query = this._query.imageTiles(request.z, fields); const url = new URL(this._query.imageTilesPath(request.imageId), this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { node: r.data, node_id: request.imageId, }; return result; }); } getMesh(url, abort) { return fetchArrayBuffer(url, abort) .then((buffer) => { const mesh = readMeshPbf(buffer); return this._convert.mesh(mesh, this._meshParameters.get(url)); }); } getSequence(sequenceId) { const query = this._query.sequence(sequenceId); const url = new URL(this._query.sequencePath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { id: sequenceId, image_ids: r.data.map(item => item.id), }; return result; }); } getSpatialImages(imageIds) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = []; const items = r.data; for (const item of items) { const spatialImage = this._convert.spatialImage(item); this._setMeshParameters(spatialImage); const contract = { node: spatialImage, node_id: item.id, }; result.push(contract); } return result; }); } setAccessToken(accessToken) { this._accessToken = accessToken; } _createHeaders() { const headers = [ { name: "Accept", value: "application/json" }, { name: "Content-Type", value: "application/x-www-form-urlencoded", }, ]; if (this._accessToken) { headers.push({ name: "Authorization", value: `OAuth ${this._accessToken}`, }); } return headers; } _fetchGraphContract(body, url) { const method = this._method; const headers = this._createHeaders(); const query = `${url}?${body}`; return xhrFetch(query, method, "json", headers, null, null) .catch((error) => { const message = this._makeErrorMessage(error); throw new MapillaryError(message); }); } _makeErrorMessage(graphError) { const error = graphError.error; const message = error ? `${error.code} (${error.type}, ${error.fbtrace_id}): ${error.message}` : "Failed to fetch data"; return message; } _setMeshParameters(spatialImage) { var _a; this._meshParameters.set(spatialImage.mesh.url, { perspective: spatialImage.camera_type === "perspective", scale: (_a = spatialImage.atomic_scale) !== null && _a !== void 0 ? _a : 1 }); } } /** * @class Marker * * @classdesc Represents an abstract marker class that should be extended * by marker implementations used in the marker component. */ class Marker { constructor(id, lngLat) { this._id = id; this._lngLat = lngLat; } /** * Get id. * @returns {string} The id of the marker. */ get id() { return this._id; } /** * Get geometry. * * @ignore */ get geometry() { return this._geometry; } /** * Get lngLat. * @returns {LngLat} The geographic coordinates of the marker. */ get lngLat() { return this._lngLat; } /** @ignore */ createGeometry(position) { if (!!this._geometry) { return; } this._createGeometry(position); // update matrix world if raycasting occurs before first render this._geometry.updateMatrixWorld(true); } /** @ignore */ disposeGeometry() { if (!this._geometry) { return; } this._disposeGeometry(); this._geometry = undefined; } /** @ignore */ getInteractiveObjects() { if (!this._geometry) { return []; } return this._getInteractiveObjects(); } /** @ignore */ lerpAltitude(alt, alpha) { if (!this._geometry) { return; } this._geometry.position.z = (1 - alpha) * this._geometry.position.z + alpha * alt; } /** @ignore */ updatePosition(position, lngLat) { if (!!lngLat) { this._lngLat.lat = lngLat.lat; this._lngLat.lng = lngLat.lng; } if (!this._geometry) { return; } this._geometry.position.fromArray(position); this._geometry.updateMatrixWorld(true); } } /** * @class CircleMarker * * @classdesc Non-interactive marker with a flat circle shape. The circle * marker can not be configured to be interactive. * * Circle marker properties can not be updated after creation. * * To create and add one `CircleMarker` with default configuration * and one with configuration use * * @example * ```js * var defaultMarker = new CircleMarker( * "id-1", * { lat: 0, lng: 0, }); * * var configuredMarker = new CircleMarker( * "id-2", * { lat: 0, lng: 0, }, * { * color: "#0ff", * opacity: 0.3, * radius: 0.7, * }); * * markerComponent.add([defaultMarker, configuredMarker]); * ``` */ class CircleMarker extends Marker { constructor(id, lngLat, options) { super(id, lngLat); options = !!options ? options : {}; this._color = options.color != null ? options.color : 0xffffff; this._opacity = options.opacity != null ? options.opacity : 0.4; this._radius = options.radius != null ? options.radius : 1; } _createGeometry(position) { const circle = new Mesh(new CircleGeometry(this._radius, 16), new MeshBasicMaterial({ color: this._color, opacity: this._opacity, transparent: true, })); circle.up.fromArray([0, 0, 1]); circle.renderOrder = -1; const group = new Object3D(); group.add(circle); group.position.fromArray(position); this._geometry = group; } _disposeGeometry() { for (let mesh of this._geometry.children) { mesh.geometry.dispose(); mesh.material.dispose(); } } _getInteractiveObjects() { return []; } } /** * @class SimpleMarker * * @classdesc Interactive marker with ice cream shape. The sphere * inside the ice cream can be configured to be interactive. * * Simple marker properties can not be updated after creation. * * To create and add one `SimpleMarker` with default configuration * (non-interactive) and one interactive with configuration use * * @example * ```js * var defaultMarker = new SimpleMarker( * "id-1", * { lat: 0, lng: 0, }); * * var interactiveMarker = new SimpleMarker( * "id-2", * { lat: 0, lng: 0, }, * { * ballColor: "#00f", * ballOpacity: 0.5, * color: "#00f", * interactive: true, * opacity: 0.3, * radius: 0.7, * }); * * markerComponent.add([defaultMarker, interactiveMarker]); * ``` */ class SimpleMarker extends Marker { constructor(id, lngLat, options) { super(id, lngLat); options = !!options ? options : {}; this._ballColor = options.ballColor != null ? options.ballColor : 0xff0000; this._ballOpacity = options.ballOpacity != null ? options.ballOpacity : 0.8; this._circleToRayAngle = 2; this._color = options.color != null ? options.color : 0xff0000; this._interactive = !!options.interactive; this._opacity = options.opacity != null ? options.opacity : 0.4; this._radius = options.radius != null ? options.radius : 1; } _createGeometry(position) { const radius = this._radius; const height = this._markerHeight(radius); const markerMaterial = new MeshBasicMaterial({ color: this._color, opacity: this._opacity, transparent: true, depthWrite: false, }); const marker = new Mesh(this._createMarkerGeometry(radius, 8, 8), markerMaterial); const interactive = new Mesh(new SphereGeometry(radius / 2, 8, 8), new MeshBasicMaterial({ color: this._ballColor, opacity: this._ballOpacity, transparent: true, })); interactive.position.z = height; interactive.renderOrder = 1; const group = new Object3D(); group.add(interactive); group.add(marker); group.position.fromArray(position); this._geometry = group; } _disposeGeometry() { for (const mesh of this._geometry.children) { mesh.geometry.dispose(); mesh.material.dispose(); } } _getInteractiveObjects() { return this._interactive ? [this._geometry.children[0]] : []; } _markerHeight(radius) { const t = Math.tan(Math.PI - this._circleToRayAngle); return radius * Math.sqrt(1 + t * t); } _createMarkerGeometry(radius, widthSegments, heightSegments) { const height = this._markerHeight(radius); const circleToRayAngle = this._circleToRayAngle; const indexRows = []; const positions = new Float32Array(3 * (widthSegments + 1) * (heightSegments + 1)); let positionIndex = 0; for (let y = 0; y <= heightSegments; ++y) { const indexRow = []; for (let x = 0; x <= widthSegments; ++x) { const u = x / widthSegments * Math.PI * 2; const v = y / heightSegments * Math.PI; let r = radius; if (v > circleToRayAngle) { const t = Math.tan(v - circleToRayAngle); r = radius * Math.sqrt(1 + t * t); } const arrayIndex = 3 * positionIndex; const sinv = Math.sin(v); positions[arrayIndex + 0] = r * Math.cos(u) * sinv; positions[arrayIndex + 1] = r * Math.sin(u) * sinv; positions[arrayIndex + 2] = r * Math.cos(v) + height; indexRow.push(positionIndex++); } indexRows.push(indexRow); } const indices = new Uint16Array(6 * widthSegments * heightSegments); let index = 0; for (let y = 0; y < heightSegments; ++y) { for (let x = 0; x < widthSegments; ++x) { const pi1 = indexRows[y][x + 1]; const pi2 = indexRows[y][x]; const pi3 = indexRows[y + 1][x]; const pi4 = indexRows[y + 1][x + 1]; indices[index++] = pi1; indices[index++] = pi2; indices[index++] = pi4; indices[index++] = pi2; indices[index++] = pi3; indices[index++] = pi4; } } const geometry = new BufferGeometry(); const positionAttribute = new BufferAttribute(positions, 3); geometry.setAttribute("position", positionAttribute); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } } /** * @class Popup * * @classdesc Popup instance for rendering custom HTML content * on top of images. Popups are based on 2D basic image coordinates * (see the {@link Viewer} class documentation for more information about coordinate * systems) and a certain popup is therefore only relevant to a single image. * Popups related to a certain image should be removed when moving * to another image. * * A popup must have both its content and its point or rect set to be * rendered. Popup options can not be updated after creation but the * basic point or rect as well as its content can be changed by calling * the appropriate methods. * * To create and add one `Popup` with default configuration * (tooltip visuals and automatic float) and one with specific options * use * * @example * ```js * var defaultSpan = document.createElement("span"); * defaultSpan.innerHTML = "hello default"; * * var defaultPopup = new Popup(); * defaultPopup.setDOMContent(defaultSpan); * defaultPopup.setBasicPoint([0.3, 0.3]); * * var cleanSpan = document.createElement("span"); * cleanSpan.innerHTML = "hello clean"; * * var cleanPopup = new Popup({ * clean: true, * float: Alignment.Top, * offset: 10, * opacity: 0.7, * }); * * cleanPopup.setDOMContent(cleanSpan); * cleanPopup.setBasicPoint([0.6, 0.6]); * * popupComponent.add([defaultPopup, cleanPopup]); * ``` * * @description Implementation of API methods and API documentation inspired * by/used from https://github.com/mapbox/mapbox-gl-js/blob/v0.38.0/src/ui/popup.js */ class Popup { constructor(options, viewportCoords, dom) { this._options = {}; options = !!options ? options : {}; this._options.capturePointer = options.capturePointer === false ? options.capturePointer : true; this._options.clean = options.clean; this._options.float = options.float; this._options.offset = options.offset; this._options.opacity = options.opacity; this._options.position = options.position; this._dom = !!dom ? dom : new DOM(); this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._notifyChanged$ = new Subject(); } /** * @description Internal observable used by the component to * render the popup when its position or content has changed. * @ignore */ get changed$() { return this._notifyChanged$; } /** * @description Internal method used by the component to * remove all references to the popup. * @ignore */ remove() { if (this._content && this._content.parentNode) { this._content.parentNode.removeChild(this._content); } if (this._container) { this._container.parentNode.removeChild(this._container); delete this._container; } if (this._parentContainer) { delete this._parentContainer; } } /** * Sets a 2D basic image coordinates point to the popup"s anchor, and * moves the popup to it. * * @description Overwrites any previously set point or rect. * * @param {Array} basicPoint - Point in 2D basic image coordinates. * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setBasicPoint(basicPoint) { this._point = basicPoint.slice(); this._rect = null; this._notifyChanged$.next(this); } /** * Sets a 2D basic image coordinates rect to the popup"s anchor, and * moves the popup to it. * * @description Overwrites any previously set point or rect. * * @param {Array} basicRect - Rect in 2D basic image * coordinates ([topLeftX, topLeftY, bottomRightX, bottomRightY]) . * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicRect([0.3, 0.3, 0.5, 0.6]); * * popupComponent.add([popup]); * ``` */ setBasicRect(basicRect) { this._rect = basicRect.slice(); this._point = null; this._notifyChanged$.next(this); } /** * Sets the popup"s content to the element provided as a DOM node. * * @param {Node} htmlNode - A DOM node to be used as content for the popup. * * @example * ```js * var div = document.createElement("div"); * div.innerHTML = "hello image"; * * var popup = new Popup(); * popup.setDOMContent(div); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setDOMContent(htmlNode) { if (this._content && this._content.parentNode) { this._content.parentNode.removeChild(this._content); } const className = "mapillary-popup-content" + (this._options.clean === true ? "-clean" : "") + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); this._content = this._dom.createElement("div", className, this._container); this._content.appendChild(htmlNode); this._notifyChanged$.next(this); } /** * Sets the popup"s content to the HTML provided as a string. * * @description This method does not perform HTML filtering or sanitization, * and must be used only with trusted content. Consider * {@link Popup.setText} if the * content is an untrusted text string. * * @param {string} html - A string representing HTML content for the popup. * * @example * ```js * var popup = new Popup(); * popup.setHTML("
hello image
"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setHTML(html) { const frag = this._dom.document.createDocumentFragment(); const temp = this._dom.createElement("body"); let child; temp.innerHTML = html; while (true) { child = temp.firstChild; if (!child) { break; } frag.appendChild(child); } this.setDOMContent(frag); } /** * Sets the popup"s content to a string of text. * * @description This function creates a Text node in the DOM, so it cannot insert raw HTML. * Use this method for security against XSS if the popup content is user-provided. * * @param {string} text - Textual content for the popup. * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setText(text) { this.setDOMContent(this._dom.document.createTextNode(text)); } /** * @description Internal method for attaching the popup to * its parent container so that it is rendered in the DOM tree. * @ignore */ setParentContainer(parentContainer) { this._parentContainer = parentContainer; } /** * @description Internal method for updating the rendered * position of the popup called by the popup component. * @ignore */ update(renderCamera, size, transform) { if (!this._parentContainer || !this._content) { return; } if (!this._point && !this._rect) { return; } if (!this._container) { this._container = this._dom.createElement("div", "mapillary-popup", this._parentContainer); const showTip = this._options.clean !== true && this._options.float !== exports.Alignment.Center; if (showTip) { const tipClassName = "mapillary-popup-tip" + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); this._tip = this._dom.createElement("div", tipClassName, this._container); this._dom.createElement("div", "mapillary-popup-tip-inner", this._tip); } this._container.appendChild(this._content); this._parentContainer.appendChild(this._container); if (this._options.opacity != null) { this._container.style.opacity = this._options.opacity.toString(); } } let pointPixel = null; let position = this._alignmentToPopupAligment(this._options.position); let float = this._alignmentToPopupAligment(this._options.float); const classList = this._container.classList; if (this._point != null) { pointPixel = this._viewportCoords.basicToCanvasSafe(this._point[0], this._point[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); } else { const alignments = ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"]; let appliedPosition = null; for (const alignment of alignments) { if (classList.contains(`mapillary-popup-float-${alignment}`)) { appliedPosition = alignment; break; } } [pointPixel, position] = this._rectToPixel(this._rect, position, appliedPosition, renderCamera, size, transform); if (!float) { float = position; } } if (pointPixel == null) { this._container.style.display = "none"; return; } this._container.style.display = ""; if (!float) { const width = this._container.offsetWidth; const height = this._container.offsetHeight; const floats = this._pixelToFloats(pointPixel, size, width, height); float = floats.length === 0 ? "top" : floats.join("-"); } const offset = this._normalizeOffset(this._options.offset); pointPixel = [pointPixel[0] + offset[float][0], pointPixel[1] + offset[float][1]]; pointPixel = [Math.round(pointPixel[0]), Math.round(pointPixel[1])]; const floatTranslate = { "bottom": "translate(-50%,0)", "bottom-left": "translate(-100%,0)", "bottom-right": "translate(0,0)", "center": "translate(-50%,-50%)", "left": "translate(-100%,-50%)", "right": "translate(0,-50%)", "top": "translate(-50%,-100%)", "top-left": "translate(-100%,-100%)", "top-right": "translate(0,-100%)", }; for (const key in floatTranslate) { if (!floatTranslate.hasOwnProperty(key)) { continue; } classList.remove(`mapillary-popup-float-${key}`); } classList.add(`mapillary-popup-float-${float}`); this._container.style.transform = `${floatTranslate[float]} translate(${pointPixel[0]}px,${pointPixel[1]}px)`; } _rectToPixel(rect, position, appliedPosition, renderCamera, size, transform) { if (!position) { const width = this._container.offsetWidth; const height = this._container.offsetHeight; const floatOffsets = { "bottom": [0, height / 2], "bottom-left": [-width / 2, height / 2], "bottom-right": [width / 2, height / 2], "left": [-width / 2, 0], "right": [width / 2, 0], "top": [0, -height / 2], "top-left": [-width / 2, -height / 2], "top-right": [width / 2, -height / 2], }; const automaticPositions = ["top", "bottom", "left", "right"]; let largestVisibleArea = [0, null, null]; for (const automaticPosition of automaticPositions) { const autoPointBasic = this._pointFromRectPosition(rect, automaticPosition); const autoPointPixel = this._viewportCoords.basicToCanvasSafe(autoPointBasic[0], autoPointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); if (autoPointPixel == null) { continue; } const floatOffset = floatOffsets[automaticPosition]; const offsetedPosition = [autoPointPixel[0] + floatOffset[0], autoPointPixel[1] + floatOffset[1]]; const staticCoeff = appliedPosition != null && appliedPosition === automaticPosition ? 1 : 0.7; const floats = this._pixelToFloats(offsetedPosition, size, width / staticCoeff, height / (2 * staticCoeff)); if (floats.length === 0 && autoPointPixel[0] > 0 && autoPointPixel[0] < size.width && autoPointPixel[1] > 0 && autoPointPixel[1] < size.height) { return [autoPointPixel, automaticPosition]; } const minX = Math.max(offsetedPosition[0] - width / 2, 0); const maxX = Math.min(offsetedPosition[0] + width / 2, size.width); const minY = Math.max(offsetedPosition[1] - height / 2, 0); const maxY = Math.min(offsetedPosition[1] + height / 2, size.height); const visibleX = Math.max(0, maxX - minX); const visibleY = Math.max(0, maxY - minY); const visibleArea = staticCoeff * visibleX * visibleY; if (visibleArea > largestVisibleArea[0]) { largestVisibleArea[0] = visibleArea; largestVisibleArea[1] = autoPointPixel; largestVisibleArea[2] = automaticPosition; } } if (largestVisibleArea[0] > 0) { return [largestVisibleArea[1], largestVisibleArea[2]]; } } const pointBasic = this._pointFromRectPosition(rect, position); const pointPixel = this._viewportCoords.basicToCanvasSafe(pointBasic[0], pointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); return [pointPixel, position != null ? position : "top"]; } _alignmentToPopupAligment(float) { switch (float) { case exports.Alignment.Bottom: return "bottom"; case exports.Alignment.BottomLeft: return "bottom-left"; case exports.Alignment.BottomRight: return "bottom-right"; case exports.Alignment.Center: return "center"; case exports.Alignment.Left: return "left"; case exports.Alignment.Right: return "right"; case exports.Alignment.Top: return "top"; case exports.Alignment.TopLeft: return "top-left"; case exports.Alignment.TopRight: return "top-right"; default: return null; } } _normalizeOffset(offset) { if (offset == null) { return this._normalizeOffset(0); } if (typeof offset === "number") { // input specifies a radius const sideOffset = offset; const sign = sideOffset >= 0 ? 1 : -1; const cornerOffset = sign * Math.round(Math.sqrt(0.5 * Math.pow(sideOffset, 2))); return { "bottom": [0, sideOffset], "bottom-left": [-cornerOffset, cornerOffset], "bottom-right": [cornerOffset, cornerOffset], "center": [0, 0], "left": [-sideOffset, 0], "right": [sideOffset, 0], "top": [0, -sideOffset], "top-left": [-cornerOffset, -cornerOffset], "top-right": [cornerOffset, -cornerOffset], }; } else { // input specifes a value for each position return { "bottom": offset.bottom || [0, 0], "bottom-left": offset.bottomLeft || [0, 0], "bottom-right": offset.bottomRight || [0, 0], "center": offset.center || [0, 0], "left": offset.left || [0, 0], "right": offset.right || [0, 0], "top": offset.top || [0, 0], "top-left": offset.topLeft || [0, 0], "top-right": offset.topRight || [0, 0], }; } } _pixelToFloats(pointPixel, size, width, height) { const floats = []; if (pointPixel[1] < height) { floats.push("bottom"); } else if (pointPixel[1] > size.height - height) { floats.push("top"); } if (pointPixel[0] < width / 2) { floats.push("right"); } else if (pointPixel[0] > size.width - width / 2) { floats.push("left"); } return floats; } _pointFromRectPosition(rect, position) { const x0 = rect[0]; const x1 = rect[0] < rect[2] ? rect[2] : rect[2] + 1; const y0 = rect[1]; const y1 = rect[3]; switch (position) { case "bottom": return [(x0 + x1) / 2, y1]; case "bottom-left": return [x0, y1]; case "bottom-right": return [x1, y1]; case "center": return [(x0 + x1) / 2, (y0 + y1) / 2]; case "left": return [x0, (y0 + y1) / 2]; case "right": return [x1, (y0 + y1) / 2]; case "top": return [(x0 + x1) / 2, y0]; case "top-left": return [x0, y0]; case "top-right": return [x1, y0]; default: return [(x0 + x1) / 2, y1]; } } } function isBrowser() { return (typeof window !== "undefined" && typeof document !== "undefined"); } function isArraySupported() { return !!(Array.prototype && Array.prototype.concat && Array.prototype.filter && Array.prototype.includes && Array.prototype.indexOf && Array.prototype.join && Array.prototype.map && Array.prototype.push && Array.prototype.pop && Array.prototype.reverse && Array.prototype.shift && Array.prototype.slice && Array.prototype.splice && Array.prototype.sort && Array.prototype.unshift); } function isBlobSupported() { return ("Blob" in window && "URL" in window); } function isFunctionSupported() { return !!(Function.prototype && Function.prototype.apply && Function.prototype.bind); } function isJSONSupported() { return ("JSON" in window && "parse" in JSON && "stringify" in JSON); } function isMapSupported() { return "Map" in window; } function isObjectSupported() { return !!(Object.assign && Object.keys && Object.values); } function isPromiseSupported() { return !!("Promise" in window && Promise.resolve && Promise.reject && Promise.prototype && Promise.prototype.catch && Promise.prototype.then); } function isSetSupported() { return "Set" in window; } let isWebGLSupportedCache = undefined; function isWebGLSupportedCached() { if (isWebGLSupportedCache === undefined) { isWebGLSupportedCache = isWebGLSupported(); } return isWebGLSupportedCache; } function isWebGLSupported() { const attributes = { alpha: false, antialias: false, depth: true, failIfMajorPerformanceCaveat: false, premultipliedAlpha: true, preserveDrawingBuffer: false, stencil: true, }; const canvas = document.createElement("canvas"); const webGL2Context = canvas.getContext("webgl2", attributes); if (!!webGL2Context) { return true; } const context = canvas.getContext("webgl", attributes) || canvas .getContext("experimental-webgl", attributes); if (!context) { return false; } const requiredExtensions = ["OES_standard_derivatives"]; const supportedExtensions = context.getSupportedExtensions(); for (const requiredExtension of requiredExtensions) { if (supportedExtensions.indexOf(requiredExtension) === -1) { return false; } } return true; } /** * Test whether the current browser supports the full * functionality of MapillaryJS. * * @description The full functionality includes WebGL rendering. * * @return {boolean} * * @example `var supported = isSupported();` */ function isSupported() { return isFallbackSupported() && isWebGLSupportedCached(); } /** * Test whether the current browser supports the fallback * functionality of MapillaryJS. * * @description The fallback functionality does not include WebGL * rendering, only 2D canvas rendering. * * @return {boolean} * * @example `var fallbackSupported = isFallbackSupported();` */ function isFallbackSupported() { return isBrowser() && isArraySupported() && isBlobSupported() && isFunctionSupported() && isJSONSupported() && isMapSupported() && isObjectSupported() && isPromiseSupported() && isSetSupported(); } /** * Enumeration for camera controls. * * @description Specifies different modes for how the * camera is controlled through pointer, keyboard or * other modes of input. * * @enum {number} * @readonly */ exports.CameraControls = void 0; (function (CameraControls) { /** * Control the camera with custom logic by * attaching a custom camera controls * instance to the {@link Viewer}. */ CameraControls[CameraControls["Custom"] = 0] = "Custom"; /** * Control the camera from a birds perspective * to get an overview. */ CameraControls[CameraControls["Earth"] = 1] = "Earth"; /** * Control the camera in a first person view * from the street level perspective. * * @description The virtual viewer camera will * be rotated according to the orientation of * the images. */ CameraControls[CameraControls["Street"] = 2] = "Street"; /** * Control the camera in a first person view * from the street level perspective. * * @description The virtual viewer camera will * maintain gravity alignment for its up vector * at all times. */ CameraControls[CameraControls["Gravity"] = 3] = "Gravity"; })(exports.CameraControls || (exports.CameraControls = {})); /** * Enumeration for render mode * @enum {number} * @readonly * @description Modes for specifying how rendering is done * in the viewer. All modes preserves the original aspect * ratio of the images. */ exports.RenderMode = void 0; (function (RenderMode) { /** * Displays all content within the viewer. * * @description Black bars shown on both * sides of the content. Bars are shown * either below and above or to the left * and right of the content depending on * the aspect ratio relation between the * image and the viewer. */ RenderMode[RenderMode["Letterbox"] = 0] = "Letterbox"; /** * Fills the viewer by cropping content. * * @description Cropping is done either * in horizontal or vertical direction * depending on the aspect ratio relation * between the image and the viewer. */ RenderMode[RenderMode["Fill"] = 1] = "Fill"; })(exports.RenderMode || (exports.RenderMode = {})); exports.RenderPass = void 0; (function (RenderPass) { /** * Occurs after the background render pass. */ RenderPass[RenderPass["Opaque"] = 0] = "Opaque"; /** * Occurs last in the render sequence, after the opaque render pass. */ RenderPass[RenderPass["Transparent"] = 1] = "Transparent"; })(exports.RenderPass || (exports.RenderPass = {})); /** * Enumeration for transition mode * @enum {number} * @readonly * @description Modes for specifying how transitions * between images are performed. */ exports.TransitionMode = void 0; (function (TransitionMode) { /** * Default transitions. * * @description The viewer dynamically determines * whether transitions should be performed with or * without motion and blending for each transition * based on the underlying data. */ TransitionMode[TransitionMode["Default"] = 0] = "Default"; /** * Instantaneous transitions. * * @description All transitions are performed * without motion or blending. */ TransitionMode[TransitionMode["Instantaneous"] = 1] = "Instantaneous"; })(exports.TransitionMode || (exports.TransitionMode = {})); class ComponentController { constructor(container, navigator, observer, key, options, componentService) { this._container = container; this._observer = observer; this._navigator = navigator; this._options = options != null ? options : {}; this._key = key; this._navigable = key == null; this._componentService = !!componentService ? componentService : new ComponentService(this._container, this._navigator); this._coverComponent = this._componentService.getCover(); this._initializeComponents(); if (key) { this._initilizeCoverComponent(); this._subscribeCoverComponent(); } else { this._navigator.movedToId$.pipe(first((k) => { return k != null; })) .subscribe((k) => { this._key = k; this._componentService.deactivateCover(); this._coverComponent.configure({ id: this._key, state: CoverState.Hidden, }); this._subscribeCoverComponent(); this._navigator.stateService.start(); this._navigator.cacheService.start(); this._navigator.panService.start(); this._observer.startEmit(); }); } } get navigable() { return this._navigable; } get(name) { return this._componentService.get(name); } activate(name) { this._componentService.activate(name); } activateCover() { this._coverComponent.configure({ state: CoverState.Visible }); } deactivate(name) { this._componentService.deactivate(name); } deactivateCover() { this._coverComponent.configure({ state: CoverState.Loading }); } remove() { this._componentService.remove(); if (this._configurationSubscription != null) { this._configurationSubscription.unsubscribe(); } } _initializeComponents() { var _a, _b; const options = this._options; this._uFalse((_a = options.fallback) === null || _a === void 0 ? void 0 : _a.image, "imagefallback"); this._uFalse((_b = options.fallback) === null || _b === void 0 ? void 0 : _b.navigation, "navigationfallback"); this._uFalse(options.marker, "marker"); this._uFalse(options.popup, "popup"); this._uFalse(options.spatial, "spatial"); this._uFalse(options.tag, "tag"); this._uTrue(options.attribution, "attribution"); this._uTrue(options.bearing, "bearing"); this._uTrue(options.cache, "cache"); this._uTrue(options.direction, "direction"); this._uTrue(options.image, "image"); this._uTrue(options.keyboard, "keyboard"); this._uTrue(options.pointer, "pointer"); this._uTrue(options.sequence, "sequence"); this._uTrue(options.zoom, "zoom"); } _initilizeCoverComponent() { let options = this._options; this._coverComponent.configure({ id: this._key }); if (options.cover === undefined || options.cover) { this.activateCover(); } else { this.deactivateCover(); } } _setNavigable(navigable) { if (this._navigable === navigable) { return; } this._navigable = navigable; this._observer.navigable$.next(navigable); } _subscribeCoverComponent() { this._configurationSubscription = this._coverComponent.configuration$.pipe(distinctUntilChanged(undefined, (c) => { return c.state; })) .subscribe((conf) => { if (conf.state === CoverState.Loading) { this._navigator.stateService.currentId$.pipe(first(), switchMap((key) => { const keyChanged = key == null || key !== conf.id; if (keyChanged) { this._setNavigable(false); } return keyChanged ? this._navigator.moveTo$(conf.id) : this._navigator.stateService.currentImage$.pipe(first()); })) .subscribe(() => { this._navigator.stateService.start(); this._navigator.cacheService.start(); this._navigator.panService.start(); this._observer.startEmit(); this._coverComponent.configure({ state: CoverState.Hidden }); this._componentService.deactivateCover(); this._setNavigable(true); }, (error) => { console.error("Failed to deactivate cover.", error); this._coverComponent.configure({ state: CoverState.Visible }); }); } else if (conf.state === CoverState.Visible) { this._observer.stopEmit(); this._navigator.stateService.stop(); this._navigator.cacheService.stop(); this._navigator.playService.stop(); this._navigator.panService.stop(); this._componentService.activateCover(); this._setNavigable(conf.id == null); } }); } _uFalse(option, name) { if (option === undefined) { this._componentService.deactivate(name); return; } if (typeof option === "boolean") { if (option) { this._componentService.activate(name); } else { this._componentService.deactivate(name); } return; } this._componentService.configure(name, option); this._componentService.activate(name); } _uTrue(option, name) { if (option === undefined) { this._componentService.activate(name); return; } if (typeof option === "boolean") { if (option) { this._componentService.activate(name); } else { this._componentService.deactivate(name); } return; } this._componentService.configure(name, option); this._componentService.activate(name); } } class DOMRenderer { constructor(element, renderService, currentFrame$) { this._adaptiveOperation$ = new Subject(); this._render$ = new Subject(); this._renderAdaptive$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._renderService = renderService; this._currentFrame$ = currentFrame$; const subs = this._subscriptions; const rootNode = virtualDom.create(virtualDom.h("div.mapillary-dom-renderer", [])); element.appendChild(rootNode); this._offset$ = this._adaptiveOperation$.pipe(scan((adaptive, operation) => { return operation(adaptive); }, { elementHeight: element.offsetHeight, elementWidth: element.offsetWidth, imageAspect: 0, renderMode: exports.RenderMode.Fill, }), filter((adaptive) => { return adaptive.imageAspect > 0 && adaptive.elementWidth > 0 && adaptive.elementHeight > 0; }), map((adaptive) => { const elementAspect = adaptive.elementWidth / adaptive.elementHeight; const ratio = adaptive.imageAspect / elementAspect; let verticalOffset = 0; let horizontalOffset = 0; if (adaptive.renderMode === exports.RenderMode.Letterbox) { if (adaptive.imageAspect > elementAspect) { verticalOffset = adaptive.elementHeight * (1 - 1 / ratio) / 2; } else { horizontalOffset = adaptive.elementWidth * (1 - ratio) / 2; } } else { if (adaptive.imageAspect > elementAspect) { horizontalOffset = -adaptive.elementWidth * (ratio - 1) / 2; } else { verticalOffset = -adaptive.elementHeight * (1 / ratio - 1) / 2; } } return { bottom: verticalOffset, left: horizontalOffset, right: horizontalOffset, top: verticalOffset, }; })); const imageAspectSubscription = this._currentFrame$.pipe(filter((frame) => { return frame.state.currentImage != null; }), distinctUntilChanged((k1, k2) => { return k1 === k2; }, (frame) => { return frame.state.currentImage.id; }), map((frame) => { return frame.state.currentTransform.basicAspect; }), map((aspect) => { return (adaptive) => { adaptive.imageAspect = aspect; return adaptive; }; })) .subscribe(this._adaptiveOperation$); const renderAdaptiveSubscription = combineLatest(this._renderAdaptive$.pipe(scan((vNodeHashes, vNodeHash) => { if (vNodeHash.vNode == null) { delete vNodeHashes[vNodeHash.name]; } else { vNodeHashes[vNodeHash.name] = vNodeHash.vNode; } return vNodeHashes; }, {})), this._offset$).pipe(map((vo) => { const vNodes = []; const hashes = vo[0]; for (const name in hashes) { if (!hashes.hasOwnProperty(name)) { continue; } vNodes.push(hashes[name]); } const offset = vo[1]; const properties = { style: { bottom: offset.bottom + "px", left: offset.left + "px", "pointer-events": "none", position: "absolute", right: offset.right + "px", top: offset.top + "px", }, }; return { name: "mapillary-dom-adaptive-renderer", vNode: virtualDom.h("div.mapillary-dom-adaptive-renderer", properties, vNodes), }; })) .subscribe(this._render$); this._vNode$ = this._render$.pipe(scan((vNodeHashes, vNodeHash) => { if (vNodeHash.vNode == null) { delete vNodeHashes[vNodeHash.name]; } else { vNodeHashes[vNodeHash.name] = vNodeHash.vNode; } return vNodeHashes; }, {}), map((hashes) => { const vNodes = []; for (const name in hashes) { if (!hashes.hasOwnProperty(name)) { continue; } vNodes.push(hashes[name]); } return virtualDom.h("div.mapillary-dom-renderer", vNodes); })); this._vPatch$ = this._vNode$.pipe(scan((nodePatch, vNode) => { nodePatch.vpatch = virtualDom.diff(nodePatch.vNode, vNode); nodePatch.vNode = vNode; return nodePatch; }, { vNode: virtualDom.h("div.mapillary-dom-renderer", []), vpatch: null }), pluck("vpatch")); this._element$ = this._vPatch$.pipe(scan((oldElement, vPatch) => { return virtualDom.patch(oldElement, vPatch); }, rootNode), publishReplay(1), refCount()); subs.push(imageAspectSubscription); subs.push(renderAdaptiveSubscription); subs.push(this._element$.subscribe(() => { })); subs.push(this._renderService.size$.pipe(map((size) => { return (adaptive) => { adaptive.elementWidth = size.width; adaptive.elementHeight = size.height; return adaptive; }; })) .subscribe(this._adaptiveOperation$)); subs.push(this._renderService.renderMode$.pipe(map((renderMode) => { return (adaptive) => { adaptive.renderMode = renderMode; return adaptive; }; })) .subscribe(this._adaptiveOperation$)); } get element$() { return this._element$; } get render$() { return this._render$; } get renderAdaptive$() { return this._renderAdaptive$; } clear(name) { this._renderAdaptive$.next({ name: name, vNode: null }); this._render$.next({ name: name, vNode: null }); } remove() { this._subscriptions.unsubscribe(); } } class GLRenderer { constructor(canvas, canvasContainer, renderService) { this._renderFrame$ = new Subject(); this._renderCameraOperation$ = new Subject(); this._render$ = new Subject(); this._clear$ = new Subject(); this._renderOperation$ = new Subject(); this._rendererOperation$ = new Subject(); this._eraserOperation$ = new Subject(); this._triggerOperation$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._opaqueRender$ = new Subject(); this._transparentRender$ = new Subject(); this._renderService = renderService; const subs = this._subscriptions; this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { return operation(renderer); }, { needsRender: false, renderer: null }), filter((renderer) => { return !!renderer.renderer; })); this._renderCollection$ = this._renderOperation$.pipe(scan((hashes, operation) => { return operation(hashes); }, {}), share()); this._renderCamera$ = this._renderCameraOperation$.pipe(scan((rc, operation) => { return operation(rc); }, { frameId: -1, needsRender: false, perspective: null })); this._eraser$ = this._eraserOperation$.pipe(startWith((eraser) => { return eraser; }), scan((eraser, operation) => { return operation(eraser); }, { needsRender: false })); const trigger$ = this._triggerOperation$.pipe(startWith((trigger) => { return trigger; }), scan((trigger, operation) => { return operation(trigger); }, { needsRender: false })); const clearColor = new Color(0x0F0F0F); const renderSubscription = combineLatest(this._renderer$, this._renderCollection$, this._renderCamera$, this._eraser$, trigger$).pipe(map(([renderer, hashes, rc, eraser, trigger]) => { const renders = Object.keys(hashes) .map((key) => { return hashes[key]; }); return { camera: rc, eraser: eraser, trigger: trigger, renderer: renderer, renders: renders }; }), filter((co) => { let needsRender = co.renderer.needsRender || co.camera.needsRender || co.eraser.needsRender || co.trigger.needsRender; const frameId = co.camera.frameId; for (const render of co.renders) { if (render.frameId !== frameId) { return false; } needsRender = needsRender || render.needsRender; } return needsRender; }), distinctUntilChanged((n1, n2) => { return n1 === n2; }, (co) => { return co.eraser.needsRender || co.trigger.needsRender ? -co.camera.frameId : co.camera.frameId; })) .subscribe((co) => { co.renderer.needsRender = false; co.camera.needsRender = false; co.eraser.needsRender = false; co.trigger.needsRender = false; const perspectiveCamera = co.camera.perspective; const backgroundRenders = []; const opaqueRenders = []; for (const render of co.renders) { if (render.pass === RenderPass.Background) { backgroundRenders.push(render.render); } else if (render.pass === RenderPass.Opaque) { opaqueRenders.push(render.render); } } const renderer = co.renderer.renderer; renderer.resetState(); renderer.setClearColor(clearColor, 1.0); renderer.clear(); for (const renderBackground of backgroundRenders) { renderBackground(perspectiveCamera, renderer); } renderer.clearDepth(); for (const renderOpaque of opaqueRenders) { renderOpaque(perspectiveCamera, renderer); } renderer.resetState(); this._opaqueRender$.next(); this._transparentRender$.next(); }); subs.push(renderSubscription); subs.push(this._renderFrame$.pipe(map((rc) => { return (irc) => { irc.frameId = rc.frameId; irc.perspective = rc.perspective; if (rc.changed === true) { irc.needsRender = true; } return irc; }; })) .subscribe(this._renderCameraOperation$)); this._renderFrameSubscribe(); const renderHash$ = this._render$.pipe(map((hash) => { return (hashes) => { hashes[hash.name] = hash.renderer; return hashes; }; })); const clearHash$ = this._clear$.pipe(map((name) => { return (hashes) => { delete hashes[name]; return hashes; }; })); subs.push(merge(renderHash$, clearHash$) .subscribe(this._renderOperation$)); this._webGLRenderer$ = this._render$.pipe(first(), map(() => { canvasContainer.appendChild(canvas); const element = renderService.element; const webGLRenderer = new WebGLRenderer({ antialias: true, canvas: canvas, }); webGLRenderer.setPixelRatio(window.devicePixelRatio); webGLRenderer.setSize(element.offsetWidth, element.offsetHeight); webGLRenderer.autoClear = false; return webGLRenderer; }), publishReplay(1), refCount()); subs.push(this._webGLRenderer$ .subscribe(() => { })); const createRenderer$ = this._webGLRenderer$.pipe(first(), map((webGLRenderer) => { return (renderer) => { renderer.needsRender = true; renderer.renderer = webGLRenderer; return renderer; }; })); const resizeRenderer$ = this._renderService.size$.pipe(map((size) => { return (renderer) => { if (renderer.renderer == null) { return renderer; } renderer.renderer.setSize(size.width, size.height); renderer.needsRender = true; return renderer; }; })); const clearRenderer$ = this._clear$.pipe(map(() => { return (renderer) => { if (renderer.renderer == null) { return renderer; } renderer.needsRender = true; return renderer; }; })); subs.push(merge(createRenderer$, resizeRenderer$, clearRenderer$) .subscribe(this._rendererOperation$)); const renderCollectionEmpty$ = this._renderCollection$.pipe(filter((hashes) => { return Object.keys(hashes).length === 0; }), share()); subs.push(renderCollectionEmpty$ .subscribe(() => { if (this._renderFrameSubscription == null) { return; } this._renderFrameSubscription.unsubscribe(); this._renderFrameSubscription = null; this._renderFrameSubscribe(); })); subs.push(renderCollectionEmpty$.pipe(map(() => { return (eraser) => { eraser.needsRender = true; return eraser; }; })) .subscribe(this._eraserOperation$)); } get render$() { return this._render$; } get opaqueRender$() { return this._opaqueRender$; } get transparentRender$() { return this._transparentRender$; } get webGLRenderer$() { return this._webGLRenderer$; } clear(name) { this._clear$.next(name); } remove() { this._rendererOperation$.next((renderer) => { if (renderer.renderer != null) { const extension = renderer.renderer .getContext() .getExtension("WEBGL_lose_context"); if (!!extension) { extension.loseContext(); } renderer.renderer = null; } return renderer; }); if (this._renderFrameSubscription != null) { this._renderFrameSubscription.unsubscribe(); } this._subscriptions.unsubscribe(); } triggerRerender() { this._renderService.renderCameraFrame$ .pipe(skip(1), first()) .subscribe(() => { this._triggerOperation$.next((trigger) => { trigger.needsRender = true; return trigger; }); }); } _renderFrameSubscribe() { this._render$.pipe(first(), map(() => { return (irc) => { irc.needsRender = true; return irc; }; })) .subscribe((operation) => { this._renderCameraOperation$.next(operation); }); this._renderFrameSubscription = this._render$.pipe(first(), mergeMap(() => { return this._renderService.renderCameraFrame$; })) .subscribe(this._renderFrame$); } } /** * @class Camera * * @classdesc Holds information about a camera. */ class Camera { /** * Create a new camera instance. * @param {Transform} [transform] - Optional transform instance. */ constructor(transform) { if (transform != null) { this._position = new Vector3().fromArray(transform.unprojectSfM([0, 0], 0)); this._lookat = new Vector3().fromArray(transform.unprojectSfM([0, 0], 10)); this._up = transform.upVector(); this._focal = this._getFocal(transform); } else { this._position = new Vector3(0, 0, 0); this._lookat = new Vector3(1, 0, 0); this._up = new Vector3(0, 0, 1); this._focal = 1; } } /** * Get position. * @returns {THREE.Vector3} The position vector. */ get position() { return this._position; } /** * Get lookat. * @returns {THREE.Vector3} The lookat vector. */ get lookat() { return this._lookat; } /** * Get up. * @returns {THREE.Vector3} The up vector. */ get up() { return this._up; } /** * Get focal. * @returns {number} The focal length. */ get focal() { return this._focal; } /** * Set focal. */ set focal(value) { this._focal = value; } /** * Update this camera to the linearly interpolated value of two other cameras. * * @param {Camera} a - First camera. * @param {Camera} b - Second camera. * @param {number} alpha - Interpolation value on the interval [0, 1]. */ lerpCameras(a, b, alpha) { this._position.subVectors(b.position, a.position).multiplyScalar(alpha).add(a.position); this._lookat.subVectors(b.lookat, a.lookat).multiplyScalar(alpha).add(a.lookat); this._up.subVectors(b.up, a.up).multiplyScalar(alpha).add(a.up); this._focal = (1 - alpha) * a.focal + alpha * b.focal; } /** * Copy the properties of another camera to this camera. * * @param {Camera} other - Another camera. */ copy(other) { this._position.copy(other.position); this._lookat.copy(other.lookat); this._up.copy(other.up); this._focal = other.focal; } /** * Clone this camera. * * @returns {Camera} A camera with cloned properties equal to this camera. */ clone() { let camera = new Camera(); camera.position.copy(this._position); camera.lookat.copy(this._lookat); camera.up.copy(this._up); camera.focal = this._focal; return camera; } /** * Determine the distance between this camera and another camera. * * @param {Camera} other - Another camera. * @returns {number} The distance between the cameras. */ diff(other) { let pd = this._position.distanceToSquared(other.position); let ld = this._lookat.distanceToSquared(other.lookat); let ud = this._up.distanceToSquared(other.up); let fd = 100 * Math.abs(this._focal - other.focal); return Math.max(pd, ld, ud, fd); } /** * Get the focal length based on the transform. * * @description Returns the focal length corresponding * to a 90 degree field of view for spherical * transforms. * * Returns the transform focal length for other * projection types. * * @returns {number} Focal length. */ _getFocal(transform) { if (!isSpherical(transform.cameraType)) { return transform.focal; } return 0.5 / Math.tan(Math.PI / 2); } } class RenderCamera { constructor(elementWidth, elementHeight, renderMode) { this._spatial = new Spatial(); this._viewportCoords = new ViewportCoords(); this._size = { width: elementWidth, height: elementHeight }; this._initialFov = 60; this._alpha = -1; this._stateTransitionAlpha = -1; this._stateTransitionFov = -1; this._renderMode = renderMode; this._zoom = 0; this._frameId = -1; this._changed = false; this._changedForFrame = -1; this._currentImageId = null; this._previousImageId = null; this._currentSpherical = false; this._previousSpherical = false; this._state = null; this._currentBearings = []; this._previousBearings = []; this._currentFov = this._initialFov; this._previousFov = this._initialFov; this._camera = new Camera(); this._currentCameraUp = new Vector3(); this._previousCameraUp = new Vector3(); this._currentTransformUp = new Vector3(); this._previousTransformUp = new Vector3(); this._currentTransformForward = new Vector3(); this._previousTransformForward = new Vector3(); this._perspective = new PerspectiveCamera$1(this._initialFov, this._computeAspect(elementWidth, elementHeight), 1e-3, 1e6); this._perspective.position.copy(this._camera.position); this._perspective.up.copy(this._camera.up); this._perspective.lookAt(this._camera.lookat); this._perspective.updateMatrixWorld(true); this._perspective.matrixAutoUpdate = false; this._rotation = { phi: 0, theta: 0 }; } get alpha() { return this._alpha; } get camera() { return this._camera; } get changed() { return this._frameId === this._changedForFrame; } get frameId() { return this._frameId; } get perspective() { return this._perspective; } get renderMode() { return this._renderMode; } get rotation() { return this._rotation; } get zoom() { return this._zoom; } get size() { return this._size; } getTilt() { return 90 - this._spatial.radToDeg(this._rotation.theta); } fovToZoom(fov) { fov = Math.min(90, Math.max(0, fov)); const currentFov = this._computeCurrentFov(0); const actualFov = this._alpha === 1 ? currentFov : this._interpolateFov(currentFov, this._computePreviousFov(0), this._alpha); const y0 = Math.tan(actualFov / 2 * Math.PI / 180); const y1 = Math.tan(fov / 2 * Math.PI / 180); const zoom = Math.log(y0 / y1) / Math.log(2); return zoom; } setFrame(frame) { const state = frame.state; if (state.state !== this._state) { this._state = state.state; if (this._state !== State.Custom) { this.setRenderMode(this._renderMode); this.setSize(this._size); } if (this._state === State.Earth) { this._stateTransitionFov = this._zoomedFovToFov(this._perspective.fov, this._zoom); } this._changed = true; } const currentImageId = state.currentImage.id; const previousImageId = !!state.previousImage ? state.previousImage.id : null; if (currentImageId !== this._currentImageId || this._changed) { this._currentImageId = currentImageId; this._currentSpherical = isSpherical(state.currentTransform.cameraType); this._currentBearings = this._computeBearings(state.currentTransform); this._currentCameraUp.copy(state.currentCamera.up); this._currentTransformUp.copy(state.currentTransform.upVector()); this._currentTransformForward.copy(new Vector3().fromArray(state.currentTransform.unprojectSfM([0, 0], 10)) .sub(new Vector3().fromArray(state.currentTransform. unprojectSfM([0, 0], 0)))); this._changed = true; } if (previousImageId !== this._previousImageId) { this._previousImageId = previousImageId; this._previousSpherical = isSpherical(state.previousTransform.cameraType); this._previousBearings = this._computeBearings(state.previousTransform); this._previousCameraUp.copy(state.previousCamera.up); this._previousTransformUp.copy(state.previousTransform.upVector()); this._previousTransformForward.copy(new Vector3().fromArray(state.previousTransform.unprojectSfM([0, 0], 10)) .sub(new Vector3().fromArray(state.previousTransform. unprojectSfM([0, 0], 0)))); this._changed = true; } const zoom = state.zoom; if (zoom !== this._zoom) { this._changed = true; } const camera = state.camera; if (this._changed) { this._currentFov = this._computeCurrentFov(zoom); this._previousFov = this._computePreviousFov(zoom); } const alpha = state.alpha; const sta = state.stateTransitionAlpha; if (this._changed || alpha !== this._alpha || sta !== this._stateTransitionAlpha) { this._alpha = alpha; this._stateTransitionAlpha = sta; switch (this._state) { case State.Earth: { const startFov = this._stateTransitionFov; const endFov = this._focalToFov(state.camera.focal); const fov = MathUtils.lerp(startFov, endFov, sta); this._perspective.fov = this._fovToZoomedFov(fov, zoom); break; } case State.Custom: break; default: this._perspective.fov = this._interpolateFov(this._currentFov, this._previousFov, this._alpha); this._changed = true; break; } this._zoom = zoom; if (this._state !== State.Custom) { this._perspective.updateProjectionMatrix(); } } if (this._camera.diff(camera) > 1e-9) { this._camera.copy(camera); this._rotation = this._computeRotation(camera); this._perspective.up.copy(camera.up); this._perspective.position.copy(camera.position); // Workaround for shaking camera this._perspective.matrixAutoUpdate = true; this._perspective.lookAt(camera.lookat); this._perspective.matrixAutoUpdate = false; this._perspective.updateMatrix(); this._perspective.updateMatrixWorld(false); this._changed = true; } this._setFrameId(frame.id); } setProjectionMatrix(matrix) { this._perspective.fov = this._focalToFov(matrix[5] / 2); this._perspective.projectionMatrix.fromArray(matrix); this._perspective.projectionMatrixInverse .copy(this._perspective.projectionMatrix) .invert(); this._changed = true; } setRenderMode(renderMode) { this._renderMode = renderMode; if (this._state === State.Custom) { return; } this._perspective.fov = this._computeFov(); this._perspective.updateProjectionMatrix(); this._changed = true; } setSize(size) { this._size = size; if (this._state === State.Custom) { return; } this._perspective.aspect = this._computeAspect(size.width, size.height); this._perspective.fov = this._computeFov(); this._perspective.updateProjectionMatrix(); this._changed = true; } _computeAspect(elementWidth, elementHeight) { return elementWidth === 0 ? 0 : elementWidth / elementHeight; } _computeCurrentFov(zoom) { if (this._perspective.aspect === 0) { return 0; } if (!this._currentImageId) { return this._initialFov; } return this._currentSpherical ? this._fovToZoomedFov(90, zoom) : this._computeVerticalBearingFov(this._currentBearings, this._renderMode, zoom, this.perspective.aspect); } _computeFov() { this._currentFov = this._computeCurrentFov(this._zoom); this._previousFov = this._computePreviousFov(this._zoom); return this._interpolateFov(this._currentFov, this._previousFov, this._alpha); } _computePreviousFov(zoom) { if (this._perspective.aspect === 0) { return 0; } if (!this._currentImageId) { return this._initialFov; } return !this._previousImageId ? this._currentFov : this._previousSpherical ? this._fovToZoomedFov(90, zoom) : this._computeVerticalBearingFov(this._previousBearings, this._renderMode, zoom, this.perspective.aspect); } _computeBearings(transform) { const vertices = [[0, 0]]; const directions = [[1, 0]]; const pointsPerLine = 25; return computeBearings(transform, vertices, directions, pointsPerLine, this._viewportCoords); } _computeRotation(camera) { let direction = camera.lookat.clone().sub(camera.position); let up = camera.up.clone(); let phi = this._spatial.azimuthal(direction.toArray(), up.toArray()); let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); return { phi: phi, theta: theta }; } _computeVerticalBearingFov(bearings, renderMode, zoom, aspect) { const { _spatial } = this; const projections = bearings .map(b => _spatial.projectToPlane(b, [1, 0, 0])) .map(p => [p[1], -p[2]]); const fovs = projections.map(p => 2 * _spatial.radToDeg(Math.abs(Math.atan2(p[0], p[1])))); const vFovMin = fovs.length > 0 ? 0.995 * Math.min(...fovs) : 125; const fovMin = this._fovToZoomedFov(vFovMin, zoom); const fovMax = this._fovToZoomedFov(125, zoom); const vFovFill = Math.min(fovMin, fovMax); if (renderMode === exports.RenderMode.Fill) { return vFovFill; } const vFovLetterbox = Math.max(...fovs); const hFovLetterbox = aspect * vFovLetterbox; const fovCoeff = 1.25; const hFovFill = aspect * vFovFill; const hFovMax = fovCoeff * hFovFill; let vFov = vFovLetterbox; if (hFovLetterbox > hFovMax) { vFov *= hFovMax / hFovLetterbox; } const vFovMax = fovCoeff * vFovFill; vFov = Math.min(vFov, vFovMax, fovMax); vFov = Math.max(vFov, vFovFill); return vFov; } _fovToZoomedFov(fov, zoom) { return fov / Math.pow(2, zoom); } _zoomedFovToFov(zoomedFov, zoom) { return Math.pow(2, zoom) * zoomedFov; } _focalToFov(focal) { return 2 * Math.atan2(1, 2 * focal) * 180 / Math.PI; } _interpolateFov(v1, v2, alpha) { return alpha * v1 + (1 - alpha) * v2; } _setFrameId(frameId) { this._frameId = frameId; if (this._changed) { this._changed = false; this._changedForFrame = frameId; } } } class RenderService { constructor(element, currentFrame$, renderMode, renderCamera) { this._subscriptions = new SubscriptionHolder(); this._element = element; this._currentFrame$ = currentFrame$; this._spatial = new Spatial(); renderMode = renderMode != null ? renderMode : exports.RenderMode.Fill; this._resize$ = new Subject(); this._projectionMatrix$ = new Subject(); this._renderCameraOperation$ = new Subject(); this._size$ = new BehaviorSubject({ height: this._element.offsetHeight, width: this._element.offsetWidth, }); const subs = this._subscriptions; subs.push(this._resize$.pipe(map(() => { return { height: this._element.offsetHeight, width: this._element.offsetWidth, }; })) .subscribe(this._size$)); this._renderMode$ = new BehaviorSubject(renderMode); this._renderCameraHolder$ = this._renderCameraOperation$.pipe(startWith((rc) => { return rc; }), scan((rc, operation) => { return operation(rc); }, renderCamera !== null && renderCamera !== void 0 ? renderCamera : new RenderCamera(this._element.offsetWidth, this._element.offsetHeight, renderMode)), publishReplay(1), refCount()); this._renderCameraFrame$ = this._currentFrame$.pipe(withLatestFrom(this._renderCameraHolder$), tap(([frame, rc]) => { rc.setFrame(frame); }), map((args) => { return args[1]; }), publishReplay(1), refCount()); this._renderCamera$ = this._renderCameraFrame$.pipe(filter((rc) => { return rc.changed; }), publishReplay(1), refCount()); this._bearing$ = this._renderCamera$.pipe(map((rc) => { let bearing = this._spatial.radToDeg(this._spatial.azimuthalToBearing(rc.rotation.phi)); return this._spatial.wrap(bearing, 0, 360); }), publishReplay(1), refCount()); subs.push(this._size$.pipe(skip(1), map((size) => { return (rc) => { rc.setSize(size); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._renderMode$.pipe(skip(1), map((rm) => { return (rc) => { rc.setRenderMode(rm); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._projectionMatrix$.pipe(map((projectionMatrix) => { return (rc) => { rc.setProjectionMatrix(projectionMatrix); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._bearing$.subscribe(() => { })); subs.push(this._renderCameraHolder$.subscribe(() => { })); subs.push(this._size$.subscribe(() => { })); subs.push(this._renderMode$.subscribe(() => { })); subs.push(this._renderCamera$.subscribe(() => { })); subs.push(this._renderCameraFrame$.subscribe(() => { })); } get bearing$() { return this._bearing$; } get element() { return this._element; } get projectionMatrix$() { return this._projectionMatrix$; } get renderCamera$() { return this._renderCamera$; } get renderCameraFrame$() { return this._renderCameraFrame$; } get renderMode$() { return this._renderMode$; } get resize$() { return this._resize$; } get size$() { return this._size$; } dispose() { this._subscriptions.unsubscribe(); } } class KeyboardService { constructor(canvasContainer) { this._keyDown$ = fromEvent(canvasContainer, "keydown"); this._keyUp$ = fromEvent(canvasContainer, "keyup"); } get keyDown$() { return this._keyDown$; } get keyUp$() { return this._keyUp$; } } // MouseEvent.button const LEFT_BUTTON = 0; const RIGHT_BUTTON = 2; // MouseEvent.buttons const BUTTONS_MAP = { [LEFT_BUTTON]: 1, [RIGHT_BUTTON]: 2 }; class MouseService { constructor(container, canvasContainer, domContainer, doc) { this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._activeSubject$ = new BehaviorSubject(false); this._active$ = this._activeSubject$ .pipe(distinctUntilChanged(), publishReplay(1), refCount()); this._claimMouse$ = new Subject(); this._claimWheel$ = new Subject(); this._deferPixelClaims$ = new Subject(); this._deferPixels$ = this._deferPixelClaims$ .pipe(scan((claims, claim) => { if (claim.deferPixels == null) { delete claims[claim.name]; } else { claims[claim.name] = claim.deferPixels; } return claims; }, {}), map((claims) => { let deferPixelMax = -1; for (const key in claims) { if (!claims.hasOwnProperty(key)) { continue; } const deferPixels = claims[key]; if (deferPixels > deferPixelMax) { deferPixelMax = deferPixels; } } return deferPixelMax; }), startWith(-1), publishReplay(1), refCount()); subs.push(this._deferPixels$.subscribe(() => { })); this._documentMouseMove$ = fromEvent(doc, "pointermove") .pipe(filter(this._isMousePen)); this._documentMouseUp$ = fromEvent(doc, "pointerup") .pipe(filter(this._isMousePen)); this._mouseDown$ = fromEvent(canvasContainer, "pointerdown") .pipe(filter(this._isMousePen)); this._mouseEnter$ = fromEvent(canvasContainer, "pointerenter") .pipe(filter(this._isMousePen)); this._mouseLeave$ = fromEvent(canvasContainer, "pointerleave") .pipe(filter(this._isMousePen)); this._mouseMove$ = fromEvent(canvasContainer, "pointermove") .pipe(filter(this._isMousePen)); this._mouseUp$ = fromEvent(canvasContainer, "pointerup") .pipe(filter(this._isMousePen)); this._mouseOut$ = fromEvent(canvasContainer, "pointerout") .pipe(filter(this._isMousePen)); this._mouseOver$ = fromEvent(canvasContainer, "pointerover") .pipe(filter(this._isMousePen)); this._domMouseDown$ = fromEvent(domContainer, "pointerdown") .pipe(filter(this._isMousePen)); this._domMouseMove$ = fromEvent(domContainer, "pointermove") .pipe(filter(this._isMousePen)); this._click$ = fromEvent(canvasContainer, "click"); this._contextMenu$ = fromEvent(canvasContainer, "contextmenu"); this._windowBlur$ = fromEvent(window, "blur"); this._dblClick$ = merge(fromEvent(container, "click"), fromEvent(canvasContainer, "dblclick")) .pipe(bufferCount(3, 1), filter((events) => { const event1 = events[0]; const event2 = events[1]; const event3 = events[2]; return event1.type === "click" && event2.type === "click" && event3.type === "dblclick" && event1.target.parentNode === canvasContainer && event2.target.parentNode === canvasContainer; }), map((events) => { return events[2]; }), share()); subs.push(merge(this._domMouseDown$, this._domMouseMove$, this._dblClick$, this._contextMenu$) .subscribe((event) => { event.preventDefault(); })); this._mouseWheel$ = merge(fromEvent(canvasContainer, "wheel"), fromEvent(domContainer, "wheel")) .pipe(share()); this._consistentContextMenu$ = merge(this._mouseDown$, this._mouseMove$, this._mouseOut$, this._mouseUp$, this._contextMenu$) .pipe(bufferCount(3, 1), filter((events) => { // fire context menu on mouse up both on mac and windows return events[0].type === "pointerdown" && events[1].type === "contextmenu" && events[2].type === "pointerup"; }), map((events) => { return events[1]; }), share()); const dragStop$ = merge(this._windowBlur$, this._documentMouseMove$ .pipe(filter((e) => { return this._buttonReleased(e, LEFT_BUTTON); })), this._documentMouseUp$ .pipe(filter((e) => { return this._mouseButton(e) === LEFT_BUTTON; }))) .pipe(share()); const mouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._mouseDown$, dragStop$, true) .pipe(share()); this._mouseDragStart$ = this._createMouseDragStart$(mouseDragInitiate$) .pipe(share()); this._mouseDrag$ = this._createMouseDrag$(mouseDragInitiate$, dragStop$) .pipe(share()); this._mouseDragEnd$ = this._createMouseDragEnd$(this._mouseDragStart$, dragStop$) .pipe(share()); const domMouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._domMouseDown$, dragStop$, false) .pipe(share()); this._domMouseDragStart$ = this._createMouseDragStart$(domMouseDragInitiate$) .pipe(share()); this._domMouseDrag$ = this._createMouseDrag$(domMouseDragInitiate$, dragStop$) .pipe(share()); this._domMouseDragEnd$ = this._createMouseDragEnd$(this._domMouseDragStart$, dragStop$) .pipe(share()); const rightDragStop$ = merge(this._windowBlur$, this._documentMouseMove$.pipe(filter((e) => { return this._buttonReleased(e, RIGHT_BUTTON); })), this._documentMouseUp$.pipe(filter((e) => { return this._mouseButton(e) === RIGHT_BUTTON; }))) .pipe(share()); const mouseRightDragInitiate$ = this._createMouseDragInitiate$(RIGHT_BUTTON, this._mouseDown$, rightDragStop$, true) .pipe(share()); this._mouseRightDragStart$ = this._createMouseDragStart$(mouseRightDragInitiate$) .pipe(share()); this._mouseRightDrag$ = this._createMouseDrag$(mouseRightDragInitiate$, rightDragStop$) .pipe(share()); this._mouseRightDragEnd$ = this._createMouseDragEnd$(this._mouseRightDragStart$, rightDragStop$) .pipe(share()); this._proximateClick$ = this._mouseDown$ .pipe(switchMap((mouseDown) => { return this._click$.pipe(takeUntil(this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$)), take(1)); }), share()); this._staticClick$ = this._mouseDown$ .pipe(switchMap(() => { return this._click$.pipe(takeUntil(this._documentMouseMove$), take(1)); }), share()); subs.push(this._mouseDragStart$.subscribe()); subs.push(this._mouseDrag$.subscribe()); subs.push(this._mouseDragEnd$.subscribe()); subs.push(this._domMouseDragStart$.subscribe()); subs.push(this._domMouseDrag$.subscribe()); subs.push(this._domMouseDragEnd$.subscribe()); subs.push(this._mouseRightDragStart$.subscribe()); subs.push(this._mouseRightDrag$.subscribe()); subs.push(this._mouseRightDragEnd$.subscribe()); subs.push(this._staticClick$.subscribe()); this._mouseOwner$ = this._createOwner$(this._claimMouse$) .pipe(publishReplay(1), refCount()); this._wheelOwner$ = this._createOwner$(this._claimWheel$) .pipe(publishReplay(1), refCount()); subs.push(this._mouseOwner$.subscribe(() => { })); subs.push(this._wheelOwner$.subscribe(() => { })); } get active$() { return this._active$; } get activate$() { return this._activeSubject$; } get documentMouseMove$() { return this._documentMouseMove$; } get documentMouseUp$() { return this._documentMouseUp$; } get domMouseDragStart$() { return this._domMouseDragStart$; } get domMouseDrag$() { return this._domMouseDrag$; } get domMouseDragEnd$() { return this._domMouseDragEnd$; } get domMouseDown$() { return this._domMouseDown$; } get domMouseMove$() { return this._domMouseMove$; } get mouseOwner$() { return this._mouseOwner$; } get mouseDown$() { return this._mouseDown$; } get mouseEnter$() { return this._mouseEnter$; } get mouseMove$() { return this._mouseMove$; } get mouseLeave$() { return this._mouseLeave$; } get mouseOut$() { return this._mouseOut$; } get mouseOver$() { return this._mouseOver$; } get mouseUp$() { return this._mouseUp$; } get click$() { return this._click$; } get dblClick$() { return this._dblClick$; } get contextMenu$() { return this._consistentContextMenu$; } get mouseWheel$() { return this._mouseWheel$; } get mouseDragStart$() { return this._mouseDragStart$; } get mouseDrag$() { return this._mouseDrag$; } get mouseDragEnd$() { return this._mouseDragEnd$; } get mouseRightDragStart$() { return this._mouseRightDragStart$; } get mouseRightDrag$() { return this._mouseRightDrag$; } get mouseRightDragEnd$() { return this._mouseRightDragEnd$; } get proximateClick$() { return this._proximateClick$; } get staticClick$() { return this._staticClick$; } get windowBlur$() { return this._windowBlur$; } dispose() { this._subscriptions.unsubscribe(); } claimMouse(name, zindex) { this._claimMouse$.next({ name: name, zindex: zindex }); } unclaimMouse(name) { this._claimMouse$.next({ name: name, zindex: null }); } deferPixels(name, deferPixels) { this._deferPixelClaims$.next({ name: name, deferPixels: deferPixels }); } undeferPixels(name) { this._deferPixelClaims$.next({ name: name, deferPixels: null }); } claimWheel(name, zindex) { this._claimWheel$.next({ name: name, zindex: zindex }); } unclaimWheel(name) { this._claimWheel$.next({ name: name, zindex: null }); } filtered$(name, observable$) { return this._filtered(name, observable$, this._mouseOwner$); } filteredWheel$(name, observable$) { return this._filtered(name, observable$, this._wheelOwner$); } _createDeferredMouseMove$(origin, mouseMove$) { return mouseMove$.pipe(map((mouseMove) => { const deltaX = mouseMove.clientX - origin.clientX; const deltaY = mouseMove.clientY - origin.clientY; return [mouseMove, Math.sqrt(deltaX * deltaX + deltaY * deltaY)]; }), withLatestFrom(this._deferPixels$), filter(([[, delta], deferPixels]) => { return delta > deferPixels; }), map(([[mouseMove]]) => { return mouseMove; })); } _createMouseDrag$(mouseDragStartInitiate$, stop$) { return mouseDragStartInitiate$.pipe(map(([, mouseMove]) => { return mouseMove; }), switchMap((mouseMove) => { return concat(of(mouseMove), this._documentMouseMove$).pipe(takeUntil(stop$)); })); } _createMouseDragEnd$(mouseDragStart$, stop$) { return mouseDragStart$.pipe(switchMap(() => { return stop$.pipe(first()); })); } _createMouseDragStart$(mouseDragStartInitiate$) { return mouseDragStartInitiate$.pipe(map(([mouseDown]) => { return mouseDown; })); } _createMouseDragInitiate$(button, mouseDown$, stop$, defer) { return mouseDown$.pipe(filter((mouseDown) => { return this._mouseButton(mouseDown) === button; }), switchMap((mouseDown) => { return combineLatest(of(mouseDown), defer ? this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$) : this._documentMouseMove$).pipe(takeUntil(stop$), take(1)); })); } _createOwner$(claim$) { return claim$.pipe(scan((claims, claim) => { if (claim.zindex == null) { delete claims[claim.name]; } else { claims[claim.name] = claim.zindex; } return claims; }, {}), map((claims) => { let owner = null; let zIndexMax = -1; for (const name in claims) { if (!claims.hasOwnProperty(name)) { continue; } if (claims[name] > zIndexMax) { zIndexMax = claims[name]; owner = name; } } return owner; }), startWith(null)); } _filtered(name, observable$, owner$) { return observable$.pipe(withLatestFrom(owner$), filter(([, owner]) => { return owner === name; }), map(([item]) => { return item; })); } _mouseButton(event) { const upOrDown = event.type === "pointerdown" || event.type === "pointerup"; const InstallTrigger = window.InstallTrigger; if (upOrDown && typeof InstallTrigger !== "undefined" && event.button === RIGHT_BUTTON && event.ctrlKey && window.navigator.platform.toUpperCase().indexOf("MAC") >= 0) { // Fix for the fact that Firefox (detected by InstallTrigger) // on Mac determines e.button = 2 when using Control + left click. return LEFT_BUTTON; } return event.button; } _buttonReleased(event, button) { // Right button `mouseup` is not fired in // Chrome on Mac outside the window or iframe. If // the button is no longer pressed during move // it may have been released and drag stop // should be emitted. const flag = BUTTONS_MAP[button]; return event.buttons === undefined || (event.buttons & flag) !== flag; } _isMousePen(event) { const type = event.pointerType; return type === "mouse" || type === "pen"; } } class SpriteAtlas { set json(value) { this._json = value; } set image(value) { this._image = value; this._texture = new Texture(this._image); this._texture.minFilter = NearestFilter; } get loaded() { return !!(this._image && this._json); } getGLSprite(name) { if (!this.loaded) { throw new Error("Sprites cannot be retrieved before the atlas is loaded."); } let definition = this._json[name]; if (!definition) { console.warn("Sprite with key" + name + "does not exist in sprite definition."); return new Object3D(); } let texture = this._texture.clone(); texture.needsUpdate = true; let width = this._image.width; let height = this._image.height; texture.offset.x = definition.x / width; texture.offset.y = (height - definition.y - definition.height) / height; texture.repeat.x = definition.width / width; texture.repeat.y = definition.height / height; let material = new SpriteMaterial({ map: texture }); return new Sprite(material); } getDOMSprite(name, float) { if (!this.loaded) { throw new Error("Sprites cannot be retrieved before the atlas is loaded."); } if (float == null) { float = exports.Alignment.Center; } let definition = this._json[name]; if (!definition) { console.warn("Sprite with key" + name + "does not exist in sprite definition."); return virtualDom.h("div", {}, []); } let clipTop = definition.y; let clipRigth = definition.x + definition.width; let clipBottom = definition.y + definition.height; let clipLeft = definition.x; let left = -definition.x; let top = -definition.y; let height = this._image.height; let width = this._image.width; switch (float) { case exports.Alignment.Bottom: case exports.Alignment.Center: case exports.Alignment.Top: left -= definition.width / 2; break; case exports.Alignment.BottomLeft: case exports.Alignment.Left: case exports.Alignment.TopLeft: left -= definition.width; break; case exports.Alignment.BottomRight: case exports.Alignment.Right: case exports.Alignment.TopRight: } switch (float) { case exports.Alignment.Center: case exports.Alignment.Left: case exports.Alignment.Right: top -= definition.height / 2; break; case exports.Alignment.Top: case exports.Alignment.TopLeft: case exports.Alignment.TopRight: top -= definition.height; break; case exports.Alignment.Bottom: case exports.Alignment.BottomLeft: case exports.Alignment.BottomRight: } let pixelRatioInverse = 1 / definition.pixelRatio; clipTop *= pixelRatioInverse; clipRigth *= pixelRatioInverse; clipBottom *= pixelRatioInverse; clipLeft *= pixelRatioInverse; left *= pixelRatioInverse; top *= pixelRatioInverse; height *= pixelRatioInverse; width *= pixelRatioInverse; let properties = { src: this._image.src, style: { clip: `rect(${clipTop}px, ${clipRigth}px, ${clipBottom}px, ${clipLeft}px)`, height: `${height}px`, left: `${left}px`, position: "absolute", top: `${top}px`, width: `${width}px`, }, }; return virtualDom.h("img", properties, []); } } class SpriteService { constructor(sprite) { this._retina = window.devicePixelRatio > 1; this._spriteAtlasOperation$ = new Subject(); this._spriteAtlas$ = this._spriteAtlasOperation$.pipe(startWith((atlas) => { return atlas; }), scan((atlas, operation) => { return operation(atlas); }, new SpriteAtlas()), publishReplay(1), refCount()); this._atlasSubscription = this._spriteAtlas$ .subscribe(() => { }); if (sprite == null) { return; } let format = this._retina ? "@2x" : ""; let imageXmlHTTP = new XMLHttpRequest(); imageXmlHTTP.open("GET", sprite + format + ".png", true); imageXmlHTTP.responseType = "arraybuffer"; imageXmlHTTP.onload = () => { let image = new Image(); image.onload = () => { this._spriteAtlasOperation$.next((atlas) => { atlas.image = image; return atlas; }); }; let blob = new Blob([imageXmlHTTP.response]); image.src = window.URL.createObjectURL(blob); }; imageXmlHTTP.onerror = (error) => { console.error(new Error(`Failed to fetch sprite sheet (${sprite}${format}.png)`)); }; imageXmlHTTP.send(); let jsonXmlHTTP = new XMLHttpRequest(); jsonXmlHTTP.open("GET", sprite + format + ".json", true); jsonXmlHTTP.responseType = "text"; jsonXmlHTTP.onload = () => { let json = JSON.parse(jsonXmlHTTP.response); this._spriteAtlasOperation$.next((atlas) => { atlas.json = json; return atlas; }); }; jsonXmlHTTP.onerror = (error) => { console.error(new Error(`Failed to fetch sheet (${sprite}${format}.json)`)); }; jsonXmlHTTP.send(); } get spriteAtlas$() { return this._spriteAtlas$; } dispose() { this._atlasSubscription.unsubscribe(); } } class TouchService { constructor(canvasContainer, domContainer) { this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._activeSubject$ = new BehaviorSubject(false); this._active$ = this._activeSubject$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); subs.push(fromEvent(domContainer, "touchmove") .subscribe((event) => { event.preventDefault(); })); this._touchStart$ = fromEvent(canvasContainer, "touchstart"); this._touchMove$ = fromEvent(canvasContainer, "touchmove"); this._touchEnd$ = fromEvent(canvasContainer, "touchend"); this._touchCancel$ = fromEvent(canvasContainer, "touchcancel"); const tapStart$ = this._touchStart$.pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; }), share()); this._doubleTap$ = tapStart$.pipe(bufferWhen(() => { return tapStart$.pipe(first(), switchMap(() => { return merge(timer(300), tapStart$).pipe(take(1)); })); }), filter((events) => { return events.length === 2; }), map((events) => { return events[events.length - 1]; }), share()); subs.push(this._doubleTap$ .subscribe((event) => { event.preventDefault(); })); this._singleTouchMove$ = this._touchMove$.pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; }), share()); let singleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; })); let multipleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length >= 1; })); let touchStop$ = merge(this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length === 0; })); this._singleTouchDragStart$ = singleTouchStart$.pipe(mergeMap(() => { return this._singleTouchMove$.pipe(takeUntil(merge(touchStop$, multipleTouchStart$)), take(1)); })); this._singleTouchDragEnd$ = singleTouchStart$.pipe(mergeMap(() => { return merge(touchStop$, multipleTouchStart$).pipe(first()); })); this._singleTouchDrag$ = singleTouchStart$.pipe(switchMap(() => { return this._singleTouchMove$.pipe(skip(1), takeUntil(merge(multipleTouchStart$, touchStop$))); })); let touchesChanged$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$); this._pinchStart$ = touchesChanged$.pipe(filter((te) => { return te.touches.length === 2 && te.targetTouches.length === 2; })); this._pinchEnd$ = touchesChanged$.pipe(filter((te) => { return te.touches.length !== 2 || te.targetTouches.length !== 2; })); this._pinchOperation$ = new Subject(); this._pinch$ = this._pinchOperation$.pipe(scan((pinch, operation) => { return operation(pinch); }, { changeX: 0, changeY: 0, clientX: 0, clientY: 0, distance: 0, distanceChange: 0, distanceX: 0, distanceY: 0, originalEvent: null, pageX: 0, pageY: 0, screenX: 0, screenY: 0, touch1: null, touch2: null, })); const pinchSubscription = this._touchMove$.pipe(filter((te) => { return te.touches.length === 2 && te.targetTouches.length === 2; }), map((te) => { return (previous) => { let touch1 = te.touches[0]; let touch2 = te.touches[1]; let minX = Math.min(touch1.clientX, touch2.clientX); let maxX = Math.max(touch1.clientX, touch2.clientX); let minY = Math.min(touch1.clientY, touch2.clientY); let maxY = Math.max(touch1.clientY, touch2.clientY); let centerClientX = minX + (maxX - minX) / 2; let centerClientY = minY + (maxY - minY) / 2; let centerPageX = centerClientX + touch1.pageX - touch1.clientX; let centerPageY = centerClientY + touch1.pageY - touch1.clientY; let centerScreenX = centerClientX + touch1.screenX - touch1.clientX; let centerScreenY = centerClientY + touch1.screenY - touch1.clientY; let distanceX = Math.abs(touch1.clientX - touch2.clientX); let distanceY = Math.abs(touch1.clientY - touch2.clientY); let distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); let distanceChange = distance - previous.distance; let changeX = distanceX - previous.distanceX; let changeY = distanceY - previous.distanceY; let current = { changeX: changeX, changeY: changeY, clientX: centerClientX, clientY: centerClientY, distance: distance, distanceChange: distanceChange, distanceX: distanceX, distanceY: distanceY, originalEvent: te, pageX: centerPageX, pageY: centerPageY, screenX: centerScreenX, screenY: centerScreenY, touch1: touch1, touch2: touch2, }; return current; }; })) .subscribe(this._pinchOperation$); subs.push(pinchSubscription); this._pinchChange$ = this._pinchStart$.pipe(switchMap(() => { return this._pinch$.pipe(skip(1), takeUntil(this._pinchEnd$)); })); } get active$() { return this._active$; } get activate$() { return this._activeSubject$; } get doubleTap$() { return this._doubleTap$; } get touchStart$() { return this._touchStart$; } get touchMove$() { return this._touchMove$; } get touchEnd$() { return this._touchEnd$; } get touchCancel$() { return this._touchCancel$; } get singleTouchDragStart$() { return this._singleTouchDragStart$; } get singleTouchDrag$() { return this._singleTouchDrag$; } get singleTouchDragEnd$() { return this._singleTouchDragEnd$; } get pinch$() { return this._pinchChange$; } get pinchStart$() { return this._pinchStart$; } get pinchEnd$() { return this._pinchEnd$; } dispose() { this._subscriptions.unsubscribe(); } } class ConfigurationService { constructor(options) { var _a, _b, _c, _d; const host = (_b = (_a = options === null || options === void 0 ? void 0 : options.url) === null || _a === void 0 ? void 0 : _a.exploreHost) !== null && _b !== void 0 ? _b : "www.mapillary.com"; const scheme = (_d = (_c = options === null || options === void 0 ? void 0 : options.url) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : "https"; const exploreUrl = `${scheme}://${host}`; this._exploreUrl$ = of(exploreUrl); const imageTiling = (options === null || options === void 0 ? void 0 : options.imageTiling) === false ? false : true; this._imageTiling$ = of(imageTiling); } get exploreUrl$() { return this._exploreUrl$; } get imageTiling$() { return this._imageTiling$; } } class Container { constructor(options, stateService, dom) { var _a; this._onWindowResize = () => { if (this._trackResize) { this.renderService.resize$.next(); } }; this._dom = dom !== null && dom !== void 0 ? dom : new DOM(); if (typeof options.container === "string") { this._container = this._dom.document .getElementById(options.container); if (!this._container) { throw new Error(`Container "${options.container}" not found.`); } } else if (options.container instanceof HTMLElement) { this._container = options.container; } else { throw new Error(`Invalid type: "container" must be ` + `a String or HTMLElement.`); } this._trackResize = options.trackResize === false ? false : true; this.id = (_a = this._container.id) !== null && _a !== void 0 ? _a : "mapillary-fallback-container-id"; this._container.classList .add("mapillary-viewer"); this._canvasContainer = this._dom .createElement("div", "mapillary-interactive", this._container); this._canvas = this._dom .createElement("canvas", "mapillary-canvas"); this._canvas.style.position = "absolute"; this._canvas.setAttribute("tabindex", "0"); // Add DOM container after canvas container to // render DOM elements on top of the interactive // canvas. this._domContainer = this._dom .createElement("div", "mapillary-dom", this._container); this.configurationService = new ConfigurationService(options); this.renderService = new RenderService(this._container, stateService.currentState$, options.renderMode); this.glRenderer = new GLRenderer(this._canvas, this._canvasContainer, this.renderService); this.domRenderer = new DOMRenderer(this._domContainer, this.renderService, stateService.currentState$); this.keyboardService = new KeyboardService(this._canvasContainer); this.mouseService = new MouseService(this._container, this._canvasContainer, this._domContainer, document); this.touchService = new TouchService(this._canvasContainer, this._domContainer); this.spriteService = new SpriteService(options.sprite); window.addEventListener("resize", this._onWindowResize, false); } get canvas() { return !!this._canvas.parentNode ? this._canvas : null; } get canvasContainer() { return this._canvasContainer; } get container() { return this._container; } get domContainer() { return this._domContainer; } remove() { window.removeEventListener("resize", this._onWindowResize, false); this.spriteService.dispose(); this.touchService.dispose(); this.mouseService.dispose(); this.glRenderer.remove(); this.domRenderer.remove(); this.renderService.dispose(); this._removeNode(this._canvasContainer); this._removeNode(this._domContainer); this._container.classList .remove("mapillary-viewer"); } _removeNode(node) { if (node.parentNode) { node.parentNode.removeChild(node); } } } class CacheService { constructor(_graphService, _stateService, _api) { this._graphService = _graphService; this._stateService = _stateService; this._api = _api; this._subscriptions = new SubscriptionHolder(); this._started = false; this._cellDepth = 1; } get started() { return this._started; } configure(configuration) { if (!configuration) { this._cellDepth = 1; return; } this._cellDepth = Math.max(1, Math.min(3, configuration.cellDepth)); } start() { if (this._started) { return; } const subs = this._subscriptions; subs.push(this._stateService.reference$ .pipe(withLatestFrom(this._stateService.currentState$, this._graphService.graphMode$), map(([, frame, mode]) => { const state = this._frameToState(frame); return this._makeKeepers(state, mode); }), switchMap((keepers) => { return this._graphService .uncache$(keepers.imageIds, keepers.cellIds, keepers.sequenceId); })) .subscribe(() => { })); subs.push(this._stateService.currentState$ .pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; }), map((frame) => { return this._frameToState(frame); }), bufferCount(1, 5), withLatestFrom(this._graphService.graphMode$), switchMap(([stateBuffer, graphMode]) => { const keepers = this._makeKeepers(stateBuffer[0], graphMode); return this._graphService .uncache$(keepers.imageIds, keepers.cellIds, keepers.sequenceId); })) .subscribe(() => { })); subs.push(this._graphService.graphMode$ .pipe(skip(1), withLatestFrom(this._stateService.currentState$), switchMap(([mode, frame]) => { return mode === GraphMode.Sequence ? this._keyToEdges(frame.state.currentImage.id, (image) => { return image.sequenceEdges$; }) : from(frame.state.trajectory .map((image) => { return image.id; }) .slice(frame.state.currentIndex)).pipe(mergeMap((key) => { return this._keyToEdges(key, (image) => { return image.spatialEdges$; }); }, 6)); })) .subscribe(() => { })); subs.push(merge(this._graphService.dataAdded$, this._graphService.dataDeleted$) .pipe(withLatestFrom(this._stateService.currentId$), switchMap(([_, imageId]) => { return this._graphService.hasImage$(imageId).pipe(filter((exists) => { return exists; }), mergeMap(() => { return this._graphService.cacheImage$(imageId) .pipe(catchError((error) => { console.warn(`Cache service data event caching failed ${imageId}`, error); return empty(); })); })); })) .subscribe(() => { })); this._started = true; } stop() { if (!this._started) { return; } this._subscriptions.unsubscribe(); this._started = false; } _keyToEdges(key, imageToEdgeMap) { return this._graphService.cacheImage$(key).pipe(switchMap(imageToEdgeMap), first((status) => { return status.cached; }), timeout(15000), catchError((error) => { console.error(`Failed to cache edges (${key}).`, error); return empty(); })); } _frameToState(frame) { const state = frame.state; const trajectory = state.trajectory; const trajectoryIds = trajectory .map((n) => { return n.id; }); const sequenceId = trajectory[trajectory.length - 1].sequenceId; return { lngLat: state.currentImage.originalLngLat, sequenceId, trajectoryIds, }; } _makeKeepers(state, graphMode) { const imageIds = state.trajectoryIds.filter(id => !isNullImageId(id)); const lngLat = state.lngLat; const geometry = this._api.data.geometry; const cellId = geometry.lngLatToCellId(lngLat); const cellIds = connectedComponent(cellId, this._cellDepth, geometry); const sequenceId = graphMode === GraphMode.Sequence ? state.sequenceId : undefined; return { cellIds, imageIds, sequenceId }; } } class LoadingService { constructor() { this._loadersSubject$ = new Subject(); this._loaders$ = this._loadersSubject$.pipe(scan((loaders, loader) => { if (loader.task !== undefined) { loaders[loader.task] = loader.loading; } return loaders; }, {}), startWith({}), publishReplay(1), refCount()); } get loading$() { return this._loaders$.pipe(map((loaders) => { for (const key in loaders) { if (!loaders.hasOwnProperty(key)) { continue; } if (loaders[key]) { return true; } } return false; }), debounceTime(100), distinctUntilChanged()); } taskLoading$(task) { return this._loaders$.pipe(map((loaders) => { return !!loaders[task]; }), debounceTime(100), distinctUntilChanged()); } startLoading(task) { this._loadersSubject$.next({ loading: true, task: task }); } stopLoading(task) { this._loadersSubject$.next({ loading: false, task: task }); } } var PanMode; (function (PanMode) { PanMode[PanMode["Disabled"] = 0] = "Disabled"; PanMode[PanMode["Enabled"] = 1] = "Enabled"; PanMode[PanMode["Started"] = 2] = "Started"; })(PanMode || (PanMode = {})); class PanService { constructor(graphService, stateService, cameraFactory, enabled, graphCalculator, spatial, viewportCoords) { this._subscriptions = new SubscriptionHolder(); this._graphService = graphService; this._stateService = stateService; this._cameraFactory = cameraFactory; this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); this._mode = enabled !== false ? PanMode.Enabled : PanMode.Disabled; this._panImagesSubject$ = new Subject(); this._panImages$ = this._panImagesSubject$.pipe(startWith([]), publishReplay(1), refCount()); this._subscriptions.push(this._panImages$.subscribe()); } get panImages$() { return this._panImages$; } dispose() { this.stop(); if (this._panImagesSubscription != null) { this._panImagesSubscription.unsubscribe(); } this._subscriptions.unsubscribe(); } enable() { if (this._mode !== PanMode.Disabled) { return; } this._mode = PanMode.Enabled; this.start(); } disable() { if (this._mode === PanMode.Disabled) { return; } this.stop(); this._mode = PanMode.Disabled; } start() { if (this._mode !== PanMode.Enabled) { return; } const panImages$ = this._stateService.currentImage$.pipe(filter((image) => { return !isNullImageId(image.id); }), switchMap((current) => { if (!current.merged || isSpherical(current.cameraType)) { return of([]); } const current$ = of(current); const bounds = this._graphCalculator.boundingBoxCorners(current.lngLat, 20); const adjacent$ = this._graphService .cacheBoundingBox$(bounds[0], bounds[1]).pipe(catchError((error) => { console.error(`Failed to cache periphery bounding box (${current.id})`, error); return empty(); }), map((images) => { if (isSpherical(current.cameraType)) { return []; } const potential = []; for (const image of images) { if (image.id === current.id) { continue; } if (image.mergeId !== current.mergeId) { continue; } if (isSpherical(image.cameraType)) { continue; } if (this._distance(image, current) > 4) { continue; } potential.push(image); } return potential; })); return combineLatest(current$, adjacent$).pipe(withLatestFrom(this._stateService.reference$), map(([[cn, adjacent], reference]) => { const currentDirection = this._spatial.viewingDirection(cn.rotation); const currentTranslation = computeTranslation({ lat: cn.lngLat.lat, lng: cn.lngLat.lng, alt: cn.computedAltitude }, cn.rotation, reference); const currentTransform = this._createTransform(cn, currentTranslation); const currentAzimuthal = this._spatial.wrap(this._spatial.azimuthal(currentDirection.toArray(), currentTransform.upVector().toArray()), 0, 2 * Math.PI); const currentHFov = this._computeHorizontalFov(currentTransform) / 180 * Math.PI; const preferredOverlap = Math.PI / 8; let left = undefined; let right = undefined; for (const a of adjacent) { const translation = computeTranslation({ lat: a.lngLat.lat, lng: a.lngLat.lng, alt: a.computedAltitude }, a.rotation, reference); const transform = this._createTransform(a, translation); const hFov = this._computeHorizontalFov(transform) / 180 * Math.PI; const direction = this._spatial.viewingDirection(a.rotation); const azimuthal = this._spatial.wrap(this._spatial.azimuthal(direction.toArray(), transform.upVector().toArray()), 0, 2 * Math.PI); const directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); let overlap = Number.NEGATIVE_INFINITY; if (directionChange > 0) { if (currentAzimuthal > azimuthal) { overlap = currentAzimuthal - 2 * Math.PI + currentHFov / 2 - (azimuthal - hFov / 2); } else { overlap = currentAzimuthal + currentHFov / 2 - (azimuthal - hFov / 2); } } else { if (currentAzimuthal < azimuthal) { overlap = azimuthal + hFov / 2 - (currentAzimuthal + 2 * Math.PI - currentHFov / 2); } else { overlap = azimuthal + hFov / 2 - (currentAzimuthal - currentHFov / 2); } } const nonOverlap = Math.abs(hFov - overlap); const distanceCost = this._distance(a, cn); const timeCost = Math.min(this._timeDifference(a, cn), 4); const overlapCost = 20 * Math.abs(overlap - preferredOverlap); const fovCost = Math.min(5, 1 / Math.min(hFov / currentHFov, 1)); const nonOverlapCost = overlap > 0 ? -2 * nonOverlap : 0; const cost = distanceCost + timeCost + overlapCost + fovCost + nonOverlapCost; if (overlap > 0 && overlap < 0.5 * currentHFov && overlap < 0.5 * hFov && nonOverlap > 0.5 * currentHFov) { if (directionChange > 0) { if (!left) { left = [cost, a, transform, hFov]; } else { if (cost < left[0]) { left = [cost, a, transform, hFov]; } } } else { if (!right) { right = [cost, a, transform, hFov]; } else { if (cost < right[0]) { right = [cost, a, transform, hFov]; } } } } } const panImagess = []; if (!!left) { panImagess.push([left[1], left[2], left[3]]); } if (!!right) { panImagess.push([right[1], right[2], right[3]]); } return panImagess; }), startWith([])); })); const traversing$ = this._stateService.state$.pipe(map((state) => { return state === State.Traversing || state === State.GravityTraversing; }), distinctUntilChanged()); const imagesAhead$ = this._stateService.currentState$.pipe(map((frame) => { return frame.state.imagesAhead > 0; }), distinctUntilChanged()); this._panImagesSubscription = combineLatest(traversing$, imagesAhead$) .pipe(switchMap(([traversing, imagesAhead]) => { return traversing && !imagesAhead ? panImages$ : of([]); })) .subscribe((panImages) => { this._panImagesSubject$.next(panImages); }); this._mode = PanMode.Started; } stop() { if (this._mode !== PanMode.Started) { return; } this._panImagesSubscription.unsubscribe(); this._panImagesSubject$.next([]); this._mode = PanMode.Enabled; } _distance(image, reference) { const [x, y, z] = geodeticToEnu(image.lngLat.lng, image.lngLat.lat, image.computedAltitude, reference.lngLat.lng, reference.lngLat.lat, reference.computedAltitude); return Math.sqrt(x * x + y * y + z * z); } _timeDifference(image, reference) { const milliSecond = (1000 * 60 * 60 * 24 * 30); return Math.abs(image.capturedAt - reference.capturedAt) / milliSecond; } _createTransform(image, translation) { return new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.assetsCached ? image.image : undefined, image.assetsCached ? image.camera : this._cameraFactory.makeCamera(image.cameraType, image.cameraParameters)); } _computeHorizontalFov(transform) { const vertices = [[1, 0]]; const directions = [[0, 0.5]]; const pointsPerLine = 12; const bearings = computeBearings(transform, vertices, directions, pointsPerLine, this._viewportCoords); const projections = bearings .map(b => this._spatial.projectToPlane(b, [0, 1, 0])) .map(p => [p[0], -p[2]]); const angles = projections.map(p => Math.abs(Math.atan2(p[0], p[1]))); const fov = 2 * Math.max(...angles); return this._spatial.radToDeg(fov); } } /** * @class API * * @classdesc Provides methods for access to the API. */ class APIWrapper { constructor(_data) { this._data = _data; } get data() { return this._data; } getCoreImages$(cellId) { return this._wrap$(this._data.getCoreImages(cellId)); } getImages$(imageIds) { return this._wrap$(this._data.getImages(imageIds)); } getImageTiles$(tiles) { return this._wrap$(this._data.getImageTiles(tiles)); } getSequence$(sequenceId) { return this._wrap$(this._data.getSequence(sequenceId)); } getSpatialImages$(imageIds) { return this._wrap$(this._data.getSpatialImages(imageIds)); } setAccessToken(accessToken) { this._data.setAccessToken(accessToken); } setDataProvider(data) { this._data = data; } _wrap$(promise) { return Observable.create((subscriber) => { promise.then((value) => { subscriber.next(value); subscriber.complete(); }, (error) => { subscriber.error(error); }); }); } } const EPSILON = 1e-8; function bearing$1(point, parameters, uniforms) { const [x, y] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Transformation const [xd, yd] = [x / focal, y / focal]; // Undistortion const dTheta = Math.sqrt(xd * xd + yd * yd); const d = distortionFromDistortedRadius(dTheta, k1, k2, radialPeak); const theta = dTheta / d; // Unprojection const r = Math.sin(theta); const denomTheta = dTheta > EPSILON ? 1 / dTheta : 1; const xb = r * xd * denomTheta; const yb = r * yd * denomTheta; const zb = Math.cos(theta); return [xb, yb, zb]; } function project$1(point, parameters, uniforms) { const [x, y, z] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Projection const r = Math.sqrt(x * x + y * y); let theta = Math.atan2(r, z); if (theta > radialPeak) { theta = radialPeak; } const xp = theta / r * x; const yp = theta / r * y; // Distortion const theta2 = Math.pow(theta, 2); const distortion = 1.0 + theta2 * (k1 + theta2 * k2); const xd = xp * distortion; const yd = yp * distortion; // Transformation const xt = focal * xd; const yt = focal * yd; return [xt, yt]; } const FISHEYE_CAMERA_TYPE = "fisheye"; const FISHEYE_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing, Parameters parameters, Uniforms uniforms) { float x = bearing.x; float y = bearing.y; float z = bearing.z; float focal = parameters.focal; float k1 = parameters.k1; float k2 = parameters.k2; float radialPeak = uniforms.radialPeak; // Projection if (z < 0.) { return vec2(POSITIVE_INFINITY, POSITIVE_INFINITY); } float r = sqrt(x * x + y * y); float theta = atan(r, z); if (theta > radialPeak) { theta = radialPeak; } float xp = theta / r * x; float yp = theta / r * y; // Distortion float theta2 = theta * theta; float distortion = 1.0 + theta2 * (k1 + theta2 * k2); float xd = xp * distortion; float yd = yp * distortion; // Transformation float xt = focal * xd; float yt = focal * yd; return vec2(xt, yt); } `; class FisheyeCamera extends Camera$1 { constructor(parameters) { super(FISHEYE_CAMERA_TYPE, FISHEYE_PROJECT_FUNCTION); const [focal, k1, k2] = parameters; this.parameters.focal = focal; this.parameters.k1 = k1; this.parameters.k2 = k2; const radialPeak = makeRadialPeak(k1, k2); this.uniforms.radialPeak = radialPeak; } bearingFromSfm(point) { return bearing$1(point, this.parameters, this.uniforms); } projectToSfm(point) { return project$1(point, this.parameters, this.uniforms); } } function bearing(point) { const [x, y] = point; // Unprojection const lng = x * 2 * Math.PI; const lat = -y * 2 * Math.PI; const xb = Math.cos(lat) * Math.sin(lng); const yb = -Math.sin(lat); const zb = Math.cos(lat) * Math.cos(lng); return [xb, yb, zb]; } function project(point) { const [x, y, z] = point; // Projection const lng = Math.atan2(x, z); const lat = Math.atan2(-y, Math.sqrt(x * x + z * z)); const xp = lng / (2 * Math.PI); const yp = -lat / (2 * Math.PI); return [xp, yp]; } const SPHERICAL_CAMERA_TYPE = "spherical"; const SPHERICAL_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing) { float x = bearing.x; float y = bearing.y; float z = bearing.z; // Projection float lat = -asin(y); float lng = atan(x, z); float xn = lng / PI2; float yn = -lat / PI2; return vec2(xn, yn); } `; class SphericalCamera extends Camera$1 { constructor() { super(SPHERICAL_CAMERA_TYPE, SPHERICAL_PROJECT_FUNCTION); } bearingFromSfm(point) { return bearing(point); } projectToSfm(point) { return project(point); } } const vertex = /* glsl */ ` #include #include void main() { #include #include } `; const fragment = /* glsl */ ` #include #include #include #include #include #expand #expand #expand void main() { #include #expand #include #include } `; // tslint:disable-next-line:variable-name const Shader = { texture: { fragment: fragment, vertex: vertex, }, }; class ProjectionService { constructor() { this._cameraFactory = new Map(); this.registerCamera(FISHEYE_CAMERA_TYPE, FisheyeCamera); this.registerCamera(PERSPECTIVE_CAMERA_TYPE, PerspectiveCamera); this.registerCamera(SPHERICAL_CAMERA_TYPE, SphericalCamera); this._shader = Shader.texture; this._shaderChanged$ = new Subject(); this._shader$ = this._shaderChanged$.pipe(startWith(this._shader), publishReplay(1), refCount()); this._shaderSubscription = this._shader$.subscribe(); } get shader$() { return this._shader$; } dispose() { this._shaderSubscription.unsubscribe(); } hasCamera(type) { return this._cameraFactory.has(type); } getShader() { return this._shader; } makeCamera(type, parameters) { if (!this.hasCamera(type)) { return new PerspectiveCamera([0.85, 0, 0]); } return new (this._cameraFactory.get(type))(parameters); } registerCamera(type, ctor) { this._cameraFactory.set(type, ctor); } setShader(shader) { this._shader = shader ? { fragment: `${shader.fragment}`, vertex: `${shader.vertex}`, } : Shader.texture; this._shaderChanged$.next(this._shader); } } /** * @class GraphService * * @classdesc Represents a service for graph operations. */ class GraphService { /** * Create a new graph service instance. * * @param {Graph} graph - Graph instance to be operated on. */ constructor(graph, cameraFactory) { this._dataAdded$ = new Subject(); this._dataDeleted$ = new Subject(); this._dataReset$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._onDataAdded = (event) => { this._graph$ .pipe(first(), mergeMap(graph => { return graph.updateCells$(event.cellIds); }), withLatestFrom(this._graph$.pipe(first())), tap(([_, graph]) => { graph.resetSpatialArea(); graph.resetSpatialEdges(); })) .subscribe(([cellId]) => { this._dataAdded$.next(cellId); }); }; this._onDataDeleted = (event) => { if (!event.clusterIds.length) { return; } this._graph$ .pipe(first(), mergeMap(graph => { graph.resetSpatialArea(); graph.resetSpatialEdges(); return graph.deleteClusters$(event.clusterIds); })) .subscribe(null, null, () => { this._dataDeleted$.next(event.clusterIds); }); }; this._cameraFactory = cameraFactory !== null && cameraFactory !== void 0 ? cameraFactory : new ProjectionService(); const subs = this._subscriptions; this._graph$ = concat(of(graph), graph.changed$).pipe(publishReplay(1), refCount()); subs.push(this._graph$.subscribe(() => { })); this._graphMode = GraphMode.Spatial; this._graphModeSubject$ = new Subject(); this._graphMode$ = this._graphModeSubject$.pipe(startWith(this._graphMode), publishReplay(1), refCount()); subs.push(this._graphMode$.subscribe(() => { })); this._firstGraphSubjects$ = []; this._initializeCacheSubscriptions = []; this._sequenceSubscriptions = []; this._spatialSubscriptions = []; graph.api.data.on("datacreate", this._onDataAdded); graph.api.data.on("datadelete", this._onDataDeleted); } /** * Get dataAdded$. * * @returns {Observable} Observable emitting * a cell id every time data has been added to a cell. */ get dataAdded$() { return this._dataAdded$; } /** * Get dataDeleted$. * * @returns {Observable} Observable emitting * a cluster id every time a cluster has been deleted. */ get dataDeleted$() { return this._dataDeleted$; } get dataReset$() { return this._dataReset$; } /** * Get filter observable. * * @desciption Emits the filter every time it has changed. * * @returns {Observable} Observable * emitting the filter function every time it is set. */ get filter$() { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.filter$; })); } /** * Get graph mode observable. * * @description Emits the current graph mode. * * @returns {Observable} Observable * emitting the current graph mode when it changes. */ get graphMode$() { return this._graphMode$; } /** * Cache full images in a bounding box. * * @description When called, the full properties of * the image are retrieved. The image cache is not initialized * for any new images retrieved and the image assets are not * retrieved, {@link cacheImage$} needs to be called for caching * assets. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * @return {Observable>} Observable emitting a single item, * the images of the bounding box, when they have all been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheBoundingBox$(sw, ne) { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.cacheBoundingBox$(sw, ne); })); } /** * Cache full images in a cell. * * @description When called, the full properties of * the image are retrieved. The image cache is not initialized * for any new images retrieved and the image assets are not * retrieved, {@link cacheImage$} needs to be called for caching * assets. * * @param {string} cellId - Id of the cell. * @return {Observable>} Observable emitting a single item, * the images of the cell, when they have all been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheCell$(cellId) { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.cacheCell$(cellId); })); } /** * Cache a image in the graph and retrieve it. * * @description When called, the full properties of * the image are retrieved and the image cache is initialized. * After that the image assets are cached and the image * is emitted to the observable when. * In parallel to caching the image assets, the sequence and * spatial edges of the image are cached. For this, the sequence * of the image and the required tiles and spatial images are * retrieved. The sequence and spatial edges may be set before * or after the image is returned. * * @param {string} id - Id of the image to cache. * @return {Observable} Observable emitting a single item, * the image, when it has been retrieved and its assets are cached. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheImage$(id) { const firstGraphSubject$ = new Subject(); this._firstGraphSubjects$.push(firstGraphSubject$); const firstGraph$ = firstGraphSubject$.pipe(publishReplay(1), refCount()); const image$ = firstGraph$.pipe(map((graph) => { return graph.getNode(id); }), mergeMap((image) => { return image.assetsCached ? of(image) : image.cacheAssets$(this._cameraFactory); }), publishReplay(1), refCount()); image$.subscribe(undefined, (error) => { console.error(`Failed to cache image (${id}).`, error); }); let initializeCacheSubscription; initializeCacheSubscription = this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingFull(id) || !graph.hasNode(id)) { return graph.cacheFull$(id); } if (graph.isCachingFill(id) || !graph.getNode(id).complete) { return graph.cacheFill$(id); } return of(graph); }), tap((graph) => { if (!graph.hasNode(id)) { throw new GraphMapillaryError(`Failed to cache image (${id})`); } if (!graph.hasInitializedCache(id)) { graph.initializeCache(id); } }), finalize(() => { if (initializeCacheSubscription == null) { return; } this._removeFromArray(initializeCacheSubscription, this._initializeCacheSubscriptions); this._removeFromArray(firstGraphSubject$, this._firstGraphSubjects$); })) .subscribe((graph) => { firstGraphSubject$.next(graph); firstGraphSubject$.complete(); }, (error) => { firstGraphSubject$.error(error); }); if (!initializeCacheSubscription.closed) { this._initializeCacheSubscriptions.push(initializeCacheSubscription); } const graphSequence$ = firstGraph$.pipe(catchError(() => { return empty(); }), mergeMap((graph) => { if (graph.isCachingNodeSequence(id) || !graph.hasNodeSequence(id)) { return graph.cacheNodeSequence$(id); } return of(graph); }), publishReplay(1), refCount()); let sequenceSubscription; sequenceSubscription = graphSequence$.pipe(tap((graph) => { const node = graph.getNode(id); if (!node.hasInitializedCache()) { return; } if (!node.sequenceEdges.cached) { graph.cacheSequenceEdges(id); } }), finalize(() => { if (sequenceSubscription == null) { return; } this._removeFromArray(sequenceSubscription, this._sequenceSubscriptions); })) .subscribe(() => { return; }, (error) => { console.error(`Failed to cache sequence edges (${id}).`, error); }); if (!sequenceSubscription.closed) { this._sequenceSubscriptions.push(sequenceSubscription); } if (this._graphMode === GraphMode.Spatial) { let spatialSubscription; spatialSubscription = firstGraph$.pipe(catchError(() => { return empty(); }), expand((graph) => { if (graph.hasTiles(id)) { return empty(); } return from(graph.cacheTiles$(id)).pipe(mergeMap((graph$) => { return graph$.pipe(mergeMap((g) => { if (g.isCachingTiles(id)) { return empty(); } return of(g); }), catchError((error) => { console.error(`Failed to cache tile data (${id}).`, error); return empty(); })); })); }), takeLast(1), mergeMap((graph) => { if (graph.hasSpatialArea(id)) { return of(graph); } return from(graph.cacheSpatialArea$(id)).pipe(mergeMap((graph$) => { return graph$.pipe(catchError((error) => { if (error instanceof CancelMapillaryError) { // tslint:disable-next-line:no-console console.debug(`Failed to cache spatial area (${id}).`, error); } else { console.error(`Failed to cache spatial area (${id}).`, error); } return empty(); })); })); }), takeLast(1), mergeMap((graph) => { return graph.hasNodeSequence(id) ? of(graph) : graph.cacheNodeSequence$(id); }), tap((graph) => { const node = graph.getNode(id); if (!node.hasInitializedCache()) { return; } if (!node.spatialEdges.cached) { graph.cacheSpatialEdges(id); } }), finalize(() => { if (spatialSubscription == null) { return; } this._removeFromArray(spatialSubscription, this._spatialSubscriptions); })) .subscribe(() => { return; }, (error) => { const message = `Failed to cache spatial edges (${id}).`; console.error(message, error); }); if (!spatialSubscription.closed) { this._spatialSubscriptions.push(spatialSubscription); } } return image$.pipe(first((image) => { return image.assetsCached; })); } /** * Cache a sequence in the graph and retrieve it. * * @param {string} sequenceId - Sequence id. * @returns {Observable} Observable emitting a single item, * the sequence, when it has been retrieved and its assets are cached. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheSequence$(sequenceId) { return this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { return graph.cacheSequence$(sequenceId); } return of(graph); }), map((graph) => { return graph.getSequence(sequenceId); })); } /** * Cache a sequence and its images in the graph and retrieve the sequence. * * @description Caches a sequence and its assets are cached and * retrieves all images belonging to the sequence. The image assets * or edges will not be cached. * * @param {string} sequenceId - Sequence id. * @param {string} referenceImageId - Id of image to use as reference * for optimized caching. * @returns {Observable} Observable emitting a single item, * the sequence, when it has been retrieved, its assets are cached and * all images belonging to the sequence has been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheSequenceImages$(sequenceId, referenceImageId) { return this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { return graph.cacheSequence$(sequenceId); } return of(graph); }), mergeMap((graph) => { if (graph.isCachingSequenceNodes(sequenceId) || !graph.hasSequenceNodes(sequenceId)) { return graph.cacheSequenceNodes$(sequenceId, referenceImageId); } return of(graph); }), map((graph) => { return graph.getSequence(sequenceId); })); } /** * Dispose the graph service and its children. */ dispose() { this._graph$ .pipe(first()) .subscribe((graph) => { graph.unsubscribe(); }); this._subscriptions.unsubscribe(); } /** * Check if an image exists in the graph. * * @description If a node has been deleted it will not exist. * * @return {Observable} Observable emitting a single item, * a value indicating if the image exists in the graph. */ hasImage$(id) { return this._graph$.pipe(first(), map((graph) => { return graph.hasNode(id); })); } /** * Set a spatial edge filter on the graph. * * @description Resets the spatial edges of all cached images. * * @param {FilterExpression} filter - Filter expression to be applied. * @return {Observable} Observable emitting a single item, * the graph, when the spatial edges have been reset. */ setFilter$(filter) { this._resetSubscriptions(this._spatialSubscriptions); return this._graph$.pipe(first(), tap((graph) => { graph.resetSpatialEdges(); graph.setFilter(filter); }), map(() => { return undefined; })); } /** * Set the graph mode. * * @description If graph mode is set to spatial, caching * is performed with emphasis on spatial edges. If graph * mode is set to sequence no tile data is requested and * no spatial edges are computed. * * When setting graph mode to sequence all spatial * subscriptions are aborted. * * @param {GraphMode} mode - Graph mode to set. */ setGraphMode(mode) { if (this._graphMode === mode) { return; } if (mode === GraphMode.Sequence) { this._resetSubscriptions(this._spatialSubscriptions); } this._graphMode = mode; this._graphModeSubject$.next(this._graphMode); } /** * Reset the graph. * * @description Resets the graph but keeps the images of the * supplied ids. * * @return {Observable} Observable emitting a single item, * the graph, when it has been reset. */ reset$() { this._abortSubjects(this._firstGraphSubjects$); this._resetSubscriptions(this._initializeCacheSubscriptions); this._resetSubscriptions(this._sequenceSubscriptions); this._resetSubscriptions(this._spatialSubscriptions); return this._graph$.pipe(first(), tap((graph) => { graph.reset(); this._dataReset$.next(); }), map(() => { return undefined; })); } /** * Uncache the graph. * * @description Uncaches the graph by removing tiles, images and * sequences. Keeps the images of the supplied ids and the tiles * related to those images. * * @param {Array} keepIds - Ids of images to keep in graph. * @param {Array} keepCellIds - Ids of cells to keep in graph. * @param {string} keepSequenceId - Optional id of sequence * for which the belonging images should not be disposed or * removed from the graph. These images may still be uncached if * not specified in keep ids param. * @return {Observable} Observable emitting a single item, * the graph, when the graph has been uncached. */ uncache$(keepIds, keepCellIds, keepSequenceId) { return this._graph$.pipe(first(), tap((graph) => { graph.uncache(keepIds, keepCellIds, keepSequenceId); }), map(() => { return undefined; })); } _abortSubjects(subjects) { for (const subject of subjects.slice()) { this._removeFromArray(subject, subjects); subject.error(new Error("Cache image request was aborted.")); } } _removeFromArray(object, objects) { const index = objects.indexOf(object); if (index !== -1) { objects.splice(index, 1); } } _resetSubscriptions(subscriptions) { for (const subscription of subscriptions.slice()) { this._removeFromArray(subscription, subscriptions); if (!subscription.closed) { subscription.unsubscribe(); } } } } class FrameGenerator { constructor(root) { if (root.requestAnimationFrame) { this._cancelAnimationFrame = root.cancelAnimationFrame.bind(root); this._requestAnimationFrame = root.requestAnimationFrame.bind(root); } else if (root.mozRequestAnimationFrame) { this._cancelAnimationFrame = root.mozCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.mozRequestAnimationFrame.bind(root); } else if (root.webkitRequestAnimationFrame) { this._cancelAnimationFrame = root.webkitCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.webkitRequestAnimationFrame.bind(root); } else if (root.msRequestAnimationFrame) { this._cancelAnimationFrame = root.msCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.msRequestAnimationFrame.bind(root); } else if (root.oRequestAnimationFrame) { this._cancelAnimationFrame = root.oCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.oRequestAnimationFrame.bind(root); } else { this._cancelAnimationFrame = root.clearTimeout.bind(root); this._requestAnimationFrame = (cb) => { return root.setTimeout(cb, 1000 / 60); }; } } get cancelAnimationFrame() { return this._cancelAnimationFrame; } get requestAnimationFrame() { return this._requestAnimationFrame; } } class StateBase { constructor(state) { this._spatial = new Spatial(); this._referenceThreshold = 250; this._transitionThreshold = 62.5; this._transitionMode = state.transitionMode; this._reference = state.reference; this._alpha = state.alpha; this._stateTransitionAlpha = 0; this._camera = state.camera.clone(); this._zoom = state.zoom; this._currentIndex = state.currentIndex; this._trajectory = state.trajectory.slice(); this._trajectoryTransforms = []; this._trajectoryCameras = []; for (let image of this._trajectory) { let translation = this._imageToTranslation(image, this._reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.push(transform); this._trajectoryCameras.push(new Camera(transform)); } this._currentImage = this._trajectory.length > 0 ? this._trajectory[this._currentIndex] : null; this._previousImage = this._trajectory.length > 1 && this.currentIndex > 0 ? this._trajectory[this._currentIndex - 1] : null; this._currentCamera = this._trajectoryCameras.length > 0 ? this._trajectoryCameras[this._currentIndex].clone() : new Camera(); this._previousCamera = this._trajectoryCameras.length > 1 && this.currentIndex > 0 ? this._trajectoryCameras[this._currentIndex - 1].clone() : this._currentCamera.clone(); } get reference() { return this._reference; } get alpha() { return this._getAlpha(); } get stateTransitionAlpha() { return this._getStateTransitionAlpha(); } get camera() { return this._camera; } get zoom() { return this._zoom; } get trajectory() { return this._trajectory; } get currentIndex() { return this._currentIndex; } get currentImage() { return this._currentImage; } get previousImage() { return this._previousImage; } get currentCamera() { return this._currentCamera; } get previousCamera() { return this._previousCamera; } get currentTransform() { return this._trajectoryTransforms.length > 0 ? this._trajectoryTransforms[this.currentIndex] : null; } get previousTransform() { return this._trajectoryTransforms.length > 1 && this.currentIndex > 0 ? this._trajectoryTransforms[this.currentIndex - 1] : null; } get motionless() { return this._motionless; } get transitionMode() { return this._transitionMode; } move(delta) { } moveTo(position) { } rotate(delta) { } rotateUnbounded(delta) { } rotateWithoutInertia(delta) { } rotateBasic(basicRotation) { } rotateBasicUnbounded(basicRotation) { } rotateBasicWithoutInertia(basicRotation) { } rotateToBasic(basic) { } setSpeed(speed) { } zoomIn(delta, reference) { } update(delta) { } setCenter(center) { } setZoom(zoom) { } dolly(delta) { } orbit(rotation) { } setViewMatrix(matrix) { } truck(direction) { } append(images) { if (images.length < 1) { throw Error("Trajectory can not be empty"); } if (this._currentIndex < 0) { this.set(images); } else { this._trajectory = this._trajectory.concat(images); this._appendToTrajectories(images); } } prepend(images) { if (images.length < 1) { throw Error("Trajectory can not be empty"); } this._trajectory = images.slice().concat(this._trajectory); this._currentIndex += images.length; this._setCurrentImage(); let referenceReset = this._setReference(); if (referenceReset) { this._setTrajectories(); } else { this._prependToTrajectories(images); } this._setCurrentCamera(); } remove(n) { if (n < 0) { throw Error("n must be a positive integer"); } if (this._currentIndex - 1 < n) { throw Error("Current and previous images can not be removed"); } for (let i = 0; i < n; i++) { this._trajectory.shift(); this._trajectoryTransforms.shift(); this._trajectoryCameras.shift(); this._currentIndex--; } this._setCurrentImage(); } clearPrior() { if (this._currentIndex > 0) { this.remove(this._currentIndex - 1); } } clear() { this.cut(); if (this._currentIndex > 0) { this.remove(this._currentIndex - 1); } } cut() { while (this._trajectory.length - 1 > this._currentIndex) { this._trajectory.pop(); this._trajectoryTransforms.pop(); this._trajectoryCameras.pop(); } } set(images) { this._setTrajectory(images); this._setCurrentImage(); this._setReference(); this._setTrajectories(); this._setCurrentCamera(); } getCenter() { return this._currentImage != null ? this.currentTransform.projectBasic(this._camera.lookat.toArray()) : [0.5, 0.5]; } setTransitionMode(mode) { this._transitionMode = mode; } _getAlpha() { return 1; } _getStateTransitionAlpha() { return 1; } _setCurrent() { this._setCurrentImage(); let referenceReset = this._setReference(); if (referenceReset) { this._setTrajectories(); } this._setCurrentCamera(); } _setCurrentCamera() { this._currentCamera = this._trajectoryCameras[this._currentIndex].clone(); this._previousCamera = this._currentIndex > 0 ? this._trajectoryCameras[this._currentIndex - 1].clone() : this._currentCamera.clone(); } _motionlessTransition() { const imagesSet = this._currentImage != null && this._previousImage != null; if (!imagesSet) { return false; } return this._transitionMode === exports.TransitionMode.Instantaneous || !(this._currentImage.merged && this._previousImage.merged && this._hasStructure() && this._withinDistance()); } _setReference() { const { currentImage, previousImage, reference } = this; const referenceDistance = this._spatial.distanceFromLngLat(currentImage.lngLat.lng, currentImage.lngLat.lat, reference.lng, reference.lat); // do not reset reference if image is within threshold distance if (referenceDistance < this._referenceThreshold) { return false; } if (previousImage != null) { const transitionDistance = this._spatial.distanceFromLngLat(currentImage.lngLat.lng, currentImage.lngLat.lat, previousImage.lngLat.lng, previousImage.lngLat.lat); if (transitionDistance < this._transitionThreshold) { return false; } } // do not reset reference if previous image exist and // transition is with motion if (previousImage != null && !this._motionlessTransition()) { return false; } this._reference.lat = currentImage.lngLat.lat; this._reference.lng = currentImage.lngLat.lng; this._reference.alt = currentImage.computedAltitude; return true; } _setCurrentImage() { this._currentImage = this._trajectory.length > 0 ? this._trajectory[this._currentIndex] : null; this._previousImage = this._currentIndex > 0 ? this._trajectory[this._currentIndex - 1] : null; } _setTrajectory(images) { if (images.length < 1) { throw new ArgumentMapillaryError("Trajectory can not be empty"); } if (this._currentImage != null) { this._trajectory = [this._currentImage].concat(images); this._currentIndex = 1; } else { this._trajectory = images.slice(); this._currentIndex = 0; } } _setTrajectories() { this._trajectoryTransforms.length = 0; this._trajectoryCameras.length = 0; this._appendToTrajectories(this._trajectory); } _appendToTrajectories(images) { for (let image of images) { if (!image.assetsCached) { throw new ArgumentMapillaryError("Assets must be cached when image is added to trajectory"); } let translation = this._imageToTranslation(image, this.reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.push(transform); this._trajectoryCameras.push(new Camera(transform)); } } _prependToTrajectories(images) { for (let image of images.reverse()) { if (!image.assetsCached) { throw new ArgumentMapillaryError("Assets must be cached when added to trajectory"); } let translation = this._imageToTranslation(image, this.reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.unshift(transform); this._trajectoryCameras.unshift(new Camera(transform)); } } _imageToTranslation(image, reference) { return computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); } _hasStructure() { const current = this._currentImage; const previous = this._previousImage; return current.mesh.vertices.length > 0 && previous.mesh.vertices.length > 0; } _withinDistance() { const current = this._currentImage; const previous = this._previousImage; if (!current || !previous) { return true; } const distance = this._spatial.distanceFromLngLat(current.lngLat.lng, current.lngLat.lat, previous.lngLat.lng, previous.lngLat.lat); // 50 km/h moves 28m in 2s return distance < 30; } } class CustomState extends StateBase { constructor(state) { super(state); } setViewMatrix(viewMatrix) { const viewMatrixInverse = new Matrix4() .fromArray(viewMatrix) .invert(); const me = viewMatrixInverse.elements; const eye = new Vector3(me[12], me[13], me[14]); const forward = new Vector3(-me[8], -me[9], -me[10]); const up = new Vector3(me[4], me[5], me[6]); const camera = this._camera; camera.position.copy(eye); camera.lookat.copy(eye .clone() .add(forward)); camera.up.copy(up); const focal = 0.5 / Math.tan(Math.PI / 3); camera.focal = focal; } } class EarthState extends StateBase { constructor(state) { super(state); this._transition = 0; const eye = this._camera.position.clone(); const forward = this._camera.lookat .clone() .sub(eye) .normalize(); const xy = Math.sqrt(forward.x * forward.x + forward.y * forward.y); const angle = Math.atan2(forward.z, xy); const lookat = new Vector3(); if (angle > -Math.PI / 45) { lookat.copy(eye); eye.add(new Vector3(forward.x, forward.y, 0) .multiplyScalar(-50)); eye.z = 30; } else { // Target a point on invented ground and keep forward direction const l0 = eye.clone(); const n = new Vector3(0, 0, 1); const p0 = new Vector3(0, 0, -2); const d = new Vector3().subVectors(p0, l0).dot(n) / forward.dot(n); const maxDistance = 10000; const intersection = l0 .clone() .add(forward. clone() .multiplyScalar(Math.min(maxDistance, d))); lookat.copy(intersection); const t = eye .clone() .sub(intersection) .normalize(); eye.copy(intersection.add(t.multiplyScalar(Math.max(50, t.length())))); } const eye1 = this._camera.position.clone(); const lookat1 = eye1.clone().add(forward.clone().normalize().multiplyScalar(10)); const up1 = this._camera.up.clone(); const eye0 = lookat1.clone(); const lookat0 = eye0.clone().add(forward.clone().normalize().multiplyScalar(10)); const up0 = up1.clone(); const eye2 = eye.clone(); const lookat2 = lookat.clone(); const up2 = new Vector3(0, 0, 1); const eye3 = eye.clone().add(lookat2.clone().sub(eye2).normalize().multiplyScalar(-10)); const lookat3 = lookat2.clone(); const up3 = up2.clone(); this._curveE = new CatmullRomCurve3([eye0, eye1, eye2, eye3]); this._curveL = new CatmullRomCurve3([lookat0, lookat1, lookat2, lookat3]); this._curveU = new CatmullRomCurve3([up0, up1, up2, up3]); this._zoom0 = this._zoom; this._zoom1 = 0; this._camera.focal = 0.5 / Math.tan(Math.PI / 4); } get _isTransitioning() { return this._transition < 1; } dolly(delta) { if (this._isTransitioning) { return; } const camera = this._camera; const offset = camera.position .clone() .sub(camera.lookat); const length = offset.length(); const scaled = length * Math.pow(2, -delta); const clipped = Math.max(1, Math.min(scaled, 4000)); offset.normalize(); offset.multiplyScalar(clipped); camera.position .copy(camera.lookat) .add(offset); } orbit(rotation) { if (this._isTransitioning) { return; } const camera = this._camera; const q = new Quaternion() .setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); const qInverse = q .clone() .invert(); const offset = camera.position .clone() .sub(camera.lookat); offset.applyQuaternion(q); const length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += rotation.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += rotation.theta; const threshold = Math.PI / 36; theta = Math.max(threshold, Math.min(Math.PI / 2 - threshold, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); camera.position .copy(camera.lookat) .add(offset.multiplyScalar(length)); } truck(direction) { if (this._isTransitioning) { return; } const camera = this._camera; camera.position .add(new Vector3().fromArray(direction)); camera.lookat .add(new Vector3().fromArray(direction)); } update(delta) { if (!this._isTransitioning) { return; } this._transition = Math.min(this._transition + 2 * delta / 3, 1); const sta = MathUtils.smootherstep(this._transition, 0, 1); const t = (sta + 1) / 3; const eye = this._curveE.getPoint(t); const lookat = this._curveL.getPoint(t); const up = this._curveU.getPoint(t); this._camera.position.copy(eye); this._camera.lookat.copy(lookat); this._camera.up.copy(up); this._zoom = MathUtils.lerp(this._zoom0, this._zoom1, sta); this._stateTransitionAlpha = sta; } _getStateTransitionAlpha() { return this._stateTransitionAlpha; } } class EulerRotationDelta { constructor(phi, theta) { this._phi = phi; this._theta = theta; } get phi() { return this._phi; } set phi(value) { this._phi = value; } get theta() { return this._theta; } set theta(value) { this._theta = value; } get isZero() { return this._phi === 0 && this._theta === 0; } copy(delta) { this._phi = delta.phi; this._theta = delta.theta; } lerp(other, alpha) { this._phi = (1 - alpha) * this._phi + alpha * other.phi; this._theta = (1 - alpha) * this._theta + alpha * other.theta; } multiply(value) { this._phi *= value; this._theta *= value; } threshold(value) { this._phi = Math.abs(this._phi) > value ? this._phi : 0; this._theta = Math.abs(this._theta) > value ? this._theta : 0; } lengthSquared() { return this._phi * this._phi + this._theta * this._theta; } reset() { this._phi = 0; this._theta = 0; } } class InteractiveStateBase extends StateBase { constructor(state) { super(state); this._animationSpeed = 1 / 40; this._rotationDelta = new EulerRotationDelta(0, 0); this._requestedRotationDelta = null; this._basicRotation = [0, 0]; this._requestedBasicRotation = null; this._requestedBasicRotationUnbounded = null; this._rotationAcceleration = 0.86; this._rotationIncreaseAlpha = 0.97; this._rotationDecreaseAlpha = 0.9; this._rotationThreshold = 1e-3; this._unboundedRotationAlpha = 0.8; this._desiredZoom = state.zoom; this._minZoom = 0; this._maxZoom = 3; this._lookatDepth = 10; this._desiredLookat = null; this._desiredCenter = null; } rotate(rotationDelta) { if (this._currentImage == null) { return; } if (rotationDelta.phi === 0 && rotationDelta.theta === 0) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedBasicRotation = null; if (this._requestedRotationDelta != null) { this._requestedRotationDelta.phi = this._requestedRotationDelta.phi + rotationDelta.phi; this._requestedRotationDelta.theta = this._requestedRotationDelta.theta + rotationDelta.theta; } else { this._requestedRotationDelta = new EulerRotationDelta(rotationDelta.phi, rotationDelta.theta); } } rotateUnbounded(delta) { if (this._currentImage == null) { return; } this._requestedBasicRotation = null; this._requestedRotationDelta = null; this._applyRotation(delta, this._currentCamera); this._applyRotation(delta, this._previousCamera); if (!this._desiredLookat) { return; } const q = new Quaternion().setFromUnitVectors(this._currentCamera.up, new Vector3(0, 0, 1)); const qInverse = q.clone().invert(); const offset = new Vector3() .copy(this._desiredLookat) .sub(this._camera.position) .applyQuaternion(q); const length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += delta.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += delta.theta; theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); this._desiredLookat .copy(this._camera.position) .add(offset.multiplyScalar(length)); } rotateWithoutInertia(rotationDelta) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedBasicRotation = null; this._requestedRotationDelta = null; const threshold = Math.PI / (10 * Math.pow(2, this._zoom)); const delta = { phi: this._spatial.clamp(rotationDelta.phi, -threshold, threshold), theta: this._spatial.clamp(rotationDelta.theta, -threshold, threshold), }; this._applyRotation(delta, this._currentCamera); this._applyRotation(delta, this._previousCamera); } rotateBasic(basicRotation) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedRotationDelta = null; if (this._requestedBasicRotation != null) { this._requestedBasicRotation[0] += basicRotation[0]; this._requestedBasicRotation[1] += basicRotation[1]; let threshold = 0.05 / Math.pow(2, this._zoom); this._requestedBasicRotation[0] = this._spatial.clamp(this._requestedBasicRotation[0], -threshold, threshold); this._requestedBasicRotation[1] = this._spatial.clamp(this._requestedBasicRotation[1], -threshold, threshold); } else { this._requestedBasicRotation = basicRotation.slice(); } } rotateBasicUnbounded(basicRotation) { if (this._currentImage == null) { return; } if (this._requestedBasicRotationUnbounded != null) { this._requestedBasicRotationUnbounded[0] += basicRotation[0]; this._requestedBasicRotationUnbounded[1] += basicRotation[1]; } else { this._requestedBasicRotationUnbounded = basicRotation.slice(); } } rotateBasicWithoutInertia(basic) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; const threshold = 0.05 / Math.pow(2, this._zoom); const basicRotation = basic.slice(); basicRotation[0] = this._spatial.clamp(basicRotation[0], -threshold, threshold); basicRotation[1] = this._spatial.clamp(basicRotation[1], -threshold, threshold); this._applyRotationBasic(basicRotation); } rotateToBasic(basic) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; basic[0] = this._spatial.clamp(basic[0], 0, 1); basic[1] = this._spatial.clamp(basic[1], 0, 1); let lookat = this.currentTransform.unprojectBasic(basic, this._lookatDepth); this._currentCamera.lookat.fromArray(lookat); } zoomIn(delta, reference) { if (this._currentImage == null) { return; } this._desiredZoom = Math.max(this._minZoom, Math.min(this._maxZoom, this._desiredZoom + delta)); let currentCenter = this.currentTransform.projectBasic(this._currentCamera.lookat.toArray()); let currentCenterX = currentCenter[0]; let currentCenterY = currentCenter[1]; let zoom0 = Math.pow(2, this._zoom); let zoom1 = Math.pow(2, this._desiredZoom); let refX = reference[0]; let refY = reference[1]; if (isSpherical(this.currentTransform.cameraType)) { if (refX - currentCenterX > 0.5) { refX = refX - 1; } else if (currentCenterX - refX > 0.5) { refX = 1 + refX; } } let newCenterX = refX - zoom0 / zoom1 * (refX - currentCenterX); let newCenterY = refY - zoom0 / zoom1 * (refY - currentCenterY); if (isSpherical(this._currentImage.cameraType)) { newCenterX = this._spatial .wrap(newCenterX + this._basicRotation[0], 0, 1); newCenterY = this._spatial .clamp(newCenterY + this._basicRotation[1], 0.05, 0.95); } else { newCenterX = this._spatial.clamp(newCenterX, 0, 1); newCenterY = this._spatial.clamp(newCenterY, 0, 1); } this._desiredLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic([newCenterX, newCenterY], this._lookatDepth)); } setCenter(center) { this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; this._desiredZoom = this._zoom; let clamped = [ this._spatial.clamp(center[0], 0, 1), this._spatial.clamp(center[1], 0, 1), ]; if (this._currentImage == null) { this._desiredCenter = clamped; return; } this._desiredCenter = null; let currentLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic(clamped, this._lookatDepth)); let previousTransform = this.previousTransform != null ? this.previousTransform : this.currentTransform; let previousLookat = new Vector3() .fromArray(previousTransform.unprojectBasic(clamped, this._lookatDepth)); this._currentCamera.lookat.copy(currentLookat); this._previousCamera.lookat.copy(previousLookat); } setZoom(zoom) { this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; this._zoom = this._spatial.clamp(zoom, this._minZoom, this._maxZoom); this._desiredZoom = this._zoom; } _applyRotation(delta, camera) { if (camera == null) { return; } let q = new Quaternion().setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); let qInverse = q.clone().invert(); let offset = new Vector3(); offset.copy(camera.lookat).sub(camera.position); offset.applyQuaternion(q); let length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += delta.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += delta.theta; theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); camera.lookat.copy(camera.position).add(offset.multiplyScalar(length)); } _applyRotationBasic(basicRotation) { let currentImage = this._currentImage; let previousImage = this._previousImage != null ? this.previousImage : this.currentImage; let currentCamera = this._currentCamera; let previousCamera = this._previousCamera; let currentTransform = this.currentTransform; let previousTransform = this.previousTransform != null ? this.previousTransform : this.currentTransform; let currentBasic = currentTransform.projectBasic(currentCamera.lookat.toArray()); let previousBasic = previousTransform.projectBasic(previousCamera.lookat.toArray()); if (isSpherical(currentImage.cameraType)) { currentBasic[0] = this._spatial.wrap(currentBasic[0] + basicRotation[0], 0, 1); currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0.05, 0.95); } else { currentBasic[0] = this._spatial.clamp(currentBasic[0] + basicRotation[0], 0, 1); currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } if (isSpherical(previousImage.cameraType)) { previousBasic[0] = this._spatial.wrap(previousBasic[0] + basicRotation[0], 0, 1); previousBasic[1] = this._spatial.clamp(previousBasic[1] + basicRotation[1], 0.05, 0.95); } else { previousBasic[0] = this._spatial.clamp(previousBasic[0] + basicRotation[0], 0, 1); previousBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } let currentLookat = currentTransform.unprojectBasic(currentBasic, this._lookatDepth); currentCamera.lookat.fromArray(currentLookat); let previousLookat = previousTransform.unprojectBasic(previousBasic, this._lookatDepth); previousCamera.lookat.fromArray(previousLookat); } _updateZoom(animationSpeed) { let diff = this._desiredZoom - this._zoom; let sign = diff > 0 ? 1 : diff < 0 ? -1 : 0; if (diff === 0) { return; } else if (Math.abs(diff) < 2e-3) { this._zoom = this._desiredZoom; if (this._desiredLookat != null) { this._desiredLookat = null; } } else { this._zoom += sign * Math.max(Math.abs(5 * animationSpeed * diff), 2e-3); } } _updateLookat(animationSpeed) { if (this._desiredLookat === null) { return; } let diff = this._desiredLookat.distanceToSquared(this._currentCamera.lookat); if (Math.abs(diff) < 1e-6) { this._currentCamera.lookat.copy(this._desiredLookat); this._desiredLookat = null; } else { this._currentCamera.lookat.lerp(this._desiredLookat, 5 * animationSpeed); } } _updateRotation() { if (this._requestedRotationDelta != null) { let length = this._rotationDelta.lengthSquared(); let requestedLength = this._requestedRotationDelta.lengthSquared(); if (requestedLength > length) { this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationIncreaseAlpha); } else { this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationDecreaseAlpha); } this._requestedRotationDelta = null; return; } if (this._rotationDelta.isZero) { return; } const alpha = isSpherical(this.currentImage.cameraType) ? 1 : this._alpha; this._rotationDelta.multiply(this._rotationAcceleration * alpha); this._rotationDelta.threshold(this._rotationThreshold); } _updateRotationBasic() { if (this._requestedBasicRotation != null) { let x = this._basicRotation[0]; let y = this._basicRotation[1]; let reqX = this._requestedBasicRotation[0]; let reqY = this._requestedBasicRotation[1]; if (Math.abs(reqX) > Math.abs(x)) { this._basicRotation[0] = (1 - this._rotationIncreaseAlpha) * x + this._rotationIncreaseAlpha * reqX; } else { this._basicRotation[0] = (1 - this._rotationDecreaseAlpha) * x + this._rotationDecreaseAlpha * reqX; } if (Math.abs(reqY) > Math.abs(y)) { this._basicRotation[1] = (1 - this._rotationIncreaseAlpha) * y + this._rotationIncreaseAlpha * reqY; } else { this._basicRotation[1] = (1 - this._rotationDecreaseAlpha) * y + this._rotationDecreaseAlpha * reqY; } this._requestedBasicRotation = null; return; } if (this._requestedBasicRotationUnbounded != null) { let reqX = this._requestedBasicRotationUnbounded[0]; let reqY = this._requestedBasicRotationUnbounded[1]; if (Math.abs(reqX) > 0) { this._basicRotation[0] = (1 - this._unboundedRotationAlpha) * this._basicRotation[0] + this._unboundedRotationAlpha * reqX; } if (Math.abs(reqY) > 0) { this._basicRotation[1] = (1 - this._unboundedRotationAlpha) * this._basicRotation[1] + this._unboundedRotationAlpha * reqY; } if (this._desiredLookat != null) { let desiredBasicLookat = this.currentTransform.projectBasic(this._desiredLookat.toArray()); desiredBasicLookat[0] += reqX; desiredBasicLookat[1] += reqY; this._desiredLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic(desiredBasicLookat, this._lookatDepth)); } this._requestedBasicRotationUnbounded = null; } if (this._basicRotation[0] === 0 && this._basicRotation[1] === 0) { return; } this._basicRotation[0] = this._rotationAcceleration * this._basicRotation[0]; this._basicRotation[1] = this._rotationAcceleration * this._basicRotation[1]; if (Math.abs(this._basicRotation[0]) < this._rotationThreshold / Math.pow(2, this._zoom) && Math.abs(this._basicRotation[1]) < this._rotationThreshold / Math.pow(2, this._zoom)) { this._basicRotation = [0, 0]; } } _clearRotation() { if (isSpherical(this._currentImage.cameraType)) { return; } if (this._requestedRotationDelta != null) { this._requestedRotationDelta = null; } if (!this._rotationDelta.isZero) { this._rotationDelta.reset(); } if (this._requestedBasicRotation != null) { this._requestedBasicRotation = null; } if (this._basicRotation[0] > 0 || this._basicRotation[1] > 0) { this._basicRotation = [0, 0]; } } _setDesiredCenter() { if (this._desiredCenter == null) { return; } let lookatDirection = new Vector3() .fromArray(this.currentTransform.unprojectBasic(this._desiredCenter, this._lookatDepth)) .sub(this._currentCamera.position); this._currentCamera.lookat.copy(this._currentCamera.position.clone().add(lookatDirection)); this._previousCamera.lookat.copy(this._previousCamera.position.clone().add(lookatDirection)); this._desiredCenter = null; } _setDesiredZoom() { this._desiredZoom = isSpherical(this._currentImage.cameraType) || this._previousImage == null ? this._zoom : 0; } } class TraversingState extends InteractiveStateBase { constructor(state) { super(state); this._adjustCameras(); this._motionless = this._motionlessTransition(); this._baseAlpha = this._alpha; this._speedCoefficient = 1; this._smoothing = false; } append(images) { let emptyTrajectory = this._trajectory.length === 0; if (emptyTrajectory) { this._resetTransition(); } super.append(images); if (emptyTrajectory) { this._setDesiredCenter(); this._setDesiredZoom(); } } prepend(images) { let emptyTrajectory = this._trajectory.length === 0; if (emptyTrajectory) { this._resetTransition(); } super.prepend(images); if (emptyTrajectory) { this._setDesiredCenter(); this._setDesiredZoom(); } } set(images) { super.set(images); this._desiredLookat = null; this._resetTransition(); this._clearRotation(); this._setDesiredCenter(); this._setDesiredZoom(); if (this._trajectory.length < 3) { this._smoothing = true; } } setSpeed(speed) { this._speedCoefficient = this._spatial.clamp(speed, 0, 10); } update(delta) { if (this._alpha === 1 && this._currentIndex + this._alpha < this._trajectory.length) { this._currentIndex += 1; this._smoothing = this._trajectory.length < 3 && this._currentIndex + 1 === this._trajectory.length; this._setCurrent(); this._resetTransition(); this._clearRotation(); this._desiredZoom = isSpherical(this._currentImage.cameraType) ? this._zoom : 0; this._desiredLookat = null; } let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; this._baseAlpha = Math.min(1, this._baseAlpha + this._speedCoefficient * animationSpeed); if (this._smoothing) { this._alpha = MathUtils.smootherstep(this._baseAlpha, 0, 1); } else { this._alpha = this._baseAlpha; } this._updateRotation(); if (!this._rotationDelta.isZero) { this._applyRotation(this._rotationDelta, this._previousCamera); this._applyRotation(this._rotationDelta, this._currentCamera); } this._updateRotationBasic(); if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { this._applyRotationBasic(this._basicRotation); } this._updateZoom(animationSpeed); this._updateLookat(animationSpeed); this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.ceil(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } let lookat = this._camera.lookat.clone().sub(this._camera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); if (isSpherical(this._currentImage.cameraType) && !isNullImageId(this._previousImage.id)) { this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } } _resetTransition() { this._alpha = 0; this._baseAlpha = 0; this._motionless = this._motionlessTransition(); } } const UP = new Vector3(0, 0, 1); class GravityTraversingState extends TraversingState { constructor(state) { super(state); this._camera.up.copy(UP); this._alignCameras(); this._alignTrajectory(); } append(images) { super.append(images); this._alignTrajectory(); } prepend(images) { super.prepend(images); this._alignTrajectory(); } set(images) { super.set(images); this._alignCameras(); this._alignTrajectory(); } update(delta) { super.update(delta); } _alignTrajectory() { for (const camera of this._trajectoryCameras) { camera.up.copy(UP); } } _alignCameras() { var _a, _b; (_a = this._previousCamera) === null || _a === void 0 ? void 0 : _a.up.copy(UP); (_b = this._currentCamera) === null || _b === void 0 ? void 0 : _b.up.copy(UP); } } class InteractiveWaitingState extends InteractiveStateBase { constructor(state) { super(state); this._adjustCameras(); this._motionless = this._motionlessTransition(); } prepend(images) { super.prepend(images); this._motionless = this._motionlessTransition(); } set(images) { super.set(images); this._motionless = this._motionlessTransition(); } move(delta) { this._alpha = Math.max(0, Math.min(1, this._alpha + delta)); } moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } update(delta) { this._updateRotation(); if (!this._rotationDelta.isZero) { this._applyRotation(this._rotationDelta, this._previousCamera); this._applyRotation(this._rotationDelta, this._currentCamera); } this._updateRotationBasic(); if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { this._applyRotationBasic(this._basicRotation); } let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; this._updateZoom(animationSpeed); this._updateLookat(animationSpeed); this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.round(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } if (isSpherical(this._currentImage.cameraType)) { let lookat = this._camera.lookat.clone().sub(this._camera.position); this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } if (isSpherical(this._previousImage.cameraType)) { let lookat = this._currentCamera.lookat.clone().sub(this._currentCamera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); } } } class WaitingState extends StateBase { constructor(state) { super(state); this._zoom = 0; this._adjustCameras(); this._motionless = this._motionlessTransition(); } prepend(images) { super.prepend(images); this._motionless = this._motionlessTransition(); } set(images) { super.set(images); this._motionless = this._motionlessTransition(); } move(delta) { this._alpha = Math.max(0, Math.min(1, this._alpha + delta)); } moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } update() { this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.round(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } if (isSpherical(this._currentImage.cameraType)) { let lookat = this._camera.lookat.clone().sub(this._camera.position); this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } if (isSpherical(this._previousImage.cameraType)) { let lookat = this._currentCamera.lookat.clone().sub(this._currentCamera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); } } } class StateTransitionMatrix { constructor() { const custom = State[State.Custom]; const earth = State[State.Earth]; const gravityTraverse = State[State.GravityTraversing]; const traverse = State[State.Traversing]; const wait = State[State.Waiting]; const waitInteractively = State[State.WaitingInteractively]; this._creators = new Map(); const creator = this._creators; creator.set(custom, CustomState); creator.set(earth, EarthState); creator.set(gravityTraverse, GravityTraversingState); creator.set(traverse, TraversingState); creator.set(wait, WaitingState); creator.set(waitInteractively, InteractiveWaitingState); this._transitions = new Map(); const transitions = this._transitions; transitions.set(custom, [earth, gravityTraverse, traverse]); transitions.set(earth, [custom, gravityTraverse, traverse]); transitions.set(gravityTraverse, [custom, earth, traverse, wait, waitInteractively]); transitions.set(traverse, [custom, earth, gravityTraverse, wait, waitInteractively]); transitions.set(wait, [gravityTraverse, traverse, waitInteractively]); transitions.set(waitInteractively, [gravityTraverse, traverse, wait]); } getState(state) { if (state instanceof CustomState) { return State.Custom; } else if (state instanceof EarthState) { return State.Earth; } else if (state instanceof GravityTraversingState) { return State.GravityTraversing; } else if (state instanceof TraversingState) { return State.Traversing; } else if (state instanceof WaitingState) { return State.Waiting; } else if (state instanceof InteractiveWaitingState) { return State.WaitingInteractively; } throw new Error("Invalid state instance"); } generate(state, options) { const concreteState = this._creators.get(State[state]); return new concreteState(options); } transition(state, to) { if (!this.validate(state, to)) { throw new Error("Invalid transition"); } return this.generate(to, state); } validate(state, to) { const source = State[this.getState(state)]; const target = State[to]; const transitions = this._transitions; return transitions.has(source) && transitions.get(source).includes(target); } } class StateContext { constructor(state, transitionMode) { this._transitions = new StateTransitionMatrix(); this._state = this._transitions.generate(state, { alpha: 1, camera: new Camera(), currentIndex: -1, reference: { alt: 0, lat: 0, lng: 0 }, trajectory: [], transitionMode: transitionMode == null ? exports.TransitionMode.Default : transitionMode, zoom: 0, }); } get state() { return this._transitions.getState(this._state); } get reference() { return this._state.reference; } get alpha() { return this._state.alpha; } get stateTransitionAlpha() { return this._state.stateTransitionAlpha; } get camera() { return this._state.camera; } get zoom() { return this._state.zoom; } get currentImage() { return this._state.currentImage; } get previousImage() { return this._state.previousImage; } get currentCamera() { return this._state.currentCamera; } get previousCamera() { return this._state.previousCamera; } get currentTransform() { return this._state.currentTransform; } get previousTransform() { return this._state.previousTransform; } get trajectory() { return this._state.trajectory; } get currentIndex() { return this._state.currentIndex; } get lastImage() { return this._state.trajectory[this._state.trajectory.length - 1]; } get imagesAhead() { return this._state.trajectory.length - 1 - this._state.currentIndex; } get motionless() { return this._state.motionless; } custom() { this._transition(State.Custom); } earth() { this._transition(State.Earth); } gravityTraverse() { this._transition(State.GravityTraversing); } traverse() { this._transition(State.Traversing); } wait() { this._transition(State.Waiting); } waitInteractively() { this._transition(State.WaitingInteractively); } getCenter() { return this._state.getCenter(); } setCenter(center) { this._state.setCenter(center); } setZoom(zoom) { this._state.setZoom(zoom); } update(delta) { this._state.update(delta); } append(images) { this._state.append(images); } prepend(images) { this._state.prepend(images); } remove(n) { this._state.remove(n); } clear() { this._state.clear(); } clearPrior() { this._state.clearPrior(); } cut() { this._state.cut(); } set(images) { this._state.set(images); } setViewMatrix(matrix) { this._state.setViewMatrix(matrix); } rotate(delta) { this._state.rotate(delta); } rotateUnbounded(delta) { this._state.rotateUnbounded(delta); } rotateWithoutInertia(delta) { this._state.rotateWithoutInertia(delta); } rotateBasic(basicRotation) { this._state.rotateBasic(basicRotation); } rotateBasicUnbounded(basicRotation) { this._state.rotateBasicUnbounded(basicRotation); } rotateBasicWithoutInertia(basicRotation) { this._state.rotateBasicWithoutInertia(basicRotation); } rotateToBasic(basic) { this._state.rotateToBasic(basic); } move(delta) { this._state.move(delta); } moveTo(delta) { this._state.moveTo(delta); } zoomIn(delta, reference) { this._state.zoomIn(delta, reference); } setSpeed(speed) { this._state.setSpeed(speed); } setTransitionMode(mode) { this._state.setTransitionMode(mode); } dolly(delta) { this._state.dolly(delta); } orbit(rotation) { this._state.orbit(rotation); } truck(direction) { this._state.truck(direction); } _transition(to) { if (!this._transitions.validate(this._state, to)) { const from = this._transitions.getState(this._state); console.warn(`Transition not valid (${State[from]} - ${State[to]})`); return; } const state = this._transitions.transition(this._state, to); this._state = state; } } class StateService { constructor(initialState, transitionMode) { this._appendImage$ = new Subject(); this._clock = new Clock(); this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._start$ = new Subject(); this._frame$ = new Subject(); this._contextOperation$ = new BehaviorSubject((context) => { return context; }); this._context$ = this._contextOperation$.pipe(scan((context, operation) => { return operation(context); }, new StateContext(initialState, transitionMode)), publishReplay(1), refCount()); this._state$ = this._context$.pipe(map((context) => { return context.state; }), distinctUntilChanged(), publishReplay(1), refCount()); this._currentState$ = this._frame$.pipe(withLatestFrom(this._context$, (frameId, context) => { return [frameId, context]; }), filter((fc) => { return fc[1].currentImage != null; }), tap((fc) => { fc[1].update(this._clock.getDelta()); }), map((fc) => { return { fps: 60, id: fc[0], state: fc[1] }; }), share()); this._lastState$ = this._currentState$.pipe(publishReplay(1), refCount()); let imageChanged$ = this._currentState$.pipe(distinctUntilChanged(undefined, (f) => { return f.state.currentImage.id; }), publishReplay(1), refCount()); let imageChangedSubject$ = new Subject(); subs.push(imageChanged$ .subscribe(imageChangedSubject$)); this._reference$ = imageChangedSubject$.pipe(map((f) => { const { reference } = f.state; return { lng: reference.lng, lat: reference.lat, alt: reference.alt, }; }), distinctUntilChanged((r1, r2) => { return r1.lat === r2.lat && r1.lng === r2.lng; }), publishReplay(1), refCount()); this._currentId$ = new BehaviorSubject(null); subs.push(imageChangedSubject$.pipe(map((f) => { return f.state.currentImage.id; })) .subscribe(this._currentId$)); this._currentImage$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentImage; }), publishReplay(1), refCount()); this._currentCamera$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentCamera; }), publishReplay(1), refCount()); this._currentTransform$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentTransform; }), publishReplay(1), refCount()); this._currentImageExternal$ = imageChanged$.pipe(map((f) => { return f.state.currentImage; }), publishReplay(1), refCount()); subs.push(this._appendImage$.pipe(map((image) => { return (context) => { context.append([image]); return context; }; })) .subscribe(this._contextOperation$)); this._inMotionOperation$ = new Subject(); subs.push(imageChanged$.pipe(map(() => { return true; })) .subscribe(this._inMotionOperation$)); subs.push(this._inMotionOperation$.pipe(distinctUntilChanged(), filter((moving) => { return moving; }), switchMap(() => { return this._currentState$.pipe(filter((frame) => { return frame.state.imagesAhead === 0; }), map((frame) => { return [frame.state.camera.clone(), frame.state.zoom]; }), pairwise(), map((pair) => { let c1 = pair[0][0]; let c2 = pair[1][0]; let z1 = pair[0][1]; let z2 = pair[1][1]; return c1.diff(c2) > 1e-5 || Math.abs(z1 - z2) > 1e-5; }), first((changed) => { return !changed; })); })) .subscribe(this._inMotionOperation$)); this._inMotion$ = this._inMotionOperation$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); this._inTranslationOperation$ = new Subject(); subs.push(imageChanged$.pipe(map(() => { return true; })) .subscribe(this._inTranslationOperation$)); subs.push(this._inTranslationOperation$.pipe(distinctUntilChanged(), filter((inTranslation) => { return inTranslation; }), switchMap(() => { return this._currentState$.pipe(filter((frame) => { return frame.state.imagesAhead === 0; }), map((frame) => { return frame.state.camera.position.clone(); }), pairwise(), map((pair) => { return pair[0].distanceToSquared(pair[1]) !== 0; }), first((changed) => { return !changed; })); })) .subscribe(this._inTranslationOperation$)); this._inTranslation$ = this._inTranslationOperation$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); subs.push(this._reference$.subscribe(() => { })); subs.push(this._state$.subscribe(() => { })); subs.push(this._currentImage$.subscribe(() => { })); subs.push(this._currentCamera$.subscribe(() => { })); subs.push(this._currentTransform$.subscribe(() => { })); subs.push(this._currentImageExternal$.subscribe(() => { })); subs.push(this._lastState$.subscribe(() => { })); subs.push(this._inMotion$.subscribe(() => { })); subs.push(this._inTranslation$.subscribe(() => { })); this._frameId = null; this._frameGenerator = new FrameGenerator(window); } get currentState$() { return this._currentState$; } get currentImage$() { return this._currentImage$; } get currentId$() { return this._currentId$; } get currentImageExternal$() { return this._currentImageExternal$; } get currentCamera$() { return this._currentCamera$; } get currentTransform$() { return this._currentTransform$; } get state$() { return this._state$; } get reference$() { return this._reference$; } get inMotion$() { return this._inMotion$; } get inTranslation$() { return this._inTranslation$; } get appendImage$() { return this._appendImage$; } dispose() { this.stop(); this._subscriptions.unsubscribe(); } custom() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.custom(); }); } earth() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.earth(); }); } gravityTraverse() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.gravityTraverse(); }); } traverse() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.traverse(); }); } wait() { this._invokeContextOperation((context) => { context.wait(); }); } waitInteractively() { this._invokeContextOperation((context) => { context.waitInteractively(); }); } appendImagess(images) { this._invokeContextOperation((context) => { context.append(images); }); } prependImages(images) { this._invokeContextOperation((context) => { context.prepend(images); }); } removeImages(n) { this._invokeContextOperation((context) => { context.remove(n); }); } clearImages() { this._invokeContextOperation((context) => { context.clear(); }); } clearPriorImages() { this._invokeContextOperation((context) => { context.clearPrior(); }); } cutImages() { this._invokeContextOperation((context) => { context.cut(); }); } setImages(images) { this._invokeContextOperation((context) => { context.set(images); }); } setViewMatrix(matrix) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setViewMatrix(matrix); }); } rotate(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotate(delta); }); } rotateUnbounded(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateUnbounded(delta); }); } rotateWithoutInertia(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateWithoutInertia(delta); }); } rotateBasic(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasic(basicRotation); }); } rotateBasicUnbounded(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasicUnbounded(basicRotation); }); } rotateBasicWithoutInertia(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasicWithoutInertia(basicRotation); }); } rotateToBasic(basic) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateToBasic(basic); }); } move(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.move(delta); }); } moveTo(position) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.moveTo(position); }); } dolly(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.dolly(delta); }); } orbit(rotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.orbit(rotation); }); } truck(direction) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.truck(direction); }); } /** * Change zoom level while keeping the reference point position approximately static. * * @parameter {number} delta - Change in zoom level. * @parameter {Array} reference - Reference point in basic coordinates. */ zoomIn(delta, reference) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.zoomIn(delta, reference); }); } getCenter() { return this._lastState$.pipe(first(), map((frame) => { return frame.state.getCenter(); })); } getZoom() { return this._lastState$.pipe(first(), map((frame) => { return frame.state.zoom; })); } setCenter(center) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setCenter(center); }); } setSpeed(speed) { this._invokeContextOperation((context) => { context.setSpeed(speed); }); } setTransitionMode(mode) { this._invokeContextOperation((context) => { context.setTransitionMode(mode); }); } setZoom(zoom) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setZoom(zoom); }); } start() { this._clock.start(); if (this._frameId == null) { this._start$.next(null); this._frameId = this._frameGenerator.requestAnimationFrame(this._frame.bind(this)); this._frame$.next(this._frameId); } } stop() { this._clock.stop(); if (this._frameId != null) { this._frameGenerator.cancelAnimationFrame(this._frameId); this._frameId = null; } } _invokeContextOperation(action) { this._contextOperation$ .next((context) => { action(context); return context; }); } _frame() { this._frameId = this._frameGenerator.requestAnimationFrame(this._frame.bind(this)); this._frame$.next(this._frameId); } } function cameraControlsToState(cameraControls) { switch (cameraControls) { case exports.CameraControls.Custom: return State.Custom; case exports.CameraControls.Earth: return State.Earth; case exports.CameraControls.Gravity: return State.GravityTraversing; case exports.CameraControls.Street: return State.Traversing; default: return null; } } class Navigator { constructor(options, api, graphService, loadingService, stateService, cacheService, playService, panService) { var _a; if (api) { this._api = api; } else if (options.dataProvider) { this._api = new APIWrapper(options.dataProvider); } else { this._api = new APIWrapper(new GraphDataProvider({ accessToken: options.accessToken, })); } this._projectionService = new ProjectionService(); this._graphService = graphService !== null && graphService !== void 0 ? graphService : new GraphService(new Graph(this.api, options), this._projectionService); this._loadingName = "navigator"; this._loadingService = loadingService !== null && loadingService !== void 0 ? loadingService : new LoadingService(); const cameraControls = (_a = options.cameraControls) !== null && _a !== void 0 ? _a : exports.CameraControls.Street; this._stateService = stateService !== null && stateService !== void 0 ? stateService : new StateService(cameraControlsToState(cameraControls), options.transitionMode); this._cacheService = cacheService !== null && cacheService !== void 0 ? cacheService : new CacheService(this._graphService, this._stateService, this._api); this._playService = playService !== null && playService !== void 0 ? playService : new PlayService(this._graphService, this._stateService); this._panService = panService !== null && panService !== void 0 ? panService : new PanService(this._graphService, this._stateService, this._projectionService, options.combinedPanning); this._idRequested$ = new BehaviorSubject(null); this._movedToId$ = new BehaviorSubject(null); this._request$ = null; this._requestSubscription = null; this._imageRequestSubscription = null; } get api() { return this._api; } get cacheService() { return this._cacheService; } get graphService() { return this._graphService; } get loadingService() { return this._loadingService; } get movedToId$() { return this._movedToId$; } get panService() { return this._panService; } get playService() { return this._playService; } get projectionService() { return this._projectionService; } get stateService() { return this._stateService; } dispose() { this._abortRequest("viewer removed"); this._cacheService.stop(); this._graphService.dispose(); this._panService.dispose(); this._playService.dispose(); this._stateService.dispose(); } moveDir$(direction) { this._abortRequest(`in dir ${exports.NavigationDirection[direction]}`); this._loadingService.startLoading(this._loadingName); const image$ = this.stateService.currentImage$.pipe(first(), mergeMap((image) => { return ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first(), map((status) => { for (let edge of status.edges) { if (edge.data.direction === direction) { return edge.target; } } return null; })); }), mergeMap((directionId) => { if (directionId == null) { this._loadingService.stopLoading(this._loadingName); return throwError(new Error(`Direction (${direction}) does not exist for current image.`)); } return this._moveTo$(directionId); })); return this._makeRequest$(image$); } moveTo$(id) { this._abortRequest(`to id ${id}`); this._loadingService.startLoading(this._loadingName); const image$ = this._moveTo$(id); return this._makeRequest$(image$); } reset$() { this._abortRequest("to reset"); return this._reset$() .pipe(map(() => undefined)); } setFilter$(filter) { this._stateService.clearImages(); return this._movedToId$.pipe(first(), mergeMap((id) => { if (id != null) { return this._trajectoryIds$().pipe(mergeMap((ids) => { return this._graphService.setFilter$(filter).pipe(mergeMap(() => { return this._cacheIds$(ids); })); }), takeLast(1)); } return this._idRequested$.pipe(first(), mergeMap((requestedId) => { if (requestedId != null) { return this._graphService.setFilter$(filter).pipe(mergeMap(() => { return this._graphService.cacheImage$(requestedId); })); } return this._graphService.setFilter$(filter).pipe(map(() => { return undefined; })); })); }), map(() => { return undefined; })); } setAccessToken$(accessToken) { this._abortRequest("to set user token"); return this._reset$(() => this._api.setAccessToken(accessToken)); } _cacheIds$(ids) { const cacheImages$ = ids .map((id) => { return this._graphService.hasImage$(id).pipe(filter((exists) => { return exists; }), mergeMap(() => { return this._graphService.cacheImage$(id); })); }); return from(cacheImages$).pipe(mergeAll()); } _abortRequest(reason) { if (this._requestSubscription != null) { this._requestSubscription.unsubscribe(); this._requestSubscription = null; } if (this._imageRequestSubscription != null) { this._imageRequestSubscription.unsubscribe(); this._imageRequestSubscription = null; } this._idRequested$.next(null); if (this._request$ != null) { if (!(this._request$.isStopped || this._request$.hasError)) { this._request$.error(new CancelMapillaryError(`Request aborted by a subsequent request ${reason}.`)); } this._request$ = null; } } _makeRequest$(image$) { const request$ = new ReplaySubject(1); this._requestSubscription = request$ .subscribe(undefined, () => { }); this._request$ = request$; this._imageRequestSubscription = image$ .subscribe((image) => { this._request$ = null; request$.next(image); request$.complete(); }, (error) => { this._request$ = null; request$.error(error); }); return request$; } _moveTo$(id) { this._idRequested$.next(id); return this._graphService.cacheImage$(id).pipe(tap((image) => { this._stateService.setImages([image]); this._movedToId$.next(image.id); }), finalize(() => { this._loadingService.stopLoading(this._loadingName); })); } _reset$(preCallback) { this._movedToId$.next(null); return makeNullImage$().pipe(tap((image) => { this._stateService.setImages([image]); this._stateService.setImages([image]); this._stateService.clearImages(); }), first(), tap(() => { if (preCallback) { preCallback(); } }), mergeMap(() => { return this._graphService.reset$(); })); } _trajectoryIds$() { return this._stateService.currentState$.pipe(first(), map((frame) => { return frame.state.trajectory .map((image) => { return image.id; }) .filter((id) => { return !isNullImageId(id); }); })); } } class Projection { constructor(viewportCoords, spatial) { this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); } basicToCanvas(basicPoint, container, render, transform) { return this._viewportCoords .basicToCanvasSafe(basicPoint[0], basicPoint[1], container, transform, render.perspective); } canvasToBasic(canvasPoint, container, render, transform) { let basicPoint = this._viewportCoords .canvasToBasic(canvasPoint[0], canvasPoint[1], container, transform, render.perspective); if (basicPoint[0] < 0 || basicPoint[0] > 1 || basicPoint[1] < 0 || basicPoint[1] > 1) { basicPoint = null; } return basicPoint; } eventToUnprojection(event, container, render, reference, transform) { const pixelPoint = this._viewportCoords .canvasPosition(event, container); return this.canvasToUnprojection(pixelPoint, container, render, reference, transform); } canvasToUnprojection(canvasPoint, container, render, reference, transform) { const canvasX = canvasPoint[0]; const canvasY = canvasPoint[1]; const [viewportX, viewportY] = this._viewportCoords .canvasToViewport(canvasX, canvasY, container); const point3d = new Vector3(viewportX, viewportY, 1) .unproject(render.perspective); let basicPoint = transform .projectBasic(point3d.toArray()); if (basicPoint[0] < 0 || basicPoint[0] > 1 || basicPoint[1] < 0 || basicPoint[1] > 1) { basicPoint = null; } const direction3d = point3d .clone() .sub(render.camera.position) .normalize(); const dist = -2 / direction3d.z; let lngLat = null; if (dist > 0 && dist < 100 && !!basicPoint) { const point = direction3d .clone() .multiplyScalar(dist) .add(render.camera.position); const [lng, lat] = enuToGeodetic(point.x, point.y, point.z, reference.lng, reference.lat, reference.alt); lngLat = { lat, lng }; } const unprojection = { basicPoint: basicPoint, lngLat: lngLat, pixelPoint: [canvasX, canvasY], }; return unprojection; } cameraToLngLat(render, reference) { const position = render.camera.position; const [lng, lat] = enuToGeodetic(position.x, position.y, position.z, reference.lng, reference.lat, reference.alt); return { lat, lng }; } lngLatToCanvas(lngLat, container, render, reference) { const point3d = geodeticToEnu(lngLat.lng, lngLat.lat, 0, reference.lng, reference.lat, reference.alt); const canvas = this._viewportCoords .projectToCanvasSafe(point3d, container, render.perspective); return canvas; } distanceBetweenLngLats(lngLat1, lngLat2) { return this._spatial .distanceFromLngLat(lngLat1.lng, lngLat1.lat, lngLat2.lng, lngLat2.lat); } } class Observer { constructor(viewer, navigator, container) { this._subscriptions = new SubscriptionHolder(); this._emitSubscriptions = new SubscriptionHolder(); this._container = container; this._viewer = viewer; this._navigator = navigator; this._projection = new Projection(); this._started = false; this._navigable$ = new Subject(); const subs = this._subscriptions; // load, navigable, dataloading should always emit, // also when cover is activated. subs.push(this._navigable$ .subscribe((navigable) => { const type = "navigable"; const event = { navigable, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.loadingService.loading$ .subscribe((loading) => { const type = "dataloading"; const event = { loading, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.glRenderer.opaqueRender$ .pipe(first()) .subscribe(() => { const type = "load"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); } get started() { return this._started; } get navigable$() { return this._navigable$; } get projection() { return this._projection; } dispose() { this.stopEmit(); this._subscriptions.unsubscribe(); } project$(lngLat) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentImage$, this._navigator.stateService.reference$).pipe(first(), map(([render, image, reference]) => { if (this._projection .distanceBetweenLngLats(lngLat, image.lngLat) > 1000) { return null; } const canvasPoint = this._projection.lngLatToCanvas(lngLat, this._container.container, render, reference); return !!canvasPoint ? [Math.round(canvasPoint[0]), Math.round(canvasPoint[1])] : null; })); } projectBasic$(basicPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, transform]) => { const canvasPoint = this._projection.basicToCanvas(basicPoint, this._container.container, render, transform); return !!canvasPoint ? [Math.round(canvasPoint[0]), Math.round(canvasPoint[1])] : null; })); } startEmit() { if (this._started) { return; } this._started = true; const subs = this._emitSubscriptions; subs.push(this._navigator.stateService.currentImageExternal$ .subscribe((image) => { const type = "image"; const event = { image: isNullImageId(image.id) ? null : image, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.currentImageExternal$.pipe(switchMap((image) => { return image.sequenceEdges$; })) .subscribe((status) => { const type = "sequenceedges"; const event = { status, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.currentImageExternal$.pipe(switchMap((image) => { return image.spatialEdges$; })) .subscribe((status) => { const type = "spatialedges"; const event = { status, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.reference$ .subscribe((reference) => { const type = "reference"; const event = { reference, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.graphService.dataReset$ .subscribe(() => { const type = "reset"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(combineLatest(this._navigator.stateService.inMotion$, this._container.mouseService.active$, this._container.touchService.active$).pipe(map((values) => { return values[0] || values[1] || values[2]; }), distinctUntilChanged()) .subscribe((started) => { const type = started ? "movestart" : "moveend"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.bearing$.pipe(auditTime(100), distinctUntilChanged((b1, b2) => { return Math.abs(b2 - b1) < 1; })) .subscribe((bearing) => { const type = "bearing"; const event = { bearing, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.mouseService.mouseDragEnd$.subscribe((originalEvent) => { const type = "dragend"; const event = { originalEvent, target: this._viewer, type, }; this._viewer.fire(type, event); })); const mouseMove$ = this._container.mouseService.active$.pipe(switchMap((active) => { return active ? empty() : this._container.mouseService.mouseMove$; })); subs.push(merge(this._mapMouseEvent$("click", this._container.mouseService.staticClick$), this._mapMouseEvent$("contextmenu", this._container.mouseService.contextMenu$), this._mapMouseEvent$("dblclick", this._container.mouseService.dblClick$), this._mapMouseEvent$("drag", this._container.mouseService.mouseDrag$), this._mapMouseEvent$("dragstart", this._container.mouseService.mouseDragStart$), this._mapMouseEvent$("mousedown", this._container.mouseService.mouseDown$), this._mapMouseEvent$("mousemove", mouseMove$), this._mapMouseEvent$("mouseout", this._container.mouseService.mouseOut$), this._mapMouseEvent$("mouseover", this._container.mouseService.mouseOver$), this._mapMouseEvent$("mouseup", this._container.mouseService.mouseUp$)) .pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.currentTransform$, this._navigator.stateService.state$), map(([[type, event], render, reference, transform, state]) => { const unprojection = this._projection.eventToUnprojection(event, this._container.container, render, reference, transform); const basicPoint = state === State.Traversing || state === State.GravityTraversing ? unprojection.basicPoint : null; return { basicPoint, lngLat: unprojection.lngLat, originalEvent: event, pixelPoint: unprojection.pixelPoint, target: this._viewer, type: type, }; })) .subscribe((event) => { this._viewer.fire(event.type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged(([x1, y1], [x2, y2]) => { return this._closeTo(x1, x2, 1e-2) && this._closeTo(y1, y2, 1e-2); }, (rc) => { return rc.camera.position.toArray(); })) .subscribe(() => { const type = "position"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.reference$ .subscribe(() => { const type = "position"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged(([phi1, theta1], [phi2, theta2]) => { return this._closeTo(phi1, phi2, 1e-3) && this._closeTo(theta1, theta2, 1e-3); }, (rc) => { return [rc.rotation.phi, rc.rotation.theta]; })) .subscribe(() => { const type = "pov"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged((fov1, fov2) => { return this._closeTo(fov1, fov2, 1e-2); }, (rc) => { return rc.perspective.fov; })) .subscribe(() => { const type = "fov"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); } stopEmit() { if (!this.started) { return; } this._emitSubscriptions.unsubscribe(); this._started = false; } unproject$(canvasPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, reference, transform]) => { const unprojection = this._projection.canvasToUnprojection(canvasPoint, this._container.container, render, reference, transform); return unprojection.lngLat; })); } unprojectBasic$(canvasPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, transform]) => { return this._projection.canvasToBasic(canvasPoint, this._container.container, render, transform); })); } _closeTo(v1, v2, absoluteTolerance) { return Math.abs(v1 - v2) <= absoluteTolerance; } _mapMouseEvent$(type, mouseEvent$) { return mouseEvent$.pipe(map((event) => { return [type, event]; })); } } class CustomRenderer { constructor(_container, _navigator) { this._container = _container; this._navigator = _navigator; this._renderers = {}; } add(renderer, viewer) { const subs = new SubscriptionHolder(); this._renderers[renderer.id] = { subs, renderer }; subs.push(combineLatest([ this._container.glRenderer.webGLRenderer$, this._navigator.stateService.reference$, ]) .pipe(take(1)) .subscribe(([gl, reference]) => { renderer.onAdd(viewer, reference, gl.getContext()); })); const render$ = renderer.renderPass === exports.RenderPass.Opaque ? this._container.glRenderer.opaqueRender$ : this._container.glRenderer.transparentRender$; subs.push(render$ .pipe(withLatestFrom(this._container.renderService.renderCamera$, this._container.glRenderer.webGLRenderer$)) .subscribe(([, renderCamera, glRenderer]) => { const context = glRenderer.getContext(); const viewMatrix = renderCamera.perspective.matrixWorldInverse; const projectionMatrix = renderCamera.perspective.projectionMatrix; renderer.render(context, viewMatrix.toArray(), projectionMatrix.toArray()); })); subs.push(this._navigator.stateService.reference$ .pipe(skip(1)) .subscribe((reference) => { renderer.onReference(viewer, reference); })); } dispose(viewer) { for (const id of Object.keys(this._renderers)) { this.remove(id, viewer); } } has(id) { return id in this._renderers; } remove(id, viewer) { this._renderers[id].subs.unsubscribe(); const renderer = this._renderers[id].renderer; delete this._renderers[id]; this._container.glRenderer.webGLRenderer$ .subscribe((gl) => { renderer.onRemove(viewer, gl.getContext()); }); } } class CustomCameraControls { constructor(_container, _navigator) { this._container = _container; this._navigator = _navigator; this._controls = null; this._subscriptions = new SubscriptionHolder(); } attach(controls, viewer) { if (this._controls) { throw new MapillaryError("Custom camera controls already attached"); } this._controls = controls; const attach$ = new Subject(); const active$ = attach$ .pipe(switchMap(() => { return this._navigator.stateService.state$; }), map((state) => { return state === State.Custom; }), distinctUntilChanged()); const subs = this._subscriptions; subs.push(active$ .pipe(startWith(false), pairwise(), withLatestFrom(this._navigator.stateService.reference$, this._container.renderService.renderCamera$)) .subscribe(([[deactivate, activate], ref, cam]) => { if (activate) { controls.onActivate(viewer, cam.perspective.matrixWorldInverse.toArray(), cam.perspective.projectionMatrix.toArray(), ref); } else if (deactivate) { controls.onDeactivate(viewer); } })); subs.push(active$ .pipe(switchMap(active => { return active ? this._navigator.stateService.currentState$ .pipe(skip(1)) : empty(); })) .subscribe(frame => { controls.onAnimationFrame(viewer, frame.id); })); subs.push(active$ .pipe(switchMap(active => { return active ? this._navigator.stateService.reference$ .pipe(skip(1)) : empty(); })) .subscribe(ref => controls.onReference(viewer, ref))); subs.push(active$ .pipe(switchMap(active => { return active ? this._container.renderService.size$ .pipe(skip(1)) : empty(); })) .subscribe(() => controls.onResize(viewer))); subs.push(combineLatest([ // Include to ensure GL renderer has been initialized this._container.glRenderer.webGLRenderer$, this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.state$, ]) .pipe(first()) .subscribe(() => { const projectionMatrixCallback = (projectionMatrix) => { if (!this._controls || controls !== this._controls) { return; } this._updateProjectionMatrix(projectionMatrix); }; const viewMatrixCallback = (viewMatrix) => { if (!this._controls || controls !== this._controls) { return; } this._updateViewMatrix(viewMatrix); }; controls.onAttach(viewer, viewMatrixCallback, projectionMatrixCallback); attach$.next(); attach$.complete(); })); } detach(viewer) { const controls = this._controls; this._controls = null; this._subscriptions.unsubscribe(); return new Promise(resolve => { this._navigator.stateService.state$ .pipe(take(1)) .subscribe(state => { if (!controls) { resolve(null); return; } if (state === State.Custom) { controls.onDeactivate(viewer); } controls.onDetach(viewer); resolve(controls); }); }); } dispose(viewer) { this.detach(viewer); } has(controls) { return !!this._controls && controls === this._controls; } _updateProjectionMatrix(projectionMatrix) { this._navigator.stateService.state$ .pipe(first()) .subscribe(state => { if (state !== State.Custom) { const message = "Incorrect camera control mode for " + "projection matrix update"; console.warn(message); return; } this._container.renderService.projectionMatrix$ .next(projectionMatrix); }); } _updateViewMatrix(viewMatrix) { this._navigator.stateService.state$ .pipe(first()) .subscribe(state => { if (state !== State.Custom) { const message = "Incorrect camera control mode for " + "view matrix update"; console.warn(message); return; } this._navigator.stateService.setViewMatrix(viewMatrix); }); } } /** * @class Viewer * * @classdesc The Viewer object represents the navigable image viewer. * Create a Viewer by specifying a container, client ID, image ID and * other options. The viewer exposes methods and events for programmatic * interaction. * * In the case of asynchronous methods, MapillaryJS returns promises to * the results. Notifications are always emitted through JavaScript events. */ class Viewer extends EventEmitter { /** * Create a new viewer instance. * * @description The `Viewer` object represents the street imagery * viewer on your web page. It exposes methods and properties that * you can use to programatically change the view, and fires * events as users interact with it. * * It is possible to initialize the viewer with or * without a ID. * * When you want to show a specific image in the viewer from * the start you should initialize it with a ID. * * When you do not know the first image ID at implementation * time, e.g. in a map-viewer application you should initialize * the viewer without a ID and call `moveTo` instead. * * When initializing with an ID the viewer is bound to that ID * until the image for that ID has been successfully loaded. * Also, a cover with the image of the ID will be shown. * If the data for that ID can not be loaded because the ID is * faulty or other errors occur it is not possible to navigate * to another ID because the viewer is not navigable. The viewer * becomes navigable when the data for the ID has been loaded and * the image is shown in the viewer. This way of initializing * the viewer is mostly for embedding in blog posts and similar * where one wants to show a specific image initially. * * If the viewer is initialized without a ID (with null or * undefined) it is not bound to any particular ID and it is * possible to move to any ID with `viewer.moveTo("")`. * If the first move to a ID fails it is possible to move to another * ID. The viewer will show a black background until a move * succeeds. This way of intitializing is suited for a map-viewer * application when the initial ID is not known at implementation * time. * * @param {ViewerOptions} options - Optional configuration object * specifying Viewer"s and the components" initial setup. * * @example * ```js * var viewer = new Viewer({ * accessToken: "", * container: "", * }); * ``` */ constructor(options) { super(); this._navigator = new Navigator(options); this._container = new Container(options, this._navigator.stateService); this._observer = new Observer(this, this._navigator, this._container); this._componentController = new ComponentController(this._container, this._navigator, this._observer, options.imageId, options.component); this._customRenderer = new CustomRenderer(this._container, this._navigator); this._customCameraControls = new CustomCameraControls(this._container, this._navigator); } /** * Returns the data provider used by the viewer to fetch * all contracts, ents, and buffers. * * @description The viewer"s data provider can be set * upon initialization through the {@link ViewerOptions.dataProvider} * property. * * @returns {IDataProvider} The viewer"s data provider. */ get dataProvider() { return this._navigator.api.data; } /** * Return a boolean indicating if the viewer is in a navigable state. * * @description The navigable state indicates if the viewer supports * moving, i.e. calling the {@link moveTo} and {@link moveDir} * methods or changing the authentication state, * i.e. calling {@link setAccessToken}. The viewer will not be in a navigable * state if the cover is activated and the viewer has been supplied a ID. * When the cover is deactivated or the viewer is activated without being * supplied a ID it will be navigable. * * @returns {boolean} Boolean indicating whether the viewer is navigable. */ get isNavigable() { return this._componentController.navigable; } /** * Activate the combined panning functionality. * * @description The combined panning functionality is active by default. */ activateCombinedPanning() { this._navigator.panService.enable(); } /** * Activate a component. * * @param {ComponentName | FallbackComponentName} name - Name of * the component which will become active. * * @example * ```js * viewer.activateComponent("marker"); * ``` */ activateComponent(name) { this._componentController.activate(name); } /** * Activate the cover (deactivates all other components). */ activateCover() { this._componentController.activateCover(); } /** * Add a custom renderer to the viewer"s rendering pipeline. * * @description During a render pass, custom renderers * are called in the order they were added. * * @param renderer - The custom renderer implementation. */ addCustomRenderer(renderer) { this._customRenderer.add(renderer, this); } /** * Attach custom camera controls to control the viewer"s * camera pose and projection. * * @description Custom camera controls allow the API user * to move the viewer"s camera freely and define the camera * projection. These camera properties are used * to render the viewer 3D scene directly into the * viewer"s GL context. * * Only a single custom camera control instance can be * attached to the viewer. A new custom camera control * instance can be attached after detaching a previous * one. * * Set the viewer"s camera controls to * {@link CameraControls.Custom} to activate attached * camera controls. If {@link CameraControls.Custom} * has already been set when a custom camera control * instance is attached, it will be activated immediately. * * Set the viewer"s camera controls to any other * {@link CameraControls} mode to deactivate the * custom camera controls. * * @param controls - The custom camera controls implementation. * * @throws {MapillaryError} When camera controls attached * are already attached to the viewer. */ attachCustomCameraControls(controls) { this._customCameraControls.attach(controls, this); } /** * Deactivate the combined panning functionality. * * @description Deactivating the combined panning functionality * could be needed in scenarios involving sequence only navigation. */ deactivateCombinedPanning() { this._navigator.panService.disable(); } /** * Deactivate a component. * * @param {ComponentName | FallbackComponentName} name - Name * of component which become inactive. * * @example * ```js * viewer.deactivateComponent("pointer"); * ``` */ deactivateComponent(name) { this._componentController.deactivate(name); } /** * Deactivate the cover (activates all components marked as active). */ deactivateCover() { this._componentController.deactivateCover(); } /** * Detach a previously attached custom camera control * instance from the viewer. * * @description If no custom camera control instance * has previously been attached, calling this method * has no effect. * * Already attached custom camera controls need to * be detached before attaching another custom camera * control instance. */ detachCustomCameraControls() { return this._customCameraControls.detach(this); } fire(type, event) { super.fire(type, event); } /** * Get the bearing of the current viewer camera. * * @description The bearing depends on how the camera * is currently rotated and does not correspond * to the compass angle of the current image if the view * has been panned. * * Bearing is measured in degrees clockwise with respect to * north. * * @returns {Promise} Promise to the bearing * of the current viewer camera. * * @example * ```js * viewer.getBearing().then(b => { console.log(b); }); * ``` */ getBearing() { return new Promise((resolve, reject) => { this._container.renderService.bearing$.pipe(first()) .subscribe((bearing) => { resolve(bearing); }, (error) => { reject(error); }); }); } /** * Get the viewer"s camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * @returns {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.getCameraControls().then(c => { console.log(c); }); * ``` */ getCameraControls() { return new Promise((resolve, reject) => { this._navigator.stateService.state$.pipe(first()) .subscribe((state) => { switch (state) { case State.Custom: resolve(exports.CameraControls.Custom); break; case State.Earth: resolve(exports.CameraControls.Earth); break; case State.GravityTraversing: resolve(exports.CameraControls.Gravity); break; default: resolve(exports.CameraControls.Street); break; } }, (error) => { reject(error); }); }); } /** * Returns the viewer"s canvas element. * * @description This is the element onto which the viewer renders * the WebGL content. * * @returns {HTMLCanvasElement} The viewer"s canvas element, or * null or not initialized. */ getCanvas() { return this._container.canvas; } /** * Returns the HTML element containing the viewer"s canvas element. * * @description This is the element to which event bindings for viewer * interactivity (such as panning and zooming) are attached. * * @returns {HTMLDivElement} The container for the viewer"s * canvas element. */ getCanvasContainer() { return this._container.canvasContainer; } /** * Get the basic coordinates of the current image that is * at the center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and have the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @returns {Promise} Promise to the basic coordinates * of the current image at the center for the viewport. * * @example * ```js * viewer.getCenter().then(c => { console.log(c); }); * ``` */ getCenter() { return new Promise((resolve, reject) => { this._navigator.stateService.getCenter() .subscribe((center) => { resolve(center); }, (error) => { reject(error); }); }); } /** * Get a component. * * @param {string} name - Name of component. * @returns {Component} The requested component. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * ``` */ getComponent(name) { return this._componentController.get(name); } /** * Returns the viewer"s containing HTML element. * * @returns {HTMLElement} The viewer"s container. */ getContainer() { return this._container.container; } /** * Get the viewer"s current vertical field of view. * * @description The vertical field of view rendered on the viewer canvas * measured in degrees. * * @returns {Promise} Promise to the current field of view * of the viewer camera. * * @example * ```js * viewer.getFieldOfView().then(fov => { console.log(fov); }); * ``` */ getFieldOfView() { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first()) .subscribe((rc) => { resolve(rc.perspective.fov); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current image. * * @returns {Promise} Promise to the current image. * * @example * ```js * viewer.getImage().then(image => { console.log(image.id); }); * ``` */ getImage() { return new Promise((resolve, reject) => { this._navigator.stateService.currentImage$.pipe(first()) .subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current point of view. * * @returns {Promise} Promise to the current point of view * of the viewer camera. * * @example * ```js * viewer.getPointOfView().then(pov => { console.log(pov); }); * ``` */ getPointOfView() { return new Promise((resolve, reject) => { combineLatest(this._container.renderService.renderCamera$, this._container.renderService.bearing$).pipe(first()) .subscribe(([rc, bearing]) => { resolve({ bearing: bearing, tilt: rc.getTilt(), }); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current position * * @returns {Promise} Promise to the viewers"s current * position. * * @example * ```js * viewer.getPosition().then(pos => { console.log(pos); }); * ``` */ getPosition() { return new Promise((resolve, reject) => { combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.reference$).pipe(first()) .subscribe(([render, reference]) => { resolve(this._observer.projection.cameraToLngLat(render, reference)); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current reference position. * * @description The reference position specifies the origin in * the viewer"s topocentric coordinate system. * * @returns {Promise} Promise to the reference position. * * @example * ```js * viewer.getReference().then(reference => { console.log(reference); }); * ``` */ getReference() { return new Promise((resolve, reject) => { this._navigator.stateService.reference$.pipe(first()) .subscribe((reference) => { resolve(reference); }, (error) => { reject(error); }); }); } /** * Get the image"s current zoom level. * * @returns {Promise} Promise to the viewers"s current * zoom level. * * @example * ```js * viewer.getZoom().then(z => { console.log(z); }); * ``` */ getZoom() { return new Promise((resolve, reject) => { this._navigator.stateService.getZoom() .subscribe((zoom) => { resolve(zoom); }, (error) => { reject(error); }); }); } /** * Check if a controls instance is the camera controls that are * currently attached to the viewer. * * @param {ICustomCameraControls} controls - Camera controls instance. * @returns {boolean} Value indicating whether the controls instance * is currently attached. */ hasCustomCameraControls(controls) { return this._customCameraControls.has(controls); } /** * Check if a custom renderer has been added to the viewer"s * rendering pipeline. * * @param {string} id - Unique ID of the custom renderer. * @returns {boolean} Value indicating whether the customer * renderer has been added. */ hasCustomRenderer(rendererId) { return this._customRenderer.has(rendererId); } /** * Navigate in a given direction. * * @param {NavigationDirection} direction - Direction in which which to move. * @returns {Promise} Promise to the image that was navigated to. * @throws If the current image does not have the edge direction * or the edges has not yet been cached. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent move request * is made before the move dir call has completed. * * @example * ```js * viewer.moveDir(NavigationDirection.Next).then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ moveDir(direction) { const moveDir$ = this.isNavigable ? this._navigator.moveDir$(direction) : throwError(new Error("Calling moveDir is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { moveDir$.subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } /** * Navigate to a given image ID. * * @param {string} imageId - Id of the image to move to. * @returns {Promise} Promise to the image that was navigated to. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent * move request is made before the move to ID call has completed. * * @example * ```js * viewer.moveTo("").then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ moveTo(imageId) { const moveTo$ = this.isNavigable ? this._navigator.moveTo$(imageId) : throwError(new Error("Calling moveTo is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { moveTo$.subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Project geodetic coordinates to canvas pixel coordinates. * * @description The geodetic coordinates may not always correspond to pixel * coordinates, e.g. if the geodetic coordinates have a position behind the * viewer camera. In the case of no correspondence the returned value will * be `null`. * * If the distance from the viewer camera position to the provided * longitude-latitude is more than 1000 meters `null` will be returned. * * The projection is performed from the ground plane, i.e. * the altitude with respect to the ground plane for the geodetic * point is zero. * * Note that whenever the camera moves, the result of the method will be * different. * * @param {LngLat} lngLat - Geographical coordinates to project. * @returns {Promise>} Promise to the pixel coordinates corresponding * to the lngLat. * * @example * ```js * viewer.project({ lat: 0, lng: 0 }) * .then(pixelPoint => { * if (!pixelPoint) { * console.log("no correspondence"); * } * * console.log(pixelPoint); * }); * ``` */ project(lngLat) { return new Promise((resolve, reject) => { this._observer.project$(lngLat) .subscribe((pixelPoint) => { resolve(pixelPoint); }, (error) => { reject(error); }); }); } /** * Project basic image coordinates for the current image to canvas pixel * coordinates. * * @description The basic image coordinates may not always correspond to a * pixel point that lies in the visible area of the viewer container. In the * case of no correspondence the returned value can be `null`. * * * @param {Array} basicPoint - Basic images coordinates to project. * @returns {Promise>} Promise to the pixel coordinates corresponding * to the basic image point. * * @example * ```js * viewer.projectFromBasic([0.3, 0.7]) * .then(pixelPoint => { console.log(pixelPoint); }); * ``` */ projectFromBasic(basicPoint) { return new Promise((resolve, reject) => { this._observer.projectBasic$(basicPoint) .subscribe((pixelPoint) => { resolve(pixelPoint); }, (error) => { reject(error); }); }); } /** * Clean up and release all internal resources associated with * this viewer. * * @description This includes DOM elements, event bindings, and * WebGL resources. * * Use this method when you are done using the viewer and wish to * ensure that it no longer consumes browser resources. Afterwards, * you must not call any other methods on the viewer. * * @fires remove * * @example * ```js * viewer.remove(); * ``` */ remove() { this._customRenderer.dispose(this); this._customCameraControls.dispose(this); this._observer.dispose(); this._componentController.remove(); this._navigator.dispose(); this._container.remove(); const type = "remove"; const event = { target: this, type, }; this.fire(type, event); } /** * Remove a custom renderer from the viewer"s rendering pipeline. * * @param id - Unique ID of the custom renderer. */ removeCustomRenderer(rendererId) { this._customRenderer.remove(rendererId, this); } /** * Reset the viewer"s cache. * * @description All images in the viewer"s cache at the moment of the * reset will be disposed. * * @returns {Promise} Promise that resolves when viewer"s cache * has been reset. * * @throws When viewer is not navigable. * * @example * ```js * viewer.reset() * .then(() => { console.log("viewer reset"); }); * ``` */ reset() { const reset$ = this.isNavigable ? this._navigator.reset$() : throwError(new Error("Calling reset is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { reset$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Detect the viewer"s new width and height and resize it * manually. * * @description The components will also detect the viewer"s * new size and resize their rendered elements if needed. * * When the {@link ViewerOptions.trackResize} option is * set to true, the viewer will automatically resize * when the browser window is resized. If any other * custom behavior is preferred, the option should be set * to false and the {@link Viewer.resize} method should * be called on demand. * * @example * ```js * viewer.resize(); * ``` */ resize() { this._container.renderService.resize$.next(); } /** * Register a class constructor for a camera type. * * @description The Viewer will invoke the camera * constructor each time that camera type should be * instantiated. * * @param {string} type - Camera type. * @param ctor - Contructor for Camera class implementing the * {@link ICamera} interface. * * @example * ```js * import {MyCamera} from "./MyCameraClasses"; * viewer.registerCamera("my-camera-type", MyCamera); * ``` */ registerCamera(type, ctor) { this._navigator.projectionService.registerCamera(type, ctor); } /** * Set the viewer"s camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * @param {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.setCameraControls(CameraControls.Street); * ``` */ setCameraControls(controls) { const state = cameraControlsToState(controls); if (state === State.Custom) { this._navigator.stateService.custom(); } else if (state === State.Earth) { this._navigator.stateService.earth(); } else if (state === State.GravityTraversing) { this._navigator.stateService.gravityTraverse(); } else if (state === State.Traversing) { this._navigator.stateService.traverse(); } else { console.warn(`Unsupported camera control transition (${controls})`); } } /** * Set the basic coordinates of the current image to be in the * center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and has the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @param {number[]} The basic coordinates of the current * image to be at the center for the viewport. * * @example * ```js * viewer.setCenter([0.5, 0.5]); * ``` */ setCenter(center) { this._navigator.stateService.setCenter(center); } /** * Set a new data provider instance. * * @description Resets the viewer"s cache (see {@link Viewer.reset}). * * @returns {Promise} Promise that resolves when viewer"s data * provider has been set. * * @throws When viewer is not navigable. * * @example * ```js * const myDataProvider = new MyDataProvider(); * viewer.setDataProvider(myDataProvider) * .then(() => { console.log("data provider set"); }); * ``` */ setDataProvider(provider) { const reset$ = this.isNavigable ? this._navigator.reset$() .pipe(tap(() => { this._navigator.api.setDataProvider(provider); const type = "dataprovider"; const event = { target: this, type, }; this.fire(type, event); })) : throwError(new Error("Calling setDataProvider is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { reset$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Set the viewer"s current vertical field of view. * * @description Sets the vertical field of view rendered * on the viewer canvas measured in degrees. The value * will be clamped to be able to set a valid zoom level * based on the projection model of the current image and * the viewer"s current render mode. * * @param {number} fov - Vertical field of view in degrees. * * @example * ```js * viewer.setFieldOfView(45); * ``` */ setFieldOfView(fov) { this._container.renderService.renderCamera$.pipe(first()) .subscribe((rc) => { const zoom = rc.fovToZoom(fov); this._navigator.stateService.setZoom(zoom); }); } /** * Set the filter selecting images to use when calculating * the spatial edges. * * @description The following filter types are supported: * * Comparison * * `["==", key, value]` equality: `image[key] = value` * * `["!=", key, value]` inequality: `image[key] ≠ value` * * `["<", key, value]` less than: `image[key] < value` * * `["<=", key, value]` less than or equal: `image[key] ≤ value` * * `[">", key, value]` greater than: `image[key] > value` * * `[">=", key, value]` greater than or equal: `image[key] ≥ value` * * Set membership * * `["in", key, v0, ..., vn]` set inclusion: `image[key] ∈ {v0, ..., vn}` * * `["!in", key, v0, ..., vn]` set exclusion: `image[key] ∉ {v0, ..., vn}` * * Combining * * `["all", f0, ..., fn]` logical `AND`: `f0 ∧ ... ∧ fn` * * A key must be a string that identifies a property name of a * simple {@link Image} property, i.e. a key of the {@link FilterKey} * type. A value must be a string, number, or * boolean. Strictly-typed comparisons are used. The values * `f0, ..., fn` of the combining filter must be filter expressions. * * Clear the filter by setting it to null or empty array. * * Commonly used filter properties (see the {@link Image} class * documentation for a full list of properties that can be used * in a filter) are shown the the example code. * * @param {FilterExpression} [filter] - The filter expression. * Applied filter is cleared if omitted. * @returns {Promise} Promise that resolves after filter is applied. * * @example * ```js * // Examples * viewer.setFilter(["==", "cameraType", "spherical"]); * viewer.setFilter([">=", "capturedAt", ]); * viewer.setFilter(["in", "sequenceId", "", ""]); * // Clear filter * viewer.setFilter([]); * ``` */ setFilter(filter) { return new Promise((resolve, reject) => { this._navigator.setFilter$(filter) .subscribe(() => { }, (error) => { reject(error); }, () => { resolve(undefined); }); }); } /** * Set the viewer"s render mode. * * @param {RenderMode} renderMode - Render mode. * * @example * ```js * viewer.setRenderMode(RenderMode.Letterbox); * ``` */ setRenderMode(renderMode) { this._container.renderService.renderMode$.next(renderMode); } /** * Set the viewer"s texture shader. * * @description The shader will be used for all registered projection * models. * * @param {GLShader} shader - Texture shader. * * @example * ```js * let myShader = { * fragment: "", * vertex: "", * }; * viewer.setShader(myShader); * ``` */ setShader(shader) { this._navigator.projectionService.setShader(shader); } /** * Set the viewer"s transition mode. * * @param {TransitionMode} transitionMode - Transition mode. * * @example * ```js * viewer.setTransitionMode(TransitionMode.Instantaneous); * ``` */ setTransitionMode(transitionMode) { this._navigator.stateService.setTransitionMode(transitionMode); } /** * Set an access token for authenticated API requests of protected * resources. * * The token may be a user access token or a client access token. * * @description When the supplied user token is null or undefined, * any previously set user bearer token will be cleared and the * viewer will make unauthenticated requests. * * Calling setAccessToken aborts all outstanding move requests. * The promises of those move requests will be rejected with a * {@link CancelMapillaryError} the rejections need to be caught. * * Calling setAccessToken also resets the complete viewer cache * so it should not be called repeatedly. * * @param {string} [accessToken] accessToken - Optional user * access token or client access token. * @returns {Promise} Promise that resolves after token * is set. * * @throws When viewer is not navigable. * * @example * ```js * viewer.setAccessToken("") * .then(() => { console.log("user token set"); }); * ``` */ setAccessToken(accessToken) { const setAccessToken$ = this.isNavigable ? this._navigator.setAccessToken$(accessToken) : throwError(new Error("Calling setAccessToken is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { setAccessToken$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Set the image"s current zoom level. * * @description Possible zoom level values are on the [0, 3] interval. * Zero means zooming out to fit the image to the view whereas three * shows the highest level of detail. * * @param {number} The image"s current zoom level. * * @example * ```js * viewer.setZoom(2); * ``` */ setZoom(zoom) { this._navigator.stateService.setZoom(zoom); } /** * Trigger the rendering of a single frame. * * @description Use this method with custom renderers to * force the viewer to rerender when the custom content * changes. Calling this multiple times before the next * frame is rendered will still result in only a single * frame being rendered. */ triggerRerender() { this._container.glRenderer.triggerRerender(); } /** * Unproject canvas pixel coordinates to geodetic * coordinates. * * @description The pixel point may not always correspond to geodetic * coordinates. In the case of no correspondence the returned value will * be `null`. * * The unprojection to a lngLat will be performed towards the ground plane, i.e. * the altitude with respect to the ground plane for the returned lngLat is zero. * * @param {Array} pixelPoint - Pixel coordinates to unproject. * @returns {Promise} Promise to the lngLat corresponding to the pixel point. * * @example * ```js * viewer.unproject([100, 100]) * .then(lngLat => { console.log(lngLat); }); * ``` */ unproject(pixelPoint) { return new Promise((resolve, reject) => { this._observer.unproject$(pixelPoint) .subscribe((lngLat) => { resolve(lngLat); }, (error) => { reject(error); }); }); } /** * Unproject canvas pixel coordinates to basic image coordinates for the * current image. * * @description The pixel point may not always correspond to basic image * coordinates. In the case of no correspondence the returned value will * be `null`. * * @param {Array} pixelPoint - Pixel coordinates to unproject. * @returns {Promise} Promise to the basic coordinates corresponding * to the pixel point. * * @example * ```js * viewer.unprojectToBasic([100, 100]) * .then(basicPoint => { console.log(basicPoint); }); * ``` */ unprojectToBasic(pixelPoint) { return new Promise((resolve, reject) => { this._observer.unprojectBasic$(pixelPoint) .subscribe((basicPoint) => { resolve(basicPoint); }, (error) => { reject(error); }); }); } } /** * Internal bootstrap * * This is a workaround to make the CommonJS unit testing * work with Jest. Once Jest/Node supports ES6 modules * fully this should be removed. GeoRBush is registered * here only to avoid loading it during * unit tests. */ Graph.register(GeoRBush); MarkerSet.register(GeoRBush); ComponentService.registerCover(CoverComponent); ComponentService.register(AttributionComponent); ComponentService.register(BearingComponent); ComponentService.register(CacheComponent); ComponentService.register(DirectionComponent); ComponentService.register(ImageComponent); ComponentService.register(KeyboardComponent); ComponentService.register(MarkerComponent); ComponentService.register(PointerComponent); ComponentService.register(PopupComponent); ComponentService.register(SequenceComponent); ComponentService.register(SpatialComponent); ComponentService.register(TagComponent); ComponentService.register(ZoomComponent); ComponentService.register(ImageFallbackComponent); ComponentService.register(NavigationFallbackComponent); exports.ArgumentMapillaryError = ArgumentMapillaryError; exports.BearingComponent = BearingComponent; exports.CacheComponent = CacheComponent; exports.CancelMapillaryError = CancelMapillaryError; exports.CircleMarker = CircleMarker; exports.Component = Component; exports.DataProviderBase = DataProviderBase; exports.DirectionComponent = DirectionComponent; exports.DragPanHandler = DragPanHandler; exports.EventEmitter = EventEmitter; exports.ExtremePointTag = ExtremePointTag; exports.FISHEYE_CAMERA_TYPE = FISHEYE_CAMERA_TYPE; exports.FisheyeCamera = FisheyeCamera; exports.Geometry = Geometry; exports.GeometryProviderBase = GeometryProviderBase; exports.GeometryTagError = GeometryTagError; exports.GraphDataProvider = GraphDataProvider; exports.GraphMapillaryError = GraphMapillaryError; exports.Image = Image$1; exports.KeyPlayHandler = KeyPlayHandler; exports.KeySequenceNavigationHandler = KeySequenceNavigationHandler; exports.KeySpatialNavigationHandler = KeySpatialNavigationHandler; exports.KeyZoomHandler = KeyZoomHandler; exports.KeyboardComponent = KeyboardComponent; exports.MapillaryError = MapillaryError; exports.Marker = Marker; exports.MarkerComponent = MarkerComponent; exports.OutlineTag = OutlineTag; exports.PERSPECTIVE_CAMERA_TYPE = PERSPECTIVE_CAMERA_TYPE; exports.PerspectiveCamera = PerspectiveCamera; exports.PointGeometry = PointGeometry; exports.PointerComponent = PointerComponent; exports.PointsGeometry = PointsGeometry; exports.PolygonGeometry = PolygonGeometry; exports.Popup = Popup; exports.PopupComponent = PopupComponent; exports.RectGeometry = RectGeometry; exports.S2GeometryProvider = S2GeometryProvider; exports.SPHERICAL_CAMERA_TYPE = SPHERICAL_CAMERA_TYPE; exports.ScrollZoomHandler = ScrollZoomHandler; exports.SequenceComponent = SequenceComponent; exports.Shader = Shader; exports.ShaderChunk = ShaderChunk; exports.SimpleMarker = SimpleMarker; exports.SpatialComponent = SpatialComponent; exports.SphericalCamera = SphericalCamera; exports.SpotTag = SpotTag; exports.Tag = Tag; exports.TagComponent = TagComponent; exports.TouchZoomHandler = TouchZoomHandler; exports.VertexGeometry = VertexGeometry; exports.Viewer = Viewer; exports.ZoomComponent = ZoomComponent; exports.decompress = decompress; exports.ecefToEnu = ecefToEnu; exports.ecefToGeodetic = ecefToGeodetic; exports.enuToEcef = enuToEcef; exports.enuToGeodetic = enuToGeodetic; exports.fetchArrayBuffer = fetchArrayBuffer; exports.geodeticToEcef = geodeticToEcef; exports.geodeticToEnu = geodeticToEnu; exports.isFallbackSupported = isFallbackSupported; exports.isSupported = isSupported; exports.readMeshPbf = readMeshPbf; exports.resolveShader = resolveShader; })); /* */}),null);
-----
PolarisBloksPrimitives",["PolarisBloksAction.react","PolarisBloksAddChild","PolarisBloksAnimatedCreate","PolarisBloksAnimatedCreateColor","PolarisBloksAnimatedCreateCubicBezier","PolarisBloksAnimatedCreateDimension","PolarisBloksAnimatedGetCurrentColorValue","PolarisBloksAnimatedGetCurrentDimensionValue","PolarisBloksAnimatedGetCurrentValue","PolarisBloksAnimatedParallel","PolarisBloksAnimatedStart","PolarisBloksApply","PolarisBloksArrayAppend","PolarisBloksArrayClone","PolarisBloksArrayConcat","PolarisBloksArrayGet","PolarisBloksArrayLength","PolarisBloksArrayMake","PolarisBloksArrayPut","PolarisBloksArrayPutAndGet","PolarisBloksArrayRemove","PolarisBloksArrayRemoveAndGet","PolarisBloksArrayUpdate","PolarisBloksAsyncAction","PolarisBloksAsyncActionWithDataManifest","PolarisBloksAsyncLoadV2","PolarisBloksBindWithArrayV2","PolarisBloksBoolAnd","PolarisBloksBoolNot","PolarisBloksBoolOr","PolarisBloksChildAtIndex","PolarisBloksClipboardSetString","PolarisBloksCollection.react","PolarisBloksConstNumber","PolarisBloksCurrentTimeMillis","PolarisBloksDangerouslyGetTreeFromParseResult","PolarisBloksDatetimeTextProvider.react","PolarisBloksDefault","PolarisBloksDelay","PolarisBloksDismissBottomSheet","PolarisBloksDismissKeyboard","PolarisBloksDummy","PolarisBloksDummyComponent","PolarisBloksFindComponentContext","PolarisBloksFlexbox.react","PolarisBloksGetAttr","PolarisBloksGetState","PolarisBloksGetVariable2","PolarisBloksImage.react","PolarisBloksIndexOfChild","PolarisBloksInflateSync","PolarisBloksInsertChildrenAfter","PolarisBloksInternalMerge.react","PolarisBloksInternalShadow.react","PolarisBloksJsonEncode","PolarisBloksLogEvent","PolarisBloksMakeFlat","PolarisBloksMapClone","PolarisBloksMapGet","PolarisBloksMapKeys","PolarisBloksMapMake","PolarisBloksMapUpdate","PolarisBloksMatch","PolarisBloksMatchesRegex","PolarisBloksNumberAnd","PolarisBloksNumberEq","PolarisBloksNumberGt","PolarisBloksNumberLt","PolarisBloksNumberMod","PolarisBloksNumberOr","PolarisBloksNumberRand","PolarisBloksOnMount.react","PolarisBloksOpenBottomSheetV2","PolarisBloksOpenScreen.react","PolarisBloksOpenSendMessage","PolarisBloksOpenUrl","PolarisBloksOpenUrlV2","PolarisBloksParseEmbedded","PolarisBloksPattern","PolarisBloksReduce","PolarisBloksReflow","PolarisBloksRemoveChild","PolarisBloksRemoveChildrenBetween","PolarisBloksRenderLifecycleExtension","PolarisBloksReplaceChild","PolarisBloksReplaceChildren","PolarisBloksReplaceEmbeddedChild","PolarisBloksRichText.react","PolarisBloksRotate3D","PolarisBloksScale3D","PolarisBloksScreenWrapper.react","PolarisBloksSessionStoreGet","PolarisBloksSetAttr","PolarisBloksShareText","PolarisBloksStringConcat","PolarisBloksStringLength","PolarisBloksStringValueOfNumber","PolarisBloksT3DFromArray","PolarisBloksTakeLast","PolarisBloksText.react","PolarisBloksTextInput.react","PolarisBloksTextInputGetText","PolarisBloksTextSpan.react","PolarisBloksToast","PolarisBloksTranslate3D","PolarisBloksVisibilityContextGetTimeSinceLastImpressionInMS","PolarisBloksVisibilityContextHasSeenBefore","PolarisBloksWhile","PolarisBloksWriteGlobalConsistencyStore","PolarisBloksWriteLocalState","PolarisBloksf32Convert","PolarisBloksf32NumberAdd","PolarisBloksf32NumberDiv","PolarisBloksf32NumberMul","PolarisBloksf32NumberSub","PolarisBloksi32Convert","PolarisBloksi32NumberAdd","PolarisBloksi32NumberDiv","PolarisBloksi32NumberMul","PolarisBloksi32NumberSub","PolarisBloksi64ConstNumber","PolarisBloksi64NumberAdd","PolarisBloksi64NumberEq","PolarisBloksi64NumberGt","PolarisBloksi64NumberLt","PolarisBloksi64NumberSub"],(function(a,b,c,d,e,f,g){"use strict";a={"bk.components.Collection":c("PolarisBloksCollection.react"),"bk.components.DatetimeTextProvider":c("PolarisBloksDatetimeTextProvider.react"),"bk.components.Flexbox":c("PolarisBloksFlexbox.react"),"bk.components.Image":c("PolarisBloksImage.react"),"bk.components.internal.Action":c("PolarisBloksAction.react"),"bk.components.internal.Merge":c("PolarisBloksInternalMerge.react"),"bk.components.internal.Shadow":c("PolarisBloksInternalShadow.react"),"bk.components.OnMount":c("PolarisBloksOnMount.react"),"bk.components.RenderLifecycleExtension":c("PolarisBloksRenderLifecycleExtension"),"bk.components.RichText":c("PolarisBloksRichText.react"),"bk.components.screen.Wrapper":c("PolarisBloksScreenWrapper.react"),"bk.components.Text":c("PolarisBloksText.react"),"bk.components.TextInput":c("PolarisBloksTextInput.react"),"bk.components.TextSpan":c("PolarisBloksTextSpan.react"),"bk.components.Video":c("PolarisBloksDummyComponent"),"bk.components.VideoVersion":c("PolarisBloksDummyComponent"),"bk.type.VideoIdentifier":c("PolarisBloksDummyComponent")};d={"bk.action.animated.Create":c("PolarisBloksAnimatedCreate"),"bk.action.animated.CreateColor":c("PolarisBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("PolarisBloksAnimatedCreateDimension"),"bk.action.animated.easing.CreateCubicBezier":c("PolarisBloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("PolarisBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("PolarisBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentValue":c("PolarisBloksAnimatedGetCurrentValue"),"bk.action.animated.Parallel":c("PolarisBloksAnimatedParallel"),"bk.action.animated.Start":c("PolarisBloksAnimatedStart"),"bk.action.array.Append":c("PolarisBloksArrayAppend"),"bk.action.array.Clone":c("PolarisBloksArrayClone"),"bk.action.array.Concat":c("PolarisBloksArrayConcat"),"bk.action.array.Get":c("PolarisBloksArrayGet"),"bk.action.array.Length":c("PolarisBloksArrayLength"),"bk.action.array.Make":c("PolarisBloksArrayMake"),"bk.action.array.Put":c("PolarisBloksArrayPut"),"bk.action.array.PutAndGet":c("PolarisBloksArrayPutAndGet"),"bk.action.array.Remove":c("PolarisBloksArrayRemove"),"bk.action.array.RemoveAndGet":c("PolarisBloksArrayRemoveAndGet"),"bk.action.array.Update":c("PolarisBloksArrayUpdate"),"bk.action.bloks.AddChild":c("PolarisBloksAddChild"),"bk.action.bloks.AsyncAction":c("PolarisBloksAsyncAction"),"bk.action.bloks.AsyncActionWithDataManifest":c("PolarisBloksAsyncActionWithDataManifest"),"bk.action.bloks.AsyncLoadV2":c("PolarisBloksAsyncLoadV2"),"bk.action.bloks.ChildAtIndex":c("PolarisBloksChildAtIndex"),"bk.action.bloks.DangerouslyGetTreeFromParseResult":c("PolarisBloksDangerouslyGetTreeFromParseResult"),"bk.action.bloks.DismissBottomSheet":c("PolarisBloksDismissBottomSheet"),"bk.action.bloks.DismissKeyboard":c("PolarisBloksDismissKeyboard"),"bk.action.bloks.Find":c("PolarisBloksFindComponentContext"),"bk.action.bloks.FindContainer":c("PolarisBloksFindComponentContext"),"bk.action.bloks.GetState":c("PolarisBloksGetState"),"bk.action.bloks.GetVariable2":c("PolarisBloksGetVariable2"),"bk.action.bloks.IndexOfChild":c("PolarisBloksIndexOfChild"),"bk.action.bloks.InflateSync":c("PolarisBloksInflateSync"),"bk.action.bloks.InsertChildrenAfter":c("PolarisBloksInsertChildrenAfter"),"bk.action.bloks.OpenBottomSheetV2":c("PolarisBloksOpenBottomSheetV2"),"bk.action.bloks.OpenScreen":c("PolarisBloksOpenScreen.react"),"bk.action.bloks.ParseEmbedded":c("PolarisBloksParseEmbedded"),"bk.action.bloks.Reduce":c("PolarisBloksReduce"),"bk.action.bloks.Reflow":c("PolarisBloksReflow"),"bk.action.bloks.RemoveChild":c("PolarisBloksRemoveChild"),"bk.action.bloks.RemoveChildrenBetween":c("PolarisBloksRemoveChildrenBetween"),"bk.action.bloks.ReplaceChild":c("PolarisBloksReplaceChild"),"bk.action.bloks.ReplaceChildren":c("PolarisBloksReplaceChildren"),"bk.action.bloks.ReplaceEmbeddedChild":c("PolarisBloksReplaceEmbeddedChild"),"bk.action.bloks.WriteGlobalConsistencyStore":c("PolarisBloksWriteGlobalConsistencyStore"),"bk.action.bloks.WriteLocalState":c("PolarisBloksWriteLocalState"),"bk.action.bool.And":c("PolarisBloksBoolAnd"),"bk.action.bool.Not":c("PolarisBloksBoolNot"),"bk.action.bool.Or":c("PolarisBloksBoolOr"),"bk.action.component.GetAttr":c("PolarisBloksGetAttr"),"bk.action.component.SetAttr":c("PolarisBloksSetAttr"),"bk.action.core.Apply":c("PolarisBloksApply"),"bk.action.core.Default":c("PolarisBloksDefault"),"bk.action.core.Delay":c("PolarisBloksDelay"),"bk.action.core.FuncConst":b=c("PolarisBloksDummy"),"bk.action.core.GetArg":b,"bk.action.core.If":b,"bk.action.core.Match":c("PolarisBloksMatch"),"bk.action.core.Pattern":c("PolarisBloksPattern"),"bk.action.core.TakeLast":c("PolarisBloksTakeLast"),"bk.action.core.While":c("PolarisBloksWhile"),"bk.action.f32.Add":c("PolarisBloksf32NumberAdd"),"bk.action.f32.Const":c("PolarisBloksConstNumber"),"bk.action.f32.Convert":c("PolarisBloksf32Convert"),"bk.action.f32.Div":c("PolarisBloksf32NumberDiv"),"bk.action.f32.Eq":c("PolarisBloksNumberEq"),"bk.action.f32.Gt":c("PolarisBloksNumberGt"),"bk.action.f32.Lt":c("PolarisBloksNumberLt"),"bk.action.f32.Mul":c("PolarisBloksf32NumberMul"),"bk.action.f32.Sub":c("PolarisBloksf32NumberSub"),"bk.action.flipper.SendData":b,"bk.action.function.BindWithArrayV2":c("PolarisBloksBindWithArrayV2"),"bk.action.i32.Add":c("PolarisBloksi32NumberAdd"),"bk.action.i32.And":c("PolarisBloksNumberAnd"),"bk.action.i32.Const":c("PolarisBloksConstNumber"),"bk.action.i32.Convert":c("PolarisBloksi32Convert"),"bk.action.i32.Div":c("PolarisBloksi32NumberDiv"),"bk.action.i32.Eq":c("PolarisBloksNumberEq"),"bk.action.i32.Gt":c("PolarisBloksNumberGt"),"bk.action.i32.Lt":c("PolarisBloksNumberLt"),"bk.action.i32.Mod":c("PolarisBloksNumberMod"),"bk.action.i32.Mul":c("PolarisBloksi32NumberMul"),"bk.action.i32.Or":c("PolarisBloksNumberOr"),"bk.action.i32.Rand":c("PolarisBloksNumberRand"),"bk.action.i32.Sub":c("PolarisBloksi32NumberSub"),"bk.action.i64.Add":c("PolarisBloksi64NumberAdd"),"bk.action.i64.Const":c("PolarisBloksi64ConstNumber"),"bk.action.i64.Eq":c("PolarisBloksi64NumberEq"),"bk.action.i64.Gt":c("PolarisBloksi64NumberGt"),"bk.action.i64.Lt":c("PolarisBloksi64NumberLt"),"bk.action.i64.Sub":c("PolarisBloksi64NumberSub"),"bk.action.io.clipboard.SetString":c("PolarisBloksClipboardSetString"),"bk.action.io.CurrentTimeMillis":c("PolarisBloksCurrentTimeMillis"),"bk.action.io.Toast":c("PolarisBloksToast"),"bk.action.logging.LogEvent":c("PolarisBloksLogEvent"),"bk.action.map.Append":b,"bk.action.map.Clone":c("PolarisBloksMapClone"),"bk.action.map.Get":c("PolarisBloksMapGet"),"bk.action.map.Keys":c("PolarisBloksMapKeys"),"bk.action.map.Make":c("PolarisBloksMapMake"),"bk.action.map.MakeFlat":c("PolarisBloksMakeFlat"),"bk.action.map.Update":c("PolarisBloksMapUpdate"),"bk.action.navigation.OpenSendMessage":c("PolarisBloksOpenSendMessage"),"bk.action.navigation.OpenUrl":c("PolarisBloksOpenUrl"),"bk.action.navigation.OpenUrlV2":c("PolarisBloksOpenUrlV2"),"bk.action.navigation.SetNavBar":b,"bk.action.session_store.Get":c("PolarisBloksSessionStoreGet"),"bk.action.share.Text":c("PolarisBloksShareText"),"bk.action.string.Concat":c("PolarisBloksStringConcat"),"bk.action.string.JsonEncode":c("PolarisBloksJsonEncode"),"bk.action.string.JsonEncodeV2":c("PolarisBloksJsonEncode"),"bk.action.string.Length":c("PolarisBloksStringLength"),"bk.action.string.MatchesRegex":c("PolarisBloksMatchesRegex"),"bk.action.string.ValueOfNumber":c("PolarisBloksStringValueOfNumber"),"bk.action.t3d.FromArray":c("PolarisBloksT3DFromArray"),"bk.action.t3d.Rotate":c("PolarisBloksRotate3D"),"bk.action.t3d.Scale":c("PolarisBloksScale3D"),"bk.action.t3d.Translate":c("PolarisBloksTranslate3D"),"bk.action.textinput.GetText":c("PolarisBloksTextInputGetText"),"bk.action.visibility_context.GetTimeSinceLastImpressionInMS":c("PolarisBloksVisibilityContextGetTimeSinceLastImpressionInMS"),"bk.action.visibility_context.HasSeenBefore":c("PolarisBloksVisibilityContextHasSeenBefore"),"bk.versioning.bloks.GlobalStateWithInitialLispy":b};g.COMPONENTS=a;g.ACTIONS=d}),98);
-----
PolarisBloksAnimationFunctions",[],(function(a,b,c,d,e,f){"use strict";function g(a,b,c,d){var e=1e-6,f=3*a-3*c+1,g=3*c-6*a,h=3*a,i=3*b-3*d+1,j=3*d-6*b,k=3*b;function l(a){return(3*f*a+2*g)*a+h}function m(a){return((f*a+g)*a+h)*a}function n(a){return((i*a+j)*a+k)*a}function o(a){var b=a,c,d;for(var f=0;f<8;f++){d=m(b)-a;if(Math.abs(d)f){d=m(b)-a;if(Math.abs(d)0?c=b:f=b;b=(c+f)/2}return b}function p(a){return n(o(a))}return p}function a(){return g(0,0,1,1)}f.cubicBezier=g;f.linearInterpolator=a}),66);
-----
PolarisBloksAnimatedCreateCubicBezier",["PolarisBloksAnimationFunctions"],(function(a,b,c,d,e,f,g){"use strict";function a(a,b,c,e,f){return d("PolarisBloksAnimationFunctions").cubicBezier(b,c,e,f)}g["default"]=a}),98);
-----
three-0.116.1",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g};function i(){(function(b,c){typeof g==="object"&&typeof h!=="undefined"?c(g):(b=b||self,c(b.THREE={}))})(this,function(a){Number.EPSILON===void 0&&(Number.EPSILON=Math.pow(2,-52));Number.isInteger===void 0&&(Number.isInteger=function(a){return typeof a==="number"&&isFinite(a)&&Math.floor(a)===a});Math.sign===void 0&&(Math.sign=function(a){return a<0?-1:a>0?1:+a});"name"in Function.prototype===!1&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^s*functions*([^(s]*)/)[1]}});Object.assign===void 0&&(Object.assign=function(a){if(a===void 0||a===null)throw new TypeError("Cannot convert undefined or null to object");var b=Object(a);for(var c=1;c>8&255]+ad[a>>16&255]+ad[a>>24&255]+"-"+ad[b&255]+ad[b>>8&255]+"-"+ad[b>>16&15|64]+ad[b>>24&255]+"-"+ad[c&63|128]+ad[c>>8&255]+"-"+ad[c>>16&255]+ad[c>>24&255]+ad[d&255]+ad[d>>8&255]+ad[d>>16&255]+ad[d>>24&255];return a.toUpperCase()},clamp:function(a,b,c){return Math.max(b,Math.min(c,a))},euclideanModulo:function(a,b){return(a%b+b)%b},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},lerp:function(a,b,c){return(1-c)*a+c*b},smoothstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(a*6-15)+10)},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*N.DEG2RAD},radToDeg:function(a){return a*N.RAD2DEG},isPowerOfTwo:function(a){return(a&a-1)===0&&a!==0},ceilPowerOfTwo:function(a){return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))},floorPowerOfTwo:function(a){return Math.pow(2,Math.floor(Math.log(a)/Math.LN2))},setQuaternionFromProperEuler:function(a,b,c,d,e){var f=Math.cos,g=Math.sin,h=f(c/2);c=g(c/2);var i=f((b+d)/2),j=g((b+d)/2),k=f((b-d)/2),l=g((b-d)/2);f=f((d-b)/2);g=g((d-b)/2);switch(e){case"XYX":a.set(h*j,c*k,c*l,h*i);break;case"YZY":a.set(c*l,h*j,c*k,h*i);break;case"ZXZ":a.set(c*k,c*l,h*j,h*i);break;case"XZX":a.set(h*j,c*g,c*f,h*i);break;case"YXY":a.set(c*f,h*j,c*g,h*i);break;case"ZYZ":a.set(c*g,c*f,h*j,h*i);break;default:}}};function O(a,b){this.x=a||0,this.y=b||0}Object.defineProperties(O.prototype,{width:{get:function(){return this.x},set:function(a){this.x=a}},height:{get:function(){return this.y},set:function(a){this.y=a}}});Object.assign(O.prototype,{isVector2:!0,set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.x=a;this.y=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+a)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},applyMatrix3:function(a){var b=this.x,c=this.y;a=a.elements;this.x=a[0]*b+a[3]*c+a[6];this.y=a[1]*b+a[4]*c+a[7];return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(a,b){this.x=Math.max(a,Math.min(b,this.x));this.y=Math.max(a,Math.min(b,this.y));return this},clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this},roundToZero:function(){this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x);this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},cross:function(a){return this.x*a.y-this.y*a.x},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length()||1)},angle:function(){var a=Math.atan2(-this.y,-this.x)+Math.PI;return a},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x;a=this.y-a.y;return b*b+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){b===void 0&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},fromBufferAttribute:function(a,b,c){c!==void 0;this.x=a.getX(b);this.y=a.getY(b);return this},rotateAround:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=this.x-a.x,e=this.y-a.y;this.x=d*c-e*b+a.x;this.y=d*b+e*c+a.y;return this},random:function(){this.x=Math.random();this.y=Math.random();return this}});function cd(){this.elements=[1,0,0,0,1,0,0,0,1],arguments.length>0}Object.assign(cd.prototype,{isMatrix3:!0,set:function(a,b,c,d,e,f,g,h,i){var j=this.elements;j[0]=a;j[1]=d;j[2]=g;j[3]=b;j[4]=e;j[5]=h;j[6]=c;j[7]=f;j[8]=i;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},clone:function(){return new this.constructor().fromArray(this.elements)},copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return this},extractBasis:function(a,b,c){a.setFromMatrix3Column(this,0);b.setFromMatrix3Column(this,1);c.setFromMatrix3Column(this,2);return this},setFromMatrix4:function(a){a=a.elements;this.set(a[0],a[4],a[8],a[1],a[5],a[9],a[2],a[6],a[10]);return this},multiply:function(a){return this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){a=a.elements;b=b.elements;var c=this.elements,d=a[0],e=a[3],f=a[6],g=a[1],h=a[4],i=a[7],j=a[2],k=a[5];a=a[8];var l=b[0],m=b[3],n=b[6],o=b[1],p=b[4],q=b[7],r=b[2],s=b[5];b=b[8];c[0]=d*l+e*o+f*r;c[3]=d*m+e*p+f*s;c[6]=d*n+e*q+f*b;c[1]=g*l+h*o+i*r;c[4]=g*m+h*p+i*s;c[7]=g*n+h*q+i*b;c[2]=j*l+k*o+a*r;c[5]=j*m+k*p+a*s;c[8]=j*n+k*q+a*b;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7];a=a[8];return b*f*a-b*g*i-c*e*a+c*g*h+d*e*i-d*f*h},getInverse:function(a,b){b!==void 0;b=a.elements;a=this.elements;var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7];b=b[8];var k=b*g-h*j,l=h*i-b*f,m=j*f-g*i,n=c*k+d*l+e*m;if(n===0)return this.set(0,0,0,0,0,0,0,0,0);n=1/n;a[0]=k*n;a[1]=(e*j-b*d)*n;a[2]=(h*d-e*g)*n;a[3]=l*n;a[4]=(b*c-e*i)*n;a[5]=(e*f-h*c)*n;a[6]=m*n;a[7]=(d*i-j*c)*n;a[8]=(g*c-d*f)*n;return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},getNormalMatrix:function(a){return this.setFromMatrix4(a).getInverse(this).transpose()},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},setUvTransform:function(a,b,c,d,e,f,g){var h=Math.cos(e);e=Math.sin(e);this.set(c*h,c*e,-c*(h*f+e*g)+f+a,-d*e,d*h,-d*(-e*f+h*g)+g+b,0,0,1)},scale:function(a,b){var c=this.elements;c[0]*=a;c[3]*=a;c[6]*=a;c[1]*=b;c[4]*=b;c[7]*=b;return this},rotate:function(a){var b=Math.cos(a);a=Math.sin(a);var c=this.elements,d=c[0],e=c[3],f=c[6],g=c[1],h=c[4],i=c[7];c[0]=b*d+a*g;c[3]=b*e+a*h;c[6]=b*f+a*i;c[1]=-a*d+b*g;c[4]=-a*e+b*h;c[7]=-a*f+b*i;return this},translate:function(a,b){var c=this.elements;c[0]+=a*c[2];c[3]+=a*c[5];c[6]+=a*c[8];c[1]+=b*c[2];c[4]+=b*c[5];c[7]+=b*c[8];return this},equals:function(b){var c=this.elements;b=b.elements;for(var a=0;a<9;a++)if(c[a]!==b[a])return!1;return!0},fromArray:function(b,c){c===void 0&&(c=0);for(var a=0;a<9;a++)this.elements[a]=b[a+c];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a}});var dd,ed={getDataURL:function(a){var b;if(typeof HTMLCanvasElement=="undefined")return a.src;else if(a instanceof HTMLCanvasElement)b=a;else{dd===void 0&&(dd=document.createElementNS("http://www.w3.org/1999/xhtml","canvas"));dd.width=a.width;dd.height=a.height;var c=dd.getContext("2d");a instanceof ImageData?c.putImageData(a,0,0):c.drawImage(a,0,0,a.width,a.height);b=dd}if(b.width>2048||b.height>2048)return b.toDataURL("image/jpeg",.6);else return b.toDataURL("image/png")}},fd=0;function gd(a,b,c,d,e,f,g,h,i,j){Object.defineProperty(this,"id",{value:fd++}),this.uuid=N.generateUUID(),this.name="",this.image=a!==void 0?a:gd.DEFAULT_IMAGE,this.mipmaps=[],this.mapping=b!==void 0?b:gd.DEFAULT_MAPPING,this.wrapS=c!==void 0?c:Da,this.wrapT=d!==void 0?d:Da,this.magFilter=e!==void 0?e:Ka,this.minFilter=f!==void 0?f:Na,this.anisotropy=i!==void 0?i:1,this.format=g!==void 0?g:cb,this.internalFormat=null,this.type=h!==void 0?h:Pa,this.offset=new O(0,0),this.repeat=new O(1,1),this.center=new O(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new cd(),this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.encoding=j!==void 0?j:pc,this.version=0,this.onUpdate=null}gd.DEFAULT_IMAGE=void 0;gd.DEFAULT_MAPPING=ua;gd.prototype=Object.assign(Object.create($c.prototype),{constructor:gd,isTexture:!0,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)},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.name=a.name;this.image=a.image;this.mipmaps=a.mipmaps.slice(0);this.mapping=a.mapping;this.wrapS=a.wrapS;this.wrapT=a.wrapT;this.magFilter=a.magFilter;this.minFilter=a.minFilter;this.anisotropy=a.anisotropy;this.format=a.format;this.internalFormat=a.internalFormat;this.type=a.type;this.offset.copy(a.offset);this.repeat.copy(a.repeat);this.center.copy(a.center);this.rotation=a.rotation;this.matrixAutoUpdate=a.matrixAutoUpdate;this.matrix.copy(a.matrix);this.generateMipmaps=a.generateMipmaps;this.premultiplyAlpha=a.premultiplyAlpha;this.flipY=a.flipY;this.unpackAlignment=a.unpackAlignment;this.encoding=a.encoding;return this},toJSON:function(b){var c=b===void 0||typeof b==="string";if(!c&&b.textures[this.uuid]!==void 0)return b.textures[this.uuid];var d={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(this.image!==void 0){var e=this.image;e.uuid===void 0&&(e.uuid=N.generateUUID());if(!c&&b.images[e.uuid]===void 0){var f;if(Array.isArray(e)){f=[];for(var a=0,g=e.length;a1)switch(this.wrapS){case Ca:a.x=a.x-Math.floor(a.x);break;case Da:a.x=a.x<0?0:1;break;case Ea:Math.abs(Math.floor(a.x)%2)===1?a.x=Math.ceil(a.x)-a.x:a.x=a.x-Math.floor(a.x);break}if(a.y<0||a.y>1)switch(this.wrapT){case Ca:a.y=a.y-Math.floor(a.y);break;case Da:a.y=a.y<0?0:1;break;case Ea:Math.abs(Math.floor(a.y)%2)===1?a.y=Math.ceil(a.y)-a.y:a.y=a.y-Math.floor(a.y);break}this.flipY&&(a.y=1-a.y);return a}});Object.defineProperty(gd.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});function hd(a,b,c,d){this.x=a||0,this.y=b||0,this.z=c||0,this.w=d!==void 0?d:1}Object.defineProperties(hd.prototype,{width:{get:function(){return this.z},set:function(a){this.z=a}},height:{get:function(){return this.w},set:function(a){this.w=a}}});Object.assign(hd.prototype,{isVector4:!0,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setScalar:function(a){this.x=a;this.y=a;this.z=a;this.w=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){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: "+a)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=a.w!==void 0?a.w:1;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;this.w+=a.w*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;this.w-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);b<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){var b,c,d,e=.01,f=.1;a=a.elements;var g=a[0],h=a[4],i=a[8],j=a[1],k=a[5],l=a[9],m=a[2],n=a[6];a=a[10];if(Math.abs(h-j)p&&o>q?oq?p=0?1:-1,o=1-m*m;if(o>Number.EPSILON){o=Math.sqrt(o);m=Math.atan2(o,m*n);f=Math.sin(f*m)/o;g=Math.sin(g*m)/o}m=g*n;h=h*f+d*m;i=i*f+k*m;j=j*f+l*m;c=c*f+e*m;if(f===1-g){o=1/Math.sqrt(h*h+i*i+j*j+c*c);h*=o;i*=o;j*=o;c*=o}}a[b]=h;a[b+1]=i;a[b+2]=j;a[b+3]=c},multiplyQuaternionsFlat:function(a,b,c,d,e,f){var g=c[d],h=c[d+1],i=c[d+2];c=c[d+3];d=e[f];var j=e[f+1],k=e[f+2];e=e[f+3];a[b]=g*e+c*d+h*k-i*j;a[b+1]=h*e+c*j+i*d-g*k;a[b+2]=i*e+c*k+g*j-h*d;a[b+3]=c*e-g*d-h*j-i*k;return a}});Object.defineProperties(kd.prototype,{x:{get:function(){return this._x},set:function(a){this._x=a,this._onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a,this._onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a,this._onChangeCallback()}},w:{get:function(){return this._w},set:function(a){this._w=a,this._onChangeCallback()}}});Object.assign(kd.prototype,{isQuaternion:!0,set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this._onChangeCallback();return this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;this._w=a.w;this._onChangeCallback();return this},setFromEuler:function(a,b){if(!(a&&a.isEuler))throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");var c=a._x,d=a._y,e=a._z;a=a.order;var f=Math.cos,g=Math.sin,h=f(c/2),i=f(d/2);f=f(e/2);c=g(c/2);d=g(d/2);g=g(e/2);switch(a){case"XYZ":this._x=c*i*f+h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f-c*d*g;break;case"YXZ":this._x=c*i*f+h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f+c*d*g;break;case"ZXY":this._x=c*i*f-h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f-c*d*g;break;case"ZYX":this._x=c*i*f-h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f+c*d*g;break;case"YZX":this._x=c*i*f+h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f-c*d*g;break;case"XZY":this._x=c*i*f-h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f+c*d*g;break;default:}b!==!1&&this._onChangeCallback();return this},setFromAxisAngle:function(a,b){b=b/2;var c=Math.sin(b);this._x=a.x*c;this._y=a.y*c;this._z=a.z*c;this._w=Math.cos(b);this._onChangeCallback();return this},setFromRotationMatrix:function(a){a=a.elements;var b=a[0],c=a[4],d=a[8],e=a[1],f=a[5],g=a[9],h=a[2],i=a[6];a=a[10];var j=b+f+a;j>0?(j=.5/Math.sqrt(j+1),this._w=.25/j,this._x=(i-g)*j,this._y=(d-h)*j,this._z=(e-c)*j):b>f&&b>a?(j=2*Math.sqrt(1+b-f-a),this._w=(i-g)/j,this._x=.25*j,this._y=(c+e)/j,this._z=(d+h)/j):f>a?(j=2*Math.sqrt(1+f-b-a),this._w=(d-h)/j,this._x=(c+e)/j,this._y=.25*j,this._z=(g+i)/j):(j=2*Math.sqrt(1+a-b-f),this._w=(e-c)/j,this._x=(d+h)/j,this._y=(g+i)/j,this._z=.25*j);this._onChangeCallback();return this},setFromUnitVectors:function(a,b){var c=1e-6,d=a.dot(b)+1;dMath.abs(a.z)?(this._x=-a.y,this._y=a.x,this._z=0,this._w=d):(this._x=0,this._y=-a.z,this._z=a.y,this._w=d)):(this._x=a.y*b.z-a.z*b.y,this._y=a.z*b.x-a.x*b.z,this._z=a.x*b.y-a.y*b.x,this._w=d);return this.normalize()},angleTo:function(a){return 2*Math.acos(Math.abs(N.clamp(this.dot(a),-1,1)))},rotateTowards:function(a,b){var c=this.angleTo(a);if(c===0)return this;b=Math.min(1,b/c);this.slerp(a,b);return this},inverse:function(){return this.conjugate()},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this._onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();a===0?(this._x=0,this._y=0,this._z=0,this._w=1):(a=1/a,this._x=this._x*a,this._y=this._y*a,this._z=this._z*a,this._w=this._w*a);this._onChangeCallback();return this},multiply:function(a,b){return b!==void 0?this.multiplyQuaternions(a,b):this.multiplyQuaternions(this,a)},premultiply:function(a){return this.multiplyQuaternions(a,this)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z;a=a._w;var f=b._x,g=b._y,h=b._z;b=b._w;this._x=c*b+a*f+d*h-e*g;this._y=d*b+a*g+e*f-c*h;this._z=e*b+a*h+c*g-d*f;this._w=a*b-c*f-d*g-e*h;this._onChangeCallback();return this},slerp:function(a,b){if(b===0)return this;if(b===1)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;g<0?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(g>=1){this._w=f;this._x=c;this._y=d;this._z=e;return this}a=1-g*g;if(a<=Number.EPSILON){var h=1-b;this._w=h*f+b*this._w;this._x=h*c+b*this._x;this._y=h*d+b*this._y;this._z=h*e+b*this._z;this.normalize();this._onChangeCallback();return this}h=Math.sqrt(a);a=Math.atan2(h,g);g=Math.sin((1-b)*a)/h;b=Math.sin(b*a)/h;this._w=f*g+this._w*b;this._x=c*g+this._x*b;this._y=d*g+this._y*b;this._z=e*g+this._z*b;this._onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){b===void 0&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this._onChangeCallback();return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},fromBufferAttribute:function(a,b){this._x=a.getX(b);this._y=a.getY(b);this._z=a.getZ(b);this._w=a.getW(b);return this},_onChange:function(a){this._onChangeCallback=a;return this},_onChangeCallback:function(){}});var ld=new P(),md=new kd();function P(a,b,c){this.x=a||0,this.y=b||0,this.z=c||0}Object.assign(P.prototype,{isVector3:!0,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setScalar:function(a){this.x=a;this.y=a;this.z=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+a)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(b!==void 0)return this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(a){!(a&&a.isEuler);return this.applyQuaternion(md.setFromEuler(a))},applyAxisAngle:function(a,b){return this.applyQuaternion(md.setFromAxisAngle(a,b))},applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyNormalMatrix:function(a){return this.applyMatrix3(a).normalize()},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,i=a*c+g*b-e*d,j=a*d+e*c-f*b;b=-e*b-f*c-g*d;this.x=h*a+b*-e+i*-g-j*-f;this.y=i*a+b*-f+j*-e-h*-g;this.z=j*a+b*-g+h*-f-i*-e;return this},project:function(a){return this.applyMatrix4(a.matrixWorldInverse).applyMatrix4(a.projectionMatrix)},unproject:function(a){return this.applyMatrix4(a.projectionMatrixInverse).applyMatrix4(a.matrixWorld)},transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;return this.normalize()},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));return this},clampScalar:function(a,b){this.x=Math.max(a,Math.min(b,this.x));this.y=Math.max(a,Math.min(b,this.y));this.z=Math.max(a,Math.min(b,this.z));return this},clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x);this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y);this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length()||1)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},cross:function(a,b){return b!==void 0?this.crossVectors(a,b):this.crossVectors(this,a)},crossVectors:function(a,b){var c=a.x,d=a.y;a=a.z;var e=b.x,f=b.y;b=b.z;this.x=d*b-a*f;this.y=a*e-c*b;this.z=c*f-d*e;return this},projectOnVector:function(a){var b=a.lengthSq();if(b===0)return this.set(0,0,0);b=a.dot(this)/b;return this.copy(a).multiplyScalar(b)},projectOnPlane:function(a){ld.copy(this).projectOnVector(a);return this.sub(ld)},reflect:function(a){return this.sub(ld.copy(a).multiplyScalar(2*this.dot(a)))},angleTo:function(a){var b=Math.sqrt(this.lengthSq()*a.lengthSq());if(b===0)return Math.PI/2;a=this.dot(a)/b;return Math.acos(N.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)+Math.abs(this.z-a.z)},setFromSpherical:function(a){return this.setFromSphericalCoords(a.radius,a.phi,a.theta)},setFromSphericalCoords:function(a,b,c){var d=Math.sin(b)*a;this.x=d*Math.sin(c);this.y=Math.cos(b)*a;this.z=d*Math.cos(c);return this},setFromCylindrical:function(a){return this.setFromCylindricalCoords(a.radius,a.theta,a.y)},setFromCylindricalCoords:function(a,b,c){this.x=a*Math.sin(b);this.y=c;this.z=a*Math.cos(b);return this},setFromMatrixPosition:function(a){a=a.elements;this.x=a[12];this.y=a[13];this.z=a[14];return this},setFromMatrixScale:function(a){var b=this.setFromMatrixColumn(a,0).length(),c=this.setFromMatrixColumn(a,1).length();a=this.setFromMatrixColumn(a,2).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){return this.fromArray(a.elements,b*4)},setFromMatrix3Column:function(a,b){return this.fromArray(a.elements,b*3)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){b===void 0&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromBufferAttribute:function(a,b,c){c!==void 0;this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);return this},random:function(){this.x=Math.random();this.y=Math.random();this.z=Math.random();return this}});var nd=new P(),od=new Q(),pd=new P(0,0,0),qd=new P(1,1,1),rd=new P(),sd=new P(),td=new P();function Q(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],arguments.length>0}Object.assign(Q.prototype,{isMatrix4:!0,set:function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){var q=this.elements;q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=h;q[2]=i;q[6]=j;q[10]=k;q[14]=l;q[3]=m;q[7]=n;q[11]=o;q[15]=p;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},clone:function(){return new Q().fromArray(this.elements)},copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return this},copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x,b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(a){var b=this.elements,c=a.elements,d=1/nd.setFromMatrixColumn(a,0).length(),e=1/nd.setFromMatrixColumn(a,1).length();a=1/nd.setFromMatrixColumn(a,2).length();b[0]=c[0]*d;b[1]=c[1]*d;b[2]=c[2]*d;b[3]=0;b[4]=c[4]*e;b[5]=c[5]*e;b[6]=c[6]*e;b[7]=0;b[8]=c[8]*a;b[9]=c[9]*a;b[10]=c[10]*a;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromEuler:function(a){!(a&&a.isEuler);var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c);c=Math.sin(c);var g=Math.cos(d);d=Math.sin(d);var h=Math.cos(e);e=Math.sin(e);if(a.order==="XYZ"){var i=f*h,j=f*e,k=c*h,l=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=j+k*d;b[5]=i-l*d;b[9]=-c*g;b[2]=l-i*d;b[6]=k+j*d;b[10]=f*g}else if(a.order==="YXZ"){var m=g*h,n=g*e,o=d*h,p=d*e;b[0]=m+p*c;b[4]=o*c-n;b[8]=f*d;b[1]=f*e;b[5]=f*h;b[9]=-c;b[2]=n*c-o;b[6]=p+m*c;b[10]=f*g}else if(a.order==="ZXY"){var m=g*h,n=g*e,o=d*h,p=d*e;b[0]=m-p*c;b[4]=-f*e;b[8]=o+n*c;b[1]=n+o*c;b[5]=f*h;b[9]=p-m*c;b[2]=-f*d;b[6]=c;b[10]=f*g}else if(a.order==="ZYX"){var i=f*h,j=f*e,k=c*h,l=c*e;b[0]=g*h;b[4]=k*d-j;b[8]=i*d+l;b[1]=g*e;b[5]=l*d+i;b[9]=j*d-k;b[2]=-d;b[6]=c*g;b[10]=f*g}else if(a.order==="YZX"){n=f*g;o=f*d;p=c*g;m=c*d;b[0]=g*h;b[4]=m-n*e;b[8]=p*e+o;b[1]=e;b[5]=f*h;b[9]=-c*h;b[2]=-d*h;b[6]=o*e+p;b[10]=n-m*e}else if(a.order==="XZY"){n=f*g;o=f*d;p=c*g;m=c*d;b[0]=g*h;b[4]=-e;b[8]=d*h;b[1]=n*e+m;b[5]=f*h;b[9]=o*e-p;b[2]=p*e-o;b[6]=c*h;b[10]=m*e+n}b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromQuaternion:function(a){return this.compose(pd,a,qd)},lookAt:function(a,b,c){var d=this.elements;td.subVectors(a,b);td.lengthSq()===0&&(td.z=1);td.normalize();rd.crossVectors(c,td);rd.lengthSq()===0&&(Math.abs(c.z)===1?td.x+=1e-4:td.z+=1e-4,td.normalize(),rd.crossVectors(c,td));rd.normalize();sd.crossVectors(td,rd);d[0]=rd.x;d[4]=sd.x;d[8]=td.x;d[1]=rd.y;d[5]=sd.y;d[9]=td.y;d[2]=rd.z;d[6]=sd.z;d[10]=td.z;return this},multiply:function(a,b){return b!==void 0?this.multiplyMatrices(a,b):this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){a=a.elements;b=b.elements;var c=this.elements,d=a[0],e=a[4],f=a[8],g=a[12],h=a[1],i=a[5],j=a[9],k=a[13],l=a[2],m=a[6],n=a[10],o=a[14],p=a[3],q=a[7],r=a[11];a=a[15];var s=b[0],t=b[4],u=b[8],v=b[12],w=b[1],x=b[5],y=b[9],z=b[13],A=b[2],B=b[6],C=b[10],D=b[14],E=b[3],F=b[7],aa=b[11];b=b[15];c[0]=d*s+e*w+f*A+g*E;c[4]=d*t+e*x+f*B+g*F;c[8]=d*u+e*y+f*C+g*aa;c[12]=d*v+e*z+f*D+g*b;c[1]=h*s+i*w+j*A+k*E;c[5]=h*t+i*x+j*B+k*F;c[9]=h*u+i*y+j*C+k*aa;c[13]=h*v+i*z+j*D+k*b;c[2]=l*s+m*w+n*A+o*E;c[6]=l*t+m*x+n*B+o*F;c[10]=l*u+m*y+n*C+o*aa;c[14]=l*v+m*z+n*D+o*b;c[3]=p*s+q*w+r*A+a*E;c[7]=p*t+q*x+r*B+a*F;c[11]=p*u+q*y+r*C+a*aa;c[15]=p*v+q*z+r*D+a*b;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[4],d=a[8],e=a[12],f=a[1],g=a[5],h=a[9],i=a[13],j=a[2],k=a[6],l=a[10],m=a[14],n=a[3],o=a[7],p=a[11];a=a[15];return n*(+e*h*k-d*i*k-e*g*l+c*i*l+d*g*m-c*h*m)+o*(+b*h*m-b*i*l+e*f*l-d*f*m+d*i*j-e*h*j)+p*(+b*i*k-b*g*m-e*f*k+c*f*m+e*g*j-c*i*j)+a*(-d*g*j-b*h*k+b*g*l+d*f*k-c*f*l+c*h*j)},transpose:function(){var b=this.elements,a;a=b[1];b[1]=b[4];b[4]=a;a=b[2];b[2]=b[8];b[8]=a;a=b[6];b[6]=b[9];b[9]=a;a=b[3];b[3]=b[12];b[12]=a;a=b[7];b[7]=b[13];b[13]=a;a=b[11];b[11]=b[14];b[14]=a;return this},setPosition:function(a,b,c){var d=this.elements;a.isVector3?(d[12]=a.x,d[13]=a.y,d[14]=a.z):(d[12]=a,d[13]=b,d[14]=c);return this},getInverse:function(a,b){b!==void 0;b=this.elements;a=a.elements;var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],m=a[10],n=a[11],o=a[12],p=a[13],q=a[14];a=a[15];var r=l*q*j-p*m*j+p*i*n-h*q*n-l*i*a+h*m*a,s=o*m*j-k*q*j-o*i*n+g*q*n+k*i*a-g*m*a,t=k*p*j-o*l*j+o*h*n-g*p*n-k*h*a+g*l*a,u=o*l*i-k*p*i-o*h*m+g*p*m+k*h*q-g*l*q,v=c*r+d*s+e*t+f*u;if(v===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);v=1/v;b[0]=r*v;b[1]=(p*m*f-l*q*f-p*e*n+d*q*n+l*e*a-d*m*a)*v;b[2]=(h*q*f-p*i*f+p*e*j-d*q*j-h*e*a+d*i*a)*v;b[3]=(l*i*f-h*m*f-l*e*j+d*m*j+h*e*n-d*i*n)*v;b[4]=s*v;b[5]=(k*q*f-o*m*f+o*e*n-c*q*n-k*e*a+c*m*a)*v;b[6]=(o*i*f-g*q*f-o*e*j+c*q*j+g*e*a-c*i*a)*v;b[7]=(g*m*f-k*i*f+k*e*j-c*m*j-g*e*n+c*i*n)*v;b[8]=t*v;b[9]=(o*l*f-k*p*f-o*d*n+c*p*n+k*d*a-c*l*a)*v;b[10]=(g*p*f-o*h*f+o*d*j-c*p*j-g*d*a+c*h*a)*v;b[11]=(k*h*f-g*l*f-k*d*j+c*l*j+g*d*n-c*h*n)*v;b[12]=u*v;b[13]=(k*p*e-o*l*e+o*d*m-c*p*m-k*d*q+c*l*q)*v;b[14]=(o*h*e-g*p*e-o*d*i+c*p*i+g*d*q-c*h*q)*v;b[15]=(g*l*e-k*h*e+k*d*i-c*l*i-g*d*m+c*h*m)*v;return this},scale:function(a){var b=this.elements,c=a.x,d=a.y;a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements,b=a[0]*a[0]+a[1]*a[1]+a[2]*a[2],c=a[4]*a[4]+a[5]*a[5]+a[6]*a[6];a=a[8]*a[8]+a[9]*a[9]+a[10]*a[10];return Math.sqrt(Math.max(b,c,a))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(1,0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=1-c,e=a.x,f=a.y;a=a.z;var g=d*e,h=d*f;this.set(g*e+c,g*f-b*a,g*a+b*f,0,g*f+b*a,h*f+c,h*a-b*e,0,g*a-b*f,h*a+b*e,d*a*a+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},makeShear:function(a,b,c){this.set(1,b,c,0,a,1,c,0,a,b,1,0,0,0,0,1);return this},compose:function(a,b,c){var d=this.elements,e=b._x,f=b._y,g=b._z;b=b._w;var h=e+e,i=f+f,j=g+g,k=e*h,l=e*i;e=e*j;var m=f*i;f=f*j;g=g*j;h=b*h;i=b*i;b=b*j;j=c.x;var n=c.y;c=c.z;d[0]=(1-(m+g))*j;d[1]=(l+b)*j;d[2]=(e-i)*j;d[3]=0;d[4]=(l-b)*n;d[5]=(1-(k+g))*n;d[6]=(f+h)*n;d[7]=0;d[8]=(e+i)*c;d[9]=(f-h)*c;d[10]=(1-(k+m))*c;d[11]=0;d[12]=a.x;d[13]=a.y;d[14]=a.z;d[15]=1;return this},decompose:function(a,b,c){var d=this.elements,e=nd.set(d[0],d[1],d[2]).length(),f=nd.set(d[4],d[5],d[6]).length(),g=nd.set(d[8],d[9],d[10]).length(),h=this.determinant();h<0&&(e=-e);a.x=d[12];a.y=d[13];a.z=d[14];od.copy(this);h=1/e;a=1/f;d=1/g;od.elements[0]*=h;od.elements[1]*=h;od.elements[2]*=h;od.elements[4]*=a;od.elements[5]*=a;od.elements[6]*=a;od.elements[8]*=d;od.elements[9]*=d;od.elements[10]*=d;b.setFromRotationMatrix(od);c.x=e;c.y=f;c.z=g;return this},makePerspective:function(a,b,c,d,e,f){f===void 0;var g=this.elements,h=2*e/(b-a),i=2*e/(c-d);b=(b+a)/(b-a);a=(c+d)/(c-d);c=-(f+e)/(f-e);d=-2*f*e/(f-e);g[0]=h;g[4]=0;g[8]=b;g[12]=0;g[1]=0;g[5]=i;g[9]=a;g[13]=0;g[2]=0;g[6]=0;g[10]=c;g[14]=d;g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=1/(b-a),i=1/(c-d),j=1/(f-e);b=(b+a)*h;a=(c+d)*i;c=(f+e)*j;g[0]=2*h;g[4]=0;g[8]=0;g[12]=-b;g[1]=0;g[5]=2*i;g[9]=0;g[13]=-a;g[2]=0;g[6]=0;g[10]=-2*j;g[14]=-c;g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},equals:function(b){var c=this.elements;b=b.elements;for(var a=0;a<16;a++)if(c[a]!==b[a])return!1;return!0},fromArray:function(b,c){c===void 0&&(c=0);for(var a=0;a<16;a++)this.elements[a]=b[a+c];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a}});var ud=new Q(),vd=new kd();function wd(a,b,c,d){this._x=a||0,this._y=b||0,this._z=c||0,this._order=d||wd.DefaultOrder}wd.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];wd.DefaultOrder="XYZ";Object.defineProperties(wd.prototype,{x:{get:function(){return this._x},set:function(a){this._x=a,this._onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a,this._onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a,this._onChangeCallback()}},order:{get:function(){return this._order},set:function(a){this._order=a,this._onChangeCallback()}}});Object.assign(wd.prototype,{isEuler:!0,set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this._onChangeCallback();return this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._order=a._order;this._onChangeCallback();return this},setFromRotationMatrix:function(a,b,c){var d=N.clamp;a=a.elements;var e=a[0],f=a[4],g=a[8],h=a[1],i=a[5],j=a[9],k=a[2],l=a[6];a=a[10];b=b||this._order;switch(b){case"XYZ":this._y=Math.asin(d(g,-1,1));Math.abs(g)<.9999999?(this._x=Math.atan2(-j,a),this._z=Math.atan2(-f,e)):(this._x=Math.atan2(l,i),this._z=0);break;case"YXZ":this._x=Math.asin(-d(j,-1,1));Math.abs(j)<.9999999?(this._y=Math.atan2(g,a),this._z=Math.atan2(h,i)):(this._y=Math.atan2(-k,e),this._z=0);break;case"ZXY":this._x=Math.asin(d(l,-1,1));Math.abs(l)<.9999999?(this._y=Math.atan2(-k,a),this._z=Math.atan2(-f,i)):(this._y=0,this._z=Math.atan2(h,e));break;case"ZYX":this._y=Math.asin(-d(k,-1,1));Math.abs(k)<.9999999?(this._x=Math.atan2(l,a),this._z=Math.atan2(h,e)):(this._x=0,this._z=Math.atan2(-f,i));break;case"YZX":this._z=Math.asin(d(h,-1,1));Math.abs(h)<.9999999?(this._x=Math.atan2(-j,i),this._y=Math.atan2(-k,e)):(this._x=0,this._y=Math.atan2(g,a));break;case"XZY":this._z=Math.asin(-d(f,-1,1));Math.abs(f)<.9999999?(this._x=Math.atan2(l,i),this._y=Math.atan2(g,e)):(this._x=Math.atan2(-j,a),this._y=0);break;default:}this._order=b;c!==!1&&this._onChangeCallback();return this},setFromQuaternion:function(a,b,c){ud.makeRotationFromQuaternion(a);return this.setFromRotationMatrix(ud,b,c)},setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(a){vd.setFromEuler(this);return this.setFromQuaternion(vd,a)},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];a[3]!==void 0&&(this._order=a[3]);this._onChangeCallback();return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){if(a)return a.set(this._x,this._y,this._z);else return new P(this._x,this._y,this._z)},_onChange:function(a){this._onChangeCallback=a;return this},_onChangeCallback:function(){}});function xd(){this.mask=1|0}Object.assign(xd.prototype,{set:function(a){this.mask=1<1){for(var a=0;a1){for(var a=0;a0){e.children=[];for(var a=0;a0&&(d.geometries=h);i.length>0&&(d.materials=i);f.length>0&&(d.textures=f);a.length>0&&(d.images=a);g.length>0&&(d.shapes=g)}d.object=e;return d;function j(a){var b=[];for(var c in a){var d=a[c];delete d.metadata;b.push(d)}return b}},clone:function(a){return new this.constructor().copy(this,a)},copy:function(a,b){b===void 0&&(b=!0);this.name=a.name;this.up.copy(a.up);this.position.copy(a.position);this.quaternion.copy(a.quaternion);this.scale.copy(a.scale);this.matrix.copy(a.matrix);this.matrixWorld.copy(a.matrixWorld);this.matrixAutoUpdate=a.matrixAutoUpdate;this.matrixWorldNeedsUpdate=a.matrixWorldNeedsUpdate;this.layers.mask=a.layers.mask;this.visible=a.visible;this.castShadow=a.castShadow;this.receiveShadow=a.receiveShadow;this.frustumCulled=a.frustumCulled;this.renderOrder=a.renderOrder;this.userData=JSON.parse(JSON.stringify(a.userData));if(b===!0)for(b=0;bf&&(f=j);k>g&&(g=k);l>h&&(h=l)}this.min.set(c,d,e);this.max.set(f,g,h);return this},setFromBufferAttribute:function(b){var c=+Infinity,d=+Infinity,e=+Infinity,f=-Infinity,g=-Infinity,h=-Infinity;for(var a=0,i=b.count;af&&(f=j);k>g&&(g=k);l>h&&(h=l)}this.min.set(c,d,e);this.max.set(f,g,h);return this},setFromPoints:function(b){this.makeEmpty();for(var a=0,c=b.length;athis.max.x||a.ythis.max.y||a.zthis.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z},getParameter:function(a,b){b===void 0&&(b=new P());return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y||a.max.zthis.max.z?!1:!0},intersectsSphere:function(a){this.clampPoint(a.center,Nd);return Nd.distanceToSquared(a.center)<=a.radius*a.radius},intersectsPlane:function(a){var b,c;a.normal.x>0?(b=a.normal.x*this.min.x,c=a.normal.x*this.max.x):(b=a.normal.x*this.max.x,c=a.normal.x*this.min.x);a.normal.y>0?(b+=a.normal.y*this.min.y,c+=a.normal.y*this.max.y):(b+=a.normal.y*this.max.y,c+=a.normal.y*this.min.y);a.normal.z>0?(b+=a.normal.z*this.min.z,c+=a.normal.z*this.max.z):(b+=a.normal.z*this.max.z,c+=a.normal.z*this.min.z);return b<=-a.constant&&c>=-a.constant},intersectsTriangle:function(a){if(this.isEmpty())return!1;this.getCenter(Vd);Wd.subVectors(this.max,Vd);Pd.subVectors(a.a,Vd);Qd.subVectors(a.b,Vd);Rd.subVectors(a.c,Vd);Sd.subVectors(Qd,Pd);Td.subVectors(Rd,Qd);Ud.subVectors(Pd,Rd);a=[0,-Sd.z,Sd.y,0,-Td.z,Td.y,0,-Ud.z,Ud.y,Sd.z,0,-Sd.x,Td.z,0,-Td.x,Ud.z,0,-Ud.x,-Sd.y,Sd.x,0,-Td.y,Td.x,0,-Ud.y,Ud.x,0];if(!$d(a,Pd,Qd,Rd,Wd))return!1;a=[1,0,0,0,1,0,0,0,1];if(!$d(a,Pd,Qd,Rd,Wd))return!1;Xd.crossVectors(Sd,Td);a=[Xd.x,Xd.y,Xd.z];return $d(a,Pd,Qd,Rd,Wd)},clampPoint:function(a,b){b===void 0&&(b=new P());return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(a){var b=Nd.copy(a).clamp(this.min,this.max);return b.sub(a).length()},getBoundingSphere:function(a){a===void 0;this.getCenter(a.center);a.radius=this.getSize(Nd).length()*.5;return a},intersect:function(a){this.min.max(a.min);this.max.min(a.max);this.isEmpty()&&this.makeEmpty();return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(a){if(this.isEmpty())return this;Md[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(a);Md[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(a);Md[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(a);Md[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(a);Md[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(a);Md[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(a);Md[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(a);Md[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(a);this.setFromPoints(Md);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});function $d(b,c,d,e,f){var a,g;for(a=0,g=b.length-3;a<=g;a+=3){Yd.fromArray(b,a);var h=f.x*Math.abs(Yd.x)+f.y*Math.abs(Yd.y)+f.z*Math.abs(Yd.z),i=c.dot(Yd),j=d.dot(Yd),k=e.dot(Yd);if(Math.max(-Math.max(i,j,k),Math.min(i,j,k))>h)return!1}return!0}var ae=new Zd();function be(a,b){this.center=a!==void 0?a:new P(),this.radius=b!==void 0?b:-1}Object.assign(be.prototype,{set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(b,c){var d=this.center;c!==void 0?d.copy(c):ae.setFromPoints(b).getCenter(d);c=0;for(var a=0,e=b.length;athis.radius*this.radius&&(b.sub(this.center).normalize(),b.multiplyScalar(this.radius).add(this.center));return b},getBoundingBox:function(a){a===void 0&&(a=new Zd());if(this.isEmpty()){a.makeEmpty();return a}a.set(this.center,this.center);a.expandByScalar(this.radius);return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius=this.radius*a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius}});var ce=new P(),de=new P(),ee=new P(),fe=new P(),ge=new P(),he=new P(),ie=new P();function je(a,b){this.origin=a!==void 0?a:new P(),this.direction=b!==void 0?b:new P(0,0,-1)}Object.assign(je.prototype,{set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){b===void 0&&(b=new P());return b.copy(this.direction).multiplyScalar(a).add(this.origin)},lookAt:function(a){this.direction.copy(a).sub(this.origin).normalize();return this},recast:function(a){this.origin.copy(this.at(a,ce));return this},closestPointToPoint:function(a,b){b===void 0&&(b=new P());b.subVectors(a,this.origin);a=b.dot(this.direction);return a<0?b.copy(this.origin):b.copy(this.direction).multiplyScalar(a).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))},distanceSqToPoint:function(a){var b=ce.subVectors(a,this.origin).dot(this.direction);if(b<0)return this.origin.distanceToSquared(a);ce.copy(this.direction).multiplyScalar(b).add(this.origin);return ce.distanceToSquared(a)},distanceSqToSegment:function(a,b,c,d){de.copy(a).add(b).multiplyScalar(.5);ee.copy(b).sub(a).normalize();fe.copy(this.origin).sub(de);a=a.distanceTo(b)*.5;b=-this.direction.dot(ee);var e=fe.dot(this.direction),f=-fe.dot(ee),g=fe.lengthSq(),h=Math.abs(1-b*b),i,j,k;if(h>0){i=b*f-e;j=b*e-f;k=a*h;if(i>=0)if(j>=-k)if(j<=k){h=1/h;i*=h;j*=h;h=i*(i+b*j+2*e)+j*(b*i+j+2*f)+g}else j=a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;else j=-a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;else j<=-k?(i=Math.max(0,-(-b*a+e)),j=i>0?-a:Math.min(Math.max(-a,-f),a),h=-i*i+j*(j+2*f)+g):j<=k?(i=0,j=Math.min(Math.max(-a,-f),a),h=j*(j+2*f)+g):(i=Math.max(0,-(b*a+e)),j=i>0?a:Math.min(Math.max(-a,-f),a),h=-i*i+j*(j+2*f)+g)}else j=b>0?-a:a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;c&&c.copy(this.direction).multiplyScalar(i).add(this.origin);d&&d.copy(ee).multiplyScalar(j).add(de);return h},intersectSphere:function(a,b){ce.subVectors(a.center,this.origin);var c=ce.dot(this.direction),d=ce.dot(ce)-c*c;a=a.radius*a.radius;if(d>a)return null;a=Math.sqrt(a-d);d=c-a;c=c+a;if(d<0&&c<0)return null;return d<0?this.at(c,b):this.at(d,b)},intersectsSphere:function(a){return this.distanceSqToPoint(a.center)<=a.radius*a.radius},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(b===0)return a.distanceToPoint(this.origin)===0?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return a>=0?a:null},intersectPlane:function(a,b){a=this.distanceToPlane(a);return a===null?null:this.at(a,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin);if(b===0)return!0;a=a.normal.dot(this.direction);return a*b<0?!0:!1},intersectBox:function(a,b){var c,d,e,f=1/this.direction.x,g=1/this.direction.y,h=1/this.direction.z,i=this.origin;f>=0?(c=(a.min.x-i.x)*f,d=(a.max.x-i.x)*f):(c=(a.max.x-i.x)*f,d=(a.min.x-i.x)*f);g>=0?(f=(a.min.y-i.y)*g,e=(a.max.y-i.y)*g):(f=(a.max.y-i.y)*g,e=(a.min.y-i.y)*g);if(c>e||f>d)return null;(f>c||c!==c)&&(c=f);(e=0?(g=(a.min.z-i.z)*h,f=(a.max.z-i.z)*h):(g=(a.max.z-i.z)*h,f=(a.min.z-i.z)*h);if(c>f||g>d)return null;(g>c||c!==c)&&(c=g);(f=0?c:d,b)},intersectsBox:function(a){return this.intersectBox(a,ce)!==null},intersectTriangle:function(a,b,c,d,e){ge.subVectors(b,a);he.subVectors(c,a);ie.crossVectors(ge,he);b=this.direction.dot(ie);if(b>0){if(d)return null;c=1}else if(b<0)c=-1,b=-b;else return null;fe.subVectors(this.origin,a);d=c*this.direction.dot(he.crossVectors(fe,he));if(d<0)return null;a=c*this.direction.dot(ge.cross(fe));if(a<0)return null;if(d+a>b)return null;d=-c*fe.dot(ie);return d<0?null:this.at(d/b,e)},applyMatrix4:function(a){this.origin.applyMatrix4(a);this.direction.transformDirection(a);return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}});var ke=new P(),le=new P(),me=new cd();function ne(a,b){this.normal=a!==void 0?a:new P(1,0,0),this.constant=b!==void 0?b:0}Object.assign(ne.prototype,{isPlane:!0,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(a,b,c){c=ke.subVectors(c,b).cross(le.subVectors(a,b)).normalize();this.setFromNormalAndCoplanarPoint(c,a);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){b===void 0&&(b=new P());return b.copy(this.normal).multiplyScalar(-this.distanceToPoint(a)).add(a)},intersectLine:function(a,b){b===void 0&&(b=new P());var c=a.delta(ke),d=this.normal.dot(c);if(d===0)return this.distanceToPoint(a.start)===0?b.copy(a.start):void 0;d=-(a.start.dot(this.normal)+this.constant)/d;return d<0||d>1?void 0:b.copy(c).multiplyScalar(d).add(a.start)},intersectsLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return b<0&&a>0||a<0&&b>0},intersectsBox:function(a){return a.intersectsPlane(this)},intersectsSphere:function(a){return a.intersectsPlane(this)},coplanarPoint:function(a){a===void 0&&(a=new P());return a.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(a,b){b=b||me.getNormalMatrix(a);a=this.coplanarPoint(ke).applyMatrix4(a);b=this.normal.applyMatrix3(b).normalize();this.constant=-a.dot(b);return this},translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&a.constant===this.constant}});var oe=new P(),pe=new P(),qe=new P(),re=new P(),se=new P(),te=new P(),ue=new P(),ve=new P(),we=new P(),xe=new P();function ye(a,b,c){this.a=a!==void 0?a:new P(),this.b=b!==void 0?b:new P(),this.c=c!==void 0?c:new P()}Object.assign(ye,{getNormal:function(a,b,c,d){d===void 0&&(d=new P());d.subVectors(c,b);oe.subVectors(a,b);d.cross(oe);c=d.lengthSq();return c>0?d.multiplyScalar(1/Math.sqrt(c)):d.set(0,0,0)},getBarycoord:function(a,b,c,d,e){oe.subVectors(d,b);pe.subVectors(c,b);qe.subVectors(a,b);d=oe.dot(oe);c=oe.dot(pe);a=oe.dot(qe);b=pe.dot(pe);var f=pe.dot(qe),g=d*b-c*c;e===void 0&&(e=new P());if(g===0)return e.set(-2,-1,-1);g=1/g;b=(b*a-c*f)*g;d=(d*f-c*a)*g;return e.set(1-b-d,d,b)},containsPoint:function(a,b,c,d){ye.getBarycoord(a,b,c,d,re);return re.x>=0&&re.y>=0&&re.x+re.y<=1},getUV:function(a,b,c,d,e,f,g,h){this.getBarycoord(a,b,c,d,re);h.set(0,0);h.addScaledVector(e,re.x);h.addScaledVector(f,re.y);h.addScaledVector(g,re.z);return h},isFrontFacing:function(a,b,c,d){oe.subVectors(c,b);pe.subVectors(a,b);return oe.cross(pe).dot(d)<0?!0:!1}});Object.assign(ye.prototype,{set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},getArea:function(){oe.subVectors(this.c,this.b);pe.subVectors(this.a,this.b);return oe.cross(pe).length()*.5},getMidpoint:function(a){a===void 0&&(a=new P());return a.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},getNormal:function(a){return ye.getNormal(this.a,this.b,this.c,a)},getPlane:function(a){a===void 0&&(a=new ne());return a.setFromCoplanarPoints(this.a,this.b,this.c)},getBarycoord:function(a,b){return ye.getBarycoord(a,this.a,this.b,this.c,b)},getUV:function(a,b,c,d,e){return ye.getUV(a,this.a,this.b,this.c,b,c,d,e)},containsPoint:function(a){return ye.containsPoint(a,this.a,this.b,this.c)},isFrontFacing:function(a){return ye.isFrontFacing(this.a,this.b,this.c,a)},intersectsBox:function(a){return a.intersectsTriangle(this)},closestPointToPoint:function(a,b){b===void 0&&(b=new P());var c=this.a,d=this.b,e=this.c,f;se.subVectors(d,c);te.subVectors(e,c);ve.subVectors(a,c);var g=se.dot(ve),h=te.dot(ve);if(g<=0&&h<=0)return b.copy(c);we.subVectors(a,d);var i=se.dot(we),j=te.dot(we);if(i>=0&&j<=i)return b.copy(d);var k=g*j-i*h;if(k<=0&&g>=0&&i<=0){f=g/(g-i);return b.copy(c).addScaledVector(se,f)}xe.subVectors(a,e);a=se.dot(xe);var l=te.dot(xe);if(l>=0&&a<=l)return b.copy(e);g=a*h-g*l;if(g<=0&&h>=0&&l<=0){h=h/(h-l);return b.copy(c).addScaledVector(te,h)}var m=i*l-a*j;if(m<=0&&j-i>=0&&a-l>=0){ue.subVectors(e,d);h=(j-i)/(j-i+(a-l));return b.copy(d).addScaledVector(ue,h)}e=1/(m+g+k);f=g*e;h=k*e;return b.copy(c).addScaledVector(se,f).addScaledVector(te,h)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)}});var ze={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},Ae={h:0,s:0,l:0},Be={h:0,s:0,l:0};function S(a,b,c){return b===void 0&&c===void 0?this.set(a):this.setRGB(a,b,c)}function Ce(a,b,c){c<0&&(c+=1);c>1&&(c-=1);if(c<1/6)return a+(b-a)*6*c;if(c<1/2)return b;return c<2/3?a+(b-a)*6*(2/3-c):a}function De(a){return a<.04045?a*.0773993808:Math.pow(a*.9478672986+.0521327014,2.4)}function Ee(a){return a<.0031308?a*12.92:1.055*Math.pow(a,.41666)-.055}Object.assign(S.prototype,{isColor:!0,r:1,g:1,b:1,set:function(a){a&&a.isColor?this.copy(a):typeof a==="number"?this.setHex(a):typeof a==="string"&&this.setStyle(a);return this},setScalar:function(a){this.r=a;this.g=a;this.b=a;return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){a=N.euclideanModulo(a,1);b=N.clamp(b,0,1);c=N.clamp(c,0,1);if(b===0)this.r=this.g=this.b=c;else{b=c<=.5?c*(1+b):c+b-c*b;c=2*c-b;this.r=Ce(c,b,a+1/3);this.g=Ce(c,b,a);this.b=Ce(c,b,a-1/3)}return this},setStyle:function(a){function b(a){if(a===void 0)return;parseFloat(a)<1}var c;if(c=/^((?:rgb|hsl)a?)(s*([^)]*))/.exec(a)){var d=c[1],e=c[2];switch(d){case"rgb":case"rgba":if(d=/^(d+)s*,s*(d+)s*,s*(d+)s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){this.r=Math.min(255,parseInt(d[1],10))/255;this.g=Math.min(255,parseInt(d[2],10))/255;this.b=Math.min(255,parseInt(d[3],10))/255;b(d[5]);return this}if(d=/^(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){this.r=Math.min(100,parseInt(d[1],10))/100;this.g=Math.min(100,parseInt(d[2],10))/100;this.b=Math.min(100,parseInt(d[3],10))/100;b(d[5]);return this}break;case"hsl":case"hsla":if(d=/^([0-9]*.?[0-9]+)s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){e=parseFloat(d[1])/360;var f=parseInt(d[2],10)/100,g=parseInt(d[3],10)/100;b(d[5]);return this.setHSL(e,f,g)}break}}else if(c=/^#([A-Fa-f0-9]+)$/.exec(a)){b=c[1];d=b.length;if(d===3){this.r=parseInt(b.charAt(0)+b.charAt(0),16)/255;this.g=parseInt(b.charAt(1)+b.charAt(1),16)/255;this.b=parseInt(b.charAt(2)+b.charAt(2),16)/255;return this}else if(d===6){this.r=parseInt(b.charAt(0)+b.charAt(1),16)/255;this.g=parseInt(b.charAt(2)+b.charAt(3),16)/255;this.b=parseInt(b.charAt(4)+b.charAt(5),16)/255;return this}}return a&&a.length>0?this.setColorName(a):this},setColorName:function(a){var b=ze[a];b!==void 0?this.setHex(b):console.warn("THREE.Color: Unknown color "+a);return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(a){this.r=a.r;this.g=a.g;this.b=a.b;return this},copyGammaToLinear:function(a,b){b===void 0&&(b=2);this.r=Math.pow(a.r,b);this.g=Math.pow(a.g,b);this.b=Math.pow(a.b,b);return this},copyLinearToGamma:function(a,b){b===void 0&&(b=2);b=b>0?1/b:1;this.r=Math.pow(a.r,b);this.g=Math.pow(a.g,b);this.b=Math.pow(a.b,b);return this},convertGammaToLinear:function(a){this.copyGammaToLinear(this,a);return this},convertLinearToGamma:function(a){this.copyLinearToGamma(this,a);return this},copySRGBToLinear:function(a){this.r=De(a.r);this.g=De(a.g);this.b=De(a.b);return this},copyLinearToSRGB:function(a){this.r=Ee(a.r);this.g=Ee(a.g);this.b=Ee(a.b);return this},convertSRGBToLinear:function(){this.copySRGBToLinear(this);return this},convertLinearToSRGB:function(){this.copyLinearToSRGB(this);return this},getHex:function(){return this.r*255<<16^this.g*255<<8^this.b*255<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a===void 0&&(a={h:0,s:0,l:0});var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h,i=(f+e)/2;if(f===e)g=0,h=0;else{var j=e-f;h=i<=.5?j/(e+f):j/(2-e-f);switch(e){case b:g=(c-d)/j+(c0&&(c.alphaTest=this.alphaTest);this.premultipliedAlpha===!0&&(c.premultipliedAlpha=this.premultipliedAlpha);this.wireframe===!0&&(c.wireframe=this.wireframe);this.wireframeLinewidth>1&&(c.wireframeLinewidth=this.wireframeLinewidth);this.wireframeLinecap!=="round"&&(c.wireframeLinecap=this.wireframeLinecap);this.wireframeLinejoin!=="round"&&(c.wireframeLinejoin=this.wireframeLinejoin);this.morphTargets===!0&&(c.morphTargets=!0);this.morphNormals===!0&&(c.morphNormals=!0);this.skinning===!0&&(c.skinning=!0);this.visible===!1&&(c.visible=!1);this.toneMapped===!1&&(c.toneMapped=!1);JSON.stringify(this.userData)!=="{}"&&(c.userData=this.userData);function d(b){var c=[];for(var d in b){var a=b[d];delete a.metadata;c.push(a)}return c}if(b){b=d(a.textures);d=d(a.images);b.length>0&&(c.textures=b);d.length>0&&(c.images=d)}return c},clone:function(){return new this.constructor().copy(this)},copy:function(b){this.name=b.name;this.fog=b.fog;this.blending=b.blending;this.side=b.side;this.flatShading=b.flatShading;this.vertexColors=b.vertexColors;this.opacity=b.opacity;this.transparent=b.transparent;this.blendSrc=b.blendSrc;this.blendDst=b.blendDst;this.blendEquation=b.blendEquation;this.blendSrcAlpha=b.blendSrcAlpha;this.blendDstAlpha=b.blendDstAlpha;this.blendEquationAlpha=b.blendEquationAlpha;this.depthFunc=b.depthFunc;this.depthTest=b.depthTest;this.depthWrite=b.depthWrite;this.stencilWriteMask=b.stencilWriteMask;this.stencilFunc=b.stencilFunc;this.stencilRef=b.stencilRef;this.stencilFuncMask=b.stencilFuncMask;this.stencilFail=b.stencilFail;this.stencilZFail=b.stencilZFail;this.stencilZPass=b.stencilZPass;this.stencilWrite=b.stencilWrite;var c=b.clippingPlanes,d=null;if(c!==null){var e=c.length;d=new Array(e);for(var a=0;a!==e;++a)d[a]=c[a].clone()}this.clippingPlanes=d;this.clipIntersection=b.clipIntersection;this.clipShadows=b.clipShadows;this.shadowSide=b.shadowSide;this.colorWrite=b.colorWrite;this.precision=b.precision;this.polygonOffset=b.polygonOffset;this.polygonOffsetFactor=b.polygonOffsetFactor;this.polygonOffsetUnits=b.polygonOffsetUnits;this.dithering=b.dithering;this.alphaTest=b.alphaTest;this.premultipliedAlpha=b.premultipliedAlpha;this.visible=b.visible;this.toneMapped=b.toneMapped;this.userData=JSON.parse(JSON.stringify(b.userData));return this},dispose:function(){this.dispatchEvent({type:"dispose"})}});Object.defineProperty(T.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});function He(a){T.call(this),this.type="MeshBasicMaterial",this.color=new S(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=la,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.skinning=!1,this.morphTargets=!1,this.setValues(a)}He.prototype=Object.create(T.prototype);He.prototype.constructor=He;He.prototype.isMeshBasicMaterial=!0;He.prototype.copy=function(a){T.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;return this};var Ie=new P();function U(a,b,c){if(Array.isArray(a))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.name="";this.array=a;this.itemSize=b;this.count=a!==void 0?a.length/b:0;this.normalized=c===!0;this.usage=Rc;this.updateRange={offset:0,count:-1};this.version=0}Object.defineProperty(U.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});Object.assign(U.prototype,{isBufferAttribute:!0,onUploadCallback:function(){},setUsage:function(a){this.usage=a;return this},copy:function(a){this.name=a.name;this.array=new a.array.constructor(a.array);this.itemSize=a.itemSize;this.count=a.count;this.normalized=a.normalized;this.usage=a.usage;return this},copyAt:function(b,c,d){b*=this.itemSize;d*=c.itemSize;for(var a=0,e=this.itemSize;a0,g=e[1]&&e[1].length>0,h=b.morphTargets,i=h.length,j;if(i>0){j=[];for(var a=0;a0){m=[];for(var a=0;a0&&c.length===0;for(var a=0;ac&&(c=b[a]);return c}var Te=1,Ue=new Q(),Ve=new R(),We=new P(),Xe=new Zd(),Ye=new Zd(),Ze=new P();function W(){Object.defineProperty(this,"id",{value:Te+=2}),this.uuid=N.generateUUID(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:Infinity},this.userData={}}W.prototype=Object.assign(Object.create($c.prototype),{constructor:W,isBufferGeometry:!0,getIndex:function(){return this.index},setIndex:function(a){Array.isArray(a)?this.index=new(Se(a)>65535?Pe:Ne)(a,1):this.index=a},getAttribute:function(a){return this.attributes[a]},setAttribute:function(a,b){this.attributes[a]=b;return this},deleteAttribute:function(a){delete this.attributes[a];return this},addGroup:function(a,b,c){this.groups.push({start:a,count:b,materialIndex:c!==void 0?c:0})},clearGroups:function(){this.groups=[]},setDrawRange:function(a,b){this.drawRange.start=a,this.drawRange.count=b},applyMatrix4:function(a){var b=this.attributes.position;b!==void 0&&(b.applyMatrix4(a),b.needsUpdate=!0);b=this.attributes.normal;if(b!==void 0){var c=new cd().getNormalMatrix(a);b.applyNormalMatrix(c);b.needsUpdate=!0}c=this.attributes.tangent;c!==void 0&&(c.transformDirection(a),c.needsUpdate=!0);this.boundingBox!==null&&this.computeBoundingBox();this.boundingSphere!==null&&this.computeBoundingSphere();return this},rotateX:function(a){Ue.makeRotationX(a);this.applyMatrix4(Ue);return this},rotateY:function(a){Ue.makeRotationY(a);this.applyMatrix4(Ue);return this},rotateZ:function(a){Ue.makeRotationZ(a);this.applyMatrix4(Ue);return this},translate:function(a,b,c){Ue.makeTranslation(a,b,c);this.applyMatrix4(Ue);return this},scale:function(a,b,c){Ue.makeScale(a,b,c);this.applyMatrix4(Ue);return this},lookAt:function(a){Ve.lookAt(a);Ve.updateMatrix();this.applyMatrix4(Ve.matrix);return this},center:function(){this.computeBoundingBox();this.boundingBox.getCenter(We).negate();this.translate(We.x,We.y,We.z);return this},setFromObject:function(a){var b=a.geometry;if(a.isPoints||a.isLine){var c=new V(b.vertices.length*3,3),d=new V(b.colors.length*3,3);this.setAttribute("position",c.copyVector3sArray(b.vertices));this.setAttribute("color",d.copyColorsArray(b.colors));if(b.lineDistances&&b.lineDistances.length===b.vertices.length){c=new V(b.lineDistances.length,1);this.setAttribute("lineDistance",c.copyArray(b.lineDistances))}b.boundingSphere!==null&&(this.boundingSphere=b.boundingSphere.clone());b.boundingBox!==null&&(this.boundingBox=b.boundingBox.clone())}else a.isMesh&&(b&&b.isGeometry&&this.fromGeometry(b));return this},setFromPoints:function(b){var c=[];for(var a=0,d=b.length;a0){c=new Float32Array(b.normals.length*3);this.setAttribute("normal",new U(c,3).copyVector3sArray(b.normals))}if(b.colors.length>0){c=new Float32Array(b.colors.length*3);this.setAttribute("color",new U(c,3).copyColorsArray(b.colors))}if(b.uvs.length>0){c=new Float32Array(b.uvs.length*2);this.setAttribute("uv",new U(c,2).copyVector2sArray(b.uvs))}if(b.uvs2.length>0){c=new Float32Array(b.uvs2.length*2);this.setAttribute("uv2",new U(c,2).copyVector2sArray(b.uvs2))}this.groups=b.groups;for(c in b.morphTargets){var d=[],e=b.morphTargets[c];for(var a=0,f=e.length;a0){h=new V(b.skinIndices.length*4,4);this.setAttribute("skinIndex",h.copyVector4sArray(b.skinIndices))}if(b.skinWeights.length>0){g=new V(b.skinWeights.length*4,4);this.setAttribute("skinWeight",g.copyVector4sArray(b.skinWeights))}b.boundingSphere!==null&&(this.boundingSphere=b.boundingSphere.clone());b.boundingBox!==null&&(this.boundingBox=b.boundingBox.clone());return this},computeBoundingBox:function(){this.boundingBox===null&&(this.boundingBox=new Zd());var a=this.attributes.position,b=this.morphAttributes.position;if(a!==void 0){this.boundingBox.setFromBufferAttribute(a);if(b)for(var a=0,c=b.length;a0&&(b.userData=this.userData);if(this.parameters!==void 0){var c=this.parameters;for(var d in c)c[d]!==void 0&&(b[d]=c[d]);return b}b.data={attributes:{}};c=this.index;c!==null&&(b.data.index={type:c.array.constructor.name,array:Array.prototype.slice.call(c.array)});c=this.attributes;for(var d in c){var e=c[d],f=e.toJSON();e.name!==""&&(f.name=e.name);b.data.attributes[d]=f}c={};var g=!1;for(var d in this.morphAttributes){var h=this.morphAttributes[d],i=[];for(var a=0,j=h.length;a0&&(c[d]=i,g=!0)}g&&(b.data.morphAttributes=c,b.data.morphTargetsRelative=this.morphTargetsRelative);f=this.groups;f.length>0&&(b.data.groups=JSON.parse(JSON.stringify(f)));h=this.boundingSphere;h!==null&&(b.data.boundingSphere={center:h.center.toArray(),radius:h.radius});return b},clone:function(){return new W().copy(this)},copy:function(b){var c,a,d;this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingBox=null;this.boundingSphere=null;this.name=b.name;var e=b.index;e!==null&&this.setIndex(e.clone());e=b.attributes;for(c in e){var f=e[c];this.setAttribute(c,f.clone())}f=b.morphAttributes;for(c in f){e=[];var g=f[c];for(a=0,d=g.length;a0){d=d[e[0]];if(d!==void 0){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(e=0,b=d.length;e0}},raycast:function(c,b){var d=this.geometry,e=this.material,f=this.matrixWorld;if(e===void 0)return;d.boundingSphere===null&&d.computeBoundingSphere();bf.copy(d.boundingSphere);bf.applyMatrix4(f);if(c.ray.intersectsSphere(bf)===!1)return;$e.getInverse(f);af.copy(c.ray).applyMatrix4($e);if(d.boundingBox!==null&&af.intersectsBox(d.boundingBox)===!1)return;var g;if(d.isBufferGeometry){var h,i,j;f=d.index;var k=d.attributes.position,l=d.morphAttributes.position,m=d.morphTargetsRelative,n=d.attributes.uv,o=d.attributes.uv2,p=d.groups,q=d.drawRange,a,r,s,t,u,v,w,x;if(f!==null)if(Array.isArray(e))for(a=0,s=p.length;a0&&(y=p);for(r=0,t=v.length;rc.far?null:{distance:d,point:qf.clone(),object:a}}function tf(b,c,d,e,f,g,h,i,j,k,l,m){cf.fromBufferAttribute(f,k);df.fromBufferAttribute(f,l);ef.fromBufferAttribute(f,m);f=b.morphTargetInfluences;if(c.morphTargets&&g&&f){jf.set(0,0,0);kf.set(0,0,0);lf.set(0,0,0);for(var a=0,n=g.length;a0)for(d=0;d0&&(this.normalsNeedUpdate=!0)},computeFlatVertexNormals:function(){var a,b,c;this.computeFaceNormals();for(a=0,b=this.faces.length;a0&&(this.normalsNeedUpdate=!0)},computeMorphNormals:function(){var a,b,c,d,e;for(c=0,d=this.faces.length;c=0;a--){b=e[a];this.faces.splice(b,1);for(f=0,d=this.faceVertexUvs.length;f0,p=k.vertexNormals.length>0,q=k.color.r!==1||k.color.g!==1||k.color.b!==1,r=k.vertexColors.length>0,s=0;s=t(s,0,0);s=t(s,1,l);s=t(s,2,m);s=t(s,3,n);s=t(s,4,o);s=t(s,5,p);s=t(s,6,q);s=t(s,7,r);d.push(s);d.push(k.a,k.b,k.c);d.push(k.materialIndex);if(n){l=this.faceVertexUvs[0][b];d.push(w(l[0]),w(l[1]),w(l[2]))}o&&d.push(u(k.normal));if(p){m=k.vertexNormals;d.push(u(m[0]),u(m[1]),u(m[2]))}q&&d.push(v(k.color));if(r){s=k.vertexColors;d.push(v(s[0]),v(s[1]),v(s[2]))}}function t(a,b,c){return c?a|1<0&&(a.data.colors=g);i.length>0&&(a.data.uvs=[i]);a.data.faces=d;return a},clone:function(){return new X().copy(this)},copy:function(b){var a,c,d,e,f,g;this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingBox=null;this.boundingSphere=null;this.name=b.name;var h=b.vertices;for(a=0,c=h.length;a0?1:-1;k.push(B.x,B.y,B.z);l.push(z/p);l.push(1-A/q);x+=1}}for(A=0;A0&&(b.defines=this.defines);b.vertexShader=this.vertexShader;b.fragmentShader=this.fragmentShader;d={};for(a in this.extensions)this.extensions[a]===!0&&(d[a]=!0);Object.keys(d).length>0&&(b.extensions=d);return b};function Ff(){R.call(this),this.type="Camera",this.matrixWorldInverse=new Q(),this.projectionMatrix=new Q(),this.projectionMatrixInverse=new Q()}Ff.prototype=Object.assign(Object.create(R.prototype),{constructor:Ff,isCamera:!0,copy:function(a,b){R.prototype.copy.call(this,a,b);this.matrixWorldInverse.copy(a.matrixWorldInverse);this.projectionMatrix.copy(a.projectionMatrix);this.projectionMatrixInverse.copy(a.projectionMatrixInverse);return this},getWorldDirection:function(a){a===void 0&&(a=new P());this.updateMatrixWorld(!0);var b=this.matrixWorld.elements;return a.set(-b[8],-b[9],-b[10]).normalize()},updateMatrixWorld:function(a){R.prototype.updateMatrixWorld.call(this,a),this.matrixWorldInverse.getInverse(this.matrixWorld)},updateWorldMatrix:function(a,b){R.prototype.updateWorldMatrix.call(this,a,b),this.matrixWorldInverse.getInverse(this.matrixWorld)},clone:function(){return new this.constructor().copy(this)}});function Gf(a,b,c,d){Ff.call(this),this.type="PerspectiveCamera",this.fov=a!==void 0?a:50,this.zoom=1,this.near=c!==void 0?c:.1,this.far=d!==void 0?d:2e3,this.focus=10,this.aspect=b!==void 0?b:1,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}Gf.prototype=Object.assign(Object.create(Ff.prototype),{constructor:Gf,isPerspectiveCamera:!0,copy:function(a,b){Ff.prototype.copy.call(this,a,b);this.fov=a.fov;this.zoom=a.zoom;this.near=a.near;this.far=a.far;this.focus=a.focus;this.aspect=a.aspect;this.view=a.view===null?null:Object.assign({},a.view);this.filmGauge=a.filmGauge;this.filmOffset=a.filmOffset;return this},setFocalLength:function(a){a=.5*this.getFilmHeight()/a;this.fov=N.RAD2DEG*2*Math.atan(a);this.updateProjectionMatrix()},getFocalLength:function(){var a=Math.tan(N.DEG2RAD*.5*this.fov);return.5*this.getFilmHeight()/a},getEffectiveFOV:function(){return N.RAD2DEG*2*Math.atan(Math.tan(N.DEG2RAD*.5*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(a,b,c,d,e,f){this.aspect=a/b,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=a,this.view.fullHeight=b,this.view.offsetX=c,this.view.offsetY=d,this.view.width=e,this.view.height=f,this.updateProjectionMatrix()},clearViewOffset:function(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()},updateProjectionMatrix:function(){var a=this.near,b=a*Math.tan(N.DEG2RAD*.5*this.fov)/this.zoom,c=2*b,d=this.aspect*c,e=-.5*d,f=this.view;if(this.view!==null&&this.view.enabled){var g=f.fullWidth,h=f.fullHeight;e+=f.offsetX*d/g;b-=f.offsetY*c/h;d*=f.width/g;c*=f.height/h}g=this.filmOffset;g!==0&&(e+=a*g/this.getFilmWidth());this.projectionMatrix.makePerspective(e,e+d,b,b-c,a,this.far);this.projectionMatrixInverse.getInverse(this.projectionMatrix)},toJSON:function(a){a=R.prototype.toJSON.call(this,a);a.object.fov=this.fov;a.object.zoom=this.zoom;a.object.near=this.near;a.object.far=this.far;a.object.focus=this.focus;a.object.aspect=this.aspect;this.view!==null&&(a.object.view=Object.assign({},this.view));a.object.filmGauge=this.filmGauge;a.object.filmOffset=this.filmOffset;return a}});var Hf=90,If=1;function Jf(a,b,c,d){R.call(this);this.type="CubeCamera";var e=new Gf(Hf,If,a,b);e.up.set(0,-1,0);e.lookAt(new P(1,0,0));this.add(e);var f=new Gf(Hf,If,a,b);f.up.set(0,-1,0);f.lookAt(new P(-1,0,0));this.add(f);var g=new Gf(Hf,If,a,b);g.up.set(0,0,1);g.lookAt(new P(0,1,0));this.add(g);var h=new Gf(Hf,If,a,b);h.up.set(0,0,-1);h.lookAt(new P(0,-1,0));this.add(h);var i=new Gf(Hf,If,a,b);i.up.set(0,-1,0);i.lookAt(new P(0,0,1));this.add(i);var j=new Gf(Hf,If,a,b);j.up.set(0,-1,0);j.lookAt(new P(0,0,-1));this.add(j);d=d||{format:bb,magFilter:Ka,minFilter:Ka};this.renderTarget=new Kf(c,d);this.renderTarget.texture.name="CubeCamera";this.update=function(a,b){this.parent===null&&this.updateMatrixWorld();var c=a.getRenderTarget(),d=this.renderTarget,k=d.texture.generateMipmaps;d.texture.generateMipmaps=!1;a.setRenderTarget(d,0);a.render(b,e);a.setRenderTarget(d,1);a.render(b,f);a.setRenderTarget(d,2);a.render(b,g);a.setRenderTarget(d,3);a.render(b,h);a.setRenderTarget(d,4);a.render(b,i);d.texture.generateMipmaps=k;a.setRenderTarget(d,5);a.render(b,j);a.setRenderTarget(c)};this.clear=function(b,c,d,e){var f=b.getRenderTarget(),g=this.renderTarget;for(var a=0;a<6;a++)b.setRenderTarget(g,a),b.clear(c,d,e);b.setRenderTarget(f)}}Jf.prototype=Object.create(R.prototype);Jf.prototype.constructor=Jf;function Kf(a,b,c){Number.isInteger(b)&&(b=c),id.call(this,a,a,b)}Kf.prototype=Object.create(id.prototype);Kf.prototype.constructor=Kf;Kf.prototype.isWebGLCubeRenderTarget=!0;Kf.prototype.fromEquirectangularTexture=function(a,b){this.texture.type=b.type;this.texture.format=b.format;this.texture.encoding=b.encoding;var c=new Ld(),d={uniforms:{tEquirect:{value:null}},vertexShader:["varying vec3 vWorldDirection;","vec3 transformDirection( in vec3 dir, in mat4 matrix ) {"," return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );","}","void main() {"," vWorldDirection = transformDirection( position, modelMatrix );"," #include "," #include ","}"].join(" "),fragmentShader:["uniform sampler2D tEquirect;","varying vec3 vWorldDirection;","#define RECIPROCAL_PI 0.31830988618","#define RECIPROCAL_PI2 0.15915494","void main() {"," vec3 direction = normalize( vWorldDirection );"," vec2 sampleUV;"," sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;"," sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;"," gl_FragColor = texture2D( tEquirect, sampleUV );","}"].join(" ")};d=new Ef({type:"CubemapFromEquirect",uniforms:zf(d.uniforms),vertexShader:d.vertexShader,fragmentShader:d.fragmentShader,side:q,blending:u});d.uniforms.tEquirect.value=b;b=new rf(new yf(5,5,5),d);c.add(b);d=new Jf(1,10,1);d.renderTarget=this;d.renderTarget.texture.name="CubeCameraTexture";d.update(a,c);b.geometry.dispose();b.material.dispose();return this};function Lf(a,b,c,d,e,f,g,h,i,j,k,l){gd.call(this,null,f,g,h,i,j,d,e,k,l),this.image={data:a||null,width:b||1,height:c||1},this.magFilter=i!==void 0?i:Fa,this.minFilter=j!==void 0?j:Fa,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.needsUpdate=!0}Lf.prototype=Object.create(gd.prototype);Lf.prototype.constructor=Lf;Lf.prototype.isDataTexture=!0;var Mf=new be(),Nf=new P();function Of(a,b,c,d,e,f){this.planes=[a!==void 0?a:new ne(),b!==void 0?b:new ne(),c!==void 0?c:new ne(),d!==void 0?d:new ne(),e!==void 0?e:new ne(),f!==void 0?f:new ne()]}Object.assign(Of.prototype,{set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},clone:function(){return new this.constructor().copy(this)},copy:function(b){var c=this.planes;for(var a=0;a<6;a++)c[a].copy(b.planes[a]);return this},setFromProjectionMatrix:function(a){var b=this.planes;a=a.elements;var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],m=a[10],n=a[11],o=a[12],p=a[13],q=a[14];a=a[15];b[0].setComponents(f-c,j-g,n-k,a-o).normalize();b[1].setComponents(f+c,j+g,n+k,a+o).normalize();b[2].setComponents(f+d,j+h,n+l,a+p).normalize();b[3].setComponents(f-d,j-h,n-l,a-p).normalize();b[4].setComponents(f-e,j-i,n-m,a-q).normalize();b[5].setComponents(f+e,j+i,n+m,a+q).normalize();return this},intersectsObject:function(a){var b=a.geometry;b.boundingSphere===null&&b.computeBoundingSphere();Mf.copy(b.boundingSphere).applyMatrix4(a.matrixWorld);return this.intersectsSphere(Mf)},intersectsSprite:function(a){Mf.center.set(0,0,0);Mf.radius=.7071067811865476;Mf.applyMatrix4(a.matrixWorld);return this.intersectsSphere(Mf)},intersectsSphere:function(b){var c=this.planes,d=b.center;b=-b.radius;for(var a=0;a<6;a++){var e=c[a].distanceToPoint(d);if(e0?b.max.x:b.min.x;Nf.y=d.normal.y>0?b.max.y:b.min.y;Nf.z=d.normal.z>0?b.max.z:b.min.z;if(d.distanceToPoint(Nf)<0)return!1}return!0},containsPoint:function(b){var c=this.planes;for(var a=0;a<6;a++)if(c[a].distanceToPoint(b)<0)return!1;return!0}});var Y={common:{diffuse:{value:new S(15658734)},opacity:{value:1},map:{value:null},uvTransform:{value:new cd()},uv2Transform:{value:new cd()},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 O(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 S(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{}}},spotShadowMap:{value:[]},spotShadowMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}}},points:{diffuse:{value:new S(15658734)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},uvTransform:{value:new cd()}},sprite:{diffuse:{value:new S(15658734)},opacity:{value:1},center:{value:new O(.5,.5)},rotation:{value:0},map:{value:null},alphaMap:{value:null},uvTransform:{value:new cd()}}};function Pf(){var a=null,b=!1,c=null;function d(e,f){if(b===!1)return;c(e,f);a.requestAnimationFrame(d)}return{start:function(){if(b===!0)return;if(c===null)return;a.requestAnimationFrame(d);b=!0},stop:function(){b=!1},setAnimationLoop:function(a){c=a},setContext:function(b){a=b}}}function Qf(a,b){var c=b.isWebGL2,d=new WeakMap();function e(b,c){var d=b.array,e=b.usage,f=a.createBuffer();a.bindBuffer(c,f);a.bufferData(c,d,e);b.onUploadCallback();c=5126;d instanceof Float32Array?c=5126:d instanceof Float64Array||(d instanceof Uint16Array?c=5123:d instanceof Int16Array?c=5122:d instanceof Uint32Array?c=5125:d instanceof Int32Array?c=5124:d instanceof Int8Array?c=5120:d instanceof Uint8Array&&(c=5121));return{buffer:f,type:c,bytesPerElement:d.BYTES_PER_ELEMENT,version:b.version}}function f(b,d,e){var f=d.array;d=d.updateRange;a.bindBuffer(e,b);d.count===-1?a.bufferSubData(e,0,f):(c?a.bufferSubData(e,d.offset*f.BYTES_PER_ELEMENT,f,d.offset,d.count):a.bufferSubData(e,d.offset*f.BYTES_PER_ELEMENT,f.subarray(d.offset,d.offset+d.count)),d.count=-1)}function g(a){a.isInterleavedBufferAttribute&&(a=a.data);return d.get(a)}function h(b){b.isInterleavedBufferAttribute&&(b=b.data);var c=d.get(b);c&&(a.deleteBuffer(c.buffer),d["delete"](b))}function i(a,b){a.isInterleavedBufferAttribute&&(a=a.data);var c=d.get(a);c===void 0?d.set(a,e(a,b)):c.version0&&a.getShaderPrecisionFormat(35632,36338).precision>0)return"highp";b="mediump"}return b==="mediump"&&(a.getShaderPrecisionFormat(35633,36337).precision>0&&a.getShaderPrecisionFormat(35632,36337).precision>0)?"mediump":"lowp"}var g=typeof WebGL2RenderingContext!=="undefined"&&a instanceof WebGL2RenderingContext||typeof WebGL2ComputeRenderingContext!=="undefined"&&a instanceof WebGL2ComputeRenderingContext,h=c.precision!==void 0?c.precision:"highp",i=f(h);i!==h&&(h=i);i=c.logarithmicDepthBuffer===!0;c=a.getParameter(34930);var j=a.getParameter(35660),k=a.getParameter(3379),l=a.getParameter(34076),m=a.getParameter(34921),n=a.getParameter(36347),o=a.getParameter(36348),p=a.getParameter(36349),q=j>0,r=g||!!b.get("OES_texture_float"),s=q&&r,t=g?a.getParameter(36183):0;return{isWebGL2:g,getMaxAnisotropy:e,getMaxPrecision:f,precision:h,logarithmicDepthBuffer:i,maxTextures:c,maxVertexTextures:j,maxTextureSize:k,maxCubemapSize:l,maxAttributes:m,maxVertexUniforms:n,maxVaryings:o,maxFragmentUniforms:p,vertexTextures:q,floatFragmentTextures:r,floatVertexTextures:s,maxSamples:t}}function ri(){var a=this,b=null,c=0,d=!1,e=!1,f=new ne(),g=new cd(),h={value:null,needsUpdate:!1};this.uniform=h;this.numPlanes=0;this.numIntersection=0;this.init=function(a,e,f){var g=a.length!==0||e||c!==0||d;d=e;b=j(a,f,0);c=a.length;return g};this.beginShadows=function(){e=!0,j(null)};this.endShadows=function(){e=!1,i()};this.setState=function(a,f,g,k,l,m){if(!d||a===null||a.length===0||e&&!g)e?j(null):i();else{g=e?0:c;var n=g*4,o=l.clippingState||null;h.value=o;o=j(a,k,n,m);for(a=0;a!==n;++a)o[a]=b[a];l.clippingState=o;this.numIntersection=f?this.numPlanes:0;this.numPlanes+=g}};function i(){h.value!==b&&(h.value=b,h.needsUpdate=c>0),a.numPlanes=c,a.numIntersection=0}function j(b,c,d,e){var i=b!==null?b.length:0,j=null;if(i!==0){j=h.value;if(e!==!0||j===null){e=d+i*4;c=c.matrixWorldInverse;g.getNormalMatrix(c);(j===null||j.length65535?Pe:Ne)(c,1);f.version=g;b.update(f,34963);h=e.get(a);h&&b.remove(h);e.set(a,f)}function i(a){var b=e.get(a);if(b){var c=a.index;c!==null&&(b.version0)return a;var e=b*c,f=Gi[e];f===void 0&&(f=new Float32Array(e),Gi[e]=f);if(b!==0){d.toArray(f,0);for(e=1,d=0;e!==b;++e)d+=c,a[e].toArray(f,d)}return f}function Mi(b,c){if(b.length!==c.length)return!1;for(var a=0,d=b.length;a/gm;function Pj(a){return a.replace(Oj,Qj)}function Qj(a,b){a=Z[b];if(a===void 0)throw new Error("Can not resolve #include <"+b+">");return Pj(a)}var Rj=/#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g,Sj=/#pragma unroll_loop_start[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}[s]+?#pragma unroll_loop_end/g;function Tj(a){return a.replace(Sj,Vj).replace(Rj,Uj)}function Uj(a,b,c,d){return Vj(a,b,c,d)}function Vj(a,b,c,d){a="";for(b=parseInt(b);b0?a.gammaFactor:1,m=c.isWebGL2?"":Ij(c);e=Jj(e);var n=d.createProgram(),o,p;c.isRawShaderMaterial?(o=[e].filter(Lj).join(" "),o.length>0&&(o+=" "),p=[m,e].filter(Lj).join(" "),p.length>0&&(p+=" ")):(o=[Wj(c),"#define SHADER_NAME "+c.shaderName,e,c.instancing?"#define USE_INSTANCING":"",c.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+l,"#define MAX_BONES "+c.maxBones,c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp2?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.envMap?"#define "+j:"",c.lightMap?"#define USE_LIGHTMAP":"",c.aoMap?"#define USE_AOMAP":"",c.emissiveMap?"#define USE_EMISSIVEMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.normalMap&&c.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",c.normalMap&&c.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",c.clearcoatMap?"#define USE_CLEARCOATMAP":"",c.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",c.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",c.displacementMap&&c.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.roughnessMap?"#define USE_ROUGHNESSMAP":"",c.metalnessMap?"#define USE_METALNESSMAP":"",c.alphaMap?"#define USE_ALPHAMAP":"",c.vertexTangents?"#define USE_TANGENT":"",c.vertexColors?"#define USE_COLOR":"",c.vertexUvs?"#define USE_UV":"",c.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",c.flatShading?"#define FLAT_SHADED":"",c.skinning?"#define USE_SKINNING":"",c.useVertexTexture?"#define BONE_TEXTURE":"",c.morphTargets?"#define USE_MORPHTARGETS":"",c.morphNormals&&c.flatShading===!1?"#define USE_MORPHNORMALS":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+h:"",c.sizeAttenuation?"#define USE_SIZEATTENUATION":"",c.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",c.logarithmicDepthBuffer&&c.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"," attribute mat4 instanceMatrix;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#ifdef USE_COLOR"," attribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS"," attribute vec3 morphTarget0;"," attribute vec3 morphTarget1;"," attribute vec3 morphTarget2;"," attribute vec3 morphTarget3;"," #ifdef USE_MORPHNORMALS"," attribute vec3 morphNormal0;"," attribute vec3 morphNormal1;"," attribute vec3 morphNormal2;"," attribute vec3 morphNormal3;"," #else"," attribute vec3 morphTarget4;"," attribute vec3 morphTarget5;"," attribute vec3 morphTarget6;"," attribute vec3 morphTarget7;"," #endif","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif"," "].filter(Lj).join(" "),p=[m,Wj(c),"#define SHADER_NAME "+c.shaderName,e,c.alphaTest?"#define ALPHATEST "+c.alphaTest+(c.alphaTest%1?"":".0"):"","#define GAMMA_FACTOR "+l,c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp2?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.matcap?"#define USE_MATCAP":"",c.envMap?"#define USE_ENVMAP":"",c.envMap?"#define "+i:"",c.envMap?"#define "+j:"",c.envMap?"#define "+k:"",c.lightMap?"#define USE_LIGHTMAP":"",c.aoMap?"#define USE_AOMAP":"",c.emissiveMap?"#define USE_EMISSIVEMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.normalMap&&c.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",c.normalMap&&c.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",c.clearcoatMap?"#define USE_CLEARCOATMAP":"",c.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",c.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.roughnessMap?"#define USE_ROUGHNESSMAP":"",c.metalnessMap?"#define USE_METALNESSMAP":"",c.alphaMap?"#define USE_ALPHAMAP":"",c.sheen?"#define USE_SHEEN":"",c.vertexTangents?"#define USE_TANGENT":"",c.vertexColors?"#define USE_COLOR":"",c.vertexUvs?"#define USE_UV":"",c.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",c.gradientMap?"#define USE_GRADIENTMAP":"",c.flatShading?"#define FLAT_SHADED":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+h:"",c.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",c.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",c.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",c.logarithmicDepthBuffer&&c.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"",(c.extensionShaderTextureLOD||c.envMap)&&c.rendererExtensionShaderTextureLod?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",c.toneMapping!==oa?"#define TONE_MAPPING":"",c.toneMapping!==oa?Z.tonemapping_pars_fragment:"",c.toneMapping!==oa?Hj("toneMapping",c.toneMapping):"",c.dithering?"#define DITHERING":"",c.outputEncoding||c.mapEncoding||c.matcapEncoding||c.envMapEncoding||c.emissiveMapEncoding||c.lightMapEncoding?Z.encodings_pars_fragment:"",c.mapEncoding?Fj("mapTexelToLinear",c.mapEncoding):"",c.matcapEncoding?Fj("matcapTexelToLinear",c.matcapEncoding):"",c.envMapEncoding?Fj("envMapTexelToLinear",c.envMapEncoding):"",c.emissiveMapEncoding?Fj("emissiveMapTexelToLinear",c.emissiveMapEncoding):"",c.lightMapEncoding?Fj("lightMapTexelToLinear",c.lightMapEncoding):"",c.outputEncoding?Gj("linearToOutputTexel",c.outputEncoding):"",c.depthPacking?"#define DEPTH_PACKING "+c.depthPacking:""," "].filter(Lj).join(" "));f=Pj(f);f=Mj(f,c);f=Nj(f,c);g=Pj(g);g=Mj(g,c);g=Nj(g,c);f=Tj(f);g=Tj(g);if(c.isWebGL2&&!c.isRawShaderMaterial){m=!1;e=/^s*#versions+300s+ess* /;c.isShaderMaterial&&f.match(e)!==null&&g.match(e)!==null&&(m=!0,f=f.replace(e,""),g=g.replace(e,""));o=["#version 300 es ","#define attribute in","#define varying out","#define texture2D texture"].join(" ")+" "+o;p=["#version 300 es ","#define varying in",m?"":"out highp vec4 pc_fragColor;",m?"":"#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(" ")+" "+p}l=o+f;i=p+g;j=Aj(d,35633,l);k=Aj(d,35632,i);d.attachShader(n,j);d.attachShader(n,k);c.index0AttributeName!==void 0?d.bindAttribLocation(n,0,c.index0AttributeName):c.morphTargets===!0&&d.bindAttribLocation(n,0,"position");d.linkProgram(n);if(a.debug.checkShaderErrors){h=d.getProgramInfoLog(n).trim();e=d.getShaderInfoLog(j).trim();m=d.getShaderInfoLog(k).trim();f=!0;g=!0;if(d.getProgramParameter(n,35714)===!1){f=!1;l=Ej(d,j,"vertex");i=Ej(d,k,"fragment")}else h!==""||(e===""||m==="")&&(g=!1);g&&(this.diagnostics={runnable:f,programLog:h,vertexShader:{log:e,prefix:o},fragmentShader:{log:m,prefix:p}})}d.deleteShader(j);d.deleteShader(k);var q;this.getUniforms=function(){q===void 0&&(q=new zj(d,n));return q};var r;this.getAttributes=function(){r===void 0&&(r=Kj(d,n));return r};this.destroy=function(){d.deleteProgram(n),this.program=void 0};this.name=c.shaderName;this.id=Bj++;this.cacheKey=b;this.usedTimes=1;this.program=n;this.vertexShader=j;this.fragmentShader=k;return this}function bk(a,b,c){var d=[],e=c.isWebGL2,f=c.logarithmicDepthBuffer,g=c.floatVertexTextures,h=c.precision,i=c.maxVertexUniforms,j=c.vertexTextures,k={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"},l=["precision","isWebGL2","supportsVertexTextures","outputEncoding","instancing","map","mapEncoding","matcap","matcapEncoding","envMap","envMapMode","envMapEncoding","envMapCubeUV","lightMap","lightMapEncoding","aoMap","emissiveMap","emissiveMapEncoding","bumpMap","normalMap","objectSpaceNormalMap","tangentSpaceNormalMap","clearcoatMap","clearcoatRoughnessMap","clearcoatNormalMap","displacementMap","specularMap","roughnessMap","metalnessMap","gradientMap","alphaMap","combine","vertexColors","vertexTangents","vertexUvs","uvsVertexOnly","fog","useFog","fogExp2","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","maxMorphTargets","maxMorphNormals","premultipliedAlpha","numDirLights","numPointLights","numSpotLights","numHemiLights","numRectAreaLights","numDirLightShadows","numPointLightShadows","numSpotLightShadows","shadowMapEnabled","shadowMapType","toneMapping","physicallyCorrectLights","alphaTest","doubleSided","flipSided","numClippingPlanes","numClipIntersection","depthPacking","dithering","sheen"];function m(a,b){if(b){b=ni[b];b={name:a.type,uniforms:Bf.clone(b.uniforms),vertexShader:b.vertexShader,fragmentShader:b.fragmentShader}}else b={name:a.type,uniforms:a.uniforms,vertexShader:a.vertexShader,fragmentShader:a.fragmentShader};return b}function n(a){a=a.skeleton;a=a.bones;if(g)return 1024;else{var b=i;b=Math.floor((b-20)/4);b=Math.min(b,a.length);return b0,maxBones:x,useVertexTexture:g,morphTargets:d.morphTargets,morphNormals:d.morphNormals,maxMorphTargets:a.maxMorphTargets,maxMorphNormals:a.maxMorphNormals,numDirLights:i.directional.length,numPointLights:i.point.length,numSpotLights:i.spot.length,numRectAreaLights:i.rectArea.length,numHemiLights:i.hemi.length,numDirLightShadows:i.directionalShadowMap.length,numPointLightShadows:i.pointShadowMap.length,numSpotLightShadows:i.spotShadowMap.length,numClippingPlanes:s,numClipIntersection:t,dithering:d.dithering,shadowMapEnabled:a.shadowMap.enabled&&l.length>0,shadowMapType:a.shadowMap.type,toneMapping:d.toneMapped?a.toneMapping:oa,physicallyCorrectLights:a.physicallyCorrectLights,premultipliedAlpha:d.premultipliedAlpha,alphaTest:d.alphaTest,doubleSided:d.side===r,flipSided:d.side===q,depthPacking:d.depthPacking!==void 0?d.depthPacking:!1,index0AttributeName:d.index0AttributeName,extensionDerivatives:d.extensions&&d.extensions.derivatives,extensionFragDepth:d.extensions&&d.extensions.fragDepth,extensionDrawBuffers:d.extensions&&d.extensions.drawBuffers,extensionShaderTextureLOD:d.extensions&&d.extensions.shaderTextureLOD,rendererExtensionFragDepth:e||b.get("EXT_frag_depth")!==null,rendererExtensionDrawBuffers:e||b.get("WEBGL_draw_buffers")!==null,rendererExtensionShaderTextureLod:e||b.get("EXT_shader_texture_lod")!==null,onBeforeCompile:d.onBeforeCompile};return w};this.getProgramCacheKey=function(b){var c=[];b.shaderID?c.push(b.shaderID):(c.push(b.fragmentShader),c.push(b.vertexShader));if(b.defines!==void 0)for(var d in b.defines)c.push(d),c.push(b.defines[d]);if(b.isRawShaderMaterial===void 0){for(d=0;d1&&c.sort(a||dk),d.length>1&&d.sort(b||ek)}function k(){for(var c=b,d=a.length;cc||e.y>c)&&(e.x>c&&(f.x=Math.floor(c/s.x),e.x=f.x*s.x,r.mapSize.x=f.x),e.y>c&&(f.y=Math.floor(c/s.y),e.y=f.y*s.y,r.mapSize.y=f.y));if(r.map===null&&!r.isPointLightShadow&&this.type===o){s={minFilter:Ka,magFilter:Ka,format:cb};r.map=new id(e.x,e.y,s);r.map.texture.name=q.name+".shadowMap";r.mapPass=new id(e.x,e.y,s);r.camera.updateProjectionMatrix()}if(r.map===null){s={minFilter:Fa,magFilter:Fa,format:cb};r.map=new id(e.x,e.y,s);r.map.texture.name=q.name+".shadowMap";r.camera.updateProjectionMatrix()}a.setRenderTarget(r.map);a.clear();s=r.getViewportCount();for(var t=0;t0);c=!1;b.isSkinnedMesh===!0&&(d.skinning===!0&&(c=!0));b=b.isInstancedMesh===!0;i=l(n,c,b)}else i=m;if(a.localClippingEnabled&&d.clipShadows===!0&&d.clippingPlanes.length!==0){l=i.uuid;n=d.uuid;c=j[l];c===void 0&&(c={},j[l]=c);b=c[n];b===void 0&&(b=i.clone(),c[n]=b);i=b}i.visible=d.visible;i.wireframe=d.wireframe;h===o?i.side=d.shadowSide!==null?d.shadowSide:d.side:i.side=d.shadowSide!==null?d.shadowSide:k[d.side];i.clipShadows=d.clipShadows;i.clippingPlanes=d.clippingPlanes;i.clipIntersection=d.clipIntersection;i.wireframeLinewidth=d.wireframeLinewidth;i.linewidth=d.linewidth;e.isPointLight===!0&&i.isMeshDistanceMaterial===!0&&(i.referencePosition.setFromMatrixPosition(e.matrixWorld),i.nearDistance=f,i.farDistance=g);return i}function A(c,e,f,g,h){if(c.visible===!1)return;var i=c.layers.test(e.layers);if(i&&(c.isMesh||c.isLine||c.isPoints)&&((c.castShadow||c.receiveShadow&&h===o)&&(!c.frustumCulled||d.intersectsObject(c)))){c.modelViewMatrix.multiplyMatrices(f.matrixWorldInverse,c.matrixWorld);i=b.update(c);var j=c.material;if(Array.isArray(j)){var k=i.groups;for(var l=0,m=k.length;l=1):f.indexOf("OpenGL ES")!==-1&&(i=parseFloat(/^OpenGL ES ([0-9])/.exec(f)[1]),Aa=i>=2);var Ba=null,Ca={},Da=new hd(),Ea=new hd();function Fa(b,c,d){var e=new Uint8Array(4),f=a.createTexture();a.bindTexture(b,f);a.texParameteri(b,10241,9728);a.texParameteri(b,10240,9728);for(b=0;be||b.height>e)&&(f=e/Math.max(b.width,b.height));if(f<1||c===!0)if(typeof HTMLImageElement!=="undefined"&&b instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&b instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&b instanceof ImageBitmap){e=c?N.floorPowerOfTwo:Math.floor;c=e(f*b.width);e=e(f*b.height);a===void 0&&(a=p(c,e));f=d?p(c,e):a;f.width=c;f.height=e;d=f.getContext("2d");d.drawImage(b,0,0,c,e);return f}else{"data"in b;return b}return b}function r(a){return N.isPowerOfTwo(a.width)&&N.isPowerOfTwo(a.height)}function s(a){return i?!1:a.wrapS!==Da||a.wrapT!==Da||a.minFilter!==Fa&&a.minFilter!==Ka}function t(a,b){return a.generateMipmaps&&b&&a.minFilter!==Fa&&a.minFilter!==Ka}function u(a,c,d,f){b.generateMipmap(a);a=e.get(c);a.__maxMipLevel=Math.log(Math.max(d,f))*Math.LOG2E}function v(a,d,e){if(i===!1)return d;if(a!==null&&b[a]!==void 0)return b[a];a=d;d===6403&&(e===5126&&(a=33326),e===5131&&(a=33325),e===5121&&(a=33321));d===6407&&(e===5126&&(a=34837),e===5131&&(a=34843),e===5121&&(a=32849));d===6408&&(e===5126&&(a=34836),e===5131&&(a=34842),e===5121&&(a=32856));(a===33325||a===33326||a===34842||a===34836)&&c.get("EXT_color_buffer_float");return a}function w(a){return a===Fa||a===Ga||a===Ia?9728:9729}function x(a){a=a.target;a.removeEventListener("dispose",x);z(a);a.isVideoTexture&&n["delete"](a);h.memory.textures--}function y(a){a=a.target;a.removeEventListener("dispose",y);A(a);h.memory.textures--}function z(a){var c=e.get(a);if(c.__webglInit===void 0)return;b.deleteTexture(c.__webglTexture);e.remove(a)}function A(a){var c=e.get(a),d=e.get(a.texture);if(!a)return;d.__webglTexture!==void 0&&b.deleteTexture(d.__webglTexture);a.depthTexture&&a.depthTexture.dispose();if(a.isWebGLCubeRenderTarget)for(d=0;d<6;d++)b.deleteFramebuffer(c.__webglFramebuffer[d]),c.__webglDepthbuffer&&b.deleteRenderbuffer(c.__webglDepthbuffer[d]);else b.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&b.deleteRenderbuffer(c.__webglDepthbuffer),c.__webglMultisampledFramebuffer&&b.deleteFramebuffer(c.__webglMultisampledFramebuffer),c.__webglColorRenderbuffer&&b.deleteRenderbuffer(c.__webglColorRenderbuffer),c.__webglDepthRenderbuffer&&b.deleteRenderbuffer(c.__webglDepthRenderbuffer);e.remove(a.texture);e.remove(a)}var B=0;function C(){B=0}function D(){var a=B;a>=j;B+=1;return a}function E(a,b){var c=e.get(a);a.isVideoTexture&&M(a);if(a.version>0&&c.__version!==a.version){var f=a.image;if(!(f===void 0))if(!(f.complete===!1)){fa(c,a,b);return}}d.activeTexture(33984+b);d.bindTexture(3553,c.__webglTexture)}function F(a,b){var c=e.get(a);if(a.version>0&&c.__version!==a.version){fa(c,a,b);return}d.activeTexture(33984+b);d.bindTexture(35866,c.__webglTexture)}function aa(a,b){var c=e.get(a);if(a.version>0&&c.__version!==a.version){fa(c,a,b);return}d.activeTexture(33984+b);d.bindTexture(32879,c.__webglTexture)}function ba(c,f){if(c.image.length!==6)return;var h=e.get(c);if(c.version>0&&h.__version!==c.version){ea(h,c);d.activeTexture(33984+f);d.bindTexture(34067,h.__webglTexture);b.pixelStorei(37440,c.flipY);var j=c&&(c.isCompressedTexture||c.image[0].isCompressedTexture),l=c.image[0]&&c.image[0].isDataTexture,m=[];for(var a=0;a<6;a++)!j&&!l?m[a]=q(c.image[a],!1,!0,k):m[a]=l?c.image[a].image:c.image[a];var n=m[0],o=r(n)||i,p=g.convert(c.format),s=g.convert(c.type),w=v(c.internalFormat,p,s);H(34067,c,o);var x;if(j){for(var a=0;a<6;a++){x=m[a].mipmaps;for(j=0;j1||e.get(d).__currentAnisotropy)&&(b.texParameterf(a,g.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(d.anisotropy,f.getMaxAnisotropy())),e.get(d).__currentAnisotropy=d.anisotropy)}}function ea(a,c){a.__webglInit===void 0&&(a.__webglInit=!0,c.addEventListener("dispose",x),a.__webglTexture=b.createTexture(),h.memory.textures++)}function fa(c,e,f){var h=3553;e.isDataTexture2DArray&&(h=35866);e.isDataTexture3D&&(h=32879);ea(c,e);d.activeTexture(33984+f);d.bindTexture(h,c.__webglTexture);b.pixelStorei(37440,e.flipY);b.pixelStorei(37441,e.premultiplyAlpha);b.pixelStorei(3317,e.unpackAlignment);f=s(e)&&r(e.image)===!1;f=q(e.image,f,!1,l);var j=r(f)||i,k=g.convert(e.format),m=g.convert(e.type),n=v(e.internalFormat,k,m);H(h,e,j);var o,p=e.mipmaps;if(e.isDepthTexture)n=6402,i?e.type===Va?n=36012:e.type===Ua?n=33190:e.type===$a?n=35056:n=33189:e.type===Va,e.format===gb&&n===6402&&(e.type!==Sa&&e.type!==Ua&&(e.type=Sa,m=g.convert(e.type))),e.format===hb&&n===6402&&(n=34041,e.type!==$a&&(e.type=$a,m=g.convert(e.type))),d.texImage2D(3553,0,n,f.width,f.height,0,k,m,null);else if(e.isDataTexture)if(p.length>0&&j){for(var a=0,w=p.length;a0&&j){for(var a=0,w=p.length;a=0){var h=e[f];if(h!==void 0){var i=h.normalized,j=h.itemSize,k=la.get(h);if(k===void 0)continue;var l=k.buffer,m=k.type,n=k.bytesPerElement;if(h.isInterleavedBufferAttribute){var o=h.data,p=o.stride,q=h.offset;o&&o.isInstancedInterleavedBuffer?(L.enableAttributeAndDivisor(g,o.meshPerAttribute),b.maxInstancedCount===void 0&&(b.maxInstancedCount=o.meshPerAttribute*o.count)):L.enableAttribute(g);I.bindBuffer(34962,l);L.vertexAttribPointer(g,j,m,i,p*n,q*n)}else h.isInstancedBufferAttribute?(L.enableAttributeAndDivisor(g,h.meshPerAttribute),b.maxInstancedCount===void 0&&(b.maxInstancedCount=h.meshPerAttribute*h.count)):L.enableAttribute(g),I.bindBuffer(34962,l),L.vertexAttribPointer(g,j,m,i,0,0)}else if(f==="instanceMatrix"){var k=la.get(a.instanceMatrix);if(k===void 0)continue;var l=k.buffer,m=k.type;L.enableAttributeAndDivisor(g+0,1);L.enableAttributeAndDivisor(g+1,1);L.enableAttributeAndDivisor(g+2,1);L.enableAttributeAndDivisor(g+3,1);I.bindBuffer(34962,l);I.vertexAttribPointer(g+0,4,m,!1,64,0);I.vertexAttribPointer(g+1,4,m,!1,64,16);I.vertexAttribPointer(g+2,4,m,!1,64,32);I.vertexAttribPointer(g+3,4,m,!1,64,48)}else if(c!==void 0){o=c[f];if(o!==void 0)switch(o.length){case 2:I.vertexAttrib2fv(g,o);break;case 3:I.vertexAttrib3fv(g,o);break;case 4:I.vertexAttrib4fv(g,o);break;default:I.vertexAttrib1fv(g,o)}}}}L.disableUnusedAttributes()}this.compile=function(a,b){m=ra.get(a,b);m.init();a.traverse(function(a){a.isLight&&(m.pushLight(a),a.castShadow&&m.pushShadow(a))});m.setupLights(b);var c={};a.traverse(function(d){if(d.material)if(Array.isArray(d.material))for(var b=0;b=0&&a.numSupportedMorphTargets++}if(a.morphNormals){a.numSupportedMorphNormals=0;for(c=0;c=0&&a.numSupportedMorphNormals++}f=d.uniforms;(!a.isShaderMaterial&&!a.isRawShaderMaterial||a.clipping===!0)&&(d.numClippingPlanes=H.numPlanes,d.numIntersection=H.numIntersection,f.clippingPlanes=H.uniform);d.environment=a.isMeshStandardMaterial?b.environment:null;d.fog=b.fog;d.needsLights=jb(a);d.lightsStateVersion=g;d.needsLights&&(f.ambientLightColor.value=e.state.ambient,f.lightProbe.value=e.state.probe,f.directionalLights.value=e.state.directional,f.directionalLightShadows.value=e.state.directionalShadow,f.spotLights.value=e.state.spot,f.spotLightShadows.value=e.state.spotShadow,f.rectAreaLights.value=e.state.rectArea,f.pointLights.value=e.state.point,f.pointLightShadows.value=e.state.pointShadow,f.hemisphereLights.value=e.state.hemi,f.directionalShadowMap.value=e.state.directionalShadowMap,f.directionalShadowMatrix.value=e.state.directionalShadowMatrix,f.spotShadowMap.value=e.state.spotShadowMap,f.spotShadowMatrix.value=e.state.spotShadowMatrix,f.pointShadowMap.value=e.state.pointShadowMap,f.pointShadowMatrix.value=e.state.pointShadowMatrix);h=d.program.getUniforms();c=zj.seqWithValue(h.seq,f);d.uniformsList=c}function Qa(a,b,c,d){ka.resetTextureUnits();var e=b.fog,f=c.isMeshStandardMaterial?b.environment:null,g=t===null?n.outputEncoding:t.texture.encoding,h=M.get(c),i=m.state.lights;if(ea&&(fa||a!==x)){var j=a===x&&c.id===v;H.setState(c.clippingPlanes,c.clipIntersection,c.clipShadows,a,h,j)}c.version===h.__version?h.program===void 0?Oa(c,b,d):c.fog&&h.fog!==e?Oa(c,b,d):h.environment!==f?Oa(c,b,d):h.needsLights&&h.lightsStateVersion!==i.state.version?Oa(c,b,d):h.numClippingPlanes!==void 0&&(h.numClippingPlanes!==H.numPlanes||h.numIntersection!==H.numIntersection)?Oa(c,b,d):h.outputEncoding!==g&&Oa(c,b,d):(Oa(c,b,d),h.__version=c.version);j=!1;i=!1;g=!1;b=h.program;var k=b.getUniforms(),l=h.uniforms;L.useProgram(b.program)&&(j=!0,i=!0,g=!0);c.id!==v&&(v=c.id,i=!0);if(j||x!==a){k.setValue(I,"projectionMatrix",a.projectionMatrix);K.logarithmicDepthBuffer&&k.setValue(I,"logDepthBufFC",2/(Math.log(a.far+1)/Math.LN2));x!==a&&(x=a,i=!0,g=!0);if(c.isShaderMaterial||c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshStandardMaterial||c.envMap){j=k.map.cameraPosition;j!==void 0&&j.setValue(I,ha.setFromMatrixPosition(a.matrixWorld))}(c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshLambertMaterial||c.isMeshBasicMaterial||c.isMeshStandardMaterial||c.isShaderMaterial)&&k.setValue(I,"isOrthographic",a.isOrthographicCamera===!0);(c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshLambertMaterial||c.isMeshBasicMaterial||c.isMeshStandardMaterial||c.isShaderMaterial||c.skinning)&&k.setValue(I,"viewMatrix",a.matrixWorldInverse)}if(c.skinning){k.setOptional(I,d,"bindMatrix");k.setOptional(I,d,"bindMatrixInverse");j=d.skeleton;if(j){a=j.bones;if(K.floatVertexTextures){if(j.boneTexture===void 0){a=Math.sqrt(a.length*4);a=N.ceilPowerOfTwo(a);a=Math.max(a,4);var o=new Float32Array(a*a*4);o.set(j.boneMatrices);var p=new Lf(o,a,a,cb,Va);j.boneMatrices=o;j.boneTexture=p;j.boneTextureSize=a}k.setValue(I,"boneTexture",j.boneTexture,ka);k.setValue(I,"boneTextureSize",j.boneTextureSize)}else k.setOptional(I,j,"boneMatrices")}}(i||h.receiveShadow!==d.receiveShadow)&&(h.receiveShadow=d.receiveShadow,k.setValue(I,"receiveShadow",d.receiveShadow));i&&(k.setValue(I,"toneMappingExposure",n.toneMappingExposure),k.setValue(I,"toneMappingWhitePoint",n.toneMappingWhitePoint),h.needsLights&&ib(l,g),e&&c.fog&&Ya(l,e),c.isMeshBasicMaterial?Ra(l,c):c.isMeshLambertMaterial?(Ra(l,c),Za(l,c)):c.isMeshToonMaterial?(Ra(l,c),ab(l,c)):c.isMeshPhongMaterial?(Ra(l,c),$a(l,c)):c.isMeshStandardMaterial?(Ra(l,c,f),c.isMeshPhysicalMaterial?db(l,c,f):bb(l,c,f)):c.isMeshMatcapMaterial?(Ra(l,c),eb(l,c)):c.isMeshDepthMaterial?(Ra(l,c),fb(l,c)):c.isMeshDistanceMaterial?(Ra(l,c),gb(l,c)):c.isMeshNormalMaterial?(Ra(l,c),hb(l,c)):c.isLineBasicMaterial?(Sa(l,c),c.isLineDashedMaterial&&Ta(l,c)):c.isPointsMaterial?Ua(l,c):c.isSpriteMaterial?Xa(l,c):c.isShadowMaterial&&(l.color.value.copy(c.color),l.opacity.value=c.opacity),l.ltc_1!==void 0&&(l.ltc_1.value=Y.LTC_1),l.ltc_2!==void 0&&(l.ltc_2.value=Y.LTC_2),zj.upload(I,h.uniformsList,l,ka),c.isShaderMaterial&&(c.uniformsNeedUpdate=!1));c.isShaderMaterial&&c.uniformsNeedUpdate===!0&&(zj.upload(I,h.uniformsList,l,ka),c.uniformsNeedUpdate=!1);c.isSpriteMaterial&&k.setValue(I,"center",d.center);k.setValue(I,"modelViewMatrix",d.modelViewMatrix);k.setValue(I,"normalMatrix",d.normalMatrix);k.setValue(I,"modelMatrix",d.matrixWorld);return b}function Ra(a,b,c){a.opacity.value=b.opacity;b.color&&a.diffuse.value.copy(b.color);b.emissive&&a.emissive.value.copy(b.emissive).multiplyScalar(b.emissiveIntensity);b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);b.specularMap&&(a.specularMap.value=b.specularMap);c=b.envMap||c;c&&(a.envMap.value=c,a.flipEnvMap.value=c.isCubeTexture?-1:1,a.reflectivity.value=b.reflectivity,a.refractionRatio.value=b.refractionRatio,a.maxMipLevel.value=M.get(c).__maxMipLevel);b.lightMap&&(a.lightMap.value=b.lightMap,a.lightMapIntensity.value=b.lightMapIntensity);b.aoMap&&(a.aoMap.value=b.aoMap,a.aoMapIntensity.value=b.aoMapIntensity);var d;b.map?d=b.map:b.specularMap?d=b.specularMap:b.displacementMap?d=b.displacementMap:b.normalMap?d=b.normalMap:b.bumpMap?d=b.bumpMap:b.roughnessMap?d=b.roughnessMap:b.metalnessMap?d=b.metalnessMap:b.alphaMap?d=b.alphaMap:b.emissiveMap&&(d=b.emissiveMap);d!==void 0&&(d.isWebGLRenderTarget&&(d=d.texture),d.matrixAutoUpdate===!0&&d.updateMatrix(),a.uvTransform.value.copy(d.matrix));var e;b.aoMap?e=b.aoMap:b.lightMap&&(e=b.lightMap);e!==void 0&&(e.isWebGLRenderTarget&&(e=e.texture),e.matrixAutoUpdate===!0&&e.updateMatrix(),a.uv2Transform.value.copy(e.matrix))}function Sa(a,b){a.diffuse.value.copy(b.color),a.opacity.value=b.opacity}function Ta(a,b){a.dashSize.value=b.dashSize,a.totalSize.value=b.dashSize+b.gapSize,a.scale.value=b.scale}function Ua(a,b){a.diffuse.value.copy(b.color);a.opacity.value=b.opacity;a.size.value=b.size*E;a.scale.value=D*.5;b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);var c;b.map?c=b.map:b.alphaMap&&(c=b.alphaMap);c!==void 0&&(c.matrixAutoUpdate===!0&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function Xa(a,b){a.diffuse.value.copy(b.color);a.opacity.value=b.opacity;a.rotation.value=b.rotation;b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);var c;b.map?c=b.map:b.alphaMap&&(c=b.alphaMap);c!==void 0&&(c.matrixAutoUpdate===!0&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function Ya(a,b){a.fogColor.value.copy(b.color),b.isFog?(a.fogNear.value=b.near,a.fogFar.value=b.far):b.isFogExp2&&(a.fogDensity.value=b.density)}function Za(a,b){b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap)}function $a(a,b){a.specular.value.copy(b.specular),a.shininess.value=Math.max(b.shininess,1e-4),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function ab(a,b){a.specular.value.copy(b.specular),a.shininess.value=Math.max(b.shininess,1e-4),b.gradientMap&&(a.gradientMap.value=b.gradientMap),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function bb(a,b,c){a.roughness.value=b.roughness,a.metalness.value=b.metalness,b.roughnessMap&&(a.roughnessMap.value=b.roughnessMap),b.metalnessMap&&(a.metalnessMap.value=b.metalnessMap),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias),(b.envMap||c)&&(a.envMapIntensity.value=b.envMapIntensity)}function db(a,b,c){bb(a,b,c),a.reflectivity.value=b.reflectivity,a.clearcoat.value=b.clearcoat,a.clearcoatRoughness.value=b.clearcoatRoughness,b.sheen&&a.sheen.value.copy(b.sheen),b.clearcoatMap&&(a.clearcoatMap.value=b.clearcoatMap),b.clearcoatRoughnessMap&&(a.clearcoatRoughnessMap.value=b.clearcoatRoughnessMap),b.clearcoatNormalMap&&(a.clearcoatNormalScale.value.copy(b.clearcoatNormalScale),a.clearcoatNormalMap.value=b.clearcoatNormalMap,b.side===q&&a.clearcoatNormalScale.value.negate()),a.transparency.value=b.transparency}function eb(a,b){b.matcap&&(a.matcap.value=b.matcap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function fb(a,b){b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function gb(a,b){b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias),a.referencePosition.value.copy(b.referencePosition),a.nearDistance.value=b.nearDistance,a.farDistance.value=b.farDistance}function hb(a,b){b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function ib(a,b){a.ambientLightColor.needsUpdate=b,a.lightProbe.needsUpdate=b,a.directionalLights.needsUpdate=b,a.directionalLightShadows.needsUpdate=b,a.pointLights.needsUpdate=b,a.pointLightShadows.needsUpdate=b,a.spotLights.needsUpdate=b,a.spotLightShadows.needsUpdate=b,a.rectAreaLights.needsUpdate=b,a.hemisphereLights.needsUpdate=b}function jb(a){return a.isMeshLambertMaterial||a.isMeshToonMaterial||a.isMeshPhongMaterial||a.isMeshStandardMaterial||a.isShadowMaterial||a.isShaderMaterial&&a.lights===!0}this.setFramebuffer=function(a){p!==a&&t===null&&I.bindFramebuffer(36160,a),p=a};this.getActiveCubeFace=function(){return r};this.getActiveMipmapLevel=function(){return s};this.getRenderTarget=function(){return t};this.setRenderTarget=function(a,b,c){t=a;r=b;s=c;a&&M.get(a).__webglFramebuffer===void 0&&ka.setupRenderTarget(a);var d=p,e=!1;if(a){var f=M.get(a).__webglFramebuffer;a.isWebGLCubeRenderTarget?(d=f[b||0],e=!0):a.isWebGLMultisampleRenderTarget?d=M.get(a).__webglMultisampledFramebuffer:d=f;z.copy(a.viewport);A.copy(a.scissor);B=a.scissorTest}else z.copy(ba).multiplyScalar(E).floor(),A.copy(ca).multiplyScalar(E).floor(),B=da;u!==d&&(I.bindFramebuffer(36160,d),u=d);L.viewport(z);L.scissor(A);L.setScissorTest(B);if(e){f=M.get(a.texture);I.framebufferTexture2D(36160,36064,34069+(b||0),f.__webglTexture,c||0)}};this.readRenderTargetPixels=function(a,b,c,d,e,f,g){if(!(a&&a.isWebGLRenderTarget))return;var h=M.get(a).__webglFramebuffer;a.isWebGLCubeRenderTarget&&g!==void 0&&(h=h[g]);if(h){g=!1;h!==u&&(I.bindFramebuffer(36160,h),g=!0);try{h=a.texture;var i=h.format;h=h.type;if(i!==cb&&wa.convert(i)!==I.getParameter(35739))return;if(h!==Pa&&wa.convert(h)!==I.getParameter(35738)&&!(h===Va&&(K.isWebGL2||J.get("OES_texture_float")||J.get("WEBGL_color_buffer_float")))&&!(h===Wa&&(K.isWebGL2?J.get("EXT_color_buffer_float"):J.get("EXT_color_buffer_half_float"))))return;I.checkFramebufferStatus(36160)===36053&&(b>=0&&b<=a.width-d&&c>=0&&c<=a.height-e&&I.readPixels(b,c,d,e,wa.convert(i),wa.convert(h),f))}finally{g&&I.bindFramebuffer(36160,u)}}};this.copyFramebufferToTexture=function(a,b,c){c===void 0&&(c=0);var d=Math.pow(2,-c),e=Math.floor(b.image.width*d);d=Math.floor(b.image.height*d);var f=wa.convert(b.format);ka.setTexture2D(b,0);I.copyTexImage2D(3553,c,f,a.x,a.y,e,d,0);L.unbindTexture()};this.copyTextureToTexture=function(a,b,c,d){d===void 0&&(d=0);var e=b.image.width,f=b.image.height,g=wa.convert(c.format),h=wa.convert(c.type);ka.setTexture2D(c,0);b.isDataTexture?I.texSubImage2D(3553,d,a.x,a.y,e,f,g,h,b.image.data):b.isCompressedTexture?I.compressedTexSubImage2D(3553,d,a.x,a.y,b.mipmaps[0].width,b.mipmaps[0].height,g,b.mipmaps[0].data):I.texSubImage2D(3553,d,a.x,a.y,g,h,b.image);d===0&&c.generateMipmaps&&I.generateMipmap(3553);L.unbindTexture()};this.initTexture=function(a){ka.setTexture2D(a,0),L.unbindTexture()};typeof __THREE_DEVTOOLS__!=="undefined"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}function Bk(a,b){this.name="",this.color=new S(a),this.density=b!==void 0?b:25e-5}Object.assign(Bk.prototype,{isFogExp2:!0,clone:function(){return new Bk(this.color,this.density)},toJSON:function(){return{type:"FogExp2",color:this.color.getHex(),density:this.density}}});function Ck(a,b,c){this.name="",this.color=new S(a),this.near=b!==void 0?b:1,this.far=c!==void 0?c:1e3}Object.assign(Ck.prototype,{isFog:!0,clone:function(){return new Ck(this.color,this.near,this.far)},toJSON:function(){return{type:"Fog",color:this.color.getHex(),near:this.near,far:this.far}}});function Dk(a,b){this.array=a,this.stride=b,this.count=a!==void 0?a.length/b:0,this.usage=Rc,this.updateRange={offset:0,count:-1},this.version=0}Object.defineProperty(Dk.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});Object.assign(Dk.prototype,{isInterleavedBuffer:!0,onUploadCallback:function(){},setUsage:function(a){this.usage=a;return this},copy:function(a){this.array=new a.array.constructor(a.array);this.count=a.count;this.stride=a.stride;this.usage=a.usage;return this},copyAt:function(b,c,d){b*=this.stride;d*=c.stride;for(var a=0,e=this.stride;ab.far)return;a.push({distance:c,point:Ik.clone(),uv:ye.getUV(Ik,Ok,Pk,Qk,Rk,Sk,Tk,new O()),face:null,object:this})},clone:function(){return new this.constructor(this.material).copy(this)},copy:function(a){R.prototype.copy.call(this,a);a.center!==void 0&&this.center.copy(a.center);return this}});function Vk(a,b,c,d,e,f){Lk.subVectors(a,c).addScalar(.5).multiply(d),e!==void 0?(Mk.x=f*Lk.x-e*Lk.y,Mk.y=e*Lk.x+f*Lk.y):Mk.copy(Lk),a.copy(b),a.x+=Mk.x,a.y+=Mk.y,a.applyMatrix4(Nk)}var Wk=new P(),Xk=new P();function Yk(){R.call(this),this._currentLevel=0,this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]}}),this.autoUpdate=!0}Yk.prototype=Object.assign(Object.create(R.prototype),{constructor:Yk,isLOD:!0,copy:function(b){R.prototype.copy.call(this,b,!1);var c=b.levels;for(var a=0,d=c.length;a0){for(var a=1,d=c.length;a0){Wk.setFromMatrixPosition(this.matrixWorld);c=b.ray.origin.distanceTo(Wk);this.getObjectForDistance(c).raycast(b,a)}},update:function(b){var c=this.levels;if(c.length>1){Wk.setFromMatrixPosition(b.matrixWorld);Xk.setFromMatrixPosition(this.matrixWorld);b=Wk.distanceTo(Xk)/b.zoom;c[0].object.visible=!0;for(var a=1,d=c.length;a=c[a].distance)c[a-1].object.visible=!1,c[a].object.visible=!0;else break;this._currentLevel=a-1;for(;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}else for(var a=0,m=l.length/3-1;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}else if(d.isGeometry){k=d.vertices;m=k.length;for(var a=0;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}},clone:function(){return new this.constructor(this.geometry,this.material).copy(this)}});var pl=new P(),ql=new P();function rl(a,b){ol.call(this,a,b),this.type="LineSegments"}rl.prototype=Object.assign(Object.create(ol.prototype),{constructor:rl,isLineSegments:!0,computeLineDistances:function(){var b=this.geometry;if(b.isBufferGeometry){if(b.index===null){var c=b.attributes.position,d=[];for(var a=0,e=c.count;a0){d=d[e[0]];if(d!==void 0){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(e=0,b=d.length;e0}},clone:function(){return new this.constructor(this.geometry,this.material).copy(this)}});function zl(b,c,d,e,f,a,g){var h=vl.distanceSqToPoint(b);if(hf.far)return;a.push({distance:b,distanceToRay:Math.sqrt(h),point:d,index:c,face:null,object:g})}}function Al(a,b,c,d,e,f,g,h,i){gd.call(this,a,b,c,d,e,f,g,h,i),this.format=g!==void 0?g:bb,this.minFilter=f!==void 0?f:Ka,this.magFilter=e!==void 0?e:Ka,this.generateMipmaps=!1}Al.prototype=Object.assign(Object.create(gd.prototype),{constructor:Al,isVideoTexture:!0,update:function(){var a=this.image;a.readyState>=a.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}});function Bl(a,b,c,d,e,f,g,h,i,j,k,l){gd.call(this,null,f,g,h,i,j,d,e,k,l),this.image={width:b,height:c},this.mipmaps=a,this.flipY=!1,this.generateMipmaps=!1}Bl.prototype=Object.create(gd.prototype);Bl.prototype.constructor=Bl;Bl.prototype.isCompressedTexture=!0;function Cl(a,b,c,d,e,f,g,h,i){gd.call(this,a,b,c,d,e,f,g,h,i),this.needsUpdate=!0}Cl.prototype=Object.create(gd.prototype);Cl.prototype.constructor=Cl;Cl.prototype.isCanvasTexture=!0;function Dl(a,b,c,d,e,f,g,h,i,j){j=j!==void 0?j:gb;if(j!==gb&&j!==hb)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");c===void 0&&j===gb&&(c=Sa);c===void 0&&j===hb&&(c=$a);gd.call(this,null,d,e,f,g,h,j,c,i);this.image={width:a,height:b};this.magFilter=g!==void 0?g:Fa;this.minFilter=h!==void 0?h:Fa;this.flipY=!1;this.generateMipmaps=!1}Dl.prototype=Object.create(gd.prototype);Dl.prototype.constructor=Dl;Dl.prototype.isDepthTexture=!0;function El(b){W.call(this);this.type="WireframeGeometry";var c=[],a,d,e,f,g,h=[0,0],i={},j,k,l,m=["a","b","c"];if(b&&b.isGeometry){var n=b.faces;for(a=0,e=n.length;a=0?(b(r-i,q,l),m.subVectors(k,l)):(b(r+i,q,l),m.subVectors(l,k));q-i>=0?(b(r,q-i,l),n.subVectors(k,l)):(b(r,q+i,l),n.subVectors(l,k));j.crossVectors(m,n).normalize();g.push(j.x,j.y,j.z);h.push(r,q)}}for(a=0;a.9&&g<.1&&(b<.2&&(f[a+0]+=1),c<.2&&(f[a+2]+=1),d<.2&&(f[a+4]+=1))}}function l(a){e.push(a.x,a.y,a.z)}function m(b,c){b=b*3;c.x=a[b+0];c.y=a[b+1];c.z=a[b+2]}function n(){var b=new P(),c=new P(),d=new P(),g=new P(),h=new O(),i=new O(),j=new O();for(var a=0,k=0;a80*d){i=e=b[0];j=c=b[1];for(var a=d;ae&&(e=k),l>c&&(c=l);m=Math.max(e-i,c-j);m=m!==0?1/m:0}$l(g,h,d,i,j,m);return h}};function Yl(a,b,c,d,e){var f;if(e===Bm(a,b,c,d)>0)for(e=b;e=b;e-=d)f=ym(e,a[e],a[e+1],f);f&&qm(f,f.next)&&(zm(f),f=f.next);return f}function Zl(a,b){if(!a)return a;b||(b=a);a=a;var c;do{c=!1;if(!a.steiner&&(qm(a,a.next)||pm(a.prev,a,a.next)===0)){zm(a);a=b=a.prev;if(a===a.next)break;c=!0}else a=a.next}while(c||a!==b);return b}function $l(a,b,c,d,e,f,g){if(!a)return;!g&&f&&jm(a,d,e,f);var h=a,i,j;while(a.prev!==a.next){i=a.prev;j=a.next;if(f?bm(a,d,e,f):am(a)){b.push(i.i/c);b.push(a.i/c);b.push(j.i/c);zm(a);a=j.next;h=j.next;continue}a=j;if(a===h){!g?$l(Zl(a),b,c,d,e,f,1):g===1?(a=cm(Zl(a),b,c),$l(a,b,c,d,e,f,2)):g===2&&dm(a,b,c,d,e,f);break}}}function am(a){var b=a.prev,c=a,d=a.next;if(pm(b,c,d)>=0)return!1;var e=a.next.next;while(e!==a.prev){if(nm(b.x,b.y,c.x,c.y,d.x,d.y,e.x,e.y)&&pm(e.prev,e,e.next)>=0)return!1;e=e.next}return!0}function bm(a,b,c,d){var e=a.prev,f=a,g=a.next;if(pm(e,f,g)>=0)return!1;var h=e.xf.x?e.x>g.x?e.x:g.x:f.x>g.x?f.x:g.x,k=e.y>f.y?e.y>g.y?e.y:g.y:f.y>g.y?f.y:g.y;h=lm(h,i,b,c,d);i=lm(j,k,b,c,d);j=a.prevZ;k=a.nextZ;while(j&&j.z>=h&&k&&k.z<=i){if(j!==a.prev&&j!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,j.x,j.y)&&pm(j.prev,j,j.next)>=0)return!1;j=j.prevZ;if(k!==a.prev&&k!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,k.x,k.y)&&pm(k.prev,k,k.next)>=0)return!1;k=k.nextZ}while(j&&j.z>=h){if(j!==a.prev&&j!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,j.x,j.y)&&pm(j.prev,j,j.next)>=0)return!1;j=j.prevZ}while(k&&k.z<=i){if(k!==a.prev&&k!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,k.x,k.y)&&pm(k.prev,k,k.next)>=0)return!1;k=k.nextZ}return!0}function cm(a,b,c){var d=a;do{var e=d.prev,f=d.next.next;!qm(e,f)&&rm(e,d,d.next,f)&&vm(e,f)&&vm(f,e)&&(b.push(e.i/c),b.push(d.i/c),b.push(f.i/c),zm(d),zm(d.next),d=a=f);d=d.next}while(d!==a);return Zl(d)}function dm(a,b,c,d,e,f){var g=a;do{var h=g.next.next;while(h!==g.prev){if(g.i!==h.i&&om(g,h)){var i=xm(g,h);g=Zl(g,g.next);i=Zl(i,i.next);$l(g,b,c,d,e,f);$l(i,b,c,d,e,f);return}h=h.next}g=g.next}while(g!==a)}function em(b,c,d,e){var f=[],a,g,h,i;for(a=0,g=c.length;a=c.next.y&&c.next.y!==c.y){var h=c.x+(e-c.y)*(c.next.x-c.x)/(c.next.y-c.y);if(h<=d&&h>f){f=h;if(h===d){if(e===c.y)return c;if(e===c.next.y)return c.next}g=c.x=c.x&&c.x>=b&&d!==c.x&&nm(eg.x||c.x===g.x&&im(g,c)))&&(g=c,j=k)),c=c.next;while(c!==h);return g}function im(a,b){return pm(a.prev,a,b.prev)<0&&pm(b.next,a,a.next)<0}function jm(a,b,c,d){var e=a;do e.z===null&&(e.z=lm(e.x,e.y,b,c,d)),e.prevZ=e.prev,e.nextZ=e.next,e=e.next;while(e!==a);e.prevZ.nextZ=null;e.prevZ=null;km(e)}function km(b){var a,c,d,e,f,g,h,i=1;do{c=b;b=null;f=null;g=0;while(c){g++;d=c;h=0;for(a=0;a0||a>0&&d)h!==0&&(a===0||!d||c.z<=d.z)?(e=c,c=c.nextZ,h--):(e=d,d=d.nextZ,a--),f?f.nextZ=e:b=e,e.prevZ=f,f=e;c=d}f.nextZ=null;i*=2}while(g>1);return b}function lm(a,b,c,d,e){a=32767*(a-c)*e;b=32767*(b-d)*e;a=(a|a<<8)&16711935;a=(a|a<<4)&252645135;a=(a|a<<2)&858993459;a=(a|a<<1)&1431655765;b=(b|b<<8)&16711935;b=(b|b<<4)&252645135;b=(b|b<<2)&858993459;b=(b|b<<1)&1431655765;return a|b<<1}function mm(a){var b=a,c=a;do(b.x=0&&(c-a)*(f-b)-(e-a)*(d-b)>=0&&(e-a)*(h-b)-(g-a)*(f-b)>=0}function om(a,b){return a.next.i!==b.i&&a.prev.i!==b.i&&!um(a,b)&&(vm(a,b)&&vm(b,a)&&wm(a,b)&&(pm(a.prev,a,b.prev)||pm(a,b.prev,b))||qm(a,b)&&pm(a.prev,a,a.next)>0&&pm(b.prev,b,b.next)>0)}function pm(a,b,c){return(b.y-a.y)*(c.x-b.x)-(b.x-a.x)*(c.y-b.y)}function qm(a,b){return a.x===b.x&&a.y===b.y}function rm(a,b,c,d){var e=tm(pm(a,b,c)),f=tm(pm(a,b,d)),g=tm(pm(c,d,a)),h=tm(pm(c,d,b));if(e!==f&&g!==h)return!0;if(e===0&&sm(a,c,b))return!0;if(f===0&&sm(a,d,b))return!0;if(g===0&&sm(c,a,d))return!0;return h===0&&sm(c,b,d)?!0:!1}function sm(a,b,c){return b.x<=Math.max(a.x,c.x)&&b.x>=Math.min(a.x,c.x)&&b.y<=Math.max(a.y,c.y)&&b.y>=Math.min(a.y,c.y)}function tm(a){return a>0?1:a<0?-1:0}function um(a,b){var c=a;do{if(c.i!==a.i&&c.next.i!==a.i&&c.i!==b.i&&c.next.i!==b.i&&rm(c,c.next,a,b))return!0;c=c.next}while(c!==a);return!1}function vm(a,b){return pm(a.prev,a,a.next)<0?pm(a,b,a.next)>=0&&pm(a,a.prev,b)>=0:pm(a,b,a.prev)<0||pm(a,a.next,b)<0}function wm(b,c){var d=b,e=!1,a=(b.x+c.x)/2;c=(b.y+c.y)/2;do d.y>c!==d.next.y>c&&d.next.y!==d.y&&a<(d.next.x-d.x)*(c-d.y)/(d.next.y-d.y)+d.x&&(e=!e),d=d.next;while(d!==b);return e}function xm(a,b){var c=new Am(a.i,a.x,a.y),d=new Am(b.i,b.x,b.y),e=a.next,f=b.prev;a.next=b;b.prev=a;c.next=e;e.prev=c;d.next=c;c.prev=d;f.next=d;d.prev=f;return d}function ym(a,b,c,d){a=new Am(a,b,c);!d?(a.prev=a,a.next=a):(a.next=d.next,a.prev=d,d.next.prev=a,d.next=a);return a}function zm(a){a.next.prev=a.prev,a.prev.next=a.next,a.prevZ&&(a.prevZ.nextZ=a.nextZ),a.nextZ&&(a.nextZ.prevZ=a.prevZ)}function Am(a,b,c){this.i=a,this.x=b,this.y=c,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function Bm(a,b,c,d){var e=0;for(var b=b,f=c-d;b2&&a[b-1].equals(a[0])&&a.pop()}function Em(b,c){for(var a=0;aNumber.EPSILON){i=Math.sqrt(h);var j=Math.sqrt(f*f+g*g),k=b.x-e/i;b=b.y+d/i;i=c.x-g/j;c=c.y+f/j;j=((i-k)*g-(c-b)*f)/(d*g-e*f);i=k+d*j-a.x;c=b+e*j-a.y;k=i*i+c*c;if(k<=2)return new O(i,c);else b=Math.sqrt(k/2)}else{j=!1;d>Number.EPSILON?f>Number.EPSILON&&(j=!0):d<-Number.EPSILON?f<-Number.EPSILON&&(j=!0):Math.sign(e)===Math.sign(g)&&(j=!0);j?(i=-e,c=d,b=Math.sqrt(h)):(i=d,c=e,b=Math.sqrt(h/2))}return new O(i/b,c/b)}p=[];for(var a=0,h=C.length,G=h-1,H=a+1;a=0;da--){G=da/o;H=l*Math.cos(G*Math.PI/2);E=m*Math.sin(G*Math.PI/2)+n;for(a=0,h=C.length;a=0){c=a;d=a-1;d<0&&(d=b.length-1);var e=0,g=i+o*2;for(e=0;e0)&&o.push(v,u,s);(j!==c-1||h0&&s(!0),b>0&&s(!1));this.setIndex(j);this.setAttribute("position",new V(k,3));this.setAttribute("normal",new V(l,3));this.setAttribute("uv",new V(m,2));function r(){var f,r,s=new P(),t=new P(),u=0,v=(b-a)/c;for(r=0;r<=e;r++){var w=[],x=r/e,y=x*(b-a)+a;for(f=0;f<=d;f++){var z=f/d,A=z*h+g,B=Math.sin(A);A=Math.cos(A);t.x=y*B;t.y=-x*c+p;t.z=y*A;k.push(t.x,t.y,t.z);s.set(B,v,A).normalize();l.push(s.x,s.y,s.z);m.push(z,1-x);w.push(n++)}o.push(w)}for(f=0;f=e)continue;i.push(g.times[k]);for(l=0;lb.tracks[a].times[0]&&(l=b.tracks[a].times[0]);for(var a=0;a=f.times[j]){j=j*i;k=nn.arraySlice(f.values,j)}else{j=f.createInterpolant();j.evaluate(b);k=j.resultBuffer}if(g==="quaternion"){j=new kd(k[0],k[1],k[2],k[3]).normalize().conjugate();j.toArray(k)}j=h.times.length;for(var l=0;l=e)){var h=b[1];a=e)break seek}f=c;c=0;break linear_scan}break validate_interval}while(c>>1;ab)--f;++f;if(e!==0||f!==d){e>=f&&(f=Math.max(f,1),e=f-1);a=this.getValueSize();this.times=nn.arraySlice(c,e,f);this.values=nn.arraySlice(this.values,e*a,f*a)}return this},validate:function(){var b=!0,c=this.getValueSize();c-Math.floor(c)!==0&&(b=!1);c=this.times;var d=this.values,e=c.length;e===0&&(b=!1);var f=null;for(var a=0;a!==e;a++){var g=c[a];if(typeof g==="number"&&isNaN(g)){b=!1;break}if(f!==null&&f>g){b=!1;break}f=g}if(d!==void 0&&nn.isTypedArray(d))for(var a=0,g=d.length;a!==g;++a){e=d[a];if(isNaN(e)){b=!1;break}}return b},optimize:function(){var b=nn.arraySlice(this.times),c=nn.arraySlice(this.values),d=this.getValueSize(),e=this.getInterpolation()===gc,f=1,g=b.length-1;for(var a=1;a0){b[f]=b[g];for(var m=g*d,j=f*d,l=0;l!==d;++l)c[j+l]=c[m+l];++f}f!==b.length?(this.times=nn.arraySlice(b,0,f),this.values=nn.arraySlice(c,0,f*d)):(this.times=b,this.values=c);return this},clone:function(){var a=nn.arraySlice(this.times,0),b=nn.arraySlice(this.values,0),c=this.constructor;c=new c(this.name,a,b);c.createInterpolant=this.createInterpolant;return c}});function tn(a,b,c){sn.call(this,a,b,c)}tn.prototype=Object.assign(Object.create(sn.prototype),{constructor:tn,ValueTypeName:"bool",ValueBufferType:Array,DefaultInterpolation:ec,InterpolantFactoryMethodLinear:void 0,InterpolantFactoryMethodSmooth:void 0});function un(a,b,c,d){sn.call(this,a,b,c,d)}un.prototype=Object.assign(Object.create(sn.prototype),{constructor:un,ValueTypeName:"color"});function vn(a,b,c,d){sn.call(this,a,b,c,d)}vn.prototype=Object.assign(Object.create(sn.prototype),{constructor:vn,ValueTypeName:"number"});function wn(a,b,c,d){on.call(this,a,b,c,d)}wn.prototype=Object.assign(Object.create(on.prototype),{constructor:wn,interpolate_:function(a,b,c,d){var e=this.resultBuffer,f=this.sampleValues,g=this.valueSize;a=a*g;c=(c-b)/(d-b);for(d=a+g;a!==d;a+=4)kd.slerpFlat(e,0,f,a-g,f,a,c);return e}});function xn(a,b,c,d){sn.call(this,a,b,c,d)}xn.prototype=Object.assign(Object.create(sn.prototype),{constructor:xn,ValueTypeName:"quaternion",DefaultInterpolation:fc,InterpolantFactoryMethodLinear:function(a){return new wn(this.times,this.values,this.getValueSize(),a)},InterpolantFactoryMethodSmooth:void 0});function yn(a,b,c,d){sn.call(this,a,b,c,d)}yn.prototype=Object.assign(Object.create(sn.prototype),{constructor:yn,ValueTypeName:"string",ValueBufferType:Array,DefaultInterpolation:ec,InterpolantFactoryMethodLinear:void 0,InterpolantFactoryMethodSmooth:void 0});function zn(a,b,c,d){sn.call(this,a,b,c,d)}zn.prototype=Object.assign(Object.create(sn.prototype),{constructor:zn,ValueTypeName:"vector"});function An(a,b,c,d){this.name=a,this.tracks=c,this.duration=b!==void 0?b:-1,this.blendMode=d!==void 0?d:kc,this.uuid=N.generateUUID(),this.duration<0&&this.resetDuration()}function Bn(a){switch(a.toLowerCase()){case"scalar":case"double":case"float":case"number":case"integer":return vn;case"vector":case"vector2":case"vector3":case"vector4":return zn;case"color":return un;case"quaternion":return xn;case"bool":case"boolean":return tn;case"string":return yn}throw new Error("THREE.KeyframeTrack: Unsupported typeName: "+a)}function Cn(a){if(a.type===void 0)throw new Error("THREE.KeyframeTrack: track type undefined, can not parse");var b=Bn(a.type);if(a.times===void 0){var c=[],d=[];nn.flattenJSON(a.keys,c,d,"value");a.times=c;a.values=d}if(b.parse!==void 0)return b.parse(a);else return new b(a.name,a.times,a.values,a.interpolation)}Object.assign(An,{parse:function(b){var c=[],d=b.tracks,e=1/(b.fps||1);for(var a=0,f=d.length;a!==f;++a)c.push(Cn(d[a]).scale(e));return new An(b.name,b.duration,c,b.blendMode)},toJSON:function(b){var c=[],d=b.tracks;b={name:b.name,duration:b.duration,tracks:c,uuid:b.uuid,blendMode:b.blendMode};for(var a=0,e=d.length;a!==e;++a)c.push(sn.toJSON(d[a]));return b},CreateFromMorphTargetSequence:function(b,c,d,e){var f=c.length,g=[];for(var a=0;a1){i=i[1];var j=e[i];j||(e[i]=j=[]);j.push(h)}}j=[];for(i in e)j.push(An.CreateFromMorphTargetSequence(i,e[i],c,d));return j},parseAnimation:function(a,b){if(!a)return null;var c=function(d,e,a,f,g){if(a.length!==0){var b=[],c=[];nn.flattenJSON(a,b,c,f);b.length!==0&&g.push(new d(e,b,c))}},d=[],e=a.name||"default",f=a.length||-1,g=a.fps||30,h=a.blendMode;a=a.hierarchy||[];for(var i=0;i0||a.search(/^data:image/jpeg/)===0;e.format=c?bb:cb;e.needsUpdate=!0;b!==void 0&&b(e)},c,d);return e}});function $(){this.type="Curve",this.arcLengthDivisions=200}Object.assign($.prototype,{getPoint:function(){return null},getPointAt:function(a,b){a=this.getUtoTmapping(a);return this.getPoint(a,b)},getPoints:function(a){a===void 0&&(a=5);var b=[];for(var c=0;c<=a;c++)b.push(this.getPoint(c/a));return b},getSpacedPoints:function(a){a===void 0&&(a=5);var b=[];for(var c=0;c<=a;c++)b.push(this.getPointAt(c/a));return b},getLength:function(){var a=this.getLengths();return a[a.length-1]},getLengths:function(a){a===void 0&&(a=this.arcLengthDivisions);if(this.cacheArcLengths&&this.cacheArcLengths.length===a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;this.cacheArcLengths=b;return b},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(b,c){var d=this.getLengths(),a,e=d.length;c?c=c:c=b*d[e-1];b=0;var f=e-1,g;while(b<=f){a=Math.floor(b+(f-b)/2);g=d[a]-c;if(g<0)b=a+1;else if(g>0)f=a-1;else{f=a;break}}a=f;if(d[a]===c)return a/(e-1);g=d[a];b=d[a+1];f=b-g;d=(c-g)/f;b=(a+d)/(e-1);return b},getTangent:function(a,b){var c=1e-4,d=a-c;a=a+c;d<0&&(d=0);a>1&&(a=1);c=this.getPoint(d);d=this.getPoint(a);a=b||(c.isVector2?new O():new P());a.copy(d).sub(c).normalize();return a},getTangentAt:function(a,b){a=this.getUtoTmapping(a);return this.getTangent(a,b)},computeFrenetFrames:function(b,c){var d=new P(),e=[],f=[],g=[],h=new P(),i=new Q(),a,j;for(a=0;a<=b;a++)j=a/b,e[a]=this.getTangentAt(j,new P()),e[a].normalize();f[0]=new P();g[0]=new P();j=Number.MAX_VALUE;var k=Math.abs(e[0].x),l=Math.abs(e[0].y),m=Math.abs(e[0].z);k<=j&&(j=k,d.set(1,0,0));l<=j&&(j=l,d.set(0,1,0));m<=j&&d.set(0,0,1);h.crossVectors(e[0],d).normalize();f[0].crossVectors(e[0],h);g[0].crossVectors(e[0],f[0]);for(a=1;a<=b;a++)f[a]=f[a-1].clone(),g[a]=g[a-1].clone(),h.crossVectors(e[a-1],e[a]),h.length()>Number.EPSILON&&(h.normalize(),k=Math.acos(N.clamp(e[a-1].dot(e[a]),-1,1)),f[a].applyMatrix4(i.makeRotationAxis(h,k))),g[a].crossVectors(e[a],f[a]);if(c===!0){k=Math.acos(N.clamp(f[0].dot(f[b]),-1,1));k/=b;e[0].dot(h.crossVectors(f[0],f[b]))>0&&(k=-k);for(a=1;a<=b;a++)f[a].applyMatrix4(i.makeRotationAxis(e[a],k*a)),g[a].crossVectors(e[a],f[a])}return{tangents:e,normals:f,binormals:g}},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.arcLengthDivisions=a.arcLengthDivisions;return this},toJSON:function(){var a={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};a.arcLengthDivisions=this.arcLengthDivisions;a.type=this.type;return a},fromJSON:function(a){this.arcLengthDivisions=a.arcLengthDivisions;return this}});function Pn(a,b,c,d,e,f,g,h){$.call(this),this.type="EllipseCurve",this.aX=a||0,this.aY=b||0,this.xRadius=c||1,this.yRadius=d||1,this.aStartAngle=e||0,this.aEndAngle=f||2*Math.PI,this.aClockwise=g||!1,this.aRotation=h||0}Pn.prototype=Object.create($.prototype);Pn.prototype.constructor=Pn;Pn.prototype.isEllipseCurve=!0;Pn.prototype.getPoint=function(a,b){b=b||new O();var c=Math.PI*2,d=this.aEndAngle-this.aStartAngle,e=Math.abs(d)c)d-=c;d0?0:(Math.floor(Math.abs(e)/d)+1)*d:a===0&&e===d-1&&(e=d-2,a=1);var f,g,h;this.closed||e>0?f=c[(e-1)%d]:(Sn.subVectors(c[0],c[1]).add(c[0]),f=Sn);g=c[e%d];h=c[(e+1)%d];this.closed||e+2c.length-2?c.length-1:d+1];c=c[d>c.length-3?c.length-1:d+2];b.set(Xn(a,e.x,f.x,g.x,c.x),Xn(a,e.y,f.y,g.y,c.y));return b};no.prototype.copy=function(b){$.prototype.copy.call(this,b);this.points=[];for(var a=0,c=b.points.length;a=b){var d=c[a]-b,e=this.curves[a],f=e.getLength();d=f===0?0:1-d/f;return e.getPointAt(d)}a++}return null},getLength:function(){var a=this.getCurveLengths();return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;var b=[],c=0;for(var a=0,d=this.curves.length;a1&&!c[c.length-1].equals(c[0])&&c.push(c[0]);return c},copy:function(b){$.prototype.copy.call(this,b);this.curves=[];for(var a=0,c=b.curves.length;a0){b=a.getPoint(0);b.equals(this.currentPoint)||this.lineTo(b.x,b.y)}this.curves.push(a);c=a.getPoint(1);this.currentPoint.copy(c);return this},copy:function(a){po.prototype.copy.call(this,a);this.currentPoint.copy(a.currentPoint);return this},toJSON:function(){var a=po.prototype.toJSON.call(this);a.currentPoint=this.currentPoint.toArray();return a},fromJSON:function(a){po.prototype.fromJSON.call(this,a);this.currentPoint.fromArray(a.currentPoint);return this}});function ro(a){qo.call(this,a),this.uuid=N.generateUUID(),this.type="Shape",this.holes=[]}ro.prototype=Object.assign(Object.create(qo.prototype),{constructor:ro,getPointsHoles:function(b){var c=[];for(var a=0,d=this.holes.length;a0?!0:!1:d.vertexColors=a.vertexColors);if(a.uniforms!==void 0)for(var e in a.uniforms){var f=a.uniforms[e];d.uniforms[e]={};switch(f.type){case"t":d.uniforms[e].value=c(f.value);break;case"c":d.uniforms[e].value=new S().setHex(f.value);break;case"v2":d.uniforms[e].value=new O().fromArray(f.value);break;case"v3":d.uniforms[e].value=new P().fromArray(f.value);break;case"v4":d.uniforms[e].value=new hd().fromArray(f.value);break;case"m3":d.uniforms[e].value=new cd().fromArray(f.value);case"m4":d.uniforms[e].value=new Q().fromArray(f.value);break;default:d.uniforms[e].value=f.value}}a.defines!==void 0&&(d.defines=a.defines);a.vertexShader!==void 0&&(d.vertexShader=a.vertexShader);a.fragmentShader!==void 0&&(d.fragmentShader=a.fragmentShader);if(a.extensions!==void 0)for(f in a.extensions)d.extensions[f]=a.extensions[f];a.shading!==void 0&&(d.flatShading=a.shading===1);a.size!==void 0&&(d.size=a.size);a.sizeAttenuation!==void 0&&(d.sizeAttenuation=a.sizeAttenuation);a.map!==void 0&&(d.map=c(a.map));a.matcap!==void 0&&(d.matcap=c(a.matcap));a.alphaMap!==void 0&&(d.alphaMap=c(a.alphaMap));a.bumpMap!==void 0&&(d.bumpMap=c(a.bumpMap));a.bumpScale!==void 0&&(d.bumpScale=a.bumpScale);a.normalMap!==void 0&&(d.normalMap=c(a.normalMap));a.normalMapType!==void 0&&(d.normalMapType=a.normalMapType);if(a.normalScale!==void 0){e=a.normalScale;Array.isArray(e)===!1&&(e=[e,e]);d.normalScale=new O().fromArray(e)}a.displacementMap!==void 0&&(d.displacementMap=c(a.displacementMap));a.displacementScale!==void 0&&(d.displacementScale=a.displacementScale);a.displacementBias!==void 0&&(d.displacementBias=a.displacementBias);a.roughnessMap!==void 0&&(d.roughnessMap=c(a.roughnessMap));a.metalnessMap!==void 0&&(d.metalnessMap=c(a.metalnessMap));a.emissiveMap!==void 0&&(d.emissiveMap=c(a.emissiveMap));a.emissiveIntensity!==void 0&&(d.emissiveIntensity=a.emissiveIntensity);a.specularMap!==void 0&&(d.specularMap=c(a.specularMap));a.envMap!==void 0&&(d.envMap=c(a.envMap));a.envMapIntensity!==void 0&&(d.envMapIntensity=a.envMapIntensity);a.reflectivity!==void 0&&(d.reflectivity=a.reflectivity);a.refractionRatio!==void 0&&(d.refractionRatio=a.refractionRatio);a.lightMap!==void 0&&(d.lightMap=c(a.lightMap));a.lightMapIntensity!==void 0&&(d.lightMapIntensity=a.lightMapIntensity);a.aoMap!==void 0&&(d.aoMap=c(a.aoMap));a.aoMapIntensity!==void 0&&(d.aoMapIntensity=a.aoMapIntensity);a.gradientMap!==void 0&&(d.gradientMap=c(a.gradientMap));a.clearcoatMap!==void 0&&(d.clearcoatMap=c(a.clearcoatMap));a.clearcoatRoughnessMap!==void 0&&(d.clearcoatRoughnessMap=c(a.clearcoatRoughnessMap));a.clearcoatNormalMap!==void 0&&(d.clearcoatNormalMap=c(a.clearcoatNormalMap));a.clearcoatNormalScale!==void 0&&(d.clearcoatNormalScale=new O().fromArray(a.clearcoatNormalScale));return d},setTextures:function(a){this.textures=a;return this}});var Ho={decodeText:function(b){if(typeof TextDecoder!=="undefined")return new TextDecoder().decode(b);var c="";for(var a=0,d=b.length;a0){b=new En(b);var f=new Mn(b);f.setCrossOrigin(this.crossOrigin);for(var b=0,g=a.length;bNumber.EPSILON){j<0&&(g=b[f],i=-i,h=b[e],j=-j);if(a.yh.y)continue;if(a.y===g.y){if(a.x===g.x)return!0}else{j=j*(a.x-g.x)-i*(a.y-g.y);if(j===0)return!0;if(j<0)continue;d=!d}}else{if(a.y!==g.y)continue;if(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=h.x)return!0}}return d}var f=Cm.isClockWise,g=this.subPaths;if(g.length===0)return[];if(c===!0)return d(g);var h,i,j;c=[];if(g.length===1){i=g[0];j=new ro();j.curves=i.curves;c.push(j);return c}var k=!f(g[0].getPoints());k=b?!k:k;var l=[],m=[],n=[],o=0,p;m[o]=void 0;n[o]=[];for(var a=0,q=g.length;a1){h=!1;p=[];for(k=0,b=m.length;k0&&(h||(n=l))}for(var a=0,g=m.length;a0){this.source.connect(this.filters[0]);for(var a=1,b=this.filters.length;a0){this.source.disconnect(this.filters[0]);for(var a=1,b=this.filters.length;a0&&this._mixBufferRegionAdditive(c,a,this._addIndex*b,1,b);for(g=b,d=b+b;g!==d;++g)if(c[g]!==c[g+b]){f.setValue(c,a);break}},saveOriginalState:function(){var a=this.binding,b=this.buffer,c=this.valueSize,d=c*this._origIndex;a.getValue(b,d);for(var a=c,e=d;a!==e;++a)b[a]=b[d+a%c];this._setIdentity();this.cumulativeWeight=0;this.cumulativeWeightAdditive=0},restoreOriginalState:function(){var a=this.valueSize*3;this.binding.setValue(this.buffer,a)},_setAdditiveIdentityNumeric:function(){var a=this._addIndex*this.valueSize;this.buffer.fill(0,a,a+this.valueSize)},_setAdditiveIdentityQuaternion:function(){this._setAdditiveIdentityNumeric(),this.buffer[this._addIndex*4+3]=1},_setAdditiveIdentityOther:function(){var a=this._origIndex*this.valueSize,b=this._addIndex*this.valueSize;this.buffer.copyWithin(b,a,this.valueSize)},_select:function(a,b,c,d,e){if(d>=.5)for(d=0;d!==e;++d)a[b+d]=a[c+d]},_slerp:function(a,b,c,d){kd.slerpFlat(a,b,a,b,a,c,d)},_slerpAdditive:function(a,b,c,d,e){e=this._workIndex*e;kd.multiplyQuaternionsFlat(a,e,a,b,a,c);kd.slerpFlat(a,b,a,b,a,e,d)},_lerp:function(b,c,d,e,f){var g=1-e;for(var a=0;a!==f;++a){var h=c+a;b[h]=b[h]*g+b[d+a]*e}},_lerpAdditive:function(b,c,d,e,f){for(var a=0;a!==f;++a){var g=c+a;b[g]=b[g]+b[d+a]*e}}});Tf="\[\]\.:\/";var rp=new RegExp("["+Tf+"]","g");Uf="[^"+Tf+"]";Vf="[^"+Tf.replace("\.","")+"]";Wf=/((?:WC+[/:])*)/.source.replace("WC",Uf);Xf=/(WCOD+)?/.source.replace("WCOD",Vf);Yf=/(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC",Uf);Zf=/.(WC+)(?:[(.+)])?/.source.replace("WC",Uf);var sp=new RegExp("^"+Wf+Xf+Yf+Zf+"$"),tp=["material","materials","bones"];function up(a,b,c){c=c||vp.parseTrackName(b);this._targetGroup=a;this._bindings=a.subscribe_(b,c)}Object.assign(up.prototype,{getValue:function(a,b){this.bind();var c=this._targetGroup.nCachedObjects_;c=this._bindings[c];c!==void 0&&c.getValue(a,b)},setValue:function(b,c){var d=this._bindings;for(var a=this._targetGroup.nCachedObjects_,e=d.length;a!==e;++a)d[a].setValue(b,c)},bind:function(){var b=this._bindings;for(var a=this._targetGroup.nCachedObjects_,c=b.length;a!==c;++a)b[a].bind()},unbind:function(){var b=this._bindings;for(var a=this._targetGroup.nCachedObjects_,c=b.length;a!==c;++a)b[a].unbind()}});function vp(a,b,c){this.path=b,this.parsedPath=c||vp.parseTrackName(b),this.node=vp.findNode(a,this.parsedPath.nodeName)||a,this.rootNode=a}Object.assign(vp,{Composite:up,create:function(a,b,c){if(!(a&&a.isAnimationObjectGroup))return new vp(a,b,c);else return new vp.Composite(a,b,c)},sanitizeNodeName:function(a){return a.replace(/s/g,"_").replace(rp,"")},parseTrackName:function(a){var b=sp.exec(a);if(!b)throw new Error("PropertyBinding: Cannot parse trackName: "+a);b={nodeName:b[2],objectName:b[3],objectIndex:b[4],propertyName:b[5],propertyIndex:b[6]};var c=b.nodeName&&b.nodeName.lastIndexOf(".");if(c!==void 0&&c!==-1){var d=b.nodeName.substring(c+1);tp.indexOf(d)!==-1&&(b.nodeName=b.nodeName.substring(0,c),b.objectName=d)}if(b.propertyName===null||b.propertyName.length===0)throw new Error("PropertyBinding: can not parse propertyName from trackName: "+a);return b},findNode:function(a,b){if(!b||b===""||b==="."||b===-1||b===a.name||b===a.uuid)return a;if(a.skeleton){var c=a.skeleton.getBoneByName(b);if(c!==void 0)return c}if(a.children){var d=function(c){for(var a=0;a=c){var k=c++,l=b[k];d[l.uuid]=j;b[j]=l;d[i]=k;b[k]=h;for(var m=0,n=f;m!==n;++m){var o=e[m],p=o[k],q=o[j];o[j]=p;o[k]=q}}}this.nCachedObjects_=c},uncache:function(){var b=this._objects,c=b.length,d=this.nCachedObjects_,e=this._indicesByUUID,f=this._bindings,g=f.length;for(var a=0,h=arguments.length;a!==h;++a){var i=arguments[a],j=i.uuid,k=e[j];if(k!==void 0){delete e[j];if(k0){b=this._interpolants;a=this._propertyBindings;switch(this.blendMode){case lc:for(var f=0,g=b.length;f!==g;++f)b[f].evaluate(c),a[f].accumulateAdditive(e);break;case kc:default:for(var f=0,g=b.length;f!==g;++f)b[f].evaluate(c),a[f].accumulate(d,e)}}},_updateWeight:function(a){var b=0;if(this.enabled){b=this.weight;var c=this._weightInterpolant;if(c!==null){var d=c.evaluate(a)[0];b*=d;a>c.parameterPositions[1]&&(this.stopFading(),d===0&&(this.enabled=!1))}}this._effectiveWeight=b;return b},_updateTimeScale:function(a){var b=0;if(!this.paused){b=this.timeScale;var c=this._timeScaleInterpolant;if(c!==null){var d=c.evaluate(a)[0];b*=d;a>c.parameterPositions[1]&&(this.stopWarping(),b===0?this.paused=!0:this.timeScale=b)}}this._effectiveTimeScale=b;return b},_updateTime:function(a){var b=this.time+a,c=this._clip.duration,d=this.loop,e=this._loopCount,f=d===dc;if(a===0)return e===-1?b:f&&(e&1)===1?c-b:b;if(d===bc){e===-1&&(this._loopCount=0,this._setEndings(!0,!0,!1));handle_stop:{if(b>=c)b=c;else if(b<0)b=0;else{this.time=b;break handle_stop}this.clampWhenFinished?this.paused=!0:this.enabled=!1;this.time=b;this._mixer.dispatchEvent({type:"finished",action:this,direction:a<0?-1:1})}}else{e===-1&&(a>=0?(e=0,this._setEndings(!0,this.repetitions===0,f)):this._setEndings(this.repetitions===0,!0,f));if(b>=c||b<0){d=Math.floor(b/c);b-=c*d;e+=Math.abs(d);var g=this.repetitions-e;if(g<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,b=a>0?c:0,this.time=b,this._mixer.dispatchEvent({type:"finished",action:this,direction:a>0?1:-1});else{if(g===1){g=a<0;this._setEndings(g,!g,f)}else this._setEndings(!1,!1,f);this._loopCount=e;this.time=b;this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:d})}}else this.time=b;if(f&&(e&1)===1)return c-b}return b},_setEndings:function(a,b,c){var d=this._interpolantSettings;c?(d.endingStart=ic,d.endingEnd=ic):(a?d.endingStart=this.zeroSlopeAtStart?ic:hc:d.endingStart=jc,b?d.endingEnd=this.zeroSlopeAtEnd?ic:hc:d.endingEnd=jc)},_scheduleFading:function(a,b,c){var d=this._mixer,e=d.time,f=this._weightInterpolant;f===null&&(f=d._lendControlInterpolant(),this._weightInterpolant=f);d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}});function yp(a){this._root=a,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1}yp.prototype=Object.assign(Object.create($c.prototype),{constructor:yp,_bindAction:function(a,b){var c=a._localRoot||this._root,d=a._clip.tracks,e=d.length,f=a._propertyBindings;a=a._interpolants;var g=c.uuid,h=this._bindingsByRootAndName,i=h[g];i===void 0&&(i={},h[g]=i);for(h=0;h!==e;++h){var j=d[h],k=j.name,l=i[k];if(l!==void 0)f[h]=l;else{l=f[h];if(l!==void 0){l._cacheIndex===null&&(++l.referenceCount,this._addInactiveBinding(l,g,k));continue}var m=b&&b._propertyBindings[h].binding.parsedPath;l=new qp(vp.create(c,k,m),j.ValueTypeName,j.getValueSize());++l.referenceCount;this._addInactiveBinding(l,g,k);f[h]=l}a[h].resultBuffer=l.buffer}},_activateAction:function(a){if(!this._isActiveAction(a)){if(a._cacheIndex===null){var b=(a._localRoot||this._root).uuid,c=a._clip.uuid,d=this._actionsByClip[c];this._bindAction(a,d&&d.knownActions[0]);this._addInactiveAction(a,c,b)}d=a._propertyBindings;for(c=0,b=d.length;c!==b;++c){var e=d[c];e.useCount++===0&&(this._lendBinding(e),e.saveOriginalState())}this._lendAction(a)}},_deactivateAction:function(b){if(this._isActiveAction(b)){var c=b._propertyBindings;for(var a=0,d=c.length;a!==d;++a){var e=c[a];--e.useCount===0&&(e.restoreOriginalState(),this._takeBackBinding(e))}this._takeBackAction(b)}},_initMemoryManager:function(){this._actions=[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;var a=this;this.stats={actions:{get total(){return a._actions.length},get inUse(){return a._nActiveActions}},bindings:{get total(){return a._bindings.length},get inUse(){return a._nActiveBindings}},controlInterpolants:{get total(){return a._controlInterpolants.length},get inUse(){return a._nActiveControlInterpolants}}}},_isActiveAction:function(a){a=a._cacheIndex;return a!==null&&athis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y},getParameter:function(a,b){b===void 0&&(b=new O());return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y?!1:!0},clampPoint:function(a,b){b===void 0&&(b=new O());return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(a){var b=Gp.copy(a).clamp(this.min,this.max);return b.sub(a).length()},intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});var Ip=new P(),Jp=new P();function Kp(a,b){this.start=a!==void 0?a:new P(),this.end=b!==void 0?b:new P()}Object.assign(Kp.prototype,{set:function(a,b){this.start.copy(a);this.end.copy(b);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},getCenter:function(a){a===void 0&&(a=new P());return a.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){a===void 0&&(a=new P());return a.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,b){b===void 0&&(b=new P());return this.delta(b).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(a,b){Ip.subVectors(a,this.start);Jp.subVectors(this.end,this.start);a=Jp.dot(Jp);var c=Jp.dot(Ip);c=c/a;b&&(c=N.clamp(c,0,1));return c},closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c===void 0&&(c=new P());return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)}});function Lp(a){R.call(this),this.material=a,this.render=function(){}}Lp.prototype=Object.create(R.prototype);Lp.prototype.constructor=Lp;Lp.prototype.isImmediateRenderObject=!0;var Mp=new P();function Np(b,c){R.call(this);this.light=b;this.light.updateMatrixWorld();this.matrix=b.matrixWorld;this.matrixAutoUpdate=!1;this.color=c;b=new W();c=[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];for(var a=0,d=1,e=32;a.99999)this.quaternion.set(0,0,0,1);else if(a.y<-.99999)this.quaternion.set(1,0,0,0);else{lq.set(a.z,0,-a.x).normalize();a=Math.acos(a.y);this.quaternion.setFromAxisAngle(lq,a)}};oq.prototype.setLength=function(a,b,c){b===void 0&&(b=.2*a),c===void 0&&(c=.2*b),this.line.scale.set(1,Math.max(1e-4,a-b),1),this.line.updateMatrix(),this.cone.scale.set(c,b,c),this.cone.position.y=a,this.cone.updateMatrix()};oq.prototype.setColor=function(a){this.line.material.color.set(a),this.cone.material.color.set(a)};oq.prototype.copy=function(a){R.prototype.copy.call(this,a,!1);this.line.copy(a.line);this.cone.copy(a.cone);return this};oq.prototype.clone=function(){return new this.constructor().copy(this)};function pq(a){a=a||1;a=[0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a];var b=[1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],c=new W();c.setAttribute("position",new V(a,3));c.setAttribute("color",new V(b,3));a=new il({vertexColors:!0,toneMapped:!1});rl.call(this,c,a);this.type="AxesHelper"}pq.prototype=Object.create(rl.prototype);pq.prototype.constructor=pq;var qq=4,rq=8,sq=Math.pow(2,rq),tq=[.125,.215,.35,.446,.526,.582],uq=rq-qq+1+tq.length,vq=20,wq={};wq[pc]=0;wq[qc]=1;wq[sc]=2;wq[uc]=3;wq[vc]=4;wq[wc]=5;wq[rc]=6;var xq=new zo();$f=Fq();var yq=$f._lodPlanes,zq=$f._sizeLods,Aq=$f._sigmas,Bq=null;ag=(1+Math.sqrt(5))/2;bg=1/ag;var Cq=[new P(1,1,1),new P(-1,1,1),new P(1,1,-1),new P(-1,1,-1),new P(0,ag,bg),new P(0,ag,-bg),new P(bg,0,ag),new P(-bg,0,ag),new P(ag,bg,0),new P(-ag,bg,0)];function Dq(a){this._renderer=a,this._pingPongRenderTarget=null,this._blurMaterial=Iq(vq),this._equirectShader=null,this._cubemapShader=null,this._compileMaterial(this._blurMaterial)}Dq.prototype={constructor:Dq,fromScene:function(a,b,c,d){b===void 0&&(b=0);c===void 0&&(c=.1);d===void 0&&(d=100);Bq=this._renderer.getRenderTarget();var e=this._allocateTargets();this._sceneToCubeUV(a,c,d,e);b>0&&this._blur(e,0,0,b);this._applyPMREM(e);this._cleanup(e);return e},fromEquirectangular:function(a){a.magFilter=Fa;a.minFilter=Fa;a.generateMipmaps=!1;return this.fromCubemap(a)},fromCubemap:function(a){Bq=this._renderer.getRenderTarget();var b=this._allocateTargets(a);this._textureToCubeUV(a,b);this._applyPMREM(b);this._cleanup(b);return b},compileCubemapShader:function(){this._cubemapShader===null&&(this._cubemapShader=Kq(),this._compileMaterial(this._cubemapShader))},compileEquirectangularShader:function(){this._equirectShader===null&&(this._equirectShader=Jq(),this._compileMaterial(this._equirectShader))},dispose:function(){this._blurMaterial.dispose();this._cubemapShader!==null&&this._cubemapShader.dispose();this._equirectShader!==null&&this._equirectShader.dispose();for(var a=0;a2?sq:0,sq,sq);e.setRenderTarget(f);e.render(c,a)}e.toneMapping=h;e.toneMappingExposure=i;e.outputEncoding=g;e.setClearColor(j,k);c.scale.z*=-1},_textureToCubeUV:function(a,b){var c=new Ld(),d=this._renderer;a.isCubeTexture?this._cubemapShader==null&&(this._cubemapShader=Kq()):this._equirectShader==null&&(this._equirectShader=Jq());var e=a.isCubeTexture?this._cubemapShader:this._equirectShader;c.add(new rf(yq[0],e));e=e.uniforms;e.envMap.value=a;a.isCubeTexture||e.texelSize.value.set(1/a.image.width,1/a.image.height);e.inputEncoding.value=wq[a.encoding];e.outputEncoding.value=wq[b.texture.encoding];Hq(b,0,0,3*sq,2*sq);d.setRenderTarget(b);d.render(c,xq)},_applyPMREM:function(b){var c=this._renderer,d=c.autoClear;c.autoClear=!1;for(var a=1;avq;k=[];var o=0;for(var a=0;arq-qq?e-rq+qq:0);Hq(c,p,n,3*q,2*q);i.setRenderTarget(c);i.render(l,xq)}};function Eq(a){return a===void 0||a.type!==Pa?!1:a.encoding===pc||a.encoding===qc||a.encoding===rc}function Fq(){var b=[],c=[],d=[],e=rq;for(var a=0;arq-qq?g=tq[a-rq+qq-1]:a==0&&(g=0);d.push(g);g=1/(f-1);f=-g/2;g=1+g/2;f=[f,f,g,f,g,g,f,f,g,g,f,g];g=6;var h=6,i=3,j=2,k=1,l=new Float32Array(i*h*g),m=new Float32Array(j*h*g),n=new Float32Array(k*h*g);for(var o=0;o2?0:-1;p=[p,q,0,p+2/3,q,0,p+2/3,q+1,0,p,q,0,p+2/3,q+1,0,p,q+1,0];l.set(p,i*h*o);m.set(f,j*h*o);q=[o,o,o,o,o,o];n.set(q,k*h*o)}p=new W();p.setAttribute("position",new U(l,i));p.setAttribute("uv",new U(m,j));p.setAttribute("faceIndex",new U(n,k));b.push(p);e>qq&&e--}return{_lodPlanes:b,_sizeLods:c,_sigmas:d}}function Gq(a){a=new id(3*sq,3*sq,a);a.texture.mapping=Aa;a.texture.name="PMREM.cubeUv";a.scissorTest=!0;return a}function Hq(a,b,c,d,e){a.viewport.set(b,c,d,e),a.scissor.set(b,c,d,e)}function Iq(a){var b=new Float32Array(a),c=new P(0,1,0);a=new cn({defines:{n:a},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:b},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:c},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[n]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; "+Mq()+" #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample(float theta, vec3 axis) { float cosTheta = cos(theta); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross(axis, vOutputDirection) * sin(theta) + axis * dot(axis, vOutputDirection) * (1.0 - cosTheta); return bilinearCubeUV(envMap, sampleDirection, mipInt); } void main() { vec3 axis = latitudinal ? poleAxis : cross(poleAxis, vOutputDirection); if (all(equal(axis, vec3(0.0)))) axis = vec3(vOutputDirection.z, 0.0, - vOutputDirection.x); axis = normalize(axis); gl_FragColor = vec4(0.0); gl_FragColor.rgb += weights[0] * getSample(0.0, axis); for (int i = 1; i < n; i++) { if (i >= samples) break; float theta = dTheta * float(i); gl_FragColor.rgb += weights[i] * getSample(-1.0 * theta, axis); gl_FragColor.rgb += weights[i] * getSample(theta, axis); } gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="SphericalGaussianBlur";return a}function Jq(){var a=new O(1,1);a=new cn({uniforms:{envMap:{value:null},texelSize:{value:a},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; "+Mq()+" #define RECIPROCAL_PI 0.31830988618 #define RECIPROCAL_PI2 0.15915494 void main() { gl_FragColor = vec4(0.0); vec3 outputDirection = normalize(vOutputDirection); vec2 uv; uv.y = asin(clamp(outputDirection.y, -1.0, 1.0)) * RECIPROCAL_PI + 0.5; uv.x = atan(outputDirection.z, outputDirection.x) * RECIPROCAL_PI2 + 0.5; vec2 f = fract(uv / texelSize - 0.5); uv -= f * texelSize; vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.x += texelSize.x; vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.y += texelSize.y; vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.x -= texelSize.x; vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb; vec3 tm = mix(tl, tr, f.x); vec3 bm = mix(bl, br, f.x); gl_FragColor.rgb = mix(tm, bm, f.y); gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="EquirectangularToCubeUV";return a}function Kq(){var a=new cn({uniforms:{envMap:{value:null},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform samplerCube envMap; "+Mq()+" void main() { gl_FragColor = vec4(0.0); gl_FragColor.rgb = envMapTexelToLinear(textureCube(envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ))).rgb; gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="CubemapToCubeUV";return a}function Lq(){return" precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; vec3 getDirection(vec2 uv, float face) { uv = 2.0 * uv - 1.0; vec3 direction = vec3(uv, 1.0); if (face == 0.0) { direction = direction.zyx; direction.z *= -1.0; } else if (face == 1.0) { direction = direction.xzy; direction.z *= -1.0; } else if (face == 3.0) { direction = direction.zyx; direction.x *= -1.0; } else if (face == 4.0) { direction = direction.xzy; direction.y *= -1.0; } else if (face == 5.0) { direction.xz *= -1.0; } return direction; } void main() { vOutputDirection = getDirection(uv, faceIndex); gl_Position = vec4( position, 1.0 ); } "}function Mq(){return" uniform int inputEncoding; uniform int outputEncoding; #include vec4 inputTexelToLinear(vec4 value){ if(inputEncoding == 0){ return value; }else if(inputEncoding == 1){ return sRGBToLinear(value); }else if(inputEncoding == 2){ return RGBEToLinear(value); }else if(inputEncoding == 3){ return RGBMToLinear(value, 7.0); }else if(inputEncoding == 4){ return RGBMToLinear(value, 16.0); }else if(inputEncoding == 5){ return RGBDToLinear(value, 256.0); }else{ return GammaToLinear(value, 2.2); } } vec4 linearToOutputTexel(vec4 value){ if(outputEncoding == 0){ return value; }else if(outputEncoding == 1){ return LinearTosRGB(value); }else if(outputEncoding == 2){ return LinearToRGBE(value); }else if(outputEncoding == 3){ return LinearToRGBM(value, 7.0); }else if(outputEncoding == 4){ return LinearToRGBM(value, 16.0); }else if(outputEncoding == 5){ return LinearToRGBD(value, 256.0); }else{ return LinearToGamma(value, 2.2); } } vec4 envMapTexelToLinear(vec4 color) { return inputTexelToLinear(color); } "}function Nq(a,b,c,d,e,f,g){return new Fe(a,b,c,e,f,g)}cg=0;dg=1;eg=0;fg=1;gg=2;function Oq(a){return a}function Pq(a){a===void 0&&(a=[]);a.isMultiMaterial=!0;a.materials=a;a.clone=function(){return a.slice()};return a}function Qq(a,b){return new yl(a,b)}function Rq(a){return new Uk(a)}function Sq(a,b){return new yl(a,b)}function Tq(a){return new tl(a)}function Uq(a){return new tl(a)}function Vq(a){return new tl(a)}function Wq(a,b,c){return new P(a,b,c)}function Xq(a,b){return new U(a,b).setUsage(Sc)}function Yq(a,b){return new Je(a,b)}function Zq(a,b){return new Ke(a,b)}function $q(a,b){return new Le(a,b)}function ar(a,b){return new Me(a,b)}function br(a,b){return new Ne(a,b)}function cr(a,b){return new Oe(a,b)}function dr(a,b){return new Pe(a,b)}function er(a,b){return new V(a,b)}function fr(a,b){return new Qe(a,b)}$.create=function(a,b){a.prototype=Object.create($.prototype);a.prototype.constructor=a;a.prototype.getPoint=b;return a};Object.assign(po.prototype,{createPointsGeometry:function(a){a=this.getPoints(a);return this.createGeometry(a)},createSpacedPointsGeometry:function(a){a=this.getSpacedPoints(a);return this.createGeometry(a)},createGeometry:function(b){var c=new X();for(var a=0,d=b.length;a-----
ThreeJS.r93",[],(function $module_ThreeJS_r93(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; var exports = {}; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (factory((global.THREE = {}))); }(this, (function (exports) { "use strict"; // Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } if ( Number.isInteger === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger Number.isInteger = function ( value ) { return typeof value === "number" && isFinite( value ) && Math.floor( value ) === value; }; } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( "name" in Function.prototype === false ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, "name", { get: function () { return this.toString().match( /^s*functions*([^(s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( "Cannot convert undefined or null to object" ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; var listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } }, hasEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return false; var listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { var index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; var array = listenerArray.slice( 0 ); for ( var i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } } } } ); var REVISION = "93"; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; var CullFaceFront = 2; var CullFaceFrontBack = 3; var FrontFaceDirectionCW = 0; var FrontFaceDirectionCCW = 1; var BasicShadowMap = 0; var PCFShadowMap = 1; var PCFSoftShadowMap = 2; var FrontSide = 0; var BackSide = 1; var DoubleSide = 2; var FlatShading = 1; var SmoothShading = 2; var NoColors = 0; var FaceColors = 1; var VertexColors = 2; var NoBlending = 0; var NormalBlending = 1; var AdditiveBlending = 2; var SubtractiveBlending = 3; var MultiplyBlending = 4; var CustomBlending = 5; var AddEquation = 100; var SubtractEquation = 101; var ReverseSubtractEquation = 102; var MinEquation = 103; var MaxEquation = 104; var ZeroFactor = 200; var OneFactor = 201; var SrcColorFactor = 202; var OneMinusSrcColorFactor = 203; var SrcAlphaFactor = 204; var OneMinusSrcAlphaFactor = 205; var DstAlphaFactor = 206; var OneMinusDstAlphaFactor = 207; var DstColorFactor = 208; var OneMinusDstColorFactor = 209; var SrcAlphaSaturateFactor = 210; var NeverDepth = 0; var AlwaysDepth = 1; var LessDepth = 2; var LessEqualDepth = 3; var EqualDepth = 4; var GreaterEqualDepth = 5; var GreaterDepth = 6; var NotEqualDepth = 7; var MultiplyOperation = 0; var MixOperation = 1; var AddOperation = 2; var NoToneMapping = 0; var LinearToneMapping = 1; var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; var EquirectangularReflectionMapping = 303; var EquirectangularRefractionMapping = 304; var SphericalReflectionMapping = 305; var CubeUVReflectionMapping = 306; var CubeUVRefractionMapping = 307; var RepeatWrapping = 1000; var ClampToEdgeWrapping = 1001; var MirroredRepeatWrapping = 1002; var NearestFilter = 1003; var NearestMipMapNearestFilter = 1004; var NearestMipMapLinearFilter = 1005; var LinearFilter = 1006; var LinearMipMapNearestFilter = 1007; var LinearMipMapLinearFilter = 1008; var UnsignedByteType = 1009; var ByteType = 1010; var ShortType = 1011; var UnsignedShortType = 1012; var IntType = 1013; var UnsignedIntType = 1014; var FloatType = 1015; var HalfFloatType = 1016; var UnsignedShort4444Type = 1017; var UnsignedShort5551Type = 1018; var UnsignedShort565Type = 1019; var UnsignedInt248Type = 1020; var AlphaFormat = 1021; var RGBFormat = 1022; var RGBAFormat = 1023; var LuminanceFormat = 1024; var LuminanceAlphaFormat = 1025; var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; var RGB_S3TC_DXT1_Format = 33776; var RGBA_S3TC_DXT1_Format = 33777; var RGBA_S3TC_DXT3_Format = 33778; var RGBA_S3TC_DXT5_Format = 33779; var RGB_PVRTC_4BPPV1_Format = 35840; var RGB_PVRTC_2BPPV1_Format = 35841; var RGBA_PVRTC_4BPPV1_Format = 35842; var RGBA_PVRTC_2BPPV1_Format = 35843; var RGB_ETC1_Format = 36196; var RGBA_ASTC_4x4_Format = 37808; var RGBA_ASTC_5x4_Format = 37809; var RGBA_ASTC_5x5_Format = 37810; var RGBA_ASTC_6x5_Format = 37811; var RGBA_ASTC_6x6_Format = 37812; var RGBA_ASTC_8x5_Format = 37813; var RGBA_ASTC_8x6_Format = 37814; var RGBA_ASTC_8x8_Format = 37815; var RGBA_ASTC_10x5_Format = 37816; var RGBA_ASTC_10x6_Format = 37817; var RGBA_ASTC_10x8_Format = 37818; var RGBA_ASTC_10x10_Format = 37819; var RGBA_ASTC_12x10_Format = 37820; var RGBA_ASTC_12x12_Format = 37821; var LoopOnce = 2200; var LoopRepeat = 2201; var LoopPingPong = 2202; var InterpolateDiscrete = 2300; var InterpolateLinear = 2301; var InterpolateSmooth = 2302; var ZeroCurvatureEnding = 2400; var ZeroSlopeEnding = 2401; var WrapAroundEnding = 2402; var TrianglesDrawMode = 0; var TriangleStripDrawMode = 1; var TriangleFanDrawMode = 2; var LinearEncoding = 3000; var sRGBEncoding = 3001; var GammaEncoding = 3007; var RGBEEncoding = 3002; var LogLuvEncoding = 3003; var RGBM7Encoding = 3004; var RGBM16Encoding = 3005; var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var _Math = { DEG2RAD: Math.PI / 180, RAD2DEG: 180 / Math.PI, generateUUID: ( function () { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 var lut = []; for ( var i = 0; i < 256; i ++ ) { lut[ i ] = ( i < 16 ? "0" : "" ) + ( i ).toString( 16 ); } return function generateUUID() { var d0 = Math.random() * 0xffffffff | 0; var d1 = Math.random() * 0xffffffff | 0; var d2 = Math.random() * 0xffffffff | 0; var d3 = Math.random() * 0xffffffff | 0; var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + "-" + lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + "-" + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + "-" + lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + "-" + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] + lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); }; } )(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, // Linear mapping from range to range mapLinear: function ( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); }, // https://en.wikipedia.org/wiki/Linear_interpolation lerp: function ( x, y, t ) { return ( 1 - t ) * x + t * y; }, // http://en.wikipedia.org/wiki/Smoothstep smoothstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); }, smootherstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); }, // Random integer from interval randInt: function ( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, // Random float from interval randFloat: function ( low, high ) { return low + Math.random() * ( high - low ); }, // Random float from <-range/2, range/2> interval randFloatSpread: function ( range ) { return range * ( 0.5 - Math.random() ); }, degToRad: function ( degrees ) { return degrees * _Math.DEG2RAD; }, radToDeg: function ( radians ) { return radians * _Math.RAD2DEG; }, isPowerOfTwo: function ( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; }, ceilPowerOfTwo: function ( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); }, floorPowerOfTwo: function ( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author philogb / http://blog.thejit.org/ * @author egraether / http://egraether.com/ * @author zz85 / http://www.lab4games.net/zz85/blog */ function Vector2( x, y ) { this.x = x || 0; this.y = y || 0; } Object.defineProperties( Vector2.prototype, { "width": { get: function () { return this.x; }, set: function ( value ) { this.x = value; } }, "height": { get: function () { return this.y; }, set: function ( value ) { this.y = value; } } } ); Object.assign( Vector2.prototype, { isVector2: true, set: function ( x, y ) { this.x = x; this.y = y; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, applyMatrix3: function ( m ) { var x = this.x, y = this.y; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; }, clampScalar: function () { var min = new Vector2(); var max = new Vector2(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal ); max.set( maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y; }, lengthSq: function () { return this.x * this.x + this.y * this.y; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, angle: function () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector2: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; }, rotateAround: function ( center, angle ) { var c = Math.cos( angle ), s = Math.sin( angle ); var x = this.x - center.x; var y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author jordi_ros / http://plattsoft.com * @author D1plo1d / http://github.com/D1plo1d * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Matrix4() { this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix4: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix4.prototype, { isMatrix4: true, set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { var te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; }, identity: function () { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, clone: function () { return new Matrix4().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; }, copyPosition: function ( m ) { var te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; }, extractBasis: function ( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; }, makeBasis: function ( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; }, extractRotation: function () { var v1 = new Vector3(); return function extractRotation( m ) { // this method does not support reflection matrices var te = this.elements; var me = m.elements; var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }; }(), makeRotationFromEuler: function ( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order." ); } var te = this.elements; var x = euler.x, y = euler.y, z = euler.z; var a = Math.cos( x ), b = Math.sin( x ); var c = Math.cos( y ), d = Math.sin( y ); var e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, makeRotationFromQuaternion: function () { var zero = new Vector3( 0, 0, 0 ); var one = new Vector3( 1, 1, 1 ); return function makeRotationFromQuaternion( q ) { return this.compose( zero, q, one ); }; }(), lookAt: function () { var x = new Vector3(); var y = new Vector3(); var z = new Vector3(); return function lookAt( eye, target, up ) { var te = this.elements; z.subVectors( eye, target ); if ( z.lengthSq() === 0 ) { // eye and target are in the same position z.z = 1; } z.normalize(); x.crossVectors( up, z ); if ( x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { z.x += 0.0001; } else { z.z += 0.0001; } z.normalize(); x.crossVectors( up, z ); } x.normalize(); y.crossVectors( z, x ); te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; return this; }; }(), multiply: function ( m, n ) { if ( n !== undefined ) { console.warn( "THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead." ); return this.multiplyMatrices( m, n ); } return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix4( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), determinant: function () { var te = this.elements; var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); }, transpose: function () { var te = this.elements; var tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; }, setPosition: function ( v ) { var te = this.elements; te[ 12 ] = v.x; te[ 13 ] = v.y; te[ 14 ] = v.z; return this; }, getInverse: function ( m, throwOnDegenerate ) { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm var te = this.elements, me = m.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) { var msg = "THREE.Matrix4: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; }, scale: function ( v ) { var te = this.elements; var x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; }, getMaxScaleOnAxis: function () { var te = this.elements; var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, makeTranslation: function ( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; }, makeRotationX: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationY: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationZ: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, makeRotationAxis: function ( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp var c = Math.cos( angle ); var s = Math.sin( angle ); var t = 1 - c; var x = axis.x, y = axis.y, z = axis.z; var tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; }, makeScale: function ( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; }, makeShear: function ( x, y, z ) { this.set( 1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1 ); return this; }, compose: function ( position, quaternion, scale ) { var te = this.elements; var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; var x2 = x + x, y2 = y + y, z2 = z + z; var xx = x * x2, xy = x * y2, xz = x * z2; var yy = y * y2, yz = y * z2, zz = z * z2; var wx = w * x2, wy = w * y2, wz = w * z2; var sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; }, decompose: function () { var vector = new Vector3(); var matrix = new Matrix4(); return function decompose( position, quaternion, scale ) { var te = this.elements; var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale var det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part matrix.copy( this ); var invSX = 1 / sx; var invSY = 1 / sy; var invSZ = 1 / sz; matrix.elements[ 0 ] *= invSX; matrix.elements[ 1 ] *= invSX; matrix.elements[ 2 ] *= invSX; matrix.elements[ 4 ] *= invSY; matrix.elements[ 5 ] *= invSY; matrix.elements[ 6 ] *= invSY; matrix.elements[ 8 ] *= invSZ; matrix.elements[ 9 ] *= invSZ; matrix.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( matrix ); scale.x = sx; scale.y = sy; scale.z = sz; return this; }; }(), makePerspective: function ( left, right, top, bottom, near, far ) { if ( far === undefined ) { console.warn( "THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs." ); } var te = this.elements; var x = 2 * near / ( right - left ); var y = 2 * near / ( top - bottom ); var a = ( right + left ) / ( right - left ); var b = ( top + bottom ) / ( top - bottom ); var c = - ( far + near ) / ( far - near ); var d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; }, makeOrthographic: function ( left, right, top, bottom, near, far ) { var te = this.elements; var w = 1.0 / ( right - left ); var h = 1.0 / ( top - bottom ); var p = 1.0 / ( far - near ); var x = ( right + left ) * w; var y = ( top + bottom ) * h; var z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Object.assign( Quaternion, { slerp: function ( qa, qb, qm, t ) { return qm.copy( qa ).slerp( qb, t ); }, slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation var x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ], x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { var s = 1 - t, cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { var sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } var tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } } ); Object.defineProperties( Quaternion.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, w: { get: function () { return this._w; }, set: function ( value ) { this._w = value; this.onChangeCallback(); } } } ); Object.assign( Quaternion.prototype, { set: function ( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._w ); }, copy: function ( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this.onChangeCallback(); return this; }, setFromEuler: function ( euler, update ) { if ( ! ( euler && euler.isEuler ) ) { throw new Error( "THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order." ); } var x = euler._x, y = euler._y, z = euler._z, order = euler.order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m var cos = Math.cos; var sin = Math.sin; var c1 = cos( x / 2 ); var c2 = cos( y / 2 ); var c3 = cos( z / 2 ); var s1 = sin( x / 2 ); var s2 = sin( y / 2 ); var s3 = sin( z / 2 ); if ( order === "XYZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "YXZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "ZXY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "ZYX" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "YZX" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "XZY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } if ( update !== false ) this.onChangeCallback(); return this; }, setFromAxisAngle: function ( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this.onChangeCallback(); return this; }, setFromUnitVectors: function () { // assumes direction vectors vFrom and vTo are normalized var v1 = new Vector3(); var r; var EPS = 0.000001; return function setFromUnitVectors( vFrom, vTo ) { if ( v1 === undefined ) v1 = new Vector3(); r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { v1.set( - vFrom.y, vFrom.x, 0 ); } else { v1.set( 0, - vFrom.z, vFrom.y ); } } else { v1.crossVectors( vFrom, vTo ); } this._x = v1.x; this._y = v1.y; this._z = v1.z; this._w = r; return this.normalize(); }; }(), inverse: function () { // quaternion is assumed to have unit length return this.conjugate(); }, conjugate: function () { this._x *= - 1; this._y *= - 1; this._z *= - 1; this.onChangeCallback(); return this; }, dot: function ( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; }, lengthSq: function () { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { var l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this.onChangeCallback(); return this; }, multiply: function ( q, p ) { if ( p !== undefined ) { console.warn( "THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead." ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); }, premultiply: function ( q ) { return this.multiplyQuaternions( q, this ); }, multiplyQuaternions: function ( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this.onChangeCallback(); return this; }, slerp: function ( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); var x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); if ( Math.abs( sinHalfTheta ) < 0.001 ) { this._w = 0.5 * ( w + this._w ); this._x = 0.5 * ( x + this._x ); this._y = 0.5 * ( y + this._y ); this._z = 0.5 * ( z + this._z ); return this; } var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this.onChangeCallback(); return this; }, equals: function ( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Object.assign( Vector3.prototype, { isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiply: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead." ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; }, multiplyVectors: function ( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; }, applyEuler: function () { var quaternion = new Quaternion(); return function applyEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order." ); } return this.applyQuaternion( quaternion.setFromEuler( euler ) ); }; }(), applyAxisAngle: function () { var quaternion = new Quaternion(); return function applyAxisAngle( axis, angle ) { return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); }; }(), applyMatrix3: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; }, applyQuaternion: function ( q ) { var x = this.x, y = this.y, z = this.z; var qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; }, project: function () { var matrix = new Matrix4(); return function project( camera ) { matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); return this.applyMatrix4( matrix ); }; }(), unproject: function () { var matrix = new Matrix4(); return function unproject( camera ) { matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); return this.applyMatrix4( matrix ); }; }(), transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, clampScalar: function () { var min = new Vector3(); var max = new Vector3(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, // TODO lengthSquared? lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, cross: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead." ); return this.crossVectors( v, w ); } return this.crossVectors( this, v ); }, crossVectors: function ( a, b ) { var ax = a.x, ay = a.y, az = a.z; var bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; }, projectOnVector: function ( vector ) { var scalar = vector.dot( this ) / vector.lengthSq(); return this.copy( vector ).multiplyScalar( scalar ); }, projectOnPlane: function () { var v1 = new Vector3(); return function projectOnPlane( planeNormal ) { v1.copy( this ).projectOnVector( planeNormal ); return this.sub( v1 ); }; }(), reflect: function () { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length var v1 = new Vector3(); return function reflect( normal ) { return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); }; }(), angleTo: function ( v ) { var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); // clamp, to handle numerical problems return Math.acos( _Math.clamp( theta, - 1, 1 ) ); }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); }, setFromSpherical: function ( s ) { var sinPhiRadius = Math.sin( s.phi ) * s.radius; this.x = sinPhiRadius * Math.sin( s.theta ); this.y = Math.cos( s.phi ) * s.radius; this.z = sinPhiRadius * Math.cos( s.theta ); return this; }, setFromCylindrical: function ( c ) { this.x = c.radius * Math.sin( c.theta ); this.y = c.y; this.z = c.radius * Math.cos( c.theta ); return this; }, setFromMatrixPosition: function ( m ) { var e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; }, setFromMatrixScale: function ( m ) { var sx = this.setFromMatrixColumn( m, 0 ).length(); var sy = this.setFromMatrixColumn( m, 1 ).length(); var sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; }, setFromMatrixColumn: function ( m, index ) { return this.fromArray( m.elements, index * 4 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector3: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } } ); /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io * @author tschw */ function Matrix3() { this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix3: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix3.prototype, { isMatrix3: true, set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { var te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; }, identity: function () { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; }, clone: function () { return new this.constructor().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; }, setFromMatrix4: function ( m ) { var me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix3( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), multiply: function ( m ) { return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; }, determinant: function () { var te = this.elements; var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; }, getInverse: function ( matrix, throwOnDegenerate ) { if ( matrix && matrix.isMatrix4 ) { console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); } var me = matrix.elements, te = this.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) { var msg = "THREE.Matrix3: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; }, transpose: function () { var tmp, m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; }, getNormalMatrix: function ( matrix4 ) { return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); }, transposeIntoArray: function ( r ) { var m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; }, setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { var c = Math.cos( rotation ); var s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); }, scale: function ( sx, sy ) { var te = this.elements; te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; return this; }, rotate: function ( theta ) { var c = Math.cos( theta ); var s = Math.sin( theta ); var te = this.elements; var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; te[ 0 ] = c * a11 + s * a21; te[ 3 ] = c * a12 + s * a22; te[ 6 ] = c * a13 + s * a23; te[ 1 ] = - s * a11 + c * a21; te[ 4 ] = - s * a12 + c * a22; te[ 7 ] = - s * a13 + c * a23; return this; }, translate: function ( tx, ty ) { var te = this.elements; te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ var textureId = 0; function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; this.mipmaps = []; this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; this.anisotropy = anisotropy !== undefined ? anisotropy : 1; this.format = format !== undefined ? format : RGBAFormat; this.type = type !== undefined ? type : UnsignedByteType; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding !== undefined ? encoding : LinearEncoding; this.version = 0; this.onUpdate = null; } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Texture, isTexture: true, 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 ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; return this; }, toJSON: function ( meta ) { var isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } function getDataURL( image ) { var canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } } if ( canvas.width > 2048 || canvas.height > 2048 ) { return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } var output = { metadata: { version: 4.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, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY }; if ( this.image !== undefined ) { // TODO: Move to THREE.Image var image = this.image; if ( image.uuid === undefined ) { image.uuid = _Math.generateUUID(); // UGH } if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { meta.images[ image.uuid ] = { uuid: image.uuid, url: getDataURL( image ) }; } output.image = image.uuid; } if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); }, transformUv: function ( uv ) { if ( this.mapping !== UVMapping ) return; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } } } ); Object.defineProperty( Texture.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector4( x, y, z, w ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = ( w !== undefined ) ? w : 1; } Object.assign( Vector4.prototype, { isVector4: true, set: function ( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setW: function ( w ) { this.w = w; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z, this.w ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z, w = this.w; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, setAxisAngleFromQuaternion: function ( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); var s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; }, setAxisAngleFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var angle, x, y, z, // variables for result epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; var xx = ( m11 + 1 ) / 2; var yy = ( m22 + 1 ) / 2; var zz = ( m33 + 1 ) / 2; var xy = ( m12 + m21 ) / 4; var xz = ( m13 + m31 ) / 4; var yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector4(); max = new Vector4(); } min.set( minVal, minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector4: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } } ); /** * @author szimek / https://github.com/szimek/ * @author alteredq / http://alteredqualia.com/ * @author Marius Kintel / https://github.com/kintel */ /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ function WebGLRenderTarget( width, height, options ) { this.width = width; this.height = height; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); options = options || {}; if ( options.minFilter === undefined ) options.minFilter = LinearFilter; this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: WebGLRenderTarget, isWebGLRenderTarget: true, setSize: function ( width, height ) { if ( this.width !== width || this.height !== height ) { this.width = width; this.height = height; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.width = source.width; this.height = source.height; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author alteredq / http://alteredqualia.com */ function WebGLRenderTargetCube( width, height, options ) { WebGLRenderTarget.call( this, width, height, options ); this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 this.activeMipMapLevel = 0; } WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; /** * @author alteredq / http://alteredqualia.com/ */ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } DataTexture.prototype = Object.create( Texture.prototype ); DataTexture.prototype.constructor = DataTexture; DataTexture.prototype.isDataTexture = true; /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Box3( min, max ) { this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); } Object.assign( Box3.prototype, { isBox3: true, set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromArray: function ( array ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = array.length; i < l; i += 3 ) { var x = array[ i ]; var y = array[ i + 1 ]; var z = array[ i + 2 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromBufferAttribute: function ( attribute ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = attribute.count; i < l; i ++ ) { var x = attribute.getX( i ); var y = attribute.getY( i ); var z = attribute.getZ( i ); if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector3(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), setFromObject: function ( object ) { this.makeEmpty(); return this.expandByObject( object ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getCenter() target is now required" ); target = new Vector3(); } return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getSize() target is now required" ); target = new Vector3(); } return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, expandByObject: function () { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms var scope, i, l; var v1 = new Vector3(); function traverse( node ) { var geometry = node.geometry; if ( geometry !== undefined ) { if ( geometry.isGeometry ) { var vertices = geometry.vertices; for ( i = 0, l = vertices.length; i < l; i ++ ) { v1.copy( vertices[ i ] ); v1.applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } else if ( geometry.isBufferGeometry ) { var attribute = geometry.attributes.position; if ( attribute !== undefined ) { for ( i = 0, l = attribute.count; i < l; i ++ ) { v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } } } } return function expandByObject( object ) { scope = this; object.updateMatrixWorld( true ); object.traverse( traverse ); return this; }; }(), containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; }, getParameter: function ( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. if ( target === undefined ) { console.warn( "THREE.Box3: .getParameter() target is now required" ); target = new Vector3(); } return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); }, intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; }, intersectsSphere: ( function () { var closestPoint = new Vector3(); return function intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, closestPoint ); // If that point is inside the sphere, the AABB and sphere intersect. return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); }; } )(), intersectsPlane: function ( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. var min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= plane.constant && max >= plane.constant ); }, intersectsTriangle: ( function () { // triangle centered vertices var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); // triangle edge vectors var f0 = new Vector3(); var f1 = new Vector3(); var f2 = new Vector3(); var testAxis = new Vector3(); var center = new Vector3(); var extents = new Vector3(); var triangleNormal = new Vector3(); function satForAxes( axes ) { var i, j; for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { testAxis.fromArray( axes, i ); // project the aabb onto the seperating axis var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z ); // project all 3 vertices of the triangle onto the seperating axis var p0 = v0.dot( testAxis ); var p1 = v1.dot( testAxis ); var p2 = v2.dot( testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } return function intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( center ); extents.subVectors( this.max, center ); // translate triangle to aabb origin v0.subVectors( triangle.a, center ); v1.subVectors( triangle.b, center ); v2.subVectors( triangle.c, center ); // compute edge vectors for triangle f0.subVectors( v1, v0 ); f1.subVectors( v2, v1 ); f2.subVectors( v0, v2 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) var axes = [ 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y, f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x, - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0 ]; if ( ! satForAxes( axes ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here triangleNormal.crossVectors( f0, f1 ); axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ]; return satForAxes( axes ); }; } )(), clampPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .clampPoint() target is now required" ); target = new Vector3(); } return target.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector3(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), getBoundingSphere: function () { var v1 = new Vector3(); return function getBoundingSphere( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getBoundingSphere() target is now required" ); target = new Sphere(); } this.getCenter( target.center ); target.radius = this.getSize( v1 ).length() * 0.5; return target; }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, applyMatrix4: function ( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty( ) ) return this; var m = matrix.elements; var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x; var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x; var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y; var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y; var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z; var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z; this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ]; this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ]; this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ]; this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ]; this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ]; this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ]; return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Sphere( center, radius ) { this.center = ( center !== undefined ) ? center : new Vector3(); this.radius = ( radius !== undefined ) ? radius : 0; } Object.assign( Sphere.prototype, { set: function ( center, radius ) { this.center.copy( center ); this.radius = radius; return this; }, setFromPoints: function () { var box = new Box3(); return function setFromPoints( points, optionalCenter ) { var center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { box.setFromPoints( points ).getCenter( center ); } var maxRadiusSq = 0; for ( var i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; }, empty: function () { return ( this.radius <= 0 ); }, containsPoint: function ( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); }, distanceToPoint: function ( point ) { return ( point.distanceTo( this.center ) - this.radius ); }, intersectsSphere: function ( sphere ) { var radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); }, intersectsBox: function ( box ) { return box.intersectsSphere( this ); }, intersectsPlane: function ( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; }, clampPoint: function ( point, target ) { var deltaLengthSq = this.center.distanceToSquared( point ); if ( target === undefined ) { console.warn( "THREE.Sphere: .clampPoint() target is now required" ); target = new Vector3(); } target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; }, getBoundingBox: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Sphere: .getBoundingBox() target is now required" ); target = new Box3(); } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; }, applyMatrix4: function ( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; }, translate: function ( offset ) { this.center.add( offset ); return this; }, equals: function ( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } } ); /** * @author bhouston / http://clara.io */ function Plane( normal, constant ) { // normal is assumed to be normalized this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); this.constant = ( constant !== undefined ) ? constant : 0; } Object.assign( Plane.prototype, { set: function ( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; }, setComponents: function ( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; }, setFromNormalAndCoplanarPoint: function ( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; }, setFromCoplanarPoints: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function setFromCoplanarPoints( a, b, c ) { var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; }, normalize: function () { // Note: will lead to a divide by zero if the plane is invalid. var inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; }, negate: function () { this.constant *= - 1; this.normal.negate(); return this; }, distanceToPoint: function ( point ) { return this.normal.dot( point ) + this.constant; }, distanceToSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; }, projectPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .projectPoint() target is now required" ); target = new Vector3(); } return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); }, intersectLine: function () { var v1 = new Vector3(); return function intersectLine( line, target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .intersectLine() target is now required" ); target = new Vector3(); } var direction = line.delta( v1 ); var denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return undefined; } var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return undefined; } return target.copy( direction ).multiplyScalar( t ).add( line.start ); }; }(), intersectsLine: function ( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. var startSign = this.distanceToPoint( line.start ); var endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); }, intersectsBox: function ( box ) { return box.intersectsPlane( this ); }, intersectsSphere: function ( sphere ) { return sphere.intersectsPlane( this ); }, coplanarPoint: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .coplanarPoint() target is now required" ); target = new Vector3(); } return target.copy( this.normal ).multiplyScalar( - this.constant ); }, applyMatrix4: function () { var v1 = new Vector3(); var m1 = new Matrix3(); return function applyMatrix4( matrix, optionalNormalMatrix ) { var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; }; }(), translate: function ( offset ) { this.constant -= offset.dot( this.normal ); return this; }, equals: function ( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / http://clara.io */ function Frustum( p0, p1, p2, p3, p4, p5 ) { this.planes = [ ( p0 !== undefined ) ? p0 : new Plane(), ( p1 !== undefined ) ? p1 : new Plane(), ( p2 !== undefined ) ? p2 : new Plane(), ( p3 !== undefined ) ? p3 : new Plane(), ( p4 !== undefined ) ? p4 : new Plane(), ( p5 !== undefined ) ? p5 : new Plane() ]; } Object.assign( Frustum.prototype, { set: function ( p0, p1, p2, p3, p4, p5 ) { var planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( frustum ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; }, setFromMatrix: function ( m ) { var planes = this.planes; var me = m.elements; var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; }, intersectsObject: function () { var sphere = new Sphere(); return function intersectsObject( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ) .applyMatrix4( object.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSprite: function () { var sphere = new Sphere(); return function intersectsSprite( sprite ) { sphere.center.set( 0, 0, 0 ); sphere.radius = 0.7071067811865476; sphere.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSphere: function ( sphere ) { var planes = this.planes; var center = sphere.center; var negRadius = - sphere.radius; for ( var i = 0; i < 6; i ++ ) { var distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; }, intersectsBox: function () { var p1 = new Vector3(), p2 = new Vector3(); return function intersectsBox( box ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { var plane = planes[ i ]; p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; var d1 = plane.distanceToPoint( p1 ); var d2 = plane.distanceToPoint( p2 ); // if both outside plane, no intersection if ( d1 < 0 && d2 < 0 ) { return false; } } return true; }; }(), containsPoint: function ( point ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } } ); var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif "; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif "; var alphatest_fragment = "#ifdef ALPHATEST if ( diffuseColor.a < ALPHATEST ) discard; #endif "; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( PHYSICAL ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness ); #endif #endif "; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = " vec3 transformed = vec3( position ); "; var beginnormal_vertex = " vec3 objectNormal = vec3( normal ); "; var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { if( decayExponent > 0.0 ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); float maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); return distanceFalloff * maxDistanceCutoffFactor; #else return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent ); #endif } return 1.0; } vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) { float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH ); return ( 1.0 - specularColor ) * fresnel + specularColor; } float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); return 1.0 / ( gl * gv ); } float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) ); float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( G * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; return specularColor * AB.x + AB.y; } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) { return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 ); } float BlinnExponentToGGXRoughness( const in float blinnExponent ) { return sqrt( 2.0 / ( blinnExponent + 2.0 ) ); } "; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ); fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 ); vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif "; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard; } #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped; } if ( clipped ) discard; #endif #endif "; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 #if ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif "; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif "; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) vViewPosition = - mvPosition.xyz; #endif "; var color_fragment = "#ifdef USE_COLOR diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#ifdef USE_COLOR varying vec3 vColor; #endif "; var color_pars_vertex = "#ifdef USE_COLOR varying vec3 vColor; #endif"; var color_vertex = "#ifdef USE_COLOR vColor.xyz = color.xyz; #endif"; var common = "#define PI 3.14159265359 #define PI2 6.28318530718 #define PI_HALF 1.5707963267949 #define RECIPROCAL_PI 0.31830988618 #define RECIPROCAL_PI2 0.15915494 #define LOG2 1.442695 #define EPSILON 1e-6 #define saturate(a) clamp( a, 0.0, 1.0 ) #define whiteCompliment(a) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract(sin(sn) * c); } struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { float distance = dot( planeNormal, point - pointOnPlane ); return - distance * planeNormal + point; } float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { return sign( dot( point - pointOnPlane, planeNormal ) ); } vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) { return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine; } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } "; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_textureSize (1024.0) int getFaceFromDirection(vec3 direction) { vec3 absDirection = abs(direction); int face = -1; if( absDirection.x > absDirection.z ) { if(absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0 : 3; else face = direction.y > 0.0 ? 1 : 4; } else { if(absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2 : 5; else face = direction.y > 0.0 ? 1 : 4; } return face; } #define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0) #define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0)) vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) { float scale = exp2(cubeUV_maxLods1 - roughnessLevel); float dxRoughness = dFdx(roughness); float dyRoughness = dFdy(roughness); vec3 dx = dFdx( vec * scale * dxRoughness ); vec3 dy = dFdy( vec * scale * dyRoughness ); float d = max( dot( dx, dx ), dot( dy, dy ) ); d = clamp(d, 1.0, cubeUV_rangeClamp); float mipLevel = 0.5 * log2(d); return vec2(floor(mipLevel), fract(mipLevel)); } #define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0) #define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize) vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) { mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel; float a = 16.0 * cubeUV_rcpTextureSize; vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) ); vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed; float powScale = exp2_packed.x * exp2_packed.y; float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25; float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x; bool bRes = mipLevel == 0.0; scale = bRes && (scale < a) ? a : scale; vec3 r; vec2 offset; int face = getFaceFromDirection(direction); float rcpPowScale = 1.0 / powScale; if( face == 0) { r = vec3(direction.x, -direction.z, direction.y); offset = vec2(0.0+mipOffset,0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 1) { r = vec3(direction.y, direction.x, direction.z); offset = vec2(scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 2) { r = vec3(direction.z, direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 3) { r = vec3(direction.x, direction.z, direction.y); offset = vec2(0.0+mipOffset,0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else if( face == 4) { r = vec3(direction.y, direction.x, -direction.z); offset = vec2(scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else { r = vec3(direction.z, -direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } r = normalize(r); float texelOffset = 0.5 * cubeUV_rcpTextureSize; vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5; vec2 base = offset + vec2( texelOffset ); return base + s * ( scale - 2.0 * texelOffset ); } #define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0) vec4 textureCubeUV(vec3 reflectedDirection, float roughness ) { float roughnessVal = roughness* cubeUV_maxLods3; float r1 = floor(roughnessVal); float r2 = r1 + 1.0; float t = fract(roughnessVal); vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness); float s = mipInfo.y; float level0 = mipInfo.x; float level1 = level0 + 1.0; level1 = level1 > 5.0 ? 5.0 : level1; level0 += min( floor( s + 0.5 ), 5.0 ); vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0); vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10)); vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0); vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20)); vec4 result = mix(color10, color20, t); return vec4(result.rgb, 1.0); } #endif "; var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif "; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif "; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias ); #endif "; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb; totalEmissiveRadiance *= emissiveColor.rgb; #endif "; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif "; var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor ); "; var encodings_pars_fragment = " vec4 LinearToLinear( in vec4 value ) { return value; } vec4 GammaToLinear( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w ); } vec4 LinearToGamma( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w ); } vec4 sRGBToLinear( in vec4 value ) { return 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.w ); } vec4 LinearTosRGB( in vec4 value ) { return 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.w ); } vec4 RGBEToLinear( in vec4 value ) { return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 ); } vec4 LinearToRGBE( in vec4 value ) { float maxComponent = max( max( value.r, value.g ), value.b ); float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 ); return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 ); } vec4 RGBMToLinear( in vec4 value, in float maxRange ) { return vec4( value.xyz * value.w * maxRange, 1.0 ); } vec4 LinearToRGBM( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float M = clamp( maxRGB / maxRange, 0.0, 1.0 ); M = ceil( M * 255.0 ) / 255.0; return vec4( value.rgb / ( M * maxRange ), M ); } vec4 RGBDToLinear( in vec4 value, in float maxRange ) { return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 ); } vec4 LinearToRGBD( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float D = max( maxRange / maxRGB, 1.0 ); D = min( floor( D ) / 255.0, 1.0 ); return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D ); } const mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 ); vec4 LinearToLogLuv( in vec4 value ) { vec3 Xp_Y_XYZp = value.rgb * cLogLuvM; Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6)); vec4 vResult; vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0; vResult.w = fract(Le); vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0; return vResult; } const mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 ); vec4 LogLuvToLinear( in vec4 value ) { float Le = value.z * 255.0 + value.w; vec3 Xp_Y_XYZp; Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0); Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y; Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z; vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM; return vec4( max(vRGB, 0.0), 1.0 ); } "; var envmap_fragment = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition ); vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToVertex, worldNormal ); #else vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; reflectVec = normalize( reflectVec ); sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; vec4 envColor = texture2D( envMap, sampleUV ); #elif defined( ENVMAP_TYPE_SPHERE ) reflectVec = normalize( reflectVec ); vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ); vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 ); #else vec4 envColor = vec4( 0.0 ); #endif envColor = envMapTexelToLinear( envColor ); #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif "; var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL ) uniform float reflectivity; uniform float envMapIntensity; #endif #ifdef USE_ENVMAP #if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) ) varying vec3 vWorldPosition; #endif #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform int maxMipLevel; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL ) uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif "; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif "; var envmap_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif "; var fog_vertex = " #ifdef USE_FOG fogDepth = -mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float fogDepth; #endif "; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) ); #else float fogFactor = smoothstep( fogNear, fogFar, fogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif "; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float fogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif "; var gradientmap_pars_fragment = "#ifdef TOON uniform sampler2D gradientMap; vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return texture2D( gradientMap, coord ).rgb; #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif } #endif "; var lightmap_fragment = "#ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #endif "; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointDirectLightIrradiance( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); #ifdef DOUBLE_SIDED vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry ); #endif } #endif "; var lights_pars_begin = "uniform vec3 ambientLightColor; vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) { directLight.color = directionalLight.color; directLight.direction = directionalLight.direction; directLight.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = pointLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); directLight.color = pointLight.color; directLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay ); directLight.visible = ( directLight.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = spotLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); float angleCos = dot( directLight.direction, spotLight.direction ); if ( angleCos > spotLight.coneCos ) { float spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos ); directLight.color = spotLight.color; directLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay ); directLight.visible = true; } else { directLight.color = vec3( 0.0 ); directLight.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) { float dotNL = dot( geometry.normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #endif "; var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL ) vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) { vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix ); #ifdef ENVMAP_TYPE_CUBE vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) ); #else vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); vec4 envMapColor = textureCubeUV( queryVec, 1.0 ); #else vec4 envMapColor = vec4( 0.0 ); #endif return PI * envMapColor.rgb * envMapIntensity; } float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) { float maxMIPLevelScalar = float( maxMIPLevel ); float desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 ); return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar ); } vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) { #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal ); #else vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel ); #ifdef ENVMAP_TYPE_CUBE vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel ); #else vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent)); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_SPHERE ) vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #endif return envMapColor.rgb * envMapIntensity; } #endif "; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength; "; var lights_phong_pars_fragment = "varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { #ifdef TOON vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; #else float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #endif #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0) "; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 ); #ifdef STANDARD material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor ); material.clearCoat = saturate( clearCoat ); material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 ); #endif "; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float specularRoughness; vec3 specularColor; #ifndef STANDARD float clearCoat; float clearCoatRoughness; #endif }; #define MAXIMUM_SPECULAR_COEFFICIENT 0.16 #define DEFAULT_SPECULAR_COEFFICIENT 0.04 float clearCoatDHRApprox( const in float roughness, const in float dotNL ) { return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) ); } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.specularRoughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos - halfWidth - halfHeight; rectCoords[ 1 ] = lightPos + halfWidth - halfHeight; rectCoords[ 2 ] = lightPos + halfWidth + halfHeight; rectCoords[ 3 ] = lightPos - halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif #ifndef STANDARD float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness ); reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); #ifndef STANDARD reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { #ifndef STANDARD float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNL = dotNV; float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness ); #ifndef STANDARD reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical #define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness ) #define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness ) float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); } "; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = normalize( vViewPosition ); IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointDirectLightIrradiance( pointLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotDirectLightIrradiance( spotLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #endif #if defined( RE_IndirectDiffuse ) vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); } #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearCoatRadiance = vec3( 0.0 ); #endif "; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV ) irradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel ); #ifndef STANDARD clearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel ); #endif #endif "; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight ); #endif "; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF uniform float logDepthBufFC; #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif #endif "; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif uniform float logDepthBufFC; #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; #else gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; #endif #endif "; var map_fragment = "#ifdef USE_MAP vec4 texelColor = texture2D( map, vUv ); texelColor = mapTexelToLinear( texelColor ); diffuseColor *= texelColor; #endif "; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif "; var map_particle_fragment = "#ifdef USE_MAP vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; vec4 mapTexel = texture2D( map, uv ); diffuseColor *= mapTexelToLinear( mapTexel ); #endif "; var map_particle_pars_fragment = "#ifdef USE_MAP uniform mat3 uvTransform; uniform sampler2D map; #endif "; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif "; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ]; objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ]; objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ]; objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ]; #endif "; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ]; transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ]; transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ]; transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ]; transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ]; transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ]; transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ]; #endif #endif "; var normal_fragment_begin = "#ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 ); #endif #endif "; var normal_fragment_maps = "#ifdef USE_NORMALMAP normal = perturbNormal2Arb( -vViewPosition, normal ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() ); #endif "; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); float scale = sign( st1.t * st0.s - st0.t * st1.s ); vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale ); vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale ); vec3 N = normalize( surf_norm ); mat3 tsn = mat3( S, T, N ); vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 ); return normalize( tsn * mapN ); } #endif "; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return (( near + viewZ ) * far ) / (( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); } "; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif "; var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); gl_Position = projectionMatrix * mvPosition; "; var dithering_fragment = "#if defined( DITHERING ) gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif "; var dithering_pars_fragment = "#if defined( DITHERING ) vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif "; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif "; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) { const vec2 offset = vec2( 0.0, 1.0 ); vec2 texelSize = vec2( 1.0 ) / size; vec2 centroidUV = floor( uv * size + 0.5 ) / size; float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare ); float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare ); float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare ); float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare ); vec2 f = fract( uv * size + 0.5 ); float a = mix( lb, lt, f.y ); float b = mix( rb, rt, f.y ); float c = mix( a, b, f.x ); return c; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif "; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif #endif "; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition; } #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition; } #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition; } #endif #endif "; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 DirectionalLight directionalLight; #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #endif #if NUM_SPOT_LIGHTS > 0 SpotLight spotLight; #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #endif #if NUM_POINT_LIGHTS > 0 PointLight pointLight; #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #endif #endif return shadow; } "; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif "; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif "; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #endif "; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif "; var tonemapping_pars_fragment = "#ifndef saturate #define saturate(a) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; uniform float toneMappingWhitePoint; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } #define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) ) vec3 Uncharted2ToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } "; var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; uniform mat3 uvTransform; #endif "; var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = uv2; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 ); #endif "; var cube_frag = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldPosition; void main() { gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) ); gl_FragColor.a *= opacity; } "; var cube_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; } "; var depth_frag = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( gl_FragCoord.z ); #endif } "; var depth_vert = "#include #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include } "; var distanceRGBA_frag = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); } "; var distanceRGBA_vert = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; } "; var equirect_frag = "uniform sampler2D tEquirect; varying vec3 vWorldPosition; #include void main() { vec3 direction = normalize( vWorldPosition ); vec2 sampleUV; sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5; gl_FragColor = texture2D( tEquirect, sampleUV ); } "; var equirect_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include } "; var linedashed_frag = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var linedashed_vert = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include void main() { #include vLineDistance = scale * lineDistance; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; #include #include #include } "; var meshbasic_frag = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var meshbasic_vert = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_ENVMAP #include #include #include #include #endif #include #include #include #include #include #include #include #include #include } "; var meshlambert_frag = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor ); #include reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshlambert_vert = "#define LAMBERT varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } "; var meshphong_frag = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphong_vert = "#define PHONG varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include } "; var meshphysical_frag = "#define PHYSICAL uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifndef STANDARD uniform float clearCoat; uniform float clearCoatRoughness; #endif varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphysical_vert = "#define PHYSICAL varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include } "; var normal_frag = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); } "; var normal_vert = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif } "; var points_frag = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var points_vert = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_SIZEATTENUATION gl_PointSize = size * ( scale / - mvPosition.z ); #else gl_PointSize = size; #endif #include #include #include #include } "; var shadow_frag = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include } "; var shadow_vert = "#include #include void main() { #include #include #include #include #include } "; var ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_pars_maps: lights_pars_maps, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normalmap_pars_fragment: normalmap_pars_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, cube_frag: cube_frag, cube_vert: cube_vert, depth_frag: depth_frag, depth_vert: depth_vert, distanceRGBA_frag: distanceRGBA_frag, distanceRGBA_vert: distanceRGBA_vert, equirect_frag: equirect_frag, equirect_vert: equirect_vert, linedashed_frag: linedashed_frag, linedashed_vert: linedashed_vert, meshbasic_frag: meshbasic_frag, meshbasic_vert: meshbasic_vert, meshlambert_frag: meshlambert_frag, meshlambert_vert: meshlambert_vert, meshphong_frag: meshphong_frag, meshphong_vert: meshphong_vert, meshphysical_frag: meshphysical_frag, meshphysical_vert: meshphysical_vert, normal_frag: normal_frag, normal_vert: normal_vert, points_frag: points_frag, points_vert: points_vert, shadow_frag: shadow_frag, shadow_vert: shadow_vert }; /** * Uniform Utilities */ var UniformsUtils = { merge: function ( uniforms ) { var merged = {}; for ( var u = 0; u < uniforms.length; u ++ ) { var tmp = this.clone( uniforms[ u ] ); for ( var p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; }, clone: function ( uniforms_src ) { var uniforms_dst = {}; for ( var u in uniforms_src ) { uniforms_dst[ u ] = {}; for ( var p in uniforms_src[ u ] ) { var parameter_src = uniforms_src[ u ][ p ]; if ( parameter_src && ( parameter_src.isColor || parameter_src.isMatrix3 || parameter_src.isMatrix4 || parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || parameter_src.isTexture ) ) { uniforms_dst[ u ][ p ] = parameter_src.clone(); } else if ( Array.isArray( parameter_src ) ) { uniforms_dst[ u ][ p ] = parameter_src.slice(); } else { uniforms_dst[ u ][ p ] = parameter_src; } } } return uniforms_dst; } }; /** * @author mrdoob / http://mrdoob.com/ */ var ColorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; function Color( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } Object.assign( Color.prototype, { isColor: true, r: 1, g: 1, b: 1, set: function ( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } return this; }, setScalar: function ( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; }, setHex: function ( hex ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; return this; }, setRGB: function ( r, g, b ) { this.r = r; this.g = g; this.b = b; return this; }, setHSL: function () { function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } return function setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 h = _Math.euclideanModulo( h, 1 ); s = _Math.clamp( s, 0, 1 ); l = _Math.clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); var q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } return this; }; }(), setStyle: function ( style ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } var m; if ( m = /^((?:rgb|hsl)a?)(s*([^)]*))/.exec( style ) ) { // rgb / hsl var color; var name = m[ 1 ]; var components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^(d+)s*,s*(d+)s*,s*(d+)s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; handleAlpha( color[ 5 ] ); return this; } if ( color = /^(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; handleAlpha( color[ 5 ] ); return this; } break; case "hsl": case "hsla": if ( color = /^([0-9]*.?[0-9]+)s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) var h = parseFloat( color[ 1 ] ) / 360; var s = parseInt( color[ 2 ], 10 ) / 100; var l = parseInt( color[ 3 ], 10 ) / 100; handleAlpha( color[ 5 ] ); return this.setHSL( h, s, l ); } break; } } else if ( m = /^#([A-Fa-f0-9]+)$/.exec( style ) ) { // hex color var hex = m[ 1 ]; var size = hex.length; if ( size === 3 ) { // #ff0 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; return this; } else if ( size === 6 ) { // #ff0000 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; return this; } } if ( style && style.length > 0 ) { // color keywords var hex = ColorKeywords[ style ]; if ( hex !== undefined ) { // red this.setHex( hex ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } } return this; }, clone: function () { return new this.constructor( this.r, this.g, this.b ); }, copy: function ( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; }, copyGammaToLinear: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; this.r = Math.pow( color.r, gammaFactor ); this.g = Math.pow( color.g, gammaFactor ); this.b = Math.pow( color.b, gammaFactor ); return this; }, copyLinearToGamma: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; this.r = Math.pow( color.r, safeInverse ); this.g = Math.pow( color.g, safeInverse ); this.b = Math.pow( color.b, safeInverse ); return this; }, convertGammaToLinear: function ( gammaFactor ) { this.copyGammaToLinear( this, gammaFactor ); return this; }, convertLinearToGamma: function ( gammaFactor ) { this.copyLinearToGamma( this, gammaFactor ); return this; }, getHex: function () { return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; }, getHexString: function () { return ( "000000" + this.getHex().toString( 16 ) ).slice( - 6 ); }, getHSL: function ( target ) { // h,s,l ranges are in 0.0 - 1.0 if ( target === undefined ) { console.warn( "THREE.Color: .getHSL() target is now required" ); target = { h: 0, s: 0, l: 0 }; } var r = this.r, g = this.g, b = this.b; var max = Math.max( r, g, b ); var min = Math.min( r, g, b ); var hue, saturation; var lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { var delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; }, getStyle: function () { return "rgb(" + ( ( this.r * 255 ) | 0 ) + "," + ( ( this.g * 255 ) | 0 ) + "," + ( ( this.b * 255 ) | 0 ) + ")"; }, offsetHSL: function () { var hsl = {}; return function ( h, s, l ) { this.getHSL( hsl ); hsl.h += h; hsl.s += s; hsl.l += l; this.setHSL( hsl.h, hsl.s, hsl.l ); return this; }; }(), add: function ( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; }, addColors: function ( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; }, addScalar: function ( s ) { this.r += s; this.g += s; this.b += s; return this; }, sub: function ( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; }, multiply: function ( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; }, multiplyScalar: function ( s ) { this.r *= s; this.g *= s; this.b *= s; return this; }, lerp: function ( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; }, equals: function ( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; }, toJSON: function () { return this.getHex(); } } ); /** * Uniforms library for shared webgl shaders */ var UniformsLib = { common: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, alphaMap: { value: null }, }, specularmap: { specularMap: { value: null }, }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, refractionRatio: { value: 0.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 Vector2( 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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color( 0xffffff ) } }, lights: { ambientLightColor: { value: [] }, directionalLights: { value: [], properties: { direction: {}, color: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, directionalShadowMap: { value: [] }, directionalShadowMatrix: { value: [] }, spotLights: { value: [], properties: { color: {}, position: {}, direction: {}, distance: {}, coneCos: {}, penumbraCos: {}, decay: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, spotShadowMap: { value: [] }, spotShadowMatrix: { value: [] }, pointLights: { value: [], properties: { color: {}, position: {}, decay: {}, distance: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {}, shadowCameraNear: {}, shadowCameraFar: {} } }, pointShadowMap: { value: [] }, pointShadowMatrix: { value: [] }, hemisphereLights: { value: [], properties: { direction: {}, skyColor: {}, groundColor: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } } }, points: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() } } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ */ var ShaderLib = { basic: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, specular: { value: new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, roughness: { value: 0.5 }, metalness: { value: 0.5 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, points: { uniforms: UniformsUtils.merge( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.normal_vert, fragmentShader: ShaderChunk.normal_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: UniformsUtils.merge( [ UniformsLib.lights, UniformsLib.fog, { color: { value: new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: UniformsUtils.merge( [ ShaderLib.standard.uniforms, { clearCoat: { value: 0 }, clearCoatRoughness: { value: 0 } } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; /** * @author mrdoob / http://mrdoob.com/ */ function WebGLAnimation() { var context = null; var isAnimating = false; var animationLoop = null; function onAnimationFrame( time, frame ) { if ( isAnimating === false ) return; animationLoop( time, frame ); context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLAttributes( gl ) { var buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { var array = attribute.array; var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; var buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); var type = gl.FLOAT; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Float64Array ) { console.warn( "THREE.WebGLAttributes: Unsupported data buffer format: Float64Array." ); } else if ( array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { var array = attribute.array; var updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( attribute.dynamic === false ) { gl.bufferData( bufferType, array, gl.STATIC_DRAW ); } else if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else if ( updateRange.count === 0 ) { console.error( "THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually." ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); updateRange.count = - 1; // reset range } } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Euler( x, y, z, order ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._order = order || Euler.DefaultOrder; } Euler.RotationOrders = [ "XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX" ]; Euler.DefaultOrder = "XYZ"; Object.defineProperties( Euler.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, order: { get: function () { return this._order; }, set: function ( value ) { this._order = value; this.onChangeCallback(); } } } ); Object.assign( Euler.prototype, { isEuler: true, set: function ( x, y, z, order ) { this._x = x; this._y = y; this._z = z; this._order = order || this._order; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._order ); }, copy: function ( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m, order, update ) { var clamp = _Math.clamp; // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements; var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; order = order || this._order; if ( order === "XYZ" ) { this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.99999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } } else if ( order === "YXZ" ) { this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.99999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } } else if ( order === "ZXY" ) { this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.99999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } } else if ( order === "ZYX" ) { this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.99999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } } else if ( order === "YZX" ) { this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.99999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } } else if ( order === "XZY" ) { this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.99999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } } else { console.warn( "THREE.Euler: .setFromRotationMatrix() given unsupported order: " + order ); } this._order = order; if ( update !== false ) this.onChangeCallback(); return this; }, setFromQuaternion: function () { var matrix = new Matrix4(); return function setFromQuaternion( q, order, update ) { matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( matrix, order, update ); }; }(), setFromVector3: function ( v, order ) { return this.set( v.x, v.y, v.z, order || this._order ); }, reorder: function () { // WARNING: this discards revolution information -bhouston var q = new Quaternion(); return function reorder( newOrder ) { q.setFromEuler( this ); return this.setFromQuaternion( q, newOrder ); }; }(), equals: function ( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); }, fromArray: function ( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; }, toVector3: function ( optionalResult ) { if ( optionalResult ) { return optionalResult.set( this._x, this._y, this._z ); } else { return new Vector3( this._x, this._y, this._z ); } }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ */ function Layers() { this.mask = 1 | 0; } Object.assign( Layers.prototype, { set: function ( channel ) { this.mask = 1 << channel | 0; }, enable: function ( channel ) { this.mask |= 1 << channel | 0; }, toggle: function ( channel ) { this.mask ^= 1 << channel | 0; }, disable: function ( channel ) { this.mask &= ~ ( 1 << channel | 0 ); }, test: function ( layers ) { return ( this.mask & layers.mask ) !== 0; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author elephantatwork / www.elephantatwork.ch */ var object3DId = 0; function Object3D() { Object.defineProperty( this, "id", { value: object3DId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); var position = new Vector3(); var rotation = new Euler(); var quaternion = new Quaternion(); var scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation.onChange( onRotationChange ); quaternion.onChange( onQuaternionChange ); Object.defineProperties( this, { position: { enumerable: true, value: position }, rotation: { enumerable: true, value: rotation }, quaternion: { enumerable: true, value: quaternion }, scale: { enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.userData = {}; } Object3D.DefaultUp = new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Object3D, isObject3D: true, onBeforeRender: function () {}, onAfterRender: function () {}, applyMatrix: function ( matrix ) { this.matrix.multiplyMatrices( matrix, this.matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); }, applyQuaternion: function ( q ) { this.quaternion.premultiply( q ); return this; }, setRotationFromAxisAngle: function ( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); }, setRotationFromEuler: function ( euler ) { this.quaternion.setFromEuler( euler, true ); }, setRotationFromMatrix: function ( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); }, setRotationFromQuaternion: function ( q ) { // assumes q is normalized this.quaternion.copy( q ); }, rotateOnAxis: function () { // rotate object on axis in object space // axis is assumed to be normalized var q1 = new Quaternion(); return function rotateOnAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( q1 ); return this; }; }(), rotateOnWorldAxis: function () { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent var q1 = new Quaternion(); return function rotateOnWorldAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( q1 ); return this; }; }(), rotateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function rotateX( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function rotateY( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function rotateZ( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), translateOnAxis: function () { // translate object by distance along axis in object space // axis is assumed to be normalized var v1 = new Vector3(); return function translateOnAxis( axis, distance ) { v1.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( v1.multiplyScalar( distance ) ); return this; }; }(), translateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function translateX( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function translateY( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function translateZ( distance ) { return this.translateOnAxis( v1, distance ); }; }(), localToWorld: function ( vector ) { return vector.applyMatrix4( this.matrixWorld ); }, worldToLocal: function () { var m1 = new Matrix4(); return function worldToLocal( vector ) { return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); }; }(), lookAt: function () { // This method does not support objects with rotated and/or translated parent(s) var m1 = new Matrix4(); var vector = new Vector3(); return function lookAt( x, y, z ) { if ( x.isVector3 ) { vector.copy( x ); } else { vector.set( x, y, z ); } if ( this.isCamera ) { m1.lookAt( this.position, vector, this.up ); } else { m1.lookAt( vector, this.position, this.up ); } this.quaternion.setFromRotationMatrix( m1 ); }; }(), add: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( ( object && object.isObject3D ) ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; object.dispatchEvent( { type: "added" } ); this.children.push( object ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; }, remove: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } var index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; object.dispatchEvent( { type: "removed" } ); this.children.splice( index, 1 ); } return this; }, getObjectById: function ( id ) { return this.getObjectByProperty( "id", id ); }, getObjectByName: function ( name ) { return this.getObjectByProperty( "name", name ); }, getObjectByProperty: function ( name, value ) { if ( this[ name ] === value ) return this; for ( var i = 0, l = this.children.length; i < l; i ++ ) { var child = this.children[ i ]; var object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; }, getWorldPosition: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldPosition() target is now required" ); target = new Vector3(); } this.updateMatrixWorld( true ); return target.setFromMatrixPosition( this.matrixWorld ); }, getWorldQuaternion: function () { var position = new Vector3(); var scale = new Vector3(); return function getWorldQuaternion( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldQuaternion() target is now required" ); target = new Quaternion(); } this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, target, scale ); return target; }; }(), getWorldScale: function () { var position = new Vector3(); var quaternion = new Quaternion(); return function getWorldScale( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldScale() target is now required" ); target = new Vector3(); } this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, quaternion, target ); return target; }; }(), getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldDirection() target is now required" ); target = new Vector3(); } this.getWorldQuaternion( quaternion ); return target.set( 0, 0, 1 ).applyQuaternion( quaternion ); }; }(), raycast: function () {}, traverse: function ( callback ) { callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } }, traverseVisible: function ( callback ) { if ( this.visible === false ) return; callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } }, traverseAncestors: function ( callback ) { var parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } }, updateMatrix: function () { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; }, updateMatrixWorld: function ( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( force ); } }, toJSON: function ( meta ) { // meta is a string when called from JSON.stringify var isRootObject = ( meta === undefined || typeof meta === "string" ); var output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization var object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( JSON.stringify( this.userData ) !== "{}" ) object.userData = this.userData; object.matrix = this.matrix.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.geometry !== undefined ) { object.geometry = serialize( meta.geometries, this.geometry ); var parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { var shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { var uuids = []; for ( var i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( var i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } if ( isRootObject ) { var geometries = extractFromCache( meta.geometries ); var materials = extractFromCache( meta.materials ); var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); var shapes = extractFromCache( meta.shapes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } }, clone: function ( recursive ) { return new this.constructor().copy( this, recursive ); }, copy: function ( source, recursive ) { if ( recursive === undefined ) recursive = true; this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( var i = 0; i < source.children.length; i ++ ) { var child = source.children[ i ]; this.add( child.clone() ); } } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author WestLangley / http://github.com/WestLangley */ function Camera() { Object3D.call( this ); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); } Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Camera, isCamera: true, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); return this; }, getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( target ) { if ( target === undefined ) { console.warn( "THREE.Camera: .getWorldDirection() target is now required" ); target = new Vector3(); } this.getWorldQuaternion( quaternion ); return target.set( 0, 0, - 1 ).applyQuaternion( quaternion ); }; }(), updateMatrixWorld: function ( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); this.matrixWorldInverse.getInverse( this.matrixWorld ); }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author alteredq / http://alteredqualia.com/ * @author arose / http://github.com/arose */ function OrthographicCamera( left, right, top, bottom, near, far ) { Camera.call( this ); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = ( near !== undefined ) ? near : 0.1; this.far = ( far !== undefined ) ? far : 2000; this.updateProjectionMatrix(); } OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: OrthographicCamera, isOrthographicCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; }, setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var dx = ( this.right - this.left ) / ( 2 * this.zoom ); var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); var cx = ( this.right + this.left ) / 2; var cy = ( this.top + this.bottom ) / 2; var left = cx - dx; var right = cx + dx; var top = cy + dy; var bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); var scaleW = ( this.right - this.left ) / this.view.width; var scaleH = ( this.top - this.bottom ) / this.view.height; left += scaleW * ( this.view.offsetX / zoomW ); right = left + scaleW * ( this.view.width / zoomW ); top -= scaleH * ( this.view.offsetY / zoomH ); bottom = top - scaleH * ( this.view.height / zoomH ); } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Face3( a, b, c, normal, color, materialIndex ) { this.a = a; this.b = b; this.c = c; this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); this.vertexNormals = Array.isArray( normal ) ? normal : []; this.color = ( color && color.isColor ) ? color : new Color(); this.vertexColors = Array.isArray( color ) ? color : []; this.materialIndex = materialIndex !== undefined ? materialIndex : 0; } Object.assign( Face3.prototype, { clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.a = source.a; this.b = source.b; this.c = source.c; this.normal.copy( source.normal ); this.color.copy( source.color ); this.materialIndex = source.materialIndex; for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { this.vertexColors[ i ] = source.vertexColors[ i ].clone(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author bhouston / http://clara.io */ var geometryId = 0; // Geometry uses even numbers as Id function Geometry() { Object.defineProperty( this, "id", { value: geometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Geometry"; this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.elementsNeedUpdate = false; this.verticesNeedUpdate = false; this.uvsNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; this.groupsNeedUpdate = false; } Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Geometry, isGeometry: true, applyMatrix: function ( matrix ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { var vertex = this.vertices[ i ]; vertex.applyMatrix4( matrix ); } for ( var i = 0, il = this.faces.length; i < il; i ++ ) { var face = this.faces[ i ]; face.normal.applyMatrix3( normalMatrix ).normalize(); for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); } } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), fromBufferGeometry: function ( geometry ) { var scope = this; var indices = geometry.index !== null ? geometry.index.array : undefined; var attributes = geometry.attributes; var positions = attributes.position.array; var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; var colors = attributes.color !== undefined ? attributes.color.array : undefined; var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; var tempNormals = []; var tempUVs = []; var tempUVs2 = []; for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); if ( normals !== undefined ) { tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); } if ( colors !== undefined ) { scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); } if ( uvs !== undefined ) { tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); } if ( uvs2 !== undefined ) { tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); } } function addFace( a, b, c, materialIndex ) { var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); scope.faces.push( face ); if ( uvs !== undefined ) { scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); } if ( uvs2 !== undefined ) { scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); } } var groups = geometry.groups; if ( groups.length > 0 ) { for ( var i = 0; i < groups.length; i ++ ) { var group = groups[ i ]; var start = group.start; var count = group.count; for ( var j = start, jl = start + count; j < jl; j += 3 ) { if ( indices !== undefined ) { addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); } else { addFace( j, j + 1, j + 2, group.materialIndex ); } } } } else { if ( indices !== undefined ) { for ( var i = 0; i < indices.length; i += 3 ) { addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } } else { for ( var i = 0; i < positions.length / 3; i += 3 ) { addFace( i, i + 1, i + 2 ); } } } this.computeFaceNormals(); if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } return this; }, center: function () { var offset = new Vector3(); return function center() { this.computeBoundingBox(); this.boundingBox.getCenter( offset ).negate(); this.translate( offset.x, offset.y, offset.z ); return this; }; }(), normalize: function () { this.computeBoundingSphere(); var center = this.boundingSphere.center; var radius = this.boundingSphere.radius; var s = radius === 0 ? 1 : 1.0 / radius; var matrix = new Matrix4(); matrix.set( s, 0, 0, - s * center.x, 0, s, 0, - s * center.y, 0, 0, s, - s * center.z, 0, 0, 0, 1 ); this.applyMatrix( matrix ); return this; }, computeFaceNormals: function () { var cb = new Vector3(), ab = new Vector3(); for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { var face = this.faces[ f ]; var vA = this.vertices[ face.a ]; var vB = this.vertices[ face.b ]; var vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); cb.normalize(); face.normal.copy( cb ); } }, computeVertexNormals: function ( areaWeighted ) { if ( areaWeighted === undefined ) areaWeighted = true; var v, vl, f, fl, face, vertices; vertices = new Array( this.vertices.length ); for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ] = new Vector3(); } if ( areaWeighted ) { // vertex normals weighted by triangle areas // http://www.iquilezles.org/www/articles/normals/normals.htm var vA, vB, vC; var cb = new Vector3(), ab = new Vector3(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vA = this.vertices[ face.a ]; vB = this.vertices[ face.b ]; vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); vertices[ face.a ].add( cb ); vertices[ face.b ].add( cb ); vertices[ face.c ].add( cb ); } } else { this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vertices[ face.a ].add( face.normal ); vertices[ face.b ].add( face.normal ); vertices[ face.c ].add( face.normal ); } } for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ].normalize(); } for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( vertices[ face.a ] ); vertexNormals[ 1 ].copy( vertices[ face.b ] ); vertexNormals[ 2 ].copy( vertices[ face.c ] ); } else { vertexNormals[ 0 ] = vertices[ face.a ].clone(); vertexNormals[ 1 ] = vertices[ face.b ].clone(); vertexNormals[ 2 ] = vertices[ face.c ].clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeFlatVertexNormals: function () { var f, fl, face; this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( face.normal ); vertexNormals[ 1 ].copy( face.normal ); vertexNormals[ 2 ].copy( face.normal ); } else { vertexNormals[ 0 ] = face.normal.clone(); vertexNormals[ 1 ] = face.normal.clone(); vertexNormals[ 2 ] = face.normal.clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeMorphNormals: function () { var i, il, f, fl, face; // save original normals // - create temp variables on first access // otherwise just copy (for faster repeated calls) for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; if ( ! face.__originalFaceNormal ) { face.__originalFaceNormal = face.normal.clone(); } else { face.__originalFaceNormal.copy( face.normal ); } if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { if ( ! face.__originalVertexNormals[ i ] ) { face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); } else { face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } } // use temp geometry to compute face and vertex normals for each morph var tmpGeo = new Geometry(); tmpGeo.faces = this.faces; for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { // create on first access if ( ! this.morphNormals[ i ] ) { this.morphNormals[ i ] = {}; this.morphNormals[ i ].faceNormals = []; this.morphNormals[ i ].vertexNormals = []; var dstNormalsFace = this.morphNormals[ i ].faceNormals; var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { faceNormal = new Vector3(); vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; dstNormalsFace.push( faceNormal ); dstNormalsVertex.push( vertexNormals ); } } var morphNormals = this.morphNormals[ i ]; // set vertices to morph target tmpGeo.vertices = this.morphTargets[ i ].vertices; // compute morph normals tmpGeo.computeFaceNormals(); tmpGeo.computeVertexNormals(); // store morph normals var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; faceNormal = morphNormals.faceNormals[ f ]; vertexNormals = morphNormals.vertexNormals[ f ]; faceNormal.copy( face.normal ); vertexNormals.a.copy( face.vertexNormals[ 0 ] ); vertexNormals.b.copy( face.vertexNormals[ 1 ] ); vertexNormals.c.copy( face.vertexNormals[ 2 ] ); } } // restore original normals for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; face.normal = face.__originalFaceNormal; face.vertexNormals = face.__originalVertexNormals; } }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.setFromPoints( this.vertices ); }, computeBoundingSphere: function () { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.setFromPoints( this.vertices ); }, merge: function ( geometry, matrix, materialIndexOffset ) { if ( ! ( geometry && geometry.isGeometry ) ) { console.error( "THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.", geometry ); return; } var normalMatrix, vertexOffset = this.vertices.length, vertices1 = this.vertices, vertices2 = geometry.vertices, faces1 = this.faces, faces2 = geometry.faces, uvs1 = this.faceVertexUvs[ 0 ], uvs2 = geometry.faceVertexUvs[ 0 ], colors1 = this.colors, colors2 = geometry.colors; if ( materialIndexOffset === undefined ) materialIndexOffset = 0; if ( matrix !== undefined ) { normalMatrix = new Matrix3().getNormalMatrix( matrix ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); vertices1.push( vertexCopy ); } // colors for ( var i = 0, il = colors2.length; i < il; i ++ ) { colors1.push( colors2[ i ].clone() ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); faceCopy.normal.copy( face.normal ); if ( normalMatrix !== undefined ) { faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); } for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( normalMatrix !== undefined ) { normal.applyMatrix3( normalMatrix ).normalize(); } faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex + materialIndexOffset; faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; if ( uv === undefined ) { continue; } for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( uv[ j ].clone() ); } uvs1.push( uvCopy ); } }, mergeMesh: function ( mesh ) { if ( ! ( mesh && mesh.isMesh ) ) { console.error( "THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.", mesh ); return; } if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); this.merge( mesh.geometry, mesh.matrix ); }, /* * Checks for duplicate vertices with hashmap. * Duplicated vertices are removed * and faces" vertices are updated. */ mergeVertices: function () { var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique = [], changes = []; var v, key; var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 var precision = Math.pow( 10, precisionPoints ); var i, il, face; var indices, j, jl; for ( i = 0, il = this.vertices.length; i < il; i ++ ) { v = this.vertices[ i ]; key = Math.round( v.x * precision ) + "_" + Math.round( v.y * precision ) + "_" + Math.round( v.z * precision ); if ( verticesMap[ key ] === undefined ) { verticesMap[ key ] = i; unique.push( this.vertices[ i ] ); changes[ i ] = unique.length - 1; } else { //console.log("Duplicate vertex found. ", i, " could be using ", verticesMap[key]); changes[ i ] = changes[ verticesMap[ key ] ]; } } // if faces are completely degenerate after merging vertices, we // have to remove them from the geometry. var faceIndicesToRemove = []; for ( i = 0, il = this.faces.length; i < il; i ++ ) { face = this.faces[ i ]; face.a = changes[ face.a ]; face.b = changes[ face.b ]; face.c = changes[ face.c ]; indices = [ face.a, face.b, face.c ]; // if any duplicate vertices are found in a Face3 // we have to remove the face as nothing can be saved for ( var n = 0; n < 3; n ++ ) { if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { faceIndicesToRemove.push( i ); break; } } } for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { var idx = faceIndicesToRemove[ i ]; this.faces.splice( idx, 1 ); for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { this.faceVertexUvs[ j ].splice( idx, 1 ); } } // Use unique set of vertices var diff = this.vertices.length - unique.length; this.vertices = unique; return diff; }, setFromPoints: function ( points ) { this.vertices = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return this; }, sortFacesByMaterialIndex: function () { var faces = this.faces; var length = faces.length; // tag faces for ( var i = 0; i < length; i ++ ) { faces[ i ]._id = i; } // sort faces function materialIndexSort( a, b ) { return a.materialIndex - b.materialIndex; } faces.sort( materialIndexSort ); // sort uvs var uvs1 = this.faceVertexUvs[ 0 ]; var uvs2 = this.faceVertexUvs[ 1 ]; var newUvs1, newUvs2; if ( uvs1 && uvs1.length === length ) newUvs1 = []; if ( uvs2 && uvs2.length === length ) newUvs2 = []; for ( var i = 0; i < length; i ++ ) { var id = faces[ i ]._id; if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); } if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Geometry", generator: "Geometry.toJSON" } }; // standard Geometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } var vertices = []; for ( var i = 0; i < this.vertices.length; i ++ ) { var vertex = this.vertices[ i ]; vertices.push( vertex.x, vertex.y, vertex.z ); } var faces = []; var normals = []; var normalsHash = {}; var colors = []; var colorsHash = {}; var uvs = []; var uvsHash = {}; for ( var i = 0; i < this.faces.length; i ++ ) { var face = this.faces[ i ]; var hasMaterial = true; var hasFaceUv = false; // deprecated var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; var hasFaceNormal = face.normal.length() > 0; var hasFaceVertexNormal = face.vertexNormals.length > 0; var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; var hasFaceVertexColor = face.vertexColors.length > 0; var faceType = 0; faceType = setBit( faceType, 0, 0 ); // isQuad faceType = setBit( faceType, 1, hasMaterial ); faceType = setBit( faceType, 2, hasFaceUv ); faceType = setBit( faceType, 3, hasFaceVertexUv ); faceType = setBit( faceType, 4, hasFaceNormal ); faceType = setBit( faceType, 5, hasFaceVertexNormal ); faceType = setBit( faceType, 6, hasFaceColor ); faceType = setBit( faceType, 7, hasFaceVertexColor ); faces.push( faceType ); faces.push( face.a, face.b, face.c ); faces.push( face.materialIndex ); if ( hasFaceVertexUv ) { var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; faces.push( getUvIndex( faceVertexUvs[ 0 ] ), getUvIndex( faceVertexUvs[ 1 ] ), getUvIndex( faceVertexUvs[ 2 ] ) ); } if ( hasFaceNormal ) { faces.push( getNormalIndex( face.normal ) ); } if ( hasFaceVertexNormal ) { var vertexNormals = face.vertexNormals; faces.push( getNormalIndex( vertexNormals[ 0 ] ), getNormalIndex( vertexNormals[ 1 ] ), getNormalIndex( vertexNormals[ 2 ] ) ); } if ( hasFaceColor ) { faces.push( getColorIndex( face.color ) ); } if ( hasFaceVertexColor ) { var vertexColors = face.vertexColors; faces.push( getColorIndex( vertexColors[ 0 ] ), getColorIndex( vertexColors[ 1 ] ), getColorIndex( vertexColors[ 2 ] ) ); } } function setBit( value, position, enabled ) { return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); } function getNormalIndex( normal ) { var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); if ( normalsHash[ hash ] !== undefined ) { return normalsHash[ hash ]; } normalsHash[ hash ] = normals.length / 3; normals.push( normal.x, normal.y, normal.z ); return normalsHash[ hash ]; } function getColorIndex( color ) { var hash = color.r.toString() + color.g.toString() + color.b.toString(); if ( colorsHash[ hash ] !== undefined ) { return colorsHash[ hash ]; } colorsHash[ hash ] = colors.length; colors.push( color.getHex() ); return colorsHash[ hash ]; } function getUvIndex( uv ) { var hash = uv.x.toString() + uv.y.toString(); if ( uvsHash[ hash ] !== undefined ) { return uvsHash[ hash ]; } uvsHash[ hash ] = uvs.length / 2; uvs.push( uv.x, uv.y ); return uvsHash[ hash ]; } data.data = {}; data.data.vertices = vertices; data.data.normals = normals; if ( colors.length > 0 ) data.data.colors = colors; if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility data.data.faces = faces; return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new Geometry().copy( this ); }, copy: function ( source ) { var i, il, j, jl, k, kl; // reset this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // vertices var vertices = source.vertices; for ( i = 0, il = vertices.length; i < il; i ++ ) { this.vertices.push( vertices[ i ].clone() ); } // colors var colors = source.colors; for ( i = 0, il = colors.length; i < il; i ++ ) { this.colors.push( colors[ i ].clone() ); } // faces var faces = source.faces; for ( i = 0, il = faces.length; i < il; i ++ ) { this.faces.push( faces[ i ].clone() ); } // face vertex uvs for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { var faceVertexUvs = source.faceVertexUvs[ i ]; if ( this.faceVertexUvs[ i ] === undefined ) { this.faceVertexUvs[ i ] = []; } for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { var uvs = faceVertexUvs[ j ], uvsCopy = []; for ( k = 0, kl = uvs.length; k < kl; k ++ ) { var uv = uvs[ k ]; uvsCopy.push( uv.clone() ); } this.faceVertexUvs[ i ].push( uvsCopy ); } } // morph targets var morphTargets = source.morphTargets; for ( i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = {}; morphTarget.name = morphTargets[ i ].name; // vertices if ( morphTargets[ i ].vertices !== undefined ) { morphTarget.vertices = []; for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); } } // normals if ( morphTargets[ i ].normals !== undefined ) { morphTarget.normals = []; for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); } } this.morphTargets.push( morphTarget ); } // morph normals var morphNormals = source.morphNormals; for ( i = 0, il = morphNormals.length; i < il; i ++ ) { var morphNormal = {}; // vertex normals if ( morphNormals[ i ].vertexNormals !== undefined ) { morphNormal.vertexNormals = []; for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; var destVertexNormal = {}; destVertexNormal.a = srcVertexNormal.a.clone(); destVertexNormal.b = srcVertexNormal.b.clone(); destVertexNormal.c = srcVertexNormal.c.clone(); morphNormal.vertexNormals.push( destVertexNormal ); } } // face normals if ( morphNormals[ i ].faceNormals !== undefined ) { morphNormal.faceNormals = []; for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); } } this.morphNormals.push( morphNormal ); } // skin weights var skinWeights = source.skinWeights; for ( i = 0, il = skinWeights.length; i < il; i ++ ) { this.skinWeights.push( skinWeights[ i ].clone() ); } // skin indices var skinIndices = source.skinIndices; for ( i = 0, il = skinIndices.length; i < il; i ++ ) { this.skinIndices.push( skinIndices[ i ].clone() ); } // line distances var lineDistances = source.lineDistances; for ( i = 0, il = lineDistances.length; i < il; i ++ ) { this.lineDistances.push( lineDistances[ i ] ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // update flags this.elementsNeedUpdate = source.elementsNeedUpdate; this.verticesNeedUpdate = source.verticesNeedUpdate; this.uvsNeedUpdate = source.uvsNeedUpdate; this.normalsNeedUpdate = source.normalsNeedUpdate; this.colorsNeedUpdate = source.colorsNeedUpdate; this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; this.groupsNeedUpdate = source.groupsNeedUpdate; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferAttribute( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } Object.defineProperty( BufferAttribute.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( BufferAttribute.prototype, { isBufferAttribute: true, onUploadCallback: function () {}, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.itemSize : 0; this.array = array; return this; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( var i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, copyArray: function ( array ) { this.array.set( array ); return this; }, copyColorsArray: function ( colors ) { var array = this.array, offset = 0; for ( var i = 0, l = colors.length; i < l; i ++ ) { var color = colors[ i ]; if ( color === undefined ) { console.warn( "THREE.BufferAttribute.copyColorsArray(): color is undefined", i ); color = new Color(); } array[ offset ++ ] = color.r; array[ offset ++ ] = color.g; array[ offset ++ ] = color.b; } return this; }, copyVector2sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i ); vector = new Vector2(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; } return this; }, copyVector3sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i ); vector = new Vector3(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; } return this; }, copyVector4sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i ); vector = new Vector4(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; array[ offset ++ ] = vector.w; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, getX: function ( index ) { return this.array[ index * this.itemSize ]; }, setX: function ( index, x ) { this.array[ index * this.itemSize ] = x; return this; }, getY: function ( index ) { return this.array[ index * this.itemSize + 1 ]; }, setY: function ( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; }, getZ: function ( index ) { return this.array[ index * this.itemSize + 2 ]; }, setZ: function ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; }, getW: function ( index ) { return this.array[ index * this.itemSize + 3 ]; }, setW: function ( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; }, setXY: function ( index, x, y ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; }, clone: function () { return new this.constructor( this.array, this.itemSize ).copy( this ); } } ); // function Int8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); } Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; function Uint8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); } Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); } Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; function Int16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); } Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; function Uint16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); } Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; function Int32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); } Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; function Uint32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); } Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; function Float32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); } Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; function Float64BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); } Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; /** * @author mrdoob / http://mrdoob.com/ */ function DirectGeometry() { this.vertices = []; this.normals = []; this.colors = []; this.uvs = []; this.uvs2 = []; this.groups = []; this.morphTargets = {}; this.skinWeights = []; this.skinIndices = []; // this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.verticesNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.uvsNeedUpdate = false; this.groupsNeedUpdate = false; } Object.assign( DirectGeometry.prototype, { computeGroups: function ( geometry ) { var group; var groups = []; var materialIndex = undefined; var faces = geometry.faces; for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; // materials if ( face.materialIndex !== materialIndex ) { materialIndex = face.materialIndex; if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } group = { start: i * 3, materialIndex: materialIndex }; } } if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } this.groups = groups; }, fromGeometry: function ( geometry ) { var faces = geometry.faces; var vertices = geometry.vertices; var faceVertexUvs = geometry.faceVertexUvs; var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; // morphs var morphTargets = geometry.morphTargets; var morphTargetsLength = morphTargets.length; var morphTargetsPosition; if ( morphTargetsLength > 0 ) { morphTargetsPosition = []; for ( var i = 0; i < morphTargetsLength; i ++ ) { morphTargetsPosition[ i ] = []; } this.morphTargets.position = morphTargetsPosition; } var morphNormals = geometry.morphNormals; var morphNormalsLength = morphNormals.length; var morphTargetsNormal; if ( morphNormalsLength > 0 ) { morphTargetsNormal = []; for ( var i = 0; i < morphNormalsLength; i ++ ) { morphTargetsNormal[ i ] = []; } this.morphTargets.normal = morphTargetsNormal; } // skins var skinIndices = geometry.skinIndices; var skinWeights = geometry.skinWeights; var hasSkinIndices = skinIndices.length === vertices.length; var hasSkinWeights = skinWeights.length === vertices.length; // if ( faces.length === 0 ) { console.error( "THREE.DirectGeometry: Faceless geometries are not supported." ); } for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); } else { var normal = face.normal; this.normals.push( normal, normal, normal ); } var vertexColors = face.vertexColors; if ( vertexColors.length === 3 ) { this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); } else { var color = face.color; this.colors.push( color, color, color ); } if ( hasFaceVertexUv === true ) { var vertexUvs = faceVertexUvs[ 0 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ", i ); this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); } } if ( hasFaceVertexUv2 === true ) { var vertexUvs = faceVertexUvs[ 1 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ", i ); this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); } } // morphs for ( var j = 0; j < morphTargetsLength; j ++ ) { var morphTarget = morphTargets[ j ].vertices; morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); } for ( var j = 0; j < morphNormalsLength; j ++ ) { var morphNormal = morphNormals[ j ].vertexNormals[ i ]; morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); } // skins if ( hasSkinIndices ) { this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); } if ( hasSkinWeights ) { this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); } } this.computeGroups( geometry ); this.verticesNeedUpdate = geometry.verticesNeedUpdate; this.normalsNeedUpdate = geometry.normalsNeedUpdate; this.colorsNeedUpdate = geometry.colorsNeedUpdate; this.uvsNeedUpdate = geometry.uvsNeedUpdate; this.groupsNeedUpdate = geometry.groupsNeedUpdate; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function arrayMax( array ) { if ( array.length === 0 ) return - Infinity; var max = array[ 0 ]; for ( var i = 1, l = array.length; i < l; ++ i ) { if ( array[ i ] > max ) max = array[ i ]; } return max; } /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id function BufferGeometry() { Object.defineProperty( this, "id", { value: bufferGeometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: BufferGeometry, isBufferGeometry: true, getIndex: function () { return this.index; }, setIndex: function ( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } }, addAttribute: function ( name, attribute ) { if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { console.warn( "THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )." ); return this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); } if ( name === "index" ) { console.warn( "THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute." ); this.setIndex( attribute ); return this; } this.attributes[ name ] = attribute; return this; }, getAttribute: function ( name ) { return this.attributes[ name ]; }, removeAttribute: function ( name ) { delete this.attributes[ name ]; return this; }, addGroup: function ( start, count, materialIndex ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex !== undefined ? materialIndex : 0 } ); }, clearGroups: function () { this.groups = []; }, setDrawRange: function ( start, count ) { this.drawRange.start = start; this.drawRange.count = count; }, applyMatrix: function ( matrix ) { var position = this.attributes.position; if ( position !== undefined ) { matrix.applyToBufferAttribute( position ); position.needsUpdate = true; } var normal = this.attributes.normal; if ( normal !== undefined ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); normalMatrix.applyToBufferAttribute( normal ); normal.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), center: function () { var offset = new Vector3(); return function center() { this.computeBoundingBox(); this.boundingBox.getCenter( offset ).negate(); this.translate( offset.x, offset.y, offset.z ); return this; }; }(), setFromObject: function ( object ) { // console.log( "THREE.BufferGeometry.setFromObject(). Converting", object, this ); var geometry = object.geometry; if ( object.isPoints || object.isLine ) { var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); this.addAttribute( "position", positions.copyVector3sArray( geometry.vertices ) ); this.addAttribute( "color", colors.copyColorsArray( geometry.colors ) ); if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); this.addAttribute( "lineDistance", lineDistances.copyArray( geometry.lineDistances ) ); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } } else if ( object.isMesh ) { if ( geometry && geometry.isGeometry ) { this.fromGeometry( geometry ); } } return this; }, setFromPoints: function ( points ) { var position = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.addAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; }, updateFromObject: function ( object ) { var geometry = object.geometry; if ( object.isMesh ) { var direct = geometry.__directGeometry; if ( geometry.elementsNeedUpdate === true ) { direct = undefined; geometry.elementsNeedUpdate = false; } if ( direct === undefined ) { return this.fromGeometry( geometry ); } direct.verticesNeedUpdate = geometry.verticesNeedUpdate; direct.normalsNeedUpdate = geometry.normalsNeedUpdate; direct.colorsNeedUpdate = geometry.colorsNeedUpdate; direct.uvsNeedUpdate = geometry.uvsNeedUpdate; direct.groupsNeedUpdate = geometry.groupsNeedUpdate; geometry.verticesNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.groupsNeedUpdate = false; geometry = direct; } var attribute; if ( geometry.verticesNeedUpdate === true ) { attribute = this.attributes.position; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.vertices ); attribute.needsUpdate = true; } geometry.verticesNeedUpdate = false; } if ( geometry.normalsNeedUpdate === true ) { attribute = this.attributes.normal; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.normals ); attribute.needsUpdate = true; } geometry.normalsNeedUpdate = false; } if ( geometry.colorsNeedUpdate === true ) { attribute = this.attributes.color; if ( attribute !== undefined ) { attribute.copyColorsArray( geometry.colors ); attribute.needsUpdate = true; } geometry.colorsNeedUpdate = false; } if ( geometry.uvsNeedUpdate ) { attribute = this.attributes.uv; if ( attribute !== undefined ) { attribute.copyVector2sArray( geometry.uvs ); attribute.needsUpdate = true; } geometry.uvsNeedUpdate = false; } if ( geometry.lineDistancesNeedUpdate ) { attribute = this.attributes.lineDistance; if ( attribute !== undefined ) { attribute.copyArray( geometry.lineDistances ); attribute.needsUpdate = true; } geometry.lineDistancesNeedUpdate = false; } if ( geometry.groupsNeedUpdate ) { geometry.computeGroups( object.geometry ); this.groups = geometry.groups; geometry.groupsNeedUpdate = false; } return this; }, fromGeometry: function ( geometry ) { geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); return this.fromDirectGeometry( geometry.__directGeometry ); }, fromDirectGeometry: function ( geometry ) { var positions = new Float32Array( geometry.vertices.length * 3 ); this.addAttribute( "position", new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); if ( geometry.normals.length > 0 ) { var normals = new Float32Array( geometry.normals.length * 3 ); this.addAttribute( "normal", new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); } if ( geometry.colors.length > 0 ) { var colors = new Float32Array( geometry.colors.length * 3 ); this.addAttribute( "color", new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); } if ( geometry.uvs.length > 0 ) { var uvs = new Float32Array( geometry.uvs.length * 2 ); this.addAttribute( "uv", new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); } if ( geometry.uvs2.length > 0 ) { var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); this.addAttribute( "uv2", new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); } // groups this.groups = geometry.groups; // morphs for ( var name in geometry.morphTargets ) { var array = []; var morphTargets = geometry.morphTargets[ name ]; for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { var morphTarget = morphTargets[ i ]; var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); array.push( attribute.copyVector3sArray( morphTarget ) ); } this.morphAttributes[ name ] = array; } // skinning if ( geometry.skinIndices.length > 0 ) { var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); this.addAttribute( "skinIndex", skinIndices.copyVector4sArray( geometry.skinIndices ) ); } if ( geometry.skinWeights.length > 0 ) { var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); this.addAttribute( "skinWeight", skinWeights.copyVector4sArray( geometry.skinWeights ) ); } // if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } return this; }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } var position = this.attributes.position; if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } }, computeBoundingSphere: function () { var box = new Box3(); var vector = new Vector3(); return function computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } var position = this.attributes.position; if ( position ) { var center = this.boundingSphere.center; box.setFromBufferAttribute( position ); box.getCenter( center ); // hoping to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case var maxRadiusSq = 0; for ( var i = 0, il = position.count; i < il; i ++ ) { vector.x = position.getX( i ); vector.y = position.getY( i ); vector.z = position.getZ( i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } }; }(), computeFaceNormals: function () { // backwards compatibility }, computeVertexNormals: function () { var index = this.index; var attributes = this.attributes; var groups = this.groups; if ( attributes.position ) { var positions = attributes.position.array; if ( attributes.normal === undefined ) { this.addAttribute( "normal", new BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { // reset existing normals to zero var array = attributes.normal.array; for ( var i = 0, il = array.length; i < il; i ++ ) { array[ i ] = 0; } } var normals = attributes.normal.array; var vA, vB, vC; var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); var cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { var indices = index.array; if ( groups.length === 0 ) { this.addGroup( 0, indices.length ); } for ( var j = 0, jl = groups.length; j < jl; ++ j ) { var group = groups[ j ]; var start = group.start; var count = group.count; for ( var i = start, il = start + count; i < il; i += 3 ) { vA = indices[ i + 0 ] * 3; vB = indices[ i + 1 ] * 3; vC = indices[ i + 2 ] * 3; pA.fromArray( positions, vA ); pB.fromArray( positions, vB ); pC.fromArray( positions, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ vA ] += cb.x; normals[ vA + 1 ] += cb.y; normals[ vA + 2 ] += cb.z; normals[ vB ] += cb.x; normals[ vB + 1 ] += cb.y; normals[ vB + 2 ] += cb.z; normals[ vC ] += cb.x; normals[ vC + 1 ] += cb.y; normals[ vC + 2 ] += cb.z; } } } else { // non-indexed elements (unconnected triangle soup) for ( var i = 0, il = positions.length; i < il; i += 9 ) { pA.fromArray( positions, i ); pB.fromArray( positions, i + 3 ); pC.fromArray( positions, i + 6 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ i ] = cb.x; normals[ i + 1 ] = cb.y; normals[ i + 2 ] = cb.z; normals[ i + 3 ] = cb.x; normals[ i + 4 ] = cb.y; normals[ i + 5 ] = cb.z; normals[ i + 6 ] = cb.x; normals[ i + 7 ] = cb.y; normals[ i + 8 ] = cb.z; } } this.normalizeNormals(); attributes.normal.needsUpdate = true; } }, merge: function ( geometry, offset ) { if ( ! ( geometry && geometry.isBufferGeometry ) ) { console.error( "THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry ); return; } if ( offset === undefined ) { offset = 0; console.warn( "THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge." ); } var attributes = this.attributes; for ( var key in attributes ) { if ( geometry.attributes[ key ] === undefined ) continue; var attribute1 = attributes[ key ]; var attributeArray1 = attribute1.array; var attribute2 = geometry.attributes[ key ]; var attributeArray2 = attribute2.array; var attributeSize = attribute2.itemSize; for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; } } return this; }, normalizeNormals: function () { var vector = new Vector3(); return function normalizeNormals() { var normals = this.attributes.normal; for ( var i = 0, il = normals.count; i < il; i ++ ) { vector.x = normals.getX( i ); vector.y = normals.getY( i ); vector.z = normals.getZ( i ); vector.normalize(); normals.setXYZ( i, vector.x, vector.y, vector.z ); } }; }(), toNonIndexed: function () { if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed." ); return this; } var geometry2 = new BufferGeometry(); var indices = this.index.array; var attributes = this.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; var array = attribute.array; var itemSize = attribute.itemSize; var array2 = new array.constructor( indices.length * itemSize ); var index = 0, index2 = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { index = indices[ i ] * itemSize; for ( var j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); } var groups = this.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } data.data = { attributes: {} }; var index = this.index; if ( index !== null ) { var array = Array.prototype.slice.call( index.array ); data.data.index = { type: index.array.constructor.name, array: array }; } var attributes = this.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var array = Array.prototype.slice.call( attribute.array ); data.data.attributes[ key ] = { itemSize: attribute.itemSize, type: attribute.array.constructor.name, array: array, normalized: attribute.normalized }; } var groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } var boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new BufferGeometry().copy( this ); }, copy: function ( source ) { var name, i, l; // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // index var index = source.index; if ( index !== null ) { this.setIndex( index.clone() ); } // attributes var attributes = source.attributes; for ( name in attributes ) { var attribute = attributes[ name ]; this.addAttribute( name, attribute.clone() ); } // morph attributes var morphAttributes = source.morphAttributes; for ( name in morphAttributes ) { var array = []; var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone() ); } this.morphAttributes[ name ] = array; } // groups var groups = source.groups; for ( i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // BoxGeometry function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { Geometry.call( this ); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); this.mergeVertices(); } BoxGeometry.prototype = Object.create( Geometry.prototype ); BoxGeometry.prototype.constructor = BoxGeometry; // BoxBufferGeometry function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { BufferGeometry.call( this ); this.type = "BoxBufferGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; var scope = this; width = width || 1; height = height || 1; depth = depth || 1; // segments widthSegments = Math.floor( widthSegments ) || 1; heightSegments = Math.floor( heightSegments ) || 1; depthSegments = Math.floor( depthSegments ) || 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var numberOfVertices = 0; var groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { var segmentWidth = width / gridX; var segmentHeight = height / gridY; var widthHalf = width / 2; var heightHalf = height / 2; var depthHalf = depth / 2; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var vertexCounter = 0; var groupCount = 0; var ix, iy; var vector = new Vector3(); // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segmentHeight - heightHalf; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = numberOfVertices + ix + gridX1 * iy; var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // PlaneGeometry function PlaneGeometry( width, height, widthSegments, heightSegments ) { Geometry.call( this ); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); this.mergeVertices(); } PlaneGeometry.prototype = Object.create( Geometry.prototype ); PlaneGeometry.prototype.constructor = PlaneGeometry; // PlaneBufferGeometry function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { BufferGeometry.call( this ); this.type = "PlaneBufferGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; width = width || 1; height = height || 1; var width_half = width / 2; var height_half = height / 2; var gridX = Math.floor( widthSegments ) || 1; var gridY = Math.floor( heightSegments ) || 1; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var segment_width = width / gridX; var segment_height = height / gridY; var ix, iy; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segment_height - height_half; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } // indices for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = ix + gridX1 * iy; var b = ix + gridX1 * ( iy + 1 ); var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ var materialId = 0; function Material() { Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.lights = true; this.blending = NormalBlending; this.side = FrontSide; this.flatShading = false; this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaTest = 0; this.premultipliedAlpha = false; this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer this.visible = true; this.userData = {}; this.needsUpdate = true; } Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Material, isMaterial: true, onBeforeCompile: function () {}, setValues: function ( values ) { if ( values === undefined ) return; for ( var key in values ) { var newValue = values[ key ]; if ( newValue === undefined ) { console.warn( "THREE.Material: "" + key + "" parameter is undefined." ); continue; } // for backward compatability if shading is set in the constructor if ( key === "shading" ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( newValue === FlatShading ) ? true : false; continue; } var currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( "THREE." + this.type + ": "" + key + "" is not a property of this material." ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else if ( key === "overdraw" ) { // ensure overdraw is backwards-compatible with legacy boolean type this[ key ] = Number( newValue ); } else { this[ key ] = newValue; } } }, toJSON: function ( meta ) { var isRoot = ( meta === undefined || typeof meta === "string" ); if ( isRoot ) { meta = { textures: {}, images: {} }; } var data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; data.reflectivity = this.reflectivity; // Scale behind envMap } if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.size !== undefined ) data.size = this.size; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; // rotation (SpriteMaterial) if ( this.rotation !== 0 ) data.rotation = this.rotation; if ( this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.morphTargets === true ) data.morphTargets = true; if ( this.skinning === true ) data.skinning = true; if ( this.visible === false ) data.visible = false; if ( JSON.stringify( this.userData ) !== "{}" ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRoot ) { var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.fog = source.fog; this.lights = source.lights; this.blending = source.blending; this.side = source.side; this.flatShading = source.flatShading; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.premultipliedAlpha = source.premultipliedAlpha; this.overdraw = source.overdraw; this.visible = source.visible; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.clipShadows = source.clipShadows; this.clipIntersection = source.clipIntersection; var srcPlanes = source.clippingPlanes, dstPlanes = null; if ( srcPlanes !== null ) { var n = srcPlanes.length; dstPlanes = new Array( n ); for ( var i = 0; i !== n; ++ i ) dstPlanes[ i ] = srcPlanes[ i ].clone(); } this.clippingPlanes = dstPlanes; this.shadowSide = source.shadowSide; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: * } */ function MeshBasicMaterial( parameters ) { Material.call( this ); this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } MeshBasicMaterial.prototype = Object.create( Material.prototype ); MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; MeshBasicMaterial.prototype.isMeshBasicMaterial = true; MeshBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: , * * skinning: , * morphTargets: , * morphNormals: * } */ function ShaderMaterial( parameters ) { Material.call( this ); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; this.fragmentShader = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.skinning = false; // set to use skinning attribute streams this.morphTargets = false; // set to use morph targets this.morphNormals = false; // set to use morph normals this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv2": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; if ( parameters !== undefined ) { if ( parameters.attributes !== undefined ) { console.error( "THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead." ); } this.setValues( parameters ); } } ShaderMaterial.prototype = Object.create( Material.prototype ); ShaderMaterial.prototype.constructor = ShaderMaterial; ShaderMaterial.prototype.isShaderMaterial = true; ShaderMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = UniformsUtils.clone( source.uniforms ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; this.extensions = source.extensions; return this; }; ShaderMaterial.prototype.toJSON = function ( meta ) { var data = Material.prototype.toJSON.call( this, meta ); data.uniforms = this.uniforms; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; return data; }; /** * @author bhouston / http://clara.io */ function Ray( origin, direction ) { this.origin = ( origin !== undefined ) ? origin : new Vector3(); this.direction = ( direction !== undefined ) ? direction : new Vector3(); } Object.assign( Ray.prototype, { set: function ( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; }, at: function ( t, target ) { if ( target === undefined ) { console.warn( "THREE.Ray: .at() target is now required" ); target = new Vector3(); } return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); }, lookAt: function ( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; }, recast: function () { var v1 = new Vector3(); return function recast( t ) { this.origin.copy( this.at( t, v1 ) ); return this; }; }(), closestPointToPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Ray: .closestPointToPoint() target is now required" ); target = new Vector3(); } target.subVectors( point, this.origin ); var directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); }, distanceToPoint: function ( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); }, distanceSqToPoint: function () { var v1 = new Vector3(); return function distanceSqToPoint( point ) { var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return v1.distanceToSquared( point ); }; }(), distanceSqToSegment: function () { var segCenter = new Vector3(); var segDir = new Vector3(); var diff = new Vector3(); return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); segDir.copy( v1 ).sub( v0 ).normalize(); diff.copy( this.origin ).sub( segCenter ); var segExtent = v0.distanceTo( v1 ) * 0.5; var a01 = - this.direction.dot( segDir ); var b0 = diff.dot( this.direction ); var b1 = - diff.dot( segDir ); var c = diff.lengthSq(); var det = Math.abs( 1 - a01 * a01 ); var s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. var invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); } return sqrDist; }; }(), intersectSphere: function () { var v1 = new Vector3(); return function intersectSphere( sphere, target ) { v1.subVectors( sphere.center, this.origin ); var tca = v1.dot( this.direction ); var d2 = v1.dot( v1 ) - tca * tca; var radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; var thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere var t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere var t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if ( t0 < 0 && t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); }; }(), intersectsSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) <= sphere.radius; }, distanceToPlane: function ( plane ) { var denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; }, intersectPlane: function ( plane, target ) { var t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); }, intersectsPlane: function ( plane ) { // check if the ray lies on the plane first var distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } var denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; }, intersectBox: function ( box, target ) { var tmin, tmax, tymin, tymax, tzmin, tzmax; var invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; var origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if ( tymin > tmin || tmin !== tmin ) tmin = tymin; if ( tymax < tmax || tmax !== tmax ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); }, intersectsBox: ( function () { var v = new Vector3(); return function intersectsBox( box ) { return this.intersectBox( box, v ) !== null; }; } )(), intersectTriangle: function () { // Compute the offset origin, edges, and normal. var diff = new Vector3(); var edge1 = new Vector3(); var edge2 = new Vector3(); var normal = new Vector3(); return function intersectTriangle( a, b, c, backfaceCulling, target ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h edge1.subVectors( b, a ); edge2.subVectors( c, a ); normal.crossVectors( edge1, edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) var DdN = this.direction.dot( normal ); var sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } diff.subVectors( this.origin, a ); var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. var QdN = - sign * diff.dot( normal ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); }; }(), applyMatrix4: function ( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; }, equals: function ( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } } ); /** * @author bhouston / http://clara.io */ function Line3( start, end ) { this.start = ( start !== undefined ) ? start : new Vector3(); this.end = ( end !== undefined ) ? end : new Vector3(); } Object.assign( Line3.prototype, { set: function ( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .getCenter() target is now required" ); target = new Vector3(); } return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); }, delta: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .delta() target is now required" ); target = new Vector3(); } return target.subVectors( this.end, this.start ); }, distanceSq: function () { return this.start.distanceToSquared( this.end ); }, distance: function () { return this.start.distanceTo( this.end ); }, at: function ( t, target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .at() target is now required" ); target = new Vector3(); } return this.delta( target ).multiplyScalar( t ).add( this.start ); }, closestPointToPointParameter: function () { var startP = new Vector3(); var startEnd = new Vector3(); return function closestPointToPointParameter( point, clampToLine ) { startP.subVectors( point, this.start ); startEnd.subVectors( this.end, this.start ); var startEnd2 = startEnd.dot( startEnd ); var startEnd_startP = startEnd.dot( startP ); var t = startEnd_startP / startEnd2; if ( clampToLine ) { t = _Math.clamp( t, 0, 1 ); } return t; }; }(), closestPointToPoint: function ( point, clampToLine, target ) { var t = this.closestPointToPointParameter( point, clampToLine ); if ( target === undefined ) { console.warn( "THREE.Line3: .closestPointToPoint() target is now required" ); target = new Vector3(); } return this.delta( target ).multiplyScalar( t ).add( this.start ); }, applyMatrix4: function ( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; }, equals: function ( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Triangle( a, b, c ) { this.a = ( a !== undefined ) ? a : new Vector3(); this.b = ( b !== undefined ) ? b : new Vector3(); this.c = ( c !== undefined ) ? c : new Vector3(); } Object.assign( Triangle, { getNormal: function () { var v0 = new Vector3(); return function getNormal( a, b, c, target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getNormal() target is now required" ); target = new Vector3(); } target.subVectors( c, b ); v0.subVectors( a, b ); target.cross( v0 ); var targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); }; }(), // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html getBarycoord: function () { var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); return function getBarycoord( point, a, b, c, target ) { v0.subVectors( c, a ); v1.subVectors( b, a ); v2.subVectors( point, a ); var dot00 = v0.dot( v0 ); var dot01 = v0.dot( v1 ); var dot02 = v0.dot( v2 ); var dot11 = v1.dot( v1 ); var dot12 = v1.dot( v2 ); var denom = ( dot00 * dot11 - dot01 * dot01 ); if ( target === undefined ) { console.warn( "THREE.Triangle: .getBarycoord() target is now required" ); target = new Vector3(); } // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } var invDenom = 1 / denom; var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); }; }(), containsPoint: function () { var v1 = new Vector3(); return function containsPoint( point, a, b, c ) { Triangle.getBarycoord( point, a, b, c, v1 ); return ( v1.x >= 0 ) && ( v1.y >= 0 ) && ( ( v1.x + v1.y ) <= 1 ); }; }() } ); Object.assign( Triangle.prototype, { set: function ( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; }, setFromPointsAndIndices: function ( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; }, getArea: function () { var v0 = new Vector3(); var v1 = new Vector3(); return function getArea() { v0.subVectors( this.c, this.b ); v1.subVectors( this.a, this.b ); return v0.cross( v1 ).length() * 0.5; }; }(), getMidpoint: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getMidpoint() target is now required" ); target = new Vector3(); } return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); }, getNormal: function ( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); }, getPlane: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getPlane() target is now required" ); target = new Vector3(); } return target.setFromCoplanarPoints( this.a, this.b, this.c ); }, getBarycoord: function ( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); }, containsPoint: function ( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); }, intersectsBox: function ( box ) { return box.intersectsTriangle( this ); }, closestPointToPoint: function () { var plane = new Plane(); var edgeList = [ new Line3(), new Line3(), new Line3() ]; var projectedPoint = new Vector3(); var closestPoint = new Vector3(); return function closestPointToPoint( point, target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .closestPointToPoint() target is now required" ); target = new Vector3(); } var minDistance = Infinity; // project the point onto the plane of the triangle plane.setFromCoplanarPoints( this.a, this.b, this.c ); plane.projectPoint( point, projectedPoint ); // check if the projection lies within the triangle if ( this.containsPoint( projectedPoint ) === true ) { // if so, this is the closest point target.copy( projectedPoint ); } else { // if not, the point falls outside the triangle. the target is the closest point to the triangle"s edges or vertices edgeList[ 0 ].set( this.a, this.b ); edgeList[ 1 ].set( this.b, this.c ); edgeList[ 2 ].set( this.c, this.a ); for ( var i = 0; i < edgeList.length; i ++ ) { edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); var distance = projectedPoint.distanceToSquared( closestPoint ); if ( distance < minDistance ) { minDistance = distance; target.copy( closestPoint ); } } } return target; }; }(), equals: function ( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ */ function Mesh( geometry, material ) { Object3D.call( this ); this.type = "Mesh"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); this.drawMode = TrianglesDrawMode; this.updateMorphTargets(); } Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Mesh, isMesh: true, setDrawMode: function ( value ) { this.drawMode = value; }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.drawMode = source.drawMode; if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } return this; }, updateMorphTargets: function () { var geometry = this.geometry; var m, ml, name; if ( geometry.isBufferGeometry ) { var morphAttributes = geometry.morphAttributes; var keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { var morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } else { var morphTargets = geometry.morphTargets; if ( morphTargets !== undefined && morphTargets.length > 0 ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) { name = morphTargets[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } }, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); var vA = new Vector3(); var vB = new Vector3(); var vC = new Vector3(); var tempA = new Vector3(); var tempB = new Vector3(); var tempC = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); var barycoord = new Vector3(); var intersectionPoint = new Vector3(); var intersectionPointWorld = new Vector3(); function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { Triangle.getBarycoord( point, p1, p2, p3, barycoord ); uv1.multiplyScalar( barycoord.x ); uv2.multiplyScalar( barycoord.y ); uv3.multiplyScalar( barycoord.z ); uv1.add( uv2 ).add( uv3 ); return uv1.clone(); } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { var intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); } if ( intersect === null ) return null; intersectionPointWorld.copy( point ); intersectionPointWorld.applyMatrix4( object.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) { vA.fromBufferAttribute( position, a ); vB.fromBufferAttribute( position, b ); vC.fromBufferAttribute( position, c ); var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint ); if ( intersection ) { if ( uv ) { uvA.fromBufferAttribute( uv, a ); uvB.fromBufferAttribute( uv, b ); uvC.fromBufferAttribute( uv, c ); intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); } var face = new Face3( a, b, c ); Triangle.getNormal( vA, vB, vC, face.normal ); intersection.face = face; } return intersection; } return function raycast( raycaster, intersects ) { var geometry = this.geometry; var material = this.material; var matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; } var intersection; if ( geometry.isBufferGeometry ) { var a, b, c; var index = geometry.index; var position = geometry.attributes.position; var uv = geometry.attributes.uv; var i, l; if ( index !== null ) { // indexed buffer geometry for ( i = 0, l = index.count; i < l; i += 3 ) { a = index.getX( i ); b = index.getX( i + 1 ); c = index.getX( i + 2 ); intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } else if ( position !== undefined ) { // non-indexed buffer geometry for ( i = 0, l = position.count; i < l; i += 3 ) { a = i; b = i + 1; c = i + 2; intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } else if ( geometry.isGeometry ) { var fvA, fvB, fvC; var isMultiMaterial = Array.isArray( material ); var vertices = geometry.vertices; var faces = geometry.faces; var uvs; var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; for ( var f = 0, fl = faces.length; f < fl; f ++ ) { var face = faces[ f ]; var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; if ( faceMaterial === undefined ) continue; fvA = vertices[ face.a ]; fvB = vertices[ face.b ]; fvC = vertices[ face.c ]; if ( faceMaterial.morphTargets === true ) { var morphTargets = geometry.morphTargets; var morphInfluences = this.morphTargetInfluences; vA.set( 0, 0, 0 ); vB.set( 0, 0, 0 ); vC.set( 0, 0, 0 ); for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { var influence = morphInfluences[ t ]; if ( influence === 0 ) continue; var targets = morphTargets[ t ].vertices; vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); } vA.add( fvA ); vB.add( fvB ); vC.add( fvC ); fvA = vA; fvB = vB; fvC = vC; } intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); if ( intersection ) { if ( uvs && uvs[ f ] ) { var uvs_f = uvs[ f ]; uvA.copy( uvs_f[ 0 ] ); uvB.copy( uvs_f[ 1 ] ); uvC.copy( uvs_f[ 2 ] ); intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); } intersection.face = face; intersection.faceIndex = f; intersects.push( intersection ); } } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) { var clearColor = new Color( 0x000000 ); var clearAlpha = 0; var planeCamera, planeMesh; var boxMesh; function render( renderList, scene, camera, forceClear ) { var background = scene.background; if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && background.isCubeTexture ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), new ShaderMaterial( { uniforms: ShaderLib.cube.uniforms, vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: true, depthWrite: false, fog: false } ) ); boxMesh.geometry.removeAttribute( "normal" ); boxMesh.geometry.removeAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; objects.update( boxMesh ); } boxMesh.material.uniforms.tCube.value = background; renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null ); } else if ( background && background.isTexture ) { if ( planeCamera === undefined ) { planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) ); objects.update( planeMesh ); } planeMesh.material.map = background; // TODO Push this to renderList renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null ); } } function setClear( color, alpha ) { state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha ) { clearColor.set( color ); clearAlpha = alpha !== undefined ? alpha : 1; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBufferRenderer( gl, extensions, info ) { var mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode ); } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount ); info.update( count, mode, geometry.maxInstancedCount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLCapabilities( gl, extensions, parameters ) { var maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; var extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension !== null ) { maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } var precision = parameters.precision !== undefined ? parameters.precision : "highp"; var maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); var vertexTextures = maxVertexTextures > 0; var floatFragmentTextures = !! extensions.get( "OES_texture_float" ); var floatVertexTextures = vertexTextures && floatFragmentTextures; return { getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures }; } /** * @author tschw */ function WebGLClipping() { var scope = this, globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false, plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping, camera ) { var enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes( planes, camera, 0 ); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { var nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4, dstArray = cache.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); for ( var i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } cache.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { var nPlanes = planes !== null ? planes.length : 0, dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { var flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; return dstArray; } } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLExtensions( gl ) { var extensions = {}; return { get: function ( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } var extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } extensions[ name ] = extension; return extension; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLGeometries( gl, attributes, info ) { var geometries = {}; var wireframeAttributes = {}; function onGeometryDispose( event ) { var geometry = event.target; var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry.index !== null ) { attributes.remove( buffergeometry.index ); } for ( var name in buffergeometry.attributes ) { attributes.remove( buffergeometry.attributes[ name ] ); } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; // TODO Remove duplicate code var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ geometry.id ]; } attribute = wireframeAttributes[ buffergeometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ buffergeometry.id ]; } // info.memory.geometries --; } function get( object, geometry ) { var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry ) return buffergeometry; geometry.addEventListener( "dispose", onGeometryDispose ); if ( geometry.isBufferGeometry ) { buffergeometry = geometry; } else if ( geometry.isGeometry ) { if ( geometry._bufferGeometry === undefined ) { geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); } buffergeometry = geometry._bufferGeometry; } geometries[ geometry.id ] = buffergeometry; info.memory.geometries ++; return buffergeometry; } function update( geometry ) { var index = geometry.index; var geometryAttributes = geometry.attributes; if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } for ( var name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets var morphAttributes = geometry.morphAttributes; for ( var name in morphAttributes ) { var array = morphAttributes[ name ]; for ( var i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function getWireframeAttribute( geometry ) { var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) return attribute; var indices = []; var geometryIndex = geometry.index; var geometryAttributes = geometry.attributes; // console.time( "wireframe" ); if ( geometryIndex !== null ) { var array = geometryIndex.array; for ( var i = 0, l = array.length; i < l; i += 3 ) { var a = array[ i + 0 ]; var b = array[ i + 1 ]; var c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { var array = geometryAttributes.position.array; for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { var a = i + 0; var b = i + 1; var c = i + 2; indices.push( a, b, b, c, c, a ); } } // console.timeEnd( "wireframe" ); attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER ); wireframeAttributes[ geometry.id ] = attribute; return attribute; } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLIndexedBufferRenderer( gl, extensions, info ) { var mode; function setMode( value ) { mode = value; } var type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode ); } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); info.update( count, mode, geometry.maxInstancedCount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } /** * @author Mugen87 / https://github.com/Mugen87 */ function WebGLInfo( gl ) { var memory = { geometries: 0, textures: 0 }; var render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { instanceCount = instanceCount || 1; render.calls ++; switch ( mode ) { case gl.TRIANGLES: render.triangles += instanceCount * ( count / 3 ); break; case gl.TRIANGLE_STRIP: case gl.TRIANGLE_FAN: render.triangles += instanceCount * ( count - 2 ); break; case gl.LINES: render.lines += instanceCount * ( count / 2 ); break; case gl.LINE_STRIP: render.lines += instanceCount * ( count - 1 ); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.frame ++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl ) { var influencesList = {}; var morphInfluences = new Float32Array( 8 ); function update( object, geometry, material, program ) { var objectInfluences = object.morphTargetInfluences; var length = objectInfluences.length; var influences = influencesList[ geometry.id ]; if ( influences === undefined ) { // initialise list influences = []; for ( var i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } var morphTargets = material.morphTargets && geometry.morphAttributes.position; var morphNormals = material.morphNormals && geometry.morphAttributes.normal; // Remove current morphAttributes for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; if ( influence[ 1 ] !== 0 ) { if ( morphTargets ) geometry.removeAttribute( "morphTarget" + i ); if ( morphNormals ) geometry.removeAttribute( "morphNormal" + i ); } } // Collect influences for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); // Add morphAttributes for ( var i = 0; i < 8; i ++ ) { var influence = influences[ i ]; if ( influence ) { var index = influence[ 0 ]; var value = influence[ 1 ]; if ( value ) { if ( morphTargets ) geometry.addAttribute( "morphTarget" + i, morphTargets[ index ] ); if ( morphNormals ) geometry.addAttribute( "morphNormal" + i, morphNormals[ index ] ); morphInfluences[ i ] = value; continue; } } morphInfluences[ i ] = 0; } program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } return { update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLObjects( geometries, info ) { var updateList = {}; function update( object ) { var frame = info.render.frame; var geometry = object.geometry; var buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateList[ buffergeometry.id ] !== frame ) { if ( geometry.isGeometry ) { buffergeometry.updateFromObject( object ); } geometries.update( buffergeometry ); updateList[ buffergeometry.id ] = frame; } return buffergeometry; } function dispose() { updateList = {}; } return { update: update, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.flipY = false; } CubeTexture.prototype = Object.create( Texture.prototype ); CubeTexture.prototype.constructor = CubeTexture; CubeTexture.prototype.isCubeTexture = true; Object.defineProperty( CubeTexture.prototype, "images", { get: function () { return this.image; }, set: function ( value ) { this.image = value; } } ); /** * @author tschw * * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program, renderer )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [renderer] ) * * uploads a uniform value(s) * the "renderer" parameter is needed for sampler uniforms * * * Static methods of the top-level container (renderer factorizations): * * .upload( gl, seq, values, renderer ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (renderer factorizations): * * .setValue( gl, name, value ) * * sets uniform with name "name" to "value" * * .set( gl, obj, prop ) * * sets uniform from object and property with same name than uniform * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ var emptyTexture = new Texture(); var emptyCubeTexture = new CubeTexture(); // --- Base for inner nodes (including the root) --- function UniformContainer() { this.seq = []; this.map = {}; } // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) var arrayCacheF32 = []; var arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms var mat4array = new Float32Array( 16 ); var mat3array = new Float32Array( 9 ); var mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { var firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 var n = nBlocks * blockSize, r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( var i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( var i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( renderer, n ) { var r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( var i = 0; i !== n; ++ i ) r[ i ] = renderer.allocTextureUnit(); return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValue1f( gl, v ) { var cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } function setValue1i( gl, v ) { var cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValue2fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValue3fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValue4fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or MatrixN) function setValue2fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValue3fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValue4fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single texture (2D / Cube) function setValueT1( gl, v, renderer ) { var cache = this.cache; var unit = renderer.allocTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } renderer.setTexture2D( v || emptyTexture, unit ); } function setValueT6( gl, v, renderer ) { var cache = this.cache; var unit = renderer.allocTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } renderer.setTextureCube( v || emptyCubeTexture, unit ); } // Integer / Boolean vectors or arrays thereof (always flat arrays) function setValue2iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } function setValue3iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } function setValue4iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValue1f; // FLOAT case 0x8b50: return setValue2fv; // _VEC2 case 0x8b51: return setValue3fv; // _VEC3 case 0x8b52: return setValue4fv; // _VEC4 case 0x8b5a: return setValue2fm; // _MAT2 case 0x8b5b: return setValue3fm; // _MAT3 case 0x8b5c: return setValue4fm; // _MAT4 case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES case 0x8b60: return setValueT6; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // Array of scalars function setValue1fv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform1fv( this.addr, v ); copyArray( cache, v ); } function setValue1iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform1iv( this.addr, v ); copyArray( cache, v ); } // Array of vectors (flat or from THREE classes) function setValueV2a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 2 ); if ( arraysEqual( cache, data ) ) return; gl.uniform2fv( this.addr, data ); this.updateCache( data ); } function setValueV3a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 3 ); if ( arraysEqual( cache, data ) ) return; gl.uniform3fv( this.addr, data ); this.updateCache( data ); } function setValueV4a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 4 ); if ( arraysEqual( cache, data ) ) return; gl.uniform4fv( this.addr, data ); this.updateCache( data ); } // Array of matrices (flat or from THREE clases) function setValueM2a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 4 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix2fv( this.addr, false, data ); this.updateCache( data ); } function setValueM3a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 9 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix3fv( this.addr, false, data ); this.updateCache( data ); } function setValueM4a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 16 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix4fv( this.addr, false, data ); this.updateCache( data ); } // Array of textures (2D / Cube) function setValueT1a( gl, v, renderer ) { var cache = this.cache; var n = v.length; var units = allocTexUnits( renderer, n ); if ( arraysEqual( cache, units ) === false ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( var i = 0; i !== n; ++ i ) { renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT6a( gl, v, renderer ) { var cache = this.cache; var n = v.length; var units = allocTexUnits( renderer, n ); if ( arraysEqual( cache, units ) === false ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( var i = 0; i !== n; ++ i ) { renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValue1fv; // FLOAT case 0x8b50: return setValueV2a; // _VEC2 case 0x8b51: return setValueV3a; // _VEC3 case 0x8b52: return setValueV4a; // _VEC4 case 0x8b5a: return setValueM2a; // _MAT2 case 0x8b5b: return setValueM3a; // _MAT3 case 0x8b5c: return setValueM4a; // _MAT4 case 0x8b5e: return setValueT1a; // SAMPLER_2D case 0x8b60: return setValueT6a; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // --- Uniform Classes --- function SingleUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function ( data ) { var cache = this.cache; if ( data instanceof Float32Array && cache.length !== data.length ) { this.cache = new Float32Array( data.length ); } copyArray( cache, data ); }; function StructuredUniform( id ) { this.id = id; UniformContainer.call( this ); // mix-in } StructuredUniform.prototype.setValue = function ( gl, value ) { // Note: Don"t need an extra "renderer" parameter, since samplers // are not allowed in structured uniforms. var seq = this.seq; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; u.setValue( gl, value[ u.id ] ); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings var RePathPart = /([wd_]+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { var path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { var match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex, id = match[ 1 ], idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist var map = container.map, next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container function WebGLUniforms( gl, program, renderer ) { UniformContainer.call( this ); this.renderer = renderer; var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( var i = 0; i < n; ++ i ) { var info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } WebGLUniforms.prototype.setValue = function ( gl, name, value ) { var u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, this.renderer ); }; WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { var v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); }; // Static interface WebGLUniforms.upload = function ( gl, seq, values, renderer ) { for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, renderer ); } } }; WebGLUniforms.seqWithValue = function ( seq, values ) { var r = []; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; }; /** * @author mrdoob / http://mrdoob.com/ */ function addLineNumbers( string ) { var lines = string.split( " " ); for ( var i = 0; i < lines.length; i ++ ) { lines[ i ] = ( i + 1 ) + ": " + lines[ i ]; } return lines.join( " " ); } function WebGLShader( gl, type, string ) { var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( "THREE.WebGLShader: Shader couldn"t compile." ); } if ( gl.getShaderInfoLog( shader ) !== "" ) { console.warn( "THREE.WebGLShader: gl.getShaderInfoLog()", type === gl.VERTEX_SHADER ? "vertex" : "fragment", gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); } // --enable-privileged-webgl-extension // console.log( type, gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return shader; } /** * @author mrdoob / http://mrdoob.com/ */ var programIdCount = 0; function getEncodingComponents( encoding ) { switch ( encoding ) { case LinearEncoding: return [ "Linear", "( value )" ]; case sRGBEncoding: return [ "sRGB", "( value )" ]; case RGBEEncoding: return [ "RGBE", "( value )" ]; case RGBM7Encoding: return [ "RGBM", "( value, 7.0 )" ]; case RGBM16Encoding: return [ "RGBM", "( value, 16.0 )" ]; case RGBDEncoding: return [ "RGBD", "( value, 256.0 )" ]; case GammaEncoding: return [ "Gamma", "( value, float( GAMMA_FACTOR ) )" ]; default: throw new Error( "unsupported encoding: " + encoding ); } } function getTexelDecodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; } function getTexelEncodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { var toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case Uncharted2ToneMapping: toneMappingName = "Uncharted2"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; default: throw new Error( "unsupported toneMapping: " + toneMapping ); } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; var chunks = [ ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( "EXT_frag_depth" ) ? "#extension GL_EXT_frag_depth : enable" : "", ( extensions.drawBuffers ) && rendererExtensions.get( "WEBGL_draw_buffers" ) ? "#extension GL_EXT_draw_buffers : require" : "", ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( "EXT_shader_texture_lod" ) ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { var chunks = []; for ( var name in defines ) { var value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { var attributes = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveAttrib( program, i ); var name = info.name; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } function parseIncludes( string ) { var pattern = /^[ ]*#include +<([wd.]+)>/gm; function replace( match, include ) { var replace = ShaderChunk[ include ]; if ( replace === undefined ) { throw new Error( "Can not resolve #include <" + include + ">" ); } return parseIncludes( replace ); } return string.replace( pattern, replace ); } function unrollLoops( string ) { var pattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; function replace( match, start, end, snippet ) { var unroll = ""; for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { unroll += snippet.replace( /[ i ]/g, "[ " + i + " ]" ); } return unroll; } return string.replace( pattern, replace ); } function WebGLProgram( renderer, extensions, code, material, shader, parameters ) { var gl = renderer.context; var defines = material.defines; var vertexShader = shader.vertexShader; var fragmentShader = shader.fragmentShader; var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; if ( parameters.shadowMapType === PCFShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; } var envMapTypeDefine = "ENVMAP_TYPE_CUBE"; var envMapModeDefine = "ENVMAP_MODE_REFLECTION"; var envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; if ( parameters.envMap ) { switch ( material.envMap.mapping ) { case CubeReflectionMapping: case CubeRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE"; break; case CubeUVReflectionMapping: case CubeUVRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE_UV"; break; case EquirectangularReflectionMapping: case EquirectangularRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_EQUIREC"; break; case SphericalReflectionMapping: envMapTypeDefine = "ENVMAP_TYPE_SPHERE"; break; } switch ( material.envMap.mapping ) { case CubeRefractionMapping: case EquirectangularRefractionMapping: envMapModeDefine = "ENVMAP_MODE_REFRACTION"; break; } switch ( material.combine ) { case MultiplyOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; break; case MixOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MIX"; break; case AddOperation: envMapBlendingDefine = "ENVMAP_BLENDING_ADD"; break; } } var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; // console.log( "building new program " ); // var customExtensions = generateExtensions( material.extensions, parameters, extensions ); var customDefines = generateDefines( defines ); // var program = gl.createProgram(); var prefixVertex, prefixFragment; if ( material.isRawShaderMaterial ) { prefixVertex = [ customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixVertex.length > 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define GAMMA_FACTOR " + gammaFactorDefine, "#define MAX_BONES " + parameters.maxBones, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 modelMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_COLOR", " attribute vec3 color;", "#endif", "#ifdef USE_MORPHTARGETS", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest + ( parameters.alphaTest % 1 ? "" : ".0" ) : "", // add ".0" if integer "#define GAMMA_FACTOR " + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", parameters.envMap && extensions.get( "EXT_shader_texture_lod" ) ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ "encodings_pars_fragment" ] : "", // this code is required here because it is used by the various encoding/decoding function defined below parameters.mapEncoding ? getTexelDecodingFunction( "mapTexelToLinear", parameters.mapEncoding ) : "", parameters.envMapEncoding ? getTexelDecodingFunction( "envMapTexelToLinear", parameters.envMapEncoding ) : "", parameters.emissiveMapEncoding ? getTexelDecodingFunction( "emissiveMapTexelToLinear", parameters.emissiveMapEncoding ) : "", parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : "", parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = parseIncludes( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = parseIncludes( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( material.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, material.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); var programLog = gl.getProgramInfoLog( program ).trim(); var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); var runnable = true; var haveDiagnostics = true; // console.log( "**VERTEX**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glVertexShader ) ); // console.log( "**FRAGMENT**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glFragmentShader ) ); if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; console.error( "THREE.WebGLProgram: shader error: ", gl.getError(), "gl.VALIDATE_STATUS", gl.getProgramParameter( program, gl.VALIDATE_STATUS ), "gl.getProgramInfoLog", programLog, vertexLog, fragmentLog ); } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: gl.getProgramInfoLog()", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, material: material, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations var cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program, renderer ); } return cachedUniforms; }; // set up caching for attribute locations var cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { gl.deleteProgram( program ); this.program = undefined; }; // DEPRECATED Object.defineProperties( this, { uniforms: { get: function () { console.warn( "THREE.WebGLProgram: .uniforms is now .getUniforms()." ); return this.getUniforms(); } }, attributes: { get: function () { console.warn( "THREE.WebGLProgram: .attributes is now .getAttributes()." ); return this.getAttributes(); } } } ); // this.name = shader.name; this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLPrograms( renderer, extensions, capabilities ) { var programs = []; var shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "phong", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow" }; var parameterNames = [ "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "shadowMapEnabled", "shadowMapType", "toneMapping", "physicallyCorrectLights", "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering" ]; function allocateBones( object ) { var skeleton = object.skeleton; var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = capabilities.maxVertexUniforms; var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = Math.min( nVertexMatrices, bones.length ); if ( maxBones < bones.length ) { console.warn( "THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "." ); return 0; } return maxBones; } } function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; if ( ! map ) { encoding = LinearEncoding; } else if ( map.isTexture ) { encoding = map.encoding; } else if ( map.isWebGLRenderTarget ) { console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don"t use render targets as textures. Use their .texture property instead." ); encoding = map.texture.encoding; } // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. if ( encoding === LinearEncoding && gammaOverrideLinear ) { encoding = GammaEncoding; } return encoding; } this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; var precision = capabilities.precision; if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } var currentRenderTarget = renderer.getRenderTarget(); var parameters = { shaderID: shaderID, precision: precision, supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, gradientMap: !! material.gradientMap, combine: material.combine, vertexColors: material.vertexColors, fog: !! fog, useFog: material.fog, fogExp: ( fog && fog.isFogExp2 ), flatShading: material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, skinning: material.skinning && maxBones > 0, maxBones: maxBones, useVertexTexture: capabilities.floatVertexTextures, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: renderer.maxMorphTargets, maxMorphNormals: renderer.maxMorphNormals, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numClippingPlanes: nClipPlanes, numClipIntersection: nClipIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, alphaTest: material.alphaTest, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false }; return parameters; }; this.getProgramCode = function ( material, parameters ) { var array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( material.fragmentShader ); array.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { array.push( name ); array.push( material.defines[ name ] ); } } for ( var i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[ parameterNames[ i ] ] ); } array.push( material.onBeforeCompile.toString() ); array.push( renderer.gammaOutput ); return array.join(); }; this.acquireProgram = function ( material, shader, parameters, code ) { var program; // Check if code has been already compiled for ( var p = 0, pl = programs.length; p < pl; p ++ ) { var programInfo = programs[ p ]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, extensions, code, material, shader, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function ( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set var i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } }; // Exposed for resource monitoring & error feedback via renderer.info: this.programs = programs; } /** * @author fordacious / fordacious.github.io */ function WebGLProperties() { var properties = new WeakMap(); function get( object ) { var map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.program && b.program && a.program !== b.program ) { return a.program.id - b.program.id; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { var renderItems = []; var renderItemsIndex = 0; var opaque = []; var transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transparent.length = 0; } function push( object, geometry, material, z, group ) { var renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, program: material.program, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.program = material.program; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } ( material.transparent === true ? transparent : opaque ).push( renderItem ); renderItemsIndex ++; } function sort() { if ( opaque.length > 1 ) opaque.sort( painterSortStable ); if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable ); } return { opaque: opaque, transparent: transparent, init: init, push: push, sort: sort }; } function WebGLRenderLists() { var lists = {}; function get( scene, camera ) { var hash = scene.id + "," + camera.id; var list = lists[ hash ]; if ( list === undefined ) { // console.log( "THREE.WebGLRenderLists:", hash ); list = new WebGLRenderList(); lists[ hash ] = list; } return list; } function dispose() { lists = {}; } return { get: get, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function UniformsCache() { var lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } var uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color(), shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() // TODO (abelnation): set RectAreaLight shadow uniforms }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } var count = 0; function WebGLLights() { var cache = new UniformsCache(); var state = { id: count ++, hash: "", ambient: [ 0, 0, 0 ], directional: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotShadowMap: [], spotShadowMatrix: [], rectArea: [], point: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [] }; var vector3 = new Vector3(); var matrix4 = new Matrix4(); var matrix42 = new Matrix4(); function setup( lights, shadows, camera ) { var r = 0, g = 0, b = 0; var directionalLength = 0; var pointLength = 0; var spotLength = 0; var rectAreaLength = 0; var hemiLength = 0; var viewMatrix = camera.matrixWorldInverse; for ( var i = 0, l = lights.length; i < l; i ++ ) { var light = lights[ i ]; var color = light.color; var intensity = light.intensity; var distance = light.distance; var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if ( light.isDirectionalLight ) { var uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.distance = distance; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.spotShadowMap[ spotLength ] = shadowMap; state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; state.spot[ spotLength ] = uniforms; spotLength ++; } else if ( light.isRectAreaLight ) { var uniforms = cache.get( light ); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); // TODO (abelnation): RectAreaLight distance? // uniforms.distance = distance; state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.distance = light.distance; uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; uniforms.shadowCameraNear = shadow.camera.near; uniforms.shadowCameraFar = shadow.camera.far; } state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { var uniforms = cache.get( light ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); uniforms.direction.normalize(); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.hash = state.id + "," + directionalLength + "," + pointLength + "," + spotLength + "," + rectAreaLength + "," + hemiLength + "," + shadows.length; } return { setup: setup, state: state }; } /** * @author Mugen87 / https://github.com/Mugen87 */ function WebGLRenderState() { var lights = new WebGLLights(); var lightsArray = []; var shadowsArray = []; var spritesArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; spritesArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function pushSprite( shadowLight ) { spritesArray.push( shadowLight ); } function setupLights( camera ) { lights.setup( lightsArray, shadowsArray, camera ); } var state = { lightsArray: lightsArray, shadowsArray: shadowsArray, spritesArray: spritesArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, pushLight: pushLight, pushShadow: pushShadow, pushSprite: pushSprite }; } function WebGLRenderStates() { var renderStates = {}; function get( scene, camera ) { var hash = scene.id + "," + camera.id; var renderState = renderStates[ hash ]; if ( renderState === undefined ) { renderState = new WebGLRenderState(); renderStates[ hash ] = renderState; } return renderState; } function dispose() { renderStates = {}; } return { get: get, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / https://clara.io * @author WestLangley / http://github.com/WestLangley * * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ function MeshDepthMaterial( parameters ) { Material.call( this ); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDepthMaterial.prototype = Object.create( Material.prototype ); MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; MeshDepthMaterial.prototype.isMeshDepthMaterial = true; MeshDepthMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.depthPacking = source.depthPacking; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * skinning: , * morphTargets: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ function MeshDistanceMaterial( parameters ) { Material.call( this ); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDistanceMaterial.prototype = Object.create( Material.prototype ); MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; MeshDistanceMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.referencePosition.copy( source.referencePosition ); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { var _frustum = new Frustum(), _projScreenMatrix = new Matrix4(), _shadowMapSize = new Vector2(), _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ), _lookTarget = new Vector3(), _lightPositionWorld = new Vector3(), _MorphingFlag = 1, _SkinningFlag = 2, _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, _depthMaterials = new Array( _NumberOfMaterialVariants ), _distanceMaterials = new Array( _NumberOfMaterialVariants ), _materialCache = {}; var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; var cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; var cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; var cube2DViewPorts = [ new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() ]; // init for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { var useMorphing = ( i & _MorphingFlag ) !== 0; var useSkinning = ( i & _SkinningFlag ) !== 0; var depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking, morphTargets: useMorphing, skinning: useSkinning } ); _depthMaterials[ i ] = depthMaterial; // var distanceMaterial = new MeshDistanceMaterial( { morphTargets: useMorphing, skinning: useSkinning } ); _distanceMaterials[ i ] = distanceMaterial; } // var scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; // TODO Clean up (needed in case of contextlost) var _gl = _renderer.context; var _state = _renderer.state; // Set GL state for depth map. _state.disable( _gl.BLEND ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // render depth map var faceCount; for ( var i = 0, il = lights.length; i < il; i ++ ) { var light = lights[ i ]; var shadow = light.shadow; var isPointLight = light && light.isPointLight; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } var shadowCamera = shadow.camera; _shadowMapSize.copy( shadow.mapSize ); _shadowMapSize.min( _maxShadowMapSize ); if ( isPointLight ) { var vpWidth = _shadowMapSize.x; var vpHeight = _shadowMapSize.y; // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); // negative X cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); // positive Z cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); // negative Z cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); // positive Y cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); // negative Y cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); _shadowMapSize.x *= 4.0; _shadowMapSize.y *= 2.0; } if ( shadow.map === null ) { var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadowCamera.updateProjectionMatrix(); } if ( shadow.isSpotLightShadow ) { shadow.update( light ); } var shadowMap = shadow.map; var shadowMatrix = shadow.matrix; _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld ); if ( isPointLight ) { faceCount = 6; // for point lights we set the shadow matrix to be a translation-only matrix // equal to inverse of the light"s position shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); } else { faceCount = 1; _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); // compute shadow matrix shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( shadowCamera.projectionMatrix ); shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); } _renderer.setRenderTarget( shadowMap ); _renderer.clear(); // render shadow map for each cube face (if omni-directional) or // run a single pass if not for ( var face = 0; face < faceCount; face ++ ) { if ( isPointLight ) { _lookTarget.copy( shadowCamera.position ); _lookTarget.add( cubeDirections[ face ] ); shadowCamera.up.copy( cubeUps[ face ] ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); var vpDimensions = cube2DViewPorts[ face ]; _state.viewport( vpDimensions ); } // update camera matrices and frustum _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); // set object matrices & frustum culling renderObject( scene, camera, shadowCamera, isPointLight ); } } scope.needsUpdate = false; }; function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) { var geometry = object.geometry; var result = null; var materialVariants = _depthMaterials; var customMaterial = object.customDepthMaterial; if ( isPointLight ) { materialVariants = _distanceMaterials; customMaterial = object.customDistanceMaterial; } if ( ! customMaterial ) { var useMorphing = false; if ( material.morphTargets ) { if ( geometry && geometry.isBufferGeometry ) { useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; } else if ( geometry && geometry.isGeometry ) { useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; } } if ( object.isSkinnedMesh && material.skinning === false ) { console.warn( "THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:", object ); } var useSkinning = object.isSkinnedMesh && material.skinning; var variantIndex = 0; if ( useMorphing ) variantIndex |= _MorphingFlag; if ( useSkinning ) variantIndex |= _SkinningFlag; result = materialVariants[ variantIndex ]; } else { result = customMaterial; } if ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) { // in this case we need a unique material instance reflecting the // appropriate state var keyA = result.uuid, keyB = material.uuid; var materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } var cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( isPointLight && result.isMeshDistanceMaterial ) { result.referencePosition.copy( lightPositionWorld ); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject( object, camera, shadowCamera, isPointLight ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); var geometry = _objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var k = 0, kl = groups.length; k < kl; k ++ ) { var group = groups[ k ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, isPointLight ); } } } /** * @author mrdoob / http://mrdoob.com/ */ function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.needsUpdate = true; } CanvasTexture.prototype = Object.create( Texture.prototype ); CanvasTexture.prototype.constructor = CanvasTexture; CanvasTexture.prototype.isCanvasTexture = true; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) { var vertexBuffer, elementBuffer; var program, attributes, uniforms; var texture; // decompose matrixWorld var spritePosition = new Vector3(); var spriteRotation = new Quaternion(); var spriteScale = new Vector3(); function init() { var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0.5, - 0.5, 1, 0, 0.5, 0.5, 1, 1, - 0.5, 0.5, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); program = createProgram(); attributes = { position: gl.getAttribLocation( program, "position" ), uv: gl.getAttribLocation( program, "uv" ) }; uniforms = { uvOffset: gl.getUniformLocation( program, "uvOffset" ), uvScale: gl.getUniformLocation( program, "uvScale" ), rotation: gl.getUniformLocation( program, "rotation" ), center: gl.getUniformLocation( program, "center" ), scale: gl.getUniformLocation( program, "scale" ), color: gl.getUniformLocation( program, "color" ), map: gl.getUniformLocation( program, "map" ), opacity: gl.getUniformLocation( program, "opacity" ), modelViewMatrix: gl.getUniformLocation( program, "modelViewMatrix" ), projectionMatrix: gl.getUniformLocation( program, "projectionMatrix" ), fogType: gl.getUniformLocation( program, "fogType" ), fogDensity: gl.getUniformLocation( program, "fogDensity" ), fogNear: gl.getUniformLocation( program, "fogNear" ), fogFar: gl.getUniformLocation( program, "fogFar" ), fogColor: gl.getUniformLocation( program, "fogColor" ), fogDepth: gl.getUniformLocation( program, "fogDepth" ), alphaTest: gl.getUniformLocation( program, "alphaTest" ) }; var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = 8; canvas.height = 8; var context = canvas.getContext( "2d" ); context.fillStyle = "white"; context.fillRect( 0, 0, 8, 8 ); texture = new CanvasTexture( canvas ); } this.render = function ( sprites, scene, camera ) { if ( sprites.length === 0 ) return; // setup gl if ( program === undefined ) { init(); } state.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.position ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); state.disable( gl.CULL_FACE ); state.enable( gl.BLEND ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); state.activeTexture( gl.TEXTURE0 ); gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; var sceneFogType = 0; var fog = scene.fog; if ( fog ) { gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); if ( fog.isFog ) { gl.uniform1f( uniforms.fogNear, fog.near ); gl.uniform1f( uniforms.fogFar, fog.far ); gl.uniform1i( uniforms.fogType, 1 ); oldFogType = 1; sceneFogType = 1; } else if ( fog.isFogExp2 ) { gl.uniform1f( uniforms.fogDensity, fog.density ); gl.uniform1i( uniforms.fogType, 2 ); oldFogType = 2; sceneFogType = 2; } } else { gl.uniform1i( uniforms.fogType, 0 ); oldFogType = 0; sceneFogType = 0; } // update positions and sort for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; } sprites.sort( painterSortStable ); // render all sprites var scale = []; var center = []; for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; var material = sprite.material; if ( material.visible === false ) continue; sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined ); gl.uniform1f( uniforms.alphaTest, material.alphaTest ); gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); scale[ 0 ] = spriteScale.x; scale[ 1 ] = spriteScale.y; center[ 0 ] = sprite.center.x - 0.5; center[ 1 ] = sprite.center.y - 0.5; var fogType = 0; if ( scene.fog && material.fog ) { fogType = sceneFogType; } if ( oldFogType !== fogType ) { gl.uniform1i( uniforms.fogType, fogType ); oldFogType = fogType; } if ( material.map !== null ) { gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { gl.uniform2f( uniforms.uvOffset, 0, 0 ); gl.uniform2f( uniforms.uvScale, 1, 1 ); } gl.uniform1f( uniforms.opacity, material.opacity ); gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); gl.uniform1f( uniforms.rotation, material.rotation ); gl.uniform2fv( uniforms.center, center ); gl.uniform2fv( uniforms.scale, scale ); state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); state.buffers.depth.setTest( material.depthTest ); state.buffers.depth.setMask( material.depthWrite ); state.buffers.color.setMask( material.colorWrite ); textures.setTexture2D( material.map || texture, 0 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined ); } // restore gl state.enable( gl.CULL_FACE ); state.reset(); }; function createProgram() { var program = gl.createProgram(); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); gl.shaderSource( vertexShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform float rotation;", "uniform vec2 center;", "uniform vec2 scale;", "uniform vec2 uvOffset;", "uniform vec2 uvScale;", "attribute vec2 position;", "attribute vec2 uv;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vUV = uvOffset + uv * uvScale;", " vec2 alignedPosition = ( position - center ) * scale;", " vec2 rotatedPosition;", " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", " vec4 mvPosition;", " mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", " mvPosition.xy += rotatedPosition;", " gl_Position = projectionMatrix * mvPosition;", " fogDepth = - mvPosition.z;", "}" ].join( " " ) ); gl.shaderSource( fragmentShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform vec3 color;", "uniform sampler2D map;", "uniform float opacity;", "uniform int fogType;", "uniform vec3 fogColor;", "uniform float fogDensity;", "uniform float fogNear;", "uniform float fogFar;", "uniform float alphaTest;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vec4 texture = texture2D( map, vUV );", " gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", " if ( gl_FragColor.a < alphaTest ) discard;", " if ( fogType > 0 ) {", " float fogFactor = 0.0;", " if ( fogType == 1 ) {", " fogFactor = smoothstep( fogNear, fogFar, fogDepth );", " } else {", " const float LOG2 = 1.442695;", " fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );", " fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", " }", " gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );", " }", "}" ].join( " " ) ); gl.compileShader( vertexShader ); gl.compileShader( fragmentShader ); gl.attachShader( program, vertexShader ); gl.attachShader( program, fragmentShader ); gl.linkProgram( program ); return program; } function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return b.id - a.id; } } } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLState( gl, extensions, utils ) { function ColorBuffer() { var locked = false; var color = new Vector4(); var currentColorMask = null; var currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { var locked = false; var currentDepthMask = null; var currentDepthFunc = null; var currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { if ( depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } } else { gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { var locked = false; var currentStencilMask = null; var currentStencilFunc = null; var currentStencilRef = null; var currentStencilFuncMask = null; var currentStencilFail = null; var currentStencilZFail = null; var currentStencilZPass = null; var currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // var colorBuffer = new ColorBuffer(); var depthBuffer = new DepthBuffer(); var stencilBuffer = new StencilBuffer(); var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var newAttributes = new Uint8Array( maxVertexAttributes ); var enabledAttributes = new Uint8Array( maxVertexAttributes ); var attributeDivisors = new Uint8Array( maxVertexAttributes ); var capabilities = {}; var compressedTextureFormats = null; var currentProgram = null; var currentBlending = null; var currentBlendEquation = null; var currentBlendSrc = null; var currentBlendDst = null; var currentBlendEquationAlpha = null; var currentBlendSrcAlpha = null; var currentBlendDstAlpha = null; var currentPremultipledAlpha = false; var currentFlipSided = null; var currentCullFace = null; var currentLineWidth = null; var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); var lineWidthAvailable = false; var version = 0; var glVersion = gl.getParameter( gl.VERSION ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL ([0-9])/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES ([0-9])/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } var currentTextureSlot = null; var currentBoundTextures = {}; var currentScissor = new Vector4(); var currentViewport = new Vector4(); function createTexture( type, target, count ) { var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. var texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( var i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } return texture; } var emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); enable( gl.BLEND ); setBlending( NormalBlending ); // function initAttributes() { for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function enable( id ) { if ( capabilities[ id ] !== true ) { gl.enable( id ); capabilities[ id ] = true; } } function disable( id ) { if ( capabilities[ id ] !== false ) { gl.disable( id ); capabilities[ id ] = false; } } function getCompressedTextureFormats() { if ( compressedTextureFormats === null ) { compressedTextureFormats = []; if ( extensions.get( "WEBGL_compressed_texture_pvrtc" ) || extensions.get( "WEBGL_compressed_texture_s3tc" ) || extensions.get( "WEBGL_compressed_texture_etc1" ) || extensions.get( "WEBGL_compressed_texture_astc" ) ) { var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); for ( var i = 0; i < formats.length; i ++ ) { compressedTextureFormats.push( formats[ i ] ); } } } return compressedTextureFormats; } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending !== NoBlending ) { enable( gl.BLEND ); } else { disable( gl.BLEND ); } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { switch ( blending ) { case AdditiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); } break; case SubtractiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); } break; case MultiplyBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); } break; default: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } } } currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; } else { blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } } currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); var flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); material.transparent === true ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) : setBlending( NoBlending ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { if ( currentTextureSlot === null ) { activeTexture(); } var boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ currentTextureSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } // function reset() { for ( var i = 0; i < enabledAttributes.length; i ++ ) { if ( enabledAttributes[ i ] === 1 ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } capabilities = {}; compressedTextureFormats = null; currentTextureSlot = null; currentBoundTextures = {}; currentProgram = null; currentBlending = null; currentFlipSided = null; currentCullFace = null; colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, initAttributes: initAttributes, enableAttribute: enableAttribute, enableAttributeAndDivisor: enableAttributeAndDivisor, disableUnusedAttributes: disableUnusedAttributes, enable: enable, disable: disable, getCompressedTextureFormats: getCompressedTextureFormats, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, scissor: scissor, viewport: viewport, reset: reset }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { var _isWebGL2 = ( typeof WebGL2RenderingContext !== "undefined" && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */ var _videoTextures = {}; var _canvas; // function clampToMaxSize( image, maxSize ) { if ( image.width > maxSize || image.height > maxSize ) { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); return; } // Warning: Scaling through the canvas will only work with images that use // premultiplied alpha. var scale = maxSize / Math.max( image.width, image.height ); var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = Math.floor( image.width * scale ); canvas.height = Math.floor( image.height * scale ); var context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); console.warn( "THREE.WebGLRenderer: image is too big (" + image.width + "x" + image.height + "). Resized to " + canvas.width + "x" + canvas.height, image ); return canvas; } return image; } function isPowerOfTwo( image ) { return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); } function makePowerOfTwo( image ) { if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) { if ( _canvas === undefined ) _canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); _canvas.width = _Math.floorPowerOfTwo( image.width ); _canvas.height = _Math.floorPowerOfTwo( image.height ); var context = _canvas.getContext( "2d" ); context.drawImage( image, 0, 0, _canvas.width, _canvas.height ); console.warn( "THREE.WebGLRenderer: image is not power of two (" + image.width + "x" + image.height + "). Resized to " + _canvas.width + "x" + _canvas.height, image ); return _canvas; } return image; } function textureNeedsPowerOfTwo( texture ) { return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) { return texture.generateMipmaps && isPowerOfTwo && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target, texture, width, height ) { _gl.generateMipmap( target ); var textureProperties = properties.get( texture ); // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { var texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { delete _videoTextures[ texture.id ]; } info.memory.textures --; } function onRenderTargetDispose( event ) { var renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); info.memory.textures --; } // function deallocateTexture( texture ) { var textureProperties = properties.get( texture ); if ( texture.image && textureProperties.__image__webglTextureCube ) { // cube texture _gl.deleteTexture( textureProperties.__image__webglTextureCube ); } else { // 2D texture if ( textureProperties.__webglInit === undefined ) return; _gl.deleteTexture( textureProperties.__webglTexture ); } // remove all webgl properties properties.remove( texture ); } function deallocateRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); if ( ! renderTarget ) return; if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } properties.remove( renderTarget.texture ); properties.remove( renderTarget ); } // function setTexture2D( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { var image = texture.image; if ( image === undefined ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is undefined", texture ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete", texture ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); } function setTextureCube( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.image.length === 6 ) { if ( texture.version > 0 && textureProperties.__version !== texture.version ) { if ( ! textureProperties.__image__webglTextureCube ) { texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__image__webglTextureCube = _gl.createTexture(); info.memory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = ( texture && texture.isCompressedTexture ); var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } } var image = cubeImage[ 0 ], isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed ) { if ( isDataTexture ) { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); } } else { var mipmap, mipmaps = cubeImage[ i ].mipmaps; for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } if ( ! isCompressed ) { textureProperties.__maxMipLevel = 0; } else { textureProperties.__maxMipLevel = mipmaps.length - 1; } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { // We assume images for cube map have the same size. generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } else { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); } } } function setTextureCubeDynamic( texture, slot ) { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); } function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { var extension; if ( isPowerOfTwoImage ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.", texture ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.", texture ); } } extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension ) { if ( texture.type === FloatType && extensions.get( "OES_texture_float_linear" ) === null ) return; if ( texture.type === HalfFloatType && extensions.get( "OES_texture_half_float_linear" ) === null ) return; if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function uploadTexture( textureProperties, texture, slot ) { if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { image = makePowerOfTwo( image ); } var isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); var mipmap, mipmaps = texture.mipmaps; if ( texture.isDepthTexture ) { // populate depth texture with dummy data var internalFormat = _gl.DEPTH_COMPONENT; if ( texture.type === FloatType ) { if ( ! _isWebGL2 ) throw new Error( "Float Depth Texture only supported in WebGL2.0" ); internalFormat = _gl.DEPTH_COMPONENT32F; } else if ( _isWebGL2 ) { // WebGL 2.0 requires signed internalformat for glTexImage2D internalFormat = _gl.DEPTH_COMPONENT16; } if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedShortType; glType = utils.convert( texture.type ); } } // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.format === DepthStencilFormat ) { internalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } texture.generateMipmaps = false; textureProperties.__maxMipLevel = mipmaps.length - 1; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); textureProperties.__maxMipLevel = 0; } } else if ( texture.isCompressedTexture ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } textureProperties.__maxMipLevel = mipmaps.length - 1; } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } texture.generateMipmaps = false; textureProperties.__maxMipLevel = mipmaps.length - 1; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); textureProperties.__maxMipLevel = 0; } } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { var glFormat = utils.convert( renderTarget.texture.format ); var glType = utils.convert( renderTarget.texture.type ); state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { // FIXME: We don"t support !depth !stencil _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; if ( renderTarget.depthTexture.format === DepthFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); if ( renderTarget.depthTexture ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( var i = 0; i < 6; i ++ ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); } } else { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); } } _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( var i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); } if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height ); } state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height ); } state.bindTexture( _gl.TEXTURE_2D, null ); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { var texture = renderTarget.texture; var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) { var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; var webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target, texture, renderTarget.width, renderTarget.height ); state.bindTexture( target, null ); } } function updateVideoTexture( texture ) { var id = texture.id; var frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures[ id ] !== frame ) { _videoTextures[ id ] = frame; texture.update(); } } this.setTexture2D = setTexture2D; this.setTextureCube = setTextureCube; this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; } /** * @author thespite / http://www.twitter.com/thespite */ function WebGLUtils( gl, extensions ) { function convert( p ) { var extension; if ( p === RepeatWrapping ) return gl.REPEAT; if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE; if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT; if ( p === NearestFilter ) return gl.NEAREST; if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST; if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR; if ( p === LinearFilter ) return gl.LINEAR; if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST; if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) return extension.HALF_FLOAT_OES; } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBFormat ) return gl.RGB; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; if ( p === AddEquation ) return gl.FUNC_ADD; if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT; if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT; if ( p === ZeroFactor ) return gl.ZERO; if ( p === OneFactor ) return gl.ONE; if ( p === SrcColorFactor ) return gl.SRC_COLOR; if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR; if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA; if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA; if ( p === DstAlphaFactor ) return gl.DST_ALPHA; if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA; if ( p === DstColorFactor ) return gl.DST_COLOR; if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR; if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE; if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } } if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } } if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; } if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { return p; } } if ( p === MinEquation || p === MaxEquation ) { extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { if ( p === MinEquation ) return extension.MIN_EXT; if ( p === MaxEquation ) return extension.MAX_EXT; } } if ( p === UnsignedInt248Type ) { extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; } return 0; } return { convert: convert }; } /** * @author mrdoob / http://mrdoob.com/ * @author greggman / http://games.greggman.com/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author tschw */ function PerspectiveCamera( fov, aspect, near, far ) { Camera.call( this ); this.type = "PerspectiveCamera"; this.fov = fov !== undefined ? fov : 50; this.zoom = 1; this.near = near !== undefined ? near : 0.1; this.far = far !== undefined ? far : 2000; this.focus = 10; this.aspect = aspect !== undefined ? aspect : 1; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: PerspectiveCamera, isPerspectiveCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; }, /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength: function ( focalLength ) { // see http://www.bobatkins.com/photography/technical/field_of_view.html var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); }, /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength: function () { var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; }, getEffectiveFOV: function () { return _Math.RAD2DEG * 2 * Math.atan( Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); }, getFilmWidth: function () { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); }, getFilmHeight: function () { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); }, /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * var w = 1920; * var h = 1080; * var fullWidth = w * 3; * var fullHeight = h * 2; * * --A-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var near = this.near, top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, height = 2 * top, width = this.aspect * height, left = - 0.5 * width, view = this.view; if ( this.view !== null && this.view.enabled ) { var fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } var skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ArrayCamera( array ) { PerspectiveCamera.call( this ); this.cameras = array || []; } ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { constructor: ArrayCamera, isArrayCamera: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebVRManager( renderer ) { var scope = this; var device = null; var frameData = null; // This was added in the facebook copy in order to better render 3D photos // in vr on the feed. var renderScaling = 1.0; var poseTarget = null; var standingMatrix = new Matrix4(); var standingMatrixInverse = new Matrix4(); if ( typeof window !== "undefined" && "VRFrameData" in window ) { frameData = new window.VRFrameData(); window.addEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange, false ); } var matrixWorldInverse = new Matrix4(); var tempQuaternion = new Quaternion(); var tempPosition = new Vector3(); var cameraL = new PerspectiveCamera(); cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 ); cameraL.layers.enable( 1 ); var cameraR = new PerspectiveCamera(); cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 ); cameraR.layers.enable( 2 ); var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); // function isPresenting() { return device !== null && device.isPresenting === true; } var currentSize, currentPixelRatio; function onVRDisplayPresentChange() { if ( isPresenting() ) { var eyeParameters = device.getEyeParameters( "left" ); var renderWidth = eyeParameters.renderWidth * renderScaling; var renderHeight = eyeParameters.renderHeight * renderScaling; currentPixelRatio = renderer.getPixelRatio(); currentSize = renderer.getSize(); renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 ); animation.start(); } else if ( scope.enabled ) { renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); animation.stop(); } } // this.enabled = false; this.userHeight = 1.6; this.autoSubmitFrame = true; // FACEBOOK - we added this back in as it broke 3D photos this.standing = false; this.setScaling = function ( value ) { renderScaling = value; } this.getDevice = function () { return device; }; this.setDevice = function ( value ) { if ( value !== undefined ) device = value; animation.setContext( value ); }; this.setPoseTarget = function ( object ) { if ( object !== undefined ) poseTarget = object; }; this.getCamera = function ( camera ) { if ( device === null ) return camera; device.depthNear = camera.near; device.depthFar = camera.far; device.getFrameData( frameData ); // if ( this.standing ) { var stageParameters = device.stageParameters; if ( stageParameters ) { standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); } else { standingMatrix.makeTranslation( 0, scope.userHeight, 0 ); } } var pose = frameData.pose; var poseObject = poseTarget !== null ? poseTarget : camera; // We want to manipulate poseObject by its position and quaternion components since users may rely on them. poseObject.matrix.copy( standingMatrix ); poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale ); if ( pose.orientation !== null ) { tempQuaternion.fromArray( pose.orientation ); poseObject.quaternion.multiply( tempQuaternion ); } if ( pose.position !== null ) { tempQuaternion.setFromRotationMatrix( standingMatrix ); tempPosition.fromArray( pose.position ); tempPosition.applyQuaternion( tempQuaternion ); poseObject.position.add( tempPosition ); } poseObject.updateMatrixWorld(); if ( device.isPresenting === false ) return camera; // cameraL.near = camera.near; cameraR.near = camera.near; cameraL.far = camera.far; cameraR.far = camera.far; cameraVR.matrixWorld.copy( camera.matrixWorld ); cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); // TODO (mrdoob) Double check this code standingMatrixInverse.getInverse( standingMatrix ); if ( this.standing) { cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); } var parent = poseObject.parent; if ( parent !== null ) { matrixWorldInverse.getInverse( parent.matrixWorld ); cameraL.matrixWorldInverse.multiply( matrixWorldInverse ); cameraR.matrixWorldInverse.multiply( matrixWorldInverse ); } // envMap and Mirror needs camera.matrixWorld cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse ); cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse ); cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); // HACK (mrdoob) // https://github.com/w3c/webvr/issues/203 cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); // var layers = device.getLayers(); if ( layers.length ) { var layer = layers[ 0 ]; if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) { cameraL.bounds.fromArray( layer.leftBounds ); } if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) { cameraR.bounds.fromArray( layer.rightBounds ); } } return cameraVR; }; this.getStandingMatrix = function () { return standingMatrix; }; this.isPresenting = isPresenting; // Animation Loop var animation = new WebGLAnimation(); this.setAnimationLoop = function ( callback ) { animation.setAnimationLoop( callback ); }; this.submitFrame = function () { if ( isPresenting() ) device.submitFrame(); }; this.dispose = function () { if ( typeof window !== "undefined" ) { window.removeEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange ); } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebXRManager( renderer, gl ) { var scope = this; var session = null; // This was added in the facebook copy in order to better render 3D photos // in vr on the feed. var renderScaling = 1.0; var framebufferScaleFactor = 1.0; var referenceSpace = null; var referenceSpaceType = "local"; var pose = null; var controllers = []; var inputSourcesMap = new Map(); // var cameraL = new PerspectiveCamera(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); var cameraR = new PerspectiveCamera(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); var _currentDepthNear = null; var _currentDepthFar = null; // this.enabled = false; this._isPresenting = false; this.autoSubmitFrame = false; // FACEBOOK: keep compatibility with API of WebVRManager this.isPresenting = function () { return scope._isPresenting; } // FACEBOOK: Temporary, should remove once WebVRManager is dead this.setDevice = function ( value ) { // nothing happens }; this.getController = function ( id ) { var controller = controllers[ id ]; if ( controller === undefined ) { controller = {}; controllers[ id ] = controller; } if ( controller.targetRay === undefined ) { controller.targetRay = new Group(); controller.targetRay.matrixAutoUpdate = false; controller.targetRay.visible = false; } return controller.targetRay; }; this.getControllerGrip = function ( id ) { var controller = controllers[ id ]; if ( controller === undefined ) { controller = {}; controllers[ id ] = controller; } if ( controller.grip === undefined ) { controller.grip = new Group(); controller.grip.matrixAutoUpdate = false; controller.grip.visible = false; } return controller.grip; }; // function onSessionEvent( event ) { var controller = inputSourcesMap.get( event.inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: event.type } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: event.type } ); } } } function onSessionEnd() { inputSourcesMap.forEach( function ( controller, inputSource ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "disconnected", data: inputSource } ); controller.targetRay.visible = false; } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "disconnected", data: inputSource } ); controller.grip.visible = false; } } ); inputSourcesMap.clear(); // renderer.setFramebuffer( null ); renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 animation.stop(); scope._isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } function onRequestReferenceSpace( value ) { referenceSpace = value; animation.setContext( session ); animation.start(); scope._isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; // Warn if function is used while presenting if ( scope._isPresenting == true ) { console.warn( "WebXRManager: Cannot change framebuffer scale while presenting VR content" ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; }; this.getReferenceSpace = function () { return referenceSpace; }; this.getSession = function () { return session; }; this.setSession = function ( value ) { session = value; if ( session !== null ) { session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); var attributes = gl.getContextAttributes(); var layerInit = { antialias: attributes.antialias, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; // eslint-disable-next-line no-undef var baseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: baseLayer } ); session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); // session.addEventListener( "inputsourceschange", updateInputSources ); } }; function updateInputSources( event ) { var inputSources = session.inputSources; // Assign inputSources to available controllers for ( var i = 0; i < controllers.length; i ++ ) { inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); } // Notify disconnected for ( var i = 0; i < event.removed.length; i ++ ) { var inputSource = event.removed[ i ]; var controller = inputSourcesMap.get( inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "disconnected", data: inputSource } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "disconnected", data: inputSource } ); } inputSourcesMap.delete( inputSource ); } } // Notify connected for ( var i = 0; i < event.added.length; i ++ ) { var inputSource = event.added[ i ]; var controller = inputSourcesMap.get( inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "connected", data: inputSource } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "connected", data: inputSource } ); } } } } // var cameraLPos = new Vector3(); var cameraRPos = new Vector3(); /** * @author jsantell / https://www.jsantell.com/ * * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); var ipd = cameraLPos.distanceTo( cameraRPos ); var projL = cameraL.projectionMatrix.elements; var projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; var left = near * leftFov; var right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. var zOffset = ipd / ( - leftFov + rightFov ); var xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. var near2 = near + zOffset; var far2 = far + zOffset; var left2 = left - xOffset; var right2 = right + ( ipd - xOffset ); var top2 = topFov * far / far2 * near2; var bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.getInverse( camera.matrixWorld ); } this.getCamera = function ( camera ) { cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraVR.near, depthFar: cameraVR.far } ); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } var parent = camera.parent; var cameras = cameraVR.cameras; updateCamera( cameraVR, parent ); for ( var i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } // update camera and its children camera.matrixWorld.copy( cameraVR.matrixWorld ); var children = camera.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } setProjectionFromUnion( cameraVR, cameraL, cameraR ); return cameraVR; }; // Animation Loop var onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( referenceSpace ); if ( pose !== null ) { var views = pose.views; var baseLayer = session.renderState.baseLayer; renderer.setFramebuffer( baseLayer.framebuffer ); for ( var i = 0; i < views.length; i ++ ) { var view = views[ i ]; var viewport = baseLayer.getViewport( view ); var camera = cameraVR.cameras[ i ]; camera.matrix.fromArray( view.transform.matrix ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraVR.matrix.copy( camera.matrix ); } } } // var inputSources = session.inputSources; for ( var i = 0; i < controllers.length; i ++ ) { var controller = controllers[ i ]; var inputSource = inputSources[ i ]; var inputPose = null; var gripPose = null; if ( inputSource ) { if ( controller.targetRay ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); if ( inputPose !== null ) { controller.targetRay.matrix.fromArray( inputPose.transform.matrix ); controller.targetRay.matrix.decompose( controller.targetRay.position, controller.targetRay.rotation, controller.targetRay.scale ); } } if ( controller.grip && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { controller.grip.matrix.fromArray( gripPose.transform.matrix ); controller.grip.matrix.decompose( controller.grip.position, controller.grip.rotation, controller.grip.scale ); } } } if ( controller.targetRay ) { controller.targetRay.visible = inputPose !== null; } if ( controller.grip ) { controller.grip.visible = gripPose !== null; } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); } var animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; this.submitFrame = function () {}; // FACEBOOK this.setScaling = function ( value ) { renderScaling = value; } } Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ * @author tschw */ function WebGLRenderer( parameters ) { console.log( "THREE.WebGLRenderer", REVISION ); parameters = parameters || {}; var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default"; var currentRenderList = null; var currentRenderState = null; // public properties this.domElement = _canvas; this.context = null; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.gammaFactor = 2.0; // for backwards compatibility this.gammaInput = false; this.gammaOutput = false; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = LinearToneMapping; this.toneMappingExposure = 1.0; this.toneMappingWhitePoint = 1.0; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // internal properties var _this = this, _isContextLost = false, // internal state cache _framebuffer = null, _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = "", _currentCamera = null, _currentArrayCamera = null, _currentViewport = new Vector4(), _currentScissor = new Vector4(), _currentScissorTest = null, // _usedTextureUnits = 0, // _width = _canvas.width, _height = _canvas.height, _pixelRatio = 1, _viewport = new Vector4( 0, 0, _width, _height ), _scissor = new Vector4( 0, 0, _width, _height ), _scissorTest = false, // frustum _frustum = new Frustum(), // clipping _clipping = new WebGLClipping(), _clippingEnabled = false, _localClippingEnabled = false, // camera matrices cache _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(); function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize var _gl; try { var contextAttributes = { alpha: _alpha, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, xrCompatible: true }; // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener( "webglcontextlost", onContextLost, false ); _canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); _gl = _context || _canvas.getContext( "webgl", contextAttributes ) || _canvas.getContext( "experimental-webgl", contextAttributes ); if ( _gl === null ) { if ( _canvas.getContext( "webgl" ) !== null ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); } var extensions, capabilities, state, info; var properties, textures, attributes, geometries, objects; var programCache, renderLists, renderStates; var background, morphtargets, bufferRenderer, indexedBufferRenderer; var spriteRenderer; var utils; function initGLContext() { extensions = new WebGLExtensions( _gl ); extensions.get( "WEBGL_depth_texture" ); extensions.get( "OES_texture_float" ); extensions.get( "OES_texture_float_linear" ); extensions.get( "OES_texture_half_float" ); extensions.get( "OES_texture_half_float_linear" ); extensions.get( "OES_standard_derivatives" ); extensions.get( "OES_element_index_uint" ); extensions.get( "ANGLE_instanced_arrays" ); utils = new WebGLUtils( _gl, extensions ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); state = new WebGLState( _gl, extensions, utils ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); attributes = new WebGLAttributes( _gl ); geometries = new WebGLGeometries( _gl, attributes, info ); objects = new WebGLObjects( geometries, info ); morphtargets = new WebGLMorphtargets( _gl ); programCache = new WebGLPrograms( _this, extensions, capabilities ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(); background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info ); spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities ); info.programs = programCache.programs; _this.context = _gl; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.state = state; _this.info = info; } initGLContext(); // vr var vr = ( "xr" in navigator ) ? new WebXRManager( _this, _gl ) : new WebVRManager( _this ); this.vr = vr; // shadow map var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); this.shadowMap = shadowMap; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function () { return { width: _width, height: _height }; }; this.setSize = function ( width, height, updateStyle ) { if ( vr.isPresenting() ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; _canvas.width = width * _pixelRatio; _canvas.height = height * _pixelRatio; if ( updateStyle !== false ) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function () { return { width: _width * _pixelRatio, height: _height * _pixelRatio }; }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = width * pixelRatio; _canvas.height = height * pixelRatio; this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function () { return _currentViewport; }; this.setViewport = function ( x, y, width, height ) { _viewport.set( x, _height - y - height, width, height ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); }; this.setScissor = function ( x, y, width, height ) { _scissor.set( x, _height - y - height, width, height ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; // Clearing this.getClearColor = function () { return background.getClearColor(); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color, depth, stencil ) { var bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // this.dispose = function () { _canvas.removeEventListener( "webglcontextlost", onContextLost, false ); _canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); objects.dispose(); vr.dispose(); animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; initGLContext(); } function onMaterialDispose( event ) { var material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReference( material ); properties.remove( material ); } function releaseMaterialProgramReference( material ) { var programInfo = properties.get( material ).program; material.program = undefined; if ( programInfo !== undefined ) { programCache.releaseProgram( programInfo ); } } // Buffer rendering function renderObjectImmediate( object, program, material ) { object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); } this.renderBufferImmediate = function ( object, program, material ) { state.initAttributes(); var buffers = properties.get( object ); if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); var programAttributes = program.getAttributes(); if ( object.hasPositions ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.position ); _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); if ( ! material.isMeshPhongMaterial && ! material.isMeshStandardMaterial && ! material.isMeshNormalMaterial && material.flatShading === true ) { for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { var array = object.normalArray; var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; array[ i + 0 ] = nx; array[ i + 1 ] = ny; array[ i + 2 ] = nz; array[ i + 3 ] = nx; array[ i + 4 ] = ny; array[ i + 5 ] = nz; array[ i + 6 ] = nx; array[ i + 7 ] = ny; array[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.normal ); _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.uv ); _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== NoColors ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.color ); _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); } state.disableUnusedAttributes(); _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, fog, material, object ); var geometryProgram = geometry.id + "_" + program.id + "_" + ( material.wireframe === true ); var updateBuffers = false; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } if ( object.morphTargetInfluences ) { morphtargets.update( object, geometry, material, program ); updateBuffers = true; } // var index = geometry.index; var position = geometry.attributes.position; var rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); rangeFactor = 2; } var attribute; var renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } if ( updateBuffers ) { setupVertexAttributes( material, program, geometry ); if ( index !== null ) { _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); } } // var dataCount = Infinity; if ( index !== null ) { dataCount = index.count; } else if ( position !== undefined ) { dataCount = position.count; } var rangeStart = geometry.drawRange.start * rangeFactor; var rangeCount = geometry.drawRange.count * rangeFactor; var groupStart = group !== null ? group.start * rangeFactor : 0; var groupCount = group !== null ? group.count * rangeFactor : Infinity; var drawStart = Math.max( rangeStart, groupStart ); var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { switch ( object.drawMode ) { case TrianglesDrawMode: renderer.setMode( _gl.TRIANGLES ); break; case TriangleStripDrawMode: renderer.setMode( _gl.TRIANGLE_STRIP ); break; case TriangleFanDrawMode: renderer.setMode( _gl.TRIANGLE_FAN ); break; } } } else if ( object.isLine ) { var lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } if ( geometry && geometry.isInstancedBufferGeometry ) { if ( geometry.maxInstancedCount > 0 ) { renderer.renderInstances( geometry, drawStart, drawCount ); } } else { renderer.render( drawStart, drawCount ); } }; function setupVertexAttributes( material, program, geometry ) { if ( geometry && geometry.isInstancedBufferGeometry ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) { console.error( "THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } state.initAttributes(); var geometryAttributes = geometry.attributes; var programAttributes = program.getAttributes(); var materialDefaultAttributeValues = material.defaultAttributeValues; for ( var name in programAttributes ) { var programAttribute = programAttributes[ name ]; if ( programAttribute >= 0 ) { var geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute !== undefined ) { var normalized = geometryAttribute.normalized; var size = geometryAttribute.itemSize; var attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; var buffer = attribute.buffer; var type = attribute.type; var bytesPerElement = attribute.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { var data = geometryAttribute.data; var stride = data.stride; var offset = geometryAttribute.offset; if ( data && data.isInstancedInterleavedBuffer ) { state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = data.meshPerAttribute * data.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); } else { if ( geometryAttribute.isInstancedBufferAttribute ) { state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); } } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: _gl.vertexAttrib2fv( programAttribute, value ); break; case 3: _gl.vertexAttrib3fv( programAttribute, value ); break; case 4: _gl.vertexAttrib4fv( programAttribute, value ); break; default: _gl.vertexAttrib1fv( programAttribute, value ); } } } } } state.disableUnusedAttributes(); } // Compile this.compile = function ( scene, camera ) { currentRenderState = renderStates.get( scene, camera ); currentRenderState.init(); scene.traverse( function ( object ) { if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( camera ); scene.traverse( function ( object ) { if ( object.material ) { if ( Array.isArray( object.material ) ) { for ( var i = 0; i < object.material.length; i ++ ) { initMaterial( object.material[ i ], scene.fog, object ); } } else { initMaterial( object.material, scene.fog, object ); } } } ); }; // Animation Loop var onAnimationFrameCallback = null; function onAnimationFrame() { if ( vr.isPresenting() ) return; if ( onAnimationFrameCallback ) onAnimationFrameCallback(); } var animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); animation.setContext( window ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; vr.setAnimationLoop( callback ); animation.start(); }; // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( ! ( camera && camera.isCamera ) ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost ) return; // reset caching for this frame _currentGeometryProgram = ""; _currentMaterialId = - 1; _currentCamera = null; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); if ( vr.enabled ) { camera = vr.getCamera( camera ); } // currentRenderState = renderStates.get( scene, camera ); currentRenderState.init(); scene.onBeforeRender( _this, scene, camera, renderTarget ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); currentRenderList = renderLists.get( scene, camera ); currentRenderList.init(); projectObject( scene, camera, _this.sortObjects ); if ( _this.sortObjects === true ) { currentRenderList.sort(); } // if ( _clippingEnabled ) _clipping.beginShadows(); var shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); currentRenderState.setupLights( camera ); if ( _clippingEnabled ) _clipping.endShadows(); // if ( this.info.autoReset ) this.info.reset(); if ( renderTarget === undefined ) { renderTarget = null; } this.setRenderTarget( renderTarget ); // background.render( currentRenderList, scene, camera, forceClear ); // render scene var opaqueObjects = currentRenderList.opaque; var transparentObjects = currentRenderList.transparent; if ( scene.overrideMaterial ) { var overrideMaterial = scene.overrideMaterial; if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); } else { // opaque pass (front-to-back order) if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); // transparent pass (back-to-front order) if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); } // custom renderers var spritesArray = currentRenderState.state.spritesArray; spriteRenderer.render( spritesArray, scene, camera ); // Generate mipmap if we"re using any kind of mipmap filtering if ( renderTarget ) { textures.updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); scene.onAfterRender( _this, scene, camera ); // The facebook 3D photos need to use the stencil buffer, and in order // do do that we can"t submit the frame here as it will clear the drawing // buffer. I"m making a fix to the ThreeJS code and opened issue // https://github.com/mrdoob/three.js/issues/13967 if ( vr.enabled && vr.autoSubmitFrame) { vr.submitFrame(); } // _gl.finish(); currentRenderList = null; currentRenderState = null; }; /* // TODO Duplicated code (Frustum) var _sphere = new Sphere(); function isObjectViewable( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ). applyMatrix4( object.matrixWorld ); return isSphereViewable( _sphere ); } function isSpriteViewable( sprite ) { _sphere.center.set( 0, 0, 0 ); _sphere.radius = 0.7071067811865476; _sphere.applyMatrix4( sprite.matrixWorld ); return isSphereViewable( _sphere ); } function isSphereViewable( sphere ) { if ( ! _frustum.intersectsSphere( sphere ) ) return false; var numPlanes = _clipping.numPlanes; if ( numPlanes === 0 ) return true; var planes = _this.clippingPlanes, center = sphere.center, negRad = - sphere.radius, i = 0; do { // out when deeper than radius in the negative halfspace if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; } while ( ++ i !== numPlanes ); return true; } */ function projectObject( object, camera, sortObjects ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { currentRenderState.pushSprite( object ); } } else if ( object.isImmediateRenderObject ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } currentRenderList.push( object, null, object.material, _vector3.z, null ); } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( object.isSkinnedMesh ) { object.skeleton.update(); } if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } var geometry = objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, _vector3.z, null ); } } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, sortObjects ); } } function renderObjects( renderList, scene, camera, overrideMaterial ) { for ( var i = 0, l = renderList.length; i < l; i ++ ) { var renderItem = renderList[ i ]; var object = renderItem.object; var geometry = renderItem.geometry; var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; var group = renderItem.group; if ( camera.isArrayCamera ) { _currentArrayCamera = camera; var cameras = camera.cameras; for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { var camera2 = cameras[ j ]; if ( object.layers.test( camera2.layers ) ) { if ( "viewport" in camera2 ) { // XR state.viewport( _currentViewport.copy( camera2.viewport ) ); } else { var bounds = camera2.bounds; var x = bounds.x * _width; var y = bounds.y * _height; var width = bounds.z * _width; var height = bounds.w * _height; state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); } renderObject( object, scene, camera2, geometry, material, group ); } } } else { _currentArrayCamera = null; renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); if ( object.isImmediateRenderObject ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, scene.fog, material, object ); _currentGeometryProgram = ""; renderObjectImmediate( object, program, material ); } else { _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); } function initMaterial( material, fog, object ) { var materialProperties = properties.get( material ); var lights = currentRenderState.state.lights; var shadowsArray = currentRenderState.state.shadowsArray; var parameters = programCache.getParameters( material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); var code = programCache.getProgramCode( material, parameters ); var program = materialProperties.program; var programChange = true; if ( program === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); } else if ( program.code !== code ) { // changed glsl or parameters releaseMaterialProgramReference( material ); } else if ( materialProperties.lightsHash !== lights.state.hash ) { properties.update( material, "lightsHash", lights.state.hash ); programChange = false; } else if ( parameters.shaderID !== undefined ) { // same glsl and uniform list return; } else { // only rebuild uniform list programChange = false; } if ( programChange ) { if ( parameters.shaderID ) { var shader = ShaderLib[ parameters.shaderID ]; materialProperties.shader = { name: material.type, uniforms: UniformsUtils.clone( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; } else { materialProperties.shader = { name: material.type, uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader }; } material.onBeforeCompile( materialProperties.shader, _this ); program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); materialProperties.program = program; material.program = program; } var programAttributes = program.getAttributes(); if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { if ( programAttributes[ "morphTarget" + i ] >= 0 ) { material.numSupportedMorphTargets ++; } } } if ( material.morphNormals ) { material.numSupportedMorphNormals = 0; for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { if ( programAttributes[ "morphNormal" + i ] >= 0 ) { material.numSupportedMorphNormals ++; } } } var uniforms = materialProperties.shader.uniforms; if ( ! material.isShaderMaterial && ! material.isRawShaderMaterial || material.clipping === true ) { materialProperties.numClippingPlanes = _clipping.numPlanes; materialProperties.numIntersection = _clipping.numIntersection; uniforms.clippingPlanes = _clipping.uniform; } materialProperties.fog = fog; // store the light setup it was created for materialProperties.lightsHash = lights.state.hash; if ( material.lights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.directionalLights.value = lights.state.directional; uniforms.spotLights.value = lights.state.spot; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.pointLights.value = lights.state.point; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } var progUniforms = materialProperties.program.getUniforms(), uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.uniformsList = uniformsList; } function setProgram( camera, fog, material, object ) { _usedTextureUnits = 0; var materialProperties = properties.get( material ); var lights = currentRenderState.state.lights; if ( _clippingEnabled ) { if ( _localClippingEnabled || camera !== _currentCamera ) { var useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) _clipping.setState( material.clippingPlanes, material.clipIntersection, material.clipShadows, camera, materialProperties, useCache ); } } if ( material.needsUpdate === false ) { if ( materialProperties.program === undefined ) { material.needsUpdate = true; } else if ( material.fog && materialProperties.fog !== fog ) { material.needsUpdate = true; } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { material.needsUpdate = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== _clipping.numPlanes || materialProperties.numIntersection !== _clipping.numIntersection ) ) { material.needsUpdate = true; } } if ( material.needsUpdate ) { initMaterial( material, fog, object ); material.needsUpdate = false; } var refreshProgram = false; var refreshMaterial = false; var refreshLights = false; var program = materialProperties.program, p_uniforms = program.getUniforms(), m_uniforms = materialProperties.shader.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } // Avoid unneeded uniform updates per ArrayCamera"s sub-camera if ( _currentCamera !== ( _currentArrayCamera || camera ) ) { _currentCamera = ( _currentArrayCamera || camera ); // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.envMap ) { var uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.skinning ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning uniforms must be set even if material didn"t change // auto-setting of texture unit for bone texture must go before other textures // not sure why, but otherwise weird things happen if ( material.skinning ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); var skeleton = object.skeleton; if ( skeleton ) { var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === undefined ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix size = _Math.ceilPowerOfTwo( size ); size = Math.max( size, 4 ); var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( skeleton.boneMatrices ); // copy current values var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); boneTexture.needsUpdate = true; skeleton.boneMatrices = boneMatrices; skeleton.boneTexture = boneTexture; skeleton.boneTextureSize = size; } p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { p_uniforms.setOptional( _gl, skeleton, "boneMatrices" ); } } } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); p_uniforms.setValue( _gl, "toneMappingWhitePoint", _this.toneMappingWhitePoint ); if ( material.lights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( m_uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsLambert( m_uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshToonMaterial ) { refreshUniformsToon( m_uniforms, material ); } else { refreshUniformsPhong( m_uniforms, material ); } } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( m_uniforms, material ); } else { refreshUniformsStandard( m_uniforms, material ); } } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDepth( m_uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDistance( m_uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsNormal( m_uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( m_uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( m_uniforms, material ); } else if ( material.isShadowMaterial ) { m_uniforms.color.value = material.color; m_uniforms.opacity.value = material.opacity; } // RectAreaLight Texture // TODO (mrdoob): Find a nicer implementation if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); material.uniformsNeedUpdate = false; } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); return program; } // Uniforms (refresh uniforms objects) function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value = material.color; } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; } if ( material.envMap ) { uniforms.envMap.value = material.envMap; // don"t flip CubeTexture envMaps, flip everything else: // WebGLRenderTargetCube will be flipped for backwards compatibility // WebGLRenderTargetCube.texture will be flipped because it"s a Texture and NOT a CubeTexture // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; uniforms.reflectivity.value = material.reflectivity; uniforms.refractionRatio.value = material.refractionRatio; uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. normal map // 4. bump map // 5. alpha map // 6. emissive map var uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.displacementMap ) { uvScaleMap = material.displacementMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.roughnessMap ) { uvScaleMap = material.roughnessMap; } else if ( material.metalnessMap ) { uvScaleMap = material.metalnessMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } else if ( material.emissiveMap ) { uvScaleMap = material.emissiveMap; } if ( uvScaleMap !== undefined ) { // backwards compatibility if ( uvScaleMap.isWebGLRenderTarget ) { uvScaleMap = uvScaleMap.texture; } if ( uvScaleMap.matrixAutoUpdate === true ) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * _pixelRatio; uniforms.scale.value = _height * 0.5; uniforms.map.value = material.map; if ( material.map !== null ) { if ( material.map.matrixAutoUpdate === true ) { material.map.updateMatrix(); } uniforms.uvTransform.value.copy( material.map.matrix ); } } function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshUniformsLambert( uniforms, material ) { if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value = material.specular; uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon( uniforms, material ) { refreshUniformsPhong( uniforms, material ); if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; } if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material ) { uniforms.clearCoat.value = material.clearCoat; uniforms.clearCoatRoughness.value = material.clearCoatRoughness; refreshUniformsStandard( uniforms, material ); } function refreshUniformsDepth( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy( material.referencePosition ); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal( uniforms, material ) { if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } // Textures function allocTextureUnit() { var textureUnit = _usedTextureUnits; if ( textureUnit >= capabilities.maxTextures ) { console.warn( "THREE.WebGLRenderer: Trying to use " + textureUnit + " texture units while this GPU supports only " + capabilities.maxTextures ); } _usedTextureUnits += 1; return textureUnit; } this.allocTextureUnit = allocTextureUnit; // this.setTexture2D = setTexture2D; this.setTexture2D = ( function () { var warned = false; // backwards compatibility: peel texture.texture return function setTexture2D( texture, slot ) { if ( texture && texture.isWebGLRenderTarget ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTexture2D: don"t use render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } textures.setTexture2D( texture, slot ); }; }() ); this.setTexture = ( function () { var warned = false; return function setTexture( texture, slot ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); warned = true; } textures.setTexture2D( texture, slot ); }; }() ); this.setTextureCube = ( function () { var warned = false; return function setTextureCube( texture, slot ) { // backwards compatibility: peel texture.texture if ( texture && texture.isWebGLRenderTargetCube ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTextureCube: don"t use cube render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture // TODO: unify these code paths if ( ( texture && texture.isCubeTexture ) || ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ // this function alone should take care of cube textures textures.setTextureCube( texture, slot ); } else { // assumed: texture property of THREE.WebGLRenderTargetCube textures.setTextureCubeDynamic( texture, slot ); } }; }() ); // this.setFramebuffer = function ( value ) { _framebuffer = value; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTarget = function ( renderTarget ) { _currentRenderTarget = renderTarget; if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } var framebuffer = _framebuffer; var isCube = false; if ( renderTarget ) { var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLRenderTargetCube ) { framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; isCube = true; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); _currentScissorTest = _scissorTest; } if ( _currentFramebuffer !== framebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _currentFramebuffer = framebuffer; } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { var textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); } }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } var framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( framebuffer ) { var restore = false; if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); restore = true; } try { var texture = renderTarget.texture; var textureFormat = texture.format; var textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( extensions.get( "OES_texture_float" ) || extensions.get( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! ( textureType === HalfFloatType && extensions.get( "EXT_color_buffer_half_float" ) ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } else { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete." ); } } finally { if ( restore ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); } } } }; this.copyFramebufferToTexture = function ( position, texture, level ) { var width = texture.image.width; var height = texture.image.height; var glFormat = utils.convert( texture.format ); this.setTexture2D( texture, 0 ); _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 ); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { var width = srcTexture.image.width; var height = srcTexture.image.height; var glFormat = utils.convert( dstTexture.format ); var glType = utils.convert( dstTexture.type ); this.setTexture2D( dstTexture, 0 ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, glFormat, glType, srcTexture.image ); } }; } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function FogExp2( color, density ) { this.name = ""; this.color = new Color( color ); this.density = ( density !== undefined ) ? density : 0.00025; } FogExp2.prototype.isFogExp2 = true; FogExp2.prototype.clone = function () { return new FogExp2( this.color, this.density ); }; FogExp2.prototype.toJSON = function ( /* meta */ ) { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Fog( color, near, far ) { this.name = ""; this.color = new Color( color ); this.near = ( near !== undefined ) ? near : 1; this.far = ( far !== undefined ) ? far : 1000; } Fog.prototype.isFog = true; Fog.prototype.clone = function () { return new Fog( this.color, this.near, this.far ); }; Fog.prototype.toJSON = function ( /* meta */ ) { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; }; /** * @author mrdoob / http://mrdoob.com/ */ function Scene() { Object3D.call( this ); this.type = "Scene"; this.background = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer } Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Scene, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * uvOffset: new THREE.Vector2(), * uvScale: new THREE.Vector2() * } */ function SpriteMaterial( parameters ) { Material.call( this ); this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.rotation = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } SpriteMaterial.prototype = Object.create( Material.prototype ); SpriteMaterial.prototype.constructor = SpriteMaterial; SpriteMaterial.prototype.isSpriteMaterial = true; SpriteMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.rotation = source.rotation; return this; }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function Sprite( material ) { Object3D.call( this ); this.type = "Sprite"; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Sprite, isSprite: true, raycast: ( function () { var intersectPoint = new Vector3(); var worldPosition = new Vector3(); var worldScale = new Vector3(); return function raycast( raycaster, intersects ) { worldPosition.setFromMatrixPosition( this.matrixWorld ); raycaster.ray.closestPointToPoint( worldPosition, intersectPoint ); worldScale.setFromMatrixScale( this.matrixWorld ); var guessSizeSq = worldScale.x * worldScale.y / 4; if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return; var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: intersectPoint.clone(), face: null, object: this } ); }; }() ), clone: function () { return new this.constructor( this.material ).copy( this ); }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); if ( source.center !== undefined ) this.center.copy( source.center ); return this; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function LOD() { Object3D.call( this ); this.type = "LOD"; Object.defineProperties( this, { levels: { enumerable: true, value: [] } } ); } LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LOD, copy: function ( source ) { Object3D.prototype.copy.call( this, source, false ); var levels = source.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; this.addLevel( level.object.clone(), level.distance ); } return this; }, addLevel: function ( object, distance ) { if ( distance === undefined ) distance = 0; distance = Math.abs( distance ); var levels = this.levels; for ( var l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, object: object } ); this.add( object ); }, getObjectForDistance: function ( distance ) { var levels = this.levels; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance < levels[ i ].distance ) { break; } } return levels[ i - 1 ].object; }, raycast: ( function () { var matrixPosition = new Vector3(); return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( matrixPosition ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); }; }() ), update: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function update( camera ) { var levels = this.levels; if ( levels.length > 1 ) { v1.setFromMatrixPosition( camera.matrixWorld ); v2.setFromMatrixPosition( this.matrixWorld ); var distance = v1.distanceTo( v2 ); levels[ 0 ].object.visible = true; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance >= levels[ i ].distance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } }; }(), toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.levels = []; var levels = this.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance } ); } return data; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author michael guerrero / http://realitymeltdown.com * @author ikerr / http://verold.com */ function Skeleton( bones, boneInverses ) { // copy the bone array bones = bones || []; this.bones = bones.slice( 0 ); this.boneMatrices = new Float32Array( this.bones.length * 16 ); // use the supplied bone inverses or calculate the inverses if ( boneInverses === undefined ) { this.calculateInverses(); } else { if ( this.bones.length === boneInverses.length ) { this.boneInverses = boneInverses.slice( 0 ); } else { console.warn( "THREE.Skeleton boneInverses is the wrong length." ); this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { this.boneInverses.push( new Matrix4() ); } } } } Object.assign( Skeleton.prototype, { calculateInverses: function () { this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { var inverse = new Matrix4(); if ( this.bones[ i ] ) { inverse.getInverse( this.bones[ i ].matrixWorld ); } this.boneInverses.push( inverse ); } }, pose: function () { var bone, i, il; // recover the bind-time world matrices for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { bone.matrixWorld.getInverse( this.boneInverses[ i ] ); } } // compute the local matrices, positions, rotations and scales for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.getInverse( bone.parent.matrixWorld ); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } }, update: ( function () { var offsetMatrix = new Matrix4(); var identityMatrix = new Matrix4(); return function update() { var bones = this.bones; var boneInverses = this.boneInverses; var boneMatrices = this.boneMatrices; var boneTexture = this.boneTexture; // flatten bone matrices to array for ( var i = 0, il = bones.length; i < il; i ++ ) { // compute the offset between the current and the original transform var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix; offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); offsetMatrix.toArray( boneMatrices, i * 16 ); } if ( boneTexture !== undefined ) { boneTexture.needsUpdate = true; } }; } )(), clone: function () { return new Skeleton( this.bones, this.boneInverses ); }, getBoneByName: function ( name ) { for ( var i = 0, il = this.bones.length; i < il; i ++ ) { var bone = this.bones[ i ]; if ( bone.name === name ) { return bone; } } return undefined; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function Bone() { Object3D.call( this ); this.type = "Bone"; } Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Bone, isBone: true } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function SkinnedMesh( geometry, material ) { Mesh.call( this, geometry, material ); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); var bones = this.initBones(); var skeleton = new Skeleton( bones ); this.bind( skeleton, this.matrixWorld ); this.normalizeSkinWeights(); } SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { constructor: SkinnedMesh, isSkinnedMesh: true, initBones: function () { var bones = [], bone, gbone; var i, il; if ( this.geometry && this.geometry.bones !== undefined ) { // first, create array of "Bone" objects from geometry data for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; // create new "Bone" object bone = new Bone(); bones.push( bone ); // apply values bone.name = gbone.name; bone.position.fromArray( gbone.pos ); bone.quaternion.fromArray( gbone.rotq ); if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); } // second, create bone hierarchy for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { // subsequent bones in the hierarchy bones[ gbone.parent ].add( bones[ i ] ); } else { // topmost bone, immediate child of the skinned mesh this.add( bones[ i ] ); } } } // now the bones are part of the scene graph and children of the skinned mesh. // let"s update the corresponding matrices this.updateMatrixWorld( true ); return bones; }, bind: function ( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.getInverse( bindMatrix ); }, pose: function () { this.skeleton.pose(); }, normalizeSkinWeights: function () { var scale, i; if ( this.geometry && this.geometry.isGeometry ) { for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) { var sw = this.geometry.skinWeights[ i ]; scale = 1.0 / sw.manhattanLength(); if ( scale !== Infinity ) { sw.multiplyScalar( scale ); } else { sw.set( 1, 0, 0, 0 ); // do something reasonable } } } else if ( this.geometry && this.geometry.isBufferGeometry ) { var vec = new Vector4(); var skinWeight = this.geometry.attributes.skinWeight; for ( i = 0; i < skinWeight.count; i ++ ) { vec.x = skinWeight.getX( i ); vec.y = skinWeight.getY( i ); vec.z = skinWeight.getZ( i ); vec.w = skinWeight.getW( i ); scale = 1.0 / vec.manhattanLength(); if ( scale !== Infinity ) { vec.multiplyScalar( scale ); } else { vec.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); } } }, updateMatrixWorld: function ( force ) { Mesh.prototype.updateMatrixWorld.call( this, force ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.getInverse( this.matrixWorld ); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.getInverse( this.bindMatrix ); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode ); } }, clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ function LineBasicMaterial( parameters ) { Material.call( this ); this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.lights = false; this.setValues( parameters ); } LineBasicMaterial.prototype = Object.create( Material.prototype ); LineBasicMaterial.prototype.constructor = LineBasicMaterial; LineBasicMaterial.prototype.isLineBasicMaterial = true; LineBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function Line( geometry, material, mode ) { if ( mode === 1 ) { console.warn( "THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead." ); return new LineSegments( geometry, material ); } Object3D.call( this ); this.type = "Line"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); } Line.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Line, isLine: true, computeLineDistances: ( function () { var start = new Vector3(); var end = new Vector3(); return function computeLineDistances() { var geometry = this.geometry; if ( geometry.isBufferGeometry ) { // we assume non-indexed geometry if ( geometry.index === null ) { var positionAttribute = geometry.attributes.position; var lineDistances = [ 0 ]; for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { start.fromBufferAttribute( positionAttribute, i - 1 ); end.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += start.distanceTo( end ); } geometry.addAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var lineDistances = geometry.lineDistances; lineDistances[ 0 ] = 0; for ( var i = 1, l = vertices.length; i < l; i ++ ) { lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); } } return this; }; }() ), raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; var precisionSq = precision * precision; var geometry = this.geometry; var matrixWorld = this.matrixWorld; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var vStart = new Vector3(); var vEnd = new Vector3(); var interSegment = new Vector3(); var interRay = new Vector3(); var step = ( this && this.isLineSegments ) ? 2 : 1; if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, l = indices.length - 1; i < l; i += step ) { var a = indices[ i ]; var b = indices[ i + 1 ]; vStart.fromArray( positions, a * 3 ); vEnd.fromArray( positions, b * 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { vStart.fromArray( positions, 3 * i ); vEnd.fromArray( positions, 3 * i + 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var nbVertices = vertices.length; for ( var i = 0; i < nbVertices - 1; i += step ) { var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LineSegments( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineSegments"; } LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineSegments, isLineSegments: true, computeLineDistances: ( function () { var start = new Vector3(); var end = new Vector3(); return function computeLineDistances() { var geometry = this.geometry; if ( geometry.isBufferGeometry ) { // we assume non-indexed geometry if ( geometry.index === null ) { var positionAttribute = geometry.attributes.position; var lineDistances = []; for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) { start.fromBufferAttribute( positionAttribute, i ); end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); } geometry.addAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var lineDistances = geometry.lineDistances; for ( var i = 0, l = vertices.length; i < l; i += 2 ) { start.copy( vertices[ i ] ); end.copy( vertices[ i + 1 ] ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); } } return this; }; }() ) } ); /** * @author mgreter / http://github.com/mgreter */ function LineLoop( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineLoop"; } LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineLoop, isLineLoop: true, } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * size: , * sizeAttenuation: * * morphTargets: * } */ function PointsMaterial( parameters ) { Material.call( this ); this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.size = 1; this.sizeAttenuation = true; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } PointsMaterial.prototype = Object.create( Material.prototype ); PointsMaterial.prototype.constructor = PointsMaterial; PointsMaterial.prototype.isPointsMaterial = true; PointsMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.morphTargets = source.morphTargets; return this; }; /** * @author alteredq / http://alteredqualia.com/ */ function Points( geometry, material ) { Object3D.call( this ); this.type = "Points"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); } Points.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Points, isPoints: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var object = this; var geometry = this.geometry; var matrixWorld = this.matrixWorld; var threshold = raycaster.params.Points.threshold; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); var localThresholdSq = localThreshold * localThreshold; var position = new Vector3(); var intersectPoint = new Vector3(); function testPoint( point, index ) { var rayPointDistanceSq = ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint.clone(), index: index, face: null, object: object } ); } } if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, il = indices.length; i < il; i ++ ) { var a = indices[ i ]; position.fromArray( positions, a * 3 ); testPoint( position, a ); } } else { for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { position.fromArray( positions, i * 3 ); testPoint( position, i ); } } } else { var vertices = geometry.vertices; for ( var i = 0, l = vertices.length; i < l; i ++ ) { testPoint( vertices[ i ], i ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Group() { Object3D.call( this ); this.type = "Group"; } Group.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Group, isGroup: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.generateMipmaps = false; } VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { constructor: VideoTexture, isVideoTexture: true, update: function () { var video = this.image; if ( video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } CompressedTexture.prototype = Object.create( Texture.prototype ); CompressedTexture.prototype.constructor = CompressedTexture; CompressedTexture.prototype.isCompressedTexture = true; /** * @author Matt DesLauriers / @mattdesl * @author atix / arthursilber.de */ function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } DepthTexture.prototype = Object.create( Texture.prototype ); DepthTexture.prototype.constructor = DepthTexture; DepthTexture.prototype.isDepthTexture = true; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function WireframeGeometry( geometry ) { BufferGeometry.call( this ); this.type = "WireframeGeometry"; // buffer var vertices = []; // helper variables var i, j, l, o, ol; var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; var key, keys = [ "a", "b", "c" ]; var vertex; // different logic for Geometry and BufferGeometry if ( geometry && geometry.isGeometry ) { // create a data structure that contains all edges without duplicates var faces = geometry.faces; for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex = geometry.vertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = geometry.vertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } else if ( geometry && geometry.isBufferGeometry ) { var position, indices, groups; var group, start, count; var index1, index2; vertex = new Vector3(); if ( geometry.index !== null ) { // indexed BufferGeometry position = geometry.attributes.position; indices = geometry.index; groups = geometry.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; } // create a data structure that contains all eges without duplicates for ( o = 0, ol = groups.length; o < ol; ++ o ) { group = groups[ o ]; start = group.start; count = group.count; for ( i = start, l = ( start + count ); i < l; i += 3 ) { for ( j = 0; j < 3; j ++ ) { edge1 = indices.getX( i + j ); edge2 = indices.getX( i + ( j + 1 ) % 3 ); edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex.fromBufferAttribute( position, e.index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); vertex.fromBufferAttribute( position, e.index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } else { // non-indexed BufferGeometry position = geometry.attributes.position; for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { for ( j = 0; j < 3; j ++ ) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) index1 = 3 * i + j; vertex.fromBufferAttribute( position, index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); index2 = 3 * i + ( ( j + 1 ) % 3 ); vertex.fromBufferAttribute( position, index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); WireframeGeometry.prototype.constructor = WireframeGeometry; /** * @author zz85 / https://github.com/zz85 * @author Mugen87 / https://github.com/Mugen87 * * Parametric Surfaces Geometry * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 */ // ParametricGeometry function ParametricGeometry( func, slices, stacks ) { Geometry.call( this ); this.type = "ParametricGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); this.mergeVertices(); } ParametricGeometry.prototype = Object.create( Geometry.prototype ); ParametricGeometry.prototype.constructor = ParametricGeometry; // ParametricBufferGeometry function ParametricBufferGeometry( func, slices, stacks ) { BufferGeometry.call( this ); this.type = "ParametricBufferGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; var EPS = 0.00001; var normal = new Vector3(); var p0 = new Vector3(), p1 = new Vector3(); var pu = new Vector3(), pv = new Vector3(); var i, j; if ( func.length < 3 ) { console.error( "THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter." ); } // generate vertices, normals and uvs var sliceCount = slices + 1; for ( i = 0; i <= stacks; i ++ ) { var v = i / stacks; for ( j = 0; j <= slices; j ++ ) { var u = j / slices; // vertex func( u, v, p0 ); vertices.push( p0.x, p0.y, p0.z ); // normal // approximate tangent vectors via finite differences if ( u - EPS >= 0 ) { func( u - EPS, v, p1 ); pu.subVectors( p0, p1 ); } else { func( u + EPS, v, p1 ); pu.subVectors( p1, p0 ); } if ( v - EPS >= 0 ) { func( u, v - EPS, p1 ); pv.subVectors( p0, p1 ); } else { func( u, v + EPS, p1 ); pv.subVectors( p1, p0 ); } // cross product of tangent vectors returns surface normal normal.crossVectors( pu, pv ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, v ); } } // generate indices for ( i = 0; i < stacks; i ++ ) { for ( j = 0; j < slices; j ++ ) { var a = i * sliceCount + j; var b = i * sliceCount + j + 1; var c = ( i + 1 ) * sliceCount + j + 1; var d = ( i + 1 ) * sliceCount + j; // faces one and two indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; /** * @author clockworkgeek / https://github.com/clockworkgeek * @author timothypratley / https://github.com/timothypratley * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ // PolyhedronGeometry function PolyhedronGeometry( vertices, indices, radius, detail ) { Geometry.call( this ); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); this.mergeVertices(); } PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; // PolyhedronBufferGeometry function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { BufferGeometry.call( this ); this.type = "PolyhedronBufferGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; radius = radius || 1; detail = detail || 0; // default buffer data var vertexBuffer = []; var uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius appplyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.addAttribute( "position", new Float32BufferAttribute( vertexBuffer, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvBuffer, 2 ) ); if ( detail === 0 ) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide( detail ) { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for ( var i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { var cols = Math.pow( 2, detail ); // we use this multidimensional array as a data structure for creating the subdivision var v = []; var i, j; // construct all of the vertices for this subdivision for ( i = 0; i <= cols; i ++ ) { v[ i ] = []; var aj = a.clone().lerp( c, i / cols ); var bj = b.clone().lerp( c, i / cols ); var rows = cols - i; for ( j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( i = 0; i < cols; i ++ ) { for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { var k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function appplyRadius( radius ) { var vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { var vertex = new Vector3(); for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; var u = azimuth( vertex ) / 2 / Math.PI + 0.5; var v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( var i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face var x0 = uvBuffer[ i + 0 ]; var x1 = uvBuffer[ i + 2 ]; var x2 = uvBuffer[ i + 4 ]; var max = Math.max( x0, x1, x2 ); var min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { var stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); var centroid = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); var azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // TetrahedronGeometry function TetrahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; // TetrahedronBufferGeometry function TetrahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; var indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "TetrahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // OctahedronGeometry function OctahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } OctahedronGeometry.prototype = Object.create( Geometry.prototype ); OctahedronGeometry.prototype.constructor = OctahedronGeometry; // OctahedronBufferGeometry function OctahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; var indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "OctahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // IcosahedronGeometry function IcosahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; // IcosahedronBufferGeometry function IcosahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; var indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "IcosahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; /** * @author Abe Pazos / https://hamoid.com * @author Mugen87 / https://github.com/Mugen87 */ // DodecahedronGeometry function DodecahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; // DodecahedronBufferGeometry function DodecahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var r = 1 / t; var vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; var indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "DodecahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; /** * @author oosmoxiecode / https://github.com/oosmoxiecode * @author WestLangley / https://github.com/WestLangley * @author zz85 / https://github.com/zz85 * @author miningold / https://github.com/miningold * @author jonobr1 / https://github.com/jonobr1 * @author Mugen87 / https://github.com/Mugen87 * */ // TubeGeometry function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { Geometry.call( this ); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; if ( taper !== undefined ) console.warn( "THREE.TubeGeometry: taper has been removed." ); var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); // expose internals this.tangents = bufferGeometry.tangents; this.normals = bufferGeometry.normals; this.binormals = bufferGeometry.binormals; // create geometry this.fromBufferGeometry( bufferGeometry ); this.mergeVertices(); } TubeGeometry.prototype = Object.create( Geometry.prototype ); TubeGeometry.prototype.constructor = TubeGeometry; // TubeBufferGeometry function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { BufferGeometry.call( this ); this.type = "TubeBufferGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; tubularSegments = tubularSegments || 64; radius = radius || 1; radialSegments = radialSegments || 8; closed = closed || false; var frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables var vertex = new Vector3(); var normal = new Vector3(); var uv = new Vector2(); var P = new Vector3(); var i, j; // buffer var vertices = []; var normals = []; var uvs = []; var indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt( i / tubularSegments, P ); // retrieve corresponding normal and binormal var N = frames.normals[ i ]; var B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( j = 0; j <= radialSegments; j ++ ) { var v = j / radialSegments * Math.PI * 2; var sin = Math.sin( v ); var cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( i = 0; i <= tubularSegments; i ++ ) { for ( j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; /** * @author oosmoxiecode * @author Mugen87 / https://github.com/Mugen87 * * based on http://www.blackpawn.com/texts/pqtorus/ */ // TorusKnotGeometry function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { Geometry.call( this ); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; if ( heightScale !== undefined ) console.warn( "THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead." ); this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); this.mergeVertices(); } TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; // TorusKnotBufferGeometry function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { BufferGeometry.call( this ); this.type = "TorusKnotBufferGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; radius = radius || 1; tube = tube || 0.4; tubularSegments = Math.floor( tubularSegments ) || 64; radialSegments = Math.floor( radialSegments ) || 8; p = p || 2; q = q || 3; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, j; var vertex = new Vector3(); var normal = new Vector3(); var P1 = new Vector3(); var P2 = new Vector3(); var B = new Vector3(); var T = new Vector3(); var N = new Vector3(); // generate vertices, normals and uvs for ( i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement var u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for ( j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. var v = j / radialSegments * Math.PI * 2; var cx = - tube * Math.cos( v ); var cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { // indices var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { var cu = Math.cos( u ); var su = Math.sin( u ); var quOverP = q / p * u; var cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; /** * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // TorusGeometry function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { Geometry.call( this ); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); this.mergeVertices(); } TorusGeometry.prototype = Object.create( Geometry.prototype ); TorusGeometry.prototype.constructor = TorusGeometry; // TorusBufferGeometry function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { BufferGeometry.call( this ); this.type = "TorusBufferGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radius = radius || 1; tube = tube || 0.4; radialSegments = Math.floor( radialSegments ) || 8; tubularSegments = Math.floor( tubularSegments ) || 6; arc = arc || Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var center = new Vector3(); var vertex = new Vector3(); var normal = new Vector3(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= radialSegments; j ++ ) { for ( i = 0; i <= tubularSegments; i ++ ) { var u = i / tubularSegments * arc; var v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); normal.subVectors( vertex, center ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= radialSegments; j ++ ) { for ( i = 1; i <= tubularSegments; i ++ ) { // indices var a = ( tubularSegments + 1 ) * j + i - 1; var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; var d = ( tubularSegments + 1 ) * j + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * Port from https://github.com/mapbox/earcut (v2.1.2) */ var Earcut = { triangulate: function ( data, holeIndices, dim ) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, outerNode = linkedList( data, 0, outerLen, dim, true ), triangles = []; if ( ! outerNode ) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if ( data.length > 80 * dim ) { minX = maxX = data[ 0 ]; minY = maxY = data[ 1 ]; for ( var i = dim; i < outerLen; i += dim ) { x = data[ i ]; y = data[ i + 1 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max( maxX - minX, maxY - minY ); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList( data, start, end, dim, clockwise ) { var i, last; if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } else { for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } if ( last && equals( last, last.next ) ) { removeNode( last ); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints( start, end ) { if ( ! start ) return start; if ( ! end ) end = start; var p = start, again; do { again = false; if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { removeNode( p ); p = end = p.prev; if ( p === p.next ) break; again = true; } else { p = p.next; } } while ( again || p !== end ); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { if ( ! ear ) return; // interlink polygon nodes in z-order if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); var stop = ear, prev, next; // iterate through ears, slicing them one by one while ( ear.prev !== ear.next ) { prev = ear.prev; next = ear.next; if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { // cut off the triangle triangles.push( prev.i / dim ); triangles.push( ear.i / dim ); triangles.push( next.i / dim ); removeNode( ear ); // skipping the next vertice leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if ( ear === stop ) { // try filtering points and slicing again if ( ! pass ) { earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); // if this didn"t work, try curing all small self-intersections locally } else if ( pass === 1 ) { ear = cureLocalIntersections( ear, triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { splitEarcut( ear, triangles, dim, minX, minY, invSize ); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar( ear ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear var p = ear.next.next; while ( p !== ear.prev ) { if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) { return false; } p = p.next; } return true; } function isEarHashed( ear, minX, minY, invSize ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); // z-order range for the current triangle bbox; var minZ = zOrder( minTX, minTY, minX, minY, invSize ), maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); // first look for points inside the triangle in increasing z-order var p = ear.nextZ; while ( p && p.z <= maxZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.nextZ; } // then look for points in decreasing z-order p = ear.prevZ; while ( p && p.z >= minZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections( start, triangles, dim ) { var p = start; do { var a = p.prev, b = p.next.next; if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { triangles.push( a.i / dim ); triangles.push( p.i / dim ); triangles.push( b.i / dim ); // remove two nodes involved removeNode( p ); removeNode( p.next ); p = start = b; } p = p.next; } while ( p !== start ); return p; } // try splitting polygon into two and triangulate them independently function splitEarcut( start, triangles, dim, minX, minY, invSize ) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while ( b !== a.prev ) { if ( a.i !== b.i && isValidDiagonal( a, b ) ) { // split the polygon in two by the diagonal var c = splitPolygon( a, b ); // filter colinear points around the cuts a = filterPoints( a, a.next ); c = filterPoints( c, c.next ); // run earcut on each half earcutLinked( a, triangles, dim, minX, minY, invSize ); earcutLinked( c, triangles, dim, minX, minY, invSize ); return; } b = b.next; } a = a.next; } while ( a !== start ); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles( data, holeIndices, outerNode, dim ) { var queue = [], i, len, start, end, list; for ( i = 0, len = holeIndices.length; i < len; i ++ ) { start = holeIndices[ i ] * dim; end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; list = linkedList( data, start, end, dim, false ); if ( list === list.next ) list.steiner = true; queue.push( getLeftmost( list ) ); } queue.sort( compareX ); // process holes from left to right for ( i = 0; i < queue.length; i ++ ) { eliminateHole( queue[ i ], outerNode ); outerNode = filterPoints( outerNode, outerNode.next ); } return outerNode; } function compareX( a, b ) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole( hole, outerNode ) { outerNode = findHoleBridge( hole, outerNode ); if ( outerNode ) { var b = splitPolygon( outerNode, hole ); filterPoints( b, b.next ); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge( hole, outerNode ) { var p = outerNode, hx = hole.x, hy = hole.y, qx = - Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); if ( x <= hx && x > qx ) { qx = x; if ( x === hx ) { if ( hy === p.y ) return p; if ( hy === p.next.y ) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while ( p !== outerNode ); if ( ! m ) return null; if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m.next; while ( p !== stop ) { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) { m = p; tanMin = tan; } } p = p.next; } return m; } // interlink polygon nodes in z-order function indexCurve( start, minX, minY, invSize ) { var p = start; do { if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while ( p !== start ); p.prevZ.nextZ = null; p.prevZ = null; sortLinked( p ); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked( list ) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while ( p ) { numMerges ++; q = p; pSize = 0; for ( i = 0; i < inSize; i ++ ) { pSize ++; q = q.nextZ; if ( ! q ) break; } qSize = inSize; while ( pSize > 0 || ( qSize > 0 && q ) ) { if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { e = p; p = p.nextZ; pSize --; } else { e = q; q = q.nextZ; qSize --; } if ( tail ) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while ( numMerges > 1 ); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder( x, y, minX, minY, invSize ) { // coords are transformed into non-negative 15-bit integer range x = 32767 * ( x - minX ) * invSize; y = 32767 * ( y - minY ) * invSize; x = ( x | ( x << 8 ) ) & 0x00FF00FF; x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; x = ( x | ( x << 2 ) ) & 0x33333333; x = ( x | ( x << 1 ) ) & 0x55555555; y = ( y | ( y << 8 ) ) & 0x00FF00FF; y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; y = ( y | ( y << 2 ) ) & 0x33333333; y = ( y | ( y << 1 ) ) & 0x55555555; return x | ( y << 1 ); } // find the leftmost node of a polygon ring function getLeftmost( start ) { var p = start, leftmost = start; do { if ( p.x < leftmost.x ) leftmost = p; p = p.next; } while ( p !== start ); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal( a, b ) { return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ); } // signed area of a triangle function area( p, q, r ) { return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); } // check if two points are equal function equals( p1, p2 ) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects( p1, q1, p2, q2 ) { if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) || ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true; return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 && area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon( a, b ) { var p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects( p, p.next, a, b ) ) { return true; } p = p.next; } while ( p !== a ); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside( a, b ) { return area( a.prev, a, a.next ) < 0 ? area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside( a, b ) { var p = a, inside = false, px = ( a.x + b.x ) / 2, py = ( a.y + b.y ) / 2; do { if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) { inside = ! inside; } p = p.next; } while ( p !== a ); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon( a, b ) { var a2 = new Node( a.i, a.x, a.y ), b2 = new Node( b.i, b.x, b.y ), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode( i, x, y, last ) { var p = new Node( i, x, y ); if ( ! last ) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode( p ) { p.next.prev = p.prev; p.prev.next = p.next; if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; } function Node( i, x, y ) { // vertice index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertice nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea( data, start, end, dim ) { var sum = 0; for ( var i = start, j = end - dim; i < end; i += dim ) { sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); j = i; } return sum; } /** * @author zz85 / http://www.lab4games.net/zz85/blog */ var ShapeUtils = { // calculate area of the contour polygon area: function ( contour ) { var n = contour.length; var a = 0.0; for ( var p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; }, isClockWise: function ( pts ) { return ShapeUtils.area( pts ) < 0; }, triangulateShape: function ( contour, holes ) { var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] var holeIndices = []; // array of hole indices var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts( contour ); addContour( vertices, contour ); // var holeIndex = contour.length; holes.forEach( removeDupEndPts ); for ( var i = 0; i < holes.length; i ++ ) { holeIndices.push( holeIndex ); holeIndex += holes[ i ].length; addContour( vertices, holes[ i ] ); } // var triangles = Earcut.triangulate( vertices, holeIndices ); // for ( var i = 0; i < triangles.length; i += 3 ) { faces.push( triangles.slice( i, i + 3 ) ); } return faces; } }; function removeDupEndPts( points ) { var l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } function addContour( vertices, contour ) { for ( var i = 0; i < contour.length; i ++ ) { vertices.push( contour[ i ].x ); vertices.push( contour[ i ].y ); } } /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline is bevel * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ // ExtrudeGeometry function ExtrudeGeometry( shapes, options ) { Geometry.call( this ); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); this.mergeVertices(); } ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; ExtrudeGeometry.prototype.toJSON = function () { var data = Geometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; var options = this.parameters.options; return toJSON( shapes, options, data ); }; // ExtrudeBufferGeometry function ExtrudeBufferGeometry( shapes, options ) { BufferGeometry.call( this ); this.type = "ExtrudeBufferGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; var scope = this; var verticesArray = []; var uvArray = []; for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; addShape( shape ); } // build geometry this.addAttribute( "position", new Float32BufferAttribute( verticesArray, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvArray, 2 ) ); this.computeVertexNormals(); // functions function addShape( shape ) { var placeholder = []; // options var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; var steps = options.steps !== undefined ? options.steps : 1; var depth = options.depth !== undefined ? options.depth : 100; var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; var extrudePath = options.extrudePath; var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if ( options.amount !== undefined ) { console.warn( "THREE.ExtrudeBufferGeometry: amount has been renamed to depth." ); depth = options.amount; } // var extrudePts, extrudeByPath = false; var splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initialization var ahole, h, hl; // looping of holes var shapePoints = shape.extractPoints( curveSegments ); var vertices = shapePoints.shape; var holes = shapePoints.holes; var reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } } var faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return vec.clone().multiplyScalar( size ).add( pt ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing var v_prev_len = Math.sqrt( v_prev_lensq ); var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges var direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } var contourMovements = []; for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = []; for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= steps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, depth / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, depth + z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, depth + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { var start = verticesArray.length / 3; if ( bevelEnabled ) { var layer = 0; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } scope.addGroup( start, verticesArray.length / 3 - start, 0 ); } // Create faces for the z-sides of the shape function buildSideFaces() { var start = verticesArray.length / 3; var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } scope.addGroup( start, verticesArray.length / 3 - start, 1 ); } function sidewalls( contour, layeroffset ) { var j, k; i = contour.length; while ( -- i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); var s = 0, sl = steps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d ); } } } function v( x, y, z ) { placeholder.push( x ); placeholder.push( y ); placeholder.push( z ); } function f3( a, b, c ) { addVertex( a ); addVertex( b ); addVertex( c ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); } function f4( a, b, c, d ) { addVertex( a ); addVertex( b ); addVertex( d ); addVertex( b ); addVertex( c ); addVertex( d ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 3 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); addUV( uvs[ 3 ] ); } function addVertex( index ) { verticesArray.push( placeholder[ index * 3 + 0 ] ); verticesArray.push( placeholder[ index * 3 + 1 ] ); verticesArray.push( placeholder[ index * 3 + 2 ] ); } function addUV( vector2 ) { uvArray.push( vector2.x ); uvArray.push( vector2.y ); } } } ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; ExtrudeBufferGeometry.prototype.toJSON = function () { var data = BufferGeometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; var options = this.parameters.options; return toJSON( shapes, options, data ); }; // var WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; return [ new Vector2( a_x, a_y ), new Vector2( b_x, b_y ), new Vector2( c_x, c_y ) ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var a_z = vertices[ indexA * 3 + 2 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var b_z = vertices[ indexB * 3 + 2 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; var c_z = vertices[ indexC * 3 + 2 ]; var d_x = vertices[ indexD * 3 ]; var d_y = vertices[ indexD * 3 + 1 ]; var d_z = vertices[ indexD * 3 + 2 ]; if ( Math.abs( a_y - b_y ) < 0.01 ) { return [ new Vector2( a_x, 1 - a_z ), new Vector2( b_x, 1 - b_z ), new Vector2( c_x, 1 - c_z ), new Vector2( d_x, 1 - d_z ) ]; } else { return [ new Vector2( a_y, 1 - a_z ), new Vector2( b_y, 1 - b_z ), new Vector2( c_y, 1 - c_z ), new Vector2( d_y, 1 - d_z ) ]; } } }; function toJSON( shapes, options, data ) { // data.shapes = []; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } // if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); return data; } /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ * * Text = 3D Text * * parameters = { * font: , // font * * size: , // size of the text * height: , // thickness to extrude text * curveSegments: , // number of points on the curves * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into text bevel goes * bevelSize: // how far from text outline is bevel * } */ // TextGeometry function TextGeometry( text, parameters ) { Geometry.call( this ); this.type = "TextGeometry"; this.parameters = { text: text, parameters: parameters }; this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); this.mergeVertices(); } TextGeometry.prototype = Object.create( Geometry.prototype ); TextGeometry.prototype.constructor = TextGeometry; // TextBufferGeometry function TextBufferGeometry( text, parameters ) { parameters = parameters || {}; var font = parameters.font; if ( ! ( font && font.isFont ) ) { console.error( "THREE.TextGeometry: font parameter is not an instance of THREE.Font." ); return new Geometry(); } var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); // translate parameters to ExtrudeGeometry API parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; ExtrudeBufferGeometry.call( this, shapes, parameters ); this.type = "TextBufferGeometry"; } TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); TextBufferGeometry.prototype.constructor = TextBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 */ // SphereGeometry function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); this.mergeVertices(); } SphereGeometry.prototype = Object.create( Geometry.prototype ); SphereGeometry.prototype.constructor = SphereGeometry; // SphereBufferGeometry function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "SphereBufferGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); phiStart = phiStart !== undefined ? phiStart : 0; phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; var thetaEnd = thetaStart + thetaLength; var ix, iy; var index = 0; var grid = []; var vertex = new Vector3(); var normal = new Vector3(); // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy <= heightSegments; iy ++ ) { var verticesRow = []; var v = iy / heightSegments; for ( ix = 0; ix <= widthSegments; ix ++ ) { var u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( vertex.x, vertex.y, vertex.z ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( iy = 0; iy < heightSegments; iy ++ ) { for ( ix = 0; ix < widthSegments; ix ++ ) { var a = grid[ iy ][ ix + 1 ]; var b = grid[ iy ][ ix ]; var c = grid[ iy + 1 ][ ix ]; var d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; /** * @author Kaleb Murphy * @author Mugen87 / https://github.com/Mugen87 */ // RingGeometry function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); this.mergeVertices(); } RingGeometry.prototype = Object.create( Geometry.prototype ); RingGeometry.prototype.constructor = RingGeometry; // RingBufferGeometry function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "RingBufferGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; innerRadius = innerRadius || 0.5; outerRadius = outerRadius || 1; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // some helper variables var segment; var radius = innerRadius; var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); var vertex = new Vector3(); var uv = new Vector2(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= phiSegments; j ++ ) { for ( i = 0; i <= thetaSegments; i ++ ) { // values are generate from the inside of the ring to the outside segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // increase the radius for next row of vertices radius += radiusStep; } // indices for ( j = 0; j < phiSegments; j ++ ) { var thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( i = 0; i < thetaSegments; i ++ ) { segment = i + thetaSegmentLevel; var a = segment; var b = segment + thetaSegments + 1; var c = segment + thetaSegments + 2; var d = segment + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); RingBufferGeometry.prototype.constructor = RingBufferGeometry; /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 * @author bhouston / http://clara.io * @author Mugen87 / https://github.com/Mugen87 */ // LatheGeometry function LatheGeometry( points, segments, phiStart, phiLength ) { Geometry.call( this ); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); this.mergeVertices(); } LatheGeometry.prototype = Object.create( Geometry.prototype ); LatheGeometry.prototype.constructor = LatheGeometry; // LatheBufferGeometry function LatheBufferGeometry( points, segments, phiStart, phiLength ) { BufferGeometry.call( this ); this.type = "LatheBufferGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ) || 12; phiStart = phiStart || 0; phiLength = phiLength || Math.PI * 2; // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); // buffers var indices = []; var vertices = []; var uvs = []; // helper variables var base; var inverseSegments = 1.0 / segments; var vertex = new Vector3(); var uv = new Vector2(); var i, j; // generate vertices and uvs for ( i = 0; i <= segments; i ++ ) { var phi = phiStart + i * inverseSegments * phiLength; var sin = Math.sin( phi ); var cos = Math.cos( phi ); for ( j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); } } // indices for ( i = 0; i < segments; i ++ ) { for ( j = 0; j < ( points.length - 1 ); j ++ ) { base = j + i * points.length; var a = base; var b = base + points.length; var c = base + points.length + 1; var d = base + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // generate normals this.computeVertexNormals(); // if the geometry is closed, we need to average the normals along the seam. // because the corresponding vertices are identical (but still have different UVs). if ( phiLength === Math.PI * 2 ) { var normals = this.attributes.normal.array; var n1 = new Vector3(); var n2 = new Vector3(); var n = new Vector3(); // this is the buffer offset for the last line of vertices base = segments * points.length * 3; for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { // select the normal of the vertex in the first line n1.x = normals[ j + 0 ]; n1.y = normals[ j + 1 ]; n1.z = normals[ j + 2 ]; // select the normal of the vertex in the last line n2.x = normals[ base + j + 0 ]; n2.y = normals[ base + j + 1 ]; n2.z = normals[ base + j + 2 ]; // average normals n.addVectors( n1, n2 ).normalize(); // assign the new values to both normals normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; } } } LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; /** * @author jonobr1 / http://jonobr1.com * @author Mugen87 / https://github.com/Mugen87 */ // ShapeGeometry function ShapeGeometry( shapes, curveSegments ) { Geometry.call( this ); this.type = "ShapeGeometry"; if ( typeof curveSegments === "object" ) { console.warn( "THREE.ShapeGeometry: Options parameter has been removed." ); curveSegments = curveSegments.curveSegments; } this.parameters = { shapes: shapes, curveSegments: curveSegments }; this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); this.mergeVertices(); } ShapeGeometry.prototype = Object.create( Geometry.prototype ); ShapeGeometry.prototype.constructor = ShapeGeometry; ShapeGeometry.prototype.toJSON = function () { var data = Geometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON$1( shapes, data ); }; // ShapeBufferGeometry function ShapeBufferGeometry( shapes, curveSegments ) { BufferGeometry.call( this ); this.type = "ShapeBufferGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; curveSegments = curveSegments || 12; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var groupStart = 0; var groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( var i = 0; i < shapes.length; i ++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { var i, l, shapeHole; var indexOffset = vertices.length / 3; var points = shape.extractPoints( curveSegments ); var shapeVertices = points.shape; var shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); // also check if holes are in the opposite direction for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } } var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { var vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // incides for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var a = face[ 0 ] + indexOffset; var b = face[ 1 ] + indexOffset; var c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; ShapeBufferGeometry.prototype.toJSON = function () { var data = BufferGeometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON$1( shapes, data ); }; // function toJSON$1( shapes, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } return data; } /** * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ function EdgesGeometry( geometry, thresholdAngle ) { BufferGeometry.call( this ); this.type = "EdgesGeometry"; this.parameters = { thresholdAngle: thresholdAngle }; thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; // buffer var vertices = []; // helper variables var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); var edge = [ 0, 0 ], edges = {}, edge1, edge2; var key, keys = [ "a", "b", "c" ]; // prepare source geometry var geometry2; if ( geometry.isBufferGeometry ) { geometry2 = new Geometry(); geometry2.fromBufferGeometry( geometry ); } else { geometry2 = geometry.clone(); } geometry2.mergeVertices(); geometry2.computeFaceNormals(); var sourceVertices = geometry2.vertices; var faces = geometry2.faces; // now create a data structure where each entry represents an edge with its adjoining faces for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; } else { edges[ key ].face2 = i; } } } // generate vertices for ( key in edges ) { var e = edges[ key ]; // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { var vertex = sourceVertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = sourceVertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); EdgesGeometry.prototype.constructor = EdgesGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // CylinderGeometry function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); this.mergeVertices(); } CylinderGeometry.prototype = Object.create( Geometry.prototype ); CylinderGeometry.prototype.constructor = CylinderGeometry; // CylinderBufferGeometry function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CylinderBufferGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; var scope = this; radiusTop = radiusTop !== undefined ? radiusTop : 1; radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; height = height || 1; radialSegments = Math.floor( radialSegments ) || 8; heightSegments = Math.floor( heightSegments ) || 1; openEnded = openEnded !== undefined ? openEnded : false; thetaStart = thetaStart !== undefined ? thetaStart : 0.0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var index = 0; var indexArray = []; var halfHeight = height / 2; var groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function generateTorso() { var x, y; var normal = new Vector3(); var vertex = new Vector3(); var groupCount = 0; // this will be used to calculate the normal var slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( y = 0; y <= heightSegments; y ++ ) { var indexRow = []; var v = y / heightSegments; // calculate the radius of the current row var radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var sinTheta = Math.sin( theta ); var cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( x = 0; x < radialSegments; x ++ ) { for ( y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices var a = indexArray[ y ][ x ]; var b = indexArray[ y + 1 ][ x ]; var c = indexArray[ y + 1 ][ x + 1 ]; var d = indexArray[ y ][ x + 1 ]; // faces indices.push( a, b, d ); indices.push( b, c, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { var x, centerIndexStart, centerIndexEnd; var uv = new Vector2(); var vertex = new Vector3(); var groupCount = 0; var radius = ( top === true ) ? radiusTop : radiusBottom; var sign = ( top === true ) ? 1 : - 1; // save the index of the first center vertex centerIndexStart = index; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, halfHeight * sign, 0 ); // normal normals.push( 0, sign, 0 ); // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var cosTheta = Math.cos( theta ); var sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( x = 0; x < radialSegments; x ++ ) { var c = centerIndexStart + x; var i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; /** * @author abelnation / http://github.com/abelnation */ // ConeGeometry function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); ConeGeometry.prototype.constructor = ConeGeometry; // ConeBufferGeometry function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeBufferGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; /** * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 * @author hughes */ // CircleGeometry function CircleGeometry( radius, segments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); this.mergeVertices(); } CircleGeometry.prototype = Object.create( Geometry.prototype ); CircleGeometry.prototype.constructor = CircleGeometry; // CircleBufferGeometry function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CircleBufferGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; segments = segments !== undefined ? Math.max( 3, segments ) : 8; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, s; var vertex = new Vector3(); var uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { var segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; var Geometries = /*#__PURE__*/Object.freeze({ WireframeGeometry: WireframeGeometry, ParametricGeometry: ParametricGeometry, ParametricBufferGeometry: ParametricBufferGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronBufferGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronBufferGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronBufferGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronBufferGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronBufferGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeBufferGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotBufferGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusBufferGeometry, TextGeometry: TextGeometry, TextBufferGeometry: TextBufferGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereBufferGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingBufferGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneBufferGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheBufferGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeBufferGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeBufferGeometry, EdgesGeometry: EdgesGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeBufferGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderBufferGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleBufferGeometry, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxBufferGeometry }); /** * @author mrdoob / http://mrdoob.com/ * * parameters = { * color: * } */ function ShadowMaterial( parameters ) { Material.call( this ); this.type = "ShadowMaterial"; this.color = new Color( 0x000000 ); this.transparent = true; this.setValues( parameters ); } ShadowMaterial.prototype = Object.create( Material.prototype ); ShadowMaterial.prototype.constructor = ShadowMaterial; ShadowMaterial.prototype.isShadowMaterial = true; ShadowMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function RawShaderMaterial( parameters ) { ShaderMaterial.call( this, parameters ); this.type = "RawShaderMaterial"; } RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); RawShaderMaterial.prototype.constructor = RawShaderMaterial; RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshStandardMaterial( parameters ) { Material.call( this ); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color( 0xffffff ); // diffuse this.roughness = 0.5; this.metalness = 0.5; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshStandardMaterial.prototype = Object.create( Material.prototype ); MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; MeshStandardMaterial.prototype.isMeshStandardMaterial = true; MeshStandardMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.defines = { "STANDARD": "" }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * reflectivity: * } */ function MeshPhysicalMaterial( parameters ) { MeshStandardMaterial.call( this ); this.defines = { "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.reflectivity = 0.5; // maps to F0 = 0.04 this.clearCoat = 0.0; this.clearCoatRoughness = 0.0; this.setValues( parameters ); } MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; MeshPhysicalMaterial.prototype.copy = function ( source ) { MeshStandardMaterial.prototype.copy.call( this, source ); this.defines = { "PHYSICAL": "" }; this.reflectivity = source.reflectivity; this.clearCoat = source.clearCoat; this.clearCoatRoughness = source.clearCoatRoughness; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshPhongMaterial( parameters ) { Material.call( this ); this.type = "MeshPhongMaterial"; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshPhongMaterial.prototype = Object.create( Material.prototype ); MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; MeshPhongMaterial.prototype.isMeshPhongMaterial = true; MeshPhongMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author takahirox / http://github.com/takahirox * * parameters = { * gradientMap: new THREE.Texture( ) * } */ function MeshToonMaterial( parameters ) { MeshPhongMaterial.call( this ); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.gradientMap = null; this.setValues( parameters ); } MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); MeshToonMaterial.prototype.constructor = MeshToonMaterial; MeshToonMaterial.prototype.isMeshToonMaterial = true; MeshToonMaterial.prototype.copy = function ( source ) { MeshPhongMaterial.prototype.copy.call( this, source ); this.gradientMap = source.gradientMap; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshNormalMaterial( parameters ) { Material.call( this ); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshNormalMaterial.prototype = Object.create( Material.prototype ); MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; MeshNormalMaterial.prototype.isMeshNormalMaterial = true; MeshNormalMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshLambertMaterial( parameters ) { Material.call( this ); this.type = "MeshLambertMaterial"; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshLambertMaterial.prototype = Object.create( Material.prototype ); MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; MeshLambertMaterial.prototype.isMeshLambertMaterial = true; MeshLambertMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ function LineDashedMaterial( parameters ) { LineBasicMaterial.call( this ); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues( parameters ); } LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); LineDashedMaterial.prototype.constructor = LineDashedMaterial; LineDashedMaterial.prototype.isLineDashedMaterial = true; LineDashedMaterial.prototype.copy = function ( source ) { LineBasicMaterial.prototype.copy.call( this, source ); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; }; var Materials = /*#__PURE__*/Object.freeze({ ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); /** * @author mrdoob / http://mrdoob.com/ */ var Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; /** * @author mrdoob / http://mrdoob.com/ */ function LoadingManager( onLoad, onProgress, onError ) { var scope = this; var isLoading = false; var itemsLoaded = 0; var itemsTotal = 0; var urlModifier = undefined; this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; this.resolveURL = function ( url ) { if ( urlModifier ) { return urlModifier( url ); } return url; }; this.setURLModifier = function ( transform ) { urlModifier = transform; return this; }; } var DefaultLoadingManager = new LoadingManager(); /** * @author mrdoob / http://mrdoob.com/ */ var loading = {}; function FileLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FileLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } // Check if request is duplicate if ( loading[ url ] !== undefined ) { loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); return; } // Check for data: URI var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; var dataUriRegexResult = url.match( dataUriRegex ); // Safari can not handle Data URIs through XMLHttpRequest so process manually if ( dataUriRegexResult ) { var mimeType = dataUriRegexResult[ 1 ]; var isBase64 = !! dataUriRegexResult[ 2 ]; var data = dataUriRegexResult[ 3 ]; data = window.decodeURIComponent( data ); if ( isBase64 ) data = window.atob( data ); try { var response; var responseType = ( this.responseType || "" ).toLowerCase(); switch ( responseType ) { case "arraybuffer": case "blob": var view = new Uint8Array( data.length ); for ( var i = 0; i < data.length; i ++ ) { view[ i ] = data.charCodeAt( i ); } if ( responseType === "blob" ) { response = new Blob( [ view.buffer ], { type: mimeType } ); } else { response = view.buffer; } break; case "document": var parser = new DOMParser(); response = parser.parseFromString( data, mimeType ); break; case "json": response = JSON.parse( data ); break; default: // "text" or other response = data; break; } // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); }, 0 ); } catch ( error ) { // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onError ) onError( error ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, 0 ); } } else { // Initialise array for duplicate requests loading[ url ] = []; loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); var request = new XMLHttpRequest(); request.open( "GET", url, true ); request.addEventListener( "load", function ( event ) { var response = this.response; Cache.add( url, response ); var callbacks = loading[ url ]; delete loading[ url ]; if ( this.status === 200 || this.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if ( this.status === 0 ) console.warn( "THREE.FileLoader: HTTP Status 0 received." ); for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( response ); } scope.manager.itemEnd( url ); } else { for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); } }, false ); request.addEventListener( "progress", function ( event ) { var callbacks = loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onProgress ) callback.onProgress( event ); } }, false ); request.addEventListener( "error", function ( event ) { var callbacks = loading[ url ]; delete loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, false ); if ( this.responseType !== undefined ) request.responseType = this.responseType; if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : "text/plain" ); for ( var header in this.requestHeader ) { request.setRequestHeader( header, this.requestHeader[ header ] ); } request.send( null ); } scope.manager.itemStart( url ); return request; }, setPath: function ( value ) { this.path = value; return this; }, setResponseType: function ( value ) { this.responseType = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setMimeType: function ( value ) { this.mimeType = value; return this; }, setRequestHeader: function ( value ) { this.requestHeader = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * * Abstract Base class to block based textures loader (dds, pvr, ...) */ function CompressedTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( CompressedTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var images = []; var texture = new CompressedTexture(); texture.image = images; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( "arraybuffer" ); function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { var texDatas = scope._parser( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { var loaded = 0; for ( var i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { var texDatas = scope._parser( buffer, true ); if ( texDatas.isCubemap ) { var faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( var f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps: [] }; for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author Nikos M. / https://github.com/foo123/ * * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ function DataTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( DataTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texture = new DataTexture(); var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var texData = scope._parser( buffer ); if ( ! texData ) return; if ( undefined !== texData.image ) { texture.image = texData.image; } else if ( undefined !== texData.data ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; if ( undefined !== texData.format ) { texture.format = texData.format; } if ( undefined !== texData.type ) { texture.type = texData.type; } if ( undefined !== texData.mipmaps ) { texture.mipmaps = texData.mipmaps; } if ( 1 === texData.mipmapCount ) { texture.minFilter = LinearFilter; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ImageLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( ImageLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } var image = document.createElementNS( "http://www.w3.org/1999/xhtml", "img" ); function onImageLoad() { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); Cache.add( url, this ); if ( onLoad ) onLoad( this ); scope.manager.itemEnd( url ); } function onImageError( event ) { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); if ( onError ) onError( event ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); } image.addEventListener( "load", onImageLoad, false ); image.addEventListener( "error", onImageError, false ); if ( url.substr( 0, 5 ) !== "data:" ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart( url ); image.src = url; return image; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function CubeTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( CubeTextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( urls, onLoad, onProgress, onError ) { var texture = new CubeTexture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); var loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( var i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function TextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( TextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { var texture = new Texture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); loader.load( url, function ( image ) { texture.image = image; // JPEGs can"t have an alpha channel, so memory can be saved by storing them as RGB. var isJPEG = url.search( /.(jpg|jpeg)$/ ) > 0 || url.search( /^data:image/jpeg/ ) === 0; texture.format = isJPEG ? RGBFormat : RGBAFormat; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Extensible curve object * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t ) * .getPointAt( u, optionalTarget ), .getTangentAt( u ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ /************************************************************** * Abstract Curve base class **************************************************************/ function Curve() { this.type = "Curve"; this.arcLengthDivisions = 200; } Object.assign( Curve.prototype, { // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint: function ( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; }, // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt: function ( u, optionalTarget ) { var t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); }, // Get sequence of points using getPoint( t ) getPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; }, // Get sequence of points using getPointAt( u ) getSpacedPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; }, // Get total curve arc length getLength: function () { var lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; }, // Get list of cumulative segment lengths getLengths: function ( divisions ) { if ( divisions === undefined ) divisions = this.arcLengthDivisions; if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; var cache = []; var current, last = this.getPoint( 0 ); var p, sum = 0; cache.push( 0 ); for ( p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. }, updateArcLengths: function () { this.needsUpdate = true; this.getLengths(); }, // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping: function ( u, distance ) { var arcLengths = this.getLengths(); var i = 0, il = arcLengths.length; var targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance var low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points var lengthBefore = arcLengths[ i ]; var lengthAfter = arcLengths[ i + 1 ]; var segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t var t = ( i + segmentFraction ) / ( il - 1 ); return t; }, // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent: function ( t ) { var delta = 0.0001; var t1 = t - delta; var t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; var pt1 = this.getPoint( t1 ); var pt2 = this.getPoint( t2 ); var vec = pt2.clone().sub( pt1 ); return vec.normalize(); }, getTangentAt: function ( u ) { var t = this.getUtoTmapping( u ); return this.getTangent( t ); }, computeFrenetFrames: function ( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf var normal = new Vector3(); var tangents = []; var normals = []; var binormals = []; var vec = new Vector3(); var mat = new Matrix4(); var i, u, theta; // compute the tangent vectors for each segment on the curve for ( i = 0; i <= segments; i ++ ) { u = i / segments; tangents[ i ] = this.getTangentAt( u ); tangents[ i ].normalize(); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); var min = Number.MAX_VALUE; var tx = Math.abs( tangents[ 0 ].x ); var ty = Math.abs( tangents[ 0 ].y ); var tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; }, fromJSON: function ( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } ); function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { Curve.call( this ); this.type = "EllipseCurve"; this.aX = aX || 0; this.aY = aY || 0; this.xRadius = xRadius || 1; this.yRadius = yRadius || 1; this.aStartAngle = aStartAngle || 0; this.aEndAngle = aEndAngle || 2 * Math.PI; this.aClockwise = aClockwise || false; this.aRotation = aRotation || 0; } EllipseCurve.prototype = Object.create( Curve.prototype ); EllipseCurve.prototype.constructor = EllipseCurve; EllipseCurve.prototype.isEllipseCurve = true; EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var twoPi = Math.PI * 2; var deltaAngle = this.aEndAngle - this.aStartAngle; var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } var angle = this.aStartAngle + t * deltaAngle; var x = this.aX + this.xRadius * Math.cos( angle ); var y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { var cos = Math.cos( this.aRotation ); var sin = Math.sin( this.aRotation ); var tx = x - this.aX; var ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set( x, y ); }; EllipseCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; }; EllipseCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; }; EllipseCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; }; function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); this.type = "ArcCurve"; } ArcCurve.prototype = Object.create( EllipseCurve.prototype ); ArcCurve.prototype.constructor = ArcCurve; ArcCurve.prototype.isArcCurve = true; /** * @author zz85 https://github.com/zz85 * * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { var c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { var t2 = t * t; var t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // var tmp = new Vector3(); var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); function CatmullRomCurve3( points, closed, curveType, tension ) { Curve.call( this ); this.type = "CatmullRomCurve3"; this.points = points || []; this.closed = closed || false; this.curveType = curveType || "centripetal"; this.tension = tension || 0.5; } CatmullRomCurve3.prototype = Object.create( Curve.prototype ); CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var points = this.points; var l = points.length; var p = ( l - ( this.closed ? 0 : 1 ) ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } var p0, p1, p2, p3; // 4 points if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } p1 = points[ intPoint % l ]; p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom var pow = this.curveType === "chordal" ? 0.5 : 0.25; var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; }; CatmullRomCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; }; CatmullRomCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; }; CatmullRomCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Bezier Curves formulas obtained from * http://en.wikipedia.org/wiki/Bézier_curve */ function CatmullRom( t, p0, p1, p2, p3 ) { var v0 = ( p2 - p0 ) * 0.5; var v1 = ( p3 - p1 ) * 0.5; var t2 = t * t; var t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } // function QuadraticBezierP0( t, p ) { var k = 1 - t; return k * k * p; } function QuadraticBezierP1( t, p ) { return 2 * ( 1 - t ) * t * p; } function QuadraticBezierP2( t, p ) { return t * t * p; } function QuadraticBezier( t, p0, p1, p2 ) { return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + QuadraticBezierP2( t, p2 ); } // function CubicBezierP0( t, p ) { var k = 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { var k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + CubicBezierP3( t, p3 ); } function CubicBezierCurve( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); this.v3 = v3 || new Vector2(); } CubicBezierCurve.prototype = Object.create( Curve.prototype ); CubicBezierCurve.prototype.constructor = CubicBezierCurve; CubicBezierCurve.prototype.isCubicBezierCurve = true; CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; }; CubicBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function CubicBezierCurve3( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); this.v3 = v3 || new Vector3(); } CubicBezierCurve3.prototype = Object.create( Curve.prototype ); CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; }; CubicBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function LineCurve( v1, v2 ) { Curve.call( this ); this.type = "LineCurve"; this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } LineCurve.prototype = Object.create( Curve.prototype ); LineCurve.prototype.constructor = LineCurve; LineCurve.prototype.isLineCurve = true; LineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve.prototype.getTangent = function ( /* t */ ) { var tangent = this.v2.clone().sub( this.v1 ); return tangent.normalize(); }; LineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function LineCurve3( v1, v2 ) { Curve.call( this ); this.type = "LineCurve3"; this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } LineCurve3.prototype = Object.create( Curve.prototype ); LineCurve3.prototype.constructor = LineCurve3; LineCurve3.prototype.isLineCurve3 = true; LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ) ); return point; }; QuadraticBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve3( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ), QuadraticBezier( t, v0.z, v1.z, v2.z ) ); return point; }; QuadraticBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function SplineCurve( points /* array of Vector2 */ ) { Curve.call( this ); this.type = "SplineCurve"; this.points = points || []; } SplineCurve.prototype = Object.create( Curve.prototype ); SplineCurve.prototype.constructor = SplineCurve; SplineCurve.prototype.isSplineCurve = true; SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var points = this.points; var p = ( points.length - 1 ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; var p1 = points[ intPoint ]; var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; point.set( CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) ); return point; }; SplineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } return this; }; SplineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } return data; }; SplineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector2().fromArray( point ) ); } return this; }; var Curves = /*#__PURE__*/Object.freeze({ ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /** * @author zz85 / http://www.lab4games.net/zz85/blog * **/ /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ function CurvePath() { Curve.call( this ); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { constructor: CurvePath, add: function ( curve ) { this.curves.push( curve ); }, closePath: function () { // Add a line curve if start and end of lines are not connected var startPoint = this.curves[ 0 ].getPoint( 0 ); var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } }, // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint: function ( t ) { var d = t * this.getLength(); var curveLengths = this.getCurveLengths(); var i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { var diff = curveLengths[ i ] - d; var curve = this.curves[ i ]; var segmentLength = curve.getLength(); var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; }, copy: function ( source ) { Curve.prototype.copy.call( this, source ); this.curves = []; for ( var i = 0, l = source.curves.length; i < l; i ++ ) { var curve = source.curves[ i ]; this.curves.push( curve.clone() ); } this.autoClose = source.autoClose; return this; }, toJSON: function () { var data = Curve.prototype.toJSON.call( this ); data.autoClose = this.autoClose; data.curves = []; for ( var i = 0, l = this.curves.length; i < l; i ++ ) { var curve = this.curves[ i ]; data.curves.push( curve.toJSON() ); } return data; }, fromJSON: function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.autoClose = json.autoClose; this.curves = []; for ( var i = 0, l = json.curves.length; i < l; i ++ ) { var curve = json.curves[ i ]; this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Creates free form 2d path using series of points, lines or curves. **/ function Path( points ) { CurvePath.call( this ); this.type = "Path"; this.currentPoint = new Vector2(); if ( points ) { this.setFromPoints( points ); } } Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { constructor: Path, setFromPoints: function ( points ) { this.moveTo( points[ 0 ].x, points[ 0 ].y ); for ( var i = 1, l = points.length; i < l; i ++ ) { this.lineTo( points[ i ].x, points[ i ].y ); } }, moveTo: function ( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? }, lineTo: function ( x, y ) { var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { var curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { var curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, splineThru: function ( pts /*Array of Vector*/ ) { var npts = [ this.currentPoint.clone() ].concat( pts ); var curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); }, arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); }, absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }, ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); }, absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join var firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); var lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); }, copy: function ( source ) { CurvePath.prototype.copy.call( this, source ); this.currentPoint.copy( source.currentPoint ); return this; }, toJSON: function () { var data = CurvePath.prototype.toJSON.call( this ); data.currentPoint = this.currentPoint.toArray(); return data; }, fromJSON: function ( json ) { CurvePath.prototype.fromJSON.call( this, json ); this.currentPoint.fromArray( json.currentPoint ); return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Defines a 2d shape plane using paths. **/ // STEP 1 Create a path. // STEP 2 Turn path into shape. // STEP 3 ExtrudeGeometry takes in Shape/Shapes // STEP 3a - Extract points from each shape, turn to vertices // STEP 3b - Triangulate each shape, add faces. function Shape( points ) { Path.call( this, points ); this.uuid = _Math.generateUUID(); this.type = "Shape"; this.holes = []; } Shape.prototype = Object.assign( Object.create( Path.prototype ), { constructor: Shape, getPointsHoles: function ( divisions ) { var holesPts = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; }, // get points of shape and holes (keypoints based on segments parameter) extractPoints: function ( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; }, copy: function ( source ) { Path.prototype.copy.call( this, source ); this.holes = []; for ( var i = 0, l = source.holes.length; i < l; i ++ ) { var hole = source.holes[ i ]; this.holes.push( hole.clone() ); } return this; }, toJSON: function () { var data = Path.prototype.toJSON.call( this ); data.uuid = this.uuid; data.holes = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { var hole = this.holes[ i ]; data.holes.push( hole.toJSON() ); } return data; }, fromJSON: function ( json ) { Path.prototype.fromJSON.call( this, json ); this.uuid = json.uuid; this.holes = []; for ( var i = 0, l = json.holes.length; i < l; i ++ ) { var hole = json.holes[ i ]; this.holes.push( new Path().fromJSON( hole ) ); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Light( color, intensity ) { Object3D.call( this ); this.type = "Light"; this.color = new Color( color ); this.intensity = intensity !== undefined ? intensity : 1; this.receiveShadow = undefined; } Light.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Light, isLight: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.color.copy( source.color ); this.intensity = source.intensity; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function HemisphereLight( skyColor, groundColor, intensity ) { Light.call( this, skyColor, intensity ); this.type = "HemisphereLight"; this.castShadow = undefined; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: HemisphereLight, isHemisphereLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.groundColor.copy( source.groundColor ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LightShadow( camera ) { this.camera = camera; this.bias = 0; this.radius = 1; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.matrix = new Matrix4(); } Object.assign( LightShadow.prototype, { copy: function ( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; }, clone: function () { return new this.constructor().copy( this ); }, toJSON: function () { var object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function SpotLightShadow() { LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); } SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: SpotLightShadow, isSpotLightShadow: true, update: function ( light ) { var camera = this.camera; var fov = _Math.RAD2DEG * 2 * light.angle; var aspect = this.mapSize.width / this.mapSize.height; var far = light.distance || camera.far; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function SpotLight( color, intensity, distance, angle, penumbra, decay ) { Light.call( this, color, intensity ); this.type = "SpotLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf return this.intensity * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf this.intensity = power / Math.PI; } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: SpotLight, isSpotLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PointLight( color, intensity, distance, decay ) { Light.call( this, color, intensity ); this.type = "PointLight"; Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf return this.intensity * 4 * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf this.intensity = power / ( 4 * Math.PI ); } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); } PointLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: PointLight, isPointLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function DirectionalLightShadow( ) { LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: DirectionalLightShadow } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function DirectionalLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "DirectionalLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: DirectionalLight, isDirectionalLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function AmbientLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "AmbientLight"; this.castShadow = undefined; } AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: AmbientLight, isAmbientLight: true } ); /** * @author abelnation / http://github.com/abelnation */ function RectAreaLight( color, intensity, width, height ) { Light.call( this, color, intensity ); this.type = "RectAreaLight"; this.width = ( width !== undefined ) ? width : 10; this.height = ( height !== undefined ) ? height : 10; } RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: RectAreaLight, isRectAreaLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.width = source.width; this.height = source.height; return this; }, toJSON: function ( meta ) { var data = Light.prototype.toJSON.call( this, meta ); data.object.width = this.width; data.object.height = this.height; return data; } } ); /** * * A Track that interpolates Strings * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function StringKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: StringKeyframeTrack, ValueTypeName: "string", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined } ); /** * * A Track of Boolean keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function BooleanKeyframeTrack( name, times, values ) { KeyframeTrack.call( this, name, times, values ); } BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: BooleanKeyframeTrack, ValueTypeName: "bool", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined // Note: Actually this track could have a optimized / compressed // representation of a single value and a custom interpolant that // computes "firstValue ^ isOdd( index )". } ); /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * * @author tschw */ function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; } Object.assign( Interpolant.prototype, { evaluate: function ( t ) { var pp = this.parameterPositions, i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { var right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( var giveUpAt = i1 + 2; ; ) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t, t0 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? var t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( var giveUpAt = i1 - 2; ; ) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { var mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t0, t ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); }, settings: null, // optional, subclass-specific settings structure // Note: The indirection allows central control of many interpolants. // --- Protected interface DefaultSettings_: {}, getSettings_: function () { return this.settings || this.DefaultSettings_; }, copySampleValue_: function ( index ) { // copies a sample value to the result buffer var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; }, // Template methods for derived classes: interpolate_: function ( /* i1, t0, t, t1 */ ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer }, intervalChanged_: function ( /* i1, t0, t1 */ ) { // empty } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( Interpolant.prototype, { //( 0, t, t0 ), returns this.resultBuffer beforeStart_: Interpolant.prototype.copySampleValue_, //( N-1, tN-1, t ), returns this.resultBuffer afterEnd_: Interpolant.prototype.copySampleValue_, } ); /** * Spherical linear unit quaternion interpolant. * * @author tschw */ function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: QuaternionLinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = i1 * stride, alpha = ( t - t0 ) / ( t1 - t0 ); for ( var end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } ); /** * * A Track of quaternion keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function QuaternionKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: QuaternionKeyframeTrack, ValueTypeName: "quaternion", // ValueBufferType is inherited DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodLinear: function ( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: undefined // not yet implemented } ); /** * * A Track of keyframe values that represent color. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function ColorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: ColorKeyframeTrack, ValueTypeName: "color" // ValueBufferType is inherited // DefaultInterpolation is inherited // Note: Very basic implementation and nothing special yet. // However, this is the place for color space parameterization. } ); /** * * A Track of numeric keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function NumberKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: NumberKeyframeTrack, ValueTypeName: "number" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. * * @author tschw */ function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = - 0; this._offsetPrev = - 0; this._weightNext = - 0; this._offsetNext = - 0; } CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: CubicInterpolant, DefaultSettings_: { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }, intervalChanged_: function ( i1, t0, t1 ) { var pp = this.parameterPositions, iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } var halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; }, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials var sP = - wP * ppp + 2 * wP * pp - wP * p; var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; var sN = wN * ppp - wN * pp; // combine data linearly for ( var i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } ); /** * @author tschw */ function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: LinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } ); /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. * * @author tschw */ function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: DiscreteInterpolant, interpolate_: function ( i1 /*, t0, t, t1 */ ) { return this.copySampleValue_( i1 - 1 ); } } ); /** * @author tschw * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ var AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function ( array, from, to ) { if ( AnimationUtils.isTypedArray( array ) ) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } return array.slice( from, to ); }, // converts an array to a specific type convertArray: function ( array, type, forceClone ) { if ( ! array || // let "undefined" and "null" pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === "number" ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array }, isTypedArray: function ( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); }, // returns an array by which times and values can be sorted getKeyframeOrder: function ( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } var n = times.length; var result = new Array( n ); for ( var i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function ( values, stride, order ) { var nValues = values.length; var result = new values.constructor( nValues ); for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { var srcOffset = order[ i ] * stride; for ( var j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { var i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data var value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } }; /** * * A timed sequence of keyframes for a specific property. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function KeyframeTrack( name, times, values, interpolation ) { if ( name === undefined ) throw new Error( "THREE.KeyframeTrack: track name is undefined" ); if ( times === undefined || times.length === 0 ) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name ); this.name = name; this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); this.validate(); this.optimize(); } // Static methods: Object.assign( KeyframeTrack, { // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): parse: function ( json ) { if ( json.type === undefined ) { throw new Error( "THREE.KeyframeTrack: track type undefined, can not parse" ); } var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { var times = [], values = []; AnimationUtils.flattenJSON( json.keys, times, values, "value" ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } }, toJSON: function ( track ) { var trackType = track.constructor; var json; // derived classes can define a static toJSON method if ( trackType.toJSON !== undefined ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray( track.times, Array ), "values": AnimationUtils.convertArray( track.values, Array ) }; var interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; }, _getTrackTypeForValueTypeName: function ( typeName ) { switch ( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "THREE.KeyframeTrack: Unsupported typeName: " + typeName ); } } ); Object.assign( KeyframeTrack.prototype, { constructor: KeyframeTrack, TimeBufferType: Float32Array, ValueBufferType: Float32Array, DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodDiscrete: function ( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodLinear: function ( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: function ( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); }, setInterpolation: function ( interpolation ) { var factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { var message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( "THREE.KeyframeTrack:", message ); return; } this.createInterpolant = factoryMethod; }, getInterpolation: function () { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } }, getValueSize: function () { return this.values.length / this.times.length; }, // move all keyframes either forwards or backwards in time shift: function ( timeOffset ) { if ( timeOffset !== 0.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; }, // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale: function ( timeScale ) { if ( timeScale !== 1.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; }, // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim: function ( startTime, endTime ) { var times = this.times, nKeys = times.length, from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) { ++ from; } while ( to !== - 1 && times[ to ] > endTime ) { -- to; } ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; var stride = this.getValueSize(); this.times = AnimationUtils.arraySlice( times, from, to ); this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); } return this; }, // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate: function () { var valid = true; var valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "THREE.KeyframeTrack: Invalid value size in track.", this ); valid = false; } var times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "THREE.KeyframeTrack: Track is empty.", this ); valid = false; } var prevTime = null; for ( var i = 0; i !== nKeys; i ++ ) { var currTime = times[ i ]; if ( typeof currTime === "number" && isNaN( currTime ) ) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( AnimationUtils.isTypedArray( values ) ) { for ( var i = 0, n = values.length; i !== n; ++ i ) { var value = values[ i ]; if ( isNaN( value ) ) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value ); valid = false; break; } } } } return valid; }, // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize: function () { var times = this.times, values = this.values, stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, writeIndex = 1, lastIndex = times.length - 1; for ( var i = 1; i < lastIndex; ++ i ) { var keep = false; var time = times[ i ]; var timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors var offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( var j = 0; j !== stride; ++ j ) { var value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; var readOffset = i * stride, writeOffset = writeIndex * stride; for ( var j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); } return this; } } ); /** * * A Track of vectored keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function VectorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: VectorKeyframeTrack, ValueTypeName: "vector" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * * Reusable set of Tracks that represent an animation. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ function AnimationClip( name, duration, tracks ) { this.name = name; this.tracks = tracks; this.duration = ( duration !== undefined ) ? duration : - 1; this.uuid = _Math.generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } this.optimize(); } Object.assign( AnimationClip, { parse: function ( json ) { var tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); } return new AnimationClip( json.name, json.duration, tracks ); }, toJSON: function ( clip ) { var tracks = [], clipTracks = clip.tracks; var json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid }; for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; }, CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { var numMorphTargets = morphTargetSequence.length; var tracks = []; for ( var i = 0; i < numMorphTargets; i ++ ) { var times = []; var values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); var order = AnimationUtils.getKeyframeOrder( times ); times = AnimationUtils.sortedArray( times, 1, order ); values = AnimationUtils.sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[ i ].name + "]", times, values ).scale( 1.0 / fps ) ); } return new AnimationClip( name, - 1, tracks ); }, findByName: function ( objectOrClipArray, name ) { var clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { var o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( var i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; }, CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { var animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 var pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = morphTargets[ i ]; var parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { var name = parts[ 1 ]; var animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } var clips = []; for ( var name in animationToMorphTargets ) { clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; }, // parse the animation.hierarchy format parseAnimation: function ( animation, bones ) { if ( ! animation ) { console.error( "THREE.AnimationClip: No animation in JSONLoader data." ); return null; } var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { var times = []; var values = []; AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; var tracks = []; var clipName = animation.name || "default"; // automatic length determination in AnimationClip. var duration = animation.length || - 1; var fps = animation.fps || 30; var hierarchyTracks = animation.hierarchy || []; for ( var h = 0; h < hierarchyTracks.length; h ++ ) { var animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets if ( animationKeys[ 0 ].morphTargets ) { // figure out all morph targets used in this track var morphTargetNames = {}; for ( var k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[ k ].morphTargets ) { for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( var morphTargetName in morphTargetNames ) { var times = []; var values = []; for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { var animationKey = animationKeys[ k ]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values ) ); } duration = morphTargetNames.length * ( fps || 1.0 ); } else { // ...assume skeletal animation var boneName = ".bones[" + bones[ h ].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks ); } } if ( tracks.length === 0 ) { return null; } var clip = new AnimationClip( clipName, duration, tracks ); return clip; } } ); Object.assign( AnimationClip.prototype, { resetDuration: function () { var tracks = this.tracks, duration = 0; for ( var i = 0, n = tracks.length; i !== n; ++ i ) { var track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; }, trim: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; }, optimize: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function MaterialLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.textures = {}; } Object.assign( MaterialLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, setTextures: function ( value ) { this.textures = value; }, parse: function ( json ) { var textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( "THREE.MaterialLoader: Undefined texture", name ); } return textures[ name ]; } var material = new Materials[ json.type ](); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.rotation !== undefined ) material.rotation = json.rotation; if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; if ( json.scale !== undefined ) material.scale = json.scale; if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; if ( json.skinning !== undefined ) material.skinning = json.skinning; if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; if ( json.dithering !== undefined ) material.dithering = json.dithering; if ( json.visible !== undefined ) material.visible = json.visible; if ( json.userData !== undefined ) material.userData = json.userData; // Deprecated if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.alphaMap !== undefined ) { material.alphaMap = getTexture( json.alphaMap ); material.transparent = true; } if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalScale !== undefined ) { var normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); return material; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferGeometryLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( BufferGeometryLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, parse: function ( json ) { var geometry = new BufferGeometry(); var index = json.data.index; if ( index !== undefined ) { var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } var attributes = json.data.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); } var groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( var i = 0, n = groups.length; i !== n; ++ i ) { var group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } var boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { var center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } return geometry; } } ); var TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, // Workaround for IE11 pre KB2929437. See #11440 Uint8ClampedArray: typeof Uint8ClampedArray !== "undefined" ? Uint8ClampedArray : Uint8Array, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; /** * @author alteredq / http://alteredqualia.com/ */ function Loader() {} Loader.Handlers = { handlers: [], add: function ( regex, loader ) { this.handlers.push( regex, loader ); }, get: function ( file ) { var handlers = this.handlers; for ( var i = 0, l = handlers.length; i < l; i += 2 ) { var regex = handlers[ i ]; var loader = handlers[ i + 1 ]; if ( regex.test( file ) ) { return loader; } } return null; } }; Object.assign( Loader.prototype, { crossOrigin: undefined, onLoadStart: function () {}, onLoadProgress: function () {}, onLoadComplete: function () {}, initMaterials: function ( materials, texturePath, crossOrigin ) { var array = []; for ( var i = 0; i < materials.length; ++ i ) { array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } return array; }, createMaterial: ( function () { var BlendingMode = { NoBlending: NoBlending, NormalBlending: NormalBlending, AdditiveBlending: AdditiveBlending, SubtractiveBlending: SubtractiveBlending, MultiplyBlending: MultiplyBlending, CustomBlending: CustomBlending }; var color = new Color(); var textureLoader = new TextureLoader(); var materialLoader = new MaterialLoader(); return function createMaterial( m, texturePath, crossOrigin ) { // convert from old material format var textures = {}; function loadTexture( path, repeat, offset, wrap, anisotropy ) { var fullPath = texturePath + path; var loader = Loader.Handlers.get( fullPath ); var texture; if ( loader !== null ) { texture = loader.load( fullPath ); } else { textureLoader.setCrossOrigin( crossOrigin ); texture = textureLoader.load( fullPath ); } if ( repeat !== undefined ) { texture.repeat.fromArray( repeat ); if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; } if ( offset !== undefined ) { texture.offset.fromArray( offset ); } if ( wrap !== undefined ) { if ( wrap[ 0 ] === "repeat" ) texture.wrapS = RepeatWrapping; if ( wrap[ 0 ] === "mirror" ) texture.wrapS = MirroredRepeatWrapping; if ( wrap[ 1 ] === "repeat" ) texture.wrapT = RepeatWrapping; if ( wrap[ 1 ] === "mirror" ) texture.wrapT = MirroredRepeatWrapping; } if ( anisotropy !== undefined ) { texture.anisotropy = anisotropy; } var uuid = _Math.generateUUID(); textures[ uuid ] = texture; return uuid; } // var json = { uuid: _Math.generateUUID(), type: "MeshLambertMaterial" }; for ( var name in m ) { var value = m[ name ]; switch ( name ) { case "DbgColor": case "DbgIndex": case "opticalDensity": case "illumination": break; case "DbgName": json.name = value; break; case "blending": json.blending = BlendingMode[ value ]; break; case "colorAmbient": case "mapAmbient": console.warn( "THREE.Loader.createMaterial:", name, "is no longer supported." ); break; case "colorDiffuse": json.color = color.fromArray( value ).getHex(); break; case "colorSpecular": json.specular = color.fromArray( value ).getHex(); break; case "colorEmissive": json.emissive = color.fromArray( value ).getHex(); break; case "specularCoef": json.shininess = value; break; case "shading": if ( value.toLowerCase() === "basic" ) json.type = "MeshBasicMaterial"; if ( value.toLowerCase() === "phong" ) json.type = "MeshPhongMaterial"; if ( value.toLowerCase() === "standard" ) json.type = "MeshStandardMaterial"; break; case "mapDiffuse": json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); break; case "mapDiffuseRepeat": case "mapDiffuseOffset": case "mapDiffuseWrap": case "mapDiffuseAnisotropy": break; case "mapEmissive": json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); break; case "mapEmissiveRepeat": case "mapEmissiveOffset": case "mapEmissiveWrap": case "mapEmissiveAnisotropy": break; case "mapLight": json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); break; case "mapLightRepeat": case "mapLightOffset": case "mapLightWrap": case "mapLightAnisotropy": break; case "mapAO": json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); break; case "mapAORepeat": case "mapAOOffset": case "mapAOWrap": case "mapAOAnisotropy": break; case "mapBump": json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); break; case "mapBumpScale": json.bumpScale = value; break; case "mapBumpRepeat": case "mapBumpOffset": case "mapBumpWrap": case "mapBumpAnisotropy": break; case "mapNormal": json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); break; case "mapNormalFactor": json.normalScale = value; break; case "mapNormalRepeat": case "mapNormalOffset": case "mapNormalWrap": case "mapNormalAnisotropy": break; case "mapSpecular": json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); break; case "mapSpecularRepeat": case "mapSpecularOffset": case "mapSpecularWrap": case "mapSpecularAnisotropy": break; case "mapMetalness": json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); break; case "mapMetalnessRepeat": case "mapMetalnessOffset": case "mapMetalnessWrap": case "mapMetalnessAnisotropy": break; case "mapRoughness": json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); break; case "mapRoughnessRepeat": case "mapRoughnessOffset": case "mapRoughnessWrap": case "mapRoughnessAnisotropy": break; case "mapAlpha": json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); break; case "mapAlphaRepeat": case "mapAlphaOffset": case "mapAlphaWrap": case "mapAlphaAnisotropy": break; case "flipSided": json.side = BackSide; break; case "doubleSided": json.side = DoubleSide; break; case "transparency": console.warn( "THREE.Loader.createMaterial: transparency has been renamed to opacity" ); json.opacity = value; break; case "depthTest": case "depthWrite": case "colorWrite": case "opacity": case "reflectivity": case "transparent": case "visible": case "wireframe": json[ name ] = value; break; case "vertexColors": if ( value === true ) json.vertexColors = VertexColors; if ( value === "face" ) json.vertexColors = FaceColors; break; default: console.error( "THREE.Loader.createMaterial: Unsupported", name, value ); break; } } if ( json.type === "MeshBasicMaterial" ) delete json.emissive; if ( json.type !== "MeshPhongMaterial" ) delete json.specular; if ( json.opacity < 1 ) json.transparent = true; materialLoader.setTextures( textures ); return materialLoader.parse( json ); }; } )() } ); /** * @author Don McCurdy / https://www.donmccurdy.com */ var LoaderUtils = { decodeText: function ( array ) { if ( typeof TextDecoder !== "undefined" ) { return new TextDecoder().decode( array ); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. var s = ""; for ( var i = 0, il = array.length; i < il; i ++ ) { // Implicitly assumes little-endian. s += String.fromCharCode( array[ i ] ); } // Merges multi-byte utf-8 characters. return decodeURIComponent( escape( s ) ); }, extractUrlBase: function ( url ) { var index = url.lastIndexOf( "/" ); if ( index === - 1 ) return "./"; return url.substr( 0, index + 1 ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function JSONLoader( manager ) { if ( typeof manager === "boolean" ) { console.warn( "THREE.JSONLoader: showStatus parameter has been removed from constructor." ); manager = undefined; } this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.withCredentials = false; } Object.assign( JSONLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : LoaderUtils.extractUrlBase( url ); var loader = new FileLoader( this.manager ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { var json = JSON.parse( text ); var metadata = json.metadata; if ( metadata !== undefined ) { var type = metadata.type; if ( type !== undefined ) { if ( type.toLowerCase() === "object" ) { console.error( "THREE.JSONLoader: " + url + " should be loaded with THREE.ObjectLoader instead." ); return; } } } var object = scope.parse( json, texturePath ); onLoad( object.geometry, object.materials ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, parse: ( function () { function parseModel( json, geometry ) { function isBitSet( value, position ) { return value & ( 1 << position ); } var i, j, fi, offset, zLength, colorIndex, normalIndex, uvIndex, materialIndex, type, isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor, vertex, face, faceA, faceB, hex, normal, uvLayer, uv, u, v, faces = json.faces, vertices = json.vertices, normals = json.normals, colors = json.colors, scale = json.scale, nUvLayers = 0; if ( json.uvs !== undefined ) { // disregard empty arrays for ( i = 0; i < json.uvs.length; i ++ ) { if ( json.uvs[ i ].length ) nUvLayers ++; } for ( i = 0; i < nUvLayers; i ++ ) { geometry.faceVertexUvs[ i ] = []; } } offset = 0; zLength = vertices.length; while ( offset < zLength ) { vertex = new Vector3(); vertex.x = vertices[ offset ++ ] * scale; vertex.y = vertices[ offset ++ ] * scale; vertex.z = vertices[ offset ++ ] * scale; geometry.vertices.push( vertex ); } offset = 0; zLength = faces.length; while ( offset < zLength ) { type = faces[ offset ++ ]; isQuad = isBitSet( type, 0 ); hasMaterial = isBitSet( type, 1 ); hasFaceVertexUv = isBitSet( type, 3 ); hasFaceNormal = isBitSet( type, 4 ); hasFaceVertexNormal = isBitSet( type, 5 ); hasFaceColor = isBitSet( type, 6 ); hasFaceVertexColor = isBitSet( type, 7 ); // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); if ( isQuad ) { faceA = new Face3(); faceA.a = faces[ offset ]; faceA.b = faces[ offset + 1 ]; faceA.c = faces[ offset + 3 ]; faceB = new Face3(); faceB.a = faces[ offset + 1 ]; faceB.b = faces[ offset + 2 ]; faceB.c = faces[ offset + 3 ]; offset += 4; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; faceA.materialIndex = materialIndex; faceB.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; geometry.faceVertexUvs[ i ][ fi + 1 ] = []; for ( j = 0; j < 4; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; faceA.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); faceB.normal.copy( faceA.normal ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 4; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); if ( i !== 2 ) faceA.vertexNormals.push( normal ); if ( i !== 0 ) faceB.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; faceA.color.setHex( hex ); faceB.color.setHex( hex ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 4; i ++ ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); } } geometry.faces.push( faceA ); geometry.faces.push( faceB ); } else { face = new Face3(); face.a = faces[ offset ++ ]; face.b = faces[ offset ++ ]; face.c = faces[ offset ++ ]; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; face.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; for ( j = 0; j < 3; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); geometry.faceVertexUvs[ i ][ fi ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; face.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 3; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); face.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; face.color.setHex( colors[ colorIndex ] ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 3; i ++ ) { colorIndex = faces[ offset ++ ]; face.vertexColors.push( new Color( colors[ colorIndex ] ) ); } } geometry.faces.push( face ); } } } function parseSkin( json, geometry ) { var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; if ( json.skinWeights ) { for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { var x = json.skinWeights[ i ]; var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; geometry.skinWeights.push( new Vector4( x, y, z, w ) ); } } if ( json.skinIndices ) { for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { var a = json.skinIndices[ i ]; var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; geometry.skinIndices.push( new Vector4( a, b, c, d ) ); } } geometry.bones = json.bones; if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { console.warn( "When skinning, number of vertices (" + geometry.vertices.length + "), skinIndices (" + geometry.skinIndices.length + "), and skinWeights (" + geometry.skinWeights.length + ") should match." ); } } function parseMorphing( json, geometry ) { var scale = json.scale; if ( json.morphTargets !== undefined ) { for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { geometry.morphTargets[ i ] = {}; geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; geometry.morphTargets[ i ].vertices = []; var dstVertices = geometry.morphTargets[ i ].vertices; var srcVertices = json.morphTargets[ i ].vertices; for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { var vertex = new Vector3(); vertex.x = srcVertices[ v ] * scale; vertex.y = srcVertices[ v + 1 ] * scale; vertex.z = srcVertices[ v + 2 ] * scale; dstVertices.push( vertex ); } } } if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { console.warn( "THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors." ); var faces = geometry.faces; var morphColors = json.morphColors[ 0 ].colors; for ( var i = 0, l = faces.length; i < l; i ++ ) { faces[ i ].color.fromArray( morphColors, i * 3 ); } } } function parseAnimations( json, geometry ) { var outputAnimations = []; // parse old style Bone/Hierarchy animations var animations = []; if ( json.animation !== undefined ) { animations.push( json.animation ); } if ( json.animations !== undefined ) { if ( json.animations.length ) { animations = animations.concat( json.animations ); } else { animations.push( json.animations ); } } for ( var i = 0; i < animations.length; i ++ ) { var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); if ( clip ) outputAnimations.push( clip ); } // parse implicit morph animations if ( geometry.morphTargets ) { // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); outputAnimations = outputAnimations.concat( morphAnimationClips ); } if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; } return function parse( json, texturePath ) { if ( json.data !== undefined ) { // Geometry 4.0 spec json = json.data; } if ( json.scale !== undefined ) { json.scale = 1.0 / json.scale; } else { json.scale = 1.0; } var geometry = new Geometry(); parseModel( json, geometry ); parseSkin( json, geometry ); parseMorphing( json, geometry ); parseAnimations( json, geometry ); geometry.computeFaceNormals(); geometry.computeBoundingSphere(); if ( json.materials === undefined || json.materials.length === 0 ) { return { geometry: geometry }; } else { var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); return { geometry: geometry, materials: materials }; } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function ObjectLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.texturePath = ""; } Object.assign( ObjectLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( this.texturePath === "" ) { this.texturePath = url.substring( 0, url.lastIndexOf( "/" ) + 1 ); } var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { var json = null; try { json = JSON.parse( text ); } catch ( error ) { if ( onError !== undefined ) onError( error ); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message ); return; } var metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { console.error( "THREE.ObjectLoader: Can"t load " + url + ". Use THREE.JSONLoader instead." ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; return this; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, parse: function ( json, onLoad ) { var shapes = this.parseShape( json.shapes ); var geometries = this.parseGeometries( json.geometries, shapes ); var images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); var textures = this.parseTextures( json.textures, images ); var materials = this.parseMaterials( json.materials, textures ); var object = this.parseObject( json.object, geometries, materials ); if ( json.animations ) { object.animations = this.parseAnimations( json.animations ); } if ( json.images === undefined || json.images.length === 0 ) { if ( onLoad !== undefined ) onLoad( object ); } return object; }, parseShape: function ( json ) { var shapes = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var shape = new Shape().fromJSON( json[ i ] ); shapes[ shape.uuid ] = shape; } } return shapes; }, parseGeometries: function ( json, shapes ) { var geometries = {}; if ( json !== undefined ) { var geometryLoader = new JSONLoader(); var bufferGeometryLoader = new BufferGeometryLoader(); for ( var i = 0, l = json.length; i < l; i ++ ) { var geometry; var data = json[ i ]; switch ( data.type ) { case "PlaneGeometry": case "PlaneBufferGeometry": geometry = new Geometries[ data.type ]( data.width, data.height, data.widthSegments, data.heightSegments ); break; case "BoxGeometry": case "BoxBufferGeometry": case "CubeGeometry": // backwards compatible geometry = new Geometries[ data.type ]( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); break; case "CircleGeometry": case "CircleBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.segments, data.thetaStart, data.thetaLength ); break; case "CylinderGeometry": case "CylinderBufferGeometry": geometry = new Geometries[ data.type ]( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "ConeGeometry": case "ConeBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "SphereGeometry": case "SphereBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); break; case "DodecahedronGeometry": case "DodecahedronBufferGeometry": case "IcosahedronGeometry": case "IcosahedronBufferGeometry": case "OctahedronGeometry": case "OctahedronBufferGeometry": case "TetrahedronGeometry": case "TetrahedronBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.detail ); break; case "RingGeometry": case "RingBufferGeometry": geometry = new Geometries[ data.type ]( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); break; case "TorusGeometry": case "TorusBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); break; case "TorusKnotGeometry": case "TorusKnotBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); break; case "LatheGeometry": case "LatheBufferGeometry": geometry = new Geometries[ data.type ]( data.points, data.segments, data.phiStart, data.phiLength ); break; case "PolyhedronGeometry": case "PolyhedronBufferGeometry": geometry = new Geometries[ data.type ]( data.vertices, data.indices, data.radius, data.details ); break; case "ShapeGeometry": case "ShapeBufferGeometry": var geometryShapes = []; for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { var shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } geometry = new Geometries[ data.type ]( geometryShapes, data.curveSegments ); break; case "ExtrudeGeometry": case "ExtrudeBufferGeometry": var geometryShapes = []; for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { var shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } var extrudePath = data.options.extrudePath; if ( extrudePath !== undefined ) { data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); } geometry = new Geometries[ data.type ]( geometryShapes, data.options ); break; case "BufferGeometry": geometry = bufferGeometryLoader.parse( data ); break; case "Geometry": geometry = geometryLoader.parse( data, this.texturePath ).geometry; break; default: console.warn( "THREE.ObjectLoader: Unsupported geometry type "" + data.type + """ ); continue; } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; geometries[ data.uuid ] = geometry; } } return geometries; }, parseMaterials: function ( json, textures ) { var materials = {}; if ( json !== undefined ) { var loader = new MaterialLoader(); loader.setTextures( textures ); for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.type === "MultiMaterial" ) { // Deprecated var array = []; for ( var j = 0; j < data.materials.length; j ++ ) { array.push( loader.parse( data.materials[ j ] ) ); } materials[ data.uuid ] = array; } else { materials[ data.uuid ] = loader.parse( data ); } } } return materials; }, parseAnimations: function ( json ) { var animations = []; for ( var i = 0; i < json.length; i ++ ) { var data = json[ i ]; var clip = AnimationClip.parse( data ); if ( data.uuid !== undefined ) clip.uuid = data.uuid; animations.push( clip ); } return animations; }, parseImages: function ( json, onLoad ) { var scope = this; var images = {}; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); } if ( json !== undefined && json.length > 0 ) { var manager = new LoadingManager( onLoad ); var loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( var i = 0, l = json.length; i < l; i ++ ) { var image = json[ i ]; var path = /^(//)|([a-z]+:(//)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; images[ image.uuid ] = loadImage( path ); } } return images; }, parseTextures: function ( json, images ) { function parseConstant( value, type ) { if ( typeof value === "number" ) return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value ); return type[ value ]; } var textures = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.image === undefined ) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined image", data.image ); } var texture = new Texture( images[ data.image ] ); texture.needsUpdate = true; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.center !== undefined ) texture.center.fromArray( data.center ); if ( data.rotation !== undefined ) texture.rotation = data.rotation; if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); } if ( data.format !== undefined ) texture.format = data.format; if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; textures[ data.uuid ] = texture; } } return textures; }, parseObject: function ( data, geometries, materials ) { var object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined geometry", name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( Array.isArray( name ) ) { var array = []; for ( var i = 0, l = name.length; i < l; i ++ ) { var uuid = name[ i ]; if ( materials[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", uuid ); } array.push( materials[ uuid ] ); } return array; } if ( materials[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", name ); } return materials[ name ]; } switch ( data.type ) { case "Scene": object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } } if ( data.fog !== undefined ) { if ( data.fog.type === "Fog" ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === "FogExp2" ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "AmbientLight": object = new AmbientLight( data.color, data.intensity ); break; case "DirectionalLight": object = new DirectionalLight( data.color, data.intensity ); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case "SkinnedMesh": console.warn( "THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet." ); case "Mesh": var geometry = getGeometry( data.geometry ); var material = getMaterial( data.material ); if ( geometry.bones && geometry.bones.length > 0 ) { object = new SkinnedMesh( geometry, material ); } else { object = new Mesh( geometry, material ); } break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); break; case "LineLoop": object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineSegments": object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "PointCloud": case "Points": object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "Sprite": object = new Sprite( getMaterial( data.material ) ); break; case "Group": object = new Group(); break; default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { object.matrix.fromArray( data.matrix ); if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.children !== undefined ) { var children = data.children; for ( var i = 0; i < children.length; i ++ ) { object.add( this.parseObject( children[ i ], geometries, materials ) ); } } if ( data.type === "LOD" ) { var levels = data.levels; for ( var l = 0; l < levels.length; l ++ ) { var level = levels[ l ]; var child = object.getObjectByProperty( "uuid", level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance ); } } } return object; } } ); var TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, SphericalReflectionMapping: SphericalReflectionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; var TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; var TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipMapNearestFilter: NearestMipMapNearestFilter, NearestMipMapLinearFilter: NearestMipMapLinearFilter, LinearFilter: LinearFilter, LinearMipMapNearestFilter: LinearMipMapNearestFilter, LinearMipMapLinearFilter: LinearMipMapLinearFilter }; /** * @author thespite / http://clicktorelease.com/ */ function ImageBitmapLoader( manager ) { if ( typeof createImageBitmap === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported." ); } if ( typeof fetch === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: fetch() not supported." ); } this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.options = undefined; } ImageBitmapLoader.prototype = { constructor: ImageBitmapLoader, setOptions: function setOptions( options ) { this.options = options; return this; }, load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } fetch( url ).then( function ( res ) { return res.blob(); } ).then( function ( blob ) { return createImageBitmap( blob, scope.options ); } ).then( function ( imageBitmap ) { Cache.add( url, imageBitmap ); if ( onLoad ) onLoad( imageBitmap ); scope.manager.itemEnd( url ); } ).catch( function ( e ) { if ( onError ) onError( e ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); }, setCrossOrigin: function ( /* value */ ) { return this; }, setPath: function ( value ) { this.path = value; return this; } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" **/ function ShapePath() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } Object.assign( ShapePath.prototype, { moveTo: function ( x, y ) { this.currentPath = new Path(); this.subPaths.push( this.currentPath ); this.currentPath.moveTo( x, y ); }, lineTo: function ( x, y ) { this.currentPath.lineTo( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); }, splineThru: function ( pts ) { this.currentPath.splineThru( pts ); }, toShapes: function ( isCCW, noHoles ) { function toShapesNoHoles( inSubpaths ) { var shapes = []; for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { var tmpPath = inSubpaths[ i ]; var tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { var polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line var inside = false; for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { var edgeLowPt = inPolygon[ p ]; var edgeHighPt = inPolygon[ q ]; var edgeDx = edgeHighPt.x - edgeLowPt.x; var edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } var isClockWise = ShapeUtils.isClockWise; var subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; if ( noHoles === true ) return toShapesNoHoles( subPaths ); var solid, tmpPath, tmpShape, shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); var betterShapeHoles = []; var newShapes = []; var newShapeHoles = []; var mainIdx = 0; var tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( var i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log("cw", i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { var ambiguous = false; var toChange = []; for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { var sho = newShapeHoles[ sIdx ]; for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { var ho = sho[ hIdx ]; var hole_unassigned = true; for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { // console.log("to change: ", toChange); if ( ! ambiguous ) newShapeHoles = betterShapeHoles; } } var tmpHoles; for ( var i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author mrdoob / http://mrdoob.com/ */ function Font( data ) { this.type = "Font"; this.data = data; } Object.assign( Font.prototype, { isFont: true, generateShapes: function ( text, size, divisions ) { if ( size === undefined ) size = 100; if ( divisions === undefined ) divisions = 4; var shapes = []; var paths = createPaths( text, size, divisions, this.data ); for ( var p = 0, pl = paths.length; p < pl; p ++ ) { Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); } return shapes; } } ); function createPaths( text, size, divisions, data ) { var chars = Array.from ? Array.from( text ) : String( text ).split( "" ); // see #13988 var scale = size / data.resolution; var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; var paths = []; var offsetX = 0, offsetY = 0; for ( var i = 0; i < chars.length; i ++ ) { var char = chars[ i ]; if ( char === " " ) { offsetX = 0; offsetY -= line_height; } else { var ret = createPath( char, divisions, scale, offsetX, offsetY, data ); offsetX += ret.offsetX; paths.push( ret.path ); } } return paths; } function createPath( char, divisions, scale, offsetX, offsetY, data ) { var glyph = data.glyphs[ char ] || data.glyphs[ "?" ]; if ( ! glyph ) return; var path = new ShapePath(); var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; if ( glyph.o ) { var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( " " ) ); for ( var i = 0, l = outline.length; i < l; ) { var action = outline[ i ++ ]; switch ( action ) { case "m": // moveTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.moveTo( x, y ); break; case "l": // lineTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.lineTo( x, y ); break; case "q": // quadraticCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); break; case "b": // bezierCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; cpx2 = outline[ i ++ ] * scale + offsetX; cpy2 = outline[ i ++ ] * scale + offsetY; path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); break; } } } return { offsetX: glyph.ha * scale, path: path }; } /** * @author mrdoob / http://mrdoob.com/ */ function FontLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FontLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { var json; try { json = JSON.parse( text ); } catch ( e ) { console.warn( "THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead." ); json = JSON.parse( text.substring( 65, text.length - 2 ) ); } var font = scope.parse( json ); if ( onLoad ) onLoad( font ); }, onProgress, onError ); }, parse: function ( json ) { return new Font( json ); }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ var context; var AudioContext = { getContext: function () { if ( context === undefined ) { context = new ( window.AudioContext || window.webkitAudioContext )(); } return context; }, setContext: function ( value ) { context = value; } }; /** * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function AudioLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( AudioLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var context = AudioContext.getContext(); context.decodeAudioData( buffer, function ( audioBuffer ) { onLoad( audioBuffer ); } ); }, onProgress, onError ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function StereoCamera() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; } Object.assign( StereoCamera.prototype, { update: ( function () { var instance, focus, fov, aspect, near, far, zoom, eyeSep; var eyeRight = new Matrix4(); var eyeLeft = new Matrix4(); return function update( camera ) { var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || aspect !== camera.aspect * this.aspect || near !== camera.near || far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep; if ( needsUpdate ) { instance = this; focus = camera.focus; fov = camera.fov; aspect = camera.aspect * this.aspect; near = camera.near; far = camera.far; zoom = camera.zoom; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ var projectionMatrix = camera.projectionMatrix.clone(); eyeSep = this.eyeSep / 2; var eyeSepOnProjection = eyeSep * near / focus; var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; var xmin, xmax; // translate xOffset eyeLeft.elements[ 12 ] = - eyeSep; eyeRight.elements[ 12 ] = eyeSep; // for left eye xmin = - ymax * aspect + eyeSepOnProjection; xmax = ymax * aspect + eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( projectionMatrix ); // for right eye xmin = - ymax * aspect - eyeSepOnProjection; xmax = ymax * aspect - eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); }; } )() } ); /** * Camera for rendering cube maps * - renders scene into axis-aligned cube * * @author alteredq / http://alteredqualia.com/ */ function CubeCamera( near, far, cubeResolution ) { Object3D.call( this ); this.type = "CubeCamera"; var fov = 90, aspect = 1; var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); this.add( cameraPX ); var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); this.add( cameraNX ); var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); this.add( cameraPY ); var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); this.add( cameraNY ); var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); this.add( cameraPZ ); var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.renderTarget.texture.name = "CubeCamera"; this.update = function ( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); var renderTarget = this.renderTarget; var generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderTarget.activeCubeFace = 0; renderer.render( scene, cameraPX, renderTarget ); renderTarget.activeCubeFace = 1; renderer.render( scene, cameraNX, renderTarget ); renderTarget.activeCubeFace = 2; renderer.render( scene, cameraPY, renderTarget ); renderTarget.activeCubeFace = 3; renderer.render( scene, cameraNY, renderTarget ); renderTarget.activeCubeFace = 4; renderer.render( scene, cameraPZ, renderTarget ); renderTarget.texture.generateMipmaps = generateMipmaps; renderTarget.activeCubeFace = 5; renderer.render( scene, cameraNZ, renderTarget ); renderer.setRenderTarget( null ); }; this.clear = function ( renderer, color, depth, stencil ) { var renderTarget = this.renderTarget; for ( var i = 0; i < 6; i ++ ) { renderTarget.activeCubeFace = i; renderer.setRenderTarget( renderTarget ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( null ); }; } CubeCamera.prototype = Object.create( Object3D.prototype ); CubeCamera.prototype.constructor = CubeCamera; /** * @author mrdoob / http://mrdoob.com/ */ function AudioListener() { Object3D.call( this ); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; } AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: AudioListener, getInput: function () { return this.gain; }, removeFilter: function ( ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } }, getFilter: function () { return this.filter; }, setFilter: function ( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); }, getMasterVolume: function () { return this.gain.gain.value; }, setMasterVolume: function ( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); }, updateMatrixWorld: ( function () { var position = new Vector3(); var quaternion = new Quaternion(); var scale = new Vector3(); var orientation = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); var listener = this.context.listener; var up = this.up; this.matrixWorld.decompose( position, quaternion, scale ); orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); if ( listener.positionX ) { listener.positionX.setValueAtTime( position.x, this.context.currentTime ); listener.positionY.setValueAtTime( position.y, this.context.currentTime ); listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); listener.upX.setValueAtTime( up.x, this.context.currentTime ); listener.upY.setValueAtTime( up.y, this.context.currentTime ); listener.upZ.setValueAtTime( up.z, this.context.currentTime ); } else { listener.setPosition( position.x, position.y, position.z ); listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function Audio( listener ) { Object3D.call( this ); this.type = "Audio"; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.loop = false; this.startTime = 0; this.offset = 0; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.sourceType = "empty"; this.filters = []; } Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Audio, getOutput: function () { return this.gain; }, setNodeSource: function ( audioNode ) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; }, setMediaElementSource: function ( mediaElement ) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource( mediaElement ); this.connect(); return this; }, setBuffer: function ( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = "buffer"; if ( this.autoplay ) this.play(); return this; }, play: function () { if ( this.isPlaying === true ) { console.warn( "THREE.Audio: Audio is already playing." ); return; } if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } var source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.onended = this.onEnded.bind( this ); source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); this.startTime = this.context.currentTime; source.start( this.startTime, this.offset ); this.isPlaying = true; this.source = source; return this.connect(); }, pause: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } if ( this.isPlaying === true ) { this.source.stop(); this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; this.isPlaying = false; } return this; }, stop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.source.stop(); this.offset = 0; this.isPlaying = false; return this; }, connect: function () { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } return this; }, disconnect: function () { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } return this; }, getFilters: function () { return this.filters; }, setFilters: function ( value ) { if ( ! value ) value = []; if ( this.isPlaying === true ) { this.disconnect(); this.filters = value; this.connect(); } else { this.filters = value; } return this; }, getFilter: function () { return this.getFilters()[ 0 ]; }, setFilter: function ( filter ) { return this.setFilters( filter ? [ filter ] : [] ); }, setPlaybackRate: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); } return this; }, getPlaybackRate: function () { return this.playbackRate; }, onEnded: function () { this.isPlaying = false; }, getLoop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return false; } return this.loop; }, setLoop: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; }, getVolume: function () { return this.gain.gain.value; }, setVolume: function ( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PositionalAudio( listener ) { Audio.call( this, listener ); this.panner = this.context.createPanner(); this.panner.connect( this.gain ); } PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { constructor: PositionalAudio, getOutput: function () { return this.panner; }, getRefDistance: function () { return this.panner.refDistance; }, setRefDistance: function ( value ) { this.panner.refDistance = value; }, getRolloffFactor: function () { return this.panner.rolloffFactor; }, setRolloffFactor: function ( value ) { this.panner.rolloffFactor = value; }, getDistanceModel: function () { return this.panner.distanceModel; }, setDistanceModel: function ( value ) { this.panner.distanceModel = value; }, getMaxDistance: function () { return this.panner.maxDistance; }, setMaxDistance: function ( value ) { this.panner.maxDistance = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); position.setFromMatrixPosition( this.matrixWorld ); this.panner.setPosition( position.x, position.y, position.z ); }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function AudioAnalyser( audio, fftSize ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } Object.assign( AudioAnalyser.prototype, { getFrequencyData: function () { this.analyser.getByteFrequencyData( this.data ); return this.data; }, getAverageFrequency: function () { var value = 0, data = this.getFrequencyData(); for ( var i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } ); /** * * Buffered scene graph property that allows weighted accumulation. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function PropertyMixer( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; var bufferType = Float64Array, mixFunction; switch ( typeName ) { case "quaternion": mixFunction = this._slerp; break; case "string": case "bool": bufferType = Array; mixFunction = this._select; break; default: mixFunction = this._lerp; } this.buffer = new bufferType( valueSize * 4 ); // layout: [ incoming | accu0 | accu1 | orig ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property this._mixBufferRegion = mixFunction; this.cumulativeWeight = 0; this.useCount = 0; this.referenceCount = 0; } Object.assign( PropertyMixer.prototype, { // accumulate data in the "incoming" region into "accu" accumulate: function ( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place var buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride, currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( var i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; var mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; }, // apply the state of "accu" to the binding when accus differ apply: function ( accuIndex ) { var stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, binding = this.binding; this.cumulativeWeight = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) var originalValueOffset = stride * 3; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } for ( var i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } }, // remember the state of the bound property and copy it to both accus saveOriginalState: function () { var binding = this.binding; var buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * 3; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } this.cumulativeWeight = 0; }, // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState: function () { var originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); }, // mix functions _select: function ( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( var i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } }, _slerp: function ( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); }, _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { var s = 1 - t; for ( var i = 0; i !== stride; ++ i ) { var j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } } ); /** * * A reference to a real property in the scene graph. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ // Characters [].:/ are reserved for track binding syntax. var RESERVED_CHARS_RE = "\[\]\.:\/"; function Composite( targetGroup, path, optionalParsedPath ) { var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); } Object.assign( Composite.prototype, { getValue: function ( array, offset ) { this.bind(); // bind all binding var firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); }, setValue: function ( array, offset ) { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } }, bind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } }, unbind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } } ); function PropertyBinding( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; this.rootNode = rootNode; } Object.assign( PropertyBinding, { Composite: Composite, create: function ( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } }, /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ sanitizeNodeName: ( function () { var reservedRe = new RegExp( "[" + RESERVED_CHARS_RE + "]", "g" ); return function sanitizeNodeName( name ) { return name.replace( /s/g, "_" ).replace( reservedRe, "" ); }; }() ), parseTrackName: function () { // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. var wordChar = "[^" + RESERVED_CHARS_RE + "]"; var wordCharOrDot = "[^" + RESERVED_CHARS_RE.replace( "\.", "" ) + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. var directoryRe = /((?:WC+[/:])*)/.source.replace( "WC", wordChar ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". var nodeRe = /(WCOD+)?/.source.replace( "WCOD", wordCharOrDot ); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. var objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", wordChar ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. var propertyRe = /.(WC+)(?:[(.+)])?/.source.replace( "WC", wordChar ); var trackRe = new RegExp( "" + "^" + directoryRe + nodeRe + objectRe + propertyRe + "$" ); var supportedObjectNames = [ "material", "materials", "bones" ]; return function parseTrackName( trackName ) { var matches = trackRe.exec( trackName ); if ( ! matches ) { throw new Error( "PropertyBinding: Cannot parse trackName: " + trackName ); } var results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], // required propertyIndex: matches[ 6 ] }; var lastDot = results.nodeName && results.nodeName.lastIndexOf( "." ); if ( lastDot !== undefined && lastDot !== - 1 ) { var objectName = results.nodeName.substring( lastDot + 1 ); // Object names must be checked against a whitelist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) { results.nodeName = results.nodeName.substring( 0, lastDot ); results.objectName = objectName; } } if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName ); } return results; }; }(), findNode: function ( root, nodeName ) { if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { var bone = root.skeleton.getBoneByName( nodeName ); if ( bone !== undefined ) { return bone; } } // search into node subtree. if ( root.children ) { var searchNodeSubtree = function ( children ) { for ( var i = 0; i < children.length; i ++ ) { var childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } var result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; var subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; } } ); Object.assign( PropertyBinding.prototype, { // prototype, continued // these are used to "bind" a nonexistent property _getValue_unavailable: function () {}, _setValue_unavailable: function () {}, BindingType: { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }, Versioning: { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }, GetterByBindingType: [ function getValue_direct( buffer, offset ) { buffer[ offset ] = this.node[ this.propertyName ]; }, function getValue_array( buffer, offset ) { var source = this.resolvedProperty; for ( var i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } }, function getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; }, function getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } ], SetterByBindingTypeAndVersioning: [ [ // Direct function setValue_direct( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; }, function setValue_direct_setNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // EntireArray function setValue_array( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } }, function setValue_array_setNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; }, function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // ArrayElement function setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; }, function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // HasToFromArray function setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); }, function setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; }, function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } ] ], getValue: function getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. }, setValue: function getValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); }, // create getter / setter pair for a property in the scene graph bind: function () { var targetObject = this.node, parsedPath = this.parsedPath, objectName = parsedPath.objectName, propertyName = parsedPath.propertyName, propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.error( "THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found." ); return; } if ( objectName ) { var objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case "materials": if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.materials ) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this ); return; } targetObject = targetObject.material.materials; break; case "bones": if ( ! targetObject.skeleton ) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( var i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; default: if ( targetObject[ objectName ] === undefined ) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property var nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { var nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject ); return; } // determine versioning scheme var versioning = this.Versioning.None; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; this.targetObject = targetObject; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; this.targetObject = targetObject; } // determine how the property gets bound var bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this ); return; } if ( targetObject.geometry.isBufferGeometry ) { if ( ! targetObject.geometry.morphAttributes ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this ); return; } for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) { if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } else { if ( ! targetObject.geometry.morphTargets ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.", this ); return; } for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( Array.isArray( nodeProperty ) ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; }, unbind: function () { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( PropertyBinding.prototype, { // initial state of these methods that calls "bind" _getValue_unbound: PropertyBinding.prototype.getValue, _setValue_unbound: PropertyBinding.prototype.setValue, } ); /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. * * @author tschw */ function AnimationObjectGroup() { this.uuid = _Math.generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite var indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( var i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays var scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } Object.assign( AnimationObjectGroup.prototype, { isAnimationObjectGroup: true, add: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length, knownObject = undefined; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject ) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; }, remove: function () { var objects = this._objects, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region var lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; }, // remove & forget uncache: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object"s place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop var lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; indicesByUUID[ lastObject.uuid ] = index; objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; }, // Internal interface used by befriended PropertyBinding.Composite: subscribe_: function ( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ], bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; var paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { var object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; }, unsubscribe_: function ( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { var paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } } ); /** * * Action provided by AnimationMixer for scheduling clip playback on specific * objects. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw * */ function AnimationAction( mixer, clip, localRoot ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot || null; var tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); var interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( var i = 0; i !== nTracks; ++ i ) { var interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = - 1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } Object.assign( AnimationAction.prototype, { // State & Scheduling play: function () { this._mixer._activateAction( this ); return this; }, stop: function () { this._mixer._deactivateAction( this ); return this.reset(); }, reset: function () { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = - 1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); }, isRunning: function () { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); }, // return true when play has been called isScheduled: function () { return this._mixer._isActiveAction( this ); }, startAt: function ( time ) { this._startTime = time; return this; }, setLoop: function ( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; }, // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight: function ( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); }, // return the weight considering fading and .enabled getEffectiveWeight: function () { return this._effectiveWeight; }, fadeIn: function ( duration ) { return this._scheduleFading( duration, 0, 1 ); }, fadeOut: function ( duration ) { return this._scheduleFading( duration, 1, 0 ); }, crossFadeFrom: function ( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if ( warp ) { var fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; }, crossFadeTo: function ( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); }, stopFading: function () { var weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; }, // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale: function ( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); }, // return the time scale considering warping and .paused getEffectiveTimeScale: function () { return this._effectiveTimeScale; }, setDuration: function ( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); }, syncWith: function ( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); }, halt: function ( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); }, warp: function ( startTimeScale, endTimeScale, duration ) { var mixer = this._mixer, now = mixer.time, interpolant = this._timeScaleInterpolant, timeScale = this.timeScale; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; }, stopWarping: function () { var timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; }, // Object Accessors getMixer: function () { return this._mixer; }, getClip: function () { return this._clip; }, getRoot: function () { return this._localRoot || this._mixer._root; }, // Interna _update: function ( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer if ( ! this.enabled ) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight( time ); return; } var startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action var timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); var clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 var weight = this._updateWeight( time ); if ( weight > 0 ) { var interpolants = this._interpolants; var propertyMixers = this._propertyBindings; for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } }, _updateWeight: function ( time ) { var weight = 0; if ( this.enabled ) { weight = this.weight; var interpolant = this._weightInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; }, _updateTimeScale: function ( time ) { var timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; var interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; }, _updateTime: function ( deltaTime ) { var time = this.time + deltaTime; if ( deltaTime === 0 ) return time; var duration = this._clip.duration, loop = this.loop, loopCount = this._loopCount; if ( loop === LoopOnce ) { if ( loopCount === - 1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else break handle_stop; if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime < 0 ? - 1 : 1 } ); } } else { // repetitive Repeat or PingPong var pingPong = ( loop === LoopPingPong ); if ( loopCount === - 1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around var loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); var pending = this.repetitions - loopCount; if ( pending <= 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime > 0 ? 1 : - 1 } ); } else { // keep running if ( pending === 1 ) { // entering the last round var atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this._mixer.dispatchEvent( { type: "loop", action: this, loopDelta: loopDelta } ); } } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" this.time = time; return duration - time; } } this.time = time; return time; }, _setEndings: function ( atStart, atEnd, pingPong ) { var settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } }, _scheduleFading: function ( duration, weightNow, weightThen ) { var mixer = this._mixer, now = mixer.time, interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } } ); /** * * Player for AnimationClips. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function AnimationMixer( root ) { this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: AnimationMixer, _bindAction: function ( action, prototypeAction ) { var root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName, bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( var i = 0; i !== nTracks; ++ i ) { var track = tracks[ i ], trackName = track.name, binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } var path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } }, _activateAction: function ( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind var rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } var bindings = action._propertyBindings; // increment reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } }, _deactivateAction: function ( action ) { if ( this._isActiveAction( action ) ) { var bindings = action._propertyBindings; // decrement reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } }, // Memory manager _initMemoryManager: function () { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; var scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; }, // Memory management for AnimationAction objects _isActiveAction: function ( action ) { var index = action._cacheIndex; return index !== null && index < this._nActiveActions; }, _addInactiveAction: function ( action, clipUuid, rootUuid ) { var actions = this._actions, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { var knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; }, _removeInactiveAction: function ( action ) { var actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; var clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; var actionByRoot = actionsForClip.actionByRoot, rootUuid = ( action._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); }, _removeInactiveBindingsForAction: function ( action ) { var bindings = action._propertyBindings; for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } }, _lendAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s var actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; }, _takeBackAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a var actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; }, // Memory management for PropertyMixer objects _addInactiveBinding: function ( binding, rootUuid, trackName ) { var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], bindings = this._bindings; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); }, _removeInactiveBinding: function ( binding ) { var bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; remove_empty_map: { for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars delete bindingsByRoot[ rootUuid ]; } }, _lendBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; }, _takeBackBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; }, // Memory management of Interpolants for weight and time scale _lendControlInterpolant: function () { var interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++, interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, this._controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; }, _takeBackControlInterpolant: function ( interpolant ) { var interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; }, _controlInterpolantsResultBuffer: new Float32Array( 1 ), // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject !== null ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ], prototypeAction = null; if ( actionsForClip !== undefined ) { var existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it var newAction = new AnimationAction( this, clipObject, optionalRoot ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; }, // get an existing action existingAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; }, // deactivates all previously scheduled actions stopAllAction: function () { var actions = this._actions, nActions = this._nActiveActions, bindings = this._bindings, nBindings = this._nActiveBindings; this._nActiveActions = 0; this._nActiveBindings = 0; for ( var i = 0; i !== nActions; ++ i ) { actions[ i ].reset(); } for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].useCount = 0; } return this; }, // advance the time and update apply the animation update: function ( deltaTime ) { deltaTime *= this.timeScale; var actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( var i = 0; i !== nActions; ++ i ) { var action = actions[ i ]; action._update( time, deltaTime, timeDirection, accuIndex ); } // update scene graph var bindings = this._bindings, nBindings = this._nActiveBindings; for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; }, // return this mixer"s root target object getRoot: function () { return this._root; }, // free all resources specific to a particular clip uncacheClip: function ( clip ) { var actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away var actionsToRemove = actionsForClip.knownActions; for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { var action = actionsToRemove[ i ]; this._deactivateAction( action ); var cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } }, // free all resources specific to a particular root target object uncacheRoot: function ( root ) { var rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( var clipUuid in actionsByClip ) { var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( var trackName in bindingByName ) { var binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } }, // remove a targeted clip from the cache uncacheAction: function ( clip, optionalRoot ) { var action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Uniform( value ) { if ( typeof value === "string" ) { console.warn( "THREE.Uniform: Type parameter is no longer needed." ); value = arguments[ 1 ]; } this.value = value; } Uniform.prototype.clone = function () { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferGeometry() { BufferGeometry.call( this ); this.type = "InstancedBufferGeometry"; this.maxInstancedCount = undefined; } InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { constructor: InstancedBufferGeometry, isInstancedBufferGeometry: true, copy: function ( source ) { BufferGeometry.prototype.copy.call( this, source ); this.maxInstancedCount = source.maxInstancedCount; return this; }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } Object.defineProperties( InterleavedBufferAttribute.prototype, { count: { get: function () { return this.data.count; } }, array: { get: function () { return this.data.array; } } } ); Object.assign( InterleavedBufferAttribute.prototype, { isInterleavedBufferAttribute: true, setX: function ( index, x ) { this.data.array[ index * this.data.stride + this.offset ] = x; return this; }, setY: function ( index, y ) { this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; }, setZ: function ( index, z ) { this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; }, setW: function ( index, w ) { this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; }, getX: function ( index ) { return this.data.array[ index * this.data.stride + this.offset ]; }, getY: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 1 ]; }, getZ: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 2 ]; }, getW: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 3 ]; }, setXY: function ( index, x, y ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBuffer( array, stride ) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( InterleavedBuffer.prototype, { isInterleavedBuffer: true, onUploadCallback: function () {}, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.stride : 0; this.array = array; return this; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( var i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, clone: function () { return new this.constructor().copy( this ); }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { InterleavedBuffer.call( this, array, stride ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { constructor: InstancedInterleavedBuffer, isInstancedInterleavedBuffer: true, copy: function ( source ) { InterleavedBuffer.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { BufferAttribute.call( this, array, itemSize ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { constructor: InstancedBufferAttribute, isInstancedBufferAttribute: true, copy: function ( source ) { BufferAttribute.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ function Raycaster( origin, direction, near, far ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near || 0; this.far = far || Infinity; this.params = { Mesh: {}, Line: {}, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; Object.defineProperties( this.params, { PointCloud: { get: function () { console.warn( "THREE.Raycaster: params.PointCloud has been renamed to params.Points." ); return this.Points; } } } ); } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.visible === false ) return; object.raycast( raycaster, intersects ); if ( recursive === true ) { var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } Object.assign( Raycaster.prototype, { linePrecision: 1, set: function ( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); }, setFromCamera: function ( coords, camera ) { if ( ( camera && camera.isPerspectiveCamera ) ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); } else if ( ( camera && camera.isOrthographicCamera ) ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); } else { console.error( "THREE.Raycaster: Unsupported camera type." ); } }, intersectObject: function ( object, recursive, optionalTarget ) { var intersects = optionalTarget || []; intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; }, intersectObjects: function ( objects, recursive, optionalTarget ) { var intersects = optionalTarget || []; if ( Array.isArray( objects ) === false ) { console.warn( "THREE.Raycaster.intersectObjects: objects is not an Array." ); return intersects; } for ( var i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function Clock( autoStart ) { this.autoStart = ( autoStart !== undefined ) ? autoStart : true; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } Object.assign( Clock.prototype, { start: function () { this.startTime = ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; }, stop: function () { this.getElapsedTime(); this.running = false; this.autoStart = false; }, getElapsedTime: function () { this.getDelta(); return this.elapsedTime; }, getDelta: function () { var diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { var newTime = ( typeof performance === "undefined" ? Date : performance ).now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } ); /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley * * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The poles (phi) are at the positive and negative y axis. * The equator starts at positive z. */ function Spherical( radius, phi, theta ) { this.radius = ( radius !== undefined ) ? radius : 1.0; this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere return this; } Object.assign( Spherical.prototype, { set: function ( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; }, // restrict phi to be betwee EPS and PI-EPS makeSafe: function () { var EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; }, setFromVector3: function ( vec3 ) { this.radius = vec3.length(); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle } return this; } } ); /** * @author Mugen87 / https://github.com/Mugen87 * * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system * */ function Cylindrical( radius, theta, y ) { this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane return this; } Object.assign( Cylindrical.prototype, { set: function ( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; }, setFromVector3: function ( vec3 ) { this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); this.theta = Math.atan2( vec3.x, vec3.z ); this.y = vec3.y; return this; } } ); /** * @author bhouston / http://clara.io */ function Box2( min, max ) { this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); } Object.assign( Box2.prototype, { set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector2(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .getCenter() target is now required" ); target = new Vector2(); } return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .getSize() target is now required" ); target = new Vector2(); } return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; }, getParameter: function ( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. if ( target === undefined ) { console.warn( "THREE.Box2: .getParameter() target is now required" ); target = new Vector2(); } return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); }, intersectsBox: function ( box ) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; }, clampPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .clampPoint() target is now required" ); target = new Vector2(); } return target.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector2(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author alteredq / http://alteredqualia.com/ */ function ImmediateRenderObject( material ) { Object3D.call( this ); this.material = material; this.render = function ( /* renderCallback */ ) {}; } ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; ImmediateRenderObject.prototype.isImmediateRenderObject = true; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function VertexNormalsHelper( object, size, hex, linewidth ) { this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xff0000; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length * 3; } else if ( objGeometry && objGeometry.isBufferGeometry ) { nNormals = objGeometry.attributes.normal.count; } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; VertexNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { var keys = [ "a", "b", "c" ]; this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { var vertex = vertices[ face[ keys[ j ] ] ]; var normal = face.vertexNormals[ j ]; v1.copy( vertex ).applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } } else if ( objGeometry && objGeometry.isBufferGeometry ) { var objPos = objGeometry.attributes.position; var objNorm = objGeometry.attributes.normal; var idx = 0; // for simplicity, ignore index and drawcalls, and render every normal for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function SpotLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new BufferGeometry(); var positions = [ 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 ]; for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { var p1 = ( i / l ) * Math.PI * 2; var p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } SpotLightHelper.prototype = Object.create( Object3D.prototype ); SpotLightHelper.prototype.constructor = SpotLightHelper; SpotLightHelper.prototype.dispose = function () { this.cone.geometry.dispose(); this.cone.material.dispose(); }; SpotLightHelper.prototype.update = function () { var vector = new Vector3(); var vector2 = new Vector3(); return function update() { this.light.updateMatrixWorld(); var coneLength = this.light.distance ? this.light.distance : 1000; var coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); vector.setFromMatrixPosition( this.light.matrixWorld ); vector2.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( vector2.sub( vector ) ); if ( this.color !== undefined ) { this.cone.material.color.set( this.color ); } else { this.cone.material.color.copy( this.light.color ); } }; }(); /** * @author Sean Griffin / http://twitter.com/sgrif * @author Michael Guerrero / http://realitymeltdown.com * @author mrdoob / http://mrdoob.com/ * @author ikerr / http://verold.com * @author Mugen87 / https://github.com/Mugen87 */ function getBoneList( object ) { var boneList = []; if ( object && object.isBone ) { boneList.push( object ); } for ( var i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } return boneList; } function SkeletonHelper( object ) { var bones = getBoneList( object ); var geometry = new BufferGeometry(); var vertices = []; var colors = []; var color1 = new Color( 0, 0, 1 ); var color2 = new Color( 0, 1, 0 ); for ( var i = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); LineSegments.call( this, geometry, material ); this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } SkeletonHelper.prototype = Object.create( LineSegments.prototype ); SkeletonHelper.prototype.constructor = SkeletonHelper; SkeletonHelper.prototype.updateMatrixWorld = function () { var vector = new Vector3(); var boneMatrix = new Matrix4(); var matrixWorldInv = new Matrix4(); return function updateMatrixWorld( force ) { var bones = this.bones; var geometry = this.geometry; var position = geometry.getAttribute( "position" ); matrixWorldInv.getInverse( this.root.matrixWorld ); for ( var i = 0, j = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j, vector.x, vector.y, vector.z ); boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j + 1, vector.x, vector.y, vector.z ); j += 2; } } geometry.getAttribute( "position" ).needsUpdate = true; Object3D.prototype.updateMatrixWorld.call( this, force ); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function PointLightHelper( light, sphereSize, color ) { this.light = light; this.light.updateMatrixWorld(); this.color = color; var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); Mesh.call( this, geometry, material ); this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); var d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } PointLightHelper.prototype = Object.create( Mesh.prototype ); PointLightHelper.prototype.constructor = PointLightHelper; PointLightHelper.prototype.dispose = function () { this.geometry.dispose(); this.material.dispose(); }; PointLightHelper.prototype.update = function () { if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { this.material.color.copy( this.light.color ); } /* var d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ }; /** * @author abelnation / http://github.com/abelnation * @author Mugen87 / http://github.com/Mugen87 * @author WestLangley / http://github.com/WestLangley */ function RectAreaLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var material = new LineBasicMaterial( { fog: false } ); var geometry = new BufferGeometry(); geometry.addAttribute( "position", new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) ); this.line = new Line( geometry, material ); this.add( this.line ); this.update(); } RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; RectAreaLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; RectAreaLightHelper.prototype.update = function () { // calculate new dimensions of the helper var hx = this.light.width * 0.5; var hy = this.light.height * 0.5; var position = this.line.geometry.attributes.position; var array = position.array; // update vertices array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0; array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0; position.needsUpdate = true; if ( this.color !== undefined ) { this.line.material.color.set( this.color ); } else { this.line.material.color.copy( this.light.color ); } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function HemisphereLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new OctahedronBufferGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); this.material = new MeshBasicMaterial( { wireframe: true, fog: false } ); if ( this.color === undefined ) this.material.vertexColors = VertexColors; var position = geometry.getAttribute( "position" ); var colors = new Float32Array( position.count * 3 ); geometry.addAttribute( "color", new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, this.material ) ); this.update(); } HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; HemisphereLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; HemisphereLightHelper.prototype.update = function () { var vector = new Vector3(); var color1 = new Color(); var color2 = new Color(); return function update() { var mesh = this.children[ 0 ]; if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { var colors = mesh.geometry.getAttribute( "color" ); color1.copy( this.light.color ); color2.copy( this.light.groundColor ); for ( var i = 0, l = colors.count; i < l; i ++ ) { var color = ( i < ( l / 2 ) ) ? color1 : color2; colors.setXYZ( i, color.r, color.g, color.b ); } colors.needsUpdate = true; } mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); }; }(); /** * @author mrdoob / http://mrdoob.com/ */ function GridHelper( size, divisions, color1, color2 ) { size = size || 10; divisions = divisions || 10; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var center = divisions / 2; var step = size / divisions; var halfSize = size / 2; var vertices = [], colors = []; for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { vertices.push( - halfSize, 0, k, halfSize, 0, k ); vertices.push( k, 0, - halfSize, k, 0, halfSize ); var color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } GridHelper.prototype = Object.create( LineSegments.prototype ); GridHelper.prototype.constructor = GridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 * @author Hectate / http://www.github.com/Hectate */ function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { radius = radius || 10; radials = radials || 16; circles = circles || 8; divisions = divisions || 64; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var vertices = []; var colors = []; var x, z; var v, i, j, r, color; // create the radials for ( i = 0; i <= radials; i ++ ) { v = ( i / radials ) * ( Math.PI * 2 ); x = Math.sin( v ) * radius; z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } // create the circles for ( i = 0; i <= circles; i ++ ) { color = ( i & 1 ) ? color1 : color2; r = radius - ( radius / circles * i ); for ( j = 0; j < divisions; j ++ ) { // first vertex v = ( j / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } PolarGridHelper.prototype = Object.create( LineSegments.prototype ); PolarGridHelper.prototype.constructor = PolarGridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function FaceNormalsHelper( object, size, hex, linewidth ) { // FaceNormalsHelper only supports THREE.Geometry this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xffff00; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length; } else { console.warn( "THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead." ); } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; FaceNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var normal = face.normal; v1.copy( vertices[ face.a ] ) .add( vertices[ face.b ] ) .add( vertices[ face.c ] ) .divideScalar( 3 ) .applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function DirectionalLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if ( size === undefined ) size = 1; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.lightPlane = new Line( geometry, material ); this.add( this.lightPlane ); geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.targetLine = new Line( geometry, material ); this.add( this.targetLine ); this.update(); } DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; DirectionalLightHelper.prototype.dispose = function () { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); }; DirectionalLightHelper.prototype.update = function () { var v1 = new Vector3(); var v2 = new Vector3(); var v3 = new Vector3(); return function update() { v1.setFromMatrixPosition( this.light.matrixWorld ); v2.setFromMatrixPosition( this.light.target.matrixWorld ); v3.subVectors( v2, v1 ); this.lightPlane.lookAt( v3 ); if ( this.color !== undefined ) { this.lightPlane.material.color.set( this.color ); this.targetLine.material.color.set( this.color ); } else { this.lightPlane.material.color.copy( this.light.color ); this.targetLine.material.color.copy( this.light.color ); } this.targetLine.lookAt( v3 ); this.targetLine.scale.z = v3.length(); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author Mugen87 / https://github.com/Mugen87 * * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * http://evanw.github.com/lightgl.js/tests/shadowmap.html */ function CameraHelper( camera ) { var geometry = new BufferGeometry(); var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); var vertices = []; var colors = []; var pointMap = {}; // colors var colorFrustum = new Color( 0xffaa00 ); var colorCone = new Color( 0xff0000 ); var colorUp = new Color( 0x00aaff ); var colorTarget = new Color( 0xffffff ); var colorCross = new Color( 0x333333 ); // near addLine( "n1", "n2", colorFrustum ); addLine( "n2", "n4", colorFrustum ); addLine( "n4", "n3", colorFrustum ); addLine( "n3", "n1", colorFrustum ); // far addLine( "f1", "f2", colorFrustum ); addLine( "f2", "f4", colorFrustum ); addLine( "f4", "f3", colorFrustum ); addLine( "f3", "f1", colorFrustum ); // sides addLine( "n1", "f1", colorFrustum ); addLine( "n2", "f2", colorFrustum ); addLine( "n3", "f3", colorFrustum ); addLine( "n4", "f4", colorFrustum ); // cone addLine( "p", "n1", colorCone ); addLine( "p", "n2", colorCone ); addLine( "p", "n3", colorCone ); addLine( "p", "n4", colorCone ); // up addLine( "u1", "u2", colorUp ); addLine( "u2", "u3", colorUp ); addLine( "u3", "u1", colorUp ); // target addLine( "c", "t", colorTarget ); addLine( "p", "c", colorCross ); // cross addLine( "cn1", "cn2", colorCross ); addLine( "cn3", "cn4", colorCross ); addLine( "cf1", "cf2", colorCross ); addLine( "cf3", "cf4", colorCross ); function addLine( a, b, color ) { addPoint( a, color ); addPoint( b, color ); } function addPoint( id, color ) { vertices.push( 0, 0, 0 ); colors.push( color.r, color.g, color.b ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); LineSegments.call( this, geometry, material ); this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } CameraHelper.prototype = Object.create( LineSegments.prototype ); CameraHelper.prototype.constructor = CameraHelper; CameraHelper.prototype.update = function () { var geometry, pointMap; var vector = new Vector3(); var camera = new Camera(); function setPoint( point, x, y, z ) { vector.set( x, y, z ).unproject( camera ); var points = pointMap[ point ]; if ( points !== undefined ) { var position = geometry.getAttribute( "position" ); for ( var i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], vector.x, vector.y, vector.z ); } } } return function update() { geometry = this.geometry; pointMap = this.pointMap; var w = 1, h = 1; // we need just camera projection matrix // world matrix must be identity camera.projectionMatrix.copy( this.camera.projectionMatrix ); // center / target setPoint( "c", 0, 0, - 1 ); setPoint( "t", 0, 0, 1 ); // near setPoint( "n1", - w, - h, - 1 ); setPoint( "n2", w, - h, - 1 ); setPoint( "n3", - w, h, - 1 ); setPoint( "n4", w, h, - 1 ); // far setPoint( "f1", - w, - h, 1 ); setPoint( "f2", w, - h, 1 ); setPoint( "f3", - w, h, 1 ); setPoint( "f4", w, h, 1 ); // up setPoint( "u1", w * 0.7, h * 1.1, - 1 ); setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", 0, h * 2, - 1 ); // cross setPoint( "cf1", - w, 0, 1 ); setPoint( "cf2", w, 0, 1 ); setPoint( "cf3", 0, - h, 1 ); setPoint( "cf4", 0, h, 1 ); setPoint( "cn1", - w, 0, - 1 ); setPoint( "cn2", w, 0, - 1 ); setPoint( "cn3", 0, - h, - 1 ); setPoint( "cn4", 0, h, - 1 ); geometry.getAttribute( "position" ).needsUpdate = true; }; }(); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 */ function BoxHelper( object, color ) { this.object = object; if ( color === undefined ) color = 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = new Float32Array( 8 * 3 ); var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.matrixAutoUpdate = false; this.update(); } BoxHelper.prototype = Object.create( LineSegments.prototype ); BoxHelper.prototype.constructor = BoxHelper; BoxHelper.prototype.update = ( function () { var box = new Box3(); return function update( object ) { if ( object !== undefined ) { console.warn( "THREE.BoxHelper: .update() has no longer arguments." ); } if ( this.object !== undefined ) { box.setFromObject( this.object ); } if ( box.isEmpty() ) return; var min = box.min; var max = box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ var position = this.geometry.attributes.position; var array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); }; } )(); BoxHelper.prototype.setFromObject = function ( object ) { this.object = object; this.update(); return this; }; /** * @author WestLangley / http://github.com/WestLangley */ function Box3Helper( box, hex ) { this.type = "Box3Helper"; this.box = box; var color = ( hex !== undefined ) ? hex : 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.geometry.computeBoundingSphere(); } Box3Helper.prototype = Object.create( LineSegments.prototype ); Box3Helper.prototype.constructor = Box3Helper; Box3Helper.prototype.updateMatrixWorld = function ( force ) { var box = this.box; if ( box.isEmpty() ) return; box.getCenter( this.position ); box.getSize( this.scale ); this.scale.multiplyScalar( 0.5 ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley */ function PlaneHelper( plane, size, hex ) { this.type = "PlaneHelper"; this.plane = plane; this.size = ( size === undefined ) ? 1 : size; var color = ( hex !== undefined ) ? hex : 0xffff00; var positions = [ 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, 0, 1, 0, 0, 0 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); geometry.computeBoundingSphere(); Line.call( this, geometry, new LineBasicMaterial( { color: color } ) ); // var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ]; var geometry2 = new BufferGeometry(); geometry2.addAttribute( "position", new Float32BufferAttribute( positions2, 3 ) ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) ); } PlaneHelper.prototype = Object.create( Line.prototype ); PlaneHelper.prototype.constructor = PlaneHelper; PlaneHelper.prototype.updateMatrixWorld = function ( force ) { var scale = - this.plane.constant; if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt( this.plane.normal ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * * Parameters: * dir - Vector3 * origin - Vector3 * length - Number * color - color in hex value * headLength - Number * headWidth - Number */ var lineGeometry, coneGeometry; function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { // dir is assumed to be normalized Object3D.call( this ); if ( color === undefined ) color = 0xffff00; if ( length === undefined ) length = 1; if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; if ( lineGeometry === undefined ) { lineGeometry = new BufferGeometry(); lineGeometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); coneGeometry.translate( 0, - 0.5, 0 ); } this.position.copy( origin ); this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } ArrowHelper.prototype = Object.create( Object3D.prototype ); ArrowHelper.prototype.constructor = ArrowHelper; ArrowHelper.prototype.setDirection = ( function () { var axis = new Vector3(); var radians; return function setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { axis.set( dir.z, 0, - dir.x ).normalize(); radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( axis, radians ); } }; }() ); ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); }; ArrowHelper.prototype.setColor = function ( color ) { this.line.material.color.copy( color ); this.cone.material.color.copy( color ); }; /** * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ */ function AxesHelper( size ) { size = size || 1; var vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; var colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } AxesHelper.prototype = Object.create( LineSegments.prototype ); AxesHelper.prototype.constructor = AxesHelper; /** * @author mrdoob / http://mrdoob.com/ */ function Face4( a, b, c, d, normal, color, materialIndex ) { console.warn( "THREE.Face4 has been removed. A THREE.Face3 will be created instead." ); return new Face3( a, b, c, normal, color, materialIndex ); } var LineStrip = 0; var LinePieces = 1; function MeshFaceMaterial( materials ) { console.warn( "THREE.MeshFaceMaterial has been removed. Use an Array instead." ); return materials; } function MultiMaterial( materials ) { if ( materials === undefined ) materials = []; console.warn( "THREE.MultiMaterial has been removed. Use an Array instead." ); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud( geometry, material ) { console.warn( "THREE.PointCloud has been renamed to THREE.Points." ); return new Points( geometry, material ); } function Particle( material ) { console.warn( "THREE.Particle has been renamed to THREE.Sprite." ); return new Sprite( material ); } function ParticleSystem( geometry, material ) { console.warn( "THREE.ParticleSystem has been renamed to THREE.Points." ); return new Points( geometry, material ); } function PointCloudMaterial( parameters ) { console.warn( "THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleBasicMaterial( parameters ) { console.warn( "THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleSystemMaterial( parameters ) { console.warn( "THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function Vertex( x, y, z ) { console.warn( "THREE.Vertex has been removed. Use THREE.Vector3 instead." ); return new Vector3( x, y, z ); } // function DynamicBufferAttribute( array, itemSize ) { console.warn( "THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead." ); return new BufferAttribute( array, itemSize ).setDynamic( true ); } function Int8Attribute( array, itemSize ) { console.warn( "THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead." ); return new Int8BufferAttribute( array, itemSize ); } function Uint8Attribute( array, itemSize ) { console.warn( "THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead." ); return new Uint8BufferAttribute( array, itemSize ); } function Uint8ClampedAttribute( array, itemSize ) { console.warn( "THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead." ); return new Uint8ClampedBufferAttribute( array, itemSize ); } function Int16Attribute( array, itemSize ) { console.warn( "THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead." ); return new Int16BufferAttribute( array, itemSize ); } function Uint16Attribute( array, itemSize ) { console.warn( "THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead." ); return new Uint16BufferAttribute( array, itemSize ); } function Int32Attribute( array, itemSize ) { console.warn( "THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead." ); return new Int32BufferAttribute( array, itemSize ); } function Uint32Attribute( array, itemSize ) { console.warn( "THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead." ); return new Uint32BufferAttribute( array, itemSize ); } function Float32Attribute( array, itemSize ) { console.warn( "THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead." ); return new Float32BufferAttribute( array, itemSize ); } function Float64Attribute( array, itemSize ) { console.warn( "THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead." ); return new Float64BufferAttribute( array, itemSize ); } // Curve.create = function ( construct, getPoint ) { console.log( "THREE.Curve.create() has been deprecated" ); construct.prototype = Object.create( Curve.prototype ); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Object.assign( CurvePath.prototype, { createPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from path points (for Line or Points objects) var pts = this.getPoints( divisions ); return this.createGeometry( pts ); }, createSpacedPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from equidistant sampling along the path var pts = this.getSpacedPoints( divisions ); return this.createGeometry( pts ); }, createGeometry: function ( points ) { console.warn( "THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); var geometry = new Geometry(); for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return geometry; } } ); // Object.assign( Path.prototype, { fromPoints: function ( points ) { console.warn( "THREE.Path: .fromPoints() has been renamed to .setFromPoints()." ); this.setFromPoints( points ); } } ); // function ClosedSplineCurve3( points ) { console.warn( "THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; this.closed = true; } ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function SplineCurve3( points ) { console.warn( "THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function Spline( points ) { console.warn( "THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } Spline.prototype = Object.create( CatmullRomCurve3.prototype ); Object.assign( Spline.prototype, { initFromArray: function ( /* a */ ) { console.error( "THREE.Spline: .initFromArray() has been removed." ); }, getControlPointsArray: function ( /* optionalTarget */ ) { console.error( "THREE.Spline: .getControlPointsArray() has been removed." ); }, reparametrizeByArcLength: function ( /* samplingCoef */ ) { console.error( "THREE.Spline: .reparametrizeByArcLength() has been removed." ); } } ); // function AxisHelper( size ) { console.warn( "THREE.AxisHelper has been renamed to THREE.AxesHelper." ); return new AxesHelper( size ); } function BoundingBoxHelper( object, color ) { console.warn( "THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead." ); return new BoxHelper( object, color ); } function EdgesHelper( object, hex ) { console.warn( "THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead." ); return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } GridHelper.prototype.setColors = function () { console.error( "THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead." ); }; SkeletonHelper.prototype.update = function () { console.error( "THREE.SkeletonHelper: update() no longer needs to be called." ); }; function WireframeHelper( object, hex ) { console.warn( "THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead." ); return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } // Object.assign( Loader.prototype, { extractUrlBase: function ( url ) { console.warn( "THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead." ); return LoaderUtils.extractUrlBase( url ); } } ); function XHRLoader( manager ) { console.warn( "THREE.XHRLoader has been renamed to THREE.FileLoader." ); return new FileLoader( manager ); } function BinaryTextureLoader( manager ) { console.warn( "THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader." ); return new DataTextureLoader( manager ); } // Object.assign( Box2.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box2: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box2: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box2: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Object.assign( Box3.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box3: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box3: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Line3.prototype.center = function ( optionalTarget ) { console.warn( "THREE.Line3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }; Object.assign( _Math, { random16: function () { console.warn( "THREE.Math: .random16() has been deprecated. Use Math.random() instead." ); return Math.random(); }, nearestPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo()." ); return _Math.floorPowerOfTwo( value ); }, nextPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo()." ); return _Math.ceilPowerOfTwo( value ); } } ); Object.assign( Matrix3.prototype, { flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead." ); return vector.applyMatrix3( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix3: .multiplyVector3Array() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix3: .applyToVector3Array() has been removed." ); } } ); Object.assign( Matrix4.prototype, { extractPosition: function ( m ) { console.warn( "THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()." ); return this.copyPosition( m ); }, flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, getPosition: function () { var v1; return function getPosition() { if ( v1 === undefined ) v1 = new Vector3(); console.warn( "THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead." ); return v1.setFromMatrixColumn( this, 3 ); }; }(), setRotationFromQuaternion: function ( q ) { console.warn( "THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()." ); return this.makeRotationFromQuaternion( q ); }, multiplyToArray: function () { console.warn( "THREE.Matrix4: .multiplyToArray() has been removed." ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector4: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix4: .multiplyVector3Array() has been removed." ); }, rotateAxis: function ( v ) { console.warn( "THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead." ); v.transformDirection( this ); }, crossVector: function ( vector ) { console.warn( "THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, translate: function () { console.error( "THREE.Matrix4: .translate() has been removed." ); }, rotateX: function () { console.error( "THREE.Matrix4: .rotateX() has been removed." ); }, rotateY: function () { console.error( "THREE.Matrix4: .rotateY() has been removed." ); }, rotateZ: function () { console.error( "THREE.Matrix4: .rotateZ() has been removed." ); }, rotateByAxis: function () { console.error( "THREE.Matrix4: .rotateByAxis() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix4: .applyToVector3Array() has been removed." ); }, makeFrustum: function ( left, right, bottom, top, near, far ) { console.warn( "THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead." ); return this.makePerspective( left, right, top, bottom, near, far ); } } ); Plane.prototype.isIntersectionLine = function ( line ) { console.warn( "THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()." ); return this.intersectsLine( line ); }; Quaternion.prototype.multiplyVector3 = function ( vector ) { console.warn( "THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead." ); return vector.applyQuaternion( this ); }; Object.assign( Ray.prototype, { isIntersectionBox: function ( box ) { console.warn( "THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionPlane: function ( plane ) { console.warn( "THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()." ); return this.intersectsPlane( plane ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); } } ); Object.assign( Triangle.prototype, { area: function () { console.warn( "THREE.Triangle: .area() has been renamed to .getArea()." ); return this.getArea(); }, barycoordFromPoint: function ( point, target ) { console.warn( "THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()." ); return this.getBarycoord( point, target ); }, midpoint: function ( target ) { console.warn( "THREE.Triangle: .midpoint() has been renamed to .getMidpoint()." ); return this.getMidpoint( target ); }, normal: function ( target ) { console.warn( "THREE.Triangle: .normal() has been renamed to .getNormal()." ); return this.getNormal( target ); }, plane: function ( target ) { console.warn( "THREE.Triangle: .plane() has been renamed to .getPlane()." ); return this.getPlane( target ); } } ); Object.assign( Triangle, { barycoordFromPoint: function ( point, a, b, c, target ) { console.warn( "THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()." ); return Triangle.getBarycoord( point, a, b, c, target ); }, normal: function ( a, b, c, target ) { console.warn( "THREE.Triangle: .normal() has been renamed to .getNormal()." ); return Triangle.getNormal( a, b, c, target ); } } ); Object.assign( Shape.prototype, { extractAllPoints: function ( divisions ) { console.warn( "THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead." ); return this.extractPoints( divisions ); }, extrude: function ( options ) { console.warn( "THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead." ); return new ExtrudeGeometry( this, options ); }, makeGeometry: function ( options ) { console.warn( "THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead." ); return new ShapeGeometry( this, options ); } } ); Object.assign( Vector2.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector3.prototype, { setEulerFromRotationMatrix: function () { console.error( "THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead." ); }, setEulerFromQuaternion: function () { console.error( "THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead." ); }, getPositionFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()." ); return this.setFromMatrixPosition( m ); }, getScaleFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()." ); return this.setFromMatrixScale( m ); }, getColumnFromMatrix: function ( index, matrix ) { console.warn( "THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()." ); return this.setFromMatrixColumn( matrix, index ); }, applyProjection: function ( m ) { console.warn( "THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead." ); return this.applyMatrix4( m ); }, fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector4.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, lengthManhattan: function () { console.warn( "THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); // Object.assign( Geometry.prototype, { computeTangents: function () { console.error( "THREE.Geometry: .computeTangents() has been removed." ); }, computeLineDistances: function () { console.error( "THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead." ); } } ); Object.assign( Object3D.prototype, { getChildByName: function ( name ) { console.warn( "THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()." ); return this.getObjectByName( name ); }, renderDepth: function () { console.warn( "THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead." ); }, translate: function ( distance, axis ) { console.warn( "THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead." ); return this.translateOnAxis( axis, distance ); }, getWorldRotation: function () { console.error( "THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead." ); } } ); Object.defineProperties( Object3D.prototype, { eulerOrder: { get: function () { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); return this.rotation.order; }, set: function ( value ) { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); this.rotation.order = value; } }, 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." ); } } } ); Object.defineProperties( LOD.prototype, { objects: { get: function () { console.warn( "THREE.LOD: .objects has been renamed to .levels." ); return this.levels; } } } ); Object.defineProperty( Skeleton.prototype, "useVertexTexture", { get: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); }, set: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); } } ); Object.defineProperty( Curve.prototype, "__arcLengthDivisions", { get: function () { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); return this.arcLengthDivisions; }, set: function ( value ) { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); this.arcLengthDivisions = value; } } ); // PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup." ); if ( filmGauge !== undefined ) this.filmGauge = filmGauge; this.setFocalLength( focalLength ); }; // Object.defineProperties( Light.prototype, { onlyShadow: { set: function () { console.warn( "THREE.Light: .onlyShadow has been removed." ); } }, shadowCameraFov: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFov is now .shadow.camera.fov." ); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraLeft is now .shadow.camera.left." ); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraRight is now .shadow.camera.right." ); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraTop is now .shadow.camera.top." ); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom." ); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraNear is now .shadow.camera.near." ); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFar is now .shadow.camera.far." ); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn( "THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead." ); } }, shadowBias: { set: function ( value ) { console.warn( "THREE.Light: .shadowBias is now .shadow.bias." ); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn( "THREE.Light: .shadowDarkness has been removed." ); } }, shadowMapWidth: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapWidth is now .shadow.mapSize.width." ); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapHeight is now .shadow.mapSize.height." ); this.shadow.mapSize.height = value; } } } ); // Object.defineProperties( BufferAttribute.prototype, { length: { get: function () { console.warn( "THREE.BufferAttribute: .length has been deprecated. Use .count instead." ); return this.array.length; } }, copyIndicesArray: function ( /* indices */ ) { console.error( "THREE.BufferAttribute: .copyIndicesArray() has been removed." ); } } ); Object.assign( BufferGeometry.prototype, { addIndex: function ( index ) { console.warn( "THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()." ); this.setIndex( index ); }, addDrawCall: function ( start, count, indexOffset ) { if ( indexOffset !== undefined ) { console.warn( "THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset." ); } console.warn( "THREE.BufferGeometry: .addDrawCall() is now .addGroup()." ); this.addGroup( start, count ); }, clearDrawCalls: function () { console.warn( "THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()." ); this.clearGroups(); }, computeTangents: function () { console.warn( "THREE.BufferGeometry: .computeTangents() has been removed." ); }, computeOffsets: function () { console.warn( "THREE.BufferGeometry: .computeOffsets() has been removed." ); } } ); Object.defineProperties( BufferGeometry.prototype, { drawcalls: { get: function () { console.error( "THREE.BufferGeometry: .drawcalls has been renamed to .groups." ); return this.groups; } }, offsets: { get: function () { console.warn( "THREE.BufferGeometry: .offsets has been renamed to .groups." ); return this.groups; } } } ); // Object.assign( ExtrudeBufferGeometry.prototype, { getArrays: function () { console.error( "THREE.ExtrudeBufferGeometry: .getArrays() has been removed." ); }, addShapeList: function () { console.error( "THREE.ExtrudeBufferGeometry: .addShapeList() has been removed." ); }, addShape: function () { console.error( "THREE.ExtrudeBufferGeometry: .addShape() has been removed." ); } } ); // Object.defineProperties( Uniform.prototype, { dynamic: { set: function () { console.warn( "THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead." ); } }, onUpdate: { value: function () { console.warn( "THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead." ); return this; } } } ); // Object.defineProperties( Material.prototype, { wrapAround: { get: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); }, set: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); } }, wrapRGB: { get: function () { console.warn( "THREE.Material: .wrapRGB has been removed." ); return new Color(); } }, shading: { get: function () { console.error( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); }, set: function ( value ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( value === FlatShading ); } } } ); Object.defineProperties( MeshPhongMaterial.prototype, { metal: { get: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead." ); return false; }, set: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead" ); } } } ); Object.defineProperties( ShaderMaterial.prototype, { derivatives: { get: function () { console.warn( "THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); return this.extensions.derivatives; }, set: function ( value ) { console.warn( "THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); this.extensions.derivatives = value; } } } ); // Object.assign( WebGLRenderer.prototype, { animate: function ( callback ) { console.warn( "THREE.WebGLRenderer: .animate() is now .setAnimationLoop()." ); this.setAnimationLoop( callback ); }, getCurrentRenderTarget: function () { console.warn( "THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()." ); return this.getRenderTarget(); }, getMaxAnisotropy: function () { console.warn( "THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()." ); return this.capabilities.getMaxAnisotropy(); }, getPrecision: function () { console.warn( "THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision." ); return this.capabilities.precision; }, resetGLState: function () { console.warn( "THREE.WebGLRenderer: .resetGLState() is now .state.reset()." ); return this.state.reset(); }, supportsFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )." ); return this.extensions.get( "OES_texture_float" ); }, supportsHalfFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )." ); return this.extensions.get( "OES_texture_half_float" ); }, supportsStandardDerivatives: function () { console.warn( "THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )." ); return this.extensions.get( "OES_standard_derivatives" ); }, supportsCompressedTextureS3TC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )." ); return this.extensions.get( "WEBGL_compressed_texture_s3tc" ); }, supportsCompressedTexturePVRTC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )." ); return this.extensions.get( "WEBGL_compressed_texture_pvrtc" ); }, supportsBlendMinMax: function () { console.warn( "THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )." ); return this.extensions.get( "EXT_blend_minmax" ); }, supportsVertexTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures." ); return this.capabilities.vertexTextures; }, supportsInstancedArrays: function () { console.warn( "THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )." ); return this.extensions.get( "ANGLE_instanced_arrays" ); }, enableScissorTest: function ( boolean ) { console.warn( "THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()." ); this.setScissorTest( boolean ); }, initMaterial: function () { console.warn( "THREE.WebGLRenderer: .initMaterial() has been removed." ); }, addPrePlugin: function () { console.warn( "THREE.WebGLRenderer: .addPrePlugin() has been removed." ); }, addPostPlugin: function () { console.warn( "THREE.WebGLRenderer: .addPostPlugin() has been removed." ); }, updateShadowMap: function () { console.warn( "THREE.WebGLRenderer: .updateShadowMap() has been removed." ); }, setFaceCulling: function () { console.warn( "THREE.WebGLRenderer: .setFaceCulling() has been removed." ); } } ); Object.defineProperties( WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled." ); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type." ); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn( "THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead." ); return undefined; }, set: function ( /* value */ ) { console.warn( "THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead." ); } } } ); Object.defineProperties( WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn( "THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead." ); return undefined; }, set: function ( /* cullFace */ ) { 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." ); return undefined; }, 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." ); return undefined; }, set: function () { console.warn( "THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead." ); } } } ); // Object.defineProperties( WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); return this.texture.wrapS; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); return this.texture.wrapT; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); return this.texture.magFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); return this.texture.minFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); return this.texture.anisotropy; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); return this.texture.offset; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); this.texture.offset = value; } }, repeat: { get: function () { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); return this.texture.repeat; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); this.texture.repeat = value; } }, format: { get: function () { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); return this.texture.format; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); this.texture.format = value; } }, type: { get: function () { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); return this.texture.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); return this.texture.generateMipmaps; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); this.texture.generateMipmaps = value; } } } ); // Audio.prototype.load = function ( file ) { console.warn( "THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead." ); var scope = this; var audioLoader = new AudioLoader(); audioLoader.load( file, function ( buffer ) { scope.setBuffer( buffer ); } ); return this; }; AudioAnalyser.prototype.getData = function () { console.warn( "THREE.AudioAnalyser: .getData() is now .getFrequencyData()." ); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { console.warn( "THREE.CubeCamera: .updateCubeMap() is now .update()." ); return this.update( renderer, scene ); }; // var GeometryUtils = { merge: function ( geometry1, geometry2, materialIndexOffset ) { console.warn( "THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead." ); var matrix; if ( geometry2.isMesh ) { geometry2.matrixAutoUpdate && geometry2.updateMatrix(); matrix = geometry2.matrix; geometry2 = geometry2.geometry; } geometry1.merge( geometry2, matrix, materialIndexOffset ); }, center: function ( geometry ) { console.warn( "THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead." ); return geometry.center(); } }; var ImageUtils = { crossOrigin: undefined, loadTexture: function ( url, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead." ); var loader = new TextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( url, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadTextureCube: function ( urls, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead." ); var loader = new CubeTextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( urls, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadCompressedTexture: function () { console.error( "THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead." ); }, loadCompressedTextureCube: function () { console.error( "THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead." ); } }; // function Projector() { console.error( "THREE.Projector has been moved to /examples/js/renderers/Projector.js." ); this.projectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .projectVector() is now vector.project()." ); vector.project( camera ); }; this.unprojectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .unprojectVector() is now vector.unproject()." ); vector.unproject( camera ); }; this.pickingRay = function () { console.error( "THREE.Projector: .pickingRay() is now raycaster.setFromCamera()." ); }; } // function CanvasRenderer() { console.error( "THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js" ); this.domElement = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); this.clear = function () {}; this.render = function () {}; this.setClearColor = function () {}; this.setSize = function () {}; } // var SceneUtils = { createMultiMaterialObject: function ( /* geometry, materials */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); }, detach: function ( /* child, parent, scene */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); }, attach: function ( /* child, scene, parent */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); } }; // function LensFlare() { console.error( "THREE.LensFlare has been moved to /examples/js/objects/Lensflare.js" ); } exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.ShaderLib = ShaderLib; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.ShaderChunk = ShaderChunk; exports.FogExp2 = FogExp2; exports.Fog = Fog; exports.Scene = Scene; exports.Sprite = Sprite; exports.LOD = LOD; exports.SkinnedMesh = SkinnedMesh; exports.Skeleton = Skeleton; exports.Bone = Bone; exports.Mesh = Mesh; exports.LineSegments = LineSegments; exports.LineLoop = LineLoop; exports.Line = Line; exports.Points = Points; exports.Group = Group; exports.VideoTexture = VideoTexture; exports.DataTexture = DataTexture; exports.CompressedTexture = CompressedTexture; exports.CubeTexture = CubeTexture; exports.CanvasTexture = CanvasTexture; exports.DepthTexture = DepthTexture; exports.Texture = Texture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.DataTextureLoader = DataTextureLoader; exports.CubeTextureLoader = CubeTextureLoader; exports.TextureLoader = TextureLoader; exports.ObjectLoader = ObjectLoader; exports.MaterialLoader = MaterialLoader; exports.BufferGeometryLoader = BufferGeometryLoader; exports.DefaultLoadingManager = DefaultLoadingManager; exports.LoadingManager = LoadingManager; exports.JSONLoader = JSONLoader; exports.ImageLoader = ImageLoader; exports.ImageBitmapLoader = ImageBitmapLoader; exports.FontLoader = FontLoader; exports.FileLoader = FileLoader; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.Cache = Cache; exports.AudioLoader = AudioLoader; exports.SpotLightShadow = SpotLightShadow; exports.SpotLight = SpotLight; exports.PointLight = PointLight; exports.RectAreaLight = RectAreaLight; exports.HemisphereLight = HemisphereLight; exports.DirectionalLightShadow = DirectionalLightShadow; exports.DirectionalLight = DirectionalLight; exports.AmbientLight = AmbientLight; exports.LightShadow = LightShadow; exports.Light = Light; exports.StereoCamera = StereoCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.OrthographicCamera = OrthographicCamera; exports.CubeCamera = CubeCamera; exports.ArrayCamera = ArrayCamera; exports.Camera = Camera; exports.AudioListener = AudioListener; exports.PositionalAudio = PositionalAudio; exports.AudioContext = AudioContext; exports.AudioAnalyser = AudioAnalyser; exports.Audio = Audio; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.StringKeyframeTrack = StringKeyframeTrack; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.PropertyMixer = PropertyMixer; exports.PropertyBinding = PropertyBinding; exports.KeyframeTrack = KeyframeTrack; exports.AnimationUtils = AnimationUtils; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationMixer = AnimationMixer; exports.AnimationClip = AnimationClip; exports.Uniform = Uniform; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.BufferGeometry = BufferGeometry; exports.Geometry = Geometry; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InterleavedBuffer = InterleavedBuffer; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.Face3 = Face3; exports.Object3D = Object3D; exports.Raycaster = Raycaster; exports.Layers = Layers; exports.EventDispatcher = EventDispatcher; exports.Clock = Clock; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.LinearInterpolant = LinearInterpolant; exports.DiscreteInterpolant = DiscreteInterpolant; exports.CubicInterpolant = CubicInterpolant; exports.Interpolant = Interpolant; exports.Triangle = Triangle; exports.Math = _Math; exports.Spherical = Spherical; exports.Cylindrical = Cylindrical; exports.Plane = Plane; exports.Frustum = Frustum; exports.Sphere = Sphere; exports.Ray = Ray; exports.Matrix4 = Matrix4; exports.Matrix3 = Matrix3; exports.Box3 = Box3; exports.Box2 = Box2; exports.Line3 = Line3; exports.Euler = Euler; exports.Vector4 = Vector4; exports.Vector3 = Vector3; exports.Vector2 = Vector2; exports.Quaternion = Quaternion; exports.Color = Color; exports.ImmediateRenderObject = ImmediateRenderObject; exports.VertexNormalsHelper = VertexNormalsHelper; exports.SpotLightHelper = SpotLightHelper; exports.SkeletonHelper = SkeletonHelper; exports.PointLightHelper = PointLightHelper; exports.RectAreaLightHelper = RectAreaLightHelper; exports.HemisphereLightHelper = HemisphereLightHelper; exports.GridHelper = GridHelper; exports.PolarGridHelper = PolarGridHelper; exports.FaceNormalsHelper = FaceNormalsHelper; exports.DirectionalLightHelper = DirectionalLightHelper; exports.CameraHelper = CameraHelper; exports.BoxHelper = BoxHelper; exports.Box3Helper = Box3Helper; exports.PlaneHelper = PlaneHelper; exports.ArrowHelper = ArrowHelper; exports.AxesHelper = AxesHelper; exports.Shape = Shape; exports.Path = Path; exports.ShapePath = ShapePath; exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; exports.ShapeUtils = ShapeUtils; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.ParametricGeometry = ParametricGeometry; exports.ParametricBufferGeometry = ParametricBufferGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OctahedronBufferGeometry = OctahedronBufferGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; exports.TubeGeometry = TubeGeometry; exports.TubeBufferGeometry = TubeBufferGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusBufferGeometry = TorusBufferGeometry; exports.TextGeometry = TextGeometry; exports.TextBufferGeometry = TextBufferGeometry; exports.SphereGeometry = SphereGeometry; exports.SphereBufferGeometry = SphereBufferGeometry; exports.RingGeometry = RingGeometry; exports.RingBufferGeometry = RingBufferGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneBufferGeometry = PlaneBufferGeometry; exports.LatheGeometry = LatheGeometry; exports.LatheBufferGeometry = LatheBufferGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapeBufferGeometry = ShapeBufferGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry; exports.EdgesGeometry = EdgesGeometry; exports.ConeGeometry = ConeGeometry; exports.ConeBufferGeometry = ConeBufferGeometry; exports.CylinderGeometry = CylinderGeometry; exports.CylinderBufferGeometry = CylinderBufferGeometry; exports.CircleGeometry = CircleGeometry; exports.CircleBufferGeometry = CircleBufferGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.ShadowMaterial = ShadowMaterial; exports.SpriteMaterial = SpriteMaterial; exports.RawShaderMaterial = RawShaderMaterial; exports.ShaderMaterial = ShaderMaterial; exports.PointsMaterial = PointsMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshBasicMaterial = MeshBasicMaterial; exports.LineDashedMaterial = LineDashedMaterial; exports.LineBasicMaterial = LineBasicMaterial; exports.Material = Material; exports.Float64BufferAttribute = Float64BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.BufferAttribute = BufferAttribute; exports.ArcCurve = ArcCurve; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.EllipseCurve = EllipseCurve; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.SplineCurve = SplineCurve; exports.REVISION = REVISION; exports.MOUSE = MOUSE; exports.CullFaceNone = CullFaceNone; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.FrontFaceDirectionCW = FrontFaceDirectionCW; exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; exports.BasicShadowMap = BasicShadowMap; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.FrontSide = FrontSide; exports.BackSide = BackSide; exports.DoubleSide = DoubleSide; exports.FlatShading = FlatShading; exports.SmoothShading = SmoothShading; exports.NoColors = NoColors; exports.FaceColors = FaceColors; exports.VertexColors = VertexColors; exports.NoBlending = NoBlending; exports.NormalBlending = NormalBlending; exports.AdditiveBlending = AdditiveBlending; exports.SubtractiveBlending = SubtractiveBlending; exports.MultiplyBlending = MultiplyBlending; exports.CustomBlending = CustomBlending; exports.AddEquation = AddEquation; exports.SubtractEquation = SubtractEquation; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.MinEquation = MinEquation; exports.MaxEquation = MaxEquation; exports.ZeroFactor = ZeroFactor; exports.OneFactor = OneFactor; exports.SrcColorFactor = SrcColorFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.SrcAlphaFactor = SrcAlphaFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.DstAlphaFactor = DstAlphaFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.NeverDepth = NeverDepth; exports.AlwaysDepth = AlwaysDepth; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.EqualDepth = EqualDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterDepth = GreaterDepth; exports.NotEqualDepth = NotEqualDepth; exports.MultiplyOperation = MultiplyOperation; exports.MixOperation = MixOperation; exports.AddOperation = AddOperation; exports.NoToneMapping = NoToneMapping; exports.LinearToneMapping = LinearToneMapping; exports.ReinhardToneMapping = ReinhardToneMapping; exports.Uncharted2ToneMapping = Uncharted2ToneMapping; exports.CineonToneMapping = CineonToneMapping; exports.UVMapping = UVMapping; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.SphericalReflectionMapping = SphericalReflectionMapping; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.RepeatWrapping = RepeatWrapping; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.NearestFilter = NearestFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.LinearFilter = LinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.UnsignedByteType = UnsignedByteType; exports.ByteType = ByteType; exports.ShortType = ShortType; exports.UnsignedShortType = UnsignedShortType; exports.IntType = IntType; exports.UnsignedIntType = UnsignedIntType; exports.FloatType = FloatType; exports.HalfFloatType = HalfFloatType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShort565Type = UnsignedShort565Type; exports.UnsignedInt248Type = UnsignedInt248Type; exports.AlphaFormat = AlphaFormat; exports.RGBFormat = RGBFormat; exports.RGBAFormat = RGBAFormat; exports.LuminanceFormat = LuminanceFormat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.RGBEFormat = RGBEFormat; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.LoopOnce = LoopOnce; exports.LoopRepeat = LoopRepeat; exports.LoopPingPong = LoopPingPong; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.WrapAroundEnding = WrapAroundEnding; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.LinearEncoding = LinearEncoding; exports.sRGBEncoding = sRGBEncoding; exports.GammaEncoding = GammaEncoding; exports.RGBEEncoding = RGBEEncoding; exports.LogLuvEncoding = LogLuvEncoding; exports.RGBM7Encoding = RGBM7Encoding; exports.RGBM16Encoding = RGBM16Encoding; exports.RGBDEncoding = RGBDEncoding; exports.BasicDepthPacking = BasicDepthPacking; exports.RGBADepthPacking = RGBADepthPacking; exports.CubeGeometry = BoxGeometry; exports.Face4 = Face4; exports.LineStrip = LineStrip; exports.LinePieces = LinePieces; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MultiMaterial = MultiMaterial; exports.PointCloud = PointCloud; exports.Particle = Particle; exports.ParticleSystem = ParticleSystem; exports.PointCloudMaterial = PointCloudMaterial; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Vertex = Vertex; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Int16Attribute = Int16Attribute; exports.Uint16Attribute = Uint16Attribute; exports.Int32Attribute = Int32Attribute; exports.Uint32Attribute = Uint32Attribute; exports.Float32Attribute = Float32Attribute; exports.Float64Attribute = Float64Attribute; exports.ClosedSplineCurve3 = ClosedSplineCurve3; exports.SplineCurve3 = SplineCurve3; exports.Spline = Spline; exports.AxisHelper = AxisHelper; exports.BoundingBoxHelper = BoundingBoxHelper; exports.EdgesHelper = EdgesHelper; exports.WireframeHelper = WireframeHelper; exports.XHRLoader = XHRLoader; exports.BinaryTextureLoader = BinaryTextureLoader; exports.GeometryUtils = GeometryUtils; exports.ImageUtils = ImageUtils; exports.Projector = Projector; exports.CanvasRenderer = CanvasRenderer; exports.SceneUtils = SceneUtils; exports.LensFlare = LensFlare; Object.defineProperty(exports, "__esModule", { value: true }); }))); module.exports = exports; /* */}),null);
-----
babylonjs-5.0.0-alpha.56",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g};function i(){!function(a,b){"object"==typeof g&&"object"==typeof h?h.exports=b():"object"==typeof g?g.babylonjs=b():a.BABYLON=b()}("undefined"!=typeof self?self:"undefined"!=typeof a?a:this,function(){return function(a){var b={};function c(d){if(b[d])return b[d].exports;var e=b[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,c),e.l=!0,e.exports}return c.m=a,c.c=b,c.d=function(a,b,d){c.o(a,b)||Object.defineProperty(a,b,{enumerable:!0,get:d})},c.r=function(a){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})},c.t=function(a,b){if(1&b&&(a=c(a)),8&b)return a;if(4&b&&"object"==typeof a&&a&&a.__esModule)return a;var d=Object.create(null);if(c.r(d),Object.defineProperty(d,"default",{enumerable:!0,value:a}),2&b&&"string"!=typeof a)for(b in a)c.d(d,b,function(b){return a[b]}.bind(null,b));return d},c.n=function(a){var b=a&&a.__esModule?function(){return a["default"]}:function(){return a};return c.d(b,"a",b),b},c.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},c.p="",c(c.s=202)}([function(a,b,c){c.d(b,"d",function(){return h}),c.d(b,"e",function(){return i}),c.d(b,"f",function(){return j}),c.d(b,"b",function(){return k}),c.d(b,"a",function(){return l}),c.d(b,"c",function(){return n});var d=c(14),e=c(22),f=c(51);a=c(10);var g=c(81),h=function(){function a(a,b){void 0===a&&(a=0),void 0===b&&(b=0),this.x=a,this.y=b}return a.prototype.toString=function(){return"{X: "+this.x+" Y: "+this.y+"}"},a.prototype.getClassName=function(){return"Vector2"},a.prototype.getHashCode=function(){var a=0|this.x;return 397*a^(0|this.y)},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.x,a[b+1]=this.y,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.copyFrom=function(a){return this.x=a.x,this.y=a.y,this},a.prototype.copyFromFloats=function(a,b){return this.x=a,this.y=b,this},a.prototype.set=function(a,b){return this.copyFromFloats(a,b)},a.prototype.add=function(b){return new a(this.x+b.x,this.y+b.y)},a.prototype.addToRef=function(a,b){return b.x=this.x+a.x,b.y=this.y+a.y,this},a.prototype.addInPlace=function(a){return this.x+=a.x,this.y+=a.y,this},a.prototype.addVector3=function(b){return new a(this.x+b.x,this.y+b.y)},a.prototype.subtract=function(b){return new a(this.x-b.x,this.y-b.y)},a.prototype.subtractToRef=function(a,b){return b.x=this.x-a.x,b.y=this.y-a.y,this},a.prototype.subtractInPlace=function(a){return this.x-=a.x,this.y-=a.y,this},a.prototype.multiplyInPlace=function(a){return this.x*=a.x,this.y*=a.y,this},a.prototype.multiply=function(b){return new a(this.x*b.x,this.y*b.y)},a.prototype.multiplyToRef=function(a,b){return b.x=this.x*a.x,b.y=this.y*a.y,this},a.prototype.multiplyByFloats=function(b,c){return new a(this.x*b,this.y*c)},a.prototype.divide=function(b){return new a(this.x/b.x,this.y/b.y)},a.prototype.divideToRef=function(a,b){return b.x=this.x/a.x,b.y=this.y/a.y,this},a.prototype.divideInPlace=function(a){return this.divideToRef(a,this)},a.prototype.negate=function(){return new a(-this.x,-this.y)},a.prototype.negateInPlace=function(){return this.x*=-1,this.y*=-1,this},a.prototype.negateToRef=function(a){return a.copyFromFloats(-1*this.x,-1*this.y)},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this},a.prototype.scale=function(b){var c=new a(0,0);return this.scaleToRef(b,c),c},a.prototype.scaleToRef=function(a,b){return b.x=this.x*a,b.y=this.y*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.x+=this.x*a,b.y+=this.y*a,this},a.prototype.equals=function(a){return a&&this.x===a.x&&this.y===a.y},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this.x,a.x,b)&&d.a.WithinEpsilon(this.y,a.y,b)},a.prototype.floor=function(){return new a(Math.floor(this.x),Math.floor(this.y))},a.prototype.fract=function(){return new a(this.x-Math.floor(this.x),this.y-Math.floor(this.y))},a.prototype.rotateToRef=function(a,b){var c=Math.cos(a);a=Math.sin(a);return b.x=c*this.x-a*this.y,b.y=a*this.x+c*this.y,this},a.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},a.prototype.lengthSquared=function(){return this.x*this.x+this.y*this.y},a.prototype.normalize=function(){return a.NormalizeToRef(this,this),this},a.prototype.clone=function(){return new a(this.x,this.y)},a.Zero=function(){return new a(0,0)},a.One=function(){return new a(1,1)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1]},a.CatmullRom=function(b,c,d,e,f){var h=f*f,g=f*h;return new a(.5*(2*c.x+(-b.x+d.x)*f+(2*b.x-5*c.x+4*d.x-e.x)*h+(-b.x+3*c.x-3*d.x+e.x)*g),.5*(2*c.y+(-b.y+d.y)*f+(2*b.y-5*c.y+4*d.y-e.y)*h+(-b.y+3*c.y-3*d.y+e.y)*g))},a.Clamp=function(b,c,d){var e=b.x;e=(e=e>d.x?d.x:e)d.y?d.y:b)c.x?b.x:c.x,b.y>c.y?b.y:c.y)},a.Transform=function(b,c){var d=a.Zero();return a.TransformToRef(b,c,d),d},a.TransformToRef=function(a,b,c){b=b.m;var d=a.x*b[0]+a.y*b[4]+b[12];a=a.x*b[1]+a.y*b[5]+b[13];c.x=d,c.y=a},a.PointInTriangle=function(a,b,c,d){var e=.5*(-c.y*d.x+b.y*(-c.x+d.x)+b.x*(c.y-d.y)+c.x*d.y),f=e<0?-1:1;d=(b.y*d.x-b.x*d.y+(d.y-b.y)*a.x+(b.x-d.x)*a.y)*f;c=(b.x*c.y-b.y*c.x+(b.y-c.y)*a.x+(c.x-b.x)*a.y)*f;return d>0&&c>0&&d+c<2*e*f},a.Distance=function(b,c){return Math.sqrt(a.DistanceSquared(b,c))},a.DistanceSquared=function(a,b){var c=a.x-b.x;a=a.y-b.y;return c*c+a*a},a.Center=function(b,c){return a.CenterToRef(b,c,a.Zero())},a.CenterToRef=function(a,b,c){return c.copyFromFloats((a.x+b.x)/2,(a.y+b.y)/2)},a.DistanceOfPointFromSegment=function(b,c,d){var e=a.DistanceSquared(c,d);if(0===e)return a.Distance(b,c);d=d.subtract(c);e=Math.max(0,Math.min(1,a.Dot(b.subtract(c),d)/e));c=c.add(d.multiplyByFloats(e,e));return a.Distance(b,c)},a}(),i=function(){function a(a,b,c){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),this._isDirty=!0,this._x=a,this._y=b,this._z=c}return Object.defineProperty(a.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"z",{get:function(){return this._z},set:function(a){this._z=a,this._isDirty=!0},enumerable:!1,configurable:!0}),a.prototype.toString=function(){return"{X: "+this._x+" Y: "+this._y+" Z: "+this._z+"}"},a.prototype.getClassName=function(){return"Vector3"},a.prototype.getHashCode=function(){var a=0|this._x;return 397*(397*a^(0|this._y))^(0|this._z)},a.prototype.asArray=function(){var a=[];return this.toArray(a,0),a},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this._x,a[b+1]=this._y,a[b+2]=this._z,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.toQuaternion=function(){return k.RotationYawPitchRoll(this._y,this._x,this._z)},a.prototype.addInPlace=function(a){return this.addInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.addInPlaceFromFloats=function(a,b,c){return this.x+=a,this.y+=b,this.z+=c,this},a.prototype.add=function(b){return new a(this._x+b._x,this._y+b._y,this._z+b._z)},a.prototype.addToRef=function(a,b){return b.copyFromFloats(this._x+a._x,this._y+a._y,this._z+a._z)},a.prototype.subtractInPlace=function(a){return this.x-=a._x,this.y-=a._y,this.z-=a._z,this},a.prototype.subtract=function(b){return new a(this._x-b._x,this._y-b._y,this._z-b._z)},a.prototype.subtractToRef=function(a,b){return this.subtractFromFloatsToRef(a._x,a._y,a._z,b)},a.prototype.subtractFromFloats=function(b,c,d){return new a(this._x-b,this._y-c,this._z-d)},a.prototype.subtractFromFloatsToRef=function(a,b,c,d){return d.copyFromFloats(this._x-a,this._y-b,this._z-c)},a.prototype.negate=function(){return new a(-this._x,-this._y,-this._z)},a.prototype.negateInPlace=function(){return this.x*=-1,this.y*=-1,this.z*=-1,this},a.prototype.negateToRef=function(a){return a.copyFromFloats(-1*this._x,-1*this._y,-1*this._z)},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this.z*=a,this},a.prototype.scale=function(b){return new a(this._x*b,this._y*b,this._z*b)},a.prototype.scaleToRef=function(a,b){return b.copyFromFloats(this._x*a,this._y*a,this._z*a)},a.prototype.scaleAndAddToRef=function(a,b){return b.addInPlaceFromFloats(this._x*a,this._y*a,this._z*a)},a.prototype.projectOnPlane=function(b,c){var d=a.Zero();return this.projectOnPlaneToRef(b,c,d),d},a.prototype.projectOnPlaneToRef=function(b,c,d){var e=b.normal;b=b.d;var g=m.Vector3[0];this.subtractToRef(c,g),g.normalize();var f=a.Dot(g,e);e=-(a.Dot(c,e)+b)/f;b=g.scaleInPlace(e);c.addToRef(b,d)},a.prototype.equals=function(a){return a&&this._x===a._x&&this._y===a._y&&this._z===a._z},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this._x,a._x,b)&&d.a.WithinEpsilon(this._y,a._y,b)&&d.a.WithinEpsilon(this._z,a._z,b)},a.prototype.equalsToFloats=function(a,b,c){return this._x===a&&this._y===b&&this._z===c},a.prototype.multiplyInPlace=function(a){return this.x*=a._x,this.y*=a._y,this.z*=a._z,this},a.prototype.multiply=function(a){return this.multiplyByFloats(a._x,a._y,a._z)},a.prototype.multiplyToRef=function(a,b){return b.copyFromFloats(this._x*a._x,this._y*a._y,this._z*a._z)},a.prototype.multiplyByFloats=function(b,c,d){return new a(this._x*b,this._y*c,this._z*d)},a.prototype.divide=function(b){return new a(this._x/b._x,this._y/b._y,this._z/b._z)},a.prototype.divideToRef=function(a,b){return b.copyFromFloats(this._x/a._x,this._y/a._y,this._z/a._z)},a.prototype.divideInPlace=function(a){return this.divideToRef(a,this)},a.prototype.minimizeInPlace=function(a){return this.minimizeInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.maximizeInPlace=function(a){return this.maximizeInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.minimizeInPlaceFromFloats=function(a,b,c){return athis._x&&(this.x=a),b>this._y&&(this.y=b),c>this._z&&(this.z=c),this},a.prototype.isNonUniformWithinEpsilon=function(a){var b=Math.abs(this._x),c=Math.abs(this._y);if(!d.a.WithinEpsilon(b,c,a))return!0;var e=Math.abs(this._z);return!d.a.WithinEpsilon(b,e,a)||!d.a.WithinEpsilon(c,e,a)},Object.defineProperty(a.prototype,"isNonUniform",{get:function(){var a=Math.abs(this._x);return a!==Math.abs(this._y)||a!==Math.abs(this._z)},enumerable:!1,configurable:!0}),a.prototype.floor=function(){return new a(Math.floor(this._x),Math.floor(this._y),Math.floor(this._z))},a.prototype.fract=function(){return new a(this._x-Math.floor(this._x),this._y-Math.floor(this._y),this._z-Math.floor(this._z))},a.prototype.length=function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z)},a.prototype.lengthSquared=function(){return this._x*this._x+this._y*this._y+this._z*this._z},a.prototype.normalize=function(){return this.normalizeFromLength(this.length())},a.prototype.reorderInPlace=function(a){var b=this;return"xyz"===(a=a.toLowerCase())||(m.Vector3[0].copyFrom(this),["x","y","z"].forEach(function(c,d){b[c]=m.Vector3[0][a[d]]})),this},a.prototype.rotateByQuaternionToRef=function(b,c){return b.toRotationMatrix(m.Matrix[0]),a.TransformCoordinatesToRef(this,m.Matrix[0],c),c},a.prototype.rotateByQuaternionAroundPointToRef=function(a,b,c){return this.subtractToRef(b,m.Vector3[0]),m.Vector3[0].rotateByQuaternionToRef(a,m.Vector3[0]),b.addToRef(m.Vector3[0],c),c},a.prototype.cross=function(b){return a.Cross(this,b)},a.prototype.normalizeFromLength=function(a){return 0===a||1===a?this:this.scaleInPlace(1/a)},a.prototype.normalizeToNew=function(){var b=new a(0,0,0);return this.normalizeToRef(b),b},a.prototype.normalizeToRef=function(a){var b=this.length();return 0===b||1===b?a.copyFromFloats(this._x,this._y,this._z):this.scaleToRef(1/b,a)},a.prototype.clone=function(){return new a(this._x,this._y,this._z)},a.prototype.copyFrom=function(a){return this.copyFromFloats(a._x,a._y,a._z)},a.prototype.copyFromFloats=function(a,b,c){return this.x=a,this.y=b,this.z=c,this},a.prototype.set=function(a,b,c){return this.copyFromFloats(a,b,c)},a.prototype.setAll=function(a){return this.x=this.y=this.z=a,this},a.GetClipFactor=function(b,c,d,e){b=a.Dot(b,d)-e;return b/(b-(a.Dot(c,d)-e))},a.GetAngleBetweenVectors=function(b,c,d){b=b.normalizeToRef(m.Vector3[1]);c=c.normalizeToRef(m.Vector3[2]);var g=a.Dot(b,c),e=Math.acos(g),f=m.Vector3[3];return a.CrossToRef(b,c,f),a.Dot(f,d)>0?isNaN(e)?0:e:isNaN(e)?-Math.PI:-Math.acos(g)},a.GetAngleBetweenVectorsOnPlane=function(b,c,e){m.Vector3[0].copyFrom(b);b=m.Vector3[0];m.Vector3[1].copyFrom(c);c=m.Vector3[1];m.Vector3[2].copyFrom(e);e=m.Vector3[2];var f=m.Vector3[3],g=m.Vector3[4];b.normalize(),c.normalize(),e.normalize(),a.CrossToRef(e,b,f),a.CrossToRef(f,e,g);b=Math.atan2(a.Dot(c,f),a.Dot(c,g));return d.a.NormalizeRadians(b)},a.SlerpToRef=function(b,c,f,k){f=d.a.Clamp(f,0,1);var g=m.Vector3[0],h=m.Vector3[1];g.copyFrom(b),b=g.length(),g.normalizeFromLength(b),h.copyFrom(c),c=h.length(),h.normalizeFromLength(c);var i,j=a.Dot(g,h);if(j<1-e.a){j=Math.acos(j);var l=1/Math.sin(j);i=Math.sin((1-f)*j)*l,j=Math.sin(f*j)*l}else i=1-f,j=f;g.scaleInPlace(i),h.scaleInPlace(j),k.copyFrom(g).addInPlace(h),k.scaleInPlace(d.a.Lerp(b,c,f))},a.SmoothToRef=function(b,c,d,e,f){a.SlerpToRef(b,c,0===e?1:d/e,f)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2])},a.FromFloatArray=function(b,c){return a.FromArray(b,c)},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2]},a.FromFloatArrayToRef=function(b,c,d){return a.FromArrayToRef(b,c,d)},a.FromFloatsToRef=function(a,b,c,d){d.copyFromFloats(a,b,c)},a.Zero=function(){return new a(0,0,0)},a.One=function(){return new a(1,1,1)},a.Up=function(){return new a(0,1,0)},Object.defineProperty(a,"UpReadOnly",{get:function(){return a._UpReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RightReadOnly",{get:function(){return a._RightReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"LeftHandedForwardReadOnly",{get:function(){return a._LeftHandedForwardReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RightHandedForwardReadOnly",{get:function(){return a._RightHandedForwardReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ZeroReadOnly",{get:function(){return a._ZeroReadOnly},enumerable:!1,configurable:!0}),a.Down=function(){return new a(0,-1,0)},a.Forward=function(b){return void 0===b&&(b=!1),new a(0,0,b?-1:1)},a.Backward=function(b){return void 0===b&&(b=!1),new a(0,0,b?1:-1)},a.Right=function(){return new a(1,0,0)},a.Left=function(){return new a(-1,0,0)},a.TransformCoordinates=function(b,c){var d=a.Zero();return a.TransformCoordinatesToRef(b,c,d),d},a.TransformCoordinatesToRef=function(b,c,d){a.TransformCoordinatesFromFloatsToRef(b._x,b._y,b._z,c,d)},a.TransformCoordinatesFromFloatsToRef=function(a,b,c,d,e){d=d.m;var h=a*d[0]+b*d[4]+c*d[8]+d[12],f=a*d[1]+b*d[5]+c*d[9]+d[13],g=a*d[2]+b*d[6]+c*d[10]+d[14];a=1/(a*d[3]+b*d[7]+c*d[11]+d[15]);e.x=h*a,e.y=f*a,e.z=g*a},a.TransformNormal=function(b,c){var d=a.Zero();return a.TransformNormalToRef(b,c,d),d},a.TransformNormalToRef=function(a,b,c){this.TransformNormalFromFloatsToRef(a._x,a._y,a._z,b,c)},a.TransformNormalFromFloatsToRef=function(a,b,c,d,e){d=d.m;e.x=a*d[0]+b*d[4]+c*d[8],e.y=a*d[1]+b*d[5]+c*d[9],e.z=a*d[2]+b*d[6]+c*d[10]},a.CatmullRom=function(b,c,d,e,f){var h=f*f,g=f*h;return new a(.5*(2*c._x+(-b._x+d._x)*f+(2*b._x-5*c._x+4*d._x-e._x)*h+(-b._x+3*c._x-3*d._x+e._x)*g),.5*(2*c._y+(-b._y+d._y)*f+(2*b._y-5*c._y+4*d._y-e._y)*h+(-b._y+3*c._y-3*d._y+e._y)*g),.5*(2*c._z+(-b._z+d._z)*f+(2*b._z-5*c._z+4*d._z-e._z)*h+(-b._z+3*c._z-3*d._z+e._z)*g))},a.Clamp=function(b,c,d){var e=new a;return a.ClampToRef(b,c,d,e),e},a.ClampToRef=function(a,b,c,d){var e=a._x;e=(e=e>c._x?c._x:e)c._y?c._y:f)c._z?c._z:a)0&&o<0?(u.copyFrom(h),v=c,n=f):o>0&&s<0?(u.copyFrom(j),v=f,n=p):(u.copyFrom(i).scaleInPlace(-1),v=p,n=c);q=m.Vector3[9];h=m.Vector3[4];if(v.subtractToRef(t,w),n.subtractToRef(t,q),a.CrossToRef(w,q,h),!(a.Dot(h,k)<0))return g.copyFrom(t),Math.abs(r*l);o=m.Vector3[5];a.CrossToRef(u,h,o),o.normalize();s=m.Vector3[9];s.copyFrom(v).subtractInPlace(t);j=s.length();if(jthis.x&&(this.x=a.x),a.y>this.y&&(this.y=a.y),a.z>this.z&&(this.z=a.z),a.w>this.w&&(this.w=a.w),this},a.prototype.floor=function(){return new a(Math.floor(this.x),Math.floor(this.y),Math.floor(this.z),Math.floor(this.w))},a.prototype.fract=function(){return new a(this.x-Math.floor(this.x),this.y-Math.floor(this.y),this.z-Math.floor(this.z),this.w-Math.floor(this.w))},a.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},a.prototype.lengthSquared=function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},a.prototype.normalize=function(){var a=this.length();return 0===a?this:this.scaleInPlace(1/a)},a.prototype.toVector3=function(){return new i(this.x,this.y,this.z)},a.prototype.clone=function(){return new a(this.x,this.y,this.z,this.w)},a.prototype.copyFrom=function(a){return this.x=a.x,this.y=a.y,this.z=a.z,this.w=a.w,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.x=a,this.y=b,this.z=c,this.w=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.setAll=function(a){return this.x=this.y=this.z=this.w=a,this},a.FromArray=function(b,c){return c||(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2],c.w=a[b+3]},a.FromFloatArrayToRef=function(b,c,d){a.FromArrayToRef(b,c,d)},a.FromFloatsToRef=function(a,b,c,d,e){e.x=a,e.y=b,e.z=c,e.w=d},a.Zero=function(){return new a(0,0,0,0)},a.One=function(){return new a(1,1,1,1)},a.Normalize=function(b){var c=a.Zero();return a.NormalizeToRef(b,c),c},a.NormalizeToRef=function(a,b){b.copyFrom(a),b.normalize()},a.Minimize=function(a,b){a=a.clone();return a.minimizeInPlace(b),a},a.Maximize=function(a,b){a=a.clone();return a.maximizeInPlace(b),a},a.Distance=function(b,c){return Math.sqrt(a.DistanceSquared(b,c))},a.DistanceSquared=function(a,b){var c=a.x-b.x,d=a.y-b.y,e=a.z-b.z;a=a.w-b.w;return c*c+d*d+e*e+a*a},a.Center=function(b,c){return a.CenterToRef(b,c,a.Zero())},a.CenterToRef=function(a,b,c){return c.copyFromFloats((a.x+b.x)/2,(a.y+b.y)/2,(a.z+b.z)/2,(a.w+b.w)/2)},a.TransformCoordinates=function(b,c){var d=a.Zero();return a.TransformCoordinatesToRef(b,c,d),d},a.TransformCoordinatesToRef=function(b,c,d){a.TransformCoordinatesFromFloatsToRef(b._x,b._y,b._z,c,d)},a.TransformCoordinatesFromFloatsToRef=function(a,b,c,d,e){d=d.m;var h=a*d[0]+b*d[4]+c*d[8]+d[12],f=a*d[1]+b*d[5]+c*d[9]+d[13],g=a*d[2]+b*d[6]+c*d[10]+d[14];a=a*d[3]+b*d[7]+c*d[11]+d[15];e.x=h,e.y=f,e.z=g,e.w=a},a.TransformNormal=function(b,c){var d=a.Zero();return a.TransformNormalToRef(b,c,d),d},a.TransformNormalToRef=function(a,b,c){b=b.m;var d=a.x*b[0]+a.y*b[4]+a.z*b[8],e=a.x*b[1]+a.y*b[5]+a.z*b[9];b=a.x*b[2]+a.y*b[6]+a.z*b[10];c.x=d,c.y=e,c.z=b,c.w=a.w},a.TransformNormalFromFloatsToRef=function(a,b,c,d,e,f){e=e.m;f.x=a*e[0]+b*e[4]+c*e[8],f.y=a*e[1]+b*e[5]+c*e[9],f.z=a*e[2]+b*e[6]+c*e[10],f.w=d},a.FromVector3=function(b,c){return void 0===c&&(c=0),new a(b._x,b._y,b._z,c)},a}(),k=function(){function a(a,b,c,d){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),this._isDirty=!0,this._x=a,this._y=b,this._z=c,this._w=d}return Object.defineProperty(a.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"z",{get:function(){return this._z},set:function(a){this._z=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"w",{get:function(){return this._w},set:function(a){this._w=a,this._isDirty=!0},enumerable:!1,configurable:!0}),a.prototype.toString=function(){return"{X: "+this._x+" Y: "+this._y+" Z: "+this._z+" W: "+this._w+"}"},a.prototype.getClassName=function(){return"Quaternion"},a.prototype.getHashCode=function(){var a=0|this._x;return 397*(397*(397*a^(0|this._y))^(0|this._z))^(0|this._w)},a.prototype.asArray=function(){return[this._x,this._y,this._z,this._w]},a.prototype.equals=function(a){return a&&this._x===a._x&&this._y===a._y&&this._z===a._z&&this._w===a._w},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this._x,a._x,b)&&d.a.WithinEpsilon(this._y,a._y,b)&&d.a.WithinEpsilon(this._z,a._z,b)&&d.a.WithinEpsilon(this._w,a._w,b)},a.prototype.clone=function(){return new a(this._x,this._y,this._z,this._w)},a.prototype.copyFrom=function(a){return this.x=a._x,this.y=a._y,this.z=a._z,this.w=a._w,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.x=a,this.y=b,this.z=c,this.w=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.add=function(b){return new a(this._x+b._x,this._y+b._y,this._z+b._z,this._w+b._w)},a.prototype.addInPlace=function(a){return this._x+=a._x,this._y+=a._y,this._z+=a._z,this._w+=a._w,this},a.prototype.subtract=function(b){return new a(this._x-b._x,this._y-b._y,this._z-b._z,this._w-b._w)},a.prototype.scale=function(b){return new a(this._x*b,this._y*b,this._z*b,this._w*b)},a.prototype.scaleToRef=function(a,b){return b.x=this._x*a,b.y=this._y*a,b.z=this._z*a,b.w=this._w*a,this},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this.z*=a,this.w*=a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.x+=this._x*a,b.y+=this._y*a,b.z+=this._z*a,b.w+=this._w*a,this},a.prototype.multiply=function(b){var c=new a(0,0,0,1);return this.multiplyToRef(b,c),c},a.prototype.multiplyToRef=function(a,b){var c=this._x*a._w+this._y*a._z-this._z*a._y+this._w*a._x,d=-this._x*a._z+this._y*a._w+this._z*a._x+this._w*a._y,e=this._x*a._y-this._y*a._x+this._z*a._w+this._w*a._z;a=-this._x*a._x-this._y*a._y-this._z*a._z+this._w*a._w;return b.copyFromFloats(c,d,e,a),this},a.prototype.multiplyInPlace=function(a){return this.multiplyToRef(a,this),this},a.prototype.conjugateToRef=function(a){return a.copyFromFloats(-this._x,-this._y,-this._z,this._w),this},a.prototype.conjugateInPlace=function(){return this.x*=-1,this.y*=-1,this.z*=-1,this},a.prototype.conjugate=function(){return new a(-this._x,-this._y,-this._z,this._w)},a.prototype.length=function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},a.prototype.normalize=function(){var a=this.length();if(0===a)return this;a=1/a;return this.x*=a,this.y*=a,this.z*=a,this.w*=a,this},a.prototype.toEulerAngles=function(){var a=i.Zero();return this.toEulerAnglesToRef(a),a},a.prototype.toEulerAnglesToRef=function(a){var b=this._z,c=this._x,d=this._y,e=this._w,f=e*e,j=b*b,g=c*c,h=d*d,i=d*b-c*e;return i<-.4999999?(a.y=2*Math.atan2(d,e),a.x=Math.PI/2,a.z=0):i>.4999999?(a.y=2*Math.atan2(d,e),a.x=-Math.PI/2,a.z=0):(a.z=Math.atan2(2*(c*d+b*e),-j-g+h+f),a.x=Math.asin(-2*(b*d-c*e)),a.y=Math.atan2(2*(b*c+d*e),j-g-h+f)),this},a.prototype.toRotationMatrix=function(a){return l.FromQuaternionToRef(this,a),this},a.prototype.fromRotationMatrix=function(b){return a.FromRotationMatrixToRef(b,this),this},a.FromRotationMatrix=function(b){var c=new a;return a.FromRotationMatrixToRef(b,c),c},a.FromRotationMatrixToRef=function(a,b){a=a.m;var c=a[0],d=a[4],k=a[8],e=a[1],f=a[5],g=a[9],h=a[2],i=a[6];a=a[10];var j=c+f+a;j>0?(j=.5/Math.sqrt(j+1),b.w=.25/j,b.x=(i-g)*j,b.y=(k-h)*j,b.z=(e-d)*j):c>f&&c>a?(j=2*Math.sqrt(1+c-f-a),b.w=(i-g)/j,b.x=.25*j,b.y=(d+e)/j,b.z=(k+h)/j):f>a?(j=2*Math.sqrt(1+f-c-a),b.w=(k-h)/j,b.x=(d+e)/j,b.y=.25*j,b.z=(g+i)/j):(j=2*Math.sqrt(1+a-c-f),b.w=(e-d)/j,b.x=(k+h)/j,b.y=(g+i)/j,b.z=.25*j)},a.Dot=function(a,b){return a._x*b._x+a._y*b._y+a._z*b._z+a._w*b._w},a.AreClose=function(b,c){return a.Dot(b,c)>=0},a.SmoothToRef=function(b,c,e,f,g){e=0===f?1:e/f;e=d.a.Clamp(e,0,1),a.SlerpToRef(b,c,e,g)},a.Zero=function(){return new a(0,0,0,0)},a.Inverse=function(b){return new a(-b._x,-b._y,-b._z,b._w)},a.InverseToRef=function(a,b){return b.set(-a._x,-a._y,-a._z,a._w),b},a.Identity=function(){return new a(0,0,0,1)},a.IsIdentity=function(a){return a&&0===a._x&&0===a._y&&0===a._z&&1===a._w},a.RotationAxis=function(b,c){return a.RotationAxisToRef(b,c,new a)},a.RotationAxisToRef=function(a,b,c){var d=Math.sin(b/2);return a.normalize(),c.w=Math.cos(b/2),c.x=a._x*d,c.y=a._y*d,c.z=a._z*d,c},a.FromArray=function(b,c){return c||(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2],c.w=a[b+3]},a.FromEulerAngles=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(c,b,d,e),e},a.FromEulerAnglesToRef=function(b,c,d,e){return a.RotationYawPitchRollToRef(c,b,d,e),e},a.FromEulerVector=function(b){var c=new a;return a.RotationYawPitchRollToRef(b._y,b._x,b._z,c),c},a.FromEulerVectorToRef=function(b,c){return a.RotationYawPitchRollToRef(b._y,b._x,b._z,c),c},a.FromUnitVectorsToRef=function(a,b,c){var d=i.Dot(a,b)+1;return dMath.abs(a.z)?c.set(-a.y,a.x,0,0):c.set(0,-a.z,a.y,0):(i.CrossToRef(a,b,n.Vector3[0]),c.set(n.Vector3[0].x,n.Vector3[0].y,n.Vector3[0].z,d)),c.normalize()},a.RotationYawPitchRoll=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(b,c,d,e),e},a.RotationYawPitchRollToRef=function(a,b,c,d){c=.5*c;b=.5*b;a=.5*a;var e=Math.sin(c);c=Math.cos(c);var f=Math.sin(b);b=Math.cos(b);var g=Math.sin(a);a=Math.cos(a);d.x=a*f*c+g*b*e,d.y=g*b*c-a*f*e,d.z=a*b*e-g*f*c,d.w=a*b*c+g*f*e},a.RotationAlphaBetaGamma=function(b,c,d){var e=new a;return a.RotationAlphaBetaGammaToRef(b,c,d,e),e},a.RotationAlphaBetaGammaToRef=function(a,b,c,d){var e=.5*(c+a);c=.5*(c-a);a=.5*b;d.x=Math.cos(c)*Math.sin(a),d.y=Math.sin(c)*Math.sin(a),d.z=Math.sin(e)*Math.cos(a),d.w=Math.cos(e)*Math.cos(a)},a.RotationQuaternionFromAxis=function(b,c,d){var e=new a(0,0,0,0);return a.RotationQuaternionFromAxisToRef(b,c,d,e),e},a.RotationQuaternionFromAxisToRef=function(b,c,d,e){var f=m.Matrix[0];l.FromXYZAxesToRef(b.normalize(),c.normalize(),d.normalize(),f),a.FromRotationMatrixToRef(f,e)},a.FromLookDirectionLH=function(b,c){var d=new a;return a.FromLookDirectionLHToRef(b,c,d),d},a.FromLookDirectionLHToRef=function(b,c,d){var e=m.Matrix[0];l.LookDirectionLHToRef(b,c,e),a.FromRotationMatrixToRef(e,d)},a.FromLookDirectionRH=function(b,c){var d=new a;return a.FromLookDirectionRHToRef(b,c,d),d},a.FromLookDirectionRHToRef=function(b,c,d){var e=m.Matrix[0];return l.LookDirectionRHToRef(b,c,e),a.FromRotationMatrixToRef(e,d)},a.Slerp=function(b,c,d){var e=a.Identity();return a.SlerpToRef(b,c,d,e),e},a.SlerpToRef=function(a,b,c,d){var e,f,i=a._x*b._x+a._y*b._y+a._z*b._z+a._w*b._w,g=!1;if(i<0&&(g=!0,i=-i),i>.999999)f=1-c,e=g?-c:c;else{i=Math.acos(i);var h=1/Math.sin(i);f=Math.sin((1-c)*i)*h,e=g?-Math.sin(c*i)*h:Math.sin(c*i)*h}d.x=f*a._x+e*b._x,d.y=f*a._y+e*b._y,d.z=f*a._z+e*b._z,d.w=f*a._w+e*b._w},a.Hermite=function(b,c,d,e,f){var j=f*f,g=f*j,h=2*g-3*j+1,i=-2*g+3*j;f=g-2*j+f;g=g-j;return new a(b._x*h+d._x*i+c._x*f+e._x*g,b._y*h+d._y*i+c._y*f+e._y*g,b._z*h+d._z*i+c._z*f+e._z*g,b._w*h+d._w*i+c._w*f+e._w*g)},a.Hermite1stDerivative=function(b,c,d,e,f){var g=a.Zero();return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.x=6*(g-e)*a.x+(3*g-4*e+1)*b.x+6*(-g+e)*c.x+(3*g-2*e)*d.x,f.y=6*(g-e)*a.y+(3*g-4*e+1)*b.y+6*(-g+e)*c.y+(3*g-2*e)*d.y,f.z=6*(g-e)*a.z+(3*g-4*e+1)*b.z+6*(-g+e)*c.z+(3*g-2*e)*d.z,f.w=6*(g-e)*a.w+(3*g-4*e+1)*b.w+6*(-g+e)*c.w+(3*g-2*e)*d.w},a}(),l=function(){function a(){this._isIdentity=!1,this._isIdentityDirty=!0,this._isIdentity3x2=!0,this._isIdentity3x2Dirty=!0,this.updateFlag=-1,g.a.MatrixTrackPrecisionChange&&g.a.MatrixTrackedMatrices.push(this),this._m=new g.a.MatrixCurrentType(16),this._markAsUpdated()}return Object.defineProperty(a,"Use64Bits",{get:function(){return g.a.MatrixUse64Bits},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"m",{get:function(){return this._m},enumerable:!1,configurable:!0}),a.prototype._markAsUpdated=function(){this.updateFlag=a._updateFlagSeed++,this._isIdentity=!1,this._isIdentity3x2=!1,this._isIdentityDirty=!0,this._isIdentity3x2Dirty=!0},a.prototype._updateIdentityStatus=function(a,b,c,d){void 0===b&&(b=!1),void 0===c&&(c=!1),void 0===d&&(d=!0),this._isIdentity=a,this._isIdentity3x2=a||c,this._isIdentityDirty=!this._isIdentity&&b,this._isIdentity3x2Dirty=!this._isIdentity3x2&&d},a.prototype.isIdentity=function(){if(this._isIdentityDirty){this._isIdentityDirty=!1;var a=this._m;this._isIdentity=1===a[0]&&0===a[1]&&0===a[2]&&0===a[3]&&0===a[4]&&1===a[5]&&0===a[6]&&0===a[7]&&0===a[8]&&0===a[9]&&1===a[10]&&0===a[11]&&0===a[12]&&0===a[13]&&0===a[14]&&1===a[15]}return this._isIdentity},a.prototype.isIdentityAs3x2=function(){return this._isIdentity3x2Dirty&&(this._isIdentity3x2Dirty=!1,1!==this._m[0]||1!==this._m[5]||1!==this._m[15]||0!==this._m[1]||0!==this._m[2]||0!==this._m[3]||0!==this._m[4]||0!==this._m[6]||0!==this._m[7]||0!==this._m[8]||0!==this._m[9]||0!==this._m[10]||0!==this._m[11]||0!==this._m[12]||0!==this._m[13]||0!==this._m[14]?this._isIdentity3x2=!1:this._isIdentity3x2=!0),this._isIdentity3x2},a.prototype.determinant=function(){if(!0===this._isIdentity)return 1;var a=this._m,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],p=a[5],g=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11],m=a[12],n=a[13],o=a[14];a=a[15];var q=k*a-o*l,r=j*a-n*l,s=j*o-n*k;a=i*a-m*l;l=i*o-k*m;o=i*n-m*j;return b*+(p*q-g*r+h*s)+c*-(f*q-g*a+h*l)+d*+(f*r-p*a+h*o)+e*-(f*s-p*l+g*o)},a.prototype.toArray=function(){return this._m},a.prototype.asArray=function(){return this._m},a.prototype.invert=function(){return this.invertToRef(this),this},a.prototype.reset=function(){return a.FromValuesToRef(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,this),this._updateIdentityStatus(!1),this},a.prototype.add=function(b){var c=new a;return this.addToRef(b,c),c},a.prototype.addToRef=function(a,b){for(var c=this._m,d=b._m,a=a.m,e=0;e<16;e++)d[e]=c[e]+a[e];return b._markAsUpdated(),this},a.prototype.addToSelf=function(a){for(var b=this._m,a=a.m,c=0;c<16;c++)b[c]+=a[c];return this._markAsUpdated(),this},a.prototype.invertToRef=function(b){if(!0===this._isIdentity)return a.IdentityToRef(b),this;var c=this._m,d=c[0],e=c[1],f=c[2],p=c[3],g=c[4],h=c[5],i=c[6],j=c[7],k=c[8],l=c[9],m=c[10],n=c[11],o=c[12],q=c[13],r=c[14];c=c[15];var s=m*c-r*n,t=l*c-q*n,u=l*r-q*m,v=k*c-o*n,w=k*r-m*o,x=k*q-o*l,y=+(h*s-i*t+j*u),z=-(g*s-i*v+j*w),A=+(g*t-h*v+j*x),B=-(g*u-h*w+i*x),C=d*y+e*z+f*A+p*B;if(0===C)return b.copyFrom(this),this;C=1/C;var D=i*c-r*j,E=h*c-q*j,F=h*r-q*i;c=g*c-o*j;r=g*r-o*i;q=g*q-o*h;o=i*n-m*j;var G=h*n-l*j,H=h*m-l*i;n=g*n-k*j;j=g*m-k*i;m=g*l-k*h;i=-(e*s-f*t+p*u);g=+(d*s-f*v+p*w);l=-(d*t-e*v+p*x);k=+(d*u-e*w+f*x);h=+(e*D-f*E+p*F);s=-(d*D-f*c+p*r);t=+(d*E-e*c+p*q);v=-(d*F-e*r+f*q);u=-(e*o-f*G+p*H);w=+(d*o-f*n+p*j);x=-(d*G-e*n+p*m);D=+(d*H-e*j+f*m);return a.FromValuesToRef(y*C,i*C,h*C,u*C,z*C,g*C,s*C,w*C,A*C,l*C,t*C,x*C,B*C,k*C,v*C,D*C,b),this},a.prototype.addAtIndex=function(a,b){return this._m[a]+=b,this._markAsUpdated(),this},a.prototype.multiplyAtIndex=function(a,b){return this._m[a]*=b,this._markAsUpdated(),this},a.prototype.setTranslationFromFloats=function(a,b,c){return this._m[12]=a,this._m[13]=b,this._m[14]=c,this._markAsUpdated(),this},a.prototype.addTranslationFromFloats=function(a,b,c){return this._m[12]+=a,this._m[13]+=b,this._m[14]+=c,this._markAsUpdated(),this},a.prototype.setTranslation=function(a){return this.setTranslationFromFloats(a._x,a._y,a._z)},a.prototype.getTranslation=function(){return new i(this._m[12],this._m[13],this._m[14])},a.prototype.getTranslationToRef=function(a){return a.x=this._m[12],a.y=this._m[13],a.z=this._m[14],this},a.prototype.removeRotationAndScaling=function(){var b=this.m;return a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,b[12],b[13],b[14],b[15],this),this._updateIdentityStatus(0===b[12]&&0===b[13]&&0===b[14]&&1===b[15]),this},a.prototype.multiply=function(b){var c=new a;return this.multiplyToRef(b,c),c},a.prototype.copyFrom=function(a){a.copyToArray(this._m);a=a;return this.updateFlag=a.updateFlag,this._updateIdentityStatus(a._isIdentity,a._isIdentityDirty,a._isIdentity3x2,a._isIdentity3x2Dirty),this},a.prototype.copyToArray=function(a,b){void 0===b&&(b=0);var c=this._m;return a[b]=c[0],a[b+1]=c[1],a[b+2]=c[2],a[b+3]=c[3],a[b+4]=c[4],a[b+5]=c[5],a[b+6]=c[6],a[b+7]=c[7],a[b+8]=c[8],a[b+9]=c[9],a[b+10]=c[10],a[b+11]=c[11],a[b+12]=c[12],a[b+13]=c[13],a[b+14]=c[14],a[b+15]=c[15],this},a.prototype.multiplyToRef=function(a,b){return this._isIdentity?(b.copyFrom(a),this):a._isIdentity?(b.copyFrom(this),this):(this.multiplyToArray(a,b._m,0),b._markAsUpdated(),this)},a.prototype.multiplyToArray=function(a,b,c){var d=this._m;a=a.m;var e=d[0],p=d[1],f=d[2],g=d[3],h=d[4],i=d[5],j=d[6],k=d[7],m=d[8],n=d[9],o=d[10],q=d[11],r=d[12],s=d[13],t=d[14];d=d[15];var u=a[0],v=a[1],w=a[2],x=a[3],y=a[4],z=a[5],A=a[6],B=a[7],C=a[8],D=a[9],E=a[10],F=a[11],G=a[12],H=a[13],I=a[14];a=a[15];return b[c]=e*u+p*y+f*C+g*G,b[c+1]=e*v+p*z+f*D+g*H,b[c+2]=e*w+p*A+f*E+g*I,b[c+3]=e*x+p*B+f*F+g*a,b[c+4]=h*u+i*y+j*C+k*G,b[c+5]=h*v+i*z+j*D+k*H,b[c+6]=h*w+i*A+j*E+k*I,b[c+7]=h*x+i*B+j*F+k*a,b[c+8]=m*u+n*y+o*C+q*G,b[c+9]=m*v+n*z+o*D+q*H,b[c+10]=m*w+n*A+o*E+q*I,b[c+11]=m*x+n*B+o*F+q*a,b[c+12]=r*u+s*y+t*C+d*G,b[c+13]=r*v+s*z+t*D+d*H,b[c+14]=r*w+s*A+t*E+d*I,b[c+15]=r*x+s*B+t*F+d*a,this},a.prototype.equals=function(a){a=a;if(!a)return!1;if((this._isIdentity||a._isIdentity)&&!this._isIdentityDirty&&!a._isIdentityDirty)return this._isIdentity&&a._isIdentity;var b=this.m;a=a.m;return b[0]===a[0]&&b[1]===a[1]&&b[2]===a[2]&&b[3]===a[3]&&b[4]===a[4]&&b[5]===a[5]&&b[6]===a[6]&&b[7]===a[7]&&b[8]===a[8]&&b[9]===a[9]&&b[10]===a[10]&&b[11]===a[11]&&b[12]===a[12]&&b[13]===a[13]&&b[14]===a[14]&&b[15]===a[15]},a.prototype.clone=function(){var b=new a;return b.copyFrom(this),b},a.prototype.getClassName=function(){return"Matrix"},a.prototype.getHashCode=function(){for(var a=0|this._m[0],b=1;b<16;b++)a=397*a^(0|this._m[b]);return a},a.prototype.decomposeToTransformNode=function(a){return a.rotationQuaternion=a.rotationQuaternion||new k,this.decompose(a.scaling,a.rotationQuaternion,a.position)},a.prototype.decompose=function(b,c,d){if(this._isIdentity)return d&&d.setAll(0),b&&b.setAll(1),c&&c.copyFromFloats(0,0,0,1),!0;var e=this._m;if(d&&d.copyFromFloats(e[12],e[13],e[14]),(b=b||m.Vector3[0]).x=Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]),b.y=Math.sqrt(e[4]*e[4]+e[5]*e[5]+e[6]*e[6]),b.z=Math.sqrt(e[8]*e[8]+e[9]*e[9]+e[10]*e[10]),this.determinant()<=0&&(b.y*=-1),0===b._x||0===b._y||0===b._z)return c&&c.copyFromFloats(0,0,0,1),!1;if(c){d=1/b._x;var f=1/b._y;b=1/b._z;a.FromValuesToRef(e[0]*d,e[1]*d,e[2]*d,0,e[4]*f,e[5]*f,e[6]*f,0,e[8]*b,e[9]*b,e[10]*b,0,0,0,0,1,m.Matrix[0]),k.FromRotationMatrixToRef(m.Matrix[0],c)}return!0},a.prototype.getRow=function(a){if(a<0||a>3)return null;a=4*a;return new j(this._m[a+0],this._m[a+1],this._m[a+2],this._m[a+3])},a.prototype.setRow=function(a,b){return this.setRowFromFloats(a,b.x,b.y,b.z,b.w)},a.prototype.transpose=function(){return a.Transpose(this)},a.prototype.transposeToRef=function(b){return a.TransposeToRef(this,b),this},a.prototype.setRowFromFloats=function(a,b,c,d,e){if(a<0||a>3)return this;a=4*a;return this._m[a+0]=b,this._m[a+1]=c,this._m[a+2]=d,this._m[a+3]=e,this._markAsUpdated(),this},a.prototype.scale=function(b){var c=new a;return this.scaleToRef(b,c),c},a.prototype.scaleToRef=function(a,b){for(var c=0;c<16;c++)b._m[c]=this._m[c]*a;return b._markAsUpdated(),this},a.prototype.scaleAndAddToRef=function(a,b){for(var c=0;c<16;c++)b._m[c]+=this._m[c]*a;return b._markAsUpdated(),this},a.prototype.toNormalMatrix=function(b){var c=m.Matrix[0];this.invertToRef(c),c.transposeToRef(b);c=b._m;a.FromValuesToRef(c[0],c[1],c[2],0,c[4],c[5],c[6],0,c[8],c[9],c[10],0,0,0,0,1,b)},a.prototype.getRotationMatrix=function(){var b=new a;return this.getRotationMatrixToRef(b),b},a.prototype.getRotationMatrixToRef=function(b){var c=m.Vector3[0];if(!this.decompose(c))return a.IdentityToRef(b),this;var d=this._m,e=1/c._x,f=1/c._y;c=1/c._z;return a.FromValuesToRef(d[0]*e,d[1]*e,d[2]*e,0,d[4]*f,d[5]*f,d[6]*f,0,d[8]*c,d[9]*c,d[10]*c,0,0,0,0,1,b),this},a.prototype.toggleModelMatrixHandInPlace=function(){var a=this._m;a[2]*=-1,a[6]*=-1,a[8]*=-1,a[9]*=-1,a[14]*=-1,this._markAsUpdated()},a.prototype.toggleProjectionMatrixHandInPlace=function(){var a=this._m;a[8]*=-1,a[9]*=-1,a[10]*=-1,a[11]*=-1,this._markAsUpdated()},a.FromArray=function(b,c){void 0===c&&(c=0);var d=new a;return a.FromArrayToRef(b,c,d),d},a.FromArrayToRef=function(a,b,c){for(var d=0;d<16;d++)c._m[d]=a[d+b];c._markAsUpdated()},a.FromFloat32ArrayToRefScaled=function(a,b,c,d){for(var e=0;e<16;e++)d._m[e]=a[e+b]*c;d._markAsUpdated()},Object.defineProperty(a,"IdentityReadOnly",{get:function(){return a._identityReadOnly},enumerable:!1,configurable:!0}),a.FromValuesToRef=function(a,b,c,d,e,f,p,g,h,i,j,k,l,m,n,o,q){var r=q._m;r[0]=a,r[1]=b,r[2]=c,r[3]=d,r[4]=e,r[5]=f,r[6]=p,r[7]=g,r[8]=h,r[9]=i,r[10]=j,r[11]=k,r[12]=l,r[13]=m,r[14]=n,r[15]=o,q._markAsUpdated()},a.FromValues=function(b,c,d,e,f,p,g,h,i,j,k,l,m,n,o,q){var r=new a,s=r._m;return s[0]=b,s[1]=c,s[2]=d,s[3]=e,s[4]=f,s[5]=p,s[6]=g,s[7]=h,s[8]=i,s[9]=j,s[10]=k,s[11]=l,s[12]=m,s[13]=n,s[14]=o,s[15]=q,r._markAsUpdated(),r},a.Compose=function(b,c,d){var e=new a;return a.ComposeToRef(b,c,d,e),e},a.ComposeToRef=function(a,b,c,d){var e=d._m,f=b._x,p=b._y,g=b._z;b=b._w;var h=f+f,i=p+p,j=g+g,k=f*h,n=f*i;f=f*j;var o=p*i;p=p*j;g=g*j;h=b*h;i=b*i;b=b*j;j=a._x;var q=a._y;a=a._z;e[0]=(1-(o+g))*j,e[1]=(n+b)*j,e[2]=(f-i)*j,e[3]=0,e[4]=(n-b)*q,e[5]=(1-(k+g))*q,e[6]=(p+h)*q,e[7]=0,e[8]=(f+i)*a,e[9]=(p-h)*a,e[10]=(1-(k+o))*a,e[11]=0,e[12]=c._x,e[13]=c._y,e[14]=c._z,e[15]=1,d._markAsUpdated()},a.Identity=function(){var b=a.FromValues(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return b._updateIdentityStatus(!0),b},a.IdentityToRef=function(b){a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,b),b._updateIdentityStatus(!0)},a.Zero=function(){var b=a.FromValues(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);return b._updateIdentityStatus(!1),b},a.RotationX=function(b){var c=new a;return a.RotationXToRef(b,c),c},a.Invert=function(b){var c=new a;return b.invertToRef(c),c},a.RotationXToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(1,0,0,0,0,b,d,0,0,-d,b,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationY=function(b){var c=new a;return a.RotationYToRef(b,c),c},a.RotationYToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(b,0,-d,0,0,1,0,0,d,0,b,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationZ=function(b){var c=new a;return a.RotationZToRef(b,c),c},a.RotationZToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(b,d,0,0,-d,b,0,0,0,0,1,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationAxis=function(b,c){var d=new a;return a.RotationAxisToRef(b,c,d),d},a.RotationAxisToRef=function(a,b,c){var d=Math.sin(-b);b=Math.cos(-b);var e=1-b;a.normalize();var f=c._m;f[0]=a._x*a._x*e+b,f[1]=a._x*a._y*e-a._z*d,f[2]=a._x*a._z*e+a._y*d,f[3]=0,f[4]=a._y*a._x*e+a._z*d,f[5]=a._y*a._y*e+b,f[6]=a._y*a._z*e-a._x*d,f[7]=0,f[8]=a._z*a._x*e-a._y*d,f[9]=a._z*a._y*e+a._x*d,f[10]=a._z*a._z*e+b,f[11]=0,f[12]=0,f[13]=0,f[14]=0,f[15]=1,c._markAsUpdated()},a.RotationAlignToRef=function(a,b,c){var d=i.Dot(b,a),f=c._m;if(d<-1+e.a)f[0]=-1,f[1]=0,f[2]=0,f[3]=0,f[4]=0,f[5]=-1,f[6]=0,f[7]=0,f[8]=0,f[9]=0,f[10]=1,f[11]=0;else{b=i.Cross(b,a);a=1/(1+d);f[0]=b._x*b._x*a+d,f[1]=b._y*b._x*a-b._z,f[2]=b._z*b._x*a+b._y,f[3]=0,f[4]=b._x*b._y*a+b._z,f[5]=b._y*b._y*a+d,f[6]=b._z*b._y*a-b._x,f[7]=0,f[8]=b._x*b._z*a-b._y,f[9]=b._y*b._z*a+b._x,f[10]=b._z*b._z*a+d,f[11]=0}f[12]=0,f[13]=0,f[14]=0,f[15]=1,c._markAsUpdated()},a.RotationYawPitchRoll=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(b,c,d,e),e},a.RotationYawPitchRollToRef=function(a,b,c,d){k.RotationYawPitchRollToRef(a,b,c,m.Quaternion[0]),m.Quaternion[0].toRotationMatrix(d)},a.Scaling=function(b,c,d){var e=new a;return a.ScalingToRef(b,c,d,e),e},a.ScalingToRef=function(b,c,d,e){a.FromValuesToRef(b,0,0,0,0,c,0,0,0,0,d,0,0,0,0,1,e),e._updateIdentityStatus(1===b&&1===c&&1===d)},a.Translation=function(b,c,d){var e=new a;return a.TranslationToRef(b,c,d,e),e},a.TranslationToRef=function(b,c,d,e){a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,b,c,d,1,e),e._updateIdentityStatus(0===b&&0===c&&0===d)},a.Lerp=function(b,c,d){var e=new a;return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){for(var e=d._m,a=a.m,b=b.m,f=0;f<16;f++)e[f]=a[f]*(1-c)+b[f]*c;d._markAsUpdated()},a.DecomposeLerp=function(b,c,d){var e=new a;return a.DecomposeLerpToRef(b,c,d,e),e},a.DecomposeLerpToRef=function(b,c,d,e){var f=m.Vector3[0],n=m.Quaternion[0],g=m.Vector3[1];b.decompose(f,n,g);b=m.Vector3[2];var h=m.Quaternion[1],j=m.Vector3[3];c.decompose(b,h,j);c=m.Vector3[4];i.LerpToRef(f,b,d,c);f=m.Quaternion[2];k.SlerpToRef(n,h,d,f);b=m.Vector3[5];i.LerpToRef(g,j,d,b),a.ComposeToRef(c,f,b,e)},a.LookAtLH=function(b,c,d){var e=new a;return a.LookAtLHToRef(b,c,d,e),e},a.LookAtLHToRef=function(b,c,d,e){var f=m.Vector3[0],h=m.Vector3[1],g=m.Vector3[2];c.subtractToRef(b,g),g.normalize(),i.CrossToRef(d,g,f);c=f.lengthSquared();0===c?f.x=1:f.normalizeFromLength(Math.sqrt(c)),i.CrossToRef(g,f,h),h.normalize();d=-i.Dot(f,b);c=-i.Dot(h,b);b=-i.Dot(g,b);a.FromValuesToRef(f._x,h._x,g._x,0,f._y,h._y,g._y,0,f._z,h._z,g._z,0,d,c,b,1,e)},a.LookAtRH=function(b,c,d){var e=new a;return a.LookAtRHToRef(b,c,d,e),e},a.LookAtRHToRef=function(b,c,d,e){var f=m.Vector3[0],h=m.Vector3[1],g=m.Vector3[2];b.subtractToRef(c,g),g.normalize(),i.CrossToRef(d,g,f);c=f.lengthSquared();0===c?f.x=1:f.normalizeFromLength(Math.sqrt(c)),i.CrossToRef(g,f,h),h.normalize();d=-i.Dot(f,b);c=-i.Dot(h,b);b=-i.Dot(g,b);a.FromValuesToRef(f._x,h._x,g._x,0,f._y,h._y,g._y,0,f._z,h._z,g._z,0,d,c,b,1,e)},a.LookDirectionLH=function(b,c){var d=new a;return a.LookDirectionLHToRef(b,c,d),d},a.LookDirectionLHToRef=function(b,c,d){var e=m.Vector3[0];e.copyFrom(b),e.scaleInPlace(-1);b=m.Vector3[1];i.CrossToRef(c,e,b),a.FromValuesToRef(b._x,b._y,b._z,0,c._x,c._y,c._z,0,e._x,e._y,e._z,0,0,0,0,1,d)},a.LookDirectionRH=function(b,c){var d=new a;return a.LookDirectionRHToRef(b,c,d),d},a.LookDirectionRHToRef=function(b,c,d){var e=m.Vector3[2];i.CrossToRef(c,b,e),a.FromValuesToRef(e._x,e._y,e._z,0,c._x,c._y,c._z,0,b._x,b._y,b._z,0,0,0,0,1,d)},a.OrthoLH=function(b,c,d,e,f){var g=new a;return a.OrthoLHToRef(b,c,d,e,g,f),g},a.OrthoLHToRef=function(b,c,d,e,f,h){b=2/b;c=2/c;var g=2/(e-d);e=-(e+d)/(e-d);a.FromValuesToRef(b,0,0,0,0,c,0,0,0,0,g,0,0,0,e,1,f),h&&f.multiplyToRef(o,f),f._updateIdentityStatus(1===b&&1===c&&1===g&&0===e)},a.OrthoOffCenterLH=function(b,c,d,e,f,i,g){var h=new a;return a.OrthoOffCenterLHToRef(b,c,d,e,f,i,h,g),h},a.OrthoOffCenterLHToRef=function(b,c,d,e,f,l,g,h){var i=2/(c-b),j=2/(e-d),k=2/(l-f);l=-(l+f)/(l-f);f=(b+c)/(b-c);b=(e+d)/(d-e);a.FromValuesToRef(i,0,0,0,0,j,0,0,0,0,k,0,f,b,l,1,g),h&&g.multiplyToRef(o,g),g._markAsUpdated()},a.OrthoOffCenterRH=function(b,c,d,e,f,i,g){var h=new a;return a.OrthoOffCenterRHToRef(b,c,d,e,f,i,h,g),h},a.OrthoOffCenterRHToRef=function(b,c,d,e,f,i,g,h){a.OrthoOffCenterLHToRef(b,c,d,e,f,i,g,h),g._m[10]*=-1},a.PerspectiveLH=function(b,c,d,e,f,i){void 0===i&&(i=0);var g=new a;b=2*d/b;c=2*d/c;var h=(e+d)/(e-d);e=-2*e*d/(e-d);d=Math.tan(i);return a.FromValuesToRef(b,0,0,0,0,c,0,d,0,0,h,1,0,0,e,0,g),f&&g.multiplyToRef(o,g),g._updateIdentityStatus(!1),g},a.PerspectiveFovLH=function(b,c,d,e,f,h){void 0===h&&(h=0);var g=new a;return a.PerspectiveFovLHToRef(b,c,d,e,g,!0,f,h),g},a.PerspectiveFovLHToRef=function(b,c,d,e,f,j,g,h){void 0===j&&(j=!0),void 0===h&&(h=0);d=d;e=e;b=1/Math.tan(.5*b);var i=j?b/c:b;j=j?b:b*c;b=0!==e?(e+d)/(e-d):1;c=0!==e?-2*e*d/(e-d):-2*d;e=Math.tan(h);a.FromValuesToRef(i,0,0,0,0,j,0,e,0,0,b,1,0,0,c,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovReverseLHToRef=function(b,c,d,e,f,i,g,h){void 0===i&&(i=!0),void 0===h&&(h=0);e=1/Math.tan(.5*b);b=i?e/c:e;i=i?e:e*c;e=Math.tan(h);a.FromValuesToRef(b,0,0,0,0,i,0,e,0,0,-d,1,0,0,1,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovRH=function(b,c,d,e,f,h){void 0===h&&(h=0);var g=new a;return a.PerspectiveFovRHToRef(b,c,d,e,g,!0,f,h),g},a.PerspectiveFovRHToRef=function(b,c,d,e,f,j,g,h){void 0===j&&(j=!0),void 0===h&&(h=0);d=d;e=e;b=1/Math.tan(.5*b);var i=j?b/c:b;j=j?b:b*c;b=0!==e?-(e+d)/(e-d):-1;c=0!==e?-2*e*d/(e-d):-2*d;e=Math.tan(h);a.FromValuesToRef(i,0,0,0,0,j,0,e,0,0,b,-1,0,0,c,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovReverseRHToRef=function(b,c,d,e,f,i,g,h){void 0===i&&(i=!0),void 0===h&&(h=0);e=1/Math.tan(.5*b);b=i?e/c:e;i=i?e:e*c;e=Math.tan(h);a.FromValuesToRef(b,0,0,0,0,i,0,e,0,0,-d,-1,0,0,-1,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovWebVRToRef=function(a,b,c,d,e,f,n){void 0===e&&(e=!1),void 0===n&&(n=0);e=e?-1:1;var g=Math.tan(a.upDegrees*Math.PI/180),h=Math.tan(a.downDegrees*Math.PI/180),i=Math.tan(a.leftDegrees*Math.PI/180);a=Math.tan(a.rightDegrees*Math.PI/180);var j=2/(i+a),k=2/(g+h);n=Math.tan(n);var p=d._m;p[0]=j,p[1]=p[2]=p[3]=p[4]=0,p[5]=k,p[6]=0,p[7]=n,p[8]=(i-a)*j*.5,p[9]=-(g-h)*k*.5,p[10]=-c/(b-c),p[11]=1*e,p[12]=p[13]=p[15]=0,p[14]=-2*c*b/(c-b),f&&d.multiplyToRef(o,d),d._markAsUpdated()},a.GetFinalMatrix=function(b,c,d,e,f,j){var g=b.width,h=b.height,i=b.x;b=b.y;j=a.FromValues(g/2,0,0,0,0,-h/2,0,0,0,0,j-f,0,i+g/2,h/2+b,f,1);i=m.Matrix[0];return c.multiplyToRef(d,i),i.multiplyToRef(e,i),i.multiply(j)},a.GetAsMatrix2x2=function(a){a=a.m;a=[a[0],a[1],a[4],a[5]];return g.a.MatrixUse64Bits?a:new Float32Array(a)},a.GetAsMatrix3x3=function(a){a=a.m;a=[a[0],a[1],a[2],a[4],a[5],a[6],a[8],a[9],a[10]];return g.a.MatrixUse64Bits?a:new Float32Array(a)},a.Transpose=function(b){var c=new a;return a.TransposeToRef(b,c),c},a.TransposeToRef=function(a,b){var c=b._m,d=a.m;c[0]=d[0],c[1]=d[4],c[2]=d[8],c[3]=d[12],c[4]=d[1],c[5]=d[5],c[6]=d[9],c[7]=d[13],c[8]=d[2],c[9]=d[6],c[10]=d[10],c[11]=d[14],c[12]=d[3],c[13]=d[7],c[14]=d[11],c[15]=d[15],b._markAsUpdated(),b._updateIdentityStatus(a._isIdentity,a._isIdentityDirty)},a.Reflection=function(b){var c=new a;return a.ReflectionToRef(b,c),c},a.ReflectionToRef=function(b,c){b.normalize();var d=b.normal.x,e=b.normal.y,f=b.normal.z,i=-2*d,g=-2*e,h=-2*f;a.FromValuesToRef(i*d+1,g*d,h*d,0,i*e,g*e+1,h*e,0,i*f,g*f,h*f+1,0,i*b.d,g*b.d,h*b.d,1,c)},a.FromXYZAxesToRef=function(b,c,d,e){a.FromValuesToRef(b._x,b._y,b._z,0,c._x,c._y,c._z,0,d._x,d._y,d._z,0,0,0,0,1,e)},a.FromQuaternionToRef=function(a,b){var c=a._x*a._x,d=a._y*a._y,e=a._z*a._z,f=a._x*a._y,j=a._z*a._w,g=a._z*a._x,h=a._y*a._w,i=a._y*a._z;a=a._x*a._w;b._m[0]=1-2*(d+e),b._m[1]=2*(f+j),b._m[2]=2*(g-h),b._m[3]=0,b._m[4]=2*(f-j),b._m[5]=1-2*(e+c),b._m[6]=2*(i+a),b._m[7]=0,b._m[8]=2*(g+h),b._m[9]=2*(i-a),b._m[10]=1-2*(d+c),b._m[11]=0,b._m[12]=0,b._m[13]=0,b._m[14]=0,b._m[15]=1,b._markAsUpdated()},a._updateFlagSeed=0,a._identityReadOnly=a.Identity(),a}(),m=function(){function a(){}return a.Vector3=f.a.BuildTuple(11,i.Zero),a.Matrix=f.a.BuildTuple(2,l.Identity),a.Quaternion=f.a.BuildTuple(3,k.Zero),a}(),n=function(){function a(){}return a.Vector2=f.a.BuildTuple(3,h.Zero),a.Vector3=f.a.BuildTuple(13,i.Zero),a.Vector4=f.a.BuildTuple(3,j.Zero),a.Quaternion=f.a.BuildTuple(2,k.Zero),a.Matrix=f.a.BuildTuple(8,l.Identity),a}();Object(a.b)("BABYLON.Vector2",h),Object(a.b)("BABYLON.Vector3",i),Object(a.b)("BABYLON.Vector4",j),Object(a.b)("BABYLON.Matrix",l);var o=l.FromValues(1,0,0,0,0,1,0,0,0,0,.5,0,0,0,.5,1)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.ALPHA_DISABLE=0,a.ALPHA_ADD=1,a.ALPHA_COMBINE=2,a.ALPHA_SUBTRACT=3,a.ALPHA_MULTIPLY=4,a.ALPHA_MAXIMIZED=5,a.ALPHA_ONEONE=6,a.ALPHA_PREMULTIPLIED=7,a.ALPHA_PREMULTIPLIED_PORTERDUFF=8,a.ALPHA_INTERPOLATE=9,a.ALPHA_SCREENMODE=10,a.ALPHA_ONEONE_ONEONE=11,a.ALPHA_ALPHATOCOLOR=12,a.ALPHA_REVERSEONEMINUS=13,a.ALPHA_SRC_DSTONEMINUSSRCALPHA=14,a.ALPHA_ONEONE_ONEZERO=15,a.ALPHA_EXCLUSION=16,a.ALPHA_LAYER_ACCUMULATE=17,a.ALPHA_EQUATION_ADD=0,a.ALPHA_EQUATION_SUBSTRACT=1,a.ALPHA_EQUATION_REVERSE_SUBTRACT=2,a.ALPHA_EQUATION_MAX=3,a.ALPHA_EQUATION_MIN=4,a.ALPHA_EQUATION_DARKEN=5,a.DELAYLOADSTATE_NONE=0,a.DELAYLOADSTATE_LOADED=1,a.DELAYLOADSTATE_LOADING=2,a.DELAYLOADSTATE_NOTLOADED=4,a.NEVER=512,a.ALWAYS=519,a.LESS=513,a.EQUAL=514,a.LEQUAL=515,a.GREATER=516,a.GEQUAL=518,a.NOTEQUAL=517,a.KEEP=7680,a.ZERO=0,a.REPLACE=7681,a.INCR=7682,a.DECR=7683,a.INVERT=5386,a.INCR_WRAP=34055,a.DECR_WRAP=34056,a.TEXTURE_CLAMP_ADDRESSMODE=0,a.TEXTURE_WRAP_ADDRESSMODE=1,a.TEXTURE_MIRROR_ADDRESSMODE=2,a.TEXTURE_CREATIONFLAG_STORAGE=1,a.TEXTUREFORMAT_ALPHA=0,a.TEXTUREFORMAT_LUMINANCE=1,a.TEXTUREFORMAT_LUMINANCE_ALPHA=2,a.TEXTUREFORMAT_RGB=4,a.TEXTUREFORMAT_RGBA=5,a.TEXTUREFORMAT_RED=6,a.TEXTUREFORMAT_R=6,a.TEXTUREFORMAT_RG=7,a.TEXTUREFORMAT_RED_INTEGER=8,a.TEXTUREFORMAT_R_INTEGER=8,a.TEXTUREFORMAT_RG_INTEGER=9,a.TEXTUREFORMAT_RGB_INTEGER=10,a.TEXTUREFORMAT_RGBA_INTEGER=11,a.TEXTUREFORMAT_BGRA=12,a.TEXTUREFORMAT_DEPTH24_STENCIL8=13,a.TEXTUREFORMAT_DEPTH32_FLOAT=14,a.TEXTUREFORMAT_DEPTH16=15,a.TEXTUREFORMAT_DEPTH24=16,a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM=36492,a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT=36495,a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT=36494,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5=33779,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3=33778,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1=33777,a.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1=33776,a.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4=37808,a.TEXTURETYPE_UNSIGNED_BYTE=0,a.TEXTURETYPE_UNSIGNED_INT=0,a.TEXTURETYPE_FLOAT=1,a.TEXTURETYPE_HALF_FLOAT=2,a.TEXTURETYPE_BYTE=3,a.TEXTURETYPE_SHORT=4,a.TEXTURETYPE_UNSIGNED_SHORT=5,a.TEXTURETYPE_INT=6,a.TEXTURETYPE_UNSIGNED_INTEGER=7,a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4=8,a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1=9,a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5=10,a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV=11,a.TEXTURETYPE_UNSIGNED_INT_24_8=12,a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV=13,a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV=14,a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV=15,a.TEXTURETYPE_UNDEFINED=16,a.TEXTURE_NEAREST_SAMPLINGMODE=1,a.TEXTURE_NEAREST_NEAREST=1,a.TEXTURE_BILINEAR_SAMPLINGMODE=2,a.TEXTURE_LINEAR_LINEAR=2,a.TEXTURE_TRILINEAR_SAMPLINGMODE=3,a.TEXTURE_LINEAR_LINEAR_MIPLINEAR=3,a.TEXTURE_NEAREST_NEAREST_MIPNEAREST=4,a.TEXTURE_NEAREST_LINEAR_MIPNEAREST=5,a.TEXTURE_NEAREST_LINEAR_MIPLINEAR=6,a.TEXTURE_NEAREST_LINEAR=7,a.TEXTURE_NEAREST_NEAREST_MIPLINEAR=8,a.TEXTURE_LINEAR_NEAREST_MIPNEAREST=9,a.TEXTURE_LINEAR_NEAREST_MIPLINEAR=10,a.TEXTURE_LINEAR_LINEAR_MIPNEAREST=11,a.TEXTURE_LINEAR_NEAREST=12,a.TEXTURE_EXPLICIT_MODE=0,a.TEXTURE_SPHERICAL_MODE=1,a.TEXTURE_PLANAR_MODE=2,a.TEXTURE_CUBIC_MODE=3,a.TEXTURE_PROJECTION_MODE=4,a.TEXTURE_SKYBOX_MODE=5,a.TEXTURE_INVCUBIC_MODE=6,a.TEXTURE_EQUIRECTANGULAR_MODE=7,a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE=8,a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE=9,a.TEXTURE_FILTERING_QUALITY_OFFLINE=4096,a.TEXTURE_FILTERING_QUALITY_HIGH=64,a.TEXTURE_FILTERING_QUALITY_MEDIUM=16,a.TEXTURE_FILTERING_QUALITY_LOW=8,a.SCALEMODE_FLOOR=1,a.SCALEMODE_NEAREST=2,a.SCALEMODE_CEILING=3,a.MATERIAL_TextureDirtyFlag=1,a.MATERIAL_LightDirtyFlag=2,a.MATERIAL_FresnelDirtyFlag=4,a.MATERIAL_AttributesDirtyFlag=8,a.MATERIAL_MiscDirtyFlag=16,a.MATERIAL_PrePassDirtyFlag=32,a.MATERIAL_AllDirtyFlag=63,a.MATERIAL_TriangleFillMode=0,a.MATERIAL_WireFrameFillMode=1,a.MATERIAL_PointFillMode=2,a.MATERIAL_PointListDrawMode=3,a.MATERIAL_LineListDrawMode=4,a.MATERIAL_LineLoopDrawMode=5,a.MATERIAL_LineStripDrawMode=6,a.MATERIAL_TriangleStripDrawMode=7,a.MATERIAL_TriangleFanDrawMode=8,a.MATERIAL_ClockWiseSideOrientation=0,a.MATERIAL_CounterClockWiseSideOrientation=1,a.ACTION_NothingTrigger=0,a.ACTION_OnPickTrigger=1,a.ACTION_OnLeftPickTrigger=2,a.ACTION_OnRightPickTrigger=3,a.ACTION_OnCenterPickTrigger=4,a.ACTION_OnPickDownTrigger=5,a.ACTION_OnDoublePickTrigger=6,a.ACTION_OnPickUpTrigger=7,a.ACTION_OnPickOutTrigger=16,a.ACTION_OnLongPressTrigger=8,a.ACTION_OnPointerOverTrigger=9,a.ACTION_OnPointerOutTrigger=10,a.ACTION_OnEveryFrameTrigger=11,a.ACTION_OnIntersectionEnterTrigger=12,a.ACTION_OnIntersectionExitTrigger=13,a.ACTION_OnKeyDownTrigger=14,a.ACTION_OnKeyUpTrigger=15,a.PARTICLES_BILLBOARDMODE_Y=2,a.PARTICLES_BILLBOARDMODE_ALL=7,a.PARTICLES_BILLBOARDMODE_STRETCHED=8,a.MESHES_CULLINGSTRATEGY_STANDARD=0,a.MESHES_CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY=1,a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION=2,a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY=3,a.SCENELOADER_NO_LOGGING=0,a.SCENELOADER_MINIMAL_LOGGING=1,a.SCENELOADER_SUMMARY_LOGGING=2,a.SCENELOADER_DETAILED_LOGGING=3,a.PREPASS_IRRADIANCE_TEXTURE_TYPE=0,a.PREPASS_POSITION_TEXTURE_TYPE=1,a.PREPASS_VELOCITY_TEXTURE_TYPE=2,a.PREPASS_REFLECTIVITY_TEXTURE_TYPE=3,a.PREPASS_COLOR_TEXTURE_TYPE=4,a.PREPASS_DEPTH_TEXTURE_TYPE=5,a.PREPASS_NORMAL_TEXTURE_TYPE=6,a.PREPASS_ALBEDO_SQRT_TEXTURE_TYPE=7,a.BUFFER_CREATIONFLAG_READ=1,a.BUFFER_CREATIONFLAG_WRITE=2,a.BUFFER_CREATIONFLAG_READWRITE=3,a.BUFFER_CREATIONFLAG_UNIFORM=4,a.BUFFER_CREATIONFLAG_VERTEX=8,a.BUFFER_CREATIONFLAG_INDEX=16,a.BUFFER_CREATIONFLAG_STORAGE=32,a.SUBMESH_DRAWWRAPPER_MAINPASS="bjs_mainpass",a.SUBMESH_DRAWWRAPPER_SHADOWGENERATOR_PREFIX="bjs_shadowgenerator_",a.SUBMESH_DRAWWRAPPER_DEPTHRENDERER_PREFIX="bjs_depthrenderer_",a.SUBMESH_DRAWWRAPPER_OUTLINERENDERER_PREFIX="bjs_outlinerenderer_",a.INPUT_ALT_KEY=18,a.INPUT_CTRL_KEY=17,a.INPUT_META_KEY1=91,a.INPUT_META_KEY2=92,a.INPUT_META_KEY3=93,a.INPUT_SHIFT_KEY=16,a.SNAPSHOTRENDERING_STANDARD=0,a.SNAPSHOTRENDERING_FAST=1,a.PERSPECTIVE_CAMERA=0,a.ORTHOGRAPHIC_CAMERA=1,a.FOVMODE_VERTICAL_FIXED=0,a.FOVMODE_HORIZONTAL_FIXED=1,a.RIG_MODE_NONE=0,a.RIG_MODE_STEREOSCOPIC_ANAGLYPH=10,a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL=11,a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED=12,a.RIG_MODE_STEREOSCOPIC_OVERUNDER=13,a.RIG_MODE_STEREOSCOPIC_INTERLACED=14,a.RIG_MODE_VR=20,a.RIG_MODE_WEBVR=21,a.RIG_MODE_CUSTOM=22,a.MAX_SUPPORTED_UV_SETS=6,a}()},function(a,b,c){c.d(b,"d",function(){return e}),c.d(b,"a",function(){return f}),c.d(b,"c",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"e",function(){return i}),c.d(b,"f",function(){return j});var d=function(a,b){return(d=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])})(a,b)};function e(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Class extends value "+String(b)+" is not a constructor or null");function c(){this.constructor=a}d(a,b),a.prototype=null===b?Object.create(b):(c.prototype=b.prototype,new c)}var f=function(){return(f=Object.assign||function(a){for(var b,c=1,d=arguments.length;c=0;h--)(e=a[h])&&(g=(f<3?e(g):f>3?e(b,c,g):e(b,c))||g);return f>3&&g&&Object.defineProperty(b,c,g),g}function h(a,b,c,d){return new(c||(c=Promise))(function(e,f){function g(a){try{i(d.next(a))}catch(a){f(a)}}function h(a){try{i(d["throw"](a))}catch(a){f(a)}}function i(a){var b;a.done?e(a.value):(b=a.value,b instanceof c?b:new c(function(a){a(b)})).then(g,h)}i((d=d.apply(a,b||[])).next())})}function i(a,b){var c,d,e,f={label:0,sent:function(){if(1&e[0])throw e[1];return e[1]},trys:[],ops:[]};return g={next:g(0),"throw":g(1),"return":g(2)},"function"==typeof Symbol&&(g[Symbol.iterator]=function(){return this}),g;function g(g){return function(h){return function(g){if(c)throw new TypeError("Generator is already executing.");for(;f;)try{if(c=1,d&&(e=2&g[0]?d["return"]:g[0]?d["throw"]||((e=d["return"])&&e.call(d),0):d.next)&&!(e=e.call(d,g[1])).done)return e;switch(d=0,e&&(g=[2&g[0],e.value]),g[0]){case 0:case 1:e=g;break;case 4:return f.label++,{value:g[1],done:!1};case 5:f.label++,d=g[1],g=[0];continue;case 7:g=f.ops.pop(),f.trys.pop();continue;default:if(!(e=f.trys,(e=e.length>0&&e[e.length-1])||6!==g[0]&&2!==g[0])){f=0;continue}if(3===g[0]&&(!e||g[1]>e[0]&&g[1]0},a.prototype.clear=function(){this._observers=new Array,this._onObserverAdded=null},a.prototype.clone=function(){var b=new a;return b._observers=this._observers.slice(0),b},a.prototype.hasSpecificMask=function(a){void 0===a&&(a=-1);for(var b=0,c=this._observers;b"+c+"
";a._AddLogEntry(c),a._GenerateLimitMessage(b,0)}},a._WarnDisabled=function(a,b){},a._WarnEnabled=function(b,c){if(void 0===c||a._CheckLimit(b,c)){c=a._FormatMessage(b);c="
"+b+"

";a._AddLogEntry(c),a._GenerateLimitMessage(b,1)}},a._ErrorDisabled=function(a,b){},a._ErrorEnabled=function(b,c){if(void 0===c||a._CheckLimit(b,c)){c=a._FormatMessage(b);a.errorsCount++,!1;c="
"+c+"

";a._AddLogEntry(c),a._GenerateLimitMessage(b,2)}},Object.defineProperty(a,"LogCache",{get:function(){return a._LogCache},enumerable:!1,configurable:!0}),a.ClearLogCache=function(){a._LogCache="",a._LogLimitOutputs={},a.errorsCount=0},Object.defineProperty(a,"LogLevels",{set:function(b){(b&a.MessageLogLevel)===a.MessageLogLevel?a.Log=a._LogEnabled:a.Log=a._LogDisabled,(b&a.WarningLogLevel)===a.WarningLogLevel?a.Warn=a._WarnEnabled:a.Warn=a._WarnDisabled,(b&a.ErrorLogLevel)===a.ErrorLogLevel?a.Error=a._ErrorEnabled:a.Error=a._ErrorDisabled},enumerable:!1,configurable:!0}),a.NoneLogLevel=0,a.MessageLogLevel=1,a.WarningLogLevel=2,a.ErrorLogLevel=4,a.AllLogLevel=7,a.MessageLimitReached="Too many %TYPE%s (%LIMIT%), no more %TYPE%s will be reported for this message.",a._LogCache="",a._LogLimitOutputs={},a.errorsCount=0,a.Log=a._LogEnabled,a.Warn=a._WarnEnabled,a.Error=a._ErrorEnabled,a}()},function(a,b,c){c.d(b,"a",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"c",function(){return i});var d=c(14),e=c(22),f=c(51);a=c(10);var g=function(){function a(a,b,c){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),this.r=a,this.g=b,this.b=c}return a.prototype.toString=function(){return"{R: "+this.r+" G:"+this.g+" B:"+this.b+"}"},a.prototype.getClassName=function(){return"Color3"},a.prototype.getHashCode=function(){var a=255*this.r|0;return 397*(397*a^(255*this.g|0))^(255*this.b|0)},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.r,a[b+1]=this.g,a[b+2]=this.b,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.toColor4=function(a){return void 0===a&&(a=1),new h(this.r,this.g,this.b,a)},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.toLuminance=function(){return.3*this.r+.59*this.g+.11*this.b},a.prototype.multiply=function(b){return new a(this.r*b.r,this.g*b.g,this.b*b.b)},a.prototype.multiplyToRef=function(a,b){return b.r=this.r*a.r,b.g=this.g*a.g,b.b=this.b*a.b,this},a.prototype.equals=function(a){return a&&this.r===a.r&&this.g===a.g&&this.b===a.b},a.prototype.equalsFloats=function(a,b,c){return this.r===a&&this.g===b&&this.b===c},a.prototype.scale=function(b){return new a(this.r*b,this.g*b,this.b*b)},a.prototype.scaleToRef=function(a,b){return b.r=this.r*a,b.g=this.g*a,b.b=this.b*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.r+=this.r*a,b.g+=this.g*a,b.b+=this.b*a,this},a.prototype.clampToRef=function(a,b,c){return void 0===a&&(a=0),void 0===b&&(b=1),c.r=d.a.Clamp(this.r,a,b),c.g=d.a.Clamp(this.g,a,b),c.b=d.a.Clamp(this.b,a,b),this},a.prototype.add=function(b){return new a(this.r+b.r,this.g+b.g,this.b+b.b)},a.prototype.addToRef=function(a,b){return b.r=this.r+a.r,b.g=this.g+a.g,b.b=this.b+a.b,this},a.prototype.subtract=function(b){return new a(this.r-b.r,this.g-b.g,this.b-b.b)},a.prototype.subtractToRef=function(a,b){return b.r=this.r-a.r,b.g=this.g-a.g,b.b=this.b-a.b,this},a.prototype.clone=function(){return new a(this.r,this.g,this.b)},a.prototype.copyFrom=function(a){return this.r=a.r,this.g=a.g,this.b=a.b,this},a.prototype.copyFromFloats=function(a,b,c){return this.r=a,this.g=b,this.b=c,this},a.prototype.set=function(a,b,c){return this.copyFromFloats(a,b,c)},a.prototype.toHexString=function(){var a=Math.round(255*this.r),b=Math.round(255*this.g),c=Math.round(255*this.b);return"#"+d.a.ToHex(a)+d.a.ToHex(b)+d.a.ToHex(c)},a.prototype.toLinearSpace=function(){var b=new a;return this.toLinearSpaceToRef(b),b},a.prototype.toHSV=function(){var b=new a;return this.toHSVToRef(b),b},a.prototype.toHSVToRef=function(a){var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),j=0,g=0,h=e,i=e-f;0!==e&&(g=i/e),e!=f&&(e==b?(j=(c-d)/i,c=0&&a<=1?(e=b,f=h):a>=1&&a<=2?(e=h,f=b):a>=2&&a<=3?(f=b,g=h):a>=3&&a<=4?(f=h,g=b):a>=4&&a<=5?(e=h,g=b):a>=5&&a<=6&&(e=b,g=h);a=c-b;d.set(e+a,f+a,g+a)},a.FromHexString=function(b){if("#"!==b.substring(0,1)||7!==b.length)return new a(0,0,0);var c=parseInt(b.substring(1,3),16),d=parseInt(b.substring(3,5),16);b=parseInt(b.substring(5,7),16);return a.FromInts(c,d,b)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2])},a.FromArrayToRef=function(a,b,c){void 0===b&&(b=0),c.r=a[b],c.g=a[b+1],c.b=a[b+2]},a.FromInts=function(b,c,d){return new a(b/255,c/255,d/255)},a.Lerp=function(b,c,d){var e=new a(0,0,0);return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){d.r=a.r+(b.r-a.r)*c,d.g=a.g+(b.g-a.g)*c,d.b=a.b+(b.b-a.b)*c},a.Hermite1stDerivative=function(b,c,d,e,f){var g=a.Black();return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.r=6*(g-e)*a.r+(3*g-4*e+1)*b.r+6*(-g+e)*c.r+(3*g-2*e)*d.r,f.g=6*(g-e)*a.g+(3*g-4*e+1)*b.g+6*(-g+e)*c.g+(3*g-2*e)*d.g,f.b=6*(g-e)*a.b+(3*g-4*e+1)*b.b+6*(-g+e)*c.b+(3*g-2*e)*d.b},a.Red=function(){return new a(1,0,0)},a.Green=function(){return new a(0,1,0)},a.Blue=function(){return new a(0,0,1)},a.Black=function(){return new a(0,0,0)},Object.defineProperty(a,"BlackReadOnly",{get:function(){return a._BlackReadOnly},enumerable:!1,configurable:!0}),a.White=function(){return new a(1,1,1)},a.Purple=function(){return new a(.5,0,.5)},a.Magenta=function(){return new a(1,0,1)},a.Yellow=function(){return new a(1,1,0)},a.Gray=function(){return new a(.5,.5,.5)},a.Teal=function(){return new a(0,1,1)},a.Random=function(){return new a(Math.random(),Math.random(),Math.random())},a._BlackReadOnly=a.Black(),a}(),h=function(){function a(a,b,c,d){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),this.r=a,this.g=b,this.b=c,this.a=d}return a.prototype.addInPlace=function(a){return this.r+=a.r,this.g+=a.g,this.b+=a.b,this.a+=a.a,this},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.r,a[b+1]=this.g,a[b+2]=this.b,a[b+3]=this.a,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.equals=function(a){return a&&this.r===a.r&&this.g===a.g&&this.b===a.b&&this.a===a.a},a.prototype.add=function(b){return new a(this.r+b.r,this.g+b.g,this.b+b.b,this.a+b.a)},a.prototype.subtract=function(b){return new a(this.r-b.r,this.g-b.g,this.b-b.b,this.a-b.a)},a.prototype.subtractToRef=function(a,b){return b.r=this.r-a.r,b.g=this.g-a.g,b.b=this.b-a.b,b.a=this.a-a.a,this},a.prototype.scale=function(b){return new a(this.r*b,this.g*b,this.b*b,this.a*b)},a.prototype.scaleToRef=function(a,b){return b.r=this.r*a,b.g=this.g*a,b.b=this.b*a,b.a=this.a*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.r+=this.r*a,b.g+=this.g*a,b.b+=this.b*a,b.a+=this.a*a,this},a.prototype.clampToRef=function(a,b,c){return void 0===a&&(a=0),void 0===b&&(b=1),c.r=d.a.Clamp(this.r,a,b),c.g=d.a.Clamp(this.g,a,b),c.b=d.a.Clamp(this.b,a,b),c.a=d.a.Clamp(this.a,a,b),this},a.prototype.multiply=function(b){return new a(this.r*b.r,this.g*b.g,this.b*b.b,this.a*b.a)},a.prototype.multiplyToRef=function(a,b){return b.r=this.r*a.r,b.g=this.g*a.g,b.b=this.b*a.b,b.a=this.a*a.a,b},a.prototype.toString=function(){return"{R: "+this.r+" G:"+this.g+" B:"+this.b+" A:"+this.a+"}"},a.prototype.getClassName=function(){return"Color4"},a.prototype.getHashCode=function(){var a=255*this.r|0;return 397*(397*(397*a^(255*this.g|0))^(255*this.b|0))^(255*this.a|0)},a.prototype.clone=function(){return new a(this.r,this.g,this.b,this.a)},a.prototype.copyFrom=function(a){return this.r=a.r,this.g=a.g,this.b=a.b,this.a=a.a,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.r=a,this.g=b,this.b=c,this.a=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.toHexString=function(a){void 0===a&&(a=!1);var b=Math.round(255*this.r),c=Math.round(255*this.g),e=Math.round(255*this.b);if(a)return"#"+d.a.ToHex(b)+d.a.ToHex(c)+d.a.ToHex(e);a=Math.round(255*this.a);return"#"+d.a.ToHex(b)+d.a.ToHex(c)+d.a.ToHex(e)+d.a.ToHex(a)},a.prototype.toLinearSpace=function(){var b=new a;return this.toLinearSpaceToRef(b),b},a.prototype.toLinearSpaceToRef=function(a){return a.r=Math.pow(this.r,e.d),a.g=Math.pow(this.g,e.d),a.b=Math.pow(this.b,e.d),a.a=this.a,this},a.prototype.toGammaSpace=function(){var b=new a;return this.toGammaSpaceToRef(b),b},a.prototype.toGammaSpaceToRef=function(a){return a.r=Math.pow(this.r,e.c),a.g=Math.pow(this.g,e.c),a.b=Math.pow(this.b,e.c),a.a=this.a,this},a.FromHexString=function(b){if("#"!==b.substring(0,1)||9!==b.length)return new a(0,0,0,0);var c=parseInt(b.substring(1,3),16),d=parseInt(b.substring(3,5),16),e=parseInt(b.substring(5,7),16);b=parseInt(b.substring(7,9),16);return a.FromInts(c,d,e,b)},a.Lerp=function(b,c,d){var e=new a(0,0,0,0);return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){d.r=a.r+(b.r-a.r)*c,d.g=a.g+(b.g-a.g)*c,d.b=a.b+(b.b-a.b)*c,d.a=a.a+(b.a-a.a)*c},a.Hermite1stDerivative=function(b,c,d,e,f){var g=new a;return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.r=6*(g-e)*a.r+(3*g-4*e+1)*b.r+6*(-g+e)*c.r+(3*g-2*e)*d.r,f.g=6*(g-e)*a.g+(3*g-4*e+1)*b.g+6*(-g+e)*c.g+(3*g-2*e)*d.g,f.b=6*(g-e)*a.b+(3*g-4*e+1)*b.b+6*(-g+e)*c.b+(3*g-2*e)*d.b,f.a=6*(g-e)*a.a+(3*g-4*e+1)*b.a+6*(-g+e)*c.a+(3*g-2*e)*d.a},a.FromColor3=function(b,c){return void 0===c&&(c=1),new a(b.r,b.g,b.b,c)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){void 0===b&&(b=0),c.r=a[b],c.g=a[b+1],c.b=a[b+2],c.a=a[b+3]},a.FromInts=function(b,c,d,e){return new a(b/255,c/255,d/255,e/255)},a.CheckColors4=function(a,b){if(a.length===3*b){for(var b=[],c=0;c0},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasThinInstances",{get:function(){var a;return(null!==(a=this._thinInstanceDataStorage.instancesCount)&&void 0!==a?a:0)>0},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcedInstanceCount",{get:function(){return this._internalMeshDataInfo._forcedInstanceCount},set:function(a){this._internalMeshDataInfo._forcedInstanceCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"source",{get:function(){return this._internalMeshDataInfo._source},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cloneMeshMap",{get:function(){return this._internalMeshDataInfo.meshMap},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isUnIndexed",{get:function(){return this._unIndexed},set:function(a){this._unIndexed!==a&&(this._unIndexed=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"worldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.instancesData},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"previousWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.instancesPreviousData},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manualUpdateOfWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.manualUpdate},set:function(a){this._instanceDataStorage.manualUpdate=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manualUpdateOfPreviousWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.previousManualUpdate},set:function(a){this._instanceDataStorage.previousManualUpdate=a},enumerable:!1,configurable:!0}),b.prototype.instantiateHierarchy=function(a,b,c){void 0===a&&(a=null);var d=!(this.getTotalVertices()>0)||b&&b.doNotInstantiate?this.clone("Clone of "+(this.name||this.id),a||this.parent,!0):this.createInstance("instance of "+(this.name||this.id));d&&(d.parent=a||this.parent,d.position=this.position.clone(),d.scaling=this.scaling.clone(),this.rotationQuaternion?d.rotationQuaternion=this.rotationQuaternion.clone():d.rotation=this.rotation.clone(),c&&c(this,d));for(var a=0,e=this.getChildTransformNodes(!0);a0},enumerable:!1,configurable:!0}),b.prototype.getLODLevels=function(){return this._internalMeshDataInfo._LODLevels},b.prototype._sortLODLevels=function(){var a=this._internalMeshDataInfo._useLODScreenCoverage?-1:1;this._internalMeshDataInfo._LODLevels.sort(function(b,c){return b.distanceOrScreenCoveragec.distanceOrScreenCoverage?-a:0})},b.prototype.addLODLevel=function(a,b){if(b&&b._masterMesh)return u.a.Warn("You cannot use a mesh as LOD level twice"),this;a=new y.a(a,b);return this._internalMeshDataInfo._LODLevels.push(a),b&&(b._masterMesh=this),this._sortLODLevels(),this},b.prototype.getLODLevelAtDistance=function(a){for(var b=this._internalMeshDataInfo,c=0;cf*e)return this.onLODLevelSelection&&this.onLODLevelSelection(e,this,this),this;for(a=0;a0||this.hasThinInstances);this.computeWorldMatrix();b=this.material||d.defaultMaterial;if(b)if(b._storeEffectOnSubMeshes)for(var d=0,h=this.subMeshes;d0){var c=this.getIndices();if(!c)return null;c=c.length;var d=!1;if(a)d=!0;else for(var a=0,e=this.subMeshes;ac){d=!0;break}if(f.verticesStart+f.verticesCount>b){d=!0;break}}if(!d)return this.subMeshes[0]}return this.releaseSubMeshes(),new o.a(0,0,b,0,this.getTotalIndices(),this)},b.prototype.subdivide=function(a){if(!(a<1)){for(var b=this.getTotalIndices(),c=b/a|0,d=0;c%3!=0;)c++;this.releaseSubMeshes();for(var e=0;e=b);e++)o.a.CreateFromIndices(0,d,e===a-1?b-d:c,this),d+=c;this.synchronizeInstances()}},b.prototype.setVerticesData=function(a,b,c,d){if(void 0===c&&(c=!1),this._geometry)this._geometry.setVerticesData(a,b,c,d);else{d=new m.a;d.set(b,a);b=this.getScene();new n.a(n.a.RandomId(),b,d,c,this)}return this},b.prototype.removeVerticesData=function(a){this._geometry&&this._geometry.removeVerticesData(a)},b.prototype.markVerticesDataAsUpdatable=function(a,b){void 0===b&&(b=!0);var c=this.getVertexBuffer(a);c&&c.isUpdatable()!==b&&this.setVerticesData(a,this.getVerticesData(a),b)},b.prototype.setVerticesBuffer=function(a,b){return void 0===b&&(b=!0),this._geometry||(this._geometry=n.a.CreateGeometryForMesh(this)),this._geometry.setVerticesBuffer(a,null,b),this},b.prototype.updateVerticesData=function(a,b,c,d){return this._geometry?(d?(this.makeGeometryUnique(),this.updateVerticesData(a,b,c,!1)):this._geometry.updateVerticesData(a,b,c),this):this},b.prototype.updateMeshPositions=function(a,b){void 0===b&&(b=!0);var c=this.getVerticesData(l.b.PositionKind);if(!c)return this;if(a(c),this.updateVerticesData(l.b.PositionKind,c,!1,!1),b){a=this.getIndices();b=this.getVerticesData(l.b.NormalKind);if(!b)return this;m.a.ComputeNormals(c,a,b),this.updateVerticesData(l.b.NormalKind,b,!1,!1)}return this},b.prototype.makeGeometryUnique=function(){if(!this._geometry)return this;if(1===this._geometry.meshes.length)return this;var a=this._geometry,b=this._geometry.copy(n.a.RandomId());return a.releaseForMesh(this,!0),b.applyToMesh(this),this},b.prototype.setIndices=function(a,b,c){if(void 0===b&&(b=null),void 0===c&&(c=!1),this._geometry)this._geometry.setIndices(a,b,c);else{b=new m.a;b.indices=a;a=this.getScene();new n.a(n.a.RandomId(),a,b,c,this)}return this},b.prototype.updateIndices=function(a,b,c){return void 0===c&&(c=!1),this._geometry?(this._geometry.updateIndices(a,b,c),this):this},b.prototype.toLeftHanded=function(){return this._geometry?(this._geometry.toLeftHanded(),this):this},b.prototype._bind=function(a,b,c){if(!this._geometry)return this;var d,e=this.getScene().getEngine();if(this.morphTargetManager&&this.morphTargetManager.isUsingTextureForTargets&&this.morphTargetManager._bind(b),this._unIndexed)d=null;else switch(c){case p.a.PointFillMode:d=null;break;case p.a.WireFrameFillMode:d=a._getLinesIndexBuffer(this.getIndices(),e);break;default:case p.a.TriangleFillMode:d=this._geometry.getIndexBuffer()}return!this._userInstancedBuffersStorage||this.hasThinInstances?this._geometry._bind(b,d):this._geometry._bind(b,d,this._userInstancedBuffersStorage.vertexBuffers,this._userInstancedBuffersStorage.vertexArrayObjects),this},b.prototype._draw=function(a,b,c){if(!this._geometry||!this._geometry.getVertexBuffers()||!this._unIndexed&&!this._geometry.getIndexBuffer())return this;this._internalMeshDataInfo._onBeforeDrawObservable&&this._internalMeshDataInfo._onBeforeDrawObservable.notifyObservers(this);var d=this.getScene().getEngine();return this._unIndexed||b==p.a.PointFillMode?d.drawArraysType(b,a.verticesStart,a.verticesCount,this.forcedInstanceCount||c):b==p.a.WireFrameFillMode?d.drawElementsType(b,0,a._linesIndexCount,this.forcedInstanceCount||c):d.drawElementsType(b,a.indexStart,a.indexCount,this.forcedInstanceCount||c),this},b.prototype.registerBeforeRender=function(a){return this.onBeforeRenderObservable.add(a),this},b.prototype.unregisterBeforeRender=function(a){return this.onBeforeRenderObservable.removeCallback(a),this},b.prototype.registerAfterRender=function(a){return this.onAfterRenderObservable.add(a),this},b.prototype.unregisterAfterRender=function(a){return this.onAfterRenderObservable.removeCallback(a),this},b.prototype._getInstancesRenderList=function(a,b){if(void 0===b&&(b=!1),this._instanceDataStorage.isFrozen){if(b)return this._instanceDataStorage.batchCacheReplacementModeInFrozenMode.hardwareInstancedRendering[a]=!1,this._instanceDataStorage.batchCacheReplacementModeInFrozenMode.renderSelf[a]=!0,this._instanceDataStorage.batchCacheReplacementModeInFrozenMode;if(this._instanceDataStorage.previousBatch)return this._instanceDataStorage.previousBatch}var c=this.getScene(),d=c._isInIntermediateRendering(),e=d?this._internalAbstractMeshDataInfo._onlyForInstancesIntermediate:this._internalAbstractMeshDataInfo._onlyForInstances,f=this._instanceDataStorage.batchCache;if(f.mustReturn=!1,f.renderSelf[a]=b||!e&&this.isEnabled()&&this.isVisible,f.visibleInstances[a]=null,this._instanceDataStorage.visibleInstances&&!b){e=this._instanceDataStorage.visibleInstances;c=c.getRenderId();d=d?e.intermediateDefaultRenderId:e.defaultRenderId;f.visibleInstances[a]=e[c],!f.visibleInstances[a]&&d&&(f.visibleInstances[a]=e[d])}return f.hardwareInstancedRendering[a]=!b&&this._instanceDataStorage.hardwareInstancedRendering&&null!==f.visibleInstances[a]&&void 0!==f.visibleInstances[a],this._instanceDataStorage.previousBatch=f,f},b.prototype._renderWithInstances=function(a,c,d,e,f){var g=d.visibleInstances[a._id];if(!g)return this;for(var h=this._instanceDataStorage,j=h.instancesBufferSize,k=h.instancesBuffer,o=h.instancesPreviousBuffer,x=16*(g.length+1)*4;h.instancesBufferSizeb._distanceToCamera?-1:a._distanceToCamerak&&d++,0!==r&&q++,p+=r,k=r}if(i[q]++,q>f&&(f=q),0===p)e++;else{r=1/p;k=0;for(s=0;s.001&&g++}}q=this.skeleton.bones.length;p=this.getVerticesData(l.b.MatricesIndicesKind);r=this.getVerticesData(l.b.MatricesIndicesExtraKind);k=0;for(j=0;j=q||a<0)&&k++}return{skinned:!0,valid:0===e&&0===g&&0===k,report:"Number of Weights = "+c/4+" Maximum influences = "+f+" Missing Weights = "+e+" Not Sorted = "+d+" Not Normalized = "+g+" WeightCounts = ["+i+"] Number of bones = "+q+" Bad Bone Indices = "+k}},b.prototype._checkDelayState=function(){var a=this.getScene();return this._geometry?this._geometry.load(a):this.delayLoadState===s.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=s.a.DELAYLOADSTATE_LOADING,this._queueLoad(a)),this},b.prototype._queueLoad=function(a){var b=this;a._addPendingData(this);var c=-1!==this.delayLoadingFile.indexOf(".babylonbinarymeshdata");return f.b.LoadFile(this.delayLoadingFile,function(c){c instanceof ArrayBuffer?b._delayLoadingFunction(c,b):b._delayLoadingFunction(JSON.parse(c),b),b.instances.forEach(function(a){a.refreshBoundingInfo(),a._syncSubMeshes()}),b.delayLoadState=s.a.DELAYLOADSTATE_LOADED,a._removePendingData(b)},function(){},a.offlineProvider,c),this},b.prototype.isInFrustum=function(b){return this.delayLoadState!==s.a.DELAYLOADSTATE_LOADING&&!!a.prototype.isInFrustum.call(this,b)&&(this._checkDelayState(),!0)},b.prototype.setMaterialById=function(a){var b,c=this.getScene().materials;for(b=c.length-1;b>-1;b--)if(c[b].id===a)return this.material=c[b],this;c=this.getScene().multiMaterials;for(b=c.length-1;b>-1;b--)if(c[b].id===a)return this.material=c[b],this;return this},b.prototype.getAnimatables=function(){var a=new Array;return this.material&&a.push(this.material),this.skeleton&&a.push(this.skeleton),a},b.prototype.bakeTransformIntoVertices=function(a){if(!this.isVerticesDataPresent(l.b.PositionKind))return this;var b=this.subMeshes.splice(0);this._resetPointsArrayCache();var c,d=this.getVerticesData(l.b.PositionKind),e=new Array;for(c=0;c-1&&(e.morphTargetManager=c.getMorphTargetManagerById(a.morphTargetManagerId)),void 0!==a.skeletonId&&null!==a.skeletonId&&(e.skeleton=c.getLastSkeletonById(a.skeletonId),a.numBoneInfluencers&&(e.numBoneInfluencers=a.numBoneInfluencers)),a.animations){for(d=0;d4,k=j?this.getVerticesData(l.b.MatricesIndicesExtraKind):null,u=j?this.getVerticesData(l.b.MatricesWeightsExtraKind):null,a=a.getTransformMatrices(this),v=i.e.Zero(),w=new i.a,x=new i.a,y=0,z=0;z0&&(i.a.FromFloat32ArrayToRefScaled(a,Math.floor(16*f[y+h]),A,x),w.addToSelf(x));if(j)for(h=0;h<4;h++)(A=u[y+h])>0&&(i.a.FromFloat32ArrayToRefScaled(a,Math.floor(16*k[y+h]),A,x),w.addToSelf(x));i.e.TransformCoordinatesFromFloatsToRef(c._sourcePositions[z],c._sourcePositions[z+1],c._sourcePositions[z+2],w,v),v.toArray(d,z),b&&(i.e.TransformNormalFromFloatsToRef(c._sourceNormals[z],c._sourceNormals[z+1],c._sourceNormals[z+2],w,v),v.toArray(e,z)),w.reset()}return this.updateVerticesData(l.b.PositionKind,d),b&&this.updateVerticesData(l.b.NormalKind,e),this},b.MinMax=function(a){var b=null,c=null;return a.forEach(function(a){a=a.getBoundingInfo().boundingBox;b&&c?(b.minimizeInPlace(a.minimumWorld),c.maximizeInPlace(a.maximumWorld)):(b=a.minimumWorld,c=a.maximumWorld)}),b&&c?{min:b,max:c}:{min:i.e.Zero(),max:i.e.Zero()}},b.Center=function(a){a=a instanceof Array?b.MinMax(a):a;return i.e.Center(a.min,a.max)},b.MergeMeshes=function(a,c,d,e,f,g){if(void 0===c&&(c=!0),0===(a=a.filter(Boolean)).length)return null;var h;if(!d){var i=0;for(h=0;h=65536)return u.a.Warn("Cannot merge meshes because resulting mesh will have more than 65536 vertices. Please use allow32BitsIndices = true to use 32 bits indices"),null}if(g){var j;f=!1}i=new Array;var w=new Array,k=new Array;for(h=0;h0?a.name:d+a.name,(Object(n.g)(a.url,"data:")||b.UseSerializedUrlIfAny&&a.url)&&(h=a.url),f=new b(h,c,!e,a.invertY,a.samplingMode,g)}return f},a,c);return h},b.CreateFromBase64String=function(a,c,d,e,f,g,l,i,j,k){return void 0===g&&(g=b.TRILINEAR_SAMPLINGMODE),void 0===l&&(l=null),void 0===i&&(i=null),void 0===j&&(j=h.a.TEXTUREFORMAT_RGBA),new b("data:"+c,d,e,f,g,l,i,a,!1,j,void 0,void 0,k)},b.LoadFromDataString=function(a,c,d,e,f,g,n,i,j,k,m){return void 0===e&&(e=!1),void 0===g&&(g=!0),void 0===n&&(n=b.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=null),void 0===j&&(j=null),void 0===k&&(k=h.a.TEXTUREFORMAT_RGBA),"data:"!==a.substr(0,5)&&(a="data:"+a),new b(a,d,f,g,n,i,j,c,e,k,void 0,void 0,m)},b.SerializeBuffers=!0,b.ForceSerializeBuffers=!1,b.OnTextureLoadErrorObservable=new f.c,b._CubeTextureParser=function(a,b,c){throw Object(j.a)("CubeTexture")},b._CreateMirror=function(a,b,c,d){throw Object(j.a)("MirrorTexture")},b._CreateRenderTargetTexture=function(a,b,c,d,e){throw Object(j.a)("RenderTargetTexture")},b.NEAREST_SAMPLINGMODE=h.a.TEXTURE_NEAREST_SAMPLINGMODE,b.NEAREST_NEAREST_MIPLINEAR=h.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR,b.BILINEAR_SAMPLINGMODE=h.a.TEXTURE_BILINEAR_SAMPLINGMODE,b.LINEAR_LINEAR_MIPNEAREST=h.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,b.TRILINEAR_SAMPLINGMODE=h.a.TEXTURE_TRILINEAR_SAMPLINGMODE,b.LINEAR_LINEAR_MIPLINEAR=h.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR,b.NEAREST_NEAREST_MIPNEAREST=h.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST,b.NEAREST_LINEAR_MIPNEAREST=h.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST,b.NEAREST_LINEAR_MIPLINEAR=h.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR,b.NEAREST_LINEAR=h.a.TEXTURE_NEAREST_LINEAR,b.NEAREST_NEAREST=h.a.TEXTURE_NEAREST_NEAREST,b.LINEAR_NEAREST_MIPNEAREST=h.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST,b.LINEAR_NEAREST_MIPLINEAR=h.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR,b.LINEAR_LINEAR=h.a.TEXTURE_LINEAR_LINEAR,b.LINEAR_NEAREST=h.a.TEXTURE_LINEAR_NEAREST,b.EXPLICIT_MODE=h.a.TEXTURE_EXPLICIT_MODE,b.SPHERICAL_MODE=h.a.TEXTURE_SPHERICAL_MODE,b.PLANAR_MODE=h.a.TEXTURE_PLANAR_MODE,b.CUBIC_MODE=h.a.TEXTURE_CUBIC_MODE,b.PROJECTION_MODE=h.a.TEXTURE_PROJECTION_MODE,b.SKYBOX_MODE=h.a.TEXTURE_SKYBOX_MODE,b.INVCUBIC_MODE=h.a.TEXTURE_INVCUBIC_MODE,b.EQUIRECTANGULAR_MODE=h.a.TEXTURE_EQUIRECTANGULAR_MODE,b.FIXED_EQUIRECTANGULAR_MODE=h.a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE,b.FIXED_EQUIRECTANGULAR_MIRRORED_MODE=h.a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE,b.CLAMP_ADDRESSMODE=h.a.TEXTURE_CLAMP_ADDRESSMODE,b.WRAP_ADDRESSMODE=h.a.TEXTURE_WRAP_ADDRESSMODE,b.MIRROR_ADDRESSMODE=h.a.TEXTURE_MIRROR_ADDRESSMODE,b.UseSerializedUrlIfAny=!1,Object(d.c)([Object(e.d)()],b.prototype,"url",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uOffset",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vOffset",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uScale",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vScale",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"wAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"wRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"homogeneousRotationInUVTransform",void 0),Object(d.c)([Object(e.d)()],b.prototype,"isBlocking",null),b}(a.a);Object(i.b)("BABYLON.Texture",p),e.a._TextureParser=p.Parse},function(a,b,c){c.d(b,"b",function(){return r}),c.d(b,"c",function(){return s}),c.d(b,"a",function(){return t});var d=c(2),e=c(6),f=c(35),g=c(7),h=c(47),i=c(34),j=c(26),k=c(57),l=c(23),m=c(54);a=c(168);var n=c(99),o=c(138),p=c(115),q=c(139),r=function(){function a(){}return Object.defineProperty(a,"BaseUrl",{get:function(){return m.d.BaseUrl},set:function(a){m.d.BaseUrl=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"DefaultRetryStrategy",{get:function(){return m.d.DefaultRetryStrategy},set:function(a){m.d.DefaultRetryStrategy=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"CorsBehavior",{get:function(){return m.d.CorsBehavior},set:function(a){m.d.CorsBehavior=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"UseFallbackTexture",{get:function(){return l.a.UseFallbackTexture},set:function(a){l.a.UseFallbackTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RegisteredExternalClasses",{get:function(){return o.a.RegisteredExternalClasses},set:function(a){o.a.RegisteredExternalClasses=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"fallbackTexture",{get:function(){return l.a.FallbackTexture},set:function(a){l.a.FallbackTexture=a},enumerable:!1,configurable:!0}),a.FetchToRef=function(a,b,c,d,e,f){a=4*((Math.abs(a)*c%c|0)+(Math.abs(b)*d%d|0)*c);f.r=e[a]/255,f.g=e[a+1]/255,f.b=e[a+2]/255,f.a=e[a+3]/255},a.Mix=function(a,b,c){return a*(1-c)+b*c},a.Instantiate=function(a){return o.a.Instantiate(a)},a.Slice=function(a,b,c){return q.a.Slice(a,b,c)},a.SliceToArray=function(a,b,c){return q.a.SliceToArray(a,b,c)},a.SetImmediate=function(a){n.a.SetImmediate(a)},a.IsExponentOfTwo=function(a){var b=1;do b*=2;while(b1?1:Math.round(255*p)}d=n}p=k.createImageData(b,c);p.data.set(d),k.putImageData(p,0,0);o=a._ScreenshotCanvas;if(h){n=document.createElement("canvas");n.width=b,n.height=c;d=n.getContext("2d");if(!d)return;d.translate(0,c),d.scale(1,-1),d.drawImage(a._ScreenshotCanvas,0,0),o=n}i?a.ToBlob(o,function(a){var b=new FileReader;b.onload=function(a){a=a.target.result;e&&e(a)},b.readAsArrayBuffer(a)},f,j):a.EncodeScreenshotCanvasData(e,f,g,o,j)}},a.DumpDataAsync=function(b,c,d,e,f,g,h,i){return void 0===e&&(e="image/png"),void 0===g&&(g=!1),void 0===h&&(h=!1),new Promise(function(j){a.DumpData(b,c,d,function(a){return j(a)},e,f,g,h,i)})},a.ToBlob=function(a,b,c,d){void 0===c&&(c="image/png"),a.toBlob||(a.toBlob=function(a,b,c){var d=this;setTimeout(function(){for(var e=atob(d.toDataURL(b,c).split(",")[1]),f=e.length,g=new Uint8Array(f),h=0;h=b)break;if(d(g),f&&f()){a.breakLoop();break}}a.executeNext()},g)},e)},a}();l.a.FallbackTexture="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAAABgAAAAAQAAAGAAAAABcGFpbnQubmV0IDQuMC41AP/bAEMABAIDAwMCBAMDAwQEBAQFCQYFBQUFCwgIBgkNCw0NDQsMDA4QFBEODxMPDAwSGBITFRYXFxcOERkbGRYaFBYXFv/bAEMBBAQEBQUFCgYGChYPDA8WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFv/AABEIAQABAAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APH6KKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76P//Z",a.a.Apply()},function(a,b,c){c.d(b,"a",function(){return o});var d=c(2),e=c(6),f=c(35),g=c(23),h=c(26),i=c(27),j=c(1),k=c(173),l=c(60),m=c(95),n=c(7),o=(c(140),c(124),c(141),function(a){function b(c,d,g,h){void 0===h&&(h=!1);var i=a.call(this,c,d,g,h)||this;if(i.enableOfflineSupport=!1,i.disableManifestCheck=!1,i.scenes=new Array,i._virtualScenes=new Array,i.onNewSceneAddedObservable=new e.c,i.postProcesses=new Array,i.isPointerLock=!1,i.onResizeObservable=new e.c,i.onCanvasBlurObservable=new e.c,i.onCanvasFocusObservable=new e.c,i.onCanvasPointerOutObservable=new e.c,i.onBeginFrameObservable=new e.c,i.customAnimationFrameRequester=null,i.onEndFrameObservable=new e.c,i.onBeforeShaderCompilationObservable=new e.c,i.onAfterShaderCompilationObservable=new e.c,i._deterministicLockstep=!1,i._lockstepMaxSteps=4,i._timeStep=1/60,i._fps=60,i._deltaTime=0,i._drawCalls=new l.a,i.canvasTabIndex=1,i.disablePerformanceMonitorInBackground=!1,i._performanceMonitor=new k.a,b.Instances.push(i),!c)return i;if(g=i._creationOptions,c.getContext){var j=c;if(i._sharedInit(j,!!g.doNotHandleTouchAction,g.audioEngine),Object(f.e)()){var n=document;i._onFullscreenChange=function(){void 0!==n.fullscreen?i.isFullscreen=n.fullscreen:void 0!==n.mozFullScreen?i.isFullscreen=n.mozFullScreen:void 0!==n.webkitIsFullScreen?i.isFullscreen=n.webkitIsFullScreen:void 0!==n.msIsFullScreen&&(i.isFullscreen=n.msIsFullScreen),i.isFullscreen&&i._pointerLockRequested&&j&&b._RequestPointerlock(j)},document.addEventListener("fullscreenchange",i._onFullscreenChange,!1),document.addEventListener("mozfullscreenchange",i._onFullscreenChange,!1),document.addEventListener("webkitfullscreenchange",i._onFullscreenChange,!1),document.addEventListener("msfullscreenchange",i._onFullscreenChange,!1),i._onPointerLockChange=function(){i.isPointerLock=n.mozPointerLockElement===j||n.webkitPointerLockElement===j||n.msPointerLockElement===j||n.pointerLockElement===j},document.addEventListener("pointerlockchange",i._onPointerLockChange,!1),document.addEventListener("mspointerlockchange",i._onPointerLockChange,!1),document.addEventListener("mozpointerlockchange",i._onPointerLockChange,!1),document.addEventListener("webkitpointerlockchange",i._onPointerLockChange,!1),!b.audioEngine&&g.audioEngine&&b.AudioEngineFactory&&(b.audioEngine=b.AudioEngineFactory(i.getRenderingCanvas(),i.getAudioContext(),i.getAudioDestination()))}i._connectVREvents(),i.enableOfflineSupport=void 0!==b.OfflineProviderFactory,i._deterministicLockstep=!!g.deterministicLockstep,i._lockstepMaxSteps=g.lockstepMaxSteps||0,i._timeStep=g.timeStep||1/60}return i._prepareVRComponent(),g.autoEnableWebVR&&i.initWebVR(),i}return Object(d.d)(b,a),Object.defineProperty(b,"NpmPackage",{get:function(){return i.a.NpmPackage},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Version",{get:function(){return i.a.Version},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Instances",{get:function(){return g.a.Instances},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LastCreatedEngine",{get:function(){return g.a.LastCreatedEngine},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LastCreatedScene",{get:function(){return g.a.LastCreatedScene},enumerable:!1,configurable:!0}),b.prototype.createImageBitmap=function(a,b){return createImageBitmap(a,b)},b.prototype.resizeImageBitmap=function(a,b,c){var d=this.createCanvas(b,c).getContext("2d");if(!d)throw new Error("Unable to get 2d context for resizeImageBitmap");return d.drawImage(a,0,0),d.getImageData(0,0,b,c).data},b.MarkAllMaterialsAsDirty=function(a,c){for(var d=0;d0?this.customAnimationFrameRequester?(this.customAnimationFrameRequester.requestID=this._queueNewFrame(this.customAnimationFrameRequester.renderFunction||this._boundRenderFunction,this.customAnimationFrameRequester),this._frameHandler=this.customAnimationFrameRequester.requestID):this.isVRPresenting()?this._requestVRFrame():this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow()):this._renderingQueueLaunched=!1},b.prototype._renderViews=function(){return!1},b.prototype.switchFullscreen=function(a){this.isFullscreen?this.exitFullscreen():this.enterFullscreen(a)},b.prototype.enterFullscreen=function(a){this.isFullscreen||(this._pointerLockRequested=a,this._renderingCanvas&&b._RequestFullscreen(this._renderingCanvas))},b.prototype.exitFullscreen=function(){this.isFullscreen&&b._ExitFullscreen()},b.prototype.enterPointerlock=function(){this._renderingCanvas&&b._RequestPointerlock(this._renderingCanvas)},b.prototype.exitPointerlock=function(){b._ExitPointerlock()},b.prototype.beginFrame=function(){this._measureFps(),this.onBeginFrameObservable.notifyObservers(this),a.prototype.beginFrame.call(this)},b.prototype.endFrame=function(){a.prototype.endFrame.call(this),this._submitVRFrame(),this.onEndFrameObservable.notifyObservers(this)},b.prototype.resize=function(b){void 0===b&&(b=!1),this.isVRPresenting()||a.prototype.resize.call(this,b)},b.prototype.setSize=function(b,c,d){if(void 0===d&&(d=!1),!this._renderingCanvas)return!1;if(!a.prototype.setSize.call(this,b,c,d))return!1;if(this.scenes){for(b=0;b1&&e){var g=this.createTransformFeedback();this.bindTransformFeedback(g),this.setTranformFeedbackVaryings(f,e),a.transformFeedback=g}return d.linkProgram(f),this.webGLVersion>1&&e&&this.bindTransformFeedback(null),a.context=d,a.vertexShader=b,a.fragmentShader=c,a.isParallelCompiled||this._finalizePipelineContext(a),f},b.prototype._releaseTexture=function(b){a.prototype._releaseTexture.call(this,b)},b.prototype._releaseRenderTargetWrapper=function(b){a.prototype._releaseRenderTargetWrapper.call(this,b),this.scenes.forEach(function(a){a.postProcesses.forEach(function(a){a._outputTexture===b&&(a._outputTexture=null)}),a.cameras.forEach(function(a){a._postProcesses.forEach(function(a){a&&a._outputTexture===b&&(a._outputTexture=null)})})})},b.prototype._rescaleTexture=function(a,c,d,e,f){var g=this;this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_MAG_FILTER,this._gl.LINEAR),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_MIN_FILTER,this._gl.LINEAR),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_WRAP_S,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_WRAP_T,this._gl.CLAMP_TO_EDGE);var h=this.createRenderTargetTexture({width:c.width,height:c.height},{generateMipMaps:!1,type:j.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:j.a.TEXTURE_BILINEAR_SAMPLINGMODE,generateDepthBuffer:!1,generateStencilBuffer:!1});!this._rescalePostProcess&&b._RescalePostProcessFactory&&(this._rescalePostProcess=b._RescalePostProcessFactory(this)),this._rescalePostProcess.getEffect().executeWhenCompiled(function(){g._rescalePostProcess.onApply=function(b){b._bindTexture("textureSampler",a)};var b=d;b||(b=g.scenes[g.scenes.length-1]),b.postProcessManager.directRender([g._rescalePostProcess],h,!0),g._bindTextureDirectly(g._gl.TEXTURE_2D,c,!0),g._gl.copyTexImage2D(g._gl.TEXTURE_2D,0,e,0,0,c.width,c.height,0),g.unBindFramebuffer(h),h.dispose(),f&&f()})},b.prototype.getFps=function(){return this._fps},b.prototype.getDeltaTime=function(){return this._deltaTime},b.prototype._measureFps=function(){this._performanceMonitor.sampleFrame(),this._fps=this._performanceMonitor.averageFPS,this._deltaTime=this._performanceMonitor.instantaneousFrameTime||0},b.prototype._uploadImageToTexture=function(a,b,c,d){void 0===c&&(c=0),void 0===d&&(d=0);var e=this._gl,f=this._getWebGLTextureType(a.type),g=this._getInternalFormat(a.format),h=this._getRGBABufferInternalSizedFormat(a.type,g),i=a.isCube?e.TEXTURE_CUBE_MAP:e.TEXTURE_2D;this._bindTextureDirectly(i,a,!0),this._unpackFlipY(a.invertY);var j=e.TEXTURE_2D;a.isCube&&(j=e.TEXTURE_CUBE_MAP_POSITIVE_X+c),e.texImage2D(j,d,h,g,f,b),this._bindTextureDirectly(i,null,!0)},b.prototype.updateTextureComparisonFunction=function(a,b){if(1!==this.webGLVersion){var c=this._gl;a.isCube?(this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,a,!0),0===b?(c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_FUNC,j.a.LEQUAL),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_FUNC,b),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE)),this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,null)):(this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0),0===b?(c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_FUNC,j.a.LEQUAL),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_FUNC,b),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE)),this._bindTextureDirectly(this._gl.TEXTURE_2D,null)),a._comparisonFunction=b}else n.a.Error("WebGL 1 does not support texture comparison.")},b.prototype.createInstancesBuffer=function(a){var b=this._gl.createBuffer();if(!b)throw new Error("Unable to create instance buffer");b=new m.a(b);return b.capacity=a,this.bindArrayBuffer(b),this._gl.bufferData(this._gl.ARRAY_BUFFER,a,this._gl.DYNAMIC_DRAW),b.references=1,b},b.prototype.deleteInstancesBuffer=function(a){this._gl.deleteBuffer(a)},b.prototype._clientWaitAsync=function(a,b,c){void 0===b&&(b=0),void 0===c&&(c=10);var d=this._gl;return new Promise(function(e,f){var g=function(){var h=d.clientWaitSync(a,b,0);h!=d.WAIT_FAILED?h!=d.TIMEOUT_EXPIRED?e():setTimeout(g,c):f()};g()})},b.prototype._readPixelsAsync=function(a,b,c,d,e,f,g){if(this._webGLVersion<2)throw new Error("_readPixelsAsync only work on WebGL2+");var h=this._gl,i=h.createBuffer();h.bindBuffer(h.PIXEL_PACK_BUFFER,i),h.bufferData(h.PIXEL_PACK_BUFFER,g.byteLength,h.STREAM_READ),h.readPixels(a,b,c,d,e,f,0),h.bindBuffer(h.PIXEL_PACK_BUFFER,null);var j=h.fenceSync(h.SYNC_GPU_COMMANDS_COMPLETE,0);return j?(h.flush(),this._clientWaitAsync(j,0,10).then(function(){return h.deleteSync(j),h.bindBuffer(h.PIXEL_PACK_BUFFER,i),h.getBufferSubData(h.PIXEL_PACK_BUFFER,0,g),h.bindBuffer(h.PIXEL_PACK_BUFFER,null),h.deleteBuffer(i),g})):null},b.prototype.dispose=function(){for(this.hideLoadingUI(),this.onNewSceneAddedObservable.clear();this.postProcesses.length;)this.postProcesses[0].dispose();for(this._rescalePostProcess&&this._rescalePostProcess.dispose();this.scenes.length;)this.scenes[0].dispose();for(;this._virtualScenes.length;)this._virtualScenes[0].dispose();1===b.Instances.length&&b.audioEngine&&(b.audioEngine.dispose(),b.audioEngine=null),this.disableVR(),this.deviceInputSystem&&this.deviceInputSystem.dispose(),Object(f.e)()&&(window.removeEventListener("blur",this._onBlur),window.removeEventListener("focus",this._onFocus),this._renderingCanvas&&(this._renderingCanvas.removeEventListener("focus",this._onCanvasFocus),this._renderingCanvas.removeEventListener("blur",this._onCanvasBlur),this._renderingCanvas.removeEventListener("pointerout",this._onCanvasPointerOut)),Object(f.c)()&&(document.removeEventListener("fullscreenchange",this._onFullscreenChange),document.removeEventListener("mozfullscreenchange",this._onFullscreenChange),document.removeEventListener("webkitfullscreenchange",this._onFullscreenChange),document.removeEventListener("msfullscreenchange",this._onFullscreenChange),document.removeEventListener("pointerlockchange",this._onPointerLockChange),document.removeEventListener("mspointerlockchange",this._onPointerLockChange),document.removeEventListener("mozpointerlockchange",this._onPointerLockChange),document.removeEventListener("webkitpointerlockchange",this._onPointerLockChange))),a.prototype.dispose.call(this);var c=b.Instances.indexOf(this);c>=0&&b.Instances.splice(c,1),this.onResizeObservable.clear(),this.onCanvasBlurObservable.clear(),this.onCanvasFocusObservable.clear(),this.onCanvasPointerOutObservable.clear(),this.onBeginFrameObservable.clear(),this.onEndFrameObservable.clear()},b.prototype._disableTouchAction=function(){this._renderingCanvas&&this._renderingCanvas.setAttribute&&(this._renderingCanvas.setAttribute("touch-action","none"),this._renderingCanvas.style.touchAction="none",this._renderingCanvas.style.msTouchAction="none")},b.prototype.displayLoadingUI=function(){if(Object(f.e)()){var a=this.loadingScreen;a&&a.displayLoadingUI()}},b.prototype.hideLoadingUI=function(){if(Object(f.e)()){var a=this._loadingScreen;a&&a.hideLoadingUI()}},Object.defineProperty(b.prototype,"loadingScreen",{get:function(){return!this._loadingScreen&&this._renderingCanvas&&(this._loadingScreen=b.DefaultLoadingScreenFactory(this._renderingCanvas)),this._loadingScreen},set:function(a){this._loadingScreen=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loadingUIText",{set:function(a){this.loadingScreen.loadingUIText=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loadingUIBackgroundColor",{set:function(a){this.loadingScreen.loadingUIBackgroundColor=a},enumerable:!1,configurable:!0}),b.prototype.createVideoElement=function(a){return document.createElement("video")},b._RequestPointerlock=function(a){a.requestPointerLock=a.requestPointerLock||a.msRequestPointerLock||a.mozRequestPointerLock||a.webkitRequestPointerLock,a.requestPointerLock&&a.requestPointerLock()},b._ExitPointerlock=function(){var a=document;document.exitPointerLock=document.exitPointerLock||a.msExitPointerLock||a.mozExitPointerLock||a.webkitExitPointerLock,document.exitPointerLock&&document.exitPointerLock()},b._RequestFullscreen=function(a){var b=a.requestFullscreen||a.msRequestFullscreen||a.webkitRequestFullscreen||a.mozRequestFullScreen;b&&b.call(a)},b._ExitFullscreen=function(){var a=document;document.exitFullscreen?document.exitFullscreen():a.mozCancelFullScreen?a.mozCancelFullScreen():a.webkitCancelFullScreen?a.webkitCancelFullScreen():a.msCancelFullScreen&&a.msCancelFullScreen()},b.ALPHA_DISABLE=j.a.ALPHA_DISABLE,b.ALPHA_ADD=j.a.ALPHA_ADD,b.ALPHA_COMBINE=j.a.ALPHA_COMBINE,b.ALPHA_SUBTRACT=j.a.ALPHA_SUBTRACT,b.ALPHA_MULTIPLY=j.a.ALPHA_MULTIPLY,b.ALPHA_MAXIMIZED=j.a.ALPHA_MAXIMIZED,b.ALPHA_ONEONE=j.a.ALPHA_ONEONE,b.ALPHA_PREMULTIPLIED=j.a.ALPHA_PREMULTIPLIED,b.ALPHA_PREMULTIPLIED_PORTERDUFF=j.a.ALPHA_PREMULTIPLIED_PORTERDUFF,b.ALPHA_INTERPOLATE=j.a.ALPHA_INTERPOLATE,b.ALPHA_SCREENMODE=j.a.ALPHA_SCREENMODE,b.DELAYLOADSTATE_NONE=j.a.DELAYLOADSTATE_NONE,b.DELAYLOADSTATE_LOADED=j.a.DELAYLOADSTATE_LOADED,b.DELAYLOADSTATE_LOADING=j.a.DELAYLOADSTATE_LOADING,b.DELAYLOADSTATE_NOTLOADED=j.a.DELAYLOADSTATE_NOTLOADED,b.NEVER=j.a.NEVER,b.ALWAYS=j.a.ALWAYS,b.LESS=j.a.LESS,b.EQUAL=j.a.EQUAL,b.LEQUAL=j.a.LEQUAL,b.GREATER=j.a.GREATER,b.GEQUAL=j.a.GEQUAL,b.NOTEQUAL=j.a.NOTEQUAL,b.KEEP=j.a.KEEP,b.REPLACE=j.a.REPLACE,b.INCR=j.a.INCR,b.DECR=j.a.DECR,b.INVERT=j.a.INVERT,b.INCR_WRAP=j.a.INCR_WRAP,b.DECR_WRAP=j.a.DECR_WRAP,b.TEXTURE_CLAMP_ADDRESSMODE=j.a.TEXTURE_CLAMP_ADDRESSMODE,b.TEXTURE_WRAP_ADDRESSMODE=j.a.TEXTURE_WRAP_ADDRESSMODE,b.TEXTURE_MIRROR_ADDRESSMODE=j.a.TEXTURE_MIRROR_ADDRESSMODE,b.TEXTUREFORMAT_ALPHA=j.a.TEXTUREFORMAT_ALPHA,b.TEXTUREFORMAT_LUMINANCE=j.a.TEXTUREFORMAT_LUMINANCE,b.TEXTUREFORMAT_LUMINANCE_ALPHA=j.a.TEXTUREFORMAT_LUMINANCE_ALPHA,b.TEXTUREFORMAT_RGB=j.a.TEXTUREFORMAT_RGB,b.TEXTUREFORMAT_RGBA=j.a.TEXTUREFORMAT_RGBA,b.TEXTUREFORMAT_RED=j.a.TEXTUREFORMAT_RED,b.TEXTUREFORMAT_R=j.a.TEXTUREFORMAT_R,b.TEXTUREFORMAT_RG=j.a.TEXTUREFORMAT_RG,b.TEXTUREFORMAT_RED_INTEGER=j.a.TEXTUREFORMAT_RED_INTEGER,b.TEXTUREFORMAT_R_INTEGER=j.a.TEXTUREFORMAT_R_INTEGER,b.TEXTUREFORMAT_RG_INTEGER=j.a.TEXTUREFORMAT_RG_INTEGER,b.TEXTUREFORMAT_RGB_INTEGER=j.a.TEXTUREFORMAT_RGB_INTEGER,b.TEXTUREFORMAT_RGBA_INTEGER=j.a.TEXTUREFORMAT_RGBA_INTEGER,b.TEXTURETYPE_UNSIGNED_BYTE=j.a.TEXTURETYPE_UNSIGNED_BYTE,b.TEXTURETYPE_UNSIGNED_INT=j.a.TEXTURETYPE_UNSIGNED_INT,b.TEXTURETYPE_FLOAT=j.a.TEXTURETYPE_FLOAT,b.TEXTURETYPE_HALF_FLOAT=j.a.TEXTURETYPE_HALF_FLOAT,b.TEXTURETYPE_BYTE=j.a.TEXTURETYPE_BYTE,b.TEXTURETYPE_SHORT=j.a.TEXTURETYPE_SHORT,b.TEXTURETYPE_UNSIGNED_SHORT=j.a.TEXTURETYPE_UNSIGNED_SHORT,b.TEXTURETYPE_INT=j.a.TEXTURETYPE_INT,b.TEXTURETYPE_UNSIGNED_INTEGER=j.a.TEXTURETYPE_UNSIGNED_INTEGER,b.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4=j.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4,b.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1=j.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1,b.TEXTURETYPE_UNSIGNED_SHORT_5_6_5=j.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5,b.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV=j.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV,b.TEXTURETYPE_UNSIGNED_INT_24_8=j.a.TEXTURETYPE_UNSIGNED_INT_24_8,b.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV=j.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV,b.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV=j.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV,b.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV=j.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV,b.TEXTURE_NEAREST_SAMPLINGMODE=j.a.TEXTURE_NEAREST_SAMPLINGMODE,b.TEXTURE_BILINEAR_SAMPLINGMODE=j.a.TEXTURE_BILINEAR_SAMPLINGMODE,b.TEXTURE_TRILINEAR_SAMPLINGMODE=j.a.TEXTURE_TRILINEAR_SAMPLINGMODE,b.TEXTURE_NEAREST_NEAREST_MIPLINEAR=j.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR,b.TEXTURE_LINEAR_LINEAR_MIPNEAREST=j.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,b.TEXTURE_LINEAR_LINEAR_MIPLINEAR=j.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR,b.TEXTURE_NEAREST_NEAREST_MIPNEAREST=j.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST,b.TEXTURE_NEAREST_LINEAR_MIPNEAREST=j.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST,b.TEXTURE_NEAREST_LINEAR_MIPLINEAR=j.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR,b.TEXTURE_NEAREST_LINEAR=j.a.TEXTURE_NEAREST_LINEAR,b.TEXTURE_NEAREST_NEAREST=j.a.TEXTURE_NEAREST_NEAREST,b.TEXTURE_LINEAR_NEAREST_MIPNEAREST=j.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST,b.TEXTURE_LINEAR_NEAREST_MIPLINEAR=j.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR,b.TEXTURE_LINEAR_LINEAR=j.a.TEXTURE_LINEAR_LINEAR,b.TEXTURE_LINEAR_NEAREST=j.a.TEXTURE_LINEAR_NEAREST,b.TEXTURE_EXPLICIT_MODE=j.a.TEXTURE_EXPLICIT_MODE,b.TEXTURE_SPHERICAL_MODE=j.a.TEXTURE_SPHERICAL_MODE,b.TEXTURE_PLANAR_MODE=j.a.TEXTURE_PLANAR_MODE,b.TEXTURE_CUBIC_MODE=j.a.TEXTURE_CUBIC_MODE,b.TEXTURE_PROJECTION_MODE=j.a.TEXTURE_PROJECTION_MODE,b.TEXTURE_SKYBOX_MODE=j.a.TEXTURE_SKYBOX_MODE,b.TEXTURE_INVCUBIC_MODE=j.a.TEXTURE_INVCUBIC_MODE,b.TEXTURE_EQUIRECTANGULAR_MODE=j.a.TEXTURE_EQUIRECTANGULAR_MODE,b.TEXTURE_FIXED_EQUIRECTANGULAR_MODE=j.a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE,b.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE=j.a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE,b.SCALEMODE_FLOOR=j.a.SCALEMODE_FLOOR,b.SCALEMODE_NEAREST=j.a.SCALEMODE_NEAREST,b.SCALEMODE_CEILING=j.a.SCALEMODE_CEILING,b._RescalePostProcessFactory=null,b}(i.a))},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.WithinEpsilon=function(a,b,c){return void 0===c&&(c=1401298e-51),Math.abs(a-b)<=c},a.ToHex=function(a){var b=a.toString(16);return a<=15?("0"+b).toUpperCase():b.toUpperCase()},a.Sign=function(a){return 0===(a=+a)||isNaN(a)?a:a>0?1:-1},a.Clamp=function(a,b,c){return void 0===b&&(b=0),void 0===c&&(c=1),Math.min(c,Math.max(b,a))},a.Log2=function(a){return Math.log(a)*Math.LOG2E},a.ILog2=function(a){if(Math.log2)return Math.floor(Math.log2(a));if(a<0)return NaN;if(0===a)return-1/0;var b=0;if(a<1){for(;a<1;)b++,a*=2;b=-b}else if(a>1)for(;a>1;)b++,a=Math.floor(a/2);return b},a.Repeat=function(a,b){return a-Math.floor(a/b)*b},a.Normalize=function(a,b,c){return(a-b)/(c-b)},a.Denormalize=function(a,b,c){return a*(c-b)+b},a.DeltaAngle=function(b,c){c=a.Repeat(c-b,360);return c>180&&(c-=360),c},a.PingPong=function(b,c){b=a.Repeat(b,2*c);return c-Math.abs(b-c)},a.SmoothStep=function(b,c,d){d=a.Clamp(d);return c*(d=-2*d*d*d+3*d*d)+b*(1-d)},a.MoveTowards=function(b,c,d){return Math.abs(c-b)<=d?c:b+a.Sign(c-b)*d},a.MoveTowardsAngle=function(b,c,d){var e=a.DeltaAngle(b,c),f;return-d180&&(c-=360),b+c*a.Clamp(d)},a.InverseLerp=function(b,c,d){return b!=c?a.Clamp((d-b)/(c-b)):0},a.Hermite=function(a,b,c,d,e){var f=e*e,g=e*f;return a*(2*g-3*f+1)+c*(-2*g+3*f)+b*(g-2*f+e)+d*(g-f)},a.Hermite1stDerivative=function(a,b,c,d,e){var f=e*e;return 6*(f-e)*a+(3*f-4*e+1)*b+6*(-f+e)*c+(3*f-2*e)*d},a.RandomRange=function(a,b){return a===b?a:Math.random()*(b-a)+a},a.RangeToPercent=function(a,b,c){return(a-b)/(c-b)},a.PercentToRange=function(a,b,c){return(c-b)*a+b},a.NormalizeRadians=function(b){return b-=a.TwoPi*Math.floor((b+Math.PI)/a.TwoPi)},a.HCF=function(b,c){b=b%c;return 0===b?c:a.HCF(c,b)},a.TwoPi=2*Math.PI,a}()},function(a,b,c){c.d(b,"a",function(){return l});var d=c(7),e=c(21),f=c(23),g=c(4),h=c(52),i=c(1),j=c(8),k=c(134),l=function(){function a(){}return a.BindSceneUniformBuffer=function(a,b){b.bindToEffect(a,"Scene")},a.PrepareDefinesForMergedUV=function(a,b,c){b._needUVs=!0,b[c]=!0,a.getTextureMatrix().isIdentityAs3x2()?(b[c+"DIRECTUV"]=a.coordinatesIndex+1,b["MAINUV"+(a.coordinatesIndex+1)]=!0):b[c+"DIRECTUV"]=0},a.BindTextureMatrix=function(a,b,c){a=a.getTextureMatrix();b.updateMatrix(c+"Matrix",a)},a.GetFogState=function(a,b){return b.fogEnabled&&a.applyFog&&b.fogMode!==e.a.FOGMODE_NONE},a.PrepareDefinesForMisc=function(a,b,c,d,e,f,g){g._areMiscDirty&&(g.LOGARITHMICDEPTH=c,g.POINTSIZE=d,g.FOG=e&&this.GetFogState(a,b),g.NONUNIFORMSCALING=a.nonUniformScaling,g.ALPHATEST=f)},a.PrepareDefinesForFrameBoundValues=function(a,b,c,d,e,f){void 0===e&&(e=null),void 0===f&&(f=!1);var g,h,i,j,k,l=!1;g=null==e?void 0!==a.clipPlane&&null!==a.clipPlane:e,h=null==e?void 0!==a.clipPlane2&&null!==a.clipPlane2:e,i=null==e?void 0!==a.clipPlane3&&null!==a.clipPlane3:e,j=null==e?void 0!==a.clipPlane4&&null!==a.clipPlane4:e,k=null==e?void 0!==a.clipPlane5&&null!==a.clipPlane5:e,a=null==e?void 0!==a.clipPlane6&&null!==a.clipPlane6:e,c.CLIPPLANE!==g&&(c.CLIPPLANE=g,l=!0),c.CLIPPLANE2!==h&&(c.CLIPPLANE2=h,l=!0),c.CLIPPLANE3!==i&&(c.CLIPPLANE3=i,l=!0),c.CLIPPLANE4!==j&&(c.CLIPPLANE4=j,l=!0),c.CLIPPLANE5!==k&&(c.CLIPPLANE5=k,l=!0),c.CLIPPLANE6!==a&&(c.CLIPPLANE6=a,l=!0),c.DEPTHPREPASS!==!b.getColorWrite()&&(c.DEPTHPREPASS=!c.DEPTHPREPASS,l=!0),c.INSTANCES!==d&&(c.INSTANCES=d,l=!0),c.THIN_INSTANCES!==f&&(c.THIN_INSTANCES=f,l=!0),l&&c.markAsUnprocessed()},a.PrepareDefinesForBones=function(a,b){if(a.useBones&&a.computeBonesUsingShaders&&a.skeleton){b.NUM_BONE_INFLUENCERS=a.numBoneInfluencers;var c=void 0!==b.BONETEXTURE;if(a.skeleton.isUsingTextureForMatrices&&c)b.BONETEXTURE=!0;else{b.BonesPerMesh=a.skeleton.bones.length+1,b.BONETEXTURE=!c&&void 0;c=a.getScene().prePassRenderer;if(c&&c.enabled){c=-1===c.excludedSkinnedMesh.indexOf(a);b.BONES_VELOCITY_ENABLED=c}}}else b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0},a.PrepareDefinesForMorphTargets=function(a,b){a=a.morphTargetManager;a?(b.MORPHTARGETS_UV=a.supportsUVs&&b.UV1,b.MORPHTARGETS_TANGENT=a.supportsTangents&&b.TANGENT,b.MORPHTARGETS_NORMAL=a.supportsNormals&&b.NORMAL,b.MORPHTARGETS=a.numInfluencers>0,b.NUM_MORPH_INFLUENCERS=a.numInfluencers,b.MORPHTARGETS_TEXTURE=a.isUsingTextureForTargets):(b.MORPHTARGETS_UV=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS=!1,b.NUM_MORPH_INFLUENCERS=0)},a.PrepareDefinesForAttributes=function(a,b,c,d,e,f){if(void 0===e&&(e=!1),void 0===f&&(f=!0),!b._areAttributesDirty&&b._needNormals===b._normals&&b._needUVs===b._uvs)return!1;b._normals=b._needNormals,b._uvs=b._needUVs,b.NORMAL=b._needNormals&&a.isVerticesDataPresent(g.b.NormalKind),b._needNormals&&a.isVerticesDataPresent(g.b.TangentKind)&&(b.TANGENT=!0);for(var h=1;h<=i.a.MAX_SUPPORTED_UV_SETS;++h)b["UV"+h]=!!b._needUVs&&a.isVerticesDataPresent("uv"+(1===h?"":h));if(c){h=a.useVertexColors&&a.isVerticesDataPresent(g.b.ColorKind);b.VERTEXCOLOR=h,b.VERTEXALPHA=a.hasVertexAlpha&&h&&f}return d&&this.PrepareDefinesForBones(a,b),e&&this.PrepareDefinesForMorphTargets(a,b),!0},a.PrepareDefinesForMultiview=function(a,b){if(a.activeCamera){var c=b.MULTIVIEW;b.MULTIVIEW=null!==a.activeCamera.outputRenderTarget&&a.activeCamera.outputRenderTarget.getViewCount()>1,b.MULTIVIEW!=c&&b.markAsUnprocessed()}},a.PrepareDefinesForOIT=function(a,b,c){var d=b.ORDER_INDEPENDENT_TRANSPARENCY;b.ORDER_INDEPENDENT_TRANSPARENCY=a.useOrderIndependentTransparency&&c,d!==b.ORDER_INDEPENDENT_TRANSPARENCY&&b.markAsUnprocessed()},a.PrepareDefinesForPrePass=function(a,b,c){var d=b.PREPASS;if(b._arePrePassDirty){var e=[{type:i.a.PREPASS_POSITION_TEXTURE_TYPE,define:"PREPASS_POSITION",index:"PREPASS_POSITION_INDEX"},{type:i.a.PREPASS_VELOCITY_TEXTURE_TYPE,define:"PREPASS_VELOCITY",index:"PREPASS_VELOCITY_INDEX"},{type:i.a.PREPASS_REFLECTIVITY_TEXTURE_TYPE,define:"PREPASS_REFLECTIVITY",index:"PREPASS_REFLECTIVITY_INDEX"},{type:i.a.PREPASS_IRRADIANCE_TEXTURE_TYPE,define:"PREPASS_IRRADIANCE",index:"PREPASS_IRRADIANCE_INDEX"},{type:i.a.PREPASS_ALBEDO_SQRT_TEXTURE_TYPE,define:"PREPASS_ALBEDO_SQRT",index:"PREPASS_ALBEDO_SQRT_INDEX"},{type:i.a.PREPASS_DEPTH_TEXTURE_TYPE,define:"PREPASS_DEPTH",index:"PREPASS_DEPTH_INDEX"},{type:i.a.PREPASS_NORMAL_TEXTURE_TYPE,define:"PREPASS_NORMAL",index:"PREPASS_NORMAL_INDEX"}];if(a.prePassRenderer&&a.prePassRenderer.enabled&&c){b.PREPASS=!0,b.SCENE_MRT_COUNT=a.prePassRenderer.mrtCount;for(c=0;c0&&(g.shadowEnabled=!0,f.prepareDefines(e,d))}}c.lightmapMode!=h.a.LIGHTMAP_DEFAULT?(g.lightmapMode=!0,e["LIGHTMAPEXCLUDED"+d]=!0,e["LIGHTMAPNOSPECULAR"+d]=c.lightmapMode==h.a.LIGHTMAP_SHADOWSONLY):(e["LIGHTMAPEXCLUDED"+d]=!1,e["LIGHTMAPNOSPECULAR"+d]=!1)},a.PrepareDefinesForLights=function(a,b,c,d,e,f){if(void 0===e&&(e=4),void 0===f&&(f=!1),!c._areLightsDirty)return c._needNormals;var g=0,h={needNormals:!1,needRebuild:!1,lightmapMode:!1,shadowEnabled:!1,specularEnabled:!1};if(a.lightsEnabled&&!f)for(var f=0,i=b.lightSources;f0&&(e=d+f,b.addFallback(e,"LIGHT"+f)),a.SHADOWS||(a["SHADOW"+f]&&b.addFallback(d,"SHADOW"+f),a["SHADOWPCF"+f]&&b.addFallback(d,"SHADOWPCF"+f),a["SHADOWPCSS"+f]&&b.addFallback(d,"SHADOWPCSS"+f),a["SHADOWPOISSON"+f]&&b.addFallback(d,"SHADOWPOISSON"+f),a["SHADOWESM"+f]&&b.addFallback(d,"SHADOWESM"+f),a["SHADOWCLOSEESM"+f]&&b.addFallback(d,"SHADOWCLOSEESM"+f));return e++},a.PrepareAttributesForMorphTargetsInfluencers=function(a,b,c){this._TmpMorphInfluencers.NUM_MORPH_INFLUENCERS=c,this.PrepareAttributesForMorphTargets(a,b,this._TmpMorphInfluencers)},a.PrepareAttributesForMorphTargets=function(a,b,c){var e=c.NUM_MORPH_INFLUENCERS;if(e>0&&f.a.LastCreatedEngine){var h=f.a.LastCreatedEngine.getCaps().maxVertexAttribs,i=b.morphTargetManager;if(null==i?void 0:i.isUsingTextureForTargets)return;for(var j=i&&i.supportsNormals&&c.NORMAL,k=i&&i.supportsTangents&&c.TANGENT,i=i&&i.supportsUVs&&c.UV1,c=0;ch&&d.a.Error("Cannot add more vertex attributes for mesh "+b.name)}},a.PrepareAttributesForBones=function(a,b,c,d){c.NUM_BONE_INFLUENCERS>0&&(d.addCPUSkinningFallback(0,b),a.push(g.b.MatricesIndicesKind),a.push(g.b.MatricesWeightsKind),c.NUM_BONE_INFLUENCERS>4&&(a.push(g.b.MatricesIndicesExtraKind),a.push(g.b.MatricesWeightsExtraKind)))},a.PrepareAttributesForInstances=function(a,b){(b.INSTANCES||b.THIN_INSTANCES)&&this.PushAttributesForInstances(a,!!b.PREPASS_VELOCITY)},a.PushAttributesForInstances=function(a,b){void 0===b&&(b=!1),a.push("world0"),a.push("world1"),a.push("world2"),a.push("world3"),b&&(a.push("previousWorld0"),a.push("previousWorld1"),a.push("previousWorld2"),a.push("previousWorld3"))},a.BindLightProperties=function(a,b,c){a.transferToEffect(b,c+"")},a.BindLight=function(a,b,c,d,e,f){void 0===f&&(f=!0),a._bindLight(b,c,d,e,f)},a.BindLights=function(a,b,c,d,e){void 0===e&&(e=4);for(var e=Math.min(b.lightSources.length,e),f=0;f-1){var f=e.getTransformMatrixTexture(b);c.setTexture("boneSampler",f),c.setFloat("boneTextureWidth",4*(e.bones.length+1))}else{f=e.getTransformMatrices(b);f&&(c.setMatrices("mBones",f),d&&b.getScene().prePassRenderer&&b.getScene().prePassRenderer.getIndex(i.a.PREPASS_VELOCITY_TEXTURE_TYPE)&&(d.previousBones[b.uniqueId]||(d.previousBones[b.uniqueId]=f.slice()),c.setMatrices("mPreviousBones",d.previousBones[b.uniqueId]),a._CopyBonesTransformationMatrices(f,d.previousBones[b.uniqueId])))}}},a._CopyBonesTransformationMatrices=function(a,b){return b.set(a),b},a.BindMorphTargetParameters=function(a,b){var c=a.morphTargetManager;a&&c&&b.setFloatArray("morphTargetInfluences",c.influences)},a.BindLogDepth=function(a,b,c){a.LOGARITHMICDEPTH&&b.setFloat("logarithmicDepthConstant",2/(Math.log(c.activeCamera.maxZ+1)/Math.LN2))},a.BindClipPlane=function(a,b){k.a.BindClipPlane(a,b)},a._TmpMorphInfluencers={NUM_MORPH_INFLUENCERS:0},a._tempFogColor=j.a.Black(),a}()},function(a,b,c){var d,e,f,g,h;c.d(b,"a",function(){return d}),c.d(b,"c",function(){return e}),c.d(b,"b",function(){return f}),c.d(b,"e",function(){return g}),c.d(b,"d",function(){return h}),(function(a){a[a.Generic=0]="Generic",a[a.Keyboard=1]="Keyboard",a[a.Mouse=2]="Mouse",a[a.Touch=3]="Touch",a[a.DualShock=4]="DualShock",a[a.Xbox=5]="Xbox",a[a.Switch=6]="Switch"})(d||(d={})),(function(a){a[a.Horizontal=0]="Horizontal",a[a.Vertical=1]="Vertical",a[a.LeftClick=2]="LeftClick",a[a.MiddleClick=3]="MiddleClick",a[a.RightClick=4]="RightClick",a[a.BrowserBack=5]="BrowserBack",a[a.BrowserForward=6]="BrowserForward",a[a.MouseWheelX=7]="MouseWheelX",a[a.MouseWheelY=8]="MouseWheelY",a[a.MouseWheelZ=9]="MouseWheelZ",a[a.DeltaHorizontal=10]="DeltaHorizontal",a[a.DeltaVertical=11]="DeltaVertical",a[a.FakeMove=12]="FakeMove"})(e||(e={})),(function(a){a[a.Cross=0]="Cross",a[a.Circle=1]="Circle",a[a.Square=2]="Square",a[a.Triangle=3]="Triangle",a[a.L1=4]="L1",a[a.R1=5]="R1",a[a.L2=6]="L2",a[a.R2=7]="R2",a[a.Share=8]="Share",a[a.Options=9]="Options",a[a.L3=10]="L3",a[a.R3=11]="R3",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.TouchPad=17]="TouchPad",a[a.LStickXAxis=18]="LStickXAxis",a[a.LStickYAxis=19]="LStickYAxis",a[a.RStickXAxis=20]="RStickXAxis",a[a.RStickYAxis=21]="RStickYAxis"})(f||(f={})),(function(a){a[a.A=0]="A",a[a.B=1]="B",a[a.X=2]="X",a[a.Y=3]="Y",a[a.LB=4]="LB",a[a.RB=5]="RB",a[a.LT=6]="LT",a[a.RT=7]="RT",a[a.Back=8]="Back",a[a.Start=9]="Start",a[a.LS=10]="LS",a[a.RS=11]="RS",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.LStickXAxis=17]="LStickXAxis",a[a.LStickYAxis=18]="LStickYAxis",a[a.RStickXAxis=19]="RStickXAxis",a[a.RStickYAxis=20]="RStickYAxis"})(g||(g={})),(function(a){a[a.B=0]="B",a[a.A=1]="A",a[a.Y=2]="Y",a[a.X=3]="X",a[a.L=4]="L",a[a.R=5]="R",a[a.ZL=6]="ZL",a[a.ZR=7]="ZR",a[a.Minus=8]="Minus",a[a.Plus=9]="Plus",a[a.LS=10]="LS",a[a.RS=11]="RS",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.Capture=17]="Capture",a[a.LStickXAxis=18]="LStickXAxis",a[a.LStickYAxis=19]="LStickYAxis",a[a.RStickXAxis=20]="RStickXAxis",a[a.RStickYAxis=21]="RStickYAxis"})(h||(h={}))},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f});var d=c(2),e=function(){function a(){}return a.NAME_EFFECTLAYER="EffectLayer",a.NAME_LAYER="Layer",a.NAME_LENSFLARESYSTEM="LensFlareSystem",a.NAME_BOUNDINGBOXRENDERER="BoundingBoxRenderer",a.NAME_PARTICLESYSTEM="ParticleSystem",a.NAME_GAMEPAD="Gamepad",a.NAME_SIMPLIFICATIONQUEUE="SimplificationQueue",a.NAME_GEOMETRYBUFFERRENDERER="GeometryBufferRenderer",a.NAME_PREPASSRENDERER="PrePassRenderer",a.NAME_DEPTHRENDERER="DepthRenderer",a.NAME_DEPTHPEELINGRENDERER="DepthPeelingRenderer",a.NAME_POSTPROCESSRENDERPIPELINEMANAGER="PostProcessRenderPipelineManager",a.NAME_SPRITE="Sprite",a.NAME_SUBSURFACE="SubSurface",a.NAME_OUTLINERENDERER="Outline",a.NAME_PROCEDURALTEXTURE="ProceduralTexture",a.NAME_SHADOWGENERATOR="ShadowGenerator",a.NAME_OCTREE="Octree",a.NAME_PHYSICSENGINE="PhysicsEngine",a.NAME_AUDIO="Audio",a.STEP_ISREADYFORMESH_EFFECTLAYER=0,a.STEP_BEFOREEVALUATEACTIVEMESH_BOUNDINGBOXRENDERER=0,a.STEP_EVALUATESUBMESH_BOUNDINGBOXRENDERER=0,a.STEP_PREACTIVEMESH_BOUNDINGBOXRENDERER=0,a.STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER=1,a.STEP_BEFORECAMERADRAW_PREPASS=0,a.STEP_BEFORECAMERADRAW_EFFECTLAYER=1,a.STEP_BEFORECAMERADRAW_LAYER=2,a.STEP_BEFORERENDERTARGETDRAW_PREPASS=0,a.STEP_BEFORERENDERTARGETDRAW_LAYER=1,a.STEP_BEFORERENDERINGMESH_PREPASS=0,a.STEP_BEFORERENDERINGMESH_OUTLINE=1,a.STEP_AFTERRENDERINGMESH_PREPASS=0,a.STEP_AFTERRENDERINGMESH_OUTLINE=1,a.STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW=0,a.STEP_AFTERRENDERINGGROUPDRAW_BOUNDINGBOXRENDERER=1,a.STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE=0,a.STEP_BEFORECAMERAUPDATE_GAMEPAD=1,a.STEP_BEFORECLEAR_PROCEDURALTEXTURE=0,a.STEP_AFTERRENDERTARGETDRAW_PREPASS=0,a.STEP_AFTERRENDERTARGETDRAW_LAYER=1,a.STEP_AFTERCAMERADRAW_PREPASS=0,a.STEP_AFTERCAMERADRAW_EFFECTLAYER=1,a.STEP_AFTERCAMERADRAW_LENSFLARESYSTEM=2,a.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW=3,a.STEP_AFTERCAMERADRAW_LAYER=4,a.STEP_AFTERRENDER_AUDIO=0,a.STEP_GATHERRENDERTARGETS_DEPTHRENDERER=0,a.STEP_GATHERRENDERTARGETS_GEOMETRYBUFFERRENDERER=1,a.STEP_GATHERRENDERTARGETS_SHADOWGENERATOR=2,a.STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER=3,a.STEP_GATHERACTIVECAMERARENDERTARGETS_DEPTHRENDERER=0,a.STEP_BEFORECLEARSTAGE_PREPASS=0,a.STEP_BEFORERENDERTARGETCLEARSTAGE_PREPASS=0,a.STEP_POINTERMOVE_SPRITE=0,a.STEP_POINTERDOWN_SPRITE=0,a.STEP_POINTERUP_SPRITE=0,a}(),f=function(a){function b(b){return a.apply(this,b)||this}return Object(d.d)(b,a),b.Create=function(){return Object.create(b.prototype)},b.prototype.registerStep=function(a,b,c){var d=0;for(Number.MAX_VALUE;d0){d=null!==(d=null===(d=this.indices)||void 0===d?void 0:d.length)&&void 0!==d?d:0;if(this.indices||(this.indices=new Array(e)),this.indices.length!==e)if(Array.isArray(this.indices))this.indices.length=e;else{c=c||this.indices instanceof Uint32Array?new Uint32Array(e):new Uint16Array(e);c.set(this.indices),this.indices=c}for(var e=this.positions?this.positions.length/3:0,c=0,f=b;cd.bbSize.y?d.bbSize.x:d.bbSize.y;ia>d.bbSize.z?ia:d.bbSize.z,G=d.subDiv.X*D/d.bbSize.x,H=d.subDiv.Y*D/d.bbSize.y,I=d.subDiv.Z*D/d.bbSize.z,J=d.subDiv.max*d.subDiv.max,d.facetPartitioning.length=0}for(f=0;fa.LongPressDelay&&!d._isPointerSwiping()&&(d._startingPointerTime=0,f.processTrigger(s.a.ACTION_OnLongPressTrigger,o.a.CreateNew(b.pickedMesh,c)))},a.LongPressDelay)}}else for(var g=0,h=e._pointerDownStage;ga.DragMovementThreshold||Math.abs(this._startingPointerPosition.y-this._pointerY)>a.DragMovementThreshold},a.prototype.simulatePointerUp=function(a,b,c){b=new PointerEvent("pointerup",b);var d=new B;c?d.doubleClick=!0:d.singleClick=!0,this._checkPrePointerObservable(a,b,w.a.POINTERUP)||this._processPointerUp(a,b,d)},a.prototype._processPointerUp=function(a,b,c){var d=this._scene;if(a&&a&&a.pickedMesh){if(this._pickedUpMesh=a.pickedMesh,this._pickedDownMesh===this._pickedUpMesh&&(d.onPointerPick&&d.onPointerPick(b,a),c.singleClick&&!c.ignore&&d.onPointerObservable.hasObservers())){var e=w.a.POINTERPICK,f=new w.b(e,b,a);this._setRayOnPointerInfo(f),d.onPointerObservable.notifyObservers(f,e)}e=a.pickedMesh._getActionManagerForTrigger();if(e&&!c.ignore){e.processTrigger(s.a.ACTION_OnPickUpTrigger,o.a.CreateNew(a.pickedMesh,b,a)),!c.hasSwiped&&c.singleClick&&e.processTrigger(s.a.ACTION_OnPickTrigger,o.a.CreateNew(a.pickedMesh,b,a));e=a.pickedMesh._getActionManagerForTrigger(s.a.ACTION_OnDoublePickTrigger);c.doubleClick&&e&&e.processTrigger(s.a.ACTION_OnDoublePickTrigger,o.a.CreateNew(a.pickedMesh,b,a))}}else if(!c.ignore)for(var e=0,g=d._pointerUpStage;ea.DoubleClickDelay&&!f._doubleClickOccured||b!==f._previousButtonPressed)&&(f._doubleClickOccured=!1,c.singleClick=!0,c.ignore=!1,d(c,f._currentPickResult))},this._initClickEvent=function(b,c,d,e){var g=new B;f._currentPickResult=null;var h=null,i=b.hasSpecificMask(w.a.POINTERPICK)||c.hasSpecificMask(w.a.POINTERPICK)||b.hasSpecificMask(w.a.POINTERTAP)||c.hasSpecificMask(w.a.POINTERTAP)||b.hasSpecificMask(w.a.POINTERDOUBLETAP)||c.hasSpecificMask(w.a.POINTERDOUBLETAP);!i&&x.a&&(h=f._initActionManager(h,g))&&(i=h.hasPickTriggers);var j=!1;if(i){i=d.button;if(g.hasSwiped=f._isPointerSwiping(),!g.hasSwiped){d=!a.ExclusiveDoubleClickMode;d||(d=!b.hasSpecificMask(w.a.POINTERDOUBLETAP)&&!c.hasSpecificMask(w.a.POINTERDOUBLETAP))&&!x.a.HasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)&&(h=f._initActionManager(h,g))&&(d=!h.hasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)),d?(Date.now()-f._previousStartingPointerTime>a.DoubleClickDelay||i!==f._previousButtonPressed)&&(g.singleClick=!0,e(g,f._currentPickResult),j=!0):(f._previousDelayedSimpleClickTimeout=f._delayedSimpleClickTimeout,f._delayedSimpleClickTimeout=window.setTimeout(f._delayedSimpleClick.bind(f,i,g,e),a.DoubleClickDelay));d=b.hasSpecificMask(w.a.POINTERDOUBLETAP)||c.hasSpecificMask(w.a.POINTERDOUBLETAP);!d&&x.a.HasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)&&(h=f._initActionManager(h,g))&&(d=h.hasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)),d&&(i===f._previousButtonPressed&&Date.now()-f._previousStartingPointerTime=z.c.LeftClick&&a.inputIndex<=z.c.RightClick&&1===a.currentState&&f._onPointerDown(e),b&&a.inputIndex>=z.c.LeftClick&&a.inputIndex<=z.c.RightClick&&0===a.currentState&&f._onPointerUp(e),d&&(a.inputIndex===z.c.Horizontal||a.inputIndex===z.c.Vertical||a.inputIndex===z.c.DeltaHorizontal||a.inputIndex===z.c.DeltaVertical||a.inputIndex===z.c.FakeMove?f._onPointerMove(e):a.inputIndex!==z.c.MouseWheelX&&a.inputIndex!==z.c.MouseWheelY&&a.inputIndex!==z.c.MouseWheelZ||f._onPointerMove(e)))}),this._alreadyAttached=!0}},a.prototype.detachControl=function(){this._alreadyAttachedTo&&this._alreadyAttached&&(this._deviceInputSystem.onInputChangedObservable.remove(this._onInputObserver),this._scene.doNotHandleCursors||(this._alreadyAttachedTo.style.cursor=this._scene.defaultCursor),this._alreadyAttached=!1)},a.prototype.setPointerOverMesh=function(a,b,c){if(void 0===b&&(b=0),this._meshUnderPointerId[b]!==a){var d,e=this._meshUnderPointerId[b];e&&(d=e._getActionManagerForTrigger(s.a.ACTION_OnPointerOutTrigger))&&d.processTrigger(s.a.ACTION_OnPointerOutTrigger,o.a.CreateNew(e,void 0,{pointerId:b})),a?(this._meshUnderPointerId[b]=a,this._pointerOverMesh=a,(d=a._getActionManagerForTrigger(s.a.ACTION_OnPointerOverTrigger))&&d.processTrigger(s.a.ACTION_OnPointerOverTrigger,o.a.CreateNew(a,void 0,{pointerId:b,pickResult:c}))):(delete this._meshUnderPointerId[b],this._pointerOverMesh=null)}},a.prototype.getPointerOverMesh=function(){return this._pointerOverMesh},a.prototype._invalidateMesh=function(a){for(var b in this._pointerOverMesh===a&&(this._pointerOverMesh=null),this._pickedDownMesh===a&&(this._pickedDownMesh=null),this._pickedUpMesh===a&&(this._pickedUpMesh=null),this._meshUnderPointerId)this._meshUnderPointerId[b]===a&&delete this._meshUnderPointerId[b]},a.DragMovementThreshold=10,a.LongPressDelay=500,a.DoubleClickDelay=300,a.ExclusiveDoubleClickMode=!1,a}(),D=c(60),E=c(8),F=c(96),G=c(137),H=c(54),I=c(65),J=c(146);b=c(172);var K=function(a){function b(c,e){var f=a.call(this)||this;f._inputManager=new C(f),f.cameraToUseForPointers=null,f._isScene=!0,f._blockEntityCollection=!1,f.autoClear=!0,f.autoClearDepthAndStencil=!0,f.clearColor=new E.b(.2,.2,.3,1),f.ambientColor=new E.a(0,0,0),f._environmentIntensity=1,f._forceWireframe=!1,f._skipFrustumClipping=!1,f._forcePointsCloud=!1,f.animationsEnabled=!0,f._animationPropertiesOverride=null,f.useConstantAnimationDeltaTime=!1,f.constantlyUpdateMeshUnderPointer=!1,f.hoverCursor="pointer",f.defaultCursor="",f.doNotHandleCursors=!1,f.preventDefaultOnPointerDown=!0,f.preventDefaultOnPointerUp=!0,f.metadata=null,f.reservedDataStore=null,f.disableOfflineSupportExceptionRules=new Array,f.onDisposeObservable=new g.c,f._onDisposeObserver=null,f.onBeforeRenderObservable=new g.c,f._onBeforeRenderObserver=null,f.onAfterRenderObservable=new g.c,f.onAfterRenderCameraObservable=new g.c,f._onAfterRenderObserver=null,f.onBeforeAnimationsObservable=new g.c,f.onAfterAnimationsObservable=new g.c,f.onBeforeDrawPhaseObservable=new g.c,f.onAfterDrawPhaseObservable=new g.c,f.onReadyObservable=new g.c,f.onBeforeCameraRenderObservable=new g.c,f._onBeforeCameraRenderObserver=null,f.onAfterCameraRenderObservable=new g.c,f._onAfterCameraRenderObserver=null,f.onBeforeActiveMeshesEvaluationObservable=new g.c,f.onAfterActiveMeshesEvaluationObservable=new g.c,f.onBeforeParticlesRenderingObservable=new g.c,f.onAfterParticlesRenderingObservable=new g.c,f.onDataLoadedObservable=new g.c,f.onNewCameraAddedObservable=new g.c,f.onCameraRemovedObservable=new g.c,f.onNewLightAddedObservable=new g.c,f.onLightRemovedObservable=new g.c,f.onNewGeometryAddedObservable=new g.c,f.onGeometryRemovedObservable=new g.c,f.onNewTransformNodeAddedObservable=new g.c,f.onTransformNodeRemovedObservable=new g.c,f.onNewMeshAddedObservable=new g.c,f.onMeshRemovedObservable=new g.c,f.onNewSkeletonAddedObservable=new g.c,f.onSkeletonRemovedObservable=new g.c,f.onNewMaterialAddedObservable=new g.c,f.onNewMultiMaterialAddedObservable=new g.c,f.onMaterialRemovedObservable=new g.c,f.onMultiMaterialRemovedObservable=new g.c,f.onNewTextureAddedObservable=new g.c,f.onTextureRemovedObservable=new g.c,f.onBeforeRenderTargetsRenderObservable=new g.c,f.onAfterRenderTargetsRenderObservable=new g.c,f.onBeforeStepObservable=new g.c,f.onAfterStepObservable=new g.c,f.onActiveCameraChanged=new g.c,f.onBeforeRenderingGroupObservable=new g.c,f.onAfterRenderingGroupObservable=new g.c,f.onMeshImportedObservable=new g.c,f.onAnimationFileImportedObservable=new g.c,f._registeredForLateAnimationBindings=new h.b(256),f.onPrePointerObservable=new g.c,f.onPointerObservable=new g.c,f.onPreKeyboardObservable=new g.c,f.onKeyboardObservable=new g.c,f._useRightHandedSystem=!1,f._timeAccumulator=0,f._currentStepId=0,f._currentInternalStep=0,f._fogEnabled=!0,f._fogMode=b.FOGMODE_NONE,f.fogColor=new E.a(.2,.2,.3),f.fogDensity=.1,f.fogStart=0,f.fogEnd=1e3,f.needsPreviousWorldMatrices=!1,f._shadowsEnabled=!0,f._lightsEnabled=!0,f.activeCameras=new Array,f._texturesEnabled=!0,f.physicsEnabled=!0,f.particlesEnabled=!0,f.spritesEnabled=!0,f._skeletonsEnabled=!0,f.lensFlaresEnabled=!0,f.collisionsEnabled=!0,f.gravity=new k.e(0,-9.807,0),f.postProcessesEnabled=!0,f.renderTargetsEnabled=!0,f.dumpNextRenderTargets=!1,f.customRenderTargets=new Array,f.importedMeshesFiles=new Array,f.probesEnabled=!0,f._meshesForIntersections=new h.b(256),f.proceduralTexturesEnabled=!0,f._totalVertices=new D.a,f._activeIndices=new D.a,f._activeParticles=new D.a,f._activeBones=new D.a,f._animationTime=0,f.animationTimeScale=1,f._renderId=0,f._frameId=0,f._executeWhenReadyTimeoutId=-1,f._intermediateRendering=!1,f._defaultFrameBufferCleared=!1,f._viewUpdateFlag=-1,f._projectionUpdateFlag=-1,f._toBeDisposed=new Array(256),f._activeRequests=new Array,f._pendingData=new Array,f._isDisposed=!1,f.dispatchAllSubMeshesOfActiveMeshes=!1,f._activeMeshes=new h.a(256),f._processedMaterials=new h.a(256),f._renderTargets=new h.b(256),f._activeParticleSystems=new h.a(256),f._activeSkeletons=new h.b(32),f._softwareSkinnedMeshes=new h.b(32),f._activeAnimatables=new Array,f._transformMatrix=k.a.Zero(),f.requireLightSorting=!1,f._components=[],f._serializableComponents=[],f._transientComponents=[],f._beforeCameraUpdateStage=r.b.Create(),f._beforeClearStage=r.b.Create(),f._beforeRenderTargetClearStage=r.b.Create(),f._gatherRenderTargetsStage=r.b.Create(),f._gatherActiveCameraRenderTargetsStage=r.b.Create(),f._isReadyForMeshStage=r.b.Create(),f._beforeEvaluateActiveMeshStage=r.b.Create(),f._evaluateSubMeshStage=r.b.Create(),f._preActiveMeshStage=r.b.Create(),f._cameraDrawRenderTargetStage=r.b.Create(),f._beforeCameraDrawStage=r.b.Create(),f._beforeRenderTargetDrawStage=r.b.Create(),f._beforeRenderingGroupDrawStage=r.b.Create(),f._beforeRenderingMeshStage=r.b.Create(),f._afterRenderingMeshStage=r.b.Create(),f._afterRenderingGroupDrawStage=r.b.Create(),f._afterCameraDrawStage=r.b.Create(),f._afterRenderTargetDrawStage=r.b.Create(),f._afterRenderStage=r.b.Create(),f._pointerMoveStage=r.b.Create(),f._pointerDownStage=r.b.Create(),f._pointerUpStage=r.b.Create(),f.geometriesByUniqueId=null,f._defaultMeshCandidates={data:[],length:0},f._defaultSubMeshCandidates={data:[],length:0},f._preventFreeActiveMeshesAndRenderingGroups=!1,f._activeMeshesFrozen=!1,f._skipEvaluateActiveMeshesCompletely=!1,f._allowPostProcessClearColor=!0,f.getDeterministicFrameTime=function(){return f._engine.getTimeStep()},f._blockMaterialDirtyMechanism=!1,f._perfCollector=null,f.onComputePressureChanged=new g.c;var i=Object(d.a)({useGeometryUniqueIdsMap:!0,useMaterialMeshMap:!0,useClonedMeshMap:!0,virtual:!1},e);return f._engine=c||u.a.LastCreatedEngine,i.virtual?f._engine._virtualScenes.push(f):(u.a._LastCreatedScene=f,f._engine.scenes.push(f)),f._uid=null,f._renderingManager=new q.b(f),p.a&&(f.postProcessManager=new p.a(f)),Object(t.e)()&&f.attachControl(),f._createUbo(),l.a&&(f._imageProcessingConfiguration=new l.a),f.setDefaultCandidateProviders(),i.useGeometryUniqueIdsMap&&(f.geometriesByUniqueId={}),f.useMaterialMeshMap=i.useMaterialMeshMap,f.useClonedMeshMap=i.useClonedMeshMap,e&&e.virtual||f._engine.onNewSceneAddedObservable.notifyObservers(f),J.a.IsAvailable&&(f._computePressureObserver=new J.a(function(a){f.onComputePressureChanged.notifyObservers(a)},{cpuUtilizationThresholds:[.25,.5,.75,.9],cpuSpeedThresholds:[.5]}),f._computePressureObserver.observe()),f}return Object(d.d)(b,a),b.DefaultMaterialFactory=function(a){throw Object(v.a)("StandardMaterial")},b.CollisionCoordinatorFactory=function(){throw Object(v.a)("DefaultCollisionCoordinator")},Object.defineProperty(b.prototype,"environmentTexture",{get:function(){return this._environmentTexture},set:function(a){this._environmentTexture!==a&&(this._environmentTexture=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"environmentIntensity",{get:function(){return this._environmentIntensity},set:function(a){this._environmentIntensity!==a&&(this._environmentIntensity=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forceWireframe",{get:function(){return this._forceWireframe},set:function(a){this._forceWireframe!==a&&(this._forceWireframe=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skipFrustumClipping",{get:function(){return this._skipFrustumClipping},set:function(a){this._skipFrustumClipping!==a&&(this._skipFrustumClipping=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcePointsCloud",{get:function(){return this._forcePointsCloud},set:function(a){this._forcePointsCloud!==a&&(this._forcePointsCloud=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"animationPropertiesOverride",{get:function(){return this._animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"onDispose",{set:function(a){this._onDisposeObserver&&this.onDisposeObservable.remove(this._onDisposeObserver),this._onDisposeObserver=this.onDisposeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"beforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),a&&(this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"afterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),a&&(this._onAfterRenderObserver=this.onAfterRenderObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"beforeCameraRender",{set:function(a){this._onBeforeCameraRenderObserver&&this.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=this.onBeforeCameraRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"afterCameraRender",{set:function(a){this._onAfterCameraRenderObserver&&this.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=this.onAfterCameraRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"unTranslatedPointer",{get:function(){return this._inputManager.unTranslatedPointer},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DragMovementThreshold",{get:function(){return C.DragMovementThreshold},set:function(a){C.DragMovementThreshold=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LongPressDelay",{get:function(){return C.LongPressDelay},set:function(a){C.LongPressDelay=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DoubleClickDelay",{get:function(){return C.DoubleClickDelay},set:function(a){C.DoubleClickDelay=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ExclusiveDoubleClickMode",{get:function(){return C.ExclusiveDoubleClickMode},set:function(a){C.ExclusiveDoubleClickMode=a},enumerable:!1,configurable:!0}),b.prototype.bindEyePosition=function(a,b,c){var d;void 0===b&&(b="vEyePosition"),void 0===c&&(c=!1);d=this._forcedViewPosition?this._forcedViewPosition:this._mirroredCameraPosition?this._mirroredCameraPosition:null!==(d=this.activeCamera.globalPosition)&&void 0!==d?d:this.activeCamera.devicePosition;var e=this.useRightHandedSystem===(null!=this._mirroredCameraPosition);return k.c.Vector4[0].set(d.x,d.y,d.z,e?-1:1),a&&(c?a.setFloat3(b,k.c.Vector4[0].x,k.c.Vector4[0].y,k.c.Vector4[0].z):a.setVector4(b,k.c.Vector4[0])),k.c.Vector4[0]},b.prototype.finalizeSceneUbo=function(){var a=this.getSceneUniformBuffer(),b=this.bindEyePosition(null);return a.updateFloat4("vEyePosition",b.x,b.y,b.z,b.w),a.update(),a},Object.defineProperty(b.prototype,"useRightHandedSystem",{get:function(){return this._useRightHandedSystem},set:function(a){this._useRightHandedSystem!==a&&(this._useRightHandedSystem=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),b.prototype.setStepId=function(a){this._currentStepId=a},b.prototype.getStepId=function(){return this._currentStepId},b.prototype.getInternalStep=function(){return this._currentInternalStep},Object.defineProperty(b.prototype,"fogEnabled",{get:function(){return this._fogEnabled},set:function(a){this._fogEnabled!==a&&(this._fogEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fogMode",{get:function(){return this._fogMode},set:function(a){this._fogMode!==a&&(this._fogMode=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"prePass",{get:function(){return!!this.prePassRenderer&&this.prePassRenderer.defaultRT.enabled},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowsEnabled",{get:function(){return this._shadowsEnabled},set:function(a){this._shadowsEnabled!==a&&(this._shadowsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_LightDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lightsEnabled",{get:function(){return this._lightsEnabled},set:function(a){this._lightsEnabled!==a&&(this._lightsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_LightDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"activeCamera",{get:function(){return this._activeCamera},set:function(a){a!==this._activeCamera&&(this._activeCamera=a,this.onActiveCameraChanged.notifyObservers(this))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"defaultMaterial",{get:function(){return this._defaultMaterial||(this._defaultMaterial=b.DefaultMaterialFactory(this)),this._defaultMaterial},set:function(a){this._defaultMaterial=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"texturesEnabled",{get:function(){return this._texturesEnabled},set:function(a){this._texturesEnabled!==a&&(this._texturesEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skeletonsEnabled",{get:function(){return this._skeletonsEnabled},set:function(a){this._skeletonsEnabled!==a&&(this._skeletonsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_AttributesDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionCoordinator",{get:function(){return this._collisionCoordinator||(this._collisionCoordinator=b.CollisionCoordinatorFactory(),this._collisionCoordinator.init(this)),this._collisionCoordinator},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"frustumPlanes",{get:function(){return this._frustumPlanes},enumerable:!1,configurable:!0}),b.prototype._registerTransientComponents=function(){if(this._transientComponents.length>0){for(var a=0,b=this._transientComponents;a0)return!1;for(a=0;a0,e=0,f=this._isReadyForMeshStage;e0)for(c=0,d=this.activeCameras;c0},enumerable:!1,configurable:!0}),b.prototype.executeWhenReady=function(a){var b=this;this.onReadyObservable.add(a),-1===this._executeWhenReadyTimeoutId&&(this._executeWhenReadyTimeoutId=setTimeout(function(){b._checkIsReady()},150))},b.prototype.whenReadyAsync=function(){var a=this;return new Promise(function(b){a.executeWhenReady(function(){b()})})},b.prototype._checkIsReady=function(){var a=this;return this._registerTransientComponents(),this.isReady()?(this.onReadyObservable.notifyObservers(this),this.onReadyObservable.clear(),void (this._executeWhenReadyTimeoutId=-1)):this._isDisposed?(this.onReadyObservable.clear(),void (this._executeWhenReadyTimeoutId=-1)):void (this._executeWhenReadyTimeoutId=setTimeout(function(){a._checkIsReady()},150))},Object.defineProperty(b.prototype,"animatables",{get:function(){return this._activeAnimatables},enumerable:!1,configurable:!0}),b.prototype.resetLastAnimationTimeFrame=function(){this._animationTimeLast=f.a.Now},b.prototype.getViewMatrix=function(){return this._viewMatrix},b.prototype.getProjectionMatrix=function(){return this._projectionMatrix},b.prototype.getTransformMatrix=function(){return this._transformMatrix},b.prototype.setTransformMatrix=function(a,b,c,d){this._viewUpdateFlag===a.updateFlag&&this._projectionUpdateFlag===b.updateFlag||(this._viewUpdateFlag=a.updateFlag,this._projectionUpdateFlag=b.updateFlag,this._viewMatrix=a,this._projectionMatrix=b,this._viewMatrix.multiplyToRef(this._projectionMatrix,this._transformMatrix),this._frustumPlanes?F.a.GetPlanesToRef(this._transformMatrix,this._frustumPlanes):this._frustumPlanes=F.a.GetPlanes(this._transformMatrix),this._multiviewSceneUbo&&this._multiviewSceneUbo.useUbo?this._updateMultiviewUbo(c,d):this._sceneUbo.useUbo&&(this._sceneUbo.updateMatrix("viewProjection",this._transformMatrix),this._sceneUbo.updateMatrix("view",this._viewMatrix),this._sceneUbo.updateMatrix("projection",this._projectionMatrix)))},b.prototype.getSceneUniformBuffer=function(){return this._multiviewSceneUbo?this._multiviewSceneUbo:this._sceneUbo},b.prototype.getUniqueId=function(){return G.a.UniqueId},b.prototype.addMesh=function(a,b){var c=this;void 0===b&&(b=!1),this._blockEntityCollection||(this.meshes.push(a),a._resyncLightSources(),a.parent||a._addToSceneRootNodes(),this.onNewMeshAddedObservable.notifyObservers(a),b&&a.getChildMeshes().forEach(function(a){c.addMesh(a)}))},b.prototype.removeMesh=function(a,b){var c=this;void 0===b&&(b=!1);var d=this.meshes.indexOf(a);return-1!==d&&(this.meshes[d]=this.meshes[this.meshes.length-1],this.meshes.pop(),a.parent||a._removeFromSceneRootNodes()),this._inputManager._invalidateMesh(a),this.onMeshRemovedObservable.notifyObservers(a),b&&a.getChildMeshes().forEach(function(a){c.removeMesh(a)}),d},b.prototype.addTransformNode=function(a){this._blockEntityCollection||(a._indexInSceneTransformNodesArray=this.transformNodes.length,this.transformNodes.push(a),a.parent||a._addToSceneRootNodes(),this.onNewTransformNodeAddedObservable.notifyObservers(a))},b.prototype.removeTransformNode=function(a){var b=a._indexInSceneTransformNodesArray;if(-1!==b){if(b!==this.transformNodes.length-1){var c=this.transformNodes[this.transformNodes.length-1];this.transformNodes[b]=c,c._indexInSceneTransformNodesArray=b}a._indexInSceneTransformNodesArray=-1,this.transformNodes.pop(),a.parent||a._removeFromSceneRootNodes()}return this.onTransformNodeRemovedObservable.notifyObservers(a),b},b.prototype.removeSkeleton=function(a){var b=this.skeletons.indexOf(a);return-1!==b&&(this.skeletons.splice(b,1),this.onSkeletonRemovedObservable.notifyObservers(a)),b},b.prototype.removeMorphTargetManager=function(a){a=this.morphTargetManagers.indexOf(a);return-1!==a&&this.morphTargetManagers.splice(a,1),a},b.prototype.removeLight=function(a){var b=this.lights.indexOf(a);if(-1!==b){for(var c=0,d=this.meshes;c0?this.activeCamera=this.cameras[0]:this.activeCamera=null),this.onCameraRemovedObservable.notifyObservers(a),b},b.prototype.removeParticleSystem=function(a){a=this.particleSystems.indexOf(a);return-1!==a&&this.particleSystems.splice(a,1),a},b.prototype.removeAnimation=function(a){a=this.animations.indexOf(a);return-1!==a&&this.animations.splice(a,1),a},b.prototype.stopAnimation=function(a,b,c){},b.prototype.removeAnimationGroup=function(a){a=this.animationGroups.indexOf(a);return-1!==a&&this.animationGroups.splice(a,1),a},b.prototype.removeMultiMaterial=function(a){var b=this.multiMaterials.indexOf(a);return-1!==b&&this.multiMaterials.splice(b,1),this.onMultiMaterialRemovedObservable.notifyObservers(a),b},b.prototype.removeMaterial=function(a){var b=a._indexInSceneMaterialArray;if(-1!==b&&b=0;b--)if(this.materials[b].id===a)return this.materials[b];return null},b.prototype.getMaterialByName=function(a){for(var b=0;b=0;b--)if(this.meshes[b].id===a)return this.meshes[b];return null},b.prototype.getLastEntryById=function(a){var b;for(b=this.meshes.length-1;b>=0;b--)if(this.meshes[b].id===a)return this.meshes[b];for(b=this.transformNodes.length-1;b>=0;b--)if(this.transformNodes[b].id===a)return this.transformNodes[b];for(b=this.cameras.length-1;b>=0;b--)if(this.cameras[b].id===a)return this.cameras[b];for(b=this.lights.length-1;b>=0;b--)if(this.lights[b].id===a)return this.lights[b];return null},b.prototype.getNodeById=function(a){var b=this.getMeshById(a);if(b)return b;b=this.getTransformNodeById(a);if(b)return b;b=this.getLightById(a);if(b)return b;b=this.getCameraById(a);if(b)return b;b=this.getBoneById(a);return b||null},b.prototype.getNodeByName=function(a){var b=this.getMeshByName(a);if(b)return b;b=this.getTransformNodeByName(a);if(b)return b;b=this.getLightByName(a);if(b)return b;b=this.getCameraByName(a);if(b)return b;b=this.getBoneByName(a);return b||null},b.prototype.getMeshByName=function(a){for(var b=0;b=0;b--)if(this.skeletons[b].id===a)return this.skeletons[b];return null},b.prototype.getSkeletonByUniqueId=function(a){for(var b=0;b0&&(null===(a=this.activeCamera)||void 0===a||a._activeMeshes.reset(),this._activeMeshes.reset(),this._renderingManager.reset(),this._processedMaterials.reset(),this._activeParticleSystems.reset(),this._activeSkeletons.reset(),this._softwareSkinnedMeshes.reset());else if(this._activeMeshesFrozen&&this._activeMeshes.length){if(!this._skipEvaluateActiveMeshesCompletely)for(var a=this._activeMeshes.length,b=0;b0&&0!=(d.layerMask&this.activeCamera.layerMask)&&(this._skipFrustumClipping||d.alwaysSelectAsActiveMesh||d.isInFrustum(this._frustumPlanes)))){this._activeMeshes.push(d),this.activeCamera._activeMeshes.push(d),e!==d&&e._activate(this._renderId,!1);for(var f=0,g=this._preActiveMeshStage;f0)for(var c=this.getActiveSubMeshCandidates(b),d=c.length,e=0;e1?this.setTransformMatrix(a._rigCameras[0].getViewMatrix(),a._rigCameras[0].getProjectionMatrix(),a._rigCameras[1].getViewMatrix(),a._rigCameras[1].getProjectionMatrix()):this.updateTransformMatrix(),this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera),this._evaluateActiveMeshes();for(c=0;c0&&this._renderTargets.concatWithNoDuplicate(a.customRenderTargets),b&&b.customRenderTargets&&b.customRenderTargets.length>0&&this._renderTargets.concatWithNoDuplicate(b.customRenderTargets),this.environmentTexture&&this.environmentTexture.isRenderTarget&&this._renderTargets.pushNoDuplicate(this.environmentTexture);for(f=0,c=this._gatherActiveCameraRenderTargetsStage;f0){e.b.StartPerformanceCounter("Render targets",this._renderTargets.length>0);for(c=0;c0),this._renderId++}for(f=0,g=this._cameraDrawRenderTargetStage;f1&&this.getEngine().getCaps().multiview)return this._renderForCamera(a,void 0,b),void this.onAfterRenderCameraObservable.notifyObservers(a);if(a._useMultiviewToSingleView)this._renderMultiviewToSingleView(a);else{this.onBeforeCameraRenderObservable.notifyObservers(a);for(b=0;b-1&&(d.trigger===s.a.ACTION_OnIntersectionExitTrigger&&d._executeCurrent(o.a.CreateNew(b,void 0,f)),b.actionManager.hasSpecificTrigger(s.a.ACTION_OnIntersectionExitTrigger,function(a){a=a.mesh?a.mesh:a;return f===a})&&d.trigger!==s.a.ACTION_OnIntersectionExitTrigger||b._intersectionsInProgress.splice(g,1))}}}},b.prototype._advancePhysicsEngineStep=function(a){},b.prototype._animate=function(){},b.prototype.animate=function(){if(this._engine.isDeterministicLockStep()){var a=Math.max(b.MinDeltaTime,Math.min(this._engine.getDeltaTime(),b.MaxDeltaTime))+this._timeAccumulator,c=this._engine.getTimeStep(),d=1e3/c/1e3,e=0,f=this._engine.getLockstepMaxSteps(),g=Math.floor(a/c);for(g=Math.min(g,f);a>0&&e0)for(b=0;b0),this._intermediateRendering=!0;for(var f=0;f0),this._intermediateRendering=!1,this._renderId++}this.activeCamera=d,this._activeCamera&&this._activeCamera.cameraRigMode!==s.a.RIG_MODE_CUSTOM&&!this.prePass&&this._bindFrameBuffer(this._activeCamera,!1),this.onAfterRenderTargetsRenderObservable.notifyObservers(this);for(g=0,f=this._beforeClearStage;g0)for(b=0;b0);else{if(!this.activeCamera)throw new Error("No camera defined");this._processSubCameras(this.activeCamera,!1)}this._checkIntersections();for(f=0,g=this._afterRenderStage;f-1&&this._engine.scenes.splice(a,1),u.a._LastCreatedScene===this&&(this._engine.scenes.length>0?u.a._LastCreatedScene=this._engine.scenes[this._engine.scenes.length-1]:u.a._LastCreatedScene=null),(a=this._engine._virtualScenes.indexOf(this))>-1&&this._engine._virtualScenes.splice(a,1),this._engine.wipeCaches(!0),this._isDisposed=!0}},Object.defineProperty(b.prototype,"isDisposed",{get:function(){return this._isDisposed},enumerable:!1,configurable:!0}),b.prototype.clearCachedVertexData=function(){for(var a=0;a0&&parseInt(v[v.length-1])>=A)continue}for(v=0,A=w;v1?new r.a:new q,this._badOS=/iPad/i.test(navigator.userAgent)||/iPhone/i.test(navigator.userAgent),this._creationOptions=d,!1}}return Object.defineProperty(a,"NpmPackage",{get:function(){return"babylonjs@5.0.0-alpha.56"},enumerable:!1,configurable:!0}),Object.defineProperty(a,"Version",{get:function(){return"5.0.0-alpha.56"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"description",{get:function(){var a=this.name+this.webGLVersion;return this._caps.parallelShaderCompile&&(a+=" - Parallel shader compilation"),a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"WebGL"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"version",{get:function(){return this._webGLVersion},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ShadersRepository",{get:function(){return f.a.ShadersRepository},set:function(a){f.a.ShadersRepository=a},enumerable:!1,configurable:!0}),a.prototype._getShaderProcessor=function(a){return this._shaderProcessor},Object.defineProperty(a.prototype,"useReverseDepthBuffer",{get:function(){return this._useReverseDepthBuffer},set:function(a){a!==this._useReverseDepthBuffer&&(this._useReverseDepthBuffer=a,this._depthCullingState.depthFunc=a?l.a.GEQUAL:l.a.LEQUAL)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frameId",{get:function(){return this._frameId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsUniformBuffers",{get:function(){return this.webGLVersion>1&&!this.disableUniformBuffers},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_shouldUseHighPrecisionShader",{get:function(){return!(!this._caps.highPrecisionShaderSupported||!this._highPrecisionShadersAllowed)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"needPOTTextures",{get:function(){return this._webGLVersion<2||this.forcePOTTextures},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"activeRenderLoops",{get:function(){return this._activeRenderLoops},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"doNotHandleContextLost",{get:function(){return this._doNotHandleContextLost},set:function(a){this._doNotHandleContextLost=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_supportsHardwareTextureRescaling",{get:function(){return!1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"framebufferDimensionsObject",{set:function(a){this._framebufferDimensionsObject=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentViewport",{get:function(){return this._cachedViewport},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture",{get:function(){return this._emptyTexture||(this._emptyTexture=this.createRawTexture(new Uint8Array(4),1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture3D",{get:function(){return this._emptyTexture3D||(this._emptyTexture3D=this.createRawTexture3D(new Uint8Array(4),1,1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture3D},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture2DArray",{get:function(){return this._emptyTexture2DArray||(this._emptyTexture2DArray=this.createRawTexture2DArray(new Uint8Array(4),1,1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture2DArray},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyCubeTexture",{get:function(){if(!this._emptyCubeTexture){var a=new Uint8Array(4);a=[a,a,a,a,a,a];this._emptyCubeTexture=this.createRawCubeTexture(a,1,l.a.TEXTUREFORMAT_RGBA,l.a.TEXTURETYPE_UNSIGNED_INT,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)}return this._emptyCubeTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isWebGPU",{get:function(){return this._isWebGPU},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderPlatformName",{get:function(){return this._shaderPlatformName},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"snapshotRendering",{get:function(){return this._snapshotRenderingEnabled},set:function(a){this._snapshotRenderingEnabled=!1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"snapshotRenderingMode",{get:function(){return this._snapshotRenderingMode},set:function(a){this._snapshotRenderingMode=a},enumerable:!1,configurable:!0}),a.prototype.snapshotRenderingReset=function(){this.snapshotRendering=!1},a._createCanvas=function(a,b){if("undefined"==typeof document)return new OffscreenCanvas(a,b);var c=document.createElement("canvas");return c.width=a,c.height=b,c},a.prototype.createCanvas=function(b,c){return a._createCanvas(b,c)},a.prototype.createCanvasImage=function(){return document.createElement("img")},a.prototype._restoreEngineAfterContextLost=function(a){var b=this;setTimeout(function(){return Object(d.b)(b,void 0,void 0,function(){var b,c,e,f,g;return Object(d.e)(this,function(d){switch(d.label){case 0:return this._dummyFramebuffer=null,b=this._depthCullingState.depthTest,c=this._depthCullingState.depthFunc,e=this._depthCullingState.depthMask,f=this._stencilState.stencilTest,[4,a()];case 1:return d.sent(),this._rebuildEffects(),null===(g=this._rebuildComputeEffects)||void 0===g||g.call(this),this._rebuildInternalTextures(),this._rebuildRenderTargetWrappers(),this._rebuildBuffers(),this.wipeCaches(!0),this._depthCullingState.depthTest=b,this._depthCullingState.depthFunc=c,this._depthCullingState.depthMask=e,this._stencilState.stencilTest=f,n.a.Warn(this.name+" context successfully restored."),this.onContextRestoredObservable.notifyObservers(this),this._contextWasLost=!1,[2]}})})},0)},a.prototype._sharedInit=function(a,b,c){this._renderingCanvas=a},a.prototype._getShaderProcessingContext=function(a){return null},a.prototype._rebuildInternalTextures=function(){for(var a=0,b=this._internalTexturesCache.slice();a1?this._gl.getParameter(this._gl.MAX_SAMPLES):1,maxCubemapTextureSize:this._gl.getParameter(this._gl.MAX_CUBE_MAP_TEXTURE_SIZE),maxRenderTextureSize:this._gl.getParameter(this._gl.MAX_RENDERBUFFER_SIZE),maxVertexAttribs:this._gl.getParameter(this._gl.MAX_VERTEX_ATTRIBS),maxVaryingVectors:this._gl.getParameter(this._gl.MAX_VARYING_VECTORS),maxFragmentUniformVectors:this._gl.getParameter(this._gl.MAX_FRAGMENT_UNIFORM_VECTORS),maxVertexUniformVectors:this._gl.getParameter(this._gl.MAX_VERTEX_UNIFORM_VECTORS),parallelShaderCompile:this._gl.getExtension("KHR_parallel_shader_compile")||void 0,standardDerivatives:this._webGLVersion>1||null!==this._gl.getExtension("OES_standard_derivatives"),maxAnisotropy:1,astc:this._gl.getExtension("WEBGL_compressed_texture_astc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_astc"),bptc:this._gl.getExtension("EXT_texture_compression_bptc")||this._gl.getExtension("WEBKIT_EXT_texture_compression_bptc"),s3tc:this._gl.getExtension("WEBGL_compressed_texture_s3tc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"),s3tc_srgb:this._gl.getExtension("WEBGL_compressed_texture_s3tc_srgb")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc_srgb"),pvrtc:this._gl.getExtension("WEBGL_compressed_texture_pvrtc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"),etc1:this._gl.getExtension("WEBGL_compressed_texture_etc1")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_etc1"),etc2:this._gl.getExtension("WEBGL_compressed_texture_etc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_etc")||this._gl.getExtension("WEBGL_compressed_texture_es3_0"),textureAnisotropicFilterExtension:this._gl.getExtension("EXT_texture_filter_anisotropic")||this._gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic")||this._gl.getExtension("MOZ_EXT_texture_filter_anisotropic"),uintIndices:this._webGLVersion>1||null!==this._gl.getExtension("OES_element_index_uint"),fragmentDepthSupported:this._webGLVersion>1||null!==this._gl.getExtension("EXT_frag_depth"),highPrecisionShaderSupported:!1,timerQuery:this._gl.getExtension("EXT_disjoint_timer_query_webgl2")||this._gl.getExtension("EXT_disjoint_timer_query"),supportOcclusionQuery:this._webGLVersion>1,canUseTimestampForTimerQuery:!1,drawBuffersExtension:!1,maxMSAASamples:1,colorBufferFloat:!!(this._webGLVersion>1&&this._gl.getExtension("EXT_color_buffer_float")),textureFloat:!!(this._webGLVersion>1||this._gl.getExtension("OES_texture_float")),textureHalfFloat:!!(this._webGLVersion>1||this._gl.getExtension("OES_texture_half_float")),textureHalfFloatRender:!1,textureFloatLinearFiltering:!1,textureFloatRender:!1,textureHalfFloatLinearFiltering:!1,vertexArrayObject:!1,instancedArrays:!1,textureLOD:!!(this._webGLVersion>1||this._gl.getExtension("EXT_shader_texture_lod")),blendMinMax:!1,multiview:this._gl.getExtension("OVR_multiview2"),oculusMultiview:this._gl.getExtension("OCULUS_multiview"),depthTextureExtension:!1,canUseGLInstanceID:this._webGLVersion>1,canUseGLVertexID:this._webGLVersion>1,supportComputeShaders:!1,supportSRGBBuffers:!1},this._glVersion=this._gl.getParameter(this._gl.VERSION);var a=this._gl.getExtension("WEBGL_debug_renderer_info");if(null!=a&&(this._glRenderer=this._gl.getParameter(a.UNMASKED_RENDERER_WEBGL),this._glVendor=this._gl.getParameter(a.UNMASKED_VENDOR_WEBGL)),this._glVendor||(this._glVendor="Unknown vendor"),this._glRenderer||(this._glRenderer="Unknown renderer"),36193!==this._gl.HALF_FLOAT_OES&&(this._gl.HALF_FLOAT_OES=36193),34842!==this._gl.RGBA16F&&(this._gl.RGBA16F=34842),34836!==this._gl.RGBA32F&&(this._gl.RGBA32F=34836),35056!==this._gl.DEPTH24_STENCIL8&&(this._gl.DEPTH24_STENCIL8=35056),this._caps.timerQuery&&(1===this._webGLVersion&&(this._gl.getQuery=this._caps.timerQuery.getQueryEXT.bind(this._caps.timerQuery)),this._caps.canUseTimestampForTimerQuery=this._gl.getQuery(this._caps.timerQuery.TIMESTAMP_EXT,this._caps.timerQuery.QUERY_COUNTER_BITS_EXT)>0),this._caps.maxAnisotropy=this._caps.textureAnisotropicFilterExtension?this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0,this._caps.textureFloatLinearFiltering=!(!this._caps.textureFloat||!this._gl.getExtension("OES_texture_float_linear")),this._caps.textureFloatRender=!(!this._caps.textureFloat||!this._canRenderToFloatFramebuffer()),this._caps.textureHalfFloatLinearFiltering=!!(this._webGLVersion>1||this._caps.textureHalfFloat&&this._gl.getExtension("OES_texture_half_float_linear")),this._caps.astc&&(this._gl.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR=this._caps.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR),this._caps.bptc&&(this._gl.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT=this._caps.bptc.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT),this._caps.s3tc_srgb&&(this._gl.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT=this._caps.s3tc_srgb.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,this._gl.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT=this._caps.s3tc_srgb.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT),this._webGLVersion>1&&5131!==this._gl.HALF_FLOAT_OES&&(this._gl.HALF_FLOAT_OES=5131),this._caps.textureHalfFloatRender=this._caps.textureHalfFloat&&this._canRenderToHalfFloatFramebuffer(),this._webGLVersion>1)this._caps.drawBuffersExtension=!0,this._caps.maxMSAASamples=this._gl.getParameter(this._gl.MAX_SAMPLES);else{a=this._gl.getExtension("WEBGL_draw_buffers");if(null!==a){this._caps.drawBuffersExtension=!0,this._gl.drawBuffers=a.drawBuffersWEBGL.bind(a),this._gl.DRAW_FRAMEBUFFER=this._gl.FRAMEBUFFER;for(var b=0;b<16;b++)this._gl["COLOR_ATTACHMENT"+b+"_WEBGL"]=a["COLOR_ATTACHMENT"+b+"_WEBGL"]}}if(this._webGLVersion>1)this._caps.depthTextureExtension=!0;else{b=this._gl.getExtension("WEBGL_depth_texture");null!=b&&(this._caps.depthTextureExtension=!0,this._gl.UNSIGNED_INT_24_8=b.UNSIGNED_INT_24_8_WEBGL)}if(this.disableVertexArrayObjects)this._caps.vertexArrayObject=!1;else if(this._webGLVersion>1)this._caps.vertexArrayObject=!0;else{a=this._gl.getExtension("OES_vertex_array_object");null!=a&&(this._caps.vertexArrayObject=!0,this._gl.createVertexArray=a.createVertexArrayOES.bind(a),this._gl.bindVertexArray=a.bindVertexArrayOES.bind(a),this._gl.deleteVertexArray=a.deleteVertexArrayOES.bind(a))}if(this._webGLVersion>1)this._caps.instancedArrays=!0;else{b=this._gl.getExtension("ANGLE_instanced_arrays");null!=b?(this._caps.instancedArrays=!0,this._gl.drawArraysInstanced=b.drawArraysInstancedANGLE.bind(b),this._gl.drawElementsInstanced=b.drawElementsInstancedANGLE.bind(b),this._gl.vertexAttribDivisor=b.vertexAttribDivisorANGLE.bind(b)):this._caps.instancedArrays=!1}if(this._gl.getShaderPrecisionFormat){a=this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER,this._gl.HIGH_FLOAT);b=this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER,this._gl.HIGH_FLOAT);a&&b&&(this._caps.highPrecisionShaderSupported=0!==a.precision&&0!==b.precision)}if(this._webGLVersion>1)this._caps.blendMinMax=!0;else{a=this._gl.getExtension("EXT_blend_minmax");null!=a&&(this._caps.blendMinMax=!0,this._gl.MAX=a.MAX_EXT,this._gl.MIN=a.MIN_EXT)}if(this._webGLVersion>1)this._caps.supportSRGBBuffers=!0;else{b=this._gl.getExtension("EXT_sRGB");null!=b&&(this._caps.supportSRGBBuffers=!0,this._gl.SRGB=b.SRGB_EXT,this._gl.SRGB8=b.SRGB_ALPHA_EXT,this._gl.SRGB8_ALPHA8=b.SRGB_ALPHA_EXT)}this._depthCullingState.depthTest=!0,this._depthCullingState.depthFunc=this._gl.LEQUAL,this._depthCullingState.depthMask=!0,this._maxSimultaneousTextures=this._caps.maxCombinedTexturesImageUnits;for(a=0;a=0&&this._activeRenderLoops.splice(a,1)}else this._activeRenderLoops=[]},a.prototype._renderLoop=function(){if(!this._contextWasLost){var a=!0;if(!this.renderEvenInBackground&&this._windowIsBackground&&(a=!1),a){this.beginFrame();for(a=0;a0?this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow()):this._renderingQueueLaunched=!1},a.prototype.getRenderingCanvas=function(){return this._renderingCanvas},a.prototype.getAudioContext=function(){return this._audioContext},a.prototype.getAudioDestination=function(){return this._audioDestination},a.prototype.getHostWindow=function(){return Object(o.e)()?this._renderingCanvas&&this._renderingCanvas.ownerDocument&&this._renderingCanvas.ownerDocument.defaultView?this._renderingCanvas.ownerDocument.defaultView:window:null},a.prototype.getRenderWidth=function(a){return void 0===a&&(a=!1),!a&&this._currentRenderTarget?this._currentRenderTarget.width:this._framebufferDimensionsObject?this._framebufferDimensionsObject.framebufferWidth:this._gl.drawingBufferWidth},a.prototype.getRenderHeight=function(a){return void 0===a&&(a=!1),!a&&this._currentRenderTarget?this._currentRenderTarget.height:this._framebufferDimensionsObject?this._framebufferDimensionsObject.framebufferHeight:this._gl.drawingBufferHeight},a.prototype._queueNewFrame=function(b,c){return a.QueueNewFrame(b,c)},a.prototype.runRenderLoop=function(a){-1===this._activeRenderLoops.indexOf(a)&&(this._activeRenderLoops.push(a),this._renderingQueueLaunched||(this._renderingQueueLaunched=!0,this._boundRenderFunction=this._renderLoop.bind(this),this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow())))},a.prototype.clear=function(a,b,c,d){void 0===d&&(d=!1);var e=this.stencilStateComposer.useStencilGlobalOnly;this.stencilStateComposer.useStencilGlobalOnly=!0,this.applyStates(),this.stencilStateComposer.useStencilGlobalOnly=e;e=0;b&&a&&(this._gl.clearColor(a.r,a.g,a.b,void 0!==a.a?a.a:1),e|=this._gl.COLOR_BUFFER_BIT),c&&(this.useReverseDepthBuffer?(this._depthCullingState.depthFunc=this._gl.GEQUAL,this._gl.clearDepth(0)):this._gl.clearDepth(1),e|=this._gl.DEPTH_BUFFER_BIT),d&&(this._gl.clearStencil(0),e|=this._gl.STENCIL_BUFFER_BIT),this._gl.clear(e)},a.prototype._viewport=function(a,b,c,d){a===this._viewportCached.x&&b===this._viewportCached.y&&c===this._viewportCached.z&&d===this._viewportCached.w||(this._viewportCached.x=a,this._viewportCached.y=b,this._viewportCached.z=c,this._viewportCached.w=d,this._gl.viewport(a,b,c,d))},a.prototype.setViewport=function(a,b,c){b=b||this.getRenderWidth();c=c||this.getRenderHeight();var d=a.x||0,e=a.y||0;this._cachedViewport=a,this._viewport(d*b,e*c,b*a.width,c*a.height)},a.prototype.beginFrame=function(){},a.prototype.endFrame=function(){this._badOS&&this.flushFramebuffer(),this._frameId++},a.prototype.resize=function(a){var b,c;void 0===a&&(a=!1),Object(o.e)()?(b=this._renderingCanvas?this._renderingCanvas.clientWidth||this._renderingCanvas.width:window.innerWidth,c=this._renderingCanvas?this._renderingCanvas.clientHeight||this._renderingCanvas.height:window.innerHeight):(b=this._renderingCanvas?this._renderingCanvas.width:100,c=this._renderingCanvas?this._renderingCanvas.height:100),this.setSize(b/this._hardwareScalingLevel,c/this._hardwareScalingLevel,a)},a.prototype.setSize=function(a,b,c){return void 0===c&&(c=!1),!!this._renderingCanvas&&(a|=0,b|=0,!(!c&&this._renderingCanvas.width===a&&this._renderingCanvas.height===b)&&(this._renderingCanvas.width=a,this._renderingCanvas.height=b,!0))},a.prototype.bindFramebuffer=function(a,b,c,d,e,f,g){var h,i;void 0===b&&(b=0),void 0===f&&(f=0),void 0===g&&(g=0);var j=a;this._currentRenderTarget&&this.unBindFramebuffer(this._currentRenderTarget),this._currentRenderTarget=a,this._bindUnboundFramebuffer(j._MSAAFramebuffer?j._MSAAFramebuffer:j._framebuffer);j=this._gl;a.is2DArray?j.framebufferTextureLayer(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,null===(h=a.texture._hardwareTexture)||void 0===h?void 0:h.underlyingResource,f,g):a.isCube&&j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_CUBE_MAP_POSITIVE_X+b,null===(h=a.texture._hardwareTexture)||void 0===h?void 0:h.underlyingResource,f);h=a._depthStencilTexture;if(h){var k=a._depthStencilTextureWithStencil?j.DEPTH_STENCIL_ATTACHMENT:j.DEPTH_ATTACHMENT;a.is2DArray?j.framebufferTextureLayer(j.FRAMEBUFFER,k,null===(i=h._hardwareTexture)||void 0===i?void 0:i.underlyingResource,f,g):a.isCube?j.framebufferTexture2D(j.FRAMEBUFFER,k,j.TEXTURE_CUBE_MAP_POSITIVE_X+b,null===(i=h._hardwareTexture)||void 0===i?void 0:i.underlyingResource,f):j.framebufferTexture2D(j.FRAMEBUFFER,k,j.TEXTURE_2D,null===(g=h._hardwareTexture)||void 0===g?void 0:g.underlyingResource,f)}this._cachedViewport&&!e?this.setViewport(this._cachedViewport,c,d):(c||(c=a.width,f&&(c/=Math.pow(2,f))),d||(d=a.height,f&&(d/=Math.pow(2,f))),this._viewport(0,0,c,d)),this.wipeCaches()},a.prototype.setState=function(a,b,c,d,e,f,g){void 0===b&&(b=0),void 0===d&&(d=!1),void 0===g&&(g=0),(this._depthCullingState.cull!==a||c)&&(this._depthCullingState.cull=a);e=null===(a=null!==(a=this.cullBackFaces)&&void 0!==a?a:e)||void 0===a||a?this._gl.BACK:this._gl.FRONT;(this._depthCullingState.cullFace!==e||c)&&(this._depthCullingState.cullFace=e),this.setZOffset(b),this.setZOffsetUnits(g);a=d?this._gl.CW:this._gl.CCW;(this._depthCullingState.frontFace!==a||c)&&(this._depthCullingState.frontFace=a),this._stencilStateComposer.stencilMaterial=f},a.prototype.setZOffset=function(a){this._depthCullingState.zOffset=this.useReverseDepthBuffer?-a:a},a.prototype.getZOffset=function(){var a=this._depthCullingState.zOffset;return this.useReverseDepthBuffer?-a:a},a.prototype.setZOffsetUnits=function(a){this._depthCullingState.zOffsetUnits=this.useReverseDepthBuffer?-a:a},a.prototype.getZOffsetUnits=function(){var a=this._depthCullingState.zOffsetUnits;return this.useReverseDepthBuffer?-a:a},a.prototype._bindUnboundFramebuffer=function(a){this._currentFramebuffer!==a&&(this._gl.bindFramebuffer(this._gl.FRAMEBUFFER,a),this._currentFramebuffer=a)},a.prototype._currentFrameBufferIsDefaultFrameBuffer=function(){return null===this._currentFramebuffer},a.prototype.generateMipmaps=function(a){this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0),this._gl.generateMipmap(this._gl.TEXTURE_2D),this._bindTextureDirectly(this._gl.TEXTURE_2D,null)},a.prototype.unBindFramebuffer=function(a,b,c){void 0===b&&(b=!1);var d=a;this._currentRenderTarget=null;var e=this._gl;if(d._MSAAFramebuffer){if(a.isMulti)return void this.unBindMultiColorAttachmentFramebuffer(a,b,c);e.bindFramebuffer(e.READ_FRAMEBUFFER,d._MSAAFramebuffer),e.bindFramebuffer(e.DRAW_FRAMEBUFFER,d._framebuffer),e.blitFramebuffer(0,0,a.width,a.height,0,0,a.width,a.height,e.COLOR_BUFFER_BIT,e.NEAREST)}!(null===(e=a.texture)||void 0===e?void 0:e.generateMipMaps)||b||a.isCube||this.generateMipmaps(a.texture),c&&(d._MSAAFramebuffer&&this._bindUnboundFramebuffer(d._framebuffer),c()),this._bindUnboundFramebuffer(null)},a.prototype.flushFramebuffer=function(){this._gl.flush()},a.prototype.restoreDefaultFramebuffer=function(){this._currentRenderTarget?this.unBindFramebuffer(this._currentRenderTarget):this._bindUnboundFramebuffer(null),this._cachedViewport&&this.setViewport(this._cachedViewport),this.wipeCaches()},a.prototype._resetVertexBufferBinding=function(){this.bindArrayBuffer(null),this._cachedVertexBuffers=null},a.prototype.createVertexBuffer=function(a){return this._createVertexBuffer(a,this._gl.STATIC_DRAW)},a.prototype._createVertexBuffer=function(a,b){var c=this._gl.createBuffer();if(!c)throw new Error("Unable to create vertex buffer");c=new s.a(c);return this.bindArrayBuffer(c),a instanceof Array?this._gl.bufferData(this._gl.ARRAY_BUFFER,new Float32Array(a),b):this._gl.bufferData(this._gl.ARRAY_BUFFER,a,b),this._resetVertexBufferBinding(),c.references=1,c},a.prototype.createDynamicVertexBuffer=function(a){return this._createVertexBuffer(a,this._gl.DYNAMIC_DRAW)},a.prototype._resetIndexBufferBinding=function(){this.bindIndexBuffer(null),this._cachedIndexBuffer=null},a.prototype.createIndexBuffer=function(a,b){var c=this._gl.createBuffer(),d=new s.a(c);if(!c)throw new Error("Unable to create index buffer");this.bindIndexBuffer(d);c=this._normalizeIndexData(a);return this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER,c,b?this._gl.DYNAMIC_DRAW:this._gl.STATIC_DRAW),this._resetIndexBufferBinding(),d.references=1,d.is32Bits=4===c.BYTES_PER_ELEMENT,d},a.prototype._normalizeIndexData=function(a){if(2===a.BYTES_PER_ELEMENT)return a;if(this._caps.uintIndices){if(a instanceof Uint32Array)return a;for(var b=0;b=65535)return new Uint32Array(a);return new Uint16Array(a)}return new Uint16Array(a)},a.prototype.bindArrayBuffer=function(a){this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.bindBuffer(a,this._gl.ARRAY_BUFFER)},a.prototype.bindUniformBlock=function(a,b,c){a=a.program;b=this._gl.getUniformBlockIndex(a,b);this._gl.uniformBlockBinding(a,b,c)},a.prototype.bindIndexBuffer=function(a){this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.bindBuffer(a,this._gl.ELEMENT_ARRAY_BUFFER)},a.prototype.bindBuffer=function(a,b){(this._vaoRecordInProgress||this._currentBoundBuffer[b]!==a)&&(this._gl.bindBuffer(b,a?a.underlyingResource:null),this._currentBoundBuffer[b]=a)},a.prototype.updateArrayBuffer=function(a){this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,a)},a.prototype._vertexAttribPointer=function(a,b,c,d,e,f,g){var h=this._currentBufferPointers[b];if(h){var i=!1;h.active?(h.buffer!==a&&(h.buffer=a,i=!0),h.size!==c&&(h.size=c,i=!0),h.type!==d&&(h.type=d,i=!0),h.normalized!==e&&(h.normalized=e,i=!0),h.stride!==f&&(h.stride=f,i=!0),h.offset!==g&&(h.offset=g,i=!0)):(i=!0,h.active=!0,h.index=b,h.size=c,h.type=d,h.normalized=e,h.stride=f,h.offset=g,h.buffer=a),(i||this._vaoRecordInProgress)&&(this.bindArrayBuffer(a),this._gl.vertexAttribPointer(b,c,d,e,f,g))}},a.prototype._bindIndexBufferWithCache=function(a){null!=a&&this._cachedIndexBuffer!==a&&(this._cachedIndexBuffer=a,this.bindIndexBuffer(a),this._uintIndicesCurrentlySet=a.is32Bits)},a.prototype._bindVertexBuffersAttributes=function(a,b,c){var d=b.getAttributesNames();this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.unbindAllAttributes();for(var e=0;e=0){var g=d[e],h=null;if(c&&(h=c[g]),h||(h=a[g]),!h)continue;this._gl.enableVertexAttribArray(f),this._vaoRecordInProgress||(this._vertexAttribArraysEnabled[f]=!0);g=h.getBuffer();g&&(this._vertexAttribPointer(g,f,h.getSize(),h.type,h.normalized,h.byteStride,h.byteOffset),h.getIsInstanced()&&(this._gl.vertexAttribDivisor(f,h.getInstanceDivisor()),this._vaoRecordInProgress||(this._currentInstanceLocations.push(f),this._currentInstanceBuffers.push(g))))}}},a.prototype.recordVertexArrayObject=function(a,b,c,d){var e=this._gl.createVertexArray();return this._vaoRecordInProgress=!0,this._gl.bindVertexArray(e),this._mustWipeVertexAttributes=!0,this._bindVertexBuffersAttributes(a,c,d),this.bindIndexBuffer(b),this._vaoRecordInProgress=!1,this._gl.bindVertexArray(null),e},a.prototype.bindVertexArrayObject=function(a,b){this._cachedVertexArrayObject!==a&&(this._cachedVertexArrayObject=a,this._gl.bindVertexArray(a),this._cachedVertexBuffers=null,this._cachedIndexBuffer=null,this._uintIndicesCurrentlySet=null!=b&&b.is32Bits,this._mustWipeVertexAttributes=!0)},a.prototype.bindBuffersDirectly=function(a,b,c,d,e){if(this._cachedVertexBuffers!==a||this._cachedEffectForVertexBuffers!==e){this._cachedVertexBuffers=a,this._cachedEffectForVertexBuffers=e;var f=e.getAttributesCount();this._unbindVertexArrayObject(),this.unbindAllAttributes();for(var g=0,h=0;h=0&&(this._gl.enableVertexAttribArray(i),this._vertexAttribArraysEnabled[i]=!0,this._vertexAttribPointer(a,i,c[h],this._gl.FLOAT,!1,d,g)),g+=4*c[h]}}this._bindIndexBufferWithCache(b)},a.prototype._unbindVertexArrayObject=function(){this._cachedVertexArrayObject&&(this._cachedVertexArrayObject=null,this._gl.bindVertexArray(null))},a.prototype.bindBuffers=function(a,b,c,d){this._cachedVertexBuffers===a&&this._cachedEffectForVertexBuffers===c||(this._cachedVertexBuffers=a,this._cachedEffectForVertexBuffers=c,this._bindVertexBuffersAttributes(a,c,d)),this._bindIndexBufferWithCache(b)},a.prototype.unbindInstanceAttributes=function(){for(var a,b=0,c=this._currentInstanceLocations.length;b1?"#version 300 es #define WEBGL2 ":"";b=this._compileShader(b,"vertex",d,g);c=this._compileShader(c,"fragment",d,g);return this._createShaderProgram(a,b,c,e,f)},a.prototype.inlineShaderCode=function(a){return a},a.prototype.createPipelineContext=function(a){a=new t.a;return a.engine=this,this._caps.parallelShaderCompile&&(a.isParallelCompiled=!0),a},a.prototype.createMaterialContext=function(){},a.prototype.createDrawContext=function(){},a.prototype._createShaderProgram=function(a,b,c,d,e){e=d.createProgram();if(a.program=e,!e)throw new Error("Unable to create program");return d.attachShader(e,b),d.attachShader(e,c),d.linkProgram(e),a.context=d,a.vertexShader=b,a.fragmentShader=c,a.isParallelCompiled||this._finalizePipelineContext(a),e},a.prototype._finalizePipelineContext=function(a){var b=a.context,c=a.vertexShader,d=a.fragmentShader,e=a.program;if(!b.getProgramParameter(e,b.LINK_STATUS)){var f,g;if(!this._gl.getShaderParameter(c,this._gl.COMPILE_STATUS)&&(f=this._gl.getShaderInfoLog(c)))throw a.vertexCompilationError=f,new Error("VERTEX SHADER "+f);if(!this._gl.getShaderParameter(d,this._gl.COMPILE_STATUS)&&(f=this._gl.getShaderInfoLog(d)))throw a.fragmentCompilationError=f,new Error("FRAGMENT SHADER "+f);if(g=b.getProgramInfoLog(e))throw a.programLinkError=g,new Error(g)}if(this.validateShaderPrograms&&(b.validateProgram(e),!b.getProgramParameter(e,b.VALIDATE_STATUS)&&(g=b.getProgramInfoLog(e))))throw a.programValidationError=g,new Error(g);b.deleteShader(c),b.deleteShader(d),a.vertexShader=void 0,a.fragmentShader=void 0,a.onCompiled&&(a.onCompiled(),a.onCompiled=void 0)},a.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i,j){e=a;e.program=d?this.createRawShaderProgram(e,b,c,void 0,i):this.createShaderProgram(e,b,c,h,void 0,i),e.program.__SPECTOR_rebuildProgram=g},a.prototype._isRenderingStateCompiled=function(a){a=a;return!!this._gl.getProgramParameter(a.program,this._caps.parallelShaderCompile.COMPLETION_STATUS_KHR)&&(this._finalizePipelineContext(a),!0)},a.prototype._executeWhenRenderingStateIsCompiled=function(a,b){a=a;if(a.isParallelCompiled){var c=a.onCompiled;a.onCompiled=c?function(){c(),b()}:b}else b()},a.prototype.getUniforms=function(a,b){for(var c=new Array,a=a,d=0;d1||this.isWebGPU||b)},a.prototype._createTextureBase=function(b,c,d,f,g,h,i,j,k,o,p,q,r,s,t,u){var v=this;void 0===g&&(g=l.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===h&&(h=null),void 0===i&&(i=null),void 0===o&&(o=null),void 0===p&&(p=null),void 0===q&&(q=null),void 0===r&&(r=null);var w="data:"===(b=b||"").substr(0,5),x="blob:"===b.substr(0,5),y=w&&-1!==b.indexOf(";base64,"),z=p||new m.a(this,m.b.Url),A=b;!this._transformTextureUrl||y||p||o||(b=this._transformTextureUrl(b)),A!==b&&(z._originalUrl=A);var B=b.lastIndexOf("."),C=r||(B>-1?b.substring(B).toLowerCase():""),D=null;C.indexOf("?")>-1&&(C=C.split("?")[0]);for(var B=0,E=a._TextureLoaders;Bi||c.height>i||!w._supportsHardwareTextureRescaling)return w._prepareWorkingCanvas(),!(!w._workingCanvas||!w._workingContext)&&(w._workingCanvas.width=a,w._workingCanvas.height=b,w._workingContext.drawImage(c,0,0,c.width,c.height,0,0,a,b),h.texImage2D(h.TEXTURE_2D,0,k,e,h.UNSIGNED_BYTE,w._workingCanvas),f.width=a,f.height=b,!1);var l=new m.a(w,m.b.Temp);return w._bindTextureDirectly(h.TEXTURE_2D,l,!0),h.texImage2D(h.TEXTURE_2D,0,k,e,h.UNSIGNED_BYTE,c),w._rescaleTexture(l,f,d,k,function(){w._releaseTexture(l),w._bindTextureDirectly(h.TEXTURE_2D,f,!0),g()}),!0},h,i,j,k,s,t,v)},a._FileToolsLoadImage=function(a,b,c,d,e,f){throw Object(g.a)("FileTools")},a.prototype._rescaleTexture=function(a,b,c,d,e){},a.prototype.createRawTexture=function(a,b,c,d,e,f,h,i,j){throw void 0===j&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype.createRawCubeTexture=function(a,b,c,d,e,f,h,i){throw Object(g.a)("Engine.RawTexture")},a.prototype.createRawTexture3D=function(a,b,c,d,e,f,h,i,j,k){throw void 0===k&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype.createRawTexture2DArray=function(a,b,c,d,e,f,h,i,j,k){throw void 0===k&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype._unpackFlipY=function(a){this._unpackFlipYCached!==a&&(this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL,a?1:0),this.enableUnpackFlipYCached&&(this._unpackFlipYCached=a))},a.prototype._getUnpackAlignement=function(){return this._gl.getParameter(this._gl.UNPACK_ALIGNMENT)},a.prototype._getTextureTarget=function(a){return a.isCube?this._gl.TEXTURE_CUBE_MAP:a.is3D?this._gl.TEXTURE_3D:a.is2DArray||a.isMultiview?this._gl.TEXTURE_2D_ARRAY:this._gl.TEXTURE_2D},a.prototype.updateTextureSamplingMode=function(a,b,c){void 0===c&&(c=!1);var d=this._getTextureTarget(b),e=this._getSamplingParameters(a,b.generateMipMaps||c);this._setTextureParameterInteger(d,this._gl.TEXTURE_MAG_FILTER,e.mag,b),this._setTextureParameterInteger(d,this._gl.TEXTURE_MIN_FILTER,e.min),c&&(b.generateMipMaps=!0,this._gl.generateMipmap(d)),this._bindTextureDirectly(d,null),b.samplingMode=a},a.prototype.updateTextureDimensions=function(a,b,c,d){},a.prototype.updateTextureWrappingMode=function(a,b,c,d){void 0===c&&(c=null),void 0===d&&(d=null);var e=this._getTextureTarget(a);null!==b&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_S,this._getTextureWrapMode(b),a),a._cachedWrapU=b),null!==c&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_T,this._getTextureWrapMode(c),a),a._cachedWrapV=c),(a.is2DArray||a.is3D)&&null!==d&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_R,this._getTextureWrapMode(d),a),a._cachedWrapR=d),this._bindTextureDirectly(e,null)},a.prototype._setupDepthStencilTexture=function(a,b,c,d,e,f){void 0===f&&(f=1);c=b.width||b;var g=b.height||b;b=b.layers||0;a.baseWidth=c,a.baseHeight=g,a.width=c,a.height=g,a.is2DArray=b>0,a.depth=b,a.isReady=!0,a.samples=f,a.generateMipMaps=!1,a.samplingMode=d?l.a.TEXTURE_BILINEAR_SAMPLINGMODE:l.a.TEXTURE_NEAREST_SAMPLINGMODE,a.type=l.a.TEXTURETYPE_UNSIGNED_INT,a._comparisonFunction=e;c=this._gl;g=this._getTextureTarget(a);b=this._getSamplingParameters(a.samplingMode,!1);c.texParameteri(g,c.TEXTURE_MAG_FILTER,b.mag),c.texParameteri(g,c.TEXTURE_MIN_FILTER,b.min),c.texParameteri(g,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(g,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),0===e?(c.texParameteri(g,c.TEXTURE_COMPARE_FUNC,l.a.LEQUAL),c.texParameteri(g,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(g,c.TEXTURE_COMPARE_FUNC,e),c.texParameteri(g,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE))},a.prototype._uploadCompressedDataToTextureDirectly=function(a,b,c,d,e,f,g){void 0===f&&(f=0),void 0===g&&(g=0);var h=this._gl,i=h.TEXTURE_2D;if(a.isCube&&(i=h.TEXTURE_CUBE_MAP_POSITIVE_X+f),a._useSRGBBuffer)switch(b){case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:b=h.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4:b=h.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1:this._caps.s3tc_srgb?b=h.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:a._useSRGBBuffer=!1;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:this._caps.s3tc_srgb?b=h.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:a._useSRGBBuffer=!1;break;default:a._useSRGBBuffer=!1}this._gl.compressedTexImage2D(i,g,b,c,d,0,e)},a.prototype._uploadDataToTextureDirectly=function(a,b,c,d,e,f){void 0===c&&(c=0),void 0===d&&(d=0),void 0===f&&(f=!1);var g=this._gl,h=this._getWebGLTextureType(a.type),i=this._getInternalFormat(a.format);e=void 0===e?this._getRGBABufferInternalSizedFormat(a.type,a.format,a._useSRGBBuffer):this._getInternalFormat(e,a._useSRGBBuffer);this._unpackFlipY(a.invertY);var j=g.TEXTURE_2D;a.isCube&&(j=g.TEXTURE_CUBE_MAP_POSITIVE_X+c);c=Math.round(Math.log(a.width)*Math.LOG2E);var k=Math.round(Math.log(a.height)*Math.LOG2E);c=f?a.width:Math.pow(2,Math.max(c-d,0));f=f?a.height:Math.pow(2,Math.max(k-d,0));g.texImage2D(j,d,e,c,f,0,i,h,b)},a.prototype.updateTextureData=function(a,b,c,d,e,f,g,h){void 0===g&&(g=0),void 0===h&&(h=0);var i=this._gl,j=this._getWebGLTextureType(a.type),k=this._getInternalFormat(a.format);this._unpackFlipY(a.invertY);var m=i.TEXTURE_2D;a.isCube&&(m=i.TEXTURE_CUBE_MAP_POSITIVE_X+g),i.texSubImage2D(m,h,c,d,e,f,k,j,b)},a.prototype._uploadArrayBufferViewToTexture=function(a,b,c,d){void 0===c&&(c=0),void 0===d&&(d=0);var e=this._gl;e=a.isCube?e.TEXTURE_CUBE_MAP:e.TEXTURE_2D;this._bindTextureDirectly(e,a,!0),this._uploadDataToTextureDirectly(a,b,c,d),this._bindTextureDirectly(e,null,!0)},a.prototype._prepareWebGLTextureContinuation=function(a,b,c,d,e){var f=this._gl;if(f){e=this._getSamplingParameters(e,!c);f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MAG_FILTER,e.mag),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MIN_FILTER,e.min),c||d||f.generateMipmap(f.TEXTURE_2D),this._bindTextureDirectly(f.TEXTURE_2D,null),b&&b._removePendingData(a),a.onLoadedObservable.notifyObservers(a),a.onLoadedObservable.clear()}},a.prototype._prepareWebGLTexture=function(b,c,d,e,f,g,h,i,j){var k=this;void 0===j&&(j=l.a.TEXTURE_TRILINEAR_SAMPLINGMODE);var o=this.getCaps().maxTextureSize,q=Math.min(o,this.needPOTTextures?a.GetExponentOfTwo(e.width,o):e.width);o=Math.min(o,this.needPOTTextures?a.GetExponentOfTwo(e.height,o):e.height);var r=this._gl;r&&(b._hardwareTexture?(this._bindTextureDirectly(r.TEXTURE_2D,b,!0),this._unpackFlipY(void 0===f||!!f),b.baseWidth=e.width,b.baseHeight=e.height,b.width=q,b.height=o,b.isReady=!0,i(q,o,e,c,b,function(){k._prepareWebGLTextureContinuation(b,d,g,h,j)})||this._prepareWebGLTextureContinuation(b,d,g,h,j)):d&&d._removePendingData(b))},a.prototype._setupFramebufferDepthAttachments=function(a,b,c,d,e){void 0===e&&(e=1);var f=this._gl;if(a&&b)return this._createRenderBuffer(c,d,e,f.DEPTH_STENCIL,f.DEPTH24_STENCIL8,f.DEPTH_STENCIL_ATTACHMENT);if(b){b=f.DEPTH_COMPONENT16;return this._webGLVersion>1&&(b=f.DEPTH_COMPONENT32F),this._createRenderBuffer(c,d,e,b,b,f.DEPTH_ATTACHMENT)}return a?this._createRenderBuffer(c,d,e,f.STENCIL_INDEX8,f.STENCIL_INDEX8,f.STENCIL_ATTACHMENT):null},a.prototype._createRenderBuffer=function(a,b,c,d,e,f,g){void 0===g&&(g=!0);var h=this._gl,i=h.createRenderbuffer();return h.bindRenderbuffer(h.RENDERBUFFER,i),c>1&&h.renderbufferStorageMultisample?h.renderbufferStorageMultisample(h.RENDERBUFFER,c,e,a,b):h.renderbufferStorage(h.RENDERBUFFER,d,a,b),h.framebufferRenderbuffer(h.FRAMEBUFFER,f,h.RENDERBUFFER,i),g&&h.bindRenderbuffer(h.RENDERBUFFER,null),i},a.prototype._releaseTexture=function(a){var b;this._deleteTexture(null===(b=a._hardwareTexture)||void 0===b?void 0:b.underlyingResource),this.unbindAllTextures();b=this._internalTexturesCache.indexOf(a);-1!==b&&this._internalTexturesCache.splice(b,1),a._lodTextureHigh&&a._lodTextureHigh.dispose(),a._lodTextureMid&&a._lodTextureMid.dispose(),a._lodTextureLow&&a._lodTextureLow.dispose(),a._irradianceTexture&&a._irradianceTexture.dispose()},a.prototype._releaseRenderTargetWrapper=function(a){a=this._renderTargetWrapperCache.indexOf(a);-1!==a&&this._renderTargetWrapperCache.splice(a,1)},a.prototype._deleteTexture=function(a){a&&this._gl.deleteTexture(a)},a.prototype._setProgram=function(a){this._currentProgram!==a&&(this._gl.useProgram(a),this._currentProgram=a)},a.prototype.bindSamplers=function(a){var b=a.getPipelineContext();this._setProgram(b.program);for(var b=a.getSamplers(),c=0;c-1;if(c&&f&&(this._activeChannel=b._associatedChannel),this._boundTexturesCache[this._activeChannel]!==b||d){if(this._activateCurrentTexture(),b&&b.isMultiview)throw!1,"_bindTextureDirectly called with a multiview texture!";this._gl.bindTexture(a,null!==(a=null===(d=null==b?void 0:b._hardwareTexture)||void 0===d?void 0:d.underlyingResource)&&void 0!==a?a:null),this._boundTexturesCache[this._activeChannel]=b,b&&(b._associatedChannel=this._activeChannel)}else c&&(e=!0,this._activateCurrentTexture());return f&&!c&&this._bindSamplerUniformToChannel(b._associatedChannel,this._activeChannel),e},a.prototype._bindTexture=function(a,b,c){if(void 0!==a){b&&(b._associatedChannel=a),this._activeChannel=a;c=b?this._getTextureTarget(b):this._gl.TEXTURE_2D;this._bindTextureDirectly(c,b)}},a.prototype.unbindAllTextures=function(){for(var a=0;a1&&(this._bindTextureDirectly(this._gl.TEXTURE_3D,null),this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY,null))},a.prototype.setTexture=function(a,b,c,d){void 0!==a&&(b&&(this._boundUniforms[a]=b),this._setTexture(a,c))},a.prototype._bindSamplerUniformToChannel=function(a,b){a=this._boundUniforms[a];a&&a._currentState!==b&&(this._gl.uniform1i(a,b),a._currentState=b)},a.prototype._getTextureWrapMode=function(a){switch(a){case l.a.TEXTURE_WRAP_ADDRESSMODE:return this._gl.REPEAT;case l.a.TEXTURE_CLAMP_ADDRESSMODE:return this._gl.CLAMP_TO_EDGE;case l.a.TEXTURE_MIRROR_ADDRESSMODE:return this._gl.MIRRORED_REPEAT}return this._gl.REPEAT},a.prototype._setTexture=function(a,b,c,d,e){if(void 0===c&&(c=!1),void 0===d&&(d=!1),!b)return null!=this._boundTexturesCache[a]&&(this._activeChannel=a,this._bindTextureDirectly(this._gl.TEXTURE_2D,null),this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,null),this.webGLVersion>1&&(this._bindTextureDirectly(this._gl.TEXTURE_3D,null),this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY,null))),!1;if(b.video)this._activeChannel=a,b.update();else if(b.delayLoadState===l.a.DELAYLOADSTATE_NOTLOADED)return b.delayLoad(),!1;e=d?b.depthStencilTexture:b.isReady()?b.getInternalTexture():b.isCube?this.emptyCubeTexture:b.is3D?this.emptyTexture3D:b.is2DArray?this.emptyTexture2DArray:this.emptyTexture,!c&&e&&(e._associatedChannel=a);d=!0;this._boundTexturesCache[a]===e&&(c||this._bindSamplerUniformToChannel(e._associatedChannel,a),d=!1),this._activeChannel=a;a=this._getTextureTarget(e);if(d&&this._bindTextureDirectly(a,e,c),e&&!e.isMultiview){if(e.isCube&&e._cachedCoordinatesMode!==b.coordinatesMode){e._cachedCoordinatesMode=b.coordinatesMode;d=b.coordinatesMode!==l.a.TEXTURE_CUBIC_MODE&&b.coordinatesMode!==l.a.TEXTURE_SKYBOX_MODE?l.a.TEXTURE_WRAP_ADDRESSMODE:l.a.TEXTURE_CLAMP_ADDRESSMODE;b.wrapU=d,b.wrapV=d}e._cachedWrapU!==b.wrapU&&(e._cachedWrapU=b.wrapU,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_S,this._getTextureWrapMode(b.wrapU),e)),e._cachedWrapV!==b.wrapV&&(e._cachedWrapV=b.wrapV,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_T,this._getTextureWrapMode(b.wrapV),e)),e.is3D&&e._cachedWrapR!==b.wrapR&&(e._cachedWrapR=b.wrapR,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_R,this._getTextureWrapMode(b.wrapR),e)),this._setAnisotropicLevel(a,e,b.anisotropicFilteringLevel)}return!0},a.prototype.setTextureArray=function(a,b,c,d){if(void 0!==a&&b){this._textureUnits&&this._textureUnits.length===c.length||(this._textureUnits=new Int32Array(c.length));for(d=0;d=this._caps.maxVertexAttribs||!this._vertexAttribArraysEnabled[a]||this.disableAttributeByIndex(a)}},a.prototype.releaseEffects=function(){for(var a in this._compiledEffects){var b=this._compiledEffects[a].getPipelineContext();this._deletePipelineContext(b)}this._compiledEffects={}},a.prototype.dispose=function(){var a;this.stopRenderLoop(),this.onBeforeTextureInitObservable&&this.onBeforeTextureInitObservable.clear(),this._emptyTexture&&(this._releaseTexture(this._emptyTexture),this._emptyTexture=null),this._emptyCubeTexture&&(this._releaseTexture(this._emptyCubeTexture),this._emptyCubeTexture=null),this._dummyFramebuffer&&this._gl.deleteFramebuffer(this._dummyFramebuffer),this.releaseEffects(),null===(a=this.releaseComputeEffects)||void 0===a||a.call(this),this.unbindAllAttributes(),this._boundUniforms=[],Object(o.e)()&&this._renderingCanvas&&(this._doNotHandleContextLost||(this._renderingCanvas.removeEventListener("webglcontextlost",this._onContextLost),this._renderingCanvas.removeEventListener("webglcontextrestored",this._onContextRestored)),window.removeEventListener("resize",this._checkForMobile)),this._workingCanvas=null,this._workingContext=null,this._currentBufferPointers=[],this._renderingCanvas=null,this._currentProgram=null,this._boundRenderFunction=null,f.a.ResetCache();for(var a=0,b=this._activeRequests;a1?this._caps.colorBufferFloat:this._canRenderToFramebuffer(l.a.TEXTURETYPE_FLOAT)},a.prototype._canRenderToHalfFloatFramebuffer=function(){return this._webGLVersion>1?this._caps.colorBufferFloat:this._canRenderToFramebuffer(l.a.TEXTURETYPE_HALF_FLOAT)},a.prototype._canRenderToFramebuffer=function(a){for(var b=this._gl;b.getError()!==b.NO_ERROR;);var c=!0,d=b.createTexture();b.bindTexture(b.TEXTURE_2D,d),b.texImage2D(b.TEXTURE_2D,0,this._getRGBABufferInternalSizedFormat(a),1,1,0,b.RGBA,this._getWebGLTextureType(a),null),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);a=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,a),b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,d,0);var e=b.checkFramebufferStatus(b.FRAMEBUFFER);if((c=(c=c&&e===b.FRAMEBUFFER_COMPLETE)&&b.getError()===b.NO_ERROR)&&(b.clear(b.COLOR_BUFFER_BIT),c=c&&b.getError()===b.NO_ERROR),c){b.bindFramebuffer(b.FRAMEBUFFER,null);e=b.RGBA;var f=b.UNSIGNED_BYTE,g=new Uint8Array(4);b.readPixels(0,0,1,1,e,f,g),c=c&&b.getError()===b.NO_ERROR}for(b.deleteTexture(d),b.deleteFramebuffer(a),b.bindFramebuffer(b.FRAMEBUFFER,null);!c&&b.getError()!==b.NO_ERROR;);return c},a.prototype._getWebGLTextureType=function(a){if(1===this._webGLVersion){switch(a){case l.a.TEXTURETYPE_FLOAT:return this._gl.FLOAT;case l.a.TEXTURETYPE_HALF_FLOAT:return this._gl.HALF_FLOAT_OES;case l.a.TEXTURETYPE_UNSIGNED_BYTE:return this._gl.UNSIGNED_BYTE;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.UNSIGNED_SHORT_4_4_4_4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.UNSIGNED_SHORT_5_5_5_1;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.UNSIGNED_SHORT_5_6_5}return this._gl.UNSIGNED_BYTE}switch(a){case l.a.TEXTURETYPE_BYTE:return this._gl.BYTE;case l.a.TEXTURETYPE_UNSIGNED_BYTE:return this._gl.UNSIGNED_BYTE;case l.a.TEXTURETYPE_SHORT:return this._gl.SHORT;case l.a.TEXTURETYPE_UNSIGNED_SHORT:return this._gl.UNSIGNED_SHORT;case l.a.TEXTURETYPE_INT:return this._gl.INT;case l.a.TEXTURETYPE_UNSIGNED_INTEGER:return this._gl.UNSIGNED_INT;case l.a.TEXTURETYPE_FLOAT:return this._gl.FLOAT;case l.a.TEXTURETYPE_HALF_FLOAT:return this._gl.HALF_FLOAT;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.UNSIGNED_SHORT_4_4_4_4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.UNSIGNED_SHORT_5_5_5_1;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.UNSIGNED_SHORT_5_6_5;case l.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:return this._gl.UNSIGNED_INT_2_10_10_10_REV;case l.a.TEXTURETYPE_UNSIGNED_INT_24_8:return this._gl.UNSIGNED_INT_24_8;case l.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:return this._gl.UNSIGNED_INT_10F_11F_11F_REV;case l.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:return this._gl.UNSIGNED_INT_5_9_9_9_REV;case l.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV:return this._gl.FLOAT_32_UNSIGNED_INT_24_8_REV}return this._gl.UNSIGNED_BYTE},a.prototype._getInternalFormat=function(a,b){void 0===b&&(b=!1);var c=b?this._gl.SRGB8_ALPHA8:this._gl.RGBA;switch(a){case l.a.TEXTUREFORMAT_ALPHA:c=this._gl.ALPHA;break;case l.a.TEXTUREFORMAT_LUMINANCE:c=this._gl.LUMINANCE;break;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:c=this._gl.LUMINANCE_ALPHA;break;case l.a.TEXTUREFORMAT_RED:c=this._gl.RED;break;case l.a.TEXTUREFORMAT_RG:c=this._gl.RG;break;case l.a.TEXTUREFORMAT_RGB:c=b?this._gl.SRGB:this._gl.RGB;break;case l.a.TEXTUREFORMAT_RGBA:c=b?this._gl.SRGB8_ALPHA8:this._gl.RGBA}if(this._webGLVersion>1)switch(a){case l.a.TEXTUREFORMAT_RED_INTEGER:c=this._gl.RED_INTEGER;break;case l.a.TEXTUREFORMAT_RG_INTEGER:c=this._gl.RG_INTEGER;break;case l.a.TEXTUREFORMAT_RGB_INTEGER:c=this._gl.RGB_INTEGER;break;case l.a.TEXTUREFORMAT_RGBA_INTEGER:c=this._gl.RGBA_INTEGER}return c},a.prototype._getRGBABufferInternalSizedFormat=function(a,b,c){if(void 0===c&&(c=!1),1===this._webGLVersion){if(void 0!==b)switch(b){case l.a.TEXTUREFORMAT_ALPHA:return this._gl.ALPHA;case l.a.TEXTUREFORMAT_LUMINANCE:return this._gl.LUMINANCE;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:return this._gl.LUMINANCE_ALPHA;case l.a.TEXTUREFORMAT_RGB:return c?this._gl.SRGB:this._gl.RGB}return this._gl.RGBA}switch(a){case l.a.TEXTURETYPE_BYTE:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R8_SNORM;case l.a.TEXTUREFORMAT_RG:return this._gl.RG8_SNORM;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB8_SNORM;case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R8I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG8I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB8I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGBA8I;default:return this._gl.RGBA8_SNORM}case l.a.TEXTURETYPE_UNSIGNED_BYTE:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R8;case l.a.TEXTUREFORMAT_RG:return this._gl.RG8;case l.a.TEXTUREFORMAT_RGB:return c?this._gl.SRGB8:this._gl.RGB8;case l.a.TEXTUREFORMAT_RGBA:return c?this._gl.SRGB8_ALPHA8:this._gl.RGBA8;case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R8UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG8UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB8UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGBA8UI;case l.a.TEXTUREFORMAT_ALPHA:return this._gl.ALPHA;case l.a.TEXTUREFORMAT_LUMINANCE:return this._gl.LUMINANCE;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:return this._gl.LUMINANCE_ALPHA;default:return this._gl.RGBA8}case l.a.TEXTURETYPE_SHORT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R16I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG16I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB16I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA16I}case l.a.TEXTURETYPE_UNSIGNED_SHORT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R16UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG16UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB16UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA16UI}case l.a.TEXTURETYPE_INT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R32I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG32I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB32I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA32I}case l.a.TEXTURETYPE_UNSIGNED_INTEGER:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R32UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG32UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB32UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA32UI}case l.a.TEXTURETYPE_FLOAT:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R32F;case l.a.TEXTUREFORMAT_RG:return this._gl.RG32F;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB32F;case l.a.TEXTUREFORMAT_RGBA:default:return this._gl.RGBA32F}case l.a.TEXTURETYPE_HALF_FLOAT:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R16F;case l.a.TEXTUREFORMAT_RG:return this._gl.RG16F;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB16F;case l.a.TEXTUREFORMAT_RGBA:default:return this._gl.RGBA16F}case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.RGB565;case l.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:return this._gl.R11F_G11F_B10F;case l.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:return this._gl.RGB9_E5;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.RGBA4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.RGB5_A1;case l.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:switch(b){case l.a.TEXTUREFORMAT_RGBA:return this._gl.RGB10_A2;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGB10_A2UI;default:return this._gl.RGB10_A2}}return c?this._gl.SRGB8_ALPHA8:this._gl.RGBA8},a.prototype._getRGBAMultiSampleBufferFormat=function(a){return a===l.a.TEXTURETYPE_FLOAT?this._gl.RGBA32F:a===l.a.TEXTURETYPE_HALF_FLOAT?this._gl.RGBA16F:this._gl.RGBA8},a.prototype._loadFile=function(b,c,d,e,f,g){var h=this;b=a._FileToolsLoadFile(b,c,d,e,f,g);return this._activeRequests.push(b),b.onCompleteObservable.add(function(a){h._activeRequests.splice(h._activeRequests.indexOf(a),1)}),b},a._FileToolsLoadFile=function(a,b,c,d,e,f){throw Object(g.a)("FileTools")},a.prototype.readPixels=function(a,b,c,d,e,f){void 0===e&&(e=!0),void 0===f&&(f=!0);var g=e?4:3;e=e?this._gl.RGBA:this._gl.RGB;g=new Uint8Array(d*c*g);return f&&this.flushFramebuffer(),this._gl.readPixels(a,b,c,d,e,this._gl.UNSIGNED_BYTE,g),Promise.resolve(g)},Object.defineProperty(a,"IsSupportedAsync",{get:function(){return Promise.resolve(this.isSupported())},enumerable:!1,configurable:!0}),Object.defineProperty(a,"IsSupported",{get:function(){return this.isSupported()},enumerable:!1,configurable:!0}),a.isSupported=function(){if(null!==this._HasMajorPerformanceCaveat)return!this._HasMajorPerformanceCaveat;if(null===this._IsSupported)try{var a=this._createCanvas(1,1);a=a.getContext("webgl")||a.getContext("experimental-webgl");this._IsSupported=null!=a&&!!window.WebGLRenderingContext}catch(a){this._IsSupported=!1}return this._IsSupported},Object.defineProperty(a,"HasMajorPerformanceCaveat",{get:function(){if(null===this._HasMajorPerformanceCaveat)try{var a=this._createCanvas(1,1);a=a.getContext("webgl",{failIfMajorPerformanceCaveat:!0})||a.getContext("experimental-webgl",{failIfMajorPerformanceCaveat:!0});this._HasMajorPerformanceCaveat=!a}catch(a){this._HasMajorPerformanceCaveat=!1}return this._HasMajorPerformanceCaveat},enumerable:!1,configurable:!0}),a.CeilingPOT=function(a){return a--,a|=a>>1,a|=a>>2,a|=a>>4,a|=a>>8,a|=a>>16,++a},a.FloorPOT=function(a){return a|=a>>1,a|=a>>2,a|=a>>4,a|=a>>8,(a|=a>>16)-(a>>1)},a.NearestPOT=function(b){var c=a.CeilingPOT(b),d=a.FloorPOT(b);return c-b>b-d?d:c},a.GetExponentOfTwo=function(b,c,d){var e;switch(void 0===d&&(d=l.a.SCALEMODE_NEAREST),d){case l.a.SCALEMODE_FLOOR:e=a.FloorPOT(b);break;case l.a.SCALEMODE_NEAREST:e=a.NearestPOT(b);break;case l.a.SCALEMODE_CEILING:default:e=a.CeilingPOT(b)}return Math.min(e,c)},a.QueueNewFrame=function(a,b){return Object(o.e)()?(b||(b=window),b.requestPostAnimationFrame?b.requestPostAnimationFrame(a):b.requestAnimationFrame?b.requestAnimationFrame(a):b.msRequestAnimationFrame?b.msRequestAnimationFrame(a):b.webkitRequestAnimationFrame?b.webkitRequestAnimationFrame(a):b.mozRequestAnimationFrame?b.mozRequestAnimationFrame(a):b.oRequestAnimationFrame?b.oRequestAnimationFrame(a):window.setTimeout(a,16)):"undefined"!=typeof requestAnimationFrame?requestAnimationFrame(a):setTimeout(a,16)},a.prototype.getHostDocument=function(){return this._renderingCanvas&&this._renderingCanvas.ownerDocument?this._renderingCanvas.ownerDocument:document},a.prototype.getFontOffset=function(a){var b=document.createElement("span");b.innerHTML="Hg",b.setAttribute("style","font: "+a+" !important");a=document.createElement("div");a.style.display="inline-block",a.style.width="1px",a.style.height="0px",a.style.verticalAlign="bottom";var c=document.createElement("div");c.style.whiteSpace="nowrap",c.appendChild(b),c.appendChild(a),document.body.appendChild(c);var d=0,e=0;try{e=a.getBoundingClientRect().top-b.getBoundingClientRect().top,a.style.verticalAlign="baseline",d=a.getBoundingClientRect().top-b.getBoundingClientRect().top}finally{document.body.removeChild(c)}return{ascent:d,height:e,descent:e-d}},a.ExceptionList=[{key:"Chrome/63.0",capture:"63\.0\.3239\.(\d+)",captureConstraint:108,targets:["uniformBuffer"]},{key:"Firefox/58",capture:null,captureConstraint:null,targets:["uniformBuffer"]},{key:"Firefox/59",capture:null,captureConstraint:null,targets:["uniformBuffer"]},{key:"Chrome/72.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Chrome/73.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Chrome/74.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Mac OS.+Chrome/71",capture:null,captureConstraint:null,targets:["vao"]},{key:"Mac OS.+Chrome/72",capture:null,captureConstraint:null,targets:["vao"]}],a._TextureLoaders=[],a.CollisionsEpsilon=.001,a._IsSupported=null,a._HasMajorPerformanceCaveat=null,a}()},function(a,b,c){c.d(b,"a",function(){return q});var d=c(2),e=c(3),f=c(44),g=c(12),h=c(6),i=c(0),j=c(30),k=c(7),l=c(10),m=c(26),n=c(61),o=c(96),p=c(1),q=function(a){function b(c,d,e,g){void 0===g&&(g=!0);c=a.call(this,c,e)||this;return c._position=i.e.Zero(),c._upVector=i.e.Up(),c.orthoLeft=null,c.orthoRight=null,c.orthoBottom=null,c.orthoTop=null,c.fov=.8,c.projectionPlaneTilt=0,c.minZ=1,c.maxZ=1e4,c.inertia=.9,c.mode=b.PERSPECTIVE_CAMERA,c.isIntermediate=!1,c.viewport=new n.a(0,0,1,1),c.layerMask=268435455,c.fovMode=b.FOVMODE_VERTICAL_FIXED,c.cameraRigMode=b.RIG_MODE_NONE,c.customRenderTargets=new Array,c.outputRenderTarget=null,c.onViewMatrixChangedObservable=new h.c,c.onProjectionMatrixChangedObservable=new h.c,c.onAfterCheckInputsObservable=new h.c,c.onRestoreStateObservable=new h.c,c.isRigCamera=!1,c._rigCameras=new Array,c._webvrViewMatrix=i.a.Identity(),c._skipRendering=!1,c._projectionMatrix=new i.a,c._postProcesses=new Array,c._activeMeshes=new f.a(256),c._globalPosition=i.e.Zero(),c._computedViewMatrix=i.a.Identity(),c._doNotComputeProjectionMatrix=!1,c._transformMatrix=i.a.Zero(),c._refreshFrustumPlanes=!0,c._absoluteRotation=i.b.Identity(),c._isCamera=!0,c._isLeftCamera=!1,c._isRightCamera=!1,c.getScene().addCamera(c),g&&!c.getScene().activeCamera&&(c.getScene().activeCamera=c),c.position=d,c}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this._position=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"upVector",{get:function(){return this._upVector},set:function(a){this._upVector=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"screenArea",{get:function(){var a,c=0,d=0;if(this.mode===b.PERSPECTIVE_CAMERA)this.fovMode===b.FOVMODE_VERTICAL_FIXED?(d=2*this.minZ*Math.tan(this.fov/2),c=this.getEngine().getAspectRatio(this)*d):d=(c=2*this.minZ*Math.tan(this.fov/2))/this.getEngine().getAspectRatio(this);else{var e=this.getEngine().getRenderWidth()/2,f=this.getEngine().getRenderHeight()/2;c=(null!==(a=this.orthoRight)&&void 0!==a?a:e)-(null!==(a=this.orthoLeft)&&void 0!==a?a:-e),d=(null!==(a=this.orthoTop)&&void 0!==a?a:f)-(null!==(e=this.orthoBottom)&&void 0!==e?e:-f)}return c*d},enumerable:!1,configurable:!0}),b.prototype.storeState=function(){return this._stateStored=!0,this._storedFov=this.fov,this},b.prototype._restoreStateValues=function(){return!!this._stateStored&&(this.fov=this._storedFov,!0)},b.prototype.restoreState=function(){return!!this._restoreStateValues()&&(this.onRestoreStateObservable.notifyObservers(this),!0)},b.prototype.getClassName=function(){return"Camera"},b.prototype.toString=function(a){var b="Name: "+this.name;if(b+=", type: "+this.getClassName(),this.animations)for(var c=0;c-1?(k.a.Error("You"re trying to reuse a post process not defined as reusable."),0):(null==b||b<0?this._postProcesses.push(a):null===this._postProcesses[b]?this._postProcesses[b]=a:this._postProcesses.splice(b,0,a),this._cascadePostProcessesToRigCams(),this._scene.prePassRenderer&&this._scene.prePassRenderer.markAsDirty(),this._postProcesses.indexOf(a))},b.prototype.detachPostProcess=function(a){a=this._postProcesses.indexOf(a);-1!==a&&(this._postProcesses[a]=null),this._scene.prePassRenderer&&this._scene.prePassRenderer.markAsDirty(),this._cascadePostProcessesToRigCams()},b.prototype.getWorldMatrix=function(){return this._isSynchronizedViewMatrix()||this.getViewMatrix(),this._worldMatrix},b.prototype._getViewMatrix=function(){return i.a.Identity()},b.prototype.getViewMatrix=function(a){return!a&&this._isSynchronizedViewMatrix()||(this.updateCache(),this._computedViewMatrix=this._getViewMatrix(),this._currentRenderId=this.getScene().getRenderId(),this._childUpdateId++,this._refreshFrustumPlanes=!0,this._cameraRigParams&&this._cameraRigParams.vrPreViewMatrix&&this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix,this._computedViewMatrix),this.parent&&this.parent.onViewMatrixChangedObservable&&this.parent.onViewMatrixChangedObservable.notifyObservers(this.parent),this.onViewMatrixChangedObservable.notifyObservers(this),this._computedViewMatrix.invertToRef(this._worldMatrix)),this._computedViewMatrix},b.prototype.freezeProjectionMatrix=function(a){this._doNotComputeProjectionMatrix=!0,void 0!==a&&(this._projectionMatrix=a)},b.prototype.unfreezeProjectionMatrix=function(){this._doNotComputeProjectionMatrix=!1},b.prototype.getProjectionMatrix=function(a){if(this._doNotComputeProjectionMatrix||!a&&this._isSynchronizedProjectionMatrix())return this._projectionMatrix;this._cache.mode=this.mode,this._cache.minZ=this.minZ,this._cache.maxZ=this.maxZ,this._refreshFrustumPlanes=!0;a=this.getEngine();var c=this.getScene();if(this.mode===b.PERSPECTIVE_CAMERA){this._cache.fov=this.fov,this._cache.fovMode=this.fovMode,this._cache.aspectRatio=a.getAspectRatio(this),this._cache.projectionPlaneTilt=this.projectionPlaneTilt,this.minZ<=0&&(this.minZ=.1);var d=a.useReverseDepthBuffer;(c.useRightHandedSystem?i.a.PerspectiveFovRHToRef:i.a.PerspectiveFovLHToRef)(this.fov,a.getAspectRatio(this),d?this.maxZ:this.minZ,d?this.minZ:this.maxZ,this._projectionMatrix,this.fovMode===b.FOVMODE_VERTICAL_FIXED,a.isNDCHalfZRange,this.projectionPlaneTilt)}else{d=a.getRenderWidth()/2;var e=a.getRenderHeight()/2;c.useRightHandedSystem?i.a.OrthoOffCenterRHToRef(null!==(c=this.orthoLeft)&&void 0!==c?c:-d,null!==(c=this.orthoRight)&&void 0!==c?c:d,null!==(c=this.orthoBottom)&&void 0!==c?c:-e,null!==(c=this.orthoTop)&&void 0!==c?c:e,this.minZ,this.maxZ,this._projectionMatrix,a.isNDCHalfZRange):i.a.OrthoOffCenterLHToRef(null!==(c=this.orthoLeft)&&void 0!==c?c:-d,null!==(c=this.orthoRight)&&void 0!==c?c:d,null!==(c=this.orthoBottom)&&void 0!==c?c:-e,null!==(d=this.orthoTop)&&void 0!==d?d:e,this.minZ,this.maxZ,this._projectionMatrix,a.isNDCHalfZRange),this._cache.orthoLeft=this.orthoLeft,this._cache.orthoRight=this.orthoRight,this._cache.orthoBottom=this.orthoBottom,this._cache.orthoTop=this.orthoTop,this._cache.renderWidth=a.getRenderWidth(),this._cache.renderHeight=a.getRenderHeight()}return this.onProjectionMatrixChangedObservable.notifyObservers(this),this._projectionMatrix},b.prototype.getTransformationMatrix=function(){return this._computedViewMatrix.multiplyToRef(this._projectionMatrix,this._transformMatrix),this._transformMatrix},b.prototype._updateFrustumPlanes=function(){this._refreshFrustumPlanes&&(this.getTransformationMatrix(),this._frustumPlanes?o.a.GetPlanesToRef(this._transformMatrix,this._frustumPlanes):this._frustumPlanes=o.a.GetPlanes(this._transformMatrix),this._refreshFrustumPlanes=!1)},b.prototype.isInFrustum=function(a,b){if(void 0===b&&(b=!1),this._updateFrustumPlanes(),b&&this.rigCameras.length>0){var c=!1;return this.rigCameras.forEach(function(b){b._updateFrustumPlanes(),c=c||a.isInFrustum(b._frustumPlanes)}),c}return a.isInFrustum(this._frustumPlanes)},b.prototype.isCompletelyInFrustum=function(a){return this._updateFrustumPlanes(),a.isCompletelyInFrustum(this._frustumPlanes)},b.prototype.getForwardRay=function(a,b,c){throw Object(m.a)("Ray")},b.prototype.getForwardRayToRef=function(a,b,c,d){throw Object(m.a)("Ray")},b.prototype.dispose=function(c,d){for(void 0===d&&(d=!1),this.onViewMatrixChangedObservable.clear(),this.onProjectionMatrixChangedObservable.clear(),this.onAfterCheckInputsObservable.clear(),this.onRestoreStateObservable.clear(),this.inputs&&this.inputs.clear(),this.getScene().stopAnimation(this),this.getScene().removeCamera(this);this._rigCameras.length>0;){var e=this._rigCameras.pop();e&&e.dispose()}if(this._parentContainer){e=this._parentContainer.cameras.indexOf(this);e>-1&&this._parentContainer.cameras.splice(e,1),this._parentContainer=null}if(this._rigPostProcess)this._rigPostProcess.dispose(this),this._rigPostProcess=null,this._postProcesses=[];else if(this.cameraRigMode!==b.RIG_MODE_NONE)this._rigPostProcess=null,this._postProcesses=[];else for(e=this._postProcesses.length;--e>=0;){var f=this._postProcesses[e];f&&f.dispose(this)}for(e=this.customRenderTargets.length;--e>=0;)this.customRenderTargets[e].dispose();this.customRenderTargets=[],this._activeMeshes.dispose(),a.prototype.dispose.call(this,c,d)},Object.defineProperty(b.prototype,"isLeftCamera",{get:function(){return this._isLeftCamera},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isRightCamera",{get:function(){return this._isRightCamera},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"leftCamera",{get:function(){return this._rigCameras.length<1?null:this._rigCameras[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightCamera",{get:function(){return this._rigCameras.length<2?null:this._rigCameras[1]},enumerable:!1,configurable:!0}),b.prototype.getLeftTarget=function(){return this._rigCameras.length<1?null:this._rigCameras[0].getTarget()},b.prototype.getRightTarget=function(){return this._rigCameras.length<2?null:this._rigCameras[1].getTarget()},b.prototype.setCameraRigMode=function(a,c){if(this.cameraRigMode!==a){for(;this._rigCameras.length>0;){var d=this._rigCameras.pop();d&&d.dispose()}if(this.cameraRigMode=a,this._cameraRigParams={},this._cameraRigParams.interaxialDistance=c.interaxialDistance||.0637,this._cameraRigParams.stereoHalfAngle=g.b.ToRadians(this._cameraRigParams.interaxialDistance/.0637),this.cameraRigMode!==b.RIG_MODE_NONE){d=this.createRigCamera(this.name+"_L",0);d&&(d._isLeftCamera=!0);a=this.createRigCamera(this.name+"_R",1);a&&(a._isRightCamera=!0),d&&a&&(this._rigCameras.push(d),this._rigCameras.push(a))}this._setRigMode(c),this._cascadePostProcessesToRigCams(),this.update()}},b.prototype._setRigMode=function(a){},b.prototype._getVRProjectionMatrix=function(){return i.a.PerspectiveFovLHToRef(this._cameraRigParams.vrMetrics.aspectRatioFov,this._cameraRigParams.vrMetrics.aspectRatio,this.minZ,this.maxZ,this._cameraRigParams.vrWorkMatrix,!0,this.getEngine().isNDCHalfZRange),this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix,this._projectionMatrix),this._projectionMatrix},b.prototype._updateCameraRotationMatrix=function(){},b.prototype._updateWebVRCameraRotationMatrix=function(){},b.prototype._getWebVRProjectionMatrix=function(){return i.a.Identity()},b.prototype._getWebVRViewMatrix=function(){return i.a.Identity()},b.prototype.setCameraRigParameter=function(a,b){this._cameraRigParams||(this._cameraRigParams={}),this._cameraRigParams[a]=b,"interaxialDistance"===a&&(this._cameraRigParams.stereoHalfAngle=g.b.ToRadians(b/.0637))},b.prototype.createRigCamera=function(a,b){return null},b.prototype._updateRigCameras=function(){for(var a=0;a=1)&&(this.needAlphaBlending()||a.visibility<1||a.hasVertexAlpha)},a.prototype.needAlphaTesting=function(){return!!this._forceAlphaTest},a.prototype._shouldTurnAlphaTestOn=function(a){return!this.needAlphaBlendingForMesh(a)&&this.needAlphaTesting()},a.prototype.getAlphaTestTexture=function(){return null},a.prototype.markDirty=function(){for(var a=0,b=this.getScene().meshes;a-1&&this._parentContainer.materials.splice(d,1),this._parentContainer=null}if(!0!==c)if(this.meshMap)for(d in this.meshMap)(c=this.meshMap[d])&&(c.material=null,this.releaseVertexArrayObject(c,a));else for(d=0,b=b.meshes;d100&&(this.soft=!0),this._physicsEngine=this._scene.getPhysicsEngine(),this._physicsEngine?(this.object.rotationQuaternion||(this.object.rotation?this.object.rotationQuaternion=f.b.RotationYawPitchRoll(this.object.rotation.y,this.object.rotation.x,this.object.rotation.z):this.object.rotationQuaternion=new f.b),this._options.mass=void 0===c.mass?0:c.mass,this._options.friction=void 0===c.friction?.2:c.friction,this._options.restitution=void 0===c.restitution?.2:c.restitution,this.soft&&(this._options.mass=this._options.mass>0?this._options.mass:1,this._options.pressure=void 0===c.pressure?200:c.pressure,this._options.stiffness=void 0===c.stiffness?1:c.stiffness,this._options.velocityIterations=void 0===c.velocityIterations?20:c.velocityIterations,this._options.positionIterations=void 0===c.positionIterations?20:c.positionIterations,this._options.fixedPoints=void 0===c.fixedPoints?0:c.fixedPoints,this._options.margin=void 0===c.margin?0:c.margin,this._options.damping=void 0===c.damping?0:c.damping,this._options.path=void 0===c.path?null:c.path,this._options.shape=void 0===c.shape?null:c.shape),this._joints=[],!this.object.parent||this._options.ignoreParent?this._init():this.object.parent.physicsImpostor&&d.a.Warn("You must affect impostors to children before affecting impostor to parent.")):d.a.Error("Physics not enabled. Please use scene.enablePhysics(...) before creating impostors."))):d.a.Error("No object was provided. A physics object is obligatory")}return Object.defineProperty(a.prototype,"isDisposed",{get:function(){return this._isDisposed},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mass",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyMass(this):0},set:function(a){this.setMass(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"friction",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyFriction(this):0},set:function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyFriction(this,a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"restitution",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyRestitution(this):0},set:function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyRestitution(this,a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"pressure",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.setBodyPressure?a.getBodyPressure(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyPressure&&b.setBodyPressure(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stiffness",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyStiffness?a.getBodyStiffness(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyStiffness&&b.setBodyStiffness(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"velocityIterations",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyVelocityIterations?a.getBodyVelocityIterations(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyVelocityIterations&&b.setBodyVelocityIterations(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"positionIterations",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyPositionIterations?a.getBodyPositionIterations(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyPositionIterations&&b.setBodyPositionIterations(this,a)}},enumerable:!1,configurable:!0}),a.prototype._init=function(){this._physicsEngine&&(this._physicsEngine.removeImpostor(this),this.physicsBody=null,this._parent=this._parent||this._getPhysicsParent(),this._isDisposed||this.parent&&!this._options.ignoreParent||this._physicsEngine.addImpostor(this))},a.prototype._getPhysicsParent=function(){return this.object.parent instanceof g.a?this.object.parent.physicsImpostor:null},a.prototype.isBodyInitRequired=function(){return this._bodyUpdateRequired||!this._physicsBody&&!this._parent},a.prototype.setScalingUpdated=function(){this.forceUpdate()},a.prototype.forceUpdate=function(){this._init(),this.parent&&!this._options.ignoreParent&&this.parent.forceUpdate()},Object.defineProperty(a.prototype,"physicsBody",{get:function(){return this._parent&&!this._options.ignoreParent?this._parent.physicsBody:this._physicsBody},set:function(a){this._physicsBody&&this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().removePhysicsBody(this),this._physicsBody=a,this.resetUpdateFlags()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"parent",{get:function(){return!this._options.ignoreParent&&this._parent?this._parent:null},set:function(a){this._parent=a},enumerable:!1,configurable:!0}),a.prototype.resetUpdateFlags=function(){this._bodyUpdateRequired=!1},a.prototype.getObjectExtendSize=function(){if(this.object.getBoundingInfo){var b=this.object.rotationQuaternion,c=this.object.scaling.clone();this.object.rotationQuaternion=a.IDENTITY_QUATERNION;var d=this.object.computeWorldMatrix&&this.object.computeWorldMatrix(!0);d&&d.decompose(c,void 0,void 0);d=this.object.getBoundingInfo().boundingBox.extendSize.scale(2).multiplyInPlace(c);return d.x=Math.abs(d.x),d.y=Math.abs(d.y),d.z=Math.abs(d.z),this.object.rotationQuaternion=b,this.object.computeWorldMatrix&&this.object.computeWorldMatrix(!0),d}return a.DEFAULT_OBJECT_SIZE},a.prototype.getObjectCenter=function(){return this.object.getBoundingInfo?this.object.getBoundingInfo().boundingBox.centerWorld:this.object.position},a.prototype.getParam=function(a){return this._options[a]},a.prototype.setParam=function(a,b){this._options[a]=b,this._bodyUpdateRequired=!0},a.prototype.setMass=function(a){this.getParam("mass")!==a&&this.setParam("mass",a),this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyMass(this,a)},a.prototype.getLinearVelocity=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getLinearVelocity(this):f.e.Zero()},a.prototype.setLinearVelocity=function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setLinearVelocity(this,a)},a.prototype.getAngularVelocity=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getAngularVelocity(this):f.e.Zero()},a.prototype.setAngularVelocity=function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setAngularVelocity(this,a)},a.prototype.executeNativeFunction=function(a){this._physicsEngine&&a(this._physicsEngine.getPhysicsPlugin().world,this.physicsBody)},a.prototype.registerBeforePhysicsStep=function(a){this._onBeforePhysicsStepCallbacks.push(a)},a.prototype.unregisterBeforePhysicsStep=function(a){a=this._onBeforePhysicsStepCallbacks.indexOf(a);a>-1?this._onBeforePhysicsStepCallbacks.splice(a,1):d.a.Warn("Function to remove was not found")},a.prototype.registerAfterPhysicsStep=function(a){this._onAfterPhysicsStepCallbacks.push(a)},a.prototype.unregisterAfterPhysicsStep=function(a){a=this._onAfterPhysicsStepCallbacks.indexOf(a);a>-1?this._onAfterPhysicsStepCallbacks.splice(a,1):d.a.Warn("Function to remove was not found")},a.prototype.registerOnPhysicsCollide=function(a,b){a=a instanceof Array?a:[a];this._onPhysicsCollideCallbacks.push({callback:b,otherImpostors:a})},a.prototype.unregisterOnPhysicsCollide=function(a,b){var c=a instanceof Array?a:[a],e=-1;this._onPhysicsCollideCallbacks.some(function(a,d){if(a.callback===b&&a.otherImpostors.length===c.length){a=a.otherImpostors.every(function(a){return c.indexOf(a)>-1});return a&&(e=d),a}return!1})?this._onPhysicsCollideCallbacks.splice(e,1):d.a.Warn("Function to remove was not found")},a.prototype.getParentsRotation=function(){var a=this.object.parent;for(this._tmpQuat.copyFromFloats(0,0,0,1);a;)a.rotationQuaternion?this._tmpQuat2.copyFrom(a.rotationQuaternion):f.b.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,a.rotation.z,this._tmpQuat2),this._tmpQuat.multiplyToRef(this._tmpQuat2,this._tmpQuat),a=a.parent;return this._tmpQuat},a.prototype.applyForce=function(a,b){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().applyForce(this,a,b),this},a.prototype.applyImpulse=function(a,b){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().applyImpulse(this,a,b),this},a.prototype.createJoint=function(a,b,c){b=new h.e(b,c);return this.addJoint(a,b),this},a.prototype.addJoint=function(a,b){return this._joints.push({otherImpostor:a,joint:b}),this._physicsEngine&&this._physicsEngine.addJoint(this,a,b),this},a.prototype.addAnchor=function(a,b,c,d,e){if(!this._physicsEngine)return this;var f=this._physicsEngine.getPhysicsPlugin();return f.appendAnchor?(this._physicsEngine&&f.appendAnchor(this,a,b,c,d,e),this):this},a.prototype.addHook=function(a,b,c,d){if(!this._physicsEngine)return this;var e=this._physicsEngine.getPhysicsPlugin();return e.appendAnchor?(this._physicsEngine&&e.appendHook(this,a,b,c,d),this):this},a.prototype.sleep=function(){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().sleepBody(this),this},a.prototype.wakeUp=function(){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().wakeUpBody(this),this},a.prototype.clone=function(b){return b?new a(b,this.type,this._options,this._scene):null},a.prototype.dispose=function(){var a=this;this._physicsEngine&&(this._joints.forEach(function(b){a._physicsEngine&&a._physicsEngine.removeJoint(a,b.otherImpostor,b.joint)}),this._physicsEngine.removeImpostor(this),this.parent&&this.parent.forceUpdate(),this._isDisposed=!0)},a.prototype.setDeltaPosition=function(a){this._deltaPosition.copyFrom(a)},a.prototype.setDeltaRotation=function(a){this._deltaRotation||(this._deltaRotation=new f.b),this._deltaRotation.copyFrom(a),this._deltaRotationConjugated=this._deltaRotation.conjugate()},a.prototype.getBoxSizeToRef=function(a){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().getBoxSizeToRef(this,a),this},a.prototype.getRadius=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getRadius(this):0},a.prototype.syncBoneWithImpostor=function(b,c,d,e,f){var g=a._tmpVecs[0],j=this.object;if(j.rotationQuaternion)if(f){var h=a._tmpQuat;j.rotationQuaternion.multiplyToRef(f,h),b.setRotationQuaternion(h,i.c.WORLD,c)}else b.setRotationQuaternion(j.rotationQuaternion,i.c.WORLD,c);g.x=0,g.y=0,g.z=0,d&&(g.x=d.x,g.y=d.y,g.z=d.z,b.getDirectionToRef(g,c,g),null==e&&(e=d.length()),g.x*=e,g.y*=e,g.z*=e),b.getParent()?(g.addInPlace(j.getAbsolutePosition()),b.setAbsolutePosition(g,c)):(c.setAbsolutePosition(j.getAbsolutePosition()),c.position.x-=g.x,c.position.y-=g.y,c.position.z-=g.z)},a.prototype.syncImpostorWithBone=function(b,c,d,e,f,g){var j=this.object;if(j.rotationQuaternion)if(f){var h=a._tmpQuat;b.getRotationQuaternionToRef(i.c.WORLD,c,h),h.multiplyToRef(f,j.rotationQuaternion)}else b.getRotationQuaternionToRef(i.c.WORLD,c,j.rotationQuaternion);h=a._tmpVecs[0];f=a._tmpVecs[1];g||((g=a._tmpVecs[2]).x=0,g.y=1,g.z=0),b.getDirectionToRef(g,c,f),b.getAbsolutePositionToRef(c,h),null==e&&d&&(e=d.length()),null!=e&&(h.x+=f.x*e,h.y+=f.y*e,h.z+=f.z*e),j.setAbsolutePosition(h)},a.DEFAULT_OBJECT_SIZE=new f.e(1,1,1),a.IDENTITY_QUATERNION=f.b.Identity(),a._tmpVecs=e.a.BuildArray(3,f.e.Zero),a._tmpQuat=f.b.Identity(),a.NoImpostor=0,a.SphereImpostor=1,a.BoxImpostor=2,a.PlaneImpostor=3,a.MeshImpostor=4,a.CapsuleImpostor=6,a.CylinderImpostor=7,a.ParticleImpostor=8,a.HeightmapImpostor=9,a.ConvexHullImpostor=10,a.CustomImpostor=100,a.RopeImpostor=101,a.ClothImpostor=102,a.SoftbodyImpostor=103,a}()},function(a,b,c){c.d(b,"b",function(){return x}),c.d(b,"a",function(){return y});var d=c(2),e=c(3),f=c(44),g=c(21),h=c(0),i=c(8),j=c(4),k=c(119),l=c(49),m=c(29);a=c(83);b=c(94);var n=c(15),o=c(11),p=c(10),q=c(20),r=c(5),s="uniform vec4 vEyePosition; uniform vec4 vDiffuseColor; #ifdef SPECULARTERM uniform vec4 vSpecularColor; #endif uniform vec3 vEmissiveColor; uniform vec3 vAmbientColor; uniform float visibility; #ifdef DIFFUSE uniform vec2 vDiffuseInfos; #endif #ifdef AMBIENT uniform vec2 vAmbientInfos; #endif #ifdef OPACITY uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #ifdef ALPHATEST uniform float alphaCutOff; #endif #if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(REFRACTION) uniform mat4 view; #endif #ifdef REFRACTION uniform vec4 vRefractionInfos; #ifndef REFRACTIONMAP_3D uniform mat4 refractionMatrix; #endif #ifdef REFRACTIONFRESNEL uniform vec4 refractionLeftColor; uniform vec4 refractionRightColor; #endif #if defined(USE_LOCAL_REFRACTIONMAP_CUBIC) && defined(REFRACTIONMAP_3D) uniform vec3 vRefractionPosition; uniform vec3 vRefractionSize; #endif #endif #if defined(SPECULAR) && defined(SPECULARTERM) uniform vec2 vSpecularInfos; #endif #ifdef DIFFUSEFRESNEL uniform vec4 diffuseLeftColor; uniform vec4 diffuseRightColor; #endif #ifdef OPACITYFRESNEL uniform vec4 opacityParts; #endif #ifdef EMISSIVEFRESNEL uniform vec4 emissiveLeftColor; uniform vec4 emissiveRightColor; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; #if defined(REFLECTIONMAP_PLANAR) || defined(REFLECTIONMAP_CUBIC) || defined(REFLECTIONMAP_PROJECTION) || defined(REFLECTIONMAP_EQUIRECTANGULAR) || defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_SKYBOX) uniform mat4 reflectionMatrix; #endif #ifndef REFLECTIONMAP_SKYBOX #if defined(USE_LOCAL_REFLECTIONMAP_CUBIC) && defined(REFLECTIONMAP_CUBIC) uniform vec3 vReflectionPosition; uniform vec3 vReflectionSize; #endif #endif #ifdef REFLECTIONFRESNEL uniform vec4 reflectionLeftColor; uniform vec4 reflectionRightColor; #endif #endif #ifdef DETAIL uniform vec4 vDetailInfos; #endif";r.a.IncludesShadersStore.defaultFragmentDeclaration=s;c(142),c(180);s="layout(std140,column_major) uniform; uniform Material { vec4 diffuseLeftColor; vec4 diffuseRightColor; vec4 opacityParts; vec4 reflectionLeftColor; vec4 reflectionRightColor; vec4 refractionLeftColor; vec4 refractionRightColor; vec4 emissiveLeftColor; vec4 emissiveRightColor; vec2 vDiffuseInfos; vec2 vAmbientInfos; vec2 vOpacityInfos; vec2 vReflectionInfos; vec3 vReflectionPosition; vec3 vReflectionSize; vec2 vEmissiveInfos; vec2 vLightmapInfos; vec2 vSpecularInfos; vec3 vBumpInfos; mat4 diffuseMatrix; mat4 ambientMatrix; mat4 opacityMatrix; mat4 reflectionMatrix; mat4 emissiveMatrix; mat4 lightmapMatrix; mat4 specularMatrix; mat4 bumpMatrix; vec2 vTangentSpaceParams; float pointSize; float alphaCutOff; mat4 refractionMatrix; vec4 vRefractionInfos; vec3 vRefractionPosition; vec3 vRefractionSize; vec4 vSpecularColor; vec3 vEmissiveColor; vec4 vDiffuseColor; vec3 vAmbientColor; vec4 vDetailInfos; mat4 detailMatrix; }; #include #include ";r.a.IncludesShadersStore.defaultUboDeclaration=s;c(190),c(191),c(149),c(64),c(150),c(151),c(181),c(152),c(182),c(192),c(153),c(120),c(121),c(154),c(155),c(122),c(127),c(143),c(193),c(109),c(156),c(194),c(157),c(183),c(158);s="#include<__decl__defaultFragment> #if defined(BUMP) || !defined(NORMAL) #extension GL_OES_standard_derivatives : enable #endif #include[SCENE_MRT_COUNT] #include #define CUSTOM_FRAGMENT_BEGIN #ifdef LOGARITHMICDEPTH #extension GL_EXT_frag_depth : enable #endif #define RECIPROCAL_PI2 0.15915494 varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include[1..7] #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse,_SAMPLERNAME_,diffuse) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_SAMPLERNAME_,lightmap) #ifdef REFRACTION #ifdef REFRACTIONMAP_3D uniform samplerCube refractionCubeSampler; #else uniform sampler2D refraction2DSampler; #endif #endif #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular,_SAMPLERNAME_,specular) #endif #include #ifdef REFLECTION #ifdef REFLECTIONMAP_3D uniform samplerCube reflectionCubeSampler; #else uniform sampler2D reflection2DSampler; #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #include #endif #include #include #include #include #include #include #include #define CUSTOM_FRAGMENT_DEFINITIONS void main(void) { #define CUSTOM_FRAGMENT_MAIN_BEGIN #include #include vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); vec4 baseColor=vec4(1.,1.,1.,1.); vec3 diffuseColor=vDiffuseColor.rgb; float alpha=vDiffuseColor.a; #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=normalize(-cross(dFdx(vPositionW),dFdy(vPositionW))); #endif #include #ifdef TWOSIDEDLIGHTING normalW=gl_FrontFacing ? normalW : -normalW; #endif #ifdef DIFFUSE baseColor=texture2D(diffuseSampler,vDiffuseUV+uvOffset); #if defined(ALPHATEST) && !defined(ALPHATEST_AFTERALLALPHACOMPUTATIONS) if (baseColor.a #ifdef VERTEXCOLOR baseColor.rgb*=vColor.rgb; #endif #ifdef DETAIL baseColor.rgb=baseColor.rgb*2.0*mix(0.5,detailColor.r,vDetailInfos.y); #endif #define CUSTOM_FRAGMENT_UPDATE_DIFFUSE vec3 baseAmbientColor=vec3(1.,1.,1.); #ifdef AMBIENT baseAmbientColor=texture2D(ambientSampler,vAmbientUV+uvOffset).rgb*vAmbientInfos.y; #endif #define CUSTOM_FRAGMENT_BEFORE_LIGHTS #ifdef SPECULARTERM float glossiness=vSpecularColor.a; vec3 specularColor=vSpecularColor.rgb; #ifdef SPECULAR vec4 specularMapColor=texture2D(specularSampler,vSpecularUV+uvOffset); specularColor=specularMapColor.rgb; #ifdef GLOSSINESS glossiness=glossiness*specularMapColor.a; #endif #endif #else float glossiness=0.; #endif vec3 diffuseBase=vec3(0.,0.,0.); lightingInfo info; #ifdef SPECULARTERM vec3 specularBase=vec3(0.,0.,0.); #endif float shadow=1.; #ifdef LIGHTMAP vec4 lightmapColor=texture2D(lightmapSampler,vLightmapUV+uvOffset); #ifdef RGBDLIGHTMAP lightmapColor.rgb=fromRGBD(lightmapColor); #endif lightmapColor.rgb*=vLightmapInfos.y; #endif #include[0..maxSimultaneousLights] vec4 refractionColor=vec4(0.,0.,0.,1.); #ifdef REFRACTION vec3 refractionVector=normalize(refract(-viewDirectionW,normalW,vRefractionInfos.y)); #ifdef REFRACTIONMAP_3D #ifdef USE_LOCAL_REFRACTIONMAP_CUBIC refractionVector=parallaxCorrectNormal(vPositionW,refractionVector,vRefractionSize,vRefractionPosition); #endif refractionVector.y=refractionVector.y*vRefractionInfos.w; if (dot(refractionVector,viewDirectionW)<1.0) { refractionColor=textureCube(refractionCubeSampler,refractionVector); } #else vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*vRefractionInfos.z,1.0))); vec2 refractionCoords=vRefractionUVW.xy/vRefractionUVW.z; refractionCoords.y=1.0-refractionCoords.y; refractionColor=texture2D(refraction2DSampler,refractionCoords); #endif #ifdef RGBDREFRACTION refractionColor.rgb=fromRGBD(refractionColor); #endif #ifdef IS_REFRACTION_LINEAR refractionColor.rgb=toGammaSpace(refractionColor.rgb); #endif refractionColor.rgb*=vRefractionInfos.x; #endif vec4 reflectionColor=vec4(0.,0.,0.,1.); #ifdef REFLECTION vec3 vReflectionUVW=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #ifdef REFLECTIONMAP_OPPOSITEZ vReflectionUVW.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D #ifdef ROUGHNESS float bias=vReflectionInfos.y; #ifdef SPECULARTERM #ifdef SPECULAR #ifdef GLOSSINESS bias*=(1.0-specularMapColor.a); #endif #endif #endif reflectionColor=textureCube(reflectionCubeSampler,vReflectionUVW,bias); #else reflectionColor=textureCube(reflectionCubeSampler,vReflectionUVW); #endif #else vec2 coords=vReflectionUVW.xy; #ifdef REFLECTIONMAP_PROJECTION coords/=vReflectionUVW.z; #endif coords.y=1.0-coords.y; reflectionColor=texture2D(reflection2DSampler,coords); #endif #ifdef RGBDREFLECTION reflectionColor.rgb=fromRGBD(reflectionColor); #endif #ifdef IS_REFLECTION_LINEAR reflectionColor.rgb=toGammaSpace(reflectionColor.rgb); #endif reflectionColor.rgb*=vReflectionInfos.x; #ifdef REFLECTIONFRESNEL float reflectionFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,reflectionRightColor.a,reflectionLeftColor.a); #ifdef REFLECTIONFRESNELFROMSPECULAR #ifdef SPECULARTERM reflectionColor.rgb*=specularColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #else reflectionColor.rgb*=reflectionLeftColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #endif #else reflectionColor.rgb*=reflectionLeftColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #endif #endif #endif #ifdef REFRACTIONFRESNEL float refractionFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,refractionRightColor.a,refractionLeftColor.a); refractionColor.rgb*=refractionLeftColor.rgb*(1.0-refractionFresnelTerm)+refractionFresnelTerm*refractionRightColor.rgb; #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vOpacityUV+uvOffset); #ifdef OPACITYRGB opacityMap.rgb=opacityMap.rgb*vec3(0.3,0.59,0.11); alpha*=(opacityMap.x+opacityMap.y+opacityMap.z)* vOpacityInfos.y; #else alpha*=opacityMap.a*vOpacityInfos.y; #endif #endif #ifdef VERTEXALPHA alpha*=vColor.a; #endif #ifdef OPACITYFRESNEL float opacityFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,opacityParts.z,opacityParts.w); alpha+=opacityParts.x*(1.0-opacityFresnelTerm)+opacityFresnelTerm*opacityParts.y; #endif #ifdef ALPHATEST #ifdef ALPHATEST_AFTERALLALPHACOMPUTATIONS if (alpha #include #ifdef IMAGEPROCESSINGPOSTPROCESS color.rgb=toLinearSpace(color.rgb); #else #ifdef IMAGEPROCESSING color.rgb=toLinearSpace(color.rgb); color=applyImageProcessing(color); #endif #endif color.a*=visibility; #ifdef PREMULTIPLYALPHA color.rgb*=color.a; #endif #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR #ifdef PREPASS float writeGeometryInfo=color.a>0.4 ? 1.0 : 0.0; gl_FragData[0]=color; #ifdef PREPASS_POSITION gl_FragData[PREPASS_POSITION_INDEX]=vec4(vPositionW,writeGeometryInfo); #endif #ifdef PREPASS_VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[PREPASS_VELOCITY_INDEX]=vec4(velocity,0.0,writeGeometryInfo); #endif #ifdef PREPASS_IRRADIANCE gl_FragData[PREPASS_IRRADIANCE_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_DEPTH gl_FragData[PREPASS_DEPTH_INDEX]=vec4(vViewPos.z,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_NORMAL gl_FragData[PREPASS_NORMAL_INDEX]=vec4((view*vec4(normalW,0.0)).rgb,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT gl_FragData[PREPASS_ALBEDO_SQRT_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_REFLECTIVITY #if defined(SPECULAR) gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(specularMapColor.rgb,writeGeometryInfo); #else gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #endif #endif #if !defined(PREPASS) || defined(WEBGL2) gl_FragColor=color; #endif #if ORDER_INDEPENDENT_TRANSPARENCY if (fragDepth == nearestDepth) { frontColor.rgb+=color.rgb*color.a*alphaMultiplier; frontColor.a=1.0-alphaMultiplier*(1.0-color.a); } else { backColor+=color; } #endif } ";r.a.ShadersStore.defaultPixelShader=s;s=" uniform mat4 viewProjection; uniform mat4 view; #ifdef DIFFUSE uniform mat4 diffuseMatrix; uniform vec2 vDiffuseInfos; #endif #ifdef AMBIENT uniform mat4 ambientMatrix; uniform vec2 vAmbientInfos; #endif #ifdef OPACITY uniform mat4 opacityMatrix; uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; uniform mat4 emissiveMatrix; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; uniform mat4 lightmapMatrix; #endif #if defined(SPECULAR) && defined(SPECULARTERM) uniform vec2 vSpecularInfos; uniform mat4 specularMatrix; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform mat4 bumpMatrix; #endif #ifdef REFLECTION uniform mat4 reflectionMatrix; #endif #ifdef POINTSIZE uniform float pointSize; #endif #ifdef DETAIL uniform vec4 vDetailInfos; uniform mat4 detailMatrix; #endif";r.a.IncludesShadersStore.defaultVertexDeclaration=s;c(195),c(87),c(92),c(196),c(197),c(198),c(123),c(159),c(160),c(161),c(101),c(102),c(110),c(111),c(88),c(89),c(199),c(200),c(201),c(184),c(112),c(185),c(162);r.a.IncludesShadersStore.pointCloudVertex="#ifdef POINTSIZE gl_PointSize=pointSize; #endif";c(186);s="#include<__decl__defaultVertex> #define CUSTOM_VERTEX_BEGIN attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #ifdef TANGENT attribute vec4 tangent; #endif #ifdef UV1 attribute vec2 uv; #endif #include[2..7] #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include #include #include[1..7] #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap) #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular) #endif #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump) varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #include #define CUSTOM_VERTEX_DEFINITIONS void main(void) { #define CUSTOM_VERTEX_MAIN_BEGIN vec3 positionUpdated=position; #ifdef NORMAL vec3 normalUpdated=normal; #endif #ifdef TANGENT vec4 tangentUpdated=tangent; #endif #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=positionUpdated; #endif #define CUSTOM_VERTEX_UPDATE_POSITION #define CUSTOM_VERTEX_UPDATE_NORMAL #include #if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*finalPreviousWorld*vec4(positionUpdated,1.0); #endif #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vNormalW=normalUpdated/vec3(dot(normalWorld[0],normalWorld[0]),dot(normalWorld[1],normalWorld[1]),dot(normalWorld[2],normalWorld[2])); vNormalW=normalize(normalWorld*vNormalW); #else #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normalUpdated); #endif #endif #define CUSTOM_VERTEX_UPDATE_WORLDPOS #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif vPositionW=vec3(worldPos); #include #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(positionUpdated,0.0))); #endif #ifndef UV1 vec2 uvUpdated=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uvUpdated; #endif #include[2..7] #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse,_MATRIXNAME_,diffuse,_INFONAME_,DiffuseInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_MATRIXNAME_,emissive,_INFONAME_,EmissiveInfos.x) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_MATRIXNAME_,lightmap,_INFONAME_,LightmapInfos.x) #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular,_MATRIXNAME_,specular,_INFONAME_,SpecularInfos.x) #endif #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_MATRIXNAME_,bump,_INFONAME_,BumpInfos.x) #include #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #include #include #define CUSTOM_VERTEX_MAIN_END } ";r.a.ShadersStore.defaultVertexShader=s;var t=c(1),u=c(74),v=c(100),w={effect:null,subMesh:null},x=function(a){function b(){var b=a.call(this)||this;return b.MAINUV1=!1,b.MAINUV2=!1,b.MAINUV3=!1,b.MAINUV4=!1,b.MAINUV5=!1,b.MAINUV6=!1,b.DIFFUSE=!1,b.DIFFUSEDIRECTUV=0,b.DETAIL=!1,b.DETAILDIRECTUV=0,b.DETAIL_NORMALBLENDMETHOD=0,b.AMBIENT=!1,b.AMBIENTDIRECTUV=0,b.OPACITY=!1,b.OPACITYDIRECTUV=0,b.OPACITYRGB=!1,b.REFLECTION=!1,b.EMISSIVE=!1,b.EMISSIVEDIRECTUV=0,b.SPECULAR=!1,b.SPECULARDIRECTUV=0,b.BUMP=!1,b.BUMPDIRECTUV=0,b.PARALLAX=!1,b.PARALLAXOCCLUSION=!1,b.SPECULAROVERALPHA=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.ALPHATEST=!1,b.DEPTHPREPASS=!1,b.ALPHAFROMDIFFUSE=!1,b.POINTSIZE=!1,b.FOG=!1,b.SPECULARTERM=!1,b.DIFFUSEFRESNEL=!1,b.OPACITYFRESNEL=!1,b.REFLECTIONFRESNEL=!1,b.REFRACTIONFRESNEL=!1,b.EMISSIVEFRESNEL=!1,b.FRESNEL=!1,b.NORMAL=!1,b.TANGENT=!1,b.UV1=!1,b.UV2=!1,b.UV3=!1,b.UV4=!1,b.UV5=!1,b.UV6=!1,b.VERTEXCOLOR=!1,b.VERTEXALPHA=!1,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.BONETEXTURE=!1,b.BONES_VELOCITY_ENABLED=!1,b.INSTANCES=!1,b.THIN_INSTANCES=!1,b.GLOSSINESS=!1,b.ROUGHNESS=!1,b.EMISSIVEASILLUMINATION=!1,b.LINKEMISSIVEWITHDIFFUSE=!1,b.REFLECTIONFRESNELFROMSPECULAR=!1,b.LIGHTMAP=!1,b.LIGHTMAPDIRECTUV=0,b.OBJECTSPACE_NORMALMAP=!1,b.USELIGHTMAPASSHADOWMAP=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFRACTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.INVERTCUBICMAP=!1,b.LOGARITHMICDEPTH=!1,b.REFRACTION=!1,b.REFRACTIONMAP_3D=!1,b.REFLECTIONOVERALPHA=!1,b.TWOSIDEDLIGHTING=!1,b.SHADOWFLOAT=!1,b.MORPHTARGETS=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_UV=!1,b.NUM_MORPH_INFLUENCERS=0,b.MORPHTARGETS_TEXTURE=!1,b.NONUNIFORMSCALING=!1,b.PREMULTIPLYALPHA=!1,b.ALPHATEST_AFTERALLALPHACOMPUTATIONS=!1,b.ALPHABLEND=!0,b.PREPASS=!1,b.PREPASS_IRRADIANCE=!1,b.PREPASS_IRRADIANCE_INDEX=-1,b.PREPASS_ALBEDO_SQRT=!1,b.PREPASS_ALBEDO_SQRT_INDEX=-1,b.PREPASS_DEPTH=!1,b.PREPASS_DEPTH_INDEX=-1,b.PREPASS_NORMAL=!1,b.PREPASS_NORMAL_INDEX=-1,b.PREPASS_POSITION=!1,b.PREPASS_POSITION_INDEX=-1,b.PREPASS_VELOCITY=!1,b.PREPASS_VELOCITY_INDEX=-1,b.PREPASS_REFLECTIVITY=!1,b.PREPASS_REFLECTIVITY_INDEX=-1,b.SCENE_MRT_COUNT=0,b.RGBDLIGHTMAP=!1,b.RGBDREFLECTION=!1,b.RGBDREFRACTION=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.MULTIVIEW=!1,b.ORDER_INDEPENDENT_TRANSPARENCY=!1,b.IS_REFLECTION_LINEAR=!1,b.IS_REFRACTION_LINEAR=!1,b.EXPOSURE=!1,b.rebuild(),b}return Object(d.d)(b,a),b.prototype.setReflectionMode=function(a){for(var b=0,c=["REFLECTIONMAP_CUBIC","REFLECTIONMAP_EXPLICIT","REFLECTIONMAP_PLANAR","REFLECTIONMAP_PROJECTION","REFLECTIONMAP_PROJECTION","REFLECTIONMAP_SKYBOX","REFLECTIONMAP_SPHERICAL","REFLECTIONMAP_EQUIRECTANGULAR","REFLECTIONMAP_EQUIRECTANGULAR_FIXED","REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED"];b0,f.REFLECTIONOVERALPHA=this._useReflectionOverAlpha,f.INVERTCUBICMAP=this._reflectionTexture.coordinatesMode===o.a.INVCUBIC_MODE,f.REFLECTIONMAP_3D=this._reflectionTexture.isCube,f.RGBDREFLECTION=this._reflectionTexture.isRGBD,f.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!this._reflectionTexture.invertZ:this._reflectionTexture.invertZ,this._reflectionTexture.coordinatesMode){case o.a.EXPLICIT_MODE:f.setReflectionMode("REFLECTIONMAP_EXPLICIT");break;case o.a.PLANAR_MODE:f.setReflectionMode("REFLECTIONMAP_PLANAR");break;case o.a.PROJECTION_MODE:f.setReflectionMode("REFLECTIONMAP_PROJECTION");break;case o.a.SKYBOX_MODE:f.setReflectionMode("REFLECTIONMAP_SKYBOX");break;case o.a.SPHERICAL_MODE:f.setReflectionMode("REFLECTIONMAP_SPHERICAL");break;case o.a.EQUIRECTANGULAR_MODE:f.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR");break;case o.a.FIXED_EQUIRECTANGULAR_MODE:f.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR_FIXED");break;case o.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:f.setReflectionMode("REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED");break;case o.a.CUBIC_MODE:case o.a.INVCUBIC_MODE:default:f.setReflectionMode("REFLECTIONMAP_CUBIC")}f.USE_LOCAL_REFLECTIONMAP_CUBIC=!!this._reflectionTexture.boundingBoxSize}else f.REFLECTION=!1,f.REFLECTIONMAP_OPPOSITEZ=!1;if(this._emissiveTexture&&b.EmissiveTextureEnabled){if(!this._emissiveTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._emissiveTexture,f,"EMISSIVE")}else f.EMISSIVE=!1;if(this._lightmapTexture&&b.LightmapTextureEnabled){if(!this._lightmapTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._lightmapTexture,f,"LIGHTMAP"),f.USELIGHTMAPASSHADOWMAP=this._useLightmapAsShadowmap,f.RGBDLIGHTMAP=this._lightmapTexture.isRGBD}else f.LIGHTMAP=!1;if(this._specularTexture&&b.SpecularTextureEnabled){if(!this._specularTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._specularTexture,f,"SPECULAR"),f.GLOSSINESS=this._useGlossinessFromSpecularMapAlpha}else f.SPECULAR=!1;if(e.getEngine().getCaps().standardDerivatives&&this._bumpTexture&&b.BumpTextureEnabled){if(!this._bumpTexture.isReady())return!1;n.a.PrepareDefinesForMergedUV(this._bumpTexture,f,"BUMP"),f.PARALLAX=this._useParallax,f.PARALLAXOCCLUSION=this._useParallaxOcclusion,f.OBJECTSPACE_NORMALMAP=this._useObjectSpaceNormalMap}else f.BUMP=!1;if(this._refractionTexture&&b.RefractionTextureEnabled){if(!this._refractionTexture.isReadyOrNotBlocking())return!1;f._needUVs=!0,f.REFRACTION=!0,f.REFRACTIONMAP_3D=this._refractionTexture.isCube,f.RGBDREFRACTION=this._refractionTexture.isRGBD,f.USE_LOCAL_REFRACTIONMAP_CUBIC=!!this._refractionTexture.boundingBoxSize}else f.REFRACTION=!1;f.TWOSIDEDLIGHTING=!this._backFaceCulling&&this._twoSidedLighting}else f.DIFFUSE=!1,f.AMBIENT=!1,f.OPACITY=!1,f.REFLECTION=!1,f.EMISSIVE=!1,f.LIGHTMAP=!1,f.BUMP=!1,f.REFRACTION=!1;f.ALPHAFROMDIFFUSE=this._shouldUseAlphaFromDiffuseTexture(),f.EMISSIVEASILLUMINATION=this._useEmissiveAsIllumination,f.LINKEMISSIVEWITHDIFFUSE=this._linkEmissiveWithDiffuse,f.SPECULAROVERALPHA=this._useSpecularOverAlpha,f.PREMULTIPLYALPHA=this.alphaMode===t.a.ALPHA_PREMULTIPLIED||this.alphaMode===t.a.ALPHA_PREMULTIPLIED_PORTERDUFF,f.ALPHATEST_AFTERALLALPHACOMPUTATIONS=null!==this.transparencyMode,f.ALPHABLEND=null===this.transparencyMode||this.needAlphaBlendingForMesh(a)}if(!this.detailMap.isReadyForSubMesh(f,e))return!1;if(f._areImageProcessingDirty&&this._imageProcessingConfiguration){if(!this._imageProcessingConfiguration.isReady())return!1;this._imageProcessingConfiguration.prepareDefines(f),f.IS_REFLECTION_LINEAR=null!=this.reflectionTexture&&!this.reflectionTexture.gammaSpace,f.IS_REFRACTION_LINEAR=null!=this.refractionTexture&&!this.refractionTexture.gammaSpace}if(f._areFresnelDirty&&(b.FresnelEnabled?(this._diffuseFresnelParameters||this._opacityFresnelParameters||this._emissiveFresnelParameters||this._refractionFresnelParameters||this._reflectionFresnelParameters)&&(f.DIFFUSEFRESNEL=this._diffuseFresnelParameters&&this._diffuseFresnelParameters.isEnabled,f.OPACITYFRESNEL=this._opacityFresnelParameters&&this._opacityFresnelParameters.isEnabled,f.REFLECTIONFRESNEL=this._reflectionFresnelParameters&&this._reflectionFresnelParameters.isEnabled,f.REFLECTIONFRESNELFROMSPECULAR=this._useReflectionFresnelFromSpecular,f.REFRACTIONFRESNEL=this._refractionFresnelParameters&&this._refractionFresnelParameters.isEnabled,f.EMISSIVEFRESNEL=this._emissiveFresnelParameters&&this._emissiveFresnelParameters.isEnabled,f._needNormals=!0,f.FRESNEL=!0):f.FRESNEL=!1),n.a.PrepareDefinesForMisc(a,e,this._useLogarithmicDepth,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a)||this._forceAlphaTest,f),n.a.PrepareDefinesForAttributes(a,f,!0,!0,!0),n.a.PrepareDefinesForFrameBoundValues(e,g,f,d,null,c.getRenderingMesh().hasThinInstances),this.detailMap.prepareDefines(f,e),f.isDirty){d=f._areLightsDisposed;f.markAsProcessed();var p=new u.a;f.REFLECTION&&p.addFallback(0,"REFLECTION"),f.SPECULAR&&p.addFallback(0,"SPECULAR"),f.BUMP&&p.addFallback(0,"BUMP"),f.PARALLAX&&p.addFallback(1,"PARALLAX"),f.PARALLAXOCCLUSION&&p.addFallback(0,"PARALLAXOCCLUSION"),f.SPECULAROVERALPHA&&p.addFallback(0,"SPECULAROVERALPHA"),f.FOG&&p.addFallback(1,"FOG"),f.POINTSIZE&&p.addFallback(0,"POINTSIZE"),f.LOGARITHMICDEPTH&&p.addFallback(0,"LOGARITHMICDEPTH"),n.a.HandleFallbacksForShadows(f,p,this._maxSimultaneousLights),f.SPECULARTERM&&p.addFallback(0,"SPECULARTERM"),f.DIFFUSEFRESNEL&&p.addFallback(1,"DIFFUSEFRESNEL"),f.OPACITYFRESNEL&&p.addFallback(2,"OPACITYFRESNEL"),f.REFLECTIONFRESNEL&&p.addFallback(3,"REFLECTIONFRESNEL"),f.EMISSIVEFRESNEL&&p.addFallback(4,"EMISSIVEFRESNEL"),f.FRESNEL&&p.addFallback(4,"FRESNEL"),f.MULTIVIEW&&p.addFallback(0,"MULTIVIEW");var q=[j.b.PositionKind];f.NORMAL&&q.push(j.b.NormalKind),f.TANGENT&&q.push(j.b.TangentKind);for(h=1;h<=t.a.MAX_SUPPORTED_UV_SETS;++h)f["UV"+h]&&q.push("uv"+(1===h?"":h));f.VERTEXCOLOR&&q.push(j.b.ColorKind),n.a.PrepareAttributesForBones(q,a,f,p),n.a.PrepareAttributesForInstances(q,f),n.a.PrepareAttributesForMorphTargets(q,a,f);h="default";a=["world","view","viewProjection","vEyePosition","vLightsType","vAmbientColor","vDiffuseColor","vSpecularColor","vEmissiveColor","visibility","vFogInfos","vFogColor","pointSize","vDiffuseInfos","vAmbientInfos","vOpacityInfos","vReflectionInfos","vEmissiveInfos","vSpecularInfos","vBumpInfos","vLightmapInfos","vRefractionInfos","mBones","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","diffuseMatrix","ambientMatrix","opacityMatrix","reflectionMatrix","emissiveMatrix","specularMatrix","bumpMatrix","normalMatrix","lightmapMatrix","refractionMatrix","diffuseLeftColor","diffuseRightColor","opacityParts","reflectionLeftColor","reflectionRightColor","emissiveLeftColor","emissiveRightColor","refractionLeftColor","refractionRightColor","vReflectionPosition","vReflectionSize","vRefractionPosition","vRefractionSize","logarithmicDepthConstant","vTangentSpaceParams","alphaCutOff","boneTextureWidth","morphTargetTextureInfo","morphTargetTextureIndices"];var i=["diffuseSampler","ambientSampler","opacitySampler","reflectionCubeSampler","reflection2DSampler","emissiveSampler","specularSampler","bumpSampler","lightmapSampler","refractionCubeSampler","refraction2DSampler","boneSampler","morphTargets","oitDepthSampler","oitFrontColorSampler"],m=["Material","Scene","Mesh"];v.a.AddUniforms(a),v.a.AddSamplers(i),k.a.AddUniforms(a),k.a.AddSamplers(i),l.a&&(l.a.PrepareUniforms(a,f),l.a.PrepareSamplers(i,f)),n.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:m,samplers:i,defines:f,maxSimultaneousLights:this._maxSimultaneousLights});var y={};this.customShaderNameResolve&&(h=this.customShaderNameResolve(h,a,m,i,f,q,y));var z=f.toString(),A=c.effect;h=e.getEngine().createEffect(h,{attributes:q,uniformsNames:a,uniformBuffersNames:m,samplers:i,defines:z,fallbacks:p,onCompiled:this.onCompiled,onError:this.onError,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights,maxSimultaneousMorphTargets:f.NUM_MORPH_INFLUENCERS},processFinalCode:y.processFinalCode,multiTarget:f.PREPASS},g);if(h)if(this._onEffectCreatedObservable&&(w.effect=h,w.subMesh=c,this._onEffectCreatedObservable.notifyObservers(w)),this.allowShaderHotSwapping&&A&&!h.isReady()){if(h=A,f.markAsUnprocessed(),d)return f._areLightsDisposed=!0,!1}else e.resetCachedMaterial(),c.setEffect(h,f,this._materialContext),this.buildUniformLayout()}return!(!c.effect||!c.effect.isReady())&&(f._renderId=e.getRenderId(),c.effect._wasPreviouslyReady=!0,!0)},b.prototype.buildUniformLayout=function(){var a=this._uniformBuffer;a.addUniform("diffuseLeftColor",4),a.addUniform("diffuseRightColor",4),a.addUniform("opacityParts",4),a.addUniform("reflectionLeftColor",4),a.addUniform("reflectionRightColor",4),a.addUniform("refractionLeftColor",4),a.addUniform("refractionRightColor",4),a.addUniform("emissiveLeftColor",4),a.addUniform("emissiveRightColor",4),a.addUniform("vDiffuseInfos",2),a.addUniform("vAmbientInfos",2),a.addUniform("vOpacityInfos",2),a.addUniform("vReflectionInfos",2),a.addUniform("vReflectionPosition",3),a.addUniform("vReflectionSize",3),a.addUniform("vEmissiveInfos",2),a.addUniform("vLightmapInfos",2),a.addUniform("vSpecularInfos",2),a.addUniform("vBumpInfos",3),a.addUniform("diffuseMatrix",16),a.addUniform("ambientMatrix",16),a.addUniform("opacityMatrix",16),a.addUniform("reflectionMatrix",16),a.addUniform("emissiveMatrix",16),a.addUniform("lightmapMatrix",16),a.addUniform("specularMatrix",16),a.addUniform("bumpMatrix",16),a.addUniform("vTangentSpaceParams",2),a.addUniform("pointSize",1),a.addUniform("alphaCutOff",1),a.addUniform("refractionMatrix",16),a.addUniform("vRefractionInfos",4),a.addUniform("vRefractionPosition",3),a.addUniform("vRefractionSize",3),a.addUniform("vSpecularColor",4),a.addUniform("vEmissiveColor",3),a.addUniform("vDiffuseColor",4),a.addUniform("vAmbientColor",3),v.a.PrepareUniformBuffer(a),a.create()},b.prototype.unbind=function(){if(this._activeEffect){var b=!1;this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&(this._activeEffect.setTexture("reflection2DSampler",null),b=!0),this._refractionTexture&&this._refractionTexture.isRenderTarget&&(this._activeEffect.setTexture("refraction2DSampler",null),b=!0),b&&this._markAllSubMeshesAsTexturesDirty()}a.prototype.unbind.call(this)},b.prototype.bindForSubMesh=function(a,c,d){var e=this.getScene(),f=d._materialDefines;if(f){d=d.effect;if(d){this._activeEffect=d,c.getMeshUniformBuffer().bindToEffect(d,"Mesh"),c.transferToEffect(a),this.prePassConfiguration.bindForSubMesh(this._activeEffect,e,c,a,this.isFrozen),f.OBJECTSPACE_NORMALMAP&&(a.toNormalMatrix(this._normalMatrix),this.bindOnlyNormalMatrix(this._normalMatrix));a=this._mustRebind(e,d,c.visibility);n.a.BindBonesParameters(c,d);var h=this._uniformBuffer;if(a){if(h.bindToEffect(d,"Material"),this.bindViewProjection(d),!h.useUbo||!this.isFrozen||!h.isSync){if(b.FresnelEnabled&&f.FRESNEL&&(this.diffuseFresnelParameters&&this.diffuseFresnelParameters.isEnabled&&(h.updateColor4("diffuseLeftColor",this.diffuseFresnelParameters.leftColor,this.diffuseFresnelParameters.power),h.updateColor4("diffuseRightColor",this.diffuseFresnelParameters.rightColor,this.diffuseFresnelParameters.bias)),this.opacityFresnelParameters&&this.opacityFresnelParameters.isEnabled&&h.updateColor4("opacityParts",new i.a(this.opacityFresnelParameters.leftColor.toLuminance(),this.opacityFresnelParameters.rightColor.toLuminance(),this.opacityFresnelParameters.bias),this.opacityFresnelParameters.power),this.reflectionFresnelParameters&&this.reflectionFresnelParameters.isEnabled&&(h.updateColor4("reflectionLeftColor",this.reflectionFresnelParameters.leftColor,this.reflectionFresnelParameters.power),h.updateColor4("reflectionRightColor",this.reflectionFresnelParameters.rightColor,this.reflectionFresnelParameters.bias)),this.refractionFresnelParameters&&this.refractionFresnelParameters.isEnabled&&(h.updateColor4("refractionLeftColor",this.refractionFresnelParameters.leftColor,this.refractionFresnelParameters.power),h.updateColor4("refractionRightColor",this.refractionFresnelParameters.rightColor,this.refractionFresnelParameters.bias)),this.emissiveFresnelParameters&&this.emissiveFresnelParameters.isEnabled&&(h.updateColor4("emissiveLeftColor",this.emissiveFresnelParameters.leftColor,this.emissiveFresnelParameters.power),h.updateColor4("emissiveRightColor",this.emissiveFresnelParameters.rightColor,this.emissiveFresnelParameters.bias))),e.texturesEnabled){if(this._diffuseTexture&&b.DiffuseTextureEnabled&&(h.updateFloat2("vDiffuseInfos",this._diffuseTexture.coordinatesIndex,this._diffuseTexture.level),n.a.BindTextureMatrix(this._diffuseTexture,h,"diffuse")),this._ambientTexture&&b.AmbientTextureEnabled&&(h.updateFloat2("vAmbientInfos",this._ambientTexture.coordinatesIndex,this._ambientTexture.level),n.a.BindTextureMatrix(this._ambientTexture,h,"ambient")),this._opacityTexture&&b.OpacityTextureEnabled&&(h.updateFloat2("vOpacityInfos",this._opacityTexture.coordinatesIndex,this._opacityTexture.level),n.a.BindTextureMatrix(this._opacityTexture,h,"opacity")),this._hasAlphaChannel()&&h.updateFloat("alphaCutOff",this.alphaCutOff),this._reflectionTexture&&b.ReflectionTextureEnabled&&(h.updateFloat2("vReflectionInfos",this._reflectionTexture.level,this.roughness),h.updateMatrix("reflectionMatrix",this._reflectionTexture.getReflectionTextureMatrix()),this._reflectionTexture.boundingBoxSize)){var j=this._reflectionTexture;h.updateVector3("vReflectionPosition",j.boundingBoxPosition),h.updateVector3("vReflectionSize",j.boundingBoxSize)}if(this._emissiveTexture&&b.EmissiveTextureEnabled&&(h.updateFloat2("vEmissiveInfos",this._emissiveTexture.coordinatesIndex,this._emissiveTexture.level),n.a.BindTextureMatrix(this._emissiveTexture,h,"emissive")),this._lightmapTexture&&b.LightmapTextureEnabled&&(h.updateFloat2("vLightmapInfos",this._lightmapTexture.coordinatesIndex,this._lightmapTexture.level),n.a.BindTextureMatrix(this._lightmapTexture,h,"lightmap")),this._specularTexture&&b.SpecularTextureEnabled&&(h.updateFloat2("vSpecularInfos",this._specularTexture.coordinatesIndex,this._specularTexture.level),n.a.BindTextureMatrix(this._specularTexture,h,"specular")),this._bumpTexture&&e.getEngine().getCaps().standardDerivatives&&b.BumpTextureEnabled&&(h.updateFloat3("vBumpInfos",this._bumpTexture.coordinatesIndex,1/this._bumpTexture.level,this.parallaxScaleBias),n.a.BindTextureMatrix(this._bumpTexture,h,"bump"),e._mirroredCameraPosition?h.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?1:-1,this._invertNormalMapY?1:-1):h.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?-1:1,this._invertNormalMapY?-1:1)),this._refractionTexture&&b.RefractionTextureEnabled){var k=1;(this._refractionTexture.isCube||(h.updateMatrix("refractionMatrix",this._refractionTexture.getReflectionTextureMatrix()),this._refractionTexture.depth&&(k=this._refractionTexture.depth)),h.updateFloat4("vRefractionInfos",this._refractionTexture.level,this.indexOfRefraction,k,this.invertRefractionY?-1:1),this._refractionTexture.boundingBoxSize)&&(j=this._refractionTexture,(h.updateVector3("vRefractionPosition",j.boundingBoxPosition),h.updateVector3("vRefractionSize",j.boundingBoxSize)))}}this.pointsCloud&&h.updateFloat("pointSize",this.pointSize),f.SPECULARTERM&&h.updateColor4("vSpecularColor",this.specularColor,this.specularPower),h.updateColor3("vEmissiveColor",b.EmissiveTextureEnabled?this.emissiveColor:i.a.BlackReadOnly),h.updateColor4("vDiffuseColor",this.diffuseColor,this.alpha),e.ambientColor.multiplyToRef(this.ambientColor,this._globalAmbientColor),h.updateColor3("vAmbientColor",this._globalAmbientColor)}e.texturesEnabled&&(this._diffuseTexture&&b.DiffuseTextureEnabled&&d.setTexture("diffuseSampler",this._diffuseTexture),this._ambientTexture&&b.AmbientTextureEnabled&&d.setTexture("ambientSampler",this._ambientTexture),this._opacityTexture&&b.OpacityTextureEnabled&&d.setTexture("opacitySampler",this._opacityTexture),this._reflectionTexture&&b.ReflectionTextureEnabled&&(this._reflectionTexture.isCube?d.setTexture("reflectionCubeSampler",this._reflectionTexture):d.setTexture("reflection2DSampler",this._reflectionTexture)),this._emissiveTexture&&b.EmissiveTextureEnabled&&d.setTexture("emissiveSampler",this._emissiveTexture),this._lightmapTexture&&b.LightmapTextureEnabled&&d.setTexture("lightmapSampler",this._lightmapTexture),this._specularTexture&&b.SpecularTextureEnabled&&d.setTexture("specularSampler",this._specularTexture),this._bumpTexture&&e.getEngine().getCaps().standardDerivatives&&b.BumpTextureEnabled&&d.setTexture("bumpSampler",this._bumpTexture),this._refractionTexture&&b.RefractionTextureEnabled)&&(k=1,this._refractionTexture.isCube?d.setTexture("refractionCubeSampler",this._refractionTexture):d.setTexture("refraction2DSampler",this._refractionTexture));this.getScene().useOrderIndependentTransparency&&this.needAlphaBlendingForMesh(c)&&this.getScene().depthPeelingRenderer.bind(d),this.detailMap.bindForSubMesh(h,e,this.isFrozen),n.a.BindClipPlane(d,e),this.bindEyePosition(d)}!a&&this.isFrozen||(e.lightsEnabled&&!this._disableLighting&&n.a.BindLights(e,c,d,f,this._maxSimultaneousLights),(e.fogEnabled&&c.applyFog&&e.fogMode!==g.a.FOGMODE_NONE||this._reflectionTexture||this._refractionTexture||c.receiveShadows)&&this.bindView(d),n.a.BindFogParameters(e,c,d),f.NUM_MORPH_INFLUENCERS&&n.a.BindMorphTargetParameters(c,d),this.useLogarithmicDepth&&n.a.BindLogDepth(f,d,e),this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(this._activeEffect)),this._afterBind(c,this._activeEffect),h.update()}}},b.prototype.getAnimatables=function(){var a=[];return this._diffuseTexture&&this._diffuseTexture.animations&&this._diffuseTexture.animations.length>0&&a.push(this._diffuseTexture),this._ambientTexture&&this._ambientTexture.animations&&this._ambientTexture.animations.length>0&&a.push(this._ambientTexture),this._opacityTexture&&this._opacityTexture.animations&&this._opacityTexture.animations.length>0&&a.push(this._opacityTexture),this._reflectionTexture&&this._reflectionTexture.animations&&this._reflectionTexture.animations.length>0&&a.push(this._reflectionTexture),this._emissiveTexture&&this._emissiveTexture.animations&&this._emissiveTexture.animations.length>0&&a.push(this._emissiveTexture),this._specularTexture&&this._specularTexture.animations&&this._specularTexture.animations.length>0&&a.push(this._specularTexture),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._lightmapTexture&&this._lightmapTexture.animations&&this._lightmapTexture.animations.length>0&&a.push(this._lightmapTexture),this._refractionTexture&&this._refractionTexture.animations&&this._refractionTexture.animations.length>0&&a.push(this._refractionTexture),this.detailMap.getAnimatables(a),a},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);return this._diffuseTexture&&b.push(this._diffuseTexture),this._ambientTexture&&b.push(this._ambientTexture),this._opacityTexture&&b.push(this._opacityTexture),this._reflectionTexture&&b.push(this._reflectionTexture),this._emissiveTexture&&b.push(this._emissiveTexture),this._specularTexture&&b.push(this._specularTexture),this._bumpTexture&&b.push(this._bumpTexture),this._lightmapTexture&&b.push(this._lightmapTexture),this._refractionTexture&&b.push(this._refractionTexture),this.detailMap.getActiveTextures(b),b},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._diffuseTexture===b||this._ambientTexture===b||this._opacityTexture===b||this._reflectionTexture===b||this._emissiveTexture===b||this._specularTexture===b||this._bumpTexture===b||this._lightmapTexture===b||this._refractionTexture===b||this.detailMap.hasTexture(b)},b.prototype.dispose=function(b,c){var d;c&&(null===(d=this._diffuseTexture)||void 0===d||d.dispose(),null===(d=this._ambientTexture)||void 0===d||d.dispose(),null===(d=this._opacityTexture)||void 0===d||d.dispose(),null===(d=this._reflectionTexture)||void 0===d||d.dispose(),null===(d=this._emissiveTexture)||void 0===d||d.dispose(),null===(d=this._specularTexture)||void 0===d||d.dispose(),null===(d=this._bumpTexture)||void 0===d||d.dispose(),null===(d=this._lightmapTexture)||void 0===d||d.dispose(),null===(d=this._refractionTexture)||void 0===d||d.dispose()),this.detailMap.dispose(c),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b,c)},b.prototype.clone=function(a){var c=this,d=e.a.Clone(function(){return new b(a,c.getScene())},this);return d.name=a,d.id=a,this.stencil.copyTo(d.stencil),d},b.prototype.serialize=function(){var a=e.a.Serialize(this);return a.stencil=this.stencil.serialize(),a},b.Parse=function(a,c,d){var f=e.a.Parse(function(){return new b(a.name,c)},a,c,d);return a.stencil&&f.stencil.parse(a.stencil,c,d),f},Object.defineProperty(b,"DiffuseTextureEnabled",{get:function(){return q.a.DiffuseTextureEnabled},set:function(a){q.a.DiffuseTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DetailTextureEnabled",{get:function(){return q.a.DetailTextureEnabled},set:function(a){q.a.DetailTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"AmbientTextureEnabled",{get:function(){return q.a.AmbientTextureEnabled},set:function(a){q.a.AmbientTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"OpacityTextureEnabled",{get:function(){return q.a.OpacityTextureEnabled},set:function(a){q.a.OpacityTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ReflectionTextureEnabled",{get:function(){return q.a.ReflectionTextureEnabled},set:function(a){q.a.ReflectionTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"EmissiveTextureEnabled",{get:function(){return q.a.EmissiveTextureEnabled},set:function(a){q.a.EmissiveTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"SpecularTextureEnabled",{get:function(){return q.a.SpecularTextureEnabled},set:function(a){q.a.SpecularTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BumpTextureEnabled",{get:function(){return q.a.BumpTextureEnabled},set:function(a){q.a.BumpTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LightmapTextureEnabled",{get:function(){return q.a.LightmapTextureEnabled},set:function(a){q.a.LightmapTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"RefractionTextureEnabled",{get:function(){return q.a.RefractionTextureEnabled},set:function(a){q.a.RefractionTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ColorGradingTextureEnabled",{get:function(){return q.a.ColorGradingTextureEnabled},set:function(a){q.a.ColorGradingTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"FresnelEnabled",{get:function(){return q.a.FresnelEnabled},set:function(a){q.a.FresnelEnabled=a},enumerable:!1,configurable:!0}),Object(d.c)([Object(e.n)("diffuseTexture")],b.prototype,"_diffuseTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"diffuseTexture",void 0),Object(d.c)([Object(e.n)("ambientTexture")],b.prototype,"_ambientTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTexture",void 0),Object(d.c)([Object(e.n)("opacityTexture")],b.prototype,"_opacityTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"opacityTexture",void 0),Object(d.c)([Object(e.n)("reflectionTexture")],b.prototype,"_reflectionTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(d.c)([Object(e.n)("emissiveTexture")],b.prototype,"_emissiveTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveTexture",void 0),Object(d.c)([Object(e.n)("specularTexture")],b.prototype,"_specularTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"specularTexture",void 0),Object(d.c)([Object(e.n)("bumpTexture")],b.prototype,"_bumpTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"bumpTexture",void 0),Object(d.c)([Object(e.n)("lightmapTexture")],b.prototype,"_lightmapTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"lightmapTexture",void 0),Object(d.c)([Object(e.n)("refractionTexture")],b.prototype,"_refractionTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"refractionTexture",void 0),Object(d.c)([Object(e.f)("ambient")],b.prototype,"ambientColor",void 0),Object(d.c)([Object(e.f)("diffuse")],b.prototype,"diffuseColor",void 0),Object(d.c)([Object(e.f)("specular")],b.prototype,"specularColor",void 0),Object(d.c)([Object(e.f)("emissive")],b.prototype,"emissiveColor",void 0),Object(d.c)([Object(e.d)()],b.prototype,"specularPower",void 0),Object(d.c)([Object(e.d)("useAlphaFromDiffuseTexture")],b.prototype,"_useAlphaFromDiffuseTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"useAlphaFromDiffuseTexture",void 0),Object(d.c)([Object(e.d)("useEmissiveAsIllumination")],b.prototype,"_useEmissiveAsIllumination",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useEmissiveAsIllumination",void 0),Object(d.c)([Object(e.d)("linkEmissiveWithDiffuse")],b.prototype,"_linkEmissiveWithDiffuse",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"linkEmissiveWithDiffuse",void 0),Object(d.c)([Object(e.d)("useSpecularOverAlpha")],b.prototype,"_useSpecularOverAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useSpecularOverAlpha",void 0),Object(d.c)([Object(e.d)("useReflectionOverAlpha")],b.prototype,"_useReflectionOverAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useReflectionOverAlpha",void 0),Object(d.c)([Object(e.d)("disableLighting")],b.prototype,"_disableLighting",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"disableLighting",void 0),Object(d.c)([Object(e.d)("useObjectSpaceNormalMap")],b.prototype,"_useObjectSpaceNormalMap",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useObjectSpaceNormalMap",void 0),Object(d.c)([Object(e.d)("useParallax")],b.prototype,"_useParallax",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallax",void 0),Object(d.c)([Object(e.d)("useParallaxOcclusion")],b.prototype,"_useParallaxOcclusion",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallaxOcclusion",void 0),Object(d.c)([Object(e.d)()],b.prototype,"parallaxScaleBias",void 0),Object(d.c)([Object(e.d)("roughness")],b.prototype,"_roughness",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"roughness",void 0),Object(d.c)([Object(e.d)()],b.prototype,"indexOfRefraction",void 0),Object(d.c)([Object(e.d)()],b.prototype,"invertRefractionY",void 0),Object(d.c)([Object(e.d)()],b.prototype,"alphaCutOff",void 0),Object(d.c)([Object(e.d)("useLightmapAsShadowmap")],b.prototype,"_useLightmapAsShadowmap",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLightmapAsShadowmap",void 0),Object(d.c)([Object(e.i)("diffuseFresnelParameters")],b.prototype,"_diffuseFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"diffuseFresnelParameters",void 0),Object(d.c)([Object(e.i)("opacityFresnelParameters")],b.prototype,"_opacityFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelAndMiscDirty")],b.prototype,"opacityFresnelParameters",void 0),Object(d.c)([Object(e.i)("reflectionFresnelParameters")],b.prototype,"_reflectionFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"reflectionFresnelParameters",void 0),Object(d.c)([Object(e.i)("refractionFresnelParameters")],b.prototype,"_refractionFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"refractionFresnelParameters",void 0),Object(d.c)([Object(e.i)("emissiveFresnelParameters")],b.prototype,"_emissiveFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"emissiveFresnelParameters",void 0),Object(d.c)([Object(e.d)("useReflectionFresnelFromSpecular")],b.prototype,"_useReflectionFresnelFromSpecular",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"useReflectionFresnelFromSpecular",void 0),Object(d.c)([Object(e.d)("useGlossinessFromSpecularMapAlpha")],b.prototype,"_useGlossinessFromSpecularMapAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useGlossinessFromSpecularMapAlpha",void 0),Object(d.c)([Object(e.d)("maxSimultaneousLights")],b.prototype,"_maxSimultaneousLights",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(d.c)([Object(e.d)("invertNormalMapX")],b.prototype,"_invertNormalMapX",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapX",void 0),Object(d.c)([Object(e.d)("invertNormalMapY")],b.prototype,"_invertNormalMapY",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapY",void 0),Object(d.c)([Object(e.d)("twoSidedLighting")],b.prototype,"_twoSidedLighting",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"twoSidedLighting",void 0),Object(d.c)([Object(e.d)()],b.prototype,"useLogarithmicDepth",null),b}(b.a);Object(p.b)("BABYLON.StandardMaterial",y),g.a.DefaultMaterialFactory=function(a){return new y("default material",a)}},function(a,b,c){c.d(b,"a",function(){return x});var d=c(2),e=c(12),f=c(6),g=c(0),h=c(13),i=c(4),j=c(18),k=c(46),l=c(50),m=c(70),n=c(1),o=c(67),p=c(174),q=c(26),r=c(114),s=c(8),t=c(22),u=c(25);a=c(10);var v=function(){this.facetNb=0,this.partitioningSubdivisions=10,this.partitioningBBoxRatio=1.01,this.facetDataEnabled=!1,this.facetParameters={},this.bbSize=g.e.Zero(),this.subDiv={max:1,X:1,Y:1,Z:1},this.facetDepthSort=!1,this.facetDepthSortEnabled=!1},w=function(){this._hasVertexAlpha=!1,this._useVertexColors=!0,this._numBoneInfluencers=4,this._applyFog=!0,this._receiveShadows=!1,this._facetData=new v,this._visibility=1,this._skeleton=null,this._layerMask=268435455,this._computeBonesUsingShaders=!0,this._isActive=!1,this._onlyForInstances=!1,this._isActiveIntermediate=!1,this._onlyForInstancesIntermediate=!1,this._actAsRegularMesh=!1,this._currentLOD=null,this._currentLODIsUpToDate=!1,this._collisionRetryCount=3,this._morphTargetManager=null,this._renderingGroupId=0,this._material=null,this._positions=null,this._meshCollisionData=new p.a},x=function(a){function b(c,d){void 0===d&&(d=null);var e=a.call(this,c,d,!1)||this;return e._internalAbstractMeshDataInfo=new w,e.cullingStrategy=b.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY,e.onCollideObservable=new f.c,e.onCollisionPositionChangeObservable=new f.c,e.onMaterialChangedObservable=new f.c,e.definedFacingForward=!0,e._occlusionQuery=null,e._renderingGroup=null,e.alphaIndex=Number.MAX_VALUE,e.isVisible=!0,e.isPickable=!0,e.isNearPickable=!1,e.isNearGrabbable=!1,e.showSubMeshesBoundingBox=!1,e.isBlocker=!1,e.enablePointerMoveEvents=!1,e.outlineColor=s.a.Red(),e.outlineWidth=.02,e.overlayColor=s.a.Red(),e.overlayAlpha=.5,e.useOctreeForRenderingSelection=!0,e.useOctreeForPicking=!0,e.useOctreeForCollisions=!0,e.alwaysSelectAsActiveMesh=!1,e.doNotSyncBoundingInfo=!1,e.actionManager=null,e.ellipsoid=new g.e(.5,1,.5),e.ellipsoidOffset=new g.e(0,0,0),e.edgesWidth=1,e.edgesColor=new s.b(1,0,0,1),e._edgesRenderer=null,e._masterMesh=null,e._boundingInfo=null,e._boundingInfoIsDirty=!0,e._renderId=0,e._intersectionsInProgress=new Array,e._unIndexed=!1,e._lightSources=new Array,e._waitingData={lods:null,actions:null,freezeWorldMatrix:null},e._bonesTransformMatrices=null,e._transformMatrixTexture=null,e.onRebuildObservable=new f.c,e._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null),b.subtractToRef(e._internalAbstractMeshDataInfo._meshCollisionData._oldPositionForCollisions,e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions),e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions.length()>h.a.CollisionsEpsilon&&e.position.addInPlace(e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions),c&&e.onCollideObservable.notifyObservers(c),e.onCollisionPositionChangeObservable.notifyObservers(e.position)},e.getScene().addMesh(e),e._resyncLightSources(),e._uniformBuffer=new o.a(e.getScene().getEngine(),void 0,void 0,c),e._buildUniformLayout(),e}return Object(d.d)(b,a),Object.defineProperty(b,"BILLBOARDMODE_NONE",{get:function(){return k.a.BILLBOARDMODE_NONE},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_X",{get:function(){return k.a.BILLBOARDMODE_X},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_Y",{get:function(){return k.a.BILLBOARDMODE_Y},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_Z",{get:function(){return k.a.BILLBOARDMODE_Z},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_ALL",{get:function(){return k.a.BILLBOARDMODE_ALL},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_USE_POSITION",{get:function(){return k.a.BILLBOARDMODE_USE_POSITION},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"facetNb",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetNb},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"partitioningSubdivisions",{get:function(){return this._internalAbstractMeshDataInfo._facetData.partitioningSubdivisions},set:function(a){this._internalAbstractMeshDataInfo._facetData.partitioningSubdivisions=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"partitioningBBoxRatio",{get:function(){return this._internalAbstractMeshDataInfo._facetData.partitioningBBoxRatio},set:function(a){this._internalAbstractMeshDataInfo._facetData.partitioningBBoxRatio=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"mustDepthSortFacets",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDepthSort},set:function(a){this._internalAbstractMeshDataInfo._facetData.facetDepthSort=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"facetDepthSortFrom",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDepthSortFrom},set:function(a){this._internalAbstractMeshDataInfo._facetData.facetDepthSortFrom=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionRetryCount",{get:function(){return this._internalAbstractMeshDataInfo._collisionRetryCount},set:function(a){this._internalAbstractMeshDataInfo._collisionRetryCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isFacetDataEnabled",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDataEnabled},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"morphTargetManager",{get:function(){return this._internalAbstractMeshDataInfo._morphTargetManager},set:function(a){this._internalAbstractMeshDataInfo._morphTargetManager!==a&&(this._internalAbstractMeshDataInfo._morphTargetManager=a,this._syncGeometryWithMorphTargetManager())},enumerable:!1,configurable:!0}),b.prototype._syncGeometryWithMorphTargetManager=function(){},b.prototype._updateNonUniformScalingState=function(b){return!!a.prototype._updateNonUniformScalingState.call(this,b)&&(this._markSubMeshesAsMiscDirty(),!0)},Object.defineProperty(b.prototype,"onCollide",{set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver&&this.onCollideObservable.remove(this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver),this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver=this.onCollideObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"onCollisionPositionChange",{set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver&&this.onCollisionPositionChangeObservable.remove(this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver),this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver=this.onCollisionPositionChangeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"visibility",{get:function(){return this._internalAbstractMeshDataInfo._visibility},set:function(a){if(this._internalAbstractMeshDataInfo._visibility!==a){var b=this._internalAbstractMeshDataInfo._visibility;this._internalAbstractMeshDataInfo._visibility=a,(1===b&&1!==a||1!==b&&1===a)&&this._markSubMeshesAsMiscDirty()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"renderingGroupId",{get:function(){return this._internalAbstractMeshDataInfo._renderingGroupId},set:function(a){this._internalAbstractMeshDataInfo._renderingGroupId=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"material",{get:function(){return this._internalAbstractMeshDataInfo._material},set:function(a){this._internalAbstractMeshDataInfo._material!==a&&(this._internalAbstractMeshDataInfo._material&&this._internalAbstractMeshDataInfo._material.meshMap&&(this._internalAbstractMeshDataInfo._material.meshMap[this.uniqueId]=void 0),this._internalAbstractMeshDataInfo._material=a,a&&a.meshMap&&(a.meshMap[this.uniqueId]=this),this.onMaterialChangedObservable.hasObservers()&&this.onMaterialChangedObservable.notifyObservers(this),this.subMeshes&&this._unBindEffect())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"receiveShadows",{get:function(){return this._internalAbstractMeshDataInfo._receiveShadows},set:function(a){this._internalAbstractMeshDataInfo._receiveShadows!==a&&(this._internalAbstractMeshDataInfo._receiveShadows=a,this._markSubMeshesAsLightDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasVertexAlpha",{get:function(){return this._internalAbstractMeshDataInfo._hasVertexAlpha},set:function(a){this._internalAbstractMeshDataInfo._hasVertexAlpha!==a&&(this._internalAbstractMeshDataInfo._hasVertexAlpha=a,this._markSubMeshesAsAttributesDirty(),this._markSubMeshesAsMiscDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useVertexColors",{get:function(){return this._internalAbstractMeshDataInfo._useVertexColors},set:function(a){this._internalAbstractMeshDataInfo._useVertexColors!==a&&(this._internalAbstractMeshDataInfo._useVertexColors=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"computeBonesUsingShaders",{get:function(){return this._internalAbstractMeshDataInfo._computeBonesUsingShaders},set:function(a){this._internalAbstractMeshDataInfo._computeBonesUsingShaders!==a&&(this._internalAbstractMeshDataInfo._computeBonesUsingShaders=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"numBoneInfluencers",{get:function(){return this._internalAbstractMeshDataInfo._numBoneInfluencers},set:function(a){this._internalAbstractMeshDataInfo._numBoneInfluencers!==a&&(this._internalAbstractMeshDataInfo._numBoneInfluencers=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"applyFog",{get:function(){return this._internalAbstractMeshDataInfo._applyFog},set:function(a){this._internalAbstractMeshDataInfo._applyFog!==a&&(this._internalAbstractMeshDataInfo._applyFog=a,this._markSubMeshesAsMiscDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"layerMask",{get:function(){return this._internalAbstractMeshDataInfo._layerMask},set:function(a){a!==this._internalAbstractMeshDataInfo._layerMask&&(this._internalAbstractMeshDataInfo._layerMask=a,this._resyncLightSources())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionMask},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionResponse",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionResponse},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionResponse=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionGroup",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionGroup},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionGroup=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"surroundingMeshes",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._surroundingMeshes},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._surroundingMeshes=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lightSources",{get:function(){return this._lightSources},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"_positions",{get:function(){return null},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skeleton",{get:function(){return this._internalAbstractMeshDataInfo._skeleton},set:function(a){var b=this._internalAbstractMeshDataInfo._skeleton;b&&b.needInitialSkinMatrix&&b._unregisterMeshWithPoseMatrix(this),a&&a.needInitialSkinMatrix&&a._registerMeshWithPoseMatrix(this),this._internalAbstractMeshDataInfo._skeleton=a,this._internalAbstractMeshDataInfo._skeleton||(this._bonesTransformMatrices=null),this._markSubMeshesAsAttributesDirty()},enumerable:!1,configurable:!0}),b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("world",16),this._uniformBuffer.addUniform("visibility",1),this._uniformBuffer.create()},b.prototype.transferToEffect=function(a){var b=this._uniformBuffer;b.updateMatrix("world",a),b.updateFloat("visibility",this._internalAbstractMeshDataInfo._visibility),b.update()},b.prototype.getMeshUniformBuffer=function(){return this._uniformBuffer},b.prototype.getClassName=function(){return"AbstractMesh"},b.prototype.toString=function(a){var b="Name: "+this.name+", isInstance: "+("InstancedMesh"!==this.getClassName()?"YES":"NO");b+=", # of submeshes: "+(this.subMeshes?this.subMeshes.length:0);var c=this._internalAbstractMeshDataInfo._skeleton;return c&&(b+=", skeleton: "+c.name),a&&(b+=", billboard mode: "+["NONE","X","Y",null,"Z",null,null,"ALL"][this.billboardMode],b+=", freeze wrld mat: "+(this._isWorldMatrixFrozen||this._waitingData.freezeWorldMatrix?"YES":"NO")),b},b.prototype._getEffectiveParent=function(){return this._masterMesh&&this.billboardMode!==k.a.BILLBOARDMODE_NONE?this._masterMesh:a.prototype._getEffectiveParent.call(this)},b.prototype._getActionManagerForTrigger=function(a,b){if(void 0===b&&(b=!0),this.actionManager&&(b||this.actionManager.isRecursive)){if(!a)return this.actionManager;if(this.actionManager.hasSpecificTrigger(a))return this.actionManager}return this.parent?this.parent._getActionManagerForTrigger(a,!1):null},b.prototype._rebuild=function(a){if(this.onRebuildObservable.notifyObservers(this),null!==this._occlusionQuery&&(this._occlusionQuery=null),this.subMeshes)for(var a=0,b=this.subMeshes;a4;a=h?this.getVerticesData(i.b.MatricesIndicesExtraKind):null;var j=h?this.getVerticesData(i.b.MatricesWeightsExtraKind):null;this.skeleton.prepare();for(var k=this.skeleton.getTransformMatrices(this),l=g.c.Vector3[0],m=g.c.Matrix[0],v=g.c.Matrix[1],w=0,x=0;x0&&(g.a.FromFloat32ArrayToRefScaled(k,Math.floor(16*c[w+u]),y,v),m.addToSelf(v));if(h)for(u=0;u<4;u++)(y=j[w+u])>0&&(g.a.FromFloat32ArrayToRefScaled(k,Math.floor(16*a[w+u]),y,v),m.addToSelf(v));g.e.TransformCoordinatesFromFloatsToRef(d[x],d[x+1],d[x+2],m,l),l.toArray(d,x),this._positions&&this._positions[x/3].copyFrom(l)}}}if(d&&b&&this.morphTargetManager)for(y=0,u=0,l=0;l0){v=a.getPositions();v&&(d[l]+=(v[l]-d[l])*k)}}if(y++,this._positions&&3===y){y=0;h=3*u;this._positions[u++].copyFromFloats(d[h],d[h+1],d[h+2])}}return d},b.prototype._updateBoundingInfo=function(){var a=this._effectiveMesh;return this._boundingInfo?this._boundingInfo.update(a.worldMatrixFromCache):this._boundingInfo=new m.a(this.position,this.position,a.worldMatrixFromCache),this._updateSubMeshesBoundingInfo(a.worldMatrixFromCache),this},b.prototype._updateSubMeshesBoundingInfo=function(a){if(!this.subMeshes)return this;for(var b=this.subMeshes.length,c=0;c1||!d.IsGlobal)&&d.updateBoundingInfo(a)}return this},b.prototype._afterComputeWorldMatrix=function(){this.doNotSyncBoundingInfo||(this._boundingInfoIsDirty=!0)},Object.defineProperty(b.prototype,"_effectiveMesh",{get:function(){return this.skeleton&&this.skeleton.overrideMesh||this},enumerable:!1,configurable:!0}),b.prototype.isInFrustum=function(a){return this.getBoundingInfo().isInFrustum(a,this.cullingStrategy)},b.prototype.isCompletelyInFrustum=function(a){return this.getBoundingInfo().isCompletelyInFrustum(a)},b.prototype.intersectsMesh=function(a,b,c){void 0===b&&(b=!1);var d=this.getBoundingInfo(),e=a.getBoundingInfo();if(d.intersects(e,b))return!0;if(c)for(d=0,e=this.getChildMeshes();d1&&!f._checkCollision(a)||this._collideForSubMesh(f,b,a)}return this},b.prototype._checkCollision=function(a){if(!this.getBoundingInfo()._checkCollision(a))return this;var b=g.c.Matrix[0],c=g.c.Matrix[1];return g.a.ScalingToRef(1/a._radius.x,1/a._radius.y,1/a._radius.z,b),this.worldMatrixFromCache.multiplyToRef(b,c),this._processCollisionsForSubMeshes(a,c),this},b.prototype._generatePointsArray=function(){return!1},b.prototype.intersects=function(a,b,c,d,e,f){void 0===d&&(d=!1),void 0===f&&(f=!1);var h=new l.a,i="InstancedLinesMesh"===this.getClassName()||"LinesMesh"===this.getClassName()?this.intersectionThreshold:0,j=this.getBoundingInfo();if(!this.subMeshes)return h;if(!(f||a.intersectsSphere(j.boundingSphere,i)&&a.intersectsBox(j.boundingBox,i)))return h;if(d)return h.hit=!f,h.pickedMesh=f?null:this,h.distance=f?0:g.e.Distance(a.origin,j.boundingSphere.center),h.subMeshId=0,h;if(!this._generatePointsArray())return h;for(var i=null,d=this._scene.getIntersectingSubMeshCandidates(this,a),f=d.length,k=!1,m=0;m1)||t.canIntersects(a)){o=t.intersects(a,this._positions,this.getIndices(),b,c);if(o&&(b||!i||o.distance-1&&this._parentContainer.meshes.splice(g,1),this._parentContainer=null}if(c&&this.material&&("MultiMaterial"===this.material.getClassName()?this.material.dispose(!1,!0,!0):this.material.dispose(!1,!0)),!b)for(d=0;d65535){f=!0;break}a.depthSortedIndices=f?new Uint32Array(c):new Uint16Array(c)}if(a.facetDepthSortFunction=function(a,b){return b.sqDistance-a.sqDistance},!a.facetDepthSortFrom){h=this.getScene().activeCamera;a.facetDepthSortFrom=h?h.position:g.e.Zero()}a.depthSortedFacets=[];for(f=0;ft.a?e.maximum.x-e.minimum.x:t.a,a.bbSize.y=e.maximum.y-e.minimum.y>t.a?e.maximum.y-e.minimum.y:t.a,a.bbSize.z=e.maximum.z-e.minimum.z>t.a?e.maximum.z-e.minimum.z:t.a;h=a.bbSize.x>a.bbSize.y?a.bbSize.x:a.bbSize.y;if(h=h>a.bbSize.z?h:a.bbSize.z,a.subDiv.max=a.partitioningSubdivisions,a.subDiv.X=Math.floor(a.subDiv.max*a.bbSize.x/h),a.subDiv.Y=Math.floor(a.subDiv.max*a.bbSize.y/h),a.subDiv.Z=Math.floor(a.subDiv.max*a.bbSize.z/h),a.subDiv.X=a.subDiv.X<1?1:a.subDiv.X,a.subDiv.Y=a.subDiv.Y<1?1:a.subDiv.Y,a.subDiv.Z=a.subDiv.Z<1?1:a.subDiv.Z,a.facetParameters.facetNormals=this.getFacetLocalNormals(),a.facetParameters.facetPositions=this.getFacetLocalPositions(),a.facetParameters.facetPartitioning=this.getFacetLocalPartitioning(),a.facetParameters.bInfo=e,a.facetParameters.bbSize=a.bbSize,a.facetParameters.subDiv=a.subDiv,a.facetParameters.ratio=this.partitioningBBoxRatio,a.facetParameters.depthSort=a.facetDepthSort,a.facetDepthSort&&a.facetDepthSortEnabled&&(this.computeWorldMatrix(!0),this._worldMatrix.invertToRef(a.invertedMatrix),g.e.TransformCoordinatesToRef(a.facetDepthSortFrom,a.invertedMatrix,a.facetDepthSortOrigin),a.facetParameters.distanceTo=a.facetDepthSortOrigin),a.facetParameters.depthSortedFacets=a.depthSortedFacets,d&&j.a.ComputeNormals(b,c,d,a.facetParameters),a.facetDepthSort&&a.facetDepthSortEnabled){a.depthSortedFacets.sort(a.facetDepthSortFunction);h=a.depthSortedIndices.length/3|0;for(f=0;fe.subDiv.max||b<0||b>e.subDiv.max||c<0||c>e.subDiv.max?null:e.facetPartitioning[a+e.subDiv.max*b+e.subDiv.max*e.subDiv.max*c]},b.prototype.getClosestFacetAtCoordinates=function(a,b,c,d,e,f){void 0===e&&(e=!1),void 0===f&&(f=!0);var h=this.getWorldMatrix(),i=g.c.Matrix[5];h.invertToRef(i);var j=g.c.Vector3[8];g.e.TransformCoordinatesFromFloatsToRef(a,b,c,i,j);a=this.getClosestFacetAtLocalCoordinates(j.x,j.y,j.z,d,e,f);return d&&g.e.TransformCoordinatesFromFloatsToRef(d.x,d.y,d.z,h,d),a},b.prototype.getClosestFacetAtLocalCoordinates=function(a,b,c,d,e,f){void 0===e&&(e=!1),void 0===f&&(f=!0);var g=null,h,i,j,k=this.getFacetLocalPositions(),l=this.getFacetLocalNormals(),m=this.getFacetsAtLocalCoordinates(a,b,c);if(!m)return null;for(var n,o,x,y=Number.MAX_VALUE,v=y,w=0;w=0||e&&!f&&j<=0)&&(j=o.x*x.x+o.y*x.y+o.z*x.z,x=-(o.x*a+o.y*b+o.z*c-j)/(o.x*o.x+o.y*o.y+o.z*o.z),(v=(h=(j=a+o.x*x)-a)*h+(i=(h=b+o.y*x)-b)*i+(o=(i=c+o.z*x)-c)*o)-1?a:i.a.GetShadersRepository(this._shaderLanguage)+a,this._engine._loadFile(e+"."+b.toLowerCase()+".fx",d))}else d(window.atob(a.substr(7)));else d(a.substr(7))},Object.defineProperty(a.prototype,"vertexSourceCode",{get:function(){var a;return this._vertexSourceCodeOverride&&this._fragmentSourceCodeOverride?this._vertexSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getVertexShaderCode())&&void 0!==a?a:this._vertexSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"fragmentSourceCode",{get:function(){var a;return this._vertexSourceCodeOverride&&this._fragmentSourceCodeOverride?this._fragmentSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getFragmentShaderCode())&&void 0!==a?a:this._fragmentSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawVertexSourceCode",{get:function(){return this._rawVertexSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawFragmentSourceCode",{get:function(){return this._rawFragmentSourceCode},enumerable:!1,configurable:!0}),a.prototype._rebuildProgram=function(a,b,c,d){var f=this;this._isReady=!1,this._vertexSourceCodeOverride=a,this._fragmentSourceCodeOverride=b,this.onError=function(a,b){d&&d(b)},this.onCompiled=function(){var a=f.getEngine().scenes;if(a)for(var b=0;b=d&&(e="Offending line ["+d+"] in "+(c?"fragment":"vertex")+" code: "+b[d-1])}}return[a,e]},a.prototype._processCompilationErrors=function(b,c){var d;void 0===c&&(c=null),this._compilationError=b.message;b=this._attributesNames;var e=this._fallbacks;if(g.a.Error("Unable to compile effect:"),g.a.Error("Uniforms: "+this._uniformsNames.map(function(a){return" "+a})),g.a.Error("Attributes: "+b.map(function(a){return" "+a})),g.a.Error("Defines: "+this.defines),a.LogShaderCodeOnCompilationError){b=null;var f=null,h=null;(null===(d=this._pipelineContext)||void 0===d?void 0:d._getVertexShaderCode())&&(h=(d=this._getShaderCodeAndErrorLine(this._pipelineContext._getVertexShaderCode(),this._compilationError,!1))[0],b=d[1],h&&(g.a.Error("Vertex code:"),g.a.Error(h))),(null===(d=this._pipelineContext)||void 0===d?void 0:d._getFragmentShaderCode())&&(h=(d=this._getShaderCodeAndErrorLine(null===(d=this._pipelineContext)||void 0===d?void 0:d._getFragmentShaderCode(),this._compilationError,!0))[0],f=d[1],h&&(g.a.Error("Fragment code:"),g.a.Error(h))),b&&g.a.Error(b),f&&g.a.Error(f)}g.a.Error("Error: "+this._compilationError),c&&(this._pipelineContext=c,this._isReady=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this)),e?(this._pipelineContext=null,e.hasMoreFallbacks?(this._allFallbacksProcessed=!1,g.a.Error("Trying next fallback."),this.defines=e.reduce(this.defines,this),this._prepareEffect()):(this._allFallbacksProcessed=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this),this.onErrorObservable.clear(),this._fallbacks&&this._fallbacks.unBindMesh())):this._allFallbacksProcessed=!0},Object.defineProperty(a.prototype,"isSupported",{get:function(){return""===this._compilationError},enumerable:!1,configurable:!0}),a.prototype._bindTexture=function(a,b){this._engine._bindTexture(this._samplers[a],b,a)},a.prototype.setTexture=function(a,b){this._engine.setTexture(this._samplers[a],this._uniforms[a],b,a)},a.prototype.setDepthStencilTexture=function(a,b){this._engine.setDepthStencilTexture(this._samplers[a],this._uniforms[a],b,a)},a.prototype.setTextureArray=function(a,b){var c=a+"Ex";if(-1===this._samplerList.indexOf(c+"0")){for(var d=this._samplerList.indexOf(a),e=1;e0&&(b.push(g-1),b.push(g)),g++}k=new f.a;return k.indices=b,k.positions=c,a&&(k.colors=e),k}function j(a){var b=a.dashSize||3,c=a.gapSize||1,e=a.dashNb||200;a=a.points;var g=new Array,h=new Array,i=d.e.Zero(),j=0,k=0,o=0,p=0,q=0;for(q=0;q>2,e=(3&b)<<4|(b=j>4,f=(15&b)<<2|(c=j>6,g=63&c,isNaN(b)?f=g=64:isNaN(c)&&(g=64),i+=h.charAt(d)+h.charAt(e)+h.charAt(f)+h.charAt(g);return i},h=function(a){return atob(a)},i=function(a){for(var a=h(a),b=a.length,c=new Uint8Array(new ArrayBuffer(b)),d=0;d0)):!c._pointerCaptures[e.pointerId]&&h.distance>d.distance&&(c.mainSceneTrackerPredicate&&c.mainSceneTrackerPredicate(d.pickedMesh)?(c._notifyObservers(b,d,e),b.skipOnPointerObservable=!0):b.type!==f.a.POINTERMOVE&&b.type!==f.a.POINTERUP||(c._lastPointerEvents[e.pointerId]&&(c.onPointerOutObservable.notifyObservers(e.pointerId),delete c._lastPointerEvents[e.pointerId]),c._notifyObservers(b,h,e))),b.type===f.a.POINTERUP&&c._pointerCaptures[e.pointerId]&&(c._pointerCaptures[e.pointerId]=!1))}}}}),this._originalPointerObserver&&a.onPrePointerObservable.makeObserverTopPriority(this._originalPointerObserver)),this.utilityLayerScene.autoClear=!1,this._afterRenderObserver=this.originalScene.onAfterCameraRenderObservable.add(function(a){c.shouldRender&&a==c.getRenderCamera()&&c.render()}),this._sceneDisposeObserver=this.originalScene.onDisposeObservable.add(function(){c.dispose()}),this._updateCamera()}return a.prototype.getRenderCamera=function(a){if(this._renderCamera)return this._renderCamera;var b;return b=this.originalScene.activeCameras&&this.originalScene.activeCameras.length>1?this.originalScene.activeCameras[this.originalScene.activeCameras.length-1]:this.originalScene.activeCamera,a&&b&&b.isRigCamera?b.rigParent:b},a.prototype.setRenderCamera=function(a){this._renderCamera=a},a.prototype._getSharedGizmoLight=function(){return this._sharedGizmoLight||(this._sharedGizmoLight=new i.a("shared gizmo light",new j.e(0,1,0),this.utilityLayerScene),this._sharedGizmoLight.intensity=2,this._sharedGizmoLight.groundColor=k.a.Gray()),this._sharedGizmoLight},Object.defineProperty(a,"DefaultUtilityLayer",{get:function(){return null==a._DefaultUtilityLayer?a._CreateDefaultUtilityLayerFromScene(h.a.LastCreatedScene):a._DefaultUtilityLayer},enumerable:!1,configurable:!0}),a._CreateDefaultUtilityLayerFromScene=function(b){return a._DefaultUtilityLayer=new a(b),a._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(function(){a._DefaultUtilityLayer=null}),a._DefaultUtilityLayer},Object.defineProperty(a,"DefaultKeepDepthUtilityLayer",{get:function(){return null==a._DefaultKeepDepthUtilityLayer&&(a._DefaultKeepDepthUtilityLayer=new a(h.a.LastCreatedScene),a._DefaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil=!1,a._DefaultKeepDepthUtilityLayer.originalScene.onDisposeObservable.addOnce(function(){a._DefaultKeepDepthUtilityLayer=null})),a._DefaultKeepDepthUtilityLayer},enumerable:!1,configurable:!0}),a.prototype._notifyObservers=function(a,b,c){a.skipOnPointerObservable||(this.utilityLayerScene.onPointerObservable.notifyObservers(new f.b(a.type,a.event,b),a.type),this._lastPointerEvents[c.pointerId]=!0)},a.prototype.render=function(){if(this._updateCamera(),this.utilityLayerScene.activeCamera){var a=this.utilityLayerScene.activeCamera.getScene(),b=this.utilityLayerScene.activeCamera;b._scene=this.utilityLayerScene,b.leftCamera&&(b.leftCamera._scene=this.utilityLayerScene),b.rightCamera&&(b.rightCamera._scene=this.utilityLayerScene),this.utilityLayerScene.render(!1),b._scene=a,b.leftCamera&&(b.leftCamera._scene=a),b.rightCamera&&(b.rightCamera._scene=a)}},a.prototype.dispose=function(){this.onPointerOutObservable.clear(),this._afterRenderObserver&&this.originalScene.onAfterCameraRenderObservable.remove(this._afterRenderObserver),this._sceneDisposeObserver&&this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver),this._originalPointerObserver&&this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver),this.utilityLayerScene.dispose()},a.prototype._updateCamera=function(){this.utilityLayerScene.cameraToUseForPointers=this.getRenderCamera(),this.utilityLayerScene.activeCamera=this.getRenderCamera()},a._DefaultUtilityLayer=null,a._DefaultKeepDepthUtilityLayer=null,a}()},function(a,b,c){var d;c.d(b,"a",function(){return d}),(function(a){a[a.GLSL=0]="GLSL",a[a.WGSL=1]="WGSL"})(d||(d={}))},function(a,b,c){c.d(b,"a",function(){return h});var d=c(51),e=c(0),f=c(50),g=c(126);a=c(21);b=c(28);var h=function(){function a(a,b,c){void 0===c&&(c=Number.MAX_VALUE),this.origin=a,this.direction=b,this.length=c}return a.prototype.clone=function(){return new a(this.origin.clone(),this.direction.clone(),this.length)},a.prototype.intersectsBoxMinMax=function(b,c,d){void 0===d&&(d=0);var e,f,g,h;b=a._TmpVector3[0].copyFromFloats(b.x-d,b.y-d,b.z-d);c=a._TmpVector3[1].copyFromFloats(c.x+d,c.y+d,c.z+d);d=0;var i=Number.MAX_VALUE;if(Math.abs(this.direction.x)<1e-7){if(this.origin.xc.x)return!1}else if(e=1/this.direction.x,f=(b.x-this.origin.x)*e,(g=(c.x-this.origin.x)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;if(Math.abs(this.direction.y)<1e-7){if(this.origin.yc.y)return!1}else if(e=1/this.direction.y,f=(b.y-this.origin.y)*e,(g=(c.y-this.origin.y)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;if(Math.abs(this.direction.z)<1e-7){if(this.origin.zc.z)return!1}else if(e=1/this.direction.z,f=(b.z-this.origin.z)*e,(g=(c.z-this.origin.z)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;return!0},a.prototype.intersectsBox=function(a,b){return void 0===b&&(b=0),this.intersectsBoxMinMax(a.minimum,a.maximum,b)},a.prototype.intersectsSphere=function(a,b){void 0===b&&(b=0);var c=a.center.x-this.origin.x,d=a.center.y-this.origin.y,e=a.center.z-this.origin.z,f=c*c+d*d+e*e;a=a.radius+b;b=a*a;if(f<=b)return!0;a=c*this.direction.x+d*this.direction.y+e*this.direction.z;return!(a<0)&&f-a*a<=b},a.prototype.intersectsTriangle=function(b,c,d){var f=a._TmpVector3[0],i=a._TmpVector3[1],j=a._TmpVector3[2],h=a._TmpVector3[3],k=a._TmpVector3[4];c.subtractToRef(b,f),d.subtractToRef(b,i),e.e.CrossToRef(this.direction,i,j);c=e.e.Dot(f,j);if(0===c)return null;d=1/c;this.origin.subtractToRef(b,h);c=e.e.Dot(h,j)*d;if(c<0||c>1)return null;e.e.CrossToRef(h,f,k);b=e.e.Dot(this.direction,k)*d;if(b<0||c+b>1)return null;j=e.e.Dot(i,k)*d;return j>this.length?null:new g.a(1-c-b,c,j)},a.prototype.intersectsPlane=function(a){var b=e.e.Dot(a.normal,this.direction);if(Math.abs(b)<999999997475243e-21)return null;var c=e.e.Dot(a.normal,this.origin);return(a=(-a.d-c)/b)<0?a<-999999997475243e-21?null:0:a},a.prototype.intersectsAxis=function(a,b){switch(void 0===b&&(b=0),a){case"y":return(a=(this.origin.y-b)/this.direction.y)>0?null:new e.e(this.origin.x+this.direction.x*-a,b,this.origin.z+this.direction.z*-a);case"x":return(a=(this.origin.x-b)/this.direction.x)>0?null:new e.e(b,this.origin.y+this.direction.y*-a,this.origin.z+this.direction.z*-a);case"z":return(a=(this.origin.z-b)/this.direction.z)>0?null:new e.e(this.origin.x+this.direction.x*-a,this.origin.y+this.direction.y*-a,b);default:return null}},a.prototype.intersectsMesh=function(b,c){var d=e.c.Matrix[0];return b.getWorldMatrix().invertToRef(d),this._tmpRay?a.TransformToRef(this,d,this._tmpRay):this._tmpRay=a.Transform(this,d),b.intersects(this._tmpRay,c)},a.prototype.intersectsMeshes=function(a,b,c){c?c.length=0:c=[];for(var d=0;db.distance?1:0},a.prototype.intersectionSegment=function(b,c,d){var f=this.origin,g=e.c.Vector3[0],i=e.c.Vector3[1],j=e.c.Vector3[2],h=e.c.Vector3[3];c.subtractToRef(b,g),this.direction.scaleToRef(a.rayl,j),f.addToRef(j,i),b.subtractToRef(f,h);var k;c=e.e.Dot(g,g);i=e.e.Dot(g,j);b=e.e.Dot(j,j);f=e.e.Dot(g,h);var l=e.e.Dot(j,h),m=c*b-i*i,n=m,o=m;mn&&(m=n,k=l+i,o=b)),k<0?(k=0,-f<0?m=0:-f>c?m=n:(m=-f,n=c)):k>o&&(k=o,-f+i<0?m=0:-f+i>c?m=n:(m=-f+i,n=c)),l=Math.abs(m)0&&b<=this.length&&c.lengthSquared()=a.distance?null:c:null},a.a.prototype._internalPick=function(a,b,c,d,g){if(!f.a)return null;for(var i=null,j=0;jthis.data.length&&(this.data.length*=2)},a.prototype.forEach=function(a){for(var b=0;bthis.data.length&&(this.data.length=2*(this.length+a.length));for(var b=0;b=this.length?-1:a},a.prototype.contains=function(a){return-1!==this.indexOf(a)},a._GlobalId=0,a}(),f=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._duplicateId=0,b}return Object(d.d)(b,a),b.prototype.push=function(b){a.prototype.push.call(this,b),b.__smartArrayFlags||(b.__smartArrayFlags={}),b.__smartArrayFlags[this._id]=this._duplicateId},b.prototype.pushNoDuplicate=function(a){return(!a.__smartArrayFlags||a.__smartArrayFlags[this._id]!==this._duplicateId)&&(this.push(a),!0)},b.prototype.reset=function(){a.prototype.reset.call(this),this._duplicateId++},b.prototype.concatWithNoDuplicate=function(a){if(0!==a.length){this.length+a.length>this.data.length&&(this.data.length=2*(this.length+a.length));for(var b=0;b-1&&this._parentContainer.transformNodes.splice(d,1),this._parentContainer=null}if(this.onAfterWorldMatrixUpdateObservable.clear(),b)for(var d=0,e=this.getChildTransformNodes(!0);d0)if("object"==typeof k[0])for(l=0;l1)?1:a.arc||1,i=a.slice&&a.slice<=0?1:a.slice||1,j=0===a.sideOrientation?0:a.sideOrientation||f.a.DEFAULTSIDE,k=!!a.dedupTopBottomIndices,c=new d.e(c/2,e/2,g/2),e=2+b,g=2*e,b=[],l=[],m=[],n=[],o=0;o<=e;o++){for(var p=o/e,q=p*Math.PI*i,r=0;r<=g;r++){var s=r/g,t=s*Math.PI*2*h,u=d.a.RotationZ(-q);t=d.a.RotationY(t);u=d.e.TransformCoordinates(d.e.Up(),u);u=d.e.TransformCoordinates(u,t);t=u.multiply(c);u=u.divide(c).normalize();l.push(t.x,t.y,t.z),m.push(u.x,u.y,u.z),n.push(s,p)}if(o>0)for(t=l.length/3,u=t-2*(g+1);u+g+21&&(b.push(u),b.push(u+1),b.push(u+g+1)),(o0&&-1===this.includedOnlyMeshes.indexOf(a))&&!(this.excludedMeshes&&this.excludedMeshes.length>0&&-1!==this.excludedMeshes.indexOf(a))&&(0===this.includeOnlyWithLayerMask||0!=(this.includeOnlyWithLayerMask&a.layerMask))&&!(0!==this.excludeWithLayerMask&&this.excludeWithLayerMask&a.layerMask)},b.prototype.dispose=function(b,c){if(void 0===c&&(c=!1),this._shadowGenerator&&(this._shadowGenerator.dispose(),this._shadowGenerator=null),this.getScene().stopAnimation(this),this._parentContainer){var d=this._parentContainer.lights.indexOf(this);d>-1&&this._parentContainer.lights.splice(d,1),this._parentContainer=null}for(var d=0,e=this.getScene().meshes;d0&&(a.excludedMeshesIds=[],this.excludedMeshes.forEach(function(b){a.excludedMeshesIds.push(b.id)})),this.includedOnlyMeshes.length>0&&(a.includedOnlyMeshesIds=[],this.includedOnlyMeshes.forEach(function(b){a.includedOnlyMeshesIds.push(b.id)})),e.a.AppendSerializedAnimations(this,a),a.ranges=this.serializeAnimationRanges(),a},b.GetConstructorFromName=function(a,b,c){a=h.a.Construct("Light_Type_"+a,b,c);return a||null},b.Parse=function(a,c){var d=b.GetConstructorFromName(a.type,a.name,c);if(!d)return null;d=e.a.Parse(d,a,c);if(a.excludedMeshesIds&&(d._excludedMeshesIds=a.excludedMeshesIds),a.includedOnlyMeshesIds&&(d._includedOnlyMeshesIds=a.includedOnlyMeshesIds),a.parentId&&(d._waitingParentId=a.parentId),void 0!==a.falloffType&&(d.falloffType=a.falloffType),void 0!==a.lightmapMode&&(d.lightmapMode=a.lightmapMode),a.animations){for(var f=0;f=0&&this._scene.textures.splice(b,1),this._scene.onTextureRemovedObservable.notifyObservers(this),this._scene=null,this._parentContainer){b=this._parentContainer.textures.indexOf(this);b>-1&&this._parentContainer.textures.splice(b,1),this._parentContainer=null}}this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.metadata=null,a.prototype.dispose.call(this)},b.prototype.serialize=function(){if(!this.name)return null;var a=e.a.Serialize(this);return e.a.AppendSerializedAnimations(this,a),a},b.WhenAllReady=function(a,b){var c=a.length;if(0!==c)for(var d=0;d=200&&a.status<300||0===a.status&&(!Object(f.e)()||A())){try{b(h?a.response:a.responseText,a)}catch(a){g(a)}return}var c=t.DefaultRetryStrategy;if(c){c=c(j,a,l);if(-1!==c)return a.removeEventListener("loadend",q),a=new e.a,void (d=setTimeout(function(){return p(l+1)},c))}c=new r("Error status: "+a.status+" "+a.statusText+" - Unable to load "+j,a);s&&s(c)}};a.addEventListener("readystatechange",u),a.send()};p(0)};if(d&&d.enableSceneOffline){var q=function(a){a&&a.status>400?s&&s(a):p()};d.open(function(){d&&d.loadFile(t.BaseUrl+a,function(a){v||b(a),k.onCompleteObservable.notifyObservers(k)},c?function(a){v||c(a)}:void 0,q,h)},q)}else p();return k},A=function(){return"undefined"!=typeof location&&"file:"===location.protocol},B=function(a){return p.test(a)};function C(a){return Object(j.b)(a.split(",")[1])}var D=function(a){return Object(j.c)(a.split(",")[1])};k.a._FileToolsLoadImage=w,k.a._FileToolsLoadFile=y,b.a._FileToolsLoadFile=y,Object(o.b)(C,D,t,B,A,y,w,x,z,v)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b,c,d,e,f){this.source=a,this.pointerX=b,this.pointerY=c,this.meshUnderPointer=d,this.sourceEvent=e,this.additionalData=f}return a.CreateNew=function(b,c,d){var e=b.getScene();return new a(b,e.pointerX,e.pointerY,e.meshUnderPointer||b,c,d)},a.CreateNewFromSprite=function(b,c,d,e){return new a(b,c.pointerX,c.pointerY,c.meshUnderPointer,d,e)},a.CreateNewFromScene=function(b,c){return new a(null,b.pointerX,b.pointerY,b.meshUnderPointer,c)},a.CreateNewFromPrimitive=function(b,c,d,e){return new a(b,c.x,c.y,null,d,e)},a}()},function(a,b,c){c.d(b,"b",function(){return j}),c.d(b,"a",function(){return k}),c.d(b,"c",function(){return l});var d=c(0),e=c(8),f=c(9),g=c(18),h=c(21),i=c(25);function j(a){var b=a.height||2,c=0===a.diameterTop?0:a.diameterTop||a.diameter||1,h=0===a.diameterBottom?0:a.diameterBottom||a.diameter||1;c=c||1e-5,h=h||1e-5;var j,k=a.tessellation||24,l=a.subdivisions||1,m=!!a.hasRings,n=!!a.enclose,o=0===a.cap?0:a.cap||f.a.CAP_ALL,p=a.arc&&(a.arc<=0||a.arc>1)?1:a.arc||1,q=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE,r=a.faceUV||new Array(3),s=a.faceColors,t=2+(1+(1!==p&&n?2:0))*(m?l:1);for(j=0;j1e3&&(this._lastSecAverage=this._lastSecAccumulated/this._lastSecValueCount,this._lastSecTime=a,this._lastSecAccumulated=0,this._lastSecValueCount=0)},a.Enabled=!0,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b,c,d){this.x=a,this.y=b,this.width=c,this.height=d}return a.prototype.toGlobal=function(b,c){return new a(this.x*b,this.y*c,this.width*b,this.height*c)},a.prototype.toGlobalToRef=function(a,b,c){return c.x=this.x*a,c.y=this.y*b,c.width=this.width*a,c.height=this.height*b,this},a.prototype.clone=function(){return new a(this.x,this.y,this.width,this.height)},a}()},function(a,b,c){c.d(b,"c",function(){return h}),c.d(b,"b",function(){return i}),c.d(b,"a",function(){return j});var d=c(0),e=c(8),f=c(9),g=c(18);function h(a){var b=[0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23],c=[0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0],f=[],h=a.width||a.size||1,i=a.height||a.size||1,j=a.depth||a.size||1,k=a.wrap||!1,l=void 0===a.topBaseAt?1:a.topBaseAt,m=void 0===a.bottomBaseAt?0:a.bottomBaseAt;l=[2,0,3,1][(l+4)%4];m=[2,0,1,3][(m+4)%4];var n=[1,-1,1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,1];if(k){b=[2,3,0,2,0,1,4,5,6,4,6,7,9,10,11,9,11,8,12,14,15,12,13,14],n=[-1,1,1,1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,1,1,1,-1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1];for(var k=[[1,1,1],[-1,1,1],[-1,1,-1],[1,1,-1]],o=[[-1,-1,1],[1,-1,1],[1,-1,-1],[-1,-1,-1]],p=[17,18,19,16],q=[22,23,20,21];l>0;)k.unshift(k.pop()),p.unshift(p.pop()),l--;for(;m>0;)o.unshift(o.pop()),q.unshift(q.pop()),m--;k=k.flat(),o=o.flat(),n=n.concat(k).concat(o),b.push(p[0],p[2],p[3],p[0],p[1],p[2]),b.push(q[0],q[2],q[3],q[0],q[1],q[2])}var r=[h/2,i/2,j/2];l=n.reduce(function(a,b,c){return a.concat(b*r[c%3])},[]);for(m=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE,k=a.faceUV||new Array(6),o=a.faceColors,p=[],q=0;q<6;q++)void 0===k[q]&&(k[q]=new d.f(0,0,1,1)),o&&void 0===o[q]&&(o[q]=new e.b(1,1,1,1));for(h=0;h<6;h++)if(f.push(k[h].z,k[h].w),f.push(k[h].x,k[h].w),f.push(k[h].x,k[h].y),f.push(k[h].z,k[h].y),o)for(i=0;i<4;i++)p.push(o[h].r,o[h].g,o[h].b,o[h].a);g.a._ComputeSides(m,l,b,c,f,a.frontUVs,a.backUVs);j=new g.a;if(j.indices=b,j.positions=l,j.normals=c,j.uvs=f,o){n=m===g.a.DOUBLESIDE?p.concat(p):p;j.colors=n}return j}function i(a,b,c){void 0===c&&(c=null);a=new f.a(a,c);return b.sideOrientation=f.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,h(b).applyToMesh(a,b.updatable),a}var j={CreateBox:i};g.a.CreateBox=h,f.a.CreateBox=function(a,b,c,d,e){return void 0===c&&(c=null),i(a,{size:b,sideOrientation:e,updatable:d},c)}},function(a,b,c){c.d(b,"a",function(){return j});var d=c(4),e=c(126),f=c(70),g=c(1),h=c(114),i=c(37),j=function(){function a(a,b,c,d,e,f,h,j,k){void 0===j&&(j=!0),void 0===k&&(k=!0),this.materialIndex=a,this.verticesStart=b,this.verticesCount=c,this.indexStart=d,this.indexCount=e,this._materialDefines=null,this._materialEffect=null,this._mainDrawWrapperOverride=null,this._linesIndexCount=0,this._linesIndexBuffer=null,this._lastColliderWorldVertices=null,this._lastColliderTransformMatrix=null,this._renderId=0,this._alphaIndex=0,this._distanceToCamera=0,this._currentMaterial=null,this._mesh=f,this._renderingMesh=h||f,k&&f.subMeshes.push(this),this._drawWrappers={},this._mainDrawWrapper=new i.a(this._mesh.getScene().getEngine(),!1),this._drawWrappers[g.a.SUBMESH_DRAWWRAPPER_MAINPASS]=this._mainDrawWrapper,this._trianglePlanes=[],this._id=f.subMeshes.length-1,j&&(this.refreshBoundingInfo(),f.computeWorldMatrix(!0))}return Object.defineProperty(a.prototype,"materialDefines",{get:function(){return this._mainDrawWrapperOverride?this._mainDrawWrapperOverride.defines:this._mainDrawWrapper.defines},set:function(a){var b;(null!==(b=this._mainDrawWrapperOverride)&&void 0!==b?b:this._mainDrawWrapper).defines=a,this._materialDefines=a},enumerable:!1,configurable:!0}),a.prototype._getDrawWrapper=function(a,b){if(void 0===b&&(b=!1),a===g.a.SUBMESH_DRAWWRAPPER_MAINPASS)return this._mainDrawWrapper;var c=this._drawWrappers[a];return!c&&b&&(this._drawWrappers[a]=c=new i.a(this._mesh.getScene().getEngine())),c},a.prototype._removeEffect=function(a){delete this._drawWrappers[a]},Object.defineProperty(a.prototype,"effect",{get:function(){return this._mainDrawWrapperOverride?this._mainDrawWrapperOverride.effect:this._mainDrawWrapper.effect},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_drawWrapper",{get:function(){var a;return null!==(a=this._mainDrawWrapperOverride)&&void 0!==a?a:this._mainDrawWrapper},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_drawWrapperOverride",{get:function(){return this._mainDrawWrapperOverride},enumerable:!1,configurable:!0}),a.prototype._setMainDrawWrapperOverride=function(a){this._mainDrawWrapperOverride=a;a=null!==(a=this._mainDrawWrapperOverride)&&void 0!==a?a:this._mainDrawWrapper;this._materialEffect=a.effect,this._materialDefines=a.defines},a.prototype.setEffect=function(a,b,c,d){var e;void 0===b&&(b=null),void 0===d&&(d=!0);e=null!==(e=this._mainDrawWrapperOverride)&&void 0!==e?e:this._mainDrawWrapper;e.setEffect(a,b),void 0!==c&&(e.materialContext=c,d&&c.reset()),a!==this._materialEffect?(this._materialEffect=a,this._materialDefines=b):a||(this._materialDefines=null,e.materialContext=void 0)},a.AddToMesh=function(b,c,d,e,f,g,h,i){return void 0===i&&(i=!0),new a(b,c,d,e,f,g,h,i)},Object.defineProperty(a.prototype,"IsGlobal",{get:function(){return 0===this.verticesStart&&this.verticesCount===this._mesh.getTotalVertices()},enumerable:!1,configurable:!0}),a.prototype.getBoundingInfo=function(){return this.IsGlobal?this._mesh.getBoundingInfo():this._boundingInfo},a.prototype.setBoundingInfo=function(a){return this._boundingInfo=a,this},a.prototype.getMesh=function(){return this._mesh},a.prototype.getRenderingMesh=function(){return this._renderingMesh},a.prototype.getReplacementMesh=function(){return this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:null},a.prototype.getEffectiveMesh=function(){var a=this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:null;return a||this._renderingMesh},a.prototype.getMaterial=function(){var a=this._renderingMesh.material;if(null==a)return this._mesh.getScene().defaultMaterial;if(this._IsMultiMaterial(a)){var b=a.getSubMaterial(this.materialIndex);return this._currentMaterial!==b&&(this._currentMaterial=b,this._mainDrawWrapper.defines=null),b}return a},a.prototype._IsMultiMaterial=function(a){return void 0!==a.getSubMaterial},a.prototype.refreshBoundingInfo=function(a){if(void 0===a&&(a=null),this._lastColliderWorldVertices=null,this.IsGlobal||!this._renderingMesh||!this._renderingMesh.geometry)return this;if(a||(a=this._renderingMesh.getVerticesData(d.b.PositionKind)),!a)return this._boundingInfo=this._mesh.getBoundingInfo(),this;var b=this._renderingMesh.getIndices();if(0===this.indexStart&&this.indexCount===b.length){var c=this._renderingMesh.getBoundingInfo();c={minimum:c.minimum.clone(),maximum:c.maximum.clone()}}else c=Object(h.b)(a,b,this.indexStart,this.indexCount,this._renderingMesh.geometry.boundingBias);return this._boundingInfo?this._boundingInfo.reConstruct(c.minimum,c.maximum):this._boundingInfo=new f.a(c.minimum,c.maximum),this},a.prototype._checkCollision=function(a){return this.getBoundingInfo()._checkCollision(a)},a.prototype.updateBoundingInfo=function(a){var b=this.getBoundingInfo();return b||(this.refreshBoundingInfo(),b=this.getBoundingInfo()),b&&b.update(a),this},a.prototype.isInFrustum=function(a){var b=this.getBoundingInfo();return!!b&&b.isInFrustum(a,this._mesh.cullingStrategy)},a.prototype.isCompletelyInFrustum=function(a){var b=this.getBoundingInfo();return!!b&&b.isCompletelyInFrustum(a)},a.prototype.render=function(a){return this._renderingMesh.render(this,a,this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:void 0),this},a.prototype._getLinesIndexBuffer=function(a,b){if(!this._linesIndexBuffer){for(var c=[],d=this.indexStart;di&&(i=l)}return new a(b,h,i-h+1,c,d,e,f,g)},a}()},function(a,b,c){a="helperFunctions";b="const float PI=3.1415926535897932384626433832795; const float HALF_MIN=5.96046448e-08; const float LinearEncodePowerApprox=2.2; const float GammaEncodePowerApprox=1.0/LinearEncodePowerApprox; const vec3 LuminanceEncodeApprox=vec3(0.2126,0.7152,0.0722); const float Epsilon=0.0000001; #define saturate(x) clamp(x,0.0,1.0) #define absEps(x) abs(x)+Epsilon #define maxEps(x) max(x,Epsilon) #define saturateEps(x) clamp(x,Epsilon,1.0) mat3 transposeMat3(mat3 inMatrix) { vec3 i0=inMatrix[0]; vec3 i1=inMatrix[1]; vec3 i2=inMatrix[2]; mat3 outMatrix=mat3( vec3(i0.x,i1.x,i2.x), vec3(i0.y,i1.y,i2.y), vec3(i0.z,i1.z,i2.z) ); return outMatrix; } mat3 inverseMat3(mat3 inMatrix) { float a00=inMatrix[0][0],a01=inMatrix[0][1],a02=inMatrix[0][2]; float a10=inMatrix[1][0],a11=inMatrix[1][1],a12=inMatrix[1][2]; float a20=inMatrix[2][0],a21=inMatrix[2][1],a22=inMatrix[2][2]; float b01=a22*a11-a12*a21; float b11=-a22*a10+a12*a20; float b21=a21*a10-a11*a20; float det=a00*b01+a01*b11+a02*b21; return mat3(b01,(-a22*a01+a02*a21),(a12*a01-a02*a11), b11,(a22*a00-a02*a20),(-a12*a00+a02*a10), b21,(-a21*a00+a01*a20),(a11*a00-a01*a10))/det; } float toLinearSpace(float color) { return pow(color,LinearEncodePowerApprox); } vec3 toLinearSpace(vec3 color) { return pow(color,vec3(LinearEncodePowerApprox)); } vec4 toLinearSpace(vec4 color) { return vec4(pow(color.rgb,vec3(LinearEncodePowerApprox)),color.a); } vec3 toGammaSpace(vec3 color) { return pow(color,vec3(GammaEncodePowerApprox)); } vec4 toGammaSpace(vec4 color) { return vec4(pow(color.rgb,vec3(GammaEncodePowerApprox)),color.a); } float toGammaSpace(float color) { return pow(color,GammaEncodePowerApprox); } float square(float value) { return value*value; } float pow5(float value) { float sq=value*value; return sq*sq*value; } float getLuminance(vec3 color) { return clamp(dot(color,LuminanceEncodeApprox),0.,1.); } float getRand(vec2 seed) { return fract(sin(dot(seed.xy ,vec2(12.9898,78.233)))*43758.5453); } float dither(vec2 seed,float varianceAmount) { float rand=getRand(seed); float dither=mix(-varianceAmount/255.0,varianceAmount/255.0,rand); return dither; } const float rgbdMaxRange=255.0; vec4 toRGBD(vec3 color) { float maxRGB=maxEps(max(color.r,max(color.g,color.b))); float D=max(rgbdMaxRange/maxRGB,1.); D=clamp(floor(D)/255.0,0.,1.); vec3 rgb=color.rgb*D; rgb=toGammaSpace(rgb); return vec4(clamp(rgb,0.,1.),D); } vec3 fromRGBD(vec4 rgbd) { rgbd.rgb=toLinearSpace(rgbd.rgb); return rgbd.rgb/rgbd.a; } vec3 parallaxCorrectNormal( vec3 vertexPos,vec3 origVec,vec3 cubeSize,vec3 cubePos ) { vec3 invOrigVec=vec3(1.0,1.0,1.0)/origVec; vec3 halfSize=cubeSize*0.5; vec3 intersecAtMaxPlane=(cubePos+halfSize-vertexPos)*invOrigVec; vec3 intersecAtMinPlane=(cubePos-halfSize-vertexPos)*invOrigVec; vec3 largestIntersec=max(intersecAtMaxPlane,intersecAtMinPlane); float distance=min(min(largestIntersec.x,largestIntersec.y),largestIntersec.z); vec3 intersectPositionWS=vertexPos+origVec*distance; return intersectPositionWS-cubePos; } ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.CompareLightsPriority=function(a,b){return a.shadowEnabled!==b.shadowEnabled?(b.shadowEnabled?1:0)-(a.shadowEnabled?1:0):b.renderPriority-a.renderPriority},a.FALLOFF_DEFAULT=0,a.FALLOFF_PHYSICAL=1,a.FALLOFF_GLTF=2,a.FALLOFF_STANDARD=3,a.LIGHTMAP_DEFAULT=0,a.LIGHTMAP_SPECULAR=1,a.LIGHTMAP_SHADOWSONLY=2,a.INTENSITYMODE_AUTOMATIC=0,a.INTENSITYMODE_LUMINOUSPOWER=1,a.INTENSITYMODE_LUMINOUSINTENSITY=2,a.INTENSITYMODE_ILLUMINANCE=3,a.INTENSITYMODE_LUMINANCE=4,a.LIGHTTYPEID_POINTLIGHT=0,a.LIGHTTYPEID_DIRECTIONALLIGHT=1,a.LIGHTTYPEID_SPOTLIGHT=2,a.LIGHTTYPEID_HEMISPHERICLIGHT=3,a}()},function(a,b,c){c.d(b,"b",function(){return f}),c.d(b,"a",function(){return g}),c.d(b,"c",function(){return h});var d=c(9),e=c(18);function f(a){var b=[],c=[],d=[],f=[],g=a.width||a.size||1,h=a.height||a.size||1,i=0===a.sideOrientation?0:a.sideOrientation||e.a.DEFAULTSIDE;g=g/2;h=h/2;c.push(-g,-h,0),d.push(0,0,-1),f.push(0,0),c.push(g,-h,0),d.push(0,0,-1),f.push(1,0),c.push(g,h,0),d.push(0,0,-1),f.push(1,1),c.push(-g,h,0),d.push(0,0,-1),f.push(0,1),b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),e.a._ComputeSides(i,c,b,d,f,a.frontUVs,a.backUVs);g=new e.a;return g.indices=b,g.positions=c,g.normals=d,g.uvs=f,g}function g(a,b,c){void 0===c&&(c=null);a=new d.a(a,c);return b.sideOrientation=d.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,f(b).applyToMesh(a,b.updatable),b.sourcePlane&&(a.translate(b.sourcePlane.normal,-b.sourcePlane.d),a.setDirection(b.sourcePlane.normal.scale(-1))),a}var h={CreatePlane:g};e.a.CreatePlane=f,d.a.CreatePlane=function(a,b,c,d,e){return g(a,{size:b,width:b,height:b,sideOrientation:e,updatable:d},c)}},function(a,b,c){c.d(b,"a",function(){return f});var d=c(7),e=c(12),f=(c(179),function(){function a(a,b,c,d){this._valueCache={},this._engine=a,this._noUBO=!a.supportsUniformBuffers,this._dynamic=c,this._name=null!=d?d:"no-name",this._data=b||[],this._uniformLocations={},this._uniformSizes={},this._uniformArraySizes={},this._uniformLocationPointer=0,this._needSync=!1,this._engine._features.trackUbosInFrame&&(this._buffers=[],this._bufferIndex=-1,this._createBufferOnWrite=!1,this._currentFrameId=0),this._noUBO?(this.updateMatrix3x3=this._updateMatrix3x3ForEffect,this.updateMatrix2x2=this._updateMatrix2x2ForEffect,this.updateFloat=this._updateFloatForEffect,this.updateFloat2=this._updateFloat2ForEffect,this.updateFloat3=this._updateFloat3ForEffect,this.updateFloat4=this._updateFloat4ForEffect,this.updateFloatArray=this._updateFloatArrayForEffect,this.updateArray=this._updateArrayForEffect,this.updateIntArray=this._updateIntArrayForEffect,this.updateMatrix=this._updateMatrixForEffect,this.updateMatrices=this._updateMatricesForEffect,this.updateVector3=this._updateVector3ForEffect,this.updateVector4=this._updateVector4ForEffect,this.updateColor3=this._updateColor3ForEffect,this.updateColor4=this._updateColor4ForEffect,this.updateDirectColor4=this._updateDirectColor4ForEffect,this.updateInt=this._updateIntForEffect,this.updateInt2=this._updateInt2ForEffect,this.updateInt3=this._updateInt3ForEffect,this.updateInt4=this._updateInt4ForEffect):(this._engine._uniformBuffers.push(this),this.updateMatrix3x3=this._updateMatrix3x3ForUniform,this.updateMatrix2x2=this._updateMatrix2x2ForUniform,this.updateFloat=this._updateFloatForUniform,this.updateFloat2=this._updateFloat2ForUniform,this.updateFloat3=this._updateFloat3ForUniform,this.updateFloat4=this._updateFloat4ForUniform,this.updateFloatArray=this._updateFloatArrayForUniform,this.updateArray=this._updateArrayForUniform,this.updateIntArray=this._updateIntArrayForUniform,this.updateMatrix=this._updateMatrixForUniform,this.updateMatrices=this._updateMatricesForUniform,this.updateVector3=this._updateVector3ForUniform,this.updateVector4=this._updateVector4ForUniform,this.updateColor3=this._updateColor3ForUniform,this.updateColor4=this._updateColor4ForUniform,this.updateDirectColor4=this._updateDirectColor4ForUniform,this.updateInt=this._updateIntForUniform,this.updateInt2=this._updateInt2ForUniform,this.updateInt3=this._updateInt3ForUniform,this.updateInt4=this._updateInt4ForUniform)}return Object.defineProperty(a.prototype,"useUbo",{get:function(){return!this._noUBO},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSync",{get:function(){return!this._needSync},enumerable:!1,configurable:!0}),a.prototype.isDynamic=function(){return void 0!==this._dynamic},a.prototype.getData=function(){return this._bufferData},a.prototype.getBuffer=function(){return this._buffer},a.prototype._fillAlignment=function(a){if(a=a<=2?a:4,this._uniformLocationPointer%a!=0){var b=this._uniformLocationPointer;this._uniformLocationPointer+=a-this._uniformLocationPointer%a;for(a=this._uniformLocationPointer-b,b=0;b0){if(b instanceof Array)throw"addUniform should not be use with Array in UBO: "+a;(this._fillAlignment(4),this._uniformArraySizes[a]={strideSize:b,arraySize:c},16==b)?b*=c:b=b*c+(4-b)*c;c=[];for(var d=0;d0?(this._needSync=0!==this._bufferIndex,this._bufferIndex=0,this._buffer=this._buffers[this._bufferIndex][0]):this._bufferIndex=-1,this._currentEffect&&this._buffer&&this._currentEffect.bindUniformBuffer(this._buffer,this._currentEffectName))},a.prototype.updateUniform=function(a,b,c){this._checkNewFrame();var f=this._uniformLocations[a];if(void 0===f){if(this._buffer)return void d.a.Error("Cannot add an uniform after UBO has been created.");this.addUniform(a,c),f=this._uniformLocations[a]}if(this._buffer||this.create(),this._dynamic)for(a=0;aj.max||j.min>i.max)},m=function(){function a(a,b,c){this._isLocked=!1,this.boundingBox=new g.a(a,b,c),this.boundingSphere=new h.a(a,b,c)}return a.prototype.reConstruct=function(a,b,c){this.boundingBox.reConstruct(a,b,c),this.boundingSphere.reConstruct(a,b,c)},Object.defineProperty(a.prototype,"minimum",{get:function(){return this.boundingBox.minimum},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maximum",{get:function(){return this.boundingBox.maximum},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isLocked",{get:function(){return this._isLocked},set:function(a){this._isLocked=a},enumerable:!1,configurable:!0}),a.prototype.update=function(a){this._isLocked||(this.boundingBox._update(a),this.boundingSphere._update(a))},a.prototype.centerOn=function(b,c){var d=a.TmpVector3[0].copyFrom(b).subtractInPlace(c);b=a.TmpVector3[1].copyFrom(b).addInPlace(c);return this.boundingBox.reConstruct(d,b,this.boundingBox.getWorldMatrix()),this.boundingSphere.reConstruct(d,b,this.boundingBox.getWorldMatrix()),this},a.prototype.encapsulate=function(a){var b=e.e.Minimize(this.minimum,a);a=e.e.Maximize(this.maximum,a);return this.reConstruct(b,a,this.boundingBox.getWorldMatrix()),this},a.prototype.encapsulateBoundingInfo=function(a){return this.encapsulate(a.boundingBox.centerWorld.subtract(a.boundingBox.extendSizeWorld)),this.encapsulate(a.boundingBox.centerWorld.add(a.boundingBox.extendSizeWorld)),this},a.prototype.scale=function(a){return this.boundingBox.scale(a),this.boundingSphere.scale(a),this},a.prototype.isInFrustum=function(a,b){return void 0===b&&(b=f.a.MESHES_CULLINGSTRATEGY_STANDARD),!(b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION&&b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY||!this.boundingSphere.isCenterInFrustum(a))||!!this.boundingSphere.isInFrustum(a)&&(!(b!==f.a.MESHES_CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY&&b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY)||this.boundingBox.isInFrustum(a))},Object.defineProperty(a.prototype,"diagonalLength",{get:function(){var b=this.boundingBox;return b.maximumWorld.subtractToRef(b.minimumWorld,a.TmpVector3[0]).length()},enumerable:!1,configurable:!0}),a.prototype.isCompletelyInFrustum=function(a){return this.boundingBox.isCompletelyInFrustum(a)},a.prototype._checkCollision=function(a){return a._canDoCollision(this.boundingSphere.centerWorld,this.boundingSphere.radiusWorld,this.boundingBox.minimumWorld,this.boundingBox.maximumWorld)},a.prototype.intersectsPoint=function(a){return!!this.boundingSphere.centerWorld&&!!this.boundingSphere.intersectsPoint(a)&&!!this.boundingBox.intersectsPoint(a)},a.prototype.intersects=function(a,b){if(!h.a.Intersects(this.boundingSphere,a.boundingSphere))return!1;if(!g.a.Intersects(this.boundingBox,a.boundingBox))return!1;if(!b)return!0;b=this.boundingBox;a=a.boundingBox;return!!l(b.directions[0],b,a)&&!!l(b.directions[1],b,a)&&!!l(b.directions[2],b,a)&&!!l(a.directions[0],b,a)&&!!l(a.directions[1],b,a)&&!!l(a.directions[2],b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[2]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[2]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[2]),b,a)},a.TmpVector3=d.a.BuildArray(2,e.e.Zero),a}()},function(a,b,c){c.d(b,"a",function(){return o});var d=c(2),e=c(3),f=c(0),g=c(4),h=c(11),i=c(15);a=c(29);b=c(10);var j=c(8),k=c(74),l=c(57),m=c(13),n={effect:null,subMesh:null},o=function(a){function b(b,c,e,g){void 0===g&&(g={});b=a.call(this,b,c)||this;return b._textures={},b._textureArrays={},b._externalTextures={},b._floats={},b._ints={},b._floatsArrays={},b._colors3={},b._colors3Arrays={},b._colors4={},b._colors4Arrays={},b._vectors2={},b._vectors3={},b._vectors4={},b._matrices={},b._matrixArrays={},b._matrices3x3={},b._matrices2x2={},b._vectors2Arrays={},b._vectors3Arrays={},b._vectors4Arrays={},b._uniformBuffers={},b._textureSamplers={},b._storageBuffers={},b._cachedWorldViewMatrix=new f.a,b._cachedWorldViewProjectionMatrix=new f.a,b._multiview=!1,b._shaderPath=e,b._options=Object(d.a)({needAlphaBlending:!1,needAlphaTesting:!1,attributes:["position","normal","uv"],uniforms:["worldViewProjection"],uniformBuffers:[],samplers:[],externalTextures:[],samplerObjects:[],storageBuffers:[],defines:[],useClipPlane:!1},g),b}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"shaderPath",{get:function(){return this._shaderPath},set:function(a){this._shaderPath=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"options",{get:function(){return this._options},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"ShaderMaterial"},b.prototype.needAlphaBlending=function(){return this.alpha<1||this._options.needAlphaBlending},b.prototype.needAlphaTesting=function(){return this._options.needAlphaTesting},b.prototype._checkUniform=function(a){-1===this._options.uniforms.indexOf(a)&&this._options.uniforms.push(a)},b.prototype.setTexture=function(a,b){return-1===this._options.samplers.indexOf(a)&&this._options.samplers.push(a),this._textures[a]=b,this},b.prototype.setTextureArray=function(a,b){return-1===this._options.samplers.indexOf(a)&&this._options.samplers.push(a),this._checkUniform(a),this._textureArrays[a]=b,this},b.prototype.setExternalTexture=function(a,b){return-1===this._options.externalTextures.indexOf(a)&&this._options.externalTextures.push(a),this._externalTextures[a]=b,this},b.prototype.setFloat=function(a,b){return this._checkUniform(a),this._floats[a]=b,this},b.prototype.setInt=function(a,b){return this._checkUniform(a),this._ints[a]=b,this},b.prototype.setFloats=function(a,b){return this._checkUniform(a),this._floatsArrays[a]=b,this},b.prototype.setColor3=function(a,b){return this._checkUniform(a),this._colors3[a]=b,this},b.prototype.setColor3Array=function(a,b){return this._checkUniform(a),this._colors3Arrays[a]=b.reduce(function(a,b){return b.toArray(a,a.length),a},[]),this},b.prototype.setColor4=function(a,b){return this._checkUniform(a),this._colors4[a]=b,this},b.prototype.setColor4Array=function(a,b){return this._checkUniform(a),this._colors4Arrays[a]=b.reduce(function(a,b){return b.toArray(a,a.length),a},[]),this},b.prototype.setVector2=function(a,b){return this._checkUniform(a),this._vectors2[a]=b,this},b.prototype.setVector3=function(a,b){return this._checkUniform(a),this._vectors3[a]=b,this},b.prototype.setVector4=function(a,b){return this._checkUniform(a),this._vectors4[a]=b,this},b.prototype.setMatrix=function(a,b){return this._checkUniform(a),this._matrices[a]=b,this},b.prototype.setMatrices=function(a,b){this._checkUniform(a);for(var c=new Float32Array(16*b.length),d=0;d1&&(this._multiview=!0,q.push("#define MULTIVIEW"),-1!==this._options.uniforms.indexOf("viewProjection")&&-1===this._options.uniforms.indexOf("viewProjectionR")&&this._options.uniforms.push("viewProjectionR"));for(var t=0;t4&&(f.push(g.b.MatricesIndicesExtraKind),f.push(g.b.MatricesWeightsExtraKind));u=a.skeleton;q.push("#define NUM_BONE_INFLUENCERS "+a.numBoneInfluencers),h.addCPUSkinningFallback(0,a),u.isUsingTextureForMatrices?(q.push("#define BONETEXTURE"),-1===this._options.uniforms.indexOf("boneTextureWidth")&&this._options.uniforms.push("boneTextureWidth"),-1===this._options.samplers.indexOf("boneSampler")&&this._options.samplers.push("boneSampler")):(q.push("#define BonesPerMesh "+(u.bones.length+1)),-1===this._options.uniforms.indexOf("mBones")&&this._options.uniforms.push("mBones"))}else q.push("#define NUM_BONE_INFLUENCERS 0");u=0;var v=a?a.morphTargetManager:null;if(v){var w=v.supportsUVs&&-1!==q.indexOf("#define UV1"),x=v.supportsTangents&&-1!==q.indexOf("#define TANGENT"),y=v.supportsNormals&&-1!==q.indexOf("#define NORMAL");u=v.numInfluencers,w&&q.push("#define MORPHTARGETS_UV"),x&&q.push("#define MORPHTARGETS_TANGENT"),y&&q.push("#define MORPHTARGETS_NORMAL"),u>0&&q.push("#define MORPHTARGETS"),v.isUsingTextureForTargets&&(q.push("#define MORPHTARGETS_TEXTURE"),-1===this._options.uniforms.indexOf("morphTargetTextureIndices")&&this._options.uniforms.push("morphTargetTextureIndices"),-1===this._options.samplers.indexOf("morphTargets")&&this._options.samplers.push("morphTargets")),q.push("#define NUM_MORPH_INFLUENCERS "+u);for(t=0;t0&&((o=o.slice()).push("morphTargetInfluences"),o.push("morphTargetTextureInfo"),o.push("morphTargetTextureIndices"))}else q.push("#define NUM_MORPH_INFLUENCERS 0");for(v in this._textures)if(!this._textures[v].isReady())return!1;a&&this._shouldTurnAlphaTestOn(a)&&q.push("#define ALPHATEST"),(null===this._options.useClipPlane&&e.clipPlane||this._options.useClipPlane)&&(q.push("#define CLIPPLANE"),-1===o.indexOf("vClipPlane")&&o.push("vClipPlane")),(null===this._options.useClipPlane&&e.clipPlane2||this._options.useClipPlane)&&(q.push("#define CLIPPLANE2"),-1===o.indexOf("vClipPlane2")&&o.push("vClipPlane2")),(null===this._options.useClipPlane&&e.clipPlane3||this._options.useClipPlane)&&(q.push("#define CLIPPLANE3"),-1===o.indexOf("vClipPlane3")&&o.push("vClipPlane3")),(null===this._options.useClipPlane&&e.clipPlane4||this._options.useClipPlane)&&(q.push("#define CLIPPLANE4"),-1===o.indexOf("vClipPlane4")&&o.push("vClipPlane4")),(null===this._options.useClipPlane&&e.clipPlane5||this._options.useClipPlane)&&(q.push("#define CLIPPLANE5"),-1===o.indexOf("vClipPlane5")&&o.push("vClipPlane5")),(null===this._options.useClipPlane&&e.clipPlane6||this._options.useClipPlane)&&(q.push("#define CLIPPLANE6"),-1===o.indexOf("vClipPlane6")&&o.push("vClipPlane6")),this.customShaderNameResolve&&(o=o.slice(),r=r.slice(),s=s.slice(),j=this.customShaderNameResolve(j,o,r,s,q,f));x=d;y=q.join(" ");return this._cachedDefines!==y&&(this._cachedDefines=y,d=p.createEffect(j,{attributes:f,uniformsNames:o,uniformBuffersNames:r,samplers:s,defines:y,fallbacks:h,onCompiled:this.onCompiled,onError:this.onError,indexParameters:{maxSimultaneousMorphTargets:u},shaderLanguage:this._options.shaderLanguage},p),this._drawWrapper.effect=d,this._onEffectCreatedObservable&&(n.effect=d,n.subMesh=null!==(t=null!=c?c:null==a?void 0:a.subMeshes[0])&&void 0!==t?t:null,this._onEffectCreatedObservable.notifyObservers(n))),this._effectUsesInstances=!!b,null!==(w=!(null==d?void 0:d.isReady()))&&void 0!==w&&!w&&(x!==d&&e.resetCachedMaterial(),d._wasPreviouslyReady=!0,!0)},b.prototype.bindOnlyWorldMatrix=function(a,b){var c=this.getScene();b=null!=b?b:this.getEffect();b&&(-1!==this._options.uniforms.indexOf("world")&&b.setMatrix("world",a),-1!==this._options.uniforms.indexOf("worldView")&&(a.multiplyToRef(c.getViewMatrix(),this._cachedWorldViewMatrix),b.setMatrix("worldView",this._cachedWorldViewMatrix)),-1!==this._options.uniforms.indexOf("worldViewProjection")&&(a.multiplyToRef(c.getTransformMatrix(),this._cachedWorldViewProjectionMatrix),b.setMatrix("worldViewProjection",this._cachedWorldViewProjectionMatrix)))},b.prototype.bindForSubMesh=function(a,b,c){this.bind(a,b,null===(a=c._drawWrapperOverride)||void 0===a?void 0:a.effect)},b.prototype.bind=function(a,b,c){this.bindOnlyWorldMatrix(a,c);c=null!=c?c:this.getEffect();var d=this._options.uniformBuffers,e=!1;if(c&&d&&d.length>0&&this.getScene().getEngine().supportsUniformBuffers)for(var f=0;f0&&i.a.BindMorphTargetParameters(b,c)}e=this.getEffect();this._drawWrapper.effect=c,this._afterBind(b,c),this._drawWrapper.effect=e},b.prototype._afterBind=function(b,c){void 0===c&&(c=null),a.prototype._afterBind.call(this,b,c),this.getScene()._cachedEffect=c},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);for(var c in this._textures)b.push(this._textures[c]);for(var c in this._textureArrays)for(var d=this._textureArrays[c],e=0;e1)throw"Multiple drag modes specified in dragBehavior options. Only one expected"}return Object.defineProperty(a.prototype,"currentDraggingPointerID",{get:function(){return this.currentDraggingPointerId},set:function(a){this.currentDraggingPointerId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enabled",{get:function(){return this._enabled},set:function(a){a!=this._enabled&&this.onEnabledObservable.notifyObservers(a),this._enabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"options",{get:function(){return this._options},set:function(a){this._options=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"PointerDrag"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(b,c){var f=this;this._scene=b.getScene(),b.isNearGrabbable=!0,this.attachedNode=b,a._planeScene||(this._debugMode?a._planeScene=this._scene:(a._planeScene=new e.a(this._scene.getEngine(),{virtual:!0}),a._planeScene.detachControl(),this._scene.onDisposeObservable.addOnce(function(){a._planeScene.dispose(),a._planeScene=null}))),this._dragPlane=Object(k.a)("pointerDragPlane",{size:this._debugMode?1:1e4,updatable:!1,sideOrientation:d.a.DOUBLESIDE},a._planeScene),this.lastDragPosition=new g.e(0,0,0);var l=c||function(a){return f.attachedNode==a||a.isDescendantOf(f.attachedNode)};this._pointerObserver=this._scene.onPointerObservable.add(function(b,c){if(f.enabled){if(b.type==h.a.POINTERDOWN)f.startAndReleaseDragOnPointerEvents&&!f.dragging&&b.pickInfo&&b.pickInfo.hit&&b.pickInfo.pickedMesh&&b.pickInfo.pickedPoint&&b.pickInfo.ray&&l(b.pickInfo.pickedMesh)&&f._startDrag(b.event.pointerId,b.pickInfo.ray,b.pickInfo.pickedPoint);else if(b.type==h.a.POINTERUP)f.startAndReleaseDragOnPointerEvents&&f.currentDraggingPointerId==b.event.pointerId&&f.releaseDrag();else if(b.type==h.a.POINTERMOVE){c=b.event.pointerId;if(f.currentDraggingPointerId===a._AnyMouseId&&c!==a._AnyMouseId){var d=b.event;("mouse"===d.pointerType||!f._scene.getEngine().hostInformation.isMobile&&d instanceof MouseEvent)&&(f._lastPointerRay[f.currentDraggingPointerId]&&(f._lastPointerRay[c]=f._lastPointerRay[f.currentDraggingPointerId],delete f._lastPointerRay[f.currentDraggingPointerId]),f.currentDraggingPointerId=c)}f._lastPointerRay[c]||(f._lastPointerRay[c]=new i.a(new g.e,new g.e)),b.pickInfo&&b.pickInfo.ray&&(f._lastPointerRay[c].origin.copyFrom(b.pickInfo.ray.origin),f._lastPointerRay[c].direction.copyFrom(b.pickInfo.ray.direction),f.currentDraggingPointerId==c&&f.dragging&&f._moveDrag(b.pickInfo.ray))}}else f._attachedToElement&&f.releaseDrag()}),this._beforeRenderObserver=this._scene.onBeforeRenderObservable.add(function(){f._moving&&f.moveAttached&&(j.a._RemoveAndStorePivotPoint(f.attachedNode),f._targetPosition.subtractToRef(f.attachedNode.absolutePosition,f._tmpVector),f._tmpVector.scaleInPlace(f.dragDeltaRatio),f.attachedNode.getAbsolutePosition().addToRef(f._tmpVector,f._tmpVector),f.validateDrag(f._tmpVector)&&f.attachedNode.setAbsolutePosition(f._tmpVector),j.a._RestorePivotPoint(f.attachedNode))})},a.prototype.releaseDrag=function(){if(this.dragging&&(this.dragging=!1,this.onDragEndObservable.notifyObservers({dragPlanePoint:this.lastDragPosition,pointerId:this.currentDraggingPointerId})),this.currentDraggingPointerId=-1,this._moving=!1,this.detachCameraControls&&this._attachedToElement&&this._scene.activeCamera&&!this._scene.activeCamera.leftCamera){if("ArcRotateCamera"===this._scene.activeCamera.getClassName()){var a=this._scene.activeCamera;a.attachControl(!a.inputs||a.inputs.noPreventDefault,a._useCtrlForPanning,a._panningMouseButton)}else this._scene.activeCamera.attachControl(!this._scene.activeCamera.inputs||this._scene.activeCamera.inputs.noPreventDefault);this._attachedToElement=!1}},a.prototype.startDrag=function(b,c,d){void 0===b&&(b=a._AnyMouseId),this._startDrag(b,c,d);c=this._lastPointerRay[b];b===a._AnyMouseId&&(c=this._lastPointerRay[Object.keys(this._lastPointerRay)[0]]),c&&this._moveDrag(c)},a.prototype._startDrag=function(a,b,c){if(this._scene.activeCamera&&!this.dragging&&this.attachedNode){j.a._RemoveAndStorePivotPoint(this.attachedNode),b?(this._startDragRay.direction.copyFrom(b.direction),this._startDragRay.origin.copyFrom(b.origin)):(this._startDragRay.origin.copyFrom(this._scene.activeCamera.position),this.attachedNode.getWorldMatrix().getTranslationToRef(this._tmpVector),this._tmpVector.subtractToRef(this._scene.activeCamera.position,this._startDragRay.direction)),this._updateDragPlanePosition(this._startDragRay,c||this._tmpVector);b=this._pickWithRayOnDragPlane(this._startDragRay);b&&(this.dragging=!0,this.currentDraggingPointerId=a,this.lastDragPosition.copyFrom(b),this.onDragStartObservable.notifyObservers({dragPlanePoint:b,pointerId:this.currentDraggingPointerId}),this._targetPosition.copyFrom(this.attachedNode.getAbsolutePosition()),this.detachCameraControls&&this._scene.activeCamera&&this._scene.activeCamera.inputs&&!this._scene.activeCamera.leftCamera&&(this._scene.activeCamera.inputs.attachedToElement?(this._scene.activeCamera.detachControl(),this._attachedToElement=!0):this._attachedToElement=!1)),j.a._RestorePivotPoint(this.attachedNode)}},a.prototype._moveDrag=function(a){this._moving=!0;var b=this._pickWithRayOnDragPlane(a);if(b){this.updateDragPlane&&this._updateDragPlanePosition(a,b);a=0;this._options.dragAxis?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragAxis,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._worldDragAxis):this._worldDragAxis.copyFrom(this._options.dragAxis),b.subtractToRef(this.lastDragPosition,this._tmpVector),a=g.e.Dot(this._tmpVector,this._worldDragAxis),this._worldDragAxis.scaleToRef(a,this._dragDelta)):(a=this._dragDelta.length(),b.subtractToRef(this.lastDragPosition,this._dragDelta)),this._targetPosition.addInPlace(this._dragDelta),this.onDragObservable.notifyObservers({dragDistance:a,delta:this._dragDelta,dragPlanePoint:b,dragPlaneNormal:this._dragPlane.forward,pointerId:this.currentDraggingPointerId}),this.lastDragPosition.copyFrom(b)}},a.prototype._pickWithRayOnDragPlane=function(b){var c=this;if(!b)return null;var d=Math.acos(g.e.Dot(this._dragPlane.forward,b.direction));if(d>Math.PI/2&&(d=Math.PI-d),this.maxDragAngle>0&&d>this.maxDragAngle){if(this._useAlternatePickedPointAboveMaxDragAngle){this._tmpVector.copyFrom(b.direction),this.attachedNode.absolutePosition.subtractToRef(b.origin,this._alternatePickedPoint),this._alternatePickedPoint.normalize(),this._alternatePickedPoint.scaleInPlace(this._useAlternatePickedPointAboveMaxDragAngleDragSpeed*g.e.Dot(this._alternatePickedPoint,this._tmpVector)),this._tmpVector.addInPlace(this._alternatePickedPoint);d=g.e.Dot(this._dragPlane.forward,this._tmpVector);return this._dragPlane.forward.scaleToRef(-d,this._alternatePickedPoint),this._alternatePickedPoint.addInPlace(this._tmpVector),this._alternatePickedPoint.addInPlace(this.attachedNode.absolutePosition),this._alternatePickedPoint}return null}d=a._planeScene.pickWithRay(b,function(a){return a==c._dragPlane});return d&&d.hit&&d.pickedMesh&&d.pickedPoint?d.pickedPoint:null},a.prototype._updateDragPlanePosition=function(a,b){this._pointA.copyFrom(b),this._options.dragAxis?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragAxis,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis):this._localAxis.copyFrom(this._options.dragAxis),a.origin.subtractToRef(this._pointA,this._pointC),this._pointC.normalize(),Math.abs(g.e.Dot(this._localAxis,this._pointC))>.999?Math.abs(g.e.Dot(g.e.UpReadOnly,this._pointC))>.999?this._lookAt.copyFrom(g.e.Right()):this._lookAt.copyFrom(g.e.UpReadOnly):(g.e.CrossToRef(this._localAxis,this._pointC,this._lookAt),g.e.CrossToRef(this._localAxis,this._lookAt,this._lookAt),this._lookAt.normalize()),this._dragPlane.position.copyFrom(this._pointA),this._pointA.addToRef(this._lookAt,this._lookAt),this._dragPlane.lookAt(this._lookAt)):this._options.dragPlaneNormal?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragPlaneNormal,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis):this._localAxis.copyFrom(this._options.dragPlaneNormal),this._dragPlane.position.copyFrom(this._pointA),this._pointA.addToRef(this._localAxis,this._lookAt),this._dragPlane.lookAt(this._lookAt)):(this._dragPlane.position.copyFrom(this._pointA),this._dragPlane.lookAt(a.origin)),this._dragPlane.position.copyFrom(this.attachedNode.getAbsolutePosition()),this._dragPlane.computeWorldMatrix(!0)},a.prototype.detach=function(){this.attachedNode.isNearGrabbable=!1,this._pointerObserver&&this._scene.onPointerObservable.remove(this._pointerObserver),this._beforeRenderObserver&&this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver),this._dragPlane.dispose(),this.releaseDrag()},a._AnyMouseId=-2,a}()},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f}),c.d(b,"c",function(){return g});var d=c(2),e=function(){function a(){}return a.KEYDOWN=1,a.KEYUP=2,a}(),f=function(a,b){this.type=a,this.event=b},g=function(a){function b(b,c){var d=a.call(this,b,c)||this;return d.type=b,d.event=c,d.skipOnPointerObservable=!1,d}return Object(d.d)(b,a),b}(f)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._defines={},this._currentRank=32,this._maxRank=-1,this._mesh=null}return a.prototype.unBindMesh=function(){this._mesh=null},a.prototype.addFallback=function(a,b){this._defines[a]||(athis._maxRank&&(this._maxRank=a),this._defines[a]=new Array),this._defines[a].push(b)},a.prototype.addCPUSkinningFallback=function(a,b){this._mesh=b,athis._maxRank&&(this._maxRank=a)},Object.defineProperty(a.prototype,"hasMoreFallbacks",{get:function(){return this._currentRank<=this._maxRank},enumerable:!1,configurable:!0}),a.prototype.reduce=function(a,b){if(this._mesh&&this._mesh.computeBonesUsingShaders&&this._mesh.numBoneInfluencers>0){this._mesh.computeBonesUsingShaders=!1,a=a.replace("#define NUM_BONE_INFLUENCERS "+this._mesh.numBoneInfluencers,"#define NUM_BONE_INFLUENCERS 0"),b._bonesComputationForcedToCPU=!0;for(var c=this._mesh.getScene(),d=0;d0&&(e.computeBonesUsingShaders=!1)}}else{g=this._defines[this._currentRank];if(g)for(d=0;d=this.subMaterials.length?this.getScene().defaultMaterial:this.subMaterials[a]},b.prototype.getActiveTextures=function(){var b;return(b=a.prototype.getActiveTextures.call(this)).concat.apply(b,this.subMaterials.map(function(a){return a?a.getActiveTextures():[]}))},b.prototype.hasTexture=function(b){var c;if(a.prototype.hasTexture.call(this,b))return!0;for(var d=0;d=0&&e.multiMaterials.splice(d,1),a.prototype.dispose.call(this,b,c)}},b.ParseMultiMaterial=function(a,c){var d=new b(a.name,c);d.id=a.id,e.a&&e.a.AddTagsTo(d,a.tags);for(var f=0;f0&&(this._indexBuffer=this._engine.createIndexBuffer(this._indices,this._updatable)),a._syncGeometryWithMorphTargetManager(),a.synchronizeInstances()},a.prototype.notifyUpdate=function(a){this.onGeometryUpdated&&this.onGeometryUpdated(this,a),this._vertexArrayObjects&&this._disposeVertexArrayObjects();for(var a=0,b=this._meshes;a0){for(var b=0;b0){for(b=0;b0){for(b=0;b-1&&this._parentContainer.geometries.splice(c,1),this._parentContainer=null}this._isDisposed=!0},a.prototype.copy=function(b){var c=new f.a;c.indices=[];var d=this.getIndices();if(d)for(var e=0;e0){var i=new Float32Array(b,f.positionsAttrDesc.offset,f.positionsAttrDesc.count);c.setVerticesData(g.b.PositionKind,i,!1)}if(f.normalsAttrDesc&&f.normalsAttrDesc.count>0){i=new Float32Array(b,f.normalsAttrDesc.offset,f.normalsAttrDesc.count);c.setVerticesData(g.b.NormalKind,i,!1)}if(f.tangetsAttrDesc&&f.tangetsAttrDesc.count>0){i=new Float32Array(b,f.tangetsAttrDesc.offset,f.tangetsAttrDesc.count);c.setVerticesData(g.b.TangentKind,i,!1)}if(f.uvsAttrDesc&&f.uvsAttrDesc.count>0){i=new Float32Array(b,f.uvsAttrDesc.offset,f.uvsAttrDesc.count);c.setVerticesData(g.b.UVKind,i,!1)}if(f.uvs2AttrDesc&&f.uvs2AttrDesc.count>0){i=new Float32Array(b,f.uvs2AttrDesc.offset,f.uvs2AttrDesc.count);c.setVerticesData(g.b.UV2Kind,i,!1)}if(f.uvs3AttrDesc&&f.uvs3AttrDesc.count>0){i=new Float32Array(b,f.uvs3AttrDesc.offset,f.uvs3AttrDesc.count);c.setVerticesData(g.b.UV3Kind,i,!1)}if(f.uvs4AttrDesc&&f.uvs4AttrDesc.count>0){i=new Float32Array(b,f.uvs4AttrDesc.offset,f.uvs4AttrDesc.count);c.setVerticesData(g.b.UV4Kind,i,!1)}if(f.uvs5AttrDesc&&f.uvs5AttrDesc.count>0){i=new Float32Array(b,f.uvs5AttrDesc.offset,f.uvs5AttrDesc.count);c.setVerticesData(g.b.UV5Kind,i,!1)}if(f.uvs6AttrDesc&&f.uvs6AttrDesc.count>0){i=new Float32Array(b,f.uvs6AttrDesc.offset,f.uvs6AttrDesc.count);c.setVerticesData(g.b.UV6Kind,i,!1)}if(f.colorsAttrDesc&&f.colorsAttrDesc.count>0){i=new Float32Array(b,f.colorsAttrDesc.offset,f.colorsAttrDesc.count);c.setVerticesData(g.b.ColorKind,i,!1,f.colorsAttrDesc.stride)}if(f.matricesIndicesAttrDesc&&f.matricesIndicesAttrDesc.count>0){for(var i=new Int32Array(b,f.matricesIndicesAttrDesc.offset,f.matricesIndicesAttrDesc.count),j=[],k=0;k>8),j.push((16711680&l)>>16),j.push(l>>24&255)}c.setVerticesData(g.b.MatricesIndicesKind,j,!1)}if(f.matricesIndicesExtraAttrDesc&&f.matricesIndicesExtraAttrDesc.count>0){for(i=new Int32Array(b,f.matricesIndicesExtraAttrDesc.offset,f.matricesIndicesExtraAttrDesc.count),j=[],k=0;k>8),j.push((16711680&l)>>16),j.push(l>>24&255));c.setVerticesData(g.b.MatricesIndicesExtraKind,j,!1)}if(f.matricesWeightsAttrDesc&&f.matricesWeightsAttrDesc.count>0){l=new Float32Array(b,f.matricesWeightsAttrDesc.offset,f.matricesWeightsAttrDesc.count);c.setVerticesData(g.b.MatricesWeightsKind,l,!1)}if(f.indicesAttrDesc&&f.indicesAttrDesc.count>0){i=new Int32Array(b,f.indicesAttrDesc.offset,f.indicesAttrDesc.count);c.setIndices(i,null)}if(f.subMeshesAttrDesc&&f.subMeshesAttrDesc.count>0){l=new Int32Array(b,f.subMeshesAttrDesc.offset,5*f.subMeshesAttrDesc.count);c.subMeshes=[];for(k=0;k>8),j.push((16711680&i)>>16),j.push(i>>24&255)}c.setVerticesData(g.b.MatricesIndicesKind,j,b.matricesIndices._updatable)}if(b.matricesIndicesExtra)if(b.matricesIndicesExtra._isExpanded)delete b.matricesIndices._isExpanded,c.setVerticesData(g.b.MatricesIndicesExtraKind,b.matricesIndicesExtra,b.matricesIndicesExtra._updatable);else{for(j=[],k=0;k>8),j.push((16711680&i)>>16),j.push(i>>24&255));c.setVerticesData(g.b.MatricesIndicesExtraKind,j,b.matricesIndicesExtra._updatable)}b.matricesWeights&&(a._CleanMatricesWeights(b,c),c.setVerticesData(g.b.MatricesWeightsKind,b.matricesWeights,b.matricesWeights._updatable)),b.matricesWeightsExtra&&c.setVerticesData(g.b.MatricesWeightsExtraKind,b.matricesWeightsExtra,b.matricesWeights._updatable),c.setIndices(b.indices,null)}if(b.subMeshes){c.subMeshes=[];for(m=0;m-1){var d=b.getScene().getLastSkeletonById(a.skeletonId);if(d){c=d.bones.length;for(var d=b.getVerticesData(g.b.MatricesIndicesKind),e=b.getVerticesData(g.b.MatricesIndicesExtraKind),f=a.matricesWeights,h=a.matricesWeightsExtra,j=a.numBoneInfluencer,k=f.length,m=0;mj-1)&&(o=j-1),n>.001){q=1/n;for(p=0;p<4;p++)f[m+p]*=q;if(h)for(p=0;p<4;p++)h[m+p]*=q}else o>=4?(h[m+o-4]=1-n,e[m+o-4]=c):(f[m+o]=1-n,d[m+o]=c)}b.setVerticesData(g.b.MatricesIndicesKind,d),a.matricesWeightsExtra&&b.setVerticesData(g.b.MatricesIndicesExtraKind,e)}}}},a.Parse=function(b,c,e){if(c.getGeometryById(b.id))return null;var h=new a(b.id,c,void 0,b.updatable);return m.a&&m.a.AddTagsTo(h,b.tags),b.delayLoadingFile?(h.delayLoadState=k.a.DELAYLOADSTATE_NOTLOADED,h.delayLoadingFile=e+b.delayLoadingFile,h._boundingInfo=new j.a(d.e.FromArray(b.boundingBoxMinimum),d.e.FromArray(b.boundingBoxMaximum)),h._delayInfo=[],b.hasUVs&&h._delayInfo.push(g.b.UVKind),b.hasUVs2&&h._delayInfo.push(g.b.UV2Kind),b.hasUVs3&&h._delayInfo.push(g.b.UV3Kind),b.hasUVs4&&h._delayInfo.push(g.b.UV4Kind),b.hasUVs5&&h._delayInfo.push(g.b.UV5Kind),b.hasUVs6&&h._delayInfo.push(g.b.UV6Kind),b.hasColors&&h._delayInfo.push(g.b.ColorKind),b.hasMatricesIndices&&h._delayInfo.push(g.b.MatricesIndicesKind),b.hasMatricesWeights&&h._delayInfo.push(g.b.MatricesWeightsKind),h._delayLoadingFunction=f.a.ImportVertexData):f.a.ImportVertexData(b,h),c.pushGeometry(h,!0),h},a}()},function(a,b,c){c.d(b,"e",function(){return d}),c.d(b,"c",function(){return h}),c.d(b,"a",function(){return i}),c.d(b,"b",function(){return j}),c.d(b,"f",function(){return k}),c.d(b,"g",function(){return l}),c.d(b,"d",function(){return m});var d,e=c(14),f=c(0),g=c(22);!function(a){a[a.CW=0]="CW",a[a.CCW=1]="CCW"}(d||(d={}));var h=function(){function a(){}return a.Interpolate=function(a,b,c,d,e){for(var f=1-3*d+3*b,d=3*d-6*b,b=3*b,g=a,h=0;h<5;h++){var i=g*g;g-=(f*(i*g)+d*i+b*g-a)*(1/(3*f*i+2*d*g+b)),g=Math.min(1,Math.max(0,g))}return 3*Math.pow(1-g,2)*g*c+3*(1-g)*Math.pow(g,2)*e+Math.pow(g,3)},a}(),i=function(){function a(a){this._radians=a,this._radians<0&&(this._radians+=2*Math.PI)}return a.prototype.degrees=function(){return 180*this._radians/Math.PI},a.prototype.radians=function(){return this._radians},a.BetweenTwoPoints=function(b,c){c=c.subtract(b);return new a(Math.atan2(c.y,c.x))},a.FromRadians=function(b){return new a(b)},a.FromDegrees=function(b){return new a(b*Math.PI/180)},a}(),j=function(a,b,c){this.startPoint=a,this.midPoint=b,this.endPoint=c;var e=Math.pow(b.x,2)+Math.pow(b.y,2),g=(Math.pow(a.x,2)+Math.pow(a.y,2)-e)/2;e=(e-Math.pow(c.x,2)-Math.pow(c.y,2))/2;var h=(a.x-b.x)*(b.y-c.y)-(b.x-c.x)*(a.y-b.y);this.centerPoint=new f.d((g*(b.y-c.y)-e*(a.y-b.y))/h,((a.x-b.x)*e-(b.x-c.x)*g)/h),this.radius=this.centerPoint.subtract(this.startPoint).length(),this.startAngle=i.BetweenTwoPoints(this.centerPoint,this.startPoint);a=this.startAngle.degrees();e=i.BetweenTwoPoints(this.centerPoint,this.midPoint).degrees();b=i.BetweenTwoPoints(this.centerPoint,this.endPoint).degrees();e-a>180&&(e-=360),e-a<-180&&(e+=360),b-e>180&&(b-=360),b-e<-180&&(b+=360),this.orientation=e-a<0?d.CW:d.CCW,this.angle=i.FromDegrees(this.orientation===d.CW?a-b:b-a)},k=function(){function a(a,b){this._points=new Array,this._length=0,this.closed=!1,this._points.push(new f.d(a,b))}return a.prototype.addLineTo=function(a,b){if(this.closed)return this;a=new f.d(a,b);b=this._points[this._points.length-1];return this._points.push(a),this._length+=a.subtract(b).length(),this},a.prototype.addArcTo=function(a,b,c,e,g){if(void 0===g&&(g=36),this.closed)return this;var h=this._points[this._points.length-1];a=new f.d(a,b);b=new f.d(c,e);c=new j(h,a,b);e=c.angle.radians()/g;c.orientation===d.CW&&(e*=-1);for(h=c.startAngle.radians()+e,a=0;a1)return f.d.Zero();for(var a=a*this.length(),b=0,c=0;c=b&&a<=g){d=d.normalize();var h=a-b;return new f.d(e.x+d.x*h,e.y+d.y*h)}b=g}return f.d.Zero()},a.StartingAt=function(b,c){return new a(b,c)},a}(),l=function(){function a(a,b,c,d){void 0===b&&(b=null),void 0===d&&(d=!1),this.path=a,this._curve=new Array,this._distances=new Array,this._tangents=new Array,this._normals=new Array,this._binormals=new Array,this._pointAtData={id:0,point:f.e.Zero(),previousPointArrayIndex:0,position:0,subPosition:0,interpolateReady:!1,interpolationMatrix:f.a.Identity()};for(var e=0;ec){var d=b;b=c,c=d}d=this.getCurve();var e=this.getPointAt(b),f=this.getPreviousPointIndexAt(b),g=this.getPointAt(c),h=this.getPreviousPointIndexAt(c)+1,i=[];return 0!==b&&(f++,i.push(e)),i.push.apply(i,d.slice(f,h)),1===c&&1!==b||i.push(g),new a(i,this.getNormalAt(b),this._raw,this._alignTangentsWithPath)},a.prototype.update=function(a,b,c){void 0===b&&(b=null),void 0===c&&(c=!1);for(var d=0;db+1;)b++,c=this._curve[a].subtract(this._curve[a-b]);return c},a.prototype._normalVector=function(a,b){var c=a.length();(0===c&&(c=1),null==b)?(c=e.a.WithinEpsilon(Math.abs(a.y)/c,1,g.a)?e.a.WithinEpsilon(Math.abs(a.x)/c,1,g.a)?e.a.WithinEpsilon(Math.abs(a.z)/c,1,g.a)?f.e.Zero():new f.e(0,0,1):new f.e(1,0,0):new f.e(0,-1,0),c=f.e.Cross(a,c)):(c=f.e.Cross(a,b),f.e.CrossToRef(c,a,c));return c.normalize(),c},a.prototype._updatePointAtData=function(a,b){if(void 0===b&&(b=!1),this._pointAtData.id===a)return this._pointAtData.interpolateReady||this._updateInterpolationMatrix(),this._pointAtData;this._pointAtData.id=a;var c=this.getPoints();if(a<=0)return this._setPointAtData(0,0,c[0],0,b);if(a>=1)return this._setPointAtData(1,1,c[c.length-1],c.length-1,b);for(var d,e=c[0],g=0,h=a*this.length(),i=1;ih){j=(g-h)/j;var k=e.subtract(d);k=d.add(k.scaleInPlace(j));return this._setPointAtData(a,1-j,k,i-1,b)}e=d}return this._pointAtData},a.prototype._setPointAtData=function(a,b,c,d,e){return this._pointAtData.point=c,this._pointAtData.position=a,this._pointAtData.subPosition=b,this._pointAtData.previousPointArrayIndex=d,this._pointAtData.interpolateReady=e,e&&this._updateInterpolationMatrix(),this._pointAtData},a.prototype._updateInterpolationMatrix=function(){this._pointAtData.interpolationMatrix=f.a.Identity();var a=this._pointAtData.previousPointArrayIndex;if(a!==this._tangents.length-1){var b=a+1,c=this._tangents[a].clone(),d=this._normals[a].clone();a=this._binormals[a].clone();var e=this._tangents[b].clone(),g=this._normals[b].clone();b=this._binormals[b].clone();d=f.b.RotationQuaternionFromAxis(d,a,c);a=f.b.RotationQuaternionFromAxis(g,b,e);f.b.Slerp(d,a,this._pointAtData.subPosition).toRotationMatrix(this._pointAtData.interpolationMatrix)}},a}(),m=function(){function a(a){this._length=0,this._points=a,this._length=this._computeLength(a)}return a.CreateQuadraticBezier=function(b,c,d,e){e=e>2?e:3;for(var g=new Array,h=function(a,b,c,d){return(1-a)*(1-a)*b+2*a*(1-a)*c+a*a*d},i=0;i<=e;i++)g.push(new f.e(h(i/e,b.x,c.x,d.x),h(i/e,b.y,c.y,d.y),h(i/e,b.z,c.z,d.z)));return new a(g)},a.CreateCubicBezier=function(b,c,d,e,g){g=g>3?g:4;for(var h=new Array,i=function(a,b,c,d,e){return(1-a)*(1-a)*(1-a)*b+3*a*(1-a)*(1-a)*c+3*a*a*(1-a)*d+a*a*a*e},j=0;j<=g;j++)h.push(new f.e(i(j/g,b.x,c.x,d.x,e.x),i(j/g,b.y,c.y,d.y,e.y),i(j/g,b.z,c.z,d.z,e.z)));return new a(h)},a.CreateHermiteSpline=function(b,c,d,e,g){for(var h=new Array,i=1/g,j=0;j<=g;j++)h.push(f.e.Hermite(b,c,d,e,j*i));return new a(h)},a.CreateCatmullRomSpline=function(b,c,d){var e=new Array,g=1/c,h=0;if(d){for(var d=b.length,i=0;io.snapDistance){var c=Math.floor(Math.abs(p)/o.snapDistance);p%=o.snapDistance,a.delta.normalizeToRef(q),q.scaleInPlace(o.snapDistance*c),o.attachedNode.getWorldMatrix().getTranslationToRef(r),r.addInPlace(q),o.dragBehavior.validateDrag(r)&&(o.attachedNode.getWorldMatrix().addTranslationFromFloats(q.x,q.y,q.z),o.attachedNode.updateCache(),s.snapDistance=o.snapDistance*c,o.onSnapObservable.notifyObservers(s),b=!0)}b&&o._matrixChanged()}}),o.dragBehavior.onDragStartObservable.add(function(){o._dragging=!0}),o.dragBehavior.onDragEndObservable.add(function(){o._dragging=!1});n=g._getSharedGizmoLight();n.includedOnlyMeshes=n.includedOnlyMeshes.concat(o._rootMesh.getChildMeshes(!1));var t={gizmoMeshes:i.getChildMeshes(),colliderMeshes:d.getChildMeshes(),material:o._coloredMaterial,hoverMaterial:o._hoverMaterial,disableMaterial:o._disableMaterial,active:!1,dragBehavior:o.dragBehavior};return null===(c=o._parent)||void 0===c||c.addToAxisCache(d,t),o._pointerObserver=g.utilityLayerScene.onPointerObservable.add(function(a){if(!o._customMeshSet&&(o._isHovered=!(-1==t.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!o._parent)){a=o.dragBehavior.enabled?o._isHovered||o._dragging?o._hoverMaterial:o._coloredMaterial:o._disableMaterial;o._setGizmoMeshMaterial(t.gizmoMeshes,a)}}),o.dragBehavior.onEnabledObservable.add(function(a){o._setGizmoMeshMaterial(t.gizmoMeshes,a?t.material:t.disableMaterial)}),o}return Object(d.d)(b,a),b._CreateArrow=function(a,b,c,d){void 0===c&&(c=1),void 0===d&&(d=!1);var e=new g.a("arrow",a),f=Object(i.a)("cylinder",{diameterTop:0,height:.075,diameterBottom:.0375*(1+(c-1)/4),tessellation:96},a);c=Object(i.a)("cylinder",{diameterTop:.005*c,height:.275,diameterBottom:.005*c,tessellation:96},a);return f.parent=e,f.material=b,f.rotation.x=Math.PI/2,f.position.z+=.3,c.parent=e,c.material=b,c.position.z+=.1375,c.rotation.x=Math.PI/2,d&&(c.visibility=0,f.visibility=0),e},b._CreateArrowInstance=function(a,b){for(var a=new g.a("arrow",a),c=0,b=b.getChildMeshes();ch?h:Math.floor(i);var j,k,l;h=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE;var m=a.uvs,n=a.colors,o=[],p=[],q=[],r=[],s=[],t=[],u=[],v=[],w=[],x=[];if(b.length<2){var y=[],z=[];for(k=0;k0&&(C=A[l].subtract(A[l-1]).length()+u[j],s[j].push(C),u[j]=C),l++;d&&(l--,o.push(A[0].x,A[0].y,A[0].z),C=A[l].subtract(A[0]).length()+u[j],s[j].push(C),u[j]=C),w[j]=B+y,x[j]=i,i+=B+y}l=null;A=null;for(k=0;k0 #ifdef BONETEXTURE uniform sampler2D boneSampler; uniform float boneTextureWidth; #else uniform mat4 mBones[BonesPerMesh]; #ifdef BONES_VELOCITY_ENABLED uniform mat4 mPreviousBones[BonesPerMesh]; #endif #endif attribute vec4 matricesIndices; attribute vec4 matricesWeights; #if NUM_BONE_INFLUENCERS>4 attribute vec4 matricesIndicesExtra; attribute vec4 matricesWeightsExtra; #endif #ifdef BONETEXTURE #define inline mat4 readMatrixFromRawSampler(sampler2D smp,float index) { float offset=index*4.0; float dx=1.0/boneTextureWidth; vec4 m0=texture2D(smp,vec2(dx*(offset+0.5),0.)); vec4 m1=texture2D(smp,vec2(dx*(offset+1.5),0.)); vec4 m2=texture2D(smp,vec2(dx*(offset+2.5),0.)); vec4 m3=texture2D(smp,vec2(dx*(offset+3.5),0.)); return mat4(m0,m1,m2,m3); } #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="instancesVertex";b="#ifdef INSTANCES mat4 finalWorld=mat4(world0,world1,world2,world3); #if defined(PREPASS_VELOCITY) || defined(VELOCITY) mat4 finalPreviousWorld=mat4(previousWorld0,previousWorld1,previousWorld2,previousWorld3); #endif #ifdef THIN_INSTANCES finalWorld=world*finalWorld; #if defined(PREPASS_VELOCITY) || defined(VELOCITY) finalPreviousWorld=previousWorld*finalPreviousWorld; #endif #endif #else mat4 finalWorld=world; #if defined(PREPASS_VELOCITY) || defined(VELOCITY) mat4 finalPreviousWorld=previousWorld; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#if NUM_BONE_INFLUENCERS>0 mat4 influence; #ifdef BONETEXTURE influence=readMatrixFromRawSampler(boneSampler,matricesIndices[0])*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[1])*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[2])*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[3])*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[0])*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[1])*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[2])*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[3])*matricesWeightsExtra[3]; #endif #else influence=mBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence+=mBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence+=mBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence+=mBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence+=mBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence+=mBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence+=mBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence+=mBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif #endif finalWorld=finalWorld*influence; #endif";c(5).a.IncludesShadersStore.bonesVertex=a},function(a,b,c){c.d(b,"a",function(){return f});var d=c(1),e=c(84),f=function(){function a(a,b){void 0===b&&(b=!0),this._wrapU=d.a.TEXTURE_WRAP_ADDRESSMODE,this._wrapV=d.a.TEXTURE_WRAP_ADDRESSMODE,this.wrapR=d.a.TEXTURE_WRAP_ADDRESSMODE,this.anisotropicFilteringLevel=4,this.delayLoadState=d.a.DELAYLOADSTATE_NONE,this._texture=null,this._engine=null,this._cachedSize=e.a.Zero(),this._cachedBaseSize=e.a.Zero(),this._initialSamplingMode=d.a.TEXTURE_BILINEAR_SAMPLINGMODE,this._texture=a,this._texture&&b&&(this._engine=this._texture.getEngine())}return Object.defineProperty(a.prototype,"wrapU",{get:function(){return this._wrapU},set:function(a){this._wrapU=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapV",{get:function(){return this._wrapV},set:function(a){this._wrapV=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"coordinatesMode",{get:function(){return 0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isCube",{get:function(){return!!this._texture&&this._texture.isCube},set:function(a){this._texture&&(this._texture.isCube=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is3D",{get:function(){return!!this._texture&&this._texture.is3D},set:function(a){this._texture&&(this._texture.is3D=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is2DArray",{get:function(){return!!this._texture&&this._texture.is2DArray},set:function(a){this._texture&&(this._texture.is2DArray=a)},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"ThinTexture"},a.prototype.isReady=function(){return this.delayLoadState===d.a.DELAYLOADSTATE_NOTLOADED?(this.delayLoad(),!1):!!this._texture&&this._texture.isReady},a.prototype.delayLoad=function(){},a.prototype.getInternalTexture=function(){return this._texture},a.prototype.getSize=function(){if(this._texture){if(this._texture.width)return this._cachedSize.width=this._texture.width,this._cachedSize.height=this._texture.height,this._cachedSize;if(this._texture._size)return this._cachedSize.width=this._texture._size,this._cachedSize.height=this._texture._size,this._cachedSize}return this._cachedSize},a.prototype.getBaseSize=function(){return this.isReady()&&this._texture?this._texture._size?(this._cachedBaseSize.width=this._texture._size,this._cachedBaseSize.height=this._texture._size,this._cachedBaseSize):(this._cachedBaseSize.width=this._texture.baseWidth,this._cachedBaseSize.height=this._texture.baseHeight,this._cachedBaseSize):(this._cachedBaseSize.width=0,this._cachedBaseSize.height=0,this._cachedBaseSize)},Object.defineProperty(a.prototype,"samplingMode",{get:function(){return this._texture?this._texture.samplingMode:this._initialSamplingMode},enumerable:!1,configurable:!0}),a.prototype.updateSamplingMode=function(a){this._texture&&this._engine&&this._engine.updateTextureSamplingMode(a,this._texture)},a.prototype.releaseInternalTexture=function(){this._texture&&(this._texture.dispose(),this._texture=null)},a.prototype.dispose=function(){this._texture&&(this.releaseInternalTexture(),this._engine=null)},a}()},function(a,b,c){c.d(b,"a",function(){return s});var d=c(39),e=function(){function a(){this.children=[]}return a.prototype.isValid=function(a){return!0},a.prototype.process=function(a,b){var c="";if(this.line){var e=this.line,f=b.processor;f&&((f.lineProcessor&&(e=f.lineProcessor(e,b.isFragment,b.processingContext)),f.attributeProcessor&&Object(d.g)(this.line,"attribute"))?e=f.attributeProcessor(this.line,a,b.processingContext):f.varyingProcessor&&Object(d.g)(this.line,"varying")?e=f.varyingProcessor(this.line,b.isFragment,a,b.processingContext):f.uniformProcessor&&f.uniformRegexp&&f.uniformRegexp.test(this.line)?b.lookForClosingBracketForUniformBuffer||(e=f.uniformProcessor(this.line,b.isFragment,a,b.processingContext)):f.uniformBufferProcessor&&f.uniformBufferRegexp&&f.uniformBufferRegexp.test(this.line)?b.lookForClosingBracketForUniformBuffer||(e=f.uniformBufferProcessor(this.line,b.isFragment,b.processingContext),b.lookForClosingBracketForUniformBuffer=!0):f.textureProcessor&&f.textureRegexp&&f.textureRegexp.test(this.line)?e=f.textureProcessor(this.line,b.isFragment,a,b.processingContext):(f.uniformProcessor||f.uniformBufferProcessor)&&Object(d.g)(this.line,"uniform")&&!b.lookForClosingBracketForUniformBuffer&&(/uniforms+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/.test(this.line)?f.uniformProcessor&&(e=f.uniformProcessor(this.line,b.isFragment,a,b.processingContext)):f.uniformBufferProcessor&&(e=f.uniformBufferProcessor(this.line,b.isFragment,b.processingContext),b.lookForClosingBracketForUniformBuffer=!0)),b.lookForClosingBracketForUniformBuffer&&-1!==this.line.indexOf("}")&&(b.lookForClosingBracketForUniformBuffer=!1,f.endOfUniformBufferProcessor&&(e=f.endOfUniformBufferProcessor(this.line,b.isFragment,b.processingContext))));c+=e+" "}return this.children.forEach(function(d){c+=d.process(a,b)}),this.additionalDefineKey&&(a[this.additionalDefineKey]=this.additionalDefineValue||"true"),c},a}(),f=function(){function a(){}return Object.defineProperty(a.prototype,"currentLine",{get:function(){return this._lines[this.lineIndex]},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"canRead",{get:function(){return this.lineIndex1){for(e();-1!==d&&a._OperatorPriority[g()]>=a._OperatorPriority[m];)c.push(h());f(m),i++}else j+=k;i++}for(e();-1!==d;)"("===g()?h():c.push(h());return c},a._OperatorPriority={")":0,"(":1,"||":2,"&&":3},a._Stack=["","","","","","","","","","","","","","","","","","","",""],a}(),k=function(a){function b(b,c){void 0===c&&(c=!1);var d=a.call(this)||this;return d.define=b,d.not=c,d}return Object(g.d)(b,a),b.prototype.isTrue=function(a){a=void 0!==a[this.define];return this.not&&(a=!a),a},b}(j),l=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(g.d)(b,a),b.prototype.isTrue=function(a){return this.leftOperand.isTrue(a)||this.rightOperand.isTrue(a)},b}(j),m=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(g.d)(b,a),b.prototype.isTrue=function(a){return this.leftOperand.isTrue(a)&&this.rightOperand.isTrue(a)},b}(j),n=function(a){function b(b,c,d){var e=a.call(this)||this;return e.define=b,e.operand=c,e.testValue=d,e}return Object(g.d)(b,a),b.prototype.isTrue=function(a){a=a[this.define];void 0===a&&(a=this.define);var b=!1;a=parseInt(a);var c=parseInt(this.testValue);switch(this.operand){case">":b=a>c;break;case"<":b=a=":b=a>=c;break;case"==":b=a===c}return b},b}(j),o=c(26),p=c(41),q=/defineds*?((.+?))/g,r=/defineds*?[(.+?)]/g,s=function(){function a(){}return a.Initialize=function(a){a.processor&&a.processor.initializeShaders&&a.processor.initializeShaders(a.processingContext)},a.Process=function(a,b,c,d){var e,f=this;(null===(e=b.processor)||void 0===e?void 0:e.preProcessShaderCode)&&(a=b.processor.preProcessShaderCode(a)),this._ProcessIncludes(a,b,function(a){a=f._ProcessShaderConversion(a,b,d);c(a)})},a.PreProcess=function(a,b,c,d){var e,f=this;(null===(e=b.processor)||void 0===e?void 0:e.preProcessShaderCode)&&(a=b.processor.preProcessShaderCode(a)),this._ProcessIncludes(a,b,function(a){a=f._ApplyPreProcessing(a,b,d);c(a)})},a.Finalize=function(a,b,c){return c.processor&&c.processor.finalizeShaders?c.processor.finalizeShaders(a,b,c.processingContext):{vertexCode:a,fragmentCode:b}},a._ProcessPrecision=function(a,b){var c;if(null===(c=b.processor)||void 0===c?void 0:c.noPrecision)return a;c=b.shouldUseHighPrecisionShader;return-1===a.indexOf("precision highp float")?a=c?"precision highp float; "+a:"precision mediump float; "+a:c||(a=a.replace("precision highp float","precision mediump float")),a},a._ExtractOperation=function(a){var b=/defined((.+))/.exec(a);if(b&&b.length)return new k(b[1].trim(),"!"===a[0]);for(var b="",c=0,d=0,e=["==",">=","<=","<",">"];d-1));d++);if(-1===c)return new k(a);e=a.substring(0,c).trim();d=a.substring(c+b.length).trim();return new n(e,b,d)},a._BuildSubExpression=function(a){a=a.replace(q,"defined[$1]");for(var b=[],c=0,a=j.infixToPostfix(a);c=2){var e=b[b.length-1],f=b[b.length-2];b.length-=2;d="&&"==d?new m:new l;"string"==typeof e&&(e=e.replace(r,"defined($1)")),"string"==typeof f&&(f=f.replace(r,"defined($1)")),d.leftOperand="string"==typeof f?this._ExtractOperation(f):f,d.rightOperand="string"==typeof e?this._ExtractOperation(e):e,b.push(d)}}f=b[b.length-1];return"string"==typeof f&&(f=f.replace(r,"defined($1)")),"string"==typeof f?this._ExtractOperation(f):f},a._BuildExpression=function(a,b){var c=new i,d=a.substring(0,b);a=a.substring(b);return a=a.substring(0,(a.indexOf("//")+1||a.length+1)-1).trim(),c.testExpression="#ifdef"===d?new k(a):"#ifndef"===d?new k(a,!0):this._BuildSubExpression(a),c},a._MoveCursorWithinIf=function(a,b,c){for(var d=a.currentLine;this._MoveCursor(a,c);){var f=(d=a.currentLine).substring(0,5).toLowerCase();if("#else"===f){var g=new e;return b.children.push(g),void this._MoveCursor(a,g)}if("#elif"===f){g=this._BuildExpression(d,5);b.children.push(g),c=g}}},a._MoveCursor=function(a,b){for(;a.canRead;){a.lineIndex++;var c=a.currentLine,d=/(#ifdef)|(#else)|(#elif)|(#endif)|(#ifndef)|(#if)/.exec(c);if(d&&d.length)switch(d[0]){case"#ifdef":d=new h;b.children.push(d);var f=this._BuildExpression(c,6);d.children.push(f),this._MoveCursorWithinIf(a,d,f);break;case"#else":case"#elif":return!0;case"#endif":return!1;case"#ifndef":d=new h;b.children.push(d);f=this._BuildExpression(c,7);d.children.push(f),this._MoveCursorWithinIf(a,d,f);break;case"#if":(d=new h,f=this._BuildExpression(c,3)),(b.children.push(d),d.children.push(f),this._MoveCursorWithinIf(a,d,f))}else{d=new e;if(d.line=c,b.children.push(d),"#"===c[0]&&"d"===c[1]){f=c.replace(";","").split(" ");d.additionalDefineKey=f[1],3===f.length&&(d.additionalDefineValue=f[2])}}}return!1},a._EvaluatePreProcessors=function(a,b,c){var d=new e,g=new f;return g.lineIndex=-1,g.lines=a.split(" "),this._MoveCursor(g,d),d.process(b,c)},a._PreparePreProcessors=function(a,b){for(var f,c={},d=0,e=a.defines;d1?f[1]:""}return(null===(f=a.processor)||void 0===f?void 0:f.shaderLanguage)===p.a.GLSL&&(c.GL_ES="true"),c.__VERSION__=a.version,c[a.platformName]="true",b._getGlobalDefines(c),c},a._ProcessShaderConversion=function(a,b,c){a=this._ProcessPrecision(a,b);if(!b.processor)return a;if(b.processor.shaderLanguage===p.a.GLSL&&-1!==a.indexOf("#version 3"))return a.replace("#version 300 es","");var d=b.defines,e=this._PreparePreProcessors(b,c);return b.processor.preProcessor&&(a=b.processor.preProcessor(a,d,b.isFragment,b.processingContext)),a=this._EvaluatePreProcessors(a,e,b),b.processor.postProcessor&&(a=b.processor.postProcessor(a,d,b.isFragment,b.processingContext,c)),c._features.needShaderCodeInlining&&(a=c.inlineShaderCode(a)),a},a._ApplyPreProcessing=function(a,b,c){var d;a=a;var e=b.defines,f=this._PreparePreProcessors(b,c);return(null===(d=b.processor)||void 0===d?void 0:d.preProcessor)&&(a=b.processor.preProcessor(a,e,b.isFragment,b.processingContext)),a=this._EvaluatePreProcessors(a,f,b),(null===(d=b.processor)||void 0===d?void 0:d.postProcessor)&&(a=b.processor.postProcessor(a,e,b.isFragment,b.processingContext,c)),c._features.needShaderCodeInlining&&(a=c.inlineShaderCode(a)),a},a._ProcessIncludes=function(b,c,d){for(var e=this,f=/#includes?<(.+)>(((.*)))*([(.*)])*/g,g=f.exec(b),h=new String(b),i=!1;null!=g;){var j=g[1];if(-1!==j.indexOf("__decl__")&&(j=j.replace(/__decl__/,""),c.supportsUniformBuffers&&(j=(j=j.replace(/Vertex/,"Ubo")).replace(/Fragment/,"Ubo")),j+="Declaration"),!c.includesShadersStore[j]){var k=c.shadersRepository+"ShadersInclude/"+j+".fx";return void a._FileToolsLoadFile(k,function(a){c.includesShadersStore[j]=a,e._ProcessIncludes(h,c,d)})}k=c.includesShadersStore[j];if(g[2])for(var l=g[3].split(","),q=0;q=0||k.indexOf("#include <")>=0,g=f.exec(b)}i?this._ProcessIncludes(h.toString(),c,d):d(h)},a._FileToolsLoadFile=function(a,b,c,d,e,f){throw Object(o.a)("FileTools")},a}()},function(a,b,c){a="instancesDeclaration";b="#ifdef INSTANCES attribute vec4 world0; attribute vec4 world1; attribute vec4 world2; attribute vec4 world3; #if defined(THIN_INSTANCES) && !defined(WORLD_UBO) uniform mat4 world; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) attribute vec4 previousWorld0; attribute vec4 previousWorld1; attribute vec4 previousWorld2; attribute vec4 previousWorld3; #ifdef THIN_INSTANCES uniform mat4 previousWorld; #endif #endif #else #if !defined(WORLD_UBO) uniform mat4 world; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) uniform mat4 previousWorld; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return i});var d=c(2),e=c(3),f=c(0),g=c(8);a=c(30);var h=c(52);a.a.AddNodeConstructor("Light_Type_3",function(a,b){return function(){return new i(a,f.e.Zero(),b)}});var i=function(a){function b(b,c,d){b=a.call(this,b,d)||this;return b.groundColor=new g.a(0,0,0),b.direction=c||f.e.Up(),b}return Object(d.d)(b,a),b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("vLightData",4),this._uniformBuffer.addUniform("vLightDiffuse",4),this._uniformBuffer.addUniform("vLightSpecular",4),this._uniformBuffer.addUniform("vLightGround",3),this._uniformBuffer.addUniform("shadowsInfo",3),this._uniformBuffer.addUniform("depthValues",2),this._uniformBuffer.create()},b.prototype.getClassName=function(){return"HemisphericLight"},b.prototype.setDirectionToTarget=function(a){return this.direction=f.e.Normalize(a.subtract(f.e.Zero())),this.direction},b.prototype.getShadowGenerator=function(){return null},b.prototype.transferToEffect=function(a,b){a=f.e.Normalize(this.direction);return this._uniformBuffer.updateFloat4("vLightData",a.x,a.y,a.z,0,b),this._uniformBuffer.updateColor3("vLightGround",this.groundColor.scale(this.intensity),b),this},b.prototype.transferToNodeMaterialEffect=function(a,b){var c=f.e.Normalize(this.direction);return a.setFloat3(b,c.x,c.y,c.z),this},b.prototype.computeWorldMatrix=function(){return this._worldMatrix||(this._worldMatrix=f.a.Identity()),this._worldMatrix},b.prototype.getTypeID=function(){return h.a.LIGHTTYPEID_HEMISPHERICLIGHT},b.prototype.prepareLightSpecificDefines=function(a,b){a["HEMILIGHT"+b]=!0},Object(d.c)([Object(e.f)()],b.prototype,"groundColor",void 0),Object(d.c)([Object(e.p)()],b.prototype,"direction",void 0),b}(h.a)},function(a,b,c){c.d(b,"a",function(){return f});var d=c(2),e=c(0),f=function(a){function b(b,c){b=a.call(this,b,c)||this;return b._normalMatrix=new e.a,b._storeEffectOnSubMeshes=!0,b}return Object(d.d)(b,a),b.prototype.getEffect=function(){return this._activeEffect},b.prototype.isReady=function(a,b){return!!a&&(!a.subMeshes||0===a.subMeshes.length||this.isReadyForSubMesh(a,a.subMeshes[0],b))},b.prototype._isReadyForSubMesh=function(a){var b=a._materialDefines;return!(this.checkReadyOnEveryCall||!a.effect||!b||b._renderId!==this.getScene().getRenderId())},b.prototype.bindOnlyWorldMatrix=function(a){this._activeEffect.setMatrix("world",a)},b.prototype.bindOnlyNormalMatrix=function(a){this._activeEffect.setMatrix("normalMatrix",a)},b.prototype.bind=function(a,b){b&&this.bindForSubMesh(a,b,b.subMeshes[0])},b.prototype._afterBind=function(b,c){void 0===c&&(c=null),a.prototype._afterBind.call(this,b,c),this.getScene()._cachedEffect=c},b.prototype._mustRebind=function(a,b,c){return void 0===c&&(c=1),a.isCachedMaterialInvalid(this,b,c)},b}(c(29).a)},function(a,b,c){c.d(b,"a",function(){return e});var d=c(2),e=function(a){function b(b){var c=a.call(this)||this;return c._buffer=b,c}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"underlyingResource",{get:function(){return this._buffer},enumerable:!1,configurable:!0}),b}(c(78).a)},function(a,b,c){c.d(b,"a",function(){return e});var d=c(68),e=function(){function a(){}return a.GetPlanes=function(b){for(var c=[],e=0;e<6;e++)c.push(new d.a(0,0,0,0));return a.GetPlanesToRef(b,c),c},a.GetNearPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[2],b.normal.y=a[7]+a[6],b.normal.z=a[11]+a[10],b.d=a[15]+a[14],b.normalize()},a.GetFarPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[2],b.normal.y=a[7]-a[6],b.normal.z=a[11]-a[10],b.d=a[15]-a[14],b.normalize()},a.GetLeftPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[0],b.normal.y=a[7]+a[4],b.normal.z=a[11]+a[8],b.d=a[15]+a[12],b.normalize()},a.GetRightPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[0],b.normal.y=a[7]-a[4],b.normal.z=a[11]-a[8],b.d=a[15]-a[12],b.normalize()},a.GetTopPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[1],b.normal.y=a[7]-a[5],b.normal.z=a[11]-a[9],b.d=a[15]-a[13],b.normalize()},a.GetBottomPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[1],b.normal.y=a[7]+a[5],b.normal.z=a[11]+a[9],b.d=a[15]+a[13],b.normalize()},a.GetPlanesToRef=function(b,c){a.GetNearPlaneToRef(b,c[0]),a.GetFarPlaneToRef(b,c[1]),a.GetLeftPlaneToRef(b,c[2]),a.GetRightPlaneToRef(b,c[3]),a.GetTopPlaneToRef(b,c[4]),a.GetBottomPlaneToRef(b,c[5])},a}()},function(a,b,c){c.d(b,"a",function(){return f});var d=c(51),e=c(0),f=function(){function a(a,b,c){this.center=e.e.Zero(),this.centerWorld=e.e.Zero(),this.minimum=e.e.Zero(),this.maximum=e.e.Zero(),this.reConstruct(a,b,c)}return a.prototype.reConstruct=function(a,b,c){this.minimum.copyFrom(a),this.maximum.copyFrom(b);var d=e.e.Distance(a,b);b.addToRef(a,this.center).scaleInPlace(.5),this.radius=.5*d,this._update(c||e.a.IdentityReadOnly)},a.prototype.scale=function(b){b=this.radius*b;var c=a.TmpVector3;b=c[0].setAll(b);var d=this.center.subtractToRef(b,c[1]);b=this.center.addToRef(b,c[2]);return this.reConstruct(d,b,this._worldMatrix),this},a.prototype.getWorldMatrix=function(){return this._worldMatrix},a.prototype._update=function(b){if(b.isIdentity())this.centerWorld.copyFrom(this.center),this.radiusWorld=this.radius;else{e.e.TransformCoordinatesToRef(this.center,b,this.centerWorld);var c=a.TmpVector3[0];e.e.TransformNormalFromFloatsToRef(1,1,1,b,c),this.radiusWorld=Math.max(Math.abs(c.x),Math.abs(c.y),Math.abs(c.z))*this.radius}},a.prototype.isInFrustum=function(a){for(var b=this.centerWorld,c=this.radiusWorld,d=0;d<6;d++)if(a[d].dotCoordinate(b)<=-c)return!1;return!0},a.prototype.isCenterInFrustum=function(a){for(var b=this.centerWorld,c=0;c<6;c++)if(a[c].dotCoordinate(b)<0)return!1;return!0},a.prototype.intersectsPoint=function(a){a=e.e.DistanceSquared(this.centerWorld,a);return!(this.radiusWorld*this.radiusWorld=d.a.ACTION_OnPickTrigger&&c<=d.a.ACTION_OnPickUpTrigger)return!0}return!1},enumerable:!1,configurable:!0}),a.HasSpecificTrigger=function(b){for(var c in a.Triggers)if(a.Triggers.hasOwnProperty(c)&&parseInt(c)===b)return!0;return!1},a.Triggers={},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(35),e=function(){function a(){}return a.SetImmediate=function(a){Object(d.e)()&&window.setImmediate?window.setImmediate(a):setTimeout(a,1)},a}()},function(a,b,c){c.d(b,"a",function(){return i});var d=c(2),e=c(29),f=c(3),g=c(20),h=c(15),i=function(){function a(a){this._texture=null,this.diffuseBlendLevel=1,this.roughnessBlendLevel=1,this.bumpLevel=1,this._normalBlendMethod=e.a.MATERIAL_NORMALBLENDMETHOD_WHITEOUT,this._isEnabled=!1,this.isEnabled=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isEnabled)return!0;var c=b.getEngine();return!(a._areTexturesDirty&&b.texturesEnabled&&c.getCaps().standardDerivatives&&this._texture&&g.a.DetailTextureEnabled&&!this._texture.isReady())},a.prototype.prepareDefines=function(a,b){if(this._isEnabled){a.DETAIL_NORMALBLENDMETHOD=this._normalBlendMethod;b=b.getEngine();a._areTexturesDirty&&(b.getCaps().standardDerivatives&&this._texture&&g.a.DetailTextureEnabled&&this._isEnabled?(h.a.PrepareDefinesForMergedUV(this._texture,a,"DETAIL"),a.DETAIL_NORMALBLENDMETHOD=this._normalBlendMethod):a.DETAIL=!1)}else a.DETAIL=!1},a.prototype.bindForSubMesh=function(a,b,c){this._isEnabled&&(a.useUbo&&c&&a.isSync||this._texture&&g.a.DetailTextureEnabled&&(a.updateFloat4("vDetailInfos",this._texture.coordinatesIndex,this.diffuseBlendLevel,this.bumpLevel,this.roughnessBlendLevel),h.a.BindTextureMatrix(this._texture,a,"detail")),b.texturesEnabled&&this._texture&&g.a.DetailTextureEnabled&&a.setTexture("detailSampler",this._texture))},a.prototype.hasTexture=function(a){return this._texture===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"DetailMap"},a.AddUniforms=function(a){a.push("vDetailInfos"),a.push("detailMatrix")},a.AddSamplers=function(a){a.push("detailSampler")},a.PrepareUniformBuffer=function(a){a.addUniform("vDetailInfos",4),a.addUniform("detailMatrix",16)},a.prototype.copyTo=function(a){f.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return f.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;f.a.Parse(function(){return d},a,b,c)},Object(d.c)([Object(f.n)("detailTexture"),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(d.c)([Object(f.d)()],a.prototype,"diffuseBlendLevel",void 0),Object(d.c)([Object(f.d)()],a.prototype,"roughnessBlendLevel",void 0),Object(d.c)([Object(f.d)()],a.prototype,"bumpLevel",void 0),Object(d.c)([Object(f.d)(),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"normalBlendMethod",void 0),Object(d.c)([Object(f.d)(),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),a}()},function(a,b,c){a="morphTargetsVertexGlobalDeclaration";b="#ifdef MORPHTARGETS uniform float morphTargetInfluences[NUM_MORPH_INFLUENCERS]; #ifdef MORPHTARGETS_TEXTURE precision mediump sampler2DArray; uniform float morphTargetTextureIndices[NUM_MORPH_INFLUENCERS]; uniform vec3 morphTargetTextureInfo; uniform sampler2DArray morphTargets; vec3 readVector3FromRawSampler(int targetIndex,float vertexIndex) { float y=floor(vertexIndex/morphTargetTextureInfo.y); float x=vertexIndex-y*morphTargetTextureInfo.y; vec3 textureUV=vec3((x+0.5)/morphTargetTextureInfo.y,(y+0.5)/morphTargetTextureInfo.z,morphTargetTextureIndices[targetIndex]); return texture(morphTargets,textureUV).xyz; } #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertexDeclaration";b="#ifdef MORPHTARGETS #ifndef MORPHTARGETS_TEXTURE attribute vec3 position{X}; #ifdef MORPHTARGETS_NORMAL attribute vec3 normal{X}; #endif #ifdef MORPHTARGETS_TANGENT attribute vec3 tangent{X}; #endif #ifdef MORPHTARGETS_UV attribute vec2 uv_{X}; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return f});var d=c(4),e=c(1),f=function(){function a(a){this._vertexBuffers={},this._scene=a}return a.prototype._prepareBuffers=function(){if(!this._vertexBuffers[d.b.PositionKind]){var a=[];a.push(1,1),a.push(-1,1),a.push(-1,-1),a.push(1,-1),this._vertexBuffers[d.b.PositionKind]=new d.b(this._scene.getEngine(),a,d.b.PositionKind,!1,!1,2),this._buildIndexBuffer()}},a.prototype._buildIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._scene.getEngine().createIndexBuffer(a)},a.prototype._rebuild=function(){var a=this._vertexBuffers[d.b.PositionKind];a&&(a._rebuild(),this._buildIndexBuffer())},a.prototype._prepareFrame=function(a,b){void 0===a&&(a=null),void 0===b&&(b=null);var c=this._scene.activeCamera;return!!c&&!(!(b=b||c._postProcesses.filter(function(a){return null!=a}))||0===b.length||!this._scene.postProcessesEnabled)&&(b[0].activate(c,a,null!=b),!0)},a.prototype.directRender=function(a,b,c,d,f,g){var h;void 0===b&&(b=null),void 0===c&&(c=!1),void 0===d&&(d=0),void 0===f&&(f=0),void 0===g&&(g=!1);for(var i=this._scene.getEngine(),j=0;j3?0:n,o);s=Object(f.a)(a,{pathArray:y,closeArray:l,closePath:m,updatable:q,sideOrientation:r,invertUV:t,frontUVs:u||void 0,backUVs:v||void 0},p);return s._creationDataStorage.pathArray=y,s._creationDataStorage.path3D=w,s._creationDataStorage.cap=n,s}var k={ExtrudeShape:h,ExtrudeShapeCustom:i};e.a.ExtrudeShape=function(a,b,c,d,f,g,i,j,k,l){return void 0===i&&(i=null),h(a,{shape:b,path:c,scale:d,rotation:f,cap:0===g?0:g||e.a.NO_CAP,sideOrientation:k,instance:l,updatable:j},i)},e.a.ExtrudeShapeCustom=function(a,b,c,d,f,g,h,j,k,l,m,n){return i(a,{shape:b,path:c,scaleFunction:d,rotationFunction:f,ribbonCloseArray:g,ribbonClosePath:h,cap:0===j?0:j||e.a.NO_CAP,sideOrientation:m,instance:n,updatable:l},k)}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.FilesToLoad={},a}()},function(a,b,c){c.d(b,"b",function(){return j}),c.d(b,"a",function(){return k});var d=c(2),e=c(8),f=c(4);a=c(9);b=c(177);var g=c(29),h=c(71),i=c(15);c(187),c(188);a.a._LinesMeshParser=function(a,b){return j.Parse(a,b)};var j=function(a){function b(b,c,d,k,l,g,i,j){void 0===c&&(c=null),void 0===d&&(d=null),void 0===k&&(k=null);b=a.call(this,b,c,d,k,l)||this;b.useVertexColor=g,b.useVertexAlpha=i,b.color=new e.a(1,1,1),b.alpha=1,k&&(b.color=k.color.clone(),b.alpha=k.alpha,b.useVertexColor=k.useVertexColor,b.useVertexAlpha=k.useVertexAlpha),b.intersectionThreshold=.1;c={attributes:[f.b.PositionKind],uniforms:["vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","world","viewProjection"],needAlphaBlending:!0,defines:[]};return!1===i&&(c.needAlphaBlending=!1),g?(c.defines.push("#define VERTEXCOLOR"),c.attributes.push(f.b.ColorKind)):(c.uniforms.push("color"),b.color4=new e.b),j?b.material=j:b._lineMaterial=new h.a("colorShader",b.getScene(),"color",c),b}return Object(d.d)(b,a),b.prototype._isShaderMaterial=function(a){return"ShaderMaterial"===a.getClassName()},b.prototype._addClipPlaneDefine=function(a){if(this._isShaderMaterial(this._lineMaterial)){a="#define "+a;-1===this._lineMaterial.options.defines.indexOf(a)&&this._lineMaterial.options.defines.push(a)}},b.prototype._removeClipPlaneDefine=function(a){if(this._isShaderMaterial(this._lineMaterial)){a="#define "+a;a=this._lineMaterial.options.defines.indexOf(a);-1!==a&&this._lineMaterial.options.defines.splice(a,1)}},b.prototype.isReady=function(){var b=this.getScene();return b.clipPlane?this._addClipPlaneDefine("CLIPPLANE"):this._removeClipPlaneDefine("CLIPPLANE"),b.clipPlane2?this._addClipPlaneDefine("CLIPPLANE2"):this._removeClipPlaneDefine("CLIPPLANE2"),b.clipPlane3?this._addClipPlaneDefine("CLIPPLANE3"):this._removeClipPlaneDefine("CLIPPLANE3"),b.clipPlane4?this._addClipPlaneDefine("CLIPPLANE4"):this._removeClipPlaneDefine("CLIPPLANE4"),b.clipPlane5?this._addClipPlaneDefine("CLIPPLANE5"):this._removeClipPlaneDefine("CLIPPLANE5"),b.clipPlane6?this._addClipPlaneDefine("CLIPPLANE6"):this._removeClipPlaneDefine("CLIPPLANE6"),!!this._lineMaterial.isReady(this)&&a.prototype.isReady.call(this)},b.prototype.getClassName=function(){return"LinesMesh"},Object.defineProperty(b.prototype,"material",{get:function(){return this._lineMaterial},set:function(a){this._lineMaterial=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"checkCollisions",{get:function(){return!1},set:function(a){},enumerable:!1,configurable:!0}),b.prototype._bind=function(a,b,c){if(!this._geometry)return this;a=this._lineMaterial.getEffect();b=this.isUnIndexed?null:this._geometry.getIndexBuffer();if(this._userInstancedBuffersStorage?this._geometry._bind(a,b,this._userInstancedBuffersStorage.vertexBuffers,this._userInstancedBuffersStorage.vertexArrayObjects):this._geometry._bind(a,b),!this.useVertexColor&&this._isShaderMaterial(this._lineMaterial)){c=this.color;b=c.r;var d=c.g;c=c.b;this.color4.set(b,d,c,this.alpha),this._lineMaterial.setColor4("color",this.color4)}return i.a.BindClipPlane(a,this.getScene()),this},b.prototype._draw=function(a,b,c){if(!this._geometry||!this._geometry.getVertexBuffers()||!this._unIndexed&&!this._geometry.getIndexBuffer())return this;b=this.getScene().getEngine();return this._unIndexed?b.drawArraysType(g.a.LineListDrawMode,a.verticesStart,a.verticesCount,c):b.drawElementsType(g.a.LineListDrawMode,a.indexStart,a.indexCount,c),this},b.prototype.dispose=function(b){this._lineMaterial.dispose(!1,!1,!0),a.prototype.dispose.call(this,b)},b.prototype.clone=function(a,c,d){return void 0===c&&(c=null),new b(a,this.getScene(),c,this,d)},b.prototype.createInstance=function(a){a=new k(a,this);if(this.instancedBuffers)for(var b in a.instancedBuffers={},this.instancedBuffers)a.instancedBuffers[b]=this.instancedBuffers[b];return a},b.prototype.serialize=function(b){a.prototype.serialize.call(this,b),b.color=this.color.asArray(),b.alpha=this.alpha},b.Parse=function(a,c){c=new b(a.name,c);return c.color=e.a.FromArray(a.color),c.alpha=a.alpha,c},b}(a.a),k=function(a){function b(b,c){b=a.call(this,b,c)||this;return b.intersectionThreshold=c.intersectionThreshold,b}return Object(d.d)(b,a),b.prototype.getClassName=function(){return"InstancedLinesMesh"},b}(b.a)},function(a,b,c){c.r(b),c.d(b,"AxesViewer",function(){return i}),c.d(b,"BoneAxesViewer",function(){return l}),c.d(b,"DebugLayerTab",function(){return d}),c.d(b,"DebugLayer",function(){return p}),c.d(b,"PhysicsViewer",function(){return y}),c.d(b,"RayHelper",function(){return A}),c.d(b,"SkeletonViewer",function(){return H}),c.d(b,"DirectionalLightFrustumViewer",function(){return K});var d,e=c(0),f=c(32),g=c(82),h=c(8),i=function(){function a(b,c,d,i,j,k){if(void 0===c&&(c=1),void 0===d&&(d=2),this._scaleLinesFactor=4,this._instanced=!1,this.scene=null,this.scaleLines=1,this.scaleLines=c,!i){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Red().scale(.5),i=g.a._CreateArrow(b,c)}if(!j){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Green().scale(.5),j=g.a._CreateArrow(b,c)}if(!k){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Blue().scale(.5),k=g.a._CreateArrow(b,c)}this._xAxis=i,this._xAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._yAxis=j,this._yAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._zAxis=k,this._zAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),null!=d&&(a._SetRenderingGroupId(this._xAxis,d),a._SetRenderingGroupId(this._yAxis,d),a._SetRenderingGroupId(this._zAxis,d)),this.scene=b,this.update(new e.e,e.e.Right(),e.e.Up(),e.e.Forward())}return Object.defineProperty(a.prototype,"xAxis",{get:function(){return this._xAxis},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"yAxis",{get:function(){return this._yAxis},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zAxis",{get:function(){return this._zAxis},enumerable:!1,configurable:!0}),a.prototype.update=function(a,b,c,d){this._xAxis.position.copyFrom(a),this._xAxis.setDirection(b),this._xAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._yAxis.position.copyFrom(a),this._yAxis.setDirection(c),this._yAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._zAxis.position.copyFrom(a),this._zAxis.setDirection(d),this._zAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor)},a.prototype.createInstance=function(){var b=g.a._CreateArrowInstance(this.scene,this._xAxis),c=g.a._CreateArrowInstance(this.scene,this._yAxis),d=g.a._CreateArrowInstance(this.scene,this._zAxis);b=new a(this.scene,this.scaleLines,null,b,c,d);return b._instanced=!0,b},a.prototype.dispose=function(){this._xAxis&&this._xAxis.dispose(!1,!this._instanced),this._yAxis&&this._yAxis.dispose(!1,!this._instanced),this._zAxis&&this._zAxis.dispose(!1,!this._instanced),this.scene=null},a._SetRenderingGroupId=function(a,b){a.getChildMeshes().forEach(function(a){a.renderingGroupId=b})},a}(),j=c(2),k=c(25),l=function(a){function b(b,c,d,f){void 0===f&&(f=1);b=a.call(this,b,f)||this;return b.pos=e.e.Zero(),b.xaxis=e.e.Zero(),b.yaxis=e.e.Zero(),b.zaxis=e.e.Zero(),b.mesh=d,b.bone=c,b}return Object(j.d)(b,a),b.prototype.update=function(){if(this.mesh&&this.bone){var b=this.bone;b._markAsDirtyAndCompose(),b.getAbsolutePositionToRef(this.mesh,this.pos),b.getDirectionToRef(k.a.X,this.mesh,this.xaxis),b.getDirectionToRef(k.a.Y,this.mesh,this.yaxis),b.getDirectionToRef(k.a.Z,this.mesh,this.zaxis),a.prototype.update.call(this,this.pos,this.xaxis,this.yaxis,this.zaxis)}},b.prototype.dispose=function(){this.mesh&&(this.mesh=null,this.bone=null,a.prototype.dispose.call(this))},b}(i),m=c(12),n=c(6);a=c(21);var o=c(13);Object.defineProperty(a.a.prototype,"debugLayer",{get:function(){return this._debugLayer||(this._debugLayer=new p(this)),this._debugLayer},enumerable:!0,configurable:!0}),(function(a){a[a.Properties=0]="Properties",a[a.Debug=1]="Debug",a[a.Statistics=2]="Statistics",a[a.Tools=3]="Tools",a[a.Settings=4]="Settings"})(d||(d={}));var p=function(){function a(a){var b=this;this.BJSINSPECTOR=this._getGlobalInspector(),this._scene=a,this._scene.onDisposeObservable.add(function(){b._scene._debugLayer&&b._scene._debugLayer.hide()})}return Object.defineProperty(a.prototype,"onPropertyChangedObservable",{get:function(){return this.BJSINSPECTOR&&this.BJSINSPECTOR.Inspector?this.BJSINSPECTOR.Inspector.OnPropertyChangedObservable:(this._onPropertyChangedObservable||(this._onPropertyChangedObservable=new n.c),this._onPropertyChangedObservable)},enumerable:!1,configurable:!0}),a.prototype._createInspector=function(a){if(!this.isVisible()){if(this._onPropertyChangedObservable){for(var b=0,c=this._onPropertyChangedObservable.observers;b-1&&this._debugMeshMeshes.splice(e,1),this._numMeshes--,this._numMeshes>0?(this._meshes[d]=this._meshes[this._numMeshes],this._impostors[d]=this._impostors[this._numMeshes],this._meshes[this._numMeshes]=null,this._impostors[this._numMeshes]=null):(this._meshes[0]=null,this._impostors[0]=null),b=!0;break}b&&0===this._numMeshes&&this._scene.unregisterBeforeRender(this._renderFunction)}},a.prototype._getDebugMaterial=function(a){return this._debugMaterial||(this._debugMaterial=new f.a("",a),this._debugMaterial.wireframe=!0,this._debugMaterial.emissiveColor=h.a.White(),this._debugMaterial.disableLighting=!0),this._debugMaterial},a.prototype._getDebugBoxMesh=function(a){return this._debugBoxMesh||(this._debugBoxMesh=Object(r.b)("physicsBodyBoxViewMesh",{size:1},a),this._debugBoxMesh.rotationQuaternion=e.b.Identity(),this._debugBoxMesh.material=this._getDebugMaterial(a),this._debugBoxMesh.setEnabled(!1)),this._debugBoxMesh.createInstance("physicsBodyBoxViewInstance")},a.prototype._getDebugSphereMesh=function(a){return this._debugSphereMesh||(this._debugSphereMesh=Object(s.a)("physicsBodySphereViewMesh",{diameter:1},a),this._debugSphereMesh.rotationQuaternion=e.b.Identity(),this._debugSphereMesh.material=this._getDebugMaterial(a),this._debugSphereMesh.setEnabled(!1)),this._debugSphereMesh.createInstance("physicsBodySphereViewInstance")},a.prototype._getDebugCapsuleMesh=function(a){return this._debugCapsuleMesh||(this._debugCapsuleMesh=Object(x.b)("physicsBodyCapsuleViewMesh",{height:1},a),this._debugCapsuleMesh.rotationQuaternion=e.b.Identity(),this._debugCapsuleMesh.material=this._getDebugMaterial(a),this._debugCapsuleMesh.setEnabled(!1)),this._debugCapsuleMesh.createInstance("physicsBodyCapsuleViewInstance")},a.prototype._getDebugCylinderMesh=function(a){return this._debugCylinderMesh||(this._debugCylinderMesh=Object(w.a)("physicsBodyCylinderViewMesh",{diameterTop:1,diameterBottom:1,height:1},a),this._debugCylinderMesh.rotationQuaternion=e.b.Identity(),this._debugCylinderMesh.material=this._getDebugMaterial(a),this._debugCylinderMesh.setEnabled(!1)),this._debugCylinderMesh.createInstance("physicsBodyCylinderViewInstance")},a.prototype._getDebugMeshMesh=function(a,b){var c=new q.a(a.name,b,null,a);return c.position=e.e.Zero(),c.setParent(a),c.material=this._getDebugMaterial(b),this._debugMeshMeshes.push(c),c},a.prototype._getDebugMesh=function(a,b){var c=this;if(!this._utilityLayer)return null;if(b&&b.parent&&b.parent.physicsImpostor)return null;var d=null,e=this._utilityLayer.utilityLayerScene;switch(a.type){case u.a.BoxImpostor:d=this._getDebugBoxMesh(e),a.getBoxSizeToRef(d.scaling);break;case u.a.SphereImpostor:d=this._getDebugSphereMesh(e);var f=a.getRadius();d.scaling.x=2*f,d.scaling.y=2*f,d.scaling.z=2*f;break;case u.a.CapsuleImpostor:d=this._getDebugCapsuleMesh(e);f=a.object.getBoundingInfo();d.scaling.x=2*(f.boundingBox.maximum.x-f.boundingBox.minimum.x)*a.object.scaling.x,d.scaling.y=(f.boundingBox.maximum.y-f.boundingBox.minimum.y)*a.object.scaling.y,d.scaling.z=2*(f.boundingBox.maximum.z-f.boundingBox.minimum.z)*a.object.scaling.z;break;case u.a.MeshImpostor:b&&(d=this._getDebugMeshMesh(b,e));break;case u.a.NoImpostor:b&&b.getChildMeshes().filter(function(a){return a.physicsImpostor?1:0}).forEach(function(a){if(a.physicsImpostor&&"Mesh"===a.getClassName()){var b=a.getBoundingInfo(),f=b.boundingBox.minimum;b=b.boundingBox.maximum;switch(a.physicsImpostor.type){case u.a.BoxImpostor:(d=c._getDebugBoxMesh(e)).position.copyFrom(f),d.position.addInPlace(b),d.position.scaleInPlace(.5);break;case u.a.SphereImpostor:d=c._getDebugSphereMesh(e);break;case u.a.CylinderImpostor:d=c._getDebugCylinderMesh(e);break;default:d=null}d&&(d.scaling.x=b.x-f.x,d.scaling.y=b.y-f.y,d.scaling.z=b.z-f.z,d.parent=a)}});d=null;break;case u.a.CylinderImpostor:d=this._getDebugCylinderMesh(e),f=a.object.getBoundingInfo(),(d.scaling.x=(f.boundingBox.maximum.x-f.boundingBox.minimum.x)*a.object.scaling.x,d.scaling.y=(f.boundingBox.maximum.y-f.boundingBox.minimum.y)*a.object.scaling.y,d.scaling.z=(f.boundingBox.maximum.z-f.boundingBox.minimum.z)*a.object.scaling.z)}return d},a.prototype.dispose=function(){for(var a=this._numMeshes,b=0;ba.DISPLAY_SPHERE_AND_SPURS&&(c=a.DISPLAY_LINES),this.displayMode=c,this.update(),this._bindObs()}return a.CreateBoneWeightShader=function(a,b){var c,d,e,f,g,i=a.skeleton;c=null!==(c=a.colorBase)&&void 0!==c?c:h.a.Black();d=null!==(d=a.colorZero)&&void 0!==d?d:h.a.Blue();e=null!==(e=a.colorQuarter)&&void 0!==e?e:h.a.Green();f=null!==(f=a.colorHalf)&&void 0!==f?f:h.a.Yellow();g=null!==(g=a.colorFull)&&void 0!==g?g:h.a.Red();a=null!==(a=a.targetBoneIndex)&&void 0!==a?a:0;F.a.ShadersStore["boneWeights:"+i.name+"VertexShader"]="precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 view; uniform mat4 projection; uniform mat4 worldViewProjection; #include #if NUM_BONE_INFLUENCERS == 0 attribute vec4 matricesIndices; attribute vec4 matricesWeights; #endif #include varying vec3 vColor; uniform vec3 colorBase; uniform vec3 colorZero; uniform vec3 colorQuarter; uniform vec3 colorHalf; uniform vec3 colorFull; uniform float targetBoneIndex; void main() { vec3 positionUpdated = position; #include #include vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0); vec3 color = colorBase; float totalWeight = 0.; if(matricesIndices[0] == targetBoneIndex && matricesWeights[0] > 0.){ totalWeight += matricesWeights[0]; } if(matricesIndices[1] == targetBoneIndex && matricesWeights[1] > 0.){ totalWeight += matricesWeights[1]; } if(matricesIndices[2] == targetBoneIndex && matricesWeights[2] > 0.){ totalWeight += matricesWeights[2]; } if(matricesIndices[3] == targetBoneIndex && matricesWeights[3] > 0.){ totalWeight += matricesWeights[3]; } color = mix(color, colorZero, smoothstep(0., 0.25, totalWeight)); color = mix(color, colorQuarter, smoothstep(0.25, 0.5, totalWeight)); color = mix(color, colorHalf, smoothstep(0.5, 0.75, totalWeight)); color = mix(color, colorFull, smoothstep(0.75, 1.0, totalWeight)); vColor = color; gl_Position = projection * view * worldPos; }",F.a.ShadersStore["boneWeights:"+i.name+"FragmentShader"]=" precision highp float; varying vec3 vPosition; varying vec3 vColor; void main() { vec4 color = vec4(vColor, 1.0); gl_FragColor = color; } ";b=new C.a("boneWeight:"+i.name,b,{vertex:"boneWeights:"+i.name,fragment:"boneWeights:"+i.name},{attributes:["position","normal","matricesIndices","matricesWeights"],uniforms:["world","worldView","worldViewProjection","view","projection","viewProjection","colorBase","colorZero","colorQuarter","colorHalf","colorFull","targetBoneIndex"]});return b.setColor3("colorBase",c),b.setColor3("colorZero",d),b.setColor3("colorQuarter",e),b.setColor3("colorHalf",f),b.setColor3("colorFull",g),b.setFloat("targetBoneIndex",a),b.getClassName=function(){return"BoneWeightShader"},b.transparencyMode=B.a.MATERIAL_OPAQUE,b},a.CreateSkeletonMapShader=function(b,c){var d=b.skeleton;b=null!==(b=b.colorMap)&&void 0!==b?b:[{color:new h.a(1,.38,.18),location:0},{color:new h.a(.59,.18,1),location:.2},{color:new h.a(.59,1,.18),location:.4},{color:new h.a(1,.87,.17),location:.6},{color:new h.a(1,.17,.42),location:.8},{color:new h.a(.17,.68,1),location:1}];var e=d.bones.length+1;e=a._CreateBoneMapColorBuffer(e,b,c);b=new C.a("boneWeights:"+d.name,c,{vertexSource:"precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 view; uniform mat4 projection; uniform mat4 worldViewProjection; uniform float colorMap["+4*d.bones.length+"]; #include #if NUM_BONE_INFLUENCERS == 0 attribute vec4 matricesIndices; attribute vec4 matricesWeights; #endif #include varying vec3 vColor; void main() { vec3 positionUpdated = position; #include #include vec3 color = vec3(0.); bool first = true; for (int i = 0; i < 4; i++) { int boneIdx = int(matricesIndices[i]); float boneWgt = matricesWeights[i]; vec3 c = vec3(colorMap[boneIdx * 4 + 0], colorMap[boneIdx * 4 + 1], colorMap[boneIdx * 4 + 2]); if (boneWgt > 0.) { if (first) { first = false; color = c; } else { color = mix(color, c, boneWgt); } } } vColor = color; vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0); gl_Position = projection * view * worldPos; }",fragmentSource:" precision highp float; varying vec3 vColor; void main() { vec4 color = vec4( vColor, 1.0 ); gl_FragColor = color; } "},{attributes:["position","normal","matricesIndices","matricesWeights"],uniforms:["world","worldView","worldViewProjection","view","projection","viewProjection","colorMap"]});return b.setFloats("colorMap",e),b.getClassName=function(){return"SkeletonMapShader"},b.transparencyMode=B.a.MATERIAL_OPAQUE,b},a._CreateBoneMapColorBuffer=function(a,b,c){c=new D.a("temp",{width:a,height:1},c,!1);var d=c.getContext(),e=d.createLinearGradient(0,0,a,0);b.forEach(function(a){e.addColorStop(a.location,a.color.toHexString())}),d.fillStyle=e,d.fillRect(0,0,a,1),c.update();for(b=[],d=d.getImageData(0,0,a,1).data,a=0;aa.DISPLAY_SPHERE_AND_SPURS&&(b=a.DISPLAY_LINES),this.options.displayMode=b},enumerable:!1,configurable:!0}),a.prototype._bindObs=function(){var b=this;switch(this.displayMode){case a.DISPLAY_LINES:this._obs=this.scene.onBeforeRenderObservable.add(function(){b._displayLinesUpdate()})}},a.prototype.update=function(){switch(this.displayMode){case a.DISPLAY_LINES:this._displayLinesUpdate();break;case a.DISPLAY_SPHERES:this._buildSpheresAndSpurs(!0);break;case a.DISPLAY_SPHERE_AND_SPURS:this._buildSpheresAndSpurs(!1)}this._buildLocalAxes()},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this.isEnabled!==a&&(this._isEnabled=a,this.debugMesh&&this.debugMesh.setEnabled(a),a&&!this._obs?this._bindObs():!a&&this._obs&&(this.scene.onBeforeRenderObservable.remove(this._obs),this._obs=null))},enumerable:!1,configurable:!0}),a.prototype._getBonePosition=function(a,b,c,d,f,g){void 0===d&&(d=0),void 0===f&&(f=0),void 0===g&&(g=0);var h=e.c.Matrix[0],i=b.getParent();if(h.copyFrom(b.getLocalMatrix()),0!==d||0!==f||0!==g){b=e.c.Matrix[1];e.a.IdentityToRef(b),b.setTranslationFromFloats(d,f,g),b.multiplyToRef(h,h)}i&&h.multiplyToRef(i.getAbsoluteTransform(),h),h.multiplyToRef(c,h),a.x=h.m[12],a.y=h.m[13],a.z=h.m[14]},a.prototype._getLinesForBonesWithLength=function(a,b){for(var c=a.length,d=this.mesh._effectiveMesh.position,f=0,g=0;g=0;b--){var g=a[b],h=g.getParent();if(h&&(this._boneIndices.has(g.getIndex())||this.options.useAllBones)){var i=this._debugLines[c];i||(i=[e.e.Zero(),e.e.Zero()],this._debugLines[c]=i),g.getAbsolutePositionToRef(d,i[0]),h.getAbsolutePositionToRef(d,i[1]),i[0].subtractInPlace(f),i[1].subtractInPlace(f),c++}}},a.prototype._revert=function(a){this.options.pauseAnimations&&(this.scene.animationsEnabled=a,this.utilityLayer.utilityLayerScene.animationsEnabled=a)},a.prototype._getAbsoluteBindPoseToRef=function(a,b){null!==a&&-1!==a._index?(this._getAbsoluteBindPoseToRef(a.getParent(),b),a.getBaseMatrix().multiplyToRef(b,b)):b.copyFrom(e.a.Identity())},a.prototype._buildSpheresAndSpurs=function(a){var b;void 0===a&&(a=!0),this._debugMesh&&(this._debugMesh.dispose(),this._debugMesh=null,this.ready=!1),this._ready=!1;var c=null===(b=this.utilityLayer)||void 0===b?void 0:b.utilityLayerScene,d=this.skeleton.bones,f=[],g=[];b=this.scene.animationsEnabled;try{this.options.pauseAnimations&&(this.scene.animationsEnabled=!1,c.animationsEnabled=!1),this.options.returnToRest&&this.skeleton.returnToRest(),this.autoUpdateBonesMatrices&&this.skeleton.computeAbsoluteTransforms();for(var h=Number.NEGATIVE_INFINITY,i=this.options.displayOptions||{},j=function(b){var j=d[b];if(-1===j._index||!k._boneIndices.has(j.getIndex())&&!k.options.useAllBones)return"continue";var n=new e.a;k._getAbsoluteBindPoseToRef(j,n);var u=new e.e;n.decompose(void 0,void 0,u),j.children.forEach(function(b,d){d=new e.a;b.getBaseMatrix().multiplyToRef(n,d);var f=new e.e;d.decompose(void 0,void 0,f);d=e.e.Distance(u,f);if(d>h&&(h=d),!a){for(var d=f.clone().subtract(u.clone()),k=d.length(),f=d.normalize().scale(k),d=i.midStep||.165,l=i.midStepFactor||.215,d=f.scale(d),d=Object(G.b)("skeletonViewer",{shape:[new e.e(1,-1,0),new e.e(1,1,0),new e.e(-1,1,0),new e.e(-1,-1,0),new e.e(1,-1,0)],path:[e.e.Zero(),d,f],scaleFunction:function(a){switch(a){case 0:case 2:return 0;case 1:return k*l}return 0},sideOrientation:q.a.DEFAULTSIDE,updatable:!1},c),f=d.getTotalVertices(),p=[],r=[],t=0;t9?r.push(b.getIndex(),0,0,0):r.push(j.getIndex(),0,0,0);d.position=u.clone(),d.setVerticesData(E.b.MatricesWeightsKind,p,!1),d.setVerticesData(E.b.MatricesIndicesKind,r,!1),d.convertToFlatShadedMesh(),g.push(d)}});for(var b=i.sphereBaseSize||.2,b=Object(s.a)("skeletonViewer",{segments:6,diameter:b,updatable:!0},c),v=b.getTotalVertices(),z=[],A=[],B=0;B=6&&a._showPlanes)}),this._oldPosition.set(Number.NaN,Number.NaN,Number.NaN),this._visible=!0},a.prototype.hide=function(){this._lightHelperFrustumMeshes.forEach(function(a){a.setEnabled(!1)}),this._visible=!1},a.prototype.update=function(){if(this._visible&&(!this._oldPosition.equals(this._light.position)||!this._oldDirection.equals(this._light.direction)||this._oldAutoCalc!==this._light.autoCalcShadowZBounds||this._oldMinZ!==this._light.shadowMinZ||this._oldMaxZ!==this._light.shadowMaxZ)){this._oldPosition.copyFrom(this._light.position),this._oldDirection.copyFrom(this._light.direction),this._oldAutoCalc=this._light.autoCalcShadowZBounds,this._oldMinZ=this._light.shadowMinZ,this._oldMaxZ=this._light.shadowMaxZ,e.c.Vector3[0].set(this._light.orthoLeft,this._light.orthoBottom,void 0!==this._light.shadowMinZ?this._light.shadowMinZ:this._camera.minZ),e.c.Vector3[1].set(this._light.orthoRight,this._light.orthoTop,void 0!==this._light.shadowMaxZ?this._light.shadowMaxZ:this._camera.maxZ);var a=this._getInvertViewMatrix();e.c.Vector3[2].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[1].y,e.c.Vector3[0].z),e.c.Vector3[3].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[0].y,e.c.Vector3[0].z),e.c.Vector3[4].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[0].y,e.c.Vector3[0].z),e.c.Vector3[5].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[1].y,e.c.Vector3[0].z),e.e.TransformCoordinatesToRef(e.c.Vector3[2],a,e.c.Vector3[2]),e.e.TransformCoordinatesToRef(e.c.Vector3[3],a,e.c.Vector3[3]),e.e.TransformCoordinatesToRef(e.c.Vector3[4],a,e.c.Vector3[4]),e.e.TransformCoordinatesToRef(e.c.Vector3[5],a,e.c.Vector3[5]),e.c.Vector3[6].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[1].y,e.c.Vector3[1].z),e.c.Vector3[7].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[0].y,e.c.Vector3[1].z),e.c.Vector3[8].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[0].y,e.c.Vector3[1].z),e.c.Vector3[9].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[1].y,e.c.Vector3[1].z),e.e.TransformCoordinatesToRef(e.c.Vector3[6],a,e.c.Vector3[6]),e.e.TransformCoordinatesToRef(e.c.Vector3[7],a,e.c.Vector3[7]),e.e.TransformCoordinatesToRef(e.c.Vector3[8],a,e.c.Vector3[8]),e.e.TransformCoordinatesToRef(e.c.Vector3[9],a,e.c.Vector3[9]),Object(z.e)("nearlines",{updatable:!0,points:this._nearLinesPoints,instance:this._lightHelperFrustumMeshes[0]},this._scene),Object(z.e)("farlines",{updatable:!0,points:this._farLinesPoints,instance:this._lightHelperFrustumMeshes[1]},this._scene),Object(z.e)("trlines",{updatable:!0,points:this._trLinesPoints,instance:this._lightHelperFrustumMeshes[2]},this._scene),Object(z.e)("brlines",{updatable:!0,points:this._brLinesPoints,instance:this._lightHelperFrustumMeshes[3]},this._scene),Object(z.e)("tllines",{updatable:!0,points:this._tlLinesPoints,instance:this._lightHelperFrustumMeshes[4]},this._scene),Object(z.e)("bllines",{updatable:!0,points:this._blLinesPoints,instance:this._lightHelperFrustumMeshes[5]},this._scene),e.c.Vector3[2].toArray(this._nearPlaneVertices,0),e.c.Vector3[3].toArray(this._nearPlaneVertices,3),e.c.Vector3[4].toArray(this._nearPlaneVertices,6),e.c.Vector3[5].toArray(this._nearPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[6].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._nearPlaneVertices,0),e.c.Vector3[6].toArray(this._farPlaneVertices,0),e.c.Vector3[7].toArray(this._farPlaneVertices,3),e.c.Vector3[8].toArray(this._farPlaneVertices,6),e.c.Vector3[9].toArray(this._farPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[7].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._farPlaneVertices,0),e.c.Vector3[2].toArray(this._rightPlaneVertices,0),e.c.Vector3[6].toArray(this._rightPlaneVertices,3),e.c.Vector3[7].toArray(this._rightPlaneVertices,6),e.c.Vector3[3].toArray(this._rightPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[8].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._rightPlaneVertices,0),e.c.Vector3[5].toArray(this._leftPlaneVertices,0),e.c.Vector3[9].toArray(this._leftPlaneVertices,3),e.c.Vector3[8].toArray(this._leftPlaneVertices,6),e.c.Vector3[4].toArray(this._leftPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[9].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._leftPlaneVertices,0),e.c.Vector3[2].toArray(this._topPlaneVertices,0),e.c.Vector3[6].toArray(this._topPlaneVertices,3),e.c.Vector3[9].toArray(this._topPlaneVertices,6),e.c.Vector3[5].toArray(this._topPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[10].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._topPlaneVertices,0),e.c.Vector3[3].toArray(this._bottomPlaneVertices,0),e.c.Vector3[7].toArray(this._bottomPlaneVertices,3),e.c.Vector3[8].toArray(this._bottomPlaneVertices,6),e.c.Vector3[4].toArray(this._bottomPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[11].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._bottomPlaneVertices,0)}},a.prototype.dispose=function(){this._lightHelperFrustumMeshes.forEach(function(a){var b;null===(b=a.material)||void 0===b||b.dispose(),a.dispose()}),this._rootNode.dispose()},a.prototype._createGeometry=function(){var a=this;this._rootNode=new J.a("directionalLightHelperRoot_"+this._light.name,this._scene),this._rootNode.parent=this._light.parent,this._nearLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly];var b=Object(z.e)("nearlines",{updatable:!0,points:this._nearLinesPoints},this._scene);b.parent=this._rootNode,b.alwaysSelectAsActiveMesh=!0,this._farLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly];var c=Object(z.e)("farlines",{updatable:!0,points:this._farLinesPoints},this._scene);c.parent=this._rootNode,c.alwaysSelectAsActiveMesh=!0,this._trLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var d=Object(z.e)("trlines",{updatable:!0,points:this._trLinesPoints},this._scene);d.parent=this._rootNode,d.alwaysSelectAsActiveMesh=!0,this._brLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var g=Object(z.e)("brlines",{updatable:!0,points:this._brLinesPoints},this._scene);g.parent=this._rootNode,g.alwaysSelectAsActiveMesh=!0,this._tlLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var i=Object(z.e)("tllines",{updatable:!0,points:this._tlLinesPoints},this._scene);i.parent=this._rootNode,i.alwaysSelectAsActiveMesh=!0,this._blLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var j=Object(z.e)("bllines",{updatable:!0,points:this._blLinesPoints},this._scene);j.parent=this._rootNode,j.alwaysSelectAsActiveMesh=!0,this._lightHelperFrustumMeshes.push(b,c,d,g,i,j);b=function(b,c,d){var e=new q.a(b+"plane",a._scene);b=new f.a(b+"PlaneMat",a._scene);e.material=b,e.parent=a._rootNode,e.alwaysSelectAsActiveMesh=!0,b.emissiveColor=c,b.alpha=a.transparency,b.backFaceCulling=!1,b.disableLighting=!0;c=new I.a;c.positions=d,c.indices=[0,1,2,0,2,3],c.applyToMesh(e,!0),a._lightHelperFrustumMeshes.push(e)};this._nearPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._farPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._rightPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._leftPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._topPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._bottomPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],b("near",new h.a(1,0,0),this._nearPlaneVertices),b("far",new h.a(.3,0,0),this._farPlaneVertices),b("right",new h.a(0,1,0),this._rightPlaneVertices),b("left",new h.a(0,.3,0),this._leftPlaneVertices),b("top",new h.a(0,0,1),this._topPlaneVertices),b("bottom",new h.a(0,0,.3),this._bottomPlaneVertices),this._nearLinesPoints[0]=e.c.Vector3[2],this._nearLinesPoints[1]=e.c.Vector3[3],this._nearLinesPoints[2]=e.c.Vector3[4],this._nearLinesPoints[3]=e.c.Vector3[5],this._nearLinesPoints[4]=e.c.Vector3[2],this._farLinesPoints[0]=e.c.Vector3[6],this._farLinesPoints[1]=e.c.Vector3[7],this._farLinesPoints[2]=e.c.Vector3[8],this._farLinesPoints[3]=e.c.Vector3[9],this._farLinesPoints[4]=e.c.Vector3[6],this._trLinesPoints[0]=e.c.Vector3[2],this._trLinesPoints[1]=e.c.Vector3[6],this._brLinesPoints[0]=e.c.Vector3[3],this._brLinesPoints[1]=e.c.Vector3[7],this._tlLinesPoints[0]=e.c.Vector3[4],this._tlLinesPoints[1]=e.c.Vector3[8],this._blLinesPoints[0]=e.c.Vector3[5],this._blLinesPoints[1]=e.c.Vector3[9]},a.prototype._getInvertViewMatrix=function(){return e.a.LookAtLHToRef(this._light.position,this._light.position.add(this._light.direction),e.e.UpReadOnly,this._inverseViewMatrix),this._inverseViewMatrix.invertToRef(this._inverseViewMatrix),this._inverseViewMatrix},a}()},function(a,b,c){a="clipPlaneFragment";b="#if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) if (false) {} #endif #ifdef CLIPPLANE else if (fClipDistance>0.0) { discard; } #endif #ifdef CLIPPLANE2 else if (fClipDistance2>0.0) { discard; } #endif #ifdef CLIPPLANE3 else if (fClipDistance3>0.0) { discard; } #endif #ifdef CLIPPLANE4 else if (fClipDistance4>0.0) { discard; } #endif #ifdef CLIPPLANE5 else if (fClipDistance5>0.0) { discard; } #endif #ifdef CLIPPLANE6 else if (fClipDistance6>0.0) { discard; } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertexGlobal";b="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE float vertexID; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertex";b="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE vertexID=float(gl_VertexID)*morphTargetTextureInfo.x; positionUpdated+=(readVector3FromRawSampler({X},vertexID)-position)*morphTargetInfluences[{X}]; vertexID+=1.0; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(readVector3FromRawSampler({X},vertexID)-normal)*morphTargetInfluences[{X}]; vertexID+=1.0; #endif #ifdef MORPHTARGETS_UV uvUpdated+=(readVector3FromRawSampler({X},vertexID).xy-uv)*morphTargetInfluences[{X}]; vertexID+=1.0; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz+=(readVector3FromRawSampler({X},vertexID)-tangent.xyz)*morphTargetInfluences[{X}]; #endif #else positionUpdated+=(position{X}-position)*morphTargetInfluences[{X}]; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(normal{X}-normal)*morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz+=(tangent{X}-tangent.xyz)*morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_UV uvUpdated+=(uv_{X}-uv)*morphTargetInfluences[{X}]; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneVertex";b="#ifdef CLIPPLANE fClipDistance=dot(worldPos,vClipPlane); #endif #ifdef CLIPPLANE2 fClipDistance2=dot(worldPos,vClipPlane2); #endif #ifdef CLIPPLANE3 fClipDistance3=dot(worldPos,vClipPlane3); #endif #ifdef CLIPPLANE4 fClipDistance4=dot(worldPos,vClipPlane4); #endif #ifdef CLIPPLANE5 fClipDistance5=dot(worldPos,vClipPlane5); #endif #ifdef CLIPPLANE6 fClipDistance6=dot(worldPos,vClipPlane6); #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){var d;c.d(b,"a",function(){return d}),c.d(b,"b",function(){return e}),(function(a){a[a.PointerMove=0]="PointerMove",a[a.PointerDown=1]="PointerDown",a[a.PointerUp=2]="PointerUp"})(d||(d={}));var e=function(){function a(){}return a.DOM_DELTA_PIXEL=0,a.DOM_DELTA_LINE=1,a.DOM_DELTA_PAGE=2,a}()},function(a,b,c){c.d(b,"b",function(){return h}),c.d(b,"a",function(){return i});var d=c(2),e=c(0),f=c(3),g=function(){function a(){}return a.extractMinAndMaxIndexed=function(a,b,c,d,e,f){for(var g=c;g=f.c.LeftClick&&c<=f.c.RightClick&&(e.type=1===d?"pointerdown":"pointerup",e.button=c-2),e},a._createWheelEvent=function(a,b,c,d,g,h){h=this._createMouseEvent(a,b,c,d,g,h);return h.type="wheel",h.deltaMode=e.b.DOM_DELTA_PIXEL,h.deltaX=c===f.c.MouseWheelX?d:g.pollInput(a,b,f.c.MouseWheelX),h.deltaY=c===f.c.MouseWheelY?d:g.pollInput(a,b,f.c.MouseWheelY),h.deltaZ=c===f.c.MouseWheelZ?d:g.pollInput(a,b,f.c.MouseWheelZ),h},a._createMouseEvent=function(a,b,c,d,e,g){var h=this._createEvent(g),i=e.pollInput(a,b,f.c.Horizontal);a=e.pollInput(a,b,f.c.Vertical);b=c===f.c.DeltaHorizontal?d:0;d=c===f.c.DeltaVertical?d:0;var j=c===f.c.DeltaHorizontal&&g?b-g.getBoundingClientRect().x:0;c=c===f.c.DeltaVertical&&g?d-g.getBoundingClientRect().y:0;return this._checkNonCharacterKeys(h,e),h.clientX=i,h.clientY=a,h.movementX=b,h.movementY=d,h.offsetX=j,h.offsetY=c,h.x=i,h.y=a,h},a._createKeyboardEvent=function(a,b,c,d){d=this._createEvent(d);return this._checkNonCharacterKeys(d,c),d.type=1===b?"keydown":"keyup",d.key=String.fromCharCode(a),d.keyCode=a,d},a._checkNonCharacterKeys=function(a,b){var c=b.isDeviceAvailable(f.a.Keyboard),e=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_ALT_KEY),g=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_CTRL_KEY),h=c&&(1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY1)||1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY2)||1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY3));c=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_SHIFT_KEY);a.altKey=e,a.ctrlKey=g,a.metaKey=h,a.shiftKey=c},a._createEvent=function(a){var b={preventDefault:function(){}};return b.target=a,b},a}()},function(a,b,c){c.d(b,"a",function(){return g});var d=c(51),e=c(0),f=c(22),g=function(){function a(a,b,c){this.vectors=d.a.BuildArray(8,e.e.Zero),this.center=e.e.Zero(),this.centerWorld=e.e.Zero(),this.extendSize=e.e.Zero(),this.extendSizeWorld=e.e.Zero(),this.directions=d.a.BuildArray(3,e.e.Zero),this.vectorsWorld=d.a.BuildArray(8,e.e.Zero),this.minimumWorld=e.e.Zero(),this.maximumWorld=e.e.Zero(),this.minimum=e.e.Zero(),this.maximum=e.e.Zero(),this.reConstruct(a,b,c)}return a.prototype.reConstruct=function(a,b,c){var d=a.x,f=a.y,g=a.z,h=b.x,i=b.y,j=b.z,k=this.vectors;this.minimum.copyFromFloats(d,f,g),this.maximum.copyFromFloats(h,i,j),k[0].copyFromFloats(d,f,g),k[1].copyFromFloats(h,i,j),k[2].copyFromFloats(h,f,g),k[3].copyFromFloats(d,i,g),k[4].copyFromFloats(d,f,j),k[5].copyFromFloats(h,i,g),k[6].copyFromFloats(d,i,j),k[7].copyFromFloats(h,f,j),b.addToRef(a,this.center).scaleInPlace(.5),b.subtractToRef(a,this.extendSize).scaleInPlace(.5),this._worldMatrix=c||e.a.IdentityReadOnly,this._update(this._worldMatrix)},a.prototype.scale=function(b){var c=a.TmpVector3,d=this.maximum.subtractToRef(this.minimum,c[0]),e=d.length();d.normalizeFromLength(e);e=e*b;b=d.scaleInPlace(.5*e);d=this.center.subtractToRef(b,c[1]);e=this.center.addToRef(b,c[2]);return this.reConstruct(d,e,this._worldMatrix),this},a.prototype.getWorldMatrix=function(){return this._worldMatrix},a.prototype._update=function(a){var b=this.minimumWorld,c=this.maximumWorld,d=this.directions,f=this.vectorsWorld,g=this.vectors;if(a.isIdentity()){b.copyFrom(this.minimum),c.copyFrom(this.maximum);for(h=0;h<8;++h)f[h].copyFrom(g[h]);this.extendSizeWorld.copyFrom(this.extendSize),this.centerWorld.copyFrom(this.center)}else{b.setAll(Number.MAX_VALUE),c.setAll(-Number.MAX_VALUE);for(var h=0;h<8;++h){var i=f[h];e.e.TransformCoordinatesToRef(g[h],a,i),b.minimizeInPlace(i),c.maximizeInPlace(i)}c.subtractToRef(b,this.extendSizeWorld).scaleInPlace(.5),c.addToRef(b,this.centerWorld).scaleInPlace(.5)}e.e.FromArrayToRef(a.m,0,d[0]),e.e.FromArrayToRef(a.m,4,d[1]),e.e.FromArrayToRef(a.m,8,d[2]),this._worldMatrix=a},a.prototype.isInFrustum=function(b){return a.IsInFrustum(this.vectorsWorld,b)},a.prototype.isCompletelyInFrustum=function(b){return a.IsCompletelyInFrustum(this.vectorsWorld,b)},a.prototype.intersectsPoint=function(a){var b=this.minimumWorld,c=this.maximumWorld,d=b.x,e=b.y;b=b.z;var g=c.x,h=c.y;c=c.z;var i=a.x,j=a.y;a=a.z;var k=-f.a;return!(g-ii-d)&&!(h-jj-e)&&!(c-aa-b)},a.prototype.intersectsSphere=function(b){return a.IntersectsSphere(this.minimumWorld,this.maximumWorld,b.centerWorld,b.radiusWorld)},a.prototype.intersectsMinMax=function(a,b){var c=this.minimumWorld,d=this.maximumWorld,e=c.x,f=c.y;c=c.z;var g=d.x,h=d.y;d=d.z;var i=a.x,j=a.y;a=a.z;var k=b.x,l=b.y;b=b.z;return!(gk)&&!(hl)&&!(db)},a.Intersects=function(a,b){return a.intersectsMinMax(b.minimumWorld,b.maximumWorld)},a.IntersectsSphere=function(b,c,d,f){var g=a.TmpVector3[0];return e.e.ClampToRef(d,b,c,g),e.e.DistanceSquared(d,g)<=f*f},a.IsCompletelyInFrustum=function(a,b){for(var c=0;c<6;++c)for(var d=b[c],e=0;e<8;++e)if(d.dotCoordinate(a[e])<0)return!1;return!0},a.IsInFrustum=function(a,b){for(var c=0;c<6;++c){for(var d=!0,e=b[c],f=0;f<8;++f)if(e.dotCoordinate(a[f])>=0){d=!1;break}if(d)return!1}return!0},a.TmpVector3=d.a.BuildArray(3,e.e.Zero),a}()},function(a,b,c){c.d(b,"a",function(){return g});var d=c(24),e=c(85),f=c(1),g=function(){function a(a,b,c,d){this._textures=null,this._attachments=null,this._generateStencilBuffer=!1,this._generateDepthBuffer=!1,this._depthStencilTextureWithStencil=!1,this._isMulti=a,this._isCube=b,this._size=c,this._engine=d,this._depthStencilTexture=null}return Object.defineProperty(a.prototype,"isCube",{get:function(){return this._isCube},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isMulti",{get:function(){return this._isMulti},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is2DArray",{get:function(){return this.layers>0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"size",{get:function(){return this.width},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"width",{get:function(){return this._size.width||this._size},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"height",{get:function(){return this._size.height||this._size},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"layers",{get:function(){return this._size.layers||0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"texture",{get:function(){var a;return null!==(a=null===(a=this._textures)||void 0===a?void 0:a[0])&&void 0!==a?a:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"textures",{get:function(){return this._textures},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"samples",{get:function(){var a;return null!==(a=null===(a=this.texture)||void 0===a?void 0:a.samples)&&void 0!==a?a:1},enumerable:!1,configurable:!0}),a.prototype.setSamples=function(a,b,c){return void 0===b&&(b=!0),void 0===c&&(c=!1),this.samples!==a||c?this._isMulti?this._engine.updateMultipleRenderTargetTextureSampleCount(this,a,b):this._engine.updateRenderTargetTextureSampleCount(this,a):a},a.prototype.setTextures=function(a){Array.isArray(a)?this._textures=a:this._textures=a?[a]:null},a.prototype.setTexture=function(a,b,c){void 0===b&&(b=0),void 0===c&&(c=!0),this._textures||(this._textures=[]),this._textures[b]&&c&&this._textures[b].dispose(),this._textures[b]=a},a.prototype.createDepthStencilTexture=function(a,b,c,d,e){var g;return void 0===a&&(a=0),void 0===b&&(b=!0),void 0===c&&(c=!1),void 0===d&&(d=1),void 0===e&&(e=f.a.TEXTUREFORMAT_DEPTH16),null===(g=this._depthStencilTexture)||void 0===g||g.dispose(),this._depthStencilTextureWithStencil=c,this._depthStencilTexture=this._engine.createDepthStencilTexture(this._size,{bilinearFiltering:b,comparisonFunction:a,generateStencil:c,isCube:this._isCube,samples:d,depthTextureFormat:e},this),this._depthStencilTexture},a.prototype._shareDepth=function(a){this._depthStencilTexture&&(a._depthStencilTexture&&a._depthStencilTexture.dispose(),a._depthStencilTexture=this._depthStencilTexture,this._depthStencilTexture.incrementReferences())},a.prototype._swapAndDie=function(a){a&&this.texture&&this.texture._swapAndDie(a),this._textures=null,this.dispose(!0)},a.prototype._cloneRenderTargetWrapper=function(){var a=null;if(this._isMulti){var b=this.textures;if(b&&b.length>0){var c=!1,f=b.length,g=b[b.length-1]._source;g!==d.b.Depth&&g!==d.b.DepthStencil||(c=!0,f--);for(var g=[],h=[],i=0;i1&&a.setSamples(this.samples),a._swapRenderTargetWrapper(this),a.dispose()}},a.prototype.releaseTextures=function(){var a;if(this._textures)for(var b=0;null!==(a=b<(null===(a=this._textures)||void 0===a?void 0:a.length))&&void 0!==a&&a;++b)this._textures[b].dispose();this._textures=null},a.prototype.dispose=function(a){void 0===a&&(a=!1),a||(null===(a=this._depthStencilTexture)||void 0===a||a.dispose(),this._depthStencilTexture=null,this.releaseTextures()),this._engine._releaseRenderTargetWrapper(this)},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.previousWorldMatrices={},this.previousBones={}}return a.AddUniforms=function(a){a.push("previousWorld","previousViewProjection","mPreviousBones")},a.AddSamplers=function(a){},a.prototype.bindForSubMesh=function(a,b,c,e,f){if(b.prePassRenderer&&b.prePassRenderer.enabled&&b.prePassRenderer.currentRTisSceneRT&&-1!==b.prePassRenderer.getIndex(d.a.PREPASS_VELOCITY_TEXTURE_TYPE)){this.previousWorldMatrices[c.uniqueId]||(this.previousWorldMatrices[c.uniqueId]=e.clone()),this.previousViewProjection||(this.previousViewProjection=b.getTransformMatrix().clone(),this.currentViewProjection=b.getTransformMatrix().clone());f=b.getEngine();this.currentViewProjection.updateFlag!==b.getTransformMatrix().updateFlag?(this._lastUpdateFrameId=f.frameId,this.previousViewProjection.copyFrom(this.currentViewProjection),this.currentViewProjection.copyFrom(b.getTransformMatrix())):this._lastUpdateFrameId!==f.frameId&&(this._lastUpdateFrameId=f.frameId,this.previousViewProjection.copyFrom(this.currentViewProjection)),a.setMatrix("previousWorld",this.previousWorldMatrices[c.uniqueId]),a.setMatrix("previousViewProjection",this.previousViewProjection),this.previousWorldMatrices[c.uniqueId]=e.clone()}},a}()},function(a,b,c){a="imageProcessingDeclaration";b="#ifdef EXPOSURE uniform float exposureLinear; #endif #ifdef CONTRAST uniform float contrast; #endif #ifdef VIGNETTE uniform vec2 vInverseScreenSize; uniform vec4 vignetteSettings1; uniform vec4 vignetteSettings2; #endif #ifdef COLORCURVES uniform vec4 vCameraColorCurveNegative; uniform vec4 vCameraColorCurveNeutral; uniform vec4 vCameraColorCurvePositive; #endif #ifdef COLORGRADING #ifdef COLORGRADING3D uniform highp sampler3D txColorTransform; #else uniform sampler2D txColorTransform; #endif uniform vec4 colorTransformSettings; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="imageProcessingFunctions";b="#if defined(COLORGRADING) && !defined(COLORGRADING3D) #define inline vec3 sampleTexture3D(sampler2D colorTransform,vec3 color,vec2 sampler3dSetting) { float sliceSize=2.0*sampler3dSetting.x; #ifdef SAMPLER3DGREENDEPTH float sliceContinuous=(color.g-sampler3dSetting.x)*sampler3dSetting.y; #else float sliceContinuous=(color.b-sampler3dSetting.x)*sampler3dSetting.y; #endif float sliceInteger=floor(sliceContinuous); float sliceFraction=sliceContinuous-sliceInteger; #ifdef SAMPLER3DGREENDEPTH vec2 sliceUV=color.rb; #else vec2 sliceUV=color.rg; #endif sliceUV.x*=sliceSize; sliceUV.x+=sliceInteger*sliceSize; sliceUV=saturate(sliceUV); vec4 slice0Color=texture2D(colorTransform,sliceUV); sliceUV.x+=sliceSize; sliceUV=saturate(sliceUV); vec4 slice1Color=texture2D(colorTransform,sliceUV); vec3 result=mix(slice0Color.rgb,slice1Color.rgb,sliceFraction); #ifdef SAMPLER3DBGRMAP color.rgb=result.rgb; #else color.rgb=result.bgr; #endif return color; } #endif #ifdef TONEMAPPING_ACES const mat3 ACESInputMat=mat3( vec3(0.59719,0.07600,0.02840), vec3(0.35458,0.90834,0.13383), vec3(0.04823,0.01566,0.83777) ); const mat3 ACESOutputMat=mat3( vec3( 1.60475,-0.10208,-0.00327), vec3(-0.53108,1.10813,-0.07276), vec3(-0.07367,-0.00605,1.07602) ); vec3 RRTAndODTFit(vec3 v) { vec3 a=v*(v+0.0245786)-0.000090537; vec3 b=v*(0.983729*v+0.4329510)+0.238081; return a/b; } vec3 ACESFitted(vec3 color) { color=ACESInputMat*color; color=RRTAndODTFit(color); color=ACESOutputMat*color; color=saturate(color); return color; } #endif vec4 applyImageProcessing(vec4 result) { #ifdef EXPOSURE result.rgb*=exposureLinear; #endif #ifdef VIGNETTE vec2 viewportXY=gl_FragCoord.xy*vInverseScreenSize; viewportXY=viewportXY*2.0-1.0; vec3 vignetteXY1=vec3(viewportXY*vignetteSettings1.xy+vignetteSettings1.zw,1.0); float vignetteTerm=dot(vignetteXY1,vignetteXY1); float vignette=pow(vignetteTerm,vignetteSettings2.w); vec3 vignetteColor=vignetteSettings2.rgb; #ifdef VIGNETTEBLENDMODEMULTIPLY vec3 vignetteColorMultiplier=mix(vignetteColor,vec3(1,1,1),vignette); result.rgb*=vignetteColorMultiplier; #endif #ifdef VIGNETTEBLENDMODEOPAQUE result.rgb=mix(vignetteColor,result.rgb,vignette); #endif #endif #ifdef TONEMAPPING #ifdef TONEMAPPING_ACES result.rgb=ACESFitted(result.rgb); #else const float tonemappingCalibration=1.590579; result.rgb=1.0-exp2(-tonemappingCalibration*result.rgb); #endif #endif result.rgb=toGammaSpace(result.rgb); result.rgb=saturate(result.rgb); #ifdef CONTRAST vec3 resultHighContrast=result.rgb*result.rgb*(3.0-2.0*result.rgb); if (contrast<1.0) { result.rgb=mix(vec3(0.5,0.5,0.5),result.rgb,contrast); } else { result.rgb=mix(result.rgb,resultHighContrast,contrast-1.0); } #endif #ifdef COLORGRADING vec3 colorTransformInput=result.rgb*colorTransformSettings.xxx+colorTransformSettings.yyy; #ifdef COLORGRADING3D vec3 colorTransformOutput=texture(txColorTransform,colorTransformInput).rgb; #else vec3 colorTransformOutput=sampleTexture3D(txColorTransform,colorTransformInput,colorTransformSettings.yz).rgb; #endif result.rgb=mix(result.rgb,colorTransformOutput,colorTransformSettings.www); #endif #ifdef COLORCURVES float luma=getLuminance(result.rgb); vec2 curveMix=clamp(vec2(luma*3.0-1.5,luma*-3.0+1.5),vec2(0.0),vec2(1.0)); vec4 colorCurve=vCameraColorCurveNeutral+curveMix.x*vCameraColorCurvePositive-curveMix.y*vCameraColorCurveNegative; result.rgb*=colorCurve.rgb; result.rgb=mix(vec3(luma),result.rgb,colorCurve.a); #endif return result; }";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneFragmentDeclaration";b="#ifdef CLIPPLANE varying float fClipDistance; #endif #ifdef CLIPPLANE2 varying float fClipDistance2; #endif #ifdef CLIPPLANE3 varying float fClipDistance3; #endif #ifdef CLIPPLANE4 varying float fClipDistance4; #endif #ifdef CLIPPLANE5 varying float fClipDistance5; #endif #ifdef CLIPPLANE6 varying float fClipDistance6; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneVertexDeclaration";b="#ifdef CLIPPLANE uniform vec4 vClipPlane; varying float fClipDistance; #endif #ifdef CLIPPLANE2 uniform vec4 vClipPlane2; varying float fClipDistance2; #endif #ifdef CLIPPLANE3 uniform vec4 vClipPlane3; varying float fClipDistance3; #endif #ifdef CLIPPLANE4 uniform vec4 vClipPlane4; varying float fClipDistance4; #endif #ifdef CLIPPLANE5 uniform vec4 vClipPlane5; varying float fClipDistance5; #endif #ifdef CLIPPLANE6 uniform vec4 vClipPlane6; varying float fClipDistance6; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return e});a=c(27);var d=c(1);function e(a,b,c,e){switch(void 0===c&&(c=!1),a){case d.a.TEXTURETYPE_BYTE:a=new Int8Array(b);return e&&a.set(new Int8Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_BYTE:a=new Uint8Array(b);return e&&a.set(new Uint8Array(e)),a;case d.a.TEXTURETYPE_SHORT:a=b instanceof ArrayBuffer?new Int16Array(b):new Int16Array(c?b/2:b);return e&&a.set(new Int16Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_SHORT:case d.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:case d.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:case d.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:case d.a.TEXTURETYPE_HALF_FLOAT:a=b instanceof ArrayBuffer?new Uint16Array(b):new Uint16Array(c?b/2:b);return e&&a.set(new Uint16Array(e)),a;case d.a.TEXTURETYPE_INT:a=b instanceof ArrayBuffer?new Int32Array(b):new Int32Array(c?b/4:b);return e&&a.set(new Int32Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_INTEGER:case d.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:case d.a.TEXTURETYPE_UNSIGNED_INT_24_8:case d.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:case d.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:case d.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV:a=b instanceof ArrayBuffer?new Uint32Array(b):new Uint32Array(c?b/4:b);return e&&a.set(new Uint32Array(e)),a;case d.a.TEXTURETYPE_FLOAT:a=b instanceof ArrayBuffer?new Float32Array(b):new Float32Array(c?b/4:b);return e&&a.set(new Float32Array(e)),a}c=new Uint8Array(b);return e&&c.set(new Uint8Array(e)),c}a.a.prototype._readTexturePixelsSync=function(a,b,c,f,d,g,h,i){void 0===f&&(f=-1),void 0===d&&(d=0),void 0===g&&(g=null),void 0===h&&(h=!0),void 0===i&&(i=!1);var j=this._gl;if(!j)throw new Error("Engine does not have gl rendering context.");if(!this._dummyFramebuffer){var k=j.createFramebuffer();if(!k)throw new Error("Unable to create dummy framebuffer");this._dummyFramebuffer=k}j.bindFramebuffer(j.FRAMEBUFFER,this._dummyFramebuffer),f>-1?j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_CUBE_MAP_POSITIVE_X+f,null===(k=a._hardwareTexture)||void 0===k?void 0:k.underlyingResource,d):j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_2D,null===(f=a._hardwareTexture)||void 0===f?void 0:f.underlyingResource,d);k=void 0!==a.type?this._getWebGLTextureType(a.type):j.UNSIGNED_BYTE;if(i)g||(g=e(a.type,4*b*c));else switch(k){case j.UNSIGNED_BYTE:g||(g=new Uint8Array(4*b*c)),k=j.UNSIGNED_BYTE;break;default:g||(g=new Float32Array(4*b*c)),k=j.FLOAT}return h&&this.flushFramebuffer(),j.readPixels(0,0,b,c,j.RGBA,k,g),j.bindFramebuffer(j.FRAMEBUFFER,this._currentFramebuffer),g},a.a.prototype._readTexturePixels=function(a,b,c,f,d,e,g,h){return void 0===f&&(f=-1),void 0===d&&(d=0),void 0===e&&(e=null),void 0===g&&(g=!0),void 0===h&&(h=!1),Promise.resolve(this._readTexturePixelsSync(a,b,c,f,d,e,g,h))}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._count=0,this._data={}}return a.prototype.copyFrom=function(a){var b=this;this.clear(),a.forEach(function(a,c){return b.add(a,c)})},a.prototype.get=function(a){a=this._data[a];if(void 0!==a)return a},a.prototype.getOrAddWithFactory=function(a,b){var c=this.get(a);return void 0!==c||(c=b(a))&&this.add(a,c),c},a.prototype.getOrAdd=function(a,b){var c=this.get(a);return void 0!==c?c:(this.add(a,b),b)},a.prototype.contains=function(a){return void 0!==this._data[a]},a.prototype.add=function(a,b){return void 0===this._data[a]&&(this._data[a]=b,++this._count,!0)},a.prototype.set=function(a,b){return void 0!==this._data[a]&&(this._data[a]=b,!0)},a.prototype.getAndRemove=function(a){var b=this.get(a);return void 0!==b?(delete this._data[a],--this._count,b):null},a.prototype.remove=function(a){return!!this.contains(a)&&(delete this._data[a],--this._count,!0)},a.prototype.clear=function(){this._data={},this._count=0},Object.defineProperty(a.prototype,"count",{get:function(){return this._count},enumerable:!1,configurable:!0}),a.prototype.forEach=function(a){for(var b in this._data)a(b,this._data[b])},a.prototype.first=function(a){for(var b in this._data){var c=a(b,this._data[b]);if(c)return c}return null},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a,b,c){this.bu=a,this.bv=b,this.distance=c,this.faceId=0,this.subMeshId=0}},function(a,b,c){a="logDepthDeclaration";b="#ifdef LOGARITHMICDEPTH uniform float logarithmicDepthConstant; varying float vFragmentDepth; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a){void 0===a&&(a=!0),this._isDepthTestDirty=!1,this._isDepthMaskDirty=!1,this._isDepthFuncDirty=!1,this._isCullFaceDirty=!1,this._isCullDirty=!1,this._isZOffsetDirty=!1,this._isFrontFaceDirty=!1,a&&this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isDepthFuncDirty||this._isDepthTestDirty||this._isDepthMaskDirty||this._isCullFaceDirty||this._isCullDirty||this._isZOffsetDirty||this._isFrontFaceDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zOffset",{get:function(){return this._zOffset},set:function(a){this._zOffset!==a&&(this._zOffset=a,this._isZOffsetDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zOffsetUnits",{get:function(){return this._zOffsetUnits},set:function(a){this._zOffsetUnits!==a&&(this._zOffsetUnits=a,this._isZOffsetDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cullFace",{get:function(){return this._cullFace},set:function(a){this._cullFace!==a&&(this._cullFace=a,this._isCullFaceDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cull",{get:function(){return this._cull},set:function(a){this._cull!==a&&(this._cull=a,this._isCullDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthFunc",{get:function(){return this._depthFunc},set:function(a){this._depthFunc!==a&&(this._depthFunc=a,this._isDepthFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthMask",{get:function(){return this._depthMask},set:function(a){this._depthMask!==a&&(this._depthMask=a,this._isDepthMaskDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthTest",{get:function(){return this._depthTest},set:function(a){this._depthTest!==a&&(this._depthTest=a,this._isDepthTestDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frontFace",{get:function(){return this._frontFace},set:function(a){this._frontFace!==a&&(this._frontFace=a,this._isFrontFaceDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this._depthMask=!0,this._depthTest=!0,this._depthFunc=null,this._cullFace=null,this._cull=null,this._zOffset=0,this._zOffsetUnits=0,this._frontFace=null,this._isDepthTestDirty=!0,this._isDepthMaskDirty=!0,this._isDepthFuncDirty=!1,this._isCullFaceDirty=!1,this._isCullDirty=!1,this._isZOffsetDirty=!0,this._isFrontFaceDirty=!1},a.prototype.apply=function(a){this.isDirty&&(this._isCullDirty&&(this.cull?a.enable(a.CULL_FACE):a.disable(a.CULL_FACE),this._isCullDirty=!1),this._isCullFaceDirty&&(a.cullFace(this.cullFace),this._isCullFaceDirty=!1),this._isDepthMaskDirty&&(a.depthMask(this.depthMask),this._isDepthMaskDirty=!1),this._isDepthTestDirty&&(this.depthTest?a.enable(a.DEPTH_TEST):a.disable(a.DEPTH_TEST),this._isDepthTestDirty=!1),this._isDepthFuncDirty&&(a.depthFunc(this.depthFunc),this._isDepthFuncDirty=!1),this._isZOffsetDirty&&(this.zOffset||this.zOffsetUnits?(a.enable(a.POLYGON_OFFSET_FILL),a.polygonOffset(this.zOffset,this.zOffsetUnits)):a.disable(a.POLYGON_OFFSET_FILL),this._isZOffsetDirty=!1),this._isFrontFaceDirty&&(a.frontFace(this.frontFace),this._isFrontFaceDirty=!1))},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.samplingMode=-1,this._useMipMaps=!0,this._cachedWrapU=null,this._cachedWrapV=null,this._cachedWrapR=null,this._cachedAnisotropicFilteringLevel=null,this._comparisonFunction=0}return Object.defineProperty(a.prototype,"wrapU",{get:function(){return this._cachedWrapU},set:function(a){this._cachedWrapU=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapV",{get:function(){return this._cachedWrapV},set:function(a){this._cachedWrapV=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapR",{get:function(){return this._cachedWrapR},set:function(a){this._cachedWrapR=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"anisotropicFilteringLevel",{get:function(){return this._cachedAnisotropicFilteringLevel},set:function(a){this._cachedAnisotropicFilteringLevel=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"comparisonFunction",{get:function(){return this._comparisonFunction},set:function(a){this._comparisonFunction=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useMipMaps",{get:function(){return this._useMipMaps},set:function(a){this._useMipMaps=a},enumerable:!1,configurable:!0}),a.prototype.setParameters=function(a,b,c,e,f,g){return void 0===a&&(a=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===b&&(b=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===c&&(c=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===e&&(e=1),void 0===f&&(f=d.a.TEXTURE_BILINEAR_SAMPLINGMODE),void 0===g&&(g=0),this._cachedWrapU=a,this._cachedWrapV=b,this._cachedWrapR=c,this._cachedAnisotropicFilteringLevel=e,this.samplingMode=f,this._comparisonFunction=g,this},a.prototype.compareSampler=function(a){return this._cachedWrapU===a._cachedWrapU&&this._cachedWrapV===a._cachedWrapV&&this._cachedWrapR===a._cachedWrapR&&this._cachedAnisotropicFilteringLevel===a._cachedAnisotropicFilteringLevel&&this.samplingMode===a.samplingMode&&this._comparisonFunction===a._comparisonFunction&&this._useMipMaps===a._useMipMaps},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(41),e=function(){function a(){this.shaderLanguage=d.a.GLSL}return a.prototype.attributeProcessor=function(a){return a.replace("attribute","in")},a.prototype.varyingProcessor=function(a,b){return a.replace("varying",b?"in":"out")},a.prototype.postProcessor=function(a,b,c,d,e){d=-1!==a.search(/#extension.+GL_EXT_draw_buffers.+require/);if(a=(a=a.replace(/#extension.+(GL_OVR_multiview2|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g,"")).replace(/texture2Ds*(/g,"texture("),c)a=(a=(a=(a=(a=(a=(a=a.replace(/texture2DLodEXTs*(/g,"textureLod(")).replace(/textureCubeLodEXTs*(/g,"textureLod(")).replace(/textureCubes*(/g,"texture(")).replace(/gl_FragDepthEXT/g,"gl_FragDepth")).replace(/gl_FragColor/g,"glFragColor")).replace(/gl_FragData/g,"glFragData")).replace(/voids+?mains*(/g,(d?"":"out vec4 glFragColor; ")+"void main(");else if(-1!==b.indexOf("#define MULTIVIEW"))return"#extension GL_OVR_multiview2 : require layout (num_views = 2) in; "+a;return a},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b){if(void 0===a&&(a=null),this._MSAARenderBuffer=null,this._context=b,!a&&!(a=b.createTexture()))throw new Error("Unable to create webGL texture");this.set(a)}return Object.defineProperty(a.prototype,"underlyingResource",{get:function(){return this._webGLTexture},enumerable:!1,configurable:!0}),a.prototype.setUsage=function(a,b,c,d,e){},a.prototype.set=function(a){this._webGLTexture=a},a.prototype.reset=function(){this._webGLTexture=null,this._MSAARenderBuffer=null},a.prototype.release=function(){this._MSAARenderBuffer&&(this._context.deleteRenderbuffer(this._MSAARenderBuffer),this._MSAARenderBuffer=null),this._webGLTexture&&this._context.deleteTexture(this._webGLTexture),this.reset()},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a){void 0===a&&(a=!0),this._isStencilTestDirty=!1,this._isStencilMaskDirty=!1,this._isStencilFuncDirty=!1,this._isStencilOpDirty=!1,this.useStencilGlobalOnly=!1,a&&this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isStencilTestDirty||this._isStencilMaskDirty||this._isStencilFuncDirty||this._isStencilOpDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"func",{get:function(){return this._func},set:function(a){this._func!==a&&(this._func=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"funcRef",{get:function(){return this._funcRef},set:function(a){this._funcRef!==a&&(this._funcRef=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"funcMask",{get:function(){return this._funcMask},set:function(a){this._funcMask!==a&&(this._funcMask=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opStencilFail",{get:function(){return this._opStencilFail},set:function(a){this._opStencilFail!==a&&(this._opStencilFail=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opDepthFail",{get:function(){return this._opDepthFail},set:function(a){this._opDepthFail!==a&&(this._opDepthFail=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opStencilDepthPass",{get:function(){return this._opStencilDepthPass},set:function(a){this._opStencilDepthPass!==a&&(this._opStencilDepthPass=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mask",{get:function(){return this._mask},set:function(a){this._mask!==a&&(this._mask=a,this._isStencilMaskDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enabled",{get:function(){return this._enabled},set:function(a){this._enabled!==a&&(this._enabled=a,this._isStencilTestDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.reset=function(){var a;this.stencilMaterial=void 0,null===(a=this.stencilGlobal)||void 0===a||a.reset(),this._isStencilTestDirty=!0,this._isStencilMaskDirty=!0,this._isStencilFuncDirty=!0,this._isStencilOpDirty=!0},a.prototype.apply=function(a){var b;if(a){b=!this.useStencilGlobalOnly&&!!(null===(b=this.stencilMaterial)||void 0===b?void 0:b.enabled);this.enabled=b?this.stencilMaterial.enabled:this.stencilGlobal.enabled,this.func=b?this.stencilMaterial.func:this.stencilGlobal.func,this.funcRef=b?this.stencilMaterial.funcRef:this.stencilGlobal.funcRef,this.funcMask=b?this.stencilMaterial.funcMask:this.stencilGlobal.funcMask,this.opStencilFail=b?this.stencilMaterial.opStencilFail:this.stencilGlobal.opStencilFail,this.opDepthFail=b?this.stencilMaterial.opDepthFail:this.stencilGlobal.opDepthFail,this.opStencilDepthPass=b?this.stencilMaterial.opStencilDepthPass:this.stencilGlobal.opStencilDepthPass,this.mask=b?this.stencilMaterial.mask:this.stencilGlobal.mask,this.isDirty&&(this._isStencilTestDirty&&(this.enabled?a.enable(a.STENCIL_TEST):a.disable(a.STENCIL_TEST),this._isStencilTestDirty=!1),this._isStencilMaskDirty&&(a.stencilMask(this.mask),this._isStencilMaskDirty=!1),this._isStencilFuncDirty&&(a.stencilFunc(this.func,this.funcRef,this.funcMask),this._isStencilFuncDirty=!1),this._isStencilOpDirty&&(a.stencilOp(this.opStencilFail,this.opDepthFail,this.opStencilDepthPass),this._isStencilOpDirty=!1))}},a}()},function(a,b,c){c.d(b,"a",function(){return f});var d=c(170),e=c(171),f=function(){function a(){}return a.Create=function(a){return a.deviceInputSystem||(a.deviceInputSystem="undefined"!=typeof _native&&_native.DeviceInputSystem?new d.a(new _native.DeviceInputSystem):new e.a(a)),a.deviceInputSystem},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.BindClipPlane=function(a,b){if(b.clipPlane){var c=b.clipPlane;a.setFloat4("vClipPlane",c.normal.x,c.normal.y,c.normal.z,c.d)}b.clipPlane2&&(c=b.clipPlane2,a.setFloat4("vClipPlane2",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane3&&(c=b.clipPlane3,a.setFloat4("vClipPlane3",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane4&&(c=b.clipPlane4,a.setFloat4("vClipPlane4",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane5&&(c=b.clipPlane5,a.setFloat4("vClipPlane5",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane6&&(c=b.clipPlane6,a.setFloat4("vClipPlane6",c.normal.x,c.normal.y,c.normal.z,c.d))},a}()},function(a,b,c){c.d(b,"c",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"a",function(){return i});var d=c(18),e=c(0),f=c(9);function g(a){void 0===a&&(a={subdivisions:2,tessellation:16,height:1,radius:.25,capSubdivisions:6});var b,c,f=Math.max(a.subdivisions?a.subdivisions:2,1),g=Math.max(a.tessellation?a.tessellation:16,3),h=Math.max(a.height?a.height:1,0),i=Math.max(a.radius?a.radius:.25,0),j=Math.max(a.capSubdivisions?a.capSubdivisions:6,1);g=g;f=f;var k=Math.max(a.radiusTop?a.radiusTop:i,0);i=Math.max(a.radiusBottom?a.radiusBottom:i,0);var l=h-(k+i),m=2*Math.PI,n=Math.max(a.topCapSubdivisions?a.topCapSubdivisions:j,1);j=Math.max(a.bottomCapSubdivisions?a.bottomCapSubdivisions:j,1);var o=Math.acos((i-k)/h),p=[],q=[],r=[],s=[],t=0,u=[];l=.5*l;var v=.5*Math.PI,w=e.e.Zero(),x=e.e.Zero(),y=Math.cos(o),z=Math.sin(o),A=new e.d(k*z,l+k*y).subtract(new e.d(i*z,i*y-l)).length(),B=k*o+A+i*(v-o),C=0;for(c=0;c<=n;c++){var D=[],E=v-o*(c/n);C+=k*o/n;var F=Math.cos(E),G=Math.sin(E),H=F*k;for(b=0;b<=g;b++){var I=(aa=b/g)*m+0,J=Math.sin(I),K=Math.cos(I);x.x=H*J,x.y=l+G*k,x.z=H*K,q.push(x.x,x.y,x.z),w.set(F*J,G,F*K),r.push(w.x,w.y,w.z),s.push(aa,1-C/B),D.push(t),t++}u.push(D)}h=h-k-i+y*k-y*i;var L=z*(i-k)/h;for(c=1;c<=f;c++){D=[];C+=A/f;H=z*(c*(i-k)/f+k);for(b=0;b<=g;b++)(I=(aa=b/g)*m+0,J=Math.sin(I),K=Math.cos(I)),(x.x=H*J,x.y=l+y*k-c*h/f,x.z=H*K,q.push(x.x,x.y,x.z),w.set(J,L,K).normalize(),r.push(w.x,w.y,w.z),s.push(aa,1-C/B),D.push(t),t++);u.push(D)}for(c=1;c<=j;c++){D=[],E=v-o-(Math.PI-o)*(c/j);C+=i*o/j;F=Math.cos(E),G=Math.sin(E),H=F*i;for(b=0;b<=g;b++){var aa;I=(aa=b/g)*m+0,J=Math.sin(I),K=Math.cos(I);x.x=H*J,x.y=G*i-l,x.z=H*K,q.push(x.x,x.y,x.z),w.set(F*J,G,F*K),r.push(w.x,w.y,w.z),s.push(aa,1-C/B),D.push(t),t++}u.push(D)}for(b=0;b=a&&0===c?b instanceof Array?this._gl.bufferSubData(this._gl.ARRAY_BUFFER,c,new Float32Array(b)):this._gl.bufferSubData(this._gl.ARRAY_BUFFER,c,b):b instanceof Array?this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,new Float32Array(b).subarray(c,c+d)):(b=b instanceof ArrayBuffer?new Uint8Array(b,c,d):new Uint8Array(b.buffer,b.byteOffset+c,d),this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,b)),this._resetVertexBufferBinding()}},function(a,b,c){a="sceneUboDeclaration";b="layout(std140,column_major) uniform; uniform Scene { mat4 viewProjection; #ifdef MULTIVIEW mat4 viewProjectionR; #endif mat4 view; mat4 projection; vec4 vEyePosition; }; ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="fogFragmentDeclaration";b="#ifdef FOG #define FOGMODE_NONE 0. #define FOGMODE_EXP 1. #define FOGMODE_EXP2 2. #define FOGMODE_LINEAR 3. #define E 2.71828 uniform vec4 vFogInfos; uniform vec3 vFogColor; varying vec3 vFogDistance; float CalcFogFactor() { float fogCoeff=1.0; float fogStart=vFogInfos.y; float fogEnd=vFogInfos.z; float fogDensity=vFogInfos.w; float fogDistance=length(vFogDistance); if (FOGMODE_LINEAR == vFogInfos.x) { fogCoeff=(fogEnd-fogDistance)/(fogEnd-fogStart); } else if (FOGMODE_EXP == vFogInfos.x) { fogCoeff=1.0/pow(E,fogDistance*fogDensity); } else if (FOGMODE_EXP2 == vFogInfos.x) { fogCoeff=1.0/pow(E,fogDistance*fogDistance*fogDensity*fogDensity); } return clamp(fogCoeff,0.0,1.0); } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){var d=c(27),e=c(24);d.a.prototype.createDynamicTexture=function(a,b,c,f){var g=new e.a(this,e.b.Dynamic);return g.baseWidth=a,g.baseHeight=b,c&&(a=this.needPOTTextures?d.a.GetExponentOfTwo(a,this._caps.maxTextureSize):a,b=this.needPOTTextures?d.a.GetExponentOfTwo(b,this._caps.maxTextureSize):b),g.width=a,g.height=b,g.isReady=!1,g.generateMipMaps=c,g.samplingMode=f,this.updateTextureSamplingMode(f,g),this._internalTexturesCache.push(g),g},d.a.prototype.updateDynamicTexture=function(a,b,c,d,e,f){if(void 0===d&&(d=!1),void 0===f&&(f=!1),a){var g=this._gl,h=g.TEXTURE_2D;f=this._bindTextureDirectly(h,a,!0,f);this._unpackFlipY(void 0===c?a.invertY:c),d&&g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL,1);c=this._getWebGLTextureType(a.type);e=this._getInternalFormat(e||a.format);var i=this._getRGBABufferInternalSizedFormat(a.type,e);g.texImage2D(h,0,i,e,c,b),a.generateMipMaps&&g.generateMipmap(h),f||this._bindTextureDirectly(h,null),d&&g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL,0),a.isReady=!0}}},function(a,b,c){var d;c.d(b,"a",function(){return d}),c.d(b,"b",function(){return e});var e=function(a,b,c,e,f,g,h,i,j,k){d={DecodeBase64UrlToBinary:a,DecodeBase64UrlToString:b,DefaultRetryStrategy:c.DefaultRetryStrategy,BaseUrl:c.BaseUrl,CorsBehavior:c.CorsBehavior,PreprocessUrl:c.PreprocessUrl,IsBase64DataUrl:e,IsFileURL:f,LoadFile:g,LoadImage:h,ReadFile:i,RequestFile:j,SetCorsBehavior:k},Object.defineProperty(d,"DefaultRetryStrategy",{get:function(){return c.DefaultRetryStrategy},set:function(a){c.DefaultRetryStrategy=a}}),Object.defineProperty(d,"BaseUrl",{get:function(){return c.BaseUrl},set:function(a){c.BaseUrl=a}}),Object.defineProperty(d,"PreprocessUrl",{get:function(){return c.PreprocessUrl},set:function(a){c.PreprocessUrl=a}}),Object.defineProperty(d,"CorsBehavior",{get:function(){return c.CorsBehavior},set:function(a){c.CorsBehavior=a}})}},function(a,b,c){c.d(b,"a",function(){return e});var d=c(35),e=function(){function a(b,c){a.IsAvailable&&(this._observer=new window.ComputePressureObserver(b,c))}return Object.defineProperty(a,"IsAvailable",{get:function(){return Object(d.e)()&&"ComputePressureObserver"in window},enumerable:!1,configurable:!0}),a.prototype.observe=function(){var a;(null===(a=this._observer)||void 0===a?void 0:a.observe)&&(null===(a=this._observer)||void 0===a||a.observe())},a.prototype.unobserve=function(){var a;(null===(a=this._observer)||void 0===a?void 0:a.unobserve)&&(null===(a=this._observer)||void 0===a||a.unobserve())},a}()},function(a,b,c){c.d(b,"b",function(){return e}),c.d(b,"c",function(){return f}),c.d(b,"d",function(){return g}),c.d(b,"a",function(){return h});var d=c(2);function e(a,b,c){void 0===c&&(c=!1);var d=b.width;b=b.height;if(a instanceof Float32Array){for(var e=a.byteLength/a.BYTES_PER_ELEMENT,f=new Uint8Array(e);--e>=0;){var g=a[e];g<0?g=0:g>1&&(g=1),f[e]=255*g}a=f}g=document.createElement("canvas");g.width=d,g.height=b;e=g.getContext("2d");if(!e)return null;f=e.createImageData(d,b);if(f.data.set(a),e.putImageData(f,0,0),c){a=document.createElement("canvas");a.width=d,a.height=b;e=a.getContext("2d");return e?(e.translate(0,b),e.scale(1,-1),e.drawImage(g,0,0),a.toDataURL("image/png")):null}return g.toDataURL("image/png")}function f(a,b,c){void 0===b&&(b=0),void 0===c&&(c=0);var d=a.getInternalTexture();if(!d)return null;b=a._readPixelsSync(b,c);return b?e(b,a.getSize(),d.invertY):null}function g(a,b,c){return void 0===b&&(b=0),void 0===c&&(c=0),Object(d.b)(this,void 0,void 0,function(){var f,g;return Object(d.e)(this,function(d){switch(d.label){case 0:return(f=a.getInternalTexture())?[4,a.readPixels(b,c)]:[2,null];case 1:return(g=d.sent())?[2,e(g,a.getSize(),f.invertY)]:[2,null]}})})}var h={GenerateBase64StringFromPixelData:e,GenerateBase64StringFromTexture:f,GenerateBase64StringFromTextureAsync:g}},function(a,b,c){c.r(b),c.d(b,"AbstractScene",function(){return d.a}),c.d(b,"AbstractActionManager",function(){return e.a}),c.d(b,"Action",function(){return j}),c.d(b,"ActionEvent",function(){return k.a}),c.d(b,"ActionManager",function(){return E}),c.d(b,"Condition",function(){return m}),c.d(b,"ValueCondition",function(){return n}),c.d(b,"PredicateCondition",function(){return o}),c.d(b,"StateCondition",function(){return p}),c.d(b,"SwitchBooleanAction",function(){return s}),c.d(b,"SetStateAction",function(){return t}),c.d(b,"SetValueAction",function(){return u}),c.d(b,"IncrementValueAction",function(){return v}),c.d(b,"PlayAnimationAction",function(){return w}),c.d(b,"StopAnimationAction",function(){return x}),c.d(b,"DoNothingAction",function(){return y}),c.d(b,"CombineAction",function(){return z}),c.d(b,"ExecuteCodeAction",function(){return A}),c.d(b,"SetParentAction",function(){return B}),c.d(b,"PlaySoundAction",function(){return F}),c.d(b,"StopSoundAction",function(){return G}),c.d(b,"InterpolateValueAction",function(){return ba}),c.d(b,"Animatable",function(){return ka}),c.d(b,"_IAnimationState",function(){return N}),c.d(b,"Animation",function(){return O}),c.d(b,"TargetedAnimation",function(){return la}),c.d(b,"AnimationGroup",function(){return ma}),c.d(b,"AnimationPropertiesOverride",function(){return na}),c.d(b,"EasingFunction",function(){return pa}),c.d(b,"CircleEase",function(){return qa}),c.d(b,"BackEase",function(){return ra}),c.d(b,"BounceEase",function(){return sa}),c.d(b,"CubicEase",function(){return ta}),c.d(b,"ElasticEase",function(){return ua}),c.d(b,"ExponentialEase",function(){return va}),c.d(b,"PowerEase",function(){return wa}),c.d(b,"QuadraticEase",function(){return xa}),c.d(b,"QuarticEase",function(){return ya}),c.d(b,"QuinticEase",function(){return za}),c.d(b,"SineEase",function(){return Aa}),c.d(b,"BezierCurveEase",function(){return Ba}),c.d(b,"RuntimeAnimation",function(){return ha}),c.d(b,"AnimationEvent",function(){return Ca}),c.d(b,"AnimationKeyInterpolation",function(){return H}),c.d(b,"AnimationRange",function(){return K}),c.d(b,"PathCursor",function(){return Da}),c.d(b,"KeepAssets",function(){return Ea}),c.d(b,"InstantiatedEntries",function(){return Fa}),c.d(b,"AssetContainer",function(){return Ga}),c.d(b,"Analyser",function(){return Ha}),c.d(b,"AudioEngine",function(){return Ja}),c.d(b,"AudioSceneComponent",function(){return Oa}),c.d(b,"Sound",function(){return La}),c.d(b,"SoundTrack",function(){return Ma}),c.d(b,"WeightedSound",function(){return Sa}),c.d(b,"AutoRotationBehavior",function(){return Ua}),c.d(b,"BouncingBehavior",function(){return Va}),c.d(b,"FramingBehavior",function(){return Wa}),c.d(b,"AttachToBoxBehavior",function(){return Ya}),c.d(b,"FadeInOutBehavior",function(){return Za}),c.d(b,"MultiPointerScaleBehavior",function(){return ab}),c.d(b,"PointerDragBehavior",function(){return $a.a}),c.d(b,"SixDofDragBehavior",function(){return fb}),c.d(b,"SurfaceMagnetismBehavior",function(){return gb}),c.d(b,"BaseSixDofDragBehavior",function(){return db}),c.d(b,"FollowBehavior",function(){return ib}),c.d(b,"HandConstraintZone",function(){return Pa}),c.d(b,"HandConstraintOrientation",function(){return Qa}),c.d(b,"HandConstraintVisibility",function(){return Ra}),c.d(b,"HandConstraintBehavior",function(){return lb}),c.d(b,"Bone",function(){return ja}),c.d(b,"BoneIKController",function(){return mb}),c.d(b,"BoneLookController",function(){return nb}),c.d(b,"Skeleton",function(){return tb}),c.d(b,"Buffer",function(){return W.a}),c.d(b,"VertexBuffer",function(){return W.b}),c.d(b,"DataBuffer",function(){return ub.a}),c.d(b,"StorageBuffer",function(){return vb}),c.d(b,"BaseCameraMouseWheelInput",function(){return xb}),c.d(b,"BaseCameraPointersInput",function(){return yb}),c.d(b,"ArcRotateCameraGamepadInput",function(){return Eb}),c.d(b,"ArcRotateCameraKeyboardMoveInput",function(){return Gb}),c.d(b,"ArcRotateCameraMouseWheelInput",function(){return Ib}),c.d(b,"ArcRotateCameraPointersInput",function(){return Jb}),c.d(b,"ArcRotateCameraVRDeviceOrientationInput",function(){return Lb}),c.d(b,"FlyCameraKeyboardInput",function(){return Mb}),c.d(b,"FlyCameraMouseInput",function(){return Nb}),c.d(b,"FollowCameraKeyboardMoveInput",function(){return Ob}),c.d(b,"FollowCameraMouseWheelInput",function(){return Pb}),c.d(b,"FollowCameraPointersInput",function(){return Qb}),c.d(b,"FreeCameraDeviceOrientationInput",function(){return Xb}),c.d(b,"FreeCameraGamepadInput",function(){return Yb}),c.d(b,"FreeCameraKeyboardMoveInput",function(){return Rb}),c.d(b,"FreeCameraMouseInput",function(){return Tb}),c.d(b,"FreeCameraMouseWheelInput",function(){return Ub}),c.d(b,"FreeCameraTouchInput",function(){return Vb}),c.d(b,"FreeCameraVirtualJoystickInput",function(){return bc}),c.d(b,"CameraInputTypes",function(){return zb}),c.d(b,"CameraInputsManager",function(){return Ab}),c.d(b,"Camera",function(){return cb.a}),c.d(b,"TargetCamera",function(){return cc}),c.d(b,"FreeCamera",function(){return dc}),c.d(b,"FreeCameraInputsManager",function(){return Wb}),c.d(b,"TouchCamera",function(){return ec}),c.d(b,"ArcRotateCamera",function(){return fc}),c.d(b,"ArcRotateCameraInputsManager",function(){return Kb}),c.d(b,"DeviceOrientationCamera",function(){return gc}),c.d(b,"FlyCamera",function(){return ic}),c.d(b,"FlyCameraInputsManager",function(){return hc}),c.d(b,"FollowCamera",function(){return lc}),c.d(b,"ArcFollowCamera",function(){return mc}),c.d(b,"FollowCameraInputsManager",function(){return jc}),c.d(b,"GamepadCamera",function(){return zc}),c.d(b,"AnaglyphArcRotateCamera",function(){return Kc}),c.d(b,"AnaglyphFreeCamera",function(){return Lc}),c.d(b,"AnaglyphGamepadCamera",function(){return Mc}),c.d(b,"AnaglyphUniversalCamera",function(){return Nc}),c.d(b,"StereoscopicArcRotateCamera",function(){return Sc}),c.d(b,"StereoscopicFreeCamera",function(){return Tc}),c.d(b,"StereoscopicGamepadCamera",function(){return Uc}),c.d(b,"StereoscopicUniversalCamera",function(){return Vc}),c.d(b,"StereoscopicScreenUniversalCamera",function(){return Wc}),c.d(b,"UniversalCamera",function(){return yc}),c.d(b,"VirtualJoysticksCamera",function(){return Xc}),c.d(b,"VRCameraMetrics",function(){return Yc}),c.d(b,"VRDeviceOrientationArcRotateCamera",function(){return hd}),c.d(b,"VRDeviceOrientationFreeCamera",function(){return id}),c.d(b,"VRDeviceOrientationGamepadCamera",function(){return jd}),c.d(b,"OnAfterEnteringVRObservableEvent",function(){return Nd}),c.d(b,"VRExperienceHelper",function(){return Od}),c.d(b,"WebVRFreeCamera",function(){return md}),c.d(b,"setStereoscopicAnaglyphRigMode",function(){return Jc}),c.d(b,"setStereoscopicRigMode",function(){return Rc}),c.d(b,"setVRRigMode",function(){return gd}),c.d(b,"setWebVRRigMode",function(){return ld}),c.d(b,"Collider",function(){return Qd}),c.d(b,"DefaultCollisionCoordinator",function(){return Rd}),c.d(b,"PickingInfo",function(){return Td.a}),c.d(b,"IntersectionInfo",function(){return Ud.a}),c.d(b,"_MeshCollisionData",function(){return Vd.a}),c.d(b,"ComputeEffect",function(){return Yd}),c.d(b,"ComputeShader",function(){return ae}),c.d(b,"BoundingBox",function(){return be.a}),c.d(b,"BoundingInfo",function(){return ce.a}),c.d(b,"BoundingSphere",function(){return de.a}),c.d(b,"Octree",function(){return fe}),c.d(b,"OctreeBlock",function(){return ee}),c.d(b,"OctreeSceneComponent",function(){return ge}),c.d(b,"Ray",function(){return nc.a}),c.d(b,"AxesViewer",function(){return he.AxesViewer}),c.d(b,"BoneAxesViewer",function(){return he.BoneAxesViewer}),c.d(b,"DebugLayerTab",function(){return he.DebugLayerTab}),c.d(b,"DebugLayer",function(){return he.DebugLayer}),c.d(b,"PhysicsViewer",function(){return he.PhysicsViewer}),c.d(b,"RayHelper",function(){return he.RayHelper}),c.d(b,"SkeletonViewer",function(){return he.SkeletonViewer}),c.d(b,"DirectionalLightFrustumViewer",function(){return he.DirectionalLightFrustumViewer}),c.d(b,"DeviceInputSystem",function(){return ie.a}),c.d(b,"DeviceType",function(){return je.a}),c.d(b,"PointerInput",function(){return je.c}),c.d(b,"DualShockInput",function(){return je.b}),c.d(b,"XboxInput",function(){return je.e}),c.d(b,"SwitchInput",function(){return je.d}),c.d(b,"DeviceEventFactory",function(){return ke.a}),c.d(b,"NativeDeviceInputWrapper",function(){return le.a}),c.d(b,"WebDeviceInputSystem",function(){return me.a}),c.d(b,"DeviceSource",function(){return ne}),c.d(b,"DeviceSourceManager",function(){return oe}),c.d(b,"Constants",function(){return r.a}),c.d(b,"ThinEngine",function(){return pb.a}),c.d(b,"Engine",function(){return T.a}),c.d(b,"EngineStore",function(){return C.a}),c.d(b,"NullEngineOptions",function(){return pe.b}),c.d(b,"NullEngine",function(){return pe.a}),c.d(b,"_OcclusionDataStorage",function(){return se}),c.d(b,"_forceTransformFeedbackToBundle",function(){return te}),c.d(b,"EngineView",function(){return we}),c.d(b,"allocateAndCopyTypedBuffer",function(){return xe.a}),c.d(b,"ComputeBindingType",function(){return Sd}),c.d(b,"NativeDataStream",function(){return ze}),c.d(b,"WebGLPipelineContext",function(){return $g.a}),c.d(b,"WebGLHardwareTexture",function(){return ah.a}),c.d(b,"PowerPreference",function(){return rf}),c.d(b,"FeatureName",function(){return sf}),c.d(b,"BufferUsage",function(){return tf}),c.d(b,"MapMode",function(){return uf}),c.d(b,"TextureDimension",function(){return vf}),c.d(b,"TextureUsage",function(){return wf}),c.d(b,"TextureViewDimension",function(){return xf}),c.d(b,"TextureAspect",function(){return yf}),c.d(b,"TextureFormat",function(){return Y}),c.d(b,"AddressMode",function(){return zf}),c.d(b,"FilterMode",function(){return Af}),c.d(b,"CompareFunction",function(){return Bf}),c.d(b,"ShaderStage",function(){return Cf}),c.d(b,"BufferBindingType",function(){return Df}),c.d(b,"SamplerBindingType",function(){return Ef}),c.d(b,"TextureSampleType",function(){return Ff}),c.d(b,"StorageTextureAccess",function(){return Gf}),c.d(b,"CompilationMessageType",function(){return Hf}),c.d(b,"PrimitiveTopology",function(){return If}),c.d(b,"FrontFace",function(){return Jf}),c.d(b,"CullMode",function(){return Kf}),c.d(b,"ColorWrite",function(){return Lf}),c.d(b,"BlendFactor",function(){return Mf}),c.d(b,"BlendOperation",function(){return Nf}),c.d(b,"StencilOperation",function(){return Of}),c.d(b,"IndexFormat",function(){return Pf}),c.d(b,"VertexFormat",function(){return Qf}),c.d(b,"InputStepMode",function(){return Rf}),c.d(b,"LoadOp",function(){return Sf}),c.d(b,"StoreOp",function(){return Tf}),c.d(b,"QueryType",function(){return Uf}),c.d(b,"PipelineStatisticName",function(){return Vf}),c.d(b,"CanvasCompositingAlphaMode",function(){return Wf}),c.d(b,"DeviceLostReason",function(){return Xf}),c.d(b,"ErrorFilter",function(){return Yf}),c.d(b,"WebGPUEngine",function(){return Ug}),c.d(b,"WebGPUCacheRenderPipeline",function(){return wg}),c.d(b,"WebGPUCacheRenderPipelineTree",function(){return yg}),c.d(b,"WebGPUCacheBindGroups",function(){return Hg}),c.d(b,"WebGPUCacheSampler",function(){return sg}),c.d(b,"WebGPUTintWASM",function(){return Tg}),c.d(b,"WebGL2ShaderProcessor",function(){return lf.a}),c.d(b,"NativeEngine",function(){return qf}),c.d(b,"ShaderCodeInliner",function(){return kf}),c.d(b,"PerformanceConfigurator",function(){return bh.a}),c.d(b,"EngineFactory",function(){return ch}),c.d(b,"ShaderStore",function(){return X.a}),c.d(b,"KeyboardEventTypes",function(){return Fb.a}),c.d(b,"KeyboardInfo",function(){return Fb.b}),c.d(b,"KeyboardInfoPre",function(){return Fb.c}),c.d(b,"PointerEventTypes",function(){return Ta.a}),c.d(b,"PointerInfoBase",function(){return Ta.c}),c.d(b,"PointerInfoPre",function(){return Ta.d}),c.d(b,"PointerInfo",function(){return Ta.b}),c.d(b,"ClipboardEventTypes",function(){return dh}),c.d(b,"ClipboardInfo",function(){return eh}),c.d(b,"DeviceInputEventType",function(){return wb.a}),c.d(b,"EventConstants",function(){return wb.b}),c.d(b,"DaydreamController",function(){return ih}),c.d(b,"GearVRController",function(){return jh}),c.d(b,"GenericController",function(){return kh}),c.d(b,"OculusTouchController",function(){return lh}),c.d(b,"PoseEnabledControllerType",function(){return kc}),c.d(b,"PoseEnabledControllerHelper",function(){return qc}),c.d(b,"PoseEnabledController",function(){return rc}),c.d(b,"ViveController",function(){return mh}),c.d(b,"WebVRController",function(){return nd}),c.d(b,"WindowsMotionController",function(){return oh}),c.d(b,"XRWindowsMotionController",function(){return ph}),c.d(b,"StickValues",function(){return Bb}),c.d(b,"Gamepad",function(){return Cb}),c.d(b,"GenericPad",function(){return Db}),c.d(b,"GamepadManager",function(){return wc}),c.d(b,"GamepadSystemSceneComponent",function(){return xc}),c.d(b,"Xbox360Button",function(){return oc}),c.d(b,"Xbox360Dpad",function(){return pc}),c.d(b,"Xbox360Pad",function(){return uc}),c.d(b,"DualShockButton",function(){return sc}),c.d(b,"DualShockDpad",function(){return tc}),c.d(b,"DualShockPad",function(){return vc}),c.d(b,"AxisDragGizmo",function(){return qh.a}),c.d(b,"AxisScaleGizmo",function(){return uh}),c.d(b,"BoundingBoxGizmo",function(){return yh}),c.d(b,"Gizmo",function(){return sh.a}),c.d(b,"GizmoManager",function(){return Jh}),c.d(b,"PlaneRotationGizmo",function(){return Bh}),c.d(b,"PositionGizmo",function(){return Eh}),c.d(b,"RotationGizmo",function(){return Ch}),c.d(b,"ScaleGizmo",function(){return Ih}),c.d(b,"LightGizmo",function(){return Th}),c.d(b,"CameraGizmo",function(){return Uh}),c.d(b,"PlaneDragGizmo",function(){return Dh}),c.d(b,"EnvironmentHelper",function(){return ei}),c.d(b,"PhotoDome",function(){return gi}),c.d(b,"_forceSceneHelpersToBundle",function(){return gj}),c.d(b,"VideoDome",function(){return ij}),c.d(b,"EngineInstrumentation",function(){return jj}),c.d(b,"SceneInstrumentation",function(){return kj}),c.d(b,"_TimeToken",function(){return qe}),c.d(b,"EffectLayer",function(){return lj}),c.d(b,"EffectLayerSceneComponent",function(){return mj}),c.d(b,"GlowLayer",function(){return nj}),c.d(b,"HighlightLayer",function(){return pj}),c.d(b,"Layer",function(){return rj}),c.d(b,"LayerSceneComponent",function(){return qj}),c.d(b,"LensFlare",function(){return sj}),c.d(b,"LensFlareSystem",function(){return tj}),c.d(b,"LensFlareSystemSceneComponent",function(){return uj}),c.d(b,"Light",function(){return Kh.a}),c.d(b,"ShadowLight",function(){return Lh}),c.d(b,"ShadowGenerator",function(){return vj}),c.d(b,"CascadedShadowGenerator",function(){return Ej}),c.d(b,"ShadowGeneratorSceneComponent",function(){return Fj}),c.d(b,"DirectionalLight",function(){return Mh}),c.d(b,"HemisphericLight",function(){return kd.a}),c.d(b,"PointLight",function(){return Gj}),c.d(b,"SpotLight",function(){return Sh}),c.d(b,"DefaultLoadingScreen",function(){return Hj}),c.d(b,"_BabylonLoaderRegistered",function(){return bk}),c.d(b,"BabylonFileLoaderConfiguration",function(){return ck}),c.d(b,"SceneLoaderAnimationGroupLoadingMode",function(){return Zg}),c.d(b,"SceneLoader",function(){return hh}),c.d(b,"SceneLoaderFlags",function(){return fh.a}),c.d(b,"BackgroundMaterial",function(){return di}),c.d(b,"ColorCurves",function(){return ik.a}),c.d(b,"EffectFallbacks",function(){return bi.a}),c.d(b,"Effect",function(){return $f.a}),c.d(b,"FresnelParameters",function(){return jk}),c.d(b,"ImageProcessingConfigurationDefines",function(){return od.b}),c.d(b,"ImageProcessingConfiguration",function(){return od.a}),c.d(b,"Material",function(){return qi.a}),c.d(b,"MaterialDefines",function(){return Zh.a}),c.d(b,"ThinMaterialHelper",function(){return kk.a}),c.d(b,"MaterialHelper",function(){return Yh.a}),c.d(b,"MultiMaterial",function(){return Jj.a}),c.d(b,"OcclusionMaterial",function(){return lk}),c.d(b,"PBRMaterialDefines",function(){return ti}),c.d(b,"PBRBaseMaterial",function(){return ui}),c.d(b,"PBRBaseSimpleMaterial",function(){return mk}),c.d(b,"PBRMaterial",function(){return vi}),c.d(b,"PBRMetallicRoughnessMaterial",function(){return nk}),c.d(b,"PBRSpecularGlossinessMaterial",function(){return ok}),c.d(b,"PushMaterial",function(){return $h.a}),c.d(b,"ShaderLanguage",function(){return Xd.a}),c.d(b,"ShaderMaterial",function(){return zh.a}),c.d(b,"StandardMaterialDefines",function(){return pd.b}),c.d(b,"StandardMaterial",function(){return pd.a}),c.d(b,"BaseTexture",function(){return Ie.a}),c.d(b,"ColorGradingTexture",function(){return pk}),c.d(b,"CubeTexture",function(){return Xh}),c.d(b,"DynamicTexture",function(){return qd.a}),c.d(b,"EquiRectangularCubeTexture",function(){return qk}),c.d(b,"ExternalTexture",function(){return Dg}),c.d(b,"HDRFiltering",function(){return Oj}),c.d(b,"HDRCubeTexture",function(){return Pj}),c.d(b,"HtmlElementTexture",function(){return rk}),c.d(b,"InternalTextureSource",function(){return ob.b}),c.d(b,"InternalTexture",function(){return ob.a}),c.d(b,"_DDSTextureLoader",function(){return Ci}),c.d(b,"_ENVTextureLoader",function(){return Di}),c.d(b,"_KTXTextureLoader",function(){return Ii}),c.d(b,"_TGATextureLoader",function(){return vk}),c.d(b,"_HDRTextureLoader",function(){return wk}),c.d(b,"_BasisTextureLoader",function(){return Ik}),c.d(b,"MirrorTexture",function(){return Wh}),c.d(b,"MultiRenderTarget",function(){return Jk}),c.d(b,"TexturePacker",function(){return Lk}),c.d(b,"TexturePackerFrame",function(){return Kk}),c.d(b,"CustomProceduralTexture",function(){return Ok}),c.d(b,"NoiseProceduralTexture",function(){return Pk}),c.d(b,"ProceduralTexture",function(){return Nk}),c.d(b,"ProceduralTextureSceneComponent",function(){return Mk}),c.d(b,"RawCubeTexture",function(){return Vk}),c.d(b,"RawTexture",function(){return sb}),c.d(b,"RawTexture2DArray",function(){return Rj}),c.d(b,"RawTexture3D",function(){return Wk}),c.d(b,"RefractionTexture",function(){return Xk}),c.d(b,"RenderTargetTexture",function(){return cd}),c.d(b,"TextureSampler",function(){return $d.a}),c.d(b,"Texture",function(){return V.a}),c.d(b,"ThinTexture",function(){return Yk.a}),c.d(b,"ThinRenderTargetTexture",function(){return Zk}),c.d(b,"VideoTexture",function(){return hj}),c.d(b,"UniformBuffer",function(){return $c.a}),c.d(b,"MaterialFlags",function(){return ai.a}),c.d(b,"NodeMaterialBlockTargets",function(){return Z}),c.d(b,"NodeMaterialBlockConnectionPointTypes",function(){return $}),c.d(b,"NodeMaterialBlockConnectionPointMode",function(){return Qk}),c.d(b,"NodeMaterialSystemValues",function(){return Rk}),c.d(b,"NodeMaterialModes",function(){return Sk}),c.d(b,"NodeMaterialConnectionPointCompatibilityStates",function(){return Tk}),c.d(b,"NodeMaterialConnectionPointDirection",function(){return Uk}),c.d(b,"NodeMaterialConnectionPoint",function(){return $k}),c.d(b,"NodeMaterialBlock",function(){return al}),c.d(b,"NodeMaterialDefines",function(){return Kl}),c.d(b,"NodeMaterial",function(){return Ll}),c.d(b,"VertexOutputBlock",function(){return fl}),c.d(b,"BonesBlock",function(){return Ml}),c.d(b,"InstancesBlock",function(){return Nl}),c.d(b,"MorphTargetsBlock",function(){return Ol}),c.d(b,"LightInformationBlock",function(){return Pl}),c.d(b,"FragmentOutputBlock",function(){return il}),c.d(b,"ImageProcessingBlock",function(){return Ql}),c.d(b,"PerturbNormalBlock",function(){return Rl}),c.d(b,"DiscardBlock",function(){return Sl}),c.d(b,"FrontFacingBlock",function(){return Tl}),c.d(b,"DerivativeBlock",function(){return Ul}),c.d(b,"FragCoordBlock",function(){return Vl}),c.d(b,"ScreenSizeBlock",function(){return Wl}),c.d(b,"FogBlock",function(){return Xl}),c.d(b,"LightBlock",function(){return Yl}),c.d(b,"TextureBlock",function(){return am}),c.d(b,"ReflectionTextureBlock",function(){return bm}),c.d(b,"CurrentScreenBlock",function(){return nl}),c.d(b,"SceneDepthBlock",function(){return cm}),c.d(b,"ImageSourceBlock",function(){return $l}),c.d(b,"InputBlock",function(){return ml}),c.d(b,"AnimatedInputBlockTypes",function(){return hl}),c.d(b,"MultiplyBlock",function(){return tl}),c.d(b,"AddBlock",function(){return dm}),c.d(b,"ScaleBlock",function(){return em}),c.d(b,"ClampBlock",function(){return fm}),c.d(b,"CrossBlock",function(){return gm}),c.d(b,"DotBlock",function(){return hm}),c.d(b,"TransformBlock",function(){return dl}),c.d(b,"RemapBlock",function(){return sl}),c.d(b,"NormalizeBlock",function(){return im}),c.d(b,"TrigonometryBlockOperations",function(){return Gl}),c.d(b,"TrigonometryBlock",function(){return Il}),c.d(b,"ColorMergerBlock",function(){return jm}),c.d(b,"VectorMergerBlock",function(){return rl}),c.d(b,"ColorSplitterBlock",function(){return Fl}),c.d(b,"VectorSplitterBlock",function(){return km}),c.d(b,"LerpBlock",function(){return lm}),c.d(b,"DivideBlock",function(){return mm}),c.d(b,"SubtractBlock",function(){return nm}),c.d(b,"StepBlock",function(){return om}),c.d(b,"OneMinusBlock",function(){return pm}),c.d(b,"ViewDirectionBlock",function(){return qm}),c.d(b,"FresnelBlock",function(){return rm}),c.d(b,"MaxBlock",function(){return sm}),c.d(b,"MinBlock",function(){return tm}),c.d(b,"DistanceBlock",function(){return um}),c.d(b,"LengthBlock",function(){return vm}),c.d(b,"NegateBlock",function(){return wm}),c.d(b,"PowBlock",function(){return xm}),c.d(b,"RandomNumberBlock",function(){return ym}),c.d(b,"ArcTan2Block",function(){return zm}),c.d(b,"SmoothStepBlock",function(){return Am}),c.d(b,"ReciprocalBlock",function(){return Bm}),c.d(b,"ReplaceColorBlock",function(){return Cm}),c.d(b,"PosterizeBlock",function(){return Em}),c.d(b,"WaveBlockKind",function(){return Dm}),c.d(b,"WaveBlock",function(){return Fm}),c.d(b,"GradientBlockColorStep",function(){return Gm}),c.d(b,"GradientBlock",function(){return Hm}),c.d(b,"NLerpBlock",function(){return Im}),c.d(b,"WorleyNoise3DBlock",function(){return Jm}),c.d(b,"SimplexPerlin3DBlock",function(){return Km}),c.d(b,"NormalBlendBlock",function(){return Lm}),c.d(b,"Rotate2dBlock",function(){return Mm}),c.d(b,"ReflectBlock",function(){return Nm}),c.d(b,"RefractBlock",function(){return Om}),c.d(b,"DesaturateBlock",function(){return Pm}),c.d(b,"PBRMetallicRoughnessBlock",function(){return Xm}),c.d(b,"SheenBlock",function(){return Qm}),c.d(b,"AnisotropyBlock",function(){return Rm}),c.d(b,"ReflectionBlock",function(){return Sm}),c.d(b,"ClearCoatBlock",function(){return Tm}),c.d(b,"RefractionBlock",function(){return Um}),c.d(b,"SubSurfaceBlock",function(){return Vm}),c.d(b,"ParticleTextureBlock",function(){return ol}),c.d(b,"ParticleRampGradientBlock",function(){return pl}),c.d(b,"ParticleBlendMultiplyBlock",function(){return ql}),c.d(b,"ModBlock",function(){return Ym}),c.d(b,"MatrixBuilderBlock",function(){return $m}),c.d(b,"ConditionalBlockConditions",function(){return Zm}),c.d(b,"ConditionalBlock",function(){return an}),c.d(b,"CloudBlock",function(){return bn}),c.d(b,"NodeMaterialOptimizer",function(){return cn}),c.d(b,"PropertyTypeForEdition",function(){return el}),c.d(b,"editableInPropertyPage",function(){return gl}),c.d(b,"EffectRenderer",function(){return Mj}),c.d(b,"EffectWrapper",function(){return Nj}),c.d(b,"ShadowDepthWrapper",function(){return en}),c.d(b,"DrawWrapper",function(){return Ec.a}),c.d(b,"Scalar",function(){return I.a}),c.d(b,"extractMinAndMaxIndexed",function(){return fn.b}),c.d(b,"extractMinAndMax",function(){return fn.a}),c.d(b,"Space",function(){return R.c}),c.d(b,"Axis",function(){return R.a}),c.d(b,"Coordinate",function(){return R.b}),c.d(b,"Color3",function(){return h.a}),c.d(b,"Color4",function(){return h.b}),c.d(b,"TmpColors",function(){return h.c}),c.d(b,"ToGammaSpace",function(){return hb.c}),c.d(b,"ToLinearSpace",function(){return hb.d}),c.d(b,"PHI",function(){return hb.b}),c.d(b,"Epsilon",function(){return hb.a}),c.d(b,"Frustum",function(){return ed.a}),c.d(b,"Orientation",function(){return oa.e}),c.d(b,"BezierCurve",function(){return oa.c}),c.d(b,"Angle",function(){return oa.a}),c.d(b,"Arc2",function(){return oa.b}),c.d(b,"Path2",function(){return oa.f}),c.d(b,"Path3D",function(){return oa.g}),c.d(b,"Curve3",function(){return oa.d}),c.d(b,"Plane",function(){return Hb.a}),c.d(b,"Size",function(){return aa.a}),c.d(b,"Vector2",function(){return g.d}),c.d(b,"Vector3",function(){return g.e}),c.d(b,"Vector4",function(){return g.f}),c.d(b,"Quaternion",function(){return g.b}),c.d(b,"Matrix",function(){return g.a}),c.d(b,"TmpVectors",function(){return g.c}),c.d(b,"PositionNormalVertex",function(){return Ae}),c.d(b,"PositionNormalTextureVertex",function(){return Be}),c.d(b,"Viewport",function(){return Oc.a}),c.d(b,"SphericalHarmonics",function(){return Ge}),c.d(b,"SphericalPolynomial",function(){return He}),c.d(b,"AbstractMesh",function(){return bb.a}),c.d(b,"DracoCompression",function(){return jn}),c.d(b,"MeshoptCompression",function(){return kn}),c.d(b,"CSG",function(){return rn}),c.d(b,"Geometry",function(){return Ij.a}),c.d(b,"GroundMesh",function(){return Cd}),c.d(b,"TrailMesh",function(){return sn}),c.d(b,"InstancedMesh",function(){return tn.a}),c.d(b,"LinesMesh",function(){return un.b}),c.d(b,"InstancedLinesMesh",function(){return un.a}),c.d(b,"_CreationDataStorage",function(){return S.b}),c.d(b,"_InstancesBatch",function(){return S.c}),c.d(b,"Mesh",function(){return S.a}),c.d(b,"_injectLTSMesh",function(){return vn.a}),c.d(b,"VertexData",function(){return yd.a}),c.d(b,"MeshBuilder",function(){return fo}),c.d(b,"SimplificationSettings",function(){return go}),c.d(b,"SimplificationQueue",function(){return ho}),c.d(b,"SimplificationType",function(){return co}),c.d(b,"QuadraticErrorSimplification",function(){return mo}),c.d(b,"SimplicationQueueSceneComponent",function(){return no}),c.d(b,"Polygon",function(){return In}),c.d(b,"PolygonMeshBuilder",function(){return Jn}),c.d(b,"_PrimaryIsoTriangle",function(){return Zn}),c.d(b,"PolyhedronData",function(){return $n}),c.d(b,"GeodesicData",function(){return ao}),c.d(b,"SubMesh",function(){return ln.a}),c.d(b,"MeshLODLevel",function(){return oo.a}),c.d(b,"TransformNode",function(){return eb.a}),c.d(b,"CreateBoxVertexData",function(){return rh.c}),c.d(b,"CreateBox",function(){return rh.b}),c.d(b,"BoxBuilder",function(){return rh.a}),c.d(b,"CreateTiledBoxVertexData",function(){return An}),c.d(b,"CreateTiledBox",function(){return Bn}),c.d(b,"TiledBoxBuilder",function(){return Cn}),c.d(b,"CreateDisc",function(){return Oh}),c.d(b,"DiscBuilder",function(){return Ph}),c.d(b,"CreateRibbonVertexData",function(){return wn.b}),c.d(b,"CreateRibbon",function(){return wn.a}),c.d(b,"RibbonBuilder",function(){return wn.c}),c.d(b,"CreateSphereVertexData",function(){return vh.b}),c.d(b,"CreateSphere",function(){return vh.a}),c.d(b,"SphereBuilder",function(){return vh.c}),c.d(b,"CreateHemisphere",function(){return Qh}),c.d(b,"HemisphereBuilder",function(){return Rh}),c.d(b,"CreateCylinderVertexData",function(){return xd.b}),c.d(b,"CreateCylinder",function(){return xd.a}),c.d(b,"CylinderBuilder",function(){return xd.c}),c.d(b,"CreateTorusVertexData",function(){return zd}),c.d(b,"CreateTorus",function(){return Ad}),c.d(b,"TorusBuilder",function(){return Bd}),c.d(b,"CreateTorusKnotVertexData",function(){return Dn}),c.d(b,"CreateTorusKnot",function(){return En}),c.d(b,"TorusKnotBuilder",function(){return Fn}),c.d(b,"CreateLineSystemVertexData",function(){return wh.d}),c.d(b,"CreateDashedLinesVertexData",function(){return wh.b}),c.d(b,"CreateLineSystem",function(){return wh.c}),c.d(b,"CreateLines",function(){return wh.e}),c.d(b,"CreateDashedLines",function(){return wh.a}),c.d(b,"LinesBuilder",function(){return wh.f}),c.d(b,"CreatePolygonVertexData",function(){return Kn}),c.d(b,"CreatePolygon",function(){return Ln}),c.d(b,"ExtrudePolygon",function(){return Mn}),c.d(b,"PolygonBuilder",function(){return Nn}),c.d(b,"ExtrudeShape",function(){return Zj.a}),c.d(b,"ExtrudeShapeCustom",function(){return Zj.b}),c.d(b,"ShapeBuilder",function(){return Zj.c}),c.d(b,"CreateLathe",function(){return On}),c.d(b,"LatheBuilder",function(){return Pn}),c.d(b,"CreatePlaneVertexData",function(){return Ah.b}),c.d(b,"CreatePlane",function(){return Ah.a}),c.d(b,"PlaneBuilder",function(){return Ah.c}),c.d(b,"CreateTiledPlaneVertexData",function(){return xn}),c.d(b,"CreateTiledPlane",function(){return yn}),c.d(b,"TiledPlaneBuilder",function(){return zn}),c.d(b,"CreateGroundVertexData",function(){return Dd}),c.d(b,"CreateTiledGroundVertexData",function(){return Ed}),c.d(b,"CreateGroundFromHeightMapVertexData",function(){return Fd}),c.d(b,"CreateGround",function(){return Gd}),c.d(b,"CreateTiledGround",function(){return Hd}),c.d(b,"CreateGroundFromHeightMap",function(){return Id}),c.d(b,"GroundBuilder",function(){return Jd}),c.d(b,"CreateTube",function(){return Qn}),c.d(b,"TubeBuilder",function(){return Rn}),c.d(b,"CreatePolyhedronVertexData",function(){return Fh}),c.d(b,"CreatePolyhedron",function(){return Gh}),c.d(b,"PolyhedronBuilder",function(){return Hh}),c.d(b,"CreateIcoSphereVertexData",function(){return Sn}),c.d(b,"CreateIcoSphere",function(){return Tn}),c.d(b,"IcoSphereBuilder",function(){return Un}),c.d(b,"CreateGeodesic",function(){return bo}),c.d(b,"CreateDecal",function(){return Vn}),c.d(b,"DecalBuilder",function(){return Wn}),c.d(b,"CreateCapsuleVertexData",function(){return Xn.c}),c.d(b,"CreateCapsule",function(){return Xn.b}),c.d(b,"CapsuleBuilder",function(){return Xn.a}),c.d(b,"WebGLDataBuffer",function(){return po.a}),c.d(b,"WebGPUDataBuffer",function(){return mg}),c.d(b,"MorphTarget",function(){return Qj}),c.d(b,"MorphTargetManager",function(){return Sj}),c.d(b,"RecastJSPlugin",function(){return qo}),c.d(b,"RecastJSCrowd",function(){return ro}),c.d(b,"Node",function(){return L.a}),c.d(b,"Database",function(){return so}),c.d(b,"BaseParticleSystem",function(){return El}),c.d(b,"BoxParticleEmitter",function(){return ul}),c.d(b,"ConeParticleEmitter",function(){return vl}),c.d(b,"CylinderParticleEmitter",function(){return wl}),c.d(b,"CylinderDirectedParticleEmitter",function(){return xl}),c.d(b,"HemisphericParticleEmitter",function(){return yl}),c.d(b,"PointParticleEmitter",function(){return zl}),c.d(b,"SphereParticleEmitter",function(){return Al}),c.d(b,"SphereDirectedParticleEmitter",function(){return Bl}),c.d(b,"CustomParticleEmitter",function(){return Cl}),c.d(b,"MeshParticleEmitter",function(){return Dl}),c.d(b,"WebGL2ParticleSystem",function(){return uo}),c.d(b,"ComputeShaderParticleSystem",function(){return vo}),c.d(b,"GPUParticleSystem",function(){return Eo}),c.d(b,"Particle",function(){return Bo}),c.d(b,"ParticleHelper",function(){return Go}),c.d(b,"ParticleSystem",function(){return Do}),c.d(b,"ParticleSystemSet",function(){return Fo}),c.d(b,"SolidParticle",function(){return Io}),c.d(b,"ModelShape",function(){return Jo}),c.d(b,"DepthSortedParticle",function(){return Ko}),c.d(b,"SolidParticleVertex",function(){return Lo}),c.d(b,"SolidParticleSystem",function(){return Mo}),c.d(b,"CloudPoint",function(){return No}),c.d(b,"PointsGroup",function(){return Oo}),c.d(b,"PointColor",function(){return Ho}),c.d(b,"PointsCloudSystem",function(){return Po}),c.d(b,"SubEmitterType",function(){return wo}),c.d(b,"SubEmitter",function(){return Co}),c.d(b,"PhysicsEngine",function(){return Vj}),c.d(b,"PhysicsEngineSceneComponent",function(){return So}),c.d(b,"PhysicsHelper",function(){return To}),c.d(b,"PhysicsRadialExplosionEventOptions",function(){return Yo}),c.d(b,"PhysicsUpdraftEventOptions",function(){return Zo}),c.d(b,"PhysicsVortexEventOptions",function(){return $o}),c.d(b,"PhysicsRadialImpulseFalloff",function(){return Qo}),c.d(b,"PhysicsUpdraftMode",function(){return Ro}),c.d(b,"PhysicsImpostor",function(){return Tj.a}),c.d(b,"PhysicsJoint",function(){return Uj.e}),c.d(b,"DistanceJoint",function(){return Uj.a}),c.d(b,"MotorEnabledJoint",function(){return Uj.d}),c.d(b,"HingeJoint",function(){return Uj.c}),c.d(b,"Hinge2Joint",function(){return Uj.b}),c.d(b,"CannonJSPlugin",function(){return Xj}),c.d(b,"AmmoJSPlugin",function(){return $j}),c.d(b,"OimoJSPlugin",function(){return Yj}),c.d(b,"AnaglyphPostProcess",function(){return Ic}),c.d(b,"BlackAndWhitePostProcess",function(){return ap}),c.d(b,"BloomEffect",function(){return ep}),c.d(b,"BloomMergePostProcess",function(){return dp}),c.d(b,"BlurPostProcess",function(){return Vh}),c.d(b,"ChromaticAberrationPostProcess",function(){return fp}),c.d(b,"CircleOfConfusionPostProcess",function(){return gp}),c.d(b,"ColorCorrectionPostProcess",function(){return hp}),c.d(b,"ConvolutionPostProcess",function(){return ip}),c.d(b,"DepthOfFieldBlurPostProcess",function(){return jp}),c.d(b,"DepthOfFieldEffectBlurLevel",function(){return kp}),c.d(b,"DepthOfFieldEffect",function(){return np}),c.d(b,"DepthOfFieldMergePostProcessOptions",function(){return lp}),c.d(b,"DepthOfFieldMergePostProcess",function(){return mp}),c.d(b,"DisplayPassPostProcess",function(){return op}),c.d(b,"ExtractHighlightsPostProcess",function(){return cp}),c.d(b,"FilterPostProcess",function(){return pp}),c.d(b,"FxaaPostProcess",function(){return qp}),c.d(b,"GrainPostProcess",function(){return rp}),c.d(b,"HighlightsPostProcess",function(){return sp}),c.d(b,"ImageProcessingPostProcess",function(){return td}),c.d(b,"MotionBlurPostProcess",function(){return wp}),c.d(b,"PassPostProcess",function(){return Gc}),c.d(b,"PassCubePostProcess",function(){return Hc}),c.d(b,"PostProcess",function(){return Fc}),c.d(b,"PostProcessManager",function(){return ad.a}),c.d(b,"RefractionPostProcess",function(){return xp}),c.d(b,"DefaultRenderingPipeline",function(){return Cp}),c.d(b,"LensRenderingPipeline",function(){return Dp}),c.d(b,"SSAO2RenderingPipeline",function(){return Fp}),c.d(b,"SSAORenderingPipeline",function(){return Gp}),c.d(b,"StandardRenderingPipeline",function(){return Jp}),c.d(b,"PostProcessRenderEffect",function(){return bp}),c.d(b,"PostProcessRenderPipeline",function(){return zp}),c.d(b,"PostProcessRenderPipelineManager",function(){return Ap}),c.d(b,"PostProcessRenderPipelineManagerSceneComponent",function(){return Bp}),c.d(b,"SharpenPostProcess",function(){return yp}),c.d(b,"StereoscopicInterlacePostProcessI",function(){return Pc}),c.d(b,"StereoscopicInterlacePostProcess",function(){return Qc}),c.d(b,"TonemappingOperator",function(){return Kp}),c.d(b,"TonemapPostProcess",function(){return Lp}),c.d(b,"VolumetricLightScatteringPostProcess",function(){return Mp}),c.d(b,"VRDistortionCorrectionPostProcess",function(){return Zc}),c.d(b,"VRMultiviewToSingleviewPostProcess",function(){return fd}),c.d(b,"ScreenSpaceReflectionPostProcess",function(){return Ip}),c.d(b,"ScreenSpaceCurvaturePostProcess",function(){return Np}),c.d(b,"ReflectionProbe",function(){return ak}),c.d(b,"BoundingBoxRenderer",function(){return Op}),c.d(b,"DepthRenderer",function(){return wj}),c.d(b,"DepthRendererSceneComponent",function(){return Pp}),c.d(b,"DepthPeelingRenderer",function(){return Rp}),c.d(b,"DepthPeelingSceneComponent",function(){return Sp}),c.d(b,"EdgesRenderer",function(){return Up}),c.d(b,"LineEdgesRenderer",function(){return Vp}),c.d(b,"GeometryBufferRenderer",function(){return tp}),c.d(b,"GeometryBufferRendererSceneComponent",function(){return vp}),c.d(b,"PrePassRenderer",function(){return Xp}),c.d(b,"PrePassRendererSceneComponent",function(){return Yp}),c.d(b,"SubSurfaceSceneComponent",function(){return aq}),c.d(b,"OutlineRenderer",function(){return bq}),c.d(b,"RenderingGroup",function(){return cq.a}),c.d(b,"RenderingGroupInfo",function(){return bd.a}),c.d(b,"RenderingManager",function(){return bd.b}),c.d(b,"UtilityLayerRenderer",function(){return th.a}),c.d(b,"Scene",function(){return P.a}),c.d(b,"_injectLTSScene",function(){return dq.a}),c.d(b,"SceneComponentConstants",function(){return Na.a}),c.d(b,"Stage",function(){return Na.b}),c.d(b,"Sprite",function(){return eq}),c.d(b,"SpriteManager",function(){return hq}),c.d(b,"SpriteMap",function(){return jq}),c.d(b,"SpritePackedManager",function(){return kq}),c.d(b,"SpriteSceneComponent",function(){return fq}),c.d(b,"AlphaState",function(){return lq.a}),c.d(b,"DepthCullingState",function(){return Bg.a}),c.d(b,"StencilState",function(){return mq.a}),c.d(b,"StencilStateComposer",function(){return zg.a}),c.d(b,"AndOrNotEvaluator",function(){return nq.a}),c.d(b,"AssetTaskState",function(){return iq}),c.d(b,"AbstractAssetTask",function(){return oq}),c.d(b,"AssetsProgressEvent",function(){return pq}),c.d(b,"ContainerAssetTask",function(){return qq}),c.d(b,"MeshAssetTask",function(){return rq}),c.d(b,"TextFileAssetTask",function(){return sq}),c.d(b,"BinaryFileAssetTask",function(){return tq}),c.d(b,"ImageAssetTask",function(){return uq}),c.d(b,"TextureAssetTask",function(){return vq}),c.d(b,"CubeTextureAssetTask",function(){return wq}),c.d(b,"HDRCubeTextureAssetTask",function(){return xq}),c.d(b,"EquiRectangularCubeTextureAssetTask",function(){return yq}),c.d(b,"AssetsManager",function(){return zq}),c.d(b,"BasisTranscodeConfiguration",function(){return yk}),c.d(b,"BasisToolsOptions",function(){return zk}),c.d(b,"GetInternalFormatFromBasisFormat",function(){return Ak}),c.d(b,"TranscodeAsync",function(){return Ek}),c.d(b,"LoadTextureFromTranscodeResult",function(){return Fk}),c.d(b,"BasisTools",function(){return Gk}),c.d(b,"DDSTools",function(){return Bi}),c.d(b,"expandToProperty",function(){return J.b}),c.d(b,"serialize",function(){return J.d}),c.d(b,"serializeAsTexture",function(){return J.n}),c.d(b,"serializeAsColor3",function(){return J.f}),c.d(b,"serializeAsFresnelParameters",function(){return J.i}),c.d(b,"serializeAsVector2",function(){return J.o}),c.d(b,"serializeAsVector3",function(){return J.p}),c.d(b,"serializeAsMeshReference",function(){return J.l}),c.d(b,"serializeAsColorCurves",function(){return J.h}),c.d(b,"serializeAsColor4",function(){return J.g}),c.d(b,"serializeAsImageProcessingConfiguration",function(){return J.j}),c.d(b,"serializeAsQuaternion",function(){return J.m}),c.d(b,"serializeAsMatrix",function(){return J.k}),c.d(b,"serializeAsCameraReference",function(){return J.e}),c.d(b,"SerializationHelper",function(){return J.a}),c.d(b,"nativeOverride",function(){return J.c}),c.d(b,"Deferred",function(){return Aq}),c.d(b,"GetEnvInfo",function(){return Ue}),c.d(b,"normalizeEnvInfo",function(){return Ve}),c.d(b,"CreateEnvTextureAsync",function(){return We}),c.d(b,"CreateImageDataArrayBufferViews",function(){return Ye}),c.d(b,"UploadEnvLevelsAsync",function(){return Ze}),c.d(b,"UploadLevelsAsync",function(){return af}),c.d(b,"UploadEnvSpherical",function(){return bf}),c.d(b,"_UpdateRGBDAsync",function(){return cf}),c.d(b,"EnvironmentTextureTools",function(){return df}),c.d(b,"MeshExploder",function(){return Bq}),c.d(b,"FilesInput",function(){return Dq}),c.d(b,"CubeMapToSphericalPolynomialTools",function(){return Se}),c.d(b,"HDRTools",function(){return Lj}),c.d(b,"PanoramaToCubeMapTools",function(){return Kj}),c.d(b,"KhronosTextureContainer",function(){return Ei}),c.d(b,"EventState",function(){return f.a}),c.d(b,"Observer",function(){return f.d}),c.d(b,"MultiObserver",function(){return f.b}),c.d(b,"Observable",function(){return f.c}),c.d(b,"PerformanceMonitor",function(){return Eq.a}),c.d(b,"RollingAverage",function(){return Eq.b}),c.d(b,"PromisePolyfill",function(){return Fq.a}),c.d(b,"SceneOptimization",function(){return Gq}),c.d(b,"TextureOptimization",function(){return Hq}),c.d(b,"HardwareScalingOptimization",function(){return Iq}),c.d(b,"ShadowsOptimization",function(){return Jq}),c.d(b,"PostProcessesOptimization",function(){return Kq}),c.d(b,"LensFlaresOptimization",function(){return Lq}),c.d(b,"CustomOptimization",function(){return Mq}),c.d(b,"ParticlesOptimization",function(){return Nq}),c.d(b,"RenderTargetsOptimization",function(){return Oq}),c.d(b,"MergeMeshesOptimization",function(){return Pq});c.d(b,"SceneOptimizerOptions",function(){return Qq}),c.d(b,"SceneOptimizer",function(){return Rq}),c.d(b,"SceneSerializer",function(){return Vq}),c.d(b,"SmartArray",function(){return Ac.a}),c.d(b,"SmartArrayNoDuplicate",function(){return Ac.b}),c.d(b,"StringDictionary",function(){return $b.a}),c.d(b,"Tags",function(){return Wq.a}),c.d(b,"CreateResizedCopy",function(){return Le}),c.d(b,"ApplyPostProcess",function(){return Me}),c.d(b,"ToHalfFloat",function(){return Ne}),c.d(b,"FromHalfFloat",function(){return Oe}),c.d(b,"TextureTools",function(){return Pe}),c.d(b,"GetTGAHeader",function(){return sk}),c.d(b,"UploadContent",function(){return tk}),c.d(b,"TGATools",function(){return uk}),c.d(b,"Tools",function(){return U.b}),c.d(b,"className",function(){return U.c}),c.d(b,"AsyncLoop",function(){return U.a}),c.d(b,"VideoRecorder",function(){return Xq}),c.d(b,"JoystickAxis",function(){return Zb}),c.d(b,"VirtualJoystick",function(){return ac}),c.d(b,"WorkerPool",function(){return Fi}),c.d(b,"Logger",function(){return q.a}),c.d(b,"RegisterClass",function(){return i.b}),c.d(b,"GetClass",function(){return i.a}),c.d(b,"FilesInputStore",function(){return Cq.a}),c.d(b,"DeepCopier",function(){return D.a}),c.d(b,"PivotTools",function(){return xh.a}),c.d(b,"PrecisionDate",function(){return Q.a}),c.d(b,"CreateScreenshot",function(){return Yq}),c.d(b,"CreateScreenshotAsync",function(){return Zq}),c.d(b,"CreateScreenshotWithResizeAsync",function(){return $q}),c.d(b,"CreateScreenshotUsingRenderTarget",function(){return ar}),c.d(b,"CreateScreenshotUsingRenderTargetAsync",function(){return br}),c.d(b,"ScreenshotTools",function(){return er}),c.d(b,"WebRequest",function(){return M.a}),c.d(b,"InspectableType",function(){return dr}),c.d(b,"GetEnvironmentBRDFTexture",function(){return ii}),c.d(b,"BRDFTextureTools",function(){return ji}),c.d(b,"RGBDTextureTools",function(){return Qe}),c.d(b,"ColorGradient",function(){return xo}),c.d(b,"Color3Gradient",function(){return yo}),c.d(b,"FactorGradient",function(){return zo}),c.d(b,"GradientHelper",function(){return Ao}),c.d(b,"PerfCounter",function(){return re.a}),c.d(b,"RetryStrategy",function(){return gr.a}),c.d(b,"LoadFileError",function(){return ue.h}),c.d(b,"RequestFileError",function(){return ue.m}),c.d(b,"ReadFileError",function(){return ue.k}),c.d(b,"FileToolsOptions",function(){return ue.d}),c.d(b,"SetCorsBehavior",function(){return ue.n}),c.d(b,"LoadImage",function(){return ue.i}),c.d(b,"ReadFile",function(){return ue.j}),c.d(b,"LoadFile",function(){return ue.g}),c.d(b,"RequestFile",function(){return ue.l}),c.d(b,"IsFileURL",function(){return ue.f}),c.d(b,"IsBase64DataUrl",function(){return ue.e}),c.d(b,"DecodeBase64UrlToBinary",function(){return ue.a}),c.d(b,"DecodeBase64UrlToString",function(){return ue.b}),c.d(b,"FileTools",function(){return ue.c}),c.d(b,"_injectLTSFileTools",function(){return hr.b}),c.d(b,"EndsWith",function(){return gh.e}),c.d(b,"StartsWith",function(){return gh.g}),c.d(b,"Decode",function(){return gh.a}),c.d(b,"EncodeArrayBufferToBase64",function(){return gh.d}),c.d(b,"DecodeBase64ToString",function(){return gh.c}),c.d(b,"DecodeBase64ToBinary",function(){return gh.b}),c.d(b,"PadNumber",function(){return gh.f}),c.d(b,"StringTools",function(){return gh.h}),c.d(b,"DataReader",function(){return ir}),c.d(b,"MinMaxReducer",function(){return xj}),c.d(b,"DepthReducer",function(){return yj}),c.d(b,"DataStorage",function(){return jr}),c.d(b,"SceneRecorder",function(){return kr}),c.d(b,"KhronosTextureContainer2",function(){return Gi}),c.d(b,"Trajectory",function(){return lr}),c.d(b,"TrajectoryClassifier",function(){return pr}),c.d(b,"TimerState",function(){return Yi}),c.d(b,"setAndStartTimer",function(){return bj}),c.d(b,"AdvancedTimer",function(){return cj}),c.d(b,"GenerateBase64StringFromPixelData",function(){return qr.b}),c.d(b,"GenerateBase64StringFromTexture",function(){return qr.c}),c.d(b,"GenerateBase64StringFromTextureAsync",function(){return qr.d}),c.d(b,"CopyTools",function(){return qr.a}),c.d(b,"Reflector",function(){return rr}),c.d(b,"IsWindowObjectExist",function(){return Ia.e}),c.d(b,"IsNavigatorAvailable",function(){return Ia.d}),c.d(b,"IsDocumentAvailable",function(){return Ia.c}),c.d(b,"GetDOMTextContent",function(){return Ia.b}),c.d(b,"DomManagement",function(){return Ia.a}),c.d(b,"ComputePressureObserverWrapper",function(){return sr.a}),c.d(b,"PerformanceViewerCollector",function(){return vr}),c.d(b,"PerfCollectionStrategy",function(){return xr}),c.d(b,"DynamicFloat32Array",function(){return tr}),c.d(b,"WebXRCamera",function(){return Ji}),c.d(b,"WebXREnterExitUIButton",function(){return Zi}),c.d(b,"WebXREnterExitUIOptions",function(){return $i}),c.d(b,"WebXREnterExitUI",function(){return aj}),c.d(b,"WebXRExperienceHelper",function(){return Ki}),c.d(b,"WebXRInput",function(){return Ui}),c.d(b,"WebXRInputSource",function(){return Ti}),c.d(b,"WebXRManagedOutputCanvasOptions",function(){return ud}),c.d(b,"WebXRManagedOutputCanvas",function(){return vd}),c.d(b,"WebXRState",function(){return rd}),c.d(b,"WebXRTrackingState",function(){return sd}),c.d(b,"WebXRSessionManager",function(){return wd}),c.d(b,"WebXRDefaultExperienceOptions",function(){return ej}),c.d(b,"WebXRDefaultExperience",function(){return fj}),c.d(b,"WebXRFeatureName",function(){return jb}),c.d(b,"WebXRFeaturesManager",function(){return kb}),c.d(b,"WebXRAbstractFeature",function(){return Vi}),c.d(b,"WebXRHitTestLegacy",function(){return yr}),c.d(b,"WebXRAnchorSystem",function(){return Ar}),c.d(b,"WebXRPlaneDetector",function(){return Cr}),c.d(b,"WebXRBackgroundRemover",function(){return Dr}),c.d(b,"WebXRMotionControllerTeleportation",function(){return dj}),c.d(b,"WebXRControllerPointerSelection",function(){return Wi}),c.d(b,"IWebXRControllerPhysicsOptions",function(){return Er}),c.d(b,"WebXRControllerPhysics",function(){return Fr}),c.d(b,"WebXRHitTest",function(){return Gr}),c.d(b,"WebXRFeaturePointSystem",function(){return Hr}),c.d(b,"WebXRHand",function(){return Kr}),c.d(b,"WebXRHandTracking",function(){return Lr}),c.d(b,"WebXRMeshDetector",function(){return Nr}),c.d(b,"WebXRImageTracking",function(){return Or}),c.d(b,"WebXRNearInteraction",function(){return Xi}),c.d(b,"WebXRDomOverlay",function(){return Pr}),c.d(b,"WebXRControllerMovement",function(){return Qr}),c.d(b,"WebXRLightEstimation",function(){return Sr}),c.d(b,"WebXREyeTracking",function(){return Tr}),c.d(b,"WebXRWalkingLocomotion",function(){return Yr}),c.d(b,"WebXRAbstractMotionController",function(){return Mi}),c.d(b,"WebXRControllerComponent",function(){return Li}),c.d(b,"WebXRGenericHandController",function(){return Zr}),c.d(b,"WebXRGenericTriggerMotionController",function(){return Ni}),c.d(b,"WebXRMicrosoftMixedRealityController",function(){return as}),c.d(b,"WebXRMotionControllerManager",function(){return Ri}),c.d(b,"WebXROculusTouchMotionController",function(){return cs}),c.d(b,"WebXRHTCViveMotionController",function(){return es}),c.d(b,"WebXRProfiledMotionController",function(){return Pi});var d=c(43),e=c(98),f=c(6),g=c(0),h=c(8),i=c(10),j=function(){function a(a,b){this.triggerOptions=a,this.onBeforeExecuteObservable=new f.c,a.parameter?(this.trigger=a.trigger,this._triggerParameter=a.parameter):a.trigger?this.trigger=a.trigger:this.trigger=a,this._nextActiveAction=this,this._condition=b}return a.prototype._prepare=function(){},a.prototype.getTriggerParameter=function(){return this._triggerParameter},a.prototype.setTriggerParameter=function(a){this._triggerParameter=a},a.prototype._evaluateConditionForCurrentFrame=function(){var a=this._condition;if(!a)return!0;var b=this._actionManager.getScene().getRenderId();return a._evaluationId!==b&&(a._evaluationId=b,a._currentResult=a.isValid()),a._currentResult},a.prototype._executeCurrent=function(a){this._evaluateConditionForCurrentFrame()&&(this.onBeforeExecuteObservable.notifyObservers(this),this._nextActiveAction.execute(a),this.skipToNextActiveAction())},a.prototype.execute=function(a){},a.prototype.skipToNextActiveAction=function(){this._nextActiveAction._child?(this._nextActiveAction._child._actionManager||(this._nextActiveAction._child._actionManager=this._actionManager),this._nextActiveAction=this._nextActiveAction._child):this._nextActiveAction=this},a.prototype.then=function(a){return this._child=a,a._actionManager=this._actionManager,a._prepare(),a},a.prototype._getProperty=function(a){return this._actionManager._getProperty(a)},a.prototype._getEffectiveTarget=function(a,b){return this._actionManager._getEffectiveTarget(a,b)},a.prototype.serialize=function(a){},a.prototype._serialize=function(a,b){a={type:1,children:[],name:a.name,properties:a.properties||[]};if(this._child&&this._child.serialize(a),this._condition){var c=this._condition.serialize();return c.children.push(a),b&&b.children.push(c),c}return b&&b.children.push(a),a},a._SerializeValueAsString=function(a){return"number"==typeof a?a.toString():"boolean"==typeof a?a?"true":"false":a instanceof g.d?a.x+", "+a.y:a instanceof g.e?a.x+", "+a.y+", "+a.z:a instanceof h.a?a.r+", "+a.g+", "+a.b:a instanceof h.b?a.r+", "+a.g+", "+a.b+", "+a.a:a},a._GetTargetProperty=function(a){return{name:"target",targetType:a._isMesh?"MeshProperties":a._isLight?"LightProperties":a._isCamera?"CameraProperties":"SceneProperties",value:a._isScene?"Scene":a.name}},a}();Object(i.b)("BABYLON.Action",j);var k=c(55),l=c(2),m=function(){function a(a){this._actionManager=a}return a.prototype.isValid=function(){return!0},a.prototype._getProperty=function(a){return this._actionManager._getProperty(a)},a.prototype._getEffectiveTarget=function(a,b){return this._actionManager._getEffectiveTarget(a,b)},a.prototype.serialize=function(){},a.prototype._serialize=function(a){return{type:2,children:[],name:a.name,properties:a.properties}},a}(),n=function(a){function b(c,d,e,f,g){void 0===g&&(g=b.IsEqual);c=a.call(this,c)||this;return c.propertyPath=e,c.value=f,c.operator=g,c._target=d,c._effectiveTarget=c._getEffectiveTarget(d,c.propertyPath),c._property=c._getProperty(c.propertyPath),c}return Object(l.d)(b,a),Object.defineProperty(b,"IsEqual",{get:function(){return b._IsEqual},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsDifferent",{get:function(){return b._IsDifferent},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsGreater",{get:function(){return b._IsGreater},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsLesser",{get:function(){return b._IsLesser},enumerable:!1,configurable:!0}),b.prototype.isValid=function(){switch(this.operator){case b.IsGreater:return this._effectiveTarget[this._property]>this.value;case b.IsLesser:return this._effectiveTarget[this._property]-1&&this._scene.actionManagers.splice(a,1)},b.prototype.getScene=function(){return this._scene},b.prototype.hasSpecificTriggers=function(a){for(var b=0;b-1)return!0}return!1},b.prototype.hasSpecificTriggers2=function(a,b){for(var c=0;c=b.OnPickTrigger&&c.trigger<=b.OnPointerOutTrigger)return!0}return!1},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasPickTriggers",{get:function(){for(var a=0;a=b.OnPickTrigger&&c.trigger<=b.OnPickUpTrigger)return!0}return!1},enumerable:!1,configurable:!0}),b.prototype.registerAction=function(a){return a.trigger===b.OnEveryFrameTrigger&&this.getScene().actionManager!==this?(q.a.Warn("OnEveryFrameTrigger can only be used with scene.actionManager"),null):(this.actions.push(a),b.Triggers[a.trigger]?b.Triggers[a.trigger]++:b.Triggers[a.trigger]=1,a._actionManager=this,a._prepare(),a)},b.prototype.unregisterAction=function(a){var c=this.actions.indexOf(a);return-1!==c&&(this.actions.splice(c,1),b.Triggers[a.trigger]-=1,0===b.Triggers[a.trigger]&&delete b.Triggers[a.trigger],a._actionManager=null,!0)},b.prototype.processTrigger=function(a,c){for(var d=0;d0;if(2===a.type?r.push(e):r.push(c),x){for(var x=new Array,z=0;z0){var t=k.properties[0].value;t=null===k.properties[0].targetType?t:d.getMeshByName(t);t._meshId&&(t.mesh=d.getMeshById(t._meshId)),t={trigger:b[k.name],parameter:t}}else t=b[k.name];for(var u=0;u=0?c:0;e=0;b=h._keys[0];f=h._keys.length-1;var i=h._keys[f],j={referenceValue:b.value,referencePosition:g.c.Vector3[0],referenceQuaternion:g.c.Quaternion[0],referenceScaling:g.c.Vector3[1],keyPosition:g.c.Vector3[2],keyQuaternion:g.c.Quaternion[1],keyScaling:g.c.Vector3[3]},k=!1,q=b.frame,r=i.frame;if(d){d=h.getRange(d);d&&(q=d.from,r=d.to)}d=b.frame===q;var u=i.frame===r;if(1===h._keys.length){var z=h._getKeyValue(h._keys[0]);j.referenceValue=z.clone?z.clone():z,k=!0}else c<=b.frame?(z=h._getKeyValue(b.value),(j.referenceValue=z.clone?z.clone():z,k=!0)):c>=i.frame&&(z=h._getKeyValue(i.value),(j.referenceValue=z.clone?z.clone():z,k=!0));for(i=0;!k||!d||!u&&i=A.frame&&c<=B.frame){if(c===A.frame)z=h._getKeyValue(A.value);else if(c===B.frame)z=h._getKeyValue(B.value);else{var C={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT};z=h._interpolate(c,C)}j.referenceValue=z.clone?z.clone():z,k=!0}if(!d&&q>=A.frame&&q<=B.frame){if(q===A.frame)e=i;else if(q===B.frame)e=i+1;else{C={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT};var D={frame:q,value:(z=h._interpolate(q,C)).clone?z.clone():z};h._keys.splice(i+1,0,D),e=i+1}d=!0}!u&&r>=A.frame&&r<=B.frame&&(r===A.frame?f=i:r===B.frame?f=i+1:((C={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT},D={frame:r,value:(z=h._interpolate(r,C)).clone?z.clone():z}),(h._keys.splice(i+1,0,D),f=i+1)),u=!0);i++}h.dataType===a.ANIMATIONTYPE_QUATERNION?j.referenceValue.normalize().conjugateInPlace():h.dataType===a.ANIMATIONTYPE_MATRIX&&(j.referenceValue.decompose(j.referenceScaling,j.referenceQuaternion,j.referencePosition),j.referenceQuaternion.normalize().conjugateInPlace());for(i=e;i<=f;i++){D=h._keys[i];if(!i||h.dataType===a.ANIMATIONTYPE_FLOAT||D.value!==b.value)switch(h.dataType){case a.ANIMATIONTYPE_MATRIX:D.value.decompose(j.keyScaling,j.keyQuaternion,j.keyPosition),j.keyPosition.subtractInPlace(j.referencePosition),j.keyScaling.divideInPlace(j.referenceScaling),j.referenceQuaternion.multiplyToRef(j.keyQuaternion,j.keyQuaternion),g.a.ComposeToRef(j.keyScaling,j.keyQuaternion,j.keyPosition,D.value);break;case a.ANIMATIONTYPE_QUATERNION:j.referenceValue.multiplyToRef(D.value,D.value);break;case a.ANIMATIONTYPE_VECTOR2:case a.ANIMATIONTYPE_VECTOR3:case a.ANIMATIONTYPE_COLOR3:case a.ANIMATIONTYPE_COLOR4:D.value.subtractToRef(j.referenceValue,D.value);break;case a.ANIMATIONTYPE_SIZE:D.value.width-=j.referenceValue.width,D.value.height-=j.referenceValue.height;break;default:D.value-=j.referenceValue}}return h},a.TransitionTo=function(a,b,c,d,e,f,g,h){if(void 0===h&&(h=null),g<=0)return c[a]=b,h&&h(),null;e=e*(g/1e3);f.setKeys([{frame:0,value:c[a].clone?c[a].clone():c[a]},{frame:e,value:b}]),c.animations||(c.animations=[]),c.animations.push(f);g=d.beginAnimation(c,0,e,!1);return g.onAnimationEnd=h,g},Object.defineProperty(a.prototype,"runtimeAnimations",{get:function(){return this._runtimeAnimations},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasRunningRuntimeAnimations",{get:function(){for(var a=0,b=this._runtimeAnimations;a=0;d--)this._keys[d].frame>=b&&this._keys[d].frame<=c&&this._keys.splice(d,1);this._ranges[a]=null}},a.prototype.getRange=function(a){return this._ranges[a]},a.prototype.getKeys=function(){return this._keys},a.prototype.getHighestFrame=function(){for(var a=0,b=0,c=this._keys.length;b0)return c.highLimitValue.clone?c.highLimitValue.clone():c.highLimitValue;var d=this._keys;if(1===d.length)return this._getKeyValue(d[0].value);var e=c.key;if(d[e].frame>=b)for(;e-1>=0&&d[e].frame>=b;)e--;for(e=e;e=b){c.key=e;var g=d[e],h=this._getKeyValue(g.value);if(g.interpolation===H.STEP)return h;var i=this._getKeyValue(f.value),j=void 0!==g.outTangent&&void 0!==f.inTangent,k=f.frame-g.frame,o=(b-g.frame)/k,p=this.getEasingFunction();switch(null!=p&&(o=p.ease(o)),this.dataType){case a.ANIMATIONTYPE_FLOAT:p=j?this.floatInterpolateFunctionWithTangents(h,g.outTangent*k,i,f.inTangent*k,o):this.floatInterpolateFunction(h,i,o);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return p;case a.ANIMATIONLOOPMODE_RELATIVE:return c.offsetValue*c.repeatCount+p}break;case a.ANIMATIONTYPE_QUATERNION:p=j?this.quaternionInterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),o):this.quaternionInterpolateFunction(h,i,o);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return p;case a.ANIMATIONLOOPMODE_RELATIVE:return p.addInPlace(c.offsetValue.scale(c.repeatCount))}return p;case a.ANIMATIONTYPE_VECTOR3:p=j?this.vector3InterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),o):this.vector3InterpolateFunction(h,i,o);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return p;case a.ANIMATIONLOOPMODE_RELATIVE:return p.add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_VECTOR2:p=j?this.vector2InterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),o):this.vector2InterpolateFunction(h,i,o);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return p;case a.ANIMATIONLOOPMODE_RELATIVE:return p.add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_SIZE:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.sizeInterpolateFunction(h,i,o);case a.ANIMATIONLOOPMODE_RELATIVE:return this.sizeInterpolateFunction(h,i,o).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_COLOR3:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.color3InterpolateFunction(h,i,o);case a.ANIMATIONLOOPMODE_RELATIVE:return this.color3InterpolateFunction(h,i,o).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_COLOR4:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.color4InterpolateFunction(h,i,o);case a.ANIMATIONLOOPMODE_RELATIVE:return this.color4InterpolateFunction(h,i,o).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_MATRIX:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:if(a.AllowMatricesInterpolation)return this.matrixInterpolateFunction(h,i,o,c.workValue);case a.ANIMATIONLOOPMODE_RELATIVE:return h}}break}}return this._getKeyValue(d[d.length-1].value)},a.prototype.matrixInterpolateFunction=function(b,c,d,e){return a.AllowMatrixDecomposeForInterpolation?e?(g.a.DecomposeLerpToRef(b,c,d,e),e):g.a.DecomposeLerp(b,c,d):e?(g.a.LerpToRef(b,c,d,e),e):g.a.Lerp(b,c,d)},a.prototype.clone=function(){var b=new a(this.name,this.targetPropertyPath.join("."),this.framePerSecond,this.dataType,this.loopMode);if(b.enableBlending=this.enableBlending,b.blendingSpeed=this.blendingSpeed,this._keys&&b.setKeys(this._keys),this._ranges)for(var c in b._ranges={},this._ranges){var d=this._ranges[c];d&&(b._ranges[c]=d.clone())}return b},a.prototype.setKeys=function(a){this._keys=a.slice(0)},a.prototype.serialize=function(){var b={};b.name=this.name,b.property=this.targetProperty,b.framePerSecond=this.framePerSecond,b.dataType=this.dataType,b.loopBehavior=this.loopMode,b.enableBlending=this.enableBlending,b.blendingSpeed=this.blendingSpeed;var c=this.dataType;b.keys=[];for(var d=this.getKeys(),e=0;e=1&&(j=n.values[1]),n.values.length>=2&&(k=n.values[2]);break;case a.ANIMATIONTYPE_QUATERNION:if(c=g.b.FromArray(n.values),n.values.length>=8){var o=g.b.FromArray(n.values.slice(4,8));o.equals(g.b.Zero())||(j=o)}if(n.values.length>=12){o=g.b.FromArray(n.values.slice(8,12));o.equals(g.b.Zero())||(k=o)}break;case a.ANIMATIONTYPE_MATRIX:c=g.a.FromArray(n.values);break;case a.ANIMATIONTYPE_COLOR3:c=h.a.FromArray(n.values);break;case a.ANIMATIONTYPE_COLOR4:c=h.b.FromArray(n.values);break;case a.ANIMATIONTYPE_VECTOR3:default:c=g.e.FromArray(n.values)}o={};o.frame=n.frame,o.value=c,null!=j&&(o.inTangent=j),null!=k&&(o.outTangent=k),i.push(o)}if(e.setKeys(i),b.ranges)for(d=0;d0&&h.forEach(function(a){e._events.push(a._clone())}),this._enableBlending=a&&a.animationPropertiesOverride?a.animationPropertiesOverride.enableBlending:this._animation.enableBlending}return Object.defineProperty(a.prototype,"currentFrame",{get:function(){return this._currentFrame},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"weight",{get:function(){return this._weight},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentValue",{get:function(){return this._currentValue},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"targetPath",{get:function(){return this._targetPath},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"target",{get:function(){return this._currentActiveTarget},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isAdditive",{get:function(){return this._host&&this._host.isAdditive},enumerable:!1,configurable:!0}),a.prototype._preparePath=function(a,b){void 0===b&&(b=0);var c=this._animation.targetPropertyPath;if(c.length>1){for(var d=a[c[0]],e=1;e-1&&this._animation.runtimeAnimations.splice(a,1)},a.prototype.setValue=function(a,b){if(this._targetIsArray)for(var c=0;cb[b.length-1].frame&&(a=b[b.length-1].frame);b=this._events;if(b.length)for(var c=0;cthis._maxFrame)&&(b=this._minFrame),(cthis._maxFrame)&&(c=this._maxFrame);var i,j=c-b,k=a*(g.framePerSecond*e)/1e3+this._ratioOffset,o=0;if(this._previousDelay=a,this._previousRatio=k,!d&&c>=b&&k>=j)h=!1,o=g._getKeyValue(this._maxValue);else if(!d&&b>=c&&k<=j)h=!1,o=g._getKeyValue(this._minValue);else if(this._animationState.loopMode!==O.ANIMATIONLOOPMODE_CYCLE){a=c.toString()+b.toString();if(!this._offsetsCache[a]){this._animationState.repeatCount=0,this._animationState.loopMode=O.ANIMATIONLOOPMODE_CYCLE;d=g._interpolate(b,this._animationState);var p=g._interpolate(c,this._animationState);switch(this._animationState.loopMode=this._getCorrectLoopMode(),g.dataType){case O.ANIMATIONTYPE_FLOAT:this._offsetsCache[a]=p-d;break;case O.ANIMATIONTYPE_QUATERNION:case O.ANIMATIONTYPE_VECTOR3:case O.ANIMATIONTYPE_VECTOR2:case O.ANIMATIONTYPE_SIZE:case O.ANIMATIONTYPE_COLOR3:this._offsetsCache[a]=p.subtract(d)}this._highLimitsCache[a]=p}o=this._highLimitsCache[a],i=this._offsetsCache[a]}if(void 0===i)switch(g.dataType){case O.ANIMATIONTYPE_FLOAT:i=0;break;case O.ANIMATIONTYPE_QUATERNION:i=ca;break;case O.ANIMATIONTYPE_VECTOR3:i=da;break;case O.ANIMATIONTYPE_VECTOR2:i=ea;break;case O.ANIMATIONTYPE_SIZE:i=fa;break;case O.ANIMATIONTYPE_COLOR3:i=ga}if(this._host&&this._host.syncRoot){d=this._host.syncRoot;p=b+(c-b)*((d.masterFrame-d.fromFrame)/(d.toFrame-d.fromFrame))}else p=k>0&&b>c||k<0&&b0&&this.currentFrame>p||e<0&&this.currentFrame>0,this._animationState.highLimitValue=o,this._animationState.offsetValue=i;c=g._interpolate(p,this._animationState);if(this.setValue(c,f),a.length)for(d=0;d0&&p>=a[d].frame&&a[d].frame>=b||j<0&&p<=a[d].frame&&a[d].frame<=b){e=a[d];e.isDone||(e.onlyOnce&&(a.splice(d,1),d--),e.isDone=!0,e.action(p))}return h||(this._stopped=!0),h},a}(),P=c(21),Q=c(34),ia=c(51),R=c(25),ja=function(a){function b(b,c,d,e,f,h,i){void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=null);var j=a.call(this,b,c.getScene())||this;return j.name=b,j.children=new Array,j.animations=new Array,j._index=null,j._absoluteTransform=new g.a,j._invertedAbsoluteTransform=new g.a,j._scalingDeterminant=1,j._worldTransform=new g.a,j._needToDecompose=!0,j._needToCompose=!1,j._linkedTransformNode=null,j._waitingTransformNodeId=null,j._skeleton=c,j._localMatrix=e?e.clone():g.a.Identity(),j._restPose=f||j._localMatrix.clone(),j._baseMatrix=h||j._localMatrix.clone(),j._index=i,c.bones.push(j),j.setParent(d,!1),(h||e)&&j._updateDifferenceMatrix(),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"_matrix",{get:function(){return this._compose(),this._localMatrix},set:function(a){this._needToCompose=!1,a.updateFlag!==this._localMatrix.updateFlag&&(this._localMatrix.copyFrom(a),this._markAsDirtyAndDecompose())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"Bone"},b.prototype.getSkeleton=function(){return this._skeleton},b.prototype.getParent=function(){return this._parent},b.prototype.getChildren=function(){return this.children},b.prototype.getIndex=function(){return null===this._index?this.getSkeleton().bones.indexOf(this):this._index},b.prototype.setParent=function(a,b){if(void 0===b&&(b=!0),this._parent!==a){if(this._parent){var c=this._parent.children.indexOf(this);-1!==c&&this._parent.children.splice(c,1)}this._parent=a,this._parent&&this._parent.children.push(this),b&&this._updateDifferenceMatrix(),this.markAsDirty()}},b.prototype.getLocalMatrix=function(){return this._compose(),this._localMatrix},b.prototype.getBaseMatrix=function(){return this._baseMatrix},b.prototype.getRestPose=function(){return this._restPose},b.prototype.setRestPose=function(a){this._restPose.copyFrom(a)},b.prototype.getBindPose=function(){return this._baseMatrix},b.prototype.setBindPose=function(a){this.updateMatrix(a)},b.prototype.getWorldMatrix=function(){return this._worldTransform},b.prototype.returnToRest=function(){if(this._linkedTransformNode){var a=g.c.Vector3[0],b=g.c.Quaternion[0],c=g.c.Vector3[1];this.getRestPose().decompose(a,b,c),this._linkedTransformNode.position.copyFrom(c),this._linkedTransformNode.rotationQuaternion=null!==(c=this._linkedTransformNode.rotationQuaternion)&&void 0!==c?c:g.b.Identity(),this._linkedTransformNode.rotationQuaternion.copyFrom(b),this._linkedTransformNode.scaling.copyFrom(a)}else this._matrix=this._restPose},b.prototype.getInvertedAbsoluteTransform=function(){return this._invertedAbsoluteTransform},b.prototype.getAbsoluteTransform=function(){return this._absoluteTransform},b.prototype.linkTransformNode=function(a){this._linkedTransformNode&&this._skeleton._numBonesWithLinkedTransformNode--,this._linkedTransformNode=a,this._linkedTransformNode&&this._skeleton._numBonesWithLinkedTransformNode++},b.prototype.getTransformNode=function(){return this._linkedTransformNode},Object.defineProperty(b.prototype,"position",{get:function(){return this._decompose(),this._localPosition},set:function(a){this._decompose(),this._localPosition.copyFrom(a),this._markAsDirtyAndCompose()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotation",{get:function(){return this.getRotation()},set:function(a){this.setRotation(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotationQuaternion",{get:function(){return this._decompose(),this._localRotation},set:function(a){this.setRotationQuaternion(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaling",{get:function(){return this.getScale()},set:function(a){this.setScale(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"animationPropertiesOverride",{get:function(){return this._skeleton.animationPropertiesOverride},enumerable:!1,configurable:!0}),b.prototype._decompose=function(){this._needToDecompose&&(this._needToDecompose=!1,this._localScaling||(this._localScaling=g.e.Zero(),this._localRotation=g.b.Zero(),this._localPosition=g.e.Zero()),this._localMatrix.decompose(this._localScaling,this._localRotation,this._localPosition))},b.prototype._compose=function(){this._needToCompose&&(this._localScaling?(this._needToCompose=!1,g.a.ComposeToRef(this._localScaling,this._localRotation,this._localPosition,this._localMatrix)):this._needToCompose=!1)},b.prototype.updateMatrix=function(a,b,c){void 0===b&&(b=!0),void 0===c&&(c=!0),this._baseMatrix.copyFrom(a),b&&this._updateDifferenceMatrix(),c?this._matrix=a:this.markAsDirty()},b.prototype._updateDifferenceMatrix=function(a,b){if(void 0===b&&(b=!0),a||(a=this._baseMatrix),this._parent?a.multiplyToRef(this._parent._absoluteTransform,this._absoluteTransform):this._absoluteTransform.copyFrom(a),this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform),b)for(a=0;a-1&&(this._scene._activeAnimatables.splice(a,1),this._scene._activeAnimatables.push(this))}return this},a.prototype.getAnimations=function(){return this._runtimeAnimations},a.prototype.appendAnimations=function(a,b){for(var c=this,d=0;d-1){for(var d=(f=this._runtimeAnimations).length-1;d>=0;d--){var e=f[d];a&&e.animation.name!=a||b&&!b(e.target)||(e.dispose(),f.splice(d,1))}0==f.length&&(this._scene._activeAnimatables.splice(c,1),this._raiseOnAnimationEnd())}}else if((d=this._scene._activeAnimatables.indexOf(this))>-1){this._scene._activeAnimatables.splice(d,1);var f=this._runtimeAnimations;for(d=0;d0)return;this._animationTimeLast=a}this.deltaTime=this.useConstantAnimationDeltaTime?16:(a-this._animationTimeLast)*this.animationTimeScale,this._animationTimeLast=a;a=this._activeAnimatables;if(0!==a.length){this._animationTime+=this.deltaTime;for(var b=this._animationTime,c=0;cc&&e>0&&(e*=-1),h&&this.stopAnimation(a,void 0,i),g||(g=new ka(this,a,b,c,d,e,f,void 0,j,k));k=!i||i(a);if(a.animations&&k&&g.appendAnimations(a,a.animations),a.getAnimatables)for(k=a.getAnimatables(),a=0;ad&&f>0)f*=-1;else if(d>c&&f<0){var j=d;d=c,c=j}return new ka(this,a,c,d,e,f,g,b,h,i)},P.a.prototype.beginDirectHierarchyAnimation=function(a,b,c,d,e,f,g,h,i,j){void 0===j&&(j=!1);b=a.getDescendants(b);var k=[];k.push(this.beginDirectAnimation(a,c,d,e,f,g,h,i,j));for(a=0,b=b;a0)e.copyFrom(d);else if(1===a.animations.length){if(g.b.SlerpToRef(d,c.currentValue,Math.min(1,a.totalWeight),e),0===a.totalAdditiveWeight)return e}else if(a.animations.length>1){c=1;var f=void 0,h=void 0;if(a.totalWeight<1){var i=1-a.totalWeight;h=[],(f=[]).push(d),h.push(i)}else{if(2===a.animations.length&&(g.b.SlerpToRef(a.animations[0].currentValue,a.animations[1].currentValue,a.animations[1].weight/a.totalWeight,b),0===a.totalAdditiveWeight))return b;f=[],h=[],c=a.totalWeight}for(d=0;d=j&&g.frame<=f&&(d?(i=g.value.clone(),n?(h=i.getTranslation(),i.setTranslation(h.scaleInPlace(a))):p&&e?(h=i.getTranslation(),i.setTranslation(h.multiplyInPlace(e))):i=g.value):i=g.value,q.push({frame:g.frame+c,value:i}));return this.animations[0].createRange(b,j+c,f+c),!0};var la=function(){function a(){}return a.prototype.getClassName=function(){return"TargetedAnimation"},a.prototype.serialize=function(){var a={};return a.animation=this.animation.serialize(),a.targetId=this.target.id,a},a}(),ma=function(){function a(a,b){void 0===b&&(b=null),this.name=a,this._targetedAnimations=new Array,this._animatables=new Array,this._from=Number.MAX_VALUE,this._to=-Number.MAX_VALUE,this._speedRatio=1,this._loopAnimation=!1,this._isAdditive=!1,this._parentContainer=null,this.onAnimationEndObservable=new f.c,this.onAnimationLoopObservable=new f.c,this.onAnimationGroupLoopObservable=new f.c,this.onAnimationGroupEndObservable=new f.c,this.onAnimationGroupPauseObservable=new f.c,this.onAnimationGroupPlayObservable=new f.c,this._scene=b||C.a.LastCreatedScene,this.uniqueId=this._scene.getUniqueId(),this._scene.addAnimationGroup(this)}return Object.defineProperty(a.prototype,"from",{get:function(){return this._from},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"to",{get:function(){return this._to},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isStarted",{get:function(){return this._isStarted},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isPlaying",{get:function(){return this._isStarted&&!this._isPaused},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"speedRatio",{get:function(){return this._speedRatio},set:function(a){if(this._speedRatio!==a){this._speedRatio=a;for(a=0;ab[0].frame&&(this._from=b[0].frame),this._toa){e={frame:a,value:e.value,inTangent:e.inTangent,outTangent:e.outTangent,interpolation:e.interpolation};d.splice(0,0,e)}f.frame-1&&this._scene.animationGroups.splice(a,1),this._parentContainer){a=this._parentContainer.animationGroups.indexOf(this);a>-1&&this._parentContainer.animationGroups.splice(a,1),this._parentContainer=null}this.onAnimationEndObservable.clear(),this.onAnimationGroupEndObservable.clear(),this.onAnimationGroupPauseObservable.clear(),this.onAnimationGroupPlayObservable.clear(),this.onAnimationLoopObservable.clear(),this.onAnimationGroupLoopObservable.clear()},a.prototype._checkAnimationGroupEnded=function(a){a=this._animatables.indexOf(a);a>-1&&this._animatables.splice(a,1),0===this._animatables.length&&(this._isStarted=!1,this.onAnimationGroupEndObservable.notifyObservers(this))},a.prototype.clone=function(b,c,d){void 0===d&&(d=!1);for(var b=new a(b||this.name,this._scene),e=0,f=this._targetedAnimations;e=.5?.5*(1-this.easeInCore(2*(1-b)))+.5:.5*this.easeInCore(2*b)},a.EASINGMODE_EASEIN=0,a.EASINGMODE_EASEOUT=1,a.EASINGMODE_EASEINOUT=2,a}(),qa=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a=Math.max(0,Math.min(1,a)),1-Math.sqrt(1-a*a)},b}(pa),ra=function(a){function b(b){void 0===b&&(b=1);var c=a.call(this)||this;return c.amplitude=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.amplitude);return Math.pow(a,3)-a*b*Math.sin(3.141592653589793*a)},b}(pa),sa=function(a){function b(b,c){void 0===b&&(b=3),void 0===c&&(c=2);var d=a.call(this)||this;return d.bounces=b,d.bounciness=c,d}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.bounces),c=this.bounciness;c<=1&&(c=1.001);var d=Math.pow(c,b),e=1-c;d=(1-d)/e+.5*d;var f=a*d;f=Math.log(-f*(1-c)+1)/Math.log(c);f=Math.floor(f);var g=f+1,h=(1-Math.pow(c,f))/(e*d);g=.5*(h+(1-Math.pow(c,g))/(e*d));e=a-g;d=g-h;return-Math.pow(1/c,b-f)/(d*d)*(e-d)*(e+d)},b}(pa),ta=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a},b}(pa),ua=function(a){function b(b,c){void 0===b&&(b=3),void 0===c&&(c=3);var d=a.call(this)||this;return d.oscillations=b,d.springiness=c,d}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.oscillations),c=Math.max(0,this.springiness);return(0==c?a:(Math.exp(c*a)-1)/(Math.exp(c)-1))*Math.sin((6.283185307179586*b+1.5707963267948966)*a)},b}(pa),va=function(a){function b(b){void 0===b&&(b=2);var c=a.call(this)||this;return c.exponent=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return this.exponent<=0?a:(Math.exp(this.exponent*a)-1)/(Math.exp(this.exponent)-1)},b}(pa),wa=function(a){function b(b){void 0===b&&(b=2);var c=a.call(this)||this;return c.power=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.power);return Math.pow(a,b)},b}(pa),xa=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a},b}(pa),ya=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a*a},b}(pa),za=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a*a*a},b}(pa),Aa=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return 1-Math.sin(1.5707963267948966*(1-a))},b}(pa),Ba=function(a){function b(b,c,d,e){void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),void 0===e&&(e=1);var f=a.call(this)||this;return f.x1=b,f.y1=c,f.x2=d,f.y2=e,f}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return oa.c.Interpolate(a,this.x1,this.y1,this.x2,this.y2)},b}(pa),Ca=function(){function a(a,b,c){this.frame=a,this.action=b,this.onlyOnce=c,this.isDone=!1}return a.prototype._clone=function(){return new a(this.frame,this.action,this.onlyOnce)},a}(),Da=function(){function a(a){this.path=a,this._onchange=new Array,this.value=0,this.animations=new Array}return a.prototype.getPoint=function(){var a=this.path.getPointAtLengthPosition(this.value);return new g.e(a.x,0,a.y)},a.prototype.moveAhead=function(a){return void 0===a&&(a=.002),this.move(a),this},a.prototype.moveBack=function(a){return void 0===a&&(a=.002),this.move(-a),this},a.prototype.move=function(a){if(Math.abs(a)>1)throw"step size should be less than 1.";return this.value+=a,this.ensureLimits(),this.raiseOnChange(),this},a.prototype.ensureLimits=function(){for(;this.value>1;)this.value-=1;for(;this.value<0;)this.value+=1;return this},a.prototype.raiseOnChange=function(){var a=this;return this._onchange.forEach(function(b){return b(a)}),this},a.prototype.onchange=function(a){return this._onchange.push(a),this},a}(),S=c(9),Ea=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b}(d.a),Fa=function(){this.rootNodes=[],this.skeletons=[],this.animationGroups=[]},Ga=function(a){function b(b){var c=a.call(this)||this;return c._wasAddedToScene=!1,c.scene=b,c.sounds=[],c.effectLayers=[],c.layers=[],c.lensFlareSystems=[],c.proceduralTextures=[],c.reflectionProbes=[],b.onDisposeObservable.add(function(){c._wasAddedToScene||c.dispose()}),c._onContextRestoredObserver=b.getEngine().onContextRestoredObservable.add(function(){for(var a=0,b=c.geometries;a-1&&b.animations.splice(e,1)}},e=0,f=a.animations;e0&&(d=!0,this._soundLoaded(c));break;case"String":b.push(c);case"Array":0===b.length&&(b=c);for(c=0;c0&&(this._htmlAudioElement.currentTime=0)):this._streamingSource.disconnect(),this.isPlaying=!1;else if((null===(b=T.a.audioEngine)||void 0===b?void 0:b.audioContext)&&this._soundSource){b=a?T.a.audioEngine.audioContext.currentTime+a:void 0;this._soundSource.stop(b),void 0===b?(this.isPlaying=!1,this._soundSource.onended=function(){}):this._soundSource.onended=function(){c.isPlaying=!1},this.isPaused||(this._startOffset=0)}},a.prototype.pause=function(){var a;this.isPlaying&&(this.isPaused=!0,this._streaming?(this._htmlAudioElement?this._htmlAudioElement.pause():this._streamingSource.disconnect(),this.isPlaying=!1):(null===(a=T.a.audioEngine)||void 0===a?void 0:a.audioContext)&&(this.stop(0),this._startOffset+=T.a.audioEngine.audioContext.currentTime-this._startTime))},a.prototype.setVolume=function(a,b){var c;(null===(c=T.a.audioEngine)||void 0===c?void 0:c.canUseWebAudio)&&this._soundGain&&(b&&T.a.audioEngine.audioContext?(this._soundGain.gain.cancelScheduledValues(T.a.audioEngine.audioContext.currentTime),this._soundGain.gain.setValueAtTime(this._soundGain.gain.value,T.a.audioEngine.audioContext.currentTime),this._soundGain.gain.linearRampToValueAtTime(a,T.a.audioEngine.audioContext.currentTime+b)):this._soundGain.gain.value=a),this._volume=a},a.prototype.setPlaybackRate=function(a){this._playbackRate=a,this.isPlaying&&(this._streaming&&this._htmlAudioElement?this._htmlAudioElement.playbackRate=this._playbackRate:this._soundSource&&(this._soundSource.playbackRate.value=this._playbackRate))},a.prototype.getVolume=function(){return this._volume},a.prototype.attachToMesh=function(a){var b=this;this._connectedTransformNode&&this._registerFunc&&(this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc),this._registerFunc=null),this._connectedTransformNode=a,this.spatialSound||(this.spatialSound=!0,this._createSpatialParameters(),this.isPlaying&&this.loop&&(this.stop(),this.play(0,this._offset,this._length))),this._onRegisterAfterWorldMatrixUpdate(this._connectedTransformNode),this._registerFunc=function(a){return b._onRegisterAfterWorldMatrixUpdate(a)},this._connectedTransformNode.registerAfterWorldMatrixUpdate(this._registerFunc)},a.prototype.detachFromMesh=function(){this._connectedTransformNode&&this._registerFunc&&(this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc),this._registerFunc=null,this._connectedTransformNode=null)},a.prototype._onRegisterAfterWorldMatrixUpdate=function(a){if(a.getBoundingInfo){var b=a.getBoundingInfo();this.setPosition(b.boundingSphere.centerWorld)}else this.setPosition(a.absolutePosition);(null===(b=T.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._isDirectional&&this.isPlaying&&this._updateDirection()},a.prototype.clone=function(){var b=this;if(this._streaming)return null;var c=function(){b._isReadyToPlay?(e._audioBuffer=b.getAudioBuffer(),e._isReadyToPlay=!0,e.autoplay&&e.play(0,b._offset,b._length)):window.setTimeout(c,300)},d={autoplay:this.autoplay,loop:this.loop,volume:this._volume,spatialSound:this.spatialSound,maxDistance:this.maxDistance,useCustomAttenuation:this.useCustomAttenuation,rolloffFactor:this.rolloffFactor,refDistance:this.refDistance,distanceModel:this.distanceModel},e=new a(this.name+"_cloned",new ArrayBuffer(0),this._scene,null,d);return this.useCustomAttenuation&&e.setAttenuationFunction(this._customAttenuationFunction),e.setPosition(this._position),e.setPlaybackRate(this._playbackRate),c(),e},a.prototype.getAudioBuffer=function(){return this._audioBuffer},a.prototype.getSoundSource=function(){return this._soundSource},a.prototype.getSoundGain=function(){return this._soundGain},a.prototype.serialize=function(){var a={name:this.name,url:this.name,autoplay:this.autoplay,loop:this.loop,volume:this._volume,spatialSound:this.spatialSound,maxDistance:this.maxDistance,rolloffFactor:this.rolloffFactor,refDistance:this.refDistance,distanceModel:this.distanceModel,playbackRate:this._playbackRate,panningModel:this._panningModel,soundTrackId:this.soundTrackId,metadata:this.metadata};return this.spatialSound&&(this._connectedTransformNode&&(a.connectedMeshId=this._connectedTransformNode.id),a.position=this._position.asArray(),a.refDistance=this.refDistance,a.distanceModel=this.distanceModel,a.isDirectional=this._isDirectional,a.localDirectionToMesh=this._localDirection.asArray(),a.coneInnerAngle=this._coneInnerAngle,a.coneOuterAngle=this._coneOuterAngle,a.coneOuterGain=this._coneOuterGain),a},a.Parse=function(b,c,d,e){var f=b.name;d=b.url?d+b.url:d+f;var h,i={autoplay:b.autoplay,loop:b.loop,volume:b.volume,spatialSound:b.spatialSound,maxDistance:b.maxDistance,rolloffFactor:b.rolloffFactor,refDistance:b.refDistance,distanceModel:b.distanceModel,playbackRate:b.playbackRate};if(e){var j=function(){e._isReadyToPlay?(h._audioBuffer=e.getAudioBuffer(),h._isReadyToPlay=!0,h.autoplay&&h.play(0,h._offset,h._length)):window.setTimeout(j,300)};h=new a(f,new ArrayBuffer(0),c,null,i),j()}else h=new a(f,d,c,function(){c._removePendingData(h)},i),c._addPendingData(h);if(b.position){f=g.e.FromArray(b.position);h.setPosition(f)}if(b.isDirectional&&(h.setDirectionalCone(b.coneInnerAngle||360,b.coneOuterAngle||360,b.coneOuterGain||0),b.localDirectionToMesh)){d=g.e.FromArray(b.localDirectionToMesh);h.setLocalDirectionToMesh(d)}if(b.connectedMeshId){i=c.getMeshById(b.connectedMeshId);i&&h.attachToMesh(i)}return b.metadata&&(h.metadata=b.metadata),h},a._SceneComponentInitialization=function(a){throw Object(Ka.a)("AudioSceneComponent")},a}(),Ma=function(){function a(a,b){void 0===b&&(b={}),this.id=-1,this._isInitialized=!1,this._scene=a,this.soundCollection=new Array,this._options=b,!this._options.mainTrack&&this._scene.soundTracks&&(this._scene.soundTracks.push(this),this.id=this._scene.soundTracks.length-1)}return a.prototype._initializeSoundTrackAudioGraph=function(){var a;(null===(a=T.a.audioEngine)||void 0===a?void 0:a.canUseWebAudio)&&T.a.audioEngine.audioContext&&(this._outputAudioNode=T.a.audioEngine.audioContext.createGain(),this._outputAudioNode.connect(T.a.audioEngine.masterGain),this._options&&this._options.volume&&(this._outputAudioNode.gain.value=this._options.volume),this._isInitialized=!0)},a.prototype.dispose=function(){if(T.a.audioEngine&&T.a.audioEngine.canUseWebAudio){for(this._connectedAnalyser&&this._connectedAnalyser.stopDebugCanvas();this.soundCollection.length;)this.soundCollection[0].dispose();this._outputAudioNode&&this._outputAudioNode.disconnect(),this._outputAudioNode=null}},a.prototype.addSound=function(a){var b;this._isInitialized||this._initializeSoundTrackAudioGraph(),(null===(b=T.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._outputAudioNode&&a.connectToSoundTrackAudioNode(this._outputAudioNode),a.soundTrackId&&(-1===a.soundTrackId?this._scene.mainSoundTrack.removeSound(a):this._scene.soundTracks&&this._scene.soundTracks[a.soundTrackId].removeSound(a)),this.soundCollection.push(a),a.soundTrackId=this.id},a.prototype.removeSound=function(a){a=this.soundCollection.indexOf(a);-1!==a&&this.soundCollection.splice(a,1)},a.prototype.setVolume=function(a){var b;(null===(b=T.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._outputAudioNode&&(this._outputAudioNode.gain.value=a)},a.prototype.switchPanningModelToHRTF=function(){var a;if(null===(a=T.a.audioEngine)||void 0===a?void 0:a.canUseWebAudio)for(a=0;a0?b.activeCameras[0]:b.activeCamera){this._cachedCameraPosition.equals(d.globalPosition)||(this._cachedCameraPosition.copyFrom(d.globalPosition),c.audioContext.listener.setPosition(d.globalPosition.x,d.globalPosition.y,d.globalPosition.z)),d.rigCameras&&d.rigCameras.length>0&&(d=d.rigCameras[0]);d=g.a.Invert(d.getViewMatrix());d=g.e.TransformNormal(a._CameraDirection,d);d.normalize(),isNaN(d.x)||isNaN(d.y)||isNaN(d.z)||this._cachedCameraDirection.equals(d)||(this._cachedCameraDirection.copyFrom(d),c.audioContext.listener.setOrientation(d.x,d.y,d.z,0,1,0))}else c.audioContext.listener.setPosition(0,0,0)}for(d=0;d0?1/a:0,e=0;e0},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){var b=this;this._attachedCamera=a;var c=this._attachedCamera.getScene();this._onPrePointerObservableObserver=c.onPrePointerObservable.add(function(a){a.type!==Ta.a.POINTERDOWN?a.type===Ta.a.POINTERUP&&(b._isPointerDown=!1):b._isPointerDown=!0}),this._onAfterCheckInputsObserver=a.onAfterCheckInputsObservable.add(function(){var a=Q.a.Now,c=0;null!=b._lastFrameTime&&(c=a-b._lastFrameTime),b._lastFrameTime=a,b._applyUserInteraction();a=a-b._lastInteractionTime-b._idleRotationWaitTime;a=Math.max(Math.min(a/b._idleRotationSpinupTime,1),0);b._cameraRotationSpeed=b._idleRotationSpeed*a,b._attachedCamera&&(b._attachedCamera.alpha-=b._cameraRotationSpeed*(c/1e3))})},a.prototype.detach=function(){if(this._attachedCamera){var a=this._attachedCamera.getScene();this._onPrePointerObservableObserver&&a.onPrePointerObservable.remove(this._onPrePointerObservableObserver),this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._attachedCamera=null}},a.prototype._userIsZooming=function(){return!!this._attachedCamera&&0!==this._attachedCamera.inertialRadiusOffset},a.prototype._shouldAnimationStopForInteraction=function(){if(!this._attachedCamera)return!1;var a=!1;return this._lastFrameRadius===this._attachedCamera.radius&&0!==this._attachedCamera.inertialRadiusOffset&&(a=!0),this._lastFrameRadius=this._attachedCamera.radius,this._zoomStopsAnimation?a:this._userIsZooming()},a.prototype._applyUserInteraction=function(){this._userIsMoving()&&!this._shouldAnimationStopForInteraction()&&(this._lastInteractionTime=Q.a.Now)},a.prototype._userIsMoving=function(){return!!this._attachedCamera&&(0!==this._attachedCamera.inertialAlphaOffset||0!==this._attachedCamera.inertialBetaOffset||0!==this._attachedCamera.inertialRadiusOffset||0!==this._attachedCamera.inertialPanningX||0!==this._attachedCamera.inertialPanningY||this._isPointerDown)},a}(),Va=function(){function a(){this.transitionDuration=450,this.lowerRadiusTransitionRange=2,this.upperRadiusTransitionRange=-2,this._autoTransitionRange=!1,this._radiusIsAnimating=!1,this._radiusBounceTransition=null,this._animatables=new Array}return Object.defineProperty(a.prototype,"name",{get:function(){return"Bouncing"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"autoTransitionRange",{get:function(){return this._autoTransitionRange},set:function(a){var b=this;if(this._autoTransitionRange!==a){this._autoTransitionRange=a;var c=this._attachedCamera;c&&(a?this._onMeshTargetChangedObserver=c.onMeshTargetChangedObservable.add(function(a){if(a){a.computeWorldMatrix(!0);a=a.getBoundingInfo().diagonalLength;b.lowerRadiusTransitionRange=.05*a,b.upperRadiusTransitionRange=.05*a}}):this._onMeshTargetChangedObserver&&c.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver))}},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){var b=this;this._attachedCamera=a,this._onAfterCheckInputsObserver=a.onAfterCheckInputsObservable.add(function(){b._attachedCamera&&(b._isRadiusAtLimit(b._attachedCamera.lowerRadiusLimit)&&b._applyBoundRadiusAnimation(b.lowerRadiusTransitionRange),b._isRadiusAtLimit(b._attachedCamera.upperRadiusLimit)&&b._applyBoundRadiusAnimation(b.upperRadiusTransitionRange))})},a.prototype.detach=function(){this._attachedCamera&&(this._onAfterCheckInputsObserver&&this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._onMeshTargetChangedObserver&&this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver),this._attachedCamera=null)},a.prototype._isRadiusAtLimit=function(a){return!!this._attachedCamera&&this._attachedCamera.radius===a&&!this._radiusIsAnimating},a.prototype._applyBoundRadiusAnimation=function(b){var c=this;if(this._attachedCamera){this._radiusBounceTransition||(a.EasingFunction.setEasingMode(a.EasingMode),this._radiusBounceTransition=O.CreateAnimation("radius",O.ANIMATIONTYPE_FLOAT,60,a.EasingFunction)),this._cachedWheelPrecision=this._attachedCamera.wheelPrecision,this._attachedCamera.wheelPrecision=1/0,this._attachedCamera.inertialRadiusOffset=0,this.stopAllAnimations(),this._radiusIsAnimating=!0;b=O.TransitionTo("radius",this._attachedCamera.radius+b,this._attachedCamera,this._attachedCamera.getScene(),60,this._radiusBounceTransition,this.transitionDuration,function(){return c._clearAnimationLocks()});b&&this._animatables.push(b)}},a.prototype._clearAnimationLocks=function(){this._radiusIsAnimating=!1,this._attachedCamera&&(this._attachedCamera.wheelPrecision=this._cachedWheelPrecision)},a.prototype.stopAllAnimations=function(){for(this._attachedCamera&&(this._attachedCamera.animations=[]);this._animatables.length;)this._animatables[0].onAnimationEnd=null,this._animatables[0].stop(),this._animatables.shift()},a.EasingFunction=new ra(.3),a.EasingMode=pa.EASINGMODE_EASEOUT,a}(),Wa=function(){function a(){this._mode=a.FitFrustumSidesMode,this._radiusScale=1,this._positionScale=.5,this._defaultElevation=.3,this._elevationReturnTime=1500,this._elevationReturnWaitTime=1e3,this._zoomStopsAnimation=!1,this._framingTime=1500,this.autoCorrectCameraLimitsAndSensibility=!0,this._isPointerDown=!1,this._lastInteractionTime=-1/0,this._animatables=new Array,this._betaIsAnimating=!1}return Object.defineProperty(a.prototype,"name",{get:function(){return"Framing"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mode",{get:function(){return this._mode},set:function(a){this._mode=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"radiusScale",{get:function(){return this._radiusScale},set:function(a){this._radiusScale=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"positionScale",{get:function(){return this._positionScale},set:function(a){this._positionScale=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"defaultElevation",{get:function(){return this._defaultElevation},set:function(a){this._defaultElevation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"elevationReturnTime",{get:function(){return this._elevationReturnTime},set:function(a){this._elevationReturnTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"elevationReturnWaitTime",{get:function(){return this._elevationReturnWaitTime},set:function(a){this._elevationReturnWaitTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zoomStopsAnimation",{get:function(){return this._zoomStopsAnimation},set:function(a){this._zoomStopsAnimation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"framingTime",{get:function(){return this._framingTime},set:function(a){this._framingTime=a},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(b){var c=this;this._attachedCamera=b;var d=this._attachedCamera.getScene();a.EasingFunction.setEasingMode(a.EasingMode),this._onPrePointerObservableObserver=d.onPrePointerObservable.add(function(a){a.type!==Ta.a.POINTERDOWN?a.type===Ta.a.POINTERUP&&(c._isPointerDown=!1):c._isPointerDown=!0}),this._onMeshTargetChangedObserver=b.onMeshTargetChangedObservable.add(function(a){a&&c.zoomOnMesh(a)}),this._onAfterCheckInputsObserver=b.onAfterCheckInputsObservable.add(function(){c._applyUserInteraction(),c._maintainCameraAboveGround()})},a.prototype.detach=function(){if(this._attachedCamera){var a=this._attachedCamera.getScene();this._onPrePointerObservableObserver&&a.onPrePointerObservable.remove(this._onPrePointerObservableObserver),this._onAfterCheckInputsObserver&&this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._onMeshTargetChangedObserver&&this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver),this._attachedCamera=null}},a.prototype.zoomOnMesh=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null),a.computeWorldMatrix(!0);a=a.getBoundingInfo().boundingBox;this.zoomOnBoundingInfo(a.minimumWorld,a.maximumWorld,b,c)},a.prototype.zoomOnMeshHierarchy=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null),a.computeWorldMatrix(!0);a=a.getHierarchyBoundingVectors(!0);this.zoomOnBoundingInfo(a.min,a.max,b,c)},a.prototype.zoomOnMeshesHierarchy=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null);for(var d=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),e=new g.e(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),f=0;fd.upperRadiusLimit?d.upperRadiusLimit:b),b):0},a.prototype._maintainCameraAboveGround=function(){var b=this;if(!(this._elevationReturnTime<0)){var c=Q.a.Now-this._lastInteractionTime,d=.5*Math.PI-this._defaultElevation,e=.5*Math.PI;if(this._attachedCamera&&!this._betaIsAnimating&&this._attachedCamera.beta>e&&c>=this._elevationReturnWaitTime){this._betaIsAnimating=!0,this.stopAllAnimations(),this._betaTransition||(this._betaTransition=O.CreateAnimation("beta",O.ANIMATIONTYPE_FLOAT,60,a.EasingFunction));e=O.TransitionTo("beta",d,this._attachedCamera,this._attachedCamera.getScene(),60,this._betaTransition,this._elevationReturnTime,function(){b._clearAnimationLocks(),b.stopAllAnimations()});e&&this._animatables.push(e)}}},a.prototype._getFrustumSlope=function(){var a=this._attachedCamera;if(!a)return g.d.Zero();var b=a.getScene().getEngine().getAspectRatio(a);a=Math.tan(a.fov/2);b=a*b;return new g.d(b,a)},a.prototype._clearAnimationLocks=function(){this._betaIsAnimating=!1},a.prototype._applyUserInteraction=function(){this.isUserIsMoving&&(this._lastInteractionTime=Q.a.Now,this.stopAllAnimations(),this._clearAnimationLocks())},a.prototype.stopAllAnimations=function(){for(this._attachedCamera&&(this._attachedCamera.animations=[]);this._animatables.length;)this._animatables[0]&&(this._animatables[0].onAnimationEnd=null,this._animatables[0].stop()),this._animatables.shift()},Object.defineProperty(a.prototype,"isUserIsMoving",{get:function(){return!!this._attachedCamera&&(0!==this._attachedCamera.inertialAlphaOffset||0!==this._attachedCamera.inertialBetaOffset||0!==this._attachedCamera.inertialRadiusOffset||0!==this._attachedCamera.inertialPanningX||0!==this._attachedCamera.inertialPanningY||this._isPointerDown)},enumerable:!1,configurable:!0}),a.EasingFunction=new va,a.EasingMode=pa.EASINGMODE_EASEINOUT,a.IgnoreBoundsSizeMode=0,a.FitFrustumSidesMode=1,a}(),Xa=function(a,b,c,d){void 0===b&&(b=new g.e),void 0===c&&(c=0),void 0===d&&(d=!1),this.direction=a,this.rotatedDirection=b,this.diff=c,this.ignore=d},Ya=function(){function a(a){this.ui=a,this.name="AttachToBoxBehavior",this.distanceAwayFromFace=.15,this.distanceAwayFromBottomOfFace=.15,this._faceVectors=[new Xa(g.e.Up()),new Xa(g.e.Down()),new Xa(g.e.Left()),new Xa(g.e.Right()),new Xa(g.e.Forward()),new Xa(g.e.Forward().scaleInPlace(-1))],this._tmpMatrix=new g.a,this._tmpVector=new g.e,this._zeroVector=g.e.Zero(),this._lookAtTmpMatrix=new g.a}return a.prototype.init=function(){},a.prototype._closestFace=function(a){var b=this;return this._faceVectors.forEach(function(c){b._target.rotationQuaternion||(b._target.rotationQuaternion=g.b.RotationYawPitchRoll(b._target.rotation.y,b._target.rotation.x,b._target.rotation.z)),b._target.rotationQuaternion.toRotationMatrix(b._tmpMatrix),g.e.TransformCoordinatesToRef(c.direction,b._tmpMatrix,c.rotatedDirection),c.diff=g.e.GetAngleBetweenVectors(c.rotatedDirection,a,g.e.Cross(c.rotatedDirection,a))}),this._faceVectors.reduce(function(a,b){return a.ignore?b:b.ignore||a.diff1)return a._setAllVisibility(a._ownerNode,1),void (a._hoverValue=a.fadeInTime+a.delay);if(a._ownerNode.visibility<0&&(a._setAllVisibility(a._ownerNode,0),a._hoverValue<0))return void (a._hoverValue=0);setTimeout(a._update,a._millisecondsPerFrame)}}}return Object.defineProperty(a.prototype,"name",{get:function(){return"FadeInOut"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){this._ownerNode=a,this._setAllVisibility(this._ownerNode,0)},a.prototype.detach=function(){this._ownerNode=null},a.prototype.fadeIn=function(a){this._hovered=a,this._update()},a.prototype._setAllVisibility=function(a,b){var c=this;a.visibility=b,a.getChildMeshes().forEach(function(a){c._setAllVisibility(a,b)})},a}(),$a=c(72),ab=function(){function a(){this._startDistance=0,this._initialScale=new g.e(0,0,0),this._targetScale=new g.e(0,0,0),this._sceneRenderObserver=null,this._dragBehaviorA=new $a.a({}),this._dragBehaviorA.moveAttached=!1,this._dragBehaviorB=new $a.a({}),this._dragBehaviorB.moveAttached=!1}return Object.defineProperty(a.prototype,"name",{get:function(){return"MultiPointerScale"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype._getCurrentDistance=function(){return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length()},a.prototype.attach=function(a){var b=this;this._ownerNode=a,this._dragBehaviorA.onDragStartObservable.add(function(c){b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging&&(b._dragBehaviorA.currentDraggingPointerId==b._dragBehaviorB.currentDraggingPointerId?b._dragBehaviorA.releaseDrag():(b._initialScale.copyFrom(a.scaling),b._startDistance=b._getCurrentDistance()))}),this._dragBehaviorB.onDragStartObservable.add(function(c){b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging&&(b._dragBehaviorA.currentDraggingPointerId==b._dragBehaviorB.currentDraggingPointerId?b._dragBehaviorB.releaseDrag():(b._initialScale.copyFrom(a.scaling),b._startDistance=b._getCurrentDistance()))}),[this._dragBehaviorA,this._dragBehaviorB].forEach(function(a){a.onDragObservable.add(function(){if(b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging){var a=b._getCurrentDistance()/b._startDistance;b._initialScale.scaleToRef(a,b._targetScale)}})}),a.addBehavior(this._dragBehaviorA),a.addBehavior(this._dragBehaviorB),this._sceneRenderObserver=a.getScene().onBeforeRenderObservable.add(function(){if(b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging){var c=b._targetScale.subtract(a.scaling).scaleInPlace(.1);c.length()>.01&&a.scaling.addInPlace(c)}})},a.prototype.detach=function(){var a=this;this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver),[this._dragBehaviorA,this._dragBehaviorB].forEach(function(b){b.onDragStartObservable.clear(),b.onDragObservable.clear(),a._ownerNode.removeBehavior(b)})},a}(),bb=c(33),cb=c(28),db=function(){function a(){this._attachedToElement=!1,this._virtualMeshesInfo={},this._tmpVector=new g.e,this._tmpQuaternion=new g.b,this._dragType={NONE:0,DRAG:1,DRAG_WITH_CONTROLLER:2,NEAR_DRAG:3},this._moving=!1,this._dragging=this._dragType.NONE,this.draggableMeshes=null,this.zDragFactor=3,this.currentDraggingPointerIds=[],this.detachCameraControls=!0,this.onDragStartObservable=new f.c,this.onDragObservable=new f.c,this.onDragEndObservable=new f.c,this.allowMultiPointer=!0}return Object.defineProperty(a.prototype,"currentDraggingPointerId",{get:function(){return void 0!==this.currentDraggingPointerIds[0]?this.currentDraggingPointerIds[0]:-1},set:function(a){this.currentDraggingPointerIds[0]=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentDraggingPointerID",{get:function(){return this.currentDraggingPointerId},set:function(a){this.currentDraggingPointerId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"BaseSixDofDrag"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isMoving",{get:function(){return this._moving},enumerable:!1,configurable:!0}),a.prototype.init=function(){},Object.defineProperty(a.prototype,"_pointerCamera",{get:function(){return this._scene.cameraToUseForPointers?this._scene.cameraToUseForPointers:this._scene.activeCamera},enumerable:!1,configurable:!0}),a.prototype._createVirtualMeshInfo=function(){var b=new bb.a("",a._virtualScene);b.rotationQuaternion=new g.b;var c=new bb.a("",a._virtualScene);c.rotationQuaternion=new g.b;var d=new bb.a("",a._virtualScene);return d.rotationQuaternion=new g.b,{dragging:!1,moving:!1,dragMesh:b,originMesh:c,pivotMesh:d,startingPivotPosition:new g.e,startingPivotOrientation:new g.b,startingPosition:new g.e,startingOrientation:new g.b,lastOriginPosition:new g.e,lastDragPosition:new g.e}},a.prototype._resetVirtualMeshesPosition=function(){for(var a=0;a0)return;!c._pointerCamera||c._pointerCamera.cameraRigMode!==cb.a.RIG_MODE_NONE||c._pointerCamera._isLeftCamera||c._pointerCamera._isRightCamera||a.pickInfo.ray.origin.copyFrom(c._pointerCamera.globalPosition),c._ownerNode.computeWorldMatrix(!0);d=c._virtualMeshesInfo[b];f?(c._dragging=a.pickInfo.originMesh?c._dragType.NEAR_DRAG:c._dragType.DRAG_WITH_CONTROLLER,d.originMesh.position.copyFrom(a.pickInfo.aimTransform.position),c._dragging===c._dragType.NEAR_DRAG&&a.pickInfo.gripTransform?d.originMesh.rotationQuaternion.copyFrom(a.pickInfo.gripTransform.rotationQuaternion):d.originMesh.rotationQuaternion.copyFrom(a.pickInfo.aimTransform.rotationQuaternion)):(c._dragging=c._dragType.DRAG,d.originMesh.position.copyFrom(a.pickInfo.ray.origin)),d.lastOriginPosition.copyFrom(d.originMesh.position),d.dragMesh.position.copyFrom(a.pickInfo.pickedPoint),d.lastDragPosition.copyFrom(a.pickInfo.pickedPoint),d.pivotMesh.position.copyFrom(c._ownerNode.getAbsolutePivotPoint()),d.pivotMesh.rotationQuaternion.copyFrom(c._ownerNode.absoluteRotationQuaternion),d.startingPosition.copyFrom(d.dragMesh.position),d.startingPivotPosition.copyFrom(d.pivotMesh.position),d.startingOrientation.copyFrom(d.dragMesh.rotationQuaternion),d.startingPivotOrientation.copyFrom(d.pivotMesh.rotationQuaternion),f?(d.originMesh.addChild(d.dragMesh),d.originMesh.addChild(d.pivotMesh)):d.originMesh.lookAt(d.dragMesh.position),d.dragging=!0,-1===c.currentDraggingPointerIds.indexOf(b)&&c.currentDraggingPointerIds.push(b),c.detachCameraControls&&c._pointerCamera&&!c._pointerCamera.leftCamera&&(c._pointerCamera.inputs&&c._pointerCamera.inputs.attachedToElement?(c._pointerCamera.detachControl(),c._attachedToElement=!0):c._attachedToElement=!1),c._targetDragStart(d.pivotMesh.position,d.pivotMesh.rotationQuaternion,b),c.onDragStartObservable.notifyObservers({position:d.pivotMesh.position})}}else if(a.type==Ta.a.POINTERUP||a.type==Ta.a.POINTERDOUBLETAP){d=c.currentDraggingPointerIds.indexOf(b);e.dragging=!1,-1!==d&&(c.currentDraggingPointerIds.splice(d,1),0===c.currentDraggingPointerIds.length&&(c._moving=!1,c._dragging=c._dragType.NONE,c.detachCameraControls&&c._attachedToElement&&c._pointerCamera&&!c._pointerCamera.leftCamera&&(c._pointerCamera.attachControl(!0),c._attachedToElement=!1)),e.originMesh.removeChild(e.dragMesh),e.originMesh.removeChild(e.pivotMesh),c._targetDragEnd(b),c.onDragEndObservable.notifyObservers({}))}else if(a.type==Ta.a.POINTERMOVE&&(-1!==c.currentDraggingPointerIds.indexOf(b)&&e.dragging&&a.pickInfo&&(a.pickInfo.ray||a.pickInfo.aimTransform))){d=c.zDragFactor;(c.currentDraggingPointerIds.length>1||a.pickInfo.originMesh)&&(d=0),c._ownerNode.computeWorldMatrix(!0),f?c._pointerUpdateXR(a.pickInfo.aimTransform,a.pickInfo.gripTransform,b,d):c._pointerUpdate2D(a.pickInfo.ray,b,d),c._tmpQuaternion.copyFrom(e.startingPivotOrientation),c._tmpQuaternion.x=-c._tmpQuaternion.x,c._tmpQuaternion.y=-c._tmpQuaternion.y,c._tmpQuaternion.z=-c._tmpQuaternion.z,e.pivotMesh.absoluteRotationQuaternion.multiplyToRef(c._tmpQuaternion,c._tmpQuaternion),e.pivotMesh.absolutePosition.subtractToRef(e.startingPivotPosition,c._tmpVector),c.onDragObservable.notifyObservers({delta:c._tmpVector,position:e.pivotMesh.position,pickInfo:a.pickInfo}),c._targetDrag(c._tmpVector,c._tmpQuaternion,b),e.lastDragPosition.copyFrom(e.dragMesh.absolutePosition),c._moving=!0}})},a.prototype._applyZOffset=function(a,b,c){a.position.z-=a.position.z<1?b*c:b*c*a.position.z,a.position.z<0&&(a.position.z=0)},a.prototype._targetDragStart=function(a,b,c){},a.prototype._targetDrag=function(a,b,c){},a.prototype._targetDragEnd=function(a){},a.prototype.detach=function(){for(var a in this._scene&&(this.detachCameraControls&&this._attachedToElement&&this._pointerCamera&&!this._pointerCamera.leftCamera&&(this._pointerCamera.attachControl(!0),this._attachedToElement=!1),this._scene.onPointerObservable.remove(this._pointerObserver)),this._virtualMeshesInfo)this._virtualMeshesInfo[a].originMesh.dispose(),this._virtualMeshesInfo[a].dragMesh.dispose();this.onDragEndObservable.clear(),this.onDragObservable.clear(),this.onDragStartObservable.clear()},a}(),eb=c(46),fb=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._sceneRenderObserver=null,b._targetPosition=new g.e(0,0,0),b._targetOrientation=new g.b,b._targetScaling=new g.e(1,1,1),b._startingPosition=new g.e(0,0,0),b._startingOrientation=new g.b,b._startingScaling=new g.e(1,1,1),b.onPositionChangedObservable=new f.c,b.dragDeltaRatio=.2,b.rotateDraggedObject=!0,b.rotateAroundYOnly=!1,b.rotateWithMotionController=!0,b.disableMovement=!1,b.faceCameraOnDragStart=!1,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"name",{get:function(){return"SixDofDrag"},enumerable:!1,configurable:!0}),b.prototype.attach=function(b){var c=this;a.prototype.attach.call(this,b),b.isNearGrabbable=!0,this._virtualTransformNode=new eb.a("virtual_sixDof",db._virtualScene),this._virtualTransformNode.rotationQuaternion=g.b.Identity(),this._sceneRenderObserver=b.getScene().onBeforeRenderObservable.add(function(){if(1===c.currentDraggingPointerIds.length&&c._moving&&!c.disableMovement){var a=b.parent;b.setParent(null),b.position.addInPlace(c._targetPosition.subtract(b.position).scale(c.dragDeltaRatio)),c.onPositionChangedObservable.notifyObservers({position:b.absolutePosition}),(!a||a.scaling&&!a.scaling.isNonUniformWithinEpsilon(.001))&&g.b.SlerpToRef(b.rotationQuaternion,c._targetOrientation,c.dragDeltaRatio,b.rotationQuaternion),b.setParent(a)}})},b.prototype._getPositionOffsetAround=function(a,b,c){var d=g.c.Matrix[0],e=g.c.Matrix[1],f=g.c.Matrix[2],h=g.c.Matrix[3],i=g.c.Matrix[4];return g.a.TranslationToRef(a.x,a.y,a.z,d),g.a.TranslationToRef(-a.x,-a.y,-a.z,e),g.a.FromQuaternionToRef(c,f),g.a.ScalingToRef(b,b,b,h),e.multiplyToRef(f,i),i.multiplyToRef(h,i),i.multiplyToRef(d,i),i.getTranslation()},b.prototype._onePointerPositionUpdated=function(a,b){g.c.Vector3[0].setAll(0),this._dragging===this._dragType.DRAG?this.rotateDraggedObject&&(this.rotateAroundYOnly?g.b.RotationYawPitchRollToRef(b.toEulerAngles().y,0,0,g.c.Quaternion[0]):g.c.Quaternion[0].copyFrom(b),g.c.Quaternion[0].multiplyToRef(this._startingOrientation,this._targetOrientation)):(this._dragging===this._dragType.NEAR_DRAG||this._dragging===this._dragType.DRAG_WITH_CONTROLLER&&this.rotateWithMotionController)&&b.multiplyToRef(this._startingOrientation,this._targetOrientation),this._targetPosition.copyFrom(this._startingPosition).addInPlace(a)},b.prototype._twoPointersPositionUpdated=function(){var a=this._virtualMeshesInfo[this.currentDraggingPointerIds[0]].startingPosition,b=this._virtualMeshesInfo[this.currentDraggingPointerIds[1]].startingPosition,c=g.c.Vector3[0];a.addToRef(b,c),c.scaleInPlace(.5);var d=g.c.Vector3[1];b.subtractToRef(a,d);b=this._virtualMeshesInfo[this.currentDraggingPointerIds[0]].dragMesh.absolutePosition;a=this._virtualMeshesInfo[this.currentDraggingPointerIds[1]].dragMesh.absolutePosition;var e=g.c.Vector3[2];b.addToRef(a,e),e.scaleInPlace(.5);var f=g.c.Vector3[3];a.subtractToRef(b,f);a=f.length()/d.length();b=e.subtract(c);e=g.b.FromEulerAngles(0,g.e.GetAngleBetweenVectorsOnPlane(d.normalize(),f.normalize(),g.e.UpReadOnly),0);d=this._ownerNode.parent;this._ownerNode.setParent(null);f=this._getPositionOffsetAround(c.subtract(this._virtualTransformNode.getAbsolutePivotPoint()),a,e);this._virtualTransformNode.rotationQuaternion.multiplyToRef(e,this._ownerNode.rotationQuaternion),this._virtualTransformNode.scaling.scaleToRef(a,this._ownerNode.scaling),this._virtualTransformNode.position.addToRef(b.addInPlace(f),this._ownerNode.position),this.onPositionChangedObservable.notifyObservers({position:this._ownerNode.position}),this._ownerNode.setParent(d)},b.prototype._targetDragStart=function(){var a=this.currentDraggingPointerIds.length,b=this._ownerNode.parent;this._ownerNode.rotationQuaternion||(this._ownerNode.rotationQuaternion=g.b.RotationYawPitchRoll(this._ownerNode.rotation.y,this._ownerNode.rotation.x,this._ownerNode.rotation.z));var c=this._ownerNode.getAbsolutePivotPoint();if(this._ownerNode.setParent(null),1===a){if(this._targetPosition.copyFrom(this._ownerNode.position),this._targetOrientation.copyFrom(this._ownerNode.rotationQuaternion),this._targetScaling.copyFrom(this._ownerNode.scaling),this.faceCameraOnDragStart&&this._scene.activeCamera){var d=g.c.Vector3[0];this._scene.activeCamera.position.subtractToRef(c,d),d.normalize();var e=g.c.Quaternion[0];this._scene.useRightHandedSystem?g.b.FromLookDirectionRHToRef(d,new g.e(0,1,0),e):g.b.FromLookDirectionLHToRef(d,new g.e(0,1,0),e),e.normalize(),g.b.RotationYawPitchRollToRef(e.toEulerAngles().y,0,0,g.c.Quaternion[0]),this._targetOrientation.copyFrom(g.c.Quaternion[0])}this._startingPosition.copyFrom(this._targetPosition),this._startingOrientation.copyFrom(this._targetOrientation),this._startingScaling.copyFrom(this._targetScaling)}else 2===a&&(this._virtualTransformNode.setPivotPoint(new g.e(0,0,0),R.c.LOCAL),this._virtualTransformNode.position.copyFrom(this._ownerNode.position),this._virtualTransformNode.scaling.copyFrom(this._ownerNode.scaling),this._virtualTransformNode.rotationQuaternion.copyFrom(this._ownerNode.rotationQuaternion),this._virtualTransformNode.setPivotPoint(c,R.c.WORLD),this._resetVirtualMeshesPosition());this._ownerNode.setParent(b)},b.prototype._targetDrag=function(a,b,c){1===this.currentDraggingPointerIds.length?this._onePointerPositionUpdated(a,b):2===this.currentDraggingPointerIds.length&&this._twoPointersPositionUpdated()},b.prototype._targetDragEnd=function(){if(1===this.currentDraggingPointerIds.length){this._resetVirtualMeshesPosition();var a=this.faceCameraOnDragStart;this.faceCameraOnDragStart=!1,this._targetDragStart(),this.faceCameraOnDragStart=a}},b.prototype.detach=function(){a.prototype.detach.call(this),this._ownerNode&&(this._ownerNode.isNearGrabbable=!1,this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver)),this._virtualTransformNode&&this._virtualTransformNode.dispose()},b}(db),gb=function(){function a(){this._attachPointLocalOffset=new g.e,this._workingPosition=new g.e,this._workingQuaternion=new g.b,this._lastTick=-1,this._hit=!1,this.hitNormalOffset=.05,this.meshes=[],this.interpolatePose=!0,this.lerpTime=250,this.keepOrientationVertical=!0,this.enabled=!0,this.maxStickingDistance=.8}return Object.defineProperty(a.prototype,"name",{get:function(){return"SurfaceMagnetism"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a,b){this._attachedMesh=a,this._scene=b||a.getScene(),this._attachedMesh.rotationQuaternion||(this._attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this._attachedMesh.rotation.y,this._attachedMesh.rotation.x,this._attachedMesh.rotation.z)),this.updateAttachPoint(),this._workingPosition.copyFrom(this._attachedMesh.position),this._workingQuaternion.copyFrom(this._attachedMesh.rotationQuaternion),this._addObservables()},a.prototype.detach=function(){this._attachedMesh=null,this._removeObservables()},a.prototype._getTargetPose=function(a){if(!this._attachedMesh)return null;if(a&&a.hit){var b=a.getNormal(!0,!0);a=a.pickedPoint;if(!b||!a)return null;b.normalize();var c=g.c.Vector3[0];return c.copyFrom(b),c.scaleInPlace(this.hitNormalOffset),c.addInPlace(a),this._attachedMesh.parent&&(g.c.Matrix[0].copyFrom(this._attachedMesh.parent.getWorldMatrix()).invert(),g.e.TransformNormalToRef(c,g.c.Matrix[0],c)),{position:c,quaternion:g.b.RotationYawPitchRoll(-Math.atan2(b.x,-b.z),this.keepOrientationVertical?0:Math.atan2(b.y,Math.sqrt(b.z*b.z+b.x*b.x)),0)}}return null},a.prototype.updateAttachPoint=function(){this._getAttachPointOffsetToRef(this._attachPointLocalOffset)},a.prototype.findAndUpdateTarget=function(a){if(this._hit=!1,!a.ray)return!1;a=a.ray.intersectsMeshes(this.meshes)[0];if(this._attachedMesh&&a&&a.hit&&a.pickedMesh){a=this._getTargetPose(a);a&&g.e.Distance(this._attachedMesh.position,a.position)c&&(g.b.RotationAxisToRef(d,-h+c,f),b.rotateByQuaternionToRef(f,b),e=!0)}h=this._angleBetweenVectorAndPlane(b,d)*(this._scene.useRightHandedSystem?-1:1);c=this.maxViewHorizontalDegrees*Math.PI/180*.5;return h<-c?(g.b.RotationAxisToRef(a,-h-c,f),b.rotateByQuaternionToRef(f,b),e=!0):h>c&&(g.b.RotationAxisToRef(a,-h+c,f),b.rotateByQuaternionToRef(f,b),e=!0),e},a.prototype._orientationClamp=function(a,b){var c=this._tmpVectors[0];c.copyFrom(a).scaleInPlace(-1).normalize();a=this._tmpVectors[1];var d=this._tmpVectors[2];a.copyFromFloats(0,1,0),g.e.CrossToRef(c,a,d);var e=d.length();ethis.orientToCameraDeadzoneDegrees},a.prototype._updateLeashing=function(a){if(this.attachedNode){var b=this.attachedNode.parent;this.attachedNode.setParent(null);var c=this.attachedNode.getWorldMatrix(),d=this._workingPosition,e=this._workingQuaternion,f=this.attachedNode.getPivotPoint(),h=this._tmpInvertView;h.copyFrom(a.getViewMatrix()),h.invert(),g.e.TransformCoordinatesToRef(f,c,d);var i=this._tmpPosition;i.copyFromFloats(0,0,0),g.e.TransformCoordinatesToRef(i,c,i),i.scaleInPlace(-1).subtractInPlace(f),d.subtractInPlace(a.globalPosition),this.ignoreCameraPitchAndRoll&&this._applyPitchOffset(h);var j=!1,k=this._tmpForward;k.copyFromFloats(0,0,this._scene.useRightHandedSystem?-1:1),g.e.TransformNormalToRef(k,h,k);var m=this._tmpNodeForward;if(m.copyFromFloats(0,0,this._scene.useRightHandedSystem?-1:1),g.e.TransformNormalToRef(m,c,m),this._recenterNextUpdate)d.copyFrom(k).scaleInPlace(this.defaultDistance);else if(this.ignoreAngleClamp){c=d.length();d.copyFrom(k).scaleInPlace(c)}else j=this._angularClamp(h,d);k=!1;this.ignoreDistanceClamp||(k=this._distanceClamp(d,j),this._applyVerticalClamp(d)),this.useFixedVerticalOffset&&(d.y=i.y-a.globalPosition.y+this.fixedVerticalOffset),(j||k||this._passedOrientationDeadzone(d,m)||this._recenterNextUpdate)&&this._orientationClamp(d,e),this._workingPosition.subtractInPlace(f),this._recenterNextUpdate=!1,this.attachedNode.setParent(b)}},a.prototype._updateTransformToGoal=function(a){if(this.attachedNode&&this.followedCamera&&this._enabled){this.attachedNode.rotationQuaternion||(this.attachedNode.rotationQuaternion=g.b.Identity());var b=this.attachedNode.parent;if(this.attachedNode.setParent(null),!this.interpolatePose)return this.attachedNode.position.copyFrom(this.followedCamera.globalPosition).addInPlace(this._workingPosition),void this.attachedNode.rotationQuaternion.copyFrom(this._workingQuaternion);var c=new g.e;c.copyFrom(this.attachedNode.position).subtractInPlace(this.followedCamera.globalPosition),g.e.SmoothToRef(c,this._workingPosition,a,this.lerpTime,c),c.addInPlace(this.followedCamera.globalPosition),this.attachedNode.position.copyFrom(c);c=new g.b;c.copyFrom(this.attachedNode.rotationQuaternion),g.b.SmoothToRef(c,this._workingQuaternion,a,this.lerpTime,this.attachedNode.rotationQuaternion),this.attachedNode.setParent(b)}},a.prototype._addObservables=function(){var a=this;this._lastTick=Date.now(),this._onBeforeRender=this._scene.onBeforeRenderObservable.add(function(){if(a.followedCamera){var b=Date.now();a._updateLeashing(a.followedCamera),a._updateTransformToGoal(b-a._lastTick),a._lastTick=b}})},a.prototype._removeObservables=function(){this._onBeforeRender&&this._scene.onBeforeRenderObservable.remove(this._onBeforeRender)},a}(),jb=function(){function a(){}return a.ANCHOR_SYSTEM="xr-anchor-system",a.BACKGROUND_REMOVER="xr-background-remover",a.HIT_TEST="xr-hit-test",a.MESH_DETECTION="xr-mesh-detection",a.PHYSICS_CONTROLLERS="xr-physics-controller",a.PLANE_DETECTION="xr-plane-detection",a.POINTER_SELECTION="xr-controller-pointer-selection",a.TELEPORTATION="xr-controller-teleportation",a.FEATURE_POINTS="xr-feature-points",a.HAND_TRACKING="xr-hand-tracking",a.IMAGE_TRACKING="xr-image-tracking",a.NEAR_INTERACTION="xr-near-interaction",a.DOM_OVERLAY="xr-dom-overlay",a.MOVEMENT="xr-controller-movement",a.LIGHT_ESTIMATION="xr-light-estimation",a.EYE_TRACKING="xr-eye-tracking",a.WALKING_LOCOMOTION="xr-walking-locomotion",a}(),kb=function(){function a(a){var b=this;this._xrSessionManager=a,this._features={},this._xrSessionManager.onXRSessionInit.add(function(){b.getEnabledFeatures().forEach(function(a){var c=b._features[a];!c.enabled||c.featureImplementation.attached||c.featureImplementation.disableAutoAttach||b.attachFeature(a)})}),this._xrSessionManager.onXRSessionEnded.add(function(){b.getEnabledFeatures().forEach(function(a){var c=b._features[a];c.enabled&&c.featureImplementation.attached&&b.detachFeature(a)})})}return a.AddWebXRFeature=function(a,b,c,d){void 0===c&&(c=1),void 0===d&&(d=!1),this._AvailableFeatures[a]=this._AvailableFeatures[a]||{latest:c},c>this._AvailableFeatures[a].latest&&(this._AvailableFeatures[a].latest=c),d&&(this._AvailableFeatures[a].stable=c),this._AvailableFeatures[a][c]=b},a.ConstructFeature=function(a,b,c,d){void 0===b&&(b=1);a=this._AvailableFeatures[a][b];if(!a)throw new Error("feature not found");return a(c,d)},a.GetAvailableFeatures=function(){return Object.keys(this._AvailableFeatures)},a.GetAvailableVersions=function(a){return Object.keys(this._AvailableFeatures[a])},a.GetLatestVersionOfFeature=function(a){return this._AvailableFeatures[a]&&this._AvailableFeatures[a].latest||-1},a.GetStableVersionOfFeature=function(a){return this._AvailableFeatures[a]&&this._AvailableFeatures[a].stable||-1},a.prototype.attachFeature=function(a){a=this._features[a];a&&a.enabled&&!a.featureImplementation.attached&&a.featureImplementation.attach()},a.prototype.detachFeature=function(a){a=this._features[a];a&&a.featureImplementation.attached&&a.featureImplementation.detach()},a.prototype.disableFeature=function(a){a="string"==typeof a?a:a.Name;var b=this._features[a];return!(!b||!b.enabled)&&(b.enabled=!1,this.detachFeature(a),b.featureImplementation.dispose(),!0)},a.prototype.dispose=function(){var a=this;this.getEnabledFeatures().forEach(function(b){a.disableFeature(b),a._features[b].featureImplementation.dispose()})},a.prototype.enableFeature=function(b,c,d,e,f){var g=this;void 0===c&&(c="latest"),void 0===d&&(d={}),void 0===e&&(e=!0),void 0===f&&(f=!0);b="string"==typeof b?b:b.Name;var h=0;if("string"==typeof c){if(!c)throw new Error("Error in provided version - "+b+" ("+c+")");if(-1===(h="stable"===c?a.GetStableVersionOfFeature(b):"latest"===c?a.GetLatestVersionOfFeature(b):+c)||isNaN(h))throw new Error("feature not found - "+b+" ("+c+")")}else h=c;c=a._ConflictingFeatures[b];if(void 0!==c&&-1!==this.getEnabledFeatures().indexOf(c))throw new Error("Feature "+b+" cannot be enabled while "+c+" is enabled.");c=this._features[b];d=a.ConstructFeature(b,h,this._xrSessionManager,d);if(!d)throw new Error("feature not found - "+b);c&&this.disableFeature(b);c=d();if(c.dependsOn&&!c.dependsOn.every(function(a){return!!g._features[a]}))throw new Error("Dependant features missing. Make sure the following features are enabled - "+c.dependsOn.join(", "));if(c.isCompatible())return this._features[b]={featureImplementation:c,enabled:!0,version:h,required:f},e?this._xrSessionManager.session&&!this._features[b].featureImplementation.attached&&this.attachFeature(b):this._features[b].featureImplementation.disableAutoAttach=!0,this._features[b].featureImplementation;if(f)throw new Error("required feature not compatible");return U.b.Warn("Feature "+b+" not compatible with the current environment/browser and was not enabled."),c},a.prototype.getEnabledFeature=function(a){return this._features[a]&&this._features[a].featureImplementation},a.prototype.getEnabledFeatures=function(){return Object.keys(this._features)},a.prototype._extendXRSessionInitObject=function(a){return Object(l.b)(this,void 0,void 0,function(){var b,c,d,e,f,g,h;return Object(l.e)(this,function(i){switch(i.label){case 0:b=this.getEnabledFeatures(),c=0,d=b,i.label=1;case 1:return c0&&(f.lengthSquared()-h2*this.palmUpStrictness-1&&(a=!0)}}this._node.setEnabled(a&&b)},a.prototype.detach=function(){this._scene.onBeforeRenderObservable.remove(this._sceneRenderObserver)},a.prototype.linkToXRExperience=function(a){this._eyeTracking=a.featuresManager.getEnabledFeature(jb.EYE_TRACKING),this._handTracking=a.featuresManager.getEnabledFeature(jb.HAND_TRACKING)},a}(),mb=function(){function a(a,b,c){if(this.targetPosition=g.e.Zero(),this.poleTargetPosition=g.e.Zero(),this.poleTargetLocalOffset=g.e.Zero(),this.poleAngle=0,this.slerpAmount=1,this._bone1Quat=g.b.Identity(),this._bone1Mat=g.a.Identity(),this._bone2Ang=Math.PI,this._maxAngle=Math.PI,this._rightHandedSystem=!1,this._bendAxis=g.e.Right(),this._slerping=!1,this._adjustRoll=0,this._bone2=b,this._bone1=b.getParent(),this._bone1){this.mesh=a;var d=b.getPosition();if(b.getAbsoluteTransform().determinant()>0&&(this._rightHandedSystem=!0,this._bendAxis.x=0,this._bendAxis.y=0,this._bendAxis.z=-1,d.x>d.y&&d.x>d.z&&(this._adjustRoll=.5*Math.PI,this._bendAxis.z=1)),this._bone1.length){b=this._bone1.getScale();d=this._bone2.getScale();this._bone1Length=this._bone1.length*b.y*this.mesh.scaling.y,this._bone2Length=this._bone2.length*d.y*this.mesh.scaling.y}else if(this._bone1.children[0]){a.computeWorldMatrix(!0);b=this._bone2.children[0].getAbsolutePosition(a);d=this._bone2.getAbsolutePosition(a);var e=this._bone1.getAbsolutePosition(a);this._bone1Length=g.e.Distance(b,d),this._bone2Length=g.e.Distance(d,e)}this._bone1.getRotationMatrixToRef(R.c.WORLD,a,this._bone1Mat),this.maxAngle=Math.PI,c&&(c.targetMesh&&(this.targetMesh=c.targetMesh,this.targetMesh.computeWorldMatrix(!0)),c.poleTargetMesh?(this.poleTargetMesh=c.poleTargetMesh,this.poleTargetMesh.computeWorldMatrix(!0)):c.poleTargetBone?this.poleTargetBone=c.poleTargetBone:this._bone1.getParent()&&(this.poleTargetBone=this._bone1.getParent()),c.poleTargetLocalOffset&&this.poleTargetLocalOffset.copyFrom(c.poleTargetLocalOffset),c.poleAngle&&(this.poleAngle=c.poleAngle),c.bendAxis&&this._bendAxis.copyFrom(c.bendAxis),c.maxAngle&&(this.maxAngle=c.maxAngle),c.slerpAmount&&(this.slerpAmount=c.slerpAmount))}}return Object.defineProperty(a.prototype,"maxAngle",{get:function(){return this._maxAngle},set:function(a){this._setMaxAngle(a)},enumerable:!1,configurable:!0}),a.prototype._setMaxAngle=function(a){a<0&&(a=0),(a>Math.PI||null==a)&&(a=Math.PI),this._maxAngle=a;var b=this._bone1Length,c=this._bone2Length;this._maxReach=Math.sqrt(b*b+c*c-2*b*c*Math.cos(a))},a.prototype.update=function(){var b=this._bone1;if(b){var c=this.targetPosition,d=this.poleTargetPosition,e=a._tmpMats[0],f=a._tmpMats[1];this.targetMesh&&c.copyFrom(this.targetMesh.getAbsolutePosition()),this.poleTargetBone?this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset,this.mesh,d):this.poleTargetMesh&&g.e.TransformCoordinatesToRef(this.poleTargetLocalOffset,this.poleTargetMesh.getWorldMatrix(),d);var h=a._tmpVecs[0],i=a._tmpVecs[1],j=a._tmpVecs[2],k=a._tmpVecs[3],n=a._tmpVecs[4],o=a._tmpQuat;b.getAbsolutePositionToRef(this.mesh,h),d.subtractToRef(h,n),0==n.x&&0==n.y&&0==n.z?n.y=1:n.normalize(),c.subtractToRef(h,k),k.normalize(),g.e.CrossToRef(k,n,i),i.normalize(),g.e.CrossToRef(k,i,j),j.normalize(),g.a.FromXYZAxesToRef(j,k,i,e);b=this._bone1Length;d=this._bone2Length;n=g.e.Distance(h,c);this._maxReach>0&&(n=Math.min(this._maxReach,n));j=(d*d+n*n-b*b)/(2*d*n);i=(n*n+b*b-d*d)/(2*n*b);j>1&&(j=1),i>1&&(i=1),j<-1&&(j=-1),i<-1&&(i=-1);h=Math.acos(j);c=Math.acos(i);d=-h-c;if(this._rightHandedSystem)g.a.RotationYawPitchRollToRef(0,0,this._adjustRoll,f),f.multiplyToRef(e,e),g.a.RotationAxisToRef(this._bendAxis,c,f),f.multiplyToRef(e,e);else{n=a._tmpVecs[5];n.copyFrom(this._bendAxis),n.x*=-1,g.a.RotationAxisToRef(n,-c,f),f.multiplyToRef(e,e)}this.poleAngle&&(g.a.RotationAxisToRef(k,this.poleAngle,f),e.multiplyToRef(f,e)),this._bone1&&(this.slerpAmount<1?(this._slerping||g.b.FromRotationMatrixToRef(this._bone1Mat,this._bone1Quat),g.b.FromRotationMatrixToRef(e,o),g.b.SlerpToRef(this._bone1Quat,o,this.slerpAmount,this._bone1Quat),d=this._bone2Ang*(1-this.slerpAmount)+d*this.slerpAmount,this._bone1.setRotationQuaternion(this._bone1Quat,R.c.WORLD,this.mesh),this._slerping=!0):(this._bone1.setRotationMatrix(e,R.c.WORLD,this.mesh),this._bone1Mat.copyFrom(e),this._slerping=!1),this._updateLinkedTransformRotation(this._bone1)),this._bone2.setAxisAngle(this._bendAxis,d,R.c.LOCAL),this._updateLinkedTransformRotation(this._bone2),this._bone2Ang=d}},a.prototype._updateLinkedTransformRotation=function(a){a._linkedTransformNode&&(a._linkedTransformNode.rotationQuaternion||(a._linkedTransformNode.rotationQuaternion=new g.b),a.getRotationQuaternionToRef(R.c.LOCAL,null,a._linkedTransformNode.rotationQuaternion))},a._tmpVecs=[g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero()],a._tmpQuat=g.b.Identity(),a._tmpMats=[g.a.Identity(),g.a.Identity()],a}(),nb=function(){function a(a,b,c,d){if(this.upAxis=g.e.Up(),this.upAxisSpace=R.c.LOCAL,this.adjustYaw=0,this.adjustPitch=0,this.adjustRoll=0,this.slerpAmount=1,this._boneQuat=g.b.Identity(),this._slerping=!1,this._firstFrameSkipped=!1,this._fowardAxis=g.e.Forward(),this.mesh=a,this.bone=b,this.target=c,d&&(d.adjustYaw&&(this.adjustYaw=d.adjustYaw),d.adjustPitch&&(this.adjustPitch=d.adjustPitch),d.adjustRoll&&(this.adjustRoll=d.adjustRoll),null!=d.maxYaw?this.maxYaw=d.maxYaw:this.maxYaw=Math.PI,null!=d.minYaw?this.minYaw=d.minYaw:this.minYaw=-Math.PI,null!=d.maxPitch?this.maxPitch=d.maxPitch:this.maxPitch=Math.PI,null!=d.minPitch?this.minPitch=d.minPitch:this.minPitch=-Math.PI,null!=d.slerpAmount&&(this.slerpAmount=d.slerpAmount),null!=d.upAxis&&(this.upAxis=d.upAxis),null!=d.upAxisSpace&&(this.upAxisSpace=d.upAxisSpace),null!=d.yawAxis||null!=d.pitchAxis)){a=R.a.Y;c=R.a.X;null!=d.yawAxis&&(a=d.yawAxis.clone()).normalize(),null!=d.pitchAxis&&(c=d.pitchAxis.clone()).normalize();d=g.e.Cross(c,a);this._transformYawPitch=g.a.Identity(),g.a.FromXYZAxesToRef(c,a,d,this._transformYawPitch),this._transformYawPitchInv=this._transformYawPitch.clone(),this._transformYawPitch.invert()}b.getParent()||this.upAxisSpace!=R.c.BONE||(this.upAxisSpace=R.c.LOCAL)}return Object.defineProperty(a.prototype,"minYaw",{get:function(){return this._minYaw},set:function(a){this._minYaw=a,this._minYawSin=Math.sin(a),this._minYawCos=Math.cos(a),null!=this._maxYaw&&(this._midYawConstraint=.5*this._getAngleDiff(this._minYaw,this._maxYaw)+this._minYaw,this._yawRange=this._maxYaw-this._minYaw)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxYaw",{get:function(){return this._maxYaw},set:function(a){this._maxYaw=a,this._maxYawSin=Math.sin(a),this._maxYawCos=Math.cos(a),null!=this._minYaw&&(this._midYawConstraint=.5*this._getAngleDiff(this._minYaw,this._maxYaw)+this._minYaw,this._yawRange=this._maxYaw-this._minYaw)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"minPitch",{get:function(){return this._minPitch},set:function(a){this._minPitch=a,this._minPitchTan=Math.tan(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxPitch",{get:function(){return this._maxPitch},set:function(a){this._maxPitch=a,this._maxPitchTan=Math.tan(a)},enumerable:!1,configurable:!0}),a.prototype.update=function(){if(this.slerpAmount<1&&!this._firstFrameSkipped)this._firstFrameSkipped=!0;else{var b=this.bone,c=a._tmpVecs[0];b.getAbsolutePositionToRef(this.mesh,c);var d=this.target,e=a._tmpMats[0],f=a._tmpMats[1],h=this.mesh;b=b.getParent();var i=a._tmpVecs[1];i.copyFrom(this.upAxis),this.upAxisSpace==R.c.BONE&&b?(this._transformYawPitch&&g.e.TransformCoordinatesToRef(i,this._transformYawPitchInv,i),b.getDirectionToRef(i,this.mesh,i)):this.upAxisSpace==R.c.LOCAL&&(h.getDirectionToRef(i,i),1==h.scaling.x&&1==h.scaling.y&&1==h.scaling.z||i.normalize());var j=!1,k=!1;if(this._maxYaw==Math.PI&&this._minYaw==-Math.PI||(j=!0),this._maxPitch==Math.PI&&this._minPitch==-Math.PI||(k=!0),j||k){var o=a._tmpMats[2],q=a._tmpMats[3];if(this.upAxisSpace==R.c.BONE&&1==i.y&&b)b.getRotationMatrixToRef(R.c.WORLD,this.mesh,o);else if(this.upAxisSpace!=R.c.LOCAL||1!=i.y||b){(r=a._tmpVecs[2]).copyFrom(this._fowardAxis),this._transformYawPitch&&g.e.TransformCoordinatesToRef(r,this._transformYawPitchInv,r),b?b.getDirectionToRef(r,this.mesh,r):h.getDirectionToRef(r,r);b=g.e.Cross(i,r);b.normalize();var r=g.e.Cross(b,i);g.a.FromXYZAxesToRef(b,i,r,o)}else o.copyFrom(h.getWorldMatrix());o.invertToRef(q);b=null;if(k){r=a._tmpVecs[3];d.subtractToRef(c,r),g.e.TransformCoordinatesToRef(r,q,r),b=Math.sqrt(r.x*r.x+r.z*r.z);h=Math.atan2(r.y,b);k=h;h>this._maxPitch?(r.y=this._maxPitchTan*b,k=this._maxPitch):hthis._maxYaw||hMath.PI?this._isAngleBetween(h,this._maxYaw,this._midYawConstraint)?(r.z=this._maxYawCos*b,r.x=this._maxYawSin*b,k=this._maxYaw):this._isAngleBetween(h,this._midYawConstraint,this._minYaw)&&(r.z=this._minYawCos*b,r.x=this._minYawSin*b,k=this._minYaw):h>this._maxYaw?(r.z=this._maxYawCos*b,r.x=this._maxYawSin*b,k=this._maxYaw):hMath.PI){j=a._tmpVecs[8];j.copyFrom(R.a.Z),this._transformYawPitch&&g.e.TransformCoordinatesToRef(j,this._transformYawPitchInv,j);var s=a._tmpMats[4];this._boneQuat.toRotationMatrix(s),this.mesh.getWorldMatrix().multiplyToRef(s,s),g.e.TransformCoordinatesToRef(j,s,j),g.e.TransformCoordinatesToRef(j,q,j);s=Math.atan2(j.x,j.z);if(this._getAngleBetween(s,h)>this._getAngleBetween(s,this._midYawConstraint)){null==b&&(b=Math.sqrt(r.x*r.x+r.z*r.z));q=this._getAngleBetween(s,this._maxYaw);this._getAngleBetween(s,this._minYaw)Math.PI?b-=2*Math.PI:b<-Math.PI&&(b+=2*Math.PI),b},a.prototype._getAngleBetween=function(a,b){return(a=(a=(a%=2*Math.PI)<0?a+2*Math.PI:a)<(b=(b%=2*Math.PI)<0?b+2*Math.PI:b)?b-a:a-b)>Math.PI&&(a=2*Math.PI-a),a},a.prototype._isAngleBetween=function(a,b,c){if(a=(a%=2*Math.PI)<0?a+2*Math.PI:a,(b=(b%=2*Math.PI)<0?b+2*Math.PI:b)<(c=(c%=2*Math.PI)<0?c+2*Math.PI:c)){if(a>b&&ac&&a>n,A=0;A<6;A++){var B=m[n][A];l&&(B=qb(B,o,o,e)),y.texImage2D(A,n,k,o,o,0,j,f,B)}x._bindTextureDirectly(y.TEXTURE_CUBE_MAP,null)}else x.updateRawCubeTexture(z,a,d,e,w);z.isReady=!0,null==b||b._removePendingData(z),i&&i()}}(a)},void 0,null==b?void 0:b.offlineProvider,!0,function(a,c){null==b||b._removePendingData(z),j&&a&&j(a.status+" "+a.statusText,c)}),z},pb.a.prototype.createRawTexture2DArray=a(!1),pb.a.prototype.createRawTexture3D=a(!0),pb.a.prototype.updateRawTexture2DArray=rb(!1),pb.a.prototype.updateRawTexture3D=rb(!0);var sb=function(a){function b(b,c,d,e,f,g,h,i,j,k){void 0===g&&(g=!0),void 0===h&&(h=!1),void 0===i&&(i=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT);f=a.call(this,null,f,!g,h,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,k)||this;return f.format=e,f._engine?(f._engine._caps.textureFloatLinearFiltering||j!==r.a.TEXTURETYPE_FLOAT||(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),f._engine._caps.textureHalfFloatLinearFiltering||j!==r.a.TEXTURETYPE_HALF_FLOAT||(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),f._texture=f._engine.createRawTexture(b,c,d,e,g,h,i,null,j,null!=k?k:0),f.wrapU=V.a.CLAMP_ADDRESSMODE,f.wrapV=V.a.CLAMP_ADDRESSMODE,f):f}return Object(l.d)(b,a),b.prototype.update=function(a){this._getEngine().updateRawTexture(this._texture,a,this._texture.format,this._texture.invertY,null,this._texture.type)},b.CreateLuminanceTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_LUMINANCE,e,f,g,h)},b.CreateLuminanceAlphaTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_LUMINANCE_ALPHA,e,f,g,h)},b.CreateAlphaTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_ALPHA,e,f,g,h)},b.CreateRGBTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGB,e,f,g,h,i)},b.CreateRGBATexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGBA,e,f,g,h,i)},b.CreateRGBAStorageTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGBA,e,f,g,h,i,r.a.TEXTURE_CREATIONFLAG_STORAGE)},b.CreateRTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=V.a.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_FLOAT),new b(a,c,d,r.a.TEXTUREFORMAT_R,e,f,g,h,i)},b.CreateRStorageTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=V.a.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_FLOAT),new b(a,c,d,r.a.TEXTUREFORMAT_R,e,f,g,h,i,r.a.TEXTURE_CREATIONFLAG_STORAGE)},b}(V.a),tb=function(){function a(a,b,c){this.name=a,this.id=b,this.bones=new Array,this.needInitialSkinMatrix=!1,this.overrideMesh=null,this._isDirty=!0,this._meshesWithPoseMatrix=new Array,this._identity=g.a.Identity(),this._ranges={},this._lastAbsoluteTransformsUpdateId=-1,this._canUseTextureForBones=!1,this._uniqueId=0,this._numBonesWithLinkedTransformNode=0,this._hasWaitingData=null,this._waitingOverrideMeshId=null,this._parentContainer=null,this.doNotSerialize=!1,this._useTextureToStoreBoneMatrices=!0,this._animationPropertiesOverride=null,this.onBeforeComputeObservable=new f.c,this.bones=[],this._scene=c||C.a.LastCreatedScene,this._uniqueId=this._scene.getUniqueId(),this._scene.addSkeleton(this),this._isDirty=!0;a=this._scene.getEngine().getCaps();this._canUseTextureForBones=a.textureFloat&&a.maxVertexTextureImageUnits>0}return Object.defineProperty(a.prototype,"useTextureToStoreBoneMatrices",{get:function(){return this._useTextureToStoreBoneMatrices},set:function(a){this._useTextureToStoreBoneMatrices=a,this._markAsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationPropertiesOverride",{get:function(){return this._animationPropertiesOverride?this._animationPropertiesOverride:this._scene.animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUsingTextureForMatrices",{get:function(){return this.useTextureToStoreBoneMatrices&&this._canUseTextureForBones},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"Skeleton"},a.prototype.getChildren=function(){return this.bones.filter(function(a){return!a.getParent()})},a.prototype.getTransformMatrices=function(a){return this.needInitialSkinMatrix&&a._bonesTransformMatrices?a._bonesTransformMatrices:(this._transformMatrices||this.prepare(),this._transformMatrices)},a.prototype.getTransformMatrixTexture=function(a){return this.needInitialSkinMatrix&&a._transformMatrixTexture?a._transformMatrixTexture:this._transformMatrixTexture},a.prototype.getScene=function(){return this._scene},a.prototype.toString=function(a){var b="Name: "+this.name+", nBones: "+this.bones.length;if(b+=", nAnimationRanges: "+(this._ranges?Object.keys(this._ranges).length:"none"),a){b+=", Ranges: {";a=!0;for(var c in this._ranges)a&&(b+=", ",a=!1),b+=c;b+="}"}return b},a.prototype.getBoneIndexByName=function(a){for(var b=0,c=this.bones.length;b-1&&this._meshesWithPoseMatrix.splice(a,1)},a.prototype._computeTransformMatrices=function(a,b){this.onBeforeComputeObservable.notifyObservers(this);for(var c=0;c0)for(var a=0,b=this.bones;a-1&&this._parentContainer.skeletons.splice(a,1),this._parentContainer=null}this._transformMatrixTexture&&(this._transformMatrixTexture.dispose(),this._transformMatrixTexture=null)},a.prototype.serialize=function(){var a,b={};b.name=this.name,b.id=this.id,this.dimensionsAtRest&&(b.dimensionsAtRest=this.dimensionsAtRest.asArray()),b.bones=[],b.needInitialSkinMatrix=this.needInitialSkinMatrix,b.overrideMeshId=null===(a=this.overrideMesh)||void 0===a?void 0:a.id;for(a=0;a0&&(d.animation=c.animations[0].serialize()),b.ranges=[],this._ranges){c=this._ranges[d];if(c){var e={};e.name=d,e.from=c.from,e.to=c.to,b.ranges.push(e)}}}return b},a.Parse=function(b,c){var d;c=new a(b.name,b.id,c);for(b.dimensionsAtRest&&(c.dimensionsAtRest=g.e.FromArray(b.dimensionsAtRest)),c.needInitialSkinMatrix=b.needInitialSkinMatrix,b.overrideMeshId&&(c._hasWaitingData=!0,c._waitingOverrideMeshId=b.overrideMeshId),d=0;d-1&&(h=c.bones[e.parentBoneIndex]);var i=e.rest?g.a.FromArray(e.rest):null;h=new ja(e.name,c,h,g.a.FromArray(e.matrix),i,null,f);void 0!==e.id&&null!==e.id&&(h.id=e.id),e.length&&(h.length=e.length),e.metadata&&(h.metadata=e.metadata),e.animation&&h.animations.push(O.Parse(e.animation)),void 0!==e.linkedTransformNodeId&&null!==e.linkedTransformNodeId&&(c._hasWaitingData=!0,h._waitingTransformNodeId=e.linkedTransformNodeId)}if(b.ranges)for(d=0;d0&&(a=this._meshesWithPoseMatrix[0].getPoseMatrix()),a},a.prototype.sortBones=function(){for(var a=new Array,b=new Array(this.bones.length),c=0;c=2&&(this._leftStick={x:this.browserGamepad.axes[this._leftStickAxisX],y:this.browserGamepad.axes[this._leftStickAxisY]}),this.browserGamepad.axes.length>=4&&(this._rightStick={x:this.browserGamepad.axes[this._rightStickAxisX],y:this.browserGamepad.axes[this._rightStickAxisY]})}return Object.defineProperty(a.prototype,"isConnected",{get:function(){return this._isConnected},enumerable:!1,configurable:!0}),a.prototype.onleftstickchanged=function(a){this._onleftstickchanged=a},a.prototype.onrightstickchanged=function(a){this._onrightstickchanged=a},Object.defineProperty(a.prototype,"leftStick",{get:function(){return this._leftStick},set:function(a){!this._onleftstickchanged||this._leftStick.x===a.x&&this._leftStick.y===a.y||this._onleftstickchanged(a),this._leftStick=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rightStick",{get:function(){return this._rightStick},set:function(a){!this._onrightstickchanged||this._rightStick.x===a.x&&this._rightStick.y===a.y||this._onrightstickchanged(a),this._rightStick=a},enumerable:!1,configurable:!0}),a.prototype.update=function(){this._leftStick&&(this.leftStick={x:this.browserGamepad.axes[this._leftStickAxisX],y:this.browserGamepad.axes[this._leftStickAxisY]},this._invertLeftStickY&&(this.leftStick.y*=-1)),this._rightStick&&(this.rightStick={x:this.browserGamepad.axes[this._rightStickAxisX],y:this.browserGamepad.axes[this._rightStickAxisY]})},a.prototype.dispose=function(){},a.GAMEPAD=0,a.GENERIC=1,a.XBOX=2,a.POSE_ENABLED=3,a.DUALSHOCK=4,a}(),Db=function(a){function b(b,c,d){b=a.call(this,b,c,d)||this;return b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.type=Cb.GENERIC,b._buttons=new Array(d.buttons.length),b}return Object(l.d)(b,a),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype.update=function(){a.prototype.update.call(this);for(var b=0;b.005&&(a.inertialAlphaOffset+=c)}if(0!=b.y){c=b.y/this.gamepadRotationSensibility*this._yAxisScale;0!=c&&Math.abs(c)>.005&&(a.inertialBetaOffset+=c)}}b=this.gamepad.leftStick;if(b&&0!=b.y){a=b.y/this.gamepadMoveSensibility;0!=a&&Math.abs(a)>.005&&(this.camera.inertialRadiusOffset-=a)}}},a.prototype.getClassName=function(){return"ArcRotateCameraGamepadInput"},a.prototype.getSimpleName=function(){return"gamepad"},Object(l.c)([Object(J.d)()],a.prototype,"gamepadRotationSensibility",void 0),Object(l.c)([Object(J.d)()],a.prototype,"gamepadMoveSensibility",void 0),a}();zb.ArcRotateCameraGamepadInput=Eb;var Fb=c(73),Gb=function(){function a(){this.keysUp=[38],this.keysDown=[40],this.keysLeft=[37],this.keysRight=[39],this.keysReset=[220],this.panningSensibility=50,this.zoomingSensibility=25,this.useAltToZoom=!0,this.angularSpeed=.01,this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;d.metaKey||(c.type===Fb.a.KEYDOWN?(b._ctrlPressed=d.ctrlKey,b._altPressed=d.altKey,(-1!==b.keysUp.indexOf(d.keyCode)||-1!==b.keysDown.indexOf(d.keyCode)||-1!==b.keysLeft.indexOf(d.keyCode)||-1!==b.keysRight.indexOf(d.keyCode)||-1!==b.keysReset.indexOf(d.keyCode))&&(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),d.preventDefault&&(a||d.preventDefault()))):-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysReset.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),d.preventDefault&&(a||d.preventDefault())))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b0?b/(1+this.wheelDeltaPercentage):b*(1+this.wheelDeltaPercentage)},a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this._wheel=function(c,d){if(c.type===Ta.a.POINTERWHEEL){d=c.event;c=d;var e=0,f=d.deltaMode===wb.b.DOM_DELTA_LINE?40:1;if(e=void 0!==d.deltaY?-d.deltaY*f:void 0!==d.wheelDeltaY?-d.wheelDeltaY*f:c.wheelDelta,b.customComputeDeltaFromMouseWheel)f=b.customComputeDeltaFromMouseWheel(e,b,d);else if(b.wheelDeltaPercentage){if((f=b.computeDeltaFromMouseWheelLegacyEvent(e,b.camera.radius))>0){for(var c=b.camera.radius,g=b.camera.inertialRadiusOffset+f,h=0;h<20&&Math.abs(g)>.001;h++)c-=g,g*=b.camera.inertia;c=I.a.Clamp(c,0,Number.MAX_VALUE),f=b.computeDeltaFromMouseWheelLegacyEvent(e,c)}}else f=e/(40*b.wheelPrecision);f&&(b.zoomToMouseLocation&&b._hitPlane?b._zoomToMouse(f):b.camera.inertialRadiusOffset+=f),d.preventDefault&&(a||d.preventDefault())}},this._observer=this.camera.getScene().onPointerObservable.add(this._wheel,Ta.a.POINTERWHEEL),this.zoomToMouseLocation&&this._inertialPanning.setAll(0)},a.prototype.detachControl=function(a){this._observer&&(this.camera.getScene().onPointerObservable.remove(this._observer),this._observer=null,this._wheel=null)},a.prototype.checkInputs=function(){if(this.zoomToMouseLocation){var a=this.camera;0+a.inertialAlphaOffset+a.inertialBetaOffset+a.inertialRadiusOffset&&(this._updateHitPlane(),a.target.addInPlace(this._inertialPanning),this._inertialPanning.scaleInPlace(a.inertia),this._zeroIfClose(this._inertialPanning))}},a.prototype.getClassName=function(){return"ArcRotateCameraMouseWheelInput"},a.prototype.getSimpleName=function(){return"mousewheel"},a.prototype._updateHitPlane=function(){var a=this.camera;a=a.target.subtract(a.position);this._hitPlane=Hb.a.FromPositionAndNormal(g.e.Zero(),a)},a.prototype._getPosition=function(){var a,b=this.camera,c=b.getScene();c=c.createPickingRay(c.pointerX,c.pointerY,g.a.Identity(),b,!1);b=0;return this._hitPlane&&(b=null!==(a=c.intersectsPlane(this._hitPlane))&&void 0!==a?a:0),c.origin.addInPlace(c.direction.scaleInPlace(b))},a.prototype._zoomToMouse=function(a){var b,c=this.camera,d=1-c.inertia;if(c.lowerRadiusLimit){b=null!==(b=c.lowerRadiusLimit)&&void 0!==b?b:0;c.radius-(c.inertialRadiusOffset+a)/db&&(a=(c.radius-b)*d-c.inertialRadiusOffset)}b=a/d/c.radius;b=this._getPosition().subtract(c.target).scale(b);b.scaleInPlace(d),this._inertialPanning.addInPlace(b),c.inertialRadiusOffset+=a},a.prototype._zeroIfClose=function(a){Math.abs(a.x)this.camera.pinchToPanMaxDistance?(this._computePinchZoom(c,d),this._isPinching=!0):this._computeMultiTouchPanning(e,f)):this.multiTouchPanning?this._computeMultiTouchPanning(e,f):this.pinchZoom&&this._computePinchZoom(c,d))},b.prototype.onButtonDown=function(a){this._isPanClick=a.button===this.camera._panningMouseButton},b.prototype.onButtonUp=function(a){this._twoFingerActivityCount=0,this._isPinching=!1},b.prototype.onLostFocus=function(){this._isPanClick=!1,this._twoFingerActivityCount=0,this._isPinching=!1},b.MinimumRadiusForPinch=.001,Object(l.c)([Object(J.d)()],b.prototype,"buttons",void 0),Object(l.c)([Object(J.d)()],b.prototype,"angularSensibilityX",void 0),Object(l.c)([Object(J.d)()],b.prototype,"angularSensibilityY",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchPrecision",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchDeltaPercentage",void 0),Object(l.c)([Object(J.d)()],b.prototype,"useNaturalPinchZoom",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchZoom",void 0),Object(l.c)([Object(J.d)()],b.prototype,"panningSensibility",void 0),Object(l.c)([Object(J.d)()],b.prototype,"multiTouchPanning",void 0),Object(l.c)([Object(J.d)()],b.prototype,"multiTouchPanAndZoom",void 0),b}(yb);zb.ArcRotateCameraPointersInput=Jb;var Kb=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addMouseWheel=function(){return this.add(new Ib),this},b.prototype.addPointers=function(){return this.add(new Jb),this},b.prototype.addKeyboard=function(){return this.add(new Gb),this},b}(Ab);Kb.prototype.addVRDeviceOrientation=function(){return this.add(new Lb),this};var Lb=function(){function a(){this.alphaCorrection=1,this.gammaCorrection=1,this._alpha=0,this._gamma=0,this._dirty=!1,this._deviceOrientationHandler=this._onOrientationEvent.bind(this)}return a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this.camera.attachControl(a);var c=this.camera.getScene().getEngine().getHostWindow();c&&("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"===a?c.addEventListener("deviceorientation",b._deviceOrientationHandler):U.b.Warn("Permission not granted.")})["catch"](function(a){U.b.Error(a)}):c.addEventListener("deviceorientation",this._deviceOrientationHandler))},a.prototype._onOrientationEvent=function(a){null!==a.alpha&&(this._alpha=(0|+a.alpha)*this.alphaCorrection),null!==a.gamma&&(this._gamma=(0|+a.gamma)*this.gammaCorrection),this._dirty=!0},a.prototype.checkInputs=function(){this._dirty&&(this._dirty=!1,this._gamma<0&&(this._gamma=180+this._gamma),this.camera.alpha=-this._alpha/180*Math.PI%Math.PI*2,this.camera.beta=this._gamma/180*Math.PI)},a.prototype.detachControl=function(a){window.removeEventListener("deviceorientation",this._deviceOrientationHandler)},a.prototype.getClassName=function(){return"ArcRotateCameraVRDeviceOrientationInput"},a.prototype.getSimpleName=function(){return"VRDeviceOrientation"},a}();zb.ArcRotateCameraVRDeviceOrientationInput=Lb;var Mb=function(){function a(){this.keysForward=[87],this.keysBackward=[83],this.keysUp=[69],this.keysDown=[81],this.keysRight=[68],this.keysLeft=[65],this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;c.type===Fb.a.KEYDOWN?-1===b.keysForward.indexOf(d.keyCode)&&-1===b.keysBackward.indexOf(d.keyCode)&&-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)||(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),a||d.preventDefault()):-1===b.keysForward.indexOf(d.keyCode)&&-1===b.keysBackward.indexOf(d.keyCode)&&-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),a||d.preventDefault())}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.getClassName=function(){return"FlyCameraKeyboardInput"},a.prototype._onLostFocus=function(a){this._keys=[]},a.prototype.getSimpleName=function(){return"keyboard"},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b=0&&b._keys.splice(c,1),d.preventDefault&&(a||d.preventDefault())))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){var a=this;this._onKeyboardObserver&&this._keys.forEach(function(b){-1!==a.keysHeightOffsetIncr.indexOf(b)&&a._modifierHeightOffset()?a.camera.heightOffset+=a.heightSensibility:-1!==a.keysHeightOffsetDecr.indexOf(b)&&a._modifierHeightOffset()?a.camera.heightOffset-=a.heightSensibility:-1!==a.keysRotationOffsetIncr.indexOf(b)&&a._modifierRotationOffset()?(a.camera.rotationOffset+=a.rotationSensibility,a.camera.rotationOffset%=360):-1!==a.keysRotationOffsetDecr.indexOf(b)&&a._modifierRotationOffset()?(a.camera.rotationOffset-=a.rotationSensibility,a.camera.rotationOffset%=360):-1!==a.keysRadiusIncr.indexOf(b)&&a._modifierRadius()?a.camera.radius+=a.radiusSensibility:-1!==a.keysRadiusDecr.indexOf(b)&&a._modifierRadius()&&(a.camera.radius-=a.radiusSensibility)})},a.prototype.getClassName=function(){return"FollowCameraKeyboardMoveInput"},a.prototype.getSimpleName=function(){return"keyboard"},a.prototype._modifierHeightOffset=function(){return this.keysHeightOffsetModifierAlt===this._altPressed&&this.keysHeightOffsetModifierCtrl===this._ctrlPressed&&this.keysHeightOffsetModifierShift===this._shiftPressed},a.prototype._modifierRotationOffset=function(){return this.keysRotationOffsetModifierAlt===this._altPressed&&this.keysRotationOffsetModifierCtrl===this._ctrlPressed&&this.keysRotationOffsetModifierShift===this._shiftPressed},a.prototype._modifierRadius=function(){return this.keysRadiusModifierAlt===this._altPressed&&this.keysRadiusModifierCtrl===this._ctrlPressed&&this.keysRadiusModifierShift===this._shiftPressed},Object(l.c)([Object(J.d)()],a.prototype,"keysHeightOffsetIncr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysHeightOffsetDecr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysHeightOffsetModifierAlt",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysHeightOffsetModifierCtrl",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysHeightOffsetModifierShift",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRotationOffsetIncr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRotationOffsetDecr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRotationOffsetModifierAlt",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRotationOffsetModifierCtrl",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRotationOffsetModifierShift",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRadiusIncr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRadiusDecr",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRadiusModifierAlt",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRadiusModifierCtrl",void 0),Object(l.c)([Object(J.d)()],a.prototype,"keysRadiusModifierShift",void 0),Object(l.c)([Object(J.d)()],a.prototype,"heightSensibility",void 0),Object(l.c)([Object(J.d)()],a.prototype,"rotationSensibility",void 0),Object(l.c)([Object(J.d)()],a.prototype,"radiusSensibility",void 0),a}();zb.FollowCameraKeyboardMoveInput=Ob;var Pb=function(){function a(){this.axisControlRadius=!0,this.axisControlHeight=!1,this.axisControlRotation=!1,this.wheelPrecision=3,this.wheelDeltaPercentage=0}return a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this._wheel=function(c,d){if(c.type===Ta.a.POINTERWHEEL){d=c.event;c=0;var e=Math.max(-1,Math.min(1,d.deltaY||d.wheelDelta||-d.detail));b.wheelDeltaPercentage?(console.assert(b.axisControlRadius+b.axisControlHeight+b.axisControlRotation<=1,"wheelDeltaPercentage only usable when mouse wheel controls ONE axis. Currently enabled: axisControlRadius: "+b.axisControlRadius+", axisControlHeightOffset: "+b.axisControlHeight+", axisControlRotationOffset: "+b.axisControlRotation),b.axisControlRadius?c=.01*e*b.wheelDeltaPercentage*b.camera.radius:b.axisControlHeight?c=.01*e*b.wheelDeltaPercentage*b.camera.heightOffset:b.axisControlRotation&&(c=.01*e*b.wheelDeltaPercentage*b.camera.rotationOffset)):c=e*b.wheelPrecision,c&&(b.axisControlRadius?b.camera.radius+=c:b.axisControlHeight?b.camera.heightOffset-=c:b.axisControlRotation&&(b.camera.rotationOffset-=c)),d.preventDefault&&(a||d.preventDefault())}},this._observer=this.camera.getScene().onPointerObservable.add(this._wheel,Ta.a.POINTERWHEEL)},a.prototype.detachControl=function(a){this._observer&&(this.camera.getScene().onPointerObservable.remove(this._observer),this._observer=null,this._wheel=null)},a.prototype.getClassName=function(){return"ArcRotateCameraMouseWheelInput"},a.prototype.getSimpleName=function(){return"mousewheel"},Object(l.c)([Object(J.d)()],a.prototype,"axisControlRadius",void 0),Object(l.c)([Object(J.d)()],a.prototype,"axisControlHeight",void 0),Object(l.c)([Object(J.d)()],a.prototype,"axisControlRotation",void 0),Object(l.c)([Object(J.d)()],a.prototype,"wheelPrecision",void 0),Object(l.c)([Object(J.d)()],a.prototype,"wheelDeltaPercentage",void 0),a}();zb.FollowCameraMouseWheelInput=Pb;var Qb=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.angularSensibilityX=1,b.angularSensibilityY=1,b.pinchPrecision=1e4,b.pinchDeltaPercentage=0,b.axisXControlRadius=!1,b.axisXControlHeight=!1,b.axisXControlRotation=!0,b.axisYControlRadius=!1,b.axisYControlHeight=!0,b.axisYControlRotation=!1,b.axisPinchControlRadius=!0,b.axisPinchControlHeight=!1,b.axisPinchControlRotation=!1,b.warningEnable=!0,b._warningCounter=0,b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FollowCameraPointersInput"},b.prototype.onTouch=function(a,b,c){this._warning(),this.axisXControlRotation?this.camera.rotationOffset+=b/this.angularSensibilityX:this.axisYControlRotation&&(this.camera.rotationOffset+=c/this.angularSensibilityX),this.axisXControlHeight?this.camera.heightOffset+=b/this.angularSensibilityY:this.axisYControlHeight&&(this.camera.heightOffset+=c/this.angularSensibilityY),this.axisXControlRadius?this.camera.radius-=b/this.angularSensibilityY:this.axisYControlRadius&&(this.camera.radius-=c/this.angularSensibilityY)},b.prototype.onMultiTouch=function(a,b,c,d,e,f){if(!(0===c&&null===e||0===d&&null===f)){a=(d-c)/(this.pinchPrecision*(this.angularSensibilityX+this.angularSensibilityY)/2);this.pinchDeltaPercentage?(a*=.01*this.pinchDeltaPercentage,this.axisPinchControlRotation&&(this.camera.rotationOffset+=a*this.camera.rotationOffset),this.axisPinchControlHeight&&(this.camera.heightOffset+=a*this.camera.heightOffset),this.axisPinchControlRadius&&(this.camera.radius-=a*this.camera.radius)):(this.axisPinchControlRotation&&(this.camera.rotationOffset+=a),this.axisPinchControlHeight&&(this.camera.heightOffset+=a),this.axisPinchControlRadius&&(this.camera.radius-=a))}},b.prototype._warning=function(){if(this.warningEnable&&this._warningCounter++%100==0){var a="It probably only makes sense to control ONE camera property with each pointer axis. Set "warningEnable = false" if you are sure. Currently enabled: ";console.assert(this.axisXControlRotation+this.axisXControlHeight+this.axisXControlRadius<=1,a+"axisXControlRotation: "+this.axisXControlRotation+", axisXControlHeight: "+this.axisXControlHeight+", axisXControlRadius: "+this.axisXControlRadius),console.assert(this.axisYControlRotation+this.axisYControlHeight+this.axisYControlRadius<=1,a+"axisYControlRotation: "+this.axisYControlRotation+", axisYControlHeight: "+this.axisYControlHeight+", axisYControlRadius: "+this.axisYControlRadius),console.assert(this.axisPinchControlRotation+this.axisPinchControlHeight+this.axisPinchControlRadius<=1,a+"axisPinchControlRotation: "+this.axisPinchControlRotation+", axisPinchControlHeight: "+this.axisPinchControlHeight+", axisPinchControlRadius: "+this.axisPinchControlRadius)}},Object(l.c)([Object(J.d)()],b.prototype,"angularSensibilityX",void 0),Object(l.c)([Object(J.d)()],b.prototype,"angularSensibilityY",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchPrecision",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchDeltaPercentage",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisXControlRadius",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisXControlHeight",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisXControlRotation",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisYControlRadius",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisYControlHeight",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisYControlRotation",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisPinchControlRadius",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisPinchControlHeight",void 0),Object(l.c)([Object(J.d)()],b.prototype,"axisPinchControlRotation",void 0),b}(yb);zb.FollowCameraPointersInput=Qb;var Rb=function(){function a(){this.keysUp=[38],this.keysUpward=[33],this.keysDown=[40],this.keysDownward=[34],this.keysLeft=[37],this.keysRight=[39],this.rotationSpeed=.5,this.keysRotateLeft=[],this.keysRotateRight=[],this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=U.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;d.metaKey||(c.type===Fb.a.KEYDOWN?-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysUpward.indexOf(d.keyCode)&&-1===b.keysDownward.indexOf(d.keyCode)&&-1===b.keysRotateLeft.indexOf(d.keyCode)&&-1===b.keysRotateRight.indexOf(d.keyCode)||(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),a||d.preventDefault()):-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysUpward.indexOf(d.keyCode)&&-1===b.keysDownward.indexOf(d.keyCode)&&-1===b.keysRotateLeft.indexOf(d.keyCode)&&-1===b.keysRotateRight.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),a||d.preventDefault()))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b1)a.cameraRotation.x=-this._offsetY/this.touchAngularSensibility;else{var b=a._computeLocalCameraSpeed();b=new g.e(0,0,b*this._offsetY/this.touchMoveSensibility);g.a.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,0,a._cameraRotationMatrix),a.cameraDirection.addInPlace(g.e.TransformCoordinates(b,a._cameraRotationMatrix))}}},a.prototype.getClassName=function(){return"FreeCameraTouchInput"},a.prototype.getSimpleName=function(){return"touch"},Object(l.c)([Object(J.d)()],a.prototype,"touchAngularSensibility",void 0),Object(l.c)([Object(J.d)()],a.prototype,"touchMoveSensibility",void 0),a}();zb.FreeCameraTouchInput=Vb;var Wb=function(a){function b(b){b=a.call(this,b)||this;return b._mouseInput=null,b._mouseWheelInput=null,b}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Rb),this},b.prototype.addMouse=function(a){return void 0===a&&(a=!0),this._mouseInput||(this._mouseInput=new Tb(a),this.add(this._mouseInput)),this},b.prototype.removeMouse=function(){return this._mouseInput&&this.remove(this._mouseInput),this},b.prototype.addMouseWheel=function(){return this._mouseWheelInput||(this._mouseWheelInput=new Ub,this.add(this._mouseWheelInput)),this},b.prototype.removeMouseWheel=function(){return this._mouseWheelInput&&this.remove(this._mouseWheelInput),this},b.prototype.addTouch=function(){return this.add(new Vb),this},b.prototype.clear=function(){a.prototype.clear.call(this),this._mouseInput=null},b}(Ab);Wb.prototype.addDeviceOrientation=function(){return this._deviceOrientationInput||(this._deviceOrientationInput=new Xb,this.add(this._deviceOrientationInput)),this};var Xb=function(){function a(){var a=this;this._screenOrientationAngle=0,this._screenQuaternion=new g.b,this._alpha=0,this._beta=0,this._gamma=0,this._onDeviceOrientationChangedObservable=new f.c,this._orientationChanged=function(){a._screenOrientationAngle=void 0!==window.orientation?+window.orientation:window.screen.orientation&&window.screen.orientation.angle?window.screen.orientation.angle:0,a._screenOrientationAngle=-U.b.ToRadians(a._screenOrientationAngle/2),a._screenQuaternion.copyFromFloats(0,Math.sin(a._screenOrientationAngle),0,Math.cos(a._screenOrientationAngle))},this._deviceOrientation=function(b){a._alpha=null!==b.alpha?b.alpha:0,a._beta=null!==b.beta?b.beta:0,a._gamma=null!==b.gamma?b.gamma:0,null!==b.alpha&&a._onDeviceOrientationChangedObservable.notifyObservers()},this._constantTranform=new g.b(-Math.sqrt(.5),0,0,Math.sqrt(.5)),this._orientationChanged()}return a.WaitForOrientationChangeAsync=function(a){return new Promise(function(b,c){var d=!1,e=function(){window.removeEventListener("deviceorientation",e),d=!0,b()};a&&setTimeout(function(){d||(window.removeEventListener("deviceorientation",e),c("WaitForOrientationChangeAsync timed out"))},a),"undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"==a?window.addEventListener("deviceorientation",e):U.b.Warn("Permission not granted.")})["catch"](function(a){U.b.Error(a)}):window.addEventListener("deviceorientation",e)})},Object.defineProperty(a.prototype,"camera",{get:function(){return this._camera},set:function(a){var b=this;this._camera=a,null==this._camera||this._camera.rotationQuaternion||(this._camera.rotationQuaternion=new g.b),this._camera&&this._camera.onDisposeObservable.add(function(){b._onDeviceOrientationChangedObservable.clear()})},enumerable:!1,configurable:!0}),a.prototype.attachControl=function(){var a=this,b=this.camera.getScene().getEngine().getHostWindow();if(b){var c=function(){b.addEventListener("orientationchange",a._orientationChanged),b.addEventListener("deviceorientation",a._deviceOrientation),a._orientationChanged()};"undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"===a?c():U.b.Warn("Permission not granted.")})["catch"](function(a){U.b.Error(a)}):c()}},a.prototype.detachControl=function(a){window.removeEventListener("orientationchange",this._orientationChanged),window.removeEventListener("deviceorientation",this._deviceOrientation),this._alpha=0},a.prototype.checkInputs=function(){this._alpha&&(g.b.RotationYawPitchRollToRef(U.b.ToRadians(this._alpha),U.b.ToRadians(this._beta),-U.b.ToRadians(this._gamma),this.camera.rotationQuaternion),this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion),this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform),this._camera.rotationQuaternion.z*=-1,this._camera.rotationQuaternion.w*=-1)},a.prototype.getClassName=function(){return"FreeCameraDeviceOrientationInput"},a.prototype.getSimpleName=function(){return"deviceOrientation"},a}();zb.FreeCameraDeviceOrientationInput=Xb;var Yb=function(){function a(){this.gamepadAngularSensibility=200,this.gamepadMoveSensibility=40,this.deadzoneDelta=.1,this._yAxisScale=1,this._cameraTransform=g.a.Identity(),this._deltaTransform=g.e.Zero(),this._vector3=g.e.Zero(),this._vector2=g.d.Zero()}return Object.defineProperty(a.prototype,"invertYAxis",{get:function(){return 1!==this._yAxisScale},set:function(a){this._yAxisScale=a?-1:1},enumerable:!1,configurable:!0}),a.prototype.attachControl=function(){var a=this,b=this.camera.getScene().gamepadManager;this._onGamepadConnectedObserver=b.onGamepadConnectedObservable.add(function(b){b.type!==Cb.POSE_ENABLED&&(a.gamepad&&b.type!==Cb.XBOX||(a.gamepad=b))}),this._onGamepadDisconnectedObserver=b.onGamepadDisconnectedObservable.add(function(b){a.gamepad===b&&(a.gamepad=null)}),this.gamepad=b.getGamepadByType(Cb.XBOX),!this.gamepad&&b.gamepads.length&&(this.gamepad=b.gamepads[0])},a.prototype.detachControl=function(a){this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver),this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver),this.gamepad=null},a.prototype.checkInputs=function(){if(this.gamepad&&this.gamepad.leftStick){var a=this.camera,b=this.gamepad.leftStick;0!==this.gamepadMoveSensibility&&(b.x=Math.abs(b.x)>this.deadzoneDelta?b.x/this.gamepadMoveSensibility:0,b.y=Math.abs(b.y)>this.deadzoneDelta?b.y/this.gamepadMoveSensibility:0);var c=this.gamepad.rightStick;c&&0!==this.gamepadAngularSensibility?(c.x=Math.abs(c.x)>this.deadzoneDelta?c.x/this.gamepadAngularSensibility:0,c.y=(Math.abs(c.y)>this.deadzoneDelta?c.y/this.gamepadAngularSensibility:0)*this._yAxisScale):c={x:0,y:0},a.rotationQuaternion?a.rotationQuaternion.toRotationMatrix(this._cameraTransform):g.a.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,0,this._cameraTransform);var d=50*a._computeLocalCameraSpeed();this._vector3.copyFromFloats(b.x*d,0,-b.y*d),g.e.TransformCoordinatesToRef(this._vector3,this._cameraTransform,this._deltaTransform),a.cameraDirection.addInPlace(this._deltaTransform),this._vector2.copyFromFloats(c.y,c.x),a.cameraRotation.addInPlace(this._vector2)}},a.prototype.getClassName=function(){return"FreeCameraGamepadInput"},a.prototype.getSimpleName=function(){return"gamepad"},Object(l.c)([Object(J.d)()],a.prototype,"gamepadAngularSensibility",void 0),Object(l.c)([Object(J.d)()],a.prototype,"gamepadMoveSensibility",void 0),a}();zb.FreeCameraGamepadInput=Yb;var Zb,$b=c(125);!function(a){a[a.X=0]="X",a[a.Y=1]="Y",a[a.Z=2]="Z"}(Zb||(Zb={}));var ac=function(){function a(b,c){var d=this;c=Object(l.a)(Object(l.a)({},a._GetDefaultOptions()),c);if(this._leftJoystick=!!b,a._globalJoystickIndex++,this._axisTargetedByLeftAndRight=Zb.X,this._axisTargetedByUpAndDown=Zb.Y,this.reverseLeftRight=!1,this.reverseUpDown=!1,this._touches=new $b.a,this.deltaPosition=g.e.Zero(),this._joystickSensibility=25,this._inversedSensibility=1/(this._joystickSensibility/1e3),this._onResize=function(b){a.vjCanvasWidth=window.innerWidth,a.vjCanvasHeight=window.innerHeight,a.Canvas&&(a.Canvas.width=a.vjCanvasWidth,a.Canvas.height=a.vjCanvasHeight),a.halfWidth=a.vjCanvasWidth/2},!a.Canvas){window.addEventListener("resize",this._onResize,!1),a.Canvas=document.createElement("canvas"),a.vjCanvasWidth=window.innerWidth,a.vjCanvasHeight=window.innerHeight,a.Canvas.width=window.innerWidth,a.Canvas.height=window.innerHeight,a.Canvas.style.width="100%",a.Canvas.style.height="100%",a.Canvas.style.position="absolute",a.Canvas.style.backgroundColor="transparent",a.Canvas.style.top="0px",a.Canvas.style.left="0px",a.Canvas.style.zIndex="5",a.Canvas.style.msTouchAction="none",a.Canvas.style.touchAction="none",a.Canvas.setAttribute("touch-action","none");b=a.Canvas.getContext("2d");if(!b)throw new Error("Unable to create canvas for virtual joystick");a.vjCanvasContext=b,a.vjCanvasContext.strokeStyle="#ffffff",a.vjCanvasContext.lineWidth=2,document.body.appendChild(a.Canvas)}a.halfWidth=a.Canvas.width/2,this.pressed=!1,this.limitToContainer=c.limitToContainer,this._joystickColor=c.color,this.containerSize=c.containerSize,this.puckSize=c.puckSize,c.position&&this.setPosition(c.position.x,c.position.y),c.puckImage&&this.setPuckImage(c.puckImage),c.containerImage&&this.setContainerImage(c.containerImage),c.alwaysVisible&&a._alwaysVisibleSticks++,this.alwaysVisible=c.alwaysVisible,this._joystickPointerId=-1,this._joystickPointerPos=new g.d(0,0),this._joystickPreviousPointerPos=new g.d(0,0),this._joystickPointerStartPos=new g.d(0,0),this._deltaJoystickVector=new g.d(0,0),this._onPointerDownHandlerRef=function(a){d._onPointerDown(a)},this._onPointerMoveHandlerRef=function(a){d._onPointerMove(a)},this._onPointerUpHandlerRef=function(a){d._onPointerUp(a)},a.Canvas.addEventListener("pointerdown",this._onPointerDownHandlerRef,!1),a.Canvas.addEventListener("pointermove",this._onPointerMoveHandlerRef,!1),a.Canvas.addEventListener("pointerup",this._onPointerUpHandlerRef,!1),a.Canvas.addEventListener("pointerout",this._onPointerUpHandlerRef,!1),a.Canvas.addEventListener("contextmenu",function(a){a.preventDefault()},!1),requestAnimationFrame(function(){d._drawVirtualJoystick()})}return a._GetDefaultOptions=function(){return{puckSize:40,containerSize:60,color:"cyan",puckImage:void 0,containerImage:void 0,position:void 0,alwaysVisible:!1,limitToContainer:!1}},a.prototype.setJoystickSensibility=function(a){this._joystickSensibility=a,this._inversedSensibility=1/(this._joystickSensibility/1e3)},a.prototype._onPointerDown=function(b){b.preventDefault(),(!0===this._leftJoystick?b.clientXa.halfWidth)&&this._joystickPointerId<0?(this._joystickPointerId=b.pointerId,this._joystickPosition?(this._joystickPointerStartPos=this._joystickPosition.clone(),this._joystickPointerPos=this._joystickPosition.clone(),this._joystickPreviousPointerPos=this._joystickPosition.clone(),this._onPointerMove(b)):(this._joystickPointerStartPos.x=b.clientX,this._joystickPointerStartPos.y=b.clientY,this._joystickPointerPos=this._joystickPointerStartPos.clone(),this._joystickPreviousPointerPos=this._joystickPointerStartPos.clone()),this._deltaJoystickVector.x=0,this._deltaJoystickVector.y=0,this.pressed=!0,this._touches.add(b.pointerId.toString(),b)):a._globalJoystickIndex<2&&this._action&&(this._action(),this._touches.add(b.pointerId.toString(),{x:b.clientX,y:b.clientY,prevX:b.clientX,prevY:b.clientY}))},a.prototype._onPointerMove=function(b){if(this._joystickPointerId==b.pointerId){if(this.limitToContainer){var c=new g.d(b.clientX-this._joystickPointerStartPos.x,b.clientY-this._joystickPointerStartPos.y),d=c.length();d>this.containerSize&&c.scaleInPlace(this.containerSize/d),this._joystickPointerPos.x=this._joystickPointerStartPos.x+c.x,this._joystickPointerPos.y=this._joystickPointerStartPos.y+c.y}else this._joystickPointerPos.x=b.clientX,this._joystickPointerPos.y=b.clientY;this._deltaJoystickVector=this._joystickPointerPos.clone(),this._deltaJoystickVector=this._deltaJoystickVector.subtract(this._joystickPointerStartPos),0=0?this.rotation.y=-Math.atan(a.z/a.x)+Math.PI/2:this.rotation.y=-Math.atan(a.z/a.x)-Math.PI/2,this.rotation.z=0,isNaN(this.rotation.x)&&(this.rotation.x=0),isNaN(this.rotation.y)&&(this.rotation.y=0),isNaN(this.rotation.z)&&(this.rotation.z=0),this.rotationQuaternion&&g.b.RotationYawPitchRollToRef(this.rotation.y,this.rotation.x,this.rotation.z,this.rotationQuaternion)},Object.defineProperty(b.prototype,"target",{get:function(){return this.getTarget()},set:function(a){this.setTarget(a)},enumerable:!1,configurable:!0}),b.prototype.getTarget=function(){return this._currentTarget},b.prototype._decideIfNeedsToMove=function(){return Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){if(this.parent)return this.parent.getWorldMatrix().invertToRef(g.c.Matrix[0]),g.e.TransformNormalToRef(this.cameraDirection,g.c.Matrix[0],g.c.Vector3[0]),void this.position.addInPlace(g.c.Vector3[0]);this.position.addInPlace(this.cameraDirection)},b.prototype._checkInputs=function(){var b=this.invertRotation?-this.inverseRotationSpeed:1,c=this._decideIfNeedsToMove(),d=Math.abs(this.cameraRotation.x)>0||Math.abs(this.cameraRotation.y)>0;(c&&this._updatePosition(),d)&&((this.rotationQuaternion&&this.rotationQuaternion.toEulerAnglesToRef(this.rotation),this.rotation.x+=this.cameraRotation.x*b,this.rotation.y+=this.cameraRotation.y*b,!this.noRotationConstraint)&&(this.rotation.x>1.570796&&(this.rotation.x=1.570796),this.rotation.x<-1.570796&&(this.rotation.x=-1.570796)),this.rotationQuaternion&&(this.rotation.lengthSquared()&&g.b.RotationYawPitchRollToRef(this.rotation.y,this.rotation.x,this.rotation.z,this.rotationQuaternion)));c&&(Math.abs(this.cameraDirection.x)T.a.CollisionsEpsilon&&(f.position.addInPlace(f._diffPosition),f.onCollide&&c&&f.onCollide(c))},f.inputs=new Wb(f),f.inputs.addKeyboard().addMouse(),f}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"angularSensibility",{get:function(){var a=this.inputs.attached.mouse;return a?a.angularSensibility:0},set:function(a){var b=this.inputs.attached.mouse;b&&(b.angularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUpward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUpward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUpward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDownward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDownward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDownward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRotateLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRotateLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRotateLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRotateRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRotateRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRotateRight=a)},enumerable:!1,configurable:!0}),b.prototype.attachControl=function(a,b){b=U.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b)},b.prototype.detachControl=function(a){this.inputs.detachElement(),this.cameraDirection=new g.e(0,0,0),this.cameraRotation=new g.d(0,0)},Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._collisionMask},set:function(a){this._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),b.prototype._collideWithWorld=function(a){(this.parent?g.e.TransformCoordinates(this.position,this.parent.getWorldMatrix()):this.position).subtractFromFloatsToRef(0,this.ellipsoid.y,0,this._oldPosition),this._oldPosition.addInPlace(this.ellipsoidOffset);var b=this.getScene().collisionCoordinator;this._collider||(this._collider=b.createCollider()),this._collider._radius=this.ellipsoid,this._collider.collisionMask=this._collisionMask;var c=a;this.applyGravity&&(c=a.add(this.getScene().gravity)),b.getNewPosition(this._oldPosition,c,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)},b.prototype._checkInputs=function(){this._localDirection||(this._localDirection=g.e.Zero(),this._transformedDirection=g.e.Zero()),this.inputs.checkInputs(),a.prototype._checkInputs.call(this)},b.prototype._decideIfNeedsToMove=function(){return this._needMoveForGravity||Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){this.checkCollisions&&this.getScene().collisionsEnabled?this._collideWithWorld(this.cameraDirection):a.prototype._updatePosition.call(this)},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"FreeCamera"},Object(l.c)([Object(J.p)()],b.prototype,"ellipsoid",void 0),Object(l.c)([Object(J.p)()],b.prototype,"ellipsoidOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"checkCollisions",void 0),Object(l.c)([Object(J.d)()],b.prototype,"applyGravity",void 0),b}(cc);L.a.AddNodeConstructor("TouchCamera",function(a,b){return function(){return new ec(a,g.e.Zero(),b)}});var ec=function(a){function b(b,c,d){b=a.call(this,b,c,d)||this;return b.inputs.addTouch(),b._setupInputs(),b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"touchAngularSensibility",{get:function(){var a=this.inputs.attached.touch;return a?a.touchAngularSensibility:0},set:function(a){var b=this.inputs.attached.touch;b&&(b.touchAngularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"touchMoveSensibility",{get:function(){var a=this.inputs.attached.touch;return a?a.touchMoveSensibility:0},set:function(a){var b=this.inputs.attached.touch;b&&(b.touchMoveSensibility=a)},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"TouchCamera"},b.prototype._setupInputs=function(){var a=this.inputs.attached.touch,b=this.inputs.attached.mouse;b?b.touchEnabled=!1:a.allowMouse=!0},b}(dc);L.a.AddNodeConstructor("ArcRotateCamera",function(a,b){return function(){return new fc(a,0,0,1,g.e.Zero(),b)}});var fc=function(a){function b(b,c,d,e,h,i,j){void 0===j&&(j=!0);var k=a.call(this,b,g.e.Zero(),i,j)||this;return k.inertialAlphaOffset=0,k.inertialBetaOffset=0,k.inertialRadiusOffset=0,k.lowerAlphaLimit=null,k.upperAlphaLimit=null,k.lowerBetaLimit=.01,k.upperBetaLimit=Math.PI-.01,k.lowerRadiusLimit=null,k.upperRadiusLimit=null,k.inertialPanningX=0,k.inertialPanningY=0,k.pinchToPanMaxDistance=20,k.panningDistanceLimit=null,k.panningOriginTarget=g.e.Zero(),k.panningInertia=.9,k.zoomOnFactor=1,k.targetScreenOffset=g.d.Zero(),k.allowUpsideDown=!0,k.useInputToRestoreState=!0,k._viewMatrix=new g.a,k.panningAxis=new g.e(1,1,0),k._transformedDirection=new g.e,k.mapPanning=!1,k.onMeshTargetChangedObservable=new f.c,k.checkCollisions=!1,k.collisionRadius=new g.e(.5,.5,.5),k._previousPosition=g.e.Zero(),k._collisionVelocity=g.e.Zero(),k._newPosition=g.e.Zero(),k._computationVector=g.e.Zero(),k._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null),c?(k.setPosition(b),k.onCollide&&k.onCollide(c)):k._previousPosition.copyFrom(k._position);a=Math.cos(k.alpha);b=Math.sin(k.alpha);c=Math.cos(k.beta);var d=Math.sin(k.beta);0===d&&(d=1e-4);var e=k._getTargetPosition();k._computationVector.copyFromFloats(k.radius*a*d,k.radius*c,k.radius*b*d),e.addToRef(k._computationVector,k._newPosition),k._position.copyFrom(k._newPosition);a=k.upVector;k.allowUpsideDown&&k.beta<0&&(a=a.clone().negate()),k._computeViewMatrix(k._position,e,a),k._viewMatrix.addAtIndex(12,k.targetScreenOffset.x),k._viewMatrix.addAtIndex(13,k.targetScreenOffset.y),k._collisionTriggered=!1},k._target=g.e.Zero(),h&&k.setTarget(h),k.alpha=c,k.beta=d,k.radius=e,k.getViewMatrix(),k.inputs=new Kb(k),k.inputs.addKeyboard().addMouseWheel().addPointers(),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"target",{get:function(){return this._target},set:function(a){this.setTarget(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"targetHost",{get:function(){return this._targetHost},set:function(a){a&&this.setTarget(a)},enumerable:!1,configurable:!0}),b.prototype.getTarget=function(){return this.target},Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this.setPosition(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"upVector",{get:function(){return this._upVector},set:function(a){this._upToYMatrix||(this._YToUpMatrix=new g.a,this._upToYMatrix=new g.a,this._upVector=g.e.Zero()),a.normalize(),this._upVector.copyFrom(a),this.setMatUp()},enumerable:!1,configurable:!0}),b.prototype.setMatUp=function(){g.a.RotationAlignToRef(g.e.UpReadOnly,this._upVector,this._YToUpMatrix),g.a.RotationAlignToRef(this._upVector,g.e.UpReadOnly,this._upToYMatrix)},Object.defineProperty(b.prototype,"angularSensibilityX",{get:function(){var a=this.inputs.attached.pointers;return a?a.angularSensibilityX:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.angularSensibilityX=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"angularSensibilityY",{get:function(){var a=this.inputs.attached.pointers;return a?a.angularSensibilityY:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.angularSensibilityY=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pinchPrecision",{get:function(){var a=this.inputs.attached.pointers;return a?a.pinchPrecision:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.pinchPrecision=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pinchDeltaPercentage",{get:function(){var a=this.inputs.attached.pointers;return a?a.pinchDeltaPercentage:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.pinchDeltaPercentage=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useNaturalPinchZoom",{get:function(){var a=this.inputs.attached.pointers;return!!a&&a.useNaturalPinchZoom},set:function(a){var b=this.inputs.attached.pointers;b&&(b.useNaturalPinchZoom=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"panningSensibility",{get:function(){var a=this.inputs.attached.pointers;return a?a.panningSensibility:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.panningSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wheelPrecision",{get:function(){var a=this.inputs.attached.mousewheel;return a?a.wheelPrecision:0},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.wheelPrecision=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"zoomToMouseLocation",{get:function(){var a=this.inputs.attached.mousewheel;return!!a&&a.zoomToMouseLocation},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.zoomToMouseLocation=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wheelDeltaPercentage",{get:function(){var a=this.inputs.attached.mousewheel;return a?a.wheelDeltaPercentage:0},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.wheelDeltaPercentage=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bouncingBehavior",{get:function(){return this._bouncingBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useBouncingBehavior",{get:function(){return null!=this._bouncingBehavior},set:function(a){a!==this.useBouncingBehavior&&(a?(this._bouncingBehavior=new Va,this.addBehavior(this._bouncingBehavior)):this._bouncingBehavior&&(this.removeBehavior(this._bouncingBehavior),this._bouncingBehavior=null))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"framingBehavior",{get:function(){return this._framingBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useFramingBehavior",{get:function(){return null!=this._framingBehavior},set:function(a){a!==this.useFramingBehavior&&(a?(this._framingBehavior=new Wa,this.addBehavior(this._framingBehavior)):this._framingBehavior&&(this.removeBehavior(this._framingBehavior),this._framingBehavior=null))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"autoRotationBehavior",{get:function(){return this._autoRotationBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useAutoRotationBehavior",{get:function(){return null!=this._autoRotationBehavior},set:function(a){a!==this.useAutoRotationBehavior&&(a?(this._autoRotationBehavior=new Ua,this.addBehavior(this._autoRotationBehavior)):this._autoRotationBehavior&&(this.removeBehavior(this._autoRotationBehavior),this._autoRotationBehavior=null))},enumerable:!1,configurable:!0}),b.prototype._initCache=function(){a.prototype._initCache.call(this),this._cache._target=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._cache.alpha=void 0,this._cache.beta=void 0,this._cache.radius=void 0,this._cache.targetScreenOffset=g.d.Zero()},b.prototype._updateCache=function(b){b||a.prototype._updateCache.call(this),this._cache._target.copyFrom(this._getTargetPosition()),this._cache.alpha=this.alpha,this._cache.beta=this.beta,this._cache.radius=this.radius,this._cache.targetScreenOffset.copyFrom(this.targetScreenOffset)},b.prototype._getTargetPosition=function(){if(this._targetHost&&this._targetHost.getAbsolutePosition){var a=this._targetHost.getAbsolutePosition();this._targetBoundingCenter?a.addToRef(this._targetBoundingCenter,this._target):this._target.copyFrom(a)}a=this._getLockedTargetPosition();return a||this._target},b.prototype.storeState=function(){return this._storedAlpha=this.alpha,this._storedBeta=this.beta,this._storedRadius=this.radius,this._storedTarget=this._getTargetPosition().clone(),this._storedTargetScreenOffset=this.targetScreenOffset.clone(),a.prototype.storeState.call(this)},b.prototype._restoreStateValues=function(){return!!a.prototype._restoreStateValues.call(this)&&(this.setTarget(this._storedTarget.clone()),this.alpha=this._storedAlpha,this.beta=this._storedBeta,this.radius=this._storedRadius,this.targetScreenOffset=this._storedTargetScreenOffset.clone(),this.inertialAlphaOffset=0,this.inertialBetaOffset=0,this.inertialRadiusOffset=0,this.inertialPanningX=0,this.inertialPanningY=0,!0)},b.prototype._isSynchronizedViewMatrix=function(){return!!a.prototype._isSynchronizedViewMatrix.call(this)&&this._cache._target.equals(this._getTargetPosition())&&this._cache.alpha===this.alpha&&this._cache.beta===this.beta&&this._cache.radius===this.radius&&this._cache.targetScreenOffset.equals(this.targetScreenOffset)},b.prototype.attachControl=function(a,b,c,d){var e=this;void 0===c&&(c=!0),void 0===d&&(d=2),b=U.b.BackCompatCameraNoPreventDefault(arguments),this._useCtrlForPanning=c,this._panningMouseButton=d,"boolean"==typeof arguments[0]&&(arguments.length>1&&(this._useCtrlForPanning=arguments[1]),arguments.length>2&&(this._panningMouseButton=arguments[2])),this.inputs.attachElement(b),this._reset=function(){e.inertialAlphaOffset=0,e.inertialBetaOffset=0,e.inertialRadiusOffset=0,e.inertialPanningX=0,e.inertialPanningY=0}},b.prototype.detachControl=function(a){this.inputs.detachElement(),this._reset&&this._reset()},b.prototype._checkInputs=function(){if(!this._collisionTriggered){if(this.inputs.checkInputs(),0!==this.inertialAlphaOffset||0!==this.inertialBetaOffset||0!==this.inertialRadiusOffset){var b=this.inertialAlphaOffset;this.beta<=0&&(b*=-1),this.getScene().useRightHandedSystem&&(b*=-1),this.parent&&this.parent._getWorldMatrixDeterminant()<0&&(b*=-1),this.alpha+=b,this.beta+=this.inertialBetaOffset,this.radius-=this.inertialRadiusOffset,this.inertialAlphaOffset*=this.inertia,this.inertialBetaOffset*=this.inertia,this.inertialRadiusOffset*=this.inertia,Math.abs(this.inertialAlphaOffset)Math.PI&&(this.beta=this.beta-2*Math.PI):this.betathis.upperBetaLimit&&(this.beta=this.upperBetaLimit),null!==this.lowerAlphaLimit&&this.alphathis.upperAlphaLimit&&(this.alpha=this.upperAlphaLimit),null!==this.lowerRadiusLimit&&this.radiusthis.upperRadiusLimit&&(this.radius=this.upperRadiusLimit,this.inertialRadiusOffset=0)},b.prototype.rebuildAnglesAndRadius=function(){this._position.subtractToRef(this._getTargetPosition(),this._computationVector),0===this._upVector.x&&1===this._upVector.y&&0===this._upVector.z||g.e.TransformCoordinatesToRef(this._computationVector,this._upToYMatrix,this._computationVector),this.radius=this._computationVector.length(),0===this.radius&&(this.radius=1e-4);var a=this.alpha;0===this._computationVector.x&&0===this._computationVector.z?this.alpha=Math.PI/2:this.alpha=Math.acos(this._computationVector.x/Math.sqrt(Math.pow(this._computationVector.x,2)+Math.pow(this._computationVector.z,2))),this._computationVector.z<0&&(this.alpha=2*Math.PI-this.alpha);a=Math.round((a-this.alpha)/(2*Math.PI));this.alpha+=2*a*Math.PI,this.beta=Math.acos(this._computationVector.y/this.radius),this._checkLimits()},b.prototype.setPosition=function(a){this._position.equals(a)||(this._position.copyFrom(a),this.rebuildAnglesAndRadius())},b.prototype.setTarget=function(a,b,c){if(void 0===b&&(b=!1),void 0===c&&(c=!1),a.getBoundingInfo)this._targetBoundingCenter=b?a.getBoundingInfo().boundingBox.centerWorld.clone():null,a.computeWorldMatrix(),this._targetHost=a,this._target=this._getTargetPosition(),this.onMeshTargetChangedObservable.notifyObservers(this._targetHost);else{b=a;a=this._getTargetPosition();if(a&&!c&&a.equals(b))return;this._targetHost=null,this._target=b,this._targetBoundingCenter=null,this.onMeshTargetChangedObservable.notifyObservers(null)}this.rebuildAnglesAndRadius()},b.prototype._getViewMatrix=function(){var a=Math.cos(this.alpha),b=Math.sin(this.alpha),c=Math.cos(this.beta),d=Math.sin(this.beta);0===d&&(d=1e-4),0===this.radius&&(this.radius=1e-4);var e=this._getTargetPosition();if(this._computationVector.copyFromFloats(this.radius*a*d,this.radius*c,this.radius*b*d),0===this._upVector.x&&1===this._upVector.y&&0===this._upVector.z||g.e.TransformCoordinatesToRef(this._computationVector,this._YToUpMatrix,this._computationVector),e.addToRef(this._computationVector,this._newPosition),this.getScene().collisionsEnabled&&this.checkCollisions){a=this.getScene().collisionCoordinator;this._collider||(this._collider=a.createCollider()),this._collider._radius=this.collisionRadius,this._newPosition.subtractToRef(this._position,this._collisionVelocity),this._collisionTriggered=!0,a.getNewPosition(this._position,this._collisionVelocity,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)}else{this._position.copyFrom(this._newPosition);c=this.upVector;this.allowUpsideDown&&d<0&&(c=c.negate()),this._computeViewMatrix(this._position,e,c),this._viewMatrix.addAtIndex(12,this.targetScreenOffset.x),this._viewMatrix.addAtIndex(13,this.targetScreenOffset.y)}return this._currentTarget=e,this._viewMatrix},b.prototype.zoomOn=function(a,b){void 0===b&&(b=!1),a=a||this.getScene().meshes;a=S.a.MinMax(a);var c=g.e.Distance(a.min,a.max);this.radius=c*this.zoomOnFactor,this.focusOn({min:a.min,max:a.max,distance:c},b)},b.prototype.focusOn=function(a,b){var c;if(void 0===b&&(b=!1),void 0===a.min){var d=a||this.getScene().meshes;d=S.a.MinMax(d),c=g.e.Distance(d.min,d.max)}else d=a,c=a.distance;this._target=S.a.Center(d),b||(this.maxZ=2*c)},b.prototype.createRigCamera=function(a,c){var d=0;switch(this.cameraRigMode){case cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH:case cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:case cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER:case cb.a.RIG_MODE_STEREOSCOPIC_INTERLACED:case cb.a.RIG_MODE_VR:d=this._cameraRigParams.stereoHalfAngle*(0===c?1:-1);break;case cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:d=this._cameraRigParams.stereoHalfAngle*(0===c?-1:1)}c=new b(a,this.alpha+d,this.beta,this.radius,this._target,this.getScene());return c._cameraRigParams={},c.isRigCamera=!0,c.rigParent=this,c.upVector=this.upVector,c},b.prototype._updateRigCameras=function(){var b=this._rigCameras[0],c=this._rigCameras[1];switch(b.beta=c.beta=this.beta,this.cameraRigMode){case cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH:case cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:case cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER:case cb.a.RIG_MODE_STEREOSCOPIC_INTERLACED:case cb.a.RIG_MODE_VR:b.alpha=this.alpha-this._cameraRigParams.stereoHalfAngle,c.alpha=this.alpha+this._cameraRigParams.stereoHalfAngle;break;case cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:b.alpha=this.alpha+this._cameraRigParams.stereoHalfAngle,c.alpha=this.alpha-this._cameraRigParams.stereoHalfAngle}a.prototype._updateRigCameras.call(this)},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"ArcRotateCamera"},Object(l.c)([Object(J.d)()],b.prototype,"alpha",void 0),Object(l.c)([Object(J.d)()],b.prototype,"beta",void 0),Object(l.c)([Object(J.d)()],b.prototype,"radius",void 0),Object(l.c)([Object(J.p)("target")],b.prototype,"_target",void 0),Object(l.c)([Object(J.l)("targetHost")],b.prototype,"_targetHost",void 0),Object(l.c)([Object(J.d)()],b.prototype,"inertialAlphaOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"inertialBetaOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"inertialRadiusOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerAlphaLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperAlphaLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerBetaLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperBetaLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerRadiusLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperRadiusLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"inertialPanningX",void 0),Object(l.c)([Object(J.d)()],b.prototype,"inertialPanningY",void 0),Object(l.c)([Object(J.d)()],b.prototype,"pinchToPanMaxDistance",void 0),Object(l.c)([Object(J.d)()],b.prototype,"panningDistanceLimit",void 0),Object(l.c)([Object(J.p)()],b.prototype,"panningOriginTarget",void 0),Object(l.c)([Object(J.d)()],b.prototype,"panningInertia",void 0),Object(l.c)([Object(J.d)()],b.prototype,"zoomToMouseLocation",null),Object(l.c)([Object(J.d)()],b.prototype,"zoomOnFactor",void 0),Object(l.c)([Object(J.d)()],b.prototype,"targetScreenOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"allowUpsideDown",void 0),Object(l.c)([Object(J.d)()],b.prototype,"useInputToRestoreState",void 0),b}(cc);L.a.AddNodeConstructor("DeviceOrientationCamera",function(a,b){return function(){return new gc(a,g.e.Zero(),b)}});var gc=function(a){function b(b,c,d){var e=a.call(this,b,c,d)||this;return e._tmpDragQuaternion=new g.b,e._disablePointerInputWhenUsingDeviceOrientation=!0,e._dragFactor=0,e._quaternionCache=new g.b,e.inputs.addDeviceOrientation(),e.inputs._deviceOrientationInput&&e.inputs._deviceOrientationInput._onDeviceOrientationChangedObservable.addOnce(function(){e._disablePointerInputWhenUsingDeviceOrientation&&e.inputs._mouseInput&&(e.inputs._mouseInput._allowCameraRotation=!1,e.inputs._mouseInput.onPointerMovedObservable.add(function(a){0!=e._dragFactor&&(e._initialQuaternion||(e._initialQuaternion=new g.b),g.b.FromEulerAnglesToRef(0,a.offsetX*e._dragFactor,0,e._tmpDragQuaternion),e._initialQuaternion.multiplyToRef(e._tmpDragQuaternion,e._initialQuaternion))}))}),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"disablePointerInputWhenUsingDeviceOrientation",{get:function(){return this._disablePointerInputWhenUsingDeviceOrientation},set:function(a){this._disablePointerInputWhenUsingDeviceOrientation=a},enumerable:!1,configurable:!0}),b.prototype.enableHorizontalDragging=function(a){void 0===a&&(a=1/300),this._dragFactor=a},b.prototype.getClassName=function(){return"DeviceOrientationCamera"},b.prototype._checkInputs=function(){a.prototype._checkInputs.call(this),this._quaternionCache.copyFrom(this.rotationQuaternion),this._initialQuaternion&&this._initialQuaternion.multiplyToRef(this.rotationQuaternion,this.rotationQuaternion)},b.prototype.resetToCurrentRotation=function(a){var b=this;void 0===a&&(a=R.a.Y),this.rotationQuaternion&&(this._initialQuaternion||(this._initialQuaternion=new g.b),this._initialQuaternion.copyFrom(this._quaternionCache||this.rotationQuaternion),["x","y","z"].forEach(function(c){a[c]?b._initialQuaternion[c]*=-1:b._initialQuaternion[c]=0}),this._initialQuaternion.normalize(),this._initialQuaternion.multiplyToRef(this.rotationQuaternion,this.rotationQuaternion))},b}(dc),hc=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Mb),this},b.prototype.addMouse=function(a){return void 0===a&&(a=!0),this.add(new Nb(a)),this},b}(Ab),ic=function(a){function b(b,c,d,e){void 0===e&&(e=!0);var f=a.call(this,b,c,d,e)||this;return f.ellipsoid=new g.e(1,1,1),f.ellipsoidOffset=new g.e(0,0,0),f.checkCollisions=!1,f.applyGravity=!1,f.cameraDirection=g.e.Zero(),f._trackRoll=0,f.rollCorrect=100,f.bankedTurn=!1,f.bankedTurnLimit=Math.PI/2,f.bankedTurnMultiplier=1,f._needMoveForGravity=!1,f._oldPosition=g.e.Zero(),f._diffPosition=g.e.Zero(),f._newPosition=g.e.Zero(),f._collisionMask=-1,f._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null);a=b,f._newPosition.copyFrom(a),f._newPosition.subtractToRef(f._oldPosition,f._diffPosition),f._diffPosition.length()>T.a.CollisionsEpsilon&&(f.position.addInPlace(f._diffPosition),f.onCollide&&c&&f.onCollide(c))},f.inputs=new hc(f),f.inputs.addKeyboard().addMouse(),f}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"angularSensibility",{get:function(){var a=this.inputs.attached.mouse;return a?a.angularSensibility:0},set:function(a){var b=this.inputs.attached.mouse;b&&(b.angularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysForward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysForward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysForward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysBackward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysBackward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysBackward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),b.prototype.attachControl=function(a,b){b=U.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b)},b.prototype.detachControl=function(){this.inputs.detachElement(),this.cameraDirection=new g.e(0,0,0)},Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._collisionMask},set:function(a){this._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),b.prototype._collideWithWorld=function(a){(this.parent?g.e.TransformCoordinates(this.position,this.parent.getWorldMatrix()):this.position).subtractFromFloatsToRef(0,this.ellipsoid.y,0,this._oldPosition),this._oldPosition.addInPlace(this.ellipsoidOffset);var b=this.getScene().collisionCoordinator;this._collider||(this._collider=b.createCollider()),this._collider._radius=this.ellipsoid,this._collider.collisionMask=this._collisionMask;var c=a;this.applyGravity&&(c=a.add(this.getScene().gravity)),b.getNewPosition(this._oldPosition,c,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)},b.prototype._checkInputs=function(){this._localDirection||(this._localDirection=g.e.Zero(),this._transformedDirection=g.e.Zero()),this.inputs.checkInputs(),a.prototype._checkInputs.call(this)},b.prototype._decideIfNeedsToMove=function(){return this._needMoveForGravity||Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){this.checkCollisions&&this.getScene().collisionsEnabled?this._collideWithWorld(this.cameraDirection):a.prototype._updatePosition.call(this)},b.prototype.restoreRoll=function(a){var b=this._trackRoll,c=b-this.rotation.z;Math.abs(c)>=.001&&(this.rotation.z+=c/a,Math.abs(b-this.rotation.z)<=.001&&(this.rotation.z=b))},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"FlyCamera"},Object(l.c)([Object(J.p)()],b.prototype,"ellipsoid",void 0),Object(l.c)([Object(J.p)()],b.prototype,"ellipsoidOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"checkCollisions",void 0),Object(l.c)([Object(J.d)()],b.prototype,"applyGravity",void 0),b}(cc),jc=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Ob),this},b.prototype.addMouseWheel=function(){return this.add(new Pb),this},b.prototype.addPointers=function(){return this.add(new Qb),this},b.prototype.addVRDeviceOrientation=function(){return!1,this},b}(Ab);L.a.AddNodeConstructor("FollowCamera",function(a,b){return function(){return new lc(a,g.e.Zero(),b)}}),L.a.AddNodeConstructor("ArcFollowCamera",function(a,b){return function(){return new mc(a,0,0,1,null,b)}});var kc,lc=function(a){function b(b,c,d,e){void 0===e&&(e=null);b=a.call(this,b,c,d)||this;return b.radius=12,b.lowerRadiusLimit=null,b.upperRadiusLimit=null,b.rotationOffset=0,b.lowerRotationOffsetLimit=null,b.upperRotationOffsetLimit=null,b.heightOffset=4,b.lowerHeightOffsetLimit=null,b.upperHeightOffsetLimit=null,b.cameraAcceleration=.05,b.maxCameraSpeed=20,b.lockedTarget=e,b.inputs=new jc(b),b.inputs.addKeyboard().addMouseWheel().addPointers(),b}return Object(l.d)(b,a),b.prototype._follow=function(a){if(a){var b=g.c.Matrix[0];a.absoluteRotationQuaternion.toRotationMatrix(b);b=Math.atan2(b.m[8],b.m[10]);b=U.b.ToRadians(this.rotationOffset)+b;a=a.getAbsolutePosition();var c=a.x+Math.sin(b)*this.radius;b=a.z+Math.cos(b)*this.radius;c=c-this.position.x;var d=a.y+this.heightOffset-this.position.y;b=b-this.position.z;c=c*this.cameraAcceleration*2;d=d*this.cameraAcceleration;b=b*this.cameraAcceleration*2;(c>this.maxCameraSpeed||c<-this.maxCameraSpeed)&&(c=c<1?-this.maxCameraSpeed:this.maxCameraSpeed),(d>this.maxCameraSpeed||d<-this.maxCameraSpeed)&&(d=d<1?-this.maxCameraSpeed:this.maxCameraSpeed),(b>this.maxCameraSpeed||b<-this.maxCameraSpeed)&&(b=b<1?-this.maxCameraSpeed:this.maxCameraSpeed),this.position=new g.e(this.position.x+c,this.position.y+d,this.position.z+b),this.setTarget(a)}},b.prototype.attachControl=function(a,b){b=U.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b),this._reset=function(){}},b.prototype.detachControl=function(a){this.inputs.detachElement(),this._reset&&this._reset()},b.prototype._checkInputs=function(){this.inputs.checkInputs(),this._checkLimits(),a.prototype._checkInputs.call(this),this.lockedTarget&&this._follow(this.lockedTarget)},b.prototype._checkLimits=function(){null!==this.lowerRadiusLimit&&this.radiusthis.upperRadiusLimit&&(this.radius=this.upperRadiusLimit),null!==this.lowerHeightOffsetLimit&&this.heightOffsetthis.upperHeightOffsetLimit&&(this.heightOffset=this.upperHeightOffsetLimit),null!==this.lowerRotationOffsetLimit&&this.rotationOffsetthis.upperRotationOffsetLimit&&(this.rotationOffset=this.upperRotationOffsetLimit)},b.prototype.getClassName=function(){return"FollowCamera"},Object(l.c)([Object(J.d)()],b.prototype,"radius",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerRadiusLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperRadiusLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"rotationOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerRotationOffsetLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperRotationOffsetLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"heightOffset",void 0),Object(l.c)([Object(J.d)()],b.prototype,"lowerHeightOffsetLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"upperHeightOffsetLimit",void 0),Object(l.c)([Object(J.d)()],b.prototype,"cameraAcceleration",void 0),Object(l.c)([Object(J.d)()],b.prototype,"maxCameraSpeed",void 0),Object(l.c)([Object(J.l)("lockedTargetId")],b.prototype,"lockedTarget",void 0),b}(cc),mc=function(a){function b(b,c,d,e,f,h){b=a.call(this,b,g.e.Zero(),h)||this;return b.alpha=c,b.beta=d,b.radius=e,b._cartesianCoordinates=g.e.Zero(),b.setMeshTarget(f),b}return Object(l.d)(b,a),b.prototype.setMeshTarget=function(a){this._meshTarget=a,this._follow()},b.prototype._follow=function(){if(this._meshTarget){this._cartesianCoordinates.x=this.radius*Math.cos(this.alpha)*Math.cos(this.beta),this._cartesianCoordinates.y=this.radius*Math.sin(this.beta),this._cartesianCoordinates.z=this.radius*Math.sin(this.alpha)*Math.cos(this.beta);var a=this._meshTarget.getAbsolutePosition();this.position=a.add(this._cartesianCoordinates),this.setTarget(a)}},b.prototype._checkInputs=function(){a.prototype._checkInputs.call(this),this._follow()},b.prototype.getClassName=function(){return"ArcFollowCamera"},b}(cc),nc=c(42);!function(a){a[a.VIVE=0]="VIVE",a[a.OCULUS=1]="OCULUS",a[a.WINDOWS=2]="WINDOWS",a[a.GEAR_VR=3]="GEAR_VR",a[a.DAYDREAM=4]="DAYDREAM",a[a.GENERIC=5]="GENERIC"}(kc||(kc={}));var oc,pc,qc=function(){function a(){}return a.InitiateController=function(a){for(var b=0,c=this._ControllerFactories;bthis._maxRotationDistFromHeadset){a=a-(a<0?-this._maxRotationDistFromHeadset:this._maxRotationDistFromHeadset);this._draggedRoomRotation+=a;var c=Math.sin(-a);a=Math.cos(-a);this._calculatedPosition.x=this._calculatedPosition.x*a-this._calculatedPosition.z*c,this._calculatedPosition.z=this._calculatedPosition.x*c+this._calculatedPosition.z*a}}g.e.TransformCoordinatesToRef(this._calculatedPosition,this._deviceToWorld,this.devicePosition),this._deviceToWorld.getRotationMatrixToRef(this._workingMatrix),g.b.FromRotationMatrixToRef(this._workingMatrix,this.deviceRotationQuaternion),this.deviceRotationQuaternion.multiplyInPlace(this._calculatedRotation),this._mesh&&(this._mesh.position.copyFrom(this.devicePosition),this._mesh.rotationQuaternion&&this._mesh.rotationQuaternion.copyFrom(this.deviceRotationQuaternion))}},b.prototype.updateFromDevice=function(a){if(!this.isXR&&a){this.rawPose=a,a.position&&(this._deviceRoomPosition.copyFromFloats(a.position[0],a.position[1],-a.position[2]),this._mesh&&this._mesh.getScene().useRightHandedSystem&&(this._deviceRoomPosition.z*=-1),this._trackPosition&&this._deviceRoomPosition.scaleToRef(this.deviceScaleFactor,this._calculatedPosition),this._calculatedPosition.addInPlace(this.position));var b=this.rawPose;a.orientation&&b.orientation&&4===b.orientation.length&&(this._deviceRoomRotationQuaternion.copyFromFloats(b.orientation[0],b.orientation[1],-b.orientation[2],-b.orientation[3]),this._mesh&&(this._mesh.getScene().useRightHandedSystem?(this._deviceRoomRotationQuaternion.z*=-1,this._deviceRoomRotationQuaternion.w*=-1):this._deviceRoomRotationQuaternion.multiplyToRef(this._leftHandSystemQuaternion,this._deviceRoomRotationQuaternion)),this._deviceRoomRotationQuaternion.multiplyToRef(this.rotationQuaternion,this._calculatedRotation))}},b.prototype.attachToMesh=function(a){if(this._mesh&&(this._mesh.parent=null),this._mesh=a,this._poseControlledCamera&&(this._mesh.parent=this._poseControlledCamera),this._mesh.rotationQuaternion||(this._mesh.rotationQuaternion=new g.b),!this.isXR&&(this._updatePoseAndMesh(),this._pointingPoseNode)){for(var b=[],c=this._pointingPoseNode;c.parent;)b.push(c.parent),c=c.parent;b.reverse().forEach(function(a){a.computeWorldMatrix(!0)})}this._meshAttachedObservable.notifyObservers(a)},b.prototype.attachToPoseControlledCamera=function(a){this._poseControlledCamera=a,this._mesh&&(this._mesh.parent=this._poseControlledCamera)},b.prototype.dispose=function(){this._mesh&&this._mesh.dispose(),this._mesh=null,a.prototype.dispose.call(this)},Object.defineProperty(b.prototype,"mesh",{get:function(){return this._mesh},enumerable:!1,configurable:!0}),b.prototype.getForwardRay=function(a){if(void 0===a&&(a=100),!this.mesh)return new nc.a(g.e.Zero(),new g.e(0,0,1),a);var b=this._pointingPoseNode?this._pointingPoseNode.getWorldMatrix():this.mesh.getWorldMatrix(),c=b.getTranslation(),d=new g.e(0,0,-1);d=g.e.TransformNormal(d,b);b=g.e.Normalize(d);return new nc.a(c,b,a)},b.POINTING_POSE="POINTING_POSE",b}(Cb);!function(a){a[a.A=0]="A",a[a.B=1]="B",a[a.X=2]="X",a[a.Y=3]="Y",a[a.LB=4]="LB",a[a.RB=5]="RB",a[a.Back=8]="Back",a[a.Start=9]="Start",a[a.LeftStick=10]="LeftStick",a[a.RightStick=11]="RightStick"}(oc||(oc={})),(function(a){a[a.Up=12]="Up",a[a.Down=13]="Down",a[a.Left=14]="Left",a[a.Right=15]="Right"})(pc||(pc={}));var sc,tc,uc=function(a){function b(b,c,d,e){void 0===e&&(e=!1);b=a.call(this,b,c,d,0,1,2,3)||this;return b._leftTrigger=0,b._rightTrigger=0,b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.onPadDownObservable=new f.c,b.onPadUpObservable=new f.c,b._buttonA=0,b._buttonB=0,b._buttonX=0,b._buttonY=0,b._buttonBack=0,b._buttonStart=0,b._buttonLB=0,b._buttonRB=0,b._buttonLeftStick=0,b._buttonRightStick=0,b._dPadUp=0,b._dPadDown=0,b._dPadLeft=0,b._dPadRight=0,b._isXboxOnePad=!1,b.type=Cb.XBOX,b._isXboxOnePad=e,b}return Object(l.d)(b,a),b.prototype.onlefttriggerchanged=function(a){this._onlefttriggerchanged=a},b.prototype.onrighttriggerchanged=function(a){this._onrighttriggerchanged=a},Object.defineProperty(b.prototype,"leftTrigger",{get:function(){return this._leftTrigger},set:function(a){this._onlefttriggerchanged&&this._leftTrigger!==a&&this._onlefttriggerchanged(a),this._leftTrigger=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightTrigger",{get:function(){return this._rightTrigger},set:function(a){this._onrighttriggerchanged&&this._rightTrigger!==a&&this._onrighttriggerchanged(a),this._rightTrigger=a},enumerable:!1,configurable:!0}),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype.ondpaddown=function(a){this._ondpaddown=a},b.prototype.ondpadup=function(a){this._ondpadup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype._setDPadValue=function(a,b,c){return a!==b&&(1===a&&(this._ondpaddown&&this._ondpaddown(c),this.onPadDownObservable.notifyObservers(c)),0===a&&(this._ondpadup&&this._ondpadup(c),this.onPadUpObservable.notifyObservers(c))),a},Object.defineProperty(b.prototype,"buttonA",{get:function(){return this._buttonA},set:function(a){this._buttonA=this._setButtonValue(a,this._buttonA,oc.A)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonB",{get:function(){return this._buttonB},set:function(a){this._buttonB=this._setButtonValue(a,this._buttonB,oc.B)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonX",{get:function(){return this._buttonX},set:function(a){this._buttonX=this._setButtonValue(a,this._buttonX,oc.X)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonY",{get:function(){return this._buttonY},set:function(a){this._buttonY=this._setButtonValue(a,this._buttonY,oc.Y)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonStart",{get:function(){return this._buttonStart},set:function(a){this._buttonStart=this._setButtonValue(a,this._buttonStart,oc.Start)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonBack",{get:function(){return this._buttonBack},set:function(a){this._buttonBack=this._setButtonValue(a,this._buttonBack,oc.Back)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLB",{get:function(){return this._buttonLB},set:function(a){this._buttonLB=this._setButtonValue(a,this._buttonLB,oc.LB)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRB",{get:function(){return this._buttonRB},set:function(a){this._buttonRB=this._setButtonValue(a,this._buttonRB,oc.RB)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLeftStick",{get:function(){return this._buttonLeftStick},set:function(a){this._buttonLeftStick=this._setButtonValue(a,this._buttonLeftStick,oc.LeftStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRightStick",{get:function(){return this._buttonRightStick},set:function(a){this._buttonRightStick=this._setButtonValue(a,this._buttonRightStick,oc.RightStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadUp",{get:function(){return this._dPadUp},set:function(a){this._dPadUp=this._setDPadValue(a,this._dPadUp,pc.Up)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadDown",{get:function(){return this._dPadDown},set:function(a){this._dPadDown=this._setDPadValue(a,this._dPadDown,pc.Down)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadLeft",{get:function(){return this._dPadLeft},set:function(a){this._dPadLeft=this._setDPadValue(a,this._dPadLeft,pc.Left)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadRight",{get:function(){return this._dPadRight},set:function(a){this._dPadRight=this._setDPadValue(a,this._dPadRight,pc.Right)},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this),this._isXboxOnePad,this.buttonA=this.browserGamepad.buttons[0].value,this.buttonB=this.browserGamepad.buttons[1].value,this.buttonX=this.browserGamepad.buttons[2].value,this.buttonY=this.browserGamepad.buttons[3].value,this.buttonLB=this.browserGamepad.buttons[4].value,this.buttonRB=this.browserGamepad.buttons[5].value,this.leftTrigger=this.browserGamepad.buttons[6].value,this.rightTrigger=this.browserGamepad.buttons[7].value,this.buttonBack=this.browserGamepad.buttons[8].value,this.buttonStart=this.browserGamepad.buttons[9].value,this.buttonLeftStick=this.browserGamepad.buttons[10].value,this.buttonRightStick=this.browserGamepad.buttons[11].value,this.dPadUp=this.browserGamepad.buttons[12].value,this.dPadDown=this.browserGamepad.buttons[13].value,this.dPadLeft=this.browserGamepad.buttons[14].value,this.dPadRight=this.browserGamepad.buttons[15].value},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.onButtonDownObservable.clear(),this.onButtonUpObservable.clear(),this.onPadDownObservable.clear(),this.onPadUpObservable.clear()},b}(Cb);!function(a){a[a.Cross=0]="Cross",a[a.Circle=1]="Circle",a[a.Square=2]="Square",a[a.Triangle=3]="Triangle",a[a.L1=4]="L1",a[a.R1=5]="R1",a[a.Share=8]="Share",a[a.Options=9]="Options",a[a.LeftStick=10]="LeftStick",a[a.RightStick=11]="RightStick"}(sc||(sc={})),(function(a){a[a.Up=12]="Up",a[a.Down=13]="Down",a[a.Left=14]="Left",a[a.Right=15]="Right"})(tc||(tc={}));var vc=function(a){function b(b,c,d){b=a.call(this,b.replace("STANDARD GAMEPAD","SONY PLAYSTATION DUALSHOCK"),c,d,0,1,2,3)||this;return b._leftTrigger=0,b._rightTrigger=0,b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.onPadDownObservable=new f.c,b.onPadUpObservable=new f.c,b._buttonCross=0,b._buttonCircle=0,b._buttonSquare=0,b._buttonTriangle=0,b._buttonShare=0,b._buttonOptions=0,b._buttonL1=0,b._buttonR1=0,b._buttonLeftStick=0,b._buttonRightStick=0,b._dPadUp=0,b._dPadDown=0,b._dPadLeft=0,b._dPadRight=0,b.type=Cb.DUALSHOCK,b}return Object(l.d)(b,a),b.prototype.onlefttriggerchanged=function(a){this._onlefttriggerchanged=a},b.prototype.onrighttriggerchanged=function(a){this._onrighttriggerchanged=a},Object.defineProperty(b.prototype,"leftTrigger",{get:function(){return this._leftTrigger},set:function(a){this._onlefttriggerchanged&&this._leftTrigger!==a&&this._onlefttriggerchanged(a),this._leftTrigger=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightTrigger",{get:function(){return this._rightTrigger},set:function(a){this._onrighttriggerchanged&&this._rightTrigger!==a&&this._onrighttriggerchanged(a),this._rightTrigger=a},enumerable:!1,configurable:!0}),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype.ondpaddown=function(a){this._ondpaddown=a},b.prototype.ondpadup=function(a){this._ondpadup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype._setDPadValue=function(a,b,c){return a!==b&&(1===a&&(this._ondpaddown&&this._ondpaddown(c),this.onPadDownObservable.notifyObservers(c)),0===a&&(this._ondpadup&&this._ondpadup(c),this.onPadUpObservable.notifyObservers(c))),a},Object.defineProperty(b.prototype,"buttonCross",{get:function(){return this._buttonCross},set:function(a){this._buttonCross=this._setButtonValue(a,this._buttonCross,sc.Cross)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonCircle",{get:function(){return this._buttonCircle},set:function(a){this._buttonCircle=this._setButtonValue(a,this._buttonCircle,sc.Circle)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonSquare",{get:function(){return this._buttonSquare},set:function(a){this._buttonSquare=this._setButtonValue(a,this._buttonSquare,sc.Square)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonTriangle",{get:function(){return this._buttonTriangle},set:function(a){this._buttonTriangle=this._setButtonValue(a,this._buttonTriangle,sc.Triangle)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonOptions",{get:function(){return this._buttonOptions},set:function(a){this._buttonOptions=this._setButtonValue(a,this._buttonOptions,sc.Options)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonShare",{get:function(){return this._buttonShare},set:function(a){this._buttonShare=this._setButtonValue(a,this._buttonShare,sc.Share)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonL1",{get:function(){return this._buttonL1},set:function(a){this._buttonL1=this._setButtonValue(a,this._buttonL1,sc.L1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonR1",{get:function(){return this._buttonR1},set:function(a){this._buttonR1=this._setButtonValue(a,this._buttonR1,sc.R1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLeftStick",{get:function(){return this._buttonLeftStick},set:function(a){this._buttonLeftStick=this._setButtonValue(a,this._buttonLeftStick,sc.LeftStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRightStick",{get:function(){return this._buttonRightStick},set:function(a){this._buttonRightStick=this._setButtonValue(a,this._buttonRightStick,sc.RightStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadUp",{get:function(){return this._dPadUp},set:function(a){this._dPadUp=this._setDPadValue(a,this._dPadUp,tc.Up)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadDown",{get:function(){return this._dPadDown},set:function(a){this._dPadDown=this._setDPadValue(a,this._dPadDown,tc.Down)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadLeft",{get:function(){return this._dPadLeft},set:function(a){this._dPadLeft=this._setDPadValue(a,this._dPadLeft,tc.Left)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadRight",{get:function(){return this._dPadRight},set:function(a){this._dPadRight=this._setDPadValue(a,this._dPadRight,tc.Right)},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this),this.buttonCross=this.browserGamepad.buttons[0].value,this.buttonCircle=this.browserGamepad.buttons[1].value,this.buttonSquare=this.browserGamepad.buttons[2].value,this.buttonTriangle=this.browserGamepad.buttons[3].value,this.buttonL1=this.browserGamepad.buttons[4].value,this.buttonR1=this.browserGamepad.buttons[5].value,this.leftTrigger=this.browserGamepad.buttons[6].value,this.rightTrigger=this.browserGamepad.buttons[7].value,this.buttonShare=this.browserGamepad.buttons[8].value,this.buttonOptions=this.browserGamepad.buttons[9].value,this.buttonLeftStick=this.browserGamepad.buttons[10].value,this.buttonRightStick=this.browserGamepad.buttons[11].value,this.dPadUp=this.browserGamepad.buttons[12].value,this.dPadDown=this.browserGamepad.buttons[13].value,this.dPadLeft=this.browserGamepad.buttons[14].value,this.dPadRight=this.browserGamepad.buttons[15].value},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.onButtonDownObservable.clear(),this.onButtonUpObservable.clear(),this.onPadDownObservable.clear(),this.onPadUpObservable.clear()},b}(Cb),wc=function(){function a(a){var b=this;if(this._scene=a,this._babylonGamepads=[],this._oneGamepadConnected=!1,this._isMonitoring=!1,this.onGamepadDisconnectedObservable=new f.c,Object(Ia.e)()?(this._gamepadEventSupported="GamepadEvent"in window,this._gamepadSupport=navigator&&(navigator.getGamepads||navigator.webkitGetGamepads||navigator.msGetGamepads||navigator.webkitGamepads)):this._gamepadEventSupported=!1,this.onGamepadConnectedObservable=new f.c(function(a){for(var c in b._babylonGamepads){var d=b._babylonGamepads[c];d&&d._isConnected&&b.onGamepadConnectedObservable.notifyObserver(a,d)}}),this._onGamepadConnectedEvent=function(a){var c;a=a.gamepad;a.index in b._babylonGamepads&&b._babylonGamepads[a.index].isConnected||(b._babylonGamepads[a.index]?((c=b._babylonGamepads[a.index]).browserGamepad=a,c._isConnected=!0):c=b._addNewGamepad(a),b.onGamepadConnectedObservable.notifyObservers(c),b._startMonitoringGamepads())},this._onGamepadDisconnectedEvent=function(a){a=a.gamepad;for(var c in b._babylonGamepads)if(b._babylonGamepads[c].index===a.index){var d=b._babylonGamepads[c];d._isConnected=!1,b.onGamepadDisconnectedObservable.notifyObservers(d),d.dispose&&d.dispose();break}},this._gamepadSupport)if(this._updateGamepadObjects(),this._babylonGamepads.length&&this._startMonitoringGamepads(),this._gamepadEventSupported){a=this._scene?this._scene.getEngine().getHostWindow():window;a&&(a.addEventListener("gamepadconnected",this._onGamepadConnectedEvent,!1),a.addEventListener("gamepaddisconnected",this._onGamepadDisconnectedEvent,!1))}else this._startMonitoringGamepads()}return Object.defineProperty(a.prototype,"gamepads",{get:function(){return this._babylonGamepads},enumerable:!1,configurable:!0}),a.prototype.getGamepadByType=function(a){void 0===a&&(a=Cb.XBOX);for(var b=0,c=this._babylonGamepads;b1?"COLOR_ATTACHMENT"+b:"COLOR_ATTACHMENT"+b+"_WEBGL"];b=-1!==c?e.TEXTURE_CUBE_MAP_POSITIVE_X+c:e.TEXTURE_2D;e.framebufferTexture2D(e.FRAMEBUFFER,f,b,a._hardwareTexture.underlyingResource,d),this._engine._bindUnboundFramebuffer(g)}},b.prototype.setTexture=function(b,c,d){void 0===c&&(c=0),void 0===d&&(d=!0),a.prototype.setTexture.call(this,b,c,d),this._bindTextureRenderTarget(b,c)},b.prototype.dispose=function(b){void 0===b&&(b=!1);var c=this._context;b||(this._colorTextureArray&&(this._context.deleteTexture(this._colorTextureArray),this._colorTextureArray=null),this._depthStencilTextureArray&&(this._context.deleteTexture(this._depthStencilTextureArray),this._depthStencilTextureArray=null)),this._framebuffer&&(c.deleteFramebuffer(this._framebuffer),this._framebuffer=null),this._depthStencilBuffer&&(c.deleteRenderbuffer(this._depthStencilBuffer),this._depthStencilBuffer=null),this._MSAAFramebuffer&&(c.deleteFramebuffer(this._MSAAFramebuffer),this._MSAAFramebuffer=null),a.prototype.dispose.call(this,b)},b}(Cc.a);pb.a.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new Dc(a,b,c,this,this._gl);return this._renderTargetWrapperCache.push(a),a},pb.a.prototype.createRenderTargetTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!1,a),d=new Bc.b;void 0!==b&&"object"==typeof b?(d.generateDepthBuffer=!!b.generateDepthBuffer,d.generateStencilBuffer=!!b.generateStencilBuffer):(d.generateDepthBuffer=!0,d.generateStencilBuffer=!1);b=this._createInternalTexture(a,b);var e=a.width||a;a=a.height||a;var f=this._currentFramebuffer,g=this._gl,h=g.createFramebuffer();return this._bindUnboundFramebuffer(h),c._depthStencilBuffer=this._setupFramebufferDepthAttachments(!!d.generateStencilBuffer,d.generateDepthBuffer,e,a),b.is2DArray||g.framebufferTexture2D(g.FRAMEBUFFER,g.COLOR_ATTACHMENT0,g.TEXTURE_2D,b._hardwareTexture.underlyingResource,0),this._bindUnboundFramebuffer(f),c._framebuffer=h,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=!!d.generateStencilBuffer,c.setTextures(b),c},pb.a.prototype.createDepthStencilTexture=function(a,b,c){if(b.isCube){var d=a.width||a;return this._createDepthStencilCubeTexture(d,b,c)}return this._createDepthStencilTexture(a,b,c)},pb.a.prototype._createDepthStencilTexture=function(a,b,c){var d=this._gl,e=a.layers||0,f=0!==e?d.TEXTURE_2D_ARRAY:d.TEXTURE_2D,g=new ob.a(this,ob.b.DepthStencil);if(!this._caps.depthTextureExtension)return q.a.Error("Depth texture is not supported by your browser or hardware."),g;b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1},b);this._bindTextureDirectly(f,g,!0),this._setupDepthStencilTexture(g,a,b.generateStencil,0!==b.comparisonFunction&&b.bilinearFiltering,b.comparisonFunction),c._depthStencilTexture=g,c._depthStencilTextureWithStencil=b.generateStencil;a=b.generateStencil?d.UNSIGNED_INT_24_8:d.UNSIGNED_INT;c=b.generateStencil?d.DEPTH_STENCIL:d.DEPTH_COMPONENT;var h=c;return this.webGLVersion>1&&(h=b.generateStencil?d.DEPTH24_STENCIL8:d.DEPTH_COMPONENT24),g.is2DArray?d.texImage3D(f,0,h,g.width,g.height,e,0,c,a,null):d.texImage2D(f,0,h,g.width,g.height,0,c,a,null),this._bindTextureDirectly(f,null),this._internalTexturesCache.push(g),g},pb.a.prototype.updateRenderTargetTextureSampleCount=function(a,b){if(this.webGLVersion<2||!a||!a.texture)return 1;if(a.samples===b)return b;var c=this._gl;b=Math.min(b,this.getCaps().maxMSAASamples),a._depthStencilBuffer&&(c.deleteRenderbuffer(a._depthStencilBuffer),a._depthStencilBuffer=null),a._MSAAFramebuffer&&(c.deleteFramebuffer(a._MSAAFramebuffer),a._MSAAFramebuffer=null);var d=a.texture._hardwareTexture;if(d._MSAARenderBuffer&&(c.deleteRenderbuffer(d._MSAARenderBuffer),d._MSAARenderBuffer=null),b>1&&c.renderbufferStorageMultisample){var e=c.createFramebuffer();if(!e)throw new Error("Unable to create multi sampled framebuffer");a._MSAAFramebuffer=e,this._bindUnboundFramebuffer(a._MSAAFramebuffer);e=this._createRenderBuffer(a.texture.width,a.texture.height,b,-1,this._getRGBAMultiSampleBufferFormat(a.texture.type),c.COLOR_ATTACHMENT0,!1);if(!e)throw new Error("Unable to create multi sampled framebuffer");d._MSAARenderBuffer=e}else this._bindUnboundFramebuffer(a._framebuffer);return a.texture.samples=b,a._depthStencilBuffer=this._setupFramebufferDepthAttachments(a._generateStencilBuffer,a._generateDepthBuffer,a.texture.width,a.texture.height,b),this._bindUnboundFramebuffer(null),b};var Ec=c(37),Fc=function(){function a(a,b,c,d,e,h,i,j,k,s,t,u,v,w,x){void 0===i&&(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),void 0===s&&(s=null),void 0===t&&(t=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===u&&(u="postprocess"),void 0===w&&(w=!1),void 0===x&&(x=r.a.TEXTUREFORMAT_RGBA),this._parentContainer=null,this.width=-1,this.height=-1,this.nodeMaterialSource=null,this._outputTexture=null,this.autoClear=!0,this.alphaMode=r.a.ALPHA_DISABLE,this.animations=new Array,this.enablePixelPerfectMode=!1,this.forceFullscreenViewport=!0,this.scaleMode=r.a.SCALEMODE_FLOOR,this.alwaysForcePOT=!1,this._samples=1,this.adaptScaleToCurrentViewport=!1,this._reusable=!1,this._renderId=0,this._textures=new Ac.a(2),this._textureCache=[],this._currentRenderTextureInd=0,this._scaleRatio=new g.d(1,1),this._texelSize=g.d.Zero(),this.onActivateObservable=new f.c,this.onSizeChangedObservable=new f.c,this.onApplyObservable=new f.c,this.onBeforeRenderObservable=new f.c,this.onAfterRenderObservable=new f.c,this.name=a,null!=h?(this._camera=h,this._scene=h.getScene(),h.attachPostProcess(this),this._engine=this._scene.getEngine(),this._scene.postProcesses.push(this),this.uniqueId=this._scene.getUniqueId()):j&&(this._engine=j,this._engine.postProcesses.push(this)),this._options=e,this.renderTargetSamplingMode=i||r.a.TEXTURE_NEAREST_SAMPLINGMODE,this._reusable=k||!1,this._textureType=t,this._textureFormat=x,this._samplers=d||[],this._samplers.push("textureSampler"),this._fragmentUrl=b,this._vertexUrl=u,this._parameters=c||[],this._parameters.push("scale"),this._indexParameters=v,this._drawWrapper=new Ec.a(this._engine),w||this.updateEffect(s)}return Object.defineProperty(a.prototype,"samples",{get:function(){return this._samples},set:function(a){var b=this;this._samples=Math.min(a,this._engine.getCaps().maxMSAASamples),this._textures.forEach(function(a){a.samples!==b._samples&&b._engine.updateRenderTargetTextureSampleCount(a,b._samples)})},enumerable:!1,configurable:!0}),a.prototype.getEffectName=function(){return this._fragmentUrl},Object.defineProperty(a.prototype,"onActivate",{set:function(a){this._onActivateObserver&&this.onActivateObservable.remove(this._onActivateObserver),a&&(this._onActivateObserver=this.onActivateObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onSizeChanged",{set:function(a){this._onSizeChangedObserver&&this.onSizeChangedObservable.remove(this._onSizeChangedObserver),this._onSizeChangedObserver=this.onSizeChangedObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onApply",{set:function(a){this._onApplyObserver&&this.onApplyObservable.remove(this._onApplyObserver),this._onApplyObserver=this.onApplyObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onBeforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onAfterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=this.onAfterRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"inputTexture",{get:function(){return this._textures.data[this._currentRenderTextureInd]},set:function(a){this._forcedOutputTexture=a},enumerable:!1,configurable:!0}),a.prototype.restoreDefaultInputTexture=function(){this._forcedOutputTexture&&(this._forcedOutputTexture=null,this.markTextureDirty())},a.prototype.getCamera=function(){return this._camera},Object.defineProperty(a.prototype,"texelSize",{get:function(){return this._shareOutputWithPostProcess?this._shareOutputWithPostProcess.texelSize:(this._forcedOutputTexture&&this._texelSize.copyFromFloats(1/this._forcedOutputTexture.width,1/this._forcedOutputTexture.height),this._texelSize)},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"PostProcess"},a.prototype.getEngine=function(){return this._engine},a.prototype.getEffect=function(){return this._drawWrapper.effect},a.prototype.shareOutputWith=function(a){return this._disposeTextures(),this._shareOutputWithPostProcess=a,this},a.prototype.useOwnOutput=function(){0==this._textures.length&&(this._textures=new Ac.a(2)),this._shareOutputWithPostProcess=null},a.prototype.updateEffect=function(a,b,c,d,e,f,g,h){void 0===a&&(a=null),void 0===b&&(b=null),void 0===c&&(c=null),this._postProcessDefines=a,this._drawWrapper.effect=this._engine.createEffect({vertex:null!=g?g:this._vertexUrl,fragment:null!=h?h:this._fragmentUrl},["position"],b||this._parameters,c||this._samplers,null!==a?a:"",void 0,e,f,d||this._indexParameters)},a.prototype.isReusable=function(){return this._reusable},a.prototype.markTextureDirty=function(){this.width=-1},a.prototype._createRenderTargetTexture=function(a,b,c){void 0===c&&(c=0);for(var d=0;d=0;b--)if(a-this._textureCache[b].lastUsedRenderId>100){for(var c=!1,d=0;d0&&this._textures.reset(),this.width=a,this.height=b;for(a=null,b=0;b=0;a--)this._textureCache[a].texture.dispose();this._textureCache.length=0},a.prototype.setPrePassRenderer=function(a){return!!this._prePassEffectConfiguration&&(this._prePassEffectConfiguration=a.addEffectConfiguration(this._prePassEffectConfiguration),this._prePassEffectConfiguration.enabled=!0,!0)},a.prototype.dispose=function(a){var b;if(a=a||this._camera,this._disposeTextures(),this._scene&&-1!==(b=this._scene.postProcesses.indexOf(this))&&this._scene.postProcesses.splice(b,1),this._parentContainer){var c=this._parentContainer.postProcesses.indexOf(this);c>-1&&this._parentContainer.postProcesses.splice(c,1),this._parentContainer=null}if(-1!==(b=this._engine.postProcesses.indexOf(this))&&this._engine.postProcesses.splice(b,1),a){if(a.detachPostProcess(this),0===(b=a._postProcesses.indexOf(this))&&a._postProcesses.length>0){c=this._camera._getFirstPostProcess();c&&c.markTextureDirty()}this.onActivateObservable.clear(),this.onAfterRenderObservable.clear(),this.onApplyObservable.clear(),this.onBeforeRenderObservable.clear(),this.onSizeChangedObservable.clear()}},a.prototype.serialize=function(){var a=J.a.Serialize(this),b=this.getCamera()||this._scene&&this._scene.activeCamera;return a.customType="BABYLON."+this.getClassName(),a.cameraId=b?b.id:null,a.reusable=this._reusable,a.textureType=this._textureType,a.fragmentUrl=this._fragmentUrl,a.parameters=this._parameters,a.samplers=this._samplers,a.options=this._options,a.defines=this._postProcessDefines,a.textureFormat=this._textureFormat,a.vertexUrl=this._vertexUrl,a.indexParameters=this._indexParameters,a},a.prototype.clone=function(){var b=this.serialize();b._engine=this._engine,b.cameraId=null;b=a.Parse(b,this._scene,"");return b?(b.onActivateObservable=this.onActivateObservable.clone(),b.onSizeChangedObservable=this.onSizeChangedObservable.clone(),b.onApplyObservable=this.onApplyObservable.clone(),b.onBeforeRenderObservable=this.onBeforeRenderObservable.clone(),b.onAfterRenderObservable=this.onAfterRenderObservable.clone(),b._prePassEffectConfiguration=this._prePassEffectConfiguration,b):null},a.Parse=function(a,b,c){var d=Object(i.a)(a.customType);if(!d||!d._Parse)return null;var e=b?b.getCameraById(a.cameraId):null;return d._Parse(a,e,b,c)},a._Parse=function(b,c,d,e){return J.a.Parse(function(){return new a(b.name,b.fragmentUrl,b.parameters,b.samplers,b.options,c,b.renderTargetSamplingMode,b._engine,b.reusable,b.defines,b.textureType,b.vertexUrl,b.indexParameters,!1,b.textureFormat)},b,d,e)},Object(l.c)([Object(J.d)()],a.prototype,"uniqueId",void 0),Object(l.c)([Object(J.d)()],a.prototype,"name",void 0),Object(l.c)([Object(J.d)()],a.prototype,"width",void 0),Object(l.c)([Object(J.d)()],a.prototype,"height",void 0),Object(l.c)([Object(J.d)()],a.prototype,"renderTargetSamplingMode",void 0),Object(l.c)([Object(J.g)()],a.prototype,"clearColor",void 0),Object(l.c)([Object(J.d)()],a.prototype,"autoClear",void 0),Object(l.c)([Object(J.d)()],a.prototype,"alphaMode",void 0),Object(l.c)([Object(J.d)()],a.prototype,"alphaConstants",void 0),Object(l.c)([Object(J.d)()],a.prototype,"enablePixelPerfectMode",void 0),Object(l.c)([Object(J.d)()],a.prototype,"forceFullscreenViewport",void 0),Object(l.c)([Object(J.d)()],a.prototype,"scaleMode",void 0),Object(l.c)([Object(J.d)()],a.prototype,"alwaysForcePOT",void 0),Object(l.c)([Object(J.d)("samples")],a.prototype,"_samples",void 0),Object(l.c)([Object(J.d)()],a.prototype,"adaptScaleToCurrentViewport",void 0),a}();Object(i.b)("BABYLON.PostProcess",Fc);rb=" varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor=texture2D(textureSampler,vUV); }";X.a.ShadersStore.passPixelShader=rb;a=" varying vec2 vUV; uniform samplerCube textureSampler; void main(void) { vec2 uv=vUV*2.0-1.0; #ifdef POSITIVEX gl_FragColor=textureCube(textureSampler,vec3(1.001,uv.y,uv.x)); #endif #ifdef NEGATIVEX gl_FragColor=textureCube(textureSampler,vec3(-1.001,uv.y,uv.x)); #endif #ifdef POSITIVEY gl_FragColor=textureCube(textureSampler,vec3(uv.y,1.001,uv.x)); #endif #ifdef NEGATIVEY gl_FragColor=textureCube(textureSampler,vec3(uv.y,-1.001,uv.x)); #endif #ifdef POSITIVEZ gl_FragColor=textureCube(textureSampler,vec3(uv,1.001)); #endif #ifdef NEGATIVEZ gl_FragColor=textureCube(textureSampler,vec3(uv,-1.001)); #endif }";X.a.ShadersStore.passCubePixelShader=a;var Gc=function(a){function b(b,c,d,e,f,g,h,i){return void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1),a.call(this,b,"pass",null,null,c,d,e,f,g,void 0,h,void 0,null,i)||this}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"PassPostProcess"},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,a._engine,a.reusable)},a,d,e)},b}(Fc);Object(i.b)("BABYLON.PassPostProcess",Gc);var Hc=function(a){function b(b,c,d,e,f,g,h,i){void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1);b=a.call(this,b,"passCube",null,null,c,d,e,f,g,"#define POSITIVEX",h,void 0,null,i)||this;return b._face=0,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"face",{get:function(){return this._face},set:function(a){if(!(a<0||a>5))switch(this._face=a,this._face){case 0:this.updateEffect("#define POSITIVEX");break;case 1:this.updateEffect("#define NEGATIVEX");break;case 2:this.updateEffect("#define POSITIVEY");break;case 3:this.updateEffect("#define NEGATIVEY");break;case 4:this.updateEffect("#define POSITIVEZ");break;case 5:this.updateEffect("#define NEGATIVEZ")}},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PassCubePostProcess"},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,a._engine,a.reusable)},a,d,e)},b}(Fc);T.a._RescalePostProcessFactory=function(a){return new Gc("rescale",1,null,r.a.TEXTURE_BILINEAR_SAMPLINGMODE,a,!1,r.a.TEXTURETYPE_UNSIGNED_INT)};rb=" varying vec2 vUV; uniform sampler2D textureSampler; uniform sampler2D leftSampler; void main(void) { vec4 leftFrag=texture2D(leftSampler,vUV); leftFrag=vec4(1.0,leftFrag.g,leftFrag.b,1.0); vec4 rightFrag=texture2D(textureSampler,vUV); rightFrag=vec4(rightFrag.r,1.0,1.0,1.0); gl_FragColor=vec4(rightFrag.rgb*leftFrag.rgb,1.0); }";X.a.ShadersStore.anaglyphPixelShader=rb;var Ic=function(a){function b(b,c,d,e,f,g){var h=a.call(this,b,"anaglyph",null,["leftSampler"],c,d[1],e,f,g)||this;return h._passedProcess=d[0]._rigPostProcess,h.onApplyObservable.add(function(a){a.setTextureFromPostProcess("leftSampler",h._passedProcess)}),h}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphPostProcess"},b}(Fc);function Jc(a){a._rigCameras[0]._rigPostProcess=new Gc(a.name+"_passthru",1,a._rigCameras[0]),a._rigCameras[1]._rigPostProcess=new Ic(a.name+"_anaglyph",1,a._rigCameras)}Object(i.b)("BABYLON.AnaglyphPostProcess",Ic),L.a.AddNodeConstructor("AnaglyphArcRotateCamera",function(a,b,c){return function(){return new Kc(a,0,0,1,g.e.Zero(),c.interaxial_distance,b)}});var Kc=function(a){function b(b,c,d,e,f,g,h){b=a.call(this,b,c,d,e,f,h)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=g,b.setCameraRigMode(cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:g}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphArcRotateCamera"},b}(fc);L.a.AddNodeConstructor("AnaglyphFreeCamera",function(a,b,c){return function(){return new Lc(a,g.e.Zero(),c.interaxial_distance,b)}});var Lc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphFreeCamera"},b}(dc);L.a.AddNodeConstructor("AnaglyphGamepadCamera",function(a,b,c){return function(){return new Mc(a,g.e.Zero(),c.interaxial_distance,b)}});var Mc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphGamepadCamera"},b}(zc);L.a.AddNodeConstructor("AnaglyphUniversalCamera",function(a,b,c){return function(){return new Nc(a,g.e.Zero(),c.interaxial_distance,b)}});var Nc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(cb.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphUniversalCamera"},b}(yc),Oc=c(61);a="const vec3 TWO=vec3(2.0,2.0,2.0); varying vec2 vUV; uniform sampler2D camASampler; uniform sampler2D textureSampler; uniform vec2 stepSize; void main(void) { bool useCamA; bool useCamB; vec2 texCoord1; vec2 texCoord2; vec3 frag1; vec3 frag2; #ifdef IS_STEREOSCOPIC_HORIZ useCamB=vUV.x>0.5; useCamA=!useCamB; texCoord1=vec2(useCamB ? (vUV.x-0.5)*2.0 : vUV.x*2.0,vUV.y); texCoord2=vec2(texCoord1.x+stepSize.x,vUV.y); #else #ifdef IS_STEREOSCOPIC_INTERLACED float rowNum=floor(vUV.y/stepSize.y); useCamA=mod(rowNum,2.0) == 1.0; useCamB=mod(rowNum,2.0) == 0.0; texCoord1=vec2(vUV.x,vUV.y); texCoord2=vec2(vUV.x,vUV.y); #else useCamB=vUV.y>0.5; useCamA=!useCamB; texCoord1=vec2(vUV.x,useCamB ? (vUV.y-0.5)*2.0 : vUV.y*2.0); texCoord2=vec2(vUV.x,texCoord1.y+stepSize.y); #endif #endif if (useCamB){ frag1=texture2D(textureSampler,texCoord1).rgb; frag2=texture2D(textureSampler,texCoord2).rgb; }else if (useCamA){ frag1=texture2D(camASampler ,texCoord1).rgb; frag2=texture2D(camASampler ,texCoord2).rgb; }else { discard; } gl_FragColor=vec4((frag1+frag2)/TWO,1.0); } ";X.a.ShadersStore.stereoscopicInterlacePixelShader=a;var Pc=function(a){function b(b,c,d,e,f,h,i){var j=a.call(this,b,"stereoscopicInterlace",["stepSize"],["camASampler"],1,c[1],f,h,i,e?"#define IS_STEREOSCOPIC_INTERLACED 1":d?"#define IS_STEREOSCOPIC_HORIZ 1":void 0)||this;return j._passedProcess=c[0]._rigPostProcess,j._stepSize=new g.d(1/j.width,1/j.height),j.onSizeChangedObservable.add(function(){j._stepSize=new g.d(1/j.width,1/j.height)}),j.onApplyObservable.add(function(a){a.setTextureFromPostProcess("camASampler",j._passedProcess),a.setFloat2("stepSize",j._stepSize.x,j._stepSize.y)}),j}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicInterlacePostProcessI"},b}(Fc),Qc=function(a){function b(b,c,d,e,f,h){var i=a.call(this,b,"stereoscopicInterlace",["stepSize"],["camASampler"],1,c[1],e,f,h,d?"#define IS_STEREOSCOPIC_HORIZ 1":void 0)||this;return i._passedProcess=c[0]._rigPostProcess,i._stepSize=new g.d(1/i.width,1/i.height),i.onSizeChangedObservable.add(function(){i._stepSize=new g.d(1/i.width,1/i.height)}),i.onApplyObservable.add(function(a){a.setTextureFromPostProcess("camASampler",i._passedProcess),a.setFloat2("stepSize",i._stepSize.x,i._stepSize.y)}),i}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicInterlacePostProcess"},b}(Fc);function Rc(a){var b=a.cameraRigMode===cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL||a.cameraRigMode===cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED,c=a.cameraRigMode===cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;a.cameraRigMode===cb.a.RIG_MODE_STEREOSCOPIC_INTERLACED?(a._rigCameras[0]._rigPostProcess=new Gc(a.name+"_passthru",1,a._rigCameras[0]),a._rigCameras[1]._rigPostProcess=new Pc(a.name+"_stereoInterlace",a._rigCameras,!1,!0)):(a._rigCameras[c?1:0].viewport=new Oc.a(0,0,b?.5:1,b?1:.5),a._rigCameras[c?0:1].viewport=new Oc.a(b?.5:0,b?0:.5,b?.5:1,b?1:.5))}L.a.AddNodeConstructor("StereoscopicArcRotateCamera",function(a,b,c){return function(){return new Sc(a,0,0,1,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Sc=function(a){function b(b,c,d,e,f,g,h,i){b=a.call(this,b,c,d,e,f,i)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=g,b.isStereoscopicSideBySide=h,b.setCameraRigMode(h?cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:g}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicArcRotateCamera"},b}(fc);L.a.AddNodeConstructor("StereoscopicFreeCamera",function(a,b,c){return function(){return new Tc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Tc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicFreeCamera"},b}(dc);L.a.AddNodeConstructor("StereoscopicGamepadCamera",function(a,b,c){return function(){return new Uc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Uc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicGamepadCamera"},b}(zc);L.a.AddNodeConstructor("StereoscopicFreeCamera",function(a,b,c){return function(){return new Vc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Vc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:cb.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicUniversalCamera"},b}(yc),Wc=function(a){function b(b,c,d,e,f){void 0===e&&(e=1),void 0===f&&(f=.065);b=a.call(this,b,c,d)||this;return b._distanceBetweenEyes=f,b._distanceToProjectionPlane=e,b.setCameraRigMode(cb.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL,{stereoHalfAngle:0}),b._cameraRigParams.stereoHalfAngle=0,b._cameraRigParams.interaxialDistance=f,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"distanceBetweenEyes",{get:function(){return this._distanceBetweenEyes},set:function(a){this._distanceBetweenEyes=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"distanceToProjectionPlane",{get:function(){return this._distanceToProjectionPlane},set:function(a){this._distanceToProjectionPlane=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"StereoscopicUniversalCamera"},b.prototype.createRigCamera=function(a,b){b=new cc(a,g.e.Zero(),this.getScene());a=new eb.a("tm_"+a,this.getScene());return b.parent=a,a.setPivotMatrix(g.a.Identity(),!1),b.isRigCamera=!0,b.rigParent=this,b},b.prototype._updateRigCameras=function(){for(var a=0;a1.0 || tc.y<0.0 || tc.y>1.0) gl_FragColor=vec4(0.0,0.0,0.0,0.0); else{ gl_FragColor=texture2D(textureSampler,tc); } }";X.a.ShadersStore.vrDistortionCorrectionPixelShader=rb;var Zc=function(a){function b(b,c,d,e){var f=a.call(this,b,"vrDistortionCorrection",["LensCenter","Scale","ScaleIn","HmdWarpParam"],null,e.postProcessScaleFactor,c,V.a.BILINEAR_SAMPLINGMODE)||this;return f._isRightEye=d,f._distortionFactors=e.distortionK,f._postProcessScaleFactor=e.postProcessScaleFactor,f._lensCenterOffset=e.lensCenterOffset,f.adaptScaleToCurrentViewport=!0,f.onSizeChangedObservable.add(function(){f._scaleIn=new g.d(2,2/f.aspectRatio),f._scaleFactor=new g.d(1/f._postProcessScaleFactor*.5,1/f._postProcessScaleFactor*.5*f.aspectRatio),f._lensCenter=new g.d(f._isRightEye?.5-.5*f._lensCenterOffset:.5+.5*f._lensCenterOffset,.5)}),f.onApplyObservable.add(function(a){a.setFloat2("LensCenter",f._lensCenter.x,f._lensCenter.y),a.setFloat2("Scale",f._scaleFactor.x,f._scaleFactor.y),a.setFloat2("ScaleIn",f._scaleIn.x,f._scaleIn.y),a.setFloat4("HmdWarpParam",f._distortionFactors[0],f._distortionFactors[1],f._distortionFactors[2],f._distortionFactors[3])}),f}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"VRDistortionCorrectionPostProcess"},b}(Fc);a="precision mediump sampler2DArray; varying vec2 vUV; uniform sampler2DArray multiviewSampler; uniform int imageIndex; void main(void) { gl_FragColor=texture2D(multiviewSampler,vec3(vUV,imageIndex)); }";X.a.ShadersStore.vrMultiviewToSingleviewPixelShader=a;var $c=c(67),ad=c(103),bd=c(104);pb.a.prototype.createRenderTargetCubeTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!0,a);b=Object(l.a)({generateMipMaps:!0,generateDepthBuffer:!0,generateStencilBuffer:!1,type:r.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,format:r.a.TEXTUREFORMAT_RGBA},b);b.generateStencilBuffer=b.generateDepthBuffer&&b.generateStencilBuffer,(b.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(b.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(b.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);var d=this._gl,e=new ob.a(this,ob.b.RenderTarget);this._bindTextureDirectly(d.TEXTURE_CUBE_MAP,e,!0);var f=this._getSamplingParameters(b.samplingMode,b.generateMipMaps);b.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloat||(b.type=r.a.TEXTURETYPE_UNSIGNED_INT,q.a.Warn("Float textures are not supported. Cube render target forced to TEXTURETYPE_UNESIGNED_BYTE type")),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_MAG_FILTER,f.mag),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_MIN_FILTER,f.min),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_WRAP_S,d.CLAMP_TO_EDGE),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_WRAP_T,d.CLAMP_TO_EDGE);for(f=0;f<6;f++)d.texImage2D(d.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,this._getRGBABufferInternalSizedFormat(b.type,b.format),a,a,0,this._getInternalFormat(b.format),this._getWebGLTextureType(b.type),null);f=d.createFramebuffer();return this._bindUnboundFramebuffer(f),c._depthStencilBuffer=this._setupFramebufferDepthAttachments(b.generateStencilBuffer,b.generateDepthBuffer,a,a),b.generateMipMaps&&d.generateMipmap(d.TEXTURE_CUBE_MAP),this._bindTextureDirectly(d.TEXTURE_CUBE_MAP,null),this._bindUnboundFramebuffer(null),c._framebuffer=f,c._generateDepthBuffer=b.generateDepthBuffer,c._generateStencilBuffer=b.generateStencilBuffer,e.width=a,e.height=a,e.isReady=!0,e.isCube=!0,e.samples=1,e.generateMipMaps=b.generateMipMaps,e.samplingMode=b.samplingMode,e.type=b.type,e.format=b.format,this._internalTexturesCache.push(e),c.setTextures(e),c};var cd=function(a){function b(b,c,d,e,h,i,j,k,t,u,v,w,x,y,z){void 0===h&&(h=!0),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===j&&(j=!1),void 0===k&&(k=V.a.TRILINEAR_SAMPLINGMODE),void 0===t&&(t=!0),void 0===u&&(u=!1),void 0===v&&(v=!1),void 0===w&&(w=r.a.TEXTUREFORMAT_RGBA),void 0===x&&(x=!1);k=a.call(this,null,d,!e,void 0,k,void 0,void 0,void 0,void 0,w)||this;if(k.renderParticles=!0,k.renderSprites=!1,k.ignoreCameraViewport=!1,k.onBeforeBindObservable=new f.c,k.onAfterUnbindObservable=new f.c,k.onBeforeRenderObservable=new f.c,k.onAfterRenderObservable=new f.c,k.onClearObservable=new f.c,k.onResizeObservable=new f.c,k._cleared=!1,k.skipInitialClear=!1,k._currentRefreshId=-1,k._refreshRate=1,k._samples=1,k._canRescale=!0,k._renderTarget=null,k.boundingBoxPosition=g.e.Zero(),!(d=k.getScene()))return k;w=k.getScene().getEngine();return k._coordinatesMode=V.a.PROJECTION_MODE,k.renderList=new Array,k.name=b,k.isRenderTarget=!0,k._initialSizeParameter=c,k._processSizeParameter(c),k._resizeObserver=w.onResizeObservable.add(function(){}),k._generateMipMaps=!!e,k._doNotChangeAspectRatio=h,k._renderingManager=new bd.b(d),k._renderingManager._useSceneAutoClearSetup=!0,v||(k._renderTargetOptions={generateMipMaps:e,type:i,format:null!==(b=k._format)&&void 0!==b?b:void 0,samplingMode:k.samplingMode,generateDepthBuffer:t,generateStencilBuffer:u,samples:y,creationFlags:z},k.samplingMode===V.a.NEAREST_SAMPLINGMODE&&(k.wrapU=V.a.CLAMP_ADDRESSMODE,k.wrapV=V.a.CLAMP_ADDRESSMODE),x||(j?(k._renderTarget=d.getEngine().createRenderTargetCubeTexture(k.getRenderSize(),k._renderTargetOptions),k.coordinatesMode=V.a.INVCUBIC_MODE,k._textureMatrix=g.a.Identity()):k._renderTarget=d.getEngine().createRenderTargetTexture(k._size,k._renderTargetOptions),k._texture=k._renderTarget.texture,void 0!==y&&(k.samples=y))),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"renderList",{get:function(){return this._renderList},set:function(a){this._renderList=a,this._renderList&&this._hookArray(this._renderList)},enumerable:!1,configurable:!0}),b.prototype._hookArray=function(a){var b=this,c=a.push;a.push=function(){for(var d,e=[],f=0;f0&&(this._postProcesses[0].autoClear=!1))}},b.prototype._shouldRender=function(){return-1===this._currentRefreshId||this.refreshRate===this._currentRefreshId?(this._currentRefreshId=1,!0):(this._currentRefreshId++,!1)},b.prototype.getRenderSize=function(){return this.getRenderWidth()},b.prototype.getRenderWidth=function(){return this._size.width?this._size.width:this._size},b.prototype.getRenderHeight=function(){return this._size.width?this._size.height:this._size},b.prototype.getRenderLayers=function(){var a=this._size.layers;return a||0},b.prototype.disableRescaling=function(){this._canRescale=!1},Object.defineProperty(b.prototype,"canRescale",{get:function(){return this._canRescale},enumerable:!1,configurable:!0}),b.prototype.scale=function(a){a=Math.max(1,this.getRenderSize()*a);this.resize(a)},b.prototype.getReflectionTextureMatrix=function(){return this.isCube?this._textureMatrix:a.prototype.getReflectionTextureMatrix.call(this)},b.prototype.resize=function(a){var b,c=this.isCube;null===(b=this._renderTarget)||void 0===b||b.dispose(),this._renderTarget=null;b=this.getScene();b&&(this._processSizeParameter(a),this._renderTarget=c?b.getEngine().createRenderTargetCubeTexture(this.getRenderSize(),this._renderTargetOptions):b.getEngine().createRenderTargetTexture(this._size,this._renderTargetOptions),this._texture=this._renderTarget.texture,void 0!==this._renderTargetOptions.samples&&(this.samples=this._renderTargetOptions.samples),this.onResizeObservable.hasObservers()&&this.onResizeObservable.notifyObservers(this))},b.prototype.render=function(a,b){if(void 0===a&&(a=!1),void 0===b&&(b=!1),f=this.getScene()){var c=f.getEngine();if(void 0!==this.useCameraPostProcesses&&(a=this.useCameraPostProcesses),this._waitingRenderList){this.renderList=[];for(var d=0;d1||this.activeCamera&&this.activeCamera!==f.activeCamera)&&f.setTransformMatrix(f.activeCamera.getViewMatrix(),f.activeCamera.getProjectionMatrix(!0)),c.setViewport(f.activeCamera.viewport)),f.resetCachedMaterial()}},b.prototype._bestReflectionRenderTargetDimension=function(a,b){b=a*b;b=T.a.NearestPOT(b+16384/(128+b));return Math.min(T.a.FloorPOT(a),b)},b.prototype._prepareRenderingManager=function(a,b,c,d){var e=this.getScene();if(e){this._renderingManager.reset();for(var f=e.getRenderId(),g=0;g=0&&this._renderingManager.dispatchParticles(i)}}},b.prototype._bindFrameBuffer=function(a,b){void 0===a&&(a=0),void 0===b&&(b=0);var c=this.getScene();if(c){c=c.getEngine();this._renderTarget&&c.bindFramebuffer(this._renderTarget,this.isCube?a:void 0,void 0,void 0,this.ignoreCameraViewport,0,b)}},b.prototype.unbindFrameBuffer=function(a,b){var c=this;this._renderTarget&&a.unBindFramebuffer(this._renderTarget,this.isCube,function(){c.onAfterRenderObservable.notifyObservers(b)})},b.prototype._prepareFrame=function(a,b,c,d){this._postProcessManager?this._prePassEnabled||this._postProcessManager._prepareFrame(this._texture,this._postProcesses):d&&a.postProcessManager._prepareFrame(this._texture)||this._bindFrameBuffer(b,c)},b.prototype.renderToTarget=function(a,b,c,d,e){var f;void 0===d&&(d=0),void 0===e&&(e=null);var g=this.getScene();if(g){var h=g.getEngine();if(this._texture){if(null===(f=h._debugPushGroup)||void 0===f||f.call(h,"render to face #"+a+" layer #"+d,1),this._prepareFrame(g,a,d,b),this.is2DArray?this.onBeforeRenderObservable.notifyObservers(d):this.onBeforeRenderObservable.notifyObservers(a),h.snapshotRendering&&h.snapshotRenderingMode===r.a.SNAPSHOTRENDERING_FAST)this.onClearObservable.hasObservers()?this.onClearObservable.notifyObservers(h):this.skipInitialClear||h.clear(this.clearColor||g.clearColor,!0,!0,!0);else{f=null;var i=this.renderList?this.renderList:g.getActiveMeshes().data,j=this.renderList?this.renderList.length:g.getActiveMeshes().length;this.getCustomRenderList&&(f=this.getCustomRenderList(this.is2DArray?d:a,i,j)),f?this._prepareRenderingManager(f,f.length,e,!1):(this._defaultRenderListPrepared||(this._prepareRenderingManager(i,j,e,!this.renderList),this._defaultRenderListPrepared=!0),f=i);for(j=0,e=g._beforeRenderTargetClearStage;j=0&&b.customRenderTargets.splice(c,1);for(var d=0,b=b.cameras;d=0&&e.customRenderTargets.splice(c,1)}null===(e=this._renderTarget)||void 0===e||e.dispose(),this._renderTarget=null,this._texture=null,a.prototype.dispose.call(this)}},b.prototype._rebuild=function(){this.refreshRate===b.REFRESHRATE_RENDER_ONCE&&(this.refreshRate=b.REFRESHRATE_RENDER_ONCE),this._postProcessManager&&this._postProcessManager._rebuild()},b.prototype.freeRenderingGroups=function(){this._renderingManager&&this._renderingManager.freeRenderingGroups()},b.prototype.getViewCount=function(){return 1},b.REFRESHRATE_RENDER_ONCE=0,b.REFRESHRATE_RENDER_ONEVERYFRAME=1,b.REFRESHRATE_RENDER_ONEVERYTWOFRAMES=2,b}(V.a);V.a._CreateRenderTargetTexture=function(a,b,c,d,e){return new cd(a,b,c,d)};var dd=function(a){function b(b,c){void 0===c&&(c=512);c=a.call(this,"multiview rtt",c,b,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT,!1,void 0,!1,!1,!0,void 0,!0)||this;b=b.getEngine().createMultiviewRenderTargetTexture(c.getRenderWidth(),c.getRenderHeight());return c._texture=b.texture,c._texture.isMultiview=!0,c._texture.format=r.a.TEXTUREFORMAT_RGBA,c.samples=c._getEngine().getCaps().maxSamples||c.samples,c}return Object(l.d)(b,a),b.prototype._bindFrameBuffer=function(a){this._renderTarget&&this.getScene().getEngine().bindMultiviewFramebuffer(this._renderTarget)},b.prototype.getViewCount=function(){return 2},b}(cd),ed=c(96);T.a.prototype.createMultiviewRenderTargetTexture=function(a,b){var c=this._gl;if(!this.getCaps().multiview)throw"Multiview is not supported";var d=this._createHardwareRenderTargetWrapper(!1,!1,{width:a,height:b});d._framebuffer=c.createFramebuffer();var e=new ob.a(this,ob.b.Unknown,!0);return e.width=a,e.height=b,d._colorTextureArray=c.createTexture(),c.bindTexture(c.TEXTURE_2D_ARRAY,d._colorTextureArray),c.texStorage3D(c.TEXTURE_2D_ARRAY,1,c.RGBA8,a,b,2),d._depthStencilTextureArray=c.createTexture(),c.bindTexture(c.TEXTURE_2D_ARRAY,d._depthStencilTextureArray),c.texStorage3D(c.TEXTURE_2D_ARRAY,1,c.DEPTH32F_STENCIL8,a,b,2),e.isReady=!0,d.setTextures(e),d},T.a.prototype.bindMultiviewFramebuffer=function(a){a=a;var b=this._gl,c=this.getCaps().oculusMultiview||this.getCaps().multiview;if(this.bindFramebuffer(a,void 0,void 0,void 0,!0),b.bindFramebuffer(b.DRAW_FRAMEBUFFER,a._framebuffer),!a._colorTextureArray||!a._depthStencilTextureArray)throw"Invalid multiview frame buffer";this.getCaps().oculusMultiview?(c.framebufferTextureMultisampleMultiviewOVR(b.DRAW_FRAMEBUFFER,b.COLOR_ATTACHMENT0,a._colorTextureArray,0,a.samples,0,2),c.framebufferTextureMultisampleMultiviewOVR(b.DRAW_FRAMEBUFFER,b.DEPTH_STENCIL_ATTACHMENT,a._depthStencilTextureArray,0,a.samples,0,2)):(c.framebufferTextureMultiviewOVR(b.DRAW_FRAMEBUFFER,b.COLOR_ATTACHMENT0,a._colorTextureArray,0,0,2),c.framebufferTextureMultiviewOVR(b.DRAW_FRAMEBUFFER,b.DEPTH_STENCIL_ATTACHMENT,a._depthStencilTextureArray,0,0,2))},cb.a.prototype._useMultiviewToSingleView=!1,cb.a.prototype._multiviewTexture=null,cb.a.prototype._resizeOrCreateMultiviewTexture=function(a,b){this._multiviewTexture?this._multiviewTexture.getRenderWidth()==a&&this._multiviewTexture.getRenderHeight()==b||(this._multiviewTexture.dispose(),this._multiviewTexture=new dd(this.getScene(),{width:a,height:b})):this._multiviewTexture=new dd(this.getScene(),{width:a,height:b})},P.a.prototype._transformMatrixR=g.a.Zero(),P.a.prototype._multiviewSceneUbo=null,P.a.prototype._createMultiviewUbo=function(){this._multiviewSceneUbo=new $c.a(this.getEngine(),void 0,!0,"scene_multiview"),this._multiviewSceneUbo.addUniform("viewProjection",16),this._multiviewSceneUbo.addUniform("viewProjectionR",16),this._multiviewSceneUbo.addUniform("view",16),this._multiviewSceneUbo.addUniform("projection",16),this._multiviewSceneUbo.addUniform("viewPosition",4)},P.a.prototype._updateMultiviewUbo=function(a,b){a&&b&&a.multiplyToRef(b,this._transformMatrixR),a&&b&&(a.multiplyToRef(b,g.c.Matrix[0]),ed.a.GetRightPlaneToRef(g.c.Matrix[0],this._frustumPlanes[3])),this._multiviewSceneUbo&&(this._multiviewSceneUbo.updateMatrix("viewProjection",this.getTransformMatrix()),this._multiviewSceneUbo.updateMatrix("viewProjectionR",this._transformMatrixR),this._multiviewSceneUbo.updateMatrix("view",this._viewMatrix),this._multiviewSceneUbo.updateMatrix("projection",this._projectionMatrix))},P.a.prototype._renderMultiviewToSingleView=function(a){a._resizeOrCreateMultiviewTexture(a._rigPostProcess&&a._rigPostProcess&&a._rigPostProcess.width>0?a._rigPostProcess.width:this.getEngine().getRenderWidth(!0),a._rigPostProcess&&a._rigPostProcess&&a._rigPostProcess.height>0?a._rigPostProcess.height:this.getEngine().getRenderHeight(!0)),this._multiviewSceneUbo||this._createMultiviewUbo(),a.outputRenderTarget=a._multiviewTexture,this._renderForCamera(a),a.outputRenderTarget=null;for(var b=0;b=2&&a.onControllersAttachedObservable.notifyObservers(a.controllers)}}})},b}(dc),nd=function(a){function b(b){var c=a.call(this,b)||this;return c.onTriggerStateChangedObservable=new f.c,c.onMainButtonStateChangedObservable=new f.c,c.onSecondaryButtonStateChangedObservable=new f.c,c.onPadStateChangedObservable=new f.c,c.onPadValuesChangedObservable=new f.c,c.pad={x:0,y:0},c._changes={pressChanged:!1,touchChanged:!1,valueChanged:!1,changed:!1},c._buttons=new Array(b.buttons.length),c.hand=b.hand,c}return Object(l.d)(b,a),b.prototype.onButtonStateChange=function(a){this._onButtonStateChange=a},Object.defineProperty(b.prototype,"defaultModel",{get:function(){return this._defaultModel},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this);for(var b=0;b #include #include void main(void) { vec4 result=texture2D(textureSampler,vUV); #ifdef IMAGEPROCESSING #ifndef FROMLINEARSPACE result.rgb=toLinearSpace(result.rgb); #endif result=applyImageProcessing(result); #else #ifdef FROMLINEARSPACE result=applyImageProcessing(result); #endif #endif gl_FragColor=result; }");X.a.ShadersStore.imageProcessingPixelShader=rb;var rd,sd,td=function(a){function b(b,c,d,e,f,g,h,i){void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT);var j=a.call(this,b,"imageProcessing",[],[],c,d,e,f,g,null,h,"postprocess",null,!0)||this;return j._fromLinearSpace=!0,j._defines={IMAGEPROCESSING:!1,VIGNETTE:!1,VIGNETTEBLENDMODEMULTIPLY:!1,VIGNETTEBLENDMODEOPAQUE:!1,TONEMAPPING:!1,TONEMAPPING_ACES:!1,CONTRAST:!1,COLORCURVES:!1,COLORGRADING:!1,COLORGRADING3D:!1,FROMLINEARSPACE:!1,SAMPLER3DGREENDEPTH:!1,SAMPLER3DBGRMAP:!1,IMAGEPROCESSINGPOSTPROCESS:!1,EXPOSURE:!1},i?(i.applyByPostProcess=!0,j._attachImageProcessingConfiguration(i,!0),j.fromLinearSpace=!1):(j._attachImageProcessingConfiguration(null,!0),j.imageProcessingConfiguration.applyByPostProcess=!0),j.onApply=function(a){j.imageProcessingConfiguration.bind(a,j.aspectRatio)},j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){a.applyByPostProcess=!0,this._attachImageProcessingConfiguration(a)},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a,b){var c=this;if(void 0===b&&(b=!1),a!==this._imageProcessingConfiguration){if(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a)this._imageProcessingConfiguration=a;else{a=this.getEngine();var d=this.getCamera();if(d)d=d.getScene();else if(a&&a.scenes){a=a.scenes;d=a[a.length-1]}else d=C.a.LastCreatedScene;this._imageProcessingConfiguration=d?d.imageProcessingConfiguration:new od.a}this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){c._updateParameters()})),b||this._updateParameters()}},Object.defineProperty(b.prototype,"isSupported",{get:function(){var a=this.getEffect();return!a||a.isSupported},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorCurves",{get:function(){return this.imageProcessingConfiguration.colorCurves},set:function(a){this.imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorGradingTexture",{get:function(){return this.imageProcessingConfiguration.colorGradingTexture},set:function(a){this.imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"exposure",{get:function(){return this.imageProcessingConfiguration.exposure},set:function(a){this.imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toneMappingType",{get:function(){return this._imageProcessingConfiguration.toneMappingType},set:function(a){this._imageProcessingConfiguration.toneMappingType=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"contrast",{get:function(){return this.imageProcessingConfiguration.contrast},set:function(a){this.imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteStretch",{get:function(){return this.imageProcessingConfiguration.vignetteStretch},set:function(a){this.imageProcessingConfiguration.vignetteStretch=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCentreX",{get:function(){return this.imageProcessingConfiguration.vignetteCentreX},set:function(a){this.imageProcessingConfiguration.vignetteCentreX=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCentreY",{get:function(){return this.imageProcessingConfiguration.vignetteCentreY},set:function(a){this.imageProcessingConfiguration.vignetteCentreY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteWeight",{get:function(){return this.imageProcessingConfiguration.vignetteWeight},set:function(a){this.imageProcessingConfiguration.vignetteWeight=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteColor",{get:function(){return this.imageProcessingConfiguration.vignetteColor},set:function(a){this.imageProcessingConfiguration.vignetteColor=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCameraFov",{get:function(){return this.imageProcessingConfiguration.vignetteCameraFov},set:function(a){this.imageProcessingConfiguration.vignetteCameraFov=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteBlendMode",{get:function(){return this.imageProcessingConfiguration.vignetteBlendMode},set:function(a){this.imageProcessingConfiguration.vignetteBlendMode=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteEnabled",{get:function(){return this.imageProcessingConfiguration.vignetteEnabled},set:function(a){this.imageProcessingConfiguration.vignetteEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fromLinearSpace",{get:function(){return this._fromLinearSpace},set:function(a){this._fromLinearSpace!==a&&(this._fromLinearSpace=a,this._updateParameters())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"ImageProcessingPostProcess"},b.prototype._updateParameters=function(){this._defines.FROMLINEARSPACE=this._fromLinearSpace,this.imageProcessingConfiguration.prepareDefines(this._defines,!0);var a="";for(var b in this._defines)this._defines[b]&&(a+="#define "+b+"; ");b=["textureSampler"];var c=["scale"];od.a&&(od.a.PrepareSamplers(b,this._defines),od.a.PrepareUniforms(c,this._defines)),this.updateEffect(a,c,b)},b.prototype.dispose=function(b){a.prototype.dispose.call(this,b),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration&&(this.imageProcessingConfiguration.applyByPostProcess=!1)},Object(l.c)([Object(J.d)()],b.prototype,"_fromLinearSpace",void 0),b}(Fc),ud=function(){function a(){}return a.GetDefaults=function(b){var c=new a;return c.canvasOptions={antialias:!0,depth:!0,stencil:!b||b.isStencilEnable,alpha:!0,multiview:!1,framebufferScaleFactor:1},c.newCanvasCssStyle="position:absolute; bottom:0px;right:0px;z-index:10;width:90%;height:100%;background-color: #000000;",c},a}(),vd=function(){function a(a,b){var c=this;if(void 0===b&&(b=ud.GetDefaults()),this._options=b,this._canvas=null,this._engine=null,this.xrLayer=null,this.onXRLayerInitObservable=new f.c,this._engine=a.scene.getEngine(),this._engine.onDisposeObservable.addOnce(function(){c._engine=null}),b.canvasElement)this._setManagedOutputCanvas(b.canvasElement);else{b=document.createElement("canvas");b.style.cssText=this._options.newCanvasCssStyle||"position:absolute; bottom:0px;right:0px;",this._setManagedOutputCanvas(b)}a.onXRSessionInit.add(function(){c._addCanvas()}),a.onXRSessionEnded.add(function(){c._removeCanvas()})}return a.prototype.dispose=function(){this._removeCanvas(),this._setManagedOutputCanvas(null)},a.prototype.initializeXRLayerAsync=function(a){var b=this,c=function(){var c=new XRWebGLLayer(a,b.canvasContext,b._options.canvasOptions);return b.onXRLayerInitObservable.notifyObservers(c),c};return this.canvasContext.makeXRCompatible?this.canvasContext.makeXRCompatible().then(function(){},function(){U.b.Warn("Error executing makeXRCompatible. This does not mean that the session will work incorrectly.")}).then(function(){return b.xrLayer=c(),b.xrLayer}):(this.xrLayer=c(),Promise.resolve(this.xrLayer))},a.prototype._addCanvas=function(){var a=this;this._canvas&&this._engine&&this._canvas!==this._engine.getRenderingCanvas()&&document.body.appendChild(this._canvas),this.xrLayer?this._setCanvasSize(!0):this.onXRLayerInitObservable.addOnce(function(b){a._setCanvasSize(!0,b)})},a.prototype._removeCanvas=function(){this._canvas&&this._engine&&document.body.contains(this._canvas)&&this._canvas!==this._engine.getRenderingCanvas()&&document.body.removeChild(this._canvas),this._setCanvasSize(!1)},a.prototype._setCanvasSize=function(a,b){void 0===a&&(a=!0),void 0===b&&(b=this.xrLayer),this._canvas&&this._engine&&(a?b&&(this._canvas!==this._engine.getRenderingCanvas()?(this._canvas.style.width=b.framebufferWidth+"px",this._canvas.style.height=b.framebufferHeight+"px"):this._engine.setSize(b.framebufferWidth,b.framebufferHeight)):this._originalCanvasSize&&(this._canvas!==this._engine.getRenderingCanvas()?(this._canvas.style.width=this._originalCanvasSize.width+"px",this._canvas.style.height=this._originalCanvasSize.height+"px"):this._engine.setSize(this._originalCanvasSize.width,this._originalCanvasSize.height)))},a.prototype._setManagedOutputCanvas=function(a){this._removeCanvas(),a?(this._originalCanvasSize={width:a.offsetWidth,height:a.offsetHeight},this._canvas=a,this.canvasContext=this._canvas.getContext("webgl2"),this.canvasContext||(this.canvasContext=this._canvas.getContext("webgl"))):(this._canvas=null,this.canvasContext=null)},a}(),wd=function(){function a(a){var b=this;this.scene=a,this._sessionEnded=!1,this._baseLayer=null,this._renderTargetTextures=[],this.currentTimestamp=-1,this.defaultHeightCompensation=1.7,this.onXRFrameObservable=new f.c,this.onXRReferenceSpaceChanged=new f.c,this.onXRSessionEnded=new f.c,this.onXRSessionInit=new f.c,this._engine=a.getEngine(),this._engine.onDisposeObservable.addOnce(function(){b._engine=null})}return Object.defineProperty(a.prototype,"referenceSpace",{get:function(){return this._referenceSpace},set:function(a){this._referenceSpace=a,this.onXRReferenceSpaceChanged.notifyObservers(this._referenceSpace)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"sessionMode",{get:function(){return this._sessionMode},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this._sessionEnded||this.exitXRAsync(),this.onXRFrameObservable.clear(),this.onXRSessionEnded.clear(),this.onXRReferenceSpaceChanged.clear(),this.onXRSessionInit.clear()},a.prototype.exitXRAsync=function(){return this.session&&!this._sessionEnded?(this._sessionEnded=!0,this.session.end()["catch"](function(a){q.a.Warn("Could not end XR session.")})):Promise.resolve()},a.prototype.getRenderTargetTextureForEye=function(a){return this._rttProvider.getRenderTargetForEye(a)},a.prototype.getWebXRRenderTarget=function(a){var b=this.scene.getEngine();return this._xrNavigator.xr["native"]?this._xrNavigator.xr.getWebXRRenderTarget(b):((a=a||ud.GetDefaults(b)).canvasElement=a.canvasElement||b.getRenderingCanvas()||void 0,new vd(this,a))},a.prototype.initializeAsync=function(){return this._xrNavigator=navigator,this._xrNavigator.xr?Promise.resolve():Promise.reject("WebXR not available")},a.prototype.initializeSessionAsync=function(a,b){var c=this;return void 0===a&&(a="immersive-vr"),void 0===b&&(b={}),this._xrNavigator.xr.requestSession(a,b).then(function(b){return c.session=b,c._sessionMode=a,c.onXRSessionInit.notifyObservers(b),c._sessionEnded=!1,c.session.addEventListener("end",function(){c._sessionEnded=!0,c.onXRSessionEnded.notifyObservers(null),c._rttProvider=null,c._engine&&(c._engine.framebufferDimensionsObject=null,c._engine.restoreDefaultFramebuffer(),c._engine.customAnimationFrameRequester=null,c._engine._renderLoop()),c.isNative&&(c._renderTargetTextures.forEach(function(a){return a.dispose()}),c._renderTargetTextures.length=0)},{once:!0}),c.session})},a.prototype.isSessionSupportedAsync=function(b){return a.IsSessionSupportedAsync(b)},a.prototype.resetReferenceSpace=function(){this.referenceSpace=this.baseReferenceSpace},a.prototype.runXRRenderLoop=function(){var a=this;if(!this._sessionEnded&&this._engine){var b,c,d,e;(this._engine.customAnimationFrameRequester={requestAnimationFrame:this.session.requestAnimationFrame.bind(this.session),renderFunction:function(b,c){!a._sessionEnded&&a._engine&&(a.currentFrame=c,a.currentTimestamp=b,c&&(a._engine.framebufferDimensionsObject=a._baseLayer,a.onXRFrameObservable.notifyObservers(c),a._engine._renderLoop(),a._engine.framebufferDimensionsObject=null))}},this._xrNavigator.xr["native"])?this._rttProvider=this._xrNavigator.xr.getNativeRenderTargetProvider(this.session,this._createRenderTargetTexture.bind(this),this._destroyRenderTargetTexture.bind(this)):(this._rttProvider={getRenderTargetForEye:function(){var f=a._baseLayer;return f.framebufferWidth===c&&f.framebufferHeight===d&&f.framebuffer===e||(b=a._createRenderTargetTexture(f.framebufferWidth,f.framebufferHeight,f.framebuffer),c=f.framebufferWidth,d=f.framebufferHeight,e=f.framebuffer),b}},this._engine.framebufferDimensionsObject=this._baseLayer);"undefined"!=typeof window&&window.cancelAnimationFrame&&window.cancelAnimationFrame(this._engine._frameHandler),this._engine._renderLoop()}},a.prototype.setReferenceSpaceTypeAsync=function(a){var b=this;return void 0===a&&(a="local-floor"),this.session.requestReferenceSpace(a).then(function(a){return a},function(a){return q.a.Error("XR.requestReferenceSpace failed for the following reason: "),q.a.Error(a),q.a.Log("Defaulting to universally-supported "viewer" reference space type."),b.session.requestReferenceSpace("viewer").then(function(a){var c=new XRRigidTransform({x:0,y:-b.defaultHeightCompensation,z:0});return a.getOffsetReferenceSpace(c)},function(a){throw q.a.Error(a),"XR initialization failed: required "viewer" reference space type not supported."})}).then(function(a){return b.session.requestReferenceSpace("viewer").then(function(c){return b.viewerReferenceSpace=c,a})}).then(function(a){return b.referenceSpace=b.baseReferenceSpace=a,b.referenceSpace})},a.prototype.updateRenderStateAsync=function(a){return a.baseLayer&&(this._baseLayer=a.baseLayer),this.session.updateRenderState(a)},a.IsSessionSupportedAsync=function(a){if(!navigator.xr)return Promise.resolve(!1);var b=navigator.xr.isSessionSupported||navigator.xr.supportsSession;return b?b.call(navigator.xr,a).then(function(a){a=void 0===a||a;return Promise.resolve(a)})["catch"](function(a){return q.a.Warn(a),Promise.resolve(!1)}):Promise.resolve(!1)},Object.defineProperty(a.prototype,"isNative",{get:function(){var a;return null!==(a=this._xrNavigator.xr["native"])&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentFrameRate",{get:function(){var a;return null===(a=this.session)||void 0===a?void 0:a.frameRate},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportedFrameRates",{get:function(){var a;return null===(a=this.session)||void 0===a?void 0:a.supportedFrameRates},enumerable:!1,configurable:!0}),a.prototype.updateTargetFrameRate=function(a){return this.session.updateTargetFrameRate(a)},Object.defineProperty(a.prototype,"isFixedFoveationSupported",{get:function(){var a;return null!==!!(null===(a=this._baseLayer)||void 0===a?void 0:a.fixedFoveation)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"fixedFoveation",{get:function(){var a;return void 0!==(null===(a=this._baseLayer)||void 0===a?void 0:a.fixedFoveation)?this._baseLayer.fixedFoveation:null},set:function(a){var b;a=Math.max(0,Math.min(1,a||0));void 0!==(null===(b=this._baseLayer)||void 0===b?void 0:b.fixedFoveation)&&(this._baseLayer.fixedFoveation=a)},enumerable:!1,configurable:!0}),a.prototype._createRenderTargetTexture=function(a,b,c){if(!this._engine)throw new Error("Engine is disposed");var d=new ob.a(this._engine,ob.b.Unknown,!0);d.width=a,d.height=b;a=new cd("XR renderTargetTexture",{width:a,height:b},this.scene);b=a.renderTarget;return b.setTexture(d,0),b._framebuffer=c,a._texture=d,a.disableRescaling(),"immersive-ar"===this._sessionMode&&(a.skipInitialClear=!0),this._renderTargetTextures.push(a),a},a.prototype._destroyRenderTargetTexture=function(a){this._renderTargetTextures.splice(this._renderTargetTextures.indexOf(a),1),a.dispose()},a}();!function(a){a[a.ENTERING_XR=0]="ENTERING_XR",a[a.EXITING_XR=1]="EXITING_XR",a[a.IN_XR=2]="IN_XR",a[a.NOT_IN_XR=3]="NOT_IN_XR"}(rd||(rd={})),(function(a){a[a.NOT_TRACKING=0]="NOT_TRACKING",a[a.TRACKING_LOST=1]="TRACKING_LOST",a[a.TRACKING=2]="TRACKING"})(sd||(sd={}));var xd=c(56),yd=c(18);function zd(a){for(var b=[],c=[],d=[],e=[],f=a.diameter||1,h=a.thickness||.5,i=a.tessellation||16,j=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,k=i+1,o=0;o<=i;o++)for(var t=o/i,u=o*Math.PI*2/i-Math.PI/2,u=g.a.Translation(f/2,0,0).multiply(g.a.RotationY(u)),v=0;v<=i;v++){var w=1-v/i,x=v*Math.PI*2/i+Math.PI,y=Math.cos(x);x=Math.sin(x);y=new g.e(y,x,0);x=y.scale(h/2);w=new g.d(t,w);x=g.e.TransformCoordinates(x,u),y=g.e.TransformNormal(y,u),c.push(x.x,x.y,x.z),d.push(y.x,y.y,y.z),e.push(w.x,w.y);x=(o+1)%k;y=(v+1)%k;b.push(o*k+v),b.push(o*k+y),b.push(x*k+v),b.push(o*k+y),b.push(x*k+y),b.push(x*k+v)}yd.a._ComputeSides(j,c,b,d,e,a.frontUVs,a.backUVs);w=new yd.a;return w.indices=b,w.positions=c,w.normals=d,w.uvs=e,w}function Ad(a,b,c){a=new S.a(a,c);return b.sideOrientation=S.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,zd(b).applyToMesh(a,b.updatable),a}var Bd={CreateTorus:Ad};yd.a.CreateTorus=zd,S.a.CreateTorus=function(a,b,c,d,e,f,g){return Ad(a,{diameter:b,thickness:c,tessellation:d,sideOrientation:g,updatable:f},e)},S.a._GroundMeshParser=function(a,b){return Cd.Parse(a,b)};var Cd=function(a){function b(b,c){b=a.call(this,b,c)||this;return b.generateOctree=!1,b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"GroundMesh"},Object.defineProperty(b.prototype,"subdivisions",{get:function(){return Math.min(this._subdivisionsX,this._subdivisionsY)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"subdivisionsX",{get:function(){return this._subdivisionsX},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"subdivisionsY",{get:function(){return this._subdivisionsY},enumerable:!1,configurable:!0}),b.prototype.optimize=function(a,b){(void 0===b&&(b=32),this._subdivisionsX=a,this._subdivisionsY=a,this.subdivide(a)),this.createOrUpdateSubmeshesOctree&&this.createOrUpdateSubmeshesOctree(b)},b.prototype.getHeightAtCoordinates=function(a,b){var c=this.getWorldMatrix(),d=g.c.Matrix[5];c.invertToRef(d);var e=g.c.Vector3[8];if(g.e.TransformCoordinatesFromFloatsToRef(a,0,b,d,e),a=e.x,b=e.z,athis._maxX||bthis._maxZ)return this.position.y;this._heightQuads&&0!=this._heightQuads.length||(this._initHeightQuads(),this._computeHeightQuads());d=this._getFacetAt(a,b);a=-(d.x*a+d.z*b+d.w)/d.y;return g.e.TransformCoordinatesFromFloatsToRef(0,a,0,c,e),e.y},b.prototype.getNormalAtCoordinates=function(a,b){var c=new g.e(0,1,0);return this.getNormalAtCoordinatesToRef(a,b,c),c},b.prototype.getNormalAtCoordinatesToRef=function(a,b,c){var d=this.getWorldMatrix(),e=g.c.Matrix[5];d.invertToRef(e);var f=g.c.Vector3[8];if(g.e.TransformCoordinatesFromFloatsToRef(a,0,b,e,f),a=f.x,b=f.z,athis._maxX||bthis._maxZ)return this;this._heightQuads&&0!=this._heightQuads.length||(this._initHeightQuads(),this._computeHeightQuads());e=this._getFacetAt(a,b);return g.e.TransformNormalFromFloatsToRef(e.x,e.y,e.z,d,c),this},b.prototype.updateCoordinateHeights=function(){return this._heightQuads&&0!=this._heightQuads.length||this._initHeightQuads(),this._computeHeightQuads(),this},b.prototype._getFacetAt=function(a,b){var c=Math.floor((a+this._maxX)*this._subdivisionsX/this._width),d=Math.floor(-(b+this._maxZ)*this._subdivisionsY/this._height+this._subdivisionsY);d=this._heightQuads[d*this._subdivisionsX+c];return ba.maxHeight){k=!0;var s=a.maxHeight;a.maxHeight=a.minHeight,a.minHeight=s}for(s=0;s<=a.subdivisions;s++)for(b=0;b<=a.subdivisions;b++){var t=new g.e(b*a.width/a.subdivisions-a.width/2,0,(a.subdivisions-s)*a.height/a.subdivisions-a.height/2),u=4*(((t.x+a.width/2)/a.width*(a.bufferWidth-1)|0)+((1-(t.z+a.height/2)/a.height)*(a.bufferHeight-1)|0)*a.bufferWidth),v=a.buffer[u]/255,w=a.buffer[u+1]/255,x=a.buffer[u+2]/255;u=a.buffer[u+3]/255;k&&(v=1-v,w=1-w,x=1-x);v=v*i.r+w*i.g+x*i.b;t.y=u>=j?a.minHeight+(a.maxHeight-a.minHeight)*v:a.minHeight-hb.a,d.push(t.x,t.y,t.z),e.push(0,0,0),f.push(b/a.subdivisions,1-s/a.subdivisions)}for(s=0;s=a.minHeight;k=d[3*x+1]>=a.minHeight;j=d[3*u+1]>=a.minHeight;t&&k&&j&&(c.push(w),c.push(x),c.push(u)),d[3*v+1]>=a.minHeight&&t&&j&&(c.push(v),c.push(w),c.push(u))}yd.a.ComputeNormals(d,c,e);i=new yd.a;return i.indices=c,i.positions=d,i.normals=e,i.uvs=f,i}function Gd(a,b,c){a=new Cd(a,c);return a._setReady(!1),a._subdivisionsX=b.subdivisionsX||b.subdivisions||1,a._subdivisionsY=b.subdivisionsY||b.subdivisions||1,a._width=b.width||1,a._height=b.height||1,a._maxX=a._width/2,a._maxZ=a._height/2,a._minX=-a._maxX,a._minZ=-a._maxZ,Dd(b).applyToMesh(a,b.updatable),a._setReady(!0),a}function Hd(a,b,c){void 0===c&&(c=null);a=new S.a(a,c);return Ed(b).applyToMesh(a,b.updatable),a}function Id(a,b,c,d){void 0===d&&(d=null);var e=c.width||10,f=c.height||10,g=c.subdivisions||1,i=c.minHeight||0,j=c.maxHeight||1,k=c.colorFilter||new h.a(.3,.59,.11),p=c.alphaFilter||0,q=c.updatable,r=c.onReady;d=d||C.a.LastCreatedScene;var s=new Cd(a,d);s._subdivisionsX=g,s._subdivisionsY=g,s._width=e,s._height=f,s._maxX=s._width/2,s._maxZ=s._height/2,s._minX=-s._maxX,s._minZ=-s._maxZ,s._setReady(!1);return U.b.LoadImage(b,function(a){var b=a.width,c=a.height;if(!d.isDisposed){a=null==d?void 0:d.getEngine().resizeImageBitmap(a,b,c);Fd({width:e,height:f,subdivisions:g,minHeight:i,maxHeight:j,colorFilter:k,buffer:a,bufferWidth:b,bufferHeight:c,alphaFilter:p}).applyToMesh(s,q),r&&r(s),s._setReady(!0)}},function(){},d.offlineProvider),s}var Jd={CreateGround:Gd,CreateGroundFromHeightMap:Id,CreateTiledGround:Hd};yd.a.CreateGround=Dd,yd.a.CreateTiledGround=Ed,yd.a.CreateGroundFromHeightMap=Fd,S.a.CreateGround=function(a,b,c,d,e,f){return Gd(a,{width:b,height:c,subdivisions:d,updatable:f},e)},S.a.CreateTiledGround=function(a,b,c,d,e,f,g,h,i){return Hd(a,{xmin:b,zmin:c,xmax:d,zmax:e,subdivisions:f,precision:g,updatable:i},h)},S.a.CreateGroundFromHeightMap=function(a,b,c,d,e,f,g,h,i,j,k){return Id(a,b,{width:c,height:d,subdivisions:e,minHeight:f,maxHeight:g,updatable:i,onReady:j,alphaFilter:k},h)};var Kd;a=function(){function a(b,c){if(void 0===c&&(c=null),this.scene=b,this._pointerDownOnMeshAsked=!1,this._isActionableMesh=!1,this._teleportationRequestInitiated=!1,this._teleportationBackRequestInitiated=!1,this._rotationRightAsked=!1,this._rotationLeftAsked=!1,this._dpadPressed=!0,this._activePointer=!1,this._id=a._idCounter++,c)this._gazeTracker=c.clone("gazeTracker");else{this._gazeTracker=Ad("gazeTracker",{diameter:.0035,thickness:.0025,tessellation:20,updatable:!1},b),this._gazeTracker.bakeCurrentTransformIntoVertices(),this._gazeTracker.isPickable=!1,this._gazeTracker.isVisible=!1;c=new pd.a("targetMat",b);c.specularColor=h.a.Black(),c.emissiveColor=new h.a(.7,.7,.7),c.backFaceCulling=!1,this._gazeTracker.material=c}}return a.prototype._getForwardRay=function(a){return new nc.a(g.e.Zero(),new g.e(0,0,a))},a.prototype._selectionPointerDown=function(){this._pointerDownOnMeshAsked=!0,this._currentHit&&this.scene.simulatePointerDown(this._currentHit,{pointerId:this._id})},a.prototype._selectionPointerUp=function(){this._currentHit&&this.scene.simulatePointerUp(this._currentHit,{pointerId:this._id}),this._pointerDownOnMeshAsked=!1},a.prototype._activatePointer=function(){this._activePointer=!0},a.prototype._deactivatePointer=function(){this._activePointer=!1},a.prototype._updatePointerDistance=function(a){},a.prototype.dispose=function(){this._interactionsEnabled=!1,this._teleportationEnabled=!1,this._gazeTracker&&this._gazeTracker.dispose()},a._idCounter=0,a}();var Ld=function(a){function b(b,c,d){var e=a.call(this,c,d)||this;e.webVRController=b,e._laserPointer=Object(xd.a)("laserPointer",{updatable:!1,height:1,diameterTop:.004,diameterBottom:2e-4,tessellation:20,subdivisions:1},c);d=new pd.a("laserPointerMat",c);if(d.emissiveColor=new h.a(.7,.7,.7),d.alpha=.6,e._laserPointer.material=d,e._laserPointer.rotation.x=Math.PI/2,e._laserPointer.position.z=-.5,e._laserPointer.isVisible=!1,e._laserPointer.isPickable=!1,!b.mesh){d=new S.a("preloadControllerMesh",c);c=new S.a(rc.POINTING_POSE,c);c.rotation.x=-.7,d.addChild(c),b.attachToMesh(d)}return e._setLaserPointerParent(b.mesh),e._meshAttachedObserver=b._meshAttachedObservable.add(function(a){e._setLaserPointerParent(a)}),e}return Object(l.d)(b,a),b.prototype._getForwardRay=function(a){return this.webVRController.getForwardRay(a)},b.prototype._activatePointer=function(){a.prototype._activatePointer.call(this),this._laserPointer.isVisible=!0},b.prototype._deactivatePointer=function(){a.prototype._deactivatePointer.call(this),this._laserPointer.isVisible=!1},b.prototype._setLaserPointerColor=function(a){this._laserPointer.material.emissiveColor=a},b.prototype._setLaserPointerLightingDisabled=function(a){this._laserPointer.material.disableLighting=a},b.prototype._setLaserPointerParent=function(a){var b=function(a){a.isPickable=!1,a.getChildMeshes().forEach(function(a){b(a)})};b(a);var c=a.getChildren(void 0,!1);a=a;this.webVRController._pointingPoseNode=null;for(var d=0;d=0){a=c[d],this.webVRController._pointingPoseNode=a;break}this._laserPointer.parent=a},b.prototype._updatePointerDistance=function(a){void 0===a&&(a=100),this._laserPointer.scaling.y=a,this._laserPointer.position.z=-a/2},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._laserPointer.dispose(),this._meshAttachedObserver&&this.webVRController._meshAttachedObservable.remove(this._meshAttachedObserver)},b}(a),Md=function(a){function b(b,c){c=a.call(this,c)||this;return c.getCamera=b,c}return Object(l.d)(b,a),b.prototype._getForwardRay=function(a){var b=this.getCamera();return b?b.getForwardRay(a):new nc.a(g.e.Zero(),g.e.Forward())},b}(a),Nd=function(){},Od=function(){function a(b,c){var d=this;if(void 0===c&&(c={}),this.webVROptions=c,this._webVRsupported=!1,this._webVRready=!1,this._webVRrequesting=!1,this._webVRpresenting=!1,this._fullscreenVRpresenting=!1,this.enableGazeEvenWhenNoPointerLock=!1,this.exitVROnDoubleTap=!0,this.onEnteringVRObservable=new f.c,this.onAfterEnteringVRObservable=new f.c,this.onExitingVRObservable=new f.c,this.onControllerMeshLoadedObservable=new f.c,this._useCustomVRButton=!1,this._teleportationRequested=!1,this._teleportActive=!1,this._floorMeshesCollection=[],this._teleportationMode=a.TELEPORTATIONMODE_CONSTANTTIME,this._teleportationTime=122,this._teleportationSpeed=20,this._rotationAllowed=!0,this._teleportBackwardsVector=new g.e(0,-1,-1),this._isDefaultTeleportationTarget=!0,this._teleportationFillColor="#444444",this._teleportationBorderColor="#FFFFFF",this._rotationAngle=0,this._haloCenter=new g.e(0,0,0),this._padSensibilityUp=.65,this._padSensibilityDown=.35,this._leftController=null,this._rightController=null,this._gazeColor=new h.a(.7,.7,.7),this._laserColor=new h.a(.7,.7,.7),this._pickedLaserColor=new h.a(.2,.2,1),this._pickedGazeColor=new h.a(0,0,1),this.onNewMeshSelected=new f.c,this.onMeshSelectedWithController=new f.c,this.onNewMeshPicked=new f.c,this.onBeforeCameraTeleport=new f.c,this.onAfterCameraTeleport=new f.c,this.onSelectedMeshUnselected=new f.c,this.teleportationEnabled=!0,this._teleportationInitialized=!1,this._interactionsEnabled=!1,this._interactionsRequested=!1,this._displayGaze=!0,this._displayLaserPointer=!0,this.updateGazeTrackerScale=!0,this.updateGazeTrackerColor=!0,this.updateControllerLaserColor=!0,this.requestPointerLockOnFullScreen=!0,this.xrTestDone=!1,this._onResize=function(){d.moveButtonToBottomRight(),d._fullscreenVRpresenting&&d._webVRready&&d.exitVR()},this._onFullscreenChange=function(){var a=document;void 0!==a.fullscreen?d._fullscreenVRpresenting=document.fullscreen:void 0!==a.mozFullScreen?d._fullscreenVRpresenting=a.mozFullScreen:void 0!==a.webkitIsFullScreen?d._fullscreenVRpresenting=a.webkitIsFullScreen:void 0!==a.msIsFullScreen?d._fullscreenVRpresenting=a.msIsFullScreen:void 0!==document.msFullscreenElement&&(d._fullscreenVRpresenting=document.msFullscreenElement),!d._fullscreenVRpresenting&&d._inputElement&&(d.exitVR(),!d._useCustomVRButton&&d._btnVR&&(d._btnVR.style.top=d._inputElement.offsetTop+d._inputElement.offsetHeight-70+"px",d._btnVR.style.left=d._inputElement.offsetLeft+d._inputElement.offsetWidth-100+"px",d.updateButtonVisibility()))},this._cachedAngularSensibility={angularSensibilityX:null,angularSensibilityY:null,angularSensibility:null},this.beforeRender=function(){d._leftController&&d._leftController._activePointer&&d._castRayAndSelectObject(d._leftController),d._rightController&&d._rightController._activePointer&&d._castRayAndSelectObject(d._rightController),d._noControllerIsActive&&(d._scene.getEngine().isPointerLock||d.enableGazeEvenWhenNoPointerLock)?d._castRayAndSelectObject(d._cameraGazer):d._cameraGazer._gazeTracker.isVisible=!1},this._onNewGamepadConnected=function(a){if(a.type!==Cb.POSE_ENABLED)a.leftStick&&a.onleftstickchanged(function(a){d._teleportationInitialized&&d.teleportationEnabled&&(!d._leftController&&!d._rightController||d._leftController&&!d._leftController._activePointer&&d._rightController&&!d._rightController._activePointer)&&(d._checkTeleportWithRay(a,d._cameraGazer),d._checkTeleportBackwards(a,d._cameraGazer))}),a.rightStick&&a.onrightstickchanged(function(a){d._teleportationInitialized&&d._checkRotate(a,d._cameraGazer)}),a.type===Cb.XBOX&&(a.onbuttondown(function(a){d._interactionsEnabled&&a===oc.A&&d._cameraGazer._selectionPointerDown()}),a.onbuttonup(function(a){d._interactionsEnabled&&a===oc.A&&d._cameraGazer._selectionPointerUp()}));else{a=a;var b=new Ld(a,d._scene,d._cameraGazer._gazeTracker);"right"===a.hand||d._leftController&&d._leftController.webVRController!=a?d._rightController=b:d._leftController=b,d._tryEnableInteractionOnController(b)}},this._tryEnableInteractionOnController=function(a){d._interactionsRequested&&!a._interactionsEnabled&&d._enableInteractionOnController(a),d._teleportationRequested&&!a._teleportationEnabled&&d._enableTeleportationOnController(a)},this._onNewGamepadDisconnected=function(a){a instanceof nd&&("left"===a.hand&&null!=d._leftController&&(d._leftController.dispose(),d._leftController=null),"right"===a.hand&&null!=d._rightController&&(d._rightController.dispose(),d._rightController=null))},this._workingVector=g.e.Zero(),this._workingQuaternion=g.b.Identity(),this._workingMatrix=g.a.Identity(),q.a.Warn("WebVR is deprecated. Please avoid using this experience helper and use the WebXR experience helper instead"),this._scene=b,this._inputElement=b.getEngine().getInputElement(),"getVRDisplays"in navigator||void 0!==c.useXR||(c.useXR=!0),void 0===c.createFallbackVRDeviceOrientationFreeCamera&&(c.createFallbackVRDeviceOrientationFreeCamera=!0),void 0===c.createDeviceOrientationCamera&&(c.createDeviceOrientationCamera=!0),void 0===c.laserToggle&&(c.laserToggle=!0),void 0===c.defaultHeight&&(c.defaultHeight=1.7),c.useCustomVRButton&&(this._useCustomVRButton=!0,c.customVRButton&&(this._btnVR=c.customVRButton)),c.rayLength&&(this._rayLength=c.rayLength),this._defaultHeight=c.defaultHeight,c.positionScale&&(this._rayLength*=c.positionScale,this._defaultHeight*=c.positionScale),this._hasEnteredVR=!1,this._scene.activeCamera?this._position=this._scene.activeCamera.position.clone():this._position=new g.e(0,this._defaultHeight,0),c.createDeviceOrientationCamera||!this._scene.activeCamera){if(this._deviceOrientationCamera=new gc("deviceOrientationVRHelper",this._position.clone(),b),this._scene.activeCamera&&(this._deviceOrientationCamera.minZ=this._scene.activeCamera.minZ,this._deviceOrientationCamera.maxZ=this._scene.activeCamera.maxZ,this._scene.activeCamera instanceof cc&&this._scene.activeCamera.rotation)){var e=this._scene.activeCamera;e.rotationQuaternion?this._deviceOrientationCamera.rotationQuaternion.copyFrom(e.rotationQuaternion):this._deviceOrientationCamera.rotationQuaternion.copyFrom(g.b.RotationYawPitchRoll(e.rotation.y,e.rotation.x,e.rotation.z)),this._deviceOrientationCamera.rotation=e.rotation.clone()}this._scene.activeCamera=this._deviceOrientationCamera,this._inputElement&&this._scene.activeCamera.attachControl()}else this._existingCamera=this._scene.activeCamera;this.webVROptions.useXR&&navigator.xr?wd.IsSessionSupportedAsync("immersive-vr").then(function(a){a?(q.a.Log("Using WebXR. It is recommended to use the WebXRDefaultExperience directly"),b.createDefaultXRExperienceAsync({floorMeshes:c.floorMeshes||[]}).then(function(a){d.xr=a,d.xrTestDone=!0,d._cameraGazer=new Md(function(){return d.xr.baseExperience.camera},b),d.xr.baseExperience.onStateChangedObservable.add(function(a){switch(a){case rd.ENTERING_XR:d.onEnteringVRObservable.notifyObservers(d),d._interactionsEnabled||d.xr.pointerSelection.detach(),d.xr.pointerSelection.displayLaserPointer=d._displayLaserPointer;break;case rd.EXITING_XR:d.onExitingVRObservable.notifyObservers(d),d._scene.getEngine().resize();break;case rd.IN_XR:d._hasEnteredVR=!0;break;case rd.NOT_IN_XR:d._hasEnteredVR=!1}})})):d.completeVRInit(b,c)}):this.completeVRInit(b,c)}return Object.defineProperty(a.prototype,"onEnteringVR",{get:function(){return this.onEnteringVRObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onExitingVR",{get:function(){return this.onExitingVRObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onControllerMeshLoaded",{get:function(){return this.onControllerMeshLoadedObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"teleportationTarget",{get:function(){return this._teleportationTarget},set:function(a){a&&(a.name="teleportationTarget",this._isDefaultTeleportationTarget=!1,this._teleportationTarget=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"gazeTrackerMesh",{get:function(){return this._cameraGazer._gazeTracker},set:function(a){a&&(this._cameraGazer._gazeTracker&&this._cameraGazer._gazeTracker.dispose(),this._leftController&&this._leftController._gazeTracker&&this._leftController._gazeTracker.dispose(),this._rightController&&this._rightController._gazeTracker&&this._rightController._gazeTracker.dispose(),this._cameraGazer._gazeTracker=a,this._cameraGazer._gazeTracker.bakeCurrentTransformIntoVertices(),this._cameraGazer._gazeTracker.isPickable=!1,this._cameraGazer._gazeTracker.isVisible=!1,this._cameraGazer._gazeTracker.name="gazeTracker",this._leftController&&(this._leftController._gazeTracker=this._cameraGazer._gazeTracker.clone("gazeTracker")),this._rightController&&(this._rightController._gazeTracker=this._cameraGazer._gazeTracker.clone("gazeTracker")))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"leftControllerGazeTrackerMesh",{get:function(){return this._leftController?this._leftController._gazeTracker:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rightControllerGazeTrackerMesh",{get:function(){return this._rightController?this._rightController._gazeTracker:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"displayGaze",{get:function(){return this._displayGaze},set:function(a){this._displayGaze=a,a||(this._cameraGazer._gazeTracker.isVisible=!1,this._leftController&&(this._leftController._gazeTracker.isVisible=!1),this._rightController&&(this._rightController._gazeTracker.isVisible=!1))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"displayLaserPointer",{get:function(){return this._displayLaserPointer},set:function(a){this._displayLaserPointer=a,a?(this._rightController&&this._rightController._activatePointer(),this._leftController&&this._leftController._activatePointer()):(this._rightController&&(this._rightController._deactivatePointer(),this._rightController._gazeTracker.isVisible=!1),this._leftController&&(this._leftController._deactivatePointer(),this._leftController._gazeTracker.isVisible=!1))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"deviceOrientationCamera",{get:function(){return this._deviceOrientationCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentVRCamera",{get:function(){return this._webVRready?this._webVRCamera:this._scene.activeCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"webVRCamera",{get:function(){return this._webVRCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vrDeviceOrientationCamera",{get:function(){return this._vrDeviceOrientationCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vrButton",{get:function(){return this._btnVR},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_teleportationRequestInitiated",{get:function(){return this._cameraGazer._teleportationRequestInitiated||null!==this._leftController&&this._leftController._teleportationRequestInitiated||null!==this._rightController&&this._rightController._teleportationRequestInitiated},enumerable:!1,configurable:!0}),a.prototype.completeVRInit=function(a,b){var c=this;if(this.xrTestDone=!0,b.createFallbackVRDeviceOrientationFreeCamera&&(b.useMultiview&&(b.vrDeviceOrientationCameraMetrics||(b.vrDeviceOrientationCameraMetrics=Yc.GetDefault()),b.vrDeviceOrientationCameraMetrics.multiviewEnabled=!0),this._vrDeviceOrientationCamera=new id("VRDeviceOrientationVRHelper",this._position,this._scene,!0,b.vrDeviceOrientationCameraMetrics),this._vrDeviceOrientationCamera.angularSensibility=Number.MAX_VALUE),this._webVRCamera=new md("WebVRHelper",this._position,this._scene,b),this._webVRCamera.useStandingMatrix(),this._cameraGazer=new Md(function(){return c.currentVRCamera},a),!this._useCustomVRButton){this._btnVR=document.createElement("BUTTON"),this._btnVR.className="babylonVRicon",this._btnVR.id="babylonVRiconbtn",this._btnVR.title="Click to switch to VR";var d=".babylonVRicon { position: absolute; right: 20px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-image: url("+(window.SVGSVGElement?"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%222048%22%20height%3D%221152%22%20viewBox%3D%220%200%202048%201152%22%20version%3D%221.1%22%3E%3Cpath%20transform%3D%22rotate%28180%201024%2C576.0000000000001%29%22%20d%3D%22m1109%2C896q17%2C0%2030%2C-12t13%2C-30t-12.5%2C-30.5t-30.5%2C-12.5l-170%2C0q-18%2C0%20-30.5%2C12.5t-12.5%2C30.5t13%2C30t30%2C12l170%2C0zm-85%2C256q59%2C0%20132.5%2C-1.5t154.5%2C-5.5t164.5%2C-11.5t163%2C-20t150%2C-30t124.5%2C-41.5q23%2C-11%2042%2C-24t38%2C-30q27%2C-25%2041%2C-61.5t14%2C-72.5l0%2C-257q0%2C-123%20-47%2C-232t-128%2C-190t-190%2C-128t-232%2C-47l-81%2C0q-37%2C0%20-68.5%2C14t-60.5%2C34.5t-55.5%2C45t-53%2C45t-53%2C34.5t-55.5%2C14t-55.5%2C-14t-53%2C-34.5t-53%2C-45t-55.5%2C-45t-60.5%2C-34.5t-68.5%2C-14l-81%2C0q-123%2C0%20-232%2C47t-190%2C128t-128%2C190t-47%2C232l0%2C257q0%2C68%2038%2C115t97%2C73q54%2C24%20124.5%2C41.5t150%2C30t163%2C20t164.5%2C11.5t154.5%2C5.5t132.5%2C1.5zm939%2C-298q0%2C39%20-24.5%2C67t-58.5%2C42q-54%2C23%20-122%2C39.5t-143.5%2C28t-155.5%2C19t-157%2C11t-148.5%2C5t-129.5%2C1.5q-59%2C0%20-130%2C-1.5t-148%2C-5t-157%2C-11t-155.5%2C-19t-143.5%2C-28t-122%2C-39.5q-34%2C-14%20-58.5%2C-42t-24.5%2C-67l0%2C-257q0%2C-106%2040.5%2C-199t110%2C-162.5t162.5%2C-109.5t199%2C-40l81%2C0q27%2C0%2052%2C14t50%2C34.5t51%2C44.5t55.5%2C44.5t63.5%2C34.5t74%2C14t74%2C-14t63.5%2C-34.5t55.5%2C-44.5t51%2C-44.5t50%2C-34.5t52%2C-14l14%2C0q37%2C0%2070%2C0.5t64.5%2C4.5t63.5%2C12t68%2C23q71%2C30%20128.5%2C78.5t98.5%2C110t63.5%2C133.5t22.5%2C149l0%2C257z%22%20fill%3D%22white%22%20/%3E%3C/svg%3E%0A":"https://cdn.babylonjs.com/Assets/vrButton.png")+"); background-size: 80%; background-repeat:no-repeat; background-position: center; border: none; outline: none; transition: transform 0.125s ease-out } .babylonVRicon:hover { transform: scale(1.05) } .babylonVRicon:active {background-color: rgba(51,51,51,1) } .babylonVRicon:focus {background-color: rgba(51,51,51,1) }";d+=".babylonVRicon.vrdisplaypresenting { display: none; }";var e=document.createElement("style");e.appendChild(document.createTextNode(d)),document.getElementsByTagName("head")[0].appendChild(e),this.moveButtonToBottomRight()}this._btnVR&&this._btnVR.addEventListener("click",function(){c.isInVRMode?c._scene.getEngine().disableVR():c.enterVR()});d=this._scene.getEngine().getHostWindow();d&&(d.addEventListener("resize",this._onResize),document.addEventListener("fullscreenchange",this._onFullscreenChange,!1),document.addEventListener("mozfullscreenchange",this._onFullscreenChange,!1),document.addEventListener("webkitfullscreenchange",this._onFullscreenChange,!1),document.addEventListener("msfullscreenchange",this._onFullscreenChange,!1),document.onmsfullscreenchange=this._onFullscreenChange,b.createFallbackVRDeviceOrientationFreeCamera?this.displayVRButton():this._scene.getEngine().onVRDisplayChangedObservable.add(function(a){a.vrDisplay&&c.displayVRButton()}),this._onKeyDown=function(a){27===a.keyCode&&c.isInVRMode&&c.exitVR()},document.addEventListener("keydown",this._onKeyDown),this._scene.onPrePointerObservable.add(function(){c._hasEnteredVR&&c.exitVROnDoubleTap&&(c.exitVR(),c._fullscreenVRpresenting&&c._scene.getEngine().exitFullscreen())},Ta.a.POINTERDOUBLETAP,!1),this._onVRDisplayChanged=function(a){return c.onVRDisplayChanged(a)},this._onVrDisplayPresentChange=function(){return c.onVrDisplayPresentChange()},this._onVRRequestPresentStart=function(){c._webVRrequesting=!0,c.updateButtonVisibility()},this._onVRRequestPresentComplete=function(){c._webVRrequesting=!1,c.updateButtonVisibility()},a.getEngine().onVRDisplayChangedObservable.add(this._onVRDisplayChanged),a.getEngine().onVRRequestPresentStart.add(this._onVRRequestPresentStart),a.getEngine().onVRRequestPresentComplete.add(this._onVRRequestPresentComplete),d.addEventListener("vrdisplaypresentchange",this._onVrDisplayPresentChange),a.onDisposeObservable.add(function(){c.dispose()}),this._webVRCamera.onControllerMeshLoadedObservable.add(function(a){return c._onDefaultMeshLoaded(a)}),this._scene.gamepadManager.onGamepadConnectedObservable.add(this._onNewGamepadConnected),this._scene.gamepadManager.onGamepadDisconnectedObservable.add(this._onNewGamepadDisconnected),this.updateButtonVisibility(),this._circleEase=new qa,this._circleEase.setEasingMode(pa.EASINGMODE_EASEINOUT),this._teleportationEasing=this._circleEase,a.onPointerObservable.add(function(b){c._interactionsEnabled&&a.activeCamera===c.vrDeviceOrientationCamera&&"mouse"===b.event.pointerType&&(b.type===Ta.a.POINTERDOWN?c._cameraGazer._selectionPointerDown():b.type===Ta.a.POINTERUP&&c._cameraGazer._selectionPointerUp())}),this.webVROptions.floorMeshes&&this.enableTeleportation({floorMeshes:this.webVROptions.floorMeshes}))},a.prototype._onDefaultMeshLoaded=function(a){this._leftController&&this._leftController.webVRController==a&&a.mesh&&this._leftController._setLaserPointerParent(a.mesh),this._rightController&&this._rightController.webVRController==a&&a.mesh&&this._rightController._setLaserPointerParent(a.mesh);try{this.onControllerMeshLoadedObservable.notifyObservers(a)}catch(a){q.a.Warn("Error in your custom logic onControllerMeshLoaded: "+a)}},Object.defineProperty(a.prototype,"isInVRMode",{get:function(){return this.xr&&this.webVROptions.useXR&&this.xr.baseExperience.state===rd.IN_XR||this._webVRpresenting||this._fullscreenVRpresenting},enumerable:!1,configurable:!0}),a.prototype.onVrDisplayPresentChange=function(){var a=this._scene.getEngine().getVRDevice();if(a){var b=this._webVRpresenting;this._webVRpresenting=a.isPresenting,b&&!this._webVRpresenting&&this.exitVR()}else q.a.Warn("Detected VRDisplayPresentChange on an unknown VRDisplay. Did you can enterVR on the vrExperienceHelper?");this.updateButtonVisibility()},a.prototype.onVRDisplayChanged=function(a){this._webVRsupported=a.vrSupported,this._webVRready=!!a.vrDisplay,this._webVRpresenting=a.vrDisplay&&a.vrDisplay.isPresenting,this.updateButtonVisibility()},a.prototype.moveButtonToBottomRight=function(){if(this._inputElement&&!this._useCustomVRButton&&this._btnVR){var a=this._inputElement.getBoundingClientRect();this._btnVR.style.top=a.top+a.height-70+"px",this._btnVR.style.left=a.left+a.width-100+"px"}},a.prototype.displayVRButton=function(){this._useCustomVRButton||this._btnVRDisplayed||!this._btnVR||(document.body.appendChild(this._btnVR),this._btnVRDisplayed=!0)},a.prototype.updateButtonVisibility=function(){this._btnVR&&!this._useCustomVRButton&&(this._btnVR.className="babylonVRicon",this.isInVRMode?this._btnVR.className+=" vrdisplaypresenting":(this._webVRready&&(this._btnVR.className+=" vrdisplayready"),this._webVRsupported&&(this._btnVR.className+=" vrdisplaysupported"),this._webVRrequesting&&(this._btnVR.className+=" vrdisplayrequesting")))},a.prototype.enterVR=function(){var a=this;if(this.xr)this.xr.baseExperience.enterXRAsync("immersive-vr","local-floor",this.xr.renderTarget);else{if(this.onEnteringVRObservable)try{this.onEnteringVRObservable.notifyObservers(this)}catch(a){q.a.Warn("Error in your custom logic onEnteringVR: "+a)}if(this._scene.activeCamera){if(this._position=this._scene.activeCamera.position.clone(),this.vrDeviceOrientationCamera&&(this.vrDeviceOrientationCamera.rotation=g.b.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles(),this.vrDeviceOrientationCamera.angularSensibility=2e3),this.webVRCamera){var b=this.webVRCamera.deviceRotationQuaternion.toEulerAngles().y;b=g.b.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles().y-b;var c=this.webVRCamera.rotationQuaternion.toEulerAngles().y;this.webVRCamera.rotationQuaternion=g.b.FromEulerAngles(0,c+b,0)}this._existingCamera=this._scene.activeCamera,this._existingCamera.angularSensibilityX&&(this._cachedAngularSensibility.angularSensibilityX=this._existingCamera.angularSensibilityX,this._existingCamera.angularSensibilityX=Number.MAX_VALUE),this._existingCamera.angularSensibilityY&&(this._cachedAngularSensibility.angularSensibilityY=this._existingCamera.angularSensibilityY,this._existingCamera.angularSensibilityY=Number.MAX_VALUE),this._existingCamera.angularSensibility&&(this._cachedAngularSensibility.angularSensibility=this._existingCamera.angularSensibility,this._existingCamera.angularSensibility=Number.MAX_VALUE)}this._webVRrequesting||(this._webVRready?this._webVRpresenting||(this._scene.getEngine().onVRRequestPresentComplete.addOnce(function(b){a.onAfterEnteringVRObservable.notifyObservers({success:b})}),this._webVRCamera.position=this._position,this._scene.activeCamera=this._webVRCamera):this._vrDeviceOrientationCamera&&(this._vrDeviceOrientationCamera.position=this._position,this._scene.activeCamera&&(this._vrDeviceOrientationCamera.minZ=this._scene.activeCamera.minZ),this._scene.activeCamera=this._vrDeviceOrientationCamera,this._scene.getEngine().enterFullscreen(this.requestPointerLockOnFullScreen),this.updateButtonVisibility(),this._vrDeviceOrientationCamera.onViewMatrixChangedObservable.addOnce(function(){a.onAfterEnteringVRObservable.notifyObservers({success:!0})})),this._scene.activeCamera&&this._inputElement&&this._scene.activeCamera.attachControl(),this._interactionsEnabled&&this._scene.registerBeforeRender(this.beforeRender),this._displayLaserPointer&&[this._leftController,this._rightController].forEach(function(a){a&&a._activatePointer()}),this._hasEnteredVR=!0)}},a.prototype.exitVR=function(){if(this.xr)this.xr.baseExperience.exitXRAsync();else if(this._hasEnteredVR){if(this.onExitingVRObservable)try{this.onExitingVRObservable.notifyObservers(this)}catch(a){q.a.Warn("Error in your custom logic onExitingVR: "+a)}this._webVRpresenting&&this._scene.getEngine().disableVR(),this._scene.activeCamera&&(this._position=this._scene.activeCamera.position.clone()),this.vrDeviceOrientationCamera&&(this.vrDeviceOrientationCamera.angularSensibility=Number.MAX_VALUE),this._deviceOrientationCamera?(this._deviceOrientationCamera.position=this._position,this._scene.activeCamera=this._deviceOrientationCamera,this._cachedAngularSensibility.angularSensibilityX&&(this._deviceOrientationCamera.angularSensibilityX=this._cachedAngularSensibility.angularSensibilityX,this._cachedAngularSensibility.angularSensibilityX=null),this._cachedAngularSensibility.angularSensibilityY&&(this._deviceOrientationCamera.angularSensibilityY=this._cachedAngularSensibility.angularSensibilityY,this._cachedAngularSensibility.angularSensibilityY=null),this._cachedAngularSensibility.angularSensibility&&(this._deviceOrientationCamera.angularSensibility=this._cachedAngularSensibility.angularSensibility,this._cachedAngularSensibility.angularSensibility=null)):this._existingCamera&&(this._existingCamera.position=this._position,this._scene.activeCamera=this._existingCamera,this._inputElement&&this._scene.activeCamera.attachControl(),this._cachedAngularSensibility.angularSensibilityX&&(this._existingCamera.angularSensibilityX=this._cachedAngularSensibility.angularSensibilityX,this._cachedAngularSensibility.angularSensibilityX=null),this._cachedAngularSensibility.angularSensibilityY&&(this._existingCamera.angularSensibilityY=this._cachedAngularSensibility.angularSensibilityY,this._cachedAngularSensibility.angularSensibilityY=null),this._cachedAngularSensibility.angularSensibility&&(this._existingCamera.angularSensibility=this._cachedAngularSensibility.angularSensibility,this._cachedAngularSensibility.angularSensibility=null)),this.updateButtonVisibility(),this._interactionsEnabled&&(this._scene.unregisterBeforeRender(this.beforeRender),this._cameraGazer._gazeTracker.isVisible=!1,this._leftController&&(this._leftController._gazeTracker.isVisible=!1),this._rightController&&(this._rightController._gazeTracker.isVisible=!1)),this._scene.getEngine().resize(),[this._leftController,this._rightController].forEach(function(a){a&&a._deactivatePointer()}),this._hasEnteredVR=!1;var a=this._scene.getEngine();a._onVrDisplayPresentChange&&a._onVrDisplayPresentChange()}},Object.defineProperty(a.prototype,"position",{get:function(){return this._position},set:function(a){this._position=a,this._scene.activeCamera&&(this._scene.activeCamera.position=a)},enumerable:!1,configurable:!0}),a.prototype.enableInteractions=function(){var a=this;if(!this._interactionsEnabled){if(this._interactionsRequested=!0,this.xr)return void (this.xr.baseExperience.state===rd.IN_XR&&this.xr.pointerSelection.attach());this._leftController&&this._enableInteractionOnController(this._leftController),this._rightController&&this._enableInteractionOnController(this._rightController),this.raySelectionPredicate=function(b){return b.isVisible&&(b.isPickable||b.name===a._floorMeshName)},this.meshSelectionPredicate=function(){return!0},this._raySelectionPredicate=function(b){return!!(a._isTeleportationFloor(b)||-1===b.name.indexOf("gazeTracker")&&-1===b.name.indexOf("teleportationTarget")&&-1===b.name.indexOf("torusTeleportation"))&&a.raySelectionPredicate(b)},this._interactionsEnabled=!0}},Object.defineProperty(a.prototype,"_noControllerIsActive",{get:function(){return!(this._leftController&&this._leftController._activePointer||this._rightController&&this._rightController._activePointer)},enumerable:!1,configurable:!0}),a.prototype._isTeleportationFloor=function(a){for(var b=0;b-1||this._floorMeshesCollection.push(a))},a.prototype.removeFloorMesh=function(a){if(this._floorMeshesCollection){a=this._floorMeshesCollection.indexOf(a);-1!==a&&this._floorMeshesCollection.splice(a,1)}},a.prototype.enableTeleportation=function(a){var b=this;if(void 0===a&&(a={}),!this._teleportationInitialized){if(this._teleportationRequested=!0,this.enableInteractions(),this.webVROptions.useXR&&(a.floorMeshes||a.floorMeshName)){var c=a.floorMeshes||[];if(!c.length){var d=this._scene.getMeshByName(a.floorMeshName);d&&c.push(d)}if(this.xr)return c.forEach(function(a){b.xr.teleportation.addFloorMesh(a)}),void (this.xr.teleportation.attached||this.xr.teleportation.attach());if(!this.xrTestDone){var e=function(){b.xrTestDone&&(b._scene.unregisterBeforeRender(e),b.xr?b.xr.teleportation.attached||b.xr.teleportation.attach():b.enableTeleportation(a))};return void this._scene.registerBeforeRender(e)}}a.floorMeshName&&(this._floorMeshName=a.floorMeshName),a.floorMeshes&&(this._floorMeshesCollection=a.floorMeshes),a.teleportationMode&&(this._teleportationMode=a.teleportationMode),a.teleportationTime&&a.teleportationTime>0&&(this._teleportationTime=a.teleportationTime),a.teleportationSpeed&&a.teleportationSpeed>0&&(this._teleportationSpeed=a.teleportationSpeed),void 0!==a.easingFunction&&(this._teleportationEasing=a.easingFunction),null!=this._leftController&&this._enableTeleportationOnController(this._leftController),null!=this._rightController&&this._enableTeleportationOnController(this._rightController);d=new od.a;d.vignetteColor=new h.b(0,0,0,0),d.vignetteEnabled=!0,this._postProcessMove=new td("postProcessMove",1,this._webVRCamera,void 0,void 0,void 0,void 0,d),this._webVRCamera.detachPostProcess(this._postProcessMove),this._teleportationInitialized=!0,this._isDefaultTeleportationTarget&&(this._createTeleportationCircles(),this._teleportationTarget.scaling.scaleInPlace(this._webVRCamera.deviceScaleFactor))}},a.prototype._enableInteractionOnController=function(a){var b=this;a.webVRController.mesh&&(a._interactionsEnabled=!0,this.isInVRMode&&this._displayLaserPointer&&a._activatePointer(),this.webVROptions.laserToggle&&a.webVRController.onMainButtonStateChangedObservable.add(function(c){b._displayLaserPointer&&1===c.value&&(a._activePointer?a._deactivatePointer():a._activatePointer(),b.displayGaze&&(a._gazeTracker.isVisible=a._activePointer))}),a.webVRController.onTriggerStateChangedObservable.add(function(c){var d=a;b._noControllerIsActive&&(d=b._cameraGazer),d._pointerDownOnMeshAsked?c.valueb._padSensibilityUp&&d._selectionPointerDown()}))},a.prototype._checkTeleportWithRay=function(a,b){this._teleportationRequestInitiated&&!b._teleportationRequestInitiated||(b._teleportationRequestInitiated?Math.sqrt(a.y*a.y+a.x*a.x)-this._padSensibilityDown&&(b._rotationLeftAsked=!1):a.x<-this._padSensibilityUp&&b._dpadPressed&&(b._rotationLeftAsked=!0,this._rotationAllowed&&this._rotateCamera(!1)),b._rotationRightAsked?a.xthis._padSensibilityUp&&b._dpadPressed&&(b._rotationRightAsked=!0,this._rotationAllowed&&this._rotateCamera(!0)))},a.prototype._checkTeleportBackwards=function(a,b){if(!b._teleportationRequestInitiated)if(a.y>this._padSensibilityUp&&b._dpadPressed){if(!b._teleportationBackRequestInitiated){if(!this.currentVRCamera)return;a=g.b.FromRotationMatrix(this.currentVRCamera.getWorldMatrix().getRotationMatrix());var c=this.currentVRCamera.position;this.currentVRCamera.devicePosition&&this.currentVRCamera.deviceRotationQuaternion&&(a=this.currentVRCamera.deviceRotationQuaternion,c=this.currentVRCamera.devicePosition),a.toEulerAnglesToRef(this._workingVector),this._workingVector.z=0,this._workingVector.x=0,g.b.RotationYawPitchRollToRef(this._workingVector.y,this._workingVector.x,this._workingVector.z,this._workingQuaternion),this._workingQuaternion.toRotationMatrix(this._workingMatrix),g.e.TransformCoordinatesToRef(this._teleportBackwardsVector,this._workingMatrix,this._workingVector);a=new nc.a(c,this._workingVector);c=this._scene.pickWithRay(a,this._raySelectionPredicate);c&&c.pickedPoint&&c.pickedMesh&&this._isTeleportationFloor(c.pickedMesh)&&c.distance<5&&this.teleportCamera(c.pickedPoint),b._teleportationBackRequestInitiated=!0}}else b._teleportationBackRequestInitiated=!1},a.prototype._enableTeleportationOnController=function(a){var b=this;a.webVRController.mesh&&(a._interactionsEnabled||this._enableInteractionOnController(a),a._interactionsEnabled=!0,a._teleportationEnabled=!0,a.webVRController.controllerType===kc.VIVE&&(a._dpadPressed=!1,a.webVRController.onPadStateChangedObservable.add(function(b){a._dpadPressed=b.pressed,a._dpadPressed||(a._rotationLeftAsked=!1,a._rotationRightAsked=!1,a._teleportationBackRequestInitiated=!1)})),a.webVRController.onPadValuesChangedObservable.add(function(c){b.teleportationEnabled&&(b._checkTeleportBackwards(c,a),b._checkTeleportWithRay(c,a)),b._checkRotate(c,a)}))},a.prototype._createTeleportationCircles=function(){this._teleportationTarget=Gd("teleportationTarget",{width:2,height:2,subdivisions:2},this._scene),this._teleportationTarget.isPickable=!1;var a=new qd.a("DynamicTexture",512,this._scene,!0);a.hasAlpha=!0;var b=a.getContext();b.beginPath(),b.arc(256,256,200,0,2*Math.PI,!1),b.fillStyle=this._teleportationFillColor,b.fill(),b.lineWidth=10,b.strokeStyle=this._teleportationBorderColor,b.stroke(),b.closePath(),a.update();b=new pd.a("TextPlaneMaterial",this._scene);b.diffuseTexture=a,this._teleportationTarget.material=b;a=Ad("torusTeleportation",{diameter:.75,thickness:.1,tessellation:25,updatable:!1},this._scene);a.isPickable=!1,a.parent=this._teleportationTarget;b=new O("animationInnerCircle","position.y",30,O.ANIMATIONTYPE_FLOAT,O.ANIMATIONLOOPMODE_CYCLE);var c=[];c.push({frame:0,value:0}),c.push({frame:30,value:.4}),c.push({frame:60,value:0}),b.setKeys(c);c=new Aa;c.setEasingMode(pa.EASINGMODE_EASEINOUT),b.setEasingFunction(c),a.animations=[],a.animations.push(b),this._scene.beginAnimation(a,0,60,!0),this._hideTeleportationTarget()},a.prototype._displayTeleportationTarget=function(){this._teleportActive=!0,this._teleportationInitialized&&(this._teleportationTarget.isVisible=!0,this._isDefaultTeleportationTarget&&(this._teleportationTarget.getChildren()[0].isVisible=!0))},a.prototype._hideTeleportationTarget=function(){this._teleportActive=!1,this._teleportationInitialized&&(this._teleportationTarget.isVisible=!1,this._isDefaultTeleportationTarget&&(this._teleportationTarget.getChildren()[0].isVisible=!1))},a.prototype._rotateCamera=function(a){var b=this;if(this.currentVRCamera instanceof dc){a?this._rotationAngle++:this._rotationAngle--,this.currentVRCamera.animations=[];a=g.b.FromRotationMatrix(g.a.RotationY(Math.PI/4*this._rotationAngle));var c=new O("animationRotation","rotationQuaternion",90,O.ANIMATIONTYPE_QUATERNION,O.ANIMATIONLOOPMODE_CONSTANT),d=[];d.push({frame:0,value:this.currentVRCamera.rotationQuaternion}),d.push({frame:6,value:a}),c.setKeys(d),c.setEasingFunction(this._circleEase),this.currentVRCamera.animations.push(c),this._postProcessMove.animations=[];a=new O("animationPP","vignetteWeight",90,O.ANIMATIONTYPE_FLOAT,O.ANIMATIONLOOPMODE_CONSTANT);d=[];d.push({frame:0,value:0}),d.push({frame:3,value:4}),d.push({frame:6,value:0}),a.setKeys(d),a.setEasingFunction(this._circleEase),this._postProcessMove.animations.push(a);c=new O("animationPP2","vignetteStretch",90,O.ANIMATIONTYPE_FLOAT,O.ANIMATIONLOOPMODE_CONSTANT);d=[];d.push({frame:0,value:0}),d.push({frame:3,value:10}),d.push({frame:6,value:0}),c.setKeys(d),c.setEasingFunction(this._circleEase),this._postProcessMove.animations.push(c),this._postProcessMove.imageProcessingConfiguration.vignetteWeight=0,this._postProcessMove.imageProcessingConfiguration.vignetteStretch=0,this._postProcessMove.samples=4,this._webVRCamera.attachPostProcess(this._postProcessMove),this._scene.beginAnimation(this._postProcessMove,0,6,!1,1,function(){b._webVRCamera.detachPostProcess(b._postProcessMove)}),this._scene.beginAnimation(this.currentVRCamera,0,6,!1,1)}},a.prototype._moveTeleportationSelectorTo=function(a,b,c){if(a.pickedPoint){b._teleportationRequestInitiated&&(this._displayTeleportationTarget(),this._haloCenter.copyFrom(a.pickedPoint),this._teleportationTarget.position.copyFrom(a.pickedPoint));b=this._convertNormalToDirectionOfRay(a.getNormal(!0,!1),c);if(b){a=g.e.Cross(R.a.Y,b);c=g.e.Cross(b,a);g.e.RotationFromAxisToRef(c,b,a,this._teleportationTarget.rotation)}this._teleportationTarget.position.y+=.1}},a.prototype.teleportCamera=function(b){var c=this;if(this.currentVRCamera instanceof dc){this.webVRCamera.leftCamera?(this._workingVector.copyFrom(this.webVRCamera.leftCamera.globalPosition),this._workingVector.subtractInPlace(this.webVRCamera.position),b.subtractToRef(this._workingVector,this._workingVector)):this._workingVector.copyFrom(b),this.isInVRMode?this._workingVector.y+=this.webVRCamera.deviceDistanceToRoomGround()*this._webVRCamera.deviceScaleFactor:this._workingVector.y+=this._defaultHeight,this.onBeforeCameraTeleport.notifyObservers(this._workingVector);if(this._teleportationMode==a.TELEPORTATIONMODE_CONSTANTSPEED){b=90;var d=g.e.Distance(this.currentVRCamera.position,this._workingVector);d=this._teleportationSpeed/d}else b=Math.round(90*this._teleportationTime/1e3),d=1;this.currentVRCamera.animations=[];var e=new O("animationCameraTeleportation","position",90,O.ANIMATIONTYPE_VECTOR3,O.ANIMATIONLOOPMODE_CONSTANT),f=[{frame:0,value:this.currentVRCamera.position},{frame:b,value:this._workingVector}];e.setKeys(f),e.setEasingFunction(this._teleportationEasing),this.currentVRCamera.animations.push(e),this._postProcessMove.animations=[];f=Math.round(b/2);e=new O("animationPP","vignetteWeight",90,O.ANIMATIONTYPE_FLOAT,O.ANIMATIONLOOPMODE_CONSTANT);var h=[];h.push({frame:0,value:0}),h.push({frame:f,value:8}),h.push({frame:b,value:0}),e.setKeys(h),this._postProcessMove.animations.push(e);h=new O("animationPP2","vignetteStretch",90,O.ANIMATIONTYPE_FLOAT,O.ANIMATIONLOOPMODE_CONSTANT);e=[];e.push({frame:0,value:0}),e.push({frame:f,value:10}),e.push({frame:b,value:0}),h.setKeys(e),this._postProcessMove.animations.push(h),this._postProcessMove.imageProcessingConfiguration.vignetteWeight=0,this._postProcessMove.imageProcessingConfiguration.vignetteStretch=0,this._webVRCamera.attachPostProcess(this._postProcessMove),this._scene.beginAnimation(this._postProcessMove,0,b,!1,d,function(){c._webVRCamera.detachPostProcess(c._postProcessMove)}),this._scene.beginAnimation(this.currentVRCamera,0,b,!1,d,function(){c.onAfterCameraTeleport.notifyObservers(c._workingVector)}),this._hideTeleportationTarget()}},a.prototype._convertNormalToDirectionOfRay=function(a,b){a&&Math.acos(g.e.Dot(a,b.direction))b){c=b;b=e,e=c}return e>0&&e0&&b=0))},a.prototype._canDoCollision=function(a,b,c,d){a=g.e.Distance(this._basePointWorld,a);var e=Math.max(this._radius.x,this._radius.y,this._radius.z);return!(a>this._velocityWorldLength+e+b)&&!!function(a,b,c,d){return!(a.x>c.x+d)&&!(c.x-d>b.x)&&!(a.y>c.y+d)&&!(c.y-d>b.y)&&!(a.z>c.z+d)&&!(c.z-d>b.z)}(c,d,this._basePointWorld,this._velocityWorldLength+e)},a.prototype._testTriangle=function(a,b,c,d,e,f,h){var i,j=!1;b||(b=[]),b[a]||(b[a]=new Hb.a(0,0,0,0),b[a].copyFromPoints(c,d,e));b=b[a];if(f||b.isFrontFacingTo(this._normalizedVelocity,0)){a=b.signedDistanceTo(this._basePoint);f=g.e.Dot(b.normal,this._velocity);if(0==f){if(Math.abs(a)>=1)return;j=!0,i=0}else{var k=(1-a)/f;if((i=(-1-a)/f)>k){a=k;k=i,i=a}if(i>1||k<0)return;i<0&&(i=0),i>1&&(i=1)}this._collisionPoint.copyFromFloats(0,0,0);f=!1;a=1;if(j||(this._basePoint.subtractToRef(b.normal,this._planeIntersectionPoint),this._velocity.scaleToRef(i,this._tempVector),this._planeIntersectionPoint.addInPlace(this._tempVector),this._checkPointInTriangle(this._planeIntersectionPoint,c,d,e,b.normal)&&(f=!0,a=i,this._collisionPoint.copyFrom(this._planeIntersectionPoint))),!f){k=this._velocitySquaredLength;this._basePoint.subtractToRef(c,this._tempVector);j=2*g.e.Dot(this._velocity,this._tempVector);b=this._tempVector.lengthSquared()-1;i=Pd(k,j,b,a);i.found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(c)),this._basePoint.subtractToRef(d,this._tempVector),j=2*g.e.Dot(this._velocity,this._tempVector),b=this._tempVector.lengthSquared()-1,(i=Pd(k,j,b,a)).found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(d)),this._basePoint.subtractToRef(e,this._tempVector),j=2*g.e.Dot(this._velocity,this._tempVector),b=this._tempVector.lengthSquared()-1,(i=Pd(k,j,b,a)).found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(e)),d.subtractToRef(c,this._edge),c.subtractToRef(this._basePoint,this._baseToVertex);var l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex);if(k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found){var p=(m*i.root-o)/l;p>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),c.addToRef(this._edge,this._collisionPoint))}e.subtractToRef(d,this._edge),d.subtractToRef(this._basePoint,this._baseToVertex),l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex),k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found&&(p=(m*i.root-o)/l)>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),d.addToRef(this._edge,this._collisionPoint)),c.subtractToRef(e,this._edge),e.subtractToRef(this._basePoint,this._baseToVertex),l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex),k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found&&(p=(m*i.root-o)/l)>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),e.addToRef(this._edge,this._collisionPoint))}if(f){d=a*a*this._velocitySquaredLength;(!this.collisionFound||d=d)e.copyFrom(a);else{var h=f?f.collisionMask:c.collisionMask;c._initialize(a,b,g);for(var i=f&&f.surroundingMeshes||this._scene.meshes,j=0;j-1?a:this._shaderRepository+a,this._engine._loadFile(c+"."+b.toLowerCase()+".fx",d)):d(window.atob(a.substr(7))):d(a.substr(7))},Object.defineProperty(a.prototype,"computeSourceCode",{get:function(){var a;return this._computeSourceCodeOverride?this._computeSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getComputeShaderCode())&&void 0!==a?a:this._computeSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawComputeSourceCode",{get:function(){return this._rawComputeSourceCode},enumerable:!1,configurable:!0}),a.prototype._prepareEffect=function(){var a=this,b=this.defines,c=this._pipelineContext;this._isReady=!1;try{var d=this._engine;this._pipelineContext=d.createComputePipelineContext(),this._pipelineContext._name=this._key,d._prepareComputePipelineContext(this._pipelineContext,this._computeSourceCodeOverride?this._computeSourceCodeOverride:this._computeSourceCode,this._rawComputeSourceCode,this._computeSourceCodeOverride?null:b,this._entryPoint),d._executeWhenComputeStateIsCompiled(this._pipelineContext,function(){a._compilationError="",a._isReady=!0,a.onCompiled&&a.onCompiled(a),a.onCompileObservable.notifyObservers(a),a.onCompileObservable.clear(),c&&a.getEngine()._deleteComputePipelineContext(c)}),this._pipelineContext.isAsync&&this._checkIsReady(c)}catch(a){this._processCompilationErrors(a,c)}},a.prototype._getShaderCodeAndErrorLine=function(a,b){var c=null;if(b&&a){b=b.match(/COMPUTE SHADER ERROR: 0:(d+?):/);if(b&&2===b.length){b=parseInt(b[1]);var d=a.split(" ",-1);d.length>=b&&(c="Offending line ["+b+"] in compute code: "+d[b-1])}}return[a,c]},a.prototype._processCompilationErrors=function(b,c){var d;if(void 0===c&&(c=null),this._compilationError=b.message,q.a.Error("Unable to compile compute effect:"),q.a.Error("Defines: "+this.defines),a.LogShaderCodeOnCompilationError){b=null;var e=null;(null===(d=this._pipelineContext)||void 0===d?void 0:d._getComputeShaderCode())&&(e=(d=this._getShaderCodeAndErrorLine(this._pipelineContext._getComputeShaderCode(),this._compilationError))[0],b=d[1],e&&(q.a.Error("Compute code:"),q.a.Error(e))),b&&q.a.Error(b)}q.a.Error("Error: "+this._compilationError),c&&(this._pipelineContext=c,this._isReady=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this))},a.prototype.dispose=function(){this._pipelineContext&&this._pipelineContext.dispose(),this._engine._releaseComputeEffect(this)},a.RegisterShader=function(a,b){X.a.GetShadersStore(Xd.a.WGSL)[a+"ComputeShader"]=b},a._uniqueIdSeed=0,a.LogShaderCodeOnCompilationError=!0,a}();!function(a){a[a.Texture=0]="Texture",a[a.StorageTexture=1]="StorageTexture",a[a.UniformBuffer=2]="UniformBuffer",a[a.StorageBuffer=3]="StorageBuffer",a[a.TextureWithoutSampler=4]="TextureWithoutSampler",a[a.Sampler=5]="Sampler"}(Sd||(Sd={})),pb.a.prototype.createComputeEffect=function(a,b){throw new Error("createComputeEffect: This engine does not support compute shaders!")},pb.a.prototype.createComputePipelineContext=function(){throw new Error("createComputePipelineContext: This engine does not support compute shaders!")},pb.a.prototype.createComputeContext=function(){},pb.a.prototype.computeDispatch=function(a,b,c,d,e,f,g){throw new Error("computeDispatch: This engine does not support compute shaders!")},pb.a.prototype.areAllComputeEffectsReady=function(){return!0},pb.a.prototype.releaseComputeEffects=function(){},pb.a.prototype._prepareComputePipelineContext=function(a,b,c,d,e){},pb.a.prototype._rebuildComputeEffects=function(){},pb.a.prototype._executeWhenComputeStateIsCompiled=function(a,b){b()},pb.a.prototype._releaseComputeEffect=function(a){},pb.a.prototype._deleteComputePipelineContext=function(a){};var Zd=c(137),$d=c(129),ae=function(){function a(a,b,c,d){void 0===d&&(d={}),this._bindings={},this._samplers={},this._contextIsDirty=!1,this.onCompiled=null,this.onError=null,this.name=a,this._engine=b,this.uniqueId=Zd.a.UniqueId,this._engine.getCaps().supportComputeShaders?d.bindingsMapping?(this._context=b.createComputeContext(),this._shaderPath=c,this._options=Object(l.a)({bindingsMapping:{},defines:[]},d)):q.a.Error("You must provide the binding mappings as browsers don"t support reflection for wgsl shaders yet!"):q.a.Error("This engine does not support compute shaders!")}return Object.defineProperty(a.prototype,"options",{get:function(){return this._options},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderPath",{get:function(){return this._shaderPath},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"ComputeShader"},a.prototype.setTexture=function(a,b,c){void 0===c&&(c=!0);var d=this._bindings[a];this._bindings[a]={type:c?Sd.Texture:Sd.TextureWithoutSampler,object:b,indexInGroupEntries:null==d?void 0:d.indexInGroupEntries},this._contextIsDirty||(this._contextIsDirty=!d||d.object!==b||d.type!==this._bindings[a].type)},a.prototype.setStorageTexture=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.StorageTexture,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setUniformBuffer=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.UniformBuffer,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setStorageBuffer=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.StorageBuffer,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setTextureSampler=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||!b.compareSampler(c.object)),this._bindings[a]={type:Sd.Sampler,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.isReady=function(){var a=this._effect;for(var b in this._bindings){var c=this._bindings[b],d=c.type;c=c.object;switch(d){case Sd.Texture:case Sd.TextureWithoutSampler:case Sd.StorageTexture:if(!c.isReady())return!1}}d=[];c=this._shaderPath;if(this._options.defines)for(b=0;bthis.capacity&&this._depth-1&&this.entries.splice(b,1)}},a.prototype.addEntries=function(a){for(var b=0;b-1&&(b._gpuFrameTimeToken=null,b._gpuFrameTime.fetchNewFrame(),b._gpuFrameTime.addCount(a,!0))}})):(this.onBeginFrameObservable.remove(this._onBeginFrameObserver),this._onBeginFrameObserver=null,this.onEndFrameObservable.remove(this._onEndFrameObserver),this._onEndFrameObserver=null))},T.a.prototype._getGlAlgorithmType=function(a){return a===bb.a.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE?this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE:this._gl.ANY_SAMPLES_PASSED},Object.defineProperty(bb.a.prototype,"isOcclusionQueryInProgress",{get:function(){return this._occlusionDataStorage.isOcclusionQueryInProgress},set:function(a){this._occlusionDataStorage.isOcclusionQueryInProgress=a},enumerable:!1,configurable:!0}),Object.defineProperty(bb.a.prototype,"_occlusionDataStorage",{get:function(){return this.__occlusionDataStorage||(this.__occlusionDataStorage=new se),this.__occlusionDataStorage},enumerable:!1,configurable:!0}),Object.defineProperty(bb.a.prototype,"isOccluded",{get:function(){return this._occlusionDataStorage.isOccluded},set:function(a){this._occlusionDataStorage.isOccluded=a},enumerable:!0,configurable:!0}),Object.defineProperty(bb.a.prototype,"occlusionQueryAlgorithmType",{get:function(){return this._occlusionDataStorage.occlusionQueryAlgorithmType},set:function(a){this._occlusionDataStorage.occlusionQueryAlgorithmType=a},enumerable:!0,configurable:!0}),Object.defineProperty(bb.a.prototype,"occlusionType",{get:function(){return this._occlusionDataStorage.occlusionType},set:function(a){this._occlusionDataStorage.occlusionType=a},enumerable:!0,configurable:!0}),Object.defineProperty(bb.a.prototype,"occlusionRetryCount",{get:function(){return this._occlusionDataStorage.occlusionRetryCount},set:function(a){this._occlusionDataStorage.occlusionRetryCount=a},enumerable:!0,configurable:!0}),bb.a.prototype._checkOcclusionQuery=function(){var a=this._occlusionDataStorage;if(a.occlusionType===bb.a.OCCLUSION_TYPE_NONE)return a.isOccluded=!1,!1;var b=this.getEngine();if(!b.getCaps().supportOcclusionQuery)return a.isOccluded=!1,!1;if(!b.isQueryResultAvailable)return a.isOccluded=!1,!1;if(this.isOcclusionQueryInProgress&&this._occlusionQuery)if(b.isQueryResultAvailable(this._occlusionQuery)){var c=b.getQueryResult(this._occlusionQuery);a.isOcclusionQueryInProgress=!1,a.occlusionInternalRetryCounter=0,a.isOccluded=!(c>0)}else{if(a.occlusionInternalRetryCounter++,!(-1!==a.occlusionRetryCount&&a.occlusionInternalRetryCounter>a.occlusionRetryCount))return a.occlusionType!==bb.a.OCCLUSION_TYPE_OPTIMISTIC&&a.isOccluded;a.isOcclusionQueryInProgress=!1,a.occlusionInternalRetryCounter=0,a.isOccluded=a.occlusionType!==bb.a.OCCLUSION_TYPE_OPTIMISTIC&&a.isOccluded}c=this.getScene();if(c.getBoundingBoxRenderer){c=c.getBoundingBoxRenderer();null===this._occlusionQuery&&(this._occlusionQuery=b.createQuery()),b.beginOcclusionQuery(a.occlusionQueryAlgorithmType,this._occlusionQuery)&&(c.renderOcclusionBoundingBox(this),b.endOcclusionQuery(a.occlusionQueryAlgorithmType),this._occlusionDataStorage.isOcclusionQueryInProgress=!0)}return a.isOccluded};var te=!0;T.a.prototype.createTransformFeedback=function(){return this._gl.createTransformFeedback()},T.a.prototype.deleteTransformFeedback=function(a){this._gl.deleteTransformFeedback(a)},T.a.prototype.bindTransformFeedback=function(a){this._gl.bindTransformFeedback(this._gl.TRANSFORM_FEEDBACK,a)},T.a.prototype.beginTransformFeedback=function(a){void 0===a&&(a=!0),this._gl.beginTransformFeedback(a?this._gl.POINTS:this._gl.TRIANGLES)},T.a.prototype.endTransformFeedback=function(){this._gl.endTransformFeedback()},T.a.prototype.setTranformFeedbackVaryings=function(a,b){this._gl.transformFeedbackVaryings(a,b,this._gl.INTERLEAVED_ATTRIBS)},T.a.prototype.bindTransformFeedbackBuffer=function(a){this._gl.bindBufferBase(this._gl.TRANSFORM_FEEDBACK_BUFFER,0,a?a.underlyingResource:null)};c(144);pb.a.prototype.createExternalTexture=function(a){return null},pb.a.prototype.setExternalTexture=function(a,b){throw new Error("setExternalTexture: This engine does not support external textures!")},pb.a.prototype.updateVideoTexture=function(a,b,c){if(a&&!a._isDisabled){var d=this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0);this._unpackFlipY(!c);try{if(void 0===this._videoTextureSupported&&(this._gl.getError(),this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,b),0!==this._gl.getError()?this._videoTextureSupported=!1:this._videoTextureSupported=!0),this._videoTextureSupported)this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,b);else{if(!a._workingCanvas){a._workingCanvas=this.createCanvas(a.width,a.height);c=a._workingCanvas.getContext("2d");if(!c)throw new Error("Unable to get 2d context");a._workingContext=c,a._workingCanvas.width=a.width,a._workingCanvas.height=a.height}a._workingContext.clearRect(0,0,a.width,a.height),a._workingContext.drawImage(b,0,0,b.videoWidth,b.videoHeight,0,0,a.width,a.height),this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,a._workingCanvas)}a.generateMipMaps&&this._gl.generateMipmap(this._gl.TEXTURE_2D),d||this._bindTextureDirectly(this._gl.TEXTURE_2D,null),a.isReady=!0}catch(b){a._isDisabled=!0}}},pb.a.prototype.restoreSingleAttachment=function(){var a=this._gl;this.bindAttachments([a.BACK])},pb.a.prototype.restoreSingleAttachmentForRenderTarget=function(){var a=this._gl;this.bindAttachments([a.COLOR_ATTACHMENT0])},pb.a.prototype.buildTextureLayout=function(a){for(var b=this._gl,c=[],d=0;d1?"COLOR_ATTACHMENT"+g:"COLOR_ATTACHMENT"+g+"_WEBGL"],d.readBuffer(e[g]),d.drawBuffers(e),d.blitFramebuffer(0,0,h.width,h.height,0,0,h.width,h.height,d.COLOR_BUFFER_BIT,d.NEAREST)}for(g=0;g1?"COLOR_ATTACHMENT"+g:"COLOR_ATTACHMENT"+g+"_WEBGL"];d.drawBuffers(e)}for(g=0;g1&&(b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8||b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24||b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH32_FLOAT)&&(h=b.depthTextureFormat));var z=this._gl,A=z.createFramebuffer();this._bindUnboundFramebuffer(A);var B=a.width||a;a=a.height||a;var C=[],E=[];b=this.webGLVersion>1&&g&&b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8;var F=this._setupFramebufferDepthAttachments(!b&&f,!g&&e,B,a);y._framebuffer=A,y._depthStencilBuffer=F,y._generateDepthBuffer=!g&&e,y._generateStencilBuffer=!b&&f,y._attachments=E;for(A=0;A1?"COLOR_ATTACHMENT"+A:"COLOR_ATTACHMENT"+A+"_WEBGL"];C.push(f),E.push(G),z.activeTexture(z["TEXTURE"+A]),z.bindTexture(z.TEXTURE_2D,f._hardwareTexture.underlyingResource),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_MAG_FILTER,b.mag),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_MIN_FILTER,b.min),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_WRAP_S,z.CLAMP_TO_EDGE),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_WRAP_T,z.CLAMP_TO_EDGE),z.texImage2D(z.TEXTURE_2D,0,this._getRGBABufferInternalSizedFormat(e),B,a,0,z.RGBA,this._getWebGLTextureType(e),null),z.framebufferTexture2D(z.DRAW_FRAMEBUFFER,G,z.TEXTURE_2D,f._hardwareTexture.underlyingResource,0),d&&this._gl.generateMipmap(this._gl.TEXTURE_2D),this._bindTextureDirectly(z.TEXTURE_2D,null),f.baseWidth=B,f.baseHeight=a,f.width=B,f.height=a,f.isReady=!0,f.samples=1,f.generateMipMaps=d,f.samplingMode=F,f.type=e,this._internalTexturesCache.push(f)}if(g&&this._caps.depthTextureExtension){b=new ob.a(this,ob.b.Depth);G=r.a.TEXTURETYPE_UNSIGNED_SHORT;F=z.DEPTH_COMPONENT16;e=z.DEPTH_COMPONENT;f=z.UNSIGNED_SHORT;i=z.DEPTH_ATTACHMENT;this.webGLVersion<2?F=z.DEPTH_COMPONENT:h===r.a.TEXTUREFORMAT_DEPTH32_FLOAT?(G=r.a.TEXTURETYPE_FLOAT,f=z.FLOAT,F=z.DEPTH_COMPONENT32F):h===r.a.TEXTUREFORMAT_DEPTH24?(G=r.a.TEXTURETYPE_UNSIGNED_INT,f=z.UNSIGNED_INT,F=z.DEPTH_COMPONENT24,i=z.DEPTH_ATTACHMENT):h===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8&&(G=r.a.TEXTURETYPE_UNSIGNED_INT_24_8,f=z.UNSIGNED_INT_24_8,F=z.DEPTH24_STENCIL8,e=z.DEPTH_STENCIL,i=z.DEPTH_STENCIL_ATTACHMENT),z.activeTexture(z.TEXTURE0),z.bindTexture(z.TEXTURE_2D,b._hardwareTexture.underlyingResource),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_MAG_FILTER,z.NEAREST),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_MIN_FILTER,z.NEAREST),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_WRAP_S,z.CLAMP_TO_EDGE),z.texParameteri(z.TEXTURE_2D,z.TEXTURE_WRAP_T,z.CLAMP_TO_EDGE),z.texImage2D(z.TEXTURE_2D,0,F,B,a,0,e,f,null),z.framebufferTexture2D(z.FRAMEBUFFER,i,z.TEXTURE_2D,b._hardwareTexture.underlyingResource,0),b.baseWidth=B,b.baseHeight=a,b.width=B,b.height=a,b.isReady=!0,b.samples=1,b.generateMipMaps=d,b.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE,b.format=h,b.type=G,C.push(b),this._internalTexturesCache.push(b)}return y.setTextures(C),c&&z.drawBuffers(E),this._bindUnboundFramebuffer(null),this.resetTextureCache(),y},pb.a.prototype.updateMultipleRenderTargetTextureSampleCount=function(a,b,c){if(void 0===c&&(c=!0),this.webGLVersion<2||!a||!a.texture)return 1;if(a.samples===b)return b;var d=a._attachments.length;if(0===d)return 1;var e=this._gl;b=Math.min(b,this.getCaps().maxMSAASamples),a._depthStencilBuffer&&(e.deleteRenderbuffer(a._depthStencilBuffer),a._depthStencilBuffer=null),a._MSAAFramebuffer&&(e.deleteFramebuffer(a._MSAAFramebuffer),a._MSAAFramebuffer=null);for(var f=0;f1&&e.renderbufferStorageMultisample){var g=e.createFramebuffer();if(!g)throw new Error("Unable to create multi sampled framebuffer");a._MSAAFramebuffer=g,this._bindUnboundFramebuffer(g);g=[];for(f=0;f1?"COLOR_ATTACHMENT"+f:"COLOR_ATTACHMENT"+f+"_WEBGL"],k=this._createRenderBuffer(h.width,h.height,b,-1,this._getRGBAMultiSampleBufferFormat(h.type),j);if(!k)throw new Error("Unable to create multi sampled framebuffer");i._MSAARenderBuffer=k,h.samples=b,g.push(j)}c&&e.drawBuffers(g)}else this._bindUnboundFramebuffer(a._framebuffer);return a._depthStencilBuffer=this._setupFramebufferDepthAttachments(a._generateStencilBuffer,a._generateDepthBuffer,a.texture.width,a.texture.height,b),this._bindUnboundFramebuffer(null),b};var ue=c(54),ve=c(115);pb.a.prototype._createDepthStencilCubeTexture=function(a,b,c){var d=new ob.a(this,ob.b.DepthStencil);if(d.isCube=!0,1===this.webGLVersion)return q.a.Error("Depth cube texture is not supported by WebGL 1."),d;b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1},b);var e=this._gl;this._bindTextureDirectly(e.TEXTURE_CUBE_MAP,d,!0),this._setupDepthStencilTexture(d,a,b.generateStencil,b.bilinearFiltering,b.comparisonFunction),c._depthStencilTexture=d,c._depthStencilTextureWithStencil=b.generateStencil;for(c=0;c<6;c++)b.generateStencil?e.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+c,0,e.DEPTH24_STENCIL8,a,a,0,e.DEPTH_STENCIL,e.UNSIGNED_INT_24_8,null):e.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+c,0,e.DEPTH_COMPONENT24,a,a,0,e.DEPTH_COMPONENT,e.UNSIGNED_INT,null);return this._bindTextureDirectly(e.TEXTURE_CUBE_MAP,null),this._internalTexturesCache.push(d),d},pb.a.prototype._partialLoadFile=function(a,b,c,d,e){void 0===e&&(e=null),this._loadFile(a,function(a){c[b]=a,c._internalCount++,6===c._internalCount&&d(c)},void 0,void 0,!0,function(a,b){e&&a&&e(a.status+" "+a.statusText,b)})},pb.a.prototype._cascadeLoadFiles=function(a,b,c,d){void 0===d&&(d=null);a=[];a._internalCount=0;for(var e=0;e<6;e++)this._partialLoadFile(c[e],e,a,b,d)},pb.a.prototype._cascadeLoadImgs=function(a,b,c,d,e,f){void 0===e&&(e=null);var g=[];g._internalCount=0;for(var h=0;h<6;h++)this._partialLoadImg(d[h],h,g,a,b,c,e,f)},pb.a.prototype._partialLoadImg=function(a,b,c,d,e,f,g,h){void 0===g&&(g=null);var i=Object(ve.a)();Object(ue.i)(a,function(a){c[b]=a,c._internalCount++,d&&d._removePendingData(i),6===c._internalCount&&f&&f(e,c)},function(a,b){d&&d._removePendingData(i),g&&g(a,b)},d?d.offlineProvider:null,h),d&&d._addPendingData(i)},pb.a.prototype._setCubeMapTextureParams=function(a,b){var c=this._gl;c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_MAG_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_MIN_FILTER,b?c.LINEAR_MIPMAP_LINEAR:c.LINEAR),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),a.samplingMode=b?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:r.a.TEXTURE_LINEAR_LINEAR,this._bindTextureDirectly(c.TEXTURE_CUBE_MAP,null)},pb.a.prototype.createCubeTextureBase=function(a,b,c,d,e,f,g,h,i,j,k,u,A,B,C){var D=this;void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=!1),void 0===j&&(j=0),void 0===k&&(k=0),void 0===u&&(u=null),void 0===A&&(A=null),void 0===B&&(B=null),void 0===C&&(C=!1);var E=u||new ob.a(this,ob.b.Cube);E.isCube=!0,E.url=a,E.generateMipMaps=!d,E._lodGenerationScale=j,E._lodGenerationOffset=k,E._useSRGBBuffer=!!C&&this._caps.supportSRGBBuffers&&(this.webGLVersion>1||this.isWebGPU||!!d),this._doNotHandleContextLost||(E._extension=h,E._files=c);var F=a;this._transformTextureUrl&&!u&&(a=this._transformTextureUrl(a));for(var u=a.lastIndexOf("."),u=h||(u>-1?a.substring(u).toLowerCase():""),G=null,H=0,I=pb.a._TextureLoaders;H-1?a.substring(c,a.length):"";return(b>-1?a.substring(0,b):a)+this._textureFormatInUse+c}pb.a.prototype.createStorageBuffer=function(a,b){throw new Error("createStorageBuffer: Unsupported method in this engine!")},pb.a.prototype.updateStorageBuffer=function(a,b,c,d){},pb.a.prototype.readFromStorageBuffer=function(a,b,c,d){throw new Error("readFromStorageBuffer: Unsupported method in this engine!")},pb.a.prototype.setStorageBuffer=function(a,b){throw new Error("setStorageBuffer: Unsupported method in this engine!")},Object.defineProperty(T.a.prototype,"texturesSupported",{get:function(){var a=new Array;return this._caps.astc&&a.push("-astc.ktx"),this._caps.s3tc&&a.push("-dxt.ktx"),this._caps.pvrtc&&a.push("-pvrtc.ktx"),this._caps.etc2&&a.push("-etc2.ktx"),this._caps.etc1&&a.push("-etc1.ktx"),a},enumerable:!0,configurable:!0}),Object.defineProperty(T.a.prototype,"textureFormatInUse",{get:function(){return this._textureFormatInUse||null},enumerable:!0,configurable:!0}),T.a.prototype.setCompressedTextureExclusions=function(a){this._excludedCompressedTextures=a},T.a.prototype.setTextureFormatToUse=function(a){for(var b=this.texturesSupported,c=0,d=b.length;cthis._length&&this._flush()},a.prototype._flush=function(){this._nativeDataStream.writeBuffer(this._uint32s.buffer,this._position),this._position=0},a.DEFAULT_BUFFER_SIZE=65536,a}(),Ae=function(){function a(a,b){void 0===a&&(a=g.e.Zero()),void 0===b&&(b=g.e.Up()),this.position=a,this.normal=b}return a.prototype.clone=function(){return new a(this.position.clone(),this.normal.clone())},a}(),Be=function(){function a(a,b,c){void 0===a&&(a=g.e.Zero()),void 0===b&&(b=g.e.Up()),void 0===c&&(c=g.d.Zero()),this.position=a,this.normal=b,this.uv=c}return a.prototype.clone=function(){return new a(this.position.clone(),this.normal.clone(),this.uv.clone())},a}(),Ce=[Math.sqrt(1/(4*Math.PI)),-Math.sqrt(3/(4*Math.PI)),Math.sqrt(3/(4*Math.PI)),-Math.sqrt(3/(4*Math.PI)),Math.sqrt(15/(4*Math.PI)),-Math.sqrt(15/(4*Math.PI)),Math.sqrt(5/(16*Math.PI)),-Math.sqrt(15/(4*Math.PI)),Math.sqrt(15/(16*Math.PI))],De=[function(a){return 1},function(a){return a.y},function(a){return a.z},function(a){return a.x},function(a){return a.x*a.y},function(a){return a.y*a.z},function(a){return 3*a.z*a.z-1},function(a){return a.x*a.z},function(a){return a.x*a.x-a.y*a.y}],Ee=function(a,b){return Ce[a]*De[a](b)},Fe=[Math.PI,2*Math.PI/3,2*Math.PI/3,2*Math.PI/3,Math.PI/4,Math.PI/4,Math.PI/4,Math.PI/4,Math.PI/4],Ge=function(){function a(){this.preScaled=!1,this.l00=g.e.Zero(),this.l1_1=g.e.Zero(),this.l10=g.e.Zero(),this.l11=g.e.Zero(),this.l2_2=g.e.Zero(),this.l2_1=g.e.Zero(),this.l20=g.e.Zero(),this.l21=g.e.Zero(),this.l22=g.e.Zero()}return a.prototype.addLight=function(a,b,c){g.c.Vector3[0].set(b.r,b.g,b.b);b=g.c.Vector3[0];var d=g.c.Vector3[1];b.scaleToRef(c,d),d.scaleToRef(Ee(0,a),g.c.Vector3[2]),this.l00.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(1,a),g.c.Vector3[2]),this.l1_1.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(2,a),g.c.Vector3[2]),this.l10.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(3,a),g.c.Vector3[2]),this.l11.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(4,a),g.c.Vector3[2]),this.l2_2.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(5,a),g.c.Vector3[2]),this.l2_1.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(6,a),g.c.Vector3[2]),this.l20.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(7,a),g.c.Vector3[2]),this.l21.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(8,a),g.c.Vector3[2]),this.l22.addInPlace(g.c.Vector3[2])},a.prototype.scaleInPlace=function(a){this.l00.scaleInPlace(a),this.l1_1.scaleInPlace(a),this.l10.scaleInPlace(a),this.l11.scaleInPlace(a),this.l2_2.scaleInPlace(a),this.l2_1.scaleInPlace(a),this.l20.scaleInPlace(a),this.l21.scaleInPlace(a),this.l22.scaleInPlace(a)},a.prototype.convertIncidentRadianceToIrradiance=function(){this.l00.scaleInPlace(Fe[0]),this.l1_1.scaleInPlace(Fe[1]),this.l10.scaleInPlace(Fe[2]),this.l11.scaleInPlace(Fe[3]),this.l2_2.scaleInPlace(Fe[4]),this.l2_1.scaleInPlace(Fe[5]),this.l20.scaleInPlace(Fe[6]),this.l21.scaleInPlace(Fe[7]),this.l22.scaleInPlace(Fe[8])},a.prototype.convertIrradianceToLambertianRadiance=function(){this.scaleInPlace(1/Math.PI)},a.prototype.preScaleForRendering=function(){this.preScaled=!0,this.l00.scaleInPlace(Ce[0]),this.l1_1.scaleInPlace(Ce[1]),this.l10.scaleInPlace(Ce[2]),this.l11.scaleInPlace(Ce[3]),this.l2_2.scaleInPlace(Ce[4]),this.l2_1.scaleInPlace(Ce[5]),this.l20.scaleInPlace(Ce[6]),this.l21.scaleInPlace(Ce[7]),this.l22.scaleInPlace(Ce[8])},a.prototype.updateFromArray=function(a){return g.e.FromArrayToRef(a[0],0,this.l00),g.e.FromArrayToRef(a[1],0,this.l1_1),g.e.FromArrayToRef(a[2],0,this.l10),g.e.FromArrayToRef(a[3],0,this.l11),g.e.FromArrayToRef(a[4],0,this.l2_2),g.e.FromArrayToRef(a[5],0,this.l2_1),g.e.FromArrayToRef(a[6],0,this.l20),g.e.FromArrayToRef(a[7],0,this.l21),g.e.FromArrayToRef(a[8],0,this.l22),this},a.prototype.updateFromFloatsArray=function(a){return g.e.FromFloatsToRef(a[0],a[1],a[2],this.l00),g.e.FromFloatsToRef(a[3],a[4],a[5],this.l1_1),g.e.FromFloatsToRef(a[6],a[7],a[8],this.l10),g.e.FromFloatsToRef(a[9],a[10],a[11],this.l11),g.e.FromFloatsToRef(a[12],a[13],a[14],this.l2_2),g.e.FromFloatsToRef(a[15],a[16],a[17],this.l2_1),g.e.FromFloatsToRef(a[18],a[19],a[20],this.l20),g.e.FromFloatsToRef(a[21],a[22],a[23],this.l21),g.e.FromFloatsToRef(a[24],a[25],a[26],this.l22),this},a.FromArray=function(b){return(new a).updateFromArray(b)},a.FromPolynomial=function(b){var c=new a;return c.l00=b.xx.scale(.376127).add(b.yy.scale(.376127)).add(b.zz.scale(.376126)),c.l1_1=b.y.scale(.977204),c.l10=b.z.scale(.977204),c.l11=b.x.scale(.977204),c.l2_2=b.xy.scale(1.16538),c.l2_1=b.yz.scale(1.16538),c.l20=b.zz.scale(1.34567).subtract(b.xx.scale(.672834)).subtract(b.yy.scale(.672834)),c.l21=b.zx.scale(1.16538),c.l22=b.xx.scale(1.16538).subtract(b.yy.scale(1.16538)),c.l1_1.scaleInPlace(-1),c.l11.scaleInPlace(-1),c.l2_1.scaleInPlace(-1),c.l21.scaleInPlace(-1),c.scaleInPlace(Math.PI),c},a}(),He=function(){function a(){this.x=g.e.Zero(),this.y=g.e.Zero(),this.z=g.e.Zero(),this.xx=g.e.Zero(),this.yy=g.e.Zero(),this.zz=g.e.Zero(),this.xy=g.e.Zero(),this.yz=g.e.Zero(),this.zx=g.e.Zero()}return Object.defineProperty(a.prototype,"preScaledHarmonics",{get:function(){return this._harmonics||(this._harmonics=Ge.FromPolynomial(this)),this._harmonics.preScaled||this._harmonics.preScaleForRendering(),this._harmonics},enumerable:!1,configurable:!0}),a.prototype.addAmbient=function(a){g.c.Vector3[0].copyFromFloats(a.r,a.g,a.b);a=g.c.Vector3[0];this.xx.addInPlace(a),this.yy.addInPlace(a),this.zz.addInPlace(a)},a.prototype.scaleInPlace=function(a){this.x.scaleInPlace(a),this.y.scaleInPlace(a),this.z.scaleInPlace(a),this.xx.scaleInPlace(a),this.yy.scaleInPlace(a),this.zz.scaleInPlace(a),this.yz.scaleInPlace(a),this.zx.scaleInPlace(a),this.xy.scaleInPlace(a)},a.prototype.updateFromHarmonics=function(a){return this._harmonics=a,this.x.copyFrom(a.l11),this.x.scaleInPlace(1.02333).scaleInPlace(-1),this.y.copyFrom(a.l1_1),this.y.scaleInPlace(1.02333).scaleInPlace(-1),this.z.copyFrom(a.l10),this.z.scaleInPlace(1.02333),this.xx.copyFrom(a.l00),g.c.Vector3[0].copyFrom(a.l20).scaleInPlace(.247708),g.c.Vector3[1].copyFrom(a.l22).scaleInPlace(.429043),this.xx.scaleInPlace(.886277).subtractInPlace(g.c.Vector3[0]).addInPlace(g.c.Vector3[1]),this.yy.copyFrom(a.l00),this.yy.scaleInPlace(.886277).subtractInPlace(g.c.Vector3[0]).subtractInPlace(g.c.Vector3[1]),this.zz.copyFrom(a.l00),g.c.Vector3[0].copyFrom(a.l20).scaleInPlace(.495417),this.zz.scaleInPlace(.886277).addInPlace(g.c.Vector3[0]),this.yz.copyFrom(a.l2_1),this.yz.scaleInPlace(.858086).scaleInPlace(-1),this.zx.copyFrom(a.l21),this.zx.scaleInPlace(.858086).scaleInPlace(-1),this.xy.copyFrom(a.l2_2),this.xy.scaleInPlace(.858086),this.scaleInPlace(1/Math.PI),this},a.FromHarmonics=function(b){return(new a).updateFromHarmonics(b)},a.FromArray=function(b){var c=new a;return g.e.FromArrayToRef(b[0],0,c.x),g.e.FromArrayToRef(b[1],0,c.y),g.e.FromArrayToRef(b[2],0,c.z),g.e.FromArrayToRef(b[3],0,c.xx),g.e.FromArrayToRef(b[4],0,c.yy),g.e.FromArrayToRef(b[5],0,c.zz),g.e.FromArrayToRef(b[6],0,c.yz),g.e.FromArrayToRef(b[7],0,c.zx),g.e.FromArrayToRef(b[8],0,c.xy),c},a}(),Ie=c(53);rb=" varying vec2 vUV; uniform sampler2D textureSampler; #include void main(void) { gl_FragColor=vec4(fromRGBD(texture2D(textureSampler,vUV)),1.0); }";X.a.ShadersStore.rgbdDecodePixelShader=rb;var Je,Ke;function Le(a,b,c,d){void 0===d&&(d=!0);var e=a.getScene(),f=e.getEngine(),g=new cd("resized"+a.name,{width:b,height:c},e,!a.noMipmap,!0,a._texture.type,!1,a.samplingMode,!1);g.wrapU=a.wrapU,g.wrapV=a.wrapV,g.uOffset=a.uOffset,g.vOffset=a.vOffset,g.uScale=a.uScale,g.vScale=a.vScale,g.uAng=a.uAng,g.vAng=a.vAng,g.wAng=a.wAng,g.coordinatesIndex=a.coordinatesIndex,g.level=a.level,g.anisotropicFilteringLevel=a.anisotropicFilteringLevel,g._texture.isReady=!1,a.wrapU=V.a.CLAMP_ADDRESSMODE,a.wrapV=V.a.CLAMP_ADDRESSMODE;var h=new Gc("pass",1,null,d?V.a.BILINEAR_SAMPLINGMODE:V.a.NEAREST_SAMPLINGMODE,f,!1,r.a.TEXTURETYPE_UNSIGNED_INT);return h.getEffect().executeWhenCompiled(function(){h.onApply=function(b){b.setTexture("textureSampler",a)};var b=g.renderTarget;b&&(e.postProcessManager.directRender([h],b),f.unBindFramebuffer(b),g.disposeFramebufferObjects(),h.dispose(),g.getInternalTexture().isReady=!0)}),g}function Me(a,b,c,d,e,f){var g=b.getEngine();return b.isReady=!1,e=null!=e?e:b.samplingMode,d=null!=d?d:b.type,f=null!=f?f:b.format,-1===d&&(d=r.a.TEXTURETYPE_UNSIGNED_BYTE),new Promise(function(h){var i=new Fc("postprocess",a,null,null,1,null,e,g,!1,void 0,d,void 0,null,!1,f),j=g.createRenderTargetTexture({width:b.width,height:b.height},{generateDepthBuffer:!1,generateMipMaps:!1,generateStencilBuffer:!1,samplingMode:e,type:d,format:f});i.getEffect().executeWhenCompiled(function(){i.onApply=function(a){a._bindTexture("textureSampler",b),a.setFloat2("scale",1,1)},c.postProcessManager.directRender([i],j,!0),g.restoreDefaultFramebuffer(),g._releaseTexture(b),i&&i.dispose(),j._swapAndDie(b),b.type=d,b.format=r.a.TEXTUREFORMAT_RGBA,b.isReady=!0,h(b)})})}function Ne(a){Je||(Je=new Float32Array(1),Ke=new Int32Array(Je.buffer)),Je[0]=a;a=Ke[0];var b=a>>16&32768,c=a>>12&2047,d=a>>23&255;return d<103?b:d>142?(b|=31744,b|=(255==d?0:1)&&8388607&a):d<113?b|=((c|=2048)>>114-d)+(c>>113-d&1):(b|=d-112<<10|c>>1,b+=1&c)}function Oe(a){var b=(32768&a)>>15,c=(31744&a)>>10;a=1023&a;return 0===c?(b?-1:1)*Math.pow(2,-14)*(a/Math.pow(2,10)):31==c?a?NaN:1/0*(b?-1:1):(b?-1:1)*Math.pow(2,c-15)*(1+a/Math.pow(2,10))}var Pe={CreateResizedCopy:Le,ApplyPostProcess:Me,ToHalfFloat:Ne,FromHalfFloat:Oe},Qe=function(){function a(){}return a.ExpandRGBDTexture=function(a){var b=a._texture;if(b&&a.isRGBD){var c=b.getEngine(),d=c.getCaps(),e=b.isReady,f=!1;d.textureHalfFloatRender&&d.textureHalfFloatLinearFiltering?(f=!0,b.type=r.a.TEXTURETYPE_HALF_FLOAT):d.textureFloatRender&&d.textureFloatLinearFiltering&&(f=!0,b.type=r.a.TEXTURETYPE_FLOAT),f&&(b.isReady=!1,b._isRGBD=!1,b.invertY=!1);d=function(){if(f){var d=new Fc("rgbdDecode","rgbdDecode",null,null,1,null,r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,c,!1,void 0,b.type,void 0,null,!1),e=c.createRenderTargetTexture(b.width,{generateDepthBuffer:!1,generateMipMaps:!1,generateStencilBuffer:!1,samplingMode:b.samplingMode,type:b.type,format:r.a.TEXTUREFORMAT_RGBA});d.getEffect().executeWhenCompiled(function(){d.onApply=function(a){a._bindTexture("textureSampler",b),a.setFloat2("scale",1,1)},a.getScene().postProcessManager.directRender([d],e,!0),c.restoreDefaultFramebuffer(),c._releaseTexture(b),d&&d.dispose(),e._swapAndDie(b),b.isReady=!0})}};e?d():a.onLoadObservable.addOnce(d)}},a.EncodeTextureToRGBD=function(a,b,c){return void 0===c&&(c=r.a.TEXTURETYPE_UNSIGNED_BYTE),Me("rgbdEncode",a,b,c,r.a.TEXTURE_NEAREST_SAMPLINGMODE,r.a.TEXTUREFORMAT_RGBA)},a}(),Re=function(a,b,c,d){this.name=a,this.worldAxisForNormal=b,this.worldAxisForFileX=c,this.worldAxisForFileY=d},Se=function(){function a(){}return a.ConvertCubeMapTextureToSphericalPolynomial=function(a){var b,c=this;if(!a.isCube)return null;null===(b=a.getScene())||void 0===b||b.getEngine().flushFramebuffer();var d,e,f=a.getSize().width,g=a.readPixels(0,void 0,void 0,!1),h=a.readPixels(1,void 0,void 0,!1);a.isRenderTarget?(d=a.readPixels(3,void 0,void 0,!1),e=a.readPixels(2,void 0,void 0,!1)):(d=a.readPixels(2,void 0,void 0,!1),e=a.readPixels(3,void 0,void 0,!1));var i=a.readPixels(4,void 0,void 0,!1),j=a.readPixels(5,void 0,void 0,!1),k=a.gammaSpace,n=r.a.TEXTUREFORMAT_RGBA,o=r.a.TEXTURETYPE_UNSIGNED_INT;return a.textureType!=r.a.TEXTURETYPE_FLOAT&&a.textureType!=r.a.TEXTURETYPE_HALF_FLOAT||(o=r.a.TEXTURETYPE_FLOAT),new Promise(function(a,b){Promise.all([h,g,d,e,i,j]).then(function(b){var d=b[0],e=b[1],g=b[2],h=b[3],i=b[4];b=b[5];e={size:f,right:e,left:d,up:g,down:h,front:i,back:b,format:n,type:o,gammaSpace:k};a(c.ConvertCubeMapToSphericalPolynomial(e))})})},a.ConvertCubeMapToSphericalPolynomial=function(a){for(var b=new Ge,c=0,d=2/a.size,e=d,f=.5*d-1,g=0;g<6;g++)for(var i=this.FileFaces[g],j=a[i.name],k=f,v=a.format===r.a.TEXTUREFORMAT_RGBA?4:3,w=0;w void main(void) { gl_FragColor=toRGBD(texture2D(textureSampler,vUV).rgb); }";X.a.ShadersStore.rgbdEncodePixelShader=a;var Te=[134,22,135,150,246,214,150,54];function Ue(a){for(var a=new DataView(a.buffer,a.byteOffset,a.byteLength),b=0,c=0;c2)throw new Error("Unsupported babylon environment map version ""+a.version+"". Latest supported version is "2".");return 2===a.version?a:a=Object(l.a)(Object(l.a)({},a),{version:2,imageType:"image/png"})}function We(a,b){var c;return void 0===b&&(b={}),Object(l.b)(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k,K,L,aa,M,N,O,ba,ca,da,ea,fa,ga,ha,Q,ia,R,ja,ka,la,ma,na,oa;return Object(l.e)(this,function(m){switch(m.label){case 0:if(!(d=a.getInternalTexture()))return[2,Promise.reject("The cube texture is invalid.")];if(e=null!==(c=b.imageType)&&void 0!==c?c:"image/png",f=d.getEngine(),a.textureType!==r.a.TEXTURETYPE_HALF_FLOAT&&a.textureType!==r.a.TEXTURETYPE_FLOAT&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_BYTE&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_INT&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_INTEGER&&-1!==a.textureType)return[2,Promise.reject("The cube texture should allow HDR (Full Float or Half Float).")];if(g=r.a.TEXTURETYPE_FLOAT,!f.getCaps().textureFloatRender&&(g=r.a.TEXTURETYPE_HALF_FLOAT,!f.getCaps().textureHalfFloatRender))return[2,Promise.reject("Env texture can only be created when the browser supports half float or full float rendering.")];h=d.width,i=new P.a(f),j={},f.flushFramebuffer(),k=I.a.ILog2(d.width),ma=0,m.label=1;case 1:if(!(ma<=k))return[3,9];K=Math.pow(2,k-ma),na=0,m.label=2;case 2:return na<6?[4,a.readPixels(na,ma,void 0,!1)]:[3,8];case 3:if((L=m.sent())&&L.byteLength===L.length){for(aa=new Float32Array(4*L.byteLength),M=0;M=48&&a<=57||a>=65&&a<=90||a>=97&&a<=122||95==a}function hf(a){for(var b=0,c="",d=!1,e=[];b=0&&a.charAt(b)!==c;)b--;return b}var kf=function(){function a(a,b){void 0===b&&(b=20),this.debug=!1,this._sourceCode=a,this._numMaxIterations=b,this._functionDescr=[],this.inlineToken="#define inline"}return Object.defineProperty(a.prototype,"code",{get:function(){return this._sourceCode},enumerable:!1,configurable:!0}),a.prototype.processCode=function(){this.debug&&!1,this._collectFunctions(),this._processInlining(this._numMaxIterations),this.debug&&!1},a.prototype._collectFunctions=function(){for(var b=0;b=0&&i.push(k.substring(m+1))}"void"!==f&&i.push("return"),this._functionDescr.push({name:e,type:f,parameters:i,body:g,callIndex:0}),b=h+1;k=c>0?this._sourceCode.substring(0,c):"";m=h+1=0&&this._replaceFunctionCallsByCode(););return this.debug&&!1,a>=0},a.prototype._replaceFunctionCallsByCode=function(){for(var a=!1,b=0,c=this._functionDescr;b0?this._sourceCode.substring(0,j):"";n=m+1=0){var g=b[c[e]];if(g){var h=g.getBuffer();h&&this._engine.recordVertexBuffer(a,h.nativeVertexBuffer,f,g.byteOffset,g.byteStride,g.getSize(),this._getNativeAttribType(g.type),g.normalized)}}}},b.prototype.bindBuffers=function(a,b,c){this._boundBuffersVertexArray&&this._deleteVertexArray(this._boundBuffersVertexArray),this._boundBuffersVertexArray=this._engine.createVertexArray(),this._recordVertexArrayObject(this._boundBuffersVertexArray,a,b,c),this.bindVertexArrayObject(this._boundBuffersVertexArray)},b.prototype.recordVertexArrayObject=function(a,b,c){var d=this._engine.createVertexArray();return this._recordVertexArrayObject(d,a,b,c),d},b.prototype._deleteVertexArray=function(a){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXARRAY),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.bindVertexArrayObject=function(a){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDVERTEXARRAY),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.releaseVertexArrayObject=function(a){this._deleteVertexArray(a)},b.prototype.getAttributes=function(a,b){a=a;return this._engine.getAttributes(a.nativeProgram,b)},b.prototype.drawElementsType=function(a,b,c,d){this._drawCalls.addCount(1,!1),this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXED),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.drawArraysType=function(a,b,c,d){this._drawCalls.addCount(1,!1),this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAW),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.createPipelineContext=function(){return new mf(this)},b.prototype.createMaterialContext=function(){},b.prototype.createDrawContext=function(){},b.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i){e=a;e.nativeProgram=d?this.createRawShaderProgram(a,b,c,void 0,i):this.createShaderProgram(a,b,c,h,void 0,i)},b.prototype._isRenderingStateCompiled=function(a){return!0},b.prototype._executeWhenRenderingStateIsCompiled=function(a,b){b()},b.prototype.createRawShaderProgram=function(a,b,c,d,e){throw new Error("Not Supported")},b.prototype.createShaderProgram=function(a,b,c,d,e,f){this.onBeforeShaderCompilationObservable.notifyObservers(this);a=new kf(b);a.processCode(),b=a.code;e=new kf(c);e.processCode(),c=e.code,b=pb.a._ConcatenateShader(b,d),c=pb.a._ConcatenateShader(c,d);f=this._engine.createProgram(b,c);return this.onAfterShaderCompilationObservable.notifyObservers(this),f},b.prototype.inlineShaderCode=function(a){a=new kf(a);return a.debug=!1,a.processCode(),a.code},b.prototype._setProgram=function(a){this._currentProgram!==a&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETPROGRAM),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand(),this._currentProgram=a)},b.prototype._deletePipelineContext=function(a){a=a;a&&a.nativeProgram&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEPROGRAM),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeProgram),this._commandBufferEncoder.finishEncodingCommand())},b.prototype.getUniforms=function(a,b){a=a;return this._engine.getUniforms(a.nativeProgram,b)},b.prototype.bindUniformBlock=function(a,b,c){throw new Error("Not Implemented")},b.prototype.bindSamplers=function(a){var b=a.getPipelineContext();this._setProgram(b.nativeProgram);for(var b=a.getSamplers(),c=0;c-1?a.substring(L).toLowerCase():""),aa=null,M=0,N=T.a._TextureLoaders;M-1?a.substring(b).toLowerCase():""))){if(c&&6===c.length)throw new Error("Multi-file loading not allowed on env files.");this._loadFile(a,function(a){return function(a){var b=Ue(a);v.width=b.width,v.height=b.width,bf(v,b);var c=b.specular;if(!c)throw new Error("Nothing else parsed so far");v._lodGenerationScale=c.lodGenerationScale;c=Ye(a,b);v.format=r.a.TEXTUREFORMAT_RGBA,v.type=r.a.TEXTURETYPE_UNSIGNED_INT,v.generateMipMaps=!0,v.getEngine().updateTextureSamplingMode(V.a.TRILINEAR_SAMPLINGMODE,v),v._isRGBD=!0,v.invertY=!0,u._engine.loadCubeTextureWithMips(v._hardwareTexture.underlyingResource,c,!1,t,function(){v.isReady=!0,e&&e()},function(){throw new Error("Could not load a native cube texture.")})}(new Uint8Array(a))},void 0,void 0,!0,function(a,b){f&&a&&f(a.status+" "+a.statusText,b)})}else{if(!c||6!==c.length)throw new Error("Cannot load cubemap because 6 files were not defined");g=[c[0],c[3],c[1],c[4],c[2],c[5]];Promise.all(g.map(function(a){return U.b.LoadFileAsync(a).then(function(a){return new Uint8Array(a)})})).then(function(a){return new Promise(function(b,c){u._engine.loadCubeTexture(v._hardwareTexture.underlyingResource,a,!d,!0,t,b,c)})}).then(function(){v.isReady=!0,e&&e()},function(a){f&&f("Failed to load cubemap: "+a.message,a)})}return this._internalTexturesCache.push(v),v},b.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new nf(a,b,c,this);return this._renderTargetWrapperCache.push(a),a},b.prototype.createRenderTargetTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!1,a),d=new Bc.b;void 0!==b&&"object"==typeof b?(d.generateMipMaps=b.generateMipMaps,d.generateDepthBuffer=void 0===b.generateDepthBuffer||b.generateDepthBuffer,d.generateStencilBuffer=d.generateDepthBuffer&&b.generateStencilBuffer,d.type=void 0===b.type?r.a.TEXTURETYPE_UNSIGNED_INT:b.type,d.samplingMode=void 0===b.samplingMode?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:b.samplingMode,d.format=void 0===b.format?r.a.TEXTUREFORMAT_RGBA:b.format):(d.generateMipMaps=b,d.generateDepthBuffer=!0,d.generateStencilBuffer=!1,d.type=r.a.TEXTURETYPE_UNSIGNED_INT,d.samplingMode=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,d.format=r.a.TEXTUREFORMAT_RGBA),(d.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(d.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(d.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);b=new ob.a(this,ob.b.RenderTarget);var e=a.width||a;a=a.height||a;d.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloat||(d.type=r.a.TEXTURETYPE_UNSIGNED_INT,q.a.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type"));var f=this._engine.createFrameBuffer(b._hardwareTexture.underlyingResource,e,a,this._getNativeTextureFormat(d.format,d.type),!!d.generateStencilBuffer,d.generateDepthBuffer,!!d.generateMipMaps);return c._framebuffer=f,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=!!d.generateStencilBuffer,b.baseWidth=e,b.baseHeight=a,b.width=e,b.height=a,b.isReady=!0,b.samples=1,b.generateMipMaps=!!d.generateMipMaps,b.samplingMode=d.samplingMode,b.type=d.type,b.format=d.format,this._internalTexturesCache.push(b),c.setTextures(b),c},b.prototype.updateTextureSamplingMode=function(a,b){if(b._hardwareTexture){var c=this._getNativeSamplingMode(a);this._setTextureSampling(b._hardwareTexture.underlyingResource,c)}b.samplingMode=a},b.prototype.bindFramebuffer=function(a,b,c,d,e){e=a;if(b)throw new Error("Cuboid frame buffers are not yet supported in NativeEngine.");if(c||d)throw new Error("Required width/height for frame buffers not yet supported in NativeEngine.");e._framebufferDepthStencil?this._bindUnboundFramebuffer(e._framebufferDepthStencil):this._bindUnboundFramebuffer(e._framebuffer)},b.prototype.unBindFramebuffer=function(a,b,c){c&&c(),this._bindUnboundFramebuffer(null)},b.prototype.createDynamicVertexBuffer=function(a){return this.createVertexBuffer(a,!0)},b.prototype.updateDynamicIndexBuffer=function(a,b,c){void 0===c&&(c=0);a=a;b=this._normalizeIndexData(b);a.is32Bits=4===b.BYTES_PER_ELEMENT,this._engine.updateDynamicIndexBuffer(a.nativeIndexBuffer,b.buffer,b.byteOffset,b.byteLength,c)},b.prototype.updateDynamicVertexBuffer=function(a,b,c,d){a=a;b=ArrayBuffer.isView(b)?b:new Float32Array(b);this._engine.updateDynamicVertexBuffer(a.nativeVertexBuffer,b.buffer,b.byteOffset+(null!=c?c:0),null!=d?d:b.byteLength)},b.prototype._setTexture=function(a,b,c,d){void 0===d&&(d=!1);c=this._boundUniforms[a];if(!c)return!1;if(!b)return null!=this._boundTexturesCache[a]&&(this._activeChannel=a,this._setTextureCore(c,null)),!1;if(b.video)this._activeChannel=a,b.update();else if(b.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED)return b.delayLoad(),!1;return d=d?b.depthStencilTexture:b.isReady()?b.getInternalTexture():b.isCube?this.emptyCubeTexture:b.is3D?this.emptyTexture3D:b.is2DArray?this.emptyTexture2DArray:this.emptyTexture,this._activeChannel=a,!(!d||!d._hardwareTexture)&&(this._setTextureWrapMode(d._hardwareTexture.underlyingResource,this._getAddressMode(b.wrapU),this._getAddressMode(b.wrapV),this._getAddressMode(b.wrapR)),this._updateAnisotropicLevel(b),this._setTextureCore(c,d._hardwareTexture.underlyingResource),!0)},b.prototype._setTextureSampling=function(a,b){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTURESAMPLING),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._setTextureWrapMode=function(a,b,c,d){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTUREWRAPMODE),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.encodeCommandArgAsUInt32(d),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._setTextureCore=function(a,b){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTURE),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsNativeData(b),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._updateAnisotropicLevel=function(a){var b=a.getInternalTexture();a=a.anisotropicFilteringLevel;b&&b._hardwareTexture&&b._cachedAnisotropicFilteringLevel!==a&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTUREANISOTROPICLEVEL),this._commandBufferEncoder.encodeCommandArgAsNativeData(b._hardwareTexture.underlyingResource),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.finishEncodingCommand(),b._cachedAnisotropicFilteringLevel=a)},b.prototype._getAddressMode=function(a){switch(a){case r.a.TEXTURE_WRAP_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_WRAP;case r.a.TEXTURE_CLAMP_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_CLAMP;case r.a.TEXTURE_MIRROR_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_MIRROR;default:throw new Error("Unexpected wrap mode: "+a+".")}},b.prototype._bindTexture=function(a,b){a=this._boundUniforms[a];if(a&&b&&b._hardwareTexture){b=b._hardwareTexture.underlyingResource;this._setTextureCore(a,b)}},b.prototype._deleteBuffer=function(a){a.nativeIndexBuffer&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEINDEXBUFFER),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeIndexBuffer),this._commandBufferEncoder.finishEncodingCommand(),delete a.nativeIndexBuffer),a.nativeVertexBuffer&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXBUFFER),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeVertexBuffer),this._commandBufferEncoder.finishEncodingCommand(),delete a.nativeVertexBuffer)},b.prototype.createCanvas=function(a,b){if(!_native.Canvas)throw new Error("Native Canvas plugin not available.");var c=new _native.Canvas;return c.width=a,c.height=b,c},b.prototype.createCanvasImage=function(){if(!_native.Canvas)throw new Error("Native Canvas plugin not available.");return new _native.Image},b.prototype._uploadCompressedDataToTextureDirectly=function(a,b,c,d,e,f,g){throw new Error("_uploadCompressedDataToTextureDirectly not implemented.")},b.prototype._uploadDataToTextureDirectly=function(a,b,c,d){throw new Error("_uploadDataToTextureDirectly not implemented.")},b.prototype._uploadArrayBufferViewToTexture=function(a,b,c,d){throw new Error("_uploadArrayBufferViewToTexture not implemented.")},b.prototype._uploadImageToTexture=function(a,b,c,d){throw new Error("_uploadArrayBufferViewToTexture not implemented.")},b.prototype._getNativeSamplingMode=function(a){switch(a){case r.a.TEXTURE_NEAREST_NEAREST:return _native.Engine.TEXTURE_NEAREST_NEAREST;case r.a.TEXTURE_LINEAR_LINEAR:return _native.Engine.TEXTURE_LINEAR_LINEAR;case r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR:return _native.Engine.TEXTURE_LINEAR_LINEAR_MIPLINEAR;case r.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST:return _native.Engine.TEXTURE_NEAREST_NEAREST_MIPNEAREST;case r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST:return _native.Engine.TEXTURE_NEAREST_LINEAR_MIPNEAREST;case r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR:return _native.Engine.TEXTURE_NEAREST_LINEAR_MIPLINEAR;case r.a.TEXTURE_NEAREST_LINEAR:return _native.Engine.TEXTURE_NEAREST_LINEAR;case r.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR:return _native.Engine.TEXTURE_NEAREST_NEAREST_MIPLINEAR;case r.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST:return _native.Engine.TEXTURE_LINEAR_NEAREST_MIPNEAREST;case r.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR:return _native.Engine.TEXTURE_LINEAR_NEAREST_MIPLINEAR;case r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST:return _native.Engine.TEXTURE_LINEAR_LINEAR_MIPNEAREST;case r.a.TEXTURE_LINEAR_NEAREST:return _native.Engine.TEXTURE_LINEAR_NEAREST;default:throw new Error("Unsupported sampling mode: "+a+".")}},b.prototype._getStencilFunc=function(a){switch(a){case r.a.LESS:return _native.Engine.STENCIL_TEST_LESS;case r.a.LEQUAL:return _native.Engine.STENCIL_TEST_LEQUAL;case r.a.EQUAL:return _native.Engine.STENCIL_TEST_EQUAL;case r.a.GEQUAL:return _native.Engine.STENCIL_TEST_GEQUAL;case r.a.GREATER:return _native.Engine.STENCIL_TEST_GREATER;case r.a.NOTEQUAL:return _native.Engine.STENCIL_TEST_NOTEQUAL;case r.a.NEVER:return _native.Engine.STENCIL_TEST_NEVER;case r.a.ALWAYS:return _native.Engine.STENCIL_TEST_ALWAYS;default:throw new Error("Unsupported stencil func mode: "+a+".")}},b.prototype._getStencilOpFail=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_FAIL_S_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_FAIL_S_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_FAIL_S_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_FAIL_S_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_FAIL_S_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_FAIL_S_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_FAIL_S_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_FAIL_S_DECRSAT;default:throw new Error("Unsupported stencil OpFail mode: "+a+".")}},b.prototype._getStencilDepthFail=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_FAIL_Z_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_FAIL_Z_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_FAIL_Z_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_FAIL_Z_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_FAIL_Z_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_FAIL_Z_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_FAIL_Z_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_FAIL_Z_DECRSAT;default:throw new Error("Unsupported stencil depthFail mode: "+a+".")}},b.prototype._getStencilDepthPass=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_PASS_Z_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_PASS_Z_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_PASS_Z_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_PASS_Z_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_PASS_Z_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_PASS_Z_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_PASS_Z_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_PASS_Z_DECRSAT;default:throw new Error("Unsupported stencil opPass mode: "+a+".")}},b.prototype._getNativeTextureFormat=function(a,b){if(a==r.a.TEXTUREFORMAT_RGB&&b==r.a.TEXTURETYPE_UNSIGNED_INT)return _native.Engine.TEXTURE_FORMAT_RGB8;if(a==r.a.TEXTUREFORMAT_RGBA&&b==r.a.TEXTURETYPE_UNSIGNED_INT)return _native.Engine.TEXTURE_FORMAT_RGBA8;if(a==r.a.TEXTUREFORMAT_RGBA&&b==r.a.TEXTURETYPE_FLOAT)return _native.Engine.TEXTURE_FORMAT_RGBA32F;throw new Error("Unsupported texture format or type: format "+a+", type "+b+".")},b.prototype._getNativeAlphaMode=function(a){switch(a){case r.a.ALPHA_DISABLE:return _native.Engine.ALPHA_DISABLE;case r.a.ALPHA_ADD:return _native.Engine.ALPHA_ADD;case r.a.ALPHA_COMBINE:return _native.Engine.ALPHA_COMBINE;case r.a.ALPHA_SUBTRACT:return _native.Engine.ALPHA_SUBTRACT;case r.a.ALPHA_MULTIPLY:return _native.Engine.ALPHA_MULTIPLY;case r.a.ALPHA_MAXIMIZED:return _native.Engine.ALPHA_MAXIMIZED;case r.a.ALPHA_ONEONE:return _native.Engine.ALPHA_ONEONE;case r.a.ALPHA_PREMULTIPLIED:return _native.Engine.ALPHA_PREMULTIPLIED;case r.a.ALPHA_PREMULTIPLIED_PORTERDUFF:return _native.Engine.ALPHA_PREMULTIPLIED_PORTERDUFF;case r.a.ALPHA_INTERPOLATE:return _native.Engine.ALPHA_INTERPOLATE;case r.a.ALPHA_SCREENMODE:return _native.Engine.ALPHA_SCREENMODE;default:throw new Error("Unsupported alpha mode: "+a+".")}},b.prototype._getNativeAttribType=function(a){switch(a){case W.b.BYTE:return _native.Engine.ATTRIB_TYPE_INT8;case W.b.UNSIGNED_BYTE:return _native.Engine.ATTRIB_TYPE_UINT8;case W.b.SHORT:return _native.Engine.ATTRIB_TYPE_INT16;case W.b.UNSIGNED_SHORT:return _native.Engine.ATTRIB_TYPE_UINT16;case W.b.FLOAT:return _native.Engine.ATTRIB_TYPE_FLOAT;default:throw new Error("Unsupported attribute type: "+a+".")}},b.prototype.getFontOffset=function(a){return{ascent:0,height:0,descent:0}},b.PROTOCOL_VERSION=2,b}(T.a);qf._createNativeDataStream=function(){return _native.NativeDataStream.VALIDATION_ENABLED?new Zf:new ze};var rf,sf,tf,uf,vf,wf,xf,yf,Y,zf,Af,Bf,Cf,Df,Ef,Ff,Gf,Hf,If,Jf,Kf,Lf,Mf,Nf,Of,Pf,Qf,Rf,Sf,Tf,Uf,Vf,Wf,Xf,Yf,Zf=function(a){function b(){return a.call(this)||this}return Object(l.d)(b,a),b.prototype.writeUint32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_UINT_32),a.prototype.writeUint32.call(this,b)},b.prototype.writeInt32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_INT_32),a.prototype.writeInt32.call(this,b)},b.prototype.writeFloat32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_FLOAT_32),a.prototype.writeFloat32.call(this,b)},b.prototype.writeUint32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_UINT_32_ARRAY),a.prototype.writeUint32Array.call(this,b)},b.prototype.writeInt32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_INT_32_ARRAY),a.prototype.writeInt32Array.call(this,b)},b.prototype.writeFloat32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_FLOAT_32_ARRAY),a.prototype.writeFloat32Array.call(this,b)},b.prototype.writeNativeData=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_NATIVE_DATA),a.prototype.writeNativeData.call(this,b)},b.prototype.writeBoolean=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_BOOLEAN),a.prototype.writeBoolean.call(this,b)},b}(ze),$f=c(36);!function(a){a.SRGB="srgb"}(rf||(rf={})),(function(a){a.LowPower="low-power",a.HighPerformance="high-performance"})(rf||(rf={})),(function(a){a.DepthClamping="depth-clamping",a.Depth24UnormStencil8="depth24unorm-stencil8",a.Depth32FloatStencil8="depth32float-stencil8",a.PipelineStatisticsQuery="pipeline-statistics-query",a.TextureCompressionBC="texture-compression-bc",a.TimestampQuery="timestamp-query"})(sf||(sf={})),(function(a){a[a.MapRead=1]="MapRead",a[a.MapWrite=2]="MapWrite",a[a.CopySrc=4]="CopySrc",a[a.CopyDst=8]="CopyDst",a[a.Index=16]="Index",a[a.Vertex=32]="Vertex",a[a.Uniform=64]="Uniform",a[a.Storage=128]="Storage",a[a.Indirect=256]="Indirect",a[a.QueryResolve=512]="QueryResolve"})(tf||(tf={})),(function(a){a[a.Read=1]="Read",a[a.Write=2]="Write"})(uf||(uf={})),(function(a){a.E1d="1d",a.E2d="2d",a.E3d="3d"})(vf||(vf={})),(function(a){a[a.CopySrc=1]="CopySrc",a[a.CopyDst=2]="CopyDst",a[a.TextureBinding=4]="TextureBinding",a[a.StorageBinding=8]="StorageBinding",a[a.RenderAttachment=16]="RenderAttachment"})(wf||(wf={})),(function(a){a.E1d="1d",a.E2d="2d",a.E2dArray="2d-array",a.Cube="cube",a.CubeArray="cube-array",a.E3d="3d"})(xf||(xf={})),(function(a){a.All="all",a.StencilOnly="stencil-only",a.DepthOnly="depth-only"})(yf||(yf={})),(function(a){a.R8Unorm="r8unorm",a.R8Snorm="r8snorm",a.R8Uint="r8uint",a.R8Sint="r8sint",a.R16Uint="r16uint",a.R16Sint="r16sint",a.R16Float="r16float",a.RG8Unorm="rg8unorm",a.RG8Snorm="rg8snorm",a.RG8Uint="rg8uint",a.RG8Sint="rg8sint",a.R32Uint="r32uint",a.R32Sint="r32sint",a.R32Float="r32float",a.RG16Uint="rg16uint",a.RG16Sint="rg16sint",a.RG16Float="rg16float",a.RGBA8Unorm="rgba8unorm",a.RGBA8UnormSRGB="rgba8unorm-srgb",a.RGBA8Snorm="rgba8snorm",a.RGBA8Uint="rgba8uint",a.RGBA8Sint="rgba8sint",a.BGRA8Unorm="bgra8unorm",a.BGRA8UnormSRGB="bgra8unorm-srgb",a.RGB9E5UFloat="rgb9e5ufloat",a.RGB10A2Unorm="rgb10a2unorm",a.RG11B10UFloat="rg11b10ufloat",a.RG32Uint="rg32uint",a.RG32Sint="rg32sint",a.RG32Float="rg32float",a.RGBA16Uint="rgba16uint",a.RGBA16Sint="rgba16sint",a.RGBA16Float="rgba16float",a.RGBA32Uint="rgba32uint",a.RGBA32Sint="rgba32sint",a.RGBA32Float="rgba32float",a.Stencil8="stencil8",a.Depth16Unorm="depth16unorm",a.Depth24Plus="depth24plus",a.Depth24PlusStencil8="depth24plus-stencil8",a.Depth32Float="depth32float",a.BC1RGBAUnorm="bc1-rgba-unorm",a.BC1RGBAUnormSRGB="bc1-rgba-unorm-srgb",a.BC2RGBAUnorm="bc2-rgba-unorm",a.BC2RGBAUnormSRGB="bc2-rgba-unorm-srgb",a.BC3RGBAUnorm="bc3-rgba-unorm",a.BC3RGBAUnormSRGB="bc3-rgba-unorm-srgb",a.BC4RUnorm="bc4-r-unorm",a.BC4RSnorm="bc4-r-snorm",a.BC5RGUnorm="bc5-rg-unorm",a.BC5RGSnorm="bc5-rg-snorm",a.BC6HRGBUFloat="bc6h-rgb-ufloat",a.BC6HRGBFloat="bc6h-rgb-float",a.BC7RGBAUnorm="bc7-rgba-unorm",a.BC7RGBAUnormSRGB="bc7-rgba-unorm-srgb",a.Depth24UnormStencil8="depth24unorm-stencil8",a.Depth32FloatStencil8="depth32float-stencil8"})(Y||(Y={})),(function(a){a.ClampToEdge="clamp-to-edge",a.Repeat="repeat",a.MirrorRepeat="mirror-repeat"})(zf||(zf={})),(function(a){a.Nearest="nearest",a.Linear="linear"})(Af||(Af={})),(function(a){a.Never="never",a.Less="less",a.Equal="equal",a.LessEqual="less-equal",a.Greater="greater",a.NotEqual="not-equal",a.GreaterEqual="greater-equal",a.Always="always"})(Bf||(Bf={})),(function(a){a[a.Vertex=1]="Vertex",a[a.Fragment=2]="Fragment",a[a.Compute=4]="Compute"})(Cf||(Cf={})),(function(a){a.Uniform="uniform",a.Storage="storage",a.ReadOnlyStorage="read-only-storage"})(Df||(Df={})),(function(a){a.Filtering="filtering",a.NonFiltering="non-filtering",a.Comparison="comparison"})(Ef||(Ef={})),(function(a){a.Float="float",a.UnfilterableFloat="unfilterable-float",a.Depth="depth",a.Sint="sint",a.Uint="uint"})(Ff||(Ff={})),(function(a){a.WriteOnly="write-only"})(Gf||(Gf={})),(function(a){a.Error="error",a.Warning="warning",a.Info="info"})(Hf||(Hf={})),(function(a){a.PointList="point-list",a.LineList="line-list",a.LineStrip="line-strip",a.TriangleList="triangle-list",a.TriangleStrip="triangle-strip"})(If||(If={})),(function(a){a.CCW="ccw",a.CW="cw"})(Jf||(Jf={})),(function(a){a.None="none",a.Front="front",a.Back="back"})(Kf||(Kf={})),(function(a){a[a.Red=1]="Red",a[a.Green=2]="Green",a[a.Blue=4]="Blue",a[a.Alpha=8]="Alpha",a[a.All=15]="All"})(Lf||(Lf={})),(function(a){a.Zero="zero",a.One="one",a.Src="src",a.OneMinusSrc="one-minus-src",a.SrcAlpha="src-alpha",a.OneMinusSrcAlpha="one-minus-src-alpha",a.Dst="dst",a.OneMinusDst="one-minus-dst",a.DstAlpha="dst-alpha",a.OneMinusDstAlpha="one-minus-dst-alpha",a.SrcAlphaSaturated="src-alpha-saturated",a.Constant="constant",a.OneMinusConstant="one-minus-constant"})(Mf||(Mf={})),(function(a){a.Add="add",a.Subtract="subtract",a.ReverseSubtract="reverse-subtract",a.Min="min",a.Max="max"})(Nf||(Nf={})),(function(a){a.Keep="keep",a.Zero="zero",a.Replace="replace",a.Invert="invert",a.IncrementClamp="increment-clamp",a.DecrementClamp="decrement-clamp",a.IncrementWrap="increment-wrap",a.DecrementWrap="decrement-wrap"})(Of||(Of={})),(function(a){a.Uint16="uint16",a.Uint32="uint32"})(Pf||(Pf={})),(function(a){a.Uint8x2="uint8x2",a.Uint8x4="uint8x4",a.Sint8x2="sint8x2",a.Sint8x4="sint8x4",a.Unorm8x2="unorm8x2",a.Unorm8x4="unorm8x4",a.Snorm8x2="snorm8x2",a.Snorm8x4="snorm8x4",a.Uint16x2="uint16x2",a.Uint16x4="uint16x4",a.Sint16x2="sint16x2",a.Sint16x4="sint16x4",a.Unorm16x2="unorm16x2",a.Unorm16x4="unorm16x4",a.Snorm16x2="snorm16x2",a.Snorm16x4="snorm16x4",a.Float16x2="float16x2",a.Float16x4="float16x4",a.Float32="float32",a.Float32x2="float32x2",a.Float32x3="float32x3",a.Float32x4="float32x4",a.Uint32="uint32",a.Uint32x2="uint32x2",a.Uint32x3="uint32x3",a.Uint32x4="uint32x4",a.Sint32="sint32",a.Sint32x2="sint32x2",a.Sint32x3="sint32x3",a.Sint32x4="sint32x4"})(Qf||(Qf={})),(function(a){a.Vertex="vertex",a.Instance="instance"})(Rf||(Rf={})),(function(a){a.Load="load"})(Sf||(Sf={})),(function(a){a.Store="store",a.Discard="discard"})(Tf||(Tf={})),(function(a){a.Occlusion="occlusion",a.PipelineStatistics="pipeline-statistics",a.Timestamp="timestamp"})(Uf||(Uf={})),(function(a){a.VertexShaderInvocations="vertex-shader-invocations",a.ClipperInvocations="clipper-invocations",a.ClipperPrimitivesOut="clipper-primitives-out",a.FragmentShaderInvocations="fragment-shader-invocations",a.ComputeShaderInvocations="compute-shader-invocations"})(Vf||(Vf={})),(function(a){a.Opaque="opaque",a.Premultiplied="premultiplied"})(Wf||(Wf={})),(function(a){a.Destroyed="destroyed"})(Xf||(Xf={})),(function(a){a.OutOfMemory="out-of-memory",a.Validation="validation"})(Yf||(Yf={}));var ag=function(){function a(){this.shaderLanguage=Xd.a.GLSL}return a.prototype._addUniformToLeftOverUBO=function(a,b,c){a=(c=this._getArraySize(a,b,c))[0],b=c[1],c=c[2];for(var d=0;d=0&&(f.push(g[d]),e.push(b))}this.shaderProcessingContext.attributeNamesFromEffect=f,this.shaderProcessingContext.attributeLocationsFromEffect=e},a.prototype.buildUniformLayout=function(){if(this.shaderProcessingContext.leftOverUniforms.length){this.uniformBuffer=new $c.a(this.engine,void 0,void 0,"leftOver-"+this._name);for(var a=0,b=this.shaderProcessingContext.leftOverUniforms;a)?$/,"$1");d=ag.UniformSizes[d];this.uniformBuffer.addUniform(c.name,d,c.length),this._leftOverUniformsByName[c.name]=c.type}this.uniformBuffer.create()}},a.prototype.dispose=function(){this.uniformBuffer&&this.uniformBuffer.dispose()},a.prototype.setInt=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt(a,b)},a.prototype.setInt2=function(a,b,c){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt2(a,b,c)},a.prototype.setInt3=function(a,b,c,d){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt3(a,b,c,d)},a.prototype.setInt4=function(a,b,c,d,e){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt4(a,b,c,d,e)},a.prototype.setIntArray=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateIntArray(a,b)},a.prototype.setIntArray2=function(a,b){this.setIntArray(a,b)},a.prototype.setIntArray3=function(a,b){this.setIntArray(a,b)},a.prototype.setIntArray4=function(a,b){this.setIntArray(a,b)},a.prototype.setArray=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateArray(a,b)},a.prototype.setArray2=function(a,b){this.setArray(a,b)},a.prototype.setArray3=function(a,b){this.setArray(a,b)},a.prototype.setArray4=function(a,b){this.setArray(a,b)},a.prototype.setMatrices=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrices(a,b)},a.prototype.setMatrix=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix(a,b)},a.prototype.setMatrix3x3=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix3x3(a,b)},a.prototype.setMatrix2x2=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix2x2(a,b)},a.prototype.setFloat=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat(a,b)},a.prototype.setVector2=function(a,b){this.setFloat2(a,b.x,b.y)},a.prototype.setFloat2=function(a,b,c){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat2(a,b,c)},a.prototype.setVector3=function(a,b){this.setFloat3(a,b.x,b.y,b.z)},a.prototype.setFloat3=function(a,b,c,d){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat3(a,b,c,d)},a.prototype.setVector4=function(a,b){this.setFloat4(a,b.x,b.y,b.z,b.w)},a.prototype.setFloat4=function(a,b,c,d,e){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat4(a,b,c,d,e)},a.prototype.setColor3=function(a,b){this.setFloat3(a,b.r,b.g,b.b)},a.prototype.setColor4=function(a,b,c){this.setFloat4(a,b.r,b.g,b.b,c)},a.prototype.setDirectColor4=function(a,b){this.setFloat4(a,b.r,b.g,b.b,b.a)},a.prototype._getVertexShaderCode=function(){var a;return null===(a=this.sources)||void 0===a?void 0:a.vertex},a.prototype._getFragmentShaderCode=function(){var a;return null===(a=this.sources)||void 0===a?void 0:a.fragment},a}(),cg={mat2:2,mat3:3,mat4:4,mat2x2:2,mat3x3:3,mat4x4:4},dg=function(){function a(a){this.shaderLanguage=a,this._attributeNextLocation=0,this._varyingNextLocation=0,this.freeGroupIndex=0,this.freeBindingIndex=0,this.availableVaryings={},this.availableAttributes={},this.availableBuffers={},this.availableTextures={},this.availableSamplers={},this.orderedAttributes=[],this.bindGroupLayoutEntries=[],this.bindGroupLayoutEntryInfo=[],this.bindGroupEntries=[],this.bufferNames=[],this.textureNames=[],this.samplerNames=[],this.leftOverUniforms=[],this._findStartingGroupBinding()}return Object.defineProperty(a,"KnownUBOs",{get:function(){return a._SimplifiedKnownBindings?a._SimplifiedKnownUBOs:a._KnownUBOs},enumerable:!1,configurable:!0}),a.prototype._findStartingGroupBinding=function(){var b=a.KnownUBOs,c=[];for(var d in b){var e=b[d].binding;-1!==e.groupIndex&&(void 0===c[e.groupIndex]?c[e.groupIndex]=e.bindingIndex:c[e.groupIndex]=Math.max(c[e.groupIndex],e.bindingIndex))}this.freeGroupIndex=c.length-1,0===this.freeGroupIndex?(this.freeGroupIndex++,this.freeBindingIndex=0):this.freeBindingIndex=c[c.length-1]+1},a.prototype.getAttributeNextLocation=function(a,b){void 0===b&&(b=0);var c=this._attributeNextLocation;return this._attributeNextLocation+=(null!==(a=cg[a])&&void 0!==a?a:1)*(b||1),c},a.prototype.getVaryingNextLocation=function(a,b){void 0===b&&(b=0);var c=this._varyingNextLocation;return this._varyingNextLocation+=(null!==(a=cg[a])&&void 0!==a?a:1)*(b||1),c},a.prototype.getNextFreeUBOBinding=function(){return this._getNextFreeBinding(1)},a.prototype._getNextFreeBinding=function(a){if(this.freeBindingIndex>65536-a&&(this.freeGroupIndex++,this.freeBindingIndex=0),4===this.freeGroupIndex)throw"Too many textures or UBOs have been declared and it is not supported in WebGPU.";var b={groupIndex:this.freeGroupIndex,bindingIndex:this.freeBindingIndex};return this.freeBindingIndex+=a,b},a._SimplifiedKnownBindings=!0,a._SimplifiedKnownUBOs={Scene:{binding:{groupIndex:0,bindingIndex:0}},Light0:{binding:{groupIndex:-1,bindingIndex:-1}},Light1:{binding:{groupIndex:-1,bindingIndex:-1}},Light2:{binding:{groupIndex:-1,bindingIndex:-1}},Light3:{binding:{groupIndex:-1,bindingIndex:-1}},Light4:{binding:{groupIndex:-1,bindingIndex:-1}},Light5:{binding:{groupIndex:-1,bindingIndex:-1}},Light6:{binding:{groupIndex:-1,bindingIndex:-1}},Light7:{binding:{groupIndex:-1,bindingIndex:-1}},Light8:{binding:{groupIndex:-1,bindingIndex:-1}},Light9:{binding:{groupIndex:-1,bindingIndex:-1}},Light10:{binding:{groupIndex:-1,bindingIndex:-1}},Light11:{binding:{groupIndex:-1,bindingIndex:-1}},Light12:{binding:{groupIndex:-1,bindingIndex:-1}},Light13:{binding:{groupIndex:-1,bindingIndex:-1}},Light14:{binding:{groupIndex:-1,bindingIndex:-1}},Light15:{binding:{groupIndex:-1,bindingIndex:-1}},Light16:{binding:{groupIndex:-1,bindingIndex:-1}},Light17:{binding:{groupIndex:-1,bindingIndex:-1}},Light18:{binding:{groupIndex:-1,bindingIndex:-1}},Light19:{binding:{groupIndex:-1,bindingIndex:-1}},Light20:{binding:{groupIndex:-1,bindingIndex:-1}},Light21:{binding:{groupIndex:-1,bindingIndex:-1}},Light22:{binding:{groupIndex:-1,bindingIndex:-1}},Light23:{binding:{groupIndex:-1,bindingIndex:-1}},Light24:{binding:{groupIndex:-1,bindingIndex:-1}},Light25:{binding:{groupIndex:-1,bindingIndex:-1}},Light26:{binding:{groupIndex:-1,bindingIndex:-1}},Light27:{binding:{groupIndex:-1,bindingIndex:-1}},Light28:{binding:{groupIndex:-1,bindingIndex:-1}},Light29:{binding:{groupIndex:-1,bindingIndex:-1}},Light30:{binding:{groupIndex:-1,bindingIndex:-1}},Light31:{binding:{groupIndex:-1,bindingIndex:-1}},Material:{binding:{groupIndex:-1,bindingIndex:-1}},Mesh:{binding:{groupIndex:-1,bindingIndex:-1}}},a._KnownUBOs={Scene:{binding:{groupIndex:0,bindingIndex:0}},Light0:{binding:{groupIndex:1,bindingIndex:0}},Light1:{binding:{groupIndex:1,bindingIndex:1}},Light2:{binding:{groupIndex:1,bindingIndex:2}},Light3:{binding:{groupIndex:1,bindingIndex:3}},Light4:{binding:{groupIndex:1,bindingIndex:4}},Light5:{binding:{groupIndex:1,bindingIndex:5}},Light6:{binding:{groupIndex:1,bindingIndex:6}},Light7:{binding:{groupIndex:1,bindingIndex:7}},Light8:{binding:{groupIndex:1,bindingIndex:8}},Light9:{binding:{groupIndex:1,bindingIndex:9}},Light10:{binding:{groupIndex:1,bindingIndex:10}},Light11:{binding:{groupIndex:1,bindingIndex:11}},Light12:{binding:{groupIndex:1,bindingIndex:12}},Light13:{binding:{groupIndex:1,bindingIndex:13}},Light14:{binding:{groupIndex:1,bindingIndex:14}},Light15:{binding:{groupIndex:1,bindingIndex:15}},Light16:{binding:{groupIndex:1,bindingIndex:16}},Light17:{binding:{groupIndex:1,bindingIndex:17}},Light18:{binding:{groupIndex:1,bindingIndex:18}},Light19:{binding:{groupIndex:1,bindingIndex:19}},Light20:{binding:{groupIndex:1,bindingIndex:20}},Light21:{binding:{groupIndex:1,bindingIndex:21}},Light22:{binding:{groupIndex:1,bindingIndex:22}},Light23:{binding:{groupIndex:1,bindingIndex:23}},Light24:{binding:{groupIndex:1,bindingIndex:24}},Light25:{binding:{groupIndex:1,bindingIndex:25}},Light26:{binding:{groupIndex:1,bindingIndex:26}},Light27:{binding:{groupIndex:1,bindingIndex:27}},Light28:{binding:{groupIndex:1,bindingIndex:28}},Light29:{binding:{groupIndex:1,bindingIndex:29}},Light30:{binding:{groupIndex:1,bindingIndex:30}},Light31:{binding:{groupIndex:1,bindingIndex:31}},Material:{binding:{groupIndex:2,bindingIndex:0}},Mesh:{binding:{groupIndex:2,bindingIndex:1}}},a}(),eg=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._missingVaryings=[],b._textureArrayProcessing=[],b.shaderLanguage=Xd.a.GLSL,b}return Object(l.d)(b,a),b.prototype._getArraySize=function(a,b,c){var d=0,e=a.indexOf("["),f=a.indexOf("]");if(e>0&&f>0){f=a.substring(e+1,f);d=+f,isNaN(d)&&(d=+c[f.trim()]),a=a.substr(0,e)}return[a,b,d]},b.prototype.initializeShaders=function(a){this.webgpuProcessingContext=a,this._missingVaryings.length=0,this._textureArrayProcessing.length=0},b.prototype.varyingProcessor=function(a,b,c,d){this._preProcessors=c;d=/s*varyings+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/gm.exec(a);if(null!=d){var e,f=d[1],g=d[2];b?(e=this.webgpuProcessingContext.availableVaryings[g],this._missingVaryings[e]="",void 0===e&&q.a.Warn("Invalid fragment shader: The varying named ""+g+"" is not declared in the vertex shader! This declaration will be ignored.")):(e=this.webgpuProcessingContext.getVaryingNextLocation(f,this._getArraySize(g,f,c)[2]),this.webgpuProcessingContext.availableVaryings[g]=e,this._missingVaryings[e]="layout(location = "+e+") in "+f+" "+g+";"),a=a.replace(d[0],void 0===e?"":"layout(location = "+e+") "+(b?"in":"out")+" "+f+" "+g+";")}return a},b.prototype.attributeProcessor=function(a,b,c){this._preProcessors=b;c=/s*attributes+(S+)s+(S+)s*;/gm.exec(a);if(null!=c){var d=c[1],e=c[2];b=this.webgpuProcessingContext.getAttributeNextLocation(d,this._getArraySize(e,d,b)[2]);this.webgpuProcessingContext.availableAttributes[e]=b,this.webgpuProcessingContext.orderedAttributes[b]=e,a=a.replace(c[0],"layout(location = "+b+") in "+d+" "+e+";")}return a},b.prototype.uniformProcessor=function(a,b,c,d){var e,f;this._preProcessors=c;d=/s*uniforms+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/gm.exec(a);if(null!=d){var g=d[1];d=d[2];if(0===g.indexOf("sampler")||1===g.indexOf("sampler")){var h=0;d=(e=this._getArraySize(d,g,c))[0],g=e[1],h=e[2];e=this.webgpuProcessingContext.availableTextures[d];if(!e){e={autoBindSampler:!0,isTextureArray:h>0,isStorageTexture:!1,textures:[],sampleType:Ff.Float};for(var i=0;i<(h||1);++i)e.textures.push(this.webgpuProcessingContext.getNextFreeUBOBinding())}f=null!==(f=ag._SamplerTypeByWebGLSamplerType[g])&&void 0!==f?f:"sampler";var j=!!ag._IsComparisonSamplerByWebGPUSamplerType[f],k=j?Ef.Comparison:Ef.Filtering,m=d+ag.AutoSamplerSuffix,r=this.webgpuProcessingContext.availableSamplers[m];r||(r={binding:this.webgpuProcessingContext.getNextFreeUBOBinding(),type:k});k="u"===g.charAt(0)?"u":"i"===g.charAt(0)?"i":"";k&&(g=g.substr(1));j=j?Ff.Depth:"u"===k?Ff.Uint:"i"===k?Ff.Sint:Ff.Float;e.sampleType=j;j=h>0;var s=r.binding.groupIndex,t=r.binding.bindingIndex,z=ag._SamplerFunctionByWebGLSamplerType[g],C=ag._TextureTypeByWebGLSamplerType[g],D=ag._GpuTextureViewDimensionByWebGPUTextureType[C];if(j){j=[];j.push("layout(set = "+s+", binding = "+t+") uniform "+k+f+" "+m+";"),a=" ";for(i=0;i0?" ":"")+"#define "+d+i+" "+k+z+"("+d+"Texture"+i+", "+m+")"}a=j.join(" ")+a,this._textureArrayProcessing.push(d)}else h=1,a="layout(set = "+s+", binding = "+t+") uniform "+k+f+" "+m+"; layout(set = "+e.textures[0].groupIndex+", binding = "+e.textures[0].bindingIndex+") uniform "+C+" "+d+"Texture; #define "+d+" "+k+z+"("+d+"Texture, "+m+")";this.webgpuProcessingContext.availableTextures[d]=e,this.webgpuProcessingContext.availableSamplers[m]=r,this._addSamplerBindingDescription(m,r,!b);for(i=0;i0?b+=" "+d.type+" "+d.name+"["+d.length+"]; ":b+=" "+d.type+" "+d.name+"; "}return b+="}; "},b.prototype.finalizeShaders=function(a,b,c){for(c=0;c0&&(b=d+" "+b)}d=this._buildLeftOverUBO();return a=d+a,b=d+b,this._collectBindingNames(),this._preCreateBindGroupEntries(),this._preProcessors=null,{vertexCode:a,fragmentCode:b}},b}(ag);rb="#if NUM_BONE_INFLUENCERS>0 #ifdef BONETEXTURE var boneSampler : texture_2d; uniform boneTextureWidth : f32; #else uniform mBones : array; #ifdef BONES_VELOCITY_ENABLED uniform mPreviousBones : array; #endif #endif attribute matricesIndices : vec4; attribute matricesWeights : vec4; #if NUM_BONE_INFLUENCERS>4 attribute matricesIndicesExtra : vec4; attribute matricesWeightsExtra : vec4; #endif #ifdef BONETEXTURE fn readMatrixFromRawSampler(smp : texture_2d,index : f32) -> mat4x4 { let offset=i32(index)*4; let m0=textureLoad(smp,vec2(offset+0,0),0); let m1=textureLoad(smp,vec2(offset+1,0),0); let m2=textureLoad(smp,vec2(offset+2,0),0); let m3=textureLoad(smp,vec2(offset+3,0),0); return mat4x4(m0,m1,m2,m3); } #endif #endif";X.a.IncludesShadersStoreWGSL.bonesDeclaration=rb;a="#if NUM_BONE_INFLUENCERS>0 var influence : mat4x4; #ifdef BONETEXTURE influence=readMatrixFromRawSampler(boneSampler,matricesIndices[0])*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[1])*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[2])*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[3])*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[0])*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[1])*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[2])*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[3])*matricesWeightsExtra[3]; #endif #else influence=uniforms.mBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence=influence+uniforms.mBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence=influence+uniforms.mBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence=influence+uniforms.mBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence=influence+uniforms.mBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence=influence+uniforms.mBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence=influence+uniforms.mBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence=influence+uniforms.mBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif #endif finalWorld=finalWorld*influence; #endif";X.a.IncludesShadersStoreWGSL.bonesVertex=a;rb="#ifdef INSTANCES attribute world0 : vec4; attribute world1 : vec4; attribute world2 : vec4; attribute world3 : vec4; #if defined(THIN_INSTANCES) && !defined(WORLD_UBO) uniform world : mat4x4; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) attribute previousWorld0 : vec4; attribute previousWorld1 : vec4; attribute previousWorld2 : vec4; attribute previousWorld3 : vec4; #ifdef THIN_INSTANCES uniform previousWorld : mat4x4; #endif #endif #else #if !defined(WORLD_UBO) uniform world : mat4x4; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) uniform previousWorld : mat4x4; #endif #endif";X.a.IncludesShadersStoreWGSL.instancesDeclaration=rb;a="#ifdef INSTANCES var finalWorld=mat4x4(world0,world1,world2,world3); #if defined(PREPASS_VELOCITY) || defined(VELOCITY) var finalPreviousWorld=mat4x4(previousWorld0,previousWorld1,previousWorld2,previousWorld3); #endif #ifdef THIN_INSTANCES #if !defined(WORLD_UBO) finalWorld=uniforms.world*finalWorld; #else finalWorld=mesh.world*finalWorld; #endif #if defined(PREPASS_VELOCITY) || defined(VELOCITY) finalPreviousWorld=previousWorld*finalPreviousWorld; #endif #endif #else #if !defined(WORLD_UBO) var finalWorld=uniforms.world; #else var finalWorld=mesh.world; #endif #if defined(PREPASS_VELOCITY) || defined(VELOCITY) var finalPreviousWorld=previousWorld; #endif #endif";X.a.IncludesShadersStoreWGSL.instancesVertex=a;rb="[[block]] struct Mesh { world : mat4x4; visibility : f32; }; var mesh : Mesh; #define WORLD_UBO ";X.a.IncludesShadersStoreWGSL.meshUboDeclaration=rb;a="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE vertexID=f32(gl_VertexID)*uniforms.morphTargetTextureInfo.x; positionUpdated=positionUpdated+(readVector3FromRawSampler({X},vertexID)-position)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #ifdef MORPHTARGETS_NORMAL normalUpdated=normalUpdated+(readVector3FromRawSampler({X},vertexID)-normal)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #endif #ifdef MORPHTARGETS_UV uvUpdated=uvUpdated+(readVector3FromRawSampler({X},vertexID).xy-uv)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz=tangentUpdated.xyz+(readVector3FromRawSampler({X},vertexID)-tangent.xyz)*uniforms.morphTargetInfluences[{X}]; #endif #else positionUpdated=positionUpdated+(position{X}-position)*uniforms.morphTargetInfluences[{X}]; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(normal{X}-normal)*uniforms.morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz=tangentUpdated.xyz+(tangent{X}-tangent.xyz)*uniforms.morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_UV uvUpdated=uvUpdated+(uv_{X}-uv)*uniforms.morphTargetInfluences[{X}]; #endif #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertex=a;rb="#ifdef MORPHTARGETS #ifndef MORPHTARGETS_TEXTURE attribute position{X} : vec3; #ifdef MORPHTARGETS_NORMAL attribute normal{X} : vec3; #endif #ifdef MORPHTARGETS_TANGENT attribute tangent{X} : vec3; #endif #ifdef MORPHTARGETS_UV attribute uv_{X} : vec2; #endif #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertexDeclaration=rb;X.a.IncludesShadersStoreWGSL.morphTargetsVertexGlobal="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE var vertexID : f32; #endif #endif";a="#ifdef MORPHTARGETS uniform morphTargetInfluences : array; #ifdef MORPHTARGETS_TEXTURE uniform morphTargetTextureIndices : array; uniform morphTargetTextureInfo : vec3; var morphTargets : texture_2d_array; var morphTargetsSampler : sampler; fn readVector3FromRawSampler(targetIndex : i32,vertexIndex : f32) -> vec3 { let y=floor(vertexIndex/uniforms.morphTargetTextureInfo.y); let x=vertexIndex-y*uniforms.morphTargetTextureInfo.y; let textureUV=vec2((x+0.5)/uniforms.morphTargetTextureInfo.y,(y+0.5)/uniforms.morphTargetTextureInfo.z); return textureSampleLevel(morphTargets,morphTargetsSampler,textureUV,i32(uniforms.morphTargetTextureIndices[targetIndex]),0.0).xyz; } #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertexGlobalDeclaration=a;rb="[[block]] struct Scene { viewProjection : mat4x4; #ifdef MULTIVIEW viewProjectionR : mat4x4; #endif view : mat4x4; projection : mat4x4; vEyePosition : vec4; }; var scene : Scene; ";X.a.IncludesShadersStoreWGSL.sceneUboDeclaration=rb;var fg,gg={texture_1d:xf.E1d,texture_2d:xf.E2d,texture_2d_array:xf.E2dArray,texture_3d:xf.E3d,texture_cube:xf.Cube,texture_cube_array:xf.CubeArray,texture_multisampled_2d:xf.E2d,texture_depth_2d:xf.E2d,texture_depth_2d_array:xf.E2dArray,texture_depth_cube:xf.Cube,texture_depth_cube_array:xf.CubeArray,texture_depth_multisampled_2d:xf.E2d,texture_storage_1d:xf.E1d,texture_storage_2d:xf.E2d,texture_storage_2d_array:xf.E2dArray,texture_storage_3d:xf.E3d,texture_external:null},hg=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.shaderLanguage=Xd.a.WGSL,b.uniformRegexp=/uniforms+(w+)s*:s*(.+)s*;/,b.textureRegexp=/vars+(w+)s*:s*((array)?s*(,s*w+s*>s*)?);/,b.noPrecision=!0,b}return Object(l.d)(b,a),b.prototype._getArraySize=function(a,b,c){var d=0,e=b.lastIndexOf(">");if(b.indexOf("array")>=0&&e>0){for(var f=e;f>0&&" "!==b.charAt(f)&&","!==b.charAt(f);)f--;e=b.substring(f+1,e);for(d=+e,isNaN(d)&&(d=+c[e.trim()]);f>0&&(" "===b.charAt(f)||","===b.charAt(f));)f--;b=b.substring(b.indexOf("<")+1,f+1)}return[a,b,d]},b.prototype.initializeShaders=function(a){this.webgpuProcessingContext=a,this._attributesWGSL=[],this._attributesDeclWGSL=[],this._attributeNamesWGSL=[],this._varyingsWGSL=[],this._varyingsDeclWGSL=[],this._varyingNamesWGSL=[]},b.prototype.preProcessShaderCode=function(a){return hf(a)},b.prototype.varyingProcessor=function(a,b,c,d){d=/s*varyings+(?:(?:highp)?|(?:lowp)?)s*(S+)s*:s*(.+)s*;/gm.exec(a);if(null!==d){var e=d[2];d=d[1];b?void 0===this.webgpuProcessingContext.availableVaryings[d]&&q.a.Warn("Invalid fragment shader: The varying named ""+d+"" is not declared in the vertex shader! This declaration will be ignored."):(b=this.webgpuProcessingContext.getVaryingNextLocation(e,this._getArraySize(d,e,c)[2]),this.webgpuProcessingContext.availableVaryings[d]=b,this._varyingsWGSL.push("[[location("+b+")]] "+d+" : "+e+";"),this._varyingsDeclWGSL.push("var "+d+" : "+e+";"),this._varyingNamesWGSL.push(d)),a=""}return a},b.prototype.attributeProcessor=function(a,b,c){c=/s*attributes+(S+)s*:s*(.+)s*;/gm.exec(a);if(null!==c){var d=c[2];c=c[1];b=this.webgpuProcessingContext.getAttributeNextLocation(d,this._getArraySize(c,d,b)[2]);this.webgpuProcessingContext.availableAttributes[c]=b,this.webgpuProcessingContext.orderedAttributes[b]=c,this._attributesWGSL.push("[[location("+b+")]] "+c+" : "+d+";"),this._attributesDeclWGSL.push("var "+c+" : "+d+";"),this._attributeNamesWGSL.push(c),a=""}return a},b.prototype.uniformProcessor=function(a,b,c,d){b=this.uniformRegexp.exec(a);if(null!==b){d=b[2];b=b[1];this._addUniformToLeftOverUBO(b,d,c),a=""}return a},b.prototype.textureProcessor=function(a,b,c,d){d=this.textureRegexp.exec(a);if(null!==d){var e=d[1],f=d[2],g=!!d[3],h=d[4],i=h.indexOf("storage")>0;d=d[6];var j=i?d.substring(0,d.indexOf(",")).trim():null;g=g?this._getArraySize(e,f,c)[2]:0;f=this.webgpuProcessingContext.availableTextures[e];if(f)g=f.textures.length;else{f={isTextureArray:g>0,isStorageTexture:i,textures:[],sampleType:Ff.Float},g=g||1;for(c=0;c0;var k=gg[h];i=i?Ff.Depth:"u32"===d?Ff.Uint:"i32"===d?Ff.Sint:Ff.Float;if(f.sampleType=i,void 0===k)throw"Can"t get the texture dimension corresponding to the texture function ""+h+""!";for(c=0;c0&&(e+=this._attributesWGSL.join(" ")),e+=" }; ";var f="struct FragmentInputs { [[builtin(position)]] position : vec4; ";this._varyingsWGSL.length>0&&(f+=this._varyingsWGSL.join(" ")),a="var gl_VertexID : u32; var gl_InstanceID : u32; var gl_Position : vec4; "+e+d+(f+=" }; ")+c+a;for(e=" var output : FragmentInputs; gl_VertexID = input.vertexIndex; gl_InstanceID = input.instanceIndex; ",d=0;d0&&(g+=this._varyingsWGSL.join(" ")),g+=" }; ";for(var e="struct FragmentOutputs { [[location(0)]] color : vec4; ",f=!1,h=0;!(f||(h=b.indexOf("gl_FragDepth",h))<0);){var i=h;for(f=!0;h>1&&" "!==b.charAt(h);){if("/"===b.charAt(h)&&"/"===b.charAt(h-1)){f=!1;break}h--}h=i+12}f&&(e+=" [[builtin(frag_depth)]] fragDepth: f32; "),b="var gl_FragCoord : vec4; var gl_FrontFacing : bool; var gl_FragColor : vec4; var gl_FragDepth : f32; "+g+c+(e+="}; ")+b;i=" var output : FragmentOutputs; gl_FragCoord = input.position; gl_FrontFacing = input.frontFacing; ";for(d=0;d)?$/,"$1");g=ag.UniformSizes[g];f.length>0?c+=g<=2?" [[align(16)]] "+f.name+" : [[stride(16)]] array<"+f.type+", "+f.length+">; ":" "+f.name+" : array<"+f.type+", "+f.length+">; ":c+=" "+f.name+" : "+f.type+"; "}return c+="}; ",c+="[[group("+b.binding.groupIndex+"), binding("+b.binding.bindingIndex+")]] var uniforms : "+a+"; "},b.prototype._injectStartingAndEndingCode=function(a,b,c){if(b){var d=a.indexOf("fn main");if(d>=0){for(;d++s+(S+)s*:s*(S+)s*;/gm;;){var d=c.exec(a);if(null===d)break;var e=d[1],f=d[3],g=d[4],h=d[5],i=this.webgpuProcessingContext.availableBuffers[g];if(!i){var j="uniform"===e?dg.KnownUBOs[h]:null;j?(g=h,-1===(h=j.binding).groupIndex&&(h=this.webgpuProcessingContext.getNextFreeUBOBinding())):h=this.webgpuProcessingContext.getNextFreeUBOBinding(),i={binding:h},this.webgpuProcessingContext.availableBuffers[g]=i}this._addBufferBindingDescription(g,this.webgpuProcessingContext.availableBuffers[g],"read_write"===f?Df.Storage:"storage"===e?Df.ReadOnlyStorage:Df.Uniform,b);j=i.binding.groupIndex;h=i.binding.bindingIndex;g=a.substring(0,d.index);f="[[group("+j+"), binding("+h+")]] ";e=a.substring(d.index);a=g+f+e,c.lastIndex+=f.length}return a},b}(ag),ig=function(){function a(a){void 0===a&&(a=null),this.format=Y.RGBA8Unorm,this.textureUsages=0,this.textureAdditionalUsages=0,this._webgpuTexture=a,this._webgpuMSAATexture=null,this.view=null}return Object.defineProperty(a.prototype,"underlyingResource",{get:function(){return this._webgpuTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"msaaTexture",{get:function(){return this._webgpuMSAATexture},set:function(a){this._webgpuMSAATexture=a},enumerable:!1,configurable:!0}),a.prototype.set=function(a){this._webgpuTexture=a},a.prototype.setMSAATexture=function(a){this._webgpuMSAATexture=a},a.prototype.setUsage=function(a,b,c,d,e){b=a!==ob.b.RenderTarget&&b,this.createView({format:this.format,dimension:c?xf.Cube:xf.E2d,mipLevelCount:b?I.a.ILog2(Math.max(d,e))+1:1,baseArrayLayer:0,baseMipLevel:0,arrayLayerCount:c?6:1,aspect:yf.All})},a.prototype.createView=function(a){this.view=this._webgpuTexture.createView(a)},a.prototype.reset=function(){this._webgpuTexture=null,this._webgpuMSAATexture=null,this.view=null},a.prototype.release=function(){var a;null===(a=this._webgpuTexture)||void 0===a||a.destroy(),null===(a=this._webgpuMSAATexture)||void 0===a||a.destroy(),this.reset()},a}();!function(a){a[a.MipMap=0]="MipMap",a[a.InvertYPremultiplyAlpha=1]="InvertYPremultiplyAlpha",a[a.Clear=2]="Clear"}(fg||(fg={}));var jg,kg=[{vertex:" const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f)); layout(location = 0) out vec2 vTex; void main() { vTex = tex[gl_VertexIndex]; gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" layout(set = 0, binding = 0) uniform sampler imgSampler; layout(set = 0, binding = 1) uniform texture2D img; layout(location = 0) in vec2 vTex; layout(location = 0) out vec4 outColor; void main() { outColor = texture(sampler2D(img, imgSampler), vTex); } "},{vertex:" #extension GL_EXT_samplerless_texture_functions : enable const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f)); layout(set = 0, binding = 0) uniform texture2D img; #ifdef INVERTY layout(location = 0) out flat ivec2 vTextureSize; #endif void main() { #ifdef INVERTY vTextureSize = textureSize(img, 0); #endif gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" #extension GL_EXT_samplerless_texture_functions : enable layout(set = 0, binding = 0) uniform texture2D img; #ifdef INVERTY layout(location = 0) in flat ivec2 vTextureSize; #endif layout(location = 0) out vec4 outColor; void main() { #ifdef INVERTY vec4 color = texelFetch(img, ivec2(gl_FragCoord.x, vTextureSize.y - gl_FragCoord.y), 0); #else vec4 color = texelFetch(img, ivec2(gl_FragCoord.xy), 0); #endif #ifdef PREMULTIPLYALPHA color.rgb *= color.a; #endif outColor = color; } "},{vertex:" const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" layout(set = 0, binding = 0) uniform Uniforms { uniform vec4 color; }; layout(location = 0) out vec4 outColor; void main() { outColor = color; } "}],lg=function(){function a(a,b,c,d){this._pipelines={},this._compiledShaders=[],this._deferredReleaseTextures=[],this._device=a,this._glslang=b,this._tintWASM=c,this._bufferManager=d,this._mipmapSampler=a.createSampler({minFilter:Af.Linear}),this._getPipeline(Y.RGBA8Unorm)}return a.ComputeNumMipmapLevels=function(a,b){return I.a.ILog2(Math.max(a,b))+1},a.prototype._getPipeline=function(a,b,c){void 0===b&&(b=fg.MipMap);var d=b===fg.MipMap?1:b===fg.InvertYPremultiplyAlpha?((c.invertY?1:0)<<1)+((c.premultiplyAlpha?1:0)<<2):b===fg.Clear?8:0;this._pipelines[a]||(this._pipelines[a]=[]);var e=this._pipelines[a][d];if(!e){var f="#version 450 ";b===fg.InvertYPremultiplyAlpha&&(c.invertY&&(f+="#define INVERTY "),c.premultiplyAlpha&&(f+="#define PREMULTIPLYALPHA "));c=this._compiledShaders[d];if(!c){var g=this._glslang.compileGLSL(f+kg[b].vertex,"vertex");f=this._glslang.compileGLSL(f+kg[b].fragment,"fragment");this._tintWASM&&(g=this._tintWASM.convertSpirV2WGSL(g),f=this._tintWASM.convertSpirV2WGSL(f));b=this._device.createShaderModule({code:g});g=this._device.createShaderModule({code:f});c=this._compiledShaders[d]=[b,g]}e=this._pipelines[a][d]=this._device.createRenderPipeline({vertex:{module:c[0],entryPoint:"main"},fragment:{module:c[1],entryPoint:"main",targets:[{format:a}]},primitive:{topology:If.TriangleStrip,stripIndexFormat:Pf.Uint16}})}return e},a._GetTextureTypeFromFormat=function(a){switch(a){case Y.R8Unorm:case Y.R8Snorm:case Y.R8Uint:case Y.R8Sint:case Y.RG8Unorm:case Y.RG8Snorm:case Y.RG8Uint:case Y.RG8Sint:case Y.RGBA8Unorm:case Y.RGBA8UnormSRGB:case Y.RGBA8Snorm:case Y.RGBA8Uint:case Y.RGBA8Sint:case Y.BGRA8Unorm:case Y.BGRA8UnormSRGB:case Y.RGB10A2Unorm:case Y.RGB9E5UFloat:case Y.RG11B10UFloat:case Y.Depth24UnormStencil8:case Y.Depth32FloatStencil8:case Y.BC7RGBAUnorm:case Y.BC7RGBAUnormSRGB:case Y.BC6HRGBUFloat:case Y.BC6HRGBFloat:case Y.BC5RGUnorm:case Y.BC5RGSnorm:case Y.BC3RGBAUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC2RGBAUnormSRGB:case Y.BC4RUnorm:case Y.BC4RSnorm:case Y.BC1RGBAUnorm:case Y.BC1RGBAUnormSRGB:return r.a.TEXTURETYPE_UNSIGNED_BYTE;case Y.R16Uint:case Y.R16Sint:case Y.RG16Uint:case Y.RG16Sint:case Y.RGBA16Uint:case Y.RGBA16Sint:case Y.Depth16Unorm:return r.a.TEXTURETYPE_UNSIGNED_SHORT;case Y.R16Float:case Y.RG16Float:case Y.RGBA16Float:return r.a.TEXTURETYPE_HALF_FLOAT;case Y.R32Uint:case Y.R32Sint:case Y.RG32Uint:case Y.RG32Sint:case Y.RGBA32Uint:case Y.RGBA32Sint:return r.a.TEXTURETYPE_UNSIGNED_INTEGER;case Y.R32Float:case Y.RG32Float:case Y.RGBA32Float:case Y.Depth32Float:return r.a.TEXTURETYPE_FLOAT;case Y.Stencil8:throw"No fixed size for Stencil8 format!";case Y.Depth24Plus:throw"No fixed size for Depth24Plus format!";case Y.Depth24PlusStencil8:throw"No fixed size for Depth24PlusStencil8 format!"}return r.a.TEXTURETYPE_UNSIGNED_BYTE},a._GetBlockInformationFromFormat=function(a){switch(a){case Y.R8Unorm:case Y.R8Snorm:case Y.R8Uint:case Y.R8Sint:return{width:1,height:1,length:1};case Y.R16Uint:case Y.R16Sint:case Y.R16Float:case Y.RG8Unorm:case Y.RG8Snorm:case Y.RG8Uint:case Y.RG8Sint:return{width:1,height:1,length:2};case Y.R32Uint:case Y.R32Sint:case Y.R32Float:case Y.RG16Uint:case Y.RG16Sint:case Y.RG16Float:case Y.RGBA8Unorm:case Y.RGBA8UnormSRGB:case Y.RGBA8Snorm:case Y.RGBA8Uint:case Y.RGBA8Sint:case Y.BGRA8Unorm:case Y.BGRA8UnormSRGB:case Y.RGB9E5UFloat:case Y.RGB10A2Unorm:case Y.RG11B10UFloat:return{width:1,height:1,length:4};case Y.RG32Uint:case Y.RG32Sint:case Y.RG32Float:case Y.RGBA16Uint:case Y.RGBA16Sint:case Y.RGBA16Float:return{width:1,height:1,length:8};case Y.RGBA32Uint:case Y.RGBA32Sint:case Y.RGBA32Float:return{width:1,height:1,length:16};case Y.Stencil8:throw"No fixed size for Stencil8 format!";case Y.Depth16Unorm:return{width:1,height:1,length:2};case Y.Depth24Plus:throw"No fixed size for Depth24Plus format!";case Y.Depth24PlusStencil8:throw"No fixed size for Depth24PlusStencil8 format!";case Y.Depth32Float:case Y.Depth24UnormStencil8:return{width:1,height:1,length:4};case Y.Depth32FloatStencil8:return{width:1,height:1,length:5};case Y.BC7RGBAUnorm:case Y.BC7RGBAUnormSRGB:case Y.BC6HRGBUFloat:case Y.BC6HRGBFloat:case Y.BC5RGUnorm:case Y.BC5RGSnorm:case Y.BC3RGBAUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC2RGBAUnormSRGB:return{width:4,height:4,length:16};case Y.BC4RUnorm:case Y.BC4RSnorm:case Y.BC1RGBAUnorm:case Y.BC1RGBAUnormSRGB:return{width:4,height:4,length:8}}return{width:1,height:1,length:4}},a._IsHardwareTexture=function(a){return!!a.release},a._IsInternalTexture=function(a){return!!a.dispose},a.GetCompareFunction=function(a){switch(a){case r.a.ALWAYS:return Bf.Always;case r.a.EQUAL:return Bf.Equal;case r.a.GREATER:return Bf.Greater;case r.a.GEQUAL:return Bf.GreaterEqual;case r.a.LESS:return Bf.Less;case r.a.LEQUAL:return Bf.LessEqual;case r.a.NEVER:return Bf.Never;case r.a.NOTEQUAL:return Bf.NotEqual;default:return Bf.Less}},a.IsImageBitmap=function(a){return void 0!==a.close},a.IsImageBitmapArray=function(a){return Array.isArray(a)&&void 0!==a[0].close},a.prototype.setCommandEncoder=function(a){this._commandEncoderForCreation=a},a.IsCompressedFormat=function(a){switch(a){case Y.BC7RGBAUnormSRGB:case Y.BC7RGBAUnorm:case Y.BC6HRGBFloat:case Y.BC6HRGBUFloat:case Y.BC5RGSnorm:case Y.BC5RGUnorm:case Y.BC4RSnorm:case Y.BC4RUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC3RGBAUnorm:case Y.BC2RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC1RGBAUnormSRGB:case Y.BC1RGBAUnorm:return!0}return!1},a.GetWebGPUTextureFormat=function(a,b,c){switch(void 0===c&&(c=!1),b){case r.a.TEXTUREFORMAT_DEPTH16:return Y.Depth16Unorm;case r.a.TEXTUREFORMAT_DEPTH24_STENCIL8:return Y.Depth24PlusStencil8;case r.a.TEXTUREFORMAT_DEPTH32_FLOAT:return Y.Depth32Float;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:return c?Y.BC7RGBAUnormSRGB:Y.BC7RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:return Y.BC6HRGBUFloat;case r.a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:return Y.BC6HRGBFloat;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:return c?Y.BC3RGBAUnormSRGB:Y.BC3RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:return c?Y.BC2RGBAUnormSRGB:Y.BC2RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:return c?Y.BC1RGBAUnormSRGB:Y.BC1RGBAUnorm}switch(a){case r.a.TEXTURETYPE_BYTE:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R8Snorm;case r.a.TEXTUREFORMAT_RG:return Y.RG8Snorm;case r.a.TEXTUREFORMAT_RGB:throw"RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R8Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG8Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:return Y.RGBA8Sint;default:return Y.RGBA8Snorm}case r.a.TEXTURETYPE_UNSIGNED_BYTE:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R8Unorm;case r.a.TEXTUREFORMAT_RG:return Y.RG8Unorm;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:return c?Y.RGBA8UnormSRGB:Y.RGBA8Unorm;case r.a.TEXTUREFORMAT_BGRA:return c?Y.BGRA8UnormSRGB:Y.BGRA8Unorm;case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R8Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG8Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:return Y.RGBA8Uint;case r.a.TEXTUREFORMAT_ALPHA:throw"TEXTUREFORMAT_ALPHA format not supported in WebGPU";case r.a.TEXTUREFORMAT_LUMINANCE:throw"TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";case r.a.TEXTUREFORMAT_LUMINANCE_ALPHA:throw"TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";default:return Y.RGBA8Unorm}case r.a.TEXTURETYPE_SHORT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R16Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG16Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA16Sint}case r.a.TEXTURETYPE_UNSIGNED_SHORT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R16Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG16Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA16Uint}case r.a.TEXTURETYPE_INT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R32Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG32Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA32Sint}case r.a.TEXTURETYPE_UNSIGNED_INTEGER:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R32Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG32Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA32Uint}case r.a.TEXTURETYPE_FLOAT:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R32Float;case r.a.TEXTUREFORMAT_RG:return Y.RG32Float;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:default:return Y.RGBA32Float}case r.a.TEXTURETYPE_HALF_FLOAT:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R16Float;case r.a.TEXTUREFORMAT_RG:return Y.RG16Float;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:default:return Y.RGBA16Float}case r.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:throw"TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:throw"TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:throw"TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:throw"TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:throw"TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:switch(b){case r.a.TEXTUREFORMAT_RGBA:return Y.RGB10A2Unorm;case r.a.TEXTUREFORMAT_RGBA_INTEGER:throw"TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";default:return Y.RGB10A2Unorm}}return c?Y.RGBA8UnormSRGB:Y.RGBA8Unorm},a.prototype.invertYPreMultiplyAlpha=function(a,b,c,d,e,f,g,h,i,j){var k,m;void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=0),void 0===h&&(h=0),void 0===i&&(i=1);var n=void 0===j,r=this._getPipeline(d,fg.InvertYPremultiplyAlpha,{invertY:e,premultiplyAlpha:f}),s=r.getBindGroupLayout(0);n&&(j=this._device.createCommandEncoder({})),null===(m=(k=j).pushDebugGroup)||void 0===m||m.call(k,"internal process texture - invertY="+e+" premultiplyAlpha="+f);m=this.createTexture({width:b,height:c,layers:1},!1,!1,!1,!1,!1,d,1,j,wf.CopySrc|wf.RenderAttachment|wf.TextureBinding);k=j.beginRenderPass({colorAttachments:[{view:m.createView({format:d,dimension:xf.E2d,baseMipLevel:0,mipLevelCount:1,arrayLayerCount:1,baseArrayLayer:0}),loadValue:Sf.Load,storeOp:Tf.Store}]});e=this._device.createBindGroup({layout:s,entries:[{binding:0,resource:a.createView({format:d,dimension:xf.E2d,baseMipLevel:h,mipLevelCount:1,arrayLayerCount:i,baseArrayLayer:Math.max(g,0)})}]});k.setPipeline(r),k.setBindGroup(0,e),k.draw(4,1,0,0),k.endPass(),j.copyTextureToTexture({texture:m},{texture:a,mipLevel:h,origin:{x:0,y:0,z:Math.max(g,0)}},{width:b,height:c,depthOrArrayLayers:1}),this._deferredReleaseTextures.push([m,null]),null===(s=(f=j).popDebugGroup)||void 0===s||s.call(f),n&&(this._device.queue.submit([j.finish()]),j=null)},a.prototype.copyWithInvertY=function(a,b,c,d){var e,f,g=void 0===d;b=this._getPipeline(b,fg.InvertYPremultiplyAlpha,{invertY:!0,premultiplyAlpha:!1});var h=b.getBindGroupLayout(0);g&&(d=this._device.createCommandEncoder({})),null===(f=(e=d).pushDebugGroup)||void 0===f||f.call(e,"internal copy texture with invertY");f=d.beginRenderPass(c);e=this._device.createBindGroup({layout:h,entries:[{binding:0,resource:a}]});f.setPipeline(b),f.setBindGroup(0,e),f.draw(4,1,0,0),f.endPass(),null===(h=(c=d).popDebugGroup)||void 0===h||h.call(c),g&&(this._device.queue.submit([d.finish()]),d=null)},a.prototype.createTexture=function(b,c,d,e,f,g,h,i,j,k,q){void 0===c&&(c=!1),void 0===d&&(d=!1),void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=!1),void 0===h&&(h=Y.RGBA8Unorm),void 0===i&&(i=1),void 0===k&&(k=-1),void 0===q&&(q=0),i>1&&(i=4);var r=b.layers||1,s={width:b.width,height:b.height,depthOrArrayLayers:r},t=a.IsCompressedFormat(h),u=c?a.ComputeNumMipmapLevels(b.width,b.height):1;k=k>=0?k:wf.CopySrc|wf.CopyDst|wf.TextureBinding;q|=c&&!t?wf.CopySrc|wf.RenderAttachment:0,t||(q|=wf.RenderAttachment|wf.CopyDst);t=this._device.createTexture({size:s,dimension:g?vf.E3d:vf.E2d,format:h,usage:k|q,sampleCount:i,mipLevelCount:u});return a.IsImageBitmap(b)&&(this.updateTexture(b,t,b.width,b.height,r,h,0,0,e,f,0,0,j),c&&d&&this.generateMipmaps(t,h,u,0,j)),t},a.prototype.createCubeTexture=function(b,c,d,e,f,g,h,i,j,k){void 0===c&&(c=!1),void 0===d&&(d=!1),void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=Y.RGBA8Unorm),void 0===h&&(h=1),void 0===j&&(j=-1),void 0===k&&(k=0),h>1&&(h=4);var p=a.IsImageBitmapArray(b)?b[0].width:b.width,q=a.IsImageBitmapArray(b)?b[0].height:b.height,r=a.IsCompressedFormat(g),s=c?a.ComputeNumMipmapLevels(p,q):1;j=j>=0?j:wf.CopySrc|wf.CopyDst|wf.TextureBinding;k|=c&&!r?wf.CopySrc|wf.RenderAttachment:0,r||(k|=wf.RenderAttachment|wf.CopyDst);r=this._device.createTexture({size:{width:p,height:q,depthOrArrayLayers:6},dimension:vf.E2d,format:g,usage:j|k,sampleCount:h,mipLevelCount:s});return a.IsImageBitmapArray(b)&&(this.updateCubeTextures(b,r,p,q,g,e,f,0,0,i),c&&d&&this.generateCubeMipmaps(r,g,s,i)),r},a.prototype.generateCubeMipmaps=function(a,b,c,d){var e,f,g=void 0===d;g&&(d=this._device.createCommandEncoder({})),null===(f=(e=d).pushDebugGroup)||void 0===f||f.call(e,"create cube mipmaps - "+c+" levels");for(f=0;f<6;++f)this.generateMipmaps(a,b,c,f,d);null===(a=(e=d).popDebugGroup)||void 0===a||a.call(e),g&&(this._device.queue.submit([d.finish()]),d=null)},a.prototype.generateMipmaps=function(a,b,c,d,e){var f,g;void 0===d&&(d=0);var h=void 0===e,i=this._getPipeline(b),j=i.getBindGroupLayout(0);h&&(e=this._device.createCommandEncoder({})),null===(g=(f=e).pushDebugGroup)||void 0===g||g.call(f,"create mipmaps for face #"+d+" - "+c+" levels");for(g=1;g15728640;)this._device.queue.writeBuffer(f,b+i,c.buffer,a+i,15728640),i+=15728640;this._device.queue.writeBuffer(f,b+i,c.buffer,a+i,e-i)},a.prototype._GetHalfFloatAsFloatRGBAArrayBuffer=function(a,b,c){c||(c=new Float32Array(a));for(b=new Uint16Array(b);a--;)c[a]=Oe(b[a]);return c},a.prototype.readDataFromBuffer=function(a,b,c,d,e,f,g,h,i,j,k){var p=this;void 0===g&&(g=r.a.TEXTURETYPE_UNSIGNED_BYTE),void 0===h&&(h=0),void 0===i&&(i=null),void 0===j&&(j=!0),void 0===k&&(k=!1);var u=g===r.a.TEXTURETYPE_FLOAT?2:g===r.a.TEXTURETYPE_HALF_FLOAT?1:0;return new Promise(function(c,l){a.mapAsync(uf.Read,h,b).then(function(){var l=a.getMappedRange(h,b),m=i;if(k)m=null===m?Object(xe.a)(g,b,!0,l):Object(xe.a)(g,m.buffer,void 0,l);else if(null===m)switch(u){case 0:(m=new Uint8Array(b)).set(new Uint8Array(l));break;case 1:m=p._GetHalfFloatAsFloatRGBAArrayBuffer(b/2,l);break;case 2:(m=new Float32Array(b/4)).set(new Float32Array(l))}else switch(u){case 0:(m=new Uint8Array(m.buffer)).set(new Uint8Array(l));break;case 1:m=p._GetHalfFloatAsFloatRGBAArrayBuffer(b/2,l,i);break;case 2:(m=new Float32Array(m.buffer)).set(new Float32Array(l))}if(e!==f){1!==u||k||(e*=2,f*=2);for(var l=new Uint8Array(m.buffer),v=e,w=0,x=1;x1&&(0!==f||0!==g)?{magFilter:Af.Linear,minFilter:Af.Linear,mipmapFilter:Af.Linear,anisotropyEnabled:!0}:{magFilter:c,minFilter:d,mipmapFilter:e,lodMinClamp:f,lodMaxClamp:g}},a._GetWrappingMode=function(a){switch(a){case r.a.TEXTURE_WRAP_ADDRESSMODE:return zf.Repeat;case r.a.TEXTURE_CLAMP_ADDRESSMODE:return zf.ClampToEdge;case r.a.TEXTURE_MIRROR_ADDRESSMODE:return zf.MirrorRepeat}return zf.Repeat},a._GetSamplerWrappingDescriptor=function(a){return{addressModeU:this._GetWrappingMode(a._cachedWrapU),addressModeV:this._GetWrappingMode(a._cachedWrapV),addressModeW:this._GetWrappingMode(a._cachedWrapR)}},a._GetSamplerDescriptor=function(a){var b,c=a.useMipMaps&&null!==(b=a._cachedAnisotropicFilteringLevel)&&void 0!==b?b:1,d=this._GetSamplerFilterDescriptor(a,c);return Object(l.a)(Object(l.a)(Object(l.a)({},d),this._GetSamplerWrappingDescriptor(a)),{compare:a._comparisonFunction?lg.GetCompareFunction(a._comparisonFunction):void 0,maxAnisotropy:d.anisotropyEnabled?c:1})},a.prototype.getSampler=function(b,c,d){if(void 0===c&&(c=!1),void 0===d&&(d=0),this.disabled)return this._device.createSampler(a._GetSamplerDescriptor(b));c?d=0:0===d&&(d=a.GetSamplerHashCode(b));var e=c?void 0:this._samplers[d];return e||(e=this._device.createSampler(a._GetSamplerDescriptor(b)),c||(this._samplers[d]=e)),e},a}();!function(a){a[a.StencilReadMask=0]="StencilReadMask",a[a.StencilWriteMask=1]="StencilWriteMask",a[a.DepthBias=2]="DepthBias",a[a.DepthBiasSlopeScale=3]="DepthBiasSlopeScale",a[a.MRTAttachments1=4]="MRTAttachments1",a[a.MRTAttachments2=5]="MRTAttachments2",a[a.DepthStencilState=6]="DepthStencilState",a[a.RasterizationState=7]="RasterizationState",a[a.ColorStates=8]="ColorStates",a[a.ShaderStage=9]="ShaderStage",a[a.TextureStage=10]="TextureStage",a[a.VertexState=11]="VertexState",a[a.NumStates=12]="NumStates"}(jg||(jg={}));var tg={"":0,r8unorm:1,r8snorm:2,r8uint:3,r8sint:4,r16uint:5,r16sint:6,r16float:7,rg8unorm:8,rg8snorm:9,rg8uint:10,rg8sint:11,r32uint:12,r32sint:13,r32float:14,rg16uint:15,rg16sint:16,rg16float:17,rgba8unorm:18,"rgba8unorm-srgb":19,rgba8snorm:20,rgba8uint:21,rgba8sint:22,bgra8unorm:23,"bgra8unorm-srgb":24,rgb9e5ufloat:25,rgb10a2unorm:26,rg11b10ufloat:27,rg32uint:28,rg32sint:29,rg32float:30,rgba16uint:31,rgba16sint:32,rgba16float:33,rgba32uint:34,rgba32sint:35,rgba32float:36,stencil8:37,depth16unorm:38,depth24plus:39,"depth24plus-stencil8":40,depth32float:41,"bc1-rgba-unorm":42,"bc1-rgba-unorm-srgb":43,"bc2-rgba-unorm":44,"bc2-rgba-unorm-srgb":45,"bc3-rgba-unorm":46,"bc3-rgba-unorm-srgb":47,"bc4-r-unorm":48,"bc4-r-snorm":49,"bc5-rg-unorm":50,"bc5-rg-snorm":51,"bc6h-rgb-ufloat":52,"bc6h-rgb-float":53,"bc7-rgba-unorm":54,"bc7-rgba-unorm-srgb":55,"depth24unorm-stencil8":56,"depth32float-stencil8":57},ug={0:1,1:2,768:3,769:4,770:5,771:6,772:7,773:8,774:9,775:10,776:11,32769:12,32770:13,32771:12,32772:13},vg={0:0,7680:1,7681:2,7682:3,7683:4,5386:5,34055:6,34056:7},wg=function(){function a(a,b,c){this._device=a,this._useTextureStage=c,this._states=new Array(30),this._statesLength=0,this._stateDirtyLowestIndex=0,this._emptyVertexBuffer=b,this._mrtFormats=[],this._parameter={token:void 0,pipeline:null},this.disabled=!1,this.vertexBuffers=[],this._kMaxVertexBufferStride=2048,this.reset()}return a.prototype.reset=function(){this._isDirty=!0,this.vertexBuffers.length=0,this.setAlphaToCoverage(!1),this.resetDepthCullingState(),this.setClampDepth(!1),this.setDepthBias(0),this._webgpuColorFormat=[Y.BGRA8Unorm],this.setColorFormat(Y.BGRA8Unorm),this.setMRTAttachments([],[]),this.setAlphaBlendEnabled(!1),this.setAlphaBlendFactors([null,null,null,null],[null,null]),this.setWriteMask(15),this.setDepthStencilFormat(Y.Depth24PlusStencil8),this.setStencilEnabled(!1),this.resetStencilState(),this.setBuffers(null,null,null),this._setTextureState(0)},Object.defineProperty(a.prototype,"colorFormats",{get:function(){return this._mrtAttachments1>0?this._mrtFormats:this._webgpuColorFormat},enumerable:!1,configurable:!0}),a.prototype.getRenderPipeline=function(b,c,d,e){if(void 0===e&&(e=0),this.disabled){var f=a._GetTopology(b);return this._setVertexState(c),this._parameter.pipeline=this._createRenderPipeline(c,f,d),a.NumCacheMiss++,a._NumPipelineCreationCurrentFrame++,this._parameter.pipeline}if(this._setShaderStage(c.uniqueId),this._setRasterizationState(b,d),this._setColorStates(),this._setDepthStencilState(),this._setVertexState(c),this._setTextureState(e),this.lastStateDirtyLowestIndex=this._stateDirtyLowestIndex,!this._isDirty&&this._parameter.pipeline)return this._stateDirtyLowestIndex=this._statesLength,a.NumCacheHitWithoutHash++,this._parameter.pipeline;if(this._getRenderPipeline(this._parameter),this._isDirty=!1,this._stateDirtyLowestIndex=this._statesLength,this._parameter.pipeline)return a.NumCacheHitWithHash++,this._parameter.pipeline;f=a._GetTopology(b);return this._parameter.pipeline=this._createRenderPipeline(c,f,d),this._setRenderPipeline(this._parameter),a.NumCacheMiss++,a._NumPipelineCreationCurrentFrame++,this._parameter.pipeline},a.prototype.endFrame=function(){a.NumPipelineCreationLastFrame=a._NumPipelineCreationCurrentFrame,a._NumPipelineCreationCurrentFrame=0},a.prototype.setAlphaToCoverage=function(a){this._alphaToCoverageEnabled=a},a.prototype.setFrontFace=function(a){this._frontFace=a},a.prototype.setCullEnabled=function(a){this._cullEnabled=a},a.prototype.setCullFace=function(a){this._cullFace=a},a.prototype.setClampDepth=function(a){this._clampDepth=a},a.prototype.resetDepthCullingState=function(){this.setDepthCullingState(!1,2,1,0,0,!0,!0,r.a.ALWAYS)},a.prototype.setDepthCullingState=function(a,b,c,d,e,f,g,h){this._depthWriteEnabled=g,this._depthTestEnabled=f,this._depthCompare=(null!=h?h:r.a.ALWAYS)-512,this._cullFace=c,this._cullEnabled=a,this._frontFace=b,this.setDepthBiasSlopeScale(d),this.setDepthBias(e)},a.prototype.setDepthBias=function(a){this._depthBias!==a&&(this._depthBias=a,this._states[jg.DepthBias]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthBias))},a.prototype.setDepthBiasSlopeScale=function(a){this._depthBiasSlopeScale!==a&&(this._depthBiasSlopeScale=a,this._states[jg.DepthBiasSlopeScale]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthBiasSlopeScale))},a.prototype.setColorFormat=function(a){this._webgpuColorFormat[0]=a,this._colorFormat=tg[a]},a.prototype.setMRTAttachments=function(a,b){if(a.length>10)throw"Can"t handle more than 10 attachments for a MRT in cache render pipeline!";this.mrtAttachments=a,this.mrtTextureArray=b;for(var c=[0,0],d=0,e=0,f=0,g=0;g=32&&(e=0,d++)}}this._mrtFormats.length=f,this._mrtAttachments1===c[0]&&this._mrtAttachments2===c[1]||(this._mrtAttachments1=c[0],this._mrtAttachments2=c[1],this._states[jg.MRTAttachments1]=c[0],this._states[jg.MRTAttachments2]=c[1],this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.MRTAttachments1))},a.prototype.setAlphaBlendEnabled=function(a){this._alphaBlendEnabled=a},a.prototype.setAlphaBlendFactors=function(a,b){this._alphaBlendFuncParams=a,this._alphaBlendEqParams=b},a.prototype.setWriteMask=function(a){this._writeMask=a},a.prototype.setDepthStencilFormat=function(a){this._webgpuDepthStencilFormat=a,this._depthStencilFormat=void 0===a?0:tg[a]},a.prototype.setDepthTestEnabled=function(a){this._depthTestEnabled=a},a.prototype.setDepthWriteEnabled=function(a){this._depthWriteEnabled=a},a.prototype.setDepthCompare=function(a){this._depthCompare=(null!=a?a:r.a.ALWAYS)-512},a.prototype.setStencilEnabled=function(a){this._stencilEnabled=a},a.prototype.setStencilCompare=function(a){this._stencilFrontCompare=(null!=a?a:r.a.ALWAYS)-512},a.prototype.setStencilDepthFailOp=function(a){this._stencilFrontDepthFailOp=null===a?1:vg[a]},a.prototype.setStencilPassOp=function(a){this._stencilFrontPassOp=null===a?2:vg[a]},a.prototype.setStencilFailOp=function(a){this._stencilFrontFailOp=null===a?1:vg[a]},a.prototype.setStencilReadMask=function(a){this._stencilReadMask!==a&&(this._stencilReadMask=a,this._states[jg.StencilReadMask]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.StencilReadMask))},a.prototype.setStencilWriteMask=function(a){this._stencilWriteMask!==a&&(this._stencilWriteMask=a,this._states[jg.StencilWriteMask]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.StencilWriteMask))},a.prototype.resetStencilState=function(){this.setStencilState(!1,r.a.ALWAYS,r.a.KEEP,r.a.REPLACE,r.a.KEEP,255,255)},a.prototype.setStencilState=function(a,b,c,d,e,f,g){this._stencilEnabled=a,this._stencilFrontCompare=(null!=b?b:r.a.ALWAYS)-512,this._stencilFrontDepthFailOp=null===c?1:vg[c],this._stencilFrontPassOp=null===d?2:vg[d],this._stencilFrontFailOp=null===e?1:vg[e],this.setStencilReadMask(f),this.setStencilWriteMask(g)},a.prototype.setBuffers=function(a,b,c){this._vertexBuffers=a,this._overrideVertexBuffers=c,this._indexBuffer=b},a._GetTopology=function(a){switch(a){case r.a.MATERIAL_TriangleFillMode:return If.TriangleList;case r.a.MATERIAL_PointFillMode:return If.PointList;case r.a.MATERIAL_WireFrameFillMode:return If.LineList;case r.a.MATERIAL_PointListDrawMode:return If.PointList;case r.a.MATERIAL_LineListDrawMode:return If.LineList;case r.a.MATERIAL_LineLoopDrawMode:throw"LineLoop is an unsupported fillmode in WebGPU";case r.a.MATERIAL_LineStripDrawMode:return If.LineStrip;case r.a.MATERIAL_TriangleStripDrawMode:return If.TriangleStrip;case r.a.MATERIAL_TriangleFanDrawMode:throw"TriangleFan is an unsupported fillmode in WebGPU";default:return If.TriangleList}},a._GetAphaBlendOperation=function(a){switch(a){case 32774:return Nf.Add;case 32778:return Nf.Subtract;case 32779:return Nf.ReverseSubtract;case 32775:return Nf.Min;case 32776:return Nf.Max;default:return Nf.Add}},a._GetAphaBlendFactor=function(a){switch(a){case 0:return Mf.Zero;case 1:return Mf.One;case 768:return Mf.Src;case 769:return Mf.OneMinusSrc;case 770:return Mf.SrcAlpha;case 771:return Mf.OneMinusSrcAlpha;case 772:return Mf.DstAlpha;case 773:return Mf.OneMinusDstAlpha;case 774:return Mf.Dst;case 775:return Mf.OneMinusDst;case 776:return Mf.SrcAlphaSaturated;case 32769:return Mf.Constant;case 32770:return Mf.OneMinusConstant;case 32771:return Mf.Constant;case 32772:return Mf.OneMinusConstant;default:return Mf.One}},a._GetCompareFunction=function(a){switch(a){case 0:return Bf.Never;case 1:return Bf.Less;case 2:return Bf.Equal;case 3:return Bf.LessEqual;case 4:return Bf.Greater;case 5:return Bf.NotEqual;case 6:return Bf.GreaterEqual;case 7:return Bf.Always}return Bf.Never},a._GetStencilOpFunction=function(a){switch(a){case 0:return Of.Zero;case 1:return Of.Keep;case 2:return Of.Replace;case 3:return Of.IncrementClamp;case 4:return Of.DecrementClamp;case 5:return Of.Invert;case 6:return Of.IncrementWrap;case 7:return Of.DecrementWrap}return Of.Keep},a._GetVertexInputDescriptorFormat=function(a){var b=a.type,c=a.normalized,d=a.getSize();switch(b){case W.b.BYTE:switch(d){case 1:case 2:return c?Qf.Snorm8x2:Qf.Sint8x2;case 3:case 4:return c?Qf.Snorm8x4:Qf.Sint8x4}break;case W.b.UNSIGNED_BYTE:switch(d){case 1:case 2:return c?Qf.Unorm8x2:Qf.Uint8x2;case 3:case 4:return c?Qf.Unorm8x4:Qf.Uint8x4}break;case W.b.SHORT:switch(d){case 1:case 2:return c?Qf.Snorm16x2:Qf.Sint16x2;case 3:case 4:return c?Qf.Snorm16x4:Qf.Sint16x4}break;case W.b.UNSIGNED_SHORT:switch(d){case 1:case 2:return c?Qf.Unorm16x2:Qf.Uint16x2;case 3:case 4:return c?Qf.Unorm16x4:Qf.Uint16x4}break;case W.b.INT:switch(d){case 1:return Qf.Sint32;case 2:return Qf.Sint32x2;case 3:return Qf.Sint32x3;case 4:return Qf.Sint32x4}break;case W.b.UNSIGNED_INT:switch(d){case 1:return Qf.Uint32;case 2:return Qf.Uint32x2;case 3:return Qf.Uint32x3;case 4:return Qf.Uint32x4}break;case W.b.FLOAT:switch(d){case 1:return Qf.Float32;case 2:return Qf.Float32x2;case 3:return Qf.Float32x3;case 4:return Qf.Float32x4}}throw new Error("Invalid Format ""+a.getKind()+"" - type="+b+", normalized="+c+", size="+d)},a.prototype._getAphaBlendState=function(){return this._alphaBlendEnabled?{srcFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[2]),dstFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[3]),operation:a._GetAphaBlendOperation(this._alphaBlendEqParams[1])}:null},a.prototype._getColorBlendState=function(){return this._alphaBlendEnabled?{srcFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[0]),dstFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[1]),operation:a._GetAphaBlendOperation(this._alphaBlendEqParams[0])}:null},a.prototype._setShaderStage=function(a){this._shaderId!==a&&(this._shaderId=a,this._states[jg.ShaderStage]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.ShaderStage))},a.prototype._setRasterizationState=function(a,b){a=this._frontFace-1+((this._cullEnabled?this._cullFace:0)<<1)+((this._clampDepth?1:0)<<3)+((this._alphaToCoverageEnabled?1:0)<<4)+(a<<5)+(b<<8);this._rasterizationState!==a&&(this._rasterizationState=a,this._states[jg.RasterizationState]=this._rasterizationState,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.RasterizationState))},a.prototype._setColorStates=function(){var a=((this._writeMask?1:0)<<22)+(this._colorFormat<<23)+((this._depthWriteEnabled?1:0)<<29);this._alphaBlendEnabled&&(a+=((null===this._alphaBlendFuncParams[0]?2:ug[this._alphaBlendFuncParams[0]])<<0)+((null===this._alphaBlendFuncParams[1]?2:ug[this._alphaBlendFuncParams[1]])<<4)+((null===this._alphaBlendFuncParams[2]?2:ug[this._alphaBlendFuncParams[2]])<<8)+((null===this._alphaBlendFuncParams[3]?2:ug[this._alphaBlendFuncParams[3]])<<12)+((null===this._alphaBlendEqParams[0]?1:this._alphaBlendEqParams[0]-32773)<<16)+((null===this._alphaBlendEqParams[1]?1:this._alphaBlendEqParams[1]-32773)<<19)),a!==this._colorStates&&(this._colorStates=a,this._states[jg.ColorStates]=this._colorStates,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.ColorStates))},a.prototype._setDepthStencilState=function(){var a=this._stencilEnabled?this._stencilFrontCompare+(this._stencilFrontDepthFailOp<<3)+(this._stencilFrontPassOp<<6)+(this._stencilFrontFailOp<<9):591;a=this._depthStencilFormat+((this._depthTestEnabled?this._depthCompare:7)<<6)+(a<<10);this._depthStencilState!==a&&(this._depthStencilState=a,this._states[jg.DepthStencilState]=this._depthStencilState,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthStencilState))},a.prototype._setVertexState=function(a){for(var b,c,d,e=this._statesLength,f=jg.VertexState,a=a._pipelineContext,g=a.shaderProcessingContext.attributeNamesFromEffect,a=a.shaderProcessingContext.attributeLocationsFromEffect,h=0,i=0;i0)for(var j=0;j=this._video.HAVE_CURRENT_DATA},a.prototype.dispose=function(){},a}(),Eg=function(){function a(){this.uniqueId=a._Counter++,this.reset()}return Object.defineProperty(a.prototype,"forceBindGroupCreation",{get:function(){return this._numExternalTextures>0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasFloatTextures",{get:function(){return this._numFloatTextures>0},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this.samplers={},this.textures={},this.buffers={},this.isDirty=!0,this._numFloatTextures=0,this._numExternalTextures=0},a.prototype.setSampler=function(a,b){var c=this.samplers[a],d=-1;c?d=c.hashCode:this.samplers[a]=c={sampler:b,hashCode:0},c.sampler=b,c.hashCode=b?sg.GetSamplerHashCode(b):0,this.isDirty||(this.isDirty=d!==c.hashCode)},a.prototype.setTexture=function(a,b){var c,d=this.textures[a],e=-1;d?e=null!==(c=null===(c=d.texture)||void 0===c?void 0:c.uniqueId)&&void 0!==c?c:-1:this.textures[a]=d={texture:b,isFloatTexture:!1,isExternalTexture:!1},d.isExternalTexture&&this._numExternalTextures--,d.isFloatTexture&&this._numFloatTextures--,b?(d.isFloatTexture=b.type===r.a.TEXTURETYPE_FLOAT,d.isExternalTexture=Dg.IsExternalTexture(b),d.isFloatTexture&&this._numFloatTextures++,d.isExternalTexture&&this._numExternalTextures++):(d.isFloatTexture=!1,d.isExternalTexture=!1),d.texture=b,this.isDirty||(this.isDirty=e!==(null!==(c=null==b?void 0:b.uniqueId)&&void 0!==c?c:-1))},a.prototype.setBuffer=function(a,b){var c;this.isDirty||(this.isDirty=(null==b?void 0:b.uniqueId)!==(null===(c=this.buffers[a])||void 0===c?void 0:c.uniqueId)),this.buffers[a]=b},a._Counter=0,a}(),Fg=function(){function a(){this.uniqueId=a._Counter++}return a._Counter=0,a}(),Gg=function(){this.values={}},Hg=function(){function a(a,b,c){this.disabled=!1,this._device=a,this._cacheSampler=b,this._engine=c}return Object.defineProperty(a,"Statistics",{get:function(){return{totalCreated:a.NumBindGroupsCreatedTotal,lastFrameCreated:a.NumBindGroupsCreatedLastFrame,lookupLastFrame:a.NumBindGroupsLookupLastFrame,noLookupLastFrame:a.NumBindGroupsNoLookupLastFrame}},enumerable:!1,configurable:!0}),a.prototype.endFrame=function(){a.NumBindGroupsCreatedLastFrame=a._NumBindGroupsCreatedCurrentFrame,a.NumBindGroupsLookupLastFrame=a._NumBindGroupsLookupCurrentFrame,a.NumBindGroupsNoLookupLastFrame=a._NumBindGroupsNoLookupCurrentFrame,a._NumBindGroupsCreatedCurrentFrame=0,a._NumBindGroupsLookupCurrentFrame=0,a._NumBindGroupsNoLookupCurrentFrame=0},a.prototype.getBindGroups=function(b,c){var d,e,f=void 0,g=a._Cache,h=this.disabled||c.forceBindGroupCreation;if(!h){if(!c.isDirty)return a._NumBindGroupsNoLookupCurrentFrame++,c.bindGroups;for(var i=0,j=b.shaderProcessingContext.bufferNames;i":b}),50);continue}i[h].resource=this._cacheSampler.getSampler(v,!1,u.hashCode)}else q.a.Error("Sampler ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50);else if(g.texture||g.storageTexture)if(u=c.textures[d]){if(this._engine.dbgSanityChecks&&null===u.texture){q.a.Error("Trying to bind a null texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(u,function(a,b){return"texture"===a?"":b}),50);continue}v=u.texture._hardwareTexture;if(this._engine.dbgSanityChecks&&(!v||!v.view)){q.a.Error("Trying to bind a null gpu texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(u,function(a,b){return"texture"===a?"":b})+", isReady="+(null===(e=u.texture)||void 0===e?void 0:e.isReady),50);continue}i[h].resource=v.view}else q.a.Error("Texture ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50);else if(g.externalTexture){if(u=c.textures[d]){if(this._engine.dbgSanityChecks&&null===u.texture){q.a.Error("Trying to bind a null external texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(u,function(a,b){return"texture"===a?"":b}),50);continue}e=u.texture.underlyingResource;if(this._engine.dbgSanityChecks&&!e){q.a.Error("Trying to bind a null gpu external texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(u,function(a,b){return"texture"===a?"":b})+", isReady="+(null===(v=u.texture)||void 0===v?void 0:v.isReady),50);continue}i[h].resource=this._device.importExternalTexture({source:e})}else q.a.Error("Texture ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50)}else if(g.buffer){u=c.buffers[d];if(u){v=u.underlyingResource;i[h].resource.buffer=v,i[h].resource.size=u.capacity}else q.a.Error("Can"t find buffer ""+d+"". entry="+JSON.stringify(g)+", buffers="+JSON.stringify(c.buffers),50)}}e=k[m];f[m]=this._device.createBindGroup({layout:e,entries:i})}return f},a.NumBindGroupsCreatedTotal=0,a.NumBindGroupsCreatedLastFrame=0,a.NumBindGroupsLookupLastFrame=0,a.NumBindGroupsNoLookupLastFrame=0,a._Cache=new Gg,a._NumBindGroupsCreatedCurrentFrame=0,a._NumBindGroupsLookupCurrentFrame=0,a._NumBindGroupsNoLookupCurrentFrame=0,a}();a="uniform float depthValue; const vec2 pos[4]={ vec2(-1.0,1.0), vec2(1.0,1.0), vec2(-1.0,-1.0), vec2(1.0,-1.0) }; void main(void) { gl_Position=vec4(pos[gl_VertexID],depthValue,1.0); } ";X.a.ShadersStore.clearQuadVertexShader=a;X.a.ShadersStore.clearQuadPixelShader="uniform vec4 color; void main() { gl_FragColor=color; } ";var Ig=function(){function a(a,b,c){this._bindGroups={},this._bundleCache={},this._device=a,this._engine=b,this._cacheRenderPipeline=new yg(this._device,c,!b._caps.textureFloatLinearFiltering),this._cacheRenderPipeline.setDepthTestEnabled(!1),this._cacheRenderPipeline.setStencilReadMask(255),this._effect=b.createEffect("clearQuad",[],["color","depthValue"])}return a.prototype.setDepthStencilFormat=function(a){this._depthTextureFormat=a,this._cacheRenderPipeline.setDepthStencilFormat(a)},a.prototype.setColorFormat=function(a){this._cacheRenderPipeline.setColorFormat(a)},a.prototype.setMRTAttachments=function(a,b){this._cacheRenderPipeline.setMRTAttachments(a,b)},a.prototype.clear=function(a,b,c,d,e){var f;void 0===e&&(e=1);var g=null,h=0;if(a)f=a;else{if(h=(b?b.r+256*b.g+256*b.b*256+256*b.a*256*256:0)+(c?Math.pow(2,32):0)+(d?Math.pow(2,33):0)+(this._engine.useReverseDepthBuffer?Math.pow(2,34):0)+e*Math.pow(2,35),g=this._bundleCache[h])return g;f=this._device.createRenderBundleEncoder({colorFormats:this._cacheRenderPipeline.colorFormats,depthStencilFormat:this._depthTextureFormat,sampleCount:e})}this._cacheRenderPipeline.setDepthWriteEnabled(!!c),this._cacheRenderPipeline.setStencilEnabled(!!d),this._cacheRenderPipeline.setStencilWriteMask(d?255:0),this._cacheRenderPipeline.setStencilCompare(d?r.a.ALWAYS:r.a.NEVER),this._cacheRenderPipeline.setStencilPassOp(d?r.a.REPLACE:r.a.KEEP),this._cacheRenderPipeline.setWriteMask(b?15:0);c=this._cacheRenderPipeline.getRenderPipeline(r.a.MATERIAL_TriangleStripDrawMode,this._effect,e);d=this._effect._pipelineContext;b&&this._effect.setDirectColor4("color",b),this._effect.setFloat("depthValue",this._engine.useReverseDepthBuffer?this._engine._clearReverseDepthValue:this._engine._clearDepthValue),null===(e=d.uniformBuffer)||void 0===e||e.update();e=null===(b=d.uniformBuffer)||void 0===b?void 0:b.getBuffer();b=this._bindGroups[e.uniqueId];if(!b){d=d.bindGroupLayouts;(b=this._bindGroups[e.uniqueId]=[]).push(this._device.createBindGroup({layout:d[0],entries:[]})),dg._SimplifiedKnownBindings||b.push(this._device.createBindGroup({layout:d[1],entries:[]})),b.push(this._device.createBindGroup({layout:d[dg._SimplifiedKnownBindings?1:2],entries:[{binding:0,resource:{buffer:e.underlyingResource,size:e.capacity}}]}))}f.setPipeline(c);for(d=0;d=0&&(b._gpuFrameTimeCounter.fetchNewFrame(),b._gpuFrameTimeCounter.addCount(a,!0)),b._measureDurationState=0}))},a}(),Rg=function(){function a(a,b){this._querySet=new Pg(2,Uf.Timestamp,a,b)}return a.prototype.start=function(a){a.writeTimestamp(this._querySet.querySet,0)},a.prototype.stop=function(a){return Object(l.b)(this,void 0,void 0,function(){return Object(l.e)(this,function(b){return a.writeTimestamp(this._querySet.querySet,1),[2,this._querySet.readTwoValuesAndSubtract(0)]})})},a.prototype.dispose=function(){this._querySet.dispose()},a}(),Sg=function(){function a(a,b,c,d,e){void 0===d&&(d=50),void 0===e&&(e=100),this._availableIndices=[],this._engine=a,this._device=b,this._bufferManager=c,this._frameLastBuffer=-1,this._currentTotalIndices=0,this._countIncrement=e,this._allocateNewIndices(d)}return Object.defineProperty(a.prototype,"querySet",{get:function(){return this._querySet.querySet},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasQueries",{get:function(){return this._currentTotalIndices!==this._availableIndices.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"canBeginQuery",{get:function(){switch(this._engine._getCurrentRenderPassIndex()){case 0:return void 0!==this._engine._mainRenderPassWrapper.renderPassDescriptor.occlusionQuerySet;case 1:return void 0!==this._engine._rttRenderPassWrapper.renderPassDescriptor.occlusionQuerySet}return!1},enumerable:!1,configurable:!0}),a.prototype.createQuery=function(){0===this._availableIndices.length&&this._allocateNewIndices();var a=this._availableIndices[this._availableIndices.length-1];return this._availableIndices.length--,a},a.prototype.deleteQuery=function(a){this._availableIndices[this._availableIndices.length-1]=a},a.prototype.isQueryResultAvailable=function(a){return this._retrieveQueryBuffer(),!!this._lastBuffer&&a=0||void 0,pvrtc:null,etc1:null,etc2:null,bptc:this._deviceEnabledExtensions.indexOf(sf.TextureCompressionBC)>=0||void 0,maxAnisotropy:16,uintIndices:!0,fragmentDepthSupported:!0,highPrecisionShaderSupported:!0,colorBufferFloat:!0,textureFloat:!0,textureFloatLinearFiltering:!1,textureFloatRender:!0,textureHalfFloat:!0,textureHalfFloatLinearFiltering:!0,textureHalfFloatRender:!0,textureLOD:!0,drawBuffersExtension:!0,depthTextureExtension:!0,vertexArrayObject:!1,instancedArrays:!0,timerQuery:void 0,supportOcclusionQuery:"undefined"!=typeof BigUint64Array,canUseTimestampForTimerQuery:!0,multiview:!1,oculusMultiview:!1,parallelShaderCompile:void 0,blendMinMax:!0,maxMSAASamples:4,canUseGLInstanceID:!0,canUseGLVertexID:!0,supportComputeShaders:!0,supportSRGBBuffers:!0},this._caps.parallelShaderCompile=null,this._features={forceBitmapOverHTMLImageElement:!0,supportRenderAndCopyToLodForFloatTextures:!0,supportDepthStencilTexture:!0,supportShadowSamplers:!0,uniformBufferHardCheckMatrix:!1,allowTexturePrefiltering:!0,trackUbosInFrame:!0,checkUbosContentBeforeUpload:!0,supportCSM:!0,basisNeedsPOT:!1,support3DTextures:!0,needTypeSuffixInShaderConstants:!0,supportMSAA:!0,supportSSAO2:!0,supportExtendedTextureFormats:!0,supportSwitchCaseInShader:!0,supportSyncTextureRead:!1,needsInvertingBitmap:!1,useUBOBindingCache:!1,needShaderCodeInlining:!0,_collectUbosUpdatedInFrame:!1}},b.prototype._initializeContextAndSwapChain=function(){this._context=this._canvas.getContext("webgpu"),this._configureContext(this._canvas.width,this._canvas.height),this._colorFormat=this._options.swapChainFormat,this._mainRenderPassWrapper.colorAttachmentGPUTextures=[new ig],this._mainRenderPassWrapper.colorAttachmentGPUTextures[0].format=this._colorFormat,this._invertYFinalFramebuffer&&(this._mainRenderPassCopyWrapper.colorAttachmentGPUTextures=[new ig],this._mainRenderPassCopyWrapper.colorAttachmentGPUTextures[0].format=this._colorFormat)},b.prototype._initializeMainAttachments=function(){var a,b;if(this._mainTextureExtends={width:this.getRenderWidth(),height:this.getRenderHeight(),depthOrArrayLayers:1},this._options.antialiasing){var c={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:this._mainPassSampleCount,dimension:vf.E2d,format:this._options.swapChainFormat,usage:wf.RenderAttachment};null===(a=this._mainTexture)||void 0===a||a.destroy(),this._mainTexture=this._device.createTexture(c),a=[{view:this._mainTexture.createView(),loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}]}else a=[{view:void 0,loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}];if(this._invertYFinalFramebuffer){c={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:1,dimension:vf.E2d,format:this._options.swapChainFormat,usage:wf.RenderAttachment|wf.TextureBinding};null===(b=this._mainTextureLastCopy)||void 0===b||b.destroy(),this._mainTextureLastCopy=this._device.createTexture(c),this._options.antialiasing?a[0].resolveTarget=this._mainTextureLastCopy.createView():a[0].view=this._mainTextureLastCopy.createView(),this._mainRenderPassCopyWrapper.renderPassDescriptor={colorAttachments:[{view:void 0,loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}]}}this._mainRenderPassWrapper.depthTextureFormat=this.isStencilEnable?Y.Depth24PlusStencil8:Y.Depth32Float,this._setDepthTextureFormat(this._mainRenderPassWrapper);b={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:this._mainPassSampleCount,dimension:vf.E2d,format:this._mainRenderPassWrapper.depthTextureFormat,usage:wf.RenderAttachment};this._depthTexture&&this._depthTexture.destroy(),this._depthTexture=this._device.createTexture(b);c={view:this._depthTexture.createView(),depthLoadValue:this._clearDepthValue,depthStoreOp:Tf.Store,stencilLoadValue:this._clearStencilValue,stencilStoreOp:Tf.Store};this._mainRenderPassWrapper.renderPassDescriptor={colorAttachments:a,depthStencilAttachment:c},null!==this._mainRenderPassWrapper.renderPass&&this._endMainRenderPass()},b.prototype._configureContext=function(a,b){this._context.configure({device:this._device,format:this._options.swapChainFormat,usage:wf.RenderAttachment|wf.CopySrc,compositingAlphaMode:this.premultipliedAlpha?Wf.Premultiplied:Wf.Opaque,size:{width:a,height:b,depthOrArrayLayers:1}})},b.prototype.setSize=function(b,c,d){return void 0===d&&(d=!1),!!a.prototype.setSize.call(this,b,c,d)&&(this.dbgVerboseLogsForFirstFrames&&(void 0===this._count&&(this._count=0),(!this._count||this._count65535?c=new Uint32Array(a):(c=new Uint16Array(a),b=!1);a=this._bufferManager.createBuffer(c,tf.Index|tf.CopyDst);return a.is32Bits=b,a},b.prototype._createBuffer=function(a,b){a=a instanceof Array?new Float32Array(a):a instanceof ArrayBuffer?new Uint8Array(a):a;var c=0;return b&r.a.BUFFER_CREATIONFLAG_READ&&(c|=tf.CopySrc),b&r.a.BUFFER_CREATIONFLAG_WRITE&&(c|=tf.CopyDst),b&r.a.BUFFER_CREATIONFLAG_UNIFORM&&(c|=tf.Uniform),b&r.a.BUFFER_CREATIONFLAG_VERTEX&&(c|=tf.Vertex),b&r.a.BUFFER_CREATIONFLAG_INDEX&&(c|=tf.Index),b&r.a.BUFFER_CREATIONFLAG_STORAGE&&(c|=tf.Storage),this._bufferManager.createBuffer(a,c)},b.prototype.bindBuffersDirectly=function(a,b,c,d,e){throw"Not implemented on WebGPU"},b.prototype.updateAndBindInstancesBuffer=function(a,b,c){throw"Not implemented on WebGPU"},b.prototype.bindBuffers=function(a,b,c,d){this._currentIndexBuffer=b,this._currentOverrideVertexBuffers=null!=d?d:null,this._cacheRenderPipeline.setBuffers(a,b,this._currentOverrideVertexBuffers)},b.prototype._releaseBuffer=function(a){return this._bufferManager.releaseBuffer(a)},b.prototype.createEffect=function(a,b,c,d,e,f,g,h,i,j){var k;void 0===j&&(j=Xd.a.GLSL);var o=a.vertexElement||a.vertex||a.vertexToken||a.vertexSource||a,p=a.fragmentElement||a.fragment||a.fragmentToken||a.fragmentSource||a,q=this._getGlobalDefines();k=null!==(k=null!=e?e:b.defines)&&void 0!==k?k:"";q&&(k+=" "+q);q=o+"+"+p+"@"+k;if(this._compiledEffects[q]){o=this._compiledEffects[q];return g&&o.isReady()&&g(o),o}p=new $f.a(a,b,c,d,this,e,f,g,h,i,q,j);return this._compiledEffects[q]=p,p},b.prototype._compileRawShaderToSpirV=function(a,b){return this._glslang.compileGLSL(a,b)},b.prototype._compileShaderToSpirV=function(a,b,c,d){return this._compileRawShaderToSpirV(d+(c?c+" ":"")+a,b)},b.prototype._getWGSLShader=function(a,b,c,d){return(c?"//"+c.split(" ").join(" //")+" ":"")+a},b.prototype._createPipelineStageDescriptor=function(a,b,c){return this._tintWASM&&c===Xd.a.GLSL&&(a=this._tintWASM.convertSpirV2WGSL(a),b=this._tintWASM.convertSpirV2WGSL(b)),{vertexStage:{module:this._device.createShaderModule({code:a}),entryPoint:"main"},fragmentStage:{module:this._device.createShaderModule({code:b}),entryPoint:"main"}}},b.prototype._compileRawPipelineStageDescriptor=function(a,b,c){a=c===Xd.a.GLSL?this._compileRawShaderToSpirV(a,"vertex"):a;b=c===Xd.a.GLSL?this._compileRawShaderToSpirV(b,"fragment"):b;return this._createPipelineStageDescriptor(a,b,c)},b.prototype._compilePipelineStageDescriptor=function(a,b,c,d){this.onBeforeShaderCompilationObservable.notifyObservers(this);a=d===Xd.a.GLSL?this._compileShaderToSpirV(a,"vertex",c,"#version 450 "):this._getWGSLShader(a,"vertex",c,"#version 450 ");b=d===Xd.a.GLSL?this._compileShaderToSpirV(b,"fragment",c,"#version 450 "):this._getWGSLShader(b,"fragment",c,"#version 450 ");c=this._createPipelineStageDescriptor(a,b,d);return this.onAfterShaderCompilationObservable.notifyObservers(this),c},b.prototype.createRawShaderProgram=function(a,b,c,d,e){throw"Not available on WebGPU"},b.prototype.createShaderProgram=function(a,b,c,d,e,f){throw"Not available on WebGPU"},b.prototype.inlineShaderCode=function(a){a=new kf(a);return a.debug=!1,a.processCode(),a.code},b.prototype.createPipelineContext=function(a){return new bg(a,this)},b.prototype.createMaterialContext=function(){return new Eg},b.prototype.createDrawContext=function(){return new Fg},b.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i,j){g=a;i=g.shaderProcessingContext.shaderLanguage;this.dbgShowShaderCode&&(!1,!1,!1),g.sources={fragment:c,vertex:b,rawVertex:e,rawFragment:f},g.stages=d?this._compileRawPipelineStageDescriptor(b,c,i):this._compilePipelineStageDescriptor(b,c,h,i)},b.prototype.getAttributes=function(a,b){for(var c=new Array(b.length),a=a,d=0;d0;for(var e in a){var f=a[e],g=c[e],h=g.group;g=g.binding;var i=f.type,j=f.object,k=f.indexInGroupEntries;switch((o=this._bindGroupEntries[h])||(o=this._bindGroupEntries[h]=[]),i){case Sd.Sampler:h=j;void 0!==k&&d?o[k].resource=this._cacheSampler.getSampler(h):(f.indexInGroupEntries=o.length,o.push({binding:g,resource:this._cacheSampler.getSampler(h)}));break;case Sd.Texture:case Sd.TextureWithoutSampler:var m=(h=j)._texture._hardwareTexture;void 0!==k&&d?(i===Sd.Texture&&(o[k++].resource=this._cacheSampler.getSampler(h._texture)),o[k].resource=m.view):(f.indexInGroupEntries=o.length,i===Sd.Texture&&o.push({binding:g-1,resource:this._cacheSampler.getSampler(h._texture)}),o.push({binding:g,resource:m.view}));break;case Sd.StorageTexture:0==((m=(h=j)._texture._hardwareTexture).textureAdditionalUsages&wf.StorageBinding)&&q.a.Error("computeDispatch: The texture (name="+h.name+", uniqueId="+h.uniqueId+") is not a storage texture!",50),void 0!==k&&d?o[k].resource=m.view:(f.indexInGroupEntries=o.length,o.push({binding:g,resource:m.view}));break;case Sd.UniformBuffer:case Sd.StorageBuffer:i=(Sd.UniformBuffer,j).getBuffer();h=i.underlyingResource;void 0!==k&&d?(o[k].resource.buffer=h,o[k].resource.size=i.capacity):(f.indexInGroupEntries=o.length,o.push({binding:g,resource:{buffer:h,offset:0,size:i.capacity}}))}}for(m=0;m1&&(b=4);for(var d=0;d>n,B=[],C=0;C<6;C++){var D=k[n][m[C]];j&&(D=Yg(D,A,A,e)),B.push(new Uint8Array(D.buffer,D.byteOffset,D.byteLength))}y._textureHelper.updateCubeTextures(B,l.underlyingResource,A,A,l.format,x,!1,0,0,y._uploadEncoder)}else{for(B=[],C=0;C<6;C++)B.push(a[f[C]]);y.updateRawCubeTexture(z,B,d,e,x)}z.isReady=!0,null==b||b._removePendingData(z),i&&i()}}(a)},void 0,null==b?void 0:b.offlineProvider,!0,function(a,c){null==b||b._removePendingData(z),j&&a&&j(a.status+" "+a.statusText,c)}),z},Ug.prototype.createRawTexture3D=function(a,b,c,d,e,f,g,h,i,j,k){void 0===i&&(i=null),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=0);var m=ob.b.Raw3D;m=new ob.a(this,m);return m.baseWidth=b,m.baseHeight=c,m.baseDepth=d,m.width=b,m.height=c,m.depth=d,m.format=e,m.type=j,m.generateMipMaps=f,m.samplingMode=h,m.is3D=!0,this._doNotHandleContextLost||(m._bufferView=a),this._textureHelper.createGPUTextureForInternalTexture(m,b,c,void 0,k),this.updateRawTexture3D(m,a,e,g,i,j),this._internalTexturesCache.push(m),m},Ug.prototype.updateRawTexture3D=function(a,b,c,d,e,f){if(void 0===e&&(e=null),void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),this._doNotHandleContextLost||(a._bufferView=b,a.format=c,a.invertY=d,a._compression=e),b){e=a._hardwareTexture;c===r.a.TEXTUREFORMAT_RGB&&(b=Yg(b,a.width,a.height,f));c=new Uint8Array(b.buffer,b.byteOffset,b.byteLength);this._textureHelper.updateTexture(c,a,a.width,a.height,a.depth,e.format,0,0,d,!1,0,0,this._uploadEncoder),a.generateMipMaps&&this._generateMipmaps(a,this._uploadEncoder)}a.isReady=!0},Ug.prototype.createRawTexture2DArray=function(a,b,c,d,e,f,g,h,i,j,k){void 0===i&&(i=null),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=0);var m=ob.b.Raw2DArray;m=new ob.a(this,m);return m.baseWidth=b,m.baseHeight=c,m.baseDepth=d,m.width=b,m.height=c,m.depth=d,m.format=e,m.type=j,m.generateMipMaps=f,m.samplingMode=h,m.is2DArray=!0,this._doNotHandleContextLost||(m._bufferView=a),this._textureHelper.createGPUTextureForInternalTexture(m,b,c,d,k),this.updateRawTexture2DArray(m,a,e,g,i,j),this._internalTexturesCache.push(m),m},Ug.prototype.updateRawTexture2DArray=function(a,b,c,d,e,f){if(void 0===e&&(e=null),void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),this._doNotHandleContextLost||(a._bufferView=b,a.format=c,a.invertY=d,a._compression=e),b){e=a._hardwareTexture;c===r.a.TEXTUREFORMAT_RGB&&(b=Yg(b,a.width,a.height,f));c=new Uint8Array(b.buffer,b.byteOffset,b.byteLength);this._textureHelper.updateTexture(c,a,a.width,a.height,a.depth,e.format,0,0,d,!1,0,0,this._uploadEncoder),a.generateMipMaps&&this._generateMipmaps(a,this._uploadEncoder)}a.isReady=!0},Ug.prototype._readTexturePixels=function(a,b,c,d,e,f,g,h){void 0===d&&(d=-1),void 0===e&&(e=0),void 0===f&&(f=null),void 0===g&&(g=!0),void 0===h&&(h=!1);a=a._hardwareTexture;return g&&this.flushFramebuffer(),this._textureHelper.readPixels(a.underlyingResource,0,0,b,c,a.format,d,e,f,h)},Ug.prototype._readTexturePixelsSync=function(a,b,c,d,e,f,g,h){throw"_readTexturePixelsSync is unsupported in WebGPU!"},Ug.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new Cc.a(a,b,c,this);return this._renderTargetWrapperCache.push(a),a},Ug.prototype.createRenderTargetTexture=function(a,b){var c,d=this._createHardwareRenderTargetWrapper(!1,!1,a),e=new Bc.b;void 0!==b&&"object"==typeof b?(e.generateMipMaps=b.generateMipMaps,e.generateDepthBuffer=void 0===b.generateDepthBuffer||b.generateDepthBuffer,e.generateStencilBuffer=e.generateDepthBuffer&&b.generateStencilBuffer,e.type=void 0===b.type?r.a.TEXTURETYPE_UNSIGNED_INT:b.type,e.samplingMode=void 0===b.samplingMode?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:b.samplingMode,e.format=void 0===b.format?r.a.TEXTUREFORMAT_RGBA:b.format,e.samples=null!==(c=b.samples)&&void 0!==c?c:1,e.creationFlags=null!==(c=b.creationFlags)&&void 0!==c?c:0):(e.generateMipMaps=b,e.generateDepthBuffer=!0,e.generateStencilBuffer=!1,e.type=r.a.TEXTURETYPE_UNSIGNED_INT,e.samplingMode=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,e.format=r.a.TEXTUREFORMAT_RGBA,e.samples=1,e.creationFlags=0),(e.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(e.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(e.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);c=new ob.a(this,ob.b.RenderTarget);var f=a.width||a,g=a.height||a;a=a.layers||0;return d._generateDepthBuffer=e.generateDepthBuffer,d._generateStencilBuffer=!!e.generateStencilBuffer,c.baseWidth=f,c.baseHeight=g,c.width=f,c.height=g,c.depth=a,c.isReady=!0,c.samples=e.samples,c.generateMipMaps=!!e.generateMipMaps,c.samplingMode=e.samplingMode,c.type=e.type,c.format=e.format,c.is2DArray=a>0,c._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,c._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE,this._internalTexturesCache.push(c),d.setTextures(c),(d._generateDepthBuffer||d._generateStencilBuffer)&&d.createDepthStencilTexture(0,void 0===e.samplingMode||e.samplingMode===r.a.TEXTURE_BILINEAR_SAMPLINGMODE||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR||e.samplingMode===r.a.TEXTURE_TRILINEAR_SAMPLINGMODE||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,d._generateStencilBuffer,d.samples),void 0!==b&&"object"==typeof b&&b.createMipMaps&&!e.generateMipMaps&&(c.generateMipMaps=!0),this._textureHelper.createGPUTextureForInternalTexture(c,void 0,void 0,void 0,e.creationFlags),void 0!==b&&"object"==typeof b&&b.createMipMaps&&!e.generateMipMaps&&(c.generateMipMaps=!1),d},Ug.prototype._createDepthStencilTexture=function(a,b,c){c=new ob.a(this,ob.b.DepthStencil);b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1,samples:1,depthTextureFormat:r.a.TEXTUREFORMAT_DEPTH16},b);return c.format=b.generateStencil?r.a.TEXTUREFORMAT_DEPTH24_STENCIL8:b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH16?r.a.TEXTUREFORMAT_DEPTH32_FLOAT:b.depthTextureFormat,this._setupDepthStencilTexture(c,a,b.generateStencil,b.bilinearFiltering,b.comparisonFunction,b.samples),this._textureHelper.createGPUTextureForInternalTexture(c),this._internalTexturesCache.push(c),c},Ug.prototype._setupDepthStencilTexture=function(a,b,c,d,e,f){void 0===f&&(f=1);c=b.width||b;var g=b.height||b;b=b.layers||0;a.baseWidth=c,a.baseHeight=g,a.width=c,a.height=g,a.is2DArray=b>0,a.depth=b,a.isReady=!0,a.samples=f,a.generateMipMaps=!1,a.samplingMode=d?r.a.TEXTURE_BILINEAR_SAMPLINGMODE:r.a.TEXTURE_NEAREST_SAMPLINGMODE,a.type=r.a.TEXTURETYPE_FLOAT,a._comparisonFunction=e,a._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,a._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE},Ug.prototype.updateRenderTargetTextureSampleCount=function(a,b){return a&&a.texture&&a.samples!==b?((b=Math.min(b,this.getCaps().maxMSAASamples))>1&&(b=4),this._textureHelper.createMSAATexture(a.texture,b),a._depthStencilTexture&&(this._textureHelper.createMSAATexture(a._depthStencilTexture,b),a._depthStencilTexture.samples=b),a.texture.samples=b,b):b},Ug.prototype.createRenderTargetCubeTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!0,a),d=Object(l.a)({generateMipMaps:!0,generateDepthBuffer:!0,generateStencilBuffer:!1,type:r.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,format:r.a.TEXTUREFORMAT_RGBA,samples:1},b);d.generateStencilBuffer=d.generateDepthBuffer&&d.generateStencilBuffer,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=d.generateStencilBuffer;var e=new ob.a(this,ob.b.RenderTarget);return e.width=a,e.height=a,e.depth=0,e.isReady=!0,e.isCube=!0,e.samples=d.samples,e.generateMipMaps=d.generateMipMaps,e.samplingMode=d.samplingMode,e.type=d.type,e.format=d.format,this._internalTexturesCache.push(e),c.setTextures(e),(c._generateDepthBuffer||c._generateStencilBuffer)&&c.createDepthStencilTexture(0,void 0===d.samplingMode||d.samplingMode===r.a.TEXTURE_BILINEAR_SAMPLINGMODE||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR||d.samplingMode===r.a.TEXTURE_TRILINEAR_SAMPLINGMODE||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,c._generateStencilBuffer,c.samples),b&&b.createMipMaps&&!d.generateMipMaps&&(e.generateMipMaps=!0),this._textureHelper.createGPUTextureForInternalTexture(e),b&&b.createMipMaps&&!d.generateMipMaps&&(e.generateMipMaps=!1),c},$f.a.prototype.setTextureSampler=function(a,b){this._engine.setTextureSampler(a,b)},Ug.prototype.setTextureSampler=function(a,b){var c;null===(c=this._currentMaterialContext)||void 0===c||c.setSampler(a,b)},$f.a.prototype.setStorageBuffer=function(a,b){this._engine.setStorageBuffer(a,b)},Ug.prototype.createStorageBuffer=function(a,b){return this._createBuffer(a,b|r.a.BUFFER_CREATIONFLAG_STORAGE)},Ug.prototype.updateStorageBuffer=function(a,b,c,d){var e;a=a;void 0===c&&(c=0),void 0===d?d=(e=b instanceof Array?new Float32Array(b):b instanceof ArrayBuffer?new Uint8Array(b):b).byteLength:e=b instanceof Array?new Float32Array(b):b instanceof ArrayBuffer?new Uint8Array(b):b,this._bufferManager.setSubData(a,c,e,0,d)},Ug.prototype.readFromStorageBuffer=function(a,b,c,d){var e=this;c=c||a.capacity;var f=this._bufferManager.createRawBuffer(c,tf.MapRead|tf.CopyDst);return this._renderTargetEncoder.copyBufferToBuffer(a.underlyingResource,null!=b?b:0,f,0,c),new Promise(function(a,b){e.onEndFrameObservable.addOnce(function(){f.mapAsync(uf.Read,0,c).then(function(){var b=f.getMappedRange(0,c),g=d;if(void 0===g)(g=new Uint8Array(c)).set(new Uint8Array(b));else{var h=g.constructor;(g=new h(g.buffer)).set(new h(b))}f.unmap(),e._bufferManager.releaseBuffer(f),a(g)},function(a){return b(a)})})})},Ug.prototype.setStorageBuffer=function(a,b){var c;null===(c=this._currentMaterialContext)||void 0===c||c.setBuffer(a,null!==(c=null==b?void 0:b.getBuffer())&&void 0!==c?c:null)},Ug.prototype.createUniformBuffer=function(a){return a=a instanceof Array?new Float32Array(a):a,this._bufferManager.createBuffer(a,tf.Uniform|tf.CopyDst)},Ug.prototype.createDynamicUniformBuffer=function(a){return this.createUniformBuffer(a)},Ug.prototype.updateUniformBuffer=function(a,b,c,d){void 0===c&&(c=0);var e;a=a;void 0===d?d=(e=b instanceof Float32Array?b:new Float32Array(b)).byteLength:e=b instanceof Float32Array?b:new Float32Array(b),this._bufferManager.setSubData(a,c,e,0,d)},Ug.prototype.bindUniformBufferBase=function(a,b,c){this._currentMaterialContext.setBuffer(c,a)},Ug.prototype.bindUniformBlock=function(a,b,c){},Ug.prototype.updateVideoTexture=function(a,b,c){var d,e=this;if(a&&!a._isDisabled){void 0===this._videoTextureSupported&&(this._videoTextureSupported=!0);var f=a._hardwareTexture;(null===(d=a._hardwareTexture)||void 0===d?void 0:d.underlyingResource)||(f=this._textureHelper.createGPUTextureForInternalTexture(a)),this.createImageBitmap(b).then(function(b){e._textureHelper.updateTexture(b,a,a.width,a.height,a.depth,f.format,0,0,!c,!1,0,0,e._uploadEncoder),a.generateMipMaps&&e._generateMipmaps(a,e._uploadEncoder),a.isReady=!0})["catch"](function(b){a.isReady=!0})}};var Zg,$g=c(167),ah=c(131),bh=c(81),ch=function(){function a(){}return a.CreateAsync=function(a,b){return Ug.IsSupportedAsync.then(function(c){return c?Ug.CreateAsync(a,b):T.a.IsSupported?new Promise(function(c){c(new T.a(a,void 0,b))}):new Promise(function(a){a(new pe.a(b))})})},a}(),dh=function(){function a(){}return a.COPY=1,a.CUT=2,a.PASTE=3,a}(),eh=function(){function a(a,b){this.type=a,this.event=b}return a.GetTypeFromCharacter=function(a){switch(a){case 67:return dh.COPY;case 86:return dh.PASTE;case 88:return dh.CUT;default:return-1}},a}(),fh=c(77),gh=c(39);!function(a){a[a.Clean=0]="Clean",a[a.Stop=1]="Stop",a[a.Sync=2]="Sync",a[a.NoSync=3]="NoSync"}(Zg||(Zg={}));var hh=function(){function a(){}return Object.defineProperty(a,"ForceFullSceneLoadingForIncremental",{get:function(){return fh.a.ForceFullSceneLoadingForIncremental},set:function(a){fh.a.ForceFullSceneLoadingForIncremental=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ShowLoadingScreen",{get:function(){return fh.a.ShowLoadingScreen},set:function(a){fh.a.ShowLoadingScreen=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"loggingLevel",{get:function(){return fh.a.loggingLevel},set:function(a){fh.a.loggingLevel=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"CleanBoneMatrixWeights",{get:function(){return fh.a.CleanBoneMatrixWeights},set:function(a){fh.a.CleanBoneMatrixWeights=a},enumerable:!1,configurable:!0}),a.GetDefaultPlugin=function(){return a._registeredPlugins[".babylon"]},a._GetPluginForExtension=function(b){var c=a._registeredPlugins[b];return c||(q.a.Warn("Unable to find a plugin to load "+b+" files. Trying to use .babylon default plugin. To load from a specific filetype (eg. gltf) see: https://doc.babylonjs.com/how_to/load_from_any_file_type"),a.GetDefaultPlugin())},a._GetPluginForDirectLoad=function(b){for(var c in a._registeredPlugins){var d=a._registeredPlugins[c].plugin;if(d.canDirectLoad&&d.canDirectLoad(b))return a._registeredPlugins[c]}return a.GetDefaultPlugin()},a._GetPluginForFilename=function(b){var c=b.indexOf("?");-1!==c&&(b=b.substring(0,c));c=b.lastIndexOf(".");c=b.substring(c,b.length).toLowerCase();return a._GetPluginForExtension(c)},a._GetDirectLoad=function(a){return"data:"===a.substr(0,5)?a.substr(5):null},a._FormatErrorMessage=function(a,b,c){a="Unable to load from "+a.url;return b?a+=": "+b:c&&(a+=": "+c),a},a._LoadData=function(b,c,d,e,f,g,h){var i,j=a._GetDirectLoad(b.url);h=h?a._GetPluginForExtension(h):j?a._GetPluginForDirectLoad(b.url):a._GetPluginForFilename(b.url);if(!(i=void 0!==h.plugin.createPlugin?h.plugin.createPlugin():h.plugin))throw"The loader plugin corresponding to the file type you are trying to load has not been found. If using es6, please import the plugin you wish to use before.";if(a.OnPluginActivatedObservable.notifyObservers(i),j&&(i.canDirectLoad&&i.canDirectLoad(b.url)||!Object(ue.e)(b.url))){if(i.directLoad){var k=i.directLoad(c,j);k.then?k.then(function(a){d(i,a)})["catch"](function(a){f("Error in directLoad of _loadData: "+a,a)}):d(i,k)}else d(i,j);return i}var q=h.isBinary,r=function(a,b){c.isDisposed?f("Scene has been disposed"):d(i,a,b)},s=null,t=!1;k=i.onDisposeObservable;k&&k.add(function(){t=!0,s&&(s.abort(),s=null),g()});j=function(){if(!t){var a=function(a,b){f(null==a?void 0:a.statusText,b)},d=b.file||b.url;s=i.loadFile?i.loadFile(c,d,r,e,q,a):c._loadFile(d,r,e,!0,q,a)}};h=c.getEngine();k=h.enableOfflineSupport;if(k){for(var x=!1,y=0,z=c.disableOfflineSupportExceptionRules;yk.snapDistance?(d=Math.floor(Math.abs(u)/k.snapDistance),u<0&&(d*=-1),u%=k.snapDistance,z.scaleToRef(k.snapDistance*d,z),c=!0):z.scaleInPlace(0)),g.a.ScalingToRef(1+z.x,1+z.y,1+z.z,k._tmpMatrix2),k._tmpMatrix2.multiplyToRef(k.attachedNode.getWorldMatrix(),k._tmpMatrix),k._tmpMatrix.decompose(k._tmpVector);Math.abs(k._tmpVector.x)<1e5&&Math.abs(k._tmpVector.y)<1e5&&Math.abs(k._tmpVector.z)<1e5&&k.attachedNode.getWorldMatrix().copyFrom(k._tmpMatrix),c&&(E.snapDistance=k.snapDistance*d,k.onSnapObservable.notifyObservers(E)),k._matrixChanged()}}),k.dragBehavior.onDragStartObservable.add(function(){k._dragging=!0}),k.dragBehavior.onDragObservable.add(function(a){return r(a.dragDistance)}),k.dragBehavior.onDragEndObservable.add(i),null===(j=null===(j=null===(j=null==e?void 0:e.uniformScaleGizmo)||void 0===j?void 0:j.dragBehavior)||void 0===j?void 0:j.onDragObservable)||void 0===j||j.add(function(a){return r(a.delta.y)}),null===(j=null===(e=null===(j=null==e?void 0:e.uniformScaleGizmo)||void 0===j?void 0:j.dragBehavior)||void 0===e?void 0:e.onDragEndObservable)||void 0===j||j.add(i);var F={gizmoMeshes:[l,m],colliderMeshes:[c.arrowMesh,c.arrowTail],material:k._coloredMaterial,hoverMaterial:k._hoverMaterial,disableMaterial:k._disableMaterial,active:!1,dragBehavior:k.dragBehavior};null===(e=k._parent)||void 0===e||e.addToAxisCache(k._gizmoMesh,F),k._pointerObserver=d.utilityLayerScene.onPointerObservable.add(function(a){if(!k._customMeshSet&&(k._isHovered=!(-1==F.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!k._parent)){a=k.dragBehavior.enabled?k._isHovered||k._dragging?k._hoverMaterial:k._coloredMaterial:k._disableMaterial;k._setGizmoMeshMaterial(F.gizmoMeshes,a)}}),k.dragBehavior.onEnabledObservable.add(function(a){k._setGizmoMeshMaterial(F.gizmoMeshes,a?k._coloredMaterial:k._disableMaterial)});j=d._getSharedGizmoLight();return j.includedOnlyMeshes=j.includedOnlyMeshes.concat(k._rootMesh.getChildMeshes()),k}return Object(l.d)(b,a),b.prototype._createGizmoMesh=function(a,b,c){void 0===c&&(c=!1);var d=Object(rh.b)("yPosMesh",{size:.4*(1+(b-1)/4)},this.gizmoLayer.utilityLayerScene);b=Object(xd.a)("cylinder",{diameterTop:.005*b,height:.275,diameterBottom:.005*b,tessellation:96},this.gizmoLayer.utilityLayerScene);return d.scaling.scaleInPlace(.1),d.material=this._coloredMaterial,d.rotation.x=Math.PI/2,d.position.z+=.3,b.material=this._coloredMaterial,b.position.z+=.1375,b.rotation.x=Math.PI/2,c&&(d.visibility=0,b.visibility=0),a.addChild(d),a.addChild(b),{arrowMesh:d,arrowTail:b}},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedMesh=this._parent.attachedMesh,this.attachedNode=this._parent.attachedNode):(this.attachedMesh=null,this.attachedNode=null)},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),this._gizmoMesh&&this._gizmoMesh.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()}),a.prototype.dispose.call(this)},b.prototype.setCustomMesh=function(b,c){var d=this;void 0===c&&(c=!1),a.prototype.setCustomMesh.call(this,b),c&&(this._rootMesh.getChildMeshes().forEach(function(a){a.material=d._coloredMaterial,a.color&&(a.color=d._coloredMaterial.diffuseColor)}),this._customMeshSet=!1)},b}(sh.a),vh=c(48),wh=c(38),xh=c(75),yh=function(a){function b(b,c){void 0===b&&(b=h.a.Gray()),void 0===c&&(c=th.a.DefaultKeepDepthUtilityLayer);var d=a.call(this,c)||this;d._boundingDimensions=new g.e(1,1,1),d._renderObserver=null,d._pointerObserver=null,d._scaleDragSpeed=.2,d._tmpQuaternion=new g.b,d._tmpVector=new g.e(0,0,0),d._tmpRotationMatrix=new g.a,d.ignoreChildren=!1,d.includeChildPredicate=null,d.rotationSphereSize=.1,d.scaleBoxSize=.1,d.fixedDragMeshScreenSize=!1,d.fixedDragMeshBoundsSize=!1,d.fixedDragMeshScreenSizeDistanceFactor=10,d.onDragStartObservable=new f.c,d.onScaleBoxDragObservable=new f.c,d.onScaleBoxDragEndObservable=new f.c,d.onRotationSphereDragObservable=new f.c,d.onRotationSphereDragEndObservable=new f.c,d.scalePivot=null,d._axisFactor=new g.e(1,1,1),d._existingMeshScale=new g.e,d._dragMesh=null,d.pointerDragBehavior=new $a.a,d.updateScale=!1,d._anchorMesh=new bb.a("anchor",c.utilityLayerScene),d.coloredMaterial=new pd.a("",c.utilityLayerScene),d.coloredMaterial.disableLighting=!0,d.hoverColoredMaterial=new pd.a("",c.utilityLayerScene),d.hoverColoredMaterial.disableLighting=!0,d._lineBoundingBox=new bb.a("",c.utilityLayerScene),d._lineBoundingBox.rotationQuaternion=new g.b;var e=[];e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(d._boundingDimensions.x,0,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(0,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(0,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,0,0),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,0,0),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,d._boundingDimensions.y,0),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,d._boundingDimensions.y,0),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,d._boundingDimensions.z),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.forEach(function(a){a.color=b,a.position.addInPlace(new g.e(-d._boundingDimensions.x/2,-d._boundingDimensions.y/2,-d._boundingDimensions.z/2)),a.isPickable=!1,d._lineBoundingBox.addChild(a)}),d._rootMesh.addChild(d._lineBoundingBox),d.setColor(b),d._rotateSpheresParent=new bb.a("",c.utilityLayerScene),d._rotateSpheresParent.rotationQuaternion=new g.b;for(var e=function(a){var b=Object(vh.a)("",{diameter:1},c.utilityLayerScene);b.rotationQuaternion=new g.b,b.material=i.coloredMaterial,b.isNearGrabbable=!0,(t=new $a.a({})).moveAttached=!1,t.updateDragPlane=!1,b.addBehavior(t);var e=new g.e(1,0,0),f=0;t.onDragStartObservable.add(function(){e.copyFrom(b.forward),f=0}),t.onDragObservable.add(function(b){if(d.onRotationSphereDragObservable.notifyObservers({}),d.attachedMesh){var c=d.attachedMesh.parent;if(c&&c.scaling&&c.scaling.isNonUniformWithinEpsilon(.001))return void q.a.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");xh.a._RemoveAndStorePivotPoint(d.attachedMesh);var h=e,i=b.dragPlaneNormal.scale(g.e.Dot(b.dragPlaneNormal,h));h=h.subtract(i).normalizeToNew();i=g.e.Dot(h,b.delta)<0?Math.abs(b.delta.length()):-Math.abs(b.delta.length());i=i/d._boundingDimensions.length()*d._anchorMesh.scaling.length(),d.attachedMesh.rotationQuaternion||(d.attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(d.attachedMesh.rotation.y,d.attachedMesh.rotation.x,d.attachedMesh.rotation.z)),d._anchorMesh.rotationQuaternion||(d._anchorMesh.rotationQuaternion=g.b.RotationYawPitchRoll(d._anchorMesh.rotation.y,d._anchorMesh.rotation.x,d._anchorMesh.rotation.z)),f+=i,Math.abs(f)<=2*Math.PI&&(a>=8?g.b.RotationYawPitchRollToRef(0,0,i,d._tmpQuaternion):a>=4?g.b.RotationYawPitchRollToRef(i,0,0,d._tmpQuaternion):g.b.RotationYawPitchRollToRef(0,i,0,d._tmpQuaternion),d._anchorMesh.addChild(d.attachedMesh),d._anchorMesh.rotationQuaternion.multiplyToRef(d._tmpQuaternion,d._anchorMesh.rotationQuaternion),d._anchorMesh.removeChild(d.attachedMesh),d.attachedMesh.setParent(c)),d.updateBoundingBox(),xh.a._RestorePivotPoint(d.attachedMesh)}d._updateDummy()}),t.onDragStartObservable.add(function(){d.onDragStartObservable.notifyObservers({}),d._selectNode(b)}),t.onDragEndObservable.add(function(){d.onRotationSphereDragEndObservable.notifyObservers({}),d._selectNode(null),d._updateDummy()}),i._rotateSpheresParent.addChild(b)},i=this,j=0;j<12;j++)e(j);d._rootMesh.addChild(d._rotateSpheresParent),d._scaleBoxesParent=new bb.a("",c.utilityLayerScene),d._scaleBoxesParent.rotationQuaternion=new g.b;for(var k=0;k<3;k++)for(var o=0;o<3;o++)for(var t,e=function(){var a=(1===k?1:0)+(1===o?1:0)+(1===v?1:0);if(1===a||3===a)return"continue";var b=Object(rh.b)("",{size:1},c.utilityLayerScene);b.material=u.coloredMaterial,b.metadata=2===a,b.isNearGrabbable=!0;var e=new g.e(k-1,o-1,v-1).normalize();(t=new $a.a({dragAxis:e})).updateDragPlane=!1,t.moveAttached=!1,b.addBehavior(t),t.onDragObservable.add(function(c){if(d.onScaleBoxDragObservable.notifyObservers({}),d.attachedMesh){var f=d.attachedMesh.parent;if(f&&f.scaling&&f.scaling.isNonUniformWithinEpsilon(.001))return void q.a.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");xh.a._RemoveAndStorePivotPoint(d.attachedMesh);c=c.dragDistance/d._boundingDimensions.length()*d._anchorMesh.scaling.length();c=new g.e(c,c,c);2===a&&(c.x*=Math.abs(e.x),c.y*=Math.abs(e.y),c.z*=Math.abs(e.z)),c.scaleInPlace(d._scaleDragSpeed),c.multiplyInPlace(d._axisFactor),d.updateBoundingBox(),d.scalePivot?(d.attachedMesh.getWorldMatrix().getRotationMatrixToRef(d._tmpRotationMatrix),d._boundingDimensions.scaleToRef(.5,d._tmpVector),g.e.TransformCoordinatesToRef(d._tmpVector,d._tmpRotationMatrix,d._tmpVector),d._anchorMesh.position.subtractInPlace(d._tmpVector),d._boundingDimensions.multiplyToRef(d.scalePivot,d._tmpVector),g.e.TransformCoordinatesToRef(d._tmpVector,d._tmpRotationMatrix,d._tmpVector),d._anchorMesh.position.addInPlace(d._tmpVector)):(b.absolutePosition.subtractToRef(d._anchorMesh.position,d._tmpVector),d._anchorMesh.position.subtractInPlace(d._tmpVector)),d._anchorMesh.addChild(d.attachedMesh),d._anchorMesh.scaling.addInPlace(c),(d._anchorMesh.scaling.x<0||d._anchorMesh.scaling.y<0||d._anchorMesh.scaling.z<0)&&d._anchorMesh.scaling.subtractInPlace(c),d._anchorMesh.removeChild(d.attachedMesh),d.attachedMesh.setParent(f),xh.a._RestorePivotPoint(d.attachedMesh)}d._updateDummy()}),t.onDragStartObservable.add(function(){d.onDragStartObservable.notifyObservers({}),d._selectNode(b)}),t.onDragEndObservable.add(function(){d.onScaleBoxDragEndObservable.notifyObservers({}),d._selectNode(null),d._updateDummy()}),u._scaleBoxesParent.addChild(b)},u=this,v=0;v<3;v++)e();d._rootMesh.addChild(d._scaleBoxesParent);var w=new Array;return d._pointerObserver=c.utilityLayerScene.onPointerObservable.add(function(a){w[a.event.pointerId]?a.pickInfo&&a.pickInfo.pickedMesh!=w[a.event.pointerId]&&(w[a.event.pointerId].material=d.coloredMaterial,delete w[a.event.pointerId]):d._rotateSpheresParent.getChildMeshes().concat(d._scaleBoxesParent.getChildMeshes()).forEach(function(b){a.pickInfo&&a.pickInfo.pickedMesh==b&&(w[a.event.pointerId]=b,b.material=d.hoverColoredMaterial)})}),d._renderObserver=d.gizmoLayer.originalScene.onBeforeRenderObservable.add(function(){d.attachedMesh&&!d._existingMeshScale.equals(d.attachedMesh.scaling)?d.updateBoundingBox():(d.fixedDragMeshScreenSize||d.fixedDragMeshBoundsSize)&&(d._updateRotationSpheres(),d._updateScaleBoxes()),d._dragMesh&&d.attachedMesh&&d.pointerDragBehavior.dragging&&(d._lineBoundingBox.position.rotateByQuaternionToRef(d._rootMesh.rotationQuaternion,d._tmpVector),d.attachedMesh.setAbsolutePosition(d._dragMesh.position.add(d._tmpVector.scale(-1))))}),d.updateBoundingBox(),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"axisFactor",{get:function(){return this._axisFactor},set:function(a){this._axisFactor=a;for(var a=this._scaleBoxesParent.getChildMeshes(),b=0,c=0;c<3;c++)for(var d=0;d<3;d++)for(var e=0;e<3;e++){var f=(1===c?1:0)+(1===d?1:0)+(1===e?1:0);if(1!==f&&3!==f){if(a[b]){f=new g.e(c-1,d-1,e-1);f.multiplyInPlace(this._axisFactor),a[b].setEnabled(f.lengthSquared()>hb.a)}b++}}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleDragSpeed",{get:function(){return this._scaleDragSpeed},set:function(a){this._scaleDragSpeed=a},enumerable:!1,configurable:!0}),b.prototype.setColor=function(a){this.coloredMaterial.emissiveColor=a,this.hoverColoredMaterial.emissiveColor=a.clone().add(new h.a(.3,.3,.3)),this._lineBoundingBox.getChildren().forEach(function(b){b.color&&(b.color=a)})},b.prototype._attachedNodeChanged=function(a){var b=this;if(a){this._anchorMesh.scaling.setAll(1),xh.a._RemoveAndStorePivotPoint(a);var c=a.parent;this._anchorMesh.addChild(a),this._anchorMesh.removeChild(a),a.setParent(c),xh.a._RestorePivotPoint(a),this.updateBoundingBox(),a.getChildMeshes(!1).forEach(function(a){a.markAsDirty("scaling")}),this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(function(){b._updateDummy()})}},b.prototype._selectNode=function(a){this._rotateSpheresParent.getChildMeshes().concat(this._scaleBoxesParent.getChildMeshes()).forEach(function(b){b.isVisible=!a||b==a})},b.prototype.updateBoundingBox=function(){if(this.attachedMesh){xh.a._RemoveAndStorePivotPoint(this.attachedMesh);var a=this.attachedMesh.parent;this.attachedMesh.setParent(null);var b=null;this.attachedMesh.skeleton&&(b=this.attachedMesh.skeleton.overrideMesh,this.attachedMesh.skeleton.overrideMesh=null),this._update(),this.attachedMesh.rotationQuaternion||(this.attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this.attachedMesh.rotation.y,this.attachedMesh.rotation.x,this.attachedMesh.rotation.z)),this._anchorMesh.rotationQuaternion||(this._anchorMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this._anchorMesh.rotation.y,this._anchorMesh.rotation.x,this._anchorMesh.rotation.z)),this._anchorMesh.rotationQuaternion.copyFrom(this.attachedMesh.rotationQuaternion),this._tmpQuaternion.copyFrom(this.attachedMesh.rotationQuaternion),this._tmpVector.copyFrom(this.attachedMesh.position),this.attachedMesh.rotationQuaternion.set(0,0,0,1),this.attachedMesh.position.set(0,0,0);var c=this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren,this.includeChildPredicate);c.max.subtractToRef(c.min,this._boundingDimensions),this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions),this._lineBoundingBox.position.set((c.max.x+c.min.x)/2,(c.max.y+c.min.y)/2,(c.max.z+c.min.z)/2),this._rotateSpheresParent.position.copyFrom(this._lineBoundingBox.position),this._scaleBoxesParent.position.copyFrom(this._lineBoundingBox.position),this._lineBoundingBox.computeWorldMatrix(),this._anchorMesh.position.copyFrom(this._lineBoundingBox.absolutePosition),this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion),this.attachedMesh.position.copyFrom(this._tmpVector),this.attachedMesh.setParent(a),this.attachedMesh.skeleton&&(this.attachedMesh.skeleton.overrideMesh=b)}this._updateRotationSpheres(),this._updateScaleBoxes(),this.attachedMesh&&(this._existingMeshScale.copyFrom(this.attachedMesh.scaling),xh.a._RestorePivotPoint(this.attachedMesh))},b.prototype._updateRotationSpheres=function(){for(var a=this._rotateSpheresParent.getChildMeshes(),b=0;b<3;b++)for(var c=0;c<2;c++)for(var d=0;d<2;d++){var e=4*b+2*c+d;if(0==b&&(a[e].position.set(this._boundingDimensions.x/2,this._boundingDimensions.y*c,this._boundingDimensions.z*d),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Right()).normalizeToNew().add(a[e].position))),1==b&&(a[e].position.set(this._boundingDimensions.x*c,this._boundingDimensions.y/2,this._boundingDimensions.z*d),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Up()).normalizeToNew().add(a[e].position))),2==b&&(a[e].position.set(this._boundingDimensions.x*c,this._boundingDimensions.y*d,this._boundingDimensions.z/2),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Forward()).normalizeToNew().add(a[e].position))),this.fixedDragMeshScreenSize&&this.gizmoLayer.utilityLayerScene.activeCamera){a[e].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera.position,this._tmpVector);var f=this.rotationSphereSize*this._tmpVector.length()/this.fixedDragMeshScreenSizeDistanceFactor;a[e].scaling.set(f,f,f)}else this.fixedDragMeshBoundsSize?a[e].scaling.set(this.rotationSphereSize*this._boundingDimensions.x,this.rotationSphereSize*this._boundingDimensions.y,this.rotationSphereSize*this._boundingDimensions.z):a[e].scaling.set(this.rotationSphereSize,this.rotationSphereSize,this.rotationSphereSize)}},b.prototype._updateScaleBoxes=function(){for(var a=this._scaleBoxesParent.getChildMeshes(),b=0,c=0;c<3;c++)for(var d=0;d<3;d++)for(var e=0;e<3;e++){var f=(1===c?1:0)+(1===d?1:0)+(1===e?1:0);if(1!==f&&3!==f){if(a[b])if(a[b].position.set(this._boundingDimensions.x*(c/2),this._boundingDimensions.y*(d/2),this._boundingDimensions.z*(e/2)),a[b].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),this.fixedDragMeshScreenSize&&this.gizmoLayer.utilityLayerScene.activeCamera){a[b].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera.position,this._tmpVector);f=this.scaleBoxSize*this._tmpVector.length()/this.fixedDragMeshScreenSizeDistanceFactor;a[b].scaling.set(f,f,f)}else this.fixedDragMeshBoundsSize?a[b].scaling.set(this.scaleBoxSize*this._boundingDimensions.x,this.scaleBoxSize*this._boundingDimensions.y,this.scaleBoxSize*this._boundingDimensions.z):a[b].scaling.set(this.scaleBoxSize,this.scaleBoxSize,this.scaleBoxSize);b++}}},b.prototype.setEnabledRotationAxis=function(a){this._rotateSpheresParent.getChildMeshes().forEach(function(b,c){c<4?b.setEnabled(-1!=a.indexOf("x")):c<8?b.setEnabled(-1!=a.indexOf("y")):b.setEnabled(-1!=a.indexOf("z"))})},b.prototype.setEnabledScaling=function(a,b){void 0===b&&(b=!1),this._scaleBoxesParent.getChildMeshes().forEach(function(c,d){d=a;b&&!0===c.metadata&&(d=!1),c.setEnabled(d)})},b.prototype._updateDummy=function(){this._dragMesh&&(this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition()),this._dragMesh.scaling.copyFrom(this._lineBoundingBox.scaling),this._dragMesh.rotationQuaternion.copyFrom(this._rootMesh.rotationQuaternion))},b.prototype.enableDragBehavior=function(){this._dragMesh=Object(rh.b)("dummy",{size:1},this.gizmoLayer.utilityLayerScene),this._dragMesh.visibility=0,this._dragMesh.rotationQuaternion=new g.b,this.pointerDragBehavior.useObjectOrientationForDragging=!1,this._dragMesh.addBehavior(this.pointerDragBehavior)},b.prototype.dispose=function(){this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.gizmoLayer.originalScene.onBeforeRenderObservable.remove(this._renderObserver),this._lineBoundingBox.dispose(),this._rotateSpheresParent.dispose(),this._scaleBoxesParent.dispose(),this._dragMesh&&this._dragMesh.dispose(),a.prototype.dispose.call(this)},b.MakeNotPickableAndWrapInBoundingBox=function(a){var b=function(a){a.isPickable=!1,a.getChildMeshes().forEach(function(a){b(a)})};b(a),a.rotationQuaternion||(a.rotationQuaternion=g.b.RotationYawPitchRoll(a.rotation.y,a.rotation.x,a.rotation.z));var c=a.position.clone(),d=a.rotationQuaternion.clone();a.rotationQuaternion.set(0,0,0,1),a.position.set(0,0,0);var e=Object(rh.b)("box",{size:1},a.getScene()),f=a.getHierarchyBoundingVectors();return f.max.subtractToRef(f.min,e.scaling),0===e.scaling.y&&(e.scaling.y=hb.a),0===e.scaling.x&&(e.scaling.x=hb.a),0===e.scaling.z&&(e.scaling.z=hb.a),e.position.set((f.max.x+f.min.x)/2,(f.max.y+f.min.y)/2,(f.max.z+f.min.z)/2),a.addChild(e),a.rotationQuaternion.copyFrom(d),a.position.copyFrom(c),a.removeChild(e),e.addChild(a),e.visibility=0,e},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo")},b}(sh.a),zh=c(71),Ah=c(66),Bh=function(a){function b(c,d,e,i,j,k,m){void 0===d&&(d=h.a.Gray()),void 0===e&&(e=th.a.DefaultUtilityLayer),void 0===i&&(i=32),void 0===j&&(j=null),void 0===m&&(m=1);var p=a.call(this,e)||this;p._pointerObserver=null,p.snapDistance=0,p.onSnapObservable=new f.c,p.angle=0,p._isEnabled=!0,p._parent=null,p._dragging=!1,p._angles=new g.e,p._parent=j,p._coloredMaterial=new pd.a("",e.utilityLayerScene),p._coloredMaterial.diffuseColor=d,p._coloredMaterial.specularColor=d.subtract(new h.a(.1,.1,.1)),p._hoverMaterial=new pd.a("",e.utilityLayerScene),p._hoverMaterial.diffuseColor=h.a.Yellow(),p._disableMaterial=new pd.a("",e.utilityLayerScene),p._disableMaterial.diffuseColor=h.a.Gray(),p._disableMaterial.alpha=.4,p._gizmoMesh=new S.a("",e.utilityLayerScene);k=p._createGizmoMesh(p._gizmoMesh,m,i);j=k.rotationMesh;d=k.collider;p._rotationDisplayPlane=Object(Ah.a)("rotationDisplay",{size:.6,updatable:!1},p.gizmoLayer.utilityLayerScene),p._rotationDisplayPlane.rotation.z=.5*Math.PI,p._rotationDisplayPlane.parent=p._gizmoMesh,p._rotationDisplayPlane.setEnabled(!1),$f.a.ShadersStore.rotationGizmoVertexShader=b._rotationGizmoVertexShader,$f.a.ShadersStore.rotationGizmoFragmentShader=b._rotationGizmoFragmentShader,p._rotationShaderMaterial=new zh.a("shader",p.gizmoLayer.utilityLayerScene,{vertex:"rotationGizmo",fragment:"rotationGizmo"},{attributes:["position","uv"],uniforms:["worldViewProjection","angles"]}),p._rotationShaderMaterial.backFaceCulling=!1,p._rotationDisplayPlane.material=p._rotationShaderMaterial,p._rotationDisplayPlane.visibility=.999,p._gizmoMesh.lookAt(p._rootMesh.position.add(c)),p._rootMesh.addChild(p._gizmoMesh),p._gizmoMesh.scaling.scaleInPlace(1/3),p.dragBehavior=new $a.a({dragPlaneNormal:c}),p.dragBehavior.moveAttached=!1,p.dragBehavior.maxDragAngle=b.MaxDragAngle,p.dragBehavior._useAlternatePickedPointAboveMaxDragAngle=!0,p._rootMesh.addBehavior(p.dragBehavior);var q=new g.e,z=new g.a,B=new g.e,C=new g.e;p.dragBehavior.onDragStartObservable.add(function(a){p.attachedNode&&(q.copyFrom(a.dragPlanePoint),p._rotationDisplayPlane.setEnabled(!0),p._rotationDisplayPlane.getWorldMatrix().invertToRef(z),g.e.TransformCoordinatesToRef(a.dragPlanePoint,z,q),p._angles.x=Math.atan2(q.y,q.x)+Math.PI,p._angles.y=0,p._angles.z=p.updateGizmoRotationToMatchAttachedMesh?1:0,p._dragging=!0,q.copyFrom(a.dragPlanePoint),p._rotationShaderMaterial.setVector3("angles",p._angles),p.angle=0)}),p.dragBehavior.onDragEndObservable.add(function(){p._dragging=!1,p._rotationDisplayPlane.setEnabled(!1)});var D={snapDistance:0},E=0,F=new g.a,G=new g.b;p.dragBehavior.onDragObservable.add(function(a){if(p.attachedNode){var b=new g.e(1,1,1),d=new g.b(0,0,0,1),f=new g.e(0,0,0);p._handlePivot(),p.attachedNode.getWorldMatrix().decompose(b,d,f);var h=a.dragPlanePoint.subtract(f).normalize(),i=q.subtract(f).normalize(),j=g.e.Cross(h,i);h=g.e.Dot(h,i);i=Math.atan2(j.length(),h);B.copyFrom(c),C.copyFrom(c),p.updateGizmoRotationToMatchAttachedMesh&&(d.toRotationMatrix(z),C=g.e.TransformCoordinates(B,z));h=!1;if(e.utilityLayerScene.activeCamera){var k=e.utilityLayerScene.activeCamera.position.subtract(f).normalize();g.e.Dot(k,C)>0&&(B.scaleInPlace(-1),C.scaleInPlace(-1),h=!0)}g.e.Dot(C,j)>0&&(i=-i);k=!1;if(0!=p.snapDistance)if(E+=i,Math.abs(E)>p.snapDistance){j=Math.floor(Math.abs(E)/p.snapDistance);E<0&&(j*=-1),E%=p.snapDistance,i=p.snapDistance*j,k=!0}else i=0;j=Math.sin(i/2);if(G.set(B.x*j,B.y*j,B.z*j,Math.cos(i/2)),F.determinant()>0){j=new g.e;G.toEulerAnglesToRef(j),g.b.RotationYawPitchRollToRef(j.y,-j.x,-j.z,G)}p.updateGizmoRotationToMatchAttachedMesh?d.multiplyToRef(G,d):G.multiplyToRef(d,d),p.attachedNode.getWorldMatrix().copyFrom(g.a.Compose(b,d,f)),q.copyFrom(a.dragPlanePoint),k&&(D.snapDistance=i,p.onSnapObservable.notifyObservers(D)),p._angles.y+=i,p.angle+=h?-i:i,p._rotationShaderMaterial.setVector3("angles",p._angles),p._matrixChanged()}});m=e._getSharedGizmoLight();m.includedOnlyMeshes=m.includedOnlyMeshes.concat(p._rootMesh.getChildMeshes(!1));var H={colliderMeshes:[d],gizmoMeshes:[j],material:p._coloredMaterial,hoverMaterial:p._hoverMaterial,disableMaterial:p._disableMaterial,active:!1,dragBehavior:p.dragBehavior};return null===(i=p._parent)||void 0===i||i.addToAxisCache(p._gizmoMesh,H),p._pointerObserver=e.utilityLayerScene.onPointerObservable.add(function(a){if(!p._customMeshSet&&(p.dragBehavior.maxDragAngle=b.MaxDragAngle,p._isHovered=!(-1==H.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!p._parent)){a=H.dragBehavior.enabled?p._isHovered||p._dragging?p._hoverMaterial:p._coloredMaterial:p._disableMaterial;p._setGizmoMeshMaterial(H.gizmoMeshes,a)}}),p.dragBehavior.onEnabledObservable.add(function(a){p._setGizmoMeshMaterial(H.gizmoMeshes,a?p._coloredMaterial:p._disableMaterial)}),p}return Object(l.d)(b,a),b.prototype._createGizmoMesh=function(a,b,c){var d=Ad("ignore",{diameter:.6,thickness:.03*b,tessellation:c},this.gizmoLayer.utilityLayerScene);d.visibility=0;b=Ad("",{diameter:.6,thickness:.005*b,tessellation:c},this.gizmoLayer.utilityLayerScene);return b.material=this._coloredMaterial,b.rotation.x=Math.PI/2,d.rotation.x=Math.PI/2,a.addChild(b),a.addChild(d),{rotationMesh:b,collider:d}},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedMesh=this._parent.attachedMesh):this.attachedMesh=null},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),this._gizmoMesh&&this._gizmoMesh.dispose(),this._rotationDisplayPlane&&this._rotationDisplayPlane.dispose(),this._rotationShaderMaterial&&this._rotationShaderMaterial.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()}),a.prototype.dispose.call(this)},b.MaxDragAngle=9*Math.PI/20,b._rotationGizmoVertexShader=" precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 worldViewProjection; varying vec3 vPosition; varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }",b._rotationGizmoFragmentShader=" precision highp float; varying vec2 vUV; varying vec3 vPosition; uniform vec3 angles; #define twopi 6.283185307 void main(void) { vec2 uv = vUV - vec2(0.5); float angle = atan(uv.y, uv.x) + 3.141592; float delta = gl_FrontFacing ? angles.y : -angles.y; float begin = angles.x - delta * angles.z; float start = (begin < (begin + delta)) ? begin : (begin + delta); float end = (begin > (begin + delta)) ? begin : (begin + delta); float len = sqrt(dot(uv,uv)); float opacity = 1. - step(0.5, len); float base = abs(floor(start / twopi)) * twopi; start += base; end += base; float intensity = 0.; for (int i = 0; i < 5; i++) { intensity += max(step(start, angle) - step(end, angle), 0.); angle += twopi; } gl_FragColor = vec4(1.,1.,0., min(intensity * 0.25, 0.8)) * opacity; }",b}(sh.a),Ch=function(a){function b(b,c,d,e,i,j){void 0===b&&(b=th.a.DefaultUtilityLayer),void 0===c&&(c=32),void 0===d&&(d=!1),void 0===e&&(e=1);var k=a.call(this,b)||this;k.onDragStartObservable=new f.c,k.onDragEndObservable=new f.c,k._observables=[],k._gizmoAxisCache=new Map;var o=j&&j.xOptions&&j.xOptions.color?j.xOptions.color:h.a.Red().scale(.5),p=j&&j.yOptions&&j.yOptions.color?j.yOptions.color:h.a.Green().scale(.5),q=j&&j.zOptions&&j.zOptions.color?j.zOptions.color:h.a.Blue().scale(.5);return k.xGizmo=new Bh(new g.e(1,0,0),o,b,c,k,d,e),k.yGizmo=new Bh(new g.e(0,1,0),p,b,c,k,d,e),k.zGizmo=new Bh(new g.e(0,0,1),q,b,c,k,d,e),[k.xGizmo,k.yGizmo,k.zGizmo].forEach(function(a){j&&null!=j.updateScale&&(a.updateScale=j.updateScale),a.dragBehavior.onDragStartObservable.add(function(){k.onDragStartObservable.notifyObservers({})}),a.dragBehavior.onDragEndObservable.add(function(){k.onDragEndObservable.notifyObservers({})})}),k.attachedMesh=null,k.attachedNode=null,i?i.addToAxisCache(k._gizmoAxisCache):sh.a.GizmoAxisPointerObserver(b,k._gizmoAxisCache),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"attachedMesh",{get:function(){return this._meshAttached},set:function(a){this._meshAttached=a,this._nodeAttached=a,this._checkBillboardTransform(),[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){b.isEnabled?b.attachedMesh=a:b.attachedMesh=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"attachedNode",{get:function(){return this._nodeAttached},set:function(a){this._meshAttached=null,this._nodeAttached=a,this._checkBillboardTransform(),[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){b.isEnabled?b.attachedNode=a:b.attachedNode=null})},enumerable:!1,configurable:!0}),b.prototype._checkBillboardTransform=function(){this._nodeAttached&&this._nodeAttached.billboardMode&&!1},Object.defineProperty(b.prototype,"isHovered",{get:function(){var a=!1;return[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){a=a||b.isHovered}),a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"updateGizmoRotationToMatchAttachedMesh",{get:function(){return this.xGizmo.updateGizmoRotationToMatchAttachedMesh},set:function(a){this.xGizmo&&(this.xGizmo.updateGizmoRotationToMatchAttachedMesh=a,this.yGizmo.updateGizmoRotationToMatchAttachedMesh=a,this.zGizmo.updateGizmoRotationToMatchAttachedMesh=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapDistance",{get:function(){return this.xGizmo.snapDistance},set:function(a){this.xGizmo&&(this.xGizmo.snapDistance=a,this.yGizmo.snapDistance=a,this.zGizmo.snapDistance=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleRatio",{get:function(){return this.xGizmo.scaleRatio},set:function(a){this.xGizmo&&(this.xGizmo.scaleRatio=a,this.yGizmo.scaleRatio=a,this.zGizmo.scaleRatio=a)},enumerable:!1,configurable:!0}),b.prototype.addToAxisCache=function(a,b){this._gizmoAxisCache.set(a,b)},b.prototype.dispose=function(){var a=this;this.xGizmo.dispose(),this.yGizmo.dispose(),this.zGizmo.dispose(),this.onDragStartObservable.clear(),this.onDragEndObservable.clear(),this._observables.forEach(function(b){a.gizmoLayer.utilityLayerScene.onPointerObservable.remove(b)})},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)")},b}(sh.a),Dh=function(a){function b(c,d,e,i){void 0===d&&(d=h.a.Gray()),void 0===e&&(e=th.a.DefaultUtilityLayer),void 0===i&&(i=null);var j=a.call(this,e)||this;j._pointerObserver=null,j.snapDistance=0,j.onSnapObservable=new f.c,j._isEnabled=!1,j._parent=null,j._dragging=!1,j._parent=i,j._coloredMaterial=new pd.a("",e.utilityLayerScene),j._coloredMaterial.diffuseColor=d,j._coloredMaterial.specularColor=d.subtract(new h.a(.1,.1,.1)),j._hoverMaterial=new pd.a("",e.utilityLayerScene),j._hoverMaterial.diffuseColor=h.a.Yellow(),j._disableMaterial=new pd.a("",e.utilityLayerScene),j._disableMaterial.diffuseColor=h.a.Gray(),j._disableMaterial.alpha=.4,j._gizmoMesh=b._CreatePlane(e.utilityLayerScene,j._coloredMaterial),j._gizmoMesh.lookAt(j._rootMesh.position.add(c)),j._gizmoMesh.scaling.scaleInPlace(1/3),j._gizmoMesh.parent=j._rootMesh;var k=0,o=new g.e,q={snapDistance:0};j.dragBehavior=new $a.a({dragPlaneNormal:c}),j.dragBehavior.moveAttached=!1,j._rootMesh.addBehavior(j.dragBehavior),j.dragBehavior.onDragObservable.add(function(a){if(j.attachedNode){if(j._handlePivot(),0==j.snapDistance)j.attachedNode.getWorldMatrix().addTranslationFromFloats(a.delta.x,a.delta.y,a.delta.z);else if(k+=a.dragDistance,Math.abs(k)>j.snapDistance){var b=Math.floor(Math.abs(k)/j.snapDistance);k%=j.snapDistance,a.delta.normalizeToRef(o),o.scaleInPlace(j.snapDistance*b),j.attachedNode.getWorldMatrix().addTranslationFromFloats(o.x,o.y,o.z),q.snapDistance=j.snapDistance*b,j.onSnapObservable.notifyObservers(q)}j._matrixChanged()}}),j.dragBehavior.onDragStartObservable.add(function(){j._dragging=!0}),j.dragBehavior.onDragEndObservable.add(function(){j._dragging=!1});i=e._getSharedGizmoLight();i.includedOnlyMeshes=i.includedOnlyMeshes.concat(j._rootMesh.getChildMeshes(!1));var r={gizmoMeshes:j._gizmoMesh.getChildMeshes(),colliderMeshes:j._gizmoMesh.getChildMeshes(),material:j._coloredMaterial,hoverMaterial:j._hoverMaterial,disableMaterial:j._disableMaterial,active:!1,dragBehavior:j.dragBehavior};return null===(d=j._parent)||void 0===d||d.addToAxisCache(j._gizmoMesh,r),j._pointerObserver=e.utilityLayerScene.onPointerObservable.add(function(a){if(!j._customMeshSet&&(j._isHovered=!(-1==r.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!j._parent)){a=r.dragBehavior.enabled?j._isHovered||j._dragging?j._hoverMaterial:j._coloredMaterial:j._disableMaterial;j._setGizmoMeshMaterial(r.gizmoMeshes,a)}}),j.dragBehavior.onEnabledObservable.add(function(a){j._setGizmoMeshMaterial(r.gizmoMeshes,a?j._coloredMaterial:j._disableMaterial)}),j}return Object(l.d)(b,a),b._CreatePlane=function(a,b){var c=new eb.a("plane",a);a=Object(Ah.a)("dragPlane",{width:.1375,height:.1375,sideOrientation:2},a);return a.material=b,a.parent=c,c},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedNode=this._parent.attachedNode):this.attachedNode=null},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),a.prototype.dispose.call(this),this._gizmoMesh&&this._gizmoMesh.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()})},b}(sh.a),Eh=function(a){function b(b,c,d){void 0===b&&(b=th.a.DefaultUtilityLayer),void 0===c&&(c=1);var e=a.call(this,b)||this;return e._meshAttached=null,e._nodeAttached=null,e._observables=[],e._gizmoAxisCache=new Map,e.onDragStartObservable=new f.c,e.onDragEndObservable=new f.c,e._planarGizmoEnabled=!1,e.xGizmo=new qh.a(new g.e(1,0,0),h.a.Red().scale(.5),b,e,c),e.yGizmo=new qh.a(new g.e(0,1,0),h.a.Green().scale(.5),b,e,c),e.zGizmo=new qh.a(new g.e(0,0,1),h.a.Blue().scale(.5),b,e,c),e.xPlaneGizmo=new Dh(new g.e(1,0,0),h.a.Red().scale(.5),e.gizmoLayer,e),e.yPlaneGizmo=new Dh(new g.e(0,1,0),h.a.Green().scale(.5),e.gizmoLayer,e),e.zPlaneGizmo=new Dh(new g.e(0,0,1),h.a.Blue().scale(.5),e.gizmoLayer,e),[e.xGizmo,e.yGizmo,e.zGizmo,e.xPlaneGizmo,e.yPlaneGizmo,e.zPlaneGizmo].forEach(function(a){a.dragBehavior.onDragStartObservable.add(function(){e.onDragStartObservable.notifyObservers({})}),a.dragBehavior.onDragEndObservable.add(function(){e.onDragEndObservable.notifyObservers({})})}),e.attachedMesh=null,d?d.addToAxisCache(e._gizmoAxisCache):sh.a.GizmoAxisPointerObserver(b,e._gizmoAxisCache),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"attachedMesh",{get:function(){return this._meshAttached},set:function(a){this._meshAttached=a,this._nodeAttached=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b.isEnabled?b.attachedMesh=a:b.attachedMesh=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"attachedNode",{get:function(){return this._nodeAttached},set:function(a){this._meshAttached=null,this._nodeAttached=null,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b.isEnabled?b.attachedNode=a:b.attachedNode=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isHovered",{get:function(){var a=!1;return[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){a=a||b.isHovered}),a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"planarGizmoEnabled",{get:function(){return this._planarGizmoEnabled},set:function(a){var b=this;this._planarGizmoEnabled=a,[this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(c){c&&(c.isEnabled=a,a&&(c.attachedMesh?c.attachedMesh=b.attachedMesh:c.attachedNode=b.attachedNode))},this)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"updateGizmoRotationToMatchAttachedMesh",{get:function(){return this._updateGizmoRotationToMatchAttachedMesh},set:function(a){this._updateGizmoRotationToMatchAttachedMesh=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.updateGizmoRotationToMatchAttachedMesh=a)})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapDistance",{get:function(){return this._snapDistance},set:function(a){this._snapDistance=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.snapDistance=a)})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleRatio",{get:function(){return this._scaleRatio},set:function(a){this._scaleRatio=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.scaleRatio=a)})},enumerable:!1,configurable:!0}),b.prototype.addToAxisCache=function(a,b){this._gizmoAxisCache.set(a,b)},b.prototype.dispose=function(){var a=this;[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(a){a&&a.dispose()}),this._observables.forEach(function(b){a.gizmoLayer.utilityLayerScene.onPointerObservable.remove(b)}),this.onDragStartObservable.clear(),this.onDragEndObservable.clear()},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo,gizmo.xPlaneGizmo, gizmo.yPlaneGizmo, gizmo.zPlaneGizmo)")},b}(sh.a);function Fh(a){var b=[];b[0]={vertex:[[0,0,1.732051],[1.632993,0,-.5773503],[-.8164966,1.414214,-.5773503],[-.8164966,-1.414214,-.5773503]],face:[[0,1,2],[0,2,3],[0,3,1],[1,3,2]]},b[1]={vertex:[[0,0,1.414214],[1.414214,0,0],[0,1.414214,0],[-1.414214,0,0],[0,-1.414214,0],[0,0,-1.414214]],face:[[0,1,2],[0,2,3],[0,3,4],[0,4,1],[1,4,5],[1,5,2],[2,5,3],[3,5,4]]},b[2]={vertex:[[0,0,1.070466],[.7136442,0,.7978784],[-.3568221,.618034,.7978784],[-.3568221,-.618034,.7978784],[.7978784,.618034,.3568221],[.7978784,-.618034,.3568221],[-.9341724,.381966,.3568221],[.1362939,1,.3568221],[.1362939,-1,.3568221],[-.9341724,-.381966,.3568221],[.9341724,.381966,-.3568221],[.9341724,-.381966,-.3568221],[-.7978784,.618034,-.3568221],[-.1362939,1,-.3568221],[-.1362939,-1,-.3568221],[-.7978784,-.618034,-.3568221],[.3568221,.618034,-.7978784],[.3568221,-.618034,-.7978784],[-.7136442,0,-.7978784],[0,0,-1.070466]],face:[[0,1,4,7,2],[0,2,6,9,3],[0,3,8,5,1],[1,5,11,10,4],[2,7,13,12,6],[3,9,15,14,8],[4,10,16,13,7],[5,8,14,17,11],[6,12,18,15,9],[10,11,17,19,16],[12,13,16,19,18],[14,15,18,19,17]]},b[3]={vertex:[[0,0,1.175571],[1.051462,0,.5257311],[.3249197,1,.5257311],[-.8506508,.618034,.5257311],[-.8506508,-.618034,.5257311],[.3249197,-1,.5257311],[.8506508,.618034,-.5257311],[.8506508,-.618034,-.5257311],[-.3249197,1,-.5257311],[-1.051462,0,-.5257311],[-.3249197,-1,-.5257311],[0,0,-1.175571]],face:[[0,1,2],[0,2,3],[0,3,4],[0,4,5],[0,5,1],[1,5,7],[1,7,6],[1,6,2],[2,6,8],[2,8,3],[3,8,9],[3,9,4],[4,9,10],[4,10,5],[5,10,7],[6,7,11],[6,11,8],[7,10,11],[8,11,9],[9,11,10]]},b[4]={vertex:[[0,0,1.070722],[.7148135,0,.7971752],[-.104682,.7071068,.7971752],[-.6841528,.2071068,.7971752],[-.104682,-.7071068,.7971752],[.6101315,.7071068,.5236279],[1.04156,.2071068,.1367736],[.6101315,-.7071068,.5236279],[-.3574067,1,.1367736],[-.7888348,-.5,.5236279],[-.9368776,.5,.1367736],[-.3574067,-1,.1367736],[.3574067,1,-.1367736],[.9368776,-.5,-.1367736],[.7888348,.5,-.5236279],[.3574067,-1,-.1367736],[-.6101315,.7071068,-.5236279],[-1.04156,-.2071068,-.1367736],[-.6101315,-.7071068,-.5236279],[.104682,.7071068,-.7971752],[.6841528,-.2071068,-.7971752],[.104682,-.7071068,-.7971752],[-.7148135,0,-.7971752],[0,0,-1.070722]],face:[[0,2,3],[1,6,5],[4,9,11],[7,15,13],[8,16,10],[12,14,19],[17,22,18],[20,21,23],[0,1,5,2],[0,3,9,4],[0,4,7,1],[1,7,13,6],[2,5,12,8],[2,8,10,3],[3,10,17,9],[4,11,15,7],[5,6,14,12],[6,13,20,14],[8,12,19,16],[9,17,18,11],[10,16,22,17],[11,18,21,15],[13,15,21,20],[14,20,23,19],[16,19,23,22],[18,22,23,21]]},b[5]={vertex:[[0,0,1.322876],[1.309307,0,.1889822],[-.9819805,.8660254,.1889822],[.1636634,-1.299038,.1889822],[.3273268,.8660254,-.9449112],[-.8183171,-.4330127,-.9449112]],face:[[0,3,1],[2,4,5],[0,1,4,2],[0,2,5,3],[1,3,5,4]]},b[6]={vertex:[[0,0,1.159953],[1.013464,0,.5642542],[-.3501431,.9510565,.5642542],[-.7715208,-.6571639,.5642542],[.6633206,.9510565,-.03144481],[.8682979,-.6571639,-.3996071],[-1.121664,.2938926,-.03144481],[-.2348831,-1.063314,-.3996071],[.5181548,.2938926,-.9953061],[-.5850262,-.112257,-.9953061]],face:[[0,1,4,2],[0,2,6,3],[1,5,8,4],[3,6,9,7],[5,7,9,8],[0,3,7,5,1],[2,4,8,9,6]]},b[7]={vertex:[[0,0,1.118034],[.8944272,0,.6708204],[-.2236068,.8660254,.6708204],[-.7826238,-.4330127,.6708204],[.6708204,.8660254,.2236068],[1.006231,-.4330127,-.2236068],[-1.006231,.4330127,.2236068],[-.6708204,-.8660254,-.2236068],[.7826238,.4330127,-.6708204],[.2236068,-.8660254,-.6708204],[-.8944272,0,-.6708204],[0,0,-1.118034]],face:[[0,1,4,2],[0,2,6,3],[1,5,8,4],[3,6,10,7],[5,9,11,8],[7,10,11,9],[0,3,7,9,5,1],[2,4,8,11,10,6]]},b[8]={vertex:[[-.729665,.670121,.319155],[-.655235,-.29213,-.754096],[-.093922,-.607123,.537818],[.702196,.595691,.485187],[.776626,-.36656,-.588064]],face:[[1,4,2],[0,1,2],[3,0,2],[4,3,2],[4,1,0,3]]},b[9]={vertex:[[-.868849,-.100041,.61257],[-.329458,.976099,.28078],[-.26629,-.013796,-.477654],[-.13392,-1.034115,.229829],[.738834,.707117,-.307018],[.859683,-.535264,-.338508]],face:[[3,0,2],[5,3,2],[4,5,2],[1,4,2],[0,1,2],[0,3,5,4,1]]},b[10]={vertex:[[-.610389,.243975,.531213],[-.187812,-.48795,-.664016],[-.187812,.9759,-.664016],[.187812,-.9759,.664016],[.798201,.243975,.132803]],face:[[1,3,0],[3,4,0],[3,1,4],[0,2,1],[0,4,2],[2,4,1]]},b[11]={vertex:[[-1.028778,.392027,-.048786],[-.640503,-.646161,.621837],[-.125162,-.395663,-.540059],[.004683,.888447,-.651988],[.125161,.395663,.540059],[.632925,-.791376,.433102],[1.031672,.157063,-.354165]],face:[[3,2,0],[2,1,0],[2,5,1],[0,4,3],[0,1,4],[4,1,5],[2,3,6],[3,4,6],[5,2,6],[4,5,6]]},b[12]={vertex:[[-.669867,.334933,-.529576],[-.669867,.334933,.529577],[-.4043,1.212901,0],[-.334933,-.669867,-.529576],[-.334933,-.669867,.529577],[.334933,.669867,-.529576],[.334933,.669867,.529577],[.4043,-1.212901,0],[.669867,-.334933,-.529576],[.669867,-.334933,.529577]],face:[[8,9,7],[6,5,2],[3,8,7],[5,0,2],[4,3,7],[0,1,2],[9,4,7],[1,6,2],[9,8,5,6],[8,3,0,5],[3,4,1,0],[4,9,6,1]]},b[13]={vertex:[[-.931836,.219976,-.264632],[-.636706,.318353,.692816],[-.613483,-.735083,-.264632],[-.326545,.979634,0],[-.318353,-.636706,.692816],[-.159176,.477529,-.856368],[.159176,-.477529,-.856368],[.318353,.636706,.692816],[.326545,-.979634,0],[.613482,.735082,-.264632],[.636706,-.318353,.692816],[.931835,-.219977,-.264632]],face:[[11,10,8],[7,9,3],[6,11,8],[9,5,3],[2,6,8],[5,0,3],[4,2,8],[0,1,3],[10,4,8],[1,7,3],[10,11,9,7],[11,6,5,9],[6,2,0,5],[2,4,1,0],[4,10,7,1]]},b[14]={vertex:[[-.93465,.300459,-.271185],[-.838689,-.260219,-.516017],[-.711319,.717591,.128359],[-.710334,-.156922,.080946],[-.599799,.556003,-.725148],[-.503838,-.004675,-.969981],[-.487004,.26021,.48049],[-.460089,-.750282,-.512622],[-.376468,.973135,-.325605],[-.331735,-.646985,.084342],[-.254001,.831847,.530001],[-.125239,-.494738,-.966586],[.029622,.027949,.730817],[.056536,-.982543,-.262295],[.08085,1.087391,.076037],[.125583,-.532729,.485984],[.262625,.599586,.780328],[.391387,-.726999,-.716259],[.513854,-.868287,.139347],[.597475,.85513,.326364],[.641224,.109523,.783723],[.737185,-.451155,.538891],[.848705,-.612742,-.314616],[.976075,.365067,.32976],[1.072036,-.19561,.084927]],face:[[15,18,21],[12,20,16],[6,10,2],[3,0,1],[9,7,13],[2,8,4,0],[0,4,5,1],[1,5,11,7],[7,11,17,13],[13,17,22,18],[18,22,24,21],[21,24,23,20],[20,23,19,16],[16,19,14,10],[10,14,8,2],[15,9,13,18],[12,15,21,20],[6,12,16,10],[3,6,2,0],[9,3,1,7],[9,15,12,6,3],[22,17,11,5,4,8,14,19,23,24]]};var c,d,e,f,i,j=a.type&&(a.type<0||a.type>=b.length)?0:a.type||0,k=a.size,o=a.sizeX||k||1,p=a.sizeY||k||1;k=a.sizeZ||k||1;b=a.custom||b[j];j=b.face.length;var q=a.faceUV||new Array(j),G=a.faceColors,H=void 0===a.flat||a.flat,I=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,J=new Array,K=new Array,L=new Array,aa=new Array,M=new Array,N=0,O=0,ba=new Array,ca=0,da=0;if(H)for(da=0;da0&&a.forEach(function(a,c){b._gizmoAxisCache.set(c,a)})},a.prototype.dispose=function(){var a=this;for(var b in this._pointerObservers.forEach(function(b){a.scene.onPointerObservable.remove(b)}),this.gizmos){var c=this.gizmos[b];c&&c.dispose()}this._defaultKeepDepthUtilityLayer!==th.a._DefaultKeepDepthUtilityLayer&&(null===(c=this._defaultKeepDepthUtilityLayer)||void 0===c||c.dispose()),this._defaultUtilityLayer!==th.a._DefaultUtilityLayer&&(null===(b=this._defaultUtilityLayer)||void 0===b||b.dispose()),this.boundingBoxDragBehavior.detach(),this.onAttachedToMeshObservable.clear()},a}(),Kh=c(52),Lh=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._needProjectionMatrixCompute=!0,b}return Object(l.d)(b,a),b.prototype._setPosition=function(a){this._position=a},Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this._setPosition(a)},enumerable:!1,configurable:!0}),b.prototype._setDirection=function(a){this._direction=a},Object.defineProperty(b.prototype,"direction",{get:function(){return this._direction},set:function(a){this._setDirection(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowMinZ",{get:function(){return this._shadowMinZ},set:function(a){this._shadowMinZ=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowMaxZ",{get:function(){return this._shadowMaxZ},set:function(a){this._shadowMaxZ=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),b.prototype.computeTransformedInformation=function(){return!(!this.parent||!this.parent.getWorldMatrix)&&(this.transformedPosition||(this.transformedPosition=g.e.Zero()),g.e.TransformCoordinatesToRef(this.position,this.parent.getWorldMatrix(),this.transformedPosition),this.direction&&(this.transformedDirection||(this.transformedDirection=g.e.Zero()),g.e.TransformNormalToRef(this.direction,this.parent.getWorldMatrix(),this.transformedDirection)),!0)},b.prototype.getDepthScale=function(){return 50},b.prototype.getShadowDirection=function(a){return this.transformedDirection?this.transformedDirection:this.direction},b.prototype.getAbsolutePosition=function(){return this.transformedPosition?this.transformedPosition:this.position},b.prototype.setDirectionToTarget=function(a){return this.direction=g.e.Normalize(a.subtract(this.position)),this.direction},b.prototype.getRotation=function(){this.direction.normalize();var a=g.e.Cross(this.direction,R.a.Y),b=g.e.Cross(a,this.direction);return g.e.RotationFromAxis(a,b,this.direction)},b.prototype.needCube=function(){return!1},b.prototype.needProjectionMatrixCompute=function(){return this._needProjectionMatrixCompute},b.prototype.forceProjectionMatrixCompute=function(){this._needProjectionMatrixCompute=!0},b.prototype._initCache=function(){a.prototype._initCache.call(this),this._cache.position=g.e.Zero()},b.prototype._isSynchronized=function(){return!!this._cache.position.equals(this.position)},b.prototype.computeWorldMatrix=function(a){return!a&&this.isSynchronized()?(this._currentRenderId=this.getScene().getRenderId(),this._worldMatrix):(this._updateCache(),this._cache.position.copyFrom(this.position),this._worldMatrix||(this._worldMatrix=g.a.Identity()),g.a.TranslationToRef(this.position.x,this.position.y,this.position.z,this._worldMatrix),this.parent&&this.parent.getWorldMatrix&&(this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(),this._worldMatrix),this._markSyncedWithParent()),this._worldMatrixDeterminantIsDirty=!0,this._worldMatrix)},b.prototype.getDepthMinZ=function(a){return void 0!==this.shadowMinZ?this.shadowMinZ:a.minZ},b.prototype.getDepthMaxZ=function(a){return void 0!==this.shadowMaxZ?this.shadowMaxZ:a.maxZ},b.prototype.setShadowProjectionMatrix=function(a,b,c){return this.customProjectionMatrixBuilder?this.customProjectionMatrixBuilder(b,c,a):this._setDefaultShadowProjectionMatrix(a,b,c),this},Object(l.c)([Object(J.p)()],b.prototype,"position",null),Object(l.c)([Object(J.p)()],b.prototype,"direction",null),Object(l.c)([Object(J.d)()],b.prototype,"shadowMinZ",null),Object(l.c)([Object(J.d)()],b.prototype,"shadowMaxZ",null),b}(Kh.a);L.a.AddNodeConstructor("Light_Type_1",function(a,b){return function(){return new Mh(a,g.e.Zero(),b)}});var Mh=function(a){function b(b,c,d){b=a.call(this,b,d)||this;return b._shadowFrustumSize=0,b._shadowOrthoScale=.1,b.autoUpdateExtends=!0,b.autoCalcShadowZBounds=!1,b._orthoLeft=Number.MAX_VALUE,b._orthoRight=Number.MIN_VALUE,b._orthoTop=Number.MIN_VALUE,b._orthoBottom=Number.MAX_VALUE,b.position=c.scale(-1),b.direction=c,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"shadowFrustumSize",{get:function(){return this._shadowFrustumSize},set:function(a){this._shadowFrustumSize=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowOrthoScale",{get:function(){return this._shadowOrthoScale},set:function(a){this._shadowOrthoScale=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoLeft",{get:function(){return this._orthoLeft},set:function(a){this._orthoLeft=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoRight",{get:function(){return this._orthoRight},set:function(a){this._orthoRight=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoTop",{get:function(){return this._orthoTop},set:function(a){this._orthoTop=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoBottom",{get:function(){return this._orthoBottom},set:function(a){this._orthoBottom=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"DirectionalLight"},b.prototype.getTypeID=function(){return Kh.a.LIGHTTYPEID_DIRECTIONALLIGHT},b.prototype._setDefaultShadowProjectionMatrix=function(a,b,c){this.shadowFrustumSize>0?this._setDefaultFixedFrustumShadowProjectionMatrix(a):this._setDefaultAutoExtendShadowProjectionMatrix(a,b,c)},b.prototype._setDefaultFixedFrustumShadowProjectionMatrix=function(a){var b=this.getScene().activeCamera;b&&g.a.OrthoLHToRef(this.shadowFrustumSize,this.shadowFrustumSize,void 0!==this.shadowMinZ?this.shadowMinZ:b.minZ,void 0!==this.shadowMaxZ?this.shadowMaxZ:b.maxZ,a,this.getScene().getEngine().isNDCHalfZRange)},b.prototype._setDefaultAutoExtendShadowProjectionMatrix=function(a,b,c){var d=this.getScene().activeCamera;if(d){if(this.autoUpdateExtends||this._orthoLeft===Number.MAX_VALUE){var e=g.e.Zero();this._orthoLeft=Number.MAX_VALUE,this._orthoRight=Number.MIN_VALUE,this._orthoTop=Number.MIN_VALUE,this._orthoBottom=Number.MAX_VALUE;for(var f=Number.MAX_VALUE,h=Number.MIN_VALUE,i=0;ithis._orthoRight&&(this._orthoRight=e.x),e.y>this._orthoTop&&(this._orthoTop=e.y),this.autoCalcShadowZBounds&&(e.zh&&(h=e.z))}this.autoCalcShadowZBounds&&(this._shadowMinZ=f,this._shadowMaxZ=h)}k=this._orthoRight-this._orthoLeft;j=this._orthoTop-this._orthoBottom;i=void 0!==this.shadowMinZ?this.shadowMinZ:d.minZ;c=void 0!==this.shadowMaxZ?this.shadowMaxZ:d.maxZ;e=this.getScene().getEngine().useReverseDepthBuffer;g.a.OrthoOffCenterLHToRef(this._orthoLeft-k*this.shadowOrthoScale,this._orthoRight+k*this.shadowOrthoScale,this._orthoBottom-j*this.shadowOrthoScale,this._orthoTop+j*this.shadowOrthoScale,e?c:i,e?i:c,a,this.getScene().getEngine().isNDCHalfZRange)}},b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("vLightData",4),this._uniformBuffer.addUniform("vLightDiffuse",4),this._uniformBuffer.addUniform("vLightSpecular",4),this._uniformBuffer.addUniform("shadowsInfo",3),this._uniformBuffer.addUniform("depthValues",2),this._uniformBuffer.create()},b.prototype.transferToEffect=function(a,b){return this.computeTransformedInformation()?(this._uniformBuffer.updateFloat4("vLightData",this.transformedDirection.x,this.transformedDirection.y,this.transformedDirection.z,1,b),this):(this._uniformBuffer.updateFloat4("vLightData",this.direction.x,this.direction.y,this.direction.z,1,b),this)},b.prototype.transferToNodeMaterialEffect=function(a,b){return this.computeTransformedInformation()?(a.setFloat3(b,this.transformedDirection.x,this.transformedDirection.y,this.transformedDirection.z),this):(a.setFloat3(b,this.direction.x,this.direction.y,this.direction.z),this)},b.prototype.getDepthMinZ=function(a){a=this._scene.getEngine();return!a.useReverseDepthBuffer&&a.isNDCHalfZRange?0:1},b.prototype.getDepthMaxZ=function(a){a=this._scene.getEngine();return a.useReverseDepthBuffer&&a.isNDCHalfZRange?0:1},b.prototype.prepareLightSpecificDefines=function(a,b){a["DIRLIGHT"+b]=!0},Object(l.c)([Object(J.d)()],b.prototype,"shadowFrustumSize",null),Object(l.c)([Object(J.d)()],b.prototype,"shadowOrthoScale",null),Object(l.c)([Object(J.d)()],b.prototype,"autoUpdateExtends",void 0),Object(l.c)([Object(J.d)()],b.prototype,"autoCalcShadowZBounds",void 0),Object(l.c)([Object(J.d)("orthoLeft")],b.prototype,"_orthoLeft",void 0),Object(l.c)([Object(J.d)("orthoRight")],b.prototype,"_orthoRight",void 0),Object(l.c)([Object(J.d)("orthoTop")],b.prototype,"_orthoTop",void 0),Object(l.c)([Object(J.d)("orthoBottom")],b.prototype,"_orthoBottom",void 0),b}(Lh);function Nh(a){var b=new Array,c=new Array,d=new Array,e=new Array,f=a.radius||.5,g=a.tessellation||64,h=a.arc&&(a.arc<=0||a.arc>1)?1:a.arc||1,i=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE;b.push(0,0,0),e.push(.5,.5);for(var j=2*Math.PI*h,j=1===h?j/g:j/(g-1),k=0,r=0;r1e-4){b=this.attachedMesh.forward;this._light.direction=new g.e(b.x,b.y,b.z),this._cachedForward.copyFrom(this.attachedMesh.forward)}else g.e.DistanceSquared(this.attachedMesh.forward,this._light.direction)>1e-4&&(this.attachedMesh.setDirection(this._light.direction),this.attachedMesh.computeWorldMatrix(!0),this._cachedForward.copyFrom(this.attachedMesh.forward))}},b.prototype.dispose=function(){this.onClickedObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this._material.dispose(),a.prototype.dispose.call(this),this._attachedMeshParent.dispose()},b._CreateHemisphericLightMesh=function(a){var c=new S.a("hemisphereLight",a),d=Qh(c.name,{segments:10,diameter:1},a);return d.position.z=-.15,d.rotation.x=Math.PI/2,d.parent=c,this._CreateLightLines(3,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreatePointLightMesh=function(a){var c=new S.a("pointLight",a),d=Object(vh.a)(c.name,{segments:10,diameter:1},a);return d.rotation.x=Math.PI/2,d.parent=c,this._CreateLightLines(5,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreateSpotLightMesh=function(a){var c=new S.a("spotLight",a);Object(vh.a)(c.name,{segments:10,diameter:1},a).parent=c;var d=Qh(c.name,{segments:10,diameter:2},a);return d.parent=c,d.rotation.x=-Math.PI/2,this._CreateLightLines(2,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreateDirectionalLightMesh=function(a){var c=new S.a("directionalLight",a),d=new S.a(c.name,a);d.parent=c,Object(vh.a)(c.name,{diameter:1.2,segments:10},a).parent=d;var e=Object(xd.a)(c.name,{updatable:!1,height:6,diameterTop:.3,diameterBottom:.3,tessellation:6,subdivisions:1},a);e.parent=d,(f=e.clone(c.name)).scaling.y=.5,f.position.x+=1.25,(e=e.clone(c.name)).scaling.y=.5,e.position.x+=-1.25;var f;a=Object(xd.a)(c.name,{updatable:!1,height:1,diameterTop:0,diameterBottom:.6,tessellation:6,subdivisions:1},a);return a.position.y+=3,a.parent=d,(f=a.clone(c.name)).position.y=1.5,f.position.x+=1.25,(e=a.clone(c.name)).position.y=1.5,e.position.x+=-1.25,d.scaling.scaleInPlace(b._Scale),d.rotation.z=Math.PI/2,d.rotation.y=Math.PI/2,c},b._Scale=.007,b._CreateLightLines=function(a,b){var c=new S.a("root",b);c.rotation.x=Math.PI/2;var d=new S.a("linePivot",b);d.parent=c;b=Object(xd.a)("line",{updatable:!1,height:2,diameterTop:.2,diameterBottom:.3,tessellation:6,subdivisions:1},b);if(b.position.y=b.scaling.y/2+1.2,b.parent=d,a<2)return d;for(b=0;b<4;b++)(e=d.clone("lineParentClone")).rotation.z=Math.PI/4,e.rotation.y=Math.PI/2+Math.PI/2*b,e.getChildMeshes()[0].scaling.y=.5,e.getChildMeshes()[0].scaling.x=e.getChildMeshes()[0].scaling.z=.8,e.getChildMeshes()[0].position.y=e.getChildMeshes()[0].scaling.y/2+1.2;if(a<3)return c;for(b=0;b<4;b++)(e=d.clone("linePivotClone")).rotation.z=Math.PI/2,e.rotation.y=Math.PI/2*b;if(a<4)return c;for(b=0;b<4;b++){var e;(e=d.clone("linePivotClone")).rotation.z=Math.PI+Math.PI/4,e.rotation.y=Math.PI/2+Math.PI/2*b,e.getChildMeshes()[0].scaling.y=.5,e.getChildMeshes()[0].scaling.x=e.getChildMeshes()[0].scaling.z=.8,e.getChildMeshes()[0].position.y=e.getChildMeshes()[0].scaling.y/2+1.2}return a<5||((e=d.clone("linePivotClone")).rotation.z=Math.PI),c},b}(sh.a),Uh=function(a){function b(b){void 0===b&&(b=th.a.DefaultUtilityLayer);var c=a.call(this,b)||this;return c._pointerObserver=null,c.onClickedObservable=new f.c,c._camera=null,c._invProjection=new g.a,c._material=new pd.a("cameraGizmoMaterial",c.gizmoLayer.utilityLayerScene),c._material.diffuseColor=new h.a(.5,.5,.5),c._material.specularColor=new h.a(.1,.1,.1),c._pointerObserver=b.utilityLayerScene.onPointerObservable.add(function(a){c._camera&&(c._isHovered=!(!a.pickInfo||-1==c._rootMesh.getChildMeshes().indexOf(a.pickInfo.pickedMesh)),c._isHovered&&0===a.event.button&&c.onClickedObservable.notifyObservers(c._camera))},Ta.a.POINTERDOWN),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"displayFrustum",{get:function(){return this._cameraLinesMesh.isEnabled()},set:function(a){this._cameraLinesMesh.setEnabled(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"camera",{get:function(){return this._camera},set:function(a){var c=this;if(this._camera=a,this.attachedNode=a,a){this._cameraMesh&&this._cameraMesh.dispose(),this._cameraLinesMesh&&this._cameraLinesMesh.dispose(),this._cameraMesh=b._CreateCameraMesh(this.gizmoLayer.utilityLayerScene),this._cameraLinesMesh=b._CreateCameraFrustum(this.gizmoLayer.utilityLayerScene),this._cameraMesh.getChildMeshes(!1).forEach(function(a){a.material=c._material}),this._cameraMesh.parent=this._rootMesh,this._cameraLinesMesh.parent=this._rootMesh,this.gizmoLayer.utilityLayerScene.activeCamera&&this.gizmoLayer.utilityLayerScene.activeCamera.maxZ<1.5*a.maxZ&&(this.gizmoLayer.utilityLayerScene.activeCamera.maxZ=1.5*a.maxZ),this.attachedNode.reservedDataStore||(this.attachedNode.reservedDataStore={}),this.attachedNode.reservedDataStore.cameraGizmo=this;a=this.gizmoLayer._getSharedGizmoLight();a.includedOnlyMeshes=a.includedOnlyMeshes.concat(this._cameraMesh.getChildMeshes(!1)),this._update()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"material",{get:function(){return this._material},enumerable:!1,configurable:!0}),b.prototype._update=function(){a.prototype._update.call(this),this._camera&&(this._camera.getProjectionMatrix().invertToRef(this._invProjection),this._cameraLinesMesh.setPivotMatrix(this._invProjection,!1),this._cameraLinesMesh.scaling.x=1/this._rootMesh.scaling.x,this._cameraLinesMesh.scaling.y=1/this._rootMesh.scaling.y,this._cameraLinesMesh.scaling.z=1/this._rootMesh.scaling.z,this._cameraMesh.parent=null,this._cameraMesh.rotation.y=.5*Math.PI*(this._camera.getScene().useRightHandedSystem?1:-1),this._cameraMesh.parent=this._rootMesh)},b.prototype.dispose=function(){this.onClickedObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this._cameraMesh&&this._cameraMesh.dispose(),this._cameraLinesMesh&&this._cameraLinesMesh.dispose(),this._material.dispose(),a.prototype.dispose.call(this)},b._CreateCameraMesh=function(a){var c=new S.a("rootCameraGizmo",a),d=new S.a(c.name,a);d.parent=c,Object(rh.b)(c.name,{width:1,height:.8,depth:.5},a).parent=d;var e=Object(xd.a)(c.name,{height:.5,diameterTop:.8,diameterBottom:.8},a);e.parent=d,e.position.y=.3,e.position.x=-.6,e.rotation.x=.5*Math.PI;e=Object(xd.a)(c.name,{height:.5,diameterTop:.6,diameterBottom:.6},a);e.parent=d,e.position.y=.5,e.position.x=.4,e.rotation.x=.5*Math.PI;e=Object(xd.a)(c.name,{height:.5,diameterTop:.5,diameterBottom:.5},a);return e.parent=d,e.position.y=0,e.position.x=.6,e.rotation.z=.5*Math.PI,c.scaling.scaleInPlace(b._Scale),d.position.x=-.9,c},b._CreateCameraFrustum=function(a){var b=new S.a("rootCameraGizmo",a),c=new S.a(b.name,a);c.parent=b;for(var d=0;d<4;d+=2)for(var e=0;e<4;e+=2){var f;(f=Object(wh.e)("lines",{points:[new g.e(-1+e,-1+d,-1),new g.e(-1+e,-1+d,1)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1,(f=Object(wh.e)("lines",{points:[new g.e(-1,-1+e,-1+d),new g.e(1,-1+e,-1+d)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1,(f=Object(wh.e)("lines",{points:[new g.e(-1+e,-1,-1+d),new g.e(-1+e,1,-1+d)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1}return b},b._Scale=.05,b}(sh.a);X.a.IncludesShadersStore.kernelBlurVaryingDeclaration="varying vec2 sampleCoord{X};";rb="vec4 pack(float depth) { const vec4 bit_shift=vec4(255.0*255.0*255.0,255.0*255.0,255.0,1.0); const vec4 bit_mask=vec4(0.0,1.0/255.0,1.0/255.0,1.0/255.0); vec4 res=fract(depth*bit_shift); res-=res.xxyz*bit_mask; return res; } float unpack(vec4 color) { const vec4 bit_shift=vec4(1.0/(255.0*255.0*255.0),1.0/(255.0*255.0),1.0/255.0,1.0); return dot(color,bit_shift); }";X.a.IncludesShadersStore.packingFunctions=rb;a="#ifdef DOF factor=sampleCoC(sampleCoord{X}); computedWeight=KERNEL_WEIGHT{X}*factor; sumOfWeights+=computedWeight; #else computedWeight=KERNEL_WEIGHT{X}; #endif #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCoord{X}))*computedWeight; #else blend+=texture2D(textureSampler,sampleCoord{X})*computedWeight; #endif";X.a.IncludesShadersStore.kernelBlurFragment=a;rb="#ifdef DOF factor=sampleCoC(sampleCenter+delta*KERNEL_DEP_OFFSET{X}); computedWeight=KERNEL_DEP_WEIGHT{X}*factor; sumOfWeights+=computedWeight; #else computedWeight=KERNEL_DEP_WEIGHT{X}; #endif #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCenter+delta*KERNEL_DEP_OFFSET{X}))*computedWeight; #else blend+=texture2D(textureSampler,sampleCenter+delta*KERNEL_DEP_OFFSET{X})*computedWeight; #endif";X.a.IncludesShadersStore.kernelBlurFragment2=rb;a=" uniform sampler2D textureSampler; uniform vec2 delta; varying vec2 sampleCenter; #ifdef DOF uniform sampler2D circleOfConfusionSampler; uniform vec2 cameraMinMaxZ; float sampleDistance(in vec2 offset) { float depth=texture2D(circleOfConfusionSampler,offset).g; return cameraMinMaxZ.x+(cameraMinMaxZ.y-cameraMinMaxZ.x)*depth; } float sampleCoC(in vec2 offset) { float coc=texture2D(circleOfConfusionSampler,offset).r; return coc; } #endif #include[0..varyingCount] #ifdef PACKEDFLOAT #include #endif void main(void) { float computedWeight=0.0; #ifdef PACKEDFLOAT float blend=0.; #else vec4 blend=vec4(0.); #endif #ifdef DOF float sumOfWeights=CENTER_WEIGHT; float factor=0.0; #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCenter))*CENTER_WEIGHT; #else blend+=texture2D(textureSampler,sampleCenter)*CENTER_WEIGHT; #endif #endif #include[0..varyingCount] #include[0..depCount] #ifdef PACKEDFLOAT gl_FragColor=pack(blend); #else gl_FragColor=blend; #endif #ifdef DOF gl_FragColor/=sumOfWeights; #endif }";X.a.ShadersStore.kernelBlurPixelShader=a;X.a.IncludesShadersStore.kernelBlurVertex="sampleCoord{X}=sampleCenter+delta*KERNEL_OFFSET{X};";rb=" attribute vec2 position; uniform vec2 delta; varying vec2 sampleCenter; #include[0..varyingCount] const vec2 madd=vec2(0.5,0.5); void main(void) { sampleCenter=(position*madd+madd); #include[0..varyingCount] gl_Position=vec4(position,0.0,1.0); }";X.a.ShadersStore.kernelBlurVertexShader=rb;var Vh=function(a){function b(b,c,d,e,f,g,h,i,j,k,n){void 0===g&&(g=V.a.BILINEAR_SAMPLINGMODE),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=""),void 0===n&&(n=!1);var o=a.call(this,b,"kernelBlur",["delta","direction","cameraMinMaxZ"],["circleOfConfusionSampler"],e,f,g,h,i,null,j,"kernelBlur",{varyingCount:0,depCount:0},!0)||this;return o.blockCompilation=n,o._packedFloat=!1,o._staticDefines="",o._staticDefines=k,o.direction=c,o.onApplyObservable.add(function(a){o._outputTexture?a.setFloat2("delta",1/o._outputTexture.width*o.direction.x,1/o._outputTexture.height*o.direction.y):a.setFloat2("delta",1/o.width*o.direction.x,1/o.height*o.direction.y)}),o.kernel=d,o}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"kernel",{get:function(){return this._idealKernel},set:function(a){this._idealKernel!==a&&(a=Math.max(a,1),this._idealKernel=a,this._kernel=this._nearestBestKernel(a),this.blockCompilation||this._updateParameters())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"packedFloat",{get:function(){return this._packedFloat},set:function(a){this._packedFloat!==a&&(this._packedFloat=a,this.blockCompilation||this._updateParameters())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"BlurPostProcess"},b.prototype.updateEffect=function(a,b,c,d,e,f){this._updateParameters(e,f)},b.prototype._updateParameters=function(b,c){for(var d=this._kernel,e=(d-1)/2,f=[],g=[],h=0,i=0;i0)return Math.max(d,3)}return Math.max(a,3)},b.prototype._gaussianWeight=function(a){a=-a*a/(1/3*2*(1/3));return 1/(Math.sqrt(2*Math.PI)*(1/3))*Math.exp(a)},b.prototype._glslFloat=function(a,b){return void 0===b&&(b=8),a.toFixed(b).replace(/0+$/,"")},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.direction,a.kernel,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable,a.textureType,void 0,!1)},a,d,e)},Object(l.c)([Object(J.d)("kernel")],b.prototype,"_kernel",void 0),Object(l.c)([Object(J.d)("packedFloat")],b.prototype,"_packedFloat",void 0),Object(l.c)([Object(J.o)()],b.prototype,"direction",void 0),b}(Fc);Object(i.b)("BABYLON.BlurPostProcess",Vh);var Wh=function(a){function b(b,c,d,e,f,h,i){void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===h&&(h=V.a.BILINEAR_SAMPLINGMODE),void 0===i&&(i=!0);var j=a.call(this,b,c,d,e,!0,f,!1,h,i)||this;j.scene=d,j.mirrorPlane=new Hb.a(0,1,0,1),j._transformMatrix=g.a.Zero(),j._mirrorMatrix=g.a.Zero(),j._adaptiveBlurKernel=0,j._blurKernelX=0,j._blurKernelY=0,j._blurRatio=1,j.ignoreCameraViewport=!0,j._updateGammaSpace(),j._imageProcessingConfigChangeObserver=d.imageProcessingConfiguration.onUpdateParameters.add(function(){j._updateGammaSpace()});var k,m=j.getScene().getEngine();return j.onBeforeBindObservable.add(function(){var a;null===(a=m._debugPushGroup)||void 0===a||a.call(m,"mirror generation for "+b,1)}),j.onAfterUnbindObservable.add(function(){var a;null===(a=m._debugPopGroup)||void 0===a||a.call(m,1)}),j.onBeforeRenderObservable.add(function(){g.a.ReflectionToRef(j.mirrorPlane,j._mirrorMatrix),j._mirrorMatrix.multiplyToRef(d.getViewMatrix(),j._transformMatrix),d.setTransformMatrix(j._transformMatrix,d.getProjectionMatrix()),k=d.clipPlane,d.clipPlane=j.mirrorPlane,d.getEngine().cullBackFaces=!1,d._mirroredCameraPosition=g.e.TransformCoordinates(d.activeCamera.globalPosition,j._mirrorMatrix)}),j.onAfterRenderObservable.add(function(){d.updateTransformMatrix(),d.getEngine().cullBackFaces=null,d._mirroredCameraPosition=null,d.clipPlane=k}),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"blurRatio",{get:function(){return this._blurRatio},set:function(a){this._blurRatio!==a&&(this._blurRatio=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"adaptiveBlurKernel",{set:function(a){this._adaptiveBlurKernel=a,this._autoComputeBlurKernel()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernel",{set:function(a){this.blurKernelX=a,this.blurKernelY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernelX",{get:function(){return this._blurKernelX},set:function(a){this._blurKernelX!==a&&(this._blurKernelX=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernelY",{get:function(){return this._blurKernelY},set:function(a){this._blurKernelY!==a&&(this._blurKernelY=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),b.prototype._autoComputeBlurKernel=function(){var a=this.getScene().getEngine(),b=this.getRenderWidth()/a.getRenderWidth();a=this.getRenderHeight()/a.getRenderHeight();this.blurKernelX=this._adaptiveBlurKernel*b,this.blurKernelY=this._adaptiveBlurKernel*a},b.prototype._onRatioRescale=function(){this._sizeRatio&&(this.resize(this._initialSizeParameter),this._adaptiveBlurKernel||this._preparePostProcesses()),this._adaptiveBlurKernel&&this._autoComputeBlurKernel()},b.prototype._updateGammaSpace=function(){this.gammaSpace=!this.scene.imageProcessingConfiguration.isEnabled||!this.scene.imageProcessingConfiguration.applyByPostProcess},b.prototype._preparePostProcesses=function(){if(this.clearPostProcesses(!0),this._blurKernelX&&this._blurKernelY){var a=this.getScene().getEngine(),b=a.getCaps().textureFloatRender&&a.getCaps().textureFloatLinearFiltering?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_HALF_FLOAT;this._blurX=new Vh("horizontal blur",new g.d(1,0),this._blurKernelX,this._blurRatio,null,V.a.BILINEAR_SAMPLINGMODE,a,!1,b),this._blurX.autoClear=!1,1===this._blurRatio&&this.samples<2&&this._texture?this._blurX.inputTexture=this._renderTarget:this._blurX.alwaysForcePOT=!0,this._blurY=new Vh("vertical blur",new g.d(0,1),this._blurKernelY,this._blurRatio,null,V.a.BILINEAR_SAMPLINGMODE,a,!1,b),this._blurY.autoClear=!1,this._blurY.alwaysForcePOT=1!==this._blurRatio,this.addPostProcess(this._blurX),this.addPostProcess(this._blurY)}else this._blurY&&(this.removePostProcess(this._blurY),this._blurY.dispose(),this._blurY=null),this._blurX&&(this.removePostProcess(this._blurX),this._blurX.dispose(),this._blurX=null)},b.prototype.clone=function(){var a=this.getScene();if(!a)return this;var c=this.getSize();c=new b(this.name,c.width,a,this._renderTargetOptions.generateMipMaps,this._renderTargetOptions.type,this._renderTargetOptions.samplingMode,this._renderTargetOptions.generateDepthBuffer);return c.hasAlpha=this.hasAlpha,c.level=this.level,c.mirrorPlane=this.mirrorPlane.clone(),this.renderList&&(c.renderList=this.renderList.slice(0)),c},b.prototype.serialize=function(){if(!this.name)return null;var b=a.prototype.serialize.call(this);return b.mirrorPlane=this.mirrorPlane.asArray(),b},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.scene.imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingConfigChangeObserver)},b}(cd);V.a._CreateMirror=function(a,b,c,d){return new Wh(a,b,c,d)};var Xh=function(a){function b(b,c,d,e,h,i,j,k,t,u,v,w,x,y,z){void 0===d&&(d=null),void 0===e&&(e=!1),void 0===h&&(h=null),void 0===i&&(i=null),void 0===j&&(j=null),void 0===k&&(k=r.a.TEXTUREFORMAT_RGBA),void 0===t&&(t=!1),void 0===u&&(u=null),void 0===v&&(v=!1),void 0===w&&(w=.8),void 0===x&&(x=0);c=a.call(this,c)||this;return c._lodScale=.8,c._lodOffset=0,c.onLoadObservable=new f.c,c.boundingBoxPosition=g.e.Zero(),c._rotationY=0,c._files=null,c._forcedExtension=null,c._extensions=null,c.name=b,c.url=b,c._noMipmap=e,c.hasAlpha=!1,c._format=k,c.isCube=!0,c._textureMatrix=g.a.Identity(),c._createPolynomials=v,c.coordinatesMode=V.a.CUBIC_MODE,c._extensions=d,c._files=h,c._forcedExtension=u,c._loaderOptions=y,c._useSRGBBuffer=z,c._lodScale=w,c._lodOffset=x,b||h?(c.updateURL(b,u,i,t,j,d,null===(e=c.getScene())||void 0===e?void 0:e.useDelayedTextureLoading,h),c):c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"boundingBoxSize",{get:function(){return this._boundingBoxSize},set:function(a){if(!this._boundingBoxSize||!this._boundingBoxSize.equals(a)){this._boundingBoxSize=a;a=this.getScene();a&&a.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag)}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotationY",{get:function(){return this._rotationY},set:function(a){this._rotationY=a,this.setReflectionTextureMatrix(g.a.RotationY(this._rotationY))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"noMipmap",{get:function(){return this._noMipmap},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcedExtension",{get:function(){return this._forcedExtension},enumerable:!1,configurable:!0}),b.CreateFromImages=function(a,c,d){var e="";return a.forEach(function(a){return e+=a}),new b(e,c,null,d,a)},b.CreateFromPrefilteredData=function(a,c,d,e){void 0===d&&(d=null),void 0===e&&(e=!0);var f=c.useDelayedTextureLoading;c.useDelayedTextureLoading=!1;a=new b(a,c,null,!1,null,null,null,void 0,!0,d,e);return c.useDelayedTextureLoading=f,a},b.prototype.getClassName=function(){return"CubeTexture"},b.prototype.updateURL=function(a,b,c,d,e,f,g,h){void 0===c&&(c=null),void 0===d&&(d=!1),void 0===e&&(e=null),void 0===f&&(f=null),void 0===g&&(g=!1),void 0===h&&(h=null),this.name&&!Object(gh.g)(this.name,"data:")||(this.name=a),this.url=a;var i=a.lastIndexOf(".");b=b||(i>-1?a.substring(i).toLowerCase():"");i=0===b.indexOf(".dds");b=0===b.indexOf(".env");if(b?(this.gammaSpace=!1,this._prefiltered=!1,this.anisotropicFilteringLevel=1):(this._prefiltered=d,d&&(this.gammaSpace=!1,this.anisotropicFilteringLevel=1)),h)this._files=h;else if(b||i||f||(f=["_px.jpg","_py.jpg","_pz.jpg","_nx.jpg","_ny.jpg","_nz.jpg"]),this._files=this._files||[],this._files.length=0,f){for(d=0;d ";X.a.IncludesShadersStore.backgroundUboDeclaration=rb;c(153),c(150),c(151),c(181),c(152),c(122),c(143),c(109),c(157),c(158);a="#ifdef TEXTURELODSUPPORT #extension GL_EXT_shader_texture_lod : enable #endif precision highp float; #include<__decl__backgroundFragment> #include #define RECIPROCAL_PI2 0.15915494 varying vec3 vPositionW; #ifdef MAINUV1 varying vec2 vMainUV1; #endif #ifdef MAINUV2 varying vec2 vMainUV2; #endif #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef DIFFUSE #if DIFFUSEDIRECTUV == 1 #define vDiffuseUV vMainUV1 #elif DIFFUSEDIRECTUV == 2 #define vDiffuseUV vMainUV2 #else varying vec2 vDiffuseUV; #endif uniform sampler2D diffuseSampler; #endif #ifdef REFLECTION #ifdef REFLECTIONMAP_3D #define sampleReflection(s,c) textureCube(s,c) uniform samplerCube reflectionSampler; #ifdef TEXTURELODSUPPORT #define sampleReflectionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #else #define sampleReflection(s,c) texture2D(s,c) uniform sampler2D reflectionSampler; #ifdef TEXTURELODSUPPORT #define sampleReflectionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #include #endif #ifndef FROMLINEARSPACE #define FROMLINEARSPACE; #endif #ifndef SHADOWONLY #define SHADOWONLY; #endif #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include #include #include #ifdef REFLECTIONFRESNEL #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 vec3 fresnelSchlickEnvironmentGGX(float VdotN,vec3 reflectance0,vec3 reflectance90,float smoothness) { float weight=mix(FRESNEL_MAXIMUM_ON_ROUGH,1.0,smoothness); return reflectance0+weight*(reflectance90-reflectance0)*pow5(saturate(1.0-VdotN)); } #endif void main(void) { #include vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=vec3(0.0,1.0,0.0); #endif float shadow=1.; float globalShadow=0.; float shadowLightCount=0.; #include[0..maxSimultaneousLights] #ifdef SHADOWINUSE globalShadow/=shadowLightCount; #else globalShadow=1.0; #endif #ifndef BACKMAT_SHADOWONLY vec4 reflectionColor=vec4(1.,1.,1.,1.); #ifdef REFLECTION vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D vec3 reflectionCoords=reflectionVector; #else vec2 reflectionCoords=reflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION reflectionCoords/=reflectionVector.z; #endif reflectionCoords.y=1.0-reflectionCoords.y; #endif #ifdef REFLECTIONBLUR float reflectionLOD=vReflectionInfos.y; #ifdef TEXTURELODSUPPORT reflectionLOD=reflectionLOD*log2(vReflectionMicrosurfaceInfos.x)*vReflectionMicrosurfaceInfos.y+vReflectionMicrosurfaceInfos.z; reflectionColor=sampleReflectionLod(reflectionSampler,reflectionCoords,reflectionLOD); #else float lodReflectionNormalized=saturate(reflectionLOD); float lodReflectionNormalizedDoubled=lodReflectionNormalized*2.0; vec4 reflectionSpecularMid=sampleReflection(reflectionSampler,reflectionCoords); if(lodReflectionNormalizedDoubled<1.0){ reflectionColor=mix( sampleReflection(reflectionSamplerHigh,reflectionCoords), reflectionSpecularMid, lodReflectionNormalizedDoubled ); } else { reflectionColor=mix( reflectionSpecularMid, sampleReflection(reflectionSamplerLow,reflectionCoords), lodReflectionNormalizedDoubled-1.0 ); } #endif #else vec4 reflectionSample=sampleReflection(reflectionSampler,reflectionCoords); reflectionColor=reflectionSample; #endif #ifdef RGBDREFLECTION reflectionColor.rgb=fromRGBD(reflectionColor); #endif #ifdef GAMMAREFLECTION reflectionColor.rgb=toLinearSpace(reflectionColor.rgb); #endif #ifdef REFLECTIONBGR reflectionColor.rgb=reflectionColor.bgr; #endif reflectionColor.rgb*=vReflectionInfos.x; #endif vec3 diffuseColor=vec3(1.,1.,1.); float finalAlpha=alpha; #ifdef DIFFUSE vec4 diffuseMap=texture2D(diffuseSampler,vDiffuseUV); #ifdef GAMMADIFFUSE diffuseMap.rgb=toLinearSpace(diffuseMap.rgb); #endif diffuseMap.rgb*=vDiffuseInfos.y; #ifdef DIFFUSEHASALPHA finalAlpha*=diffuseMap.a; #endif diffuseColor=diffuseMap.rgb; #endif #ifdef REFLECTIONFRESNEL vec3 colorBase=diffuseColor; #else vec3 colorBase=reflectionColor.rgb*diffuseColor; #endif colorBase=max(colorBase,0.0); #ifdef USERGBCOLOR vec3 finalColor=colorBase; #else #ifdef USEHIGHLIGHTANDSHADOWCOLORS vec3 mainColor=mix(vPrimaryColorShadow.rgb,vPrimaryColor.rgb,colorBase); #else vec3 mainColor=vPrimaryColor.rgb; #endif vec3 finalColor=colorBase*mainColor; #endif #ifdef REFLECTIONFRESNEL vec3 reflectionAmount=vReflectionControl.xxx; vec3 reflectionReflectance0=vReflectionControl.yyy; vec3 reflectionReflectance90=vReflectionControl.zzz; float VdotN=dot(normalize(vEyePosition.xyz),normalW); vec3 planarReflectionFresnel=fresnelSchlickEnvironmentGGX(saturate(VdotN),reflectionReflectance0,reflectionReflectance90,1.0); reflectionAmount*=planarReflectionFresnel; #ifdef REFLECTIONFALLOFF float reflectionDistanceFalloff=1.0-saturate(length(vPositionW.xyz-vBackgroundCenter)*vReflectionControl.w); reflectionDistanceFalloff*=reflectionDistanceFalloff; reflectionAmount*=reflectionDistanceFalloff; #endif finalColor=mix(finalColor,reflectionColor.rgb,saturate(reflectionAmount)); #endif #ifdef OPACITYFRESNEL float viewAngleToFloor=dot(normalW,normalize(vEyePosition.xyz-vBackgroundCenter)); const float startAngle=0.1; float fadeFactor=saturate(viewAngleToFloor/startAngle); finalAlpha*=fadeFactor*fadeFactor; #endif #ifdef SHADOWINUSE finalColor=mix(finalColor*shadowLevel,finalColor,globalShadow); #endif vec4 color=vec4(finalColor,finalAlpha); #else vec4 color=vec4(vPrimaryColor.rgb,(1.0-clamp(globalShadow,0.,1.))*alpha); #endif #include #ifdef IMAGEPROCESSINGPOSTPROCESS color.rgb=clamp(color.rgb,0.,30.0); #else color=applyImageProcessing(color); #endif #ifdef PREMULTIPLYALPHA color.rgb*=color.a; #endif #ifdef NOISE color.rgb+=dither(vPositionW.xy,0.5); color=max(color,0.0); #endif gl_FragColor=color; } ";X.a.ShadersStore.backgroundPixelShader=a;rb="uniform mat4 view; uniform mat4 viewProjection; uniform float shadowLevel; #ifdef DIFFUSE uniform mat4 diffuseMatrix; uniform vec2 vDiffuseInfos; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; uniform mat4 reflectionMatrix; uniform vec3 vReflectionMicrosurfaceInfos; uniform float fFovMultiplier; #endif #ifdef POINTSIZE uniform float pointSize; #endif";X.a.IncludesShadersStore.backgroundVertexDeclaration=rb;c(87),c(92),c(123),c(159),c(160),c(161),c(88),c(89),c(112),c(185),c(162);a="precision highp float; #include<__decl__backgroundVertex> #include attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #include #include varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #ifdef MAINUV1 varying vec2 vMainUV1; #endif #ifdef MAINUV2 varying vec2 vMainUV2; #endif #if defined(DIFFUSE) && DIFFUSEDIRECTUV == 0 varying vec2 vDiffuseUV; #endif #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif void main(void) { #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=position; #endif #include #include #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*finalWorld*vec4(position,1.0); } else { gl_Position=viewProjectionR*finalWorld*vec4(position,1.0); } #else gl_Position=viewProjection*finalWorld*vec4(position,1.0); #endif vec4 worldPos=finalWorld*vec4(position,1.0); vPositionW=vec3(worldPos); #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normal); #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(position,0.0))); #ifdef EQUIRECTANGULAR_RELFECTION_FOV mat3 screenToWorld=inverseMat3(mat3(finalWorld*viewProjection)); vec3 segment=mix(vDirectionW,screenToWorld*vec3(0.0,0.0,1.0),abs(fFovMultiplier-1.0)); if (fFovMultiplier<=1.0) { vDirectionW=normalize(segment); } else { vDirectionW=normalize(vDirectionW+(vDirectionW-segment)); } #endif #endif #ifndef UV1 vec2 uv=vec2(0.,0.); #endif #ifndef UV2 vec2 uv2=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uv; #endif #ifdef MAINUV2 vMainUV2=uv2; #endif #if defined(DIFFUSE) && DIFFUSEDIRECTUV == 0 if (vDiffuseInfos.x == 0.) { vDiffuseUV=vec2(diffuseMatrix*vec4(uv,1.0,0.0)); } else { vDiffuseUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); } #endif #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #ifdef POINTSIZE gl_PointSize=pointSize; #endif } ";X.a.ShadersStore.backgroundVertexShader=a;var bi=c(74),ci=function(a){function b(){var b=a.call(this)||this;return b.DIFFUSE=!1,b.DIFFUSEDIRECTUV=0,b.GAMMADIFFUSE=!1,b.DIFFUSEHASALPHA=!1,b.OPACITYFRESNEL=!1,b.REFLECTIONBLUR=!1,b.REFLECTIONFRESNEL=!1,b.REFLECTIONFALLOFF=!1,b.TEXTURELODSUPPORT=!1,b.PREMULTIPLYALPHA=!1,b.USERGBCOLOR=!1,b.USEHIGHLIGHTANDSHADOWCOLORS=!1,b.BACKMAT_SHADOWONLY=!1,b.NOISE=!1,b.REFLECTIONBGR=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.EXPOSURE=!1,b.MULTIVIEW=!1,b.REFLECTION=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.INVERTCUBICMAP=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.LODINREFLECTIONALPHA=!1,b.GAMMAREFLECTION=!1,b.RGBDREFLECTION=!1,b.EQUIRECTANGULAR_RELFECTION_FOV=!1,b.MAINUV1=!1,b.MAINUV2=!1,b.UV1=!1,b.UV2=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.POINTSIZE=!1,b.FOG=!1,b.NORMAL=!1,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.INSTANCES=!1,b.SHADOWFLOAT=!1,b.LOGARITHMICDEPTH=!1,b.NONUNIFORMSCALING=!1,b.ALPHATEST=!1,b.rebuild(),b}return Object(l.d)(b,a),b}(Zh.a),di=function(a){function b(b,c){var d=a.call(this,b,c)||this;return d.primaryColor=h.a.White(),d._primaryColorShadowLevel=0,d._primaryColorHighlightLevel=0,d.reflectionTexture=null,d.reflectionBlur=0,d.diffuseTexture=null,d._shadowLights=null,d.shadowLights=null,d.shadowLevel=0,d.sceneCenter=g.e.Zero(),d.opacityFresnel=!0,d.reflectionFresnel=!1,d.reflectionFalloffDistance=0,d.reflectionAmount=1,d.reflectionReflectance0=.05,d.reflectionReflectance90=.5,d.useRGBColor=!0,d.enableNoise=!1,d._fovMultiplier=1,d.useEquirectangularFOV=!1,d._maxSimultaneousLights=4,d.maxSimultaneousLights=4,d._shadowOnly=!1,d.shadowOnly=!1,d._imageProcessingObserver=null,d.switchToBGR=!1,d._renderTargets=new Ac.a(16),d._reflectionControls=g.f.Zero(),d._white=h.a.White(),d._primaryShadowColor=h.a.Black(),d._primaryHighlightColor=h.a.Black(),d._attachImageProcessingConfiguration(null),d.getRenderTargetTextures=function(){return d._renderTargets.reset(),d._diffuseTexture&&d._diffuseTexture.isRenderTarget&&d._renderTargets.push(d._diffuseTexture),d._reflectionTexture&&d._reflectionTexture.isRenderTarget&&d._renderTargets.push(d._reflectionTexture),d._renderTargets},d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"_perceptualColor",{get:function(){return this.__perceptualColor},set:function(a){this.__perceptualColor=a,this._computePrimaryColorFromPerceptualColor(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"primaryColorShadowLevel",{get:function(){return this._primaryColorShadowLevel},set:function(a){this._primaryColorShadowLevel=a,this._computePrimaryColors(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"primaryColorHighlightLevel",{get:function(){return this._primaryColorHighlightLevel},set:function(a){this._primaryColorHighlightLevel=a,this._computePrimaryColors(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"reflectionStandardFresnelWeight",{set:function(a){a=a;a<.5?(a*=2,this.reflectionReflectance0=b.StandardReflectance0*a,this.reflectionReflectance90=b.StandardReflectance90*a):(a=2*a-1,this.reflectionReflectance0=b.StandardReflectance0+(1-b.StandardReflectance0)*a,this.reflectionReflectance90=b.StandardReflectance90+(1-b.StandardReflectance90)*a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fovMultiplier",{get:function(){return this._fovMultiplier},set:function(a){isNaN(a)&&(a=1),this._fovMultiplier=Math.max(0,Math.min(2,a))},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a){var b=this;a!==this._imageProcessingConfiguration&&(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration=a||this.getScene().imageProcessingConfiguration,this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){b._computePrimaryColorFromPerceptualColor(),b._markAllSubMeshesAsImageProcessingDirty()})))},Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a),this._markAllSubMeshesAsTexturesDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraToneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraExposure",{get:function(){return this._imageProcessingConfiguration.exposure},set:function(a){this._imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraContrast",{get:function(){return this._imageProcessingConfiguration.contrast},set:function(a){this._imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingTexture",{get:function(){return this._imageProcessingConfiguration.colorGradingTexture},set:function(a){this.imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurves",{get:function(){return this.imageProcessingConfiguration.colorCurves},set:function(a){this.imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasRenderTargetTextures",{get:function(){return!(!this._diffuseTexture||!this._diffuseTexture.isRenderTarget)||!(!this._reflectionTexture||!this._reflectionTexture.isRenderTarget)},enumerable:!1,configurable:!0}),b.prototype.needAlphaTesting=function(){return!0},b.prototype.needAlphaBlending=function(){return this.alpha<1||null!=this._diffuseTexture&&this._diffuseTexture.hasAlpha||this._shadowOnly},b.prototype.isReadyForSubMesh=function(a,b,c){var d=this;if(void 0===c&&(c=!1),b.effect&&this.isFrozen&&b.effect._wasPreviouslyReady)return!0;b._materialDefines||(b.materialDefines=new ci);var e=this.getScene(),f=b._materialDefines;if(this._isReadyForSubMesh(b))return!0;var g=e.getEngine();if(Yh.a.PrepareDefinesForLights(e,a,f,!1,this._maxSimultaneousLights),f._needNormals=!0,Yh.a.PrepareDefinesForMultiview(e,f),f._areTexturesDirty){if(f._needUVs=!1,e.texturesEnabled){if(e.getEngine().getCaps().textureLOD&&(f.TEXTURELODSUPPORT=!0),this._diffuseTexture&&ai.a.DiffuseTextureEnabled){if(!this._diffuseTexture.isReadyOrNotBlocking())return!1;Yh.a.PrepareDefinesForMergedUV(this._diffuseTexture,f,"DIFFUSE"),f.DIFFUSEHASALPHA=this._diffuseTexture.hasAlpha,f.GAMMADIFFUSE=this._diffuseTexture.gammaSpace,f.OPACITYFRESNEL=this._opacityFresnel}else f.DIFFUSE=!1,f.DIFFUSEHASALPHA=!1,f.GAMMADIFFUSE=!1,f.OPACITYFRESNEL=!1;var h=this._reflectionTexture;if(h&&ai.a.ReflectionTextureEnabled){if(!h.isReadyOrNotBlocking())return!1;switch(f.REFLECTION=!0,f.GAMMAREFLECTION=h.gammaSpace,f.RGBDREFLECTION=h.isRGBD,f.REFLECTIONBLUR=this._reflectionBlur>0,f.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!h.invertZ:h.invertZ,f.LODINREFLECTIONALPHA=h.lodLevelInAlpha,f.EQUIRECTANGULAR_RELFECTION_FOV=this.useEquirectangularFOV,f.REFLECTIONBGR=this.switchToBGR,h.coordinatesMode===V.a.INVCUBIC_MODE&&(f.INVERTCUBICMAP=!0),f.REFLECTIONMAP_3D=h.isCube,h.coordinatesMode){case V.a.EXPLICIT_MODE:f.REFLECTIONMAP_EXPLICIT=!0;break;case V.a.PLANAR_MODE:f.REFLECTIONMAP_PLANAR=!0;break;case V.a.PROJECTION_MODE:f.REFLECTIONMAP_PROJECTION=!0;break;case V.a.SKYBOX_MODE:f.REFLECTIONMAP_SKYBOX=!0;break;case V.a.SPHERICAL_MODE:f.REFLECTIONMAP_SPHERICAL=!0;break;case V.a.EQUIRECTANGULAR_MODE:f.REFLECTIONMAP_EQUIRECTANGULAR=!0;break;case V.a.FIXED_EQUIRECTANGULAR_MODE:f.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!0;break;case V.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:f.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!0;break;case V.a.CUBIC_MODE:case V.a.INVCUBIC_MODE:default:f.REFLECTIONMAP_CUBIC=!0}this.reflectionFresnel?(f.REFLECTIONFRESNEL=!0,f.REFLECTIONFALLOFF=this.reflectionFalloffDistance>0,this._reflectionControls.x=this.reflectionAmount,this._reflectionControls.y=this.reflectionReflectance0,this._reflectionControls.z=this.reflectionReflectance90,this._reflectionControls.w=1/this.reflectionFalloffDistance):(f.REFLECTIONFRESNEL=!1,f.REFLECTIONFALLOFF=!1)}else f.REFLECTION=!1,f.REFLECTIONFRESNEL=!1,f.REFLECTIONFALLOFF=!1,f.REFLECTIONBLUR=!1,f.REFLECTIONMAP_3D=!1,f.REFLECTIONMAP_SPHERICAL=!1,f.REFLECTIONMAP_PLANAR=!1,f.REFLECTIONMAP_CUBIC=!1,f.REFLECTIONMAP_PROJECTION=!1,f.REFLECTIONMAP_SKYBOX=!1,f.REFLECTIONMAP_EXPLICIT=!1,f.REFLECTIONMAP_EQUIRECTANGULAR=!1,f.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,f.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,f.INVERTCUBICMAP=!1,f.REFLECTIONMAP_OPPOSITEZ=!1,f.LODINREFLECTIONALPHA=!1,f.GAMMAREFLECTION=!1,f.RGBDREFLECTION=!1}f.PREMULTIPLYALPHA=this.alphaMode===r.a.ALPHA_PREMULTIPLIED||this.alphaMode===r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,f.USERGBCOLOR=this._useRGBColor,f.NOISE=this._enableNoise}if(f._areLightsDirty&&(f.USEHIGHLIGHTANDSHADOWCOLORS=!this._useRGBColor&&(0!==this._primaryColorShadowLevel||0!==this._primaryColorHighlightLevel),f.BACKMAT_SHADOWONLY=this._shadowOnly),f._areImageProcessingDirty&&this._imageProcessingConfiguration){if(!this._imageProcessingConfiguration.isReady())return!1;this._imageProcessingConfiguration.prepareDefines(f)}if(Yh.a.PrepareDefinesForMisc(a,e,!1,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a),f),Yh.a.PrepareDefinesForFrameBoundValues(e,g,f,c,null,b.getRenderingMesh().hasThinInstances),Yh.a.PrepareDefinesForAttributes(a,f,!1,!0,!1)&&a&&(e.getEngine().getCaps().standardDerivatives||a.isVerticesDataPresent(W.b.NormalKind)||(a.createNormals(!0),q.a.Warn("BackgroundMaterial: Normals have been created for the mesh: "+a.name))),f.isDirty){f.markAsProcessed(),e.resetCachedMaterial();h=new bi.a;f.FOG&&h.addFallback(0,"FOG"),f.POINTSIZE&&h.addFallback(1,"POINTSIZE"),f.MULTIVIEW&&h.addFallback(0,"MULTIVIEW"),Yh.a.HandleFallbacksForShadows(f,h,this._maxSimultaneousLights);c=[W.b.PositionKind];f.NORMAL&&c.push(W.b.NormalKind),f.UV1&&c.push(W.b.UVKind),f.UV2&&c.push(W.b.UV2Kind),Yh.a.PrepareAttributesForBones(c,a,f,h),Yh.a.PrepareAttributesForInstances(c,f);a=["world","view","viewProjection","vEyePosition","vLightsType","vFogInfos","vFogColor","pointSize","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","mBones","vPrimaryColor","vPrimaryColorShadow","vReflectionInfos","reflectionMatrix","vReflectionMicrosurfaceInfos","fFovMultiplier","shadowLevel","alpha","vBackgroundCenter","vReflectionControl","vDiffuseInfos","diffuseMatrix"];var i=["diffuseSampler","reflectionSampler","reflectionSamplerLow","reflectionSamplerHigh"],j=["Material","Scene"];od.a&&(od.a.PrepareUniforms(a,f),od.a.PrepareSamplers(i,f)),Yh.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:f,maxSimultaneousLights:this._maxSimultaneousLights});var k=f.toString();c=e.getEngine().createEffect("background",{attributes:c,uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:k,fallbacks:h,onCompiled:function(a){d.onCompiled&&d.onCompiled(a),Yh.a.BindSceneUniformBuffer(a,e.getSceneUniformBuffer())},onError:this.onError,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights}},g);b.setEffect(c,f,this._materialContext),this.buildUniformLayout()}return!(!b.effect||!b.effect.isReady())&&(f._renderId=e.getRenderId(),b.effect._wasPreviouslyReady=!0,!0)},b.prototype._computePrimaryColorFromPerceptualColor=function(){this.__perceptualColor&&(this._primaryColor.copyFrom(this.__perceptualColor),this._primaryColor.toLinearSpaceToRef(this._primaryColor),this._imageProcessingConfiguration&&this._primaryColor.scaleToRef(1/this._imageProcessingConfiguration.exposure,this._primaryColor),this._computePrimaryColors())},b.prototype._computePrimaryColors=function(){0===this._primaryColorShadowLevel&&0===this._primaryColorHighlightLevel||(this._primaryColor.scaleToRef(this._primaryColorShadowLevel,this._primaryShadowColor),this._primaryColor.subtractToRef(this._primaryShadowColor,this._primaryShadowColor),this._white.subtractToRef(this._primaryColor,this._primaryHighlightColor),this._primaryHighlightColor.scaleToRef(this._primaryColorHighlightLevel,this._primaryHighlightColor),this._primaryColor.addToRef(this._primaryHighlightColor,this._primaryHighlightColor))},b.prototype.buildUniformLayout=function(){this._uniformBuffer.addUniform("vPrimaryColor",4),this._uniformBuffer.addUniform("vPrimaryColorShadow",4),this._uniformBuffer.addUniform("vDiffuseInfos",2),this._uniformBuffer.addUniform("vReflectionInfos",2),this._uniformBuffer.addUniform("diffuseMatrix",16),this._uniformBuffer.addUniform("reflectionMatrix",16),this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos",3),this._uniformBuffer.addUniform("fFovMultiplier",1),this._uniformBuffer.addUniform("pointSize",1),this._uniformBuffer.addUniform("shadowLevel",1),this._uniformBuffer.addUniform("alpha",1),this._uniformBuffer.addUniform("vBackgroundCenter",3),this._uniformBuffer.addUniform("vReflectionControl",4),this._uniformBuffer.create()},b.prototype.unbind=function(){this._diffuseTexture&&this._diffuseTexture.isRenderTarget&&this._uniformBuffer.setTexture("diffuseSampler",null),this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&this._uniformBuffer.setTexture("reflectionSampler",null),a.prototype.unbind.call(this)},b.prototype.bindOnlyWorldMatrix=function(a){this._activeEffect.setMatrix("world",a)},b.prototype.bindForSubMesh=function(a,b,c){var d=this.getScene(),e=c._materialDefines;if(e){c=c.effect;if(c){this._activeEffect=c,this.bindOnlyWorldMatrix(a),Yh.a.BindBonesParameters(b,this._activeEffect);a=this._mustRebind(d,c,b.visibility);if(a){this._uniformBuffer.bindToEffect(c,"Material"),this.bindViewProjection(c);var f=this._reflectionTexture;this._uniformBuffer.useUbo&&this.isFrozen&&this._uniformBuffer.isSync||(d.texturesEnabled&&(this._diffuseTexture&&ai.a.DiffuseTextureEnabled&&(this._uniformBuffer.updateFloat2("vDiffuseInfos",this._diffuseTexture.coordinatesIndex,this._diffuseTexture.level),Yh.a.BindTextureMatrix(this._diffuseTexture,this._uniformBuffer,"diffuse")),f&&ai.a.ReflectionTextureEnabled&&(this._uniformBuffer.updateMatrix("reflectionMatrix",f.getReflectionTextureMatrix()),this._uniformBuffer.updateFloat2("vReflectionInfos",f.level,this._reflectionBlur),this._uniformBuffer.updateFloat3("vReflectionMicrosurfaceInfos",f.getSize().width,f.lodGenerationScale,f.lodGenerationOffset))),this.shadowLevel>0&&this._uniformBuffer.updateFloat("shadowLevel",this.shadowLevel),this._uniformBuffer.updateFloat("alpha",this.alpha),this.pointsCloud&&this._uniformBuffer.updateFloat("pointSize",this.pointSize),e.USEHIGHLIGHTANDSHADOWCOLORS?(this._uniformBuffer.updateColor4("vPrimaryColor",this._primaryHighlightColor,1),this._uniformBuffer.updateColor4("vPrimaryColorShadow",this._primaryShadowColor,1)):this._uniformBuffer.updateColor4("vPrimaryColor",this._primaryColor,1)),this._uniformBuffer.updateFloat("fFovMultiplier",this._fovMultiplier),d.texturesEnabled&&(this._diffuseTexture&&ai.a.DiffuseTextureEnabled&&this._uniformBuffer.setTexture("diffuseSampler",this._diffuseTexture),f&&ai.a.ReflectionTextureEnabled&&(e.REFLECTIONBLUR&&e.TEXTURELODSUPPORT?this._uniformBuffer.setTexture("reflectionSampler",f):e.REFLECTIONBLUR?(this._uniformBuffer.setTexture("reflectionSampler",f._lodTextureMid||f),this._uniformBuffer.setTexture("reflectionSamplerLow",f._lodTextureLow||f),this._uniformBuffer.setTexture("reflectionSamplerHigh",f._lodTextureHigh||f)):this._uniformBuffer.setTexture("reflectionSampler",f),e.REFLECTIONFRESNEL&&(this._uniformBuffer.updateFloat3("vBackgroundCenter",this.sceneCenter.x,this.sceneCenter.y,this.sceneCenter.z),this._uniformBuffer.updateFloat4("vReflectionControl",this._reflectionControls.x,this._reflectionControls.y,this._reflectionControls.z,this._reflectionControls.w)))),Yh.a.BindClipPlane(this._activeEffect,d),d.bindEyePosition(c)}!a&&this.isFrozen||(d.lightsEnabled&&Yh.a.BindLights(d,b,this._activeEffect,e,this._maxSimultaneousLights),this.bindView(c),Yh.a.BindFogParameters(d,b,this._activeEffect,!0),this._imageProcessingConfiguration&&this._imageProcessingConfiguration.bind(this._activeEffect)),this._afterBind(b,this._activeEffect),this._uniformBuffer.update()}}},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._reflectionTexture===b||this._diffuseTexture===b},b.prototype.dispose=function(b,c){void 0===b&&(b=!1),void 0===c&&(c=!1),c&&(this.diffuseTexture&&this.diffuseTexture.dispose(),this.reflectionTexture&&this.reflectionTexture.dispose()),this._renderTargets.dispose(),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b)},b.prototype.clone=function(a){var c=this;return J.a.Clone(function(){return new b(a,c.getScene())},this)},b.prototype.serialize=function(){var a=J.a.Serialize(this);return a.customType="BABYLON.BackgroundMaterial",a},b.prototype.getClassName=function(){return"BackgroundMaterial"},b.Parse=function(a,c,d){return J.a.Parse(function(){return new b(a.name,c)},a,c,d)},b.StandardReflectance0=.05,b.StandardReflectance90=.5,Object(l.c)([Object(J.f)()],b.prototype,"_primaryColor",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"primaryColor",void 0),Object(l.c)([Object(J.f)()],b.prototype,"__perceptualColor",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_primaryColorShadowLevel",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_primaryColorHighlightLevel",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"primaryColorHighlightLevel",null),Object(l.c)([Object(J.n)()],b.prototype,"_reflectionTexture",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionBlur",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionBlur",void 0),Object(l.c)([Object(J.n)()],b.prototype,"_diffuseTexture",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"diffuseTexture",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"shadowLights",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_shadowLevel",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"shadowLevel",void 0),Object(l.c)([Object(J.p)()],b.prototype,"_sceneCenter",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"sceneCenter",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_opacityFresnel",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"opacityFresnel",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionFresnel",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionFresnel",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionFalloffDistance",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionFalloffDistance",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionAmount",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionAmount",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionReflectance0",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionReflectance0",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_reflectionReflectance90",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionReflectance90",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_useRGBColor",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRGBColor",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_enableNoise",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"enableNoise",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_maxSimultaneousLights",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_shadowOnly",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"shadowOnly",void 0),Object(l.c)([Object(J.j)()],b.prototype,"_imageProcessingConfiguration",void 0),b}($h.a);Object(i.b)("BABYLON.BackgroundMaterial",di);var ei=function(){function a(b,c){var d=this;this._errorHandler=function(a,b){d.onErrorObservable.notifyObservers({message:a,exception:b})},this._options=Object(l.a)(Object(l.a)({},a._getDefaultOptions()),b),this._scene=c,this.onErrorObservable=new f.c,this._setupBackground(),this._setupImageProcessing()}return a._getDefaultOptions=function(){return{createGround:!0,groundSize:15,groundTexture:this._groundTextureCDNUrl,groundColor:new h.a(.2,.2,.3).toLinearSpace().scale(3),groundOpacity:.9,enableGroundShadow:!0,groundShadowLevel:.5,enableGroundMirror:!1,groundMirrorSizeRatio:.3,groundMirrorBlurKernel:64,groundMirrorAmount:1,groundMirrorFresnelWeight:1,groundMirrorFallOffDistance:0,groundMirrorTextureType:r.a.TEXTURETYPE_UNSIGNED_INT,groundYBias:1e-5,createSkybox:!0,skyboxSize:20,skyboxTexture:this._skyboxTextureCDNUrl,skyboxColor:new h.a(.2,.2,.3).toLinearSpace().scale(3),backgroundYRotation:0,sizeAuto:!0,rootPosition:g.e.Zero(),setupImageProcessing:!0,environmentTexture:this._environmentTextureCDNUrl,cameraExposure:.8,cameraContrast:1.2,toneMappingEnabled:!0}},Object.defineProperty(a.prototype,"rootMesh",{get:function(){return this._rootMesh},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skybox",{get:function(){return this._skybox},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skyboxTexture",{get:function(){return this._skyboxTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skyboxMaterial",{get:function(){return this._skyboxMaterial},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"ground",{get:function(){return this._ground},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundTexture",{get:function(){return this._groundTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMirror",{get:function(){return this._groundMirror},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMirrorRenderList",{get:function(){return this._groundMirror?this._groundMirror.renderList:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMaterial",{get:function(){return this._groundMaterial},enumerable:!1,configurable:!0}),a.prototype.updateOptions=function(a){a=Object(l.a)(Object(l.a)({},this._options),a);this._ground&&!a.createGround&&(this._ground.dispose(),this._ground=null),this._groundMaterial&&!a.createGround&&(this._groundMaterial.dispose(),this._groundMaterial=null),this._groundTexture&&this._options.groundTexture!=a.groundTexture&&(this._groundTexture.dispose(),this._groundTexture=null),this._skybox&&!a.createSkybox&&(this._skybox.dispose(),this._skybox=null),this._skyboxMaterial&&!a.createSkybox&&(this._skyboxMaterial.dispose(),this._skyboxMaterial=null),this._skyboxTexture&&this._options.skyboxTexture!=a.skyboxTexture&&(this._skyboxTexture.dispose(),this._skyboxTexture=null),this._groundMirror&&!a.enableGroundMirror&&(this._groundMirror.dispose(),this._groundMirror=null),this._scene.environmentTexture&&this._options.environmentTexture!=a.environmentTexture&&this._scene.environmentTexture.dispose(),this._options=a,this._setupBackground(),this._setupImageProcessing()},a.prototype.setMainColor=function(a){this.groundMaterial&&(this.groundMaterial.primaryColor=a),this.skyboxMaterial&&(this.skyboxMaterial.primaryColor=a),this.groundMirror&&(this.groundMirror.clearColor=new h.b(a.r,a.g,a.b,1))},a.prototype._setupImageProcessing=function(){this._options.setupImageProcessing&&(this._scene.imageProcessingConfiguration.contrast=this._options.cameraContrast,this._scene.imageProcessingConfiguration.exposure=this._options.cameraExposure,this._scene.imageProcessingConfiguration.toneMappingEnabled=this._options.toneMappingEnabled,this._setupEnvironmentTexture())},a.prototype._setupEnvironmentTexture=function(){if(!this._scene.environmentTexture)if(this._options.environmentTexture instanceof Ie.a)this._scene.environmentTexture=this._options.environmentTexture;else{var a=Xh.CreateFromPrefilteredData(this._options.environmentTexture,this._scene);this._scene.environmentTexture=a}},a.prototype._setupBackground=function(){this._rootMesh||(this._rootMesh=new S.a("BackgroundHelper",this._scene)),this._rootMesh.rotation.y=this._options.backgroundYRotation;var a=this._getSceneSize();this._options.createGround&&(this._setupGround(a),this._setupGroundMaterial(),this._setupGroundDiffuseTexture(),this._options.enableGroundMirror&&this._setupGroundMirrorTexture(a),this._setupMirrorInGroundMaterial()),this._options.createSkybox&&(this._setupSkybox(a),this._setupSkyboxMaterial(),this._setupSkyboxReflectionTexture()),this._rootMesh.position.x=a.rootPosition.x,this._rootMesh.position.z=a.rootPosition.z,this._rootMesh.position.y=a.rootPosition.y},a.prototype._getSceneSize=function(){var a=this,b=this._options.groundSize,c=this._options.skyboxSize,d=this._options.rootPosition;if(!this._scene.meshes||1===this._scene.meshes.length)return{groundSize:b,skyboxSize:c,rootPosition:d};var e=this._scene.getWorldExtends(function(b){return b!==a._ground&&b!==a._rootMesh&&b!==a._skybox}),f=e.max.subtract(e.min);if(this._options.sizeAuto){this._scene.activeCamera instanceof fc&&this._scene.activeCamera.upperRadiusLimit&&(c=b=2*this._scene.activeCamera.upperRadiusLimit);var g=f.length();g>b&&(c=b=2*g),b*=1.1,c*=1.5,(d=e.min.add(f.scale(.5))).y=e.min.y-this._options.groundYBias}return{groundSize:b,skyboxSize:c,rootPosition:d}},a.prototype._setupGround=function(a){var b=this;this._ground&&!this._ground.isDisposed()||(this._ground=Object(Ah.a)("BackgroundPlane",{size:a.groundSize},this._scene),this._ground.rotation.x=Math.PI/2,this._ground.parent=this._rootMesh,this._ground.onDisposeObservable.add(function(){b._ground=null})),this._ground.receiveShadows=this._options.enableGroundShadow},a.prototype._setupGroundMaterial=function(){this._groundMaterial||(this._groundMaterial=new di("BackgroundPlaneMaterial",this._scene)),this._groundMaterial.alpha=this._options.groundOpacity,this._groundMaterial.alphaMode=r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,this._groundMaterial.shadowLevel=this._options.groundShadowLevel,this._groundMaterial.primaryColor=this._options.groundColor,this._groundMaterial.useRGBColor=!1,this._groundMaterial.enableNoise=!0,this._ground&&(this._ground.material=this._groundMaterial)},a.prototype._setupGroundDiffuseTexture=function(){this._groundMaterial&&(this._groundTexture||(this._options.groundTexture instanceof Ie.a?this._groundMaterial.diffuseTexture=this._options.groundTexture:(this._groundTexture=new V.a(this._options.groundTexture,this._scene,void 0,void 0,void 0,void 0,this._errorHandler),this._groundTexture.gammaSpace=!1,this._groundTexture.hasAlpha=!0,this._groundMaterial.diffuseTexture=this._groundTexture)))},a.prototype._setupGroundMirrorTexture=function(a){var b=V.a.CLAMP_ADDRESSMODE;if(!this._groundMirror&&(this._groundMirror=new Wh("BackgroundPlaneMirrorTexture",{ratio:this._options.groundMirrorSizeRatio},this._scene,!1,this._options.groundMirrorTextureType,V.a.BILINEAR_SAMPLINGMODE,!0),this._groundMirror.mirrorPlane=new Hb.a(0,-1,0,a.rootPosition.y),this._groundMirror.anisotropicFilteringLevel=1,this._groundMirror.wrapU=b,this._groundMirror.wrapV=b,this._groundMirror.gammaSpace=!1,this._groundMirror.renderList))for(a=0;a0&&a.push(this._texture),this._textureRoughness&&this._textureRoughness.animations&&this._textureRoughness.animations.length>0&&a.push(this._textureRoughness),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._tintTexture&&this._tintTexture.animations&&this._tintTexture.animations.length>0&&a.push(this._tintTexture)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose(),null===(a=this._textureRoughness)||void 0===a||a.dispose(),null===(a=this._bumpTexture)||void 0===a||a.dispose(),null===(a=this._tintTexture)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"PBRClearCoatConfiguration"},a.AddFallbacks=function(a,b,c){return a.CLEARCOAT_BUMP&&b.addFallback(c++,"CLEARCOAT_BUMP"),a.CLEARCOAT_TINT&&b.addFallback(c++,"CLEARCOAT_TINT"),a.CLEARCOAT&&b.addFallback(c++,"CLEARCOAT"),c},a.AddUniforms=function(a){a.push("vClearCoatTangentSpaceParams","vClearCoatParams","vClearCoatRefractionParams","vClearCoatTintParams","clearCoatColorAtDistance","clearCoatMatrix","clearCoatRoughnessMatrix","clearCoatBumpMatrix","clearCoatTintMatrix","vClearCoatInfos","vClearCoatBumpInfos","vClearCoatTintInfos")},a.AddSamplers=function(a){a.push("clearCoatSampler","clearCoatRoughnessSampler","clearCoatBumpSampler","clearCoatTintSampler")},a.PrepareUniformBuffer=function(a){a.addUniform("vClearCoatParams",2),a.addUniform("vClearCoatRefractionParams",4),a.addUniform("vClearCoatInfos",4),a.addUniform("clearCoatMatrix",16),a.addUniform("clearCoatRoughnessMatrix",16),a.addUniform("vClearCoatBumpInfos",2),a.addUniform("vClearCoatTangentSpaceParams",2),a.addUniform("clearCoatBumpMatrix",16),a.addUniform("vClearCoatTintParams",4),a.addUniform("clearCoatColorAtDistance",1),a.addUniform("vClearCoatTintInfos",2),a.addUniform("clearCoatTintMatrix",16)},a.prototype.copyTo=function(a){J.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return J.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;J.a.Parse(function(){return d},a,b,c)},a._DefaultIndexOfRefraction=1.5,Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(J.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(J.d)()],a.prototype,"roughness",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"indexOfRefraction",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useRoughnessFromMainTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"textureRoughness",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"remapF0OnInterfaceChange",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"bumpTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isTintEnabled",void 0),Object(l.c)([Object(J.f)()],a.prototype,"tintColor",void 0),Object(l.c)([Object(J.d)()],a.prototype,"tintColorAtDistance",void 0),Object(l.c)([Object(J.d)()],a.prototype,"tintThickness",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"tintTexture",void 0),a}(),li=function(){function a(a){this._isEnabled=!1,this.isEnabled=!1,this.intensity=1,this.direction=new g.d(1,0),this._texture=null,this.texture=null,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){return!this._isEnabled||!(a._areTexturesDirty&&b.texturesEnabled&&this._texture&&ai.a.AnisotropicTextureEnabled&&!this._texture.isReadyOrNotBlocking())},a.prototype.prepareDefines=function(a,b,c){this._isEnabled?(a.ANISOTROPIC=this._isEnabled,this._isEnabled&&!b.isVerticesDataPresent(W.b.TangentKind)&&(a._needUVs=!0,a.MAINUV1=!0),a._areTexturesDirty&&c.texturesEnabled&&(this._texture&&ai.a.AnisotropicTextureEnabled?Yh.a.PrepareDefinesForMergedUV(this._texture,a,"ANISOTROPIC_TEXTURE"):a.ANISOTROPIC_TEXTURE=!1)):(a.ANISOTROPIC=!1,a.ANISOTROPIC_TEXTURE=!1)},a.prototype.bindForSubMesh=function(a,b,c){this._isEnabled&&(a.useUbo&&c&&a.isSync||(this._texture&&ai.a.AnisotropicTextureEnabled&&(a.updateFloat2("vAnisotropyInfos",this._texture.coordinatesIndex,this._texture.level),Yh.a.BindTextureMatrix(this._texture,a,"anisotropy")),a.updateFloat3("vAnisotropy",this.direction.x,this.direction.y,this.intensity)),b.texturesEnabled&&this._texture&&ai.a.AnisotropicTextureEnabled&&a.setTexture("anisotropySampler",this._texture))},a.prototype.hasTexture=function(a){return this._texture===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture)},a.prototype.dispose=function(a){a&&this._texture&&this._texture.dispose()},a.prototype.getClassName=function(){return"PBRAnisotropicConfiguration"},a.AddFallbacks=function(a,b,c){return a.ANISOTROPIC&&b.addFallback(c++,"ANISOTROPIC"),c},a.AddUniforms=function(a){a.push("vAnisotropy","vAnisotropyInfos","anisotropyMatrix")},a.PrepareUniformBuffer=function(a){a.addUniform("vAnisotropy",3),a.addUniform("vAnisotropyInfos",2),a.addUniform("anisotropyMatrix",16)},a.AddSamplers=function(a){a.push("anisotropySampler")},a.prototype.copyTo=function(a){J.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return J.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;J.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(J.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(J.o)()],a.prototype,"direction",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),a}(),mi=function(){function a(b){this._useEnergyConservation=a.DEFAULT_USE_ENERGY_CONSERVATION,this.useEnergyConservation=a.DEFAULT_USE_ENERGY_CONSERVATION,this._useSmithVisibilityHeightCorrelated=a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED,this.useSmithVisibilityHeightCorrelated=a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED,this._useSphericalHarmonics=a.DEFAULT_USE_SPHERICAL_HARMONICS,this.useSphericalHarmonics=a.DEFAULT_USE_SPHERICAL_HARMONICS,this._useSpecularGlossinessInputEnergyConservation=a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION,this.useSpecularGlossinessInputEnergyConservation=a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION,this._internalMarkAllSubMeshesAsMiscDirty=b}return a.prototype._markAllSubMeshesAsMiscDirty=function(){this._internalMarkAllSubMeshesAsMiscDirty()},a.prototype.prepareDefines=function(a){a.BRDF_V_HEIGHT_CORRELATED=this._useSmithVisibilityHeightCorrelated,a.MS_BRDF_ENERGY_CONSERVATION=this._useEnergyConservation&&this._useSmithVisibilityHeightCorrelated,a.SPHERICAL_HARMONICS=this._useSphericalHarmonics,a.SPECULAR_GLOSSINESS_ENERGY_CONSERVATION=this._useSpecularGlossinessInputEnergyConservation},a.prototype.getClassName=function(){return"PBRBRDFConfiguration"},a.prototype.copyTo=function(a){J.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return J.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;J.a.Parse(function(){return d},a,b,c)},a.DEFAULT_USE_ENERGY_CONSERVATION=!0,a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED=!0,a.DEFAULT_USE_SPHERICAL_HARMONICS=!0,a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION=!0,Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useEnergyConservation",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSmithVisibilityHeightCorrelated",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSphericalHarmonics",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSpecularGlossinessInputEnergyConservation",void 0),a}(),ni=function(){function a(a){this._isEnabled=!1,this.isEnabled=!1,this._linkSheenWithAlbedo=!1,this.linkSheenWithAlbedo=!1,this.intensity=1,this.color=h.a.White(),this._texture=null,this.texture=null,this._useRoughnessFromMainTexture=!0,this.useRoughnessFromMainTexture=!0,this._roughness=null,this.roughness=null,this._textureRoughness=null,this.textureRoughness=null,this._albedoScaling=!1,this.albedoScaling=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isEnabled)return!0;if(a._areTexturesDirty&&b.texturesEnabled){if(this._texture&&ai.a.SheenTextureEnabled&&!this._texture.isReadyOrNotBlocking())return!1;if(this._textureRoughness&&ai.a.SheenTextureEnabled&&!this._textureRoughness.isReadyOrNotBlocking())return!1}return!0},a.prototype.prepareDefines=function(a,b){var c;this._isEnabled?(a.SHEEN=this._isEnabled,a.SHEEN_LINKWITHALBEDO=this._linkSheenWithAlbedo,a.SHEEN_ROUGHNESS=null!==this._roughness,a.SHEEN_ALBEDOSCALING=this._albedoScaling,a.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=this._useRoughnessFromMainTexture,a.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=null!==this._texture&&this._texture._texture===(null===(c=this._textureRoughness)||void 0===c?void 0:c._texture)&&this._texture.checkTransformsAreIdentical(this._textureRoughness),a._areTexturesDirty&&b.texturesEnabled&&(this._texture&&ai.a.SheenTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._texture,a,"SHEEN_TEXTURE"),a.SHEEN_GAMMATEXTURE=this._texture.gammaSpace):a.SHEEN_TEXTURE=!1,this._textureRoughness&&ai.a.SheenTextureEnabled?Yh.a.PrepareDefinesForMergedUV(this._textureRoughness,a,"SHEEN_TEXTURE_ROUGHNESS"):a.SHEEN_TEXTURE_ROUGHNESS=!1)):(a.SHEEN=!1,a.SHEEN_TEXTURE=!1,a.SHEEN_TEXTURE_ROUGHNESS=!1,a.SHEEN_LINKWITHALBEDO=!1,a.SHEEN_ROUGHNESS=!1,a.SHEEN_ALBEDOSCALING=!1,a.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,a.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=!1)},a.prototype.bindForSubMesh=function(a,b,c,d){if(this._isEnabled){d=d._materialDefines;var e=d.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL;a.useUbo&&c&&a.isSync||(e&&ai.a.SheenTextureEnabled?(a.updateFloat4("vSheenInfos",this._texture.coordinatesIndex,this._texture.level,-1,-1),Yh.a.BindTextureMatrix(this._texture,a,"sheen")):(this._texture||this._textureRoughness)&&ai.a.SheenTextureEnabled&&(a.updateFloat4("vSheenInfos",null!==(c=null===(c=this._texture)||void 0===c?void 0:c.coordinatesIndex)&&void 0!==c?c:0,null!==(c=null===(c=this._texture)||void 0===c?void 0:c.level)&&void 0!==c?c:0,null!==(c=null===(c=this._textureRoughness)||void 0===c?void 0:c.coordinatesIndex)&&void 0!==c?c:0,null!==(c=null===(c=this._textureRoughness)||void 0===c?void 0:c.level)&&void 0!==c?c:0),this._texture&&Yh.a.BindTextureMatrix(this._texture,a,"sheen"),!this._textureRoughness||e||d.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE||Yh.a.BindTextureMatrix(this._textureRoughness,a,"sheenRoughness")),a.updateFloat4("vSheenColor",this.color.r,this.color.g,this.color.b,this.intensity),null!==this._roughness&&a.updateFloat("vSheenRoughness",this._roughness)),b.texturesEnabled&&(this._texture&&ai.a.SheenTextureEnabled&&a.setTexture("sheenSampler",this._texture),this._textureRoughness&&!e&&!d.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE&&ai.a.SheenTextureEnabled&&a.setTexture("sheenRoughnessSampler",this._textureRoughness))}},a.prototype.hasTexture=function(a){return this._texture===a||this._textureRoughness===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture),this._textureRoughness&&a.push(this._textureRoughness)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture),this._textureRoughness&&this._textureRoughness.animations&&this._textureRoughness.animations.length>0&&a.push(this._textureRoughness)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose(),null===(a=this._textureRoughness)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"PBRSheenConfiguration"},a.AddFallbacks=function(a,b,c){return a.SHEEN&&b.addFallback(c++,"SHEEN"),c},a.AddUniforms=function(a){a.push("vSheenColor","vSheenRoughness","vSheenInfos","sheenMatrix","sheenRoughnessMatrix")},a.PrepareUniformBuffer=function(a){a.addUniform("vSheenColor",4),a.addUniform("vSheenRoughness",1),a.addUniform("vSheenInfos",4),a.addUniform("sheenMatrix",16),a.addUniform("sheenRoughnessMatrix",16)},a.AddSamplers=function(a){a.push("sheenSampler"),a.push("sheenRoughnessSampler")},a.prototype.copyTo=function(a){J.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return J.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;J.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"linkSheenWithAlbedo",void 0),Object(l.c)([Object(J.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(J.f)()],a.prototype,"color",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useRoughnessFromMainTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"roughness",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"textureRoughness",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"albedoScaling",void 0),a}(),oi=function(){function a(a,b,c){this._isRefractionEnabled=!1,this.isRefractionEnabled=!1,this._isTranslucencyEnabled=!1,this.isTranslucencyEnabled=!1,this._isScatteringEnabled=!1,this.isScatteringEnabled=!1,this._scatteringDiffusionProfileIndex=0,this.refractionIntensity=1,this.translucencyIntensity=1,this.useAlbedoToTintRefraction=!1,this.useAlbedoToTintTranslucency=!1,this._thicknessTexture=null,this.thicknessTexture=null,this._refractionTexture=null,this.refractionTexture=null,this._indexOfRefraction=1.5,this.indexOfRefraction=1.5,this._volumeIndexOfRefraction=-1,this._invertRefractionY=!1,this.invertRefractionY=!1,this._linkRefractionWithTransparency=!1,this.linkRefractionWithTransparency=!1,this.minimumThickness=0,this.maximumThickness=1,this.useThicknessAsDepth=!1,this.tintColor=h.a.White(),this.tintColorAtDistance=1,this.diffusionDistance=h.a.White(),this._useMaskFromThicknessTexture=!1,this.useMaskFromThicknessTexture=!1,this._refractionIntensityTexture=null,this.refractionIntensityTexture=null,this._translucencyIntensityTexture=null,this.translucencyIntensityTexture=null,this._useGltfStyleTextures=!1,this.useGltfStyleTextures=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a,this._internalMarkScenePrePassDirty=b,this._scene=c}return Object.defineProperty(a.prototype,"scatteringDiffusionProfile",{get:function(){return this._scene.subSurfaceConfiguration?this._scene.subSurfaceConfiguration.ssDiffusionProfileColors[this._scatteringDiffusionProfileIndex]:null},set:function(a){this._scene.enableSubSurfaceForPrePass()&&a&&(this._scatteringDiffusionProfileIndex=this._scene.subSurfaceConfiguration.addDiffusionProfile(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"volumeIndexOfRefraction",{get:function(){return this._volumeIndexOfRefraction>=1?this._volumeIndexOfRefraction:this._indexOfRefraction},set:function(a){this._volumeIndexOfRefraction=a>=1?a:-1},enumerable:!1,configurable:!0}),a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype._markScenePrePassDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty(),this._internalMarkScenePrePassDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isRefractionEnabled&&!this._isTranslucencyEnabled&&!this._isScatteringEnabled)return!0;if(a._areTexturesDirty&&b.texturesEnabled){if(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&!this._thicknessTexture.isReadyOrNotBlocking())return!1;a=this._getRefractionTexture(b);if(a&&ai.a.RefractionTextureEnabled&&!a.isReadyOrNotBlocking())return!1}return!0},a.prototype.prepareDefines=function(a,b){if(!this._isRefractionEnabled&&!this._isTranslucencyEnabled&&!this._isScatteringEnabled)return a.SUBSURFACE=!1,a.SS_TRANSLUCENCY=!1,a.SS_SCATTERING=!1,void (a.SS_REFRACTION=!1);if(a._areTexturesDirty){if(a.SUBSURFACE=!1,a.SS_TRANSLUCENCY=this._isTranslucencyEnabled,a.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=!1,a.SS_SCATTERING=this._isScatteringEnabled,a.SS_THICKNESSANDMASK_TEXTURE=!1,a.SS_REFRACTIONINTENSITY_TEXTURE=!1,a.SS_TRANSLUCENCYINTENSITY_TEXTURE=!1,a.SS_HAS_THICKNESS=!1,a.SS_MASK_FROM_THICKNESS_TEXTURE=!1,a.SS_USE_GLTF_TEXTURES=!1,a.SS_REFRACTION=!1,a.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=!1,a.SS_REFRACTIONMAP_3D=!1,a.SS_GAMMAREFRACTION=!1,a.SS_RGBDREFRACTION=!1,a.SS_LINEARSPECULARREFRACTION=!1,a.SS_REFRACTIONMAP_OPPOSITEZ=!1,a.SS_LODINREFRACTIONALPHA=!1,a.SS_LINKREFRACTIONTOTRANSPARENCY=!1,a.SS_ALBEDOFORREFRACTIONTINT=!1,a.SS_ALBEDOFORTRANSLUCENCYTINT=!1,a.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=!1,a.SS_USE_THICKNESS_AS_DEPTH=!1,this._isRefractionEnabled||this._isTranslucencyEnabled||this._isScatteringEnabled){a.SUBSURFACE=!0;var c=!!this._thicknessTexture&&!!this._refractionIntensityTexture&&this._refractionIntensityTexture.checkTransformsAreIdentical(this._thicknessTexture)&&this._refractionIntensityTexture._texture===this._thicknessTexture._texture,d=!!this._thicknessTexture&&!!this._translucencyIntensityTexture&&this._translucencyIntensityTexture.checkTransformsAreIdentical(this._thicknessTexture)&&this._translucencyIntensityTexture._texture===this._thicknessTexture._texture;c=(c||!this._refractionIntensityTexture)&&(d||!this._translucencyIntensityTexture);a._areTexturesDirty&&b.texturesEnabled&&(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&Yh.a.PrepareDefinesForMergedUV(this._thicknessTexture,a,"SS_THICKNESSANDMASK_TEXTURE"),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&!c&&Yh.a.PrepareDefinesForMergedUV(this._refractionIntensityTexture,a,"SS_REFRACTIONINTENSITY_TEXTURE"),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&!c&&Yh.a.PrepareDefinesForMergedUV(this._translucencyIntensityTexture,a,"SS_TRANSLUCENCYINTENSITY_TEXTURE")),a.SS_HAS_THICKNESS=this.maximumThickness-this.minimumThickness!=0,a.SS_MASK_FROM_THICKNESS_TEXTURE=(this._useMaskFromThicknessTexture||!!this._refractionIntensityTexture||!!this._translucencyIntensityTexture)&&c,a.SS_USE_GLTF_TEXTURES=this._useGltfStyleTextures,a.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=(this._useMaskFromThicknessTexture||!!this._refractionIntensityTexture)&&c,a.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=(this._useMaskFromThicknessTexture||!!this._translucencyIntensityTexture)&&c}if(this._isRefractionEnabled&&b.texturesEnabled){d=this._getRefractionTexture(b);d&&ai.a.RefractionTextureEnabled&&(a.SS_REFRACTION=!0,a.SS_REFRACTIONMAP_3D=d.isCube,a.SS_GAMMAREFRACTION=d.gammaSpace,a.SS_RGBDREFRACTION=d.isRGBD,a.SS_LINEARSPECULARREFRACTION=d.linearSpecularLOD,a.SS_REFRACTIONMAP_OPPOSITEZ=d.invertZ,a.SS_LODINREFRACTIONALPHA=d.lodLevelInAlpha,a.SS_LINKREFRACTIONTOTRANSPARENCY=this._linkRefractionWithTransparency,a.SS_ALBEDOFORREFRACTIONTINT=this.useAlbedoToTintRefraction,a.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=d.isCube&&d.boundingBoxSize,a.SS_USE_THICKNESS_AS_DEPTH=this.useThicknessAsDepth)}this._isTranslucencyEnabled&&(a.SS_ALBEDOFORTRANSLUCENCYTINT=this.useAlbedoToTintTranslucency)}},a.prototype.bindForSubMesh=function(a,b,c,d,e,f,h){if(this._isRefractionEnabled||this._isTranslucencyEnabled||this._isScatteringEnabled){c=h._materialDefines;var i=this._getRefractionTexture(b);if(!a.useUbo||!d||!a.isSync){this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&(a.updateFloat2("vThicknessInfos",this._thicknessTexture.coordinatesIndex,this._thicknessTexture.level),Yh.a.BindTextureMatrix(this._thicknessTexture,a,"thickness")),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&c.SS_REFRACTIONINTENSITY_TEXTURE&&(a.updateFloat2("vRefractionIntensityInfos",this._refractionIntensityTexture.coordinatesIndex,this._refractionIntensityTexture.level),Yh.a.BindTextureMatrix(this._refractionIntensityTexture,a,"refractionIntensity")),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&c.SS_TRANSLUCENCYINTENSITY_TEXTURE&&(a.updateFloat2("vTranslucencyIntensityInfos",this._translucencyIntensityTexture.coordinatesIndex,this._translucencyIntensityTexture.level),Yh.a.BindTextureMatrix(this._translucencyIntensityTexture,a,"translucencyIntensity")),h.getRenderingMesh().getWorldMatrix().decompose(g.c.Vector3[0]);d=Math.max(Math.abs(g.c.Vector3[0].x),Math.abs(g.c.Vector3[0].y),Math.abs(g.c.Vector3[0].z));if(a.updateFloat2("vThicknessParam",this.minimumThickness*d,(this.maximumThickness-this.minimumThickness)*d),i&&ai.a.RefractionTextureEnabled){a.updateMatrix("refractionMatrix",i.getReflectionTextureMatrix());h=1;i.isCube||i.depth&&(h=i.depth);d=i.getSize().width;var j=this.volumeIndexOfRefraction;if(a.updateFloat4("vRefractionInfos",i.level,1/j,h,this._invertRefractionY?-1:1),a.updateFloat4("vRefractionMicrosurfaceInfos",d,i.lodGenerationScale,i.lodGenerationOffset,1/this.indexOfRefraction),f&&a.updateFloat2("vRefractionFilteringInfo",d,I.a.Log2(d)),i.boundingBoxSize){j=i;a.updateVector3("vRefractionPosition",j.boundingBoxPosition),a.updateVector3("vRefractionSize",j.boundingBoxSize)}}this.isScatteringEnabled&&a.updateFloat("scatteringDiffusionProfile",this._scatteringDiffusionProfileIndex),a.updateColor3("vDiffusionDistance",this.diffusionDistance),a.updateFloat4("vTintColor",this.tintColor.r,this.tintColor.g,this.tintColor.b,Math.max(1e-5,this.tintColorAtDistance)),a.updateFloat3("vSubSurfaceIntensity",this.refractionIntensity,this.translucencyIntensity,0)}b.texturesEnabled&&(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&a.setTexture("thicknessSampler",this._thicknessTexture),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&c.SS_REFRACTIONINTENSITY_TEXTURE&&a.setTexture("refractionIntensitySampler",this._refractionIntensityTexture),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&c.SS_TRANSLUCENCYINTENSITY_TEXTURE&&a.setTexture("translucencyIntensitySampler",this._translucencyIntensityTexture),i&&ai.a.RefractionTextureEnabled&&(e?a.setTexture("refractionSampler",i):(a.setTexture("refractionSampler",i._lodTextureMid||i),a.setTexture("refractionSamplerLow",i._lodTextureLow||i),a.setTexture("refractionSamplerHigh",i._lodTextureHigh||i))))}},a.prototype.unbind=function(a){return!(!this._refractionTexture||!this._refractionTexture.isRenderTarget)&&(a.setTexture("refractionSampler",null),!0)},a.prototype._getRefractionTexture=function(a){return this._refractionTexture?this._refractionTexture:this._isRefractionEnabled?a.environmentTexture:null},Object.defineProperty(a.prototype,"disableAlphaBlending",{get:function(){return this.isRefractionEnabled&&this._linkRefractionWithTransparency},enumerable:!1,configurable:!0}),a.prototype.fillRenderTargetTextures=function(a){ai.a.RefractionTextureEnabled&&this._refractionTexture&&this._refractionTexture.isRenderTarget&&a.push(this._refractionTexture)},a.prototype.hasTexture=function(a){return this._thicknessTexture===a||this._refractionTexture===a},a.prototype.hasRenderTargetTextures=function(){return!!(ai.a.RefractionTextureEnabled&&this._refractionTexture&&this._refractionTexture.isRenderTarget)},a.prototype.getActiveTextures=function(a){this._thicknessTexture&&a.push(this._thicknessTexture),this._refractionTexture&&a.push(this._refractionTexture)},a.prototype.getAnimatables=function(a){this._thicknessTexture&&this._thicknessTexture.animations&&this._thicknessTexture.animations.length>0&&a.push(this._thicknessTexture),this._refractionTexture&&this._refractionTexture.animations&&this._refractionTexture.animations.length>0&&a.push(this._refractionTexture)},a.prototype.dispose=function(a){a&&(this._thicknessTexture&&this._thicknessTexture.dispose(),this._refractionTexture&&this._refractionTexture.dispose())},a.prototype.getClassName=function(){return"PBRSubSurfaceConfiguration"},a.AddFallbacks=function(a,b,c){return a.SS_SCATTERING&&b.addFallback(c++,"SS_SCATTERING"),a.SS_TRANSLUCENCY&&b.addFallback(c++,"SS_TRANSLUCENCY"),c},a.AddUniforms=function(a){a.push("vDiffusionDistance","vTintColor","vSubSurfaceIntensity","vRefractionMicrosurfaceInfos","vRefractionFilteringInfo","vRefractionInfos","vThicknessInfos","vRefractionIntensityInfos","vTranslucencyIntensityInfos","vThicknessParam","vRefractionPosition","vRefractionSize","refractionMatrix","thicknessMatrix","refractionIntensityMatrix","translucencyIntensityMatrix","scatteringDiffusionProfile")},a.AddSamplers=function(a){a.push("thicknessSampler","refractionIntensitySampler","translucencyIntensitySampler","refractionSampler","refractionSamplerLow","refractionSamplerHigh")},a.PrepareUniformBuffer=function(a){a.addUniform("vRefractionMicrosurfaceInfos",4),a.addUniform("vRefractionFilteringInfo",2),a.addUniform("vTranslucencyIntensityInfos",2),a.addUniform("vRefractionInfos",4),a.addUniform("refractionMatrix",16),a.addUniform("vThicknessInfos",2),a.addUniform("vRefractionIntensityInfos",2),a.addUniform("thicknessMatrix",16),a.addUniform("refractionIntensityMatrix",16),a.addUniform("translucencyIntensityMatrix",16),a.addUniform("vThicknessParam",2),a.addUniform("vDiffusionDistance",3),a.addUniform("vTintColor",4),a.addUniform("vSubSurfaceIntensity",3),a.addUniform("vRefractionPosition",3),a.addUniform("vRefractionSize",3),a.addUniform("scatteringDiffusionProfile",1)},a.prototype.copyTo=function(a){J.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return J.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;J.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isRefractionEnabled",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isTranslucencyEnabled",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markScenePrePassDirty")],a.prototype,"isScatteringEnabled",void 0),Object(l.c)([Object(J.d)()],a.prototype,"_scatteringDiffusionProfileIndex",void 0),Object(l.c)([Object(J.d)()],a.prototype,"refractionIntensity",void 0),Object(l.c)([Object(J.d)()],a.prototype,"translucencyIntensity",void 0),Object(l.c)([Object(J.d)()],a.prototype,"useAlbedoToTintRefraction",void 0),Object(l.c)([Object(J.d)()],a.prototype,"useAlbedoToTintTranslucency",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"thicknessTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"refractionTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"indexOfRefraction",void 0),Object(l.c)([Object(J.d)()],a.prototype,"_volumeIndexOfRefraction",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"volumeIndexOfRefraction",null),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"invertRefractionY",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"linkRefractionWithTransparency",void 0),Object(l.c)([Object(J.d)()],a.prototype,"minimumThickness",void 0),Object(l.c)([Object(J.d)()],a.prototype,"maximumThickness",void 0),Object(l.c)([Object(J.d)()],a.prototype,"useThicknessAsDepth",void 0),Object(l.c)([Object(J.f)()],a.prototype,"tintColor",void 0),Object(l.c)([Object(J.d)()],a.prototype,"tintColorAtDistance",void 0),Object(l.c)([Object(J.f)()],a.prototype,"diffusionDistance",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useMaskFromThicknessTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"refractionIntensityTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"translucencyIntensityTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useGltfStyleTextures",void 0),a}(),pi=c(119),qi=c(29);rb=(c(190),c(191),"uniform vec4 vEyePosition; uniform vec3 vReflectionColor; uniform vec4 vAlbedoColor; uniform vec4 vLightingIntensity; uniform vec4 vReflectivityColor; uniform vec4 vMetallicReflectanceFactors; uniform vec3 vEmissiveColor; uniform float visibility; uniform vec3 vAmbientColor; #ifdef ALBEDO uniform vec2 vAlbedoInfos; #endif #ifdef AMBIENT uniform vec4 vAmbientInfos; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #ifdef OPACITY uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; #endif #ifdef REFLECTIVITY uniform vec3 vReflectivityInfos; #endif #ifdef MICROSURFACEMAP uniform vec2 vMicroSurfaceSamplerInfos; #endif #if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION) || defined(PREPASS) uniform mat4 view; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; #ifdef REALTIME_FILTERING uniform vec2 vReflectionFilteringInfo; #endif uniform mat4 reflectionMatrix; uniform vec3 vReflectionMicrosurfaceInfos; #if defined(USE_LOCAL_REFLECTIONMAP_CUBIC) && defined(REFLECTIONMAP_CUBIC) uniform vec3 vReflectionPosition; uniform vec3 vReflectionSize; #endif #endif #if defined(SS_REFRACTION) && defined(SS_USE_LOCAL_REFRACTIONMAP_CUBIC) uniform vec3 vRefractionPosition; uniform vec3 vRefractionSize; #endif #ifdef CLEARCOAT uniform vec2 vClearCoatParams; uniform vec4 vClearCoatRefractionParams; #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS) uniform vec4 vClearCoatInfos; #endif #ifdef CLEARCOAT_TEXTURE uniform mat4 clearCoatMatrix; #endif #ifdef CLEARCOAT_TEXTURE_ROUGHNESS uniform mat4 clearCoatRoughnessMatrix; #endif #ifdef CLEARCOAT_BUMP uniform vec2 vClearCoatBumpInfos; uniform vec2 vClearCoatTangentSpaceParams; uniform mat4 clearCoatBumpMatrix; #endif #ifdef CLEARCOAT_TINT uniform vec4 vClearCoatTintParams; uniform float clearCoatColorAtDistance; #ifdef CLEARCOAT_TINT_TEXTURE uniform vec2 vClearCoatTintInfos; uniform mat4 clearCoatTintMatrix; #endif #endif #endif #ifdef ANISOTROPIC uniform vec3 vAnisotropy; #ifdef ANISOTROPIC_TEXTURE uniform vec2 vAnisotropyInfos; uniform mat4 anisotropyMatrix; #endif #endif #ifdef SHEEN uniform vec4 vSheenColor; #ifdef SHEEN_ROUGHNESS uniform float vSheenRoughness; #endif #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS) uniform vec4 vSheenInfos; #endif #ifdef SHEEN_TEXTURE uniform mat4 sheenMatrix; #endif #ifdef SHEEN_TEXTURE_ROUGHNESS uniform mat4 sheenRoughnessMatrix; #endif #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION uniform vec4 vRefractionMicrosurfaceInfos; uniform vec4 vRefractionInfos; uniform mat4 refractionMatrix; #ifdef REALTIME_FILTERING uniform vec2 vRefractionFilteringInfo; #endif #endif #ifdef SS_THICKNESSANDMASK_TEXTURE uniform vec2 vThicknessInfos; uniform mat4 thicknessMatrix; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE uniform vec2 vRefractionIntensityInfos; uniform mat4 refractionIntensityMatrix; #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE uniform vec2 vTranslucencyIntensityInfos; uniform mat4 translucencyIntensityMatrix; #endif uniform vec2 vThicknessParam; uniform vec3 vDiffusionDistance; uniform vec4 vTintColor; uniform vec3 vSubSurfaceIntensity; #endif #ifdef PREPASS #ifdef SS_SCATTERING uniform float scatteringDiffusionProfile; #endif #endif #if DEBUGMODE>0 uniform vec2 vDebugMode; #endif #ifdef DETAIL uniform vec4 vDetailInfos; #endif #ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS uniform vec3 vSphericalL00; uniform vec3 vSphericalL1_1; uniform vec3 vSphericalL10; uniform vec3 vSphericalL11; uniform vec3 vSphericalL2_2; uniform vec3 vSphericalL2_1; uniform vec3 vSphericalL20; uniform vec3 vSphericalL21; uniform vec3 vSphericalL22; #else uniform vec3 vSphericalX; uniform vec3 vSphericalY; uniform vec3 vSphericalZ; uniform vec3 vSphericalXX_ZZ; uniform vec3 vSphericalYY_ZZ; uniform vec3 vSphericalZZ; uniform vec3 vSphericalXY; uniform vec3 vSphericalYZ; uniform vec3 vSphericalZX; #endif #endif ");X.a.IncludesShadersStore.pbrFragmentDeclaration=rb;c(180);a="layout(std140,column_major) uniform; uniform Material { vec2 vAlbedoInfos; vec4 vAmbientInfos; vec2 vOpacityInfos; vec2 vEmissiveInfos; vec2 vLightmapInfos; vec3 vReflectivityInfos; vec2 vMicroSurfaceSamplerInfos; vec2 vReflectionInfos; vec2 vReflectionFilteringInfo; vec3 vReflectionPosition; vec3 vReflectionSize; vec3 vBumpInfos; mat4 albedoMatrix; mat4 ambientMatrix; mat4 opacityMatrix; mat4 emissiveMatrix; mat4 lightmapMatrix; mat4 reflectivityMatrix; mat4 microSurfaceSamplerMatrix; mat4 bumpMatrix; vec2 vTangentSpaceParams; mat4 reflectionMatrix; vec3 vReflectionColor; vec4 vAlbedoColor; vec4 vLightingIntensity; vec3 vReflectionMicrosurfaceInfos; float pointSize; vec4 vReflectivityColor; vec3 vEmissiveColor; vec3 vAmbientColor; vec2 vDebugMode; vec4 vMetallicReflectanceFactors; vec2 vMetallicReflectanceInfos; mat4 metallicReflectanceMatrix; vec2 vReflectanceInfos; mat4 reflectanceMatrix; vec2 vClearCoatParams; vec4 vClearCoatRefractionParams; vec4 vClearCoatInfos; mat4 clearCoatMatrix; mat4 clearCoatRoughnessMatrix; vec2 vClearCoatBumpInfos; vec2 vClearCoatTangentSpaceParams; mat4 clearCoatBumpMatrix; vec4 vClearCoatTintParams; float clearCoatColorAtDistance; vec2 vClearCoatTintInfos; mat4 clearCoatTintMatrix; vec3 vAnisotropy; vec2 vAnisotropyInfos; mat4 anisotropyMatrix; vec4 vSheenColor; float vSheenRoughness; vec4 vSheenInfos; mat4 sheenMatrix; mat4 sheenRoughnessMatrix; vec4 vRefractionMicrosurfaceInfos; vec2 vRefractionFilteringInfo; vec2 vTranslucencyIntensityInfos; vec4 vRefractionInfos; mat4 refractionMatrix; vec2 vThicknessInfos; vec2 vRefractionIntensityInfos; mat4 thicknessMatrix; mat4 refractionIntensityMatrix; mat4 translucencyIntensityMatrix; vec2 vThicknessParam; vec3 vDiffusionDistance; vec4 vTintColor; vec3 vSubSurfaceIntensity; vec3 vRefractionPosition; vec3 vRefractionSize; float scatteringDiffusionProfile; vec4 vDetailInfos; mat4 detailMatrix; vec3 vSphericalL00; vec3 vSphericalL1_1; vec3 vSphericalL10; vec3 vSphericalL11; vec3 vSphericalL2_2; vec3 vSphericalL2_1; vec3 vSphericalL20; vec3 vSphericalL21; vec3 vSphericalL22; vec3 vSphericalX; vec3 vSphericalY; vec3 vSphericalZ; vec3 vSphericalXX_ZZ; vec3 vSphericalYY_ZZ; vec3 vSphericalZZ; vec3 vSphericalXY; vec3 vSphericalYZ; vec3 vSphericalZX; }; #include #include ";X.a.IncludesShadersStore.pbrUboDeclaration=a;c(149);rb=" varying vec3 vPositionW; #if DEBUGMODE>0 varying vec4 vClipSpacePosition; #endif #include[1..7] #ifdef NORMAL varying vec3 vNormalW; #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) varying vec3 vEnvironmentIrradiance; #endif #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif";X.a.IncludesShadersStore.pbrFragmentExtraDeclaration=rb;c(182);a="#ifdef _DEFINENAME_ #if _DEFINENAME_DIRECTUV == 1 #define v_VARYINGNAME_UV vMainUV1 #elif _DEFINENAME_DIRECTUV == 2 #define v_VARYINGNAME_UV vMainUV2 #elif _DEFINENAME_DIRECTUV == 3 #define v_VARYINGNAME_UV vMainUV3 #elif _DEFINENAME_DIRECTUV == 4 #define v_VARYINGNAME_UV vMainUV4 #elif _DEFINENAME_DIRECTUV == 5 #define v_VARYINGNAME_UV vMainUV5 #elif _DEFINENAME_DIRECTUV == 6 #define v_VARYINGNAME_UV vMainUV6 #else varying vec2 v_VARYINGNAME_UV; #endif #endif ";X.a.IncludesShadersStore.samplerFragmentAlternateDeclaration=a;rb="#include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_SAMPLERNAME_,albedo) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_SAMPLERNAME_,lightmap) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity,_SAMPLERNAME_,reflectivity) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler,_SAMPLERNAME_,microSurface) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance,_SAMPLERNAME_,metallicReflectance) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance,_SAMPLERNAME_,reflectance) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat,_SAMPLERNAME_,clearCoat) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness) #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) uniform sampler2D clearCoatRoughnessSampler; #endif #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump,_SAMPLERNAME_,clearCoatBump) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint,_SAMPLERNAME_,clearCoatTint) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen,_SAMPLERNAME_,sheen) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness) #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) uniform sampler2D sheenRoughnessSampler; #endif #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy,_SAMPLERNAME_,anisotropy) #endif #ifdef REFLECTION #ifdef REFLECTIONMAP_3D #define sampleReflection(s,c) textureCube(s,c) uniform samplerCube reflectionSampler; #ifdef LODBASEDMICROSFURACE #define sampleReflectionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #ifdef USEIRRADIANCEMAP uniform samplerCube irradianceSampler; #endif #else #define sampleReflection(s,c) texture2D(s,c) uniform sampler2D reflectionSampler; #ifdef LODBASEDMICROSFURACE #define sampleReflectionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform sampler2D reflectionSamplerLow; uniform sampler2D reflectionSamplerHigh; #endif #ifdef USEIRRADIANCEMAP uniform sampler2D irradianceSampler; #endif #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #endif #ifdef ENVIRONMENTBRDF uniform sampler2D environmentBrdfSampler; #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION #ifdef SS_REFRACTIONMAP_3D #define sampleRefraction(s,c) textureCube(s,c) uniform samplerCube refractionSampler; #ifdef LODBASEDMICROSFURACE #define sampleRefractionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube refractionSamplerLow; uniform samplerCube refractionSamplerHigh; #endif #else #define sampleRefraction(s,c) texture2D(s,c) uniform sampler2D refractionSampler; #ifdef LODBASEDMICROSFURACE #define sampleRefractionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform sampler2D refractionSamplerLow; uniform sampler2D refractionSamplerHigh; #endif #endif #endif #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness,_SAMPLERNAME_,thickness) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity,_SAMPLERNAME_,refractionIntensity) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity,_SAMPLERNAME_,translucencyIntensity) #endif";X.a.IncludesShadersStore.pbrFragmentSamplersDeclaration=rb;c(127);X.a.IncludesShadersStore.subSurfaceScatteringFunctions="bool testLightingForSSS(float diffusionProfile) { return diffusionProfile<1.; }";a=" vec3 hemisphereCosSample(vec2 u) { float phi=2.*PI*u.x; float cosTheta2=1.-u.y; float cosTheta=sqrt(cosTheta2); float sinTheta=sqrt(1.-cosTheta2); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); } vec3 hemisphereImportanceSampleDggx(vec2 u,float a) { float phi=2.*PI*u.x; float cosTheta2=(1.-u.y)/(1.+(a+1.)*((a-1.)*u.y)); float cosTheta=sqrt(cosTheta2); float sinTheta=sqrt(1.-cosTheta2); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); } vec3 hemisphereImportanceSampleDCharlie(vec2 u,float a) { float phi=2.*PI*u.x; float sinTheta=pow(u.y,a/(2.*a+1.)); float cosTheta=sqrt(1.-sinTheta*sinTheta); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); }";X.a.IncludesShadersStore.importanceSampling=a;rb=" #define RECIPROCAL_PI2 0.15915494 #define RECIPROCAL_PI 0.31830988618 #define MINIMUMVARIANCE 0.0005 float convertRoughnessToAverageSlope(float roughness) { return square(roughness)+MINIMUMVARIANCE; } float fresnelGrazingReflectance(float reflectance0) { float reflectance90=saturate(reflectance0*25.0); return reflectance90; } vec2 getAARoughnessFactors(vec3 normalVector) { #ifdef SPECULARAA vec3 nDfdx=dFdx(normalVector.xyz); vec3 nDfdy=dFdy(normalVector.xyz); float slopeSquare=max(dot(nDfdx,nDfdx),dot(nDfdy,nDfdy)); float geometricRoughnessFactor=pow(saturate(slopeSquare),0.333); float geometricAlphaGFactor=sqrt(slopeSquare); geometricAlphaGFactor*=0.75; return vec2(geometricRoughnessFactor,geometricAlphaGFactor); #else return vec2(0.); #endif } #ifdef ANISOTROPIC vec2 getAnisotropicRoughness(float alphaG,float anisotropy) { float alphaT=max(alphaG*(1.0+anisotropy),MINIMUMVARIANCE); float alphaB=max(alphaG*(1.0-anisotropy),MINIMUMVARIANCE); return vec2(alphaT,alphaB); } vec3 getAnisotropicBentNormals(const vec3 T,const vec3 B,const vec3 N,const vec3 V,float anisotropy) { vec3 anisotropicFrameDirection=anisotropy>=0.0 ? B : T; vec3 anisotropicFrameTangent=cross(normalize(anisotropicFrameDirection),V); vec3 anisotropicFrameNormal=cross(anisotropicFrameTangent,anisotropicFrameDirection); vec3 anisotropicNormal=normalize(mix(N,anisotropicFrameNormal,abs(anisotropy))); return anisotropicNormal; } #endif #if defined(CLEARCOAT) || defined(SS_REFRACTION) vec3 cocaLambert(vec3 alpha,float distance) { return exp(-alpha*distance); } vec3 cocaLambert(float NdotVRefract,float NdotLRefract,vec3 alpha,float thickness) { return cocaLambert(alpha,(thickness*((NdotLRefract+NdotVRefract)/(NdotLRefract*NdotVRefract)))); } vec3 computeColorAtDistanceInMedia(vec3 color,float distance) { return -log(color)/distance; } vec3 computeClearCoatAbsorption(float NdotVRefract,float NdotLRefract,vec3 clearCoatColor,float clearCoatThickness,float clearCoatIntensity) { vec3 clearCoatAbsorption=mix(vec3(1.0), cocaLambert(NdotVRefract,NdotLRefract,clearCoatColor,clearCoatThickness), clearCoatIntensity); return clearCoatAbsorption; } #endif #ifdef MICROSURFACEAUTOMATIC float computeDefaultMicroSurface(float microSurface,vec3 reflectivityColor) { const float kReflectivityNoAlphaWorkflow_SmoothnessMax=0.95; float reflectivityLuminance=getLuminance(reflectivityColor); float reflectivityLuma=sqrt(reflectivityLuminance); microSurface=reflectivityLuma*kReflectivityNoAlphaWorkflow_SmoothnessMax; return microSurface; } #endif";X.a.IncludesShadersStore.pbrHelperFunctions=rb;a="#ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS vec3 computeEnvironmentIrradiance(vec3 normal) { return vSphericalL00 +vSphericalL1_1*(normal.y) +vSphericalL10*(normal.z) +vSphericalL11*(normal.x) +vSphericalL2_2*(normal.y*normal.x) +vSphericalL2_1*(normal.y*normal.z) +vSphericalL20*((3.0*normal.z*normal.z)-1.0) +vSphericalL21*(normal.z*normal.x) +vSphericalL22*(normal.x*normal.x-(normal.y*normal.y)); } #else vec3 computeEnvironmentIrradiance(vec3 normal) { float Nx=normal.x; float Ny=normal.y; float Nz=normal.z; vec3 C1=vSphericalZZ.rgb; vec3 Cx=vSphericalX.rgb; vec3 Cy=vSphericalY.rgb; vec3 Cz=vSphericalZ.rgb; vec3 Cxx_zz=vSphericalXX_ZZ.rgb; vec3 Cyy_zz=vSphericalYY_ZZ.rgb; vec3 Cxy=vSphericalXY.rgb; vec3 Cyz=vSphericalYZ.rgb; vec3 Czx=vSphericalZX.rgb; vec3 a1=Cyy_zz*Ny+Cy; vec3 a2=Cyz*Nz+a1; vec3 b1=Czx*Nz+Cx; vec3 b2=Cxy*Ny+b1; vec3 b3=Cxx_zz*Nx+b2; vec3 t1=Cz*Nz+C1; vec3 t2=a2*Ny+t1; vec3 t3=b3*Nx+t2; return t3; } #endif #endif";X.a.IncludesShadersStore.harmonicsFunctions=a;rb=" struct preLightingInfo { vec3 lightOffset; float lightDistanceSquared; float lightDistance; float attenuation; vec3 L; vec3 H; float NdotV; float NdotLUnclamped; float NdotL; float VdotH; float roughness; }; preLightingInfo computePointAndSpotPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.lightOffset=lightData.xyz-vPositionW; result.lightDistanceSquared=dot(result.lightOffset,result.lightOffset); result.lightDistance=sqrt(result.lightDistanceSquared); result.L=normalize(result.lightOffset); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); result.NdotLUnclamped=dot(N,result.L); result.NdotL=saturateEps(result.NdotLUnclamped); return result; } preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.lightDistance=length(-lightData.xyz); result.L=normalize(-lightData.xyz); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); result.NdotLUnclamped=dot(N,result.L); result.NdotL=saturateEps(result.NdotLUnclamped); return result; } preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.NdotL=dot(N,lightData.xyz)*0.5+0.5; result.NdotL=saturateEps(result.NdotL); result.NdotLUnclamped=result.NdotL; #ifdef SPECULARTERM result.L=normalize(lightData.xyz); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); #endif return result; }";X.a.IncludesShadersStore.pbrDirectLightingSetupFunctions=rb;a="float computeDistanceLightFalloff_Standard(vec3 lightOffset,float range) { return max(0.,1.0-length(lightOffset)/range); } float computeDistanceLightFalloff_Physical(float lightDistanceSquared) { return 1.0/maxEps(lightDistanceSquared); } float computeDistanceLightFalloff_GLTF(float lightDistanceSquared,float inverseSquaredRange) { float lightDistanceFalloff=1.0/maxEps(lightDistanceSquared); float factor=lightDistanceSquared*inverseSquaredRange; float attenuation=saturate(1.0-factor*factor); attenuation*=attenuation; lightDistanceFalloff*=attenuation; return lightDistanceFalloff; } float computeDistanceLightFalloff(vec3 lightOffset,float lightDistanceSquared,float range,float inverseSquaredRange) { #ifdef USEPHYSICALLIGHTFALLOFF return computeDistanceLightFalloff_Physical(lightDistanceSquared); #elif defined(USEGLTFLIGHTFALLOFF) return computeDistanceLightFalloff_GLTF(lightDistanceSquared,inverseSquaredRange); #else return computeDistanceLightFalloff_Standard(lightOffset,range); #endif } float computeDirectionalLightFalloff_Standard(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle,float exponent) { float falloff=0.0; float cosAngle=maxEps(dot(-lightDirection,directionToLightCenterW)); if (cosAngle>=cosHalfAngle) { falloff=max(0.,pow(cosAngle,exponent)); } return falloff; } float computeDirectionalLightFalloff_Physical(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle) { const float kMinusLog2ConeAngleIntensityRatio=6.64385618977; float concentrationKappa=kMinusLog2ConeAngleIntensityRatio/(1.0-cosHalfAngle); vec4 lightDirectionSpreadSG=vec4(-lightDirection*concentrationKappa,-concentrationKappa); float falloff=exp2(dot(vec4(directionToLightCenterW,1.0),lightDirectionSpreadSG)); return falloff; } float computeDirectionalLightFalloff_GLTF(vec3 lightDirection,vec3 directionToLightCenterW,float lightAngleScale,float lightAngleOffset) { float cd=dot(-lightDirection,directionToLightCenterW); float falloff=saturate(cd*lightAngleScale+lightAngleOffset); falloff*=falloff; return falloff; } float computeDirectionalLightFalloff(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle,float exponent,float lightAngleScale,float lightAngleOffset) { #ifdef USEPHYSICALLIGHTFALLOFF return computeDirectionalLightFalloff_Physical(lightDirection,directionToLightCenterW,cosHalfAngle); #elif defined(USEGLTFLIGHTFALLOFF) return computeDirectionalLightFalloff_GLTF(lightDirection,directionToLightCenterW,lightAngleScale,lightAngleOffset); #else return computeDirectionalLightFalloff_Standard(lightDirection,directionToLightCenterW,cosHalfAngle,exponent); #endif }";X.a.IncludesShadersStore.pbrDirectLightingFalloffFunctions=a;rb=" #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 #ifdef MS_BRDF_ENERGY_CONSERVATION vec3 getEnergyConservationFactor(const vec3 specularEnvironmentR0,const vec3 environmentBrdf) { return 1.0+specularEnvironmentR0*(1.0/environmentBrdf.y-1.0); } #endif #ifdef ENVIRONMENTBRDF vec3 getBRDFLookup(float NdotV,float perceptualRoughness) { vec2 UV=vec2(NdotV,perceptualRoughness); vec4 brdfLookup=texture2D(environmentBrdfSampler,UV); #ifdef ENVIRONMENTBRDF_RGBD brdfLookup.rgb=fromRGBD(brdfLookup.rgba); #endif return brdfLookup.rgb; } vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0,const vec3 specularEnvironmentR90,const vec3 environmentBrdf) { #ifdef BRDF_V_HEIGHT_CORRELATED vec3 reflectance=(specularEnvironmentR90-specularEnvironmentR0)*environmentBrdf.x+specularEnvironmentR0*environmentBrdf.y; #else vec3 reflectance=specularEnvironmentR0*environmentBrdf.x+specularEnvironmentR90*environmentBrdf.y; #endif return reflectance; } vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0,const vec3 environmentBrdf) { #ifdef BRDF_V_HEIGHT_CORRELATED vec3 reflectance=mix(environmentBrdf.xxx,environmentBrdf.yyy,specularEnvironmentR0); #else vec3 reflectance=specularEnvironmentR0*environmentBrdf.x+environmentBrdf.y; #endif return reflectance; } #endif #if !defined(ENVIRONMENTBRDF) || defined(REFLECTIONMAP_SKYBOX) || defined(ALPHAFRESNEL) vec3 getReflectanceFromAnalyticalBRDFLookup_Jones(float VdotN,vec3 reflectance0,vec3 reflectance90,float smoothness) { float weight=mix(FRESNEL_MAXIMUM_ON_ROUGH,1.0,smoothness); return reflectance0+weight*(reflectance90-reflectance0)*pow5(saturate(1.0-VdotN)); } #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) vec3 getSheenReflectanceFromBRDFLookup(const vec3 reflectance0,const vec3 environmentBrdf) { vec3 sheenEnvironmentReflectance=reflectance0*environmentBrdf.b; return sheenEnvironmentReflectance; } #endif vec3 fresnelSchlickGGX(float VdotH,vec3 reflectance0,vec3 reflectance90) { return reflectance0+(reflectance90-reflectance0)*pow5(1.0-VdotH); } float fresnelSchlickGGX(float VdotH,float reflectance0,float reflectance90) { return reflectance0+(reflectance90-reflectance0)*pow5(1.0-VdotH); } #ifdef CLEARCOAT vec3 getR0RemappedForClearCoat(vec3 f0) { #ifdef CLEARCOAT_DEFAULTIOR #ifdef MOBILE return saturate(f0*(f0*0.526868+0.529324)-0.0482256); #else return saturate(f0*(f0*(0.941892-0.263008*f0)+0.346479)-0.0285998); #endif #else vec3 s=sqrt(f0); vec3 t=(vClearCoatRefractionParams.z+vClearCoatRefractionParams.w*s)/(vClearCoatRefractionParams.w+vClearCoatRefractionParams.z*s); return t*t; #endif } #endif float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH,float alphaG) { float a2=square(alphaG); float d=NdotH*NdotH*(a2-1.0)+1.0; return a2/(PI*d*d); } #ifdef SHEEN float normalDistributionFunction_CharlieSheen(float NdotH,float alphaG) { float invR=1./alphaG; float cos2h=NdotH*NdotH; float sin2h=1.-cos2h; return (2.+invR)*pow(sin2h,invR*.5)/(2.*PI); } #endif #ifdef ANISOTROPIC float normalDistributionFunction_BurleyGGX_Anisotropic(float NdotH,float TdotH,float BdotH,const vec2 alphaTB) { float a2=alphaTB.x*alphaTB.y; vec3 v=vec3(alphaTB.y*TdotH,alphaTB.x*BdotH,a2*NdotH); float v2=dot(v,v); float w2=a2/v2; return a2*w2*w2*RECIPROCAL_PI; } #endif #ifdef BRDF_V_HEIGHT_CORRELATED float smithVisibility_GGXCorrelated(float NdotL,float NdotV,float alphaG) { #ifdef MOBILE float GGXV=NdotL*(NdotV*(1.0-alphaG)+alphaG); float GGXL=NdotV*(NdotL*(1.0-alphaG)+alphaG); return 0.5/(GGXV+GGXL); #else float a2=alphaG*alphaG; float GGXV=NdotL*sqrt(NdotV*(NdotV-a2*NdotV)+a2); float GGXL=NdotV*sqrt(NdotL*(NdotL-a2*NdotL)+a2); return 0.5/(GGXV+GGXL); #endif } #else float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot,float alphaG) { #ifdef MOBILE return 1.0/(dot+alphaG+(1.0-alphaG)*dot )); #else float alphaSquared=alphaG*alphaG; return 1.0/(dot+sqrt(alphaSquared+(1.0-alphaSquared)*dot*dot)); #endif } float smithVisibility_TrowbridgeReitzGGXFast(float NdotL,float NdotV,float alphaG) { float visibility=smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL,alphaG)*smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV,alphaG); return visibility; } #endif #ifdef ANISOTROPIC float smithVisibility_GGXCorrelated_Anisotropic(float NdotL,float NdotV,float TdotV,float BdotV,float TdotL,float BdotL,const vec2 alphaTB) { float lambdaV=NdotL*length(vec3(alphaTB.x*TdotV,alphaTB.y*BdotV,NdotV)); float lambdaL=NdotV*length(vec3(alphaTB.x*TdotL,alphaTB.y*BdotL,NdotL)); float v=0.5/(lambdaV+lambdaL); return v; } #endif #ifdef CLEARCOAT float visibility_Kelemen(float VdotH) { return 0.25/(VdotH*VdotH); } #endif #ifdef SHEEN float visibility_Ashikhmin(float NdotL,float NdotV) { return 1./(4.*(NdotL+NdotV-NdotL*NdotV)); } #endif float diffuseBRDF_Burley(float NdotL,float NdotV,float VdotH,float roughness) { float diffuseFresnelNV=pow5(saturateEps(1.0-NdotL)); float diffuseFresnelNL=pow5(saturateEps(1.0-NdotV)); float diffuseFresnel90=0.5+2.0*VdotH*VdotH*roughness; float fresnel = (1.0+(diffuseFresnel90-1.0)*diffuseFresnelNL) * (1.0+(diffuseFresnel90-1.0)*diffuseFresnelNV); return fresnel/PI; } #ifdef SS_TRANSLUCENCY vec3 transmittanceBRDF_Burley(const vec3 tintColor,const vec3 diffusionDistance,float thickness) { vec3 S=1./maxEps(diffusionDistance); vec3 temp=exp((-0.333333333*thickness)*S); return tintColor.rgb*0.25*(temp*temp*temp+3.0*temp); } float computeWrappedDiffuseNdotL(float NdotL,float w) { float t=1.0+w; float invt2=1.0/square(t); return saturate((NdotL+w)*invt2); } #endif ";X.a.IncludesShadersStore.pbrBRDFFunctions=rb;a="#ifdef NUM_SAMPLES #if NUM_SAMPLES>0 #if defined(WEBGL2) || defined(WEBGPU) float radicalInverse_VdC(uint bits) { bits=(bits << 16u) | (bits >> 16u); bits=((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits=((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits=((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits=((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits)*2.3283064365386963e-10; } vec2 hammersley(uint i,uint N) { return vec2(float(i)/float(N),radicalInverse_VdC(i)); } #else float vanDerCorpus(int n,int base) { float invBase=1.0/float(base); float denom=1.0; float result=0.0; for(int i=0; i<32; ++i) { if(n>0) { denom=mod(float(n),2.0); result+=denom*invBase; invBase=invBase/2.0; n=int(float(n)/2.0); } } return result; } vec2 hammersley(int i,int N) { return vec2(float(i)/float(N),vanDerCorpus(i,2)); } #endif float log4(float x) { return log2(x)/2.; } const float NUM_SAMPLES_FLOAT=float(NUM_SAMPLES); const float NUM_SAMPLES_FLOAT_INVERSED=1./NUM_SAMPLES_FLOAT; const float K=4.; #define inline vec3 irradiance(samplerCube inputTexture,vec3 inputN,vec2 filteringInfo) { vec3 n=normalize(inputN); vec3 result=vec3(0.0); vec3 tangent=abs(n.z)<0.999 ? vec3(0.,0.,1.) : vec3(1.,0.,0.); tangent=normalize(cross(tangent,n)); vec3 bitangent=cross(n,tangent); mat3 tbn=mat3(tangent,bitangent,n); float maxLevel=filteringInfo.y; float dim0=filteringInfo.x; float omegaP=(4.*PI)/(6.*dim0*dim0); #if defined(WEBGL2) || defined(WEBGPU) for(uint i=0u; i0.) { float pdf_inversed=PI/NoL; float omegaS=NUM_SAMPLES_FLOAT_INVERSED*pdf_inversed; float l=log4(omegaS)-log4(omegaP)+log4(K); float mipLevel=clamp(l,0.0,maxLevel); vec3 c=textureCubeLodEXT(inputTexture,tbn*Ls,mipLevel).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif result+=c; } } result=result*NUM_SAMPLES_FLOAT_INVERSED; return result; } #define inline vec3 radiance(float alphaG,samplerCube inputTexture,vec3 inputN,vec2 filteringInfo) { vec3 n=normalize(inputN); if (alphaG == 0.) { vec3 c=textureCube(inputTexture,n).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif return c; } else { vec3 result=vec3(0.); vec3 tangent=abs(n.z)<0.999 ? vec3(0.,0.,1.) : vec3(1.,0.,0.); tangent=normalize(cross(tangent,n)); vec3 bitangent=cross(n,tangent); mat3 tbn=mat3(tangent,bitangent,n); float maxLevel=filteringInfo.y; float dim0=filteringInfo.x; float omegaP=(4.*PI)/(6.*dim0*dim0); float weight=0.; #if defined(WEBGL2) || defined(WEBGPU) for(uint i=0u; i0.) { float pdf_inversed=4./normalDistributionFunction_TrowbridgeReitzGGX(NoH,alphaG); float omegaS=NUM_SAMPLES_FLOAT_INVERSED*pdf_inversed; float l=log4(omegaS)-log4(omegaP)+log4(K); float mipLevel=clamp(float(l),0.0,maxLevel); weight+=NoL; vec3 c=textureCubeLodEXT(inputTexture,tbn*L,mipLevel).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif result+=c*NoL; } } result=result/weight; return result; } } #endif #endif";X.a.IncludesShadersStore.hdrFilteringFunctions=a;rb="#define CLEARCOATREFLECTANCE90 1.0 struct lightingInfo { vec3 diffuse; #ifdef SPECULARTERM vec3 specular; #endif #ifdef CLEARCOAT vec4 clearCoat; #endif #ifdef SHEEN vec3 sheen; #endif }; float adjustRoughnessFromLightProperties(float roughness,float lightRadius,float lightDistance) { #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF) float lightRoughness=lightRadius/lightDistance; float totalRoughness=saturate(lightRoughness+roughness); return totalRoughness; #else return roughness; #endif } vec3 computeHemisphericDiffuseLighting(preLightingInfo info,vec3 lightColor,vec3 groundColor) { return mix(groundColor,lightColor,info.NdotL); } vec3 computeDiffuseLighting(preLightingInfo info,vec3 lightColor) { float diffuseTerm=diffuseBRDF_Burley(info.NdotL,info.NdotV,info.VdotH,info.roughness); return diffuseTerm*info.attenuation*info.NdotL*lightColor; } #define inline vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler,mat4 textureProjectionMatrix){ vec4 strq=textureProjectionMatrix*vec4(vPositionW,1.0); strq/=strq.w; vec3 textureColor=texture2D(projectionLightSampler,strq.xy).rgb; return toLinearSpace(textureColor); } #ifdef SS_TRANSLUCENCY vec3 computeDiffuseAndTransmittedLighting(preLightingInfo info,vec3 lightColor,vec3 transmittance) { float NdotL=absEps(info.NdotLUnclamped); float wrapNdotL=computeWrappedDiffuseNdotL(NdotL,0.02); float trAdapt=step(0.,info.NdotLUnclamped); vec3 transmittanceNdotL=mix(transmittance*wrapNdotL,vec3(wrapNdotL),trAdapt); float diffuseTerm=diffuseBRDF_Burley(NdotL,info.NdotV,info.VdotH,info.roughness); return diffuseTerm*transmittanceNdotL*info.attenuation*lightColor; } #endif #ifdef SPECULARTERM vec3 computeSpecularLighting(preLightingInfo info,vec3 N,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float roughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(roughness); vec3 fresnel=fresnelSchlickGGX(info.VdotH,reflectance0,reflectance90); float distribution=normalDistributionFunction_TrowbridgeReitzGGX(NdotH,alphaG); #ifdef BRDF_V_HEIGHT_CORRELATED float smithVisibility=smithVisibility_GGXCorrelated(info.NdotL,info.NdotV,alphaG); #else float smithVisibility=smithVisibility_TrowbridgeReitzGGXFast(info.NdotL,info.NdotV,alphaG); #endif vec3 specTerm=fresnel*distribution*smithVisibility; return specTerm*info.attenuation*info.NdotL*lightColor; } #endif #ifdef ANISOTROPIC vec3 computeAnisotropicSpecularLighting(preLightingInfo info,vec3 V,vec3 N,vec3 T,vec3 B,float anisotropy,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float TdotH=dot(T,info.H); float BdotH=dot(B,info.H); float TdotV=dot(T,V); float BdotV=dot(B,V); float TdotL=dot(T,info.L); float BdotL=dot(B,info.L); float alphaG=convertRoughnessToAverageSlope(info.roughness); vec2 alphaTB=getAnisotropicRoughness(alphaG,anisotropy); alphaTB=max(alphaTB,square(geometricRoughnessFactor)); vec3 fresnel=fresnelSchlickGGX(info.VdotH,reflectance0,reflectance90); float distribution=normalDistributionFunction_BurleyGGX_Anisotropic(NdotH,TdotH,BdotH,alphaTB); float smithVisibility=smithVisibility_GGXCorrelated_Anisotropic(info.NdotL,info.NdotV,TdotV,BdotV,TdotL,BdotL,alphaTB); vec3 specTerm=fresnel*distribution*smithVisibility; return specTerm*info.attenuation*info.NdotL*lightColor; } #endif #ifdef CLEARCOAT vec4 computeClearCoatLighting(preLightingInfo info,vec3 Ncc,float geometricRoughnessFactor,float clearCoatIntensity,vec3 lightColor) { float NccdotL=saturateEps(dot(Ncc,info.L)); float NccdotH=saturateEps(dot(Ncc,info.H)); float clearCoatRoughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(clearCoatRoughness); float fresnel=fresnelSchlickGGX(info.VdotH,vClearCoatRefractionParams.x,CLEARCOATREFLECTANCE90); fresnel*=clearCoatIntensity; float distribution=normalDistributionFunction_TrowbridgeReitzGGX(NccdotH,alphaG); float kelemenVisibility=visibility_Kelemen(info.VdotH); float clearCoatTerm=fresnel*distribution*kelemenVisibility; return vec4( clearCoatTerm*info.attenuation*NccdotL*lightColor, 1.0-fresnel ); } vec3 computeClearCoatLightingAbsorption(float NdotVRefract,vec3 L,vec3 Ncc,vec3 clearCoatColor,float clearCoatThickness,float clearCoatIntensity) { vec3 LRefract=-refract(L,Ncc,vClearCoatRefractionParams.y); float NdotLRefract=saturateEps(dot(Ncc,LRefract)); vec3 absorption=computeClearCoatAbsorption(NdotVRefract,NdotLRefract,clearCoatColor,clearCoatThickness,clearCoatIntensity); return absorption; } #endif #ifdef SHEEN vec3 computeSheenLighting(preLightingInfo info,vec3 N,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float roughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(roughness); float fresnel=1.; float distribution=normalDistributionFunction_CharlieSheen(NdotH,alphaG); float visibility=visibility_Ashikhmin(info.NdotL,info.NdotV); float sheenTerm=fresnel*distribution*visibility; return sheenTerm*info.attenuation*info.NdotL*lightColor; } #endif ";X.a.IncludesShadersStore.pbrDirectLightingFunctions=rb;a="#if defined(REFLECTION) || defined(SS_REFRACTION) float getLodFromAlphaG(float cubeMapDimensionPixels,float microsurfaceAverageSlope) { float microsurfaceAverageSlopeTexels=cubeMapDimensionPixels*microsurfaceAverageSlope; float lod=log2(microsurfaceAverageSlopeTexels); return lod; } float getLinearLodFromRoughness(float cubeMapDimensionPixels,float roughness) { float lod=log2(cubeMapDimensionPixels)*roughness; return lod; } #endif #if defined(ENVIRONMENTBRDF) && defined(RADIANCEOCCLUSION) float environmentRadianceOcclusion(float ambientOcclusion,float NdotVUnclamped) { float temp=NdotVUnclamped+ambientOcclusion; return saturate(square(temp)-1.0+ambientOcclusion); } #endif #if defined(ENVIRONMENTBRDF) && defined(HORIZONOCCLUSION) float environmentHorizonOcclusion(vec3 view,vec3 normal,vec3 geometricNormal) { vec3 reflection=reflect(view,normal); float temp=saturate(1.0+1.1*dot(reflection,geometricNormal)); return square(temp); } #endif #if defined(LODINREFLECTIONALPHA) || defined(SS_LODINREFRACTIONALPHA) #define UNPACK_LOD(x) (1.0-x)*255.0 float getLodFromAlphaG(float cubeMapDimensionPixels,float alphaG,float NdotV) { float microsurfaceAverageSlope=alphaG; microsurfaceAverageSlope*=sqrt(abs(NdotV)); return getLodFromAlphaG(cubeMapDimensionPixels,microsurfaceAverageSlope); } #endif";X.a.IncludesShadersStore.pbrIBLFunctions=a;c(154),c(155);rb="struct albedoOpacityOutParams { vec3 surfaceAlbedo; float alpha; }; #define pbr_inline void albedoOpacityBlock( in vec4 vAlbedoColor, #ifdef ALBEDO in vec4 albedoTexture, in vec2 albedoInfos, #endif #ifdef OPACITY in vec4 opacityMap, in vec2 vOpacityInfos, #endif #ifdef DETAIL in vec4 detailColor, in vec4 vDetailInfos, #endif out albedoOpacityOutParams outParams ) { vec3 surfaceAlbedo=vAlbedoColor.rgb; float alpha=vAlbedoColor.a; #ifdef ALBEDO #if defined(ALPHAFROMALBEDO) || defined(ALPHATEST) alpha*=albedoTexture.a; #endif #ifdef GAMMAALBEDO surfaceAlbedo*=toLinearSpace(albedoTexture.rgb); #else surfaceAlbedo*=albedoTexture.rgb; #endif surfaceAlbedo*=albedoInfos.y; #endif #ifdef VERTEXCOLOR surfaceAlbedo*=vColor.rgb; #endif #ifdef DETAIL float detailAlbedo=2.0*mix(0.5,detailColor.r,vDetailInfos.y); surfaceAlbedo.rgb=surfaceAlbedo.rgb*detailAlbedo*detailAlbedo; #endif #define CUSTOM_FRAGMENT_UPDATE_ALBEDO #ifdef OPACITY #ifdef OPACITYRGB alpha=getLuminance(opacityMap.rgb); #else alpha*=opacityMap.a; #endif alpha*=vOpacityInfos.y; #endif #ifdef VERTEXALPHA alpha*=vColor.a; #endif #if !defined(SS_LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL) #ifdef ALPHATEST if (alpha0 vec4 surfaceMetallicColorMap; vec4 surfaceReflectivityColorMap; vec2 metallicRoughness; vec3 metallicF0; #endif }; #define pbr_inline void reflectivityBlock( in vec4 vReflectivityColor, #ifdef METALLICWORKFLOW in vec3 surfaceAlbedo, in vec4 metallicReflectanceFactors, #endif #ifdef REFLECTIVITY in vec3 reflectivityInfos, in vec4 surfaceMetallicOrReflectivityColorMap, #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) in vec3 ambientOcclusionColorIn, #endif #ifdef MICROSURFACEMAP in vec4 microSurfaceTexel, #endif #ifdef DETAIL in vec4 detailColor, in vec4 vDetailInfos, #endif out reflectivityOutParams outParams ) { float microSurface=vReflectivityColor.a; vec3 surfaceReflectivityColor=vReflectivityColor.rgb; #ifdef METALLICWORKFLOW vec2 metallicRoughness=surfaceReflectivityColor.rg; #ifdef REFLECTIVITY #if DEBUGMODE>0 outParams.surfaceMetallicColorMap=surfaceMetallicOrReflectivityColorMap; #endif #ifdef AOSTOREINMETALMAPRED vec3 aoStoreInMetalMap=vec3(surfaceMetallicOrReflectivityColorMap.r,surfaceMetallicOrReflectivityColorMap.r,surfaceMetallicOrReflectivityColorMap.r); outParams.ambientOcclusionColor=mix(ambientOcclusionColorIn,aoStoreInMetalMap,reflectivityInfos.z); #endif #ifdef METALLNESSSTOREINMETALMAPBLUE metallicRoughness.r*=surfaceMetallicOrReflectivityColorMap.b; #else metallicRoughness.r*=surfaceMetallicOrReflectivityColorMap.r; #endif #ifdef ROUGHNESSSTOREINMETALMAPALPHA metallicRoughness.g*=surfaceMetallicOrReflectivityColorMap.a; #else #ifdef ROUGHNESSSTOREINMETALMAPGREEN metallicRoughness.g*=surfaceMetallicOrReflectivityColorMap.g; #endif #endif #endif #ifdef DETAIL float detailRoughness=mix(0.5,detailColor.b,vDetailInfos.w); float loLerp=mix(0.,metallicRoughness.g,detailRoughness*2.); float hiLerp=mix(metallicRoughness.g,1.,(detailRoughness-0.5)*2.); metallicRoughness.g=mix(loLerp,hiLerp,step(detailRoughness,0.5)); #endif #ifdef MICROSURFACEMAP metallicRoughness.g*=microSurfaceTexel.r; #endif #if DEBUGMODE>0 outParams.metallicRoughness=metallicRoughness; #endif #define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS microSurface=1.0-metallicRoughness.g; vec3 baseColor=surfaceAlbedo; #ifdef FROSTBITE_REFLECTANCE outParams.surfaceAlbedo=baseColor.rgb*(1.0-metallicRoughness.r); surfaceReflectivityColor=mix(0.16*reflectance*reflectance,baseColor,metallicRoughness.r); #else vec3 metallicF0=metallicReflectanceFactors.rgb; #if DEBUGMODE>0 outParams.metallicF0=metallicF0; #endif outParams.surfaceAlbedo=mix(baseColor.rgb*(1.0-metallicF0),vec3(0.,0.,0.),metallicRoughness.r); surfaceReflectivityColor=mix(metallicF0,baseColor,metallicRoughness.r); #endif #else #ifdef REFLECTIVITY surfaceReflectivityColor*=surfaceMetallicOrReflectivityColorMap.rgb; #if DEBUGMODE>0 outParams.surfaceReflectivityColorMap=surfaceMetallicOrReflectivityColorMap; #endif #ifdef MICROSURFACEFROMREFLECTIVITYMAP microSurface*=surfaceMetallicOrReflectivityColorMap.a; microSurface*=reflectivityInfos.z; #else #ifdef MICROSURFACEAUTOMATIC microSurface*=computeDefaultMicroSurface(microSurface,surfaceReflectivityColor); #endif #ifdef MICROSURFACEMAP microSurface*=microSurfaceTexel.r; #endif #define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE #endif #endif #endif microSurface=saturate(microSurface); float roughness=1.-microSurface; outParams.microSurface=microSurface; outParams.roughness=roughness; outParams.surfaceReflectivityColor=surfaceReflectivityColor; } ";X.a.IncludesShadersStore.pbrBlockReflectivity=a;rb="struct ambientOcclusionOutParams { vec3 ambientOcclusionColor; #if DEBUGMODE>0 vec3 ambientOcclusionColorMap; #endif }; #define pbr_inline void ambientOcclusionBlock( #ifdef AMBIENT in vec3 ambientOcclusionColorMap_, in vec4 vAmbientInfos, #endif out ambientOcclusionOutParams outParams ) { vec3 ambientOcclusionColor=vec3(1.,1.,1.); #ifdef AMBIENT vec3 ambientOcclusionColorMap=ambientOcclusionColorMap_*vAmbientInfos.y; #ifdef AMBIENTINGRAYSCALE ambientOcclusionColorMap=vec3(ambientOcclusionColorMap.r,ambientOcclusionColorMap.r,ambientOcclusionColorMap.r); #endif ambientOcclusionColor=mix(ambientOcclusionColor,ambientOcclusionColorMap,vAmbientInfos.z); #if DEBUGMODE>0 outParams.ambientOcclusionColorMap=ambientOcclusionColorMap; #endif #endif outParams.ambientOcclusionColor=ambientOcclusionColor; } ";X.a.IncludesShadersStore.pbrBlockAmbientOcclusion=rb;a="#ifdef ALPHAFRESNEL #if defined(ALPHATEST) || defined(ALPHABLEND) struct alphaFresnelOutParams { float alpha; }; #define pbr_inline void alphaFresnelBlock( in vec3 normalW, in vec3 viewDirectionW, in float alpha, in float microSurface, out alphaFresnelOutParams outParams ) { float opacityPerceptual=alpha; #ifdef LINEARALPHAFRESNEL float opacity0=opacityPerceptual; #else float opacity0=opacityPerceptual*opacityPerceptual; #endif float opacity90=fresnelGrazingReflectance(opacity0); vec3 normalForward=faceforward(normalW,-viewDirectionW,normalW); outParams.alpha=getReflectanceFromAnalyticalBRDFLookup_Jones(saturate(dot(viewDirectionW,normalForward)),vec3(opacity0),vec3(opacity90),sqrt(microSurface)).x; #ifdef ALPHATEST if (outParams.alpha0 vec3 anisotropyMapData; #endif }; #define pbr_inline void anisotropicBlock( in vec3 vAnisotropy, #ifdef ANISOTROPIC_TEXTURE in vec3 anisotropyMapData, #endif in mat3 TBN, in vec3 normalW, in vec3 viewDirectionW, out anisotropicOutParams outParams ) { float anisotropy=vAnisotropy.b; vec3 anisotropyDirection=vec3(vAnisotropy.xy,0.); #ifdef ANISOTROPIC_TEXTURE anisotropy*=anisotropyMapData.b; anisotropyDirection.rg*=anisotropyMapData.rg*2.0-1.0; #if DEBUGMODE>0 outParams.anisotropyMapData=anisotropyMapData; #endif #endif mat3 anisoTBN=mat3(normalize(TBN[0]),normalize(TBN[1]),normalize(TBN[2])); vec3 anisotropicTangent=normalize(anisoTBN*anisotropyDirection); vec3 anisotropicBitangent=normalize(cross(anisoTBN[2],anisotropicTangent)); outParams.anisotropy=anisotropy; outParams.anisotropicTangent=anisotropicTangent; outParams.anisotropicBitangent=anisotropicBitangent; outParams.anisotropicNormal=getAnisotropicBentNormals(anisotropicTangent,anisotropicBitangent,normalW,viewDirectionW,anisotropy); } #endif ";X.a.IncludesShadersStore.pbrBlockAnisotropic=rb;a="#ifdef REFLECTION struct reflectionOutParams { vec4 environmentRadiance; vec3 environmentIrradiance; #ifdef REFLECTIONMAP_3D vec3 reflectionCoords; #else vec2 reflectionCoords; #endif #ifdef SS_TRANSLUCENCY #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) vec3 irradianceVector; #endif #endif #endif }; #define pbr_inline void createReflectionCoords( in vec3 vPositionW, in vec3 normalW, #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #ifdef REFLECTIONMAP_3D out vec3 reflectionCoords #else out vec2 reflectionCoords #endif ) { #ifdef ANISOTROPIC vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),anisotropicOut.anisotropicNormal); #else vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #endif #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D reflectionCoords=reflectionVector; #else reflectionCoords=reflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION reflectionCoords/=reflectionVector.z; #endif reflectionCoords.y=1.0-reflectionCoords.y; #endif } #define pbr_inline #define inline void sampleReflectionTexture( in float alphaG, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) in float NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION in float roughness, #endif #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, const vec3 reflectionCoords, #else in sampler2D reflectionSampler, const vec2 reflectionCoords, #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif out vec4 environmentRadiance ) { #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) float reflectionLOD=getLodFromAlphaG(vReflectionMicrosurfaceInfos.x,alphaG,NdotVUnclamped); #elif defined(LINEARSPECULARREFLECTION) float reflectionLOD=getLinearLodFromRoughness(vReflectionMicrosurfaceInfos.x,roughness); #else float reflectionLOD=getLodFromAlphaG(vReflectionMicrosurfaceInfos.x,alphaG); #endif #ifdef LODBASEDMICROSFURACE reflectionLOD=reflectionLOD*vReflectionMicrosurfaceInfos.y+vReflectionMicrosurfaceInfos.z; #ifdef LODINREFLECTIONALPHA float automaticReflectionLOD=UNPACK_LOD(sampleReflection(reflectionSampler,reflectionCoords).a); float requestedReflectionLOD=max(automaticReflectionLOD,reflectionLOD); #else float requestedReflectionLOD=reflectionLOD; #endif #ifdef REALTIME_FILTERING environmentRadiance=vec4(radiance(alphaG,reflectionSampler,reflectionCoords,vReflectionFilteringInfo),1.0); #else environmentRadiance=sampleReflectionLod(reflectionSampler,reflectionCoords,reflectionLOD); #endif #else float lodReflectionNormalized=saturate(reflectionLOD/log2(vReflectionMicrosurfaceInfos.x)); float lodReflectionNormalizedDoubled=lodReflectionNormalized*2.0; vec4 environmentMid=sampleReflection(reflectionSampler,reflectionCoords); if (lodReflectionNormalizedDoubled<1.0){ environmentRadiance=mix( sampleReflection(reflectionSamplerHigh,reflectionCoords), environmentMid, lodReflectionNormalizedDoubled ); } else { environmentRadiance=mix( environmentMid, sampleReflection(reflectionSamplerLow,reflectionCoords), lodReflectionNormalizedDoubled-1.0 ); } #endif #ifdef RGBDREFLECTION environmentRadiance.rgb=fromRGBD(environmentRadiance); #endif #ifdef GAMMAREFLECTION environmentRadiance.rgb=toLinearSpace(environmentRadiance.rgb); #endif environmentRadiance.rgb*=vReflectionInfos.x; environmentRadiance.rgb*=vReflectionColor.rgb; } #define pbr_inline #define inline void reflectionBlock( in vec3 vPositionW, in vec3 normalW, in float alphaG, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) in float NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION in float roughness, #endif #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, #else in sampler2D reflectionSampler, #endif #if defined(NORMAL) && defined(USESPHERICALINVERTEX) in vec3 vEnvironmentIrradiance, #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) in mat4 reflectionMatrix, #endif #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D in samplerCube irradianceSampler, #else in sampler2D irradianceSampler, #endif #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif out reflectionOutParams outParams ) { vec4 environmentRadiance=vec4(0.,0.,0.,0.); #ifdef REFLECTIONMAP_3D vec3 reflectionCoords=vec3(0.); #else vec2 reflectionCoords=vec2(0.); #endif createReflectionCoords( vPositionW, normalW, #ifdef ANISOTROPIC anisotropicOut, #endif reflectionCoords ); sampleReflectionTexture( alphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION roughness, #endif #ifdef REFLECTIONMAP_3D reflectionSampler, reflectionCoords, #else reflectionSampler, reflectionCoords, #endif #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentRadiance ); vec3 environmentIrradiance=vec3(0.,0.,0.); #ifdef USESPHERICALFROMREFLECTIONMAP #if defined(NORMAL) && defined(USESPHERICALINVERTEX) environmentIrradiance=vEnvironmentIrradiance; #else #ifdef ANISOTROPIC vec3 irradianceVector=vec3(reflectionMatrix*vec4(anisotropicOut.anisotropicNormal,0)).xyz; #else vec3 irradianceVector=vec3(reflectionMatrix*vec4(normalW,0)).xyz; #endif #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z*=-1.0; #endif #ifdef INVERTCUBICMAP irradianceVector.y*=-1.0; #endif #if defined(REALTIME_FILTERING) environmentIrradiance=irradiance(reflectionSampler,irradianceVector,vReflectionFilteringInfo); #else environmentIrradiance=computeEnvironmentIrradiance(irradianceVector); #endif #ifdef SS_TRANSLUCENCY outParams.irradianceVector=irradianceVector; #endif #endif #elif defined(USEIRRADIANCEMAP) vec4 environmentIrradiance4=sampleReflection(irradianceSampler,reflectionCoords); environmentIrradiance=environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance.rgb=fromRGBD(environmentIrradiance4); #endif #ifdef GAMMAREFLECTION environmentIrradiance.rgb=toLinearSpace(environmentIrradiance.rgb); #endif #endif environmentIrradiance*=vReflectionColor.rgb; outParams.environmentRadiance=environmentRadiance; outParams.environmentIrradiance=environmentIrradiance; outParams.reflectionCoords=reflectionCoords; } #endif ";X.a.IncludesShadersStore.pbrBlockReflection=a;rb="#ifdef SHEEN struct sheenOutParams { float sheenIntensity; vec3 sheenColor; float sheenRoughness; #ifdef SHEEN_LINKWITHALBEDO vec3 surfaceAlbedo; #endif #if defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) float sheenAlbedoScaling; #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) vec3 finalSheenRadianceScaled; #endif #if DEBUGMODE>0 vec4 sheenMapData; vec3 sheenEnvironmentReflectance; #endif }; #define pbr_inline #define inline void sheenBlock( in vec4 vSheenColor, #ifdef SHEEN_ROUGHNESS in float vSheenRoughness, #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) in vec4 sheenMapRoughnessData, #endif #endif in float roughness, #ifdef SHEEN_TEXTURE in vec4 sheenMapData, in float sheenMapLevel, #endif in float reflectance, #ifdef SHEEN_LINKWITHALBEDO in vec3 baseColor, in vec3 surfaceAlbedo, #endif #ifdef ENVIRONMENTBRDF in float NdotV, in vec3 environmentBrdf, #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) in vec2 AARoughnessFactors, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, in vec4 vLightingIntensity, #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, in vec3 reflectionCoords, #else in sampler2D reflectionSampler, in vec2 reflectionCoords, #endif in float NdotVUnclamped, #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) in float seo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) in float eho, #endif #endif out sheenOutParams outParams ) { float sheenIntensity=vSheenColor.a; #ifdef SHEEN_TEXTURE #if DEBUGMODE>0 outParams.sheenMapData=sheenMapData; #endif #endif #ifdef SHEEN_LINKWITHALBEDO float sheenFactor=pow5(1.0-sheenIntensity); vec3 sheenColor=baseColor.rgb*(1.0-sheenFactor); float sheenRoughness=sheenIntensity; outParams.surfaceAlbedo=surfaceAlbedo*sheenFactor; #ifdef SHEEN_TEXTURE sheenIntensity*=sheenMapData.a; #endif #else vec3 sheenColor=vSheenColor.rgb; #ifdef SHEEN_TEXTURE #ifdef SHEEN_GAMMATEXTURE sheenColor.rgb*=toLinearSpace(sheenMapData.rgb); #else sheenColor.rgb*=sheenMapData.rgb; #endif sheenColor.rgb*=sheenMapLevel; #endif #ifdef SHEEN_ROUGHNESS float sheenRoughness=vSheenRoughness; #ifdef SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE #if defined(SHEEN_TEXTURE) sheenRoughness*=sheenMapData.a; #endif #elif defined(SHEEN_TEXTURE_ROUGHNESS) #ifdef SHEEN_TEXTURE_ROUGHNESS_IDENTICAL sheenRoughness*=sheenMapData.a; #else sheenRoughness*=sheenMapRoughnessData.a; #endif #endif #else float sheenRoughness=roughness; #ifdef SHEEN_TEXTURE sheenIntensity*=sheenMapData.a; #endif #endif #if !defined(SHEEN_ALBEDOSCALING) sheenIntensity*=(1.-reflectance); #endif sheenColor*=sheenIntensity; #endif #ifdef ENVIRONMENTBRDF #ifdef SHEEN_ROUGHNESS vec3 environmentSheenBrdf=getBRDFLookup(NdotV,sheenRoughness); #else vec3 environmentSheenBrdf=environmentBrdf; #endif #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) float sheenAlphaG=convertRoughnessToAverageSlope(sheenRoughness); #ifdef SPECULARAA sheenAlphaG+=AARoughnessFactors.y; #endif vec4 environmentSheenRadiance=vec4(0.,0.,0.,0.); sampleReflectionTexture( sheenAlphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION sheenRoughness, #endif reflectionSampler, reflectionCoords, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentSheenRadiance ); vec3 sheenEnvironmentReflectance=getSheenReflectanceFromBRDFLookup(sheenColor,environmentSheenBrdf); #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) sheenEnvironmentReflectance*=seo; #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) sheenEnvironmentReflectance*=eho; #endif #if DEBUGMODE>0 outParams.sheenEnvironmentReflectance=sheenEnvironmentReflectance; #endif outParams.finalSheenRadianceScaled= environmentSheenRadiance.rgb * sheenEnvironmentReflectance * vLightingIntensity.z; #endif #if defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) outParams.sheenAlbedoScaling=1.0-sheenIntensity*max(max(sheenColor.r,sheenColor.g),sheenColor.b)*environmentSheenBrdf.b; #endif outParams.sheenIntensity=sheenIntensity; outParams.sheenColor=sheenColor; outParams.sheenRoughness=sheenRoughness; } #endif ";X.a.IncludesShadersStore.pbrBlockSheen=rb;a="struct clearcoatOutParams { vec3 specularEnvironmentR0; float conservationFactor; vec3 clearCoatNormalW; vec2 clearCoatAARoughnessFactors; float clearCoatIntensity; float clearCoatRoughness; #ifdef REFLECTION vec3 finalClearCoatRadianceScaled; #endif #ifdef CLEARCOAT_TINT vec3 absorption; float clearCoatNdotVRefract; vec3 clearCoatColor; float clearCoatThickness; #endif #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) vec3 energyConservationFactorClearCoat; #endif #if DEBUGMODE>0 mat3 TBNClearCoat; vec2 clearCoatMapData; vec4 clearCoatTintMapData; vec4 environmentClearCoatRadiance; float clearCoatNdotV; vec3 clearCoatEnvironmentReflectance; #endif }; #ifdef CLEARCOAT #define pbr_inline #define inline void clearcoatBlock( in vec3 vPositionW, in vec3 geometricNormalW, in vec3 viewDirectionW, in vec2 vClearCoatParams, #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) in vec4 clearCoatMapRoughnessData, #endif in vec3 specularEnvironmentR0, #ifdef CLEARCOAT_TEXTURE in vec2 clearCoatMapData, #endif #ifdef CLEARCOAT_TINT in vec4 vClearCoatTintParams, in float clearCoatColorAtDistance, in vec4 vClearCoatRefractionParams, #ifdef CLEARCOAT_TINT_TEXTURE in vec4 clearCoatTintMapData, #endif #endif #ifdef CLEARCOAT_BUMP in vec2 vClearCoatBumpInfos, in vec4 clearCoatBumpMapData, in vec2 vClearCoatBumpUV, #if defined(TANGENT) && defined(NORMAL) in mat3 vTBN, #else in vec2 vClearCoatTangentSpaceParams, #endif #ifdef OBJECTSPACE_NORMALMAP in mat4 normalMatrix, #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) in vec3 faceNormal, #endif #ifdef REFLECTION in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, in vec4 vLightingIntensity, #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, #else in sampler2D reflectionSampler, #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION in float ambientMonochrome, #endif #endif #if defined(CLEARCOAT_BUMP) || defined(TWOSIDEDLIGHTING) in float frontFacingMultiplier, #endif out clearcoatOutParams outParams ) { float clearCoatIntensity=vClearCoatParams.x; float clearCoatRoughness=vClearCoatParams.y; #ifdef CLEARCOAT_TEXTURE clearCoatIntensity*=clearCoatMapData.x; #ifdef CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE clearCoatRoughness*=clearCoatMapData.y; #endif #if DEBUGMODE>0 outParams.clearCoatMapData=clearCoatMapData; #endif #endif #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) #ifdef CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL clearCoatRoughness*=clearCoatMapData.y; #else clearCoatRoughness*=clearCoatMapRoughnessData.y; #endif #endif outParams.clearCoatIntensity=clearCoatIntensity; outParams.clearCoatRoughness=clearCoatRoughness; #ifdef CLEARCOAT_TINT vec3 clearCoatColor=vClearCoatTintParams.rgb; float clearCoatThickness=vClearCoatTintParams.a; #ifdef CLEARCOAT_TINT_TEXTURE #ifdef CLEARCOAT_TINT_GAMMATEXTURE clearCoatColor*=toLinearSpace(clearCoatTintMapData.rgb); #else clearCoatColor*=clearCoatTintMapData.rgb; #endif clearCoatThickness*=clearCoatTintMapData.a; #if DEBUGMODE>0 outParams.clearCoatTintMapData=clearCoatTintMapData; #endif #endif outParams.clearCoatColor=computeColorAtDistanceInMedia(clearCoatColor,clearCoatColorAtDistance); outParams.clearCoatThickness=clearCoatThickness; #endif #ifdef CLEARCOAT_REMAP_F0 vec3 specularEnvironmentR0Updated=getR0RemappedForClearCoat(specularEnvironmentR0); #else vec3 specularEnvironmentR0Updated=specularEnvironmentR0; #endif outParams.specularEnvironmentR0=mix(specularEnvironmentR0,specularEnvironmentR0Updated,clearCoatIntensity); vec3 clearCoatNormalW=geometricNormalW; #ifdef CLEARCOAT_BUMP #ifdef NORMALXYSCALE float clearCoatNormalScale=1.0; #else float clearCoatNormalScale=vClearCoatBumpInfos.y; #endif #if defined(TANGENT) && defined(NORMAL) mat3 TBNClearCoat=vTBN; #else vec2 TBNClearCoatUV=vClearCoatBumpUV*frontFacingMultiplier; mat3 TBNClearCoat=cotangent_frame(clearCoatNormalW*clearCoatNormalScale,vPositionW,TBNClearCoatUV,vClearCoatTangentSpaceParams); #endif #if DEBUGMODE>0 outParams.TBNClearCoat=TBNClearCoat; #endif #ifdef OBJECTSPACE_NORMALMAP clearCoatNormalW=normalize(clearCoatBumpMapData.xyz*2.0-1.0); clearCoatNormalW=normalize(mat3(normalMatrix)*clearCoatNormalW); #else clearCoatNormalW=perturbNormal(TBNClearCoat,clearCoatBumpMapData.xyz,vClearCoatBumpInfos.y); #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) clearCoatNormalW*=sign(dot(clearCoatNormalW,faceNormal)); #endif #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) clearCoatNormalW=clearCoatNormalW*frontFacingMultiplier; #endif outParams.clearCoatNormalW=clearCoatNormalW; outParams.clearCoatAARoughnessFactors=getAARoughnessFactors(clearCoatNormalW.xyz); float clearCoatNdotVUnclamped=dot(clearCoatNormalW,viewDirectionW); float clearCoatNdotV=absEps(clearCoatNdotVUnclamped); #if DEBUGMODE>0 outParams.clearCoatNdotV=clearCoatNdotV; #endif #ifdef CLEARCOAT_TINT vec3 clearCoatVRefract=-refract(vPositionW,clearCoatNormalW,vClearCoatRefractionParams.y); outParams.clearCoatNdotVRefract=absEps(dot(clearCoatNormalW,clearCoatVRefract)); #endif #if defined(ENVIRONMENTBRDF) && (!defined(REFLECTIONMAP_SKYBOX) || defined(MS_BRDF_ENERGY_CONSERVATION)) vec3 environmentClearCoatBrdf=getBRDFLookup(clearCoatNdotV,clearCoatRoughness); #endif #if defined(REFLECTION) float clearCoatAlphaG=convertRoughnessToAverageSlope(clearCoatRoughness); #ifdef SPECULARAA clearCoatAlphaG+=outParams.clearCoatAARoughnessFactors.y; #endif vec4 environmentClearCoatRadiance=vec4(0.,0.,0.,0.); vec3 clearCoatReflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),clearCoatNormalW); #ifdef REFLECTIONMAP_OPPOSITEZ clearCoatReflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D vec3 clearCoatReflectionCoords=clearCoatReflectionVector; #else vec2 clearCoatReflectionCoords=clearCoatReflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION clearCoatReflectionCoords/=clearCoatReflectionVector.z; #endif clearCoatReflectionCoords.y=1.0-clearCoatReflectionCoords.y; #endif sampleReflectionTexture( clearCoatAlphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) clearCoatNdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION clearCoatRoughness, #endif reflectionSampler, clearCoatReflectionCoords, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentClearCoatRadiance ); #if DEBUGMODE>0 outParams.environmentClearCoatRadiance=environmentClearCoatRadiance; #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) vec3 clearCoatEnvironmentReflectance=getReflectanceFromBRDFLookup(vec3(vClearCoatRefractionParams.x),environmentClearCoatBrdf); #ifdef RADIANCEOCCLUSION float clearCoatSeo=environmentRadianceOcclusion(ambientMonochrome,clearCoatNdotVUnclamped); clearCoatEnvironmentReflectance*=clearCoatSeo; #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D float clearCoatEho=environmentHorizonOcclusion(-viewDirectionW,clearCoatNormalW,geometricNormalW); clearCoatEnvironmentReflectance*=clearCoatEho; #endif #endif #endif #else vec3 clearCoatEnvironmentReflectance=getReflectanceFromAnalyticalBRDFLookup_Jones(clearCoatNdotV,vec3(1.),vec3(1.),sqrt(1.-clearCoatRoughness)); #endif clearCoatEnvironmentReflectance*=clearCoatIntensity; #if DEBUGMODE>0 outParams.clearCoatEnvironmentReflectance=clearCoatEnvironmentReflectance; #endif outParams.finalClearCoatRadianceScaled= environmentClearCoatRadiance.rgb * clearCoatEnvironmentReflectance * vLightingIntensity.z; #endif #if defined(CLEARCOAT_TINT) outParams.absorption=computeClearCoatAbsorption(outParams.clearCoatNdotVRefract,outParams.clearCoatNdotVRefract,outParams.clearCoatColor,clearCoatThickness,clearCoatIntensity); #endif float fresnelIBLClearCoat=fresnelSchlickGGX(clearCoatNdotV,vClearCoatRefractionParams.x,CLEARCOATREFLECTANCE90); fresnelIBLClearCoat*=clearCoatIntensity; outParams.conservationFactor=(1.-fresnelIBLClearCoat); #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) outParams.energyConservationFactorClearCoat=getEnergyConservationFactor(outParams.specularEnvironmentR0,environmentClearCoatBrdf); #endif } #endif ";X.a.IncludesShadersStore.pbrBlockClearcoat=a;rb="struct subSurfaceOutParams { vec3 specularEnvironmentReflectance; #ifdef SS_REFRACTION vec3 finalRefraction; vec3 surfaceAlbedo; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY float alpha; #endif #ifdef REFLECTION float refractionFactorForIrradiance; #endif #endif #ifdef SS_TRANSLUCENCY vec3 transmittance; float translucencyIntensity; #ifdef REFLECTION vec3 refractionIrradiance; #endif #endif #if DEBUGMODE>0 vec4 thicknessMap; vec4 environmentRefraction; vec3 refractionTransmittance; #endif }; #ifdef SUBSURFACE #define pbr_inline #define inline void subSurfaceBlock( in vec3 vSubSurfaceIntensity, in vec2 vThicknessParam, in vec4 vTintColor, in vec3 normalW, in vec3 specularEnvironmentReflectance, #ifdef SS_THICKNESSANDMASK_TEXTURE in vec4 thicknessMap, #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE in vec4 refractionIntensityMap, #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE in vec4 translucencyIntensityMap, #endif #ifdef REFLECTION #ifdef SS_TRANSLUCENCY in mat4 reflectionMatrix, #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) in vec3 irradianceVector_, #endif #if defined(REALTIME_FILTERING) in samplerCube reflectionSampler, in vec2 vReflectionFilteringInfo, #endif #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D in samplerCube irradianceSampler, #else in sampler2D irradianceSampler, #endif #endif #endif #endif #if defined(SS_REFRACTION) || defined(SS_TRANSLUCENCY) in vec3 surfaceAlbedo, #endif #ifdef SS_REFRACTION in vec3 vPositionW, in vec3 viewDirectionW, in mat4 view, in vec4 vRefractionInfos, in mat4 refractionMatrix, in vec4 vRefractionMicrosurfaceInfos, in vec4 vLightingIntensity, #ifdef SS_LINKREFRACTIONTOTRANSPARENCY in float alpha, #endif #ifdef SS_LODINREFRACTIONALPHA in float NdotVUnclamped, #endif #ifdef SS_LINEARSPECULARREFRACTION in float roughness, #endif in float alphaG, #ifdef SS_REFRACTIONMAP_3D in samplerCube refractionSampler, #ifndef LODBASEDMICROSFURACE in samplerCube refractionSamplerLow, in samplerCube refractionSamplerHigh, #endif #else in sampler2D refractionSampler, #ifndef LODBASEDMICROSFURACE in sampler2D refractionSamplerLow, in sampler2D refractionSamplerHigh, #endif #endif #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #ifdef REALTIME_FILTERING in vec2 vRefractionFilteringInfo, #endif #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC in vec3 refractionPosition, in vec3 refractionSize, #endif #endif #ifdef SS_TRANSLUCENCY in vec3 vDiffusionDistance, #endif out subSurfaceOutParams outParams ) { outParams.specularEnvironmentReflectance=specularEnvironmentReflectance; #ifdef SS_REFRACTION float refractionIntensity=vSubSurfaceIntensity.x; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY refractionIntensity*=(1.0-alpha); outParams.alpha=1.0; #endif #endif #ifdef SS_TRANSLUCENCY float translucencyIntensity=vSubSurfaceIntensity.y; #endif #ifdef SS_THICKNESSANDMASK_TEXTURE #if defined(SS_USE_GLTF_TEXTURES) float thickness=thicknessMap.g*vThicknessParam.y+vThicknessParam.x; #else float thickness=thicknessMap.r*vThicknessParam.y+vThicknessParam.x; #endif #if DEBUGMODE>0 outParams.thicknessMap=thicknessMap; #endif #ifdef SS_MASK_FROM_THICKNESS_TEXTURE #if defined(SS_REFRACTION) && defined(SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE) #if defined(SS_USE_GLTF_TEXTURES) refractionIntensity*=thicknessMap.r; #else refractionIntensity*=thicknessMap.g; #endif #endif #if defined(SS_TRANSLUCENCY) && defined(SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE) translucencyIntensity*=thicknessMap.b; #endif #endif #else float thickness=vThicknessParam.y; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE #ifdef SS_USE_GLTF_TEXTURES refractionIntensity*=refractionIntensityMap.r; #else refractionIntensity*=refractionIntensityMap.g; #endif #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE translucencyIntensity*=translucencyIntensityMap.b; #endif #ifdef SS_TRANSLUCENCY thickness=maxEps(thickness); vec3 transmittance=transmittanceBRDF_Burley(vTintColor.rgb,vDiffusionDistance,thickness); transmittance*=translucencyIntensity; outParams.transmittance=transmittance; outParams.translucencyIntensity=translucencyIntensity; #endif #ifdef SS_REFRACTION vec4 environmentRefraction=vec4(0.,0.,0.,0.); #ifdef ANISOTROPIC vec3 refractionVector=refract(-viewDirectionW,anisotropicOut.anisotropicNormal,vRefractionInfos.y); #else vec3 refractionVector=refract(-viewDirectionW,normalW,vRefractionInfos.y); #endif #ifdef SS_REFRACTIONMAP_OPPOSITEZ refractionVector.z*=-1.0; #endif #ifdef SS_REFRACTIONMAP_3D #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC refractionVector=parallaxCorrectNormal(vPositionW,refractionVector,refractionSize,refractionPosition); #endif refractionVector.y=refractionVector.y*vRefractionInfos.w; vec3 refractionCoords=refractionVector; refractionCoords=vec3(refractionMatrix*vec4(refractionCoords,0)); #else #ifdef SS_USE_THICKNESS_AS_DEPTH vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*thickness,1.0))); #else vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*vRefractionInfos.z,1.0))); #endif vec2 refractionCoords=vRefractionUVW.xy/vRefractionUVW.z; refractionCoords.y=1.0-refractionCoords.y; #endif #ifdef SS_HAS_THICKNESS float ior=vRefractionInfos.y; #else float ior=vRefractionMicrosurfaceInfos.w; #endif #ifdef SS_LODINREFRACTIONALPHA float refractionAlphaG=alphaG; refractionAlphaG=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLodFromAlphaG(vRefractionMicrosurfaceInfos.x,refractionAlphaG,NdotVUnclamped); #elif defined(SS_LINEARSPECULARREFRACTION) float refractionRoughness=alphaG; refractionRoughness=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLinearLodFromRoughness(vRefractionMicrosurfaceInfos.x,refractionRoughness); #else float refractionAlphaG=alphaG; refractionAlphaG=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLodFromAlphaG(vRefractionMicrosurfaceInfos.x,refractionAlphaG); #endif #ifdef LODBASEDMICROSFURACE refractionLOD=refractionLOD*vRefractionMicrosurfaceInfos.y+vRefractionMicrosurfaceInfos.z; #ifdef SS_LODINREFRACTIONALPHA float automaticRefractionLOD=UNPACK_LOD(sampleRefraction(refractionSampler,refractionCoords).a); float requestedRefractionLOD=max(automaticRefractionLOD,refractionLOD); #else float requestedRefractionLOD=refractionLOD; #endif #ifdef REALTIME_FILTERING environmentRefraction=vec4(radiance(alphaG,refractionSampler,refractionCoords,vRefractionFilteringInfo),1.0); #else environmentRefraction=sampleRefractionLod(refractionSampler,refractionCoords,requestedRefractionLOD); #endif #else float lodRefractionNormalized=saturate(refractionLOD/log2(vRefractionMicrosurfaceInfos.x)); float lodRefractionNormalizedDoubled=lodRefractionNormalized*2.0; vec4 environmentRefractionMid=sampleRefraction(refractionSampler,refractionCoords); if (lodRefractionNormalizedDoubled<1.0){ environmentRefraction=mix( sampleRefraction(refractionSamplerHigh,refractionCoords), environmentRefractionMid, lodRefractionNormalizedDoubled ); } else { environmentRefraction=mix( environmentRefractionMid, sampleRefraction(refractionSamplerLow,refractionCoords), lodRefractionNormalizedDoubled-1.0 ); } #endif #ifdef SS_RGBDREFRACTION environmentRefraction.rgb=fromRGBD(environmentRefraction); #endif #ifdef SS_GAMMAREFRACTION environmentRefraction.rgb=toLinearSpace(environmentRefraction.rgb); #endif environmentRefraction.rgb*=vRefractionInfos.x; #endif #ifdef SS_REFRACTION vec3 refractionTransmittance=vec3(refractionIntensity); #ifdef SS_THICKNESSANDMASK_TEXTURE vec3 volumeAlbedo=computeColorAtDistanceInMedia(vTintColor.rgb,vTintColor.w); refractionTransmittance*=cocaLambert(volumeAlbedo,thickness); #elif defined(SS_LINKREFRACTIONTOTRANSPARENCY) float maxChannel=max(max(surfaceAlbedo.r,surfaceAlbedo.g),surfaceAlbedo.b); vec3 volumeAlbedo=saturate(maxChannel*surfaceAlbedo); environmentRefraction.rgb*=volumeAlbedo; #else vec3 volumeAlbedo=computeColorAtDistanceInMedia(vTintColor.rgb,vTintColor.w); refractionTransmittance*=cocaLambert(volumeAlbedo,vThicknessParam.y); #endif #ifdef SS_ALBEDOFORREFRACTIONTINT environmentRefraction.rgb*=surfaceAlbedo.rgb; #endif outParams.surfaceAlbedo=surfaceAlbedo*(1.-refractionIntensity); #ifdef REFLECTION outParams.refractionFactorForIrradiance=(1.-refractionIntensity); #endif #ifdef UNUSED_MULTIPLEBOUNCES vec3 bounceSpecularEnvironmentReflectance=(2.0*specularEnvironmentReflectance)/(1.0+specularEnvironmentReflectance); outParams.specularEnvironmentReflectance=mix(bounceSpecularEnvironmentReflectance,specularEnvironmentReflectance,refractionIntensity); #endif refractionTransmittance*=1.0-outParams.specularEnvironmentReflectance; #if DEBUGMODE>0 outParams.refractionTransmittance=refractionTransmittance; #endif outParams.finalRefraction=environmentRefraction.rgb*refractionTransmittance*vLightingIntensity.z; #if DEBUGMODE>0 outParams.environmentRefraction=environmentRefraction; #endif #endif #if defined(REFLECTION) && defined(SS_TRANSLUCENCY) #if defined(NORMAL) && defined(USESPHERICALINVERTEX) || !defined(USESPHERICALFROMREFLECTIONMAP) vec3 irradianceVector=vec3(reflectionMatrix*vec4(normalW,0)).xyz; #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z*=-1.0; #endif #ifdef INVERTCUBICMAP irradianceVector.y*=-1.0; #endif #else vec3 irradianceVector=irradianceVector_; #endif #if defined(USESPHERICALFROMREFLECTIONMAP) #if defined(REALTIME_FILTERING) vec3 refractionIrradiance=irradiance(reflectionSampler,-irradianceVector,vReflectionFilteringInfo); #else vec3 refractionIrradiance=computeEnvironmentIrradiance(-irradianceVector); #endif #elif defined(USEIRRADIANCEMAP) #ifdef REFLECTIONMAP_3D vec3 irradianceCoords=irradianceVector; #else vec2 irradianceCoords=irradianceVector.xy; #ifdef REFLECTIONMAP_PROJECTION irradianceCoords/=irradianceVector.z; #endif irradianceCoords.y=1.0-irradianceCoords.y; #endif vec4 refractionIrradiance=sampleReflection(irradianceSampler,-irradianceCoords); #ifdef RGBDREFLECTION refractionIrradiance.rgb=fromRGBD(refractionIrradiance); #endif #ifdef GAMMAREFLECTION refractionIrradiance.rgb=toLinearSpace(refractionIrradiance.rgb); #endif #else vec4 refractionIrradiance=vec4(0.); #endif refractionIrradiance.rgb*=transmittance; #ifdef SS_ALBEDOFORTRANSLUCENCYTINT refractionIrradiance.rgb*=surfaceAlbedo.rgb; #endif outParams.refractionIrradiance=refractionIrradiance.rgb; #endif } #endif ";X.a.IncludesShadersStore.pbrBlockSubSurface=rb;c(193);a="vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=normalize(cross(dFdx(vPositionW),dFdy(vPositionW)))*vEyePosition.w; #endif vec3 geometricNormalW=normalW; #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) geometricNormalW=gl_FrontFacing ? geometricNormalW : -geometricNormalW; #endif ";X.a.IncludesShadersStore.pbrBlockNormalGeometric=a;c(156);rb="#if defined(FORCENORMALFORWARD) && defined(NORMAL) vec3 faceNormal=normalize(cross(dFdx(vPositionW),dFdy(vPositionW)))*vEyePosition.w; #if defined(TWOSIDEDLIGHTING) faceNormal=gl_FrontFacing ? faceNormal : -faceNormal; #endif normalW*=sign(dot(normalW,faceNormal)); #endif #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) normalW=gl_FrontFacing ? normalW : -normalW; #endif ";X.a.IncludesShadersStore.pbrBlockNormalFinal=rb;c(194);a="#ifdef LIGHTMAP vec4 lightmapColor=texture2D(lightmapSampler,vLightmapUV+uvOffset); #ifdef RGBDLIGHTMAP lightmapColor.rgb=fromRGBD(lightmapColor); #endif #ifdef GAMMALIGHTMAP lightmapColor.rgb=toLinearSpace(lightmapColor.rgb); #endif lightmapColor.rgb*=vLightmapInfos.y; #endif ";X.a.IncludesShadersStore.pbrBlockLightmapInit=a;rb="float NdotVUnclamped=dot(normalW,viewDirectionW); float NdotV=absEps(NdotVUnclamped); float alphaG=convertRoughnessToAverageSlope(roughness); vec2 AARoughnessFactors=getAARoughnessFactors(normalW.xyz); #ifdef SPECULARAA alphaG+=AARoughnessFactors.y; #endif #if defined(ENVIRONMENTBRDF) vec3 environmentBrdf=getBRDFLookup(NdotV,roughness); #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION #ifdef AMBIENTINGRAYSCALE float ambientMonochrome=aoOut.ambientOcclusionColor.r; #else float ambientMonochrome=getLuminance(aoOut.ambientOcclusionColor); #endif float seo=environmentRadianceOcclusion(ambientMonochrome,NdotVUnclamped); #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D float eho=environmentHorizonOcclusion(-viewDirectionW,normalW,geometricNormalW); #endif #endif #endif #endif ";X.a.IncludesShadersStore.pbrBlockGeometryInfo=rb;a="float reflectance=max(max(reflectivityOut.surfaceReflectivityColor.r,reflectivityOut.surfaceReflectivityColor.g),reflectivityOut.surfaceReflectivityColor.b); vec3 specularEnvironmentR0=reflectivityOut.surfaceReflectivityColor.rgb; #ifdef METALLICWORKFLOW vec3 specularEnvironmentR90=vec3(metallicReflectanceFactors.a); #else vec3 specularEnvironmentR90=vec3(1.0,1.0,1.0); #endif #ifdef ALPHAFRESNEL float reflectance90=fresnelGrazingReflectance(reflectance); specularEnvironmentR90=specularEnvironmentR90*reflectance90; #endif ";X.a.IncludesShadersStore.pbrBlockReflectance0=a;rb="#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) vec3 specularEnvironmentReflectance=getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,environmentBrdf); #ifdef RADIANCEOCCLUSION specularEnvironmentReflectance*=seo; #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D specularEnvironmentReflectance*=eho; #endif #endif #endif #else vec3 specularEnvironmentReflectance=getReflectanceFromAnalyticalBRDFLookup_Jones(NdotV,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,sqrt(microSurface)); #endif #ifdef CLEARCOAT specularEnvironmentReflectance*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) specularEnvironmentReflectance*=clearcoatOut.absorption; #endif #endif ";X.a.IncludesShadersStore.pbrBlockReflectance=rb;a="vec3 diffuseBase=vec3(0.,0.,0.); #ifdef SPECULARTERM vec3 specularBase=vec3(0.,0.,0.); #endif #ifdef CLEARCOAT vec3 clearCoatBase=vec3(0.,0.,0.); #endif #ifdef SHEEN vec3 sheenBase=vec3(0.,0.,0.); #endif preLightingInfo preInfo; lightingInfo info; float shadow=1.; #if defined(CLEARCOAT) && defined(CLEARCOAT_TINT) vec3 absorption=vec3(0.); #endif ";X.a.IncludesShadersStore.pbrBlockDirectLighting=a;rb=" #if defined(ENVIRONMENTBRDF) #ifdef MS_BRDF_ENERGY_CONSERVATION vec3 energyConservationFactor=getEnergyConservationFactor(clearcoatOut.specularEnvironmentR0,environmentBrdf); #endif #endif #ifndef METALLICWORKFLOW #ifdef SPECULAR_GLOSSINESS_ENERGY_CONSERVATION surfaceAlbedo.rgb=(1.-reflectance)*surfaceAlbedo.rgb; #endif #endif #if defined(SHEEN) && defined(SHEEN_ALBEDOSCALING) && defined(ENVIRONMENTBRDF) surfaceAlbedo.rgb=sheenOut.sheenAlbedoScaling*surfaceAlbedo.rgb; #endif #ifdef REFLECTION vec3 finalIrradiance=reflectionOut.environmentIrradiance; #if defined(CLEARCOAT) finalIrradiance*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) finalIrradiance*=clearcoatOut.absorption; #endif #endif #if defined(SS_REFRACTION) finalIrradiance*=subSurfaceOut.refractionFactorForIrradiance; #endif #if defined(SS_TRANSLUCENCY) finalIrradiance*=(1.0-subSurfaceOut.translucencyIntensity); finalIrradiance+=subSurfaceOut.refractionIrradiance; #endif finalIrradiance*=surfaceAlbedo.rgb; finalIrradiance*=vLightingIntensity.z; finalIrradiance*=aoOut.ambientOcclusionColor; #endif #ifdef SPECULARTERM vec3 finalSpecular=specularBase; finalSpecular=max(finalSpecular,0.0); vec3 finalSpecularScaled=finalSpecular*vLightingIntensity.x*vLightingIntensity.w; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalSpecularScaled*=energyConservationFactor; #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) finalSpecularScaled*=sheenOut.sheenAlbedoScaling; #endif #endif #ifdef REFLECTION vec3 finalRadiance=reflectionOut.environmentRadiance.rgb; finalRadiance*=subSurfaceOut.specularEnvironmentReflectance; vec3 finalRadianceScaled=finalRadiance*vLightingIntensity.z; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalRadianceScaled*=energyConservationFactor; #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) finalRadianceScaled*=sheenOut.sheenAlbedoScaling; #endif #endif #ifdef SHEEN vec3 finalSheen=sheenBase*sheenOut.sheenColor; finalSheen=max(finalSheen,0.0); vec3 finalSheenScaled=finalSheen*vLightingIntensity.x*vLightingIntensity.w; #if defined(CLEARCOAT) && defined(REFLECTION) && defined(ENVIRONMENTBRDF) sheenOut.finalSheenRadianceScaled*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) sheenOut.finalSheenRadianceScaled*=clearcoatOut.absorption; #endif #endif #endif #ifdef CLEARCOAT vec3 finalClearCoat=clearCoatBase; finalClearCoat=max(finalClearCoat,0.0); vec3 finalClearCoatScaled=finalClearCoat*vLightingIntensity.x*vLightingIntensity.w; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalClearCoatScaled*=clearcoatOut.energyConservationFactorClearCoat; #endif #ifdef SS_REFRACTION subSurfaceOut.finalRefraction*=clearcoatOut.conservationFactor; #ifdef CLEARCOAT_TINT subSurfaceOut.finalRefraction*=clearcoatOut.absorption; #endif #endif #endif #ifdef ALPHABLEND float luminanceOverAlpha=0.0; #if defined(REFLECTION) && defined(RADIANCEOVERALPHA) luminanceOverAlpha+=getLuminance(finalRadianceScaled); #if defined(CLEARCOAT) luminanceOverAlpha+=getLuminance(clearcoatOut.finalClearCoatRadianceScaled); #endif #endif #if defined(SPECULARTERM) && defined(SPECULAROVERALPHA) luminanceOverAlpha+=getLuminance(finalSpecularScaled); #endif #if defined(CLEARCOAT) && defined(CLEARCOATOVERALPHA) luminanceOverAlpha+=getLuminance(finalClearCoatScaled); #endif #if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA) || defined(CLEARCOATOVERALPHA) alpha=saturate(alpha+luminanceOverAlpha*luminanceOverAlpha); #endif #endif ";X.a.IncludesShadersStore.pbrBlockFinalLitComponents=rb;a=" vec3 finalDiffuse=diffuseBase; finalDiffuse*=surfaceAlbedo.rgb; finalDiffuse=max(finalDiffuse,0.0); finalDiffuse*=vLightingIntensity.x; vec3 finalAmbient=vAmbientColor; finalAmbient*=surfaceAlbedo.rgb; vec3 finalEmissive=vEmissiveColor; #ifdef EMISSIVE vec3 emissiveColorTex=texture2D(emissiveSampler,vEmissiveUV+uvOffset).rgb; #ifdef GAMMAEMISSIVE finalEmissive*=toLinearSpace(emissiveColorTex.rgb); #else finalEmissive*=emissiveColorTex.rgb; #endif finalEmissive*=vEmissiveInfos.y; #endif finalEmissive*=vLightingIntensity.y; #ifdef AMBIENT vec3 ambientOcclusionForDirectDiffuse=mix(vec3(1.),aoOut.ambientOcclusionColor,vAmbientInfos.w); #else vec3 ambientOcclusionForDirectDiffuse=aoOut.ambientOcclusionColor; #endif finalAmbient*=aoOut.ambientOcclusionColor; finalDiffuse*=ambientOcclusionForDirectDiffuse; ";X.a.IncludesShadersStore.pbrBlockFinalUnlitComponents=a;rb="vec4 finalColor=vec4( finalAmbient + finalDiffuse + #ifndef UNLIT #ifdef REFLECTION finalIrradiance + #endif #ifdef SPECULARTERM finalSpecularScaled + #endif #ifdef SHEEN finalSheenScaled + #endif #ifdef CLEARCOAT finalClearCoatScaled + #endif #ifdef REFLECTION finalRadianceScaled + #if defined(SHEEN) && defined(ENVIRONMENTBRDF) sheenOut.finalSheenRadianceScaled + #endif #ifdef CLEARCOAT clearcoatOut.finalClearCoatRadianceScaled + #endif #endif #ifdef SS_REFRACTION subSurfaceOut.finalRefraction + #endif #endif finalEmissive, alpha); #ifdef LIGHTMAP #ifndef LIGHTMAPEXCLUDED #ifdef USELIGHTMAPASSHADOWMAP finalColor.rgb*=lightmapColor.rgb; #else finalColor.rgb+=lightmapColor.rgb; #endif #endif #endif #define CUSTOM_FRAGMENT_BEFORE_FOG finalColor=max(finalColor,0.0); ";X.a.IncludesShadersStore.pbrBlockFinalColorComposition=rb;c(183);a="#if defined(IMAGEPROCESSINGPOSTPROCESS) || defined(SS_SCATTERING) finalColor.rgb=clamp(finalColor.rgb,0.,30.0); #else finalColor=applyImageProcessing(finalColor); #endif finalColor.a*=visibility; #ifdef PREMULTIPLYALPHA finalColor.rgb*=finalColor.a; #endif ";X.a.IncludesShadersStore.pbrBlockImageProcessing=a;rb="#if DEBUGMODE>0 if (vClipSpacePosition.x/vClipSpacePosition.w>=vDebugMode.x) { #if DEBUGMODE == 1 gl_FragColor.rgb=vPositionW.rgb; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 2 && defined(NORMAL) gl_FragColor.rgb=vNormalW.rgb; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 3 && defined(BUMP) || DEBUGMODE == 3 && defined(PARALLAX) || DEBUGMODE == 3 && defined(ANISOTROPIC) gl_FragColor.rgb=TBN[0]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 4 && defined(BUMP) || DEBUGMODE == 4 && defined(PARALLAX) || DEBUGMODE == 4 && defined(ANISOTROPIC) gl_FragColor.rgb=TBN[1]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 5 gl_FragColor.rgb=normalW; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 6 && defined(MAINUV1) gl_FragColor.rgb=vec3(vMainUV1,0.0); #elif DEBUGMODE == 7 && defined(MAINUV2) gl_FragColor.rgb=vec3(vMainUV2,0.0); #elif DEBUGMODE == 8 && defined(CLEARCOAT) && defined(CLEARCOAT_BUMP) gl_FragColor.rgb=clearcoatOut.TBNClearCoat[0]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 9 && defined(CLEARCOAT) && defined(CLEARCOAT_BUMP) gl_FragColor.rgb=clearcoatOut.TBNClearCoat[1]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 10 && defined(CLEARCOAT) gl_FragColor.rgb=clearcoatOut.clearCoatNormalW; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 11 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicNormal; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 12 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicTangent; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 13 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicBitangent; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 20 && defined(ALBEDO) gl_FragColor.rgb=albedoTexture.rgb; #elif DEBUGMODE == 21 && defined(AMBIENT) gl_FragColor.rgb=aoOut.ambientOcclusionColorMap.rgb; #elif DEBUGMODE == 22 && defined(OPACITY) gl_FragColor.rgb=opacityMap.rgb; #elif DEBUGMODE == 23 && defined(EMISSIVE) gl_FragColor.rgb=emissiveColorTex.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 24 && defined(LIGHTMAP) gl_FragColor.rgb=lightmapColor.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 25 && defined(REFLECTIVITY) && defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.surfaceMetallicColorMap.rgb; #elif DEBUGMODE == 26 && defined(REFLECTIVITY) && !defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.surfaceReflectivityColorMap.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 27 && defined(CLEARCOAT) && defined(CLEARCOAT_TEXTURE) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatMapData.rg,0.0); #elif DEBUGMODE == 28 && defined(CLEARCOAT) && defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE) gl_FragColor.rgb=clearcoatOut.clearCoatTintMapData.rgb; #elif DEBUGMODE == 29 && defined(SHEEN) && defined(SHEEN_TEXTURE) gl_FragColor.rgb=sheenOut.sheenMapData.rgb; #elif DEBUGMODE == 30 && defined(ANISOTROPIC) && defined(ANISOTROPIC_TEXTURE) gl_FragColor.rgb=anisotropicOut.anisotropyMapData.rgb; #elif DEBUGMODE == 31 && defined(SUBSURFACE) && defined(SS_THICKNESSANDMASK_TEXTURE) gl_FragColor.rgb=subSurfaceOut.thicknessMap.rgb; #elif DEBUGMODE == 40 && defined(SS_REFRACTION) gl_FragColor.rgb=subSurfaceOut.environmentRefraction.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 41 && defined(REFLECTION) gl_FragColor.rgb=reflectionOut.environmentRadiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 42 && defined(CLEARCOAT) && defined(REFLECTION) gl_FragColor.rgb=clearcoatOut.environmentClearCoatRadiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 50 gl_FragColor.rgb=diffuseBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 51 && defined(SPECULARTERM) gl_FragColor.rgb=specularBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 52 && defined(CLEARCOAT) gl_FragColor.rgb=clearCoatBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 53 && defined(SHEEN) gl_FragColor.rgb=sheenBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 54 && defined(REFLECTION) gl_FragColor.rgb=reflectionOut.environmentIrradiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 60 gl_FragColor.rgb=surfaceAlbedo.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 61 gl_FragColor.rgb=clearcoatOut.specularEnvironmentR0; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 62 && defined(METALLICWORKFLOW) gl_FragColor.rgb=vec3(reflectivityOut.metallicRoughness.r); #elif DEBUGMODE == 71 && defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.metallicF0; #elif DEBUGMODE == 63 gl_FragColor.rgb=vec3(roughness); #elif DEBUGMODE == 64 gl_FragColor.rgb=vec3(alphaG); #elif DEBUGMODE == 65 gl_FragColor.rgb=vec3(NdotV); #elif DEBUGMODE == 66 && defined(CLEARCOAT) && defined(CLEARCOAT_TINT) gl_FragColor.rgb=clearcoatOut.clearCoatColor.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 67 && defined(CLEARCOAT) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatRoughness); #elif DEBUGMODE == 68 && defined(CLEARCOAT) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatNdotV); #elif DEBUGMODE == 69 && defined(SUBSURFACE) && defined(SS_TRANSLUCENCY) gl_FragColor.rgb=subSurfaceOut.transmittance; #elif DEBUGMODE == 70 && defined(SUBSURFACE) && defined(SS_REFRACTION) gl_FragColor.rgb=subSurfaceOut.refractionTransmittance; #elif DEBUGMODE == 80 && defined(RADIANCEOCCLUSION) gl_FragColor.rgb=vec3(seo); #elif DEBUGMODE == 81 && defined(HORIZONOCCLUSION) gl_FragColor.rgb=vec3(eho); #elif DEBUGMODE == 82 && defined(MS_BRDF_ENERGY_CONSERVATION) gl_FragColor.rgb=vec3(energyConservationFactor); #elif DEBUGMODE == 83 && defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) gl_FragColor.rgb=specularEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 84 && defined(CLEARCOAT) && defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) gl_FragColor.rgb=clearcoatOut.clearCoatEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 85 && defined(SHEEN) && defined(REFLECTION) gl_FragColor.rgb=sheenOut.sheenEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 86 && defined(ALPHABLEND) gl_FragColor.rgb=vec3(luminanceOverAlpha); #elif DEBUGMODE == 87 gl_FragColor.rgb=vec3(alpha); #endif gl_FragColor.rgb*=vDebugMode.y; #ifdef DEBUGMODE_NORMALIZE gl_FragColor.rgb=normalize(gl_FragColor.rgb)*0.5+0.5; #endif #ifdef DEBUGMODE_GAMMA gl_FragColor.rgb=toGammaSpace(gl_FragColor.rgb); #endif gl_FragColor.a=1.0; #ifdef PREPASS gl_FragData[0]=toLinearSpace(gl_FragColor); gl_FragData[1]=vec4(0.,0.,0.,0.); #endif return; } #endif";X.a.IncludesShadersStore.pbrDebug=rb;a="#if defined(BUMP) || !defined(NORMAL) || defined(FORCENORMALFORWARD) || defined(SPECULARAA) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) #extension GL_OES_standard_derivatives : enable #endif #ifdef LODBASEDMICROSFURACE #extension GL_EXT_shader_texture_lod : enable #endif #define CUSTOM_FRAGMENT_BEGIN #ifdef LOGARITHMICDEPTH #extension GL_EXT_frag_depth : enable #endif #include[SCENE_MRT_COUNT] precision highp float; #include #ifndef FROMLINEARSPACE #define FROMLINEARSPACE #endif #include<__decl__pbrFragment> #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef REFLECTION #include #endif #define CUSTOM_FRAGMENT_DEFINITIONS #include #include #include #include #include #include #include #include #include void main(void) { #define CUSTOM_FRAGMENT_MAIN_BEGIN #include #include #include #include #include albedoOpacityOutParams albedoOpacityOut; #ifdef ALBEDO vec4 albedoTexture=texture2D(albedoSampler,vAlbedoUV+uvOffset); #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vOpacityUV+uvOffset); #endif albedoOpacityBlock( vAlbedoColor, #ifdef ALBEDO albedoTexture, vAlbedoInfos, #endif #ifdef OPACITY opacityMap, vOpacityInfos, #endif #ifdef DETAIL detailColor, vDetailInfos, #endif albedoOpacityOut ); vec3 surfaceAlbedo=albedoOpacityOut.surfaceAlbedo; float alpha=albedoOpacityOut.alpha; #define CUSTOM_FRAGMENT_UPDATE_ALPHA #include #define CUSTOM_FRAGMENT_BEFORE_LIGHTS ambientOcclusionOutParams aoOut; #ifdef AMBIENT vec3 ambientOcclusionColorMap=texture2D(ambientSampler,vAmbientUV+uvOffset).rgb; #endif ambientOcclusionBlock( #ifdef AMBIENT ambientOcclusionColorMap, vAmbientInfos, #endif aoOut ); #include #ifdef UNLIT vec3 diffuseBase=vec3(1.,1.,1.); #else vec3 baseColor=surfaceAlbedo; reflectivityOutParams reflectivityOut; #if defined(REFLECTIVITY) vec4 surfaceMetallicOrReflectivityColorMap=texture2D(reflectivitySampler,vReflectivityUV+uvOffset); vec4 baseReflectivity=surfaceMetallicOrReflectivityColorMap; #ifndef METALLICWORKFLOW #ifdef REFLECTIVITY_GAMMA surfaceMetallicOrReflectivityColorMap=toLinearSpace(surfaceMetallicOrReflectivityColorMap); #endif surfaceMetallicOrReflectivityColorMap.rgb*=vReflectivityInfos.y; #endif #endif #if defined(MICROSURFACEMAP) vec4 microSurfaceTexel=texture2D(microSurfaceSampler,vMicroSurfaceSamplerUV+uvOffset)*vMicroSurfaceSamplerInfos.y; #endif #ifdef METALLICWORKFLOW vec4 metallicReflectanceFactors=vMetallicReflectanceFactors; #ifdef REFLECTANCE vec4 reflectanceFactorsMap=texture2D(reflectanceSampler,vReflectanceUV+uvOffset); #ifdef REFLECTANCE_GAMMA reflectanceFactorsMap=toLinearSpace(reflectanceFactorsMap); #endif metallicReflectanceFactors.rgb*=reflectanceFactorsMap.rgb; #endif #ifdef METALLIC_REFLECTANCE vec4 metallicReflectanceFactorsMap=texture2D(metallicReflectanceSampler,vMetallicReflectanceUV+uvOffset); #ifdef METALLIC_REFLECTANCE_GAMMA metallicReflectanceFactorsMap=toLinearSpace(metallicReflectanceFactorsMap); #endif #ifndef METALLIC_REFLECTANCE_USE_ALPHA_ONLY metallicReflectanceFactors.rgb*=metallicReflectanceFactorsMap.rgb; #endif metallicReflectanceFactors*=metallicReflectanceFactorsMap.a; #endif #endif reflectivityBlock( vReflectivityColor, #ifdef METALLICWORKFLOW surfaceAlbedo, metallicReflectanceFactors, #endif #ifdef REFLECTIVITY vReflectivityInfos, surfaceMetallicOrReflectivityColorMap, #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor, #endif #ifdef MICROSURFACEMAP microSurfaceTexel, #endif #ifdef DETAIL detailColor, vDetailInfos, #endif reflectivityOut ); float microSurface=reflectivityOut.microSurface; float roughness=reflectivityOut.roughness; #ifdef METALLICWORKFLOW surfaceAlbedo=reflectivityOut.surfaceAlbedo; #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor=reflectivityOut.ambientOcclusionColor; #endif #ifdef ALPHAFRESNEL #if defined(ALPHATEST) || defined(ALPHABLEND) alphaFresnelOutParams alphaFresnelOut; alphaFresnelBlock( normalW, viewDirectionW, alpha, microSurface, alphaFresnelOut ); alpha=alphaFresnelOut.alpha; #endif #endif #include #ifdef ANISOTROPIC anisotropicOutParams anisotropicOut; #ifdef ANISOTROPIC_TEXTURE vec3 anisotropyMapData=texture2D(anisotropySampler,vAnisotropyUV+uvOffset).rgb*vAnisotropyInfos.y; #endif anisotropicBlock( vAnisotropy, #ifdef ANISOTROPIC_TEXTURE anisotropyMapData, #endif TBN, normalW, viewDirectionW, anisotropicOut ); #endif #ifdef REFLECTION reflectionOutParams reflectionOut; reflectionBlock( vPositionW, normalW, alphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #ifdef ANISOTROPIC anisotropicOut, #endif #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION roughness, #endif reflectionSampler, #if defined(NORMAL) && defined(USESPHERICALINVERTEX) vEnvironmentIrradiance, #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) reflectionMatrix, #endif #endif #ifdef USEIRRADIANCEMAP irradianceSampler, #endif #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif reflectionOut ); #endif #include #ifdef SHEEN sheenOutParams sheenOut; #ifdef SHEEN_TEXTURE vec4 sheenMapData=texture2D(sheenSampler,vSheenUV+uvOffset); #endif #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) vec4 sheenMapRoughnessData=texture2D(sheenRoughnessSampler,vSheenRoughnessUV+uvOffset)*vSheenInfos.w; #endif sheenBlock( vSheenColor, #ifdef SHEEN_ROUGHNESS vSheenRoughness, #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) sheenMapRoughnessData, #endif #endif roughness, #ifdef SHEEN_TEXTURE sheenMapData, vSheenInfos.y, #endif reflectance, #ifdef SHEEN_LINKWITHALBEDO baseColor, surfaceAlbedo, #endif #ifdef ENVIRONMENTBRDF NdotV, environmentBrdf, #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) AARoughnessFactors, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, vLightingIntensity, reflectionSampler, reflectionOut.reflectionCoords, NdotVUnclamped, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) seo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) eho, #endif #endif sheenOut ); #ifdef SHEEN_LINKWITHALBEDO surfaceAlbedo=sheenOut.surfaceAlbedo; #endif #endif clearcoatOutParams clearcoatOut; #ifdef CLEARCOAT #ifdef CLEARCOAT_TEXTURE vec2 clearCoatMapData=texture2D(clearCoatSampler,vClearCoatUV+uvOffset).rg*vClearCoatInfos.y; #endif #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) vec4 clearCoatMapRoughnessData=texture2D(clearCoatRoughnessSampler,vClearCoatRoughnessUV+uvOffset)*vClearCoatInfos.w; #endif #if defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE) vec4 clearCoatTintMapData=texture2D(clearCoatTintSampler,vClearCoatTintUV+uvOffset); #endif #ifdef CLEARCOAT_BUMP vec4 clearCoatBumpMapData=texture2D(clearCoatBumpSampler,vClearCoatBumpUV+uvOffset); #endif clearcoatBlock( vPositionW, geometricNormalW, viewDirectionW, vClearCoatParams, #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) clearCoatMapRoughnessData, #endif specularEnvironmentR0, #ifdef CLEARCOAT_TEXTURE clearCoatMapData, #endif #ifdef CLEARCOAT_TINT vClearCoatTintParams, clearCoatColorAtDistance, vClearCoatRefractionParams, #ifdef CLEARCOAT_TINT_TEXTURE clearCoatTintMapData, #endif #endif #ifdef CLEARCOAT_BUMP vClearCoatBumpInfos, clearCoatBumpMapData, vClearCoatBumpUV, #if defined(TANGENT) && defined(NORMAL) vTBN, #else vClearCoatTangentSpaceParams, #endif #ifdef OBJECTSPACE_NORMALMAP normalMatrix, #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) faceNormal, #endif #ifdef REFLECTION vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, vLightingIntensity, reflectionSampler, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION ambientMonochrome, #endif #endif #if defined(CLEARCOAT_BUMP) || defined(TWOSIDEDLIGHTING) (gl_FrontFacing ? 1. : -1.), #endif clearcoatOut ); #else clearcoatOut.specularEnvironmentR0=specularEnvironmentR0; #endif #include subSurfaceOutParams subSurfaceOut; #ifdef SUBSURFACE #ifdef SS_THICKNESSANDMASK_TEXTURE vec4 thicknessMap=texture2D(thicknessSampler,vThicknessUV+uvOffset); #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE vec4 refractionIntensityMap=texture2D(refractionIntensitySampler,vRefractionIntensityUV+uvOffset); #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE vec4 translucencyIntensityMap=texture2D(translucencyIntensitySampler,vTranslucencyIntensityUV+uvOffset); #endif subSurfaceBlock( vSubSurfaceIntensity, vThicknessParam, vTintColor, normalW, specularEnvironmentReflectance, #ifdef SS_THICKNESSANDMASK_TEXTURE thicknessMap, #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE refractionIntensityMap, #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE translucencyIntensityMap, #endif #ifdef REFLECTION #ifdef SS_TRANSLUCENCY reflectionMatrix, #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) reflectionOut.irradianceVector, #endif #if defined(REALTIME_FILTERING) reflectionSampler, vReflectionFilteringInfo, #endif #endif #ifdef USEIRRADIANCEMAP irradianceSampler, #endif #endif #endif #if defined(SS_REFRACTION) || defined(SS_TRANSLUCENCY) surfaceAlbedo, #endif #ifdef SS_REFRACTION vPositionW, viewDirectionW, view, vRefractionInfos, refractionMatrix, vRefractionMicrosurfaceInfos, vLightingIntensity, #ifdef SS_LINKREFRACTIONTOTRANSPARENCY alpha, #endif #ifdef SS_LODINREFRACTIONALPHA NdotVUnclamped, #endif #ifdef SS_LINEARSPECULARREFRACTION roughness, #endif alphaG, refractionSampler, #ifndef LODBASEDMICROSFURACE refractionSamplerLow, refractionSamplerHigh, #endif #ifdef ANISOTROPIC anisotropicOut, #endif #ifdef REALTIME_FILTERING vRefractionFilteringInfo, #endif #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC vRefractionPosition, vRefractionSize, #endif #endif #ifdef SS_TRANSLUCENCY vDiffusionDistance, #endif subSurfaceOut ); #ifdef SS_REFRACTION surfaceAlbedo=subSurfaceOut.surfaceAlbedo; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY alpha=subSurfaceOut.alpha; #endif #endif #else subSurfaceOut.specularEnvironmentReflectance=specularEnvironmentReflectance; #endif #include #include[0..maxSimultaneousLights] #include #endif #include #define CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION #include #include #include(color,finalColor) #include #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR #ifdef PREPASS float writeGeometryInfo=finalColor.a>0.4 ? 1.0 : 0.0; #ifdef PREPASS_POSITION gl_FragData[PREPASS_POSITION_INDEX]=vec4(vPositionW,writeGeometryInfo); #endif #ifdef PREPASS_VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[PREPASS_VELOCITY_INDEX]=vec4(velocity,0.0,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT vec3 sqAlbedo=sqrt(surfaceAlbedo); #endif #ifdef PREPASS_IRRADIANCE vec3 irradiance=finalDiffuse; #ifndef UNLIT #ifdef REFLECTION irradiance+=finalIrradiance; #endif #endif #ifdef SS_SCATTERING gl_FragData[0]=vec4(finalColor.rgb-irradiance,finalColor.a); irradiance/=sqAlbedo; #else gl_FragData[0]=finalColor; float scatteringDiffusionProfile=255.; #endif gl_FragData[PREPASS_IRRADIANCE_INDEX]=vec4(clamp(irradiance,vec3(0.),vec3(1.)),scatteringDiffusionProfile/255.); #else gl_FragData[0]=vec4(finalColor.rgb,finalColor.a); #endif #ifdef PREPASS_DEPTH gl_FragData[PREPASS_DEPTH_INDEX]=vec4(vViewPos.z,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_NORMAL gl_FragData[PREPASS_NORMAL_INDEX]=vec4((view*vec4(normalW,0.0)).rgb,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT gl_FragData[PREPASS_ALBEDO_SQRT_INDEX]=vec4(sqAlbedo,writeGeometryInfo); #endif #ifdef PREPASS_REFLECTIVITY #if defined(REFLECTIVITY) gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(baseReflectivity.rgb,writeGeometryInfo); #else gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #endif #endif #if !defined(PREPASS) || defined(WEBGL2) gl_FragColor=finalColor; #endif #if ORDER_INDEPENDENT_TRANSPARENCY if (fragDepth == nearestDepth) { frontColor.rgb+=finalColor.rgb*finalColor.a*alphaMultiplier; frontColor.a=1.0-alphaMultiplier*(1.0-finalColor.a); } else { backColor+=finalColor; } #endif #include } ";X.a.ShadersStore.pbrPixelShader=a;rb="uniform mat4 view; uniform mat4 viewProjection; #ifdef ALBEDO uniform mat4 albedoMatrix; uniform vec2 vAlbedoInfos; #endif #ifdef AMBIENT uniform mat4 ambientMatrix; uniform vec4 vAmbientInfos; #endif #ifdef OPACITY uniform mat4 opacityMatrix; uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; uniform mat4 emissiveMatrix; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; uniform mat4 lightmapMatrix; #endif #ifdef REFLECTIVITY uniform vec3 vReflectivityInfos; uniform mat4 reflectivityMatrix; #endif #ifdef METALLIC_REFLECTANCE uniform vec2 vMetallicReflectanceInfos; uniform mat4 metallicReflectanceMatrix; #endif #ifdef REFLECTANCE uniform vec2 vReflectanceInfos; uniform mat4 reflectanceMatrix; #endif #ifdef MICROSURFACEMAP uniform vec2 vMicroSurfaceSamplerInfos; uniform mat4 microSurfaceSamplerMatrix; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform mat4 bumpMatrix; #endif #ifdef POINTSIZE uniform float pointSize; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; uniform mat4 reflectionMatrix; #endif #ifdef CLEARCOAT #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS) uniform vec4 vClearCoatInfos; #endif #ifdef CLEARCOAT_TEXTURE uniform mat4 clearCoatMatrix; #endif #ifdef CLEARCOAT_TEXTURE_ROUGHNESS uniform mat4 clearCoatRoughnessMatrix; #endif #ifdef CLEARCOAT_BUMP uniform vec2 vClearCoatBumpInfos; uniform mat4 clearCoatBumpMatrix; #endif #ifdef CLEARCOAT_TINT_TEXTURE uniform vec2 vClearCoatTintInfos; uniform mat4 clearCoatTintMatrix; #endif #endif #ifdef ANISOTROPIC #ifdef ANISOTROPIC_TEXTURE uniform vec2 vAnisotropyInfos; uniform mat4 anisotropyMatrix; #endif #endif #ifdef SHEEN #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS) uniform vec4 vSheenInfos; #endif #ifdef SHEEN_TEXTURE uniform mat4 sheenMatrix; #endif #ifdef SHEEN_TEXTURE_ROUGHNESS uniform mat4 sheenRoughnessMatrix; #endif #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION uniform vec4 vRefractionInfos; uniform mat4 refractionMatrix; #endif #ifdef SS_THICKNESSANDMASK_TEXTURE uniform vec2 vThicknessInfos; uniform mat4 thicknessMatrix; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE uniform vec2 vRefractionIntensityInfos; uniform mat4 refractionIntensityMatrix; #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE uniform vec2 vTranslucencyIntensityInfos; uniform mat4 translucencyIntensityMatrix; #endif #endif #ifdef NORMAL #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) #ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS uniform vec3 vSphericalL00; uniform vec3 vSphericalL1_1; uniform vec3 vSphericalL10; uniform vec3 vSphericalL11; uniform vec3 vSphericalL2_2; uniform vec3 vSphericalL2_1; uniform vec3 vSphericalL20; uniform vec3 vSphericalL21; uniform vec3 vSphericalL22; #else uniform vec3 vSphericalX; uniform vec3 vSphericalY; uniform vec3 vSphericalZ; uniform vec3 vSphericalXX_ZZ; uniform vec3 vSphericalYY_ZZ; uniform vec3 vSphericalZZ; uniform vec3 vSphericalXY; uniform vec3 vSphericalYZ; uniform vec3 vSphericalZX; #endif #endif #endif #endif #ifdef DETAIL uniform vec4 vDetailInfos; uniform mat4 detailMatrix; #endif";X.a.IncludesShadersStore.pbrVertexDeclaration=rb;c(195),c(196),c(197),c(198),c(101),c(102),c(110),c(111),c(199),c(200),c(201),c(184),c(186);a="precision highp float; #include<__decl__pbrVertex> #define CUSTOM_VERTEX_BEGIN attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #ifdef TANGENT attribute vec4 tangent; #endif #ifdef UV1 attribute vec2 uv; #endif #include[2..7] #include[1..7] #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include #include #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance) #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness) #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness) #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy) #endif #ifdef SUBSURFACE #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity) #endif varying vec3 vPositionW; #if DEBUGMODE>0 varying vec4 vClipSpacePosition; #endif #ifdef NORMAL varying vec3 vNormalW; #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) varying vec3 vEnvironmentIrradiance; #include #endif #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #include #define CUSTOM_VERTEX_DEFINITIONS void main(void) { #define CUSTOM_VERTEX_MAIN_BEGIN vec3 positionUpdated=position; #ifdef NORMAL vec3 normalUpdated=normal; #endif #ifdef TANGENT vec4 tangentUpdated=tangent; #endif #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=positionUpdated; #endif #define CUSTOM_VERTEX_UPDATE_POSITION #define CUSTOM_VERTEX_UPDATE_NORMAL #include #if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); vPositionW=vec3(worldPos); #include #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vNormalW=normalUpdated/vec3(dot(normalWorld[0],normalWorld[0]),dot(normalWorld[1],normalWorld[1]),dot(normalWorld[2],normalWorld[2])); vNormalW=normalize(normalWorld*vNormalW); #else #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normalUpdated); #endif #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) vec3 reflectionVector=vec3(reflectionMatrix*vec4(vNormalW,0)).xyz; #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif vEnvironmentIrradiance=computeEnvironmentIrradiance(reflectionVector); #endif #endif #define CUSTOM_VERTEX_UPDATE_WORLDPOS #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif #if DEBUGMODE>0 vClipSpacePosition=gl_Position; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(positionUpdated,0.0))); #endif #ifndef UV1 vec2 uvUpdated=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uvUpdated; #endif #include[2..7] #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_MATRIXNAME_,albedo,_INFONAME_,AlbedoInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_MATRIXNAME_,emissive,_INFONAME_,EmissiveInfos.x) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_MATRIXNAME_,lightmap,_INFONAME_,LightmapInfos.x) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity,_MATRIXNAME_,reflectivity,_INFONAME_,ReflectivityInfos.x) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler,_MATRIXNAME_,microSurfaceSampler,_INFONAME_,MicroSurfaceSamplerInfos.x) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance,_MATRIXNAME_,metallicReflectance,_INFONAME_,MetallicReflectanceInfos.x) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance,_MATRIXNAME_,reflectance,_INFONAME_,ReflectanceInfos.x) #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_MATRIXNAME_,bump,_INFONAME_,BumpInfos.x) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat,_MATRIXNAME_,clearCoat,_INFONAME_,ClearCoatInfos.x) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness,_MATRIXNAME_,clearCoatRoughness,_INFONAME_,ClearCoatInfos.z) #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump,_MATRIXNAME_,clearCoatBump,_INFONAME_,ClearCoatBumpInfos.x) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint,_MATRIXNAME_,clearCoatTint,_INFONAME_,ClearCoatTintInfos.x) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen,_MATRIXNAME_,sheen,_INFONAME_,SheenInfos.x) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness,_MATRIXNAME_,sheen,_INFONAME_,SheenInfos.z) #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy,_MATRIXNAME_,anisotropy,_INFONAME_,AnisotropyInfos.x) #endif #ifdef SUBSURFACE #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness,_MATRIXNAME_,thickness,_INFONAME_,ThicknessInfos.x) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity,_MATRIXNAME_,refractionIntensity,_INFONAME_,RefractionIntensityInfos.x) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity,_MATRIXNAME_,translucencyIntensity,_INFONAME_,TranslucencyIntensityInfos.x) #endif #include #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #ifdef POINTSIZE gl_PointSize=pointSize; #endif #include #define CUSTOM_VERTEX_MAIN_END }";X.a.ShadersStore.pbrVertexShader=a;var ri=c(100),si={effect:null,subMesh:null},ti=function(a){function b(){var b=a.call(this)||this;return b.PBR=!0,b.NUM_SAMPLES="0",b.REALTIME_FILTERING=!1,b.MAINUV1=!1,b.MAINUV2=!1,b.MAINUV3=!1,b.MAINUV4=!1,b.MAINUV5=!1,b.MAINUV6=!1,b.UV1=!1,b.UV2=!1,b.UV3=!1,b.UV4=!1,b.UV5=!1,b.UV6=!1,b.ALBEDO=!1,b.GAMMAALBEDO=!1,b.ALBEDODIRECTUV=0,b.VERTEXCOLOR=!1,b.DETAIL=!1,b.DETAILDIRECTUV=0,b.DETAIL_NORMALBLENDMETHOD=0,b.AMBIENT=!1,b.AMBIENTDIRECTUV=0,b.AMBIENTINGRAYSCALE=!1,b.OPACITY=!1,b.VERTEXALPHA=!1,b.OPACITYDIRECTUV=0,b.OPACITYRGB=!1,b.ALPHATEST=!1,b.DEPTHPREPASS=!1,b.ALPHABLEND=!1,b.ALPHAFROMALBEDO=!1,b.ALPHATESTVALUE="0.5",b.SPECULAROVERALPHA=!1,b.RADIANCEOVERALPHA=!1,b.ALPHAFRESNEL=!1,b.LINEARALPHAFRESNEL=!1,b.PREMULTIPLYALPHA=!1,b.EMISSIVE=!1,b.EMISSIVEDIRECTUV=0,b.GAMMAEMISSIVE=!1,b.REFLECTIVITY=!1,b.REFLECTIVITY_GAMMA=!1,b.REFLECTIVITYDIRECTUV=0,b.SPECULARTERM=!1,b.MICROSURFACEFROMREFLECTIVITYMAP=!1,b.MICROSURFACEAUTOMATIC=!1,b.LODBASEDMICROSFURACE=!1,b.MICROSURFACEMAP=!1,b.MICROSURFACEMAPDIRECTUV=0,b.METALLICWORKFLOW=!1,b.ROUGHNESSSTOREINMETALMAPALPHA=!1,b.ROUGHNESSSTOREINMETALMAPGREEN=!1,b.METALLNESSSTOREINMETALMAPBLUE=!1,b.AOSTOREINMETALMAPRED=!1,b.METALLIC_REFLECTANCE=!1,b.METALLIC_REFLECTANCE_GAMMA=!1,b.METALLIC_REFLECTANCEDIRECTUV=0,b.METALLIC_REFLECTANCE_USE_ALPHA_ONLY=!1,b.REFLECTANCE=!1,b.REFLECTANCE_GAMMA=!1,b.REFLECTANCEDIRECTUV=0,b.ENVIRONMENTBRDF=!1,b.ENVIRONMENTBRDF_RGBD=!1,b.NORMAL=!1,b.TANGENT=!1,b.BUMP=!1,b.BUMPDIRECTUV=0,b.OBJECTSPACE_NORMALMAP=!1,b.PARALLAX=!1,b.PARALLAXOCCLUSION=!1,b.NORMALXYSCALE=!0,b.LIGHTMAP=!1,b.LIGHTMAPDIRECTUV=0,b.USELIGHTMAPASSHADOWMAP=!1,b.GAMMALIGHTMAP=!1,b.RGBDLIGHTMAP=!1,b.REFLECTION=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.INVERTCUBICMAP=!1,b.USESPHERICALFROMREFLECTIONMAP=!1,b.USEIRRADIANCEMAP=!1,b.SPHERICAL_HARMONICS=!1,b.USESPHERICALINVERTEX=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.LODINREFLECTIONALPHA=!1,b.GAMMAREFLECTION=!1,b.RGBDREFLECTION=!1,b.LINEARSPECULARREFLECTION=!1,b.RADIANCEOCCLUSION=!1,b.HORIZONOCCLUSION=!1,b.INSTANCES=!1,b.THIN_INSTANCES=!1,b.PREPASS=!1,b.PREPASS_IRRADIANCE=!1,b.PREPASS_IRRADIANCE_INDEX=-1,b.PREPASS_ALBEDO_SQRT=!1,b.PREPASS_ALBEDO_SQRT_INDEX=-1,b.PREPASS_DEPTH=!1,b.PREPASS_DEPTH_INDEX=-1,b.PREPASS_NORMAL=!1,b.PREPASS_NORMAL_INDEX=-1,b.PREPASS_POSITION=!1,b.PREPASS_POSITION_INDEX=-1,b.PREPASS_VELOCITY=!1,b.PREPASS_VELOCITY_INDEX=-1,b.PREPASS_REFLECTIVITY=!1,b.PREPASS_REFLECTIVITY_INDEX=-1,b.SCENE_MRT_COUNT=0,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.BONETEXTURE=!1,b.BONES_VELOCITY_ENABLED=!1,b.NONUNIFORMSCALING=!1,b.MORPHTARGETS=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_UV=!1,b.NUM_MORPH_INFLUENCERS=0,b.MORPHTARGETS_TEXTURE=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.EXPOSURE=!1,b.MULTIVIEW=!1,b.ORDER_INDEPENDENT_TRANSPARENCY=!1,b.USEPHYSICALLIGHTFALLOFF=!1,b.USEGLTFLIGHTFALLOFF=!1,b.TWOSIDEDLIGHTING=!1,b.SHADOWFLOAT=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.POINTSIZE=!1,b.FOG=!1,b.LOGARITHMICDEPTH=!1,b.FORCENORMALFORWARD=!1,b.SPECULARAA=!1,b.CLEARCOAT=!1,b.CLEARCOAT_DEFAULTIOR=!1,b.CLEARCOAT_TEXTURE=!1,b.CLEARCOAT_TEXTURE_ROUGHNESS=!1,b.CLEARCOAT_TEXTUREDIRECTUV=0,b.CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV=0,b.CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,b.CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL=!1,b.CLEARCOAT_BUMP=!1,b.CLEARCOAT_BUMPDIRECTUV=0,b.CLEARCOAT_REMAP_F0=!0,b.CLEARCOAT_TINT=!1,b.CLEARCOAT_TINT_TEXTURE=!1,b.CLEARCOAT_TINT_GAMMATEXTURE=!1,b.CLEARCOAT_TINT_TEXTUREDIRECTUV=0,b.ANISOTROPIC=!1,b.ANISOTROPIC_TEXTURE=!1,b.ANISOTROPIC_TEXTUREDIRECTUV=0,b.BRDF_V_HEIGHT_CORRELATED=!1,b.MS_BRDF_ENERGY_CONSERVATION=!1,b.SPECULAR_GLOSSINESS_ENERGY_CONSERVATION=!1,b.SHEEN=!1,b.SHEEN_TEXTURE=!1,b.SHEEN_GAMMATEXTURE=!1,b.SHEEN_TEXTURE_ROUGHNESS=!1,b.SHEEN_TEXTUREDIRECTUV=0,b.SHEEN_TEXTURE_ROUGHNESSDIRECTUV=0,b.SHEEN_LINKWITHALBEDO=!1,b.SHEEN_ROUGHNESS=!1,b.SHEEN_ALBEDOSCALING=!1,b.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,b.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=!1,b.SUBSURFACE=!1,b.SS_REFRACTION=!1,b.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=!1,b.SS_TRANSLUCENCY=!1,b.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=!1,b.SS_SCATTERING=!1,b.SS_THICKNESSANDMASK_TEXTURE=!1,b.SS_THICKNESSANDMASK_TEXTUREDIRECTUV=0,b.SS_HAS_THICKNESS=!1,b.SS_REFRACTIONINTENSITY_TEXTURE=!1,b.SS_REFRACTIONINTENSITY_TEXTUREDIRECTUV=0,b.SS_TRANSLUCENCYINTENSITY_TEXTURE=!1,b.SS_TRANSLUCENCYINTENSITY_TEXTUREDIRECTUV=0,b.SS_REFRACTIONMAP_3D=!1,b.SS_REFRACTIONMAP_OPPOSITEZ=!1,b.SS_LODINREFRACTIONALPHA=!1,b.SS_GAMMAREFRACTION=!1,b.SS_RGBDREFRACTION=!1,b.SS_LINEARSPECULARREFRACTION=!1,b.SS_LINKREFRACTIONTOTRANSPARENCY=!1,b.SS_ALBEDOFORREFRACTIONTINT=!1,b.SS_ALBEDOFORTRANSLUCENCYTINT=!1,b.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=!1,b.SS_USE_THICKNESS_AS_DEPTH=!1,b.SS_MASK_FROM_THICKNESS_TEXTURE=!1,b.SS_USE_GLTF_TEXTURES=!1,b.UNLIT=!1,b.DEBUGMODE=0,b.rebuild(),b}return Object(l.d)(b,a),b.prototype.reset=function(){a.prototype.reset.call(this),this.ALPHATESTVALUE="0.5",this.PBR=!0,this.NORMALXYSCALE=!0},b}(Zh.a),ui=function(a){function b(c,d){var e=a.call(this,c,d)||this;return e._directIntensity=1,e._emissiveIntensity=1,e._environmentIntensity=1,e._specularIntensity=1,e._lightingInfos=new g.f(e._directIntensity,e._emissiveIntensity,e._environmentIntensity,e._specularIntensity),e._disableBumpMap=!1,e._albedoTexture=null,e._ambientTexture=null,e._ambientTextureStrength=1,e._ambientTextureImpactOnAnalyticalLights=b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,e._opacityTexture=null,e._reflectionTexture=null,e._emissiveTexture=null,e._reflectivityTexture=null,e._metallicTexture=null,e._metallic=null,e._roughness=null,e._metallicF0Factor=1,e._metallicReflectanceColor=h.a.White(),e._useOnlyMetallicFromMetallicReflectanceTexture=!1,e._metallicReflectanceTexture=null,e._reflectanceTexture=null,e._microSurfaceTexture=null,e._bumpTexture=null,e._lightmapTexture=null,e._ambientColor=new h.a(0,0,0),e._albedoColor=new h.a(1,1,1),e._reflectivityColor=new h.a(1,1,1),e._reflectionColor=new h.a(1,1,1),e._emissiveColor=new h.a(0,0,0),e._microSurface=.9,e._useLightmapAsShadowmap=!1,e._useHorizonOcclusion=!0,e._useRadianceOcclusion=!0,e._useAlphaFromAlbedoTexture=!1,e._useSpecularOverAlpha=!0,e._useMicroSurfaceFromReflectivityMapAlpha=!1,e._useRoughnessFromMetallicTextureAlpha=!0,e._useRoughnessFromMetallicTextureGreen=!1,e._useMetallnessFromMetallicTextureBlue=!1,e._useAmbientOcclusionFromMetallicTextureRed=!1,e._useAmbientInGrayScale=!1,e._useAutoMicroSurfaceFromReflectivityMap=!1,e._lightFalloff=b.LIGHTFALLOFF_PHYSICAL,e._useRadianceOverAlpha=!0,e._useObjectSpaceNormalMap=!1,e._useParallax=!1,e._useParallaxOcclusion=!1,e._parallaxScaleBias=.05,e._disableLighting=!1,e._maxSimultaneousLights=4,e._invertNormalMapX=!1,e._invertNormalMapY=!1,e._twoSidedLighting=!1,e._alphaCutOff=.4,e._forceAlphaTest=!1,e._useAlphaFresnel=!1,e._useLinearAlphaFresnel=!1,e._environmentBRDFTexture=null,e._forceIrradianceInFragment=!1,e._realTimeFiltering=!1,e._realTimeFilteringQuality=r.a.TEXTURE_FILTERING_QUALITY_LOW,e._forceNormalForward=!1,e._enableSpecularAntiAliasing=!1,e._imageProcessingObserver=null,e._renderTargets=new Ac.a(16),e._globalAmbientColor=new h.a(0,0,0),e._useLogarithmicDepth=!1,e._unlit=!1,e._debugMode=0,e.debugMode=0,e.debugLimit=-1,e.debugFactor=1,e.clearCoat=new ki(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.anisotropy=new li(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.brdf=new mi(e._markAllSubMeshesAsMiscDirty.bind(e)),e.sheen=new ni(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.detailMap=new ri.a(e._markAllSubMeshesAsTexturesDirty.bind(e)),e._attachImageProcessingConfiguration(null),e.getRenderTargetTextures=function(){return e._renderTargets.reset(),ai.a.ReflectionTextureEnabled&&e._reflectionTexture&&e._reflectionTexture.isRenderTarget&&e._renderTargets.push(e._reflectionTexture),e.subSurface.fillRenderTargetTextures(e._renderTargets),e._renderTargets},e._environmentBRDFTexture=ii(d),e.subSurface=new oi(e._markAllSubMeshesAsTexturesDirty.bind(e),e._markScenePrePassDirty.bind(e),d),e.prePassConfiguration=new pi.a,e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"realTimeFiltering",{get:function(){return this._realTimeFiltering},set:function(a){this._realTimeFiltering=a,this.markAsDirty(r.a.MATERIAL_TextureDirtyFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"realTimeFilteringQuality",{get:function(){return this._realTimeFilteringQuality},set:function(a){this._realTimeFilteringQuality=a,this.markAsDirty(r.a.MATERIAL_TextureDirtyFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"canRenderToMRT",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a){var b=this;a!==this._imageProcessingConfiguration&&(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration=a||this.getScene().imageProcessingConfiguration,this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){b._markAllSubMeshesAsImageProcessingDirty()})))},Object.defineProperty(b.prototype,"hasRenderTargetTextures",{get:function(){return!!(ai.a.ReflectionTextureEnabled&&this._reflectionTexture&&this._reflectionTexture.isRenderTarget)||this.subSurface.hasRenderTargetTextures()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isPrePassCapable",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PBRBaseMaterial"},Object.defineProperty(b.prototype,"useLogarithmicDepth",{get:function(){return this._useLogarithmicDepth},set:function(a){this._useLogarithmicDepth=a&&this.getScene().getEngine().getCaps().fragmentDepthSupported},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"_disableAlphaBlending",{get:function(){return this.subSurface.disableAlphaBlending||this._transparencyMode===b.PBRMATERIAL_OPAQUE||this._transparencyMode===b.PBRMATERIAL_ALPHATEST},enumerable:!1,configurable:!0}),b.prototype.needAlphaBlending=function(){return!this._disableAlphaBlending&&(this.alpha<1||null!=this._opacityTexture||this._shouldUseAlphaFromAlbedoTexture())},b.prototype.needAlphaTesting=function(){return!!this._forceAlphaTest||!this.subSurface.disableAlphaBlending&&this._hasAlphaChannel()&&(null==this._transparencyMode||this._transparencyMode===b.PBRMATERIAL_ALPHATEST)},b.prototype._shouldUseAlphaFromAlbedoTexture=function(){return null!=this._albedoTexture&&this._albedoTexture.hasAlpha&&this._useAlphaFromAlbedoTexture&&this._transparencyMode!==b.PBRMATERIAL_OPAQUE},b.prototype._hasAlphaChannel=function(){return null!=this._albedoTexture&&this._albedoTexture.hasAlpha||null!=this._opacityTexture},b.prototype.getAlphaTestTexture=function(){return this._albedoTexture},b.prototype.isReadyForSubMesh=function(a,b,c){if(b.effect&&this.isFrozen&&b.effect._wasPreviouslyReady)return!0;b._materialDefines||(b.materialDefines=new ti);var d=b._materialDefines;if(this._isReadyForSubMesh(b))return!0;var e=this.getScene(),f=e.getEngine();if(d._areTexturesDirty&&e.texturesEnabled){if(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&!this._albedoTexture.isReadyOrNotBlocking())return!1;if(this._ambientTexture&&ai.a.AmbientTextureEnabled&&!this._ambientTexture.isReadyOrNotBlocking())return!1;if(this._opacityTexture&&ai.a.OpacityTextureEnabled&&!this._opacityTexture.isReadyOrNotBlocking())return!1;var g=this._getReflectionTexture();if(g&&ai.a.ReflectionTextureEnabled){if(!g.isReadyOrNotBlocking())return!1;if(g.irradianceTexture&&!g.irradianceTexture.isReadyOrNotBlocking())return!1}if(this._lightmapTexture&&ai.a.LightmapTextureEnabled&&!this._lightmapTexture.isReadyOrNotBlocking())return!1;if(this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&!this._emissiveTexture.isReadyOrNotBlocking())return!1;if(ai.a.SpecularTextureEnabled){if(this._metallicTexture){if(!this._metallicTexture.isReadyOrNotBlocking())return!1}else if(this._reflectivityTexture&&!this._reflectivityTexture.isReadyOrNotBlocking())return!1;if(this._metallicReflectanceTexture&&!this._metallicReflectanceTexture.isReadyOrNotBlocking())return!1;if(this._reflectanceTexture&&!this._reflectanceTexture.isReadyOrNotBlocking())return!1;if(this._microSurfaceTexture&&!this._microSurfaceTexture.isReadyOrNotBlocking())return!1}if(f.getCaps().standardDerivatives&&this._bumpTexture&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&!this._bumpTexture.isReady())return!1;if(this._environmentBRDFTexture&&ai.a.ReflectionTextureEnabled&&!this._environmentBRDFTexture.isReady())return!1}if(!(this.subSurface.isReadyForSubMesh(d,e)&&this.clearCoat.isReadyForSubMesh(d,e,f,this._disableBumpMap)&&this.sheen.isReadyForSubMesh(d,e)&&this.anisotropy.isReadyForSubMesh(d,e)&&this.detailMap.isReadyForSubMesh(d,e)))return!1;if(d._areImageProcessingDirty&&this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.isReady())return!1;f.getCaps().standardDerivatives||a.isVerticesDataPresent(W.b.NormalKind)||(a.createNormals(!0),q.a.Warn("PBRMaterial: Normals have been created for the mesh: "+a.name));g=b.effect;f=d._areLightsDisposed;a=this._prepareEffect(a,d,this.onCompiled,this.onError,c,null,b.getRenderingMesh().hasThinInstances);if(a)if(this._onEffectCreatedObservable&&(si.effect=a,si.subMesh=b,this._onEffectCreatedObservable.notifyObservers(si)),this.allowShaderHotSwapping&&g&&!a.isReady()){if(a=g,d.markAsUnprocessed(),f)return d._areLightsDisposed=!0,!1}else e.resetCachedMaterial(),b.setEffect(a,d,this._materialContext),this.buildUniformLayout();return!(!b.effect||!b.effect.isReady())&&(d._renderId=e.getRenderId(),b.effect._wasPreviouslyReady=!0,!0)},b.prototype.isMetallicWorkflow=function(){return!(null==this._metallic&&null==this._roughness&&!this._metallicTexture)},b.prototype._prepareEffect=function(a,b,c,d,e,f,g){if(void 0===c&&(c=null),void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),this._prepareDefines(a,b,e,f,g),!b.isDirty)return null;b.markAsProcessed();e=this.getScene().getEngine();f=new bi.a;g=0;b.USESPHERICALINVERTEX&&f.addFallback(g++,"USESPHERICALINVERTEX"),b.FOG&&f.addFallback(g,"FOG"),b.SPECULARAA&&f.addFallback(g,"SPECULARAA"),b.POINTSIZE&&f.addFallback(g,"POINTSIZE"),b.LOGARITHMICDEPTH&&f.addFallback(g,"LOGARITHMICDEPTH"),b.PARALLAX&&f.addFallback(g,"PARALLAX"),b.PARALLAXOCCLUSION&&f.addFallback(g++,"PARALLAXOCCLUSION"),g=li.AddFallbacks(b,f,g),g=li.AddFallbacks(b,f,g),g=oi.AddFallbacks(b,f,g),g=ni.AddFallbacks(b,f,g),b.ENVIRONMENTBRDF&&f.addFallback(g++,"ENVIRONMENTBRDF"),b.TANGENT&&f.addFallback(g++,"TANGENT"),b.BUMP&&f.addFallback(g++,"BUMP"),g=Yh.a.HandleFallbacksForShadows(b,f,this._maxSimultaneousLights,g++),b.SPECULARTERM&&f.addFallback(g++,"SPECULARTERM"),b.USESPHERICALFROMREFLECTIONMAP&&f.addFallback(g++,"USESPHERICALFROMREFLECTIONMAP"),b.USEIRRADIANCEMAP&&f.addFallback(g++,"USEIRRADIANCEMAP"),b.LIGHTMAP&&f.addFallback(g++,"LIGHTMAP"),b.NORMAL&&f.addFallback(g++,"NORMAL"),b.AMBIENT&&f.addFallback(g++,"AMBIENT"),b.EMISSIVE&&f.addFallback(g++,"EMISSIVE"),b.VERTEXCOLOR&&f.addFallback(g++,"VERTEXCOLOR"),b.MORPHTARGETS&&f.addFallback(g++,"MORPHTARGETS"),b.MULTIVIEW&&f.addFallback(0,"MULTIVIEW");g=[W.b.PositionKind];b.NORMAL&&g.push(W.b.NormalKind),b.TANGENT&&g.push(W.b.TangentKind);for(var h=1;h<=r.a.MAX_SUPPORTED_UV_SETS;++h)b["UV"+h]&&g.push("uv"+(1===h?"":h));b.VERTEXCOLOR&&g.push(W.b.ColorKind),Yh.a.PrepareAttributesForBones(g,a,b,f),Yh.a.PrepareAttributesForInstances(g,b),Yh.a.PrepareAttributesForMorphTargets(g,a,b);h="pbr";a=["world","view","viewProjection","vEyePosition","vLightsType","vAmbientColor","vAlbedoColor","vReflectivityColor","vMetallicReflectanceFactors","vEmissiveColor","visibility","vReflectionColor","vFogInfos","vFogColor","pointSize","vAlbedoInfos","vAmbientInfos","vOpacityInfos","vReflectionInfos","vReflectionPosition","vReflectionSize","vEmissiveInfos","vReflectivityInfos","vReflectionFilteringInfo","vMetallicReflectanceInfos","vReflectanceInfos","vMicroSurfaceSamplerInfos","vBumpInfos","vLightmapInfos","mBones","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","albedoMatrix","ambientMatrix","opacityMatrix","reflectionMatrix","emissiveMatrix","reflectivityMatrix","normalMatrix","microSurfaceSamplerMatrix","bumpMatrix","lightmapMatrix","metallicReflectanceMatrix","reflectanceMatrix","vLightingIntensity","logarithmicDepthConstant","vSphericalX","vSphericalY","vSphericalZ","vSphericalXX_ZZ","vSphericalYY_ZZ","vSphericalZZ","vSphericalXY","vSphericalYZ","vSphericalZX","vSphericalL00","vSphericalL1_1","vSphericalL10","vSphericalL11","vSphericalL2_2","vSphericalL2_1","vSphericalL20","vSphericalL21","vSphericalL22","vReflectionMicrosurfaceInfos","vTangentSpaceParams","boneTextureWidth","vDebugMode","morphTargetTextureInfo","morphTargetTextureIndices"];var i=["albedoSampler","reflectivitySampler","ambientSampler","emissiveSampler","bumpSampler","lightmapSampler","opacitySampler","reflectionSampler","reflectionSamplerLow","reflectionSamplerHigh","irradianceSampler","microSurfaceSampler","environmentBrdfSampler","boneSampler","metallicReflectanceSampler","reflectanceSampler","morphTargets","oitDepthSampler","oitFrontColorSampler"],j=["Material","Scene","Mesh"];ri.a.AddUniforms(a),ri.a.AddSamplers(i),oi.AddUniforms(a),oi.AddSamplers(i),ki.AddUniforms(a),ki.AddSamplers(i),li.AddUniforms(a),li.AddSamplers(i),ni.AddUniforms(a),ni.AddSamplers(i),pi.a.AddUniforms(a),pi.a.AddSamplers(i),od.a&&(od.a.PrepareUniforms(a,b),od.a.PrepareSamplers(i,b)),Yh.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:b,maxSimultaneousLights:this._maxSimultaneousLights});var k={};this.customShaderNameResolve&&(h=this.customShaderNameResolve(h,a,j,i,b,g,k));var m=b.toString();return e.createEffect(h,{attributes:g,uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:m,fallbacks:f,onCompiled:c,onError:d,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights,maxSimultaneousMorphTargets:b.NUM_MORPH_INFLUENCERS},processFinalCode:k.processFinalCode,multiTarget:b.PREPASS},e)},b.prototype._prepareDefines=function(a,c,d,e,f){void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=!1);var g=this.getScene(),h=g.getEngine();Yh.a.PrepareDefinesForLights(g,a,c,!0,this._maxSimultaneousLights,this._disableLighting),c._needNormals=!0,Yh.a.PrepareDefinesForMultiview(g,c);var i=this.needAlphaBlendingForMesh(a)&&this.getScene().useOrderIndependentTransparency;if(Yh.a.PrepareDefinesForPrePass(g,c,this.canRenderToMRT&&!i),Yh.a.PrepareDefinesForOIT(g,c,i),c.METALLICWORKFLOW=this.isMetallicWorkflow(),c._areTexturesDirty){if(c._needUVs=!1,g.texturesEnabled){g.getEngine().getCaps().textureLOD&&(c.LODBASEDMICROSFURACE=!0),this._albedoTexture&&ai.a.DiffuseTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._albedoTexture,c,"ALBEDO"),c.GAMMAALBEDO=this._albedoTexture.gammaSpace):c.ALBEDO=!1,this._ambientTexture&&ai.a.AmbientTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._ambientTexture,c,"AMBIENT"),c.AMBIENTINGRAYSCALE=this._useAmbientInGrayScale):c.AMBIENT=!1,this._opacityTexture&&ai.a.OpacityTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._opacityTexture,c,"OPACITY"),c.OPACITYRGB=this._opacityTexture.getAlphaFromRGB):c.OPACITY=!1;i=this._getReflectionTexture();if(i&&ai.a.ReflectionTextureEnabled){switch(c.REFLECTION=!0,c.GAMMAREFLECTION=i.gammaSpace,c.RGBDREFLECTION=i.isRGBD,c.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!i.invertZ:i.invertZ,c.LODINREFLECTIONALPHA=i.lodLevelInAlpha,c.LINEARSPECULARREFLECTION=i.linearSpecularLOD,this.realTimeFiltering&&this.realTimeFilteringQuality>0?(c.NUM_SAMPLES=""+this.realTimeFilteringQuality,h._features.needTypeSuffixInShaderConstants&&(c.NUM_SAMPLES=c.NUM_SAMPLES+"u"),c.REALTIME_FILTERING=!0):c.REALTIME_FILTERING=!1,i.coordinatesMode===V.a.INVCUBIC_MODE&&(c.INVERTCUBICMAP=!0),c.REFLECTIONMAP_3D=i.isCube,c.REFLECTIONMAP_CUBIC=!1,c.REFLECTIONMAP_EXPLICIT=!1,c.REFLECTIONMAP_PLANAR=!1,c.REFLECTIONMAP_PROJECTION=!1,c.REFLECTIONMAP_SKYBOX=!1,c.REFLECTIONMAP_SPHERICAL=!1,c.REFLECTIONMAP_EQUIRECTANGULAR=!1,c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,i.coordinatesMode){case V.a.EXPLICIT_MODE:c.REFLECTIONMAP_EXPLICIT=!0;break;case V.a.PLANAR_MODE:c.REFLECTIONMAP_PLANAR=!0;break;case V.a.PROJECTION_MODE:c.REFLECTIONMAP_PROJECTION=!0;break;case V.a.SKYBOX_MODE:c.REFLECTIONMAP_SKYBOX=!0;break;case V.a.SPHERICAL_MODE:c.REFLECTIONMAP_SPHERICAL=!0;break;case V.a.EQUIRECTANGULAR_MODE:c.REFLECTIONMAP_EQUIRECTANGULAR=!0;break;case V.a.FIXED_EQUIRECTANGULAR_MODE:c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!0;break;case V.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!0;break;case V.a.CUBIC_MODE:case V.a.INVCUBIC_MODE:default:c.REFLECTIONMAP_CUBIC=!0,c.USE_LOCAL_REFLECTIONMAP_CUBIC=!!i.boundingBoxSize}i.coordinatesMode!==V.a.SKYBOX_MODE&&(i.irradianceTexture?(c.USEIRRADIANCEMAP=!0,c.USESPHERICALFROMREFLECTIONMAP=!1):i.isCube&&(c.USESPHERICALFROMREFLECTIONMAP=!0,c.USEIRRADIANCEMAP=!1,this._forceIrradianceInFragment||this.realTimeFiltering||g.getEngine().getCaps().maxVaryingVectors<=8?c.USESPHERICALINVERTEX=!1:c.USESPHERICALINVERTEX=!0))}else c.REFLECTION=!1,c.REFLECTIONMAP_3D=!1,c.REFLECTIONMAP_SPHERICAL=!1,c.REFLECTIONMAP_PLANAR=!1,c.REFLECTIONMAP_CUBIC=!1,c.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,c.REFLECTIONMAP_PROJECTION=!1,c.REFLECTIONMAP_SKYBOX=!1,c.REFLECTIONMAP_EXPLICIT=!1,c.REFLECTIONMAP_EQUIRECTANGULAR=!1,c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,c.INVERTCUBICMAP=!1,c.USESPHERICALFROMREFLECTIONMAP=!1,c.USEIRRADIANCEMAP=!1,c.USESPHERICALINVERTEX=!1,c.REFLECTIONMAP_OPPOSITEZ=!1,c.LODINREFLECTIONALPHA=!1,c.GAMMAREFLECTION=!1,c.RGBDREFLECTION=!1,c.LINEARSPECULARREFLECTION=!1;if(this._lightmapTexture&&ai.a.LightmapTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._lightmapTexture,c,"LIGHTMAP"),c.USELIGHTMAPASSHADOWMAP=this._useLightmapAsShadowmap,c.GAMMALIGHTMAP=this._lightmapTexture.gammaSpace,c.RGBDLIGHTMAP=this._lightmapTexture.isRGBD):c.LIGHTMAP=!1,this._emissiveTexture&&ai.a.EmissiveTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._emissiveTexture,c,"EMISSIVE"),c.GAMMAEMISSIVE=this._emissiveTexture.gammaSpace):c.EMISSIVE=!1,ai.a.SpecularTextureEnabled){if(this._metallicTexture?(Yh.a.PrepareDefinesForMergedUV(this._metallicTexture,c,"REFLECTIVITY"),c.ROUGHNESSSTOREINMETALMAPALPHA=this._useRoughnessFromMetallicTextureAlpha,c.ROUGHNESSSTOREINMETALMAPGREEN=!this._useRoughnessFromMetallicTextureAlpha&&this._useRoughnessFromMetallicTextureGreen,c.METALLNESSSTOREINMETALMAPBLUE=this._useMetallnessFromMetallicTextureBlue,c.AOSTOREINMETALMAPRED=this._useAmbientOcclusionFromMetallicTextureRed,c.REFLECTIVITY_GAMMA=!1):this._reflectivityTexture?(Yh.a.PrepareDefinesForMergedUV(this._reflectivityTexture,c,"REFLECTIVITY"),c.MICROSURFACEFROMREFLECTIVITYMAP=this._useMicroSurfaceFromReflectivityMapAlpha,c.MICROSURFACEAUTOMATIC=this._useAutoMicroSurfaceFromReflectivityMap,c.REFLECTIVITY_GAMMA=this._reflectivityTexture.gammaSpace):c.REFLECTIVITY=!1,this._metallicReflectanceTexture||this._reflectanceTexture){i=null!==this._metallicReflectanceTexture&&this._metallicReflectanceTexture._texture===(null===(i=this._reflectanceTexture)||void 0===i?void 0:i._texture)&&this._metallicReflectanceTexture.checkTransformsAreIdentical(this._reflectanceTexture);c.METALLIC_REFLECTANCE_USE_ALPHA_ONLY=this._useOnlyMetallicFromMetallicReflectanceTexture&&!i,this._metallicReflectanceTexture?(Yh.a.PrepareDefinesForMergedUV(this._metallicReflectanceTexture,c,"METALLIC_REFLECTANCE"),c.METALLIC_REFLECTANCE_GAMMA=this._metallicReflectanceTexture.gammaSpace):c.METALLIC_REFLECTANCE=!1,this._reflectanceTexture&&!i&&(!this._metallicReflectanceTexture||this._metallicReflectanceTexture&&this._useOnlyMetallicFromMetallicReflectanceTexture)?(Yh.a.PrepareDefinesForMergedUV(this._reflectanceTexture,c,"REFLECTANCE"),c.REFLECTANCE_GAMMA=this._reflectanceTexture.gammaSpace):c.REFLECTANCE=!1}else c.METALLIC_REFLECTANCE=!1,c.REFLECTANCE=!1;this._microSurfaceTexture?Yh.a.PrepareDefinesForMergedUV(this._microSurfaceTexture,c,"MICROSURFACEMAP"):c.MICROSURFACEMAP=!1}else c.REFLECTIVITY=!1,c.MICROSURFACEMAP=!1;g.getEngine().getCaps().standardDerivatives&&this._bumpTexture&&ai.a.BumpTextureEnabled&&!this._disableBumpMap?(Yh.a.PrepareDefinesForMergedUV(this._bumpTexture,c,"BUMP"),this._useParallax&&this._albedoTexture&&ai.a.DiffuseTextureEnabled?(c.PARALLAX=!0,c.PARALLAXOCCLUSION=!!this._useParallaxOcclusion):c.PARALLAX=!1,c.OBJECTSPACE_NORMALMAP=this._useObjectSpaceNormalMap):c.BUMP=!1,this._environmentBRDFTexture&&ai.a.ReflectionTextureEnabled?(c.ENVIRONMENTBRDF=!0,c.ENVIRONMENTBRDF_RGBD=this._environmentBRDFTexture.isRGBD):(c.ENVIRONMENTBRDF=!1,c.ENVIRONMENTBRDF_RGBD=!1),this._shouldUseAlphaFromAlbedoTexture()?c.ALPHAFROMALBEDO=!0:c.ALPHAFROMALBEDO=!1}c.SPECULAROVERALPHA=this._useSpecularOverAlpha,this._lightFalloff===b.LIGHTFALLOFF_STANDARD?(c.USEPHYSICALLIGHTFALLOFF=!1,c.USEGLTFLIGHTFALLOFF=!1):this._lightFalloff===b.LIGHTFALLOFF_GLTF?(c.USEPHYSICALLIGHTFALLOFF=!1,c.USEGLTFLIGHTFALLOFF=!0):(c.USEPHYSICALLIGHTFALLOFF=!0,c.USEGLTFLIGHTFALLOFF=!1),c.RADIANCEOVERALPHA=this._useRadianceOverAlpha,!this.backFaceCulling&&this._twoSidedLighting?c.TWOSIDEDLIGHTING=!0:c.TWOSIDEDLIGHTING=!1,c.SPECULARAA=g.getEngine().getCaps().standardDerivatives&&this._enableSpecularAntiAliasing}(c._areTexturesDirty||c._areMiscDirty)&&(c.ALPHATESTVALUE=this._alphaCutOff+(this._alphaCutOff%1==0?".":""),c.PREMULTIPLYALPHA=this.alphaMode===r.a.ALPHA_PREMULTIPLIED||this.alphaMode===r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,c.ALPHABLEND=this.needAlphaBlendingForMesh(a),c.ALPHAFRESNEL=this._useAlphaFresnel||this._useLinearAlphaFresnel,c.LINEARALPHAFRESNEL=this._useLinearAlphaFresnel),c._areImageProcessingDirty&&this._imageProcessingConfiguration&&this._imageProcessingConfiguration.prepareDefines(c),c.FORCENORMALFORWARD=this._forceNormalForward,c.RADIANCEOCCLUSION=this._useRadianceOcclusion,c.HORIZONOCCLUSION=this._useHorizonOcclusion,c._areMiscDirty&&(Yh.a.PrepareDefinesForMisc(a,g,this._useLogarithmicDepth,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a)||this._forceAlphaTest,c),c.UNLIT=this._unlit||(this.pointsCloud||this.wireframe)&&!a.isVerticesDataPresent(W.b.NormalKind),c.DEBUGMODE=this._debugMode),this.detailMap.prepareDefines(c,g),this.subSurface.prepareDefines(c,g),this.clearCoat.prepareDefines(c,g),this.anisotropy.prepareDefines(c,a,g),this.brdf.prepareDefines(c),this.sheen.prepareDefines(c,g),Yh.a.PrepareDefinesForFrameBoundValues(g,h,c,!!d,e,f),Yh.a.PrepareDefinesForAttributes(a,c,!0,!0,!0,this._transparencyMode!==b.PBRMATERIAL_OPAQUE)},b.prototype.forceCompilation=function(a,b,c){var d=this;c=Object(l.a)({clipPlane:!1,useInstances:!1},c);var e=new ti;e=this._prepareEffect(a,e,void 0,void 0,c.useInstances,c.clipPlane,a.hasThinInstances);this._onEffectCreatedObservable&&(si.effect=e,si.subMesh=null,this._onEffectCreatedObservable.notifyObservers(si)),e.isReady()?b&&b(this):e.onCompileObservable.add(function(){b&&b(d)})},b.prototype.buildUniformLayout=function(){var a=this._uniformBuffer;a.addUniform("vAlbedoInfos",2),a.addUniform("vAmbientInfos",4),a.addUniform("vOpacityInfos",2),a.addUniform("vEmissiveInfos",2),a.addUniform("vLightmapInfos",2),a.addUniform("vReflectivityInfos",3),a.addUniform("vMicroSurfaceSamplerInfos",2),a.addUniform("vReflectionInfos",2),a.addUniform("vReflectionFilteringInfo",2),a.addUniform("vReflectionPosition",3),a.addUniform("vReflectionSize",3),a.addUniform("vBumpInfos",3),a.addUniform("albedoMatrix",16),a.addUniform("ambientMatrix",16),a.addUniform("opacityMatrix",16),a.addUniform("emissiveMatrix",16),a.addUniform("lightmapMatrix",16),a.addUniform("reflectivityMatrix",16),a.addUniform("microSurfaceSamplerMatrix",16),a.addUniform("bumpMatrix",16),a.addUniform("vTangentSpaceParams",2),a.addUniform("reflectionMatrix",16),a.addUniform("vReflectionColor",3),a.addUniform("vAlbedoColor",4),a.addUniform("vLightingIntensity",4),a.addUniform("vReflectionMicrosurfaceInfos",3),a.addUniform("pointSize",1),a.addUniform("vReflectivityColor",4),a.addUniform("vEmissiveColor",3),a.addUniform("vAmbientColor",3),a.addUniform("vDebugMode",2),a.addUniform("vMetallicReflectanceFactors",4),a.addUniform("vMetallicReflectanceInfos",2),a.addUniform("metallicReflectanceMatrix",16),a.addUniform("vReflectanceInfos",2),a.addUniform("reflectanceMatrix",16),ki.PrepareUniformBuffer(a),li.PrepareUniformBuffer(a),ni.PrepareUniformBuffer(a),oi.PrepareUniformBuffer(a),ri.a.PrepareUniformBuffer(a),a.addUniform("vSphericalL00",3),a.addUniform("vSphericalL1_1",3),a.addUniform("vSphericalL10",3),a.addUniform("vSphericalL11",3),a.addUniform("vSphericalL2_2",3),a.addUniform("vSphericalL2_1",3),a.addUniform("vSphericalL20",3),a.addUniform("vSphericalL21",3),a.addUniform("vSphericalL22",3),a.addUniform("vSphericalX",3),a.addUniform("vSphericalY",3),a.addUniform("vSphericalZ",3),a.addUniform("vSphericalXX_ZZ",3),a.addUniform("vSphericalYY_ZZ",3),a.addUniform("vSphericalZZ",3),a.addUniform("vSphericalXY",3),a.addUniform("vSphericalYZ",3),a.addUniform("vSphericalZX",3),a.create()},b.prototype.unbind=function(){if(this._activeEffect){var b=!1;this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&(this._activeEffect.setTexture("reflection2DSampler",null),b=!0),this.subSurface.unbind(this._activeEffect)&&(b=!0),b&&this._markAllSubMeshesAsTexturesDirty()}a.prototype.unbind.call(this)},b.prototype.bindForSubMesh=function(a,b,c){var d=this.getScene(),e=c._materialDefines;if(e){var f=c.effect;if(f){this._activeEffect=f,b.getMeshUniformBuffer().bindToEffect(f,"Mesh"),b.transferToEffect(a),this.prePassConfiguration.bindForSubMesh(this._activeEffect,d,b,a,this.isFrozen),e.OBJECTSPACE_NORMALMAP&&(a.toNormalMatrix(this._normalMatrix),this.bindOnlyNormalMatrix(this._normalMatrix));a=this._mustRebind(d,f,b.visibility);Yh.a.BindBonesParameters(b,this._activeEffect,this.prePassConfiguration);var g=null,i=this._uniformBuffer;if(a){var j=d.getEngine();if(i.bindToEffect(f,"Material"),this.bindViewProjection(f),g=this._getReflectionTexture(),!i.useUbo||!this.isFrozen||!i.isSync){if(d.texturesEnabled){if(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&(i.updateFloat2("vAlbedoInfos",this._albedoTexture.coordinatesIndex,this._albedoTexture.level),Yh.a.BindTextureMatrix(this._albedoTexture,i,"albedo")),this._ambientTexture&&ai.a.AmbientTextureEnabled&&(i.updateFloat4("vAmbientInfos",this._ambientTexture.coordinatesIndex,this._ambientTexture.level,this._ambientTextureStrength,this._ambientTextureImpactOnAnalyticalLights),Yh.a.BindTextureMatrix(this._ambientTexture,i,"ambient")),this._opacityTexture&&ai.a.OpacityTextureEnabled&&(i.updateFloat2("vOpacityInfos",this._opacityTexture.coordinatesIndex,this._opacityTexture.level),Yh.a.BindTextureMatrix(this._opacityTexture,i,"opacity")),g&&ai.a.ReflectionTextureEnabled){if(i.updateMatrix("reflectionMatrix",g.getReflectionTextureMatrix()),i.updateFloat2("vReflectionInfos",g.level,0),g.boundingBoxSize){var k=g;i.updateVector3("vReflectionPosition",k.boundingBoxPosition),i.updateVector3("vReflectionSize",k.boundingBoxSize)}if(this.realTimeFiltering){k=g.getSize().width;i.updateFloat2("vReflectionFilteringInfo",k,I.a.Log2(k))}if(!e.USEIRRADIANCEMAP){k=g.sphericalPolynomial;if(e.USESPHERICALFROMREFLECTIONMAP&&k)if(e.SPHERICAL_HARMONICS){var m=k.preScaledHarmonics;i.updateVector3("vSphericalL00",m.l00),i.updateVector3("vSphericalL1_1",m.l1_1),i.updateVector3("vSphericalL10",m.l10),i.updateVector3("vSphericalL11",m.l11),i.updateVector3("vSphericalL2_2",m.l2_2),i.updateVector3("vSphericalL2_1",m.l2_1),i.updateVector3("vSphericalL20",m.l20),i.updateVector3("vSphericalL21",m.l21),i.updateVector3("vSphericalL22",m.l22)}else i.updateFloat3("vSphericalX",k.x.x,k.x.y,k.x.z),i.updateFloat3("vSphericalY",k.y.x,k.y.y,k.y.z),i.updateFloat3("vSphericalZ",k.z.x,k.z.y,k.z.z),i.updateFloat3("vSphericalXX_ZZ",k.xx.x-k.zz.x,k.xx.y-k.zz.y,k.xx.z-k.zz.z),i.updateFloat3("vSphericalYY_ZZ",k.yy.x-k.zz.x,k.yy.y-k.zz.y,k.yy.z-k.zz.z),i.updateFloat3("vSphericalZZ",k.zz.x,k.zz.y,k.zz.z),i.updateFloat3("vSphericalXY",k.xy.x,k.xy.y,k.xy.z),i.updateFloat3("vSphericalYZ",k.yz.x,k.yz.y,k.yz.z),i.updateFloat3("vSphericalZX",k.zx.x,k.zx.y,k.zx.z)}i.updateFloat3("vReflectionMicrosurfaceInfos",g.getSize().width,g.lodGenerationScale,g.lodGenerationOffset)}this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&(i.updateFloat2("vEmissiveInfos",this._emissiveTexture.coordinatesIndex,this._emissiveTexture.level),Yh.a.BindTextureMatrix(this._emissiveTexture,i,"emissive")),this._lightmapTexture&&ai.a.LightmapTextureEnabled&&(i.updateFloat2("vLightmapInfos",this._lightmapTexture.coordinatesIndex,this._lightmapTexture.level),Yh.a.BindTextureMatrix(this._lightmapTexture,i,"lightmap")),ai.a.SpecularTextureEnabled&&(this._metallicTexture?(i.updateFloat3("vReflectivityInfos",this._metallicTexture.coordinatesIndex,this._metallicTexture.level,this._ambientTextureStrength),Yh.a.BindTextureMatrix(this._metallicTexture,i,"reflectivity")):this._reflectivityTexture&&(i.updateFloat3("vReflectivityInfos",this._reflectivityTexture.coordinatesIndex,this._reflectivityTexture.level,1),Yh.a.BindTextureMatrix(this._reflectivityTexture,i,"reflectivity")),this._metallicReflectanceTexture&&(i.updateFloat2("vMetallicReflectanceInfos",this._metallicReflectanceTexture.coordinatesIndex,this._metallicReflectanceTexture.level),Yh.a.BindTextureMatrix(this._metallicReflectanceTexture,i,"metallicReflectance")),this._reflectanceTexture&&e.REFLECTANCE&&(i.updateFloat2("vReflectanceInfos",this._reflectanceTexture.coordinatesIndex,this._reflectanceTexture.level),Yh.a.BindTextureMatrix(this._reflectanceTexture,i,"reflectance")),this._microSurfaceTexture&&(i.updateFloat2("vMicroSurfaceSamplerInfos",this._microSurfaceTexture.coordinatesIndex,this._microSurfaceTexture.level),Yh.a.BindTextureMatrix(this._microSurfaceTexture,i,"microSurfaceSampler"))),this._bumpTexture&&j.getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&(i.updateFloat3("vBumpInfos",this._bumpTexture.coordinatesIndex,this._bumpTexture.level,this._parallaxScaleBias),Yh.a.BindTextureMatrix(this._bumpTexture,i,"bump"),d._mirroredCameraPosition?i.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?1:-1,this._invertNormalMapY?1:-1):i.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?-1:1,this._invertNormalMapY?-1:1))}if(this.pointsCloud&&i.updateFloat("pointSize",this.pointSize),e.METALLICWORKFLOW){h.c.Color3[0].r=void 0===this._metallic||null===this._metallic?1:this._metallic,h.c.Color3[0].g=void 0===this._roughness||null===this._roughness?1:this._roughness,i.updateColor4("vReflectivityColor",h.c.Color3[0],1);m=this.subSurface.indexOfRefraction;k=Math.pow((m-1)/(m+1),2);this._metallicReflectanceColor.scaleToRef(k*this._metallicF0Factor,h.c.Color3[0]);m=this._metallicF0Factor;i.updateColor4("vMetallicReflectanceFactors",h.c.Color3[0],m)}else i.updateColor4("vReflectivityColor",this._reflectivityColor,this._microSurface);i.updateColor3("vEmissiveColor",ai.a.EmissiveTextureEnabled?this._emissiveColor:h.a.BlackReadOnly),i.updateColor3("vReflectionColor",this._reflectionColor),!e.SS_REFRACTION&&this.subSurface.linkRefractionWithTransparency?i.updateColor4("vAlbedoColor",this._albedoColor,1):i.updateColor4("vAlbedoColor",this._albedoColor,this.alpha),this._lightingInfos.x=this._directIntensity,this._lightingInfos.y=this._emissiveIntensity,this._lightingInfos.z=this._environmentIntensity*d.environmentIntensity,this._lightingInfos.w=this._specularIntensity,i.updateVector4("vLightingIntensity",this._lightingInfos),d.ambientColor.multiplyToRef(this._ambientColor,this._globalAmbientColor),i.updateColor3("vAmbientColor",this._globalAmbientColor),i.updateFloat2("vDebugMode",this.debugLimit,this.debugFactor)}d.texturesEnabled&&(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&i.setTexture("albedoSampler",this._albedoTexture),this._ambientTexture&&ai.a.AmbientTextureEnabled&&i.setTexture("ambientSampler",this._ambientTexture),this._opacityTexture&&ai.a.OpacityTextureEnabled&&i.setTexture("opacitySampler",this._opacityTexture),g&&ai.a.ReflectionTextureEnabled&&(e.LODBASEDMICROSFURACE?i.setTexture("reflectionSampler",g):(i.setTexture("reflectionSampler",g._lodTextureMid||g),i.setTexture("reflectionSamplerLow",g._lodTextureLow||g),i.setTexture("reflectionSamplerHigh",g._lodTextureHigh||g)),e.USEIRRADIANCEMAP&&i.setTexture("irradianceSampler",g.irradianceTexture)),e.ENVIRONMENTBRDF&&i.setTexture("environmentBrdfSampler",this._environmentBRDFTexture),this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&i.setTexture("emissiveSampler",this._emissiveTexture),this._lightmapTexture&&ai.a.LightmapTextureEnabled&&i.setTexture("lightmapSampler",this._lightmapTexture),ai.a.SpecularTextureEnabled&&(this._metallicTexture?i.setTexture("reflectivitySampler",this._metallicTexture):this._reflectivityTexture&&i.setTexture("reflectivitySampler",this._reflectivityTexture),this._metallicReflectanceTexture&&i.setTexture("metallicReflectanceSampler",this._metallicReflectanceTexture),this._reflectanceTexture&&e.REFLECTANCE&&i.setTexture("reflectanceSampler",this._reflectanceTexture),this._microSurfaceTexture&&i.setTexture("microSurfaceSampler",this._microSurfaceTexture)),this._bumpTexture&&j.getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&i.setTexture("bumpSampler",this._bumpTexture)),this.getScene().useOrderIndependentTransparency&&this.needAlphaBlendingForMesh(b)&&this.getScene().depthPeelingRenderer.bind(f),this.detailMap.bindForSubMesh(i,d,this.isFrozen),this.subSurface.bindForSubMesh(i,d,j,this.isFrozen,e.LODBASEDMICROSFURACE,this.realTimeFiltering,c),this.clearCoat.bindForSubMesh(i,d,j,this._disableBumpMap,this.isFrozen,this._invertNormalMapX,this._invertNormalMapY,c),this.anisotropy.bindForSubMesh(i,d,this.isFrozen),this.sheen.bindForSubMesh(i,d,this.isFrozen,c),Yh.a.BindClipPlane(this._activeEffect,d),this.bindEyePosition(f)}!a&&this.isFrozen||(d.lightsEnabled&&!this._disableLighting&&Yh.a.BindLights(d,b,this._activeEffect,e,this._maxSimultaneousLights),(d.fogEnabled&&b.applyFog&&d.fogMode!==P.a.FOGMODE_NONE||g||b.receiveShadows)&&this.bindView(f),Yh.a.BindFogParameters(d,b,this._activeEffect,!0),e.NUM_MORPH_INFLUENCERS&&Yh.a.BindMorphTargetParameters(b,this._activeEffect),this._imageProcessingConfiguration.bind(this._activeEffect),Yh.a.BindLogDepth(e,this._activeEffect,d)),this._afterBind(b,this._activeEffect),i.update()}}},b.prototype.getAnimatables=function(){var a=[];return this._albedoTexture&&this._albedoTexture.animations&&this._albedoTexture.animations.length>0&&a.push(this._albedoTexture),this._ambientTexture&&this._ambientTexture.animations&&this._ambientTexture.animations.length>0&&a.push(this._ambientTexture),this._opacityTexture&&this._opacityTexture.animations&&this._opacityTexture.animations.length>0&&a.push(this._opacityTexture),this._reflectionTexture&&this._reflectionTexture.animations&&this._reflectionTexture.animations.length>0&&a.push(this._reflectionTexture),this._emissiveTexture&&this._emissiveTexture.animations&&this._emissiveTexture.animations.length>0&&a.push(this._emissiveTexture),this._metallicTexture&&this._metallicTexture.animations&&this._metallicTexture.animations.length>0?a.push(this._metallicTexture):this._reflectivityTexture&&this._reflectivityTexture.animations&&this._reflectivityTexture.animations.length>0&&a.push(this._reflectivityTexture),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._lightmapTexture&&this._lightmapTexture.animations&&this._lightmapTexture.animations.length>0&&a.push(this._lightmapTexture),this.detailMap.getAnimatables(a),this.subSurface.getAnimatables(a),this.clearCoat.getAnimatables(a),this.sheen.getAnimatables(a),this.anisotropy.getAnimatables(a),a},b.prototype._getReflectionTexture=function(){return this._reflectionTexture?this._reflectionTexture:this.getScene().environmentTexture},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);return this._albedoTexture&&b.push(this._albedoTexture),this._ambientTexture&&b.push(this._ambientTexture),this._opacityTexture&&b.push(this._opacityTexture),this._reflectionTexture&&b.push(this._reflectionTexture),this._emissiveTexture&&b.push(this._emissiveTexture),this._reflectivityTexture&&b.push(this._reflectivityTexture),this._metallicTexture&&b.push(this._metallicTexture),this._metallicReflectanceTexture&&b.push(this._metallicReflectanceTexture),this._reflectanceTexture&&b.push(this._reflectanceTexture),this._microSurfaceTexture&&b.push(this._microSurfaceTexture),this._bumpTexture&&b.push(this._bumpTexture),this._lightmapTexture&&b.push(this._lightmapTexture),this.detailMap.getActiveTextures(b),this.subSurface.getActiveTextures(b),this.clearCoat.getActiveTextures(b),this.sheen.getActiveTextures(b),this.anisotropy.getActiveTextures(b),b},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._albedoTexture===b||this._ambientTexture===b||this._opacityTexture===b||this._reflectionTexture===b||this._reflectivityTexture===b||this._metallicTexture===b||this._metallicReflectanceTexture===b||this._reflectanceTexture===b||this._microSurfaceTexture===b||this._bumpTexture===b||this._lightmapTexture===b||this.detailMap.hasTexture(b)||this.subSurface.hasTexture(b)||this.clearCoat.hasTexture(b)||this.sheen.hasTexture(b)||this.anisotropy.hasTexture(b)},b.prototype.setPrePassRenderer=function(a){if(this.subSurface.isScatteringEnabled){a=this.getScene().enableSubSurfaceForPrePass();return a&&(a.enabled=!0),!0}return!1},b.prototype.dispose=function(b,c){var d;c&&(this._environmentBRDFTexture&&this.getScene().environmentBRDFTexture!==this._environmentBRDFTexture&&this._environmentBRDFTexture.dispose(),null===(d=this._albedoTexture)||void 0===d||d.dispose(),null===(d=this._ambientTexture)||void 0===d||d.dispose(),null===(d=this._opacityTexture)||void 0===d||d.dispose(),null===(d=this._reflectionTexture)||void 0===d||d.dispose(),null===(d=this._emissiveTexture)||void 0===d||d.dispose(),null===(d=this._metallicTexture)||void 0===d||d.dispose(),null===(d=this._reflectivityTexture)||void 0===d||d.dispose(),null===(d=this._bumpTexture)||void 0===d||d.dispose(),null===(d=this._lightmapTexture)||void 0===d||d.dispose(),null===(d=this._metallicReflectanceTexture)||void 0===d||d.dispose(),null===(d=this._reflectanceTexture)||void 0===d||d.dispose(),null===(d=this._microSurfaceTexture)||void 0===d||d.dispose()),this.detailMap.dispose(c),this.subSurface.dispose(c),this.clearCoat.dispose(c),this.sheen.dispose(c),this.anisotropy.dispose(c),this._renderTargets.dispose(),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b,c)},b.PBRMATERIAL_OPAQUE=qi.a.MATERIAL_OPAQUE,b.PBRMATERIAL_ALPHATEST=qi.a.MATERIAL_ALPHATEST,b.PBRMATERIAL_ALPHABLEND=qi.a.MATERIAL_ALPHABLEND,b.PBRMATERIAL_ALPHATESTANDBLEND=qi.a.MATERIAL_ALPHATESTANDBLEND,b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS=0,b.LIGHTFALLOFF_PHYSICAL=0,b.LIGHTFALLOFF_GLTF=1,b.LIGHTFALLOFF_STANDARD=2,Object(l.c)([Object(J.j)()],b.prototype,"_imageProcessingConfiguration",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsMiscDirty")],b.prototype,"debugMode",void 0),Object(l.c)([Object(J.d)()],b.prototype,"useLogarithmicDepth",null),b}($h.a),vi=function(a){function b(c,d){c=a.call(this,c,d)||this;return c.directIntensity=1,c.emissiveIntensity=1,c.environmentIntensity=1,c.specularIntensity=1,c.disableBumpMap=!1,c.ambientTextureStrength=1,c.ambientTextureImpactOnAnalyticalLights=b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,c.metallicF0Factor=1,c.metallicReflectanceColor=h.a.White(),c.useOnlyMetallicFromMetallicReflectanceTexture=!1,c.ambientColor=new h.a(0,0,0),c.albedoColor=new h.a(1,1,1),c.reflectivityColor=new h.a(1,1,1),c.reflectionColor=new h.a(1,1,1),c.emissiveColor=new h.a(0,0,0),c.microSurface=1,c.useLightmapAsShadowmap=!1,c.useAlphaFromAlbedoTexture=!1,c.forceAlphaTest=!1,c.alphaCutOff=.4,c.useSpecularOverAlpha=!0,c.useMicroSurfaceFromReflectivityMapAlpha=!1,c.useRoughnessFromMetallicTextureAlpha=!0,c.useRoughnessFromMetallicTextureGreen=!1,c.useMetallnessFromMetallicTextureBlue=!1,c.useAmbientOcclusionFromMetallicTextureRed=!1,c.useAmbientInGrayScale=!1,c.useAutoMicroSurfaceFromReflectivityMap=!1,c.useRadianceOverAlpha=!0,c.useObjectSpaceNormalMap=!1,c.useParallax=!1,c.useParallaxOcclusion=!1,c.parallaxScaleBias=.05,c.disableLighting=!1,c.forceIrradianceInFragment=!1,c.maxSimultaneousLights=4,c.invertNormalMapX=!1,c.invertNormalMapY=!1,c.twoSidedLighting=!1,c.useAlphaFresnel=!1,c.useLinearAlphaFresnel=!1,c.environmentBRDFTexture=null,c.forceNormalForward=!1,c.enableSpecularAntiAliasing=!1,c.useHorizonOcclusion=!0,c.useRadianceOcclusion=!0,c.unlit=!1,c._environmentBRDFTexture=ii(d),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"refractionTexture",{get:function(){return this.subSurface.refractionTexture},set:function(a){this.subSurface.refractionTexture=a,a?this.subSurface.isRefractionEnabled=!0:this.subSurface.linkRefractionWithTransparency||(this.subSurface.isRefractionEnabled=!1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"indexOfRefraction",{get:function(){return this.subSurface.indexOfRefraction},set:function(a){this.subSurface.indexOfRefraction=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"invertRefractionY",{get:function(){return this.subSurface.invertRefractionY},set:function(a){this.subSurface.invertRefractionY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"linkRefractionWithTransparency",{get:function(){return this.subSurface.linkRefractionWithTransparency},set:function(a){this.subSurface.linkRefractionWithTransparency=a,a&&(this.subSurface.isRefractionEnabled=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"usePhysicalLightFalloff",{get:function(){return this._lightFalloff===ui.LIGHTFALLOFF_PHYSICAL},set:function(a){a!==this.usePhysicalLightFalloff&&(this._markAllSubMeshesAsTexturesDirty(),this._lightFalloff=a?ui.LIGHTFALLOFF_PHYSICAL:ui.LIGHTFALLOFF_STANDARD)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useGLTFLightFalloff",{get:function(){return this._lightFalloff===ui.LIGHTFALLOFF_GLTF},set:function(a){a!==this.useGLTFLightFalloff&&(this._markAllSubMeshesAsTexturesDirty(),this._lightFalloff=a?ui.LIGHTFALLOFF_GLTF:ui.LIGHTFALLOFF_STANDARD)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a),this._markAllSubMeshesAsTexturesDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraToneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraExposure",{get:function(){return this._imageProcessingConfiguration.exposure},set:function(a){this._imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraContrast",{get:function(){return this._imageProcessingConfiguration.contrast},set:function(a){this._imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingTexture",{get:function(){return this._imageProcessingConfiguration.colorGradingTexture},set:function(a){this._imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurves",{get:function(){return this._imageProcessingConfiguration.colorCurves},set:function(a){this._imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PBRMaterial"},b.prototype.clone=function(a){var c=this,d=J.a.Clone(function(){return new b(a,c.getScene())},this);return d.id=a,d.name=a,this.stencil.copyTo(d.stencil),this.clearCoat.copyTo(d.clearCoat),this.anisotropy.copyTo(d.anisotropy),this.brdf.copyTo(d.brdf),this.sheen.copyTo(d.sheen),this.subSurface.copyTo(d.subSurface),d},b.prototype.serialize=function(){var a=J.a.Serialize(this);return a.customType="BABYLON.PBRMaterial",a.stencil=this.stencil.serialize(),a.clearCoat=this.clearCoat.serialize(),a.anisotropy=this.anisotropy.serialize(),a.brdf=this.brdf.serialize(),a.sheen=this.sheen.serialize(),a.subSurface=this.subSurface.serialize(),a},b.Parse=function(a,c,d){var e=J.a.Parse(function(){return new b(a.name,c)},a,c,d);return a.stencil&&e.stencil.parse(a.stencil,c,d),a.clearCoat&&e.clearCoat.parse(a.clearCoat,c,d),a.anisotropy&&e.anisotropy.parse(a.anisotropy,c,d),a.brdf&&e.brdf.parse(a.brdf,c,d),a.sheen&&e.sheen.parse(a.sheen,c,d),a.subSurface&&e.subSurface.parse(a.subSurface,c,d),e},b.PBRMATERIAL_OPAQUE=ui.PBRMATERIAL_OPAQUE,b.PBRMATERIAL_ALPHATEST=ui.PBRMATERIAL_ALPHATEST,b.PBRMATERIAL_ALPHABLEND=ui.PBRMATERIAL_ALPHABLEND,b.PBRMATERIAL_ALPHATESTANDBLEND=ui.PBRMATERIAL_ALPHATESTANDBLEND,b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS=ui.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"directIntensity",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveIntensity",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"environmentIntensity",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"specularIntensity",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"disableBumpMap",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"albedoTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTextureStrength",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTextureImpactOnAnalyticalLights",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"opacityTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectivityTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallic",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"roughness",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicF0Factor",void 0),Object(l.c)([Object(J.f)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicReflectanceColor",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useOnlyMetallicFromMetallicReflectanceTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicReflectanceTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectanceTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"microSurfaceTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"bumpTexture",void 0),Object(l.c)([Object(J.n)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty",null)],b.prototype,"lightmapTexture",void 0),Object(l.c)([Object(J.f)("ambient"),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientColor",void 0),Object(l.c)([Object(J.f)("albedo"),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"albedoColor",void 0),Object(l.c)([Object(J.f)("reflectivity"),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectivityColor",void 0),Object(l.c)([Object(J.f)("reflection"),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionColor",void 0),Object(l.c)([Object(J.f)("emissive"),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveColor",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"microSurface",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLightmapAsShadowmap",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"useAlphaFromAlbedoTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"forceAlphaTest",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"alphaCutOff",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useSpecularOverAlpha",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useMicroSurfaceFromReflectivityMapAlpha",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRoughnessFromMetallicTextureAlpha",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRoughnessFromMetallicTextureGreen",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useMetallnessFromMetallicTextureBlue",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAmbientOcclusionFromMetallicTextureRed",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAmbientInGrayScale",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAutoMicroSurfaceFromReflectivityMap",void 0),Object(l.c)([Object(J.d)()],b.prototype,"usePhysicalLightFalloff",null),Object(l.c)([Object(J.d)()],b.prototype,"useGLTFLightFalloff",null),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRadianceOverAlpha",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useObjectSpaceNormalMap",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallax",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallaxOcclusion",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"parallaxScaleBias",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"disableLighting",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"forceIrradianceInFragment",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapX",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapY",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"twoSidedLighting",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAlphaFresnel",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLinearAlphaFresnel",void 0),Object(l.c)([Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"environmentBRDFTexture",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"forceNormalForward",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"enableSpecularAntiAliasing",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useHorizonOcclusion",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRadianceOcclusion",void 0),Object(l.c)([Object(J.d)(),Object(J.b)("_markAllSubMeshesAsMiscDirty")],b.prototype,"unlit",void 0),b}(ui);Object(i.b)("BABYLON.PBRMaterial",vi);function wi(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var xi=wi("DXT1"),yi=wi("DXT3"),zi=wi("DXT5"),Ai=wi("DX10"),Bi=function(){function a(){}return a.GetDDSInfo=function(a){var b=new Int32Array(a.buffer,a.byteOffset,31);a=new Int32Array(a.buffer,a.byteOffset,35);var c=1;131072&b[2]&&(c=Math.max(1,b[7]));var d=b[21];a=d===Ai?a[32]:0;var e=r.a.TEXTURETYPE_UNSIGNED_INT;switch(d){case 113:e=r.a.TEXTURETYPE_HALF_FLOAT;break;case 116:e=r.a.TEXTURETYPE_FLOAT;break;case Ai:if(10===a){e=r.a.TEXTURETYPE_HALF_FLOAT;break}if(2===a){e=r.a.TEXTURETYPE_FLOAT;break}}return{width:b[4],height:b[3],mipmapCount:c,isFourCC:4==(4&b[20]),isRGB:64==(64&b[20]),isLuminance:131072==(131072&b[20]),isCube:512==(512&b[28]),isCompressed:d===xi||d===yi||d===zi,dxgiFormat:a,textureType:e}},a._GetHalfFloatAsFloatRGBAArrayBuffer=function(b,c,d,e,f,g){for(var e=new Float32Array(e),f=new Uint16Array(f,d),d=0,h=0;h>8)},a._GetRGBArrayBuffer=function(a,b,c,d,e,f,g,h){for(var d=new Uint8Array(d),e=new Uint8Array(e,c),c=0,i=0;i0?e.sphericalPolynomial=Se.ConvertCubeMapToSphericalPolynomial({size:J[4],right:k[0],left:k[1],up:k[2],down:k[3],front:k[4],back:k[5],format:r.a.TEXTUREFORMAT_RGBA,type:r.a.TEXTURETYPE_FLOAT,gammaSpace:!1}):e.sphericalPolynomial=void 0}else q.a.Error("Compressed textures are not supported on this platform.");else q.a.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");else q.a.Error("Invalid magic number in DDS header")},a.StoreLODInAlphaChannel=!1,a}();pb.a.prototype.createPrefilteredCubeTexture=function(a,b,c,d,e,f,g,h,i){var j=this;void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=!0);return this.createCubeTexture(a,b,null,!1,function(a){if(a){var f=a.texture;if(i?a.info.sphericalPolynomial&&(f._sphericalPolynomial=a.info.sphericalPolynomial):f._sphericalPolynomial=new He,f._source=ob.b.CubePrefiltered,j.getCaps().textureLOD)e&&e(f);else{var g=j._gl,h=a.width;if(h){for(var k=[],p=0;p<3;p++){var s=1-p/2,t=d,u=I.a.Log2(h)*c+d;t=t+(u-t)*s;s=Math.round(Math.min(Math.max(t,0),u));t=new ob.a(j,ob.b.Temp);if(t.type=f.type,t.format=f.format,t.width=Math.pow(2,Math.max(I.a.Log2(h)-s,0)),t.height=t.width,t.isCube=!0,t._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,t._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE,j._bindTextureDirectly(g.TEXTURE_CUBE_MAP,t,!0),t.samplingMode=r.a.TEXTURE_LINEAR_LINEAR,g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_MAG_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_MIN_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_WRAP_S,g.CLAMP_TO_EDGE),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_WRAP_T,g.CLAMP_TO_EDGE),a.isDDS){u=a.info;var w=a.data;j._unpackFlipY(u.isCompressed),Bi.UploadDDSLevels(j,t,w,u,!0,6,s)}else q.a.Warn("DDS is the only prefiltered cube map supported so far.");j._bindTextureDirectly(g.TEXTURE_CUBE_MAP,null);w=new Ie.a(b);w.isCube=!0,w._texture=t,t.isReady=!0,k.push(w)}f._lodTextureHigh=k[2],f._lodTextureMid=k[1],f._lodTextureLow=k[0],e&&e(f)}}}else e&&e(null)},f,g,h,i,c,d)};var Ci=function(){function a(){this.supportCascades=!0}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".dds")},a.prototype.loadCubeData=function(a,b,c,d,e){var f;e=b.getEngine();var g=!1;if(Array.isArray(a))for(var h=0;h1)&&b.generateMipMaps,e._unpackFlipY(f.isCompressed),Bi.UploadDDSLevels(e,b,i,f,g,6,-1,h),f.isFourCC||1!==f.mipmapCount||e.generateMipMapsForCubemap(b)}else{i=a;f=Bi.GetDDSInfo(i),b.width=f.width,b.height=f.height,c&&(f.sphericalPolynomial=new He),g=(f.isRGB||f.isLuminance||f.mipmapCount>1)&&b.generateMipMaps,e._unpackFlipY(f.isCompressed),Bi.UploadDDSLevels(e,b,i,f,g,6),f.isFourCC||1!==f.mipmapCount||e.generateMipMapsForCubemap(b,!1)}e._setCubeMapTextureParams(b,g),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d({isDDS:!0,width:b.width,info:f,data:a,texture:b})},a.prototype.loadData=function(a,b,c){var d=Bi.GetDDSInfo(a),e=(d.isRGB||d.isLuminance||d.mipmapCount>1)&&b.generateMipMaps&&d.width>>d.mipmapCount-1==1;c(d.width,d.height,e,d.isFourCC,function(){Bi.UploadDDSLevels(b.getEngine(),b,a,d,e,1)})},a}();T.a._TextureLoaders.push(new Ci);var Di=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".env")},a.prototype.loadCubeData=function(a,b,c,d,e){if(!Array.isArray(a)){c=Ue(a);if(c){b.width=c.width,b.height=c.width;try{bf(b,c),Ze(b,a,c).then(function(){b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()},function(a){null==e||e("Can not upload environment levels",a)})}catch(a){null==e||e("Can not upload environment file",a)}}else e&&e("Can not parse the environment file",null)}},a.prototype.loadData=function(a,b,c){throw".env not supported in 2d."},a}();T.a._TextureLoaders.push(new Di);var Ei=function(){function a(b,c,d,e){if(this.data=b,this.isInvalid=!1,!a.IsValid(b))return this.isInvalid=!0,void q.a.Error("texture missing KTX identifier");d=Uint32Array.BYTES_PER_ELEMENT;e=new DataView(this.data.buffer,this.data.byteOffset+12,13*d);b=67305985===e.getUint32(0,!0);this.glType=e.getUint32(1*d,b),this.glTypeSize=e.getUint32(2*d,b),this.glFormat=e.getUint32(3*d,b),this.glInternalFormat=e.getUint32(4*d,b),this.glBaseInternalFormat=e.getUint32(5*d,b),this.pixelWidth=e.getUint32(6*d,b),this.pixelHeight=e.getUint32(7*d,b),this.pixelDepth=e.getUint32(8*d,b),this.numberOfArrayElements=e.getUint32(9*d,b),this.numberOfFaces=e.getUint32(10*d,b),this.numberOfMipmapLevels=e.getUint32(11*d,b),this.bytesOfKeyValueData=e.getUint32(12*d,b),0===this.glType?(this.numberOfMipmapLevels=Math.max(1,this.numberOfMipmapLevels),0!==this.pixelHeight&&0===this.pixelDepth?0===this.numberOfArrayElements?this.numberOfFaces===c?this.loadType=a.COMPRESSED_2D:q.a.Error("number of faces expected"+c+", but found "+this.numberOfFaces):q.a.Error("texture arrays not currently supported"):q.a.Error("only 2D textures currently supported")):q.a.Error("only compressed formats currently supported")}return a.prototype.uploadLevels=function(b,c){switch(this.loadType){case a.COMPRESSED_2D:this._upload2DCompressedLevels(b,c);break;case a.TEX_2D:case a.COMPRESSED_3D:case a.TEX_3D:}},a.prototype._upload2DCompressedLevels=function(b,c){for(var d=a.HEADER_LEN+this.bytesOfKeyValueData,e=this.pixelWidth,f=this.pixelHeight,c=c?this.numberOfMipmapLevels:1,g=0;g=12){a=new Uint8Array(a.buffer,a.byteOffset,12);if(171===a[0]&&75===a[1]&&84===a[2]&&88===a[3]&&32===a[4]&&49===a[5]&&49===a[6]&&187===a[7]&&13===a[8]&&10===a[9]&&26===a[10]&&10===a[11])return!0}return!1},a.HEADER_LEN=64,a.COMPRESSED_2D=0,a.COMPRESSED_3D=1,a.TEX_2D=2,a.TEX_3D=3,a}(),Fi=function(){function a(a){this._pendingActions=new Array,this._workerInfos=a.map(function(a){return{worker:a,active:!1}})}return a.prototype.dispose=function(){for(var a=0,b=this._workerInfos;a1,a.errors)throw new Error("KTX2 container - could not transcode the data. "+a.errors);for(c=0;c=12){a=new Uint8Array(a.buffer,a.byteOffset,12);if(171===a[0]&&75===a[1]&&84===a[2]&&88===a[3]&&32===a[4]&&50===a[5]&&48===a[6]&&187===a[7]&&13===a[8]&&10===a[9]&&26===a[10]&&10===a[11])return!0}return!1},a.URLConfig={jsDecoderModule:"https://preview.babylonjs.com/babylon.ktx2Decoder.js",wasmUASTCToASTC:null,wasmUASTCToBC7:null,wasmUASTCToRGBA_UNORM:null,wasmUASTCToRGBA_SRGB:null,jsMSCTranscoder:null,wasmMSCTranscoder:null,wasmZSTDDecoder:null},a.DefaultNumWorkers=a.GetDefaultNumWorkers(),a}();function Hi(){var a;onmessage=function(b){if(b.data)switch(b.data.action){case"init":var c=b.data.urls;importScripts(c.jsDecoderModule),null!==c.wasmUASTCToASTC&&(KTX2DECODER.LiteTranscoder_UASTC_ASTC.WasmModuleURL=c.wasmUASTCToASTC),null!==c.wasmUASTCToBC7&&(KTX2DECODER.LiteTranscoder_UASTC_BC7.WasmModuleURL=c.wasmUASTCToBC7),null!==c.wasmUASTCToRGBA_UNORM&&(KTX2DECODER.LiteTranscoder_UASTC_RGBA_UNORM.WasmModuleURL=c.wasmUASTCToRGBA_UNORM),null!==c.wasmUASTCToRGBA_SRGB&&(KTX2DECODER.LiteTranscoder_UASTC_RGBA_SRGB.WasmModuleURL=c.wasmUASTCToRGBA_SRGB),null!==c.jsMSCTranscoder&&(KTX2DECODER.MSCTranscoder.JSModuleURL=c.jsMSCTranscoder),null!==c.wasmMSCTranscoder&&(KTX2DECODER.MSCTranscoder.WasmModuleURL=c.wasmMSCTranscoder),null!==c.wasmZSTDDecoder&&(KTX2DECODER.ZSTDDecoder.WasmModuleURL=c.wasmZSTDDecoder),a=new KTX2DECODER.KTX2Decoder,postMessage({action:"init"});break;case"decode":a.decode(b.data.data,b.data.caps,b.data.options).then(function(a){for(var b=[],c=0;c1&&b.generateMipMaps;c._unpackFlipY(!0),e.uploadLevels(b,b.generateMipMaps),b.width=e.pixelWidth,b.height=e.pixelHeight,c._setCubeMapTextureParams(b,a),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()}},a.prototype.loadData=function(a,b,c,d){if(Ei.IsValid(a)){b._invertVScale=!b.invertY;var e=new Ei(a,1);c(e.pixelWidth,e.pixelHeight,b.generateMipMaps,!0,function(){e.uploadLevels(b,b.generateMipMaps)},e.isInvalid)}else Gi.IsValid(a)?new Gi(b.getEngine()).uploadAsync(a,b,d).then(function(){c(b.width,b.height,b.generateMipMaps,!0,function(){},!1)},function(a){q.a.Warn("Failed to load KTX2 texture data: "+a.message),c(0,0,!1,!1,function(){},!0)}):(q.a.Error("texture missing KTX identifier"),c(0,0,!1,!1,function(){},!0))},a}();T.a._TextureLoaders.unshift(new Ii);var Ji=function(a){function b(b,c,d){var e=a.call(this,b,g.e.Zero(),c)||this;return e._xrSessionManager=d,e._firstFrame=!1,e._referenceQuaternion=g.b.Identity(),e._referencedPosition=new g.e,e._trackingState=sd.NOT_TRACKING,e.onBeforeCameraTeleport=new f.c,e.onAfterCameraTeleport=new f.c,e.onTrackingStateChanged=new f.c,e.compensateOnFirstFrame=!0,e._rotate180=new g.b(0,1,0,0),e.minZ=.1,e.rotationQuaternion=new g.b,e.cameraRigMode=cb.a.RIG_MODE_CUSTOM,e.updateUpVectorFromRotation=!0,e._updateNumberOfRigCameras(1),e.freezeProjectionMatrix(),e._xrSessionManager.onXRSessionInit.add(function(){e._referencedPosition.copyFromFloats(0,0,0),e._referenceQuaternion.copyFromFloats(0,0,0,1),e._firstFrame=e.compensateOnFirstFrame}),e._xrSessionManager.onXRFrameObservable.add(function(a){e._firstFrame&&e._updateFromXRSession(),e._updateReferenceSpace(),e._updateFromXRSession()},void 0,!0),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"trackingState",{get:function(){return this._trackingState},enumerable:!1,configurable:!0}),b.prototype._setTrackingState=function(a){this._trackingState!==a&&(this._trackingState=a,this.onTrackingStateChanged.notifyObservers(a))},Object.defineProperty(b.prototype,"realWorldHeight",{get:function(){var a=this._xrSessionManager.currentFrame&&this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.baseReferenceSpace);return a&&a.transform?a.transform.position.y:0},enumerable:!1,configurable:!0}),b.prototype._updateForDualEyeDebugging=function(){this._updateNumberOfRigCameras(2),this.rigCameras[0].viewport=new Oc.a(0,0,.5,1),this.rigCameras[0].outputRenderTarget=null,this.rigCameras[1].viewport=new Oc.a(.5,0,.5,1),this.rigCameras[1].outputRenderTarget=null},b.prototype.setTransformationFromNonVRCamera=function(a,b){(void 0===a&&(a=this.getScene().activeCamera),void 0===b&&(b=!0),a&&a!==this)&&(a.computeWorldMatrix().decompose(void 0,this.rotationQuaternion,this.position),this.position.y=0,g.b.FromEulerAnglesToRef(0,this.rotationQuaternion.toEulerAngles().y,0,this.rotationQuaternion),this._firstFrame=!0,b&&this._xrSessionManager.resetReferenceSpace())},b.prototype.getClassName=function(){return"WebXRCamera"},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._lastXRViewerPose=void 0},b.prototype._updateFromXRSession=function(){var a=this,b=this._xrSessionManager.currentFrame&&this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.referenceSpace);if(this._lastXRViewerPose=b||void 0,b){var c=b.emulatedPosition?sd.TRACKING_LOST:sd.TRACKING;if(this._setTrackingState(c),b.transform){c=b.transform.orientation;if(void 0===b.transform.orientation.x)return;var d=b.transform.position;this._referencedPosition.set(d.x,d.y,d.z),this._referenceQuaternion.set(c.x,c.y,c.z,c.w),this._scene.useRightHandedSystem||(this._referencedPosition.z*=-1,this._referenceQuaternion.z*=-1,this._referenceQuaternion.w*=-1),this._firstFrame?(this._firstFrame=!1,this.position.y+=this._referencedPosition.y,this._referenceQuaternion.copyFromFloats(0,0,0,1)):(this.rotationQuaternion.copyFrom(this._referenceQuaternion),this.position.copyFrom(this._referencedPosition))}this.rigCameras.length!==b.views.length&&this._updateNumberOfRigCameras(b.views.length),b.views.forEach(function(b,c){var d=a.rigCameras[c];d.isLeftCamera||d.isRightCamera||("right"===b.eye?d._isRightCamera=!0:"left"===b.eye&&(d._isLeftCamera=!0));var e=b.transform.position,f=b.transform.orientation;if(d.parent=a.parent,d.position.set(e.x,e.y,e.z),d.rotationQuaternion.set(f.x,f.y,f.z,f.w),a._scene.useRightHandedSystem?d.rotationQuaternion.multiplyInPlace(a._rotate180):(d.position.z*=-1,d.rotationQuaternion.z*=-1,d.rotationQuaternion.w*=-1),g.a.FromFloat32ArrayToRefScaled(b.projectionMatrix,0,1,d._projectionMatrix),a._scene.useRightHandedSystem||d._projectionMatrix.toggleProjectionMatrixHandInPlace(),0===c&&a._projectionMatrix.copyFrom(d._projectionMatrix),a._xrSessionManager.session.renderState.baseLayer){e=a._xrSessionManager.session.renderState.baseLayer.getViewport(b);f=a._xrSessionManager.session.renderState.baseLayer.framebufferWidth;c=a._xrSessionManager.session.renderState.baseLayer.framebufferHeight;d.viewport.width=e.width/f,d.viewport.height=e.height/c,d.viewport.x=e.x/f,d.viewport.y=e.y/c}d.outputRenderTarget=a._xrSessionManager.getRenderTargetTextureForEye(b.eye)})}else this._setTrackingState(sd.NOT_TRACKING)},b.prototype._updateNumberOfRigCameras=function(a){for(void 0===a&&(a=1);this.rigCameras.lengtha;){b=this.rigCameras.pop();b&&b.dispose()}},b.prototype._updateReferenceSpace=function(){if(!this.position.equals(this._referencedPosition)||!this.rotationQuaternion.equals(this._referenceQuaternion)){var a=g.c.Matrix[0],c=g.c.Matrix[1],d=g.c.Matrix[2];g.a.ComposeToRef(b._ScaleReadOnly,this._referenceQuaternion,this._referencedPosition,a),g.a.ComposeToRef(b._ScaleReadOnly,this.rotationQuaternion,this.position,c),a.invert().multiplyToRef(c,d),d.invert(),this._scene.useRightHandedSystem||d.toggleModelMatrixHandInPlace(),d.decompose(void 0,this._referenceQuaternion,this._referencedPosition);a=new XRRigidTransform({x:this._referencedPosition.x,y:this._referencedPosition.y,z:this._referencedPosition.z},{x:this._referenceQuaternion.x,y:this._referenceQuaternion.y,z:this._referenceQuaternion.z,w:this._referenceQuaternion.w});this._xrSessionManager.referenceSpace=this._xrSessionManager.referenceSpace.getOffsetReferenceSpace(a)}},b._ScaleReadOnly=g.e.One(),b}(dc),Ki=function(){function a(a){var b=this;this.scene=a,this._nonVRCamera=null,this._attachedToElement=!1,this._spectatorCamera=null,this._originalSceneAutoClear=!0,this._supported=!1,this._spectatorMode=!1,this.onInitialXRPoseSetObservable=new f.c,this.onStateChangedObservable=new f.c,this.state=rd.NOT_IN_XR,this.sessionManager=new wd(a),this.camera=new Ji("webxr",a,this.sessionManager),this.featuresManager=new kb(this.sessionManager),a.onDisposeObservable.add(function(){b.exitXRAsync()})}return a.CreateAsync=function(b){var c=new a(b);return c.sessionManager.initializeAsync().then(function(){return c._supported=!0,c})["catch"](function(a){throw c._setState(rd.NOT_IN_XR),c.dispose(),a})},a.prototype.dispose=function(){var a;this.camera.dispose(),this.onStateChangedObservable.clear(),this.onInitialXRPoseSetObservable.clear(),this.sessionManager.dispose(),null===(a=this._spectatorCamera)||void 0===a||a.dispose(),this._nonVRCamera&&(this.scene.activeCamera=this._nonVRCamera)},a.prototype.enterXRAsync=function(a,b,c,d){var e,f;return void 0===c&&(c=this.sessionManager.getWebXRRenderTarget()),void 0===d&&(d={}),Object(l.b)(this,void 0,void 0,function(){var g,h=this;return Object(l.e)(this,function(i){switch(i.label){case 0:if(!this._supported)throw"WebXR not supported in this browser or environment";return this._setState(rd.ENTERING_XR),"viewer"!==b&&"local"!==b&&(d.optionalFeatures=d.optionalFeatures||[],d.optionalFeatures.push(b)),[4,this.featuresManager._extendXRSessionInitObject(d)];case 1:d=i.sent(),"immersive-ar"===a&&"unbounded"!==b&&q.a.Warn("We recommend using "unbounded" reference space type when using "immersive-ar" session mode"),i.label=2;case 2:return i.trys.push([2,7,,8]),[4,this.sessionManager.initializeSessionAsync(a,d)];case 3:return i.sent(),[4,this.sessionManager.setReferenceSpaceTypeAsync(b)];case 4:return i.sent(),[4,c.initializeXRLayerAsync(this.sessionManager.session)];case 5:return i.sent(),[4,this.sessionManager.updateRenderStateAsync({depthFar:this.camera.maxZ,depthNear:this.camera.minZ,baseLayer:c.xrLayer})];case 6:return i.sent(),this.sessionManager.runXRRenderLoop(),this._originalSceneAutoClear=this.scene.autoClear,this._nonVRCamera=this.scene.activeCamera,this._attachedToElement=!!(null===(e=this._nonVRCamera)||void 0===e?void 0:e.inputs.attachedToElement),null===(f=this._nonVRCamera)||void 0===f||f.detachControl(),this.scene.activeCamera=this.camera,"immersive-ar"!==a?this._nonXRToXRCamera():(this.scene.autoClear=!1,this.camera.compensateOnFirstFrame=!1),this.sessionManager.onXRSessionEnded.addOnce(function(){h.camera.rigCameras.forEach(function(a){a.outputRenderTarget=null}),h.scene.autoClear=h._originalSceneAutoClear,h.scene.activeCamera=h._nonVRCamera,h._attachedToElement&&h._nonVRCamera&&h._nonVRCamera.attachControl(!!h._nonVRCamera.inputs.noPreventDefault),"immersive-ar"!==a&&h.camera.compensateOnFirstFrame&&(h._nonVRCamera.setPosition?h._nonVRCamera.setPosition(h.camera.position):h._nonVRCamera.position.copyFrom(h.camera.position)),h._setState(rd.NOT_IN_XR)}),this.sessionManager.onXRFrameObservable.addOnce(function(){h._setState(rd.IN_XR)}),[2,this.sessionManager];case 7:throw g=i.sent(),!1,!1,this._setState(rd.NOT_IN_XR),g;case 8:return[2]}})})},a.prototype.exitXRAsync=function(){return this.state!==rd.IN_XR?Promise.resolve():(this._setState(rd.EXITING_XR),this.sessionManager.exitXRAsync())},a.prototype.enableSpectatorMode=function(){var a=this;if(!this._spectatorMode){var b=function(){a._spectatorCamera&&(a._spectatorCamera.position.copyFrom(a.camera.rigCameras[0].globalPosition),a._spectatorCamera.rotationQuaternion.copyFrom(a.camera.rigCameras[0].absoluteRotation))},c=function(){a.state===rd.IN_XR?(a._spectatorCamera=new yc("webxr-spectator",g.e.Zero(),a.scene),a._spectatorCamera.rotationQuaternion=new g.b,a.scene.activeCameras=[a.camera,a._spectatorCamera],a.sessionManager.onXRFrameObservable.add(b),a.scene.onAfterRenderCameraObservable.add(function(b){b===a.camera&&(a.scene.getEngine().framebufferDimensionsObject=null)})):a.state===rd.EXITING_XR&&(a.sessionManager.onXRFrameObservable.removeCallback(b),a.scene.activeCameras=null)};this._spectatorMode=!0,this.onStateChangedObservable.add(c),c()}},a.prototype._nonXRToXRCamera=function(){this.camera.setTransformationFromNonVRCamera(this._nonVRCamera),this.onInitialXRPoseSetObservable.notifyObservers(this.camera)},a.prototype._setState=function(a){this.state!==a&&(this.state=a,this.onStateChangedObservable.notifyObservers(this.state))},a}(),Li=function(){function a(a,b,c,d){void 0===c&&(c=-1),void 0===d&&(d=[]),this.id=a,this.type=b,this._buttonIndex=c,this._axesIndices=d,this._axes={x:0,y:0},this._changes={},this._currentValue=0,this._hasChanges=!1,this._pressed=!1,this._touched=!1,this.onAxisValueChangedObservable=new f.c,this.onButtonStateChangedObservable=new f.c}return Object.defineProperty(a.prototype,"axes",{get:function(){return this._axes},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"changes",{get:function(){return this._changes},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasChanges",{get:function(){return this._hasChanges},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"pressed",{get:function(){return this._pressed},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"touched",{get:function(){return this._touched},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"value",{get:function(){return this._currentValue},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.onAxisValueChangedObservable.clear(),this.onButtonStateChangedObservable.clear()},a.prototype.isAxes=function(){return 0!==this._axesIndices.length},a.prototype.isButton=function(){return-1!==this._buttonIndex},a.prototype.update=function(a){var b=!1,c=!1;if(this._hasChanges=!1,this._changes={},this.isButton()){var d=a.buttons[this._buttonIndex];if(!d)return;this._currentValue!==d.value&&(this.changes.value={current:d.value,previous:this._currentValue},b=!0,this._currentValue=d.value),this._touched!==d.touched&&(this.changes.touched={current:d.touched,previous:this._touched},b=!0,this._touched=d.touched),this._pressed!==d.pressed&&(this.changes.pressed={current:d.pressed,previous:this._pressed},b=!0,this._pressed=d.pressed)}this.isAxes()&&(this._axes.x!==a.axes[this._axesIndices[0]]&&(this.changes.axes={current:{x:a.axes[this._axesIndices[0]],y:this._axes.y},previous:{x:this._axes.x,y:this._axes.y}},this._axes.x=a.axes[this._axesIndices[0]],c=!0),this._axes.y!==a.axes[this._axesIndices[1]]&&(this.changes.axes?this.changes.axes.current.y=a.axes[this._axesIndices[1]]:this.changes.axes={current:{x:this._axes.x,y:a.axes[this._axesIndices[1]]},previous:{x:this._axes.x,y:this._axes.y}},this._axes.y=a.axes[this._axesIndices[1]],c=!0)),b&&(this._hasChanges=!0,this.onButtonStateChangedObservable.notifyObservers(this)),c&&(this._hasChanges=!0,this.onAxisValueChangedObservable.notifyObservers(this._axes))},a.BUTTON_TYPE="button",a.SQUEEZE_TYPE="squeeze",a.THUMBSTICK_TYPE="thumbstick",a.TOUCHPAD_TYPE="touchpad",a.TRIGGER_TYPE="trigger",a}(),Mi=function(){function a(a,b,c,d,e,g){var h=this;void 0===e&&(e=!1),this.scene=a,this.layout=b,this.gamepadObject=c,this.handedness=d,this._doNotLoadControllerMesh=e,this._controllerCache=g,this._initComponent=function(a){if(a){var b=h.layout.components[a],c=b.type,d=b.gamepadIndices.button,e=[];void 0!==b.gamepadIndices.xAxis&&void 0!==b.gamepadIndices.yAxis&&e.push(b.gamepadIndices.xAxis,b.gamepadIndices.yAxis),h.components[a]=new Li(a,c,d,e)}},this._modelReady=!1,this.components={},this.disableAnimation=!1,this.onModelLoadedObservable=new f.c,b.components&&Object.keys(b.components).forEach(this._initComponent)}return a.prototype.dispose=function(){var a=this;this.getComponentIds().forEach(function(b){return a.getComponent(b).dispose()}),this.rootMesh&&(this.rootMesh.getChildren(void 0,!0).forEach(function(a){a.setEnabled(!1)}),this.rootMesh.dispose(!!this._controllerCache,!this._controllerCache))},a.prototype.getAllComponentsOfType=function(a){var b=this;return this.getComponentIds().map(function(a){return b.components[a]}).filter(function(b){return b.type===a})},a.prototype.getComponent=function(a){return this.components[a]},a.prototype.getComponentIds=function(){return Object.keys(this.components)},a.prototype.getComponentOfType=function(a){return this.getAllComponentsOfType(a)[0]||null},a.prototype.getMainComponent=function(){return this.getComponent(this.layout.selectComponentId)},a.prototype.loadModel=function(){return Object(l.b)(this,void 0,void 0,function(){var a,b,c=this;return Object(l.e)(this,function(d){return a=!this._getModelLoadingConstraints(),b=this._getGenericFilenameAndPath(),a?q.a.Warn("Falling back to generic models"):b=this._getFilenameAndPath(),[2,new Promise(function(d,e){var f=function(b){a?c._getGenericParentMesh(b):c._setRootMesh(b),c._processLoadedModel(b),c._modelReady=!0,c.onModelLoadedObservable.notifyObservers(c),d(!0)};if(c._controllerCache){var g=c._controllerCache.filter(function(a){return a.filename===b.filename&&a.path===b.path});if(g[0])return g[0].meshes.forEach(function(a){return a.setEnabled(!0)}),void f(g[0].meshes)}hh.ImportMesh("",b.path,b.filename,c.scene,function(a){c._controllerCache&&c._controllerCache.push(Object(l.a)(Object(l.a)({},b),{meshes:a})),f(a)},null,function(a,d){q.a.Log(d),q.a.Warn("Failed to retrieve controller model of type "+c.profileId+" from the remote server: "+b.path+b.filename),e(d)})})]})})},a.prototype.updateFromXRFrame=function(a){var b=this;this.getComponentIds().forEach(function(a){return b.getComponent(a).update(b.gamepadObject)}),this.updateModel(a)},Object.defineProperty(a.prototype,"handness",{get:function(){return this.handedness},enumerable:!1,configurable:!0}),a.prototype.pulse=function(a,b,c){return void 0===c&&(c=0),this.gamepadObject.hapticActuators&&this.gamepadObject.hapticActuators[c]?this.gamepadObject.hapticActuators[c].pulse(a,b):Promise.resolve(!1)},a.prototype._getChildByName=function(a,b){return a.getChildren(function(a){return a.name===b},!1)[0]},a.prototype._getImmediateChildByName=function(a,b){return a.getChildren(function(a){return a.name==b},!0)[0]},a.prototype._lerpTransform=function(a,b,c){if(a.minMesh&&a.maxMesh&&a.valueMesh&&a.minMesh.rotationQuaternion&&a.maxMesh.rotationQuaternion&&a.valueMesh.rotationQuaternion){c=c?.5*b+.5:b;g.b.SlerpToRef(a.minMesh.rotationQuaternion,a.maxMesh.rotationQuaternion,c,a.valueMesh.rotationQuaternion),g.e.LerpToRef(a.minMesh.position,a.maxMesh.position,c,a.valueMesh.position)}},a.prototype.updateModel=function(a){this._modelReady&&this._updateModel(a)},a.prototype._getGenericFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},a.prototype._getGenericParentMesh=function(a){var b=this;this.rootMesh=new S.a(this.profileId+" "+this.handedness,this.scene),a.forEach(function(a){a.parent||(a.isPickable=!1,a.setParent(b.rootMesh))}),this.rootMesh.rotationQuaternion=g.b.FromEulerAngles(0,Math.PI,0)},a}(),Ni=function(a){function b(c,d,e){c=a.call(this,c,Oi[e],d,e)||this;return c.profileId=b.ProfileId,c}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},b.prototype._getModelLoadingConstraints=function(){return!0},b.prototype._processLoadedModel=function(a){},b.prototype._setRootMesh=function(a){var b=this;this.rootMesh=new S.a(this.profileId+" "+this.handedness,this.scene),a.forEach(function(a){a.isPickable=!1,a.parent||a.setParent(b.rootMesh)}),this.rootMesh.rotationQuaternion=g.b.FromEulerAngles(0,Math.PI,0)},b.prototype._updateModel=function(){},b.ProfileId="generic-trigger",b}(Mi),Oi={left:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-left",assetPath:"left.glb"},right:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-right",assetPath:"right.glb"},none:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-none",assetPath:"none.glb"}},Pi=function(a){function b(b,c,d,e,f){b=a.call(this,b,d.layouts[c.handedness||"none"],c.gamepad,c.handedness,void 0,f)||this;return b._repositoryUrl=e,b.controllerCache=f,b._buttonMeshMapping={},b._touchDots={},b.profileId=d.profileId,b}return Object(l.d)(b,a),b.prototype.dispose=function(){var b=this;a.prototype.dispose.call(this),this.controllerCache||Object.keys(this._touchDots).forEach(function(a){b._touchDots[a].dispose()})},b.prototype._getFilenameAndPath=function(){return{filename:this.layout.assetPath,path:this._repositoryUrl+"/profiles/"+this.profileId+"/"}},b.prototype._getModelLoadingConstraints=function(){var a=hh.IsPluginForExtensionAvailable(".glb");return a||q.a.Warn("glTF / glb loader was not registered, using generic controller instead"),a},b.prototype._processLoadedModel=function(a){var b=this;this.getComponentIds().forEach(function(a){var c=b.layout.components[a];b._buttonMeshMapping[a]={mainMesh:b._getChildByName(b.rootMesh,c.rootNodeName),states:{}},Object.keys(c.visualResponses).forEach(function(d){var e=c.visualResponses[d];if("transform"===e.valueNodeProperty)b._buttonMeshMapping[a].states[d]={valueMesh:b._getChildByName(b.rootMesh,e.valueNodeName),minMesh:b._getChildByName(b.rootMesh,e.minNodeName),maxMesh:b._getChildByName(b.rootMesh,e.maxNodeName)};else{e=c.type===Li.TOUCHPAD_TYPE&&c.touchPointNodeName?c.touchPointNodeName:e.valueNodeName;if(b._buttonMeshMapping[a].states[d]={valueMesh:b._getChildByName(b.rootMesh,e)},c.type===Li.TOUCHPAD_TYPE&&!b._touchDots[d]){e=Object(vh.a)(d+"dot",{diameter:.0015,segments:8},b.scene);e.material=new pd.a(d+"mat",b.scene),e.material.diffuseColor=h.a.Red(),e.parent=b._buttonMeshMapping[a].states[d].valueMesh||null,e.isVisible=!1,b._touchDots[d]=e}}})})},b.prototype._setRootMesh=function(a){var b;this.rootMesh=new S.a(this.profileId+"-"+this.handedness,this.scene),this.rootMesh.isPickable=!1;for(var c=0;cd/10&&(g.isVisible=!0),(h+=b._scene.getEngine().getDeltaTime())>=d)b._scene.simulatePointerDown(c.pick,j),i=!0,b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,j),g.isVisible=!1;else{var a=1-h/d;g.scaling.set(a,a,a)}else i=!1,h=0;b._scene.simulatePointerMove(c.pick,j),f=c.pick}}),void 0!==this._options.renderingGroupId&&(g.renderingGroupId=this._options.renderingGroupId),a&&a.onDisposeObservable.addOnce(function(){c.pick&&!b._options.disablePointerUpOnTouchOut&&i&&b._scene.simulatePointerUp(c.pick,j),g.dispose()})},b.prototype._attachScreenRayMode=function(a){var b=this,c=this._controllers[a.uniqueId],d=!1,e={pointerId:c.id,pointerType:"xr"};c.onFrameObserver=this._xrSessionManager.onXRFrameObservable.add(function(){!c.pick||b._options.disablePointerUpOnTouchOut&&d||(d?b._scene.simulatePointerMove(c.pick,e):(b._scene.simulatePointerDown(c.pick,e),d=!0,b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,e)))}),a.onDisposeObservable.addOnce(function(){c.pick&&d&&!b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,e)})},b.prototype._attachTrackedPointerRayMode=function(a){var b=this,c=this._controllers[a.uniqueId];if(this._options.forceGazeMode)return this._attachGazeMode(a);var d={pointerId:c.id,pointerType:"xr"};if(c.onFrameObserver=this._xrSessionManager.onXRFrameObservable.add(function(){c.laserPointer.material.disableLighting=b.disablePointerLighting,c.selectionMesh.material.disableLighting=b.disableSelectionMeshLighting,c.pick&&b._scene.simulatePointerMove(c.pick,d)}),a.inputSource.gamepad){var e=function(e){b._options.overrideButtonId&&(c.selectionComponent=e.getComponent(b._options.overrideButtonId)),c.selectionComponent||(c.selectionComponent=e.getMainComponent()),c.onButtonChangedObserver=c.selectionComponent.onButtonStateChangedObservable.add(function(e){if(e.changes.pressed){e=e.changes.pressed.current;c.pick?(b._options.enablePointerSelectionOnAllControllers||a.uniqueId===b._attachedController)&&(e?(b._scene.simulatePointerDown(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshPickedColor,c.laserPointer.material.emissiveColor=b.laserPointerPickedColor):(b._scene.simulatePointerUp(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshDefaultColor,c.laserPointer.material.emissiveColor=b.laserPointerDefaultColor)):!e||b._options.enablePointerSelectionOnAllControllers||b._options.disableSwitchOnClick||(b._attachedController=a.uniqueId)}})};a.motionController?e(a.motionController):a.onMotionControllerInitObservable.add(e)}else{e=function(a){c.xrController&&a.inputSource===c.xrController.inputSource&&c.pick&&(b._scene.simulatePointerDown(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshPickedColor,c.laserPointer.material.emissiveColor=b.laserPointerPickedColor)};var f=function(a){c.xrController&&a.inputSource===c.xrController.inputSource&&c.pick&&(b._scene.simulatePointerUp(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshDefaultColor,c.laserPointer.material.emissiveColor=b.laserPointerDefaultColor)};c.eventListeners={selectend:f,selectstart:e},this._xrSessionManager.session.addEventListener("selectstart",e),this._xrSessionManager.session.addEventListener("selectend",f)}},b.prototype._convertNormalToDirectionOfRay=function(a,b){a&&Math.acos(g.e.Dot(a,b.direction))a},b.prototype._updatePointerDistance=function(a,b){void 0===b&&(b=100),a.scaling.y=b,this._scene.useRightHandedSystem&&(b*=-1),a.position.z=b/2+.05},Object.defineProperty(b.prototype,"lasterPointerDefaultColor",{get:function(){return this.laserPointerDefaultColor},enumerable:!1,configurable:!0}),b._idCounter=200,b.Name=jb.POINTER_SELECTION,b.Version=1,b}(Vi);kb.AddWebXRFeature(Wi.Name,function(a,b){return function(){return new Wi(a,b)}},Wi.Version,!0);var Xi=function(a){function b(c,d){var e=a.call(this,c)||this;return e._options=d,e._attachController=function(a){if(!e._controllers[a.uniqueId]){var c=e._generateNewHandTipMesh(),d=e._generateVisualCue();switch(e._controllers[a.uniqueId]={xrController:a,meshUnderPointer:null,nearInteractionMesh:null,pick:null,pickIndexMeshTip:c,grabRay:new nc.a(new g.e,new g.e),hoverInteraction:!1,nearInteraction:!1,grabInteraction:!1,id:b._idCounter++,pickedPointVisualCue:d},e._attachedController?!e._options.enableNearInteractionOnAllControllers&&e._options.preferredHandedness&&a.inputSource.handedness===e._options.preferredHandedness&&(e._attachedController=a.uniqueId):e._options.enableNearInteractionOnAllControllers||(e._attachedController=a.uniqueId),a.inputSource.targetRayMode){case"tracked-pointer":return e._attachNearInteractionMode(a);case"gaze":case"screen":return null}}},e._controllers={},e._farInteractionFeature=null,e.selectionMeshDefaultColor=new h.a(.8,.8,.8),e.selectionMeshPickedColor=new h.a(.3,.3,1),e._hoverRadius=.1,e._pickRadius=.02,e._nearGrabLengthScale=5,e._indexTipQuaternion=new g.b,e._indexTipOrientationVector=g.e.Zero(),e._scene=e._xrSessionManager.scene,e._options.farInteractionFeature&&(e._farInteractionFeature=e._options.farInteractionFeature),e}return Object(l.d)(b,a),b.prototype.attach=function(){var b=this;return!!a.prototype.attach.call(this)&&(this._options.xrInput.controllers.forEach(this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable,this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable,function(a){b._detachController(a.uniqueId)}),this._scene.constantlyUpdateMeshUnderPointer=!0,!0)},b.prototype.detach=function(){var b=this;return!!a.prototype.detach.call(this)&&(Object.keys(this._controllers).forEach(function(a){b._detachController(a)}),!0)},b.prototype.getMeshUnderPointer=function(a){return this._controllers[a]?this._controllers[a].meshUnderPointer:null},b.prototype.getXRControllerByPointerId=function(a){for(var b=Object.keys(this._controllers),c=0;ci&&(h=0,f.copyFrom(b.center)),-1!==h&&h=a.timeout&&(a.contextObservable.remove(e),a.onEnded&&a.onEnded(f))},a.observableParameters.mask,a.observableParameters.insertFirst,a.observableParameters.scope);return e}!function(a){a[a.INIT=0]="INIT",a[a.STARTED=1]="STARTED",a[a.ENDED=2]="ENDED"}(Yi||(Yi={}));var cj=function(){function a(a){var b,c=this;this.onEachCountObservable=new f.c,this.onTimerAbortedObservable=new f.c,this.onTimerEndedObservable=new f.c,this.onStateChangedObservable=new f.c,this._observer=null,this._breakOnNextTick=!1,this._tick=function(a){var b=Date.now();c._timer=b-c._startTime;b={startTime:c._startTime,currentTime:b,deltaTime:c._timer,completeRate:c._timer/c._timeToEnd,payload:a};a=c._breakOnNextTick||c._breakCondition(b);a||c._timer>=c._timeToEnd?c._stop(b,a):c.onEachCountObservable.notifyObservers(b)},this._setState(Yi.INIT),this._contextObservable=a.contextObservable,this._observableParameters=null!==(b=a.observableParameters)&&void 0!==b?b:{},this._breakCondition=null!==(b=a.breakCondition)&&void 0!==b?b:function(){return!1},a.onEnded&&this.onTimerEndedObservable.add(a.onEnded),a.onTick&&this.onEachCountObservable.add(a.onTick),a.onAborted&&this.onTimerAbortedObservable.add(a.onAborted)}return Object.defineProperty(a.prototype,"breakCondition",{set:function(a){this._breakCondition=a},enumerable:!1,configurable:!0}),a.prototype.clearObservables=function(){this.onEachCountObservable.clear(),this.onTimerAbortedObservable.clear(),this.onTimerEndedObservable.clear(),this.onStateChangedObservable.clear()},a.prototype.start=function(a){if(void 0===a&&(a=this._timeToEnd),this._state===Yi.STARTED)throw new Error("Timer already started. Please stop it before starting again");this._timeToEnd=a,this._startTime=Date.now(),this._timer=0,this._observer=this._contextObservable.add(this._tick,this._observableParameters.mask,this._observableParameters.insertFirst,this._observableParameters.scope),this._setState(Yi.STARTED)},a.prototype.stop=function(){this._state===Yi.STARTED&&(this._breakOnNextTick=!0)},a.prototype.dispose=function(){this._observer&&this._contextObservable.remove(this._observer),this.clearObservables()},a.prototype._setState=function(a){this._state=a,this.onStateChangedObservable.notifyObservers(this._state)},a.prototype._stop=function(a,b){void 0===b&&(b=!1),this._contextObservable.remove(this._observer),this._setState(Yi.ENDED),b?this.onTimerAbortedObservable.notifyObservers(a):this.onTimerEndedObservable.notifyObservers(a)},a}(),dj=function(a){function b(b,c){var d=a.call(this,b)||this;return d._options=c,d._controllers={},d._snappedToPoint=!1,d._tmpRay=new nc.a(new g.e,new g.e),d._tmpVector=new g.e,d._tmpQuaternion=new g.b,d.skipNextTeleportation=!1,d.backwardsMovementEnabled=!0,d.backwardsTeleportationDistance=.7,d.parabolicCheckRadius=5,d.parabolicRayEnabled=!0,d.straightRayEnabled=!0,d.rotationAngle=Math.PI/8,d.onTargetMeshPositionUpdatedObservable=new f.c,d.teleportationEnabled=!0,d._rotationEnabled=!0,d._attachController=function(a){if(!(d._controllers[a.uniqueId]||d._options.forceHandedness&&a.inputSource.handedness!==d._options.forceHandedness)){d._controllers[a.uniqueId]={xrController:a,teleportationState:{forward:!1,backwards:!1,rotating:!1,currentRotation:0,baseRotation:0}};var b=d._controllers[a.uniqueId];if("tracked-pointer"===b.xrController.inputSource.targetRayMode&&b.xrController.inputSource.gamepad){var c=function(){if(a.motionController){var c=a.motionController.getComponentOfType(Li.THUMBSTICK_TYPE)||a.motionController.getComponentOfType(Li.TOUCHPAD_TYPE);if(!c||d._options.useMainComponentOnly){var e=a.motionController.getMainComponent();if(!e)return;b.teleportationComponent=e,b.onButtonChangedObserver=e.onButtonStateChangedObservable.add(function(){d.teleportationEnabled&&e.changes.pressed&&(e.changes.pressed.current?(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y,b.teleportationState.currentRotation=0,bj({timeout:d._options.timeToTeleport||3e3,contextObservable:d._xrSessionManager.onXRFrameObservable,breakCondition:function(){return!e.pressed},onEnded:function(){d._currentTeleportationControllerId===b.xrController.uniqueId&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)}})):(b.teleportationState.forward=!1,d._currentTeleportationControllerId=""))})}else b.teleportationComponent=c,b.onAxisChangedObserver=c.onAxisValueChangedObservable.add(function(c){if(c.y<=.7&&b.teleportationState.backwards&&(b.teleportationState.backwards=!1),c.y>.7&&!b.teleportationState.forward&&d.backwardsMovementEnabled&&!d.snapPointsOnly&&!b.teleportationState.backwards){b.teleportationState.backwards=!0,d._tmpQuaternion.copyFrom(d._options.xrInput.xrCamera.rotationQuaternion),d._tmpQuaternion.toEulerAnglesToRef(d._tmpVector),d._tmpVector.x=0,d._tmpVector.z=0,g.b.FromEulerVectorToRef(d._tmpVector,d._tmpQuaternion),d._tmpVector.set(0,0,d.backwardsTeleportationDistance*(d._xrSessionManager.scene.useRightHandedSystem?1:-1)),d._tmpVector.rotateByQuaternionToRef(d._tmpQuaternion,d._tmpVector),d._tmpVector.addInPlace(d._options.xrInput.xrCamera.position),d._tmpRay.origin.copyFrom(d._tmpVector),d._tmpRay.length=d._options.xrInput.xrCamera.realWorldHeight+.1,d._tmpRay.direction.set(0,-1,0);var e=d._xrSessionManager.scene.pickWithRay(d._tmpRay,function(a){return-1!==d._floorMeshes.indexOf(a)});e&&e.pickedPoint&&(d._options.xrInput.xrCamera.position.x=e.pickedPoint.x,d._options.xrInput.xrCamera.position.z=e.pickedPoint.z)}if(c.y<-.7&&!d._currentTeleportationControllerId&&!b.teleportationState.rotating&&d.teleportationEnabled&&(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y),c.x){if(b.teleportationState.forward)d._currentTeleportationControllerId===b.xrController.uniqueId&&(d.rotationEnabled?setTimeout(function(){b.teleportationState.currentRotation=Math.atan2(c.x,c.y*(d._xrSessionManager.scene.useRightHandedSystem?1:-1))}):b.teleportationState.currentRotation=0);else if(!b.teleportationState.rotating&&Math.abs(c.x)>.7){b.teleportationState.rotating=!0;e=d.rotationAngle*(c.x>0?1:-1)*(d._xrSessionManager.scene.useRightHandedSystem?-1:1);g.b.FromEulerAngles(0,e,0).multiplyToRef(d._options.xrInput.xrCamera.rotationQuaternion,d._options.xrInput.xrCamera.rotationQuaternion)}}else b.teleportationState.rotating=!1;0===c.x&&0===c.y&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)})}};a.motionController?c():a.onMotionControllerInitObservable.addOnce(function(){c()})}else d._xrSessionManager.scene.onPointerObservable.add(function(c){c.type===Ta.a.POINTERDOWN?(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y,b.teleportationState.currentRotation=0,bj({timeout:d._options.timeToTeleport||3e3,contextObservable:d._xrSessionManager.onXRFrameObservable,onEnded:function(){d._currentTeleportationControllerId===b.xrController.uniqueId&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)}})):c.type===Ta.a.POINTERUP&&(b.teleportationState.forward=!1,d._currentTeleportationControllerId="")})}},d._options.teleportationTargetMesh||d._createDefaultTargetMesh(),d._floorMeshes=d._options.floorMeshes||[],d._snapToPositions=d._options.snapPositions||[],d._setTargetMeshVisibility(!1),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"rotationEnabled",{get:function(){return this._rotationEnabled},set:function(a){if(this._rotationEnabled=a,this._options.teleportationTargetMesh){var b=this._options.teleportationTargetMesh.getChildMeshes(!1,function(a){return"rotationCone"===a.name});b[0]&&b[0].setEnabled(a)}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"teleportationTargetMesh",{get:function(){return this._options.teleportationTargetMesh||null},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapPointsOnly",{get:function(){return!!this._options.snapPointsOnly},set:function(a){this._options.snapPointsOnly=a},enumerable:!1,configurable:!0}),b.prototype.addFloorMesh=function(a){this._floorMeshes.push(a)},b.prototype.addBlockerMesh=function(a){this._options.pickBlockerMeshes=this._options.pickBlockerMeshes||[],this._options.pickBlockerMeshes.push(a)},b.prototype.addSnapPoint=function(a){this._snapToPositions.push(a)},b.prototype.attach=function(){var b=this;return!!a.prototype.attach.call(this)&&(this._currentTeleportationControllerId="",this._options.xrInput.controllers.forEach(this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable,this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable,function(a){b._detachController(a.uniqueId)}),!0)},b.prototype.detach=function(){var b=this;return!!a.prototype.detach.call(this)&&(Object.keys(this._controllers).forEach(function(a){b._detachController(a)}),this._setTargetMeshVisibility(!1),this._currentTeleportationControllerId="",this._controllers={},!0)},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._options.teleportationTargetMesh&&this._options.teleportationTargetMesh.dispose(!1,!0)},b.prototype.removeFloorMesh=function(a){a=this._floorMeshes.indexOf(a);-1!==a&&this._floorMeshes.splice(a,1)},b.prototype.removeBlockerMesh=function(a){this._options.pickBlockerMeshes=this._options.pickBlockerMeshes||[];a=this._options.pickBlockerMeshes.indexOf(a);-1!==a&&this._options.pickBlockerMeshes.splice(a,1)},b.prototype.removeFloorMeshByName=function(a){a=this._xrSessionManager.scene.getMeshByName(a);a&&this.removeFloorMesh(a)},b.prototype.removeSnapPoint=function(a){var b=this._snapToPositions.indexOf(a);if(-1===b)for(var c=0;c=j.video.HAVE_CURRENT_DATA;return!h.poster||h.autoPlay&&f?f&&j._createInternalTexture():(j._texture=j._getEngine().createTexture(h.poster,!1,!j.invertY,d),j._displayingPosterTexture=!0),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"onUserActionRequestedObservable",{get:function(){return this._onUserActionRequestedObservable||(this._onUserActionRequestedObservable=new f.c),this._onUserActionRequestedObservable},enumerable:!1,configurable:!0}),b.prototype._processError=function(a){this._errorFound=!0,this._onError?this._onError(null==a?void 0:a.message):q.a.Error(null==a?void 0:a.message)},b.prototype._handlePlay=function(){var a=this;this._errorFound=!1,this.video.play()["catch"](function(b){if("NotAllowedError"===(null==b?void 0:b.name)){if(a._onUserActionRequestedObservable&&a._onUserActionRequestedObservable.hasObservers())return void a._onUserActionRequestedObservable.notifyObservers(a);if(!a.video.muted)return q.a.Warn("Unable to autoplay a video with sound. Trying again with muted turned true"),a.video.muted=!0,a._errorFound=!1,void a.video.play()["catch"](function(b){a._processError(b)})}a._processError(b)})},b.prototype.getClassName=function(){return"VideoTexture"},b.prototype._getName=function(a){return a instanceof HTMLVideoElement?a.currentSrc:"object"==typeof a?a.toString():a},b.prototype._getVideo=function(a){if(a.isNative)return a;if(a instanceof HTMLVideoElement)return U.b.SetCorsBehavior(a.currentSrc,a),a;var b=document.createElement("video");return"string"==typeof a?(U.b.SetCorsBehavior(a,b),b.src=a):(U.b.SetCorsBehavior(a[0],b),a.forEach(function(a){var c=document.createElement("source");c.src=a,b.appendChild(c)})),b},b.prototype._rebuild=function(){this.update()},b.prototype.update=function(){this.autoUpdateTexture&&this.updateTexture(!0)},b.prototype.updateTexture=function(a){a&&(this.video.paused&&this._stillImageCaptured||(this._stillImageCaptured=!0,this._updateInternalTexture()))},b.prototype.updateURL=function(a){this.video.src=a,this._currentSrc=a},b.prototype.clone=function(){return new b(this.name,this._currentSrc,this.getScene(),this._generateMipMaps,this.invertY,this.samplingMode,this._settings)},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._currentSrc=null,this._onUserActionRequestedObservable&&(this._onUserActionRequestedObservable.clear(),this._onUserActionRequestedObservable=null),this.video.removeEventListener(this._createInternalTextureOnEvent,this._createInternalTexture),this.video.removeEventListener("paused",this._updateInternalTexture),this.video.removeEventListener("seeked",this._updateInternalTexture),this.video.removeEventListener("emptied",this.reset),this.video.pause()},b.CreateFromStreamAsync=function(a,c,d,e){void 0===e&&(e=!0);var f=a.getEngine().createVideoElement(d);return a.getEngine()._badOS&&(document.body.appendChild(f),f.style.transform="scale(0.0001, 0.0001)",f.style.opacity="0",f.style.position="fixed",f.style.bottom="0px",f.style.right="0px"),f.setAttribute("autoplay",""),f.setAttribute("muted","true"),f.setAttribute("playsinline",""),f.muted=!0,void 0!==f.mozSrcObject?f.mozSrcObject=c:"object"==typeof f.srcObject?f.srcObject=c:(window.URL=window.URL||window.webkitURL||window.mozURL||window.msURL,f.src=window.URL&&window.URL.createObjectURL(c)),new Promise(function(c){var d=function(){c(new b("video",f,a,!0,e)),f.removeEventListener("playing",d)};f.addEventListener("playing",d),f.play()})},b.CreateFromWebCamAsync=function(a,b,c,d){var e,f=this;if(void 0===c&&(c=!1),void 0===d&&(d=!0),b&&b.deviceId&&(e={exact:b.deviceId}),navigator.mediaDevices)return navigator.mediaDevices.getUserMedia({video:b,audio:c}).then(function(c){return f.CreateFromStreamAsync(a,c,b,d)});var g=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;return g&&g({video:{deviceId:e,width:{min:b&&b.minWidth||256,max:b&&b.maxWidth||640},height:{min:b&&b.minHeight||256,max:b&&b.maxHeight||480}},audio:c},function(c){return f.CreateFromStreamAsync(a,c,b,d)},function(a){q.a.Error(a.name)}),Promise.reject("No support for userMedia on this device")},b.CreateFromWebCam=function(a,b,c,d,e){void 0===d&&(d=!1),void 0===e&&(e=!0),this.CreateFromWebCamAsync(a,c,d,e).then(function(a){b&&b(a)})["catch"](function(a){q.a.Error(a.name)})},b}(V.a),ij=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"videoTexture",{get:function(){return this._texture},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"videoMode",{get:function(){return this.textureMode},set:function(a){this.textureMode=a},enumerable:!1,configurable:!0}),b.prototype._initTexture=function(a,b,c){var d=this,e={loop:c.loop,autoPlay:c.autoPlay,autoUpdateTexture:!0,poster:c.poster};a=new hj((this.name||"videoDome")+"_texture",a,b,c.generateMipMaps,this._useDirectMapping,V.a.TRILINEAR_SAMPLINGMODE,e);return c.clickToPlay&&(b.onPointerUp=function(){d._texture.video.play()}),a.onLoadObservable.add(function(){d.onLoadObservable.notifyObservers()}),a},b.MODE_MONOSCOPIC=fi.MODE_MONOSCOPIC,b.MODE_TOPBOTTOM=fi.MODE_TOPBOTTOM,b.MODE_SIDEBYSIDE=fi.MODE_SIDEBYSIDE,b}(fi),jj=function(){function a(a){this.engine=a,this._captureGPUFrameTime=!1,this._captureShaderCompilationTime=!1,this._shaderCompilationTime=new re.a,this._onBeginFrameObserver=null,this._onEndFrameObserver=null,this._onBeforeShaderCompilationObserver=null,this._onAfterShaderCompilationObserver=null}return Object.defineProperty(a.prototype,"gpuFrameTimeCounter",{get:function(){return this.engine.getGPUFrameTimeCounter()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureGPUFrameTime",{get:function(){return this._captureGPUFrameTime},set:function(a){a!==this._captureGPUFrameTime&&(this._captureGPUFrameTime=a,this.engine.captureGPUFrameTime(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderCompilationTimeCounter",{get:function(){return this._shaderCompilationTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureShaderCompilationTime",{get:function(){return this._captureShaderCompilationTime},set:function(a){var b=this;a!==this._captureShaderCompilationTime&&(this._captureShaderCompilationTime=a,a?(this._onBeforeShaderCompilationObserver=this.engine.onBeforeShaderCompilationObservable.add(function(){b._shaderCompilationTime.fetchNewFrame(),b._shaderCompilationTime.beginMonitoring()}),this._onAfterShaderCompilationObserver=this.engine.onAfterShaderCompilationObservable.add(function(){b._shaderCompilationTime.endMonitoring()})):(this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver),this._onBeforeShaderCompilationObserver=null,this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver),this._onAfterShaderCompilationObserver=null))},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.engine.onBeginFrameObservable.remove(this._onBeginFrameObserver),this._onBeginFrameObserver=null,this.engine.onEndFrameObservable.remove(this._onEndFrameObserver),this._onEndFrameObserver=null,this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver),this._onBeforeShaderCompilationObserver=null,this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver),this._onAfterShaderCompilationObserver=null,this.engine=null},a}(),kj=function(){function a(a){var b=this;this.scene=a,this._captureActiveMeshesEvaluationTime=!1,this._activeMeshesEvaluationTime=new re.a,this._captureRenderTargetsRenderTime=!1,this._renderTargetsRenderTime=new re.a,this._captureFrameTime=!1,this._frameTime=new re.a,this._captureRenderTime=!1,this._renderTime=new re.a,this._captureInterFrameTime=!1,this._interFrameTime=new re.a,this._captureParticlesRenderTime=!1,this._particlesRenderTime=new re.a,this._captureSpritesRenderTime=!1,this._spritesRenderTime=new re.a,this._capturePhysicsTime=!1,this._physicsTime=new re.a,this._captureAnimationsTime=!1,this._animationsTime=new re.a,this._captureCameraRenderTime=!1,this._cameraRenderTime=new re.a,this._onBeforeActiveMeshesEvaluationObserver=null,this._onAfterActiveMeshesEvaluationObserver=null,this._onBeforeRenderTargetsRenderObserver=null,this._onAfterRenderTargetsRenderObserver=null,this._onAfterRenderObserver=null,this._onBeforeDrawPhaseObserver=null,this._onAfterDrawPhaseObserver=null,this._onBeforeAnimationsObserver=null,this._onBeforeParticlesRenderingObserver=null,this._onAfterParticlesRenderingObserver=null,this._onBeforeSpritesRenderingObserver=null,this._onAfterSpritesRenderingObserver=null,this._onBeforePhysicsObserver=null,this._onAfterPhysicsObserver=null,this._onAfterAnimationsObserver=null,this._onBeforeCameraRenderObserver=null,this._onAfterCameraRenderObserver=null,this._onBeforeAnimationsObserver=a.onBeforeAnimationsObservable.add(function(){b._captureActiveMeshesEvaluationTime&&b._activeMeshesEvaluationTime.fetchNewFrame(),b._captureRenderTargetsRenderTime&&b._renderTargetsRenderTime.fetchNewFrame(),b._captureFrameTime&&(U.b.StartPerformanceCounter("Scene rendering"),b._frameTime.beginMonitoring()),b._captureInterFrameTime&&b._interFrameTime.endMonitoring(),b._captureParticlesRenderTime&&b._particlesRenderTime.fetchNewFrame(),b._captureSpritesRenderTime&&b._spritesRenderTime.fetchNewFrame(),b._captureAnimationsTime&&b._animationsTime.beginMonitoring(),b.scene.getEngine()._drawCalls.fetchNewFrame()}),this._onAfterRenderObserver=a.onAfterRenderObservable.add(function(){b._captureFrameTime&&(U.b.EndPerformanceCounter("Scene rendering"),b._frameTime.endMonitoring()),b._captureRenderTime&&b._renderTime.endMonitoring(!1),b._captureInterFrameTime&&b._interFrameTime.beginMonitoring()})}return Object.defineProperty(a.prototype,"activeMeshesEvaluationTimeCounter",{get:function(){return this._activeMeshesEvaluationTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureActiveMeshesEvaluationTime",{get:function(){return this._captureActiveMeshesEvaluationTime},set:function(a){var b=this;a!==this._captureActiveMeshesEvaluationTime&&(this._captureActiveMeshesEvaluationTime=a,a?(this._onBeforeActiveMeshesEvaluationObserver=this.scene.onBeforeActiveMeshesEvaluationObservable.add(function(){U.b.StartPerformanceCounter("Active meshes evaluation"),b._activeMeshesEvaluationTime.beginMonitoring()}),this._onAfterActiveMeshesEvaluationObserver=this.scene.onAfterActiveMeshesEvaluationObservable.add(function(){U.b.EndPerformanceCounter("Active meshes evaluation"),b._activeMeshesEvaluationTime.endMonitoring()})):(this.scene.onBeforeActiveMeshesEvaluationObservable.remove(this._onBeforeActiveMeshesEvaluationObserver),this._onBeforeActiveMeshesEvaluationObserver=null,this.scene.onAfterActiveMeshesEvaluationObservable.remove(this._onAfterActiveMeshesEvaluationObserver),this._onAfterActiveMeshesEvaluationObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderTargetsRenderTimeCounter",{get:function(){return this._renderTargetsRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureRenderTargetsRenderTime",{get:function(){return this._captureRenderTargetsRenderTime},set:function(a){var b=this;a!==this._captureRenderTargetsRenderTime&&(this._captureRenderTargetsRenderTime=a,a?(this._onBeforeRenderTargetsRenderObserver=this.scene.onBeforeRenderTargetsRenderObservable.add(function(){U.b.StartPerformanceCounter("Render targets rendering"),b._renderTargetsRenderTime.beginMonitoring()}),this._onAfterRenderTargetsRenderObserver=this.scene.onAfterRenderTargetsRenderObservable.add(function(){U.b.EndPerformanceCounter("Render targets rendering"),b._renderTargetsRenderTime.endMonitoring(!1)})):(this.scene.onBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver),this._onBeforeRenderTargetsRenderObserver=null,this.scene.onAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver),this._onAfterRenderTargetsRenderObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"particlesRenderTimeCounter",{get:function(){return this._particlesRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureParticlesRenderTime",{get:function(){return this._captureParticlesRenderTime},set:function(a){var b=this;a!==this._captureParticlesRenderTime&&(this._captureParticlesRenderTime=a,a?(this._onBeforeParticlesRenderingObserver=this.scene.onBeforeParticlesRenderingObservable.add(function(){U.b.StartPerformanceCounter("Particles"),b._particlesRenderTime.beginMonitoring()}),this._onAfterParticlesRenderingObserver=this.scene.onAfterParticlesRenderingObservable.add(function(){U.b.EndPerformanceCounter("Particles"),b._particlesRenderTime.endMonitoring(!1)})):(this.scene.onBeforeParticlesRenderingObservable.remove(this._onBeforeParticlesRenderingObserver),this._onBeforeParticlesRenderingObserver=null,this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver),this._onAfterParticlesRenderingObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"spritesRenderTimeCounter",{get:function(){return this._spritesRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureSpritesRenderTime",{get:function(){return this._captureSpritesRenderTime},set:function(a){var b=this;a!==this._captureSpritesRenderTime&&(this._captureSpritesRenderTime=a,this.scene.spriteManagers&&(a?(this._onBeforeSpritesRenderingObserver=this.scene.onBeforeSpritesRenderingObservable.add(function(){U.b.StartPerformanceCounter("Sprites"),b._spritesRenderTime.beginMonitoring()}),this._onAfterSpritesRenderingObserver=this.scene.onAfterSpritesRenderingObservable.add(function(){U.b.EndPerformanceCounter("Sprites"),b._spritesRenderTime.endMonitoring(!1)})):(this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver),this._onBeforeSpritesRenderingObserver=null,this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver),this._onAfterSpritesRenderingObserver=null)))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"physicsTimeCounter",{get:function(){return this._physicsTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"capturePhysicsTime",{get:function(){return this._capturePhysicsTime},set:function(a){var b=this;a!==this._capturePhysicsTime&&this.scene.onBeforePhysicsObservable&&(this._capturePhysicsTime=a,a?(this._onBeforePhysicsObserver=this.scene.onBeforePhysicsObservable.add(function(){U.b.StartPerformanceCounter("Physics"),b._physicsTime.beginMonitoring()}),this._onAfterPhysicsObserver=this.scene.onAfterPhysicsObservable.add(function(){U.b.EndPerformanceCounter("Physics"),b._physicsTime.endMonitoring()})):(this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver),this._onBeforePhysicsObserver=null,this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver),this._onAfterPhysicsObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationsTimeCounter",{get:function(){return this._animationsTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureAnimationsTime",{get:function(){return this._captureAnimationsTime},set:function(a){var b=this;a!==this._captureAnimationsTime&&(this._captureAnimationsTime=a,a?this._onAfterAnimationsObserver=this.scene.onAfterAnimationsObservable.add(function(){b._animationsTime.endMonitoring()}):(this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver),this._onAfterAnimationsObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frameTimeCounter",{get:function(){return this._frameTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureFrameTime",{get:function(){return this._captureFrameTime},set:function(a){this._captureFrameTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"interFrameTimeCounter",{get:function(){return this._interFrameTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureInterFrameTime",{get:function(){return this._captureInterFrameTime},set:function(a){this._captureInterFrameTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderTimeCounter",{get:function(){return this._renderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureRenderTime",{get:function(){return this._captureRenderTime},set:function(a){var b=this;a!==this._captureRenderTime&&(this._captureRenderTime=a,a?(this._onBeforeDrawPhaseObserver=this.scene.onBeforeDrawPhaseObservable.add(function(){b._renderTime.beginMonitoring(),U.b.StartPerformanceCounter("Main render")}),this._onAfterDrawPhaseObserver=this.scene.onAfterDrawPhaseObservable.add(function(){b._renderTime.endMonitoring(!1),U.b.EndPerformanceCounter("Main render")})):(this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver),this._onBeforeDrawPhaseObserver=null,this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver),this._onAfterDrawPhaseObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cameraRenderTimeCounter",{get:function(){return this._cameraRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureCameraRenderTime",{get:function(){return this._captureCameraRenderTime},set:function(a){var b=this;a!==this._captureCameraRenderTime&&(this._captureCameraRenderTime=a,a?(this._onBeforeCameraRenderObserver=this.scene.onBeforeCameraRenderObservable.add(function(a){b._cameraRenderTime.beginMonitoring(),U.b.StartPerformanceCounter("Rendering camera "+a.name)}),this._onAfterCameraRenderObserver=this.scene.onAfterCameraRenderObservable.add(function(a){b._cameraRenderTime.endMonitoring(!1),U.b.EndPerformanceCounter("Rendering camera "+a.name)})):(this.scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=null,this.scene.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"drawCallsCounter",{get:function(){return this.scene.getEngine()._drawCalls},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=null,this.scene.onBeforeActiveMeshesEvaluationObservable.remove(this._onBeforeActiveMeshesEvaluationObserver),this._onBeforeActiveMeshesEvaluationObserver=null,this.scene.onAfterActiveMeshesEvaluationObservable.remove(this._onAfterActiveMeshesEvaluationObserver),this._onAfterActiveMeshesEvaluationObserver=null,this.scene.onBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver),this._onBeforeRenderTargetsRenderObserver=null,this.scene.onAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver),this._onAfterRenderTargetsRenderObserver=null,this.scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver),this._onBeforeAnimationsObserver=null,this.scene.onBeforeParticlesRenderingObservable.remove(this._onBeforeParticlesRenderingObserver),this._onBeforeParticlesRenderingObserver=null,this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver),this._onAfterParticlesRenderingObserver=null,this._onBeforeSpritesRenderingObserver&&(this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver),this._onBeforeSpritesRenderingObserver=null),this._onAfterSpritesRenderingObserver&&(this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver),this._onAfterSpritesRenderingObserver=null),this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver),this._onBeforeDrawPhaseObserver=null,this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver),this._onAfterDrawPhaseObserver=null,this._onBeforePhysicsObserver&&(this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver),this._onBeforePhysicsObserver=null),this._onAfterPhysicsObserver&&(this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver),this._onAfterPhysicsObserver=null),this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver),this._onAfterAnimationsObserver=null,this.scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=null,this.scene.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=null,this.scene=null},a}();rb="#if defined(DIFFUSE_ISLINEAR) || defined(EMISSIVE_ISLINEAR) #include #endif #ifdef DIFFUSE varying vec2 vUVDiffuse; uniform sampler2D diffuseSampler; #endif #ifdef OPACITY varying vec2 vUVOpacity; uniform sampler2D opacitySampler; uniform float opacityIntensity; #endif #ifdef EMISSIVE varying vec2 vUVEmissive; uniform sampler2D emissiveSampler; #endif #ifdef VERTEXALPHA varying vec4 vColor; #endif uniform vec4 glowColor; void main(void) { vec4 finalColor=glowColor; #ifdef DIFFUSE vec4 albedoTexture=texture2D(diffuseSampler,vUVDiffuse); #ifdef DIFFUSE_ISLINEAR albedoTexture=toGammaSpace(albedoTexture); #endif #ifdef GLOW finalColor.a*=albedoTexture.a; #endif #ifdef HIGHLIGHT finalColor.a=albedoTexture.a; #endif #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vUVOpacity); #ifdef OPACITYRGB finalColor.a*=getLuminance(opacityMap.rgb); #else finalColor.a*=opacityMap.a; #endif finalColor.a*=opacityIntensity; #endif #ifdef VERTEXALPHA finalColor.a*=vColor.a; #endif #ifdef ALPHATEST if (finalColor.a #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; varying vec4 vPosition; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #ifdef DIFFUSE varying vec2 vUVDiffuse; uniform mat4 diffuseMatrix; #endif #ifdef OPACITY varying vec2 vUVOpacity; uniform mat4 opacityMatrix; #endif #ifdef EMISSIVE varying vec2 vUVEmissive; uniform mat4 emissiveMatrix; #endif #ifdef VERTEXALPHA attribute vec4 color; varying vec4 vColor; #endif void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include #ifdef CUBEMAP vPosition=finalWorld*vec4(positionUpdated,1.0); gl_Position=viewProjection*finalWorld*vec4(position,1.0); #else vPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); gl_Position=vPosition; #endif #ifdef DIFFUSE #ifdef DIFFUSEUV1 vUVDiffuse=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef DIFFUSEUV2 vUVDiffuse=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef OPACITY #ifdef OPACITYUV1 vUVOpacity=vec2(opacityMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef OPACITYUV2 vUVOpacity=vec2(opacityMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef EMISSIVE #ifdef EMISSIVEUV1 vUVEmissive=vec2(emissiveMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef EMISSIVEUV2 vUVEmissive=vec2(emissiveMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef VERTEXALPHA vColor=color; #endif }";X.a.ShadersStore.glowMapGenerationVertexShader=a;var lj=function(){function a(b,c){this._vertexBuffers={},this._maxSize=0,this._mainTextureDesiredSize={width:0,height:0},this._shouldRender=!0,this._postProcesses=[],this._textures=[],this._emissiveTextureAndColor={texture:null,color:new h.b},this.neutralColor=new h.b,this.isEnabled=!0,this.disableBoundingBoxesFromEffectLayer=!1,this.onDisposeObservable=new f.c,this.onBeforeRenderMainTextureObservable=new f.c,this.onBeforeComposeObservable=new f.c,this.onBeforeRenderMeshToEffect=new f.c,this.onAfterRenderMeshToEffect=new f.c,this.onAfterComposeObservable=new f.c,this.onSizeChangedObservable=new f.c,this.name=b,this._scene=c||C.a.LastCreatedScene,a._SceneComponentInitialization(this._scene),this._engine=this._scene.getEngine(),this._maxSize=this._engine.getCaps().maxTextureSize,this._scene.effectLayers.push(this),this._effectLayerMapGenerationDrawWrapper=new Ec.a(this._engine),this._mergeDrawWrapper=new Ec.a(this._engine),this._generateIndexBuffer(),this._generateVertexBuffer()}return Object.defineProperty(a.prototype,"camera",{get:function(){return this._effectLayerOptions.camera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderingGroupId",{get:function(){return this._effectLayerOptions.renderingGroupId},set:function(a){this._effectLayerOptions.renderingGroupId=a},enumerable:!1,configurable:!0}),a.prototype._init=function(a){this._effectLayerOptions=Object(l.a)({mainTextureRatio:.5,alphaBlendingMode:r.a.ALPHA_COMBINE,camera:null,renderingGroupId:-1},a),this._setMainTextureSize(),this._createMainTexture(),this._createTextureAndPostProcesses(),this._mergeDrawWrapper.setEffect(this._createMergeEffect())},a.prototype._generateIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._engine.createIndexBuffer(a)},a.prototype._generateVertexBuffer=function(){var a=[];a.push(1,1),a.push(-1,1),a.push(-1,-1),a.push(1,-1);a=new W.b(this._engine,a,W.b.PositionKind,!1,!1,2);this._vertexBuffers[W.b.PositionKind]=a},a.prototype._setMainTextureSize=function(){this._effectLayerOptions.mainTextureFixedSize?(this._mainTextureDesiredSize.width=this._effectLayerOptions.mainTextureFixedSize,this._mainTextureDesiredSize.height=this._effectLayerOptions.mainTextureFixedSize):(this._mainTextureDesiredSize.width=this._engine.getRenderWidth()*this._effectLayerOptions.mainTextureRatio,this._mainTextureDesiredSize.height=this._engine.getRenderHeight()*this._effectLayerOptions.mainTextureRatio,this._mainTextureDesiredSize.width=this._engine.needPOTTextures?T.a.GetExponentOfTwo(this._mainTextureDesiredSize.width,this._maxSize):this._mainTextureDesiredSize.width,this._mainTextureDesiredSize.height=this._engine.needPOTTextures?T.a.GetExponentOfTwo(this._mainTextureDesiredSize.height,this._maxSize):this._mainTextureDesiredSize.height),this._mainTextureDesiredSize.width=Math.floor(this._mainTextureDesiredSize.width),this._mainTextureDesiredSize.height=Math.floor(this._mainTextureDesiredSize.height)},a.prototype._createMainTexture=function(){var a=this;if(this._mainTexture=new cd("HighlightLayerMainRTT",{width:this._mainTextureDesiredSize.width,height:this._mainTextureDesiredSize.height},this._scene,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT),this._mainTexture.activeCamera=this._effectLayerOptions.camera,this._mainTexture.wrapU=V.a.CLAMP_ADDRESSMODE,this._mainTexture.wrapV=V.a.CLAMP_ADDRESSMODE,this._mainTexture.anisotropicFilteringLevel=1,this._mainTexture.updateSamplingMode(V.a.BILINEAR_SAMPLINGMODE),this._mainTexture.renderParticles=!1,this._mainTexture.renderList=null,this._mainTexture.ignoreCameraViewport=!0,this._mainTexture.customRenderFunction=function(b,c,d,e){var f;a.onBeforeRenderMainTextureObservable.notifyObservers(a);var g=a._scene.getEngine();if(e.length){for(g.setColorWrite(!1),f=0;f4&&(f.push(W.b.MatricesIndicesExtraKind),f.push(W.b.MatricesWeightsExtraKind)),e.push("#define NUM_BONE_INFLUENCERS "+g.numBoneInfluencers);k=g.skeleton;k&&k.isUsingTextureForMatrices?e.push("#define BONETEXTURE"):e.push("#define BonesPerMesh "+(k?k.bones.length+1:0)),g.numBoneInfluencers>0&&j.addCPUSkinningFallback(0,g)}else e.push("#define NUM_BONE_INFLUENCERS 0");d=g.morphTargetManager;m=0;d&&d.numInfluencers>0&&(e.push("#define MORPHTARGETS"),m=d.numInfluencers,e.push("#define NUM_MORPH_INFLUENCERS "+m),d.isUsingTextureForTargets&&e.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(f,g,m)),b&&(e.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(f),a.getRenderingMesh().hasThinInstances&&e.push("#define THIN_INSTANCES")),this._addCustomEffectDefines(e);c=e.join(" ");return this._cachedDefines!==c&&(this._cachedDefines=c,this._effectLayerMapGenerationDrawWrapper.setEffect(this._scene.getEngine().createEffect("glowMapGeneration",f,["world","mBones","viewProjection","glowColor","morphTargetInfluences","boneTextureWidth","diffuseMatrix","emissiveMatrix","opacityMatrix","opacityIntensity","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","emissiveSampler","opacitySampler","boneSampler","morphTargets"],c,j,void 0,void 0,{maxSimultaneousMorphTargets:m}))),this._effectLayerMapGenerationDrawWrapper.effect.isReady()},a.prototype.render=function(){var a=this._mergeDrawWrapper;if(a.effect.isReady()){for(var b=0;b-1&&this._scene.effectLayers.splice(a,1),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onBeforeRenderMainTextureObservable.clear(),this.onBeforeComposeObservable.clear(),this.onBeforeRenderMeshToEffect.clear(),this.onAfterRenderMeshToEffect.clear(),this.onAfterComposeObservable.clear(),this.onSizeChangedObservable.clear()},a.prototype.getClassName=function(){return"EffectLayer"},a.Parse=function(a,b,c){return U.b.Instantiate(a.customType).Parse(a,b,c)},a._SceneComponentInitialization=function(a){throw Object(Ka.a)("EffectLayerSceneComponent")},Object(l.c)([Object(J.d)()],a.prototype,"name",void 0),Object(l.c)([Object(J.g)()],a.prototype,"neutralColor",void 0),Object(l.c)([Object(J.d)()],a.prototype,"isEnabled",void 0),Object(l.c)([Object(J.e)()],a.prototype,"camera",null),Object(l.c)([Object(J.d)()],a.prototype,"renderingGroupId",null),Object(l.c)([Object(J.d)()],a.prototype,"disableBoundingBoxesFromEffectLayer",void 0),a}();d.a.AddParser(Na.a.NAME_EFFECTLAYER,function(a,b,c,d){if(a.effectLayers){c.effectLayers||(c.effectLayers=new Array);for(var e=0;e0){this._previousStencilState=this._engine.getStencilBuffer();for(var d=0,c=c;d-1)){this._renderEffects=!0,this._needStencil=this._needStencil||e.needStencil();e=e._mainTexture;e._shouldRender()&&(this.scene.incrementRenderId(),e.render(!1,!1),b=!0)}}this.scene.incrementRenderId()}return b},a.prototype._setStencil=function(){this._needStencil&&this._engine.setStencilBuffer(!0)},a.prototype._setStencilBack=function(){this._needStencil&&this._engine.setStencilBuffer(this._previousStencilState)},a.prototype._draw=function(a){if(this._renderEffects){this._engine.setDepthBuffer(!1);for(var b=this.scene.effectLayers,c=0;c-1},b.prototype.referenceMeshToUseItsOwnMaterial=function(a){var b=this;this._meshesUsingTheirOwnMaterials.push(a.uniqueId),a.onDisposeObservable.add(function(){b._disposeMesh(a)})},b.prototype.unReferenceMeshFromUsingItsOwnMaterial=function(a){for(var b=this._meshesUsingTheirOwnMaterials.indexOf(a.uniqueId);b>=0;)this._meshesUsingTheirOwnMaterials.splice(b,1),b=this._meshesUsingTheirOwnMaterials.indexOf(a.uniqueId)},b.prototype._disposeMesh=function(a){this.removeIncludedOnlyMesh(a),this.removeExcludedMesh(a)},b.prototype.getClassName=function(){return"GlowLayer"},b.prototype.serialize=function(){var a,b=J.a.Serialize(this);if(b.customType="BABYLON.GlowLayer",b.includedMeshes=[],this._includedOnlyMeshes.length)for(a=0;a0&&a.isBackground===b&&a.renderTargetTextures.indexOf(d)>-1&&0!=(a.layerMask&c)},a.prototype._drawRenderTargetBackground=function(a){var b=this;this._draw(function(c){return b._drawRenderTargetPredicate(c,!0,b.scene.activeCamera.layerMask,a)})},a.prototype._drawRenderTargetForeground=function(a){var b=this;this._draw(function(c){return b._drawRenderTargetPredicate(c,!1,b.scene.activeCamera.layerMask,a)})},a.prototype.addFromContainer=function(a){var b=this;a.layers&&a.layers.forEach(function(a){b.scene.layers.push(a)})},a.prototype.removeFromContainer=function(a,b){var c=this;void 0===b&&(b=!1),a.layers&&a.layers.forEach(function(a){var d=c.scene.layers.indexOf(a);-1!==d&&c.scene.layers.splice(d,1),b&&a.dispose()})},a}();wi=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec4 color; #include void main(void) { vec4 baseColor=texture2D(textureSampler,vUV); #ifdef LINEAR baseColor.rgb=toGammaSpace(baseColor.rgb); #endif #ifdef ALPHATEST if (baseColor.a<0.4) discard; #endif gl_FragColor=baseColor*color; }";X.a.ShadersStore.layerPixelShader=wi;rb=" attribute vec2 position; uniform vec2 scale; uniform vec2 offset; uniform mat4 textureMatrix; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vec2 shiftedPosition=position*scale+offset; vUV=vec2(textureMatrix*vec4(shiftedPosition*madd+madd,1.0,0.0)); gl_Position=vec4(shiftedPosition,0.0,1.0); }";X.a.ShadersStore.layerVertexShader=rb;var rj=function(){function a(a,b,c,d,e){this.name=a,this.scale=new g.d(1,1),this.offset=new g.d(0,0),this.alphaBlendingMode=r.a.ALPHA_COMBINE,this.layerMask=268435455,this.renderTargetTextures=[],this.renderOnlyInRenderTargetTextures=!1,this.isEnabled=!0,this._vertexBuffers={},this.onDisposeObservable=new f.c,this.onBeforeRenderObservable=new f.c,this.onAfterRenderObservable=new f.c,this.texture=b?new V.a(b,c,!0):null,this.isBackground=void 0===d||d,this.color=void 0===e?new h.b(1,1,1,1):e,this._scene=c||C.a.LastCreatedScene;a=this._scene._getComponent(Na.a.NAME_LAYER);a||(a=new qj(this._scene),this._scene._addComponent(a)),this._scene.layers.push(this);b=this._scene.getEngine();this._drawWrapper=new Ec.a(b);d=[];d.push(1,1),d.push(-1,1),d.push(-1,-1),d.push(1,-1);e=new W.b(b,d,W.b.PositionKind,!1,!1,2);this._vertexBuffers[W.b.PositionKind]=e,this._createIndexBuffer()}return Object.defineProperty(a.prototype,"onDispose",{set:function(a){this._onDisposeObserver&&this.onDisposeObservable.remove(this._onDisposeObserver),this._onDisposeObserver=this.onDisposeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onBeforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onAfterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=this.onAfterRenderObservable.add(a)},enumerable:!1,configurable:!0}),a.prototype._createIndexBuffer=function(){var a=this._scene.getEngine(),b=[];b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),this._indexBuffer=a.createIndexBuffer(b)},a.prototype._rebuild=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&a._rebuild(),this._createIndexBuffer()},a.prototype.render=function(){if(this.isEnabled){var a=this._scene.getEngine(),b="";this.alphaTest&&(b="#define ALPHATEST"),this.texture&&!this.texture.gammaSpace&&(b+=" #define LINEAR"),this._previousDefines!==b&&(this._previousDefines=b,this._drawWrapper.effect=a.createEffect("layer",[W.b.PositionKind],["textureMatrix","color","scale","offset"],["textureSampler"],b));b=this._drawWrapper.effect;b&&b.isReady()&&this.texture&&this.texture.isReady()&&(a=this._scene.getEngine(),(this.onBeforeRenderObservable.notifyObservers(this),a.enableEffect(this._drawWrapper),a.setState(!1),b.setTexture("textureSampler",this.texture),b.setMatrix("textureMatrix",this.texture.getTextureMatrix()),b.setFloat4("color",this.color.r,this.color.g,this.color.b,this.color.a),b.setVector2("offset",this.offset),b.setVector2("scale",this.scale),a.bindBuffers(this._vertexBuffers,this._indexBuffer,b),this.alphaTest?a.drawElementsType(qi.a.TriangleFillMode,0,6):(a.setAlphaMode(this.alphaBlendingMode),a.drawElementsType(qi.a.TriangleFillMode,0,6),a.setAlphaMode(r.a.ALPHA_DISABLE)),this.onAfterRenderObservable.notifyObservers(this)))}},a.prototype.dispose=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&(a.dispose(),this._vertexBuffers[W.b.PositionKind]=null),this._indexBuffer&&(this._scene.getEngine()._releaseBuffer(this._indexBuffer),this._indexBuffer=null),this.texture&&(this.texture.dispose(),this.texture=null),this.renderTargetTextures=[];a=this._scene.layers.indexOf(this);this._scene.layers.splice(a,1),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onAfterRenderObservable.clear(),this.onBeforeRenderObservable.clear()},a}(),sj=function(){function a(a,b,c,d,e){this.size=a,this.position=b,this.alphaMode=r.a.ALPHA_ONEONE,this.color=c||new h.a(1,1,1),this.texture=d?new V.a(d,e.getScene(),!0):null,this._system=e,e.lensFlares.push(this)}return a.AddFlare=function(b,c,d,e,f){return new a(b,c,d,e,f)},a.prototype.dispose=function(){this.texture&&this.texture.dispose();var a=this._system.lensFlares.indexOf(this);this._system.lensFlares.splice(a,1)},a}();a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec4 color; void main(void) { vec4 baseColor=texture2D(textureSampler,vUV); gl_FragColor=baseColor*color; }";X.a.ShadersStore.lensFlarePixelShader=a;wi=" attribute vec2 position; uniform mat4 viewportMatrix; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vUV=position*madd+madd; gl_Position=viewportMatrix*vec4(position,0.0,1.0); }";X.a.ShadersStore.lensFlareVertexShader=wi;var tj=function(){function a(b,c,d){this.name=b,this.lensFlares=new Array,this.borderLimit=300,this.viewportBorder=0,this.layerMask=268435455,this._vertexBuffers={},this._isEnabled=!0,this._scene=d||C.a.LastCreatedScene,a._SceneComponentInitialization(this._scene),this._emitter=c,this.id=b,d.lensFlareSystems.push(this),this.meshesSelectionPredicate=function(a){return d.activeCamera&&a.material&&a.isVisible&&a.isEnabled()&&a.isBlocker&&0!=(a.layerMask&d.activeCamera.layerMask)};c=d.getEngine();this._drawWrapper=new Ec.a(c);b=[];b.push(1,1),b.push(-1,1),b.push(-1,-1),b.push(1,-1),this._vertexBuffers[W.b.PositionKind]=new W.b(c,b,W.b.PositionKind,!1,!1,2),this._createIndexBuffer(),this._drawWrapper.effect=c.createEffect("lensFlare",[W.b.PositionKind],["color","viewportMatrix"],["textureSampler"],"")}return a.prototype._createIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._scene.getEngine().createIndexBuffer(a)},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a},enumerable:!1,configurable:!0}),a.prototype.getScene=function(){return this._scene},a.prototype.getEmitter=function(){return this._emitter},a.prototype.setEmitter=function(a){this._emitter=a},a.prototype.getEmitterPosition=function(){return this._emitter.getAbsolutePosition?this._emitter.getAbsolutePosition():this._emitter.position},a.prototype.computeEffectivePosition=function(a){var b=this.getEmitterPosition();b=g.e.Project(b,g.a.Identity(),this._scene.getTransformMatrix(),a),this._positionX=b.x,this._positionY=b.y,b=g.e.TransformCoordinates(this.getEmitterPosition(),this._scene.getViewMatrix()),this.viewportBorder>0&&(a.x-=this.viewportBorder,a.y-=this.viewportBorder,a.width+=2*this.viewportBorder,a.height+=2*this.viewportBorder,b.x+=this.viewportBorder,b.y+=this.viewportBorder,this._positionX+=this.viewportBorder,this._positionY+=this.viewportBorder);var c=this._scene.useRightHandedSystem;return!!(b.z>0&&!c||b.z<0&&c)&&(this._positionX>a.x&&this._positionXa.y&&(this._positionY,a.y,a.height),!0)},a.prototype._isVisible=function(){if(!this._isEnabled||!this._scene.activeCamera)return!1;var a=this.getEmitterPosition().subtract(this._scene.activeCamera.globalPosition),b=a.length();a.normalize();a=new nc.a(this._scene.activeCamera.globalPosition,a);a=this._scene.pickWithRay(a,this.meshesSelectionPredicate,!0);return!a||!a.hit||a.distance>b},a.prototype.render=function(){if(!this._drawWrapper.effect.isReady()||!this._scene.activeCamera)return!1;var a,b,c=this._scene.getEngine(),d=this._scene.activeCamera.viewport.toGlobal(c.getRenderWidth(!0),c.getRenderHeight(!0));if(!this.computeEffectivePosition(d))return!1;if(!this._isVisible())return!1;a=(a=this._positionXd.x+d.width-this.borderLimit?this._positionX-d.x-d.width+this.borderLimit:0)>(b=this._positionYd.y+d.height-this.borderLimit?this._positionY-d.y-d.height+this.borderLimit:0)?a:b;(a-=this.viewportBorder)>this.borderLimit&&(a=this.borderLimit);b=1-I.a.Clamp(a/this.borderLimit,0,1);if(b<0)return!1;b>1&&(b=1),this.viewportBorder>0&&(d.x+=this.viewportBorder,d.y+=this.viewportBorder,d.width-=2*this.viewportBorder,d.height-=2*this.viewportBorder,this._positionX-=this.viewportBorder,this._positionY-=this.viewportBorder);a=d.x+d.width/2;var e=d.y+d.height/2,f=a-this._positionX,h=e-this._positionY;c.enableEffect(this._drawWrapper),c.setState(!1),c.setDepthBuffer(!1),c.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect);for(var i=0;i0);for(var c=0,d=b;c0)}},a}();tj._SceneComponentInitialization=function(a){var b=a._getComponent(Na.a.NAME_LENSFLARESYSTEM);b||(b=new uj(a),a._addComponent(b))};rb=" float bayerDither2(vec2 _P) { return mod(2.0*_P.y+_P.x+1.0,4.0); } float bayerDither4(vec2 _P) { vec2 P1=mod(_P,2.0); vec2 P2=floor(0.5*mod(_P,4.0)); return 4.0*bayerDither2(P1)+bayerDither2(P2); } float bayerDither8(vec2 _P) { vec2 P1=mod(_P,2.0); vec2 P2=floor(0.5*mod(_P,4.0)); vec2 P4=floor(0.25*mod(_P,8.0)); return 4.0*(4.0*bayerDither2(P1)+bayerDither2(P2))+bayerDither2(P4); } ";X.a.IncludesShadersStore.bayerDitherFunctions=rb;a="#if SM_FLOAT == 0 #include #endif #if SM_SOFTTRANSPARENTSHADOW == 1 #include uniform float softTransparentShadowSM; #endif varying float vDepthMetricSM; #if SM_USEDISTANCE == 1 uniform vec3 lightDataSM; varying vec3 vPositionWSM; #endif uniform vec3 biasAndScaleSM; uniform vec2 depthValuesSM; #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 varying float zSM; #endif ";X.a.IncludesShadersStore.shadowMapFragmentExtraDeclaration=a;wi=" float depthSM=vDepthMetricSM; #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 #if SM_USEDISTANCE == 1 depthSM=(length(vPositionWSM-lightDataSM)+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else #ifdef USE_REVERSE_DEPTHBUFFER depthSM=(-zSM+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else depthSM=(zSM+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #endif #ifdef USE_REVERSE_DEPTHBUFFER gl_FragDepth=clamp(1.0-depthSM,0.0,1.0); #else gl_FragDepth=clamp(depthSM,0.0,1.0); #endif #elif SM_USEDISTANCE == 1 depthSM=(length(vPositionWSM-lightDataSM)+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #if SM_ESM == 1 depthSM=clamp(exp(-min(87.,biasAndScaleSM.z*depthSM)),0.,1.); #endif #if SM_FLOAT == 1 gl_FragColor=vec4(depthSM,1.0,1.0,1.0); #else gl_FragColor=pack(depthSM); #endif return;";X.a.IncludesShadersStore.shadowMapFragment=wi;rb="#include #ifdef ALPHATEST varying vec2 vUV; uniform sampler2D diffuseSampler; #endif #include void main(void) { #include #ifdef ALPHATEST float alphaFromAlphaTexture=texture2D(diffuseSampler,vUV).a; if (alphaFromAlphaTexture<0.4) discard; #endif #if SM_SOFTTRANSPARENTSHADOW == 1 #ifdef ALPHATEST if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM*alphaFromAlphaTexture) discard; #else if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM) discard; #endif #endif #include }";X.a.ShadersStore.shadowMapPixelShader=rb;a="uniform mat4 viewProjection; #ifdef MULTIVIEW uniform mat4 viewProjectionR; #endif uniform mat4 view; uniform mat4 projection; uniform vec4 vEyePosition; ";X.a.IncludesShadersStore.sceneVertexDeclaration=a;X.a.IncludesShadersStore.meshVertexDeclaration="uniform mat4 world; uniform float visibility; ";X.a.IncludesShadersStore.shadowMapVertexDeclaration="#include #include ";wi="layout(std140,column_major) uniform; #include #include ";X.a.IncludesShadersStore.shadowMapUboDeclaration=wi;rb="#if SM_NORMALBIAS == 1 uniform vec3 lightDataSM; #endif uniform vec3 biasAndScaleSM; uniform vec2 depthValuesSM; varying float vDepthMetricSM; #if SM_USEDISTANCE == 1 varying vec3 vPositionWSM; #endif #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 varying float zSM; #endif ";X.a.IncludesShadersStore.shadowMapVertexExtraDeclaration=rb;a=" #if SM_NORMALBIAS == 1 #if SM_DIRECTIONINLIGHTDATA == 1 vec3 worldLightDirSM=normalize(-lightDataSM.xyz); #else vec3 directionToLightSM=lightDataSM.xyz-worldPos.xyz; vec3 worldLightDirSM=normalize(directionToLightSM); #endif float ndlSM=dot(vNormalW,worldLightDirSM); float sinNLSM=sqrt(1.0-ndlSM*ndlSM); float normalBiasSM=biasAndScaleSM.y*sinNLSM; worldPos.xyz-=vNormalW*normalBiasSM; #endif ";X.a.IncludesShadersStore.shadowMapVertexNormalBias=a;wi="#if SM_USEDISTANCE == 1 vPositionWSM=worldPos.xyz; #endif #if SM_DEPTHTEXTURE == 1 #ifdef IS_NDC_HALF_ZRANGE #define BIASFACTOR 0.5 #else #define BIASFACTOR 1.0 #endif #ifdef USE_REVERSE_DEPTHBUFFER gl_Position.z-=biasAndScaleSM.x*gl_Position.w*BIASFACTOR; #else gl_Position.z+=biasAndScaleSM.x*gl_Position.w*BIASFACTOR; #endif #endif #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 zSM=gl_Position.z; gl_Position.z=0.0; #elif SM_USEDISTANCE == 0 #ifdef USE_REVERSE_DEPTHBUFFER vDepthMetricSM=(-gl_Position.z+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else vDepthMetricSM=(gl_Position.z+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #endif ";X.a.IncludesShadersStore.shadowMapVertexMetric=wi;rb=" attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #include #include #include[0..maxSimultaneousMorphTargets] #ifdef INSTANCES attribute vec4 world0; attribute vec4 world1; attribute vec4 world2; attribute vec4 world3; #endif #include #include<__decl__shadowMapVertex> #ifdef ALPHATEST varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #include #include void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #ifdef NORMAL vec3 normalUpdated=normal; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); #ifdef NORMAL mat3 normWorldSM=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vec3 vNormalW=normalUpdated/vec3(dot(normWorldSM[0],normWorldSM[0]),dot(normWorldSM[1],normWorldSM[1]),dot(normWorldSM[2],normWorldSM[2])); vNormalW=normalize(normWorldSM*vNormalW); #else #ifdef NONUNIFORMSCALING normWorldSM=transposeMat3(inverseMat3(normWorldSM)); #endif vec3 vNormalW=normalize(normWorldSM*normalUpdated); #endif #endif #include gl_Position=viewProjection*worldPos; #include #ifdef ALPHATEST #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #include }";X.a.ShadersStore.shadowMapVertexShader=rb;a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec2 screenSize; void main(void) { vec4 colorDepth=vec4(0.0); for (int x=-OFFSET; x<=OFFSET; x++) for (int y=-OFFSET; y<=OFFSET; y++) colorDepth+=texture2D(textureSampler,vUV+vec2(x,y)/screenSize); gl_FragColor=(colorDepth/float((OFFSET*2+1)*(OFFSET*2+1))); }";X.a.ShadersStore.depthBoxBlurPixelShader=a;wi="#if SM_SOFTTRANSPARENTSHADOW == 1 if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM*alpha) discard; #endif ";X.a.IncludesShadersStore.shadowMapFragmentSoftTransparentShadow=wi;var vj=function(){function a(b,c,d){if(this.onBeforeShadowMapRenderObservable=new f.c,this.onAfterShadowMapRenderObservable=new f.c,this.onBeforeShadowMapRenderMeshObservable=new f.c,this.onAfterShadowMapRenderMeshObservable=new f.c,this._bias=5e-5,this._normalBias=0,this._blurBoxOffset=1,this._blurScale=2,this._blurKernel=1,this._useKernelBlur=!1,this._filter=a.FILTER_NONE,this._filteringQuality=a.QUALITY_HIGH,this._contactHardeningLightSizeUVRatio=.1,this._darkness=0,this._transparencyShadow=!1,this.enableSoftTransparentShadow=!1,this.frustumEdgeFalloff=0,this.forceBackFacesOnly=!1,this._lightDirection=g.e.Zero(),this._viewMatrix=g.a.Zero(),this._projectionMatrix=g.a.Zero(),this._transformMatrix=g.a.Zero(),this._cachedPosition=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._cachedDirection=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._currentFaceIndex=0,this._currentFaceIndexCache=0,this._defaultTextureMatrix=g.a.Identity(),this._mapSize=b,this._light=c,this._scene=c.getScene(),c._shadowGenerator=this,this.id=c.id,this._useUBO=this._scene.getEngine().supportsUniformBuffers,this._nameForDrawWrapper=[r.a.SUBMESH_DRAWWRAPPER_SHADOWGENERATOR_PREFIX+a._Counter++],c.needCube())for(b=this._nameForDrawWrapper[0],c=0;c<6;++c)this._nameForDrawWrapper[c]=b+"_"+c;a._SceneComponentInitialization(this._scene);b=this._scene.getEngine().getCaps();d?b.textureFloatRender&&b.textureFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_FLOAT:b.textureHalfFloatRender&&b.textureHalfFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_HALF_FLOAT:this._textureType=r.a.TEXTURETYPE_UNSIGNED_INT:b.textureHalfFloatRender&&b.textureHalfFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_HALF_FLOAT:b.textureFloatRender&&b.textureFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_FLOAT:this._textureType=r.a.TEXTURETYPE_UNSIGNED_INT,this._initializeGenerator(),this._applyFilterValues(),this._nameForDrawWrapperCurrent=this._nameForDrawWrapper[0]}return Object.defineProperty(a.prototype,"bias",{get:function(){return this._bias},set:function(a){this._bias=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"normalBias",{get:function(){return this._normalBias},set:function(a){this._normalBias=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurBoxOffset",{get:function(){return this._blurBoxOffset},set:function(a){this._blurBoxOffset!==a&&(this._blurBoxOffset=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurScale",{get:function(){return this._blurScale},set:function(a){this._blurScale!==a&&(this._blurScale=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurKernel",{get:function(){return this._blurKernel},set:function(a){this._blurKernel!==a&&(this._blurKernel=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useKernelBlur",{get:function(){return this._useKernelBlur},set:function(a){this._useKernelBlur!==a&&(this._useKernelBlur=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthScale",{get:function(){return void 0!==this._depthScale?this._depthScale:this._light.getDepthScale()},set:function(a){this._depthScale=a},enumerable:!1,configurable:!0}),a.prototype._validateFilter=function(a){return a},Object.defineProperty(a.prototype,"filter",{get:function(){return this._filter},set:function(b){if(b=this._validateFilter(b),this._light.needCube()){if(b===a.FILTER_BLUREXPONENTIALSHADOWMAP)return void (this.useExponentialShadowMap=!0);if(b===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP)return void (this.useCloseExponentialShadowMap=!0);if(b===a.FILTER_PCF||b===a.FILTER_PCSS)return void (this.usePoissonSampling=!0)}b!==a.FILTER_PCF&&b!==a.FILTER_PCSS||this._scene.getEngine()._features.supportShadowSamplers?this._filter!==b&&(this._filter=b,this._disposeBlurPostProcesses(),this._applyFilterValues(),this._light._markMeshesAsLightDirty()):this.usePoissonSampling=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"usePoissonSampling",{get:function(){return this.filter===a.FILTER_POISSONSAMPLING},set:function(b){var c=this._validateFilter(a.FILTER_POISSONSAMPLING);(b||this.filter===a.FILTER_POISSONSAMPLING)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useExponentialShadowMap",{get:function(){return this.filter===a.FILTER_EXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_EXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_EXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useBlurExponentialShadowMap",{get:function(){return this.filter===a.FILTER_BLUREXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_BLUREXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_BLUREXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useCloseExponentialShadowMap",{get:function(){return this.filter===a.FILTER_CLOSEEXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_CLOSEEXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_CLOSEEXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useBlurCloseExponentialShadowMap",{get:function(){return this.filter===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"usePercentageCloserFiltering",{get:function(){return this.filter===a.FILTER_PCF},set:function(b){var c=this._validateFilter(a.FILTER_PCF);(b||this.filter===a.FILTER_PCF)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"filteringQuality",{get:function(){return this._filteringQuality},set:function(a){this._filteringQuality!==a&&(this._filteringQuality=a,this._disposeBlurPostProcesses(),this._applyFilterValues(),this._light._markMeshesAsLightDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useContactHardeningShadow",{get:function(){return this.filter===a.FILTER_PCSS},set:function(b){var c=this._validateFilter(a.FILTER_PCSS);(b||this.filter===a.FILTER_PCSS)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"contactHardeningLightSizeUVRatio",{get:function(){return this._contactHardeningLightSizeUVRatio},set:function(a){this._contactHardeningLightSizeUVRatio=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"darkness",{get:function(){return this._darkness},set:function(a){this.setDarkness(a)},enumerable:!1,configurable:!0}),a.prototype.getDarkness=function(){return this._darkness},a.prototype.setDarkness=function(a){return this._darkness=a>=1?1:a<=0?0:a,this},Object.defineProperty(a.prototype,"transparencyShadow",{get:function(){return this._transparencyShadow},set:function(a){this.setTransparencyShadow(a)},enumerable:!1,configurable:!0}),a.prototype.setTransparencyShadow=function(a){return this._transparencyShadow=a,this},a.prototype.getShadowMap=function(){return this._shadowMap},a.prototype.getShadowMapForRendering=function(){return this._shadowMap2?this._shadowMap2:this._shadowMap},a.prototype.getClassName=function(){return a.CLASSNAME},a.prototype.addShadowCaster=function(a,b){if(void 0===b&&(b=!0),!this._shadowMap)return this;if(this._shadowMap.renderList||(this._shadowMap.renderList=[]),-1===this._shadowMap.renderList.indexOf(a)&&this._shadowMap.renderList.push(a),b)for(b=0,a=a.getChildMeshes();b=e.length)return void (a&&a(c));setTimeout(i,16)}};i()}else a&&a(this)}else a&&a(this)}else a&&a(this)},a.prototype.forceCompilationAsync=function(a){var b=this;return new Promise(function(c){b.forceCompilation(function(){c()},a)})},a.prototype._isReadyCustomDefines=function(a,b,c){},a.prototype._prepareShadowDefines=function(a,b,c,d){c.push("#define SM_FLOAT "+(this._textureType!==r.a.TEXTURETYPE_UNSIGNED_INT?"1":"0")),c.push("#define SM_ESM "+(this.useExponentialShadowMap||this.useBlurExponentialShadowMap?"1":"0")),c.push("#define SM_DEPTHTEXTURE "+(this.usePercentageCloserFiltering||this.useContactHardeningShadow?"1":"0"));var e=a.getMesh();return c.push("#define SM_NORMALBIAS "+(this.normalBias&&e.isVerticesDataPresent(W.b.NormalKind)?"1":"0")),c.push("#define SM_DIRECTIONINLIGHTDATA "+(this.getLight().getTypeID()===Kh.a.LIGHTTYPEID_DIRECTIONALLIGHT?"1":"0")),c.push("#define SM_USEDISTANCE "+(this._light.needCube()?"1":"0")),c.push("#define SM_SOFTTRANSPARENTSHADOW "+(this.enableSoftTransparentShadow&&d?"1":"0")),this._isReadyCustomDefines(c,a,b),c},a.prototype.isReady=function(a,b,c){var d=a.getMaterial(),e=null==d?void 0:d.shadowDepthWrapper,f=[];if(this._prepareShadowDefines(a,b,f,c),e){if(!e.isReadyForSubMesh(a,f,this,b,this._nameForDrawWrapperCurrent))return!1}else{c=a._getDrawWrapper(this._nameForDrawWrapperCurrent,!0);e=c.effect;var g=c.defines,h=[W.b.PositionKind],i=a.getMesh();if(this.normalBias&&i.isVerticesDataPresent(W.b.NormalKind)&&(h.push(W.b.NormalKind),f.push("#define NORMAL"),i.nonUniformScaling&&f.push("#define NONUNIFORMSCALING")),d&&d.needAlphaTesting()){d=d.getAlphaTestTexture();if(d){if(!d.isReady())return!1;f.push("#define ALPHATEST"),i.isVerticesDataPresent(W.b.UVKind)&&(h.push(W.b.UVKind),f.push("#define UV1")),i.isVerticesDataPresent(W.b.UV2Kind)&&1===d.coordinatesIndex&&(h.push(W.b.UV2Kind),f.push("#define UV2"))}}d=new bi.a;if(i.useBones&&i.computeBonesUsingShaders&&i.skeleton){h.push(W.b.MatricesIndicesKind),h.push(W.b.MatricesWeightsKind),i.numBoneInfluencers>4&&(h.push(W.b.MatricesIndicesExtraKind),h.push(W.b.MatricesWeightsExtraKind));var j=i.skeleton;f.push("#define NUM_BONE_INFLUENCERS "+i.numBoneInfluencers),i.numBoneInfluencers>0&&d.addCPUSkinningFallback(0,i),j.isUsingTextureForMatrices?f.push("#define BONETEXTURE"):f.push("#define BonesPerMesh "+(j.bones.length+1))}else f.push("#define NUM_BONE_INFLUENCERS 0");j=i.morphTargetManager;var k=0;j&&j.numInfluencers>0&&(f.push("#define MORPHTARGETS"),k=j.numInfluencers,f.push("#define NUM_MORPH_INFLUENCERS "+k),j.isUsingTextureForTargets&&f.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(h,i,k));j=this._scene;if(j.clipPlane&&f.push("#define CLIPPLANE"),j.clipPlane2&&f.push("#define CLIPPLANE2"),j.clipPlane3&&f.push("#define CLIPPLANE3"),j.clipPlane4&&f.push("#define CLIPPLANE4"),j.clipPlane5&&f.push("#define CLIPPLANE5"),j.clipPlane6&&f.push("#define CLIPPLANE6"),b&&(f.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(h),a.getRenderingMesh().hasThinInstances&&f.push("#define THIN_INSTANCES")),this.customShaderOptions&&this.customShaderOptions.defines)for(i=0,j=this.customShaderOptions.defines;i #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; uniform vec2 depthValues; #if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif varying float vDepthMetric; void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #ifdef USE_REVERSE_DEPTHBUFFER vDepthMetric=((-gl_Position.z+depthValues.x)/(depthValues.y)); #else vDepthMetric=((gl_Position.z+depthValues.x)/(depthValues.y)); #endif #if defined(ALPHATEST) || defined(BASIC_RENDER) #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif } ";X.a.ShadersStore.depthVertexShader=a;var wj=function(){function a(b,c,d,e,f){var g=this;void 0===c&&(c=r.a.TEXTURETYPE_FLOAT),void 0===d&&(d=null),void 0===e&&(e=!1),void 0===f&&(f=V.a.TRILINEAR_SAMPLINGMODE),this.enabled=!0,this.forceDepthWriteTransparentMeshes=!1,this.useOnlyInActiveCamera=!1,this._scene=b,this._storeNonLinearDepth=e,this.isPacked=c===r.a.TEXTURETYPE_UNSIGNED_BYTE,this.isPacked?this._clearColor=new h.b(1,1,1,1):this._clearColor=new h.b(1,0,0,1),a._SceneComponentInitialization(this._scene),this._nameForDrawWrapper=r.a.SUBMESH_DRAWWRAPPER_DEPTHRENDERER_PREFIX+a._Counter++,this._camera=d;var i=b.getEngine();f!==V.a.NEAREST_SAMPLINGMODE&&(c!==r.a.TEXTURETYPE_FLOAT||i._caps.textureFloatLinearFiltering||(f=V.a.NEAREST_SAMPLINGMODE),c!==r.a.TEXTURETYPE_HALF_FLOAT||i._caps.textureHalfFloatLinearFiltering||(f=V.a.NEAREST_SAMPLINGMODE));e=this.isPacked||!i._features.supportExtendedTextureFormats?r.a.TEXTUREFORMAT_RGBA:r.a.TEXTUREFORMAT_R;this._depthMap=new cd("depthMap",{width:i.getRenderWidth(),height:i.getRenderHeight()},this._scene,!1,!0,c,!1,f,void 0,void 0,void 0,e),this._depthMap.wrapU=V.a.CLAMP_ADDRESSMODE,this._depthMap.wrapV=V.a.CLAMP_ADDRESSMODE,this._depthMap.refreshRate=1,this._depthMap.renderParticles=!1,this._depthMap.renderList=null,this._depthMap.activeCamera=this._camera,this._depthMap.ignoreCameraViewport=!0,this._depthMap.useCameraPostProcesses=!1,this._depthMap.onClearObservable.add(function(a){a.clear(g._clearColor,!0,!0,!0)}),this._depthMap.onBeforeBindObservable.add(function(){var a;null===(a=i._debugPushGroup)||void 0===a||a.call(i,"depth renderer",1)}),this._depthMap.onAfterUnbindObservable.add(function(){var a;null===(a=i._debugPopGroup)||void 0===a||a.call(i,1)});var j=function(a){var b,c=a.getRenderingMesh(),d=a.getEffectiveMesh(),e=g._scene,f=e.getEngine(),h=a.getMaterial();if(d._internalAbstractMeshDataInfo._isActiveIntermediate=!1,h&&!d.infiniteDistance&&!h.disableDepthWrite&&0!==a.verticesCount&&a._renderId!==e.getRenderId()){var i=d._getWorldMatrixDeterminant()<0;b=null!==(b=c.overrideMaterialSideOrientation)&&void 0!==b?b:h.sideOrientation;(e.useRightHandedSystem&&!i||!e.useRightHandedSystem&&i)&&(b=b===r.a.MATERIAL_ClockWiseSideOrientation?r.a.MATERIAL_CounterClockWiseSideOrientation:r.a.MATERIAL_ClockWiseSideOrientation);i=b===r.a.MATERIAL_ClockWiseSideOrientation;f.setState(h.backFaceCulling,0,!1,i,h.cullBackFaces);b=c._getInstancesRenderList(a._id,!!a.getReplacementMesh());if(!b.mustReturn){i=f.getCaps().instancedArrays&&(null!==b.visibleInstances[a._id]&&void 0!==b.visibleInstances[a._id]||c.hasThinInstances);var j=g._camera||e.activeCamera;if(g.isReady(a,i)&&j){a._renderId=e.getRenderId();var k=a._getDrawWrapper(g._nameForDrawWrapper),l=Ec.a.GetEffect(k),m=j.mode===cb.a.ORTHOGRAPHIC_CAMERA;f.enableEffect(k),i||c._bind(a,l,h.fillMode),l.setMatrix("viewProjection",e.getTransformMatrix()),l.setMatrix("world",d.getWorldMatrix());if(m?(k=!f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:1,e=f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:1):(k=f.useReverseDepthBuffer&&f.isNDCHalfZRange?j.minZ:f.isNDCHalfZRange?0:j.minZ,e=f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:j.maxZ),l.setFloat2("depthValues",k,k+e),h&&h.needAlphaTesting()){m=h.getAlphaTestTexture();m&&(l.setTexture("diffuseSampler",m),l.setMatrix("diffuseMatrix",m.getTextureMatrix()))}if(c.useBones&&c.computeBonesUsingShaders&&c.skeleton){f=c.skeleton;if(f.isUsingTextureForMatrices){j=f.getTransformMatrixTexture(c);if(!j)return;l.setTexture("boneSampler",j),l.setFloat("boneTextureWidth",4*(f.bones.length+1))}else l.setMatrices("mBones",f.getTransformMatrices(c))}Yh.a.BindMorphTargetParameters(c,l),c.morphTargetManager&&c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(l),c._processRendering(d,a,l,h.fillMode,b,i,function(a,b){return l.setMatrix("world",b)})}}}};this._depthMap.customRenderFunction=function(a,b,c,d){var e;if(d.length)for(e=0;e4&&(i.push(W.b.MatricesIndicesExtraKind),i.push(W.b.MatricesWeightsExtraKind)),d.push("#define NUM_BONE_INFLUENCERS "+j.numBoneInfluencers),d.push("#define BonesPerMesh "+(j.skeleton?j.skeleton.bones.length+1:0));c=a.getRenderingMesh().skeleton;(null==c?void 0:c.isUsingTextureForMatrices)&&d.push("#define BONETEXTURE")}else d.push("#define NUM_BONE_INFLUENCERS 0");c=j.morphTargetManager;var k=0;c&&c.numInfluencers>0&&(k=c.numInfluencers,d.push("#define MORPHTARGETS"),d.push("#define NUM_MORPH_INFLUENCERS "+k),c.isUsingTextureForTargets&&d.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(i,j,k)),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(i),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES")),this._storeNonLinearDepth&&d.push("#define NONLINEARDEPTH"),this.isPacked&&d.push("#define PACKED");c=d.join(" ");return h!==c&&(h=c,g=f.createEffect("depth",i,["world","mBones","boneTextureWidth","viewProjection","diffuseMatrix","depthValues","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","morphTargets","boneSampler"],c,void 0,void 0,void 0,{maxSimultaneousMorphTargets:k})),e.setEffect(g,h),g.isReady()},a.prototype.getDepthMap=function(){return this._depthMap},a.prototype.dispose=function(){this._depthMap.dispose()},a._Counter=0,a._SceneComponentInitialization=function(a){throw Object(Ka.a)("DepthRendererSceneComponent")},a}();wi="varying vec2 vUV; uniform sampler2D textureSampler; #if defined(INITIAL) uniform sampler2D sourceTexture; uniform vec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*(texSize-1.0)); float f1=texelFetch(sourceTexture,coord,0).r; float f2=texelFetch(sourceTexture,coord+ivec2(1,0),0).r; float f3=texelFetch(sourceTexture,coord+ivec2(1,1),0).r; float f4=texelFetch(sourceTexture,coord+ivec2(0,1),0).r; float minz=min(min(min(f1,f2),f3),f4); #ifdef DEPTH_REDUX float maxz=max(max(max(sign(1.0-f1)*f1,sign(1.0-f2)*f2),sign(1.0-f3)*f3),sign(1.0-f4)*f4); #else float maxz=max(max(max(f1,f2),f3),f4); #endif glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(MAIN) uniform vec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*(texSize-1.0)); vec2 f1=texelFetch(textureSampler,coord,0).rg; vec2 f2=texelFetch(textureSampler,coord+ivec2(1,0),0).rg; vec2 f3=texelFetch(textureSampler,coord+ivec2(1,1),0).rg; vec2 f4=texelFetch(textureSampler,coord+ivec2(0,1),0).rg; float minz=min(min(min(f1.x,f2.x),f3.x),f4.x); float maxz=max(max(max(f1.y,f2.y),f3.y),f4.y); glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(ONEBEFORELAST) uniform ivec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*vec2(texSize-1)); vec2 f1=texelFetch(textureSampler,coord % texSize,0).rg; vec2 f2=texelFetch(textureSampler,(coord+ivec2(1,0)) % texSize,0).rg; vec2 f3=texelFetch(textureSampler,(coord+ivec2(1,1)) % texSize,0).rg; vec2 f4=texelFetch(textureSampler,(coord+ivec2(0,1)) % texSize,0).rg; float minz=min(f1.x,f2.x); float maxz=max(f1.y,f2.y); glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(LAST) void main(void) { glFragColor=vec4(0.); discard; } #endif ";X.a.ShadersStore.minmaxReduxPixelShader=wi;var xj=function(){function a(a){var b=this;this.onAfterReductionPerformed=new f.c,this._forceFullscreenViewport=!0,this._activated=!1,this._camera=a,this._postProcessManager=new ad.a(a.getScene()),this._onContextRestoredObserver=a.getEngine().onContextRestoredObservable.add(function(){b._postProcessManager._rebuild()})}return Object.defineProperty(a.prototype,"sourceTexture",{get:function(){return this._sourceTexture},enumerable:!1,configurable:!0}),a.prototype.setSourceTexture=function(a,b,c,d){var e=this;if(void 0===c&&(c=r.a.TEXTURETYPE_HALF_FLOAT),void 0===d&&(d=!0),a!==this._sourceTexture){this.dispose(!1),this._sourceTexture=a,this._reductionSteps=[],this._forceFullscreenViewport=d;var f=this._camera.getScene();a=new Fc("Initial reduction phase","minmaxRedux",["texSize"],["sourceTexture"],1,null,r.a.TEXTURE_NEAREST_NEAREST,f.getEngine(),!1,"#define INITIAL"+(b?" #define DEPTH_REDUX":""),c,void 0,void 0,void 0,r.a.TEXTUREFORMAT_RG);a.autoClear=!1,a.forceFullscreenViewport=d;b=this._sourceTexture.getRenderWidth();var g=this._sourceTexture.getRenderHeight();a.onApply=function(a,b){return function(c){c.setTexture("sourceTexture",e._sourceTexture),c.setFloat2("texSize",a,b)}}(b,g),this._reductionSteps.push(a);for(a=1;b>1||g>1;){b=Math.max(Math.round(b/2),1),g=Math.max(Math.round(g/2),1);var h=new Fc("Reduction phase "+a,"minmaxRedux",["texSize"],null,{width:b,height:g},null,r.a.TEXTURE_NEAREST_NEAREST,f.getEngine(),!1,"#define "+(1==b&&1==g?"LAST":1==b||1==g?"ONEBEFORELAST":"MAIN"),c,void 0,void 0,void 0,r.a.TEXTUREFORMAT_RG);(h.autoClear=!1,h.forceFullscreenViewport=d,h.onApply=function(a,b){return function(c){1==a||1==b?c.setInt2("texSize",a,b):c.setFloat2("texSize",a,b)}}(b,g),this._reductionSteps.push(h),a++,1==b&&1==g)&&h.onAfterRenderObservable.add(function(a,b,c){var d=new Float32Array(4*a*b),g={min:0,max:0};return function(){f.getEngine()._readTexturePixels(c.inputTexture.texture,a,b,-1,0,d,!1),g.min=d[0],g.max=d[1],e.onAfterReductionPerformed.notifyObservers(g)}}(b,g,h))}}},Object.defineProperty(a.prototype,"refreshRate",{get:function(){return this._sourceTexture?this._sourceTexture.refreshRate:-1},set:function(a){this._sourceTexture&&(this._sourceTexture.refreshRate=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"activated",{get:function(){return this._activated},enumerable:!1,configurable:!0}),a.prototype.activate=function(){var a=this;!this._onAfterUnbindObserver&&this._sourceTexture&&(this._onAfterUnbindObserver=this._sourceTexture.onAfterUnbindObservable.add(function(){var b,c=a._camera.getScene().getEngine();null===(b=c._debugPushGroup)||void 0===b||b.call(c,"min max reduction",1),a._reductionSteps[0].activate(a._camera),a._postProcessManager.directRender(a._reductionSteps,a._reductionSteps[0].inputTexture,a._forceFullscreenViewport),c.unBindFramebuffer(a._reductionSteps[0].inputTexture,!1),null===(b=c._debugPopGroup)||void 0===b||b.call(c,1)}),this._activated=!0)},a.prototype.deactivate=function(){this._onAfterUnbindObserver&&this._sourceTexture&&(this._sourceTexture.onAfterUnbindObservable.remove(this._onAfterUnbindObserver),this._onAfterUnbindObserver=null,this._activated=!1)},a.prototype.dispose=function(a){if(void 0===a&&(a=!0),a&&(this.onAfterReductionPerformed.clear(),this._onContextRestoredObserver&&(this._camera.getEngine().onContextRestoredObservable.remove(this._onContextRestoredObserver),this._onContextRestoredObserver=null)),this.deactivate(),this._reductionSteps){for(var b=0;bb&&(a=0,b=1),a<0&&(a=0),b>1&&(b=1),this._minDistance=a,this._maxDistance=b,this._breaksAreDirty=!0)},Object.defineProperty(b.prototype,"minDistance",{get:function(){return this._minDistance},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"maxDistance",{get:function(){return this._maxDistance},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return b.CLASSNAME},b.prototype.getCascadeMinExtents=function(a){return a>=0&&a=0&&athis._scene.activeCamera.maxZ||(this._shadowMaxZ=a,this._light._markMeshesAsLightDirty(),this._breaksAreDirty=!0):this._shadowMaxZ=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"debug",{get:function(){return this._debug},set:function(a){this._debug=a,this._light._markMeshesAsLightDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"depthClamp",{get:function(){return this._depthClamp},set:function(a){this._depthClamp=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cascadeBlendPercentage",{get:function(){return this._cascadeBlendPercentage},set:function(a){this._cascadeBlendPercentage=a,this._light._markMeshesAsLightDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lambda",{get:function(){return this._lambda},set:function(a){a=Math.min(Math.max(a,0),1);this._lambda!=a&&(this._lambda=a,this._breaksAreDirty=!0)},enumerable:!1,configurable:!0}),b.prototype.getCascadeViewMatrix=function(a){return a>=0&&a=0&&a=0&&a=a&&(c=0,a=1),c==b._minDistance&&a==b._maxDistance||b.setMinMaxDistance(c,a)}),this._depthReducer.setDepthRenderer(this._depthRenderer)),this._depthReducer.activate()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"autoCalcDepthBoundsRefreshRate",{get:function(){var a;return null!==(a=null===(a=null===(a=this._depthReducer)||void 0===a?void 0:a.depthRenderer)||void 0===a?void 0:a.getDepthMap().refreshRate)&&void 0!==a?a:-1},set:function(a){var b;(null===(b=this._depthReducer)||void 0===b?void 0:b.depthRenderer)&&(this._depthReducer.depthRenderer.getDepthMap().refreshRate=a)},enumerable:!1,configurable:!0}),b.prototype.splitFrustum=function(){this._breaksAreDirty=!0},b.prototype._splitFrustum=function(){var a=this._scene.activeCamera;if(a){for(var b=a.minZ,a=a.maxZ,c=a-b,d=this._minDistance,e=b+d*c,a=b+(this._shadowMaxZ=b?Math.min((this._shadowMaxZ-b)/(a-b),this._maxDistance):this._maxDistance)*c,f=a-e,a=a/e,g=0;gMath.PI;)e-=2*Math.PI;e=e/Math.PI;a=a/Math.PI;e=.5*e+.5;e=Math.round(e*c);e<0?e=0:e>=c&&(e=c-1);a=Math.round(a*d);a<0?a=0:a>=d&&(a=d-1);d=d-a-1;return{r:b[d*c*3+3*e+0],g:b[d*c*3+3*e+1],b:b[d*c*3+3*e+2]}},a.FACE_LEFT=[new g.e(-1,-1,-1),new g.e(1,-1,-1),new g.e(-1,1,-1),new g.e(1,1,-1)],a.FACE_RIGHT=[new g.e(1,-1,1),new g.e(-1,-1,1),new g.e(1,1,1),new g.e(-1,1,1)],a.FACE_FRONT=[new g.e(1,-1,-1),new g.e(1,-1,1),new g.e(1,1,-1),new g.e(1,1,1)],a.FACE_BACK=[new g.e(-1,-1,1),new g.e(-1,-1,-1),new g.e(-1,1,1),new g.e(-1,1,-1)],a.FACE_DOWN=[new g.e(1,1,-1),new g.e(1,1,1),new g.e(-1,1,-1),new g.e(-1,1,1)],a.FACE_UP=[new g.e(-1,-1,-1),new g.e(-1,-1,1),new g.e(1,-1,-1),new g.e(1,-1,1)],a}(),Lj=function(){function a(){}return a.Ldexp=function(a,b){return b>1023?a*Math.pow(2,1023)*Math.pow(2,b-1023):b<-1074?a*Math.pow(2,-1074)*Math.pow(2,b+1074):a*Math.pow(2,b)},a.Rgbe2float=function(a,b,c,d,e,f){e>0?(e=this.Ldexp(1,e-136),a[f+0]=b*e,a[f+1]=c*e,a[f+2]=d*e):(a[f+0]=0,a[f+1]=0,a[f+2]=0)},a.readStringLine=function(a,b){for(var c="",d="",e=b;e32767)throw"HDR Bad header format, unsupported size";return{height:a,width:d,dataPosition:e+=b.length+1}},a.GetCubeMapTextureData=function(a,b){a=new Uint8Array(a);var c=this.RGBE_ReadHeader(a);a=this.RGBE_ReadPixels(a,c);return Kj.ConvertPanoramaToCubemap(a,c.width,c.height,b)},a.RGBE_ReadPixels=function(a,b){return this.RGBE_ReadPixels_RLE(a,b)},a.RGBE_ReadPixels_RLE=function(a,b){for(var c,d,e,f,g,h=b.height,i=b.width,j=b.dataPosition,k=0,o=0,q=0,s=new ArrayBuffer(4*i),s=new Uint8Array(s),t=new ArrayBuffer(b.width*b.height*4*3),t=new Float32Array(t);h>0;){if(c=a[j++],d=a[j++],e=a[j++],f=a[j++],2!=c||2!=d||128&e||b.width<8||b.width>32767)return this.RGBE_ReadPixels_NOT_RLE(a,b);if((e<<8|f)!=i)throw"HDR Bad header format, wrong scan line width";for(k=0,q=0;q<4;q++)for(o=(q+1)*i;k128){if(0==(g=c-128)||g>o-k)throw"HDR Bad Format, bad scanline data (run)";for(;g-->0;)s[k++]=d}else{if(0==(g=c)||g>o-k)throw"HDR Bad Format, bad scanline data (non-run)";if(s[k++]=d,--g>0)for(var u=0;u0;){for(g=0;g #include #include #include uniform float alphaG; uniform samplerCube inputTexture; uniform vec2 vFilteringInfo; uniform float hdrScale; varying vec3 direction; void main() { vec3 color=radiance(alphaG,inputTexture,direction,vFilteringInfo); gl_FragColor=vec4(color*hdrScale,1.0); }";X.a.ShadersStore.hdrFilteringPixelShader=a;var Oj=function(){function a(a,b){void 0===b&&(b={}),this._lodGenerationOffset=0,this._lodGenerationScale=.8,this.quality=r.a.TEXTURE_FILTERING_QUALITY_OFFLINE,this.hdrScale=1,this._engine=a,this.hdrScale=b.hdrScale||this.hdrScale,this.quality=b.hdrScale||this.quality}return a.prototype._createRenderTarget=function(a){var b=r.a.TEXTURETYPE_UNSIGNED_BYTE;this._engine.getCaps().textureHalfFloatRender?b=r.a.TEXTURETYPE_HALF_FLOAT:this._engine.getCaps().textureFloatRender&&(b=r.a.TEXTURETYPE_FLOAT);a=this._engine.createRenderTargetCubeTexture(a,{format:r.a.TEXTUREFORMAT_RGBA,type:b,createMipMaps:!0,generateMipMaps:!1,generateDepthBuffer:!1,generateStencilBuffer:!1,samplingMode:r.a.TEXTURE_NEAREST_SAMPLINGMODE});return this._engine.updateTextureWrappingMode(a.texture,r.a.TEXTURE_CLAMP_ADDRESSMODE,r.a.TEXTURE_CLAMP_ADDRESSMODE,r.a.TEXTURE_CLAMP_ADDRESSMODE),this._engine.updateTextureSamplingMode(r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,a.texture,!0),a},a.prototype._prefilterInternal=function(a){var b=a.getSize().width,c=I.a.ILog2(b)+1,d=this._effectWrapper.effect,e=this._createRenderTarget(b);this._effectRenderer.setViewport();var f=a.getInternalTexture();f&&this._engine.updateTextureSamplingMode(r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,f,!0),this._effectRenderer.applyEffectWrapper(this._effectWrapper);f=[[new g.e(0,0,-1),new g.e(0,-1,0),new g.e(1,0,0)],[new g.e(0,0,1),new g.e(0,-1,0),new g.e(-1,0,0)],[new g.e(1,0,0),new g.e(0,0,1),new g.e(0,1,0)],[new g.e(1,0,0),new g.e(0,0,-1),new g.e(0,-1,0)],[new g.e(1,0,0),new g.e(0,-1,0),new g.e(0,0,1)],[new g.e(-1,0,0),new g.e(0,-1,0),new g.e(0,0,-1)]];d.setFloat("hdrScale",this.hdrScale),d.setFloat2("vFilteringInfo",a.getSize().width,c),d.setTexture("inputTexture",a);for(var h=0;h<6;h++){d.setVector3("up",f[h][0]),d.setVector3("right",f[h][1]),d.setVector3("front",f[h][2]);for(var i=0;i255){t=255/t;k*=t,q*=t,s*=t}f[3*j+0]=k,f[3*j+1]=q,f[3*j+2]=s}g?d.push(g):f?d.push(f):d.push(i)}return d},null,this._onLoad,this._onError)},b.prototype.clone=function(){var a=new b(this.url,this.getScene()||this._getEngine(),this._size,this._noMipmap,this._generateHarmonics,this.gammaSpace);return a.level=this.level,a.wrapU=this.wrapU,a.wrapV=this.wrapV,a.coordinatesIndex=this.coordinatesIndex,a.coordinatesMode=this.coordinatesMode,a},b.prototype.delayLoad=function(){this.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=r.a.DELAYLOADSTATE_LOADED,this._texture=this._getFromCache(this.url,this._noMipmap),this._texture||this.loadTexture())},b.prototype.getReflectionTextureMatrix=function(){return this._textureMatrix},b.prototype.setReflectionTextureMatrix=function(a){var b=this;this._textureMatrix=a,a.updateFlag!==this._textureMatrix.updateFlag&&a.isIdentity()!==this._textureMatrix.isIdentity()&&(null===(a=this.getScene())||void 0===a||a.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return-1!==a.getActiveTextures().indexOf(b)}))},b.Parse=function(a,c,d){var e=null;return a.name&&!a.isRenderTarget&&((e=new b(d+a.name,c,a.size,a.noMipmap,a.generateHarmonics,a.useInGammaSpace)).name=a.name,e.hasAlpha=a.hasAlpha,e.level=a.level,e.coordinatesMode=a.coordinatesMode,e.isBlocking=a.isBlocking),e&&(a.boundingBoxPosition&&(e.boundingBoxPosition=g.e.FromArray(a.boundingBoxPosition)),a.boundingBoxSize&&(e.boundingBoxSize=g.e.FromArray(a.boundingBoxSize)),a.rotationY&&(e.rotationY=a.rotationY)),e},b.prototype.serialize=function(){if(!this.name)return null;var a={};return a.name=this.name,a.hasAlpha=this.hasAlpha,a.isCube=!0,a.level=this.level,a.size=this._size,a.coordinatesMode=this.coordinatesMode,a.useInGammaSpace=this.gammaSpace,a.generateHarmonics=this._generateHarmonics,a.customType="BABYLON.HDRCubeTexture",a.noMipmap=this._noMipmap,a.isBlocking=this._isBlocking,a.rotationY=this._rotationY,a},b._facesMapping=["right","left","up","down","front","back"],b}(Ie.a);Object(i.b)("BABYLON.HDRCubeTexture",Pj);var Qj=function(){function a(a,b,c){void 0===b&&(b=0),void 0===c&&(c=null),this.name=a,this.animations=new Array,this._positions=null,this._normals=null,this._tangents=null,this._uvs=null,this._uniqueId=0,this.onInfluenceChanged=new f.c,this._onDataLayoutChanged=new f.c,this._animationPropertiesOverride=null,this._scene=c||C.a.LastCreatedScene,this.influence=b,this._scene&&(this._uniqueId=this._scene.getUniqueId())}return Object.defineProperty(a.prototype,"influence",{get:function(){return this._influence},set:function(a){if(this._influence!==a){var b=this._influence;this._influence=a,this.onInfluenceChanged.hasObservers()&&this.onInfluenceChanged.notifyObservers(0===b||0===a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationPropertiesOverride",{get:function(){return!this._animationPropertiesOverride&&this._scene?this._scene.animationPropertiesOverride:this._animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasPositions",{get:function(){return!!this._positions},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasNormals",{get:function(){return!!this._normals},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasTangents",{get:function(){return!!this._tangents},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasUVs",{get:function(){return!!this._uvs},enumerable:!1,configurable:!0}),a.prototype.setPositions=function(a){var b=this.hasPositions;this._positions=a,b!==this.hasPositions&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getPositions=function(){return this._positions},a.prototype.setNormals=function(a){var b=this.hasNormals;this._normals=a,b!==this.hasNormals&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getNormals=function(){return this._normals},a.prototype.setTangents=function(a){var b=this.hasTangents;this._tangents=a,b!==this.hasTangents&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getTangents=function(){return this._tangents},a.prototype.setUVs=function(a){var b=this.hasUVs;this._uvs=a,b!==this.hasUVs&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getUVs=function(){return this._uvs},a.prototype.clone=function(){var b=this,c=J.a.Clone(function(){return new a(b.name,b.influence,b._scene)},this);return c._positions=this._positions,c._normals=this._normals,c._tangents=this._tangents,c._uvs=this._uvs,c},a.prototype.serialize=function(){var a={};return a.name=this.name,a.influence=this.influence,a.positions=Array.prototype.slice.call(this.getPositions()),null!=this.id&&(a.id=this.id),this.hasNormals&&(a.normals=Array.prototype.slice.call(this.getNormals())),this.hasTangents&&(a.tangents=Array.prototype.slice.call(this.getTangents())),this.hasUVs&&(a.uvs=Array.prototype.slice.call(this.getUVs())),J.a.AppendSerializedAnimations(this,a),a},a.prototype.getClassName=function(){return"MorphTarget"},a.Parse=function(b){var c=new a(b.name,b.influence);if(c.setPositions(b.positions),null!=b.id&&(c.id=b.id),b.normals&&c.setNormals(b.normals),b.tangents&&c.setTangents(b.tangents),b.uvs&&c.setUVs(b.uvs),b.animations)for(var d=0;d0}}return Object.defineProperty(a.prototype,"areUpdatesFrozen",{get:function(){return this._blockCounter>0},set:function(a){a?this._blockCounter++:(this._blockCounter--,this._blockCounter<=0&&(this._blockCounter=0,this._syncActiveTargets(!0)))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vertexCount",{get:function(){return this._vertexCount},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsNormals",{get:function(){return this._supportsNormals&&this.enableNormalMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsTangents",{get:function(){return this._supportsTangents&&this.enableTangentMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsUVs",{get:function(){return this._supportsUVs&&this.enableUVMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"numTargets",{get:function(){return this._targets.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"numInfluencers",{get:function(){return this._activeTargets.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"influences",{get:function(){return this._influences},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useTextureToStoreTargets",{get:function(){return this._useTextureToStoreTargets},set:function(a){this._useTextureToStoreTargets=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUsingTextureForTargets",{get:function(){return a.EnableTextureStorage&&this.useTextureToStoreTargets&&this._canUseTextureForTargets},enumerable:!1,configurable:!0}),a.prototype.getActiveTarget=function(a){return this._activeTargets.data[a]},a.prototype.getTarget=function(a){return this._targets[a]},a.prototype.addTarget=function(a){var b=this;this._targets.push(a),this._targetInfluenceChangedObservers.push(a.onInfluenceChanged.add(function(a){b._syncActiveTargets(a)})),this._targetDataLayoutChangedObservers.push(a._onDataLayoutChanged.add(function(){b._syncActiveTargets(!0)})),this._syncActiveTargets(!0)},a.prototype.removeTarget=function(a){var b=this._targets.indexOf(a);b>=0&&(this._targets.splice(b,1),a.onInfluenceChanged.remove(this._targetInfluenceChangedObservers.splice(b,1)[0]),a._onDataLayoutChanged.remove(this._targetDataLayoutChangedObservers.splice(b,1)[0]),this._syncActiveTargets(!0))},a.prototype._bind=function(a){a.setFloat3("morphTargetTextureInfo",this._textureVertexStride,this._textureWidth,this._textureHeight),a.setFloatArray("morphTargetTextureIndices",this._morphTargetTextureIndices),a.setTexture("morphTargets",this._targetStoreTexture)},a.prototype.clone=function(){for(var b=new a(this._scene),c=0,d=this._targets;ca&&(this._textureHeight=Math.ceil(this._textureWidth/a),this._textureWidth=a);a=!0;if(this._targetStoreTexture){var b=this._targetStoreTexture.getSize();b.width===this._textureWidth&&b.height===this._textureHeight&&this._targetStoreTexture.depth===this._targets.length&&(a=!1)}if(a){this._targetStoreTexture&&this._targetStoreTexture.dispose();for(var b=this._targets.length,a=new Float32Array(b*this._textureWidth*this._textureHeight*4),c=0,d=0;d-1&&this._parentContainer.morphTargetManagers.splice(a,1),this._parentContainer=null}},a.Parse=function(b,c){c=new a(c);c._uniqueId=b.id;for(var d=0,b=b.targets;d-1&&this._impostors.splice(b,1).length&&this.getPhysicsPlugin().removePhysicsBody(a)},a.prototype.addJoint=function(a,b,c){a={mainImpostor:a,connectedImpostor:b,joint:c};c.physicsPlugin=this._physicsPlugin,this._joints.push(a),this._physicsPlugin.generateJoint(a)},a.prototype.removeJoint=function(a,b,c){var d=this._joints.filter(function(d){return d.connectedImpostor===b&&d.joint===c&&d.mainImpostor===a});d.length&&this._physicsPlugin.removeJoint(d[0])},a.prototype._step=function(a){var b=this;this._impostors.forEach(function(a){a.isBodyInitRequired()&&b._physicsPlugin.generatePhysicsBody(a)}),a>.1?a=.1:a<=0&&(a=1/60),this._physicsPlugin.executeStep(a,this._impostors)},a.prototype.getPhysicsPlugin=function(){return this._physicsPlugin},a.prototype.getImpostors=function(){return this._impostors},a.prototype.getImpostorForPhysicsObject=function(a){for(var b=0;b0&&(this._physicsBodysToRemoveAfterStep.forEach(function(b){"function"==typeof a.world.removeBody?a.world.removeBody(b):a.world.remove(b)}),this._physicsBodysToRemoveAfterStep=[])},a.prototype.applyImpulse=function(a,b,c){c=new this.BJSCANNON.Vec3(c.x,c.y,c.z);b=new this.BJSCANNON.Vec3(b.x,b.y,b.z);a.physicsBody.applyImpulse(b,c)},a.prototype.applyForce=function(a,b,c){c=new this.BJSCANNON.Vec3(c.x,c.y,c.z);b=new this.BJSCANNON.Vec3(b.x,b.y,b.z);a.physicsBody.applyForce(b,c)},a.prototype.generatePhysicsBody=function(a){if(this._removeMarkedPhysicsBodiesFromWorld(),a.parent)a.physicsBody&&(this.removePhysicsBody(a),a.forceUpdate());else{if(a.isBodyInitRequired()){var b=this._createShape(a),c=a.physicsBody;c&&this.removePhysicsBody(a);var d=this._addMaterial("mat-"+a.uniqueId,a.getParam("friction"),a.getParam("restitution"));d={mass:a.getParam("mass"),material:d};var e=a.getParam("nativeOptions");for(var f in e)e.hasOwnProperty(f)&&(d[f]=e[f]);a.physicsBody=new this.BJSCANNON.Body(d),a.physicsBody.addEventListener("collide",a.onCollide),this.world.addEventListener("preStep",a.beforeStep),this.world.addEventListener("postStep",a.afterStep),a.physicsBody.addShape(b),"function"==typeof this.world.addBody?this.world.addBody(a.physicsBody):this.world.add(a.physicsBody),c&&["force","torque","velocity","angularVelocity"].forEach(function(b){var d=c[b];a.physicsBody[b].set(d.x,d.y,d.z)}),this._processChildMeshes(a)}this._updatePhysicsBodyTransformation(a)}},a.prototype._processChildMeshes=function(a){var b=this,c=a.object.getChildMeshes?a.object.getChildMeshes(!0):[],d=a.object.rotationQuaternion;if(d?d.conjugateToRef(this._tmpQuaternion):this._tmpQuaternion.set(0,0,0,1),c.length){var e=function(c){if(c.rotationQuaternion){var d=c.getPhysicsImpostor();if(d&&(d.parent!==a&&c.parent)){var f=c.getAbsolutePosition().subtract(c.parent.getAbsolutePosition()),g=c.rotationQuaternion.multiply(b._tmpQuaternion);d.physicsBody&&(b.removePhysicsBody(d),d.physicsBody=null),d.parent=a,d.resetUpdateFlags(),a.physicsBody.addShape(b._createShape(d),new b.BJSCANNON.Vec3(f.x,f.y,f.z),new b.BJSCANNON.Quaternion(g.x,g.y,g.z,g.w)),a.physicsBody.mass+=d.getParam("mass")}c.getChildMeshes(!0).filter(function(a){return!!a.physicsImpostor}).forEach(e)}};c.filter(function(a){return!!a.physicsImpostor}).forEach(e)}},a.prototype.removePhysicsBody=function(a){a.physicsBody.removeEventListener("collide",a.onCollide),this.world.removeEventListener("preStep",a.beforeStep),this.world.removeEventListener("postStep",a.afterStep),-1===this._physicsBodysToRemoveAfterStep.indexOf(a.physicsBody)&&this._physicsBodysToRemoveAfterStep.push(a.physicsBody)},a.prototype.generateJoint=function(a){var b=a.mainImpostor.physicsBody,c=a.connectedImpostor.physicsBody;if(b&&c){var d,e=a.joint.jointData,f={pivotA:e.mainPivot?(new this.BJSCANNON.Vec3).set(e.mainPivot.x,e.mainPivot.y,e.mainPivot.z):null,pivotB:e.connectedPivot?(new this.BJSCANNON.Vec3).set(e.connectedPivot.x,e.connectedPivot.y,e.connectedPivot.z):null,axisA:e.mainAxis?(new this.BJSCANNON.Vec3).set(e.mainAxis.x,e.mainAxis.y,e.mainAxis.z):null,axisB:e.connectedAxis?(new this.BJSCANNON.Vec3).set(e.connectedAxis.x,e.connectedAxis.y,e.connectedAxis.z):null,maxForce:e.nativeParams.maxForce,collideConnected:!!e.collision};switch(a.joint.type){case Uj.e.HingeJoint:case Uj.e.Hinge2Joint:d=new this.BJSCANNON.HingeConstraint(b,c,f);break;case Uj.e.DistanceJoint:d=new this.BJSCANNON.DistanceConstraint(b,c,e.maxDistance||2);break;case Uj.e.SpringJoint:var g=e;d=new this.BJSCANNON.Spring(b,c,{restLength:g.length,stiffness:g.stiffness,damping:g.damping,localAnchorA:f.pivotA,localAnchorB:f.pivotB});break;case Uj.e.LockJoint:d=new this.BJSCANNON.LockConstraint(b,c,f);break;case Uj.e.PointToPointJoint:case Uj.e.BallAndSocketJoint:default:d=new this.BJSCANNON.PointToPointConstraint(b,f.pivotA,c,f.pivotB,f.maxForce)}d.collideConnected=!!e.collision,a.joint.physicsJoint=d,a.joint.type!==Uj.e.SpringJoint?this.world.addConstraint(d):(a.joint.jointData.forceApplicationCallback=a.joint.jointData.forceApplicationCallback||function(){d.applyForce()},a.mainImpostor.registerAfterPhysicsStep(a.joint.jointData.forceApplicationCallback))}},a.prototype.removeJoint=function(a){a.joint.type!==Uj.e.SpringJoint?this.world.removeConstraint(a.joint.physicsJoint):a.mainImpostor.unregisterAfterPhysicsStep(a.joint.jointData.forceApplicationCallback)},a.prototype._addMaterial=function(a,b,c){var d,e;for(d=0;d1e3*c));g++);this.time+=d;for(e=this.time%c/c,f=a,g=this.bodies,d=0;d!==g.length;d++){c=g[d];c.type!==b.Body.STATIC&&c.sleepState!==b.Body.SLEEPING?(c.position.vsub(c.previousPosition,f),f.scale(e,f),c.position.vadd(f,c.interpolatedPosition)):(c.interpolatedPosition.set(c.position.x,c.position.y,c.position.z),c.interpolatedQuaternion.set(c.quaternion.x,c.quaternion.y,c.quaternion.z,c.quaternion.w))}}}},a.prototype.raycast=function(a,b){return this._cannonRaycastResult.reset(),this.world.raycastClosest(a,b,{},this._cannonRaycastResult),this._raycastResult.reset(a,b),this._cannonRaycastResult.hasHit&&(this._raycastResult.setHitData({x:this._cannonRaycastResult.hitNormalWorld.x,y:this._cannonRaycastResult.hitNormalWorld.y,z:this._cannonRaycastResult.hitNormalWorld.z},{x:this._cannonRaycastResult.hitPointWorld.x,y:this._cannonRaycastResult.hitPointWorld.y,z:this._cannonRaycastResult.hitPointWorld.z}),this._raycastResult.setHitDistance(this._cannonRaycastResult.distance)),this._raycastResult},a}();Vj.DefaultPluginFactory=function(){return new Xj};var Yj=function(){function a(a,b,c){void 0===a&&(a=!0),void 0===c&&(c=OIMO),this._useDeltaForWorldStep=a,this.name="OimoJSPlugin",this._fixedTimeStep=1/60,this._tmpImpostorsArray=[],this._tmpPositionVector=g.e.Zero(),this.BJSOIMO=c,this.world=new this.BJSOIMO.World({iterations:b}),this.world.clear(),this._raycastResult=new Wj}return a.prototype.setGravity=function(a){this.world.gravity.set(a.x,a.y,a.z)},a.prototype.setTimeStep=function(a){this.world.timeStep=a},a.prototype.getTimeStep=function(){return this.world.timeStep},a.prototype.executeStep=function(a,b){var c=this;b.forEach(function(a){a.beforeStep()}),this.world.timeStep=this._useDeltaForWorldStep?a:this._fixedTimeStep,this.world.step(),b.forEach(function(a){a.afterStep(),c._tmpImpostorsArray[a.uniqueId]=a});for(a=this.world.contacts;null!==a;)if(!a.touching||a.body1.sleeping||a.body2.sleeping){b=this._tmpImpostorsArray[+a.body1.name];var d=this._tmpImpostorsArray[+a.body2.name];b&&d?(b.onCollide({body:d.physicsBody,point:null}),d.onCollide({body:b.physicsBody,point:null}),a=a.next):a=a.next}else a=a.next},a.prototype.applyImpulse=function(a,b,c){var d=a.physicsBody.mass;a.physicsBody.applyImpulse(c.scale(this.world.invScale),b.scale(this.world.invScale*d))},a.prototype.applyForce=function(a,b,c){q.a.Warn("Oimo doesn"t support applying force. Using impule instead."),this.applyImpulse(a,b,c)},a.prototype.generatePhysicsBody=function(a){var b=this;if(a.parent)a.physicsBody&&(this.removePhysicsBody(a),a.forceUpdate());else{if(a.isBodyInitRequired()){var c={name:a.uniqueId,config:[a.getParam("mass")||.001,a.getParam("friction"),a.getParam("restitution")],size:[],type:[],pos:[],posShape:[],rot:[],rotShape:[],move:0!==a.getParam("mass"),density:a.getParam("mass"),friction:a.getParam("friction"),restitution:a.getParam("restitution"),world:this.world},d=[a];(h=a.object).getChildMeshes&&h.getChildMeshes().forEach(function(a){a.physicsImpostor&&d.push(a.physicsImpostor)});var e=function(a){return Math.max(a,Vj.Epsilon)},f=new g.b;d.forEach(function(d){if(d.object.rotationQuaternion){var g=d.object.rotationQuaternion;f.copyFrom(g),d.object.rotationQuaternion.set(0,0,0,1),d.object.computeWorldMatrix(!0);var h=f.toEulerAngles(),i=d.getObjectExtendSize();if(d===a){var j=a.getObjectCenter();a.object.getAbsolutePivotPoint().subtractToRef(j,b._tmpPositionVector),b._tmpPositionVector.divideInPlace(a.object.scaling),c.pos.push(j.x),c.pos.push(j.y),c.pos.push(j.z),c.posShape.push(0,0,0),c.rotShape.push(0,0,0)}else{j=d.object.position.clone();c.posShape.push(j.x),c.posShape.push(j.y),c.posShape.push(j.z),c.rotShape.push(57.29577951308232*h.x,57.29577951308232*h.y,57.29577951308232*h.z)}switch(d.object.rotationQuaternion.copyFrom(f),d.type){case Tj.a.ParticleImpostor:q.a.Warn("No Particle support in OIMO.js. using SphereImpostor instead");case Tj.a.SphereImpostor:j=i.x;h=i.y;var k=i.z;j=Math.max(e(j),e(h),e(k))/2;c.type.push("sphere"),c.size.push(j),c.size.push(j),c.size.push(j);break;case Tj.a.CylinderImpostor:h=e(i.x)/2;k=e(i.y);c.type.push("cylinder"),c.size.push(h),c.size.push(k),c.size.push(k);break;case Tj.a.PlaneImpostor:case Tj.a.BoxImpostor:default:h=e(i.x),k=e(i.y);j=e(i.z);c.type.push("box"),c.size.push(h),c.size.push(k),c.size.push(j)}d.object.rotationQuaternion=g}}),a.physicsBody=this.world.add(c),a.physicsBody.resetQuaternion(f),a.physicsBody.updatePosition(0)}else this._tmpPositionVector.copyFromFloats(0,0,0);var h;a.setDeltaPosition(this._tmpPositionVector)}},a.prototype.removePhysicsBody=function(a){this.world.removeRigidBody(a.physicsBody)},a.prototype.generateJoint=function(a){var b=a.mainImpostor.physicsBody,c=a.connectedImpostor.physicsBody;if(b&&c){var d,e=a.joint.jointData,f=e.nativeParams||{};b={body1:b,body2:c,axe1:f.axe1||(e.mainAxis?e.mainAxis.asArray():null),axe2:f.axe2||(e.connectedAxis?e.connectedAxis.asArray():null),pos1:f.pos1||(e.mainPivot?e.mainPivot.asArray():null),pos2:f.pos2||(e.connectedPivot?e.connectedPivot.asArray():null),min:f.min,max:f.max,collision:f.collision||e.collision,spring:f.spring,world:this.world};switch(a.joint.type){case Uj.e.BallAndSocketJoint:d="jointBall";break;case Uj.e.SpringJoint:q.a.Warn("OIMO.js doesn"t support Spring Constraint. Simulating using DistanceJoint instead");c=e;b.min=c.length||b.min,b.max=Math.max(b.min,b.max);case Uj.e.DistanceJoint:d="jointDistance",b.max=e.maxDistance;break;case Uj.e.PrismaticJoint:d="jointPrisme";break;case Uj.e.SliderJoint:d="jointSlide";break;case Uj.e.WheelJoint:d="jointWheel";break;case Uj.e.HingeJoint:default:d="jointHinge"}b.type=d,a.joint.physicsJoint=this.world.add(b)}},a.prototype.removeJoint=function(a){try{this.world.removeJoint(a.joint.physicsJoint)}catch(a){q.a.Warn(a)}},a.prototype.isSupported=function(){return void 0!==this.BJSOIMO},a.prototype.setTransformationFromPhysicsBody=function(a){if(!a.physicsBody.sleeping){if(a.physicsBody.shapes.next){for(var b=a.physicsBody.shapes;b.next;)b=b.next;a.object.position.set(b.position.x,b.position.y,b.position.z)}else{b=a.physicsBody.getPosition();a.object.position.set(b.x,b.y,b.z)}if(a.object.rotationQuaternion){b=a.physicsBody.getQuaternion();a.object.rotationQuaternion.set(b.x,b.y,b.z,b.w)}}},a.prototype.setPhysicsBodyTransformation=function(a,b,c){var d=a.physicsBody;a.physicsBody.shapes.next||(d.position.set(b.x,b.y,b.z),d.orientation.set(c.x,c.y,c.z,c.w),d.syncShapes(),d.awake())},a.prototype.setLinearVelocity=function(a,b){a.physicsBody.linearVelocity.set(b.x,b.y,b.z)},a.prototype.setAngularVelocity=function(a,b){a.physicsBody.angularVelocity.set(b.x,b.y,b.z)},a.prototype.getLinearVelocity=function(a){a=a.physicsBody.linearVelocity;return a?new g.e(a.x,a.y,a.z):null},a.prototype.getAngularVelocity=function(a){a=a.physicsBody.angularVelocity;return a?new g.e(a.x,a.y,a.z):null},a.prototype.setBodyMass=function(a,b){var c=0===b;a.physicsBody.shapes.density=c?1:b,a.physicsBody.setupMass(c?2:1)},a.prototype.getBodyMass=function(a){return a.physicsBody.shapes.density},a.prototype.getBodyFriction=function(a){return a.physicsBody.shapes.friction},a.prototype.setBodyFriction=function(a,b){a.physicsBody.shapes.friction=b},a.prototype.getBodyRestitution=function(a){return a.physicsBody.shapes.restitution},a.prototype.setBodyRestitution=function(a,b){a.physicsBody.shapes.restitution=b},a.prototype.sleepBody=function(a){a.physicsBody.sleep()},a.prototype.wakeUpBody=function(a){a.physicsBody.awake()},a.prototype.updateDistanceJoint=function(a,b,c){a.physicsJoint.limitMotor.upperLimit=b,void 0!==c&&(a.physicsJoint.limitMotor.lowerLimit=c)},a.prototype.setMotor=function(a,b,c,d){void 0!==c?q.a.Warn("OimoJS plugin currently has unexpected behavior when using setMotor with force parameter"):c=1e6,b*=-1;d=d?a.physicsJoint.rotationalLimitMotor2:a.physicsJoint.rotationalLimitMotor1||a.physicsJoint.rotationalLimitMotor||a.physicsJoint.limitMotor;d&&d.setMotor(b,c)},a.prototype.setLimit=function(a,b,c,d){d=d?a.physicsJoint.rotationalLimitMotor2:a.physicsJoint.rotationalLimitMotor1||a.physicsJoint.rotationalLimitMotor||a.physicsJoint.limitMotor;d&&d.setLimit(b,void 0===c?-b:c)},a.prototype.syncMeshWithImpostor=function(a,b){b=b.physicsBody;a.position.x=b.position.x,a.position.y=b.position.y,a.position.z=b.position.z,a.rotationQuaternion&&(a.rotationQuaternion.x=b.orientation.x,a.rotationQuaternion.y=b.orientation.y,a.rotationQuaternion.z=b.orientation.z,a.rotationQuaternion.w=b.orientation.s)},a.prototype.getRadius=function(a){return a.physicsBody.shapes.radius},a.prototype.getBoxSizeToRef=function(a,b){a=a.physicsBody.shapes;b.x=2*a.halfWidth,b.y=2*a.halfHeight,b.z=2*a.halfDepth},a.prototype.dispose=function(){this.world.clear()},a.prototype.raycast=function(a,b){return q.a.Warn("raycast is not currently supported by the Oimo physics plugin"),this._raycastResult.reset(a,b),this._raycastResult},a}(),Zj=c(105),$j=function(){function a(a,b,c){var d=this;void 0===a&&(a=!0),void 0===b&&(b=Ammo),void 0===c&&(c=null),this._useDeltaForWorldStep=a,this.bjsAMMO={},this.name="AmmoJSPlugin",this._timeStep=1/60,this._fixedTimeStep=1/60,this._maxSteps=5,this._tmpQuaternion=new g.b,this._tmpContactCallbackResult=!1,this._tmpContactPoint=new g.e,this._tmpMatrix=new g.a,"function"!=typeof b?(this.bjsAMMO=b,this.isSupported()?(this._collisionConfiguration=new this.bjsAMMO.btSoftBodyRigidBodyCollisionConfiguration,this._dispatcher=new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration),this._overlappingPairCache=c||new this.bjsAMMO.btDbvtBroadphase,this._solver=new this.bjsAMMO.btSequentialImpulseConstraintSolver,this._softBodySolver=new this.bjsAMMO.btDefaultSoftBodySolver,this.world=new this.bjsAMMO.btSoftRigidDynamicsWorld(this._dispatcher,this._overlappingPairCache,this._solver,this._collisionConfiguration,this._softBodySolver),this._tmpAmmoConcreteContactResultCallback=new this.bjsAMMO.ConcreteContactResultCallback,this._tmpAmmoConcreteContactResultCallback.addSingleResult=function(a,b,c,e){b=d.bjsAMMO.wrapPointer(a,d.bjsAMMO.btManifoldPoint).getPositionWorldOnA();d._tmpContactPoint.x=b.x(),d._tmpContactPoint.y=b.y(),d._tmpContactPoint.z=b.z(),d._tmpContactCallbackResult=!0},this._raycastResult=new Wj,this._tmpAmmoTransform=new this.bjsAMMO.btTransform,this._tmpAmmoTransform.setIdentity(),this._tmpAmmoQuaternion=new this.bjsAMMO.btQuaternion(0,0,0,1),this._tmpAmmoVectorA=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorB=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorC=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorD=new this.bjsAMMO.btVector3(0,0,0)):q.a.Error("AmmoJS is not available. Please make sure you included the js file.")):q.a.Error("AmmoJS is not ready. Please make sure you await Ammo() before using the plugin.")}return a.prototype.setGravity=function(a){this._tmpAmmoVectorA.setValue(a.x,a.y,a.z),this.world.setGravity(this._tmpAmmoVectorA),this.world.getWorldInfo().set_m_gravity(this._tmpAmmoVectorA)},a.prototype.setTimeStep=function(a){this._timeStep=a},a.prototype.setFixedTimeStep=function(a){this._fixedTimeStep=a},a.prototype.setMaxSteps=function(a){this._maxSteps=a},a.prototype.getTimeStep=function(){return this._timeStep},a.prototype._isImpostorInContact=function(a){return this._tmpContactCallbackResult=!1,this.world.contactTest(a.physicsBody,this._tmpAmmoConcreteContactResultCallback),this._tmpContactCallbackResult},a.prototype._isImpostorPairInContact=function(a,b){return this._tmpContactCallbackResult=!1,this.world.contactPairTest(a.physicsBody,b.physicsBody,this._tmpAmmoConcreteContactResultCallback),this._tmpContactCallbackResult},a.prototype._stepSimulation=function(a,b,c){if(void 0===a&&(a=1/60),void 0===b&&(b=10),void 0===c&&(c=1/60),0==b)this.world.stepSimulation(a,0);else for(;b>0&&a>0;)a-c0&&this._isImpostorInContact(d))for(a=0,b=d._onPhysicsCollideCallbacks;a3?3:c;b=(new this.bjsAMMO.btSoftBodyHelpers).CreateRope(this.world.getWorldInfo(),this._tmpAmmoVectorA,this._tmpAmmoVectorB,d-1,c);return b.get_m_cfg().set_collisions(17),b},a.prototype._createCustom=function(a){var b=null;return this.onCreateCustomShape&&(b=this.onCreateCustomShape(a)),null==b&&(b=new this.bjsAMMO.btCompoundShape),b},a.prototype._addHullVerts=function(a,b,c){var d=this,e=0;if(c&&c.getIndices&&c.getWorldMatrix&&c.getChildMeshes){var f=c.getIndices();f||(f=[]);var h=c.getVerticesData(W.b.PositionKind);h||(h=[]),c.computeWorldMatrix(!1);for(var i=f.length/3,j=0;j0){if(a.type!=Tj.a.NoImpostor){b=this._createShape(a,!0);b&&(this._tmpAmmoTransform.getOrigin().setValue(0,0,0),this._tmpAmmoQuaternion.setValue(0,0,0,1),this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion),d.addChildShape(this._tmpAmmoTransform,b))}return d}this.bjsAMMO.destroy(d),d=null}switch(a.type){case Tj.a.SphereImpostor:if(I.a.WithinEpsilon(f.x,f.y,1e-4)&&I.a.WithinEpsilon(f.x,f.z,1e-4))d=new this.bjsAMMO.btSphereShape(f.x/2);else{b=[new this.bjsAMMO.btVector3(0,0,0)];(d=new this.bjsAMMO.btMultiSphereShape(b,[1],1)).setLocalScaling(new this.bjsAMMO.btVector3(f.x/2,f.y/2,f.z/2))}break;case Tj.a.CapsuleImpostor:b=f.x/2;d=new this.bjsAMMO.btCapsuleShape(b,f.y-2*b);break;case Tj.a.CylinderImpostor:this._tmpAmmoVectorA.setValue(f.x/2,f.y/2,f.z/2),d=new this.bjsAMMO.btCylinderShape(this._tmpAmmoVectorA);break;case Tj.a.PlaneImpostor:case Tj.a.BoxImpostor:this._tmpAmmoVectorA.setValue(f.x/2,f.y/2,f.z/2),d=new this.bjsAMMO.btBoxShape(this._tmpAmmoVectorA);break;case Tj.a.MeshImpostor:if(0==a.getParam("mass")){b=new this.bjsAMMO.btTriangleMesh;a._pluginData.toDispose.push(b);var i=this._addMeshVerts(b,e,e);d=0==i?new this.bjsAMMO.btCompoundShape:new this.bjsAMMO.btBvhTriangleMeshShape(b);break}case Tj.a.ConvexHullImpostor:i=new this.bjsAMMO.btConvexHullShape;0==this._addHullVerts(i,e,e)?(a._pluginData.toDispose.push(i),d=new this.bjsAMMO.btCompoundShape):d=i;break;case Tj.a.NoImpostor:d=new this.bjsAMMO.btSphereShape(f.x/2);break;case Tj.a.CustomImpostor:d=this._createCustom(a);break;case Tj.a.SoftbodyImpostor:d=this._createSoftbody(a);break;case Tj.a.ClothImpostor:d=this._createCloth(a);break;case Tj.a.RopeImpostor:d=this._createRope(a);break;default:q.a.Warn("The impostor type is not currently supported by the ammo plugin.")}return d},a.prototype.setTransformationFromPhysicsBody=function(a){a.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform),a.object.position.set(this._tmpAmmoTransform.getOrigin().x(),this._tmpAmmoTransform.getOrigin().y(),this._tmpAmmoTransform.getOrigin().z()),a.object.rotationQuaternion?a.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(),this._tmpAmmoTransform.getRotation().y(),this._tmpAmmoTransform.getRotation().z(),this._tmpAmmoTransform.getRotation().w()):a.object.rotation&&(this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(),this._tmpAmmoTransform.getRotation().y(),this._tmpAmmoTransform.getRotation().z(),this._tmpAmmoTransform.getRotation().w()),this._tmpQuaternion.toEulerAnglesToRef(a.object.rotation))},a.prototype.setPhysicsBodyTransformation=function(a,b,c){var d=a.physicsBody.getWorldTransform();if(Math.abs(d.getOrigin().x()-b.x)>hb.a||Math.abs(d.getOrigin().y()-b.y)>hb.a||Math.abs(d.getOrigin().z()-b.z)>hb.a||Math.abs(d.getRotation().x()-c.x)>hb.a||Math.abs(d.getRotation().y()-c.y)>hb.a||Math.abs(d.getRotation().z()-c.z)>hb.a||Math.abs(d.getRotation().w()-c.w)>hb.a)if(this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),d.setOrigin(this._tmpAmmoVectorA),this._tmpAmmoQuaternion.setValue(c.x,c.y,c.z,c.w),d.setRotation(this._tmpAmmoQuaternion),a.physicsBody.setWorldTransform(d),0==a.mass){b=a.physicsBody.getMotionState();b&&b.setWorldTransform(d)}else a.physicsBody.activate()},a.prototype.isSupported=function(){return void 0!==this.bjsAMMO},a.prototype.setLinearVelocity=function(a,b){this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),a.soft?a.physicsBody.linearVelocity(this._tmpAmmoVectorA):a.physicsBody.setLinearVelocity(this._tmpAmmoVectorA)},a.prototype.setAngularVelocity=function(a,b){this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),a.soft?a.physicsBody.angularVelocity(this._tmpAmmoVectorA):a.physicsBody.setAngularVelocity(this._tmpAmmoVectorA)},a.prototype.getLinearVelocity=function(a){if(a.soft)var b=a.physicsBody.linearVelocity();else b=a.physicsBody.getLinearVelocity();if(!b)return null;a=new g.e(b.x(),b.y(),b.z());return this.bjsAMMO.destroy(b),a},a.prototype.getAngularVelocity=function(a){if(a.soft)var b=a.physicsBody.angularVelocity();else b=a.physicsBody.getAngularVelocity();if(!b)return null;a=new g.e(b.x(),b.y(),b.z());return this.bjsAMMO.destroy(b),a},a.prototype.setBodyMass=function(a,b){a.soft?a.physicsBody.setTotalMass(b,!1):a.physicsBody.setMassProps(b),a._pluginData.mass=b},a.prototype.getBodyMass=function(a){return a._pluginData.mass||0},a.prototype.getBodyFriction=function(a){return a._pluginData.friction||0},a.prototype.setBodyFriction=function(a,b){a.soft?a.physicsBody.get_m_cfg().set_kDF(b):a.physicsBody.setFriction(b),a._pluginData.friction=b},a.prototype.getBodyRestitution=function(a){return a._pluginData.restitution||0},a.prototype.setBodyRestitution=function(a,b){a.physicsBody.setRestitution(b),a._pluginData.restitution=b},a.prototype.getBodyPressure=function(a){return a.soft?a._pluginData.pressure||0:(q.a.Warn("Pressure is not a property of a rigid body"),0)},a.prototype.setBodyPressure=function(a,b){a.soft?a.type===Tj.a.SoftbodyImpostor?(a.physicsBody.get_m_cfg().set_kPR(b),a._pluginData.pressure=b):(a.physicsBody.get_m_cfg().set_kPR(0),a._pluginData.pressure=0):q.a.Warn("Pressure can only be applied to a softbody")},a.prototype.getBodyStiffness=function(a){return a.soft?a._pluginData.stiffness||0:(q.a.Warn("Stiffness is not a property of a rigid body"),0)},a.prototype.setBodyStiffness=function(a,b){a.soft?(b=(b=b<0?0:b)>1?1:b,a.physicsBody.get_m_materials().at(0).set_m_kLST(b),a._pluginData.stiffness=b):q.a.Warn("Stiffness cannot be applied to a rigid body")},a.prototype.getBodyVelocityIterations=function(a){return a.soft?a._pluginData.velocityIterations||0:(q.a.Warn("Velocity iterations is not a property of a rigid body"),0)},a.prototype.setBodyVelocityIterations=function(a,b){a.soft?(b=b<0?0:b,a.physicsBody.get_m_cfg().set_viterations(b),a._pluginData.velocityIterations=b):q.a.Warn("Velocity iterations cannot be applied to a rigid body")},a.prototype.getBodyPositionIterations=function(a){return a.soft?a._pluginData.positionIterations||0:(q.a.Warn("Position iterations is not a property of a rigid body"),0)},a.prototype.setBodyPositionIterations=function(a,b){a.soft?(b=b<0?0:b,a.physicsBody.get_m_cfg().set_piterations(b),a._pluginData.positionIterations=b):q.a.Warn("Position iterations cannot be applied to a rigid body")},a.prototype.appendAnchor=function(a,b,c,d,e,f){void 0===e&&(e=1),void 0===f&&(f=!1);var g=a.segments;c=Math.round((g-1)*c)+g*(g-1-Math.round((g-1)*d));a.physicsBody.appendAnchor(c,b.physicsBody,f,e)},a.prototype.appendHook=function(a,b,c,d,e){void 0===d&&(d=1),void 0===e&&(e=!1);c=Math.round(a.segments*c);a.physicsBody.appendAnchor(c,b.physicsBody,e,d)},a.prototype.sleepBody=function(a){a.physicsBody.forceActivationState(0)},a.prototype.wakeUpBody=function(a){a.physicsBody.activate()},a.prototype.updateDistanceJoint=function(a,b,c){q.a.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin")},a.prototype.setMotor=function(a,b,c,d){a.physicsJoint.enableAngularMotor(!0,b,c)},a.prototype.setLimit=function(a,b,c){q.a.Warn("setLimit is not currently supported by the Ammo physics plugin")},a.prototype.syncMeshWithImpostor=function(a,b){b.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform),a.position.x=this._tmpAmmoTransform.getOrigin().x(),a.position.y=this._tmpAmmoTransform.getOrigin().y(),a.position.z=this._tmpAmmoTransform.getOrigin().z(),a.rotationQuaternion&&(a.rotationQuaternion.x=this._tmpAmmoTransform.getRotation().x(),a.rotationQuaternion.y=this._tmpAmmoTransform.getRotation().y(),a.rotationQuaternion.z=this._tmpAmmoTransform.getRotation().z(),a.rotationQuaternion.w=this._tmpAmmoTransform.getRotation().w())},a.prototype.getRadius=function(a){return a.getObjectExtendSize().x/2},a.prototype.getBoxSizeToRef=function(a,b){a=a.getObjectExtendSize();b.x=a.x,b.y=a.y,b.z=a.z},a.prototype.dispose=function(){this.bjsAMMO.destroy(this.world),this.bjsAMMO.destroy(this._solver),this.bjsAMMO.destroy(this._overlappingPairCache),this.bjsAMMO.destroy(this._dispatcher),this.bjsAMMO.destroy(this._collisionConfiguration),this.bjsAMMO.destroy(this._tmpAmmoVectorA),this.bjsAMMO.destroy(this._tmpAmmoVectorB),this.bjsAMMO.destroy(this._tmpAmmoVectorC),this.bjsAMMO.destroy(this._tmpAmmoTransform),this.bjsAMMO.destroy(this._tmpAmmoQuaternion),this.bjsAMMO.destroy(this._tmpAmmoConcreteContactResultCallback),this.world=null},a.prototype.raycast=function(a,b){this._tmpAmmoVectorRCA=new this.bjsAMMO.btVector3(a.x,a.y,a.z),this._tmpAmmoVectorRCB=new this.bjsAMMO.btVector3(b.x,b.y,b.z);var c=new this.bjsAMMO.ClosestRayResultCallback(this._tmpAmmoVectorRCA,this._tmpAmmoVectorRCB);return this.world.rayTest(this._tmpAmmoVectorRCA,this._tmpAmmoVectorRCB,c),this._raycastResult.reset(a,b),c.hasHit()&&(this._raycastResult.setHitData({x:c.get_m_hitNormalWorld().x(),y:c.get_m_hitNormalWorld().y(),z:c.get_m_hitNormalWorld().z()},{x:c.get_m_hitPointWorld().x(),y:c.get_m_hitPointWorld().y(),z:c.get_m_hitPointWorld().z()}),this._raycastResult.calculateHitDistance()),this.bjsAMMO.destroy(c),this.bjsAMMO.destroy(this._tmpAmmoVectorRCA),this.bjsAMMO.destroy(this._tmpAmmoVectorRCB),this._raycastResult},a.DISABLE_COLLISION_FLAG=4,a.KINEMATIC_FLAG=2,a.DISABLE_DEACTIVATION_FLAG=4,a}();d.a.prototype.removeReflectionProbe=function(a){if(!this.reflectionProbes)return-1;a=this.reflectionProbes.indexOf(a);return-1!==a&&this.reflectionProbes.splice(a,1),a},d.a.prototype.addReflectionProbe=function(a){this.reflectionProbes||(this.reflectionProbes=[]),this.reflectionProbes.push(a)};var ak=function(){function a(a,b,c,d,e,f){var h=this;void 0===d&&(d=!0),void 0===e&&(e=!1),void 0===f&&(f=!1),this.name=a,this._viewMatrix=g.a.Identity(),this._target=g.e.Zero(),this._add=g.e.Zero(),this._invertYAxis=!1,this.position=g.e.Zero(),this._parentContainer=null,this._scene=c,this._scene.reflectionProbes||(this._scene.reflectionProbes=new Array),this._scene.reflectionProbes.push(this);var i=r.a.TEXTURETYPE_UNSIGNED_BYTE;if(e){e=this._scene.getEngine().getCaps();e.textureHalfFloatRender?i=r.a.TEXTURETYPE_HALF_FLOAT:e.textureFloatRender&&(i=r.a.TEXTURETYPE_FLOAT)}this._renderTargetTexture=new cd(a,b,c,d,!0,i,!0),this._renderTargetTexture.gammaSpace=!f;var j,k=c.getEngine().useReverseDepthBuffer;this._renderTargetTexture.onBeforeRenderObservable.add(function(a){switch(a){case 0:h._add.copyFromFloats(1,0,0);break;case 1:h._add.copyFromFloats(-1,0,0);break;case 2:h._add.copyFromFloats(0,h._invertYAxis?1:-1,0);break;case 3:h._add.copyFromFloats(0,h._invertYAxis?-1:1,0);break;case 4:h._add.copyFromFloats(0,0,c.useRightHandedSystem?-1:1);break;case 5:h._add.copyFromFloats(0,0,c.useRightHandedSystem?1:-1)}h._attachedMesh&&h.position.copyFrom(h._attachedMesh.getAbsolutePosition()),h.position.addToRef(h._add,h._target);a=c.useRightHandedSystem?g.a.LookAtRHToRef:g.a.LookAtLHToRef;var b=c.useRightHandedSystem?g.a.PerspectiveFovRH:g.a.PerspectiveFovLH;a(h.position,h._target,g.e.Up(),h._viewMatrix),c.activeCamera&&(h._projectionMatrix=b(Math.PI/2,1,k?c.activeCamera.maxZ:c.activeCamera.minZ,k?c.activeCamera.minZ:c.activeCamera.maxZ,h._scene.getEngine().isNDCHalfZRange),c.setTransformMatrix(h._viewMatrix,h._projectionMatrix),c.activeCamera.isRigCamera&&!h._renderTargetTexture.activeCamera&&(h._renderTargetTexture.activeCamera=c.activeCamera.rigParent||null)),c._forcedViewPosition=h.position}),this._renderTargetTexture.onBeforeBindObservable.add(function(){var b,d;null===(d=(b=c.getEngine())._debugPushGroup)||void 0===d||d.call(b,"reflection probe generation for "+a,1),j=h._scene.imageProcessingConfiguration.applyByPostProcess,f&&(c.imageProcessingConfiguration.applyByPostProcess=!0)}),this._renderTargetTexture.onAfterUnbindObservable.add(function(){var a,b;c.imageProcessingConfiguration.applyByPostProcess=j,c._forcedViewPosition=null,c.updateTransformMatrix(!0),null===(b=(a=c.getEngine())._debugPopGroup)||void 0===b||b.call(a,1)})}return Object.defineProperty(a.prototype,"samples",{get:function(){return this._renderTargetTexture.samples},set:function(a){this._renderTargetTexture.samples=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"refreshRate",{get:function(){return this._renderTargetTexture.refreshRate},set:function(a){this._renderTargetTexture.refreshRate=a},enumerable:!1,configurable:!0}),a.prototype.getScene=function(){return this._scene},Object.defineProperty(a.prototype,"cubeTexture",{get:function(){return this._renderTargetTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderList",{get:function(){return this._renderTargetTexture.renderList},enumerable:!1,configurable:!0}),a.prototype.attachToMesh=function(a){this._attachedMesh=a},a.prototype.setRenderingAutoClearDepthStencil=function(a,b){this._renderTargetTexture.setRenderingAutoClearDepthStencil(a,b)},a.prototype.dispose=function(){var a=this._scene.reflectionProbes.indexOf(this);if(-1!==a&&this._scene.reflectionProbes.splice(a,1),this._parentContainer){a=this._parentContainer.reflectionProbes.indexOf(this);a>-1&&this._parentContainer.reflectionProbes.splice(a,1),this._parentContainer=null}this._renderTargetTexture&&(this._renderTargetTexture.dispose(),this._renderTargetTexture=null)},a.prototype.toString=function(a){var b="Name: "+this.name;return a&&(b+=", position: "+this.position.toString(),this._attachedMesh&&(b+=", attached mesh: "+this._attachedMesh.name)),b},a.prototype.getClassName=function(){return"ReflectionProbe"},a.prototype.serialize=function(){var a=J.a.Serialize(this,this._renderTargetTexture.serialize());return a.isReflectionProbe=!0,a},a.Parse=function(b,c,d){var e=null;if(c.reflectionProbes)for(var f=0;f0){var d=b._waitingData.lods.ids,e=c.isEnabled(!1);if(b._waitingData.lods.distances){var f=b._waitingData.lods.distances;if(f.length>=d.length){var g=f.length>d.length?f[f.length-1]:0;c.setEnabled(!1);for(var h=0;h0&&c.addLODLevel(g,null),!0===e&&c.setEnabled(!0)}else U.b.Warn("Invalid level of detail distances for "+b.name)}}b._waitingData.lods=null}},hk=function(a,b,c,e,f){void 0===f&&(f=!1);var g=new Ga(a),h="importScene has failed JSON parse";try{var j=JSON.parse(b);h="";b=hh.loggingLevel===hh.DETAILED_LOGGING;if(void 0!==j.environmentTexture&&null!==j.environmentTexture){var k=void 0===j.isPBR||j.isPBR;if(j.environmentTextureType&&"BABYLON.HDRCubeTexture"===j.environmentTextureType){var l=j.environmentTextureSize?j.environmentTextureSize:128;l=new Pj((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a,l,!0,!k);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}else if(Object(gh.e)(j.environmentTexture,".env")){l=new Xh((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}else{l=Xh.CreateFromPrefilteredData((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}if(!0===j.createDefaultSkybox){l=void 0!==a.activeCamera&&null!==a.activeCamera?(a.activeCamera.maxZ-a.activeCamera.minZ)/2:1e3;var m=j.skyboxBlurLevel||0;a.createDefaultSkybox(a.environmentTexture,k,l,m)}g.environmentTexture=a.environmentTexture}if(void 0!==j.environmentIntensity&&null!==j.environmentIntensity&&(a.environmentIntensity=j.environmentIntensity),void 0!==j.lights&&null!==j.lights)for(k=0,l=j.lights.length;k0){for(m=0;m0){for(p=0;p-1&&void 0!==k.skeletons&&null!==k.skeletons&&!1===D.indexOf(F.skeletonId)>-1)for(aa=0,K=k.skeletons.length;aa0&&(w+1)%4==0)g[w]=255;else{x=h[w];g[w]=x/v*255}a.is3D?(a.updateSize(i,i,i),d.updateRawTexture3D(a,g,r.a.TEXTUREFORMAT_RGBA,!1)):(a.updateSize(i*i,i),d.updateRawTexture(a,g,r.a.TEXTUREFORMAT_RGBA,!1)),a.isReady=!0,c._triggerOnLoad()}},f=this.getScene();return f?f._loadFile(this.url,e):d._loadFile(this.url,e),this._texture},b.prototype.loadTexture=function(){this.url&&this.url.toLocaleLowerCase().indexOf(".3dl")==this.url.length-4&&this.load3dlTexture()},b.prototype.clone=function(){var a=new b(this.url,this.getScene()||this._getEngine());return a.level=this.level,a},b.prototype.delayLoad=function(){this.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=r.a.DELAYLOADSTATE_LOADED,this._texture=this._getFromCache(this.url,!0),this._texture||this.loadTexture())},b.Parse=function(a,c){var d=null;return a.name&&!a.isRenderTarget&&((d=new b(a.name,c)).name=a.name,d.level=a.level),d},b.prototype.serialize=function(){if(!this.name)return null;var a={};return a.name=this.name,a.level=this.level,a.customType="BABYLON.ColorGradingTexture",a},b._noneEmptyLineRegex=/S+/,b}(Ie.a);Object(i.b)("BABYLON.ColorGradingTexture",pk);var qk=function(a){function b(b,c,d,e,f,g,h){void 0===e&&(e=!1),void 0===f&&(f=!0),void 0===g&&(g=null),void 0===h&&(h=null);var i=a.call(this,c)||this;if(i._onLoad=null,i._onError=null,!b)throw new Error("Image url is not set");return i._coordinatesMode=V.a.CUBIC_MODE,i.name=b,i.url=b,i._size=d,i._noMipmap=e,i.gammaSpace=f,i._onLoad=g,i._onError=h,i.hasAlpha=!1,i.isCube=!0,i._texture=i._getFromCache(b,i._noMipmap),i._texture?g&&(i._texture.isReady?U.b.SetImmediate(function(){return g()}):i._texture.onLoadedObservable.add(g)):c.useDelayedTextureLoading?i.delayLoadState=r.a.DELAYLOADSTATE_NOTLOADED:i.loadImage(i.loadTexture.bind(i),i._onError),i}return Object(l.d)(b,a),b.prototype.loadImage=function(a,b){var c=this,d=document.createElement("canvas");Object(ue.i)(this.url,function(b){c._width=b.width,c._height=b.height,d.width=c._width,d.height=c._height;var e=d.getContext("2d");e.drawImage(b,0,0);e=e.getImageData(0,0,b.width,b.height);c._buffer=e.data.buffer,d.remove(),a()},function(a,d){b&&b(c.getClassName()+" could not be loaded",d)},null)},b.prototype.loadTexture=function(){var a=this,c=this.getScene();c&&(this._texture=c.getEngine().createRawCubeTextureFromUrl(this.url,c,this._size,r.a.TEXTUREFORMAT_RGB,c.getEngine().getCaps().textureFloat?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_UNSIGNED_INTEGER,this._noMipmap,function(){for(var c=a.getFloat32ArrayFromArrayBuffer(a._buffer),c=Kj.ConvertPanoramaToCubemap(c,a._width,a._height,a._size),d=[],e=0;e<6;e++){var f=c[b._FacesMapping[e]];d.push(f)}return d},null,this._onLoad,this._onError))},b.prototype.getFloat32ArrayFromArrayBuffer=function(a){for(var b=new DataView(a),c=new Float32Array(3*a.byteLength/4),d=0,e=0;eb.length)q.a.Error("Unable to load TGA file - Not enough data");else{c+=d.id_length;var e=!1,f=!1,g=!1;switch(d.image_type){case 9:e=!0;case 1:f=!0;break;case 10:e=!0;case 2:break;case 11:e=!0;case 3:g=!0}var h,i,j,k,u,x,y,z=d.pixel_size>>3,A=d.width*d.height*z;if(f&&(h=b.subarray(c,c+=d.colormap_length*(d.colormap_size>>3))),e){var B,C;e=new Uint8Array(A);for(var D=0,E=new Uint8Array(z);c>4){default:case 2:i=0,k=1,y=d.width,j=0,u=1,x=d.height;break;case 0:i=0,k=1,y=d.width,j=d.height-1,u=-1,x=-1;break;case 3:i=d.width-1,k=-1,y=-1,j=0,u=1,x=d.height;break;case 1:i=d.width-1,k=-1,y=-1,j=d.height-1,u=-1,x=-1}B="_getImageData"+(g?"Grey":"")+d.pixel_size+"bits";C=uk[B](d,h,e,j,u,x,i,k,y);a.getEngine()._uploadDataToTextureDirectly(a,C)}}}var uk={GetTGAHeader:sk,UploadContent:tk,_getImageData8bits:function(a,b,c,d,e,f,g,h,i){var j,k;c=c;b=b;var l=a.width;a=a.height;var m=0;a=new Uint8Array(l*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,m++)j=c[m],a[4*(k+l*d)+3]=255,a[4*(k+l*d)+2]=b[3*j+0],a[4*(k+l*d)+1]=b[3*j+1],a[4*(k+l*d)+0]=b[3*j+2];return a},_getImageData16bits:function(a,b,c,d,e,f,g,h,i){var j,k;b=c;c=a.width;a=a.height;var l=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,l+=2){var m=255*((31744&(j=b[l+0]+(b[l+1]<<8)))>>10)/31|0,n=255*((992&j)>>5)/31|0,o=255*(31&j)/31|0;a[4*(k+c*d)+0]=m,a[4*(k+c*d)+1]=n,a[4*(k+c*d)+2]=o,a[4*(k+c*d)+3]=32768&j?0:255}return a},_getImageData24bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=3)a[4*(j+c*d)+3]=255,a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+1]=b[k+1],a[4*(j+c*d)+0]=b[k+2];return a},_getImageData32bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=4)a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+1]=b[k+1],a[4*(j+c*d)+0]=b[k+2],a[4*(j+c*d)+3]=b[k+3];return a},_getImageDataGrey8bits:function(a,b,c,d,e,f,g,h,i){var j,k;b=c;c=a.width;a=a.height;var l=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,l++)j=b[l],a[4*(k+c*d)+0]=j,a[4*(k+c*d)+1]=j,a[4*(k+c*d)+2]=j,a[4*(k+c*d)+3]=255;return a},_getImageDataGrey16bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=2)a[4*(j+c*d)+0]=b[k+0],a[4*(j+c*d)+1]=b[k+0],a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+3]=b[k+1];return a}},vk=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".tga")},a.prototype.loadCubeData=function(a,b,c,d,e){throw".env not supported in Cube."},a.prototype.loadData=function(a,b,c){var d=new Uint8Array(a.buffer,a.byteOffset,a.byteLength);a=sk(d);c(a.width,a.height,b.generateMipMaps,!1,function(){tk(b,d)})},a}();T.a._TextureLoaders.push(new vk);var wk=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".hdr")},a.prototype.loadCubeData=function(a,b,c,d,e){throw".env not supported in Cube."},a.prototype.loadData=function(a,b,c){for(var a=new Uint8Array(a.buffer,a.byteOffset,a.byteLength),d=Lj.RGBE_ReadHeader(a),a=Lj.RGBE_ReadPixels(a,d),e=d.width*d.height,f=new Float32Array(4*e),g=0;g>2&3],f[p++]=e[o>>4&3],f[p++]=e[o>>6&3]}}return f}(f,0,a.getImageWidth(b,c)+3&-4,a.getImageHeight(b,c)+3&-4));return f}onmessage=function(g){if("init"===g.data.action){if(!e){Module={wasmBinary:g.data.wasmBinary};try{importScripts(g.data.url)}catch(a){postMessage({action:"error",error:a})}e=new Promise(function(a){Module.onRuntimeInitialized=function(){Module.initializeBasis(),a()}})}e.then(function(){postMessage({action:"init"})})}else if("transcode"===g.data.action){var h=g.data.config,i=g.data.imageData;i=new Module.BasisFile(i);var j=function(a){for(var b=a.getHasAlpha(),c=a.getNumImages(),d=[],e=0;e1&&b.generateMipMaps;Fk(b,a),b.getEngine()._setCubeMapTextureParams(b,c),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()})["catch"](function(a){U.b.Warn("Failed to transcode Basis file, transcoding may not be supported on this device"),b.isReady=!0,e&&e(a)})}},a.prototype.loadData=function(a,b,c){var d=b.getEngine().getCaps();d={supportedCompressionFormats:{etc1:!!d.etc1,s3tc:!!d.s3tc,pvrtc:!!d.pvrtc,etc2:!!d.etc2}};Ek(a,d).then(function(a){var d=a.fileInfo.images[0].levels[0],e=a.fileInfo.images[0].levels.length>1&&b.generateMipMaps;c(d.width,d.height,e,-1!==a.format,function(){Fk(b,a)})})["catch"](function(a){U.b.Warn("Failed to transcode Basis file, transcoding may not be supported on this device"),c(0,0,!1,!1,function(){},!0)})},a}();T.a._TextureLoaders.push(new Ik);var Jk=function(a){function b(b,c,d,e,f,g){var h=this,i=!(!f||!f.generateMipMaps)&&f.generateMipMaps,j=!(!f||!f.generateDepthTexture)&&f.generateDepthTexture,k=f&&f.depthTextureFormat?f.depthTextureFormat:r.a.TEXTUREFORMAT_DEPTH16,n=!f||void 0===f.doNotChangeAspectRatio||f.doNotChangeAspectRatio,o=!(!f||!f.drawOnlyOnFirstAttachmentByDefault)&&f.drawOnlyOnFirstAttachmentByDefault;if((h=a.call(this,b,c,e,i,n,void 0,void 0,void 0,void 0,void 0,void 0,void 0,!0)||this).isSupported){b=[];e=[];h._initTypes(d,b,e,f);n=!f||void 0===f.generateDepthBuffer||f.generateDepthBuffer;f=!(!f||void 0===f.generateStencilBuffer)&&f.generateStencilBuffer;return h._size=c,h._multiRenderTargetOptions={samplingModes:e,generateMipMaps:i,generateDepthBuffer:n,generateStencilBuffer:f,generateDepthTexture:j,depthTextureFormat:k,types:b,textureCount:d},h._count=d,h._drawOnlyOnFirstAttachmentByDefault=o,d>0&&(h._createInternalTextures(),h._createTextures(g)),h}h.dispose()}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"isSupported",{get:function(){var a;return null!==(a=null===(a=this._engine)||void 0===a?void 0:a.getCaps().drawBuffersExtension)&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"textures",{get:function(){return this._textures},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"count",{get:function(){return this._count},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"depthTexture",{get:function(){return this._textures[this._textures.length-1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wrapU",{set:function(a){if(this._textures)for(var b=0;b=0;a--)this._textures[a]._texture=null;null===(a=this._renderTarget)||void 0===a||a.dispose(),this._renderTarget=null}},b}(cd),Kk=function(a,b,c){this.id=a,this.scale=b,this.offset=c},Lk=function(){function a(b,c,d,e){return this.name=b,this.meshes=c,this.scene=e,this.options=d,this.options.map=null!==(b=this.options.map)&&void 0!==b?b:["ambientTexture","bumpTexture","diffuseTexture","emissiveTexture","lightmapTexture","opacityTexture","reflectionTexture","refractionTexture","specularTexture"],this.options.uvsIn=null!==(c=this.options.uvsIn)&&void 0!==c?c:W.b.UVKind,this.options.uvsOut=null!==(e=this.options.uvsOut)&&void 0!==e?e:W.b.UVKind,this.options.layout=null!==(d=this.options.layout)&&void 0!==d?d:a.LAYOUT_STRIP,this.options.layout===a.LAYOUT_COLNUM&&(this.options.colnum=null!==(b=this.options.colnum)&&void 0!==b?b:8),this.options.updateInputMeshes=null===(c=this.options.updateInputMeshes)||void 0===c||c,this.options.disposeSources=null===(e=this.options.disposeSources)||void 0===e||e,this._expecting=0,this.options.fillBlanks=null===(d=this.options.fillBlanks)||void 0===d||d,!0===this.options.fillBlanks&&(this.options.customFillColor=null!==(b=this.options.customFillColor)&&void 0!==b?b:"black"),this.options.frameSize=null!==(c=this.options.frameSize)&&void 0!==c?c:256,this.options.paddingRatio=null!==(e=this.options.paddingRatio)&&void 0!==e?e:.0115,this._paddingValue=Math.ceil(this.options.frameSize*this.options.paddingRatio),this._paddingValue%2!=0&&this._paddingValue++,this.options.paddingMode=null!==(d=this.options.paddingMode)&&void 0!==d?d:a.SUBUV_WRAP,this.options.paddingMode===a.SUBUV_COLOR&&(this.options.paddingColor=null!==(b=this.options.paddingColor)&&void 0!==b?b:new h.b(0,0,0,1)),this.sets={},this.frames=[],this}return a.prototype._createFrames=function(a){for(var b=this,c=this._calculateSize(),d=new g.d(1,1).divide(c),e=0,f=this._expecting,i=this.meshes.length,j=Object.keys(this.sets),k=0;k0);for(var a=0;a0)}},a}();wi=" attribute vec2 position; varying vec2 vPosition; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vPosition=position; vUV=position*madd+madd; gl_Position=vec4(position,0.0,1.0); }";X.a.ShadersStore.proceduralVertexShader=wi;var Nk=function(a){function b(b,c,d,e,g,h,i,j){void 0===g&&(g=null),void 0===h&&(h=!0),void 0===i&&(i=!1),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT);var k=a.call(this,null,e,!h)||this;k.isEnabled=!0,k.autoClear=!0,k.onGeneratedObservable=new f.c,k.onBeforeGenerationObservable=new f.c,k.nodeMaterialSource=null,k._textures={},k._currentRefreshId=-1,k._frameId=-1,k._refreshRate=1,k._vertexBuffers={},k._uniforms=new Array,k._samplers=new Array,k._floats={},k._ints={},k._floatsArrays={},k._colors3={},k._colors4={},k._vectors2={},k._vectors3={},k._matrices={},k._fallbackTextureUsed=!1,k._cachedDefines=null,k._contentUpdateId=-1,k._rtWrapper=null;var m=(e=k.getScene()||C.a.LastCreatedScene)._getComponent(Na.a.NAME_PROCEDURALTEXTURE);m||(m=new Mk(e),e._addComponent(m)),e.proceduralTextures.push(k),k._fullEngine=e.getEngine(),k.name=b,k.isRenderTarget=!0,k._size=c,k._textureType=j,k._generateMipMaps=h,k._drawWrapper=new Ec.a(k._fullEngine),k.setFragment(d),k._fallbackTexture=g,i?(k._rtWrapper=k._fullEngine.createRenderTargetCubeTexture(c,{generateMipMaps:h,generateDepthBuffer:!1,generateStencilBuffer:!1,type:j}),k.setFloat("face",0)):k._rtWrapper=k._fullEngine.createRenderTargetTexture(c,{generateMipMaps:h,generateDepthBuffer:!1,generateStencilBuffer:!1,type:j}),k._texture=k._rtWrapper.texture;m=[];return m.push(1,1),m.push(-1,1),m.push(-1,-1),m.push(1,-1),k._vertexBuffers[W.b.PositionKind]=new W.b(k._fullEngine,m,W.b.PositionKind,!1,!1,2),k._createIndexBuffer(),k}return Object(l.d)(b,a),b.prototype.getEffect=function(){return this._drawWrapper.effect},b.prototype._setEffect=function(a){this._drawWrapper.effect=a},b.prototype.getContent=function(){var a=this;return this._contentData&&this._frameId===this._contentUpdateId||(this._contentData?this._contentData.then(function(b){a._contentData=a.readPixels(0,0,b),a._contentUpdateId=a._frameId}):(this._contentData=this.readPixels(0,0),this._contentUpdateId=this._frameId)),this._contentData},b.prototype._createIndexBuffer=function(){var a=this._fullEngine,b=[];b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),this._indexBuffer=a.createIndexBuffer(b)},b.prototype._rebuild=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&a._rebuild(),this._createIndexBuffer(),this.refreshRate===cd.REFRESHRATE_RENDER_ONCE&&(this.refreshRate=cd.REFRESHRATE_RENDER_ONCE)},b.prototype.reset=function(){var a;null===(a=this._drawWrapper.effect)||void 0===a||a.dispose()},b.prototype._getDefines=function(){return""},b.prototype.isReady=function(){var a,b=this,c=this._fullEngine;if(this.nodeMaterialSource)return this._drawWrapper.effect.isReady();if(!this._fragment)return!1;if(this._fallbackTextureUsed)return!0;var d=this._getDefines();return!(!this._drawWrapper.effect||d!==this._cachedDefines||!this._drawWrapper.effect.isReady())||(a=void 0!==this._fragment.fragmentElement?{vertex:"procedural",fragmentElement:this._fragment.fragmentElement}:{vertex:"procedural",fragment:this._fragment},this._cachedDefines!==d&&(this._cachedDefines=d,this._drawWrapper.effect=c.createEffect(a,[W.b.PositionKind],this._uniforms,this._samplers,d,void 0,void 0,function(){var a;null===(a=b._rtWrapper)||void 0===a||a.dispose(),b._rtWrapper=b._texture=null,b._fallbackTexture&&(b._texture=b._fallbackTexture._texture,b._texture&&b._texture.incrementReferences()),b._fallbackTextureUsed=!0})),this._drawWrapper.effect.isReady())},b.prototype.resetRefreshCounter=function(){this._currentRefreshId=-1},b.prototype.setFragment=function(a){this._fragment=a},Object.defineProperty(b.prototype,"refreshRate",{get:function(){return this._refreshRate},set:function(a){this._refreshRate=a,this.resetRefreshCounter()},enumerable:!1,configurable:!0}),b.prototype._shouldRender=function(){return this.isEnabled&&this.isReady()&&this._texture?!this._fallbackTextureUsed&&(-1===this._currentRefreshId||this.refreshRate===this._currentRefreshId?(this._currentRefreshId=1,this._frameId++,!0):(this._currentRefreshId++,!1)):(this._texture&&(this._texture.isReady=!1),!1)},b.prototype.getRenderSize=function(){return this._size},b.prototype.resize=function(a,b){var c;this._fallbackTextureUsed||(null===(c=this._rtWrapper)||void 0===c||c.dispose(),this._rtWrapper=this._fullEngine.createRenderTargetTexture(a,{generateMipMaps:b,generateDepthBuffer:!1,generateStencilBuffer:!1,type:this._textureType}),this._texture=this._rtWrapper.texture,this._size=a,this._generateMipMaps=b)},b.prototype._checkUniform=function(a){-1===this._uniforms.indexOf(a)&&this._uniforms.push(a)},b.prototype.setTexture=function(a,b){return-1===this._samplers.indexOf(a)&&this._samplers.push(a),this._textures[a]=b,this},b.prototype.setFloat=function(a,b){return this._checkUniform(a),this._floats[a]=b,this},b.prototype.setInt=function(a,b){return this._checkUniform(a),this._ints[a]=b,this},b.prototype.setFloats=function(a,b){return this._checkUniform(a),this._floatsArrays[a]=b,this},b.prototype.setColor3=function(a,b){return this._checkUniform(a),this._colors3[a]=b,this},b.prototype.setColor4=function(a,b){return this._checkUniform(a),this._colors4[a]=b,this},b.prototype.setVector2=function(a,b){return this._checkUniform(a),this._vectors2[a]=b,this},b.prototype.setVector3=function(a,b){return this._checkUniform(a),this._vectors3[a]=b,this},b.prototype.setMatrix=function(a,b){return this._checkUniform(a),this._matrices[a]=b,this},b.prototype.render=function(a){a=this.getScene();if(a){var b=this._fullEngine;if(b.enableEffect(this._drawWrapper),this.onBeforeGenerationObservable.notifyObservers(this),b.setState(!1),!this.nodeMaterialSource){for(var c in this._textures)this._drawWrapper.effect.setTexture(c,this._textures[c]);for(c in this._ints)this._drawWrapper.effect.setInt(c,this._ints[c]);for(c in this._floats)this._drawWrapper.effect.setFloat(c,this._floats[c]);for(c in this._floatsArrays)this._drawWrapper.effect.setArray(c,this._floatsArrays[c]);for(c in this._colors3)this._drawWrapper.effect.setColor3(c,this._colors3[c]);for(c in this._colors4){var d=this._colors4[c];this._drawWrapper.effect.setFloat4(c,d.r,d.g,d.b,d.a)}for(c in this._vectors2)this._drawWrapper.effect.setVector2(c,this._vectors2[c]);for(c in this._vectors3)this._drawWrapper.effect.setVector3(c,this._vectors3[c]);for(c in this._matrices)this._drawWrapper.effect.setMatrix(c,this._matrices[c])}if(this._texture&&this._rtWrapper){if(null===(d=b._debugPushGroup)||void 0===d||d.call(b,"procedural texture generation for "+this.name,1),this.isCube)for(c=0;c<6;c++)b.bindFramebuffer(this._rtWrapper,c,void 0,void 0,!0),b.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect),this._drawWrapper.effect.setFloat("face",c),this.autoClear&&b.clear(a.clearColor,!0,!1,!1),b.drawElementsType(qi.a.TriangleFillMode,0,6);else b.bindFramebuffer(this._rtWrapper,0,void 0,void 0,!0),b.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect),this.autoClear&&b.clear(a.clearColor,!0,!1,!1),b.drawElementsType(qi.a.TriangleFillMode,0,6);b.unBindFramebuffer(this._rtWrapper,this.isCube),this.isCube&&b.generateMipMapsForCubemap(this._texture),null===(d=b._debugPopGroup)||void 0===d||d.call(b,1),this.onGenerated&&this.onGenerated(),this.onGeneratedObservable.notifyObservers(this)}}},b.prototype.clone=function(){var a=this.getSize();a=new b(this.name,a.width,this._fragment,this.getScene(),this._fallbackTexture,this._generateMipMaps);return a.hasAlpha=this.hasAlpha,a.level=this.level,a.coordinatesMode=this.coordinatesMode,a},b.prototype.dispose=function(){var b=this.getScene();if(b){var c=b.proceduralTextures.indexOf(this);c>=0&&b.proceduralTextures.splice(c,1);b=this._vertexBuffers[W.b.PositionKind];b&&(b.dispose(),this._vertexBuffers[W.b.PositionKind]=null),this._indexBuffer&&this._fullEngine._releaseBuffer(this._indexBuffer)&&(this._indexBuffer=null),this.onGeneratedObservable.clear(),this.onBeforeGenerationObservable.clear(),a.prototype.dispose.call(this)}},Object(l.c)([Object(J.d)()],b.prototype,"isEnabled",void 0),Object(l.c)([Object(J.d)()],b.prototype,"autoClear",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_generateMipMaps",void 0),Object(l.c)([Object(J.d)()],b.prototype,"_size",void 0),Object(l.c)([Object(J.d)()],b.prototype,"refreshRate",null),b}(V.a);Object(i.b)("BABYLON.ProceduralTexture",Nk);var Ok=function(a){function b(b,c,d,e,f,g){b=a.call(this,b,d,null,e,f,g)||this;return b._animate=!0,b._time=0,b._texturePath=c,b._loadJson(c),b.refreshRate=1,b}return Object(l.d)(b,a),b.prototype._loadJson=function(a){var b=this,c=function(){try{b.setFragment(b._texturePath)}catch(a){q.a.Log("No json or ShaderStore or DOM element found for CustomProceduralTexture")}};a=a+"/config.json";var d=new M.a;d.open("GET",a),d.addEventListener("load",function(){if(200===d.status||d.responseText&&d.responseText.length>0)try{b._config=JSON.parse(d.response),b.updateShaderUniforms(),b.updateTextures(),b.setFragment(b._texturePath+"/custom"),b._animate=b._config.animate,b.refreshRate=b._config.refreshrate}catch(a){c()}else c()},!1),d.addEventListener("error",function(){c()},!1);try{d.send()}catch(a){q.a.Error("CustomProceduralTexture: Error on XHR send request.")}},b.prototype.isReady=function(){if(!a.prototype.isReady.call(this))return!1;for(var b in this._textures)if(!this._textures[b].isReady())return!1;return!0},b.prototype.render=function(b){var c=this.getScene();this._animate&&c&&(this._time+=.03*c.getAnimationRatio(),this.updateShaderUniforms()),a.prototype.render.call(this,b)},b.prototype.updateTextures=function(){for(var a=0;a0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isDirectlyConnectedToVertexOutput",{get:function(){if(!this.hasEndpoints)return!1;for(var a=0,b=this._endpoints;a=0)&&(b.isExposedOnFrame=!0,b.exposedPortPosition=this.exposedPortPosition),b},a.prototype.dispose=function(){this.onConnectionObservable.clear()},a}(),al=function(){function a(a,b,c,d){void 0===b&&(b=Z.Vertex),void 0===c&&(c=!1),void 0===d&&(d=!1),this._isFinalMerger=!1,this._isInput=!1,this._name="",this._isUnique=!1,this.inputsAreExclusive=!1,this._codeVariableName="",this._inputs=new Array,this._outputs=new Array,this.comments="",this.visibleInInspector=!1,this.visibleOnFrame=!1,this._target=b,this._originalTargetIsNeutral=b===Z.Neutral,this._isFinalMerger=c,this._isInput=d,this._name=a,this.uniqueId=Zd.a.UniqueId}return Object.defineProperty(a.prototype,"name",{get:function(){return this._name},set:function(a){this.validateBlockName(a)&&(this._name=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUnique",{get:function(){return this._isUnique},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isFinalMerger",{get:function(){return this._isFinalMerger},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isInput",{get:function(){return this._isInput},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"buildId",{get:function(){return this._buildId},set:function(a){this._buildId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"target",{get:function(){return this._target},set:function(a){0==(this._target&a)&&(this._target=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"inputs",{get:function(){return this._inputs},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"outputs",{get:function(){return this._outputs},enumerable:!1,configurable:!0}),a.prototype.getInputByName=function(a){var b=this._inputs.filter(function(b){return b.name===a});return b.length?b[0]:null},a.prototype.getOutputByName=function(a){var b=this._outputs.filter(function(b){return b.name===a});return b.length?b[0]:null},a.prototype.initialize=function(a){},a.prototype.bind=function(a,b,c,d){},a.prototype._declareOutput=function(a,b){return b._getGLType(a.type)+" "+a.associatedVariableName},a.prototype._writeVariable=function(a){return a.connectedPoint?""+a.associatedVariableName:"0."},a.prototype._writeFloat=function(a){a=a.toString();return-1===a.indexOf(".")&&(a+=".0"),""+a},a.prototype.getClassName=function(){return"NodeMaterialBlock"},a.prototype.registerInput=function(a,b,c,d,e){return void 0===c&&(c=!1),(e=null!=e?e:new $k(a,this,Uk.Input)).type=b,e.isOptional=c,d&&(e.target=d),this._inputs.push(e),this},a.prototype.registerOutput=function(a,b,c,d){return(d=null!=d?d:new $k(a,this,Uk.Output)).type=b,c&&(d.target=c),this._outputs.push(d),this},a.prototype.getFirstAvailableInput=function(a){void 0===a&&(a=null);for(var b=0,c=this._inputs;b=this._outputs.length?null:this._outputs[a+1]},a.prototype.isAnAncestorOf=function(a){for(var b=0,c=this._outputs;b[0.."+c.repeatKey+"] ";a=$f.a.IncludesShadersStore[a]+" ";if(this.sharedData.emitComments&&(a=b+" "+a),!c)return a;if(c.replaceStrings)for(b=0;b[0.."+c.repeatKey+"] ":this.functions[d]="#include<"+a+"> ",void (this.sharedData.emitComments&&(this.functions[d]=b+" "+this.functions[d]));if(this.functions[d]=$f.a.IncludesShadersStore[a],this.sharedData.emitComments&&(this.functions[d]=b+" "+this.functions[d]),c.removeIfDef&&(this.functions[d]=this.functions[d].replace(/^s*?#ifdef.+$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#endif.*$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#else.*$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#elif.*$/gm,"")),c.removeAttributes&&(this.functions[d]=this.functions[d].replace(/^s*?attribute.+$/gm,"")),c.removeUniforms&&(this.functions[d]=this.functions[d].replace(/^s*?uniform.+$/gm,"")),c.removeVaryings&&(this.functions[d]=this.functions[d].replace(/^s*?varying.+$/gm,"")),c.replaceStrings)for(a=0;a0||this._emitRateGradients&&this._emitRateGradients.length>0||this._lifeTimeGradients&&this._lifeTimeGradients.length>0},a.prototype.getDragGradients=function(){return this._dragGradients},a.prototype.getLimitVelocityGradients=function(){return this._limitVelocityGradients},a.prototype.getColorGradients=function(){return this._colorGradients},a.prototype.getSizeGradients=function(){return this._sizeGradients},a.prototype.getColorRemapGradients=function(){return this._colorRemapGradients},a.prototype.getAlphaRemapGradients=function(){return this._alphaRemapGradients},a.prototype.getLifeTimeGradients=function(){return this._lifeTimeGradients},a.prototype.getAngularSpeedGradients=function(){return this._angularSpeedGradients},a.prototype.getVelocityGradients=function(){return this._velocityGradients},a.prototype.getStartSizeGradients=function(){return this._startSizeGradients},a.prototype.getEmitRateGradients=function(){return this._emitRateGradients},Object.defineProperty(a.prototype,"direction1",{get:function(){return this.particleEmitterType.direction1?this.particleEmitterType.direction1:g.e.Zero()},set:function(a){this.particleEmitterType.direction1&&(this.particleEmitterType.direction1=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"direction2",{get:function(){return this.particleEmitterType.direction2?this.particleEmitterType.direction2:g.e.Zero()},set:function(a){this.particleEmitterType.direction2&&(this.particleEmitterType.direction2=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"minEmitBox",{get:function(){return this.particleEmitterType.minEmitBox?this.particleEmitterType.minEmitBox:g.e.Zero()},set:function(a){this.particleEmitterType.minEmitBox&&(this.particleEmitterType.minEmitBox=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxEmitBox",{get:function(){return this.particleEmitterType.maxEmitBox?this.particleEmitterType.maxEmitBox:g.e.Zero()},set:function(a){this.particleEmitterType.maxEmitBox&&(this.particleEmitterType.maxEmitBox=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isBillboardBased",{get:function(){return this._isBillboardBased},set:function(a){this._isBillboardBased!==a&&(this._isBillboardBased=a,this._reset())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a)},enumerable:!1,configurable:!0}),a.prototype._attachImageProcessingConfiguration=function(a){a!==this._imageProcessingConfiguration&&(!a&&this._scene?this._imageProcessingConfiguration=this._scene.imageProcessingConfiguration:this._imageProcessingConfiguration=a)},a.prototype._reset=function(){},a.prototype._removeGradientAndTexture=function(a,b,c){if(!b)return this;for(var d=0,e=0,f=b;e-1))return this._optimizers.push(a),this},b.prototype.unregisterOptimizer=function(a){a=this._optimizers.indexOf(a);if(-1!==a)return this._optimizers.splice(a,1),this},b.prototype.addOutputNode=function(a){if(null===a.target)throw"This node is not meant to be an output node. You may want to explicitly set its target value.";return 0!=(a.target&Z.Vertex)&&this._addVertexOutputNode(a),0!=(a.target&Z.Fragment)&&this._addFragmentOutputNode(a),this},b.prototype.removeOutputNode=function(a){return null===a.target||(0!=(a.target&Z.Vertex)&&this._removeVertexOutputNode(a),0!=(a.target&Z.Fragment)&&this._removeFragmentOutputNode(a)),this},b.prototype._addVertexOutputNode=function(a){if(-1===this._vertexOutputNodes.indexOf(a))return a.target=Z.Vertex,this._vertexOutputNodes.push(a),this},b.prototype._removeVertexOutputNode=function(a){a=this._vertexOutputNodes.indexOf(a);if(-1!==a)return this._vertexOutputNodes.splice(a,1),this},b.prototype._addFragmentOutputNode=function(a){if(-1===this._fragmentOutputNodes.indexOf(a))return a.target=Z.Fragment,this._fragmentOutputNodes.push(a),this},b.prototype._removeFragmentOutputNode=function(a){a=this._fragmentOutputNodes.indexOf(a);if(-1!==a)return this._fragmentOutputNodes.splice(a,1),this},b.prototype.needAlphaBlending=function(){return!this.ignoreAlpha&&(this.alpha<1||this._sharedData&&this._sharedData.hints.needAlphaBlending)},b.prototype.needAlphaTesting=function(){return this._sharedData&&this._sharedData.hints.needAlphaTesting},b.prototype._initializeBlock=function(a,b,c,d){if(void 0===d&&(d=!0),a.initialize(b),d&&a.autoConfigure(this),a._preparationId=this._buildId,-1===this.attachedBlocks.indexOf(a)){if(a.isUnique)for(var e=a.getClassName(),f=0,g=this.attachedBlocks;f-1&&this.attachedBlocks.splice(b,1),a.isFinalMerger&&this.removeOutputNode(a)},b.prototype.build=function(a,c,d){void 0===a&&(a=!1),void 0===c&&(c=!0),void 0===d&&(d=!0),this._buildWasSuccessful=!1;var e=this.getScene().getEngine(),f=this._mode===Sk.Particle;if(0===this._vertexOutputNodes.length&&!f)throw"You must define at least one vertexOutputNode";if(0===this._fragmentOutputNodes.length)throw"You must define at least one fragmentOutputNode";this._vertexCompilationState=new bl,this._vertexCompilationState.supportUniformBuffers=e.supportsUniformBuffers,this._vertexCompilationState.target=Z.Vertex,this._fragmentCompilationState=new bl,this._fragmentCompilationState.supportUniformBuffers=e.supportsUniformBuffers,this._fragmentCompilationState.target=Z.Fragment,this._sharedData=new cl,this._vertexCompilationState.sharedData=this._sharedData,this._fragmentCompilationState.sharedData=this._sharedData,this._sharedData.buildId=this._buildId,this._sharedData.emitComments=this._options.emitComments,this._sharedData.verbose=a,this._sharedData.scene=this.getScene(),this._sharedData.allowEmptyVertexProgram=f;for(var e=[],f=[],g=0,h=this._vertexOutputNodes;g0&&(Yh.a.BindMorphTargetParameters(c,a),c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(a))},b.prototype.replaceRepeatableContent=function(a,b,c,d){b=this.position;var e=this.normal,f=this.tangent,g=this.uv,h=this.positionOutput,i=this.normalOutput,j=this.tangentOutput,k=this.uvOutput;a=a;var m=d.NUM_MORPH_INFLUENCERS;c=c.morphTargetManager;var o=c&&c.supportsNormals&&d.NORMAL,r=c&&c.supportsTangents&&d.TANGENT;d=c&&c.supportsUVs&&d.UV1;var u="";(null==c?void 0:c.isUsingTextureForTargets)&&m>0&&(u+="float vertexID; ");for(var v=0;v0)for(v=0;v(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_SAMPLERNAME_,bump)/g,replace:""},{search:/uniform sampler2D bumpSampler;/g,replace:""},{search:/vec2 parallaxOcclusion(vec3 vViewDirCoT,vec3 vNormalCoT,vec2 texCoord,float parallaxScale)/g,replace:"#define inline vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale, sampler2D bumpSampler)"},{search:/vec2 parallaxOffset(vec3 viewDir,float heightScale)/g,replace:"vec2 parallaxOffset(vec3 viewDir, float heightScale, float height_)"},{search:/texture2D(bumpSampler,vBumpUV).w/g,replace:"height_"}]});g=i&&h?"texture2D("+h+", "+d.associatedVariableName+" + uvOffset).xyz":this.normalMapColor.associatedVariableName;return b.compilationString+=this._declareOutput(this.output,b)+" = vec4(0.); ",b.compilationString+=b._emitCodeFromInclude("bumpFragment",c,{replaceStrings:[{search:/perturbNormal(TBN,texture2D(bumpSampler,vBumpUV+uvOffset).xyz,vBumpInfos.y)/g,replace:"perturbNormal(TBN, "+g+", vBumpInfos.y)"},{search:/parallaxOcclusion(invTBN*-viewDirectionW,invTBN*normalW,vBumpUV,vBumpInfos.z)/g,replace:"parallaxOcclusion((invTBN * -viewDirectionW), (invTBN * normalW), vBumpUV, vBumpInfos.z, "+(i&&this.useParallaxOcclusion?h:"bumpSampler")+")"},{search:/parallaxOffset(invTBN*viewDirectionW,vBumpInfos.z)/g,replace:"parallaxOffset(invTBN * viewDirectionW, vBumpInfos.z, "+(i?this.parallaxHeight.associatedVariableName:"0.")+")"},{search:/vTangentSpaceParams/g,replace:this._tangentSpaceParameterName},{search:/vBumpInfos.y/g,replace:k},{search:/vBumpInfos.z/g,replace:j},{search:/vBumpUV/g,replace:d.associatedVariableName},{search:/vPositionW/g,replace:e.associatedVariableName+".xyz"},{search:/normalW=/g,replace:this.output.associatedVariableName+".xyz = "},{search:/mat3(normalMatrix)*normalW/g,replace:"mat3(normalMatrix) * "+this.output.associatedVariableName+".xyz"},{search:/normalW/g,replace:f.associatedVariableName+".xyz"},{search:/viewDirectionW/g,replace:i?this.viewDirection.associatedVariableName:"vec3(0.)"},m]}),this},b.prototype._dumpPropertiesCode=function(){var b=a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".invertX = ")+this.invertX+"; ";return b+=this._codeVariableName+".invertY = "+this.invertY+"; ",b+=this._codeVariableName+".useParallaxOcclusion = "+this.useParallaxOcclusion+"; "},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.invertX=this.invertX,b.invertY=this.invertY,b.useParallaxOcclusion=this.useParallaxOcclusion,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.invertX=b.invertX,this.invertY=b.invertY,this.useParallaxOcclusion=!!b.useParallaxOcclusion},Object(l.c)([gl("Invert X axis",el.Boolean,"PROPERTIES",{notifiers:{update:!1}})],b.prototype,"invertX",void 0),Object(l.c)([gl("Invert Y axis",el.Boolean,"PROPERTIES",{notifiers:{update:!1}})],b.prototype,"invertY",void 0),Object(l.c)([gl("Use parallax occlusion",el.Boolean)],b.prototype,"useParallaxOcclusion",void 0),b}(al);Object(i.b)("BABYLON.PerturbNormalBlock",Rl);var Sl=function(a){function b(b){b=a.call(this,b,Z.Fragment,!0)||this;return b.registerInput("value",$.Float,!0),b.registerInput("cutoff",$.Float,!0),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"DiscardBlock"},Object.defineProperty(b.prototype,"value",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cutoff",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.sharedData.hints.needAlphaTesting=!0,this.cutoff.isConnected&&this.value.isConnected)return b.compilationString+="if ("+this.value.associatedVariableName+" < "+this.cutoff.associatedVariableName+") discard; ",this},b}(al);Object(i.b)("BABYLON.DiscardBlock",Sl);var Tl=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerOutput("output",$.Float,Z.Fragment),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FrontFacingBlock"},Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.target===Z.Vertex)throw"FrontFacingBlock must only be used in a fragment shader";var c=this._outputs[0];return b.compilationString+=this._declareOutput(c,b)+" = gl_FrontFacing ? 1.0 : 0.0; ",this},b}(al);Object(i.b)("BABYLON.FrontFacingBlock",Tl);var Ul=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerInput("input",$.AutoDetect,!1),b.registerOutput("dx",$.BasedOnInput),b.registerOutput("dy",$.BasedOnInput),b._outputs[0]._typeConnectionSource=b._inputs[0],b._outputs[1]._typeConnectionSource=b._inputs[0],b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"DerivativeBlock"},Object.defineProperty(b.prototype,"input",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dx",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dy",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){a.prototype._buildBlock.call(this,b);var c=this._outputs[0],d=this._outputs[1];return b._emitExtension("derivatives","#extension GL_OES_standard_derivatives : enable"),c.hasEndpoints&&(b.compilationString+=this._declareOutput(c,b)+" = dFdx("+this.input.associatedVariableName+"); "),d.hasEndpoints&&(b.compilationString+=this._declareOutput(d,b)+" = dFdy("+this.input.associatedVariableName+"); "),this},b}(al);Object(i.b)("BABYLON.DerivativeBlock",Ul);var Vl=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerOutput("xy",$.Vector2,Z.Fragment),b.registerOutput("xyz",$.Vector3,Z.Fragment),b.registerOutput("xyzw",$.Vector4,Z.Fragment),b.registerOutput("x",$.Float,Z.Fragment),b.registerOutput("y",$.Float,Z.Fragment),b.registerOutput("z",$.Float,Z.Fragment),b.registerOutput("w",$.Float,Z.Fragment),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FragCoordBlock"},Object.defineProperty(b.prototype,"xy",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"xyz",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"xyzw",{get:function(){return this._outputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"x",{get:function(){return this._outputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"y",{get:function(){return this._outputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"z",{get:function(){return this._outputs[5]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[6]},enumerable:!1,configurable:!0}),b.prototype.writeOutputs=function(a){for(var b="",c=0,d=this._outputs;c=0;Yh.a.PrepareUniformsAndSamplersForLight(e,a.uniforms,a.samplers,c["PROJECTEDLIGHTTEXTURE"+e],d,f)}},b.prototype.bind=function(a,b,c){if(c){var d=c.getScene();this.light?Yh.a.BindLight(this.light,this._lightId,d,a,!0):Yh.a.BindLights(d,c,a,!0,b.maxSimultaneousLights)}},b.prototype._injectVertexCode=function(a){var b=this.worldPosition,c="//"+this.name;this.light?(this._lightId=(void 0!==a.counters.lightCounter?a.counters.lightCounter:-1)+1,a.counters.lightCounter=this._lightId,a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString())):(a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),this._lightId=0,a.sharedData.dynamicUniformBlocks.push(this));var d="v_"+b.associatedVariableName;a._emitVaryingFromString(d,"vec4")&&(a.compilationString+=d+" = "+b.associatedVariableName+"; "),this.light?a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()},{search:/worldPos/g,replace:b.associatedVariableName}]}):(a.compilationString+="vec4 worldPos = "+b.associatedVariableName+"; ",this.view.isConnected&&(a.compilationString+="mat4 view = "+this.view.associatedVariableName+"; "),a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{repeatKey:"maxSimultaneousLights"}))},b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.target===Z.Fragment){b.sharedData.forcedBindableBlocks.push(this),b.sharedData.blocksWithDefines.push(this);var c="//"+this.name,d=this.worldPosition;b._emitFunctionFromInclude("helperFunctions",c),b._emitFunctionFromInclude("lightsFragmentFunctions",c,{replaceStrings:[{search:/vPositionW/g,replace:"v_"+d.associatedVariableName+".xyz"}]}),b._emitFunctionFromInclude("shadowsFragmentFunctions",c,{replaceStrings:[{search:/vPositionW/g,replace:"v_"+d.associatedVariableName+".xyz"}]}),this.light?b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString()):b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),0===this._lightId&&(b._registerTempVariable("viewDirectionW")&&(b.compilationString+="vec3 viewDirectionW = normalize("+this.cameraPosition.associatedVariableName+" - v_"+d.associatedVariableName+".xyz); "),b.compilationString+="lightingInfo info; ",b.compilationString+="float shadow = 1.; ",b.compilationString+="float glossiness = "+(this.glossiness.isConnected?this.glossiness.associatedVariableName:"1.0")+" * "+(this.glossPower.isConnected?this.glossPower.associatedVariableName:"1024.0")+"; ",b.compilationString+="vec3 diffuseBase = vec3(0., 0., 0.); ",b.compilationString+="vec3 specularBase = vec3(0., 0., 0.); ",b.compilationString+="vec3 normalW = "+this.worldNormal.associatedVariableName+".xyz; "),this.light?b.compilationString+=b._emitCodeFromInclude("lightFragment",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]}):b.compilationString+=b._emitCodeFromInclude("lightFragment",c,{repeatKey:"maxSimultaneousLights"});d=this.diffuseOutput;c=this.specularOutput;return b.compilationString+=this._declareOutput(d,b)+" = diffuseBase"+(this.diffuseColor.isConnected?" * "+this.diffuseColor.associatedVariableName:"")+"; ",c.hasEndpoints&&(b.compilationString+=this._declareOutput(c,b)+" = specularBase"+(this.specularColor.isConnected?" * "+this.specularColor.associatedVariableName:"")+"; "),this.shadow.hasEndpoints&&(b.compilationString+=this._declareOutput(this.shadow,b)+" = shadow; "),this}this._injectVertexCode(b)},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return this.light&&(b.lightId=this.light.id),b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),b.lightId&&(this.light=c.getLightById(b.lightId))},b}(al);Object(i.b)("BABYLON.LightBlock",Yl);var Zl=function(a){function b(b,c,d,e,f,g){c=a.call(this,b,c,d)||this;return c._blockType=e,c._blockName=f,c._nameForCheking=g,c._nameForCheking||(c._nameForCheking=b),c.needDualDirectionValidation=!0,c}return Object(l.d)(b,a),b.prototype.checkCompatibilityState=function(a){return a instanceof b&&a.name===this._nameForCheking?Tk.Compatible:Tk.TypeIncompatible},b.prototype.createCustomInputBlock=function(){return[new this._blockType(this._blockName),this.name]},b}($k),$l=function(a){function b(c){c=a.call(this,c,Z.VertexAndFragment)||this;return c.registerOutput("source",$.Object,Z.VertexAndFragment,new Zl("source",c,Uk.Output,b,"ImageSourceBlock")),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"texture",{get:function(){return this._texture},set:function(a){var b,c=this;if(this._texture!==a){b=null!==(b=null==a?void 0:a.getScene())&&void 0!==b?b:T.a.LastCreatedScene;!a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return a.hasTexture(c._texture)}),this._texture=a,a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(b){return b.hasTexture(a)})}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samplerName",{get:function(){return this._samplerName},enumerable:!1,configurable:!0}),b.prototype.bind=function(a,b,c){this.texture&&a.setTexture(this._samplerName,this.texture)},b.prototype.isReady=function(){return!(this.texture&&!this.texture.isReadyOrNotBlocking())},b.prototype.getClassName=function(){return"ImageSourceBlock"},Object.defineProperty(b.prototype,"source",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){return a.prototype._buildBlock.call(this,b),b.target===Z.Vertex&&(this._samplerName=b._getFreeVariableName(this.name+"Sampler"),b.sharedData.blockingBlocks.push(this),b.sharedData.textureBlocks.push(this),b.sharedData.bindableBlocks.push(this)),b._emit2DSampler(this._samplerName),this},b.prototype._dumpPropertiesCode=function(){var b=a.prototype._dumpPropertiesCode.call(this);return this.texture?(b+=this._codeVariableName+".texture = new BABYLON.Texture(""+this.texture.name+"", null, "+this.texture.noMipmap+", "+this.texture.invertY+", "+this.texture.samplingMode+"); ",b+=this._codeVariableName+".texture.wrapU = "+this.texture.wrapU+"; ",b+=this._codeVariableName+".texture.wrapV = "+this.texture.wrapV+"; ",b+=this._codeVariableName+".texture.uAng = "+this.texture.uAng+"; ",b+=this._codeVariableName+".texture.vAng = "+this.texture.vAng+"; ",b+=this._codeVariableName+".texture.wAng = "+this.texture.wAng+"; ",b+=this._codeVariableName+".texture.uOffset = "+this.texture.uOffset+"; ",b+=this._codeVariableName+".texture.vOffset = "+this.texture.vOffset+"; ",b+=this._codeVariableName+".texture.uScale = "+this.texture.uScale+"; ",b+=this._codeVariableName+".texture.vScale = "+this.texture.vScale+"; ",b+=this._codeVariableName+".texture.coordinatesMode = "+this.texture.coordinatesMode+"; "):b},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return this.texture&&!this.texture.isRenderTarget&&"VideoTexture"!==this.texture.getClassName()&&(b.texture=this.texture.serialize()),b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),b.texture&&!Ll.IgnoreTexturesAtLoadTime&&void 0!==b.texture.url&&(d=0===b.texture.url.indexOf("data:")?"":d,this.texture=V.a.Parse(b.texture,c,d))},b}(al);Object(i.b)("BABYLON.ImageSourceBlock",$l);var am=function(a){function b(b,c){void 0===c&&(c=!1);b=a.call(this,b,c?Z.Fragment:Z.VertexAndFragment)||this;return b.convertToGammaSpace=!1,b.convertToLinearSpace=!1,b.disableLevelMultiplication=!1,b._fragmentOnly=c,b.registerInput("uv",$.Vector2,!1,Z.VertexAndFragment),b.registerInput("source",$.Object,!0,Z.VertexAndFragment,new Zl("source",b,Uk.Input,$l,"ImageSourceBlock")),b.registerOutput("rgba",$.Color4,Z.Neutral),b.registerOutput("rgb",$.Color3,Z.Neutral),b.registerOutput("r",$.Float,Z.Neutral),b.registerOutput("g",$.Float,Z.Neutral),b.registerOutput("b",$.Float,Z.Neutral),b.registerOutput("a",$.Float,Z.Neutral),b.registerOutput("level",$.Float,Z.Neutral),b._inputs[0].acceptedConnectionPointTypes.push($.Vector3),b._inputs[0].acceptedConnectionPointTypes.push($.Vector4),b._inputs[0]._prioritizeVertex=!c,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"texture",{get:function(){var a;return this.source.isConnected?(null===(a=this.source.connectedPoint)||void 0===a?void 0:a.ownerBlock).texture:this._texture},set:function(a){var b,c=this;if(this._texture!==a){b=null!==(b=null==a?void 0:a.getScene())&&void 0!==b?b:T.a.LastCreatedScene;!a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return a.hasTexture(c._texture)}),this._texture=a,a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(b){return b.hasTexture(a)})}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samplerName",{get:function(){return this._imageSource?this._imageSource.samplerName:this._samplerName},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasImageSource",{get:function(){return!!this._imageSource},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"TextureBlock"},Object.defineProperty(b.prototype,"uv",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"source",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rgba",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rgb",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"r",{get:function(){return this._outputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"g",{get:function(){return this._outputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"b",{get:function(){return this._outputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"a",{get:function(){return this._outputs[5]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"level",{get:function(){return this._outputs[6]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"target",{get:function(){if(this._fragmentOnly)return Z.Fragment;if(!this.uv.isConnected)return Z.VertexAndFragment;if(this.uv.sourceBlock.isInput)return Z.VertexAndFragment;for(var a=this.uv.connectedPoint;a;){if(a.target===Z.Fragment)return Z.Fragment;if(a.target===Z.Vertex)return Z.VertexAndFragment;if(a.target===Z.Neutral||a.target===Z.VertexAndFragment){var b=a.ownerBlock;if(b.target===Z.Fragment)return Z.Fragment;a=null;for(var c=0,b=b.inputs;c=0;Yh.a.PrepareUniformsAndSamplersForLight(e,a.uniforms,a.samplers,c["PROJECTEDLIGHTTEXTURE"+e],d,f)}},b.prototype.isReady=function(a,b,c){return!(this._environmentBRDFTexture&&!this._environmentBRDFTexture.isReady())&&!(c._areImageProcessingDirty&&b.imageProcessingConfiguration&&!b.imageProcessingConfiguration.isReady())},b.prototype.bind=function(a,b,c){if(c){var d=c.getScene();this.light?Yh.a.BindLight(this.light,this._lightId,d,a,!0):Yh.a.BindLights(d,c,a,!0,b.maxSimultaneousLights),a.setTexture(this._environmentBrdfSamplerName,this._environmentBRDFTexture),a.setFloat2("vDebugMode",this.debugLimit,this.debugFactor);c=this._scene.ambientColor;c&&a.setColor3("ambientFromScene",c);c=d.useRightHandedSystem===(null!=d._mirroredCameraPosition);a.setFloat(this._invertNormalName,c?-1:1),a.setFloat4("vLightingIntensity",this.directIntensity,1,this.environmentIntensity*this._scene.environmentIntensity,this.specularIntensity);d=null!==(c=null===(d=this.indexOfRefraction.connectInputBlock)||void 0===d?void 0:d.value)&&void 0!==c?c:1.5;c=Math.pow((d-1)/(d+1),2);this._metallicReflectanceColor.scaleToRef(c*this._metallicF0Factor,h.c.Color3[0]);d=this._metallicF0Factor;a.setColor4(this._vMetallicReflectanceFactorsName,h.c.Color3[0],d),b.imageProcessingConfiguration&&b.imageProcessingConfiguration.bind(a)}},b.prototype._injectVertexCode=function(a){var b=this.worldPosition,c="//"+this.name;this.light?(this._lightId=(void 0!==a.counters.lightCounter?a.counters.lightCounter:-1)+1,a.counters.lightCounter=this._lightId,a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString())):(a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),this._lightId=0,a.sharedData.dynamicUniformBlocks.push(this));var d="v_"+b.associatedVariableName;a._emitVaryingFromString(d,"vec4")&&(a.compilationString+=d+" = "+b.associatedVariableName+"; ");d=this.reflection.isConnected?null===(d=this.reflection.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;d&&(d.viewConnectionPoint=this.view),a.compilationString+=null!==(d=null==d?void 0:d.handleVertexSide(a))&&void 0!==d?d:"",a._emitUniformFromString("vDebugMode","vec2","defined(IGNORE) || DEBUGMODE > 0"),a._emitUniformFromString("ambientFromScene","vec3"),a._emitVaryingFromString("vClipSpacePosition","vec4","defined(IGNORE) || DEBUGMODE > 0")&&(a._injectAtEnd+="#if DEBUGMODE > 0 ",a._injectAtEnd+="vClipSpacePosition = gl_Position; ",a._injectAtEnd+="#endif "),this.light?a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()},{search:/worldPos/g,replace:b.associatedVariableName}]}):(a.compilationString+="vec4 worldPos = "+b.associatedVariableName+"; ",this.view.isConnected&&(a.compilationString+="mat4 view = "+this.view.associatedVariableName+"; "),a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{repeatKey:"maxSimultaneousLights"}))},b.prototype._getAlbedoOpacityCode=function(){var a="albedoOpacityOutParams albedoOpacityOut; ";return a+="albedoOpacityBlock( vec4("+(this.baseColor.isConnected?this.baseColor.associatedVariableName:"vec3(1.)")+", 1.), #ifdef ALBEDO vec4(1.), vec2(1., 1.), #endif #ifdef OPACITY vec4("+(this.opacity.isConnected?this.opacity.associatedVariableName:"1.")+"), vec2(1., 1.), #endif albedoOpacityOut ); vec3 surfaceAlbedo = albedoOpacityOut.surfaceAlbedo; float alpha = albedoOpacityOut.alpha; "},b.prototype._getAmbientOcclusionCode=function(){var a="ambientOcclusionOutParams aoOut; ";return a+="ambientOcclusionBlock( #ifdef AMBIENT vec3("+(this.ambientOcc.isConnected?this.ambientOcc.associatedVariableName:"1.")+"), vec4(0., 1.0, 1.0, 0.), #endif aoOut ); "},b.prototype._getReflectivityCode=function(a){var b="reflectivityOutParams reflectivityOut; ";return this._vMetallicReflectanceFactorsName=a._getFreeVariableName("vMetallicReflectanceFactors"),a._emitUniformFromString(this._vMetallicReflectanceFactorsName,"vec4"),b+="vec3 baseColor = surfaceAlbedo; reflectivityBlock( vec4("+this.metallic.associatedVariableName+", "+this.roughness.associatedVariableName+", 0., 0.), #ifdef METALLICWORKFLOW surfaceAlbedo, "+this._vMetallicReflectanceFactorsName+", #endif #ifdef REFLECTIVITY vec3(0., 0., 1.), vec4(1.), #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor, #endif #ifdef MICROSURFACEMAP microSurfaceTexel, <== not handled! #endif reflectivityOut ); float microSurface = reflectivityOut.microSurface; float roughness = reflectivityOut.roughness; #ifdef METALLICWORKFLOW surfaceAlbedo = reflectivityOut.surfaceAlbedo; #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor = reflectivityOut.ambientOcclusionColor; #endif "},b.prototype._buildBlock=function(b){var c,d,e,f;a.prototype._buildBlock.call(this,b),this._scene=b.sharedData.scene,this._environmentBRDFTexture||(this._environmentBRDFTexture=ii(this._scene));c=this.reflection.isConnected?null===(c=this.reflection.connectedPoint)||void 0===c?void 0:c.ownerBlock:null;if(c&&(c.worldPositionConnectionPoint=this.worldPosition,c.cameraPositionConnectionPoint=this.cameraPosition,c.worldNormalConnectionPoint=this.worldNormal),b.target!==Z.Fragment)return this._injectVertexCode(b),this;b.sharedData.forcedBindableBlocks.push(this),b.sharedData.blocksWithDefines.push(this),b.sharedData.blockingBlocks.push(this);var g="//"+this.name,h="v_"+this.worldPosition.associatedVariableName,i=this.perturbedNormal;this._environmentBrdfSamplerName=b._getFreeVariableName("environmentBrdfSampler"),b._emit2DSampler(this._environmentBrdfSamplerName),b.sharedData.hints.needAlphaBlending=b.sharedData.hints.needAlphaBlending||this.useAlphaBlending,b.sharedData.hints.needAlphaTesting=b.sharedData.hints.needAlphaTesting||this.useAlphaTest,b._emitExtension("lod","#extension GL_EXT_shader_texture_lod : enable","defined(LODBASEDMICROSFURACE)"),b._emitExtension("derivatives","#extension GL_OES_standard_derivatives : enable"),b.uniforms.push("exposureLinear"),b.uniforms.push("contrast"),b.uniforms.push("vInverseScreenSize"),b.uniforms.push("vignetteSettings1"),b.uniforms.push("vignetteSettings2"),b.uniforms.push("vCameraColorCurveNegative"),b.uniforms.push("vCameraColorCurveNeutral"),b.uniforms.push("vCameraColorCurvePositive"),b.uniforms.push("txColorTransform"),b.uniforms.push("colorTransformSettings"),this.light?b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",g,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString()):b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",g,{repeatKey:"maxSimultaneousLights"}),b._emitFunctionFromInclude("helperFunctions",g),b._emitFunctionFromInclude("importanceSampling",g),b._emitFunctionFromInclude("pbrHelperFunctions",g),b._emitFunctionFromInclude("imageProcessingDeclaration",g),b._emitFunctionFromInclude("imageProcessingFunctions",g),b._emitFunctionFromInclude("shadowsFragmentFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrDirectLightingSetupFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrDirectLightingFalloffFunctions",g),b._emitFunctionFromInclude("pbrBRDFFunctions",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"}]}),b._emitFunctionFromInclude("hdrFilteringFunctions",g),b._emitFunctionFromInclude("pbrDirectLightingFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrIBLFunctions",g),b._emitFunctionFromInclude("pbrBlockAlbedoOpacity",g),b._emitFunctionFromInclude("pbrBlockReflectivity",g),b._emitFunctionFromInclude("pbrBlockAmbientOcclusion",g),b._emitFunctionFromInclude("pbrBlockAlphaFresnel",g),b._emitFunctionFromInclude("pbrBlockAnisotropic",g),b._emitUniformFromString("vLightingIntensity","vec4"),this._vNormalWName=b._getFreeVariableName("vNormalW"),b.compilationString+="vec4 "+this._vNormalWName+" = normalize("+this.worldNormal.associatedVariableName+"); ",b._registerTempVariable("viewDirectionW")&&(b.compilationString+="vec3 viewDirectionW = normalize("+this.cameraPosition.associatedVariableName+" - "+h+".xyz); "),b.compilationString+="vec3 geometricNormalW = "+this._vNormalWName+".xyz; ",b.compilationString+="vec3 normalW = "+(i.isConnected?"normalize("+i.associatedVariableName+".xyz)":"geometricNormalW")+"; ",this._invertNormalName=b._getFreeVariableName("invertNormal"),b._emitUniformFromString(this._invertNormalName,"float"),b.compilationString+=b._emitCodeFromInclude("pbrBlockNormalFinal",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"},{search:/vEyePosition.w/g,replace:this._invertNormalName}]}),b.compilationString+=this._getAlbedoOpacityCode(),b.compilationString+=b._emitCodeFromInclude("depthPrePass",g),b.compilationString+=this._getAmbientOcclusionCode(),b.compilationString+=b._emitCodeFromInclude("pbrBlockLightmapInit",g),b.compilationString+="#ifdef UNLIT vec3 diffuseBase = vec3(1., 1., 1.); #else ",b.compilationString+=this._getReflectivityCode(b),b.compilationString+=b._emitCodeFromInclude("pbrBlockGeometryInfo",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"},{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"}]});i=this.anisotropy.isConnected?null===(d=this.anisotropy.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;i&&(i.worldPositionConnectionPoint=this.worldPosition,i.worldNormalConnectionPoint=this.worldNormal,b.compilationString+=i.getCode(b,!this.perturbedNormal.isConnected)),c&&c.hasTexture&&(b.compilationString+=c.getCode(b,i?"anisotropicOut.anisotropicNormal":"normalW")),b._emitFunctionFromInclude("pbrBlockReflection",g,{replaceStrings:[{search:/computeReflectionCoords/g,replace:"computeReflectionCoordsPBR"},{search:/REFLECTIONMAP_3D/g,replace:null!==(d=null==c?void 0:c._define3DName)&&void 0!==d?d:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(i=null==c?void 0:c._defineOppositeZ)&&void 0!==i?i:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(d=null==c?void 0:c._defineProjectionName)&&void 0!==d?d:"REFLECTIONMAP_PROJECTION"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(d=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==d?d:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(i=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==i?i:"LINEARSPECULARREFLECTION"},{search:/vReflectionFilteringInfo/g,replace:null!==(d=null==c?void 0:c._vReflectionFilteringInfoName)&&void 0!==d?d:"vReflectionFilteringInfo"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockReflectance0",g,{replaceStrings:[{search:/metallicReflectanceFactors/g,replace:this._vMetallicReflectanceFactorsName}]});d=this.sheen.isConnected?null===(i=this.sheen.connectedPoint)||void 0===i?void 0:i.ownerBlock:null;d&&(b.compilationString+=d.getCode(c)),b._emitFunctionFromInclude("pbrBlockSheen",g,{replaceStrings:[{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(i=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==i?i:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(d=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==d?d:"LINEARSPECULARREFLECTION"}]});d=this.clearcoat.isConnected?null===(i=this.clearcoat.connectedPoint)||void 0===i?void 0:i.ownerBlock:null;i=!this.perturbedNormal.isConnected&&!this.anisotropy.isConnected;e=this.perturbedNormal.isConnected&&(null===(e=(null===(e=this.perturbedNormal.connectedPoint)||void 0===e?void 0:e.ownerBlock).worldTangent)||void 0===e?void 0:e.isConnected);f=this.anisotropy.isConnected&&(null===(f=this.anisotropy.connectedPoint)||void 0===f?void 0:f.ownerBlock).worldTangent.isConnected;e=e||!this.perturbedNormal.isConnected&&f;b.compilationString+=Tm.GetCode(b,d,c,h,i,e,this.worldNormal.associatedVariableName),i&&(e=null!==(f=null==d?void 0:d.worldTangent.isConnected)&&void 0!==f&&f),b._emitFunctionFromInclude("pbrBlockClearcoat",g,{replaceStrings:[{search:/computeReflectionCoords/g,replace:"computeReflectionCoordsPBR"},{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(d=null==c?void 0:c._defineOppositeZ)&&void 0!==d?d:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(f=null==c?void 0:c._defineProjectionName)&&void 0!==f?f:"REFLECTIONMAP_PROJECTION"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(d=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==d?d:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(f=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==f?f:"LINEARSPECULARREFLECTION"},{search:/defined(TANGENT)/g,replace:e?"defined(TANGENT)":"defined(IGNORE)"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockReflectance",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/REFLECTIONMAP_3D/g,replace:null!==(d=null==c?void 0:c._define3DName)&&void 0!==d?d:"REFLECTIONMAP_3D"}]});e=this.subsurface.isConnected?null===(f=this.subsurface.connectedPoint)||void 0===f?void 0:f.ownerBlock:null;f=this.subsurface.isConnected?null===(d=(null===(i=this.subsurface.connectedPoint)||void 0===i?void 0:i.ownerBlock).refraction.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;f&&(f.viewConnectionPoint=this.view,f.indexOfRefractionConnectionPoint=this.indexOfRefraction),b.compilationString+=Vm.GetCode(b,e,c,h),b._emitFunctionFromInclude("pbrBlockSubSurface",g,{replaceStrings:[{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(d=null==c?void 0:c._defineOppositeZ)&&void 0!==d?d:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(e=null==c?void 0:c._defineProjectionName)&&void 0!==e?e:"REFLECTIONMAP_PROJECTION"},{search:/SS_REFRACTIONMAP_3D/g,replace:null!==(i=null==f?void 0:f._define3DName)&&void 0!==i?i:"SS_REFRACTIONMAP_3D"},{search:/SS_LODINREFRACTIONALPHA/g,replace:null!==(d=null==f?void 0:f._defineLODRefractionAlpha)&&void 0!==d?d:"SS_LODINREFRACTIONALPHA"},{search:/SS_LINEARSPECULARREFRACTION/g,replace:null!==(c=null==f?void 0:f._defineLinearSpecularRefraction)&&void 0!==c?c:"SS_LINEARSPECULARREFRACTION"},{search:/SS_REFRACTIONMAP_OPPOSITEZ/g,replace:null!==(e=null==f?void 0:f._defineOppositeZ)&&void 0!==e?e:"SS_REFRACTIONMAP_OPPOSITEZ"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockDirectLighting",g),this.light?b.compilationString+=b._emitCodeFromInclude("lightFragment",g,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]}):b.compilationString+=b._emitCodeFromInclude("lightFragment",g,{repeatKey:"maxSimultaneousLights"}),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalLitComponents",g),b.compilationString+="#endif ";i=this.ambientColor.isConnected?this.ambientColor.associatedVariableName:"vec3(0., 0., 0.)";d=ui.DEFAULT_AO_ON_ANALYTICAL_LIGHTS.toString();-1===d.indexOf(".")&&(d+="."),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalUnlitComponents",g,{replaceStrings:[{search:/vec3 finalEmissive[sS]*?finalEmissive*=vLightingIntensity.y;/g,replace:""},{search:/vAmbientColor/g,replace:i+" * ambientFromScene"},{search:/vAmbientInfos.w/g,replace:d}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalColorComposition",g,{replaceStrings:[{search:/finalEmissive/g,replace:"vec3(0.)"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockImageProcessing",g,{replaceStrings:[{search:/visibility/g,replace:"1."}]}),b.compilationString+=b._emitCodeFromInclude("pbrDebug",g,{replaceStrings:[{search:/vNormalW/g,replace:this._vNormalWName},{search:/vPositionW/g,replace:h},{search:/albedoTexture.rgb;/g,replace:"vec3(1.); gl_FragColor.rgb = toGammaSpace(gl_FragColor.rgb); "}]});for(c=0,f=this._outputs;c "+this.b.associatedVariableName+" ? "+d+" : "+e+"; ";break;case Zm.GreaterOrEqual:b.compilationString+=this._declareOutput(c,b)+" = "+this.a.associatedVariableName+" >= "+this.b.associatedVariableName+" ? "+d+" : "+e+"; ";break;case Zm.Xor:b.compilationString+=this._declareOutput(c,b)+" = (mod("+this.a.associatedVariableName+" + "+this.b.associatedVariableName+", 2.0) > 0.0) ? "+d+" : "+e+"; ";break;case Zm.Or:b.compilationString+=this._declareOutput(c,b)+" = (min("+this.a.associatedVariableName+" + "+this.b.associatedVariableName+", 1.0) > 0.0) ? "+d+" : "+e+"; ";break;case Zm.And:b.compilationString+=this._declareOutput(c,b)+" = ("+this.a.associatedVariableName+" * "+this.b.associatedVariableName+" > 0.0) ? "+d+" : "+e+"; "}return this},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.condition=this.condition,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.condition=b.condition},b.prototype._dumpPropertiesCode=function(){return a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".condition = BABYLON.ConditionalBlockConditions.")+Zm[this.condition]+"; "},b}(al);Object(i.b)("BABYLON.ConditionalBlock",an);var bn=function(a){function b(b){b=a.call(this,b,Z.Neutral)||this;return b.octaves=6,b.registerInput("seed",$.Vector2),b.registerInput("gain",$.Float,!0),b.registerInput("lacunarity",$.Float,!0),b.registerInput("timeX",$.Float,!0),b.registerInput("timeY",$.Float,!0),b.registerOutput("output",$.Vector3),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"CloudBlock"},Object.defineProperty(b.prototype,"seed",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"gain",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lacunarity",{get:function(){return this._inputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"timeX",{get:function(){return this._inputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"timeY",{get:function(){return this._inputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),this.seed.isConnected&&this._outputs[0].hasEndpoints){var c="fbm"+this.octaves;b._emitFunction("CloudBlockCode","float cloudRandom (in vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898,78.233)))* 43758.5453123); } // Based on Morgan McGuire @morgan3d // https://www.shadertoy.com/view/4dS3Wd float fbmNoise (in vec2 st) { vec2 i = floor(st); vec2 f = fract(st); // Four corners in 2D of a tile float a = cloudRandom(i); float b = cloudRandom(i + vec2(1.0, 0.0)); float c = cloudRandom(i + vec2(0.0, 1.0)); float d = cloudRandom(i + vec2(1.0, 1.0)); vec2 u = f * f * (3.0 - 2.0 * f); return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; }","// CloudBlockCode"),b._emitFunction("CloudBlockCodeFBM"+this.octaves,"float fbm (in vec2 st, in float gain, in float lacunarity) { // Initial values float value = 0.0; float amplitude = .5; float frequency = 0.; // Loop of octaves for (int i = 0; i < OCTAVES; i++) { value += amplitude * fbmNoise(st); st *= lacunarity; amplitude *= gain; } return value; }".replace("fbm",c).replace("OCTAVES",(0|this.octaves).toString()),"// CloudBlockCode FBM");var d=b._getFreeVariableName("st");return b.compilationString+="vec2 "+d+" = "+this.seed.associatedVariableName+"; ",this.timeX.isConnected&&(b.compilationString+=d+".x += 0.1 * "+this.timeX.associatedVariableName+"; "),this.timeY.isConnected&&(b.compilationString+=d+".y += 0.1 * "+this.timeY.associatedVariableName+"; "),b.compilationString+=this._declareOutput(this._outputs[0],b)+" = vec3(0.0) + "+c+"("+d+", "+(this.gain.isConnected?this.gain.associatedVariableName:"0.5")+", "+(this.lacunarity.isConnected?this.lacunarity.associatedVariableName:"2.0")+"); ",this}},b.prototype._dumpPropertiesCode=function(){return a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".octaves = ")+this.octaves+"; "},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.octaves=this.octaves,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.octaves=b.octaves},Object(l.c)([gl("Octaves",el.Int)],b.prototype,"octaves",void 0),b}(al);Object(i.b)("BABYLON.CloudBlock",bn);var cn=function(){function a(){}return a.prototype.optimize=function(a,b){},a}(),dn=function(){function a(){this.mm=new Map}return a.prototype.get=function(a,b){a=this.mm.get(a);if(void 0!==a)return a.get(b)},a.prototype.set=function(a,b,c){var d=this.mm.get(a);void 0===d&&this.mm.set(a,d=new Map),d.set(b,c)},a}(),en=function(){function a(a,b,c){var d=this;this._baseMaterial=a,this._scene=null!=b?b:T.a.LastCreatedScene,this._options=c,this._subMeshToEffect=new Map,this._subMeshToDepthWrapper=new dn,this._meshes=new Map,this._onEffectCreatedObserver=this._baseMaterial.onEffectCreatedObservable.add(function(a){var b;b=null===(b=a.subMesh)||void 0===b?void 0:b.getMesh();b&&!d._meshes.has(b)&&d._meshes.set(b,b.onDisposeObservable.add(function(a){for(var b=d._subMeshToEffect.keys(),c=b.next();!0!==c.done;c=b.next()){var e=c.value;(null==e?void 0:e.getMesh())===a&&(d._subMeshToEffect["delete"](e),d._subMeshToDepthWrapper.mm["delete"](e))}})),d._subMeshToEffect.set(a.subMesh,a.effect),d._subMeshToDepthWrapper.mm["delete"](a.subMesh)})}return Object.defineProperty(a.prototype,"standalone",{get:function(){var a;return null!==(a=null===(a=this._options)||void 0===a?void 0:a.standalone)&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"baseMaterial",{get:function(){return this._baseMaterial},enumerable:!1,configurable:!0}),a.prototype.getEffect=function(a,b,c){a=null===(a=this._subMeshToDepthWrapper.mm.get(a))||void 0===a?void 0:a.get(b);if(!a)return null;b=a.drawWrapper[c];return b||(b=a.drawWrapper[c]=new Ec.a(this._scene.getEngine())).setEffect(a.mainDrawWrapper.effect,a.mainDrawWrapper.defines),b},a.prototype.isReadyForSubMesh=function(a,b,c,d,e){var f;return!(this.standalone&&!this._baseMaterial.isReadyForSubMesh(a.getMesh(),a,d))&&null!==(f=null===(d=this._makeEffect(a,b,c,e))||void 0===d?void 0:d.isReady())&&void 0!==f&&f},a.prototype.dispose=function(){this._baseMaterial.onEffectCreatedObservable.remove(this._onEffectCreatedObserver),this._onEffectCreatedObserver=null;for(var a=this._meshes.entries(),b=a.next();!0!==b.done;b=a.next()){var c=b.value,d=c[0];c=c[1];d.onDisposeObservable.remove(c)}},a.prototype._makeEffect=function(a,b,c,d){var e=this._scene.getEngine(),f=this._subMeshToEffect.get(a);if(!f)return null;var g=this._subMeshToDepthWrapper.get(a,c);if(!g){var h=new Ec.a(e);h.defines=a._materialDefines,(g={drawWrapper:{},mainDrawWrapper:h,depthDefines:"",token:Object(ve.a)()}).drawWrapper[d]=h,this._subMeshToDepthWrapper.set(a,c,g)}h=b.join(" ");if(g.mainDrawWrapper.effect&&h===g.depthDefines)return g.mainDrawWrapper.effect;g.depthDefines=h;a=f.rawVertexSourceCode;c=f.rawFragmentSourceCode;b=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapVertexNormalBias;var i=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapVertexMetric,j=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapFragmentSoftTransparentShadow,k=$f.a.IncludesShadersStore.shadowMapFragment;a=(a=-1!==(a=(a=a.replace(/voids+?main/g,$f.a.IncludesShadersStore.shadowMapVertexExtraDeclaration+" void main")).replace(/#define SHADOWDEPTH_NORMALBIAS|#define CUSTOM_VERTEX_UPDATE_WORLDPOS/g,b)).indexOf("#define SHADOWDEPTH_METRIC")?a.replace(/#define SHADOWDEPTH_METRIC/g,i):a.replace(/}s*$/g,i+" }")).replace(/#define SHADER_NAME.*? |out vec4 glFragColor; /g,"");b=c.indexOf("#define SHADOWDEPTH_SOFTTRANSPARENTSHADOW")>=0||c.indexOf("#define CUSTOM_FRAGMENT_BEFORE_FOG")>=0;i=-1!==c.indexOf("#define SHADOWDEPTH_FRAGMENT");var l="";b?c=c.replace(/#define SHADOWDEPTH_SOFTTRANSPARENTSHADOW|#define CUSTOM_FRAGMENT_BEFORE_FOG/g,j):l=j+" ",c=c.replace(/voids+?main/g,$f.a.IncludesShadersStore.shadowMapFragmentExtraDeclaration+" void main"),i?c=c.replace(/#define SHADOWDEPTH_FRAGMENT/g,k):l+=k+" ",l&&(c=c.replace(/}s*$/g,l+"}")),c=c.replace(/#define SHADER_NAME.*? |out vec4 glFragColor; /g,"");b=f.getUniformNames().slice();for(j in b.push("biasAndScaleSM","depthValuesSM","lightDataSM","softTransparentShadowSM"),g.mainDrawWrapper.effect=e.createEffect({vertexSource:a,fragmentSource:c,vertexToken:g.token,fragmentToken:g.token},{attributes:f.getAttributesNames(),uniformsNames:b,uniformBuffersNames:f.getUniformBuffersNames(),samplers:f.getSamplers(),defines:h+" "+f.defines.replace("#define SHADOWS","").replace(/#define SHADOWd/g,""),indexParameters:f.getIndexParameters()},e),g.drawWrapper)j!==d&&(null===(i=g.drawWrapper[j])||void 0===i||i.setEffect(g.mainDrawWrapper.effect,g.mainDrawWrapper.defines));return g.mainDrawWrapper.effect},a}(),fn=c(114);function gn(a,b,c,d,e){var f=new a.DecoderBuffer;f.Init(b,b.byteLength);var g,h,i=new a.Decoder;try{b=i.GetEncodedGeometryType(f);switch(b){case a.TRIANGULAR_MESH:g=new a.Mesh,h=i.DecodeBufferToMesh(f,g);break;case a.POINT_CLOUD:g=new a.PointCloud,h=i.DecodeBufferToPointCloud(f,g);break;default:throw new Error("Invalid geometry type "+b)}if(!h.ok()||!g.ptr)throw new Error(h.error_msg());if(b===a.TRIANGULAR_MESH){b=3*g.num_faces();var j=4*b,k=a._malloc(j);try{i.GetTrianglesUInt32Array(g,j,k);j=new Uint32Array(b);j.set(new Uint32Array(a.HEAPF32.buffer,k,b)),d(j)}finally{a._free(k)}}b=function(b,c){var d=c.num_components(),f=g.num_points(),h=f*d,j=h*Float32Array.BYTES_PER_ELEMENT,k=a._malloc(j);try{i.GetAttributeDataArrayForAllPoints(g,c,a.DT_FLOAT32,j,k);c=new Float32Array(a.HEAPF32.buffer,k,h);if("color"===b&&3===d){for(var j=new Float32Array(4*f),f=0,l=0;fa.EPSILON?1:0;j|=n,k.push(n)}switch(j){case 0:(g.e.Dot(this.normal,b.plane.normal)>0?c:d).push(b);break;case 1:e.push(b);break;case 2:f.push(b);break;case 3:var o;n=[];j=[];for(h=0;h=3&&(o=new pn(n,b.shared)).plane&&e.push(o),j.length>=3&&(o=new pn(j,b.shared)).plane&&f.push(o)}},a.EPSILON=1e-5,a}(),pn=function(){function a(a,b){this.vertices=a,this.shared=b,this.plane=on.FromPoints(a[0].pos,a[1].pos,a[2].pos)}return a.prototype.clone=function(){return new a(this.vertices.map(function(a){return a.clone()}),this.shared)},a.prototype.flip=function(){this.vertices.reverse().map(function(a){a.flip()}),this.plane.flip()},a}(),qn=function(){function a(a){this.plane=null,this.front=null,this.back=null,this.polygons=new Array,a&&this.build(a)}return a.prototype.clone=function(){var b=new a;return b.plane=this.plane&&this.plane.clone(),b.front=this.front&&this.front.clone(),b.back=this.back&&this.back.clone(),b.polygons=this.polygons.map(function(a){return a.clone()}),b},a.prototype.invert=function(){for(var a=0;a0||i>0){K=-k,aa=-l;M=k,N=l;switch(e){case S.a.CENTER:K-=g/=2,M+=g;break;case S.a.LEFT:M+=g,H=-g/2;break;case S.a.RIGHT:K-=g,H=g/2}switch(f){case S.a.CENTER:aa-=i/=2,N+=i;break;case S.a.BOTTOM:N+=i,I=-i/2;break;case S.a.TOP:aa-=i,I=i/2}}var O=[],ba=[],ca=[];ca[0]=[0,0,1,0,1,1,0,1],ca[1]=[0,0,1,0,1,1,0,1],b!==S.a.ROTATE_TILE&&b!==S.a.ROTATE_ROW||(ca[1]=[1,1,0,1,0,0,1,0]),b!==S.a.FLIP_TILE&&b!==S.a.FLIP_ROW||(ca[1]=[1,0,0,0,0,1,1,1]),b!==S.a.FLIP_N_ROTATE_TILE&&b!==S.a.FLIP_N_ROTATE_ROW||(ca[1]=[0,1,1,1,1,0,0,0]);for(var da=[],ea=[],fa=[],ga=0,ha=0;ha0||i>0){var Q,ia,R,ja;ca=i>0&&(f===S.a.CENTER||f===S.a.TOP);f=i>0&&(f===S.a.CENTER||f===S.a.BOTTOM);var ka=g>0&&(e===S.a.CENTER||e===S.a.RIGHT);e=g>0&&(e===S.a.CENTER||e===S.a.LEFT);var la;if(ca&&ka&&(O.push(K+H,aa+I,0),O.push(-k+H,aa+I,0),O.push(-k+H,aa+i+I,0),O.push(K+H,aa+i+I,0),fa.push(ga,ga+1,ga+3,ga+1,ga+2,ga+3),ga+=4,la=[Q=1-g/c,ia=1-i/d,R=1,ia,R,ja=1,Q,ja],b===S.a.ROTATE_ROW&&(la=[1-Q,1-ia,1-R,1-ia,1-R,1-ja,1-Q,1-ja]),b===S.a.FLIP_ROW&&(la=[1-Q,ia,1-R,ia,1-R,ja,1-Q,ja]),b===S.a.FLIP_N_ROTATE_ROW&&(la=[Q,1-ia,R,1-ia,R,1-ja,Q,1-ja]),da=da.concat(la),ea.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ba.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),ca&&e&&(O.push(k+H,aa+I,0),O.push(M+H,aa+I,0),O.push(M+H,aa+i+I,0),O.push(k+H,aa+i+I,0),fa.push(ga,ga+1,ga+3,ga+1,ga+2,ga+3),ga+=4,la=[Q=0,ia=1-i/d,R=g/c,ia,R,ja=1,Q,ja],(b===S.a.ROTATE_ROW||b===S.a.ROTATE_TILE&&h%2==0)&&(la=[1-Q,1-ia,1-R,1-ia,1-R,1-ja,1-Q,1-ja]),(b===S.a.FLIP_ROW||b===S.a.FLIP_TILE&&h%2==0)&&(la=[1-Q,ia,1-R,ia,1-R,ja,1-Q,ja]),(b===S.a.FLIP_N_ROTATE_ROW||b===S.a.FLIP_N_ROTATE_TILE&&h%2==0)&&(la=[Q,1-ia,R,1-ia,R,1-ja,Q,1-ja]),da=da.concat(la),ea.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ba.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),f&&ka&&(O.push(K+H,l+I,0),O.push(-k+H,l+I,0),O.push(-k+H,N+I,0),O.push(K+H,N+I,0),fa.push(ga,ga+1,ga+3,ga+1,ga+2,ga+3),ga+=4,la=[Q=1-g/c,ia=0,R=1,ia,R,ja=i/d,Q,ja],(b===S.a.ROTATE_ROW&&j%2==1||b===S.a.ROTATE_TILE&&j%1==0)&&(la=[1-Q,1-ia,1-R,1-ia,1-R,1-ja,1-Q,1-ja]),(b===S.a.FLIP_ROW&&j%2==1||b===S.a.FLIP_TILE&&j%2==0)&&(la=[1-Q,ia,1-R,ia,1-R,ja,1-Q,ja]),(b===S.a.FLIP_N_ROTATE_ROW&&j%2==1||b===S.a.FLIP_N_ROTATE_TILE&&j%2==0)&&(la=[Q,1-ia,R,1-ia,R,1-ja,Q,1-ja]),da=da.concat(la),ea.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ba.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),f&&e&&(O.push(k+H,l+I,0),O.push(M+H,l+I,0),O.push(M+H,N+I,0),O.push(k+H,N+I,0),fa.push(ga,ga+1,ga+3,ga+1,ga+2,ga+3),ga+=4,la=[Q=0,ia=0,R=g/c,ia,R,ja=i/d,Q,ja],(b===S.a.ROTATE_ROW&&j%2==1||b===S.a.ROTATE_TILE&&(j+h)%2==1)&&(la=[1-Q,1-ia,1-R,1-ia,1-R,1-ja,1-Q,1-ja]),(b===S.a.FLIP_ROW&&j%2==1||b===S.a.FLIP_TILE&&(j+h)%2==1)&&(la=[1-Q,ia,1-R,ia,1-R,ja,1-Q,ja]),(b===S.a.FLIP_N_ROTATE_ROW&&j%2==1||b===S.a.FLIP_N_ROTATE_TILE&&(j+h)%2==1)&&(la=[Q,1-ia,R,1-ia,R,1-ja,Q,1-ja]),da=da.concat(la),ea.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ba.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),ca){la=[];Q=0,ia=1-i/d,R=1,ja=1,la[0]=[Q,ia,R,ia,R,ja,Q,ja],la[1]=[Q,ia,R,ia,R,ja,Q,ja],b!==S.a.ROTATE_TILE&&b!==S.a.ROTATE_ROW||(la[1]=[1-Q,1-ia,1-R,1-ia,1-R,1-ja,1-Q,1-ja]),b!==S.a.FLIP_TILE&&b!==S.a.FLIP_ROW||(la[1]=[1-Q,ia,1-R,ia,1-R,ja,1-Q,ja]),b!==S.a.FLIP_N_ROTATE_TILE&&b!==S.a.FLIP_N_ROTATE_ROW||(la[1]=[Q,1-ia,R,1-ia,R,1-ja,Q,1-ja]);for(P=0;Pb.x&&(b.x=c.x),c.yb.y&&(b.y=c.y)}),{min:a,max:b,width:b.x-a.x,height:b.y-a.y}},a}(),In=function(){function a(){}return a.Rectangle=function(a,b,c,d){return[new g.d(a,b),new g.d(c,b),new g.d(c,d),new g.d(a,d)]},a.Circle=function(a,b,c,d){void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=32);for(var e=new Array,f=0,h=2*Math.PI/d,i=0;i0){j=f.length/3;this._points.elements.forEach(function(b){e.push(0,-1,0),f.push(b.x,-a,b.y),g.push(1-(b.x-h.min.x)/h.width,1-(b.y-h.min.y)/h.height)});var l=i.length;for(k=0;kj?Gj?G1?1:b.arc:1,f=void 0===b.closed||b.closed,h=b.shape,i=b.radius||1,j=b.tessellation||64,k=b.clip||0,t=b.updatable,u=S.a._GetDefaultSideOrientation(b.sideOrientation),v=b.cap||S.a.NO_CAP,w=2*Math.PI,x=new Array,y=b.invertUV||!1,z=0,A=0;w=w/j*e;e=new Array;for(z=0;z<=j-k;z++){e=[];for(v!=S.a.CAP_START&&v!=S.a.CAP_ALL||(e.push(new g.e(0,h[0].y,0)),e.push(new g.e(Math.cos(z*w)*h[0].x*i,h[0].y,Math.sin(z*w)*h[0].x*i))),A=0;A1)?1:b.arc||1;var y,z,A=function(a,b,c,d,e,f,h,i){for(var j,k,p,q,r=b.getTangents(),w=b.getNormals(),b=b.getDistances(),i=2*Math.PI/e*i,f=f||function(){return d},x=g.c.Matrix[0],y=h===S.a.NO_CAP||h===S.a.CAP_END?0:2,z=0;z3?0:j,b.arc);C=Object(wn.a)(a,{pathArray:z,closePath:!0,closeArray:!1,updatable:w,sideOrientation:x,invertUV:k,frontUVs:b.frontUVs,backUVs:b.backUVs},c);return C._creationDataStorage.pathArray=z,C._creationDataStorage.path3D=y,C._creationDataStorage.tessellation=h,C._creationDataStorage.cap=j,C._creationDataStorage.arc=b.arc,C._creationDataStorage.radius=f,C}S.a.CreateLathe=function(a,b,c,d,e,f,g){return On(a,{shape:b,radius:c,tessellation:d,sideOrientation:g,updatable:f},e)};var Rn={CreateTube:Qn};function Sn(a){var b,c=a.sideOrientation||yd.a.DEFAULTSIDE,d=a.radius||1,e=void 0===a.flat||a.flat,f=a.subdivisions||4,h=a.radiusX||d,i=a.radiusY||d,j=a.radiusZ||d,k=(1+Math.sqrt(5))/2;k=[-1,k,-0,1,k,0,-1,-k,0,1,-k,0,0,-1,-k,0,1,-k,0,-1,k,0,1,k,k,0,1,k,0,-1,-k,0,1,-k,0,-1];var z=[0,11,5,0,5,1,0,1,7,0,7,10,12,22,23,1,5,20,5,11,4,23,22,13,22,18,6,7,1,8,14,21,4,14,4,2,16,13,6,15,6,19,3,8,9,4,21,5,13,17,23,6,13,22,19,6,18,9,8,1],C=[0,1,2,3,4,5,6,7,8,9,10,11,0,2,3,3,3,4,7,8,9,9,10,11],D=[5,1,3,1,6,4,0,0,5,3,4,2,2,2,4,0,2,0,1,1,6,0,6,2,0,4,3,3,4,4,3,1,4,2,4,4,0,2,1,1,2,2,3,3,1,3,2,4],E=[0,0,0,0,1,0,0,1,1,0,0,0,1,1,0,0,1,1,1,0],F=new Array,G=new Array,H=new Array,I=new Array,J=0,K=new Array(3),L=new Array(3);for(b=0;b<3;b++)K[b]=g.e.Zero(),L[b]=g.d.Zero();for(var aa=0;aa<20;aa++){for(b=0;b<3;b++){var M=z[3*aa+b];K[b].copyFromFloats(k[3*C[M]],k[3*C[M]+1],k[3*C[M]+2]),K[b].normalize().scaleInPlace(d),L[b].copyFromFloats(D[2*M]*(138/1024)+60/1024+E[aa]*(-40/1024),D[2*M+1]*(239/1024)+26/1024+E[aa]*(20/1024))}for(var M=function(a,b,c,d){var k=g.e.Lerp(K[0],K[2],b/f),n=g.e.Lerp(K[1],K[2],b/f);k=f===b?K[2]:g.e.Lerp(k,n,a/(f-b));if(k.normalize(),e){n=g.e.Lerp(K[0],K[2],d/f);var o=g.e.Lerp(K[1],K[2],d/f);n=g.e.Lerp(n,o,c/(f-d))}else n=new g.e(k.x,k.y,k.z);n.x/=h,n.y/=i,n.z/=j,n.normalize();o=g.d.Lerp(L[0],L[2],b/f);c=g.d.Lerp(L[1],L[2],b/f);d=f===b?L[2]:g.d.Lerp(o,c,a/(f-b));G.push(k.x*h,k.y*i,k.z*j),H.push(n.x,n.y,n.z),I.push(d.x,d.y),F.push(J),J++},N=0;N0)?1:0)+((i=g.e.Dot(a[f+1].position,b)-c>0)?1:0)+((j=g.e.Dot(a[f+2].position,b)-c>0)?1:0)){case 0:e.push(a[f]),e.push(a[f+1]),e.push(a[f+2]);break;case 1:if(h&&(p=a[f+1],q=a[f+2],r=d(a[f],p),s=d(a[f],q)),i){p=a[f],q=a[f+2],r=d(a[f+1],p),s=d(a[f+1],q),e.push(r),e.push(q.clone()),e.push(p.clone()),e.push(q.clone()),e.push(r.clone()),e.push(s);break}j&&(p=a[f],q=a[f+1],r=d(a[f+2],p),s=d(a[f+2],q)),p&&q&&r&&s&&(e.push(p.clone()),e.push(q.clone()),e.push(r),e.push(s),e.push(r.clone()),e.push(q.clone()));break;case 2:h||(q=d(p=a[f].clone(),a[f+1]),r=d(p,a[f+2]),e.push(p),e.push(q),e.push(r)),i||(q=d(p=a[f+1].clone(),a[f+2]),r=d(p,a[f]),e.push(p),e.push(q),e.push(r)),j||(q=d(p=a[f+2].clone(),a[f]),r=d(p,a[f+1]),e.push(p),e.push(q),e.push(r))}}return e},E=0;E2?K[g[k]]=[-L[B][0],L[B][1],g[k]]:K[g[k]]=[J[L[B][0]],L[B][1],g[k]])}function M(a,b,c,d){z=b+"|"+d,(k=a+"|"+c)in g||z in g?k in g&&!(z in g)?g[z]=g[k]:z in g&&!(k in g)&&(g[k]=g[z]):(g[k]=f,g[z]=f,f++),L[c][0]>2?K[g[k]]=[-L[c][0],L[c][1],g[k]]:K[g[k]]=[J[L[c][0]],L[c][1],g[k]]}this.closestTo=K,this.vecToIdx=g},a.prototype.calcCoeffs=function(){var a=this.m,b=this.n,c=Math.sqrt(3)/3,d=a*a+b*b+a*b;this.coau=(a+b)/d,this.cobu=-b/d,this.coav=-c*(a-b)/d,this.cobv=c*(2*a+b)/d},a.prototype.createInnerFacets=function(){for(var a=this.m,b=this.n,c=0;c0&&d0){for(var f=I.a.HCF(a,b),e=a/f,d=b/f,g=1;g-1?a[c][1]>0&&b[a[c][0]].push([c,a[c][1]]):b[12].push([c,a[c][0]]);a=[];for(c=0;c<12;c++)a[c]=c;var d=12;for(c=0;c<12;c++){b[c].sort(function(a,b){return a[1]-b[1]});for(var e=0;e0;)e=b[g],this.face[e].indexOf(f)>-1?(a=(this.face[e].indexOf(f)+1)%3,f=this.face[e][a],c.push(f),d.push(e),b.splice(g,1),g=0):g++;return this.adjacentFaces.push(c),d},b.prototype.toGoldbergData=function(){var a=this,b=new $n("GeoDual","Goldberg",[],[]);b.name="GD dual";for(var c=this.vertex.length,d=new Array(c),e=0;ed){var f=e;e=d,d=f,q.a.Warn("n > m therefore m and n swapped")}f=new Zn;return f.build(d,e),Gh(a,{custom:ao.BuildGeodesicData(f),size:b.size,sizeX:b.sizeX,sizeY:b.sizeY,sizeZ:b.sizeZ,faceUV:b.faceUV,faceColors:b.faceColors,flat:b.flat,updatable:b.updatable,sideOrientation:b.sideOrientation,frontUVs:b.frontUVs,backUVs:b.backUVs},c)}var co,eo=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.faceColors=[],b.faceCenters=[],b.faceZaxis=[],b.faceXaxis=[],b.faceYaxis=[],b}return Object(l.d)(b,a),b.prototype.setMetadata=function(){this.metadata={nbSharedFaces:this.nbSharedFaces,nbUnsharedFaces:this.nbUnsharedFaces,nbFacesAtPole:this.nbFacesAtPole,nbFaces:this.nbFaces,faceCenters:this.faceCenters,faceXaxis:this.faceXaxis,faceYaxis:this.faceYaxis,faceZaxis:this.faceZaxis,adjacentFaces:this.adjacentFaces}},b.prototype.relFace=function(a,b){return void 0===b?(a>this.nbUnsharedFaces-1&&(q.a.Warn("Maximum number of unshared faces used"),a=this.nbUnsharedFaces-1),this.nbUnsharedFaces+a):(a>11&&(q.a.Warn("Last pole used"),a=11),b>this.nbFacesAtPole-1&&(q.a.Warn("Maximum number of faces at a pole used"),b=this.nbFacesAtPole-1),12+a*this.nbFacesAtPole+b)},b.prototype.refreshFaceData=function(){this.nbSharedFaces=this.metadata.nbSharedFaces,this.nbUnsharedFaces=this.metadata.nbUnsharedFaces,this.nbFacesAtPole=this.metadata.nbFacesAtPole,this.adjacentFaces=this.metadata.adjacentFaces,this.nbFaces=this.metadata.nbFaces,this.faceCenters=this.metadata.faceCenters,this.faceXaxis=this.metadata.faceXaxis,this.faceYaxis=this.metadata.faceYaxis,this.faceZaxis=this.metadata.faceZaxis},b.prototype.changeFaceColors=function(a){for(var b=0;b1&&(k=1),i.push(k,n);for(o=0;o<6;o++)k=f.x+g*Math.cos(h+o*Math.PI/3),n=f.y+g*Math.sin(h+o*Math.PI/3),k<0&&(k=0),k>1&&(k=1),j.push(k,n);for(n=d;nc){var e=d;d=c,c=e,q.a.Warn("n > m therefore m and n swapped")}e=new Zn;e.build(c,d);c=ao.BuildGeodesicData(e);d=c.toGoldbergData();e=new eo(a);b.sideOrientation=S.a._GetDefaultSideOrientation(b.sideOrientation),e._originalBuilderSideOrientation=b.sideOrientation,(function(a,b){for(var c=a.size,d=a.sizeX||c||1,e=a.sizeY||c||1,c=a.sizeZ||c||1,a=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,f=new Array,h=new Array,i=new Array,j=new Array,k=1/0,x=-1/0,y=1/0,z=-1/0,A=0;Ai||a.deleted||a.isDirty))for(var b=0;b<3;++b)if(a.error[b]>0,function(a){if(e){a=a+h.verticesStart;var b=g.e.FromArray(e,3*a);b=function(a){if(c)for(var b=0;b0&&this._reconstructedMesh.setVerticesData(W.b.NormalKind,g),h.length>0&&this._reconstructedMesh.setVerticesData(W.b.UVKind,h),i.length>0&&this._reconstructedMesh.setVerticesData(W.b.ColorKind,i);b=this._mesh.subMeshes[a];a>0&&(this._reconstructedMesh.subMeshes=[],w.forEach(function(a){ln.a.AddToMesh(a.materialIndex,a.verticesStart,a.verticesCount,a.indexStart,a.indexCount,a.getMesh())}),ln.a.AddToMesh(b.materialIndex,v,t,d,3*e.length,this._reconstructedMesh))},a.prototype.initDecimatedMesh=function(){this._reconstructedMesh=new S.a(this._mesh.name+"Decimated",this._mesh.getScene()),this._reconstructedMesh.material=this._mesh.material,this._reconstructedMesh.parent=this._mesh.parent,this._reconstructedMesh.isVisible=!1,this._reconstructedMesh.renderingGroupId=this._mesh.renderingGroupId},a.prototype.isFlipped=function(a,b,c,d,e){for(var f=0;f.999)return!0;j=g.e.Cross(j,i).normalize();if(d[f]=!1,g.e.Dot(j,h.normal)<.2)return!0}else d[f]=!0,e.push(h)}}return!1},a.prototype.updateTriangles=function(a,b,c,d){for(var d=d,e=0;e=this._thinInstanceDataStorage.instancesCount)return!1;var d=this._thinInstanceDataStorage.matrixData;return b.copyToArray(d,16*a),this._thinInstanceDataStorage.worldMatrices&&(this._thinInstanceDataStorage.worldMatrices[a]=b),c&&(this.thinInstanceBufferUpdated("matrix"),this.doNotSyncBoundingInfo||this.thinInstanceRefreshBoundingInfo(!1)),!0},S.a.prototype.thinInstanceSetAttributeAt=function(a,b,c,d){return void 0===d&&(d=!0),!(!this._userThinInstanceBuffersStorage||!this._userThinInstanceBuffersStorage.data[a]||b>=this._thinInstanceDataStorage.instancesCount)&&(this._thinInstanceUpdateBufferSize(a,0),this._userThinInstanceBuffersStorage.data[a].set(c,b*this._userThinInstanceBuffersStorage.strides[a]),d&&this.thinInstanceBufferUpdated(a),!0)},Object.defineProperty(S.a.prototype,"thinInstanceCount",{get:function(){return this._thinInstanceDataStorage.instancesCount},set:function(a){var b;a<=(null!==(b=null===(b=this._thinInstanceDataStorage.matrixData)||void 0===b?void 0:b.length)&&void 0!==b?b:0)/16&&(this._thinInstanceDataStorage.instancesCount=a)},enumerable:!0,configurable:!0}),S.a.prototype._thinInstanceCreateMatrixBuffer=function(a,b,c){void 0===c&&(c=!1);for(b=new W.a(this.getEngine(),b,!c,16,!1,!0),c=0;c<4;c++)this.setVerticesBuffer(b.createVertexBuffer(a+c,4*c,4));return b},S.a.prototype.thinInstanceSetBuffer=function(a,b,c,d){var e;void 0===c&&(c=0),void 0===d&&(d=!1),c=c||16,"matrix"===a?(null===(e=this._thinInstanceDataStorage.matrixBuffer)||void 0===e||e.dispose(),this._thinInstanceDataStorage.matrixBuffer=null,this._thinInstanceDataStorage.matrixBufferSize=b?b.length:32*c,this._thinInstanceDataStorage.matrixData=b,this._thinInstanceDataStorage.worldMatrices=null,null!==b?(this._thinInstanceDataStorage.instancesCount=b.length/c,this._thinInstanceDataStorage.matrixBuffer=this._thinInstanceCreateMatrixBuffer("world",b,d),this.doNotSyncBoundingInfo||this.thinInstanceRefreshBoundingInfo(!1)):(this._thinInstanceDataStorage.instancesCount=0,this.doNotSyncBoundingInfo||this.refreshBoundingInfo())):"previousMatrix"===a?(null===(e=this._thinInstanceDataStorage.previousMatrixBuffer)||void 0===e||e.dispose(),this._thinInstanceDataStorage.previousMatrixBuffer=null,this._thinInstanceDataStorage.previousMatrixData=b,null!==b&&(this._thinInstanceDataStorage.previousMatrixBuffer=this._thinInstanceCreateMatrixBuffer("previousWorld",b,d))):null===b?(null===(e=this._userThinInstanceBuffersStorage)||void 0===e?void 0:e.data[a])&&(this.removeVerticesData(a),delete this._userThinInstanceBuffersStorage.data[a],delete this._userThinInstanceBuffersStorage.strides[a],delete this._userThinInstanceBuffersStorage.sizes[a],delete this._userThinInstanceBuffersStorage.vertexBuffers[a]):(this._thinInstanceInitializeUserStorage(),this._userThinInstanceBuffersStorage.data[a]=b,this._userThinInstanceBuffersStorage.strides[a]=c,this._userThinInstanceBuffersStorage.sizes[a]=b.length,this._userThinInstanceBuffersStorage.vertexBuffers[a]=new W.b(this.getEngine(),b,a,!d,!1,c,!0),this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[a]))},S.a.prototype.thinInstanceBufferUpdated=function(a){var b;"matrix"===a?this._thinInstanceDataStorage.matrixBuffer&&this._thinInstanceDataStorage.matrixBuffer.updateDirectly(this._thinInstanceDataStorage.matrixData,0,this._thinInstanceDataStorage.instancesCount):(null===(b=this._userThinInstanceBuffersStorage)||void 0===b?void 0:b.vertexBuffers[a])&&this._userThinInstanceBuffersStorage.vertexBuffers[a].updateDirectly(this._userThinInstanceBuffersStorage.data[a],0)},S.a.prototype.thinInstancePartialBufferUpdate=function(a,b,c){var d;"matrix"===a?this._thinInstanceDataStorage.matrixBuffer&&this._thinInstanceDataStorage.matrixBuffer.updateDirectly(b,c):(null===(d=this._userThinInstanceBuffersStorage)||void 0===d?void 0:d.vertexBuffers[a])&&this._userThinInstanceBuffersStorage.vertexBuffers[a].updateDirectly(b,c)},S.a.prototype.thinInstanceGetWorldMatrices=function(){if(!this._thinInstanceDataStorage.matrixData||!this._thinInstanceDataStorage.matrixBuffer)return[];var a=this._thinInstanceDataStorage.matrixData;if(!this._thinInstanceDataStorage.worldMatrices){this._thinInstanceDataStorage.worldMatrices=new Array;for(var b=0;b-1&&(this.agentDestinationArmed[a]=!0,this.agentDestination[a].set(b.x,b.y,b.z))},a.prototype.agentTeleport=function(a,b){this.recastCrowd.agentTeleport(a,new this.bjsRECASTPlugin.bjsRECAST.Vec3(b.x,b.y,b.z))},a.prototype.updateAgentParameters=function(a,b){var c=this.recastCrowd.getAgentParameters(a);void 0!==b.radius&&(c.radius=b.radius),void 0!==b.height&&(c.height=b.height),void 0!==b.maxAcceleration&&(c.maxAcceleration=b.maxAcceleration),void 0!==b.maxSpeed&&(c.maxSpeed=b.maxSpeed),void 0!==b.collisionQueryRange&&(c.collisionQueryRange=b.collisionQueryRange),void 0!==b.pathOptimizationRange&&(c.pathOptimizationRange=b.pathOptimizationRange),void 0!==b.separationWeight&&(c.separationWeight=b.separationWeight),this.recastCrowd.setAgentParameters(a,c)},a.prototype.removeAgent=function(a){this.recastCrowd.removeAgent(a);a=this.agents.indexOf(a);a>-1&&(this.agents.splice(a,1),this.transforms.splice(a,1),this.reachRadii.splice(a,1),this.agentDestinationArmed.splice(a,1),this.agentDestination.splice(a,1))},a.prototype.getAgents=function(){return this.agents},a.prototype.update=function(a){this.bjsRECASTPlugin.navMesh.update();var b=this.bjsRECASTPlugin.getTimeStep(),c=this.bjsRECASTPlugin.getMaximumSubStepCount();if(b<=hb.a)this.recastCrowd.update(a);else{b=Math.floor(a/b);c&&b>c&&(b=c),b<1&&(b=1);for(c=a/b,a=0;ag&&c.y=400&&f?f(j):c()},!1),j.addEventListener("error",function(){q.a.Error("error on XHR request."),c()},!1),j.send()}else q.a.Error("Error: IndexedDB not supported by your browser or Babylon.js database is not open."),c()},a._ValidateXHRData=function(a,b){void 0===b&&(b=7);try{if(1&b){if(a.responseText&&a.responseText.length>0)return!0;if(1===b)return!1}if(2&b){var c=sk(a.response);if(c.width&&c.height&&c.width>0&&c.height>0)return!0;if(2===b)return!1}if(4&b){c=new Uint8Array(a.response,0,3);return 68===c[0]&&68===c[1]&&83===c[2]}}catch(a){}return!1},a.IsUASupportingBlobStorage=!0,a.IDBStorageEnabled=!1,a._ParseURL=function(a){document.createElement("a").href=a;var b=a.substring(0,a.lastIndexOf("#"));b=a.substring(b.lastIndexOf("/")+1,a.length);return a.substring(0,a.indexOf(b,0))},a._ReturnFullUrlLocation=function(b){return-1===b.indexOf("http:/")&&-1===b.indexOf("https:/")&&"undefined"!=typeof window?a._ParseURL(window.location.href)+b:b},a}(),to=function(){function a(a){this._isUbo(a)?(this.setMatrix3x3=a.updateMatrix3x3.bind(a),this.setMatrix2x2=a.updateMatrix2x2.bind(a),this.setFloat=a.updateFloat.bind(a),this.setFloat2=a.updateFloat2.bind(a),this.setFloat3=a.updateFloat3.bind(a),this.setFloat4=a.updateFloat4.bind(a),this.setFloatArray=a.updateFloatArray.bind(a),this.setArray=a.updateArray.bind(a),this.setIntArray=a.updateIntArray.bind(a),this.setMatrix=a.updateMatrix.bind(a),this.setMatrices=a.updateMatrices.bind(a),this.setVector3=a.updateVector3.bind(a),this.setVector4=a.updateVector4.bind(a),this.setColor3=a.updateColor3.bind(a),this.setColor4=a.updateColor4.bind(a),this.setDirectColor4=a.updateDirectColor4.bind(a),this.setInt=a.updateInt.bind(a),this.setInt2=a.updateInt2.bind(a),this.setInt3=a.updateInt3.bind(a),this.setInt4=a.updateInt4.bind(a)):(this.setMatrix3x3=a.setMatrix3x3.bind(a),this.setMatrix2x2=a.setMatrix2x2.bind(a),this.setFloat=a.setFloat.bind(a),this.setFloat2=a.setFloat2.bind(a),this.setFloat3=a.setFloat3.bind(a),this.setFloat4=a.setFloat4.bind(a),this.setFloatArray=a.setFloatArray.bind(a),this.setArray=a.setArray.bind(a),this.setIntArray=a.setIntArray.bind(a),this.setMatrix=a.setMatrix.bind(a),this.setMatrices=a.setMatrices.bind(a),this.setVector3=a.setVector3.bind(a),this.setVector4=a.setVector4.bind(a),this.setColor3=a.setColor3.bind(a),this.setColor4=a.setColor4.bind(a),this.setDirectColor4=a.setDirectColor4.bind(a),this.setInt=a.setInt.bind(a),this.setInt2=a.setInt2.bind(a),this.setInt3=a.setInt3.bind(a),this.setInt4=a.setInt4.bind(a))}return a.prototype._isUbo=function(a){return void 0!==a.addUniform},a}();X.a.ShadersStore.gpuUpdateParticlesPixelShader="#version 300 es void main() { discard; } ";wi="#version 300 es #define PI 3.14159 uniform float currentCount; uniform float timeDelta; uniform float stopFactor; #ifndef LOCAL uniform mat4 emitterWM; #endif uniform vec2 lifeTime; uniform vec2 emitPower; uniform vec2 sizeRange; uniform vec4 scaleRange; #ifndef COLORGRADIENTS uniform vec4 color1; uniform vec4 color2; #endif uniform vec3 gravity; uniform sampler2D randomSampler; uniform sampler2D randomSampler2; uniform vec4 angleRange; #ifdef BOXEMITTER uniform vec3 direction1; uniform vec3 direction2; uniform vec3 minEmitBox; uniform vec3 maxEmitBox; #endif #ifdef POINTEMITTER uniform vec3 direction1; uniform vec3 direction2; #endif #ifdef HEMISPHERICEMITTER uniform float radius; uniform float radiusRange; uniform float directionRandomizer; #endif #ifdef SPHEREEMITTER uniform float radius; uniform float radiusRange; #ifdef DIRECTEDSPHEREEMITTER uniform vec3 direction1; uniform vec3 direction2; #else uniform float directionRandomizer; #endif #endif #ifdef CYLINDEREMITTER uniform float radius; uniform float height; uniform float radiusRange; #ifdef DIRECTEDCYLINDEREMITTER uniform vec3 direction1; uniform vec3 direction2; #else uniform float directionRandomizer; #endif #endif #ifdef CONEEMITTER uniform vec2 radius; uniform float coneAngle; uniform vec2 height; uniform float directionRandomizer; #endif in vec3 position; #ifdef CUSTOMEMITTER in vec3 initialPosition; #endif in float age; in float life; in vec4 seed; in vec3 size; #ifndef COLORGRADIENTS in vec4 color; #endif in vec3 direction; #ifndef BILLBOARD in vec3 initialDirection; #endif #ifdef ANGULARSPEEDGRADIENTS in float angle; #else in vec2 angle; #endif #ifdef ANIMATESHEET in float cellIndex; #ifdef ANIMATESHEETRANDOMSTART in float cellStartOffset; #endif #endif #ifdef NOISE in vec3 noiseCoordinates1; in vec3 noiseCoordinates2; #endif out vec3 outPosition; #ifdef CUSTOMEMITTER out vec3 outInitialPosition; #endif out float outAge; out float outLife; out vec4 outSeed; out vec3 outSize; #ifndef COLORGRADIENTS out vec4 outColor; #endif out vec3 outDirection; #ifndef BILLBOARD out vec3 outInitialDirection; #endif #ifdef ANGULARSPEEDGRADIENTS out float outAngle; #else out vec2 outAngle; #endif #ifdef ANIMATESHEET out float outCellIndex; #ifdef ANIMATESHEETRANDOMSTART out float outCellStartOffset; #endif #endif #ifdef NOISE out vec3 outNoiseCoordinates1; out vec3 outNoiseCoordinates2; #endif #ifdef SIZEGRADIENTS uniform sampler2D sizeGradientSampler; #endif #ifdef ANGULARSPEEDGRADIENTS uniform sampler2D angularSpeedGradientSampler; #endif #ifdef VELOCITYGRADIENTS uniform sampler2D velocityGradientSampler; #endif #ifdef LIMITVELOCITYGRADIENTS uniform sampler2D limitVelocityGradientSampler; uniform float limitVelocityDamping; #endif #ifdef DRAGGRADIENTS uniform sampler2D dragGradientSampler; #endif #ifdef NOISE uniform vec3 noiseStrength; uniform sampler2D noiseSampler; #endif #ifdef ANIMATESHEET uniform vec4 cellInfos; #endif vec3 getRandomVec3(float offset) { return texture(randomSampler2,vec2(float(gl_VertexID)*offset/currentCount,0)).rgb; } vec4 getRandomVec4(float offset) { return texture(randomSampler,vec2(float(gl_VertexID)*offset/currentCount,0)); } void main() { float newAge=age+timeDelta; if (newAge>=life && stopFactor != 0.) { vec3 newPosition; vec3 newDirection; vec4 randoms=getRandomVec4(seed.x); outLife=lifeTime.x+(lifeTime.y-lifeTime.x)*randoms.r; outAge=newAge-life; outSeed=seed; #ifdef SIZEGRADIENTS outSize.x=texture(sizeGradientSampler,vec2(0,0)).r; #else outSize.x=sizeRange.x+(sizeRange.y-sizeRange.x)*randoms.g; #endif outSize.y=scaleRange.x+(scaleRange.y-scaleRange.x)*randoms.b; outSize.z=scaleRange.z+(scaleRange.w-scaleRange.z)*randoms.a; #ifndef COLORGRADIENTS outColor=color1+(color2-color1)*randoms.b; #endif #ifndef ANGULARSPEEDGRADIENTS outAngle.y=angleRange.x+(angleRange.y-angleRange.x)*randoms.a; outAngle.x=angleRange.z+(angleRange.w-angleRange.z)*randoms.r; #else outAngle=angleRange.z+(angleRange.w-angleRange.z)*randoms.r; #endif #ifdef POINTEMITTER vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); newPosition=vec3(0,0,0); newDirection=direction1+(direction2-direction1)*randoms3; #elif defined(BOXEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); newPosition=minEmitBox+(maxEmitBox-minEmitBox)*randoms2; newDirection=direction1+(direction2-direction1)*randoms3; #elif defined(HEMISPHERICEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float phi=2.0*PI*randoms2.x; float theta=acos(2.0*randoms2.y-1.0); float randX=cos(phi)*sin(theta); float randY=cos(theta); float randZ=sin(phi)*sin(theta); newPosition=(radius-(radius*radiusRange*randoms2.z))*vec3(randX,abs(randY),randZ); newDirection=newPosition+directionRandomizer*randoms3; #elif defined(SPHEREEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float phi=2.0*PI*randoms2.x; float theta=acos(2.0*randoms2.y-1.0); float randX=cos(phi)*sin(theta); float randY=cos(theta); float randZ=sin(phi)*sin(theta); newPosition=(radius-(radius*radiusRange*randoms2.z))*vec3(randX,randY,randZ); #ifdef DIRECTEDSPHEREEMITTER newDirection=normalize(direction1+(direction2-direction1)*randoms3); #else newDirection=normalize(newPosition+directionRandomizer*randoms3); #endif #elif defined(CYLINDEREMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float yPos=(randoms2.x-0.5)*height; float angle=randoms2.y*PI*2.; float inverseRadiusRangeSquared=((1.-radiusRange)*(1.-radiusRange)); float positionRadius=radius*sqrt(inverseRadiusRangeSquared+(randoms2.z*(1.-inverseRadiusRangeSquared))); float xPos=positionRadius*cos(angle); float zPos=positionRadius*sin(angle); newPosition=vec3(xPos,yPos,zPos); #ifdef DIRECTEDCYLINDEREMITTER newDirection=direction1+(direction2-direction1)*randoms3; #else angle=angle+((randoms3.x-0.5)*PI)*directionRandomizer; newDirection=vec3(cos(angle),(randoms3.y-0.5)*directionRandomizer,sin(angle)); newDirection=normalize(newDirection); #endif #elif defined(CONEEMITTER) vec3 randoms2=getRandomVec3(seed.y); float s=2.0*PI*randoms2.x; #ifdef CONEEMITTERSPAWNPOINT float h=0.0001; #else float h=randoms2.y*height.y; h=1.-h*h; #endif float lRadius=radius.x-radius.x*randoms2.z*radius.y; lRadius=lRadius*h; float randX=lRadius*sin(s); float randZ=lRadius*cos(s); float randY=h*height.x; newPosition=vec3(randX,randY,randZ); if (abs(cos(coneAngle)) == 1.0) { newDirection=vec3(0.,1.0,0.); } else { vec3 randoms3=getRandomVec3(seed.z); newDirection=normalize(newPosition+directionRandomizer*randoms3); } #elif defined(CUSTOMEMITTER) newPosition=initialPosition; outInitialPosition=initialPosition; #else newPosition=vec3(0.,0.,0.); newDirection=2.0*(getRandomVec3(seed.w)-vec3(0.5,0.5,0.5)); #endif float power=emitPower.x+(emitPower.y-emitPower.x)*randoms.a; #ifdef LOCAL outPosition=newPosition; #else outPosition=(emitterWM*vec4(newPosition,1.)).xyz; #endif #ifdef CUSTOMEMITTER outDirection=direction; #ifndef BILLBOARD outInitialDirection=direction; #endif #else #ifdef LOCAL vec3 initial=newDirection; #else vec3 initial=(emitterWM*vec4(newDirection,0.)).xyz; #endif outDirection=initial*power; #ifndef BILLBOARD outInitialDirection=initial; #endif #endif #ifdef ANIMATESHEET outCellIndex=cellInfos.x; #ifdef ANIMATESHEETRANDOMSTART outCellStartOffset=randoms.a*outLife; #endif #endif #ifdef NOISE outNoiseCoordinates1=noiseCoordinates1; outNoiseCoordinates2=noiseCoordinates2; #endif } else { float directionScale=timeDelta; outAge=newAge; float ageGradient=newAge/life; #ifdef VELOCITYGRADIENTS directionScale*=texture(velocityGradientSampler,vec2(ageGradient,0)).r; #endif #ifdef DRAGGRADIENTS directionScale*=1.0-texture(dragGradientSampler,vec2(ageGradient,0)).r; #endif #if defined(CUSTOMEMITTER) outPosition=position+(direction-position)*ageGradient; outInitialPosition=initialPosition; #else outPosition=position+direction*directionScale; #endif outLife=life; outSeed=seed; #ifndef COLORGRADIENTS outColor=color; #endif #ifdef SIZEGRADIENTS outSize.x=texture(sizeGradientSampler,vec2(ageGradient,0)).r; outSize.yz=size.yz; #else outSize=size; #endif #ifndef BILLBOARD outInitialDirection=initialDirection; #endif #ifdef CUSTOMEMITTER outDirection=direction; #else vec3 updatedDirection=direction+gravity*timeDelta; #ifdef LIMITVELOCITYGRADIENTS float limitVelocity=texture(limitVelocityGradientSampler,vec2(ageGradient,0)).r; float currentVelocity=length(updatedDirection); if (currentVelocity>limitVelocity) { updatedDirection=updatedDirection*limitVelocityDamping; } #endif outDirection=updatedDirection; #ifdef NOISE float fetchedR=texture(noiseSampler,vec2(noiseCoordinates1.x,noiseCoordinates1.y)*vec2(0.5)+vec2(0.5)).r; float fetchedG=texture(noiseSampler,vec2(noiseCoordinates1.z,noiseCoordinates2.x)*vec2(0.5)+vec2(0.5)).r; float fetchedB=texture(noiseSampler,vec2(noiseCoordinates2.y,noiseCoordinates2.z)*vec2(0.5)+vec2(0.5)).r; vec3 force=vec3(2.*fetchedR-1.,2.*fetchedG-1.,2.*fetchedB-1.)*noiseStrength; outDirection=outDirection+force*timeDelta; outNoiseCoordinates1=noiseCoordinates1; outNoiseCoordinates2=noiseCoordinates2; #endif #endif #ifdef ANGULARSPEEDGRADIENTS float angularSpeed=texture(angularSpeedGradientSampler,vec2(ageGradient,0)).r; outAngle=angle+angularSpeed*timeDelta; #else outAngle=vec2(angle.x+angle.y*timeDelta,angle.y); #endif #ifdef ANIMATESHEET float offsetAge=outAge; float dist=cellInfos.y-cellInfos.x; #ifdef ANIMATESHEETRANDOMSTART outCellStartOffset=cellStartOffset; offsetAge+=cellStartOffset; #else float cellStartOffset=0.; #endif float ratio=0.; if (cellInfos.w == 1.0) { ratio=clamp(mod(cellStartOffset+cellInfos.z*offsetAge,life)/life,0.,1.0); } else { ratio=clamp(cellStartOffset+cellInfos.z*offsetAge/life,0.,1.0); } outCellIndex=float(int(cellInfos.x+ratio*dist)); #endif } }";X.a.ShadersStore.gpuUpdateParticlesVertexShader=wi;var uo=function(){function a(a,b){this._renderVAO=[],this._updateVAO=[],this.alignDataInBuffer=!1,this._parent=a,this._engine=b,this._updateEffectOptions={attributes:["position","initialPosition","age","life","seed","size","color","direction","initialDirection","angle","cellIndex","cellStartOffset","noiseCoordinates1","noiseCoordinates2"],uniformsNames:["currentCount","timeDelta","emitterWM","lifeTime","color1","color2","sizeRange","scaleRange","gravity","emitPower","direction1","direction2","minEmitBox","maxEmitBox","radius","directionRandomizer","height","coneAngle","stopFactor","angleRange","radiusRange","cellInfos","noiseStrength","limitVelocityDamping"],uniformBuffersNames:[],samplers:["randomSampler","randomSampler2","sizeGradientSampler","angularSpeedGradientSampler","velocityGradientSampler","limitVelocityGradientSampler","noiseSampler","dragGradientSampler"],defines:"",fallbacks:null,onCompiled:null,onError:null,indexParameters:null,maxSimultaneousLights:0,transformFeedbackVaryings:[]}}return a.prototype.isUpdateBufferCreated=function(){return!!this._updateEffect},a.prototype.isUpdateBufferReady=function(){var a;return null!==(a=null===(a=this._updateEffect)||void 0===a?void 0:a.isReady())&&void 0!==a&&a},a.prototype.createUpdateBuffer=function(a){return this._updateEffectOptions.transformFeedbackVaryings=["outPosition"],this._updateEffectOptions.transformFeedbackVaryings.push("outAge"),this._updateEffectOptions.transformFeedbackVaryings.push("outSize"),this._updateEffectOptions.transformFeedbackVaryings.push("outLife"),this._updateEffectOptions.transformFeedbackVaryings.push("outSeed"),this._updateEffectOptions.transformFeedbackVaryings.push("outDirection"),this._parent.particleEmitterType instanceof Cl&&this._updateEffectOptions.transformFeedbackVaryings.push("outInitialPosition"),this._parent._colorGradientsTexture||this._updateEffectOptions.transformFeedbackVaryings.push("outColor"),this._parent._isBillboardBased||this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection"),this._parent.noiseTexture&&(this._updateEffectOptions.transformFeedbackVaryings.push("outNoiseCoordinates1"),this._updateEffectOptions.transformFeedbackVaryings.push("outNoiseCoordinates2")),this._updateEffectOptions.transformFeedbackVaryings.push("outAngle"),this._parent.isAnimationSheetEnabled&&(this._updateEffectOptions.transformFeedbackVaryings.push("outCellIndex"),this._parent.spriteRandomStartCell&&this._updateEffectOptions.transformFeedbackVaryings.push("outCellStartOffset")),this._updateEffectOptions.defines=a,this._updateEffect=new $f.a("gpuUpdateParticles",this._updateEffectOptions,this._engine),new to(this._updateEffect)},a.prototype.createVertexBuffers=function(a,b){this._updateVAO.push(this._createUpdateVAO(a)),this._renderVAO.push(this._engine.recordVertexArrayObject(b,null,this._parent._getWrapper(this._parent.blendMode).effect)),this._engine.bindArrayBuffer(null)},a.prototype.createParticleBuffer=function(a){return a},a.prototype.bindDrawBuffers=function(a,b){this._engine.bindVertexArrayObject(this._renderVAO[a],null)},a.prototype.preUpdateParticleBuffer=function(){var a=this._engine;if(this._engine.enableEffect(this._updateEffect),!a.setState)throw new Error("GPU particles cannot work without a full Engine. ThinEngine is not supported")},a.prototype.updateParticleBuffer=function(a,b,c){this._updateEffect.setTexture("randomSampler",this._parent._randomTexture),this._updateEffect.setTexture("randomSampler2",this._parent._randomTexture2),this._parent._sizeGradientsTexture&&this._updateEffect.setTexture("sizeGradientSampler",this._parent._sizeGradientsTexture),this._parent._angularSpeedGradientsTexture&&this._updateEffect.setTexture("angularSpeedGradientSampler",this._parent._angularSpeedGradientsTexture),this._parent._velocityGradientsTexture&&this._updateEffect.setTexture("velocityGradientSampler",this._parent._velocityGradientsTexture),this._parent._limitVelocityGradientsTexture&&this._updateEffect.setTexture("limitVelocityGradientSampler",this._parent._limitVelocityGradientsTexture),this._parent._dragGradientsTexture&&this._updateEffect.setTexture("dragGradientSampler",this._parent._dragGradientsTexture),this._parent.noiseTexture&&this._updateEffect.setTexture("noiseSampler",this._parent.noiseTexture),this._engine.bindVertexArrayObject(this._updateVAO[a],null);a=this._engine;a.bindTransformFeedbackBuffer(b.getBuffer()),a.setRasterizerState(!1),a.beginTransformFeedback(!0),a.drawArraysType(r.a.MATERIAL_PointListDrawMode,0,c),a.endTransformFeedback(),a.setRasterizerState(!0),a.bindTransformFeedbackBuffer(null)},a.prototype.releaseBuffers=function(){},a.prototype.releaseVertexBuffers=function(){for(var a=0;aa)c(b[0],b[0],1);else{for(var d=0;d=e.gradient&&a<=f.gradient)return void c(e,f,(a-e.gradient)/(f.gradient-e.gradient))}f=b.length-1;c(b[f],b[f],1)}},a}(),Bo=function(){function a(b){this.particleSystem=b,this.position=g.e.Zero(),this.direction=g.e.Zero(),this.color=new h.b(0,0,0,0),this.colorStep=new h.b(0,0,0,0),this.lifeTime=1,this.age=0,this.size=0,this.scale=new g.d(1,1),this.angle=0,this.angularSpeed=0,this.cellIndex=0,this._attachedSubEmitters=null,this._currentColor1=new h.b(0,0,0,0),this._currentColor2=new h.b(0,0,0,0),this._currentSize1=0,this._currentSize2=0,this._currentAngularSpeed1=0,this._currentAngularSpeed2=0,this._currentVelocity1=0,this._currentVelocity2=0,this._currentLimitVelocity1=0,this._currentLimitVelocity2=0,this._currentDrag1=0,this._currentDrag2=0,this.id=a._Count++,this.particleSystem.isAnimationSheetEnabled&&this.updateCellInfoFromSystem()}return a.prototype.updateCellInfoFromSystem=function(){this.cellIndex=this.particleSystem.startSpriteCellID},a.prototype.updateCellIndex=function(){var a=this.age,b=this.particleSystem.spriteCellChangeSpeed;this.particleSystem.spriteRandomStartCell&&(void 0===this._randomCellOffset&&(this._randomCellOffset=Math.random()*this.lifeTime),0===b?(b=1,a=this._randomCellOffset):a+=this._randomCellOffset);var c=this._initialEndSpriteCellID-this._initialStartSpriteCellID;a=this._initialSpriteCellLoop?I.a.Clamp(a*b%this.lifeTime/this.lifeTime):I.a.Clamp(a*b/this.lifeTime),this.cellIndex=this._initialStartSpriteCellID+a*c|0},a.prototype._inheritParticleInfoToSubEmitter=function(a){if(a.particleSystem.emitter.position){var b=a.particleSystem.emitter;if(b.position.copyFrom(this.position),a.inheritDirection){var c=g.c.Vector3[0];this.direction.normalizeToRef(c),b.setDirection(c,0,Math.PI/2)}}else a.particleSystem.emitter.copyFrom(this.position);this.direction.scaleToRef(a.inheritedVelocityAmount/2,g.c.Vector3[0]),a.particleSystem._inheritedVelocityOffset.copyFrom(g.c.Vector3[0])},a.prototype._inheritParticleInfoToSubEmitters=function(){var a=this;this._attachedSubEmitters&&this._attachedSubEmitters.length>0&&this._attachedSubEmitters.forEach(function(b){a._inheritParticleInfoToSubEmitter(b)})},a.prototype._reset=function(){this.age=0,this.id=a._Count++,this._currentColorGradient=null,this._currentSizeGradient=null,this._currentAngularSpeedGradient=null,this._currentVelocityGradient=null,this._currentLimitVelocityGradient=null,this._currentDragGradient=null,this.cellIndex=this.particleSystem.startSpriteCellID,this._randomCellOffset=void 0},a.prototype.copyTo=function(a){a.position.copyFrom(this.position),this._initialDirection?a._initialDirection?a._initialDirection.copyFrom(this._initialDirection):a._initialDirection=this._initialDirection.clone():a._initialDirection=null,a.direction.copyFrom(this.direction),this._localPosition&&(a._localPosition?a._localPosition.copyFrom(this._localPosition):a._localPosition=this._localPosition.clone()),a.color.copyFrom(this.color),a.colorStep.copyFrom(this.colorStep),a.lifeTime=this.lifeTime,a.age=this.age,a._randomCellOffset=this._randomCellOffset,a.size=this.size,a.scale.copyFrom(this.scale),a.angle=this.angle,a.angularSpeed=this.angularSpeed,a.particleSystem=this.particleSystem,a.cellIndex=this.cellIndex,a.id=this.id,a._attachedSubEmitters=this._attachedSubEmitters,this._currentColorGradient&&(a._currentColorGradient=this._currentColorGradient,a._currentColor1.copyFrom(this._currentColor1),a._currentColor2.copyFrom(this._currentColor2)),this._currentSizeGradient&&(a._currentSizeGradient=this._currentSizeGradient,a._currentSize1=this._currentSize1,a._currentSize2=this._currentSize2),this._currentAngularSpeedGradient&&(a._currentAngularSpeedGradient=this._currentAngularSpeedGradient,a._currentAngularSpeed1=this._currentAngularSpeed1,a._currentAngularSpeed2=this._currentAngularSpeed2),this._currentVelocityGradient&&(a._currentVelocityGradient=this._currentVelocityGradient,a._currentVelocity1=this._currentVelocity1,a._currentVelocity2=this._currentVelocity2),this._currentLimitVelocityGradient&&(a._currentLimitVelocityGradient=this._currentLimitVelocityGradient,a._currentLimitVelocity1=this._currentLimitVelocity1,a._currentLimitVelocity2=this._currentLimitVelocity2),this._currentDragGradient&&(a._currentDragGradient=this._currentDragGradient,a._currentDrag1=this._currentDrag1,a._currentDrag2=this._currentDrag2),this.particleSystem.isAnimationSheetEnabled&&(a._initialStartSpriteCellID=this._initialStartSpriteCellID,a._initialEndSpriteCellID=this._initialEndSpriteCellID,a._initialSpriteCellLoop=this._initialSpriteCellLoop),this.particleSystem.useRampGradients&&(a.remapData&&this.remapData?a.remapData.copyFrom(this.remapData):a.remapData=new g.f(0,0,0,0)),this._randomNoiseCoordinates1&&(a._randomNoiseCoordinates1?(a._randomNoiseCoordinates1.copyFrom(this._randomNoiseCoordinates1),a._randomNoiseCoordinates2.copyFrom(this._randomNoiseCoordinates2)):(a._randomNoiseCoordinates1=this._randomNoiseCoordinates1.clone(),a._randomNoiseCoordinates2=this._randomNoiseCoordinates2.clone()))},a._Count=0,a}();!function(a){a[a.ATTACHED=0]="ATTACHED",a[a.END=1]="END"}(wo||(wo={}));var Co=function(){function a(a){if(this.particleSystem=a,this.type=wo.END,this.inheritDirection=!1,this.inheritedVelocityAmount=0,!a.emitter||!a.emitter.dispose){var b=Object(i.a)("BABYLON.AbstractMesh");a.emitter=new b("SubemitterSystemEmitter",a.getScene())}}return a.prototype.clone=function(){var b=this.particleSystem.emitter;b?b instanceof g.e?b=b.clone():-1!==b.getClassName().indexOf("Mesh")&&((b=new(Object(i.a)("BABYLON.Mesh"))("",b.getScene())).isVisible=!1):b=new g.e;b=new a(this.particleSystem.clone(this.particleSystem.name,b));return b.particleSystem.name+="Clone",b.type=this.type,b.inheritDirection=this.inheritDirection,b.inheritedVelocityAmount=this.inheritedVelocityAmount,b.particleSystem._disposeEmitterOnDispose=!0,b.particleSystem.disposeOnStop=!0,b},a.prototype.serialize=function(a){void 0===a&&(a=!1);var b={};return b.type=this.type,b.inheritDirection=this.inheritDirection,b.inheritedVelocityAmount=this.inheritedVelocityAmount,b.particleSystem=this.particleSystem.serialize(a),b},a._ParseParticleSystem=function(a,b,c,d){throw Object(Ka.a)("ParseParticle")},a.Parse=function(b,c,d){var e=b.particleSystem;e=new a(a._ParseParticleSystem(e,c,d,!0));return e.type=b.type,e.inheritDirection=b.inheritDirection,e.inheritedVelocityAmount=b.inheritedVelocityAmount,e.particleSystem._isSubEmitter=!0,e},a.prototype.dispose=function(){this.particleSystem.dispose()},a}();a=" varying vec2 vUV; varying vec4 vColor; uniform vec4 textureMask; uniform sampler2D diffuseSampler; #include #include #include #include #ifdef RAMPGRADIENT varying vec4 remapRanges; uniform sampler2D rampSampler; #endif void main(void) { #include vec4 textureColor=texture2D(diffuseSampler,vUV); vec4 baseColor=(textureColor*textureMask+(vec4(1.,1.,1.,1.)-textureMask))*vColor; #ifdef RAMPGRADIENT float alpha=baseColor.a; float remappedColorIndex=clamp((alpha-remapRanges.x)/remapRanges.y,0.0,1.0); vec4 rampColor=texture2D(rampSampler,vec2(1.0-remappedColorIndex,0.)); baseColor.rgb*=rampColor.rgb; float finalAlpha=baseColor.a; baseColor.a=clamp((alpha*rampColor.a-remapRanges.z)/remapRanges.w,0.0,1.0); #endif #ifdef BLENDMULTIPLYMODE float sourceAlpha=vColor.a*textureColor.a; baseColor.rgb=baseColor.rgb*sourceAlpha+vec3(1.0)*(1.0-sourceAlpha); #endif #ifdef IMAGEPROCESSINGPOSTPROCESS baseColor.rgb=toLinearSpace(baseColor.rgb); #else #ifdef IMAGEPROCESSING baseColor.rgb=toLinearSpace(baseColor.rgb); baseColor=applyImageProcessing(baseColor); #endif #endif gl_FragColor=baseColor; }";X.a.ShadersStore.particlesPixelShader=a;wi=" attribute vec3 position; attribute vec4 color; attribute float angle; attribute vec2 size; #ifdef ANIMATESHEET attribute float cellIndex; #endif #ifndef BILLBOARD attribute vec3 direction; #endif #ifdef BILLBOARDSTRETCHED attribute vec3 direction; #endif #ifdef RAMPGRADIENT attribute vec4 remapData; #endif attribute vec2 offset; uniform mat4 view; uniform mat4 projection; uniform vec2 translationPivot; #ifdef ANIMATESHEET uniform vec3 particlesInfos; #endif varying vec2 vUV; varying vec4 vColor; varying vec3 vPositionW; #ifdef RAMPGRADIENT varying vec4 remapRanges; #endif #if defined(BILLBOARD) && !defined(BILLBOARDY) && !defined(BILLBOARDSTRETCHED) uniform mat4 invView; #endif #include #ifdef BILLBOARD uniform vec3 eyePosition; #endif vec3 rotate(vec3 yaxis,vec3 rotatedCorner) { vec3 xaxis=normalize(cross(vec3(0.,1.0,0.),yaxis)); vec3 zaxis=normalize(cross(yaxis,xaxis)); vec3 row0=vec3(xaxis.x,xaxis.y,xaxis.z); vec3 row1=vec3(yaxis.x,yaxis.y,yaxis.z); vec3 row2=vec3(zaxis.x,zaxis.y,zaxis.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; return position+alignedCorner; } #ifdef BILLBOARDSTRETCHED vec3 rotateAlign(vec3 toCamera,vec3 rotatedCorner) { vec3 normalizedToCamera=normalize(toCamera); vec3 normalizedCrossDirToCamera=normalize(cross(normalize(direction),normalizedToCamera)); vec3 crossProduct=normalize(cross(normalizedToCamera,normalizedCrossDirToCamera)); vec3 row0=vec3(normalizedCrossDirToCamera.x,normalizedCrossDirToCamera.y,normalizedCrossDirToCamera.z); vec3 row1=vec3(crossProduct.x,crossProduct.y,crossProduct.z); vec3 row2=vec3(normalizedToCamera.x,normalizedToCamera.y,normalizedToCamera.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; return position+alignedCorner; } #endif void main(void) { vec2 cornerPos; cornerPos=(vec2(offset.x-0.5,offset.y-0.5)-translationPivot)*size+translationPivot; #ifdef BILLBOARD vec3 rotatedCorner; #ifdef BILLBOARDY rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=position-eyePosition; yaxis.y=0.; vPositionW=rotate(normalize(yaxis),rotatedCorner); vec3 viewPos=(view*vec4(vPositionW,1.0)).xyz; #elif defined(BILLBOARDSTRETCHED) rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 toCamera=position-eyePosition; vPositionW=rotateAlign(toCamera,rotatedCorner); vec3 viewPos=(view*vec4(vPositionW,1.0)).xyz; #else rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 viewPos=(view*vec4(position,1.0)).xyz+rotatedCorner; vPositionW=(invView*vec4(viewPos,1)).xyz; #endif #ifdef RAMPGRADIENT remapRanges=remapData; #endif gl_Position=projection*vec4(viewPos,1.0); #else vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=normalize(direction); vPositionW=rotate(yaxis,rotatedCorner); gl_Position=projection*view*vec4(vPositionW,1.0); #endif vColor=color; #ifdef ANIMATESHEET float rowOffset=floor(cellIndex*particlesInfos.z); float columnOffset=cellIndex-rowOffset/particlesInfos.z; vec2 uvScale=particlesInfos.xy; vec2 uvOffset=vec2(offset.x ,1.0-offset.y); vUV=(uvOffset+vec2(columnOffset,rowOffset))*uvScale; #else vUV=offset; #endif #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=vec4(vPositionW,1.0); #endif #include }";X.a.ShadersStore.particlesVertexShader=wi;var Do=function(a){function b(b,c,d,e,i,j){void 0===e&&(e=null),void 0===i&&(i=!1),void 0===j&&(j=.01);var k=a.call(this,b)||this;k._emitterInverseWorldMatrix=g.a.Identity(),k._inheritedVelocityOffset=new g.e,k.onDisposeObservable=new f.c,k.onStoppedObservable=new f.c,k._particles=new Array,k._stockParticles=new Array,k._newPartsExcess=0,k._vertexBuffers={},k._scaledColorStep=new h.b(0,0,0,0),k._colorDiff=new h.b(0,0,0,0),k._scaledDirection=g.e.Zero(),k._scaledGravity=g.e.Zero(),k._currentRenderId=-1,k._useInstancing=!1,k._started=!1,k._stopped=!1,k._actualFrame=0,k._currentEmitRate1=0,k._currentEmitRate2=0,k._currentStartSize1=0,k._currentStartSize2=0,k._rawTextureWidth=256,k._useRampGradients=!1,k._disposeEmitterOnDispose=!1,k.isLocal=!1,k._onBeforeDrawParticlesObservable=null,k.recycleParticle=function(a){var b=k._particles.pop();b!==a&&b.copyTo(a),k._stockParticles.push(b)},k._createParticle=function(){var a;if(0!==k._stockParticles.length?(a=k._stockParticles.pop())._reset():a=new Bo(k),k._subEmitters&&k._subEmitters.length>0){var b=k._subEmitters[Math.floor(Math.random()*k._subEmitters.length)];a._attachedSubEmitters=[],b.forEach(function(b){if(b.type===wo.ATTACHED){b=b.clone();a._attachedSubEmitters.push(b),b.particleSystem.start()}})}return a},k._emitFromParticle=function(a){if(k._subEmitters&&0!==k._subEmitters.length){var b=Math.floor(Math.random()*k._subEmitters.length);k._subEmitters[b].forEach(function(b){if(b.type===wo.END){b=b.clone();a._inheritParticleInfoToSubEmitter(b),b.particleSystem._rootParticleSystem=k,k.activeSubSystems.push(b.particleSystem),b.particleSystem.start()}})}},k._capacity=c,k._epsilon=j,k._isAnimationSheetEnabled=i,d&&"Scene"!==d.getClassName()?(k._engine=d,k.defaultProjectionMatrix=g.a.PerspectiveFovLH(.8,1,.1,100,k._engine.isNDCHalfZRange)):(k._scene=d||C.a.LastCreatedScene,k._engine=k._scene.getEngine(),k.uniqueId=k._scene.getUniqueId(),k._scene.particleSystems.push(k)),k._engine.getCaps().vertexArrayObject&&(k._vertexArrayObject=null),k._attachImageProcessingConfiguration(null),k._customWrappers={0:new Ec.a(k._engine)},k._customWrappers[0].effect=e,k._drawWrapper=new Ec.a(k._engine),k._useInstancing=k._engine.getCaps().instancedArrays,k._createIndexBuffer(),k._createVertexBuffers(),k.particleEmitterType=new ul;var n=null;return k.updateFunction=function(a){var b,c=null;k.noiseTexture&&(c=k.noiseTexture.getSize(),null===(b=k.noiseTexture.getContent())||void 0===b||b.then(function(a){n=a}));for(var d,b=function(){d=a[e];var b=k._scaledUpdateSpeed,f=d.age;if(d.age+=b,d.age>d.lifeTime){var i=d.age-f;b=(d.lifeTime-f)*b/i,d.age=d.lifeTime}f=d.age/d.lifeTime;k._colorGradients&&k._colorGradients.length>0?Ao.GetCurrentGradient(f,k._colorGradients,function(a,b,c){a!==d._currentColorGradient&&(d._currentColor1.copyFrom(d._currentColor2),b.getColorToRef(d._currentColor2),d._currentColorGradient=a),h.b.LerpToRef(d._currentColor1,d._currentColor2,c,d.color)}):(d.colorStep.scaleToRef(b,k._scaledColorStep),d.color.addInPlace(k._scaledColorStep),d.color.a<0&&(d.color.a=0)),k._angularSpeedGradients&&k._angularSpeedGradients.length>0&&Ao.GetCurrentGradient(f,k._angularSpeedGradients,function(a,b,c){a!==d._currentAngularSpeedGradient&&(d._currentAngularSpeed1=d._currentAngularSpeed2,d._currentAngularSpeed2=b.getFactor(),d._currentAngularSpeedGradient=a),d.angularSpeed=I.a.Lerp(d._currentAngularSpeed1,d._currentAngularSpeed2,c)}),d.angle+=d.angularSpeed*b;var j=b;if(k._velocityGradients&&k._velocityGradients.length>0&&Ao.GetCurrentGradient(f,k._velocityGradients,function(a,b,c){a!==d._currentVelocityGradient&&(d._currentVelocity1=d._currentVelocity2,d._currentVelocity2=b.getFactor(),d._currentVelocityGradient=a),j*=I.a.Lerp(d._currentVelocity1,d._currentVelocity2,c)}),d.direction.scaleToRef(j,k._scaledDirection),k._limitVelocityGradients&&k._limitVelocityGradients.length>0&&Ao.GetCurrentGradient(f,k._limitVelocityGradients,function(a,b,c){a!==d._currentLimitVelocityGradient&&(d._currentLimitVelocity1=d._currentLimitVelocity2,d._currentLimitVelocity2=b.getFactor(),d._currentLimitVelocityGradient=a);b=I.a.Lerp(d._currentLimitVelocity1,d._currentLimitVelocity2,c);d.direction.length()>b&&d.direction.scaleInPlace(k.limitVelocityDamping)}),k._dragGradients&&k._dragGradients.length>0&&Ao.GetCurrentGradient(f,k._dragGradients,function(a,b,c){a!==d._currentDragGradient&&(d._currentDrag1=d._currentDrag2,d._currentDrag2=b.getFactor(),d._currentDragGradient=a);b=I.a.Lerp(d._currentDrag1,d._currentDrag2,c);k._scaledDirection.scaleInPlace(1-b)}),k.isLocal&&d._localPosition?(d._localPosition.addInPlace(k._scaledDirection),g.e.TransformCoordinatesToRef(d._localPosition,k._emitterWorldMatrix,d.position)):d.position.addInPlace(k._scaledDirection),n&&c&&d._randomNoiseCoordinates1){i=k._fetchR(d._randomNoiseCoordinates1.x,d._randomNoiseCoordinates1.y,c.width,c.height,n);var l=k._fetchR(d._randomNoiseCoordinates1.z,d._randomNoiseCoordinates2.x,c.width,c.height,n),s=k._fetchR(d._randomNoiseCoordinates2.y,d._randomNoiseCoordinates2.z,c.width,c.height,n),t=g.c.Vector3[0],u=g.c.Vector3[1];t.copyFromFloats((2*i-1)*k.noiseStrength.x,(2*l-1)*k.noiseStrength.y,(2*s-1)*k.noiseStrength.z),t.scaleToRef(b,u),d.direction.addInPlace(u)}if(k.gravity.scaleToRef(b,k._scaledGravity),d.direction.addInPlace(k._scaledGravity),k._sizeGradients&&k._sizeGradients.length>0&&Ao.GetCurrentGradient(f,k._sizeGradients,function(a,b,c){a!==d._currentSizeGradient&&(d._currentSize1=d._currentSize2,d._currentSize2=b.getFactor(),d._currentSizeGradient=a),d.size=I.a.Lerp(d._currentSize1,d._currentSize2,c)}),k._useRampGradients&&(k._colorRemapGradients&&k._colorRemapGradients.length>0&&Ao.GetCurrentGradient(f,k._colorRemapGradients,function(a,b,c){var e=I.a.Lerp(a.factor1,b.factor1,c);a=I.a.Lerp(a.factor2,b.factor2,c);d.remapData.x=e,d.remapData.y=a-e}),k._alphaRemapGradients&&k._alphaRemapGradients.length>0&&Ao.GetCurrentGradient(f,k._alphaRemapGradients,function(a,b,c){var e=I.a.Lerp(a.factor1,b.factor1,c);a=I.a.Lerp(a.factor2,b.factor2,c);d.remapData.z=e,d.remapData.w=a-e})),k._isAnimationSheetEnabled&&d.updateCellIndex(),d._inheritParticleInfoToSubEmitters(),d.age>=d.lifeTime)return k._emitFromParticle(d),d._attachedSubEmitters&&(d._attachedSubEmitters.forEach(function(a){a.particleSystem.disposeOnStop=!0,a.particleSystem.stop()}),d._attachedSubEmitters=null),k.recycleParticle(d),e--,"continue"},e=0;eb.gradient?1:0})},b.prototype._removeFactorGradient=function(a,b){if(a)for(var c=0,d=0,e=a;db.gradient?1:0}),this._rampGradientsTexture&&(this._rampGradientsTexture.dispose(),this._rampGradientsTexture=null),this._createRampGradientTexture())},b.prototype.addRampGradient=function(a,b){this._rampGradients||(this._rampGradients=[]);a=new yo(a,b);return this._rampGradients.push(a),this._syncRampGradientTexture(),this},b.prototype.removeRampGradient=function(a){return this._removeGradientAndTexture(a,this._rampGradients,this._rampGradientsTexture),this._rampGradientsTexture=null,this._rampGradients&&this._rampGradients.length>0&&this._createRampGradientTexture(),this},b.prototype.addColorGradient=function(a,b,c){this._colorGradients||(this._colorGradients=[]);a=new xo(a,b,c);return this._colorGradients.push(a),this._colorGradients.sort(function(a,b){return a.gradientb.gradient?1:0}),this},b.prototype.removeColorGradient=function(a){if(!this._colorGradients)return this;for(var b=0,c=0,d=this._colorGradients;c0&&(this._currentEmitRateGradient=this._emitRateGradients[0],this._currentEmitRate1=this._currentEmitRateGradient.getFactor(),this._currentEmitRate2=this._currentEmitRate1),this._emitRateGradients.length>1&&(this._currentEmitRate2=this._emitRateGradients[1].getFactor())),this._startSizeGradients&&(this._startSizeGradients.length>0&&(this._currentStartSizeGradient=this._startSizeGradients[0],this._currentStartSize1=this._currentStartSizeGradient.getFactor(),this._currentStartSize2=this._currentStartSize1),this._startSizeGradients.length>1&&(this._currentStartSize2=this._startSizeGradients[1].getFactor())),this.preWarmCycles){-1!==(null===(a=this.emitter)||void 0===a?void 0:a.getClassName().indexOf("Mesh"))&&this.emitter.computeWorldMatrix(!0);var c=this.noiseTexture;if(c&&c.onGeneratedObservable)c.onGeneratedObservable.addOnce(function(){setTimeout(function(){for(var a=0;a0&&this._scene&&this._scene.beginAnimation(this,this.beginAnimationFrom,this.beginAnimationTo,this.beginAnimationLoop)}},b.prototype.stop=function(a){void 0===a&&(a=!0),this._stopped||(this.onStoppedObservable.notifyObservers(this),this._stopped=!0,a&&this._stopSubEmitters())},b.prototype.reset=function(){this._stockParticles=[],this._particles=[]},b.prototype._appendParticleVertex=function(a,c,d,e){a=a*this._vertexBufferSize;if(this._vertexData[a++]=c.position.x+this.worldOffset.x,this._vertexData[a++]=c.position.y+this.worldOffset.y,this._vertexData[a++]=c.position.z+this.worldOffset.z,this._vertexData[a++]=c.color.r,this._vertexData[a++]=c.color.g,this._vertexData[a++]=c.color.b,this._vertexData[a++]=c.color.a,this._vertexData[a++]=c.angle,this._vertexData[a++]=c.scale.x*c.size,this._vertexData[a++]=c.scale.y*c.size,this._isAnimationSheetEnabled&&(this._vertexData[a++]=c.cellIndex),this._isBillboardBased)this.billboardMode===b.BILLBOARDMODE_STRETCHED&&(this._vertexData[a++]=c.direction.x,this._vertexData[a++]=c.direction.y,this._vertexData[a++]=c.direction.z);else if(c._initialDirection){var f=c._initialDirection;this.isLocal&&(g.e.TransformNormalToRef(f,this._emitterWorldMatrix,g.c.Vector3[0]),f=g.c.Vector3[0]),0===f.x&&0===f.z&&(f.x=.001),this._vertexData[a++]=f.x,this._vertexData[a++]=f.y,this._vertexData[a++]=f.z}else{f=c.direction;this.isLocal&&(g.e.TransformNormalToRef(f,this._emitterWorldMatrix,g.c.Vector3[0]),f=g.c.Vector3[0]),0===f.x&&0===f.z&&(f.x=.001),this._vertexData[a++]=f.x,this._vertexData[a++]=f.y,this._vertexData[a++]=f.z}this._useRampGradients&&c.remapData&&(this._vertexData[a++]=c.remapData.x,this._vertexData[a++]=c.remapData.y,this._vertexData[a++]=c.remapData.z,this._vertexData[a++]=c.remapData.w),this._useInstancing||(this._isAnimationSheetEnabled&&(0===d?d=this._epsilon:1===d&&(d=1-this._epsilon),0===e?e=this._epsilon:1===e&&(e=1-this._epsilon)),this._vertexData[a++]=d,this._vertexData[a++]=e)},b.prototype._stopSubEmitters=function(){this.activeSubSystems&&(this.activeSubSystems.forEach(function(a){a.stop(!0)}),this.activeSubSystems=new Array)},b.prototype._removeFromRoot=function(){if(this._rootParticleSystem){var a=this._rootParticleSystem.activeSubSystems.indexOf(this);-1!==a&&this._rootParticleSystem.activeSubSystems.splice(a,1),this._rootParticleSystem=null}},b.prototype._update=function(a){var b,c=this;if(this._alive=this._particles.length>0,this.emitter.position){var d=this.emitter;this._emitterWorldMatrix=d.getWorldMatrix()}else{d=this.emitter;this._emitterWorldMatrix=g.a.Translation(d.x,d.y,d.z)}this._emitterWorldMatrix.invertToRef(this._emitterInverseWorldMatrix),this.updateFunction(this._particles);for(var e,d=function(){if(f._particles.length===f._capacity)return"break";if(b=f._createParticle(),f._particles.push(b),f.targetStopDuration&&f._lifeTimeGradients&&f._lifeTimeGradients.length>0){var a=I.a.Clamp(f._actualFrame/f.targetStopDuration);Ao.GetCurrentGradient(a,f._lifeTimeGradients,function(c,d){c=c;d=d;var e=c.getFactor(),f=d.getFactor();d=(a-c.gradient)/(d.gradient-c.gradient);b.lifeTime=I.a.Lerp(e,f,d)})}else b.lifeTime=I.a.RandomRange(f.minLifeTime,f.maxLifeTime);var d=I.a.RandomRange(f.minEmitPower,f.maxEmitPower);if(f.startPositionFunction?f.startPositionFunction(f._emitterWorldMatrix,b.position,b,f.isLocal):f.particleEmitterType.startPositionFunction(f._emitterWorldMatrix,b.position,b,f.isLocal),f.isLocal&&(b._localPosition?b._localPosition.copyFrom(b.position):b._localPosition=b.position.clone(),g.e.TransformCoordinatesToRef(b._localPosition,f._emitterWorldMatrix,b.position)),f.startDirectionFunction?f.startDirectionFunction(f._emitterWorldMatrix,b.direction,b,f.isLocal):f.particleEmitterType.startDirectionFunction(f._emitterWorldMatrix,b.direction,b,f.isLocal,f._emitterInverseWorldMatrix),0===d?b._initialDirection?b._initialDirection.copyFrom(b.direction):b._initialDirection=b.direction.clone():b._initialDirection=null,b.direction.scaleInPlace(d),f._sizeGradients&&0!==f._sizeGradients.length?(b._currentSizeGradient=f._sizeGradients[0],b._currentSize1=b._currentSizeGradient.getFactor(),b.size=b._currentSize1,f._sizeGradients.length>1?b._currentSize2=f._sizeGradients[1].getFactor():b._currentSize2=b._currentSize1):b.size=I.a.RandomRange(f.minSize,f.maxSize),b.scale.copyFromFloats(I.a.RandomRange(f.minScaleX,f.maxScaleX),I.a.RandomRange(f.minScaleY,f.maxScaleY)),f._startSizeGradients&&f._startSizeGradients[0]&&f.targetStopDuration){d=f._actualFrame/f.targetStopDuration;Ao.GetCurrentGradient(d,f._startSizeGradients,function(a,d,e){a!==c._currentStartSizeGradient&&(c._currentStartSize1=c._currentStartSize2,c._currentStartSize2=d.getFactor(),c._currentStartSizeGradient=a);d=I.a.Lerp(c._currentStartSize1,c._currentStartSize2,e);b.scale.scaleInPlace(d)})}f._angularSpeedGradients&&0!==f._angularSpeedGradients.length?(b._currentAngularSpeedGradient=f._angularSpeedGradients[0],b.angularSpeed=b._currentAngularSpeedGradient.getFactor(),b._currentAngularSpeed1=b.angularSpeed,f._angularSpeedGradients.length>1?b._currentAngularSpeed2=f._angularSpeedGradients[1].getFactor():b._currentAngularSpeed2=b._currentAngularSpeed1):b.angularSpeed=I.a.RandomRange(f.minAngularSpeed,f.maxAngularSpeed),b.angle=I.a.RandomRange(f.minInitialRotation,f.maxInitialRotation),f._velocityGradients&&f._velocityGradients.length>0&&(b._currentVelocityGradient=f._velocityGradients[0],b._currentVelocity1=b._currentVelocityGradient.getFactor(),f._velocityGradients.length>1?b._currentVelocity2=f._velocityGradients[1].getFactor():b._currentVelocity2=b._currentVelocity1),f._limitVelocityGradients&&f._limitVelocityGradients.length>0&&(b._currentLimitVelocityGradient=f._limitVelocityGradients[0],b._currentLimitVelocity1=b._currentLimitVelocityGradient.getFactor(),f._limitVelocityGradients.length>1?b._currentLimitVelocity2=f._limitVelocityGradients[1].getFactor():b._currentLimitVelocity2=b._currentLimitVelocity1),f._dragGradients&&f._dragGradients.length>0&&(b._currentDragGradient=f._dragGradients[0],b._currentDrag1=b._currentDragGradient.getFactor(),f._dragGradients.length>1?b._currentDrag2=f._dragGradients[1].getFactor():b._currentDrag2=b._currentDrag1),f._colorGradients&&0!==f._colorGradients.length?(b._currentColorGradient=f._colorGradients[0],b._currentColorGradient.getColorToRef(b.color),b._currentColor1.copyFrom(b.color),f._colorGradients.length>1?f._colorGradients[1].getColorToRef(b._currentColor2):b._currentColor2.copyFrom(b.color)):(e=I.a.RandomRange(0,1),h.b.LerpToRef(f.color1,f.color2,e,b.color),f.colorDead.subtractToRef(b.color,f._colorDiff),f._colorDiff.scaleToRef(1/b.lifeTime,b.colorStep)),f._isAnimationSheetEnabled&&(b._initialStartSpriteCellID=f.startSpriteCellID,b._initialEndSpriteCellID=f.endSpriteCellID,b._initialSpriteCellLoop=f.spriteCellLoop),b.direction.addInPlace(f._inheritedVelocityOffset),f._useRampGradients&&(b.remapData=new g.f(0,1,0,1)),f.noiseTexture&&(b._randomNoiseCoordinates1?(b._randomNoiseCoordinates1.copyFromFloats(Math.random(),Math.random(),Math.random()),b._randomNoiseCoordinates2.copyFromFloats(Math.random(),Math.random(),Math.random())):(b._randomNoiseCoordinates1=new g.e(Math.random(),Math.random(),Math.random()),b._randomNoiseCoordinates2=new g.e(Math.random(),Math.random(),Math.random()))),b._inheritParticleInfoToSubEmitters()},f=this,i=0;i-1)b=this.manualEmitCount,this._newPartsExcess=0,this.manualEmitCount=0;else{var d=this.emitRate;if(this._emitRateGradients&&this._emitRateGradients.length>0&&this.targetStopDuration){var e=this._actualFrame/this.targetStopDuration;Ao.GetCurrentGradient(e,this._emitRateGradients,function(a,b,e){a!==c._currentEmitRateGradient&&(c._currentEmitRate1=c._currentEmitRate2,c._currentEmitRate2=b.getFactor(),c._currentEmitRateGradient=a),d=I.a.Lerp(c._currentEmitRate1,c._currentEmitRate2,e)})}b=d*this._scaledUpdateSpeed>>0,this._newPartsExcess+=d*this._scaledUpdateSpeed-b}if(this._newPartsExcess>1&&(b+=this._newPartsExcess>>0,this._newPartsExcess-=this._newPartsExcess>>0),this._alive=!1,this._stopped?b=0:(this._actualFrame+=this._scaledUpdateSpeed,this.targetStopDuration&&this._actualFrame>=this.targetStopDuration&&this.stop()),this._update(b),this._stopped&&(this._alive||(this._started=!1,this.onAnimationEnd&&this.onAnimationEnd(),this.disposeOnStop&&this._scene&&this._scene._toBeDisposed.push(this))),!a){for(e=0,b=0;b=0&&(d.invertToRef(g.c.Matrix[0]),e.setMatrix("invView",g.c.Matrix[0])),void 0!==this._vertexArrayObject?(this._vertexArrayObject||(this._vertexArrayObject=this._engine.recordVertexArrayObject(this._vertexBuffers,this._indexBuffer,e)),this._engine.bindVertexArrayObject(this._vertexArrayObject,this._indexBuffer)):f.bindBuffers(this._vertexBuffers,this._indexBuffer,e),this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(e),a){case b.BLENDMODE_ADD:f.setAlphaMode(r.a.ALPHA_ADD);break;case b.BLENDMODE_ONEONE:f.setAlphaMode(r.a.ALPHA_ONEONE);break;case b.BLENDMODE_STANDARD:f.setAlphaMode(r.a.ALPHA_COMBINE);break;case b.BLENDMODE_MULTIPLY:f.setAlphaMode(r.a.ALPHA_MULTIPLY)}return this._onBeforeDrawParticlesObservable&&this._onBeforeDrawParticlesObservable.notifyObservers(e),this._useInstancing?f.drawArraysType(r.a.MATERIAL_TriangleStripDrawMode,0,4,this._particles.length):f.drawElementsType(r.a.MATERIAL_TriangleFillMode,0,6*this._particles.length),this._particles.length},b.prototype.render=function(){if(!this.isReady()||!this._particles.length)return 0;var a=this._engine;a.setState&&(a.setState(!1),this.forceDepthWrite&&a.setDepthWrite(!0));a=0;return a=this.blendMode===b.BLENDMODE_MULTIPLYADD?this._render(b.BLENDMODE_MULTIPLY)+this._render(b.BLENDMODE_ADD):this._render(this.blendMode),this._engine.unbindInstanceAttributes(),this._engine.setAlphaMode(r.a.ALPHA_DISABLE),a},b.prototype.dispose=function(a){if(void 0===a&&(a=!0),this._vertexBuffer&&(this._vertexBuffer.dispose(),this._vertexBuffer=null),this._spriteBuffer&&(this._spriteBuffer.dispose(),this._spriteBuffer=null),this._indexBuffer&&(this._engine._releaseBuffer(this._indexBuffer),this._indexBuffer=null),this._vertexArrayObject&&(this._engine.releaseVertexArrayObject(this._vertexArrayObject),this._vertexArrayObject=null),a&&this.particleTexture&&(this.particleTexture.dispose(),this.particleTexture=null),a&&this.noiseTexture&&(this.noiseTexture.dispose(),this.noiseTexture=null),this._rampGradientsTexture&&(this._rampGradientsTexture.dispose(),this._rampGradientsTexture=null),this._removeFromRoot(),this._subEmitters&&this._subEmitters.length){for(a=0;a-1&&this._scene.particleSystems.splice(a,1),this._scene._activeParticleSystems.dispose());this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onStoppedObservable.clear(),this.reset()},b.prototype.clone=function(a,c){var d=Object(l.a)({},this._customWrappers),e=null,f=this._engine;if(f.createEffectForParticles&&null!=this.customShader){var g=(e=this.customShader).shaderOptions.defines.length>0?e.shaderOptions.defines.join(" "):"";d[0]=f.createEffectForParticles(e.shaderPath.fragmentElement,e.shaderOptions.uniforms,e.shaderOptions.samplers,g)}f=this.serialize();g=b.Parse(f,this._scene||this._engine,this._rootUrl);return g.name=a,g.customShader=e,g._customWrappers=d,void 0===c&&(c=this.emitter),this.noiseTexture&&(g.noiseTexture=this.noiseTexture.clone()),g.emitter=c,this.preventAutoStart||g.start(),g},b.prototype.serialize=function(a){void 0===a&&(a=!1);var c={};if(b._Serialize(c,this,a),c.textureMask=this.textureMask.asArray(),c.customShader=this.customShader,c.preventAutoStart=this.preventAutoStart,this.subEmitters){c.subEmitters=[],this._subEmitters||this._prepareSubEmitterInternalArray();for(var d=0,e=this._subEmitters;d0?k.shaderOptions.defines.join(" "):"";j=g.createEffectForParticles(k.shaderPath.fragmentElement,k.shaderOptions.uniforms,k.shaderOptions.samplers,m)}g=new b(i,f||a.capacity,c,j,a.isAnimationSheetEnabled);if(g.customShader=k,g._rootUrl=d,a.id&&(g.id=a.id),a.subEmitters){g.subEmitters=[];for(m=0,i=a.subEmitters;m #include #include #include void main() { #include vec4 textureColor=texture2D(diffuseSampler,vUV); gl_FragColor=textureColor*vColor; #ifdef BLENDMULTIPLYMODE float alpha=vColor.a*textureColor.a; gl_FragColor.rgb=gl_FragColor.rgb*alpha+vec3(1.0)*(1.0-alpha); #endif #ifdef IMAGEPROCESSINGPOSTPROCESS gl_FragColor.rgb=toLinearSpace(gl_FragColor.rgb); #else #ifdef IMAGEPROCESSING gl_FragColor.rgb=toLinearSpace(gl_FragColor.rgb); gl_FragColor=applyImageProcessing(gl_FragColor); #endif #endif } ";X.a.ShadersStore.gpuRenderParticlesPixelShader=a;wi="#ifdef CLIPPLANE uniform vec4 vClipPlane; out float fClipDistance; #endif #ifdef CLIPPLANE2 uniform vec4 vClipPlane2; out float fClipDistance2; #endif #ifdef CLIPPLANE3 uniform vec4 vClipPlane3; out float fClipDistance3; #endif #ifdef CLIPPLANE4 uniform vec4 vClipPlane4; out float fClipDistance4; #endif #ifdef CLIPPLANE5 uniform vec4 vClipPlane5; out float fClipDistance5; #endif #ifdef CLIPPLANE6 uniform vec4 vClipPlane6; out float fClipDistance6; #endif";X.a.IncludesShadersStore.clipPlaneVertexDeclaration2=wi;rb="precision highp float; uniform mat4 view; uniform mat4 projection; uniform vec2 translationPivot; uniform vec3 worldOffset; #ifdef LOCAL uniform mat4 emitterWM; #endif attribute vec3 position; attribute float age; attribute float life; attribute vec3 size; #ifndef BILLBOARD attribute vec3 initialDirection; #endif #ifdef BILLBOARDSTRETCHED attribute vec3 direction; #endif attribute float angle; #ifdef ANIMATESHEET attribute float cellIndex; #endif attribute vec2 offset; attribute vec2 uv; varying vec2 vUV; varying vec4 vColor; varying vec3 vPositionW; #if defined(BILLBOARD) && !defined(BILLBOARDY) && !defined(BILLBOARDSTRETCHED) uniform mat4 invView; #endif #include #ifdef COLORGRADIENTS uniform sampler2D colorGradientSampler; #else uniform vec4 colorDead; attribute vec4 color; #endif #ifdef ANIMATESHEET uniform vec3 sheetInfos; #endif #ifdef BILLBOARD uniform vec3 eyePosition; #endif vec3 rotate(vec3 yaxis,vec3 rotatedCorner) { vec3 xaxis=normalize(cross(vec3(0.,1.0,0.),yaxis)); vec3 zaxis=normalize(cross(yaxis,xaxis)); vec3 row0=vec3(xaxis.x,xaxis.y,xaxis.z); vec3 row1=vec3(yaxis.x,yaxis.y,yaxis.z); vec3 row2=vec3(zaxis.x,zaxis.y,zaxis.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; #ifdef LOCAL return ((emitterWM*vec4(position,1.0)).xyz+worldOffset)+alignedCorner; #else return (position+worldOffset)+alignedCorner; #endif } #ifdef BILLBOARDSTRETCHED vec3 rotateAlign(vec3 toCamera,vec3 rotatedCorner) { vec3 normalizedToCamera=normalize(toCamera); vec3 normalizedCrossDirToCamera=normalize(cross(normalize(direction),normalizedToCamera)); vec3 crossProduct=normalize(cross(normalizedToCamera,normalizedCrossDirToCamera)); vec3 row0=vec3(normalizedCrossDirToCamera.x,normalizedCrossDirToCamera.y,normalizedCrossDirToCamera.z); vec3 row1=vec3(crossProduct.x,crossProduct.y,crossProduct.z); vec3 row2=vec3(normalizedToCamera.x,normalizedToCamera.y,normalizedToCamera.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; #ifdef LOCAL return ((emitterWM*vec4(position,1.0)).xyz+worldOffset)+alignedCorner; #else return (position+worldOffset)+alignedCorner; #endif } #endif void main() { #ifdef ANIMATESHEET float rowOffset=floor(cellIndex/sheetInfos.z); float columnOffset=cellIndex-rowOffset*sheetInfos.z; vec2 uvScale=sheetInfos.xy; vec2 uvOffset=vec2(uv.x ,1.0-uv.y); vUV=(uvOffset+vec2(columnOffset,rowOffset))*uvScale; #else vUV=uv; #endif float ratio=age/life; #ifdef COLORGRADIENTS vColor=texture2D(colorGradientSampler,vec2(ratio,0)); #else vColor=color*vec4(1.0-ratio)+colorDead*vec4(ratio); #endif vec2 cornerPos=(offset-translationPivot)*size.yz*size.x+translationPivot; #ifdef BILLBOARD vec4 rotatedCorner; rotatedCorner.w=0.; #ifdef BILLBOARDY rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=(position+worldOffset)-eyePosition; yaxis.y=0.; vPositionW=rotate(normalize(yaxis),rotatedCorner.xyz); vec4 viewPosition=(view*vec4(vPositionW,1.0)); #elif defined(BILLBOARDSTRETCHED) rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 toCamera=(position+worldOffset)-eyePosition; vPositionW=rotateAlign(toCamera,rotatedCorner.xyz); vec4 viewPosition=(view*vec4(vPositionW,1.0)); #else rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; #ifdef LOCAL vec4 viewPosition=view*vec4(((emitterWM*vec4(position,1.0)).xyz+worldOffset),1.0)+rotatedCorner; #else vec4 viewPosition=view*vec4((position+worldOffset),1.0)+rotatedCorner; #endif vPositionW=(invView*viewPosition).xyz; #endif #else vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=0.; rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); vec3 yaxis=normalize(initialDirection); vPositionW=rotate(yaxis,rotatedCorner); vec4 viewPosition=view*vec4(vPositionW,1.0); #endif gl_Position=projection*viewPosition; #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=vec4(vPositionW,1.0); #endif #include }";X.a.ShadersStore.gpuRenderParticlesVertexShader=rb;var Eo=function(a){function b(b,c,d,e,h){void 0===e&&(e=null),void 0===h&&(h=!1);b=a.call(this,b)||this;if(b.layerMask=268435455,b._accumulatedCount=0,b._targetIndex=0,b._currentRenderId=-1,b._currentRenderingCameraUniqueId=-1,b._started=!1,b._stopped=!1,b._timeDelta=0,b._actualFrame=0,b._rawTextureWidth=256,b.onDisposeObservable=new f.c,b.onStoppedObservable=new f.c,b.forceDepthWrite=!1,b._preWarmDone=!1,b.isLocal=!1,b._onBeforeDrawParticlesObservable=null,d&&"Scene"!==d.getClassName()?(b._engine=d,b.defaultProjectionMatrix=g.a.PerspectiveFovLH(.8,1,.1,100,b._engine.isNDCHalfZRange)):(b._scene=d||C.a.LastCreatedScene,b._engine=b._scene.getEngine(),b.uniqueId=b._scene.getUniqueId(),b._scene.particleSystems.push(b)),b._engine.getCaps().supportComputeShaders){if(!Object(i.a)("BABYLON.ComputeShaderParticleSystem"))throw new Error("The ComputeShaderParticleSystem class is not available! Make sure you have imported it.");b._platform=new(Object(i.a)("BABYLON.ComputeShaderParticleSystem"))(b,b._engine)}else{if(!Object(i.a)("BABYLON.WebGL2ParticleSystem"))throw new Error("The WebGL2ParticleSystem class is not available! Make sure you have imported it.");b._platform=new(Object(i.a)("BABYLON.WebGL2ParticleSystem"))(b,b._engine)}b._customWrappers={0:new Ec.a(b._engine)},b._customWrappers[0].effect=e,b._drawWrapper=new Ec.a(b._engine),b._attachImageProcessingConfiguration(null),(c=null!=c?c:{}).randomTextureSize||delete c.randomTextureSize;e=Object(l.a)({capacity:5e4,randomTextureSize:b._engine.getCaps().maxTextureSize},c);c=c;isFinite(c)&&(e.capacity=c),b._capacity=e.capacity,b._activeCount=e.capacity,b._currentActiveCount=0,b._isAnimationSheetEnabled=h,b.particleEmitterType=new ul;for(c=Math.min(b._engine.getCaps().maxTextureSize,e.randomTextureSize),h=[],e=0;e1||C.a.LastCreatedEngine.getCaps().supportComputeShaders)},enumerable:!1,configurable:!0}),b.prototype.getCapacity=function(){return this._capacity},Object.defineProperty(b.prototype,"activeParticleCount",{get:function(){return this._activeCount},set:function(a){this._activeCount=Math.min(a,this._capacity)},enumerable:!1,configurable:!0}),b.prototype.isReady=function(){if(!this.emitter||this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.isReady()||!this.particleTexture||!this.particleTexture.isReady())return!1;if(this.blendMode!==Do.BLENDMODE_MULTIPLYADD){if(!this._getWrapper(this.blendMode).effect.isReady())return!1}else{if(!this._getWrapper(Do.BLENDMODE_MULTIPLY).effect.isReady())return!1;if(!this._getWrapper(Do.BLENDMODE_ADD).effect.isReady())return!1}return this._platform.isUpdateBufferCreated()?this._platform.isUpdateBufferReady():(this._recreateUpdateEffect(),!1)},b.prototype.isStarted=function(){return this._started},b.prototype.isStopped=function(){return this._stopped},b.prototype.isStopping=function(){return!1},b.prototype.getActiveCount=function(){return this._currentActiveCount},b.prototype.start=function(a){var b=this;if(void 0===a&&(a=this.startDelay),!this.targetStopDuration&&this._hasTargetStopDurationDependantGradient())throw"Particle system started with a targetStopDuration dependant gradient (eg. startSizeGradients) but no targetStopDuration set";a?setTimeout(function(){b.start(0)},a):(this._started=!0,this._stopped=!1,this._preWarmDone=!1,this.beginAnimationOnStart&&this.animations&&this.animations.length>0&&this._scene&&this._scene.beginAnimation(this,this.beginAnimationFrom,this.beginAnimationTo,this.beginAnimationLoop))},b.prototype.stop=function(){this._stopped||(this._stopped=!0)},b.prototype.reset=function(){this._releaseBuffers(),this._platform.releaseVertexBuffers(),this._currentActiveCount=0,this._targetIndex=0},b.prototype.getClassName=function(){return"GPUParticleSystem"},b.prototype.getCustomEffect=function(a){return void 0===a&&(a=0),null!==(a=null===(a=this._customWrappers[a])||void 0===a?void 0:a.effect)&&void 0!==a?a:this._customWrappers[0].effect},b.prototype._getCustomDrawWrapper=function(a){return void 0===a&&(a=0),null!==(a=this._customWrappers[a])&&void 0!==a?a:this._customWrappers[0]},b.prototype.setCustomEffect=function(a,b){void 0===b&&(b=0),this._customWrappers[b]=new Ec.a(this._engine),this._customWrappers[b].effect=a},Object.defineProperty(b.prototype,"onBeforeDrawParticlesObservable",{get:function(){return this._onBeforeDrawParticlesObservable||(this._onBeforeDrawParticlesObservable=new f.c),this._onBeforeDrawParticlesObservable},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vertexShaderName",{get:function(){return"gpuRenderParticles"},enumerable:!1,configurable:!0}),b.prototype._removeGradientAndTexture=function(b,c,d){return a.prototype._removeGradientAndTexture.call(this,b,c,d),this._releaseBuffers(),this},b.prototype.addColorGradient=function(a,b,c){this._colorGradients||(this._colorGradients=[]);c=new xo(a,b);return this._colorGradients.push(c),this._refreshColorGradient(!0),this._releaseBuffers(),this},b.prototype._refreshColorGradient=function(a){void 0===a&&(a=!1),this._colorGradients&&(a&&this._colorGradients.sort(function(a,b){return a.gradientb.gradient?1:0}),this._colorGradientsTexture&&(this._colorGradientsTexture.dispose(),this._colorGradientsTexture=null))},b.prototype.forceRefreshGradients=function(){this._refreshColorGradient(),this._refreshFactorGradient(this._sizeGradients,"_sizeGradientsTexture"),this._refreshFactorGradient(this._angularSpeedGradients,"_angularSpeedGradientsTexture"),this._refreshFactorGradient(this._velocityGradients,"_velocityGradientsTexture"),this._refreshFactorGradient(this._limitVelocityGradients,"_limitVelocityGradientsTexture"),this._refreshFactorGradient(this._dragGradients,"_dragGradientsTexture"),this.reset()},b.prototype.removeColorGradient=function(a){return this._removeGradientAndTexture(a,this._colorGradients,this._colorGradientsTexture),this._colorGradientsTexture=null,this},b.prototype._addFactorGradient=function(a,b,c){b=new zo(b,c);a.push(b),this._releaseBuffers()},b.prototype.addSizeGradient=function(a,b){return this._sizeGradients||(this._sizeGradients=[]),this._addFactorGradient(this._sizeGradients,a,b),this._refreshFactorGradient(this._sizeGradients,"_sizeGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeSizeGradient=function(a){return this._removeGradientAndTexture(a,this._sizeGradients,this._sizeGradientsTexture),this._sizeGradientsTexture=null,this},b.prototype._refreshFactorGradient=function(a,b,c){(void 0===c&&(c=!1),a)&&(c&&a.sort(function(a,b){return a.gradientb.gradient?1:0}),this[b]&&(this[b].dispose(),this[b]=null))},b.prototype.addAngularSpeedGradient=function(a,b){return this._angularSpeedGradients||(this._angularSpeedGradients=[]),this._addFactorGradient(this._angularSpeedGradients,a,b),this._refreshFactorGradient(this._angularSpeedGradients,"_angularSpeedGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeAngularSpeedGradient=function(a){return this._removeGradientAndTexture(a,this._angularSpeedGradients,this._angularSpeedGradientsTexture),this._angularSpeedGradientsTexture=null,this},b.prototype.addVelocityGradient=function(a,b){return this._velocityGradients||(this._velocityGradients=[]),this._addFactorGradient(this._velocityGradients,a,b),this._refreshFactorGradient(this._velocityGradients,"_velocityGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeVelocityGradient=function(a){return this._removeGradientAndTexture(a,this._velocityGradients,this._velocityGradientsTexture),this._velocityGradientsTexture=null,this},b.prototype.addLimitVelocityGradient=function(a,b){return this._limitVelocityGradients||(this._limitVelocityGradients=[]),this._addFactorGradient(this._limitVelocityGradients,a,b),this._refreshFactorGradient(this._limitVelocityGradients,"_limitVelocityGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeLimitVelocityGradient=function(a){return this._removeGradientAndTexture(a,this._limitVelocityGradients,this._limitVelocityGradientsTexture),this._limitVelocityGradientsTexture=null,this},b.prototype.addDragGradient=function(a,b){return this._dragGradients||(this._dragGradients=[]),this._addFactorGradient(this._dragGradients,a,b),this._refreshFactorGradient(this._dragGradients,"_dragGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeDragGradient=function(a){return this._removeGradientAndTexture(a,this._dragGradients,this._dragGradientsTexture),this._dragGradientsTexture=null,this},b.prototype.addEmitRateGradient=function(a,b,c){return this},b.prototype.removeEmitRateGradient=function(a){return this},b.prototype.addStartSizeGradient=function(a,b,c){return this},b.prototype.removeStartSizeGradient=function(a){return this},b.prototype.addColorRemapGradient=function(a,b,c){return this},b.prototype.removeColorRemapGradient=function(){return this},b.prototype.addAlphaRemapGradient=function(a,b,c){return this},b.prototype.removeAlphaRemapGradient=function(){return this},b.prototype.addRampGradient=function(a,b){return this},b.prototype.removeRampGradient=function(){return this},b.prototype.getRampGradients=function(){return null},Object.defineProperty(b.prototype,"useRampGradients",{get:function(){return!1},set:function(a){},enumerable:!1,configurable:!0}),b.prototype.addLifeTimeGradient=function(a,b,c){return this},b.prototype.removeLifeTimeGradient=function(a){return this},b.prototype._reset=function(){this._releaseBuffers()},b.prototype._createVertexBuffers=function(a,b,c){var d={};d.position=b.createVertexBuffer("position",0,3,this._attributesStrideSize,!0);var e=3;d.age=b.createVertexBuffer("age",e,1,this._attributesStrideSize,!0),e+=1,d.size=b.createVertexBuffer("size",e,3,this._attributesStrideSize,!0),e+=3,d.life=b.createVertexBuffer("life",e,1,this._attributesStrideSize,!0),e+=1,e+=4,this.billboardMode===Do.BILLBOARDMODE_STRETCHED&&(d.direction=b.createVertexBuffer("direction",e,3,this._attributesStrideSize,!0)),e+=3,this._platform.alignDataInBuffer&&(e+=1),this.particleEmitterType instanceof Cl&&(e+=3,this._platform.alignDataInBuffer&&(e+=1)),this._colorGradientsTexture||(d.color=b.createVertexBuffer("color",e,4,this._attributesStrideSize,!0),e+=4),this._isBillboardBased||(d.initialDirection=b.createVertexBuffer("initialDirection",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1)),this.noiseTexture&&(d.noiseCoordinates1=b.createVertexBuffer("noiseCoordinates1",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1),d.noiseCoordinates2=b.createVertexBuffer("noiseCoordinates2",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1)),d.angle=b.createVertexBuffer("angle",e,1,this._attributesStrideSize,!0),this._angularSpeedGradientsTexture?e++:e+=2,this._isAnimationSheetEnabled&&(d.cellIndex=b.createVertexBuffer("cellIndex",e,1,this._attributesStrideSize,!0),e+=1,this.spriteRandomStartCell&&(d.cellStartOffset=b.createVertexBuffer("cellStartOffset",e,1,this._attributesStrideSize,!0),e+=1)),d.offset=c.createVertexBuffer("offset",0,2),d.uv=c.createVertexBuffer("uv",2,2),this._platform.createVertexBuffers(a,d)},b.prototype._initialize=function(a){if(void 0===a&&(a=!1),!this._buffer0||a){a=this._engine;var b=new Array;this._attributesStrideSize=21,this._targetIndex=0,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1),this.particleEmitterType instanceof Cl&&(this._attributesStrideSize+=3,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1)),this.isBillboardBased||(this._attributesStrideSize+=3,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1)),this._colorGradientsTexture&&(this._attributesStrideSize-=4),this._angularSpeedGradientsTexture&&(this._attributesStrideSize-=1),this._isAnimationSheetEnabled&&(this._attributesStrideSize+=1,this.spriteRandomStartCell&&(this._attributesStrideSize+=1)),this.noiseTexture&&(this._attributesStrideSize+=6,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=2)),this._platform.alignDataInBuffer&&(this._attributesStrideSize+=3-(this._attributesStrideSize+3&3));for(var c=this.particleEmitterType instanceof Cl,d=g.c.Vector3[0],e=0,f=0;f0;)b.push(0)}h=new Float32Array([.5,.5,1,1,-.5,.5,0,1,.5,-.5,1,0,-.5,-.5,0,0]);c=this._platform.createParticleBuffer(b);e=this._platform.createParticleBuffer(b);this._buffer0=new W.a(a,c,!1,this._attributesStrideSize),this._buffer1=new W.a(a,e,!1,this._attributesStrideSize),this._spriteBuffer=new W.a(a,h,!1,4),this._createVertexBuffers(this._buffer0,this._buffer1,this._spriteBuffer),this._createVertexBuffers(this._buffer1,this._buffer0,this._spriteBuffer),this._sourceBuffer=this._buffer0,this._targetBuffer=this._buffer1}},b.prototype._recreateUpdateEffect=function(){var a=this.particleEmitterType?this.particleEmitterType.getEffectDefines():"";this._isBillboardBased&&(a+=" #define BILLBOARD"),this._colorGradientsTexture&&(a+=" #define COLORGRADIENTS"),this._sizeGradientsTexture&&(a+=" #define SIZEGRADIENTS"),this._angularSpeedGradientsTexture&&(a+=" #define ANGULARSPEEDGRADIENTS"),this._velocityGradientsTexture&&(a+=" #define VELOCITYGRADIENTS"),this._limitVelocityGradientsTexture&&(a+=" #define LIMITVELOCITYGRADIENTS"),this._dragGradientsTexture&&(a+=" #define DRAGGRADIENTS"),this.isAnimationSheetEnabled&&(a+=" #define ANIMATESHEET",this.spriteRandomStartCell&&(a+=" #define ANIMATESHEETRANDOMSTART")),this.noiseTexture&&(a+=" #define NOISE"),this.isLocal&&(a+=" #define LOCAL"),this._platform.isUpdateBufferCreated()&&this._cachedUpdateDefines===a||(this._cachedUpdateDefines=a,this._updateBuffer=this._platform.createUpdateBuffer(a))},b.prototype._getWrapper=function(a){var b=this._getCustomDrawWrapper(a);if(null==b?void 0:b.effect)return b;b=[];this.fillDefines(b,a);a=b.join(" ");if(this._drawWrapper.defines!==a){this._drawWrapper.defines=a;b=[];var c=[],d=[];this.fillUniformsAttributesAndSamplerNames(c,b,d),this._drawWrapper.effect=this._engine.createEffect("gpuRenderParticles",b,c,d,a)}return this._drawWrapper},b._GetAttributeNamesOrOptions=function(a,b,c,d){void 0===a&&(a=!1),void 0===b&&(b=!1),void 0===c&&(c=!1),void 0===d&&(d=!1);var e=[W.b.PositionKind,"age","life","size","angle"];return a||e.push(W.b.ColorKind),b&&e.push("cellIndex"),c||e.push("initialDirection"),d||e.push("direction"),e.push("offset",W.b.UVKind),e},b._GetEffectCreationOptions=function(a){void 0===a&&(a=!1);var b=["emitterWM","worldOffset","view","projection","colorDead","invView","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","translationPivot","eyePosition"];return a&&b.push("sheetInfos"),b},b.prototype.fillDefines=function(a,b){if(void 0===b&&(b=0),this._scene&&(this._scene.clipPlane&&a.push("#define CLIPPLANE"),this._scene.clipPlane2&&a.push("#define CLIPPLANE2"),this._scene.clipPlane3&&a.push("#define CLIPPLANE3"),this._scene.clipPlane4&&a.push("#define CLIPPLANE4"),this._scene.clipPlane5&&a.push("#define CLIPPLANE5"),this._scene.clipPlane6&&a.push("#define CLIPPLANE6")),b===Do.BLENDMODE_MULTIPLY&&a.push("#define BLENDMULTIPLYMODE"),this.isLocal&&a.push("#define LOCAL"),this._isBillboardBased)switch(a.push("#define BILLBOARD"),this.billboardMode){case Do.BILLBOARDMODE_Y:a.push("#define BILLBOARDY");break;case Do.BILLBOARDMODE_STRETCHED:a.push("#define BILLBOARDSTRETCHED");break;case Do.BILLBOARDMODE_ALL:a.push("#define BILLBOARDMODE_ALL")}this._colorGradientsTexture&&a.push("#define COLORGRADIENTS"),this.isAnimationSheetEnabled&&a.push("#define ANIMATESHEET"),this._imageProcessingConfiguration&&(this._imageProcessingConfiguration.prepareDefines(this._imageProcessingConfigurationDefines),a.push(""+this._imageProcessingConfigurationDefines.toString()))},b.prototype.fillUniformsAttributesAndSamplerNames=function(a,c,d){c.push.apply(c,b._GetAttributeNamesOrOptions(!!this._colorGradientsTexture,this._isAnimationSheetEnabled,this._isBillboardBased,this._isBillboardBased&&this.billboardMode===Do.BILLBOARDMODE_STRETCHED)),a.push.apply(a,b._GetEffectCreationOptions(this._isAnimationSheetEnabled)),d.push("diffuseSampler","colorGradientSampler"),this._imageProcessingConfiguration&&(od.a.PrepareUniforms(a,this._imageProcessingConfigurationDefines),od.a.PrepareSamplers(d,this._imageProcessingConfigurationDefines))},b.prototype.animate=function(a){void 0===a&&(a=!1),this._timeDelta=this.updateSpeed*(a?this.preWarmStepOffset:(null===(a=this._scene)||void 0===a?void 0:a.getAnimationRatio())||1),this._actualFrame+=this._timeDelta,this._stopped||this.targetStopDuration&&this._actualFrame>=this.targetStopDuration&&this.stop()},b.prototype._createFactorGradientTexture=function(a,b){var c=this[b];if(a&&a.length&&!c){for(var d=new Float32Array(this._rawTextureWidth),e=0;e=0){b=d.clone();b.invert(),e.setMatrix("invView",b)}switch(this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(e),a){case Do.BLENDMODE_ADD:this._engine.setAlphaMode(r.a.ALPHA_ADD);break;case Do.BLENDMODE_ONEONE:this._engine.setAlphaMode(r.a.ALPHA_ONEONE);break;case Do.BLENDMODE_STANDARD:this._engine.setAlphaMode(r.a.ALPHA_COMBINE);break;case Do.BLENDMODE_MULTIPLY:this._engine.setAlphaMode(r.a.ALPHA_MULTIPLY)}return this._platform.bindDrawBuffers(this._targetIndex,e),this._onBeforeDrawParticlesObservable&&this._onBeforeDrawParticlesObservable.notifyObservers(e),this._engine.drawArraysType(r.a.MATERIAL_TriangleStripDrawMode,0,4,this._currentActiveCount),this._engine.setAlphaMode(r.a.ALPHA_DISABLE),this._currentActiveCount},b.prototype.render=function(a,b){if(void 0===a&&(a=!1),void 0===b&&(b=!1),!this._started)return 0;if(this._createColorGradientTexture(),this._createSizeGradientTexture(),this._createAngularSpeedGradientTexture(),this._createVelocityGradientTexture(),this._createLimitVelocityGradientTexture(),this._createDragGradientTexture(),this._recreateUpdateEffect(),!this.isReady())return 0;if(!a&&this._scene){if(!this._preWarmDone&&this.preWarmCycles){for(var c=0;c1){c=0|this._accumulatedCount;this._accumulatedCount-=c,this._currentActiveCount=Math.min(this._activeCount,this._currentActiveCount+c)}if(!this._currentActiveCount)return 0;if(this.emitter.position)c=this.emitter.getWorldMatrix();else{var d=this.emitter;c=g.a.Translation(d.x,d.y,d.z)}d=this._engine;this._platform.preUpdateParticleBuffer(),this._updateBuffer.setFloat("currentCount",this._currentActiveCount),this._updateBuffer.setFloat("timeDelta",this._timeDelta),this._updateBuffer.setFloat("stopFactor",this._stopped?0:1),this._updateBuffer.setInt("randomTextureSize",this._randomTextureSize),this._updateBuffer.setFloat2("lifeTime",this.minLifeTime,this.maxLifeTime),this._updateBuffer.setFloat2("emitPower",this.minEmitPower,this.maxEmitPower),this._colorGradientsTexture||(this._updateBuffer.setDirectColor4("color1",this.color1),this._updateBuffer.setDirectColor4("color2",this.color2)),this._updateBuffer.setFloat2("sizeRange",this.minSize,this.maxSize),this._updateBuffer.setFloat4("scaleRange",this.minScaleX,this.maxScaleX,this.minScaleY,this.maxScaleY),this._updateBuffer.setFloat4("angleRange",this.minAngularSpeed,this.maxAngularSpeed,this.minInitialRotation,this.maxInitialRotation),this._updateBuffer.setVector3("gravity",this.gravity),this._limitVelocityGradientsTexture&&this._updateBuffer.setFloat("limitVelocityDamping",this.limitVelocityDamping),this.particleEmitterType&&this.particleEmitterType.applyToShader(this._updateBuffer),this._isAnimationSheetEnabled&&this._updateBuffer.setFloat4("cellInfos",this.startSpriteCellID,this.endSpriteCellID,this.spriteCellChangeSpeed,this.spriteCellLoop?1:0),this.noiseTexture&&this._updateBuffer.setVector3("noiseStrength",this.noiseStrength),this.isLocal||this._updateBuffer.setMatrix("emitterWM",c),this._platform.updateParticleBuffer(this._targetIndex,this._targetBuffer,this._currentActiveCount);var e=0;a||b||(d.setState(!1),this.forceDepthWrite&&d.setDepthWrite(!0),e=this.blendMode===Do.BLENDMODE_MULTIPLYADD?this._render(Do.BLENDMODE_MULTIPLY,c)+this._render(Do.BLENDMODE_ADD,c):this._render(this.blendMode,c),this._engine.setAlphaMode(r.a.ALPHA_DISABLE)),this._targetIndex++,2===this._targetIndex&&(this._targetIndex=0);a=this._sourceBuffer;return this._sourceBuffer=this._targetBuffer,this._targetBuffer=a,e},b.prototype.rebuild=function(){this._initialize(!0)},b.prototype._releaseBuffers=function(){this._buffer0&&(this._buffer0.dispose(),this._buffer0=null),this._buffer1&&(this._buffer1.dispose(),this._buffer1=null),this._spriteBuffer&&(this._spriteBuffer.dispose(),this._spriteBuffer=null),this._platform.releaseBuffers()},b.prototype.dispose=function(a){if(void 0===a&&(a=!0),this._scene){var b=this._scene.particleSystems.indexOf(this);b>-1&&this._scene.particleSystems.splice(b,1)}this._releaseBuffers(),this._platform.releaseVertexBuffers(),this._colorGradientsTexture&&(this._colorGradientsTexture.dispose(),this._colorGradientsTexture=null),this._sizeGradientsTexture&&(this._sizeGradientsTexture.dispose(),this._sizeGradientsTexture=null),this._angularSpeedGradientsTexture&&(this._angularSpeedGradientsTexture.dispose(),this._angularSpeedGradientsTexture=null),this._velocityGradientsTexture&&(this._velocityGradientsTexture.dispose(),this._velocityGradientsTexture=null),this._limitVelocityGradientsTexture&&(this._limitVelocityGradientsTexture.dispose(),this._limitVelocityGradientsTexture=null),this._dragGradientsTexture&&(this._dragGradientsTexture.dispose(),this._dragGradientsTexture=null),this._randomTexture&&(this._randomTexture.dispose(),this._randomTexture=null),this._randomTexture2&&(this._randomTexture2.dispose(),this._randomTexture2=null),a&&this.particleTexture&&(this.particleTexture.dispose(),this.particleTexture=null),a&&this.noiseTexture&&(this.noiseTexture.dispose(),this.noiseTexture=null),this.onStoppedObservable.clear(),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear()},b.prototype.clone=function(a,c){var d=Object(l.a)({},this._customWrappers),e=null,f=this._engine;if(f.createEffectForParticles&&null!=this.customShader){var g=(e=this.customShader).shaderOptions.defines.length>0?e.shaderOptions.defines.join(" "):"";d[0]=f.createEffectForParticles(e.shaderPath.fragmentElement,e.shaderOptions.uniforms,e.shaderOptions.samplers,g,void 0,void 0,void 0,this)}f=this.serialize();g=b.Parse(f,this._scene||this._engine,this._rootUrl);return g.name=a,g.customShader=e,g._customWrappers=d,void 0===c&&(c=this.emitter),this.noiseTexture&&(g.noiseTexture=this.noiseTexture.clone()),g.emitter=c,g},b.prototype.serialize=function(a){void 0===a&&(a=!1);var b={};return Do._Serialize(b,this,a),b.activeParticleCount=this.activeParticleCount,b.randomTextureSize=this._randomTextureSize,b.customShader=this.customShader,b},b.Parse=function(a,c,d,e,f){void 0===e&&(e=!1);var g,h=a.name;g=c instanceof pb.a?c:c.getEngine();h=new b(h,{capacity:f||a.capacity,randomTextureSize:a.randomTextureSize},c,null,a.isAnimationSheetEnabled);if(h._rootUrl=d,a.customShader&&g.createEffectForParticles){f=a.customShader;var i=f.shaderOptions.defines.length>0?f.shaderOptions.defines.join(" "):"";g=g.createEffectForParticles(f.shaderPath.fragmentElement,f.shaderOptions.uniforms,f.shaderOptions.samplers,i,void 0,void 0,void 0,h);h.setCustomEffect(g,0),h.customShader=f}return a.id&&(h.id=a.id),a.activeParticleCount&&(h.activeParticleCount=a.activeParticleCount),Do._Parse(a,h,c,d),a.preventAutoStart&&(h.preventAutoStart=a.preventAutoStart),e||h.preventAutoStart||h.start(),h},b}(El),Fo=function(){function a(){this._emitterNodeIsOwned=!0,this.systems=new Array}return Object.defineProperty(a.prototype,"emitterNode",{get:function(){return this._emitterNode},set:function(a){this._emitterNodeIsOwned&&this._emitterNode&&(this._emitterNode.dispose&&this._emitterNode.dispose(),this._emitterNodeIsOwned=!1);for(var b=0,c=this.systems;b0&&a.set(this._uvs32,W.b.UVKind),this._colors32.length>0&&a.set(this._colors32,W.b.ColorKind),a.applyToMesh(this.mesh,this._updatable),this.mesh.isPickable=this._pickable,this._pickable)for(var a=0,b=0;bo?o:d,c=Math.round(o/d),e=0):c=c>o?o:c;for(var d=[],z=[],A=[],C=[],D=[],E=g.e.Zero(),F=c;lo-(c=F+Math.floor((1+e)*Math.random()))&&(c=o-l),d.length=0,z.length=0,A.length=0,C.length=0,D.length=0;for(var L=0,aa=3*l;aa<3*(l+c);aa++){A.push(L);var M=h[aa],N=3*M;if(d.push(f[N],f[N+1],f[N+2]),z.push(k[N],k[N+1],k[N+2]),i){N=2*M;C.push(i[N],i[N+1])}if(j){N=4*M;D.push(j[N],j[N+1],j[N+2],j[N+3])}L++}var O;M=this.nbParticles;N=this._posToShape(d);aa=this._uvsToShapeUV(C);L=U.b.Slice(A);var ba=U.b.Slice(D),ca=U.b.Slice(z);for(E.copyFromFloats(0,0,0),O=0;O65535&&(this._needs32Bits=!0)}if(this._depthSort||this._multimaterialEnabled){E=null!==N.materialIndex?N.materialIndex:0;this.depthSortedParticles.push(new Ko(B,b,e.length,E))}return N},a.prototype._posToShape=function(a){for(var b=[],c=0;c=this.nbParticles||!this._updatable)return[];var d=this.particles,e=this.nbParticles;if(b=this.nbParticles?this.nbParticles-1:b,this._computeBoundingBox&&(0!=a||b!=this.nbParticles-1)){var Aa=this.mesh.getBoundingInfo();Aa&&(P.copyFrom(Aa.minimum),ka.copyFrom(Aa.maximum))}Aa=(e=this.particles[a]._pos)/3|0;ua=4*Aa,wa=2*Aa;for(Aa=a;Aa<=b;Aa++){var Ba=this.particles[Aa];this.updateParticle(Ba);var Ca=Ba._model._shape,Da=Ba._model._shapeUV,S=Ba._rotationMatrix,Ea=Ba.position,Fa=Ba.rotation,Ga=Ba.scaling,T=Ba._globalPosition;if(this._depthSort&&this._depthSortParticles){var U=this.depthSortedParticles[Aa];U.idx=Ba.idx,U.ind=Ba._ind,U.indicesLength=Ba._model._indicesLength,U.sqDistance=g.e.DistanceSquared(Ba.position,la)}if(!Ba.alive||Ba._stillInvisible&&!Ba.isVisible)e+=3*(za=Ca.length),ua+=4*za,wa+=2*za;else{if(Ba.isVisible){Ba._stillInvisible=!1;U=J[12];if(Ba.pivot.multiplyToRef(Ga,U),this.billboard&&(Fa.x=0,Fa.y=0),(this._computeParticleRotation||this.billboard)&&Ba.getRotationMatrix(d),null!==Ba.parentId){Fa=this.getParticleById(Ba.parentId);if(Fa){var Ha=Fa._rotationMatrix;Fa=Fa._globalPosition;var Ia=Ea.x*Ha[1]+Ea.y*Ha[4]+Ea.z*Ha[7],Ja=Ea.x*Ha[0]+Ea.y*Ha[3]+Ea.z*Ha[6],Ka=Ea.x*Ha[2]+Ea.y*Ha[5]+Ea.z*Ha[8];if(T.x=Fa.x+Ja,T.y=Fa.y+Ia,T.z=Fa.z+Ka,this._computeParticleRotation||this.billboard){Fa=d.m;S[0]=Fa[0]*Ha[0]+Fa[1]*Ha[3]+Fa[2]*Ha[6],S[1]=Fa[0]*Ha[1]+Fa[1]*Ha[4]+Fa[2]*Ha[7],S[2]=Fa[0]*Ha[2]+Fa[1]*Ha[5]+Fa[2]*Ha[8],S[3]=Fa[4]*Ha[0]+Fa[5]*Ha[3]+Fa[6]*Ha[6],S[4]=Fa[4]*Ha[1]+Fa[5]*Ha[4]+Fa[6]*Ha[7],S[5]=Fa[4]*Ha[2]+Fa[5]*Ha[5]+Fa[6]*Ha[8],S[6]=Fa[8]*Ha[0]+Fa[9]*Ha[3]+Fa[10]*Ha[6],S[7]=Fa[8]*Ha[1]+Fa[9]*Ha[4]+Fa[10]*Ha[7],S[8]=Fa[8]*Ha[2]+Fa[9]*Ha[5]+Fa[10]*Ha[8]}}else Ba.parentId=null}else(T.x=Ea.x,T.y=Ea.y,T.z=Ea.z,this._computeParticleRotation||this.billboard)&&(Fa=d.m,(S[0]=Fa[0],S[1]=Fa[1],S[2]=Fa[2],S[3]=Fa[4],S[4]=Fa[5],S[5]=Fa[6],S[6]=Fa[8],S[7]=Fa[9],S[8]=Fa[10]));Ha=J[11];for(Ba.translateFromPivot?Ha.setAll(0):Ha.copyFrom(U),za=0;za0)for(var b=0;b0&&b.set(this._uvs32,W.b.UVKind);var c=0;this._colors32.length>0&&(c=1,b.set(this._colors32,W.b.ColorKind));var d=new S.a(this.name,this._scene);b.applyToMesh(d,this._updatable),this.mesh=d,this._positions=null,this._uvs=null,this._colors=null,this._updatable||(this.particles.length=0);b=a;return b||((b=new pd.a("point cloud material",this._scene)).emissiveColor=new h.a(c,c,c),b.disableLighting=!0,b.pointsCloud=!0,b.pointSize=this._size),d.material=b,new Promise(function(a){return a(d)})},a.prototype._addParticle=function(a,b,c,d){a=new No(a,b,c,d,this);return this.particles.push(a),a},a.prototype._randomUnitVector=function(a){a.position=new g.e(Math.random(),Math.random(),Math.random()),a.color=new h.b(1,1,1,1)},a.prototype._getColorIndicesForCoord=function(a,b,c,d){a=a._groupImageData;c=c*(4*d)+4*b;d=[c,c+1,c+2,c+3];b=d[1];c=d[2];var e=d[3];d=a[d[0]];b=a[b];c=a[c];a=a[e];return new h.b(d/255,b/255,c/255,a)},a.prototype._setPointsColorOrUV=function(a,b,c,d,e,f,i){c&&a.updateFacetData();var j=2*a.getBoundingInfo().boundingSphere.radius,k=a.getVerticesData(W.b.PositionKind),s=a.getIndices(),t=a.getVerticesData(W.b.UVKind),u=a.getVerticesData(W.b.ColorKind),v=g.e.Zero();a.computeWorldMatrix();var M=a.getWorldMatrix();if(!M.isIdentity()){k=k.slice(0);for(var N=0;N1&&(Q=1),(tb=tb.b+Db)<0&&(tb=0),tb>1&&(tb=1),h.a.HSVtoRGBToRef(Eb,Q,tb,Fa),ub.set(Fa.r,Fa.g,Fa.b,1)):ub=nb.set(Math.random(),Math.random(),Math.random(),1),eb.color=new h.b(ub.x,ub.y,ub.z,ub.w),this._colors.push(ub.x,ub.y,ub.z,ub.w))}},a.prototype._colorFromTexture=function(a,b,c){var d=this;if(null===a.material)return q.a.Warn(a.name+"has no material."),b._groupImageData=null,void this._setPointsColorOrUV(a,b,c,!0,!1);var e=a.material.getActiveTextures();if(0===e.length)return q.a.Warn(a.name+"has no usable texture."),b._groupImageData=null,void this._setPointsColorOrUV(a,b,c,!0,!1);var f=a.clone();f.setEnabled(!1),this._promises.push(new Promise(function(a){Ie.a.WhenAllReady(e,function(){var g=b._textureNb;g<0&&(g=0),g>e.length-1&&(g=e.length-1);var h=function(){b._groupImgWidth=e[g].getSize().width,b._groupImgHeight=e[g].getSize().height,d._setPointsColorOrUV(f,b,c,!0,!0),f.dispose(),a()};b._groupImageData=null;var i=e[g].readPixels();i?i.then(function(a){b._groupImageData=a,h()}):h()})}))},a.prototype._calculateDensity=function(a,b,c){for(var d,e,f,h,i,d,j,k,e,m,p,f,h,i,d,j,k,q=new Array,r=g.e.Zero(),s=g.e.Zero(),t=g.e.Zero(),u=g.e.Zero(),G=g.e.Zero(),H=g.e.Zero(),J=new Array,K=0,L=c.length/3,aa=0;aa0&&(q=q.map(function(a){return a+M}));for(aa=0;aa3)&&(c=Ho.Random);var f=a.getVerticesData(W.b.PositionKind),g=a.getIndices();this._groups.push(this._groupCounter);var i=new Oo(this._groupCounter,null);switch(i._groupDensity=this._calculateDensity(b,f,g),c===Ho.Color?i._textureNb=d||0:d=d||new h.b(1,1,1,1),c){case Ho.Color:this._colorFromTexture(a,i,!1);break;case Ho.UV:this._setPointsColorOrUV(a,i,!1,!1,!1);break;case Ho.Random:this._setPointsColorOrUV(a,i,!1);break;case Ho.Stated:this._setPointsColorOrUV(a,i,!1,void 0,void 0,d,e)}return this.nbParticles+=b,this._groupCounter++,this._groupCounter-1},a.prototype.addVolumePoints=function(a,b,c,d,e){c=c||Ho.Random;(isNaN(c)||c<0||c>3)&&(c=Ho.Random);var f=a.getVerticesData(W.b.PositionKind),g=a.getIndices();this._groups.push(this._groupCounter);var i=new Oo(this._groupCounter,null);switch(i._groupDensity=this._calculateDensity(b,f,g),c===Ho.Color?i._textureNb=d||0:d=d||new h.b(1,1,1,1),c){case Ho.Color:this._colorFromTexture(a,i,!0);break;case Ho.UV:this._setPointsColorOrUV(a,i,!0,!1,!1);break;case Ho.Random:this._setPointsColorOrUV(a,i,!0);break;case Ho.Stated:this._setPointsColorOrUV(a,i,!0,void 0,void 0,d,e)}return this.nbParticles+=b,this._groupCounter++,this._groupCounter-1},a.prototype.setParticles=function(a,b,c){if(void 0===a&&(a=0),void 0===b&&(b=this.nbParticles-1),void 0===c&&(c=!0),!this._updatable||!this._isReady)return this;this.beforeUpdateParticles(a,b,c);var d=g.c.Matrix[0],e=this.mesh,f=this._colors32,h=this._positions32,i=this._uvs32,j=g.c.Vector3,k=j[5].copyFromFloats(1,0,0),r=j[6].copyFromFloats(0,1,0),w=j[7].copyFromFloats(0,0,1),B=j[8].setAll(Number.MAX_VALUE),E=j[9].setAll(-Number.MAX_VALUE);g.a.IdentityToRef(d);var F;if(this.mesh.isFacetDataEnabled&&(this._computeBoundingBox=!0),b=b>=this.nbParticles?this.nbParticles-1:b,this._computeBoundingBox&&(0!=a||b!=this.nbParticles-1)){var G=this.mesh.getBoundingInfo();G&&(B.copyFrom(G.minimum),E.copyFrom(G.maximum))}for(var G=0,H=0,I=0,J=a;J<=b;J++){var K=this.particles[J];G=3*(F=K.idx),H=4*F,I=2*F,this.updateParticle(K);F=K._rotationMatrix;var L=K.position,M=K._globalPosition;if(this._computeParticleRotation&&K.getRotationMatrix(d),null!==K.parentId){var N=this.particles[K.parentId],O=N._rotationMatrix;N=N._globalPosition;var ba=L.x*O[1]+L.y*O[4]+L.z*O[7],ca=L.x*O[0]+L.y*O[3]+L.z*O[6];L=L.x*O[2]+L.y*O[5]+L.z*O[8];if(M.x=N.x+ca,M.y=N.y+ba,M.z=N.z+L,this._computeParticleRotation){ca=d.m;F[0]=ca[0]*O[0]+ca[1]*O[3]+ca[2]*O[6],F[1]=ca[0]*O[1]+ca[1]*O[4]+ca[2]*O[7],F[2]=ca[0]*O[2]+ca[1]*O[5]+ca[2]*O[8],F[3]=ca[4]*O[0]+ca[5]*O[3]+ca[6]*O[6],F[4]=ca[4]*O[1]+ca[5]*O[4]+ca[6]*O[7],F[5]=ca[4]*O[2]+ca[5]*O[5]+ca[6]*O[8],F[6]=ca[8]*O[0]+ca[9]*O[3]+ca[10]*O[6],F[7]=ca[8]*O[1]+ca[9]*O[4]+ca[10]*O[7],F[8]=ca[8]*O[2]+ca[9]*O[5]+ca[10]*O[8]}}else(M.x=0,M.y=0,M.z=0,this._computeParticleRotation)&&(ca=d.m,(F[0]=ca[0],F[1]=ca[1],F[2]=ca[2],F[3]=ca[4],F[4]=ca[5],F[5]=ca[6],F[6]=ca[8],F[7]=ca[9],F[8]=ca[10]));ba=j[11];K.translateFromPivot?ba.setAll(0):ba.copyFrom(K.pivot);N=j[0];N.copyFrom(K.position);L=N.x-K.pivot.x;O=N.y-K.pivot.y;ca=N.z-K.pivot.z;N=L*F[0]+O*F[3]+ca*F[6];var da=L*F[1]+O*F[4]+ca*F[7];L=L*F[2]+O*F[5]+ca*F[8];N+=ba.x,da+=ba.y,L+=ba.z;O=h[G]=M.x+k.x*N+r.x*da+w.x*L;ca=h[G+1]=M.y+k.y*N+r.y*da+w.y*L;F=h[G+2]=M.z+k.z*N+r.z*da+w.z*L;if(this._computeBoundingBox&&(B.minimizeInPlaceFromFloats(O,ca,F),E.maximizeInPlaceFromFloats(O,ca,F)),this._computeParticleColor&&K.color){ba=K.color;M=this._colors32;M[H]=ba.r,M[H+1]=ba.g,M[H+2]=ba.b,M[H+3]=ba.a}if(this._computeParticleTexture&&K.uv){N=K.uv;da=this._uvs32;da[I]=N.x,da[I+1]=N.y}}return c&&(this._computeParticleColor&&e.updateVerticesData(W.b.ColorKind,f,!1,!1),this._computeParticleTexture&&e.updateVerticesData(W.b.UVKind,i,!1,!1),e.updateVerticesData(W.b.PositionKind,h,!1,!1)),this._computeBoundingBox&&(e.hasBoundingInfo?e.getBoundingInfo().reConstruct(B,E,e._worldMatrix):e.buildBoundingInfo(B,E,e._worldMatrix)),this.afterUpdateParticles(a,b,c),this},a.prototype.dispose=function(){this.mesh.dispose(),this.vars=null,this._positions=null,this._indices=null,this._normals=null,this._uvs=null,this._colors=null,this._indices32=null,this._positions32=null,this._uvs32=null,this._colors32=null},a.prototype.refreshVisibleSize=function(){return this._isVisibilityBoxLocked||this.mesh.refreshBoundingInfo(),this},a.prototype.setVisibilityBox=function(a){a=a/2;this.mesh.buildBoundingInfo(new g.e(-a,-a,-a),new g.e(a,a,a))},Object.defineProperty(a.prototype,"isAlwaysVisible",{get:function(){return this._alwaysVisible},set:function(a){this._alwaysVisible=a,this.mesh.alwaysSelectAsActiveMesh=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleRotation",{set:function(a){this._computeParticleRotation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleColor",{get:function(){return this._computeParticleColor},set:function(a){this._computeParticleColor=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleTexture",{get:function(){return this._computeParticleTexture},set:function(a){this._computeParticleTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeBoundingBox",{get:function(){return this._computeBoundingBox},set:function(a){this._computeBoundingBox=a},enumerable:!1,configurable:!0}),a.prototype.initParticles=function(){},a.prototype.recycleParticle=function(a){return a},a.prototype.updateParticle=function(a){return a},a.prototype.beforeUpdateParticles=function(a,b,c){},a.prototype.afterUpdateParticles=function(a,b,c){},a}();P.a.prototype.getPhysicsEngine=function(){return this._physicsEngine},P.a.prototype.enablePhysics=function(a,b){if(void 0===a&&(a=null),this._physicsEngine)return!0;var c=this._getComponent(Na.a.NAME_PHYSICSENGINE);c||(c=new So(this),this._addComponent(c));try{return this._physicsEngine=new Vj(a,b),this._physicsTimeAccumulator=0,!0}catch(a){return q.a.Error(a.message),!1}},P.a.prototype.disablePhysicsEngine=function(){this._physicsEngine&&(this._physicsEngine.dispose(),this._physicsEngine=null)},P.a.prototype.isPhysicsEnabled=function(){return void 0!==this._physicsEngine},P.a.prototype.deleteCompoundImpostor=function(a){a=a.parts[0].mesh;a.physicsImpostor&&(a.physicsImpostor.dispose(),a.physicsImpostor=null)},P.a.prototype._advancePhysicsEngineStep=function(a){if(this._physicsEngine){var b=this._physicsEngine.getSubTimeStep();if(b>0)for(this._physicsTimeAccumulator+=a;this._physicsTimeAccumulator>b;)this.onBeforePhysicsObservable.notifyObservers(this),this._physicsEngine._step(b/1e3),this.onAfterPhysicsObservable.notifyObservers(this),this._physicsTimeAccumulator-=b;else this.onBeforePhysicsObservable.notifyObservers(this),this._physicsEngine._step(a/1e3),this.onAfterPhysicsObservable.notifyObservers(this)}},Object.defineProperty(bb.a.prototype,"physicsImpostor",{get:function(){return this._physicsImpostor},set:function(a){var b=this;this._physicsImpostor!==a&&(this._disposePhysicsObserver&&this.onDisposeObservable.remove(this._disposePhysicsObserver),this._physicsImpostor=a,a&&(this._disposePhysicsObserver=this.onDisposeObservable.add(function(){b.physicsImpostor&&(b.physicsImpostor.dispose(),b.physicsImpostor=null)})))},enumerable:!0,configurable:!0}),bb.a.prototype.getPhysicsImpostor=function(){return this.physicsImpostor},bb.a.prototype.applyImpulse=function(a,b){return this.physicsImpostor?(this.physicsImpostor.applyImpulse(a,b),this):this},bb.a.prototype.setPhysicsLinkWith=function(a,b,c,d){return this.physicsImpostor&&a.physicsImpostor?(this.physicsImpostor.createJoint(a.physicsImpostor,Uj.e.HingeJoint,{mainPivot:b,connectedPivot:c,nativeParams:d}),this):this};var Qo,Ro,So=function(){function a(a){var b=this;this.name=Na.a.NAME_PHYSICSENGINE,this.scene=a,this.scene.onBeforePhysicsObservable=new f.c,this.scene.onAfterPhysicsObservable=new f.c,this.scene.getDeterministicFrameTime=function(){return b.scene._physicsEngine?1e3*b.scene._physicsEngine.getTimeStep():1e3/60}}return a.prototype.register=function(){},a.prototype.rebuild=function(){},a.prototype.dispose=function(){this.scene.onBeforePhysicsObservable.clear(),this.scene.onAfterPhysicsObservable.clear(),this.scene._physicsEngine&&this.scene.disablePhysicsEngine()},a}(),To=function(){function a(a){this._scene=a,this._physicsEngine=this._scene.getPhysicsEngine(),this._physicsEngine||q.a.Warn("Physics engine not enabled. Please enable the physics before you can use the methods.")}return a.prototype.applyRadialExplosionImpulse=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call this method."),null;var e=this._physicsEngine.getImpostors();if(0===e.length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);var f=new Uo(this._scene,b),g=Array();return e.forEach(function(b){var c=f.getImpostorHitData(b,a);c&&(b.applyImpulse(c.force,c.contactPoint),g.push({impostor:b,hitData:c}))}),f.triggerAffectedImpostorsCallback(g),f.dispose(!1),f},a.prototype.applyRadialExplosionForce=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;var e=this._physicsEngine.getImpostors();if(0===e.length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);var f=new Uo(this._scene,b),g=Array();return e.forEach(function(b){var c=f.getImpostorHitData(b,a);c&&(b.applyForce(c.force,c.contactPoint),g.push({impostor:b,hitData:c}))}),f.triggerAffectedImpostorsCallback(g),f.dispose(!1),f},a.prototype.gravitationalField=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);c=new Vo(this,this._scene,a,b);return c.dispose(!1),c},a.prototype.updraft=function(a,b,c,d,e){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new Zo).radius=b,b.strength=c||b.strength,b.height=d||b.height,b.updraftMode=e||b.updraftMode);c=new Wo(this._scene,a,b);return c.dispose(!1),c},a.prototype.vortex=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new $o).radius=b,b.strength=c||b.strength,b.height=d||b.height);c=new Xo(this._scene,a,b);return c.dispose(!1),c},a}(),Uo=function(){function a(a,b){this._scene=a,this._options=b,this._dataFetched=!1,this._options=Object(l.a)(Object(l.a)({},new Yo),this._options)}return a.prototype.getData=function(){return this._dataFetched=!0,{sphere:this._sphere}},a.prototype.getImpostorHitData=function(a,b){if(0===a.mass)return null;if(!this._intersectsWithSphere(a,b,this._options.radius))return null;if("Mesh"!==a.object.getClassName()&&"InstancedMesh"!==a.object.getClassName())return null;var c=a.getObjectCenter().subtract(b);a=new nc.a(b,c,this._options.radius).intersectsMesh(a.object).pickedPoint;if(!a)return null;b=g.e.Distance(b,a);if(b>this._options.radius)return null;var d=this._options.falloff===Qo.Constant?this._options.strength:this._options.strength*(1-b/this._options.radius);return{force:c.multiplyByFloats(d,d,d),contactPoint:a,distanceFromOrigin:b}},a.prototype.triggerAffectedImpostorsCallback=function(a){this._options.affectedImpostorsCallback&&this._options.affectedImpostorsCallback(a)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._sphere.dispose():setTimeout(function(){b._dataFetched||b._sphere.dispose()},0)},a.prototype._prepareSphere=function(){this._sphere||(this._sphere=Object(vh.a)("radialExplosionEventSphere",this._options.sphere,this._scene),this._sphere.isVisible=!1)},a.prototype._intersectsWithSphere=function(a,b,c){a=a.object;return this._prepareSphere(),this._sphere.position=b,this._sphere.scaling=new g.e(2*c,2*c,2*c),this._sphere._updateBoundingInfo(),this._sphere.computeWorldMatrix(!0),this._sphere.intersectsMesh(a,!0)},a}(),Vo=function(){function a(a,b,c,d){this._physicsHelper=a,this._scene=b,this._origin=c,this._options=d,this._dataFetched=!1,this._options=Object(l.a)(Object(l.a)({},new Yo),this._options),this._tickCallback=this._tick.bind(this),this._options.strength=-1*this._options.strength}return a.prototype.getData=function(){return this._dataFetched=!0,{sphere:this._sphere}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._sphere.dispose():setTimeout(function(){b._dataFetched||b._sphere.dispose()},0)},a.prototype._tick=function(){if(this._sphere)this._physicsHelper.applyRadialExplosionForce(this._origin,this._options);else{var a=this._physicsHelper.applyRadialExplosionForce(this._origin,this._options);a&&(this._sphere=a.getData().sphere.clone("radialExplosionEventSphereClone"))}},a}(),Wo=function(){function a(a,b,c){this._scene=a,this._origin=b,this._options=c,this._originTop=g.e.Zero(),this._originDirection=g.e.Zero(),this._cylinderPosition=g.e.Zero(),this._dataFetched=!1,this._physicsEngine=this._scene.getPhysicsEngine(),this._options=Object(l.a)(Object(l.a)({},new Zo),this._options),this._origin.addToRef(new g.e(0,this._options.height/2,0),this._cylinderPosition),this._origin.addToRef(new g.e(0,this._options.height,0),this._originTop),this._options.updraftMode===Ro.Perpendicular&&(this._originDirection=this._origin.subtract(this._originTop).normalize()),this._tickCallback=this._tick.bind(this),this._prepareCylinder()}return a.prototype.getData=function(){return this._dataFetched=!0,{cylinder:this._cylinder}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),this._cylinder&&(a?this._cylinder.dispose():setTimeout(function(){b._dataFetched||b._cylinder.dispose()},0))},a.prototype.getImpostorHitData=function(a){if(0===a.mass)return null;if(!this._intersectsWithCylinder(a))return null;a=a.getObjectCenter();if(this._options.updraftMode===Ro.Perpendicular)var b=this._originDirection;else b=a.subtract(this._originTop);var c=g.e.Distance(this._origin,a),d=-1*this._options.strength;return{force:b.multiplyByFloats(d,d,d),contactPoint:a,distanceFromOrigin:c}},a.prototype._tick=function(){var a=this;this._physicsEngine.getImpostors().forEach(function(b){var c=a.getImpostorHitData(b);c&&b.applyForce(c.force,c.contactPoint)})},a.prototype._prepareCylinder=function(){this._cylinder||(this._cylinder=Object(xd.a)("updraftEventCylinder",{height:this._options.height,diameter:2*this._options.radius},this._scene),this._cylinder.isVisible=!1)},a.prototype._intersectsWithCylinder=function(a){a=a.object;return this._cylinder.position=this._cylinderPosition,this._cylinder.intersectsMesh(a,!0)},a}(),Xo=function(){function a(a,b,c){this._scene=a,this._origin=b,this._options=c,this._originTop=g.e.Zero(),this._cylinderPosition=g.e.Zero(),this._dataFetched=!1,this._physicsEngine=this._scene.getPhysicsEngine(),this._options=Object(l.a)(Object(l.a)({},new $o),this._options),this._origin.addToRef(new g.e(0,this._options.height/2,0),this._cylinderPosition),this._origin.addToRef(new g.e(0,this._options.height,0),this._originTop),this._tickCallback=this._tick.bind(this),this._prepareCylinder()}return a.prototype.getData=function(){return this._dataFetched=!0,{cylinder:this._cylinder}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._cylinder.dispose():setTimeout(function(){b._dataFetched||b._cylinder.dispose()},0)},a.prototype.getImpostorHitData=function(a){if(0===a.mass)return null;if(!this._intersectsWithCylinder(a))return null;if("Mesh"!==a.object.getClassName()&&"InstancedMesh"!==a.object.getClassName())return null;var b=a.getObjectCenter(),c=new g.e(this._origin.x,b.y,this._origin.z),d=b.subtract(c);d=new nc.a(c,d,this._options.radius).intersectsMesh(a.object);a=d.pickedPoint;if(!a)return null;d=d.distance/this._options.radius;a=a.normalize();if(d>this._options.centripetalForceThreshold&&(a=a.negate()),d>this._options.centripetalForceThreshold)var e=a.x*this._options.centripetalForceMultiplier,f=a.y*this._options.updraftForceMultiplier,h=a.z*this._options.centripetalForceMultiplier;else{c=g.e.Cross(c,b).normalize();e=(c.x+a.x)*this._options.centrifugalForceMultiplier,f=this._originTop.y*this._options.updraftForceMultiplier,h=(c.z+a.z)*this._options.centrifugalForceMultiplier}c=new g.e(e,f,h);return{force:c.multiplyByFloats(this._options.strength,this._options.strength,this._options.strength),contactPoint:b,distanceFromOrigin:d}},a.prototype._tick=function(){var a=this;this._physicsEngine.getImpostors().forEach(function(b){var c=a.getImpostorHitData(b);c&&b.applyForce(c.force,c.contactPoint)})},a.prototype._prepareCylinder=function(){this._cylinder||(this._cylinder=Object(xd.a)("vortexEventCylinder",{height:this._options.height,diameter:2*this._options.radius},this._scene),this._cylinder.isVisible=!1)},a.prototype._intersectsWithCylinder=function(a){a=a.object;return this._cylinder.position=this._cylinderPosition,this._cylinder.intersectsMesh(a,!0)},a}(),Yo=function(){this.radius=5,this.strength=10,this.falloff=Qo.Constant,this.sphere={segments:32,diameter:1}},Zo=function(){this.radius=5,this.strength=10,this.height=10,this.updraftMode=Ro.Center},$o=function(){this.radius=5,this.strength=10,this.height=10,this.centripetalForceThreshold=.7,this.centripetalForceMultiplier=5,this.centrifugalForceMultiplier=.5,this.updraftForceMultiplier=.02};!function(a){a[a.Constant=0]="Constant",a[a.Linear=1]="Linear"}(Qo||(Qo={})),(function(a){a[a.Center=0]="Center",a[a.Perpendicular=1]="Perpendicular"})(Ro||(Ro={}));a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform float degree; void main(void) { vec3 color=texture2D(textureSampler,vUV).rgb; float luminance=dot(color,vec3(0.3,0.59,0.11)); vec3 blackAndWhite=vec3(luminance,luminance,luminance); gl_FragColor=vec4(color-((color-blackAndWhite)*degree),1.0); }";X.a.ShadersStore.blackAndWhitePixelShader=a;var ap=function(a){function b(b,c,d,e,f,g){var h=a.call(this,b,"blackAndWhite",["degree"],null,c,d,e,f,g)||this;return h.degree=1,h.onApplyObservable.add(function(a){a.setFloat("degree",h.degree)}),h}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"BlackAndWhitePostProcess"},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},Object(l.c)([Object(J.d)()],b.prototype,"degree",void 0),b}(Fc);Object(i.b)("BABYLON.BlackAndWhitePostProcess",ap);var bp=function(){function a(a,b,c,d){this._name=b,this._singleInstance=d||!0,this._getPostProcesses=c,this._cameras={},this._indicesForCamera={},this._postProcesses={}}return Object.defineProperty(a.prototype,"isSupported",{get:function(){for(var a in this._postProcesses)if(this._postProcesses.hasOwnProperty(a))for(var b=this._postProcesses[a],c=0;c-1?"#define MALI 1 ":null},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},b}(Fc);Object(i.b)("BABYLON.FxaaPostProcess",qp);a="#include uniform sampler2D textureSampler; uniform float intensity; uniform float animatedSeed; varying vec2 vUV; void main(void) { gl_FragColor=texture2D(textureSampler,vUV); vec2 seed=vUV*(animatedSeed); float grain=dither(seed,intensity); float lum=getLuminance(gl_FragColor.rgb); float grainAmount=(cos(-PI+(lum*PI*2.))+1.)/2.; gl_FragColor.rgb+=grain*grainAmount; gl_FragColor.rgb=max(gl_FragColor.rgb,0.0); }";X.a.ShadersStore.grainPixelShader=a;var rp=function(a){function b(b,c,d,e,f,g,h,i){void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1);var j=a.call(this,b,"grain",["intensity","animatedSeed"],[],c,d,e,f,g,null,h,void 0,null,i)||this;return j.intensity=30,j.animated=!1,j.onApplyObservable.add(function(a){a.setFloat("intensity",j.intensity),a.setFloat("animatedSeed",j.animated?Math.random()+1:1)}),j}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"GrainPostProcess"},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},Object(l.c)([Object(J.d)()],b.prototype,"intensity",void 0),Object(l.c)([Object(J.d)()],b.prototype,"animated",void 0),b}(Fc);Object(i.b)("BABYLON.GrainPostProcess",rp);wi=" varying vec2 vUV; uniform sampler2D textureSampler; const vec3 RGBLuminanceCoefficients=vec3(0.2126,0.7152,0.0722); void main(void) { vec4 tex=texture2D(textureSampler,vUV); vec3 c=tex.rgb; float luma=dot(c.rgb,RGBLuminanceCoefficients); gl_FragColor=vec4(pow(c,vec3(25.0-luma*15.0)),tex.a); }";X.a.ShadersStore.highlightsPixelShader=wi;var sp=function(a){function b(b,c,d,e,f,g,h){return void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),a.call(this,b,"highlights",null,null,c,d,e,f,g,null,h)||this}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"HighlightsPostProcess"},b}(Fc);rb="#if defined(WEBGL2) || defined(WEBGPU) layout(location=0) out vec4 glFragData[{X}]; #endif ";X.a.IncludesShadersStore.mrtFragmentDeclaration=rb;a="#extension GL_EXT_draw_buffers : require #if defined(BUMP) || !defined(NORMAL) #extension GL_OES_standard_derivatives : enable #endif precision highp float; #ifdef BUMP varying mat4 vWorldView; varying vec3 vNormalW; #else varying vec3 vNormalV; #endif varying vec4 vViewPos; #if defined(POSITION) || defined(BUMP) varying vec3 vPositionW; #endif #ifdef VELOCITY varying vec4 vCurrentPosition; varying vec4 vPreviousPosition; #endif #ifdef NEED_UV varying vec2 vUV; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #if defined(REFLECTIVITY) && (defined(HAS_SPECULAR) || defined(HAS_REFLECTIVITY)) varying vec2 vReflectivityUV; uniform sampler2D reflectivitySampler; #endif #ifdef ALPHATEST uniform sampler2D diffuseSampler; #endif #include[RENDER_TARGET_COUNT] #include #include void main() { #ifdef ALPHATEST if (texture2D(diffuseSampler,vUV).a<0.4) discard; #endif vec3 normalOutput; #ifdef BUMP vec3 normalW=normalize(vNormalW); #include normalOutput=normalize(vec3(vWorldView*vec4(normalW,0.0))); #else normalOutput=normalize(vNormalV); #endif #ifdef PREPASS #ifdef PREPASS_DEPTH gl_FragData[DEPTH_INDEX]=vec4(vViewPos.z/vViewPos.w,0.0,0.0,1.0); #endif #ifdef PREPASS_NORMAL gl_FragData[NORMAL_INDEX]=vec4(normalOutput,1.0); #endif #else gl_FragData[0]=vec4(vViewPos.z/vViewPos.w,0.0,0.0,1.0); gl_FragData[1]=vec4(normalOutput,1.0); #endif #ifdef POSITION gl_FragData[POSITION_INDEX]=vec4(vPositionW,1.0); #endif #ifdef VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[VELOCITY_INDEX]=vec4(velocity,0.0,1.0); #endif #ifdef REFLECTIVITY #ifdef HAS_SPECULAR vec4 reflectivity=texture2D(reflectivitySampler,vReflectivityUV); #elif HAS_REFLECTIVITY vec4 reflectivity=vec4(texture2D(reflectivitySampler,vReflectivityUV).rgb,1.0); #else vec4 reflectivity=vec4(0.0,0.0,0.0,1.0); #endif gl_FragData[REFLECTIVITY_INDEX]=reflectivity; #endif } ";X.a.ShadersStore.geometryPixelShader=a;X.a.IncludesShadersStore.geometryVertexDeclaration=" uniform mat4 viewProjection; uniform mat4 view;";X.a.IncludesShadersStore.geometryUboDeclaration="#include";wi="precision highp float; #include #include #include[0..maxSimultaneousMorphTargets] #include #include<__decl__geometryVertex> attribute vec3 position; attribute vec3 normal; #ifdef NEED_UV varying vec2 vUV; #ifdef ALPHATEST uniform mat4 diffuseMatrix; #endif #ifdef BUMP uniform mat4 bumpMatrix; varying vec2 vBumpUV; #endif #ifdef REFLECTIVITY uniform mat4 reflectivityMatrix; varying vec2 vReflectivityUV; #endif #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #ifdef BUMP varying mat4 vWorldView; #endif #ifdef BUMP varying vec3 vNormalW; #else varying vec3 vNormalV; #endif varying vec4 vViewPos; #if defined(POSITION) || defined(BUMP) varying vec3 vPositionW; #endif #ifdef VELOCITY uniform mat4 previousViewProjection; varying vec4 vCurrentPosition; varying vec4 vPreviousPosition; #endif void main(void) { vec3 positionUpdated=position; vec3 normalUpdated=normal; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #if defined(VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #include vec4 pos=vec4(finalWorld*vec4(positionUpdated,1.0)); #ifdef BUMP vWorldView=view*finalWorld; vNormalW=normalUpdated; #else vNormalV=normalize(vec3((view*finalWorld)*vec4(normalUpdated,0.0))); #endif vViewPos=view*pos; #if defined(VELOCITY) && defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); #if NUM_BONE_INFLUENCERS>0 mat4 previousInfluence; previousInfluence=mPreviousBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 previousInfluence+=mPreviousBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 previousInfluence+=mPreviousBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 previousInfluence+=mPreviousBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif vPreviousPosition=previousViewProjection*finalPreviousWorld*previousInfluence*vec4(positionUpdated,1.0); #else vPreviousPosition=previousViewProjection*finalPreviousWorld*vec4(positionUpdated,1.0); #endif #endif #if defined(POSITION) || defined(BUMP) vPositionW=pos.xyz/pos.w; #endif gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #ifdef NEED_UV #ifdef UV1 #if defined(ALPHATEST) && defined(ALPHATEST_UV1) vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #else vUV=uv; #endif #ifdef BUMP_UV1 vBumpUV=vec2(bumpMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef REFLECTIVITY_UV1 vReflectivityUV=vec2(reflectivityMatrix*vec4(uvUpdated,1.0,0.0)); #endif #endif #ifdef UV2 #if defined(ALPHATEST) && defined(ALPHATEST_UV2) vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #else vUV=uv2; #endif #ifdef BUMP_UV2 vBumpUV=vec2(bumpMatrix*vec4(uv2,1.0,0.0)); #endif #ifdef REFLECTIVITY_UV2 vReflectivityUV=vec2(reflectivityMatrix*vec4(uv2,1.0,0.0)); #endif #endif #endif #include } ";X.a.ShadersStore.geometryVertexShader=wi;var tp=function(){function a(b,c){void 0===c&&(c=1),this._previousTransformationMatrices={},this._previousBonesTransformationMatrices={},this.excludedSkinnedMeshesFromVelocity=[],this.renderTransparentMeshes=!0,this._resizeObserver=null,this._enablePosition=!1,this._enableVelocity=!1,this._enableReflectivity=!1,this._positionIndex=-1,this._velocityIndex=-1,this._reflectivityIndex=-1,this._depthIndex=-1,this._normalIndex=-1,this._linkedWithPrePass=!1,this._scene=b,this._ratio=c,this._useUbo=b.getEngine().supportsUniformBuffers,this._drawWrapper=new Ec.a(b.getEngine()),a._SceneComponentInitialization(this._scene),this._createRenderTargets()}return a.prototype._linkPrePassRenderer=function(a){this._linkedWithPrePass=!0,this._prePassRenderer=a,this._multiRenderTarget&&(this._multiRenderTarget.onClearObservable.clear(),this._multiRenderTarget.onClearObservable.add(function(a){}))},a.prototype._unlinkPrePassRenderer=function(){this._linkedWithPrePass=!1,this._createRenderTargets()},a.prototype._resetLayout=function(){this._enablePosition=!1,this._enableReflectivity=!1,this._enableVelocity=!1,this._attachments=[]},a.prototype._forceTextureType=function(b,c){b===a.POSITION_TEXTURE_TYPE?(this._positionIndex=c,this._enablePosition=!0):b===a.VELOCITY_TEXTURE_TYPE?(this._velocityIndex=c,this._enableVelocity=!0):b===a.REFLECTIVITY_TEXTURE_TYPE?(this._reflectivityIndex=c,this._enableReflectivity=!0):b===a.DEPTH_TEXTURE_TYPE?this._depthIndex=c:b===a.NORMAL_TEXTURE_TYPE&&(this._normalIndex=c)},a.prototype._setAttachments=function(a){this._attachments=a},a.prototype._linkInternalTexture=function(a){this._multiRenderTarget._texture=a},Object.defineProperty(a.prototype,"renderList",{get:function(){return this._multiRenderTarget.renderList},set:function(a){this._multiRenderTarget.renderList=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSupported",{get:function(){return this._multiRenderTarget.isSupported},enumerable:!1,configurable:!0}),a.prototype.getTextureIndex=function(b){switch(b){case a.POSITION_TEXTURE_TYPE:return this._positionIndex;case a.VELOCITY_TEXTURE_TYPE:return this._velocityIndex;case a.REFLECTIVITY_TEXTURE_TYPE:return this._reflectivityIndex;default:return-1}},Object.defineProperty(a.prototype,"enablePosition",{get:function(){return this._enablePosition},set:function(a){this._enablePosition=a,this._linkedWithPrePass||(this.dispose(),this._createRenderTargets())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enableVelocity",{get:function(){return this._enableVelocity},set:function(a){this._enableVelocity=a,a||(this._previousTransformationMatrices={}),this._linkedWithPrePass||(this.dispose(),this._createRenderTargets()),this._scene.needsPreviousWorldMatrices=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enableReflectivity",{get:function(){return this._enableReflectivity},set:function(a){this._enableReflectivity=a,this._linkedWithPrePass||(this.dispose(),this._createRenderTargets())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"ratio",{get:function(){return this._ratio},enumerable:!1,configurable:!0}),a.prototype.isReady=function(a,b){var c=a.getMaterial();if(c&&c.disableDepthWrite)return!1;var d=[],e=[W.b.PositionKind,W.b.NormalKind],f=a.getMesh();if(c){var g=!1;c.needAlphaTesting()&&c.getAlphaTestTexture()&&(d.push("#define ALPHATEST"),d.push("#define ALPHATEST_UV"+(c.getAlphaTestTexture().coordinatesIndex+1)),g=!0),c.bumpTexture&&ai.a.BumpTextureEnabled&&(d.push("#define BUMP"),d.push("#define BUMP_UV"+(c.bumpTexture.coordinatesIndex+1)),g=!0),this._enableReflectivity&&(c.specularTexture?(d.push("#define HAS_SPECULAR"),d.push("#define REFLECTIVITY_UV"+(c.specularTexture.coordinatesIndex+1)),g=!0):c.reflectivityTexture&&(d.push("#define HAS_REFLECTIVITY"),d.push("#define REFLECTIVITY_UV"+(c.reflectivityTexture.coordinatesIndex+1)),g=!0)),g&&(d.push("#define NEED_UV"),f.isVerticesDataPresent(W.b.UVKind)&&(e.push(W.b.UVKind),d.push("#define UV1")),f.isVerticesDataPresent(W.b.UV2Kind)&&(e.push(W.b.UV2Kind),d.push("#define UV2")))}this._linkedWithPrePass&&(d.push("#define PREPASS"),-1!==this._depthIndex&&(d.push("#define DEPTH_INDEX "+this._depthIndex),d.push("#define PREPASS_DEPTH")),-1!==this._normalIndex&&(d.push("#define NORMAL_INDEX "+this._normalIndex),d.push("#define PREPASS_NORMAL"))),this._enablePosition&&(d.push("#define POSITION"),d.push("#define POSITION_INDEX "+this._positionIndex)),this._enableVelocity&&(d.push("#define VELOCITY"),d.push("#define VELOCITY_INDEX "+this._velocityIndex),-1===this.excludedSkinnedMeshesFromVelocity.indexOf(f)&&d.push("#define BONES_VELOCITY_ENABLED")),this._enableReflectivity&&(d.push("#define REFLECTIVITY"),d.push("#define REFLECTIVITY_INDEX "+this._reflectivityIndex)),f.useBones&&f.computeBonesUsingShaders?(e.push(W.b.MatricesIndicesKind),e.push(W.b.MatricesWeightsKind),f.numBoneInfluencers>4&&(e.push(W.b.MatricesIndicesExtraKind),e.push(W.b.MatricesWeightsExtraKind)),d.push("#define NUM_BONE_INFLUENCERS "+f.numBoneInfluencers),d.push("#define BonesPerMesh "+(f.skeleton?f.skeleton.bones.length+1:0))):d.push("#define NUM_BONE_INFLUENCERS 0");c=f.morphTargetManager;g=0;c&&c.numInfluencers>0&&(g=c.numInfluencers,d.push("#define MORPHTARGETS"),d.push("#define NUM_MORPH_INFLUENCERS "+g),c.isUsingTextureForTargets&&d.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(e,f,g)),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(e,this._enableVelocity),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES")),this._linkedWithPrePass?d.push("#define RENDER_TARGET_COUNT "+this._attachments.length):d.push("#define RENDER_TARGET_COUNT "+this._multiRenderTarget.textures.length);c=d.join(" ");return this._cachedDefines!==c&&(this._cachedDefines=c,this._drawWrapper.effect=this._scene.getEngine().createEffect("geometry",{attributes:e,uniformsNames:["world","mBones","viewProjection","diffuseMatrix","view","previousWorld","previousViewProjection","mPreviousBones","bumpMatrix","reflectivityMatrix","vTangentSpaceParams","vBumpInfos","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],samplers:["diffuseSampler","bumpSampler","reflectivitySampler","morphTargets"],defines:c,onCompiled:null,fallbacks:null,onError:null,uniformBuffersNames:["Scene"],indexParameters:{buffersCount:this._multiRenderTarget.textures.length-1,maxSimultaneousMorphTargets:g}},this._scene.getEngine())),this._drawWrapper.effect.isReady()},a.prototype.getGBuffer=function(){return this._multiRenderTarget},Object.defineProperty(a.prototype,"samples",{get:function(){return this._multiRenderTarget.samples},set:function(a){this._multiRenderTarget.samples=a},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this._resizeObserver&&(this._scene.getEngine().onResizeObservable.remove(this._resizeObserver),this._resizeObserver=null),this.getGBuffer().dispose()},a.prototype._assignRenderTargetIndices=function(){var a=[],b=2;return a.push("gBuffer_Depth","gBuffer_Normal"),this._enablePosition&&(this._positionIndex=b,b++,a.push("gBuffer_Position")),this._enableVelocity&&(this._velocityIndex=b,b++,a.push("gBuffer_Velocity")),this._enableReflectivity&&(this._reflectivityIndex=b,b++,a.push("gBuffer_Reflectivity")),[b,a]},a.prototype._createRenderTargets=function(){var a=this,b=this._scene.getEngine(),c=this._assignRenderTargetIndices(),d=c[0];c=c[1];var e=r.a.TEXTURETYPE_UNSIGNED_BYTE;if(b._caps.textureFloat&&b._caps.textureFloatLinearFiltering?e=r.a.TEXTURETYPE_FLOAT:b._caps.textureHalfFloat&&b._caps.textureHalfFloatLinearFiltering&&(e=r.a.TEXTURETYPE_HALF_FLOAT),this._multiRenderTarget=new Jk("gBuffer",{width:b.getRenderWidth()*this._ratio,height:b.getRenderHeight()*this._ratio},d,this._scene,{generateMipMaps:!1,generateDepthTexture:!0,defaultType:e},c.concat("gBuffer_DepthBuffer")),this.isSupported){this._multiRenderTarget.wrapU=V.a.CLAMP_ADDRESSMODE,this._multiRenderTarget.wrapV=V.a.CLAMP_ADDRESSMODE,this._multiRenderTarget.refreshRate=1,this._multiRenderTarget.renderParticles=!1,this._multiRenderTarget.renderList=null,this._multiRenderTarget.onClearObservable.add(function(a){a.clear(new h.b(0,0,0,0),!0,!0,!0)}),this._resizeObserver=b.onResizeObservable.add(function(){a._multiRenderTarget&&a._multiRenderTarget.resize({width:b.getRenderWidth()*a._ratio,height:b.getRenderHeight()*a._ratio})});var f=function(b){var c=b.getRenderingMesh(),d=b.getEffectiveMesh(),e=a._scene,f=e.getEngine(),h=b.getMaterial();if(h){if(d._internalAbstractMeshDataInfo._isActiveIntermediate=!1,a._enableVelocity&&!a._previousTransformationMatrices[d.uniqueId]&&(a._previousTransformationMatrices[d.uniqueId]={world:g.a.Identity(),viewProjection:e.getTransformMatrix()},c.skeleton)){var i=c.skeleton.getTransformMatrices(c);a._previousBonesTransformationMatrices[c.uniqueId]=a._copyBonesTransformationMatrices(i,new Float32Array(i.length))}i=c._getInstancesRenderList(b._id,!!b.getReplacementMesh());if(!i.mustReturn){var j=f.getCaps().instancedArrays&&(null!==i.visibleInstances[b._id]||c.hasThinInstances),k=d.getWorldMatrix();if(a.isReady(b,j)){var n=a._drawWrapper.effect;if(f.enableEffect(a._drawWrapper),j||c._bind(b,n,h.fillMode),a._useUbo?(a._scene.finalizeSceneUbo(),Yh.a.BindSceneUniformBuffer(n,a._scene.getSceneUniformBuffer())):(n.setMatrix("viewProjection",e.getTransformMatrix()),n.setMatrix("view",e.getViewMatrix())),h){f=c._instanceDataStorage;if(f.isFrozen||!h.backFaceCulling&&null===h.overrideMaterialSideOrientation)f=f.sideOrientation;else{var o=d._getWorldMatrixDeterminant();null==(f=h.overrideMaterialSideOrientation)&&(f=h.sideOrientation),o<0&&(f=f===qi.a.ClockWiseSideOrientation?qi.a.CounterClockWiseSideOrientation:qi.a.ClockWiseSideOrientation)}if(h._preBind(a._drawWrapper,f),h.needAlphaTesting()){o=h.getAlphaTestTexture();o&&(n.setTexture("diffuseSampler",o),n.setMatrix("diffuseMatrix",o.getTextureMatrix()))}h.bumpTexture&&e.getEngine().getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&(n.setFloat3("vBumpInfos",h.bumpTexture.coordinatesIndex,1/h.bumpTexture.level,h.parallaxScaleBias),n.setMatrix("bumpMatrix",h.bumpTexture.getTextureMatrix()),n.setTexture("bumpSampler",h.bumpTexture),n.setFloat2("vTangentSpaceParams",h.invertNormalMapX?-1:1,h.invertNormalMapY?-1:1)),a._enableReflectivity&&(h.specularTexture?(n.setMatrix("reflectivityMatrix",h.specularTexture.getTextureMatrix()),n.setTexture("reflectivitySampler",h.specularTexture)):h.reflectivityTexture&&(n.setMatrix("reflectivityMatrix",h.reflectivityTexture.getTextureMatrix()),n.setTexture("reflectivitySampler",h.reflectivityTexture)))}c.useBones&&c.computeBonesUsingShaders&&c.skeleton&&(n.setMatrices("mBones",c.skeleton.getTransformMatrices(c)),a._enableVelocity&&n.setMatrices("mPreviousBones",a._previousBonesTransformationMatrices[c.uniqueId])),Yh.a.BindMorphTargetParameters(c,n),c.morphTargetManager&&c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(n),a._enableVelocity&&(n.setMatrix("previousWorld",a._previousTransformationMatrices[d.uniqueId].world),n.setMatrix("previousViewProjection",a._previousTransformationMatrices[d.uniqueId].viewProjection),j&&c.hasThinInstances&&n.setMatrix("world",k)),c._processRendering(d,b,n,h.fillMode,i,j,function(a,b){a||n.setMatrix("world",b)})}a._enableVelocity&&(a._previousTransformationMatrices[d.uniqueId].world=k.clone(),a._previousTransformationMatrices[d.uniqueId].viewProjection=a._scene.getTransformMatrix().clone(),c.skeleton&&a._copyBonesTransformationMatrices(c.skeleton.getTransformMatrices(c),a._previousBonesTransformationMatrices[d.uniqueId]))}}};this._multiRenderTarget.customRenderFunction=function(c,d,e,g){var h;if(a._linkedWithPrePass){if(!a._prePassRenderer.enabled)return;a._scene.getEngine().bindAttachments(a._attachments)}if(g.length){for(b.setColorWrite(!1),h=0;h0){b=this._renderEffects[b[0]].getPostProcesses();b&&(b[0].samples=a)}return!0},a.prototype.setPrePassRenderer=function(a){return!1},a.prototype.dispose=function(){},Object(l.c)([Object(J.d)()],a.prototype,"_name",void 0),a}(),Ap=function(){function a(){this._renderPipelines={}}return Object.defineProperty(a.prototype,"supportedPipelines",{get:function(){var a=[];for(var b in this._renderPipelines)if(this._renderPipelines.hasOwnProperty(b)){var c=this._renderPipelines[b];c.isSupported&&a.push(c)}return a},enumerable:!1,configurable:!0}),a.prototype.addPipeline=function(a){this._renderPipelines[a._name]=a},a.prototype.attachCamerasToRenderPipeline=function(a,b,c){void 0===c&&(c=!1);a=this._renderPipelines[a];a&&a._attachCameras(b,c)},a.prototype.detachCamerasFromRenderPipeline=function(a,b){a=this._renderPipelines[a];a&&a._detachCameras(b)},a.prototype.enableEffectInPipeline=function(a,b,c){a=this._renderPipelines[a];a&&a._enableEffect(b,c)},a.prototype.disableEffectInPipeline=function(a,b,c){a=this._renderPipelines[a];a&&a._disableEffect(b,c)},a.prototype.update=function(){for(var a in this._renderPipelines)if(this._renderPipelines.hasOwnProperty(a)){var b=this._renderPipelines[a];b.isSupported?b._update():(b.dispose(),delete this._renderPipelines[a])}},a.prototype._rebuild=function(){for(var a in this._renderPipelines)this._renderPipelines.hasOwnProperty(a)&&this._renderPipelines[a]._rebuild()},a.prototype.dispose=function(){for(var a in this._renderPipelines)this._renderPipelines.hasOwnProperty(a)&&this._renderPipelines[a].dispose()},a}();Object.defineProperty(P.a.prototype,"postProcessRenderPipelineManager",{get:function(){if(!this._postProcessRenderPipelineManager){var a=this._getComponent(Na.a.NAME_POSTPROCESSRENDERPIPELINEMANAGER);a||(a=new Bp(this),this._addComponent(a)),this._postProcessRenderPipelineManager=new Ap}return this._postProcessRenderPipelineManager},enumerable:!0,configurable:!0});var Bp=function(){function a(a){this.name=Na.a.NAME_POSTPROCESSRENDERPIPELINEMANAGER,this.scene=a}return a.prototype.register=function(){this.scene._gatherRenderTargetsStage.registerStep(Na.a.STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER,this,this._gatherRenderTargets)},a.prototype.rebuild=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager._rebuild()},a.prototype.dispose=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager.dispose()},a.prototype._gatherRenderTargets=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager.update()},a}(),Cp=function(a){function b(b,c,d,e,g){void 0===b&&(b=""),void 0===c&&(c=!0),void 0===d&&(d=C.a.LastCreatedScene),void 0===g&&(g=!0);var h=a.call(this,d.getEngine(),b)||this;h._camerasToBeAttached=[],h.SharpenPostProcessId="SharpenPostProcessEffect",h.ImageProcessingPostProcessId="ImageProcessingPostProcessEffect",h.FxaaPostProcessId="FxaaPostProcessEffect",h.ChromaticAberrationPostProcessId="ChromaticAberrationPostProcessEffect",h.GrainPostProcessId="GrainPostProcessEffect",h._glowLayer=null,h.animations=[],h._imageProcessingConfigurationObserver=null,h._sharpenEnabled=!1,h._bloomEnabled=!1,h._depthOfFieldEnabled=!1,h._depthOfFieldBlurLevel=kp.Low,h._fxaaEnabled=!1,h._imageProcessingEnabled=!0,h._bloomScale=.5,h._chromaticAberrationEnabled=!1,h._grainEnabled=!1,h._buildAllowed=!0,h.onBuildObservable=new f.c,h._resizeObserver=null,h._hardwareScaleLevel=1,h._bloomKernel=64,h._bloomWeight=.15,h._bloomThreshold=.9,h._samples=1,h._hasCleared=!1,h._prevPostProcess=null,h._prevPrevPostProcess=null,h._depthOfFieldSceneObserver=null,h._cameras=e||d.cameras,h._cameras=h._cameras.slice(),h._camerasToBeAttached=h._cameras.slice(),h._buildAllowed=g,h._scene=d;b=h._scene.getEngine().getCaps();h._hdr=c&&(b.textureHalfFloatRender||b.textureFloatRender),h._hdr?b.textureHalfFloatRender?h._defaultPipelineTextureType=r.a.TEXTURETYPE_HALF_FLOAT:b.textureFloatRender&&(h._defaultPipelineTextureType=r.a.TEXTURETYPE_FLOAT):h._defaultPipelineTextureType=r.a.TEXTURETYPE_UNSIGNED_INT,d.postProcessRenderPipelineManager.addPipeline(h);var i=h._scene.getEngine();return h.sharpen=new yp("sharpen",1,null,V.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._sharpenEffect=new bp(i,h.SharpenPostProcessId,function(){return h.sharpen},!0),h.depthOfField=new np(h._scene,null,h._depthOfFieldBlurLevel,h._defaultPipelineTextureType,!0),h.bloom=new ep(h._scene,h._bloomScale,h._bloomWeight,h.bloomKernel,h._defaultPipelineTextureType,!0),h.chromaticAberration=new fp("ChromaticAberration",i.getRenderWidth(),i.getRenderHeight(),1,null,V.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._chromaticAberrationEffect=new bp(i,h.ChromaticAberrationPostProcessId,function(){return h.chromaticAberration},!0),h.grain=new rp("Grain",1,null,V.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._grainEffect=new bp(i,h.GrainPostProcessId,function(){return h.grain},!0),h._resizeObserver=i.onResizeObservable.add(function(){h._hardwareScaleLevel=i.getHardwareScalingLevel(),h.bloomKernel=h.bloomKernel}),h._imageProcessingConfigurationObserver=h._scene.imageProcessingConfiguration.onUpdateParameters.add(function(){h.bloom._downscale._exposure=h._scene.imageProcessingConfiguration.exposure,h.imageProcessingEnabled!==h._scene.imageProcessingConfiguration.isEnabled&&(h._imageProcessingEnabled=h._scene.imageProcessingConfiguration.isEnabled,h._buildPipeline())}),h._buildPipeline(),h}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"sharpenEnabled",{get:function(){return this._sharpenEnabled},set:function(a){this._sharpenEnabled!==a&&(this._sharpenEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomKernel",{get:function(){return this._bloomKernel},set:function(a){this._bloomKernel=a,this.bloom.kernel=a/this._hardwareScaleLevel},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomWeight",{get:function(){return this._bloomWeight},set:function(a){this._bloomWeight!==a&&(this.bloom.weight=a,this._bloomWeight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomThreshold",{get:function(){return this._bloomThreshold},set:function(a){this._bloomThreshold!==a&&(this.bloom.threshold=a,this._bloomThreshold=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomScale",{get:function(){return this._bloomScale},set:function(a){this._bloomScale!==a&&(this._bloomScale=a,this._rebuildBloom(),this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomEnabled",{get:function(){return this._bloomEnabled},set:function(a){this._bloomEnabled!==a&&(this._bloomEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),b.prototype._rebuildBloom=function(){var a=this.bloom;this.bloom=new ep(this._scene,this.bloomScale,this._bloomWeight,this.bloomKernel,this._defaultPipelineTextureType,!1),this.bloom.threshold=a.threshold;for(var b=0;b1){for(var c=0,d=this._cameras;c-1&&(a.depthOfField.depthTexture=b.enableDepthRenderer(b.activeCamera).getDepthMap())})}else{this._scene.onAfterRenderTargetsRenderObservable.remove(this._depthOfFieldSceneObserver);e=this._scene.enableDepthRenderer(this._cameras[0]);this.depthOfField.depthTexture=e.getDepthMap()}this.depthOfField._isReady()||this.depthOfField._updateEffects(),this.addEffect(this.depthOfField),this._setAutoClearAndTextureSharing(this.depthOfField._effects[0],!0)}else this._scene.onAfterRenderTargetsRenderObservable.remove(this._depthOfFieldSceneObserver);this.bloomEnabled&&(this.bloom._isReady()||this.bloom._updateEffects(),this.addEffect(this.bloom),this._setAutoClearAndTextureSharing(this.bloom._effects[0],!0)),this._imageProcessingEnabled&&(this.imageProcessing=new td("imageProcessing",1,null,V.a.BILINEAR_SAMPLINGMODE,b,!1,this._defaultPipelineTextureType),this._hdr?(this.addEffect(new bp(b,this.ImageProcessingPostProcessId,function(){return a.imageProcessing},!0)),this._setAutoClearAndTextureSharing(this.imageProcessing)):this._scene.imageProcessingConfiguration.applyByPostProcess=!1,this.cameras&&0!==this.cameras.length||(this._scene.imageProcessingConfiguration.applyByPostProcess=!1),this.imageProcessing.getEffect()||this.imageProcessing._updateParameters()),this.sharpenEnabled&&(this.sharpen.isReady()||this.sharpen.updateEffect(),this.addEffect(this._sharpenEffect),this._setAutoClearAndTextureSharing(this.sharpen)),this.grainEnabled&&(this.grain.isReady()||this.grain.updateEffect(),this.addEffect(this._grainEffect),this._setAutoClearAndTextureSharing(this.grain)),this.chromaticAberrationEnabled&&(this.chromaticAberration.isReady()||this.chromaticAberration.updateEffect(),this.addEffect(this._chromaticAberrationEffect),this._setAutoClearAndTextureSharing(this.chromaticAberration)),this.fxaaEnabled&&(this.fxaa=new qp("fxaa",1,null,V.a.BILINEAR_SAMPLINGMODE,b,!1,this._defaultPipelineTextureType),this.addEffect(new bp(b,this.FxaaPostProcessId,function(){return a.fxaa},!0)),this._setAutoClearAndTextureSharing(this.fxaa,!0)),null!==this._cameras&&this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name,this._cameras),this._scene.activeCameras&&this._scene.activeCameras.length>1&&(this._scene.autoClear=!0),!this._enableMSAAOnFirstPostProcess(this.samples)&&this.samples>1&&q.a.Warn("MSAA failed to enable, MSAA is only supported in browsers that support webGL >= 2.0"),this.onBuildObservable.notifyObservers(this)}},b.prototype._disposePostProcesses=function(a){void 0===a&&(a=!1);for(var b=0;b1.0) { total_weight+=sampleScreen(col,vec2(0.01*w,2.25*h),0.70); total_weight+=sampleScreen(col,vec2(-1.62*w,-1.74*h),0.67); total_weight+=sampleScreen(col,vec2(2.49*w,0.20*h),0.65); total_weight+=sampleScreen(col,vec2(-2.07*w,1.61*h),0.63); total_weight+=sampleScreen(col,vec2(0.46*w,-2.70*h),0.61); total_weight+=sampleScreen(col,vec2(1.55*w,2.40*h),0.59); total_weight+=sampleScreen(col,vec2(-2.88*w,-0.75*h),0.56); total_weight+=sampleScreen(col,vec2(2.73*w,-1.44*h),0.54); total_weight+=sampleScreen(col,vec2(-1.08*w,3.02*h),0.52); total_weight+=sampleScreen(col,vec2(-1.28*w,-3.05*h),0.49); } if (blur_level>2.0) { total_weight+=sampleScreen(col,vec2(3.11*w,1.43*h),0.46); total_weight+=sampleScreen(col,vec2(-3.36*w,1.08*h),0.44); total_weight+=sampleScreen(col,vec2(1.80*w,-3.16*h),0.41); total_weight+=sampleScreen(col,vec2(0.83*w,3.65*h),0.38); total_weight+=sampleScreen(col,vec2(-3.16*w,-2.19*h),0.34); total_weight+=sampleScreen(col,vec2(3.92*w,-0.53*h),0.31); total_weight+=sampleScreen(col,vec2(-2.59*w,3.12*h),0.26); total_weight+=sampleScreen(col,vec2(-0.20*w,-4.15*h),0.22); total_weight+=sampleScreen(col,vec2(3.02*w,3.00*h),0.15); } col/=total_weight; if (darken>0.0) { col.rgb*=clamp(0.3,1.0,1.05-size*0.5*darken); } return col; } void main(void) { centered_screen_pos=vec2(vUV.x-0.5,vUV.y-0.5); radius2=centered_screen_pos.x*centered_screen_pos.x+centered_screen_pos.y*centered_screen_pos.y; radius=sqrt(radius2); distorted_coords=getDistortedCoords(vUV); vec2 texels_coords=vec2(vUV.x*screen_width,vUV.y*screen_height); float depth=texture2D(depthSampler,distorted_coords).r; float distance=near+(far-near)*depth; vec4 color=texture2D(textureSampler,vUV); float coc=abs(aperture*(screen_distance*(inverse_focal_length-1.0/distance)-1.0)); if (dof_enabled == false || coc<0.07) { coc=0.0; } float edge_blur_amount=0.0; if (edge_blur>0.0) { edge_blur_amount=clamp((radius*2.0-1.0+0.15*edge_blur)*1.5,0.0,1.0)*1.3; } float blur_amount=max(edge_blur_amount,coc); if (blur_amount == 0.0) { gl_FragColor=texture2D(textureSampler,distorted_coords); } else { gl_FragColor=getBlurColor(blur_amount*1.7); if (highlights) { gl_FragColor.rgb+=clamp(coc,0.0,1.0)*texture2D(highlightsSampler,distorted_coords).rgb; } if (blur_noise) { vec2 noise=rand(distorted_coords)*0.01*blur_amount; vec2 blurred_coord=vec2(distorted_coords.x+noise.x,distorted_coords.y+noise.y); gl_FragColor=0.04*texture2D(textureSampler,blurred_coord)+0.96*gl_FragColor; } } if (grain_amount>0.0) { vec4 grain_color=texture2D(grainSampler,texels_coords*0.003); gl_FragColor.rgb+=(-0.5+grain_color.rgb)*0.30*grain_amount; } } ";X.a.ShadersStore.depthOfFieldPixelShader=a;var Dp=function(a){function b(b,c,d,e,f){void 0===e&&(e=1);var g=a.call(this,d.getEngine(),b)||this;return g.LensChromaticAberrationEffect="LensChromaticAberrationEffect",g.HighlightsEnhancingEffect="HighlightsEnhancingEffect",g.LensDepthOfFieldEffect="LensDepthOfFieldEffect",g._pentagonBokehIsEnabled=!1,g._scene=d,g._depthTexture=d.enableDepthRenderer().getDepthMap(),c.grain_texture?g._grainTexture=c.grain_texture:g._createGrainTexture(),g._edgeBlur=c.edge_blur?c.edge_blur:0,g._grainAmount=c.grain_amount?c.grain_amount:0,g._chromaticAberration=c.chromatic_aberration?c.chromatic_aberration:0,g._distortion=c.distortion?c.distortion:0,g._highlightsGain=void 0!==c.dof_gain?c.dof_gain:-1,g._highlightsThreshold=c.dof_threshold?c.dof_threshold:1,g._dofDistance=void 0!==c.dof_focus_distance?c.dof_focus_distance:-1,g._dofAperture=c.dof_aperture?c.dof_aperture:1,g._dofDarken=c.dof_darken?c.dof_darken:0,g._dofPentagon=void 0===c.dof_pentagon||c.dof_pentagon,g._blurNoise=void 0===c.blur_noise||c.blur_noise,g._createChromaticAberrationPostProcess(e),g._createHighlightsPostProcess(e),g._createDepthOfFieldPostProcess(e/4),g.addEffect(new bp(d.getEngine(),g.LensChromaticAberrationEffect,function(){return g._chromaticAberrationPostProcess},!0)),g.addEffect(new bp(d.getEngine(),g.HighlightsEnhancingEffect,function(){return g._highlightsPostProcess},!0)),g.addEffect(new bp(d.getEngine(),g.LensDepthOfFieldEffect,function(){return g._depthOfFieldPostProcess},!0)),-1===g._highlightsGain&&g._disableEffect(g.HighlightsEnhancingEffect,null),d.postProcessRenderPipelineManager.addPipeline(g),f&&d.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(b,f),g}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"LensRenderingPipeline"},Object.defineProperty(b.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"edgeBlur",{get:function(){return this._edgeBlur},set:function(a){this.setEdgeBlur(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"grainAmount",{get:function(){return this._grainAmount},set:function(a){this.setGrainAmount(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"chromaticAberration",{get:function(){return this._chromaticAberration},set:function(a){this.setChromaticAberration(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dofAperture",{get:function(){return this._dofAperture},set:function(a){this.setAperture(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"edgeDistortion",{get:function(){return this._distortion},set:function(a){this.setEdgeDistortion(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dofDistortion",{get:function(){return this._dofDistance},set:function(a){this.setFocusDistance(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"darkenOutOfFocus",{get:function(){return this._dofDarken},set:function(a){this.setDarkenOutOfFocus(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurNoise",{get:function(){return this._blurNoise},set:function(a){this._blurNoise=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pentagonBokeh",{get:function(){return this._pentagonBokehIsEnabled},set:function(a){a?this.enablePentagonBokeh():this.disablePentagonBokeh()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"highlightsGain",{get:function(){return this._highlightsGain},set:function(a){this.setHighlightsGain(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"highlightsThreshold",{get:function(){return this._highlightsThreshold},set:function(a){this.setHighlightsThreshold(a)},enumerable:!1,configurable:!0}),b.prototype.setEdgeBlur=function(a){this._edgeBlur=a},b.prototype.disableEdgeBlur=function(){this._edgeBlur=0},b.prototype.setGrainAmount=function(a){this._grainAmount=a},b.prototype.disableGrain=function(){this._grainAmount=0},b.prototype.setChromaticAberration=function(a){this._chromaticAberration=a},b.prototype.disableChromaticAberration=function(){this._chromaticAberration=0},b.prototype.setEdgeDistortion=function(a){this._distortion=a},b.prototype.disableEdgeDistortion=function(){this._distortion=0},b.prototype.setFocusDistance=function(a){this._dofDistance=a},b.prototype.disableDepthOfField=function(){this._dofDistance=-1},b.prototype.setAperture=function(a){this._dofAperture=a},b.prototype.setDarkenOutOfFocus=function(a){this._dofDarken=a},b.prototype.enablePentagonBokeh=function(){this._highlightsPostProcess.updateEffect("#define PENTAGON "),this._pentagonBokehIsEnabled=!0},b.prototype.disablePentagonBokeh=function(){this._pentagonBokehIsEnabled=!1,this._highlightsPostProcess.updateEffect()},b.prototype.enableNoiseBlur=function(){this._blurNoise=!0},b.prototype.disableNoiseBlur=function(){this._blurNoise=!1},b.prototype.setHighlightsGain=function(a){this._highlightsGain=a},b.prototype.setHighlightsThreshold=function(a){-1===this._highlightsGain&&(this._highlightsGain=1),this._highlightsThreshold=a},b.prototype.disableHighlights=function(){this._highlightsGain=-1},b.prototype.dispose=function(a){void 0===a&&(a=!1),this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name,this._scene.cameras),this._chromaticAberrationPostProcess=null,this._highlightsPostProcess=null,this._depthOfFieldPostProcess=null,this._grainTexture.dispose(),a&&this._scene.disableDepthRenderer()},b.prototype._createChromaticAberrationPostProcess=function(a){var b=this;this._chromaticAberrationPostProcess=new Fc("LensChromaticAberration","chromaticAberration",["chromatic_aberration","screen_width","screen_height","direction","radialIntensity","centerPosition"],[],a,null,V.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1),this._chromaticAberrationPostProcess.onApply=function(a){a.setFloat("chromatic_aberration",b._chromaticAberration),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight()),a.setFloat("radialIntensity",1),a.setFloat2("direction",17,17),a.setFloat2("centerPosition",.5,.5)}},b.prototype._createHighlightsPostProcess=function(a){var b=this;this._highlightsPostProcess=new Fc("LensHighlights","lensHighlights",["gain","threshold","screen_width","screen_height"],[],a,null,V.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1,this._dofPentagon?"#define PENTAGON ":""),this._highlightsPostProcess.onApply=function(a){a.setFloat("gain",b._highlightsGain),a.setFloat("threshold",b._highlightsThreshold),a.setTextureFromPostProcess("textureSampler",b._chromaticAberrationPostProcess),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight())}},b.prototype._createDepthOfFieldPostProcess=function(a){var b=this;this._depthOfFieldPostProcess=new Fc("LensDepthOfField","depthOfField",["grain_amount","blur_noise","screen_width","screen_height","distortion","dof_enabled","screen_distance","aperture","darken","edge_blur","highlights","near","far"],["depthSampler","grainSampler","highlightsSampler"],a,null,V.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1),this._depthOfFieldPostProcess.onApply=function(a){a.setTexture("depthSampler",b._depthTexture),a.setTexture("grainSampler",b._grainTexture),a.setTextureFromPostProcess("textureSampler",b._highlightsPostProcess),a.setTextureFromPostProcess("highlightsSampler",b._depthOfFieldPostProcess),a.setFloat("grain_amount",b._grainAmount),a.setBool("blur_noise",b._blurNoise),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight()),a.setFloat("distortion",b._distortion),a.setBool("dof_enabled",-1!==b._dofDistance),a.setFloat("screen_distance",1/(.1-1/b._dofDistance)),a.setFloat("aperture",b._dofAperture),a.setFloat("darken",b._dofDarken),a.setFloat("edge_blur",b._edgeBlur),a.setBool("highlights",-1!==b._highlightsGain),b._scene.activeCamera&&(a.setFloat("near",b._scene.activeCamera.minZ),a.setFloat("far",b._scene.activeCamera.maxZ))}},b.prototype._createGrainTexture=function(){this._grainTexture=new qd.a("LensNoiseTexture",512,this._scene,!1,V.a.BILINEAR_SAMPLINGMODE),this._grainTexture.wrapU=V.a.WRAP_ADDRESSMODE,this._grainTexture.wrapV=V.a.WRAP_ADDRESSMODE;for(var b,a,b,c=this._grainTexture.getContext(),d=0;d<512;d++)for(var e=0;e<512;e++)b=Math.floor(255*(a=.42,b=.58,Math.random()*(b-a)+a)),c.fillStyle="rgb("+b+", "+b+", "+b+")",c.fillRect(d,e,1,1);this._grainTexture.update(!1)},b}(zp),Ep=function(){this.enabled=!1,this.name="ssao2",this.texturesRequired=[r.a.PREPASS_NORMAL_TEXTURE_TYPE,r.a.PREPASS_DEPTH_TEXTURE_TYPE]};wi=" precision highp float; uniform sampler2D textureSampler; uniform float near; uniform float far; uniform float radius; float scales[16]=float[16]( 0.1, 0.11406250000000001, 0.131640625, 0.15625, 0.187890625, 0.2265625, 0.272265625, 0.325, 0.384765625, 0.4515625, 0.525390625, 0.60625, 0.694140625, 0.7890625, 0.891015625, 1.0 ); varying vec2 vUV; float perspectiveDepthToViewZ(in float invClipZ,in float near,in float far ) { return ( near*far )/( ( far-near )*invClipZ-far ); } float viewZToPerspectiveDepth( in float viewZ,in float near,in float far ) { return ( near*far/viewZ+far)/( far-near ); } float viewZToOrthographicDepth( in float viewZ,in float near,in float far ) { return ( viewZ+near )/( near-far ); } #ifdef SSAO uniform sampler2D randomSampler; uniform sampler2D depthSampler; uniform sampler2D normalSampler; uniform float randTextureTiles; uniform float samplesFactor; uniform vec3 sampleSphere[SAMPLES]; uniform float totalStrength; uniform float base; uniform float xViewport; uniform float yViewport; uniform mat3 depthProjection; uniform float maxZ; uniform float minZAspect; uniform vec2 texelSize; uniform mat4 projection; void main() { vec3 random=texture2D(randomSampler,vUV*randTextureTiles).rgb; float depth=texture2D(depthSampler,vUV).r; float depthSign=depth/abs(depth); depth=depth*depthSign; vec3 normal=texture2D(normalSampler,vUV).rgb; float occlusion=0.0; float correctedRadius=min(radius,minZAspect*depth/near); vec3 vViewRay=vec3((vUV.x*2.0-1.0)*xViewport,(vUV.y*2.0-1.0)*yViewport,depthSign); vec3 vDepthFactor=depthProjection*vec3(1.0,1.0,depth); vec3 origin=vViewRay*vDepthFactor; vec3 rvec=random*2.0-1.0; rvec.z=0.0; float dotProduct=dot(rvec,normal); rvec=1.0-abs(dotProduct)>1e-2 ? rvec : vec3(-rvec.y,0.0,rvec.x); vec3 tangent=normalize(rvec-normal*dot(rvec,normal)); vec3 bitangent=cross(normal,tangent); mat3 tbn=mat3(tangent,bitangent,normal); float difference; for (int i=0; i1.0 || offset.y>1.0) { continue; } float sampleDepth=abs(texture2D(depthSampler,offset.xy).r); difference=depthSign*samplePosition.z-sampleDepth; float rangeCheck=1.0-smoothstep(correctedRadius*0.5,correctedRadius,difference); occlusion+=(difference>=0.0 ? 1.0 : 0.0)*rangeCheck; } occlusion=occlusion*(1.0-smoothstep(maxZ*0.75,maxZ,depth)); float ao=1.0-totalStrength*occlusion*samplesFactor; float result=clamp(ao+base,0.0,1.0); gl_FragColor=vec4(vec3(result),1.0); } #endif #ifdef BILATERAL_BLUR uniform sampler2D depthSampler; uniform float outSize; uniform float samplerOffsets[SAMPLES]; vec4 blur9(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.3846153846)*direction; vec2 off2=vec2(3.2307692308)*direction; color+=texture2D(image,uv)*0.2270270270; color+=texture2D(image,uv+(off1/resolution))*0.3162162162; color+=texture2D(image,uv-(off1/resolution))*0.3162162162; color+=texture2D(image,uv+(off2/resolution))*0.0702702703; color+=texture2D(image,uv-(off2/resolution))*0.0702702703; return color; } vec4 blur13(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.411764705882353)*direction; vec2 off2=vec2(3.2941176470588234)*direction; vec2 off3=vec2(5.176470588235294)*direction; color+=texture2D(image,uv)*0.1964825501511404; color+=texture2D(image,uv+(off1/resolution))*0.2969069646728344; color+=texture2D(image,uv-(off1/resolution))*0.2969069646728344; color+=texture2D(image,uv+(off2/resolution))*0.09447039785044732; color+=texture2D(image,uv-(off2/resolution))*0.09447039785044732; color+=texture2D(image,uv+(off3/resolution))*0.010381362401148057; color+=texture2D(image,uv-(off3/resolution))*0.010381362401148057; return color; } vec4 blur13Bilateral(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.411764705882353)*direction; vec2 off2=vec2(3.2941176470588234)*direction; vec2 off3=vec2(5.176470588235294)*direction; float compareDepth=abs(texture2D(depthSampler,uv).r); float sampleDepth; float weight; float weightSum=30.0; color+=texture2D(image,uv)*30.0; sampleDepth=abs(texture2D(depthSampler,uv+(off1/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off1/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off1/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off1/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv+(off2/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off2/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off2/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off2/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv+(off3/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off3/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off3/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off3/resolution))*weight; return color/weightSum; } void main() { #if EXPENSIVE float compareDepth=abs(texture2D(depthSampler,vUV).r); float texelsize=1.0/outSize; float result=0.0; float weightSum=0.0; for (int i=0; i0?d._ssaoCombinePostProcess.width:d._originalColorPostProcess.width),a.setFloat("near",d._scene.activeCamera.minZ),a.setFloat("far",d._scene.activeCamera.maxZ),a.setFloat("radius",d.radius),d._geometryBufferRenderer?a.setTexture("depthSampler",d._geometryBufferRenderer.getGBuffer().textures[0]):d._prePassRenderer&&a.setTexture("depthSampler",d._prePassRenderer.getRenderTarget().textures[d._prePassRenderer.getIndex(r.a.PREPASS_DEPTH_TEXTURE_TYPE)]),a.setArray("samplerOffsets",d._samplerOffsets))},this._blurVPostProcess=new Fc("BlurV","ssao2",["outSize","samplerOffsets","near","far","radius"],["depthSampler"],b,null,V.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1,"#define BILATERAL_BLUR #define BILATERAL_BLUR_V #define SAMPLES 16 #define EXPENSIVE "+(e?"1":"0")+" ",c),this._blurVPostProcess.onApply=function(a){d._scene.activeCamera&&(a.setFloat("outSize",d._ssaoCombinePostProcess.height>0?d._ssaoCombinePostProcess.height:d._originalColorPostProcess.height),a.setFloat("near",d._scene.activeCamera.minZ),a.setFloat("far",d._scene.activeCamera.maxZ),a.setFloat("radius",d.radius),d._geometryBufferRenderer?a.setTexture("depthSampler",d._geometryBufferRenderer.getGBuffer().textures[0]):d._prePassRenderer&&a.setTexture("depthSampler",d._prePassRenderer.getRenderTarget().textures[d._prePassRenderer.getIndex(r.a.PREPASS_DEPTH_TEXTURE_TYPE)]),a.setArray("samplerOffsets",d._samplerOffsets))},this._blurHPostProcess.samples=this.textureSamples,this._blurVPostProcess.samples=this.textureSamples},b.prototype._rebuild=function(){a.prototype._rebuild.call(this)},b.prototype._radicalInverse_VdC=function(a){return this._bits[0]=a,this._bits[0]=(this._bits[0]<<16|this._bits[0]>>16)>>>0,this._bits[0]=(1431655765&this._bits[0])<<1|(2863311530&this._bits[0])>>>1>>>0,this._bits[0]=(858993459&this._bits[0])<<2|(3435973836&this._bits[0])>>>2>>>0,this._bits[0]=(252645135&this._bits[0])<<4|(4042322160&this._bits[0])>>>4>>>0,this._bits[0]=(16711935&this._bits[0])<<8|(4278255360&this._bits[0])>>>8>>>0,23283064365386963e-26*this._bits[0]},b.prototype._hammersley=function(a,b){return[a/b,this._radicalInverse_VdC(a)]},b.prototype._hemisphereSample_uniform=function(a,b){b=2*b*Math.PI;a=1-(.85*a+.15);var c=Math.sqrt(1-a*a);return new g.e(Math.cos(b)*c,Math.sin(b)*c,a)},b.prototype._generateHemisphere=function(){for(var a,b=this.samples,c=[],d=0;d0.0) hitCoord-=dir; else hitCoord+=dir; info.color+=texture2D(textureSampler,projectedCoord.xy).rgb; } projectedCoord=projection*vec4(hitCoord,1.0); projectedCoord.xy/=projectedCoord.w; projectedCoord.xy=0.5*projectedCoord.xy+vec2(0.5); info.coords=vec4(projectedCoord.xy,sampledDepth,1.0); info.color+=texture2D(textureSampler,projectedCoord.xy).rgb; info.color/=float(SMOOTH_STEPS+1); return info; } ReflectionInfo getReflectionInfo(vec3 dir,vec3 hitCoord) { ReflectionInfo info; vec4 projectedCoord; float sampledDepth; dir*=step; for(int i=0; i>0)),a.push("#define SMOOTH_STEPS "+(this._smoothSteps>>0)),this.updateEffect(a.join(" "))},b._Parse=function(a,c,d,e){return J.a.Parse(function(){return new b(a.name,d,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.textureType,a.reusable)},a,d,e)},Object(l.c)([Object(J.d)()],b.prototype,"threshold",void 0),Object(l.c)([Object(J.d)()],b.prototype,"strength",void 0),Object(l.c)([Object(J.d)()],b.prototype,"reflectionSpecularFalloffExponent",void 0),Object(l.c)([Object(J.d)()],b.prototype,"step",void 0),Object(l.c)([Object(J.d)()],b.prototype,"roughnessFactor",void 0),Object(l.c)([Object(J.d)()],b.prototype,"enableSmoothReflections",null),Object(l.c)([Object(J.d)()],b.prototype,"reflectionSamples",null),Object(l.c)([Object(J.d)()],b.prototype,"smoothSteps",null),b}(Fc);Object(i.b)("BABYLON.ScreenSpaceReflectionPostProcess",Ip);rb="uniform sampler2D textureSampler; varying vec2 vUV; #if defined(PASS_POST_PROCESS) void main(void) { vec4 color=texture2D(textureSampler,vUV); gl_FragColor=color; } #endif #if defined(DOWN_SAMPLE_X4) uniform vec2 dsOffsets[16]; void main(void) { vec4 average=vec4(0.0,0.0,0.0,0.0); average=texture2D(textureSampler,vUV+dsOffsets[0]); average+=texture2D(textureSampler,vUV+dsOffsets[1]); average+=texture2D(textureSampler,vUV+dsOffsets[2]); average+=texture2D(textureSampler,vUV+dsOffsets[3]); average+=texture2D(textureSampler,vUV+dsOffsets[4]); average+=texture2D(textureSampler,vUV+dsOffsets[5]); average+=texture2D(textureSampler,vUV+dsOffsets[6]); average+=texture2D(textureSampler,vUV+dsOffsets[7]); average+=texture2D(textureSampler,vUV+dsOffsets[8]); average+=texture2D(textureSampler,vUV+dsOffsets[9]); average+=texture2D(textureSampler,vUV+dsOffsets[10]); average+=texture2D(textureSampler,vUV+dsOffsets[11]); average+=texture2D(textureSampler,vUV+dsOffsets[12]); average+=texture2D(textureSampler,vUV+dsOffsets[13]); average+=texture2D(textureSampler,vUV+dsOffsets[14]); average+=texture2D(textureSampler,vUV+dsOffsets[15]); average/=16.0; gl_FragColor=average; } #endif #if defined(BRIGHT_PASS) uniform vec2 dsOffsets[4]; uniform float brightThreshold; void main(void) { vec4 average=vec4(0.0,0.0,0.0,0.0); average=texture2D(textureSampler,vUV+vec2(dsOffsets[0].x,dsOffsets[0].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[1].x,dsOffsets[1].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[2].x,dsOffsets[2].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[3].x,dsOffsets[3].y)); average*=0.25; float luminance=length(average.rgb); if (luminanceshadowPixelDepth) accumFog+=sunColor*computeScattering(dot(rayDirection,sunDirection)); currentPosition+=stepL; } accumFog/=NB_STEPS; vec3 color=accumFog*scatteringPower; gl_FragColor=vec4(color*exp(color) ,1.0); } #endif #if defined(VLSMERGE) uniform sampler2D originalSampler; void main(void) { gl_FragColor=texture2D(originalSampler,vUV)+texture2D(textureSampler,vUV); } #endif #if defined(LUMINANCE) uniform vec2 lumOffsets[4]; void main() { float average=0.0; vec4 color=vec4(0.0); float maximum=-1e20; vec3 weight=vec3(0.299,0.587,0.114); for (int i=0; i<4; i++) { color=texture2D(textureSampler,vUV+ lumOffsets[i]); float GreyValue=dot(color.rgb,vec3(0.33,0.33,0.33)); #ifdef WEIGHTED_AVERAGE float GreyValue=dot(color.rgb,weight); #endif #ifdef BRIGHTNESS float GreyValue=max(color.r,max(color.g,color.b)); #endif #ifdef HSL_COMPONENT float GreyValue=0.5*(max(color.r,max(color.g,color.b))+min(color.r,min(color.g,color.b))); #endif #ifdef MAGNITUDE float GreyValue=length(color.rgb); #endif maximum=max(maximum,GreyValue); average+=(0.25*log(1e-5+GreyValue)); } average=exp(average); gl_FragColor=vec4(average,maximum,0.0,1.0); } #endif #if defined(LUMINANCE_DOWN_SAMPLE) uniform vec2 dsOffsets[9]; uniform float halfDestPixelSize; #ifdef FINAL_DOWN_SAMPLER #include #endif void main() { vec4 color=vec4(0.0); float average=0.0; for (int i=0; i<9; i++) { color=texture2D(textureSampler,vUV+vec2(halfDestPixelSize,halfDestPixelSize)+dsOffsets[i]); average+=color.r; } average/=9.0; #ifdef FINAL_DOWN_SAMPLER gl_FragColor=pack(average); #else gl_FragColor=vec4(average,average,0.0,1.0); #endif } #endif #if defined(HDR) uniform sampler2D textureAdderSampler; uniform float averageLuminance; void main() { vec4 color=texture2D(textureAdderSampler,vUV); #ifndef AUTO_EXPOSURE vec4 adjustedColor=color/averageLuminance; color=adjustedColor; color.a=1.0; #endif gl_FragColor=color; } #endif #if defined(LENS_FLARE) #define GHOSTS 3 uniform sampler2D lensColorSampler; uniform float strength; uniform float ghostDispersal; uniform float haloWidth; uniform vec2 resolution; uniform float distortionStrength; float hash(vec2 p) { float h=dot(p,vec2(127.1,311.7)); return -1.0+2.0*fract(sin(h)*43758.5453123); } float noise(in vec2 p) { vec2 i=floor(p); vec2 f=fract(p); vec2 u=f*f*(3.0-2.0*f); return mix(mix(hash(i+vec2(0.0,0.0)), hash(i+vec2(1.0,0.0)),u.x), mix(hash(i+vec2(0.0,1.0)), hash(i+vec2(1.0,1.0)),u.x),u.y); } float fbm(vec2 p) { float f=0.0; f+=0.5000*noise(p); p*=2.02; f+=0.2500*noise(p); p*=2.03; f+=0.1250*noise(p); p*=2.01; f+=0.0625*noise(p); p*=2.04; f/=0.9375; return f; } vec3 pattern(vec2 uv) { vec2 p=-1.0+2.0*uv; float p2=dot(p,p); float f=fbm(vec2(15.0*p2))/2.0; float r=0.2+0.6*sin(12.5*length(uv-vec2(0.5))); float g=0.2+0.6*sin(20.5*length(uv-vec2(0.5))); float b=0.2+0.6*sin(17.2*length(uv-vec2(0.5))); return (1.0-f)*vec3(r,g,b); } float luminance(vec3 color) { return dot(color.rgb,vec3(0.2126,0.7152,0.0722)); } vec4 textureDistorted(sampler2D tex,vec2 texcoord,vec2 direction,vec3 distortion) { return vec4( texture2D(tex,texcoord+direction*distortion.r).r, texture2D(tex,texcoord+direction*distortion.g).g, texture2D(tex,texcoord+direction*distortion.b).b, 1.0 ); } void main(void) { vec2 uv=-vUV+vec2(1.0); vec2 ghostDir=(vec2(0.5)-uv)*ghostDispersal; vec2 texelSize=1.0/resolution; vec3 distortion=vec3(-texelSize.x*distortionStrength,0.0,texelSize.x*distortionStrength); vec4 result=vec4(0.0); float ghostIndice=1.0; for (int i=0; i=nSamples) break; vec2 offset1=vUV+velocity*(float(i)/float(nSamples-1)-0.5); result+=texture2D(textureSampler,offset1); } gl_FragColor=result/float(nSamples); } #endif ";X.a.ShadersStore.standardPixelShader=rb;var Jp=function(a){function b(b,c,d,e,f){void 0===e&&(e=null);b=a.call(this,c.getEngine(),b)||this;return b.downSampleX4PostProcess=null,b.brightPassPostProcess=null,b.blurHPostProcesses=[],b.blurVPostProcesses=[],b.textureAdderPostProcess=null,b.volumetricLightPostProcess=null,b.volumetricLightSmoothXPostProcess=null,b.volumetricLightSmoothYPostProcess=null,b.volumetricLightMergePostProces=null,b.volumetricLightFinalPostProcess=null,b.luminancePostProcess=null,b.luminanceDownSamplePostProcesses=[],b.hdrPostProcess=null,b.textureAdderFinalPostProcess=null,b.lensFlareFinalPostProcess=null,b.hdrFinalPostProcess=null,b.lensFlarePostProcess=null,b.lensFlareComposePostProcess=null,b.motionBlurPostProcess=null,b.depthOfFieldPostProcess=null,b.fxaaPostProcess=null,b.screenSpaceReflectionPostProcess=null,b.brightThreshold=1,b.blurWidth=512,b.horizontalBlur=!1,b.lensTexture=null,b.volumetricLightCoefficient=.2,b.volumetricLightPower=4,b.volumetricLightBlurScale=64,b.sourceLight=null,b.hdrMinimumLuminance=1,b.hdrDecreaseRate=.5,b.hdrIncreaseRate=.5,b.lensColorTexture=null,b.lensFlareStrength=20,b.lensFlareGhostDispersal=1.4,b.lensFlareHaloWidth=.7,b.lensFlareDistortionStrength=16,b.lensFlareBlurWidth=512,b.lensStarTexture=null,b.lensFlareDirtTexture=null,b.depthOfFieldDistance=10,b.depthOfFieldBlurWidth=64,b.animations=[],b._currentDepthOfFieldSource=null,b._fixedExposure=1,b._currentExposure=1,b._hdrAutoExposure=!1,b._hdrCurrentLuminance=1,b._motionStrength=1,b._isObjectBasedMotionBlur=!1,b._camerasToBeAttached=[],b._bloomEnabled=!1,b._depthOfFieldEnabled=!1,b._vlsEnabled=!1,b._lensFlareEnabled=!1,b._hdrEnabled=!1,b._motionBlurEnabled=!1,b._fxaaEnabled=!1,b._screenSpaceReflectionsEnabled=!1,b._motionBlurSamples=64,b._volumetricLightStepsCount=50,b._samples=1,b._cameras=f||c.cameras,b._cameras=b._cameras.slice(),b._camerasToBeAttached=b._cameras.slice(),b._scene=c,b._basePostProcess=e,b._ratio=d,b._floatTextureType=c.getEngine().getCaps().textureFloatRender?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_HALF_FLOAT,c.postProcessRenderPipelineManager.addPipeline(b),b._buildPipeline(),b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"exposure",{get:function(){return this._fixedExposure},set:function(a){this._fixedExposure=a,this._currentExposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hdrAutoExposure",{get:function(){return this._hdrAutoExposure},set:function(a){if(this._hdrAutoExposure=a,this.hdrPostProcess){var b=["#define HDR"];a&&b.push("#define AUTO_EXPOSURE"),this.hdrPostProcess.updateEffect(b.join(" "))}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"motionStrength",{get:function(){return this._motionStrength},set:function(a){this._motionStrength=a,this._isObjectBasedMotionBlur&&this.motionBlurPostProcess&&(this.motionBlurPostProcess.motionStrength=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"objectBasedMotionBlur",{get:function(){return this._isObjectBasedMotionBlur},set:function(a){var b=this._isObjectBasedMotionBlur!==a;this._isObjectBasedMotionBlur=a,b&&this._buildPipeline()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"BloomEnabled",{get:function(){return this._bloomEnabled},set:function(a){this._bloomEnabled!==a&&(this._bloomEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"DepthOfFieldEnabled",{get:function(){return this._depthOfFieldEnabled},set:function(a){this._depthOfFieldEnabled!==a&&(this._depthOfFieldEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"LensFlareEnabled",{get:function(){return this._lensFlareEnabled},set:function(a){this._lensFlareEnabled!==a&&(this._lensFlareEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"HDREnabled",{get:function(){return this._hdrEnabled},set:function(a){this._hdrEnabled!==a&&(this._hdrEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"VLSEnabled",{get:function(){return this._vlsEnabled},set:function(a){if(this._vlsEnabled!==a){if(a&&!this._scene.enableGeometryBufferRenderer())return void q.a.Warn("Geometry renderer is not supported, cannot create volumetric lights in Standard Rendering Pipeline");this._vlsEnabled=a,this._buildPipeline()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"MotionBlurEnabled",{get:function(){return this._motionBlurEnabled},set:function(a){this._motionBlurEnabled!==a&&(this._motionBlurEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fxaaEnabled",{get:function(){return this._fxaaEnabled},set:function(a){this._fxaaEnabled!==a&&(this._fxaaEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"screenSpaceReflectionsEnabled",{get:function(){return this._screenSpaceReflectionsEnabled},set:function(a){this._screenSpaceReflectionsEnabled!==a&&(this._screenSpaceReflectionsEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"volumetricLightStepsCount",{get:function(){return this._volumetricLightStepsCount},set:function(a){this.volumetricLightPostProcess&&this.volumetricLightPostProcess.updateEffect("#define VLS #define NB_STEPS "+a.toFixed(1)),this._volumetricLightStepsCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"motionBlurSamples",{get:function(){return this._motionBlurSamples},set:function(a){this.motionBlurPostProcess&&(this._isObjectBasedMotionBlur?this.motionBlurPostProcess.motionBlurSamples=a:this.motionBlurPostProcess.updateEffect("#define MOTION_BLUR #define MAX_MOTION_SAMPLES "+a.toFixed(1))),this._motionBlurSamples=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samples",{get:function(){return this._samples},set:function(a){this._samples!==a&&(this._samples=a,this._buildPipeline())},enumerable:!1,configurable:!0}),b.prototype._buildPipeline=function(){var a=this,b=this._ratio,c=this._scene;this._disposePostProcesses(),null!==this._cameras&&(this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name,this._cameras),this._cameras=this._camerasToBeAttached.slice()),this._reset(),this._screenSpaceReflectionsEnabled&&(this.screenSpaceReflectionPostProcess=new Ip("HDRPass",c,b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,this._floatTextureType),this.screenSpaceReflectionPostProcess.onApplyObservable.add(function(){a._currentDepthOfFieldSource=a.screenSpaceReflectionPostProcess}),this.addEffect(new bp(c.getEngine(),"HDRScreenSpaceReflections",function(){return a.screenSpaceReflectionPostProcess},!0))),this._basePostProcess?this.originalPostProcess=this._basePostProcess:this.originalPostProcess=new Fc("HDRPass","standard",[],[],b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",this._floatTextureType),this.originalPostProcess.autoClear=!this.screenSpaceReflectionPostProcess,this.originalPostProcess.onApplyObservable.add(function(){a._currentDepthOfFieldSource=a.originalPostProcess}),this.addEffect(new bp(c.getEngine(),"HDRPassPostProcess",function(){return a.originalPostProcess},!0)),this._bloomEnabled&&(this._createDownSampleX4PostProcess(c,b/4),this._createBrightPassPostProcess(c,b/4),this._createBlurPostProcesses(c,b/4,1),this._createTextureAdderPostProcess(c,b),this.textureAdderFinalPostProcess=new Fc("HDRDepthOfFieldSource","standard",[],[],b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRBaseDepthOfFieldSource",function(){return a.textureAdderFinalPostProcess},!0))),this._vlsEnabled&&(this._createVolumetricLightPostProcess(c,b),this.volumetricLightFinalPostProcess=new Fc("HDRVLSFinal","standard",[],[],b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRVLSFinal",function(){return a.volumetricLightFinalPostProcess},!0))),this._lensFlareEnabled&&(this._createLensFlarePostProcess(c,b),this.lensFlareFinalPostProcess=new Fc("HDRPostLensFlareDepthOfFieldSource","standard",[],[],b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRPostLensFlareDepthOfFieldSource",function(){return a.lensFlareFinalPostProcess},!0))),this._hdrEnabled&&(this._createLuminancePostProcesses(c,this._floatTextureType),this._createHdrPostProcess(c,b),this.hdrFinalPostProcess=new Fc("HDRPostHDReDepthOfFieldSource","standard",[],[],b,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRPostHDReDepthOfFieldSource",function(){return a.hdrFinalPostProcess},!0))),this._depthOfFieldEnabled&&(this._createBlurPostProcesses(c,b/2,3,"depthOfFieldBlurWidth"),this._createDepthOfFieldPostProcess(c,b)),this._motionBlurEnabled&&this._createMotionBlurPostProcess(c,b),this._fxaaEnabled&&(this.fxaaPostProcess=new qp("fxaa",1,null,V.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRFxaa",function(){return a.fxaaPostProcess},!0))),null!==this._cameras&&this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name,this._cameras),!this._enableMSAAOnFirstPostProcess(this._samples)&&this._samples>1&&q.a.Warn("MSAA failed to enable, MSAA is only supported in browsers that support webGL >= 2.0")},b.prototype._createDownSampleX4PostProcess=function(a,b){var c=this,d=new Array(32);this.downSampleX4PostProcess=new Fc("HDRDownSampleX4","standard",["dsOffsets"],[],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define DOWN_SAMPLE_X4",this._floatTextureType),this.downSampleX4PostProcess.onApply=function(a){for(var b=0,e=c.downSampleX4PostProcess.width,f=c.downSampleX4PostProcess.height,g=-2;g<2;g++)for(var h=-2;h<2;h++)d[b]=(g+.5)*(1/e),d[b+1]=(h+.5)*(1/f),b+=2;a.setArray2("dsOffsets",d)},this.addEffect(new bp(a.getEngine(),"HDRDownSampleX4",function(){return c.downSampleX4PostProcess},!0))},b.prototype._createBrightPassPostProcess=function(a,b){var c=this,d=new Array(8);this.brightPassPostProcess=new Fc("HDRBrightPass","standard",["dsOffsets","brightThreshold"],[],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define BRIGHT_PASS",this._floatTextureType),this.brightPassPostProcess.onApply=function(a){var b=1/c.brightPassPostProcess.width,e=1/c.brightPassPostProcess.height;d[0]=-.5*b,d[1]=.5*e,d[2]=.5*b,d[3]=.5*e,d[4]=-.5*b,d[5]=-.5*e,d[6]=.5*b,d[7]=-.5*e,a.setArray2("dsOffsets",d),a.setFloat("brightThreshold",c.brightThreshold)},this.addEffect(new bp(a.getEngine(),"HDRBrightPass",function(){return c.brightPassPostProcess},!0))},b.prototype._createBlurPostProcesses=function(a,b,c,d){var e=this;void 0===d&&(d="blurWidth");var f=a.getEngine(),h=new Vh("HDRBlurH_"+c,new g.d(1,0),this[d],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,this._floatTextureType),i=new Vh("HDRBlurV_"+c,new g.d(0,1),this[d],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,this._floatTextureType);h.onActivateObservable.add(function(){var a=h.width/f.getRenderWidth();h.kernel=e[d]*a}),i.onActivateObservable.add(function(){var a=i.height/f.getRenderHeight();i.kernel=e.horizontalBlur?64*a:e[d]*a}),this.addEffect(new bp(a.getEngine(),"HDRBlurH"+c,function(){return h},!0)),this.addEffect(new bp(a.getEngine(),"HDRBlurV"+c,function(){return i},!0)),this.blurHPostProcesses.push(h),this.blurVPostProcesses.push(i)},b.prototype._createTextureAdderPostProcess=function(a,b){var c=this;this.textureAdderPostProcess=new Fc("HDRTextureAdder","standard",["exposure"],["otherSampler","lensSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define TEXTURE_ADDER",this._floatTextureType),this.textureAdderPostProcess.onApply=function(a){a.setTextureFromPostProcess("otherSampler",c._vlsEnabled?c._currentDepthOfFieldSource:c.originalPostProcess),a.setTexture("lensSampler",c.lensTexture),a.setFloat("exposure",c._currentExposure),c._currentDepthOfFieldSource=c.textureAdderFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDRTextureAdder",function(){return c.textureAdderPostProcess},!0))},b.prototype._createVolumetricLightPostProcess=function(a,b){var c=this,d=a.enableGeometryBufferRenderer();d.enablePosition=!0;var e=d.getGBuffer();this.volumetricLightPostProcess=new Fc("HDRVLS","standard",["shadowViewProjection","cameraPosition","sunDirection","sunColor","scatteringCoefficient","scatteringPower","depthValues"],["shadowMapSampler","positionSampler"],b/8,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define VLS #define NB_STEPS "+this._volumetricLightStepsCount.toFixed(1));var f=g.d.Zero();this.volumetricLightPostProcess.onApply=function(a){if(c.sourceLight&&c.sourceLight.getShadowGenerator()&&c._scene.activeCamera){var b=c.sourceLight.getShadowGenerator();a.setTexture("shadowMapSampler",b.getShadowMap()),a.setTexture("positionSampler",e.textures[2]),a.setColor3("sunColor",c.sourceLight.diffuse),a.setVector3("sunDirection",c.sourceLight.getShadowDirection()),a.setVector3("cameraPosition",c._scene.activeCamera.globalPosition),a.setMatrix("shadowViewProjection",b.getTransformMatrix()),a.setFloat("scatteringCoefficient",c.volumetricLightCoefficient),a.setFloat("scatteringPower",c.volumetricLightPower),f.x=c.sourceLight.getDepthMinZ(c._scene.activeCamera),f.y=c.sourceLight.getDepthMaxZ(c._scene.activeCamera),a.setVector2("depthValues",f)}},this.addEffect(new bp(a.getEngine(),"HDRVLS",function(){return c.volumetricLightPostProcess},!0)),this._createBlurPostProcesses(a,b/4,0,"volumetricLightBlurScale"),this.volumetricLightMergePostProces=new Fc("HDRVLSMerge","standard",[],["originalSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define VLSMERGE"),this.volumetricLightMergePostProces.onApply=function(a){a.setTextureFromPostProcess("originalSampler",c._bloomEnabled?c.textureAdderFinalPostProcess:c.originalPostProcess),c._currentDepthOfFieldSource=c.volumetricLightFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDRVLSMerge",function(){return c.volumetricLightMergePostProces},!0))},b.prototype._createLuminancePostProcesses=function(a,c){var d=this,e=Math.pow(3,b.LuminanceSteps);this.luminancePostProcess=new Fc("HDRLuminance","standard",["lumOffsets"],[],{width:e,height:e},null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LUMINANCE",c);var f=[];this.luminancePostProcess.onApply=function(a){var b=1/d.luminancePostProcess.width,c=1/d.luminancePostProcess.height;f[0]=-.5*b,f[1]=.5*c,f[2]=.5*b,f[3]=.5*c,f[4]=-.5*b,f[5]=-.5*c,f[6]=.5*b,f[7]=-.5*c,a.setArray2("lumOffsets",f)},this.addEffect(new bp(a.getEngine(),"HDRLuminance",function(){return d.luminancePostProcess},!0));for(var h=b.LuminanceSteps-1;h>=0;h--){e=Math.pow(3,h);var i="#define LUMINANCE_DOWN_SAMPLE ";0===h&&(i+="#define FINAL_DOWN_SAMPLER");e=new Fc("HDRLuminanceDownSample"+h,"standard",["dsOffsets","halfDestPixelSize"],[],{width:e,height:e},null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,i,c);this.luminanceDownSamplePostProcesses.push(e)}var j=this.luminancePostProcess;this.luminanceDownSamplePostProcesses.forEach(function(b,c){var e=new Array(18);b.onApply=function(a){if(j){for(var f=0,g=-1;g<2;g++)for(var h=-1;h<2;h++)e[f]=g/j.width,e[f+1]=h/j.height,f+=2;a.setArray2("dsOffsets",e),a.setFloat("halfDestPixelSize",.5/j.width),j=c===d.luminanceDownSamplePostProcesses.length-1?d.luminancePostProcess:b}},c===d.luminanceDownSamplePostProcesses.length-1&&(b.onAfterRender=function(){var b=a.getEngine().readPixels(0,0,1,1),c=new g.f(1/16581375,1/65025,1/255,1);b.then(function(a){a=new Uint8Array(a.buffer);d._hdrCurrentLuminance=(a[0]*c.x+a[1]*c.y+a[2]*c.z+a[3]*c.w)/100})}),d.addEffect(new bp(a.getEngine(),"HDRLuminanceDownSample"+c,function(){return b},!0))})},b.prototype._createHdrPostProcess=function(a,b){var c=this,d=["#define HDR"];this._hdrAutoExposure&&d.push("#define AUTO_EXPOSURE"),this.hdrPostProcess=new Fc("HDR","standard",["averageLuminance"],["textureAdderSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,d.join(" "),r.a.TEXTURETYPE_UNSIGNED_INT);var e=1,f=0,g=0;this.hdrPostProcess.onApply=function(b){if(b.setTextureFromPostProcess("textureAdderSampler",c._currentDepthOfFieldSource),f+=a.getEngine().getDeltaTime(),e<0)e=c._hdrCurrentLuminance;else{var d=(g-f)/1e3;c._hdrCurrentLuminancee-c.hdrIncreaseRate*d?e-=c.hdrIncreaseRate*d:e=c._hdrCurrentLuminance}c.hdrAutoExposure?c._currentExposure=c._fixedExposure/e:(e=I.a.Clamp(e,c.hdrMinimumLuminance,1e20),b.setFloat("averageLuminance",e)),g=f,c._currentDepthOfFieldSource=c.hdrFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDR",function(){return c.hdrPostProcess},!0))},b.prototype._createLensFlarePostProcess=function(a,b){var c=this;this.lensFlarePostProcess=new Fc("HDRLensFlare","standard",["strength","ghostDispersal","haloWidth","resolution","distortionStrength"],["lensColorSampler"],b/2,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LENS_FLARE",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(a.getEngine(),"HDRLensFlare",function(){return c.lensFlarePostProcess},!0)),this._createBlurPostProcesses(a,b/4,2,"lensFlareBlurWidth"),this.lensFlareComposePostProcess=new Fc("HDRLensFlareCompose","standard",["lensStarMatrix"],["otherSampler","lensDirtSampler","lensStarSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LENS_FLARE_COMPOSE",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(a.getEngine(),"HDRLensFlareCompose",function(){return c.lensFlareComposePostProcess},!0));var d=new g.d(0,0);this.lensFlarePostProcess.onApply=function(a){a.setTextureFromPostProcess("textureSampler",c._bloomEnabled?c.blurHPostProcesses[0]:c.originalPostProcess),a.setTexture("lensColorSampler",c.lensColorTexture),a.setFloat("strength",c.lensFlareStrength),a.setFloat("ghostDispersal",c.lensFlareGhostDispersal),a.setFloat("haloWidth",c.lensFlareHaloWidth),d.x=c.lensFlarePostProcess.width,d.y=c.lensFlarePostProcess.height,a.setVector2("resolution",d),a.setFloat("distortionStrength",c.lensFlareDistortionStrength)};var e=g.a.FromValues(2,0,-1,0,0,2,-1,0,0,0,1,0,0,0,0,1),f=g.a.FromValues(.5,0,.5,0,0,.5,.5,0,0,0,1,0,0,0,0,1);this.lensFlareComposePostProcess.onApply=function(a){if(c._scene.activeCamera){a.setTextureFromPostProcess("otherSampler",c.lensFlarePostProcess),a.setTexture("lensDirtSampler",c.lensFlareDirtTexture),a.setTexture("lensStarSampler",c.lensStarTexture);var b=c._scene.activeCamera.getViewMatrix().getRow(0),d=c._scene.activeCamera.getViewMatrix().getRow(2);b=g.e.Dot(b.toVector3(),new g.e(1,0,0))+g.e.Dot(d.toVector3(),new g.e(0,0,1));b*=4;d=g.a.FromValues(.5*Math.cos(b),-Math.sin(b),0,0,Math.sin(b),.5*Math.cos(b),0,0,0,0,1,0,0,0,0,1);b=f.multiply(d).multiply(e);a.setMatrix("lensStarMatrix",b),c._currentDepthOfFieldSource=c.lensFlareFinalPostProcess}}},b.prototype._createDepthOfFieldPostProcess=function(a,b){var c=this;this.depthOfFieldPostProcess=new Fc("HDRDepthOfField","standard",["distance"],["otherSampler","depthSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define DEPTH_OF_FIELD",r.a.TEXTURETYPE_UNSIGNED_INT),this.depthOfFieldPostProcess.onApply=function(a){a.setTextureFromPostProcess("otherSampler",c._currentDepthOfFieldSource),a.setTexture("depthSampler",c._getDepthTexture()),a.setFloat("distance",c.depthOfFieldDistance)},this.addEffect(new bp(a.getEngine(),"HDRDepthOfField",function(){return c.depthOfFieldPostProcess},!0))},b.prototype._createMotionBlurPostProcess=function(a,b){var c=this;if(this._isObjectBasedMotionBlur){var d=new wp("HDRMotionBlur",a,b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,r.a.TEXTURETYPE_UNSIGNED_INT);d.motionStrength=this.motionStrength,d.motionBlurSamples=this.motionBlurSamples,this.motionBlurPostProcess=d}else{this.motionBlurPostProcess=new Fc("HDRMotionBlur","standard",["inverseViewProjection","prevViewProjection","screenSize","motionScale","motionStrength"],["depthSampler"],b,null,V.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define MOTION_BLUR #define MAX_MOTION_SAMPLES "+this.motionBlurSamples.toFixed(1),r.a.TEXTURETYPE_UNSIGNED_INT);var e,f=g.a.Identity(),h=g.a.Identity();d=g.a.Identity();var i=g.d.Zero();this.motionBlurPostProcess.onApply=function(b){(d=a.getProjectionMatrix().multiply(a.getViewMatrix())).invertToRef(h),b.setMatrix("inverseViewProjection",h),b.setMatrix("prevViewProjection",f),f=d,i.x=c.motionBlurPostProcess.width,i.y=c.motionBlurPostProcess.height,b.setVector2("screenSize",i),e=a.getEngine().getFps()/60,b.setFloat("motionScale",e),b.setFloat("motionStrength",c.motionStrength),b.setTexture("depthSampler",c._getDepthTexture())}}this.addEffect(new bp(a.getEngine(),"HDRMotionBlur",function(){return c.motionBlurPostProcess},!0))},b.prototype._getDepthTexture=function(){return this._scene.getEngine().getCaps().drawBuffersExtension?this._scene.enableGeometryBufferRenderer().getGBuffer().textures[0]:this._scene.enableDepthRenderer().getDepthMap()},b.prototype._disposePostProcesses=function(){for(var a=0;a #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; uniform vec2 depthValues; #if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif void main(void) { vec3 positionUpdated=position; #if (defined(ALPHATEST) || defined(NEED_UV)) && defined(UV1) vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #if defined(ALPHATEST) || defined(BASIC_RENDER) #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif } ";X.a.ShadersStore.volumetricLightScatteringPassVertexShader=rb;a="#if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; #endif #if defined(ALPHATEST) uniform sampler2D diffuseSampler; #endif void main(void) { #if defined(ALPHATEST) vec4 diffuseColor=texture2D(diffuseSampler,vUV); if (diffuseColor.a<0.4) discard; #endif gl_FragColor=vec4(0.0,0.0,0.0,1.0); } ";X.a.ShadersStore.volumetricLightScatteringPassPixelShader=a;var Mp=function(a){function b(c,d,e,f,h,i,j,k,m){void 0===h&&(h=100),void 0===i&&(i=V.a.BILINEAR_SAMPLINGMODE);var o=a.call(this,c,"volumetricLightScattering",["decay","exposure","weight","meshPositionOnScreen","density"],["lightScatteringSampler"],d.postProcessRatio||d,e,i,j,k,"#define NUM_SAMPLES "+h)||this;return o._screenCoordinates=g.d.Zero(),o.customMeshPosition=g.e.Zero(),o.useCustomMeshPosition=!1,o.invert=!0,o.excludedMeshes=new Array,o.exposure=.3,o.decay=.96815,o.weight=.58767,o.density=.926,j=(m=null!==(c=null==e?void 0:e.getScene())&&void 0!==c?c:m).getEngine(),o._viewPort=new Oc.a(0,0,1,1).toGlobal(j.getRenderWidth(),j.getRenderHeight()),o.mesh=null!=f?f:b.CreateDefaultMesh("VolumetricLightScatteringMesh",m),o._volumetricLightScatteringPass=new Ec.a(j),o._createPass(m,d.passRatio||d),o.onActivate=function(a){o.isSupported||o.dispose(a),o.onActivate=null},o.onApplyObservable.add(function(a){o._updateMeshScreenCoordinates(m),a.setTexture("lightScatteringSampler",o._volumetricLightScatteringRTT),a.setFloat("exposure",o.exposure),a.setFloat("decay",o.decay),a.setFloat("weight",o.weight),a.setFloat("density",o.density),a.setVector2("meshPositionOnScreen",o._screenCoordinates)}),o}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"useDiffuseColor",{get:function(){return q.a.Warn("VolumetricLightScatteringPostProcess.useDiffuseColor is no longer used, use the mesh material directly instead"),!1},set:function(a){q.a.Warn("VolumetricLightScatteringPostProcess.useDiffuseColor is no longer used, use the mesh material directly instead")},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"VolumetricLightScatteringPostProcess"},b.prototype._isReady=function(a,b){var c=a.getMesh();if(c===this.mesh&&c.material)return c.material.isReady(c);var d=[],e=[W.b.PositionKind],f=a.getMaterial();f&&(f.needAlphaTesting()&&d.push("#define ALPHATEST"),c.isVerticesDataPresent(W.b.UVKind)&&(e.push(W.b.UVKind),d.push("#define UV1")),c.isVerticesDataPresent(W.b.UV2Kind)&&(e.push(W.b.UV2Kind),d.push("#define UV2"))),c.useBones&&c.computeBonesUsingShaders?(e.push(W.b.MatricesIndicesKind),e.push(W.b.MatricesWeightsKind),d.push("#define NUM_BONE_INFLUENCERS "+c.numBoneInfluencers),d.push("#define BonesPerMesh "+(c.skeleton?c.skeleton.bones.length+1:0))):d.push("#define NUM_BONE_INFLUENCERS 0"),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(e),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES"));f=d.join(" ");return this._cachedDefines!==f&&(this._cachedDefines=f,this._volumetricLightScatteringPass.effect=c.getScene().getEngine().createEffect("volumetricLightScatteringPass",e,["world","mBones","viewProjection","diffuseMatrix"],["diffuseSampler"],f,void 0,void 0,void 0,{maxSimultaneousMorphTargets:c.numBoneInfluencers})),this._volumetricLightScatteringPass.effect.isReady()},b.prototype.setCustomMeshPosition=function(a){this.customMeshPosition=a},b.prototype.getCustomMeshPosition=function(){return this.customMeshPosition},b.prototype.dispose=function(b){var c=b.getScene().customRenderTargets.indexOf(this._volumetricLightScatteringRTT);-1!==c&&b.getScene().customRenderTargets.splice(c,1),this._volumetricLightScatteringRTT.dispose(),a.prototype.dispose.call(this,b)},b.prototype.getPass=function(){return this._volumetricLightScatteringRTT},b.prototype._meshExcluded=function(a){return this.excludedMeshes.length>0&&-1!==this.excludedMeshes.indexOf(a)},b.prototype._createPass=function(a,b){var c=this,d=a.getEngine();this._volumetricLightScatteringRTT=new cd("volumetricLightScatteringMap",{width:d.getRenderWidth()*b,height:d.getRenderHeight()*b},a,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT),this._volumetricLightScatteringRTT.wrapU=V.a.CLAMP_ADDRESSMODE,this._volumetricLightScatteringRTT.wrapV=V.a.CLAMP_ADDRESSMODE,this._volumetricLightScatteringRTT.renderList=null,this._volumetricLightScatteringRTT.renderParticles=!1,this._volumetricLightScatteringRTT.ignoreCameraViewport=!0;d=this.getCamera();d?d.customRenderTargets.push(this._volumetricLightScatteringRTT):a.customRenderTargets.push(this._volumetricLightScatteringRTT);var e,f=function(a){var b=a.getRenderingMesh(),d=a.getEffectiveMesh();if(!c._meshExcluded(b)){d._internalAbstractMeshDataInfo._isActiveIntermediate=!1;var e=a.getMaterial();if(e){var f=b.getScene(),g=f.getEngine();g.setState(e.backFaceCulling,void 0,void 0,void 0,e.cullBackFaces);var h=b._getInstancesRenderList(a._id,!!a.getReplacementMesh());if(!h.mustReturn){var i=g.getCaps().instancedArrays&&(null!==h.visibleInstances[a._id]||b.hasThinInstances);if(c._isReady(a,i)){var j=c._volumetricLightScatteringPass;b===c.mesh&&(j=a.effect?a._drawWrapper:e._getDrawWrapper());var k=j.effect;if(g.enableEffect(j),i||b._bind(a,k,e.fillMode),b===c.mesh)e.bind(d.getWorldMatrix(),b);else{if(k.setMatrix("viewProjection",f.getTransformMatrix()),e&&e.needAlphaTesting()){g=e.getAlphaTestTexture();k.setTexture("diffuseSampler",g),g&&k.setMatrix("diffuseMatrix",g.getTextureMatrix())}b.useBones&&b.computeBonesUsingShaders&&b.skeleton&&k.setMatrices("mBones",b.skeleton.getTransformMatrices(b))}b._processRendering(d,a,k,qi.a.TriangleFillMode,h,i,function(a,b){return k.setMatrix("world",b)})}}}}},g=new h.b(0,0,0,1);this._volumetricLightScatteringRTT.onBeforeRenderObservable.add(function(){e=a.clearColor,a.clearColor=g}),this._volumetricLightScatteringRTT.onAfterRenderObservable.add(function(){a.clearColor=e}),this._volumetricLightScatteringRTT.customRenderFunction=function(b,c,d,e){var g,h=a.getEngine();if(e.length){for(h.setColorWrite(!1),g=0;gb._alphaIndex?1:a._alphaIndexb._distanceToCamera?-1:0}),h.setAlphaMode(r.a.ALPHA_COMBINE),g=0;g uniform vec4 color; void main(void) { #include gl_FragColor=color; }";X.a.ShadersStore.linePixelShader=wi;rb="#include #include attribute vec3 position; attribute vec4 normal; uniform mat4 viewProjection; uniform float width; uniform float aspectRatio; void main(void) { #include mat4 worldViewProjection=viewProjection*finalWorld; vec4 viewPosition=worldViewProjection*vec4(position,1.0); vec4 viewPositionNext=worldViewProjection*vec4(normal.xyz,1.0); vec2 currentScreen=viewPosition.xy/viewPosition.w; vec2 nextScreen=viewPositionNext.xy/viewPositionNext.w; currentScreen.x*=aspectRatio; nextScreen.x*=aspectRatio; vec2 dir=normalize(nextScreen-currentScreen); vec2 normalDir=vec2(-dir.y,dir.x); normalDir*=width/2.0; normalDir.x/=aspectRatio; vec4 offset=vec4(normalDir*normal.w,0.0,0.0); gl_Position=viewPosition+offset; #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=finalWorld*vec4(position,1.0); #include #endif }";X.a.ShadersStore.lineVertexShader=rb;bb.a.prototype.disableEdgesRendering=function(){return this._edgesRenderer&&(this._edgesRenderer.dispose(),this._edgesRenderer=null),this},bb.a.prototype.enableEdgesRendering=function(a,b,c){return void 0===a&&(a=.95),void 0===b&&(b=!1),this.disableEdgesRendering(),this._edgesRenderer=new Up(this,a,b,!0,c),this},Object.defineProperty(bb.a.prototype,"edgesRenderer",{get:function(){return this._edgesRenderer},enumerable:!0,configurable:!0}),un.b.prototype.enableEdgesRendering=function(a,b){return void 0===a&&(a=.95),void 0===b&&(b=!1),this.disableEdgesRendering(),this._edgesRenderer=new Vp(this,a,b),this},un.a.prototype.enableEdgesRendering=function(a,b){return void 0===a&&.95,void 0===b&&!1,un.b.prototype.enableEdgesRendering.apply(this,arguments),this};var Tp=function(){this.edges=new Array,this.edgesConnectedCount=0},Up=function(){function a(a,b,c,d,e){var f=this;void 0===b&&(b=.95),void 0===c&&(c=!1),void 0===d&&(d=!0),this.edgesWidthScalerForOrthographic=1e3,this.edgesWidthScalerForPerspective=50,this._linesPositions=new Array,this._linesNormals=new Array,this._linesIndices=new Array,this._buffers={},this._buffersForInstances={},this._checkVerticesInsteadOfIndices=!1,this.isEnabled=!0,this.customInstances=new Ac.a(32),this._source=a,this._checkVerticesInsteadOfIndices=c,this._options=null!=e?e:null,this._epsilon=b,this._prepareRessources(),d&&(null===(a=null==e?void 0:e.useAlternateEdgeFinder)||void 0===a||a?this._generateEdgesLinesAlternate():this._generateEdgesLines()),this._meshRebuildObserver=this._source.onRebuildObservable.add(function(){f._rebuild()}),this._meshDisposeObserver=this._source.onDisposeObservable.add(function(){f.dispose()})}return Object.defineProperty(a.prototype,"linesPositions",{get:function(){return this._linesPositions},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"linesNormals",{get:function(){return this._linesNormals},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"linesIndices",{get:function(){return this._linesIndices},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"lineShader",{get:function(){return this._lineShader},set:function(a){this._lineShader=a},enumerable:!1,configurable:!0}),a.GetShader=function(a){if(!a._edgeRenderLineShader){var b=new zh.a("lineShader",a,"line",{attributes:["position","normal"],uniforms:["world","viewProjection","color","width","aspectRatio"]});b.disableDepthWrite=!0,b.backFaceCulling=!1,a._edgeRenderLineShader=b}return a._edgeRenderLineShader},a.prototype._prepareRessources=function(){this._lineShader||(this._lineShader=a.GetShader(this._source.getScene()))},a.prototype._rebuild=function(){var a=this._buffers[W.b.PositionKind];a&&a._rebuild(),(a=this._buffers[W.b.NormalKind])&&a._rebuild();a=this._source.getScene().getEngine();this._ib=a.createIndexBuffer(this._linesIndices)},a.prototype.dispose=function(){this._source.onRebuildObservable.remove(this._meshRebuildObserver),this._source.onDisposeObservable.remove(this._meshDisposeObserver);var a=this._buffers[W.b.PositionKind];a&&(a.dispose(),this._buffers[W.b.PositionKind]=null),(a=this._buffers[W.b.NormalKind])&&(a.dispose(),this._buffers[W.b.NormalKind]=null),this._ib&&this._source.getScene().getEngine()._releaseBuffer(this._ib),this._lineShader.dispose()},a.prototype._processEdgeForAdjacencies=function(a,b,c,d,e){return a===c&&b===d||a===d&&b===c?0:a===d&&b===e||a===e&&b===d?1:a===e&&b===c||a===c&&b===e?2:-1},a.prototype._processEdgeForAdjacenciesWithVertices=function(a,b,c,d,e){var f=1e-10;return a.equalsWithEpsilon(c,f)&&b.equalsWithEpsilon(d,f)||a.equalsWithEpsilon(d,f)&&b.equalsWithEpsilon(c,f)?0:a.equalsWithEpsilon(d,f)&&b.equalsWithEpsilon(e,f)||a.equalsWithEpsilon(e,f)&&b.equalsWithEpsilon(d,f)?1:a.equalsWithEpsilon(e,f)&&b.equalsWithEpsilon(c,f)||a.equalsWithEpsilon(c,f)&&b.equalsWithEpsilon(e,f)?2:-1},a.prototype._checkEdge=function(a,b,c,d,e){var f;void 0===b?f=!0:f=g.e.Dot(c[a],c[b])=0&&b.push(c);for(c=0;c=a[0].length&&a[1].length>=a[2].length?f=1:a[2].length>=a[0].length&&a[2].length>=a[1].length&&(f=2);for(var g=0;g<3;++g)g===f?a[g].sort(function(a,b){return a[1]b[1]?1:0}):a[g].sort(function(a,b){return a[1]>b[1]?-1:a[1]=f+1;--j)e(a[j%3],h,j!==f+2?d[c[b+(j+1)%3]]:-1);a=h.length;c.push(d[c[b+f]],g[0],h[0]),c.push(d[c[b+(f+1)%3]],h[a-1],g[i-1]);for(var j=i<=a,e=j?i:a,d=j?a:i,f=j?i-1:a-1,k=j?0:1,i=i+a-2,a=0,l=0,m=j?g:h,j=j?h:g,h=0;i-->0;){k?c.push(m[a],j[l]):c.push(j[l],m[a]);(h+=e)>=d&&aj){x=h;h=j,j=x}(E=I[P=h+"_"+j])?E.done||(g.e.Dot(H,E.normal)0||this._source.hasThinInstances)},a.prototype.render=function(){var a=this._source.getScene();if(this.isReady()&&a.activeCamera){var b=this._source.hasInstances&&this.customInstances.length>0,c=b||this._source.hasThinInstances,d=0;if(c)if(this._buffersForInstances.world0=this._source.getVertexBuffer("world0"),this._buffersForInstances.world1=this._source.getVertexBuffer("world1"),this._buffersForInstances.world2=this._source.getVertexBuffer("world2"),this._buffersForInstances.world3=this._source.getVertexBuffer("world3"),b){b=this._source._instanceDataStorage;if(d=this.customInstances.length,!b.instancesData)return void (this._source.getScene()._activeMeshesFrozen||this.customInstances.reset());if(!b.isFrozen){for(var e=0,f=0;f0&&(b.push(!0),c.push(!1));this._multiRenderAttachments=this._engine.buildTextureLayout(a),this._clearAttachments=this._engine.buildTextureLayout(b),this._defaultAttachments=this._engine.buildTextureLayout(c)},a.prototype._resetLayout=function(){for(var b=0;b=0;a--)this.renderTargets[a].dispose();for(a=0;a #include #include #include varying vec2 vUV; uniform vec2 texelSize; uniform sampler2D textureSampler; uniform sampler2D irradianceSampler; uniform sampler2D depthSampler; uniform sampler2D albedoSampler; uniform vec2 viewportSize; uniform float metersPerUnit; const float LOG2_E=1.4426950408889634; const float SSS_PIXELS_PER_SAMPLE=4.; const int _SssSampleBudget=40; #define rcp(x) 1./x #define Sq(x) x*x #define SSS_BILATERAL_FILTER true vec3 EvalBurleyDiffusionProfile(float r,vec3 S) { vec3 exp_13=exp2(((LOG2_E*(-1.0/3.0))*r)*S); vec3 expSum=exp_13*(1.+exp_13*exp_13); return (S*rcp(8.*PI))*expSum; } vec2 SampleBurleyDiffusionProfile(float u,float rcpS) { u=1.-u; float g=1.+(4.*u)*(2.*u+sqrt(1.+(4.*u)*u)); float n=exp2(log2(g)*(-1.0/3.0)); float p=(g*n)*n; float c=1.+p+n; float d=(3./LOG2_E*2.)+(3./LOG2_E)*log2(u); float x=(3./LOG2_E)*log2(c)-d; float rcpExp=((c*c)*c)*rcp((4.*u)*((c*c)+(4.*u)*(4.*u))); float r=x*rcpS; float rcpPdf=(8.*PI*rcpS)*rcpExp; return vec2(r,rcpPdf); } vec3 ComputeBilateralWeight(float xy2,float z,float mmPerUnit,vec3 S,float rcpPdf) { #ifndef SSS_BILATERAL_FILTER z=0.; #endif float r=sqrt(xy2+(z*mmPerUnit)*(z*mmPerUnit)); float area=rcpPdf; #if SSS_CLAMP_ARTIFACT return clamp(EvalBurleyDiffusionProfile(r,S)*area,0.0,1.0); #else return EvalBurleyDiffusionProfile(r,S)*area; #endif } void EvaluateSample(int i,int n,vec3 S,float d,vec3 centerPosVS,float mmPerUnit,float pixelsPerMm, float phase,inout vec3 totalIrradiance,inout vec3 totalWeight) { float scale=rcp(float(n)); float offset=rcp(float(n))*0.5; float sinPhase,cosPhase; sinPhase=sin(phase); cosPhase=cos(phase); vec2 bdp=SampleBurleyDiffusionProfile(float(i)*scale+offset,d); float r=bdp.x; float rcpPdf=bdp.y; float phi=SampleDiskGolden(i,n).y; float sinPhi,cosPhi; sinPhi=sin(phi); cosPhi=cos(phi); float sinPsi=cosPhase*sinPhi+sinPhase*cosPhi; float cosPsi=cosPhase*cosPhi-sinPhase*sinPhi; vec2 vec=r*vec2(cosPsi,sinPsi); vec2 position; float xy2; position=vUV+round((pixelsPerMm*r)*vec2(cosPsi,sinPsi))*texelSize; xy2=r*r; vec4 textureSample=texture2D(irradianceSampler,position); float viewZ=texture2D(depthSampler,position).r; vec3 irradiance=textureSample.rgb; if (testLightingForSSS(textureSample.a)) { float relZ=viewZ-centerPosVS.z; vec3 weight=ComputeBilateralWeight(xy2,relZ,mmPerUnit,S,rcpPdf); totalIrradiance+=weight*irradiance; totalWeight+=weight; } else { } } void main(void) { vec4 irradianceAndDiffusionProfile=texture2D(irradianceSampler,vUV); vec3 centerIrradiance=irradianceAndDiffusionProfile.rgb; int diffusionProfileIndex=int(round(irradianceAndDiffusionProfile.a*255.)); float centerDepth=0.; vec4 inputColor=texture2D(textureSampler,vUV); bool passedStencilTest=testLightingForSSS(irradianceAndDiffusionProfile.a); if (passedStencilTest) { centerDepth=texture2D(depthSampler,vUV).r; } if (!passedStencilTest) { gl_FragColor=inputColor; return; } float distScale=1.; vec3 S=diffusionS[diffusionProfileIndex]; float d=diffusionD[diffusionProfileIndex]; float filterRadius=filterRadii[diffusionProfileIndex]; vec2 centerPosNDC=vUV; vec2 cornerPosNDC=vUV+0.5*texelSize; vec3 centerPosVS=vec3(centerPosNDC*viewportSize,1.0)*centerDepth; vec3 cornerPosVS=vec3(cornerPosNDC*viewportSize,1.0)*centerDepth; float mmPerUnit=1000.*(metersPerUnit*rcp(distScale)); float unitsPerMm=rcp(mmPerUnit); float unitsPerPixel=2.*abs(cornerPosVS.x-centerPosVS.x); float pixelsPerMm=rcp(unitsPerPixel)*unitsPerMm; float filterArea=PI*Sq(filterRadius*pixelsPerMm); int sampleCount=int(filterArea*rcp(SSS_PIXELS_PER_SAMPLE)); int sampleBudget=_SssSampleBudget; int texturingMode=0; vec3 albedo=texture2D(albedoSampler,vUV).rgb; if (distScale == 0. || sampleCount<1) { #ifdef DEBUG_SSS_SAMPLES vec3 green=vec3(0.,1.,0.); gl_FragColor=vec4(green,1.0); return; #endif gl_FragColor=vec4(inputColor.rgb+albedo*centerIrradiance,1.0); return; } #ifdef DEBUG_SSS_SAMPLES vec3 red=vec3(1.,0.,0.); vec3 blue=vec3(0.,0.,1.); gl_FragColor=vec4(mix(blue,red,clamp(float(sampleCount)/float(sampleBudget),0.0,1.0)),1.0); return; #endif float phase=0.; int n=min(sampleCount,sampleBudget); vec3 centerWeight=vec3(0.); vec3 totalIrradiance=vec3(0.); vec3 totalWeight=vec3(0.); for (int i=0; i=5)return q.a.Error("You already reached the maximum number of diffusion profiles."),0;for(var b=0;b #include #include[0..maxSimultaneousMorphTargets] uniform float offset; #include uniform mat4 viewProjection; #ifdef ALPHATEST varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #include void main(void) { vec3 positionUpdated=position; vec3 normalUpdated=normal; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] vec3 offsetPosition=positionUpdated+(normalUpdated*offset); #include #include gl_Position=viewProjection*finalWorld*vec4(offsetPosition,1.0); #ifdef ALPHATEST #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #include } ";X.a.ShadersStore.outlineVertexShader=wi;P.a.prototype.getOutlineRenderer=function(){return this._outlineRenderer||(this._outlineRenderer=new bq(this)),this._outlineRenderer},Object.defineProperty(S.a.prototype,"renderOutline",{get:function(){return this._renderOutline},set:function(a){a&&this.getScene().getOutlineRenderer(),this._renderOutline=a},enumerable:!0,configurable:!0}),Object.defineProperty(S.a.prototype,"renderOverlay",{get:function(){return this._renderOverlay},set:function(a){a&&this.getScene().getOutlineRenderer(),this._renderOverlay=a},enumerable:!0,configurable:!0});var bq=function(){function a(b){this.name=Na.a.NAME_OUTLINERENDERER,this.zOffset=1,this.zOffsetUnits=4,this.scene=b,this._engine=b.getEngine(),this.scene._addComponent(this),this._nameForDrawWrapper=r.a.SUBMESH_DRAWWRAPPER_OUTLINERENDERER_PREFIX+a._Counter++}return a.prototype.register=function(){this.scene._beforeRenderingMeshStage.registerStep(Na.a.STEP_BEFORERENDERINGMESH_OUTLINE,this,this._beforeRenderingMesh),this.scene._afterRenderingMeshStage.registerStep(Na.a.STEP_AFTERRENDERINGMESH_OUTLINE,this,this._afterRenderingMesh)},a.prototype.rebuild=function(){},a.prototype.dispose=function(){},a.prototype.render=function(a,b,c){void 0===c&&(c=!1);var d=this.scene,e=d.getEngine(),f=e.getCaps().instancedArrays&&(null!==b.visibleInstances[a._id]&&void 0!==b.visibleInstances[a._id]||a.getRenderingMesh().hasThinInstances);if(this.isReady(a,f)){var g=a.getMesh();g=g._internalAbstractMeshDataInfo._actAsRegularMesh?g:null;var h=a.getRenderingMesh();g=g||h;var i=a.getMaterial();if(i&&d.activeCamera){var j=a._getDrawWrapper(this._nameForDrawWrapper),k=Ec.a.GetEffect(j);if(e.enableEffect(j),i.useLogarithmicDepth&&k.setFloat("logarithmicDepthConstant",2/(Math.log(d.activeCamera.maxZ+1)/Math.LN2)),k.setFloat("offset",c?0:h.outlineWidth),k.setColor4("color",c?h.overlayColor:h.outlineColor,c?h.overlayAlpha:i.alpha),k.setMatrix("viewProjection",d.getTransformMatrix()),k.setMatrix("world",g.getWorldMatrix()),h.useBones&&h.computeBonesUsingShaders&&h.skeleton&&k.setMatrices("mBones",h.skeleton.getTransformMatrices(h)),h.morphTargetManager&&h.morphTargetManager.isUsingTextureForTargets&&h.morphTargetManager._bind(k),Yh.a.BindMorphTargetParameters(h,k),f||h._bind(a,k,i.fillMode),i&&i.needAlphaTesting()){j=i.getAlphaTestTexture();j&&(k.setTexture("diffuseSampler",j),k.setMatrix("diffuseMatrix",j.getTextureMatrix()))}e.setZOffset(-this.zOffset),e.setZOffsetUnits(-this.zOffsetUnits),h._processRendering(g,a,k,i.fillMode,b,f,function(a,b){k.setMatrix("world",b)}),e.setZOffset(0),e.setZOffsetUnits(0)}}},a.prototype.isReady=function(a,b){var c=[],d=[W.b.PositionKind,W.b.NormalKind],e=a._getDrawWrapper(this._nameForDrawWrapper,!0),f=e.effect,g=e.defines,h=a.getMesh(),i=a.getMaterial();i&&(i.needAlphaTesting()&&(c.push("#define ALPHATEST"),h.isVerticesDataPresent(W.b.UVKind)&&(d.push(W.b.UVKind),c.push("#define UV1")),h.isVerticesDataPresent(W.b.UV2Kind)&&(d.push(W.b.UV2Kind),c.push("#define UV2"))),i.useLogarithmicDepth&&c.push("#define LOGARITHMICDEPTH")),h.useBones&&h.computeBonesUsingShaders?(d.push(W.b.MatricesIndicesKind),d.push(W.b.MatricesWeightsKind),h.numBoneInfluencers>4&&(d.push(W.b.MatricesIndicesExtraKind),d.push(W.b.MatricesWeightsExtraKind)),c.push("#define NUM_BONE_INFLUENCERS "+h.numBoneInfluencers),c.push("#define BonesPerMesh "+(h.skeleton?h.skeleton.bones.length+1:0))):c.push("#define NUM_BONE_INFLUENCERS 0");i=h.morphTargetManager;var j=0;i&&i.numInfluencers>0&&(j=i.numInfluencers,c.push("#define MORPHTARGETS"),c.push("#define NUM_MORPH_INFLUENCERS "+j),i.isUsingTextureForTargets&&c.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(d,h,j)),b&&(c.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(d),a.getRenderingMesh().hasThinInstances&&c.push("#define THIN_INSTANCES"));i=c.join(" ");return g!==i&&(g=i,f=this.scene.getEngine().createEffect("outline",d,["world","mBones","viewProjection","diffuseMatrix","offset","color","logarithmicDepthConstant","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","morphTargets"],i,void 0,void 0,void 0,{maxSimultaneousMorphTargets:j})),e.setEffect(f,g),f.isReady()},a.prototype._beforeRenderingMesh=function(b,c,d){if(this._savedDepthWrite=this._engine.getDepthWrite(),b.renderOutline){var e=c.getMaterial();e&&e.needAlphaBlendingForMesh(b)&&(this._engine.cacheStencilState(),this._engine.setDepthWrite(!1),this._engine.setColorWrite(!1),this._engine.setStencilBuffer(!0),this._engine.setStencilOperationPass(r.a.REPLACE),this._engine.setStencilFunction(r.a.ALWAYS),this._engine.setStencilMask(a._StencilReference),this._engine.setStencilFunctionReference(a._StencilReference),this._engine.stencilStateComposer.useStencilGlobalOnly=!0,this.render(c,d,!0),this._engine.setColorWrite(!0),this._engine.setStencilFunction(r.a.NOTEQUAL)),this._engine.setDepthWrite(!1),this.render(c,d),this._engine.setDepthWrite(this._savedDepthWrite),e&&e.needAlphaBlendingForMesh(b)&&(this._engine.stencilStateComposer.useStencilGlobalOnly=!1,this._engine.restoreStencilState())}},a.prototype._afterRenderingMesh=function(a,b,c){if(a.renderOverlay){var d=this._engine.getAlphaMode(),e=this._engine.alphaState.alphaBlend;this._engine.setAlphaMode(r.a.ALPHA_COMBINE),this.render(b,c,!0),this._engine.setAlphaMode(d),this._engine.setDepthWrite(this._savedDepthWrite),this._engine.alphaState.alphaBlend=e}a.renderOutline&&this._savedDepthWrite&&(this._engine.setDepthWrite(!0),this._engine.setColorWrite(!1),this.render(b,c),this._engine.setColorWrite(!0))},a._Counter=0,a._StencilReference=4,a}(),cq=c(169),dq=c(172),eq=function(a){function b(b,c){var d=a.call(this)||this;return d.name=b,d.animations=new Array,d.isPickable=!1,d.useAlphaForPicking=!1,d.onDisposeObservable=new f.c,d._onAnimationEnd=null,d._endAnimation=function(){d._onAnimationEnd&&d._onAnimationEnd(),d.disposeWhenFinishedAnimating&&d.dispose()},d.color=new h.b(1,1,1,1),d.position=g.e.Zero(),d._manager=c,d._manager.sprites.push(d),d.uniqueId=d._manager.scene.getUniqueId(),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"size",{get:function(){return this.width},set:function(a){this.width=a,this.height=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manager",{get:function(){return this._manager},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"Sprite"},Object.defineProperty(b.prototype,"fromIndex",{get:function(){return this._fromIndex},set:function(a){this.playAnimation(a,this._toIndex,this._loopAnimation,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toIndex",{get:function(){return this._toIndex},set:function(a){this.playAnimation(this._fromIndex,a,this._loopAnimation,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loopAnimation",{get:function(){return this._loopAnimation},set:function(a){this.playAnimation(this._fromIndex,this._toIndex,a,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"delay",{get:function(){return Math.max(this._delay,1)},set:function(a){this.playAnimation(this._fromIndex,this._toIndex,this._loopAnimation,a,this._onAnimationEnd)},enumerable:!1,configurable:!0}),b.prototype.playAnimation=function(b,c,d,e,f){void 0===f&&(f=null),this._onAnimationEnd=f,a.prototype.playAnimation.call(this,b,c,d,e,this._endAnimation)},b.prototype.dispose=function(){for(var a=0;athis._delay&&(this._time=this._time%this._delay,this.cellIndex+=this._direction,(this._direction>0&&this.cellIndex>this._toIndex||this._direction<0&&this.cellIndex0?this._fromIndex:this._toIndex:(this.cellIndex=this._toIndex,this._animationStarted=!1,this._onBaseAnimationEnd&&this._onBaseAnimationEnd()))))},a}());P.a.prototype._internalPickSprites=function(a,b,c,d){if(!Td.a)return null;var e=null;if(!d){if(!this.activeCamera)return null;d=this.activeCamera}if(this.spriteManagers.length>0)for(var f=0;f=e.distance))&&(e=g,c))break}}return e||new Td.a},P.a.prototype._internalMultiPickSprites=function(a,b,c){if(!Td.a)return null;var d=new Array;if(!c){if(!this.activeCamera)return null;c=this.activeCamera}if(this.spriteManagers.length>0)for(var e=0;e0&&(c=e.pickSprite(a,b,this._spritePredicate,!1,e.cameraToUseForPointers||void 0))&&c.hit&&c.pickedSprite&&c.pickedSprite.actionManager){switch(e._pickedDownSprite=c.pickedSprite,d.button){case 0:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnLeftPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d));break;case 1:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnCenterPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d));break;case 2:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnRightPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d))}c.pickedSprite.actionManager&&c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickDownTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d))}return c},a.prototype._pointerUp=function(a,b,c,d){var e=this.scene;if(e.spriteManagers.length>0){a=e.pickSprite(a,b,this._spritePredicate,!1,e.cameraToUseForPointers||void 0);a&&(a.hit&&a.pickedSprite&&a.pickedSprite.actionManager&&(a.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickUpTrigger,k.a.CreateNewFromSprite(a.pickedSprite,e,d)),a.pickedSprite.actionManager&&(this.scene._inputManager._isPointerSwiping()||a.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickTrigger,k.a.CreateNewFromSprite(a.pickedSprite,e,d)))),e._pickedDownSprite&&e._pickedDownSprite.actionManager&&e._pickedDownSprite!==a.pickedSprite&&e._pickedDownSprite.actionManager.processTrigger(r.a.ACTION_OnPickOutTrigger,k.a.CreateNewFromSprite(e._pickedDownSprite,e,d)))}return c},a}();X.a.IncludesShadersStore.imageProcessingCompatibility="#ifdef IMAGEPROCESSINGPOSTPROCESS gl_FragColor.rgb=pow(gl_FragColor.rgb,vec3(2.2)); #endif";rb="uniform bool alphaTest; varying vec4 vColor; varying vec2 vUV; uniform sampler2D diffuseSampler; #include void main(void) { vec4 color=texture2D(diffuseSampler,vUV); if (alphaTest) { if (color.a<0.95) discard; } color*=vColor; #include gl_FragColor=color; #include }";X.a.ShadersStore.spritesPixelShader=rb;a=" attribute vec4 position; attribute vec2 options; attribute vec2 offsets; attribute vec2 inverts; attribute vec4 cellInfo; attribute vec4 color; uniform mat4 view; uniform mat4 projection; varying vec2 vUV; varying vec4 vColor; #include void main(void) { vec3 viewPos=(view*vec4(position.xyz,1.0)).xyz; vec2 cornerPos; float angle=position.w; vec2 size=vec2(options.x,options.y); vec2 offset=offsets.xy; cornerPos=vec2(offset.x-0.5,offset.y-0.5)*size; vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; viewPos+=rotatedCorner; gl_Position=projection*vec4(viewPos,1.0); vColor=color; vec2 uvOffset=vec2(abs(offset.x-inverts.x),abs(1.0-offset.y-inverts.y)); vec2 uvPlace=cellInfo.xy; vec2 uvSize=cellInfo.zw; vUV.x=uvPlace.x+uvSize.x*uvOffset.x; vUV.y=uvPlace.y+uvSize.y*uvOffset.y; #ifdef FOG vFogDistance=viewPos; #endif }";X.a.ShadersStore.spritesVertexShader=a;var gq=function(){function a(a,b,c,d){void 0===c&&(c=.01),void 0===d&&(d=null),this.blendMode=r.a.ALPHA_COMBINE,this.autoResetAlpha=!0,this.disableDepthWrite=!1,this.fogEnabled=!0,this._useVAO=!1,this._useInstancing=!1,this._vertexBuffers={},this._capacity=b,this._epsilon=c,this._engine=a,this._useInstancing=a.getCaps().instancedArrays,this._useVAO=a.getCaps().vertexArrayObject&&!a.disableVertexArrayObjects,this._scene=d,this._drawWrapperBase=new Ec.a(a),this._drawWrapperFog=new Ec.a(a),this._useInstancing||this._buildIndexBuffer(),this._vertexBufferSize=this._useInstancing?16:18,this._vertexData=new Float32Array(b*this._vertexBufferSize*(this._useInstancing?1:4)),this._buffer=new W.a(a,this._vertexData,!0,this._vertexBufferSize);c=this._buffer.createVertexBuffer(W.b.PositionKind,0,4,this._vertexBufferSize,this._useInstancing);d=this._buffer.createVertexBuffer("options",4,2,this._vertexBufferSize,this._useInstancing);b=6;if(this._useInstancing){var e=new Float32Array([0,0,1,0,0,1,1,1]);this._spriteBuffer=new W.a(a,e,!1,2),a=this._spriteBuffer.createVertexBuffer("offsets",0,2)}else a=this._buffer.createVertexBuffer("offsets",b,2,this._vertexBufferSize,this._useInstancing),b+=2;e=this._buffer.createVertexBuffer("inverts",b,2,this._vertexBufferSize,this._useInstancing);var f=this._buffer.createVertexBuffer("cellInfo",b+2,4,this._vertexBufferSize,this._useInstancing);b=this._buffer.createVertexBuffer(W.b.ColorKind,b+6,4,this._vertexBufferSize,this._useInstancing);this._vertexBuffers[W.b.PositionKind]=c,this._vertexBuffers.options=d,this._vertexBuffers.offsets=a,this._vertexBuffers.inverts=e,this._vertexBuffers.cellInfo=f,this._vertexBuffers[W.b.ColorKind]=b,this._drawWrapperBase.effect=this._engine.createEffect("sprites",[W.b.PositionKind,"options","offsets","inverts","cellInfo",W.b.ColorKind],["view","projection","textureInfos","alphaTest"],["diffuseSampler"],""),this._scene&&(this._drawWrapperFog.effect=this._scene.getEngine().createEffect("sprites",[W.b.PositionKind,"options","offsets","inverts","cellInfo",W.b.ColorKind],["view","projection","textureInfos","alphaTest","vFogInfos","vFogColor"],["diffuseSampler"],"#define FOG"))}return Object.defineProperty(a.prototype,"capacity",{get:function(){return this._capacity},enumerable:!1,configurable:!0}),a.prototype.render=function(a,b,c,d,e){if(void 0===e&&(e=null),this.texture&&this.texture.isReady()&&a.length){var f=this._drawWrapperBase,g=!1;this.fogEnabled&&this._scene&&this._scene.fogEnabled&&0!==this._scene.fogMode&&(f=this._drawWrapperFog,g=!0);var h=f.effect;if(h.isReady()){for(var i=this._engine,j=!(!this._scene||!this._scene.useRightHandedSystem),k=this.texture.getBaseSize(),q=Math.min(this._capacity,a.length),s=0,t=!0,u=0;u>0;b._xOffset=(b.cellIndex-h*g)*this.cellWidth/e.width,b._yOffset=h*this.cellHeight/e.height,b._xSize=this.cellWidth,b._ySize=this.cellHeight}this._vertexData[a]=b.position.x,this._vertexData[a+1]=b.position.y,this._vertexData[a+2]=b.position.z,this._vertexData[a+3]=b.angle,this._vertexData[a+4]=b.width,this._vertexData[a+5]=b.height,this._useInstancing?a-=2:(this._vertexData[a+6]=c,this._vertexData[a+7]=d),this._vertexData[a+8]=f?b.invertU?0:1:b.invertU?1:0,this._vertexData[a+9]=b.invertV?1:0,this._vertexData[a+10]=b._xOffset,this._vertexData[a+11]=b._yOffset,this._vertexData[a+12]=b._xSize/e.width,this._vertexData[a+13]=b._ySize/e.height,this._vertexData[a+14]=b.color.r,this._vertexData[a+15]=b.color.g,this._vertexData[a+16]=b.color.b,this._vertexData[a+17]=b.color.a},a.prototype._buildIndexBuffer=function(){for(var a=[],b=0,c=0;c0);f=a.substring(0,b-1)+".json";var g=new XMLHttpRequest;g.open("GET",f,!0),g.onerror=function(){q.a.Error("JSON ERROR: Unable to load JSON file."),c._fromPacked=!1,c._packedAndReady=!1},g.onload=function(){try{var a=JSON.parse(g.response),b=Reflect.ownKeys(a.frames);c._spriteMap=b,c._packedAndReady=!0,c._cellData=a.frames}catch(a){throw c._fromPacked=!1,c._packedAndReady=!1,new Error("Invalid JSON format. Please check documentation for format specifications.")}},g.send()}},a.prototype._checkTextureAlpha=function(a,b,c,d,e){if(!a.useAlphaForPicking||!this.texture)return!0;var f=this.texture.getSize();this._textureContent||(this._textureContent=new Uint8Array(f.width*f.height*4),this.texture.readPixels(0,0,this._textureContent));var h=g.c.Vector3[0];h.copyFrom(b.direction),h.normalize(),h.scaleInPlace(c),h.addInPlace(b.origin);c=(h.x-d.x)/(e.x-d.x)-.5;b=1-(h.y-d.y)/(e.y-d.y)-.5;h=a.angle;e=c*Math.cos(h)-b*Math.sin(h)+.5;d=c*Math.sin(h)+b*Math.cos(h)+.5;c=a._xOffset*f.width+e*a._xSize|0;b=a._yOffset*f.height+d*a._ySize|0;return this._textureContent[4*(c+b*f.width)+3]>.5},a.prototype.intersects=function(a,b,c,d){for(var e=Math.min(this.capacity,this.sprites.length),f=g.e.Zero(),h=g.e.Zero(),i=Number.MAX_VALUE,j=null,k=g.c.Vector3[0],m=g.c.Vector3[1],b=b.getViewMatrix(),s=a,t=a,u=0;uw){if(!this._checkTextureAlpha(v,s,w,f,h))continue;if(t=s,i=w,j=v,d)break}}}}if(j){w=new Td.a;b.invertToRef(g.c.Matrix[0]),w.hit=!0,w.pickedSprite=j,w.distance=i;v=g.c.Vector3[2];return v.copyFrom(t.direction),v.normalize(),v.scaleInPlace(i),t.origin.addToRef(v,k),w.pickedPoint=g.e.TransformCoordinates(k,g.c.Matrix[0]),w}return null},a.prototype.multiIntersects=function(a,b,c){for(var d,e=Math.min(this.capacity,this.sprites.length),f=g.e.Zero(),h=g.e.Zero(),i=[],j=g.c.Vector3[0].copyFromFloats(0,0,0),k=g.c.Vector3[1].copyFromFloats(0,0,0),b=b.getViewMatrix(),l=0;l0&&(a+=" "),a+=this._tileMaps[b]._texture._bufferView.toString();b=document.createElement("a");b.href="data:octet/stream;charset=utf-8,"+encodeURI(a),b.target="_blank",b.download=this.name+".tilemaps",b.click(),b.remove()},a.prototype.loadTileMaps=function(a){var b=this,c=new XMLHttpRequest;c.open("GET",a);var d=this.options.layerCount||0;c.onload=function(){for(var a=c.response.split(" "),e=0;e-1&&this._tasks.splice(a,1)},a.prototype._decreaseWaitingTasksCount=function(a){this._waitingTasksCount--;try{this.onProgress&&this.onProgress(this._waitingTasksCount,this._totalTasksCount,a),this.onProgressObservable.notifyObservers(new pq(this._waitingTasksCount,this._totalTasksCount,a))}catch(a){q.a.Error("Error running progress callbacks."),!1}if(0===this._waitingTasksCount){try{var b=this._tasks.slice();this.onFinish&&this.onFinish(b);for(var c=0,b=b;c-1&&this._tasks.splice(a,1)}this.onTasksDoneObservable.notifyObservers(this._tasks)}catch(a){q.a.Error("Error running tasks-done callbacks."),!1}this._isLoading=!1,this.autoHideLoadingUI&&this._scene.getEngine().hideLoadingUI()}},a.prototype._runTask=function(a){var b=this,c=function(c,d){a._setErrorObject(c,d),b.onTaskError&&b.onTaskError(a),b.onTaskErrorObservable.notifyObservers(a),b._decreaseWaitingTasksCount(a)};a.run(this._scene,function(){try{b.onTaskSuccess&&b.onTaskSuccess(a),b.onTaskSuccessObservable.notifyObservers(a),b._decreaseWaitingTasksCount(a)}catch(a){c("Error executing task success callbacks",a)}},c)},a.prototype.reset=function(){return this._isLoading=!1,this._tasks=new Array,this},a.prototype.load=function(){if(this._isLoading)return this;if(this._isLoading=!0,this._waitingTasksCount=this._tasks.length,this._totalTasksCount=this._tasks.length,0===this._waitingTasksCount)return this._isLoading=!1,this.onFinish&&this.onFinish(this._tasks),this.onTasksDoneObservable.notifyObservers(this._tasks),this;this.useDefaultLoadingScreen&&this._scene.getEngine().displayLoadingUI();for(var a=0;a=0&&this._meshes.splice(a,1),this._centerPosition=this._centerMesh.getAbsolutePosition().clone();for(b=0;b0&&this._textureLoadingCallback(a)}this._currentScene.render()}},a.prototype.drag=function(a){a.stopPropagation(),a.preventDefault()},a.prototype.drop=function(a){a.stopPropagation(),a.preventDefault(),this.loadFiles(a)},a.prototype._traverseFolder=function(a,b,c,d){var e=this,f=a.createReader(),g=a.fullPath.replace(/^//,"").replace(/(.+?)/?$/,"$1/");f.readEntries(function(a){c.count+=a.length;for(var f=0,a=a;f0)){for(var c=new Array,d=[],a=a.dataTransfer?a.dataTransfer.items:null,e=0;e0&&q.a.ClearLogCache(),this._engine.stopRenderLoop()),hh.ShowLoadingScreen=!1,this._engine.displayLoadingUI(),this.loadAsync(this._sceneFileToLoad,this._progressCallback).then(function(b){a._currentScene&&a._currentScene.dispose(),a._currentScene=b,a._sceneLoadedCallback&&a._sceneLoadedCallback(a._sceneFileToLoad,a._currentScene),a._currentScene.executeWhenReady(function(){a._engine.hideLoadingUI(),a._engine.runRenderLoop(function(){a.renderFunction()})})})["catch"](function(b){a._engine.hideLoadingUI(),a._errorCallback&&a._errorCallback(a._sceneFileToLoad,a._currentScene,b.message)})):q.a.Error("Please provide a valid .babylon file.")},a}();f.c.prototype.runCoroutineAsync=function(a){var b=this;return this.coroutineIterators||(this.coroutineIterators=[],this.add(function(){for(var a=function(a){if(b.coroutineIterators[a].paused)return"continue";var c=b.coroutineIterators[a].iterator.next();if(c.value){var d=b.coroutineIterators[a];d.paused=!0,c.value.then(function(){d.paused=!1})}c.done&&(b.coroutineIterators[a].resolver(),b.coroutineIterators.splice(a,1))},c=b.coroutineIterators.length-1;c>=0;--c)a(c)})),new Promise(function(c,d){var e;null===(e=b.coroutineIterators)||void 0===e||e.push({iterator:a,resolver:c,rejecter:d,paused:!1})})},f.c.prototype.cancelAllCoroutines=function(){this.coroutineIterators.forEach(function(a){a.rejecter()}),this.coroutineIterators=[]};var Eq=c(173),Fq=c(168),Gq=function(){function a(a){void 0===a&&(a=0),this.priority=a}return a.prototype.getDescription=function(){return""},a.prototype.apply=function(a,b){return!0},a}(),Hq=function(a){function b(b,c,d){void 0===b&&(b=0),void 0===c&&(c=1024),void 0===d&&(d=.5);var e=a.call(this,b)||this;return e.priority=b,e.maximumSize=c,e.step=d,e}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Reducing render target texture size to "+this.maximumSize},b.prototype.apply=function(a,b){for(var b=!0,c=0;cthis.maximumSize&&(d.scale(this.step),b=!1)}}return b},b}(Gq),Iq=function(a){function b(b,c,d){void 0===b&&(b=0),void 0===c&&(c=2),void 0===d&&(d=.25);var e=a.call(this,b)||this;return e.priority=b,e.maximumScale=c,e.step=d,e._currentScale=-1,e._directionOffset=1,e}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Setting hardware scaling level to "+this._currentScale},b.prototype.apply=function(a,b){return-1===this._currentScale&&(this._currentScale=a.getEngine().getHardwareScalingLevel(),this._currentScale>this.maximumScale&&(this._directionOffset=-1)),this._currentScale+=this._directionOffset*this.step,a.getEngine().setHardwareScalingLevel(this._currentScale),1===this._directionOffset?this._currentScale>=this.maximumScale:this._currentScale<=this.maximumScale},b}(Gq),Jq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning shadows on/off"},b.prototype.apply=function(a,b){return a.shadowsEnabled=b.isInImprovementMode,!0},b}(Gq),Kq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning post-processes on/off"},b.prototype.apply=function(a,b){return a.postProcessesEnabled=b.isInImprovementMode,!0},b}(Gq),Lq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning lens flares on/off"},b.prototype.apply=function(a,b){return a.lensFlaresEnabled=b.isInImprovementMode,!0},b}(Gq),Mq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return this.onGetDescription?this.onGetDescription():"Running user defined callback"},b.prototype.apply=function(a,b){return!this.onApply||this.onApply(a,b)},b}(Gq),Nq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning particles on/off"},b.prototype.apply=function(a,b){return a.particlesEnabled=b.isInImprovementMode,!0},b}(Gq),Oq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning render targets off"},b.prototype.apply=function(a,b){return a.renderTargetsEnabled=b.isInImprovementMode,!0},b}(Gq),Pq=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._canBeMerged=function(a){if(!(a instanceof S.a))return!1;a=a;return!a.isDisposed()&&!(!a.isVisible||!a.isEnabled())&&!(a.instances.length>0)&&!a.skeleton&&!a.hasLODLevels},b}return Object(l.d)(b,a),Object.defineProperty(b,"UpdateSelectionTree",{get:function(){return b._UpdateSelectionTree},set:function(a){b._UpdateSelectionTree=a},enumerable:!1,configurable:!0}),b.prototype.getDescription=function(){return"Merging similar meshes together"},b.prototype.apply=function(a,c,d){for(var c=a.meshes.slice(0),e=c.length,f=0;f=this._targetFrameRate)return this._isRunning=!1,void this.onSuccessObservable.notifyObservers(this);for(var d=!0,e=!0,f=0;f0){d.animationGroups=[];for(c=0;c0)for(d.reflectionProbes=[],f=0;f0&&setTimeout(function(){c.stopRecording()},1e3*b),this._fileName=a,this._recordedChunks=[],this._resolve=null,this._reject=null,this._canvas.isRecording=!0,this._mediaRecorder.start(this._options.recordChunckSize),new Promise(function(a,b){c._resolve=a,c._reject=b})},a.prototype.dispose=function(){this._canvas=null,this._mediaRecorder=null,this._recordedChunks=[],this._fileName=null,this._resolve=null,this._reject=null},a.prototype._handleDataAvailable=function(a){a.data.size>0&&this._recordedChunks.push(a.data)},a.prototype._handleError=function(a){if(this.stopRecording(),!this._reject)throw new a.error;this._reject(a.error)},a.prototype._handleStop=function(){this.stopRecording();var a=new Blob(this._recordedChunks);this._resolve&&this._resolve(a),window.URL.createObjectURL(a),this._fileName&&U.b.Download(a,this._fileName)},a._defaultOptions={mimeType:"video/webm",fps:25,recordChunckSize:3e3},a}();function Yq(a,b,c,d,e,f){void 0===e&&(e="image/png"),void 0===f&&(f=!1);b=cr(a,b,c);c=b.height;b=b.width;if(c&&b){U.b._ScreenshotCanvas||(U.b._ScreenshotCanvas=document.createElement("canvas")),U.b._ScreenshotCanvas.width=b,U.b._ScreenshotCanvas.height=c;var g=U.b._ScreenshotCanvas.getContext("2d"),h=a.getRenderWidth()/a.getRenderHeight(),i=b,j=i/h;j>c&&(i=(j=c)*h);var k=Math.max(0,b-i)/2,p=Math.max(0,c-j)/2;a.onEndFrameObservable.addOnce(function(){var b=a.getRenderingCanvas();g&&b&&g.drawImage(b,k,p,i,j),f?(U.b.EncodeScreenshotCanvasData(void 0,e),d&&d("")):U.b.EncodeScreenshotCanvasData(d,e)})}else q.a.Error("Invalid "size" parameter !")}function Zq(a,b,c,d){return void 0===d&&(d="image/png"),new Promise(function(e,f){Yq(a,b,c,function(a){void 0!==a?e(a):f(new Error("Data is undefined"))},d)})}function $q(a,b,c,d,e){return void 0===e&&(e="image/png"),new Promise(function(f,g){Yq(a,b,{width:c,height:d},function(){f()},e,!0)})}function ar(a,b,c,d,e,f,g,h,i,j){void 0===e&&(e="image/png"),void 0===f&&(f=1),void 0===g&&(g=!1),void 0===i&&(i=!1),void 0===j&&(j=!1);c=cr(a,b,c);var k=c.height,n=c.width;c={width:n,height:k};if(k&&n){var v=b.getScene(),w=null,x=v.activeCameras;v.activeCameras=null,v.activeCamera!==b&&(w=v.activeCamera,v.activeCamera=b),v.render();var y=new cd("screenShot",c,v,!1,!1,r.a.TEXTURETYPE_UNSIGNED_INT,!1,V.a.NEAREST_SAMPLINGMODE,void 0,j,void 0,void 0,void 0,f);y.renderList=null,y.samples=f,y.renderSprites=i,a.onEndFrameObservable.addOnce(function(){y.readPixels(void 0,void 0,void 0,!1).then(function(a){U.b.DumpData(n,k,a,d,e,h,!0),y.dispose()})});var z=function(){v.incrementRenderId(),v.resetCachedMaterial(),y.render(!0),v.incrementRenderId(),v.resetCachedMaterial(),w&&(v.activeCamera=w),v.activeCameras=x,b.getProjectionMatrix(!0),v.render()};if(g){c=new qp("antialiasing",1,v.activeCamera);y.addPostProcess(c),c.getEffect().isReady()?z():c.getEffect().onCompiled=function(){z()}}else z()}else q.a.Error("Invalid "size" parameter !")}function br(a,b,c,d,e,f,g,h){return void 0===d&&(d="image/png"),void 0===e&&(e=1),void 0===f&&(f=!1),void 0===h&&(h=!1),new Promise(function(i,j){ar(a,b,c,function(a){void 0!==a?i(a):j(new Error("Data is undefined"))},d,e,f,g,h)})}function cr(a,b,c){var d=0,e=0;if("object"==typeof c){var f=c.precision?Math.abs(c.precision):1;c.width&&c.height?(d=c.height*f,e=c.width*f):c.width&&!c.height?(e=c.width*f,d=Math.round(e/a.getAspectRatio(b))):c.height&&!c.width?(d=c.height*f,e=Math.round(d*a.getAspectRatio(b))):(e=Math.round(a.getRenderWidth()*f),d=Math.round(e/a.getAspectRatio(b)))}else isNaN(c)||(d=c,e=c);return e&&(e=Math.floor(e)),d&&(d=Math.floor(d)),{height:0|d,width:0|e}}var dr,er={CreateScreenshot:Yq,CreateScreenshotAsync:Zq,CreateScreenshotWithResizeAsync:$q,CreateScreenshotUsingRenderTarget:ar,CreateScreenshotUsingRenderTargetAsync:br};U.b.CreateScreenshot=Yq,U.b.CreateScreenshotAsync=Zq,U.b.CreateScreenshotUsingRenderTarget=ar,U.b.CreateScreenshotUsingRenderTargetAsync=br,(function(a){a[a.Checkbox=0]="Checkbox",a[a.Slider=1]="Slider",a[a.Vector3=2]="Vector3",a[a.Quaternion=3]="Quaternion",a[a.Color3=4]="Color3",a[a.String=5]="String",a[a.Button=6]="Button",a[a.Options=7]="Options",a[a.Tab=8]="Tab"})(dr||(dr={}));var fr,gr=c(164),hr=c(145),ir=function(){function a(a){this.byteOffset=0,this.buffer=a}return a.prototype.loadAsync=function(a){var b=this;return this.buffer.readAsync(this.byteOffset,a).then(function(a){b._dataView=new DataView(a.buffer,a.byteOffset,a.byteLength),b._dataByteOffset=0})},a.prototype.readUint32=function(){var a=this._dataView.getUint32(this._dataByteOffset,!0);return this._dataByteOffset+=4,this.byteOffset+=4,a},a.prototype.readUint8Array=function(a){var b=new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+this._dataByteOffset,a);return this._dataByteOffset+=a,this.byteOffset+=a,b},a.prototype.readString=function(a){return Object(gh.a)(this.readUint8Array(a))},a.prototype.skipBytes=function(a){this._dataByteOffset+=a,this.byteOffset+=a},a}(),jr=function(){function a(){}return a._GetStorage=function(){try{return localStorage.setItem("test",""),localStorage.removeItem("test"),localStorage}catch(b){var a={};return{getItem:function(b){b=a[b];return void 0===b?null:b},setItem:function(b,c){a[b]=c}}}},a.ReadString=function(a,b){a=this._Storage.getItem(a);return null!==a?a:b},a.WriteString=function(a,b){this._Storage.setItem(a,b)},a.ReadBoolean=function(a,b){a=this._Storage.getItem(a);return null!==a?"true"===a:b},a.WriteBoolean=function(a,b){this._Storage.setItem(a,b?"true":"false")},a.ReadNumber=function(a,b){a=this._Storage.getItem(a);return null!==a?parseFloat(a):b},a.WriteNumber=function(a,b){this._Storage.setItem(a,b.toString())},a._Storage=a._GetStorage(),a}(),kr=function(){function a(){this._trackedScene=null}return a.prototype.track=function(a){this._trackedScene=a,J.a.AllowLoadingUniqueId=!0,this._savedJSON=Vq.Serialize(a),J.a.AllowLoadingUniqueId=!1},a.prototype.getDelta=function(){if(!this._trackedScene)return null;var a=V.a.ForceSerializeBuffers;V.a.ForceSerializeBuffers=!1,J.a.AllowLoadingUniqueId=!0;var b=Vq.Serialize(this._trackedScene);J.a.AllowLoadingUniqueId=!1;var c={};for(var d in b)this._compareCollections(d,this._savedJSON[d],b[d],c);return V.a.ForceSerializeBuffers=a,c},a.prototype._compareArray=function(a,b,c,d){if(0===b.length&&0===c.length)return!0;if(b.length&&!isNaN(b[0])||c.length&&!isNaN(c[0])){if(b.length!==c.length)return!1;if(0===b.length)return!0;for(var e=0;ea.MAX_SEQUENCE_LENGTH)throw new Error("Sequences longer than "+a.MAX_SEQUENCE_LENGTH+" not supported.");this._alphabet=c,this._characters=b.map(function(a){return d._alphabet.getCharacterIdx(a)})}return a.prototype.serialize=function(){return JSON.stringify(this._characters)},a.Deserialize=function(b,c){c=new a([],c);return c._characters=JSON.parse(b),c},a.prototype.distance=function(b){return a._distance(this,b)},a._distance=function(b,c){var d=b._alphabet;if(d!==c._alphabet)throw new Error("Cannot Levenshtein compare Sequences built from different alphabets.");b=b._characters;c=c._characters;var e=b.length,f=c.length,g=a._costMatrix;g[0][0]=0;for(var h=0;h.98)&&(g.e.CrossToRef(a._forwardDir,a._inverseFromVec,a._upDir),a._upDir.normalize(),g.a.LookAtLHToRef(b,c,a._upDir,a._lookMatrix),d.subtractToRef(c,a._fromToVec),a._fromToVec.normalize(),g.e.TransformNormalToRef(a._fromToVec,a._lookMatrix,e),!0)},a._tokenizeSegment=function(b,c){a._bestMatch=0,a._score=g.e.Dot(b,c[0]),a._bestScore=a._score;for(var d=1;da._bestScore&&(a._bestMatch=d,a._bestScore=a._score);return a._bestMatch},a._forwardDir=new g.e,a._inverseFromVec=new g.e,a._upDir=new g.e,a._fromToVec=new g.e,a._lookMatrix=new g.a,a}(),mr=function(){function a(a){this.chars=new Array(a)}return a.Generate=function(b,c,d,e,f){void 0===b&&(b=64),void 0===c&&(c=256),void 0===d&&(d=.1),void 0===e&&(e=.001),void 0===f&&(f=[]);for(var h,k,i=new a(b),j=0;j1e-6&&q.scaleAndAddToRef(1/(q.lengthSquared()*k),o)}),o.scaleInPlace(h),i.chars[a].addInPlace(o),i.chars[a].normalize()};for(j=f.length;j4;d=Math.floor(d/2))e.push(b.resampleAtTargetResolution(d).tokenize(c.chars));return e},a.prototype.distance=function(a){for(var b=0,c=0;c0&&(this._averageDistance=Math.max(this._averageDistance/this._descriptors.length,a.MIN_AVERAGE_DISTANCE))},a.MIN_AVERAGE_DISTANCE=1,a}(),pr=function(){function a(){this._maximumAllowableMatchCost=4,this._nameToDescribedTrajectory=new Map}return a.prototype.serialize=function(){var a={};return a.maximumAllowableMatchCost=this._maximumAllowableMatchCost,a.vector3Alphabet=this._vector3Alphabet.serialize(),a.levenshteinAlphabet=this._levenshteinAlphabet.serialize(),a.nameToDescribedTrajectory=[],this._nameToDescribedTrajectory.forEach(function(b,c){a.nameToDescribedTrajectory.push(c),a.nameToDescribedTrajectory.push(b.serialize())}),JSON.stringify(a)},a.Deserialize=function(b){b=JSON.parse(b);var c=new a;c._maximumAllowableMatchCost=b.maximumAllowableMatchCost,c._vector3Alphabet=mr.Deserialize(b.vector3Alphabet),c._levenshteinAlphabet=fr.Alphabet.Deserialize(b.levenshteinAlphabet);for(var d=0;d=this._itemLength?NaN:this._view[a]},a.prototype.subarray=function(a,b){return a>=b||a<0?new Float32Array(0):(b>this._itemLength&&(b=this._itemLength),this._view.subarray(a,b))},a.prototype.push=function(a){this._view[this._itemLength]=a,this._itemLength++,this._itemLength>=this._view.length&&this._growArray()},a.prototype._growArray=function(){var a=Math.floor(1.5*this._view.length);a=new Float32Array(a);a.set(this._view),this._view=a},a}(),ur=/ /g,vr=function(){function a(b,c){var d=this;this._scene=b,this._collectDataAtFrame=function(){var b=Q.a.Now-d._startingTimestamp,c=d.datasets.ids.length,e=d.datasets.startingIndices.itemLength,f=0;if(e>0){e=d.datasets.startingIndices.at(e-1);f=e+d.datasets.data.at(e+a.NumberOfPointsOffset)+a.SliceDataOffset}if(d.datasets.startingIndices.push(f),d.datasets.data.push(b),d.datasets.data.push(c),d.datasets.ids.forEach(function(a){a=d._strategies.get(a);a&&d.datasets.data.push(a.getData())}),d.datasetObservable.hasObservers()){for(e=[b,c],b=0;b>c&255).toString(16)).substr(-2);return a},a.prototype.getCurrentSlice=function(){var a=this,b=[Q.a.Now-this._startingTimestamp,this.datasets.ids.length];this.datasets.ids.forEach(function(c){c=a._strategies.get(c);c&&a.datasetObservable.hasObservers()&&b.push(c.getData())}),this.datasetObservable.hasObservers()&&this.datasetObservable.notifyObservers(b)},a.prototype.updateMetadata=function(a,b,c){a=this._datasetMeta.get(a);a&&(a[b]=c,this.metadataObservable.notifyObservers(this._datasetMeta))},a.prototype.clear=function(a){this.datasets.data=new tr(1800),this.datasets.ids.length=0,this.datasets.startingIndices=new tr(1800),this._datasetMeta.clear(),this._strategies.forEach(function(a){return a.dispose()}),this._strategies.clear(),a||this._eventRestoreSet.clear(),this._hasLoadedData=!1},Object.defineProperty(a.prototype,"hasLoadedData",{get:function(){return this._hasLoadedData},enumerable:!1,configurable:!0}),a.prototype.loadFromFileData=function(b){b=b.replace(ur,"").split(" ").map(function(a){return a.split(",").filter(function(a){return a.length>0})}).filter(function(a){return a.length>0});var c=a.NumberOfPointsOffset;if(b.length<2)return!1;var d={ids:[],data:new tr(1800),startingIndices:new tr(1800)},e=b[0];b=b.slice(1);if(e.length<2||"timestamp"!==e[0]||"numPoints"!==e[c])return!1;for(var f=a.SliceDataOffset;f0&&this.onFeaturePointsAddedObservable.notifyObservers(d),c.length>0&&this.onFeaturePointsUpdatedObservable.notifyObservers(c)}}},b.prototype._init=function(){this._xrSessionManager.session.trySetFeaturePointCloudEnabled&&this._xrSessionManager.session.trySetFeaturePointCloudEnabled(!0)&&(this._enabled=!0)},b.Name=jb.FEATURE_POINTS,b.Version=1,b}(Vi);kb.AddWebXRFeature(Hr.Name,function(a){return function(){return new Hr(a)}},Hr.Version);var Ir=["wrist","thumb-metacarpal","thumb-phalanx-proximal","thumb-phalanx-distal","thumb-tip","index-finger-metacarpal","index-finger-phalanx-proximal","index-finger-phalanx-intermediate","index-finger-phalanx-distal","index-finger-tip","middle-finger-metacarpal","middle-finger-phalanx-proximal","middle-finger-phalanx-intermediate","middle-finger-phalanx-distal","middle-finger-tip","ring-finger-metacarpal","ring-finger-phalanx-proximal","ring-finger-phalanx-intermediate","ring-finger-phalanx-distal","ring-finger-tip","pinky-finger-metacarpal","pinky-finger-phalanx-proximal","pinky-finger-phalanx-intermediate","pinky-finger-phalanx-distal","pinky-finger-tip"],Jr=((a={}).wrist=["wrist"],a.thumb=["thumb-metacarpal","thumb-phalanx-proximal","thumb-phalanx-distal","thumb-tip"],a.index=["index-finger-metacarpal","index-finger-phalanx-proximal","index-finger-phalanx-intermediate","index-finger-phalanx-distal","index-finger-tip"],a.middle=["middle-finger-metacarpal","middle-finger-phalanx-proximal","middle-finger-phalanx-intermediate","middle-finger-phalanx-distal","middle-finger-tip"],a.ring=["ring-finger-metacarpal","ring-finger-phalanx-proximal","ring-finger-phalanx-intermediate","ring-finger-phalanx-distal","ring-finger-tip"],a.little=["pinky-finger-metacarpal","pinky-finger-phalanx-proximal","pinky-finger-phalanx-intermediate","pinky-finger-phalanx-distal","pinky-finger-tip"],a),Kr=function(){function a(a,b,c,d,e,f,h){void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===h&&(h=1),this.xrController=a,this._jointMeshes=b,this._handMesh=c,this.rigMapping=d,this._leftHandedMeshes=e,this._jointsInvisible=f,this._jointScaleFactor=h,this._jointTransforms=new Array(Ir.length),this._jointTransformMatrices=new Float32Array(16*Ir.length),this._tempJointMatrix=new g.a,this._jointRadii=new Float32Array(Ir.length),this._scene=b[0].getScene();for(a=0;ac.rotationThreshold?a.x:0,b.rotateY=Math.abs(a.y)>c.rotationThreshold?a.y:0}},{allowedComponentTypes:[Li.THUMBSTICK_TYPE,Li.TOUCHPAD_TYPE],forceHandedness:"right",axisChangedHandler:function(a,b,c,d){b.moveX=Math.abs(a.x)>c.movementThreshold?a.x:0,b.moveY=Math.abs(a.y)>c.movementThreshold?a.y:0}}]},b.Version=1,b}(Vi);kb.AddWebXRFeature(Qr.Name,function(a,b){return function(){return new Qr(a,b)}},Qr.Version,!0);var Rr=c(65),Sr=function(a){function b(b,c){var d=a.call(this,b)||this;return d.options=c,d._canvasContext=null,d._reflectionCubeMap=null,d._xrLightEstimate=null,d._xrLightProbe=null,d._xrWebGLBinding=null,d._lightDirection=g.e.Up().negateInPlace(),d._lightColor=h.a.White(),d._intensity=1,d._sphericalHarmonics=new Ge,d._cubeMapPollTime=Date.now(),d._lightEstimationPollTime=Date.now(),d._ReflectionCubeMapTextureSize=16,d.directionalLight=null,d.onReflectionCubeMapUpdatedObservable=new f.c,d._updateReflectionCubeMap=function(){var a;if(d._xrLightProbe){if(d.options.cubeMapPollInterval){var b=Date.now();if(b-d._cubeMapPollTime=this._samples.length)throw new Error("Index out of bounds");return this._samples[(this._idx+a)%this._samples.length]},a}(),Vr=function(){function a(){this._samples=new Ur(60),this._entropy=0,this.onFirstStepDetected=new f.c}return a.prototype.update=function(a,b,c,d){this._samples.push(a,b);a=this._samples.at(0);if(this._entropy*=this._entropyDecayFactor,this._entropy+=g.d.Distance(a,this._samples.at(1)),!(this._entropy>this._entropyThreshold)){for(b=this._samePointCheckStartIdx;be&&(f=i,e=h);if(!(eb*this._squaredProjectionDistanceThreshold)){j=g.c.Vector3[0];j.set(c,d,0);f=g.c.Vector3[1];f.set(e.x,e.y,0);i=g.e.Cross(j,f).z>0;k=a.clone();b=a.clone();h.subtractToRef(a,e),i?(e.scaleAndAddToRef(this._axisToApexShrinkFactor,k),e.scaleAndAddToRef(this._axisToApexExtendFactor,b)):(e.scaleAndAddToRef(this._axisToApexExtendFactor,k),e.scaleAndAddToRef(this._axisToApexShrinkFactor,b)),this.onFirstStepDetected.notifyObservers({leftApex:k,rightApex:b,currentPosition:a,currentStepDirection:i?"right":"left"})}}}}},a.prototype.reset=function(){for(var a=0;athis._maxT&&(this._maxT=this._t,this._maxTPosition.copyFromFloats(a,b)),!(this._vitalityc&&(this.onMovement.notifyObservers({deltaT:this._t-c}),c<.5&&this._t>=.5&&this.onFootfall.notifyObservers({foot:this._steppingLeft?"left":"right"})),this._t<.95*this._maxT&&(this._currentPosition.copyFromFloats(a,b),this._steppingLeft?this._leftApex.copyFrom(this._maxTPosition):this._rightApex.copyFrom(this._maxTPosition),this._reset(this._leftApex,this._rightApex,this._currentPosition,!this._steppingLeft)),!(this._axisLength<.03))},Object.defineProperty(a.prototype,"_vitalityThreshold",{get:function(){return.1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"forward",{get:function(){return this._forward},enumerable:!1,configurable:!0}),a}(),Xr=function(){function a(){var a=this;this._detector=new Vr,this._walker=null,this._movement=new g.d,this.movementThisFrame=g.e.Zero(),this._detector.onFirstStepDetected.add(function(b){a._walker||(a._walker=new Wr(b.leftApex,b.rightApex,b.currentPosition,b.currentStepDirection),a._walker.onFootfall.add(function(){}),a._walker.onMovement.add(function(b){a._walker.forward.scaleAndAddToRef(.024*b.deltaT,a._movement)}))})}return a.prototype.update=function(a,b){(b.y=0,b.normalize(),this._detector.update(a.x,a.z,b.x,b.z),this._walker)&&(this._walker.update(a.x,a.z)||(this._walker=null)),(this.movementThisFrame.set(this._movement.x,0,this._movement.y),this._movement.scaleInPlace(.96))},a}(),Yr=function(a){function b(b,c){var d=a.call(this,b)||this;return d._up=new g.e,d._forward=new g.e,d._position=new g.e,d._movement=new g.e,d._sessionManager=b,d.locomotionTarget=c.locomotionTarget,d._isLocomotionTargetWebXRCamera&&q.a.Warn("Using walking locomotion directly on a WebXRCamera may have unintended interactions with other XR techniques. Using an XR space parent is highly recommended"),d}return Object(l.d)(b,a),Object.defineProperty(b,"Name",{get:function(){return jb.WALKING_LOCOMOTION},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Version",{get:function(){return 1},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"locomotionTarget",{get:function(){return this._locomotionTarget},set:function(a){this._locomotionTarget=a,this._isLocomotionTargetWebXRCamera="WebXRCamera"===this._locomotionTarget.getClassName()},enumerable:!1,configurable:!0}),b.prototype.isCompatible=function(){return void 0===this._sessionManager.sessionMode||"immersive-vr"===this._sessionManager.sessionMode},b.prototype.attach=function(){return!(!this.isCompatible||!a.prototype.attach.call(this))&&(this._walker=new Xr,!0)},b.prototype.detach=function(){return!!a.prototype.detach.call(this)&&(this._walker=null,!0)},b.prototype._onXRFrame=function(a){a=a.getViewerPose(this._sessionManager.baseReferenceSpace);if(a){var b=this.locomotionTarget.getScene().useRightHandedSystem?1:-1;a=a.transform.matrix;this._up.copyFromFloats(a[4],a[5],b*a[6]),this._forward.copyFromFloats(a[8],a[9],b*a[10]),this._position.copyFromFloats(a[12],a[13],b*a[14]),this._forward.scaleAndAddToRef(.05,this._position),this._up.scaleAndAddToRef(-.05,this._position),this._walker.update(this._position,this._forward),this._movement.copyFrom(this._walker.movementThisFrame),this._isLocomotionTargetWebXRCamera||g.e.TransformNormalToRef(this._movement,this.locomotionTarget.getWorldMatrix(),this._movement),this.locomotionTarget.position.addInPlace(this._movement)}},b}(Vi);kb.AddWebXRFeature(Yr.Name,function(a,b){return function(){return new Yr(a,b)}},Yr.Version,!1);var Zr=function(a){function b(b,c,d){b=a.call(this,b,$r[d],c,d,!0)||this;return b.profileId="generic-hand-select-grasp",b}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},b.prototype._getModelLoadingConstraints=function(){return!0},b.prototype._processLoadedModel=function(a){},b.prototype._setRootMesh=function(a){},b.prototype._updateModel=function(){},b}(Mi);Ri.RegisterController("generic-hand-select-grasp",function(a,b){return new Zr(b,a.gamepad,a.handedness)});var $r={left:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-left",assetPath:"left.glb"},right:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-right",assetPath:"right.glb"},none:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-none",assetPath:"none.glb"}},as=function(a){function b(b,c,d){b=a.call(this,b,bs["left-right"],c,d)||this;return b._mapping={defaultButton:{valueNodeName:"VALUE",unpressedNodeName:"UNPRESSED",pressedNodeName:"PRESSED"},defaultAxis:{valueNodeName:"VALUE",minNodeName:"MIN",maxNodeName:"MAX"},buttons:{"xr-standard-trigger":{rootNodeName:"SELECT",componentProperty:"button",states:["default","touched","pressed"]},"xr-standard-squeeze":{rootNodeName:"GRASP",componentProperty:"state",states:["pressed"]},"xr-standard-touchpad":{rootNodeName:"TOUCHPAD_PRESS",labelAnchorNodeName:"squeeze-label",touchPointNodeName:"TOUCH"},"xr-standard-thumbstick":{rootNodeName:"THUMBSTICK_PRESS",componentProperty:"state",states:["pressed"]}},axes:{"xr-standard-touchpad":{"x-axis":{rootNodeName:"TOUCHPAD_TOUCH_X"},"y-axis":{rootNodeName:"TOUCHPAD_TOUCH_Y"}},"xr-standard-thumbstick":{"x-axis":{rootNodeName:"THUMBSTICK_X"},"y-axis":{rootNodeName:"THUMBSTICK_Y"}}}},b.profileId="microsoft-mixed-reality",b}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"left"===this.handedness?b.MODEL_LEFT_FILENAME:b.MODEL_RIGHT_FILENAME,path:b.MODEL_BASE_URL+"default/"}},b.prototype._getModelLoadingConstraints=function(){var a=hh.IsPluginForExtensionAvailable(".glb");return a||q.a.Warn("glTF / glb loaded was not registered, using generic controller instead"),a},b.prototype._processLoadedModel=function(a){var b=this;this.rootMesh&&(this.getComponentIds().forEach(function(a,c){if(!b.disableAnimation&&a&&b.rootMesh){var d=b._mapping.buttons[a],e=d.rootNodeName;if(!e)return void q.a.Log("Skipping unknown button at index: "+c+" with mapped name: "+a);c=b._getChildByName(b.rootMesh,e);if(!c)return void q.a.Warn("Missing button mesh with name: "+e);if(d.valueMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.valueNodeName),d.pressedMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.pressedNodeName),d.unpressedMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.unpressedNodeName),d.valueMesh&&d.pressedMesh&&d.unpressedMesh){c=b.getComponent(a);c&&c.onButtonStateChangedObservable.add(function(a){b._lerpTransform(d,a.value)},void 0,!0)}else q.a.Warn("Missing button submesh under mesh with name: "+e)}}),this.getComponentIds().forEach(function(a,c){var d=b.getComponent(a);d.isAxes()&&["x-axis","y-axis"].forEach(function(c){if(b.rootMesh){var e=b._mapping.axes[a][c],f=b._getChildByName(b.rootMesh,e.rootNodeName);f?(e.valueMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.valueNodeName),e.minMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.minNodeName),e.maxMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.maxNodeName),e.valueMesh&&e.minMesh&&e.maxMesh?d&&d.onAxisValueChangedObservable.add(function(a){a="x-axis"===c?a.x:a.y;b._lerpTransform(e,a,!0)},void 0,!0):q.a.Warn("Missing axis submesh under mesh with name: "+e.rootNodeName)):q.a.Warn("Missing axis mesh with name: "+e.rootNodeName)}})}))},b.prototype._setRootMesh=function(a){var b;this.rootMesh=new S.a(this.profileId+" "+this.handedness,this.scene),this.rootMesh.isPickable=!1;for(var c=0;cshadow ? darkness : 1.0; } #define inline float computeShadowWithPoissonSamplingCube(vec3 lightPosition,samplerCube shadowSampler,float mapSize,float darkness,vec2 depthValues) { vec3 directionToLight=vPositionW-lightPosition; float depth=length(directionToLight); depth=(depth+depthValues.x)/(depthValues.y); depth=clamp(depth,0.,1.0); directionToLight=normalize(directionToLight); directionToLight.y=-directionToLight.y; float visibility=1.; vec3 poissonDisk[4]; poissonDisk[0]=vec3(-1.0,1.0,-1.0); poissonDisk[1]=vec3(1.0,-1.0,-1.0); poissonDisk[2]=vec3(-1.0,-1.0,-1.0); poissonDisk[3]=vec3(1.0,-1.0,1.0); #ifndef SHADOWFLOAT if (unpack(textureCube(shadowSampler,directionToLight+poissonDisk[0]*mapSize))shadow ? computeFallOff(darkness,clipSpace.xy,frustumEdgeFalloff) : 1.; } #endif #define inline float computeShadow(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadow=unpack(texture2D(shadowSampler,uv)); #else float shadow=texture2D(shadowSampler,uv).x; #endif return shadowPixelDepth>shadow ? computeFallOff(darkness,clipSpace.xy,frustumEdgeFalloff) : 1.; } } #define inline float computeShadowWithPoissonSampling(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float mapSize,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); float visibility=1.; vec2 poissonDisk[4]; poissonDisk[0]=vec2(-0.94201624,-0.39906216); poissonDisk[1]=vec2(0.94558609,-0.76890725); poissonDisk[2]=vec2(-0.094184101,-0.92938870); poissonDisk[3]=vec2(0.34495938,0.29387760); #ifndef SHADOWFLOAT if (unpack(texture2D(shadowSampler,uv+poissonDisk[0]*mapSize))1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadowMapSample=unpack(texture2D(shadowSampler,uv)); #else float shadowMapSample=texture2D(shadowSampler,uv).x; #endif float esm=1.0-clamp(exp(min(87.,depthScale*shadowPixelDepth))*shadowMapSample,0.,1.-darkness); return computeFallOff(esm,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithCloseESM(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float darkness,float depthScale,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadowMapSample=unpack(texture2D(shadowSampler,uv)); #else float shadowMapSample=texture2D(shadowSampler,uv).x; #endif float esm=clamp(exp(min(87.,-depthScale*(shadowPixelDepth-shadowMapSample))),darkness,1.); return computeFallOff(esm,clipSpace.xy,frustumEdgeFalloff); } } #ifdef IS_NDC_HALF_ZRANGE #define ZINCLIP clipSpace.z #else #define ZINCLIP uvDepth.z #endif #if defined(WEBGL2) || defined(WEBGPU) #define GREATEST_LESS_THAN_ONE 0.99999994 #define inline float computeShadowWithCSMPCF1(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec4 uvDepthLayer=vec4(uvDepth.x,uvDepth.y,layer,uvDepth.z); float shadow=texture2D(shadowSampler,uvDepthLayer); shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithCSMPCF3(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=3.-2.*st; vec2 uvw1=1.+2.*st; vec2 u=vec2((2.-st.x)/uvw0.x-1.,st.x/uvw1.x+1.)*shadowMapSizeAndInverse.y; vec2 v=vec2((2.-st.y)/uvw0.y-1.,st.y/uvw1.y+1.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[0]),layer,uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[0]),layer,uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[1]),layer,uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[1]),layer,uvDepth.z)); shadow=shadow/16.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithCSMPCF5(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=4.-3.*st; vec2 uvw1=vec2(7.); vec2 uvw2=1.+3.*st; vec3 u=vec3((3.-2.*st.x)/uvw0.x-2.,(3.+st.x)/uvw1.x,st.x/uvw2.x+2.)*shadowMapSizeAndInverse.y; vec3 v=vec3((3.-2.*st.y)/uvw0.y-2.,(3.+st.y)/uvw1.y,st.y/uvw2.y+2.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[0]),layer,uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[0]),layer,uvDepth.z)); shadow+=uvw2.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[0]),layer,uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[1]),layer,uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[1]),layer,uvDepth.z)); shadow+=uvw2.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[1]),layer,uvDepth.z)); shadow+=uvw0.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[2]),layer,uvDepth.z)); shadow+=uvw1.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[2]),layer,uvDepth.z)); shadow+=uvw2.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[2]),layer,uvDepth.z)); shadow=shadow/144.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithPCF1(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; float shadow=texture2D(shadowSampler,uvDepth); shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithPCF3(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=3.-2.*st; vec2 uvw1=1.+2.*st; vec2 u=vec2((2.-st.x)/uvw0.x-1.,st.x/uvw1.x+1.)*shadowMapSizeAndInverse.y; vec2 v=vec2((2.-st.y)/uvw0.y-1.,st.y/uvw1.y+1.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[0]),uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[0]),uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[1]),uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[1]),uvDepth.z)); shadow=shadow/16.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithPCF5(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=4.-3.*st; vec2 uvw1=vec2(7.); vec2 uvw2=1.+3.*st; vec3 u=vec3((3.-2.*st.x)/uvw0.x-2.,(3.+st.x)/uvw1.x,st.x/uvw2.x+2.)*shadowMapSizeAndInverse.y; vec3 v=vec3((3.-2.*st.y)/uvw0.y-2.,(3.+st.y)/uvw1.y,st.y/uvw2.y+2.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[0]),uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[0]),uvDepth.z)); shadow+=uvw2.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[0]),uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[1]),uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[1]),uvDepth.z)); shadow+=uvw2.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[1]),uvDepth.z)); shadow+=uvw0.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[2]),uvDepth.z)); shadow+=uvw1.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[2]),uvDepth.z)); shadow+=uvw2.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[2]),uvDepth.z)); shadow=shadow/144.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } const vec3 PoissonSamplers32[64]=vec3[64]( vec3(0.06407013,0.05409927,0.), vec3(0.7366577,0.5789394,0.), vec3(-0.6270542,-0.5320278,0.), vec3(-0.4096107,0.8411095,0.), vec3(0.6849564,-0.4990818,0.), vec3(-0.874181,-0.04579735,0.), vec3(0.9989998,0.0009880066,0.), vec3(-0.004920578,-0.9151649,0.), vec3(0.1805763,0.9747483,0.), vec3(-0.2138451,0.2635818,0.), vec3(0.109845,0.3884785,0.), vec3(0.06876755,-0.3581074,0.), vec3(0.374073,-0.7661266,0.), vec3(0.3079132,-0.1216763,0.), vec3(-0.3794335,-0.8271583,0.), vec3(-0.203878,-0.07715034,0.), vec3(0.5912697,0.1469799,0.), vec3(-0.88069,0.3031784,0.), vec3(0.5040108,0.8283722,0.), vec3(-0.5844124,0.5494877,0.), vec3(0.6017799,-0.1726654,0.), vec3(-0.5554981,0.1559997,0.), vec3(-0.3016369,-0.3900928,0.), vec3(-0.5550632,-0.1723762,0.), vec3(0.925029,0.2995041,0.), vec3(-0.2473137,0.5538505,0.), vec3(0.9183037,-0.2862392,0.), vec3(0.2469421,0.6718712,0.), vec3(0.3916397,-0.4328209,0.), vec3(-0.03576927,-0.6220032,0.), vec3(-0.04661255,0.7995201,0.), vec3(0.4402924,0.3640312,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.) ); const vec3 PoissonSamplers64[64]=vec3[64]( vec3(-0.613392,0.617481,0.), vec3(0.170019,-0.040254,0.), vec3(-0.299417,0.791925,0.), vec3(0.645680,0.493210,0.), vec3(-0.651784,0.717887,0.), vec3(0.421003,0.027070,0.), vec3(-0.817194,-0.271096,0.), vec3(-0.705374,-0.668203,0.), vec3(0.977050,-0.108615,0.), vec3(0.063326,0.142369,0.), vec3(0.203528,0.214331,0.), vec3(-0.667531,0.326090,0.), vec3(-0.098422,-0.295755,0.), vec3(-0.885922,0.215369,0.), vec3(0.566637,0.605213,0.), vec3(0.039766,-0.396100,0.), vec3(0.751946,0.453352,0.), vec3(0.078707,-0.715323,0.), vec3(-0.075838,-0.529344,0.), vec3(0.724479,-0.580798,0.), vec3(0.222999,-0.215125,0.), vec3(-0.467574,-0.405438,0.), vec3(-0.248268,-0.814753,0.), vec3(0.354411,-0.887570,0.), vec3(0.175817,0.382366,0.), vec3(0.487472,-0.063082,0.), vec3(-0.084078,0.898312,0.), vec3(0.488876,-0.783441,0.), vec3(0.470016,0.217933,0.), vec3(-0.696890,-0.549791,0.), vec3(-0.149693,0.605762,0.), vec3(0.034211,0.979980,0.), vec3(0.503098,-0.308878,0.), vec3(-0.016205,-0.872921,0.), vec3(0.385784,-0.393902,0.), vec3(-0.146886,-0.859249,0.), vec3(0.643361,0.164098,0.), vec3(0.634388,-0.049471,0.), vec3(-0.688894,0.007843,0.), vec3(0.464034,-0.188818,0.), vec3(-0.440840,0.137486,0.), vec3(0.364483,0.511704,0.), vec3(0.034028,0.325968,0.), vec3(0.099094,-0.308023,0.), vec3(0.693960,-0.366253,0.), vec3(0.678884,-0.204688,0.), vec3(0.001801,0.780328,0.), vec3(0.145177,-0.898984,0.), vec3(0.062655,-0.611866,0.), vec3(0.315226,-0.604297,0.), vec3(-0.780145,0.486251,0.), vec3(-0.371868,0.882138,0.), vec3(0.200476,0.494430,0.), vec3(-0.494552,-0.711051,0.), vec3(0.612476,0.705252,0.), vec3(-0.578845,-0.768792,0.), vec3(-0.772454,-0.090976,0.), vec3(0.504440,0.372295,0.), vec3(0.155736,0.065157,0.), vec3(0.391522,0.849605,0.), vec3(-0.620106,-0.328104,0.), vec3(0.789239,-0.419965,0.), vec3(-0.545396,0.538133,0.), vec3(-0.178564,-0.596057,0.) ); #define inline float computeShadowWithCSMPCSS(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArray depthSampler,highp sampler2DArrayShadow shadowSampler,float shadowMapSizeInverse,float lightSizeUV,float darkness,float frustumEdgeFalloff,int searchTapCount,int pcfTapCount,vec3[64] poissonSamplers,vec2 lightSizeUVCorrection,float depthCorrection,float penumbraDarkness) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec4 uvDepthLayer=vec4(uvDepth.x,uvDepth.y,layer,uvDepth.z); float blockerDepth=0.0; float sumBlockerDepth=0.0; float numBlocker=0.0; for (int i=0; i1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; float blockerDepth=0.0; float sumBlockerDepth=0.0; float numBlocker=0.0; for (int i=0; i(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_SAMPLERNAME_,bump) #endif #if defined(DETAIL) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_SAMPLERNAME_,detail) #endif #if defined(BUMP) && defined(PARALLAX) const float minSamples=4.; const float maxSamples=15.; const int iMaxSamples=15; vec2 parallaxOcclusion(vec3 vViewDirCoT,vec3 vNormalCoT,vec2 texCoord,float parallaxScale) { float parallaxLimit=length(vViewDirCoT.xy)/vViewDirCoT.z; parallaxLimit*=parallaxScale; vec2 vOffsetDir=normalize(vViewDirCoT.xy); vec2 vMaxOffset=vOffsetDir*parallaxLimit; float numSamples=maxSamples+(dot(vViewDirCoT,vNormalCoT)*(minSamples-maxSamples)); float stepSize=1.0/numSamples; float currRayHeight=1.0; vec2 vCurrOffset=vec2(0,0); vec2 vLastOffset=vec2(0,0); float lastSampledHeight=1.0; float currSampledHeight=1.0; for (int i=0; icurrRayHeight) { float delta1=currSampledHeight-currRayHeight; float delta2=(currRayHeight+stepSize)-lastSampledHeight; float ratio=delta1/(delta1+delta2); vCurrOffset=(ratio)* vLastOffset+(1.0-ratio)*vCurrOffset; break; } else { currRayHeight-=stepSize; vLastOffset=vCurrOffset; vCurrOffset+=stepSize*vMaxOffset; lastSampledHeight=currSampledHeight; } } return vCurrOffset; } vec2 parallaxOffset(vec3 viewDir,float heightScale) { float height=texture2D(bumpSampler,vBumpUV).w; vec2 texCoordOffset=heightScale*viewDir.xy*height; return -texCoordOffset; } #endif ";a.a.IncludesShadersStore[b]=c},function(a,b,c){a="vec2 uvOffset=vec2(0.0,0.0); #if defined(BUMP) || defined(PARALLAX) || defined(DETAIL) #ifdef NORMALXYSCALE float normalScale=1.0; #elif defined(BUMP) float normalScale=vBumpInfos.y; #else float normalScale=1.0; #endif #if defined(TANGENT) && defined(NORMAL) mat3 TBN=vTBN; #elif defined(BUMP) vec2 TBNUV=gl_FrontFacing ? vBumpUV : -vBumpUV; mat3 TBN=cotangent_frame(normalW*normalScale,vPositionW,TBNUV,vTangentSpaceParams); #else vec2 TBNUV=gl_FrontFacing ? vDetailUV : -vDetailUV; mat3 TBN=cotangent_frame(normalW*normalScale,vPositionW,TBNUV,vec2(1.,1.)); #endif #elif defined(ANISOTROPIC) #if defined(TANGENT) && defined(NORMAL) mat3 TBN=vTBN; #else vec2 TBNUV=gl_FrontFacing ? vMainUV1 : -vMainUV1; mat3 TBN=cotangent_frame(normalW,vPositionW,TBNUV,vec2(1.,1.)); #endif #endif #ifdef PARALLAX mat3 invTBN=transposeMat3(TBN); #ifdef PARALLAXOCCLUSION uvOffset=parallaxOcclusion(invTBN*-viewDirectionW,invTBN*normalW,vBumpUV,vBumpInfos.z); #else uvOffset=parallaxOffset(invTBN*viewDirectionW,vBumpInfos.z); #endif #endif #ifdef DETAIL vec4 detailColor=texture2D(detailSampler,vDetailUV+uvOffset); vec2 detailNormalRG=detailColor.wy*2.0-1.0; float detailNormalB=sqrt(1.-saturate(dot(detailNormalRG,detailNormalRG))); vec3 detailNormal=vec3(detailNormalRG,detailNormalB); #endif #ifdef BUMP #ifdef OBJECTSPACE_NORMALMAP normalW=normalize(texture2D(bumpSampler,vBumpUV).xyz*2.0-1.0); normalW=normalize(mat3(normalMatrix)*normalW); #elif !defined(DETAIL) normalW=perturbNormal(TBN,texture2D(bumpSampler,vBumpUV+uvOffset).xyz,vBumpInfos.y); #else vec3 bumpNormal=texture2D(bumpSampler,vBumpUV+uvOffset).xyz*2.0-1.0; #if DETAIL_NORMALBLENDMETHOD == 0 detailNormal.xy*=vDetailInfos.z; vec3 blendedNormal=normalize(vec3(bumpNormal.xy+detailNormal.xy,bumpNormal.z*detailNormal.z)); #elif DETAIL_NORMALBLENDMETHOD == 1 detailNormal.xy*=vDetailInfos.z; bumpNormal+=vec3(0.0,0.0,1.0); detailNormal*=vec3(-1.0,-1.0,1.0); vec3 blendedNormal=bumpNormal*dot(bumpNormal,detailNormal)/bumpNormal.z-detailNormal; #endif normalW=perturbNormalBase(TBN,blendedNormal,vBumpInfos.y); #endif #elif defined(DETAIL) detailNormal.xy*=vDetailInfos.z; normalW=perturbNormalBase(TBN,detailNormal,vDetailInfos.z); #endif ";c(5).a.IncludesShadersStore.bumpFragment=a},function(a,b,c){a="lightFragment";b="#ifdef LIGHT{X} #if defined(SHADOWONLY) || defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X}) #else #ifdef PBR #ifdef SPOTLIGHT{X} preInfo=computePointAndSpotPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(POINTLIGHT{X}) preInfo=computePointAndSpotPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(HEMILIGHT{X}) preInfo=computeHemisphericPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(DIRLIGHT{X}) preInfo=computeDirectionalPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #endif preInfo.NdotV=NdotV; #ifdef SPOTLIGHT{X} #ifdef LIGHT_FALLOFF_GLTF{X} preInfo.attenuation=computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared,light{X}.vLightFalloff.y); preInfo.attenuation*=computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightFalloff.z,light{X}.vLightFalloff.w); #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) preInfo.attenuation=computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); preInfo.attenuation*=computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w); #elif defined(LIGHT_FALLOFF_STANDARD{X}) preInfo.attenuation=computeDistanceLightFalloff_Standard(preInfo.lightOffset,light{X}.vLightFalloff.x); preInfo.attenuation*=computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w,light{X}.vLightData.w); #else preInfo.attenuation=computeDistanceLightFalloff(preInfo.lightOffset,preInfo.lightDistanceSquared,light{X}.vLightFalloff.x,light{X}.vLightFalloff.y); preInfo.attenuation*=computeDirectionalLightFalloff(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w,light{X}.vLightData.w,light{X}.vLightFalloff.z,light{X}.vLightFalloff.w); #endif #elif defined(POINTLIGHT{X}) #ifdef LIGHT_FALLOFF_GLTF{X} preInfo.attenuation=computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared,light{X}.vLightFalloff.y); #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) preInfo.attenuation=computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); #elif defined(LIGHT_FALLOFF_STANDARD{X}) preInfo.attenuation=computeDistanceLightFalloff_Standard(preInfo.lightOffset,light{X}.vLightFalloff.x); #else preInfo.attenuation=computeDistanceLightFalloff(preInfo.lightOffset,preInfo.lightDistanceSquared,light{X}.vLightFalloff.x,light{X}.vLightFalloff.y); #endif #else preInfo.attenuation=1.0; #endif #ifdef HEMILIGHT{X} preInfo.roughness=roughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(roughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif #ifdef HEMILIGHT{X} info.diffuse=computeHemisphericDiffuseLighting(preInfo,light{X}.vLightDiffuse.rgb,light{X}.vLightGround); #elif defined(SS_TRANSLUCENCY) info.diffuse=computeDiffuseAndTransmittedLighting(preInfo,light{X}.vLightDiffuse.rgb,subSurfaceOut.transmittance); #else info.diffuse=computeDiffuseLighting(preInfo,light{X}.vLightDiffuse.rgb); #endif #ifdef SPECULARTERM #ifdef ANISOTROPIC info.specular=computeAnisotropicSpecularLighting(preInfo,viewDirectionW,normalW,anisotropicOut.anisotropicTangent,anisotropicOut.anisotropicBitangent,anisotropicOut.anisotropy,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #else info.specular=computeSpecularLighting(preInfo,normalW,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #endif #endif #ifdef SHEEN #ifdef SHEEN_LINKWITHALBEDO preInfo.roughness=sheenOut.sheenIntensity; #else #ifdef HEMILIGHT{X} preInfo.roughness=sheenOut.sheenRoughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(sheenOut.sheenRoughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif #endif info.sheen=computeSheenLighting(preInfo,normalW,sheenOut.sheenColor,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #endif #ifdef CLEARCOAT #ifdef HEMILIGHT{X} preInfo.roughness=clearcoatOut.clearCoatRoughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(clearcoatOut.clearCoatRoughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif info.clearCoat=computeClearCoatLighting(preInfo,clearcoatOut.clearCoatNormalW,clearcoatOut.clearCoatAARoughnessFactors.x,clearcoatOut.clearCoatIntensity,light{X}.vLightDiffuse.rgb); #ifdef CLEARCOAT_TINT absorption=computeClearCoatLightingAbsorption(clearcoatOut.clearCoatNdotVRefract,preInfo.L,clearcoatOut.clearCoatNormalW,clearcoatOut.clearCoatColor,clearcoatOut.clearCoatThickness,clearcoatOut.clearCoatIntensity); info.diffuse*=absorption; #ifdef SPECULARTERM info.specular*=absorption; #endif #endif info.diffuse*=info.clearCoat.w; #ifdef SPECULARTERM info.specular*=info.clearCoat.w; #endif #ifdef SHEEN info.sheen*=info.clearCoat.w; #endif #endif #else #ifdef SPOTLIGHT{X} info=computeSpotLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDirection,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightDiffuse.a,glossiness); #elif defined(HEMILIGHT{X}) info=computeHemisphericLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightGround,glossiness); #elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X}) info=computeLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightDiffuse.a,glossiness); #endif #endif #ifdef PROJECTEDLIGHTTEXTURE{X} info.diffuse*=computeProjectionTextureDiffuseLighting(projectionLightSampler{X},textureProjectionMatrix{X}); #endif #endif #ifdef SHADOW{X} #ifdef SHADOWCSM{X} for (int i=0; i=0.) { index{X}=i; break; } } #ifdef SHADOWCSMUSESHADOWMAXZ{X} if (index{X}>=0) #endif { #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithCSMPCF1(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithCSMPCF3(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithCSMPCF5(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithCSMPCSS16(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithCSMPCSS32(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #else shadow=computeShadowWithCSMPCSS64(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #endif #else shadow=computeShadowCSM(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #ifdef SHADOWCSMDEBUG{X} shadowDebug{X}=vec3(shadow)*vCascadeColorsMultiplier{X}[index{X}]; #endif #ifndef SHADOWCSMNOBLEND{X} float frustumLength=frustumLengths{X}[index{X}]; float diffRatio=clamp(diff{X}/frustumLength,0.,1.)*cascadeBlendFactor{X}; if (index{X}<(SHADOWCSMNUM_CASCADES{X}-1) && diffRatio<1.) { index{X}+=1; float nextShadow=0.; #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) nextShadow=computeShadowWithCSMPCF1(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) nextShadow=computeShadowWithCSMPCF3(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else nextShadow=computeShadowWithCSMPCF5(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) nextShadow=computeShadowWithCSMPCSS16(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) nextShadow=computeShadowWithCSMPCSS32(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #else nextShadow=computeShadowWithCSMPCSS64(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #endif #else nextShadow=computeShadowCSM(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif shadow=mix(nextShadow,shadow,diffRatio); #ifdef SHADOWCSMDEBUG{X} shadowDebug{X}=mix(vec3(nextShadow)*vCascadeColorsMultiplier{X}[index{X}],shadowDebug{X},diffRatio); #endif } #endif } #elif defined(SHADOWCLOSEESM{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithCloseESMCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.depthValues); #else shadow=computeShadowWithCloseESM(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWESM{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithESMCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.depthValues); #else shadow=computeShadowWithESM(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPOISSON{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithPoissonSamplingCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.x,light{X}.depthValues); #else shadow=computeShadowWithPoissonSampling(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithPCF1(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithPCF3(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithPCF5(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithPCSS16(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithPCSS32(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithPCSS64(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #else #if defined(SHADOWCUBE{X}) shadow=computeShadowCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.depthValues); #else shadow=computeShadow(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #endif #ifdef SHADOWONLY #ifndef SHADOWINUSE #define SHADOWINUSE #endif globalShadow+=shadow; shadowLightCount+=1.0; #endif #else shadow=1.; #endif #ifndef SHADOWONLY #ifdef CUSTOMUSERLIGHTING diffuseBase+=computeCustomDiffuseLighting(info,diffuseBase,shadow); #ifdef SPECULARTERM specularBase+=computeCustomSpecularLighting(info,specularBase,shadow); #endif #elif defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) diffuseBase+=lightmapColor.rgb*shadow; #ifdef SPECULARTERM #ifndef LIGHTMAPNOSPECULAR{X} specularBase+=info.specular*shadow*lightmapColor.rgb; #endif #endif #ifdef CLEARCOAT #ifndef LIGHTMAPNOSPECULAR{X} clearCoatBase+=info.clearCoat.rgb*shadow*lightmapColor.rgb; #endif #endif #ifdef SHEEN #ifndef LIGHTMAPNOSPECULAR{X} sheenBase+=info.sheen.rgb*shadow; #endif #endif #else #ifdef SHADOWCSMDEBUG{X} diffuseBase+=info.diffuse*shadowDebug{X}; #else diffuseBase+=info.diffuse*shadow; #endif #ifdef SPECULARTERM specularBase+=info.specular*shadow; #endif #ifdef CLEARCOAT clearCoatBase+=info.clearCoat.rgb*shadow; #endif #ifdef SHEEN sheenBase+=info.sheen.rgb*shadow; #endif #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#ifdef FOG float fog=CalcFogFactor(); #ifdef PBR fog=toLinearSpace(fog); #endif color.rgb=mix(vFogColor,color.rgb,fog); #endif";c(5).a.IncludesShadersStore.fogFragment=a},function(a,b,c){a="fogVertexDeclaration";b="#ifdef FOG varying vec3 vFogDistance; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="lightVxFragmentDeclaration";b="#ifdef LIGHT{X} uniform vec4 vLightData{X}; uniform vec4 vLightDiffuse{X}; #ifdef SPECULARTERM uniform vec4 vLightSpecular{X}; #else vec4 vLightSpecular{X}=vec4(0.); #endif #ifdef SHADOW{X} #ifdef SHADOWCSM{X} uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}]; varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromCamera{X}; #elif defined(SHADOWCUBE{X}) #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; uniform mat4 lightMatrix{X}; #endif uniform vec4 shadowsInfo{X}; uniform vec2 depthValues{X}; #endif #ifdef SPOTLIGHT{X} uniform vec4 vLightDirection{X}; uniform vec4 vLightFalloff{X}; #elif defined(POINTLIGHT{X}) uniform vec4 vLightFalloff{X}; #elif defined(HEMILIGHT{X}) uniform vec3 vLightGround{X}; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="lightVxUboDeclaration";b="#ifdef LIGHT{X} uniform Light{X} { vec4 vLightData; vec4 vLightDiffuse; vec4 vLightSpecular; #ifdef SPOTLIGHT{X} vec4 vLightDirection; vec4 vLightFalloff; #elif defined(POINTLIGHT{X}) vec4 vLightFalloff; #elif defined(HEMILIGHT{X}) vec3 vLightGround; #endif vec4 shadowsInfo; vec2 depthValues; } light{X}; #ifdef SHADOW{X} #ifdef SHADOWCSM{X} uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}]; varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromCamera{X}; #elif defined(SHADOWCUBE{X}) #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; uniform mat4 lightMatrix{X}; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="shadowsVertex";b="#ifdef SHADOWS #if defined(SHADOWCSM{X}) vPositionFromCamera{X}=view*worldPos; for (int i=0; i1)for(var h=0;h=a||-1!==c.indexOf("file:")?-1:Math.pow(2,e)*b}},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.reset()}return a.prototype.reset=function(){this.enabled=!1,this.mask=255,this.func=a.ALWAYS,this.funcRef=1,this.funcMask=255,this.opStencilFail=a.KEEP,this.opDepthFail=a.KEEP,this.opStencilDepthPass=a.REPLACE},Object.defineProperty(a.prototype,"stencilFunc",{get:function(){return this.func},set:function(a){this.func=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilFuncRef",{get:function(){return this.funcRef},set:function(a){this.funcRef=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilFuncMask",{get:function(){return this.funcMask},set:function(a){this.funcMask=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpStencilFail",{get:function(){return this.opStencilFail},set:function(a){this.opStencilFail=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpDepthFail",{get:function(){return this.opDepthFail},set:function(a){this.opDepthFail=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpStencilDepthPass",{get:function(){return this.opStencilDepthPass},set:function(a){this.opStencilDepthPass=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilMask",{get:function(){return this.mask},set:function(a){this.mask=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilTest",{get:function(){return this.enabled},set:function(a){this.enabled=a},enumerable:!1,configurable:!0}),a.ALWAYS=d.a.ALWAYS,a.KEEP=d.a.KEEP,a.REPLACE=d.a.REPLACE,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._blendFunctionParameters=new Array(4),this._blendEquationParameters=new Array(2),this._blendConstants=new Array(4),this._isBlendConstantsDirty=!1,this._alphaBlend=!1,this._isAlphaBlendDirty=!1,this._isBlendFunctionParametersDirty=!1,this._isBlendEquationParametersDirty=!1,this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isAlphaBlendDirty||this._isBlendFunctionParametersDirty||this._isBlendEquationParametersDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"alphaBlend",{get:function(){return this._alphaBlend},set:function(a){this._alphaBlend!==a&&(this._alphaBlend=a,this._isAlphaBlendDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.setAlphaBlendConstants=function(a,b,c,d){this._blendConstants[0]===a&&this._blendConstants[1]===b&&this._blendConstants[2]===c&&this._blendConstants[3]===d||(this._blendConstants[0]=a,this._blendConstants[1]=b,this._blendConstants[2]=c,this._blendConstants[3]=d,this._isBlendConstantsDirty=!0)},a.prototype.setAlphaBlendFunctionParameters=function(a,b,c,d){this._blendFunctionParameters[0]===a&&this._blendFunctionParameters[1]===b&&this._blendFunctionParameters[2]===c&&this._blendFunctionParameters[3]===d||(this._blendFunctionParameters[0]=a,this._blendFunctionParameters[1]=b,this._blendFunctionParameters[2]=c,this._blendFunctionParameters[3]=d,this._isBlendFunctionParametersDirty=!0)},a.prototype.setAlphaEquationParameters=function(a,b){this._blendEquationParameters[0]===a&&this._blendEquationParameters[1]===b||(this._blendEquationParameters[0]=a,this._blendEquationParameters[1]=b,this._isBlendEquationParametersDirty=!0)},a.prototype.reset=function(){this._alphaBlend=!1,this._blendFunctionParameters[0]=null,this._blendFunctionParameters[1]=null,this._blendFunctionParameters[2]=null,this._blendFunctionParameters[3]=null,this._blendEquationParameters[0]=null,this._blendEquationParameters[1]=null,this._blendConstants[0]=null,this._blendConstants[1]=null,this._blendConstants[2]=null,this._blendConstants[3]=null,this._isAlphaBlendDirty=!0,this._isBlendFunctionParametersDirty=!1,this._isBlendEquationParametersDirty=!1,this._isBlendConstantsDirty=!1},a.prototype.apply=function(a){this.isDirty&&(this._isAlphaBlendDirty&&(this._alphaBlend?a.enable(a.BLEND):a.disable(a.BLEND),this._isAlphaBlendDirty=!1),this._isBlendFunctionParametersDirty&&(a.blendFuncSeparate(this._blendFunctionParameters[0],this._blendFunctionParameters[1],this._blendFunctionParameters[2],this._blendFunctionParameters[3]),this._isBlendFunctionParametersDirty=!1),this._isBlendEquationParametersDirty&&(a.blendEquationSeparate(this._blendEquationParameters[0],this._blendEquationParameters[1]),this._isBlendEquationParametersDirty=!1),this._isBlendConstantsDirty&&(a.blendColor(this._blendConstants[0],this._blendConstants[1],this._blendConstants[2],this._blendConstants[3]),this._isBlendConstantsDirty=!1))},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._valueCache={},this.vertexCompilationError=null,this.fragmentCompilationError=null,this.programLinkError=null,this.programValidationError=null}return Object.defineProperty(a.prototype,"isAsync",{get:function(){return this.isParallelCompiled},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isReady",{get:function(){return!!this.program&&(!this.isParallelCompiled||this.engine._isRenderingStateCompiled(this))},enumerable:!1,configurable:!0}),a.prototype._handlesSpectorRebuildCallback=function(a){a&&this.program&&a(this.program)},a.prototype._fillEffectInformation=function(a,b,c,d,e,f,g,h){var i=this.engine;if(i.supportsUniformBuffers)for(var j in b)a.bindUniformBlock(j,b[j]);for(this.engine.getUniforms(this,c).forEach(function(a,b){d[c[b]]=a}),this._uniforms=d,j=0;jc._alphaIndex?1:b._alphaIndexb._distanceToCamera?-1:0},a.frontToBackSortCompare=function(a,b){return a._distanceToCamerab._distanceToCamera?1:0},a.prototype.prepare=function(){this._opaqueSubMeshes.reset(),this._transparentSubMeshes.reset(),this._alphaTestSubMeshes.reset(),this._depthOnlySubMeshes.reset(),this._particleSystems.reset(),this._spriteManagers.reset(),this._edgesRenderers.reset()},a.prototype.dispose=function(){this._opaqueSubMeshes.dispose(),this._transparentSubMeshes.dispose(),this._alphaTestSubMeshes.dispose(),this._depthOnlySubMeshes.dispose(),this._particleSystems.dispose(),this._spriteManagers.dispose(),this._edgesRenderers.dispose()},a.prototype.dispatch=function(a,b,c){void 0===b&&(b=a.getMesh()),void 0===c&&(c=a.getMaterial()),null!=c&&(c.needAlphaBlendingForMesh(b)?this._transparentSubMeshes.push(a):c.needAlphaTesting()?(c.needDepthPrePass&&this._depthOnlySubMeshes.push(a),this._alphaTestSubMeshes.push(a)):(c.needDepthPrePass&&this._depthOnlySubMeshes.push(a),this._opaqueSubMeshes.push(a)),b._renderingGroup=this,b._edgesRenderer&&b._edgesRenderer.isEnabled&&this._edgesRenderers.pushNoDuplicate(b._edgesRenderer))},a.prototype.dispatchSprites=function(a){this._spriteManagers.push(a)},a.prototype.dispatchParticles=function(a){this._particleSystems.push(a)},a.prototype._renderParticles=function(a){if(0!==this._particleSystems.length){var b=this._scene.activeCamera;this._scene.onBeforeParticlesRenderingObservable.notifyObservers(this._scene);for(var c=0;c=g.a.Xbox&&a<=g.a.Switch&&navigator.getGamepads&&this._updateDevice(a,b,c);d=d[c];if(void 0===d)throw"Unable to find input "+c+" for device "+g.a[a]+" in slot "+b;return d},a.prototype.isDeviceAvailable=function(a){return void 0!==this._inputs[a]},a.prototype.dispose=function(){this.onDeviceConnectedObservable.clear(),this.onDeviceDisconnectedObservable.clear(),this.onInputChangedObservable.clear(),this._elementToAttachTo&&(this._removeEvents(),window.removeEventListener("gamepadconnected",this._gamepadConnectedEvent),window.removeEventListener("gamepaddisconnected",this._gamepadDisconnectedEvent))},a.prototype._checkForConnectedDevices=function(){if(navigator.getGamepads)for(var a=0,b=navigator.getGamepads();a=0&&b._elementToAttachTo.hasPointerCapture(b._mouseId)?b._elementToAttachTo.releasePointerCapture(b._mouseId):a.pointerId&&b._elementToAttachTo.hasPointerCapture(a.pointerId)&&b._elementToAttachTo.releasePointerCapture(a.pointerId),b.onInputChangedObservable.notifyObservers(j),d!==g.a.Mouse){f=b._activeTouchIds.indexOf(a.pointerId);delete b._activeTouchIds[f],b._unregisterDevice(d,e)}}},this._wheelEventName="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll";var c=!1,d=function(){};try{var e={passive:{get:function(){c=!0}}};this._elementToAttachTo.addEventListener("test",d,e),this._elementToAttachTo.removeEventListener("test",d,e)}catch(a){}this._pointerBlurEvent=function(a){var c;if(b.isDeviceAvailable(g.a.Mouse)){a=b._inputs[g.a.Mouse][0];b._mouseId>=0&&b._elementToAttachTo.hasPointerCapture(b._mouseId)&&b._elementToAttachTo.releasePointerCapture(b._mouseId);for(var d=0;d<=g.c.BrowserForward;d++)1===a[d+2]&&(a[d+2]=0,(e=f.a.CreateDeviceEvent(g.a.Mouse,0,d+2,1,b,b._elementToAttachTo)).deviceType=g.a.Mouse,e.deviceSlot=0,e.inputIndex=d+2,e.currentState=a[d+2],e.previousState=1,b.onInputChangedObservable.notifyObservers(e))}if(b.isDeviceAvailable(g.a.Touch)){a=b._inputs[g.a.Touch];for(d in Object.keys(b._activeTouchIds)){var e,h=+d,i=b._activeTouchIds[h];(b._elementToAttachTo.hasPointerCapture(i)&&b._elementToAttachTo.releasePointerCapture(i),1===(null===(c=a[h])||void 0===c?void 0:c[g.c.LeftClick]))&&(a[h][g.c.LeftClick]=0,(e=f.a.CreateDeviceEvent(g.a.Touch,i,g.c.LeftClick,1,b,b._elementToAttachTo)).deviceType=g.a.Mouse,e.deviceSlot=h,e.inputIndex=g.c.LeftClick,e.currentState=a[h][g.c.LeftClick],e.previousState=1,b.onInputChangedObservable.notifyObservers(e),b._unregisterDevice(g.a.Touch,h))}for(;void 0!==b._activeTouchIds.pop(););}},this._pointerWheelEvent=function(c){var d=g.a.Mouse;b._inputs[d]||(b._inputs[d]=[]),b._inputs[d][0]||(b._pointerActive=!0,b._registerDevice(d,0,a.MAX_POINTER_INPUTS));var e=b._inputs[d][0];if(e){var f=e[g.c.MouseWheelX],h=e[g.c.MouseWheelY],i=e[g.c.MouseWheelZ];e[g.c.MouseWheelX]=c.deltaX||0,e[g.c.MouseWheelY]=c.deltaY||c.wheelDelta||0,e[g.c.MouseWheelZ]=c.deltaZ||0;c=c;c.deviceType=d,c.deviceSlot=0,0!==e[g.c.MouseWheelX]&&(c.inputIndex=g.c.MouseWheelX,c.previousState=f,c.currentState=e[g.c.MouseWheelX],b.onInputChangedObservable.notifyObservers(c)),0!==e[g.c.MouseWheelY]&&(c.inputIndex=g.c.MouseWheelY,c.previousState=h,c.currentState=e[g.c.MouseWheelY],b.onInputChangedObservable.notifyObservers(c)),0!==e[g.c.MouseWheelZ]&&(c.inputIndex=g.c.MouseWheelZ,c.previousState=i,c.currentState=e[g.c.MouseWheelZ],b.onInputChangedObservable.notifyObservers(c))}},this._elementToAttachTo.addEventListener(this._eventPrefix+"move",this._pointerMoveEvent),this._elementToAttachTo.addEventListener(this._eventPrefix+"down",this._pointerDownEvent),this._elementToAttachTo.addEventListener(this._eventPrefix+"up",this._pointerUpEvent),this._elementToAttachTo.addEventListener("blur",this._pointerBlurEvent),this._elementToAttachTo.addEventListener(this._wheelEventName,this._pointerWheelEvent,!!c&&{passive:!1}),this._pointerInputClearObserver=this._engine.onEndFrameObservable.add(function(){if(b.isDeviceAvailable(g.a.Mouse)){var a=b._inputs[g.a.Mouse][0];a[g.c.MouseWheelX]=0,a[g.c.MouseWheelY]=0,a[g.c.MouseWheelZ]=0,a[g.c.DeltaHorizontal]=0,a[g.c.DeltaVertical]=0}})},a.prototype._handleGamepadActions=function(){var a=this;this._gamepadConnectedEvent=function(b){a._addGamePad(b.gamepad)},this._gamepadDisconnectedEvent=function(b){if(a._gamepads){var c=a._getGamepadDeviceType(b.gamepad.id);b=b.gamepad.index;a._unregisterDevice(c,b),delete a._gamepads[b]}},window.addEventListener("gamepadconnected",this._gamepadConnectedEvent),window.addEventListener("gamepaddisconnected",this._gamepadDisconnectedEvent)},a.prototype._updateDevice=function(a,b,c){var d=navigator.getGamepads()[b];if(d&&a===this._gamepads[b]){a=this._inputs[a][b];c>=d.buttons.length?a[c]=d.axes[c-d.buttons.length].valueOf():a[c]=d.buttons[c].value}},a.prototype._getGamepadDeviceType=function(a){return-1!==a.indexOf("054c")&&-1===a.indexOf("0ce6")?g.a.DualShock:-1!==a.indexOf("Xbox One")||-1!==a.search("Xbox 360")||-1!==a.search("xinput")?g.a.Xbox:-1!==a.indexOf("057e")?g.a.Switch:g.a.Generic},a.prototype._getPointerType=function(a){var b=g.a.Mouse;return("touch"===a.pointerType||"pen"===a.pointerType||a.touches)&&(b=g.a.Touch),b},a.prototype._removeEvents=function(){this._elementToAttachTo.removeEventListener("blur",this._keyboardBlurEvent),this._elementToAttachTo.removeEventListener("blur",this._pointerBlurEvent),this._keyboardActive&&(this._elementToAttachTo.removeEventListener("keydown",this._keyboardDownEvent),this._elementToAttachTo.removeEventListener("keyup",this._keyboardUpEvent)),this._pointerActive&&(this._elementToAttachTo.removeEventListener(this._eventPrefix+"move",this._pointerMoveEvent),this._elementToAttachTo.removeEventListener(this._eventPrefix+"down",this._pointerDownEvent),this._elementToAttachTo.removeEventListener(this._eventPrefix+"up",this._pointerUpEvent),this._elementToAttachTo.removeEventListener(this._wheelEventName,this._pointerWheelEvent),this._pointerInputClearObserver&&this._engine.onEndFrameObservable.remove(this._pointerInputClearObserver))},a.MAX_KEYCODES=255,a.MAX_POINTER_INPUTS=Object.keys(g.c).length/2,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a){a.prototype.setActiveCameraByID=function(a){return this.setActiveCameraById(a)},a.prototype.getLastMaterialByID=function(a){return this.getLastMaterialById(a)},a.prototype.getMaterialByID=function(a){return this.getMaterialById(a)},a.prototype.getTextureByUniqueID=function(a){return this.getTextureByUniqueId(a)},a.prototype.getCameraByID=function(a){return this.getCameraById(a)},a.prototype.getCameraByUniqueID=function(a){return this.getCameraByUniqueId(a)},a.prototype.getBoneByID=function(a){return this.getBoneById(a)},a.prototype.getLightByID=function(a){return this.getLightById(a)},a.prototype.getLightByUniqueID=function(a){return this.getLightByUniqueId(a)},a.prototype.getParticleSystemByID=function(a){return this.getParticleSystemById(a)},a.prototype.getGeometryByID=function(a){return this.getGeometryById(a)},a.prototype.getMeshByID=function(a){return this.getMeshById(a)},a.prototype.getMeshesByID=function(a){return this.getMeshesById(a)},a.prototype.getTransformNodeByID=function(a){return this.getTransformNodeById(a)},a.prototype.getTransformNodeByUniqueID=function(a){return this.getTransformNodeByUniqueId(a)},a.prototype.getTransformNodesByID=function(a){return this.getTransformNodesById(a)},a.prototype.getMeshByUniqueID=function(a){return this.getMeshByUniqueId(a)},a.prototype.getLastMeshByID=function(a){return this.getLastMeshById(a)},a.prototype.getLastEntryByID=function(a){return this.getLastEntryById(a)},a.prototype.getNodeByID=function(a){return this.getNodeById(a)},a.prototype.getLastSkeletonByID=function(a){return this.getLastSkeletonById(a)}}},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f});var d=c(34),e=function(){function a(a){void 0===a&&(a=30),this._enabled=!0,this._rollingFrameTime=new f(a)}return a.prototype.sampleFrame=function(a){if(void 0===a&&(a=d.a.Now),this._enabled){if(null!=this._lastFrameTimeMs){var b=a-this._lastFrameTimeMs;this._rollingFrameTime.add(b)}this._lastFrameTimeMs=a}},Object.defineProperty(a.prototype,"averageFrameTime",{get:function(){return this._rollingFrameTime.average},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"averageFrameTimeVariance",{get:function(){return this._rollingFrameTime.variance},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"instantaneousFrameTime",{get:function(){return this._rollingFrameTime.history(0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"averageFPS",{get:function(){return 1e3/this._rollingFrameTime.average},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"instantaneousFPS",{get:function(){var a=this._rollingFrameTime.history(0);return 0===a?0:1e3/a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSaturated",{get:function(){return this._rollingFrameTime.isSaturated()},enumerable:!1,configurable:!0}),a.prototype.enable=function(){this._enabled=!0},a.prototype.disable=function(){this._enabled=!1,this._lastFrameTimeMs=null},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._enabled},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this._lastFrameTimeMs=null,this._rollingFrameTime.reset()},a}(),f=function(){function a(a){this._samples=new Array(a),this.reset()}return a.prototype.add=function(a){var b;if(this.isSaturated()){var c=this._samples[this._pos];b=c-this.average,this.average-=b/(this._sampleCount-1),this._m2-=b*(c-this.average)}else this._sampleCount++;b=a-this.average,this.average+=b/this._sampleCount,this._m2+=b*(a-this.average),this.variance=this._m2/(this._sampleCount-1),this._samples[this._pos]=a,this._pos++,this._pos%=this._samples.length},a.prototype.history=function(a){if(a>=this._sampleCount||a>=this._samples.length)return 0;var b=this._wrapPosition(this._pos-1);return this._samples[this._wrapPosition(b-a)]},a.prototype.isSaturated=function(){return this._sampleCount>=this._samples.length},a.prototype.reset=function(){this.average=0,this.variance=0,this._sampleCount=0,this._pos=0,this._m2=0},a.prototype._wrapPosition=function(a){var b=this._samples.length;return(a%b+b)%b},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(0),e=function(){this._checkCollisions=!1,this._collisionMask=-1,this._collisionGroup=-1,this._surroundingMeshes=null,this._collider=null,this._oldPositionForCollisions=new d.e(0,0,0),this._diffPositionForCollisions=new d.e(0,0,0),this._collisionResponse=!0}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a,b){this.distanceOrScreenCoverage=a,this.mesh=b}},function(a,b,c){c.d(b,"a",function(){return e});var d=c(26),e=function(a){a.prototype.setMaterialByID=function(a){return this.setMaterialById(a)},a.CreateDisc=a.CreateDisc||function(){throw Object(d.a)("MeshBuilder")},a.CreateBox=a.CreateBox||function(){throw Object(d.a)("MeshBuilder")},a.CreateSphere=a.CreateSphere||function(){throw Object(d.a)("MeshBuilder")},a.CreateCylinder=a.CreateCylinder||function(){throw Object(d.a)("MeshBuilder")},a.CreateTorusKnot=a.CreateTorusKnot||function(){throw Object(d.a)("MeshBuilder")},a.CreateTorus=a.CreateTorus||function(){throw Object(d.a)("MeshBuilder")},a.CreatePlane=a.CreatePlane||function(){throw Object(d.a)("MeshBuilder")},a.CreateGround=a.CreateGround||function(){throw Object(d.a)("MeshBuilder")},a.CreateTiledGround=a.CreateTiledGround||function(){throw Object(d.a)("MeshBuilder")},a.CreateGroundFromHeightMap=a.CreateGroundFromHeightMap||function(){throw Object(d.a)("MeshBuilder")},a.CreateTube=a.CreateTube||function(){throw Object(d.a)("MeshBuilder")},a.CreatePolyhedron=a.CreatePolyhedron||function(){throw Object(d.a)("MeshBuilder")},a.CreateIcoSphere=a.CreateIcoSphere||function(){throw Object(d.a)("MeshBuilder")},a.CreateDecal=a.CreateDecal||function(){throw Object(d.a)("MeshBuilder")},a.CreateCapsule=a.CreateCapsule||function(){throw Object(d.a)("MeshBuilder")},a.ExtendToGoldberg=a.ExtendToGoldberg||function(){throw Object(d.a)("MeshBuilder")}}},function(a,b,c){c.d(b,"a",function(){return k});var d=c(2),e=c(0),f=c(7);a=c(33);b=c(9);var g=c(47),h=c(46),i=c(4),j=c(12);b.a._instancedMeshFactory=function(a,b){a=new k(a,b);if(b.instancedBuffers)for(var c in a.instancedBuffers={},b.instancedBuffers)a.instancedBuffers[c]=b.instancedBuffers[c];return a};var k=function(a){function b(b,c){b=a.call(this,b,c.getScene())||this;b._indexInSourceMeshInstanceArray=-1,b._distanceToCamera=0,c.addInstance(b),b._sourceMesh=c,b._unIndexed=c._unIndexed,b.position.copyFrom(c.position),b.rotation.copyFrom(c.rotation),b.scaling.copyFrom(c.scaling),c.rotationQuaternion&&(b.rotationQuaternion=c.rotationQuaternion.clone()),b.animations=j.b.Slice(c.animations);for(var d=0,e=c.getAnimationRanges();d0!=this._getWorldMatrixDeterminant()>0)return this._internalAbstractMeshDataInfo._actAsRegularMesh=!0,!0;if(this._internalAbstractMeshDataInfo._actAsRegularMesh=!1,this._currentLOD._registerInstanceForRenderId(this,a),b){if(!this._currentLOD._internalAbstractMeshDataInfo._isActiveIntermediate)return this._currentLOD._internalAbstractMeshDataInfo._onlyForInstancesIntermediate=!0,!0}else if(!this._currentLOD._internalAbstractMeshDataInfo._isActive)return this._currentLOD._internalAbstractMeshDataInfo._onlyForInstances=!0,!0}return!1},b.prototype._postActivate=function(){this._sourceMesh.edgesShareWithInstances&&this._sourceMesh._edgesRenderer&&this._sourceMesh._edgesRenderer.isEnabled&&this._sourceMesh._renderingGroup?(this._sourceMesh._renderingGroup._edgesRenderers.pushNoDuplicate(this._sourceMesh._edgesRenderer),this._sourceMesh._edgesRenderer.customInstances.push(this.getWorldMatrix())):this._edgesRenderer&&this._edgesRenderer.isEnabled&&this._sourceMesh._renderingGroup&&this._sourceMesh._renderingGroup._edgesRenderers.push(this._edgesRenderer)},b.prototype.getWorldMatrix=function(){if(this._currentLOD&&this._currentLOD.billboardMode!==h.a.BILLBOARDMODE_NONE&&this._currentLOD._masterMesh!==this){this._billboardWorldMatrix||(this._billboardWorldMatrix=new e.a);var b=this._currentLOD._masterMesh;return this._currentLOD._masterMesh=this,e.c.Vector3[7].copyFrom(this._currentLOD.position),this._currentLOD.position.set(0,0,0),this._billboardWorldMatrix.copyFrom(this._currentLOD.computeWorldMatrix(!0)),this._currentLOD.position.copyFrom(e.c.Vector3[7]),this._currentLOD._masterMesh=b,this._billboardWorldMatrix}return a.prototype.getWorldMatrix.call(this)},Object.defineProperty(b.prototype,"isAnInstance",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype.getLOD=function(a){if(!a)return this;var b=this.getBoundingInfo();return this._currentLOD=this.sourceMesh.getLOD(a,b.boundingSphere),this._currentLOD===this.sourceMesh?this.sourceMesh:this._currentLOD},b.prototype._preActivateForIntermediateRendering=function(a){return this.sourceMesh._preActivateForIntermediateRendering(a)},b.prototype._syncSubMeshes=function(){if(this.releaseSubMeshes(),this._sourceMesh.subMeshes)for(var a=0;a=lightDirection.w) { cosAngle=max(0.,pow(cosAngle,lightData.w)); attenuation*=cosAngle; float ndl=max(0.,dot(vNormal,lightVectorW)); #ifdef NDOTL result.ndl=ndl; #endif result.diffuse=ndl*diffuseColor*attenuation; #ifdef SPECULARTERM vec3 angleW=normalize(viewDirectionW+lightVectorW); float specComp=max(0.,dot(vNormal,angleW)); specComp=pow(specComp,max(1.,glossiness)); result.specular=specComp*specularColor*attenuation; #endif return result; } result.diffuse=vec3(0.); #ifdef SPECULARTERM result.specular=vec3(0.); #endif #ifdef NDOTL result.ndl=0.; #endif return result; } lightingInfo computeHemisphericLighting(vec3 viewDirectionW,vec3 vNormal,vec4 lightData,vec3 diffuseColor,vec3 specularColor,vec3 groundColor,float glossiness) { lightingInfo result; float ndl=dot(vNormal,lightData.xyz)*0.5+0.5; #ifdef NDOTL result.ndl=ndl; #endif result.diffuse=mix(groundColor,diffuseColor,ndl); #ifdef SPECULARTERM vec3 angleW=normalize(viewDirectionW+lightData.xyz); float specComp=max(0.,dot(vNormal,angleW)); specComp=pow(specComp,max(1.,glossiness)); result.specular=specComp*specularColor; #endif return result; } #define inline vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler,mat4 textureProjectionMatrix){ vec4 strq=textureProjectionMatrix*vec4(vPositionW,1.0); strq/=strq.w; vec3 textureColor=texture2D(projectionLightSampler,strq.xy).rgb; return textureColor; }";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="samplerFragmentDeclaration";b="#ifdef _DEFINENAME_ #if _DEFINENAME_DIRECTUV == 1 #define v_VARYINGNAME_UV vMainUV1 #elif _DEFINENAME_DIRECTUV == 2 #define v_VARYINGNAME_UV vMainUV2 #elif _DEFINENAME_DIRECTUV == 3 #define v_VARYINGNAME_UV vMainUV3 #elif _DEFINENAME_DIRECTUV == 4 #define v_VARYINGNAME_UV vMainUV4 #elif _DEFINENAME_DIRECTUV == 5 #define v_VARYINGNAME_UV vMainUV5 #elif _DEFINENAME_DIRECTUV == 6 #define v_VARYINGNAME_UV vMainUV6 #else varying vec2 v_VARYINGNAME_UV; #endif uniform sampler2D _SAMPLERNAME_Sampler; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="logDepthFragment";b="#ifdef LOGARITHMICDEPTH gl_FragDepthEXT=log2(vFragmentDepth)*logarithmicDepthConstant*0.5; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#if defined(BUMP) || defined(PARALLAX) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) #if defined(TANGENT) && defined(NORMAL) vec3 tbnNormal=normalize(normalUpdated); vec3 tbnTangent=normalize(tangentUpdated.xyz); vec3 tbnBitangent=cross(tbnNormal,tbnTangent)*tangentUpdated.w; vTBN=mat3(finalWorld)*mat3(tbnTangent,tbnBitangent,tbnNormal); #endif #endif";c(5).a.IncludesShadersStore.bumpVertex=a},function(a,b,c){a="#ifdef FOG vFogDistance=(view*worldPos).xyz; #endif";c(5).a.IncludesShadersStore.fogVertex=a},function(a,b,c){a="logDepthVertex";b="#ifdef LOGARITHMICDEPTH vFragmentDepth=1.0+gl_Position.w; gl_Position.z=log2(max(0.000001,vFragmentDepth))*logarithmicDepthConstant; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a=c(5);b=(c(122),c(109),"colorPixelShader");c="#ifdef VERTEXCOLOR varying vec4 vColor; #else uniform vec4 color; #endif #include void main(void) { #include #ifdef VERTEXCOLOR gl_FragColor=vColor; #else gl_FragColor=color; #endif }";a.a.ShadersStore[b]=c},function(a,b,c){a=c(5);b=(c(87),c(123),c(92),c(88),c(89),c(112),"colorVertexShader");c=" attribute vec3 position; #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include uniform mat4 viewProjection; #ifdef MULTIVIEW uniform mat4 viewProjectionR; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif void main(void) { #include #include vec4 worldPos=finalWorld*vec4(position,1.0); #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif #include #ifdef VERTEXCOLOR vColor=color; #endif }";a.a.ShadersStore[b]=c},function(a,b){b=function(){return this}();try{b=b||new Function("return this")()}catch(a){"object"==typeof window&&(b=window)}a.exports=b},function(a,b,c){a="prePassDeclaration";b="#ifdef PREPASS #extension GL_EXT_draw_buffers : require layout(location=0) out highp vec4 glFragData[{X}]; highp vec4 gl_FragColor; #ifdef PREPASS_DEPTH varying highp vec3 vViewPos; #endif #ifdef PREPASS_VELOCITY varying highp vec4 vCurrentPosition; varying highp vec4 vPreviousPosition; #endif #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="oitDeclaration";b="#ifdef ORDER_INDEPENDENT_TRANSPARENCY #extension GL_EXT_draw_buffers : require layout(location=0) out vec2 depth; layout(location=1) out vec4 frontColor; layout(location=2) out vec4 backColor; #define MAX_DEPTH 99999.0 highp vec4 gl_FragColor; uniform sampler2D oitDepthSampler; uniform sampler2D oitFrontColorSampler; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="fresnelFunction";b="#ifdef FRESNEL float computeFresnelTerm(vec3 viewDirection,vec3 worldNormal,float bias,float power) { float fresnelTerm=pow(bias+abs(dot(viewDirection,worldNormal)),power); return clamp(fresnelTerm,0.,1.); } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#ifdef ORDER_INDEPENDENT_TRANSPARENCY float fragDepth=gl_FragCoord.z; #ifdef USE_REVERSE_DEPTHBUFFER fragDepth=1.0-fragDepth; #endif ivec2 fragCoord=ivec2(gl_FragCoord.xy); vec2 lastDepth=texelFetch(oitDepthSampler,fragCoord,0).rg; vec4 lastFrontColor=texelFetch(oitFrontColorSampler,fragCoord,0); depth.rg=vec2(-MAX_DEPTH); frontColor=lastFrontColor; backColor=vec4(0.0); float nearestDepth=-lastDepth.x; float furthestDepth=lastDepth.y; float alphaMultiplier=1.0-lastFrontColor.a; if (fragDepthfurthestDepth) { return; } if (fragDepth>nearestDepth && fragDepth0 mat4 previousInfluence; previousInfluence=mPreviousBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 previousInfluence+=mPreviousBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 previousInfluence+=mPreviousBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 previousInfluence+=mPreviousBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif vPreviousPosition=previousViewProjection*previousWorld*previousInfluence*vec4(positionUpdated,1.0); #else vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="uvVariableDeclaration";b="#if !defined(UV{X}) && defined(MAINUV{X}) vec2 uv{X}=vec2(0.,0.); #endif #ifdef MAINUV{X} vMainUV{X}=uv{X}; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="samplerVertexImplementation";b="#if defined(_DEFINENAME_) && _DEFINENAME_DIRECTUV == 0 if (v_INFONAME_ == 0.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uvUpdated,1.0,0.0)); } #ifdef UV2 else if (v_INFONAME_ == 1.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv2,1.0,0.0)); } #endif #ifdef UV3 else if (v_INFONAME_ == 2.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv3,1.0,0.0)); } #endif #ifdef UV4 else if (v_INFONAME_ == 3.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv4,1.0,0.0)); } #endif #ifdef UV5 else if (v_INFONAME_ == 4.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv5,1.0,0.0)); } #endif #ifdef UV6 else if (v_INFONAME_ == 5.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv6,1.0,0.0)); } #endif #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.r(b),(function(a){c.d(b,"Debug",function(){return h});var d=c(148),e=c(108);c.d(b,"AbstractScene",function(){return d.AbstractScene}),c.d(b,"AbstractActionManager",function(){return d.AbstractActionManager}),c.d(b,"Action",function(){return d.Action}),c.d(b,"ActionEvent",function(){return d.ActionEvent}),c.d(b,"ActionManager",function(){return d.ActionManager}),c.d(b,"Condition",function(){return d.Condition}),c.d(b,"ValueCondition",function(){return d.ValueCondition}),c.d(b,"PredicateCondition",function(){return d.PredicateCondition}),c.d(b,"StateCondition",function(){return d.StateCondition}),c.d(b,"SwitchBooleanAction",function(){return d.SwitchBooleanAction}),c.d(b,"SetStateAction",function(){return d.SetStateAction}),c.d(b,"SetValueAction",function(){return d.SetValueAction}),c.d(b,"IncrementValueAction",function(){return d.IncrementValueAction}),c.d(b,"PlayAnimationAction",function(){return d.PlayAnimationAction}),c.d(b,"StopAnimationAction",function(){return d.StopAnimationAction}),c.d(b,"DoNothingAction",function(){return d.DoNothingAction}),c.d(b,"CombineAction",function(){return d.CombineAction}),c.d(b,"ExecuteCodeAction",function(){return d.ExecuteCodeAction}),c.d(b,"SetParentAction",function(){return d.SetParentAction}),c.d(b,"PlaySoundAction",function(){return d.PlaySoundAction}),c.d(b,"StopSoundAction",function(){return d.StopSoundAction}),c.d(b,"InterpolateValueAction",function(){return d.InterpolateValueAction}),c.d(b,"Animatable",function(){return d.Animatable}),c.d(b,"_IAnimationState",function(){return d._IAnimationState}),c.d(b,"Animation",function(){return d.Animation}),c.d(b,"TargetedAnimation",function(){return d.TargetedAnimation}),c.d(b,"AnimationGroup",function(){return d.AnimationGroup}),c.d(b,"AnimationPropertiesOverride",function(){return d.AnimationPropertiesOverride}),c.d(b,"EasingFunction",function(){return d.EasingFunction}),c.d(b,"CircleEase",function(){return d.CircleEase}),c.d(b,"BackEase",function(){return d.BackEase}),c.d(b,"BounceEase",function(){return d.BounceEase}),c.d(b,"CubicEase",function(){return d.CubicEase}),c.d(b,"ElasticEase",function(){return d.ElasticEase}),c.d(b,"ExponentialEase",function(){return d.ExponentialEase}),c.d(b,"PowerEase",function(){return d.PowerEase}),c.d(b,"QuadraticEase",function(){return d.QuadraticEase}),c.d(b,"QuarticEase",function(){return d.QuarticEase}),c.d(b,"QuinticEase",function(){return d.QuinticEase}),c.d(b,"SineEase",function(){return d.SineEase}),c.d(b,"BezierCurveEase",function(){return d.BezierCurveEase}),c.d(b,"RuntimeAnimation",function(){return d.RuntimeAnimation}),c.d(b,"AnimationEvent",function(){return d.AnimationEvent}),c.d(b,"AnimationKeyInterpolation",function(){return d.AnimationKeyInterpolation}),c.d(b,"AnimationRange",function(){return d.AnimationRange}),c.d(b,"PathCursor",function(){return d.PathCursor}),c.d(b,"KeepAssets",function(){return d.KeepAssets}),c.d(b,"InstantiatedEntries",function(){return d.InstantiatedEntries}),c.d(b,"AssetContainer",function(){return d.AssetContainer}),c.d(b,"Analyser",function(){return d.Analyser}),c.d(b,"AudioEngine",function(){return d.AudioEngine}),c.d(b,"AudioSceneComponent",function(){return d.AudioSceneComponent}),c.d(b,"Sound",function(){return d.Sound}),c.d(b,"SoundTrack",function(){return d.SoundTrack}),c.d(b,"WeightedSound",function(){return d.WeightedSound}),c.d(b,"AutoRotationBehavior",function(){return d.AutoRotationBehavior}),c.d(b,"BouncingBehavior",function(){return d.BouncingBehavior}),c.d(b,"FramingBehavior",function(){return d.FramingBehavior}),c.d(b,"AttachToBoxBehavior",function(){return d.AttachToBoxBehavior}),c.d(b,"FadeInOutBehavior",function(){return d.FadeInOutBehavior}),c.d(b,"MultiPointerScaleBehavior",function(){return d.MultiPointerScaleBehavior}),c.d(b,"PointerDragBehavior",function(){return d.PointerDragBehavior}),c.d(b,"SixDofDragBehavior",function(){return d.SixDofDragBehavior}),c.d(b,"SurfaceMagnetismBehavior",function(){return d.SurfaceMagnetismBehavior}),c.d(b,"BaseSixDofDragBehavior",function(){return d.BaseSixDofDragBehavior}),c.d(b,"FollowBehavior",function(){return d.FollowBehavior}),c.d(b,"HandConstraintZone",function(){return d.HandConstraintZone}),c.d(b,"HandConstraintOrientation",function(){return d.HandConstraintOrientation}),c.d(b,"HandConstraintVisibility",function(){return d.HandConstraintVisibility}),c.d(b,"HandConstraintBehavior",function(){return d.HandConstraintBehavior}),c.d(b,"Bone",function(){return d.Bone}),c.d(b,"BoneIKController",function(){return d.BoneIKController}),c.d(b,"BoneLookController",function(){return d.BoneLookController}),c.d(b,"Skeleton",function(){return d.Skeleton}),c.d(b,"Buffer",function(){return d.Buffer}),c.d(b,"VertexBuffer",function(){return d.VertexBuffer}),c.d(b,"DataBuffer",function(){return d.DataBuffer}),c.d(b,"StorageBuffer",function(){return d.StorageBuffer}),c.d(b,"BaseCameraMouseWheelInput",function(){return d.BaseCameraMouseWheelInput}),c.d(b,"BaseCameraPointersInput",function(){return d.BaseCameraPointersInput}),c.d(b,"ArcRotateCameraGamepadInput",function(){return d.ArcRotateCameraGamepadInput}),c.d(b,"ArcRotateCameraKeyboardMoveInput",function(){return d.ArcRotateCameraKeyboardMoveInput}),c.d(b,"ArcRotateCameraMouseWheelInput",function(){return d.ArcRotateCameraMouseWheelInput}),c.d(b,"ArcRotateCameraPointersInput",function(){return d.ArcRotateCameraPointersInput}),c.d(b,"ArcRotateCameraVRDeviceOrientationInput",function(){return d.ArcRotateCameraVRDeviceOrientationInput}),c.d(b,"FlyCameraKeyboardInput",function(){return d.FlyCameraKeyboardInput}),c.d(b,"FlyCameraMouseInput",function(){return d.FlyCameraMouseInput}),c.d(b,"FollowCameraKeyboardMoveInput",function(){return d.FollowCameraKeyboardMoveInput}),c.d(b,"FollowCameraMouseWheelInput",function(){return d.FollowCameraMouseWheelInput}),c.d(b,"FollowCameraPointersInput",function(){return d.FollowCameraPointersInput}),c.d(b,"FreeCameraDeviceOrientationInput",function(){return d.FreeCameraDeviceOrientationInput}),c.d(b,"FreeCameraGamepadInput",function(){return d.FreeCameraGamepadInput}),c.d(b,"FreeCameraKeyboardMoveInput",function(){return d.FreeCameraKeyboardMoveInput}),c.d(b,"FreeCameraMouseInput",function(){return d.FreeCameraMouseInput}),c.d(b,"FreeCameraMouseWheelInput",function(){return d.FreeCameraMouseWheelInput}),c.d(b,"FreeCameraTouchInput",function(){return d.FreeCameraTouchInput}),c.d(b,"FreeCameraVirtualJoystickInput",function(){return d.FreeCameraVirtualJoystickInput}),c.d(b,"CameraInputTypes",function(){return d.CameraInputTypes}),c.d(b,"CameraInputsManager",function(){return d.CameraInputsManager}),c.d(b,"Camera",function(){return d.Camera}),c.d(b,"TargetCamera",function(){return d.TargetCamera}),c.d(b,"FreeCamera",function(){return d.FreeCamera}),c.d(b,"FreeCameraInputsManager",function(){return d.FreeCameraInputsManager}),c.d(b,"TouchCamera",function(){return d.TouchCamera}),c.d(b,"ArcRotateCamera",function(){return d.ArcRotateCamera}),c.d(b,"ArcRotateCameraInputsManager",function(){return d.ArcRotateCameraInputsManager}),c.d(b,"DeviceOrientationCamera",function(){return d.DeviceOrientationCamera}),c.d(b,"FlyCamera",function(){return d.FlyCamera}),c.d(b,"FlyCameraInputsManager",function(){return d.FlyCameraInputsManager}),c.d(b,"FollowCamera",function(){return d.FollowCamera}),c.d(b,"ArcFollowCamera",function(){return d.ArcFollowCamera}),c.d(b,"FollowCameraInputsManager",function(){return d.FollowCameraInputsManager}),c.d(b,"GamepadCamera",function(){return d.GamepadCamera}),c.d(b,"AnaglyphArcRotateCamera",function(){return d.AnaglyphArcRotateCamera}),c.d(b,"AnaglyphFreeCamera",function(){return d.AnaglyphFreeCamera}),c.d(b,"AnaglyphGamepadCamera",function(){return d.AnaglyphGamepadCamera}),c.d(b,"AnaglyphUniversalCamera",function(){return d.AnaglyphUniversalCamera}),c.d(b,"StereoscopicArcRotateCamera",function(){return d.StereoscopicArcRotateCamera}),c.d(b,"StereoscopicFreeCamera",function(){return d.StereoscopicFreeCamera}),c.d(b,"StereoscopicGamepadCamera",function(){return d.StereoscopicGamepadCamera}),c.d(b,"StereoscopicUniversalCamera",function(){return d.StereoscopicUniversalCamera}),c.d(b,"StereoscopicScreenUniversalCamera",function(){return d.StereoscopicScreenUniversalCamera}),c.d(b,"UniversalCamera",function(){return d.UniversalCamera}),c.d(b,"VirtualJoysticksCamera",function(){return d.VirtualJoysticksCamera}),c.d(b,"VRCameraMetrics",function(){return d.VRCameraMetrics}),c.d(b,"VRDeviceOrientationArcRotateCamera",function(){return d.VRDeviceOrientationArcRotateCamera}),c.d(b,"VRDeviceOrientationFreeCamera",function(){return d.VRDeviceOrientationFreeCamera}),c.d(b,"VRDeviceOrientationGamepadCamera",function(){return d.VRDeviceOrientationGamepadCamera}),c.d(b,"OnAfterEnteringVRObservableEvent",function(){return d.OnAfterEnteringVRObservableEvent}),c.d(b,"VRExperienceHelper",function(){return d.VRExperienceHelper}),c.d(b,"WebVRFreeCamera",function(){return d.WebVRFreeCamera}),c.d(b,"setStereoscopicAnaglyphRigMode",function(){return d.setStereoscopicAnaglyphRigMode}),c.d(b,"setStereoscopicRigMode",function(){return d.setStereoscopicRigMode}),c.d(b,"setVRRigMode",function(){return d.setVRRigMode}),c.d(b,"setWebVRRigMode",function(){return d.setWebVRRigMode}),c.d(b,"Collider",function(){return d.Collider}),c.d(b,"DefaultCollisionCoordinator",function(){return d.DefaultCollisionCoordinator}),c.d(b,"PickingInfo",function(){return d.PickingInfo}),c.d(b,"IntersectionInfo",function(){return d.IntersectionInfo}),c.d(b,"_MeshCollisionData",function(){return d._MeshCollisionData}),c.d(b,"ComputeEffect",function(){return d.ComputeEffect}),c.d(b,"ComputeShader",function(){return d.ComputeShader}),c.d(b,"BoundingBox",function(){return d.BoundingBox}),c.d(b,"BoundingInfo",function(){return d.BoundingInfo}),c.d(b,"BoundingSphere",function(){return d.BoundingSphere}),c.d(b,"Octree",function(){return d.Octree}),c.d(b,"OctreeBlock",function(){return d.OctreeBlock}),c.d(b,"OctreeSceneComponent",function(){return d.OctreeSceneComponent}),c.d(b,"Ray",function(){return d.Ray}),c.d(b,"AxesViewer",function(){return d.AxesViewer}),c.d(b,"BoneAxesViewer",function(){return d.BoneAxesViewer}),c.d(b,"DebugLayerTab",function(){return d.DebugLayerTab}),c.d(b,"DebugLayer",function(){return d.DebugLayer}),c.d(b,"PhysicsViewer",function(){return d.PhysicsViewer}),c.d(b,"RayHelper",function(){return d.RayHelper}),c.d(b,"SkeletonViewer",function(){return d.SkeletonViewer}),c.d(b,"DirectionalLightFrustumViewer",function(){return d.DirectionalLightFrustumViewer}),c.d(b,"DeviceInputSystem",function(){return d.DeviceInputSystem}),c.d(b,"DeviceType",function(){return d.DeviceType}),c.d(b,"PointerInput",function(){return d.PointerInput}),c.d(b,"DualShockInput",function(){return d.DualShockInput}),c.d(b,"XboxInput",function(){return d.XboxInput}),c.d(b,"SwitchInput",function(){return d.SwitchInput}),c.d(b,"DeviceEventFactory",function(){return d.DeviceEventFactory}),c.d(b,"NativeDeviceInputWrapper",function(){return d.NativeDeviceInputWrapper}),c.d(b,"WebDeviceInputSystem",function(){return d.WebDeviceInputSystem}),c.d(b,"DeviceSource",function(){return d.DeviceSource}),c.d(b,"DeviceSourceManager",function(){return d.DeviceSourceManager}),c.d(b,"Constants",function(){return d.Constants}),c.d(b,"ThinEngine",function(){return d.ThinEngine}),c.d(b,"Engine",function(){return d.Engine}),c.d(b,"EngineStore",function(){return d.EngineStore}),c.d(b,"NullEngineOptions",function(){return d.NullEngineOptions}),c.d(b,"NullEngine",function(){return d.NullEngine}),c.d(b,"_OcclusionDataStorage",function(){return d._OcclusionDataStorage}),c.d(b,"_forceTransformFeedbackToBundle",function(){return d._forceTransformFeedbackToBundle}),c.d(b,"EngineView",function(){return d.EngineView}),c.d(b,"allocateAndCopyTypedBuffer",function(){return d.allocateAndCopyTypedBuffer}),c.d(b,"ComputeBindingType",function(){return d.ComputeBindingType}),c.d(b,"NativeDataStream",function(){return d.NativeDataStream}),c.d(b,"WebGLPipelineContext",function(){return d.WebGLPipelineContext}),c.d(b,"WebGLHardwareTexture",function(){return d.WebGLHardwareTexture}),c.d(b,"PowerPreference",function(){return d.PowerPreference}),c.d(b,"FeatureName",function(){return d.FeatureName}),c.d(b,"BufferUsage",function(){return d.BufferUsage}),c.d(b,"MapMode",function(){return d.MapMode}),c.d(b,"TextureDimension",function(){return d.TextureDimension}),c.d(b,"TextureUsage",function(){return d.TextureUsage}),c.d(b,"TextureViewDimension",function(){return d.TextureViewDimension}),c.d(b,"TextureAspect",function(){return d.TextureAspect}),c.d(b,"TextureFormat",function(){return d.TextureFormat}),c.d(b,"AddressMode",function(){return d.AddressMode}),c.d(b,"FilterMode",function(){return d.FilterMode}),c.d(b,"CompareFunction",function(){return d.CompareFunction}),c.d(b,"ShaderStage",function(){return d.ShaderStage}),c.d(b,"BufferBindingType",function(){return d.BufferBindingType}),c.d(b,"SamplerBindingType",function(){return d.SamplerBindingType}),c.d(b,"TextureSampleType",function(){return d.TextureSampleType}),c.d(b,"StorageTextureAccess",function(){return d.StorageTextureAccess}),c.d(b,"CompilationMessageType",function(){return d.CompilationMessageType}),c.d(b,"PrimitiveTopology",function(){return d.PrimitiveTopology}),c.d(b,"FrontFace",function(){return d.FrontFace}),c.d(b,"CullMode",function(){return d.CullMode}),c.d(b,"ColorWrite",function(){return d.ColorWrite}),c.d(b,"BlendFactor",function(){return d.BlendFactor}),c.d(b,"BlendOperation",function(){return d.BlendOperation}),c.d(b,"StencilOperation",function(){return d.StencilOperation}),c.d(b,"IndexFormat",function(){return d.IndexFormat}),c.d(b,"VertexFormat",function(){return d.VertexFormat}),c.d(b,"InputStepMode",function(){return d.InputStepMode}),c.d(b,"LoadOp",function(){return d.LoadOp}),c.d(b,"StoreOp",function(){return d.StoreOp}),c.d(b,"QueryType",function(){return d.QueryType}),c.d(b,"PipelineStatisticName",function(){return d.PipelineStatisticName}),c.d(b,"CanvasCompositingAlphaMode",function(){return d.CanvasCompositingAlphaMode}),c.d(b,"DeviceLostReason",function(){return d.DeviceLostReason}),c.d(b,"ErrorFilter",function(){return d.ErrorFilter}),c.d(b,"WebGPUEngine",function(){return d.WebGPUEngine}),c.d(b,"WebGPUCacheRenderPipeline",function(){return d.WebGPUCacheRenderPipeline}),c.d(b,"WebGPUCacheRenderPipelineTree",function(){return d.WebGPUCacheRenderPipelineTree}),c.d(b,"WebGPUCacheBindGroups",function(){return d.WebGPUCacheBindGroups}),c.d(b,"WebGPUCacheSampler",function(){return d.WebGPUCacheSampler}),c.d(b,"WebGPUTintWASM",function(){return d.WebGPUTintWASM}),c.d(b,"WebGL2ShaderProcessor",function(){return d.WebGL2ShaderProcessor}),c.d(b,"NativeEngine",function(){return d.NativeEngine}),c.d(b,"ShaderCodeInliner",function(){return d.ShaderCodeInliner}),c.d(b,"PerformanceConfigurator",function(){return d.PerformanceConfigurator}),c.d(b,"EngineFactory",function(){return d.EngineFactory}),c.d(b,"ShaderStore",function(){return d.ShaderStore}),c.d(b,"KeyboardEventTypes",function(){return d.KeyboardEventTypes}),c.d(b,"KeyboardInfo",function(){return d.KeyboardInfo}),c.d(b,"KeyboardInfoPre",function(){return d.KeyboardInfoPre}),c.d(b,"PointerEventTypes",function(){return d.PointerEventTypes}),c.d(b,"PointerInfoBase",function(){return d.PointerInfoBase}),c.d(b,"PointerInfoPre",function(){return d.PointerInfoPre}),c.d(b,"PointerInfo",function(){return d.PointerInfo}),c.d(b,"ClipboardEventTypes",function(){return d.ClipboardEventTypes}),c.d(b,"ClipboardInfo",function(){return d.ClipboardInfo}),c.d(b,"DeviceInputEventType",function(){return d.DeviceInputEventType}),c.d(b,"EventConstants",function(){return d.EventConstants}),c.d(b,"DaydreamController",function(){return d.DaydreamController}),c.d(b,"GearVRController",function(){return d.GearVRController}),c.d(b,"GenericController",function(){return d.GenericController}),c.d(b,"OculusTouchController",function(){return d.OculusTouchController}),c.d(b,"PoseEnabledControllerType",function(){return d.PoseEnabledControllerType}),c.d(b,"PoseEnabledControllerHelper",function(){return d.PoseEnabledControllerHelper}),c.d(b,"PoseEnabledController",function(){return d.PoseEnabledController}),c.d(b,"ViveController",function(){return d.ViveController}),c.d(b,"WebVRController",function(){return d.WebVRController}),c.d(b,"WindowsMotionController",function(){return d.WindowsMotionController}),c.d(b,"XRWindowsMotionController",function(){return d.XRWindowsMotionController}),c.d(b,"StickValues",function(){return d.StickValues}),c.d(b,"Gamepad",function(){return d.Gamepad}),c.d(b,"GenericPad",function(){return d.GenericPad}),c.d(b,"GamepadManager",function(){return d.GamepadManager}),c.d(b,"GamepadSystemSceneComponent",function(){return d.GamepadSystemSceneComponent}),c.d(b,"Xbox360Button",function(){return d.Xbox360Button}),c.d(b,"Xbox360Dpad",function(){return d.Xbox360Dpad}),c.d(b,"Xbox360Pad",function(){return d.Xbox360Pad}),c.d(b,"DualShockButton",function(){return d.DualShockButton}),c.d(b,"DualShockDpad",function(){return d.DualShockDpad}),c.d(b,"DualShockPad",function(){return d.DualShockPad}),c.d(b,"AxisDragGizmo",function(){return d.AxisDragGizmo}),c.d(b,"AxisScaleGizmo",function(){return d.AxisScaleGizmo}),c.d(b,"BoundingBoxGizmo",function(){return d.BoundingBoxGizmo}),c.d(b,"Gizmo",function(){return d.Gizmo}),c.d(b,"GizmoManager",function(){return d.GizmoManager}),c.d(b,"PlaneRotationGizmo",function(){return d.PlaneRotationGizmo}),c.d(b,"PositionGizmo",function(){return d.PositionGizmo}),c.d(b,"RotationGizmo",function(){return d.RotationGizmo}),c.d(b,"ScaleGizmo",function(){return d.ScaleGizmo}),c.d(b,"LightGizmo",function(){return d.LightGizmo}),c.d(b,"CameraGizmo",function(){return d.CameraGizmo}),c.d(b,"PlaneDragGizmo",function(){return d.PlaneDragGizmo}),c.d(b,"EnvironmentHelper",function(){return d.EnvironmentHelper}),c.d(b,"PhotoDome",function(){return d.PhotoDome}),c.d(b,"_forceSceneHelpersToBundle",function(){return d._forceSceneHelpersToBundle}),c.d(b,"VideoDome",function(){return d.VideoDome}),c.d(b,"EngineInstrumentation",function(){return d.EngineInstrumentation}),c.d(b,"SceneInstrumentation",function(){return d.SceneInstrumentation}),c.d(b,"_TimeToken",function(){return d._TimeToken}),c.d(b,"EffectLayer",function(){return d.EffectLayer}),c.d(b,"EffectLayerSceneComponent",function(){return d.EffectLayerSceneComponent}),c.d(b,"GlowLayer",function(){return d.GlowLayer}),c.d(b,"HighlightLayer",function(){return d.HighlightLayer}),c.d(b,"Layer",function(){return d.Layer}),c.d(b,"LayerSceneComponent",function(){return d.LayerSceneComponent}),c.d(b,"LensFlare",function(){return d.LensFlare}),c.d(b,"LensFlareSystem",function(){return d.LensFlareSystem}),c.d(b,"LensFlareSystemSceneComponent",function(){return d.LensFlareSystemSceneComponent}),c.d(b,"Light",function(){return d.Light}),c.d(b,"ShadowLight",function(){return d.ShadowLight}),c.d(b,"ShadowGenerator",function(){return d.ShadowGenerator}),c.d(b,"CascadedShadowGenerator",function(){return d.CascadedShadowGenerator}),c.d(b,"ShadowGeneratorSceneComponent",function(){return d.ShadowGeneratorSceneComponent}),c.d(b,"DirectionalLight",function(){return d.DirectionalLight}),c.d(b,"HemisphericLight",function(){return d.HemisphericLight}),c.d(b,"PointLight",function(){return d.PointLight}),c.d(b,"SpotLight",function(){return d.SpotLight}),c.d(b,"DefaultLoadingScreen",function(){return d.DefaultLoadingScreen}),c.d(b,"_BabylonLoaderRegistered",function(){return d._BabylonLoaderRegistered}),c.d(b,"BabylonFileLoaderConfiguration",function(){return d.BabylonFileLoaderConfiguration}),c.d(b,"SceneLoaderAnimationGroupLoadingMode",function(){return d.SceneLoaderAnimationGroupLoadingMode}),c.d(b,"SceneLoader",function(){return d.SceneLoader}),c.d(b,"SceneLoaderFlags",function(){return d.SceneLoaderFlags}),c.d(b,"BackgroundMaterial",function(){return d.BackgroundMaterial}),c.d(b,"ColorCurves",function(){return d.ColorCurves}),c.d(b,"EffectFallbacks",function(){return d.EffectFallbacks}),c.d(b,"Effect",function(){return d.Effect}),c.d(b,"FresnelParameters",function(){return d.FresnelParameters}),c.d(b,"ImageProcessingConfigurationDefines",function(){return d.ImageProcessingConfigurationDefines}),c.d(b,"ImageProcessingConfiguration",function(){return d.ImageProcessingConfiguration}),c.d(b,"Material",function(){return d.Material}),c.d(b,"MaterialDefines",function(){return d.MaterialDefines}),c.d(b,"ThinMaterialHelper",function(){return d.ThinMaterialHelper}),c.d(b,"MaterialHelper",function(){return d.MaterialHelper}),c.d(b,"MultiMaterial",function(){return d.MultiMaterial}),c.d(b,"OcclusionMaterial",function(){return d.OcclusionMaterial}),c.d(b,"PBRMaterialDefines",function(){return d.PBRMaterialDefines}),c.d(b,"PBRBaseMaterial",function(){return d.PBRBaseMaterial}),c.d(b,"PBRBaseSimpleMaterial",function(){return d.PBRBaseSimpleMaterial}),c.d(b,"PBRMaterial",function(){return d.PBRMaterial}),c.d(b,"PBRMetallicRoughnessMaterial",function(){return d.PBRMetallicRoughnessMaterial}),c.d(b,"PBRSpecularGlossinessMaterial",function(){return d.PBRSpecularGlossinessMaterial}),c.d(b,"PushMaterial",function(){return d.PushMaterial}),c.d(b,"ShaderLanguage",function(){return d.ShaderLanguage}),c.d(b,"ShaderMaterial",function(){return d.ShaderMaterial}),c.d(b,"StandardMaterialDefines",function(){return d.StandardMaterialDefines}),c.d(b,"StandardMaterial",function(){return d.StandardMaterial}),c.d(b,"BaseTexture",function(){return d.BaseTexture}),c.d(b,"ColorGradingTexture",function(){return d.ColorGradingTexture}),c.d(b,"CubeTexture",function(){return d.CubeTexture}),c.d(b,"DynamicTexture",function(){return d.DynamicTexture}),c.d(b,"EquiRectangularCubeTexture",function(){return d.EquiRectangularCubeTexture}),c.d(b,"ExternalTexture",function(){return d.ExternalTexture}),c.d(b,"HDRFiltering",function(){return d.HDRFiltering}),c.d(b,"HDRCubeTexture",function(){return d.HDRCubeTexture}),c.d(b,"HtmlElementTexture",function(){return d.HtmlElementTexture}),c.d(b,"InternalTextureSource",function(){return d.InternalTextureSource}),c.d(b,"InternalTexture",function(){return d.InternalTexture}),c.d(b,"_DDSTextureLoader",function(){return d._DDSTextureLoader}),c.d(b,"_ENVTextureLoader",function(){return d._ENVTextureLoader}),c.d(b,"_KTXTextureLoader",function(){return d._KTXTextureLoader}),c.d(b,"_TGATextureLoader",function(){return d._TGATextureLoader}),c.d(b,"_HDRTextureLoader",function(){return d._HDRTextureLoader}),c.d(b,"_BasisTextureLoader",function(){return d._BasisTextureLoader}),c.d(b,"MirrorTexture",function(){return d.MirrorTexture}),c.d(b,"MultiRenderTarget",function(){return d.MultiRenderTarget}),c.d(b,"TexturePacker",function(){return d.TexturePacker}),c.d(b,"TexturePackerFrame",function(){return d.TexturePackerFrame}),c.d(b,"CustomProceduralTexture",function(){return d.CustomProceduralTexture}),c.d(b,"NoiseProceduralTexture",function(){return d.NoiseProceduralTexture}),c.d(b,"ProceduralTexture",function(){return d.ProceduralTexture}),c.d(b,"ProceduralTextureSceneComponent",function(){return d.ProceduralTextureSceneComponent}),c.d(b,"RawCubeTexture",function(){return d.RawCubeTexture}),c.d(b,"RawTexture",function(){return d.RawTexture}),c.d(b,"RawTexture2DArray",function(){return d.RawTexture2DArray}),c.d(b,"RawTexture3D",function(){return d.RawTexture3D}),c.d(b,"RefractionTexture",function(){return d.RefractionTexture}),c.d(b,"RenderTargetTexture",function(){return d.RenderTargetTexture}),c.d(b,"TextureSampler",function(){return d.TextureSampler}),c.d(b,"Texture",function(){return d.Texture}),c.d(b,"ThinTexture",function(){return d.ThinTexture}),c.d(b,"ThinRenderTargetTexture",function(){return d.ThinRenderTargetTexture}),c.d(b,"VideoTexture",function(){return d.VideoTexture}),c.d(b,"UniformBuffer",function(){return d.UniformBuffer}),c.d(b,"MaterialFlags",function(){return d.MaterialFlags}),c.d(b,"NodeMaterialBlockTargets",function(){return d.NodeMaterialBlockTargets}),c.d(b,"NodeMaterialBlockConnectionPointTypes",function(){return d.NodeMaterialBlockConnectionPointTypes}),c.d(b,"NodeMaterialBlockConnectionPointMode",function(){return d.NodeMaterialBlockConnectionPointMode}),c.d(b,"NodeMaterialSystemValues",function(){return d.NodeMaterialSystemValues}),c.d(b,"NodeMaterialModes",function(){return d.NodeMaterialModes}),c.d(b,"NodeMaterialConnectionPointCompatibilityStates",function(){return d.NodeMaterialConnectionPointCompatibilityStates}),c.d(b,"NodeMaterialConnectionPointDirection",function(){return d.NodeMaterialConnectionPointDirection}),c.d(b,"NodeMaterialConnectionPoint",function(){return d.NodeMaterialConnectionPoint}),c.d(b,"NodeMaterialBlock",function(){return d.NodeMaterialBlock}),c.d(b,"NodeMaterialDefines",function(){return d.NodeMaterialDefines}),c.d(b,"NodeMaterial",function(){return d.NodeMaterial}),c.d(b,"VertexOutputBlock",function(){return d.VertexOutputBlock}),c.d(b,"BonesBlock",function(){return d.BonesBlock}),c.d(b,"InstancesBlock",function(){return d.InstancesBlock}),c.d(b,"MorphTargetsBlock",function(){return d.MorphTargetsBlock}),c.d(b,"LightInformationBlock",function(){return d.LightInformationBlock}),c.d(b,"FragmentOutputBlock",function(){return d.FragmentOutputBlock}),c.d(b,"ImageProcessingBlock",function(){return d.ImageProcessingBlock}),c.d(b,"PerturbNormalBlock",function(){return d.PerturbNormalBlock}),c.d(b,"DiscardBlock",function(){return d.DiscardBlock}),c.d(b,"FrontFacingBlock",function(){return d.FrontFacingBlock}),c.d(b,"DerivativeBlock",function(){return d.DerivativeBlock}),c.d(b,"FragCoordBlock",function(){return d.FragCoordBlock}),c.d(b,"ScreenSizeBlock",function(){return d.ScreenSizeBlock}),c.d(b,"FogBlock",function(){return d.FogBlock}),c.d(b,"LightBlock",function(){return d.LightBlock}),c.d(b,"TextureBlock",function(){return d.TextureBlock}),c.d(b,"ReflectionTextureBlock",function(){return d.ReflectionTextureBlock}),c.d(b,"CurrentScreenBlock",function(){return d.CurrentScreenBlock}),c.d(b,"SceneDepthBlock",function(){return d.SceneDepthBlock}),c.d(b,"ImageSourceBlock",function(){return d.ImageSourceBlock}),c.d(b,"InputBlock",function(){return d.InputBlock}),c.d(b,"AnimatedInputBlockTypes",function(){return d.AnimatedInputBlockTypes}),c.d(b,"MultiplyBlock",function(){return d.MultiplyBlock}),c.d(b,"AddBlock",function(){return d.AddBlock}),c.d(b,"ScaleBlock",function(){return d.ScaleBlock}),c.d(b,"ClampBlock",function(){return d.ClampBlock}),c.d(b,"CrossBlock",function(){return d.CrossBlock}),c.d(b,"DotBlock",function(){return d.DotBlock}),c.d(b,"TransformBlock",function(){return d.TransformBlock}),c.d(b,"RemapBlock",function(){return d.RemapBlock}),c.d(b,"NormalizeBlock",function(){return d.NormalizeBlock}),c.d(b,"TrigonometryBlockOperations",function(){return d.TrigonometryBlockOperations}),c.d(b,"TrigonometryBlock",function(){return d.TrigonometryBlock}),c.d(b,"ColorMergerBlock",function(){return d.ColorMergerBlock}),c.d(b,"VectorMergerBlock",function(){return d.VectorMergerBlock}),c.d(b,"ColorSplitterBlock",function(){return d.ColorSplitterBlock}),c.d(b,"VectorSplitterBlock",function(){return d.VectorSplitterBlock}),c.d(b,"LerpBlock",function(){return d.LerpBlock}),c.d(b,"DivideBlock",function(){return d.DivideBlock}),c.d(b,"SubtractBlock",function(){return d.SubtractBlock}),c.d(b,"StepBlock",function(){return d.StepBlock}),c.d(b,"OneMinusBlock",function(){return d.OneMinusBlock}),c.d(b,"ViewDirectionBlock",function(){return d.ViewDirectionBlock}),c.d(b,"FresnelBlock",function(){return d.FresnelBlock}),c.d(b,"MaxBlock",function(){return d.MaxBlock}),c.d(b,"MinBlock",function(){return d.MinBlock}),c.d(b,"DistanceBlock",function(){return d.DistanceBlock}),c.d(b,"LengthBlock",function(){return d.LengthBlock}),c.d(b,"NegateBlock",function(){return d.NegateBlock}),c.d(b,"PowBlock",function(){return d.PowBlock}),c.d(b,"RandomNumberBlock",function(){return d.RandomNumberBlock}),c.d(b,"ArcTan2Block",function(){return d.ArcTan2Block}),c.d(b,"SmoothStepBlock",function(){return d.SmoothStepBlock}),c.d(b,"ReciprocalBlock",function(){return d.ReciprocalBlock}),c.d(b,"ReplaceColorBlock",function(){return d.ReplaceColorBlock}),c.d(b,"PosterizeBlock",function(){return d.PosterizeBlock}),c.d(b,"WaveBlockKind",function(){return d.WaveBlockKind}),c.d(b,"WaveBlock",function(){return d.WaveBlock}),c.d(b,"GradientBlockColorStep",function(){return d.GradientBlockColorStep}),c.d(b,"GradientBlock",function(){return d.GradientBlock}),c.d(b,"NLerpBlock",function(){return d.NLerpBlock}),c.d(b,"WorleyNoise3DBlock",function(){return d.WorleyNoise3DBlock}),c.d(b,"SimplexPerlin3DBlock",function(){return d.SimplexPerlin3DBlock}),c.d(b,"NormalBlendBlock",function(){return d.NormalBlendBlock}),c.d(b,"Rotate2dBlock",function(){return d.Rotate2dBlock}),c.d(b,"ReflectBlock",function(){return d.ReflectBlock}),c.d(b,"RefractBlock",function(){return d.RefractBlock}),c.d(b,"DesaturateBlock",function(){return d.DesaturateBlock}),c.d(b,"PBRMetallicRoughnessBlock",function(){return d.PBRMetallicRoughnessBlock}),c.d(b,"SheenBlock",function(){return d.SheenBlock}),c.d(b,"AnisotropyBlock",function(){return d.AnisotropyBlock}),c.d(b,"ReflectionBlock",function(){return d.ReflectionBlock}),c.d(b,"ClearCoatBlock",function(){return d.ClearCoatBlock}),c.d(b,"RefractionBlock",function(){return d.RefractionBlock}),c.d(b,"SubSurfaceBlock",function(){return d.SubSurfaceBlock}),c.d(b,"ParticleTextureBlock",function(){return d.ParticleTextureBlock}),c.d(b,"ParticleRampGradientBlock",function(){return d.ParticleRampGradientBlock}),c.d(b,"ParticleBlendMultiplyBlock",function(){return d.ParticleBlendMultiplyBlock}),c.d(b,"ModBlock",function(){return d.ModBlock}),c.d(b,"MatrixBuilderBlock",function(){return d.MatrixBuilderBlock}),c.d(b,"ConditionalBlockConditions",function(){return d.ConditionalBlockConditions}),c.d(b,"ConditionalBlock",function(){return d.ConditionalBlock}),c.d(b,"CloudBlock",function(){return d.CloudBlock}),c.d(b,"NodeMaterialOptimizer",function(){return d.NodeMaterialOptimizer}),c.d(b,"PropertyTypeForEdition",function(){return d.PropertyTypeForEdition}),c.d(b,"editableInPropertyPage",function(){return d.editableInPropertyPage}),c.d(b,"EffectRenderer",function(){return d.EffectRenderer}),c.d(b,"EffectWrapper",function(){return d.EffectWrapper}),c.d(b,"ShadowDepthWrapper",function(){return d.ShadowDepthWrapper}),c.d(b,"DrawWrapper",function(){return d.DrawWrapper}),c.d(b,"Scalar",function(){return d.Scalar}),c.d(b,"extractMinAndMaxIndexed",function(){return d.extractMinAndMaxIndexed}),c.d(b,"extractMinAndMax",function(){return d.extractMinAndMax}),c.d(b,"Space",function(){return d.Space}),c.d(b,"Axis",function(){return d.Axis}),c.d(b,"Coordinate",function(){return d.Coordinate}),c.d(b,"Color3",function(){return d.Color3}),c.d(b,"Color4",function(){return d.Color4}),c.d(b,"TmpColors",function(){return d.TmpColors}),c.d(b,"ToGammaSpace",function(){return d.ToGammaSpace}),c.d(b,"ToLinearSpace",function(){return d.ToLinearSpace}),c.d(b,"PHI",function(){return d.PHI}),c.d(b,"Epsilon",function(){return d.Epsilon}),c.d(b,"Frustum",function(){return d.Frustum}),c.d(b,"Orientation",function(){return d.Orientation}),c.d(b,"BezierCurve",function(){return d.BezierCurve}),c.d(b,"Angle",function(){return d.Angle}),c.d(b,"Arc2",function(){return d.Arc2}),c.d(b,"Path2",function(){return d.Path2}),c.d(b,"Path3D",function(){return d.Path3D}),c.d(b,"Curve3",function(){return d.Curve3}),c.d(b,"Plane",function(){return d.Plane}),c.d(b,"Size",function(){return d.Size}),c.d(b,"Vector2",function(){return d.Vector2}),c.d(b,"Vector3",function(){return d.Vector3}),c.d(b,"Vector4",function(){return d.Vector4}),c.d(b,"Quaternion",function(){return d.Quaternion}),c.d(b,"Matrix",function(){return d.Matrix}),c.d(b,"TmpVectors",function(){return d.TmpVectors}),c.d(b,"PositionNormalVertex",function(){return d.PositionNormalVertex}),c.d(b,"PositionNormalTextureVertex",function(){return d.PositionNormalTextureVertex}),c.d(b,"Viewport",function(){return d.Viewport}),c.d(b,"SphericalHarmonics",function(){return d.SphericalHarmonics}),c.d(b,"SphericalPolynomial",function(){return d.SphericalPolynomial}),c.d(b,"AbstractMesh",function(){return d.AbstractMesh}),c.d(b,"DracoCompression",function(){return d.DracoCompression}),c.d(b,"MeshoptCompression",function(){return d.MeshoptCompression}),c.d(b,"CSG",function(){return d.CSG}),c.d(b,"Geometry",function(){return d.Geometry}),c.d(b,"GroundMesh",function(){return d.GroundMesh}),c.d(b,"TrailMesh",function(){return d.TrailMesh}),c.d(b,"InstancedMesh",function(){return d.InstancedMesh}),c.d(b,"LinesMesh",function(){return d.LinesMesh}),c.d(b,"InstancedLinesMesh",function(){return d.InstancedLinesMesh}),c.d(b,"_CreationDataStorage",function(){return d._CreationDataStorage}),c.d(b,"_InstancesBatch",function(){return d._InstancesBatch}),c.d(b,"Mesh",function(){return d.Mesh}),c.d(b,"_injectLTSMesh",function(){return d._injectLTSMesh}),c.d(b,"VertexData",function(){return d.VertexData}),c.d(b,"MeshBuilder",function(){return d.MeshBuilder}),c.d(b,"SimplificationSettings",function(){return d.SimplificationSettings}),c.d(b,"SimplificationQueue",function(){return d.SimplificationQueue}),c.d(b,"SimplificationType",function(){return d.SimplificationType}),c.d(b,"QuadraticErrorSimplification",function(){return d.QuadraticErrorSimplification}),c.d(b,"SimplicationQueueSceneComponent",function(){return d.SimplicationQueueSceneComponent}),c.d(b,"Polygon",function(){return d.Polygon}),c.d(b,"PolygonMeshBuilder",function(){return d.PolygonMeshBuilder}),c.d(b,"_PrimaryIsoTriangle",function(){return d._PrimaryIsoTriangle}),c.d(b,"PolyhedronData",function(){return d.PolyhedronData}),c.d(b,"GeodesicData",function(){return d.GeodesicData}),c.d(b,"SubMesh",function(){return d.SubMesh}),c.d(b,"MeshLODLevel",function(){return d.MeshLODLevel}),c.d(b,"TransformNode",function(){return d.TransformNode}),c.d(b,"CreateBoxVertexData",function(){return d.CreateBoxVertexData}),c.d(b,"CreateBox",function(){return d.CreateBox}),c.d(b,"BoxBuilder",function(){return d.BoxBuilder}),c.d(b,"CreateTiledBoxVertexData",function(){return d.CreateTiledBoxVertexData}),c.d(b,"CreateTiledBox",function(){return d.CreateTiledBox}),c.d(b,"TiledBoxBuilder",function(){return d.TiledBoxBuilder}),c.d(b,"CreateDisc",function(){return d.CreateDisc}),c.d(b,"DiscBuilder",function(){return d.DiscBuilder}),c.d(b,"CreateRibbonVertexData",function(){return d.CreateRibbonVertexData}),c.d(b,"CreateRibbon",function(){return d.CreateRibbon}),c.d(b,"RibbonBuilder",function(){return d.RibbonBuilder}),c.d(b,"CreateSphereVertexData",function(){return d.CreateSphereVertexData}),c.d(b,"CreateSphere",function(){return d.CreateSphere}),c.d(b,"SphereBuilder",function(){return d.SphereBuilder}),c.d(b,"CreateHemisphere",function(){return d.CreateHemisphere}),c.d(b,"HemisphereBuilder",function(){return d.HemisphereBuilder}),c.d(b,"CreateCylinderVertexData",function(){return d.CreateCylinderVertexData}),c.d(b,"CreateCylinder",function(){return d.CreateCylinder}),c.d(b,"CylinderBuilder",function(){return d.CylinderBuilder}),c.d(b,"CreateTorusVertexData",function(){return d.CreateTorusVertexData}),c.d(b,"CreateTorus",function(){return d.CreateTorus}),c.d(b,"TorusBuilder",function(){return d.TorusBuilder}),c.d(b,"CreateTorusKnotVertexData",function(){return d.CreateTorusKnotVertexData}),c.d(b,"CreateTorusKnot",function(){return d.CreateTorusKnot}),c.d(b,"TorusKnotBuilder",function(){return d.TorusKnotBuilder}),c.d(b,"CreateLineSystemVertexData",function(){return d.CreateLineSystemVertexData}),c.d(b,"CreateDashedLinesVertexData",function(){return d.CreateDashedLinesVertexData}),c.d(b,"CreateLineSystem",function(){return d.CreateLineSystem}),c.d(b,"CreateLines",function(){return d.CreateLines}),c.d(b,"CreateDashedLines",function(){return d.CreateDashedLines}),c.d(b,"LinesBuilder",function(){return d.LinesBuilder}),c.d(b,"CreatePolygonVertexData",function(){return d.CreatePolygonVertexData}),c.d(b,"CreatePolygon",function(){return d.CreatePolygon}),c.d(b,"ExtrudePolygon",function(){return d.ExtrudePolygon}),c.d(b,"PolygonBuilder",function(){return d.PolygonBuilder}),c.d(b,"ExtrudeShape",function(){return d.ExtrudeShape}),c.d(b,"ExtrudeShapeCustom",function(){return d.ExtrudeShapeCustom}),c.d(b,"ShapeBuilder",function(){return d.ShapeBuilder}),c.d(b,"CreateLathe",function(){return d.CreateLathe}),c.d(b,"LatheBuilder",function(){return d.LatheBuilder}),c.d(b,"CreatePlaneVertexData",function(){return d.CreatePlaneVertexData}),c.d(b,"CreatePlane",function(){return d.CreatePlane}),c.d(b,"PlaneBuilder",function(){return d.PlaneBuilder}),c.d(b,"CreateTiledPlaneVertexData",function(){return d.CreateTiledPlaneVertexData}),c.d(b,"CreateTiledPlane",function(){return d.CreateTiledPlane}),c.d(b,"TiledPlaneBuilder",function(){return d.TiledPlaneBuilder}),c.d(b,"CreateGroundVertexData",function(){return d.CreateGroundVertexData}),c.d(b,"CreateTiledGroundVertexData",function(){return d.CreateTiledGroundVertexData}),c.d(b,"CreateGroundFromHeightMapVertexData",function(){return d.CreateGroundFromHeightMapVertexData}),c.d(b,"CreateGround",function(){return d.CreateGround}),c.d(b,"CreateTiledGround",function(){return d.CreateTiledGround}),c.d(b,"CreateGroundFromHeightMap",function(){return d.CreateGroundFromHeightMap}),c.d(b,"GroundBuilder",function(){return d.GroundBuilder}),c.d(b,"CreateTube",function(){return d.CreateTube}),c.d(b,"TubeBuilder",function(){return d.TubeBuilder}),c.d(b,"CreatePolyhedronVertexData",function(){return d.CreatePolyhedronVertexData}),c.d(b,"CreatePolyhedron",function(){return d.CreatePolyhedron}),c.d(b,"PolyhedronBuilder",function(){return d.PolyhedronBuilder}),c.d(b,"CreateIcoSphereVertexData",function(){return d.CreateIcoSphereVertexData}),c.d(b,"CreateIcoSphere",function(){return d.CreateIcoSphere}),c.d(b,"IcoSphereBuilder",function(){return d.IcoSphereBuilder}),c.d(b,"CreateGeodesic",function(){return d.CreateGeodesic}),c.d(b,"CreateDecal",function(){return d.CreateDecal}),c.d(b,"DecalBuilder",function(){return d.DecalBuilder}),c.d(b,"CreateCapsuleVertexData",function(){return d.CreateCapsuleVertexData}),c.d(b,"CreateCapsule",function(){return d.CreateCapsule}),c.d(b,"CapsuleBuilder",function(){return d.CapsuleBuilder}),c.d(b,"WebGLDataBuffer",function(){return d.WebGLDataBuffer}),c.d(b,"WebGPUDataBuffer",function(){return d.WebGPUDataBuffer}),c.d(b,"MorphTarget",function(){return d.MorphTarget}),c.d(b,"MorphTargetManager",function(){return d.MorphTargetManager}),c.d(b,"RecastJSPlugin",function(){return d.RecastJSPlugin}),c.d(b,"RecastJSCrowd",function(){return d.RecastJSCrowd}),c.d(b,"Node",function(){return d.Node}),c.d(b,"Database",function(){return d.Database}),c.d(b,"BaseParticleSystem",function(){return d.BaseParticleSystem}),c.d(b,"BoxParticleEmitter",function(){return d.BoxParticleEmitter}),c.d(b,"ConeParticleEmitter",function(){return d.ConeParticleEmitter}),c.d(b,"CylinderParticleEmitter",function(){return d.CylinderParticleEmitter}),c.d(b,"CylinderDirectedParticleEmitter",function(){return d.CylinderDirectedParticleEmitter}),c.d(b,"HemisphericParticleEmitter",function(){return d.HemisphericParticleEmitter}),c.d(b,"PointParticleEmitter",function(){return d.PointParticleEmitter}),c.d(b,"SphereParticleEmitter",function(){return d.SphereParticleEmitter}),c.d(b,"SphereDirectedParticleEmitter",function(){return d.SphereDirectedParticleEmitter}),c.d(b,"CustomParticleEmitter",function(){return d.CustomParticleEmitter}),c.d(b,"MeshParticleEmitter",function(){return d.MeshParticleEmitter}),c.d(b,"WebGL2ParticleSystem",function(){return d.WebGL2ParticleSystem}),c.d(b,"ComputeShaderParticleSystem",function(){return d.ComputeShaderParticleSystem}),c.d(b,"GPUParticleSystem",function(){return d.GPUParticleSystem}),c.d(b,"Particle",function(){return d.Particle}),c.d(b,"ParticleHelper",function(){return d.ParticleHelper}),c.d(b,"ParticleSystem",function(){return d.ParticleSystem}),c.d(b,"ParticleSystemSet",function(){return d.ParticleSystemSet}),c.d(b,"SolidParticle",function(){return d.SolidParticle}),c.d(b,"ModelShape",function(){return d.ModelShape}),c.d(b,"DepthSortedParticle",function(){return d.DepthSortedParticle}),c.d(b,"SolidParticleVertex",function(){return d.SolidParticleVertex}),c.d(b,"SolidParticleSystem",function(){return d.SolidParticleSystem}),c.d(b,"CloudPoint",function(){return d.CloudPoint}),c.d(b,"PointsGroup",function(){return d.PointsGroup}),c.d(b,"PointColor",function(){return d.PointColor}),c.d(b,"PointsCloudSystem",function(){return d.PointsCloudSystem}),c.d(b,"SubEmitterType",function(){return d.SubEmitterType}),c.d(b,"SubEmitter",function(){return d.SubEmitter}),c.d(b,"PhysicsEngine",function(){return d.PhysicsEngine}),c.d(b,"PhysicsEngineSceneComponent",function(){return d.PhysicsEngineSceneComponent}),c.d(b,"PhysicsHelper",function(){return d.PhysicsHelper}),c.d(b,"PhysicsRadialExplosionEventOptions",function(){return d.PhysicsRadialExplosionEventOptions}),c.d(b,"PhysicsUpdraftEventOptions",function(){return d.PhysicsUpdraftEventOptions}),c.d(b,"PhysicsVortexEventOptions",function(){return d.PhysicsVortexEventOptions}),c.d(b,"PhysicsRadialImpulseFalloff",function(){return d.PhysicsRadialImpulseFalloff}),c.d(b,"PhysicsUpdraftMode",function(){return d.PhysicsUpdraftMode}),c.d(b,"PhysicsImpostor",function(){return d.PhysicsImpostor}),c.d(b,"PhysicsJoint",function(){return d.PhysicsJoint}),c.d(b,"DistanceJoint",function(){return d.DistanceJoint}),c.d(b,"MotorEnabledJoint",function(){return d.MotorEnabledJoint}),c.d(b,"HingeJoint",function(){return d.HingeJoint}),c.d(b,"Hinge2Joint",function(){return d.Hinge2Joint}),c.d(b,"CannonJSPlugin",function(){return d.CannonJSPlugin}),c.d(b,"AmmoJSPlugin",function(){return d.AmmoJSPlugin}),c.d(b,"OimoJSPlugin",function(){return d.OimoJSPlugin}),c.d(b,"AnaglyphPostProcess",function(){return d.AnaglyphPostProcess}),c.d(b,"BlackAndWhitePostProcess",function(){return d.BlackAndWhitePostProcess}),c.d(b,"BloomEffect",function(){return d.BloomEffect}),c.d(b,"BloomMergePostProcess",function(){return d.BloomMergePostProcess}),c.d(b,"BlurPostProcess",function(){return d.BlurPostProcess}),c.d(b,"ChromaticAberrationPostProcess",function(){return d.ChromaticAberrationPostProcess}),c.d(b,"CircleOfConfusionPostProcess",function(){return d.CircleOfConfusionPostProcess}),c.d(b,"ColorCorrectionPostProcess",function(){return d.ColorCorrectionPostProcess}),c.d(b,"ConvolutionPostProcess",function(){return d.ConvolutionPostProcess}),c.d(b,"DepthOfFieldBlurPostProcess",function(){return d.DepthOfFieldBlurPostProcess}),c.d(b,"DepthOfFieldEffectBlurLevel",function(){return d.DepthOfFieldEffectBlurLevel}),c.d(b,"DepthOfFieldEffect",function(){return d.DepthOfFieldEffect}),c.d(b,"DepthOfFieldMergePostProcessOptions",function(){return d.DepthOfFieldMergePostProcessOptions}),c.d(b,"DepthOfFieldMergePostProcess",function(){return d.DepthOfFieldMergePostProcess}),c.d(b,"DisplayPassPostProcess",function(){return d.DisplayPassPostProcess}),c.d(b,"ExtractHighlightsPostProcess",function(){return d.ExtractHighlightsPostProcess}),c.d(b,"FilterPostProcess",function(){return d.FilterPostProcess}),c.d(b,"FxaaPostProcess",function(){return d.FxaaPostProcess}),c.d(b,"GrainPostProcess",function(){return d.GrainPostProcess}),c.d(b,"HighlightsPostProcess",function(){return d.HighlightsPostProcess}),c.d(b,"ImageProcessingPostProcess",function(){return d.ImageProcessingPostProcess}),c.d(b,"MotionBlurPostProcess",function(){return d.MotionBlurPostProcess}),c.d(b,"PassPostProcess",function(){return d.PassPostProcess}),c.d(b,"PassCubePostProcess",function(){return d.PassCubePostProcess}),c.d(b,"PostProcess",function(){return d.PostProcess}),c.d(b,"PostProcessManager",function(){return d.PostProcessManager}),c.d(b,"RefractionPostProcess",function(){return d.RefractionPostProcess}),c.d(b,"DefaultRenderingPipeline",function(){return d.DefaultRenderingPipeline}),c.d(b,"LensRenderingPipeline",function(){return d.LensRenderingPipeline}),c.d(b,"SSAO2RenderingPipeline",function(){return d.SSAO2RenderingPipeline}),c.d(b,"SSAORenderingPipeline",function(){return d.SSAORenderingPipeline}),c.d(b,"StandardRenderingPipeline",function(){return d.StandardRenderingPipeline}),c.d(b,"PostProcessRenderEffect",function(){return d.PostProcessRenderEffect}),c.d(b,"PostProcessRenderPipeline",function(){return d.PostProcessRenderPipeline}),c.d(b,"PostProcessRenderPipelineManager",function(){return d.PostProcessRenderPipelineManager}),c.d(b,"PostProcessRenderPipelineManagerSceneComponent",function(){return d.PostProcessRenderPipelineManagerSceneComponent}),c.d(b,"SharpenPostProcess",function(){return d.SharpenPostProcess}),c.d(b,"StereoscopicInterlacePostProcessI",function(){return d.StereoscopicInterlacePostProcessI}),c.d(b,"StereoscopicInterlacePostProcess",function(){return d.StereoscopicInterlacePostProcess}),c.d(b,"TonemappingOperator",function(){return d.TonemappingOperator}),c.d(b,"TonemapPostProcess",function(){return d.TonemapPostProcess}),c.d(b,"VolumetricLightScatteringPostProcess",function(){return d.VolumetricLightScatteringPostProcess}),c.d(b,"VRDistortionCorrectionPostProcess",function(){return d.VRDistortionCorrectionPostProcess}),c.d(b,"VRMultiviewToSingleviewPostProcess",function(){return d.VRMultiviewToSingleviewPostProcess}),c.d(b,"ScreenSpaceReflectionPostProcess",function(){return d.ScreenSpaceReflectionPostProcess}),c.d(b,"ScreenSpaceCurvaturePostProcess",function(){return d.ScreenSpaceCurvaturePostProcess}),c.d(b,"ReflectionProbe",function(){return d.ReflectionProbe}),c.d(b,"BoundingBoxRenderer",function(){return d.BoundingBoxRenderer}),c.d(b,"DepthRenderer",function(){return d.DepthRenderer}),c.d(b,"DepthRendererSceneComponent",function(){return d.DepthRendererSceneComponent}),c.d(b,"DepthPeelingRenderer",function(){return d.DepthPeelingRenderer}),c.d(b,"DepthPeelingSceneComponent",function(){return d.DepthPeelingSceneComponent}),c.d(b,"EdgesRenderer",function(){return d.EdgesRenderer}),c.d(b,"LineEdgesRenderer",function(){return d.LineEdgesRenderer}),c.d(b,"GeometryBufferRenderer",function(){return d.GeometryBufferRenderer}),c.d(b,"GeometryBufferRendererSceneComponent",function(){return d.GeometryBufferRendererSceneComponent}),c.d(b,"PrePassRenderer",function(){return d.PrePassRenderer}),c.d(b,"PrePassRendererSceneComponent",function(){return d.PrePassRendererSceneComponent}),c.d(b,"SubSurfaceSceneComponent",function(){return d.SubSurfaceSceneComponent}),c.d(b,"OutlineRenderer",function(){return d.OutlineRenderer}),c.d(b,"RenderingGroup",function(){return d.RenderingGroup}),c.d(b,"RenderingGroupInfo",function(){return d.RenderingGroupInfo}),c.d(b,"RenderingManager",function(){return d.RenderingManager}),c.d(b,"UtilityLayerRenderer",function(){return d.UtilityLayerRenderer}),c.d(b,"Scene",function(){return d.Scene}),c.d(b,"_injectLTSScene",function(){return d._injectLTSScene}),c.d(b,"SceneComponentConstants",function(){return d.SceneComponentConstants}),c.d(b,"Stage",function(){return d.Stage}),c.d(b,"Sprite",function(){return d.Sprite}),c.d(b,"SpriteManager",function(){return d.SpriteManager}),c.d(b,"SpriteMap",function(){return d.SpriteMap}),c.d(b,"SpritePackedManager",function(){return d.SpritePackedManager}),c.d(b,"SpriteSceneComponent",function(){return d.SpriteSceneComponent}),c.d(b,"AlphaState",function(){return d.AlphaState}),c.d(b,"DepthCullingState",function(){return d.DepthCullingState}),c.d(b,"StencilState",function(){return d.StencilState}),c.d(b,"StencilStateComposer",function(){return d.StencilStateComposer}),c.d(b,"AndOrNotEvaluator",function(){return d.AndOrNotEvaluator}),c.d(b,"AssetTaskState",function(){return d.AssetTaskState}),c.d(b,"AbstractAssetTask",function(){return d.AbstractAssetTask}),c.d(b,"AssetsProgressEvent",function(){return d.AssetsProgressEvent}),c.d(b,"ContainerAssetTask",function(){return d.ContainerAssetTask}),c.d(b,"MeshAssetTask",function(){return d.MeshAssetTask}),c.d(b,"TextFileAssetTask",function(){return d.TextFileAssetTask}),c.d(b,"BinaryFileAssetTask",function(){return d.BinaryFileAssetTask}),c.d(b,"ImageAssetTask",function(){return d.ImageAssetTask}),c.d(b,"TextureAssetTask",function(){return d.TextureAssetTask}),c.d(b,"CubeTextureAssetTask",function(){return d.CubeTextureAssetTask}),c.d(b,"HDRCubeTextureAssetTask",function(){return d.HDRCubeTextureAssetTask}),c.d(b,"EquiRectangularCubeTextureAssetTask",function(){return d.EquiRectangularCubeTextureAssetTask}),c.d(b,"AssetsManager",function(){return d.AssetsManager}),c.d(b,"BasisTranscodeConfiguration",function(){return d.BasisTranscodeConfiguration}),c.d(b,"BasisToolsOptions",function(){return d.BasisToolsOptions}),c.d(b,"GetInternalFormatFromBasisFormat",function(){return d.GetInternalFormatFromBasisFormat}),c.d(b,"TranscodeAsync",function(){return d.TranscodeAsync}),c.d(b,"LoadTextureFromTranscodeResult",function(){return d.LoadTextureFromTranscodeResult}),c.d(b,"BasisTools",function(){return d.BasisTools}),c.d(b,"DDSTools",function(){return d.DDSTools}),c.d(b,"expandToProperty",function(){return d.expandToProperty}),c.d(b,"serialize",function(){return d.serialize}),c.d(b,"serializeAsTexture",function(){return d.serializeAsTexture}),c.d(b,"serializeAsColor3",function(){return d.serializeAsColor3}),c.d(b,"serializeAsFresnelParameters",function(){return d.serializeAsFresnelParameters}),c.d(b,"serializeAsVector2",function(){return d.serializeAsVector2}),c.d(b,"serializeAsVector3",function(){return d.serializeAsVector3}),c.d(b,"serializeAsMeshReference",function(){return d.serializeAsMeshReference}),c.d(b,"serializeAsColorCurves",function(){return d.serializeAsColorCurves}),c.d(b,"serializeAsColor4",function(){return d.serializeAsColor4}),c.d(b,"serializeAsImageProcessingConfiguration",function(){return d.serializeAsImageProcessingConfiguration}),c.d(b,"serializeAsQuaternion",function(){return d.serializeAsQuaternion}),c.d(b,"serializeAsMatrix",function(){return d.serializeAsMatrix}),c.d(b,"serializeAsCameraReference",function(){return d.serializeAsCameraReference}),c.d(b,"SerializationHelper",function(){return d.SerializationHelper}),c.d(b,"nativeOverride",function(){return d.nativeOverride}),c.d(b,"Deferred",function(){return d.Deferred}),c.d(b,"GetEnvInfo",function(){return d.GetEnvInfo}),c.d(b,"normalizeEnvInfo",function(){return d.normalizeEnvInfo}),c.d(b,"CreateEnvTextureAsync",function(){return d.CreateEnvTextureAsync}),c.d(b,"CreateImageDataArrayBufferViews",function(){return d.CreateImageDataArrayBufferViews}),c.d(b,"UploadEnvLevelsAsync",function(){return d.UploadEnvLevelsAsync}),c.d(b,"UploadLevelsAsync",function(){return d.UploadLevelsAsync}),c.d(b,"UploadEnvSpherical",function(){return d.UploadEnvSpherical}),c.d(b,"_UpdateRGBDAsync",function(){return d._UpdateRGBDAsync}),c.d(b,"EnvironmentTextureTools",function(){return d.EnvironmentTextureTools}),c.d(b,"MeshExploder",function(){return d.MeshExploder}),c.d(b,"FilesInput",function(){return d.FilesInput}),c.d(b,"CubeMapToSphericalPolynomialTools",function(){return d.CubeMapToSphericalPolynomialTools}),c.d(b,"HDRTools",function(){return d.HDRTools}),c.d(b,"PanoramaToCubeMapTools",function(){return d.PanoramaToCubeMapTools}),c.d(b,"KhronosTextureContainer",function(){return d.KhronosTextureContainer}),c.d(b,"EventState",function(){return d.EventState}),c.d(b,"Observer",function(){return d.Observer}),c.d(b,"MultiObserver",function(){return d.MultiObserver}),c.d(b,"Observable",function(){return d.Observable}),c.d(b,"PerformanceMonitor",function(){return d.PerformanceMonitor}),c.d(b,"RollingAverage",function(){return d.RollingAverage}),c.d(b,"PromisePolyfill",function(){return d.PromisePolyfill}),c.d(b,"SceneOptimization",function(){return d.SceneOptimization}),c.d(b,"TextureOptimization",function(){return d.TextureOptimization}),c.d(b,"HardwareScalingOptimization",function(){return d.HardwareScalingOptimization}),c.d(b,"ShadowsOptimization",function(){return d.ShadowsOptimization}),c.d(b,"PostProcessesOptimization",function(){return d.PostProcessesOptimization}),c.d(b,"LensFlaresOptimization",function(){return d.LensFlaresOptimization}),c.d(b,"CustomOptimization",function(){return d.CustomOptimization}),c.d(b,"ParticlesOptimization",function(){return d.ParticlesOptimization}),c.d(b,"RenderTargetsOptimization",function(){return d.RenderTargetsOptimization}),c.d(b,"MergeMeshesOptimization",function(){return d.MergeMeshesOptimization}),c.d(b,"SceneOptimizerOptions",function(){return d.SceneOptimizerOptions});c.d(b,"SceneOptimizer",function(){return d.SceneOptimizer}),c.d(b,"SceneSerializer",function(){return d.SceneSerializer}),c.d(b,"SmartArray",function(){return d.SmartArray}),c.d(b,"SmartArrayNoDuplicate",function(){return d.SmartArrayNoDuplicate}),c.d(b,"StringDictionary",function(){return d.StringDictionary}),c.d(b,"Tags",function(){return d.Tags}),c.d(b,"CreateResizedCopy",function(){return d.CreateResizedCopy}),c.d(b,"ApplyPostProcess",function(){return d.ApplyPostProcess}),c.d(b,"ToHalfFloat",function(){return d.ToHalfFloat}),c.d(b,"FromHalfFloat",function(){return d.FromHalfFloat}),c.d(b,"TextureTools",function(){return d.TextureTools}),c.d(b,"GetTGAHeader",function(){return d.GetTGAHeader}),c.d(b,"UploadContent",function(){return d.UploadContent}),c.d(b,"TGATools",function(){return d.TGATools}),c.d(b,"Tools",function(){return d.Tools}),c.d(b,"className",function(){return d.className}),c.d(b,"AsyncLoop",function(){return d.AsyncLoop}),c.d(b,"VideoRecorder",function(){return d.VideoRecorder}),c.d(b,"JoystickAxis",function(){return d.JoystickAxis}),c.d(b,"VirtualJoystick",function(){return d.VirtualJoystick}),c.d(b,"WorkerPool",function(){return d.WorkerPool}),c.d(b,"Logger",function(){return d.Logger}),c.d(b,"RegisterClass",function(){return d.RegisterClass}),c.d(b,"GetClass",function(){return d.GetClass}),c.d(b,"FilesInputStore",function(){return d.FilesInputStore}),c.d(b,"DeepCopier",function(){return d.DeepCopier}),c.d(b,"PivotTools",function(){return d.PivotTools}),c.d(b,"PrecisionDate",function(){return d.PrecisionDate}),c.d(b,"CreateScreenshot",function(){return d.CreateScreenshot}),c.d(b,"CreateScreenshotAsync",function(){return d.CreateScreenshotAsync}),c.d(b,"CreateScreenshotWithResizeAsync",function(){return d.CreateScreenshotWithResizeAsync}),c.d(b,"CreateScreenshotUsingRenderTarget",function(){return d.CreateScreenshotUsingRenderTarget}),c.d(b,"CreateScreenshotUsingRenderTargetAsync",function(){return d.CreateScreenshotUsingRenderTargetAsync}),c.d(b,"ScreenshotTools",function(){return d.ScreenshotTools}),c.d(b,"WebRequest",function(){return d.WebRequest}),c.d(b,"InspectableType",function(){return d.InspectableType}),c.d(b,"GetEnvironmentBRDFTexture",function(){return d.GetEnvironmentBRDFTexture}),c.d(b,"BRDFTextureTools",function(){return d.BRDFTextureTools}),c.d(b,"RGBDTextureTools",function(){return d.RGBDTextureTools}),c.d(b,"ColorGradient",function(){return d.ColorGradient}),c.d(b,"Color3Gradient",function(){return d.Color3Gradient}),c.d(b,"FactorGradient",function(){return d.FactorGradient}),c.d(b,"GradientHelper",function(){return d.GradientHelper}),c.d(b,"PerfCounter",function(){return d.PerfCounter}),c.d(b,"RetryStrategy",function(){return d.RetryStrategy}),c.d(b,"LoadFileError",function(){return d.LoadFileError}),c.d(b,"RequestFileError",function(){return d.RequestFileError}),c.d(b,"ReadFileError",function(){return d.ReadFileError}),c.d(b,"FileToolsOptions",function(){return d.FileToolsOptions}),c.d(b,"SetCorsBehavior",function(){return d.SetCorsBehavior}),c.d(b,"LoadImage",function(){return d.LoadImage}),c.d(b,"ReadFile",function(){return d.ReadFile}),c.d(b,"LoadFile",function(){return d.LoadFile}),c.d(b,"RequestFile",function(){return d.RequestFile}),c.d(b,"IsFileURL",function(){return d.IsFileURL}),c.d(b,"IsBase64DataUrl",function(){return d.IsBase64DataUrl}),c.d(b,"DecodeBase64UrlToBinary",function(){return d.DecodeBase64UrlToBinary}),c.d(b,"DecodeBase64UrlToString",function(){return d.DecodeBase64UrlToString}),c.d(b,"FileTools",function(){return d.FileTools}),c.d(b,"_injectLTSFileTools",function(){return d._injectLTSFileTools}),c.d(b,"EndsWith",function(){return d.EndsWith}),c.d(b,"StartsWith",function(){return d.StartsWith}),c.d(b,"Decode",function(){return d.Decode}),c.d(b,"EncodeArrayBufferToBase64",function(){return d.EncodeArrayBufferToBase64}),c.d(b,"DecodeBase64ToString",function(){return d.DecodeBase64ToString}),c.d(b,"DecodeBase64ToBinary",function(){return d.DecodeBase64ToBinary}),c.d(b,"PadNumber",function(){return d.PadNumber}),c.d(b,"StringTools",function(){return d.StringTools}),c.d(b,"DataReader",function(){return d.DataReader}),c.d(b,"MinMaxReducer",function(){return d.MinMaxReducer}),c.d(b,"DepthReducer",function(){return d.DepthReducer}),c.d(b,"DataStorage",function(){return d.DataStorage}),c.d(b,"SceneRecorder",function(){return d.SceneRecorder}),c.d(b,"KhronosTextureContainer2",function(){return d.KhronosTextureContainer2}),c.d(b,"Trajectory",function(){return d.Trajectory}),c.d(b,"TrajectoryClassifier",function(){return d.TrajectoryClassifier}),c.d(b,"TimerState",function(){return d.TimerState}),c.d(b,"setAndStartTimer",function(){return d.setAndStartTimer}),c.d(b,"AdvancedTimer",function(){return d.AdvancedTimer}),c.d(b,"GenerateBase64StringFromPixelData",function(){return d.GenerateBase64StringFromPixelData}),c.d(b,"GenerateBase64StringFromTexture",function(){return d.GenerateBase64StringFromTexture}),c.d(b,"GenerateBase64StringFromTextureAsync",function(){return d.GenerateBase64StringFromTextureAsync}),c.d(b,"CopyTools",function(){return d.CopyTools}),c.d(b,"Reflector",function(){return d.Reflector}),c.d(b,"IsWindowObjectExist",function(){return d.IsWindowObjectExist}),c.d(b,"IsNavigatorAvailable",function(){return d.IsNavigatorAvailable}),c.d(b,"IsDocumentAvailable",function(){return d.IsDocumentAvailable}),c.d(b,"GetDOMTextContent",function(){return d.GetDOMTextContent}),c.d(b,"DomManagement",function(){return d.DomManagement}),c.d(b,"ComputePressureObserverWrapper",function(){return d.ComputePressureObserverWrapper}),c.d(b,"PerformanceViewerCollector",function(){return d.PerformanceViewerCollector}),c.d(b,"PerfCollectionStrategy",function(){return d.PerfCollectionStrategy}),c.d(b,"DynamicFloat32Array",function(){return d.DynamicFloat32Array}),c.d(b,"WebXRCamera",function(){return d.WebXRCamera}),c.d(b,"WebXREnterExitUIButton",function(){return d.WebXREnterExitUIButton}),c.d(b,"WebXREnterExitUIOptions",function(){return d.WebXREnterExitUIOptions}),c.d(b,"WebXREnterExitUI",function(){return d.WebXREnterExitUI}),c.d(b,"WebXRExperienceHelper",function(){return d.WebXRExperienceHelper}),c.d(b,"WebXRInput",function(){return d.WebXRInput}),c.d(b,"WebXRInputSource",function(){return d.WebXRInputSource}),c.d(b,"WebXRManagedOutputCanvasOptions",function(){return d.WebXRManagedOutputCanvasOptions}),c.d(b,"WebXRManagedOutputCanvas",function(){return d.WebXRManagedOutputCanvas}),c.d(b,"WebXRState",function(){return d.WebXRState}),c.d(b,"WebXRTrackingState",function(){return d.WebXRTrackingState}),c.d(b,"WebXRSessionManager",function(){return d.WebXRSessionManager}),c.d(b,"WebXRDefaultExperienceOptions",function(){return d.WebXRDefaultExperienceOptions}),c.d(b,"WebXRDefaultExperience",function(){return d.WebXRDefaultExperience}),c.d(b,"WebXRFeatureName",function(){return d.WebXRFeatureName}),c.d(b,"WebXRFeaturesManager",function(){return d.WebXRFeaturesManager}),c.d(b,"WebXRAbstractFeature",function(){return d.WebXRAbstractFeature}),c.d(b,"WebXRHitTestLegacy",function(){return d.WebXRHitTestLegacy}),c.d(b,"WebXRAnchorSystem",function(){return d.WebXRAnchorSystem}),c.d(b,"WebXRPlaneDetector",function(){return d.WebXRPlaneDetector}),c.d(b,"WebXRBackgroundRemover",function(){return d.WebXRBackgroundRemover}),c.d(b,"WebXRMotionControllerTeleportation",function(){return d.WebXRMotionControllerTeleportation}),c.d(b,"WebXRControllerPointerSelection",function(){return d.WebXRControllerPointerSelection}),c.d(b,"IWebXRControllerPhysicsOptions",function(){return d.IWebXRControllerPhysicsOptions}),c.d(b,"WebXRControllerPhysics",function(){return d.WebXRControllerPhysics}),c.d(b,"WebXRHitTest",function(){return d.WebXRHitTest}),c.d(b,"WebXRFeaturePointSystem",function(){return d.WebXRFeaturePointSystem}),c.d(b,"WebXRHand",function(){return d.WebXRHand}),c.d(b,"WebXRHandTracking",function(){return d.WebXRHandTracking}),c.d(b,"WebXRMeshDetector",function(){return d.WebXRMeshDetector}),c.d(b,"WebXRImageTracking",function(){return d.WebXRImageTracking}),c.d(b,"WebXRNearInteraction",function(){return d.WebXRNearInteraction}),c.d(b,"WebXRDomOverlay",function(){return d.WebXRDomOverlay}),c.d(b,"WebXRControllerMovement",function(){return d.WebXRControllerMovement}),c.d(b,"WebXRLightEstimation",function(){return d.WebXRLightEstimation}),c.d(b,"WebXREyeTracking",function(){return d.WebXREyeTracking}),c.d(b,"WebXRWalkingLocomotion",function(){return d.WebXRWalkingLocomotion}),c.d(b,"WebXRAbstractMotionController",function(){return d.WebXRAbstractMotionController}),c.d(b,"WebXRControllerComponent",function(){return d.WebXRControllerComponent}),c.d(b,"WebXRGenericHandController",function(){return d.WebXRGenericHandController}),c.d(b,"WebXRGenericTriggerMotionController",function(){return d.WebXRGenericTriggerMotionController}),c.d(b,"WebXRMicrosoftMixedRealityController",function(){return d.WebXRMicrosoftMixedRealityController}),c.d(b,"WebXRMotionControllerManager",function(){return d.WebXRMotionControllerManager}),c.d(b,"WebXROculusTouchMotionController",function(){return d.WebXROculusTouchMotionController}),c.d(b,"WebXRHTCViveMotionController",function(){return d.WebXRHTCViveMotionController}),c.d(b,"WebXRProfiledMotionController",function(){return d.WebXRProfiledMotionController});a=void 0!==a?a:"undefined"!=typeof window?window:void 0;if(void 0!==a){a.BABYLON=f,a.BABYLON=a.BABYLON||{};var f=a.BABYLON;f.Debug=f.Debug||{};a=[];for(var g in e)f.Debug[g]=e[g],a.push(g);for(var g in d)f[g]=d[g]}var h={AxesViewer:e.AxesViewer,BoneAxesViewer:e.BoneAxesViewer,PhysicsViewer:e.PhysicsViewer,SkeletonViewer:e.SkeletonViewer}}).call(this,c(189))}])})}var j=!1;function k(){j||(j=!0,i());return h.exports}function b(a){switch(a){case void 0:return k()}}e.exports=b}),null);
-----
popmotion-9.3.6",["tslib-2.2.0","hey-listen-1.0.8","style-value-types-4.1.4","framesync-5.3.0"],(function(a,b,c,d,e,f){"use strict";function a(a){return a&&typeof a==="object"&&"default"in a?a["default"]:a}var g=a(b("tslib-2.2.0")),h=a(b("hey-listen-1.0.8")),i=a(b("style-value-types-4.1.4")),j=a(b("framesync-5.3.0")),k={},l={exports:k};function m(){Object.defineProperty(k,"__esModule",{value:!0});var a=g(),b=h(),c=i(),d=j();function e(a){return a&&typeof a==="object"&&"default"in a?a:{"default":a}}var f=e(d),l=function(a,b,c){return Math.min(Math.max(c,a),b)},m=.001,n=.01,o=10,p=.05,q=1;function r(a){var c=a.duration,d=c===void 0?800:c;c=a.bounce;c=c===void 0?.25:c;var e=a.velocity,f=e===void 0?0:e;e=a.mass;a=e===void 0?1:e;var g;b.warning(d<=o*1e3,"Spring duration must be 10 seconds or less");var h=1-c;h=l(p,q,h);d=l(n,o,d/1e3);h<1?(g=function(a){var b=a*h,c=b*d;b=b-f;a=u(a,h);c=Math.exp(-c);return m-b/a*c},e=function(a){var b=a*h;b=b*d;var c=b*f+f,e=Math.pow(h,2)*Math.pow(a,2)*d;b=Math.exp(-b);var i=u(Math.pow(a,2),h);a=-g(a)+m>0?-1:1;return a*((c-e)*b)/i}):(g=function(b){var a=Math.exp(-b*d);b=(b-f)*d+1;return-m+a*b},e=function(b){var a=Math.exp(-b*d);b=(f-b)*(d*d);return a*b});c=5/d;e=t(g,e,c);d=d*1e3;if(isNaN(e))return{stiffness:100,damping:10,duration:d};else{c=Math.pow(e,2)*a;return{stiffness:c,damping:h*2*Math.sqrt(a*c),duration:d}}}var s=12;function t(a,b,c){c=c;for(var d=1;d=m;h.value=h.done?e:b;return h},flipTarget:function(){var a;l=-l;a=[e,d],d=a[0],e=a[1];q()}}}z.needsInterpolation=function(a,b){return typeof a==="string"||typeof b==="string"};var A=function(a){return 0},B=function(a,b,c){b=b-a;return b===0?1:(c-a)/b},C=function(b,c,a){return-a*b+a*c+b},D=function(a,b,c){a=a*a;b=b*b;return Math.sqrt(Math.max(0,c*(b-a)+a))},aa=[c.hex,c.rgba,c.hsla],E=function(a){return aa.find(function(b){return b.test(a)})},F=function(a){return"""+a+"" is not an animatable color. Use the equivalent color code instead."},G=function(d,e){var f=E(d),g=E(e);b.invariant(!!f,F(d));b.invariant(!!g,F(e));b.invariant(f.transform===g.transform,"Both colors must be hex/RGBA, OR both must be HSLA.");var h=f.parse(d),i=g.parse(e),j=a.__assign({},h),k=f===c.hsla?C:D;return function(a){for(var b in j)b!=="alpha"&&(j[b]=k(h[b],i[b],a));j.alpha=C(h.alpha,i.alpha,a);return f.transform(j)}},ba={x:0,y:0,z:0},H=function(a){return typeof a==="number"},ca=function(a,b){return function(c){return b(a(c))}},I=function(){var a=[];for(var b=0;b=g.numNumbers,"Complex values ""+a+"" and ""+d+"" too different to mix. Ensure all colors are of the same type.");return I(K(f.parsed,g.parsed),e)},fa=function(a,b){return function(c){return C(a,b,c)}};function ga(a){if(typeof a==="number")return fa;else if(typeof a==="string")if(c.color.test(a))return G;else return L;else if(Array.isArray(a))return K;else if(typeof a==="object")return da}function ha(a,b,c){var d=[];c=c||ga(a[0]);var e=a.length-1;for(var f=0;f=a[d]&&(f=d-1,g=!0);if(!g){g=1;for(;ge||g===d)break;f=g-1}g=B(a[f],a[f+1],e);return b[f](g)}}function M(a,c,d){d=d===void 0?{}:d;var e=d.clamp;e=e===void 0?!0:e;var f=d.ease;d=d.mixer;var g=a.length;b.invariant(g===c.length,"Both input and output ranges must be the same length");b.invariant(!f||!Array.isArray(f)||f.length===g-1,"Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.");a[0]>a[g-1]&&(a=[].concat(a),c=[].concat(c),a.reverse(),c.reverse());c=ha(c,f,d);var h=g===2?ia(a,c):ja(a,c);return e?function(b){return h(l(a[0],a[g-1],b))}:h}e=function(a){return function(b){return 1-a(1-b)}};var N=function(a){return function(b){return b<=.5?a(2*b)/2:(2-a(2*(1-b)))/2}},ka=function(a){return function(b){return Math.pow(b,a)}},O=function(a){return function(b){return b*b*((a+1)*b-a)}},la=function(a){var b=O(a);return function(a){return(a*=2)<1?.5*b(a):.5*(2-Math.pow(2,-10*(a-1)))}},P=1.525,ma=4/11,na=8/11,oa=9/10,pa=function(a){return a},Q=ka(2),qa=e(Q),ra=N(Q),sa=function(a){return 1-Math.sin(Math.acos(a))},ta=e(sa),ua=N(ta),R=O(P),va=e(R),wa=N(R);P=la(P);var xa=4356/361,ya=35442/1805,za=16061/1805,S=function(a){if(a===1||a===0)return a;var b=a*a;return a=f;return g},flipTarget:function(){h.reverse(),k=j()}}}function Fa(a){var b=a.velocity;b=b===void 0?0:b;var c=a.from;c=c===void 0?0:c;var d=a.power;d=d===void 0?.8:d;var e=a.timeConstant,f=e===void 0?350:e;e=a.restDelta;var g=e===void 0?.5:e;e=a.modifyTarget;var h={done:!1,value:c},i=d*b;a=c+i;var j=e===void 0?a:e(a);j!==a&&(i=j-c);return{next:function(a){a=-i*Math.exp(-a/f);h.done=!(a>g||a<-g);h.value=h.done?j:j+a;return h},flipTarget:function(){}}}var Ga={keyframes:T,spring:z,decay:Fa};function Ha(a){if(Array.isArray(a.to))return T;else if(Ga[a.type])return Ga[a.type];a=new Set(Object.keys(a));if(a.has("ease")||a.has("duration")&&!a.has("dampingRatio"))return T;else if(a.has("dampingRatio")||a.has("stiffness")||a.has("mass")||a.has("damping")||a.has("restSpeed")||a.has("restDelta"))return z;return T}function Ia(a,b,c){c===void 0&&(c=0);return a-b-c}function Ja(a,b,c,d){c===void 0&&(c=0);d===void 0&&(d=!0);return d?Ia(b+-a,b,c):b-(a-b)+c}function Ka(a,b,c,d){return d?a>=b+c:a<=-c}var La=function(a){var b=function(b){b=b.delta;return a(b)};return{start:function(){return f["default"].update(b,!0)},stop:function(){return d.cancelSync.update(b)}}};function Ma(b){var c,d,e=b.from,f=b.autoplay;f=f===void 0?!0:f;var g=b.driver,h=g===void 0?La:g;g=b.elapsed;var i=g===void 0?0:g;g=b.repeat;var j=g===void 0?0:g;g=b.repeatType;var k=g===void 0?"loop":g;g=b.repeatDelay;var l=g===void 0?0:g,m=b.onPlay,n=b.onStop,o=b.onComplete,p=b.onRepeat,q=b.onUpdate;g=a.__rest(b,["from","autoplay","driver","elapsed","repeat","repeatType","repeatDelay","onPlay","onStop","onComplete","onRepeat","onUpdate"]);b=g.to;var r,s=0,t=g.duration,u,v=!1,w=!0,x,y=Ha(g);((d=(c=y).needsInterpolation)===null||d===void 0?void 0:d.call(c,e,b))&&(x=M([0,100],[e,b],{clamp:!1}),e=0,b=100);var z=y(a.__assign(a.__assign({},g),{from:e,to:b}));function A(){s++,k==="reverse"?(w=s%2===0,i=Ja(i,t,l,w)):(i=Ia(i,t,l),k==="mirror"&&z.flipTarget()),v=!1,p&&p()}function B(){r.stop(),o&&o()}function C(a){w||(a=-a);i+=a;if(!v){a=z.next(Math.max(0,i));u=a.value;x&&(u=x(u));v=w?a.done:i<=0}q===null||q===void 0?void 0:q(u);v&&(s===0&&(t!==null&&t!==void 0?t:t=i),sg}function s(a){if(f===void 0)return g;return g===void 0?f:Math.abs(f-a)v||w===-1&&af)return a[b-1];if(b===c-1)return g;f=d}}}};function bb(a,b){return a/(1e3/b)}var cb=function(a,b,c){b=b-a;return((c-a)%b+b)%b+a},db=function(a,b){return 1-3*b+3*a},eb=function(a,b){return 3*b-6*a},fb=function(a){return 3*a},Y=function(a,b,c){return((db(b,c)*a+eb(b,c))*a+fb(b))*a},gb=function(a,b,c){return 3*db(b,c)*a*a+2*eb(b,c)*a+fb(b)},hb=1e-7,ib=10;function jb(a,b,c,d,e){var f,g,h=0;do g=b+(c-b)/2,f=Y(g,d,e)-a,f>0?c=g:b=g;while(Math.abs(f)>hb&&++h=lb)return mb(b,f,a,c);else if(g===0)return f;else return jb(b,d,d+$,a,c)}return function(a){return a===0||a===1?a:Y(g(a),b,d)}}var ob=function(a,b){b===void 0&&(b="end");return function(c){c=b==="end"?Math.min(c,.999):Math.max(c,.001);c=c*a;c=b==="end"?Math.floor(c):Math.ceil(c);return l(0,1,c/a)}};k.angle=Qa;k.animate=Ma;k.anticipate=P;k.applyOffset=Ra;k.attract=Ta;k.attractExpo=Ua;k.backIn=R;k.backInOut=wa;k.backOut=va;k.bounceIn=Aa;k.bounceInOut=Ba;k.bounceOut=S;k.circIn=sa;k.circInOut=ua;k.circOut=ta;k.clamp=l;k.createAnticipate=la;k.createAttractor=U;k.createBackIn=O;k.createExpoIn=ka;k.cubicBezier=nb;k.decay=Fa;k.degreesToRadians=Va;k.distance=Wa;k.easeIn=Q;k.easeInOut=ra;k.easeOut=qa;k.inertia=Oa;k.interpolate=M;k.isPoint=V;k.isPoint3D=W;k.keyframes=T;k.linear=pa;k.mirrorEasing=N;k.mix=C;k.mixColor=G;k.mixComplex=L;k.pipe=I;k.pointFromVector=Xa;k.progress=B;k.radiansToDegrees=Pa;k.reverseEasing=e;k.smooth=$a;k.smoothFrame=Za;k.snap=ab;k.spring=z;k.steps=ob;k.toDecimal=Ya;k.velocityPerFrame=bb;k.velocityPerSecond=Na;k.wrap=cb}var n=!1;function o(){n||(n=!0,m());return l.exports}function c(a){switch(a){case void 0:return o()}}e.exports=c}),null); /** * License: https://www.facebook.com/legal/license/t3hOLs8wlXy/ */
-----
framer-motion-4.1.16",["tslib-2.2.0","react-0.0.0","hey-listen-1.0.8","style-value-types-4.1.4","framesync-5.3.0","popmotion-9.3.6","emotion-is-prop-valid-0.8.8"],(function(a,b,c,d,e,f){"use strict";var g=b("tslib-2.2.0"),h=b("react-0.0.0"),i=b("hey-listen-1.0.8"),j=b("style-value-types-4.1.4"),k=b("framesync-5.3.0"),l=b("popmotion-9.3.6"),m=b("emotion-is-prop-valid-0.8.8"),n={},o={exports:n};function p(){Object.defineProperty(n,"__esModule",{value:!0});var a=g(),b=h(),c=i(),d=j(),e=k(),f=l();function o(a){return a&&typeof a==="object"&&"default"in a?a:{"default":a}}function p(a){if(a&&a.__esModule)return a;var b=Object.create(null);a&&Object.keys(a).forEach(function(c){if(c!=="default"){var d=Object.getOwnPropertyDescriptor(a,c);Object.defineProperty(b,c,d.get?d:{enumerable:!0,get:function(){return a[c]}})}});b["default"]=a;return Object.freeze(b)}var q=p(b),r=o(b),s=o(e);p=function(a){return{isEnabled:function(b){return a.some(function(a){return!!b[a]})}}};var t={measureLayout:p(["layout","layoutId","drag","_layoutResetTransform"]),animation:p(["animate","exit","variants","whileHover","whileTap","whileFocus","whileDrag"]),exit:p(["exit"]),drag:p(["drag","dragControls"]),focus:p(["whileFocus"]),hover:p(["whileHover","onHoverStart","onHoverEnd"]),tap:p(["whileTap","onTap","onTapStart","onTapCancel"]),pan:p(["onPan","onPanStart","onPanSessionStart","onPanEnd"]),layoutAnimation:p(["layout","layoutId"])};function u(a){for(var b in a){var c=a[b];c!==null&&(t[b].Component=c)}}var v=b.createContext({strict:!1}),w=Object.keys(t),x=w.length;function y(c,d,e){e=[];b.useContext(v);if(!d)return null;for(var f=0;f-1||/[A-Z]/.test(a))return!0;return!1}var ra={};function sa(a){for(var b in a)ra[b]=a[b]}var ta=["","X","Y","Z"];o=["translate","scale","rotate","skew"];var ua=["transformPerspective","x","y","z"];o.forEach(function(a){return ta.forEach(function(b){return ua.push(a+b)})});function va(a,b){return ua.indexOf(a)-ua.indexOf(b)}var wa=new Set(ua);function xa(a){return wa.has(a)}var ya=new Set(["originX","originY","originZ"]);function za(a){return ya.has(a)}function Aa(a,b){var c=b.layout;b=b.layoutId;return xa(a)||za(a)||(c||b!==void 0)&&(!!ra[a]||a==="opacity")}var P=function(a){return a!==null&&typeof a==="object"&&a.getVelocity},Ba={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};function Ca(b,c,d,e){var a=b.transform;b=b.transformKeys;var f=c.enableHardwareAcceleration;f=f===void 0?!0:f;c=c.allowTransformNone;c=c===void 0?!0:c;var g="";b.sort(va);var h=!1,i=b.length;for(var j=0;j-1&&a.splice(b,1)}var zc=function(){function a(){this.subscriptions=[]}a.prototype.add=function(a){var b=this;xc(this.subscriptions,a);return function(){return yc(b.subscriptions,a)}};a.prototype.notify=function(a,b,c){var d=this.subscriptions.length;if(!d)return;if(d===1)this.subscriptions[0](a,b,c);else for(var e=0;ep&&s;s=Array.isArray(r)?r:[r];var v=s.reduce(g,{});t===!1&&(v={});t=q.prevResolvedValues;t=t===void 0?{}:t;var w=a.__assign(a.__assign({},t),v),x=function(a){b=!0,n["delete"](a),q.needsAnimating[a]=!0};for(w in w){var y=v[w],z=t[w];if(o.hasOwnProperty(w))continue;y!==z?kb(y)&&kb(z)?!$b(y,z)?x(w):q.protectedKeys[w]=!0:y!==void 0?x(w):n.add(w):y!==void 0&&n.has(w)?x(w):q.protectedKeys[w]=!0}q.prevProp=r;q.prevResolvedValues=v;q.isActive&&(o=a.__assign(a.__assign({},o),v));f&&c.blockInitialAnimation&&(b=!1);b&&!u&&m.push.apply(m,a.__spreadArray([],a.__read(s.map(function(b){return{animation:b,options:a.__assign({type:e},h)}}))))};for(var r=0;r=3;if(!d&&!h)return;h=c.point;var b=e.getFrameData().timestamp;g.history.push(a.__assign(a.__assign({},h),{timestamp:b}));h=g.handlers;b=h.onStart;h=h.onMove;d||(b&&b(g.lastMoveEvent,c),g.startEvent=g.lastMoveEvent);h&&h(g.lastMoveEvent,c)};this.handlePointerMove=function(a,b){g.lastMoveEvent=a;g.lastMoveEventInfo=cd(b,g.transformPagePoint);if(vb(a)&&a.buttons===0){g.handlePointerUp(a,b);return}s["default"].update(g.updatePoint,!0)};this.handlePointerUp=function(a,c){g.end();var b=g.handlers,d=b.onEnd;b=b.onSessionEnd;c=ed(cd(c,g.transformPagePoint),g.history);g.startEvent&&d&&d(a,c);b&&b(a,c)};if(wb(b)&&b.touches.length>1)return;this.handlers=c;this.transformPagePoint=d;d=Bb(b);d=cd(d,this.transformPagePoint);var h=d.point,i=e.getFrameData().timestamp;this.history=[a.__assign(a.__assign({},h),{timestamp:i})];h=c.onSessionStart;h&&h(b,ed(d,this.history));this.removeListeners=f.pipe(Kb(window,"pointermove",this.handlePointerMove),Kb(window,"pointerup",this.handlePointerUp),Kb(window,"pointercancel",this.handlePointerUp))}b.prototype.updateHandlers=function(a){this.handlers=a};b.prototype.end=function(){this.removeListeners&&this.removeListeners(),e.cancelSync.update(this.updatePoint)};return b}();function cd(a,b){return b?{point:b(a.point)}:a}function dd(a,b){return{x:a.x-b.x,y:a.y-b.y}}function ed(a,b){a=a.point;return{point:a,delta:dd(a,gd(b)),offset:dd(a,fd(b)),velocity:hd(b,.1)}}function fd(a){return a[0]}function gd(a){return a[a.length-1]}function hd(a,b){if(a.length<2)return{x:0,y:0};var c=a.length-1,d=null,e=gd(a);while(c>=0){d=a[c];if(e.timestamp-d.timestamp>ac(b))break;c--}if(!d)return{x:0,y:0};a=(e.timestamp-d.timestamp)/1e3;if(a===0)return{x:0,y:0};c={x:(e.x-d.x)/a,y:(e.y-d.y)/a};c.x===Infinity&&(c.x=0);c.y===Infinity&&(c.y=0);return c}function id(a){return a}function jd(a){var b=a.top,c=a.left,d=a.right;a=a.bottom;return{x:{min:c,max:d},y:{min:b,max:a}}}function kd(a){var b=a.x;a=a.y;return{top:a.min,bottom:a.max,left:b.min,right:b.max}}function ld(b,a){var c=b.top,d=b.left,e=b.bottom;b=b.right;a===void 0&&(a=id);d=a({x:d,y:c});c=a({x:b,y:e});return{top:d.y,left:d.x,bottom:c.y,right:c.x}}function W(){return{x:{min:0,max:1},y:{min:0,max:1}}}function md(b){return{x:a.__assign({},b.x),y:a.__assign({},b.y)}}var nd={translate:0,scale:1,origin:0,originPoint:0};function od(){return{x:a.__assign({},nd),y:a.__assign({},nd)}}function X(a){return[a("x"),a("y")]}var pd=function(a){return f.clamp(0,1,a)};function qd(a,b,c){b===void 0&&(b=0);c===void 0&&(c=.01);return f.distance(a,b)d?c=f.progress(b.min,b.max-d,a.min):d>e&&(c=f.progress(a.min,a.max-e,b.min));return pd(c)}function td(a,b,c,d){d===void 0&&(d=.5),a.origin=d,a.originPoint=f.mix(b.min,b.max,a.origin),a.scale=rd(c)/rd(b),qd(a.scale,1,1e-4)&&(a.scale=1),a.translate=f.mix(c.min,c.max,a.origin)-a.originPoint,qd(a.translate)&&(a.translate=0)}function ud(a,b,c,d){td(a.x,b.x,c.x,vd(d.originX)),td(a.y,b.y,c.y,vd(d.originY))}function vd(a){return typeof a==="number"?a:.5}function wd(a,b,c){a.min=c.min+b.min,a.max=a.min+rd(b)}function xd(a,b){wd(a.target.x,a.relativeTarget.x,b.target.x),wd(a.target.y,a.relativeTarget.y,b.target.y)}function yd(a,b,c){var d=b.min;b=b.max;d!==void 0&&ab&&(a=c?f.mix(b,a,c.max):Math.min(a,b));return a}function zd(a,b,c,d,e){a=a-b*c;return d?yd(a,d,e):a}function Ad(a,b,c){return{min:b!==void 0?a.min+b:void 0,max:c!==void 0?a.max+c-(a.max-a.min):void 0}}function Bd(a,b){var c=b.top,d=b.left,e=b.bottom;b=b.right;return{x:Ad(a.x,d,b),y:Ad(a.y,c,e)}}function Cd(b,c){var d=c.min-b.min,e=c.max-b.max;c.max-c.minb?c="y":Math.abs(a.x)>b&&(c="x");return c}function qe(c){var d=c.dragControls,e=c.visualElement,f=b.useContext(z).transformPagePoint,g=D(function(){return new ne({visualElement:e})});g.setProps(a.__assign(a.__assign({},c),{transformPagePoint:f}));b.useEffect(function(){return d&&d.subscribe(g)},[g]);b.useEffect(function(){return g.mount(e)},[])}function Y(a){var c=a.onPan,d=a.onPanStart,e=a.onPanEnd,f=a.onPanSessionStart;a=a.visualElement;var g=c||d||e||f,h=b.useRef(null),i=b.useContext(z).transformPagePoint,j={onSessionStart:f,onStart:d,onMove:c,onEnd:function(a,b){h.current=null,e&&e(a,b)}};b.useEffect(function(){h.current!==null&&h.current.updateHandlers(j)});function k(a){h.current=new bd(a,j,{transformPagePoint:i})}Lb(a,"pointerdown",g&&k);Zb(function(){return h.current&&h.current.end()})}U={pan:p(Y),drag:p(qe)};var Z;(function(a){a[a.Entering=0]="Entering",a[a.Present=1]="Present",a[a.Exiting=2]="Exiting"})(Z||(Z={}));n.VisibilityAction=void 0;(function(a){a[a.Hide=0]="Hide",a[a.Show=1]="Show"})(n.VisibilityAction||(n.VisibilityAction={}));function re(a){return typeof a==="string"&&a.startsWith("var(--")}var se=/var((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?)/;function te(b){b=se.exec(b);if(!b)return[,];b=a.__read(b,3);var c=b[1];b=b[2];return[c,b]}var ue=4;function ve(b,d,e){e===void 0&&(e=1);c.invariant(e<=ue,"Max CSS variable fallback depth detected in property ""+b+"". This may indicate a circular fallback dependency.");b=a.__read(te(b),2);var f=b[0];b=b[1];if(!f)return;f=window.getComputedStyle(d).getPropertyValue(f);if(f)return f.trim();else if(re(b))return ve(b,d,e+1);else return b}function we(b,c,d){c=a.__rest(c,[]);var e=b.getInstance();if(!(e instanceof HTMLElement))return{target:c,transitionEnd:d};d&&(d=a.__assign({},d));b.forEachValue(function(b){var a=b.get();if(!re(a))return;a=ve(a,e);a&&b.set(a)});for(b in c){var f=c[b];if(!re(f))continue;var g=ve(f,e);if(!g)continue;c[b]=g;d&&((g=d[b])!==null&&g!==void 0?g:d[b]=f)}return{target:c,transitionEnd:d}}function xe(a,b){return a/(b.max-b.min)*100}function ye(a,b,c){b=c.target;if(typeof a==="string")if(d.px.test(a))a=parseFloat(a);else return a;c=xe(a,b.x);a=xe(a,b.y);return c+"% "+a+"%"}var ze="_$css";function Ae(b,c){var a=c.delta;c=c.treeScale;var e=b,g=b.includes("var("),h=[];g&&(b=b.replace(se,function(a){h.push(a);return ze}));var i=d.complex.parse(b);if(i.length>5)return e;e=d.complex.createTransformer(b);b=typeof i[0]!=="number"?1:0;var j=a.x.scale*c.x;a=a.y.scale*c.y;i[0+b]/=j;i[1+b]/=a;c=f.mix(j,a,.5);typeof i[2+b]==="number"&&(i[2+b]/=c);typeof i[3+b]==="number"&&(i[3+b]/=c);j=e(i);if(g){var k=0;j=j.replace(ze,function(){var a=h[k];k++;return a})}return j}Y={process:ye};var Be={borderRadius:a.__assign(a.__assign({},Y),{applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]}),borderTopLeftRadius:Y,borderTopRightRadius:Y,borderBottomLeftRadius:Y,borderBottomRightRadius:Y,boxShadow:{process:Ae}},Ce=1e3,De=function(c){a.__extends(b,c);function b(){var b=c!==null&&c.apply(this,arguments)||this;b.frameTarget=W();b.currentAnimationTarget=W();b.isAnimating={x:!1,y:!1};b.stopAxisAnimation={x:void 0,y:void 0};b.isAnimatingTree=!1;b.animate=function(c,d,e){e===void 0&&(e={});var f=e.originBox,g=e.targetBox,h=e.visibilityAction,i=e.shouldStackAnimate,j=e.onComplete,k=e.prevParent,l=a.__rest(e,["originBox","targetBox","visibilityAction","shouldStackAnimate","onComplete","prevParent"]);e=b.props;var m=e.visualElement,o=e.layout;if(i===!1){b.isAnimatingTree=!1;return b.safeToRemove()}if(b.isAnimatingTree&&i!==!0)return;else i&&(b.isAnimatingTree=!0);d=f||d;c=g||c;var p=!1;e=m.getProjectionParent();if(e){i=e.prevViewportBox;var q=e.getLayoutState().layout;k&&(g&&(q=k.getLayoutState().layout),f&&!Ud(k,e)&&k.prevViewportBox&&(i=k.prevViewportBox));i&&Ke(k,f,g)&&(p=!0,d=Td(i,d),c=Td(q,c))}var r=Fe(d,c);e=X(function(e){var f;if(o==="position"){var g=c[e].max-c[e].min;d[e].max=d[e].min+g}if(m.projection.isTargetLocked)return;else if(h!==void 0)m.setVisibility(h===n.VisibilityAction.Show);else if(r)return b.animateAxis(e,c[e],d[e],a.__assign(a.__assign({},l),{isRelative:p}));else{(f=(g=b.stopAxisAnimation)[e])===null||f===void 0?void 0:f.call(g);return m.setProjectionTargetAxis(e,c[e].min,c[e].max,p)}});m.syncRender();return Promise.all(e).then(function(){b.isAnimatingTree=!1,j&&j(),m.notifyLayoutAnimationComplete()})};return b}b.prototype.componentDidMount=function(){var a=this,b=this.props.visualElement;b.animateMotionValue=vc;b.enableLayoutProjection();this.unsubLayoutReady=b.onLayoutUpdate(this.animate);b.layoutSafeToRemove=function(){return a.safeToRemove()};sa(Be)};b.prototype.componentWillUnmount=function(){var a=this;this.unsubLayoutReady();X(function(b){var c;return(b=(c=a.stopAxisAnimation)[b])===null||b===void 0?void 0:b.call(c)})};b.prototype.animateAxis=function(a,b,c,d){var e=this,f;d=d===void 0?{}:d;var g=d.transition,h=d.isRelative;if(this.isAnimating[a]&&Ie(b,this.currentAnimationTarget[a]))return;(f=(d=this.stopAxisAnimation)[a])===null||f===void 0?void 0:f.call(d);this.isAnimating[a]=!0;var i=this.props.visualElement,j=this.frameTarget[a],k=i.getProjectionAnimationProgress()[a];k.clearListeners();k.set(0);k.set(0);f=function(){var d=k.get()/Ce;Rd(j,c,b,d);i.setProjectionTargetAxis(a,j.min,j.max,h)};f();var l=k.onChange(f);this.stopAxisAnimation[a]=function(){e.isAnimating[a]=!1,k.stop(),l()};this.currentAnimationTarget[a]=b;d=g||i.getDefaultTransition()||Je;f=vc(a==="x"?"layoutX":"layoutY",k,Ce,d&&uc(d,"layout")).then(this.stopAxisAnimation[a]);return f};b.prototype.safeToRemove=function(){var a,b;(b=(a=this.props).safeToRemove)===null||b===void 0?void 0:b.call(a)};b.prototype.render=function(){return null};return b}(q.Component);function Ee(b){var c=a.__read(E(),2);c=c[1];return q.createElement(De,a.__assign({},b,{safeToRemove:c}))}function Fe(a,b){return!He(a)&&!He(b)&&(!Ie(a.x,b.x)||!Ie(a.y,b.y))}var Ge={min:0,max:0};function He(a){return Ie(a.x,Ge)&&Ie(a.y,Ge)}function Ie(a,b){return a.min===b.min&&a.max===b.max}var Je={duration:.45,ease:[.4,0,.1,1]};function Ke(a,b,c){return a||!a&&!(b||c)}var Le={layoutReady:function(a){return a.notifyLayoutReady()}};function Me(){var b=new Set();return{add:function(a){return b.add(a)},flush:function(c){c=c===void 0?Le:c;var d=c.layoutReady,f=c.parent;Ub(function(c,g){var h=Array.from(b).sort(Ld),i=f?Nd(f):[];g(function(){var b=a.__spreadArray(a.__spreadArray([],a.__read(i)),a.__read(h));b.forEach(function(a){return a.resetTransform()})});c(function(){h.forEach(Pd)});g(function(){i.forEach(function(a){return a.restoreTransform()}),h.forEach(d)});c(function(){h.forEach(function(a){a.isPresent&&(a.presence=Z.Present)})});g(function(){e.flushSync.preRender(),e.flushSync.render()});c(function(){s["default"].postRender(function(){return h.forEach(Ne)}),b.clear()})});Vb()}}}function Ne(a){a.prevViewportBox=a.projection.target}var Oe=b.createContext(Me()),Pe=b.createContext(Me());function $(a){return!!a.forceUpdate}var Qe=function(c){a.__extends(b,c);function b(){return c!==null&&c.apply(this,arguments)||this}b.prototype.componentDidMount=function(){var a=this.props,b=a.syncLayout,c=a.framerSyncLayout,d=a.visualElement;$(b)&&b.register(d);$(c)&&c.register(d);d.onUnmount(function(){$(b)&&b.remove(d),$(c)&&c.remove(d)})};b.prototype.getSnapshotBeforeUpdate=function(){var a=this.props,b=a.syncLayout;a=a.visualElement;$(b)?b.syncUpdate():(Qd(a),b.add(a));return null};b.prototype.componentDidUpdate=function(){var a=this.props.syncLayout;$(a)||a.flush()};b.prototype.render=function(){return null};return b}(r["default"].Component);function Re(c){var d=b.useContext(Oe),e=b.useContext(Pe);return r["default"].createElement(Qe,a.__assign({},c,{syncLayout:d,framerSyncLayout:e}))}p={measureLayout:Re,layoutAnimation:Ee};var Se=function(){return{isEnabled:!1,isHydrated:!1,isTargetLocked:!1,target:W(),targetFinal:W()}};function Te(){return{isHydrated:!1,layout:W(),layoutCorrected:W(),treeScale:{x:1,y:1},delta:od(),deltaFinal:od(),deltaTransform:""}}qe=Te();function Ue(a,b,c){var d=a.x;a=a.y;var e=d.translate/b.x;b=a.translate/b.y;e="translate3d("+e+"px, "+b+"px, 0) ";if(c){b=c.rotate;var f=c.rotateX,g=c.rotateY;b&&(e+="rotate("+b+") ");f&&(e+="rotateX("+f+") ");g&&(e+="rotateY("+g+") ")}e+="scale("+d.scale+", "+a.scale+")";return!c&&e===We?"":e}function Ve(a){a=a.deltaFinal;return a.x.origin*100+"% "+a.y.origin*100+"% 0"}var We=Ue(qe.delta,qe.treeScale,{x:1,y:1}),Xe=["LayoutMeasure","BeforeLayoutMeasure","LayoutUpdate","ViewportBoxUpdate","Update","Render","AnimationComplete","LayoutAnimationComplete","AnimationStart","SetAxisTarget","Unmount"];function Ye(){var b=Xe.map(function(){return new zc()}),c={},d={clearAllListeners:function(){return b.forEach(function(a){return a.clear()})},updatePropListeners:function(a){return Xe.forEach(function(b){var e;(e=c[b])===null||e===void 0?void 0:e.call(c);e="on"+b;var f=a[e];f&&(c[b]=d[e](f))})}};b.forEach(function(b,c){d["on"+Xe[c]]=function(a){return b.add(a)},d["notify"+Xe[c]]=function(){var c=[];for(var d=0;d=0;b--){var c=O.path[b];if(c.projection.isEnabled){a=c;break}}C=a}return C},resolveRelativeTargetBox:function(){var a=O.getProjectionParent();if(!B.relativeTarget||!a)return;xd(B,a.projection);if(Vd(a)){var b=B.target;ee(b,b,a.getLatestValues())}},shouldResetTransform:function(){return Boolean(v._layoutResetTransform)},pointTo:function(a){D=a.projection,E=a.getLatestValues(),F===null||F===void 0?void 0:F(),F=f.pipe(a.onSetAxisTarget(O.scheduleUpdateLayoutProjection),a.onLayoutAnimationComplete(function(){var a;O.isPresent?O.presence=Z.Present:(a=O.layoutSafeToRemove)===null||a===void 0?void 0:a.call(O)}))},isPresent:!0,presence:Z.Entering});return O}};function cf(a){a.resolveRelativeTargetBox()}function df(a){a.updateLayoutProjection()}var ef=a.__spreadArray(["initial"],a.__read(T)),ff=ef.length,gf=new Set(["width","height","top","left","right","bottom","x","y"]),hf=function(a){return gf.has(a)},jf=function(a){return Object.keys(a).some(hf)},kf=function(a,b){a.set(b,!1),a.set(b)},lf=function(a){return a===d.number||a===d.px},mf;(function(a){a.width="width",a.height="height",a.left="left",a.right="right",a.top="top",a.bottom="bottom"})(mf||(mf={}));var nf=function(a,b){return parseFloat(a.split(", ")[b])};Y=function(a,b){return function(c,d){c=d.transform;if(c==="none"||!c)return 0;d=c.match(/^matrix3d((.+))$/);if(d)return nf(d[1],b);else{d=c.match(/^matrix((.+))$/);if(d)return nf(d[1],a);else return 0}}};var of=new Set(["x","y","z"]),pf=ua.filter(function(a){return!of.has(a)});function qf(a){var b=[];pf.forEach(function(c){var d=a.getValue(c);d!==void 0&&(b.push([c,d.get()]),d.set(c.startsWith("scale")?1:0))});b.length&&a.syncRender();return b}var rf={width:function(a){a=a.x;return a.max-a.min},height:function(a){a=a.y;return a.max-a.min},top:function(a,b){a=b.top;return parseFloat(a)},left:function(a,b){a=b.left;return parseFloat(a)},bottom:function(a,b){a=a.y;b=b.top;return parseFloat(b)+(a.max-a.min)},right:function(a,b){a=a.x;b=b.left;return parseFloat(b)+(a.max-a.min)},x:Y(4,13),y:Y(5,14)},sf=function(b,c,d){var e=c.measureViewportBox(),f=c.getInstance(),g=getComputedStyle(f);f=g.display;var h=g.top,i=g.left,j=g.bottom,k=g.right,a=g.transform,l={top:h,left:i,bottom:j,right:k,transform:a};f==="none"&&c.setStaticValue("display",b.display||"block");c.syncRender();var m=c.measureViewportBox();d.forEach(function(a){var d=c.getValue(a);kf(d,rf[a](e,l));b[a]=rf[a](m,g)});return b},tf=function(b,e,f,g){f===void 0&&(f={});g===void 0&&(g={});e=a.__assign({},e);g=a.__assign({},g);var h=Object.keys(e).filter(hf),i=[],j=!1,k=[];h.forEach(function(a){var h=b.getValue(a);if(!b.hasValue(a))return;var l=f[a],m=e[a],n=Ec(l),o;if(kb(m)){var p=m.length;for(var q=m[0]===null?1:0;qb?1:c(f.progress(a,b,d))}}var Pf=["TopLeft","TopRight","BottomLeft","BottomRight"],Qf=Pf.length;function Rf(a,b,c,d,e,g){for(var h=0;h=0){var l=j[k];f&&(g!==null&&g!==void 0?g:g=l);f!==null&&f!==void 0?f:f=l;if(f&&g)break}c.lead=f;c.follow=g;c.leadIsExiting=((l=c.lead)===null||l===void 0?void 0:l.presence)===Z.Exiting;h.setOptions({lead:f,follow:g,prevValues:e,crossfadeOpacity:(g===null||g===void 0?void 0:g.isPresenceRoot)||(f===null||f===void 0?void 0:f.isPresenceRoot)});c.lead!==d.follow&&(d.lead!==c.lead||d.leadIsExiting!==c.leadIsExiting)&&(i=!0)},animate:function(a,b){var d;b===void 0&&(b=!1);if(a===c.lead){b?a.pointTo(c.lead):a.setVisibility(!0);var e={};d=(d=c.follow)===null||d===void 0?void 0:d.getProjectionParent();d&&(e.prevParent=d);a.presence===Z.Entering?e.originBox=j():a.presence===Z.Exiting&&(e.targetBox=k());if(i){i=!1;d=a.getDefaultTransition();a.presence===Z.Entering?h.toLead(d):h.fromLead(d)}a.notifyLayoutReady(e)}else b?c.lead&&a.pointTo(c.lead):a.setVisibility(!1)}}}function Uf(a){var b=!1,c={};for(var d=0;d.001?1/a:Fg},Hg=!1;function Ig(a){var b=Yf(1),d=Yf(1),e=B();c.invariant(!!(a||e),"If no scale values are provided, useInvertedScale must be used within a child of another motion component.");c.warning(Hg,"useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");Hg=!0;a?(b=a.scaleX||b,d=a.scaleY||d):e&&(b=e.getValue("scaleX",1),d=e.getValue("scaleY",1));a=fg(b,Gg);e=fg(d,Gg);return{scaleX:a,scaleY:e}}n.AnimatePresence=qe;n.AnimateSharedLayout=T;n.DragControls=zg;n.FlatTree=af;n.FramerTreeLayoutContext=Pe;n.LayoutGroupContext=ba;n.LazyMotion=Wf;n.MotionConfig=Vf;n.MotionConfigContext=z;n.MotionValue=Bc;n.PresenceContext=C;n.SharedLayoutContext=Oe;n.addScaleCorrection=sa;n.animate=Kf;n.animateVisualElement=Oc;n.animationControls=wg;n.batchLayout=Ub;n.createBatcher=Me;n.createCrossfader=Lf;n.createDomMotionComponent=Bf;n.createMotionComponent=na;n.domAnimation=Y;n.domMax=Ae;n.flushLayout=Vb;n.isValidMotionProp=Oa;n.m=Ee;n.motion=Re;n.motionValue=V;n.resolveMotionValue=nb;n.snapshotViewportBox=Qd;n.transform=eg;n.useAnimation=xg;n.useCycle=yg;n.useDeprecatedAnimatedState=Eg;n.useDeprecatedInvertedScale=Ig;n.useDomEvent=ub;n.useDragControls=Bg;n.useElementScroll=ng;n.useIsPresent=F;n.useMotionTemplate=bg;n.useMotionValue=Yf;n.usePresence=E;n.useReducedMotion=vg;n.useSpring=hg;n.useTransform=fg;n.useVelocity=ig;n.useViewportScroll=sg;n.visualElement=ye}var q=!1;function r(){q||(q=!0,p());return o.exports}function a(a){switch(a){case void 0:return r()}}e.exports=a}),null);
-----
three.r138",[],(function $module_three_r138(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "138"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } const MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD, RAD2DEG, generateUUID, clamp, euclideanModulo, mapLinear, inverseLerp, lerp, damp, pingpong, smoothstep, smootherstep, randInt, randFloat, randFloatSpread, seededRandom, degToRad, radToDeg, isPowerOfTwo, ceilPowerOfTwo, floorPowerOfTwo, setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } class Source { constructor(data = null) { this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.images[this.uuid] !== undefined) { return meta.images[this.uuid]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if (data !== null) { let url; if (Array.isArray(data)) { // cube texture url = []; for (let i = 0, l = data.length; i < l; i++) { if (data[i].isDataTexture) { url.push(serializeImage(data[i].image)); } else { url.push(serializeImage(data[i])); } } } else { // texture url = serializeImage(data); } output.url = url; } if (!isRootObject) { meta.images[this.uuid] = output; } return output; } } function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } Source.prototype.isSource = true; let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.source = new Source(image); this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image(value) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); this.needsUpdate = true; return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { metadata: { version: 4.5, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON(meta).uuid, 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 (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); const image = { width, height, depth: 1 }; this.texture = new Texture(image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class DataArrayTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataArrayTexture.prototype.isDataArrayTexture = true; class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor(width, height, depth) { super(width, height); this.depth = depth; this.texture = new DataArrayTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } WebGLArrayRenderTarget.prototype.isWebGLArrayRenderTarget = true; class Data3DTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } Data3DTexture.prototype.isData3DTexture = true; class WebGL3DRenderTarget extends WebGLRenderTarget { constructor(width, height, depth) { super(width, height); this.depth = depth; this.texture = new Data3DTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } WebGL3DRenderTarget.prototype.isWebGL3DRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count, options = {}) { super(width, height, options); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); this.texture[i].isRenderTargetTexture = true; } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } setFromEuler(e) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); const nodes = extractFromCache(meta.nodes); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; if (nodes.length > 0) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (isRootObject) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation !== undefined && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth !== undefined && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRootObject) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; Material.fromType = function /*type*/ () { // TODO: Behavior added in Materials.js return null; }; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start, count, materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (this.hasAttribute("tangent") === false) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = this.getAttribute("tangent").array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance, point: _intersectionPointWorld.clone(), object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a, b, c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width, height, depth, widthSegments, heightSegments, depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; const default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; const default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options = {}) { super(size, size, options); const image = { width: size, height: size, depth: 1 }; const images = [image, image, image, image, image, image]; this.texture = new CubeTexture(images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop (callback) { animationLoop = callback; }, setContext (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { throw new Error("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } else { throw new Error("THREE.WebGLAttributes: Unsupported buffer data format: " + array); } return { buffer, type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get, remove, update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width, height, widthSegments, heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } const alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; const alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; const alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; const alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; const aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; const aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; const begin_vertex = "vec3 transformed = vec3( position );"; const beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; const bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; const bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; const clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; const clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; const clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; const clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; const color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; const color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; const color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; const color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; const common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; const cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; const defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; const displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; const displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; const emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; const emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; const encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; const encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; const envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; const envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; const envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; const envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; const envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; const fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; const fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; const fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; const fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; const gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; const lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; const lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; const lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; const lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; const envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; const lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; const lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; const lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; const lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; const lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; const lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; const lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; const lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; const lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; const logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; const logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; const logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; const logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; const map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; const map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; const map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; const map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; const metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; const metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; const morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ] #endif } #endif"; const morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; const morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { float texelIndex = float( vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; const morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; const normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; const normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; const normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; const normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; const normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; const normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; const clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; const clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; const clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; const output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; const packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; const premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; const project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; const dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; const dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; const roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; const roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; const shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; const shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; const shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; const shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; const skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; const skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; const skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; const skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; const specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; const specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; const tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; const tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; const transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; const transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef texture2DLodEXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; const uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; const uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; const uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; const uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; const uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; const uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; const worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment, alphamap_pars_fragment, alphatest_fragment, alphatest_pars_fragment, aomap_fragment, aomap_pars_fragment, begin_vertex, beginnormal_vertex, bsdfs, bumpmap_pars_fragment, clipping_planes_fragment, clipping_planes_pars_fragment, clipping_planes_pars_vertex, clipping_planes_vertex, color_fragment, color_pars_fragment, color_pars_vertex, color_vertex, common, cube_uv_reflection_fragment, defaultnormal_vertex, displacementmap_pars_vertex, displacementmap_vertex, emissivemap_fragment, emissivemap_pars_fragment, encodings_fragment, encodings_pars_fragment, envmap_fragment, envmap_common_pars_fragment, envmap_pars_fragment, envmap_pars_vertex, envmap_physical_pars_fragment, envmap_vertex, fog_vertex, fog_pars_vertex, fog_fragment, fog_pars_fragment, gradientmap_pars_fragment, lightmap_fragment, lightmap_pars_fragment, lights_lambert_vertex, lights_pars_begin, lights_toon_fragment, lights_toon_pars_fragment, lights_phong_fragment, lights_phong_pars_fragment, lights_physical_fragment, lights_physical_pars_fragment, lights_fragment_begin, lights_fragment_maps, lights_fragment_end, logdepthbuf_fragment, logdepthbuf_pars_fragment, logdepthbuf_pars_vertex, logdepthbuf_vertex, map_fragment, map_pars_fragment, map_particle_fragment, map_particle_pars_fragment, metalnessmap_fragment, metalnessmap_pars_fragment, morphcolor_vertex, morphnormal_vertex, morphtarget_pars_vertex, morphtarget_vertex, normal_fragment_begin, normal_fragment_maps, normal_pars_fragment, normal_pars_vertex, normal_vertex, normalmap_pars_fragment, clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps, clearcoat_pars_fragment, output_fragment, packing, premultiplied_alpha_fragment, project_vertex, dithering_fragment, dithering_pars_fragment, roughnessmap_fragment, roughnessmap_pars_fragment, shadowmap_pars_fragment, shadowmap_pars_vertex, shadowmap_vertex, shadowmask_pars_fragment, skinbase_vertex, skinning_pars_vertex, skinning_vertex, skinnormal_vertex, specularmap_fragment, specularmap_pars_fragment, tonemapping_fragment, tonemapping_pars_fragment, transmission_fragment, transmission_pars_fragment, uv_pars_fragment, uv_pars_vertex, uv_vertex, uv2_pars_fragment, uv2_pars_vertex, uv2_vertex, worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor () { return clearColor; }, setClearColor (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha () { return clearAlpha; }, setClearAlpha (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes, enabledAttributes, attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup, reset, resetDefaultState, dispose, releaseStatesOfGeometry, releaseStatesOfProgram, initAttributes, enableAttribute, disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2, drawBuffers, getMaxAnisotropy, getMaxPrecision, precision, logarithmicDepthBuffer, maxTextures, maxVertexTextures, maxTextureSize, maxCubemapSize, maxAttributes, maxVertexUniforms, maxVaryings, maxFragmentUniforms, vertexTextures, floatFragmentTextures, floatVertexTextures, maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get, dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); this._setSize(256); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial(this._cubemapMaterial); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial(this._equirectMaterial); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if (this._cubemapMaterial !== null) this._cubemapMaterial.dispose(); if (this._equirectMaterial !== null) this._equirectMaterial.dispose(); } // private interface _setSize(cubeSize) { this._lodMax = Math.floor(Math.log2(cubeSize)); this._cubeSize = Math.pow(2, this._lodMax); } _dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); for (let i = 0; i < this._lodPlanes.length; i++) { this._lodPlanes[i].dispose(); } } _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { if (texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping) { this._setSize(texture.image.length === 0 ? 16 : texture.image[0].width || texture.image[0].image.width); } else { // Equirectangular this._setSize(texture.image.width / 4); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max(this._cubeSize, 16 * 7); const height = 4 * this._cubeSize - 32; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(width, height, params); if (this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width) { if (this._pingPongRenderTarget !== null) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget(width, height, params); const { _lodMax } = this; ({ sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes(_lodMax)); this._blurMaterial = _getBlurShader(_lodMax, width, height); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(this._lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } const size = this._cubeSize; _setViewport(cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh(this._lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; const size = this._cubeSize; _setViewport(cubeUVRenderTarget, 0, 0, 3 * size, 2 * size); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < this._lodPlanes.length; i++) { const sigma = Math.sqrt(this._sigmas[i] * this._sigmas[i] - this._sigmas[i - 1] * this._sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(this._lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } const { _lodMax } = this; blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = _lodMax - lodIn; const outputSize = this._sizeLods[lodOut]; const x = 3 * outputSize * (lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0); const y = 4 * (this._cubeSize - outputSize); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes(lodMax) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for (let i = 0; i < totalLods; i++) { const sizeLod = Math.pow(2, lod); sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > lodMax - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - lodMax + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget(width, height, params) { const cubeUVRenderTarget = new WebGLRenderTarget(width, height, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(lodMax, width, height) { const weights = new Float32Array(MAX_SAMPLES); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new ShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0` }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCubemapMaterial() { return new ShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get, dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has (name) { return getExtension(name) !== null; }, init (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get, update, getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory, render, programs: null, autoReset: true, reset, update }; } function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== morphTargetsCount) { if (entry !== undefined) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if (hasMorphPosition === true) vertexDataCount = 1; if (hasMorphNormals === true) vertexDataCount = 2; if (hasMorphColors === true) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * morphTargetsCount); const texture = new DataArrayTexture(buffer, width, height, morphTargetsCount); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for (let i = 0; i < morphTargetsCount; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const morphColor = morphColors[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { const stride = j * vertexDataStride; if (hasMorphPosition === true) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; } if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } if (hasMorphColors === true) { morph.fromBufferAttribute(morphColor, j); if (morphColor.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 8] = morph.x; buffer[offset + stride + 9] = morph.y; buffer[offset + stride + 10] = morph.z; buffer[offset + stride + 11] = morphColor.itemSize === 4 ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update, dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyArrayTexture = new DataArrayTexture(); const empty3dTexture = new Data3DTexture(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || empty3dTexture, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyArrayTexture, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || empty3dTexture, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || !!parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphColors && parameters.isWebGL2 ? "#define USE_MORPHCOLORS" : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable, programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const envMapCubeUVHeight = !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping) ? envMap.image.height : null; const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let morphTextureStride = 0; if (geometry.morphAttributes.position !== undefined) morphTextureStride = 1; if (geometry.morphAttributes.normal !== undefined) morphTextureStride = 2; if (geometry.morphAttributes.color !== undefined) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2, shaderID, shaderName: material.type, vertexShader, fragmentShader, defines: material.defines, customVertexShaderID, customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUVHeight, lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones, useVertexTexture: floatVertexTextures, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount, morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.envMapCubeUVHeight); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.morphAttributeCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.lightMap) _programLayers.enable(7); if (parameters.aoMap) _programLayers.enable(8); if (parameters.emissiveMap) _programLayers.enable(9); if (parameters.bumpMap) _programLayers.enable(10); if (parameters.normalMap) _programLayers.enable(11); if (parameters.objectSpaceNormalMap) _programLayers.enable(12); if (parameters.tangentSpaceNormalMap) _programLayers.enable(13); if (parameters.clearcoat) _programLayers.enable(14); if (parameters.clearcoatMap) _programLayers.enable(15); if (parameters.clearcoatRoughnessMap) _programLayers.enable(16); if (parameters.clearcoatNormalMap) _programLayers.enable(17); if (parameters.displacementMap) _programLayers.enable(18); if (parameters.specularMap) _programLayers.enable(19); if (parameters.roughnessMap) _programLayers.enable(20); if (parameters.metalnessMap) _programLayers.enable(21); if (parameters.gradientMap) _programLayers.enable(22); if (parameters.alphaMap) _programLayers.enable(23); if (parameters.alphaTest) _programLayers.enable(24); if (parameters.vertexColors) _programLayers.enable(25); if (parameters.vertexAlphas) _programLayers.enable(26); if (parameters.vertexUvs) _programLayers.enable(27); if (parameters.vertexTangents) _programLayers.enable(28); if (parameters.uvsVertexOnly) _programLayers.enable(29); if (parameters.fog) _programLayers.enable(30); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.morphColors) _programLayers.enable(7); if (parameters.premultipliedAlpha) _programLayers.enable(8); if (parameters.shadowMapEnabled) _programLayers.enable(9); if (parameters.physicallyCorrectLights) _programLayers.enable(10); if (parameters.doubleSided) _programLayers.enable(11); if (parameters.flipSided) _programLayers.enable(12); if (parameters.depthPacking) _programLayers.enable(13); if (parameters.dithering) _programLayers.enable(14); if (parameters.specularIntensityMap) _programLayers.enable(15); if (parameters.specularColorMap) _programLayers.enable(16); if (parameters.transmission) _programLayers.enable(17); if (parameters.transmissionMap) _programLayers.enable(18); if (parameters.thicknessMap) _programLayers.enable(19); if (parameters.sheen) _programLayers.enable(20); if (parameters.sheenColorMap) _programLayers.enable(21); if (parameters.sheenRoughnessMap) _programLayers.enable(22); if (parameters.decodeVideoTexture) _programLayers.enable(23); if (parameters.opaque) _programLayers.enable(24); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters, getProgramCacheKey, getUniforms, acquireProgram, releaseProgram, releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs, dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get, remove, update, dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object, geometry, material, groupOrder, renderOrder: object.renderOrder, z, group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque, transmissive, transparent, init, push, unshift, finish, sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get, dispose }; } function UniformsCache() { const lights = {}; return { get (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup, setupView, state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray, shadowsArray, lights }; return { init, state, setupLights, setupLightsView, pushLight, pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get, dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked (lock) { locked = lock; }, setClear (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked (lock) { locked = lock; }, setClear (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked (lock) { locked = lock; }, setClear (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable, disable, bindFramebuffer, drawBuffers, useProgram, setBlending, setMaterial, setFlipSided, setCullFace, setLineWidth, setPolygonOffset, setScissorTest, activeTexture, bindTexture, unbindTexture, compressedTexImage2D, texImage2D, texImage3D, texStorage2D, texStorage3D, texSubImage2D, texSubImage3D, compressedTexSubImage2D, scissor, viewport, reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has("WEBGL_multisampled_render_to_texture") ? extensions.get("WEBGL_multisampled_render_to_texture") : null; const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get(source); if (webglTextures) { const webglTexture = webglTextures[textureProperties.__cacheKey]; webglTexture.usedTimes--; // the WebGLTexture object is not used anymore, remove it if (webglTexture.usedTimes === 0) { deleteTexture(texture); } // remove the weak map entry if no WebGLTexture uses the source anymore if (Object.keys(webglTextures).length === 0) { _sources.delete(source); } } properties.remove(texture); } function deleteTexture(texture) { const textureProperties = properties.get(texture); _gl.deleteTexture(textureProperties.__webglTexture); const source = texture.source; const webglTextures = _sources.get(source); delete webglTextures[textureProperties.__cacheKey]; info.memory.textures--; } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } function getTextureCacheKey(texture) { const array = []; array.push(texture.wrapS); array.push(texture.wrapT); array.push(texture.magFilter); array.push(texture.minFilter); array.push(texture.anisotropy); array.push(texture.internalFormat); array.push(texture.format); array.push(texture.type); array.push(texture.generateMipmaps); array.push(texture.premultiplyAlpha); array.push(texture.flipY); array.push(texture.unpackAlignment); array.push(texture.encoding); return array.join(); } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === null) { console.warn("THREE.WebGLRenderer: Texture marked for update but no image data found."); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { let forceUpload = false; if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get(source); if (webglTextures === undefined) { webglTextures = {}; _sources.set(source, webglTextures); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey(texture); if (textureCacheKey !== textureProperties.__cacheKey) { // if not, create a new instance of WebGLTexture if (webglTextures[textureCacheKey] === undefined) { // create new entry webglTextures[textureCacheKey] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[textureCacheKey].usedTimes++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[textureProperties.__cacheKey]; if (webglTexture !== undefined) { webglTextures[textureProperties.__cacheKey].usedTimes--; if (webglTexture.usedTimes === 0) { deleteTexture(texture); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[textureCacheKey].texture; } return forceUpload; } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); if (source.version !== source.__currentVersion || forceUpload === true) { _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataArrayTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isData3DTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } source.__currentVersion = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); if (source.version !== source.__currentVersion || forceUpload === true) { _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture; const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } source.__currentVersion = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || useMultisampledRTT(renderTarget)) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget) { if (isWebGL2) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error("THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } const renderTargetProperties = properties.get(renderTarget); const ignoreDepthValues = renderTargetProperties.__ignoreDepthValues !== undefined ? renderTargetProperties.__ignoreDepthValues : false; if (ignoreDepthValues === false) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (ignoreDepthValues === true) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } } function getRenderTargetSamples(renderTarget) { return Math.min(maxSamples, renderTarget.samples); } function useMultisampledRTT(renderTarget) { const renderTargetProperties = properties.get(renderTarget); return isWebGL2 && renderTarget.samples > 0 && extensions.has("WEBGL_multisampled_render_to_texture") === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width, height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, encoding: renderer.outputEncoding, samples: attributes.antialias ? 4 : 0 }); const renderTargetProperties = renderer.properties.get(newRenderTarget); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms, refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let _alpha; if (parameters.context !== undefined) { _alpha = _context.getContextAttributes().alpha; } else { _alpha = parameters.alpha !== undefined ? parameters.alpha : false; } let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color = true, depth = true, stencil = true) { let bits = 0; if (color) bits |= _gl.COLOR_BUFFER_BIT; if (depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); } function renderTransmissionPass(opaqueObjects, scene, camera) { const isWebGL2 = capabilities.isWebGL2; if (_transmissionRenderTarget === null) { _transmissionRenderTarget = new WebGLRenderTarget(1, 1, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: isWebGL2 && _antialias === true ? 4 : 0 }); } _this.getDrawingBufferSize(_vector2); if (isWebGL2) { _transmissionRenderTarget.setSize(_vector2.x, _vector2.y); } else { _transmissionRenderTarget.setSize(floorPowerOfTwo(_vector2.x), floorPowerOfTwo(_vector2.y)); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphColors = !!geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.morphColors !== morphColors) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } const morphAttributes = geometry.morphAttributes; if (morphAttributes.position !== undefined || morphAttributes.normal !== undefined || morphAttributes.color !== undefined && capabilities.isWebGL2 === true) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (extensions.has("WEBGL_multisampled_render_to_texture") === true) { console.warn("THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided"); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isData3DTexture || texture.isDataArrayTexture) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (capabilities.isWebGL2 && renderTarget.samples > 0 && textures.useMultisampledRTT(renderTarget) === false) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isData3DTexture) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataArrayTexture) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isData3DTexture) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance, object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data, width, height }; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index, face: null, object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width, height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius, segments, thetaStart, thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices, indices, radius, detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry, thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents, normals, binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; const Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve, CatmullRomCurve3, CubicBezierCurve, CubicBezierCurve3, EllipseCurve, LineCurve, LineCurve3, QuadraticBezierCurve, QuadraticBezierCurve3, SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes, options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points, segments, phiStart, phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes, curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius, tube, radialSegments, tubularSegments, arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius, tube, tubularSegments, radialSegments, p, q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path, tubularSegments, radius, radialSegments, closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } const Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry, ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material }; Material.fromType = function (type) { return new materialLib[type](); }; const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove (key) { delete this.files[key]; }, clear () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad, onProgress, onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad, onProgress, onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if (typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.slice(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = Material.fromType(json.type); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.slice(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = await deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } const source = images[data.image]; const image = source.data; let texture; if (Array.isArray(image)) { texture = new CubeTexture(); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(); } else { texture = new Texture(); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping, CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeUVReflectionMapping, CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (matches === null) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object.isBone === true) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; let toChange = 0; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange++; if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } if (toChange > 0 && ambiguous === false) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Euler.prototype.toVector3 = function () { console.error("THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead"); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, useQuaternion: { get () { console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default."); }, set () { console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, set () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.prototype, { wrapAround: { get () { console.warn("THREE.Material: .wrapAround has been removed."); }, set () { console.warn("THREE.Material: .wrapAround has been removed."); } }, overdraw: { get () { console.warn("THREE.Material: .overdraw has been removed."); }, set () { console.warn("THREE.Material: .overdraw has been removed."); } }, wrapRGB: { get () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get () { return this.shadowMap.enabled; }, set (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get () { return this.shadowMap.type; }, set (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set /* cullFace */ () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); } }, renderReverseSided: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead."); return undefined; }, set () { console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead."); } }, renderSingleSided: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); return undefined; }, set () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } function WebGLMultisampleRenderTarget(width, height, options) { console.error("THREE.WebGLMultisampleRenderTarget has been removed. Use a normal render target and set the "samples" property to greater 0 to enable multisampling."); const renderTarget = new WebGLRenderTarget(width, height, options); renderTarget.samples = 4; return renderTarget; } function DataTexture2DArray(data, width, height, depth) { console.warn("THREE.DataTexture2DArray has been renamed to DataArrayTexture."); return new DataArrayTexture(data, width, height, depth); } function DataTexture3D(data, width, height, depth) { console.warn("THREE.DataTexture3D has been renamed to Data3DTexture."); return new Data3DTexture(data, width, height, depth); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null); /** * (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. * * Copyright 2010-2022 Three.js Authors * SPDX-License-Identifier: MIT * * @noflow * @noformat * @nolint * @oncall horizon_cloud_platform_windows * @preserve-header * @preserve-whitespace */
-----
StdlibWebBloksPrimitives",["WebBloksAnimatedAddOnCompleteListener","WebBloksAnimatedBuild","WebBloksAnimatedCancel","WebBloksAnimatedCancelToken","WebBloksAnimatedCreate","WebBloksAnimatedCreateColor","WebBloksAnimatedCreateCubicBezier","WebBloksAnimatedCreateDimension","WebBloksAnimatedDestroy","WebBloksAnimatedGetCurrentColorValue","WebBloksAnimatedGetCurrentDimensionValue","WebBloksAnimatedGetCurrentPlayTime","WebBloksAnimatedGetCurrentValue","WebBloksAnimatedGetTotalDuration","WebBloksAnimatedIsInitialized","WebBloksAnimatedLoop","WebBloksAnimatedParallel","WebBloksAnimatedPause","WebBloksAnimatedResume","WebBloksAnimatedSequence","WebBloksAnimatedSetCurrentPlayTime","WebBloksAnimatedStagger","WebBloksAnimatedStart","WebBloksAnimatedStartToken","WebBloksCollectionSetIndex","WebBloksDataComposableTextSpan","WebBloksDataImageSpan","WebBloksDataTextSpan","WebBloksScreenClose","WebBloksScreenOpen","WebBloksScreenQueryScreenWrapper","WebBloksSpinner"],(function(a,b,c,d,e,f,g){a={"bk.components.Spinner":c("WebBloksSpinner"),"bk.components.screen.ScreenWrapper":d("WebBloksScreenQueryScreenWrapper").WebBloksScreenQueryScreenWrapper,"bk.data.ImageSpan":c("WebBloksDataImageSpan"),"bk.data.TextSpan":c("WebBloksDataTextSpan"),"bk.data.ComposableTextSpan":c("WebBloksDataComposableTextSpan")};b={"bk.action.animated.AddOnCompleteListener":c("WebBloksAnimatedAddOnCompleteListener"),"bk.action.animated.Build":c("WebBloksAnimatedBuild"),"bk.action.animated.Cancel":c("WebBloksAnimatedCancel"),"bk.action.animated.Create":c("WebBloksAnimatedCreate"),"bk.action.animated.CancelToken":c("WebBloksAnimatedCancelToken"),"bk.action.animated.CreateColor":c("WebBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("WebBloksAnimatedCreateDimension"),"bk.action.animated.Destroy":c("WebBloksAnimatedDestroy"),"bk.action.animated.easing.CreateCubicBezier":c("WebBloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("WebBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("WebBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentPlayTime":c("WebBloksAnimatedGetCurrentPlayTime"),"bk.action.animated.GetCurrentValue":c("WebBloksAnimatedGetCurrentValue"),"bk.action.animated.GetTotalDuration":c("WebBloksAnimatedGetTotalDuration"),"bk.action.animated.IsInitialized":c("WebBloksAnimatedIsInitialized"),"bk.action.animated.Loop":c("WebBloksAnimatedLoop"),"bk.action.animated.Parallel":c("WebBloksAnimatedParallel"),"bk.action.animated.Pause":c("WebBloksAnimatedPause"),"bk.action.animated.Resume":c("WebBloksAnimatedResume"),"bk.action.animated.Sequence":c("WebBloksAnimatedSequence"),"bk.action.animated.SetCurrentPlayTime":c("WebBloksAnimatedSetCurrentPlayTime"),"bk.action.animated.Stagger":c("WebBloksAnimatedStagger"),"bk.action.animated.Start":c("WebBloksAnimatedStart"),"bk.action.animated.StartToken":c("WebBloksAnimatedStartToken"),"bk.action.collection.SetIndex":c("WebBloksCollectionSetIndex"),"bk.action.screen.Close":c("WebBloksScreenClose"),"bk.action.screen.Open":c("WebBloksScreenOpen")};e={};g.COMPONENTS=a;g.ACTIONS=b;g.EXTENSION_HANDLERS=e}),98);
-----
WebBloksAnimationFunctions",[],(function(a,b,c,d,e,f){function g(a,b,c,d){var e=1e-6,f=3*a-3*c+1,g=3*c-6*a,h=3*a,i=3*b-3*d+1,j=3*d-6*b,k=3*b;function l(a){return(3*f*a+2*g)*a+h}function m(a){return((f*a+g)*a+h)*a}function n(a){return((i*a+j)*a+k)*a}function o(a){var b=a,c,d;for(var f=0;f<8;f++){d=m(b)-a;if(Math.abs(d)f){d=m(b)-a;if(Math.abs(d)0?c=b:f=b;b=(c+f)/2}return b}function p(a){return n(o(a))}return p}function a(){return g(0,0,1,1)}f.cubicBezier=g;f.linearInterpolator=a}),66);
-----
WebBloksAnimatedCreateCubicBezier",["WebBloksAnimationFunctions"],(function(a,b,c,d,e,f,g){function a(a,b,c,e,f){return d("WebBloksAnimationFunctions").cubicBezier(b,c,e,f)}g["default"]=a}),98);
-----
UFIReactionsVectorIcon",["CubicBezier"],(function(a,b,c,d,e,f,g){a=function(){var a=b.prototype;a.buildKeyFrames=function(a){var b=a.key_frames,d=a.timing_curves;return Object.keys(b).map(function(a){return parseInt(a,10)}).sort(function(a,b){return a-b}).reduce(function(a,e,f,g){var h=b[e];if(f===0){for(var i=0;i<=e;i++)a.push(h);return a}var j=null;i=d[f-1];Array.isArray(i)&&(j=new(c("CubicBezier"))(i[0],i[1]));var k=g[f-1],l=b[k];for(var m=k+1;m<=e;m++)m===e?a.push(h):(function(){var b=(m-k)/(e-k);j&&(b=j.solve(b));a.push(h.map(function(a,c){if(Array.isArray(a))return[a[0],a[1].map(function(a,d){d=l[c][1][d];return d+(a-d)*b})];if(isNaN(parseFloat(a)))return a;var d=l[c];return d+(a-d)*b}))})();return a},[],this)};function b(a){var b=this;this.buildAnimation=function(a){var b=a.anchor,c=a.property;a.key_frames=a.key_frames.map(function(a,d,e){if(b&&c==="ROTATION")return"rotate("+a[0]+" "+b.join(" ")+")";if(c==="POSITION"){d=a[0]-e[0][0];e=a[1]-e[0][1];return"translate("+d+" "+e+")"}if(c==="SCALE"){d=a.map(function(a){return a/100}).join(" ");return b?"translate("+b.join(" ")+") "+("scale("+d+") ")+("translate("+b.map(function(a){return-a}).join(" ")+")"):"scale("+d+")"}return null});return a};this.buildEffect=function(a){a.property==="gradient"&&(a.start.key_frames=a.start.key_frames.map(function(a){var b=Math.floor(a[0]),c=Math.floor(a[1]);a=Math.floor(a[2]);return"rgb("+b+","+c+","+a+")"}),a.end.key_frames=a.end.key_frames.map(function(a){var b=Math.floor(a[0]),c=Math.floor(a[1]);a=Math.floor(a[2]);return"rgb("+b+","+c+","+a+")"}));return a};this.buildFeature=function(a){a.key_frames=a.key_frames.map(function(a){return a.map(function(a){return a[0]+a[1].join(",")}).join(" ")});return a};this.buildData=function(a){a.animations&&(a.animations=a.animations.map(b.buildData,b).map(b.buildAnimation,b));a.effects&&(a.effects=a.effects.map(b.buildData,b).map(b.buildEffect,b));a.end&&(a.end.key_frames=b.buildKeyFrames(a.end));a.features&&(a.features=a.features.map(b.buildData,b).map(b.buildFeature,b));a.key_frames&&(a.key_frames=b.buildKeyFrames(a));a.start&&(a.start.key_frames=b.buildKeyFrames(a.start));a.groups&&(a.groups=a.groups.map(b.buildData,b));return a};this.$2=a.frameCount;this.$1=this.buildData(a)}a.getData=function(){return this.$1};a.getFrameCount=function(){return this.$2};return b}();g["default"]=a}),98);
-----
KF2Loader",["BlobFactory","CubicBezier","KFColor","KFComponentNames","KFGradient","KFLoaderUtils","KFManifest","KFPath2d","KFPoint","KFPosition","KFScalar","KeyframesCompatPluginsLoader","KeyframesSchema","Promise","flatbuffers","regeneratorRuntime"],(function(a,b,c,d,e,f,g){"use strict";var h,i=function(a){return a};function e(a,e,f){var g,h,i;return b("regeneratorRuntime").async(function(j){while(1)switch(j.prev=j.next){case 0:g=new(d("flatbuffers").ByteBuffer)(a);h=c("KeyframesSchema").Document.bufferHasIdentifier(g)?c("KeyframesSchema").Document.getRootAsDocument(g):null;F(h);j.next=5;return b("regeneratorRuntime").awrap(k(h,e));case 5:i=j.sent,e.addComponent(l(h,e,f,i),d("KFComponentNames").ROOT),c("KFLoaderUtils").maybeGenerateTrackMatteIDs(e);case 8:case"end":return j.stop()}},null,this)}function j(a){var b=[];for(var c=a.pluginsLength()-1;c>=0;c--)b.unshift(a.plugins(c));return b}function k(a,d){return new(h||(h=b("Promise")))(function(b,e){if(a==null)return b(null);e=j(a);return e.length===0?b(null):b(c("KeyframesCompatPluginsLoader").fromPluginTable(e,d))})}function l(a,b,e,f){var g=H(a,e),h=a.subdocumentsLength(),i=[];for(var j=0;j-----
KFLoader",["BlobFactory","CubicBezier","KFColor","KFComponentNames","KFGradient","KFLoaderUtils","KFManifest","KFPath2d","KFPoint","KFPosition","KFScalar","KFSchema","Promise","flatbuffers"],(function(a,b,c,d,e,f,g){"use strict";var h,i=a.URL||a.webkitURL||{},j=new Map(),k;function e(a,b,e){k=!1;j.clear();a=new(d("flatbuffers").ByteBuffer)(a);a=c("KFSchema").Document.bufferHasIdentifier(a)?c("KFSchema").Document.getRootAsDocument(a):null;m(a);Q(a,e);n(a,null,!0,b,e);l(b)}function l(a){k?c("KFLoaderUtils").maybeGenerateTrackMatteIDs(a):c("KFLoaderUtils").traverseEntitiesAndApply(a,function(b){b=a.getComponent(b,d("KFComponentNames").COMPOSITING);var c=b==null?void 0:b.trackMatteId;if(b==null||c==null||c===0)return;var e=j.get(c);b.trackMatteId=(b=e==null?void 0:e.getID())!=null?b:c})}function m(a,b){var d=[],e=a.manifestLength();for(var f=0;f0?e[e.length-1]:null;switch(b[k]){case c("KFSchema").CommandType.MoveTo:case c("KFSchema").CommandType.LineTo:g[0]=a[d++];g[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]]});break;case c("KFSchema").CommandType.QuadTo:g[0]=a[d++];g[1]=a[d++];h[0]=a[d++];h[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CubicTo:g[0]=a[d++];g[1]=a[d++];h[0]=a[d++];h[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.HorizontalLineTo:g[0]=a[d++];e.push({type:c("KFSchema").CommandType.LineTo,point:[g[0],g[1]]});break;case c("KFSchema").CommandType.VerticalLineTo:g[1]=a[d++];e.push({type:c("KFSchema").CommandType.LineTo,point:[g[0],g[1]]});break;case c("KFSchema").CommandType.CloseLineTo:e.push({type:c("KFSchema").CommandType.LineTo,point:[f[0],f[1]]});break;case c("KFSchema").CommandType.SmoothQuadTo:I(h,j);g[0]=a[d++];g[1]=a[d++];e.push({type:c("KFSchema").CommandType.QuadTo,point:[g[0],g[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CloseQuadTo:h[0]=a[d++];h[1]=a[d++];e.push({type:c("KFSchema").CommandType.QuadTo,point:[f[0],f[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CloseSmoothQuadTo:I(h,j);e.push({type:c("KFSchema").CommandType.QuadTo,point:[f[0],f[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.SmoothCubicTo:I(h,j);g[0]=a[d++];g[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[g[0],g[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.CloseCubicTo:h[0]=a[d++];h[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[f[0],f[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.CloseSmoothCubicTo:I(h,j);i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[f[0],f[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break}f||(f=[g[0],g[1]])}return e}var H=[0,0];function I(a,b){var d;if(b==null)return;var e=b.point,f;switch(b.type){case c("KFSchema").CommandType.QuadTo:f=(d=b.c1)!=null?d:H;break;case c("KFSchema").CommandType.CubicTo:f=(d=b.c2)!=null?d:H;break;default:f=H;break}a[0]=f[0]+2*(e[0]-f[0]);a[1]=f[1]+2*(e[1]-f[1])}function J(a){return w(a,G)}function K(a){return a?{width:a.width(),height:a.height()}:null}function L(a){return w(a,M)}function M(a){return a?[a.red(),a.green(),a.blue(),a.alpha()]:null}function N(a){if(!a)return null;var b=a.colorsLength(),c=[];for(var d=0;d-----
three.r165",[],(function $module_three_r165(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; const REVISION = "165"; const MOUSE = {LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2}; const TOUCH = {ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3}; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const ConstantColorFactor = 211; const OneMinusConstantColorFactor = 212; const ConstantAlphaFactor = 213; const OneMinusConstantAlphaFactor = 214; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const AgXToneMapping = 6; const NeutralToneMapping = 7; const AttachedBindMode = "attached"; const DetachedBindMode = "detached"; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const UnsignedInt5999Type = 35902; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RGB_BPTC_SIGNED_Format = 36494; const RGB_BPTC_UNSIGNED_Format = 36495; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ""; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const DisplayP3ColorSpace = "display-p3"; const LinearDisplayP3ColorSpace = "display-p3-linear"; const LinearTransfer = "linear"; const SRGBTransfer = "srgb"; const Rec709Primaries = "rec709"; const P3Primaries = "p3"; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const NeverCompare = 512; const LessCompare = 513; const EqualCompare = 514; const LessEqualCompare = 515; const GreaterCompare = 516; const NotEqualCompare = 517; const GreaterEqualCompare = 518; const AlwaysCompare = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const WebGLCoordinateSystem = 2000; const WebGPUCoordinateSystem = 2001; /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return ( listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1 ); } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", ]; let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = (Math.random() * 0xffffffff) | 0; const d1 = (Math.random() * 0xffffffff) | 0; const d2 = (Math.random() * 0xffffffff) | 0; const d3 = (Math.random() * 0xffffffff) | 0; const uuid = _lut[d0 & 0xff] + _lut[(d0 >> 8) & 0xff] + _lut[(d0 >> 16) & 0xff] + _lut[(d0 >> 24) & 0xff] + "-" + _lut[d1 & 0xff] + _lut[(d1 >> 8) & 0xff] + "-" + _lut[((d1 >> 16) & 0x0f) | 0x40] + _lut[(d1 >> 24) & 0xff] + "-" + _lut[(d2 & 0x3f) | 0x80] + _lut[(d2 >> 8) & 0xff] + "-" + _lut[(d2 >> 16) & 0xff] + _lut[(d2 >> 24) & 0xff] + _lut[d3 & 0xff] + _lut[(d3 >> 8) & 0xff] + _lut[(d3 >> 16) & 0xff] + _lut[(d3 >> 24) & 0xff]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return ((n % m) + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + ((x - a1) * (b2 - b1)) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s; // Mulberry32 generator let t = (_seed += 0x6d2b79f5); t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & (value - 1)) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order, ); } } function denormalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return value / 4294967295.0; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int32Array: return Math.max(value / 2147483647.0, -1.0); case Int16Array: return Math.max(value / 32767.0, -1.0); case Int8Array: return Math.max(value / 127.0, -1.0); default: throw new Error("Invalid component type."); } } function normalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return Math.round(value * 4294967295.0); case Uint16Array: return Math.round(value * 65535.0); case Uint8Array: return Math.round(value * 255.0); case Int32Array: return Math.round(value * 2147483647.0); case Int16Array: return Math.round(value * 32767.0); case Int8Array: return Math.round(value * 127.0); default: throw new Error("Invalid component type."); } } const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize, }; class Vector2 { constructor(x = 0, y = 0) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v) { this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } class Matrix3 { constructor(n11, n12, n13, n21, n22, n23, n31, n32, n33) { Matrix3.prototype.isMatrix3 = true; this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (n11 !== undefined) { this.set(n11, n12, n13, n21, n22, n23, n31, n32, n33); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return ( a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g ); } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set( sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1, ); return this; } // scale(sx, sy) { this.premultiply(_m3.makeScale(sx, sy)); return this; } rotate(theta) { this.premultiply(_m3.makeRotation(-theta)); return this; } translate(tx, ty) { this.premultiply(_m3.makeTranslation(tx, ty)); return this; } // for 2D Transforms makeTranslation(x, y) { if (x.isVector2) { this.set(1, 0, x.x, 0, 1, x.y, 0, 0, 1); } else { this.set(1, 0, x, 0, 1, y, 0, 0, 1); } return this; } makeRotation(theta) { // counterclockwise const c = Math.cos(theta); const s = Math.sin(theta); this.set(c, -s, 0, s, c, 0, 0, 0, 1); return this; } makeScale(x, y) { this.set(x, 0, 0, 0, y, 0, 0, 0, 1); return this; } // equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } const _m3 = /*@__PURE__*/ new Matrix3(); function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] >= 65535) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array, }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } const _cache = {}; function warnOnce(message) { if (message in _cache) return; _cache[message] = true; console.warn(message); } function probeAsync(gl, sync, interval) { return new Promise(function (resolve, reject) { function probe() { switch (gl.clientWaitSync(sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0)) { case gl.WAIT_FAILED: reject(); break; case gl.TIMEOUT_EXPIRED: setTimeout(probe, interval); break; default: resolve(); } } setTimeout(probe, interval); }); } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set( 0.8224621, 0.177538, 0.0, 0.0331941, 0.9668058, 0.0, 0.0170827, 0.0723974, 0.9105199, ); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set( 1.2249401, -0.2249404, 0.0, -0.0420569, 1.0420571, 0.0, -0.0196376, -0.0786361, 1.0982735, ); /** * Defines supported color spaces by transfer function and primaries, * and provides conversions to/from the Linear-sRGB reference space. */ const COLOR_SPACES = { [LinearSRGBColorSpace]: { transfer: LinearTransfer, primaries: Rec709Primaries, toReference: color => color, fromReference: color => color, }, [SRGBColorSpace]: { transfer: SRGBTransfer, primaries: Rec709Primaries, toReference: color => color.convertSRGBToLinear(), fromReference: color => color.convertLinearToSRGB(), }, [LinearDisplayP3ColorSpace]: { transfer: LinearTransfer, primaries: P3Primaries, toReference: color => color.applyMatrix3(LINEAR_DISPLAY_P3_TO_LINEAR_SRGB), fromReference: color => color.applyMatrix3(LINEAR_SRGB_TO_LINEAR_DISPLAY_P3), }, [DisplayP3ColorSpace]: { transfer: SRGBTransfer, primaries: P3Primaries, toReference: color => color .convertSRGBToLinear() .applyMatrix3(LINEAR_DISPLAY_P3_TO_LINEAR_SRGB), fromReference: color => color .applyMatrix3(LINEAR_SRGB_TO_LINEAR_DISPLAY_P3) .convertLinearToSRGB(), }, }; const SUPPORTED_WORKING_COLOR_SPACES = new Set([ LinearSRGBColorSpace, LinearDisplayP3ColorSpace, ]); const ColorManagement = { enabled: true, _workingColorSpace: LinearSRGBColorSpace, get workingColorSpace() { return this._workingColorSpace; }, set workingColorSpace(colorSpace) { if (!SUPPORTED_WORKING_COLOR_SPACES.has(colorSpace)) { throw new Error(`Unsupported working color space, "${colorSpace}".`); } this._workingColorSpace = colorSpace; }, convert: function (color, sourceColorSpace, targetColorSpace) { if ( this.enabled === false || sourceColorSpace === targetColorSpace || !sourceColorSpace || !targetColorSpace ) { return color; } const sourceToReference = COLOR_SPACES[sourceColorSpace].toReference; const targetFromReference = COLOR_SPACES[targetColorSpace].fromReference; return targetFromReference(sourceToReference(color)); }, fromWorkingColorSpace: function (color, targetColorSpace) { return this.convert(color, this._workingColorSpace, targetColorSpace); }, toWorkingColorSpace: function (color, sourceColorSpace) { return this.convert(color, sourceColorSpace, this._workingColorSpace); }, getPrimaries: function (colorSpace) { return COLOR_SPACES[colorSpace].primaries; }, getTransfer: function (colorSpace) { if (colorSpace === NoColorSpace) return LinearTransfer; return COLOR_SPACES[colorSpace].transfer; }, }; function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement === "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image, ); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) ) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height, }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.", ); return image; } } } let _sourceId = 0; class Source { constructor(data = null) { this.isSource = true; Object.defineProperty(this, "id", {value: _sourceId++}); this.uuid = generateUUID(); this.data = data; this.dataReady = true; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.images[this.uuid] !== undefined) { return meta.images[this.uuid]; } const output = { uuid: this.uuid, url: "", }; const data = this.data; if (data !== null) { let url; if (Array.isArray(data)) { // cube texture url = []; for (let i = 0, l = data.length; i < l; i++) { if (data[i].isDataTexture) { url.push(serializeImage(data[i].image)); } else { url.push(serializeImage(data[i])); } } } else { // texture url = serializeImage(data); } output.url = url; } if (!isRootObject) { meta.images[this.uuid] = output; } return output; } } function serializeImage(image) { if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) ) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.from(image.data), width: image.width, height: image.height, type: image.data.constructor.name, }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } let _textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace, ) { super(); this.isTexture = true; Object.defineProperty(this, "id", {value: _textureId++}); this.uuid = generateUUID(); this.name = ""; this.source = new Source(image); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) this.colorSpace = colorSpace; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.pmremVersion = 0; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image(value = null) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y, ); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse(JSON.stringify(source.userData)); this.needsUpdate = true; return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { metadata: { version: 4.6, type: "Texture", generator: "Texture.toJSON", }, uuid: this.uuid, name: this.name, image: this.source.toJSON(meta).uuid, mapping: this.mapping, channel: this.channel, 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, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment, }; if (Object.keys(this.userData).length > 0) output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({type: "dispose"}); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } set needsPMREMUpdate(value) { if (value === true) { this.pmremVersion++; } } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if ( Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2 ) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( (m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12), ); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); this.z = Math.trunc(this.z); this.w = Math.trunc(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return ( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w, ); } manhattanLength() { return ( Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w) ); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class RenderTarget extends EventDispatcher { constructor(width = 1, height = 1, options = {}) { super(); this.isRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); const image = {width: width, height: height, depth: 1}; options = Object.assign( { generateMipmaps: false, internalFormat: null, minFilter: LinearFilter, depthBuffer: true, stencilBuffer: false, resolveDepthBuffer: true, resolveStencilBuffer: true, depthTexture: null, samples: 0, count: 1, }, options, ); const texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace, ); texture.flipY = false; texture.generateMipmaps = options.generateMipmaps; texture.internalFormat = options.internalFormat; this.textures = []; const count = options.count; for (let i = 0; i < count; i++) { this.textures[i] = texture.clone(); this.textures[i].isRenderTargetTexture = true; } this.depthBuffer = options.depthBuffer; this.stencilBuffer = options.stencilBuffer; this.resolveDepthBuffer = options.resolveDepthBuffer; this.resolveStencilBuffer = options.resolveStencilBuffer; this.depthTexture = options.depthTexture; this.samples = options.samples; } get texture() { return this.textures[0]; } set texture(value) { this.textures[0] = value; } setSize(width, height, depth = 1) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.textures.length; i < il; i++) { this.textures[i].image.width = width; this.textures[i].image.height = height; this.textures[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy(source.scissor); this.scissorTest = source.scissorTest; this.viewport.copy(source.viewport); this.textures.length = 0; for (let i = 0, il = source.textures.length; i < il; i++) { this.textures[i] = source.textures[i].clone(); this.textures[i].isRenderTargetTexture = true; } // ensure image object is not shared, see #20328 const image = Object.assign({}, source.texture.image); this.texture.source = new Source(image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.resolveDepthBuffer = source.resolveDepthBuffer; this.resolveStencilBuffer = source.resolveStencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent({type: "dispose"}); } } class WebGLRenderTarget extends RenderTarget { constructor(width = 1, height = 1, options = {}) { super(width, height, options); this.isWebGLRenderTarget = true; } } class DataArrayTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.isDataArrayTexture = true; this.image = {data, width, height, depth}; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; this.layerUpdates = new Set(); } addLayerUpdate(layerIndex) { this.layerUpdates.add(layerIndex); } clearLayerUpdates() { this.layerUpdates.clear(); } } class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1, options = {}) { super(width, height, options); this.isWebGLArrayRenderTarget = true; this.depth = depth; this.texture = new DataArrayTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class Data3DTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.isData3DTexture = true; this.image = {data, width, height, depth}; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGL3DRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1, options = {}) { super(width, height, options); this.isWebGL3DRenderTarget = true; this.depth = depth; this.texture = new Data3DTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, ) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update = true) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order, ); } if (update === true) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return ( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w, ); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q) { return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); // normalize calls _onChangeCallback() return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // sets this quaternion to a uniform random unit quaternnion // Ken Shoemake // Uniform random rotations // D. Kirk, editor, Graphics Gems III, pages 124-132. Academic Press, New York, 1992. const theta1 = 2 * Math.PI * Math.random(); const theta2 = 2 * Math.PI * Math.random(); const x0 = Math.random(); const r1 = Math.sqrt(1 - x0); const r2 = Math.sqrt(x0); return this.set( r1 * Math.sin(theta1), r1 * Math.cos(theta1), r2 * Math.sin(theta2), r2 * Math.cos(theta2), ); } equals(quaternion) { return ( quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w ); } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); this._onChangeCallback(); return this; } toJSON() { return this.toArray(); } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor(x = 0, y = 0, z = 0) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { // quaternion q is assumed to have unit length const vx = this.x, vy = this.y, vz = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // t = 2 * cross( q.xyz, v ); const tx = 2 * (qy * vz - qz * vy); const ty = 2 * (qz * vx - qx * vz); const tz = 2 * (qx * vy - qy * vx); // v + q.w * t + cross( q.xyz, t ); this.x = vx + qw * tx + qy * tz - qz * ty; this.y = vy + qw * ty + qz * tx - qx * tz; this.z = vz + qw * tz + qx * ty - qy * tx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4( camera.projectionMatrix, ); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4( camera.matrixWorld, ); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); this.z = Math.trunc(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v) { return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$c.copy(normal).multiplyScalar(2 * this.dot(normal)), ); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return ( Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z) ); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } setFromEuler(e) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor(c) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // https://mathworld.wolfram.com/SpherePointPicking.html const theta = Math.random() * Math.PI * 2; const u = Math.random() * 2 - 1; const c = Math.sqrt(1 - u * u); this.x = c * Math.cos(theta); this.y = u; this.z = c * Math.sin(theta); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } const _vector$c = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity), ) { this.isBox3 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { this.makeEmpty(); for (let i = 0, il = array.length; i < il; i += 3) { this.expandByPoint(_vector$b.fromArray(array, i)); } return this; } setFromBufferAttribute(attribute) { this.makeEmpty(); for (let i = 0, il = attribute.count; i < il; i++) { this.expandByPoint(_vector$b.fromBufferAttribute(attribute, i)); } return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z ); } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { const positionAttribute = geometry.getAttribute("position"); // precise AABB computation based on vertex data requires at least a position attribute. // instancing isn"t supported so far and uses the normal (conservative) code path. if ( precise === true && positionAttribute !== undefined && object.isInstancedMesh !== true ) { for (let i = 0, l = positionAttribute.count; i < l; i++) { if (object.isMesh === true) { object.getVertexPosition(i, _vector$b); } else { _vector$b.fromBufferAttribute(positionAttribute, i); } _vector$b.applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (object.boundingBox !== undefined) { // object-level bounding box if (object.boundingBox === null) { object.computeBoundingBox(); } _box$4.copy(object.boundingBox); } else { // geometry-level bounding box if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$4.copy(geometry.boundingBox); } _box$4.applyMatrix4(object.matrixWorld); this.union(_box$4); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return ( this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z ); } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( (point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z), ); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return ( _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius ); } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$4.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$4, _v1$7); _f2.subVectors(_v0$2, _v2$4); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0, ]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$b).distanceTo(point); } getBoundingSphere(target) { if (this.isEmpty()) { target.makeEmpty(); } else { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; } return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), ]; const _vector$b = /*@__PURE__*/ new Vector3(); const _box$4 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$4 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the separating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$3 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.isSphere = true; this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$3.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return ( sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum ); } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { if (this.isEmpty()) { this.center.copy(point); this.radius = 0; return this; } _v1$6.subVectors(point, this.center); const lengthSq = _v1$6.lengthSq(); if (lengthSq > this.radius * this.radius) { // calculate the minimal sphere const length = Math.sqrt(lengthSq); const delta = (length - this.radius) * 0.5; this.center.addScaledVector(_v1$6, delta / length); this.radius += delta; } return this; } union(sphere) { if (sphere.isEmpty()) { return this; } if (this.isEmpty()) { this.copy(sphere); return this; } if (this.center.equals(sphere.center) === true) { this.radius = Math.max(this.radius, sphere.radius); } else { _v2$3.subVectors(sphere.center, this.center).setLength(sphere.radius); this.expandByPoint(_v1$6.copy(sphere.center).add(_v2$3)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_v2$3)); } return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.origin).addScaledVector(this.direction, t); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target .copy(this.origin) .addScaledVector(this.direction, directionDistance); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a .subVectors(point, this.origin) .dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a .copy(this.origin) .addScaledVector(this.direction, directionDistance); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.origin).addScaledVector(this.direction, s0); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segCenter).addScaledVector(_segDir, s1); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if (t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return ( this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius ); } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; if (tymin > tmin || isNaN(tmin)) tmin = tymin; if (tymax < tmax || isNaN(tmax)) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ( ray.origin.equals(this.origin) && ray.direction.equals(this.direction) ); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ) { Matrix4.prototype.isMatrix4 = true; this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (n11 !== undefined) { this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ); } } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set( me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1, ); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1, ); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31) ); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { if (x.isVector3) { this.set(1, 0, 0, x.x, 0, 1, 0, x.y, 0, 0, 1, x.z, 0, 0, 0, 1); } else { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); } return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1, ); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$4.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$4.elements[0] *= invSX; _m1$4.elements[1] *= invSX; _m1$4.elements[2] *= invSX; _m1$4.elements[4] *= invSY; _m1$4.elements[5] *= invSY; _m1$4.elements[6] *= invSY; _m1$4.elements[8] *= invSZ; _m1$4.elements[9] *= invSZ; _m1$4.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$4); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, ) { const te = this.elements; const x = (2 * near) / (right - left); const y = (2 * near) / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); let c, d; if (coordinateSystem === WebGLCoordinateSystem) { c = -(far + near) / (far - near); d = (-2 * far * near) / (far - near); } else if (coordinateSystem === WebGPUCoordinateSystem) { c = -far / (far - near); d = (-far * near) / (far - near); } else { throw new Error( "THREE.Matrix4.makePerspective(): Invalid coordinate system: " + coordinateSystem, ); } te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, ) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; let z, zInv; if (coordinateSystem === WebGLCoordinateSystem) { z = (far + near) * p; zInv = -2 * p; } else if (coordinateSystem === WebGPUCoordinateSystem) { z = near * p; zInv = -1 * p; } else { throw new Error( "THREE.Matrix4.makeOrthographic(): Invalid coordinate system: " + coordinateSystem, ); } te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = zInv; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$4 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3(0, 0, 0); const _one = /*@__PURE__*/ new Vector3(1, 1, 1); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix$2 = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order, ); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$2.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$2, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return ( euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order ); } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = "XYZ"; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = ((1 << channel) | 0) >>> 0; } enable(channel) { this.mask |= (1 << channel) | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= (1 << channel) | 0; } disable(channel) { this.mask &= ~((1 << channel) | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & ((1 << channel) | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$3 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/ new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/ new Vector3(0, 0, 1); const _addedEvent = {type: "added"}; const _removedEvent = {type: "removed"}; const _childaddedEvent = {type: "childadded", child: null}; const _childremovedEvent = {type: "childremoved", child: null}; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty(this, "id", {value: _object3DId++}); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position, }, rotation: { configurable: true, enumerable: true, value: rotation, }, quaternion: { configurable: true, enumerable: true, value: quaternion, }, scale: { configurable: true, enumerable: true, value: scale, }, modelViewMatrix: { value: new Matrix4(), }, normalMatrix: { value: new Matrix3(), }, }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeShadow(/* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */) {} onAfterShadow(/* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */) {} onBeforeRender(/* renderer, scene, camera, geometry, material, group */) {} onAfterRender(/* renderer, scene, camera, geometry, material, group */) {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(_m1$3.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$3.lookAt(_position$3, _target, this.up); } else { _m1$3.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$3); if (parent) { _m1$3.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$3); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object, ); return this; } if (object && object.isObject3D) { object.removeFromParent(); object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); _childaddedEvent.child = object; this.dispatchEvent(_childaddedEvent); _childaddedEvent.child = null; } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object, ); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); _childremovedEvent.child = object; this.dispatchEvent(_childremovedEvent); _childremovedEvent.child = null; } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { return this.remove(...this.children); } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$3.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$3.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$3); object.removeFromParent(); object.parent = this; this.children.push(object); object.updateWorldMatrix(false, true); object.dispatchEvent(_addedEvent); _childaddedEvent.child = object; this.dispatchEvent(_childaddedEvent); _childaddedEvent.child = null; return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getObjectsByProperty(name, value, result = []) { if (this[name] === value) result.push(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].getObjectsByProperty(name, value, result); } return result; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast(/* raycaster, intersects */) {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true || force === true) { child.updateMatrixWorld(force); } } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true) { child.updateWorldMatrix(false, true); } } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {}, }; output.metadata = { version: 4.6, type: "Object", generator: "Object3D.toJSON", }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (Object.keys(this.userData).length > 0) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } if (this.isBatchedMesh) { object.type = "BatchedMesh"; object.perObjectFrustumCulled = this.perObjectFrustumCulled; object.sortObjects = this.sortObjects; object.drawRanges = this._drawRanges; object.reservedRanges = this._reservedRanges; object.visibility = this._visibility; object.active = this._active; object.bounds = this._bounds.map(bound => ({ boxInitialized: bound.boxInitialized, boxMin: bound.box.min.toArray(), boxMax: bound.box.max.toArray(), sphereInitialized: bound.sphereInitialized, sphereRadius: bound.sphere.radius, sphereCenter: bound.sphere.center.toArray(), })); object.maxGeometryCount = this._maxGeometryCount; object.maxVertexCount = this._maxVertexCount; object.maxIndexCount = this._maxIndexCount; object.geometryInitialized = this._geometryInitialized; object.geometryCount = this._geometryCount; object.matricesTexture = this._matricesTexture.toJSON(meta); if (this._colorsTexture !== null) object.colorsTexture = this._colorsTexture.toJSON(meta); if (this.boundingSphere !== null) { object.boundingSphere = { center: object.boundingSphere.center.toArray(), radius: object.boundingSphere.radius, }; } if (this.boundingBox !== null) { object.boundingBox = { min: object.boundingBox.min.toArray(), max: object.boundingBox.max.toArray(), }; } } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); const nodes = extractFromCache(meta.nodes); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; if (nodes.length > 0) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations.slice(); this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3(0, 1, 0); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$2 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { target.set(0, 0, 0); return null; } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { // if the triangle is degenerate then we can"t contain a point if (this.getBarycoord(point, a, b, c, _v3$2) === null) { return false; } return _v3$2.x >= 0 && _v3$2.y >= 0 && _v3$2.x + _v3$2.y <= 1; } static getInterpolation(point, p1, p2, p3, v1, v2, v3, target) { if (this.getBarycoord(point, p1, p2, p3, _v3$2) === null) { target.x = 0; target.y = 0; if ("z" in target) target.z = 0; if ("w" in target) target.w = 0; return null; } target.setScalar(0); target.addScaledVector(v1, _v3$2.x); target.addScaledVector(v2, _v3$2.y); target.addScaledVector(v3, _v3$2.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target .addVectors(this.a, this.b) .add(this.c) .multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getInterpolation(point, v1, v2, v3, target) { return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target, ); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return ( triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c) ); } } const _colorKeywords = { aliceblue: 0xf0f8ff, antiquewhite: 0xfaebd7, aqua: 0x00ffff, aquamarine: 0x7fffd4, azure: 0xf0ffff, beige: 0xf5f5dc, bisque: 0xffe4c4, black: 0x000000, blanchedalmond: 0xffebcd, blue: 0x0000ff, blueviolet: 0x8a2be2, brown: 0xa52a2a, burlywood: 0xdeb887, cadetblue: 0x5f9ea0, chartreuse: 0x7fff00, chocolate: 0xd2691e, coral: 0xff7f50, cornflowerblue: 0x6495ed, cornsilk: 0xfff8dc, crimson: 0xdc143c, cyan: 0x00ffff, darkblue: 0x00008b, darkcyan: 0x008b8b, darkgoldenrod: 0xb8860b, darkgray: 0xa9a9a9, darkgreen: 0x006400, darkgrey: 0xa9a9a9, darkkhaki: 0xbdb76b, darkmagenta: 0x8b008b, darkolivegreen: 0x556b2f, darkorange: 0xff8c00, darkorchid: 0x9932cc, darkred: 0x8b0000, darksalmon: 0xe9967a, darkseagreen: 0x8fbc8f, darkslateblue: 0x483d8b, darkslategray: 0x2f4f4f, darkslategrey: 0x2f4f4f, darkturquoise: 0x00ced1, darkviolet: 0x9400d3, deeppink: 0xff1493, deepskyblue: 0x00bfff, dimgray: 0x696969, dimgrey: 0x696969, dodgerblue: 0x1e90ff, firebrick: 0xb22222, floralwhite: 0xfffaf0, forestgreen: 0x228b22, fuchsia: 0xff00ff, gainsboro: 0xdcdcdc, ghostwhite: 0xf8f8ff, gold: 0xffd700, goldenrod: 0xdaa520, gray: 0x808080, green: 0x008000, greenyellow: 0xadff2f, grey: 0x808080, honeydew: 0xf0fff0, hotpink: 0xff69b4, indianred: 0xcd5c5c, indigo: 0x4b0082, ivory: 0xfffff0, khaki: 0xf0e68c, lavender: 0xe6e6fa, lavenderblush: 0xfff0f5, lawngreen: 0x7cfc00, lemonchiffon: 0xfffacd, lightblue: 0xadd8e6, lightcoral: 0xf08080, lightcyan: 0xe0ffff, lightgoldenrodyellow: 0xfafad2, lightgray: 0xd3d3d3, lightgreen: 0x90ee90, lightgrey: 0xd3d3d3, lightpink: 0xffb6c1, lightsalmon: 0xffa07a, lightseagreen: 0x20b2aa, lightskyblue: 0x87cefa, lightslategray: 0x778899, lightslategrey: 0x778899, lightsteelblue: 0xb0c4de, lightyellow: 0xffffe0, lime: 0x00ff00, limegreen: 0x32cd32, linen: 0xfaf0e6, magenta: 0xff00ff, maroon: 0x800000, mediumaquamarine: 0x66cdaa, mediumblue: 0x0000cd, mediumorchid: 0xba55d3, mediumpurple: 0x9370db, mediumseagreen: 0x3cb371, mediumslateblue: 0x7b68ee, mediumspringgreen: 0x00fa9a, mediumturquoise: 0x48d1cc, mediumvioletred: 0xc71585, midnightblue: 0x191970, mintcream: 0xf5fffa, mistyrose: 0xffe4e1, moccasin: 0xffe4b5, navajowhite: 0xffdead, navy: 0x000080, oldlace: 0xfdf5e6, olive: 0x808000, olivedrab: 0x6b8e23, orange: 0xffa500, orangered: 0xff4500, orchid: 0xda70d6, palegoldenrod: 0xeee8aa, palegreen: 0x98fb98, paleturquoise: 0xafeeee, palevioletred: 0xdb7093, papayawhip: 0xffefd5, peachpuff: 0xffdab9, peru: 0xcd853f, pink: 0xffc0cb, plum: 0xdda0dd, powderblue: 0xb0e0e6, purple: 0x800080, rebeccapurple: 0x663399, red: 0xff0000, rosybrown: 0xbc8f8f, royalblue: 0x4169e1, saddlebrown: 0x8b4513, salmon: 0xfa8072, sandybrown: 0xf4a460, seagreen: 0x2e8b57, seashell: 0xfff5ee, sienna: 0xa0522d, silver: 0xc0c0c0, skyblue: 0x87ceeb, slateblue: 0x6a5acd, slategray: 0x708090, slategrey: 0x708090, snow: 0xfffafa, springgreen: 0x00ff7f, steelblue: 0x4682b4, tan: 0xd2b48c, teal: 0x008080, thistle: 0xd8bfd8, tomato: 0xff6347, turquoise: 0x40e0d0, violet: 0xee82ee, wheat: 0xf5deb3, white: 0xffffff, whitesmoke: 0xf5f5f5, yellow: 0xffff00, yellowgreen: 0x9acd32, }; const _hslA = {h: 0, s: 0, l: 0}; const _hslB = {h: 0, s: 0, l: 0}; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } class Color { constructor(r, g, b) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; return this.set(r, g, b); } set(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string const value = r; if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } } else { this.setRGB(r, g, b); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex, colorSpace = SRGBColorSpace) { hex = Math.floor(hex); this.r = ((hex >> 16) & 255) / 255; this.g = ((hex >> 8) & 255) / 255; this.b = (hex & 255) / 255; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setRGB(r, g, b, colorSpace = ColorManagement.workingColorSpace) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setHSL(h, s, l, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setStyle(style, colorSpace = SRGBColorSpace) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored.", ); } } let m; if ((m = /^(w+)(([^)]*))/.exec(style))) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if ( (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha(color[4]); return this.setRGB( Math.min(255, parseInt(color[1], 10)) / 255, Math.min(255, parseInt(color[2], 10)) / 255, Math.min(255, parseInt(color[3], 10)) / 255, colorSpace, ); } if ( (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha(color[4]); return this.setRGB( Math.min(100, parseInt(color[1], 10)) / 100, Math.min(100, parseInt(color[2], 10)) / 100, Math.min(100, parseInt(color[3], 10)) / 100, colorSpace, ); } break; case "hsl": case "hsla": if ( (color = /^s*(d*.?d+)s*,s*(d*.?d+)\%s*,s*(d*.?d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha(color[4]); return this.setHSL( parseFloat(color[1]) / 360, parseFloat(color[2]) / 100, parseFloat(color[3]) / 100, colorSpace, ); } break; default: console.warn("THREE.Color: Unknown color model " + style); } } else if ((m = /^#([A-Fa-fd]+)$/.exec(style))) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 return this.setRGB( parseInt(hex.charAt(0), 16) / 15, parseInt(hex.charAt(1), 16) / 15, parseInt(hex.charAt(2), 16) / 15, colorSpace, ); } else if (size === 6) { // #ff0000 return this.setHex(parseInt(hex, 16), colorSpace); } else { console.warn("THREE.Color: Invalid hex color " + style); } } else if (style && style.length > 0) { return this.setColorName(style, colorSpace); } return this; } setColorName(style, colorSpace = SRGBColorSpace) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex, colorSpace); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); return ( Math.round(clamp(_color.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color.g * 255, 0, 255)) * 256 + Math.round(clamp(_color.b * 255, 0, 255)) ); } getHexString(colorSpace = SRGBColorSpace) { return ("000000" + this.getHex(colorSpace).toString(16)).slice(-6); } getHSL(target, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB(target, colorSpace = ColorManagement.workingColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; if (colorSpace !== SRGBColorSpace) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${colorSpace} ${r.toFixed(3)} ${g.toFixed(3)} ${b.toFixed(3)})`; } return `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`; } offsetHSL(h, s, l) { this.getHSL(_hslA); return this.setHSL(_hslA.h + h, _hslA.s + s, _hslA.l + l); } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } setFromVector3(v) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3(m) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[0] * r + e[3] * g + e[6] * b; this.g = e[1] * r + e[4] * g + e[7] * b; this.b = e[2] * r + e[5] * g + e[8] * b; return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); return this; } toJSON() { return this.getHex(); } *[Symbol.iterator]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/ new Color(); Color.NAMES = _colorKeywords; let _materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty(this, "id", {value: _materialId++}); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.blendColor = new Color(0, 0, 0); this.blendAlpha = 0; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild(/* shaderobject, renderer */) {} onBeforeRender(/* renderer, scene, camera, geometry, object, group */) {} onBeforeCompile(/* shaderobject, renderer */) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn( `THREE.Material: parameter "${key}" has value of undefined.`, ); continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn( `THREE.Material: "${key}" is not a property of THREE.${this.type}.`, ); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if ( currentValue && currentValue.isVector3 && newValue && newValue.isVector3 ) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (isRootObject) { meta = { textures: {}, images: {}, }; } const data = { metadata: { version: 4.6, type: "Material", generator: "Material.toJSON", }, }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity !== undefined && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.dispersion !== undefined) data.dispersion = this.dispersion; if (this.iridescence !== undefined) data.iridescence = this.iridescence; if (this.iridescenceIOR !== undefined) data.iridescenceIOR = this.iridescenceIOR; if (this.iridescenceThicknessRange !== undefined) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if (this.iridescenceMap && this.iridescenceMap.isTexture) { data.iridescenceMap = this.iridescenceMap.toJSON(meta).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON(meta).uuid; } if (this.anisotropy !== undefined) data.anisotropy = this.anisotropy; if (this.anisotropyRotation !== undefined) data.anisotropyRotation = this.anisotropyRotation; if (this.anisotropyMap && this.anisotropyMap.isTexture) { data.anisotropyMap = this.anisotropyMap.toJSON(meta).uuid; } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapRotation !== undefined) data.envMapRotation = this.envMapRotation.toArray(); if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors === true) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = true; if (this.blendSrc !== SrcAlphaFactor) data.blendSrc = this.blendSrc; if (this.blendDst !== OneMinusSrcAlphaFactor) data.blendDst = this.blendDst; if (this.blendEquation !== AddEquation) data.blendEquation = this.blendEquation; if (this.blendSrcAlpha !== null) data.blendSrcAlpha = this.blendSrcAlpha; if (this.blendDstAlpha !== null) data.blendDstAlpha = this.blendDstAlpha; if (this.blendEquationAlpha !== null) data.blendEquationAlpha = this.blendEquationAlpha; if (this.blendColor && this.blendColor.isColor) data.blendColor = this.blendColor.getHex(); if (this.blendAlpha !== 0) data.blendAlpha = this.blendAlpha; if (this.depthFunc !== LessEqualDepth) data.depthFunc = this.depthFunc; if (this.depthTest === false) data.depthTest = this.depthTest; if (this.depthWrite === false) data.depthWrite = this.depthWrite; if (this.colorWrite === false) data.colorWrite = this.colorWrite; if (this.stencilWriteMask !== 0xff) data.stencilWriteMask = this.stencilWriteMask; if (this.stencilFunc !== AlwaysStencilFunc) data.stencilFunc = this.stencilFunc; if (this.stencilRef !== 0) data.stencilRef = this.stencilRef; if (this.stencilFuncMask !== 0xff) data.stencilFuncMask = this.stencilFuncMask; if (this.stencilFail !== KeepStencilOp) data.stencilFail = this.stencilFail; if (this.stencilZFail !== KeepStencilOp) data.stencilZFail = this.stencilZFail; if (this.stencilZPass !== KeepStencilOp) data.stencilZPass = this.stencilZPass; if (this.stencilWrite === true) data.stencilWrite = this.stencilWrite; // rotation (SpriteMaterial) if (this.rotation !== undefined && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth !== undefined && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaHash === true) data.alphaHash = true; if (this.alphaToCoverage === true) data.alphaToCoverage = true; if (this.premultipliedAlpha === true) data.premultipliedAlpha = true; if (this.forceSinglePass === true) data.forceSinglePass = true; if (this.wireframe === true) data.wireframe = true; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = true; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (this.fog === false) data.fog = false; if (Object.keys(this.userData).length > 0) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRootObject) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.blendColor.copy(source.blendColor); this.blendAlpha = source.blendAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaHash = source.alphaHash; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({type: "dispose"}); } set needsUpdate(value) { if (value === true) this.version++; } } class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } // Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf const _tables = /*@__PURE__*/ _generateTables(); function _generateTables() { // float32 to float16 helpers const buffer = new ArrayBuffer(4); const floatView = new Float32Array(buffer); const uint32View = new Uint32Array(buffer); const baseTable = new Uint32Array(512); const shiftTable = new Uint32Array(512); for (let i = 0; i < 256; ++i) { const e = i - 127; // very small number (0, -0) if (e < -27) { baseTable[i] = 0x0000; baseTable[i | 0x100] = 0x8000; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // small number (denorm) } else if (e < -14) { baseTable[i] = 0x0400 >> (-e - 14); baseTable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; shiftTable[i] = -e - 1; shiftTable[i | 0x100] = -e - 1; // normal number } else if (e <= 15) { baseTable[i] = (e + 15) << 10; baseTable[i | 0x100] = ((e + 15) << 10) | 0x8000; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; // large number (Infinity, -Infinity) } else if (e < 128) { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // stay (NaN, Infinity, -Infinity) } else { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; } } // float16 to float32 helpers const mantissaTable = new Uint32Array(2048); const exponentTable = new Uint32Array(64); const offsetTable = new Uint32Array(64); for (let i = 1; i < 1024; ++i) { let m = i << 13; // zero pad mantissa bits let e = 0; // zero exponent // normalized while ((m & 0x00800000) === 0) { m <<= 1; e -= 0x00800000; // decrement exponent } m &= ~0x00800000; // clear leading 1 bit e += 0x38800000; // adjust bias mantissaTable[i] = m | e; } for (let i = 1024; i < 2048; ++i) { mantissaTable[i] = 0x38000000 + ((i - 1024) << 13); } for (let i = 1; i < 31; ++i) { exponentTable[i] = i << 23; } exponentTable[31] = 0x47800000; exponentTable[32] = 0x80000000; for (let i = 33; i < 63; ++i) { exponentTable[i] = 0x80000000 + ((i - 32) << 23); } exponentTable[63] = 0xc7800000; for (let i = 1; i < 64; ++i) { if (i !== 32) { offsetTable[i] = 1024; } } return { floatView: floatView, uint32View: uint32View, baseTable: baseTable, shiftTable: shiftTable, mantissaTable: mantissaTable, exponentTable: exponentTable, offsetTable: offsetTable, }; } // float32 to float16 function toHalfFloat(val) { if (Math.abs(val) > 65504) console.warn("THREE.DataUtils.toHalfFloat(): Value out of range."); val = clamp(val, -65504, 65504); _tables.floatView[0] = val; const f = _tables.uint32View[0]; const e = (f >> 23) & 0x1ff; return _tables.baseTable[e] + ((f & 0x007fffff) >> _tables.shiftTable[e]); } // float16 to float32 function fromHalfFloat(val) { const m = val >> 10; _tables.uint32View[0] = _tables.mantissaTable[_tables.offsetTable[m] + (val & 0x3ff)] + _tables.exponentTable[m]; return _tables.floatView[0]; } const DataUtils = { toHalfFloat: toHalfFloat, fromHalfFloat: fromHalfFloat, }; const _vector$9 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized = false) { if (Array.isArray(array)) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array.", ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this._updateRange = {offset: 0, count: -1}; this.updateRanges = []; this.gpuType = FloatType; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } get updateRange() { warnOnce( "THREE.BufferAttribute: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.", ); // @deprecated, r159 return this._updateRange; } setUsage(value) { this.usage = value; return this; } addUpdateRange(start, count) { this.updateRanges.push({start, count}); } clearUpdateRanges() { this.updateRanges.length = 0; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; this.gpuType = source.gpuType; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set(value, offset); return this; } getComponent(index, component) { let value = this.array[index * this.itemSize + component]; if (this.normalized) value = denormalize(value, this.array); return value; } setComponent(index, component, value) { if (this.normalized) value = normalize(value, this.array); this.array[index * this.itemSize + component] = value; return this; } getX(index) { let x = this.array[index * this.itemSize]; if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = x; return this; } getY(index) { let y = this.array[index * this.itemSize + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { let z = this.array[index * this.itemSize + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = z; return this; } getW(index) { let w = this.array[index * this.itemSize + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from(this.array), normalized: this.normalized, }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; return data; } } // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); this.isFloat16BufferAttribute = true; } getX(index) { let x = fromHalfFloat(this.array[index * this.itemSize]); if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = toHalfFloat(x); return this; } getY(index) { let y = fromHalfFloat(this.array[index * this.itemSize + 1]); if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = toHalfFloat(y); return this; } getZ(index) { let z = fromHalfFloat(this.array[index * this.itemSize + 2]); if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = toHalfFloat(z); return this; } getW(index) { let w = fromHalfFloat(this.array[index * this.itemSize + 3]); if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = toHalfFloat(w); return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); this.array[index + 3] = toHalfFloat(w); return this; } } class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } let _id$2 = 0; const _m1$2 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$2 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$8 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty(this, "id", {value: _id$2++}); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = {start: 0, count: Infinity}; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new ( arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute )(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex, }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1$2.makeRotationFromQuaternion(q); this.applyMatrix4(_m1$2); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1$2.makeRotationX(angle); this.applyMatrix4(_m1$2); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1$2.makeRotationY(angle); this.applyMatrix4(_m1$2); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1$2.makeRotationZ(angle); this.applyMatrix4(_m1$2); return this; } translate(x, y, z) { // translate geometry _m1$2.makeTranslation(x, y, z); this.applyMatrix4(_m1$2); return this; } scale(x, y, z) { // scale geometry _m1$2.makeScale(x, y, z); this.applyMatrix4(_m1$2); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.", this, ); this.boundingBox.set( new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity), ); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$2.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$2.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$2.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$2.min); this.boundingBox.expandByPoint(_box$2.max); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this, ); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.", this, ); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$2.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$2.min, _boxMorphTargets.min); _box$2.expandByPoint(_vector$8); _vector$8.addVectors(_box$2.max, _boxMorphTargets.max); _box$2.expandByPoint(_vector$8); } else { _box$2.expandByPoint(_boxMorphTargets.min); _box$2.expandByPoint(_boxMorphTargets.max); } } } _box$2.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared(_vector$8), ); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared(_vector$8), ); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this, ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)", ); return; } const positionAttribute = attributes.position; const normalAttribute = attributes.normal; const uvAttribute = attributes.uv; if (this.hasAttribute("tangent") === false) { this.setAttribute( "tangent", new BufferAttribute(new Float32Array(4 * positionAttribute.count), 4), ); } const tangentAttribute = this.getAttribute("tangent"); const tan1 = [], tan2 = []; for (let i = 0; i < positionAttribute.count; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromBufferAttribute(positionAttribute, a); vB.fromBufferAttribute(positionAttribute, b); vC.fromBufferAttribute(positionAttribute, c); uvA.fromBufferAttribute(uvAttribute, a); uvB.fromBufferAttribute(uvAttribute, b); uvC.fromBufferAttribute(uvAttribute, c); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir .copy(vB) .multiplyScalar(uvC.y) .addScaledVector(vC, -uvB.y) .multiplyScalar(r); tdir .copy(vC) .multiplyScalar(uvB.x) .addScaledVector(vB, -uvC.x) .multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [ { start: 0, count: index.count, }, ]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(index.getX(j + 0), index.getX(j + 1), index.getX(j + 2)); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromBufferAttribute(normalAttribute, v); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangentAttribute.setXYZW(v, tmp.x, tmp.y, tmp.z, w); } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(index.getX(j + 0)); handleVertex(index.getX(j + 1)); handleVertex(index.getX(j + 2)); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute( new Float32Array(positionAttribute.count * 3), 3, ); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.", ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.6, type: "BufferGeometry", generator: "BufferGeometry.toJSON", }, }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = {attributes: {}}; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array), }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius, }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent({type: "dispose"}); } } const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); const _ray$3 = /*@__PURE__*/ new Ray(); const _sphere$6 = /*@__PURE__*/ new Sphere(); const _sphereHitAt = /*@__PURE__*/ new Vector3(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _normalA = /*@__PURE__*/ new Vector3(); const _normalB = /*@__PURE__*/ new Vector3(); const _normalC = /*@__PURE__*/ new Vector3(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial(), ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary, ); } this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } getVertexPosition(index, target) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute(position, index); const morphInfluences = this.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, index); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); } else { _morphA.addScaledVector(_tempA.sub(target), influence); } } target.add(_morphA); } return target; } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$6.copy(geometry.boundingSphere); _sphere$6.applyMatrix4(matrixWorld); // check distance from ray origin to bounding sphere _ray$3.copy(raycaster.ray).recast(raycaster.near); if (_sphere$6.containsPoint(_ray$3.origin) === false) { if (_ray$3.intersectSphere(_sphere$6, _sphereHitAt) === null) return; if ( _ray$3.origin.distanceToSquared(_sphereHitAt) > (raycaster.far - raycaster.near) ** 2 ) return; } // convert ray to local space of mesh _inverseMatrix$3.copy(matrixWorld).invert(); _ray$3.copy(raycaster.ray).applyMatrix4(_inverseMatrix$3); // test with bounding box in local space if (geometry.boundingBox !== null) { if (_ray$3.intersectsBox(geometry.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$3); } _computeIntersections(raycaster, intersects, rayLocalSpace) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min( index.count, Math.min( group.start + group.count, drawRange.start + drawRange.count, ), ); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min( position.count, Math.min( group.start + group.count, drawRange.start + drawRange.count, ), ); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } } function checkIntersection$1( object, material, raycaster, ray, pA, pB, pC, point, ) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side === FrontSide, point, ); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object, }; } function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c, ) { object.getVertexPosition(a, _vA$1); object.getVertexPosition(b, _vB$1); object.getVertexPosition(c, _vC$1); const intersection = checkIntersection$1( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint, ); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2(), ); } if (uv1) { _uvA$1.fromBufferAttribute(uv1, a); _uvB$1.fromBufferAttribute(uv1, b); _uvC$1.fromBufferAttribute(uv1, c); intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2(), ); } if (normal) { _normalA.fromBufferAttribute(normal, a); _normalB.fromBufferAttribute(normal, b); _normalC.fromBufferAttribute(normal, c); intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3(), ); if (intersection.normal.dot(ray.direction) > 0) { intersection.normal.multiplyScalar(-1); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0, }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1, ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments, }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0, ); // px buildPlane( "z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1, ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2, ); // py buildPlane( "x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3, ); // ny buildPlane( "x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4, ); // pz buildPlane( "x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5, ); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex, ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments, ); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if ( property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion) ) { if (property.isRenderTargetTexture) { console.warn( "UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().", ); dst[u][p] = null; } else { dst[u][p] = property.clone(); } } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } function cloneUniformsGroups(src) { const dst = []; for (let u = 0; u < src.length; u++) { dst.push(src[u].clone()); } return dst; } function getUnlitUniformColorSpace(renderer) { const currentRenderTarget = renderer.getRenderTarget(); if (currentRenderTarget === null) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } // https://github.com/mrdoob/three.js/issues/27868 if (currentRenderTarget.isXRRenderTarget === true) { return currentRenderTarget.texture.colorSpace; } return ColorManagement.workingColorSpace; } // Legacy const UniformsUtils = {clone: cloneUniforms, merge: mergeUniforms}; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor(parameters) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { clipCullDistance: false, // set to use vertex shader clipping multiDraw: false, // set to use vertex shader multi_draw / enable gl_DrawID }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { color: [1, 1, 1], uv: [0, 0], uv1: [0, 0], }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid, }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex(), }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray(), }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray(), }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray(), }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray(), }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray(), }; } else { data.uniforms[name] = { value: value, }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); this.coordinateSystem = WebGLCoordinateSystem; } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); this.coordinateSystem = source.coordinateSystem; return this; } getWorldDirection(target) { return super.getWorldDirection(target).negate(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } const _v3$1 = /*@__PURE__*/ new Vector3(); const _minTarget = /*@__PURE__*/ new Vector2(); const _maxTarget = /*@__PURE__*/ new Vector2(); class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = (0.5 * this.getFilmHeight()) / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return (0.5 * this.getFilmHeight()) / vExtentSlope; } getEffectiveFOV() { return ( RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom) ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Computes the 2D bounds of the camera"s viewable rectangle at a given distance along the viewing direction. * Sets minTarget and maxTarget to the coordinates of the lower-left and upper-right corners of the view rectangle. */ getViewBounds(distance, minTarget, maxTarget) { _v3$1.set(-1, -1, 0.5).applyMatrix4(this.projectionMatrixInverse); minTarget.set(_v3$1.x, _v3$1.y).multiplyScalar(-distance / _v3$1.z); _v3$1.set(1, 1, 0.5).applyMatrix4(this.projectionMatrixInverse); maxTarget.set(_v3$1.x, _v3$1.y).multiplyScalar(-distance / _v3$1.z); } /** * Computes the width and height of the camera"s viewable rectangle at a given distance along the viewing direction. * Copies the result into the target Vector2, where x is width and y is height. */ getViewSize(distance, target) { this.getViewBounds(distance, _minTarget, _maxTarget); return target.subVectors(_maxTarget, _minTarget); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1, }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = (near * Math.tan(DEG2RAD * 0.5 * this.fov)) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += (view.offsetX * width) / fullWidth; top -= (view.offsetY * height) / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += (near * skew) / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, ); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = -90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; this.renderTarget = renderTarget; this.coordinateSystem = null; this.activeMipmapLevel = 0; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; this.add(cameraNZ); } updateCoordinateSystem() { const coordinateSystem = this.coordinateSystem; const cameras = this.children.concat(); const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = cameras; for (const camera of cameras) this.remove(camera); if (coordinateSystem === WebGLCoordinateSystem) { cameraPX.up.set(0, 1, 0); cameraPX.lookAt(1, 0, 0); cameraNX.up.set(0, 1, 0); cameraNX.lookAt(-1, 0, 0); cameraPY.up.set(0, 0, -1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, 1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, 1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, 1, 0); cameraNZ.lookAt(0, 0, -1); } else if (coordinateSystem === WebGPUCoordinateSystem) { cameraPX.up.set(0, -1, 0); cameraPX.lookAt(-1, 0, 0); cameraNX.up.set(0, -1, 0); cameraNX.lookAt(1, 0, 0); cameraPY.up.set(0, 0, 1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, -1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(0, 0, -1); } else { throw new Error( "THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: " + coordinateSystem, ); } for (const camera of cameras) { this.add(camera); camera.updateMatrixWorld(); } } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const {renderTarget, activeMipmapLevel} = this; if (this.coordinateSystem !== renderer.coordinateSystem) { this.coordinateSystem = renderer.coordinateSystem; this.updateCoordinateSystem(); } const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentActiveCubeFace = renderer.getActiveCubeFace(); const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); const currentXrEnabled = renderer.xr.enabled; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0, activeMipmapLevel); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1, activeMipmapLevel); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2, activeMipmapLevel); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3, activeMipmapLevel); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4, activeMipmapLevel); renderer.render(scene, cameraPZ); // mipmaps are generated during the last call of render() // at this point, all sides of the cube render target are defined renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5, activeMipmapLevel); renderer.render(scene, cameraNZ); renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel, ); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size = 1, options = {}) { super(size, size, options); this.isWebGLCubeRenderTarget = true; const image = {width: size, height: size, depth: 1}; const images = [image, image, image, image, image, image]; this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace, ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: {value: null}, }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } `, }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending, }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1 .subVectors(c, b) .cross(_vector2.subVectors(a, b)) .normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target .copy(point) .addScaledVector(this.normal, -this.distanceToPoint(point)); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(line.start).addScaledVector(direction, t); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return (startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0); } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } const _sphere$5 = /*@__PURE__*/ new Sphere(); const _vector$7 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane(), ) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m, coordinateSystem = WebGLCoordinateSystem) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0] .setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12) .normalize(); planes[1] .setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12) .normalize(); planes[2] .setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13) .normalize(); planes[3] .setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13) .normalize(); planes[4] .setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14) .normalize(); if (coordinateSystem === WebGLCoordinateSystem) { planes[5] .setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14) .normalize(); } else if (coordinateSystem === WebGPUCoordinateSystem) { planes[5].setComponents(me2, me6, me10, me14).normalize(); } else { throw new Error( "THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: " + coordinateSystem, ); } return this; } intersectsObject(object) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _sphere$5.copy(object.boundingSphere).applyMatrix4(object.matrixWorld); } else { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$5.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); } return this.intersectsSphere(_sphere$5); } intersectsSprite(sprite) { _sphere$5.center.set(0, 0, 0); _sphere$5.radius = 0.7071067811865476; _sphere$5.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$5); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; }, }; } function WebGLAttributes(gl) { const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const size = array.byteLength; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { type = gl.HALF_FLOAT; } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array, ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version, size: size, }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute._updateRange; // @deprecated, r159 const updateRanges = attribute.updateRanges; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1 && updateRanges.length === 0) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } if (updateRanges.length !== 0) { for (let i = 0, l = updateRanges.length; i < l; i++) { const range = updateRanges[i]; gl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT, array, range.start, range.count, ); } attribute.clearUpdateRanges(); } // @deprecated, r159 if (updateRange.count !== -1) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count, ); updateRange.count = -1; // reset range } attribute.onUploadCallback(); } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version, }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { if (data.size !== attribute.array.byteLength) { throw new Error( "THREE.WebGLAttributes: The size of the buffer attribute"s array buffer does not match the original size. Resizing buffer attributes is not supported.", ); } updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update, }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments, }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments, ); } } var alphahash_fragment = "#ifdef USE_ALPHAHASH if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; #endif"; var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH const float ALPHA_HASH_SCALE = 0.05; float hash2D( vec2 value ) { return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); } float hash3D( vec3 value ) { return hash2D( vec2( hash2D( value.xy ), value.z ) ); } float getAlphaHashThreshold( vec3 position ) { float maxDeriv = max( length( dFdx( position.xyz ) ), length( dFdy( position.xyz ) ) ); float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); vec2 pixScales = vec2( exp2( floor( log2( pixScale ) ) ), exp2( ceil( log2( pixScale ) ) ) ); vec2 alpha = vec2( hash3D( floor( pixScales.x * position.xyz ) ), hash3D( floor( pixScales.y * position.xyz ) ) ); float lerpFactor = fract( log2( pixScale ) ); float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; float a = min( lerpFactor, 1.0 - lerpFactor ); vec3 cases = vec3( x * x / ( 2.0 * a * ( 1.0 - a ) ), ( x - 0.5 * a ) / ( 1.0 - a ), 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) ); float threshold = ( x < ( 1.0 - a ) ) ? ( ( x < a ) ? cases.x : cases.y ) : cases.z; return clamp( threshold , 1.0e-6, 1.0 ); } #endif"; var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST #ifdef ALPHA_TO_COVERAGE diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a ); if ( diffuseColor.a == 0.0 ) discard; #else if ( diffuseColor.a < alphaTest ) discard; #endif #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_CLEARCOAT ) clearcoatSpecularIndirect *= ambientOcclusion; #endif #if defined( USE_SHEEN ) sheenSpecularIndirect *= ambientOcclusion; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var batching_pars_vertex = "#ifdef USE_BATCHING attribute float batchId; uniform highp sampler2D batchingTexture; mat4 getBatchingMatrix( const in float i ) { int size = textureSize( batchingTexture, 0 ).x; int j = int( i ) * 4; int x = j % size; int y = j / size; vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } #endif #ifdef USE_BATCHING_COLOR uniform sampler2D batchingColorTexture; vec3 getBatchingColor( const in float i ) { int size = textureSize( batchingColorTexture, 0 ).x; int j = int( i ); int x = j % size; int y = j / size; return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb; } #endif"; var batching_vertex = "#ifdef USE_BATCHING mat4 batchingMatrix = getBatchingMatrix( batchId ); #endif"; var begin_vertex = "vec3 transformed = vec3( position ); #ifdef USE_ALPHAHASH vPosition = vec3( position ); #endif"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 rgb = XYZ_TO_REC709 * xyz; return rgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++ m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vBumpMapUv ); vec2 dSTdy = dFdy( vBumpMapUv ); float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) ); vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #ifdef ALPHA_TO_COVERAGE float distanceToPlane, distanceGradient; float clipOpacity = 1.0; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; distanceGradient = fwidth( distanceToPlane ) / 2.0; clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); if ( clipOpacity == 0.0 ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES float unionClipOpacity = 1.0; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; distanceGradient = fwidth( distanceToPlane ) / 2.0; unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); } #pragma unroll_loop_end clipOpacity *= 1.0 - unionClipOpacity; #endif diffuseColor.a *= clipOpacity; if ( diffuseColor.a == 0.0 ) discard; #else #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif #ifdef USE_BATCHING_COLOR vec3 batchingColor = getBatchingColor( batchId ); vColor.xyz *= batchingColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; #ifdef USE_ALPHAHASH varying vec3 vPosition; #endif vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float luminance( const in vec3 rgb ) { const vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 ); return dot( weights, rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); } vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define cubeUV_r0 1.0 #define cubeUV_m0 - 2.0 #define cubeUV_r1 0.8 #define cubeUV_m1 - 1.0 #define cubeUV_r4 0.4 #define cubeUV_m4 2.0 #define cubeUV_r5 0.305 #define cubeUV_m5 3.0 #define cubeUV_r6 0.21 #define cubeUV_m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= cubeUV_r1 ) { mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; } else if ( roughness >= cubeUV_r4 ) { mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; } else if ( roughness >= cubeUV_r5 ) { mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; } else if ( roughness >= cubeUV_r6 ) { mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_TANGENT vec3 transformedTangent = objectTangent; #endif #ifdef USE_BATCHING mat3 bm = mat3( batchingMatrix ); transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); transformedNormal = bm * transformedNormal; #ifdef USE_TANGENT transformedTangent = bm * transformedTangent; #endif #endif #ifdef USE_INSTANCING mat3 im = mat3( instanceMatrix ); transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); transformedNormal = im * transformedNormal; #ifdef USE_TANGENT transformedTangent = im * transformedTangent; #endif #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = " const mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3( vec3( 0.8224621, 0.177538, 0.0 ), vec3( 0.0331941, 0.9668058, 0.0 ), vec3( 0.0170827, 0.0723974, 0.9105199 ) ); const mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3( vec3( 1.2249401, - 0.2249404, 0.0 ), vec3( - 0.0420569, 1.0420571, 0.0 ), vec3( - 0.0196376, - 0.0786361, 1.0982735 ) ); vec4 LinearSRGBToLinearDisplayP3( in vec4 value ) { return vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a ); } vec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) { return vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a ); } vec4 LinearTransferOETF( in vec4 value ) { return value; } vec4 sRGBTransferOETF( in vec4 value ) { return 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 ); } vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return sRGBTransferOETF( value ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; uniform mat3 envMapRotation; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif }"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_fragment = "LambertMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; }; void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert #define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; #if defined( USE_LIGHT_PROBES ) uniform vec3 lightProbe[ 9 ]; #endif vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { vec3 lVector = pointLight.position - geometryPosition; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { vec3 lVector = spotLight.position - geometryPosition; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP vec3 getIBLIrradiance( const in vec3 normal ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #ifdef USE_ANISOTROPY vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 bentNormal = cross( bitangent, viewDir ); bentNormal = normalize( cross( bentNormal, bitangent ) ); bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); return getIBLRadiance( viewDir, bentNormal, roughness ); #else return vec3( 0.0 ); #endif } #endif #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR material.ior = ior; #ifdef USE_SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULAR_COLORMAP specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; #endif #ifdef USE_SPECULAR_INTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_DISPERSION material.dispersion = dispersion; #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEEN_COLORMAP material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEEN_ROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; #endif #endif #ifdef USE_ANISOTROPY #ifdef USE_ANISOTROPYMAP mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; #else vec2 anisotropyV = anisotropyVector; #endif material.anisotropy = length( anisotropyV ); if( material.anisotropy == 0.0 ) { anisotropyV = vec2( 1.0, 0.0 ); } else { anisotropyV /= material.anisotropy; material.anisotropy = saturate( material.anisotropy ); } material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; float dispersion; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif #ifdef IOR float ior; #endif #ifdef USE_TRANSMISSION float transmission; float transmissionAlpha; float thickness; float attenuationDistance; vec3 attenuationColor; #endif #ifdef USE_ANISOTROPY float anisotropy; float alphaT; vec3 anisotropyT; vec3 anisotropyB; #endif }; vec3 clearcoatSpecularDirect = vec3( 0.0 ); vec3 clearcoatSpecularIndirect = vec3( 0.0 ); vec3 sheenSpecularDirect = vec3( 0.0 ); vec3 sheenSpecularIndirect = vec3(0.0 ); vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } #ifdef USE_ANISOTROPY float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); float v = 0.5 / ( gv + gl ); return saturate(v); } float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { float a2 = alphaT * alphaB; highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); highp float v2 = dot( v, v ); float w2 = a2 / v2; return RECIPROCAL_PI * a2 * pow2 ( w2 ); } #endif #ifdef USE_CLEARCOAT vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { vec3 f0 = material.clearcoatF0; float f90 = material.clearcoatF90; float roughness = material.clearcoatRoughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { vec3 f0 = material.specularColor; float f90 = material.specularF90; float roughness = material.roughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); #ifdef USE_IRIDESCENCE F = mix( F, material.iridescenceFresnel, material.iridescence ); #endif #ifdef USE_ANISOTROPY float dotTL = dot( material.anisotropyT, lightDir ); float dotTV = dot( material.anisotropyT, viewDir ); float dotTH = dot( material.anisotropyT, halfDir ); float dotBL = dot( material.anisotropyB, lightDir ); float dotBV = dot( material.anisotropyB, viewDir ); float dotBH = dot( material.anisotropyB, halfDir ); float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); #else float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); #endif return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometryNormal; vec3 viewDir = geometryViewDir; vec3 position = geometryPosition; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); #endif #ifdef USE_SHEEN sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { #ifdef USE_CLEARCOAT clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " vec3 geometryPosition = - vViewPosition; vec3 geometryNormal = normal; vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); vec3 geometryClearcoatNormal = vec3( 0.0 ); #ifdef USE_CLEARCOAT geometryClearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometryViewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometryPosition, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; vec4 spotColor; vec3 spotLightCoord; bool inSpotLightMap; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometryPosition, directLight ); #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS #else #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #endif #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; #endif #undef SPOT_LIGHT_MAP_INDEX #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); #if defined( USE_LIGHT_PROBES ) irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); #endif #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometryNormal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) #ifdef USE_ANISOTROPY radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); #else radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); #endif #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vMapUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphinstance_vertex = "#ifdef USE_INSTANCING_MORPH float morphTargetInfluences[ MORPHTARGETS_COUNT ]; float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; } #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS #ifndef USE_INSTANCING_MORPH uniform float morphTargetBaseInfluence; uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; #endif uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal *= faceDirection; #endif #endif #if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) #ifdef USE_TANGENT mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn = getTangentFrame( - vViewPosition, normal, #if defined( USE_NORMALMAP ) vNormalMapUv #elif defined( USE_CLEARCOAT_NORMALMAP ) vClearcoatNormalMapUv #else vUv #endif ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn[0] *= faceDirection; tbn[1] *= faceDirection; #endif #endif #ifdef USE_CLEARCOAT_NORMALMAP #ifdef USE_TANGENT mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn2[0] *= faceDirection; tbn2[1] *= faceDirection; #endif #endif vec3 nonPerturbedNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( USE_NORMALMAP_TANGENTSPACE ) vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef USE_NORMALMAP_OBJECTSPACE uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { vec3 q0 = dFdx( eye_pos.xyz ); vec3 q1 = dFdy( eye_pos.xyz ); vec2 st0 = dFdx( uv.st ); vec2 st1 = dFdy( uv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = nonPerturbedNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var opaque_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec2 packDepthToRG( in highp float v ) { return packDepthToRGBA( v ).yx; } float unpackRGToDepth( const in highp vec2 v ) { return unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { return depth * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * depth - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING mvPosition = batchingMatrix * mvPosition; #endif #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; bool frustumTest = inFrustum && shadowCoord.z <= 1.0; if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { float shadow = 1.0; vec3 lightToPosition = shadowCoord.xyz; float lightToPositionLength = length( lightToPosition ); if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) { float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; shadow = ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } return shadow; } #endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if defined( USE_SHADOWMAP ) #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif #if NUM_SPOT_LIGHT_COORDS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { shadowWorldPosition = worldPosition; #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; #endif vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; mat4 getBoneMatrix( const in float i ) { int size = textureSize( boneTexture, 0 ).x; int j = int( i ) * 4; int x = j % size; int y = j / size; vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return saturate( toneMappingExposure * color ); } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( vec3( 1.6605, - 0.1246, - 0.0182 ), vec3( - 0.5876, 1.1329, - 0.1006 ), vec3( - 0.0728, - 0.0083, 1.1187 ) ); const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( vec3( 0.6274, 0.0691, 0.0164 ), vec3( 0.3293, 0.9195, 0.0880 ), vec3( 0.0433, 0.0113, 0.8956 ) ); vec3 agxDefaultContrastApprox( vec3 x ) { vec3 x2 = x * x; vec3 x4 = x2 * x2; return + 15.5 * x4 * x2 - 40.14 * x4 * x + 31.96 * x4 - 6.868 * x2 * x + 0.4298 * x2 + 0.1191 * x - 0.00232; } vec3 AgXToneMapping( vec3 color ) { const mat3 AgXInsetMatrix = mat3( vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) ); const mat3 AgXOutsetMatrix = mat3( vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) ); const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; color *= toneMappingExposure; color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; color = AgXInsetMatrix * color; color = max( color, 1e-10 ); color = log2( color ); color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); color = clamp( color, 0.0, 1.0 ); color = agxDefaultContrastApprox( color ); color = AgXOutsetMatrix * color; color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); color = LINEAR_REC2020_TO_LINEAR_SRGB * color; color = clamp( color, 0.0, 1.0 ); return color; } vec3 NeutralToneMapping( vec3 color ) { const float StartCompression = 0.8 - 0.04; const float Desaturation = 0.15; color *= toneMappingExposure; float x = min( color.r, min( color.g, color.b ) ); float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; color -= offset; float peak = max( color.r, max( color.g, color.b ) ); if ( peak < StartCompression ) return color; float d = 1. - StartCompression; float newPeak = 1. - d * d / ( peak + d - StartCompression ); color *= newPeak / peak; float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); return mix( color, vec3( newPeak ), g ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; material.attenuationDistance = attenuationDistance; material.attenuationColor = attenuationColor; #ifdef USE_TRANSMISSIONMAP material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; #endif #ifdef USE_THICKNESSMAP material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmitted = getIBLVolumeRefraction( n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; float w0( float a ) { return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); } float w1( float a ) { return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); } float w2( float a ){ return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); } float w3( float a ) { return ( 1.0 / 6.0 ) * ( a * a * a ); } float g0( float a ) { return w0( a ) + w1( a ); } float g1( float a ) { return w2( a ) + w3( a ); } float h0( float a ) { return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); } float h1( float a ) { return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); } vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { uv = uv * texelSize.zw + 0.5; vec2 iuv = floor( uv ); vec2 fuv = fract( uv ); float g0x = g0( fuv.x ); float g1x = g1( fuv.x ); float h0x = h0( fuv.x ); float h1x = h1( fuv.x ); float h0y = h0( fuv.y ); float h1y = h1( fuv.y ); vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); } vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); vec2 fLodSizeInv = 1.0 / fLodSize; vec2 cLodSizeInv = 1.0 / cLodSize; vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); return mix( fSample, cSample, fract( lod ) ); } vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); } vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( isinf( attenuationDistance ) ) { return vec3( 1.0 ); } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec4 transmittedLight; vec3 transmittance; #ifdef USE_DISPERSION float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); for ( int i = 0; i < 3; i ++ ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); transmittedLight[ i ] = transmissionSample[ i ]; transmittedLight.a += transmissionSample.a; transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; } transmittedLight.a /= 3.0; #else vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); #endif vec3 attenuatedColor = transmittance * transmittedLight.rgb; vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); } #endif"; var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP varying vec2 vNormalMapUv; #endif #ifdef USE_EMISSIVEMAP varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SHEEN_COLORMAP varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_SPECULARMAP varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP uniform mat3 mapTransform; varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP uniform mat3 alphaMapTransform; varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP uniform mat3 lightMapTransform; varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP uniform mat3 aoMapTransform; varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP uniform mat3 bumpMapTransform; varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP uniform mat3 normalMapTransform; varying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP uniform mat3 displacementMapTransform; varying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP uniform mat3 emissiveMapTransform; varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP uniform mat3 metalnessMapTransform; varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP uniform mat3 roughnessMapTransform; varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP uniform mat3 anisotropyMapTransform; varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP uniform mat3 clearcoatMapTransform; varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform mat3 clearcoatNormalMapTransform; varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform mat3 clearcoatRoughnessMapTransform; varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP uniform mat3 sheenColorMapTransform; varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform mat3 sheenRoughnessMapTransform; varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP uniform mat3 iridescenceMapTransform; varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform mat3 iridescenceThicknessMapTransform; varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP uniform mat3 specularMapTransform; varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP uniform mat3 specularColorMapTransform; varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform mat3 specularIntensityMapTransform; varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ANISOTROPYMAP vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING worldPosition = batchingMatrix * worldPosition; #endif #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$h = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$h = "uniform sampler2D t2D; uniform float backgroundIntensity; varying vec2 vUv; void main() { vec4 texColor = texture2D( t2D, vUv ); #ifdef DECODE_VIDEO_TEXTURE texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$g = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #elif defined( ENVMAP_TYPE_CUBE_UV ) uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform float backgroundBlurriness; uniform float backgroundIntensity; uniform mat3 backgroundRotation; varying vec3 vWorldDirection; #include void main() { #ifdef ENVMAP_TYPE_CUBE vec4 texColor = textureCube( envMap, backgroundRotation * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); #else vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldDirection; void main() { vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); gl_FragColor = texColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { vec4 diffuseColor = vec4( 1.0 ); #include #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include #include void main () { vec4 diffuseColor = vec4( 1.0 ); #include #include #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$9 = "#define LAMBERT uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_DISPERSION uniform float dispersion; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif #ifdef USE_ANISOTROPY uniform vec2 anisotropyVector; #ifdef USE_ANISOTROPYMAP uniform sampler2D anisotropyMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include #ifdef USE_POINTS_UV varying vec2 vUv; uniform mat3 uvTransform; #endif void main() { #ifdef USE_POINTS_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif #include #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include vec3 outgoingLight = vec3( 0.0 ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include vec3 outgoingLight = vec3( 0.0 ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphahash_fragment: alphahash_fragment, alphahash_pars_fragment: alphahash_pars_fragment, alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, batching_pars_vertex: batching_pars_vertex, batching_vertex: batching_vertex, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, colorspace_fragment: colorspace_fragment, colorspace_pars_fragment: colorspace_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphinstance_vertex: morphinstance_vertex, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, opaque_fragment: opaque_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1, }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, map: {value: null}, mapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, }, specularmap: { specularMap: {value: null}, specularMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, envmap: { envMap: {value: null}, envMapRotation: {value: /*@__PURE__*/ new Matrix3()}, flipEnvMap: {value: -1}, reflectivity: {value: 1.0}, // basic, lambert, phong ior: {value: 1.5}, // physical refractionRatio: {value: 0.98}, // basic, lambert, phong }, aomap: { aoMap: {value: null}, aoMapIntensity: {value: 1}, aoMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, lightmap: { lightMap: {value: null}, lightMapIntensity: {value: 1}, lightMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, bumpmap: { bumpMap: {value: null}, bumpMapTransform: {value: /*@__PURE__*/ new Matrix3()}, bumpScale: {value: 1}, }, normalmap: { normalMap: {value: null}, normalMapTransform: {value: /*@__PURE__*/ new Matrix3()}, normalScale: {value: /*@__PURE__*/ new Vector2(1, 1)}, }, displacementmap: { displacementMap: {value: null}, displacementMapTransform: {value: /*@__PURE__*/ new Matrix3()}, displacementScale: {value: 1}, displacementBias: {value: 0}, }, emissivemap: { emissiveMap: {value: null}, emissiveMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, metalnessmap: { metalnessMap: {value: null}, metalnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, roughnessmap: { roughnessMap: {value: null}, roughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, gradientmap: { gradientMap: {value: null}, }, fog: { fogDensity: {value: 0.00025}, fogNear: {value: 1}, fogFar: {value: 2000}, fogColor: {value: /*@__PURE__*/ new Color(0xffffff)}, }, 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: {}, }, }, spotLightMap: {value: []}, spotShadowMap: {value: []}, spotLightMatrix: {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: {}, }, }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {}, }, }, ltc_1: {value: null}, ltc_2: {value: null}, }, points: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, size: {value: 1.0}, scale: {value: 1.0}, map: {value: null}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, uvTransform: {value: /*@__PURE__*/ new Matrix3()}, }, sprite: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, center: {value: /*@__PURE__*/ new Vector2(0.5, 0.5)}, rotation: {value: 0.0}, map: {value: null}, mapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, }, }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog, ]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag, }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, }, ]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag, }, phong: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, specular: {value: /*@__PURE__*/ new Color(0x111111)}, shininess: {value: 30}, }, ]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag, }, standard: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, roughness: {value: 1.0}, metalness: {value: 0.0}, envMapIntensity: {value: 1}, }, ]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag, }, toon: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, }, ]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag, }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: {value: null}, }, ]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag, }, points: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.points, UniformsLib.fog, ]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag, }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.fog, { scale: {value: 1}, dashSize: {value: 1}, totalSize: {value: 2}, }, ]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag, }, depth: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.displacementmap, ]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag, }, normal: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: {value: 1.0}, }, ]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag, }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.sprite, UniformsLib.fog, ]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag, }, background: { uniforms: { uvTransform: {value: /*@__PURE__*/ new Matrix3()}, t2D: {value: null}, backgroundIntensity: {value: 1}, }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag, }, backgroundCube: { uniforms: { envMap: {value: null}, flipEnvMap: {value: -1}, backgroundBlurriness: {value: 0}, backgroundIntensity: {value: 1}, backgroundRotation: {value: /*@__PURE__*/ new Matrix3()}, }, vertexShader: ShaderChunk.backgroundCube_vert, fragmentShader: ShaderChunk.backgroundCube_frag, }, cube: { uniforms: { tCube: {value: null}, tFlip: {value: -1}, opacity: {value: 1.0}, }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag, }, equirect: { uniforms: { tEquirect: {value: null}, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag, }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: {value: /*@__PURE__*/ new Vector3()}, nearDistance: {value: 1}, farDistance: {value: 1000}, }, ]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag, }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.lights, UniformsLib.fog, { color: {value: /*@__PURE__*/ new Color(0x00000)}, opacity: {value: 1.0}, }, ]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag, }, }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms([ ShaderLib.standard.uniforms, { clearcoat: {value: 0}, clearcoatMap: {value: null}, clearcoatMapTransform: {value: /*@__PURE__*/ new Matrix3()}, clearcoatNormalMap: {value: null}, clearcoatNormalMapTransform: {value: /*@__PURE__*/ new Matrix3()}, clearcoatNormalScale: {value: /*@__PURE__*/ new Vector2(1, 1)}, clearcoatRoughness: {value: 0}, clearcoatRoughnessMap: {value: null}, clearcoatRoughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, dispersion: {value: 0}, iridescence: {value: 0}, iridescenceMap: {value: null}, iridescenceMapTransform: {value: /*@__PURE__*/ new Matrix3()}, iridescenceIOR: {value: 1.3}, iridescenceThicknessMinimum: {value: 100}, iridescenceThicknessMaximum: {value: 400}, iridescenceThicknessMap: {value: null}, iridescenceThicknessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, sheen: {value: 0}, sheenColor: {value: /*@__PURE__*/ new Color(0x000000)}, sheenColorMap: {value: null}, sheenColorMapTransform: {value: /*@__PURE__*/ new Matrix3()}, sheenRoughness: {value: 1}, sheenRoughnessMap: {value: null}, sheenRoughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, transmission: {value: 0}, transmissionMap: {value: null}, transmissionMapTransform: {value: /*@__PURE__*/ new Matrix3()}, transmissionSamplerSize: {value: /*@__PURE__*/ new Vector2()}, transmissionSamplerMap: {value: null}, thickness: {value: 0}, thicknessMap: {value: null}, thicknessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, attenuationDistance: {value: 0}, attenuationColor: {value: /*@__PURE__*/ new Color(0x000000)}, specularColor: {value: /*@__PURE__*/ new Color(1, 1, 1)}, specularColorMap: {value: null}, specularColorMapTransform: {value: /*@__PURE__*/ new Matrix3()}, specularIntensity: {value: 1}, specularIntensityMap: {value: null}, specularIntensityMapTransform: {value: /*@__PURE__*/ new Matrix3()}, anisotropyVector: {value: /*@__PURE__*/ new Vector2()}, anisotropyMap: {value: null}, anisotropyMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, ]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag, }; const _rgb = {r: 0, b: 0, g: 0}; const _e1$1 = /*@__PURE__*/ new Euler(); const _m1$1 = /*@__PURE__*/ new Matrix4(); function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha, ) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function getBackground(scene) { let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = (usePMREM ? cubeuvmaps : cubemaps).get(background); } return background; } function render(scene) { let forceClear = false; const background = getBackground(scene); if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } const environmentBlendMode = renderer.xr.getEnvironmentBlendMode(); if (environmentBlendMode === "additive") { state.buffers.color.setClear(0, 0, 0, 1, premultipliedAlpha); } else if (environmentBlendMode === "alpha-blend") { state.buffers.color.setClear(0, 0, 0, 0, premultipliedAlpha); } if (renderer.autoClear || forceClear) { // buffers might not be writable which is required to ensure a correct clear state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil, ); } } function addToRenderList(renderList, scene) { const background = getBackground(scene); if ( background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping) ) { if (boxMesh === undefined) { boxMesh = new Mesh( new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.backgroundCube.uniforms), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false, }), ); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; }, }); objects.update(boxMesh); } _e1$1.copy(scene.backgroundRotation); // accommodate left-handed frame _e1$1.x *= -1; _e1$1.y *= -1; _e1$1.z *= -1; if ( background.isCubeTexture && background.isRenderTargetTexture === false ) { // environment maps which are not cube render targets or PMREMs follow a different convention _e1$1.y *= -1; _e1$1.z *= -1; } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.uniforms.backgroundRotation.value.setFromMatrix4( _m1$1.makeRotationFromEuler(_e1$1), ); boxMesh.material.toneMapped = ColorManagement.getTransfer(background.colorSpace) !== SRGBTransfer; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null, ); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh( new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false, }), ); planeMesh.geometry.deleteAttribute("normal"); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; }, }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = ColorManagement.getTransfer(background.colorSpace) !== SRGBTransfer; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null, ); } } function setClear(color, alpha) { color.getRGB(_rgb, getUnlitUniformColorSpace(renderer)); state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha, ); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render, addToRenderList: addToRenderList, }; } function WebGLBindingStates(gl, attributes) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; let forceUpdate = false; function setup(object, material, program, geometry, index) { let updateBuffers = false; const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(object, geometry, program, index); if (updateBuffers) saveCache(object, geometry, program, index); if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers || forceUpdate) { forceUpdate = false; setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { return gl.createVertexArray(); } function bindVertexArrayObject(vao) { return gl.bindVertexArray(vao); } function deleteVertexArrayObject(vao) { return gl.deleteVertexArray(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null, }; } function needsUpdate(object, geometry, program, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { const cachedAttribute = cachedAttributes[name]; let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum++; } } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(object, geometry, program, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let attribute = attributes[name]; if (attribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) attribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if (attribute && attribute.data) { data.data = attribute.data; } cache[name] = data; attributesNum++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { gl.vertexAttribDivisor(attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset, integer, ) { if (integer === true) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; // check for integer attributes const integer = type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute, ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + (size / programAttribute.locationSize) * i) * bytesPerElement, integer, ); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute, ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, (size / programAttribute.locationSize) * i * bytesPerElement, integer, ); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); forceUpdate = true; if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes, }; } function WebGLBufferRenderer(gl, extensions, info) { let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; gl.drawArraysInstanced(mode, start, count, primcount); info.update(count, mode, primcount); } function renderMultiDraw(starts, counts, drawCount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < drawCount; i++) { this.render(starts[i], counts[i]); } } else { extension.multiDrawArraysWEBGL(mode, starts, 0, counts, 0, drawCount); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } info.update(elementCount, mode, 1); } } function renderMultiDrawInstances(starts, counts, drawCount, primcount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < starts.length; i++) { renderInstances(starts[i], counts[i], primcount[i]); } } else { extension.multiDrawArraysInstancedWEBGL( mode, starts, 0, counts, 0, primcount, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } for (let i = 0; i < primcount.length; i++) { info.update(elementCount, mode, primcount[i]); } } } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; this.renderMultiDraw = renderMultiDraw; this.renderMultiDrawInstances = renderMultiDrawInstances; } function WebGLCapabilities(gl, extensions, parameters, utils) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function textureFormatReadable(textureFormat) { if ( textureFormat !== RGBAFormat && utils.convert(textureFormat) !== gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT) ) { return false; } return true; } function textureTypeReadable(textureType) { const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || extensions.has("EXT_color_buffer_float")); if ( textureType !== UnsignedByteType && utils.convert(textureType) !== gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) textureType !== FloatType && !halfFloatSupportedByExt ) { return false; } return true; } function getMaxPrecision(precision) { if (precision === "highp") { if ( gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT) .precision > 0 ) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if ( gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT) .precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT) .precision > 0 ) { return "mediump"; } } return "lowp"; } let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead.", ); precision = maxPrecision; } const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const maxSamples = gl.getParameter(gl.MAX_SAMPLES); return { isWebGL2: true, // keeping this for backwards compatibility getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, textureFormatReadable: textureFormatReadable, textureTypeReadable: textureTypeReadable, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, maxSamples: maxSamples, }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = {value: null, needsUpdate: false}; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function (planes, camera) { globalState = projectPlanes(planes, camera, 0); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if ( !localClippingEnabled || planes === null || planes.length === 0 || (renderingShadows && !clipShadows) ) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose, }; } class OrthographicCamera extends Camera { constructor( left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000, ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1, }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem, ); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; let _oldActiveCubeFace = 0; let _oldActiveMipmapLevel = 0; let _oldXrEnabled = false; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3(-PHI, INV_PHI, 0), /*@__PURE__*/ new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/ new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/ new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/ new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/ new Vector3(0, PHI, INV_PHI), /*@__PURE__*/ new Vector3(-1, 1, -1), /*@__PURE__*/ new Vector3(1, 1, -1), /*@__PURE__*/ new Vector3(-1, 1, 1), /*@__PURE__*/ new Vector3(1, 1, 1), ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); _oldActiveCubeFace = this._renderer.getActiveCubeFace(); _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); _oldXrEnabled = this._renderer.xr.enabled; this._renderer.xr.enabled = false; this._setSize(256); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. * The smallest supported equirectangular image size is 64 x 32. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. * The smallest supported cube size is 16 x 16. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial(this._cubemapMaterial); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial(this._equirectMaterial); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if (this._cubemapMaterial !== null) this._cubemapMaterial.dispose(); if (this._equirectMaterial !== null) this._equirectMaterial.dispose(); } // private interface _setSize(cubeSize) { this._lodMax = Math.floor(Math.log2(cubeSize)); this._cubeSize = Math.pow(2, this._lodMax); } _dispose() { if (this._blurMaterial !== null) this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); for (let i = 0; i < this._lodPlanes.length; i++) { this._lodPlanes[i].dispose(); } } _cleanup(outputTarget) { this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel, ); this._renderer.xr.enabled = _oldXrEnabled; outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : texture.image[0].width || texture.image[0].image.width, ); } else { // Equirectangular this._setSize(texture.image.width / 4); } _oldTarget = this._renderer.getRenderTarget(); _oldActiveCubeFace = this._renderer.getActiveCubeFace(); _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); _oldXrEnabled = this._renderer.xr.enabled; this._renderer.xr.enabled = false; const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max(this._cubeSize, 16 * 7); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false, }; const cubeUVRenderTarget = _createRenderTarget(width, height, params); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { if (this._pingPongRenderTarget !== null) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget(width, height, params); const {_lodMax} = this; ({ sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas, } = _createPlanes(_lodMax)); this._blurMaterial = _getBlurShader(_lodMax, width, height); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(this._lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size, ); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh(this._lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; const size = this._cubeSize; _setViewport(cubeUVRenderTarget, 0, 0, 3 * size, 2 * size); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; const n = this._lodPlanes.length; for (let i = 1; i < n; i++) { const sigma = Math.sqrt( this._sigmas[i] * this._sigmas[i] - this._sigmas[i - 1] * this._sigmas[i - 1], ); const poleAxis = _axisDirections[(n - i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis, ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis, ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis, ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error( "blur direction must be either latitudinal or longitudinal!", ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(this._lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : (2 * Math.PI) / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn( `sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`, ); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp((-x * x) / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } const {_lodMax} = this; blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = _lodMax - lodIn; const outputSize = this._sizeLods[lodOut]; const x = 3 * outputSize * (lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0); const y = 4 * (this._cubeSize - outputSize); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes(lodMax) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for (let i = 0; i < totalLods; i++) { const sizeLod = Math.pow(2, lod); sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > lodMax - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - lodMax + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 2); const min = -texelSize; const max = 1 + texelSize; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = ((face % 3) * 2) / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0, ]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute(position, positionSize), ); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute( "faceIndex", new BufferAttribute(faceIndex, faceIndexSize), ); lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return {lodPlanes, sizeLods, sigmas}; } function _createRenderTarget(width, height, params) { const cubeUVRenderTarget = new WebGLRenderTarget(width, height, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(lodMax, width, height) { const weights = new Float32Array(MAX_SAMPLES); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new ShaderMaterial({ name: "SphericalGaussianBlur", defines: { n: MAX_SAMPLES, CUBEUV_TEXEL_WIDTH: 1.0 / width, CUBEUV_TEXEL_HEIGHT: 1.0 / height, CUBEUV_MAX_MIP: `${lodMax}.0`, }, uniforms: { envMap: {value: null}, samples: {value: 1}, weights: {value: weights}, latitudinal: {value: false}, dTheta: {value: 0}, mipInt: {value: 0}, poleAxis: {value: poleAxis}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false, }); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { envMap: {value: null}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false, }); } function _getCubemapMaterial() { return new ShaderMaterial({ name: "CubemapToCubeUV", uniforms: { envMap: {value: null}, flipEnvMap: {value: -1}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false, }); } function _getCommonVertexShader() { return /* glsl */ ` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { let renderTarget = cubeUVmaps.get(texture); const currentPMREMVersion = renderTarget !== undefined ? renderTarget.texture.pmremVersion : 0; if ( texture.isRenderTargetTexture && texture.pmremVersion !== currentPMREMVersion ) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); renderTarget.texture.pmremVersion = texture.pmremVersion; cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (renderTarget !== undefined) { return renderTarget.texture; } else { const image = texture.image; if ( (isEquirectMap && image && image.height > 0) || (isCubeMap && image && isCubeTextureComplete(image)) ) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); renderTarget.texture.pmremVersion = texture.pmremVersion; cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose, }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function () { getExtension("EXT_color_buffer_float"); getExtension("WEBGL_clip_cull_distance"); getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); getExtension("WEBGL_render_shared_exponent"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { warnOnce("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; }, }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } for (const name in geometry.morphAttributes) { const array = geometry.morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.remove(array[i]); } } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else if (geometryPosition !== undefined) { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } else { return; } const attribute = new ( arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute )(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute, }; } function WebGLIndexedBufferRenderer(gl, extensions, info) { let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; gl.drawElementsInstanced( mode, count, type, start * bytesPerElement, primcount, ); info.update(count, mode, primcount); } function renderMultiDraw(starts, counts, drawCount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < drawCount; i++) { this.render(starts[i] / bytesPerElement, counts[i]); } } else { extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } info.update(elementCount, mode, 1); } } function renderMultiDrawInstances(starts, counts, drawCount, primcount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < starts.length; i++) { renderInstances(starts[i] / bytesPerElement, counts[i], primcount[i]); } } else { extension.multiDrawElementsInstancedWEBGL( mode, counts, 0, type, starts, 0, primcount, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } for (let i = 0; i < primcount.length; i++) { info.update(elementCount, mode, primcount[i]); } } } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; this.renderMultiDraw = renderMultiDraw; this.renderMultiDrawInstances = renderMultiDrawInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0, }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0, }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update, }; } function WebGLMorphtargets(gl, capabilities, textures) { const morphTextures = new WeakMap(); const morph = new Vector4(); function update(object, geometry, program) { const objectInfluences = object.morphTargetInfluences; // the following encodes morph targets into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== morphTargetsCount) { if (entry !== undefined) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if (hasMorphPosition === true) vertexDataCount = 1; if (hasMorphNormals === true) vertexDataCount = 2; if (hasMorphColors === true) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * morphTargetsCount); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount, ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for (let i = 0; i < morphTargetsCount; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const morphColor = morphColors[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { const stride = j * vertexDataStride; if (hasMorphPosition === true) { morph.fromBufferAttribute(morphTarget, j); buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; } if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } if (hasMorphColors === true) { morph.fromBufferAttribute(morphColor, j); buffer[offset + stride + 8] = morph.x; buffer[offset + stride + 9] = morph.y; buffer[offset + stride + 10] = morph.z; buffer[offset + stride + 11] = morphColor.itemSize === 4 ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2(width, height), }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // if (object.isInstancedMesh === true && object.morphTexture !== null) { program .getUniforms() .setValue(gl, "morphTexture", object.morphTexture, textures); } else { let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program .getUniforms() .setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program .getUniforms() .setValue(gl, "morphTargetInfluences", objectInfluences); } program .getUniforms() .setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } return { update: update, }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if ( object.hasEventListener("dispose", onInstancedMeshDispose) === false ) { object.addEventListener("dispose", onInstancedMeshDispose); } if (updateMap.get(object) !== frame) { attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } updateMap.set(object, frame); } } if (object.isSkinnedMesh) { const skeleton = object.skeleton; if (updateMap.get(skeleton) !== frame) { skeleton.update(); updateMap.set(skeleton, frame); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose, }; } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format = DepthFormat, ) { if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat", ); } if (type === undefined && format === DepthFormat) type = UnsignedIntType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isDepthTexture = true; this.image = {width: width, height: height}; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; this.compareFunction = null; } copy(source) { super.copy(source); this.compareFunction = source.compareFunction; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.compareFunction !== null) data.compareFunction = this.compareFunction; return data; } } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyShadowTexture = /*@__PURE__*/ new DepthTexture(1, 1); emptyShadowTexture.compareFunction = LessEqualCompare; const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2i(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } } function setValueV3i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3i(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } } function setValueV4i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4i(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2ui(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } } function setValueV3ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3ui(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } } function setValueV4ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4ui(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } const emptyTexture2D = this.type === gl.SAMPLER_2D_SHADOW ? emptyShadowTexture : emptyTexture; textures.setTexture2D(v || emptyTexture2D, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || empty3dTexture, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyArrayTexture, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || empty3dTexture, units[i]); } } function setValueT6Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.type = activeInfo.type; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.type = activeInfo.type; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor(id) { this.id = id; this.seq = []; this.map = {}; } setValue(gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if ( subscript === undefined || (subscript === "[" && matchEnd + 2 === pathLength) ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr), ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container class WebGLUniforms { constructor(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } setValue(gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); } setOptional(gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); } static upload(gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } } static seqWithValue(seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; } } function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } // From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/ const COMPLETION_STATUS_KHR = 0x91b1; let programIdCount = 0; function handleSource(string, errorLine) { const lines = string.split(" "); const lines2 = []; const from = Math.max(errorLine - 6, 0); const to = Math.min(errorLine + 6, lines.length); for (let i = from; i < to; i++) { const line = i + 1; lines2.push(`${line === errorLine ? ">" : " "} ${line}: ${lines[i]}`); } return lines2.join(" "); } function getEncodingComponents(colorSpace) { const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const encodingPrimaries = ColorManagement.getPrimaries(colorSpace); let gamutMapping; if (workingPrimaries === encodingPrimaries) { gamutMapping = ""; } else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) { gamutMapping = "LinearDisplayP3ToLinearSRGB"; } else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) { gamutMapping = "LinearSRGBToLinearDisplayP3"; } switch (colorSpace) { case LinearSRGBColorSpace: case LinearDisplayP3ColorSpace: return [gamutMapping, "LinearTransferOETF"]; case SRGBColorSpace: case DisplayP3ColorSpace: return [gamutMapping, "sRGBTransferOETF"]; default: console.warn("THREE.WebGLProgram: Unsupported color space:", colorSpace); return [gamutMapping, "LinearTransferOETF"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; const errorMatches = /ERROR: 0:(d+)/.exec(errors); if (errorMatches) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt(errorMatches[1]); return ( type.toUpperCase() + " " + errors + " " + handleSource(gl.getShaderSource(shader), errorLine) ); } else { return errors; } } function getTexelEncodingFunction(functionName, colorSpace) { const components = getEncodingComponents(colorSpace); return `vec4 ${functionName}( vec4 value ) { return ${components[0]}( ${components[1]}( value ) ); }`; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case AgXToneMapping: toneMappingName = "AgX"; break; case NeutralToneMapping: toneMappingName = "Neutral"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return ( "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }" ); } function generateVertexExtensions(parameters) { const chunks = [ parameters.extensionClipCullDistance ? "#extension GL_ANGLE_clip_cull_distance : require" : "", parameters.extensionMultiDraw ? "#extension GL_ANGLE_multi_draw : require" : "", ]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize, }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string .replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights) .replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights) .replace(/NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps) .replace(/NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords) .replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights) .replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights) .replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights) .replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows) .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps, ) .replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows) .replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string .replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes) .replace( /UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection, ); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } const shaderChunkMap = new Map(); function includeReplacer(match, include) { let string = ShaderChunk[include]; if (string === undefined) { const newInclude = shaderChunkMap.get(include); if (newInclude !== undefined) { string = ShaderChunk[newInclude]; console.warn( "THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.", include, newInclude, ); } else { throw new Error("Can not resolve #include <" + include + ">"); } } return resolveIncludes(string); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [ "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, ] .filter(filterEmptyLine) .join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision(parameters), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.extensionClipCullDistance ? "#define USE_CLIP_DISTANCE" : "", parameters.batching ? "#define USE_BATCHING" : "", parameters.batchingColor ? "#define USE_BATCHING_COLOR" : "", parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.instancingMorph ? "#define USE_INSTANCING_MORPH" : "", parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.displacementMap ? "#define USE_DISPLACEMENTMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", // parameters.mapUv ? "#define MAP_UV " + parameters.mapUv : "", parameters.alphaMapUv ? "#define ALPHAMAP_UV " + parameters.alphaMapUv : "", parameters.lightMapUv ? "#define LIGHTMAP_UV " + parameters.lightMapUv : "", parameters.aoMapUv ? "#define AOMAP_UV " + parameters.aoMapUv : "", parameters.emissiveMapUv ? "#define EMISSIVEMAP_UV " + parameters.emissiveMapUv : "", parameters.bumpMapUv ? "#define BUMPMAP_UV " + parameters.bumpMapUv : "", parameters.normalMapUv ? "#define NORMALMAP_UV " + parameters.normalMapUv : "", parameters.displacementMapUv ? "#define DISPLACEMENTMAP_UV " + parameters.displacementMapUv : "", parameters.metalnessMapUv ? "#define METALNESSMAP_UV " + parameters.metalnessMapUv : "", parameters.roughnessMapUv ? "#define ROUGHNESSMAP_UV " + parameters.roughnessMapUv : "", parameters.anisotropyMapUv ? "#define ANISOTROPYMAP_UV " + parameters.anisotropyMapUv : "", parameters.clearcoatMapUv ? "#define CLEARCOATMAP_UV " + parameters.clearcoatMapUv : "", parameters.clearcoatNormalMapUv ? "#define CLEARCOAT_NORMALMAP_UV " + parameters.clearcoatNormalMapUv : "", parameters.clearcoatRoughnessMapUv ? "#define CLEARCOAT_ROUGHNESSMAP_UV " + parameters.clearcoatRoughnessMapUv : "", parameters.iridescenceMapUv ? "#define IRIDESCENCEMAP_UV " + parameters.iridescenceMapUv : "", parameters.iridescenceThicknessMapUv ? "#define IRIDESCENCE_THICKNESSMAP_UV " + parameters.iridescenceThicknessMapUv : "", parameters.sheenColorMapUv ? "#define SHEEN_COLORMAP_UV " + parameters.sheenColorMapUv : "", parameters.sheenRoughnessMapUv ? "#define SHEEN_ROUGHNESSMAP_UV " + parameters.sheenRoughnessMapUv : "", parameters.specularMapUv ? "#define SPECULARMAP_UV " + parameters.specularMapUv : "", parameters.specularColorMapUv ? "#define SPECULAR_COLORMAP_UV " + parameters.specularColorMapUv : "", parameters.specularIntensityMapUv ? "#define SPECULAR_INTENSITYMAP_UV " + parameters.specularIntensityMapUv : "", parameters.transmissionMapUv ? "#define TRANSMISSIONMAP_UV " + parameters.transmissionMapUv : "", parameters.thicknessMapUv ? "#define THICKNESSMAP_UV " + parameters.thicknessMapUv : "", // parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphColors ? "#define USE_MORPHCOLORS" : "", parameters.morphTargetsCount > 0 ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", parameters.morphTargetsCount > 0 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.numLightProbes > 0 ? "#define USE_LIGHT_PROBES" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", "uniform mat4 modelMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", "#ifdef USE_INSTANCING", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "#ifdef USE_INSTANCING_MORPH", " uniform sampler2D morphTexture;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_UV1", " attribute vec2 uv1;", "#endif", "#ifdef USE_UV2", " attribute vec2 uv2;", "#endif", "#ifdef USE_UV3", " attribute vec2 uv3;", "#endif", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " ", ] .filter(filterEmptyLine) .join(" "); prefixFragment = [ generatePrecision(parameters), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.alphaToCoverage ? "#define ALPHA_TO_COVERAGE" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.dispersion ? "#define USE_DISPERSION" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor || parameters.batchingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.numLightProbes > 0 ? "#define USE_LIGHT_PROBES" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk["colorspace_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputColorSpace, ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " ", ] .filter(filterEmptyLine) .join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ customVertexExtensions, "#define attribute in", "#define varying out", "#define texture2D texture", ].join(" ") + " " + prefixVertex; prefixFragment = [ "#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); function onFirstUse(self) { // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; if (typeof renderer.debug.onShaderError === "function") { renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader, ); } else { // default error reporting const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment", ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Material Name: " + self.name + " " + "Material Type: " + self.type + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors, ); } } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { self.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex, }, fragmentShader: { log: fragmentLog, prefix: prefixFragment, }, }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); cachedUniforms = new WebGLUniforms(gl, program); cachedAttributes = fetchAttributeLocations(gl, program); } // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { // Populates cachedUniforms and cachedAttributes onFirstUse(this); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { // Populates cachedAttributes and cachedUniforms onFirstUse(this); } return cachedAttributes; }; // indicate when the program is ready to be used. if the KHR_parallel_shader_compile extension isn"t supported, // flag the program as ready immediately. It may cause a stall when it"s first used. let programReady = parameters.rendererExtensionParallelShaderCompile === false; this.isReady = function () { if (programReady === false) { programReady = gl.getProgramParameter(program, COMPLETION_STATUS_KHR); } return programReady; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.type = parameters.shaderType; this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id$1 = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage.code); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; let set = cache.get(material); if (set === undefined) { set = new Set(); cache.set(material, set); } return set; } _getShaderStage(code) { const cache = this.shaderCache; let stage = cache.get(code); if (stage === undefined) { stage = new WebGLShaderStage(code); cache.set(code, stage); } return stage; } } class WebGLShaderStage { constructor(code) { this.id = _id$1++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping, ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const _activeChannels = new Set(); const programs = []; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite", }; function getChannel(value) { _activeChannels.add(value); if (value === 0) return "uv"; return `uv${value}`; } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || environment); const envMapCubeUVHeight = !!envMap && envMap.mapping === CubeUVReflectionMapping ? envMap.image.height : null; const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead.", ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let morphTextureStride = 0; if (geometry.morphAttributes.position !== undefined) morphTextureStride = 1; if (geometry.morphAttributes.normal !== undefined) morphTextureStride = 2; if (geometry.morphAttributes.color !== undefined) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const IS_BATCHEDMESH = object.isBatchedMesh === true; const HAS_MAP = !!material.map; const HAS_MATCAP = !!material.matcap; const HAS_ENVMAP = !!envMap; const HAS_AOMAP = !!material.aoMap; const HAS_LIGHTMAP = !!material.lightMap; const HAS_BUMPMAP = !!material.bumpMap; const HAS_NORMALMAP = !!material.normalMap; const HAS_DISPLACEMENTMAP = !!material.displacementMap; const HAS_EMISSIVEMAP = !!material.emissiveMap; const HAS_METALNESSMAP = !!material.metalnessMap; const HAS_ROUGHNESSMAP = !!material.roughnessMap; const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_DISPERSION = material.dispersion > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !!material.anisotropyMap; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !!material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !!material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !!material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !!material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !!material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !!material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !!material.sheenRoughnessMap; const HAS_SPECULARMAP = !!material.specularMap; const HAS_SPECULAR_COLORMAP = !!material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !!material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !!material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !!material.thicknessMap; const HAS_GRADIENTMAP = !!material.gradientMap; const HAS_ALPHAMAP = !!material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_ALPHAHASH = !!material.alphaHash; const HAS_EXTENSIONS = !!material.extensions; let toneMapping = NoToneMapping; if (material.toneMapped) { if ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) { toneMapping = renderer.toneMapping; } } const parameters = { shaderID: shaderID, shaderType: material.type, shaderName: material.name, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, batching: IS_BATCHEDMESH, batchingColor: IS_BATCHEDMESH && object._colorsTexture !== null, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, instancingMorph: IS_INSTANCEDMESH && object.morphTexture !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: currentRenderTarget === null ? renderer.outputColorSpace : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace, alphaToCoverage: !!material.alphaToCoverage, map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, anisotropy: HAS_ANISOTROPY, anisotropyMap: HAS_ANISOTROPYMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, dispersion: HAS_DISPERSION, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending && material.alphaToCoverage === false, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, alphaHash: HAS_ALPHAHASH, combine: material.combine, // mapUv: HAS_MAP && getChannel(material.map.channel), aoMapUv: HAS_AOMAP && getChannel(material.aoMap.channel), lightMapUv: HAS_LIGHTMAP && getChannel(material.lightMap.channel), bumpMapUv: HAS_BUMPMAP && getChannel(material.bumpMap.channel), normalMapUv: HAS_NORMALMAP && getChannel(material.normalMap.channel), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel(material.displacementMap.channel), emissiveMapUv: HAS_EMISSIVEMAP && getChannel(material.emissiveMap.channel), metalnessMapUv: HAS_METALNESSMAP && getChannel(material.metalnessMap.channel), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel(material.roughnessMap.channel), anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel(material.anisotropyMap.channel), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel(material.clearcoatMap.channel), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel(material.clearcoatNormalMap.channel), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel(material.clearcoatRoughnessMap.channel), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel(material.iridescenceMap.channel), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel(material.iridescenceThicknessMap.channel), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel(material.sheenColorMap.channel), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel(material.sheenRoughnessMap.channel), specularMapUv: HAS_SPECULARMAP && getChannel(material.specularMap.channel), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel(material.specularColorMap.channel), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel(material.specularIntensityMap.channel), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel(material.transmissionMap.channel), thicknessMapUv: HAS_THICKNESSMAP && getChannel(material.thicknessMap.channel), alphaMapUv: HAS_ALPHAMAP && getChannel(material.alphaMap.channel), // vertexTangents: !!geometry.attributes.tangent && (HAS_NORMALMAP || HAS_ANISOTROPY), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4, pointsUvs: object.isPoints === true && !!geometry.attributes.uv && (HAS_MAP || HAS_ALPHAMAP), fog: !!fog, useFog: material.fog === true, fogExp2: !!fog && fog.isFogExp2, flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numLightProbes: lights.numLightProbes, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: toneMapping, decodeVideoTexture: HAS_MAP && material.map.isVideoTexture === true && ColorManagement.getTransfer(material.map.colorSpace) === SRGBTransfer, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionClipCullDistance: HAS_EXTENSIONS && material.extensions.clipCullDistance === true && extensions.has("WEBGL_clip_cull_distance"), extensionMultiDraw: HAS_EXTENSIONS && material.extensions.multiDraw === true && extensions.has("WEBGL_multi_draw"), rendererExtensionParallelShaderCompile: extensions.has( "KHR_parallel_shader_compile", ), customProgramCacheKey: material.customProgramCacheKey(), }; // the usage of getChannel() determines the active texture channels for this shader parameters.vertexUv1s = _activeChannels.has(1); parameters.vertexUv2s = _activeChannels.has(2); parameters.vertexUv3s = _activeChannels.has(3); _activeChannels.clear(); return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputColorSpace); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputColorSpace); array.push(parameters.envMapMode); array.push(parameters.envMapCubeUVHeight); array.push(parameters.mapUv); array.push(parameters.alphaMapUv); array.push(parameters.lightMapUv); array.push(parameters.aoMapUv); array.push(parameters.bumpMapUv); array.push(parameters.normalMapUv); array.push(parameters.displacementMapUv); array.push(parameters.emissiveMapUv); array.push(parameters.metalnessMapUv); array.push(parameters.roughnessMapUv); array.push(parameters.anisotropyMapUv); array.push(parameters.clearcoatMapUv); array.push(parameters.clearcoatNormalMapUv); array.push(parameters.clearcoatRoughnessMapUv); array.push(parameters.iridescenceMapUv); array.push(parameters.iridescenceThicknessMapUv); array.push(parameters.sheenColorMapUv); array.push(parameters.sheenRoughnessMapUv); array.push(parameters.specularMapUv); array.push(parameters.specularColorMapUv); array.push(parameters.specularIntensityMapUv); array.push(parameters.transmissionMapUv); array.push(parameters.thicknessMapUv); array.push(parameters.combine); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.morphTargetsCount); array.push(parameters.morphAttributeCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numSpotLightMaps); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.numSpotLightShadowsWithMaps); array.push(parameters.numLightProbes); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); array.push(parameters.depthPacking); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.supportsVertexTextures) _programLayers.enable(0); if (parameters.instancing) _programLayers.enable(1); if (parameters.instancingColor) _programLayers.enable(2); if (parameters.instancingMorph) _programLayers.enable(3); if (parameters.matcap) _programLayers.enable(4); if (parameters.envMap) _programLayers.enable(5); if (parameters.normalMapObjectSpace) _programLayers.enable(6); if (parameters.normalMapTangentSpace) _programLayers.enable(7); if (parameters.clearcoat) _programLayers.enable(8); if (parameters.iridescence) _programLayers.enable(9); if (parameters.alphaTest) _programLayers.enable(10); if (parameters.vertexColors) _programLayers.enable(11); if (parameters.vertexAlphas) _programLayers.enable(12); if (parameters.vertexUv1s) _programLayers.enable(13); if (parameters.vertexUv2s) _programLayers.enable(14); if (parameters.vertexUv3s) _programLayers.enable(15); if (parameters.vertexTangents) _programLayers.enable(16); if (parameters.anisotropy) _programLayers.enable(17); if (parameters.alphaHash) _programLayers.enable(18); if (parameters.batching) _programLayers.enable(19); if (parameters.dispersion) _programLayers.enable(20); if (parameters.batchingColor) _programLayers.enable(21); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.fog) _programLayers.enable(0); if (parameters.useFog) _programLayers.enable(1); if (parameters.flatShading) _programLayers.enable(2); if (parameters.logarithmicDepthBuffer) _programLayers.enable(3); if (parameters.skinning) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.morphColors) _programLayers.enable(7); if (parameters.premultipliedAlpha) _programLayers.enable(8); if (parameters.shadowMapEnabled) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.useDepthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.transmission) _programLayers.enable(14); if (parameters.sheen) _programLayers.enable(15); if (parameters.opaque) _programLayers.enable(16); if (parameters.pointsUvs) _programLayers.enable(17); if (parameters.decodeVideoTexture) _programLayers.enable(18); if (parameters.alphaToCoverage) _programLayers.enable(19); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose, }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose, }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group, }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group, ); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group, ); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort, }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { const listArray = lists.get(scene); let list; if (listArray === undefined) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= listArray.length) { list = new WebGLRenderList(); listArray.push(list); } else { list = listArray[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose, }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color(), }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color(), }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3(), }; break; } lights[light.id] = uniforms; return uniforms; }, }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000, }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; }, }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst(lightA, lightB) { return ( (lightB.castShadow ? 2 : 0) - (lightA.castShadow ? 2 : 0) + (lightB.map ? 1 : 0) - (lightA.map ? 1 : 0) ); } function WebGLLights(extensions) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: -1, pointLength: -1, spotLength: -1, rectAreaLength: -1, hemiLength: -1, numDirectionalShadows: -1, numPointShadows: -1, numSpotShadows: -1, numSpotMaps: -1, numLightProbes: -1, }, ambient: [0, 0, 0], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0, numLightProbes: 0, }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; let numLightProbes = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort(shadowCastingAndTexturingLightsFirst); for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } numLightProbes++; } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; state.spot[spotLength] = uniforms; const shadow = light.shadow; if (light.map) { state.spotLightMap[numSpotMaps] = light.map; numSpotMaps++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices(light); if (light.castShadow) numSpotShadowsWithMaps++; } state.spotLightMatrix[spotLength] = shadow.matrix; if (light.castShadow) { const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; numSpotShadows++; } spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps || hash.numLightProbes !== numLightProbes ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; state.numLightProbes = numLightProbes; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; hash.numLightProbes = numLightProbes; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state, }; } function WebGLRenderState(extensions) { const lights = new WebGLLights(extensions); const lightsArray = []; const shadowsArray = []; function init(camera) { state.camera = camera; lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights() { lights.setup(lightsArray); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, camera: null, lights: lights, transmissionRenderTarget: {}, }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow, }; } function WebGLRenderStates(extensions) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { const renderStateArray = renderStates.get(scene); let renderState; if (renderStateArray === undefined) { renderState = new WebGLRenderState(extensions); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStateArray.length) { renderState = new WebGLRenderState(extensions); renderStateArray.push(renderState); } else { renderState = renderStateArray[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose, }; } class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues(parameters); } copy(source) { super.copy(source); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(renderer, objects, capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({depthPacking: RGBADepthPacking}), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = capabilities.maxTextureSize; const shadowSide = { [FrontSide]: BackSide, [BackSide]: FrontSide, [DoubleSide]: DoubleSide, }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8, }, uniforms: { shadow_pass: {value: null}, resolution: {value: new Vector2()}, radius: {value: 4.0}, }, vertexShader: vertex, fragmentShader: fragment, }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3, ), ); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = renderer.getRenderTarget(); const activeCubeFace = renderer.getActiveCubeFace(); const activeMipmapLevel = renderer.getActiveMipmapLevel(); const _state = renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // check for shadow map type changes const toVSM = _previousType !== VSMShadowMap && this.type === VSMShadowMap; const fromVSM = _previousType === VSMShadowMap && this.type !== VSMShadowMap; // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null || toVSM === true || fromVSM === true) { const pars = this.type !== VSMShadowMap ? {minFilter: NearestFilter, magFilter: NearestFilter} : {}; if (shadow.map !== null) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars, ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } renderer.setRenderTarget(shadow.map); renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w, ); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (shadow.isPointLightShadow !== true && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel, ); }; function VSMPass(shadow, camera) { const geometry = objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if (shadow.mapPass === null) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; renderer.setRenderTarget(shadow.mapPass); renderer.clear(); renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null, ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; renderer.setRenderTarget(shadow.map); renderer.clear(); renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null, ); } function getDepthMaterial(object, material, light, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; if ( (renderer.localClippingEnabled && material.clipShadows === true && Array.isArray(material.clippingPlanes) && material.clippingPlanes.length !== 0) || (material.displacementMap && material.displacementScale !== 0) || (material.alphaMap && material.alphaTest > 0) || (material.map && material.alphaTest > 0) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; material.addEventListener("dispose", onMaterialDispose); } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { const materialProperties = renderer.properties.get(result); materialProperties.light = light; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ( (object.castShadow || (object.receiveShadow && type === VSMShadowMap)) && (!object.frustumCulled || _frustum.intersectsObject(object)) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld, ); const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, type, ); object.onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, group, ); renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group, ); object.onAfterShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, group, ); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, material, light, type); object.onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, null, ); renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null, ); object.onAfterShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, null, ); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); // make sure to remove the unique distance/depth materials used for shadow map rendering for (const id in _materialCache) { const cache = _materialCache[id]; const uuid = event.target.uuid; if (uuid in cache) { const shadowMaterial = cache[uuid]; shadowMaterial.dispose(); delete cache[uuid]; } } } } function WebGLState(gl) { function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state }, }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; }, }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; }, }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentBlendColor = new Color(0, 0, 0); let currentBlendAlpha = 0; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count, dimensions) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { if (type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY) { gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data, ); } else { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data, ); } } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6, ); emptyTextures[gl.TEXTURE_2D_ARRAY] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1, ); emptyTextures[gl.TEXTURE_3D] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1, ); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } const textures = renderTarget.textures; if ( drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0 ) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { gl.drawBuffers(drawBuffers); } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT, }; equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA, [ConstantColorFactor]: gl.CONSTANT_COLOR, [OneMinusConstantColorFactor]: gl.ONE_MINUS_CONSTANT_COLOR, [ConstantAlphaFactor]: gl.CONSTANT_ALPHA, [OneMinusConstantAlphaFactor]: gl.ONE_MINUS_CONSTANT_ALPHA, }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha, ) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, ); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE, ); break; case MultiplyBlending: gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA, ); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, ); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE, ); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlendColor.set(0, 0, 0); currentBlendAlpha = 0; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[blendEquation], equationToGL[blendEquationAlpha], ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha], ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } if ( blendColor.equals(currentBlendColor) === false || blendAlpha !== currentBlendAlpha ) { gl.blendColor(blendColor.r, blendColor.g, blendColor.b, blendAlpha); currentBlendColor.copy(blendColor); currentBlendAlpha = blendAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha, ); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask, ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass, ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits, ); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture, webglSlot) { if (webglSlot === undefined) { if (currentTextureSlot === null) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[webglSlot]; if (boundTexture === undefined) { boundTexture = {type: undefined, texture: undefined}; currentBoundTextures[webglSlot] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } function updateUBOMapping(uniformsGroup, program) { let mapping = uboProgramMap.get(program); if (mapping === undefined) { mapping = new WeakMap(); uboProgramMap.set(program, mapping); } let blockIndex = mapping.get(uniformsGroup); if (blockIndex === undefined) { blockIndex = gl.getUniformBlockIndex(program, uniformsGroup.name); mapping.set(uniformsGroup, blockIndex); } } function uniformBlockBinding(uniformsGroup, program) { const mapping = uboProgramMap.get(program); const blockIndex = mapping.get(uniformsGroup); if (uboBindings.get(program) !== blockIndex) { // bind shader specific block index to global block point gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex, ); uboBindings.set(program, blockIndex); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.blendColor(0, 0, 0, 0); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlendColor = new Color(0, 0, 0); currentBlendAlpha = 0; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer, }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset, }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info, ) { const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture", ) ? extensions.get("WEBGL_multisampled_render_to_texture") : null; const supportsInvalidateFramebuffer = typeof navigator === "undefined" ? false : /OculusBrowser/g.test(navigator.userAgent); const _imageDimensions = new Vector2(); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && // eslint-disable-next-line compat/compat new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) { // Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsNewCanvas, maxSize) { let scale = 1; const dimensions = getDimensions(image); // handle case if texture exceeds max size if (dimensions.width > maxSize || dimensions.height > maxSize) { scale = maxSize / Math.max(dimensions.width, dimensions.height); } // only perform resize if necessary if (scale < 1) { // only perform resize for certain image types if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) || (typeof VideoFrame !== "undefined" && image instanceof VideoFrame) ) { const width = Math.floor(scale * dimensions.width); const height = Math.floor(scale * dimensions.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + dimensions.width + "x" + dimensions.height + ") to (" + width + "x" + height + ").", ); return canvas; } else { if ("data" in image) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + dimensions.width + "x" + dimensions.height + ").", ); } return image; } } return image; } function textureNeedsGenerateMipmaps(texture) { return ( texture.generateMipmaps && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false, ) { if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """, ); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RED_INTEGER) { if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8UI; if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.R16UI; if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.R32UI; if (glType === _gl.BYTE) internalFormat = _gl.R8I; if (glType === _gl.SHORT) internalFormat = _gl.R16I; if (glType === _gl.INT) internalFormat = _gl.R32I; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RG_INTEGER) { if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8UI; if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.RG16UI; if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.RG32UI; if (glType === _gl.BYTE) internalFormat = _gl.RG8I; if (glType === _gl.SHORT) internalFormat = _gl.RG16I; if (glType === _gl.INT) internalFormat = _gl.RG32I; } if (glFormat === _gl.RGB) { if (glType === _gl.UNSIGNED_INT_5_9_9_9_REV) internalFormat = _gl.RGB9_E5; } if (glFormat === _gl.RGBA) { const transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer(colorSpace); if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = transfer === SRGBTransfer ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getInternalDepthFormat(useStencil, depthType) { let glInternalFormat; if (useStencil) { if ( depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else if (depthType === FloatType) { glInternalFormat = _gl.DEPTH32F_STENCIL8; } else if (depthType === UnsignedShortType) { glInternalFormat = _gl.DEPTH24_STENCIL8; console.warn( "DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.", ); } } else { if ( depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (depthType === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthType === UnsignedShortType) { glInternalFormat = _gl.DEPTH_COMPONENT16; } } return glInternalFormat; } function getMipLevels(texture, image) { if ( textureNeedsGenerateMipmaps(texture) === true || (texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) ) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get(source); if (webglTextures) { const webglTexture = webglTextures[textureProperties.__cacheKey]; webglTexture.usedTimes--; // the WebGLTexture object is not used anymore, remove it if (webglTexture.usedTimes === 0) { deleteTexture(texture); } // remove the weak map entry if no WebGLTexture uses the source anymore if (Object.keys(webglTextures).length === 0) { _sources.delete(source); } } properties.remove(texture); } function deleteTexture(texture) { const textureProperties = properties.get(texture); _gl.deleteTexture(textureProperties.__webglTexture); const source = texture.source; const webglTextures = _sources.get(source); delete webglTextures[textureProperties.__cacheKey]; info.memory.textures--; } function deallocateRenderTarget(renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { if (Array.isArray(renderTargetProperties.__webglFramebuffer[i])) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer[i].length; level++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[i][level], ); } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); } if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { if (Array.isArray(renderTargetProperties.__webglFramebuffer)) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[level], ); } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); } if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer, ); if (renderTargetProperties.__webglColorRenderbuffer) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i++ ) { if (renderTargetProperties.__webglColorRenderbuffer[i]) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[i], ); } } if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } const textures = renderTarget.textures; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(textures[i]); } properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= capabilities.maxTextures) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + capabilities.maxTextures, ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey(texture) { const array = []; array.push(texture.wrapS); array.push(texture.wrapT); array.push(texture.wrapR || 0); array.push(texture.magFilter); array.push(texture.minFilter); array.push(texture.anisotropy); array.push(texture.internalFormat); array.push(texture.format); array.push(texture.type); array.push(texture.generateMipmaps); array.push(texture.premultiplyAlpha); array.push(texture.flipY); array.push(texture.unpackAlignment); array.push(texture.colorSpace); return array.join(); } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if (image === null) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found.", ); } else if (image.complete === false) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete", ); } else { uploadTexture(textureProperties, texture, slot); return; } } state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT, }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR, }; const compareToGL = { [NeverCompare]: _gl.NEVER, [AlwaysCompare]: _gl.ALWAYS, [LessCompare]: _gl.LESS, [LessEqualCompare]: _gl.LEQUAL, [EqualCompare]: _gl.EQUAL, [GreaterEqualCompare]: _gl.GEQUAL, [GreaterCompare]: _gl.GREATER, [NotEqualCompare]: _gl.NOTEQUAL, }; function setTextureParameters(textureType, texture) { if ( texture.type === FloatType && extensions.has("OES_texture_float_linear") === false && (texture.magFilter === LinearFilter || texture.magFilter === LinearMipmapNearestFilter || texture.magFilter === NearestMipmapLinearFilter || texture.magFilter === LinearMipmapLinearFilter || texture.minFilter === LinearFilter || texture.minFilter === LinearMipmapNearestFilter || texture.minFilter === NearestMipmapLinearFilter || texture.minFilter === LinearMipmapLinearFilter) ) { console.warn( "THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.", ); } _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS], ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT], ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR], ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter], ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter], ); if (texture.compareFunction) { _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE, ); _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[texture.compareFunction], ); } if (extensions.has("EXT_texture_filter_anisotropic") === true) { if (texture.magFilter === NearestFilter) return; if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; if ( texture.type === FloatType && extensions.has("OES_texture_float_linear") === false ) return; // verify extension if ( texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy ) { const extension = extensions.get("EXT_texture_filter_anisotropic"); _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy()), ); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { let forceUpload = false; if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get(source); if (webglTextures === undefined) { webglTextures = {}; _sources.set(source, webglTextures); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey(texture); if (textureCacheKey !== textureProperties.__cacheKey) { // if not, create a new instance of WebGLTexture if (webglTextures[textureCacheKey] === undefined) { // create new entry webglTextures[textureCacheKey] = { texture: _gl.createTexture(), usedTimes: 0, }; info.memory.textures++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[textureCacheKey].usedTimes++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[textureProperties.__cacheKey]; if (webglTexture !== undefined) { webglTextures[textureProperties.__cacheKey].usedTimes--; if (webglTexture.usedTimes === 0) { deleteTexture(texture); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[textureCacheKey].texture; } return forceUpload; } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataArrayTexture || texture.isCompressedArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace); const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion); let image = resizeImage( texture.image, false, capabilities.maxTextureSize, ); image = verifyColorSpace(texture, image); const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); let glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture, ); setTextureParameters(textureType, texture); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; const dataReady = source.dataReady; const levels = getMipLevels(texture, image); if (texture.isDepthTexture) { glInternalFormat = getInternalDepthFormat( texture.format === DepthStencilFormat, texture.type, ); // if (allocateMemory) { if (useTexStorage) { state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height, ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null, ); } } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0) { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height, ); } if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data, ); } } } else if (texture.isCompressedTexture) { if (texture.isCompressedArrayTexture) { if (useTexStorage && allocateMemory) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, image.depth, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { if (texture.layerUpdates.size > 0) { for (const layerIndex of texture.layerUpdates) { const layerSize = mipmap.width * mipmap.height; state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, layerIndex, mipmap.width, mipmap.height, 1, glFormat, mipmap.data.slice( layerSize * layerIndex, layerSize * (layerIndex + 1), ), 0, 0, ); } texture.clearLayerUpdates(); } else { state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0, ); } } } else { state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data, ); } } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data, ); } } } } else { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data, ); } } else { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } } } } else if (texture.isDataArrayTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth, ); } if (dataReady) { if (texture.layerUpdates.size > 0) { // When type is GL_UNSIGNED_BYTE, each of these bytes is // interpreted as one color component, depending on format. When // type is one of GL_UNSIGNED_SHORT_5_6_5, // GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1, each // unsigned value is interpreted as containing all the components // for a single pixel, with the color components arranged // according to format. // // See https://registry.khronos.org/OpenGL-Refpages/es1.1/xhtml/glTexImage2D.xml let texelSize; switch (glType) { case _gl.UNSIGNED_BYTE: switch (glFormat) { case _gl.ALPHA: texelSize = 1; break; case _gl.LUMINANCE: texelSize = 1; break; case _gl.LUMINANCE_ALPHA: texelSize = 2; break; case _gl.RGB: texelSize = 3; break; case _gl.RGBA: texelSize = 4; break; default: throw new Error( `Unknown texel size for format ${glFormat}.`, ); } break; case _gl.UNSIGNED_SHORT_4_4_4_4: case _gl.UNSIGNED_SHORT_5_5_5_1: case _gl.UNSIGNED_SHORT_5_6_5: texelSize = 1; break; default: throw new Error(`Unknown texel size for type ${glType}.`); } const layerSize = image.width * image.height * texelSize; for (const layerIndex of texture.layerUpdates) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, layerIndex, image.width, image.height, 1, glFormat, glType, image.data.slice( layerSize * layerIndex, layerSize * (layerIndex + 1), ), ); } texture.clearLayerUpdates(); } else { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data, ); } } } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data, ); } } else if (texture.isData3DTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth, ); } if (dataReady) { state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data, ); } } else { state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data, ); } } else if (texture.isFramebufferTexture) { if (allocateMemory) { if (useTexStorage) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height, ); } else { let width = image.width, height = image.height; for (let i = 0; i < levels; i++) { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null, ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0) { if (useTexStorage && allocateMemory) { const dimensions = getDimensions(mipmaps[0]); state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap, ); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { const dimensions = getDimensions(image); state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height, ); } if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image, ); } } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image, ); } } } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(textureType); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace); const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion); const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture; const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage( texture.image[i], true, capabilities.maxCubemapSize, ); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], glFormat = utils.convert(texture.format, texture.colorSpace), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const useTexStorage = texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; const dataReady = source.dataReady; let levels = getMipLevels(texture, image); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height, ); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data, ); } } else { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; const dimensions = getDimensions(cubeImage[0]); state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, dimensions.width, dimensions.height, ); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data, ); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data, ); } } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i], ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i], ); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i], ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i], ); } } } } } if (textureNeedsGenerateMipmaps(texture)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level, ) { const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { const width = Math.max(1, renderTarget.width >> level); const height = Math.max(1, renderTarget.height >> level); if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null, ); } else { state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null, ); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget), ); } else if ( textureTarget === _gl.TEXTURE_2D || (textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z) ) { // see #24753 _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, level, ); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer) { // retrieve the depth attachment types const depthTexture = renderTarget.depthTexture; const depthType = depthTexture && depthTexture.isDepthTexture ? depthTexture.type : null; const glInternalFormat = getInternalDepthFormat( renderTarget.stencilBuffer, depthType, ); const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; // set up the attachment const samples = getRenderTargetSamples(renderTarget); const isUseMultisampledRTT = useMultisampledRTT(renderTarget); if (isUseMultisampledRTT) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else if (isMultisample) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height, ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, glAttachmentType, _gl.RENDERBUFFER, renderbuffer, ); } else { const textures = renderTarget.textures; for (let i = 0; i < textures.length; i++) { const texture = textures[i]; const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height, ); } } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error( "Depth Texture with cube render targets is not supported", ); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if ( !(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture", ); } // upload an empty depth texture with framebuffer size if ( !properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get( renderTarget.depthTexture, ).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples, ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, ); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples, ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, ); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if ( renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer ) { if (isCube) throw new Error( "target.depthTexture not supported in Cube render targets", ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget, ); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i], ); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[i], renderTarget, false, ); } } else { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false, ); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0, ); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); const textures = renderTarget.textures; const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = textures.length > 1; if (!isMultipleRenderTargets) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { if (texture.mipmaps && texture.mipmaps.length > 0) { renderTargetProperties.__webglFramebuffer[i] = []; for (let level = 0; level < texture.mipmaps.length; level++) { renderTargetProperties.__webglFramebuffer[i][level] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } } else { if (texture.mipmaps && texture.mipmaps.length > 0) { renderTargetProperties.__webglFramebuffer = []; for (let level = 0; level < texture.mipmaps.length; level++) { renderTargetProperties.__webglFramebuffer[level] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } if (isMultipleRenderTargets) { for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } if ( renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false ) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); for (let i = 0; i < textures.length; i++) { const texture = textures[i]; renderTargetProperties.__webglColorRenderbuffer[i] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true, ); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true, ); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture); for (let i = 0; i < 6; i++) { if (texture.mipmaps && texture.mipmaps.length > 0) { for (let level = 0; level < texture.mipmaps.length; level++) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[i][level], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, ); } } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0, ); if (textureNeedsGenerateMipmaps(attachment)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture); if (texture.mipmaps && texture.mipmaps.length > 0) { for (let level = 0; level < texture.mipmaps.length; level++) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[level], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level, ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0, ); } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const textures = renderTarget.textures; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } const invalidationArrayRead = []; const invalidationArrayDraw = []; function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.samples > 0) { if (useMultisampledRTT(renderTarget) === false) { const textures = renderTarget.textures; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get(renderTarget); const isMultipleRenderTargets = textures.length > 1; // If MRT we need to remove FBO attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null, ); state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0, ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); for (let i = 0; i < textures.length; i++) { if (renderTarget.resolveDepthBuffer) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; // resolving stencil is slow with a D3D backend. disable it for all transmission render targets (see #27799) if (renderTarget.stencilBuffer && renderTarget.resolveStencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } if (isMultipleRenderTargets) { _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const webglTexture = properties.get(textures[i]).__webglTexture; _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0, ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST, ); if (supportsInvalidateFramebuffer === true) { invalidationArrayRead.length = 0; invalidationArrayDraw.length = 0; invalidationArrayRead.push(_gl.COLOR_ATTACHMENT0 + i); if ( renderTarget.depthBuffer && renderTarget.resolveDepthBuffer === false ) { invalidationArrayRead.push(depthStyle); invalidationArrayDraw.push(depthStyle); _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, invalidationArrayDraw, ); } _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArrayRead, ); } } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, null); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const webglTexture = properties.get(textures[i]).__webglTexture; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0, ); } } state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); } else { if ( renderTarget.depthBuffer && renderTarget.resolveDepthBuffer === false && supportsInvalidateFramebuffer ) { const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } } } } function getRenderTargetSamples(renderTarget) { return Math.min(capabilities.maxSamples, renderTarget.samples); } function useMultisampledRTT(renderTarget) { const renderTargetProperties = properties.get(renderTarget); return ( renderTarget.samples > 0 && extensions.has("WEBGL_multisampled_render_to_texture") === true && renderTargetProperties.__useRenderToTexture !== false ); } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true) return image; if (colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace) { // sRGB if (ColorManagement.getTransfer(colorSpace) === SRGBTransfer) { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.", ); } } else { console.error( "THREE.WebGLTextures: Unsupported texture color space:", colorSpace, ); } } return image; } function getDimensions(image) { if ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) { // if intrinsic data are not available, fallback to width/height _imageDimensions.width = image.naturalWidth || image.width; _imageDimensions.height = image.naturalHeight || image.height; } else if ( typeof VideoFrame !== "undefined" && image instanceof VideoFrame ) { _imageDimensions.width = image.displayWidth; _imageDimensions.height = image.displayHeight; } else { _imageDimensions.width = image.width; _imageDimensions.height = image.height; } return _imageDimensions; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils(gl, extensions) { function convert(p, colorSpace = NoColorSpace) { let extension; const transfer = ColorManagement.getTransfer(colorSpace); if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === UnsignedInt5999Type) return gl.UNSIGNED_INT_5_9_9_9_REV; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) return gl.HALF_FLOAT; if (p === AlphaFormat) return gl.ALPHA; if (p === RGBFormat) return gl.RGB; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; // WebGL2 formats. if (p === RedFormat) return gl.RED; if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if (transfer === SRGBTransfer) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC1_Format || p === RGB_ETC2_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; if (p === RGB_BPTC_SIGNED_Format) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT; if (p === RGB_BPTC_UNSIGNED_Format) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT; } else { return null; } } // RGTC if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { extension = extensions.get("EXT_texture_compression_rgtc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return extension.COMPRESSED_RED_RGTC1_EXT; if (p === SIGNED_RED_RGTC1_Format) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if (p === RED_GREEN_RGTC2_Format) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if (p === SIGNED_RED_GREEN_RGTC2_Format) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if (p === UnsignedInt248Type) return gl.UNSIGNED_INT_24_8; // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return gl[p] !== undefined ? gl[p] : null; } return {convert: convert}; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = {type: "move"}; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = {pinching: false}; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } connect(inputSource) { if (inputSource && inputSource.hand) { const hand = this._hand; if (hand) { for (const inputjoint of inputSource.hand.values()) { // Initialize hand with joints when connected this._getHandJoint(hand, inputjoint); } } } this.dispatchEvent({type: "connected", data: inputSource}); return this; } disconnect(inputSource) { this.dispatchEvent({type: "disconnected", data: inputSource}); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint(hand, inputjoint); if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this, }); } else if ( !hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this, }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); grip.matrixWorldNeedsUpdate = true; if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if (inputPose === null && gripPose !== null) { inputPose = gripPose; } if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale, ); targetRay.matrixWorldNeedsUpdate = true; if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } // private method _getHandJoint(hand, inputjoint) { if (hand.joints[inputjoint.jointName] === undefined) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; hand.add(joint); } return hand.joints[inputjoint.jointName]; } } const _occlusion_vertex = ` void main() { gl_Position = vec4( position, 1.0 ); }`; const _occlusion_fragment = ` uniform sampler2DArray depthColor; uniform float depthWidth; uniform float depthHeight; void main() { vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); if ( coord.x >= 1.0 ) { gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; } else { gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; } }`; class WebXRDepthSensing { constructor() { this.texture = null; this.mesh = null; this.depthNear = 0; this.depthFar = 0; } init(renderer, depthData, renderState) { if (this.texture === null) { const texture = new Texture(); const texProps = renderer.properties.get(texture); texProps.__webglTexture = depthData.texture; if ( depthData.depthNear != renderState.depthNear || depthData.depthFar != renderState.depthFar ) { this.depthNear = depthData.depthNear; this.depthFar = depthData.depthFar; } this.texture = texture; } } getMesh(cameraXR) { if (this.texture !== null) { if (this.mesh === null) { const viewport = cameraXR.cameras[0].viewport; const material = new ShaderMaterial({ vertexShader: _occlusion_vertex, fragmentShader: _occlusion_fragment, uniforms: { depthColor: {value: this.texture}, depthWidth: {value: viewport.z}, depthHeight: {value: viewport.w}, }, }); this.mesh = new Mesh(new PlaneGeometry(20, 20), material); } } return this.mesh; } reset() { this.texture = null; this.mesh = null; } } class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const depthSensing = new WebXRDepthSensing(); const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; const currentSize = new Vector2(); let currentPixelRatio = null; // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraXR = new ArrayCamera(); cameraXR.layers.enable(1); cameraXR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controllerIndex = controllerInputSources.indexOf(event.inputSource); if (controllerIndex === -1) { return; } const controller = controllers[controllerIndex]; if (controller !== undefined) { controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace, ); controller.dispatchEvent({type: event.type, data: event.inputSource}); } } function onSessionEnd() { session.removeEventListener("select", onSessionEvent); session.removeEventListener("selectstart", onSessionEvent); session.removeEventListener("selectend", onSessionEvent); session.removeEventListener("squeeze", onSessionEvent); session.removeEventListener("squeezestart", onSessionEvent); session.removeEventListener("squeezeend", onSessionEvent); session.removeEventListener("end", onSessionEnd); session.removeEventListener("inputsourceschange", onInputSourcesChange); for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; if (inputSource === null) continue; controllerInputSources[i] = null; controllers[i].disconnect(inputSource); } _currentDepthNear = null; _currentDepthFar = null; depthSensing.reset(); // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; renderer.setPixelRatio(currentPixelRatio); renderer.setSize(currentSize.width, currentSize.height, false); scope.dispatchEvent({type: "sessionend"}); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting.", ); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting.", ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function (space) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } currentPixelRatio = renderer.getPixelRatio(); renderer.getSize(currentSize); if (session.renderState.layers === undefined) { const layerInit = { antialias: attributes.antialias, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor, }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({baseLayer: glBaseLayer}); renderer.setPixelRatio(1); renderer.setSize( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, false, ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil, }, ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor, }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({layers: [glProjLayer]}); renderer.setPixelRatio(1); renderer.setSize( glProjLayer.textureWidth, glProjLayer.textureHeight, false, ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat, ), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0, resolveDepthBuffer: glProjLayer.ignoreDepthValues === false, }, ); } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation(foveation); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({type: "sessionstart"}); } }; this.getEnvironmentBlendMode = function () { if (session !== null) { return session.environmentBlendMode; } }; function onInputSourcesChange(event) { // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const index = controllerInputSources.indexOf(inputSource); if (index >= 0) { controllerInputSources[index] = null; controllers[index].disconnect(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; let controllerIndex = controllerInputSources.indexOf(inputSource); if (controllerIndex === -1) { // Assign input source a controller that currently has no input source for (let i = 0; i < controllers.length; i++) { if (i >= controllerInputSources.length) { controllerInputSources.push(inputSource); controllerIndex = i; break; } else if (controllerInputSources[i] === null) { controllerInputSources[i] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if (controllerIndex === -1) break; } const controller = controllers[controllerIndex]; if (controller) { controller.connect(inputSource); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale, ); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale, ); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = ((topFov * far) / far2) * near2; const bottom2 = ((bottomFov * far) / far2) * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2, ); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; if (depthSensing.texture !== null) { camera.near = depthSensing.depthNear; camera.far = depthSensing.depthFar; } cameraXR.near = cameraR.near = cameraL.near = camera.near; cameraXR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraXR.near, depthFar: cameraXR.far, }); _currentDepthNear = cameraXR.near; _currentDepthFar = cameraXR.far; cameraL.near = _currentDepthNear; cameraL.far = _currentDepthFar; cameraR.near = _currentDepthNear; cameraR.far = _currentDepthFar; cameraL.updateProjectionMatrix(); cameraR.updateProjectionMatrix(); camera.updateProjectionMatrix(); } const parent = camera.parent; const cameras = cameraXR.cameras; updateCamera(cameraXR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraXR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraXR.projectionMatrix.copy(cameraL.projectionMatrix); } // update user camera and its children updateUserCamera(camera, cameraXR, parent); }; function updateUserCamera(camera, cameraXR, parent) { if (parent === null) { camera.matrix.copy(cameraXR.matrixWorld); } else { camera.matrix.copy(parent.matrixWorld); camera.matrix.invert(); camera.matrix.multiply(cameraXR.matrixWorld); } camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); camera.updateMatrixWorld(true); camera.projectionMatrix.copy(cameraXR.projectionMatrix); camera.projectionMatrixInverse.copy(cameraXR.projectionMatrixInverse); if (camera.isPerspectiveCamera) { camera.fov = RAD2DEG * 2 * Math.atan(1 / camera.projectionMatrix.elements[5]); camera.zoom = 1; } } this.getCamera = function () { return cameraXR; }; this.getFoveation = function () { if (glProjLayer === null && glBaseLayer === null) { return undefined; } return foveation; }; this.setFoveation = function (value) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if (glProjLayer !== null) { glProjLayer.fixedFoveation = value; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = value; } }; this.hasDepthSensing = function () { return depthSensing.texture !== null; }; this.getDepthSensingMesh = function () { return depthSensing.getMesh(cameraXR); }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(customReferenceSpace || referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer, ); renderer.setRenderTarget(newRenderTarget); } let cameraXRNeedsUpdate = false; // check if it"s necessary to rebuild cameraXR"s camera list if (views.length !== cameraXR.cameras.length) { cameraXR.cameras.length = 0; cameraXRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture, ); renderer.setRenderTarget(newRenderTarget); } } let camera = cameras[i]; if (camera === undefined) { camera = new PerspectiveCamera(); camera.layers.enable(i); camera.viewport = new Vector4(); cameras[i] = camera; } camera.matrix.fromArray(view.transform.matrix); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale, ); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height, ); if (i === 0) { cameraXR.matrix.copy(camera.matrix); cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale, ); } if (cameraXRNeedsUpdate === true) { cameraXR.cameras.push(camera); } } // const enabledFeatures = session.enabledFeatures; if (enabledFeatures && enabledFeatures.includes("depth-sensing")) { const depthData = glBinding.getDepthInformation(views[0]); if (depthData && depthData.isValid && depthData.texture) { depthSensing.init(renderer, depthData, session.renderState); } } } // for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; const controller = controllers[i]; if (inputSource !== null && controller !== undefined) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace, ); } } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); if (frame.detectedPlanes) { scope.dispatchEvent({type: "planesdetected", data: frame}); } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } const _e1 = /*@__PURE__*/ new Euler(); const _m1 = /*@__PURE__*/ new Matrix4(); function WebGLMaterials(renderer, properties) { function refreshTransformUniform(map, uniform) { if (map.matrixAutoUpdate === true) { map.updateMatrix(); } uniform.value.copy(map.matrix); } function refreshFogUniforms(uniforms, fog) { fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace(renderer), ); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget, ) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsStandard(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value .copy(material.emissive) .multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform(material.bumpMap, uniforms.bumpMapTransform); uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) { uniforms.bumpScale.value *= -1; } } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform(material.normalMap, uniforms.normalMapTransform); uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) { uniforms.normalScale.value.negate(); } } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform, ); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform, ); } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform( material.specularMap, uniforms.specularMapTransform, ); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const materialProperties = properties.get(material); const envMap = materialProperties.envMap; const envMapRotation = materialProperties.envMapRotation; if (envMap) { uniforms.envMap.value = envMap; _e1.copy(envMapRotation); // accommodate left-handed frame _e1.x *= -1; _e1.y *= -1; _e1.z *= -1; if (envMap.isCubeTexture && envMap.isRenderTargetTexture === false) { // environment maps which are not cube render targets or PMREMs follow a different convention _e1.y *= -1; _e1.z *= -1; } uniforms.envMapRotation.value.setFromMatrix4( _m1.makeRotationFromEuler(_e1), ); uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; refreshTransformUniform(material.lightMap, uniforms.lightMapTransform); } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform(material.aoMap, uniforms.aoMapTransform); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.uvTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard(uniforms, material) { uniforms.metalness.value = material.metalness; if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform, ); } uniforms.roughness.value = material.roughness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform, ); } if (material.envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget, ) { uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value .copy(material.sheenColor) .multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform, ); } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform, ); } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform, ); } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform, ); } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform, ); uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.dispersion > 0) { uniforms.dispersion.value = material.dispersion; } if (material.iridescence > 0) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[0]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[1]; if (material.iridescenceMap) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform, ); } if (material.iridescenceThicknessMap) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform, ); } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height, ); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform, ); } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform, ); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } if (material.anisotropy > 0) { uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos(material.anisotropyRotation), material.anisotropy * Math.sin(material.anisotropyRotation), ); if (material.anisotropyMap) { uniforms.anisotropyMap.value = material.anisotropyMap; refreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform, ); } } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform, ); } if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform, ); } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance(uniforms, material) { const light = properties.get(material).light; uniforms.referencePosition.value.setFromMatrixPosition(light.matrixWorld); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms, }; } function WebGLUniformsGroups(gl, info, capabilities, state) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = gl.getParameter(gl.MAX_UNIFORM_BUFFER_BINDINGS); // binding points are global whereas block indices are per shader program function bind(uniformsGroup, program) { const webglProgram = program.program; state.uniformBlockBinding(uniformsGroup, webglProgram); } function update(uniformsGroup, program) { let buffer = buffers[uniformsGroup.id]; if (buffer === undefined) { prepareUniformsGroup(uniformsGroup); buffer = createBuffer(uniformsGroup); buffers[uniformsGroup.id] = buffer; uniformsGroup.addEventListener("dispose", onUniformsGroupsDispose); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping(uniformsGroup, webglProgram); // update UBO once per frame const frame = info.render.frame; if (updateList[uniformsGroup.id] !== frame) { updateBufferData(uniformsGroup); updateList[uniformsGroup.id] = frame; } } function createBuffer(uniformsGroup) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); gl.bufferData(gl.UNIFORM_BUFFER, size, usage); gl.bindBuffer(gl.UNIFORM_BUFFER, null); gl.bindBufferBase(gl.UNIFORM_BUFFER, bindingPointIndex, buffer); return buffer; } function allocateBindingPointIndex() { for (let i = 0; i < maxBindingPoints; i++) { if (allocatedBindingPoints.indexOf(i) === -1) { allocatedBindingPoints.push(i); return i; } } console.error( "THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.", ); return 0; } function updateBufferData(uniformsGroup) { const buffer = buffers[uniformsGroup.id]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); for (let i = 0, il = uniforms.length; i < il; i++) { const uniformArray = Array.isArray(uniforms[i]) ? uniforms[i] : [uniforms[i]]; for (let j = 0, jl = uniformArray.length; j < jl; j++) { const uniform = uniformArray[j]; if (hasUniformChanged(uniform, i, j, cache) === true) { const offset = uniform.__offset; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; let arrayOffset = 0; for (let k = 0; k < values.length; k++) { const value = values[k]; const info = getUniformSize(value); // TODO add integer and struct support if (typeof value === "number" || typeof value === "boolean") { uniform.__data[0] = value; gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data, ); } else if (value.isMatrix3) { // manually converting 3x3 to 3x4 uniform.__data[0] = value.elements[0]; uniform.__data[1] = value.elements[1]; uniform.__data[2] = value.elements[2]; uniform.__data[3] = 0; uniform.__data[4] = value.elements[3]; uniform.__data[5] = value.elements[4]; uniform.__data[6] = value.elements[5]; uniform.__data[7] = 0; uniform.__data[8] = value.elements[6]; uniform.__data[9] = value.elements[7]; uniform.__data[10] = value.elements[8]; uniform.__data[11] = 0; } else { value.toArray(uniform.__data, arrayOffset); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData(gl.UNIFORM_BUFFER, offset, uniform.__data); } } } gl.bindBuffer(gl.UNIFORM_BUFFER, null); } function hasUniformChanged(uniform, index, indexArray, cache) { const value = uniform.value; const indexString = index + "_" + indexArray; if (cache[indexString] === undefined) { // cache entry does not exist so far if (typeof value === "number" || typeof value === "boolean") { cache[indexString] = value; } else { cache[indexString] = value.clone(); } return true; } else { const cachedObject = cache[indexString]; // compare current value with cached entry if (typeof value === "number" || typeof value === "boolean") { if (cachedObject !== value) { cache[indexString] = value; return true; } } else { if (cachedObject.equals(value) === false) { cachedObject.copy(value); return true; } } } return false; } function prepareUniformsGroup(uniformsGroup) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes for (let i = 0, l = uniforms.length; i < l; i++) { const uniformArray = Array.isArray(uniforms[i]) ? uniforms[i] : [uniforms[i]]; for (let j = 0, jl = uniformArray.length; j < jl; j++) { const uniform = uniformArray[j]; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; for (let k = 0, kl = values.length; k < kl; k++) { const value = values[k]; const info = getUniformSize(value); // Calculate the chunk offset const chunkOffsetUniform = offset % chunkSize; // Check for chunk overflow if ( chunkOffsetUniform !== 0 && chunkSize - chunkOffsetUniform < info.boundary ) { // Add padding and adjust offset offset += chunkSize - chunkOffsetUniform; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array( info.storage / Float32Array.BYTES_PER_ELEMENT, ); uniform.__offset = offset; // Update the global offset offset += info.storage; } } } // ensure correct final padding const chunkOffset = offset % chunkSize; if (chunkOffset > 0) offset += chunkSize - chunkOffset; // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize(value) { const info = { boundary: 0, // bytes storage: 0, // bytes }; // determine sizes according to STD140 if (typeof value === "number" || typeof value === "boolean") { // float/int/bool info.boundary = 4; info.storage = 4; } else if (value.isVector2) { // vec2 info.boundary = 8; info.storage = 8; } else if (value.isVector3 || value.isColor) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if (value.isVector4) { // vec4 info.boundary = 16; info.storage = 16; } else if (value.isMatrix3) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if (value.isMatrix4) { // mat4 info.boundary = 64; info.storage = 64; } else if (value.isTexture) { console.warn( "THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.", ); } else { console.warn( "THREE.WebGLRenderer: Unsupported uniform value type.", value, ); } return info; } function onUniformsGroupsDispose(event) { const uniformsGroup = event.target; uniformsGroup.removeEventListener("dispose", onUniformsGroupsDispose); const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex, ); allocatedBindingPoints.splice(index, 1); gl.deleteBuffer(buffers[uniformsGroup.id]); delete buffers[uniformsGroup.id]; delete updateList[uniformsGroup.id]; } function dispose() { for (const id in buffers) { gl.deleteBuffer(buffers[id]); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose, }; } class WebGLRenderer { constructor(parameters = {}) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = false, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = "default", failIfMajorPerformanceCaveat = false, } = parameters; this.isWebGLRenderer = true; let _alpha; if (context !== null) { if ( typeof WebGLRenderingContext !== "undefined" && context instanceof WebGLRenderingContext ) { throw new Error( "THREE.WebGLRenderer: WebGL 1 is not supported since r163.", ); } _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } const uintClearColor = new Uint32Array(4); const intClearColor = new Int32Array(4); let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null, }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this._outputColorSpace = SRGBColorSpace; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; const _currentClearColor = new Color(0x000000); let _currentClearAlpha = 0; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true, }; let _renderBackground = false; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext(contextName, contextAttributes) { return canvas.getContext(contextName, contextAttributes); } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat, }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in canvas) canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener("webglcontextlost", onContextLost, false); canvas.addEventListener("webglcontextrestored", onContextRestore, false); canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false, ); if (_gl === null) { const contextName = "webgl2"; _gl = getContext(contextName, contextAttributes); if (_gl === null) { if (getContext(contextName)) { throw new Error( "Error creating WebGL context with your selected attributes.", ); } else { throw new Error("Error creating WebGL context."); } } } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions(_gl); extensions.init(); utils = new WebGLUtils(_gl, extensions); capabilities = new WebGLCapabilities(_gl, extensions, parameters, utils); state = new WebGLState(_gl); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info, ); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl); bindingStates = new WebGLBindingStates(_gl, attributes); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping, ); materials = new WebGLMaterials(_this, properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha, ); shadowMap = new WebGLShadowMap(_this, objects, capabilities); uniformsGroups = new WebGLUniformsGroups(_gl, info, capabilities, state); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle = true) { if (xr.isPresenting) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting.", ); return; } _width = width; _height = height; canvas.width = Math.floor(width * _pixelRatio); canvas.height = Math.floor(height * _pixelRatio); if (updateStyle === true) { canvas.style.width = width + "px"; canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor(width * pixelRatio); canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport( _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).round(), ); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor( _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).round(), ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest((_scissorTest = boolean)); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color = true, depth = true, stencil = true) { let bits = 0; if (color) { // check if we"re trying to clear an integer target let isIntegerFormat = false; if (_currentRenderTarget !== null) { const targetFormat = _currentRenderTarget.texture.format; isIntegerFormat = targetFormat === RGBAIntegerFormat || targetFormat === RGIntegerFormat || targetFormat === RedIntegerFormat; } // use the appropriate clear functions to clear the target if it"s a signed // or unsigned integer target if (isIntegerFormat) { const targetType = _currentRenderTarget.texture.type; const isUnsignedType = targetType === UnsignedByteType || targetType === UnsignedIntType || targetType === UnsignedShortType || targetType === UnsignedInt248Type || targetType === UnsignedShort4444Type || targetType === UnsignedShort5551Type; const clearColor = background.getClearColor(); const a = background.getClearAlpha(); const r = clearColor.r; const g = clearColor.g; const b = clearColor.b; if (isUnsignedType) { uintClearColor[0] = r; uintClearColor[1] = g; uintClearColor[2] = b; uintClearColor[3] = a; _gl.clearBufferuiv(_gl.COLOR, 0, uintClearColor); } else { intClearColor[0] = r; intClearColor[1] = g; intClearColor[2] = b; intClearColor[3] = a; _gl.clearBufferiv(_gl.COLOR, 0, intClearColor); } } else { bits |= _gl.COLOR_BUFFER_BIT; } } if (depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil) { bits |= _gl.STENCIL_BUFFER_BIT; this.state.buffers.stencil.setMask(0xffffffff); } _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { canvas.removeEventListener("webglcontextlost", onContextLost, false); canvas.removeEventListener( "webglcontextrestored", onContextRestore, false, ); canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false, ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function onContextRestore(/* event */) { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError(event) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage, ); } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group, ) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); if (index === undefined) return; rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = (drawRange.start + drawRange.count) * rangeFactor; if (group !== null) { drawStart = Math.max(drawStart, group.start * rangeFactor); drawEnd = Math.min(drawEnd, (group.start + group.count) * rangeFactor); } if (index !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, index.count); } else if (position !== undefined && position !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, position.count); } const drawCount = drawEnd - drawStart; if (drawCount < 0 || drawCount === Infinity) return; // bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio(), ); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isBatchedMesh) { if (object._multiDrawInstances !== null) { renderer.renderMultiDrawInstances( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount, object._multiDrawInstances, ); } else { renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount, ); } } else if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount, ); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile function prepareMaterial(material, scene, object) { if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = FrontSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = DoubleSide; } else { getProgram(material, scene, object); } } this.compile = function (scene, camera, targetScene = null) { if (targetScene === null) targetScene = scene; currentRenderState = renderStates.get(targetScene); currentRenderState.init(camera); renderStateStack.push(currentRenderState); // gather lights from both the target scene and the new object that will be added to the scene. targetScene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); if (scene !== targetScene) { scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); } currentRenderState.setupLights(); // Only initialize materials in the new scene, not the targetScene. const materials = new Set(); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; prepareMaterial(material2, targetScene, object); materials.add(material2); } } else { prepareMaterial(material, targetScene, object); materials.add(material); } } }); renderStateStack.pop(); currentRenderState = null; return materials; }; // compileAsync this.compileAsync = function (scene, camera, targetScene = null) { const materials = this.compile(scene, camera, targetScene); // Wait for all the materials in the new object to indicate that they"re // ready to be used before resolving the promise. return new Promise(resolve => { function checkMaterialsReady() { materials.forEach(function (material) { const materialProperties = properties.get(material); const program = materialProperties.currentProgram; if (program.isReady()) { // remove any programs that report they"re ready to use from the list materials.delete(material); } }); // once the list of compiling materials is empty, call the callback if (materials.size === 0) { resolve(scene); return; } // if some materials are still not ready, wait a bit and check again setTimeout(checkMaterialsReady, 10); } if (extensions.get("KHR_parallel_shader_compile") !== null) { // If we can check the compilation status of the materials without // blocking then do so right away. checkMaterialsReady(); } else { // Otherwise start by waiting a bit to give the materials we just // initialized a chance to finish. setTimeout(checkMaterialsReady, 10); } }); }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof self !== "undefined") animation.setContext(self); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.", ); return; } if (_isContextLost === true) return; // update scene graph if (scene.matrixWorldAutoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null && camera.matrixWorldAutoUpdate === true) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(camera); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse, ); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, ); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); if (xr.enabled === true && xr.isPresenting === true) { const depthSensingMesh = _this.xr.getDepthSensingMesh(); if (depthSensingMesh !== null) { projectObject(depthSensingMesh, camera, -Infinity, _this.sortObjects); } } projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } _renderBackground = xr.enabled === false || xr.isPresenting === false || xr.hasDepthSensing() === false; if (_renderBackground) { background.addToRenderList(currentRenderList, scene); } // this.info.render.frame++; if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // render scene const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; currentRenderState.setupLights(); if (camera.isArrayCamera) { const cameras = camera.cameras; if (transmissiveObjects.length > 0) { for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera2, ); } } if (_renderBackground) background.render(scene); for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { if (transmissiveObjects.length > 0) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera, ); if (_renderBackground) background.render(scene); renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; if (_clippingEnabled === true) clipping.setGlobalState( _this.clippingPlanes, currentRenderState.state.camera, ); } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3 .setFromMatrixPosition(object.matrixWorld) .applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null, ); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (!object.frustumCulled || _frustum.intersectsObject(object)) { const geometry = objects.update(object); const material = object.material; if (sortObjects) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _vector3.copy(object.boundingSphere.center); } else { if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _vector3.copy(geometry.boundingSphere.center); } _vector3 .applyMatrix4(object.matrixWorld) .applyMatrix4(_projScreenMatrix); } if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group, ); } } } else if (material.visible) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null, ); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (_clippingEnabled === true) clipping.setGlobalState(_this.clippingPlanes, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); } function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera, ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; if (overrideMaterial !== null) { return; } if ( currentRenderState.state.transmissionRenderTarget[camera.id] === undefined ) { currentRenderState.state.transmissionRenderTarget[camera.id] = new WebGLRenderTarget(1, 1, { generateMipmaps: true, type: extensions.has("EXT_color_buffer_half_float") || extensions.has("EXT_color_buffer_float") ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: 4, stencilBuffer: stencil, resolveDepthBuffer: false, resolveStencilBuffer: false, colorSpace: ColorManagement.workingColorSpace, }); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } const transmissionRenderTarget = currentRenderState.state.transmissionRenderTarget[camera.id]; const activeViewport = camera.viewport || _currentViewport; transmissionRenderTarget.setSize(activeViewport.z, activeViewport.w); // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(transmissionRenderTarget); _this.getClearColor(_currentClearColor); _currentClearAlpha = _this.getClearAlpha(); if (_currentClearAlpha < 1) _this.setClearColor(0xffffff, 0.5); if (_renderBackground) { background.render(scene); } else { _this.clear(); } // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; // Remove viewport from camera to avoid nested render calls resetting viewport to it (e.g Reflector). // Transmission render pass requires viewport to match the transmissionRenderTarget. const currentCameraViewport = camera.viewport; if (camera.viewport !== undefined) camera.viewport = undefined; currentRenderState.setupLightsView(camera); if (_clippingEnabled === true) clipping.setGlobalState(_this.clippingPlanes, camera); renderObjects(opaqueObjects, scene, camera); textures.updateMultisampleRenderTarget(transmissionRenderTarget); textures.updateRenderTargetMipmap(transmissionRenderTarget); if (extensions.has("WEBGL_multisampled_render_to_texture") === false) { // see #28131 let renderTargetNeedsUpdate = false; for (let i = 0, l = transmissiveObjects.length; i < l; i++) { const renderItem = transmissiveObjects[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if ( material.side === DoubleSide && object.layers.test(camera.layers) ) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject(object, scene, camera, geometry, material, group); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if (renderTargetNeedsUpdate === true) { textures.updateMultisampleRenderTarget(transmissionRenderTarget); textures.updateRenderTargetMipmap(transmissionRenderTarget); } } _this.setRenderTarget(currentRenderTarget); _this.setClearColor(_currentClearColor, _currentClearAlpha); if (currentCameraViewport !== undefined) camera.viewport = currentCameraViewport; _this.toneMapping = currentToneMapping; } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld, ); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object, ); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || materialProperties.environment); materialProperties.envMapRotation = materialProperties.environment !== null && material.envMap === null ? scene.environmentRotation : material.envMapRotation; if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( (!material.isShaderMaterial && !material.isRawShaderMaterial) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } materialProperties.currentProgram = program; materialProperties.uniformsList = null; return program; } function getUniformList(materialProperties) { if (materialProperties.uniformsList === null) { const progUniforms = materialProperties.currentProgram.getUniforms(); materialProperties.uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, materialProperties.uniforms, ); } return materialProperties.uniformsList; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.batching = parameters.batching; materialProperties.batchingColor = parameters.batchingColor; materialProperties.instancing = parameters.instancing; materialProperties.instancingColor = parameters.instancingColor; materialProperties.instancingMorph = parameters.instancingMorph; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = _currentRenderTarget === null ? _this.outputColorSpace : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!geometry.attributes.tangent && (!!material.normalMap || material.anisotropy > 0); const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphColors = !!geometry.morphAttributes.color; let toneMapping = NoToneMapping; if (material.toneMapped) { if ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) { toneMapping = _this.toneMapping; } } const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if ( materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version ) { needsProgramChange = true; } else if (materialProperties.outputColorSpace !== colorSpace) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batching === false ) { needsProgramChange = true; } else if ( !object.isBatchedMesh && materialProperties.batching === true ) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batchingColor === true && object.colorTexture === null ) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batchingColor === false && object.colorTexture !== null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( !object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( !object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingMorph === true && object.morphTexture === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingMorph === false && object.morphTexture !== null ) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog === true && materialProperties.fog !== fog) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection) ) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.morphColors !== morphColors) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { // common camera uniforms p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition(camera.matrixWorld), ); } if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2), ); } // consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067 if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true, ); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures, ); } } if (object.isBatchedMesh) { p_uniforms.setOptional(_gl, object, "batchingTexture"); p_uniforms.setValue( _gl, "batchingTexture", object._matricesTexture, textures, ); p_uniforms.setOptional(_gl, object, "batchingColorTexture"); if (object._colorsTexture !== null) { p_uniforms.setValue( _gl, "batchingColorTexture", object._colorsTexture, textures, ); } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || morphAttributes.color !== undefined ) { morphtargets.update(object, geometry, program); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if (material.isMeshGouraudMaterial && material.envMap !== null) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; } if ( material.isMeshStandardMaterial && material.envMap === null && scene.environment !== null ) { m_uniforms.envMapIntensity.value = scene.environmentIntensity; } if (refreshMaterial) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure, ); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog === true) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, currentRenderState.state.transmissionRenderTarget[camera.id], ); WebGLUniforms.upload( _gl, getUniformList(materialProperties), m_uniforms, textures, ); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload( _gl, getUniformList(materialProperties), m_uniforms, textures, ); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); // UBOs if (material.isShaderMaterial || material.isRawShaderMaterial) { const groups = material.uniformsGroups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; uniformsGroups.update(group, program); uniformsGroups.bind(group, program); } } return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return ( material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || (material.isShaderMaterial && material.lights === true) ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture, ) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (extensions.has("WEBGL_multisampled_render_to_texture") === true) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided", ); renderTargetProperties.__useRenderToTexture = false; } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer, ) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0, ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture, ); } const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { if (Array.isArray(__webglFramebuffer[activeCubeFace])) { framebuffer = __webglFramebuffer[activeCubeFace][activeMipmapLevel]; } else { framebuffer = __webglFramebuffer[activeCubeFace]; } isCube = true; } else if ( renderTarget.samples > 0 && textures.useMultisampledRTT(renderTarget) === false ) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { if (Array.isArray(__webglFramebuffer)) { framebuffer = __webglFramebuffer[activeMipmapLevel]; } else { framebuffer = __webglFramebuffer; } } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer, ); if (framebufferBound && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel, ); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer, ); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, ) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.", ); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (!capabilities.textureFormatReadable(textureFormat)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.", ); return; } if (!capabilities.textureTypeReadable(textureType)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.", ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height ) { _gl.readPixels( x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer, ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, ) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.", ); } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (!capabilities.textureFormatReadable(textureFormat)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.", ); } if (!capabilities.textureTypeReadable(textureType)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.", ); } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height ) { const glBuffer = _gl.createBuffer(); _gl.bindBuffer(_gl.PIXEL_PACK_BUFFER, glBuffer); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ, ); _gl.readPixels( x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), 0, ); _gl.flush(); // check if the commands have finished every 8 ms const sync = _gl.fenceSync(_gl.SYNC_GPU_COMMANDS_COMPLETE, 0); await probeAsync(_gl, sync, 4); try { _gl.bindBuffer(_gl.PIXEL_PACK_BUFFER, glBuffer); _gl.getBufferSubData(_gl.PIXEL_PACK_BUFFER, 0, buffer); } finally { _gl.deleteBuffer(glBuffer); _gl.deleteSync(sync); } return buffer; } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function ( texture, position = null, level = 0, ) { // support previous signature with position first if (texture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyFramebufferToTexture function signature has changed.", ); position = arguments[0] || null; texture = arguments[1]; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); const x = position !== null ? position.x : 0; const y = position !== null ? position.y : 0; textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, x, y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0, ) { // support previous signature with dstPosition first if (srcTexture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyTextureToTexture function signature has changed.", ); dstPosition = arguments[0] || null; srcTexture = arguments[1]; dstTexture = arguments[2]; level = arguments[3] || 0; srcRegion = null; } let width, height, minX, minY; let dstX, dstY; if (srcRegion !== null) { width = srcRegion.max.x - srcRegion.min.x; height = srcRegion.max.y - srcRegion.min.y; minX = srcRegion.min.x; minY = srcRegion.min.y; } else { width = srcTexture.image.width; height = srcTexture.image.height; minX = 0; minY = 0; } if (dstPosition !== null) { dstX = dstPosition.x; dstY = dstPosition.y; } else { dstX = 0; dstY = 0; } const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const currentUnpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT, ); const currentUnpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const currentUnpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const currentUnpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[level] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, minX); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, minY); if (srcTexture.isDataTexture) { _gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data, ); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data, ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, glFormat, glType, image, ); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, currentUnpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0, ) { // support previous signature with source box first if (srcTexture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyTextureToTexture3D function signature has changed.", ); srcRegion = arguments[0] || null; dstPosition = arguments[1] || null; srcTexture = arguments[2]; dstTexture = arguments[3]; level = arguments[4] || 0; } let width, height, depth, minX, minY, minZ; let dstX, dstY, dstZ; const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[level] : srcTexture.image; if (srcRegion !== null) { width = srcRegion.max.x - srcRegion.min.x; height = srcRegion.max.y - srcRegion.min.y; depth = srcRegion.max.z - srcRegion.min.z; minX = srcRegion.min.x; minY = srcRegion.min.y; minZ = srcRegion.min.z; } else { width = image.width; height = image.height; depth = image.depth; minX = 0; minY = 0; minZ = 0; } if (dstPosition !== null) { dstX = dstPosition.x; dstY = dstPosition.y; dstZ = dstPosition.z; } else { dstX = 0; dstY = 0; dstZ = 0; } const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isData3DTexture) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if ( dstTexture.isDataArrayTexture || dstTexture.isCompressedArrayTexture ) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.", ); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const currentUnpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT, ); const currentUnpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const currentUnpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const currentUnpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, minX); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, minY); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, minZ); if (srcTexture.isDataTexture || srcTexture.isData3DTexture) { _gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data, ); } else { if (dstTexture.isCompressedArrayTexture) { _gl.compressedTexSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, image.data, ); } else { _gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image, ); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, currentUnpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initRenderTarget = function (target) { if (properties.get(target).__webglFramebuffer === undefined) { textures.setupRenderTarget(target); } }; this.initTexture = function (texture) { if (texture.isCubeTexture) { textures.setTextureCube(texture, 0); } else if (texture.isData3DTexture) { textures.setTexture3D(texture, 0); } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { textures.setTexture2DArray(texture, 0); } else { textures.setTexture2D(texture, 0); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("observe", {detail: this}), ); } } get coordinateSystem() { return WebGLCoordinateSystem; } get outputColorSpace() { return this._outputColorSpace; } set outputColorSpace(colorSpace) { this._outputColorSpace = colorSpace; const gl = this.getContext(); gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? "display-p3" : "srgb"; gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? "display-p3" : "srgb"; } } class FogExp2 { constructor(color, density = 0.00025) { this.isFogExp2 = true; this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON(/* meta */) { return { type: "FogExp2", name: this.name, color: this.color.getHex(), density: this.density, }; } } class Fog { constructor(color, near = 1, far = 1000) { this.isFog = true; this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON(/* meta */) { return { type: "Fog", name: this.name, color: this.color.getHex(), near: this.near, far: this.far, }; } } class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.backgroundRotation = new Euler(); this.environmentIntensity = 1; this.environmentRotation = new Euler(); this.overrideMaterial = null; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("observe", {detail: this}), ); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; this.backgroundRotation.copy(source.backgroundRotation); this.environmentIntensity = source.environmentIntensity; this.environmentRotation.copy(source.environmentRotation); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); if (this.backgroundBlurriness > 0) data.object.backgroundBlurriness = this.backgroundBlurriness; if (this.backgroundIntensity !== 1) data.object.backgroundIntensity = this.backgroundIntensity; data.object.backgroundRotation = this.backgroundRotation.toArray(); if (this.environmentIntensity !== 1) data.object.environmentIntensity = this.environmentIntensity; data.object.environmentRotation = this.environmentRotation.toArray(); return data; } } class InterleavedBuffer { constructor(array, stride) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this._updateRange = {offset: 0, count: -1}; this.updateRanges = []; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } get updateRange() { warnOnce( "THREE.InterleavedBuffer: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.", ); // @deprecated, r159 return this._updateRange; } setUsage(value) { this.usage = value; return this; } addUpdateRange(start, count) { this.updateRanges.push({start, count}); } clearUpdateRanges() { this.updateRanges.length = 0; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor( data.arrayBuffers[this.array.buffer._uuid], ); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.from( new Uint32Array(this.array.buffer), ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride, }; } } const _vector$6 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } getComponent(index, component) { let value = this.array[index * this.data.stride + this.offset + component]; if (this.normalized) value = denormalize(value, this.array); return value; } setComponent(index, component, value) { if (this.normalized) value = normalize(value, this.array); this.data.array[index * this.data.stride + this.offset + component] = value; return this; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { let x = this.data.array[index * this.data.stride + this.offset]; if (this.normalized) x = denormalize(x, this.array); return x; } getY(index) { let y = this.data.array[index * this.data.stride + this.offset + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } getZ(index) { let z = this.data.array[index * this.data.stride + this.offset + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } getW(index) { let w = this.data.array[index * this.data.stride + this.offset + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setXY(index, x, y) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.", ); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute( new this.array.constructor(array), this.itemSize, this.normalized, ); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute( data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized, ); } } toJSON(data) { if (data === undefined) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.", ); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized, }; } else { // save as true interleaved attribute if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized, }; } } } class SpriteMaterial extends Material { constructor(parameters) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor(material = new SpriteMaterial()) { super(); this.isSprite = true; this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([ -0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1, ]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute( "position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false), ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false), ); } this.geometry = _geometry; this.material = material; this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.", ); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld, ); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex( _vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); transformVertex( _vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); transformVertex( _vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint, ); if (intersect === null) { // check second triangle transformVertex( _vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint, ); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2(), ), face: null, object: this, }); } copy(source, recursive) { super.copy(source, recursive); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition .subVectors(vertexPosition, center) .addScalar(0.5) .multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/ new Vector3(); const _v2$1 = /*@__PURE__*/ new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [], }, isLOD: { value: true, }, }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance, level.hysteresis); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0, hysteresis = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, hysteresis: hysteresis, object: object, }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance < levelDistance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance >= levelDistance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance, hysteresis: level.hysteresis, }); } return data; } } const _basePosition = /*@__PURE__*/ new Vector3(); const _skinIndex = /*@__PURE__*/ new Vector4(); const _skinWeight = /*@__PURE__*/ new Vector4(); const _vector3 = /*@__PURE__*/ new Vector3(); const _matrix4 = /*@__PURE__*/ new Matrix4(); const _vertex = /*@__PURE__*/ new Vector3(); const _sphere$4 = /*@__PURE__*/ new Sphere(); const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.isSkinnedMesh = true; this.type = "SkinnedMesh"; this.bindMode = AttachedBindMode; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if (this.boundingBox === null) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute("position"); for (let i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingBox.expandByPoint(_vertex); } } computeBoundingSphere() { const geometry = this.geometry; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute("position"); for (let i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingSphere.expandByPoint(_vertex); } } copy(source, recursive) { super.copy(source, recursive); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast(raycaster, intersects) { const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$4.copy(this.boundingSphere); _sphere$4.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$4) === false) return; // convert ray to local space of skinned mesh _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // test with bounding box in local space if (this.boundingBox !== null) { if (_ray$2.intersectsBox(this.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$2); } getVertexPosition(index, target) { super.getVertexPosition(index, target); this.applyBoneTransform(index, target); return target; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.fromBufferAttribute(skinWeight, i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === AttachedBindMode) { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === DetachedBindMode) { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode, ); } } applyBoneTransform(index, vector) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(vector).applyMatrix4(this.bindMatrix); vector.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix4.multiplyMatrices( skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex], ); vector.addScaledVector( _vector3.copy(_basePosition).applyMatrix4(_matrix4), weight, ); } } return vector.applyMatrix4(this.bindMatrixInverse); } } class Bone extends Object3D { constructor() { super(); this.isBone = true; this.type = "Bone"; } } class DataTexture extends Texture { constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace, ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isDataTexture = true; this.image = {data: data, width: width, height: height}; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } const _offsetMatrix = /*@__PURE__*/ new Matrix4(); const _identityMatrix$1 = /*@__PURE__*/ new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn( "THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.", ); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix$1; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = Math.ceil(size / 4) * 4; size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType, ); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Skeleton", generator: "Skeleton.toJSON", }, bones: [], boneInverses: [], }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { super(array, itemSize, normalized); this.isInstancedBufferAttribute = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); const _instanceIntersects = []; const _box3 = /*@__PURE__*/ new Box3(); const _identity = /*@__PURE__*/ new Matrix4(); const _mesh$1 = /*@__PURE__*/ new Mesh(); const _sphere$3 = /*@__PURE__*/ new Sphere(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.isInstancedMesh = true; this.instanceMatrix = new InstancedBufferAttribute( new Float32Array(count * 16), 16, ); this.instanceColor = null; this.morphTexture = null; this.count = count; this.boundingBox = null; this.boundingSphere = null; for (let i = 0; i < count; i++) { this.setMatrixAt(i, _identity); } } computeBoundingBox() { const geometry = this.geometry; const count = this.count; if (this.boundingBox === null) { this.boundingBox = new Box3(); } if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } this.boundingBox.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _box3.copy(geometry.boundingBox).applyMatrix4(_instanceLocalMatrix); this.boundingBox.union(_box3); } } computeBoundingSphere() { const geometry = this.geometry; const count = this.count; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } if (geometry.boundingSphere === null) { geometry.computeBoundingSphere(); } this.boundingSphere.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _sphere$3 .copy(geometry.boundingSphere) .applyMatrix4(_instanceLocalMatrix); this.boundingSphere.union(_sphere$3); } } copy(source, recursive) { super.copy(source, recursive); this.instanceMatrix.copy(source.instanceMatrix); if (source.morphTexture !== null) this.morphTexture = source.morphTexture.clone(); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } getMorphAt(index, object) { const objectInfluences = object.morphTargetInfluences; const array = this.morphTexture.source.data.data; const len = objectInfluences.length + 1; // All influences + the baseInfluenceSum const dataIndex = index * len + 1; // Skip the baseInfluenceSum at the beginning for (let i = 0; i < objectInfluences.length; i++) { objectInfluences[i] = array[dataIndex + i]; } } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh$1.geometry = this.geometry; _mesh$1.material = this.material; if (_mesh$1.material === undefined) return; // test with bounding sphere first if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$3.copy(this.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // now test each instance for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh$1.matrixWorld = _instanceWorldMatrix; _mesh$1.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute( new Float32Array(this.instanceMatrix.count * 3), 3, ); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } setMorphAt(index, object) { const objectInfluences = object.morphTargetInfluences; const len = objectInfluences.length + 1; // morphBaseInfluence + all influences if (this.morphTexture === null) { this.morphTexture = new DataTexture( new Float32Array(len * this.count), len, this.count, RedFormat, FloatType, ); } const array = this.morphTexture.source.data.data; let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = this.geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; const dataIndex = len * index; array[dataIndex] = morphBaseInfluence; array.set(objectInfluences, dataIndex + 1); } updateMorphTargets() {} dispose() { this.dispatchEvent({type: "dispose"}); if (this.morphTexture !== null) { this.morphTexture.dispose(); this.morphTexture = null; } return this; } } function sortOpaque(a, b) { return a.z - b.z; } function sortTransparent(a, b) { return b.z - a.z; } class MultiDrawRenderList { constructor() { this.index = 0; this.pool = []; this.list = []; } push(drawRange, z) { const pool = this.pool; const list = this.list; if (this.index >= pool.length) { pool.push({ start: -1, count: -1, z: -1, }); } const item = pool[this.index]; list.push(item); this.index++; item.start = drawRange.start; item.count = drawRange.count; item.z = z; } reset() { this.list.length = 0; this.index = 0; } } const ID_ATTR_NAME = "batchId"; const _matrix$1 = /*@__PURE__*/ new Matrix4(); const _invMatrixWorld = /*@__PURE__*/ new Matrix4(); const _identityMatrix = /*@__PURE__*/ new Matrix4(); const _whiteColor = /*@__PURE__*/ new Color(1, 1, 1); const _projScreenMatrix$2 = /*@__PURE__*/ new Matrix4(); const _frustum = /*@__PURE__*/ new Frustum(); const _box$1 = /*@__PURE__*/ new Box3(); const _sphere$2 = /*@__PURE__*/ new Sphere(); const _vector$5 = /*@__PURE__*/ new Vector3(); const _forward = /*@__PURE__*/ new Vector3(); const _temp = /*@__PURE__*/ new Vector3(); const _renderList = /*@__PURE__*/ new MultiDrawRenderList(); const _mesh = /*@__PURE__*/ new Mesh(); const _batchIntersects = []; // @TODO: SkinnedMesh support? // @TODO: geometry.groups support? // @TODO: geometry.drawRange support? // @TODO: geometry.morphAttributes support? // @TODO: Support uniform parameter per geometry // @TODO: Add an "optimize" function to pack geometry and remove data gaps // copies data from attribute "src" into "target" starting at "targetOffset" function copyAttributeData(src, target, targetOffset = 0) { const itemSize = target.itemSize; if ( src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor ) { // use the component getters and setters if the array data cannot // be copied directly const vertexCount = src.count; for (let i = 0; i < vertexCount; i++) { for (let c = 0; c < itemSize; c++) { target.setComponent(i + targetOffset, c, src.getComponent(i, c)); } } } else { // faster copy approach using typed array set function target.array.set(src.array, targetOffset * itemSize); } target.needsUpdate = true; } class BatchedMesh extends Mesh { get maxGeometryCount() { return this._maxGeometryCount; } constructor( maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material, ) { super(new BufferGeometry(), material); this.isBatchedMesh = true; this.perObjectFrustumCulled = true; this.sortObjects = true; this.boundingBox = null; this.boundingSphere = null; this.customSort = null; this._drawRanges = []; this._reservedRanges = []; this._visibility = []; this._active = []; this._bounds = []; this._maxGeometryCount = maxGeometryCount; this._maxVertexCount = maxVertexCount; this._maxIndexCount = maxIndexCount; this._geometryInitialized = false; this._geometryCount = 0; this._multiDrawCounts = new Int32Array(maxGeometryCount); this._multiDrawStarts = new Int32Array(maxGeometryCount); this._multiDrawCount = 0; this._multiDrawInstances = null; this._visibilityChanged = true; // Local matrix per geometry by using data texture this._matricesTexture = null; this._initMatricesTexture(); // Local color per geometry by using data texture this._colorsTexture = null; } _initMatricesTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 matrices * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 matrices * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64) let size = Math.sqrt(this._maxGeometryCount * 4); // 4 pixels needed for 1 matrix size = Math.ceil(size / 4) * 4; size = Math.max(size, 4); const matricesArray = new Float32Array(size * size * 4); // 4 floats per RGBA pixel const matricesTexture = new DataTexture( matricesArray, size, size, RGBAFormat, FloatType, ); this._matricesTexture = matricesTexture; } _initColorsTexture() { let size = Math.sqrt(this._maxGeometryCount); size = Math.ceil(size); // 4 floats per RGBA pixel initialized to white const colorsArray = new Float32Array(size * size * 4).fill(1); const colorsTexture = new DataTexture( colorsArray, size, size, RGBAFormat, FloatType, ); colorsTexture.colorSpace = ColorManagement.workingColorSpace; this._colorsTexture = colorsTexture; } _initializeGeometry(reference) { const geometry = this.geometry; const maxVertexCount = this._maxVertexCount; const maxGeometryCount = this._maxGeometryCount; const maxIndexCount = this._maxIndexCount; if (this._geometryInitialized === false) { for (const attributeName in reference.attributes) { const srcAttribute = reference.getAttribute(attributeName); const {array, itemSize, normalized} = srcAttribute; const dstArray = new array.constructor(maxVertexCount * itemSize); const dstAttribute = new BufferAttribute( dstArray, itemSize, normalized, ); geometry.setAttribute(attributeName, dstAttribute); } if (reference.getIndex() !== null) { const indexArray = maxVertexCount > 65536 ? new Uint32Array(maxIndexCount) : new Uint16Array(maxIndexCount); geometry.setIndex(new BufferAttribute(indexArray, 1)); } const idArray = maxGeometryCount > 65536 ? new Uint32Array(maxVertexCount) : new Uint16Array(maxVertexCount); geometry.setAttribute(ID_ATTR_NAME, new BufferAttribute(idArray, 1)); this._geometryInitialized = true; } } // Make sure the geometry is compatible with the existing combined geometry attributes _validateGeometry(geometry) { // check that the geometry doesn"t have a version of our reserved id attribute if (geometry.getAttribute(ID_ATTR_NAME)) { throw new Error( `BatchedMesh: Geometry cannot use attribute "${ID_ATTR_NAME}"`, ); } // check to ensure the geometries are using consistent attributes and indices const batchGeometry = this.geometry; if (Boolean(geometry.getIndex()) !== Boolean(batchGeometry.getIndex())) { throw new Error( "BatchedMesh: All geometries must consistently have "index".", ); } for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } if (!geometry.hasAttribute(attributeName)) { throw new Error( `BatchedMesh: Added geometry missing "${attributeName}". All geometries must have consistent attributes.`, ); } const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) { throw new Error( "BatchedMesh: All attributes must have a consistent itemSize and normalized value.", ); } } } setCustomSort(func) { this.customSort = func; return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const geometryCount = this._geometryCount; const boundingBox = this.boundingBox; const active = this._active; boundingBox.makeEmpty(); for (let i = 0; i < geometryCount; i++) { if (active[i] === false) continue; this.getMatrixAt(i, _matrix$1); this.getBoundingBoxAt(i, _box$1).applyMatrix4(_matrix$1); boundingBox.union(_box$1); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const geometryCount = this._geometryCount; const boundingSphere = this.boundingSphere; const active = this._active; boundingSphere.makeEmpty(); for (let i = 0; i < geometryCount; i++) { if (active[i] === false) continue; this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); boundingSphere.union(_sphere$2); } } addGeometry(geometry, vertexCount = -1, indexCount = -1) { this._initializeGeometry(geometry); this._validateGeometry(geometry); // ensure we"re not over geometry if (this._geometryCount >= this._maxGeometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } // get the necessary range fo the geometry const reservedRange = { vertexStart: -1, vertexCount: -1, indexStart: -1, indexCount: -1, }; let lastRange = null; const reservedRanges = this._reservedRanges; const drawRanges = this._drawRanges; const bounds = this._bounds; if (this._geometryCount !== 0) { lastRange = reservedRanges[reservedRanges.length - 1]; } if (vertexCount === -1) { reservedRange.vertexCount = geometry.getAttribute("position").count; } else { reservedRange.vertexCount = vertexCount; } if (lastRange === null) { reservedRange.vertexStart = 0; } else { reservedRange.vertexStart = lastRange.vertexStart + lastRange.vertexCount; } const index = geometry.getIndex(); const hasIndex = index !== null; if (hasIndex) { if (indexCount === -1) { reservedRange.indexCount = index.count; } else { reservedRange.indexCount = indexCount; } if (lastRange === null) { reservedRange.indexStart = 0; } else { reservedRange.indexStart = lastRange.indexStart + lastRange.indexCount; } } if ( (reservedRange.indexStart !== -1 && reservedRange.indexStart + reservedRange.indexCount > this._maxIndexCount) || reservedRange.vertexStart + reservedRange.vertexCount > this._maxVertexCount ) { throw new Error( "BatchedMesh: Reserved space request exceeds the maximum buffer size.", ); } const visibility = this._visibility; const active = this._active; const matricesTexture = this._matricesTexture; const matricesArray = this._matricesTexture.image.data; const colorsTexture = this._colorsTexture; // push new visibility states visibility.push(true); active.push(true); // update id const geometryId = this._geometryCount; this._geometryCount++; // initialize matrix information _identityMatrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; // initialize the color to white if (colorsTexture !== null) { _whiteColor.toArray(colorsTexture.image.data, geometryId * 4); colorsTexture.needsUpdate = true; } // add the reserved range and draw range objects reservedRanges.push(reservedRange); drawRanges.push({ start: hasIndex ? reservedRange.indexStart : reservedRange.vertexStart, count: -1, }); bounds.push({ boxInitialized: false, box: new Box3(), sphereInitialized: false, sphere: new Sphere(), }); // set the id for the geometry const idAttribute = this.geometry.getAttribute(ID_ATTR_NAME); for (let i = 0; i < reservedRange.vertexCount; i++) { idAttribute.setX(reservedRange.vertexStart + i, geometryId); } idAttribute.needsUpdate = true; // update the geometry this.setGeometryAt(geometryId, geometry); return geometryId; } setGeometryAt(id, geometry) { if (id >= this._geometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } this._validateGeometry(geometry); const batchGeometry = this.geometry; const hasIndex = batchGeometry.getIndex() !== null; const dstIndex = batchGeometry.getIndex(); const srcIndex = geometry.getIndex(); const reservedRange = this._reservedRanges[id]; if ( (hasIndex && srcIndex.count > reservedRange.indexCount) || geometry.attributes.position.count > reservedRange.vertexCount ) { throw new Error( "BatchedMesh: Reserved space not large enough for provided geometry.", ); } // copy geometry over const vertexStart = reservedRange.vertexStart; const vertexCount = reservedRange.vertexCount; for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } // copy attribute data const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); copyAttributeData(srcAttribute, dstAttribute, vertexStart); // fill the rest in with zeroes const itemSize = srcAttribute.itemSize; for (let i = srcAttribute.count, l = vertexCount; i < l; i++) { const index = vertexStart + i; for (let c = 0; c < itemSize; c++) { dstAttribute.setComponent(index, c, 0); } } dstAttribute.needsUpdate = true; dstAttribute.addUpdateRange( vertexStart * itemSize, vertexCount * itemSize, ); } // copy index if (hasIndex) { const indexStart = reservedRange.indexStart; // copy index data over for (let i = 0; i < srcIndex.count; i++) { dstIndex.setX(indexStart + i, vertexStart + srcIndex.getX(i)); } // fill the rest in with zeroes for (let i = srcIndex.count, l = reservedRange.indexCount; i < l; i++) { dstIndex.setX(indexStart + i, vertexStart); } dstIndex.needsUpdate = true; dstIndex.addUpdateRange(indexStart, reservedRange.indexCount); } // store the bounding boxes const bound = this._bounds[id]; if (geometry.boundingBox !== null) { bound.box.copy(geometry.boundingBox); bound.boxInitialized = true; } else { bound.boxInitialized = false; } if (geometry.boundingSphere !== null) { bound.sphere.copy(geometry.boundingSphere); bound.sphereInitialized = true; } else { bound.sphereInitialized = false; } // set drawRange count const drawRange = this._drawRanges[id]; const posAttr = geometry.getAttribute("position"); drawRange.count = hasIndex ? srcIndex.count : posAttr.count; this._visibilityChanged = true; return id; } deleteGeometry(geometryId) { // Note: User needs to call optimize() afterward to pack the data. const active = this._active; if (geometryId >= active.length || active[geometryId] === false) { return this; } active[geometryId] = false; this._visibilityChanged = true; return this; } getInstanceCountAt(id) { if (this._multiDrawInstances === null) return null; return this._multiDrawInstances[id]; } setInstanceCountAt(id, instanceCount) { if (this._multiDrawInstances === null) { this._multiDrawInstances = new Int32Array(this._maxGeometryCount).fill(1); } this._multiDrawInstances[id] = instanceCount; return id; } // get bounding box and compute it if it doesn"t exist getBoundingBoxAt(id, target) { const active = this._active; if (active[id] === false) { return null; } // compute bounding box const bound = this._bounds[id]; const box = bound.box; const geometry = this.geometry; if (bound.boxInitialized === false) { box.makeEmpty(); const index = geometry.index; const position = geometry.attributes.position; const drawRange = this._drawRanges[id]; for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i++ ) { let iv = i; if (index) { iv = index.getX(iv); } box.expandByPoint(_vector$5.fromBufferAttribute(position, iv)); } bound.boxInitialized = true; } target.copy(box); return target; } // get bounding sphere and compute it if it doesn"t exist getBoundingSphereAt(id, target) { const active = this._active; if (active[id] === false) { return null; } // compute bounding sphere const bound = this._bounds[id]; const sphere = bound.sphere; const geometry = this.geometry; if (bound.sphereInitialized === false) { sphere.makeEmpty(); this.getBoundingBoxAt(id, _box$1); _box$1.getCenter(sphere.center); const index = geometry.index; const position = geometry.attributes.position; const drawRange = this._drawRanges[id]; let maxRadiusSq = 0; for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i++ ) { let iv = i; if (index) { iv = index.getX(iv); } _vector$5.fromBufferAttribute(position, iv); maxRadiusSq = Math.max( maxRadiusSq, sphere.center.distanceToSquared(_vector$5), ); } sphere.radius = Math.sqrt(maxRadiusSq); bound.sphereInitialized = true; } target.copy(sphere); return target; } setMatrixAt(geometryId, matrix) { // @TODO: Map geometryId to index of the arrays because // optimize() can make geometryId mismatch the index const active = this._active; const matricesTexture = this._matricesTexture; const matricesArray = this._matricesTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return this; } matrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; return this; } getMatrixAt(geometryId, matrix) { const active = this._active; const matricesArray = this._matricesTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return null; } return matrix.fromArray(matricesArray, geometryId * 16); } setColorAt(geometryId, color) { if (this._colorsTexture === null) { this._initColorsTexture(); } // @TODO: Map geometryId to index of the arrays because // optimize() can make geometryId mismatch the index const active = this._active; const colorsTexture = this._colorsTexture; const colorsArray = this._colorsTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return this; } color.toArray(colorsArray, geometryId * 4); colorsTexture.needsUpdate = true; return this; } getColorAt(geometryId, color) { const active = this._active; const colorsArray = this._colorsTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return null; } return color.fromArray(colorsArray, geometryId * 4); } setVisibleAt(geometryId, value) { const visibility = this._visibility; const active = this._active; const geometryCount = this._geometryCount; // if the geometry is out of range, not active, or visibility state // does not change then return early if ( geometryId >= geometryCount || active[geometryId] === false || visibility[geometryId] === value ) { return this; } visibility[geometryId] = value; this._visibilityChanged = true; return this; } getVisibleAt(geometryId) { const visibility = this._visibility; const active = this._active; const geometryCount = this._geometryCount; // return early if the geometry is out of range or not active if (geometryId >= geometryCount || active[geometryId] === false) { return false; } return visibility[geometryId]; } raycast(raycaster, intersects) { const visibility = this._visibility; const active = this._active; const drawRanges = this._drawRanges; const geometryCount = this._geometryCount; const matrixWorld = this.matrixWorld; const batchGeometry = this.geometry; // iterate over each geometry _mesh.material = this.material; _mesh.geometry.index = batchGeometry.index; _mesh.geometry.attributes = batchGeometry.attributes; if (_mesh.geometry.boundingBox === null) { _mesh.geometry.boundingBox = new Box3(); } if (_mesh.geometry.boundingSphere === null) { _mesh.geometry.boundingSphere = new Sphere(); } for (let i = 0; i < geometryCount; i++) { if (!visibility[i] || !active[i]) { continue; } const drawRange = drawRanges[i]; _mesh.geometry.setDrawRange(drawRange.start, drawRange.count); // ge the intersects this.getMatrixAt(i, _mesh.matrixWorld).premultiply(matrixWorld); this.getBoundingBoxAt(i, _mesh.geometry.boundingBox); this.getBoundingSphereAt(i, _mesh.geometry.boundingSphere); _mesh.raycast(raycaster, _batchIntersects); // add batch id to the intersects for (let j = 0, l = _batchIntersects.length; j < l; j++) { const intersect = _batchIntersects[j]; intersect.object = this; intersect.batchId = i; intersects.push(intersect); } _batchIntersects.length = 0; } _mesh.material = null; _mesh.geometry.index = null; _mesh.geometry.attributes = {}; _mesh.geometry.setDrawRange(0, Infinity); } copy(source) { super.copy(source); this.geometry = source.geometry.clone(); this.perObjectFrustumCulled = source.perObjectFrustumCulled; this.sortObjects = source.sortObjects; this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null; this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null; this._drawRanges = source._drawRanges.map(range => ({...range})); this._reservedRanges = source._reservedRanges.map(range => ({...range})); this._visibility = source._visibility.slice(); this._active = source._active.slice(); this._bounds = source._bounds.map(bound => ({ boxInitialized: bound.boxInitialized, box: bound.box.clone(), sphereInitialized: bound.sphereInitialized, sphere: bound.sphere.clone(), })); this._maxGeometryCount = source._maxGeometryCount; this._maxVertexCount = source._maxVertexCount; this._maxIndexCount = source._maxIndexCount; this._geometryInitialized = source._geometryInitialized; this._geometryCount = source._geometryCount; this._multiDrawCounts = source._multiDrawCounts.slice(); this._multiDrawStarts = source._multiDrawStarts.slice(); this._matricesTexture = source._matricesTexture.clone(); this._matricesTexture.image.data = this._matricesTexture.image.slice(); if (this._colorsTexture !== null) { this._colorsTexture = source._colorsTexture.clone(); this._colorsTexture.image.data = this._colorsTexture.image.slice(); } return this; } dispose() { // Assuming the geometry is not shared with other meshes this.geometry.dispose(); this._matricesTexture.dispose(); this._matricesTexture = null; if (this._colorsTexture !== null) { this._colorsTexture.dispose(); this._colorsTexture = null; } return this; } onBeforeRender(renderer, scene, camera, geometry, material /*, _group*/) { // if visibility has not changed and frustum culling and object sorting is not required // then skip iterating over all items if ( !this._visibilityChanged && !this.perObjectFrustumCulled && !this.sortObjects ) { return; } // the indexed version of the multi draw function requires specifying the start // offset in bytes. const index = geometry.getIndex(); const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT; const active = this._active; const visibility = this._visibility; const multiDrawStarts = this._multiDrawStarts; const multiDrawCounts = this._multiDrawCounts; const drawRanges = this._drawRanges; const perObjectFrustumCulled = this.perObjectFrustumCulled; // prepare the frustum in the local frame if (perObjectFrustumCulled) { _projScreenMatrix$2 .multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse) .multiply(this.matrixWorld); _frustum.setFromProjectionMatrix( _projScreenMatrix$2, renderer.coordinateSystem, ); } let count = 0; if (this.sortObjects) { // get the camera position in the local frame _invMatrixWorld.copy(this.matrixWorld).invert(); _vector$5 .setFromMatrixPosition(camera.matrixWorld) .applyMatrix4(_invMatrixWorld); _forward .set(0, 0, -1) .transformDirection(camera.matrixWorld) .transformDirection(_invMatrixWorld); for (let i = 0, l = visibility.length; i < l; i++) { if (visibility[i] && active[i]) { // get the bounds in world space this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); // determine whether the batched geometry is within the frustum let culled = false; if (perObjectFrustumCulled) { culled = !_frustum.intersectsSphere(_sphere$2); } if (!culled) { // get the distance from camera used for sorting const z = _temp .subVectors(_sphere$2.center, _vector$5) .dot(_forward); _renderList.push(drawRanges[i], z); } } } // Sort the draw ranges and prep for rendering const list = _renderList.list; const customSort = this.customSort; if (customSort === null) { list.sort(material.transparent ? sortTransparent : sortOpaque); } else { customSort.call(this, list, camera); } for (let i = 0, l = list.length; i < l; i++) { const item = list[i]; multiDrawStarts[count] = item.start * bytesPerElement; multiDrawCounts[count] = item.count; count++; } _renderList.reset(); } else { for (let i = 0, l = visibility.length; i < l; i++) { if (visibility[i] && active[i]) { // determine whether the batched geometry is within the frustum let culled = false; if (perObjectFrustumCulled) { // get the bounds in world space this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); culled = !_frustum.intersectsSphere(_sphere$2); } if (!culled) { const range = drawRanges[i]; multiDrawStarts[count] = range.start * bytesPerElement; multiDrawCounts[count] = range.count; count++; } } } } this._multiDrawCount = count; this._visibilityChanged = false; } onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial /* , group */, ) { this.onBeforeRender(renderer, null, shadowCamera, geometry, depthMaterial); } } class LineBasicMaterial extends Material { constructor(parameters) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.map = null; this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _vStart = /*@__PURE__*/ new Vector3(); const _vEnd = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); const _intersectPointOnRay = /*@__PURE__*/ new Vector3(); const _intersectPointOnSegment = /*@__PURE__*/ new Vector3(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial(), ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _vStart.fromBufferAttribute(positionAttribute, i - 1); _vEnd.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _vStart.distanceTo(_vEnd); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute(lineDistances, 1), ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.", ); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, ); if (intersect) { intersects.push(intersect); } } if (this.isLineLoop) { const a = index.getX(end - 1); const b = index.getX(start); const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, ); if (intersect) { intersects.push(intersect); } } } else { const start = Math.max(0, drawRange.start); const end = Math.min( positionAttribute.count, drawRange.start + drawRange.count, ); for (let i = start, l = end - 1; i < l; i += step) { const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, i, i + 1, ); if (intersect) { intersects.push(intersect); } } if (this.isLineLoop) { const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, end - 1, start, ); if (intersect) { intersects.push(intersect); } } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } function checkIntersection(object, raycaster, ray, thresholdSq, a, b) { const positionAttribute = object.geometry.attributes.position; _vStart.fromBufferAttribute(positionAttribute, a); _vEnd.fromBufferAttribute(positionAttribute, b); const distSq = ray.distanceSqToSegment( _vStart, _vEnd, _intersectPointOnRay, _intersectPointOnSegment, ); if (distSq > thresholdSq) return; _intersectPointOnRay.applyMatrix4(object.matrixWorld); // Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(_intersectPointOnRay); if (distance < raycaster.near || distance > raycaster.far) return; return { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: _intersectPointOnSegment.clone().applyMatrix4(object.matrixWorld), index: a, face: null, faceIndex: null, object: object, }; } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute(lineDistances, 1), ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.", ); } return this; } } class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.isLineLoop = true; this.type = "LineLoop"; } } class PointsMaterial extends Material { constructor(parameters) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial(), ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this, ); } } else { const start = Math.max(0, drawRange.start); const end = Math.min( positionAttribute.count, drawRange.start + drawRange.count, ); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this, ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object, ) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object, }); } } class VideoTexture extends Texture { constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ) { super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isVideoTexture = true; this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = "requestVideoFrameCallback" in video; if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } class FramebufferTexture extends Texture { constructor(width, height) { super({width, height}); this.isFramebufferTexture = true; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } class CompressedTexture extends Texture { constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace, ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isCompressedTexture = true; this.image = {width: width, height: height}; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } class CompressedArrayTexture extends CompressedTexture { constructor(mipmaps, width, height, depth, format, type) { super(mipmaps, width, height, format, type); this.isCompressedArrayTexture = true; this.image.depth = depth; this.wrapR = ClampToEdgeWrapping; this.layerUpdates = new Set(); } addLayerUpdates(layerIndex) { this.layerUpdates.add(layerIndex); } clearLayerUpdates() { this.layerUpdates.clear(); } } class CompressedCubeTexture extends CompressedTexture { constructor(images, format, type) { super( undefined, images[0].width, images[0].height, format, type, CubeReflectionMapping, ); this.isCompressedCubeTexture = true; this.isCubeTexture = true; this.image = images; } } class CanvasTexture extends Texture { constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ) { super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isCanvasTexture = true; this.needsUpdate = true; } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint(/* t, optionalTarget */) { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if ( this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if ( tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0 ) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals, }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Curve", generator: "Curve.toJSON", }, }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0, ) { super(); this.isEllipseCurve = true; this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.isArcCurve = true; this.type = "ArcCurve"; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; }, }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5, ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return ( (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1 ); } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return ( QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2) ); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return ( CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3) ); } class CubicBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2(), ) { super(); this.isCubicBezierCurve = true; this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class CubicBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3(), ) { super(); this.isCubicBezierCurve3 = true; this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.isLineCurve = true; this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector2()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.isLineCurve3 = true; this.type = "LineCurve3"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector3()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.isQuadraticBezierCurve = true; this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.isQuadraticBezierCurve3 = true; this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class SplineCurve extends Curve { constructor(points = []) { super(); this.isSplineCurve = true; this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set( CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y), ); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } var Curves = /*#__PURE__*/ Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve, }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { const lineType = startPoint.isVector2 === true ? "LineCurve" : "LineCurve3"; this.curves.push(new Curves[lineType](endPoint, startPoint)); } return this; } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0]) ) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY), ); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY), ); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise, ); return this; } ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ); return this; } absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ) { const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class LatheGeometry extends BufferGeometry { constructor( points = [new Vector2(0, -0.5), new Vector2(0.5, 0), new Vector2(0, 0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2, ) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength, }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength, ); } } class CapsuleGeometry extends LatheGeometry { constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { const path = new Path(); path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0); path.absarc(0, length / 2, radius, 0, Math.PI * 0.5); super(path.getPoints(capSegments), radialSegments); this.type = "CapsuleGeometry"; this.parameters = { radius: radius, length: length, capSegments: capSegments, radialSegments: radialSegments, }; } static fromJSON(data) { return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments, ); } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength, }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + (s / segments) * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength, ); } } class CylinderGeometry extends BufferGeometry { constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength, }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength, ); } } class ConeGeometry extends CylinderGeometry { constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2, ) { super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength, ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength, }; } static fromJSON(data) { return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength, ); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail, }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute( "normal", new Float32BufferAttribute(vertexBuffer.slice(), 3), ); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivision with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2( -vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z), ); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details, ); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [ // (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r, ]; const indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9, ]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = /*@__PURE__*/ new Vector3(); const _v1$1 = /*@__PURE__*/ new Vector3(); const _normal = /*@__PURE__*/ new Vector3(); const _triangle = /*@__PURE__*/ new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle, }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const {a, b, c} = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if ( hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0] ) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone(), }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const {index0, index1} = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions), }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.4) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); return triangles; }, }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push((prev.i / dim) | 0); triangles.push((ear.i / dim) | 0); triangles.push((next.i / dim) | 0); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx, y0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy, x1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx, y1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy; let p = c.next; while (p !== a) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx, y0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy, x1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx, y1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy; // z-order range for the current triangle bbox; const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.prevZ; if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0 ) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0 ) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if ( !equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a) ) { triangles.push((a.i / dim) | 0); triangles.push((p.i / dim) | 0); triangles.push((b.i / dim) | 0); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize, 0); earcutLinked(c, triangles, dim, minX, minY, invSize, 0); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { outerNode = eliminateHole(queue[i], outerNode); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole(hole, outerNode) { const bridge = findHoleBridge(hole, outerNode); if (!bridge) { return outerNode; } const bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts filterPoints(bridgeReverse, bridgeReverse.next); return filterPoints(bridge, bridge.next); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode, qx = -Infinity, m; const hx = hole.x, hy = hole.y; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + ((hy - p.y) * (p.next.x - p.x)) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while (p !== outerNode); if (!m) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y, ) ) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if ( locallyInside(p, hole) && (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p))))) ) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || (qSize > 0 && q)) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = ((x - minX) * invSize) | 0; y = ((y - minY) * invSize) | 0; x = (x | (x << 8)) & 0x00ff00ff; x = (x | (x << 4)) & 0x0f0f0f0f; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; y = (y | (y << 8)) & 0x00ff00ff; y = (y | (y << 4)) & 0x0f0f0f0f; y = (y | (y << 2)) & 0x33333333; y = (y | (y << 1)) & 0x55555555; return x | (y << 1); } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return ( (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py) ); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return ( a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones"t intersect other edges ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible (area(a.prev, a, b.prev) || area(a, b.prev, b))) || // does not create opposite-facing sectors (equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0)) ); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return ( q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y) ); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b) ) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if ( p.y > py !== p.next.y > py && p.next.y !== p.y && px < ((p.next.x - p.x) * (py - p.y)) / (p.next.y - p.y) + p.x ) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor( shapes = new Shape([ new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5), ]), options = {}, ) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options, }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; const depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return pt.clone().addScaledVector(vec, size); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y, ); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos((t * Math.PI) / 2); const bs = bevelSize * Math.sin((t * Math.PI) / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, (depth / steps) * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos((t * Math.PI) / 2); const bs = bevelSize * Math.sin((t * Math.PI) / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v( vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z, ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3( face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps, ); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1, ); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1, ); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON( extrudePath, ); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [ new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y), ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD, ) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [ new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z), ]; } else { return [ new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z), ]; } }, }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } data.options = Object.assign({}, options); if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [ -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1, ]; const indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1, ]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2, ]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength, }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + (i / thetaSegments) * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength, ); } } class ShapeGeometry extends BufferGeometry { constructor( shapes = new Shape([ new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5), ]), curveSegments = 12, ) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments, }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // indices for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI, ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength, }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy === 0 && thetaStart === 0) { uOffset = 0.5 / widthSegments; } else if (iy === heightSegments && thetaEnd === Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength, ); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2, ) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc, }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = (i / tubularSegments) * arc; const v = (j / radialSegments) * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc, ); } } class TorusKnotGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3, ) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q, }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segment const u = (i / tubularSegments) * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = (j / radialSegments) * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = (q / p) * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q, ); } } class TubeGeometry extends BufferGeometry { constructor( path = new QuadraticBezierCurve3( new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0), ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false, ) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed, }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = (j / radialSegments) * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry( new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed, ); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry, }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{start: 0, count: indices.count, materialIndex: 0}]; } // create a data structure that contains all edges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + ((j + 1) % 3)); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + ((j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } var Geometries = /*#__PURE__*/ Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, CapsuleGeometry: CapsuleGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry, }); class ShadowMaterial extends Material { constructor(parameters) { super(); this.isShadowMaterial = true; this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.fog = source.fog; return this; } } class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.isRawShaderMaterial = true; this.type = "RawShaderMaterial"; } } class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.isMeshStandardMaterial = true; this.defines = {STANDARD: ""}; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.envMapIntensity = 1.0; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = {STANDARD: ""}; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.envMapIntensity = source.envMapIntensity; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.isMeshPhysicalMaterial = true; this.defines = { STANDARD: "", PHYSICAL: "", }; this.type = "MeshPhysicalMaterial"; this.anisotropyRotation = 0; this.anisotropyMap = null; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp((2.5 * (this.ior - 1)) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); }, }); this.iridescenceMap = null; this.iridescenceIOR = 1.3; this.iridescenceThicknessRange = [100, 400]; this.iridescenceThicknessMap = null; this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = Infinity; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._anisotropy = 0; this._clearcoat = 0; this._dispersion = 0; this._iridescence = 0; this._sheen = 0.0; this._transmission = 0; this.setValues(parameters); } get anisotropy() { return this._anisotropy; } set anisotropy(value) { if (this._anisotropy > 0 !== value > 0) { this.version++; } this._anisotropy = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get iridescence() { return this._iridescence; } set iridescence(value) { if (this._iridescence > 0 !== value > 0) { this.version++; } this._iridescence = value; } get dispersion() { return this._dispersion; } set dispersion(value) { if (this._dispersion > 0 !== value > 0) { this.version++; } this._dispersion = value; } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { STANDARD: "", PHYSICAL: "", }; this.anisotropy = source.anisotropy; this.anisotropyRotation = source.anisotropyRotation; this.anisotropyMap = source.anisotropyMap; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.dispersion = source.dispersion; this.ior = source.ior; this.iridescence = source.iridescence; this.iridescenceMap = source.iridescenceMap; this.iridescenceIOR = source.iridescenceIOR; this.iridescenceThicknessRange = [...source.iridescenceThicknessRange]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.isMeshPhongMaterial = true; this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshToonMaterial extends Material { constructor(parameters) { super(); this.isMeshToonMaterial = true; this.defines = {TOON: ""}; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.isMeshNormalMaterial = true; this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.isMeshLambertMaterial = true; this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.isMeshMatcapMaterial = true; this.defines = {MATCAP: ""}; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = {MATCAP: ""}; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.isLineDashedMaterial = true; this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } // converts an array to a specific type function convertArray(array, type, forceClone) { if ( !array || // let "undefined" and "null" pass (!forceClone && array.constructor === type) ) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array } function isTypedArray(object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); } // returns an array by which times and values can be sorted function getKeyframeOrder(times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; } // uses the array previously returned by "getKeyframeOrder" to sort data function sortedArray(values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; } // function for parsing AOS keyframe formats function flattenJSON(jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } } function subclip(sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = convertArray(times, track.times.constructor); track.values = convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; } function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30, ) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return ( track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType ); }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = referenceTrack.values.slice(startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = referenceTrack.values.slice(startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = interpolant.resultBuffer.slice(startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion() .fromArray(referenceValue) .normalize() .conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat( targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart, ); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } const AnimationUtils = { convertArray: convertArray, isTypedArray: isTypedArray, getKeyframeOrder: getKeyframeOrder, sortedArray: sortedArray, flattenJSON: flattenJSON, subclip: subclip, makeClipAdditive: makeClipAdditive, }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2; ; ) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2; ; ) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.copySampleValue_(0); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = (i1 + right) >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.copySampleValue_(0); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_(/* i1, t0, t, t1 */) { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_(/* i1, t0, t1 */) { // empty } } /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding, }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name, ); this.name = name; this.times = convertArray(times, this.TimeBufferType); this.values = convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { name: track.name, times: convertArray(track.times, Array), values: convertArray(track.values, Array), }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result, ); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result, ); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result, ); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = times.slice(from, to); this.values = this.values.slice(from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime, ); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime, ); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value, ); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = this.times.slice(), values = this.values.slice(), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if ( value !== values[offsetP + j] || value !== values[offsetN + j] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j ) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = times.slice(0, writeIndex); this.values = values.slice(0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = this.times.slice(); const values = this.values.slice(); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack { // No interpolation parameter because only InterpolateDiscrete is valid. constructor(name, times, values) { super(name, times, values); } } BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha, ); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result, ); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited // DefaultInterpolation is inherited; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack { // No interpolation parameter because only InterpolateDiscrete is valid. constructor(name, times, values) { super(name, times, values); } } StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; class AnimationClip { constructor( name = "", duration = -1, tracks = [], blendMode = NormalAnimationBlendMode, ) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { name: clip.name, duration: clip.duration, tracks: tracks, uuid: clip.uuid, blendMode: clip.blendMode, }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push( (i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets, ); values.push(0, 1, 0); const order = getKeyframeOrder(times); times = sortedArray(times, 1, order); values = sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values, ).scale(1.0 / fps), ); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = (o.geometry && o.geometry.animations) || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[name], fps, noLoop, ), ); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks, ) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values, ), ); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks, ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks, ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks, ); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor( this.name, this.duration, tracks, this.blendMode, ); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation, ); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; }, }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load(/* url, onLoad, onProgress, onError */) {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse(/* data */) {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } Loader.DEFAULT_MATERIAL_NAME = "__DEFAULT"; const loading = {}; class HttpError extends Error { constructor(message, response) { super(message); this.response = response; } } class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError, }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError, }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin", // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req) .then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if ( typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined ) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get("X-File-Size") || response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then( ({done, value}) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total, }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }, e => { controller.error(e); }, ); } }, }); return new Response(stream); } else { throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response, ); } }) .then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }) .then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }) .catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }) .finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load( url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps, }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError, ); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = {mipmaps: []}; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push( texDatas.mipmaps[f * texDatas.mipmapCount + i], ); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError, ); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.slice(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); texture.colorSpace = SRGBColorSpace; const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load( urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError, ); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (buffer) { let texData; try { texData = scope.parse(buffer); } catch (error) { if (onError !== undefined) { onError(error); } else { console.error(error); return; } } if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.colorSpace !== undefined) { texture.colorSpace = texData.colorSpace; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError, ); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load( url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError, ); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.isLight = true; this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() { // Empty here in base class; some subclasses override. } copy(source, recursive) { super.copy(source, recursive); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.isHemisphereLight = true; this.type = "HemisphereLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source, recursive) { super.copy(source, recursive); this.groundColor.copy(source.groundColor); return this; } } const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); const _lookTarget$1 = /*@__PURE__*/ new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse, ); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, ); shadowMatrix.multiply(_projScreenMatrix$1); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.isSpotLightShadow = true; this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } class SpotLight extends Light { constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2, ) { super(color, intensity); this.isSpotLight = true; this.type = "SpotLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; this.map = null; this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld = /*@__PURE__*/ new Vector3(); const _lookTarget = /*@__PURE__*/ new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this.isPointLightShadow = true; this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [ // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1), ]; this._cubeDirections = [ new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0), ]; this._cubeUps = [ new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), ]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation( -_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z, ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse, ); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 2) { super(color, intensity); this.isPointLight = true; this.type = "PointLight"; this.distance = distance; this.decay = decay; this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); this.isDirectionalLightShadow = true; } } class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.isDirectionalLight = true; this.type = "DirectionalLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.isAmbientLight = true; this.type = "AmbientLight"; } } class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.isRectAreaLight = true; this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.isSphericalHarmonics3 = true; this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.isLightProbe = true; this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = MaterialLoader.createMaterialFromType(json.type); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.dispersion !== undefined) material.dispersion = json.dispersion; if (json.iridescence !== undefined) material.iridescence = json.iridescence; if (json.iridescenceIOR !== undefined) material.iridescenceIOR = json.iridescenceIOR; if (json.iridescenceThicknessRange !== undefined) material.iridescenceThicknessRange = json.iridescenceThicknessRange; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex(json.attenuationColor); if (json.anisotropy !== undefined) material.anisotropy = json.anisotropy; if (json.anisotropyRotation !== undefined) material.anisotropyRotation = json.anisotropyRotation; if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.alphaHash !== undefined) material.alphaHash = json.alphaHash; if (json.depthFunc !== undefined) material.depthFunc = json.depthFunc; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.blendSrc !== undefined) material.blendSrc = json.blendSrc; if (json.blendDst !== undefined) material.blendDst = json.blendDst; if (json.blendEquation !== undefined) material.blendEquation = json.blendEquation; if (json.blendSrcAlpha !== undefined) material.blendSrcAlpha = json.blendSrcAlpha; if (json.blendDstAlpha !== undefined) material.blendDstAlpha = json.blendDstAlpha; if (json.blendEquationAlpha !== undefined) material.blendEquationAlpha = json.blendEquationAlpha; if (json.blendColor !== undefined && material.blendColor !== undefined) material.blendColor.setHex(json.blendColor); if (json.blendAlpha !== undefined) material.blendAlpha = json.blendAlpha; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== undefined) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.forceSinglePass !== undefined) material.forceSinglePass = json.forceSinglePass; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray( uniform.value, ); break; case "v3": material.uniforms[name].value = new Vector3().fromArray( uniform.value, ); break; case "v4": material.uniforms[name].value = new Vector4().fromArray( uniform.value, ); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray( uniform.value, ); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray( uniform.value, ); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.glslVersion !== undefined) material.glslVersion = json.glslVersion; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } if (json.lights !== undefined) material.lights = json.lights; if (json.clipping !== undefined) material.clipping = json.clipping; // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapRotation !== undefined) material.envMapRotation.fromArray(json.envMapRotation); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale, ); if (json.iridescenceMap !== undefined) material.iridescenceMap = getTexture(json.iridescenceMap); if (json.iridescenceThicknessMap !== undefined) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap, ); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.anisotropyMap !== undefined) material.anisotropyMap = getTexture(json.anisotropyMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } static createMaterialFromType(type) { const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material, }; return new materialLib[type](); } } class LoaderUtils { static decodeText(array) { // @deprecated, r165 console.warn( "THREE.LoaderUtils: decodeText() has been deprecated with r165 and will be removed with r175. Use TextDecoder instead.", ); if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.slice(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.isInstancedBufferGeometry = true; this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } toJSON() { const data = super.toJSON(); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data, ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized, ); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized, ); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data, ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized, ); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized, ); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message, ); return; } const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { if (onError !== undefined) onError(new Error("THREE.ObjectLoader: Can"t load " + url)); console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError, ); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject( json.object, geometries, materials, textures, animations, ); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid].data instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject( json.object, geometries, materials, textures, animations, ); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn( `THREE.ObjectLoader: Unsupported geometry type "${data.type}"`, ); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load( url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }, ); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height, }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height, ), ); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height, }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height, ), ); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = await deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value, ); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid, ); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } const source = images[data.image]; const image = source.data; let texture; if (Array.isArray(image)) { texture = new CubeTexture(); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(); } else { texture = new Texture(); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.channel !== undefined) texture.channel = data.channel; if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.internalFormat !== undefined) texture.internalFormat = data.internalFormat; if (data.type !== undefined) texture.type = data.type; if (data.colorSpace !== undefined) texture.colorSpace = data.colorSpace; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.generateMipmaps !== undefined) texture.generateMipmaps = data.generateMipmaps; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.compareFunction !== undefined) texture.compareFunction = data.compareFunction; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } if (data.fog.name !== "") { object.fog.name = data.fog.name; } } if (data.backgroundBlurriness !== undefined) object.backgroundBlurriness = data.backgroundBlurriness; if (data.backgroundIntensity !== undefined) object.backgroundIntensity = data.backgroundIntensity; if (data.backgroundRotation !== undefined) object.backgroundRotation.fromArray(data.backgroundRotation); if (data.environmentIntensity !== undefined) object.environmentIntensity = data.environmentIntensity; if (data.environmentRotation !== undefined) object.environmentRotation.fromArray(data.environmentRotation); break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far, ); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far, ); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay, ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height, ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay, ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity, ); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute( new Float32Array(instanceMatrix.array), 16, ); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute( new Float32Array(instanceColor.array), instanceColor.itemSize, ); break; case "BatchedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new BatchedMesh( data.maxGeometryCount, data.maxVertexCount, data.maxIndexCount, material, ); object.geometry = geometry; object.perObjectFrustumCulled = data.perObjectFrustumCulled; object.sortObjects = data.sortObjects; object._drawRanges = data.drawRanges; object._reservedRanges = data.reservedRanges; object._visibility = data.visibility; object._active = data.active; object._bounds = data.bounds.map(bound => { const box = new Box3(); box.min.fromArray(bound.boxMin); box.max.fromArray(bound.boxMax); const sphere = new Sphere(); sphere.radius = bound.sphereRadius; sphere.center.fromArray(bound.sphereCenter); return { boxInitialized: bound.boxInitialized, box: box, sphereInitialized: bound.sphereInitialized, sphere: sphere, }; }); object._maxGeometryCount = data.maxGeometryCount; object._maxVertexCount = data.maxVertexCount; object._maxIndexCount = data.maxIndexCount; object._geometryInitialized = data.geometryInitialized; object._geometryCount = data.geometryCount; object._matricesTexture = getTexture(data.matricesTexture.uuid); if (data.colorsTexture !== undefined) object._colorsTexture = getTexture(data.colorsTexture.uuid); break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry(data.geometry), getMaterial(data.material), ); break; case "LineLoop": object = new LineLoop( getGeometry(data.geometry), getMaterial(data.material), ); break; case "LineSegments": object = new LineSegments( getGeometry(data.geometry), getMaterial(data.material), ); break; case "PointCloud": case "Points": object = new Points( getGeometry(data.geometry), getMaterial(data.material), ); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose( object.position, object.quaternion, object.scale, ); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.up !== undefined) object.up.fromArray(data.up); if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add( this.parseObject( children[i], geometries, materials, textures, animations, ), ); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance, level.hysteresis); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn( "THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton, ); } else { child.bind(skeleton, child.bindMatrix); } } }); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping, }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter, }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); this.isImageBitmapLoader = true; if (typeof createImageBitmap === "undefined") { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported.", ); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = {premultiplyAlpha: "none"}; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); // If cached is a promise, wait for it to resolve if (cached.then) { cached .then(imageBitmap => { if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }) .catch(e => { if (onError) onError(e); }); return; } // If cached is not a promise (i.e., it"s already an imageBitmap) setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; const promise = fetch(url, fetchOptions) .then(function (res) { return res.blob(); }) .then(function (blob) { return createImageBitmap( blob, Object.assign(scope.options, {colorSpaceConversion: "none"}), ); }) .then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); return imageBitmap; }) .catch(function (e) { if (onError) onError(e); Cache.remove(url); scope.manager.itemError(url); scope.manager.itemEnd(url); }); Cache.add(url, promise); scope.manager.itemStart(url); } } let _context; class AudioContext { static getContext() { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; } static setContext(value) { _context = value; } } class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context .decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }) .catch(handleError); } catch (e) { handleError(e); } }, onProgress, onError, ); function handleError(e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } } } const _eyeRight = /*@__PURE__*/ new Matrix4(); const _eyeLeft = /*@__PURE__*/ new Matrix4(); const _projectionMatrix = /*@__PURE__*/ new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null, }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = (eyeSepHalf * cache.near) / cache.focus; const ymax = (cache.near * Math.tan(DEG2RAD * cache.fov * 0.5)) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = (2 * cache.near) / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = (2 * cache.near) / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/ new Vector3(); const _quaternion$1 = /*@__PURE__*/ new Quaternion(); const _scale$1 = /*@__PURE__*/ new Vector3(); const _orientation$1 = /*@__PURE__*/ new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z, ); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; if (this.source !== null) { this.source.stop(); this.source.onended = null; } this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this._connected === false) { return; } if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.isPlaying === true && this.source.detune !== undefined) { this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01, ); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01, ); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/ new Vector3(); const _quaternion = /*@__PURE__*/ new Quaternion(); const _scale = /*@__PURE__*/ new Vector3(); const _orientation = /*@__PURE__*/ new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } connect() { super.connect(); this.panner.connect(this.gain); } disconnect() { super.disconnect(); this.panner.disconnect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride, ); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride, ); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + (i % stride)]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t, ); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset, ); // Slerp to the intermediate result Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t, ); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /*@__PURE__*/ /((?:WC+[/:])*)/.source.replace( "WC", _wordChar, ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /*@__PURE__*/ /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", _wordChar, ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /*@__PURE__*/ /.(WC+)(?:[(.+)])?/.source.replace( "WC", _wordChar, ); const _trackRe = new RegExp( "" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$", ); const _supportedObjectNames = ["material", "materials", "bones", "map"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName); this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (matches === null) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6], }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName, ); } return results; } static findNode(root, nodeName) { if ( nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName, ); this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.warn( "THREE.PropertyBinding: No target node found for track: " + this.path + ".", ); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this, ); return; } if (!targetObject.material.materials) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this, ); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this, ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; case "map": if ("map" in targetObject) { targetObject = targetObject.map; break; } if (!targetObject.material) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this, ); return; } if (!targetObject.material.map) { console.error( "THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.", this, ); return; } targetObject = targetObject.material.map; break; default: if (targetObject[objectName] === undefined) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this, ); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject, ); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject, ); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this, ); return; } if (!targetObject.geometry.morphAttributes) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this, ); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3, }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2, }; PropertyBinding.prototype.GetterByBindingType = [ PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray, ]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ [ // Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, ], [ // EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, ], [ // ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, ], [ // HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, ], ]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.isAnimationObjectGroup = true; this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; }, }, get bindingsPerObject() { return scope._bindings.length; }, }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push( new PropertyBinding(object, paths[j], parsedPaths[j]), ); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes.", ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding, }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return ( this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this) ); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { deltaTime = 0; } else { this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true; else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1, }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1, }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta, }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } const _controlInterpolantsResultBuffer = new Float32Array(1); class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize(), ); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[0], ); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; }, }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; }, }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; }, }, }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {}, }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant( new Float32Array(2), new Float32Array(2), 1, _controlInterpolantsResultBuffer, ); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode, ); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = (this.time += deltaTime), timeDirection = Math.sign(deltaTime), accuIndex = (this._accuIndex ^= 1); // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } class Uniform { constructor(value) { this.value = value; } clone() { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone(), ); } } let _id = 0; class UniformsGroup extends EventDispatcher { constructor() { super(); this.isUniformsGroup = true; Object.defineProperty(this, "id", {value: _id++}); this.name = ""; this.usage = StaticDrawUsage; this.uniforms = []; } add(uniform) { this.uniforms.push(uniform); return this; } remove(uniform) { const index = this.uniforms.indexOf(uniform); if (index !== -1) this.uniforms.splice(index, 1); return this; } setName(name) { this.name = name; return this; } setUsage(value) { this.usage = value; return this; } dispose() { this.dispatchEvent({type: "dispose"}); return this; } copy(source) { this.name = source.name; this.usage = source.usage; const uniformsSource = source.uniforms; this.uniforms.length = 0; for (let i = 0, l = uniformsSource.length; i < l; i++) { const uniforms = Array.isArray(uniformsSource[i]) ? uniformsSource[i] : [uniformsSource[i]]; for (let j = 0; j < uniforms.length; j++) { this.uniforms.push(uniforms[j].clone()); } } return this; } clone() { return new this.constructor().copy(this); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.isInstancedInterleavedBuffer = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.isGLBufferAttribute = true; this.name = ""; this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } const _matrix = /*@__PURE__*/ new Matrix4(); class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: {threshold: 1}, LOD: {}, Points: {threshold: 1}, Sprite: {}, }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction .set(coords.x, coords.y, 0.5) .unproject(camera) .sub(this.ray.origin) .normalize(); this.camera = camera; } else if (camera.isOrthographicCamera) { this.ray.origin .set( coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far), ) .unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } setFromXRController(controller) { _matrix.identity().extractRotation(controller.matrixWorld); this.ray.origin.setFromMatrixPosition(controller.matrixWorld); this.ray.direction.set(0, 0, -1).applyMatrix4(_matrix); return this; } intersectObject(object, recursive = true, intersects = []) { intersect(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersect(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersect(object, raycaster, intersects, recursive) { let propagate = true; if (object.layers.test(raycaster.layers)) { const result = object.raycast(raycaster, intersects); if (result === false) propagate = false; } if (propagate === true && recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersect(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * phi (the polar angle) is measured from the positive y-axis. The positive y-axis is up. * theta (the azimuthal angle) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be between EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/ new Vector2(); class Box2 { constructor( min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity), ) { this.isBox2 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return ( this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y ); } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( (point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), ); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$4).distanceTo(point); } intersect(box) { this.min.max(box.min); this.max.min(box.max); if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _startP = /*@__PURE__*/ new Vector3(); const _startEnd = /*@__PURE__*/ new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/ new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.matrixAutoUpdate = false; this.color = color; this.type = "SpotLightHelper"; const geometry = new BufferGeometry(); const positions = [ 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, ]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = (i / l) * Math.PI * 2; const p2 = (j / l) * Math.PI * 2; positions.push( Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1, ); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({fog: false, toneMapped: false}); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); // update the local matrix based on the parent and light target transforms if (this.parent) { this.parent.updateWorldMatrix(true); this.matrix .copy(this.parent.matrixWorld) .invert() .multiply(this.light.matrixWorld); } else { this.matrix.copy(this.light.matrixWorld); } this.matrixWorld.copy(this.light.matrixWorld); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/ new Vector3(); const _boneMatrix = /*@__PURE__*/ new Matrix4(); const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true, }); super(geometry, material); this.isSkeletonHelper = true; this.type = "SkeletonHelper"; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } function getBoneList(object) { const boneList = []; if (object.isBone === true) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false, }); super(geometry, material); this.light = light; this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/ new Vector3(); const _color1 = /*@__PURE__*/ new Color(); const _color2 = /*@__PURE__*/ new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "HemisphereLightHelper"; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false, }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } this.light.updateWorldMatrix(true, false); mesh.lookAt( _vector$1.setFromMatrixPosition(this.light.matrixWorld).negate(), ); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "GridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PolarGridHelper extends LineSegments { constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888, ) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the sectors if (sectors > 1) { for (let i = 0; i < sectors; i++) { const v = (i / sectors) * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } } // create the rings for (let i = 0; i < rings; i++) { const color = i & 1 ? color1 : color2; const r = radius - (radius / rings) * i; for (let j = 0; j < divisions; j++) { // first vertex let v = (j / divisions) * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = ((j + 1) / divisions) * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "PolarGridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } const _v1 = /*@__PURE__*/ new Vector3(); const _v2 = /*@__PURE__*/ new Vector3(); const _v3 = /*@__PURE__*/ new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "DirectionalLightHelper"; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ -size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0, ], 3, ), ); const material = new LineBasicMaterial({fog: false, toneMapped: false}); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3), ); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/ new Vector3(); const _camera = /*@__PURE__*/ new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false, }); const vertices = []; const colors = []; const pointMap = {}; // near addLine("n1", "n2"); addLine("n2", "n4"); addLine("n4", "n3"); addLine("n3", "n1"); // far addLine("f1", "f2"); addLine("f2", "f4"); addLine("f4", "f3"); addLine("f3", "f1"); // sides addLine("n1", "f1"); addLine("n2", "f2"); addLine("n3", "f3"); addLine("n4", "f4"); // cone addLine("p", "n1"); addLine("p", "n2"); addLine("p", "n3"); addLine("p", "n4"); // up addLine("u1", "u2"); addLine("u2", "u3"); addLine("u3", "u1"); // target addLine("c", "t"); addLine("p", "c"); // cross addLine("cn1", "cn2"); addLine("cn3", "cn4"); addLine("cf1", "cf2"); addLine("cf3", "cf4"); function addLine(a, b) { addPoint(a); addPoint(b); } function addPoint(id) { vertices.push(0, 0, 0); colors.push(0, 0, 0); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); this.setColors(colorFrustum, colorCone, colorUp, colorTarget, colorCross); } setColors(frustum, cone, up, target, cross) { const geometry = this.geometry; const colorAttribute = geometry.getAttribute("color"); // near colorAttribute.setXYZ(0, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(1, frustum.r, frustum.g, frustum.b); // n1, n2 colorAttribute.setXYZ(2, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(3, frustum.r, frustum.g, frustum.b); // n2, n4 colorAttribute.setXYZ(4, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(5, frustum.r, frustum.g, frustum.b); // n4, n3 colorAttribute.setXYZ(6, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(7, frustum.r, frustum.g, frustum.b); // n3, n1 // far colorAttribute.setXYZ(8, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(9, frustum.r, frustum.g, frustum.b); // f1, f2 colorAttribute.setXYZ(10, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(11, frustum.r, frustum.g, frustum.b); // f2, f4 colorAttribute.setXYZ(12, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(13, frustum.r, frustum.g, frustum.b); // f4, f3 colorAttribute.setXYZ(14, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(15, frustum.r, frustum.g, frustum.b); // f3, f1 // sides colorAttribute.setXYZ(16, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(17, frustum.r, frustum.g, frustum.b); // n1, f1 colorAttribute.setXYZ(18, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(19, frustum.r, frustum.g, frustum.b); // n2, f2 colorAttribute.setXYZ(20, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(21, frustum.r, frustum.g, frustum.b); // n3, f3 colorAttribute.setXYZ(22, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(23, frustum.r, frustum.g, frustum.b); // n4, f4 // cone colorAttribute.setXYZ(24, cone.r, cone.g, cone.b); colorAttribute.setXYZ(25, cone.r, cone.g, cone.b); // p, n1 colorAttribute.setXYZ(26, cone.r, cone.g, cone.b); colorAttribute.setXYZ(27, cone.r, cone.g, cone.b); // p, n2 colorAttribute.setXYZ(28, cone.r, cone.g, cone.b); colorAttribute.setXYZ(29, cone.r, cone.g, cone.b); // p, n3 colorAttribute.setXYZ(30, cone.r, cone.g, cone.b); colorAttribute.setXYZ(31, cone.r, cone.g, cone.b); // p, n4 // up colorAttribute.setXYZ(32, up.r, up.g, up.b); colorAttribute.setXYZ(33, up.r, up.g, up.b); // u1, u2 colorAttribute.setXYZ(34, up.r, up.g, up.b); colorAttribute.setXYZ(35, up.r, up.g, up.b); // u2, u3 colorAttribute.setXYZ(36, up.r, up.g, up.b); colorAttribute.setXYZ(37, up.r, up.g, up.b); // u3, u1 // target colorAttribute.setXYZ(38, target.r, target.g, target.b); colorAttribute.setXYZ(39, target.r, target.g, target.b); // c, t colorAttribute.setXYZ(40, cross.r, cross.g, cross.b); colorAttribute.setXYZ(41, cross.r, cross.g, cross.b); // p, c // cross colorAttribute.setXYZ(42, cross.r, cross.g, cross.b); colorAttribute.setXYZ(43, cross.r, cross.g, cross.b); // cn1, cn2 colorAttribute.setXYZ(44, cross.r, cross.g, cross.b); colorAttribute.setXYZ(45, cross.r, cross.g, cross.b); // cn3, cn4 colorAttribute.setXYZ(46, cross.r, cross.g, cross.b); colorAttribute.setXYZ(47, cross.r, cross.g, cross.b); // cf1, cf2 colorAttribute.setXYZ(48, cross.r, cross.g, cross.b); colorAttribute.setXYZ(49, cross.r, cross.g, cross.b); // cf3, cf4 colorAttribute.needsUpdate = true; } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/ new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, ]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source, recursive) { super.copy(source, recursive); this.object = source.object; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, ]); const positions = [ 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, ]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [ 1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0, ]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [ 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, ]; const geometry2 = new BufferGeometry(); geometry2.setAttribute( "position", new Float32BufferAttribute(positions2, 3), ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false, }), ), ); } updateMatrixWorld(force) { this.position.set(0, 0, 0); this.scale.set(0.5 * this.size, 0.5 * this.size, 1); this.lookAt(this.plane.normal); this.translateZ(-this.plane.constant); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); this.children[0].geometry.dispose(); this.children[0].material.dispose(); } } const _axis = /*@__PURE__*/ new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor( dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2, ) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute( "position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3), ); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line( _lineGeometry, new LineBasicMaterial({color: color, toneMapped: false}), ); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial({color: color, toneMapped: false}), ); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } dispose() { this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); this.cone.material.dispose(); } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, ]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if ( (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x) || (edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) ) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = {s: new Shape(), p: tmpPoints}; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({h: tmpPath, p: tmpPoints[0]}); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; let toChange = 0; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange++; if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } if (toChange > 0 && ambiguous === false) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } class WebGLMultipleRenderTargets extends WebGLRenderTarget { // @deprecated, r162 constructor(width = 1, height = 1, count = 1, options = {}) { console.warn( "THREE.WebGLMultipleRenderTargets has been deprecated and will be removed in r172. Use THREE.WebGLRenderTarget and set the "count" parameter to enable MRT.", ); super(width, height, {...options, count}); this.isWebGLMultipleRenderTargets = true; } get texture() { return this.textures; } } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("register", { detail: { revision: REVISION, }, }), ); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AgXToneMapping = AgXToneMapping; exports.AlphaFormat = AlphaFormat; exports.AlwaysCompare = AlwaysCompare; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AnimationAction = AnimationAction; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.AttachedBindMode = AttachedBindMode; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BatchedMesh = BatchedMesh; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasTexture = CanvasTexture; exports.CapsuleGeometry = CapsuleGeometry; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.ColorManagement = ColorManagement; exports.CompressedArrayTexture = CompressedArrayTexture; exports.CompressedCubeTexture = CompressedCubeTexture; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeGeometry = ConeGeometry; exports.ConstantAlphaFactor = ConstantAlphaFactor; exports.ConstantColorFactor = ConstantColorFactor; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DetachedBindMode = DetachedBindMode; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DisplayP3ColorSpace = DisplayP3ColorSpace; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EllipseCurve = EllipseCurve; exports.EqualCompare = EqualCompare; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FileLoader = FileLoader; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterCompare = GreaterCompare; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualCompare = GreaterEqualCompare; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LessCompare = LessCompare; exports.LessDepth = LessDepth; exports.LessEqualCompare = LessEqualCompare; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LineSegments = LineSegments; exports.LinearDisplayP3ColorSpace = LinearDisplayP3ColorSpace; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearSRGBColorSpace = LinearSRGBColorSpace; exports.LinearToneMapping = LinearToneMapping; exports.LinearTransfer = LinearTransfer; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeutralToneMapping = NeutralToneMapping; exports.NeverCompare = NeverCompare; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColorSpace = NoColorSpace; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualCompare = NotEqualCompare; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusConstantAlphaFactor = OneMinusConstantAlphaFactor; exports.OneMinusConstantColorFactor = OneMinusConstantColorFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.P3Primaries = P3Primaries; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.RED_GREEN_RGTC2_Format = RED_GREEN_RGTC2_Format; exports.RED_RGTC1_Format = RED_RGTC1_Format; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_BPTC_SIGNED_Format = RGB_BPTC_SIGNED_Format; exports.RGB_BPTC_UNSIGNED_Format = RGB_BPTC_UNSIGNED_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.Rec709Primaries = Rec709Primaries; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RenderTarget = RenderTarget; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingGeometry = RingGeometry; exports.SIGNED_RED_GREEN_RGTC2_Format = SIGNED_RED_GREEN_RGTC2_Format; exports.SIGNED_RED_RGTC1_Format = SIGNED_RED_RGTC1_Format; exports.SRGBColorSpace = SRGBColorSpace; exports.SRGBTransfer = SRGBTransfer; exports.Scene = Scene; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.Source = Source; exports.Sphere = Sphere; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronGeometry = TetrahedronGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusGeometry = TorusGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsGroup = UniformsGroup; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedInt5999Type = UnsignedInt5999Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.VideoTexture = VideoTexture; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCoordinateSystem = WebGLCoordinateSystem; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WebGPUCoordinateSystem = WebGPUCoordinateSystem; exports.WireframeGeometry = WireframeGeometry; exports.WrapAroundEnding = WrapAroundEnding; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports.createCanvasElement = createCanvasElement; /* */}),null);
-----
BaseProgressRing.react",["BaseLoadingStateElement.react","CometProgressRingUtils","performanceNavigationStart","react","recoverableViolation","useBaseProgressRingSvg.react"],(function(a,b,c,d,e,f,g){"use strict";var h,i=h||(h=d("react"));b=h;var j=b.useCallback,k=b.useEffect,l=b.useLayoutEffect,m=b.useRef,n=b.useState,o={rootDiv:{position:"x1n2onr6",$$css:!0}},p=d("CometProgressRingUtils").getCubicBezierPercentageFunc(.45,0,.25,.85);function a(a){var b=a["aria-label"],d=a["aria-labelledby"],e=a.backgroundColor,f=a.beginningPercentage,g=a.duration,h=a.explicitStartTime,s=a.foregroundColor,t=a.onComplete,u=a.pause,v=a.percentage,w=a.size,x=a.type;x!=="default"&&v!=null&&c("recoverableViolation")("percentage should only be applied to default type","comet_ui");var y=m(null),z=m(null);a=n(!0);var A=a[0],B=a[1];a=q(x,v,f);f=a.completedPercentage;var C=a.finalPercentage;a=a.startPercentage;var D=m(a),E=m(C),F=m(f);a=c("useBaseProgressRingSvg.react")({backgroundColor:e,foregroundColor:s,isHidden:A,size:w});var G=a.backgroundRef,H=a.leftCircleRef;C=a.progressRing;var I=a.rightCircleRef,J=j(function(a){var b=I.current,d=H.current;if(b==null||d==null)return;if(!(b instanceof SVGSVGElement)||!(d instanceof SVGSVGElement))return;if((x==="default"||x==="count-down")&&u===!0)return;z.current==null&&(z.current=h!=null?h-c("performanceNavigationStart")():a);if(x==="estimated")if(F.current>75)F.current+=.3*Math.pow((100-F.current)/50,2);else{var e=(a-z.current)/g/1e3;e=p(e);F.current=e*75}else if(x==="default"&&F.currentE.current){e=(a-z.current)/1e3/g*100;F.current=D.current-e;F.current=Math.max(F.current,0)}a=Math.max(0,Math.min(100,F.current))/100*360;b.style.transform="rotate("+Math.min(a,180)+"deg)";d.style.transform="rotate("+Math.max(a-180,0)+"deg)";r(x,F.current,v)?(window.cancelAnimationFrame(y.current),b.style.opacity="0",d.style.opacity="0",z.current=null,y.current=null,G.current instanceof SVGSVGElement&&(G.current.style.opacity="0"),t&&t()):y.current=window.requestAnimationFrame(J)},[G,g,h,H,t,u,v,I,x]);l(function(){A&&B(!A)},[A]);k(function(){if(window.requestAnimationFrame!=null){y.current=window.requestAnimationFrame(J);return function(){y.current!=null&&(z.current=null,window.cancelAnimationFrame(y.current),y.current=null)}}},[w,J]);return i.jsx(c("BaseLoadingStateElement.react"),{"aria-label":b,"aria-labelledby":d,style:{height:w,width:w},xstyle:o.rootDiv,children:C})}a.displayName=a.name+" [from "+f.id+"]";function q(a,b,c){switch(a){case"default":return{completedPercentage:(a=c)!=null?a:0,finalPercentage:(a=b)!=null?a:100,startPercentage:(b=c)!=null?b:0};case"count-down":return{completedPercentage:100,finalPercentage:0,startPercentage:100};case"estimated":return{completedPercentage:0,finalPercentage:75,startPercentage:0};default:return{completedPercentage:0,finalPercentage:0,startPercentage:0}}}function r(a,b,c){return a==="count-down"?b<=0||b>100:b>=((a=c)!=null?a:100)}g["default"]=a}),98);
-----
CubicBezier",[],(function(a,b,c,d,e,f){var g=.005;a=function(){function a(a,b){this.cx=3*a[0],this.bx=3*(b[0]-a[0])-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*a[1],this.by=3*(b[1]-a[1])-this.cy,this.ay=1-this.cy-this.by}var b=a.prototype;b.sampleCurveX=function(a){return((this.ax*a+this.bx)*a+this.cx)*a};b.solve=function(a){a=this.solveCurveX(a);return((this.ay*a+this.by)*a+this.cy)*a};b.solveCurveX=function(a){var b,c,d,e;for(d=a,c=0;c<8;c++){e=this.sampleCurveX(d)-a;if(Math.abs(e)c)return c;while(be?b=d:c=d;d=(c-b)/2+b}return d};return a}();f["default"]=a}),66);
-----
KeyframesTween",["invariant","CubicBezier","KeyframesPathUtils","KeyframesSchema"],(function(a,b,c,d,e,f,g){"use strict";var h=b("KeyframesPathUtils").interpolateCubic,i=b("KeyframesPathUtils").interpolateLinear,j=b("KeyframesPathUtils").interpolateQuad;(d=b("KeyframesSchema")).Color;d.ColorAnimation;d.ColorArray;d.ColorArrayAnimation;d.Command;var k=d.CommandType;d.Path;d.PathAnimation;d.Point;d.PointAnimation;d.ScalarAnimation;d.ScalarArray;d.ScalarArrayAnimation;var l=d.TweenType;function m(a){return a}function n(a){return{x:a.x(),y:a.y()}}function o(a){return{red:a.red(),green:a.green(),blue:a.blue(),alpha:a.alpha()}}function p(a){var b=a.valuesLength(),c=[];for(var d=0;d=b.keyframes(b.keyframesLength()-1))return 1;else if(a-----
CometProgressRingUtils",["ix"],(function(a,b,c,d,e,f,g,h){"use strict";function a(a,b,c,d){function e(a,b){return 1-3*b+3*a}function f(a,b){return 3*b-6*a}function g(a){return 3*a}function h(a,b,c){return((e(b,c)*a+f(b,c))*a+g(b))*a}function i(a,b,c){return 3*e(b,c)*a*a+2*f(b,c)*a+g(b)}function j(b){var d=b;for(var e=0;e<4;++e){var f=i(d,a,c);if(f===0)return d;var g=h(d,a,c)-b;d-=g/f}return d}return function(e){return a===b&&c===d?e:h(j(e),b,d)}}function b(a,b,c){switch(b){case"12":switch(c){case"dark":switch(a){case"blue":return h("1876411");case"disabled":return h("1876443");case"dark":return h("1876427");case"light":return h("1876427");default:return h("1876427")}case"light":switch(a){case"blue":return h("1876419");case"disabled":return h("1876451");case"dark":return h("1876435");case"light":return h("1876427");default:return h("1876435")}default:return h("1876435")}case"16":switch(c){case"dark":switch(a){case"blue":return h("1876412");case"disabled":return h("1876444");case"dark":return h("1876428");case"light":return h("1876428");default:return h("1876428")}case"light":switch(a){case"blue":return h("1876420");case"disabled":return h("1876452");case"dark":return h("1876436");case"light":return h("1876428");default:return h("1876436")}default:return h("1876436")}case"20":switch(c){case"dark":switch(a){case"blue":return h("1876413");case"disabled":return h("1876445");case"dark":return h("1876429");case"light":return h("1876429");default:return h("1876429")}case"light":switch(a){case"blue":return h("1876421");case"disabled":return h("1876453");case"dark":return h("1876437");case"light":return h("1876429");default:return h("1876437")}default:return h("1876437")}case"24":switch(c){case"dark":switch(a){case"blue":return h("1876414");case"disabled":return h("1876446");case"dark":return h("1876430");case"light":return h("1876430");default:return h("1876430")}case"light":switch(a){case"blue":return h("1876422");case"disabled":return h("1876454");case"dark":return h("1876438");case"light":return h("1876430");default:return h("1876438")}default:return h("1876438")}case"32":switch(c){case"dark":switch(a){case"blue":return h("1876415");case"disabled":return h("1876447");case"dark":return h("1876431");case"light":return h("1876431");default:return h("1876431")}case"light":switch(a){case"blue":return h("1876423");case"disabled":return h("1876455");case"dark":return h("1876439");case"light":return h("1876431");default:return h("1876439")}default:return h("1876439")}case"48":switch(c){case"dark":switch(a){case"blue":return h("1876416");case"disabled":return h("1876448");case"dark":return h("1876432");case"light":return h("1876432");default:return h("1876432")}case"light":switch(a){case"blue":return h("1876424");case"disabled":return h("1876456");case"dark":return h("1876440");case"light":return h("1876432");default:return h("1876440")}default:return h("1876440")}case"60":switch(c){case"dark":switch(a){case"blue":return h("1940508");case"disabled":return h("1940512");case"dark":return h("1940510");case"light":return h("1940510");default:return h("1940510")}case"light":switch(a){case"blue":return h("1940509");case"disabled":return h("1940513");case"dark":return h("1940511");case"light":return h("1940510");default:return h("1940511")}default:return h("1940511")}case"72":switch(c){case"dark":switch(a){case"blue":return h("1876418");case"disabled":return h("1876450");case"dark":return h("1876434");case"light":return h("1876434");default:return h("1876434")}case"light":switch(a){case"blue":return h("1876426");case"disabled":return h("1876458");case"dark":return h("1876442");case"light":return h("1876434");default:return h("1876442")}default:return h("1876442")}default:return h("1876439")}}function c(a){switch(a){case"dark":return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"};case"light":return{backgroundColor:"var(--progress-ring-on-media-background)",foregroundColor:"var(--progress-ring-on-media-foreground)"};case"blue":return{backgroundColor:"var(--progress-ring-blue-background)",foregroundColor:"var(--progress-ring-blue-foreground)"};case"disabled":return{backgroundColor:"var(--progress-ring-disabled-background)",foregroundColor:"var(--progress-ring-disabled-foreground)"};default:return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"}}}g.getCubicBezierPercentageFunc=a;g.getRingGifUrl=b;g.getRingColor=c}),98);
-----
three.r165",[],(function $module_three_r165(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; const REVISION = "165"; const MOUSE = {LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2}; const TOUCH = {ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3}; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const ConstantColorFactor = 211; const OneMinusConstantColorFactor = 212; const ConstantAlphaFactor = 213; const OneMinusConstantAlphaFactor = 214; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const AgXToneMapping = 6; const NeutralToneMapping = 7; const AttachedBindMode = "attached"; const DetachedBindMode = "detached"; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const UnsignedInt5999Type = 35902; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RGB_BPTC_SIGNED_Format = 36494; const RGB_BPTC_UNSIGNED_Format = 36495; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ""; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const DisplayP3ColorSpace = "display-p3"; const LinearDisplayP3ColorSpace = "display-p3-linear"; const LinearTransfer = "linear"; const SRGBTransfer = "srgb"; const Rec709Primaries = "rec709"; const P3Primaries = "p3"; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const NeverCompare = 512; const LessCompare = 513; const EqualCompare = 514; const LessEqualCompare = 515; const GreaterCompare = 516; const NotEqualCompare = 517; const GreaterEqualCompare = 518; const AlwaysCompare = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const WebGLCoordinateSystem = 2000; const WebGPUCoordinateSystem = 2001; /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return ( listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1 ); } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", ]; let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = (Math.random() * 0xffffffff) | 0; const d1 = (Math.random() * 0xffffffff) | 0; const d2 = (Math.random() * 0xffffffff) | 0; const d3 = (Math.random() * 0xffffffff) | 0; const uuid = _lut[d0 & 0xff] + _lut[(d0 >> 8) & 0xff] + _lut[(d0 >> 16) & 0xff] + _lut[(d0 >> 24) & 0xff] + "-" + _lut[d1 & 0xff] + _lut[(d1 >> 8) & 0xff] + "-" + _lut[((d1 >> 16) & 0x0f) | 0x40] + _lut[(d1 >> 24) & 0xff] + "-" + _lut[(d2 & 0x3f) | 0x80] + _lut[(d2 >> 8) & 0xff] + "-" + _lut[(d2 >> 16) & 0xff] + _lut[(d2 >> 24) & 0xff] + _lut[d3 & 0xff] + _lut[(d3 >> 8) & 0xff] + _lut[(d3 >> 16) & 0xff] + _lut[(d3 >> 24) & 0xff]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return ((n % m) + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + ((x - a1) * (b2 - b1)) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s; // Mulberry32 generator let t = (_seed += 0x6d2b79f5); t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & (value - 1)) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order, ); } } function denormalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return value / 4294967295.0; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int32Array: return Math.max(value / 2147483647.0, -1.0); case Int16Array: return Math.max(value / 32767.0, -1.0); case Int8Array: return Math.max(value / 127.0, -1.0); default: throw new Error("Invalid component type."); } } function normalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return Math.round(value * 4294967295.0); case Uint16Array: return Math.round(value * 65535.0); case Uint8Array: return Math.round(value * 255.0); case Int32Array: return Math.round(value * 2147483647.0); case Int16Array: return Math.round(value * 32767.0); case Int8Array: return Math.round(value * 127.0); default: throw new Error("Invalid component type."); } } const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize, }; class Vector2 { constructor(x = 0, y = 0) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v) { this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } class Matrix3 { constructor(n11, n12, n13, n21, n22, n23, n31, n32, n33) { Matrix3.prototype.isMatrix3 = true; this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (n11 !== undefined) { this.set(n11, n12, n13, n21, n22, n23, n31, n32, n33); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return ( a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g ); } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set( sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1, ); return this; } // scale(sx, sy) { this.premultiply(_m3.makeScale(sx, sy)); return this; } rotate(theta) { this.premultiply(_m3.makeRotation(-theta)); return this; } translate(tx, ty) { this.premultiply(_m3.makeTranslation(tx, ty)); return this; } // for 2D Transforms makeTranslation(x, y) { if (x.isVector2) { this.set(1, 0, x.x, 0, 1, x.y, 0, 0, 1); } else { this.set(1, 0, x, 0, 1, y, 0, 0, 1); } return this; } makeRotation(theta) { // counterclockwise const c = Math.cos(theta); const s = Math.sin(theta); this.set(c, -s, 0, s, c, 0, 0, 0, 1); return this; } makeScale(x, y) { this.set(x, 0, 0, 0, y, 0, 0, 0, 1); return this; } // equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } const _m3 = /*@__PURE__*/ new Matrix3(); function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] >= 65535) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array, }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } const _cache = {}; function warnOnce(message) { if (message in _cache) return; _cache[message] = true; console.warn(message); } function probeAsync(gl, sync, interval) { return new Promise(function (resolve, reject) { function probe() { switch (gl.clientWaitSync(sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0)) { case gl.WAIT_FAILED: reject(); break; case gl.TIMEOUT_EXPIRED: setTimeout(probe, interval); break; default: resolve(); } } setTimeout(probe, interval); }); } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set( 0.8224621, 0.177538, 0.0, 0.0331941, 0.9668058, 0.0, 0.0170827, 0.0723974, 0.9105199, ); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set( 1.2249401, -0.2249404, 0.0, -0.0420569, 1.0420571, 0.0, -0.0196376, -0.0786361, 1.0982735, ); /** * Defines supported color spaces by transfer function and primaries, * and provides conversions to/from the Linear-sRGB reference space. */ const COLOR_SPACES = { [LinearSRGBColorSpace]: { transfer: LinearTransfer, primaries: Rec709Primaries, toReference: color => color, fromReference: color => color, }, [SRGBColorSpace]: { transfer: SRGBTransfer, primaries: Rec709Primaries, toReference: color => color.convertSRGBToLinear(), fromReference: color => color.convertLinearToSRGB(), }, [LinearDisplayP3ColorSpace]: { transfer: LinearTransfer, primaries: P3Primaries, toReference: color => color.applyMatrix3(LINEAR_DISPLAY_P3_TO_LINEAR_SRGB), fromReference: color => color.applyMatrix3(LINEAR_SRGB_TO_LINEAR_DISPLAY_P3), }, [DisplayP3ColorSpace]: { transfer: SRGBTransfer, primaries: P3Primaries, toReference: color => color .convertSRGBToLinear() .applyMatrix3(LINEAR_DISPLAY_P3_TO_LINEAR_SRGB), fromReference: color => color .applyMatrix3(LINEAR_SRGB_TO_LINEAR_DISPLAY_P3) .convertLinearToSRGB(), }, }; const SUPPORTED_WORKING_COLOR_SPACES = new Set([ LinearSRGBColorSpace, LinearDisplayP3ColorSpace, ]); const ColorManagement = { enabled: true, _workingColorSpace: LinearSRGBColorSpace, get workingColorSpace() { return this._workingColorSpace; }, set workingColorSpace(colorSpace) { if (!SUPPORTED_WORKING_COLOR_SPACES.has(colorSpace)) { throw new Error(`Unsupported working color space, "${colorSpace}".`); } this._workingColorSpace = colorSpace; }, convert: function (color, sourceColorSpace, targetColorSpace) { if ( this.enabled === false || sourceColorSpace === targetColorSpace || !sourceColorSpace || !targetColorSpace ) { return color; } const sourceToReference = COLOR_SPACES[sourceColorSpace].toReference; const targetFromReference = COLOR_SPACES[targetColorSpace].fromReference; return targetFromReference(sourceToReference(color)); }, fromWorkingColorSpace: function (color, targetColorSpace) { return this.convert(color, this._workingColorSpace, targetColorSpace); }, toWorkingColorSpace: function (color, sourceColorSpace) { return this.convert(color, sourceColorSpace, this._workingColorSpace); }, getPrimaries: function (colorSpace) { return COLOR_SPACES[colorSpace].primaries; }, getTransfer: function (colorSpace) { if (colorSpace === NoColorSpace) return LinearTransfer; return COLOR_SPACES[colorSpace].transfer; }, }; function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement === "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image, ); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) ) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height, }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.", ); return image; } } } let _sourceId = 0; class Source { constructor(data = null) { this.isSource = true; Object.defineProperty(this, "id", {value: _sourceId++}); this.uuid = generateUUID(); this.data = data; this.dataReady = true; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.images[this.uuid] !== undefined) { return meta.images[this.uuid]; } const output = { uuid: this.uuid, url: "", }; const data = this.data; if (data !== null) { let url; if (Array.isArray(data)) { // cube texture url = []; for (let i = 0, l = data.length; i < l; i++) { if (data[i].isDataTexture) { url.push(serializeImage(data[i].image)); } else { url.push(serializeImage(data[i])); } } } else { // texture url = serializeImage(data); } output.url = url; } if (!isRootObject) { meta.images[this.uuid] = output; } return output; } } function serializeImage(image) { if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) ) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.from(image.data), width: image.width, height: image.height, type: image.data.constructor.name, }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } let _textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace, ) { super(); this.isTexture = true; Object.defineProperty(this, "id", {value: _textureId++}); this.uuid = generateUUID(); this.name = ""; this.source = new Source(image); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) this.colorSpace = colorSpace; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.pmremVersion = 0; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image(value = null) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y, ); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse(JSON.stringify(source.userData)); this.needsUpdate = true; return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { metadata: { version: 4.6, type: "Texture", generator: "Texture.toJSON", }, uuid: this.uuid, name: this.name, image: this.source.toJSON(meta).uuid, mapping: this.mapping, channel: this.channel, 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, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment, }; if (Object.keys(this.userData).length > 0) output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({type: "dispose"}); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } set needsPMREMUpdate(value) { if (value === true) { this.pmremVersion++; } } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if ( Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2 ) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( (m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12), ); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); this.z = Math.trunc(this.z); this.w = Math.trunc(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return ( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w, ); } manhattanLength() { return ( Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w) ); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class RenderTarget extends EventDispatcher { constructor(width = 1, height = 1, options = {}) { super(); this.isRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); const image = {width: width, height: height, depth: 1}; options = Object.assign( { generateMipmaps: false, internalFormat: null, minFilter: LinearFilter, depthBuffer: true, stencilBuffer: false, resolveDepthBuffer: true, resolveStencilBuffer: true, depthTexture: null, samples: 0, count: 1, }, options, ); const texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace, ); texture.flipY = false; texture.generateMipmaps = options.generateMipmaps; texture.internalFormat = options.internalFormat; this.textures = []; const count = options.count; for (let i = 0; i < count; i++) { this.textures[i] = texture.clone(); this.textures[i].isRenderTargetTexture = true; } this.depthBuffer = options.depthBuffer; this.stencilBuffer = options.stencilBuffer; this.resolveDepthBuffer = options.resolveDepthBuffer; this.resolveStencilBuffer = options.resolveStencilBuffer; this.depthTexture = options.depthTexture; this.samples = options.samples; } get texture() { return this.textures[0]; } set texture(value) { this.textures[0] = value; } setSize(width, height, depth = 1) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.textures.length; i < il; i++) { this.textures[i].image.width = width; this.textures[i].image.height = height; this.textures[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy(source.scissor); this.scissorTest = source.scissorTest; this.viewport.copy(source.viewport); this.textures.length = 0; for (let i = 0, il = source.textures.length; i < il; i++) { this.textures[i] = source.textures[i].clone(); this.textures[i].isRenderTargetTexture = true; } // ensure image object is not shared, see #20328 const image = Object.assign({}, source.texture.image); this.texture.source = new Source(image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.resolveDepthBuffer = source.resolveDepthBuffer; this.resolveStencilBuffer = source.resolveStencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent({type: "dispose"}); } } class WebGLRenderTarget extends RenderTarget { constructor(width = 1, height = 1, options = {}) { super(width, height, options); this.isWebGLRenderTarget = true; } } class DataArrayTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.isDataArrayTexture = true; this.image = {data, width, height, depth}; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; this.layerUpdates = new Set(); } addLayerUpdate(layerIndex) { this.layerUpdates.add(layerIndex); } clearLayerUpdates() { this.layerUpdates.clear(); } } class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1, options = {}) { super(width, height, options); this.isWebGLArrayRenderTarget = true; this.depth = depth; this.texture = new DataArrayTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class Data3DTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.isData3DTexture = true; this.image = {data, width, height, depth}; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGL3DRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1, options = {}) { super(width, height, options); this.isWebGL3DRenderTarget = true; this.depth = depth; this.texture = new Data3DTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, ) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update = true) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order, ); } if (update === true) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return ( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w, ); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q) { return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); // normalize calls _onChangeCallback() return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // sets this quaternion to a uniform random unit quaternnion // Ken Shoemake // Uniform random rotations // D. Kirk, editor, Graphics Gems III, pages 124-132. Academic Press, New York, 1992. const theta1 = 2 * Math.PI * Math.random(); const theta2 = 2 * Math.PI * Math.random(); const x0 = Math.random(); const r1 = Math.sqrt(1 - x0); const r2 = Math.sqrt(x0); return this.set( r1 * Math.sin(theta1), r1 * Math.cos(theta1), r2 * Math.sin(theta2), r2 * Math.cos(theta2), ); } equals(quaternion) { return ( quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w ); } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); this._onChangeCallback(); return this; } toJSON() { return this.toArray(); } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor(x = 0, y = 0, z = 0) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { // quaternion q is assumed to have unit length const vx = this.x, vy = this.y, vz = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // t = 2 * cross( q.xyz, v ); const tx = 2 * (qy * vz - qz * vy); const ty = 2 * (qz * vx - qx * vz); const tz = 2 * (qx * vy - qy * vx); // v + q.w * t + cross( q.xyz, t ); this.x = vx + qw * tx + qy * tz - qz * ty; this.y = vy + qw * ty + qz * tx - qx * tz; this.z = vz + qw * tz + qx * ty - qy * tx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4( camera.projectionMatrix, ); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4( camera.matrixWorld, ); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar( Math.max(min, Math.min(max, length)), ); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = Math.trunc(this.x); this.y = Math.trunc(this.y); this.z = Math.trunc(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v) { return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$c.copy(normal).multiplyScalar(2 * this.dot(normal)), ); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return ( Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z) ); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } setFromEuler(e) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor(c) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // https://mathworld.wolfram.com/SpherePointPicking.html const theta = Math.random() * Math.PI * 2; const u = Math.random() * 2 - 1; const c = Math.sqrt(1 - u * u); this.x = c * Math.cos(theta); this.y = u; this.z = c * Math.sin(theta); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } const _vector$c = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity), ) { this.isBox3 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { this.makeEmpty(); for (let i = 0, il = array.length; i < il; i += 3) { this.expandByPoint(_vector$b.fromArray(array, i)); } return this; } setFromBufferAttribute(attribute) { this.makeEmpty(); for (let i = 0, il = attribute.count; i < il; i++) { this.expandByPoint(_vector$b.fromBufferAttribute(attribute, i)); } return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z ); } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { const positionAttribute = geometry.getAttribute("position"); // precise AABB computation based on vertex data requires at least a position attribute. // instancing isn"t supported so far and uses the normal (conservative) code path. if ( precise === true && positionAttribute !== undefined && object.isInstancedMesh !== true ) { for (let i = 0, l = positionAttribute.count; i < l; i++) { if (object.isMesh === true) { object.getVertexPosition(i, _vector$b); } else { _vector$b.fromBufferAttribute(positionAttribute, i); } _vector$b.applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (object.boundingBox !== undefined) { // object-level bounding box if (object.boundingBox === null) { object.computeBoundingBox(); } _box$4.copy(object.boundingBox); } else { // geometry-level bounding box if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$4.copy(geometry.boundingBox); } _box$4.applyMatrix4(object.matrixWorld); this.union(_box$4); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return ( this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z ); } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( (point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z), ); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return ( _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius ); } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$4.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$4, _v1$7); _f2.subVectors(_v0$2, _v2$4); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0, ]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$b).distanceTo(point); } getBoundingSphere(target) { if (this.isEmpty()) { target.makeEmpty(); } else { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; } return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), ]; const _vector$b = /*@__PURE__*/ new Vector3(); const _box$4 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$4 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the separating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$3 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.isSphere = true; this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$3.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return ( sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum ); } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { if (this.isEmpty()) { this.center.copy(point); this.radius = 0; return this; } _v1$6.subVectors(point, this.center); const lengthSq = _v1$6.lengthSq(); if (lengthSq > this.radius * this.radius) { // calculate the minimal sphere const length = Math.sqrt(lengthSq); const delta = (length - this.radius) * 0.5; this.center.addScaledVector(_v1$6, delta / length); this.radius += delta; } return this; } union(sphere) { if (sphere.isEmpty()) { return this; } if (this.isEmpty()) { this.copy(sphere); return this; } if (this.center.equals(sphere.center) === true) { this.radius = Math.max(this.radius, sphere.radius); } else { _v2$3.subVectors(sphere.center, this.center).setLength(sphere.radius); this.expandByPoint(_v1$6.copy(sphere.center).add(_v2$3)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_v2$3)); } return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.origin).addScaledVector(this.direction, t); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target .copy(this.origin) .addScaledVector(this.direction, directionDistance); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a .subVectors(point, this.origin) .dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a .copy(this.origin) .addScaledVector(this.direction, directionDistance); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.origin).addScaledVector(this.direction, s0); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segCenter).addScaledVector(_segDir, s1); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if (t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return ( this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius ); } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; if (tymin > tmin || isNaN(tmin)) tmin = tymin; if (tymax < tmax || isNaN(tmax)) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ( ray.origin.equals(this.origin) && ray.direction.equals(this.direction) ); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ) { Matrix4.prototype.isMatrix4 = true; this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (n11 !== undefined) { this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ); } } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44, ) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set( me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1, ); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1, ); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31) ); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { if (x.isVector3) { this.set(1, 0, 0, x.x, 0, 1, 0, x.y, 0, 0, 1, x.z, 0, 0, 0, 1); } else { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); } return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1, ); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$4.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$4.elements[0] *= invSX; _m1$4.elements[1] *= invSX; _m1$4.elements[2] *= invSX; _m1$4.elements[4] *= invSY; _m1$4.elements[5] *= invSY; _m1$4.elements[6] *= invSY; _m1$4.elements[8] *= invSZ; _m1$4.elements[9] *= invSZ; _m1$4.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$4); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, ) { const te = this.elements; const x = (2 * near) / (right - left); const y = (2 * near) / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); let c, d; if (coordinateSystem === WebGLCoordinateSystem) { c = -(far + near) / (far - near); d = (-2 * far * near) / (far - near); } else if (coordinateSystem === WebGPUCoordinateSystem) { c = -far / (far - near); d = (-far * near) / (far - near); } else { throw new Error( "THREE.Matrix4.makePerspective(): Invalid coordinate system: " + coordinateSystem, ); } te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, ) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; let z, zInv; if (coordinateSystem === WebGLCoordinateSystem) { z = (far + near) * p; zInv = -2 * p; } else if (coordinateSystem === WebGPUCoordinateSystem) { z = near * p; zInv = -1 * p; } else { throw new Error( "THREE.Matrix4.makeOrthographic(): Invalid coordinate system: " + coordinateSystem, ); } te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = zInv; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$4 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3(0, 0, 0); const _one = /*@__PURE__*/ new Vector3(1, 1, 1); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix$2 = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order, ); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$2.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$2, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return ( euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order ); } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = "XYZ"; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = ((1 << channel) | 0) >>> 0; } enable(channel) { this.mask |= (1 << channel) | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= (1 << channel) | 0; } disable(channel) { this.mask &= ~((1 << channel) | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & ((1 << channel) | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$3 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/ new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/ new Vector3(0, 0, 1); const _addedEvent = {type: "added"}; const _removedEvent = {type: "removed"}; const _childaddedEvent = {type: "childadded", child: null}; const _childremovedEvent = {type: "childremoved", child: null}; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty(this, "id", {value: _object3DId++}); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position, }, rotation: { configurable: true, enumerable: true, value: rotation, }, quaternion: { configurable: true, enumerable: true, value: quaternion, }, scale: { configurable: true, enumerable: true, value: scale, }, modelViewMatrix: { value: new Matrix4(), }, normalMatrix: { value: new Matrix3(), }, }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeShadow(/* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */) {} onAfterShadow(/* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */) {} onBeforeRender(/* renderer, scene, camera, geometry, material, group */) {} onAfterRender(/* renderer, scene, camera, geometry, material, group */) {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(_m1$3.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$3.lookAt(_position$3, _target, this.up); } else { _m1$3.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$3); if (parent) { _m1$3.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$3); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object, ); return this; } if (object && object.isObject3D) { object.removeFromParent(); object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); _childaddedEvent.child = object; this.dispatchEvent(_childaddedEvent); _childaddedEvent.child = null; } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object, ); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); _childremovedEvent.child = object; this.dispatchEvent(_childremovedEvent); _childremovedEvent.child = null; } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { return this.remove(...this.children); } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$3.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$3.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$3); object.removeFromParent(); object.parent = this; this.children.push(object); object.updateWorldMatrix(false, true); object.dispatchEvent(_addedEvent); _childaddedEvent.child = object; this.dispatchEvent(_childaddedEvent); _childaddedEvent.child = null; return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getObjectsByProperty(name, value, result = []) { if (this[name] === value) result.push(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].getObjectsByProperty(name, value, result); } return result; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast(/* raycaster, intersects */) {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true || force === true) { child.updateMatrixWorld(force); } } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true) { child.updateWorldMatrix(false, true); } } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {}, }; output.metadata = { version: 4.6, type: "Object", generator: "Object3D.toJSON", }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (Object.keys(this.userData).length > 0) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } if (this.isBatchedMesh) { object.type = "BatchedMesh"; object.perObjectFrustumCulled = this.perObjectFrustumCulled; object.sortObjects = this.sortObjects; object.drawRanges = this._drawRanges; object.reservedRanges = this._reservedRanges; object.visibility = this._visibility; object.active = this._active; object.bounds = this._bounds.map(bound => ({ boxInitialized: bound.boxInitialized, boxMin: bound.box.min.toArray(), boxMax: bound.box.max.toArray(), sphereInitialized: bound.sphereInitialized, sphereRadius: bound.sphere.radius, sphereCenter: bound.sphere.center.toArray(), })); object.maxGeometryCount = this._maxGeometryCount; object.maxVertexCount = this._maxVertexCount; object.maxIndexCount = this._maxIndexCount; object.geometryInitialized = this._geometryInitialized; object.geometryCount = this._geometryCount; object.matricesTexture = this._matricesTexture.toJSON(meta); if (this._colorsTexture !== null) object.colorsTexture = this._colorsTexture.toJSON(meta); if (this.boundingSphere !== null) { object.boundingSphere = { center: object.boundingSphere.center.toArray(), radius: object.boundingSphere.radius, }; } if (this.boundingBox !== null) { object.boundingBox = { min: object.boundingBox.min.toArray(), max: object.boundingBox.max.toArray(), }; } } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); const nodes = extractFromCache(meta.nodes); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; if (nodes.length > 0) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations.slice(); this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3(0, 1, 0); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$2 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { target.set(0, 0, 0); return null; } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { // if the triangle is degenerate then we can"t contain a point if (this.getBarycoord(point, a, b, c, _v3$2) === null) { return false; } return _v3$2.x >= 0 && _v3$2.y >= 0 && _v3$2.x + _v3$2.y <= 1; } static getInterpolation(point, p1, p2, p3, v1, v2, v3, target) { if (this.getBarycoord(point, p1, p2, p3, _v3$2) === null) { target.x = 0; target.y = 0; if ("z" in target) target.z = 0; if ("w" in target) target.w = 0; return null; } target.setScalar(0); target.addScaledVector(v1, _v3$2.x); target.addScaledVector(v2, _v3$2.y); target.addScaledVector(v3, _v3$2.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target .addVectors(this.a, this.b) .add(this.c) .multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getInterpolation(point, v1, v2, v3, target) { return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target, ); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return ( triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c) ); } } const _colorKeywords = { aliceblue: 0xf0f8ff, antiquewhite: 0xfaebd7, aqua: 0x00ffff, aquamarine: 0x7fffd4, azure: 0xf0ffff, beige: 0xf5f5dc, bisque: 0xffe4c4, black: 0x000000, blanchedalmond: 0xffebcd, blue: 0x0000ff, blueviolet: 0x8a2be2, brown: 0xa52a2a, burlywood: 0xdeb887, cadetblue: 0x5f9ea0, chartreuse: 0x7fff00, chocolate: 0xd2691e, coral: 0xff7f50, cornflowerblue: 0x6495ed, cornsilk: 0xfff8dc, crimson: 0xdc143c, cyan: 0x00ffff, darkblue: 0x00008b, darkcyan: 0x008b8b, darkgoldenrod: 0xb8860b, darkgray: 0xa9a9a9, darkgreen: 0x006400, darkgrey: 0xa9a9a9, darkkhaki: 0xbdb76b, darkmagenta: 0x8b008b, darkolivegreen: 0x556b2f, darkorange: 0xff8c00, darkorchid: 0x9932cc, darkred: 0x8b0000, darksalmon: 0xe9967a, darkseagreen: 0x8fbc8f, darkslateblue: 0x483d8b, darkslategray: 0x2f4f4f, darkslategrey: 0x2f4f4f, darkturquoise: 0x00ced1, darkviolet: 0x9400d3, deeppink: 0xff1493, deepskyblue: 0x00bfff, dimgray: 0x696969, dimgrey: 0x696969, dodgerblue: 0x1e90ff, firebrick: 0xb22222, floralwhite: 0xfffaf0, forestgreen: 0x228b22, fuchsia: 0xff00ff, gainsboro: 0xdcdcdc, ghostwhite: 0xf8f8ff, gold: 0xffd700, goldenrod: 0xdaa520, gray: 0x808080, green: 0x008000, greenyellow: 0xadff2f, grey: 0x808080, honeydew: 0xf0fff0, hotpink: 0xff69b4, indianred: 0xcd5c5c, indigo: 0x4b0082, ivory: 0xfffff0, khaki: 0xf0e68c, lavender: 0xe6e6fa, lavenderblush: 0xfff0f5, lawngreen: 0x7cfc00, lemonchiffon: 0xfffacd, lightblue: 0xadd8e6, lightcoral: 0xf08080, lightcyan: 0xe0ffff, lightgoldenrodyellow: 0xfafad2, lightgray: 0xd3d3d3, lightgreen: 0x90ee90, lightgrey: 0xd3d3d3, lightpink: 0xffb6c1, lightsalmon: 0xffa07a, lightseagreen: 0x20b2aa, lightskyblue: 0x87cefa, lightslategray: 0x778899, lightslategrey: 0x778899, lightsteelblue: 0xb0c4de, lightyellow: 0xffffe0, lime: 0x00ff00, limegreen: 0x32cd32, linen: 0xfaf0e6, magenta: 0xff00ff, maroon: 0x800000, mediumaquamarine: 0x66cdaa, mediumblue: 0x0000cd, mediumorchid: 0xba55d3, mediumpurple: 0x9370db, mediumseagreen: 0x3cb371, mediumslateblue: 0x7b68ee, mediumspringgreen: 0x00fa9a, mediumturquoise: 0x48d1cc, mediumvioletred: 0xc71585, midnightblue: 0x191970, mintcream: 0xf5fffa, mistyrose: 0xffe4e1, moccasin: 0xffe4b5, navajowhite: 0xffdead, navy: 0x000080, oldlace: 0xfdf5e6, olive: 0x808000, olivedrab: 0x6b8e23, orange: 0xffa500, orangered: 0xff4500, orchid: 0xda70d6, palegoldenrod: 0xeee8aa, palegreen: 0x98fb98, paleturquoise: 0xafeeee, palevioletred: 0xdb7093, papayawhip: 0xffefd5, peachpuff: 0xffdab9, peru: 0xcd853f, pink: 0xffc0cb, plum: 0xdda0dd, powderblue: 0xb0e0e6, purple: 0x800080, rebeccapurple: 0x663399, red: 0xff0000, rosybrown: 0xbc8f8f, royalblue: 0x4169e1, saddlebrown: 0x8b4513, salmon: 0xfa8072, sandybrown: 0xf4a460, seagreen: 0x2e8b57, seashell: 0xfff5ee, sienna: 0xa0522d, silver: 0xc0c0c0, skyblue: 0x87ceeb, slateblue: 0x6a5acd, slategray: 0x708090, slategrey: 0x708090, snow: 0xfffafa, springgreen: 0x00ff7f, steelblue: 0x4682b4, tan: 0xd2b48c, teal: 0x008080, thistle: 0xd8bfd8, tomato: 0xff6347, turquoise: 0x40e0d0, violet: 0xee82ee, wheat: 0xf5deb3, white: 0xffffff, whitesmoke: 0xf5f5f5, yellow: 0xffff00, yellowgreen: 0x9acd32, }; const _hslA = {h: 0, s: 0, l: 0}; const _hslB = {h: 0, s: 0, l: 0}; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } class Color { constructor(r, g, b) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; return this.set(r, g, b); } set(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string const value = r; if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } } else { this.setRGB(r, g, b); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex, colorSpace = SRGBColorSpace) { hex = Math.floor(hex); this.r = ((hex >> 16) & 255) / 255; this.g = ((hex >> 8) & 255) / 255; this.b = (hex & 255) / 255; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setRGB(r, g, b, colorSpace = ColorManagement.workingColorSpace) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setHSL(h, s, l, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setStyle(style, colorSpace = SRGBColorSpace) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored.", ); } } let m; if ((m = /^(w+)(([^)]*))/.exec(style))) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if ( (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha(color[4]); return this.setRGB( Math.min(255, parseInt(color[1], 10)) / 255, Math.min(255, parseInt(color[2], 10)) / 255, Math.min(255, parseInt(color[3], 10)) / 255, colorSpace, ); } if ( (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha(color[4]); return this.setRGB( Math.min(100, parseInt(color[1], 10)) / 100, Math.min(100, parseInt(color[2], 10)) / 100, Math.min(100, parseInt(color[3], 10)) / 100, colorSpace, ); } break; case "hsl": case "hsla": if ( (color = /^s*(d*.?d+)s*,s*(d*.?d+)\%s*,s*(d*.?d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components, )) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha(color[4]); return this.setHSL( parseFloat(color[1]) / 360, parseFloat(color[2]) / 100, parseFloat(color[3]) / 100, colorSpace, ); } break; default: console.warn("THREE.Color: Unknown color model " + style); } } else if ((m = /^#([A-Fa-fd]+)$/.exec(style))) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 return this.setRGB( parseInt(hex.charAt(0), 16) / 15, parseInt(hex.charAt(1), 16) / 15, parseInt(hex.charAt(2), 16) / 15, colorSpace, ); } else if (size === 6) { // #ff0000 return this.setHex(parseInt(hex, 16), colorSpace); } else { console.warn("THREE.Color: Invalid hex color " + style); } } else if (style && style.length > 0) { return this.setColorName(style, colorSpace); } return this; } setColorName(style, colorSpace = SRGBColorSpace) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex, colorSpace); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); return ( Math.round(clamp(_color.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color.g * 255, 0, 255)) * 256 + Math.round(clamp(_color.b * 255, 0, 255)) ); } getHexString(colorSpace = SRGBColorSpace) { return ("000000" + this.getHex(colorSpace).toString(16)).slice(-6); } getHSL(target, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB(target, colorSpace = ColorManagement.workingColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; if (colorSpace !== SRGBColorSpace) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${colorSpace} ${r.toFixed(3)} ${g.toFixed(3)} ${b.toFixed(3)})`; } return `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`; } offsetHSL(h, s, l) { this.getHSL(_hslA); return this.setHSL(_hslA.h + h, _hslA.s + s, _hslA.l + l); } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } setFromVector3(v) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3(m) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[0] * r + e[3] * g + e[6] * b; this.g = e[1] * r + e[4] * g + e[7] * b; this.b = e[2] * r + e[5] * g + e[8] * b; return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); return this; } toJSON() { return this.getHex(); } *[Symbol.iterator]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/ new Color(); Color.NAMES = _colorKeywords; let _materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty(this, "id", {value: _materialId++}); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.blendColor = new Color(0, 0, 0); this.blendAlpha = 0; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild(/* shaderobject, renderer */) {} onBeforeRender(/* renderer, scene, camera, geometry, object, group */) {} onBeforeCompile(/* shaderobject, renderer */) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn( `THREE.Material: parameter "${key}" has value of undefined.`, ); continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn( `THREE.Material: "${key}" is not a property of THREE.${this.type}.`, ); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if ( currentValue && currentValue.isVector3 && newValue && newValue.isVector3 ) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (isRootObject) { meta = { textures: {}, images: {}, }; } const data = { metadata: { version: 4.6, type: "Material", generator: "Material.toJSON", }, }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity !== undefined && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.dispersion !== undefined) data.dispersion = this.dispersion; if (this.iridescence !== undefined) data.iridescence = this.iridescence; if (this.iridescenceIOR !== undefined) data.iridescenceIOR = this.iridescenceIOR; if (this.iridescenceThicknessRange !== undefined) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if (this.iridescenceMap && this.iridescenceMap.isTexture) { data.iridescenceMap = this.iridescenceMap.toJSON(meta).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON(meta).uuid; } if (this.anisotropy !== undefined) data.anisotropy = this.anisotropy; if (this.anisotropyRotation !== undefined) data.anisotropyRotation = this.anisotropyRotation; if (this.anisotropyMap && this.anisotropyMap.isTexture) { data.anisotropyMap = this.anisotropyMap.toJSON(meta).uuid; } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapRotation !== undefined) data.envMapRotation = this.envMapRotation.toArray(); if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors === true) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = true; if (this.blendSrc !== SrcAlphaFactor) data.blendSrc = this.blendSrc; if (this.blendDst !== OneMinusSrcAlphaFactor) data.blendDst = this.blendDst; if (this.blendEquation !== AddEquation) data.blendEquation = this.blendEquation; if (this.blendSrcAlpha !== null) data.blendSrcAlpha = this.blendSrcAlpha; if (this.blendDstAlpha !== null) data.blendDstAlpha = this.blendDstAlpha; if (this.blendEquationAlpha !== null) data.blendEquationAlpha = this.blendEquationAlpha; if (this.blendColor && this.blendColor.isColor) data.blendColor = this.blendColor.getHex(); if (this.blendAlpha !== 0) data.blendAlpha = this.blendAlpha; if (this.depthFunc !== LessEqualDepth) data.depthFunc = this.depthFunc; if (this.depthTest === false) data.depthTest = this.depthTest; if (this.depthWrite === false) data.depthWrite = this.depthWrite; if (this.colorWrite === false) data.colorWrite = this.colorWrite; if (this.stencilWriteMask !== 0xff) data.stencilWriteMask = this.stencilWriteMask; if (this.stencilFunc !== AlwaysStencilFunc) data.stencilFunc = this.stencilFunc; if (this.stencilRef !== 0) data.stencilRef = this.stencilRef; if (this.stencilFuncMask !== 0xff) data.stencilFuncMask = this.stencilFuncMask; if (this.stencilFail !== KeepStencilOp) data.stencilFail = this.stencilFail; if (this.stencilZFail !== KeepStencilOp) data.stencilZFail = this.stencilZFail; if (this.stencilZPass !== KeepStencilOp) data.stencilZPass = this.stencilZPass; if (this.stencilWrite === true) data.stencilWrite = this.stencilWrite; // rotation (SpriteMaterial) if (this.rotation !== undefined && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth !== undefined && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaHash === true) data.alphaHash = true; if (this.alphaToCoverage === true) data.alphaToCoverage = true; if (this.premultipliedAlpha === true) data.premultipliedAlpha = true; if (this.forceSinglePass === true) data.forceSinglePass = true; if (this.wireframe === true) data.wireframe = true; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = true; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (this.fog === false) data.fog = false; if (Object.keys(this.userData).length > 0) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRootObject) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.blendColor.copy(source.blendColor); this.blendAlpha = source.blendAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaHash = source.alphaHash; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({type: "dispose"}); } set needsUpdate(value) { if (value === true) this.version++; } } class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } // Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf const _tables = /*@__PURE__*/ _generateTables(); function _generateTables() { // float32 to float16 helpers const buffer = new ArrayBuffer(4); const floatView = new Float32Array(buffer); const uint32View = new Uint32Array(buffer); const baseTable = new Uint32Array(512); const shiftTable = new Uint32Array(512); for (let i = 0; i < 256; ++i) { const e = i - 127; // very small number (0, -0) if (e < -27) { baseTable[i] = 0x0000; baseTable[i | 0x100] = 0x8000; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // small number (denorm) } else if (e < -14) { baseTable[i] = 0x0400 >> (-e - 14); baseTable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; shiftTable[i] = -e - 1; shiftTable[i | 0x100] = -e - 1; // normal number } else if (e <= 15) { baseTable[i] = (e + 15) << 10; baseTable[i | 0x100] = ((e + 15) << 10) | 0x8000; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; // large number (Infinity, -Infinity) } else if (e < 128) { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // stay (NaN, Infinity, -Infinity) } else { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; } } // float16 to float32 helpers const mantissaTable = new Uint32Array(2048); const exponentTable = new Uint32Array(64); const offsetTable = new Uint32Array(64); for (let i = 1; i < 1024; ++i) { let m = i << 13; // zero pad mantissa bits let e = 0; // zero exponent // normalized while ((m & 0x00800000) === 0) { m <<= 1; e -= 0x00800000; // decrement exponent } m &= ~0x00800000; // clear leading 1 bit e += 0x38800000; // adjust bias mantissaTable[i] = m | e; } for (let i = 1024; i < 2048; ++i) { mantissaTable[i] = 0x38000000 + ((i - 1024) << 13); } for (let i = 1; i < 31; ++i) { exponentTable[i] = i << 23; } exponentTable[31] = 0x47800000; exponentTable[32] = 0x80000000; for (let i = 33; i < 63; ++i) { exponentTable[i] = 0x80000000 + ((i - 32) << 23); } exponentTable[63] = 0xc7800000; for (let i = 1; i < 64; ++i) { if (i !== 32) { offsetTable[i] = 1024; } } return { floatView: floatView, uint32View: uint32View, baseTable: baseTable, shiftTable: shiftTable, mantissaTable: mantissaTable, exponentTable: exponentTable, offsetTable: offsetTable, }; } // float32 to float16 function toHalfFloat(val) { if (Math.abs(val) > 65504) console.warn("THREE.DataUtils.toHalfFloat(): Value out of range."); val = clamp(val, -65504, 65504); _tables.floatView[0] = val; const f = _tables.uint32View[0]; const e = (f >> 23) & 0x1ff; return _tables.baseTable[e] + ((f & 0x007fffff) >> _tables.shiftTable[e]); } // float16 to float32 function fromHalfFloat(val) { const m = val >> 10; _tables.uint32View[0] = _tables.mantissaTable[_tables.offsetTable[m] + (val & 0x3ff)] + _tables.exponentTable[m]; return _tables.floatView[0]; } const DataUtils = { toHalfFloat: toHalfFloat, fromHalfFloat: fromHalfFloat, }; const _vector$9 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized = false) { if (Array.isArray(array)) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array.", ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this._updateRange = {offset: 0, count: -1}; this.updateRanges = []; this.gpuType = FloatType; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } get updateRange() { warnOnce( "THREE.BufferAttribute: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.", ); // @deprecated, r159 return this._updateRange; } setUsage(value) { this.usage = value; return this; } addUpdateRange(start, count) { this.updateRanges.push({start, count}); } clearUpdateRanges() { this.updateRanges.length = 0; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; this.gpuType = source.gpuType; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set(value, offset); return this; } getComponent(index, component) { let value = this.array[index * this.itemSize + component]; if (this.normalized) value = denormalize(value, this.array); return value; } setComponent(index, component, value) { if (this.normalized) value = normalize(value, this.array); this.array[index * this.itemSize + component] = value; return this; } getX(index) { let x = this.array[index * this.itemSize]; if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = x; return this; } getY(index) { let y = this.array[index * this.itemSize + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { let z = this.array[index * this.itemSize + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = z; return this; } getW(index) { let w = this.array[index * this.itemSize + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from(this.array), normalized: this.normalized, }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; return data; } } // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); this.isFloat16BufferAttribute = true; } getX(index) { let x = fromHalfFloat(this.array[index * this.itemSize]); if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = toHalfFloat(x); return this; } getY(index) { let y = fromHalfFloat(this.array[index * this.itemSize + 1]); if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = toHalfFloat(y); return this; } getZ(index) { let z = fromHalfFloat(this.array[index * this.itemSize + 2]); if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = toHalfFloat(z); return this; } getW(index) { let w = fromHalfFloat(this.array[index * this.itemSize + 3]); if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = toHalfFloat(w); return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); this.array[index + 3] = toHalfFloat(w); return this; } } class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } let _id$2 = 0; const _m1$2 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$2 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$8 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty(this, "id", {value: _id$2++}); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = {start: 0, count: Infinity}; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new ( arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute )(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex, }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1$2.makeRotationFromQuaternion(q); this.applyMatrix4(_m1$2); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1$2.makeRotationX(angle); this.applyMatrix4(_m1$2); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1$2.makeRotationY(angle); this.applyMatrix4(_m1$2); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1$2.makeRotationZ(angle); this.applyMatrix4(_m1$2); return this; } translate(x, y, z) { // translate geometry _m1$2.makeTranslation(x, y, z); this.applyMatrix4(_m1$2); return this; } scale(x, y, z) { // scale geometry _m1$2.makeScale(x, y, z); this.applyMatrix4(_m1$2); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.", this, ); this.boundingBox.set( new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity), ); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$2.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$2.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$2.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$2.min); this.boundingBox.expandByPoint(_box$2.max); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this, ); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.", this, ); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$2.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$2.min, _boxMorphTargets.min); _box$2.expandByPoint(_vector$8); _vector$8.addVectors(_box$2.max, _boxMorphTargets.max); _box$2.expandByPoint(_vector$8); } else { _box$2.expandByPoint(_boxMorphTargets.min); _box$2.expandByPoint(_boxMorphTargets.max); } } } _box$2.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared(_vector$8), ); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared(_vector$8), ); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this, ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)", ); return; } const positionAttribute = attributes.position; const normalAttribute = attributes.normal; const uvAttribute = attributes.uv; if (this.hasAttribute("tangent") === false) { this.setAttribute( "tangent", new BufferAttribute(new Float32Array(4 * positionAttribute.count), 4), ); } const tangentAttribute = this.getAttribute("tangent"); const tan1 = [], tan2 = []; for (let i = 0; i < positionAttribute.count; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromBufferAttribute(positionAttribute, a); vB.fromBufferAttribute(positionAttribute, b); vC.fromBufferAttribute(positionAttribute, c); uvA.fromBufferAttribute(uvAttribute, a); uvB.fromBufferAttribute(uvAttribute, b); uvC.fromBufferAttribute(uvAttribute, c); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir .copy(vB) .multiplyScalar(uvC.y) .addScaledVector(vC, -uvB.y) .multiplyScalar(r); tdir .copy(vC) .multiplyScalar(uvB.x) .addScaledVector(vB, -uvC.x) .multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [ { start: 0, count: index.count, }, ]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(index.getX(j + 0), index.getX(j + 1), index.getX(j + 2)); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromBufferAttribute(normalAttribute, v); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangentAttribute.setXYZW(v, tmp.x, tmp.y, tmp.z, w); } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(index.getX(j + 0)); handleVertex(index.getX(j + 1)); handleVertex(index.getX(j + 2)); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute( new Float32Array(positionAttribute.count * 3), 3, ); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.", ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.6, type: "BufferGeometry", generator: "BufferGeometry.toJSON", }, }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = {attributes: {}}; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array), }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius, }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent({type: "dispose"}); } } const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); const _ray$3 = /*@__PURE__*/ new Ray(); const _sphere$6 = /*@__PURE__*/ new Sphere(); const _sphereHitAt = /*@__PURE__*/ new Vector3(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _normalA = /*@__PURE__*/ new Vector3(); const _normalB = /*@__PURE__*/ new Vector3(); const _normalC = /*@__PURE__*/ new Vector3(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial(), ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary, ); } this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } getVertexPosition(index, target) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute(position, index); const morphInfluences = this.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, index); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); } else { _morphA.addScaledVector(_tempA.sub(target), influence); } } target.add(_morphA); } return target; } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$6.copy(geometry.boundingSphere); _sphere$6.applyMatrix4(matrixWorld); // check distance from ray origin to bounding sphere _ray$3.copy(raycaster.ray).recast(raycaster.near); if (_sphere$6.containsPoint(_ray$3.origin) === false) { if (_ray$3.intersectSphere(_sphere$6, _sphereHitAt) === null) return; if ( _ray$3.origin.distanceToSquared(_sphereHitAt) > (raycaster.far - raycaster.near) ** 2 ) return; } // convert ray to local space of mesh _inverseMatrix$3.copy(matrixWorld).invert(); _ray$3.copy(raycaster.ray).applyMatrix4(_inverseMatrix$3); // test with bounding box in local space if (geometry.boundingBox !== null) { if (_ray$3.intersectsBox(geometry.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$3); } _computeIntersections(raycaster, intersects, rayLocalSpace) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min( index.count, Math.min( group.start + group.count, drawRange.start + drawRange.count, ), ); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min( position.count, Math.min( group.start + group.count, drawRange.start + drawRange.count, ), ); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c, ); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } } function checkIntersection$1( object, material, raycaster, ray, pA, pB, pC, point, ) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side === FrontSide, point, ); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object, }; } function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c, ) { object.getVertexPosition(a, _vA$1); object.getVertexPosition(b, _vB$1); object.getVertexPosition(c, _vC$1); const intersection = checkIntersection$1( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint, ); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2(), ); } if (uv1) { _uvA$1.fromBufferAttribute(uv1, a); _uvB$1.fromBufferAttribute(uv1, b); _uvC$1.fromBufferAttribute(uv1, c); intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2(), ); } if (normal) { _normalA.fromBufferAttribute(normal, a); _normalB.fromBufferAttribute(normal, b); _normalC.fromBufferAttribute(normal, c); intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3(), ); if (intersection.normal.dot(ray.direction) > 0) { intersection.normal.multiplyScalar(-1); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0, }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1, ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments, }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0, ); // px buildPlane( "z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1, ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2, ); // py buildPlane( "x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3, ); // ny buildPlane( "x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4, ); // pz buildPlane( "x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5, ); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex, ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments, ); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if ( property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion) ) { if (property.isRenderTargetTexture) { console.warn( "UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().", ); dst[u][p] = null; } else { dst[u][p] = property.clone(); } } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } function cloneUniformsGroups(src) { const dst = []; for (let u = 0; u < src.length; u++) { dst.push(src[u].clone()); } return dst; } function getUnlitUniformColorSpace(renderer) { const currentRenderTarget = renderer.getRenderTarget(); if (currentRenderTarget === null) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } // https://github.com/mrdoob/three.js/issues/27868 if (currentRenderTarget.isXRRenderTarget === true) { return currentRenderTarget.texture.colorSpace; } return ColorManagement.workingColorSpace; } // Legacy const UniformsUtils = {clone: cloneUniforms, merge: mergeUniforms}; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor(parameters) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { clipCullDistance: false, // set to use vertex shader clipping multiDraw: false, // set to use vertex shader multi_draw / enable gl_DrawID }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { color: [1, 1, 1], uv: [0, 0], uv1: [0, 0], }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid, }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex(), }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray(), }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray(), }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray(), }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray(), }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray(), }; } else { data.uniforms[name] = { value: value, }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); this.coordinateSystem = WebGLCoordinateSystem; } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); this.coordinateSystem = source.coordinateSystem; return this; } getWorldDirection(target) { return super.getWorldDirection(target).negate(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } const _v3$1 = /*@__PURE__*/ new Vector3(); const _minTarget = /*@__PURE__*/ new Vector2(); const _maxTarget = /*@__PURE__*/ new Vector2(); class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = (0.5 * this.getFilmHeight()) / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return (0.5 * this.getFilmHeight()) / vExtentSlope; } getEffectiveFOV() { return ( RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom) ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Computes the 2D bounds of the camera"s viewable rectangle at a given distance along the viewing direction. * Sets minTarget and maxTarget to the coordinates of the lower-left and upper-right corners of the view rectangle. */ getViewBounds(distance, minTarget, maxTarget) { _v3$1.set(-1, -1, 0.5).applyMatrix4(this.projectionMatrixInverse); minTarget.set(_v3$1.x, _v3$1.y).multiplyScalar(-distance / _v3$1.z); _v3$1.set(1, 1, 0.5).applyMatrix4(this.projectionMatrixInverse); maxTarget.set(_v3$1.x, _v3$1.y).multiplyScalar(-distance / _v3$1.z); } /** * Computes the width and height of the camera"s viewable rectangle at a given distance along the viewing direction. * Copies the result into the target Vector2, where x is width and y is height. */ getViewSize(distance, target) { this.getViewBounds(distance, _minTarget, _maxTarget); return target.subVectors(_maxTarget, _minTarget); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1, }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = (near * Math.tan(DEG2RAD * 0.5 * this.fov)) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += (view.offsetX * width) / fullWidth; top -= (view.offsetY * height) / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += (near * skew) / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, ); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = -90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; this.renderTarget = renderTarget; this.coordinateSystem = null; this.activeMipmapLevel = 0; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; this.add(cameraNZ); } updateCoordinateSystem() { const coordinateSystem = this.coordinateSystem; const cameras = this.children.concat(); const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = cameras; for (const camera of cameras) this.remove(camera); if (coordinateSystem === WebGLCoordinateSystem) { cameraPX.up.set(0, 1, 0); cameraPX.lookAt(1, 0, 0); cameraNX.up.set(0, 1, 0); cameraNX.lookAt(-1, 0, 0); cameraPY.up.set(0, 0, -1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, 1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, 1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, 1, 0); cameraNZ.lookAt(0, 0, -1); } else if (coordinateSystem === WebGPUCoordinateSystem) { cameraPX.up.set(0, -1, 0); cameraPX.lookAt(-1, 0, 0); cameraNX.up.set(0, -1, 0); cameraNX.lookAt(1, 0, 0); cameraPY.up.set(0, 0, 1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, -1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(0, 0, -1); } else { throw new Error( "THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: " + coordinateSystem, ); } for (const camera of cameras) { this.add(camera); camera.updateMatrixWorld(); } } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const {renderTarget, activeMipmapLevel} = this; if (this.coordinateSystem !== renderer.coordinateSystem) { this.coordinateSystem = renderer.coordinateSystem; this.updateCoordinateSystem(); } const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentActiveCubeFace = renderer.getActiveCubeFace(); const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); const currentXrEnabled = renderer.xr.enabled; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0, activeMipmapLevel); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1, activeMipmapLevel); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2, activeMipmapLevel); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3, activeMipmapLevel); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4, activeMipmapLevel); renderer.render(scene, cameraPZ); // mipmaps are generated during the last call of render() // at this point, all sides of the cube render target are defined renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5, activeMipmapLevel); renderer.render(scene, cameraNZ); renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel, ); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size = 1, options = {}) { super(size, size, options); this.isWebGLCubeRenderTarget = true; const image = {width: size, height: size, depth: 1}; const images = [image, image, image, image, image, image]; this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace, ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: {value: null}, }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } `, }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending, }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1 .subVectors(c, b) .cross(_vector2.subVectors(a, b)) .normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target .copy(point) .addScaledVector(this.normal, -this.distanceToPoint(point)); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(line.start).addScaledVector(direction, t); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return (startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0); } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } const _sphere$5 = /*@__PURE__*/ new Sphere(); const _vector$7 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane(), ) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m, coordinateSystem = WebGLCoordinateSystem) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0] .setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12) .normalize(); planes[1] .setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12) .normalize(); planes[2] .setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13) .normalize(); planes[3] .setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13) .normalize(); planes[4] .setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14) .normalize(); if (coordinateSystem === WebGLCoordinateSystem) { planes[5] .setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14) .normalize(); } else if (coordinateSystem === WebGPUCoordinateSystem) { planes[5].setComponents(me2, me6, me10, me14).normalize(); } else { throw new Error( "THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: " + coordinateSystem, ); } return this; } intersectsObject(object) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _sphere$5.copy(object.boundingSphere).applyMatrix4(object.matrixWorld); } else { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$5.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); } return this.intersectsSphere(_sphere$5); } intersectsSprite(sprite) { _sphere$5.center.set(0, 0, 0); _sphere$5.radius = 0.7071067811865476; _sphere$5.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$5); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; }, }; } function WebGLAttributes(gl) { const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const size = array.byteLength; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { type = gl.HALF_FLOAT; } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array, ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version, size: size, }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute._updateRange; // @deprecated, r159 const updateRanges = attribute.updateRanges; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1 && updateRanges.length === 0) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } if (updateRanges.length !== 0) { for (let i = 0, l = updateRanges.length; i < l; i++) { const range = updateRanges[i]; gl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT, array, range.start, range.count, ); } attribute.clearUpdateRanges(); } // @deprecated, r159 if (updateRange.count !== -1) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count, ); updateRange.count = -1; // reset range } attribute.onUploadCallback(); } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version, }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { if (data.size !== attribute.array.byteLength) { throw new Error( "THREE.WebGLAttributes: The size of the buffer attribute"s array buffer does not match the original size. Resizing buffer attributes is not supported.", ); } updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update, }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments, }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments, ); } } var alphahash_fragment = "#ifdef USE_ALPHAHASH if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; #endif"; var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH const float ALPHA_HASH_SCALE = 0.05; float hash2D( vec2 value ) { return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); } float hash3D( vec3 value ) { return hash2D( vec2( hash2D( value.xy ), value.z ) ); } float getAlphaHashThreshold( vec3 position ) { float maxDeriv = max( length( dFdx( position.xyz ) ), length( dFdy( position.xyz ) ) ); float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); vec2 pixScales = vec2( exp2( floor( log2( pixScale ) ) ), exp2( ceil( log2( pixScale ) ) ) ); vec2 alpha = vec2( hash3D( floor( pixScales.x * position.xyz ) ), hash3D( floor( pixScales.y * position.xyz ) ) ); float lerpFactor = fract( log2( pixScale ) ); float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; float a = min( lerpFactor, 1.0 - lerpFactor ); vec3 cases = vec3( x * x / ( 2.0 * a * ( 1.0 - a ) ), ( x - 0.5 * a ) / ( 1.0 - a ), 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) ); float threshold = ( x < ( 1.0 - a ) ) ? ( ( x < a ) ? cases.x : cases.y ) : cases.z; return clamp( threshold , 1.0e-6, 1.0 ); } #endif"; var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST #ifdef ALPHA_TO_COVERAGE diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a ); if ( diffuseColor.a == 0.0 ) discard; #else if ( diffuseColor.a < alphaTest ) discard; #endif #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_CLEARCOAT ) clearcoatSpecularIndirect *= ambientOcclusion; #endif #if defined( USE_SHEEN ) sheenSpecularIndirect *= ambientOcclusion; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var batching_pars_vertex = "#ifdef USE_BATCHING attribute float batchId; uniform highp sampler2D batchingTexture; mat4 getBatchingMatrix( const in float i ) { int size = textureSize( batchingTexture, 0 ).x; int j = int( i ) * 4; int x = j % size; int y = j / size; vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } #endif #ifdef USE_BATCHING_COLOR uniform sampler2D batchingColorTexture; vec3 getBatchingColor( const in float i ) { int size = textureSize( batchingColorTexture, 0 ).x; int j = int( i ); int x = j % size; int y = j / size; return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb; } #endif"; var batching_vertex = "#ifdef USE_BATCHING mat4 batchingMatrix = getBatchingMatrix( batchId ); #endif"; var begin_vertex = "vec3 transformed = vec3( position ); #ifdef USE_ALPHAHASH vPosition = vec3( position ); #endif"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 rgb = XYZ_TO_REC709 * xyz; return rgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++ m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vBumpMapUv ); vec2 dSTdy = dFdy( vBumpMapUv ); float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) ); vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #ifdef ALPHA_TO_COVERAGE float distanceToPlane, distanceGradient; float clipOpacity = 1.0; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; distanceGradient = fwidth( distanceToPlane ) / 2.0; clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); if ( clipOpacity == 0.0 ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES float unionClipOpacity = 1.0; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; distanceGradient = fwidth( distanceToPlane ) / 2.0; unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); } #pragma unroll_loop_end clipOpacity *= 1.0 - unionClipOpacity; #endif diffuseColor.a *= clipOpacity; if ( diffuseColor.a == 0.0 ) discard; #else #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif #ifdef USE_BATCHING_COLOR vec3 batchingColor = getBatchingColor( batchId ); vColor.xyz *= batchingColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; #ifdef USE_ALPHAHASH varying vec3 vPosition; #endif vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float luminance( const in vec3 rgb ) { const vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 ); return dot( weights, rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); } vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define cubeUV_r0 1.0 #define cubeUV_m0 - 2.0 #define cubeUV_r1 0.8 #define cubeUV_m1 - 1.0 #define cubeUV_r4 0.4 #define cubeUV_m4 2.0 #define cubeUV_r5 0.305 #define cubeUV_m5 3.0 #define cubeUV_r6 0.21 #define cubeUV_m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= cubeUV_r1 ) { mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; } else if ( roughness >= cubeUV_r4 ) { mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; } else if ( roughness >= cubeUV_r5 ) { mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; } else if ( roughness >= cubeUV_r6 ) { mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_TANGENT vec3 transformedTangent = objectTangent; #endif #ifdef USE_BATCHING mat3 bm = mat3( batchingMatrix ); transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); transformedNormal = bm * transformedNormal; #ifdef USE_TANGENT transformedTangent = bm * transformedTangent; #endif #endif #ifdef USE_INSTANCING mat3 im = mat3( instanceMatrix ); transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); transformedNormal = im * transformedNormal; #ifdef USE_TANGENT transformedTangent = im * transformedTangent; #endif #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = " const mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3( vec3( 0.8224621, 0.177538, 0.0 ), vec3( 0.0331941, 0.9668058, 0.0 ), vec3( 0.0170827, 0.0723974, 0.9105199 ) ); const mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3( vec3( 1.2249401, - 0.2249404, 0.0 ), vec3( - 0.0420569, 1.0420571, 0.0 ), vec3( - 0.0196376, - 0.0786361, 1.0982735 ) ); vec4 LinearSRGBToLinearDisplayP3( in vec4 value ) { return vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a ); } vec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) { return vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a ); } vec4 LinearTransferOETF( in vec4 value ) { return value; } vec4 sRGBTransferOETF( in vec4 value ) { return 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 ); } vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return sRGBTransferOETF( value ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; uniform mat3 envMapRotation; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif }"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_fragment = "LambertMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; }; void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert #define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; #if defined( USE_LIGHT_PROBES ) uniform vec3 lightProbe[ 9 ]; #endif vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { vec3 lVector = pointLight.position - geometryPosition; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { vec3 lVector = spotLight.position - geometryPosition; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP vec3 getIBLIrradiance( const in vec3 normal ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #ifdef USE_ANISOTROPY vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 bentNormal = cross( bitangent, viewDir ); bentNormal = normalize( cross( bentNormal, bitangent ) ); bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); return getIBLRadiance( viewDir, bentNormal, roughness ); #else return vec3( 0.0 ); #endif } #endif #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR material.ior = ior; #ifdef USE_SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULAR_COLORMAP specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; #endif #ifdef USE_SPECULAR_INTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_DISPERSION material.dispersion = dispersion; #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEEN_COLORMAP material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEEN_ROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; #endif #endif #ifdef USE_ANISOTROPY #ifdef USE_ANISOTROPYMAP mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; #else vec2 anisotropyV = anisotropyVector; #endif material.anisotropy = length( anisotropyV ); if( material.anisotropy == 0.0 ) { anisotropyV = vec2( 1.0, 0.0 ); } else { anisotropyV /= material.anisotropy; material.anisotropy = saturate( material.anisotropy ); } material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; float dispersion; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif #ifdef IOR float ior; #endif #ifdef USE_TRANSMISSION float transmission; float transmissionAlpha; float thickness; float attenuationDistance; vec3 attenuationColor; #endif #ifdef USE_ANISOTROPY float anisotropy; float alphaT; vec3 anisotropyT; vec3 anisotropyB; #endif }; vec3 clearcoatSpecularDirect = vec3( 0.0 ); vec3 clearcoatSpecularIndirect = vec3( 0.0 ); vec3 sheenSpecularDirect = vec3( 0.0 ); vec3 sheenSpecularIndirect = vec3(0.0 ); vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } #ifdef USE_ANISOTROPY float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); float v = 0.5 / ( gv + gl ); return saturate(v); } float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { float a2 = alphaT * alphaB; highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); highp float v2 = dot( v, v ); float w2 = a2 / v2; return RECIPROCAL_PI * a2 * pow2 ( w2 ); } #endif #ifdef USE_CLEARCOAT vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { vec3 f0 = material.clearcoatF0; float f90 = material.clearcoatF90; float roughness = material.clearcoatRoughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { vec3 f0 = material.specularColor; float f90 = material.specularF90; float roughness = material.roughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); #ifdef USE_IRIDESCENCE F = mix( F, material.iridescenceFresnel, material.iridescence ); #endif #ifdef USE_ANISOTROPY float dotTL = dot( material.anisotropyT, lightDir ); float dotTV = dot( material.anisotropyT, viewDir ); float dotTH = dot( material.anisotropyT, halfDir ); float dotBL = dot( material.anisotropyB, lightDir ); float dotBV = dot( material.anisotropyB, viewDir ); float dotBH = dot( material.anisotropyB, halfDir ); float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); #else float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); #endif return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometryNormal; vec3 viewDir = geometryViewDir; vec3 position = geometryPosition; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); #endif #ifdef USE_SHEEN sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { #ifdef USE_CLEARCOAT clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " vec3 geometryPosition = - vViewPosition; vec3 geometryNormal = normal; vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); vec3 geometryClearcoatNormal = vec3( 0.0 ); #ifdef USE_CLEARCOAT geometryClearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometryViewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometryPosition, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; vec4 spotColor; vec3 spotLightCoord; bool inSpotLightMap; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometryPosition, directLight ); #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS #else #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #endif #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; #endif #undef SPOT_LIGHT_MAP_INDEX #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); #if defined( USE_LIGHT_PROBES ) irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); #endif #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometryNormal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) #ifdef USE_ANISOTROPY radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); #else radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); #endif #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vMapUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphinstance_vertex = "#ifdef USE_INSTANCING_MORPH float morphTargetInfluences[ MORPHTARGETS_COUNT ]; float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; } #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS #ifndef USE_INSTANCING_MORPH uniform float morphTargetBaseInfluence; uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; #endif uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal *= faceDirection; #endif #endif #if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) #ifdef USE_TANGENT mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn = getTangentFrame( - vViewPosition, normal, #if defined( USE_NORMALMAP ) vNormalMapUv #elif defined( USE_CLEARCOAT_NORMALMAP ) vClearcoatNormalMapUv #else vUv #endif ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn[0] *= faceDirection; tbn[1] *= faceDirection; #endif #endif #ifdef USE_CLEARCOAT_NORMALMAP #ifdef USE_TANGENT mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn2[0] *= faceDirection; tbn2[1] *= faceDirection; #endif #endif vec3 nonPerturbedNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( USE_NORMALMAP_TANGENTSPACE ) vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef USE_NORMALMAP_OBJECTSPACE uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { vec3 q0 = dFdx( eye_pos.xyz ); vec3 q1 = dFdy( eye_pos.xyz ); vec2 st0 = dFdx( uv.st ); vec2 st1 = dFdy( uv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = nonPerturbedNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var opaque_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec2 packDepthToRG( in highp float v ) { return packDepthToRGBA( v ).yx; } float unpackRGToDepth( const in highp vec2 v ) { return unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { return depth * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * depth - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING mvPosition = batchingMatrix * mvPosition; #endif #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; bool frustumTest = inFrustum && shadowCoord.z <= 1.0; if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { float shadow = 1.0; vec3 lightToPosition = shadowCoord.xyz; float lightToPositionLength = length( lightToPosition ); if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) { float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; shadow = ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } return shadow; } #endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if defined( USE_SHADOWMAP ) #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif #if NUM_SPOT_LIGHT_COORDS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { shadowWorldPosition = worldPosition; #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; #endif vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; mat4 getBoneMatrix( const in float i ) { int size = textureSize( boneTexture, 0 ).x; int j = int( i ) * 4; int x = j % size; int y = j / size; vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return saturate( toneMappingExposure * color ); } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( vec3( 1.6605, - 0.1246, - 0.0182 ), vec3( - 0.5876, 1.1329, - 0.1006 ), vec3( - 0.0728, - 0.0083, 1.1187 ) ); const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( vec3( 0.6274, 0.0691, 0.0164 ), vec3( 0.3293, 0.9195, 0.0880 ), vec3( 0.0433, 0.0113, 0.8956 ) ); vec3 agxDefaultContrastApprox( vec3 x ) { vec3 x2 = x * x; vec3 x4 = x2 * x2; return + 15.5 * x4 * x2 - 40.14 * x4 * x + 31.96 * x4 - 6.868 * x2 * x + 0.4298 * x2 + 0.1191 * x - 0.00232; } vec3 AgXToneMapping( vec3 color ) { const mat3 AgXInsetMatrix = mat3( vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) ); const mat3 AgXOutsetMatrix = mat3( vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) ); const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; color *= toneMappingExposure; color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; color = AgXInsetMatrix * color; color = max( color, 1e-10 ); color = log2( color ); color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); color = clamp( color, 0.0, 1.0 ); color = agxDefaultContrastApprox( color ); color = AgXOutsetMatrix * color; color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); color = LINEAR_REC2020_TO_LINEAR_SRGB * color; color = clamp( color, 0.0, 1.0 ); return color; } vec3 NeutralToneMapping( vec3 color ) { const float StartCompression = 0.8 - 0.04; const float Desaturation = 0.15; color *= toneMappingExposure; float x = min( color.r, min( color.g, color.b ) ); float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; color -= offset; float peak = max( color.r, max( color.g, color.b ) ); if ( peak < StartCompression ) return color; float d = 1. - StartCompression; float newPeak = 1. - d * d / ( peak + d - StartCompression ); color *= newPeak / peak; float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); return mix( color, vec3( newPeak ), g ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; material.attenuationDistance = attenuationDistance; material.attenuationColor = attenuationColor; #ifdef USE_TRANSMISSIONMAP material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; #endif #ifdef USE_THICKNESSMAP material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmitted = getIBLVolumeRefraction( n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; float w0( float a ) { return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); } float w1( float a ) { return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); } float w2( float a ){ return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); } float w3( float a ) { return ( 1.0 / 6.0 ) * ( a * a * a ); } float g0( float a ) { return w0( a ) + w1( a ); } float g1( float a ) { return w2( a ) + w3( a ); } float h0( float a ) { return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); } float h1( float a ) { return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); } vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { uv = uv * texelSize.zw + 0.5; vec2 iuv = floor( uv ); vec2 fuv = fract( uv ); float g0x = g0( fuv.x ); float g1x = g1( fuv.x ); float h0x = h0( fuv.x ); float h1x = h1( fuv.x ); float h0y = h0( fuv.y ); float h1y = h1( fuv.y ); vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); } vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); vec2 fLodSizeInv = 1.0 / fLodSize; vec2 cLodSizeInv = 1.0 / cLodSize; vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); return mix( fSample, cSample, fract( lod ) ); } vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); } vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( isinf( attenuationDistance ) ) { return vec3( 1.0 ); } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec4 transmittedLight; vec3 transmittance; #ifdef USE_DISPERSION float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); for ( int i = 0; i < 3; i ++ ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); transmittedLight[ i ] = transmissionSample[ i ]; transmittedLight.a += transmissionSample.a; transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; } transmittedLight.a /= 3.0; #else vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); #endif vec3 attenuatedColor = transmittance * transmittedLight.rgb; vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); } #endif"; var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP varying vec2 vNormalMapUv; #endif #ifdef USE_EMISSIVEMAP varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SHEEN_COLORMAP varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_SPECULARMAP varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP uniform mat3 mapTransform; varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP uniform mat3 alphaMapTransform; varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP uniform mat3 lightMapTransform; varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP uniform mat3 aoMapTransform; varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP uniform mat3 bumpMapTransform; varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP uniform mat3 normalMapTransform; varying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP uniform mat3 displacementMapTransform; varying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP uniform mat3 emissiveMapTransform; varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP uniform mat3 metalnessMapTransform; varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP uniform mat3 roughnessMapTransform; varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP uniform mat3 anisotropyMapTransform; varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP uniform mat3 clearcoatMapTransform; varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform mat3 clearcoatNormalMapTransform; varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform mat3 clearcoatRoughnessMapTransform; varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP uniform mat3 sheenColorMapTransform; varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform mat3 sheenRoughnessMapTransform; varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP uniform mat3 iridescenceMapTransform; varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform mat3 iridescenceThicknessMapTransform; varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP uniform mat3 specularMapTransform; varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP uniform mat3 specularColorMapTransform; varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform mat3 specularIntensityMapTransform; varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ANISOTROPYMAP vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING worldPosition = batchingMatrix * worldPosition; #endif #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$h = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$h = "uniform sampler2D t2D; uniform float backgroundIntensity; varying vec2 vUv; void main() { vec4 texColor = texture2D( t2D, vUv ); #ifdef DECODE_VIDEO_TEXTURE texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$g = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #elif defined( ENVMAP_TYPE_CUBE_UV ) uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform float backgroundBlurriness; uniform float backgroundIntensity; uniform mat3 backgroundRotation; varying vec3 vWorldDirection; #include void main() { #ifdef ENVMAP_TYPE_CUBE vec4 texColor = textureCube( envMap, backgroundRotation * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); #else vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldDirection; void main() { vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); gl_FragColor = texColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { vec4 diffuseColor = vec4( 1.0 ); #include #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include #include void main () { vec4 diffuseColor = vec4( 1.0 ); #include #include #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$9 = "#define LAMBERT uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_DISPERSION uniform float dispersion; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif #ifdef USE_ANISOTROPY uniform vec2 anisotropyVector; #ifdef USE_ANISOTROPYMAP uniform sampler2D anisotropyMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include #ifdef USE_POINTS_UV varying vec2 vUv; uniform mat3 uvTransform; #endif void main() { #ifdef USE_POINTS_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif #include #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include vec3 outgoingLight = vec3( 0.0 ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include vec3 outgoingLight = vec3( 0.0 ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphahash_fragment: alphahash_fragment, alphahash_pars_fragment: alphahash_pars_fragment, alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, batching_pars_vertex: batching_pars_vertex, batching_vertex: batching_vertex, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, colorspace_fragment: colorspace_fragment, colorspace_pars_fragment: colorspace_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphinstance_vertex: morphinstance_vertex, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, opaque_fragment: opaque_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1, }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, map: {value: null}, mapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, }, specularmap: { specularMap: {value: null}, specularMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, envmap: { envMap: {value: null}, envMapRotation: {value: /*@__PURE__*/ new Matrix3()}, flipEnvMap: {value: -1}, reflectivity: {value: 1.0}, // basic, lambert, phong ior: {value: 1.5}, // physical refractionRatio: {value: 0.98}, // basic, lambert, phong }, aomap: { aoMap: {value: null}, aoMapIntensity: {value: 1}, aoMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, lightmap: { lightMap: {value: null}, lightMapIntensity: {value: 1}, lightMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, bumpmap: { bumpMap: {value: null}, bumpMapTransform: {value: /*@__PURE__*/ new Matrix3()}, bumpScale: {value: 1}, }, normalmap: { normalMap: {value: null}, normalMapTransform: {value: /*@__PURE__*/ new Matrix3()}, normalScale: {value: /*@__PURE__*/ new Vector2(1, 1)}, }, displacementmap: { displacementMap: {value: null}, displacementMapTransform: {value: /*@__PURE__*/ new Matrix3()}, displacementScale: {value: 1}, displacementBias: {value: 0}, }, emissivemap: { emissiveMap: {value: null}, emissiveMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, metalnessmap: { metalnessMap: {value: null}, metalnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, roughnessmap: { roughnessMap: {value: null}, roughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, gradientmap: { gradientMap: {value: null}, }, fog: { fogDensity: {value: 0.00025}, fogNear: {value: 1}, fogFar: {value: 2000}, fogColor: {value: /*@__PURE__*/ new Color(0xffffff)}, }, 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: {}, }, }, spotLightMap: {value: []}, spotShadowMap: {value: []}, spotLightMatrix: {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: {}, }, }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {}, }, }, ltc_1: {value: null}, ltc_2: {value: null}, }, points: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, size: {value: 1.0}, scale: {value: 1.0}, map: {value: null}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, uvTransform: {value: /*@__PURE__*/ new Matrix3()}, }, sprite: { diffuse: {value: /*@__PURE__*/ new Color(0xffffff)}, opacity: {value: 1.0}, center: {value: /*@__PURE__*/ new Vector2(0.5, 0.5)}, rotation: {value: 0.0}, map: {value: null}, mapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaMap: {value: null}, alphaMapTransform: {value: /*@__PURE__*/ new Matrix3()}, alphaTest: {value: 0}, }, }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog, ]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag, }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, }, ]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag, }, phong: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, specular: {value: /*@__PURE__*/ new Color(0x111111)}, shininess: {value: 30}, }, ]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag, }, standard: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, roughness: {value: 1.0}, metalness: {value: 0.0}, envMapIntensity: {value: 1}, }, ]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag, }, toon: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: {value: /*@__PURE__*/ new Color(0x000000)}, }, ]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag, }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: {value: null}, }, ]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag, }, points: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.points, UniformsLib.fog, ]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag, }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.fog, { scale: {value: 1}, dashSize: {value: 1}, totalSize: {value: 2}, }, ]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag, }, depth: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.displacementmap, ]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag, }, normal: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: {value: 1.0}, }, ]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag, }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.sprite, UniformsLib.fog, ]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag, }, background: { uniforms: { uvTransform: {value: /*@__PURE__*/ new Matrix3()}, t2D: {value: null}, backgroundIntensity: {value: 1}, }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag, }, backgroundCube: { uniforms: { envMap: {value: null}, flipEnvMap: {value: -1}, backgroundBlurriness: {value: 0}, backgroundIntensity: {value: 1}, backgroundRotation: {value: /*@__PURE__*/ new Matrix3()}, }, vertexShader: ShaderChunk.backgroundCube_vert, fragmentShader: ShaderChunk.backgroundCube_frag, }, cube: { uniforms: { tCube: {value: null}, tFlip: {value: -1}, opacity: {value: 1.0}, }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag, }, equirect: { uniforms: { tEquirect: {value: null}, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag, }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: {value: /*@__PURE__*/ new Vector3()}, nearDistance: {value: 1}, farDistance: {value: 1000}, }, ]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag, }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms([ UniformsLib.lights, UniformsLib.fog, { color: {value: /*@__PURE__*/ new Color(0x00000)}, opacity: {value: 1.0}, }, ]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag, }, }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms([ ShaderLib.standard.uniforms, { clearcoat: {value: 0}, clearcoatMap: {value: null}, clearcoatMapTransform: {value: /*@__PURE__*/ new Matrix3()}, clearcoatNormalMap: {value: null}, clearcoatNormalMapTransform: {value: /*@__PURE__*/ new Matrix3()}, clearcoatNormalScale: {value: /*@__PURE__*/ new Vector2(1, 1)}, clearcoatRoughness: {value: 0}, clearcoatRoughnessMap: {value: null}, clearcoatRoughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, dispersion: {value: 0}, iridescence: {value: 0}, iridescenceMap: {value: null}, iridescenceMapTransform: {value: /*@__PURE__*/ new Matrix3()}, iridescenceIOR: {value: 1.3}, iridescenceThicknessMinimum: {value: 100}, iridescenceThicknessMaximum: {value: 400}, iridescenceThicknessMap: {value: null}, iridescenceThicknessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, sheen: {value: 0}, sheenColor: {value: /*@__PURE__*/ new Color(0x000000)}, sheenColorMap: {value: null}, sheenColorMapTransform: {value: /*@__PURE__*/ new Matrix3()}, sheenRoughness: {value: 1}, sheenRoughnessMap: {value: null}, sheenRoughnessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, transmission: {value: 0}, transmissionMap: {value: null}, transmissionMapTransform: {value: /*@__PURE__*/ new Matrix3()}, transmissionSamplerSize: {value: /*@__PURE__*/ new Vector2()}, transmissionSamplerMap: {value: null}, thickness: {value: 0}, thicknessMap: {value: null}, thicknessMapTransform: {value: /*@__PURE__*/ new Matrix3()}, attenuationDistance: {value: 0}, attenuationColor: {value: /*@__PURE__*/ new Color(0x000000)}, specularColor: {value: /*@__PURE__*/ new Color(1, 1, 1)}, specularColorMap: {value: null}, specularColorMapTransform: {value: /*@__PURE__*/ new Matrix3()}, specularIntensity: {value: 1}, specularIntensityMap: {value: null}, specularIntensityMapTransform: {value: /*@__PURE__*/ new Matrix3()}, anisotropyVector: {value: /*@__PURE__*/ new Vector2()}, anisotropyMap: {value: null}, anisotropyMapTransform: {value: /*@__PURE__*/ new Matrix3()}, }, ]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag, }; const _rgb = {r: 0, b: 0, g: 0}; const _e1$1 = /*@__PURE__*/ new Euler(); const _m1$1 = /*@__PURE__*/ new Matrix4(); function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha, ) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function getBackground(scene) { let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = (usePMREM ? cubeuvmaps : cubemaps).get(background); } return background; } function render(scene) { let forceClear = false; const background = getBackground(scene); if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } const environmentBlendMode = renderer.xr.getEnvironmentBlendMode(); if (environmentBlendMode === "additive") { state.buffers.color.setClear(0, 0, 0, 1, premultipliedAlpha); } else if (environmentBlendMode === "alpha-blend") { state.buffers.color.setClear(0, 0, 0, 0, premultipliedAlpha); } if (renderer.autoClear || forceClear) { // buffers might not be writable which is required to ensure a correct clear state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil, ); } } function addToRenderList(renderList, scene) { const background = getBackground(scene); if ( background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping) ) { if (boxMesh === undefined) { boxMesh = new Mesh( new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.backgroundCube.uniforms), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false, }), ); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; }, }); objects.update(boxMesh); } _e1$1.copy(scene.backgroundRotation); // accommodate left-handed frame _e1$1.x *= -1; _e1$1.y *= -1; _e1$1.z *= -1; if ( background.isCubeTexture && background.isRenderTargetTexture === false ) { // environment maps which are not cube render targets or PMREMs follow a different convention _e1$1.y *= -1; _e1$1.z *= -1; } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.uniforms.backgroundRotation.value.setFromMatrix4( _m1$1.makeRotationFromEuler(_e1$1), ); boxMesh.material.toneMapped = ColorManagement.getTransfer(background.colorSpace) !== SRGBTransfer; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null, ); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh( new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false, }), ); planeMesh.geometry.deleteAttribute("normal"); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; }, }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = ColorManagement.getTransfer(background.colorSpace) !== SRGBTransfer; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null, ); } } function setClear(color, alpha) { color.getRGB(_rgb, getUnlitUniformColorSpace(renderer)); state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha, ); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render, addToRenderList: addToRenderList, }; } function WebGLBindingStates(gl, attributes) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; let forceUpdate = false; function setup(object, material, program, geometry, index) { let updateBuffers = false; const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(object, geometry, program, index); if (updateBuffers) saveCache(object, geometry, program, index); if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers || forceUpdate) { forceUpdate = false; setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { return gl.createVertexArray(); } function bindVertexArrayObject(vao) { return gl.bindVertexArray(vao); } function deleteVertexArrayObject(vao) { return gl.deleteVertexArray(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null, }; } function needsUpdate(object, geometry, program, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { const cachedAttribute = cachedAttributes[name]; let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum++; } } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(object, geometry, program, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let attribute = attributes[name]; if (attribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) attribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if (attribute && attribute.data) { data.data = attribute.data; } cache[name] = data; attributesNum++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { gl.vertexAttribDivisor(attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset, integer, ) { if (integer === true) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; // check for integer attributes const integer = type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute, ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + (size / programAttribute.locationSize) * i) * bytesPerElement, integer, ); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute, ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, (size / programAttribute.locationSize) * i * bytesPerElement, integer, ); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); forceUpdate = true; if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes, }; } function WebGLBufferRenderer(gl, extensions, info) { let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; gl.drawArraysInstanced(mode, start, count, primcount); info.update(count, mode, primcount); } function renderMultiDraw(starts, counts, drawCount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < drawCount; i++) { this.render(starts[i], counts[i]); } } else { extension.multiDrawArraysWEBGL(mode, starts, 0, counts, 0, drawCount); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } info.update(elementCount, mode, 1); } } function renderMultiDrawInstances(starts, counts, drawCount, primcount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < starts.length; i++) { renderInstances(starts[i], counts[i], primcount[i]); } } else { extension.multiDrawArraysInstancedWEBGL( mode, starts, 0, counts, 0, primcount, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } for (let i = 0; i < primcount.length; i++) { info.update(elementCount, mode, primcount[i]); } } } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; this.renderMultiDraw = renderMultiDraw; this.renderMultiDrawInstances = renderMultiDrawInstances; } function WebGLCapabilities(gl, extensions, parameters, utils) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function textureFormatReadable(textureFormat) { if ( textureFormat !== RGBAFormat && utils.convert(textureFormat) !== gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT) ) { return false; } return true; } function textureTypeReadable(textureType) { const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || extensions.has("EXT_color_buffer_float")); if ( textureType !== UnsignedByteType && utils.convert(textureType) !== gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) textureType !== FloatType && !halfFloatSupportedByExt ) { return false; } return true; } function getMaxPrecision(precision) { if (precision === "highp") { if ( gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT) .precision > 0 ) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if ( gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT) .precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT) .precision > 0 ) { return "mediump"; } } return "lowp"; } let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead.", ); precision = maxPrecision; } const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const maxSamples = gl.getParameter(gl.MAX_SAMPLES); return { isWebGL2: true, // keeping this for backwards compatibility getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, textureFormatReadable: textureFormatReadable, textureTypeReadable: textureTypeReadable, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, maxSamples: maxSamples, }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = {value: null, needsUpdate: false}; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function (planes, camera) { globalState = projectPlanes(planes, camera, 0); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if ( !localClippingEnabled || planes === null || planes.length === 0 || (renderingShadows && !clipShadows) ) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose, }; } class OrthographicCamera extends Camera { constructor( left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000, ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1, }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem, ); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; let _oldActiveCubeFace = 0; let _oldActiveMipmapLevel = 0; let _oldXrEnabled = false; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3(-PHI, INV_PHI, 0), /*@__PURE__*/ new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/ new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/ new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/ new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/ new Vector3(0, PHI, INV_PHI), /*@__PURE__*/ new Vector3(-1, 1, -1), /*@__PURE__*/ new Vector3(1, 1, -1), /*@__PURE__*/ new Vector3(-1, 1, 1), /*@__PURE__*/ new Vector3(1, 1, 1), ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); _oldActiveCubeFace = this._renderer.getActiveCubeFace(); _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); _oldXrEnabled = this._renderer.xr.enabled; this._renderer.xr.enabled = false; this._setSize(256); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. * The smallest supported equirectangular image size is 64 x 32. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. * The smallest supported cube size is 16 x 16. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial(this._cubemapMaterial); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial(this._equirectMaterial); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if (this._cubemapMaterial !== null) this._cubemapMaterial.dispose(); if (this._equirectMaterial !== null) this._equirectMaterial.dispose(); } // private interface _setSize(cubeSize) { this._lodMax = Math.floor(Math.log2(cubeSize)); this._cubeSize = Math.pow(2, this._lodMax); } _dispose() { if (this._blurMaterial !== null) this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); for (let i = 0; i < this._lodPlanes.length; i++) { this._lodPlanes[i].dispose(); } } _cleanup(outputTarget) { this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel, ); this._renderer.xr.enabled = _oldXrEnabled; outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : texture.image[0].width || texture.image[0].image.width, ); } else { // Equirectangular this._setSize(texture.image.width / 4); } _oldTarget = this._renderer.getRenderTarget(); _oldActiveCubeFace = this._renderer.getActiveCubeFace(); _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); _oldXrEnabled = this._renderer.xr.enabled; this._renderer.xr.enabled = false; const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max(this._cubeSize, 16 * 7); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false, }; const cubeUVRenderTarget = _createRenderTarget(width, height, params); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { if (this._pingPongRenderTarget !== null) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget(width, height, params); const {_lodMax} = this; ({ sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas, } = _createPlanes(_lodMax)); this._blurMaterial = _getBlurShader(_lodMax, width, height); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(this._lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size, ); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh(this._lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; const size = this._cubeSize; _setViewport(cubeUVRenderTarget, 0, 0, 3 * size, 2 * size); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; const n = this._lodPlanes.length; for (let i = 1; i < n; i++) { const sigma = Math.sqrt( this._sigmas[i] * this._sigmas[i] - this._sigmas[i - 1] * this._sigmas[i - 1], ); const poleAxis = _axisDirections[(n - i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis, ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis, ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis, ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error( "blur direction must be either latitudinal or longitudinal!", ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(this._lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : (2 * Math.PI) / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn( `sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`, ); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp((-x * x) / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } const {_lodMax} = this; blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = _lodMax - lodIn; const outputSize = this._sizeLods[lodOut]; const x = 3 * outputSize * (lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0); const y = 4 * (this._cubeSize - outputSize); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes(lodMax) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for (let i = 0; i < totalLods; i++) { const sizeLod = Math.pow(2, lod); sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > lodMax - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - lodMax + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 2); const min = -texelSize; const max = 1 + texelSize; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = ((face % 3) * 2) / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0, ]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute(position, positionSize), ); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute( "faceIndex", new BufferAttribute(faceIndex, faceIndexSize), ); lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return {lodPlanes, sizeLods, sigmas}; } function _createRenderTarget(width, height, params) { const cubeUVRenderTarget = new WebGLRenderTarget(width, height, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(lodMax, width, height) { const weights = new Float32Array(MAX_SAMPLES); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new ShaderMaterial({ name: "SphericalGaussianBlur", defines: { n: MAX_SAMPLES, CUBEUV_TEXEL_WIDTH: 1.0 / width, CUBEUV_TEXEL_HEIGHT: 1.0 / height, CUBEUV_MAX_MIP: `${lodMax}.0`, }, uniforms: { envMap: {value: null}, samples: {value: 1}, weights: {value: weights}, latitudinal: {value: false}, dTheta: {value: 0}, mipInt: {value: 0}, poleAxis: {value: poleAxis}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false, }); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { envMap: {value: null}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false, }); } function _getCubemapMaterial() { return new ShaderMaterial({ name: "CubemapToCubeUV", uniforms: { envMap: {value: null}, flipEnvMap: {value: -1}, }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false, }); } function _getCommonVertexShader() { return /* glsl */ ` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { let renderTarget = cubeUVmaps.get(texture); const currentPMREMVersion = renderTarget !== undefined ? renderTarget.texture.pmremVersion : 0; if ( texture.isRenderTargetTexture && texture.pmremVersion !== currentPMREMVersion ) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); renderTarget.texture.pmremVersion = texture.pmremVersion; cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (renderTarget !== undefined) { return renderTarget.texture; } else { const image = texture.image; if ( (isEquirectMap && image && image.height > 0) || (isCubeMap && image && isCubeTextureComplete(image)) ) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); renderTarget.texture.pmremVersion = texture.pmremVersion; cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose, }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function () { getExtension("EXT_color_buffer_float"); getExtension("WEBGL_clip_cull_distance"); getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); getExtension("WEBGL_render_shared_exponent"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { warnOnce("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; }, }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } for (const name in geometry.morphAttributes) { const array = geometry.morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.remove(array[i]); } } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else if (geometryPosition !== undefined) { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } else { return; } const attribute = new ( arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute )(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute, }; } function WebGLIndexedBufferRenderer(gl, extensions, info) { let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; gl.drawElementsInstanced( mode, count, type, start * bytesPerElement, primcount, ); info.update(count, mode, primcount); } function renderMultiDraw(starts, counts, drawCount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < drawCount; i++) { this.render(starts[i] / bytesPerElement, counts[i]); } } else { extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } info.update(elementCount, mode, 1); } } function renderMultiDrawInstances(starts, counts, drawCount, primcount) { if (drawCount === 0) return; const extension = extensions.get("WEBGL_multi_draw"); if (extension === null) { for (let i = 0; i < starts.length; i++) { renderInstances(starts[i] / bytesPerElement, counts[i], primcount[i]); } } else { extension.multiDrawElementsInstancedWEBGL( mode, counts, 0, type, starts, 0, primcount, 0, drawCount, ); let elementCount = 0; for (let i = 0; i < drawCount; i++) { elementCount += counts[i]; } for (let i = 0; i < primcount.length; i++) { info.update(elementCount, mode, primcount[i]); } } } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; this.renderMultiDraw = renderMultiDraw; this.renderMultiDrawInstances = renderMultiDrawInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0, }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0, }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update, }; } function WebGLMorphtargets(gl, capabilities, textures) { const morphTextures = new WeakMap(); const morph = new Vector4(); function update(object, geometry, program) { const objectInfluences = object.morphTargetInfluences; // the following encodes morph targets into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== morphTargetsCount) { if (entry !== undefined) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if (hasMorphPosition === true) vertexDataCount = 1; if (hasMorphNormals === true) vertexDataCount = 2; if (hasMorphColors === true) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * morphTargetsCount); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount, ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for (let i = 0; i < morphTargetsCount; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const morphColor = morphColors[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { const stride = j * vertexDataStride; if (hasMorphPosition === true) { morph.fromBufferAttribute(morphTarget, j); buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; } if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } if (hasMorphColors === true) { morph.fromBufferAttribute(morphColor, j); buffer[offset + stride + 8] = morph.x; buffer[offset + stride + 9] = morph.y; buffer[offset + stride + 10] = morph.z; buffer[offset + stride + 11] = morphColor.itemSize === 4 ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2(width, height), }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // if (object.isInstancedMesh === true && object.morphTexture !== null) { program .getUniforms() .setValue(gl, "morphTexture", object.morphTexture, textures); } else { let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program .getUniforms() .setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program .getUniforms() .setValue(gl, "morphTargetInfluences", objectInfluences); } program .getUniforms() .setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } return { update: update, }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if ( object.hasEventListener("dispose", onInstancedMeshDispose) === false ) { object.addEventListener("dispose", onInstancedMeshDispose); } if (updateMap.get(object) !== frame) { attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } updateMap.set(object, frame); } } if (object.isSkinnedMesh) { const skeleton = object.skeleton; if (updateMap.get(skeleton) !== frame) { skeleton.update(); updateMap.set(skeleton, frame); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose, }; } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format = DepthFormat, ) { if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat", ); } if (type === undefined && format === DepthFormat) type = UnsignedIntType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isDepthTexture = true; this.image = {width: width, height: height}; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; this.compareFunction = null; } copy(source) { super.copy(source); this.compareFunction = source.compareFunction; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.compareFunction !== null) data.compareFunction = this.compareFunction; return data; } } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyShadowTexture = /*@__PURE__*/ new DepthTexture(1, 1); emptyShadowTexture.compareFunction = LessEqualCompare; const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2i(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } } function setValueV3i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3i(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } } function setValueV4i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4i(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2ui(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } } function setValueV3ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3ui(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } } function setValueV4ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if ( cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w ) { gl.uniform4ui(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } const emptyTexture2D = this.type === gl.SAMPLER_2D_SHADOW ? emptyShadowTexture : emptyTexture; textures.setTexture2D(v || emptyTexture2D, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || empty3dTexture, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyArrayTexture, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || empty3dTexture, units[i]); } } function setValueT6Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.type = activeInfo.type; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.type = activeInfo.type; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor(id) { this.id = id; this.seq = []; this.map = {}; } setValue(gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if ( subscript === undefined || (subscript === "[" && matchEnd + 2 === pathLength) ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr), ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container class WebGLUniforms { constructor(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } setValue(gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); } setOptional(gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); } static upload(gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } } static seqWithValue(seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; } } function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } // From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/ const COMPLETION_STATUS_KHR = 0x91b1; let programIdCount = 0; function handleSource(string, errorLine) { const lines = string.split(" "); const lines2 = []; const from = Math.max(errorLine - 6, 0); const to = Math.min(errorLine + 6, lines.length); for (let i = from; i < to; i++) { const line = i + 1; lines2.push(`${line === errorLine ? ">" : " "} ${line}: ${lines[i]}`); } return lines2.join(" "); } function getEncodingComponents(colorSpace) { const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const encodingPrimaries = ColorManagement.getPrimaries(colorSpace); let gamutMapping; if (workingPrimaries === encodingPrimaries) { gamutMapping = ""; } else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) { gamutMapping = "LinearDisplayP3ToLinearSRGB"; } else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) { gamutMapping = "LinearSRGBToLinearDisplayP3"; } switch (colorSpace) { case LinearSRGBColorSpace: case LinearDisplayP3ColorSpace: return [gamutMapping, "LinearTransferOETF"]; case SRGBColorSpace: case DisplayP3ColorSpace: return [gamutMapping, "sRGBTransferOETF"]; default: console.warn("THREE.WebGLProgram: Unsupported color space:", colorSpace); return [gamutMapping, "LinearTransferOETF"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; const errorMatches = /ERROR: 0:(d+)/.exec(errors); if (errorMatches) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt(errorMatches[1]); return ( type.toUpperCase() + " " + errors + " " + handleSource(gl.getShaderSource(shader), errorLine) ); } else { return errors; } } function getTexelEncodingFunction(functionName, colorSpace) { const components = getEncodingComponents(colorSpace); return `vec4 ${functionName}( vec4 value ) { return ${components[0]}( ${components[1]}( value ) ); }`; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case AgXToneMapping: toneMappingName = "AgX"; break; case NeutralToneMapping: toneMappingName = "Neutral"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return ( "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }" ); } function generateVertexExtensions(parameters) { const chunks = [ parameters.extensionClipCullDistance ? "#extension GL_ANGLE_clip_cull_distance : require" : "", parameters.extensionMultiDraw ? "#extension GL_ANGLE_multi_draw : require" : "", ]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize, }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string .replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights) .replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights) .replace(/NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps) .replace(/NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords) .replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights) .replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights) .replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights) .replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows) .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps, ) .replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows) .replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string .replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes) .replace( /UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection, ); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } const shaderChunkMap = new Map(); function includeReplacer(match, include) { let string = ShaderChunk[include]; if (string === undefined) { const newInclude = shaderChunkMap.get(include); if (newInclude !== undefined) { string = ShaderChunk[newInclude]; console.warn( "THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.", include, newInclude, ); } else { throw new Error("Can not resolve #include <" + include + ">"); } } return resolveIncludes(string); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [ "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, ] .filter(filterEmptyLine) .join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision(parameters), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.extensionClipCullDistance ? "#define USE_CLIP_DISTANCE" : "", parameters.batching ? "#define USE_BATCHING" : "", parameters.batchingColor ? "#define USE_BATCHING_COLOR" : "", parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.instancingMorph ? "#define USE_INSTANCING_MORPH" : "", parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.displacementMap ? "#define USE_DISPLACEMENTMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", // parameters.mapUv ? "#define MAP_UV " + parameters.mapUv : "", parameters.alphaMapUv ? "#define ALPHAMAP_UV " + parameters.alphaMapUv : "", parameters.lightMapUv ? "#define LIGHTMAP_UV " + parameters.lightMapUv : "", parameters.aoMapUv ? "#define AOMAP_UV " + parameters.aoMapUv : "", parameters.emissiveMapUv ? "#define EMISSIVEMAP_UV " + parameters.emissiveMapUv : "", parameters.bumpMapUv ? "#define BUMPMAP_UV " + parameters.bumpMapUv : "", parameters.normalMapUv ? "#define NORMALMAP_UV " + parameters.normalMapUv : "", parameters.displacementMapUv ? "#define DISPLACEMENTMAP_UV " + parameters.displacementMapUv : "", parameters.metalnessMapUv ? "#define METALNESSMAP_UV " + parameters.metalnessMapUv : "", parameters.roughnessMapUv ? "#define ROUGHNESSMAP_UV " + parameters.roughnessMapUv : "", parameters.anisotropyMapUv ? "#define ANISOTROPYMAP_UV " + parameters.anisotropyMapUv : "", parameters.clearcoatMapUv ? "#define CLEARCOATMAP_UV " + parameters.clearcoatMapUv : "", parameters.clearcoatNormalMapUv ? "#define CLEARCOAT_NORMALMAP_UV " + parameters.clearcoatNormalMapUv : "", parameters.clearcoatRoughnessMapUv ? "#define CLEARCOAT_ROUGHNESSMAP_UV " + parameters.clearcoatRoughnessMapUv : "", parameters.iridescenceMapUv ? "#define IRIDESCENCEMAP_UV " + parameters.iridescenceMapUv : "", parameters.iridescenceThicknessMapUv ? "#define IRIDESCENCE_THICKNESSMAP_UV " + parameters.iridescenceThicknessMapUv : "", parameters.sheenColorMapUv ? "#define SHEEN_COLORMAP_UV " + parameters.sheenColorMapUv : "", parameters.sheenRoughnessMapUv ? "#define SHEEN_ROUGHNESSMAP_UV " + parameters.sheenRoughnessMapUv : "", parameters.specularMapUv ? "#define SPECULARMAP_UV " + parameters.specularMapUv : "", parameters.specularColorMapUv ? "#define SPECULAR_COLORMAP_UV " + parameters.specularColorMapUv : "", parameters.specularIntensityMapUv ? "#define SPECULAR_INTENSITYMAP_UV " + parameters.specularIntensityMapUv : "", parameters.transmissionMapUv ? "#define TRANSMISSIONMAP_UV " + parameters.transmissionMapUv : "", parameters.thicknessMapUv ? "#define THICKNESSMAP_UV " + parameters.thicknessMapUv : "", // parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphColors ? "#define USE_MORPHCOLORS" : "", parameters.morphTargetsCount > 0 ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", parameters.morphTargetsCount > 0 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.numLightProbes > 0 ? "#define USE_LIGHT_PROBES" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", "uniform mat4 modelMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", "#ifdef USE_INSTANCING", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "#ifdef USE_INSTANCING_MORPH", " uniform sampler2D morphTexture;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_UV1", " attribute vec2 uv1;", "#endif", "#ifdef USE_UV2", " attribute vec2 uv2;", "#endif", "#ifdef USE_UV3", " attribute vec2 uv3;", "#endif", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " ", ] .filter(filterEmptyLine) .join(" "); prefixFragment = [ generatePrecision(parameters), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.alphaToCoverage ? "#define ALPHA_TO_COVERAGE" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.dispersion ? "#define USE_DISPERSION" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor || parameters.batchingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.numLightProbes > 0 ? "#define USE_LIGHT_PROBES" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk["colorspace_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputColorSpace, ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " ", ] .filter(filterEmptyLine) .join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ customVertexExtensions, "#define attribute in", "#define varying out", "#define texture2D texture", ].join(" ") + " " + prefixVertex; prefixFragment = [ "#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); function onFirstUse(self) { // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; if (typeof renderer.debug.onShaderError === "function") { renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader, ); } else { // default error reporting const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment", ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Material Name: " + self.name + " " + "Material Type: " + self.type + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors, ); } } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { self.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex, }, fragmentShader: { log: fragmentLog, prefix: prefixFragment, }, }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); cachedUniforms = new WebGLUniforms(gl, program); cachedAttributes = fetchAttributeLocations(gl, program); } // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { // Populates cachedUniforms and cachedAttributes onFirstUse(this); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { // Populates cachedAttributes and cachedUniforms onFirstUse(this); } return cachedAttributes; }; // indicate when the program is ready to be used. if the KHR_parallel_shader_compile extension isn"t supported, // flag the program as ready immediately. It may cause a stall when it"s first used. let programReady = parameters.rendererExtensionParallelShaderCompile === false; this.isReady = function () { if (programReady === false) { programReady = gl.getProgramParameter(program, COMPLETION_STATUS_KHR); } return programReady; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.type = parameters.shaderType; this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id$1 = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage.code); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; let set = cache.get(material); if (set === undefined) { set = new Set(); cache.set(material, set); } return set; } _getShaderStage(code) { const cache = this.shaderCache; let stage = cache.get(code); if (stage === undefined) { stage = new WebGLShaderStage(code); cache.set(code, stage); } return stage; } } class WebGLShaderStage { constructor(code) { this.id = _id$1++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping, ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const _activeChannels = new Set(); const programs = []; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite", }; function getChannel(value) { _activeChannels.add(value); if (value === 0) return "uv"; return `uv${value}`; } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || environment); const envMapCubeUVHeight = !!envMap && envMap.mapping === CubeUVReflectionMapping ? envMap.image.height : null; const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead.", ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let morphTextureStride = 0; if (geometry.morphAttributes.position !== undefined) morphTextureStride = 1; if (geometry.morphAttributes.normal !== undefined) morphTextureStride = 2; if (geometry.morphAttributes.color !== undefined) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const IS_BATCHEDMESH = object.isBatchedMesh === true; const HAS_MAP = !!material.map; const HAS_MATCAP = !!material.matcap; const HAS_ENVMAP = !!envMap; const HAS_AOMAP = !!material.aoMap; const HAS_LIGHTMAP = !!material.lightMap; const HAS_BUMPMAP = !!material.bumpMap; const HAS_NORMALMAP = !!material.normalMap; const HAS_DISPLACEMENTMAP = !!material.displacementMap; const HAS_EMISSIVEMAP = !!material.emissiveMap; const HAS_METALNESSMAP = !!material.metalnessMap; const HAS_ROUGHNESSMAP = !!material.roughnessMap; const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_DISPERSION = material.dispersion > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !!material.anisotropyMap; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !!material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !!material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !!material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !!material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !!material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !!material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !!material.sheenRoughnessMap; const HAS_SPECULARMAP = !!material.specularMap; const HAS_SPECULAR_COLORMAP = !!material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !!material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !!material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !!material.thicknessMap; const HAS_GRADIENTMAP = !!material.gradientMap; const HAS_ALPHAMAP = !!material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_ALPHAHASH = !!material.alphaHash; const HAS_EXTENSIONS = !!material.extensions; let toneMapping = NoToneMapping; if (material.toneMapped) { if ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) { toneMapping = renderer.toneMapping; } } const parameters = { shaderID: shaderID, shaderType: material.type, shaderName: material.name, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, batching: IS_BATCHEDMESH, batchingColor: IS_BATCHEDMESH && object._colorsTexture !== null, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, instancingMorph: IS_INSTANCEDMESH && object.morphTexture !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: currentRenderTarget === null ? renderer.outputColorSpace : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace, alphaToCoverage: !!material.alphaToCoverage, map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, anisotropy: HAS_ANISOTROPY, anisotropyMap: HAS_ANISOTROPYMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, dispersion: HAS_DISPERSION, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending && material.alphaToCoverage === false, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, alphaHash: HAS_ALPHAHASH, combine: material.combine, // mapUv: HAS_MAP && getChannel(material.map.channel), aoMapUv: HAS_AOMAP && getChannel(material.aoMap.channel), lightMapUv: HAS_LIGHTMAP && getChannel(material.lightMap.channel), bumpMapUv: HAS_BUMPMAP && getChannel(material.bumpMap.channel), normalMapUv: HAS_NORMALMAP && getChannel(material.normalMap.channel), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel(material.displacementMap.channel), emissiveMapUv: HAS_EMISSIVEMAP && getChannel(material.emissiveMap.channel), metalnessMapUv: HAS_METALNESSMAP && getChannel(material.metalnessMap.channel), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel(material.roughnessMap.channel), anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel(material.anisotropyMap.channel), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel(material.clearcoatMap.channel), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel(material.clearcoatNormalMap.channel), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel(material.clearcoatRoughnessMap.channel), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel(material.iridescenceMap.channel), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel(material.iridescenceThicknessMap.channel), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel(material.sheenColorMap.channel), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel(material.sheenRoughnessMap.channel), specularMapUv: HAS_SPECULARMAP && getChannel(material.specularMap.channel), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel(material.specularColorMap.channel), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel(material.specularIntensityMap.channel), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel(material.transmissionMap.channel), thicknessMapUv: HAS_THICKNESSMAP && getChannel(material.thicknessMap.channel), alphaMapUv: HAS_ALPHAMAP && getChannel(material.alphaMap.channel), // vertexTangents: !!geometry.attributes.tangent && (HAS_NORMALMAP || HAS_ANISOTROPY), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4, pointsUvs: object.isPoints === true && !!geometry.attributes.uv && (HAS_MAP || HAS_ALPHAMAP), fog: !!fog, useFog: material.fog === true, fogExp2: !!fog && fog.isFogExp2, flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numLightProbes: lights.numLightProbes, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: toneMapping, decodeVideoTexture: HAS_MAP && material.map.isVideoTexture === true && ColorManagement.getTransfer(material.map.colorSpace) === SRGBTransfer, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionClipCullDistance: HAS_EXTENSIONS && material.extensions.clipCullDistance === true && extensions.has("WEBGL_clip_cull_distance"), extensionMultiDraw: HAS_EXTENSIONS && material.extensions.multiDraw === true && extensions.has("WEBGL_multi_draw"), rendererExtensionParallelShaderCompile: extensions.has( "KHR_parallel_shader_compile", ), customProgramCacheKey: material.customProgramCacheKey(), }; // the usage of getChannel() determines the active texture channels for this shader parameters.vertexUv1s = _activeChannels.has(1); parameters.vertexUv2s = _activeChannels.has(2); parameters.vertexUv3s = _activeChannels.has(3); _activeChannels.clear(); return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputColorSpace); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputColorSpace); array.push(parameters.envMapMode); array.push(parameters.envMapCubeUVHeight); array.push(parameters.mapUv); array.push(parameters.alphaMapUv); array.push(parameters.lightMapUv); array.push(parameters.aoMapUv); array.push(parameters.bumpMapUv); array.push(parameters.normalMapUv); array.push(parameters.displacementMapUv); array.push(parameters.emissiveMapUv); array.push(parameters.metalnessMapUv); array.push(parameters.roughnessMapUv); array.push(parameters.anisotropyMapUv); array.push(parameters.clearcoatMapUv); array.push(parameters.clearcoatNormalMapUv); array.push(parameters.clearcoatRoughnessMapUv); array.push(parameters.iridescenceMapUv); array.push(parameters.iridescenceThicknessMapUv); array.push(parameters.sheenColorMapUv); array.push(parameters.sheenRoughnessMapUv); array.push(parameters.specularMapUv); array.push(parameters.specularColorMapUv); array.push(parameters.specularIntensityMapUv); array.push(parameters.transmissionMapUv); array.push(parameters.thicknessMapUv); array.push(parameters.combine); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.morphTargetsCount); array.push(parameters.morphAttributeCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numSpotLightMaps); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.numSpotLightShadowsWithMaps); array.push(parameters.numLightProbes); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); array.push(parameters.depthPacking); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.supportsVertexTextures) _programLayers.enable(0); if (parameters.instancing) _programLayers.enable(1); if (parameters.instancingColor) _programLayers.enable(2); if (parameters.instancingMorph) _programLayers.enable(3); if (parameters.matcap) _programLayers.enable(4); if (parameters.envMap) _programLayers.enable(5); if (parameters.normalMapObjectSpace) _programLayers.enable(6); if (parameters.normalMapTangentSpace) _programLayers.enable(7); if (parameters.clearcoat) _programLayers.enable(8); if (parameters.iridescence) _programLayers.enable(9); if (parameters.alphaTest) _programLayers.enable(10); if (parameters.vertexColors) _programLayers.enable(11); if (parameters.vertexAlphas) _programLayers.enable(12); if (parameters.vertexUv1s) _programLayers.enable(13); if (parameters.vertexUv2s) _programLayers.enable(14); if (parameters.vertexUv3s) _programLayers.enable(15); if (parameters.vertexTangents) _programLayers.enable(16); if (parameters.anisotropy) _programLayers.enable(17); if (parameters.alphaHash) _programLayers.enable(18); if (parameters.batching) _programLayers.enable(19); if (parameters.dispersion) _programLayers.enable(20); if (parameters.batchingColor) _programLayers.enable(21); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.fog) _programLayers.enable(0); if (parameters.useFog) _programLayers.enable(1); if (parameters.flatShading) _programLayers.enable(2); if (parameters.logarithmicDepthBuffer) _programLayers.enable(3); if (parameters.skinning) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.morphColors) _programLayers.enable(7); if (parameters.premultipliedAlpha) _programLayers.enable(8); if (parameters.shadowMapEnabled) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.useDepthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.transmission) _programLayers.enable(14); if (parameters.sheen) _programLayers.enable(15); if (parameters.opaque) _programLayers.enable(16); if (parameters.pointsUvs) _programLayers.enable(17); if (parameters.decodeVideoTexture) _programLayers.enable(18); if (parameters.alphaToCoverage) _programLayers.enable(19); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose, }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose, }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group, }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group, ); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group, ); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort, }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { const listArray = lists.get(scene); let list; if (listArray === undefined) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= listArray.length) { list = new WebGLRenderList(); listArray.push(list); } else { list = listArray[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose, }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color(), }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color(), }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3(), }; break; } lights[light.id] = uniforms; return uniforms; }, }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000, }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; }, }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst(lightA, lightB) { return ( (lightB.castShadow ? 2 : 0) - (lightA.castShadow ? 2 : 0) + (lightB.map ? 1 : 0) - (lightA.map ? 1 : 0) ); } function WebGLLights(extensions) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: -1, pointLength: -1, spotLength: -1, rectAreaLength: -1, hemiLength: -1, numDirectionalShadows: -1, numPointShadows: -1, numSpotShadows: -1, numSpotMaps: -1, numLightProbes: -1, }, ambient: [0, 0, 0], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0, numLightProbes: 0, }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; let numLightProbes = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort(shadowCastingAndTexturingLightsFirst); for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } numLightProbes++; } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; state.spot[spotLength] = uniforms; const shadow = light.shadow; if (light.map) { state.spotLightMap[numSpotMaps] = light.map; numSpotMaps++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices(light); if (light.castShadow) numSpotShadowsWithMaps++; } state.spotLightMatrix[spotLength] = shadow.matrix; if (light.castShadow) { const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; numSpotShadows++; } spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps || hash.numLightProbes !== numLightProbes ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; state.numLightProbes = numLightProbes; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; hash.numLightProbes = numLightProbes; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state, }; } function WebGLRenderState(extensions) { const lights = new WebGLLights(extensions); const lightsArray = []; const shadowsArray = []; function init(camera) { state.camera = camera; lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights() { lights.setup(lightsArray); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, camera: null, lights: lights, transmissionRenderTarget: {}, }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow, }; } function WebGLRenderStates(extensions) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { const renderStateArray = renderStates.get(scene); let renderState; if (renderStateArray === undefined) { renderState = new WebGLRenderState(extensions); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStateArray.length) { renderState = new WebGLRenderState(extensions); renderStateArray.push(renderState); } else { renderState = renderStateArray[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose, }; } class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues(parameters); } copy(source) { super.copy(source); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(renderer, objects, capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({depthPacking: RGBADepthPacking}), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = capabilities.maxTextureSize; const shadowSide = { [FrontSide]: BackSide, [BackSide]: FrontSide, [DoubleSide]: DoubleSide, }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8, }, uniforms: { shadow_pass: {value: null}, resolution: {value: new Vector2()}, radius: {value: 4.0}, }, vertexShader: vertex, fragmentShader: fragment, }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3, ), ); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = renderer.getRenderTarget(); const activeCubeFace = renderer.getActiveCubeFace(); const activeMipmapLevel = renderer.getActiveMipmapLevel(); const _state = renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // check for shadow map type changes const toVSM = _previousType !== VSMShadowMap && this.type === VSMShadowMap; const fromVSM = _previousType === VSMShadowMap && this.type !== VSMShadowMap; // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null || toVSM === true || fromVSM === true) { const pars = this.type !== VSMShadowMap ? {minFilter: NearestFilter, magFilter: NearestFilter} : {}; if (shadow.map !== null) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars, ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } renderer.setRenderTarget(shadow.map); renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w, ); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (shadow.isPointLightShadow !== true && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel, ); }; function VSMPass(shadow, camera) { const geometry = objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if (shadow.mapPass === null) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; renderer.setRenderTarget(shadow.mapPass); renderer.clear(); renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null, ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; renderer.setRenderTarget(shadow.map); renderer.clear(); renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null, ); } function getDepthMaterial(object, material, light, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; if ( (renderer.localClippingEnabled && material.clipShadows === true && Array.isArray(material.clippingPlanes) && material.clippingPlanes.length !== 0) || (material.displacementMap && material.displacementScale !== 0) || (material.alphaMap && material.alphaTest > 0) || (material.map && material.alphaTest > 0) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; material.addEventListener("dispose", onMaterialDispose); } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { const materialProperties = renderer.properties.get(result); materialProperties.light = light; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ( (object.castShadow || (object.receiveShadow && type === VSMShadowMap)) && (!object.frustumCulled || _frustum.intersectsObject(object)) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld, ); const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, type, ); object.onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, group, ); renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group, ); object.onAfterShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, group, ); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, material, light, type); object.onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, null, ); renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null, ); object.onAfterShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial, null, ); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); // make sure to remove the unique distance/depth materials used for shadow map rendering for (const id in _materialCache) { const cache = _materialCache[id]; const uuid = event.target.uuid; if (uuid in cache) { const shadowMaterial = cache[uuid]; shadowMaterial.dispose(); delete cache[uuid]; } } } } function WebGLState(gl) { function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state }, }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; }, }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; }, }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentBlendColor = new Color(0, 0, 0); let currentBlendAlpha = 0; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count, dimensions) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { if (type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY) { gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data, ); } else { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data, ); } } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6, ); emptyTextures[gl.TEXTURE_2D_ARRAY] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1, ); emptyTextures[gl.TEXTURE_3D] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1, ); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } const textures = renderTarget.textures; if ( drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0 ) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { gl.drawBuffers(drawBuffers); } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT, }; equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA, [ConstantColorFactor]: gl.CONSTANT_COLOR, [OneMinusConstantColorFactor]: gl.ONE_MINUS_CONSTANT_COLOR, [ConstantAlphaFactor]: gl.CONSTANT_ALPHA, [OneMinusConstantAlphaFactor]: gl.ONE_MINUS_CONSTANT_ALPHA, }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha, ) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, ); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE, ); break; case MultiplyBlending: gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA, ); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, ); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE, ); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlendColor.set(0, 0, 0); currentBlendAlpha = 0; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[blendEquation], equationToGL[blendEquationAlpha], ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha], ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } if ( blendColor.equals(currentBlendColor) === false || blendAlpha !== currentBlendAlpha ) { gl.blendColor(blendColor.r, blendColor.g, blendColor.b, blendAlpha); currentBlendColor.copy(blendColor); currentBlendAlpha = blendAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha, ); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask, ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass, ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits, ); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture, webglSlot) { if (webglSlot === undefined) { if (currentTextureSlot === null) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[webglSlot]; if (boundTexture === undefined) { boundTexture = {type: undefined, texture: undefined}; currentBoundTextures[webglSlot] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } function updateUBOMapping(uniformsGroup, program) { let mapping = uboProgramMap.get(program); if (mapping === undefined) { mapping = new WeakMap(); uboProgramMap.set(program, mapping); } let blockIndex = mapping.get(uniformsGroup); if (blockIndex === undefined) { blockIndex = gl.getUniformBlockIndex(program, uniformsGroup.name); mapping.set(uniformsGroup, blockIndex); } } function uniformBlockBinding(uniformsGroup, program) { const mapping = uboProgramMap.get(program); const blockIndex = mapping.get(uniformsGroup); if (uboBindings.get(program) !== blockIndex) { // bind shader specific block index to global block point gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex, ); uboBindings.set(program, blockIndex); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.blendColor(0, 0, 0, 0); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlendColor = new Color(0, 0, 0); currentBlendAlpha = 0; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer, }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset, }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info, ) { const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture", ) ? extensions.get("WEBGL_multisampled_render_to_texture") : null; const supportsInvalidateFramebuffer = typeof navigator === "undefined" ? false : /OculusBrowser/g.test(navigator.userAgent); const _imageDimensions = new Vector2(); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && // eslint-disable-next-line compat/compat new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) { // Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsNewCanvas, maxSize) { let scale = 1; const dimensions = getDimensions(image); // handle case if texture exceeds max size if (dimensions.width > maxSize || dimensions.height > maxSize) { scale = maxSize / Math.max(dimensions.width, dimensions.height); } // only perform resize if necessary if (scale < 1) { // only perform resize for certain image types if ( (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement) || (typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement) || (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) || (typeof VideoFrame !== "undefined" && image instanceof VideoFrame) ) { const width = Math.floor(scale * dimensions.width); const height = Math.floor(scale * dimensions.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + dimensions.width + "x" + dimensions.height + ") to (" + width + "x" + height + ").", ); return canvas; } else { if ("data" in image) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + dimensions.width + "x" + dimensions.height + ").", ); } return image; } } return image; } function textureNeedsGenerateMipmaps(texture) { return ( texture.generateMipmaps && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false, ) { if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """, ); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RED_INTEGER) { if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8UI; if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.R16UI; if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.R32UI; if (glType === _gl.BYTE) internalFormat = _gl.R8I; if (glType === _gl.SHORT) internalFormat = _gl.R16I; if (glType === _gl.INT) internalFormat = _gl.R32I; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RG_INTEGER) { if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8UI; if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.RG16UI; if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.RG32UI; if (glType === _gl.BYTE) internalFormat = _gl.RG8I; if (glType === _gl.SHORT) internalFormat = _gl.RG16I; if (glType === _gl.INT) internalFormat = _gl.RG32I; } if (glFormat === _gl.RGB) { if (glType === _gl.UNSIGNED_INT_5_9_9_9_REV) internalFormat = _gl.RGB9_E5; } if (glFormat === _gl.RGBA) { const transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer(colorSpace); if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = transfer === SRGBTransfer ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getInternalDepthFormat(useStencil, depthType) { let glInternalFormat; if (useStencil) { if ( depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else if (depthType === FloatType) { glInternalFormat = _gl.DEPTH32F_STENCIL8; } else if (depthType === UnsignedShortType) { glInternalFormat = _gl.DEPTH24_STENCIL8; console.warn( "DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.", ); } } else { if ( depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (depthType === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthType === UnsignedShortType) { glInternalFormat = _gl.DEPTH_COMPONENT16; } } return glInternalFormat; } function getMipLevels(texture, image) { if ( textureNeedsGenerateMipmaps(texture) === true || (texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) ) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get(source); if (webglTextures) { const webglTexture = webglTextures[textureProperties.__cacheKey]; webglTexture.usedTimes--; // the WebGLTexture object is not used anymore, remove it if (webglTexture.usedTimes === 0) { deleteTexture(texture); } // remove the weak map entry if no WebGLTexture uses the source anymore if (Object.keys(webglTextures).length === 0) { _sources.delete(source); } } properties.remove(texture); } function deleteTexture(texture) { const textureProperties = properties.get(texture); _gl.deleteTexture(textureProperties.__webglTexture); const source = texture.source; const webglTextures = _sources.get(source); delete webglTextures[textureProperties.__cacheKey]; info.memory.textures--; } function deallocateRenderTarget(renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { if (Array.isArray(renderTargetProperties.__webglFramebuffer[i])) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer[i].length; level++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[i][level], ); } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); } if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { if (Array.isArray(renderTargetProperties.__webglFramebuffer)) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[level], ); } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); } if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer, ); if (renderTargetProperties.__webglColorRenderbuffer) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i++ ) { if (renderTargetProperties.__webglColorRenderbuffer[i]) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[i], ); } } if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } const textures = renderTarget.textures; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(textures[i]); } properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= capabilities.maxTextures) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + capabilities.maxTextures, ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey(texture) { const array = []; array.push(texture.wrapS); array.push(texture.wrapT); array.push(texture.wrapR || 0); array.push(texture.magFilter); array.push(texture.minFilter); array.push(texture.anisotropy); array.push(texture.internalFormat); array.push(texture.format); array.push(texture.type); array.push(texture.generateMipmaps); array.push(texture.premultiplyAlpha); array.push(texture.flipY); array.push(texture.unpackAlignment); array.push(texture.colorSpace); return array.join(); } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if (image === null) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found.", ); } else if (image.complete === false) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete", ); } else { uploadTexture(textureProperties, texture, slot); return; } } state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture(textureProperties, texture, slot); return; } state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT, }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR, }; const compareToGL = { [NeverCompare]: _gl.NEVER, [AlwaysCompare]: _gl.ALWAYS, [LessCompare]: _gl.LESS, [LessEqualCompare]: _gl.LEQUAL, [EqualCompare]: _gl.EQUAL, [GreaterEqualCompare]: _gl.GEQUAL, [GreaterCompare]: _gl.GREATER, [NotEqualCompare]: _gl.NOTEQUAL, }; function setTextureParameters(textureType, texture) { if ( texture.type === FloatType && extensions.has("OES_texture_float_linear") === false && (texture.magFilter === LinearFilter || texture.magFilter === LinearMipmapNearestFilter || texture.magFilter === NearestMipmapLinearFilter || texture.magFilter === LinearMipmapLinearFilter || texture.minFilter === LinearFilter || texture.minFilter === LinearMipmapNearestFilter || texture.minFilter === NearestMipmapLinearFilter || texture.minFilter === LinearMipmapLinearFilter) ) { console.warn( "THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.", ); } _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS], ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT], ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR], ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter], ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter], ); if (texture.compareFunction) { _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE, ); _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[texture.compareFunction], ); } if (extensions.has("EXT_texture_filter_anisotropic") === true) { if (texture.magFilter === NearestFilter) return; if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; if ( texture.type === FloatType && extensions.has("OES_texture_float_linear") === false ) return; // verify extension if ( texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy ) { const extension = extensions.get("EXT_texture_filter_anisotropic"); _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy()), ); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { let forceUpload = false; if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get(source); if (webglTextures === undefined) { webglTextures = {}; _sources.set(source, webglTextures); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey(texture); if (textureCacheKey !== textureProperties.__cacheKey) { // if not, create a new instance of WebGLTexture if (webglTextures[textureCacheKey] === undefined) { // create new entry webglTextures[textureCacheKey] = { texture: _gl.createTexture(), usedTimes: 0, }; info.memory.textures++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[textureCacheKey].usedTimes++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[textureProperties.__cacheKey]; if (webglTexture !== undefined) { webglTextures[textureProperties.__cacheKey].usedTimes--; if (webglTexture.usedTimes === 0) { deleteTexture(texture); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[textureCacheKey].texture; } return forceUpload; } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataArrayTexture || texture.isCompressedArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace); const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion); let image = resizeImage( texture.image, false, capabilities.maxTextureSize, ); image = verifyColorSpace(texture, image); const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); let glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture, ); setTextureParameters(textureType, texture); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; const dataReady = source.dataReady; const levels = getMipLevels(texture, image); if (texture.isDepthTexture) { glInternalFormat = getInternalDepthFormat( texture.format === DepthStencilFormat, texture.type, ); // if (allocateMemory) { if (useTexStorage) { state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height, ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null, ); } } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0) { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height, ); } if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data, ); } } } else if (texture.isCompressedTexture) { if (texture.isCompressedArrayTexture) { if (useTexStorage && allocateMemory) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, image.depth, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { if (texture.layerUpdates.size > 0) { for (const layerIndex of texture.layerUpdates) { const layerSize = mipmap.width * mipmap.height; state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, layerIndex, mipmap.width, mipmap.height, 1, glFormat, mipmap.data.slice( layerSize * layerIndex, layerSize * (layerIndex + 1), ), 0, 0, ); } texture.clearLayerUpdates(); } else { state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0, ); } } } else { state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data, ); } } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data, ); } } } } else { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data, ); } } else { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } } } } else if (texture.isDataArrayTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth, ); } if (dataReady) { if (texture.layerUpdates.size > 0) { // When type is GL_UNSIGNED_BYTE, each of these bytes is // interpreted as one color component, depending on format. When // type is one of GL_UNSIGNED_SHORT_5_6_5, // GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1, each // unsigned value is interpreted as containing all the components // for a single pixel, with the color components arranged // according to format. // // See https://registry.khronos.org/OpenGL-Refpages/es1.1/xhtml/glTexImage2D.xml let texelSize; switch (glType) { case _gl.UNSIGNED_BYTE: switch (glFormat) { case _gl.ALPHA: texelSize = 1; break; case _gl.LUMINANCE: texelSize = 1; break; case _gl.LUMINANCE_ALPHA: texelSize = 2; break; case _gl.RGB: texelSize = 3; break; case _gl.RGBA: texelSize = 4; break; default: throw new Error( `Unknown texel size for format ${glFormat}.`, ); } break; case _gl.UNSIGNED_SHORT_4_4_4_4: case _gl.UNSIGNED_SHORT_5_5_5_1: case _gl.UNSIGNED_SHORT_5_6_5: texelSize = 1; break; default: throw new Error(`Unknown texel size for type ${glType}.`); } const layerSize = image.width * image.height * texelSize; for (const layerIndex of texture.layerUpdates) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, layerIndex, image.width, image.height, 1, glFormat, glType, image.data.slice( layerSize * layerIndex, layerSize * (layerIndex + 1), ), ); } texture.clearLayerUpdates(); } else { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data, ); } } } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data, ); } } else if (texture.isData3DTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth, ); } if (dataReady) { state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data, ); } } else { state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data, ); } } else if (texture.isFramebufferTexture) { if (allocateMemory) { if (useTexStorage) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height, ); } else { let width = image.width, height = image.height; for (let i = 0; i < levels; i++) { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null, ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0) { if (useTexStorage && allocateMemory) { const dimensions = getDimensions(mipmaps[0]); state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height, ); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap, ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap, ); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { const dimensions = getDimensions(image); state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height, ); } if (dataReady) { state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image, ); } } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image, ); } } } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(textureType); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot, ); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace, ); const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace); const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion); const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture; const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage( texture.image[i], true, capabilities.maxCubemapSize, ); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], glFormat = utils.convert(texture.format, texture.colorSpace), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const useTexStorage = texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; const dataReady = source.dataReady; let levels = getMipLevels(texture, image); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height, ); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { if (dataReady) { state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data, ); } } else { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data, ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()", ); } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data, ); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; const dimensions = getDimensions(cubeImage[0]); state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, dimensions.width, dimensions.height, ); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data, ); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data, ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data, ); } } } else { if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i], ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i], ); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { if (dataReady) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i], ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i], ); } } } } } if (textureNeedsGenerateMipmaps(texture)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level, ) { const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { const width = Math.max(1, renderTarget.width >> level); const height = Math.max(1, renderTarget.height >> level); if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null, ); } else { state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null, ); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget), ); } else if ( textureTarget === _gl.TEXTURE_2D || (textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z) ) { // see #24753 _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, level, ); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer) { // retrieve the depth attachment types const depthTexture = renderTarget.depthTexture; const depthType = depthTexture && depthTexture.isDepthTexture ? depthTexture.type : null; const glInternalFormat = getInternalDepthFormat( renderTarget.stencilBuffer, depthType, ); const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; // set up the attachment const samples = getRenderTargetSamples(renderTarget); const isUseMultisampledRTT = useMultisampledRTT(renderTarget); if (isUseMultisampledRTT) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else if (isMultisample) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height, ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, glAttachmentType, _gl.RENDERBUFFER, renderbuffer, ); } else { const textures = renderTarget.textures; for (let i = 0; i < textures.length; i++) { const texture = textures[i]; const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, ); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height, ); } } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error( "Depth Texture with cube render targets is not supported", ); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if ( !(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture", ); } // upload an empty depth texture with framebuffer size if ( !properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get( renderTarget.depthTexture, ).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples, ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, ); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples, ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, ); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if ( renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer ) { if (isCube) throw new Error( "target.depthTexture not supported in Cube render targets", ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget, ); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i], ); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[i], renderTarget, false, ); } } else { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false, ); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0, ); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); const textures = renderTarget.textures; const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = textures.length > 1; if (!isMultipleRenderTargets) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { if (texture.mipmaps && texture.mipmaps.length > 0) { renderTargetProperties.__webglFramebuffer[i] = []; for (let level = 0; level < texture.mipmaps.length; level++) { renderTargetProperties.__webglFramebuffer[i][level] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } } else { if (texture.mipmaps && texture.mipmaps.length > 0) { renderTargetProperties.__webglFramebuffer = []; for (let level = 0; level < texture.mipmaps.length; level++) { renderTargetProperties.__webglFramebuffer[level] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } if (isMultipleRenderTargets) { for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } if ( renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false ) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); for (let i = 0; i < textures.length; i++) { const texture = textures[i]; renderTargetProperties.__webglColorRenderbuffer[i] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true, ); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true, ); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture); for (let i = 0; i < 6; i++) { if (texture.mipmaps && texture.mipmaps.length > 0) { for (let level = 0; level < texture.mipmaps.length; level++) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[i][level], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, ); } } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0, ); if (textureNeedsGenerateMipmaps(attachment)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture); if (texture.mipmaps && texture.mipmaps.length > 0) { for (let level = 0; level < texture.mipmaps.length; level++) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[level], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level, ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0, ); } if (textureNeedsGenerateMipmaps(texture)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const textures = renderTarget.textures; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } const invalidationArrayRead = []; const invalidationArrayDraw = []; function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.samples > 0) { if (useMultisampledRTT(renderTarget) === false) { const textures = renderTarget.textures; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get(renderTarget); const isMultipleRenderTargets = textures.length > 1; // If MRT we need to remove FBO attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null, ); state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0, ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); for (let i = 0; i < textures.length; i++) { if (renderTarget.resolveDepthBuffer) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; // resolving stencil is slow with a D3D backend. disable it for all transmission render targets (see #27799) if (renderTarget.stencilBuffer && renderTarget.resolveStencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } if (isMultipleRenderTargets) { _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const webglTexture = properties.get(textures[i]).__webglTexture; _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0, ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST, ); if (supportsInvalidateFramebuffer === true) { invalidationArrayRead.length = 0; invalidationArrayDraw.length = 0; invalidationArrayRead.push(_gl.COLOR_ATTACHMENT0 + i); if ( renderTarget.depthBuffer && renderTarget.resolveDepthBuffer === false ) { invalidationArrayRead.push(depthStyle); invalidationArrayDraw.push(depthStyle); _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, invalidationArrayDraw, ); } _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArrayRead, ); } } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, null); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i], ); const webglTexture = properties.get(textures[i]).__webglTexture; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer, ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0, ); } } state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer, ); } else { if ( renderTarget.depthBuffer && renderTarget.resolveDepthBuffer === false && supportsInvalidateFramebuffer ) { const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } } } } function getRenderTargetSamples(renderTarget) { return Math.min(capabilities.maxSamples, renderTarget.samples); } function useMultisampledRTT(renderTarget) { const renderTargetProperties = properties.get(renderTarget); return ( renderTarget.samples > 0 && extensions.has("WEBGL_multisampled_render_to_texture") === true && renderTargetProperties.__useRenderToTexture !== false ); } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true) return image; if (colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace) { // sRGB if (ColorManagement.getTransfer(colorSpace) === SRGBTransfer) { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.", ); } } else { console.error( "THREE.WebGLTextures: Unsupported texture color space:", colorSpace, ); } } return image; } function getDimensions(image) { if ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) { // if intrinsic data are not available, fallback to width/height _imageDimensions.width = image.naturalWidth || image.width; _imageDimensions.height = image.naturalHeight || image.height; } else if ( typeof VideoFrame !== "undefined" && image instanceof VideoFrame ) { _imageDimensions.width = image.displayWidth; _imageDimensions.height = image.displayHeight; } else { _imageDimensions.width = image.width; _imageDimensions.height = image.height; } return _imageDimensions; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils(gl, extensions) { function convert(p, colorSpace = NoColorSpace) { let extension; const transfer = ColorManagement.getTransfer(colorSpace); if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === UnsignedInt5999Type) return gl.UNSIGNED_INT_5_9_9_9_REV; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) return gl.HALF_FLOAT; if (p === AlphaFormat) return gl.ALPHA; if (p === RGBFormat) return gl.RGB; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; // WebGL2 formats. if (p === RedFormat) return gl.RED; if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if (transfer === SRGBTransfer) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC1_Format || p === RGB_ETC2_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return transfer === SRGBTransfer ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; if (p === RGB_BPTC_SIGNED_Format) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT; if (p === RGB_BPTC_UNSIGNED_Format) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT; } else { return null; } } // RGTC if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { extension = extensions.get("EXT_texture_compression_rgtc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return extension.COMPRESSED_RED_RGTC1_EXT; if (p === SIGNED_RED_RGTC1_Format) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if (p === RED_GREEN_RGTC2_Format) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if (p === SIGNED_RED_GREEN_RGTC2_Format) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if (p === UnsignedInt248Type) return gl.UNSIGNED_INT_24_8; // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return gl[p] !== undefined ? gl[p] : null; } return {convert: convert}; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = {type: "move"}; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = {pinching: false}; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } connect(inputSource) { if (inputSource && inputSource.hand) { const hand = this._hand; if (hand) { for (const inputjoint of inputSource.hand.values()) { // Initialize hand with joints when connected this._getHandJoint(hand, inputjoint); } } } this.dispatchEvent({type: "connected", data: inputSource}); return this; } disconnect(inputSource) { this.dispatchEvent({type: "disconnected", data: inputSource}); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint(hand, inputjoint); if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this, }); } else if ( !hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this, }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); grip.matrixWorldNeedsUpdate = true; if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if (inputPose === null && gripPose !== null) { inputPose = gripPose; } if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale, ); targetRay.matrixWorldNeedsUpdate = true; if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } // private method _getHandJoint(hand, inputjoint) { if (hand.joints[inputjoint.jointName] === undefined) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; hand.add(joint); } return hand.joints[inputjoint.jointName]; } } const _occlusion_vertex = ` void main() { gl_Position = vec4( position, 1.0 ); }`; const _occlusion_fragment = ` uniform sampler2DArray depthColor; uniform float depthWidth; uniform float depthHeight; void main() { vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); if ( coord.x >= 1.0 ) { gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; } else { gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; } }`; class WebXRDepthSensing { constructor() { this.texture = null; this.mesh = null; this.depthNear = 0; this.depthFar = 0; } init(renderer, depthData, renderState) { if (this.texture === null) { const texture = new Texture(); const texProps = renderer.properties.get(texture); texProps.__webglTexture = depthData.texture; if ( depthData.depthNear != renderState.depthNear || depthData.depthFar != renderState.depthFar ) { this.depthNear = depthData.depthNear; this.depthFar = depthData.depthFar; } this.texture = texture; } } getMesh(cameraXR) { if (this.texture !== null) { if (this.mesh === null) { const viewport = cameraXR.cameras[0].viewport; const material = new ShaderMaterial({ vertexShader: _occlusion_vertex, fragmentShader: _occlusion_fragment, uniforms: { depthColor: {value: this.texture}, depthWidth: {value: viewport.z}, depthHeight: {value: viewport.w}, }, }); this.mesh = new Mesh(new PlaneGeometry(20, 20), material); } } return this.mesh; } reset() { this.texture = null; this.mesh = null; } } class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const depthSensing = new WebXRDepthSensing(); const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; const currentSize = new Vector2(); let currentPixelRatio = null; // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraXR = new ArrayCamera(); cameraXR.layers.enable(1); cameraXR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controllerIndex = controllerInputSources.indexOf(event.inputSource); if (controllerIndex === -1) { return; } const controller = controllers[controllerIndex]; if (controller !== undefined) { controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace, ); controller.dispatchEvent({type: event.type, data: event.inputSource}); } } function onSessionEnd() { session.removeEventListener("select", onSessionEvent); session.removeEventListener("selectstart", onSessionEvent); session.removeEventListener("selectend", onSessionEvent); session.removeEventListener("squeeze", onSessionEvent); session.removeEventListener("squeezestart", onSessionEvent); session.removeEventListener("squeezeend", onSessionEvent); session.removeEventListener("end", onSessionEnd); session.removeEventListener("inputsourceschange", onInputSourcesChange); for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; if (inputSource === null) continue; controllerInputSources[i] = null; controllers[i].disconnect(inputSource); } _currentDepthNear = null; _currentDepthFar = null; depthSensing.reset(); // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; renderer.setPixelRatio(currentPixelRatio); renderer.setSize(currentSize.width, currentSize.height, false); scope.dispatchEvent({type: "sessionend"}); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting.", ); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting.", ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function (space) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } currentPixelRatio = renderer.getPixelRatio(); renderer.getSize(currentSize); if (session.renderState.layers === undefined) { const layerInit = { antialias: attributes.antialias, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor, }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({baseLayer: glBaseLayer}); renderer.setPixelRatio(1); renderer.setSize( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, false, ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil, }, ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor, }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({layers: [glProjLayer]}); renderer.setPixelRatio(1); renderer.setSize( glProjLayer.textureWidth, glProjLayer.textureHeight, false, ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat, ), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0, resolveDepthBuffer: glProjLayer.ignoreDepthValues === false, }, ); } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation(foveation); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({type: "sessionstart"}); } }; this.getEnvironmentBlendMode = function () { if (session !== null) { return session.environmentBlendMode; } }; function onInputSourcesChange(event) { // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const index = controllerInputSources.indexOf(inputSource); if (index >= 0) { controllerInputSources[index] = null; controllers[index].disconnect(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; let controllerIndex = controllerInputSources.indexOf(inputSource); if (controllerIndex === -1) { // Assign input source a controller that currently has no input source for (let i = 0; i < controllers.length; i++) { if (i >= controllerInputSources.length) { controllerInputSources.push(inputSource); controllerIndex = i; break; } else if (controllerInputSources[i] === null) { controllerInputSources[i] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if (controllerIndex === -1) break; } const controller = controllers[controllerIndex]; if (controller) { controller.connect(inputSource); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale, ); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale, ); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = ((topFov * far) / far2) * near2; const bottom2 = ((bottomFov * far) / far2) * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2, ); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; if (depthSensing.texture !== null) { camera.near = depthSensing.depthNear; camera.far = depthSensing.depthFar; } cameraXR.near = cameraR.near = cameraL.near = camera.near; cameraXR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraXR.near, depthFar: cameraXR.far, }); _currentDepthNear = cameraXR.near; _currentDepthFar = cameraXR.far; cameraL.near = _currentDepthNear; cameraL.far = _currentDepthFar; cameraR.near = _currentDepthNear; cameraR.far = _currentDepthFar; cameraL.updateProjectionMatrix(); cameraR.updateProjectionMatrix(); camera.updateProjectionMatrix(); } const parent = camera.parent; const cameras = cameraXR.cameras; updateCamera(cameraXR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraXR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraXR.projectionMatrix.copy(cameraL.projectionMatrix); } // update user camera and its children updateUserCamera(camera, cameraXR, parent); }; function updateUserCamera(camera, cameraXR, parent) { if (parent === null) { camera.matrix.copy(cameraXR.matrixWorld); } else { camera.matrix.copy(parent.matrixWorld); camera.matrix.invert(); camera.matrix.multiply(cameraXR.matrixWorld); } camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); camera.updateMatrixWorld(true); camera.projectionMatrix.copy(cameraXR.projectionMatrix); camera.projectionMatrixInverse.copy(cameraXR.projectionMatrixInverse); if (camera.isPerspectiveCamera) { camera.fov = RAD2DEG * 2 * Math.atan(1 / camera.projectionMatrix.elements[5]); camera.zoom = 1; } } this.getCamera = function () { return cameraXR; }; this.getFoveation = function () { if (glProjLayer === null && glBaseLayer === null) { return undefined; } return foveation; }; this.setFoveation = function (value) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if (glProjLayer !== null) { glProjLayer.fixedFoveation = value; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = value; } }; this.hasDepthSensing = function () { return depthSensing.texture !== null; }; this.getDepthSensingMesh = function () { return depthSensing.getMesh(cameraXR); }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(customReferenceSpace || referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer, ); renderer.setRenderTarget(newRenderTarget); } let cameraXRNeedsUpdate = false; // check if it"s necessary to rebuild cameraXR"s camera list if (views.length !== cameraXR.cameras.length) { cameraXR.cameras.length = 0; cameraXRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture, ); renderer.setRenderTarget(newRenderTarget); } } let camera = cameras[i]; if (camera === undefined) { camera = new PerspectiveCamera(); camera.layers.enable(i); camera.viewport = new Vector4(); cameras[i] = camera; } camera.matrix.fromArray(view.transform.matrix); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale, ); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height, ); if (i === 0) { cameraXR.matrix.copy(camera.matrix); cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale, ); } if (cameraXRNeedsUpdate === true) { cameraXR.cameras.push(camera); } } // const enabledFeatures = session.enabledFeatures; if (enabledFeatures && enabledFeatures.includes("depth-sensing")) { const depthData = glBinding.getDepthInformation(views[0]); if (depthData && depthData.isValid && depthData.texture) { depthSensing.init(renderer, depthData, session.renderState); } } } // for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; const controller = controllers[i]; if (inputSource !== null && controller !== undefined) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace, ); } } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); if (frame.detectedPlanes) { scope.dispatchEvent({type: "planesdetected", data: frame}); } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } const _e1 = /*@__PURE__*/ new Euler(); const _m1 = /*@__PURE__*/ new Matrix4(); function WebGLMaterials(renderer, properties) { function refreshTransformUniform(map, uniform) { if (map.matrixAutoUpdate === true) { map.updateMatrix(); } uniform.value.copy(map.matrix); } function refreshFogUniforms(uniforms, fog) { fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace(renderer), ); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget, ) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsStandard(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value .copy(material.emissive) .multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform(material.bumpMap, uniforms.bumpMapTransform); uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) { uniforms.bumpScale.value *= -1; } } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform(material.normalMap, uniforms.normalMapTransform); uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) { uniforms.normalScale.value.negate(); } } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform, ); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform, ); } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform( material.specularMap, uniforms.specularMapTransform, ); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const materialProperties = properties.get(material); const envMap = materialProperties.envMap; const envMapRotation = materialProperties.envMapRotation; if (envMap) { uniforms.envMap.value = envMap; _e1.copy(envMapRotation); // accommodate left-handed frame _e1.x *= -1; _e1.y *= -1; _e1.z *= -1; if (envMap.isCubeTexture && envMap.isRenderTargetTexture === false) { // environment maps which are not cube render targets or PMREMs follow a different convention _e1.y *= -1; _e1.z *= -1; } uniforms.envMapRotation.value.setFromMatrix4( _m1.makeRotationFromEuler(_e1), ); uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; refreshTransformUniform(material.lightMap, uniforms.lightMapTransform); } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform(material.aoMap, uniforms.aoMapTransform); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.uvTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard(uniforms, material) { uniforms.metalness.value = material.metalness; if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform, ); } uniforms.roughness.value = material.roughness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform, ); } if (material.envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget, ) { uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value .copy(material.sheenColor) .multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform, ); } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform, ); } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform, ); } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform, ); } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform, ); uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.dispersion > 0) { uniforms.dispersion.value = material.dispersion; } if (material.iridescence > 0) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[0]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[1]; if (material.iridescenceMap) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform, ); } if (material.iridescenceThicknessMap) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform, ); } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height, ); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform, ); } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform, ); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } if (material.anisotropy > 0) { uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos(material.anisotropyRotation), material.anisotropy * Math.sin(material.anisotropyRotation), ); if (material.anisotropyMap) { uniforms.anisotropyMap.value = material.anisotropyMap; refreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform, ); } } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform, ); } if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform, ); } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance(uniforms, material) { const light = properties.get(material).light; uniforms.referencePosition.value.setFromMatrixPosition(light.matrixWorld); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms, }; } function WebGLUniformsGroups(gl, info, capabilities, state) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = gl.getParameter(gl.MAX_UNIFORM_BUFFER_BINDINGS); // binding points are global whereas block indices are per shader program function bind(uniformsGroup, program) { const webglProgram = program.program; state.uniformBlockBinding(uniformsGroup, webglProgram); } function update(uniformsGroup, program) { let buffer = buffers[uniformsGroup.id]; if (buffer === undefined) { prepareUniformsGroup(uniformsGroup); buffer = createBuffer(uniformsGroup); buffers[uniformsGroup.id] = buffer; uniformsGroup.addEventListener("dispose", onUniformsGroupsDispose); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping(uniformsGroup, webglProgram); // update UBO once per frame const frame = info.render.frame; if (updateList[uniformsGroup.id] !== frame) { updateBufferData(uniformsGroup); updateList[uniformsGroup.id] = frame; } } function createBuffer(uniformsGroup) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); gl.bufferData(gl.UNIFORM_BUFFER, size, usage); gl.bindBuffer(gl.UNIFORM_BUFFER, null); gl.bindBufferBase(gl.UNIFORM_BUFFER, bindingPointIndex, buffer); return buffer; } function allocateBindingPointIndex() { for (let i = 0; i < maxBindingPoints; i++) { if (allocatedBindingPoints.indexOf(i) === -1) { allocatedBindingPoints.push(i); return i; } } console.error( "THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.", ); return 0; } function updateBufferData(uniformsGroup) { const buffer = buffers[uniformsGroup.id]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); for (let i = 0, il = uniforms.length; i < il; i++) { const uniformArray = Array.isArray(uniforms[i]) ? uniforms[i] : [uniforms[i]]; for (let j = 0, jl = uniformArray.length; j < jl; j++) { const uniform = uniformArray[j]; if (hasUniformChanged(uniform, i, j, cache) === true) { const offset = uniform.__offset; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; let arrayOffset = 0; for (let k = 0; k < values.length; k++) { const value = values[k]; const info = getUniformSize(value); // TODO add integer and struct support if (typeof value === "number" || typeof value === "boolean") { uniform.__data[0] = value; gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data, ); } else if (value.isMatrix3) { // manually converting 3x3 to 3x4 uniform.__data[0] = value.elements[0]; uniform.__data[1] = value.elements[1]; uniform.__data[2] = value.elements[2]; uniform.__data[3] = 0; uniform.__data[4] = value.elements[3]; uniform.__data[5] = value.elements[4]; uniform.__data[6] = value.elements[5]; uniform.__data[7] = 0; uniform.__data[8] = value.elements[6]; uniform.__data[9] = value.elements[7]; uniform.__data[10] = value.elements[8]; uniform.__data[11] = 0; } else { value.toArray(uniform.__data, arrayOffset); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData(gl.UNIFORM_BUFFER, offset, uniform.__data); } } } gl.bindBuffer(gl.UNIFORM_BUFFER, null); } function hasUniformChanged(uniform, index, indexArray, cache) { const value = uniform.value; const indexString = index + "_" + indexArray; if (cache[indexString] === undefined) { // cache entry does not exist so far if (typeof value === "number" || typeof value === "boolean") { cache[indexString] = value; } else { cache[indexString] = value.clone(); } return true; } else { const cachedObject = cache[indexString]; // compare current value with cached entry if (typeof value === "number" || typeof value === "boolean") { if (cachedObject !== value) { cache[indexString] = value; return true; } } else { if (cachedObject.equals(value) === false) { cachedObject.copy(value); return true; } } } return false; } function prepareUniformsGroup(uniformsGroup) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes for (let i = 0, l = uniforms.length; i < l; i++) { const uniformArray = Array.isArray(uniforms[i]) ? uniforms[i] : [uniforms[i]]; for (let j = 0, jl = uniformArray.length; j < jl; j++) { const uniform = uniformArray[j]; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; for (let k = 0, kl = values.length; k < kl; k++) { const value = values[k]; const info = getUniformSize(value); // Calculate the chunk offset const chunkOffsetUniform = offset % chunkSize; // Check for chunk overflow if ( chunkOffsetUniform !== 0 && chunkSize - chunkOffsetUniform < info.boundary ) { // Add padding and adjust offset offset += chunkSize - chunkOffsetUniform; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array( info.storage / Float32Array.BYTES_PER_ELEMENT, ); uniform.__offset = offset; // Update the global offset offset += info.storage; } } } // ensure correct final padding const chunkOffset = offset % chunkSize; if (chunkOffset > 0) offset += chunkSize - chunkOffset; // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize(value) { const info = { boundary: 0, // bytes storage: 0, // bytes }; // determine sizes according to STD140 if (typeof value === "number" || typeof value === "boolean") { // float/int/bool info.boundary = 4; info.storage = 4; } else if (value.isVector2) { // vec2 info.boundary = 8; info.storage = 8; } else if (value.isVector3 || value.isColor) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if (value.isVector4) { // vec4 info.boundary = 16; info.storage = 16; } else if (value.isMatrix3) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if (value.isMatrix4) { // mat4 info.boundary = 64; info.storage = 64; } else if (value.isTexture) { console.warn( "THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.", ); } else { console.warn( "THREE.WebGLRenderer: Unsupported uniform value type.", value, ); } return info; } function onUniformsGroupsDispose(event) { const uniformsGroup = event.target; uniformsGroup.removeEventListener("dispose", onUniformsGroupsDispose); const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex, ); allocatedBindingPoints.splice(index, 1); gl.deleteBuffer(buffers[uniformsGroup.id]); delete buffers[uniformsGroup.id]; delete updateList[uniformsGroup.id]; } function dispose() { for (const id in buffers) { gl.deleteBuffer(buffers[id]); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose, }; } class WebGLRenderer { constructor(parameters = {}) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = false, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = "default", failIfMajorPerformanceCaveat = false, } = parameters; this.isWebGLRenderer = true; let _alpha; if (context !== null) { if ( typeof WebGLRenderingContext !== "undefined" && context instanceof WebGLRenderingContext ) { throw new Error( "THREE.WebGLRenderer: WebGL 1 is not supported since r163.", ); } _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } const uintClearColor = new Uint32Array(4); const intClearColor = new Int32Array(4); let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null, }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this._outputColorSpace = SRGBColorSpace; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; const _currentClearColor = new Color(0x000000); let _currentClearAlpha = 0; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true, }; let _renderBackground = false; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext(contextName, contextAttributes) { return canvas.getContext(contextName, contextAttributes); } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat, }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in canvas) canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener("webglcontextlost", onContextLost, false); canvas.addEventListener("webglcontextrestored", onContextRestore, false); canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false, ); if (_gl === null) { const contextName = "webgl2"; _gl = getContext(contextName, contextAttributes); if (_gl === null) { if (getContext(contextName)) { throw new Error( "Error creating WebGL context with your selected attributes.", ); } else { throw new Error("Error creating WebGL context."); } } } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions(_gl); extensions.init(); utils = new WebGLUtils(_gl, extensions); capabilities = new WebGLCapabilities(_gl, extensions, parameters, utils); state = new WebGLState(_gl); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info, ); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl); bindingStates = new WebGLBindingStates(_gl, attributes); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping, ); materials = new WebGLMaterials(_this, properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha, ); shadowMap = new WebGLShadowMap(_this, objects, capabilities); uniformsGroups = new WebGLUniformsGroups(_gl, info, capabilities, state); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle = true) { if (xr.isPresenting) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting.", ); return; } _width = width; _height = height; canvas.width = Math.floor(width * _pixelRatio); canvas.height = Math.floor(height * _pixelRatio); if (updateStyle === true) { canvas.style.width = width + "px"; canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor(width * pixelRatio); canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport( _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).round(), ); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor( _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).round(), ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest((_scissorTest = boolean)); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color = true, depth = true, stencil = true) { let bits = 0; if (color) { // check if we"re trying to clear an integer target let isIntegerFormat = false; if (_currentRenderTarget !== null) { const targetFormat = _currentRenderTarget.texture.format; isIntegerFormat = targetFormat === RGBAIntegerFormat || targetFormat === RGIntegerFormat || targetFormat === RedIntegerFormat; } // use the appropriate clear functions to clear the target if it"s a signed // or unsigned integer target if (isIntegerFormat) { const targetType = _currentRenderTarget.texture.type; const isUnsignedType = targetType === UnsignedByteType || targetType === UnsignedIntType || targetType === UnsignedShortType || targetType === UnsignedInt248Type || targetType === UnsignedShort4444Type || targetType === UnsignedShort5551Type; const clearColor = background.getClearColor(); const a = background.getClearAlpha(); const r = clearColor.r; const g = clearColor.g; const b = clearColor.b; if (isUnsignedType) { uintClearColor[0] = r; uintClearColor[1] = g; uintClearColor[2] = b; uintClearColor[3] = a; _gl.clearBufferuiv(_gl.COLOR, 0, uintClearColor); } else { intClearColor[0] = r; intClearColor[1] = g; intClearColor[2] = b; intClearColor[3] = a; _gl.clearBufferiv(_gl.COLOR, 0, intClearColor); } } else { bits |= _gl.COLOR_BUFFER_BIT; } } if (depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil) { bits |= _gl.STENCIL_BUFFER_BIT; this.state.buffers.stencil.setMask(0xffffffff); } _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { canvas.removeEventListener("webglcontextlost", onContextLost, false); canvas.removeEventListener( "webglcontextrestored", onContextRestore, false, ); canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false, ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function onContextRestore(/* event */) { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError(event) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage, ); } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group, ) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); if (index === undefined) return; rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = (drawRange.start + drawRange.count) * rangeFactor; if (group !== null) { drawStart = Math.max(drawStart, group.start * rangeFactor); drawEnd = Math.min(drawEnd, (group.start + group.count) * rangeFactor); } if (index !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, index.count); } else if (position !== undefined && position !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, position.count); } const drawCount = drawEnd - drawStart; if (drawCount < 0 || drawCount === Infinity) return; // bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio(), ); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isBatchedMesh) { if (object._multiDrawInstances !== null) { renderer.renderMultiDrawInstances( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount, object._multiDrawInstances, ); } else { renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount, ); } } else if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount, ); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile function prepareMaterial(material, scene, object) { if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = FrontSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = DoubleSide; } else { getProgram(material, scene, object); } } this.compile = function (scene, camera, targetScene = null) { if (targetScene === null) targetScene = scene; currentRenderState = renderStates.get(targetScene); currentRenderState.init(camera); renderStateStack.push(currentRenderState); // gather lights from both the target scene and the new object that will be added to the scene. targetScene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); if (scene !== targetScene) { scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); } currentRenderState.setupLights(); // Only initialize materials in the new scene, not the targetScene. const materials = new Set(); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; prepareMaterial(material2, targetScene, object); materials.add(material2); } } else { prepareMaterial(material, targetScene, object); materials.add(material); } } }); renderStateStack.pop(); currentRenderState = null; return materials; }; // compileAsync this.compileAsync = function (scene, camera, targetScene = null) { const materials = this.compile(scene, camera, targetScene); // Wait for all the materials in the new object to indicate that they"re // ready to be used before resolving the promise. return new Promise(resolve => { function checkMaterialsReady() { materials.forEach(function (material) { const materialProperties = properties.get(material); const program = materialProperties.currentProgram; if (program.isReady()) { // remove any programs that report they"re ready to use from the list materials.delete(material); } }); // once the list of compiling materials is empty, call the callback if (materials.size === 0) { resolve(scene); return; } // if some materials are still not ready, wait a bit and check again setTimeout(checkMaterialsReady, 10); } if (extensions.get("KHR_parallel_shader_compile") !== null) { // If we can check the compilation status of the materials without // blocking then do so right away. checkMaterialsReady(); } else { // Otherwise start by waiting a bit to give the materials we just // initialized a chance to finish. setTimeout(checkMaterialsReady, 10); } }); }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof self !== "undefined") animation.setContext(self); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.", ); return; } if (_isContextLost === true) return; // update scene graph if (scene.matrixWorldAutoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null && camera.matrixWorldAutoUpdate === true) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(camera); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse, ); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, ); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); if (xr.enabled === true && xr.isPresenting === true) { const depthSensingMesh = _this.xr.getDepthSensingMesh(); if (depthSensingMesh !== null) { projectObject(depthSensingMesh, camera, -Infinity, _this.sortObjects); } } projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } _renderBackground = xr.enabled === false || xr.isPresenting === false || xr.hasDepthSensing() === false; if (_renderBackground) { background.addToRenderList(currentRenderList, scene); } // this.info.render.frame++; if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // render scene const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; currentRenderState.setupLights(); if (camera.isArrayCamera) { const cameras = camera.cameras; if (transmissiveObjects.length > 0) { for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera2, ); } } if (_renderBackground) background.render(scene); for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { if (transmissiveObjects.length > 0) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera, ); if (_renderBackground) background.render(scene); renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; if (_clippingEnabled === true) clipping.setGlobalState( _this.clippingPlanes, currentRenderState.state.camera, ); } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3 .setFromMatrixPosition(object.matrixWorld) .applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null, ); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (!object.frustumCulled || _frustum.intersectsObject(object)) { const geometry = objects.update(object); const material = object.material; if (sortObjects) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _vector3.copy(object.boundingSphere.center); } else { if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _vector3.copy(geometry.boundingSphere.center); } _vector3 .applyMatrix4(object.matrixWorld) .applyMatrix4(_projScreenMatrix); } if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group, ); } } } else if (material.visible) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null, ); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (_clippingEnabled === true) clipping.setGlobalState(_this.clippingPlanes, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); } function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera, ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; if (overrideMaterial !== null) { return; } if ( currentRenderState.state.transmissionRenderTarget[camera.id] === undefined ) { currentRenderState.state.transmissionRenderTarget[camera.id] = new WebGLRenderTarget(1, 1, { generateMipmaps: true, type: extensions.has("EXT_color_buffer_half_float") || extensions.has("EXT_color_buffer_float") ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: 4, stencilBuffer: stencil, resolveDepthBuffer: false, resolveStencilBuffer: false, colorSpace: ColorManagement.workingColorSpace, }); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } const transmissionRenderTarget = currentRenderState.state.transmissionRenderTarget[camera.id]; const activeViewport = camera.viewport || _currentViewport; transmissionRenderTarget.setSize(activeViewport.z, activeViewport.w); // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(transmissionRenderTarget); _this.getClearColor(_currentClearColor); _currentClearAlpha = _this.getClearAlpha(); if (_currentClearAlpha < 1) _this.setClearColor(0xffffff, 0.5); if (_renderBackground) { background.render(scene); } else { _this.clear(); } // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; // Remove viewport from camera to avoid nested render calls resetting viewport to it (e.g Reflector). // Transmission render pass requires viewport to match the transmissionRenderTarget. const currentCameraViewport = camera.viewport; if (camera.viewport !== undefined) camera.viewport = undefined; currentRenderState.setupLightsView(camera); if (_clippingEnabled === true) clipping.setGlobalState(_this.clippingPlanes, camera); renderObjects(opaqueObjects, scene, camera); textures.updateMultisampleRenderTarget(transmissionRenderTarget); textures.updateRenderTargetMipmap(transmissionRenderTarget); if (extensions.has("WEBGL_multisampled_render_to_texture") === false) { // see #28131 let renderTargetNeedsUpdate = false; for (let i = 0, l = transmissiveObjects.length; i < l; i++) { const renderItem = transmissiveObjects[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if ( material.side === DoubleSide && object.layers.test(camera.layers) ) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject(object, scene, camera, geometry, material, group); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if (renderTargetNeedsUpdate === true) { textures.updateMultisampleRenderTarget(transmissionRenderTarget); textures.updateRenderTargetMipmap(transmissionRenderTarget); } } _this.setRenderTarget(currentRenderTarget); _this.setClearColor(_currentClearColor, _currentClearAlpha); if (currentCameraViewport !== undefined) camera.viewport = currentCameraViewport; _this.toneMapping = currentToneMapping; } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld, ); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group, ); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object, ); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || materialProperties.environment); materialProperties.envMapRotation = materialProperties.environment !== null && material.envMap === null ? scene.environmentRotation : material.envMapRotation; if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( (!material.isShaderMaterial && !material.isRawShaderMaterial) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } materialProperties.currentProgram = program; materialProperties.uniformsList = null; return program; } function getUniformList(materialProperties) { if (materialProperties.uniformsList === null) { const progUniforms = materialProperties.currentProgram.getUniforms(); materialProperties.uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, materialProperties.uniforms, ); } return materialProperties.uniformsList; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.batching = parameters.batching; materialProperties.batchingColor = parameters.batchingColor; materialProperties.instancing = parameters.instancing; materialProperties.instancingColor = parameters.instancingColor; materialProperties.instancingMorph = parameters.instancingMorph; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = _currentRenderTarget === null ? _this.outputColorSpace : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!geometry.attributes.tangent && (!!material.normalMap || material.anisotropy > 0); const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphColors = !!geometry.morphAttributes.color; let toneMapping = NoToneMapping; if (material.toneMapped) { if ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) { toneMapping = _this.toneMapping; } } const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if ( materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version ) { needsProgramChange = true; } else if (materialProperties.outputColorSpace !== colorSpace) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batching === false ) { needsProgramChange = true; } else if ( !object.isBatchedMesh && materialProperties.batching === true ) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batchingColor === true && object.colorTexture === null ) { needsProgramChange = true; } else if ( object.isBatchedMesh && materialProperties.batchingColor === false && object.colorTexture !== null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( !object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( !object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingMorph === true && object.morphTexture === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingMorph === false && object.morphTexture !== null ) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog === true && materialProperties.fog !== fog) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection) ) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.morphColors !== morphColors) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { // common camera uniforms p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition(camera.matrixWorld), ); } if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2), ); } // consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067 if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true, ); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures, ); } } if (object.isBatchedMesh) { p_uniforms.setOptional(_gl, object, "batchingTexture"); p_uniforms.setValue( _gl, "batchingTexture", object._matricesTexture, textures, ); p_uniforms.setOptional(_gl, object, "batchingColorTexture"); if (object._colorsTexture !== null) { p_uniforms.setValue( _gl, "batchingColorTexture", object._colorsTexture, textures, ); } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || morphAttributes.color !== undefined ) { morphtargets.update(object, geometry, program); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if (material.isMeshGouraudMaterial && material.envMap !== null) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; } if ( material.isMeshStandardMaterial && material.envMap === null && scene.environment !== null ) { m_uniforms.envMapIntensity.value = scene.environmentIntensity; } if (refreshMaterial) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure, ); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog === true) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, currentRenderState.state.transmissionRenderTarget[camera.id], ); WebGLUniforms.upload( _gl, getUniformList(materialProperties), m_uniforms, textures, ); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload( _gl, getUniformList(materialProperties), m_uniforms, textures, ); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); // UBOs if (material.isShaderMaterial || material.isRawShaderMaterial) { const groups = material.uniformsGroups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; uniformsGroups.update(group, program); uniformsGroups.bind(group, program); } } return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return ( material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || (material.isShaderMaterial && material.lights === true) ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture, ) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (extensions.has("WEBGL_multisampled_render_to_texture") === true) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided", ); renderTargetProperties.__useRenderToTexture = false; } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer, ) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0, ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture, ); } const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { if (Array.isArray(__webglFramebuffer[activeCubeFace])) { framebuffer = __webglFramebuffer[activeCubeFace][activeMipmapLevel]; } else { framebuffer = __webglFramebuffer[activeCubeFace]; } isCube = true; } else if ( renderTarget.samples > 0 && textures.useMultisampledRTT(renderTarget) === false ) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { if (Array.isArray(__webglFramebuffer)) { framebuffer = __webglFramebuffer[activeMipmapLevel]; } else { framebuffer = __webglFramebuffer; } } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer, ); if (framebufferBound && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel, ); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer, ); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, ) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.", ); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (!capabilities.textureFormatReadable(textureFormat)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.", ); return; } if (!capabilities.textureTypeReadable(textureType)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.", ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height ) { _gl.readPixels( x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer, ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, ) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.", ); } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (!capabilities.textureFormatReadable(textureFormat)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.", ); } if (!capabilities.textureTypeReadable(textureType)) { throw new Error( "THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.", ); } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height ) { const glBuffer = _gl.createBuffer(); _gl.bindBuffer(_gl.PIXEL_PACK_BUFFER, glBuffer); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ, ); _gl.readPixels( x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), 0, ); _gl.flush(); // check if the commands have finished every 8 ms const sync = _gl.fenceSync(_gl.SYNC_GPU_COMMANDS_COMPLETE, 0); await probeAsync(_gl, sync, 4); try { _gl.bindBuffer(_gl.PIXEL_PACK_BUFFER, glBuffer); _gl.getBufferSubData(_gl.PIXEL_PACK_BUFFER, 0, buffer); } finally { _gl.deleteBuffer(glBuffer); _gl.deleteSync(sync); } return buffer; } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function ( texture, position = null, level = 0, ) { // support previous signature with position first if (texture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyFramebufferToTexture function signature has changed.", ); position = arguments[0] || null; texture = arguments[1]; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); const x = position !== null ? position.x : 0; const y = position !== null ? position.y : 0; textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, x, y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0, ) { // support previous signature with dstPosition first if (srcTexture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyTextureToTexture function signature has changed.", ); dstPosition = arguments[0] || null; srcTexture = arguments[1]; dstTexture = arguments[2]; level = arguments[3] || 0; srcRegion = null; } let width, height, minX, minY; let dstX, dstY; if (srcRegion !== null) { width = srcRegion.max.x - srcRegion.min.x; height = srcRegion.max.y - srcRegion.min.y; minX = srcRegion.min.x; minY = srcRegion.min.y; } else { width = srcTexture.image.width; height = srcTexture.image.height; minX = 0; minY = 0; } if (dstPosition !== null) { dstX = dstPosition.x; dstY = dstPosition.y; } else { dstX = 0; dstY = 0; } const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const currentUnpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT, ); const currentUnpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const currentUnpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const currentUnpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[level] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, minX); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, minY); if (srcTexture.isDataTexture) { _gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data, ); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data, ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, glFormat, glType, image, ); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, currentUnpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0, ) { // support previous signature with source box first if (srcTexture.isTexture !== true) { // @deprecated, r165 console.warn( "WebGLRenderer: copyTextureToTexture3D function signature has changed.", ); srcRegion = arguments[0] || null; dstPosition = arguments[1] || null; srcTexture = arguments[2]; dstTexture = arguments[3]; level = arguments[4] || 0; } let width, height, depth, minX, minY, minZ; let dstX, dstY, dstZ; const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[level] : srcTexture.image; if (srcRegion !== null) { width = srcRegion.max.x - srcRegion.min.x; height = srcRegion.max.y - srcRegion.min.y; depth = srcRegion.max.z - srcRegion.min.z; minX = srcRegion.min.x; minY = srcRegion.min.y; minZ = srcRegion.min.z; } else { width = image.width; height = image.height; depth = image.depth; minX = 0; minY = 0; minZ = 0; } if (dstPosition !== null) { dstX = dstPosition.x; dstY = dstPosition.y; dstZ = dstPosition.z; } else { dstX = 0; dstY = 0; dstZ = 0; } const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isData3DTexture) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if ( dstTexture.isDataArrayTexture || dstTexture.isCompressedArrayTexture ) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.", ); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha, ); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const currentUnpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT, ); const currentUnpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const currentUnpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const currentUnpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, minX); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, minY); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, minZ); if (srcTexture.isDataTexture || srcTexture.isData3DTexture) { _gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data, ); } else { if (dstTexture.isCompressedArrayTexture) { _gl.compressedTexSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, image.data, ); } else { _gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image, ); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, currentUnpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initRenderTarget = function (target) { if (properties.get(target).__webglFramebuffer === undefined) { textures.setupRenderTarget(target); } }; this.initTexture = function (texture) { if (texture.isCubeTexture) { textures.setTextureCube(texture, 0); } else if (texture.isData3DTexture) { textures.setTexture3D(texture, 0); } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { textures.setTexture2DArray(texture, 0); } else { textures.setTexture2D(texture, 0); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("observe", {detail: this}), ); } } get coordinateSystem() { return WebGLCoordinateSystem; } get outputColorSpace() { return this._outputColorSpace; } set outputColorSpace(colorSpace) { this._outputColorSpace = colorSpace; const gl = this.getContext(); gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? "display-p3" : "srgb"; gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? "display-p3" : "srgb"; } } class FogExp2 { constructor(color, density = 0.00025) { this.isFogExp2 = true; this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON(/* meta */) { return { type: "FogExp2", name: this.name, color: this.color.getHex(), density: this.density, }; } } class Fog { constructor(color, near = 1, far = 1000) { this.isFog = true; this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON(/* meta */) { return { type: "Fog", name: this.name, color: this.color.getHex(), near: this.near, far: this.far, }; } } class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.backgroundRotation = new Euler(); this.environmentIntensity = 1; this.environmentRotation = new Euler(); this.overrideMaterial = null; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("observe", {detail: this}), ); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; this.backgroundRotation.copy(source.backgroundRotation); this.environmentIntensity = source.environmentIntensity; this.environmentRotation.copy(source.environmentRotation); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); if (this.backgroundBlurriness > 0) data.object.backgroundBlurriness = this.backgroundBlurriness; if (this.backgroundIntensity !== 1) data.object.backgroundIntensity = this.backgroundIntensity; data.object.backgroundRotation = this.backgroundRotation.toArray(); if (this.environmentIntensity !== 1) data.object.environmentIntensity = this.environmentIntensity; data.object.environmentRotation = this.environmentRotation.toArray(); return data; } } class InterleavedBuffer { constructor(array, stride) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this._updateRange = {offset: 0, count: -1}; this.updateRanges = []; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } get updateRange() { warnOnce( "THREE.InterleavedBuffer: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.", ); // @deprecated, r159 return this._updateRange; } setUsage(value) { this.usage = value; return this; } addUpdateRange(start, count) { this.updateRanges.push({start, count}); } clearUpdateRanges() { this.updateRanges.length = 0; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor( data.arrayBuffers[this.array.buffer._uuid], ); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.from( new Uint32Array(this.array.buffer), ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride, }; } } const _vector$6 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.fromBufferAttribute(this, i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } getComponent(index, component) { let value = this.array[index * this.data.stride + this.offset + component]; if (this.normalized) value = denormalize(value, this.array); return value; } setComponent(index, component, value) { if (this.normalized) value = normalize(value, this.array); this.data.array[index * this.data.stride + this.offset + component] = value; return this; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { let x = this.data.array[index * this.data.stride + this.offset]; if (this.normalized) x = denormalize(x, this.array); return x; } getY(index) { let y = this.data.array[index * this.data.stride + this.offset + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } getZ(index) { let z = this.data.array[index * this.data.stride + this.offset + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } getW(index) { let w = this.data.array[index * this.data.stride + this.offset + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setXY(index, x, y) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.", ); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute( new this.array.constructor(array), this.itemSize, this.normalized, ); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute( data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized, ); } } toJSON(data) { if (data === undefined) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.", ); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized, }; } else { // save as true interleaved attribute if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized, }; } } } class SpriteMaterial extends Material { constructor(parameters) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor(material = new SpriteMaterial()) { super(); this.isSprite = true; this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([ -0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1, ]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute( "position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false), ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false), ); } this.geometry = _geometry; this.material = material; this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.", ); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld, ); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex( _vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); transformVertex( _vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); transformVertex( _vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint, ); if (intersect === null) { // check second triangle transformVertex( _vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos, ); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint, ); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2(), ), face: null, object: this, }); } copy(source, recursive) { super.copy(source, recursive); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition .subVectors(vertexPosition, center) .addScalar(0.5) .multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/ new Vector3(); const _v2$1 = /*@__PURE__*/ new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [], }, isLOD: { value: true, }, }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance, level.hysteresis); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0, hysteresis = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, hysteresis: hysteresis, object: object, }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance < levelDistance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance >= levelDistance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance, hysteresis: level.hysteresis, }); } return data; } } const _basePosition = /*@__PURE__*/ new Vector3(); const _skinIndex = /*@__PURE__*/ new Vector4(); const _skinWeight = /*@__PURE__*/ new Vector4(); const _vector3 = /*@__PURE__*/ new Vector3(); const _matrix4 = /*@__PURE__*/ new Matrix4(); const _vertex = /*@__PURE__*/ new Vector3(); const _sphere$4 = /*@__PURE__*/ new Sphere(); const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.isSkinnedMesh = true; this.type = "SkinnedMesh"; this.bindMode = AttachedBindMode; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if (this.boundingBox === null) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute("position"); for (let i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingBox.expandByPoint(_vertex); } } computeBoundingSphere() { const geometry = this.geometry; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute("position"); for (let i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingSphere.expandByPoint(_vertex); } } copy(source, recursive) { super.copy(source, recursive); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast(raycaster, intersects) { const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$4.copy(this.boundingSphere); _sphere$4.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$4) === false) return; // convert ray to local space of skinned mesh _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // test with bounding box in local space if (this.boundingBox !== null) { if (_ray$2.intersectsBox(this.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$2); } getVertexPosition(index, target) { super.getVertexPosition(index, target); this.applyBoneTransform(index, target); return target; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.fromBufferAttribute(skinWeight, i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === AttachedBindMode) { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === DetachedBindMode) { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode, ); } } applyBoneTransform(index, vector) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(vector).applyMatrix4(this.bindMatrix); vector.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix4.multiplyMatrices( skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex], ); vector.addScaledVector( _vector3.copy(_basePosition).applyMatrix4(_matrix4), weight, ); } } return vector.applyMatrix4(this.bindMatrixInverse); } } class Bone extends Object3D { constructor() { super(); this.isBone = true; this.type = "Bone"; } } class DataTexture extends Texture { constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace, ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isDataTexture = true; this.image = {data: data, width: width, height: height}; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } const _offsetMatrix = /*@__PURE__*/ new Matrix4(); const _identityMatrix$1 = /*@__PURE__*/ new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn( "THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.", ); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix$1; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = Math.ceil(size / 4) * 4; size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType, ); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Skeleton", generator: "Skeleton.toJSON", }, bones: [], boneInverses: [], }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { super(array, itemSize, normalized); this.isInstancedBufferAttribute = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); const _instanceIntersects = []; const _box3 = /*@__PURE__*/ new Box3(); const _identity = /*@__PURE__*/ new Matrix4(); const _mesh$1 = /*@__PURE__*/ new Mesh(); const _sphere$3 = /*@__PURE__*/ new Sphere(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.isInstancedMesh = true; this.instanceMatrix = new InstancedBufferAttribute( new Float32Array(count * 16), 16, ); this.instanceColor = null; this.morphTexture = null; this.count = count; this.boundingBox = null; this.boundingSphere = null; for (let i = 0; i < count; i++) { this.setMatrixAt(i, _identity); } } computeBoundingBox() { const geometry = this.geometry; const count = this.count; if (this.boundingBox === null) { this.boundingBox = new Box3(); } if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } this.boundingBox.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _box3.copy(geometry.boundingBox).applyMatrix4(_instanceLocalMatrix); this.boundingBox.union(_box3); } } computeBoundingSphere() { const geometry = this.geometry; const count = this.count; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } if (geometry.boundingSphere === null) { geometry.computeBoundingSphere(); } this.boundingSphere.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _sphere$3 .copy(geometry.boundingSphere) .applyMatrix4(_instanceLocalMatrix); this.boundingSphere.union(_sphere$3); } } copy(source, recursive) { super.copy(source, recursive); this.instanceMatrix.copy(source.instanceMatrix); if (source.morphTexture !== null) this.morphTexture = source.morphTexture.clone(); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } getMorphAt(index, object) { const objectInfluences = object.morphTargetInfluences; const array = this.morphTexture.source.data.data; const len = objectInfluences.length + 1; // All influences + the baseInfluenceSum const dataIndex = index * len + 1; // Skip the baseInfluenceSum at the beginning for (let i = 0; i < objectInfluences.length; i++) { objectInfluences[i] = array[dataIndex + i]; } } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh$1.geometry = this.geometry; _mesh$1.material = this.material; if (_mesh$1.material === undefined) return; // test with bounding sphere first if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$3.copy(this.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // now test each instance for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh$1.matrixWorld = _instanceWorldMatrix; _mesh$1.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute( new Float32Array(this.instanceMatrix.count * 3), 3, ); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } setMorphAt(index, object) { const objectInfluences = object.morphTargetInfluences; const len = objectInfluences.length + 1; // morphBaseInfluence + all influences if (this.morphTexture === null) { this.morphTexture = new DataTexture( new Float32Array(len * this.count), len, this.count, RedFormat, FloatType, ); } const array = this.morphTexture.source.data.data; let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = this.geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; const dataIndex = len * index; array[dataIndex] = morphBaseInfluence; array.set(objectInfluences, dataIndex + 1); } updateMorphTargets() {} dispose() { this.dispatchEvent({type: "dispose"}); if (this.morphTexture !== null) { this.morphTexture.dispose(); this.morphTexture = null; } return this; } } function sortOpaque(a, b) { return a.z - b.z; } function sortTransparent(a, b) { return b.z - a.z; } class MultiDrawRenderList { constructor() { this.index = 0; this.pool = []; this.list = []; } push(drawRange, z) { const pool = this.pool; const list = this.list; if (this.index >= pool.length) { pool.push({ start: -1, count: -1, z: -1, }); } const item = pool[this.index]; list.push(item); this.index++; item.start = drawRange.start; item.count = drawRange.count; item.z = z; } reset() { this.list.length = 0; this.index = 0; } } const ID_ATTR_NAME = "batchId"; const _matrix$1 = /*@__PURE__*/ new Matrix4(); const _invMatrixWorld = /*@__PURE__*/ new Matrix4(); const _identityMatrix = /*@__PURE__*/ new Matrix4(); const _whiteColor = /*@__PURE__*/ new Color(1, 1, 1); const _projScreenMatrix$2 = /*@__PURE__*/ new Matrix4(); const _frustum = /*@__PURE__*/ new Frustum(); const _box$1 = /*@__PURE__*/ new Box3(); const _sphere$2 = /*@__PURE__*/ new Sphere(); const _vector$5 = /*@__PURE__*/ new Vector3(); const _forward = /*@__PURE__*/ new Vector3(); const _temp = /*@__PURE__*/ new Vector3(); const _renderList = /*@__PURE__*/ new MultiDrawRenderList(); const _mesh = /*@__PURE__*/ new Mesh(); const _batchIntersects = []; // @TODO: SkinnedMesh support? // @TODO: geometry.groups support? // @TODO: geometry.drawRange support? // @TODO: geometry.morphAttributes support? // @TODO: Support uniform parameter per geometry // @TODO: Add an "optimize" function to pack geometry and remove data gaps // copies data from attribute "src" into "target" starting at "targetOffset" function copyAttributeData(src, target, targetOffset = 0) { const itemSize = target.itemSize; if ( src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor ) { // use the component getters and setters if the array data cannot // be copied directly const vertexCount = src.count; for (let i = 0; i < vertexCount; i++) { for (let c = 0; c < itemSize; c++) { target.setComponent(i + targetOffset, c, src.getComponent(i, c)); } } } else { // faster copy approach using typed array set function target.array.set(src.array, targetOffset * itemSize); } target.needsUpdate = true; } class BatchedMesh extends Mesh { get maxGeometryCount() { return this._maxGeometryCount; } constructor( maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material, ) { super(new BufferGeometry(), material); this.isBatchedMesh = true; this.perObjectFrustumCulled = true; this.sortObjects = true; this.boundingBox = null; this.boundingSphere = null; this.customSort = null; this._drawRanges = []; this._reservedRanges = []; this._visibility = []; this._active = []; this._bounds = []; this._maxGeometryCount = maxGeometryCount; this._maxVertexCount = maxVertexCount; this._maxIndexCount = maxIndexCount; this._geometryInitialized = false; this._geometryCount = 0; this._multiDrawCounts = new Int32Array(maxGeometryCount); this._multiDrawStarts = new Int32Array(maxGeometryCount); this._multiDrawCount = 0; this._multiDrawInstances = null; this._visibilityChanged = true; // Local matrix per geometry by using data texture this._matricesTexture = null; this._initMatricesTexture(); // Local color per geometry by using data texture this._colorsTexture = null; } _initMatricesTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 matrices * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 matrices * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64) let size = Math.sqrt(this._maxGeometryCount * 4); // 4 pixels needed for 1 matrix size = Math.ceil(size / 4) * 4; size = Math.max(size, 4); const matricesArray = new Float32Array(size * size * 4); // 4 floats per RGBA pixel const matricesTexture = new DataTexture( matricesArray, size, size, RGBAFormat, FloatType, ); this._matricesTexture = matricesTexture; } _initColorsTexture() { let size = Math.sqrt(this._maxGeometryCount); size = Math.ceil(size); // 4 floats per RGBA pixel initialized to white const colorsArray = new Float32Array(size * size * 4).fill(1); const colorsTexture = new DataTexture( colorsArray, size, size, RGBAFormat, FloatType, ); colorsTexture.colorSpace = ColorManagement.workingColorSpace; this._colorsTexture = colorsTexture; } _initializeGeometry(reference) { const geometry = this.geometry; const maxVertexCount = this._maxVertexCount; const maxGeometryCount = this._maxGeometryCount; const maxIndexCount = this._maxIndexCount; if (this._geometryInitialized === false) { for (const attributeName in reference.attributes) { const srcAttribute = reference.getAttribute(attributeName); const {array, itemSize, normalized} = srcAttribute; const dstArray = new array.constructor(maxVertexCount * itemSize); const dstAttribute = new BufferAttribute( dstArray, itemSize, normalized, ); geometry.setAttribute(attributeName, dstAttribute); } if (reference.getIndex() !== null) { const indexArray = maxVertexCount > 65536 ? new Uint32Array(maxIndexCount) : new Uint16Array(maxIndexCount); geometry.setIndex(new BufferAttribute(indexArray, 1)); } const idArray = maxGeometryCount > 65536 ? new Uint32Array(maxVertexCount) : new Uint16Array(maxVertexCount); geometry.setAttribute(ID_ATTR_NAME, new BufferAttribute(idArray, 1)); this._geometryInitialized = true; } } // Make sure the geometry is compatible with the existing combined geometry attributes _validateGeometry(geometry) { // check that the geometry doesn"t have a version of our reserved id attribute if (geometry.getAttribute(ID_ATTR_NAME)) { throw new Error( `BatchedMesh: Geometry cannot use attribute "${ID_ATTR_NAME}"`, ); } // check to ensure the geometries are using consistent attributes and indices const batchGeometry = this.geometry; if (Boolean(geometry.getIndex()) !== Boolean(batchGeometry.getIndex())) { throw new Error( "BatchedMesh: All geometries must consistently have "index".", ); } for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } if (!geometry.hasAttribute(attributeName)) { throw new Error( `BatchedMesh: Added geometry missing "${attributeName}". All geometries must have consistent attributes.`, ); } const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) { throw new Error( "BatchedMesh: All attributes must have a consistent itemSize and normalized value.", ); } } } setCustomSort(func) { this.customSort = func; return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const geometryCount = this._geometryCount; const boundingBox = this.boundingBox; const active = this._active; boundingBox.makeEmpty(); for (let i = 0; i < geometryCount; i++) { if (active[i] === false) continue; this.getMatrixAt(i, _matrix$1); this.getBoundingBoxAt(i, _box$1).applyMatrix4(_matrix$1); boundingBox.union(_box$1); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const geometryCount = this._geometryCount; const boundingSphere = this.boundingSphere; const active = this._active; boundingSphere.makeEmpty(); for (let i = 0; i < geometryCount; i++) { if (active[i] === false) continue; this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); boundingSphere.union(_sphere$2); } } addGeometry(geometry, vertexCount = -1, indexCount = -1) { this._initializeGeometry(geometry); this._validateGeometry(geometry); // ensure we"re not over geometry if (this._geometryCount >= this._maxGeometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } // get the necessary range fo the geometry const reservedRange = { vertexStart: -1, vertexCount: -1, indexStart: -1, indexCount: -1, }; let lastRange = null; const reservedRanges = this._reservedRanges; const drawRanges = this._drawRanges; const bounds = this._bounds; if (this._geometryCount !== 0) { lastRange = reservedRanges[reservedRanges.length - 1]; } if (vertexCount === -1) { reservedRange.vertexCount = geometry.getAttribute("position").count; } else { reservedRange.vertexCount = vertexCount; } if (lastRange === null) { reservedRange.vertexStart = 0; } else { reservedRange.vertexStart = lastRange.vertexStart + lastRange.vertexCount; } const index = geometry.getIndex(); const hasIndex = index !== null; if (hasIndex) { if (indexCount === -1) { reservedRange.indexCount = index.count; } else { reservedRange.indexCount = indexCount; } if (lastRange === null) { reservedRange.indexStart = 0; } else { reservedRange.indexStart = lastRange.indexStart + lastRange.indexCount; } } if ( (reservedRange.indexStart !== -1 && reservedRange.indexStart + reservedRange.indexCount > this._maxIndexCount) || reservedRange.vertexStart + reservedRange.vertexCount > this._maxVertexCount ) { throw new Error( "BatchedMesh: Reserved space request exceeds the maximum buffer size.", ); } const visibility = this._visibility; const active = this._active; const matricesTexture = this._matricesTexture; const matricesArray = this._matricesTexture.image.data; const colorsTexture = this._colorsTexture; // push new visibility states visibility.push(true); active.push(true); // update id const geometryId = this._geometryCount; this._geometryCount++; // initialize matrix information _identityMatrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; // initialize the color to white if (colorsTexture !== null) { _whiteColor.toArray(colorsTexture.image.data, geometryId * 4); colorsTexture.needsUpdate = true; } // add the reserved range and draw range objects reservedRanges.push(reservedRange); drawRanges.push({ start: hasIndex ? reservedRange.indexStart : reservedRange.vertexStart, count: -1, }); bounds.push({ boxInitialized: false, box: new Box3(), sphereInitialized: false, sphere: new Sphere(), }); // set the id for the geometry const idAttribute = this.geometry.getAttribute(ID_ATTR_NAME); for (let i = 0; i < reservedRange.vertexCount; i++) { idAttribute.setX(reservedRange.vertexStart + i, geometryId); } idAttribute.needsUpdate = true; // update the geometry this.setGeometryAt(geometryId, geometry); return geometryId; } setGeometryAt(id, geometry) { if (id >= this._geometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } this._validateGeometry(geometry); const batchGeometry = this.geometry; const hasIndex = batchGeometry.getIndex() !== null; const dstIndex = batchGeometry.getIndex(); const srcIndex = geometry.getIndex(); const reservedRange = this._reservedRanges[id]; if ( (hasIndex && srcIndex.count > reservedRange.indexCount) || geometry.attributes.position.count > reservedRange.vertexCount ) { throw new Error( "BatchedMesh: Reserved space not large enough for provided geometry.", ); } // copy geometry over const vertexStart = reservedRange.vertexStart; const vertexCount = reservedRange.vertexCount; for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } // copy attribute data const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); copyAttributeData(srcAttribute, dstAttribute, vertexStart); // fill the rest in with zeroes const itemSize = srcAttribute.itemSize; for (let i = srcAttribute.count, l = vertexCount; i < l; i++) { const index = vertexStart + i; for (let c = 0; c < itemSize; c++) { dstAttribute.setComponent(index, c, 0); } } dstAttribute.needsUpdate = true; dstAttribute.addUpdateRange( vertexStart * itemSize, vertexCount * itemSize, ); } // copy index if (hasIndex) { const indexStart = reservedRange.indexStart; // copy index data over for (let i = 0; i < srcIndex.count; i++) { dstIndex.setX(indexStart + i, vertexStart + srcIndex.getX(i)); } // fill the rest in with zeroes for (let i = srcIndex.count, l = reservedRange.indexCount; i < l; i++) { dstIndex.setX(indexStart + i, vertexStart); } dstIndex.needsUpdate = true; dstIndex.addUpdateRange(indexStart, reservedRange.indexCount); } // store the bounding boxes const bound = this._bounds[id]; if (geometry.boundingBox !== null) { bound.box.copy(geometry.boundingBox); bound.boxInitialized = true; } else { bound.boxInitialized = false; } if (geometry.boundingSphere !== null) { bound.sphere.copy(geometry.boundingSphere); bound.sphereInitialized = true; } else { bound.sphereInitialized = false; } // set drawRange count const drawRange = this._drawRanges[id]; const posAttr = geometry.getAttribute("position"); drawRange.count = hasIndex ? srcIndex.count : posAttr.count; this._visibilityChanged = true; return id; } deleteGeometry(geometryId) { // Note: User needs to call optimize() afterward to pack the data. const active = this._active; if (geometryId >= active.length || active[geometryId] === false) { return this; } active[geometryId] = false; this._visibilityChanged = true; return this; } getInstanceCountAt(id) { if (this._multiDrawInstances === null) return null; return this._multiDrawInstances[id]; } setInstanceCountAt(id, instanceCount) { if (this._multiDrawInstances === null) { this._multiDrawInstances = new Int32Array(this._maxGeometryCount).fill(1); } this._multiDrawInstances[id] = instanceCount; return id; } // get bounding box and compute it if it doesn"t exist getBoundingBoxAt(id, target) { const active = this._active; if (active[id] === false) { return null; } // compute bounding box const bound = this._bounds[id]; const box = bound.box; const geometry = this.geometry; if (bound.boxInitialized === false) { box.makeEmpty(); const index = geometry.index; const position = geometry.attributes.position; const drawRange = this._drawRanges[id]; for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i++ ) { let iv = i; if (index) { iv = index.getX(iv); } box.expandByPoint(_vector$5.fromBufferAttribute(position, iv)); } bound.boxInitialized = true; } target.copy(box); return target; } // get bounding sphere and compute it if it doesn"t exist getBoundingSphereAt(id, target) { const active = this._active; if (active[id] === false) { return null; } // compute bounding sphere const bound = this._bounds[id]; const sphere = bound.sphere; const geometry = this.geometry; if (bound.sphereInitialized === false) { sphere.makeEmpty(); this.getBoundingBoxAt(id, _box$1); _box$1.getCenter(sphere.center); const index = geometry.index; const position = geometry.attributes.position; const drawRange = this._drawRanges[id]; let maxRadiusSq = 0; for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i++ ) { let iv = i; if (index) { iv = index.getX(iv); } _vector$5.fromBufferAttribute(position, iv); maxRadiusSq = Math.max( maxRadiusSq, sphere.center.distanceToSquared(_vector$5), ); } sphere.radius = Math.sqrt(maxRadiusSq); bound.sphereInitialized = true; } target.copy(sphere); return target; } setMatrixAt(geometryId, matrix) { // @TODO: Map geometryId to index of the arrays because // optimize() can make geometryId mismatch the index const active = this._active; const matricesTexture = this._matricesTexture; const matricesArray = this._matricesTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return this; } matrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; return this; } getMatrixAt(geometryId, matrix) { const active = this._active; const matricesArray = this._matricesTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return null; } return matrix.fromArray(matricesArray, geometryId * 16); } setColorAt(geometryId, color) { if (this._colorsTexture === null) { this._initColorsTexture(); } // @TODO: Map geometryId to index of the arrays because // optimize() can make geometryId mismatch the index const active = this._active; const colorsTexture = this._colorsTexture; const colorsArray = this._colorsTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return this; } color.toArray(colorsArray, geometryId * 4); colorsTexture.needsUpdate = true; return this; } getColorAt(geometryId, color) { const active = this._active; const colorsArray = this._colorsTexture.image.data; const geometryCount = this._geometryCount; if (geometryId >= geometryCount || active[geometryId] === false) { return null; } return color.fromArray(colorsArray, geometryId * 4); } setVisibleAt(geometryId, value) { const visibility = this._visibility; const active = this._active; const geometryCount = this._geometryCount; // if the geometry is out of range, not active, or visibility state // does not change then return early if ( geometryId >= geometryCount || active[geometryId] === false || visibility[geometryId] === value ) { return this; } visibility[geometryId] = value; this._visibilityChanged = true; return this; } getVisibleAt(geometryId) { const visibility = this._visibility; const active = this._active; const geometryCount = this._geometryCount; // return early if the geometry is out of range or not active if (geometryId >= geometryCount || active[geometryId] === false) { return false; } return visibility[geometryId]; } raycast(raycaster, intersects) { const visibility = this._visibility; const active = this._active; const drawRanges = this._drawRanges; const geometryCount = this._geometryCount; const matrixWorld = this.matrixWorld; const batchGeometry = this.geometry; // iterate over each geometry _mesh.material = this.material; _mesh.geometry.index = batchGeometry.index; _mesh.geometry.attributes = batchGeometry.attributes; if (_mesh.geometry.boundingBox === null) { _mesh.geometry.boundingBox = new Box3(); } if (_mesh.geometry.boundingSphere === null) { _mesh.geometry.boundingSphere = new Sphere(); } for (let i = 0; i < geometryCount; i++) { if (!visibility[i] || !active[i]) { continue; } const drawRange = drawRanges[i]; _mesh.geometry.setDrawRange(drawRange.start, drawRange.count); // ge the intersects this.getMatrixAt(i, _mesh.matrixWorld).premultiply(matrixWorld); this.getBoundingBoxAt(i, _mesh.geometry.boundingBox); this.getBoundingSphereAt(i, _mesh.geometry.boundingSphere); _mesh.raycast(raycaster, _batchIntersects); // add batch id to the intersects for (let j = 0, l = _batchIntersects.length; j < l; j++) { const intersect = _batchIntersects[j]; intersect.object = this; intersect.batchId = i; intersects.push(intersect); } _batchIntersects.length = 0; } _mesh.material = null; _mesh.geometry.index = null; _mesh.geometry.attributes = {}; _mesh.geometry.setDrawRange(0, Infinity); } copy(source) { super.copy(source); this.geometry = source.geometry.clone(); this.perObjectFrustumCulled = source.perObjectFrustumCulled; this.sortObjects = source.sortObjects; this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null; this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null; this._drawRanges = source._drawRanges.map(range => ({...range})); this._reservedRanges = source._reservedRanges.map(range => ({...range})); this._visibility = source._visibility.slice(); this._active = source._active.slice(); this._bounds = source._bounds.map(bound => ({ boxInitialized: bound.boxInitialized, box: bound.box.clone(), sphereInitialized: bound.sphereInitialized, sphere: bound.sphere.clone(), })); this._maxGeometryCount = source._maxGeometryCount; this._maxVertexCount = source._maxVertexCount; this._maxIndexCount = source._maxIndexCount; this._geometryInitialized = source._geometryInitialized; this._geometryCount = source._geometryCount; this._multiDrawCounts = source._multiDrawCounts.slice(); this._multiDrawStarts = source._multiDrawStarts.slice(); this._matricesTexture = source._matricesTexture.clone(); this._matricesTexture.image.data = this._matricesTexture.image.slice(); if (this._colorsTexture !== null) { this._colorsTexture = source._colorsTexture.clone(); this._colorsTexture.image.data = this._colorsTexture.image.slice(); } return this; } dispose() { // Assuming the geometry is not shared with other meshes this.geometry.dispose(); this._matricesTexture.dispose(); this._matricesTexture = null; if (this._colorsTexture !== null) { this._colorsTexture.dispose(); this._colorsTexture = null; } return this; } onBeforeRender(renderer, scene, camera, geometry, material /*, _group*/) { // if visibility has not changed and frustum culling and object sorting is not required // then skip iterating over all items if ( !this._visibilityChanged && !this.perObjectFrustumCulled && !this.sortObjects ) { return; } // the indexed version of the multi draw function requires specifying the start // offset in bytes. const index = geometry.getIndex(); const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT; const active = this._active; const visibility = this._visibility; const multiDrawStarts = this._multiDrawStarts; const multiDrawCounts = this._multiDrawCounts; const drawRanges = this._drawRanges; const perObjectFrustumCulled = this.perObjectFrustumCulled; // prepare the frustum in the local frame if (perObjectFrustumCulled) { _projScreenMatrix$2 .multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse) .multiply(this.matrixWorld); _frustum.setFromProjectionMatrix( _projScreenMatrix$2, renderer.coordinateSystem, ); } let count = 0; if (this.sortObjects) { // get the camera position in the local frame _invMatrixWorld.copy(this.matrixWorld).invert(); _vector$5 .setFromMatrixPosition(camera.matrixWorld) .applyMatrix4(_invMatrixWorld); _forward .set(0, 0, -1) .transformDirection(camera.matrixWorld) .transformDirection(_invMatrixWorld); for (let i = 0, l = visibility.length; i < l; i++) { if (visibility[i] && active[i]) { // get the bounds in world space this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); // determine whether the batched geometry is within the frustum let culled = false; if (perObjectFrustumCulled) { culled = !_frustum.intersectsSphere(_sphere$2); } if (!culled) { // get the distance from camera used for sorting const z = _temp .subVectors(_sphere$2.center, _vector$5) .dot(_forward); _renderList.push(drawRanges[i], z); } } } // Sort the draw ranges and prep for rendering const list = _renderList.list; const customSort = this.customSort; if (customSort === null) { list.sort(material.transparent ? sortTransparent : sortOpaque); } else { customSort.call(this, list, camera); } for (let i = 0, l = list.length; i < l; i++) { const item = list[i]; multiDrawStarts[count] = item.start * bytesPerElement; multiDrawCounts[count] = item.count; count++; } _renderList.reset(); } else { for (let i = 0, l = visibility.length; i < l; i++) { if (visibility[i] && active[i]) { // determine whether the batched geometry is within the frustum let culled = false; if (perObjectFrustumCulled) { // get the bounds in world space this.getMatrixAt(i, _matrix$1); this.getBoundingSphereAt(i, _sphere$2).applyMatrix4(_matrix$1); culled = !_frustum.intersectsSphere(_sphere$2); } if (!culled) { const range = drawRanges[i]; multiDrawStarts[count] = range.start * bytesPerElement; multiDrawCounts[count] = range.count; count++; } } } } this._multiDrawCount = count; this._visibilityChanged = false; } onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial /* , group */, ) { this.onBeforeRender(renderer, null, shadowCamera, geometry, depthMaterial); } } class LineBasicMaterial extends Material { constructor(parameters) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.map = null; this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _vStart = /*@__PURE__*/ new Vector3(); const _vEnd = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); const _intersectPointOnRay = /*@__PURE__*/ new Vector3(); const _intersectPointOnSegment = /*@__PURE__*/ new Vector3(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial(), ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _vStart.fromBufferAttribute(positionAttribute, i - 1); _vEnd.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _vStart.distanceTo(_vEnd); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute(lineDistances, 1), ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.", ); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, ); if (intersect) { intersects.push(intersect); } } if (this.isLineLoop) { const a = index.getX(end - 1); const b = index.getX(start); const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, ); if (intersect) { intersects.push(intersect); } } } else { const start = Math.max(0, drawRange.start); const end = Math.min( positionAttribute.count, drawRange.start + drawRange.count, ); for (let i = start, l = end - 1; i < l; i += step) { const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, i, i + 1, ); if (intersect) { intersects.push(intersect); } } if (this.isLineLoop) { const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, end - 1, start, ); if (intersect) { intersects.push(intersect); } } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } function checkIntersection(object, raycaster, ray, thresholdSq, a, b) { const positionAttribute = object.geometry.attributes.position; _vStart.fromBufferAttribute(positionAttribute, a); _vEnd.fromBufferAttribute(positionAttribute, b); const distSq = ray.distanceSqToSegment( _vStart, _vEnd, _intersectPointOnRay, _intersectPointOnSegment, ); if (distSq > thresholdSq) return; _intersectPointOnRay.applyMatrix4(object.matrixWorld); // Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(_intersectPointOnRay); if (distance < raycaster.near || distance > raycaster.far) return; return { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: _intersectPointOnSegment.clone().applyMatrix4(object.matrixWorld), index: a, face: null, faceIndex: null, object: object, }; } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute(lineDistances, 1), ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.", ); } return this; } } class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.isLineLoop = true; this.type = "LineLoop"; } } class PointsMaterial extends Material { constructor(parameters) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial(), ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = Array.isArray(source.material) ? source.material.slice() : source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this, ); } } else { const start = Math.max(0, drawRange.start); const end = Math.min( positionAttribute.count, drawRange.start + drawRange.count, ); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this, ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object, ) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object, }); } } class VideoTexture extends Texture { constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ) { super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isVideoTexture = true; this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = "requestVideoFrameCallback" in video; if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } class FramebufferTexture extends Texture { constructor(width, height) { super({width, height}); this.isFramebufferTexture = true; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } class CompressedTexture extends Texture { constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace, ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace, ); this.isCompressedTexture = true; this.image = {width: width, height: height}; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } class CompressedArrayTexture extends CompressedTexture { constructor(mipmaps, width, height, depth, format, type) { super(mipmaps, width, height, format, type); this.isCompressedArrayTexture = true; this.image.depth = depth; this.wrapR = ClampToEdgeWrapping; this.layerUpdates = new Set(); } addLayerUpdates(layerIndex) { this.layerUpdates.add(layerIndex); } clearLayerUpdates() { this.layerUpdates.clear(); } } class CompressedCubeTexture extends CompressedTexture { constructor(images, format, type) { super( undefined, images[0].width, images[0].height, format, type, CubeReflectionMapping, ); this.isCompressedCubeTexture = true; this.isCubeTexture = true; this.image = images; } } class CanvasTexture extends Texture { constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ) { super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, ); this.isCanvasTexture = true; this.needsUpdate = true; } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint(/* t, optionalTarget */) { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if ( this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if ( tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0 ) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals, }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Curve", generator: "Curve.toJSON", }, }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0, ) { super(); this.isEllipseCurve = true; this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.isArcCurve = true; this.type = "ArcCurve"; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; }, }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5, ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return ( (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1 ); } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return ( QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2) ); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return ( CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3) ); } class CubicBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2(), ) { super(); this.isCubicBezierCurve = true; this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class CubicBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3(), ) { super(); this.isCubicBezierCurve3 = true; this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.isLineCurve = true; this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector2()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.isLineCurve3 = true; this.type = "LineCurve3"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector3()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.isQuadraticBezierCurve = true; this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.isQuadraticBezierCurve3 = true; this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z), ); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class SplineCurve extends Curve { constructor(points = []) { super(); this.isSplineCurve = true; this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set( CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y), ); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } var Curves = /*#__PURE__*/ Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve, }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { const lineType = startPoint.isVector2 === true ? "LineCurve" : "LineCurve3"; this.curves.push(new Curves[lineType](endPoint, startPoint)); } return this; } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0]) ) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY), ); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY), ); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise, ); return this; } ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ); return this; } absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ) { const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, ); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class LatheGeometry extends BufferGeometry { constructor( points = [new Vector2(0, -0.5), new Vector2(0.5, 0), new Vector2(0, 0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2, ) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength, }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength, ); } } class CapsuleGeometry extends LatheGeometry { constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { const path = new Path(); path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0); path.absarc(0, length / 2, radius, 0, Math.PI * 0.5); super(path.getPoints(capSegments), radialSegments); this.type = "CapsuleGeometry"; this.parameters = { radius: radius, length: length, capSegments: capSegments, radialSegments: radialSegments, }; } static fromJSON(data) { return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments, ); } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength, }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + (s / segments) * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength, ); } } class CylinderGeometry extends BufferGeometry { constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength, }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength, ); } } class ConeGeometry extends CylinderGeometry { constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2, ) { super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength, ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength, }; } static fromJSON(data) { return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength, ); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail, }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute( "normal", new Float32BufferAttribute(vertexBuffer.slice(), 3), ); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivision with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2( -vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z), ); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details, ); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [ // (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r, ]; const indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9, ]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = /*@__PURE__*/ new Vector3(); const _v1$1 = /*@__PURE__*/ new Vector3(); const _normal = /*@__PURE__*/ new Vector3(); const _triangle = /*@__PURE__*/ new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle, }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const {a, b, c} = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if ( hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0] ) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone(), }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const {index0, index1} = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions), }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.4) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); return triangles; }, }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push((prev.i / dim) | 0); triangles.push((ear.i / dim) | 0); triangles.push((next.i / dim) | 0); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx, y0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy, x1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx, y1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy; let p = c.next; while (p !== a) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx, y0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy, x1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx, y1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy; // z-order range for the current triangle bbox; const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.prevZ; if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0 ) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0 ) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0 ) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if ( !equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a) ) { triangles.push((a.i / dim) | 0); triangles.push((p.i / dim) | 0); triangles.push((b.i / dim) | 0); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize, 0); earcutLinked(c, triangles, dim, minX, minY, invSize, 0); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { outerNode = eliminateHole(queue[i], outerNode); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole(hole, outerNode) { const bridge = findHoleBridge(hole, outerNode); if (!bridge) { return outerNode; } const bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts filterPoints(bridgeReverse, bridgeReverse.next); return filterPoints(bridge, bridge.next); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode, qx = -Infinity, m; const hx = hole.x, hy = hole.y; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + ((hy - p.y) * (p.next.x - p.x)) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while (p !== outerNode); if (!m) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y, ) ) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if ( locallyInside(p, hole) && (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p))))) ) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || (qSize > 0 && q)) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = ((x - minX) * invSize) | 0; y = ((y - minY) * invSize) | 0; x = (x | (x << 8)) & 0x00ff00ff; x = (x | (x << 4)) & 0x0f0f0f0f; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; y = (y | (y << 8)) & 0x00ff00ff; y = (y | (y << 4)) & 0x0f0f0f0f; y = (y | (y << 2)) & 0x33333333; y = (y | (y << 1)) & 0x55555555; return x | (y << 1); } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return ( (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py) ); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return ( a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones"t intersect other edges ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible (area(a.prev, a, b.prev) || area(a, b.prev, b))) || // does not create opposite-facing sectors (equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0)) ); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return ( q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y) ); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b) ) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if ( p.y > py !== p.next.y > py && p.next.y !== p.y && px < ((p.next.x - p.x) * (py - p.y)) / (p.next.y - p.y) + p.x ) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor( shapes = new Shape([ new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5), ]), options = {}, ) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options, }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; const depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return pt.clone().addScaledVector(vec, size); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y, ); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos((t * Math.PI) / 2); const bs = bevelSize * Math.sin((t * Math.PI) / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, (depth / steps) * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos((t * Math.PI) / 2); const bs = bevelSize * Math.sin((t * Math.PI) / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v( vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z, ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3( face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps, ); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1, ); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1, ); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON( extrudePath, ); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [ new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y), ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD, ) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [ new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z), ]; } else { return [ new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z), ]; } }, }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } data.options = Object.assign({}, options); if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [ -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1, ]; const indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1, ]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2, ]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2, ) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength, }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + (i / thetaSegments) * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength, ); } } class ShapeGeometry extends BufferGeometry { constructor( shapes = new Shape([ new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5), ]), curveSegments = 12, ) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments, }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // indices for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI, ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength, }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy === 0 && thetaStart === 0) { uOffset = 0.5 / widthSegments; } else if (iy === heightSegments && thetaEnd === Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength, ); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail, }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2, ) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc, }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = (i / tubularSegments) * arc; const v = (j / radialSegments) * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc, ); } } class TorusKnotGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3, ) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q, }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segment const u = (i / tubularSegments) * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = (j / radialSegments) * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = (q / p) * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q, ); } } class TubeGeometry extends BufferGeometry { constructor( path = new QuadraticBezierCurve3( new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0), ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false, ) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed, }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = (j / radialSegments) * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry( new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed, ); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry, }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{start: 0, count: indices.count, materialIndex: 0}]; } // create a data structure that contains all edges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + ((j + 1) % 3)); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + ((j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } var Geometries = /*#__PURE__*/ Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, CapsuleGeometry: CapsuleGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry, }); class ShadowMaterial extends Material { constructor(parameters) { super(); this.isShadowMaterial = true; this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.fog = source.fog; return this; } } class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.isRawShaderMaterial = true; this.type = "RawShaderMaterial"; } } class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.isMeshStandardMaterial = true; this.defines = {STANDARD: ""}; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.envMapIntensity = 1.0; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = {STANDARD: ""}; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.envMapIntensity = source.envMapIntensity; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.isMeshPhysicalMaterial = true; this.defines = { STANDARD: "", PHYSICAL: "", }; this.type = "MeshPhysicalMaterial"; this.anisotropyRotation = 0; this.anisotropyMap = null; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp((2.5 * (this.ior - 1)) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); }, }); this.iridescenceMap = null; this.iridescenceIOR = 1.3; this.iridescenceThicknessRange = [100, 400]; this.iridescenceThicknessMap = null; this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = Infinity; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._anisotropy = 0; this._clearcoat = 0; this._dispersion = 0; this._iridescence = 0; this._sheen = 0.0; this._transmission = 0; this.setValues(parameters); } get anisotropy() { return this._anisotropy; } set anisotropy(value) { if (this._anisotropy > 0 !== value > 0) { this.version++; } this._anisotropy = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get iridescence() { return this._iridescence; } set iridescence(value) { if (this._iridescence > 0 !== value > 0) { this.version++; } this._iridescence = value; } get dispersion() { return this._dispersion; } set dispersion(value) { if (this._dispersion > 0 !== value > 0) { this.version++; } this._dispersion = value; } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { STANDARD: "", PHYSICAL: "", }; this.anisotropy = source.anisotropy; this.anisotropyRotation = source.anisotropyRotation; this.anisotropyMap = source.anisotropyMap; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.dispersion = source.dispersion; this.ior = source.ior; this.iridescence = source.iridescence; this.iridescenceMap = source.iridescenceMap; this.iridescenceIOR = source.iridescenceIOR; this.iridescenceThicknessRange = [...source.iridescenceThicknessRange]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.isMeshPhongMaterial = true; this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshToonMaterial extends Material { constructor(parameters) { super(); this.isMeshToonMaterial = true; this.defines = {TOON: ""}; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.isMeshNormalMaterial = true; this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.isMeshLambertMaterial = true; this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.envMapRotation = new Euler(); this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapRotation.copy(source.envMapRotation); this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.isMeshMatcapMaterial = true; this.defines = {MATCAP: ""}; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = {MATCAP: ""}; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.isLineDashedMaterial = true; this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } // converts an array to a specific type function convertArray(array, type, forceClone) { if ( !array || // let "undefined" and "null" pass (!forceClone && array.constructor === type) ) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array } function isTypedArray(object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); } // returns an array by which times and values can be sorted function getKeyframeOrder(times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; } // uses the array previously returned by "getKeyframeOrder" to sort data function sortedArray(values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; } // function for parsing AOS keyframe formats function flattenJSON(jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } } function subclip(sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = convertArray(times, track.times.constructor); track.values = convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; } function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30, ) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return ( track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType ); }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = referenceTrack.values.slice(startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = referenceTrack.values.slice(startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = interpolant.resultBuffer.slice(startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion() .fromArray(referenceValue) .normalize() .conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat( targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart, ); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } const AnimationUtils = { convertArray: convertArray, isTypedArray: isTypedArray, getKeyframeOrder: getKeyframeOrder, sortedArray: sortedArray, flattenJSON: flattenJSON, subclip: subclip, makeClipAdditive: makeClipAdditive, }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2; ; ) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2; ; ) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.copySampleValue_(0); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = (i1 + right) >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.copySampleValue_(0); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_(/* i1, t0, t, t1 */) { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_(/* i1, t0, t1 */) { // empty } } /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding, }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name, ); this.name = name; this.times = convertArray(times, this.TimeBufferType); this.values = convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { name: track.name, times: convertArray(track.times, Array), values: convertArray(track.values, Array), }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result, ); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result, ); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result, ); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = times.slice(from, to); this.values = this.values.slice(from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime, ); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime, ); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value, ); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = this.times.slice(), values = this.values.slice(), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if ( value !== values[offsetP + j] || value !== values[offsetN + j] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j ) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = times.slice(0, writeIndex); this.values = values.slice(0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = this.times.slice(); const values = this.values.slice(); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack { // No interpolation parameter because only InterpolateDiscrete is valid. constructor(name, times, values) { super(name, times, values); } } BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha, ); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result, ); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited // DefaultInterpolation is inherited; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack { // No interpolation parameter because only InterpolateDiscrete is valid. constructor(name, times, values) { super(name, times, values); } } StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; class AnimationClip { constructor( name = "", duration = -1, tracks = [], blendMode = NormalAnimationBlendMode, ) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { name: clip.name, duration: clip.duration, tracks: tracks, uuid: clip.uuid, blendMode: clip.blendMode, }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push( (i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets, ); values.push(0, 1, 0); const order = getKeyframeOrder(times); times = sortedArray(times, 1, order); values = sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values, ).scale(1.0 / fps), ); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = (o.geometry && o.geometry.animations) || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[name], fps, noLoop, ), ); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks, ) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values, ), ); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks, ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks, ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks, ); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor( this.name, this.duration, tracks, this.blendMode, ); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation, ); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; }, }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load(/* url, onLoad, onProgress, onError */) {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse(/* data */) {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } Loader.DEFAULT_MATERIAL_NAME = "__DEFAULT"; const loading = {}; class HttpError extends Error { constructor(message, response) { super(message); this.response = response; } } class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError, }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError, }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin", // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req) .then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if ( typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined ) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get("X-File-Size") || response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then( ({done, value}) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total, }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }, e => { controller.error(e); }, ); } }, }); return new Response(stream); } else { throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response, ); } }) .then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }) .then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }) .catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }) .finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load( url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps, }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError, ); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = {mipmaps: []}; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push( texDatas.mipmaps[f * texDatas.mipmapCount + i], ); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError, ); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.slice(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); texture.colorSpace = SRGBColorSpace; const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load( urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError, ); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (buffer) { let texData; try { texData = scope.parse(buffer); } catch (error) { if (onError !== undefined) { onError(error); } else { console.error(error); return; } } if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.colorSpace !== undefined) { texture.colorSpace = texData.colorSpace; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError, ); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load( url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError, ); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.isLight = true; this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() { // Empty here in base class; some subclasses override. } copy(source, recursive) { super.copy(source, recursive); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.isHemisphereLight = true; this.type = "HemisphereLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source, recursive) { super.copy(source, recursive); this.groundColor.copy(source.groundColor); return this; } } const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); const _lookTarget$1 = /*@__PURE__*/ new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse, ); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, ); shadowMatrix.multiply(_projScreenMatrix$1); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.isSpotLightShadow = true; this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } class SpotLight extends Light { constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2, ) { super(color, intensity); this.isSpotLight = true; this.type = "SpotLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; this.map = null; this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld = /*@__PURE__*/ new Vector3(); const _lookTarget = /*@__PURE__*/ new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this.isPointLightShadow = true; this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [ // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1), ]; this._cubeDirections = [ new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0), ]; this._cubeUps = [ new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), ]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation( -_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z, ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse, ); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 2) { super(color, intensity); this.isPointLight = true; this.type = "PointLight"; this.distance = distance; this.decay = decay; this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); this.isDirectionalLightShadow = true; } } class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.isDirectionalLight = true; this.type = "DirectionalLight"; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.isAmbientLight = true; this.type = "AmbientLight"; } } class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.isRectAreaLight = true; this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.isSphericalHarmonics3 = true; this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.isLightProbe = true; this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = MaterialLoader.createMaterialFromType(json.type); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.dispersion !== undefined) material.dispersion = json.dispersion; if (json.iridescence !== undefined) material.iridescence = json.iridescence; if (json.iridescenceIOR !== undefined) material.iridescenceIOR = json.iridescenceIOR; if (json.iridescenceThicknessRange !== undefined) material.iridescenceThicknessRange = json.iridescenceThicknessRange; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex(json.attenuationColor); if (json.anisotropy !== undefined) material.anisotropy = json.anisotropy; if (json.anisotropyRotation !== undefined) material.anisotropyRotation = json.anisotropyRotation; if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.alphaHash !== undefined) material.alphaHash = json.alphaHash; if (json.depthFunc !== undefined) material.depthFunc = json.depthFunc; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.blendSrc !== undefined) material.blendSrc = json.blendSrc; if (json.blendDst !== undefined) material.blendDst = json.blendDst; if (json.blendEquation !== undefined) material.blendEquation = json.blendEquation; if (json.blendSrcAlpha !== undefined) material.blendSrcAlpha = json.blendSrcAlpha; if (json.blendDstAlpha !== undefined) material.blendDstAlpha = json.blendDstAlpha; if (json.blendEquationAlpha !== undefined) material.blendEquationAlpha = json.blendEquationAlpha; if (json.blendColor !== undefined && material.blendColor !== undefined) material.blendColor.setHex(json.blendColor); if (json.blendAlpha !== undefined) material.blendAlpha = json.blendAlpha; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== undefined) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.forceSinglePass !== undefined) material.forceSinglePass = json.forceSinglePass; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray( uniform.value, ); break; case "v3": material.uniforms[name].value = new Vector3().fromArray( uniform.value, ); break; case "v4": material.uniforms[name].value = new Vector4().fromArray( uniform.value, ); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray( uniform.value, ); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray( uniform.value, ); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.glslVersion !== undefined) material.glslVersion = json.glslVersion; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } if (json.lights !== undefined) material.lights = json.lights; if (json.clipping !== undefined) material.clipping = json.clipping; // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapRotation !== undefined) material.envMapRotation.fromArray(json.envMapRotation); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale, ); if (json.iridescenceMap !== undefined) material.iridescenceMap = getTexture(json.iridescenceMap); if (json.iridescenceThicknessMap !== undefined) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap, ); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.anisotropyMap !== undefined) material.anisotropyMap = getTexture(json.anisotropyMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } static createMaterialFromType(type) { const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material, }; return new materialLib[type](); } } class LoaderUtils { static decodeText(array) { // @deprecated, r165 console.warn( "THREE.LoaderUtils: decodeText() has been deprecated with r165 and will be removed with r175. Use TextDecoder instead.", ); if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.slice(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.isInstancedBufferGeometry = true; this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } toJSON() { const data = super.toJSON(); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load( url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError, ); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data, ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized, ); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized, ); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data, ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized, ); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized, ); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message, ); return; } const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { if (onError !== undefined) onError(new Error("THREE.ObjectLoader: Can"t load " + url)); console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError, ); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject( json.object, geometries, materials, textures, animations, ); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid].data instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject( json.object, geometries, materials, textures, animations, ); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn( `THREE.ObjectLoader: Unsupported geometry type "${data.type}"`, ); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load( url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }, ); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height, }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height, ), ); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height, }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height, ), ); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = await deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value, ); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid, ); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } const source = images[data.image]; const image = source.data; let texture; if (Array.isArray(image)) { texture = new CubeTexture(); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(); } else { texture = new Texture(); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.channel !== undefined) texture.channel = data.channel; if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.internalFormat !== undefined) texture.internalFormat = data.internalFormat; if (data.type !== undefined) texture.type = data.type; if (data.colorSpace !== undefined) texture.colorSpace = data.colorSpace; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.generateMipmaps !== undefined) texture.generateMipmaps = data.generateMipmaps; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.compareFunction !== undefined) texture.compareFunction = data.compareFunction; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } if (data.fog.name !== "") { object.fog.name = data.fog.name; } } if (data.backgroundBlurriness !== undefined) object.backgroundBlurriness = data.backgroundBlurriness; if (data.backgroundIntensity !== undefined) object.backgroundIntensity = data.backgroundIntensity; if (data.backgroundRotation !== undefined) object.backgroundRotation.fromArray(data.backgroundRotation); if (data.environmentIntensity !== undefined) object.environmentIntensity = data.environmentIntensity; if (data.environmentRotation !== undefined) object.environmentRotation.fromArray(data.environmentRotation); break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far, ); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far, ); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay, ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height, ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay, ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity, ); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute( new Float32Array(instanceMatrix.array), 16, ); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute( new Float32Array(instanceColor.array), instanceColor.itemSize, ); break; case "BatchedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new BatchedMesh( data.maxGeometryCount, data.maxVertexCount, data.maxIndexCount, material, ); object.geometry = geometry; object.perObjectFrustumCulled = data.perObjectFrustumCulled; object.sortObjects = data.sortObjects; object._drawRanges = data.drawRanges; object._reservedRanges = data.reservedRanges; object._visibility = data.visibility; object._active = data.active; object._bounds = data.bounds.map(bound => { const box = new Box3(); box.min.fromArray(bound.boxMin); box.max.fromArray(bound.boxMax); const sphere = new Sphere(); sphere.radius = bound.sphereRadius; sphere.center.fromArray(bound.sphereCenter); return { boxInitialized: bound.boxInitialized, box: box, sphereInitialized: bound.sphereInitialized, sphere: sphere, }; }); object._maxGeometryCount = data.maxGeometryCount; object._maxVertexCount = data.maxVertexCount; object._maxIndexCount = data.maxIndexCount; object._geometryInitialized = data.geometryInitialized; object._geometryCount = data.geometryCount; object._matricesTexture = getTexture(data.matricesTexture.uuid); if (data.colorsTexture !== undefined) object._colorsTexture = getTexture(data.colorsTexture.uuid); break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry(data.geometry), getMaterial(data.material), ); break; case "LineLoop": object = new LineLoop( getGeometry(data.geometry), getMaterial(data.material), ); break; case "LineSegments": object = new LineSegments( getGeometry(data.geometry), getMaterial(data.material), ); break; case "PointCloud": case "Points": object = new Points( getGeometry(data.geometry), getMaterial(data.material), ); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose( object.position, object.quaternion, object.scale, ); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.up !== undefined) object.up.fromArray(data.up); if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add( this.parseObject( children[i], geometries, materials, textures, animations, ), ); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance, level.hysteresis); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn( "THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton, ); } else { child.bind(skeleton, child.bindMatrix); } } }); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping, }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter, }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); this.isImageBitmapLoader = true; if (typeof createImageBitmap === "undefined") { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported.", ); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = {premultiplyAlpha: "none"}; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); // If cached is a promise, wait for it to resolve if (cached.then) { cached .then(imageBitmap => { if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }) .catch(e => { if (onError) onError(e); }); return; } // If cached is not a promise (i.e., it"s already an imageBitmap) setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; const promise = fetch(url, fetchOptions) .then(function (res) { return res.blob(); }) .then(function (blob) { return createImageBitmap( blob, Object.assign(scope.options, {colorSpaceConversion: "none"}), ); }) .then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); return imageBitmap; }) .catch(function (e) { if (onError) onError(e); Cache.remove(url); scope.manager.itemError(url); scope.manager.itemEnd(url); }); Cache.add(url, promise); scope.manager.itemStart(url); } } let _context; class AudioContext { static getContext() { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; } static setContext(value) { _context = value; } } class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load( url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context .decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }) .catch(handleError); } catch (e) { handleError(e); } }, onProgress, onError, ); function handleError(e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } } } const _eyeRight = /*@__PURE__*/ new Matrix4(); const _eyeLeft = /*@__PURE__*/ new Matrix4(); const _projectionMatrix = /*@__PURE__*/ new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null, }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = (eyeSepHalf * cache.near) / cache.focus; const ymax = (cache.near * Math.tan(DEG2RAD * cache.fov * 0.5)) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = (2 * cache.near) / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = (2 * cache.near) / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/ new Vector3(); const _quaternion$1 = /*@__PURE__*/ new Quaternion(); const _scale$1 = /*@__PURE__*/ new Vector3(); const _orientation$1 = /*@__PURE__*/ new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z, ); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; if (this.source !== null) { this.source.stop(); this.source.onended = null; } this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this._connected === false) { return; } if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.isPlaying === true && this.source.detune !== undefined) { this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01, ); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01, ); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/ new Vector3(); const _quaternion = /*@__PURE__*/ new Quaternion(); const _scale = /*@__PURE__*/ new Vector3(); const _orientation = /*@__PURE__*/ new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } connect() { super.connect(); this.panner.connect(this.gain); } disconnect() { super.disconnect(); this.panner.disconnect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride, ); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride, ); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + (i % stride)]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t, ); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset, ); // Slerp to the intermediate result Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t, ); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /*@__PURE__*/ /((?:WC+[/:])*)/.source.replace( "WC", _wordChar, ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /*@__PURE__*/ /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", _wordChar, ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /*@__PURE__*/ /.(WC+)(?:[(.+)])?/.source.replace( "WC", _wordChar, ); const _trackRe = new RegExp( "" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$", ); const _supportedObjectNames = ["material", "materials", "bones", "map"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i ) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName); this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (matches === null) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6], }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName, ); } return results; } static findNode(root, nodeName) { if ( nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName, ); this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.warn( "THREE.PropertyBinding: No target node found for track: " + this.path + ".", ); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this, ); return; } if (!targetObject.material.materials) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this, ); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this, ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; case "map": if ("map" in targetObject) { targetObject = targetObject.map; break; } if (!targetObject.material) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this, ); return; } if (!targetObject.material.map) { console.error( "THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.", this, ); return; } targetObject = targetObject.material.map; break; default: if (targetObject[objectName] === undefined) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this, ); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject, ); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject, ); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this, ); return; } if (!targetObject.geometry.morphAttributes) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this, ); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3, }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2, }; PropertyBinding.prototype.GetterByBindingType = [ PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray, ]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ [ // Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, ], [ // EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, ], [ // ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, ], [ // HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, ], ]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.isAnimationObjectGroup = true; this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; }, }, get bindingsPerObject() { return scope._bindings.length; }, }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push( new PropertyBinding(object, paths[j], parsedPaths[j]), ); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes.", ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding, }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return ( this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this) ); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { deltaTime = 0; } else { this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true; else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1, }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1, }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta, }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } const _controlInterpolantsResultBuffer = new Float32Array(1); class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize(), ); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[0], ); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; }, }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; }, }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; }, }, }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {}, }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant( new Float32Array(2), new Float32Array(2), 1, _controlInterpolantsResultBuffer, ); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode, ); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = (this.time += deltaTime), timeDirection = Math.sign(deltaTime), accuIndex = (this._accuIndex ^= 1); // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } class Uniform { constructor(value) { this.value = value; } clone() { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone(), ); } } let _id = 0; class UniformsGroup extends EventDispatcher { constructor() { super(); this.isUniformsGroup = true; Object.defineProperty(this, "id", {value: _id++}); this.name = ""; this.usage = StaticDrawUsage; this.uniforms = []; } add(uniform) { this.uniforms.push(uniform); return this; } remove(uniform) { const index = this.uniforms.indexOf(uniform); if (index !== -1) this.uniforms.splice(index, 1); return this; } setName(name) { this.name = name; return this; } setUsage(value) { this.usage = value; return this; } dispose() { this.dispatchEvent({type: "dispose"}); return this; } copy(source) { this.name = source.name; this.usage = source.usage; const uniformsSource = source.uniforms; this.uniforms.length = 0; for (let i = 0, l = uniformsSource.length; i < l; i++) { const uniforms = Array.isArray(uniformsSource[i]) ? uniformsSource[i] : [uniformsSource[i]]; for (let j = 0; j < uniforms.length; j++) { this.uniforms.push(uniforms[j].clone()); } } return this; } clone() { return new this.constructor().copy(this); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.isInstancedInterleavedBuffer = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.isGLBufferAttribute = true; this.name = ""; this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } const _matrix = /*@__PURE__*/ new Matrix4(); class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: {threshold: 1}, LOD: {}, Points: {threshold: 1}, Sprite: {}, }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction .set(coords.x, coords.y, 0.5) .unproject(camera) .sub(this.ray.origin) .normalize(); this.camera = camera; } else if (camera.isOrthographicCamera) { this.ray.origin .set( coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far), ) .unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } setFromXRController(controller) { _matrix.identity().extractRotation(controller.matrixWorld); this.ray.origin.setFromMatrixPosition(controller.matrixWorld); this.ray.direction.set(0, 0, -1).applyMatrix4(_matrix); return this; } intersectObject(object, recursive = true, intersects = []) { intersect(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersect(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersect(object, raycaster, intersects, recursive) { let propagate = true; if (object.layers.test(raycaster.layers)) { const result = object.raycast(raycaster, intersects); if (result === false) propagate = false; } if (propagate === true && recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersect(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * phi (the polar angle) is measured from the positive y-axis. The positive y-axis is up. * theta (the azimuthal angle) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be between EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/ new Vector2(); class Box2 { constructor( min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity), ) { this.isBox2 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return ( this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y ); } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( (point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), ); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$4).distanceTo(point); } intersect(box) { this.min.max(box.min); this.max.min(box.max); if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _startP = /*@__PURE__*/ new Vector3(); const _startEnd = /*@__PURE__*/ new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/ new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.matrixAutoUpdate = false; this.color = color; this.type = "SpotLightHelper"; const geometry = new BufferGeometry(); const positions = [ 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, ]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = (i / l) * Math.PI * 2; const p2 = (j / l) * Math.PI * 2; positions.push( Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1, ); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({fog: false, toneMapped: false}); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); // update the local matrix based on the parent and light target transforms if (this.parent) { this.parent.updateWorldMatrix(true); this.matrix .copy(this.parent.matrixWorld) .invert() .multiply(this.light.matrixWorld); } else { this.matrix.copy(this.light.matrixWorld); } this.matrixWorld.copy(this.light.matrixWorld); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/ new Vector3(); const _boneMatrix = /*@__PURE__*/ new Matrix4(); const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true, }); super(geometry, material); this.isSkeletonHelper = true; this.type = "SkeletonHelper"; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } function getBoneList(object) { const boneList = []; if (object.isBone === true) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false, }); super(geometry, material); this.light = light; this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/ new Vector3(); const _color1 = /*@__PURE__*/ new Color(); const _color2 = /*@__PURE__*/ new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "HemisphereLightHelper"; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false, }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } this.light.updateWorldMatrix(true, false); mesh.lookAt( _vector$1.setFromMatrixPosition(this.light.matrixWorld).negate(), ); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "GridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PolarGridHelper extends LineSegments { constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888, ) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the sectors if (sectors > 1) { for (let i = 0; i < sectors; i++) { const v = (i / sectors) * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } } // create the rings for (let i = 0; i < rings; i++) { const color = i & 1 ? color1 : color2; const r = radius - (radius / rings) * i; for (let j = 0; j < divisions; j++) { // first vertex let v = (j / divisions) * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = ((j + 1) / divisions) * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "PolarGridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } const _v1 = /*@__PURE__*/ new Vector3(); const _v2 = /*@__PURE__*/ new Vector3(); const _v3 = /*@__PURE__*/ new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "DirectionalLightHelper"; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ -size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0, ], 3, ), ); const material = new LineBasicMaterial({fog: false, toneMapped: false}); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3), ); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/ new Vector3(); const _camera = /*@__PURE__*/ new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false, }); const vertices = []; const colors = []; const pointMap = {}; // near addLine("n1", "n2"); addLine("n2", "n4"); addLine("n4", "n3"); addLine("n3", "n1"); // far addLine("f1", "f2"); addLine("f2", "f4"); addLine("f4", "f3"); addLine("f3", "f1"); // sides addLine("n1", "f1"); addLine("n2", "f2"); addLine("n3", "f3"); addLine("n4", "f4"); // cone addLine("p", "n1"); addLine("p", "n2"); addLine("p", "n3"); addLine("p", "n4"); // up addLine("u1", "u2"); addLine("u2", "u3"); addLine("u3", "u1"); // target addLine("c", "t"); addLine("p", "c"); // cross addLine("cn1", "cn2"); addLine("cn3", "cn4"); addLine("cf1", "cf2"); addLine("cf3", "cf4"); function addLine(a, b) { addPoint(a); addPoint(b); } function addPoint(id) { vertices.push(0, 0, 0); colors.push(0, 0, 0); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); this.setColors(colorFrustum, colorCone, colorUp, colorTarget, colorCross); } setColors(frustum, cone, up, target, cross) { const geometry = this.geometry; const colorAttribute = geometry.getAttribute("color"); // near colorAttribute.setXYZ(0, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(1, frustum.r, frustum.g, frustum.b); // n1, n2 colorAttribute.setXYZ(2, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(3, frustum.r, frustum.g, frustum.b); // n2, n4 colorAttribute.setXYZ(4, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(5, frustum.r, frustum.g, frustum.b); // n4, n3 colorAttribute.setXYZ(6, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(7, frustum.r, frustum.g, frustum.b); // n3, n1 // far colorAttribute.setXYZ(8, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(9, frustum.r, frustum.g, frustum.b); // f1, f2 colorAttribute.setXYZ(10, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(11, frustum.r, frustum.g, frustum.b); // f2, f4 colorAttribute.setXYZ(12, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(13, frustum.r, frustum.g, frustum.b); // f4, f3 colorAttribute.setXYZ(14, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(15, frustum.r, frustum.g, frustum.b); // f3, f1 // sides colorAttribute.setXYZ(16, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(17, frustum.r, frustum.g, frustum.b); // n1, f1 colorAttribute.setXYZ(18, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(19, frustum.r, frustum.g, frustum.b); // n2, f2 colorAttribute.setXYZ(20, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(21, frustum.r, frustum.g, frustum.b); // n3, f3 colorAttribute.setXYZ(22, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(23, frustum.r, frustum.g, frustum.b); // n4, f4 // cone colorAttribute.setXYZ(24, cone.r, cone.g, cone.b); colorAttribute.setXYZ(25, cone.r, cone.g, cone.b); // p, n1 colorAttribute.setXYZ(26, cone.r, cone.g, cone.b); colorAttribute.setXYZ(27, cone.r, cone.g, cone.b); // p, n2 colorAttribute.setXYZ(28, cone.r, cone.g, cone.b); colorAttribute.setXYZ(29, cone.r, cone.g, cone.b); // p, n3 colorAttribute.setXYZ(30, cone.r, cone.g, cone.b); colorAttribute.setXYZ(31, cone.r, cone.g, cone.b); // p, n4 // up colorAttribute.setXYZ(32, up.r, up.g, up.b); colorAttribute.setXYZ(33, up.r, up.g, up.b); // u1, u2 colorAttribute.setXYZ(34, up.r, up.g, up.b); colorAttribute.setXYZ(35, up.r, up.g, up.b); // u2, u3 colorAttribute.setXYZ(36, up.r, up.g, up.b); colorAttribute.setXYZ(37, up.r, up.g, up.b); // u3, u1 // target colorAttribute.setXYZ(38, target.r, target.g, target.b); colorAttribute.setXYZ(39, target.r, target.g, target.b); // c, t colorAttribute.setXYZ(40, cross.r, cross.g, cross.b); colorAttribute.setXYZ(41, cross.r, cross.g, cross.b); // p, c // cross colorAttribute.setXYZ(42, cross.r, cross.g, cross.b); colorAttribute.setXYZ(43, cross.r, cross.g, cross.b); // cn1, cn2 colorAttribute.setXYZ(44, cross.r, cross.g, cross.b); colorAttribute.setXYZ(45, cross.r, cross.g, cross.b); // cn3, cn4 colorAttribute.setXYZ(46, cross.r, cross.g, cross.b); colorAttribute.setXYZ(47, cross.r, cross.g, cross.b); // cf1, cf2 colorAttribute.setXYZ(48, cross.r, cross.g, cross.b); colorAttribute.setXYZ(49, cross.r, cross.g, cross.b); // cf3, cf4 colorAttribute.needsUpdate = true; } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/ new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, ]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source, recursive) { super.copy(source, recursive); this.object = source.object; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, ]); const positions = [ 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, ]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [ 1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0, ]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({color: color, toneMapped: false})); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [ 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, ]; const geometry2 = new BufferGeometry(); geometry2.setAttribute( "position", new Float32BufferAttribute(positions2, 3), ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false, }), ), ); } updateMatrixWorld(force) { this.position.set(0, 0, 0); this.scale.set(0.5 * this.size, 0.5 * this.size, 1); this.lookAt(this.plane.normal); this.translateZ(-this.plane.constant); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); this.children[0].geometry.dispose(); this.children[0].material.dispose(); } } const _axis = /*@__PURE__*/ new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor( dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2, ) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute( "position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3), ); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line( _lineGeometry, new LineBasicMaterial({color: color, toneMapped: false}), ); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial({color: color, toneMapped: false}), ); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } dispose() { this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); this.cone.material.dispose(); } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, ]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false, }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if ( (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x) || (edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) ) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = {s: new Shape(), p: tmpPoints}; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({h: tmpPath, p: tmpPoints[0]}); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; let toChange = 0; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange++; if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } if (toChange > 0 && ambiguous === false) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } class WebGLMultipleRenderTargets extends WebGLRenderTarget { // @deprecated, r162 constructor(width = 1, height = 1, count = 1, options = {}) { console.warn( "THREE.WebGLMultipleRenderTargets has been deprecated and will be removed in r172. Use THREE.WebGLRenderTarget and set the "count" parameter to enable MRT.", ); super(width, height, {...options, count}); this.isWebGLMultipleRenderTargets = true; } get texture() { return this.textures; } } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("register", { detail: { revision: REVISION, }, }), ); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AgXToneMapping = AgXToneMapping; exports.AlphaFormat = AlphaFormat; exports.AlwaysCompare = AlwaysCompare; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AnimationAction = AnimationAction; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.AttachedBindMode = AttachedBindMode; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BatchedMesh = BatchedMesh; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasTexture = CanvasTexture; exports.CapsuleGeometry = CapsuleGeometry; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.ColorManagement = ColorManagement; exports.CompressedArrayTexture = CompressedArrayTexture; exports.CompressedCubeTexture = CompressedCubeTexture; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeGeometry = ConeGeometry; exports.ConstantAlphaFactor = ConstantAlphaFactor; exports.ConstantColorFactor = ConstantColorFactor; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DetachedBindMode = DetachedBindMode; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DisplayP3ColorSpace = DisplayP3ColorSpace; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EllipseCurve = EllipseCurve; exports.EqualCompare = EqualCompare; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FileLoader = FileLoader; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterCompare = GreaterCompare; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualCompare = GreaterEqualCompare; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LessCompare = LessCompare; exports.LessDepth = LessDepth; exports.LessEqualCompare = LessEqualCompare; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LineSegments = LineSegments; exports.LinearDisplayP3ColorSpace = LinearDisplayP3ColorSpace; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearSRGBColorSpace = LinearSRGBColorSpace; exports.LinearToneMapping = LinearToneMapping; exports.LinearTransfer = LinearTransfer; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeutralToneMapping = NeutralToneMapping; exports.NeverCompare = NeverCompare; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColorSpace = NoColorSpace; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualCompare = NotEqualCompare; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusConstantAlphaFactor = OneMinusConstantAlphaFactor; exports.OneMinusConstantColorFactor = OneMinusConstantColorFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.P3Primaries = P3Primaries; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.RED_GREEN_RGTC2_Format = RED_GREEN_RGTC2_Format; exports.RED_RGTC1_Format = RED_RGTC1_Format; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_BPTC_SIGNED_Format = RGB_BPTC_SIGNED_Format; exports.RGB_BPTC_UNSIGNED_Format = RGB_BPTC_UNSIGNED_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.Rec709Primaries = Rec709Primaries; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RenderTarget = RenderTarget; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingGeometry = RingGeometry; exports.SIGNED_RED_GREEN_RGTC2_Format = SIGNED_RED_GREEN_RGTC2_Format; exports.SIGNED_RED_RGTC1_Format = SIGNED_RED_RGTC1_Format; exports.SRGBColorSpace = SRGBColorSpace; exports.SRGBTransfer = SRGBTransfer; exports.Scene = Scene; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.Source = Source; exports.Sphere = Sphere; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronGeometry = TetrahedronGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusGeometry = TorusGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsGroup = UniformsGroup; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedInt5999Type = UnsignedInt5999Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.VideoTexture = VideoTexture; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCoordinateSystem = WebGLCoordinateSystem; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WebGPUCoordinateSystem = WebGPUCoordinateSystem; exports.WireframeGeometry = WireframeGeometry; exports.WrapAroundEnding = WrapAroundEnding; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports.createCanvasElement = createCanvasElement; /* */}),null);
-----
three.r138",[],(function $module_three_r138(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "138"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } const MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD, RAD2DEG, generateUUID, clamp, euclideanModulo, mapLinear, inverseLerp, lerp, damp, pingpong, smoothstep, smootherstep, randInt, randFloat, randFloatSpread, seededRandom, degToRad, radToDeg, isPowerOfTwo, ceilPowerOfTwo, floorPowerOfTwo, setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } class Source { constructor(data = null) { this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.images[this.uuid] !== undefined) { return meta.images[this.uuid]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if (data !== null) { let url; if (Array.isArray(data)) { // cube texture url = []; for (let i = 0, l = data.length; i < l; i++) { if (data[i].isDataTexture) { url.push(serializeImage(data[i].image)); } else { url.push(serializeImage(data[i])); } } } else { // texture url = serializeImage(data); } output.url = url; } if (!isRootObject) { meta.images[this.uuid] = output; } return output; } } function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } Source.prototype.isSource = true; let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.source = new Source(image); this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image(value) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); this.needsUpdate = true; return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { metadata: { version: 4.5, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON(meta).uuid, 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 (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); const image = { width, height, depth: 1 }; this.texture = new Texture(image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class DataArrayTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataArrayTexture.prototype.isDataArrayTexture = true; class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor(width, height, depth) { super(width, height); this.depth = depth; this.texture = new DataArrayTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } WebGLArrayRenderTarget.prototype.isWebGLArrayRenderTarget = true; class Data3DTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } Data3DTexture.prototype.isData3DTexture = true; class WebGL3DRenderTarget extends WebGLRenderTarget { constructor(width, height, depth) { super(width, height); this.depth = depth; this.texture = new Data3DTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } WebGL3DRenderTarget.prototype.isWebGL3DRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count, options = {}) { super(width, height, options); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); this.texture[i].isRenderTargetTexture = true; } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } setFromEuler(e) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); const nodes = extractFromCache(meta.nodes); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; if (nodes.length > 0) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (isRootObject) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation !== undefined && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth !== undefined && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRootObject) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; Material.fromType = function /*type*/ () { // TODO: Behavior added in Materials.js return null; }; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start, count, materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (this.hasAttribute("tangent") === false) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = this.getAttribute("tangent").array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance, point: _intersectionPointWorld.clone(), object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a, b, c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width, height, depth, widthSegments, heightSegments, depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; const default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; const default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options = {}) { super(size, size, options); const image = { width: size, height: size, depth: 1 }; const images = [image, image, image, image, image, image]; this.texture = new CubeTexture(images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop (callback) { animationLoop = callback; }, setContext (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { throw new Error("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } else { throw new Error("THREE.WebGLAttributes: Unsupported buffer data format: " + array); } return { buffer, type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get, remove, update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width, height, widthSegments, heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } const alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; const alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; const alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; const alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; const aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; const aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; const begin_vertex = "vec3 transformed = vec3( position );"; const beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; const bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; const bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; const clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; const clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; const clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; const clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; const color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; const color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; const color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; const color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; const common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; const cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; const defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; const displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; const displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; const emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; const emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; const encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; const encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; const envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; const envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; const envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; const envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; const envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; const fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; const fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; const fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; const fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; const gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; const lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; const lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; const lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; const lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; const envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; const lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; const lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; const lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; const lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; const lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; const lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; const lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; const lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; const lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; const logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; const logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; const logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; const logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; const map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; const map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; const map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; const map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; const metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; const metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; const morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ] #endif } #endif"; const morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; const morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { float texelIndex = float( vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; const morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; const normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; const normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; const normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; const normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; const normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; const normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; const clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; const clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; const clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; const output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; const packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; const premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; const project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; const dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; const dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; const roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; const roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; const shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; const shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; const shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; const shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; const skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; const skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; const skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; const skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; const specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; const specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; const tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; const tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; const transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; const transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef texture2DLodEXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; const uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; const uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; const uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; const uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; const uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; const uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; const worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment, alphamap_pars_fragment, alphatest_fragment, alphatest_pars_fragment, aomap_fragment, aomap_pars_fragment, begin_vertex, beginnormal_vertex, bsdfs, bumpmap_pars_fragment, clipping_planes_fragment, clipping_planes_pars_fragment, clipping_planes_pars_vertex, clipping_planes_vertex, color_fragment, color_pars_fragment, color_pars_vertex, color_vertex, common, cube_uv_reflection_fragment, defaultnormal_vertex, displacementmap_pars_vertex, displacementmap_vertex, emissivemap_fragment, emissivemap_pars_fragment, encodings_fragment, encodings_pars_fragment, envmap_fragment, envmap_common_pars_fragment, envmap_pars_fragment, envmap_pars_vertex, envmap_physical_pars_fragment, envmap_vertex, fog_vertex, fog_pars_vertex, fog_fragment, fog_pars_fragment, gradientmap_pars_fragment, lightmap_fragment, lightmap_pars_fragment, lights_lambert_vertex, lights_pars_begin, lights_toon_fragment, lights_toon_pars_fragment, lights_phong_fragment, lights_phong_pars_fragment, lights_physical_fragment, lights_physical_pars_fragment, lights_fragment_begin, lights_fragment_maps, lights_fragment_end, logdepthbuf_fragment, logdepthbuf_pars_fragment, logdepthbuf_pars_vertex, logdepthbuf_vertex, map_fragment, map_pars_fragment, map_particle_fragment, map_particle_pars_fragment, metalnessmap_fragment, metalnessmap_pars_fragment, morphcolor_vertex, morphnormal_vertex, morphtarget_pars_vertex, morphtarget_vertex, normal_fragment_begin, normal_fragment_maps, normal_pars_fragment, normal_pars_vertex, normal_vertex, normalmap_pars_fragment, clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps, clearcoat_pars_fragment, output_fragment, packing, premultiplied_alpha_fragment, project_vertex, dithering_fragment, dithering_pars_fragment, roughnessmap_fragment, roughnessmap_pars_fragment, shadowmap_pars_fragment, shadowmap_pars_vertex, shadowmap_vertex, shadowmask_pars_fragment, skinbase_vertex, skinning_pars_vertex, skinning_vertex, skinnormal_vertex, specularmap_fragment, specularmap_pars_fragment, tonemapping_fragment, tonemapping_pars_fragment, transmission_fragment, transmission_pars_fragment, uv_pars_fragment, uv_pars_vertex, uv_vertex, uv2_pars_fragment, uv2_pars_vertex, uv2_vertex, worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor () { return clearColor; }, setClearColor (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha () { return clearAlpha; }, setClearAlpha (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes, enabledAttributes, attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup, reset, resetDefaultState, dispose, releaseStatesOfGeometry, releaseStatesOfProgram, initAttributes, enableAttribute, disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2, drawBuffers, getMaxAnisotropy, getMaxPrecision, precision, logarithmicDepthBuffer, maxTextures, maxVertexTextures, maxTextureSize, maxCubemapSize, maxAttributes, maxVertexUniforms, maxVaryings, maxFragmentUniforms, vertexTextures, floatFragmentTextures, floatVertexTextures, maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get, dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); this._setSize(256); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial(this._cubemapMaterial); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial(this._equirectMaterial); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if (this._cubemapMaterial !== null) this._cubemapMaterial.dispose(); if (this._equirectMaterial !== null) this._equirectMaterial.dispose(); } // private interface _setSize(cubeSize) { this._lodMax = Math.floor(Math.log2(cubeSize)); this._cubeSize = Math.pow(2, this._lodMax); } _dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); for (let i = 0; i < this._lodPlanes.length; i++) { this._lodPlanes[i].dispose(); } } _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { if (texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping) { this._setSize(texture.image.length === 0 ? 16 : texture.image[0].width || texture.image[0].image.width); } else { // Equirectangular this._setSize(texture.image.width / 4); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max(this._cubeSize, 16 * 7); const height = 4 * this._cubeSize - 32; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(width, height, params); if (this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width) { if (this._pingPongRenderTarget !== null) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget(width, height, params); const { _lodMax } = this; ({ sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes(_lodMax)); this._blurMaterial = _getBlurShader(_lodMax, width, height); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(this._lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } const size = this._cubeSize; _setViewport(cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh(this._lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; const size = this._cubeSize; _setViewport(cubeUVRenderTarget, 0, 0, 3 * size, 2 * size); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < this._lodPlanes.length; i++) { const sigma = Math.sqrt(this._sigmas[i] * this._sigmas[i] - this._sigmas[i - 1] * this._sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(this._lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } const { _lodMax } = this; blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = _lodMax - lodIn; const outputSize = this._sizeLods[lodOut]; const x = 3 * outputSize * (lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0); const y = 4 * (this._cubeSize - outputSize); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes(lodMax) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for (let i = 0; i < totalLods; i++) { const sizeLod = Math.pow(2, lod); sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > lodMax - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - lodMax + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget(width, height, params) { const cubeUVRenderTarget = new WebGLRenderTarget(width, height, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(lodMax, width, height) { const weights = new Float32Array(MAX_SAMPLES); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new ShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0` }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCubemapMaterial() { return new ShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get, dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has (name) { return getExtension(name) !== null; }, init (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get, update, getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory, render, programs: null, autoReset: true, reset, update }; } function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== morphTargetsCount) { if (entry !== undefined) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if (hasMorphPosition === true) vertexDataCount = 1; if (hasMorphNormals === true) vertexDataCount = 2; if (hasMorphColors === true) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * morphTargetsCount); const texture = new DataArrayTexture(buffer, width, height, morphTargetsCount); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for (let i = 0; i < morphTargetsCount; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const morphColor = morphColors[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { const stride = j * vertexDataStride; if (hasMorphPosition === true) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; } if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } if (hasMorphColors === true) { morph.fromBufferAttribute(morphColor, j); if (morphColor.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 8] = morph.x; buffer[offset + stride + 9] = morph.y; buffer[offset + stride + 10] = morph.z; buffer[offset + stride + 11] = morphColor.itemSize === 4 ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update, dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyArrayTexture = new DataArrayTexture(); const empty3dTexture = new Data3DTexture(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || empty3dTexture, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyArrayTexture, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || empty3dTexture, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || !!parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphColors && parameters.isWebGL2 ? "#define USE_MORPHCOLORS" : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable, programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const envMapCubeUVHeight = !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping) ? envMap.image.height : null; const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let morphTextureStride = 0; if (geometry.morphAttributes.position !== undefined) morphTextureStride = 1; if (geometry.morphAttributes.normal !== undefined) morphTextureStride = 2; if (geometry.morphAttributes.color !== undefined) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2, shaderID, shaderName: material.type, vertexShader, fragmentShader, defines: material.defines, customVertexShaderID, customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUVHeight, lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones, useVertexTexture: floatVertexTextures, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount, morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.envMapCubeUVHeight); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.morphAttributeCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.lightMap) _programLayers.enable(7); if (parameters.aoMap) _programLayers.enable(8); if (parameters.emissiveMap) _programLayers.enable(9); if (parameters.bumpMap) _programLayers.enable(10); if (parameters.normalMap) _programLayers.enable(11); if (parameters.objectSpaceNormalMap) _programLayers.enable(12); if (parameters.tangentSpaceNormalMap) _programLayers.enable(13); if (parameters.clearcoat) _programLayers.enable(14); if (parameters.clearcoatMap) _programLayers.enable(15); if (parameters.clearcoatRoughnessMap) _programLayers.enable(16); if (parameters.clearcoatNormalMap) _programLayers.enable(17); if (parameters.displacementMap) _programLayers.enable(18); if (parameters.specularMap) _programLayers.enable(19); if (parameters.roughnessMap) _programLayers.enable(20); if (parameters.metalnessMap) _programLayers.enable(21); if (parameters.gradientMap) _programLayers.enable(22); if (parameters.alphaMap) _programLayers.enable(23); if (parameters.alphaTest) _programLayers.enable(24); if (parameters.vertexColors) _programLayers.enable(25); if (parameters.vertexAlphas) _programLayers.enable(26); if (parameters.vertexUvs) _programLayers.enable(27); if (parameters.vertexTangents) _programLayers.enable(28); if (parameters.uvsVertexOnly) _programLayers.enable(29); if (parameters.fog) _programLayers.enable(30); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.morphColors) _programLayers.enable(7); if (parameters.premultipliedAlpha) _programLayers.enable(8); if (parameters.shadowMapEnabled) _programLayers.enable(9); if (parameters.physicallyCorrectLights) _programLayers.enable(10); if (parameters.doubleSided) _programLayers.enable(11); if (parameters.flipSided) _programLayers.enable(12); if (parameters.depthPacking) _programLayers.enable(13); if (parameters.dithering) _programLayers.enable(14); if (parameters.specularIntensityMap) _programLayers.enable(15); if (parameters.specularColorMap) _programLayers.enable(16); if (parameters.transmission) _programLayers.enable(17); if (parameters.transmissionMap) _programLayers.enable(18); if (parameters.thicknessMap) _programLayers.enable(19); if (parameters.sheen) _programLayers.enable(20); if (parameters.sheenColorMap) _programLayers.enable(21); if (parameters.sheenRoughnessMap) _programLayers.enable(22); if (parameters.decodeVideoTexture) _programLayers.enable(23); if (parameters.opaque) _programLayers.enable(24); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters, getProgramCacheKey, getUniforms, acquireProgram, releaseProgram, releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs, dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get, remove, update, dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object, geometry, material, groupOrder, renderOrder: object.renderOrder, z, group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque, transmissive, transparent, init, push, unshift, finish, sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get, dispose }; } function UniformsCache() { const lights = {}; return { get (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup, setupView, state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray, shadowsArray, lights }; return { init, state, setupLights, setupLightsView, pushLight, pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get, dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked (lock) { locked = lock; }, setClear (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked (lock) { locked = lock; }, setClear (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked (lock) { locked = lock; }, setClear (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable, disable, bindFramebuffer, drawBuffers, useProgram, setBlending, setMaterial, setFlipSided, setCullFace, setLineWidth, setPolygonOffset, setScissorTest, activeTexture, bindTexture, unbindTexture, compressedTexImage2D, texImage2D, texImage3D, texStorage2D, texStorage3D, texSubImage2D, texSubImage3D, compressedTexSubImage2D, scissor, viewport, reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has("WEBGL_multisampled_render_to_texture") ? extensions.get("WEBGL_multisampled_render_to_texture") : null; const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get(source); if (webglTextures) { const webglTexture = webglTextures[textureProperties.__cacheKey]; webglTexture.usedTimes--; // the WebGLTexture object is not used anymore, remove it if (webglTexture.usedTimes === 0) { deleteTexture(texture); } // remove the weak map entry if no WebGLTexture uses the source anymore if (Object.keys(webglTextures).length === 0) { _sources.delete(source); } } properties.remove(texture); } function deleteTexture(texture) { const textureProperties = properties.get(texture); _gl.deleteTexture(textureProperties.__webglTexture); const source = texture.source; const webglTextures = _sources.get(source); delete webglTextures[textureProperties.__cacheKey]; info.memory.textures--; } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } function getTextureCacheKey(texture) { const array = []; array.push(texture.wrapS); array.push(texture.wrapT); array.push(texture.magFilter); array.push(texture.minFilter); array.push(texture.anisotropy); array.push(texture.internalFormat); array.push(texture.format); array.push(texture.type); array.push(texture.generateMipmaps); array.push(texture.premultiplyAlpha); array.push(texture.flipY); array.push(texture.unpackAlignment); array.push(texture.encoding); return array.join(); } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === null) { console.warn("THREE.WebGLRenderer: Texture marked for update but no image data found."); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { let forceUpload = false; if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get(source); if (webglTextures === undefined) { webglTextures = {}; _sources.set(source, webglTextures); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey(texture); if (textureCacheKey !== textureProperties.__cacheKey) { // if not, create a new instance of WebGLTexture if (webglTextures[textureCacheKey] === undefined) { // create new entry webglTextures[textureCacheKey] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[textureCacheKey].usedTimes++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[textureProperties.__cacheKey]; if (webglTexture !== undefined) { webglTextures[textureProperties.__cacheKey].usedTimes--; if (webglTexture.usedTimes === 0) { deleteTexture(texture); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[textureCacheKey].texture; } return forceUpload; } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); if (source.version !== source.__currentVersion || forceUpload === true) { _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataArrayTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isData3DTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } source.__currentVersion = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); if (source.version !== source.__currentVersion || forceUpload === true) { _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture; const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } source.__currentVersion = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || useMultisampledRTT(renderTarget)) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget) { if (isWebGL2) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error("THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } const renderTargetProperties = properties.get(renderTarget); const ignoreDepthValues = renderTargetProperties.__ignoreDepthValues !== undefined ? renderTargetProperties.__ignoreDepthValues : false; if (ignoreDepthValues === false) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (ignoreDepthValues === true) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } } function getRenderTargetSamples(renderTarget) { return Math.min(maxSamples, renderTarget.samples); } function useMultisampledRTT(renderTarget) { const renderTargetProperties = properties.get(renderTarget); return isWebGL2 && renderTarget.samples > 0 && extensions.has("WEBGL_multisampled_render_to_texture") === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width, height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, encoding: renderer.outputEncoding, samples: attributes.antialias ? 4 : 0 }); const renderTargetProperties = renderer.properties.get(newRenderTarget); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms, refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let _alpha; if (parameters.context !== undefined) { _alpha = _context.getContextAttributes().alpha; } else { _alpha = parameters.alpha !== undefined ? parameters.alpha : false; } let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color = true, depth = true, stencil = true) { let bits = 0; if (color) bits |= _gl.COLOR_BUFFER_BIT; if (depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); } function renderTransmissionPass(opaqueObjects, scene, camera) { const isWebGL2 = capabilities.isWebGL2; if (_transmissionRenderTarget === null) { _transmissionRenderTarget = new WebGLRenderTarget(1, 1, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: isWebGL2 && _antialias === true ? 4 : 0 }); } _this.getDrawingBufferSize(_vector2); if (isWebGL2) { _transmissionRenderTarget.setSize(_vector2.x, _vector2.y); } else { _transmissionRenderTarget.setSize(floorPowerOfTwo(_vector2.x), floorPowerOfTwo(_vector2.y)); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphColors = !!geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.morphColors !== morphColors) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } const morphAttributes = geometry.morphAttributes; if (morphAttributes.position !== undefined || morphAttributes.normal !== undefined || morphAttributes.color !== undefined && capabilities.isWebGL2 === true) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (extensions.has("WEBGL_multisampled_render_to_texture") === true) { console.warn("THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided"); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isData3DTexture || texture.isDataArrayTexture) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (capabilities.isWebGL2 && renderTarget.samples > 0 && textures.useMultisampledRTT(renderTarget) === false) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isData3DTexture) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataArrayTexture) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isData3DTexture) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance, object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data, width, height }; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index, face: null, object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width, height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius, segments, thetaStart, thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices, indices, radius, detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry, thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents, normals, binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; const Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve, CatmullRomCurve3, CubicBezierCurve, CubicBezierCurve3, EllipseCurve, LineCurve, LineCurve3, QuadraticBezierCurve, QuadraticBezierCurve3, SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes, options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points, segments, phiStart, phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes, curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius, detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius, tube, radialSegments, tubularSegments, arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius, tube, tubularSegments, radialSegments, p, q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path, tubularSegments, radius, radialSegments, closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } const Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry, ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material }; Material.fromType = function (type) { return new materialLib[type](); }; const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove (key) { delete this.files[key]; }, clear () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad, onProgress, onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad, onProgress, onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if (typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.slice(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = Material.fromType(json.type); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.slice(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = await deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } const source = images[data.image]; const image = source.data; let texture; if (Array.isArray(image)) { texture = new CubeTexture(); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(); } else { texture = new Texture(); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping, CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeUVReflectionMapping, CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (matches === null) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object.isBone === true) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; let toChange = 0; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange++; if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } if (toChange > 0 && ambiguous === false) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Euler.prototype.toVector3 = function () { console.error("THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead"); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, useQuaternion: { get () { console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default."); }, set () { console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, set () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.prototype, { wrapAround: { get () { console.warn("THREE.Material: .wrapAround has been removed."); }, set () { console.warn("THREE.Material: .wrapAround has been removed."); } }, overdraw: { get () { console.warn("THREE.Material: .overdraw has been removed."); }, set () { console.warn("THREE.Material: .overdraw has been removed."); } }, wrapRGB: { get () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get () { return this.shadowMap.enabled; }, set (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get () { return this.shadowMap.type; }, set (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set /* cullFace */ () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); } }, renderReverseSided: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead."); return undefined; }, set () { console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead."); } }, renderSingleSided: { get () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); return undefined; }, set () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } function WebGLMultisampleRenderTarget(width, height, options) { console.error("THREE.WebGLMultisampleRenderTarget has been removed. Use a normal render target and set the "samples" property to greater 0 to enable multisampling."); const renderTarget = new WebGLRenderTarget(width, height, options); renderTarget.samples = 4; return renderTarget; } function DataTexture2DArray(data, width, height, depth) { console.warn("THREE.DataTexture2DArray has been renamed to DataArrayTexture."); return new DataArrayTexture(data, width, height, depth); } function DataTexture3D(data, width, height, depth) { console.warn("THREE.DataTexture3D has been renamed to Data3DTexture."); return new Data3DTexture(data, width, height, depth); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
popmotion-9.3.6",["tslib-2.2.0","hey-listen-1.0.8","style-value-types-4.1.4","framesync-5.3.0"],(function(a,b,c,d,e,f){"use strict";function a(a){return a&&typeof a==="object"&&"default"in a?a["default"]:a}var g=a(b("tslib-2.2.0")),h=a(b("hey-listen-1.0.8")),i=a(b("style-value-types-4.1.4")),j=a(b("framesync-5.3.0")),k={},l={exports:k};function m(){Object.defineProperty(k,"__esModule",{value:!0});var a=g(),b=h(),c=i(),d=j();function e(a){return a&&typeof a==="object"&&"default"in a?a:{"default":a}}var f=e(d),l=function(a,b,c){return Math.min(Math.max(c,a),b)},m=.001,n=.01,o=10,p=.05,q=1;function r(a){var c=a.duration,d=c===void 0?800:c;c=a.bounce;c=c===void 0?.25:c;var e=a.velocity,f=e===void 0?0:e;e=a.mass;a=e===void 0?1:e;var g;b.warning(d<=o*1e3,"Spring duration must be 10 seconds or less");var h=1-c;h=l(p,q,h);d=l(n,o,d/1e3);h<1?(g=function(a){var b=a*h,c=b*d;b=b-f;a=u(a,h);c=Math.exp(-c);return m-b/a*c},e=function(a){var b=a*h;b=b*d;var c=b*f+f,e=Math.pow(h,2)*Math.pow(a,2)*d;b=Math.exp(-b);var i=u(Math.pow(a,2),h);a=-g(a)+m>0?-1:1;return a*((c-e)*b)/i}):(g=function(b){var a=Math.exp(-b*d);b=(b-f)*d+1;return-m+a*b},e=function(b){var a=Math.exp(-b*d);b=(f-b)*(d*d);return a*b});c=5/d;e=t(g,e,c);d=d*1e3;if(isNaN(e))return{stiffness:100,damping:10,duration:d};else{c=Math.pow(e,2)*a;return{stiffness:c,damping:h*2*Math.sqrt(a*c),duration:d}}}var s=12;function t(a,b,c){c=c;for(var d=1;d=m;h.value=h.done?e:b;return h},flipTarget:function(){var a;l=-l;a=[e,d],d=a[0],e=a[1];q()}}}z.needsInterpolation=function(a,b){return typeof a==="string"||typeof b==="string"};var A=function(a){return 0},B=function(a,b,c){b=b-a;return b===0?1:(c-a)/b},C=function(b,c,a){return-a*b+a*c+b},D=function(a,b,c){a=a*a;b=b*b;return Math.sqrt(Math.max(0,c*(b-a)+a))},aa=[c.hex,c.rgba,c.hsla],E=function(a){return aa.find(function(b){return b.test(a)})},F=function(a){return"""+a+"" is not an animatable color. Use the equivalent color code instead."},G=function(d,e){var f=E(d),g=E(e);b.invariant(!!f,F(d));b.invariant(!!g,F(e));b.invariant(f.transform===g.transform,"Both colors must be hex/RGBA, OR both must be HSLA.");var h=f.parse(d),i=g.parse(e),j=a.__assign({},h),k=f===c.hsla?C:D;return function(a){for(var b in j)b!=="alpha"&&(j[b]=k(h[b],i[b],a));j.alpha=C(h.alpha,i.alpha,a);return f.transform(j)}},ba={x:0,y:0,z:0},H=function(a){return typeof a==="number"},ca=function(a,b){return function(c){return b(a(c))}},I=function(){var a=[];for(var b=0;b=g.numNumbers,"Complex values ""+a+"" and ""+d+"" too different to mix. Ensure all colors are of the same type.");return I(K(f.parsed,g.parsed),e)},fa=function(a,b){return function(c){return C(a,b,c)}};function ga(a){if(typeof a==="number")return fa;else if(typeof a==="string")if(c.color.test(a))return G;else return L;else if(Array.isArray(a))return K;else if(typeof a==="object")return da}function ha(a,b,c){var d=[];c=c||ga(a[0]);var e=a.length-1;for(var f=0;f=a[d]&&(f=d-1,g=!0);if(!g){g=1;for(;ge||g===d)break;f=g-1}g=B(a[f],a[f+1],e);return b[f](g)}}function M(a,c,d){d=d===void 0?{}:d;var e=d.clamp;e=e===void 0?!0:e;var f=d.ease;d=d.mixer;var g=a.length;b.invariant(g===c.length,"Both input and output ranges must be the same length");b.invariant(!f||!Array.isArray(f)||f.length===g-1,"Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.");a[0]>a[g-1]&&(a=[].concat(a),c=[].concat(c),a.reverse(),c.reverse());c=ha(c,f,d);var h=g===2?ia(a,c):ja(a,c);return e?function(b){return h(l(a[0],a[g-1],b))}:h}e=function(a){return function(b){return 1-a(1-b)}};var N=function(a){return function(b){return b<=.5?a(2*b)/2:(2-a(2*(1-b)))/2}},ka=function(a){return function(b){return Math.pow(b,a)}},O=function(a){return function(b){return b*b*((a+1)*b-a)}},la=function(a){var b=O(a);return function(a){return(a*=2)<1?.5*b(a):.5*(2-Math.pow(2,-10*(a-1)))}},P=1.525,ma=4/11,na=8/11,oa=9/10,pa=function(a){return a},Q=ka(2),qa=e(Q),ra=N(Q),sa=function(a){return 1-Math.sin(Math.acos(a))},ta=e(sa),ua=N(ta),R=O(P),va=e(R),wa=N(R);P=la(P);var xa=4356/361,ya=35442/1805,za=16061/1805,S=function(a){if(a===1||a===0)return a;var b=a*a;return a=f;return g},flipTarget:function(){h.reverse(),k=j()}}}function Fa(a){var b=a.velocity;b=b===void 0?0:b;var c=a.from;c=c===void 0?0:c;var d=a.power;d=d===void 0?.8:d;var e=a.timeConstant,f=e===void 0?350:e;e=a.restDelta;var g=e===void 0?.5:e;e=a.modifyTarget;var h={done:!1,value:c},i=d*b;a=c+i;var j=e===void 0?a:e(a);j!==a&&(i=j-c);return{next:function(a){a=-i*Math.exp(-a/f);h.done=!(a>g||a<-g);h.value=h.done?j:j+a;return h},flipTarget:function(){}}}var Ga={keyframes:T,spring:z,decay:Fa};function Ha(a){if(Array.isArray(a.to))return T;else if(Ga[a.type])return Ga[a.type];a=new Set(Object.keys(a));if(a.has("ease")||a.has("duration")&&!a.has("dampingRatio"))return T;else if(a.has("dampingRatio")||a.has("stiffness")||a.has("mass")||a.has("damping")||a.has("restSpeed")||a.has("restDelta"))return z;return T}function Ia(a,b,c){c===void 0&&(c=0);return a-b-c}function Ja(a,b,c,d){c===void 0&&(c=0);d===void 0&&(d=!0);return d?Ia(b+-a,b,c):b-(a-b)+c}function Ka(a,b,c,d){return d?a>=b+c:a<=-c}var La=function(a){var b=function(b){b=b.delta;return a(b)};return{start:function(){return f["default"].update(b,!0)},stop:function(){return d.cancelSync.update(b)}}};function Ma(b){var c,d,e=b.from,f=b.autoplay;f=f===void 0?!0:f;var g=b.driver,h=g===void 0?La:g;g=b.elapsed;var i=g===void 0?0:g;g=b.repeat;var j=g===void 0?0:g;g=b.repeatType;var k=g===void 0?"loop":g;g=b.repeatDelay;var l=g===void 0?0:g,m=b.onPlay,n=b.onStop,o=b.onComplete,p=b.onRepeat,q=b.onUpdate;g=a.__rest(b,["from","autoplay","driver","elapsed","repeat","repeatType","repeatDelay","onPlay","onStop","onComplete","onRepeat","onUpdate"]);b=g.to;var r,s=0,t=g.duration,u,v=!1,w=!0,x,y=Ha(g);((d=(c=y).needsInterpolation)===null||d===void 0?void 0:d.call(c,e,b))&&(x=M([0,100],[e,b],{clamp:!1}),e=0,b=100);var z=y(a.__assign(a.__assign({},g),{from:e,to:b}));function A(){s++,k==="reverse"?(w=s%2===0,i=Ja(i,t,l,w)):(i=Ia(i,t,l),k==="mirror"&&z.flipTarget()),v=!1,p&&p()}function B(){r.stop(),o&&o()}function C(a){w||(a=-a);i+=a;if(!v){a=z.next(Math.max(0,i));u=a.value;x&&(u=x(u));v=w?a.done:i<=0}q===null||q===void 0?void 0:q(u);v&&(s===0&&(t!==null&&t!==void 0?t:t=i),sg}function s(a){if(f===void 0)return g;return g===void 0?f:Math.abs(f-a)v||w===-1&&af)return a[b-1];if(b===c-1)return g;f=d}}}};function bb(a,b){return a/(1e3/b)}var cb=function(a,b,c){b=b-a;return((c-a)%b+b)%b+a},db=function(a,b){return 1-3*b+3*a},eb=function(a,b){return 3*b-6*a},fb=function(a){return 3*a},Y=function(a,b,c){return((db(b,c)*a+eb(b,c))*a+fb(b))*a},gb=function(a,b,c){return 3*db(b,c)*a*a+2*eb(b,c)*a+fb(b)},hb=1e-7,ib=10;function jb(a,b,c,d,e){var f,g,h=0;do g=b+(c-b)/2,f=Y(g,d,e)-a,f>0?c=g:b=g;while(Math.abs(f)>hb&&++h=lb)return mb(b,f,a,c);else if(g===0)return f;else return jb(b,d,d+$,a,c)}return function(a){return a===0||a===1?a:Y(g(a),b,d)}}var ob=function(a,b){b===void 0&&(b="end");return function(c){c=b==="end"?Math.min(c,.999):Math.max(c,.001);c=c*a;c=b==="end"?Math.floor(c):Math.ceil(c);return l(0,1,c/a)}};k.angle=Qa;k.animate=Ma;k.anticipate=P;k.applyOffset=Ra;k.attract=Ta;k.attractExpo=Ua;k.backIn=R;k.backInOut=wa;k.backOut=va;k.bounceIn=Aa;k.bounceInOut=Ba;k.bounceOut=S;k.circIn=sa;k.circInOut=ua;k.circOut=ta;k.clamp=l;k.createAnticipate=la;k.createAttractor=U;k.createBackIn=O;k.createExpoIn=ka;k.cubicBezier=nb;k.decay=Fa;k.degreesToRadians=Va;k.distance=Wa;k.easeIn=Q;k.easeInOut=ra;k.easeOut=qa;k.inertia=Oa;k.interpolate=M;k.isPoint=V;k.isPoint3D=W;k.keyframes=T;k.linear=pa;k.mirrorEasing=N;k.mix=C;k.mixColor=G;k.mixComplex=L;k.pipe=I;k.pointFromVector=Xa;k.progress=B;k.radiansToDegrees=Pa;k.reverseEasing=e;k.smooth=$a;k.smoothFrame=Za;k.snap=ab;k.spring=z;k.steps=ob;k.toDecimal=Ya;k.velocityPerFrame=bb;k.velocityPerSecond=Na;k.wrap=cb}var n=!1;function o(){n||(n=!0,m());return l.exports}function c(a){switch(a){case void 0:return o()}}e.exports=c}),null); /** * License: https://www.facebook.com/legal/license/t3hOLs8wlXy/ */
-----
framer-motion-4.1.16",["tslib-2.2.0","react-0.0.0","hey-listen-1.0.8","style-value-types-4.1.4","framesync-5.3.0","popmotion-9.3.6","emotion-is-prop-valid-0.8.8"],(function(a,b,c,d,e,f){"use strict";var g=b("tslib-2.2.0"),h=b("react-0.0.0"),i=b("hey-listen-1.0.8"),j=b("style-value-types-4.1.4"),k=b("framesync-5.3.0"),l=b("popmotion-9.3.6"),m=b("emotion-is-prop-valid-0.8.8"),n={},o={exports:n};function p(){Object.defineProperty(n,"__esModule",{value:!0});var a=g(),b=h(),c=i(),d=j(),e=k(),f=l();function o(a){return a&&typeof a==="object"&&"default"in a?a:{"default":a}}function p(a){if(a&&a.__esModule)return a;var b=Object.create(null);a&&Object.keys(a).forEach(function(c){if(c!=="default"){var d=Object.getOwnPropertyDescriptor(a,c);Object.defineProperty(b,c,d.get?d:{enumerable:!0,get:function(){return a[c]}})}});b["default"]=a;return Object.freeze(b)}var q=p(b),r=o(b),s=o(e);p=function(a){return{isEnabled:function(b){return a.some(function(a){return!!b[a]})}}};var t={measureLayout:p(["layout","layoutId","drag","_layoutResetTransform"]),animation:p(["animate","exit","variants","whileHover","whileTap","whileFocus","whileDrag"]),exit:p(["exit"]),drag:p(["drag","dragControls"]),focus:p(["whileFocus"]),hover:p(["whileHover","onHoverStart","onHoverEnd"]),tap:p(["whileTap","onTap","onTapStart","onTapCancel"]),pan:p(["onPan","onPanStart","onPanSessionStart","onPanEnd"]),layoutAnimation:p(["layout","layoutId"])};function u(a){for(var b in a){var c=a[b];c!==null&&(t[b].Component=c)}}var v=b.createContext({strict:!1}),w=Object.keys(t),x=w.length;function y(c,d,e){e=[];b.useContext(v);if(!d)return null;for(var f=0;f-1||/[A-Z]/.test(a))return!0;return!1}var ra={};function sa(a){for(var b in a)ra[b]=a[b]}var ta=["","X","Y","Z"];o=["translate","scale","rotate","skew"];var ua=["transformPerspective","x","y","z"];o.forEach(function(a){return ta.forEach(function(b){return ua.push(a+b)})});function va(a,b){return ua.indexOf(a)-ua.indexOf(b)}var wa=new Set(ua);function xa(a){return wa.has(a)}var ya=new Set(["originX","originY","originZ"]);function za(a){return ya.has(a)}function Aa(a,b){var c=b.layout;b=b.layoutId;return xa(a)||za(a)||(c||b!==void 0)&&(!!ra[a]||a==="opacity")}var P=function(a){return a!==null&&typeof a==="object"&&a.getVelocity},Ba={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};function Ca(b,c,d,e){var a=b.transform;b=b.transformKeys;var f=c.enableHardwareAcceleration;f=f===void 0?!0:f;c=c.allowTransformNone;c=c===void 0?!0:c;var g="";b.sort(va);var h=!1,i=b.length;for(var j=0;j-1&&a.splice(b,1)}var zc=function(){function a(){this.subscriptions=[]}a.prototype.add=function(a){var b=this;xc(this.subscriptions,a);return function(){return yc(b.subscriptions,a)}};a.prototype.notify=function(a,b,c){var d=this.subscriptions.length;if(!d)return;if(d===1)this.subscriptions[0](a,b,c);else for(var e=0;ep&&s;s=Array.isArray(r)?r:[r];var v=s.reduce(g,{});t===!1&&(v={});t=q.prevResolvedValues;t=t===void 0?{}:t;var w=a.__assign(a.__assign({},t),v),x=function(a){b=!0,n["delete"](a),q.needsAnimating[a]=!0};for(w in w){var y=v[w],z=t[w];if(o.hasOwnProperty(w))continue;y!==z?kb(y)&&kb(z)?!$b(y,z)?x(w):q.protectedKeys[w]=!0:y!==void 0?x(w):n.add(w):y!==void 0&&n.has(w)?x(w):q.protectedKeys[w]=!0}q.prevProp=r;q.prevResolvedValues=v;q.isActive&&(o=a.__assign(a.__assign({},o),v));f&&c.blockInitialAnimation&&(b=!1);b&&!u&&m.push.apply(m,a.__spreadArray([],a.__read(s.map(function(b){return{animation:b,options:a.__assign({type:e},h)}}))))};for(var r=0;r=3;if(!d&&!h)return;h=c.point;var b=e.getFrameData().timestamp;g.history.push(a.__assign(a.__assign({},h),{timestamp:b}));h=g.handlers;b=h.onStart;h=h.onMove;d||(b&&b(g.lastMoveEvent,c),g.startEvent=g.lastMoveEvent);h&&h(g.lastMoveEvent,c)};this.handlePointerMove=function(a,b){g.lastMoveEvent=a;g.lastMoveEventInfo=cd(b,g.transformPagePoint);if(vb(a)&&a.buttons===0){g.handlePointerUp(a,b);return}s["default"].update(g.updatePoint,!0)};this.handlePointerUp=function(a,c){g.end();var b=g.handlers,d=b.onEnd;b=b.onSessionEnd;c=ed(cd(c,g.transformPagePoint),g.history);g.startEvent&&d&&d(a,c);b&&b(a,c)};if(wb(b)&&b.touches.length>1)return;this.handlers=c;this.transformPagePoint=d;d=Bb(b);d=cd(d,this.transformPagePoint);var h=d.point,i=e.getFrameData().timestamp;this.history=[a.__assign(a.__assign({},h),{timestamp:i})];h=c.onSessionStart;h&&h(b,ed(d,this.history));this.removeListeners=f.pipe(Kb(window,"pointermove",this.handlePointerMove),Kb(window,"pointerup",this.handlePointerUp),Kb(window,"pointercancel",this.handlePointerUp))}b.prototype.updateHandlers=function(a){this.handlers=a};b.prototype.end=function(){this.removeListeners&&this.removeListeners(),e.cancelSync.update(this.updatePoint)};return b}();function cd(a,b){return b?{point:b(a.point)}:a}function dd(a,b){return{x:a.x-b.x,y:a.y-b.y}}function ed(a,b){a=a.point;return{point:a,delta:dd(a,gd(b)),offset:dd(a,fd(b)),velocity:hd(b,.1)}}function fd(a){return a[0]}function gd(a){return a[a.length-1]}function hd(a,b){if(a.length<2)return{x:0,y:0};var c=a.length-1,d=null,e=gd(a);while(c>=0){d=a[c];if(e.timestamp-d.timestamp>ac(b))break;c--}if(!d)return{x:0,y:0};a=(e.timestamp-d.timestamp)/1e3;if(a===0)return{x:0,y:0};c={x:(e.x-d.x)/a,y:(e.y-d.y)/a};c.x===Infinity&&(c.x=0);c.y===Infinity&&(c.y=0);return c}function id(a){return a}function jd(a){var b=a.top,c=a.left,d=a.right;a=a.bottom;return{x:{min:c,max:d},y:{min:b,max:a}}}function kd(a){var b=a.x;a=a.y;return{top:a.min,bottom:a.max,left:b.min,right:b.max}}function ld(b,a){var c=b.top,d=b.left,e=b.bottom;b=b.right;a===void 0&&(a=id);d=a({x:d,y:c});c=a({x:b,y:e});return{top:d.y,left:d.x,bottom:c.y,right:c.x}}function W(){return{x:{min:0,max:1},y:{min:0,max:1}}}function md(b){return{x:a.__assign({},b.x),y:a.__assign({},b.y)}}var nd={translate:0,scale:1,origin:0,originPoint:0};function od(){return{x:a.__assign({},nd),y:a.__assign({},nd)}}function X(a){return[a("x"),a("y")]}var pd=function(a){return f.clamp(0,1,a)};function qd(a,b,c){b===void 0&&(b=0);c===void 0&&(c=.01);return f.distance(a,b)d?c=f.progress(b.min,b.max-d,a.min):d>e&&(c=f.progress(a.min,a.max-e,b.min));return pd(c)}function td(a,b,c,d){d===void 0&&(d=.5),a.origin=d,a.originPoint=f.mix(b.min,b.max,a.origin),a.scale=rd(c)/rd(b),qd(a.scale,1,1e-4)&&(a.scale=1),a.translate=f.mix(c.min,c.max,a.origin)-a.originPoint,qd(a.translate)&&(a.translate=0)}function ud(a,b,c,d){td(a.x,b.x,c.x,vd(d.originX)),td(a.y,b.y,c.y,vd(d.originY))}function vd(a){return typeof a==="number"?a:.5}function wd(a,b,c){a.min=c.min+b.min,a.max=a.min+rd(b)}function xd(a,b){wd(a.target.x,a.relativeTarget.x,b.target.x),wd(a.target.y,a.relativeTarget.y,b.target.y)}function yd(a,b,c){var d=b.min;b=b.max;d!==void 0&&ab&&(a=c?f.mix(b,a,c.max):Math.min(a,b));return a}function zd(a,b,c,d,e){a=a-b*c;return d?yd(a,d,e):a}function Ad(a,b,c){return{min:b!==void 0?a.min+b:void 0,max:c!==void 0?a.max+c-(a.max-a.min):void 0}}function Bd(a,b){var c=b.top,d=b.left,e=b.bottom;b=b.right;return{x:Ad(a.x,d,b),y:Ad(a.y,c,e)}}function Cd(b,c){var d=c.min-b.min,e=c.max-b.max;c.max-c.minb?c="y":Math.abs(a.x)>b&&(c="x");return c}function qe(c){var d=c.dragControls,e=c.visualElement,f=b.useContext(z).transformPagePoint,g=D(function(){return new ne({visualElement:e})});g.setProps(a.__assign(a.__assign({},c),{transformPagePoint:f}));b.useEffect(function(){return d&&d.subscribe(g)},[g]);b.useEffect(function(){return g.mount(e)},[])}function Y(a){var c=a.onPan,d=a.onPanStart,e=a.onPanEnd,f=a.onPanSessionStart;a=a.visualElement;var g=c||d||e||f,h=b.useRef(null),i=b.useContext(z).transformPagePoint,j={onSessionStart:f,onStart:d,onMove:c,onEnd:function(a,b){h.current=null,e&&e(a,b)}};b.useEffect(function(){h.current!==null&&h.current.updateHandlers(j)});function k(a){h.current=new bd(a,j,{transformPagePoint:i})}Lb(a,"pointerdown",g&&k);Zb(function(){return h.current&&h.current.end()})}U={pan:p(Y),drag:p(qe)};var Z;(function(a){a[a.Entering=0]="Entering",a[a.Present=1]="Present",a[a.Exiting=2]="Exiting"})(Z||(Z={}));n.VisibilityAction=void 0;(function(a){a[a.Hide=0]="Hide",a[a.Show=1]="Show"})(n.VisibilityAction||(n.VisibilityAction={}));function re(a){return typeof a==="string"&&a.startsWith("var(--")}var se=/var((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?)/;function te(b){b=se.exec(b);if(!b)return[,];b=a.__read(b,3);var c=b[1];b=b[2];return[c,b]}var ue=4;function ve(b,d,e){e===void 0&&(e=1);c.invariant(e<=ue,"Max CSS variable fallback depth detected in property ""+b+"". This may indicate a circular fallback dependency.");b=a.__read(te(b),2);var f=b[0];b=b[1];if(!f)return;f=window.getComputedStyle(d).getPropertyValue(f);if(f)return f.trim();else if(re(b))return ve(b,d,e+1);else return b}function we(b,c,d){c=a.__rest(c,[]);var e=b.getInstance();if(!(e instanceof HTMLElement))return{target:c,transitionEnd:d};d&&(d=a.__assign({},d));b.forEachValue(function(b){var a=b.get();if(!re(a))return;a=ve(a,e);a&&b.set(a)});for(b in c){var f=c[b];if(!re(f))continue;var g=ve(f,e);if(!g)continue;c[b]=g;d&&((g=d[b])!==null&&g!==void 0?g:d[b]=f)}return{target:c,transitionEnd:d}}function xe(a,b){return a/(b.max-b.min)*100}function ye(a,b,c){b=c.target;if(typeof a==="string")if(d.px.test(a))a=parseFloat(a);else return a;c=xe(a,b.x);a=xe(a,b.y);return c+"% "+a+"%"}var ze="_$css";function Ae(b,c){var a=c.delta;c=c.treeScale;var e=b,g=b.includes("var("),h=[];g&&(b=b.replace(se,function(a){h.push(a);return ze}));var i=d.complex.parse(b);if(i.length>5)return e;e=d.complex.createTransformer(b);b=typeof i[0]!=="number"?1:0;var j=a.x.scale*c.x;a=a.y.scale*c.y;i[0+b]/=j;i[1+b]/=a;c=f.mix(j,a,.5);typeof i[2+b]==="number"&&(i[2+b]/=c);typeof i[3+b]==="number"&&(i[3+b]/=c);j=e(i);if(g){var k=0;j=j.replace(ze,function(){var a=h[k];k++;return a})}return j}Y={process:ye};var Be={borderRadius:a.__assign(a.__assign({},Y),{applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]}),borderTopLeftRadius:Y,borderTopRightRadius:Y,borderBottomLeftRadius:Y,borderBottomRightRadius:Y,boxShadow:{process:Ae}},Ce=1e3,De=function(c){a.__extends(b,c);function b(){var b=c!==null&&c.apply(this,arguments)||this;b.frameTarget=W();b.currentAnimationTarget=W();b.isAnimating={x:!1,y:!1};b.stopAxisAnimation={x:void 0,y:void 0};b.isAnimatingTree=!1;b.animate=function(c,d,e){e===void 0&&(e={});var f=e.originBox,g=e.targetBox,h=e.visibilityAction,i=e.shouldStackAnimate,j=e.onComplete,k=e.prevParent,l=a.__rest(e,["originBox","targetBox","visibilityAction","shouldStackAnimate","onComplete","prevParent"]);e=b.props;var m=e.visualElement,o=e.layout;if(i===!1){b.isAnimatingTree=!1;return b.safeToRemove()}if(b.isAnimatingTree&&i!==!0)return;else i&&(b.isAnimatingTree=!0);d=f||d;c=g||c;var p=!1;e=m.getProjectionParent();if(e){i=e.prevViewportBox;var q=e.getLayoutState().layout;k&&(g&&(q=k.getLayoutState().layout),f&&!Ud(k,e)&&k.prevViewportBox&&(i=k.prevViewportBox));i&&Ke(k,f,g)&&(p=!0,d=Td(i,d),c=Td(q,c))}var r=Fe(d,c);e=X(function(e){var f;if(o==="position"){var g=c[e].max-c[e].min;d[e].max=d[e].min+g}if(m.projection.isTargetLocked)return;else if(h!==void 0)m.setVisibility(h===n.VisibilityAction.Show);else if(r)return b.animateAxis(e,c[e],d[e],a.__assign(a.__assign({},l),{isRelative:p}));else{(f=(g=b.stopAxisAnimation)[e])===null||f===void 0?void 0:f.call(g);return m.setProjectionTargetAxis(e,c[e].min,c[e].max,p)}});m.syncRender();return Promise.all(e).then(function(){b.isAnimatingTree=!1,j&&j(),m.notifyLayoutAnimationComplete()})};return b}b.prototype.componentDidMount=function(){var a=this,b=this.props.visualElement;b.animateMotionValue=vc;b.enableLayoutProjection();this.unsubLayoutReady=b.onLayoutUpdate(this.animate);b.layoutSafeToRemove=function(){return a.safeToRemove()};sa(Be)};b.prototype.componentWillUnmount=function(){var a=this;this.unsubLayoutReady();X(function(b){var c;return(b=(c=a.stopAxisAnimation)[b])===null||b===void 0?void 0:b.call(c)})};b.prototype.animateAxis=function(a,b,c,d){var e=this,f;d=d===void 0?{}:d;var g=d.transition,h=d.isRelative;if(this.isAnimating[a]&&Ie(b,this.currentAnimationTarget[a]))return;(f=(d=this.stopAxisAnimation)[a])===null||f===void 0?void 0:f.call(d);this.isAnimating[a]=!0;var i=this.props.visualElement,j=this.frameTarget[a],k=i.getProjectionAnimationProgress()[a];k.clearListeners();k.set(0);k.set(0);f=function(){var d=k.get()/Ce;Rd(j,c,b,d);i.setProjectionTargetAxis(a,j.min,j.max,h)};f();var l=k.onChange(f);this.stopAxisAnimation[a]=function(){e.isAnimating[a]=!1,k.stop(),l()};this.currentAnimationTarget[a]=b;d=g||i.getDefaultTransition()||Je;f=vc(a==="x"?"layoutX":"layoutY",k,Ce,d&&uc(d,"layout")).then(this.stopAxisAnimation[a]);return f};b.prototype.safeToRemove=function(){var a,b;(b=(a=this.props).safeToRemove)===null||b===void 0?void 0:b.call(a)};b.prototype.render=function(){return null};return b}(q.Component);function Ee(b){var c=a.__read(E(),2);c=c[1];return q.createElement(De,a.__assign({},b,{safeToRemove:c}))}function Fe(a,b){return!He(a)&&!He(b)&&(!Ie(a.x,b.x)||!Ie(a.y,b.y))}var Ge={min:0,max:0};function He(a){return Ie(a.x,Ge)&&Ie(a.y,Ge)}function Ie(a,b){return a.min===b.min&&a.max===b.max}var Je={duration:.45,ease:[.4,0,.1,1]};function Ke(a,b,c){return a||!a&&!(b||c)}var Le={layoutReady:function(a){return a.notifyLayoutReady()}};function Me(){var b=new Set();return{add:function(a){return b.add(a)},flush:function(c){c=c===void 0?Le:c;var d=c.layoutReady,f=c.parent;Ub(function(c,g){var h=Array.from(b).sort(Ld),i=f?Nd(f):[];g(function(){var b=a.__spreadArray(a.__spreadArray([],a.__read(i)),a.__read(h));b.forEach(function(a){return a.resetTransform()})});c(function(){h.forEach(Pd)});g(function(){i.forEach(function(a){return a.restoreTransform()}),h.forEach(d)});c(function(){h.forEach(function(a){a.isPresent&&(a.presence=Z.Present)})});g(function(){e.flushSync.preRender(),e.flushSync.render()});c(function(){s["default"].postRender(function(){return h.forEach(Ne)}),b.clear()})});Vb()}}}function Ne(a){a.prevViewportBox=a.projection.target}var Oe=b.createContext(Me()),Pe=b.createContext(Me());function $(a){return!!a.forceUpdate}var Qe=function(c){a.__extends(b,c);function b(){return c!==null&&c.apply(this,arguments)||this}b.prototype.componentDidMount=function(){var a=this.props,b=a.syncLayout,c=a.framerSyncLayout,d=a.visualElement;$(b)&&b.register(d);$(c)&&c.register(d);d.onUnmount(function(){$(b)&&b.remove(d),$(c)&&c.remove(d)})};b.prototype.getSnapshotBeforeUpdate=function(){var a=this.props,b=a.syncLayout;a=a.visualElement;$(b)?b.syncUpdate():(Qd(a),b.add(a));return null};b.prototype.componentDidUpdate=function(){var a=this.props.syncLayout;$(a)||a.flush()};b.prototype.render=function(){return null};return b}(r["default"].Component);function Re(c){var d=b.useContext(Oe),e=b.useContext(Pe);return r["default"].createElement(Qe,a.__assign({},c,{syncLayout:d,framerSyncLayout:e}))}p={measureLayout:Re,layoutAnimation:Ee};var Se=function(){return{isEnabled:!1,isHydrated:!1,isTargetLocked:!1,target:W(),targetFinal:W()}};function Te(){return{isHydrated:!1,layout:W(),layoutCorrected:W(),treeScale:{x:1,y:1},delta:od(),deltaFinal:od(),deltaTransform:""}}qe=Te();function Ue(a,b,c){var d=a.x;a=a.y;var e=d.translate/b.x;b=a.translate/b.y;e="translate3d("+e+"px, "+b+"px, 0) ";if(c){b=c.rotate;var f=c.rotateX,g=c.rotateY;b&&(e+="rotate("+b+") ");f&&(e+="rotateX("+f+") ");g&&(e+="rotateY("+g+") ")}e+="scale("+d.scale+", "+a.scale+")";return!c&&e===We?"":e}function Ve(a){a=a.deltaFinal;return a.x.origin*100+"% "+a.y.origin*100+"% 0"}var We=Ue(qe.delta,qe.treeScale,{x:1,y:1}),Xe=["LayoutMeasure","BeforeLayoutMeasure","LayoutUpdate","ViewportBoxUpdate","Update","Render","AnimationComplete","LayoutAnimationComplete","AnimationStart","SetAxisTarget","Unmount"];function Ye(){var b=Xe.map(function(){return new zc()}),c={},d={clearAllListeners:function(){return b.forEach(function(a){return a.clear()})},updatePropListeners:function(a){return Xe.forEach(function(b){var e;(e=c[b])===null||e===void 0?void 0:e.call(c);e="on"+b;var f=a[e];f&&(c[b]=d[e](f))})}};b.forEach(function(b,c){d["on"+Xe[c]]=function(a){return b.add(a)},d["notify"+Xe[c]]=function(){var c=[];for(var d=0;d=0;b--){var c=O.path[b];if(c.projection.isEnabled){a=c;break}}C=a}return C},resolveRelativeTargetBox:function(){var a=O.getProjectionParent();if(!B.relativeTarget||!a)return;xd(B,a.projection);if(Vd(a)){var b=B.target;ee(b,b,a.getLatestValues())}},shouldResetTransform:function(){return Boolean(v._layoutResetTransform)},pointTo:function(a){D=a.projection,E=a.getLatestValues(),F===null||F===void 0?void 0:F(),F=f.pipe(a.onSetAxisTarget(O.scheduleUpdateLayoutProjection),a.onLayoutAnimationComplete(function(){var a;O.isPresent?O.presence=Z.Present:(a=O.layoutSafeToRemove)===null||a===void 0?void 0:a.call(O)}))},isPresent:!0,presence:Z.Entering});return O}};function cf(a){a.resolveRelativeTargetBox()}function df(a){a.updateLayoutProjection()}var ef=a.__spreadArray(["initial"],a.__read(T)),ff=ef.length,gf=new Set(["width","height","top","left","right","bottom","x","y"]),hf=function(a){return gf.has(a)},jf=function(a){return Object.keys(a).some(hf)},kf=function(a,b){a.set(b,!1),a.set(b)},lf=function(a){return a===d.number||a===d.px},mf;(function(a){a.width="width",a.height="height",a.left="left",a.right="right",a.top="top",a.bottom="bottom"})(mf||(mf={}));var nf=function(a,b){return parseFloat(a.split(", ")[b])};Y=function(a,b){return function(c,d){c=d.transform;if(c==="none"||!c)return 0;d=c.match(/^matrix3d((.+))$/);if(d)return nf(d[1],b);else{d=c.match(/^matrix((.+))$/);if(d)return nf(d[1],a);else return 0}}};var of=new Set(["x","y","z"]),pf=ua.filter(function(a){return!of.has(a)});function qf(a){var b=[];pf.forEach(function(c){var d=a.getValue(c);d!==void 0&&(b.push([c,d.get()]),d.set(c.startsWith("scale")?1:0))});b.length&&a.syncRender();return b}var rf={width:function(a){a=a.x;return a.max-a.min},height:function(a){a=a.y;return a.max-a.min},top:function(a,b){a=b.top;return parseFloat(a)},left:function(a,b){a=b.left;return parseFloat(a)},bottom:function(a,b){a=a.y;b=b.top;return parseFloat(b)+(a.max-a.min)},right:function(a,b){a=a.x;b=b.left;return parseFloat(b)+(a.max-a.min)},x:Y(4,13),y:Y(5,14)},sf=function(b,c,d){var e=c.measureViewportBox(),f=c.getInstance(),g=getComputedStyle(f);f=g.display;var h=g.top,i=g.left,j=g.bottom,k=g.right,a=g.transform,l={top:h,left:i,bottom:j,right:k,transform:a};f==="none"&&c.setStaticValue("display",b.display||"block");c.syncRender();var m=c.measureViewportBox();d.forEach(function(a){var d=c.getValue(a);kf(d,rf[a](e,l));b[a]=rf[a](m,g)});return b},tf=function(b,e,f,g){f===void 0&&(f={});g===void 0&&(g={});e=a.__assign({},e);g=a.__assign({},g);var h=Object.keys(e).filter(hf),i=[],j=!1,k=[];h.forEach(function(a){var h=b.getValue(a);if(!b.hasValue(a))return;var l=f[a],m=e[a],n=Ec(l),o;if(kb(m)){var p=m.length;for(var q=m[0]===null?1:0;qb?1:c(f.progress(a,b,d))}}var Pf=["TopLeft","TopRight","BottomLeft","BottomRight"],Qf=Pf.length;function Rf(a,b,c,d,e,g){for(var h=0;h=0){var l=j[k];f&&(g!==null&&g!==void 0?g:g=l);f!==null&&f!==void 0?f:f=l;if(f&&g)break}c.lead=f;c.follow=g;c.leadIsExiting=((l=c.lead)===null||l===void 0?void 0:l.presence)===Z.Exiting;h.setOptions({lead:f,follow:g,prevValues:e,crossfadeOpacity:(g===null||g===void 0?void 0:g.isPresenceRoot)||(f===null||f===void 0?void 0:f.isPresenceRoot)});c.lead!==d.follow&&(d.lead!==c.lead||d.leadIsExiting!==c.leadIsExiting)&&(i=!0)},animate:function(a,b){var d;b===void 0&&(b=!1);if(a===c.lead){b?a.pointTo(c.lead):a.setVisibility(!0);var e={};d=(d=c.follow)===null||d===void 0?void 0:d.getProjectionParent();d&&(e.prevParent=d);a.presence===Z.Entering?e.originBox=j():a.presence===Z.Exiting&&(e.targetBox=k());if(i){i=!1;d=a.getDefaultTransition();a.presence===Z.Entering?h.toLead(d):h.fromLead(d)}a.notifyLayoutReady(e)}else b?c.lead&&a.pointTo(c.lead):a.setVisibility(!1)}}}function Uf(a){var b=!1,c={};for(var d=0;d.001?1/a:Fg},Hg=!1;function Ig(a){var b=Yf(1),d=Yf(1),e=B();c.invariant(!!(a||e),"If no scale values are provided, useInvertedScale must be used within a child of another motion component.");c.warning(Hg,"useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");Hg=!0;a?(b=a.scaleX||b,d=a.scaleY||d):e&&(b=e.getValue("scaleX",1),d=e.getValue("scaleY",1));a=fg(b,Gg);e=fg(d,Gg);return{scaleX:a,scaleY:e}}n.AnimatePresence=qe;n.AnimateSharedLayout=T;n.DragControls=zg;n.FlatTree=af;n.FramerTreeLayoutContext=Pe;n.LayoutGroupContext=ba;n.LazyMotion=Wf;n.MotionConfig=Vf;n.MotionConfigContext=z;n.MotionValue=Bc;n.PresenceContext=C;n.SharedLayoutContext=Oe;n.addScaleCorrection=sa;n.animate=Kf;n.animateVisualElement=Oc;n.animationControls=wg;n.batchLayout=Ub;n.createBatcher=Me;n.createCrossfader=Lf;n.createDomMotionComponent=Bf;n.createMotionComponent=na;n.domAnimation=Y;n.domMax=Ae;n.flushLayout=Vb;n.isValidMotionProp=Oa;n.m=Ee;n.motion=Re;n.motionValue=V;n.resolveMotionValue=nb;n.snapshotViewportBox=Qd;n.transform=eg;n.useAnimation=xg;n.useCycle=yg;n.useDeprecatedAnimatedState=Eg;n.useDeprecatedInvertedScale=Ig;n.useDomEvent=ub;n.useDragControls=Bg;n.useElementScroll=ng;n.useIsPresent=F;n.useMotionTemplate=bg;n.useMotionValue=Yf;n.usePresence=E;n.useReducedMotion=vg;n.useSpring=hg;n.useTransform=fg;n.useVelocity=ig;n.useViewportScroll=sg;n.visualElement=ye}var q=!1;function r(){q||(q=!0,p());return o.exports}function a(a){switch(a){case void 0:return r()}}e.exports=a}),null);
-----
three.r155",[],(function $module_three_r155(global,require,requireDynamic,requireLazy,module,exports){ "use strict" console.warn( "Scripts "build/three.js" and "build/three.min.js" are deprecated with r150+, and will be removed with r160. Please use ES Modules or alternatives: https://threejs.org/docs/index.html#manual/en/introduction/Installation" ); /** * @license * Copyright 2010-2023 Three.js Authors * SPDX-License-Identifier: MIT */ (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "155"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const TwoPassDoubleSide = 2; // r149 const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; /** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */ const LinearEncoding = 3000; /** @deprecated Use SRGBColorSpace in three.js r152+. */ const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ""; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const DisplayP3ColorSpace = "display-p3"; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const NeverCompare = 512; const LessCompare = 513; const EqualCompare = 514; const LessEqualCompare = 515; const GreaterCompare = 516; const NotEqualCompare = 517; const GreaterEqualCompare = 518; const AlwaysCompare = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 const WebGLCoordinateSystem = 2000; const WebGPUCoordinateSystem = 2001; /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; const listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } } hasEventListener( type, listener ) { if ( this._listeners === undefined ) return false; const listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; } removeEventListener( type, listener ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { const index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } } dispatchEvent( event ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice( 0 ); for ( let i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" ]; let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + "-" + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + "-" + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + "-" + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + "-" + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp( value, min, max ) { return Math.max( min, Math.min( max, value ) ); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { return ( ( n % m ) + m ) % m; } // Linear mapping from range to range function mapLinear( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp( x, y, value ) { if ( x !== y ) { return ( value - x ) / ( y - x ); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp( x, y, t ) { return ( 1 - t ) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp( x, y, lambda, dt ) { return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong( x, length = 1 ) { return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); } function smootherstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); } // Random integer from interval function randInt( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); } // Random float from interval function randFloat( low, high ) { return low + Math.random() * ( high - low ); } // Random float from <-range/2, range/2> interval function randFloatSpread( range ) { return range * ( 0.5 - Math.random() ); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { if ( s !== undefined ) _seed = s; // Mulberry32 generator let t = _seed += 0x6D2B79F5; t = Math.imul( t ^ t >>> 15, t | 1 ); t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } function degToRad( degrees ) { return degrees * DEG2RAD; } function radToDeg( radians ) { return radians * RAD2DEG; } function isPowerOfTwo( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; } function ceilPowerOfTwo( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); } function floorPowerOfTwo( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } function setQuaternionFromProperEuler( q, a, b, c, order ) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos( b / 2 ); const s2 = sin( b / 2 ); const c13 = cos( ( a + c ) / 2 ); const s13 = sin( ( a + c ) / 2 ); const c1_3 = cos( ( a - c ) / 2 ); const s1_3 = sin( ( a - c ) / 2 ); const c3_1 = cos( ( c - a ) / 2 ); const s3_1 = sin( ( c - a ) / 2 ); switch ( order ) { case "XYX": q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); break; case "YZY": q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); break; case "ZXZ": q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); break; case "XZX": q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); break; case "YXY": q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); break; case "ZYZ": q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order ); } } function denormalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint32Array: return value / 4294967295.0; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int32Array: return Math.max( value / 2147483647.0, - 1.0 ); case Int16Array: return Math.max( value / 32767.0, - 1.0 ); case Int8Array: return Math.max( value / 127.0, - 1.0 ); default: throw new Error( "Invalid component type." ); } } function normalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint32Array: return Math.round( value * 4294967295.0 ); case Uint16Array: return Math.round( value * 65535.0 ); case Uint8Array: return Math.round( value * 255.0 ); case Int32Array: return Math.round( value * 2147483647.0 ); case Int16Array: return Math.round( value * 32767.0 ); case Int8Array: return Math.round( value * 127.0 ); default: throw new Error( "Invalid component type." ); } } const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize }; class Vector2 { constructor( x = 0, y = 0 ) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width( value ) { this.x = value; } get height() { return this.y; } set height( value ) { this.y = value; } set( x, y ) { this.x = x; this.y = y; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y ); } copy( v ) { this.x = v.x; this.y = v.y; return this; } add( v ) { this.x += v.x; this.y += v.y; return this; } addScalar( s ) { this.x += s; this.y += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; return this; } subScalar( s ) { this.x -= s; this.y -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; return this; } divide( v ) { this.x /= v.x; this.y /= v.y; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } applyMatrix3( m ) { const x = this.x, y = this.y; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; } negate() { this.x = - this.x; this.y = - this.y; return this; } dot( v ) { return this.x * v.x + this.y * v.y; } cross( v ) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt( this.x * this.x + this.y * this.y ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ); } normalize() { return this.divideScalar( this.length() || 1 ); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2( - this.y, - this.x ) + Math.PI; return angle; } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; } rotateAround( center, angle ) { const c = Math.cos( angle ), s = Math.sin( angle ); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; } } class Matrix3 { constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { Matrix3.prototype.isMatrix3 = true; this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( n11 !== undefined ) { this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ); } } set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { const te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; } identity() { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrix3Column( this, 0 ); yAxis.setFromMatrix3Column( this, 1 ); zAxis.setFromMatrix3Column( this, 2 ); return this; } setFromMatrix4( m ) { const me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; } determinant() { const te = this.elements; const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; } getNormalMatrix( matrix4 ) { return this.setFromMatrix4( matrix4 ).invert().transpose(); } transposeIntoArray( r ) { const m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; } setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { const c = Math.cos( rotation ); const s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); return this; } // scale( sx, sy ) { this.premultiply( _m3.makeScale( sx, sy ) ); return this; } rotate( theta ) { this.premultiply( _m3.makeRotation( - theta ) ); return this; } translate( tx, ty ) { this.premultiply( _m3.makeTranslation( tx, ty ) ); return this; } // for 2D Transforms makeTranslation( x, y ) { if ( x.isVector2 ) { this.set( 1, 0, x.x, 0, 1, x.y, 0, 0, 1 ); } else { this.set( 1, 0, x, 0, 1, y, 0, 0, 1 ); } return this; } makeRotation( theta ) { // counterclockwise const c = Math.cos( theta ); const s = Math.sin( theta ); this.set( c, - s, 0, s, c, 0, 0, 0, 1 ); return this; } makeScale( x, y ) { this.set( x, 0, 0, 0, y, 0, 0, 0, 1 ); return this; } // equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } clone() { return new this.constructor().fromArray( this.elements ); } } const _m3 = /*@__PURE__*/ new Matrix3(); function arrayNeedsUint32( array ) { // assumes larger values usually on last for ( let i = array.length - 1; i >= 0; -- i ) { if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray( type, buffer ) { return new TYPED_ARRAYS[ type ]( buffer ); } function createElementNS( name ) { return document.createElementNS( "http://www.w3.org/1999/xhtml", name ); } const _cache = {}; function warnOnce( message ) { if ( message in _cache ) return; _cache[ message ] = true; console.warn( message ); } function SRGBToLinear( c ) { return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); } function LinearToSRGB( c ) { return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().fromArray( [ 0.8224621, 0.0331941, 0.0170827, 0.1775380, 0.9668058, 0.0723974, - 0.0000001, 0.0000001, 0.9105199 ] ); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().fromArray( [ 1.2249401, - 0.0420569, - 0.0196376, - 0.2249404, 1.0420571, - 0.0786361, 0.0000001, 0.0000000, 1.0982735 ] ); function DisplayP3ToLinearSRGB( color ) { // Display P3 uses the sRGB transfer functions return color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ); } function LinearSRGBToDisplayP3( color ) { // Display P3 uses the sRGB transfer functions return color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(); } // Conversions from to Linear-sRGB reference space. const TO_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertSRGBToLinear(), [ DisplayP3ColorSpace ]: DisplayP3ToLinearSRGB, }; // Conversions to from Linear-sRGB reference space. const FROM_LINEAR = { [ LinearSRGBColorSpace ]: ( color ) => color, [ SRGBColorSpace ]: ( color ) => color.convertLinearToSRGB(), [ DisplayP3ColorSpace ]: LinearSRGBToDisplayP3, }; const ColorManagement = { enabled: true, get legacyMode() { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); return ! this.enabled; }, set legacyMode( legacyMode ) { console.warn( "THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150." ); this.enabled = ! legacyMode; }, get workingColorSpace() { return LinearSRGBColorSpace; }, set workingColorSpace( colorSpace ) { console.warn( "THREE.ColorManagement: .workingColorSpace is readonly." ); }, convert: function ( color, sourceColorSpace, targetColorSpace ) { if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { return color; } const sourceToLinear = TO_LINEAR[ sourceColorSpace ]; const targetFromLinear = FROM_LINEAR[ targetColorSpace ]; if ( sourceToLinear === undefined || targetFromLinear === undefined ) { throw new Error( `Unsupported color space conversion, "${ sourceColorSpace }" to "${ targetColorSpace }".` ); } return targetFromLinear( sourceToLinear( color ) ); }, fromWorkingColorSpace: function ( color, targetColorSpace ) { return this.convert( color, this.workingColorSpace, targetColorSpace ); }, toWorkingColorSpace: function ( color, sourceColorSpace ) { return this.convert( color, sourceColorSpace, this.workingColorSpace ); }, }; let _canvas; class ImageUtils { static getDataURL( image ) { if ( /^data:/i.test( image.src ) ) { return image.src; } if ( typeof HTMLCanvasElement === "undefined" ) { return image.src; } let canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { if ( _canvas === undefined ) _canvas = createElementNS( "canvas" ); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } canvas = _canvas; } if ( canvas.width > 2048 || canvas.height > 2048 ) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image ); return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } static sRGBToLinear( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const canvas = createElementNS( "canvas" ); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height ); const imageData = context.getImageData( 0, 0, image.width, image.height ); const data = imageData.data; for ( let i = 0; i < data.length; i ++ ) { data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; } context.putImageData( imageData, 0, 0 ); return canvas; } else if ( image.data ) { const data = image.data.slice( 0 ); for ( let i = 0; i < data.length; i ++ ) { if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); } else { // assuming float data[ i ] = SRGBToLinear( data[ i ] ); } } return { data: data, width: image.width, height: image.height }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied." ); return image; } } } let sourceId = 0; class Source { constructor( data = null ) { this.isSource = true; Object.defineProperty( this, "id", { value: sourceId ++ } ); this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { return meta.images[ this.uuid ]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if ( data !== null ) { let url; if ( Array.isArray( data ) ) { // cube texture url = []; for ( let i = 0, l = data.length; i < l; i ++ ) { if ( data[ i ].isDataTexture ) { url.push( serializeImage( data[ i ].image ) ); } else { url.push( serializeImage( data[ i ] ) ); } } } else { // texture url = serializeImage( data ); } output.url = url; } if ( ! isRootObject ) { meta.images[ this.uuid ] = output; } return output; } } function serializeImage( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { // default images return ImageUtils.getDataURL( image ); } else { if ( image.data ) { // images of DataTexture return { data: Array.from( image.data ), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn( "THREE.Texture: Unable to serialize Texture." ); return {}; } } } let textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { super(); this.isTexture = true; Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = generateUUID(); this.name = ""; this.source = new Source( image ); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) if ( typeof colorSpace === "string" ) { this.colorSpace = colorSpace; } else { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image( value = null ) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.needsUpdate = true; return this; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } const output = { metadata: { version: 4.6, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON( meta ).uuid, mapping: this.mapping, channel: this.channel, 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, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment }; if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; } dispose() { this.dispatchEvent( { type: "dispose" } ); } transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } return uv; } set needsUpdate( value ) { if ( value === true ) { this.version ++; this.source.needsUpdate = true; } } get encoding() { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set encoding( encoding ) { // @deprecated, r152 warnOnce( "THREE.Texture: Property .encoding has been replaced by .colorSpace." ); this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor( x = 0, y = 0, z = 0, w = 1 ) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width( value ) { this.z = value; } get height() { return this.w; } set height( value ) { this.w = value; } set( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setW( w ) { this.w = w; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z, this.w ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } setAxisAngleFromQuaternion( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); const s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = ( m11 + 1 ) / 2; const yy = ( m22 + 1 ) / 2; const zz = ( m33 + 1 ) / 2; const xy = ( m12 + m21 ) / 4; const xz = ( m13 + m31 ) / 4; const yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class RenderTarget extends EventDispatcher { constructor( width = 1, height = 1, options = {} ) { super(); this.isRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); const image = { width: width, height: height, depth: 1 }; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); this.texture.isRenderTargetTexture = true; this.texture.flipY = false; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy( source.scissor ); this.scissorTest = source.scissorTest; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.texture.isRenderTargetTexture = true; // ensure image object is not shared, see #20328 const image = Object.assign( {}, source.texture.image ); this.texture.source = new Source( image ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class WebGLRenderTarget extends RenderTarget { constructor( width = 1, height = 1, options = {} ) { super( width, height, options ); this.isWebGLRenderTarget = true; } } class DataArrayTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { super( null ); this.isDataArrayTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor( width = 1, height = 1, depth = 1 ) { super( width, height ); this.isWebGLArrayRenderTarget = true; this.depth = depth; this.texture = new DataArrayTexture( null, width, height, depth ); this.texture.isRenderTargetTexture = true; } } class Data3DTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super( null ); this.isData3DTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGL3DRenderTarget extends WebGLRenderTarget { constructor( width = 1, height = 1, depth = 1 ) { super( width, height ); this.isWebGL3DRenderTarget = true; this.depth = depth; this.texture = new Data3DTexture( null, width, height, depth ); this.texture.isRenderTargetTexture = true; } } class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor( width = 1, height = 1, count = 1, options = {} ) { super( width, height, options ); this.isWebGLMultipleRenderTargets = true; const texture = this.texture; this.texture = []; for ( let i = 0; i < count; i ++ ) { this.texture[ i ] = texture.clone(); this.texture[ i ].isRenderTargetTexture = true; } } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; for ( let i = 0, il = this.texture.length; i < il; i ++ ) { this.texture[ i ].image.width = width; this.texture[ i ].image.height = height; this.texture[ i ].image.depth = depth; } this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } copy( source ) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy( source.scissor ); this.scissorTest = source.scissorTest; this.viewport.copy( source.viewport ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.texture.length = 0; for ( let i = 0, il = source.texture.length; i < il; i ++ ) { this.texture[ i ] = source.texture[ i ].clone(); this.texture[ i ].isRenderTargetTexture = true; } return this; } } class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( t === 0 ) { dst[ dstOffset + 0 ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; return; } if ( t === 1 ) { dst[ dstOffset + 0 ] = x1; dst[ dstOffset + 1 ] = y1; dst[ dstOffset + 2 ] = z1; dst[ dstOffset + 3 ] = w1; return; } if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { const sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { const x0 = src0[ srcOffset0 ]; const y0 = src0[ srcOffset0 + 1 ]; const z0 = src0[ srcOffset0 + 2 ]; const w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 ]; const y1 = src1[ srcOffset1 + 1 ]; const z1 = src1[ srcOffset1 + 2 ]; const w1 = src1[ srcOffset1 + 3 ]; dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w( value ) { this._w = value; this._onChangeCallback(); } set( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._w ); } copy( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler( euler, update ) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos( x / 2 ); const c2 = cos( y / 2 ); const c3 = cos( z / 2 ); const s1 = sin( x / 2 ); const s2 = sin( y / 2 ); const s3 = sin( z / 2 ); switch ( order ) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order ); } if ( update !== false ) this._onChangeCallback(); return this; } setFromAxisAngle( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this._onChangeCallback(); return this; } setFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33; if ( trace > 0 ) { const s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors( vFrom, vTo ) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot( vTo ) + 1; if ( r < Number.EPSILON ) { // vFrom and vTo point in opposite directions r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { this._x = - vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = - vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo( q ) { return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); } rotateTowards( q, step ) { const angle = this.angleTo( q ); if ( angle === 0 ) return this; const t = Math.min( 1, step / angle ); this.slerp( q, t ); return this; } identity() { return this.set( 0, 0, 0, 1 ); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= - 1; this._y *= - 1; this._z *= - 1; this._onChangeCallback(); return this; } dot( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } normalize() { let l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply( q ) { return this.multiplyQuaternions( this, q ); } premultiply( q ) { return this.multiplyQuaternions( q, this ); } multiplyQuaternions( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if ( sqrSinHalfTheta <= Number.EPSILON ) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this._onChangeCallback(); return this; } slerpQuaternions( qa, qb, t ) { return this.copy( qa ).slerp( qb, t ); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt( 1 - u1 ); const sqrtu1 = Math.sqrt( u1 ); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set( sqrt1u1 * Math.cos( u2 ), sqrtu1 * Math.sin( u3 ), sqrtu1 * Math.cos( u3 ), sqrt1u1 * Math.sin( u2 ), ); } equals( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } fromArray( array, offset = 0 ) { this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; } fromBufferAttribute( attribute, index ) { this._x = attribute.getX( index ); this._y = attribute.getY( index ); this._z = attribute.getZ( index ); this._w = attribute.getW( index ); return this; } toJSON() { return this.toArray(); } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor( x = 0, y = 0, z = 0 ) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set( x, y, z ) { if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add( v ) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler( euler ) { return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } applyAxisAngle( axis, angle ) { return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); } applyMatrix3( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; } applyNormalMatrix( m ) { return this.applyMatrix3( m ).normalize(); } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; } applyQuaternion( q ) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; } project( camera ) { return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } unproject( camera ) { return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); } transformDirection( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); } divide( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; return this; } cross( v ) { return this.crossVectors( this, v ); } crossVectors( a, b ) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector( v ) { const denominator = v.lengthSq(); if ( denominator === 0 ) return this.set( 0, 0, 0 ); const scalar = v.dot( this ) / denominator; return this.copy( v ).multiplyScalar( scalar ); } projectOnPlane( planeNormal ) { _vector$b.copy( this ).projectOnVector( planeNormal ); return this.sub( _vector$b ); } reflect( normal ) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$b.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); } setFromSpherical( s ) { return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } setFromSphericalCoords( radius, phi, theta ) { const sinPhiRadius = Math.sin( phi ) * radius; this.x = sinPhiRadius * Math.sin( theta ); this.y = Math.cos( phi ) * radius; this.z = sinPhiRadius * Math.cos( theta ); return this; } setFromCylindrical( c ) { return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); } setFromCylindricalCoords( radius, theta, y ) { this.x = radius * Math.sin( theta ); this.y = y; this.z = radius * Math.cos( theta ); return this; } setFromMatrixPosition( m ) { const e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; } setFromMatrixScale( m ) { const sx = this.setFromMatrixColumn( m, 0 ).length(); const sy = this.setFromMatrixColumn( m, 1 ).length(); const sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn( m, index ) { return this.fromArray( m.elements, index * 4 ); } setFromMatrix3Column( m, index ) { return this.fromArray( m.elements, index * 3 ); } setFromEuler( e ) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor( c ) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; } fromBufferAttribute( attribute, index ) { this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = ( Math.random() - 0.5 ) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt( 1 - u ** 2 ); this.x = f * Math.cos( t ); this.y = f * Math.sin( t ); this.z = u; return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; } } const _vector$b = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { this.isBox3 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromArray( array ) { this.makeEmpty(); for ( let i = 0, il = array.length; i < il; i += 3 ) { this.expandByPoint( _vector$a.fromArray( array, i ) ); } return this; } setFromBufferAttribute( attribute ) { this.makeEmpty(); for ( let i = 0, il = attribute.count; i < il; i ++ ) { this.expandByPoint( _vector$a.fromBufferAttribute( attribute, i ) ); } return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$a.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } setFromObject( object, precise = false ) { this.makeEmpty(); return this.expandByObject( object, precise ); } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } expandByObject( object, precise = false ) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix( false, false ); if ( object.boundingBox !== undefined ) { if ( object.boundingBox === null ) { object.computeBoundingBox(); } _box$3.copy( object.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } else { const geometry = object.geometry; if ( geometry !== undefined ) { if ( precise && geometry.attributes !== undefined && geometry.attributes.position !== undefined ) { const position = geometry.attributes.position; for ( let i = 0, l = position.count; i < l; i ++ ) { _vector$a.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); this.expandByPoint( _vector$a ); } } else { if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } _box$3.copy( geometry.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { this.expandByObject( children[ i ], precise ); } return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); } intersectsBox( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, _vector$a ); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$a.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); } intersectsPlane( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= - plane.constant && max >= - plane.constant ); } intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( _center ); _extents.subVectors( this.max, _center ); // translate triangle to aabb origin _v0$2.subVectors( triangle.a, _center ); _v1$7.subVectors( triangle.b, _center ); _v2$4.subVectors( triangle.c, _center ); // compute edge vectors for triangle _f0.subVectors( _v1$7, _v0$2 ); _f1.subVectors( _v2$4, _v1$7 ); _f2.subVectors( _v0$2, _v2$4 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors( _f0, _f1 ); axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; return satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ); } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { return this.clampPoint( point, _vector$a ).distanceTo( point ); } getBoundingSphere( target ) { if ( this.isEmpty() ) { target.makeEmpty(); } else { this.getCenter( target.center ); target.radius = this.getSize( _vector$a ).length() * 0.5; } return target; } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } applyMatrix4( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( _points ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3() ]; const _vector$a = /*@__PURE__*/ new Vector3(); const _box$3 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$4 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes( axes, v0, v1, v2, extents ) { for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { _testAxis.fromArray( axes, i ); // project the aabb onto the separating axis const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot( _testAxis ); const p1 = v1.dot( _testAxis ); const p2 = v2.dot( _testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); class Sphere { constructor( center = new Vector3(), radius = - 1 ) { this.center = center; this.radius = radius; } set( center, radius ) { this.center.copy( center ); this.radius = radius; return this; } setFromPoints( points, optionalCenter ) { const center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { _box$2.setFromPoints( points ).getCenter( center ); } let maxRadiusSq = 0; for ( let i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; } copy( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; } isEmpty() { return ( this.radius < 0 ); } makeEmpty() { this.center.set( 0, 0, 0 ); this.radius = - 1; return this; } containsPoint( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); } distanceToPoint( point ) { return ( point.distanceTo( this.center ) - this.radius ); } intersectsSphere( sphere ) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } intersectsBox( box ) { return box.intersectsSphere( this ); } intersectsPlane( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } clampPoint( point, target ) { const deltaLengthSq = this.center.distanceToSquared( point ); target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; } getBoundingBox( target ) { if ( this.isEmpty() ) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; } applyMatrix4( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate( offset ) { this.center.add( offset ); return this; } expandByPoint( point ) { if ( this.isEmpty() ) { this.center.copy( point ); this.radius = 0; return this; } _v1$6.subVectors( point, this.center ); const lengthSq = _v1$6.lengthSq(); if ( lengthSq > ( this.radius * this.radius ) ) { // calculate the minimal sphere const length = Math.sqrt( lengthSq ); const delta = ( length - this.radius ) * 0.5; this.center.addScaledVector( _v1$6, delta / length ); this.radius += delta; } return this; } union( sphere ) { if ( sphere.isEmpty() ) { return this; } if ( this.isEmpty() ) { this.copy( sphere ); return this; } if ( this.center.equals( sphere.center ) === true ) { this.radius = Math.max( this.radius, sphere.radius ); } else { _v2$3.subVectors( sphere.center, this.center ).setLength( sphere.radius ); this.expandByPoint( _v1$6.copy( sphere.center ).add( _v2$3 ) ); this.expandByPoint( _v1$6.copy( sphere.center ).sub( _v2$3 ) ); } return this; } equals( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } clone() { return new this.constructor().copy( this ); } } const _vector$9 = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { this.origin = origin; this.direction = direction; } set( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; } copy( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; } at( t, target ) { return target.copy( this.origin ).addScaledVector( this.direction, t ); } lookAt( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; } recast( t ) { this.origin.copy( this.at( t, _vector$9 ) ); return this; } closestPointToPoint( point, target ) { target.subVectors( point, this.origin ); const directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.origin ).addScaledVector( this.direction, directionDistance ); } distanceToPoint( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); } distanceSqToPoint( point ) { const directionDistance = _vector$9.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } _vector$9.copy( this.origin ).addScaledVector( this.direction, directionDistance ); return _vector$9.distanceToSquared( point ); } distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); _segDir.copy( v1 ).sub( v0 ).normalize(); _diff.copy( this.origin ).sub( _segCenter ); const segExtent = v0.distanceTo( v1 ) * 0.5; const a01 = - this.direction.dot( _segDir ); const b0 = _diff.dot( this.direction ); const b1 = - _diff.dot( _segDir ); const c = _diff.lengthSq(); const det = Math.abs( 1 - a01 * a01 ); let s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 ); } return sqrDist; } intersectSphere( sphere, target ) { _vector$9.subVectors( sphere.center, this.origin ); const tca = _vector$9.dot( this.direction ); const d2 = _vector$9.dot( _vector$9 ) - tca * tca; const radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; const thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if ( t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); } intersectsSphere( sphere ) { return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); } distanceToPlane( plane ) { const denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane( plane, target ) { const t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); } intersectsPlane( plane ) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } const denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox( box, target ) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin; if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); } intersectsBox( box ) { return this.intersectBox( box, _vector$9 ) !== null; } intersectTriangle( a, b, c, backfaceCulling, target ) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors( b, a ); _edge2.subVectors( c, a ); _normal$1.crossVectors( _edge1, _edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot( _normal$1 ); let sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } _diff.subVectors( this.origin, a ); const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. const QdN = - sign * _diff.dot( _normal$1 ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); } applyMatrix4( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; } equals( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } clone() { return new this.constructor().copy( this ); } } class Matrix4 { constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { Matrix4.prototype.isMatrix4 = true; this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( n11 !== undefined ) { this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ); } } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { const te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; } identity() { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } clone() { return new Matrix4().fromArray( this.elements ); } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; } copyPosition( m ) { const te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; } setFromMatrix3( m ) { const me = m.elements; this.set( me[ 0 ], me[ 3 ], me[ 6 ], 0, me[ 1 ], me[ 4 ], me[ 7 ], 0, me[ 2 ], me[ 5 ], me[ 8 ], 0, 0, 0, 0, 1 ); return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; } makeBasis( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; } extractRotation( m ) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromEuler( euler ) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos( x ), b = Math.sin( x ); const c = Math.cos( y ), d = Math.sin( y ); const e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromQuaternion( q ) { return this.compose( _zero, q, _one ); } lookAt( eye, target, up ) { const te = this.elements; _z.subVectors( eye, target ); if ( _z.lengthSq() === 0 ) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors( up, _z ); if ( _x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors( up, _z ); } _x.normalize(); _y.crossVectors( _z, _x ); te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; } determinant() { const te = this.elements; const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); } transpose() { const te = this.elements; let tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; } setPosition( x, y, z ) { const te = this.elements; if ( x.isVector3 ) { te[ 12 ] = x.x; te[ 13 ] = x.y; te[ 14 ] = x.z; } else { te[ 12 ] = x; te[ 13 ] = y; te[ 14 ] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; } scale( v ) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); } makeTranslation( x, y, z ) { if ( x.isVector3 ) { this.set( 1, 0, 0, x.x, 0, 1, 0, x.y, 0, 0, 1, x.z, 0, 0, 0, 1 ); } else { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); } return this; } makeRotationX( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; } makeRotationY( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; } makeRotationZ( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } makeRotationAxis( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos( angle ); const s = Math.sin( angle ); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; } makeScale( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; } makeShear( xy, xz, yx, yz, zx, zy ) { this.set( 1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1 ); return this; } compose( position, quaternion, scale ) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; } decompose( position, quaternion, scale ) { const te = this.elements; let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part _m1$2.copy( this ); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[ 0 ] *= invSX; _m1$2.elements[ 1 ] *= invSX; _m1$2.elements[ 2 ] *= invSX; _m1$2.elements[ 4 ] *= invSY; _m1$2.elements[ 5 ] *= invSY; _m1$2.elements[ 6 ] *= invSY; _m1$2.elements[ 8 ] *= invSZ; _m1$2.elements[ 9 ] *= invSZ; _m1$2.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( _m1$2 ); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { const te = this.elements; const x = 2 * near / ( right - left ); const y = 2 * near / ( top - bottom ); const a = ( right + left ) / ( right - left ); const b = ( top + bottom ) / ( top - bottom ); let c, d; if ( coordinateSystem === WebGLCoordinateSystem ) { c = - ( far + near ) / ( far - near ); d = ( - 2 * far * near ) / ( far - near ); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { c = - far / ( far - near ); d = ( - far * near ) / ( far - near ); } else { throw new Error( "THREE.Matrix4.makePerspective(): Invalid coordinate system: " + coordinateSystem ); } te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { const te = this.elements; const w = 1.0 / ( right - left ); const h = 1.0 / ( top - bottom ); const p = 1.0 / ( far - near ); const x = ( right + left ) * w; const y = ( top + bottom ) * h; let z, zInv; if ( coordinateSystem === WebGLCoordinateSystem ) { z = ( far + near ) * p; zInv = - 2 * p; } else if ( coordinateSystem === WebGPUCoordinateSystem ) { z = near * p; zInv = - 1 * p; } else { throw new Error( "THREE.Matrix4.makeOrthographic(): Invalid coordinate system: " + coordinateSystem ); } te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = zInv; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; } equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$2 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order( value ) { this._order = value; this._onChangeCallback(); } set( x, y, z, order = this._order ) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._order ); } copy( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix( m, order = this._order, update = true ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; switch ( order ) { case "XYZ": this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } break; case "YXZ": this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.9999999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } break; case "ZXY": this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.9999999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } break; case "ZYX": this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.9999999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } break; case "YZX": this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } break; case "XZY": this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.9999999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order ); } this._order = order; if ( update === true ) this._onChangeCallback(); return this; } setFromQuaternion( q, order, update ) { _matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( _matrix, order, update ); } setFromVector3( v, order = this._order ) { return this.set( v.x, v.y, v.z, order ); } reorder( newOrder ) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler( this ); return this.setFromQuaternion( _quaternion$3, newOrder ); } equals( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); } fromArray( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = "XYZ"; class Layers { constructor() { this.mask = 1 | 0; } set( channel ) { this.mask = ( 1 << channel | 0 ) >>> 0; } enable( channel ) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle( channel ) { this.mask ^= 1 << channel | 0; } disable( channel ) { this.mask &= ~ ( 1 << channel | 0 ); } disableAll() { this.mask = 0; } test( layers ) { return ( this.mask & layers.mask ) !== 0; } isEnabled( channel ) { return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$1 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty( this, "id", { value: _object3DId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation._onChange( onRotationChange ); quaternion._onChange( onQuaternionChange ); Object.defineProperties( this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldNeedsUpdate = false; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} applyMatrix4( matrix ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); this.matrix.premultiply( matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); } applyQuaternion( q ) { this.quaternion.premultiply( q ); return this; } setRotationFromAxisAngle( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); } setRotationFromEuler( euler ) { this.quaternion.setFromEuler( euler, true ); } setRotationFromMatrix( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); } setRotationFromQuaternion( q ) { // assumes q is normalized this.quaternion.copy( q ); } rotateOnAxis( axis, angle ) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( _q1 ); return this; } rotateOnWorldAxis( axis, angle ) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( _q1 ); return this; } rotateX( angle ) { return this.rotateOnAxis( _xAxis, angle ); } rotateY( angle ) { return this.rotateOnAxis( _yAxis, angle ); } rotateZ( angle ) { return this.rotateOnAxis( _zAxis, angle ); } translateOnAxis( axis, distance ) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( _v1$4.multiplyScalar( distance ) ); return this; } translateX( distance ) { return this.translateOnAxis( _xAxis, distance ); } translateY( distance ) { return this.translateOnAxis( _yAxis, distance ); } translateZ( distance ) { return this.translateOnAxis( _zAxis, distance ); } localToWorld( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( this.matrixWorld ); } worldToLocal( vector ) { this.updateWorldMatrix( true, false ); return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } lookAt( x, y, z ) { // This method does not support objects having non-uniformly-scaled parent(s) if ( x.isVector3 ) { _target.copy( x ); } else { _target.set( x, y, z ); } const parent = this.parent; this.updateWorldMatrix( true, false ); _position$3.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { _m1$1.lookAt( _position$3, _target, this.up ); } else { _m1$1.lookAt( _target, _position$3, this.up ); } this.quaternion.setFromRotationMatrix( _m1$1 ); if ( parent ) { _m1$1.extractRotation( parent.matrixWorld ); _q1.setFromRotationMatrix( _m1$1 ); this.quaternion.premultiply( _q1.invert() ); } } add( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( object && object.isObject3D ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; this.children.push( object ); object.dispatchEvent( _addedEvent ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; } remove( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } const index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; this.children.splice( index, 1 ); object.dispatchEvent( _removedEvent ); } return this; } removeFromParent() { const parent = this.parent; if ( parent !== null ) { parent.remove( this ); } return this; } clear() { for ( let i = 0; i < this.children.length; i ++ ) { const object = this.children[ i ]; object.parent = null; object.dispatchEvent( _removedEvent ); } this.children.length = 0; return this; } attach( object ) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix( true, false ); _m1$1.copy( this.matrixWorld ).invert(); if ( object.parent !== null ) { object.parent.updateWorldMatrix( true, false ); _m1$1.multiply( object.parent.matrixWorld ); } object.applyMatrix4( _m1$1 ); this.add( object ); object.updateWorldMatrix( false, true ); return this; } getObjectById( id ) { return this.getObjectByProperty( "id", id ); } getObjectByName( name ) { return this.getObjectByProperty( "name", name ); } getObjectByProperty( name, value ) { if ( this[ name ] === value ) return this; for ( let i = 0, l = this.children.length; i < l; i ++ ) { const child = this.children[ i ]; const object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; } getObjectsByProperty( name, value ) { let result = []; if ( this[ name ] === value ) result.push( this ); for ( let i = 0, l = this.children.length; i < l; i ++ ) { const childResult = this.children[ i ].getObjectsByProperty( name, value ); if ( childResult.length > 0 ) { result = result.concat( childResult ); } } return result; } getWorldPosition( target ) { this.updateWorldMatrix( true, false ); return target.setFromMatrixPosition( this.matrixWorld ); } getWorldQuaternion( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, target, _scale$2 ); return target; } getWorldScale( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, _quaternion$2, target ); return target; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); } raycast( /* raycaster, intersects */ ) {} traverse( callback ) { callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } } traverseVisible( callback ) { if ( this.visible === false ) return; callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } } traverseAncestors( callback ) { const parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } } updateMatrix() { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true || force === true ) { child.updateMatrixWorld( force ); } } } updateWorldMatrix( updateParents, updateChildren ) { const parent = this.parent; if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { parent.updateWorldMatrix( true, false ); } if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } // update children if ( updateChildren === true ) { const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { const child = children[ i ]; if ( child.matrixWorldAutoUpdate === true ) { child.updateWorldMatrix( false, true ); } } } } toJSON( meta ) { // meta is a string when called from JSON.stringify const isRootObject = ( meta === undefined || typeof meta === "string" ); const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.6, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // object specific properties if ( this.isInstancedMesh ) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); } // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.isScene ) { if ( this.background ) { if ( this.background.isColor ) { object.background = this.background.toJSON(); } else if ( this.background.isTexture ) { object.background = this.background.toJSON( meta ).uuid; } } if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { object.environment = this.environment.toJSON( meta ).uuid; } } else if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); const parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { const shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.isSkinnedMesh ) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if ( this.skeleton !== undefined ) { serialize( meta.skeletons, this.skeleton ); object.skeleton = this.skeleton.uuid; } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { const uuids = []; for ( let i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( let i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } // if ( this.animations.length > 0 ) { object.animations = []; for ( let i = 0; i < this.animations.length; i ++ ) { const animation = this.animations[ i ]; object.animations.push( serialize( meta.animations, animation ) ); } } if ( isRootObject ) { const geometries = extractFromCache( meta.geometries ); const materials = extractFromCache( meta.materials ); const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); const shapes = extractFromCache( meta.shapes ); const skeletons = extractFromCache( meta.skeletons ); const animations = extractFromCache( meta.animations ); const nodes = extractFromCache( meta.nodes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; if ( skeletons.length > 0 ) output.skeletons = skeletons; if ( animations.length > 0 ) output.animations = animations; if ( nodes.length > 0 ) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } } clone( recursive ) { return new this.constructor().copy( this, recursive ); } copy( source, recursive = true ) { this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.rotation.order = source.rotation.order; this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations.slice(); this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( let i = 0; i < source.children.length; i ++ ) { const child = source.children[ i ]; this.add( child.clone() ); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 ); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$1 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); let warnedGetUV = false; class Triangle { constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { this.a = a; this.b = b; this.c = c; } static getNormal( a, b, c, target ) { target.subVectors( c, b ); _v0$1.subVectors( a, b ); target.cross( _v0$1 ); const targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord( point, a, b, c, target ) { _v0$1.subVectors( c, a ); _v1$3.subVectors( b, a ); _v2$2.subVectors( point, a ); const dot00 = _v0$1.dot( _v0$1 ); const dot01 = _v0$1.dot( _v1$3 ); const dot02 = _v0$1.dot( _v2$2 ); const dot11 = _v1$3.dot( _v1$3 ); const dot12 = _v1$3.dot( _v2$2 ); const denom = ( dot00 * dot11 - dot01 * dot01 ); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } const invDenom = 1 / denom; const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); } static containsPoint( point, a, b, c ) { this.getBarycoord( point, a, b, c, _v3$1 ); return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target ); } static getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) { this.getBarycoord( point, p1, p2, p3, _v3$1 ); target.setScalar( 0 ); target.addScaledVector( v1, _v3$1.x ); target.addScaledVector( v2, _v3$1.y ); target.addScaledVector( v3, _v3$1.z ); return target; } static isFrontFacing( a, b, c, direction ) { _v0$1.subVectors( c, b ); _v1$3.subVectors( a, b ); // strictly front facing return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; } set( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; } setFromPointsAndIndices( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; } setFromAttributeAndIndices( attribute, i0, i1, i2 ) { this.a.fromBufferAttribute( attribute, i0 ); this.b.fromBufferAttribute( attribute, i1 ); this.c.fromBufferAttribute( attribute, i2 ); return this; } clone() { return new this.constructor().copy( this ); } copy( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; } getArea() { _v0$1.subVectors( this.c, this.b ); _v1$3.subVectors( this.a, this.b ); return _v0$1.cross( _v1$3 ).length() * 0.5; } getMidpoint( target ) { return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } getNormal( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); } getPlane( target ) { return target.setFromCoplanarPoints( this.a, this.b, this.c ); } getBarycoord( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); } getUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151 if ( warnedGetUV === false ) { console.warn( "THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation()." ); warnedGetUV = true; } return Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } getInterpolation( point, v1, v2, v3, target ) { return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target ); } containsPoint( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); } isFrontFacing( direction ) { return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); } intersectsBox( box ) { return box.intersectsTriangle( this ); } closestPointToPoint( p, target ) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors( b, a ); _vac.subVectors( c, a ); _vap.subVectors( p, a ); const d1 = _vab.dot( _vap ); const d2 = _vac.dot( _vap ); if ( d1 <= 0 && d2 <= 0 ) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy( a ); } _vbp.subVectors( p, b ); const d3 = _vab.dot( _vbp ); const d4 = _vac.dot( _vbp ); if ( d3 >= 0 && d4 <= d3 ) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy( b ); } const vc = d1 * d4 - d3 * d2; if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { v = d1 / ( d1 - d3 ); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy( a ).addScaledVector( _vab, v ); } _vcp.subVectors( p, c ); const d5 = _vab.dot( _vcp ); const d6 = _vac.dot( _vcp ); if ( d6 >= 0 && d5 <= d6 ) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy( c ); } const vb = d5 * d2 - d1 * d6; if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { w = d2 / ( d2 - d6 ); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy( a ).addScaledVector( _vac, w ); } const va = d3 * d6 - d5 * d4; if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { _vbc.subVectors( c, b ); w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC } // face region const denom = 1 / ( va + vb + vc ); // u = va * denom v = vb * denom; w = vc * denom; return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); } equals( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest( value ) { if ( this._alphaTest > 0 !== value > 0 ) { this.version ++; } this._alphaTest = value; } onBuild( /* shaderobject, renderer */ ) {} onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} onBeforeCompile( /* shaderobject, renderer */ ) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues( values ) { if ( values === undefined ) return; for ( const key in values ) { const newValue = values[ key ]; if ( newValue === undefined ) { console.warn( `THREE.Material: parameter "${ key }" has value of undefined.` ); continue; } const currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( `THREE.Material: "${ key }" is not a property of THREE.${ this.type }.` ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else { this[ key ] = newValue; } } } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( isRootObject ) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.6, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.sheen !== undefined ) data.sheen = this.sheen; if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; } if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; } if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; } if ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy; if ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation; if ( this.anisotropyMap && this.anisotropyMap.isTexture ) { data.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid; } if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) { data.lightMap = this.lightMap.toJSON( meta ).uuid; data.lightMapIntensity = this.lightMapIntensity; } if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; if ( this.combine !== undefined ) data.combine = this.combine; } if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.transmission !== undefined ) data.transmission = this.transmission; if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; if ( this.thickness !== undefined ) data.thickness = this.thickness; if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); if ( this.size !== undefined ) data.size = this.size; if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors ) data.vertexColors = true; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; if ( this.polygonOffset === true ) data.polygonOffset = true; if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.alphaHash === true ) data.alphaHash = this.alphaHash; if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.forceSinglePass === true ) data.forceSinglePass = this.forceSinglePass; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.visible === false ) data.visible = false; if ( this.toneMapped === false ) data.toneMapped = false; if ( this.fog === false ) data.fog = false; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRootObject ) { const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if ( srcPlanes !== null ) { const n = srcPlanes.length; dstPlanes = new Array( n ); for ( let i = 0; i !== n; ++ i ) { dstPlanes[ i ] = srcPlanes[ i ].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaHash = source.alphaHash; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse( JSON.stringify( source.userData ) ); return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } set needsUpdate( value ) { if ( value === true ) this.version ++; } } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } class Color { constructor( r, g, b ) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; return this.set( r, g, b ); } set( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string const value = r; if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } } else { this.setRGB( r, g, b ); } return this; } setScalar( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex( hex, colorSpace = SRGBColorSpace ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo( h, 1 ); s = clamp( s, 0, 1 ); l = clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); const q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setStyle( style, colorSpace = SRGBColorSpace ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } let m; if ( m = /^(w+)(([^)]*))/.exec( style ) ) { // rgb / hsl let color; const name = m[ 1 ]; const components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255, Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255, colorSpace ); } if ( color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha( color[ 4 ] ); return this.setRGB( Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100, Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100, colorSpace ); } break; case "hsl": case "hsla": if ( color = /^s*(d*.?d+)s*,s*(d*.?d+)\%s*,s*(d*.?d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha( color[ 4 ] ); return this.setHSL( parseFloat( color[ 1 ] ) / 360, parseFloat( color[ 2 ] ) / 100, parseFloat( color[ 3 ] ) / 100, colorSpace ); } break; default: console.warn( "THREE.Color: Unknown color model " + style ); } } else if ( m = /^#([A-Fa-fd]+)$/.exec( style ) ) { // hex color const hex = m[ 1 ]; const size = hex.length; if ( size === 3 ) { // #ff0 return this.setRGB( parseInt( hex.charAt( 0 ), 16 ) / 15, parseInt( hex.charAt( 1 ), 16 ) / 15, parseInt( hex.charAt( 2 ), 16 ) / 15, colorSpace ); } else if ( size === 6 ) { // #ff0000 return this.setHex( parseInt( hex, 16 ), colorSpace ); } else { console.warn( "THREE.Color: Invalid hex color " + style ); } } else if ( style && style.length > 0 ) { return this.setColorName( style, colorSpace ); } return this; } setColorName( style, colorSpace = SRGBColorSpace ) { // color keywords const hex = _colorKeywords[ style.toLowerCase() ]; if ( hex !== undefined ) { // red this.setHex( hex, colorSpace ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } return this; } clone() { return new this.constructor( this.r, this.g, this.b ); } copy( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear( color ) { this.r = SRGBToLinear( color.r ); this.g = SRGBToLinear( color.g ); this.b = SRGBToLinear( color.b ); return this; } copyLinearToSRGB( color ) { this.r = LinearToSRGB( color.r ); this.g = LinearToSRGB( color.g ); this.b = LinearToSRGB( color.b ); return this; } convertSRGBToLinear() { this.copySRGBToLinear( this ); return this; } convertLinearToSRGB() { this.copyLinearToSRGB( this ); return this; } getHex( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); return Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) ); } getHexString( colorSpace = SRGBColorSpace ) { return ( "000000" + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } getHSL( target, colorSpace = ColorManagement.workingColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max( r, g, b ); const min = Math.min( r, g, b ); let hue, saturation; const lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB( target, colorSpace = ColorManagement.workingColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); const r = _color.r, g = _color.g, b = _color.b; if ( colorSpace !== SRGBColorSpace ) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`; } return `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`; } offsetHSL( h, s, l ) { this.getHSL( _hslA ); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } add( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar( s ) { this.r += s; this.g += s; this.b += s; return this; } sub( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; } multiply( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar( s ) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; } lerpColors( color1, color2, alpha ) { this.r = color1.r + ( color2.r - color1.r ) * alpha; this.g = color1.g + ( color2.g - color1.g ) * alpha; this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } lerpHSL( color, alpha ) { this.getHSL( _hslA ); color.getHSL( _hslB ); const h = lerp( _hslA.h, _hslB.h, alpha ); const s = lerp( _hslA.s, _hslB.s, alpha ); const l = lerp( _hslA.l, _hslB.l, alpha ); this.setHSL( h, s, l ); return this; } setFromVector3( v ) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3( m ) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b; this.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b; this.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b; return this; } equals( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); } fromArray( array, offset = 0 ) { this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; } fromBufferAttribute( attribute, index ) { this.r = attribute.getX( index ); this.g = attribute.getY( index ); this.b = attribute.getZ( index ); return this; } toJSON() { return this.getHex(); } *[ Symbol.iterator ]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/ new Color(); Color.NAMES = _colorKeywords; class MeshBasicMaterial extends Material { constructor( parameters ) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } // Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf const _tables = /*@__PURE__*/ _generateTables(); function _generateTables() { // float32 to float16 helpers const buffer = new ArrayBuffer( 4 ); const floatView = new Float32Array( buffer ); const uint32View = new Uint32Array( buffer ); const baseTable = new Uint32Array( 512 ); const shiftTable = new Uint32Array( 512 ); for ( let i = 0; i < 256; ++ i ) { const e = i - 127; // very small number (0, -0) if ( e < - 27 ) { baseTable[ i ] = 0x0000; baseTable[ i | 0x100 ] = 0x8000; shiftTable[ i ] = 24; shiftTable[ i | 0x100 ] = 24; // small number (denorm) } else if ( e < - 14 ) { baseTable[ i ] = 0x0400 >> ( - e - 14 ); baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; shiftTable[ i ] = - e - 1; shiftTable[ i | 0x100 ] = - e - 1; // normal number } else if ( e <= 15 ) { baseTable[ i ] = ( e + 15 ) << 10; baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; shiftTable[ i ] = 13; shiftTable[ i | 0x100 ] = 13; // large number (Infinity, -Infinity) } else if ( e < 128 ) { baseTable[ i ] = 0x7c00; baseTable[ i | 0x100 ] = 0xfc00; shiftTable[ i ] = 24; shiftTable[ i | 0x100 ] = 24; // stay (NaN, Infinity, -Infinity) } else { baseTable[ i ] = 0x7c00; baseTable[ i | 0x100 ] = 0xfc00; shiftTable[ i ] = 13; shiftTable[ i | 0x100 ] = 13; } } // float16 to float32 helpers const mantissaTable = new Uint32Array( 2048 ); const exponentTable = new Uint32Array( 64 ); const offsetTable = new Uint32Array( 64 ); for ( let i = 1; i < 1024; ++ i ) { let m = i << 13; // zero pad mantissa bits let e = 0; // zero exponent // normalized while ( ( m & 0x00800000 ) === 0 ) { m <<= 1; e -= 0x00800000; // decrement exponent } m &= ~ 0x00800000; // clear leading 1 bit e += 0x38800000; // adjust bias mantissaTable[ i ] = m | e; } for ( let i = 1024; i < 2048; ++ i ) { mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); } for ( let i = 1; i < 31; ++ i ) { exponentTable[ i ] = i << 23; } exponentTable[ 31 ] = 0x47800000; exponentTable[ 32 ] = 0x80000000; for ( let i = 33; i < 63; ++ i ) { exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); } exponentTable[ 63 ] = 0xc7800000; for ( let i = 1; i < 64; ++ i ) { if ( i !== 32 ) { offsetTable[ i ] = 1024; } } return { floatView: floatView, uint32View: uint32View, baseTable: baseTable, shiftTable: shiftTable, mantissaTable: mantissaTable, exponentTable: exponentTable, offsetTable: offsetTable }; } // float32 to float16 function toHalfFloat( val ) { if ( Math.abs( val ) > 65504 ) console.warn( "THREE.DataUtils.toHalfFloat(): Value out of range." ); val = clamp( val, - 65504, 65504 ); _tables.floatView[ 0 ] = val; const f = _tables.uint32View[ 0 ]; const e = ( f >> 23 ) & 0x1ff; return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] ); } // float16 to float32 function fromHalfFloat( val ) { const m = val >> 10; _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ]; return _tables.floatView[ 0 ]; } const DataUtils = { toHalfFloat: toHalfFloat, fromHalfFloat: fromHalfFloat, }; const _vector$8 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor( array, itemSize, normalized = false ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.gpuType = FloatType; this.version = 0; } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; this.gpuType = source.gpuType; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( let i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } copyArray( array ) { this.array.set( array ); return this; } applyMatrix3( m ) { if ( this.itemSize === 2 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector2$1.fromBufferAttribute( this, i ); _vector2$1.applyMatrix3( m ); this.setXY( i, _vector2$1.x, _vector2$1.y ); } } else if ( this.itemSize === 3 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix3( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } } return this; } applyMatrix4( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyMatrix4( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.applyNormalMatrix( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$8.fromBufferAttribute( this, i ); _vector$8.transformDirection( m ); this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } return this; } set( value, offset = 0 ) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set( value, offset ); return this; } getComponent( index, component ) { let value = this.array[ index * this.itemSize + component ]; if ( this.normalized ) value = denormalize( value, this.array ); return value; } setComponent( index, component, value ) { if ( this.normalized ) value = normalize( value, this.array ); this.array[ index * this.itemSize + component ] = value; return this; } getX( index ) { let x = this.array[ index * this.itemSize ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.array[ index * this.itemSize ] = x; return this; } getY( index ) { let y = this.array[ index * this.itemSize + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.array[ index * this.itemSize + 1 ] = y; return this; } getZ( index ) { let z = this.array[ index * this.itemSize + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.array[ index * this.itemSize + 2 ] = z; return this; } getW( index ) { let w = this.array[ index * this.itemSize + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.array[ index * this.itemSize + 3 ] = w; return this; } setXY( index, x, y ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; } onUpload( callback ) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor( this.array, this.itemSize ).copy( this ); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from( this.array ), normalized: this.normalized }; if ( this.name !== "" ) data.name = this.name; if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; return data; } } // class Int8BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int8Array( array ), itemSize, normalized ); } } class Uint8BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint8Array( array ), itemSize, normalized ); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint8ClampedArray( array ), itemSize, normalized ); } } class Int16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int16Array( array ), itemSize, normalized ); } } class Uint16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); } } class Int32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Int32Array( array ), itemSize, normalized ); } } class Uint32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint32Array( array ), itemSize, normalized ); } } class Float16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); this.isFloat16BufferAttribute = true; } getX( index ) { let x = fromHalfFloat( this.array[ index * this.itemSize ] ); if ( this.normalized ) x = denormalize( x, this.array ); return x; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.array[ index * this.itemSize ] = toHalfFloat( x ); return this; } getY( index ) { let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] ); if ( this.normalized ) y = denormalize( y, this.array ); return y; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.array[ index * this.itemSize + 1 ] = toHalfFloat( y ); return this; } getZ( index ) { let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] ); if ( this.normalized ) z = denormalize( z, this.array ); return z; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.array[ index * this.itemSize + 2 ] = toHalfFloat( z ); return this; } getW( index ) { let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] ); if ( this.normalized ) w = denormalize( w, this.array ); return w; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.array[ index * this.itemSize + 3 ] = toHalfFloat( w ); return this; } setXY( index, x, y ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); this.array[ index + 2 ] = toHalfFloat( z ); return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.array[ index + 0 ] = toHalfFloat( x ); this.array[ index + 1 ] = toHalfFloat( y ); this.array[ index + 2 ] = toHalfFloat( z ); this.array[ index + 3 ] = toHalfFloat( w ); return this; } } class Float32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float32Array( array ), itemSize, normalized ); } } class Float64BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float64Array( array ), itemSize, normalized ); } } let _id$1 = 0; const _m1 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$1 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$7 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty( this, "id", { value: _id$1 ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } return this; } getAttribute( name ) { return this.attributes[ name ]; } setAttribute( name, attribute ) { this.attributes[ name ] = attribute; return this; } deleteAttribute( name ) { delete this.attributes[ name ]; return this; } hasAttribute( name ) { return this.attributes[ name ] !== undefined; } addGroup( start, count, materialIndex = 0 ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex } ); } clearGroups() { this.groups = []; } setDrawRange( start, count ) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4( matrix ) { const position = this.attributes.position; if ( position !== undefined ) { position.applyMatrix4( matrix ); position.needsUpdate = true; } const normal = this.attributes.normal; if ( normal !== undefined ) { const normalMatrix = new Matrix3().getNormalMatrix( matrix ); normal.applyNormalMatrix( normalMatrix ); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if ( tangent !== undefined ) { tangent.transformDirection( matrix ); tangent.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; } applyQuaternion( q ) { _m1.makeRotationFromQuaternion( q ); this.applyMatrix4( _m1 ); return this; } rotateX( angle ) { // rotate geometry around world x-axis _m1.makeRotationX( angle ); this.applyMatrix4( _m1 ); return this; } rotateY( angle ) { // rotate geometry around world y-axis _m1.makeRotationY( angle ); this.applyMatrix4( _m1 ); return this; } rotateZ( angle ) { // rotate geometry around world z-axis _m1.makeRotationZ( angle ); this.applyMatrix4( _m1 ); return this; } translate( x, y, z ) { // translate geometry _m1.makeTranslation( x, y, z ); this.applyMatrix4( _m1 ); return this; } scale( x, y, z ) { // scale geometry _m1.makeScale( x, y, z ); this.applyMatrix4( _m1 ); return this; } lookAt( vector ) { _obj.lookAt( vector ); _obj.updateMatrix(); this.applyMatrix4( _obj.matrix ); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter( _offset ).negate(); this.translate( _offset.x, _offset.y, _offset.z ); return this; } setFromPoints( points ) { const position = []; for ( let i = 0, l = points.length; i < l; i ++ ) { const point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.setAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; } computeBoundingBox() { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingBox.set( new Vector3( - Infinity, - Infinity, - Infinity ), new Vector3( + Infinity, + Infinity, + Infinity ) ); return; } if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _box$1.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( this.boundingBox.min, _box$1.min ); this.boundingBox.expandByPoint( _vector$7 ); _vector$7.addVectors( this.boundingBox.max, _box$1.max ); this.boundingBox.expandByPoint( _vector$7 ); } else { this.boundingBox.expandByPoint( _box$1.min ); this.boundingBox.expandByPoint( _box$1.max ); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } } computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingSphere.set( new Vector3(), Infinity ); return; } if ( position ) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _boxMorphTargets.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$7.addVectors( _box$1.min, _boxMorphTargets.min ); _box$1.expandByPoint( _vector$7 ); _vector$7.addVectors( _box$1.max, _boxMorphTargets.max ); _box$1.expandByPoint( _vector$7 ); } else { _box$1.expandByPoint( _boxMorphTargets.min ); _box$1.expandByPoint( _boxMorphTargets.max ); } } } _box$1.getCenter( center ); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for ( let i = 0, il = position.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( position, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; const morphTargetsRelative = this.morphTargetsRelative; for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { _vector$7.fromBufferAttribute( morphAttribute, j ); if ( morphTargetsRelative ) { _offset.fromBufferAttribute( position, j ); _vector$7.add( _offset ); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } } } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)" ); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if ( this.hasAttribute( "tangent" ) === false ) { this.setAttribute( "tangent", new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } const tangents = this.getAttribute( "tangent" ).array; const tan1 = [], tan2 = []; for ( let i = 0; i < nVertices; i ++ ) { tan1[ i ] = new Vector3(); tan2[ i ] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle( a, b, c ) { vA.fromArray( positions, a * 3 ); vB.fromArray( positions, b * 3 ); vC.fromArray( positions, c * 3 ); uvA.fromArray( uvs, a * 2 ); uvB.fromArray( uvs, b * 2 ); uvC.fromArray( uvs, c * 2 ); vB.sub( vA ); vC.sub( vA ); uvB.sub( uvA ); uvC.sub( uvA ); const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); // silently ignore degenerate uv triangles having coincident or colinear vertices if ( ! isFinite( r ) ) return; sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); } let groups = this.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.length } ]; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleTriangle( indices[ j + 0 ], indices[ j + 1 ], indices[ j + 2 ] ); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex( v ) { n.fromArray( normals, v * 3 ); n2.copy( n ); const t = tan1[ v ]; // Gram-Schmidt orthogonalize tmp.copy( t ); tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2.crossVectors( n2, t ); const test = tmp2.dot( tan2[ v ] ); const w = ( test < 0.0 ) ? - 1.0 : 1.0; tangents[ v * 4 ] = tmp.x; tangents[ v * 4 + 1 ] = tmp.y; tangents[ v * 4 + 2 ] = tmp.z; tangents[ v * 4 + 3 ] = w; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleVertex( indices[ j + 0 ] ); handleVertex( indices[ j + 1 ] ); handleVertex( indices[ j + 2 ] ); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute( "position" ); if ( positionAttribute !== undefined ) { let normalAttribute = this.getAttribute( "normal" ); if ( normalAttribute === undefined ) { normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); this.setAttribute( "normal", normalAttribute ); } else { // reset existing normals to zero for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { normalAttribute.setXYZ( i, 0, 0, 0 ); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { for ( let i = 0, il = index.count; i < il; i += 3 ) { const vA = index.getX( i + 0 ); const vB = index.getX( i + 1 ); const vC = index.getX( i + 2 ); pA.fromBufferAttribute( positionAttribute, vA ); pB.fromBufferAttribute( positionAttribute, vB ); pC.fromBufferAttribute( positionAttribute, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); nA.fromBufferAttribute( normalAttribute, vA ); nB.fromBufferAttribute( normalAttribute, vB ); nC.fromBufferAttribute( normalAttribute, vC ); nA.add( cb ); nB.add( cb ); nC.add( cb ); normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); } } else { // non-indexed elements (unconnected triangle soup) for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { pA.fromBufferAttribute( positionAttribute, i + 0 ); pB.fromBufferAttribute( positionAttribute, i + 1 ); pC.fromBufferAttribute( positionAttribute, i + 2 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } normalizeNormals() { const normals = this.attributes.normal; for ( let i = 0, il = normals.count; i < il; i ++ ) { _vector$7.fromBufferAttribute( normals, i ); _vector$7.normalize(); normals.setXYZ( i, _vector$7.x, _vector$7.y, _vector$7.z ); } } toNonIndexed() { function convertBufferAttribute( attribute, indices ) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor( indices.length * itemSize ); let index = 0, index2 = 0; for ( let i = 0, l = indices.length; i < l; i ++ ) { if ( attribute.isInterleavedBufferAttribute ) { index = indices[ i ] * attribute.data.stride + attribute.offset; } else { index = indices[ i ] * itemSize; } for ( let j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } return new BufferAttribute( array2, itemSize, normalized ); } // if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed." ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for ( const name in attributes ) { const attribute = attributes[ name ]; const newAttribute = convertBufferAttribute( attribute, indices ); geometry2.setAttribute( name, newAttribute ); } // morph attributes const morphAttributes = this.morphAttributes; for ( const name in morphAttributes ) { const morphArray = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { const attribute = morphAttribute[ i ]; const newAttribute = convertBufferAttribute( attribute, indices ); morphArray.push( newAttribute ); } geometry2.morphAttributes[ name ] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; } toJSON() { const data = { metadata: { version: 4.6, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { const parameters = this.parameters; for ( const key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if ( index !== null ) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call( index.array ) }; } const attributes = this.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; data.data.attributes[ key ] = attribute.toJSON( data.data ); } const morphAttributes = {}; let hasMorphAttributes = false; for ( const key in this.morphAttributes ) { const attributeArray = this.morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; array.push( attribute.toJSON( data.data ) ); } if ( array.length > 0 ) { morphAttributes[ key ] = array; hasMorphAttributes = true; } } if ( hasMorphAttributes ) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } const boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if ( index !== null ) { this.setIndex( index.clone( data ) ); } // attributes const attributes = source.attributes; for ( const name in attributes ) { const attribute = attributes[ name ]; this.setAttribute( name, attribute.clone( data ) ); } // morph attributes const morphAttributes = source.morphAttributes; for ( const name in morphAttributes ) { const array = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone( data ) ); } this.morphAttributes[ name ] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box const boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); const _ray$3 = /*@__PURE__*/ new Ray(); const _sphere$5 = /*@__PURE__*/ new Sphere(); const _sphereHitAt = /*@__PURE__*/ new Vector3(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _normalA = /*@__PURE__*/ new Vector3(); const _normalB = /*@__PURE__*/ new Vector3(); const _normalC = /*@__PURE__*/ new Vector3(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } getVertexPosition( index, target ) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute( position, index ); const morphInfluences = this.morphTargetInfluences; if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { const influence = morphInfluences[ i ]; const morphAttribute = morphPosition[ i ]; if ( influence === 0 ) continue; _tempA.fromBufferAttribute( morphAttribute, index ); if ( morphTargetsRelative ) { _morphA.addScaledVector( _tempA, influence ); } else { _morphA.addScaledVector( _tempA.sub( target ), influence ); } } target.add( _morphA ); } return target; } raycast( raycaster, intersects ) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // test with bounding sphere in world space if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$5.copy( geometry.boundingSphere ); _sphere$5.applyMatrix4( matrixWorld ); // check distance from ray origin to bounding sphere _ray$3.copy( raycaster.ray ).recast( raycaster.near ); if ( _sphere$5.containsPoint( _ray$3.origin ) === false ) { if ( _ray$3.intersectSphere( _sphere$5, _sphereHitAt ) === null ) return; if ( _ray$3.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return; } // convert ray to local space of mesh _inverseMatrix$3.copy( matrixWorld ).invert(); _ray$3.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$3 ); // test with bounding box in local space if ( geometry.boundingBox !== null ) { if ( _ray$3.intersectsBox( geometry.boundingBox ) === false ) return; } // test for intersections with geometry this._computeIntersections( raycaster, intersects, _ray$3 ); } _computeIntersections( raycaster, intersects, rayLocalSpace ) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if ( index !== null ) { // indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = index.getX( j ); const b = index.getX( j + 1 ); const c = index.getX( j + 2 ); intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = index.getX( i ); const b = index.getX( i + 1 ); const c = index.getX( i + 2 ); intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } } else if ( position !== undefined ) { // non-indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } } } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { let intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point ); } if ( intersect === null ) return null; _intersectionPointWorld.copy( point ); _intersectionPointWorld.applyMatrix4( object.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) { object.getVertexPosition( a, _vA$1 ); object.getVertexPosition( b, _vB$1 ); object.getVertexPosition( c, _vC$1 ); const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); if ( intersection ) { if ( uv ) { _uvA$1.fromBufferAttribute( uv, a ); _uvB$1.fromBufferAttribute( uv, b ); _uvC$1.fromBufferAttribute( uv, c ); intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } if ( uv1 ) { _uvA$1.fromBufferAttribute( uv1, a ); _uvB$1.fromBufferAttribute( uv1, b ); _uvC$1.fromBufferAttribute( uv1, c ); intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); intersection.uv2 = intersection.uv1; // @deprecated, r152 } if ( normal ) { _normalA.fromBufferAttribute( normal, a ); _normalB.fromBufferAttribute( normal, b ); _normalC.fromBufferAttribute( normal, c ); intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3() ); if ( intersection.normal.dot( ray.direction ) > 0 ) { intersection.normal.multiplyScalar( - 1 ); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor( widthSegments ); heightSegments = Math.floor( heightSegments ); depthSegments = Math.floor( depthSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segmentHeight - heightHalf; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); } } /** * Uniform Utilities */ function cloneUniforms( src ) { const dst = {}; for ( const u in src ) { dst[ u ] = {}; for ( const p in src[ u ] ) { const property = src[ u ][ p ]; if ( property && ( property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion ) ) { if ( property.isRenderTargetTexture ) { console.warn( "UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms()." ); dst[ u ][ p ] = null; } else { dst[ u ][ p ] = property.clone(); } } else if ( Array.isArray( property ) ) { dst[ u ][ p ] = property.slice(); } else { dst[ u ][ p ] = property; } } } return dst; } function mergeUniforms( uniforms ) { const merged = {}; for ( let u = 0; u < uniforms.length; u ++ ) { const tmp = cloneUniforms( uniforms[ u ] ); for ( const p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; } function cloneUniformsGroups( src ) { const dst = []; for ( let u = 0; u < src.length; u ++ ) { dst.push( src[ u ].clone() ); } return dst; } function getUnlitUniformColorSpace( renderer ) { if ( renderer.getRenderTarget() === null ) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } return LinearSRGBColorSpace; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor( parameters ) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv1": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if ( parameters !== undefined ) { this.setValues( parameters ); } } copy( source ) { super.copy( source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms( source.uniforms ); this.uniformsGroups = cloneUniformsGroups( source.uniformsGroups ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.glslVersion = this.glslVersion; data.uniforms = {}; for ( const name in this.uniforms ) { const uniform = this.uniforms[ name ]; const value = uniform.value; if ( value && value.isTexture ) { data.uniforms[ name ] = { type: "t", value: value.toJSON( meta ).uuid }; } else if ( value && value.isColor ) { data.uniforms[ name ] = { type: "c", value: value.getHex() }; } else if ( value && value.isVector2 ) { data.uniforms[ name ] = { type: "v2", value: value.toArray() }; } else if ( value && value.isVector3 ) { data.uniforms[ name ] = { type: "v3", value: value.toArray() }; } else if ( value && value.isVector4 ) { data.uniforms[ name ] = { type: "v4", value: value.toArray() }; } else if ( value && value.isMatrix3 ) { data.uniforms[ name ] = { type: "m3", value: value.toArray() }; } else if ( value && value.isMatrix4 ) { data.uniforms[ name ] = { type: "m4", value: value.toArray() }; } else { data.uniforms[ name ] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for ( const key in this.extensions ) { if ( this.extensions[ key ] === true ) extensions[ key ] = true; } if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; return data; } } class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); this.coordinateSystem = WebGLCoordinateSystem; } copy( source, recursive ) { super.copy( source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); this.coordinateSystem = source.coordinateSystem; return this; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } updateWorldMatrix( updateParents, updateChildren ) { super.updateWorldMatrix( updateParents, updateChildren ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } clone() { return new this.constructor().copy( this ); } } class PerspectiveCamera extends Camera { constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength( focalLength ) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan( Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = - 0.5 * width; const view = this.view; if ( this.view !== null && this.view.enabled ) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = - 90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor( near, far, renderTarget ) { super(); this.type = "CubeCamera"; this.renderTarget = renderTarget; this.coordinateSystem = null; const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.layers = this.layers; this.add( cameraPX ); const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.layers = this.layers; this.add( cameraNX ); const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.layers = this.layers; this.add( cameraPY ); const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.layers = this.layers; this.add( cameraNY ); const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.layers = this.layers; this.add( cameraPZ ); const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.layers = this.layers; this.add( cameraNZ ); } updateCoordinateSystem() { const coordinateSystem = this.coordinateSystem; const cameras = this.children.concat(); const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; for ( const camera of cameras ) this.remove( camera ); if ( coordinateSystem === WebGLCoordinateSystem ) { cameraPX.up.set( 0, 1, 0 ); cameraPX.lookAt( 1, 0, 0 ); cameraNX.up.set( 0, 1, 0 ); cameraNX.lookAt( - 1, 0, 0 ); cameraPY.up.set( 0, 0, - 1 ); cameraPY.lookAt( 0, 1, 0 ); cameraNY.up.set( 0, 0, 1 ); cameraNY.lookAt( 0, - 1, 0 ); cameraPZ.up.set( 0, 1, 0 ); cameraPZ.lookAt( 0, 0, 1 ); cameraNZ.up.set( 0, 1, 0 ); cameraNZ.lookAt( 0, 0, - 1 ); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( - 1, 0, 0 ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( 1, 0, 0 ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( 0, 1, 0 ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( 0, - 1, 0 ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( 0, 0, 1 ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( 0, 0, - 1 ); } else { throw new Error( "THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: " + coordinateSystem ); } for ( const camera of cameras ) { this.add( camera ); camera.updateMatrixWorld(); } } update( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); const renderTarget = this.renderTarget; if ( this.coordinateSystem !== renderer.coordinateSystem ) { this.coordinateSystem = renderer.coordinateSystem; this.updateCoordinateSystem(); } const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentXrEnabled = renderer.xr.enabled; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget( renderTarget, 0 ); renderer.render( scene, cameraPX ); renderer.setRenderTarget( renderTarget, 1 ); renderer.render( scene, cameraNX ); renderer.setRenderTarget( renderTarget, 2 ); renderer.render( scene, cameraPY ); renderer.setRenderTarget( renderTarget, 3 ); renderer.render( scene, cameraNY ); renderer.setRenderTarget( renderTarget, 4 ); renderer.render( scene, cameraPZ ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget( renderTarget, 5 ); renderer.render( scene, cameraNZ ); renderer.setRenderTarget( currentRenderTarget ); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images( value ) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor( size = 1, options = {} ) { super( size, size, options ); this.isWebGLCubeRenderTarget = true; const image = { width: size, height: size, depth: 1 }; const images = [ image, image, image, image, image, image ]; if ( options.encoding !== undefined ) { // @deprecated, r152 warnOnce( "THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace." ); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null }, }, vertexShader: /* glsl */` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry( 5, 5, 5 ); const material = new ShaderMaterial( { name: "CubemapFromEquirect", uniforms: cloneUniforms( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending } ); material.uniforms.tEquirect.value = texture; const mesh = new Mesh( geometry, material ); const currentMinFilter = texture.minFilter; // Avoid blurred poles if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; const camera = new CubeCamera( 1, 10, this ); camera.update( renderer, mesh ); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear( renderer, color, depth, stencil ) { const currentRenderTarget = renderer.getRenderTarget(); for ( let i = 0; i < 6; i ++ ) { renderer.setRenderTarget( this, i ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( currentRenderTarget ); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; } setComponents( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; } setFromNormalAndCoplanarPoint( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; } setFromCoplanarPoints( a, b, c ) { const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; } copy( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= - 1; this.normal.negate(); return this; } distanceToPoint( point ) { return this.normal.dot( point ) + this.constant; } distanceToSphere( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; } projectPoint( point, target ) { return target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) ); } intersectLine( line, target ) { const direction = line.delta( _vector1 ); const denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return null; } const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return null; } return target.copy( line.start ).addScaledVector( direction, t ); } intersectsLine( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint( line.start ); const endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); } intersectsBox( box ) { return box.intersectsPlane( this ); } intersectsSphere( sphere ) { return sphere.intersectsPlane( this ); } coplanarPoint( target ) { return target.copy( this.normal ).multiplyScalar( - this.constant ); } applyMatrix4( matrix, optionalNormalMatrix ) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; } translate( offset ) { this.constant -= offset.dot( this.normal ); return this; } equals( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } clone() { return new this.constructor().copy( this ); } } const _sphere$4 = /*@__PURE__*/ new Sphere(); const _vector$6 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { this.planes = [ p0, p1, p2, p3, p4, p5 ]; } set( p0, p1, p2, p3, p4, p5 ) { const planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; } copy( frustum ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; } setFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem ) { const planes = this.planes; const me = m.elements; const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); if ( coordinateSystem === WebGLCoordinateSystem ) { planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); } else if ( coordinateSystem === WebGPUCoordinateSystem ) { planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); } else { throw new Error( "THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: " + coordinateSystem ); } return this; } intersectsObject( object ) { if ( object.boundingSphere !== undefined ) { if ( object.boundingSphere === null ) object.computeBoundingSphere(); _sphere$4.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld ); } else { const geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$4.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); } return this.intersectsSphere( _sphere$4 ); } intersectsSprite( sprite ) { _sphere$4.center.set( 0, 0, 0 ); _sphere$4.radius = 0.7071067811865476; _sphere$4.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( _sphere$4 ); } intersectsSphere( sphere ) { const planes = this.planes; const center = sphere.center; const negRadius = - sphere.radius; for ( let i = 0; i < 6; i ++ ) { const distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; } intersectsBox( box ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { const plane = planes[ i ]; // corner at max distance _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; if ( plane.distanceToPoint( _vector$6 ) < 0 ) { return false; } } return true; } containsPoint( point ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } clone() { return new this.constructor().copy( this ); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame( time, frame ) { animationLoop( time, frame ); requestId = context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; requestId = context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { context.cancelAnimationFrame( requestId ); isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } function WebGLAttributes( gl, capabilities ) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); let type; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { if ( isWebGL2 ) { type = gl.HALF_FLOAT; } else { throw new Error( "THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2." ); } } else { type = gl.UNSIGNED_SHORT; } } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } else if ( array instanceof Uint8ClampedArray ) { type = gl.UNSIGNED_BYTE; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else { if ( isWebGL2 ) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); } updateRange.count = - 1; // reset range } attribute.onUploadCallback(); } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isGLBufferAttribute ) { const cached = buffers.get( attribute ); if ( ! cached || cached.version < attribute.version ) { buffers.set( attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version } ); } return; } if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor( widthSegments ); const gridY = Math.floor( heightSegments ); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segment_height - height_half; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = ix + gridX1 * iy; const b = ix + gridX1 * ( iy + 1 ); const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = ( ix + 1 ) + gridX1 * iy; indices.push( a, b, d ); indices.push( b, c, d ); } } this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); } } var alphahash_fragment = "#ifdef USE_ALPHAHASH if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; #endif"; var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH const float ALPHA_HASH_SCALE = 0.05; float hash2D( vec2 value ) { return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); } float hash3D( vec3 value ) { return hash2D( vec2( hash2D( value.xy ), value.z ) ); } float getAlphaHashThreshold( vec3 position ) { float maxDeriv = max( length( dFdx( position.xyz ) ), length( dFdy( position.xyz ) ) ); float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); vec2 pixScales = vec2( exp2( floor( log2( pixScale ) ) ), exp2( ceil( log2( pixScale ) ) ) ); vec2 alpha = vec2( hash3D( floor( pixScales.x * position.xyz ) ), hash3D( floor( pixScales.y * position.xyz ) ) ); float lerpFactor = fract( log2( pixScale ) ); float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; float a = min( lerpFactor, 1.0 - lerpFactor ); vec3 cases = vec3( x * x / ( 2.0 * a * ( 1.0 - a ) ), ( x - 0.5 * a ) / ( 1.0 - a ), 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) ); float threshold = ( x < ( 1.0 - a ) ) ? ( ( x < a ) ? cases.x : cases.y ) : cases.z; return clamp( threshold , 1.0e-6, 1.0 ); } #endif"; var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position ); #ifdef USE_ALPHAHASH vPosition = vec3( position ); #endif"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 rgb = XYZ_TO_REC709 * xyz; return rgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++ m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vBumpMapUv ); vec2 dSTdy = dFdy( vBumpMapUv ); float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = dFdx( surf_pos.xyz ); vec3 vSigmaY = dFdy( surf_pos.xyz ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; #ifdef USE_ALPHAHASH varying vec3 vPosition; #endif vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float luminance( const in vec3 rgb ) { const vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 ); return dot( weights, rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); } vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define cubeUV_r0 1.0 #define cubeUV_v0 0.339 #define cubeUV_m0 - 2.0 #define cubeUV_r1 0.8 #define cubeUV_v1 0.276 #define cubeUV_m1 - 1.0 #define cubeUV_r4 0.4 #define cubeUV_v4 0.046 #define cubeUV_m4 2.0 #define cubeUV_r5 0.305 #define cubeUV_v5 0.016 #define cubeUV_m5 3.0 #define cubeUV_r6 0.21 #define cubeUV_v6 0.0038 #define cubeUV_m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= cubeUV_r1 ) { mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; } else if ( roughness >= cubeUV_r4 ) { mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; } else if ( roughness >= cubeUV_r5 ) { mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; } else if ( roughness >= cubeUV_r6 ) { mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_fragment = "LambertMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; }; void RE_Direct_Lambert( const in IncidentLight directLight, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert #define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( LEGACY_LIGHTS ) if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #else float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP vec3 getIBLIrradiance( const in vec3 normal ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #ifdef USE_ANISOTROPY vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 bentNormal = cross( bitangent, viewDir ); bentNormal = normalize( cross( bentNormal, bitangent ) ); bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); return getIBLRadiance( viewDir, bentNormal, roughness ); #else return vec3( 0.0 ); #endif } #endif #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR material.ior = ior; #ifdef USE_SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULAR_COLORMAP specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; #endif #ifdef USE_SPECULAR_INTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEEN_COLORMAP material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEEN_ROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; #endif #endif #ifdef USE_ANISOTROPY #ifdef USE_ANISOTROPYMAP mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; #else vec2 anisotropyV = anisotropyVector; #endif material.anisotropy = length( anisotropyV ); anisotropyV /= material.anisotropy; material.anisotropy = saturate( material.anisotropy ); material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); material.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y; material.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y; #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif #ifdef IOR float ior; #endif #ifdef USE_TRANSMISSION float transmission; float transmissionAlpha; float thickness; float attenuationDistance; vec3 attenuationColor; #endif #ifdef USE_ANISOTROPY float anisotropy; float alphaT; vec3 anisotropyT; vec3 anisotropyB; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } #ifdef USE_ANISOTROPY float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); float v = 0.5 / ( gv + gl ); return saturate(v); } float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { float a2 = alphaT * alphaB; highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); highp float v2 = dot( v, v ); float w2 = a2 / v2; return RECIPROCAL_PI * a2 * pow2 ( w2 ); } #endif #ifdef USE_CLEARCOAT vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { vec3 f0 = material.clearcoatF0; float f90 = material.clearcoatF90; float roughness = material.clearcoatRoughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { vec3 f0 = material.specularColor; float f90 = material.specularF90; float roughness = material.roughness; float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); #ifdef USE_IRIDESCENCE F = mix( F, material.iridescenceFresnel, material.iridescence ); #endif #ifdef USE_ANISOTROPY float dotTL = dot( material.anisotropyT, lightDir ); float dotTV = dot( material.anisotropyT, viewDir ); float dotTH = dot( material.anisotropyT, halfDir ); float dotBL = dot( material.anisotropyB, lightDir ); float dotBV = dot( material.anisotropyB, viewDir ); float dotBH = dot( material.anisotropyB, halfDir ); float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); #else float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); #endif return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometry.viewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; vec4 spotColor; vec3 spotLightCoord; bool inSpotLightMap; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS #else #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) #endif #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; #endif #undef SPOT_LIGHT_MAP_INDEX #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) #ifdef USE_ANISOTROPY radiance += getIBLAnisotropyRadiance( geometry.viewDir, geometry.normal, material.roughness, material.anisotropyB, material.anisotropy ); #else radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #endif #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP diffuseColor *= texture2D( map, vMapUv ); #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal *= faceDirection; #endif #endif #if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) #ifdef USE_TANGENT mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn = getTangentFrame( - vViewPosition, normal, #if defined( USE_NORMALMAP ) vNormalMapUv #elif defined( USE_CLEARCOAT_NORMALMAP ) vClearcoatNormalMapUv #else vUv #endif ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn[0] *= faceDirection; tbn[1] *= faceDirection; #endif #endif #ifdef USE_CLEARCOAT_NORMALMAP #ifdef USE_TANGENT mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); #else mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); #endif #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) tbn2[0] *= faceDirection; tbn2[1] *= faceDirection; #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( USE_NORMALMAP_TANGENTSPACE ) vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef USE_NORMALMAP_OBJECTSPACE uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { vec3 q0 = dFdx( eye_pos.xyz ); vec3 q1 = dFdy( eye_pos.xyz ); vec2 st0 = dFdx( uv.st ); vec2 st1 = dFdy( uv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var opaque_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec2 packDepthToRG( in highp float v ) { return packDepthToRGBA( v ).yx; } float unpackRGToDepth( const in highp vec2 v ) { return unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { return depth * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * depth - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; bool frustumTest = inFrustum && shadowCoord.z <= 1.0; if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if defined( USE_SHADOWMAP ) #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif #if NUM_SPOT_LIGHT_COORDS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { shadowWorldPosition = worldPosition; #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; #endif vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return saturate( toneMappingExposure * color ); } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; material.attenuationDistance = attenuationDistance; material.attenuationColor = attenuationColor; #ifdef USE_TRANSMISSIONMAP material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; #endif #ifdef USE_THICKNESSMAP material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmitted = getIBLVolumeRefraction( n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness, material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; float w0( float a ) { return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); } float w1( float a ) { return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); } float w2( float a ){ return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); } float w3( float a ) { return ( 1.0 / 6.0 ) * ( a * a * a ); } float g0( float a ) { return w0( a ) + w1( a ); } float g1( float a ) { return w2( a ) + w3( a ); } float h0( float a ) { return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); } float h1( float a ) { return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); } vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { uv = uv * texelSize.zw + 0.5; vec2 iuv = floor( uv ); vec2 fuv = fract( uv ); float g0x = g0( fuv.x ); float g1x = g1( fuv.x ); float h0x = h0( fuv.x ); float h1x = h1( fuv.x ); float h0y = h0( fuv.y ); float h1y = h1( fuv.y ); vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); } vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); vec2 fLodSizeInv = 1.0 / fLodSize; vec2 cLodSizeInv = 1.0 / cLodSize; vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); return mix( fSample, cSample, fract( lod ) ); } vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); } vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( isinf( attenuationDistance ) ) { return vec3( 1.0 ); } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 attenuatedColor = transmittance * transmittedLight.rgb; vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); } #endif"; var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP varying vec2 vNormalMapUv; #endif #ifdef USE_EMISSIVEMAP varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SHEEN_COLORMAP varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_SPECULARMAP varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP uniform mat3 mapTransform; varying vec2 vMapUv; #endif #ifdef USE_ALPHAMAP uniform mat3 alphaMapTransform; varying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP uniform mat3 lightMapTransform; varying vec2 vLightMapUv; #endif #ifdef USE_AOMAP uniform mat3 aoMapTransform; varying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP uniform mat3 bumpMapTransform; varying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP uniform mat3 normalMapTransform; varying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP uniform mat3 displacementMapTransform; varying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP uniform mat3 emissiveMapTransform; varying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP uniform mat3 metalnessMapTransform; varying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP uniform mat3 roughnessMapTransform; varying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP uniform mat3 anisotropyMapTransform; varying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP uniform mat3 clearcoatMapTransform; varying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform mat3 clearcoatNormalMapTransform; varying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform mat3 clearcoatRoughnessMapTransform; varying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP uniform mat3 sheenColorMapTransform; varying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform mat3 sheenRoughnessMapTransform; varying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP uniform mat3 iridescenceMapTransform; varying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform mat3 iridescenceThicknessMapTransform; varying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP uniform mat3 specularMapTransform; varying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP uniform mat3 specularColorMapTransform; varying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform mat3 specularIntensityMapTransform; varying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP uniform mat3 transmissionMapTransform; varying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; #endif"; var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_ANISOTROPYMAP vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$h = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$h = "uniform sampler2D t2D; uniform float backgroundIntensity; varying vec2 vUv; void main() { vec4 texColor = texture2D( t2D, vUv ); texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$g = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #elif defined( ENVMAP_TYPE_CUBE_UV ) uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform float backgroundBlurriness; uniform float backgroundIntensity; varying vec3 vWorldDirection; #include void main() { #ifdef ENVMAP_TYPE_CUBE vec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness ); #else vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif texColor.rgb *= backgroundIntensity; gl_FragColor = texColor; #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldDirection; void main() { vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); gl_FragColor = texColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$9 = "#define LAMBERT uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif #ifdef USE_ANISOTROPY uniform vec2 anisotropyVector; #ifdef USE_ANISOTROPYMAP uniform sampler2D anisotropyMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include #ifdef USE_POINTS_UV varying vec2 vUv; uniform mat3 uvTransform; #endif void main() { #ifdef USE_POINTS_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphahash_fragment: alphahash_fragment, alphahash_pars_fragment: alphahash_pars_fragment, alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, colorspace_fragment: colorspace_fragment, colorspace_pars_fragment: colorspace_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, opaque_fragment: opaque_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null }, specularMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // physical refractionRatio: { value: 0.98 }, // basic, lambert, phong }, aomap: { aoMap: { value: null }, aoMapIntensity: { value: 1 }, aoMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, lightmap: { lightMap: { value: null }, lightMapIntensity: { value: 1 }, lightMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, bumpmap: { bumpMap: { value: null }, bumpMapTransform: { value: /*@__PURE__*/ new Matrix3() }, bumpScale: { value: 1 } }, normalmap: { normalMap: { value: null }, normalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, normalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) } }, displacementmap: { displacementMap: { value: null }, displacementMapTransform: { value: /*@__PURE__*/ new Matrix3() }, displacementScale: { value: 1 }, displacementBias: { value: 0 } }, emissivemap: { emissiveMap: { value: null }, emissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, metalnessmap: { metalnessMap: { value: null }, metalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, roughnessmap: { roughnessMap: { value: null }, roughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } }, gradientmap: { gradientMap: { value: null } }, fog: { fogDensity: { value: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) } }, 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: {} } }, spotLightMap: { value: [] }, spotShadowMap: { value: [] }, spotLightMatrix: { 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/ new Matrix3() } }, sprite: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, alphaTest: { value: 0 } } }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specular: { value: /*@__PURE__*/ new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } } ] ), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.sprite, UniformsLib.fog ] ), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: /*@__PURE__*/ new Matrix3() }, t2D: { value: null }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, backgroundCube: { uniforms: { envMap: { value: null }, flipEnvMap: { value: - 1 }, backgroundBlurriness: { value: 0 }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.backgroundCube_vert, fragmentShader: ShaderChunk.backgroundCube_frag }, cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: /*@__PURE__*/ new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.lights, UniformsLib.fog, { color: { value: /*@__PURE__*/ new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms( [ ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalMap: { value: null }, clearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, clearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() }, iridescenceIOR: { value: 1.3 }, iridescenceThicknessMinimum: { value: 100 }, iridescenceThicknessMaximum: { value: 400 }, iridescenceThicknessMap: { value: null }, iridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheen: { value: 0 }, sheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, sheenColorMap: { value: null }, sheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, sheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() }, transmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, thicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, attenuationDistance: { value: 0 }, attenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) }, specularColorMap: { value: null }, specularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() }, anisotropyVector: { value: /*@__PURE__*/ new Vector2() }, anisotropyMap: { value: null }, anisotropyMapTransform: { value: /*@__PURE__*/ new Matrix3() }, } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; const _rgb = { r: 0, b: 0, g: 0 }; function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render( renderList, scene ) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = ( usePMREM ? cubeuvmaps : cubemaps ).get( background ); } if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } const xr = renderer.xr; const environmentBlendMode = xr.getEnvironmentBlendMode(); switch ( environmentBlendMode ) { case "opaque": forceClear = true; break; case "additive": state.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha ); forceClear = true; break; case "alpha-blend": state.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha ); forceClear = true; break; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), new ShaderMaterial( { name: "BackgroundCubeMaterial", uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false } ) ); boxMesh.geometry.deleteAttribute( "normal" ); boxMesh.geometry.deleteAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } } ); objects.update( boxMesh ); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); } else if ( background && background.isTexture ) { if ( planeMesh === undefined ) { planeMesh = new Mesh( new PlaneGeometry( 2, 2 ), new ShaderMaterial( { name: "BackgroundMaterial", uniforms: cloneUniforms( ShaderLib.background.uniforms ), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false } ) ); planeMesh.geometry.deleteAttribute( "normal" ); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty( planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } } ); objects.update( planeMesh ); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = ( background.colorSpace === SRGBColorSpace ) ? false : true; if ( background.matrixAutoUpdate === true ) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); } } function setClear( color, alpha ) { color.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) ); state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha = 1 ) { clearColor.set( color ); clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const extension = capabilities.isWebGL2 ? null : extensions.get( "OES_vertex_array_object" ); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState( null ); let currentState = defaultState; let forceUpdate = false; function setup( object, material, program, geometry, index ) { let updateBuffers = false; if ( vaoAvailable ) { const state = getBindingState( geometry, program, material ); if ( currentState !== state ) { currentState = state; bindVertexArrayObject( currentState.object ); } updateBuffers = needsUpdate( object, geometry, program, index ); if ( updateBuffers ) saveCache( object, geometry, program, index ); } else { const wireframe = ( material.wireframe === true ); if ( currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe ) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } if ( updateBuffers || forceUpdate ) { forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); if ( index !== null ) { gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer ); } } } function createVertexArrayObject() { if ( capabilities.isWebGL2 ) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); return extension.bindVertexArrayOES( vao ); } function deleteVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); return extension.deleteVertexArrayOES( vao ); } function getBindingState( geometry, program, material ) { const wireframe = ( material.wireframe === true ); let programMap = bindingStates[ geometry.id ]; if ( programMap === undefined ) { programMap = {}; bindingStates[ geometry.id ] = programMap; } let stateMap = programMap[ program.id ]; if ( stateMap === undefined ) { stateMap = {}; programMap[ program.id ] = stateMap; } let state = stateMap[ wireframe ]; if ( state === undefined ) { state = createBindingState( createVertexArrayObject() ); stateMap[ wireframe ] = state; } return state; } function createBindingState( vao ) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for ( let i = 0; i < maxVertexAttributes; i ++ ) { newAttributes[ i ] = 0; enabledAttributes[ i ] = 0; attributeDivisors[ i ] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate( object, geometry, program, index ) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { const cachedAttribute = cachedAttributes[ name ]; let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( cachedAttribute === undefined ) return true; if ( cachedAttribute.attribute !== geometryAttribute ) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum ++; } } if ( currentState.attributesNum !== attributesNum ) return true; if ( currentState.index !== index ) return true; return false; } function saveCache( object, geometry, program, index ) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let attribute = attributes[ name ]; if ( attribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) attribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if ( attribute && attribute.data ) { data.data = attribute.data; } cache[ name ] = data; attributesNum ++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { const extension = capabilities.isWebGL2 ? gl : extensions.get( "ANGLE_instanced_arrays" ); extension[ capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE" ]( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset, integer ) { if ( integer === true ) { gl.vertexAttribIPointer( index, size, type, stride, offset ); } else { gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); } } function setupVertexAttributes( object, material, program, geometry ) { if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( geometryAttribute !== undefined ) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; // check for integer attributes (WebGL 2 only) const integer = ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType ) ); if ( geometryAttribute.isInterleavedBufferAttribute ) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if ( data.isInstancedInterleavedBuffer ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement, integer ); } } else { if ( geometryAttribute.isInstancedBufferAttribute ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, ( size / programAttribute.locationSize ) * i * bytesPerElement, integer ); } } } else if ( materialDefaultAttributeValues !== undefined ) { const value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: gl.vertexAttrib4fv( programAttribute.location, value ); break; default: gl.vertexAttrib1fv( programAttribute.location, value ); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometryId ]; } } function releaseStatesOfGeometry( geometry ) { if ( bindingStates[ geometry.id ] === undefined ) return; const programMap = bindingStates[ geometry.id ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometry.id ]; } function releaseStatesOfProgram( program ) { for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; if ( programMap[ program.id ] === undefined ) continue; const stateMap = programMap[ program.id ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ program.id ]; } } function reset() { resetDefaultState(); forceUpdate = true; if ( currentState === defaultState ) return; currentState = defaultState; bindVertexArrayObject( currentState.object ); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawArraysInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, start, count, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities( gl, extensions, parameters ) { let maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl.constructor.name === "WebGL2RenderingContext"; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has( "WEBGL_draw_buffers" ); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); const maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); const maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); const maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); const maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); const maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has( "OES_texture_float" ); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping( properties ) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping ) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function ( planes, camera ) { globalState = projectPlanes( planes, camera, 0 ); }; this.setState = function ( material, camera, useCache ) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get( material ); if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, useCache ); for ( let i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps( renderer ) { let cubemaps = new WeakMap(); function mapTextureMapping( texture, mapping ) { if ( mapping === EquirectangularReflectionMapping ) { texture.mapping = CubeReflectionMapping; } else if ( mapping === EquirectangularRefractionMapping ) { texture.mapping = CubeRefractionMapping; } return texture; } function get( texture ) { if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if ( cubemaps.has( texture ) ) { const cubemap = cubemaps.get( texture ).texture; return mapTextureMapping( cubemap, texture.mapping ); } else { const image = texture.image; if ( image && image.height > 0 ) { const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemap = cubemaps.get( texture ); if ( cubemap !== undefined ) { cubemaps.delete( texture ); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; } setViewOffset( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = ( this.right - this.left ) / ( 2 * this.zoom ); const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); const cx = ( this.right + this.left ) / 2; const cy = ( this.top + this.bottom ) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; // Golden Ratio const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3( 1, 1, 1 ), /*@__PURE__*/ new Vector3( - 1, 1, 1 ), /*@__PURE__*/ new Vector3( 1, 1, - 1 ), /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor( renderer ) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial( this._blurMaterial ); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { _oldTarget = this._renderer.getRenderTarget(); this._setSize( 256 ); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); if ( sigma > 0 ) { this._blur( cubeUVRenderTarget, 0, 0, sigma ); } this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular( equirectangular, renderTarget = null ) { return this._fromTexture( equirectangular, renderTarget ); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap( cubemap, renderTarget = null ) { return this._fromTexture( cubemap, renderTarget ); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial( this._cubemapMaterial ); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial( this._equirectMaterial ); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } // private interface _setSize( cubeSize ) { this._lodMax = Math.floor( Math.log2( cubeSize ) ); this._cubeSize = Math.pow( 2, this._lodMax ); } _dispose() { if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); for ( let i = 0; i < this._lodPlanes.length; i ++ ) { this._lodPlanes[ i ].dispose(); } } _cleanup( outputTarget ) { this._renderer.setRenderTarget( _oldTarget ); outputTarget.scissorTest = false; _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); } _fromTexture( texture, renderTarget ) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); } else { // Equirectangular this._setSize( texture.image.width / 4 ); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV( texture, cubeUVRenderTarget ); this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max( this._cubeSize, 16 * 7 ); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget( width, height, params ); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { if ( this._pingPongRenderTarget !== null ) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget( width, height, params ); const { _lodMax } = this; ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); this._blurMaterial = _getBlurShader( _lodMax, width, height ); } return cubeUVRenderTarget; } _compileMaterial( material ) { const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); this._renderer.compile( tmpMesh, _flatCamera ); } _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); const upSign = [ 1, - 1, 1, 1, 1, 1 ]; const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor( _clearColor ); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial( { name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, } ); const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); let useSolidColor = false; const background = scene.background; if ( background ) { if ( background.isColor ) { backgroundMaterial.color.copy( background ); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy( _clearColor ); useSolidColor = true; } for ( let i = 0; i < 6; i ++ ) { const col = i % 3; if ( col === 0 ) { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); } else if ( col === 1 ) { cubeCamera.up.set( 0, 0, upSign[ i ] ); cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); } else { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); renderer.setRenderTarget( cubeUVRenderTarget ); if ( useSolidColor ) { renderer.render( backgroundBox, cubeCamera ); } renderer.render( scene, cubeCamera ); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV( texture, cubeUVRenderTarget ) { const renderer = this._renderer; const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); if ( isCubeTexture ) { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; } else { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh( this._lodPlanes[ 0 ], material ); const uniforms = material.uniforms; uniforms[ "envMap" ].value = texture; const size = this._cubeSize; _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); renderer.setRenderTarget( cubeUVRenderTarget ); renderer.render( mesh, _flatCamera ); } _applyPMREM( cubeUVRenderTarget ) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for ( let i = 1; i < this._lodPlanes.length; i ++ ) { const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if ( direction !== "latitudinal" && direction !== "longitudinal" ) { console.error( "blur direction must be either latitudinal or longitudinal!" ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[ lodIn ] - 1; const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; if ( samples > MAX_SAMPLES ) { console.warn( `sigmaRadians, ${ sigmaRadians}, is too large and will clip, as it requested ${ samples} samples when the maximum is set to ${MAX_SAMPLES}` ); } const weights = []; let sum = 0; for ( let i = 0; i < MAX_SAMPLES; ++ i ) { const x = i / sigmaPixels; const weight = Math.exp( - x * x / 2 ); weights.push( weight ); if ( i === 0 ) { sum += weight; } else if ( i < samples ) { sum += 2 * weight; } } for ( let i = 0; i < weights.length; i ++ ) { weights[ i ] = weights[ i ] / sum; } blurUniforms[ "envMap" ].value = targetIn.texture; blurUniforms[ "samples" ].value = samples; blurUniforms[ "weights" ].value = weights; blurUniforms[ "latitudinal" ].value = direction === "latitudinal"; if ( poleAxis ) { blurUniforms[ "poleAxis" ].value = poleAxis; } const { _lodMax } = this; blurUniforms[ "dTheta" ].value = radiansPerPixel; blurUniforms[ "mipInt" ].value = _lodMax - lodIn; const outputSize = this._sizeLods[ lodOut ]; const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); const y = 4 * ( this._cubeSize - outputSize ); _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); renderer.setRenderTarget( targetOut ); renderer.render( blurMesh, _flatCamera ); } } function _createPlanes( lodMax ) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for ( let i = 0; i < totalLods; i ++ ) { const sizeLod = Math.pow( 2, lod ); sizeLods.push( sizeLod ); let sigma = 1.0 / sizeLod; if ( i > lodMax - LOD_MIN ) { sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; } else if ( i === 0 ) { sigma = 0; } sigmas.push( sigma ); const texelSize = 1.0 / ( sizeLod - 2 ); const min = - texelSize; const max = 1 + texelSize; const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array( positionSize * vertices * cubeFaces ); const uv = new Float32Array( uvSize * vertices * cubeFaces ); const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); for ( let face = 0; face < cubeFaces; face ++ ) { const x = ( face % 3 ) * 2 / 3 - 1; const y = face > 2 ? 0 : - 1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0 ]; position.set( coordinates, positionSize * vertices * face ); uv.set( uv1, uvSize * vertices * face ); const fill = [ face, face, face, face, face, face ]; faceIndex.set( fill, faceIndexSize * vertices * face ); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute( position, positionSize ) ); planes.setAttribute( "uv", new BufferAttribute( uv, uvSize ) ); planes.setAttribute( "faceIndex", new BufferAttribute( faceIndex, faceIndexSize ) ); lodPlanes.push( planes ); if ( lod > LOD_MIN ) { lod --; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget( width, height, params ) { const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport( target, x, y, width, height ) { target.viewport.set( x, y, width, height ); target.scissor.set( x, y, width, height ); } function _getBlurShader( lodMax, width, height ) { const weights = new Float32Array( MAX_SAMPLES ); const poleAxis = new Vector3( 0, 1, 0 ); const shaderMaterial = new ShaderMaterial( { name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0`, }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false } ); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial( { name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCubemapMaterial() { return new ShaderMaterial( { name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: - 1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCommonVertexShader() { return /* glsl */` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps( renderer ) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get( texture ) { if ( texture && texture.isTexture ) { const mapping = texture.mapping; const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); // equirect/cube map to cubeUV conversion if ( isEquirectMap || isCubeMap ) { if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get( texture ); if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); cubeUVmaps.set( texture, renderTarget ); return renderTarget.texture; } else { if ( cubeUVmaps.has( texture ) ) { return cubeUVmaps.get( texture ).texture; } else { const image = texture.image; if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); cubeUVmaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete( image ) { let count = 0; const length = 6; for ( let i = 0; i < length; i ++ ) { if ( image[ i ] !== undefined ) count ++; } return count === length; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemapUV = cubeUVmaps.get( texture ); if ( cubemapUV !== undefined ) { cubeUVmaps.delete( texture ); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if ( pmremGenerator !== null ) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions( gl ) { const extensions = {}; function getExtension( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } let extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } extensions[ name ] = extension; return extension; } return { has: function ( name ) { return getExtension( name ) !== null; }, init: function ( capabilities ) { if ( capabilities.isWebGL2 ) { getExtension( "EXT_color_buffer_float" ); } else { getExtension( "WEBGL_depth_texture" ); getExtension( "OES_texture_float" ); getExtension( "OES_texture_half_float" ); getExtension( "OES_texture_half_float_linear" ); getExtension( "OES_standard_derivatives" ); getExtension( "OES_element_index_uint" ); getExtension( "OES_vertex_array_object" ); getExtension( "ANGLE_instanced_arrays" ); } getExtension( "OES_texture_float_linear" ); getExtension( "EXT_color_buffer_half_float" ); getExtension( "WEBGL_multisampled_render_to_texture" ); }, get: function ( name ) { const extension = getExtension( name ); if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } return extension; } }; } function WebGLGeometries( gl, attributes, info, bindingStates ) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose( event ) { const geometry = event.target; if ( geometry.index !== null ) { attributes.remove( geometry.index ); } for ( const name in geometry.attributes ) { attributes.remove( geometry.attributes[ name ] ); } for ( const name in geometry.morphAttributes ) { const array = geometry.morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.remove( array[ i ] ); } } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; const attribute = wireframeAttributes.get( geometry ); if ( attribute ) { attributes.remove( attribute ); wireframeAttributes.delete( geometry ); } bindingStates.releaseStatesOfGeometry( geometry ); if ( geometry.isInstancedBufferGeometry === true ) { delete geometry._maxInstanceCount; } // info.memory.geometries --; } function get( object, geometry ) { if ( geometries[ geometry.id ] === true ) return geometry; geometry.addEventListener( "dispose", onGeometryDispose ); geometries[ geometry.id ] = true; info.memory.geometries ++; return geometry; } function update( geometry ) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for ( const name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets const morphAttributes = geometry.morphAttributes; for ( const name in morphAttributes ) { const array = morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function updateWireframeAttribute( geometry ) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if ( geometryIndex !== null ) { const array = geometryIndex.array; version = geometryIndex.version; for ( let i = 0, l = array.length; i < l; i += 3 ) { const a = array[ i + 0 ]; const b = array[ i + 1 ]; const c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else if ( geometryPosition !== undefined ) { const array = geometryPosition.array; version = geometryPosition.version; for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push( a, b, b, c, c, a ); } } else { return; } const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get( geometry ); if ( previousAttribute ) attributes.remove( previousAttribute ); // wireframeAttributes.set( geometry, attribute ); } function getWireframeAttribute( geometry ) { const currentAttribute = wireframeAttributes.get( geometry ); if ( currentAttribute ) { const geometryIndex = geometry.index; if ( geometryIndex !== null ) { // if the attribute is obsolete, create a new one if ( currentAttribute.version < geometryIndex.version ) { updateWireframeAttribute( geometry ); } } } else { updateWireframeAttribute( geometry ); } return wireframeAttributes.get( geometry ); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } let type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawElementsInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo( gl ) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { render.calls ++; switch ( mode ) { case gl.TRIANGLES: render.triangles += instanceCount * ( count / 3 ); break; case gl.LINES: render.lines += instanceCount * ( count / 2 ); break; case gl.LINE_STRIP: render.lines += instanceCount * ( count - 1 ); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } function numericalSort( a, b ) { return a[ 0 ] - b[ 0 ]; } function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl, capabilities, textures ) { const influencesList = {}; const morphInfluences = new Float32Array( 8 ); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for ( let i = 0; i < 8; i ++ ) { workInfluences[ i ] = [ i, 0 ]; } function update( object, geometry, program ) { const objectInfluences = object.morphTargetInfluences; if ( capabilities.isWebGL2 === true ) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let entry = morphTextures.get( geometry ); if ( entry === undefined || entry.count !== morphTargetsCount ) { if ( entry !== undefined ) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if ( hasMorphPosition === true ) vertexDataCount = 1; if ( hasMorphNormals === true ) vertexDataCount = 2; if ( hasMorphColors === true ) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if ( width > capabilities.maxTextureSize ) { height = Math.ceil( width / capabilities.maxTextureSize ); width = capabilities.maxTextureSize; } const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for ( let i = 0; i < morphTargetsCount; i ++ ) { const morphTarget = morphTargets[ i ]; const morphNormal = morphNormals[ i ]; const morphColor = morphColors[ i ]; const offset = width * height * 4 * i; for ( let j = 0; j < morphTarget.count; j ++ ) { const stride = j * vertexDataStride; if ( hasMorphPosition === true ) { morph.fromBufferAttribute( morphTarget, j ); buffer[ offset + stride + 0 ] = morph.x; buffer[ offset + stride + 1 ] = morph.y; buffer[ offset + stride + 2 ] = morph.z; buffer[ offset + stride + 3 ] = 0; } if ( hasMorphNormals === true ) { morph.fromBufferAttribute( morphNormal, j ); buffer[ offset + stride + 4 ] = morph.x; buffer[ offset + stride + 5 ] = morph.y; buffer[ offset + stride + 6 ] = morph.z; buffer[ offset + stride + 7 ] = 0; } if ( hasMorphColors === true ) { morph.fromBufferAttribute( morphColor, j ); buffer[ offset + stride + 8 ] = morph.x; buffer[ offset + stride + 9 ] = morph.y; buffer[ offset + stride + 10 ] = morph.z; buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2( width, height ) }; morphTextures.set( geometry, entry ); function disposeTexture() { texture.dispose(); morphTextures.delete( geometry ); geometry.removeEventListener( "dispose", disposeTexture ); } geometry.addEventListener( "dispose", disposeTexture ); } // let morphInfluencesSum = 0; for ( let i = 0; i < objectInfluences.length; i ++ ) { morphInfluencesSum += objectInfluences[ i ]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", objectInfluences ); program.getUniforms().setValue( gl, "morphTargetsTexture", entry.texture, textures ); program.getUniforms().setValue( gl, "morphTargetsTextureSize", entry.size ); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[ geometry.id ]; if ( influences === undefined || influences.length !== length ) { // initialise list influences = []; for ( let i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } // Collect influences for ( let i = 0; i < length; i ++ ) { const influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); for ( let i = 0; i < 8; i ++ ) { if ( i < length && influences[ i ][ 1 ] ) { workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; } else { workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; workInfluences[ i ][ 1 ] = 0; } } workInfluences.sort( numericalSort ); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for ( let i = 0; i < 8; i ++ ) { const influence = workInfluences[ i ]; const index = influence[ 0 ]; const value = influence[ 1 ]; if ( index !== Number.MAX_SAFE_INTEGER && value ) { if ( morphTargets && geometry.getAttribute( "morphTarget" + i ) !== morphTargets[ index ] ) { geometry.setAttribute( "morphTarget" + i, morphTargets[ index ] ); } if ( morphNormals && geometry.getAttribute( "morphNormal" + i ) !== morphNormals[ index ] ) { geometry.setAttribute( "morphNormal" + i, morphNormals[ index ] ); } morphInfluences[ i ] = value; morphInfluencesSum += value; } else { if ( morphTargets && geometry.hasAttribute( "morphTarget" + i ) === true ) { geometry.deleteAttribute( "morphTarget" + i ); } if ( morphNormals && geometry.hasAttribute( "morphNormal" + i ) === true ) { geometry.deleteAttribute( "morphNormal" + i ); } morphInfluences[ i ] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } } return { update: update }; } function WebGLObjects( gl, geometries, attributes, info ) { let updateMap = new WeakMap(); function update( object ) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateMap.get( buffergeometry ) !== frame ) { geometries.update( buffergeometry ); updateMap.set( buffergeometry, frame ); } if ( object.isInstancedMesh ) { if ( object.hasEventListener( "dispose", onInstancedMeshDispose ) === false ) { object.addEventListener( "dispose", onInstancedMeshDispose ); } if ( updateMap.get( object ) !== frame ) { attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); if ( object.instanceColor !== null ) { attributes.update( object.instanceColor, gl.ARRAY_BUFFER ); } updateMap.set( object, frame ); } } if ( object.isSkinnedMesh ) { const skeleton = object.skeleton; if ( updateMap.get( skeleton ) !== frame ) { skeleton.update(); updateMap.set( skeleton, frame ); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose( event ) { const instancedMesh = event.target; instancedMesh.removeEventListener( "dispose", onInstancedMeshDispose ); attributes.remove( instancedMesh.instanceMatrix ); if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); } return { update: update, dispose: dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array( 16 ); const mat3array = new Float32Array( 9 ); const mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { const firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( let i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( let i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( textures, n ) { let r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( let i = 0; i !== n; ++ i ) { r[ i ] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValueV3f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValueV4f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValueM3( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValueM4( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single integer / boolean function setValueV1i( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2i( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } } function setValueV3i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3i( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } } function setValueV4i( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4i( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } } // Single unsigned integer function setValueV1ui( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1ui( this.addr, v ); cache[ 0 ] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2ui( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV3ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3ui( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3uiv( this.addr, v ); copyArray( cache, v ); } } function setValueV4ui( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4ui( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4uiv( this.addr, v ); copyArray( cache, v ); } } // Single texture (2D / Cube) function setValueT1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2D( v || emptyTexture, unit ); } function setValueT3D1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture3D( v || empty3dTexture, unit ); } function setValueT6( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTextureCube( v || emptyCubeTexture, unit ); } function setValueT2DArray1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2DArray( v || emptyArrayTexture, unit ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray( gl, v ) { gl.uniform1fv( this.addr, v ); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray( gl, v ) { const data = flatten( v, this.size, 2 ); gl.uniform2fv( this.addr, data ); } function setValueV3fArray( gl, v ) { const data = flatten( v, this.size, 3 ); gl.uniform3fv( this.addr, data ); } function setValueV4fArray( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniform4fv( this.addr, data ); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniformMatrix2fv( this.addr, false, data ); } function setValueM3Array( gl, v ) { const data = flatten( v, this.size, 9 ); gl.uniformMatrix3fv( this.addr, false, data ); } function setValueM4Array( gl, v ) { const data = flatten( v, this.size, 16 ); gl.uniformMatrix4fv( this.addr, false, data ); } // Array of integer / boolean function setValueV1iArray( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValueV3iArray( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValueV4iArray( gl, v ) { gl.uniform4iv( this.addr, v ); } // Array of unsigned integer function setValueV1uiArray( gl, v ) { gl.uniform1uiv( this.addr, v ); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray( gl, v ) { gl.uniform2uiv( this.addr, v ); } function setValueV3uiArray( gl, v ) { gl.uniform3uiv( this.addr, v ); } function setValueV4uiArray( gl, v ) { gl.uniform4uiv( this.addr, v ); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT3DArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); } } function setValueT6Array( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } function setValueT2DArrayArray( gl, v, textures ) { const cache = this.cache; const n = v.length; const units = allocTexUnits( textures, n ); if ( ! arraysEqual( cache, units ) ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( let i = 0; i !== n; ++ i ) { textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor( id ) { this.id = id; this.seq = []; this.map = {}; } setValue( gl, value, textures ) { const seq = this.seq; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; u.setValue( gl, value[ u.id ], textures ); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { const match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex; let id = match[ 1 ]; const idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container class WebGLUniforms { constructor( gl, program ) { this.seq = []; this.map = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( let i = 0; i < n; ++ i ) { const info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } setValue( gl, name, value, textures ) { const u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, textures ); } setOptional( gl, object, name ) { const v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); } static upload( gl, seq, values, textures ) { for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, textures ); } } } static seqWithValue( seq, values ) { const r = []; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; } } function WebGLShader( gl, type, string ) { const shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); return shader; } let programIdCount = 0; function handleSource( string, errorLine ) { const lines = string.split( " " ); const lines2 = []; const from = Math.max( errorLine - 6, 0 ); const to = Math.min( errorLine + 6, lines.length ); for ( let i = from; i < to; i ++ ) { const line = i + 1; lines2.push( `${line === errorLine ? ">" : " "} ${line}: ${lines[ i ]}` ); } return lines2.join( " " ); } function getEncodingComponents( colorSpace ) { switch ( colorSpace ) { case LinearSRGBColorSpace: return [ "Linear", "( value )" ]; case SRGBColorSpace: return [ "sRGB", "( value )" ]; default: console.warn( "THREE.WebGLProgram: Unsupported color space:", colorSpace ); return [ "Linear", "( value )" ]; } } function getShaderErrors( gl, shader, type ) { const status = gl.getShaderParameter( shader, gl.COMPILE_STATUS ); const errors = gl.getShaderInfoLog( shader ).trim(); if ( status && errors === "" ) return ""; const errorMatches = /ERROR: 0:(d+)/.exec( errors ); if ( errorMatches ) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt( errorMatches[ 1 ] ); return type.toUpperCase() + " " + errors + " " + handleSource( gl.getShaderSource( shader ), errorLine ); } else { return errors; } } function getTexelEncodingFunction( functionName, colorSpace ) { const components = getEncodingComponents( colorSpace ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { let toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn( "THREE.WebGLProgram: Unsupported toneMapping:", toneMapping ); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( parameters ) { const chunks = [ ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? "#extension GL_EXT_draw_buffers : require" : "", ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { const chunks = []; for ( const name in defines ) { const value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { const attributes = {}; const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( let i = 0; i < n; i ++ ) { const info = gl.getActiveAttrib( program, i ); const name = info.name; let locationSize = 1; if ( info.type === gl.FLOAT_MAT2 ) locationSize = 2; if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3; if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = { type: info.type, location: gl.getAttribLocation( program, name ), locationSize: locationSize }; } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps ) .replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps ) .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes( string ) { return string.replace( includePattern, includeReplacer ); } const shaderChunkMap = new Map( [ [ "encodings_fragment", "colorspace_fragment" ], // @deprecated, r154 [ "encodings_pars_fragment", "colorspace_pars_fragment" ], // @deprecated, r154 [ "output_fragment", "opaque_fragment" ], // @deprecated, r154 ] ); function includeReplacer( match, include ) { let string = ShaderChunk[ include ]; if ( string === undefined ) { const newInclude = shaderChunkMap.get( include ); if ( newInclude !== undefined ) { string = ShaderChunk[ newInclude ]; console.warn( "THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.", include, newInclude ); } else { throw new Error( "Can not resolve #include <" + include + ">" ); } } return resolveIncludes( string ); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision( parameters ), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.displacementMap ? "#define USE_DISPLACEMENTMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", // parameters.mapUv ? "#define MAP_UV " + parameters.mapUv : "", parameters.alphaMapUv ? "#define ALPHAMAP_UV " + parameters.alphaMapUv : "", parameters.lightMapUv ? "#define LIGHTMAP_UV " + parameters.lightMapUv : "", parameters.aoMapUv ? "#define AOMAP_UV " + parameters.aoMapUv : "", parameters.emissiveMapUv ? "#define EMISSIVEMAP_UV " + parameters.emissiveMapUv : "", parameters.bumpMapUv ? "#define BUMPMAP_UV " + parameters.bumpMapUv : "", parameters.normalMapUv ? "#define NORMALMAP_UV " + parameters.normalMapUv : "", parameters.displacementMapUv ? "#define DISPLACEMENTMAP_UV " + parameters.displacementMapUv : "", parameters.metalnessMapUv ? "#define METALNESSMAP_UV " + parameters.metalnessMapUv : "", parameters.roughnessMapUv ? "#define ROUGHNESSMAP_UV " + parameters.roughnessMapUv : "", parameters.anisotropyMapUv ? "#define ANISOTROPYMAP_UV " + parameters.anisotropyMapUv : "", parameters.clearcoatMapUv ? "#define CLEARCOATMAP_UV " + parameters.clearcoatMapUv : "", parameters.clearcoatNormalMapUv ? "#define CLEARCOAT_NORMALMAP_UV " + parameters.clearcoatNormalMapUv : "", parameters.clearcoatRoughnessMapUv ? "#define CLEARCOAT_ROUGHNESSMAP_UV " + parameters.clearcoatRoughnessMapUv : "", parameters.iridescenceMapUv ? "#define IRIDESCENCEMAP_UV " + parameters.iridescenceMapUv : "", parameters.iridescenceThicknessMapUv ? "#define IRIDESCENCE_THICKNESSMAP_UV " + parameters.iridescenceThicknessMapUv : "", parameters.sheenColorMapUv ? "#define SHEEN_COLORMAP_UV " + parameters.sheenColorMapUv : "", parameters.sheenRoughnessMapUv ? "#define SHEEN_ROUGHNESSMAP_UV " + parameters.sheenRoughnessMapUv : "", parameters.specularMapUv ? "#define SPECULARMAP_UV " + parameters.specularMapUv : "", parameters.specularColorMapUv ? "#define SPECULAR_COLORMAP_UV " + parameters.specularColorMapUv : "", parameters.specularIntensityMapUv ? "#define SPECULAR_INTENSITYMAP_UV " + parameters.specularIntensityMapUv : "", parameters.transmissionMapUv ? "#define TRANSMISSIONMAP_UV " + parameters.transmissionMapUv : "", parameters.thicknessMapUv ? "#define THICKNESSMAP_UV " + parameters.thicknessMapUv : "", // parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", ( parameters.morphColors && parameters.isWebGL2 ) ? "#define USE_MORPHCOLORS" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.useLegacyLights ? "#define LEGACY_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_UV1", " attribute vec2 uv1;", "#endif", "#ifdef USE_UV2", " attribute vec2 uv2;", "#endif", "#ifdef USE_UV3", " attribute vec2 uv3;", "#endif", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, generatePrecision( parameters ), "#define SHADER_TYPE " + parameters.shaderType, "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMapObjectSpace ? "#define USE_NORMALMAP_OBJECTSPACE" : "", parameters.normalMapTangentSpace ? "#define USE_NORMALMAP_TANGENTSPACE" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.anisotropy ? "#define USE_ANISOTROPY" : "", parameters.anisotropyMap ? "#define USE_ANISOTROPYMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularColorMap ? "#define USE_SPECULAR_COLORMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULAR_INTENSITYMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.alphaHash ? "#define USE_ALPHAHASH" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEEN_COLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEEN_ROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.vertexTangents && parameters.flatShading === false ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUv1s ? "#define USE_UV1" : "", parameters.vertexUv2s ? "#define USE_UV2" : "", parameters.vertexUv3s ? "#define USE_UV3" : "", parameters.pointsUvs ? "#define USE_POINTS_UV" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.useLegacyLights ? "#define LEGACY_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk[ "colorspace_pars_fragment" ], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputColorSpace ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = resolveIncludes( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = resolveIncludes( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ "precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture" ].join( " " ) + " " + prefixVertex; prefixFragment = [ "#define varying in", ( parameters.glslVersion === GLSL3 ) ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", ( parameters.glslVersion === GLSL3 ) ? "" : "#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( " " ) + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( parameters.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); // check for link errors if ( renderer.debug.checkShaderErrors ) { const programLog = gl.getProgramInfoLog( program ).trim(); const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); let runnable = true; let haveDiagnostics = true; if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; if ( typeof renderer.debug.onShaderError === "function" ) { renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader ); } else { // default error reporting const vertexErrors = getShaderErrors( gl, glVertexShader, "vertex" ); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment" ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors ); } } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: Program Info Log:", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program ); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram( this ); gl.deleteProgram( program ); this.program = undefined; }; // this.type = parameters.shaderType; this.name = parameters.shaderName; this.id = programIdCount ++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update( material ) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage( vertexShader ); const fragmentShaderStage = this._getShaderStage( fragmentShader ); const materialShaders = this._getShaderCacheForMaterial( material ); if ( materialShaders.has( vertexShaderStage ) === false ) { materialShaders.add( vertexShaderStage ); vertexShaderStage.usedTimes ++; } if ( materialShaders.has( fragmentShaderStage ) === false ) { materialShaders.add( fragmentShaderStage ); fragmentShaderStage.usedTimes ++; } return this; } remove( material ) { const materialShaders = this.materialCache.get( material ); for ( const shaderStage of materialShaders ) { shaderStage.usedTimes --; if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); } this.materialCache.delete( material ); return this; } getVertexShaderID( material ) { return this._getShaderStage( material.vertexShader ).id; } getFragmentShaderID( material ) { return this._getShaderStage( material.fragmentShader ).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial( material ) { const cache = this.materialCache; let set = cache.get( material ); if ( set === undefined ) { set = new Set(); cache.set( material, set ); } return set; } _getShaderStage( code ) { const cache = this.shaderCache; let stage = cache.get( code ); if ( stage === undefined ) { stage = new WebGLShaderStage( code ); cache.set( code, stage ); } return stage; } } class WebGLShaderStage { constructor( code ) { this.id = _id ++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const IS_WEBGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getChannel( value ) { if ( value === 0 ) return "uv"; return `uv${ value }`; } function getParameters( material, lights, shadows, scene, object ) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; const shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let morphTextureStride = 0; if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update( material ); customVertexShaderID = _customShaders.getVertexShaderID( material ); customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const HAS_MAP = !! material.map; const HAS_MATCAP = !! material.matcap; const HAS_ENVMAP = !! envMap; const HAS_AOMAP = !! material.aoMap; const HAS_LIGHTMAP = !! material.lightMap; const HAS_BUMPMAP = !! material.bumpMap; const HAS_NORMALMAP = !! material.normalMap; const HAS_DISPLACEMENTMAP = !! material.displacementMap; const HAS_EMISSIVEMAP = !! material.emissiveMap; const HAS_METALNESSMAP = !! material.metalnessMap; const HAS_ROUGHNESSMAP = !! material.roughnessMap; const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap; const HAS_SPECULARMAP = !! material.specularMap; const HAS_SPECULAR_COLORMAP = !! material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap; const HAS_GRADIENTMAP = !! material.gradientMap; const HAS_ALPHAMAP = !! material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_ALPHAHASH = !! material.alphaHash; const HAS_EXTENSIONS = !! material.extensions; const HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1; const HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2; const HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3; let toneMapping = NoToneMapping; if ( material.toneMapped ) { if ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) { toneMapping = renderer.toneMapping; } } const parameters = { isWebGL2: IS_WEBGL2, shaderID: shaderID, shaderType: material.type, shaderName: material.name, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, anisotropy: HAS_ANISOTROPY, anisotropyMap: HAS_ANISOTROPYMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, alphaHash: HAS_ALPHAHASH, combine: material.combine, // mapUv: HAS_MAP && getChannel( material.map.channel ), aoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ), lightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ), bumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ), normalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ), emissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ), metalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ), anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ), specularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ), thicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ), alphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ), // vertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUv1s: HAS_ATTRIBUTE_UV1, vertexUv2s: HAS_ATTRIBUTE_UV2, vertexUv3s: HAS_ATTRIBUTE_UV3, pointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ), fog: !! fog, useFog: material.fog === true, fogExp2: ( fog && fog.isFogExp2 ), flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: toneMapping, useLegacyLights: renderer._useLegacyLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true, extensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true, extensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true, extensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true, rendererExtensionFragDepth: IS_WEBGL2 || extensions.has( "EXT_frag_depth" ), rendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( "WEBGL_draw_buffers" ), rendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( "EXT_shader_texture_lod" ), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey( parameters ) { const array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( parameters.customVertexShaderID ); array.push( parameters.customFragmentShaderID ); } if ( parameters.defines !== undefined ) { for ( const name in parameters.defines ) { array.push( name ); array.push( parameters.defines[ name ] ); } } if ( parameters.isRawShaderMaterial === false ) { getProgramCacheKeyParameters( array, parameters ); getProgramCacheKeyBooleans( array, parameters ); array.push( renderer.outputColorSpace ); } array.push( parameters.customProgramCacheKey ); return array.join(); } function getProgramCacheKeyParameters( array, parameters ) { array.push( parameters.precision ); array.push( parameters.outputColorSpace ); array.push( parameters.envMapMode ); array.push( parameters.envMapCubeUVHeight ); array.push( parameters.mapUv ); array.push( parameters.alphaMapUv ); array.push( parameters.lightMapUv ); array.push( parameters.aoMapUv ); array.push( parameters.bumpMapUv ); array.push( parameters.normalMapUv ); array.push( parameters.displacementMapUv ); array.push( parameters.emissiveMapUv ); array.push( parameters.metalnessMapUv ); array.push( parameters.roughnessMapUv ); array.push( parameters.anisotropyMapUv ); array.push( parameters.clearcoatMapUv ); array.push( parameters.clearcoatNormalMapUv ); array.push( parameters.clearcoatRoughnessMapUv ); array.push( parameters.iridescenceMapUv ); array.push( parameters.iridescenceThicknessMapUv ); array.push( parameters.sheenColorMapUv ); array.push( parameters.sheenRoughnessMapUv ); array.push( parameters.specularMapUv ); array.push( parameters.specularColorMapUv ); array.push( parameters.specularIntensityMapUv ); array.push( parameters.transmissionMapUv ); array.push( parameters.thicknessMapUv ); array.push( parameters.combine ); array.push( parameters.fogExp2 ); array.push( parameters.sizeAttenuation ); array.push( parameters.morphTargetsCount ); array.push( parameters.morphAttributeCount ); array.push( parameters.numDirLights ); array.push( parameters.numPointLights ); array.push( parameters.numSpotLights ); array.push( parameters.numSpotLightMaps ); array.push( parameters.numHemiLights ); array.push( parameters.numRectAreaLights ); array.push( parameters.numDirLightShadows ); array.push( parameters.numPointLightShadows ); array.push( parameters.numSpotLightShadows ); array.push( parameters.numSpotLightShadowsWithMaps ); array.push( parameters.shadowMapType ); array.push( parameters.toneMapping ); array.push( parameters.numClippingPlanes ); array.push( parameters.numClipIntersection ); array.push( parameters.depthPacking ); } function getProgramCacheKeyBooleans( array, parameters ) { _programLayers.disableAll(); if ( parameters.isWebGL2 ) _programLayers.enable( 0 ); if ( parameters.supportsVertexTextures ) _programLayers.enable( 1 ); if ( parameters.instancing ) _programLayers.enable( 2 ); if ( parameters.instancingColor ) _programLayers.enable( 3 ); if ( parameters.matcap ) _programLayers.enable( 4 ); if ( parameters.envMap ) _programLayers.enable( 5 ); if ( parameters.normalMapObjectSpace ) _programLayers.enable( 6 ); if ( parameters.normalMapTangentSpace ) _programLayers.enable( 7 ); if ( parameters.clearcoat ) _programLayers.enable( 8 ); if ( parameters.iridescence ) _programLayers.enable( 9 ); if ( parameters.alphaTest ) _programLayers.enable( 10 ); if ( parameters.vertexColors ) _programLayers.enable( 11 ); if ( parameters.vertexAlphas ) _programLayers.enable( 12 ); if ( parameters.vertexUv1s ) _programLayers.enable( 13 ); if ( parameters.vertexUv2s ) _programLayers.enable( 14 ); if ( parameters.vertexUv3s ) _programLayers.enable( 15 ); if ( parameters.vertexTangents ) _programLayers.enable( 16 ); if ( parameters.anisotropy ) _programLayers.enable( 17 ); array.push( _programLayers.mask ); _programLayers.disableAll(); if ( parameters.fog ) _programLayers.enable( 0 ); if ( parameters.useFog ) _programLayers.enable( 1 ); if ( parameters.flatShading ) _programLayers.enable( 2 ); if ( parameters.logarithmicDepthBuffer ) _programLayers.enable( 3 ); if ( parameters.skinning ) _programLayers.enable( 4 ); if ( parameters.morphTargets ) _programLayers.enable( 5 ); if ( parameters.morphNormals ) _programLayers.enable( 6 ); if ( parameters.morphColors ) _programLayers.enable( 7 ); if ( parameters.premultipliedAlpha ) _programLayers.enable( 8 ); if ( parameters.shadowMapEnabled ) _programLayers.enable( 9 ); if ( parameters.useLegacyLights ) _programLayers.enable( 10 ); if ( parameters.doubleSided ) _programLayers.enable( 11 ); if ( parameters.flipSided ) _programLayers.enable( 12 ); if ( parameters.useDepthPacking ) _programLayers.enable( 13 ); if ( parameters.dithering ) _programLayers.enable( 14 ); if ( parameters.transmission ) _programLayers.enable( 15 ); if ( parameters.sheen ) _programLayers.enable( 16 ); if ( parameters.opaque ) _programLayers.enable( 17 ); if ( parameters.pointsUvs ) _programLayers.enable( 18 ); array.push( _programLayers.mask ); } function getUniforms( material ) { const shaderID = shaderIDs[ material.type ]; let uniforms; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; uniforms = UniformsUtils.clone( shader.uniforms ); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram( parameters, cacheKey ) { let program; // Check if code has been already compiled for ( let p = 0, pl = programs.length; p < pl; p ++ ) { const preexistingProgram = programs[ p ]; if ( preexistingProgram.cacheKey === cacheKey ) { program = preexistingProgram; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); programs.push( program ); } return program; } function releaseProgram( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set const i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache( material ) { _customShaders.remove( material ); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get( object ) { let map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { let renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex ++; return renderItem; } function push( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.push( renderItem ); } else if ( material.transparent === true ) { transparent.push( renderItem ); } else { opaque.push( renderItem ); } } function unshift( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.unshift( renderItem ); } else if ( material.transparent === true ) { transparent.unshift( renderItem ); } else { opaque.unshift( renderItem ); } } function sort( customOpaqueSort, customTransparentSort ) { if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); } function finish() { // Clear references from inactive renderItems in the list for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { const renderItem = renderItems[ i ]; if ( renderItem.id === null ) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get( scene, renderCallDepth ) { const listArray = lists.get( scene ); let list; if ( listArray === undefined ) { list = new WebGLRenderList(); lists.set( scene, [ list ] ); } else { if ( renderCallDepth >= listArray.length ) { list = new WebGLRenderList(); listArray.push( list ); } else { list = listArray[ renderCallDepth ]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[ light.id ] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst( lightA, lightB ) { return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 ); } function WebGLLights( extensions, capabilities ) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: - 1, pointLength: - 1, spotLength: - 1, rectAreaLength: - 1, hemiLength: - 1, numDirectionalShadows: - 1, numPointShadows: - 1, numSpotShadows: - 1, numSpotMaps: - 1 }, ambient: [ 0, 0, 0 ], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0 }; for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup( lights, useLegacyLights ) { let r = 0, g = 0, b = 0; for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort( shadowCastingAndTexturingLightsFirst ); // artist-friendly light intensity scaling factor const scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if ( light.isLightProbe ) { for ( let j = 0; j < 9; j ++ ) { state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); } } else if ( light.isDirectionalLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[ directionalLength ] = shadowUniforms; state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; numDirectionalShadows ++; } state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); uniforms.distance = distance; uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = light.decay; state.spot[ spotLength ] = uniforms; const shadow = light.shadow; if ( light.map ) { state.spotLightMap[ numSpotMaps ] = light.map; numSpotMaps ++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices( light ); if ( light.castShadow ) numSpotShadowsWithMaps ++; } state.spotLightMatrix[ spotLength ] = shadow.matrix; if ( light.castShadow ) { const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[ spotLength ] = shadowUniforms; state.spotShadowMap[ spotLength ] = shadowMap; numSpotShadows ++; } spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = cache.get( light ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); uniforms.distance = light.distance; uniforms.decay = light.decay; if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[ pointLength ] = shadowUniforms; state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; numPointShadows ++; } state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = cache.get( light ); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } if ( rectAreaLength > 0 ) { if ( capabilities.isWebGL2 ) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if ( extensions.has( "OES_texture_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if ( extensions.has( "OES_texture_half_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error( "THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions." ); } } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; state.version = nextVersion ++; } } function setupView( lights, camera ) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; if ( light.isDirectionalLight ) { const uniforms = state.directional[ directionalLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = state.spot[ spotLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = state.rectArea[ rectAreaLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = state.point[ pointLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = state.hemi[ hemiLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); hemiLength ++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState( extensions, capabilities ) { const lights = new WebGLLights( extensions, capabilities ); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function setupLights( useLegacyLights ) { lights.setup( lightsArray, useLegacyLights ); } function setupLightsView( camera ) { lights.setupView( lightsArray, camera ); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates( extensions, capabilities ) { let renderStates = new WeakMap(); function get( scene, renderCallDepth = 0 ) { const renderStateArray = renderStates.get( scene ); let renderState; if ( renderStateArray === undefined ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStates.set( scene, [ renderState ] ); } else { if ( renderCallDepth >= renderStateArray.length ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStateArray.push( renderState ); } else { renderState = renderStateArray[ renderCallDepth ]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } class MeshDepthMaterial extends Material { constructor( parameters ) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor( parameters ) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap( _renderer, _objects, _capabilities ) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial( { defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment } ); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), 3 ) ); const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending( NoBlending ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // check for shadow map type changes const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap ); const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap ); // render depth map for ( let i = 0, il = lights.length; i < il; i ++ ) { const light = lights[ i ]; const shadow = light.shadow; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; _shadowMapSize.copy( shadow.mapSize ); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply( shadowFrameExtents ); _viewportSize.copy( shadow.mapSize ); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if ( _shadowMapSize.x > _maxTextureSize ) { _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if ( _shadowMapSize.y > _maxTextureSize ) { _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if ( shadow.map === null || toVSM === true || fromVSM === true ) { const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; if ( shadow.map !== null ) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget( shadow.map ); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for ( let vp = 0; vp < viewportCount; vp ++ ) { const viewport = shadow.getViewport( vp ); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w ); _state.viewport( _viewport ); shadow.updateMatrices( light, vp ); _frustum = shadow.getFrustum(); renderObject( scene, camera, shadow.camera, light, this.type ); } // do blur pass for VSM if ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) { VSMPass( shadow, camera ); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); }; function VSMPass( shadow, camera ) { const geometry = _objects.update( fullScreenMesh ); if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if ( shadow.mapPass === null ) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.mapPass ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.map ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); } function getDepthMaterial( object, material, light, type ) { let result = null; const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; if ( customMaterial !== undefined ) { result = customMaterial; } else { result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; if ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) || ( material.displacementMap && material.displacementScale !== 0 ) || ( material.alphaMap && material.alphaTest > 0 ) || ( material.map && material.alphaTest > 0 ) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } let cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if ( type === VSMShadowMap ) { result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; } else { result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { const materialProperties = _renderer.properties.get( result ); materialProperties.light = light; } return result; } function renderObject( object, camera, shadowCamera, light, type ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); const geometry = _objects.update( object ); const material = object.material; if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let k = 0, kl = groups.length; k < kl; k ++ ) { const group = groups[ k ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { const depthMaterial = getDepthMaterial( object, material, light, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, light, type ); } } } function WebGLState( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( ! locked ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter( gl.VERSION ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); const viewportParam = gl.getParameter( gl.VIEWPORT ); const currentScissor = new Vector4().fromArray( scissorParam ); const currentViewport = new Vector4().fromArray( viewportParam ); function createTexture( type, target, count, dimensions ) { const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( let i = 0; i < count; i ++ ) { if ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) { gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } else { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } } return texture; } const emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); if ( isWebGL2 ) { emptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 ); emptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 ); } // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); setBlending( NoBlending ); // function enable( id ) { if ( enabledCapabilities[ id ] !== true ) { gl.enable( id ); enabledCapabilities[ id ] = true; } } function disable( id ) { if ( enabledCapabilities[ id ] !== false ) { gl.disable( id ); enabledCapabilities[ id ] = false; } } function bindFramebuffer( target, framebuffer ) { if ( currentBoundFramebuffers[ target ] !== framebuffer ) { gl.bindFramebuffer( target, framebuffer ); currentBoundFramebuffers[ target ] = framebuffer; if ( isWebGL2 ) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if ( target === gl.DRAW_FRAMEBUFFER ) { currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer; } if ( target === gl.FRAMEBUFFER ) { currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer; } } return true; } return false; } function drawBuffers( renderTarget, framebuffer ) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if ( renderTarget ) { drawBuffers = currentDrawbuffers.get( framebuffer ); if ( drawBuffers === undefined ) { drawBuffers = []; currentDrawbuffers.set( framebuffer, drawBuffers ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { const textures = renderTarget.texture; if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { for ( let i = 0, il = textures.length; i < il; i ++ ) { drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { drawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if ( drawBuffers[ 0 ] !== gl.BACK ) { drawBuffers[ 0 ] = gl.BACK; needsUpdate = true; } } if ( needsUpdate ) { if ( capabilities.isWebGL2 ) { gl.drawBuffers( drawBuffers ); } else { extensions.get( "WEBGL_draw_buffers" ).drawBuffersWEBGL( drawBuffers ); } } } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } const equationToGL = { [ AddEquation ]: gl.FUNC_ADD, [ SubtractEquation ]: gl.FUNC_SUBTRACT, [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT }; if ( isWebGL2 ) { equationToGL[ MinEquation ] = gl.MIN; equationToGL[ MaxEquation ] = gl.MAX; } else { const extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { equationToGL[ MinEquation ] = extension.MIN_EXT; equationToGL[ MaxEquation ] = extension.MAX_EXT; } } const factorToGL = { [ ZeroFactor ]: gl.ZERO, [ OneFactor ]: gl.ONE, [ SrcColorFactor ]: gl.SRC_COLOR, [ SrcAlphaFactor ]: gl.SRC_ALPHA, [ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE, [ DstColorFactor ]: gl.DST_COLOR, [ DstAlphaFactor ]: gl.DST_ALPHA, [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending === NoBlending ) { if ( currentBlendingEnabled === true ) { disable( gl.BLEND ); currentBlendingEnabled = false; } return; } if ( currentBlendingEnabled === false ) { enable( gl.BLEND ); currentBlendingEnabled = true; } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation( gl.FUNC_ADD ); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if ( premultipliedAlpha ) { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.ONE, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } else { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); break; case AdditiveBlending: gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); break; case SubtractiveBlending: gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); let flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); ( material.blending === NormalBlending && material.transparent === false ) ? setBlending( NoBlending ) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); const stencilWrite = material.stencilWrite; stencilBuffer.setTest( stencilWrite ); if ( stencilWrite ) { stencilBuffer.setMask( material.stencilWriteMask ); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); material.alphaToCoverage === true ? enable( gl.SAMPLE_ALPHA_TO_COVERAGE ) : disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture, webglSlot ) { if ( webglSlot === undefined ) { if ( currentTextureSlot === null ) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[ webglSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ webglSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture !== undefined && boundTexture.type !== undefined ) { gl.bindTexture( boundTexture.type, null ); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage2D() { try { gl.texSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage3D() { try { gl.texSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage2D() { try { gl.texStorage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage3D() { try { gl.texStorage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage3D() { try { gl.texImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } function updateUBOMapping( uniformsGroup, program ) { let mapping = uboProgramMap.get( program ); if ( mapping === undefined ) { mapping = new WeakMap(); uboProgramMap.set( program, mapping ); } let blockIndex = mapping.get( uniformsGroup ); if ( blockIndex === undefined ) { blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name ); mapping.set( uniformsGroup, blockIndex ); } } function uniformBlockBinding( uniformsGroup, program ) { const mapping = uboProgramMap.get( program ); const blockIndex = mapping.get( uniformsGroup ); if ( uboBindings.get( program ) !== blockIndex ) { // bind shader specific block index to global block point gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex ); uboBindings.set( program, blockIndex ); } } // function reset() { // reset state gl.disable( gl.BLEND ); gl.disable( gl.CULL_FACE ); gl.disable( gl.DEPTH_TEST ); gl.disable( gl.POLYGON_OFFSET_FILL ); gl.disable( gl.SCISSOR_TEST ); gl.disable( gl.STENCIL_TEST ); gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ONE, gl.ZERO ); gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); gl.colorMask( true, true, true, true ); gl.clearColor( 0, 0, 0, 0 ); gl.depthMask( true ); gl.depthFunc( gl.LESS ); gl.clearDepth( 1 ); gl.stencilMask( 0xffffffff ); gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); gl.clearStencil( 0 ); gl.cullFace( gl.BACK ); gl.frontFace( gl.CCW ); gl.polygonOffset( 0, 0 ); gl.activeTexture( gl.TEXTURE0 ); gl.bindFramebuffer( gl.FRAMEBUFFER, null ); if ( isWebGL2 === true ) { gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); } gl.useProgram( null ); gl.lineWidth( 1 ); gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture" ) ? extensions.get( "WEBGL_multisampled_render_to_texture" ) : null; const supportsInvalidateFramebuffer = typeof navigator === "undefined" ? false : /OculusBrowser/g.test( navigator.userAgent ); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" // eslint-disable-next-line compat/compat && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; } catch ( err ) { // Ignore any errors } function createCanvas( width, height ) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas( width, height ) : createElementNS( "canvas" ); } function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { let scale = 1; // handle case if texture exceeds max size if ( image.width > maxSize || image.height > maxSize ) { scale = maxSize / Math.max( image.width, image.height ); } // only perform resize if necessary if ( scale < 1 || needsPowerOfTwo === true ) { // only perform resize for certain image types if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor( scale * image.width ); const height = floor( scale * image.height ); if ( _canvas === undefined ) _canvas = createCanvas( width, height ); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, width, height ); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")." ); return canvas; } else { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); } return image; } } return image; } function isPowerOfTwo$1( image ) { return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); } function textureNeedsPowerOfTwo( texture ) { if ( isWebGL2 ) return false; return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, supportsMips ) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target ) { _gl.generateMipmap( target ); } function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) { if ( isWebGL2 === false ) return glFormat; if ( internalFormatName !== null ) { if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """ ); } let internalFormat = glFormat; if ( glFormat === _gl.RED ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.R32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.R16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8; } if ( glFormat === _gl.RED_INTEGER ) { if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8UI; if ( glType === _gl.UNSIGNED_SHORT ) internalFormat = _gl.R16UI; if ( glType === _gl.UNSIGNED_INT ) internalFormat = _gl.R32UI; if ( glType === _gl.BYTE ) internalFormat = _gl.R8I; if ( glType === _gl.SHORT ) internalFormat = _gl.R16I; if ( glType === _gl.INT ) internalFormat = _gl.R32I; } if ( glFormat === _gl.RG ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8; } if ( glFormat === _gl.RGBA ) { if ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F; if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if ( glType === _gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = _gl.RGBA4; if ( glType === _gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = _gl.RGB5_A1; } if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { extensions.get( "EXT_color_buffer_float" ); } return internalFormat; } function getMipLevels( texture, image, supportsMips ) { if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { return Math.log2( Math.max( image.width, image.height ) ) + 1; } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { // user-defined mipmaps return texture.mipmaps.length; } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { _videoTextures.delete( texture ); } } function onRenderTargetDispose( event ) { const renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); } // function deallocateTexture( texture ) { const textureProperties = properties.get( texture ); if ( textureProperties.__webglInit === undefined ) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get( source ); if ( webglTextures ) { const webglTexture = webglTextures[ textureProperties.__cacheKey ]; webglTexture.usedTimes --; // the WebGLTexture object is not used anymore, remove it if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } // remove the weak map entry if no WebGLTexture uses the source anymore if ( Object.keys( webglTextures ).length === 0 ) { _sources.delete( source ); } } properties.remove( texture ); } function deleteTexture( texture ) { const textureProperties = properties.get( texture ); _gl.deleteTexture( textureProperties.__webglTexture ); const source = texture.source; const webglTextures = _sources.get( source ); delete webglTextures[ textureProperties.__cacheKey ]; info.memory.textures --; } function deallocateRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); info.memory.textures --; } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLCubeRenderTarget ) { for ( let i = 0; i < 6; i ++ ) { if ( Array.isArray( renderTargetProperties.__webglFramebuffer[ i ] ) ) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer[ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ][ level ] ); } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); } if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { if ( Array.isArray( renderTargetProperties.__webglFramebuffer ) ) { for ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ level ] ); } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); } if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); if ( renderTargetProperties.__webglColorRenderbuffer ) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) { if ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] ); } } if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { for ( let i = 0, il = texture.length; i < il; i ++ ) { const attachmentProperties = properties.get( texture[ i ] ); if ( attachmentProperties.__webglTexture ) { _gl.deleteTexture( attachmentProperties.__webglTexture ); info.memory.textures --; } properties.remove( texture[ i ] ); } } properties.remove( texture ); properties.remove( renderTarget ); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if ( textureUnit >= maxTextures ) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey( texture ) { const array = []; array.push( texture.wrapS ); array.push( texture.wrapT ); array.push( texture.wrapR || 0 ); array.push( texture.magFilter ); array.push( texture.minFilter ); array.push( texture.anisotropy ); array.push( texture.internalFormat ); array.push( texture.format ); array.push( texture.type ); array.push( texture.generateMipmaps ); array.push( texture.premultiplyAlpha ); array.push( texture.flipY ); array.push( texture.unpackAlignment ); array.push( texture.colorSpace ); return array.join(); } // function setTexture2D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if ( image === null ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found." ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete" ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture2DArray( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTexture3D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } function setTextureCube( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture( textureProperties, texture, slot ); return; } state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); } const wrappingToGL = { [ RepeatWrapping ]: _gl.REPEAT, [ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE, [ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT }; const filterToGL = { [ NearestFilter ]: _gl.NEAREST, [ NearestMipmapNearestFilter ]: _gl.NEAREST_MIPMAP_NEAREST, [ NearestMipmapLinearFilter ]: _gl.NEAREST_MIPMAP_LINEAR, [ LinearFilter ]: _gl.LINEAR, [ LinearMipmapNearestFilter ]: _gl.LINEAR_MIPMAP_NEAREST, [ LinearMipmapLinearFilter ]: _gl.LINEAR_MIPMAP_LINEAR }; const compareToGL = { [ NeverCompare ]: _gl.NEVER, [ AlwaysCompare ]: _gl.ALWAYS, [ LessCompare ]: _gl.LESS, [ LessEqualCompare ]: _gl.LEQUAL, [ EqualCompare ]: _gl.EQUAL, [ GreaterEqualCompare ]: _gl.GEQUAL, [ GreaterCompare ]: _gl.GREATER, [ NotEqualCompare ]: _gl.NOTEQUAL }; function setTextureParameters( textureType, texture, supportsMips ) { if ( supportsMips ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE ); } if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping." ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter." ); } } if ( texture.compareFunction ) { _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE ); _gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] ); } if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( texture.magFilter === NearestFilter ) return; if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; if ( texture.type === FloatType && extensions.has( "OES_texture_float_linear" ) === false ) return; // verify extension for WebGL 1 and WebGL 2 if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( "OES_texture_half_float_linear" ) === false ) ) return; // verify extension for WebGL 1 only if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function initTexture( textureProperties, texture ) { let forceUpload = false; if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get( source ); if ( webglTextures === undefined ) { webglTextures = {}; _sources.set( source, webglTextures ); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey( texture ); if ( textureCacheKey !== textureProperties.__cacheKey ) { // if not, create a new instance of WebGLTexture if ( webglTextures[ textureCacheKey ] === undefined ) { // create new entry webglTextures[ textureCacheKey ] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures ++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[ textureCacheKey ].usedTimes ++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[ textureProperties.__cacheKey ]; if ( webglTexture !== undefined ) { webglTextures[ textureProperties.__cacheKey ].usedTimes --; if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; } return forceUpload; } function uploadTexture( textureProperties, texture, slot ) { let textureType = _gl.TEXTURE_2D; if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY; if ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); image = verifyColorSpace( texture, image ); const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ); let glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); setTextureParameters( textureType, texture, supportsMips ); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); const levels = getMipLevels( texture, image, supportsMips ); if ( texture.isDepthTexture ) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if ( isWebGL2 ) { if ( texture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( texture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if ( texture.type === UnsignedInt248Type ) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if ( texture.type === FloatType ) { console.error( "WebGLRenderer: Floating point depth texture requires WebGL2." ); } } // validation checks for WebGL 1 if ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedIntType; glType = utils.convert( texture.type ); } } if ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } // if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } } } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } } else if ( texture.isCompressedTexture ) { if ( texture.isCompressedArrayTexture ) { if ( useTexStorage && allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 ); } else { state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data ); } } } } else { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else if ( texture.isDataArrayTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isData3DTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isFramebufferTexture ) { if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } else { let width = image.width, height = image.height; for ( let i = 0; i < levels; i ++ ) { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap ); } else { state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image ); } else { state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image ); } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( textureType ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } function uploadCubeTexture( textureProperties, texture, slot ) { if ( texture.image.length !== 6 ) return; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); const sourceProperties = properties.get( source ); if ( source.version !== sourceProperties.__version || forceUpload === true ) { state.activeTexture( _gl.TEXTURE0 + slot ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); const cubeImage = []; for ( let i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); } const image = cubeImage[ 0 ], supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.colorSpace ), glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); let levels = getMipLevels( texture, image, supportsMips ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); let mipmaps; if ( isCompressed ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height ); } for ( let i = 0; i < 6; i ++ ) { mipmaps = cubeImage[ i ].mipmaps; for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else { mipmaps = texture.mipmaps; if ( useTexStorage && allocateMemory ) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if ( mipmaps.length > 0 ) levels ++; state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); } for ( let i = 0; i < 6; i ++ ) { if ( isDataTexture ) { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; const mipmapImage = mipmap.image[ i ].image; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); } } } else { if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( useTexStorage ) { state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); } } } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { // We assume images for cube map have the same size. generateMipmap( _gl.TEXTURE_CUBE_MAP ); } sourceProperties.__version = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level ) { const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const renderTargetProperties = properties.get( renderTarget ); if ( ! renderTargetProperties.__hasExternalTextures ) { const width = Math.max( 1, renderTarget.width >> level ); const height = Math.max( 1, renderTarget.height >> level ); if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null ); } else { state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); } else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753 _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, level ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if ( isMultisample || useMultisampledRTT( renderTarget ) ) { const depthTexture = renderTarget.depthTexture; if ( depthTexture && depthTexture.isDepthTexture ) { if ( depthTexture.type === FloatType ) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if ( depthTexture.type === UnsignedIntType ) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples( renderTarget ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); } } } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; const samples = getRenderTargetSamples( renderTarget ); if ( renderTarget.depthTexture.format === DepthFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( let i = 0; i < 6; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); } } else { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); } } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // rebind framebuffer with external textures function rebindTextures( renderTarget, colorTexture, depthTexture ) { const renderTargetProperties = properties.get( renderTarget ); if ( colorTexture !== undefined ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0 ); } if ( depthTexture !== undefined ) { setupDepthRenderbuffer( renderTarget ); } } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { if ( textureProperties.__webglTexture === undefined ) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures ++; } const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( let i = 0; i < 6; i ++ ) { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { renderTargetProperties.__webglFramebuffer[ i ] = []; for ( let level = 0; level < texture.mipmaps.length; level ++ ) { renderTargetProperties.__webglFramebuffer[ i ][ level ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } } else { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { renderTargetProperties.__webglFramebuffer = []; for ( let level = 0; level < texture.mipmaps.length; level ++ ) { renderTargetProperties.__webglFramebuffer[ level ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } if ( isMultipleRenderTargets ) { if ( capabilities.drawBuffers ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachmentProperties = properties.get( textures[ i ] ); if ( attachmentProperties.__webglTexture === undefined ) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } } } else { console.warn( "THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension." ); } } if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = isMultipleRenderTargets ? texture : [ texture ]; renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; renderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true ); const samples = getRenderTargetSamples( renderTarget ); _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); if ( renderTarget.depthBuffer ) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); } state.bindFramebuffer( _gl.FRAMEBUFFER, null ); } } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); for ( let i = 0; i < 6; i ++ ) { if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { for ( let level = 0; level < texture.mipmaps.length; level ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ][ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 ); } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( _gl.TEXTURE_CUBE_MAP ); } state.unbindTexture(); } else if ( isMultipleRenderTargets ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachment = textures[ i ]; const attachmentProperties = properties.get( attachment ); state.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0 ); if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { generateMipmap( _gl.TEXTURE_2D ); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { if ( isWebGL2 ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error( "THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2." ); } } state.bindTexture( glTextureType, textureProperties.__webglTexture ); setTextureParameters( glTextureType, texture, supportsMips ); if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { for ( let level = 0; level < texture.mipmaps.length; level ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level ); } } else { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0 ); } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( glTextureType ); } state.unbindTexture(); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0, il = textures.length; i < il; i ++ ) { const texture = textures[ i ]; if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target ); state.unbindTexture(); } } } function updateMultisampleRenderTarget( renderTarget ) { if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ]; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = []; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get( renderTarget ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); // If MRT we need to remove FBO attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null ); state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { invalidationArray.push( _gl.COLOR_ATTACHMENT0 + i ); if ( renderTarget.depthBuffer ) { invalidationArray.push( depthStyle ); } const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; if ( ignoreDepthValues === false ) { if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT; if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT; } if ( isMultipleRenderTargets ) { _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } if ( ignoreDepthValues === true ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] ); _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] ); } if ( isMultipleRenderTargets ) { const webglTexture = properties.get( textures[ i ] ).__webglTexture; _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST ); if ( supportsInvalidateFramebuffer ) { _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray ); } } state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null ); state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null ); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const webglTexture = properties.get( textures[ i ] ).__webglTexture; state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 ); } } state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); } } function getRenderTargetSamples( renderTarget ) { return Math.min( maxSamples, renderTarget.samples ); } function useMultisampledRTT( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); return isWebGL2 && renderTarget.samples > 0 && extensions.has( "WEBGL_multisampled_render_to_texture" ) === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture( texture ) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures.get( texture ) !== frame ) { _videoTextures.set( texture, frame ); texture.update(); } } function verifyColorSpace( texture, image ) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if ( texture.isCompressedTexture === true || texture.format === _SRGBAFormat ) return image; if ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) { // sRGB if ( colorSpace === SRGBColorSpace ) { if ( isWebGL2 === false ) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if ( extensions.has( "EXT_sRGB" ) === true && format === RGBAFormat ) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear( image ); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if ( format !== RGBAFormat || type !== UnsignedByteType ) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType." ); } } } else { console.error( "THREE.WebGLTextures: Unsupported texture color space:", colorSpace ); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function convert( p, colorSpace = NoColorSpace ) { let extension; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { if ( isWebGL2 ) return gl.HALF_FLOAT; extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) { return extension.HALF_FLOAT_OES; } else { return null; } } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; // WebGL 1 sRGB fallback if ( p === _SRGBAFormat ) { extension = extensions.get( "EXT_sRGB" ); if ( extension !== null ) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if ( p === RedFormat ) return gl.RED; if ( p === RedIntegerFormat ) return gl.RED_INTEGER; if ( p === RGFormat ) return gl.RG; if ( p === RGIntegerFormat ) return gl.RG_INTEGER; if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if ( colorSpace === SRGBColorSpace ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc_srgb" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc" ); if ( extension !== null ) { if ( p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format ) { extension = extensions.get( "EXT_texture_compression_bptc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // RGTC if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { extension = extensions.get( "EXT_texture_compression_rgtc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT; if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if ( p === UnsignedInt248Type ) { if ( isWebGL2 ) return gl.UNSIGNED_INT_24_8; extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return ( gl[ p ] !== undefined ) ? gl[ p ] : null; } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor( array = [] ) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if ( this._hand === null ) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if ( this._targetRay === null ) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if ( this._grip === null ) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent( event ) { if ( this._targetRay !== null ) { this._targetRay.dispatchEvent( event ); } if ( this._grip !== null ) { this._grip.dispatchEvent( event ); } if ( this._hand !== null ) { this._hand.dispatchEvent( event ); } return this; } connect( inputSource ) { if ( inputSource && inputSource.hand ) { const hand = this._hand; if ( hand ) { for ( const inputjoint of inputSource.hand.values() ) { // Initialize hand with joints when connected this._getHandJoint( hand, inputjoint ); } } } this.dispatchEvent( { type: "connected", data: inputSource } ); return this; } disconnect( inputSource ) { this.dispatchEvent( { type: "disconnected", data: inputSource } ); if ( this._targetRay !== null ) { this._targetRay.visible = false; } if ( this._grip !== null ) { this._grip.visible = false; } if ( this._hand !== null ) { this._hand.visible = false; } return this; } update( inputSource, frame, referenceSpace ) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if ( inputSource && frame.session.visibilityState !== "visible-blurred" ) { if ( hand && inputSource.hand ) { handPose = true; for ( const inputjoint of inputSource.hand.values() ) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose( inputjoint, referenceSpace ); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint( hand, inputjoint ); if ( jointPose !== null ) { joint.matrix.fromArray( jointPose.transform.matrix ); joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints[ "index-finger-tip" ]; const thumbTip = hand.joints[ "thumb-tip" ]; const distance = indexTip.position.distanceTo( thumbTip.position ); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent( { type: "pinchend", handedness: inputSource.handedness, target: this } ); } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent( { type: "pinchstart", handedness: inputSource.handedness, target: this } ); } } else { if ( grip !== null && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { grip.matrix.fromArray( gripPose.transform.matrix ); grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); grip.matrixWorldNeedsUpdate = true; if ( gripPose.linearVelocity ) { grip.hasLinearVelocity = true; grip.linearVelocity.copy( gripPose.linearVelocity ); } else { grip.hasLinearVelocity = false; } if ( gripPose.angularVelocity ) { grip.hasAngularVelocity = true; grip.angularVelocity.copy( gripPose.angularVelocity ); } else { grip.hasAngularVelocity = false; } } } } if ( targetRay !== null ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if ( inputPose === null && gripPose !== null ) { inputPose = gripPose; } if ( inputPose !== null ) { targetRay.matrix.fromArray( inputPose.transform.matrix ); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); targetRay.matrixWorldNeedsUpdate = true; if ( inputPose.linearVelocity ) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy( inputPose.linearVelocity ); } else { targetRay.hasLinearVelocity = false; } if ( inputPose.angularVelocity ) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy( inputPose.angularVelocity ); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent( _moveEvent ); } } } if ( targetRay !== null ) { targetRay.visible = ( inputPose !== null ); } if ( grip !== null ) { grip.visible = ( gripPose !== null ); } if ( hand !== null ) { hand.visible = ( handPose !== null ); } return this; } // private method _getHandJoint( hand, inputjoint ) { if ( hand.joints[ inputjoint.jointName ] === undefined ) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[ inputjoint.jointName ] = joint; hand.add( joint ); } return hand.joints[ inputjoint.jointName ]; } } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedIntType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isDepthTexture = true; this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; this.compareFunction = null; } copy( source ) { super.copy( source ); this.compareFunction = source.compareFunction; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.compareFunction !== null ) data.compareFunction = this.compareFunction; return data; } } class WebXRManager extends EventDispatcher { constructor( renderer, gl ) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; // const cameraL = new PerspectiveCamera(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); const cameras = [ cameraL, cameraR ]; const cameraXR = new ArrayCamera(); cameraXR.layers.enable( 1 ); cameraXR.layers.enable( 2 ); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getGripSpace(); }; this.getHand = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getHandSpace(); }; // function onSessionEvent( event ) { const controllerIndex = controllerInputSources.indexOf( event.inputSource ); if ( controllerIndex === - 1 ) { return; } const controller = controllers[ controllerIndex ]; if ( controller !== undefined ) { controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace ); controller.dispatchEvent( { type: event.type, data: event.inputSource } ); } } function onSessionEnd() { session.removeEventListener( "select", onSessionEvent ); session.removeEventListener( "selectstart", onSessionEvent ); session.removeEventListener( "selectend", onSessionEvent ); session.removeEventListener( "squeeze", onSessionEvent ); session.removeEventListener( "squeezestart", onSessionEvent ); session.removeEventListener( "squeezeend", onSessionEvent ); session.removeEventListener( "end", onSessionEnd ); session.removeEventListener( "inputsourceschange", onInputSourcesChange ); for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; if ( inputSource === null ) continue; controllerInputSources[ i ] = null; controllers[ i ].disconnect( inputSource ); } _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget( initialRenderTarget ); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting." ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting." ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function ( space ) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function ( value ) { session = value; if ( session !== null ) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); session.addEventListener( "inputsourceschange", onInputSourcesChange ); if ( attributes.xrCompatible !== true ) { await gl.makeXRCompatible(); } if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { const layerInit = { antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: glBaseLayer } ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil } ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if ( attributes.depth ) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding( session, gl ); glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); session.updateRenderState( { layers: [ glProjLayer ] } ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0 } ); const renderTargetProperties = renderer.properties.get( newRenderTarget ); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation( foveation ); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); animation.setContext( session ); animation.start(); scope.isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } }; this.getEnvironmentBlendMode = function () { if ( session !== null ) { return session.environmentBlendMode; } }; function onInputSourcesChange( event ) { // Notify disconnected for ( let i = 0; i < event.removed.length; i ++ ) { const inputSource = event.removed[ i ]; const index = controllerInputSources.indexOf( inputSource ); if ( index >= 0 ) { controllerInputSources[ index ] = null; controllers[ index ].disconnect( inputSource ); } } // Notify connected for ( let i = 0; i < event.added.length; i ++ ) { const inputSource = event.added[ i ]; let controllerIndex = controllerInputSources.indexOf( inputSource ); if ( controllerIndex === - 1 ) { // Assign input source a controller that currently has no input source for ( let i = 0; i < controllers.length; i ++ ) { if ( i >= controllerInputSources.length ) { controllerInputSources.push( inputSource ); controllerIndex = i; break; } else if ( controllerInputSources[ i ] === null ) { controllerInputSources[ i ] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if ( controllerIndex === - 1 ) break; } const controller = controllers[ controllerIndex ]; if ( controller ) { controller.connect( inputSource ); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); const ipd = cameraLPos.distanceTo( cameraRPos ); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / ( - leftFov + rightFov ); const xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + ( ipd - xOffset ); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); } this.updateCamera = function ( camera ) { if ( session === null ) return; cameraXR.near = cameraR.near = cameraL.near = camera.near; cameraXR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraXR.near, depthFar: cameraXR.far } ); _currentDepthNear = cameraXR.near; _currentDepthFar = cameraXR.far; } const parent = camera.parent; const cameras = cameraXR.cameras; updateCamera( cameraXR, parent ); for ( let i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } // update projection matrix for proper view frustum culling if ( cameras.length === 2 ) { setProjectionFromUnion( cameraXR, cameraL, cameraR ); } else { // assume single camera setup (AR) cameraXR.projectionMatrix.copy( cameraL.projectionMatrix ); } // update user camera and its children updateUserCamera( camera, cameraXR, parent ); }; function updateUserCamera( camera, cameraXR, parent ) { if ( parent === null ) { camera.matrix.copy( cameraXR.matrixWorld ); } else { camera.matrix.copy( parent.matrixWorld ); camera.matrix.invert(); camera.matrix.multiply( cameraXR.matrixWorld ); } camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.updateMatrixWorld( true ); const children = camera.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } camera.projectionMatrix.copy( cameraXR.projectionMatrix ); camera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse ); if ( camera.isPerspectiveCamera ) { camera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] ); camera.zoom = 1; } } this.getCamera = function () { return cameraXR; }; this.getFoveation = function () { if ( glProjLayer === null && glBaseLayer === null ) { return undefined; } return foveation; }; this.setFoveation = function ( value ) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if ( glProjLayer !== null ) { glProjLayer.fixedFoveation = value; } if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { glBaseLayer.fixedFoveation = value; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); xrFrame = frame; if ( pose !== null ) { const views = pose.views; if ( glBaseLayer !== null ) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); renderer.setRenderTarget( newRenderTarget ); } let cameraXRNeedsUpdate = false; // check if it"s necessary to rebuild cameraXR"s camera list if ( views.length !== cameraXR.cameras.length ) { cameraXR.cameras.length = 0; cameraXRNeedsUpdate = true; } for ( let i = 0; i < views.length; i ++ ) { const view = views[ i ]; let viewport = null; if ( glBaseLayer !== null ) { viewport = glBaseLayer.getViewport( view ); } else { const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if ( i === 0 ) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); renderer.setRenderTarget( newRenderTarget ); } } let camera = cameras[ i ]; if ( camera === undefined ) { camera = new PerspectiveCamera(); camera.layers.enable( i ); camera.viewport = new Vector4(); cameras[ i ] = camera; } camera.matrix.fromArray( view.transform.matrix ); camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraXR.matrix.copy( camera.matrix ); cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale ); } if ( cameraXRNeedsUpdate === true ) { cameraXR.cameras.push( camera ); } } } // for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; const controller = controllers[ i ]; if ( inputSource !== null && controller !== undefined ) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace ); } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); if ( frame.detectedPlanes ) { scope.dispatchEvent( { type: "planesdetected", data: frame } ); } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials( renderer, properties ) { function refreshTransformUniform( map, uniform ) { if ( map.matrixAutoUpdate === true ) { map.updateMatrix(); } uniform.value.copy( map.matrix ); } function refreshFogUniforms( uniforms, fog ) { fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) ); if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshToonMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsToon( uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsPhong( uniforms, material ); } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsStandard( uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); } } else if ( material.isMeshMatcapMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsMatcap( uniforms, material ); } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsDistance( uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( uniforms, material, pixelRatio, height ); } else if ( material.isSpriteMaterial ) { refreshUniformsSprites( uniforms, material ); } else if ( material.isShadowMaterial ) { uniforms.color.value.copy( material.color ); uniforms.opacity.value = material.opacity; } else if ( material.isShaderMaterial ) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value.copy( material.color ); } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform ); uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform( material.normalMap, uniforms.normalMapTransform ); uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform ); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform ); } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform( material.specularMap, uniforms.specularMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get( material ).envMap; if ( envMap ) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; // artist-friendly light intensity scaling factor const scaleFactor = ( renderer._useLegacyLights === true ) ? Math.PI : 1; uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; refreshTransformUniform( material.lightMap, uniforms.lightMapTransform ); } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform( material.aoMap, uniforms.aoMapTransform ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.uvTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if ( material.map ) { uniforms.map.value = material.map; refreshTransformUniform( material.map, uniforms.mapTransform ); } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value.copy( material.specular ); uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon( uniforms, material ) { if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.metalness.value = material.metalness; if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform ); } uniforms.roughness.value = material.roughness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform ); } const envMap = properties.get( material ).envMap; if ( envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { uniforms.ior.value = material.ior; // also part of uniforms common if ( material.sheen > 0 ) { uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); uniforms.sheenRoughness.value = material.sheenRoughness; if ( material.sheenColorMap ) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform ); } if ( material.sheenRoughnessMap ) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform ); } } if ( material.clearcoat > 0 ) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if ( material.clearcoatMap ) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform ); } if ( material.clearcoatRoughnessMap ) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform ); } if ( material.clearcoatNormalMap ) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform ); uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); if ( material.side === BackSide ) { uniforms.clearcoatNormalScale.value.negate(); } } } if ( material.iridescence > 0 ) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ]; if ( material.iridescenceMap ) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform ); } if ( material.iridescenceThicknessMap ) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform ); } } if ( material.transmission > 0 ) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); if ( material.transmissionMap ) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform ); } uniforms.thickness.value = material.thickness; if ( material.thicknessMap ) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform ); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy( material.attenuationColor ); } if ( material.anisotropy > 0 ) { uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) ); if ( material.anisotropyMap ) { uniforms.anisotropyMap.value = material.anisotropyMap; refreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform ); } } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy( material.specularColor ); if ( material.specularColorMap ) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform ); } if ( material.specularIntensityMap ) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform ); } } function refreshUniformsMatcap( uniforms, material ) { if ( material.matcap ) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance( uniforms, material ) { const light = properties.get( material ).light; uniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld ); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function WebGLUniformsGroups( gl, info, capabilities, state ) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program function bind( uniformsGroup, program ) { const webglProgram = program.program; state.uniformBlockBinding( uniformsGroup, webglProgram ); } function update( uniformsGroup, program ) { let buffer = buffers[ uniformsGroup.id ]; if ( buffer === undefined ) { prepareUniformsGroup( uniformsGroup ); buffer = createBuffer( uniformsGroup ); buffers[ uniformsGroup.id ] = buffer; uniformsGroup.addEventListener( "dispose", onUniformsGroupsDispose ); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping( uniformsGroup, webglProgram ); // update UBO once per frame const frame = info.render.frame; if ( updateList[ uniformsGroup.id ] !== frame ) { updateBufferData( uniformsGroup ); updateList[ uniformsGroup.id ] = frame; } } function createBuffer( uniformsGroup ) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); gl.bufferData( gl.UNIFORM_BUFFER, size, usage ); gl.bindBuffer( gl.UNIFORM_BUFFER, null ); gl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer ); return buffer; } function allocateBindingPointIndex() { for ( let i = 0; i < maxBindingPoints; i ++ ) { if ( allocatedBindingPoints.indexOf( i ) === - 1 ) { allocatedBindingPoints.push( i ); return i; } } console.error( "THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached." ); return 0; } function updateBufferData( uniformsGroup ) { const buffer = buffers[ uniformsGroup.id ]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); for ( let i = 0, il = uniforms.length; i < il; i ++ ) { const uniform = uniforms[ i ]; // partly update the buffer if necessary if ( hasUniformChanged( uniform, i, cache ) === true ) { const offset = uniform.__offset; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; let arrayOffset = 0; for ( let i = 0; i < values.length; i ++ ) { const value = values[ i ]; const info = getUniformSize( value ); if ( typeof value === "number" ) { uniform.__data[ 0 ] = value; gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data ); } else if ( value.isMatrix3 ) { // manually converting 3x3 to 3x4 uniform.__data[ 0 ] = value.elements[ 0 ]; uniform.__data[ 1 ] = value.elements[ 1 ]; uniform.__data[ 2 ] = value.elements[ 2 ]; uniform.__data[ 3 ] = value.elements[ 0 ]; uniform.__data[ 4 ] = value.elements[ 3 ]; uniform.__data[ 5 ] = value.elements[ 4 ]; uniform.__data[ 6 ] = value.elements[ 5 ]; uniform.__data[ 7 ] = value.elements[ 0 ]; uniform.__data[ 8 ] = value.elements[ 6 ]; uniform.__data[ 9 ] = value.elements[ 7 ]; uniform.__data[ 10 ] = value.elements[ 8 ]; uniform.__data[ 11 ] = value.elements[ 0 ]; } else { value.toArray( uniform.__data, arrayOffset ); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data ); } } gl.bindBuffer( gl.UNIFORM_BUFFER, null ); } function hasUniformChanged( uniform, index, cache ) { const value = uniform.value; if ( cache[ index ] === undefined ) { // cache entry does not exist so far if ( typeof value === "number" ) { cache[ index ] = value; } else { const values = Array.isArray( value ) ? value : [ value ]; const tempValues = []; for ( let i = 0; i < values.length; i ++ ) { tempValues.push( values[ i ].clone() ); } cache[ index ] = tempValues; } return true; } else { // compare current value with cached entry if ( typeof value === "number" ) { if ( cache[ index ] !== value ) { cache[ index ] = value; return true; } } else { const cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ]; const values = Array.isArray( value ) ? value : [ value ]; for ( let i = 0; i < cachedObjects.length; i ++ ) { const cachedObject = cachedObjects[ i ]; if ( cachedObject.equals( values[ i ] ) === false ) { cachedObject.copy( values[ i ] ); return true; } } } } return false; } function prepareUniformsGroup( uniformsGroup ) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes let chunkOffset = 0; // offset within a single chunk in bytes for ( let i = 0, l = uniforms.length; i < l; i ++ ) { const uniform = uniforms[ i ]; const infos = { boundary: 0, // bytes storage: 0 // bytes }; const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; for ( let j = 0, jl = values.length; j < jl; j ++ ) { const value = values[ j ]; const info = getUniformSize( value ); infos.boundary += info.boundary; infos.storage += info.storage; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT ); uniform.__offset = offset; // if ( i > 0 ) { chunkOffset = offset % chunkSize; const remainingSizeInChunk = chunkSize - chunkOffset; // check for chunk overflow if ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) { // add padding and adjust offset offset += ( chunkSize - chunkOffset ); uniform.__offset = offset; } } offset += infos.storage; } // ensure correct final padding chunkOffset = offset % chunkSize; if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset ); // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize( value ) { const info = { boundary: 0, // bytes storage: 0 // bytes }; // determine sizes according to STD140 if ( typeof value === "number" ) { // float/int info.boundary = 4; info.storage = 4; } else if ( value.isVector2 ) { // vec2 info.boundary = 8; info.storage = 8; } else if ( value.isVector3 || value.isColor ) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if ( value.isVector4 ) { // vec4 info.boundary = 16; info.storage = 16; } else if ( value.isMatrix3 ) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if ( value.isMatrix4 ) { // mat4 info.boundary = 64; info.storage = 64; } else if ( value.isTexture ) { console.warn( "THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group." ); } else { console.warn( "THREE.WebGLRenderer: Unsupported uniform value type.", value ); } return info; } function onUniformsGroupsDispose( event ) { const uniformsGroup = event.target; uniformsGroup.removeEventListener( "dispose", onUniformsGroupsDispose ); const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex ); allocatedBindingPoints.splice( index, 1 ); gl.deleteBuffer( buffers[ uniformsGroup.id ] ); delete buffers[ uniformsGroup.id ]; delete updateList[ uniformsGroup.id ]; } function dispose() { for ( const id in buffers ) { gl.deleteBuffer( buffers[ id ] ); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose }; } function createCanvasElement() { const canvas = createElementNS( "canvas" ); canvas.style.display = "block"; return canvas; } class WebGLRenderer { constructor( parameters = {} ) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = true, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = "default", failIfMajorPerformanceCaveat = false, } = parameters; this.isWebGLRenderer = true; let _alpha; if ( context !== null ) { _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } const uintClearColor = new Uint32Array( 4 ); const intClearColor = new Int32Array( 4 ); let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputColorSpace = SRGBColorSpace; // physical lights this._useLegacyLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = - 1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; const _currentClearColor = new Color( 0x000000 ); let _currentClearAlpha = 0; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4( 0, 0, _width, _height ); const _scissor = new Vector4( 0, 0, _width, _height ); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext( contextNames, contextAttributes ) { for ( let i = 0; i < contextNames.length; i ++ ) { const contextName = contextNames[ i ]; const context = canvas.getContext( contextName, contextAttributes ); if ( context !== null ) return context; } return null; } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat, }; // OffscreenCanvas does not have setAttribute, see #22811 if ( "setAttribute" in canvas ) canvas.setAttribute( "data-engine", `three.js r${REVISION}` ); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener( "webglcontextlost", onContextLost, false ); canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false ); if ( _gl === null ) { const contextNames = [ "webgl2", "webgl", "experimental-webgl" ]; if ( _this.isWebGL1Renderer === true ) { contextNames.shift(); } _gl = getContext( contextNames, contextAttributes ); if ( _gl === null ) { if ( getContext( contextNames ) ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } } if ( typeof WebGLRenderingContext !== "undefined" && _gl instanceof WebGLRenderingContext ) { // @deprecated, r153 console.warn( "THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163." ); } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions( _gl ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); extensions.init( capabilities ); utils = new WebGLUtils( _gl, extensions, capabilities ); state = new WebGLState( _gl, extensions, capabilities ); info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); cubemaps = new WebGLCubeMaps( _this ); cubeuvmaps = new WebGLCubeUVMaps( _this ); attributes = new WebGLAttributes( _gl, capabilities ); bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); objects = new WebGLObjects( _gl, geometries, attributes, info ); morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); clipping = new WebGLClipping( properties ); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); materials = new WebGLMaterials( _this, properties ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions, capabilities ); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager( _this, _gl ); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function ( target ) { return target.set( _width, _height ); }; this.setSize = function ( width, height, updateStyle = true ) { if ( xr.isPresenting ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; canvas.width = Math.floor( width * _pixelRatio ); canvas.height = Math.floor( height * _pixelRatio ); if ( updateStyle === true ) { canvas.style.width = width + "px"; canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function ( target ) { return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor( width * pixelRatio ); canvas.height = Math.floor( height * pixelRatio ); this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function ( target ) { return target.copy( _currentViewport ); }; this.getViewport = function ( target ) { return target.copy( _viewport ); }; this.setViewport = function ( x, y, width, height ) { if ( x.isVector4 ) { _viewport.set( x.x, x.y, x.z, x.w ); } else { _viewport.set( x, y, width, height ); } state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissor = function ( target ) { return target.copy( _scissor ); }; this.setScissor = function ( x, y, width, height ) { if ( x.isVector4 ) { _scissor.set( x.x, x.y, x.z, x.w ); } else { _scissor.set( x, y, width, height ); } state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; this.setOpaqueSort = function ( method ) { _opaqueSort = method; }; this.setTransparentSort = function ( method ) { _transparentSort = method; }; // Clearing this.getClearColor = function ( target ) { return target.copy( background.getClearColor() ); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color = true, depth = true, stencil = true ) { let bits = 0; if ( color ) { // check if we"re trying to clear an integer target let isIntegerFormat = false; if ( _currentRenderTarget !== null ) { const targetFormat = _currentRenderTarget.texture.format; isIntegerFormat = targetFormat === RGBAIntegerFormat || targetFormat === RGIntegerFormat || targetFormat === RedIntegerFormat; } // use the appropriate clear functions to clear the target if it"s a signed // or unsigned integer target if ( isIntegerFormat ) { const targetType = _currentRenderTarget.texture.type; const isUnsignedType = targetType === UnsignedByteType || targetType === UnsignedIntType || targetType === UnsignedShortType || targetType === UnsignedInt248Type || targetType === UnsignedShort4444Type || targetType === UnsignedShort5551Type; const clearColor = background.getClearColor(); const a = background.getClearAlpha(); const r = clearColor.r; const g = clearColor.g; const b = clearColor.b; if ( isUnsignedType ) { uintClearColor[ 0 ] = r; uintClearColor[ 1 ] = g; uintClearColor[ 2 ] = b; uintClearColor[ 3 ] = a; _gl.clearBufferuiv( _gl.COLOR, 0, uintClearColor ); } else { intClearColor[ 0 ] = r; intClearColor[ 1 ] = g; intClearColor[ 2 ] = b; intClearColor[ 3 ] = a; _gl.clearBufferiv( _gl.COLOR, 0, intClearColor ); } } else { bits |= _gl.COLOR_BUFFER_BIT; } } if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; // this.dispose = function () { canvas.removeEventListener( "webglcontextlost", onContextLost, false ); canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener( "sessionstart", onXRSessionStart ); xr.removeEventListener( "sessionend", onXRSessionEnd ); if ( _transmissionRenderTarget ) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError( event ) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage ); } function onMaterialDispose( event ) { const material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReferences( material ); properties.remove( material ); } function releaseMaterialProgramReferences( material ) { const programs = properties.get( material ).programs; if ( programs !== undefined ) { programs.forEach( function ( program ) { programCache.releaseProgram( program ); } ); if ( material.isShaderMaterial ) { programCache.releaseShaderCache( material ); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); const program = setProgram( camera, scene, geometry, material, object ); state.setMaterial( material, frontFaceCW ); // let index = geometry.index; let rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); if ( index === undefined ) return; rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; if ( group !== null ) { drawStart = Math.max( drawStart, group.start * rangeFactor ); drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); } if ( index !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, index.count ); } else if ( position !== undefined && position !== null ) { drawStart = Math.max( drawStart, 0 ); drawEnd = Math.min( drawEnd, position.count ); } const drawCount = drawEnd - drawStart; if ( drawCount < 0 || drawCount === Infinity ) return; // bindingStates.setup( object, material, program, geometry, index ); let attribute; let renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { renderer.setMode( _gl.TRIANGLES ); } } else if ( object.isLine ) { let lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } else if ( object.isSprite ) { renderer.setMode( _gl.TRIANGLES ); } if ( object.isInstancedMesh ) { renderer.renderInstances( drawStart, drawCount, object.count ); } else if ( geometry.isInstancedBufferGeometry ) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); renderer.renderInstances( drawStart, drawCount, instanceCount ); } else { renderer.render( drawStart, drawCount ); } }; // Compile this.compile = function ( scene, camera ) { function prepare( material, scene, object ) { if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = FrontSide; material.needsUpdate = true; getProgram( material, scene, object ); material.side = DoubleSide; } else { getProgram( material, scene, object ); } } currentRenderState = renderStates.get( scene ); currentRenderState.init(); renderStateStack.push( currentRenderState ); scene.traverseVisible( function ( object ) { if ( object.isLight && object.layers.test( camera.layers ) ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( _this._useLegacyLights ); scene.traverse( function ( object ) { const material = object.material; if ( material ) { if ( Array.isArray( material ) ) { for ( let i = 0; i < material.length; i ++ ) { const material2 = material[ i ]; prepare( material2, scene, object ); } } else { prepare( material, scene, object ); } } } ); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time ) { if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); if ( typeof self !== "undefined" ) animation.setContext( self ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; xr.setAnimationLoop( callback ); ( callback === null ) ? animation.stop() : animation.start(); }; xr.addEventListener( "sessionstart", onXRSessionStart ); xr.addEventListener( "sessionend", onXRSessionEnd ); // Rendering this.render = function ( scene, camera ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost === true ) return; // update scene graph if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); if ( xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering } // if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); currentRenderState = renderStates.get( scene, renderStateStack.length ); currentRenderState.init(); renderStateStack.push( currentRenderState ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromProjectionMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); currentRenderList = renderLists.get( scene, renderListStack.length ); currentRenderList.init(); renderListStack.push( currentRenderList ); projectObject( scene, camera, 0, _this.sortObjects ); currentRenderList.finish(); if ( _this.sortObjects === true ) { currentRenderList.sort( _opaqueSort, _transparentSort ); } // this.info.render.frame ++; if ( _clippingEnabled === true ) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); if ( _clippingEnabled === true ) clipping.endShadows(); // if ( this.info.autoReset === true ) this.info.reset(); // background.render( currentRenderList, scene ); // render scene currentRenderState.setupLights( _this._useLegacyLights ); if ( camera.isArrayCamera ) { const cameras = camera.cameras; for ( let i = 0, l = cameras.length; i < l; i ++ ) { const camera2 = cameras[ i ]; renderScene( currentRenderList, scene, camera2, camera2.viewport ); } } else { renderScene( currentRenderList, scene, camera ); } // if ( _currentRenderTarget !== null ) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget( _currentRenderTarget ); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap( _currentRenderTarget ); } // if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = - 1; _currentCamera = null; renderStateStack.pop(); if ( renderStateStack.length > 0 ) { currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; } else { currentRenderState = null; } renderListStack.pop(); if ( renderListStack.length > 0 ) { currentRenderList = renderListStack[ renderListStack.length - 1 ]; } else { currentRenderList = null; } }; function projectObject( object, camera, groupOrder, sortObjects ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isGroup ) { groupOrder = object.renderOrder; } else if ( object.isLOD ) { if ( object.autoUpdate === true ) object.update( camera ); } else if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } const geometry = objects.update( object ); const material = object.material; if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { const geometry = objects.update( object ); const material = object.material; if ( sortObjects ) { if ( object.boundingSphere !== undefined ) { if ( object.boundingSphere === null ) object.computeBoundingSphere(); _vector3.copy( object.boundingSphere.center ); } else { if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _vector3.copy( geometry.boundingSphere.center ); } _vector3 .applyMatrix4( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, groupOrder, sortObjects ); } } function renderScene( currentRenderList, scene, camera, viewport ) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView( camera ); if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); } function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { const isWebGL2 = capabilities.isWebGL2; if ( _transmissionRenderTarget === null ) { _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { generateMipmaps: true, type: extensions.has( "EXT_color_buffer_half_float" ) ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: ( isWebGL2 ) ? 4 : 0 } ); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } _this.getDrawingBufferSize( _vector2 ); if ( isWebGL2 ) { _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); } else { _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget( _transmissionRenderTarget ); _this.getClearColor( _currentClearColor ); _currentClearAlpha = _this.getClearAlpha(); if ( _currentClearAlpha < 1 ) _this.setClearColor( 0xffffff, 0.5 ); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects( opaqueObjects, scene, camera ); textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); let renderTargetNeedsUpdate = false; for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { const renderItem = transmissiveObjects[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject( object, scene, camera, geometry, material, group ); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if ( renderTargetNeedsUpdate === true ) { textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); } _this.setRenderTarget( currentRenderTarget ); _this.setClearColor( _currentClearColor, _currentClearAlpha ); _this.toneMapping = currentToneMapping; } function renderObjects( renderList, scene, camera ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for ( let i = 0, l = renderList.length; i < l; i ++ ) { const renderItem = renderList[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if ( object.layers.test( camera.layers ) ) { renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); material.onBeforeRender( _this, scene, camera, geometry, object, group ); if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } function getProgram( material, scene, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); const programCacheKey = programCache.getProgramCacheKey( parameters ); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); if ( programs === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); programs = new Map(); materialProperties.programs = programs; } let program = programs.get( programCacheKey ); if ( program !== undefined ) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties( material, parameters ); return program; } } else { parameters.uniforms = programCache.getUniforms( material ); material.onBuild( object, parameters, _this ); material.onBeforeCompile( parameters, _this ); program = programCache.acquireProgram( parameters, programCacheKey ); programs.set( programCacheKey, program ); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties( material, parameters ); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights( material ); materialProperties.lightsStateVersion = lightsStateVersion; if ( materialProperties.needsLights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties( material, parameters ) { const materialProperties = properties.get( material ); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.instancing = parameters.instancing; materialProperties.instancingColor = parameters.instancingColor; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram( camera, scene, geometry, material, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ); const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 ); const morphTargets = !! geometry.morphAttributes.position; const morphNormals = !! geometry.morphAttributes.normal; const morphColors = !! geometry.morphAttributes.color; let toneMapping = NoToneMapping; if ( material.toneMapped ) { if ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) { toneMapping = _this.toneMapping; } } const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; if ( _clippingEnabled === true ) { if ( _localClippingEnabled === true || camera !== _currentCamera ) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState( material, camera, useCache ); } } // let needsProgramChange = false; if ( material.version === materialProperties.__version ) { if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { needsProgramChange = true; } else if ( materialProperties.outputColorSpace !== colorSpace ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) { needsProgramChange = true; } else if ( materialProperties.envMap !== envMap ) { needsProgramChange = true; } else if ( material.fog === true && materialProperties.fog !== fog ) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection ) ) { needsProgramChange = true; } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { needsProgramChange = true; } else if ( materialProperties.vertexTangents !== vertexTangents ) { needsProgramChange = true; } else if ( materialProperties.morphTargets !== morphTargets ) { needsProgramChange = true; } else if ( materialProperties.morphNormals !== morphNormals ) { needsProgramChange = true; } else if ( materialProperties.morphColors !== morphColors ) { needsProgramChange = true; } else if ( materialProperties.toneMapping !== toneMapping ) { needsProgramChange = true; } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if ( needsProgramChange === true ) { program = getProgram( material, scene, object ); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || _currentCamera !== camera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( _currentCamera !== camera ) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap ) { const uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true ); } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if ( object.isSkinnedMesh ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); const skeleton = object.skeleton; if ( skeleton ) { if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { console.warn( "THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required." ); } } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { morphtargets.update( object, geometry, program ); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue( _gl, "receiveShadow", object.receiveShadow ); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if ( material.isMeshGouraudMaterial && material.envMap !== null ) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); if ( materialProperties.needsLights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog === true ) { materials.refreshFogUniforms( m_uniforms, fog ); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); material.uniformsNeedUpdate = false; } if ( material.isSpriteMaterial ) { p_uniforms.setValue( _gl, "center", object.center ); } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); // UBOs if ( material.isShaderMaterial || material.isRawShaderMaterial ) { const groups = material.uniformsGroups; for ( let i = 0, l = groups.length; i < l; i ++ ) { if ( capabilities.isWebGL2 ) { const group = groups[ i ]; uniformsGroups.update( group, program ); uniformsGroups.bind( group, program ); } else { console.warn( "THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2." ); } } } return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights( material ) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || ( material.isShaderMaterial && material.lights === true ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { properties.get( renderTarget.texture ).__webglTexture = colorTexture; properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__hasExternalTextures = true; if ( renderTargetProperties.__hasExternalTextures ) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if ( extensions.has( "WEBGL_multisampled_render_to_texture" ) === true ) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided" ); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if ( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer( _gl.FRAMEBUFFER, null ); useDefaultFramebuffer = false; } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } else if ( renderTargetProperties.__hasExternalTextures ) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); } const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget ) { if ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) { framebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ]; } else { framebuffer = __webglFramebuffer[ activeCubeFace ]; } isCube = true; } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; } else { if ( Array.isArray( __webglFramebuffer ) ) { framebuffer = __webglFramebuffer[ activeMipmapLevel ]; } else { framebuffer = __webglFramebuffer; } } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { state.drawBuffers( renderTarget, framebuffer ); } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { const textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); } else if ( isRenderTarget3D ) { const textureProperties = properties.get( renderTarget.texture ); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); } _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } let framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[ activeCubeFaceIndex ]; } if ( framebuffer ) { state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( "EXT_color_buffer_half_float" ) || ( capabilities.isWebGL2 && extensions.has( "EXT_color_buffer_float" ) ) ); if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( "OES_texture_float" ) || extensions.has( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! halfFloatSupportedByExt ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); } } }; this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { const levelScale = Math.pow( 2, - level ); const width = Math.floor( texture.image.width * levelScale ); const height = Math.floor( texture.image.height * levelScale ); textures.setTexture2D( texture, 0 ); _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); state.unbindTexture(); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); textures.setTexture2D( dstTexture, 0 ); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { if ( srcTexture.isCompressedTexture ) { _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); } } // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { if ( _this.isWebGL1Renderer ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2." ); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); let glTarget; if ( dstTexture.isData3DTexture ) { textures.setTexture3D( dstTexture, 0 ); glTarget = _gl.TEXTURE_3D; } else if ( dstTexture.isDataArrayTexture ) { textures.setTexture2DArray( dstTexture, 0 ); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray." ); return; } _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); } else { if ( srcTexture.isCompressedArrayTexture ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture." ); _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); } else { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } } _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); state.unbindTexture(); }; this.initTexture = function ( texture ) { if ( texture.isCubeTexture ) { textures.setTextureCube( texture, 0 ); } else if ( texture.isData3DTexture ) { textures.setTexture3D( texture, 0 ); } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { textures.setTexture2DArray( texture, 0 ); } else { textures.setTexture2D( texture, 0 ); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } get coordinateSystem() { return WebGLCoordinateSystem; } get physicallyCorrectLights() { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); return ! this.useLegacyLights; } set physicallyCorrectLights( value ) { // @deprecated, r150 console.warn( "THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." ); this.useLegacyLights = ! value; } get outputEncoding() { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); return this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set outputEncoding( encoding ) { // @deprecated, r152 console.warn( "THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead." ); this.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace; } get useLegacyLights() { // @deprecated, r155 console.warn( "THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733." ); return this._useLegacyLights; } set useLegacyLights( value ) { // @deprecated, r155 console.warn( "THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733." ); this._useLegacyLights = value; } } class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor( color, density = 0.00025 ) { this.isFogExp2 = true; this.name = ""; this.color = new Color( color ); this.density = density; } clone() { return new FogExp2( this.color, this.density ); } toJSON( /* meta */ ) { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } class Fog { constructor( color, near = 1, far = 1000 ) { this.isFog = true; this.name = ""; this.color = new Color( color ); this.near = near; this.far = far; } clone() { return new Fog( this.color, this.near, this.far ); } toJSON( /* meta */ ) { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.overrideMaterial = null; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } copy( source, recursive ) { super.copy( source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.environment !== null ) this.environment = source.environment.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); if ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness; if ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity; return data; } } class InterleavedBuffer { constructor( array, stride ) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( let i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } set( value, offset = 0 ) { this.array.set( value, offset ); return this; } clone( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; } const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); const ib = new this.constructor( array, this.stride ); ib.setUsage( this.usage ); return ib; } onUpload( callback ) { this.onUploadCallback = callback; return this; } toJSON( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } const _vector$5 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor( interleavedBuffer, itemSize, offset, normalized = false ) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate( value ) { this.data.needsUpdate = value; } applyMatrix4( m ) { for ( let i = 0, l = this.data.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyMatrix4( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.applyNormalMatrix( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$5.fromBufferAttribute( this, i ); _vector$5.transformDirection( m ); this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); } return this; } setX( index, x ) { if ( this.normalized ) x = normalize( x, this.array ); this.data.array[ index * this.data.stride + this.offset ] = x; return this; } setY( index, y ) { if ( this.normalized ) y = normalize( y, this.array ); this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; } setZ( index, z ) { if ( this.normalized ) z = normalize( z, this.array ); this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; } setW( index, w ) { if ( this.normalized ) w = normalize( w, this.array ); this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; } getX( index ) { let x = this.data.array[ index * this.data.stride + this.offset ]; if ( this.normalized ) x = denormalize( x, this.array ); return x; } getY( index ) { let y = this.data.array[ index * this.data.stride + this.offset + 1 ]; if ( this.normalized ) y = denormalize( y, this.array ); return y; } getZ( index ) { let z = this.data.array[ index * this.data.stride + this.offset + 2 ]; if ( this.normalized ) z = denormalize( z, this.array ); return z; } getW( index ) { let w = this.data.array[ index * this.data.stride + this.offset + 3 ]; if ( this.normalized ) w = denormalize( w, this.array ); return w; } setXY( index, x, y ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index = index * this.data.stride + this.offset; if ( this.normalized ) { x = normalize( x, this.array ); y = normalize( y, this.array ); z = normalize( z, this.array ); w = normalize( w, this.array ); } this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } clone( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); } return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); } } toJSON( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interleaved attribute if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } class SpriteMaterial extends Material { constructor( parameters ) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor( material ) { super(); this.isSprite = true; this.type = "Sprite"; if ( _geometry === undefined ) { _geometry = new BufferGeometry(); const float32Array = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0, 0.5, - 0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, - 0.5, 0.5, 0, 0, 1 ] ); const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); _geometry.setAttribute( "position", new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); } this.geometry = _geometry; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } raycast( raycaster, intersects ) { if ( raycaster.camera === null ) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites." ); } _worldScale.setFromMatrixScale( this.matrixWorld ); _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar( - _mvPosition.z ); } const rotation = this.material.rotation; let sin, cos; if ( rotation !== 0 ) { cos = Math.cos( rotation ); sin = Math.sin( rotation ); } const center = this.center; transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvA.set( 0, 0 ); _uvB.set( 1, 0 ); _uvC.set( 1, 1 ); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); if ( intersect === null ) { // check second triangle transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvB.set( 0, 1 ); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); if ( intersect === null ) { return; } } const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), face: null, object: this } ); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.center !== undefined ) this.center.copy( source.center ); this.material = source.material; return this; } } function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { // compute position in camera space _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); // to check if rotation is not zero if ( sin !== undefined ) { _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); } else { _rotatedPosition.copy( _alignedPosition ); } vertexPosition.copy( mvPosition ); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4( _viewWorldMatrix ); } const _v1$2 = /*@__PURE__*/ new Vector3(); const _v2$1 = /*@__PURE__*/ new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties( this, { levels: { enumerable: true, value: [] }, isLOD: { value: true, } } ); this.autoUpdate = true; } copy( source ) { super.copy( source, false ); const levels = source.levels; for ( let i = 0, l = levels.length; i < l; i ++ ) { const level = levels[ i ]; this.addLevel( level.object.clone(), level.distance, level.hysteresis ); } this.autoUpdate = source.autoUpdate; return this; } addLevel( object, distance = 0, hysteresis = 0 ) { distance = Math.abs( distance ); const levels = this.levels; let l; for ( l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } ); this.add( object ); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance( distance ) { const levels = this.levels; if ( levels.length > 0 ) { let i, l; for ( i = 1, l = levels.length; i < l; i ++ ) { let levelDistance = levels[ i ].distance; if ( levels[ i ].object.visible ) { levelDistance -= levelDistance * levels[ i ].hysteresis; } if ( distance < levelDistance ) { break; } } return levels[ i - 1 ].object; } return null; } raycast( raycaster, intersects ) { const levels = this.levels; if ( levels.length > 0 ) { _v1$2.setFromMatrixPosition( this.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _v1$2 ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); } } update( camera ) { const levels = this.levels; if ( levels.length > 1 ) { _v1$2.setFromMatrixPosition( camera.matrixWorld ); _v2$1.setFromMatrixPosition( this.matrixWorld ); const distance = _v1$2.distanceTo( _v2$1 ) / camera.zoom; levels[ 0 ].object.visible = true; let i, l; for ( i = 1, l = levels.length; i < l; i ++ ) { let levelDistance = levels[ i ].distance; if ( levels[ i ].object.visible ) { levelDistance -= levelDistance * levels[ i ].hysteresis; } if ( distance >= levelDistance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } this._currentLevel = i - 1; for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.autoUpdate === false ) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for ( let i = 0, l = levels.length; i < l; i ++ ) { const level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance, hysteresis: level.hysteresis } ); } return data; } } const _basePosition = /*@__PURE__*/ new Vector3(); const _skinIndex = /*@__PURE__*/ new Vector4(); const _skinWeight = /*@__PURE__*/ new Vector4(); const _vector3 = /*@__PURE__*/ new Vector3(); const _matrix4 = /*@__PURE__*/ new Matrix4(); const _vertex = /*@__PURE__*/ new Vector3(); const _sphere$3 = /*@__PURE__*/ new Sphere(); const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); class SkinnedMesh extends Mesh { constructor( geometry, material ) { super( geometry, material ); this.isSkinnedMesh = true; this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute( "position" ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingBox.expandByPoint( _vertex ); } } computeBoundingSphere() { const geometry = this.geometry; if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute( "position" ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingSphere.expandByPoint( _vertex ); } } copy( source, recursive ) { super.copy( source, recursive ); this.bindMode = source.bindMode; this.bindMatrix.copy( source.bindMatrix ); this.bindMatrixInverse.copy( source.bindMatrixInverse ); this.skeleton = source.skeleton; if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast( raycaster, intersects ) { const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // test with bounding sphere in world space if ( this.boundingSphere === null ) this.computeBoundingSphere(); _sphere$3.copy( this.boundingSphere ); _sphere$3.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; // convert ray to local space of skinned mesh _inverseMatrix$2.copy( matrixWorld ).invert(); _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); // test with bounding box in local space if ( this.boundingBox !== null ) { if ( _ray$2.intersectsBox( this.boundingBox ) === false ) return; } // test for intersections with geometry this._computeIntersections( raycaster, intersects, _ray$2 ); } getVertexPosition( index, target ) { super.getVertexPosition( index, target ); this.applyBoneTransform( index, target ); return target; } bind( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.copy( bindMatrix ).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { vector.fromBufferAttribute( skinWeight, i ); const scale = 1.0 / vector.manhattanLength(); if ( scale !== Infinity ) { vector.multiplyScalar( scale ); } else { vector.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); } } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.copy( this.matrixWorld ).invert(); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.copy( this.bindMatrix ).invert(); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode ); } } applyBoneTransform( index, vector ) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); _basePosition.copy( vector ).applyMatrix4( this.bindMatrix ); vector.set( 0, 0, 0 ); for ( let i = 0; i < 4; i ++ ) { const weight = _skinWeight.getComponent( i ); if ( weight !== 0 ) { const boneIndex = _skinIndex.getComponent( i ); _matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); vector.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight ); } } return vector.applyMatrix4( this.bindMatrixInverse ); } boneTransform( index, vector ) { // @deprecated, r151 console.warn( "THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151." ); return this.applyBoneTransform( index, vector ); } } class Bone extends Object3D { constructor() { super(); this.isBone = true; this.type = "Bone"; } } class DataTexture extends Texture { constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isDataTexture = true; this.image = { data: data, width: width, height: height }; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } const _offsetMatrix = /*@__PURE__*/ new Matrix4(); const _identityMatrix = /*@__PURE__*/ new Matrix4(); class Skeleton { constructor( bones = [], boneInverses = [] ) { this.uuid = generateUUID(); this.bones = bones.slice( 0 ); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array( bones.length * 16 ); // calculate inverse bone matrices if necessary if ( boneInverses.length === 0 ) { this.calculateInverses(); } else { // handle special case if ( bones.length !== boneInverses.length ) { console.warn( "THREE.Skeleton: Number of inverse bone matrices does not match amount of bones." ); this.boneInverses = []; for ( let i = 0, il = this.bones.length; i < il; i ++ ) { this.boneInverses.push( new Matrix4() ); } } } } calculateInverses() { this.boneInverses.length = 0; for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const inverse = new Matrix4(); if ( this.bones[ i ] ) { inverse.copy( this.bones[ i ].matrixWorld ).invert(); } this.boneInverses.push( inverse ); } } pose() { // recover the bind-time world matrices for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone ) { bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); } } // compute the local matrices, positions, rotations and scales for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.copy( bone.parent.matrixWorld ).invert(); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for ( let i = 0, il = bones.length; i < il; i ++ ) { // compute the offset between the current and the original transform const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); _offsetMatrix.toArray( boneMatrices, i * 16 ); } if ( boneTexture !== null ) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton( this.bones, this.boneInverses ); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo( size ); size = Math.max( size, 4 ); const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( this.boneMatrices ); // copy current values const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName( name ) { for ( let i = 0, il = this.bones.length; i < il; i ++ ) { const bone = this.bones[ i ]; if ( bone.name === name ) { return bone; } } return undefined; } dispose( ) { if ( this.boneTexture !== null ) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON( json, bones ) { this.uuid = json.uuid; for ( let i = 0, l = json.bones.length; i < l; i ++ ) { const uuid = json.bones[ i ]; let bone = bones[ uuid ]; if ( bone === undefined ) { console.warn( "THREE.Skeleton: No bone found with UUID:", uuid ); bone = new Bone(); } this.bones.push( bone ); this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for ( let i = 0, l = bones.length; i < l; i ++ ) { const bone = bones[ i ]; data.bones.push( bone.uuid ); const boneInverse = boneInverses[ i ]; data.boneInverses.push( boneInverse.toArray() ); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { super( array, itemSize, normalized ); this.isInstancedBufferAttribute = true; this.meshPerAttribute = meshPerAttribute; } copy( source ) { super.copy( source ); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); const _instanceIntersects = []; const _box3 = /*@__PURE__*/ new Box3(); const _identity = /*@__PURE__*/ new Matrix4(); const _mesh = /*@__PURE__*/ new Mesh(); const _sphere$2 = /*@__PURE__*/ new Sphere(); class InstancedMesh extends Mesh { constructor( geometry, material, count ) { super( geometry, material ); this.isInstancedMesh = true; this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); this.instanceColor = null; this.count = count; this.boundingBox = null; this.boundingSphere = null; for ( let i = 0; i < count; i ++ ) { this.setMatrixAt( i, _identity ); } } computeBoundingBox() { const geometry = this.geometry; const count = this.count; if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } this.boundingBox.makeEmpty(); for ( let i = 0; i < count; i ++ ) { this.getMatrixAt( i, _instanceLocalMatrix ); _box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix ); this.boundingBox.union( _box3 ); } } computeBoundingSphere() { const geometry = this.geometry; const count = this.count; if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } this.boundingSphere.makeEmpty(); for ( let i = 0; i < count; i ++ ) { this.getMatrixAt( i, _instanceLocalMatrix ); _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix ); this.boundingSphere.union( _sphere$2 ); } } copy( source, recursive ) { super.copy( source, recursive ); this.instanceMatrix.copy( source.instanceMatrix ); if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); this.count = source.count; if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; } getColorAt( index, color ) { color.fromArray( this.instanceColor.array, index * 3 ); } getMatrixAt( index, matrix ) { matrix.fromArray( this.instanceMatrix.array, index * 16 ); } raycast( raycaster, intersects ) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if ( _mesh.material === undefined ) return; // test with bounding sphere first if ( this.boundingSphere === null ) this.computeBoundingSphere(); _sphere$2.copy( this.boundingSphere ); _sphere$2.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; // now test each instance for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { // calculate the world matrix for each instance this.getMatrixAt( instanceId, _instanceLocalMatrix ); _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast( raycaster, _instanceIntersects ); // process the result of raycast for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { const intersect = _instanceIntersects[ i ]; intersect.instanceId = instanceId; intersect.object = this; intersects.push( intersect ); } _instanceIntersects.length = 0; } } setColorAt( index, color ) { if ( this.instanceColor === null ) { this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); } color.toArray( this.instanceColor.array, index * 3 ); } setMatrixAt( index, matrix ) { matrix.toArray( this.instanceMatrix.array, index * 16 ); } updateMorphTargets() { } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class LineBasicMaterial extends Material { constructor( parameters ) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _start$1 = /*@__PURE__*/ new Vector3(); const _end$1 = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = [ 0 ]; for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { _start$1.fromBufferAttribute( positionAttribute, i - 1 ); _end$1.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += _start$1.distanceTo( _end$1 ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$1.copy( geometry.boundingSphere ); _sphere$1.applyMatrix4( matrixWorld ); _sphere$1.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; // _inverseMatrix$1.copy( matrixWorld ).invert(); _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { const a = index.getX( i ); const b = index.getX( i + 1 ); vStart.fromBufferAttribute( positionAttribute, a ); vEnd.fromBufferAttribute( positionAttribute, b ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { vStart.fromBufferAttribute( positionAttribute, i ); vEnd.fromBufferAttribute( positionAttribute, i + 1 ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { _start.fromBufferAttribute( positionAttribute, i ); _end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } } class LineLoop extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineLoop = true; this.type = "LineLoop"; } } class PointsMaterial extends Material { constructor( parameters ) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ); _sphere.applyMatrix4( matrixWorld ); _sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; // _inverseMatrix.copy( matrixWorld ).invert(); _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i ++ ) { const a = index.getX( i ); _position$2.fromBufferAttribute( positionAttribute, a ); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end; i < l; i ++ ) { _position$2.fromBufferAttribute( positionAttribute, i ); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { const rayPointDistanceSq = _ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { const intersectPoint = new Vector3(); _ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); const distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint, index: index, face: null, object: object } ); } } class VideoTexture extends Texture { constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isVideoTexture = true; this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback( updateVideo ); } if ( "requestVideoFrameCallback" in video ) { video.requestVideoFrameCallback( updateVideo ); } } clone() { return new this.constructor( this.image ).copy( this ); } update() { const video = this.image; const hasVideoFrameCallback = "requestVideoFrameCallback" in video; if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } class FramebufferTexture extends Texture { constructor( width, height ) { super( { width, height } ); this.isFramebufferTexture = true; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } class CompressedTexture extends Texture { constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace ) { super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); this.isCompressedTexture = true; this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } class CompressedArrayTexture extends CompressedTexture { constructor( mipmaps, width, height, depth, format, type ) { super( mipmaps, width, height, format, type ); this.isCompressedArrayTexture = true; this.image.depth = depth; this.wrapR = ClampToEdgeWrapping; } } class CompressedCubeTexture extends CompressedTexture { constructor( images, format, type ) { super( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping ); this.isCompressedCubeTexture = true; this.isCubeTexture = true; this.image = images; } } class CanvasTexture extends Texture { constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isCanvasTexture = true; this.needsUpdate = true; } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); } // Get sequence of points using getPoint( t ) getPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; } // Get list of cumulative segment lengths getLengths( divisions = this.arcLengthDivisions ) { if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint( 0 ); let sum = 0; cache.push( 0 ); for ( let p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping( u, distance ) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[ i ]; const lengthAfter = arcLengths[ i + 1 ]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t const t = ( i + segmentFraction ) / ( il - 1 ); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent( t, optionalTarget ) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; const pt1 = this.getPoint( t1 ); const pt2 = this.getPoint( t2 ); const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); tangent.copy( pt2 ).sub( pt1 ).normalize(); return tangent; } getTangentAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getTangent( t, optionalTarget ); } computeFrenetFrames( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for ( let i = 0; i <= segments; i ++ ) { const u = i / segments; tangents[ i ] = this.getTangentAt( u, new Vector3() ); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs( tangents[ 0 ].x ); const ty = Math.abs( tangents[ 0 ].y ); const tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( let i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( let i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.6, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { super(); this.isEllipseCurve = true; this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint( t, optionalTarget ) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos( angle ); let y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { const cos = Math.cos( this.aRotation ); const sin = Math.sin( this.aRotation ); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set( x, y ); } copy( source ) { super.copy( source ); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON( json ) { super.fromJSON( json ); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } class ArcCurve extends EllipseCurve { constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); this.isArcCurve = true; this.type = "ArcCurve"; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5 ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const points = this.points; const l = points.length; const p = ( l - ( this.closed ? 0 : 1 ) ) * t; let intPoint = Math.floor( p ); let weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } const p1 = points[ intPoint % l ]; const p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom( t, p0, p1, p2, p3 ) { const v0 = ( p2 - p0 ) * 0.5; const v1 = ( p3 - p1 ) * 0.5; const t2 = t * t; const t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } // function QuadraticBezierP0( t, p ) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1( t, p ) { return 2 * ( 1 - t ) * t * p; } function QuadraticBezierP2( t, p ) { return t * t * p; } function QuadraticBezier( t, p0, p1, p2 ) { return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + QuadraticBezierP2( t, p2 ); } // function CubicBezierP0( t, p ) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + CubicBezierP3( t, p3 ); } class CubicBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { super(); this.isCubicBezierCurve = true; this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; } } class CubicBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { super(); this.isCubicBezierCurve3 = true; this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; } } class LineCurve extends Curve { constructor( v1 = new Vector2(), v2 = new Vector2() ) { super(); this.isLineCurve = true; this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); } getTangent( t, optionalTarget = new Vector2() ) { return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); } getTangentAt( u, optionalTarget ) { return this.getTangent( u, optionalTarget ); } copy( source ) { super.copy( source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class LineCurve3 extends Curve { constructor( v1 = new Vector3(), v2 = new Vector3() ) { super(); this.isLineCurve3 = true; this.type = "LineCurve3"; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); } getTangent( t, optionalTarget = new Vector3() ) { return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); } getTangentAt( u, optionalTarget ) { return this.getTangent( u, optionalTarget ); } copy( source ) { super.copy( source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class QuadraticBezierCurve extends Curve { constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { super(); this.isQuadraticBezierCurve = true; this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class QuadraticBezierCurve3 extends Curve { constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { super(); this.isQuadraticBezierCurve3 = true; this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ), QuadraticBezier( t, v0.z, v1.z, v2.z ) ); return point; } copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; } } class SplineCurve extends Curve { constructor( points = [] ) { super(); this.isSplineCurve = true; this.type = "SplineCurve"; this.points = points; } getPoint( t, optionalTarget = new Vector2() ) { const point = optionalTarget; const points = this.points; const p = ( points.length - 1 ) * t; const intPoint = Math.floor( p ); const weight = p - intPoint; const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; const p1 = points[ intPoint ]; const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; point.set( CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector2().fromArray( point ) ); } return this; } } var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add( curve ) { this.curves.push( curve ); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[ 0 ].getPoint( 0 ); const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint( t, optionalTarget ) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { const diff = curveLengths[ i ] - d; const curve = this.curves[ i ]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u, optionalTarget ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; } copy( source ) { super.copy( source ); this.curves = []; for ( let i = 0, l = source.curves.length; i < l; i ++ ) { const curve = source.curves[ i ]; this.curves.push( curve.clone() ); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for ( let i = 0, l = this.curves.length; i < l; i ++ ) { const curve = this.curves[ i ]; data.curves.push( curve.toJSON() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.autoClose = json.autoClose; this.curves = []; for ( let i = 0, l = json.curves.length; i < l; i ++ ) { const curve = json.curves[ i ]; this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } return this; } } class Path extends CurvePath { constructor( points ) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if ( points ) { this.setFromPoints( points ); } } setFromPoints( points ) { this.moveTo( points[ 0 ].x, points[ 0 ].y ); for ( let i = 1, l = points.length; i < l; i ++ ) { this.lineTo( points[ i ].x, points[ i ].y ); } return this; } moveTo( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? return this; } lineTo( x, y ) { const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); return this; } quadraticCurveTo( aCPx, aCPy, aX, aY ) { const curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); return this; } bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { const curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); return this; } splineThru( pts /*Array of Vector*/ ) { const npts = [ this.currentPoint.clone() ].concat( pts ); const curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); return this; } arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); return this; } absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); return this; } ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); return this; } absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); const lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); return this; } copy( source ) { super.copy( source ); this.currentPoint.copy( source.currentPoint ); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON( json ) { super.fromJSON( json ); this.currentPoint.fromArray( json.currentPoint ); return this; } } class LatheGeometry extends BufferGeometry { constructor( points = [ new Vector2( 0, - 0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp( phiLength, 0, Math.PI * 2 ); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { switch ( j ) { case 0: // special handling for 1st vertex on path dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; prevNormal.copy( normal ); normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); break; case ( points.length - 1 ): // special handling for last Vertex on path initNormals.push( prevNormal.x, prevNormal.y, prevNormal.z ); break; default: // default handling for all vertices in between dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; curNormal.copy( normal ); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); prevNormal.copy( curNormal ); } } // generate vertices, uvs and normals for ( let i = 0; i <= segments; i ++ ) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin( phi ); const cos = Math.cos( phi ); for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); // normal const x = initNormals[ 3 * j + 0 ] * sin; const y = initNormals[ 3 * j + 1 ]; const z = initNormals[ 3 * j + 0 ] * cos; normals.push( x, y, z ); } } // indices for ( let i = 0; i < segments; i ++ ) { for ( let j = 0; j < ( points.length - 1 ); j ++ ) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push( a, b, d ); indices.push( c, d, b ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength ); } } class CapsuleGeometry extends LatheGeometry { constructor( radius = 1, length = 1, capSegments = 4, radialSegments = 8 ) { const path = new Path(); path.absarc( 0, - length / 2, radius, Math.PI * 1.5, 0 ); path.absarc( 0, length / 2, radius, 0, Math.PI * 0.5 ); super( path.getPoints( capSegments ), radialSegments ); this.type = "CapsuleGeometry"; this.parameters = { radius: radius, length: length, capSegments: capSegments, radialSegments: radialSegments, }; } static fromJSON( data ) { return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments ); } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max( 3, segments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( let i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); } } class CylinderGeometry extends BufferGeometry { constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor( radialSegments ); heightSegments = Math.floor( heightSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( let y = 0; y <= heightSegments; y ++ ) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin( theta ); const cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { for ( let y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices const a = indexArray[ y ][ x ]; const b = indexArray[ y + 1 ][ x ]; const c = indexArray[ y + 1 ][ x + 1 ]; const d = indexArray[ y ][ x + 1 ]; // faces indices.push( a, b, d ); indices.push( b, c, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = ( top === true ) ? radiusTop : radiusBottom; const sign = ( top === true ) ? 1 : - 1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( let x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, halfHeight * sign, 0 ); // normal normals.push( 0, sign, 0 ); // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos( theta ); const sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); } } class ConeGeometry extends CylinderGeometry { constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON( data ) { return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); } } class PolyhedronGeometry extends BufferGeometry { constructor( vertices = [], indices = [], radius = 1, detail = 0 ) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius applyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute( "position", new Float32BufferAttribute( vertexBuffer, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvBuffer, 2 ) ); if ( detail === 0 ) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide( detail ) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivision with the given detail value for ( let i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for ( let i = 0; i <= cols; i ++ ) { v[ i ] = []; const aj = a.clone().lerp( c, i / cols ); const bj = b.clone().lerp( c, i / cols ); const rows = cols - i; for ( let j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( let i = 0; i < cols; i ++ ) { for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { const k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function applyRadius( radius ) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( let i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for ( let i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; const u = azimuth( vertex ) / 2 / Math.PI + 0.5; const v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( let i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face const x0 = uvBuffer[ i + 0 ]; const x1 = uvBuffer[ i + 2 ]; const x2 = uvBuffer[ i + 4 ]; const max = Math.max( x0, x1, x2 ); const min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { const stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); const azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details ); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const t = ( 1 + Math.sqrt( 5 ) ) / 2; const r = 1 / t; const vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; const indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; super( vertices, indices, radius, detail ); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new DodecahedronGeometry( data.radius, data.detail ); } } const _v0 = /*@__PURE__*/ new Vector3(); const _v1$1 = /*@__PURE__*/ new Vector3(); const _normal = /*@__PURE__*/ new Vector3(); const _triangle = /*@__PURE__*/ new Triangle(); class EdgesGeometry extends BufferGeometry { constructor( geometry = null, thresholdAngle = 1 ) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if ( geometry !== null ) { const precisionPoints = 4; const precision = Math.pow( 10, precisionPoints ); const thresholdDot = Math.cos( DEG2RAD * thresholdAngle ); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute( "position" ); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [ 0, 0, 0 ]; const vertKeys = [ "a", "b", "c" ]; const hashes = new Array( 3 ); const edgeData = {}; const vertices = []; for ( let i = 0; i < indexCount; i += 3 ) { if ( indexAttr ) { indexArr[ 0 ] = indexAttr.getX( i ); indexArr[ 1 ] = indexAttr.getX( i + 1 ); indexArr[ 2 ] = indexAttr.getX( i + 2 ); } else { indexArr[ 0 ] = i; indexArr[ 1 ] = i + 1; indexArr[ 2 ] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute( positionAttr, indexArr[ 0 ] ); b.fromBufferAttribute( positionAttr, indexArr[ 1 ] ); c.fromBufferAttribute( positionAttr, indexArr[ 2 ] ); _triangle.getNormal( _normal ); // create hashes for the edge from the vertices hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`; hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`; hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`; // skip degenerate triangles if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) { continue; } // iterate over every edge for ( let j = 0; j < 3; j ++ ) { // get the first and next vertex making up the edge const jNext = ( j + 1 ) % 3; const vecHash0 = hashes[ j ]; const vecHash1 = hashes[ jNext ]; const v0 = _triangle[ vertKeys[ j ] ]; const v1 = _triangle[ vertKeys[ jNext ] ]; const hash = `${ vecHash0 }_${ vecHash1 }`; const reverseHash = `${ vecHash1 }_${ vecHash0 }`; if ( reverseHash in edgeData && edgeData[ reverseHash ] ) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) { vertices.push( v0.x, v0.y, v0.z ); vertices.push( v1.x, v1.y, v1.z ); } edgeData[ reverseHash ] = null; } else if ( ! ( hash in edgeData ) ) { // if we"ve already got an edge here then skip adding a new one edgeData[ hash ] = { index0: indexArr[ j ], index1: indexArr[ jNext ], normal: _normal.clone(), }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for ( const key in edgeData ) { if ( edgeData[ key ] ) { const { index0, index1 } = edgeData[ key ]; _v0.fromBufferAttribute( positionAttr, index0 ); _v1$1.fromBufferAttribute( positionAttr, index1 ); vertices.push( _v0.x, _v0.y, _v0.z ); vertices.push( _v1$1.x, _v1$1.y, _v1$1.z ); } } this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } } class Shape extends Path { constructor( points ) { super( points ); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles( divisions ) { const holesPts = []; for ( let i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; } copy( source ) { super.copy( source ); this.holes = []; for ( let i = 0, l = source.holes.length; i < l; i ++ ) { const hole = source.holes[ i ]; this.holes.push( hole.clone() ); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for ( let i = 0, l = this.holes.length; i < l; i ++ ) { const hole = this.holes[ i ]; data.holes.push( hole.toJSON() ); } return data; } fromJSON( json ) { super.fromJSON( json ); this.uuid = json.uuid; this.holes = []; for ( let i = 0, l = json.holes.length; i < l; i ++ ) { const hole = json.holes[ i ]; this.holes.push( new Path().fromJSON( hole ) ); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.4) */ const Earcut = { triangulate: function ( data, holeIndices, dim = 2 ) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; let outerNode = linkedList( data, 0, outerLen, dim, true ); const triangles = []; if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if ( data.length > 80 * dim ) { minX = maxX = data[ 0 ]; minY = maxY = data[ 1 ]; for ( let i = dim; i < outerLen; i += dim ) { x = data[ i ]; y = data[ i + 1 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max( maxX - minX, maxY - minY ); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked( outerNode, triangles, dim, minX, minY, invSize, 0 ); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList( data, start, end, dim, clockwise ) { let i, last; if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } else { for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } if ( last && equals( last, last.next ) ) { removeNode( last ); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints( start, end ) { if ( ! start ) return start; if ( ! end ) end = start; let p = start, again; do { again = false; if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { removeNode( p ); p = end = p.prev; if ( p === p.next ) break; again = true; } else { p = p.next; } } while ( again || p !== end ); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { if ( ! ear ) return; // interlink polygon nodes in z-order if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); let stop = ear, prev, next; // iterate through ears, slicing them one by one while ( ear.prev !== ear.next ) { prev = ear.prev; next = ear.next; if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { // cut off the triangle triangles.push( prev.i / dim | 0 ); triangles.push( ear.i / dim | 0 ); triangles.push( next.i / dim | 0 ); removeNode( ear ); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if ( ear === stop ) { // try filtering points and slicing again if ( ! pass ) { earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); // if this didn"t work, try curing all small self-intersections locally } else if ( pass === 1 ) { ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { splitEarcut( ear, triangles, dim, minX, minY, invSize ); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar( ear ) { const a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); let p = c.next; while ( p !== a ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.next; } return true; } function isEarHashed( ear, minX, minY, invSize ) { const a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); // z-order range for the current triangle bbox; const minZ = zOrder( x0, y0, minX, minY, invSize ), maxZ = zOrder( x1, y1, minX, minY, invSize ); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while ( p && p.z >= minZ && n && n.z <= maxZ ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while ( p && p.z >= minZ ) { if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; } // look for remaining points in increasing z-order while ( n && n.z <= maxZ ) { if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections( start, triangles, dim ) { let p = start; do { const a = p.prev, b = p.next.next; if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { triangles.push( a.i / dim | 0 ); triangles.push( p.i / dim | 0 ); triangles.push( b.i / dim | 0 ); // remove two nodes involved removeNode( p ); removeNode( p.next ); p = start = b; } p = p.next; } while ( p !== start ); return filterPoints( p ); } // try splitting polygon into two and triangulate them independently function splitEarcut( start, triangles, dim, minX, minY, invSize ) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while ( b !== a.prev ) { if ( a.i !== b.i && isValidDiagonal( a, b ) ) { // split the polygon in two by the diagonal let c = splitPolygon( a, b ); // filter colinear points around the cuts a = filterPoints( a, a.next ); c = filterPoints( c, c.next ); // run earcut on each half earcutLinked( a, triangles, dim, minX, minY, invSize, 0 ); earcutLinked( c, triangles, dim, minX, minY, invSize, 0 ); return; } b = b.next; } a = a.next; } while ( a !== start ); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles( data, holeIndices, outerNode, dim ) { const queue = []; let i, len, start, end, list; for ( i = 0, len = holeIndices.length; i < len; i ++ ) { start = holeIndices[ i ] * dim; end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; list = linkedList( data, start, end, dim, false ); if ( list === list.next ) list.steiner = true; queue.push( getLeftmost( list ) ); } queue.sort( compareX ); // process holes from left to right for ( i = 0; i < queue.length; i ++ ) { outerNode = eliminateHole( queue[ i ], outerNode ); } return outerNode; } function compareX( a, b ) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole( hole, outerNode ) { const bridge = findHoleBridge( hole, outerNode ); if ( ! bridge ) { return outerNode; } const bridgeReverse = splitPolygon( bridge, hole ); // filter collinear points around the cuts filterPoints( bridgeReverse, bridgeReverse.next ); return filterPoints( bridge, bridge.next ); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge( hole, outerNode ) { let p = outerNode, qx = - Infinity, m; const hx = hole.x, hy = hole.y; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); if ( x <= hx && x > qx ) { qx = x; m = p.x < p.next.x ? p : p.next; if ( x === hx ) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while ( p !== outerNode ); if ( ! m ) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { m = p; tanMin = tan; } } p = p.next; } while ( p !== stop ); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector( m, p ) { return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; } // interlink polygon nodes in z-order function indexCurve( start, minX, minY, invSize ) { let p = start; do { if ( p.z === 0 ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while ( p !== start ); p.prevZ.nextZ = null; p.prevZ = null; sortLinked( p ); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked( list ) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while ( p ) { numMerges ++; q = p; pSize = 0; for ( i = 0; i < inSize; i ++ ) { pSize ++; q = q.nextZ; if ( ! q ) break; } qSize = inSize; while ( pSize > 0 || ( qSize > 0 && q ) ) { if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { e = p; p = p.nextZ; pSize --; } else { e = q; q = q.nextZ; qSize --; } if ( tail ) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while ( numMerges > 1 ); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder( x, y, minX, minY, invSize ) { // coords are transformed into non-negative 15-bit integer range x = ( x - minX ) * invSize | 0; y = ( y - minY ) * invSize | 0; x = ( x | ( x << 8 ) ) & 0x00FF00FF; x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; x = ( x | ( x << 2 ) ) & 0x33333333; x = ( x | ( x << 1 ) ) & 0x55555555; y = ( y | ( y << 8 ) ) & 0x00FF00FF; y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; y = ( y | ( y << 2 ) ) & 0x33333333; y = ( y | ( y << 1 ) ) & 0x55555555; return x | ( y << 1 ); } // find the leftmost node of a polygon ring function getLeftmost( start ) { let p = start, leftmost = start; do { if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; p = p.next; } while ( p !== start ); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { return ( cx - px ) * ( ay - py ) >= ( ax - px ) * ( cy - py ) && ( ax - px ) * ( by - py ) >= ( bx - px ) * ( ay - py ) && ( bx - px ) * ( cy - py ) >= ( cx - px ) * ( by - py ); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal( a, b ) { return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones"t intersect other edges ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case } // signed area of a triangle function area( p, q, r ) { return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); } // check if two points are equal function equals( p1, p2 ) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects( p1, q1, p2, q2 ) { const o1 = sign( area( p1, q1, p2 ) ); const o2 = sign( area( p1, q1, q2 ) ); const o3 = sign( area( p2, q2, p1 ) ); const o4 = sign( area( p2, q2, q1 ) ); if ( o1 !== o2 && o3 !== o4 ) return true; // general case if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment( p, q, r ) { return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); } function sign( num ) { return num > 0 ? 1 : num < 0 ? - 1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon( a, b ) { let p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects( p, p.next, a, b ) ) return true; p = p.next; } while ( p !== a ); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside( a, b ) { return area( a.prev, a, a.next ) < 0 ? area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside( a, b ) { let p = a, inside = false; const px = ( a.x + b.x ) / 2, py = ( a.y + b.y ) / 2; do { if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) inside = ! inside; p = p.next; } while ( p !== a ); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon( a, b ) { const a2 = new Node( a.i, a.x, a.y ), b2 = new Node( b.i, b.x, b.y ), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode( i, x, y, last ) { const p = new Node( i, x, y ); if ( ! last ) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode( p ) { p.next.prev = p.prev; p.prev.next = p.next; if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; } function Node( i, x, y ) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea( data, start, end, dim ) { let sum = 0; for ( let i = start, j = end - dim; i < end; i += dim ) { sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area( contour ) { const n = contour.length; let a = 0.0; for ( let p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; } static isClockWise( pts ) { return ShapeUtils.area( pts ) < 0; } static triangulateShape( contour, holes ) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts( contour ); addContour( vertices, contour ); // let holeIndex = contour.length; holes.forEach( removeDupEndPts ); for ( let i = 0; i < holes.length; i ++ ) { holeIndices.push( holeIndex ); holeIndex += holes[ i ].length; addContour( vertices, holes[ i ] ); } // const triangles = Earcut.triangulate( vertices, holeIndices ); // for ( let i = 0; i < triangles.length; i += 3 ) { faces.push( triangles.slice( i, i + 3 ) ); } return faces; } } function removeDupEndPts( points ) { const l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } function addContour( vertices, contour ) { for ( let i = 0; i < contour.length; i ++ ) { vertices.push( contour[ i ].x ); vertices.push( contour[ i ].y ); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; const scope = this; const verticesArray = []; const uvArray = []; for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; addShape( shape ); } // build geometry this.setAttribute( "position", new Float32BufferAttribute( verticesArray, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvArray, 2 ) ); this.computeVertexNormals(); // functions function addShape( shape ) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; const depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints( curveSegments ); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return pt.clone().addScaledVector( vec, size ); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt( v_prev_lensq ); const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } const contourMovements = []; for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = []; for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( let b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos( t * Math.PI / 2 ); const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape for ( let i = 0, il = contour.length; i < il; i ++ ) { const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( let i = 0, il = ahole.length; i < il; i ++ ) { const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for ( let i = 0; i < vlen; i ++ ) { const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices for ( let s = 1; s <= steps; s ++ ) { for ( let i = 0; i < vlen; i ++ ) { const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, depth / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( let b = bevelSegments - 1; b >= 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos( t * Math.PI / 2 ); const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape for ( let i = 0, il = contour.length; i < il; i ++ ) { const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, depth + z ); } // expand holes for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( let i = 0, il = ahole.length; i < il; i ++ ) { const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, depth + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if ( bevelEnabled ) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( let i = 0; i < flen; i ++ ) { const face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } scope.addGroup( start, verticesArray.length / 3 - start, 0 ); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( let h = 0, hl = holes.length; h < hl; h ++ ) { const ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } scope.addGroup( start, verticesArray.length / 3 - start, 1 ); } function sidewalls( contour, layeroffset ) { let i = contour.length; while ( -- i >= 0 ) { const j = i; let k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { const slen1 = vlen * s; const slen2 = vlen * ( s + 1 ); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d ); } } } function v( x, y, z ) { placeholder.push( x ); placeholder.push( y ); placeholder.push( z ); } function f3( a, b, c ) { addVertex( a ); addVertex( b ); addVertex( c ); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); } function f4( a, b, c, d ) { addVertex( a ); addVertex( b ); addVertex( d ); addVertex( b ); addVertex( c ); addVertex( d ); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 3 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); addUV( uvs[ 3 ] ); } function addVertex( index ) { verticesArray.push( placeholder[ index * 3 + 0 ] ); verticesArray.push( placeholder[ index * 3 + 1 ] ); verticesArray.push( placeholder[ index * 3 + 2 ] ); } function addUV( vector2 ) { uvArray.push( vector2.x ); uvArray.push( vector2.y ); } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1( shapes, options, data ); } static fromJSON( data, shapes ) { const geometryShapes = []; for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { const shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } const extrudePath = data.options.extrudePath; if ( extrudePath !== undefined ) { data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); } return new ExtrudeGeometry( geometryShapes, data.options ); } } const WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { const a_x = vertices[ indexA * 3 ]; const a_y = vertices[ indexA * 3 + 1 ]; const b_x = vertices[ indexB * 3 ]; const b_y = vertices[ indexB * 3 + 1 ]; const c_x = vertices[ indexC * 3 ]; const c_y = vertices[ indexC * 3 + 1 ]; return [ new Vector2( a_x, a_y ), new Vector2( b_x, b_y ), new Vector2( c_x, c_y ) ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { const a_x = vertices[ indexA * 3 ]; const a_y = vertices[ indexA * 3 + 1 ]; const a_z = vertices[ indexA * 3 + 2 ]; const b_x = vertices[ indexB * 3 ]; const b_y = vertices[ indexB * 3 + 1 ]; const b_z = vertices[ indexB * 3 + 2 ]; const c_x = vertices[ indexC * 3 ]; const c_y = vertices[ indexC * 3 + 1 ]; const c_z = vertices[ indexC * 3 + 2 ]; const d_x = vertices[ indexD * 3 ]; const d_y = vertices[ indexD * 3 + 1 ]; const d_z = vertices[ indexD * 3 + 2 ]; if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { return [ new Vector2( a_x, 1 - a_z ), new Vector2( b_x, 1 - b_z ), new Vector2( c_x, 1 - c_z ), new Vector2( d_x, 1 - d_z ) ]; } else { return [ new Vector2( a_y, 1 - a_z ), new Vector2( b_y, 1 - b_z ), new Vector2( c_y, 1 - c_z ), new Vector2( d_y, 1 - d_z ) ]; } } }; function toJSON$1( shapes, options, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } data.options = Object.assign( {}, options ); if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const t = ( 1 + Math.sqrt( 5 ) ) / 2; const vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; const indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; super( vertices, indices, radius, detail ); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new IcosahedronGeometry( data.radius, data.detail ); } } class OctahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; const indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; super( vertices, indices, radius, detail ); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new OctahedronGeometry( data.radius, data.detail ); } } class RingGeometry extends BufferGeometry { constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max( 3, thetaSegments ); phiSegments = Math.max( 1, phiSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for ( let j = 0; j <= phiSegments; j ++ ) { for ( let i = 0; i <= thetaSegments; i ++ ) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // increase the radius for next row of vertices radius += radiusStep; } // indices for ( let j = 0; j < phiSegments; j ++ ) { const thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( let i = 0; i < thetaSegments; i ++ ) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); } } class ShapeGeometry extends BufferGeometry { constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( let i = 0; i < shapes.length; i ++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { const indexOffset = vertices.length / 3; const points = shape.extractPoints( curveSegments ); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); } for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { const shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { const shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { const vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // indices for ( let i = 0, l = faces.length; i < l; i ++ ) { const face = faces[ i ]; const a = face[ 0 ] + indexOffset; const b = face[ 1 ] + indexOffset; const c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON( shapes, data ); } static fromJSON( data, shapes ) { const geometryShapes = []; for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { const shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } return new ShapeGeometry( geometryShapes, data.curveSegments ); } } function toJSON( shapes, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } return data; } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max( 3, Math.floor( widthSegments ) ); heightSegments = Math.max( 2, Math.floor( heightSegments ) ); const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for ( let iy = 0; iy <= heightSegments; iy ++ ) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if ( iy === 0 && thetaStart === 0 ) { uOffset = 0.5 / widthSegments; } else if ( iy === heightSegments && thetaEnd === Math.PI ) { uOffset = - 0.5 / widthSegments; } for ( let ix = 0; ix <= widthSegments; ix ++ ) { const u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.copy( vertex ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u + uOffset, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( let iy = 0; iy < heightSegments; iy ++ ) { for ( let ix = 0; ix < widthSegments; ix ++ ) { const a = grid[ iy ][ ix + 1 ]; const b = grid[ iy ][ ix ]; const c = grid[ iy + 1 ][ ix ]; const d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor( radius = 1, detail = 0 ) { const vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; const indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; super( vertices, indices, radius, detail ); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON( data ) { return new TetrahedronGeometry( data.radius, data.detail ); } } class TorusGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2 ) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor( radialSegments ); tubularSegments = Math.floor( tubularSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for ( let j = 0; j <= radialSegments; j ++ ) { for ( let i = 0; i <= tubularSegments; i ++ ) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); normal.subVectors( vertex, center ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( let j = 1; j <= radialSegments; j ++ ) { for ( let i = 1; i <= tubularSegments; i ++ ) { // indices const a = ( tubularSegments + 1 ) * j + i - 1; const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; const d = ( tubularSegments + 1 ) * j + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); } } class TorusKnotGeometry extends BufferGeometry { constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor( tubularSegments ); radialSegments = Math.floor( radialSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for ( let i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segment const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for ( let j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = - tube * Math.cos( v ); const cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( let j = 1; j <= tubularSegments; j ++ ) { for ( let i = 1; i <= radialSegments; i ++ ) { // indices const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); const b = ( radialSegments + 1 ) * j + ( i - 1 ); const c = ( radialSegments + 1 ) * j + i; const d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { const cu = Math.cos( u ); const su = Math.sin( u ); const quOverP = q / p * u; const cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } static fromJSON( data ) { return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); } } class TubeGeometry extends BufferGeometry { constructor( path = new QuadraticBezierCurve3( new Vector3( - 1, - 1, 0 ), new Vector3( - 1, 1, 0 ), new Vector3( 1, 1, 0 ) ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( let i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt( i / tubularSegments, P ); // retrieve corresponding normal and binormal const N = frames.normals[ i ]; const B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( let j = 0; j <= radialSegments; j ++ ) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin( v ); const cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( let j = 1; j <= tubularSegments; j ++ ) { for ( let i = 1; i <= radialSegments; i ++ ) { const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); const b = ( radialSegments + 1 ) * j + ( i - 1 ); const c = ( radialSegments + 1 ) * j + i; const d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( let i = 0; i <= tubularSegments; i ++ ) { for ( let j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON( data ) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry( new Curves[ data.path.type ]().fromJSON( data.path ), data.tubularSegments, data.radius, data.radialSegments, data.closed ); } } class WireframeGeometry extends BufferGeometry { constructor( geometry = null ) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if ( geometry !== null ) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if ( geometry.index !== null ) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; } // create a data structure that contains all edges without duplicates for ( let o = 0, ol = groups.length; o < ol; ++ o ) { const group = groups[ o ]; const groupStart = group.start; const groupCount = group.count; for ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) { for ( let j = 0; j < 3; j ++ ) { const index1 = indices.getX( i + j ); const index2 = indices.getX( i + ( j + 1 ) % 3 ); start.fromBufferAttribute( position, index1 ); end.fromBufferAttribute( position, index2 ); if ( isUniqueEdge( start, end, edges ) === true ) { vertices.push( start.x, start.y, start.z ); vertices.push( end.x, end.y, end.z ); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { for ( let j = 0; j < 3; j ++ ) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + ( ( j + 1 ) % 3 ); start.fromBufferAttribute( position, index1 ); end.fromBufferAttribute( position, index2 ); if ( isUniqueEdge( start, end, edges ) === true ) { vertices.push( start.x, start.y, start.z ); vertices.push( end.x, end.y, end.z ); } } } } // build geometry this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } } function isUniqueEdge( start, end, edges ) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) { return false; } else { edges.add( hash1 ); edges.add( hash2 ); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, CapsuleGeometry: CapsuleGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); class ShadowMaterial extends Material { constructor( parameters ) { super(); this.isShadowMaterial = true; this.type = "ShadowMaterial"; this.color = new Color( 0x000000 ); this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.fog = source.fog; return this; } } class RawShaderMaterial extends ShaderMaterial { constructor( parameters ) { super( parameters ); this.isRawShaderMaterial = true; this.type = "RawShaderMaterial"; } } class MeshStandardMaterial extends Material { constructor( parameters ) { super(); this.isMeshStandardMaterial = true; this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color( 0xffffff ); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.defines = { "STANDARD": "" }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshPhysicalMaterial extends MeshStandardMaterial { constructor( parameters ) { super(); this.isMeshPhysicalMaterial = true; this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.anisotropyRotation = 0; this.anisotropyMap = null; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2( 1, 1 ); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty( this, "reflectivity", { get: function () { return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); }, set: function ( reflectivity ) { this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); } } ); this.iridescenceMap = null; this.iridescenceIOR = 1.3; this.iridescenceThicknessRange = [ 100, 400 ]; this.iridescenceThicknessMap = null; this.sheenColor = new Color( 0x000000 ); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = Infinity; this.attenuationColor = new Color( 1, 1, 1 ); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color( 1, 1, 1 ); this.specularColorMap = null; this._anisotropy = 0; this._clearcoat = 0; this._iridescence = 0; this._sheen = 0.0; this._transmission = 0; this.setValues( parameters ); } get anisotropy() { return this._anisotropy; } set anisotropy( value ) { if ( this._anisotropy > 0 !== value > 0 ) { this.version ++; } this._anisotropy = value; } get clearcoat() { return this._clearcoat; } set clearcoat( value ) { if ( this._clearcoat > 0 !== value > 0 ) { this.version ++; } this._clearcoat = value; } get iridescence() { return this._iridescence; } set iridescence( value ) { if ( this._iridescence > 0 !== value > 0 ) { this.version ++; } this._iridescence = value; } get sheen() { return this._sheen; } set sheen( value ) { if ( this._sheen > 0 !== value > 0 ) { this.version ++; } this._sheen = value; } get transmission() { return this._transmission; } set transmission( value ) { if ( this._transmission > 0 !== value > 0 ) { this.version ++; } this._transmission = value; } copy( source ) { super.copy( source ); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.anisotropy = source.anisotropy; this.anisotropyRotation = source.anisotropyRotation; this.anisotropyMap = source.anisotropyMap; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); this.ior = source.ior; this.iridescence = source.iridescence; this.iridescenceMap = source.iridescenceMap; this.iridescenceIOR = source.iridescenceIOR; this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; this.sheen = source.sheen; this.sheenColor.copy( source.sheenColor ); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy( source.attenuationColor ); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy( source.specularColor ); this.specularColorMap = source.specularColorMap; return this; } } class MeshPhongMaterial extends Material { constructor( parameters ) { super(); this.isMeshPhongMaterial = true; this.type = "MeshPhongMaterial"; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshToonMaterial extends Material { constructor( parameters ) { super(); this.isMeshToonMaterial = true; this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } class MeshNormalMaterial extends Material { constructor( parameters ) { super(); this.isMeshNormalMaterial = true; this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.flatShading = false; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } class MeshLambertMaterial extends Material { constructor( parameters ) { super(); this.isMeshLambertMaterial = true; this.type = "MeshLambertMaterial"; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshMatcapMaterial extends Material { constructor( parameters ) { super(); this.isMeshMatcapMaterial = true; this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color( 0xffffff ); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.defines = { "MATCAP": "" }; this.color.copy( source.color ); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class LineDashedMaterial extends LineBasicMaterial { constructor( parameters ) { super(); this.isLineDashedMaterial = true; this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } // same as Array.prototype.slice, but also works on typed arrays function arraySlice( array, from, to ) { if ( isTypedArray( array ) ) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } return array.slice( from, to ); } // converts an array to a specific type function convertArray( array, type, forceClone ) { if ( ! array || // let "undefined" and "null" pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === "number" ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array } function isTypedArray( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); } // returns an array by which times and values can be sorted function getKeyframeOrder( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } const n = times.length; const result = new Array( n ); for ( let i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; } // uses the array previously returned by "getKeyframeOrder" to sort data function sortedArray( values, stride, order ) { const nValues = values.length; const result = new values.constructor( nValues ); for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { const srcOffset = order[ i ] * stride; for ( let j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; } // function for parsing AOS keyframe formats function flattenJSON( jsonKeys, times, values, valuePropertyName ) { let i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data let value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } function subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for ( let i = 0; i < clip.tracks.length; ++ i ) { const track = clip.tracks[ i ]; const valueSize = track.getValueSize(); const times = []; const values = []; for ( let j = 0; j < track.times.length; ++ j ) { const frame = track.times[ j ] * fps; if ( frame < startFrame || frame >= endFrame ) continue; times.push( track.times[ j ] ); for ( let k = 0; k < valueSize; ++ k ) { values.push( track.values[ j * valueSize + k ] ); } } if ( times.length === 0 ) continue; track.times = convertArray( times, track.times.constructor ); track.values = convertArray( values, track.values.constructor ); tracks.push( track ); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for ( let i = 0; i < clip.tracks.length; ++ i ) { if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { minStartTime = clip.tracks[ i ].times[ 0 ]; } } // shift all tracks such that clip begins at t=0 for ( let i = 0; i < clip.tracks.length; ++ i ) { clip.tracks[ i ].shift( - 1 * minStartTime ); } clip.resetDuration(); return clip; } function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { if ( fps <= 0 ) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for ( let i = 0; i < numTracks; ++ i ) { const referenceTrack = referenceClip.tracks[ i ]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if ( referenceTrackType === "bool" || referenceTrackType === "string" ) continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find( function ( track ) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; } ); if ( targetTrack === undefined ) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if ( referenceTime <= referenceTrack.times[ 0 ] ) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = arraySlice( referenceTrack.values, startIndex, endIndex ); } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = arraySlice( referenceTrack.values, startIndex, endIndex ); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate( referenceTime ); referenceValue = arraySlice( interpolant.resultBuffer, startIndex, endIndex ); } // Conjugate the quaternion if ( referenceTrackType === "quaternion" ) { const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); referenceQuat.toArray( referenceValue ); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for ( let j = 0; j < numTimes; ++ j ) { const valueStart = j * targetValueSize + targetOffset; if ( referenceTrackType === "quaternion" ) { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat( targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart ); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for ( let k = 0; k < valueEnd; ++ k ) { targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } const AnimationUtils = { arraySlice: arraySlice, convertArray: convertArray, isTypedArray: isTypedArray, getKeyframeOrder: getKeyframeOrder, sortedArray: sortedArray, flattenJSON: flattenJSON, subclip: subclip, makeClipAdditive: makeClipAdditive }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate( t ) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( let giveUpAt = i1 + 2; ; ) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_( i1 - 1 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? const t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( let giveUpAt = i1 - 2; ; ) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.copySampleValue_( 0 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { const mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.copySampleValue_( 0 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_( i1 - 1 ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_( index ) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( let i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; } // Template methods for derived classes: interpolate_( /* i1, t0, t, t1 */ ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer } intervalChanged_( /* i1, t0, t1 */ ) { // empty } } /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = - 0; this._offsetPrev = - 0; this._weightNext = - 0; this._offsetNext = - 0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_( i1, t0, t1 ) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = - wP * ppp + 2 * wP * pp - wP * p; const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for ( let i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } class LinearInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( let i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1 /*, t0, t, t1 */ ) { return this.copySampleValue_( i1 - 1 ); } } class KeyframeTrack { constructor( name, times, values, interpolation ) { if ( name === undefined ) throw new Error( "THREE.KeyframeTrack: track name is undefined" ); if ( times === undefined || times.length === 0 ) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name ); this.name = name; this.times = convertArray( times, this.TimeBufferType ); this.values = convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON( track ) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if ( trackType.toJSON !== this.toJSON ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": convertArray( track.times, Array ), "values": convertArray( track.values, Array ) }; const interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); } InterpolantFactoryMethodLinear( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); } InterpolantFactoryMethodSmooth( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); } setInterpolation( interpolation ) { let factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( "THREE.KeyframeTrack:", message ); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift( timeOffset ) { if ( timeOffset !== 0.0 ) { const times = this.times; for ( let i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale( timeScale ) { if ( timeScale !== 1.0 ) { const times = this.times; for ( let i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim( startTime, endTime ) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) { ++ from; } while ( to !== - 1 && times[ to ] > endTime ) { -- to; } ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) { to = Math.max( to, 1 ); from = to - 1; } const stride = this.getValueSize(); this.times = arraySlice( times, from, to ); this.values = arraySlice( this.values, from * stride, to * stride ); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "THREE.KeyframeTrack: Invalid value size in track.", this ); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "THREE.KeyframeTrack: Track is empty.", this ); valid = false; } let prevTime = null; for ( let i = 0; i !== nKeys; i ++ ) { const currTime = times[ i ]; if ( typeof currTime === "number" && isNaN( currTime ) ) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( isTypedArray( values ) ) { for ( let i = 0, n = values.length; i !== n; ++ i ) { const value = values[ i ]; if ( isNaN( value ) ) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value ); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = arraySlice( this.times ), values = arraySlice( this.values ), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for ( let i = 1; i < lastIndex; ++ i ) { let keep = false; const time = times[ i ]; const timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( let j = 0; j !== stride; ++ j ) { const value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; const readOffset = i * stride, writeOffset = writeIndex * stride; for ( let j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = arraySlice( times, 0, writeIndex ); this.values = arraySlice( values, 0, writeIndex * stride ); } else { this.times = times; this.values = values; } return this; } clone() { const times = arraySlice( this.times, 0 ); const values = arraySlice( this.values, 0 ); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack( this.name, times, values ); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } interpolate_( i1, t0, t, t1 ) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = ( t - t0 ) / ( t1 - t0 ); let offset = i1 * stride; for ( let end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; class AnimationClip { constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } } static parse( json ) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); } const clip = new this( json.name, json.duration, tracks, json.blendMode ); clip.uuid = json.uuid; return clip; } static toJSON( clip ) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; } static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for ( let i = 0; i < numMorphTargets; i ++ ) { let times = []; let values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); const order = getKeyframeOrder( times ); times = sortedArray( times, 1, order ); values = sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[ i ].name + "]", times, values ).scale( 1.0 / fps ) ); } return new this( name, - 1, tracks ); } static findByName( objectOrClipArray, name ) { let clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( let i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; } static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { const morphTarget = morphTargets[ i ]; const parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { const name = parts[ 1 ]; let animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } const clips = []; for ( const name in animationToMorphTargets ) { clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; } // parse the animation.hierarchy format static parseAnimation( animation, bones ) { if ( ! animation ) { console.error( "THREE.AnimationClip: No animation in JSONLoader data." ); return null; } const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { const times = []; const values = []; flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || - 1; const hierarchyTracks = animation.hierarchy || []; for ( let h = 0; h < hierarchyTracks.length; h ++ ) { const animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets if ( animationKeys[ 0 ].morphTargets ) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for ( k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[ k ].morphTargets ) { for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( const morphTargetName in morphTargetNames ) { const times = []; const values = []; for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { const animationKey = animationKeys[ k ]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values ) ); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = ".bones[" + bones[ h ].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks ); } } if ( tracks.length === 0 ) { return null; } const clip = new this( clipName, duration, tracks, blendMode ); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for ( let i = 0, n = tracks.length; i !== n; ++ i ) { const track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; return this; } trim() { for ( let i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; } validate() { let valid = true; for ( let i = 0; i < this.tracks.length; i ++ ) { valid = valid && this.tracks[ i ].validate(); } return valid; } optimize() { for ( let i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } clone() { const tracks = []; for ( let i = 0; i < this.tracks.length; i ++ ) { tracks.push( this.tracks[ i ].clone() ); } return new this.constructor( this.name, this.duration, tracks, this.blendMode ); } toJSON() { return this.constructor.toJSON( this ); } } function getTrackTypeForValueTypeName( typeName ) { switch ( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "THREE.KeyframeTrack: Unsupported typeName: " + typeName ); } function parseKeyframeTrack( json ) { if ( json.type === undefined ) { throw new Error( "THREE.KeyframeTrack: track type undefined, can not parse" ); } const trackType = getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { const times = [], values = []; flattenJSON( json.keys, times, values, "value" ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } } const Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor( onLoad, onProgress, onError ) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; this.resolveURL = function ( url ) { if ( urlModifier ) { return urlModifier( url ); } return url; }; this.setURLModifier = function ( transform ) { urlModifier = transform; return this; }; this.addHandler = function ( regex, loader ) { handlers.push( regex, loader ); return this; }; this.removeHandler = function ( regex ) { const index = handlers.indexOf( regex ); if ( index !== - 1 ) { handlers.splice( index, 2 ); } return this; }; this.getHandler = function ( file ) { for ( let i = 0, l = handlers.length; i < l; i += 2 ) { const regex = handlers[ i ]; const loader = handlers[ i + 1 ]; if ( regex.global ) regex.lastIndex = 0; // see #17920 if ( regex.test( file ) ) { return loader; } } return null; }; } } const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); class Loader { constructor( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load( /* url, onLoad, onProgress, onError */ ) {} loadAsync( url, onProgress ) { const scope = this; return new Promise( function ( resolve, reject ) { scope.load( url, resolve, onProgress, reject ); } ); } parse( /* data */ ) {} setCrossOrigin( crossOrigin ) { this.crossOrigin = crossOrigin; return this; } setWithCredentials( value ) { this.withCredentials = value; return this; } setPath( path ) { this.path = path; return this; } setResourcePath( resourcePath ) { this.resourcePath = resourcePath; return this; } setRequestHeader( requestHeader ) { this.requestHeader = requestHeader; return this; } } Loader.DEFAULT_MATERIAL_NAME = "__DEFAULT"; const loading = {}; class HttpError extends Error { constructor( message, response ) { super( message ); this.response = response; } } class FileLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const cached = Cache.get( url ); if ( cached !== undefined ) { this.manager.itemStart( url ); setTimeout( () => { if ( onLoad ) onLoad( cached ); this.manager.itemEnd( url ); }, 0 ); return cached; } // Check if request is duplicate if ( loading[ url ] !== undefined ) { loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); return; } // Initialise array for duplicate requests loading[ url ] = []; loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError, } ); // create request const req = new Request( url, { headers: new Headers( this.requestHeader ), credentials: this.withCredentials ? "include" : "same-origin", // An abort controller could be added within a future PR } ); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch( req ) .then( response => { if ( response.status === 200 || response.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if ( response.status === 0 ) { console.warn( "THREE.FileLoader: HTTP Status 0 received." ); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if ( typeof ReadableStream === "undefined" || response.body === undefined || response.body.getReader === undefined ) { return response; } const callbacks = loading[ url ]; const reader = response.body.getReader(); // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get( "Content-Length" ) || response.headers.get( "X-File-Size" ); const total = contentLength ? parseInt( contentLength ) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream( { start( controller ) { readData(); function readData() { reader.read().then( ( { done, value } ) => { if ( done ) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent( "progress", { lengthComputable, loaded, total } ); for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onProgress ) callback.onProgress( event ); } controller.enqueue( value ); readData(); } } ); } } } ); return new Response( stream ); } else { throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response ); } } ) .then( response => { switch ( responseType ) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text() .then( text => { const parser = new DOMParser(); return parser.parseFromString( text, mimeType ); } ); case "json": return response.json(); default: if ( mimeType === undefined ) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec( mimeType ); const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; const decoder = new TextDecoder( label ); return response.arrayBuffer().then( ab => decoder.decode( ab ) ); } } } ) .then( data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add( url, data ); const callbacks = loading[ url ]; delete loading[ url ]; for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( data ); } } ) .catch( err => { // Abort errors and other errors are handled the same const callbacks = loading[ url ]; if ( callbacks === undefined ) { // When onLoad was called and url was deleted in `loading` this.manager.itemError( url ); throw err; } delete loading[ url ]; for ( let i = 0, il = callbacks.length; i < il; i ++ ) { const callback = callbacks[ i ]; if ( callback.onError ) callback.onError( err ); } this.manager.itemError( url ); } ) .finally( () => { this.manager.itemEnd( url ); } ); this.manager.itemStart( url ); } setResponseType( value ) { this.responseType = value; return this; } setMimeType( value ) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const animations = []; for ( let i = 0; i < json.length; i ++ ) { const clip = AnimationClip.parse( json[ i ] ); animations.push( clip ); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( "arraybuffer" ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( scope.withCredentials ); let loaded = 0; function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { const texDatas = scope.parse( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { for ( let i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { const texDatas = scope.parse( buffer, true ); if ( texDatas.isCubemap ) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( let f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps: [] }; for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; } } class ImageLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const scope = this; const cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } const image = createElementNS( "img" ); function onImageLoad() { removeEventListeners(); Cache.add( url, this ); if ( onLoad ) onLoad( this ); scope.manager.itemEnd( url ); } function onImageError( event ) { removeEventListeners(); if ( onError ) onError( event ); scope.manager.itemError( url ); scope.manager.itemEnd( url ); } function removeEventListeners() { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); } image.addEventListener( "load", onImageLoad, false ); image.addEventListener( "error", onImageError, false ); if ( url.slice( 0, 5 ) !== "data:" ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart( url ); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( urls, onLoad, onProgress, onError ) { const texture = new CubeTexture(); texture.colorSpace = SRGBColorSpace; const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); let loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( let i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.setRequestHeader( this.requestHeader ); loader.setPath( this.path ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( buffer ) { let texData; try { texData = scope.parse( buffer ); } catch ( error ) { if ( onError !== undefined ) { onError( error ); } else { console.error( error ); return; } } if ( ! texData ) return onError(); // TODO: Remove this when all loaders properly throw errors if ( texData.image !== undefined ) { texture.image = texData.image; } else if ( texData.data !== undefined ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if ( texData.colorSpace !== undefined ) { texture.colorSpace = texData.colorSpace; } else if ( texData.encoding !== undefined ) { // @deprecated, r152 texture.encoding = texData.encoding; } if ( texData.flipY !== undefined ) { texture.flipY = texData.flipY; } if ( texData.format !== undefined ) { texture.format = texData.format; } if ( texData.type !== undefined ) { texture.type = texData.type; } if ( texData.mipmaps !== undefined ) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if ( texData.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } if ( texData.generateMipmaps !== undefined ) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } class TextureLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const texture = new Texture(); const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); loader.load( url, function ( image ) { texture.image = image; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; } } class Light extends Object3D { constructor( color, intensity = 1 ) { super(); this.isLight = true; this.type = "Light"; this.color = new Color( color ); this.intensity = intensity; } dispose() { // Empty here in base class; some subclasses override. } copy( source, recursive ) { super.copy( source, recursive ); this.color.copy( source.color ); this.intensity = source.intensity; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } class HemisphereLight extends Light { constructor( skyColor, groundColor, intensity ) { super( skyColor, intensity ); this.isHemisphereLight = true; this.type = "HemisphereLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } copy( source, recursive ) { super.copy( source, recursive ); this.groundColor.copy( source.groundColor ); return this; } } const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); const _lookTarget$1 = /*@__PURE__*/ new Vector3(); class LightShadow { constructor( camera ) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2( 1, 1 ); this._viewportCount = 1; this._viewports = [ new Vector4( 0, 0, 1, 1 ) ]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices( light ) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld$1 ); _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget$1 ); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( _projScreenMatrix$1 ); } getViewport( viewportIndex ) { return this._viewports[ viewportIndex ]; } getFrameExtents() { return this._frameExtents; } dispose() { if ( this.map ) { this.map.dispose(); } if ( this.mapPass ) { this.mapPass.dispose(); } } copy( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; } clone() { return new this.constructor().copy( this ); } toJSON() { const object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); this.isSpotLightShadow = true; this.focus = 1; } updateMatrices( light ) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices( light ); } copy( source ) { super.copy( source ); this.focus = source.focus; return this; } } class SpotLight extends Light { constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2 ) { super( color, intensity ); this.isSpotLight = true; this.type = "SpotLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; this.map = null; this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power( power ) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy( source, recursive ) { super.copy( source, recursive ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); const _lightPositionWorld = /*@__PURE__*/ new Vector3(); const _lookTarget = /*@__PURE__*/ new Vector3(); class PointLightShadow extends LightShadow { constructor() { super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); this.isPointLightShadow = true; this._frameExtents = new Vector2( 4, 2 ); this._viewportCount = 6; this._viewports = [ // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4( 2, 1, 1, 1 ), // negative X new Vector4( 0, 1, 1, 1 ), // positive Z new Vector4( 3, 1, 1, 1 ), // negative Z new Vector4( 1, 1, 1, 1 ), // positive Y new Vector4( 3, 0, 1, 1 ), // negative Y new Vector4( 1, 0, 1, 1 ) ]; this._cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; this._cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; } updateMatrices( light, viewportIndex = 0 ) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if ( far !== camera.far ) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); camera.position.copy( _lightPositionWorld ); _lookTarget.copy( camera.position ); _lookTarget.add( this._cubeDirections[ viewportIndex ] ); camera.up.copy( this._cubeUps[ viewportIndex ] ); camera.lookAt( _lookTarget ); camera.updateMatrixWorld(); shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); this._frustum.setFromProjectionMatrix( _projScreenMatrix ); } } class PointLight extends Light { constructor( color, intensity, distance = 0, decay = 2 ) { super( color, intensity ); this.isPointLight = true; this.type = "PointLight"; this.distance = distance; this.decay = decay; this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power( power ) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / ( 4 * Math.PI ); } dispose() { this.shadow.dispose(); } copy( source, recursive ) { super.copy( source, recursive ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } class DirectionalLightShadow extends LightShadow { constructor() { super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); this.isDirectionalLightShadow = true; } } class DirectionalLight extends Light { constructor( color, intensity ) { super( color, intensity ); this.isDirectionalLight = true; this.type = "DirectionalLight"; this.position.copy( Object3D.DEFAULT_UP ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy( source ) { super.copy( source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } class AmbientLight extends Light { constructor( color, intensity ) { super( color, intensity ); this.isAmbientLight = true; this.type = "AmbientLight"; } } class RectAreaLight extends Light { constructor( color, intensity, width = 10, height = 10 ) { super( color, intensity ); this.isRectAreaLight = true; this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power( power ) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / ( this.width * this.height * Math.PI ); } copy( source ) { super.copy( source ); this.width = source.width; this.height = source.height; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.width = this.width; data.object.height = this.height; return data; } } /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.isSphericalHarmonics3 = true; this.coefficients = []; for ( let i = 0; i < 9; i ++ ) { this.coefficients.push( new Vector3() ); } } set( coefficients ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].copy( coefficients[ i ] ); } return this; } zero() { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].set( 0, 0, 0 ); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt( normal, target ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); // band 1 target.addScaledVector( coeff[ 1 ], 0.488603 * y ); target.addScaledVector( coeff[ 2 ], 0.488603 * z ); target.addScaledVector( coeff[ 3 ], 0.488603 * x ); // band 2 target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt( normal, target ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 // band 1 target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); // band 2 target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 return target; } add( sh ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].add( sh.coefficients[ i ] ); } return this; } addScaledSH( sh, s ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); } return this; } scale( s ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].multiplyScalar( s ); } return this; } lerp( sh, alpha ) { for ( let i = 0; i < 9; i ++ ) { this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); } return this; } equals( sh ) { for ( let i = 0; i < 9; i ++ ) { if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { return false; } } return true; } copy( sh ) { return this.set( sh.coefficients ); } clone() { return new this.constructor().copy( this ); } fromArray( array, offset = 0 ) { const coefficients = this.coefficients; for ( let i = 0; i < 9; i ++ ) { coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); } return this; } toArray( array = [], offset = 0 ) { const coefficients = this.coefficients; for ( let i = 0; i < 9; i ++ ) { coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt( normal, shBasis ) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[ 0 ] = 0.282095; // band 1 shBasis[ 1 ] = 0.488603 * y; shBasis[ 2 ] = 0.488603 * z; shBasis[ 3 ] = 0.488603 * x; // band 2 shBasis[ 4 ] = 1.092548 * x * y; shBasis[ 5 ] = 1.092548 * y * z; shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); shBasis[ 7 ] = 1.092548 * x * z; shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); } } class LightProbe extends Light { constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { super( undefined, intensity ); this.isLightProbe = true; this.sh = sh; } copy( source ) { super.copy( source ); this.sh.copy( source.sh ); return this; } fromJSON( json ) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray( json.sh ); return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.object.sh = this.sh.toArray(); return data; } } class MaterialLoader extends Loader { constructor( manager ) { super( manager ); this.textures = {}; } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( scope.manager ); loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( "THREE.MaterialLoader: Undefined texture", name ); } return textures[ name ]; } const material = MaterialLoader.createMaterialFromType( json.type ); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.sheen !== undefined ) material.sheen = json.sheen; if ( json.sheenColor !== undefined ) material.sheenColor = new Color().setHex( json.sheenColor ); if ( json.sheenRoughness !== undefined ) material.sheenRoughness = json.sheenRoughness; if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity; if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex( json.specularColor ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; if ( json.iridescence !== undefined ) material.iridescence = json.iridescence; if ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR; if ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange; if ( json.transmission !== undefined ) material.transmission = json.transmission; if ( json.thickness !== undefined ) material.thickness = json.thickness; if ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance; if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor ); if ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy; if ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.combine !== undefined ) material.combine = json.combine; if ( json.side !== undefined ) material.side = json.side; if ( json.shadowSide !== undefined ) material.shadowSide = json.shadowSide; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.alphaHash !== undefined ) material.alphaHash = json.alphaHash; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.rotation !== undefined ) material.rotation = json.rotation; if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; if ( json.scale !== undefined ) material.scale = json.scale; if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; if ( json.dithering !== undefined ) material.dithering = json.dithering; if ( json.alphaToCoverage !== undefined ) material.alphaToCoverage = json.alphaToCoverage; if ( json.premultipliedAlpha !== undefined ) material.premultipliedAlpha = json.premultipliedAlpha; if ( json.forceSinglePass !== undefined ) material.forceSinglePass = json.forceSinglePass; if ( json.visible !== undefined ) material.visible = json.visible; if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; if ( json.userData !== undefined ) material.userData = json.userData; if ( json.vertexColors !== undefined ) { if ( typeof json.vertexColors === "number" ) { material.vertexColors = ( json.vertexColors > 0 ) ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if ( json.uniforms !== undefined ) { for ( const name in json.uniforms ) { const uniform = json.uniforms[ name ]; material.uniforms[ name ] = {}; switch ( uniform.type ) { case "t": material.uniforms[ name ].value = getTexture( uniform.value ); break; case "c": material.uniforms[ name ].value = new Color().setHex( uniform.value ); break; case "v2": material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); break; case "v3": material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); break; case "v4": material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); break; case "m3": material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); break; case "m4": material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); break; default: material.uniforms[ name ].value = uniform.value; } } } if ( json.defines !== undefined ) material.defines = json.defines; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.glslVersion !== undefined ) material.glslVersion = json.glslVersion; if ( json.extensions !== undefined ) { for ( const key in json.extensions ) { material.extensions[ key ] = json.extensions[ key ]; } } if ( json.lights !== undefined ) material.lights = json.lights; if ( json.clipping !== undefined ) material.clipping = json.clipping; // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; if ( json.normalScale !== undefined ) { let normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap ); if ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); if ( json.iridescenceMap !== undefined ) material.iridescenceMap = getTexture( json.iridescenceMap ); if ( json.iridescenceThicknessMap !== undefined ) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap ); if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); if ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap ); if ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap ); if ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap ); if ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap ); return material; } setTextures( value ) { this.textures = value; return this; } static createMaterialFromType( type ) { const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material }; return new materialLib[ type ](); } } class LoaderUtils { static decodeText( array ) { if ( typeof TextDecoder !== "undefined" ) { return new TextDecoder().decode( array ); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for ( let i = 0, il = array.length; i < il; i ++ ) { // Implicitly assumes little-endian. s += String.fromCharCode( array[ i ] ); } try { // merges multi-byte utf-8 characters. return decodeURIComponent( escape( s ) ); } catch ( e ) { // see #16358 return s; } } static extractUrlBase( url ) { const index = url.lastIndexOf( "/" ); if ( index === - 1 ) return "./"; return url.slice( 0, index + 1 ); } static resolveURL( url, path ) { // Invalid URL if ( typeof url !== "string" || url === "" ) return ""; // Host Relative URL if ( /^https?:///i.test( path ) && /^//.test( url ) ) { path = path.replace( /(^https?://[^/]+).*/i, "$1" ); } // Absolute URL http://,https://,// if ( /^(https?:)?///i.test( url ) ) return url; // Data URI if ( /^data:.*,.*$/i.test( url ) ) return url; // Blob URL if ( /^blob:.*$/i.test( url ) ) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.isInstancedBufferGeometry = true; this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy( source ) { super.copy( source ); this.instanceCount = source.instanceCount; return this; } toJSON() { const data = super.toJSON(); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } class BufferGeometryLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( scope.manager ); loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); loader.load( url, function ( text ) { try { onLoad( scope.parse( JSON.parse( text ) ) ); } catch ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } }, onProgress, onError ); } parse( json ) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer( json, uuid ) { if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[ uuid ]; const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); const array = getTypedArray( interleavedBuffer.type, buffer ); const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[ uuid ] = ib; return ib; } function getArrayBuffer( json, uuid ) { if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[ uuid ]; const ab = new Uint32Array( arrayBuffer ).buffer; arrayBufferMap[ uuid ] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if ( index !== undefined ) { const typedArray = getTypedArray( index.type, index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } const attributes = json.data.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; let bufferAttribute; if ( attribute.isInterleavedBufferAttribute ) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); } else { const typedArray = getTypedArray( attribute.type, attribute.array ); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); } if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage ); if ( attribute.updateRange !== undefined ) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute( key, bufferAttribute ); } const morphAttributes = json.data.morphAttributes; if ( morphAttributes ) { for ( const key in morphAttributes ) { const attributeArray = morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; let bufferAttribute; if ( attribute.isInterleavedBufferAttribute ) { const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); } else { const typedArray = getTypedArray( attribute.type, attribute.array ); bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); } if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; array.push( bufferAttribute ); } geometry.morphAttributes[ key ] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if ( morphTargetsRelative ) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( let i = 0, n = groups.length; i !== n; ++ i ) { const group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } const boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { const center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } if ( json.name ) geometry.name = json.name; if ( json.userData ) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const path = ( this.path === "" ) ? LoaderUtils.extractUrlBase( url ) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { let json = null; try { json = JSON.parse( text ); } catch ( error ) { if ( onError !== undefined ) onError( error ); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message ); return; } const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { if ( onError !== undefined ) onError( new Error( "THREE.ObjectLoader: Can"t load " + url ) ); console.error( "THREE.ObjectLoader: Can"t load " + url ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); } async loadAsync( url, onProgress ) { const scope = this; const path = ( this.path === "" ) ? LoaderUtils.extractUrlBase( url ) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); const text = await loader.loadAsync( url, onProgress ); const json = JSON.parse( text ); const metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { throw new Error( "THREE.ObjectLoader: Can"t load " + url ); } return await scope.parseAsync( json ); } parse( json, onLoad ) { const animations = this.parseAnimations( json.animations ); const shapes = this.parseShapes( json.shapes ); const geometries = this.parseGeometries( json.geometries, shapes ); const images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); const textures = this.parseTextures( json.textures, images ); const materials = this.parseMaterials( json.materials, textures ); const object = this.parseObject( json.object, geometries, materials, textures, animations ); const skeletons = this.parseSkeletons( json.skeletons, object ); this.bindSkeletons( object, skeletons ); // if ( onLoad !== undefined ) { let hasImages = false; for ( const uuid in images ) { if ( images[ uuid ].data instanceof HTMLImageElement ) { hasImages = true; break; } } if ( hasImages === false ) onLoad( object ); } return object; } async parseAsync( json ) { const animations = this.parseAnimations( json.animations ); const shapes = this.parseShapes( json.shapes ); const geometries = this.parseGeometries( json.geometries, shapes ); const images = await this.parseImagesAsync( json.images ); const textures = this.parseTextures( json.textures, images ); const materials = this.parseMaterials( json.materials, textures ); const object = this.parseObject( json.object, geometries, materials, textures, animations ); const skeletons = this.parseSkeletons( json.skeletons, object ); this.bindSkeletons( object, skeletons ); return object; } parseShapes( json ) { const shapes = {}; if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const shape = new Shape().fromJSON( json[ i ] ); shapes[ shape.uuid ] = shape; } } return shapes; } parseSkeletons( json, object ) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse( function ( child ) { if ( child.isBone ) bones[ child.uuid ] = child; } ); // create skeletons if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const skeleton = new Skeleton().fromJSON( json[ i ], bones ); skeletons[ skeleton.uuid ] = skeleton; } } return skeletons; } parseGeometries( json, shapes ) { const geometries = {}; if ( json !== undefined ) { const bufferGeometryLoader = new BufferGeometryLoader(); for ( let i = 0, l = json.length; i < l; i ++ ) { let geometry; const data = json[ i ]; switch ( data.type ) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse( data ); break; default: if ( data.type in Geometries ) { geometry = Geometries[ data.type ].fromJSON( data, shapes ); } else { console.warn( `THREE.ObjectLoader: Unsupported geometry type "${ data.type }"` ); } } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; if ( data.userData !== undefined ) geometry.userData = data.userData; geometries[ data.uuid ] = geometry; } } return geometries; } parseMaterials( json, textures ) { const cache = {}; // MultiMaterial const materials = {}; if ( json !== undefined ) { const loader = new MaterialLoader(); loader.setTextures( textures ); for ( let i = 0, l = json.length; i < l; i ++ ) { const data = json[ i ]; if ( cache[ data.uuid ] === undefined ) { cache[ data.uuid ] = loader.parse( data ); } materials[ data.uuid ] = cache[ data.uuid ]; } } return materials; } parseAnimations( json ) { const animations = {}; if ( json !== undefined ) { for ( let i = 0; i < json.length; i ++ ) { const data = json[ i ]; const clip = AnimationClip.parse( data ); animations[ clip.uuid ] = clip; } } return animations; } parseImages( json, onLoad ) { const scope = this; const images = {}; let loader; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemError( url ); scope.manager.itemEnd( url ); } ); } function deserializeImage( image ) { if ( typeof image === "string" ) { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test( url ) ? url : scope.resourcePath + url; return loadImage( path ); } else { if ( image.data ) { return { data: getTypedArray( image.type, image.data ), width: image.width, height: image.height }; } else { return null; } } } if ( json !== undefined && json.length > 0 ) { const manager = new LoadingManager( onLoad ); loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( let i = 0, il = json.length; i < il; i ++ ) { const image = json[ i ]; const url = image.url; if ( Array.isArray( url ) ) { // load array of images e.g CubeTexture const imageArray = []; for ( let j = 0, jl = url.length; j < jl; j ++ ) { const currentUrl = url[ j ]; const deserializedImage = deserializeImage( currentUrl ); if ( deserializedImage !== null ) { if ( deserializedImage instanceof HTMLImageElement ) { imageArray.push( deserializedImage ); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); } } } images[ image.uuid ] = new Source( imageArray ); } else { // load single image const deserializedImage = deserializeImage( image.url ); images[ image.uuid ] = new Source( deserializedImage ); } } } return images; } async parseImagesAsync( json ) { const scope = this; const images = {}; let loader; async function deserializeImage( image ) { if ( typeof image === "string" ) { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test( url ) ? url : scope.resourcePath + url; return await loader.loadAsync( path ); } else { if ( image.data ) { return { data: getTypedArray( image.type, image.data ), width: image.width, height: image.height }; } else { return null; } } } if ( json !== undefined && json.length > 0 ) { loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); for ( let i = 0, il = json.length; i < il; i ++ ) { const image = json[ i ]; const url = image.url; if ( Array.isArray( url ) ) { // load array of images e.g CubeTexture const imageArray = []; for ( let j = 0, jl = url.length; j < jl; j ++ ) { const currentUrl = url[ j ]; const deserializedImage = await deserializeImage( currentUrl ); if ( deserializedImage !== null ) { if ( deserializedImage instanceof HTMLImageElement ) { imageArray.push( deserializedImage ); } else { // special case: handle array of data textures for cube textures imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); } } } images[ image.uuid ] = new Source( imageArray ); } else { // load single image const deserializedImage = await deserializeImage( image.url ); images[ image.uuid ] = new Source( deserializedImage ); } } } return images; } parseTextures( json, images ) { function parseConstant( value, type ) { if ( typeof value === "number" ) return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value ); return type[ value ]; } const textures = {}; if ( json !== undefined ) { for ( let i = 0, l = json.length; i < l; i ++ ) { const data = json[ i ]; if ( data.image === undefined ) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined image", data.image ); } const source = images[ data.image ]; const image = source.data; let texture; if ( Array.isArray( image ) ) { texture = new CubeTexture(); if ( image.length === 6 ) texture.needsUpdate = true; } else { if ( image && image.data ) { texture = new DataTexture(); } else { texture = new Texture(); } if ( image ) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); if ( data.channel !== undefined ) texture.channel = data.channel; if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.center !== undefined ) texture.center.fromArray( data.center ); if ( data.rotation !== undefined ) texture.rotation = data.rotation; if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); } if ( data.format !== undefined ) texture.format = data.format; if ( data.internalFormat !== undefined ) texture.internalFormat = data.internalFormat; if ( data.type !== undefined ) texture.type = data.type; if ( data.colorSpace !== undefined ) texture.colorSpace = data.colorSpace; if ( data.encoding !== undefined ) texture.encoding = data.encoding; // @deprecated, r152 if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; if ( data.generateMipmaps !== undefined ) texture.generateMipmaps = data.generateMipmaps; if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; if ( data.compareFunction !== undefined ) texture.compareFunction = data.compareFunction; if ( data.userData !== undefined ) texture.userData = data.userData; textures[ data.uuid ] = texture; } } return textures; } parseObject( data, geometries, materials, textures, animations ) { let object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined geometry", name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( Array.isArray( name ) ) { const array = []; for ( let i = 0, l = name.length; i < l; i ++ ) { const uuid = name[ i ]; if ( materials[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", uuid ); } array.push( materials[ uuid ] ); } return array; } if ( materials[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", name ); } return materials[ name ]; } function getTexture( uuid ) { if ( textures[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined texture", uuid ); } return textures[ uuid ]; } let geometry, material; switch ( data.type ) { case "Scene": object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } else { object.background = getTexture( data.background ); } } if ( data.environment !== undefined ) { object.environment = getTexture( data.environment ); } if ( data.fog !== undefined ) { if ( data.fog.type === "Fog" ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === "FogExp2" ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } if ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness; if ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity; break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "AmbientLight": object = new AmbientLight( data.color, data.intensity ); break; case "DirectionalLight": object = new DirectionalLight( data.color, data.intensity ); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case "LightProbe": object = new LightProbe().fromJSON( data ); break; case "SkinnedMesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); object = new SkinnedMesh( geometry, material ); if ( data.bindMode !== undefined ) object.bindMode = data.bindMode; if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix ); if ( data.skeleton !== undefined ) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); object = new Mesh( geometry, material ); break; case "InstancedMesh": geometry = getGeometry( data.geometry ); material = getMaterial( data.material ); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh( geometry, material, count ); object.instanceMatrix = new InstancedBufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); if ( instanceColor !== undefined ) object.instanceColor = new InstancedBufferAttribute( new Float32Array( instanceColor.array ), instanceColor.itemSize ); break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineLoop": object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineSegments": object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "PointCloud": case "Points": object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "Sprite": object = new Sprite( getMaterial( data.material ) ); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { object.matrix.fromArray( data.matrix ); if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.up !== undefined ) object.up.fromArray( data.up ); if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.layers !== undefined ) object.layers.mask = data.layers; if ( data.children !== undefined ) { const children = data.children; for ( let i = 0; i < children.length; i ++ ) { object.add( this.parseObject( children[ i ], geometries, materials, textures, animations ) ); } } if ( data.animations !== undefined ) { const objectAnimations = data.animations; for ( let i = 0; i < objectAnimations.length; i ++ ) { const uuid = objectAnimations[ i ]; object.animations.push( animations[ uuid ] ); } } if ( data.type === "LOD" ) { if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; const levels = data.levels; for ( let l = 0; l < levels.length; l ++ ) { const level = levels[ l ]; const child = object.getObjectByProperty( "uuid", level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance, level.hysteresis ); } } } return object; } bindSkeletons( object, skeletons ) { if ( Object.keys( skeletons ).length === 0 ) return; object.traverse( function ( child ) { if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) { const skeleton = skeletons[ child.skeleton ]; if ( skeleton === undefined ) { console.warn( "THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton ); } else { child.bind( skeleton, child.bindMatrix ); } } } ); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor( manager ) { super( manager ); this.isImageBitmapLoader = true; if ( typeof createImageBitmap === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported." ); } if ( typeof fetch === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: fetch() not supported." ); } this.options = { premultiplyAlpha: "none" }; } setOptions( options ) { this.options = options; return this; } load( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); const scope = this; const cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } const fetchOptions = {}; fetchOptions.credentials = ( this.crossOrigin === "anonymous" ) ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch( url, fetchOptions ).then( function ( res ) { return res.blob(); } ).then( function ( blob ) { return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: "none" } ) ); } ).then( function ( imageBitmap ) { Cache.add( url, imageBitmap ); if ( onLoad ) onLoad( imageBitmap ); scope.manager.itemEnd( url ); } ).catch( function ( e ) { if ( onError ) onError( e ); scope.manager.itemError( url ); scope.manager.itemEnd( url ); } ); scope.manager.itemStart( url ); } } let _context; class AudioContext { static getContext() { if ( _context === undefined ) { _context = new ( window.AudioContext || window.webkitAudioContext )(); } return _context; } static setContext( value ) { _context = value; } } class AudioLoader extends Loader { constructor( manager ) { super( manager ); } load( url, onLoad, onProgress, onError ) { const scope = this; const loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( buffer ) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice( 0 ); const context = AudioContext.getContext(); context.decodeAudioData( bufferCopy, function ( audioBuffer ) { onLoad( audioBuffer ); }, handleError ); } catch ( e ) { handleError( e ); } }, onProgress, onError ); function handleError( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } scope.manager.itemError( url ); } } } class HemisphereLightProbe extends LightProbe { constructor( skyColor, groundColor, intensity = 1 ) { super( undefined, intensity ); this.isHemisphereLightProbe = true; const color1 = new Color().set( skyColor ); const color2 = new Color().set( groundColor ); const sky = new Vector3( color1.r, color1.g, color1.b ); const ground = new Vector3( color2.r, color2.g, color2.b ); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt( Math.PI ); const c1 = c0 * Math.sqrt( 0.75 ); this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); } } class AmbientLightProbe extends LightProbe { constructor( color, intensity = 1 ) { super( undefined, intensity ); this.isAmbientLightProbe = true; const color1 = new Color().set( color ); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); } } const _eyeRight = /*@__PURE__*/ new Matrix4(); const _eyeLeft = /*@__PURE__*/ new Matrix4(); const _projectionMatrix = /*@__PURE__*/ new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update( camera ) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if ( needsUpdate ) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy( camera.projectionMatrix ); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = ( cache.near * Math.tan( DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[ 12 ] = - eyeSepHalf; _eyeRight.elements[ 12 ] = eyeSepHalf; // for left eye xmin = - ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( _projectionMatrix ); // for right eye xmin = - ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( _projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); } } class Clock { constructor( autoStart = true ) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { const newTime = now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 } const _position$1 = /*@__PURE__*/ new Vector3(); const _quaternion$1 = /*@__PURE__*/ new Quaternion(); const _scale$1 = /*@__PURE__*/ new Vector3(); const _orientation$1 = /*@__PURE__*/ new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose( _position$1, _quaternion$1, _scale$1 ); _orientation$1.set( 0, 0, - 1 ).applyQuaternion( _quaternion$1 ); if ( listener.positionX ) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime( _position$1.x, endTime ); listener.positionY.linearRampToValueAtTime( _position$1.y, endTime ); listener.positionZ.linearRampToValueAtTime( _position$1.z, endTime ); listener.forwardX.linearRampToValueAtTime( _orientation$1.x, endTime ); listener.forwardY.linearRampToValueAtTime( _orientation$1.y, endTime ); listener.forwardZ.linearRampToValueAtTime( _orientation$1.z, endTime ); listener.upX.linearRampToValueAtTime( up.x, endTime ); listener.upY.linearRampToValueAtTime( up.y, endTime ); listener.upZ.linearRampToValueAtTime( up.z, endTime ); } else { listener.setPosition( _position$1.x, _position$1.y, _position$1.z ); listener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z ); } } } class Audio extends Object3D { constructor( listener ) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource( audioNode ) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource( mediaElement ) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource( mediaElement ); this.connect(); return this; } setMediaStreamSource( mediaStream ) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource( mediaStream ); this.connect(); return this; } setBuffer( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = "buffer"; if ( this.autoplay ) this.play(); return this; } play( delay = 0 ) { if ( this.isPlaying === true ) { console.warn( "THREE.Audio: Audio is already playing." ); return; } if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind( this ); source.start( this._startedAt, this._progress + this.offset, this.duration ); this.isPlaying = true; this.source = source; this.setDetune( this.detune ); this.setPlaybackRate( this.playbackRate ); return this.connect(); } pause() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } if ( this.isPlaying === true ) { // update current progress this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; if ( this.loop === true ) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % ( this.duration || this.buffer.duration ); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this._progress = 0; if ( this.source !== null ) { this.source.stop(); this.source.onended = null; } this.isPlaying = false; return this; } connect() { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( let i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } this._connected = true; return this; } disconnect() { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( let i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters( value ) { if ( ! value ) value = []; if ( this._connected === true ) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune( value ) { this.detune = value; if ( this.source.detune === undefined ) return; // only set detune when available if ( this.isPlaying === true ) { this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[ 0 ]; } setFilter( filter ) { return this.setFilters( filter ? [ filter ] : [] ); } setPlaybackRate( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return false; } return this.loop; } setLoop( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; } setLoopStart( value ) { this.loopStart = value; return this; } setLoopEnd( value ) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } } const _position = /*@__PURE__*/ new Vector3(); const _quaternion = /*@__PURE__*/ new Quaternion(); const _scale = /*@__PURE__*/ new Vector3(); const _orientation = /*@__PURE__*/ new Vector3(); class PositionalAudio extends Audio { constructor( listener ) { super( listener ); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect( this.gain ); } connect() { super.connect(); this.panner.connect( this.gain ); } disconnect() { super.disconnect(); this.panner.disconnect( this.gain ); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance( value ) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor( value ) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel( value ) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance( value ) { this.panner.maxDistance = value; return this; } setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; this.matrixWorld.decompose( _position, _quaternion, _scale ); _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion ); const panner = this.panner; if ( panner.positionX ) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime( _position.x, endTime ); panner.positionY.linearRampToValueAtTime( _position.y, endTime ); panner.positionZ.linearRampToValueAtTime( _position.z, endTime ); panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime ); panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime ); panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime ); } else { panner.setPosition( _position.x, _position.y, _position.z ); panner.setOrientation( _orientation.x, _orientation.y, _orientation.z ); } } } class AudioAnalyser { constructor( audio, fftSize = 2048 ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } getFrequencyData() { this.analyser.getByteFrequencyData( this.data ); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for ( let i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } class PropertyMixer { constructor( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch ( typeName ) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array( valueSize * 6 ); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array( valueSize * 5 ); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array( valueSize * 5 ); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( let i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive( weight ) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if ( this.cumulativeWeightAdditive === 0 ) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply( accuIndex ) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } if ( weightAdditive > 0 ) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); } for ( let i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for ( let i = startIndex; i < endIndex; i ++ ) { this.buffer[ i ] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for ( let i = 0; i < this.valueSize; i ++ ) { this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; } } // mix functions _select( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( let i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } } _slerp( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); } _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); // Slerp to the intermediate result Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); } _lerp( buffer, dstOffset, srcOffset, t, stride ) { const s = 1 - t; for ( let i = 0; i !== stride; ++ i ) { const j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { for ( let i = 0; i !== stride; ++ i ) { const j = dstOffset + i; buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp( "[" + _RESERVED_CHARS_RE + "]", "g" ); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace( "\.", "" ) + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /*@__PURE__*/ /((?:WC+[/:])*)/.source.replace( "WC", _wordChar ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace( "WCOD", _wordCharOrDot ); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /*@__PURE__*/ /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", _wordChar ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /*@__PURE__*/ /.(WC+)(?:[(.+)])?/.source.replace( "WC", _wordChar ); const _trackRe = new RegExp( "" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$" ); const _supportedObjectNames = [ "material", "materials", "bones", "map" ]; class Composite { constructor( targetGroup, path, optionalParsedPath ) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); } getValue( array, offset ) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); } setValue( array, offset ) { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } } bind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } } unbind() { const bindings = this._bindings; for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ); this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName( name ) { return name.replace( /s/g, "_" ).replace( _reservedRe, "" ); } static parseTrackName( trackName ) { const matches = _trackRe.exec( trackName ); if ( matches === null ) { throw new Error( "PropertyBinding: Cannot parse trackName: " + trackName ); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], // required propertyIndex: matches[ 6 ] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf( "." ); if ( lastDot !== undefined && lastDot !== - 1 ) { const objectName = results.nodeName.substring( lastDot + 1 ); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { results.nodeName = results.nodeName.substring( 0, lastDot ); results.objectName = objectName; } } if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName ); } return results; } static findNode( root, nodeName ) { if ( nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { const bone = root.skeleton.getBoneByName( nodeName ); if ( bone !== undefined ) { return bone; } } // search into node subtree. if ( root.children ) { const searchNodeSubtree = function ( children ) { for ( let i = 0; i < children.length; i ++ ) { const childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } const result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; const subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct( buffer, offset ) { buffer[ offset ] = this.targetObject[ this.propertyName ]; } _getValue_array( buffer, offset ) { const source = this.resolvedProperty; for ( let i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } } _getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; } _getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } // Direct _setValue_direct( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; } _setValue_direct_setNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } } _setValue_array_setNeedsUpdate( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { const dest = this.resolvedProperty; for ( let i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; } _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); } _setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); } _setValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ); this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.warn( "THREE.PropertyBinding: No target node found for track: " + this.path + "." ); return; } if ( objectName ) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case "materials": if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.materials ) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this ); return; } targetObject = targetObject.material.materials; break; case "bones": if ( ! targetObject.skeleton ) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( let i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; case "map": if ( "map" in targetObject ) { targetObject = targetObject.map; break; } if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.map ) { console.error( "THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.", this ); return; } targetObject = targetObject.material.map; break; default: if ( targetObject[ objectName ] === undefined ) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property const nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { const nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject ); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this ); return; } if ( ! targetObject.geometry.morphAttributes ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this ); return; } if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( Array.isArray( nodeProperty ) ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [ PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray, ]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ [ // Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, ], [ // EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, ], [ // ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, ], [ // HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, ] ]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.isAnimationObjectGroup = true; this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( let i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid; let index = indicesByUUID[ uuid ]; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region const firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ]; let binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject ) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for ( let i = 0, n = arguments.length; i !== n; ++ i ) { const object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region const firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object"s place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; if ( lastIndex > 0 ) { indicesByUUID[ lastObject.uuid ] = index; } objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( let j = 0, m = nBindings; j !== m; ++ j ) { const bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[ path ]; const bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { const object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; } unsubscribe_( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } } class AnimationAction { constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( let i = 0; i !== nTracks; ++ i ) { const interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = - 1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false;// keep feeding the last frame? this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate this.zeroSlopeAtEnd = true;// clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction( this ); return this; } stop() { this._mixer._deactivateAction( this ); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = - 1;// forget previous loops this._startTime = null;// forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction( this ); } startAt( time ) { this._startTime = time; return this; } setLoop( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn( duration ) { return this._scheduleFading( duration, 0, 1 ); } fadeOut( duration ) { return this._scheduleFading( duration, 1, 0 ); } crossFadeFrom( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if ( warp ) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; } crossFadeTo( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); } stopFading() { const weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); } warp( startTimeScale, endTimeScale, duration ) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer if ( ! this.enabled ) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight( time ); return; } const startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action const timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { deltaTime = 0; } else { this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); const clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight( time ); if ( weight > 0 ) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch ( this.blendMode ) { case AdditiveAnimationBlendMode: for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulateAdditive( weight ); } break; case NormalAnimationBlendMode: default: for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } } } _updateWeight( time ) { let weight = 0; if ( this.enabled ) { weight = this.weight; const interpolant = this._weightInterpolant; if ( interpolant !== null ) { const interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale( time ) { let timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { const interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime( deltaTime ) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = ( loop === LoopPingPong ); if ( deltaTime === 0 ) { if ( loopCount === - 1 ) return time; return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; } if ( loop === LoopOnce ) { if ( loopCount === - 1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else { this.time = time; break handle_stop; } if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this.time = time; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime < 0 ? - 1 : 1 } ); } } else { // repetitive Repeat or PingPong if ( loopCount === - 1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around const loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); const pending = this.repetitions - loopCount; if ( pending <= 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime > 0 ? 1 : - 1 } ); } else { // keep running if ( pending === 1 ) { // entering the last round const atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent( { type: "loop", action: this, loopDelta: loopDelta } ); } } else { this.time = time; } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings( atStart, atEnd, pingPong ) { const settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading( duration, weightNow, weightThen ) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } } const _controlInterpolantsResultBuffer = new Float32Array( 1 ); class AnimationMixer extends EventDispatcher { constructor( root ) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction( action, prototypeAction ) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( let i = 0; i !== nTracks; ++ i ) { const track = tracks[ i ], trackName = track.name; let binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { ++ binding.referenceCount; bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } const path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } } _activateAction( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } const bindings = action._propertyBindings; // increment reference counts / sort out state for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } } _deactivateAction( action ) { if ( this._isActiveAction( action ) ) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction( action ) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction( action, clipUuid, rootUuid ) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; } _removeInactiveAction( action ) { const actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = ( action._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); } _removeInactiveBindingsForAction( action ) { const bindings = action._propertyBindings; for ( let i = 0, n = bindings.length; i !== n; ++ i ) { const binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } } _lendAction( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; } _takeBackAction( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding( binding, rootUuid, trackName ) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); } _removeInactiveBinding( binding ) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; if ( Object.keys( bindingByName ).length === 0 ) { delete bindingsByRoot[ rootUuid ]; } } _lendBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; } _takeBackBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++; let interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, _controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; } _takeBackControlInterpolant( interpolant ) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction( clip, optionalRoot, blendMode ) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[ clipUuid ]; let prototypeAction = null; if ( blendMode === undefined ) { if ( clipObject !== null ) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if ( actionsForClip !== undefined ) { const existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; } // get an existing action existingAction( clip, optionalRoot ) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for ( let i = nActions - 1; i >= 0; -- i ) { actions[ i ].stop(); } return this; } // advance the time and update apply the animation update( deltaTime ) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( let i = 0; i !== nActions; ++ i ) { const action = actions[ i ]; action._update( time, deltaTime, timeDirection, accuIndex ); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for ( let i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; } // Allows you to seek to a specific time in an animation. setTime( timeInSeconds ) { this.time = 0; // Zero out time attribute for AnimationMixer object; for ( let i = 0; i < this._actions.length; i ++ ) { this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip( clip ) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { const action = actionsToRemove[ i ]; this._deactivateAction( action ); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } } // free all resources specific to a particular root target object uncacheRoot( root ) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( const clipUuid in actionsByClip ) { const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( const trackName in bindingByName ) { const binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } } // remove a targeted clip from the cache uncacheAction( clip, optionalRoot ) { const action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } class Uniform { constructor( value ) { this.value = value; } clone() { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); } } let id = 0; class UniformsGroup extends EventDispatcher { constructor() { super(); this.isUniformsGroup = true; Object.defineProperty( this, "id", { value: id ++ } ); this.name = ""; this.usage = StaticDrawUsage; this.uniforms = []; } add( uniform ) { this.uniforms.push( uniform ); return this; } remove( uniform ) { const index = this.uniforms.indexOf( uniform ); if ( index !== - 1 ) this.uniforms.splice( index, 1 ); return this; } setName( name ) { this.name = name; return this; } setUsage( value ) { this.usage = value; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); return this; } copy( source ) { this.name = source.name; this.usage = source.usage; const uniformsSource = source.uniforms; this.uniforms.length = 0; for ( let i = 0, l = uniformsSource.length; i < l; i ++ ) { this.uniforms.push( uniformsSource[ i ].clone() ); } return this; } clone() { return new this.constructor().copy( this ); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor( array, stride, meshPerAttribute = 1 ) { super( array, stride ); this.isInstancedInterleavedBuffer = true; this.meshPerAttribute = meshPerAttribute; } copy( source ) { super.copy( source ); this.meshPerAttribute = source.meshPerAttribute; return this; } clone( data ) { const ib = super.clone( data ); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON( data ) { const json = super.toJSON( data ); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } class GLBufferAttribute { constructor( buffer, type, itemSize, elementSize, count ) { this.isGLBufferAttribute = true; this.name = ""; this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } setBuffer( buffer ) { this.buffer = buffer; return this; } setType( type, elementSize ) { this.type = type; this.elementSize = elementSize; return this; } setItemSize( itemSize ) { this.itemSize = itemSize; return this; } setCount( count ) { this.count = count; return this; } } class Raycaster { constructor( origin, direction, near = 0, far = Infinity ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); } setFromCamera( coords, camera ) { if ( camera.isPerspectiveCamera ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); this.camera = camera; } else if ( camera.isOrthographicCamera ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); this.camera = camera; } else { console.error( "THREE.Raycaster: Unsupported camera type: " + camera.type ); } } intersectObject( object, recursive = true, intersects = [] ) { intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; } intersectObjects( objects, recursive = true, intersects = [] ) { for ( let i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.layers.test( raycaster.layers ) ) { object.raycast( raycaster, intersects ); } if ( recursive === true ) { const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor( radius = 1, phi = 0, theta = 0 ) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be between EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; } setFromVector3( v ) { return this.setFromCartesianCoords( v.x, v.y, v.z ); } setFromCartesianCoords( x, y, z ) { this.radius = Math.sqrt( x * x + y * y + z * z ); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( x, z ); this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); } return this; } clone() { return new this.constructor().copy( this ); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor( radius = 1, theta = 0, y = 0 ) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3( v ) { return this.setFromCartesianCoords( v.x, v.y, v.z ); } setFromCartesianCoords( x, y, z ) { this.radius = Math.sqrt( x * x + z * z ); this.theta = Math.atan2( x, z ); this.y = y; return this; } clone() { return new this.constructor().copy( this ); } } const _vector$4 = /*@__PURE__*/ new Vector2(); class Box2 { constructor( min = new Vector2( + Infinity, + Infinity ), max = new Vector2( - Infinity, - Infinity ) ) { this.isBox2 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$4.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); } intersectsBox( box ) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { return this.clampPoint( point, _vector$4 ).distanceTo( point ); } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _startP = /*@__PURE__*/ new Vector3(); const _startEnd = /*@__PURE__*/ new Vector3(); class Line3 { constructor( start = new Vector3(), end = new Vector3() ) { this.start = start; this.end = end; } set( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; } copy( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; } getCenter( target ) { return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); } delta( target ) { return target.subVectors( this.end, this.start ); } distanceSq() { return this.start.distanceToSquared( this.end ); } distance() { return this.start.distanceTo( this.end ); } at( t, target ) { return this.delta( target ).multiplyScalar( t ).add( this.start ); } closestPointToPointParameter( point, clampToLine ) { _startP.subVectors( point, this.start ); _startEnd.subVectors( this.end, this.start ); const startEnd2 = _startEnd.dot( _startEnd ); const startEnd_startP = _startEnd.dot( _startP ); let t = startEnd_startP / startEnd2; if ( clampToLine ) { t = clamp( t, 0, 1 ); } return t; } closestPointToPoint( point, clampToLine, target ) { const t = this.closestPointToPointParameter( point, clampToLine ); return this.delta( target ).multiplyScalar( t ).add( this.start ); } applyMatrix4( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; } equals( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } clone() { return new this.constructor().copy( this ); } } const _vector$3 = /*@__PURE__*/ new Vector3(); class SpotLightHelper extends Object3D { constructor( light, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "SpotLightHelper"; const geometry = new BufferGeometry(); const positions = [ 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 ]; for ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { const p1 = ( i / l ) * Math.PI * 2; const p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); this.light.target.updateWorldMatrix( true, false ); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); _vector$3.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( _vector$3 ); if ( this.color !== undefined ) { this.cone.material.color.set( this.color ); } else { this.cone.material.color.copy( this.light.color ); } } } const _vector$2 = /*@__PURE__*/ new Vector3(); const _boneMatrix = /*@__PURE__*/ new Matrix4(); const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); class SkeletonHelper extends LineSegments { constructor( object ) { const bones = getBoneList( object ); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color( 0, 0, 1 ); const color2 = new Color( 0, 1, 0 ); for ( let i = 0; i < bones.length; i ++ ) { const bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); super( geometry, material ); this.isSkeletonHelper = true; this.type = "SkeletonHelper"; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld( force ) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute( "position" ); _matrixWorldInv.copy( this.root.matrixWorld ).invert(); for ( let i = 0, j = 0; i < bones.length; i ++ ) { const bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); _vector$2.setFromMatrixPosition( _boneMatrix ); position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); _vector$2.setFromMatrixPosition( _boneMatrix ); position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); j += 2; } } geometry.getAttribute( "position" ).needsUpdate = true; super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); } } function getBoneList( object ) { const boneList = []; if ( object.isBone === true ) { boneList.push( object ); } for ( let i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } return boneList; } class PointLightHelper extends Mesh { constructor( light, sphereSize, color ) { const geometry = new SphereGeometry( sphereSize, 4, 2 ); const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); super( geometry, material ); this.light = light; this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { this.material.color.copy( this.light.color ); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/ new Vector3(); const _color1 = /*@__PURE__*/ new Color(); const _color2 = /*@__PURE__*/ new Color(); class HemisphereLightHelper extends Object3D { constructor( light, size, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "HemisphereLightHelper"; const geometry = new OctahedronGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); if ( this.color === undefined ) this.material.vertexColors = true; const position = geometry.getAttribute( "position" ); const colors = new Float32Array( position.count * 3 ); geometry.setAttribute( "color", new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, this.material ) ); this.update(); } dispose() { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); } update() { const mesh = this.children[ 0 ]; if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { const colors = mesh.geometry.getAttribute( "color" ); _color1.copy( this.light.color ); _color2.copy( this.light.groundColor ); for ( let i = 0, l = colors.count; i < l; i ++ ) { const color = ( i < ( l / 2 ) ) ? _color1 : _color2; colors.setXYZ( i, color.r, color.g, color.b ); } colors.needsUpdate = true; } this.light.updateWorldMatrix( true, false ); mesh.lookAt( _vector$1.setFromMatrixPosition( this.light.matrixWorld ).negate() ); } } class GridHelper extends LineSegments { constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { color1 = new Color( color1 ); color2 = new Color( color2 ); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { vertices.push( - halfSize, 0, k, halfSize, 0, k ); vertices.push( k, 0, - halfSize, k, 0, halfSize ); const color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "GridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PolarGridHelper extends LineSegments { constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) { color1 = new Color( color1 ); color2 = new Color( color2 ); const vertices = []; const colors = []; // create the sectors if ( sectors > 1 ) { for ( let i = 0; i < sectors; i ++ ) { const v = ( i / sectors ) * ( Math.PI * 2 ); const x = Math.sin( v ) * radius; const z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); const color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } } // create the rings for ( let i = 0; i < rings; i ++ ) { const color = ( i & 1 ) ? color1 : color2; const r = radius - ( radius / rings * i ); for ( let j = 0; j < divisions; j ++ ) { // first vertex let v = ( j / divisions ) * ( Math.PI * 2 ); let x = Math.sin( v ) * r; let z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "PolarGridHelper"; } dispose() { this.geometry.dispose(); this.material.dispose(); } } const _v1 = /*@__PURE__*/ new Vector3(); const _v2 = /*@__PURE__*/ new Vector3(); const _v3 = /*@__PURE__*/ new Vector3(); class DirectionalLightHelper extends Object3D { constructor( light, size, color ) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = "DirectionalLightHelper"; if ( size === undefined ) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); this.lightPlane = new Line( geometry, material ); this.add( this.lightPlane ); geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.targetLine = new Line( geometry, material ); this.add( this.targetLine ); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { this.light.updateWorldMatrix( true, false ); this.light.target.updateWorldMatrix( true, false ); _v1.setFromMatrixPosition( this.light.matrixWorld ); _v2.setFromMatrixPosition( this.light.target.matrixWorld ); _v3.subVectors( _v2, _v1 ); this.lightPlane.lookAt( _v2 ); if ( this.color !== undefined ) { this.lightPlane.material.color.set( this.color ); this.targetLine.material.color.set( this.color ); } else { this.lightPlane.material.color.copy( this.light.color ); this.targetLine.material.color.copy( this.light.color ); } this.targetLine.lookAt( _v2 ); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/ new Vector3(); const _camera = /*@__PURE__*/ new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor( camera ) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } ); const vertices = []; const colors = []; const pointMap = {}; // near addLine( "n1", "n2" ); addLine( "n2", "n4" ); addLine( "n4", "n3" ); addLine( "n3", "n1" ); // far addLine( "f1", "f2" ); addLine( "f2", "f4" ); addLine( "f4", "f3" ); addLine( "f3", "f1" ); // sides addLine( "n1", "f1" ); addLine( "n2", "f2" ); addLine( "n3", "f3" ); addLine( "n4", "f4" ); // cone addLine( "p", "n1" ); addLine( "p", "n2" ); addLine( "p", "n3" ); addLine( "p", "n4" ); // up addLine( "u1", "u2" ); addLine( "u2", "u3" ); addLine( "u3", "u1" ); // target addLine( "c", "t" ); addLine( "p", "c" ); // cross addLine( "cn1", "cn2" ); addLine( "cn3", "cn4" ); addLine( "cf1", "cf2" ); addLine( "cf3", "cf4" ); function addLine( a, b ) { addPoint( a ); addPoint( b ); } function addPoint( id ) { vertices.push( 0, 0, 0 ); colors.push( 0, 0, 0 ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); super( geometry, material ); this.type = "CameraHelper"; this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); // colors const colorFrustum = new Color( 0xffaa00 ); const colorCone = new Color( 0xff0000 ); const colorUp = new Color( 0x00aaff ); const colorTarget = new Color( 0xffffff ); const colorCross = new Color( 0x333333 ); this.setColors( colorFrustum, colorCone, colorUp, colorTarget, colorCross ); } setColors( frustum, cone, up, target, cross ) { const geometry = this.geometry; const colorAttribute = geometry.getAttribute( "color" ); // near colorAttribute.setXYZ( 0, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 1, frustum.r, frustum.g, frustum.b ); // n1, n2 colorAttribute.setXYZ( 2, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 3, frustum.r, frustum.g, frustum.b ); // n2, n4 colorAttribute.setXYZ( 4, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 5, frustum.r, frustum.g, frustum.b ); // n4, n3 colorAttribute.setXYZ( 6, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 7, frustum.r, frustum.g, frustum.b ); // n3, n1 // far colorAttribute.setXYZ( 8, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 9, frustum.r, frustum.g, frustum.b ); // f1, f2 colorAttribute.setXYZ( 10, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 11, frustum.r, frustum.g, frustum.b ); // f2, f4 colorAttribute.setXYZ( 12, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 13, frustum.r, frustum.g, frustum.b ); // f4, f3 colorAttribute.setXYZ( 14, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 15, frustum.r, frustum.g, frustum.b ); // f3, f1 // sides colorAttribute.setXYZ( 16, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 17, frustum.r, frustum.g, frustum.b ); // n1, f1 colorAttribute.setXYZ( 18, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 19, frustum.r, frustum.g, frustum.b ); // n2, f2 colorAttribute.setXYZ( 20, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 21, frustum.r, frustum.g, frustum.b ); // n3, f3 colorAttribute.setXYZ( 22, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 23, frustum.r, frustum.g, frustum.b ); // n4, f4 // cone colorAttribute.setXYZ( 24, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 25, cone.r, cone.g, cone.b ); // p, n1 colorAttribute.setXYZ( 26, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 27, cone.r, cone.g, cone.b ); // p, n2 colorAttribute.setXYZ( 28, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 29, cone.r, cone.g, cone.b ); // p, n3 colorAttribute.setXYZ( 30, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 31, cone.r, cone.g, cone.b ); // p, n4 // up colorAttribute.setXYZ( 32, up.r, up.g, up.b ); colorAttribute.setXYZ( 33, up.r, up.g, up.b ); // u1, u2 colorAttribute.setXYZ( 34, up.r, up.g, up.b ); colorAttribute.setXYZ( 35, up.r, up.g, up.b ); // u2, u3 colorAttribute.setXYZ( 36, up.r, up.g, up.b ); colorAttribute.setXYZ( 37, up.r, up.g, up.b ); // u3, u1 // target colorAttribute.setXYZ( 38, target.r, target.g, target.b ); colorAttribute.setXYZ( 39, target.r, target.g, target.b ); // c, t colorAttribute.setXYZ( 40, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 41, cross.r, cross.g, cross.b ); // p, c // cross colorAttribute.setXYZ( 42, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 43, cross.r, cross.g, cross.b ); // cn1, cn2 colorAttribute.setXYZ( 44, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 45, cross.r, cross.g, cross.b ); // cn3, cn4 colorAttribute.setXYZ( 46, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 47, cross.r, cross.g, cross.b ); // cf1, cf2 colorAttribute.setXYZ( 48, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 49, cross.r, cross.g, cross.b ); // cf3, cf4 colorAttribute.needsUpdate = true; } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); // center / target setPoint( "c", pointMap, geometry, _camera, 0, 0, - 1 ); setPoint( "t", pointMap, geometry, _camera, 0, 0, 1 ); // near setPoint( "n1", pointMap, geometry, _camera, - w, - h, - 1 ); setPoint( "n2", pointMap, geometry, _camera, w, - h, - 1 ); setPoint( "n3", pointMap, geometry, _camera, - w, h, - 1 ); setPoint( "n4", pointMap, geometry, _camera, w, h, - 1 ); // far setPoint( "f1", pointMap, geometry, _camera, - w, - h, 1 ); setPoint( "f2", pointMap, geometry, _camera, w, - h, 1 ); setPoint( "f3", pointMap, geometry, _camera, - w, h, 1 ); setPoint( "f4", pointMap, geometry, _camera, w, h, 1 ); // up setPoint( "u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 ); setPoint( "u2", pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", pointMap, geometry, _camera, 0, h * 2, - 1 ); // cross setPoint( "cf1", pointMap, geometry, _camera, - w, 0, 1 ); setPoint( "cf2", pointMap, geometry, _camera, w, 0, 1 ); setPoint( "cf3", pointMap, geometry, _camera, 0, - h, 1 ); setPoint( "cf4", pointMap, geometry, _camera, 0, h, 1 ); setPoint( "cn1", pointMap, geometry, _camera, - w, 0, - 1 ); setPoint( "cn2", pointMap, geometry, _camera, w, 0, - 1 ); setPoint( "cn3", pointMap, geometry, _camera, 0, - h, - 1 ); setPoint( "cn4", pointMap, geometry, _camera, 0, h, - 1 ); geometry.getAttribute( "position" ).needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint( point, pointMap, geometry, camera, x, y, z ) { _vector.set( x, y, z ).unproject( camera ); const points = pointMap[ point ]; if ( points !== undefined ) { const position = geometry.getAttribute( "position" ); for ( let i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], _vector.x, _vector.y, _vector.z ); } } } const _box = /*@__PURE__*/ new Box3(); class BoxHelper extends LineSegments { constructor( object, color = 0xffff00 ) { const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); const positions = new Float32Array( 8 * 3 ); const geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.setAttribute( "position", new BufferAttribute( positions, 3 ) ); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update( object ) { if ( object !== undefined ) { console.warn( "THREE.BoxHelper: .update() has no longer arguments." ); } if ( this.object !== undefined ) { _box.setFromObject( this.object ); } if ( _box.isEmpty() ) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject( object ) { this.object = object; this.update(); return this; } copy( source, recursive ) { super.copy( source, recursive ); this.object = source.object; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class Box3Helper extends LineSegments { constructor( box, color = 0xffff00 ) { const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; const geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld( force ) { const box = this.box; if ( box.isEmpty() ) return; box.getCenter( this.position ); box.getSize( this.scale ); this.scale.multiplyScalar( 0.5 ); super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PlaneHelper extends Line { constructor( plane, size = 1, hex = 0xffff00 ) { const color = hex; const positions = [ 1, - 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ]; const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); geometry.computeBoundingSphere(); super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ]; const geometry2 = new BufferGeometry(); geometry2.setAttribute( "position", new Float32BufferAttribute( positions2, 3 ) ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) ); } updateMatrixWorld( force ) { this.position.set( 0, 0, 0 ); this.scale.set( 0.5 * this.size, 0.5 * this.size, 1 ); this.lookAt( this.plane.normal ); this.translateZ( - this.plane.constant ); super.updateMatrixWorld( force ); } dispose() { this.geometry.dispose(); this.material.dispose(); this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); } } const _axis = /*@__PURE__*/ new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) { super(); this.type = "ArrowHelper"; if ( _lineGeometry === undefined ) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); _coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 ); _coneGeometry.translate( 0, - 0.5, 0 ); } this.position.copy( origin ); this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { _axis.set( dir.z, 0, - dir.x ).normalize(); const radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( _axis, radians ); } } setLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) { this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458 this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); } setColor( color ) { this.line.material.color.set( color ); this.cone.material.color.set( color ); } copy( source ) { super.copy( source, false ); this.line.copy( source.line ); this.cone.copy( source.cone ); return this; } dispose() { this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); this.cone.material.dispose(); } } class AxesHelper extends LineSegments { constructor( size = 1 ) { const vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; const colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; const geometry = new BufferGeometry(); geometry.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.setAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); super( geometry, material ); this.type = "AxesHelper"; } setColors( xAxisColor, yAxisColor, zAxisColor ) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set( xAxisColor ); color.toArray( array, 0 ); color.toArray( array, 3 ); color.set( yAxisColor ); color.toArray( array, 6 ); color.toArray( array, 9 ); color.set( zAxisColor ); color.toArray( array, 12 ); color.toArray( array, 15 ); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo( x, y ) { this.currentPath = new Path(); this.subPaths.push( this.currentPath ); this.currentPath.moveTo( x, y ); return this; } lineTo( x, y ) { this.currentPath.lineTo( x, y ); return this; } quadraticCurveTo( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); return this; } bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); return this; } splineThru( pts ) { this.currentPath.splineThru( pts ); return this; } toShapes( isCCW ) { function toShapesNoHoles( inSubpaths ) { const shapes = []; for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { const tmpPath = inSubpaths[ i ]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { let edgeLowPt = inPolygon[ p ]; let edgeHighPt = inPolygon[ q ]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; let solid, tmpPath, tmpShape; const shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( let i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log("cw", i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { let ambiguous = false; let toChange = 0; for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { const sho = newShapeHoles[ sIdx ]; for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { const ho = sho[ hIdx ]; let hole_unassigned = true; for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange ++; if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } if ( toChange > 0 && ambiguous === false ) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for ( let i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } } if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "register", { detail: { revision: REVISION, } } ) ); } if ( typeof window !== "undefined" ) { if ( window.__THREE__ ) { console.warn( "WARNING: Multiple instances of Three.js being imported." ); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysCompare = AlwaysCompare; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationAction = AnimationAction; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasTexture = CanvasTexture; exports.CapsuleGeometry = CapsuleGeometry; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.ColorManagement = ColorManagement; exports.CompressedArrayTexture = CompressedArrayTexture; exports.CompressedCubeTexture = CompressedCubeTexture; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.Data3DTexture = Data3DTexture; exports.DataArrayTexture = DataArrayTexture; exports.DataTexture = DataTexture; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DisplayP3ColorSpace = DisplayP3ColorSpace; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EllipseCurve = EllipseCurve; exports.EqualCompare = EqualCompare; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FileLoader = FileLoader; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterCompare = GreaterCompare; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualCompare = GreaterEqualCompare; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LessCompare = LessCompare; exports.LessDepth = LessDepth; exports.LessEqualCompare = LessEqualCompare; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LineSegments = LineSegments; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearSRGBColorSpace = LinearSRGBColorSpace; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverCompare = NeverCompare; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColorSpace = NoColorSpace; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualCompare = NotEqualCompare; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.RED_GREEN_RGTC2_Format = RED_GREEN_RGTC2_Format; exports.RED_RGTC1_Format = RED_RGTC1_Format; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RenderTarget = RenderTarget; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingGeometry = RingGeometry; exports.SIGNED_RED_GREEN_RGTC2_Format = SIGNED_RED_GREEN_RGTC2_Format; exports.SIGNED_RED_RGTC1_Format = SIGNED_RED_RGTC1_Format; exports.SRGBColorSpace = SRGBColorSpace; exports.Scene = Scene; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.Source = Source; exports.Sphere = Sphere; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronGeometry = TetrahedronGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusGeometry = TorusGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeGeometry = TubeGeometry; exports.TwoPassDoubleSide = TwoPassDoubleSide; exports.UVMapping = UVMapping; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsGroup = UniformsGroup; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGL3DRenderTarget = WebGL3DRenderTarget; exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget; exports.WebGLCoordinateSystem = WebGLCoordinateSystem; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WebGPUCoordinateSystem = WebGPUCoordinateSystem; exports.WireframeGeometry = WireframeGeometry; exports.WrapAroundEnding = WrapAroundEnding; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; })); /* */}),null);
-----
three.r137_mod",[],(function $module_three_r137_mod(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "137"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.image = image; this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { 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 (this.image !== undefined) { // TODO: Move to THREE.Image const image = this.image; if (image.uuid === undefined) { image.uuid = generateUUID(); // UGH } if (!isRootObject && meta.images[image.uuid] === undefined) { let url; if (Array.isArray(image)) { // process array of images e.g. CubeTexture url = []; for (let i = 0, l = image.length; i < l; i++) { // check cube texture with data textures if (image[i].isDataTexture) { url.push(serializeImage(image[i].image)); } else { url.push(serializeImage(image[i])); } } } else { // process single image url = serializeImage(image); } meta.images[image.uuid] = { uuid: image.uuid, url: url }; } output.image = image.uuid; } if (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) this.version++; } } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); this.texture = new Texture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.image = { width: width, height: height, depth: 1 }; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } setTexture(texture) { texture.image = { width: this.width, height: this.height, depth: this.depth }; this.texture = texture; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count) { super(width, height); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class WebGLMultisampleRenderTarget extends WebGLRenderTarget { constructor(width, height, options = {}) { super(width, height, options); this.samples = 4; this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true; this.useRenderToTexture = options.useRenderToTexture !== undefined ? options.useRenderToTexture : false; this.useRenderbuffer = this.useRenderToTexture === false; } copy(source) { super.copy.call(this, source); this.samples = source.samples; this.useRenderToTexture = source.useRenderToTexture; this.useRenderbuffer = source.useRenderbuffer; return this; } } WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } toVector3(optionalResult) { if (optionalResult) { return optionalResult.set(this._x, this._y, this._z); } else { return new Vector3(this._x, this._y, this._z); } } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRoot = meta === undefined || typeof meta === "string"; if (isRoot) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRoot) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (attributes.tangent === undefined) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = attributes.tangent.array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options, dummy) { if (Number.isInteger(options)) { console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"); options = dummy; } super(size, size, options); options = options || {}; // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture = new CubeTexture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type = gl.FLOAT; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Float64Array) { console.warn("THREE.WebGLAttributes: Unsupported data buffer format: Float64Array."); } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { console.warn("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_maxMipLevel 8.0 #define cubeUV_minMipLevel 4.0 #define cubeUV_maxTileSize 256.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); float texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; if ( mipInt < cubeUV_maxMipLevel ) { uv.y += 2.0 * cubeUV_maxTileSize; } uv.y += filterInt * 2.0 * cubeUV_minTileSize; uv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize ); uv *= texelSize; return texture2D( envMap, uv ).rgb; } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1, 2 ) * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec3 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset, const in int stride ) { float texelIndex = float( vertexIndex * stride + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ).xyz; } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #ifndef USE_MORPHNORMALS if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 1 ) * morphTargetInfluences[ i ]; #else if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 2 ) * morphTargetInfluences[ i ]; #endif } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef TEXTURE_LOD_EXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; var uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data && data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; const LOD_MIN = 4; const LOD_MAX = 8; const SIZE_MAX = Math.pow(2, LOD_MAX); // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/_createPlanes(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._blurMaterial = _getBlurShader(MAX_SAMPLES); this._equirectShader = null; this._cubemapShader = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = this._allocateTargets(); this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); this._compileMaterial(this._cubemapShader); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); this._compileMaterial(this._equirectShader); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); if (this._cubemapShader !== null) this._cubemapShader.dispose(); if (this._equirectShader !== null) this._equirectShader.dispose(); for (let i = 0; i < _lodPlanes.length; i++) { _lodPlanes[i].dispose(); } } // private interface _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(texture); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets(texture) { // warning: null texture is valid const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(params); cubeUVRenderTarget.depthBuffer = texture ? false : true; if (this._pingPongRenderTarget === null) { this._pingPongRenderTarget = _createRenderTarget(params); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(_lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } _setViewport(cubeUVRenderTarget, col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); } this._cubemapShader.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); } } const material = isCubeTexture ? this._cubemapShader : this._equirectShader; const mesh = new Mesh(_lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; if (!isCubeTexture) { uniforms["texelSize"].value.set(1.0 / texture.image.width, 1.0 / texture.image.height); } _setViewport(cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < TOTAL_LODS; i++) { const sigma = Math.sqrt(_sigmas[i] * _sigmas[i] - _sigmas[i - 1] * _sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(_lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = _sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = LOD_MAX - lodIn; const outputSize = _sizeLods[lodOut]; const x = 3 * Math.max(0, SIZE_MAX - 2 * outputSize); const y = (lodOut === 0 ? 0 : 2 * SIZE_MAX) + 2 * outputSize * (lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes() { const _lodPlanes = []; const _sizeLods = []; const _sigmas = []; let lod = LOD_MAX; for (let i = 0; i < TOTAL_LODS; i++) { const sizeLod = Math.pow(2, lod); _sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > LOD_MAX - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - LOD_MAX + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } _sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); _lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { _lodPlanes, _sizeLods, _sigmas }; } function _createRenderTarget(params) { const cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(maxSamples) { const weights = new Float32Array(maxSamples); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new RawShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": maxSamples }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectShader() { const texelSize = new Vector2(1, 1); const shaderMaterial = new RawShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null }, "texelSize": { value: texelSize } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; #include void main() { gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); vec2 f = fract( uv / texelSize - 0.5 ); uv -= f * texelSize; vec3 tl = texture2D ( envMap, uv ).rgb; uv.x += texelSize.x; vec3 tr = texture2D ( envMap, uv ).rgb; uv.y += texelSize.y; vec3 br = texture2D ( envMap, uv ).rgb; uv.x -= texelSize.x; vec3 bl = texture2D ( envMap, uv ).rgb; vec3 tm = mix( tl, tr, f.x ); vec3 bm = mix( bl, br, f.x ); gl_FragColor.rgb = mix( tm, bm, f.y ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCubemapShader() { const shaderMaterial = new RawShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } class DataTexture2DArray extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture2DArray.prototype.isDataTexture2DArray = true; function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector3(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const numberOfMorphTargets = geometry.morphAttributes.position.length; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== numberOfMorphTargets) { if (entry !== undefined) entry.texture.dispose(); const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal || []; const numberOfVertices = geometry.attributes.position.count; const numberOfVertexData = hasMorphNormals === true ? 2 : 1; // (v,n) vs. (v) let width = numberOfVertices * numberOfVertexData; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * numberOfMorphTargets); const texture = new DataTexture2DArray(buffer, width, height, numberOfMorphTargets); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = numberOfVertexData * 4; for (let i = 0; i < numberOfMorphTargets; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); const stride = j * vertexDataStride; buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } } } entry = { count: numberOfMorphTargets, texture: texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update: update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose }; } class DataTexture3D extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture3D.prototype.isDataTexture3D = true; /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyTexture2dArray = new DataTexture2DArray(); const emptyTexture3d = new DataTexture3D(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || emptyTexture3d, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyTexture2dArray, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || emptyTexture3d, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyTexture2dArray, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.transparent ? "" : "#define OPAQUE", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2: isWebGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUV: !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping), lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, transparent: material.transparent, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!object.geometry && !!object.geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!object.geometry && !!object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones: maxBones, useVertexTexture: floatVertexTextures, morphTargets: !!object.geometry && !!object.geometry.morphAttributes.position, morphNormals: !!object.geometry && !!object.geometry.morphAttributes.normal, morphTargetsCount: !!object.geometry && !!object.geometry.morphAttributes.position ? object.geometry.morphAttributes.position.length : 0, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.envMapCubeUV) _programLayers.enable(7); if (parameters.lightMap) _programLayers.enable(8); if (parameters.aoMap) _programLayers.enable(9); if (parameters.emissiveMap) _programLayers.enable(10); if (parameters.bumpMap) _programLayers.enable(11); if (parameters.normalMap) _programLayers.enable(12); if (parameters.objectSpaceNormalMap) _programLayers.enable(13); if (parameters.tangentSpaceNormalMap) _programLayers.enable(14); if (parameters.clearcoat) _programLayers.enable(15); if (parameters.clearcoatMap) _programLayers.enable(16); if (parameters.clearcoatRoughnessMap) _programLayers.enable(17); if (parameters.clearcoatNormalMap) _programLayers.enable(18); if (parameters.displacementMap) _programLayers.enable(19); if (parameters.specularMap) _programLayers.enable(20); if (parameters.roughnessMap) _programLayers.enable(21); if (parameters.metalnessMap) _programLayers.enable(22); if (parameters.gradientMap) _programLayers.enable(23); if (parameters.alphaMap) _programLayers.enable(24); if (parameters.alphaTest) _programLayers.enable(25); if (parameters.vertexColors) _programLayers.enable(26); if (parameters.vertexAlphas) _programLayers.enable(27); if (parameters.vertexUvs) _programLayers.enable(28); if (parameters.vertexTangents) _programLayers.enable(29); if (parameters.uvsVertexOnly) _programLayers.enable(30); if (parameters.fog) _programLayers.enable(31); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.premultipliedAlpha) _programLayers.enable(7); if (parameters.shadowMapEnabled) _programLayers.enable(8); if (parameters.physicallyCorrectLights) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.depthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.specularIntensityMap) _programLayers.enable(14); if (parameters.specularColorMap) _programLayers.enable(15); if (parameters.transmission) _programLayers.enable(16); if (parameters.transmissionMap) _programLayers.enable(17); if (parameters.thicknessMap) _programLayers.enable(18); if (parameters.sheen) _programLayers.enable(19); if (parameters.sheenColorMap) _programLayers.enable(20); if (parameters.sheenRoughnessMap) _programLayers.enable(21); if (parameters.decodeVideoTexture) _programLayers.enable(22); if (parameters.transparent) _programLayers.enable(23); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, texImage3D: texImage3D, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const hasMultisampledRenderToTexture = extensions.has("WEBGL_multisampled_render_to_texture"); const MultisampledRenderToTextureExtension = hasMultisampledRenderToTexture ? extensions.get("WEBGL_multisampled_render_to_texture") : undefined; const _videoTextures = new WeakMap(); let _canvas; // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } info.memory.textures--; } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; _gl.deleteTexture(textureProperties.__webglTexture); properties.remove(texture); } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (!renderTarget) return; if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === undefined) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined"); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataTexture2DArray) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isDataTexture3D) textureType = _gl.TEXTURE_3D; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataTexture2DArray) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isDataTexture3D) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture && (texture.isCompressedTexture || texture.image[0].isCompressedTexture); const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || renderTarget.useRenderToTexture) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (renderTarget.useRenderbuffer) { if (isWebGL2) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (isRenderTarget3D) { // Render targets containing layers, i.e: Texture 3D and 2d arrays if (isWebGL2) { const isTexture3D = texture.isDataTexture3D; glTextureType = isTexture3D ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.useRenderbuffer) { if (isWebGL2) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } if (!renderTarget.ignoreDepthForMultisampleCopy) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } const renderTargetProperties = properties.get(renderTarget); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (renderTarget.ignoreDepthForMultisampleCopy) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } function getRenderTargetSamples(renderTarget) { return isWebGL2 && (renderTarget.useRenderbuffer || renderTarget.useRenderToTexture) ? Math.min(maxSamples, renderTarget.samples) : 0; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // backwards compatibility let warnedTexture2D = false; let warnedTextureCube = false; function safeSetTexture2D(texture, slot) { if (texture && texture.isWebGLRenderTarget) { if (warnedTexture2D === false) { console.warn("THREE.WebGLTextures.safeSetTexture2D: don"t use render targets as textures. Use their .texture property instead."); warnedTexture2D = true; } texture = texture.texture; } setTexture2D(texture, slot); } function safeSetTextureCube(texture, slot) { if (texture && texture.isWebGLCubeRenderTarget) { if (warnedTextureCube === false) { console.warn("THREE.WebGLTextures.safeSetTextureCube: don"t use cube render targets as textures. Use their .texture property instead."); warnedTextureCube = true; } texture = texture.texture; } setTextureCube(texture, slot); } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.safeSetTexture2D = safeSetTexture2D; this.safeSetTextureCube = safeSetTextureCube; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; const hasMultisampledRenderToTexture = renderer.extensions.has("WEBGL_multisampled_render_to_texture"); let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let isMultisample = false; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { isMultisample = attributes.antialias; let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); if (isMultisample) { newRenderTarget = new WebGLMultisampleRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, useRenderToTexture: hasMultisampledRenderToTexture, encoding: renderer.outputEncoding }); } else { newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, encoding: renderer.outputEncoding }); } } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; // ***Custom Modification*** // Needed to get references to GL Buffers that will be used with // compute shaders run in WebGL"s TransformFeedback _this.attributes = attributes; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color, depth, stencil) { let bits = 0; if (color === undefined || color) bits |= _gl.COLOR_BUFFER_BIT; if (depth === undefined || depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil === undefined || stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); } function renderTransmissionPass(opaqueObjects, scene, camera) { if (_transmissionRenderTarget === null) { const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; _transmissionRenderTarget = new renderTargetType(1024, 1024, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, magFilter: NearestFilter, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, useRenderToTexture: extensions.has("WEBGL_multisampled_render_to_texture") }); } const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphTargetsCount = !!geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } if (!!geometry && (geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined)) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (renderTarget.useRenderToTexture) { console.warn("render-to-texture extension was disabled because an external texture was provided"); renderTarget.useRenderToTexture = false; renderTarget.useRenderbuffer = true; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isDataTexture3D || texture.isDataTexture2DArray) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (renderTarget.useRenderbuffer) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isDataTexture3D) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataTexture2DArray) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isDataTexture3D) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, object: object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter; this.minFilter = minFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV: function (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1, hash2); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; var Materials = /*#__PURE__*/Object.freeze({ __proto__: null, ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, MeshMatcapMaterial: MeshMatcapMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray: function (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray: function (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder: function (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip: function (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive: function (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * (fps || 1.0); } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } if (typeof ReadableStream === "undefined" || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.substr(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = new Materials[json.type](); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.substr(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = await deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } let texture; const image = images[data.image]; if (Array.isArray(image)) { texture = new CubeTexture(image); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(image.data, image.width, image.height); } else { texture = new Texture(image); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext: function () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext: function (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (!matches) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (!nodeName || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera && camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera && camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object && object.isBone) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; const toChange = []; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange.push({ froms: sIdx, tos: s2Idx, hole: hIdx }); if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } // console.log("ambiguous: ", ambiguous); if (toChange.length > 0) { // console.log("to change: ", toChange); if (!ambiguous) newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add: function /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get: function /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get: function () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set: function (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, 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."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get: function () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, 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."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set: function () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set: function (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set: function (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set: function (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get: function () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get: function () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set: function /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get: function () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get: function () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.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 () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get: function () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set: function (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get: function () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set: function (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get: function () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set: function (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get: function () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get: function () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set: function (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* cullFace */ () { 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."); return undefined; }, 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."); return undefined; }, set: function () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get: function () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get: function () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get: function () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject: function /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach: function /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach: function /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
StdlibWebBloksPrimitives",["WebBloksAnimatedCancel","WebBloksAnimatedCreate","WebBloksAnimatedCreateColor","WebBloksAnimatedCreateCubicBezier","WebBloksAnimatedCreateDimension","WebBloksAnimatedGetCurrentColorValue","WebBloksAnimatedGetCurrentDimensionValue","WebBloksAnimatedGetCurrentValue","WebBloksAnimatedLoop","WebBloksAnimatedParallel","WebBloksAnimatedSequence","WebBloksAnimatedStart"],(function(a,b,c,d,e,f,g){a={};b={"bk.action.animated.Cancel":c("WebBloksAnimatedCancel"),"bk.action.animated.Create":c("WebBloksAnimatedCreate"),"bk.action.animated.CreateColor":c("WebBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("WebBloksAnimatedCreateDimension"),"bk.action.animated.easing.CreateCubicBezier":c("WebBloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("WebBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("WebBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentValue":c("WebBloksAnimatedGetCurrentValue"),"bk.action.animated.Loop":c("WebBloksAnimatedLoop"),"bk.action.animated.Parallel":c("WebBloksAnimatedParallel"),"bk.action.animated.Sequence":c("WebBloksAnimatedSequence"),"bk.action.animated.Start":c("WebBloksAnimatedStart")};d={};g.COMPONENTS=a;g.ACTIONS=b;g.EXTENSION_HANDLERS=d}),98);
-----
konva-7.2.3",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g};function i(){Object.defineProperty(g,"__esModule",{value:!0});g._registerNode=g._NODES_REGISTRY=g.Konva=g.glob=g._parseUA=void 0;var b=Math.PI/180;function c(){return typeof window!=="undefined"&&({}.toString.call(window)==="[object Window]"||{}.toString.call(window)==="[object global]")}var d=function(a){var b=a.indexOf("msie ");if(b>0)return parseInt(a.substring(b+5,a.indexOf(".",b)),10);b=a.indexOf("trident/");if(b>0){b=a.indexOf("rv:");return parseInt(a.substring(b+3,a.indexOf(".",b)),10)}b=a.indexOf("edge/");return b>0?parseInt(a.substring(b+5,a.indexOf(".",b)),10):!1},e=function(a){var b=a.toLowerCase(),c=/(chrome)[ /]([w.]+)/.exec(b)||/(webkit)[ /]([w.]+)/.exec(b)||/(opera)(?:.*version|)[ /]([w.]+)/.exec(b)||/(msie) ([w.]+)/.exec(b)||b.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([w.]+)|)/.exec(b)||[],e=!!a.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);a=!!a.match(/IEMobile/i);return{browser:c[1]||"",version:c[2]||"0",isIE:d(b),mobile:e,ieMobile:a}};g._parseUA=e;g.glob=typeof a!=="undefined"?a:typeof window!=="undefined"?window:typeof WorkerGlobalScope!=="undefined"?self:{};g.Konva={_global:g.glob,version:"7.2.3",isBrowser:c(),isUnminified:/param/.test(function(a){}.toString()),dblClickWindow:400,getAngle:function(a){return g.Konva.angleDeg?a*b:a},enableTrace:!1,_pointerEventsEnabled:!1,hitOnDragEnabled:!1,captureTouchEventsEnabled:!1,listenClickTap:!1,inDblClickWindow:!1,pixelRatio:void 0,dragDistance:3,angleDeg:!0,showWarnings:!0,dragButtons:[0,1],isDragging:function(){return g.Konva.DD.isDragging},isDragReady:function(){return!!g.Konva.DD.node},UA:g._parseUA(g.glob.navigator&&g.glob.navigator.userAgent||""),document:g.glob.document,_injectGlobal:function(a){g.glob.Konva=a},_parseUA:g._parseUA};g._NODES_REGISTRY={};e=function(a){g._NODES_REGISTRY[a.prototype.getClassName()]=a,g.Konva[a.prototype.getClassName()]=a};g._registerNode=e}var j=!1;function k(){j||(j=!0,i());return h.exports}var l={},m={exports:l};function n(){Object.defineProperty(l,"__esModule",{value:!0});l.Util=l.Transform=l.Collection=void 0;var a=k(),b=function(){function a(){}a.toCollection=function(b){var c=new a(),d=b.length,e;for(e=0;e0?Math.acos(a/f):-Math.acos(a/f);e.scaleX=f;e.scaleY=g/f;e.skewX=(a*c+b*d)/g;e.skewY=0}else if(c!=0||d!=0){f=Math.sqrt(c*c+d*d);e.rotation=Math.PI/2-(d>0?Math.acos(-c/f):-Math.acos(c/f));e.scaleX=g/f;e.scaleY=f;e.skewX=0;e.skewY=(a*c+b*d)/g}e.rotation=l.Util._getRotation(e.rotation);return e};return a}();l.Transform=b;var c="[object Array]",d="[object Number]",e="[object String]",f="[object Boolean]",g=Math.PI/180,h=180/Math.PI,i="#",j="",m="0";b="Konva warning: ";var n="Konva error: ",o="rgb(",p={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,132,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,255,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,203],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[119,128,144],slategrey:[119,128,144],snow:[255,255,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],transparent:[255,255,255,0],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,5]},q=/rgb((d{1,3}),(d{1,3}),(d{1,3}))/,r=[];l.Util={_isElement:function(a){return!!(a&&a.nodeType==1)},_isFunction:function(a){return!!(a&&a.constructor&&a.call&&a.apply)},_isPlainObject:function(a){return!!a&&a.constructor===Object},_isArray:function(a){return Object.prototype.toString.call(a)===c},_isNumber:function(a){return Object.prototype.toString.call(a)===d&&!isNaN(a)&&isFinite(a)},_isString:function(a){return Object.prototype.toString.call(a)===e},_isBoolean:function(a){return Object.prototype.toString.call(a)===f},isObject:function(a){return a instanceof Object},isValidSelector:function(a){if(typeof a!=="string")return!1;a=a[0];return a==="#"||a==="."||a===a.toUpperCase()},_sign:function(a){if(a===0)return 1;if(a>0)return 1;else return-1},requestAnimFrame:function(a){r.push(a),r.length===1&&requestAnimationFrame(function(){var a=r;r=[];a.forEach(function(a){a()})})},createCanvasElement:function(){var a=document.createElement("canvas");try{a.style=a.style||{}}catch(a){}return a},createImageElement:function(){return document.createElement("img")},_isInDocument:function(a){while(a=a.parentNode)if(a==document)return!0;return!1},_simplifyArray:function(a){var b=[],c=a.length,d=l.Util,e,f;for(e=0;e>16&255,g:a>>8&255,b:a&255}},getRandomColor:function(){var a=(Math.random()*16777215<<0).toString(16);while(a.length<6)a=m+a;return i+a},get:function(a,b){if(a===void 0)return b;else return a},getRGB:function(a){var b;if(a in p){b=p[a];return{r:b[0],g:b[1],b:b[2]}}else if(a[0]===i)return this._hexToRgb(a.substring(1));else if(a.substr(0,4)===o){b=q.exec(a.replace(/ /g,""));return{r:parseInt(b[1],10),g:parseInt(b[2],10),b:parseInt(b[3],10)}}else return{r:0,g:0,b:0}},colorToRGBA:function(a){a=a||"black";return l.Util._namedColorToRBA(a)||l.Util._hex3ColorToRGBA(a)||l.Util._hex6ColorToRGBA(a)||l.Util._rgbColorToRGBA(a)||l.Util._rgbaColorToRGBA(a)||l.Util._hslColorToRGBA(a)},_namedColorToRBA:function(a){a=p[a.toLowerCase()];return!a?null:{r:a[0],g:a[1],b:a[2],a:1}},_rgbColorToRGBA:function(a){if(a.indexOf("rgb(")===0){a=a.match(/rgb(([^)]+))/)[1];a=a.split(/ *, */).map(Number);return{r:a[0],g:a[1],b:a[2],a:1}}},_rgbaColorToRGBA:function(a){if(a.indexOf("rgba(")===0){a=a.match(/rgba(([^)]+))/)[1];a=a.split(/ *, */).map(Number);return{r:a[0],g:a[1],b:a[2],a:a[3]}}},_hex6ColorToRGBA:function(a){if(a[0]==="#"&&a.length===7)return{r:parseInt(a.slice(1,3),16),g:parseInt(a.slice(3,5),16),b:parseInt(a.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(a){if(a[0]==="#"&&a.length===4)return{r:parseInt(a[1]+a[1],16),g:parseInt(a[2]+a[2],16),b:parseInt(a[3]+a[3],16),a:1}},_hslColorToRGBA:function(a){if(/hsl((d+),s*([d.]+)%,s*([d.]+)%)/g.test(a)){a=/hsl((d+),s*([d.]+)%,s*([d.]+)%)/g.exec(a);a[0];a=a.slice(1);var b=Number(a[0])/360,c=Number(a[1])/100;a=Number(a[2])/100;var d,e,f=void 0;if(c===0){f=a*255;return{r:Math.round(f),g:Math.round(f),b:Math.round(f),a:1}}a<.5?d=a*(1+c):d=a+c-a*c;c=2*a-d;a=[0,0,0];for(var g=0;g<3;g++)e=b+1/3*-(g-1),e<0&&e++,e>1&&e--,6*e<1?f=c+(d-c)*6*e:2*e<1?f=d:3*e<2?f=c+(d-c)*(2/3-e)*6:f=c,a[g]=f*255;return{r:Math.round(a[0]),g:Math.round(a[1]),b:Math.round(a[2]),a:1}}},haveIntersection:function(a,b){return!(b.x>a.x+a.width||b.x+b.widtha.y+a.height||b.y+b.height1?(g=c,h=d,i=(c-e)*(c-e)+(d-f)*(d-f)):(g=a+j*(c-a),h=b+j*(d-b),i=(g-e)*(g-e)+(h-f)*(h-f))}return[g,h,i]},_getProjectionToLine:function(a,b,c){var d=l.Util.cloneObject(a),e=Number.MAX_VALUE;b.forEach(function(f,g){if(!c&&g===b.length-1)return;g=b[(g+1)%b.length];f=l.Util._getProjectionToSegment(f.x,f.y,g.x,g.y,a.x,a.y);g=f[0];var h=f[1];f=f[2];fb.length){var f=b;b=a;a=f}for(f=0;f255)return 255;else if(a<0)return 0;return Math.round(a)}q.RGBComponent=d;function e(a){if(a>1)return 1;else if(a<1e-4)return 1e-4;return a}q.alphaComponent=e;function f(){if(a.Konva.isUnminified)return function(a,d){b.Util._isNumber(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a number.");return a}}q.getNumberValidator=f;function g(d){if(a.Konva.isUnminified)return function(a,e){var f=b.Util._isNumber(a),g=b.Util._isArray(a)&&a.length==d;!f&&!g&&b.Util.warn(c(a)+" is a not valid value for ""+e+"" attribute. The value should be a number or Array("+d+")");return a}}q.getNumberOrArrayOfNumbersValidator=g;function h(){if(a.Konva.isUnminified)return function(a,d){var e=b.Util._isNumber(a),f=a==="auto";e||f||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a number or "auto".");return a}}q.getNumberOrAutoValidator=h;function i(){if(a.Konva.isUnminified)return function(a,d){b.Util._isString(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a string.");return a}}q.getStringValidator=i;function j(){if(a.Konva.isUnminified)return function(a,d){var e=b.Util._isString(a),f=Object.prototype.toString.call(a)==="[object CanvasGradient]";e||f||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a string or a native gradient.");return a}}q.getStringOrGradientValidator=j;function l(){if(a.Konva.isUnminified)return function(a,d){b.Util._isFunction(a)||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a function.");return a}}q.getFunctionValidator=l;function m(){if(a.Konva.isUnminified)return function(a,d){!b.Util._isArray(a)?b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a array of numbers."):a.forEach(function(a){b.Util._isNumber(a)||b.Util.warn("""+d+"" attribute has non numeric element "+a+". Make sure that all elements are numbers.")});return a}}q.getNumberArrayValidator=m;function n(){if(a.Konva.isUnminified)return function(a,d){var e=a===!0||a===!1;e||b.Util.warn(c(a)+" is a not valid value for ""+d+"" attribute. The value should be a boolean.");return a}}q.getBooleanValidator=n;function o(d){if(a.Konva.isUnminified)return function(a,e){b.Util.isObject(a)||b.Util.warn(c(a)+" is a not valid value for ""+e+"" attribute. The value should be an object with properties "+d);return a}}q.getComponentValidator=o}var t=!1;function u(){t||(t=!0,s());return r.exports}var v={},w={exports:v};function x(){Object.defineProperty(v,"__esModule",{value:!0});v.Factory=void 0;var a=p(),b=u(),c="get",d="set";v.Factory={addGetterSetter:function(a,b,c,d,e){v.Factory.addGetter(a,b,c),v.Factory.addSetter(a,b,d,e),v.Factory.addOverloadedGetterSetter(a,b)},addGetter:function(b,d,e){var f=c+a.Util._capitalize(d);b.prototype[f]=b.prototype[f]||function(){var a=this.attrs[d];return a===void 0?e:a}},addSetter:function(b,c,e,f){var g=d+a.Util._capitalize(c);b.prototype[g]||v.Factory.overWriteSetter(b,c,e,f)},overWriteSetter:function(b,c,e,f){var g=d+a.Util._capitalize(c);b.prototype[g]=function(a){e&&a!==void 0&&a!==null&&(a=e.call(this,a,c));this._setAttr(c,a);f&&f.call(this);return this}},addComponentsGetterSetter:function(e,f,g,h,i){var j=g.length,k=a.Util._capitalize,l=c+k(f),m=d+k(f),n;e.prototype[l]=function(){var a={};for(l=0;l=o&&b.shift()};a.prototype.reset=function(){var a=this.getCanvas().getPixelRatio();this.setTransform(1*a,0,0,1*a,0,0)};a.prototype.getCanvas=function(){return this.canvas};a.prototype.clear=function(a){var b=this.getCanvas();a?this.clearRect(a.x||0,a.y||0,a.width||0,a.height||0):this.clearRect(0,0,b.getWidth()/b.pixelRatio,b.getHeight()/b.pixelRatio)};a.prototype._applyLineCap=function(a){a=a.getLineCap();a&&this.setAttr("lineCap",a)};a.prototype._applyOpacity=function(a){a=a.getAbsoluteOpacity();a!==1&&this.setAttr("globalAlpha",a)};a.prototype._applyLineJoin=function(a){a=a.attrs.lineJoin;a&&this.setAttr("lineJoin",a)};a.prototype.setAttr=function(a,b){this._context[a]=b};a.prototype.arc=function(a,b,c,d,e,f){this._context.arc(a,b,c,d,e,f)};a.prototype.arcTo=function(a,b,c,d,e){this._context.arcTo(a,b,c,d,e)};a.prototype.beginPath=function(){this._context.beginPath()};a.prototype.bezierCurveTo=function(a,b,c,d,e,f){this._context.bezierCurveTo(a,b,c,d,e,f)};a.prototype.clearRect=function(a,b,c,d){this._context.clearRect(a,b,c,d)};a.prototype.clip=function(){this._context.clip()};a.prototype.closePath=function(){this._context.closePath()};a.prototype.createImageData=function(a,b){var c=arguments;if(c.length===2)return this._context.createImageData(a,b);else if(c.length===1)return this._context.createImageData(a)};a.prototype.createLinearGradient=function(a,b,c,d){return this._context.createLinearGradient(a,b,c,d)};a.prototype.createPattern=function(a,b){return this._context.createPattern(a,b)};a.prototype.createRadialGradient=function(a,b,c,d,e,f){return this._context.createRadialGradient(a,b,c,d,e,f)};a.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){var j=arguments,k=this._context;j.length===3?k.drawImage(a,b,c):j.length===5?k.drawImage(a,b,c,d,e):j.length===9&&k.drawImage(a,b,c,d,e,f,g,h,i)};a.prototype.ellipse=function(a,b,c,d,e,f,g,h){this._context.ellipse(a,b,c,d,e,f,g,h)};a.prototype.isPointInPath=function(a,b){return this._context.isPointInPath(a,b)};a.prototype.fill=function(){this._context.fill()};a.prototype.fillRect=function(a,b,c,d){this._context.fillRect(a,b,c,d)};a.prototype.strokeRect=function(a,b,c,d){this._context.strokeRect(a,b,c,d)};a.prototype.fillText=function(a,b,c){this._context.fillText(a,b,c)};a.prototype.measureText=function(a){return this._context.measureText(a)};a.prototype.getImageData=function(a,b,c,d){return this._context.getImageData(a,b,c,d)};a.prototype.lineTo=function(a,b){this._context.lineTo(a,b)};a.prototype.moveTo=function(a,b){this._context.moveTo(a,b)};a.prototype.rect=function(a,b,c,d){this._context.rect(a,b,c,d)};a.prototype.putImageData=function(a,b,c){this._context.putImageData(a,b,c)};a.prototype.quadraticCurveTo=function(a,b,c,d){this._context.quadraticCurveTo(a,b,c,d)};a.prototype.restore=function(){this._context.restore()};a.prototype.rotate=function(a){this._context.rotate(a)};a.prototype.save=function(){this._context.save()};a.prototype.scale=function(a,b){this._context.scale(a,b)};a.prototype.setLineDash=function(a){this._context.setLineDash?this._context.setLineDash(a):"mozDash"in this._context?this._context.mozDash=a:"webkitLineDash"in this._context&&(this._context.webkitLineDash=a)};a.prototype.getLineDash=function(){return this._context.getLineDash()};a.prototype.setTransform=function(a,b,c,d,e,f){this._context.setTransform(a,b,c,d,e,f)};a.prototype.stroke=function(){this._context.stroke()};a.prototype.strokeText=function(a,b,c,d){this._context.strokeText(a,b,c,d)};a.prototype.transform=function(a,b,c,d,e,f){this._context.transform(a,b,c,d,e,f)};a.prototype.translate=function(a,b){this._context.translate(a,b)};a.prototype._enableTrace=function(){var a=this,c=m.length,d=b.Util._simplifyArray,e=this.setAttr,f,g,h=function(b){var c=a[b],e;a[b]=function(){g=d(Array.prototype.slice.call(arguments,0));e=c.apply(a,arguments);a._trace({method:b,args:g});return e}};for(f=0;f0&&d[0].getDepth()<=a&&h(d)}b.nodeType!==D&&h(b.getStage().getChildren());return c};b.prototype.getDepth=function(){var a=0,b=this.parent;while(b)a++,b=b.parent;return a};b.prototype._batchTransformChanges=function(a){this._batchingTransformChange=!0,a(),this._batchingTransformChange=!1,this._needClearTransformCache&&(this._clearCache(C),this._clearSelfAndDescendantCache(l,!0)),this._needClearTransformCache=!1};b.prototype.setPosition=function(a){var b=this;this._batchTransformChanges(function(){b.x(a.x),b.y(a.y)});return this};b.prototype.getPosition=function(){return{x:this.x(),y:this.y()}};b.prototype.getAbsolutePosition=function(b){var c=!1,d=this.parent;while(d){if(d.isCached()){c=!0;break}d=d.parent}c&&!b&&(b=!0);d=this.getAbsoluteTransform(b).getMatrix();c=new a.Transform();b=this.offset();c.m=d.slice();c.translate(b.x,b.y);return c.getTranslation()};b.prototype.setAbsolutePosition=function(a){var b=this._clearTransform();this.attrs.x=b.x;this.attrs.y=b.y;delete b.x;delete b.y;this._clearCache(C);var c=this._getAbsoluteTransform().copy();c.invert();c.translate(a.x,a.y);a={x:this.attrs.x+c.getTranslation().x,y:this.attrs.y+c.getTranslation().y};this._setTransform(b);this.setPosition({x:a.x,y:a.y});this._clearCache(C);this._clearSelfAndDescendantCache(l);return this};b.prototype._setTransform=function(a){var b;for(b in a)this.attrs[b]=a[b]};b.prototype._clearTransform=function(){var a={x:this.x(),y:this.y(),rotation:this.rotation(),scaleX:this.scaleX(),scaleY:this.scaleY(),offsetX:this.offsetX(),offsetY:this.offsetY(),skewX:this.skewX(),skewY:this.skewY()};this.attrs.x=0;this.attrs.y=0;this.attrs.rotation=0;this.attrs.scaleX=1;this.attrs.scaleY=1;this.attrs.offsetX=0;this.attrs.offsetY=0;this.attrs.skewX=0;this.attrs.skewY=0;return a};b.prototype.move=function(a){var b=a.x;a=a.y;var c=this.x(),d=this.y();b!==void 0&&(c+=b);a!==void 0&&(d+=a);this.setPosition({x:c,y:d});return this};b.prototype._eachAncestorReverse=function(a,b){var c=[],d=this.getParent();if(b&&b._id===this._id)return;c.unshift(this);while(d&&(!b||d._id!==b._id))c.unshift(d),d=d.parent;d=c.length;for(b=0;b0){this.parent.children.splice(b,1);this.parent.children.splice(b-1,0,this);this.parent._setChildrenIndices();return!0}return!1};b.prototype.moveToBottom=function(){if(!this.parent){a.Util.warn("Node has no parent. moveToBottom function is ignored.");return!1}var b=this.index;if(b>0){this.parent.children.splice(b,1);this.parent.children.unshift(this);this.parent._setChildrenIndices();return!0}return!1};b.prototype.setZIndex=function(b){if(!this.parent){a.Util.warn("Node has no parent. zIndex parameter is ignored.");return this}(b<0||b>=this.parent.children.length)&&a.Util.warn("Unexpected value "+b+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var c=this.index;this.parent.children.splice(c,1);this.parent.children.splice(b,0,this);this.parent._setChildrenIndices();return this};b.prototype.getAbsoluteOpacity=function(){return this._getCache(i,this._getAbsoluteOpacity)};b.prototype._getAbsoluteOpacity=function(){var a=this.opacity(),b=this.getParent();b&&!b._isUnderCache&&(a*=b.getAbsoluteOpacity());return a};b.prototype.moveTo=function(a){this.getParent()!==a&&(this._remove(),a.add(this));return this};b.prototype.toObject=function(){var b={},c=this.getAttrs(),d,e,f;b.attrs={};for(d in c){e=c[d];f=a.Util.isObject(e)&&!a.Util._isPlainObject(e)&&!a.Util._isArray(e);if(f)continue;f=typeof this[d]==="function"&&this[d];delete c[d];f=f?f.call(this):null;c[d]=e;f!==e&&(b.attrs[d]=e)}b.className=this.getClassName();return a.Util._prepareToStringify(b)};b.prototype.toJSON=function(){return JSON.stringify(this.toObject())};b.prototype.getParent=function(){return this.parent};b.prototype.findAncestors=function(a,b,c){var d=[];b&&this._isMatch(a)&&d.push(this);b=this.parent;while(b){if(b===c)return d;b._isMatch(a)&&d.push(b);b=b.parent}return d};b.prototype.isAncestorOf=function(a){return!1};b.prototype.findAncestor=function(a,b,c){return this.findAncestors(a,b,c)[0]};b.prototype._isMatch=function(b){if(!b)return!1;if(typeof b==="function")return b(this);b=b.replace(/ /g,"").split(",");var c=b.length,d,e;for(d=0;d=0;if(!c)return;if(this.isDragging())return;c=!1;e.DD._dragElements.forEach(function(a){b.isAncestorOf(a.node)&&(c=!0)});c||this._createDragElement(a)})};b.prototype._dragChange=function(){if(this.attrs.draggable)this._listenDrag();else{this._dragCleanup();var a=this.getStage();if(!a)return;a=e.DD._dragElements.get(this._id);var b=a&&a.dragStatus==="dragging";a=a&&a.dragStatus==="ready";b?this.stopDrag():a&&e.DD._dragElements["delete"](this._id)}};b.prototype._dragCleanup=function(){this.off("mousedown.konva"),this.off("touchstart.konva")};b.create=function(b,c){a.Util._isString(b)&&(b=JSON.parse(b));return this._createNode(b,c)};b._createNode=function(c,e){var f=b.prototype.getClassName.call(c),g=c.children;e&&(c.attrs.container=e);d._NODES_REGISTRY[f]||(a.Util.warn("Can not find a node with class name ""+f+"". Fallback to "Shape"."),f="Shape");e=d._NODES_REGISTRY[f];f=new e(c.attrs);if(g){e=g.length;for(c=0;c0};c.prototype.removeChildren=function(){var a;for(var c=0;c1){for(var c=0;c0?a[0]:void 0};c.prototype._generalFind=function(a,c){var d=[];this._descendants(function(b){var e=b._isMatch(a);e&&d.push(b);return e&&c?!0:!1});return b.Collection.toCollection(d)};c.prototype._descendants=function(a){var b;for(var c=0;c-1&&V.stages.splice(a,1);return this};h.prototype.getPointerPosition=function(){var a=this._pointerPositions[0]||this._changedPointerPositions[0];if(!a){b.Util.warn(pa);return null}return{x:a.x,y:a.y}};h.prototype._getPointerById=function(a){return this._pointerPositions.find(function(b){return b.id===a})};h.prototype.getPointersPositions=function(){return this._pointerPositions};h.prototype.getStage=function(){return this};h.prototype.getContent=function(){return this.content};h.prototype._toKonvaCanvas=function(a){a=a||{};a.x=a.x||0;a.y=a.y||0;a.width=a.width||this.width();a.height=a.height||this.height();var b=new f.SceneCanvas({width:a.width,height:a.height,pixelRatio:a.pixelRatio||1}),c=b.getContext()._context,d=this.children;(a.x||a.y)&&c.translate(-1*a.x,-1*a.y);d.each(function(b){if(!b.isVisible())return;b=b._toKonvaCanvas(a);c.drawImage(b._canvas,a.x,a.y,b.getWidth()/b.getPixelRatio(),b.getHeight()/b.getPixelRatio())});return b};h.prototype.getIntersection=function(a,b){if(!a)return null;var c=this.children,d=c.length;d=d-1;var e;for(d=d;d>=0;d--){e=c[d].getIntersection(a,b);if(e)return e}return null};h.prototype._resizeDOM=function(){var a=this.width(),b=this.height();this.content&&(this.content.style.width=a+m,this.content.style.height=b+m);this.bufferCanvas.setSize(a,b);this.bufferHitCanvas.setSize(a,b);this.children.each(function(c){c.setSize({width:a,height:b}),c.draw()})};h.prototype.add=function(a){if(arguments.length>1){for(var d=0;dla&&b.Util.warn("The stage has "+f+" layers. Recommended maximum number of layers is 3-5. Adding more layers into the stage may drop the performance. Rethink your tree structure, you can use Konva.Group.");a.setSize({width:this.width(),height:this.height()});a.draw();e.Konva.isBrowser&&this.content.appendChild(a.canvas._canvas);return this};h.prototype.getParent=function(){return null};h.prototype.getLayer=function(){return null};h.prototype.hasPointerCapture=function(a){return i.hasPointerCapture(a,this)};h.prototype.setPointerCapture=function(a){i.setPointerCapture(a,this)};h.prototype.releaseCapture=function(a){i.releaseCapture(a,this)};h.prototype.getLayers=function(){return this.getChildren()};h.prototype._bindContentEvents=function(){if(!e.Konva.isBrowser)return;for(var a=0;a0};c.prototype.destroy=function(){d.Node.prototype.destroy.call(this);delete W.shapes[this.colorKey];delete this.colorKey;return this};c.prototype._useBufferCanvas=function(a){var b;if(!this.getStage())return!1;b=(b=this.attrs.perfectDrawEnabled)!==null&&b!==void 0?b:!0;if(!b)return!1;b=a||this.hasFill();a=this.hasStroke();var c=this.getAbsoluteOpacity()!==1;if(b&&a&&c)return!0;c=this.hasShadow();var d=this.shadowForStrokeEnabled();return b&&a&&c&&d?!0:!1};c.prototype.setStrokeHitEnabled=function(a){b.Util.warn("strokeHitEnabled property is deprecated. Please use hitStrokeWidth instead."),a?this.hitStrokeWidth("auto"):this.hitStrokeWidth(0)};c.prototype.getStrokeHitEnabled=function(){if(this.hitStrokeWidth()===0)return!1;else return!0};c.prototype.getSelfRect=function(){var a=this.size();return{x:this._centroid?-a.width/2:0,y:this._centroid?-a.height/2:0,width:a.width,height:a.height}};c.prototype.getClientRect=function(a){a===void 0&&(a={});var b=a.skipTransform,c=a.relativeTo,d=this.getSelfRect(),e=!a.skipStroke&&this.hasStroke();e=e&&this.strokeWidth()||0;var f=d.width+e,g=d.height+e;a=!a.skipShadow&&this.hasShadow();var h=a?this.shadowOffsetX():0,i=a?this.shadowOffsetY():0;f=f+Math.abs(h);g=g+Math.abs(i);a=a&&this.shadowBlur()||0;f=f+a*2;g=g+a*2;var j=0;Math.round(e/2)!==e/2&&(j=1);f={width:f+j,height:g+j,x:-Math.round(e/2+a)+Math.min(h,0)+d.x,y:-Math.round(e/2+a)+Math.min(i,0)+d.y};return!b?this._transformedRect(f,c):f};c.prototype.drawScene=function(a,b){var c=this.getLayer();a=a||c.getCanvas();c=a.getContext();var d=this._getCanvasCache(),e=this.getSceneFunc(),f=this.hasShadow(),g=a.isCache;a=a.isCache;var h=b===this;if(!this.isVisible()&&!g)return this;if(d){c.save();g=this.getAbsoluteTransform(b).getMatrix();c.transform(g[0],g[1],g[2],g[3],g[4],g[5]);this._drawCachedSceneCanvas(c);c.restore();return this}if(!e)return this;c.save();if(this._useBufferCanvas()&&!a){d=this.getStage();g=d.bufferCanvas;a=g.getContext();a.clear();a.save();a._applyLineJoin(this);d=this.getAbsoluteTransform(b).getMatrix();a.transform(d[0],d[1],d[2],d[3],d[4],d[5]);e.call(this,a,this);a.restore();a=g.pixelRatio;f&&c._applyShadow(this);c._applyOpacity(this);c._applyGlobalCompositeOperation(this);c.drawImage(g._canvas,0,0,g.width/a,g.height/a)}else{c._applyLineJoin(this);if(!h){d=this.getAbsoluteTransform(b).getMatrix();c.transform(d[0],d[1],d[2],d[3],d[4],d[5]);c._applyOpacity(this);c._applyGlobalCompositeOperation(this)}f&&c._applyShadow(this);e.call(this,c,this)}c.restore();return this};c.prototype.drawHit=function(a,c,d){d===void 0&&(d=!1);if(!this.shouldDrawHit(c,d))return this;d=this.getLayer();a=a||d.hitCanvas;d=a&&a.getContext();a=this.hitFunc()||this.sceneFunc();var e=this._getCanvasCache();e=e&&e.hit;this.colorKey||b.Util.warn("Looks like your canvas has a destroyed shape in it. Do not reuse shape after you destroyed it. See the shape in logs above. If you want to reuse shape you should call remove() instead of destroy()");if(e){d.save();e=this.getAbsoluteTransform(c).getMatrix();d.transform(e[0],e[1],e[2],e[3],e[4],e[5]);this._drawCachedHitCanvas(d);d.restore();return this}if(!a)return this;d.save();d._applyLineJoin(this);e=this===c;if(!e){e=this.getAbsoluteTransform(c).getMatrix();d.transform(e[0],e[1],e[2],e[3],e[4],e[5])}a.call(this,d,this);d.restore();return this};c.prototype.drawHitFromCache=function(a){a===void 0&&(a=0);var c=this._getCanvasCache(),d=this._getCachedSceneCanvas();c=c.hit;var e=c.getContext(),f=c.getWidth();c=c.getHeight();var g,h,i;e.clear();e.drawImage(d._canvas,0,0,f,c);try{d=e.getImageData(0,0,f,c);f=d.data;c=f.length;g=b.Util._hexToRgb(this.colorKey);for(h=0;ha?(f[h]=g.r,f[h+1]=g.g,f[h+2]=g.b,f[h+3]=255):f[h+3]=0;e.putImageData(d,0,0)}catch(a){b.Util.error("Unable to draw hit graph from cached scene canvas. "+a.message)}return this};c.prototype.hasPointerCapture=function(a){return g.hasPointerCapture(a,this)};c.prototype.setPointerCapture=function(a){g.setPointerCapture(a,this)};c.prototype.releaseCapture=function(a){g.releaseCapture(a,this)};return c}(d.Node);W.Shape=B;B.prototype._fillFunc=q;B.prototype._strokeFunc=r;B.prototype._fillFuncHit=s;B.prototype._strokeFuncHit=t;B.prototype._centroid=!1;B.prototype.nodeType="Shape";f._registerNode(B);B.prototype.eventListeners={};B.prototype.on.call(B.prototype,"shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva",v);B.prototype.on.call(B.prototype,"shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva",w);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva",x);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva",y);B.prototype.on.call(B.prototype,"fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva",A);c.Factory.addGetterSetter(B,"stroke",void 0,e.getStringOrGradientValidator());c.Factory.addGetterSetter(B,"strokeWidth",2,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillAfterStrokeEnabled",!1);c.Factory.addGetterSetter(B,"hitStrokeWidth","auto",e.getNumberOrAutoValidator());c.Factory.addGetterSetter(B,"strokeHitEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"perfectDrawEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"shadowForStrokeEnabled",!0,e.getBooleanValidator());c.Factory.addGetterSetter(B,"lineJoin");c.Factory.addGetterSetter(B,"lineCap");c.Factory.addGetterSetter(B,"sceneFunc");c.Factory.addGetterSetter(B,"hitFunc");c.Factory.addGetterSetter(B,"dash");c.Factory.addGetterSetter(B,"dashOffset",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowColor",void 0,e.getStringValidator());c.Factory.addGetterSetter(B,"shadowBlur",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowOpacity",1,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"shadowOffset",["x","y"]);c.Factory.addGetterSetter(B,"shadowOffsetX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"shadowOffsetY",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternImage");c.Factory.addGetterSetter(B,"fill",void 0,e.getStringOrGradientValidator());c.Factory.addGetterSetter(B,"fillPatternX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternY",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillLinearGradientColorStops");c.Factory.addGetterSetter(B,"strokeLinearGradientColorStops");c.Factory.addGetterSetter(B,"fillRadialGradientStartRadius",0);c.Factory.addGetterSetter(B,"fillRadialGradientEndRadius",0);c.Factory.addGetterSetter(B,"fillRadialGradientColorStops");c.Factory.addGetterSetter(B,"fillPatternRepeat","repeat");c.Factory.addGetterSetter(B,"fillEnabled",!0);c.Factory.addGetterSetter(B,"strokeEnabled",!0);c.Factory.addGetterSetter(B,"shadowEnabled",!0);c.Factory.addGetterSetter(B,"dashEnabled",!0);c.Factory.addGetterSetter(B,"strokeScaleEnabled",!0);c.Factory.addGetterSetter(B,"fillPriority","color");c.Factory.addComponentsGetterSetter(B,"fillPatternOffset",["x","y"]);c.Factory.addGetterSetter(B,"fillPatternOffsetX",0,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternOffsetY",0,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"fillPatternScale",["x","y"]);c.Factory.addGetterSetter(B,"fillPatternScaleX",1,e.getNumberValidator());c.Factory.addGetterSetter(B,"fillPatternScaleY",1,e.getNumberValidator());c.Factory.addComponentsGetterSetter(B,"fillLinearGradientStartPoint",["x","y"]);c.Factory.addComponentsGetterSetter(B,"strokeLinearGradientStartPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillLinearGradientStartPointX",0);c.Factory.addGetterSetter(B,"strokeLinearGradientStartPointX",0);c.Factory.addGetterSetter(B,"fillLinearGradientStartPointY",0);c.Factory.addGetterSetter(B,"strokeLinearGradientStartPointY",0);c.Factory.addComponentsGetterSetter(B,"fillLinearGradientEndPoint",["x","y"]);c.Factory.addComponentsGetterSetter(B,"strokeLinearGradientEndPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillLinearGradientEndPointX",0);c.Factory.addGetterSetter(B,"strokeLinearGradientEndPointX",0);c.Factory.addGetterSetter(B,"fillLinearGradientEndPointY",0);c.Factory.addGetterSetter(B,"strokeLinearGradientEndPointY",0);c.Factory.addComponentsGetterSetter(B,"fillRadialGradientStartPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillRadialGradientStartPointX",0);c.Factory.addGetterSetter(B,"fillRadialGradientStartPointY",0);c.Factory.addComponentsGetterSetter(B,"fillRadialGradientEndPoint",["x","y"]);c.Factory.addGetterSetter(B,"fillRadialGradientEndPointX",0);c.Factory.addGetterSetter(B,"fillRadialGradientEndPointY",0);c.Factory.addGetterSetter(B,"fillPatternRotation",0);c.Factory.backCompat(B,{dashArray:"dash",getDashArray:"getDash",setDashArray:"getDash",drawFunc:"sceneFunc",getDrawFunc:"getSceneFunc",setDrawFunc:"setSceneFunc",drawHitFunc:"hitFunc",getDrawHitFunc:"getHitFunc",setDrawHitFunc:"setHitFunc"});b.Collection.mapMethods(B)}var pa=!1;function X(){pa||(pa=!0,oa());return na.exports}var qa={},ra={exports:qa};function sa(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(qa,"__esModule",{value:!0});qa.Layer=void 0;var b=p(),c=ea(),d=R(),e=z(),f=J(),g=u(),h=X(),i=k(),j="#",l="beforeDraw",m="draw",n=[{x:0,y:0},{x:-1,y:-1},{x:1,y:-1},{x:1,y:1},{x:-1,y:1}],o=n.length,q=function(g){a(e,g);function e(a){a=g.call(this,a)||this;a.canvas=new f.SceneCanvas();a.hitCanvas=new f.HitCanvas({pixelRatio:1});a._waitingForDraw=!1;a.on("visibleChange.konva",a._checkVisibility);a._checkVisibility();a.on("imageSmoothingEnabledChange.konva",a._setSmoothEnabled);a._setSmoothEnabled();return a}e.prototype.createPNGStream=function(){var a=this.canvas._canvas;return a.createPNGStream()};e.prototype.getCanvas=function(){return this.canvas};e.prototype.getHitCanvas=function(){return this.hitCanvas};e.prototype.getContext=function(){return this.getCanvas().getContext()};e.prototype.clear=function(a){this.getContext().clear(a);this.getHitCanvas().getContext().clear(a);return this};e.prototype.setZIndex=function(a){g.prototype.setZIndex.call(this,a);var b=this.getStage();b&&(b.content.removeChild(this.getCanvas()._canvas),a0)return{antialiased:!0};return{}};e.prototype.drawScene=function(a,b){var d=this.getLayer();a=a||d&&d.getCanvas();this._fire(l,{node:this});this.clearBeforeDraw()&&a.getContext().clear();c.Container.prototype.drawScene.call(this,a,b);this._fire(m,{node:this});return this};e.prototype.drawHit=function(a,b){var d=this.getLayer();a=a||d&&d.hitCanvas;d&&d.clearBeforeDraw()&&d.getHitCanvas().getContext().clear();c.Container.prototype.drawHit.call(this,a,b);return this};e.prototype.enableHitGraph=function(){this.hitGraphEnabled(!0);return this};e.prototype.disableHitGraph=function(){this.hitGraphEnabled(!1);return this};e.prototype.setHitGraphEnabled=function(a){b.Util.warn("hitGraphEnabled method is deprecated. Please use layer.listening() instead."),this.listening(a)};e.prototype.getHitGraphEnabled=function(a){b.Util.warn("hitGraphEnabled method is deprecated. Please use layer.listening() instead.");return this.listening()};e.prototype.toggleHitCanvas=function(){if(!this.parent)return;var a=this.parent,b=!!this.hitCanvas._canvas.parentNode;b?a.content.removeChild(this.hitCanvas._canvas):a.content.appendChild(this.hitCanvas._canvas)};return e}(c.Container);qa.Layer=q;q.prototype.nodeType="Layer";i._registerNode(q);e.Factory.addGetterSetter(q,"imageSmoothingEnabled",!0);e.Factory.addGetterSetter(q,"clearBeforeDraw",!0);e.Factory.addGetterSetter(q,"hitGraphEnabled",!0,g.getBooleanValidator());b.Collection.mapMethods(q)}var ta=!1;function ua(){ta||(ta=!0,sa());return ra.exports}var va={},wa={exports:va};function xa(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(va,"__esModule",{value:!0});va.FastLayer=void 0;var b=p(),c=ua(),d=k();c=function(c){a(d,c);function d(a){a=c.call(this,a)||this;a.listening(!1);b.Util.warn("Konva.Fast layer is deprecated. Please use "new Konva.Layer({ listening: false })" instead.");return a}return d}(c.Layer);va.FastLayer=c;c.prototype.nodeType="FastLayer";d._registerNode(c);b.Collection.mapMethods(c)}var ya=!1;function za(){ya||(ya=!0,xa());return wa.exports}var Aa={},Ba={exports:Aa};function Ca(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Aa,"__esModule",{value:!0});Aa.Group=void 0;var b=p(),c=ea(),d=k();c=function(c){a(d,c);function d(){return c!==null&&c.apply(this,arguments)||this}d.prototype._validateAdd=function(a){a=a.getType();a!=="Group"&&a!=="Shape"&&b.Util["throw"]("You may only add groups and shapes to groups.")};return d}(c.Container);Aa.Group=c;c.prototype.nodeType="Group";d._registerNode(c);b.Collection.mapMethods(c)}var Da=!1;function Ea(){Da||(Da=!0,Ca());return Ba.exports}var Fa={},Ga={exports:Fa};function Ha(){Object.defineProperty(Fa,"__esModule",{value:!0});Fa.Animation=void 0;var a=k(),b=function(){return a.glob.performance&&a.glob.performance.now?function(){return a.glob.performance.now()}:function(){return new Date().getTime()}}(),c=function(){function a(c,d){this.id=a.animIdCounter++,this.frame={time:0,timeDiff:0,lastTime:b(),frameRate:0},this.func=c,this.setLayers(d)}a.prototype.setLayers=function(a){var b;!a?b=[]:a.length>0?b=a:b=[a];this.layers=b;return this};a.prototype.getLayers=function(){return this.layers};a.prototype.addLayer=function(a){var b=this.layers,c=b.length,d;for(d=0;dthis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():a<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=a,this.update())};a.prototype.getTime=function(){return this._time};a.prototype.setPosition=function(a){this.prevPos=this._pos,this.propFunc(a),this._pos=a};a.prototype.getPosition=function(a){a===void 0&&(a=this._time);return this.func(a,this.begin,this._change,this.duration)};a.prototype.play=function(){this.state=g,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")};a.prototype.reverse=function(){this.state=h,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")};a.prototype.seek=function(a){this.pause(),this._time=a,this.update(),this.fire("onSeek")};a.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")};a.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")};a.prototype.update=function(){this.setPosition(this.getPosition(this._time)),this.fire("onUpdate")};a.prototype.onEnterFrame=function(){var a=this.getTimer()-this._startTime;this.state===g?this.setTime(a):this.state===h&&this.setTime(this.duration-a)};a.prototype.pause=function(){this.state=f,this.fire("onPause")};a.prototype.getTimer=function(){return new Date().getTime()};return a}(),m=function(){function c(f){var g=this,h=f.node,j=h._id,k,m=f.easing||Y.Easings.Linear,n=!!f.yoyo,o;typeof f.duration==="undefined"?k=.3:f.duration===0?k=.001:k=f.duration;this.node=h;this._id=i++;h=h.getLayer()||(h instanceof d.Konva.Stage?h.getLayers():null);h||a.Util.error("Tween constructor have `node` that is not in a layer. Please add node into layer first.");this.anim=new b.Animation(function(){g.tween.onEnterFrame()},h);this.tween=new l(o,function(a){g._tweenFunc(a)},m,0,1,k*1e3,n);this._addListeners();c.attrs[j]||(c.attrs[j]={});c.attrs[j][this._id]||(c.attrs[j][this._id]={});c.tweens[j]||(c.tweens[j]={});for(o in f)e[o]===void 0&&this._addAttr(o,f[o]);this.reset();this.onFinish=f.onFinish;this.onReset=f.onReset;this.onUpdate=f.onUpdate}c.prototype._addAttr=function(b,d){var e=this.node,f=e._id,g,h,i,k,l,m;h=c.tweens[f][b];h&&delete c.attrs[f][h][b];h=e.getAttr(b);if(a.Util._isArray(d)){g=[];i=Math.max(d.length,h.length);b==="points"&&d.length!==h.length&&(d.length>h.length?(l=h,h=a.Util._prepareArrayForTween(h,d,e.closed())):(k=d,d=a.Util._prepareArrayForTween(d,h,e.closed())));if(b.indexOf("fill")===0)for(e=0;e4){d=this.getTensionPoints();g=d.length;h=e?0:4;e||a.quadraticCurveTo(d[0],d[1],d[2],d[3]);while(h4;f&&(e=this.getTensionPoints());var g=d.length,h,i;f?(h=d[g-2]-(e[e.length-2]+e[e.length-4])/2,i=d[g-1]-(e[e.length-1]+e[e.length-3])/2):(h=d[g-2]-d[g-4],i=d[g-1]-d[g-3]);var j=(Math.atan2(i,h)+c)%c,k=this.pointerLength(),l=this.pointerWidth();a.save();a.beginPath();a.translate(d[g-2],d[g-1]);a.rotate(j);a.moveTo(0,0);a.lineTo(-k,l/2);a.lineTo(-k,-l/2);a.closePath();a.restore();this.pointerAtBeginning()&&(a.save(),a.translate(d[0],d[1]),f?(h=(e[0]+e[2])/2-d[0],i=(e[1]+e[3])/2-d[1]):(h=d[2]-d[0],i=d[3]-d[1]),a.rotate((Math.atan2(-i,-h)+c)%c),a.moveTo(0,0),a.lineTo(-k,l/2),a.lineTo(-k,-l/2),a.closePath(),a.restore());g=this.dashEnabled();g&&(this.attrs.dashEnabled=!1,a.setLineDash([]));a.fillStrokeShape(this);g&&(this.attrs.dashEnabled=!0)};c.prototype.getSelfRect=function(){var a=b.prototype.getSelfRect.call(this),c=this.pointerWidth()/2;return{x:a.x-c,y:a.y-c,width:a.width+c*2,height:a.height+c*2}};return c}(d.Line);cb.Arrow=d;d.prototype.className="Arrow";f._registerNode(d);c.Factory.addGetterSetter(d,"pointerLength",10,e.getNumberValidator());c.Factory.addGetterSetter(d,"pointerWidth",10,e.getNumberValidator());c.Factory.addGetterSetter(d,"pointerAtBeginning",!1);b.Collection.mapMethods(d)}var fb=!1;function gb(){fb||(fb=!0,eb());return db.exports}var hb={},ib={exports:hb};function jb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(hb,"__esModule",{value:!0});hb.Circle=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){a.beginPath(),a.arc(0,0,this.attrs.radius||0,0,Math.PI*2,!1),a.closePath(),a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radius()*2};c.prototype.getHeight=function(){return this.radius()*2};c.prototype.setWidth=function(a){this.radius()!==a/2&&this.radius(a/2)};c.prototype.setHeight=function(a){this.radius()!==a/2&&this.radius(a/2)};return c}(d.Shape);hb.Circle=d;d.prototype._centroid=!0;d.prototype.className="Circle";d.prototype._attrsAffectingSize=["radius"];f._registerNode(d);c.Factory.addGetterSetter(d,"radius",0,e.getNumberValidator());b.Collection.mapMethods(d)}var kb=!1;function lb(){kb||(kb=!0,jb());return ib.exports}var mb={},nb={exports:mb};function ob(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(mb,"__esModule",{value:!0});mb.Ellipse=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this.radiusX(),c=this.radiusY();a.beginPath();a.save();b!==c&&a.scale(1,c/b);a.arc(0,0,b,0,Math.PI*2,!1);a.restore();a.closePath();a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radiusX()*2};c.prototype.getHeight=function(){return this.radiusY()*2};c.prototype.setWidth=function(a){this.radiusX(a/2)};c.prototype.setHeight=function(a){this.radiusY(a/2)};return c}(d.Shape);mb.Ellipse=d;d.prototype.className="Ellipse";d.prototype._centroid=!0;d.prototype._attrsAffectingSize=["radiusX","radiusY"];f._registerNode(d);c.Factory.addComponentsGetterSetter(d,"radius",["x","y"]);c.Factory.addGetterSetter(d,"radiusX",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"radiusY",0,e.getNumberValidator());b.Collection.mapMethods(d)}var pb=!1;function qb(){pb||(pb=!0,ob());return nb.exports}var rb={},sb={exports:rb};function tb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(rb,"__esModule",{value:!0});rb.Image=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(c){a(d,c);function d(){return c!==null&&c.apply(this,arguments)||this}d.prototype._useBufferCanvas=function(){return c.prototype._useBufferCanvas.call(this,!0)};d.prototype._sceneFunc=function(a){var b=this.getWidth(),c=this.getHeight(),d=this.attrs.image,e;if(d){var f=this.attrs.cropWidth,g=this.attrs.cropHeight;f&&g?e=[d,this.cropX(),this.cropY(),f,g,0,0,b,c]:e=[d,0,0,b,c]}(this.hasFill()||this.hasStroke())&&(a.beginPath(),a.rect(0,0,b,c),a.closePath(),a.fillStrokeShape(this));d&&a.drawImage.apply(a,e)};d.prototype._hitFunc=function(a){var b=this.width(),c=this.height();a.beginPath();a.rect(0,0,b,c);a.closePath();a.fillStrokeShape(this)};d.prototype.getWidth=function(){var a;return(a=this.attrs.width)!==null&&a!==void 0?a:((a=this.image())===null||a===void 0?void 0:a.width)||0};d.prototype.getHeight=function(){var a;return(a=this.attrs.height)!==null&&a!==void 0?a:((a=this.image())===null||a===void 0?void 0:a.height)||0};d.fromURL=function(a,c){var e=b.Util.createImageElement();e.onload=function(){var a=new d({image:e});c(a)};e.crossOrigin="Anonymous";e.src=a};return d}(d.Shape);rb.Image=d;d.prototype.className="Image";f._registerNode(d);c.Factory.addGetterSetter(d,"image");c.Factory.addComponentsGetterSetter(d,"crop",["x","y","width","height"]);c.Factory.addGetterSetter(d,"cropX",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropY",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropWidth",0,e.getNumberValidator());c.Factory.addGetterSetter(d,"cropHeight",0,e.getNumberValidator());b.Collection.mapMethods(d)}var ub=!1;function vb(){ub||(ub=!0,tb());return sb.exports}var Z={},wb={exports:Z};function xb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Z,"__esModule",{value:!0});Z.Tag=Z.Label=void 0;var b=p(),c=z(),d=X(),e=Ea(),f=u(),g=k(),h=["fontFamily","fontSize","fontStyle","padding","lineHeight","text","width","height"],i="Change.konva",j="none",l="up",m="right",n="down",o="left",q=h.length;e=function(b){a(c,b);function c(a){a=b.call(this,a)||this;a.on("add.konva",function(a){this._addListeners(a.child),this._sync()});return a}c.prototype.getText=function(){return this.find("Text")[0]};c.prototype.getTag=function(){return this.find("Tag")[0]};c.prototype._addListeners=function(a){var b=this,c,d=function(){b._sync()};for(c=0;ci?h:i,n=h>i?1:h/i;i=h>i?i/h:1;a.translate(e,g);a.rotate(l);a.scale(n,i);a.arc(0,0,m,j,j+k,1-f);a.scale(1/n,1/i);a.rotate(-l);a.translate(-e,-g);break;case"z":c=!0;a.closePath();break}}!c&&!this.hasFill()?a.strokeShape(this):a.fillStrokeShape(this)};c.prototype.getSelfRect=function(){var a=[];this.dataArray.forEach(function(b){if(b.command==="A"){var d=b.points[4],e=b.points[5],f=b.points[4]+e,g=Math.PI/180;Math.abs(d-f)f;e-=g){var h=c.getPointOnEllipticalArc(b.points[0],b.points[1],b.points[2],b.points[3],e,0);a.push(h.x,h.y)}else for(e=d+g;ethis.dataArray[b].pathLength)a-=this.dataArray[b].pathLength,++b;if(b===d){d=this.dataArray[b-1].points.slice(-2);return{x:d[0],y:d[1]}}if(a<.01){d=this.dataArray[b].points.slice(0,2);return{x:d[0],y:d[1]}}d=this.dataArray[b];b=d.points;switch(d.command){case"L":return c.getPointOnLine(a,d.start.x,d.start.y,b[0],b[1]);case"C":return c.getPointOnCubicBezier(a/d.pathLength,d.start.x,d.start.y,b[0],b[1],b[2],b[3],b[4],b[5]);case"Q":return c.getPointOnQuadraticBezier(a/d.pathLength,d.start.x,d.start.y,b[0],b[1],b[2],b[3]);case"A":var e=b[0],f=b[1],g=b[2],h=b[3],i=b[4],j=b[5];b=b[6];i+=j*a/d.pathLength;return c.getPointOnEllipticalArc(e,f,g,h,i,b)}return null};c.getLineLength=function(a,b,c,d){return Math.sqrt((c-a)*(c-a)+(d-b)*(d-b))};c.getPointOnLine=function(a,b,c,d,e,f,g){f===void 0&&(f=b);g===void 0&&(g=c);var h=(e-c)/(d-b+1e-8),i=Math.sqrt(a*a/(1+h*h));d0){if(isNaN(h[0]))break;l=null;i=[];k=e;var m=f,n,o,p,q;switch(j){case"l":e+=h.shift();f+=h.shift();l="L";i.push(e,f);break;case"L":e=h.shift();f=h.shift();i.push(e,f);break;case"m":var r=h.shift(),s=h.shift();e+=r;f+=s;l="M";if(a.length>2&&a[a.length-1].command==="z")for(var t=a.length-2;t>=0;t--)if(a[t].command==="M"){e=a[t].points[0]+r;f=a[t].points[1]+s;break}i.push(e,f);j="l";break;case"M":e=h.shift();f=h.shift();l="M";i.push(e,f);j="L";break;case"h":e+=h.shift();l="L";i.push(e,f);break;case"H":e=h.shift();l="L";i.push(e,f);break;case"v":f+=h.shift();l="L";i.push(e,f);break;case"V":f=h.shift();l="L";i.push(e,f);break;case"C":i.push(h.shift(),h.shift(),h.shift(),h.shift());e=h.shift();f=h.shift();i.push(e,f);break;case"c":i.push(e+h.shift(),f+h.shift(),e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="C";i.push(e,f);break;case"S":r=e;s=f;t=a[a.length-1];t.command==="C"&&(r=e+(e-t.points[2]),s=f+(f-t.points[3]));i.push(r,s,h.shift(),h.shift());e=h.shift();f=h.shift();l="C";i.push(e,f);break;case"s":r=e;s=f;t=a[a.length-1];t.command==="C"&&(r=e+(e-t.points[2]),s=f+(f-t.points[3]));i.push(r,s,e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="C";i.push(e,f);break;case"Q":i.push(h.shift(),h.shift());e=h.shift();f=h.shift();i.push(e,f);break;case"q":i.push(e+h.shift(),f+h.shift());e+=h.shift();f+=h.shift();l="Q";i.push(e,f);break;case"T":r=e;s=f;t=a[a.length-1];t.command==="Q"&&(r=e+(e-t.points[0]),s=f+(f-t.points[1]));e=h.shift();f=h.shift();l="Q";i.push(r,s,e,f);break;case"t":r=e;s=f;t=a[a.length-1];t.command==="Q"&&(r=e+(e-t.points[0]),s=f+(f-t.points[1]));e+=h.shift();f+=h.shift();l="Q";i.push(r,s,e,f);break;case"A":t=h.shift();r=h.shift();s=h.shift();n=h.shift();o=h.shift();p=e;q=f;e=h.shift();f=h.shift();l="A";i=this.convertEndpointToCenterParameterization(p,q,e,f,n,o,t,r,s);break;case"a":t=h.shift();r=h.shift();s=h.shift();n=h.shift();o=h.shift();p=e;q=f;e+=h.shift();f+=h.shift();l="A";i=this.convertEndpointToCenterParameterization(p,q,e,f,n,o,t,r,s);break}a.push({command:l||j,points:i,start:{x:k,y:m},pathLength:this.calcLength(k,m,l||j,i)})}(j==="z"||j==="Z")&&a.push({command:"z",points:[],start:void 0,pathLength:0})}return a};c.calcLength=function(a,b,d,e){var f,g,h,i=c;switch(d){case"L":return i.getLineLength(a,b,e[0],e[1]);case"C":d=0;f=i.getPointOnCubicBezier(0,a,b,e[0],e[1],e[2],e[3],e[4],e[5]);for(h=.01;h<=1;h+=.01)g=i.getPointOnCubicBezier(h,a,b,e[0],e[1],e[2],e[3],e[4],e[5]),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;return d;case"Q":d=0;f=i.getPointOnQuadraticBezier(0,a,b,e[0],e[1],e[2],e[3]);for(h=.01;h<=1;h+=.01)g=i.getPointOnQuadraticBezier(h,a,b,e[0],e[1],e[2],e[3]),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;return d;case"A":d=0;a=e[4];b=e[5];var j=e[4]+b,k=Math.PI/180;Math.abs(a-j)j;h-=k)g=i.getPointOnEllipticalArc(e[0],e[1],e[2],e[3],h,0),d+=i.getLineLength(f.x,f.y,g.x,g.y),f=g;else for(h=a+k;h1&&(g*=Math.sqrt(l),h*=Math.sqrt(l));l=Math.sqrt((g*g*(h*h)-g*g*(k*k)-h*h*(j*j))/(g*g*(k*k)+h*h*(j*j)));e===f&&(l*=-1);isNaN(l)&&(l=0);e=l*g*k/h;l=l*-h*j/g;a=(a+c)/2+Math.cos(i)*e-Math.sin(i)*l;c=(b+d)/2+Math.sin(i)*e+Math.cos(i)*l;var m=function(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1])},n=function(a,b){return(a[0]*b[0]+a[1]*b[1])/(m(a)*m(b))};b=function(a,b){return(a[0]*b[1]=1&&(e=0);f===0&&e>0&&(e=e-2*Math.PI);f===1&&e<0&&(e=e+2*Math.PI);return[a,c,g,h,d,e,i,f]};return c}(d.Shape);Ab.Path=d;d.prototype.className="Path";d.prototype._attrsAffectingSize=["data"];e._registerNode(d);c.Factory.addGetterSetter(d,"data");b.Collection.mapMethods(d)}var Db=!1;function Eb(){Db||(Db=!0,Cb());return Bb.exports}var Fb={},Gb={exports:Fb};function Hb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Fb,"__esModule",{value:!0});Fb.Rect=void 0;var b=p(),c=z(),d=X(),e=k(),f=u();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this.cornerRadius(),c=this.width(),d=this.height();a.beginPath();if(!b)a.rect(0,0,c,d);else{var e=0,f=0,g=0,h=0;typeof b==="number"?e=f=g=h=Math.min(b,c/2,d/2):(e=Math.min(b[0]||0,c/2,d/2),f=Math.min(b[1]||0,c/2,d/2),h=Math.min(b[2]||0,c/2,d/2),g=Math.min(b[3]||0,c/2,d/2));a.moveTo(e,0);a.lineTo(c-f,0);a.arc(c-f,f,f,Math.PI*3/2,0,!1);a.lineTo(c,d-h);a.arc(c-h,d-h,h,0,Math.PI/2,!1);a.lineTo(g,d);a.arc(g,d-g,g,Math.PI/2,Math.PI,!1);a.lineTo(0,e);a.arc(e,e,e,Math.PI,Math.PI*3/2,!1)}a.closePath();a.fillStrokeShape(this)};return c}(d.Shape);Fb.Rect=d;d.prototype.className="Rect";e._registerNode(d);c.Factory.addGetterSetter(d,"cornerRadius",0,f.getNumberOrArrayOfNumbersValidator(4));b.Collection.mapMethods(d)}var Ib=!1;function Jb(){Ib||(Ib=!0,Hb());return Gb.exports}var Kb={},Lb={exports:Kb};function Mb(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(Kb,"__esModule",{value:!0});Kb.RegularPolygon=void 0;var b=p(),c=z(),d=X(),e=u(),f=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){var b=this._getPoints();a.beginPath();a.moveTo(b[0].x,b[0].y);for(var c=1;c=0,c=a.indexOf(""")>=0||a.indexOf(""")>=0;b&&!c&&(a="""+a+""");return a}).join(", ")}var J;function K(){if(J)return J;J=b.Util.createCanvasElement().getContext(n);return J}function L(a){a.fillText(this._partialText,this._partialTextX,this._partialTextY)}function M(a){a.strokeText(this._partialText,this._partialTextX,this._partialTextY)}function N(a){a=a||{};!a.fillLinearGradientColorStops&&!a.fillRadialGradientColorStops&&!a.fillPatternImage&&(a.fill=a.fill||"black");return a}d=function(c){a(d,c);function d(a){a=c.call(this,N(a))||this;a._partialTextX=0;a._partialTextY=0;for(var b=0;b1&&(s+=f)}};d.prototype._hitFunc=function(a){var b=this.getWidth(),c=this.getHeight();a.beginPath();a.rect(0,0,b,c);a.closePath();a.fillStrokeShape(this)};d.prototype.setText=function(a){a=b.Util._isString(a)?a:a===null||a===void 0?"":a+"";this._setAttr(r,a);return this};d.prototype.getWidth=function(){var a=this.attrs.width===i||this.attrs.width===void 0;return a?this.getTextWidth()+this.padding()*2:this.attrs.width};d.prototype.getHeight=function(){var a=this.attrs.height===i||this.attrs.height===void 0;return a?this.fontSize()*this.textArr.length*this.lineHeight()+this.padding()*2:this.attrs.height};d.prototype.getTextWidth=function(){return this.textWidth};d.prototype.getTextHeight=function(){b.Util.warn("text.getTextHeight() method is deprecated. Use text.height() - for full height and text.fontSize() - for one line height.");return this.textHeight};d.prototype.measureSize=function(a){var b=K(),c=this.fontSize();b.save();b.font=this._getContextFont();a=b.measureText(a);b.restore();return{width:a.width,height:c}};d.prototype._getContextFont=function(){return e.Konva.UA.isIE?this.fontStyle()+A+this.fontSize()+y+this.fontFamily():this.fontStyle()+A+this.fontVariant()+A+(this.fontSize()+y)+I(this.fontFamily())};d.prototype._addTextLine=function(a){this.align()===l&&(a=a.trim());var b=this._getTextWidth(a);return this.textArr.push({text:a,width:b})};d.prototype._getTextWidth=function(a){var b=this.letterSpacing(),c=a.length;return K().measureText(a).width+(c?b*(c-1):0)};d.prototype._setTextData=function(){var a=this.text().split(" "),b=+this.fontSize(),c=0,d=this.lineHeight()*b,e=this.attrs.width,f=this.attrs.height,g=e!==i&&e!==void 0,h=f!==i&&f!==void 0,j=this.padding();e=e-j*2;f=f-j*2;j=0;var k=this.wrap(),l=k!==E;k=k!==D&&l;var m=this.ellipsis();this.textArr=[];K().font=this._getContextFont();var n=m?this._getTextWidth(F):0;for(var p=0,q=a.length;pe)while(r.length>0){var t=0,u=r.length,v="",w=0;while(t>>1,y=r.slice(0,x+1),z=this._getTextWidth(y)+n;z<=e?(t=x+1,v=y,w=z):u=x}if(v){if(k){y=r[v.length];z=y===A||y===o;z&&w<=e?x=v.length:x=Math.max(v.lastIndexOf(A),v.lastIndexOf(o))+1;x>0&&(t=x,v=v.slice(0,t),w=this._getTextWidth(v))}v=v.trimRight();this._addTextLine(v);c=Math.max(c,w);j+=d;if(!l||h&&j+d>f){u=this.textArr[this.textArr.length-1];if(u&&m){y=this._getTextWidth(u.text+F)0){s=this._getTextWidth(r);if(s<=e){this._addTextLine(r);j+=d;c=Math.max(c,s);break}}}else break}else this._addTextLine(r),j+=d,c=Math.max(c,s);if(h&&j+d>f)break}this.textHeight=b;this.textWidth=c};d.prototype.getStrokeScaleEnabled=function(){return!0};return d}(d.Shape);$.Text=d;d.prototype._fillFunc=L;d.prototype._strokeFunc=M;d.prototype.className=s;d.prototype._attrsAffectingSize=["text","fontSize","padding","wrap","lineHeight"];g._registerNode(d);c.Factory.overWriteSetter(d,"width",f.getNumberOrAutoValidator());c.Factory.overWriteSetter(d,"height",f.getNumberOrAutoValidator());c.Factory.addGetterSetter(d,"fontFamily","Arial");c.Factory.addGetterSetter(d,"fontSize",12,f.getNumberValidator());c.Factory.addGetterSetter(d,"fontStyle",x);c.Factory.addGetterSetter(d,"fontVariant",x);c.Factory.addGetterSetter(d,"padding",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"align",q);c.Factory.addGetterSetter(d,"verticalAlign",t);c.Factory.addGetterSetter(d,"lineHeight",1,f.getNumberValidator());c.Factory.addGetterSetter(d,"wrap",C);c.Factory.addGetterSetter(d,"ellipsis",!1,f.getBooleanValidator());c.Factory.addGetterSetter(d,"letterSpacing",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"text","",f.getStringValidator());c.Factory.addGetterSetter(d,"textDecoration","");b.Collection.mapMethods(d)}var fc=!1;function gc(){fc||(fc=!0,ec());return dc.exports}var hc={},ic={exports:hc};function jc(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(hc,"__esModule",{value:!0});hc.TextPath=void 0;var b=p(),c=z(),d=X(),e=Eb(),f=gc(),g=u(),h=k(),i="",j="normal";function l(a){a.fillText(this.partialText,0,0)}function m(a){a.strokeText(this.partialText,0,0)}d=function(c){a(d,c);function d(a){var d=c.call(this,a)||this;d.dummyCanvas=b.Util.createCanvasElement();d.dataArray=[];d.dataArray=e.Path.parsePathData(d.attrs.data);d.on("dataChange.konva",function(){this.dataArray=e.Path.parsePathData(this.attrs.data),this._setTextData()});d.on("textChange.konva alignChange.konva letterSpacingChange.konva kerningFuncChange.konva",d._setTextData);a&&a.getKerning&&(b.Util.warn("getKerning TextPath API is deprecated. Please use "kerningFunc" instead."),d.kerningFunc(a.getKerning));d._setTextData();return d}d.prototype._sceneFunc=function(a){a.setAttr("font",this._getContextFont());a.setAttr("textBaseline",this.textBaseline());a.setAttr("textAlign","left");a.save();var b=this.textDecoration(),c=this.fill(),d=this.fontSize(),e=this.glyphInfo;b==="underline"&&a.beginPath();for(var f=0;f=1){var c=b[0].p0;a.moveTo(c.x,c.y)}for(c=0;c0&&(i+=a.dataArray[b].pathLength);b=0;d==="center"&&(b=Math.max(0,i/2-h/2));d==="right"&&(b=Math.max(0,i-h));var j=f.stringToArray(this.text()),k=this.text().split(" ").length-1,l,m,n,o=-1,p=0,q=function(){p=0;var b=a.dataArray;for(var c=o+1;c0){o=c;return b[c]}else b[c].command==="M"&&(l={x:b[c].points[0],y:b[c].points[1]});return{}},r=function(f){var b=a._getTextSize(f).width+c;f===" "&&d==="justify"&&(b+=(i-h)/k);f=0;var g=0;m=void 0;while(Math.abs(b-f)/b>.01&&g<20){g++;var j=f;while(n===void 0)n=q(),n&&j+n.pathLengthb?m=e.Path.getPointOnLine(b,l.x,l.y,n.points[0],n.points[1],l.x,l.y):n=void 0;break;case"A":var o=n.points[4],r=n.points[5],s=n.points[4]+r;p===0?p=o+1e-8:b>f?p+=Math.PI/180*r/Math.abs(r):p-=Math.PI/360*r/Math.abs(r);(r<0&&p=0&&p>s)&&(p=s,j=!0);m=e.Path.getPointOnEllipticalArc(n.points[0],n.points[1],n.points[2],n.points[3],p,n.points[6]);break;case"C":p===0?b>n.pathLength?p=1e-8:p=b/n.pathLength:b>f?p+=(b-f)/n.pathLength/2:p=Math.max(p-(f-b)/n.pathLength/2,0);p>1&&(p=1,j=!0);m=e.Path.getPointOnCubicBezier(p,n.start.x,n.start.y,n.points[0],n.points[1],n.points[2],n.points[3],n.points[4],n.points[5]);break;case"Q":p===0?p=b/n.pathLength:b>f?p+=(b-f)/n.pathLength:p-=(f-b)/n.pathLength;p>1&&(p=1,j=!0);m=e.Path.getPointOnQuadraticBezier(p,n.start.x,n.start.y,n.points[0],n.points[1],n.points[2],n.points[3]);break}m!==void 0&&(f=e.Path.getLineLength(l.x,l.y,m.x,m.y));j&&(j=!1,n=void 0)}},s="C",t=a._getTextSize(s).width+c;b=b/t-1;for(t=0;th.x?-1:1,k=this.findOne(".top-left").y()>h.y?-1:1;e=c*this.cos*j;b=c*this.sin*k;this.findOne(".top-left").x(h.x-e);this.findOne(".top-left").y(h.y-b)}}else if(this._movingAnchorName==="top-center")this.findOne(".top-left").y(d.y());else if(this._movingAnchorName==="top-right"){if(f){h=g?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()};c=Math.sqrt(Math.pow(d.x()-h.x,2)+Math.pow(h.y-d.y(),2));var j=this.findOne(".top-right").x()h.y?-1:1;e=c*this.cos*j;b=c*this.sin*k;this.findOne(".top-right").x(h.x+e);this.findOne(".top-right").y(h.y-b)}var l=d.position();this.findOne(".top-left").y(l.y);this.findOne(".bottom-right").x(l.x)}else if(this._movingAnchorName==="middle-left")this.findOne(".top-left").x(d.x());else if(this._movingAnchorName==="middle-right")this.findOne(".bottom-right").x(d.x());else if(this._movingAnchorName==="bottom-left"){if(f){h=g?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()};c=Math.sqrt(Math.pow(h.x-d.x(),2)+Math.pow(d.y()-h.y,2));var j=h.x=0){var h=g.point({x:-this.padding()*2,y:0});a.x+=h.x;a.y+=h.y;a.width+=this.padding()*2;this._movingAnchorName=this._movingAnchorName.replace("left","right");this._anchorDragOffset.x-=h.x;this._anchorDragOffset.y-=h.y;if(!f){this.update();return}}else if(this._movingAnchorName&&a.width<0&&this._movingAnchorName.indexOf("right")>=0){var h=g.point({x:this.padding()*2,y:0});this._movingAnchorName=this._movingAnchorName.replace("right","left");this._anchorDragOffset.x-=h.x;this._anchorDragOffset.y-=h.y;a.width+=this.padding()*2;if(!f){this.update();return}}if(this._movingAnchorName&&a.height<0&&this._movingAnchorName.indexOf("top")>=0){var h=g.point({x:0,y:-this.padding()*2});a.x+=h.x;a.y+=h.y;this._movingAnchorName=this._movingAnchorName.replace("top","bottom");this._anchorDragOffset.x-=h.x;this._anchorDragOffset.y-=h.y;a.height+=this.padding()*2;if(!f){this.update();return}}else if(this._movingAnchorName&&a.height<0&&this._movingAnchorName.indexOf("bottom")>=0){var h=g.point({x:0,y:this.padding()*2});this._movingAnchorName=this._movingAnchorName.replace("bottom","top");this._anchorDragOffset.x-=h.x;this._anchorDragOffset.y-=h.y;a.height+=this.padding()*2;if(!f){this.update();return}}if(this.boundBoxFunc()){g=this.boundBoxFunc()(e,a);g?a=g:c.Util.warn("boundBoxFunc returned falsy. You should return new bound rect from it!")}h=1e7;f=new c.Transform();f.translate(e.x,e.y);f.rotate(e.rotation);f.scale(e.width/h,e.height/h);g=new c.Transform();g.translate(a.x,a.y);g.rotate(a.rotation);g.scale(a.width/h,a.height/h);var j=g.multiply(f.invert());this._nodes.forEach(function(a){var e=a.getParent().getAbsoluteTransform(),f=a.getTransform().copy();f.translate(a.offsetX(),a.offsetY());var g=new c.Transform();g.multiply(e.copy().invert()).multiply(j).multiply(e).multiply(f);e=g.decompose();a.setAttrs(e);d._fire("transform",{evt:b,target:a});a._fire("transform",{evt:b,target:a});(f=a.getLayer())===null||f===void 0?void 0:f.batchDraw()});this.rotation(c.Util._getRotation(a.rotation));this._resetTransformCache();this.update();this.getLayer().batchDraw()};b.prototype.forceUpdate=function(){this._resetTransformCache(),this.update()};b.prototype._batchChangeChild=function(a,b){a=this.findOne(a);a.setAttrs(b)};b.prototype.update=function(){var a=this,b=this._getNodeRect();this.rotation(c.Util._getRotation(b.rotation));var d=b.width;b=b.height;var e=this.enabledAnchors(),f=this.resizeEnabled(),g=this.padding(),h=this.anchorSize();this.find("._anchor").each(function(b){b.setAttrs({width:h,height:h,offsetX:h/2,offsetY:h/2,stroke:a.anchorStroke(),strokeWidth:a.anchorStrokeWidth(),fill:a.anchorFill(),cornerRadius:a.anchorCornerRadius()})});this._batchChangeChild(".top-left",{x:0,y:0,offsetX:h/2+g,offsetY:h/2+g,visible:f&&e.indexOf("top-left")>=0});this._batchChangeChild(".top-center",{x:d/2,y:0,offsetY:h/2+g,visible:f&&e.indexOf("top-center")>=0});this._batchChangeChild(".top-right",{x:d,y:0,offsetX:h/2-g,offsetY:h/2+g,visible:f&&e.indexOf("top-right")>=0});this._batchChangeChild(".middle-left",{x:0,y:b/2,offsetX:h/2+g,visible:f&&e.indexOf("middle-left")>=0});this._batchChangeChild(".middle-right",{x:d,y:b/2,offsetX:h/2-g,visible:f&&e.indexOf("middle-right")>=0});this._batchChangeChild(".bottom-left",{x:0,y:b,offsetX:h/2+g,offsetY:h/2-g,visible:f&&e.indexOf("bottom-left")>=0});this._batchChangeChild(".bottom-center",{x:d/2,y:b,offsetY:h/2-g,visible:f&&e.indexOf("bottom-center")>=0});this._batchChangeChild(".bottom-right",{x:d,y:b,offsetX:h/2-g,offsetY:h/2-g,visible:f&&e.indexOf("bottom-right")>=0});this._batchChangeChild(".rotater",{x:d/2,y:-this.rotateAnchorOffset()*c.Util._sign(b)-g,visible:this.rotateEnabled()});this._batchChangeChild(".back",{width:d,height:b,visible:this.borderEnabled(),stroke:this.borderStroke(),strokeWidth:this.borderStrokeWidth(),dash:this.borderDash(),x:0,y:0});(f=this.getLayer())===null||f===void 0?void 0:f.batchDraw()};b.prototype.isTransforming=function(){return this._transforming};b.prototype.stopTransform=function(){if(this._transforming){this._removeEvents();var a=this.findOne("."+this._movingAnchorName);a&&a.stopDrag()}};b.prototype.destroy=function(){this.getStage()&&this._cursorChange&&(this.getStage().content.style.cursor="");h.Group.prototype.destroy.call(this);this.detach();this._removeEvents();return this};b.prototype.toObject=function(){return e.Node.prototype.toObject.call(this)};return b}(h.Group);mc.Transformer=C;function D(a){a instanceof Array||c.Util.warn("enabledAnchors value should be an array");a instanceof Array&&a.forEach(function(a){v.indexOf(a)===-1&&c.Util.warn("Unknown anchor name: "+a+". Available names are: "+v.join(", "))});return a||[]}C.prototype.className="Transformer";l._registerNode(C);d.Factory.addGetterSetter(C,"enabledAnchors",v,D);d.Factory.addGetterSetter(C,"resizeEnabled",!0);d.Factory.addGetterSetter(C,"anchorSize",10,j.getNumberValidator());d.Factory.addGetterSetter(C,"rotateEnabled",!0);d.Factory.addGetterSetter(C,"rotationSnaps",[]);d.Factory.addGetterSetter(C,"rotateAnchorOffset",50,j.getNumberValidator());d.Factory.addGetterSetter(C,"rotationSnapTolerance",5,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderEnabled",!0);d.Factory.addGetterSetter(C,"anchorStroke","rgb(0, 161, 255)");d.Factory.addGetterSetter(C,"anchorStrokeWidth",1,j.getNumberValidator());d.Factory.addGetterSetter(C,"anchorFill","white");d.Factory.addGetterSetter(C,"anchorCornerRadius",0,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderStroke","rgb(0, 161, 255)");d.Factory.addGetterSetter(C,"borderStrokeWidth",1,j.getNumberValidator());d.Factory.addGetterSetter(C,"borderDash");d.Factory.addGetterSetter(C,"keepRatio",!0);d.Factory.addGetterSetter(C,"centeredScaling",!1);d.Factory.addGetterSetter(C,"ignoreStroke",!1);d.Factory.addGetterSetter(C,"padding",0,j.getNumberValidator());d.Factory.addGetterSetter(C,"node");d.Factory.addGetterSetter(C,"nodes");d.Factory.addGetterSetter(C,"boundBoxFunc");d.Factory.addGetterSetter(C,"shouldOverdrawWholeArea",!1);d.Factory.backCompat(C,{lineEnabled:"borderEnabled",rotateHandlerOffset:"rotateAnchorOffset",enabledHandlers:"enabledAnchors"});c.Collection.mapMethods(C)}var pc=!1;function qc(){pc||(pc=!0,oc());return nc.exports}var rc={},sc={exports:rc};function tc(){var a=this&&this.__extends||function(){var a=function(b,c){a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])};return a(b,c)};return function(b,c){a(b,c);function d(){this.constructor=b}b.prototype=c===null?Object.create(c):(d.prototype=c.prototype,new d())}}();Object.defineProperty(rc,"__esModule",{value:!0});rc.Wedge=void 0;var b=p(),c=z(),d=X(),e=k(),f=u(),g=k();d=function(b){a(c,b);function c(){return b!==null&&b.apply(this,arguments)||this}c.prototype._sceneFunc=function(a){a.beginPath(),a.arc(0,0,this.radius(),0,e.Konva.getAngle(this.angle()),this.clockwise()),a.lineTo(0,0),a.closePath(),a.fillStrokeShape(this)};c.prototype.getWidth=function(){return this.radius()*2};c.prototype.getHeight=function(){return this.radius()*2};c.prototype.setWidth=function(a){this.radius(a/2)};c.prototype.setHeight=function(a){this.radius(a/2)};return c}(d.Shape);rc.Wedge=d;d.prototype.className="Wedge";d.prototype._centroid=!0;d.prototype._attrsAffectingSize=["radius"];g._registerNode(d);c.Factory.addGetterSetter(d,"radius",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"angle",0,f.getNumberValidator());c.Factory.addGetterSetter(d,"clockwise",!1);c.Factory.backCompat(d,{angleDeg:"angle",getAngleDeg:"getAngle",setAngleDeg:"setAngle"});b.Collection.mapMethods(d)}var uc=!1;function vc(){uc||(uc=!0,tc());return sc.exports}var wc={},xc={exports:wc};function yc(){Object.defineProperty(wc,"__esModule",{value:!0});wc.Blur=void 0;var a=z(),b=R(),c=u();function d(){this.r=0,this.g=0,this.b=0,this.a=0,this.next=null}var e=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],f=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function g(a,b){var c=a.data,g=a.width;a=a.height;var h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=b+b+1,E=g-1,F=a-1,G=b+1,H=G*(G+1)/2,I=new d(),J=null,K=I,L,M,N=e[b],O=f[b];for(j=1;j>O,B!==0?(B=255/B,c[D]=(m*N>>O)*B,c[D+1]=(n*N>>O)*B,c[D+2]=(o*N>>O)*B):c[D]=c[D+1]=c[D+2]=0,m-=q,n-=r,o-=s,p-=t,q-=L.r,r-=L.g,s-=L.b,t-=L.a,k=l+((k=h+b+1)>O,B>0?(B=255/B,c[k]=(m*N>>O)*B,c[k+1]=(n*N>>O)*B,c[k+2]=(o*N>>O)*B):c[k]=c[k+1]=c[k+2]=0,m-=q,n-=r,o-=s,p-=t,q-=L.r,r-=L.g,s-=L.b,t-=L.a,k=h+((k=i+G)0&&g(a,b)};wc.Blur=h;a.Factory.addGetterSetter(b.Node,"blurRadius",0,c.getNumberValidator(),a.Factory.afterSetFilter)}var zc=!1;function Ac(){zc||(zc=!0,yc());return xc.exports}var Bc={},Cc={exports:Bc};function Dc(){Object.defineProperty(Bc,"__esModule",{value:!0});Bc.Brighten=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=this.brightness()*255;a=a.data;var c=a.length,d;for(d=0;d255?255:d,e=e<0?0:e>255?255:e,f=f<0?0:f>255?255:f,a[g]=d,a[g+1]=e,a[g+2]=f};Gc.Contrast=d;a.Factory.addGetterSetter(b.Node,"contrast",0,c.getNumberValidator(),a.Factory.afterSetFilter)}var Jc=!1;function Kc(){Jc||(Jc=!0,Ic());return Hc.exports}var Lc={},Mc={exports:Lc};function Nc(){Object.defineProperty(Lc,"__esModule",{value:!0});Lc.Emboss=void 0;var a=z(),b=R(),c=p(),d=u(),e=function(a){var b=this.embossStrength()*10,d=this.embossWhiteLevel()*255,e=this.embossDirection(),f=this.embossBlend(),g=0,h=0,i=a.data,j=a.width;a=a.height;var k=j*4,l=a;switch(e){case"top-left":g=-1;h=-1;break;case"top":g=-1;h=0;break;case"top-right":g=-1;h=1;break;case"right":g=0;h=1;break;case"bottom-right":g=1;h=1;break;case"bottom":g=1;h=0;break;case"bottom-left":g=1;h=-1;break;case"left":g=0;h=-1;break;default:c.Util.error("Unknown emboss direction: "+e)}do{e=(l-1)*k;var m=g;l+m<1&&(m=0);l+m>a&&(m=0);m=(l-1+m)*j*4;var n=j;do{var o=e+(n-1)*4,p=h;n+p<1&&(p=0);n+p>j&&(p=0);p=m+(n-1+p)*4;var q=i[o]-i[p],r=i[o+1]-i[p+1];p=i[o+2]-i[p+2];q=q;var s=q>0?q:-q,t=r>0?r:-r,u=p>0?p:-p;t>s&&(q=r);u>s&&(q=p);q*=b;if(f){t=i[o]+q;r=i[o+1]+q;u=i[o+2]+q;i[o]=t>255?255:t<0?0:t;i[o+1]=r>255?255:r<0?0:r;i[o+2]=u>255?255:u<0?0:u}else{s=d-q;s<0?s=0:s>255&&(s=255);i[o]=i[o+1]=i[o+2]=s}}while(--n)}while(--l)};Lc.Emboss=e;a.Factory.addGetterSetter(b.Node,"embossStrength",.5,d.getNumberValidator(),a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossWhiteLevel",.5,d.getNumberValidator(),a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossDirection","top-left",null,a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"embossBlend",!1,null,a.Factory.afterSetFilter)}var Oc=!1;function Pc(){Oc||(Oc=!0,Nc());return Mc.exports}var Qc={},Rc={exports:Qc};function Sc(){Object.defineProperty(Qc,"__esModule",{value:!0});Qc.Enhance=void 0;var a=z(),b=R(),c=u();function d(a,b,c,d,e){c=c-b;e=e-d;if(c===0)return d+e/2;if(e===0)return d;a=(a-b)/c;a=e*a+d;return a}var e=function(a){a=a.data;var b=a.length,c=a[0],e=c,f,g=a[1],h=g,i=a[2],j=i,k,l=this.enhance();if(l===0)return;for(k=0;ke&&(e=f),f=a[k+1],fh&&(h=f),f=a[k+2],fj&&(j=f);e===c&&(e=255,c=0);h===g&&(h=255,g=0);j===i&&(j=255,i=0);var m,n,o,p,q,r;l>0?(f=e+l*(255-e),n=c-l*(c-0),o=h+l*(255-h),p=g-l*(g-0),q=j+l*(255-j),r=i-l*(i-0)):(m=(e+c)*.5,f=e+l*(e-m),n=c+l*(c-m),m=(h+g)*.5,o=h+l*(h-m),p=g+l*(g-m),m=(j+i)*.5,q=j+l*(j-m),r=i+l*(i-m));for(k=0;kn?m:n;m=a;a=e;var o,p,q=360/a*Math.PI/180,r,s;for(p=0;po?n:o;n=a;var p=e,q;c=c.polarRotation||0;for(h=0;hb&&(t=s,u=0,v=-1);for(h=0;h=0&&n=0&&o=0&&n=0&&o=255*4?255:0}return g}function k(a,b,c){var d=[1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9],e=Math.round(Math.sqrt(d.length)),f=Math.floor(e/2),g=[];for(var h=0;h=0&&n=0&&o=d)continue;for(g=q;g=e)continue;h=(d*g+f)*4;i+=a[h+0];j+=a[h+1];k+=a[h+2];l+=a[h+3];u+=1}}i=i/u;j=j/u;k=k/u;l=l/u;for(f=o;f=d)continue;for(g=q;g=e)continue;h=(d*g+f)*4;a[h+0]=i;a[h+1]=j;a[h+2]=k;a[h+3]=l}}}};Dd.Pixelate=e;a.Factory.addGetterSetter(c.Node,"pixelSize",8,d.getNumberValidator(),a.Factory.afterSetFilter)}var Gd=!1;function Hd(){Gd||(Gd=!0,Fd());return Ed.exports}var Id={},Jd={exports:Id};function Kd(){Object.defineProperty(Id,"__esModule",{value:!0});Id.Posterize=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=Math.round(this.levels()*254)+1;a=a.data;var c=a.length;b=255/b;var d;for(d=0;d255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"green",0,function(a){this._filterUpToDate=!1;if(a>255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"blue",0,c.RGBComponent,a.Factory.afterSetFilter)}var Qd=!1;function Rd(){Qd||(Qd=!0,Pd());return Od.exports}var Sd={},Td={exports:Sd};function Ud(){Object.defineProperty(Sd,"__esModule",{value:!0});Sd.RGBA=void 0;var a=z(),b=R(),c=u(),d=function(a){a=a.data;var b=a.length,c=this.red(),d=this.green(),e=this.blue(),f=this.alpha(),g,h;for(g=0;g255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"green",0,function(a){this._filterUpToDate=!1;if(a>255)return 255;else if(a<0)return 0;else return Math.round(a)});a.Factory.addGetterSetter(b.Node,"blue",0,c.RGBComponent,a.Factory.afterSetFilter);a.Factory.addGetterSetter(b.Node,"alpha",1,function(a){this._filterUpToDate=!1;if(a>1)return 1;else if(a<0)return 0;else return a})}var Vd=!1;function Wd(){Vd||(Vd=!0,Ud());return Td.exports}var Xd={},Yd={exports:Xd};function Zd(){Object.defineProperty(Xd,"__esModule",{value:!0});Xd.Sepia=void 0;var a=function(a){a=a.data;var b=a.length,c,d,e,f;for(c=0;c127&&(h=255-h);i>127&&(i=255-i);j>127&&(j=255-j);b[g]=h;b[g+1]=i;b[g+2]=j}while(--f)}while(--a)};be.Solarize=a}var ee=!1;function fe(){ee||(ee=!0,de());return ce.exports}var ge={},he={exports:ge};function ie(){Object.defineProperty(ge,"__esModule",{value:!0});ge.Threshold=void 0;var a=z(),b=R(),c=u(),d=function(a){var b=this.threshold()*255;a=a.data;var c=a.length,d;for(d=0;d-----
three.r137",[],(function $module_three_r137(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.THREE = {})); })(this, (function (exports) { "use strict"; const REVISION = "137"; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const SmoothShading = 2; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = "100"; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = []; for (let i = 0; i < 256; i++) { _lut[i] = (i < 16 ? "0" : "") + i.toString(16); } let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + "-" + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + "-" + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + "-" + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + "-" + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range to range function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm _seed = _seed * 16807 % 2147483647; return (_seed - 1) / 2147483646; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case "XYX": q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case "YZY": q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case "ZXZ": q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case "XZX": q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case "YXY": q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case "ZYZ": q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order); } } var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler }); class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } Vector2.prototype.isVector2 = true; class Matrix3 { constructor() { this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } scale(sx, sy) { const te = this.elements; te[0] *= sx; te[3] *= sx; te[6] *= sx; te[1] *= sy; te[4] *= sy; te[7] *= sy; return this; } rotate(theta) { const c = Math.cos(theta); const s = Math.sin(theta); const te = this.elements; const a11 = te[0], a12 = te[3], a13 = te[6]; const a21 = te[1], a22 = te[4], a23 = te[7]; te[0] = c * a11 + s * a21; te[3] = c * a12 + s * a22; te[6] = c * a13 + s * a23; te[1] = -s * a11 + c * a21; te[4] = -s * a12 + c * a22; te[7] = -s * a13 + c * a23; return this; } translate(tx, ty) { const te = this.elements; te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } Matrix3.prototype.isMatrix3 = true; function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] > 65535) return true; } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS("http://www.w3.org/1999/xhtml", name); } const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } class Color { constructor(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string return this.set(r); } return this.setRGB(r, g, b); } set(value) { if (value && value.isColor) { this.copy(value); } else if (typeof value === "number") { this.setHex(value); } else if (typeof value === "string") { this.setStyle(value); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; return this; } setRGB(r, g, b) { this.r = r; this.g = g; this.b = b; return this; } setHSL(h, s, l) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } return this; } setStyle(style) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn("THREE.Color: Alpha component of " + style + " will be ignored."); } } let m; if (m = /^((?:rgb|hsl)a?)(([^)]*))/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case "rgb": case "rgba": if (color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min(255, parseInt(color[1], 10)) / 255; this.g = Math.min(255, parseInt(color[2], 10)) / 255; this.b = Math.min(255, parseInt(color[3], 10)) / 255; handleAlpha(color[4]); return this; } if (color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min(100, parseInt(color[1], 10)) / 100; this.g = Math.min(100, parseInt(color[2], 10)) / 100; this.b = Math.min(100, parseInt(color[3], 10)) / 100; handleAlpha(color[4]); return this; } break; case "hsl": case "hsla": if (color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat(color[1]) / 360; const s = parseInt(color[2], 10) / 100; const l = parseInt(color[3], 10) / 100; handleAlpha(color[4]); return this.setHSL(h, s, l); } break; } } else if (m = /^#([A-Fa-fd]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; return this; } else if (size === 6) { // #ff0000 this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; return this; } } if (style && style.length > 0) { return this.setColorName(style); } return this; } setColorName(style) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex); } else { // unknown color console.warn("THREE.Color: Unknown color " + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex() { return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; } getHexString() { return ("000000" + this.getHex().toString(16)).slice(-6); } getHSL(target) { // h,s,l ranges are in 0.0 - 1.0 const r = this.r, g = this.g, b = this.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getStyle() { return "rgb(" + (this.r * 255 | 0) + "," + (this.g * 255 | 0) + "," + (this.b * 255 | 0) + ")"; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); if (attribute.normalized === true) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } } Color.NAMES = _colorKeywords; Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement == "undefined") { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS("canvas"); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext("2d"); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image); return canvas.toDataURL("image/jpeg", 0.6); } else { return canvas.toDataURL("image/png"); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const canvas = createElementNS("canvas"); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height }; } else { console.warn("THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."); return image; } } } let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding) { super(); Object.defineProperty(this, "id", { value: textureId++ }); this.uuid = generateUUID(); this.name = ""; this.image = image; this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === "string"; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { 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 (this.image !== undefined) { // TODO: Move to THREE.Image const image = this.image; if (image.uuid === undefined) { image.uuid = generateUUID(); // UGH } if (!isRootObject && meta.images[image.uuid] === undefined) { let url; if (Array.isArray(image)) { // process array of images e.g. CubeTexture url = []; for (let i = 0, l = image.length; i < l; i++) { // check cube texture with data textures if (image[i].isDataTexture) { url.push(serializeImage(image[i].image)); } else { url.push(serializeImage(image[i])); } } } else { // process single image url = serializeImage(image); } meta.images[image.uuid] = { uuid: image.uuid, url: url }; } output.image = image.uuid; } if (JSON.stringify(this.userData) !== "{}") output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: "dispose" }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) this.version++; } } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype.isTexture = true; function serializeImage(image) { if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.prototype.slice.call(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn("THREE.Texture: Unable to serialize Texture."); return {}; } } } class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } Vector4.prototype.isVector4 = true; /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width, height, options = {}) { super(); this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); this.texture = new Texture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.image = { width: width, height: height, depth: 1 }; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } setTexture(texture) { texture.image = { width: this.width, height: this.height, depth: this.depth }; this.texture = texture; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); // ensure image object is not shared, see #20328 this.texture.image = Object.assign({}, source.texture.image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } WebGLRenderTarget.prototype.isWebGLRenderTarget = true; class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width, height, count) { super(width, height); const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.set(0, 0, this.width, this.height); this.scissor.set(0, 0, this.width, this.height); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); } return this; } } WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; class WebGLMultisampleRenderTarget extends WebGLRenderTarget { constructor(width, height, options = {}) { super(width, height, options); this.samples = 4; this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true; this.useRenderToTexture = options.useRenderToTexture !== undefined ? options.useRenderToTexture : false; this.useRenderbuffer = this.useRenderToTexture === false; } copy(source) { super.copy.call(this, source); this.samples = source.samples; this.useRenderToTexture = source.useRenderToTexture; this.useRenderbuffer = source.useRenderbuffer; return this; } } WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this._x = x; this._y = y; this._z = z; this._w = w; } static slerp(qa, qb, qm, t) { console.warn("THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead."); return qm.slerpQuaternions(qa, qb, t); } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { if (!(euler && euler.isEuler)) { throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q, p) { if (p !== undefined) { console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."); return this.multiplyQuaternions(q, p); } return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Quaternion.prototype.isQuaternion = true; class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error("index is out of range: " + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error("index is out of range: " + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."); return this.addVectors(v, w); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."); return this.subVectors(v, w); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."); return this.multiplyVectors(v, w); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."); } return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v, w) { if (w !== undefined) { console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."); return this.crossVectors(v, w); } return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$c.copy(this).projectOnVector(planeNormal); return this.sub(_vector$c); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$c.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index, offset) { if (offset !== undefined) { console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); } this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } Vector3.prototype.isVector3 = true; const _vector$c = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromBufferAttribute(attribute) { let minX = +Infinity; let minY = +Infinity; let minZ = +Infinity; let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity; for (let i = 0, l = attribute.count; i < l; i++) { const x = attribute.getX(i); const y = attribute.getY(i); const z = attribute.getZ(i); if (x < minX) minX = x; if (y < minY) minY = y; if (z < minZ) minZ = z; if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } this.min.set(minX, minY, minZ); this.max.set(maxX, maxY, maxZ); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$b.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix(false, false); const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$b.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$b); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$b); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$3.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$3, _v1$7); _f2.subVectors(_v0$2, _v2$3); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$3, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$b.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } getBoundingSphere(target) { this.getCenter(target.center); target.radius = this.getSize(_vector$b).length() * 0.5; return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box3.prototype.isBox3 = true; const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$b = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _toFarthestPoint = /*@__PURE__*/new Vector3(); const _toPoint = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$a = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.direction).multiplyScalar(t).add(this.origin); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$a)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$a.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); return _vector$a.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); } return sqrDist; } intersectSphere(sphere, target) { _vector$a.subVectors(sphere.center, this.origin); const tca = _vector$a.dot(this.direction); const d2 = _vector$a.dot(_vector$a) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if (tymin > tmin || tmin !== tmin) tmin = tymin; if (tymax < tmax || tmax !== tmax) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$a) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor() { this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (arguments.length > 0) { console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead."); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { if (!(euler && euler.isEuler)) { console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === "XYZ") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === "YXZ") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === "ZXY") { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === "ZYX") { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === "YZX") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === "XZY") { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m, n) { if (n !== undefined) { console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."); return this.multiplyMatrices(m, n); } return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far) { if (far === undefined) { console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); } const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); const c = -(far + near) / (far - near); const d = -2 * far * near / (far - near); te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; const z = (far + near) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } Matrix4.prototype.isMatrix4 = true; const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix$1 = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) { this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case "XYZ": this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case "YXZ": this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case "ZXY": this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case "ZYX": this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case "YZX": this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case "XZY": this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix$1.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix$1, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } toVector3(optionalResult) { if (optionalResult) { return optionalResult.set(this._x, this._y, this._z); } else { return new Vector3(this._x, this._y, this._z); } } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} } Euler.prototype.isEuler = true; Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"]; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender() {} onAfterRender() {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error("THREE.Object3D.add: object can"t be added as a child of itself.", object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty("id", id); } getObjectByName(name) { return this.getObjectByProperty("name", name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast() {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === "string"; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== "") object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (JSON.stringify(this.userData) !== "{}") object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DefaultUp = new Vector3(0, 1, 0); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.set(0, 0); target.addScaledVector(uv1, _v3$1.x); target.addScaledVector(uv2, _v3$1.y); target.addScaledVector(uv3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: materialId++ }); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild() {} onBeforeRender() {} onBeforeCompile() {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn("THREE.Material: "" + key + "" parameter is undefined."); continue; } // for backward compatability if shading is set in the constructor if (key === "shading") { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = newValue === FlatShading ? true : false; continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn("THREE." + this.type + ": "" + key + "" is not a property of this material."); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRoot = meta === undefined || typeof meta === "string"; if (isRoot) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== "round") data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== "round") data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (JSON.stringify(this.userData) !== "{}") data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRoot) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.fog = source.fog; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } set needsUpdate(value) { if (value === true) this.version++; } } Material.prototype.isMaterial = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * } */ class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.type = "MeshBasicMaterial"; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshBasicMaterial.prototype.isMeshBasicMaterial = true; const _vector$9 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized) { if (Array.isArray(array)) { throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } copyColorsArray(colors) { const array = this.array; let offset = 0; for (let i = 0, l = colors.length; i < l; i++) { let color = colors[i]; if (color === undefined) { console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined", i); color = new Color(); } array[offset++] = color.r; array[offset++] = color.g; array[offset++] = color.b; } return this; } copyVector2sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i); vector = new Vector2(); } array[offset++] = vector.x; array[offset++] = vector.y; } return this; } copyVector3sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i); vector = new Vector3(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; } return this; } copyVector4sArray(vectors) { const array = this.array; let offset = 0; for (let i = 0, l = vectors.length; i < l; i++) { let vector = vectors[i]; if (vector === undefined) { console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i); vector = new Vector4(); } array[offset++] = vector.x; array[offset++] = vector.y; array[offset++] = vector.z; array[offset++] = vector.w; } return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.fromBufferAttribute(this, i); _vector$9.applyMatrix3(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyMatrix4(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.applyNormalMatrix(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$9.x = this.getX(i); _vector$9.y = this.getY(i); _vector$9.z = this.getZ(i); _vector$9.transformDirection(m); this.setXYZ(i, _vector$9.x, _vector$9.y, _vector$9.z); } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } getX(index) { return this.array[index * this.itemSize]; } setX(index, x) { this.array[index * this.itemSize] = x; return this; } getY(index) { return this.array[index * this.itemSize + 1]; } setY(index, y) { this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { return this.array[index * this.itemSize + 2]; } setZ(index, z) { this.array[index * this.itemSize + 2] = z; return this; } getW(index) { return this.array[index * this.itemSize + 3]; } setW(index, w) { this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call(this.array), normalized: this.normalized }; if (this.name !== "") data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } BufferAttribute.prototype.isBufferAttribute = true; // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } // let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$8 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, "id", { value: _id$1++ }); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute("position", new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$8); _vector$8.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$8); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error("THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error("THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$8.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$8); _vector$8.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$8); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$8.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$8.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$8.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error("THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error("THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)"); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (attributes.tangent === undefined) { this.setAttribute("tangent", new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = attributes.tangent.array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute("position"); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute("normal"); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute("normal", normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge(geometry, offset) { if (!(geometry && geometry.isBufferGeometry)) { console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry); return; } if (offset === undefined) { offset = 0; console.warn("THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."); } const attributes = this.attributes; for (const key in attributes) { if (geometry.attributes[key] === undefined) continue; const attribute1 = attributes[key]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[key]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); for (let i = 0, j = attributeOffset; i < length; i++, j++) { attributeArray1[j] = attributeArray2[i]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$8.fromBufferAttribute(normals, i); _vector$8.normalize(); normals.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn("THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== "") data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if (source.parameters !== undefined) this.parameters = Object.assign({}, source.parameters); return this; } dispose() { this.dispatchEvent({ type: "dispose" }); } } BufferGeometry.prototype.isBufferGeometry = true; const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _tempB = /*@__PURE__*/new Vector3(); const _tempC = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _morphB = /*@__PURE__*/new Vector3(); const _morphC = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$3.copy(geometry.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // Check boundingBox before continuing if (geometry.boundingBox !== null) { if (_ray$2.intersectsBox(geometry.boundingBox) === false) return; } let intersection; if (geometry.isBufferGeometry) { const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } else if (geometry.isGeometry) { console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } } Mesh.prototype.isMesh = true; function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { _vA$1.fromBufferAttribute(position, a); _vB$1.fromBufferAttribute(position, b); _vC$1.fromBufferAttribute(position, c); const morphInfluences = object.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, a); _tempB.fromBufferAttribute(morphAttribute, b); _tempC.fromBufferAttribute(morphAttribute, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA$1), influence); _morphB.addScaledVector(_tempB.sub(_vB$1), influence); _morphC.addScaledVector(_tempC.sub(_vC$1), influence); } } _vA$1.add(_morphA); _vB$1.add(_morphB); _vC$1.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA$1); object.boneTransform(b, _vB$1); object.boneTransform(c, _vC$1); } const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv2) { _uvA$1.fromBufferAttribute(uv2, a); _uvB$1.fromBufferAttribute(uv2, b); _uvC$1.fromBufferAttribute(uv2, c); intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { dst[u][p] = property.clone(); } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; /** * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: * } */ class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [1, 1, 1], "uv": [0, 0], "uv2": [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { if (parameters.attributes !== undefined) { console.error("THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead."); } this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: "t", value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: "c", value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: "v2", value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: "v3", value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: "v4", value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: "m3", value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: "m4", value: value.toArray() }; } else { data.uniforms[name] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } ShaderMaterial.prototype.isShaderMaterial = true; class Camera extends Object3D { constructor() { super(); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } Camera.prototype.isCamera = true; class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = "CubeCamera"; if (renderTarget.isWebGLCubeRenderTarget !== true) { console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; cameraPX.up.set(0, -1, 0); cameraPX.lookAt(new Vector3(1, 0, 0)); this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; cameraNX.up.set(0, -1, 0); cameraNX.lookAt(new Vector3(-1, 0, 0)); this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; cameraPY.up.set(0, 0, 1); cameraPY.lookAt(new Vector3(0, 1, 0)); this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; cameraNY.up.set(0, 0, -1); cameraNY.lookAt(new Vector3(0, -1, 0)); this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(new Vector3(0, 0, 1)); this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(new Vector3(0, 0, -1)); this.add(cameraNZ); } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size, options, dummy) { if (Number.isInteger(options)) { console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"); options = dummy; } super(size, size, options); options = options || {}; // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture = new CubeTexture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */ ` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */ ` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: "CubemapFromEquirect", uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(direction).multiplyScalar(t).add(line.start); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/new Sphere(); const _vector$7 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); return this; } intersectsObject(object) { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSprite(sprite) { _sphere$2.center.set(0, 0, 0); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$2); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$7) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type = gl.FLOAT; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Float64Array) { console.warn("THREE.WebGLAttributes: Unsupported data buffer format: Float64Array."); } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { console.warn("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2."); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_maxMipLevel 8.0 #define cubeUV_minMipLevel 4.0 #define cubeUV_maxTileSize 256.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); float texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize ); vec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; if ( mipInt < cubeUV_maxMipLevel ) { uv.y += 2.0 * cubeUV_maxTileSize; } uv.y += filterInt * 2.0 * cubeUV_minTileSize; uv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize ); uv *= texelSize; return texture2D( envMap, uv ).rgb; } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) #ifdef ENVMAP_MODE_REFRACTION uniform float refractionRatio; #endif vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec; #ifdef ENVMAP_MODE_REFLECTION reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); #else reflectVec = refract( - viewDir, normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); vec3 FssEss = specularColor * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1, 2 ) * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform vec2 morphTargetsTextureSize; vec3 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset, const in int stride ) { float texelIndex = float( vertexIndex * stride + offset ); float y = floor( texelIndex / morphTargetsTextureSize.x ); float x = texelIndex - y * morphTargetsTextureSize.x; vec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex ); return texture( morphTargetsTexture, morphUV ).xyz; } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #ifndef USE_MORPHNORMALS if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 1 ) * morphTargetInfluences[ i ]; #else if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 2 ) * morphTargetInfluences[ i ]; #endif } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef TEXTURE_LOD_EXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; var uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 } }, 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 Vector2(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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color(0xffffff) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { diffuse: { value: new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } }; const ShaderLib = { basic: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, specular: { value: new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: new Matrix3() }, t2D: { value: null } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: mergeUniforms([UniformsLib.envmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2(1, 1) }, clearcoatNormalMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: new Color(0x000000) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: new Color(0x000000) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: new Color(1, 1, 1) }, specularColorMap: { value: null } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; function WebGLBackground(renderer, cubemaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { background = cubemaps.get(background); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if (session && session.environmentBlendMode === "additive") { background = null; } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: "BackgroundCubeMaterial", uniforms: cloneUniforms(ShaderLib.cube.uniforms), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute("normal"); boxMesh.geometry.deleteAttribute("uv"); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // enable code injection for non-built-in material Object.defineProperty(boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: "BackgroundMaterial", uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute("normal"); // enable code injection for non-built-in material Object.defineProperty(planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get("OES_vertex_array_object"); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(geometry, index); if (updateBuffers) saveCache(geometry, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (object.isInstancedMesh === true) { updateBuffers = true; } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers) { setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(geometry, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; for (const key in geometryAttributes) { const cachedAttribute = cachedAttributes[key]; const geometryAttribute = geometryAttributes[key]; if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(geometry, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; for (const key in attributes) { const attribute = attributes[key]; const data = {}; data.attribute = attribute; if (attribute.data) { data.data = attribute.data; } cache[key] = data; attributesNum++; } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get("ANGLE_instanced_arrays"); extension[capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE"](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset) { if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get("ANGLE_instanced_arrays") === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === "instanceMatrix" && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === "instanceColor" && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data && data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatilibity function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawArraysInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === "highp") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return "highp"; } precision = "mediump"; } if (precision === "mediump") { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return "mediump"; } } return "lowp"; } const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext; let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn("THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead."); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has("WEBGL_draw_buffers"); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has("OES_texture_float"); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping, camera) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes(planes, camera, 0); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there"s no local clipping if (renderingShadows) { // there"s no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } OrthographicCamera.prototype.isOrthographicCamera = true; class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.type = "RawShaderMaterial"; } } RawShaderMaterial.prototype.isRawShaderMaterial = true; const LOD_MIN = 4; const LOD_MAX = 8; const SIZE_MAX = Math.pow(2, LOD_MAX); // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/_createPlanes(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._blurMaterial = _getBlurShader(MAX_SAMPLES); this._equirectShader = null; this._cubemapShader = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = this._allocateTargets(); this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); this._compileMaterial(this._cubemapShader); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); this._compileMaterial(this._equirectShader); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); if (this._cubemapShader !== null) this._cubemapShader.dispose(); if (this._equirectShader !== null) this._equirectShader.dispose(); for (let i = 0; i < _lodPlanes.length; i++) { _lodPlanes[i].dispose(); } } // private interface _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(texture); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets(texture) { // warning: null texture is valid const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(params); cubeUVRenderTarget.depthBuffer = texture ? false : true; if (this._pingPongRenderTarget === null) { this._pingPongRenderTarget = _createRenderTarget(params); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(_lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } _setViewport(cubeUVRenderTarget, col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapShader === null) { this._cubemapShader = _getCubemapShader(); } this._cubemapShader.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectShader === null) { this._equirectShader = _getEquirectShader(); } } const material = isCubeTexture ? this._cubemapShader : this._equirectShader; const mesh = new Mesh(_lodPlanes[0], material); const uniforms = material.uniforms; uniforms["envMap"].value = texture; if (!isCubeTexture) { uniforms["texelSize"].value.set(1.0 / texture.image.width, 1.0 / texture.image.height); } _setViewport(cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < TOTAL_LODS; i++) { const sigma = Math.sqrt(_sigmas[i] * _sigmas[i] - _sigmas[i - 1] * _sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== "latitudinal" && direction !== "longitudinal") { console.error("blur direction must be either latitudinal or longitudinal!"); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(_lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = _sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms["envMap"].value = targetIn.texture; blurUniforms["samples"].value = samples; blurUniforms["weights"].value = weights; blurUniforms["latitudinal"].value = direction === "latitudinal"; if (poleAxis) { blurUniforms["poleAxis"].value = poleAxis; } blurUniforms["dTheta"].value = radiansPerPixel; blurUniforms["mipInt"].value = LOD_MAX - lodIn; const outputSize = _sizeLods[lodOut]; const x = 3 * Math.max(0, SIZE_MAX - 2 * outputSize); const y = (lodOut === 0 ? 0 : 2 * SIZE_MAX) + 2 * outputSize * (lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes() { const _lodPlanes = []; const _sizeLods = []; const _sigmas = []; let lod = LOD_MAX; for (let i = 0; i < TOTAL_LODS; i++) { const sizeLod = Math.pow(2, lod); _sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > LOD_MAX - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - LOD_MAX + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } _sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 1); const min = -texelSize / 2; const max = 1 + texelSize / 2; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute("position", new BufferAttribute(position, positionSize)); planes.setAttribute("uv", new BufferAttribute(uv, uvSize)); planes.setAttribute("faceIndex", new BufferAttribute(faceIndex, faceIndexSize)); _lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { _lodPlanes, _sizeLods, _sigmas }; } function _createRenderTarget(params) { const cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(maxSamples) { const weights = new Float32Array(maxSamples); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new RawShaderMaterial({ name: "SphericalGaussianBlur", defines: { "n": maxSamples }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectShader() { const texelSize = new Vector2(1, 1); const shaderMaterial = new RawShaderMaterial({ name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null }, "texelSize": { value: texelSize } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; #include void main() { gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); vec2 f = fract( uv / texelSize - 0.5 ); uv -= f * texelSize; vec3 tl = texture2D ( envMap, uv ).rgb; uv.x += texelSize.x; vec3 tr = texture2D ( envMap, uv ).rgb; uv.y += texelSize.y; vec3 br = texture2D ( envMap, uv ).rgb; uv.x -= texelSize.x; vec3 bl = texture2D ( envMap, uv ).rgb; vec3 tm = mix( tl, tr, f.x ); vec3 bm = mix( bl, br, f.x ); gl_FragColor.rgb = mix( tm, bm, f.y ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCubemapShader() { const shaderMaterial = new RawShaderMaterial({ name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */ ` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getCommonVertexShader() { return ( /* glsl */ ` precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener("dispose", onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case "WEBGL_depth_texture": extension = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture"); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension("WEBGL_compressed_texture_s3tc") || gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function (capabilities) { if (capabilities.isWebGL2) { getExtension("EXT_color_buffer_float"); } else { getExtension("WEBGL_depth_texture"); getExtension("OES_texture_float"); getExtension("OES_texture_half_float"); getExtension("OES_texture_half_float_linear"); getExtension("OES_standard_derivatives"); getExtension("OES_element_index_uint"); getExtension("OES_vertex_array_object"); getExtension("ANGLE_instanced_arrays"); } getExtension("OES_texture_float_linear"); getExtension("EXT_color_buffer_half_float"); getExtension("WEBGL_multisampled_render_to_texture"); }, get: function (name) { const extension = getExtension(name); if (extension === null) { console.warn("THREE.WebGLRenderer: " + name + " extension not supported."); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } geometry.removeEventListener("dispose", onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener("dispose", onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get("ANGLE_instanced_arrays"); methodName = "drawElementsInstancedANGLE"; if (extension === null) { console.error("THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error("THREE.WebGLInfo: Unknown draw mode:", mode); break; } } function reset() { render.frame++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } class DataTexture2DArray extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture2DArray.prototype.isDataTexture2DArray = true; function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function denormalize(morph, attribute) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if (array instanceof Int8Array) denominator = 127;else if (array instanceof Int16Array) denominator = 32767;else if (array instanceof Int32Array) denominator = 2147483647;else console.error("THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array); morph.divideScalar(denominator); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector3(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, material, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const numberOfMorphTargets = geometry.morphAttributes.position.length; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== numberOfMorphTargets) { if (entry !== undefined) entry.texture.dispose(); const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal || []; const numberOfVertices = geometry.attributes.position.count; const numberOfVertexData = hasMorphNormals === true ? 2 : 1; // (v,n) vs. (v) let width = numberOfVertices * numberOfVertexData; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * numberOfMorphTargets); const texture = new DataTexture2DArray(buffer, width, height, numberOfMorphTargets); texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = numberOfVertexData * 4; for (let i = 0; i < numberOfMorphTargets; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { morph.fromBufferAttribute(morphTarget, j); if (morphTarget.normalized === true) denormalize(morph, morphTarget); const stride = j * vertexDataStride; buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); if (morphNormal.normalized === true) denormalize(morph, morphNormal); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } } } entry = { count: numberOfMorphTargets, texture: texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener("dispose", disposeTexture); } geometry.addEventListener("dispose", disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", objectInfluences); program.getUniforms().setValue(gl, "morphTargetsTexture", entry.texture, textures); program.getUniforms().setValue(gl, "morphTargetsTextureSize", entry.size); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute("morphTarget" + i) !== morphTargets[index]) { geometry.setAttribute("morphTarget" + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute("morphNormal" + i) !== morphNormals[index]) { geometry.setAttribute("morphNormal" + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute("morphTarget" + i) === true) { geometry.deleteAttribute("morphTarget" + i); } if (morphNormals && geometry.hasAttribute("morphNormal" + i) === true) { geometry.deleteAttribute("morphNormal" + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, "morphTargetBaseInfluence", morphBaseInfluence); program.getUniforms().setValue(gl, "morphTargetInfluences", morphInfluences); } } return { update: update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener("dispose", onInstancedMeshDispose) === false) { object.addEventListener("dispose", onInstancedMeshDispose); } attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener("dispose", onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose }; } class DataTexture3D extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture3D.prototype.isDataTexture3D = true; /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = new Texture(); const emptyTexture2dArray = new DataTexture2DArray(); const emptyTexture3d = new DataTexture3D(); const emptyCubeTexture = new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array) function setValueV2i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } function setValueV3i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } function setValueV4i(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } function setValueV3ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } function setValueV4ui(gl, v) { const cache = this.cache; if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || emptyTexture3d, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.safeSetTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyTexture2dArray, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || emptyTexture3d, units[i]); } } function setValueT6Array(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.safeSetTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const n = v.length; const units = allocTexUnits(textures, n); gl.uniform1iv(this.addr, units); for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyTexture2dArray, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- function SingleUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function (data) { const cache = this.cache; if (data instanceof Float32Array && cache.length !== data.length) { this.cache = new Float32Array(data.length); } copyArray(cache, data); }; function StructuredUniform(id) { this.id = id; this.seq = []; this.map = {}; } StructuredUniform.prototype.setValue = function (gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === "]", subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container function WebGLUniforms(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); }; WebGLUniforms.prototype.setOptional = function (gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); }; // Static interface WebGLUniforms.upload = function (gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } }; WebGLUniforms.seqWithValue = function (seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; }; function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function addLineNumbers(string) { const lines = string.split(" "); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join(" "); } function getEncodingComponents(encoding) { switch (encoding) { case LinearEncoding: return ["Linear", "( value )"]; case sRGBEncoding: return ["sRGB", "( value )"]; default: console.warn("THREE.WebGLProgram: Unsupported encoding:", encoding); return ["Linear", "( value )"]; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === "") return ""; // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return type.toUpperCase() + " " + errors + " " + addLineNumbers(gl.getShaderSource(shader)); } function getTexelEncodingFunction(functionName, encoding) { const components = getEncodingComponents(encoding); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[0] + components[1] + "; }"; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn("THREE.WebGLProgram: Unsupported toneMapping:", toneMapping); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ? "#extension GL_OES_standard_derivatives : enable" : "", (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? "#extension GL_EXT_draw_buffers : require" : "", (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : ""]; return chunks.filter(filterEmptyLine).join(" "); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push("#define " + name + " " + value); } return chunks.join(" "); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ""; } function replaceLightNums(string, parameters) { return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } function includeReplacer(match, include) { const string = ShaderChunk[include]; if (string === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(string); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0) { prefixVertex += " "; } prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join(" "); if (prefixFragment.length > 0) { prefixFragment += " "; } } else { prefixVertex = [generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define MAX_BONES " + parameters.maxBones, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_TEXTURE" : "", parameters.morphTargets && parameters.isWebGL2 ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " "].filter(filterEmptyLine).join(" "); prefixFragment = [customExtensions, generatePrecision(parameters), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? "#define USE_FOG" : "", parameters.useFog && parameters.fogExp2 ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.normalMap && parameters.objectSpaceNormalMap ? "#define OBJECTSPACE_NORMALMAP" : "", parameters.normalMap && parameters.tangentSpaceNormalMap ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", parameters.toneMapping !== NoToneMapping ? "#define TONE_MAPPING" : "", parameters.toneMapping !== NoToneMapping ? ShaderChunk["tonemapping_pars_fragment"] : "", // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction("toneMapping", parameters.toneMapping) : "", parameters.dithering ? "#define DITHERING" : "", parameters.transparent ? "" : "#define OPAQUE", ShaderChunk["encodings_pars_fragment"], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction("linearToOutputTexel", parameters.outputEncoding), parameters.depthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " "].filter(filterEmptyLine).join(" "); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = ["precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture"].join(" ") + " " + prefixVertex; prefixFragment = ["#define varying in", parameters.glslVersion === GLSL3 ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", parameters.glslVersion === GLSL3 ? "" : "#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(" ") + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, "position"); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; const vertexErrors = getShaderErrors(gl, glVertexShader, "vertex"); const fragmentErrors = getShaderErrors(gl, glFragmentShader, "fragment"); console.error("THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors); } else if (programLog !== "") { console.warn("THREE.WebGLProgram: Program Info Log:", programLog); } else if (vertexLog === "" || fragmentLog === "") { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; if (cache.has(material) === false) { cache.set(material, new Set()); } return cache.get(material); } _getShaderStage(code) { const cache = this.shaderCache; if (cache.has(code) === false) { const stage = new WebGLShaderStage(); cache.set(code, stage); } return cache.get(code); } } class WebGLShaderStage { constructor() { this.id = _id++; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const floatVertexTextures = capabilities.floatVertexTextures; const maxVertexUniforms = capabilities.maxVertexUniforms; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getMaxBones(object) { const skeleton = object.skeleton; const bones = skeleton.bones; if (floatVertexTextures) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) const nVertexUniforms = maxVertexUniforms; const nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); const maxBones = Math.min(nVertexMatrices, bones.length); if (maxBones < bones.length) { console.warn("THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "."); return 0; } return maxBones; } } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) const maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn("THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead."); } } let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const parameters = { isWebGL2: isWebGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: currentRenderTarget === null ? renderer.outputEncoding : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding, map: !!material.map, matcap: !!material.matcap, envMap: !!envMap, envMapMode: envMap && envMap.mapping, envMapCubeUV: !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping), lightMap: !!material.lightMap, aoMap: !!material.aoMap, emissiveMap: !!material.emissiveMap, bumpMap: !!material.bumpMap, normalMap: !!material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !!material.map && material.map.isVideoTexture === true && material.map.encoding === sRGBEncoding, clearcoat: useClearcoat, clearcoatMap: useClearcoat && !!material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !!material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !!material.clearcoatNormalMap, displacementMap: !!material.displacementMap, roughnessMap: !!material.roughnessMap, metalnessMap: !!material.metalnessMap, specularMap: !!material.specularMap, specularIntensityMap: !!material.specularIntensityMap, specularColorMap: !!material.specularColorMap, transparent: material.transparent, alphaMap: !!material.alphaMap, alphaTest: useAlphaTest, gradientMap: !!material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !!material.sheenColorMap, sheenRoughnessMap: !!material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !!material.transmissionMap, thicknessMap: !!material.thicknessMap, combine: material.combine, vertexTangents: !!material.normalMap && !!object.geometry && !!object.geometry.attributes.tangent, vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!object.geometry && !!object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || !!material.sheenColorMap || !!material.sheenRoughnessMap, uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || material.transmission > 0 || !!material.transmissionMap || !!material.thicknessMap || !!material.specularIntensityMap || !!material.specularColorMap || material.sheen > 0 || !!material.sheenColorMap || !!material.sheenRoughnessMap) && !!material.displacementMap, fog: !!fog, useFog: material.fog, fogExp2: fog && fog.isFogExp2, flatShading: !!material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true && maxBones > 0, maxBones: maxBones, useVertexTexture: floatVertexTextures, morphTargets: !!object.geometry && !!object.geometry.morphAttributes.position, morphNormals: !!object.geometry && !!object.geometry.morphAttributes.normal, morphTargetsCount: !!object.geometry && !!object.geometry.morphAttributes.position ? object.geometry.morphAttributes.position.length : 0, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has("EXT_frag_depth"), rendererExtensionDrawBuffers: isWebGL2 || extensions.has("WEBGL_draw_buffers"), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has("EXT_shader_texture_lod"), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputEncoding); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputEncoding); array.push(parameters.envMapMode); array.push(parameters.combine); array.push(parameters.vertexUvs); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.maxBones); array.push(parameters.morphTargetsCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.map) _programLayers.enable(4); if (parameters.matcap) _programLayers.enable(5); if (parameters.envMap) _programLayers.enable(6); if (parameters.envMapCubeUV) _programLayers.enable(7); if (parameters.lightMap) _programLayers.enable(8); if (parameters.aoMap) _programLayers.enable(9); if (parameters.emissiveMap) _programLayers.enable(10); if (parameters.bumpMap) _programLayers.enable(11); if (parameters.normalMap) _programLayers.enable(12); if (parameters.objectSpaceNormalMap) _programLayers.enable(13); if (parameters.tangentSpaceNormalMap) _programLayers.enable(14); if (parameters.clearcoat) _programLayers.enable(15); if (parameters.clearcoatMap) _programLayers.enable(16); if (parameters.clearcoatRoughnessMap) _programLayers.enable(17); if (parameters.clearcoatNormalMap) _programLayers.enable(18); if (parameters.displacementMap) _programLayers.enable(19); if (parameters.specularMap) _programLayers.enable(20); if (parameters.roughnessMap) _programLayers.enable(21); if (parameters.metalnessMap) _programLayers.enable(22); if (parameters.gradientMap) _programLayers.enable(23); if (parameters.alphaMap) _programLayers.enable(24); if (parameters.alphaTest) _programLayers.enable(25); if (parameters.vertexColors) _programLayers.enable(26); if (parameters.vertexAlphas) _programLayers.enable(27); if (parameters.vertexUvs) _programLayers.enable(28); if (parameters.vertexTangents) _programLayers.enable(29); if (parameters.uvsVertexOnly) _programLayers.enable(30); if (parameters.fog) _programLayers.enable(31); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.useFog) _programLayers.enable(0); if (parameters.flatShading) _programLayers.enable(1); if (parameters.logarithmicDepthBuffer) _programLayers.enable(2); if (parameters.skinning) _programLayers.enable(3); if (parameters.useVertexTexture) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.premultipliedAlpha) _programLayers.enable(7); if (parameters.shadowMapEnabled) _programLayers.enable(8); if (parameters.physicallyCorrectLights) _programLayers.enable(9); if (parameters.doubleSided) _programLayers.enable(10); if (parameters.flipSided) _programLayers.enable(11); if (parameters.depthPacking) _programLayers.enable(12); if (parameters.dithering) _programLayers.enable(13); if (parameters.specularIntensityMap) _programLayers.enable(14); if (parameters.specularColorMap) _programLayers.enable(15); if (parameters.transmission) _programLayers.enable(16); if (parameters.transmissionMap) _programLayers.enable(17); if (parameters.thicknessMap) _programLayers.enable(18); if (parameters.sheen) _programLayers.enable(19); if (parameters.sheenColorMap) _programLayers.enable(20); if (parameters.sheenRoughnessMap) _programLayers.enable(21); if (parameters.decodeVideoTexture) _programLayers.enable(22); if (parameters.transparent) _programLayers.enable(23); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { let list; if (lists.has(scene) === false) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= lists.get(scene).length) { list = new WebGLRenderList(); lists.get(scene).push(list); } else { list = lists.get(scene)[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, physicallyCorrectLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort(shadowCastingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = physicallyCorrectLights !== true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; state.spotShadowMatrix[spotLength] = light.shadow.matrix; numSpotShadows++; } state.spot[spotLength] = uniforms; spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has("OES_texture_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has("OES_texture_half_float_linear") === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions."); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); uniforms.direction.normalize(); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(physicallyCorrectLights) { lights.setup(lightsArray, physicallyCorrectLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { let renderState; if (renderStates.has(scene) === false) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStates.get(scene).length) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.get(scene).push(renderState); } else { renderState = renderStates.get(scene)[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } /** * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } MeshDepthMaterial.prototype.isMeshDepthMaterial = true; /** * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.setValues(parameters); } copy(source) { super.copy(source); this.referencePosition.copy(source.referencePosition); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; const vertex = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn("THREE.WebGLShadowMap:", light, "has no shadow."); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.camera.updateProjectionMatrix(); } if (shadow.map === null) { const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; } if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { result.referencePosition.setFromMatrixPosition(light.matrixWorld); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { if (depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } } else { gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf("WebGL") !== -1) { version = parseFloat(/^WebGL (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf("OpenGL ES") !== -1) { version = parseFloat(/^OpenGL ES (d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get("WEBGL_draw_buffers").drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get("EXT_blend_minmax"); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error("THREE.WebGLState: Invalid blending: ", blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture) { if (currentTextureSlot === null) { activeTexture(); } let boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[currentTextureSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error("THREE.WebGLState:", error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, texImage3D: texImage3D, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const hasMultisampledRenderToTexture = extensions.has("WEBGL_multisampled_render_to_texture"); const MultisampledRenderToTextureExtension = hasMultisampledRenderToTexture ? extensions.get("WEBGL_multisampled_render_to_texture") : undefined; const _videoTextures = new WeakMap(); let _canvas; // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") !== null; } catch (err) {// Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? new OffscreenCanvas(width, height) : createElementNS("canvas"); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, width, height); console.warn("THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")."); return canvas; } else { if ("data" in image) { console.warn("THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")."); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, encoding, isVideoTexture = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = encoding === sRGBEncoding && isVideoTexture === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get("EXT_color_buffer_float"); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener("dispose", onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } info.memory.textures--; } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener("dispose", onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; _gl.deleteTexture(textureProperties.__webglTexture); properties.remove(texture); } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (!renderTarget) return; if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn("THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures); } textureUnits += 1; return textureUnit; } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === undefined) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined"); } else if (image.complete === false) { console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete"); } else { uploadTexture(textureProperties, texture, slot); return; } } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping."); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter."); } } if (extensions.has("EXT_texture_filter_anisotropic") === true) { const extension = extensions.get("EXT_texture_filter_anisotropic"); if (texture.type === FloatType && extensions.has("OES_texture_float_linear") === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has("OES_texture_half_float_linear") === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener("dispose", onTextureDispose); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataTexture2DArray) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isDataTexture3D) textureType = _gl.TEXTURE_3D; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(textureType, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error("WebGLRenderer: Floating point depth texture requires WebGL2."); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."); texture.type = UnsignedShortType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } else if (texture.isDataTexture2DArray) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isDataTexture3D) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; initTexture(textureProperties, texture); state.activeTexture(_gl.TEXTURE0 + slot); state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture && (texture.isCompressedTexture || texture.image[0].isCompressedTexture); const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.encoding), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = textureProperties.__version === undefined; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } textureProperties.__version = texture.version; if (texture.onUpdate) texture.onUpdate(texture); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || renderTarget.useRenderToTexture) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { // Use the first texture for MRT so far const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[0] : renderTarget.texture; const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && renderTarget.useRenderbuffer) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error("Depth Texture with cube render targets is not supported"); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture"); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (renderTarget.useRenderToTexture) { MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error("Unknown depthTexture format"); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error("target.depthTexture not supported in Cube render targets"); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener("dispose", onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); } } else if (renderTarget.useRenderbuffer) { if (isWebGL2) { renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); const glFormat = utils.convert(texture.format, texture.encoding); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.encoding); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer); _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (isRenderTarget3D) { // Render targets containing layers, i.e: Texture 3D and 2d arrays if (isWebGL2) { const isTexture3D = texture.isDataTexture3D; glTextureType = isTexture3D ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2."); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (renderTarget.useRenderbuffer) { if (isWebGL2) { const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = [_gl.COLOR_ATTACHMENT0]; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } if (!renderTarget.ignoreDepthForMultisampleCopy) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } const renderTargetProperties = properties.get(renderTarget); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); if (renderTarget.ignoreDepthForMultisampleCopy) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } else { console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); } } } function getRenderTargetSamples(renderTarget) { return isWebGL2 && (renderTarget.useRenderbuffer || renderTarget.useRenderToTexture) ? Math.min(maxSamples, renderTarget.samples) : 0; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat) return image; if (encoding !== LinearEncoding) { // sRGB if (encoding === sRGBEncoding) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has("EXT_sRGB") === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."); } } } else { console.error("THREE.WebGLTextures: Unsupported texture encoding:", encoding); } } return image; } // backwards compatibility let warnedTexture2D = false; let warnedTextureCube = false; function safeSetTexture2D(texture, slot) { if (texture && texture.isWebGLRenderTarget) { if (warnedTexture2D === false) { console.warn("THREE.WebGLTextures.safeSetTexture2D: don"t use render targets as textures. Use their .texture property instead."); warnedTexture2D = true; } texture = texture.texture; } setTexture2D(texture, slot); } function safeSetTextureCube(texture, slot) { if (texture && texture.isWebGLCubeRenderTarget) { if (warnedTextureCube === false) { console.warn("THREE.WebGLTextures.safeSetTextureCube: don"t use cube render targets as textures. Use their .texture property instead."); warnedTextureCube = true; } texture = texture.texture; } setTextureCube(texture, slot); } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.safeSetTexture2D = safeSetTexture2D; this.safeSetTextureCube = safeSetTextureCube; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, encoding = null) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get("OES_texture_half_float"); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; if (p === RedFormat) return gl.RED; if (p === RGBFormat) { console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228"); return gl.RGBA; } // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get("EXT_sRGB"); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (encoding === sRGBEncoding) { extension = extensions.get("WEBGL_compressed_texture_s3tc_srgb"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get("WEBGL_compressed_texture_s3tc"); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get("WEBGL_compressed_texture_pvrtc"); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get("WEBGL_compressed_texture_etc1"); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get("WEBGL_compressed_texture_etc"); if (extension !== null) { if (p === RGB_ETC2_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get("WEBGL_compressed_texture_astc"); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get("EXT_texture_compression_bptc"); if (extension !== null) { if (p === RGBA_BPTC_Format) return encoding === sRGBEncoding ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get("WEBGL_depth_texture"); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.cameras = array; } } ArrayCamera.prototype.isArrayCamera = true; class Group extends Object3D { constructor() { super(); this.type = "Group"; } } Group.prototype.isGroup = true; const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } disconnect(inputSource) { this.dispatchEvent({ type: "disconnected", data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== "visible-blurred") { if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); if (hand.joints[inputjoint.jointName] === undefined) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; // ?? hand.add(joint); } const joint = hand.joints[inputjoint.jointName]; if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints["index-finger-tip"]; const thumbTip = hand.joints["thumb-tip"]; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: "pinchend", handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: "pinchstart", handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat"); } if (type === undefined && format === DepthFormat) type = UnsignedShortType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } DepthTexture.prototype.isDepthTexture = true; class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; const hasMultisampledRenderToTexture = renderer.extensions.has("WEBGL_multisampled_render_to_texture"); let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let isMultisample = false; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const inputSourcesMap = new Map(); // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable(1); cameraVR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controller = inputSourcesMap.get(event.inputSource); if (controller) { controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { inputSourcesMap.forEach(function (controller, inputSource) { controller.disconnect(inputSource); }); inputSourcesMap.clear(); _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: "sessionend" }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting."); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn("THREE.WebXRManager: Cannot change reference space type while presenting."); } }; this.getReferenceSpace = function () { return referenceSpace; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener("select", onSessionEvent); session.addEventListener("selectstart", onSessionEvent); session.addEventListener("selectend", onSessionEvent); session.addEventListener("squeeze", onSessionEvent); session.addEventListener("squeezestart", onSessionEvent); session.addEventListener("squeezeend", onSessionEvent); session.addEventListener("end", onSessionEnd); session.addEventListener("inputsourceschange", onInputSourcesChange); if (attributes.xrCompatible !== true) { await gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding }); } else { isMultisample = attributes.antialias; let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } const projectionlayerInit = { colorFormat: renderer.outputEncoding === sRGBEncoding ? gl.SRGB8_ALPHA8 : gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); if (isMultisample) { newRenderTarget = new WebGLMultisampleRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, useRenderToTexture: hasMultisampledRenderToTexture, encoding: renderer.outputEncoding }); } else { newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, ignoreDepth: glProjLayer.ignoreDepthValues, encoding: renderer.outputEncoding }); } } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation(1.0); referenceSpace = await session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: "sessionstart" }); } }; function onInputSourcesChange(event) { const inputSources = session.inputSources; // Assign inputSources to available controllers for (let i = 0; i < controllers.length; i++) { inputSourcesMap.set(inputSources[i], controllers[i]); } // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "disconnected", data: inputSource }); inputSourcesMap.delete(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; const controller = inputSourcesMap.get(inputSource); if (controller) { controller.dispatchEvent({ type: "connected", data: inputSource }); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraVR.near, depthFar: cameraVR.far }); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera(cameraVR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } cameraVR.matrixWorld.decompose(cameraVR.position, cameraVR.quaternion, cameraVR.scale); // update user camera and its children camera.position.copy(cameraVR.position); camera.quaternion.copy(cameraVR.quaternion); camera.scale.copy(cameraVR.scale); camera.matrix.copy(cameraVR.matrix); camera.matrixWorld.copy(cameraVR.matrixWorld); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraVR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if (glProjLayer !== null) { return glProjLayer.fixedFoveation; } if (glBaseLayer !== null) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function (foveation) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if (glProjLayer !== null) { glProjLayer.fixedFoveation = foveation; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if (views.length !== cameraVR.cameras.length) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } const camera = cameras[i]; camera.matrix.fromArray(view.transform.matrix); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraVR.matrix.copy(camera.matrix); } if (cameraVRNeedsUpdate === true) { cameraVR.cameras.push(camera); } } } // const inputSources = session.inputSources; for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; const inputSource = inputSources[i]; controller.update(inputSource, frame, referenceSpace); } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(properties) { function refreshFogUniforms(uniforms, fog) { uniforms.fogColor.value.copy(fog.color); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsLambert(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } else { refreshUniformsStandard(uniforms, material); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDepth(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsNormal(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. specular intensity map // 14. specular tint map // 15. transmission map // 16. thickness map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.specularMap) { uvScaleMap = material.specularMap; } else if (material.displacementMap) { uvScaleMap = material.displacementMap; } else if (material.normalMap) { uvScaleMap = material.normalMap; } else if (material.bumpMap) { uvScaleMap = material.bumpMap; } else if (material.roughnessMap) { uvScaleMap = material.roughnessMap; } else if (material.metalnessMap) { uvScaleMap = material.metalnessMap; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } else if (material.emissiveMap) { uvScaleMap = material.emissiveMap; } else if (material.clearcoatMap) { uvScaleMap = material.clearcoatMap; } else if (material.clearcoatNormalMap) { uvScaleMap = material.clearcoatNormalMap; } else if (material.clearcoatRoughnessMap) { uvScaleMap = material.clearcoatRoughnessMap; } else if (material.specularIntensityMap) { uvScaleMap = material.specularIntensityMap; } else if (material.specularColorMap) { uvScaleMap = material.specularColorMap; } else if (material.transmissionMap) { uvScaleMap = material.transmissionMap; } else if (material.thicknessMap) { uvScaleMap = material.thicknessMap; } else if (material.sheenColorMap) { uvScaleMap = material.sheenColorMap; } else if (material.sheenRoughnessMap) { uvScaleMap = material.sheenRoughnessMap; } if (uvScaleMap !== undefined) { // backwards compatibility if (uvScaleMap.isWebGLRenderTarget) { uvScaleMap = uvScaleMap.texture; } if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if (material.aoMap) { uv2ScaleMap = material.aoMap; } else if (material.lightMap) { uv2ScaleMap = material.lightMap; } if (uv2ScaleMap !== undefined) { // backwards compatibility if (uv2ScaleMap.isWebGLRenderTarget) { uv2ScaleMap = uv2ScaleMap.texture; } if (uv2ScaleMap.matrixAutoUpdate === true) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if (material.map) { uvScaleMap = material.map; } else if (material.alphaMap) { uvScaleMap = material.alphaMap; } if (uvScaleMap !== undefined) { if (uvScaleMap.matrixAutoUpdate === true) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy(uvScaleMap.matrix); } } function refreshUniformsLambert(uniforms, material) { if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsStandard(uniforms, material) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; } if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { refreshUniformsStandard(uniforms, material); uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDepth(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance(uniforms, material) { if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy(material.referencePosition); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal(uniforms, material) { if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) uniforms.bumpScale.value *= -1; } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) uniforms.normalScale.value.negate(); } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS("canvas"); canvas.style.display = "block"; return canvas; } function WebGLRenderer(parameters = {}) { const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = _canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ("setAttribute" in _canvas) _canvas.setAttribute("data-engine", `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener("webglcontextlost", onContextLost, false); _canvas.addEventListener("webglcontextrestored", onContextRestore, false); if (_gl === null) { const contextNames = ["webgl2", "webgl", "experimental-webgl"]; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error("Error creating WebGL context with your selected attributes."); } else { throw new Error("Error creating WebGL context."); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch (error) { console.error("THREE.WebGLRenderer: " + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, state, objects, _alpha, _premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get("WEBGL_lose_context"); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle) { if (xr.isPresenting) { console.warn("THREE.WebGLRenderer: Can"t change size while VR device is presenting."); return; } _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if (updateStyle !== false) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color, depth, stencil) { let bits = 0; if (color === undefined || color) bits |= _gl.COLOR_BUFFER_BIT; if (depth === undefined || depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil === undefined || stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { _canvas.removeEventListener("webglcontextlost", onContextLost, false); _canvas.removeEventListener("webglcontextrestored", onContextRestore, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener("sessionstart", onXRSessionStart); xr.removeEventListener("sessionend", onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log("THREE.WebGLRenderer: Context Lost."); _isContextLost = true; } function /* event */ onContextRestore() { console.log("THREE.WebGLRenderer: Context Restored."); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onMaterialDispose(event) { const material = event.target; material.removeEventListener("dispose", onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; const position = geometry.attributes.position; // if (index === null) { if (position === undefined || position.count === 0) return; } else if (index.count === 0) { return; } // let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // const dataCount = index !== null ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max(rangeStart, groupStart); const drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; const drawCount = Math.max(0, drawEnd - drawStart + 1); if (drawCount === 0) return; // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.physicallyCorrectLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; getProgram(material2, scene, object); } } else { getProgram(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof window !== "undefined") animation.setContext(window); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener("sessionstart", onXRSessionStart); xr.addEventListener("sessionend", onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); return; } if (_isContextLost === true) return; // update scene graph if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.physicallyCorrectLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (object.isSkinnedMesh) { // update skeleton only once in a frame if (object.skeleton.frame !== info.render.frame) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if (!object.frustumCulled || _frustum.intersectsObject(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); } function renderTransmissionPass(opaqueObjects, scene, camera) { if (_transmissionRenderTarget === null) { const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; _transmissionRenderTarget = new renderTargetType(1024, 1024, { generateMipmaps: true, type: utils.convert(HalfFloatType) !== null ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, magFilter: NearestFilter, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, useRenderToTexture: extensions.has("WEBGL_multisampled_render_to_texture") }); } const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); _this.setRenderTarget(currentRenderTarget); } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener("dispose", onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!material.normalMap && !!geometry.attributes.tangent; const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphTargetsCount = !!geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputEncoding !== encoding) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, "projectionMatrix", camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, "logDepthBufFC", 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, "isOrthographic", camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, "viewMatrix", camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, "bindMatrix"); p_uniforms.setOptional(_gl, object, "bindMatrixInverse"); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, "boneTexture", skeleton.boneTexture, textures); p_uniforms.setValue(_gl, "boneTextureSize", skeleton.boneTextureSize); } else { p_uniforms.setOptional(_gl, skeleton, "boneMatrices"); } } } if (!!geometry && (geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined)) { morphtargets.update(object, geometry, material, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, "receiveShadow", object.receiveShadow); } if (refreshMaterial) { p_uniforms.setValue(_gl, "toneMappingExposure", _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, "center", object.center); } // common matrices p_uniforms.setValue(_gl, "modelViewMatrix", object.modelViewMatrix); p_uniforms.setValue(_gl, "normalMatrix", object.normalMatrix); p_uniforms.setValue(_gl, "modelMatrix", object.matrixWorld); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (renderTarget.useRenderToTexture) { console.warn("render-to-texture extension was disabled because an external texture was provided"); renderTarget.useRenderToTexture = false; renderTarget.useRenderbuffer = true; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const texture = renderTarget.texture; if (texture.isDataTexture3D || texture.isDataTexture2DArray) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (renderTarget.useRenderbuffer) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget."); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has("EXT_color_buffer_half_float") || capabilities.isWebGL2 && extensions.has("EXT_color_buffer_float")); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has("OES_texture_float") || extensions.has("WEBGL_color_buffer_float"))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type."); return; } if (_gl.checkFramebufferStatus(_gl.FRAMEBUFFER) === _gl.FRAMEBUFFER_COMPLETE) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } else { console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { if (texture.isFramebufferTexture !== true) { console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture."); return; } const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2."); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isDataTexture3D) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataTexture2DArray) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray."); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isDataTexture3D) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedTexture) { console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture."); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { textures.setTexture2D(texture, 0); state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } WebGLRenderer.prototype.isWebGLRenderer = true; class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.name = ""; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON() { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; } } FogExp2.prototype.isFogExp2 = true; class Fog { constructor(color, near = 1, far = 1000) { this.name = ""; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON() { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; } } Fog.prototype.isFog = true; class Scene extends Object3D { constructor() { super(); this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe", { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); return data; } } Scene.prototype.isScene = true; class InterleavedBuffer { constructor(array, stride) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } InterleavedBuffer.prototype.isInterleavedBuffer = true; const _vector$6 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyMatrix4(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.applyNormalMatrix(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$6.x = this.getX(i); _vector$6.y = this.getY(i); _vector$6.z = this.getZ(i); _vector$6.transformDirection(m); this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); } return this; } setX(index, x) { this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { return this.data.array[index * this.data.stride + this.offset]; } getY(index) { return this.data.array[index * this.data.stride + this.offset + 1]; } getZ(index) { return this.data.array[index * this.data.stride + this.offset + 2]; } getW(index) { return this.data.array[index * this.data.stride + this.offset + 3]; } setXY(index, x, y) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log("THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data."); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interlaved attribtue if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; /** * parameters = { * color: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */ class SpriteMaterial extends Material { constructor(parameters) { super(); this.type = "SpriteMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; return this; } } SpriteMaterial.prototype.isSpriteMaterial = true; let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.type = "Sprite"; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error("THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites."); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getUV(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source) { super.copy(source); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } Sprite.prototype.isSprite = true; function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = "LOD"; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, object: object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance < levels[i].distance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { if (distance >= levels[i].distance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector$5 = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); } copy(source) { super.copy(source); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; return this; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.x = skinWeight.getX(i); vector.y = skinWeight.getY(i); vector.z = skinWeight.getZ(i); vector.w = skinWeight.getW(i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === "attached") { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === "detached") { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn("THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode); } } boneTransform(index, target) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(target).applyMatrix4(this.bindMatrix); target.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); target.addScaledVector(_vector$5.copy(_basePosition).applyMatrix4(_matrix), weight); } } return target.applyMatrix4(this.bindMatrixInverse); } } SkinnedMesh.prototype.isSkinnedMesh = true; class Bone extends Object3D { constructor() { super(); this.type = "Bone"; } } Bone.prototype.isBone = true; class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter; this.minFilter = minFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } DataTexture.prototype.isDataTexture = true; const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.frame = -1; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn("THREE.Skeleton: Number of inverse bone matrices does not match amount of bones."); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn("THREE.Skeleton: No bone found with UUID:", uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Skeleton", generator: "Skeleton.toJSON" }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { if (typeof normalized === "number") { meshPerAttribute = normalized; normalized = false; console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."); } super(array, itemSize, normalized); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _mesh = /*@__PURE__*/new Mesh(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.frustumCulled = false; } copy(source) { super.copy(source); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: "dispose" }); } } InstancedMesh.prototype.isInstancedMesh = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ class LineBasicMaterial extends Material { constructor(parameters) { super(); this.type = "LineBasicMaterial"; this.color = new Color(0xffffff); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; } } LineBasicMaterial.prototype.isLineBasicMaterial = true; const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } else if (geometry.isGeometry) { console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Line.prototype.isLine = true; const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; if (geometry.isBufferGeometry) { // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute("lineDistance", new Float32BufferAttribute(lineDistances, 1)); } else { console.warn("THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry."); } } else if (geometry.isGeometry) { console.error("THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } return this; } } LineSegments.prototype.isLineSegments = true; class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.type = "LineLoop"; } } LineLoop.prototype.isLineLoop = true; /** * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * } */ class PointsMaterial extends Material { constructor(parameters) { super(); this.type = "PointsMaterial"; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; } } PointsMaterial.prototype.isPointsMaterial = true; const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source) { super.copy(source); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; if (geometry.isBufferGeometry) { const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } else { console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } updateMorphTargets() { const geometry = this.geometry; if (geometry.isBufferGeometry) { const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } else { const morphTargets = geometry.morphTargets; if (morphTargets !== undefined && morphTargets.length > 0) { console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } } } Points.prototype.isPoints = true; function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ("requestVideoFrameCallback" in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ("requestVideoFrameCallback" in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } VideoTexture.prototype.isVideoTexture = true; class FramebufferTexture extends Texture { constructor(width, height, format) { super({ width, height }); this.format = format; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } FramebufferTexture.prototype.isFramebufferTexture = true; class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } CompressedTexture.prototype.isCompressedTexture = true; class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.needsUpdate = true; } } CanvasTexture.prototype.isCanvasTexture = true; class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute("position", new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute("normal", new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute("uv", new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [// (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = new Vector3(); const _v1$1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = "EdgesGeometry"; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute("position"); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ["a", "b", "c"]; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we"ve already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint() { console.warn("THREE.Curve: .getPoint() not implemented."); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.type = "EllipseCurve"; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } EllipseCurve.prototype.isEllipseCurve = true; class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.type = "ArcCurve"; } } ArcCurve.prototype.isArcCurve = true; /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = new Vector3(); const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = "centripetal", tension = 0.5) { super(); this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === "centripetal" || this.curveType === "chordal") { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === "catmullrom") { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.type = "CubicBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve.prototype.isCubicBezierCurve = true; class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.type = "CubicBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "LineCurve"; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget) { const tangent = optionalTarget || new Vector2(); tangent.copy(this.v2).sub(this.v1).normalize(); return tangent; } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } LineCurve.prototype.isLineCurve = true; class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "LineCurve3"; this.isLineCurve3 = true; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.type = "QuadraticBezierCurve"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.type = "QuadraticBezierCurve3"; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; class SplineCurve extends Curve { constructor(points = []) { super(); this.type = "SplineCurve"; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } SplineCurve.prototype.isSplineCurve = true; var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = "Path"; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/ ) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = "Shape"; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.2) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear let p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed const minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; const minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { const b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.next); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode; const hx = hole.x; const hy = hole.y; let qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones"t intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline (including bevelOffset) is bevel * bevelOffset: , // how far from shape outline does bevel start * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; let depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if (options.amount !== undefined) { console.warn("THREE.ExtrudeBufferGeometry: amount has been renamed to depth."); depth = options.amount; } // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist"); return vec.clone().multiplyScalar(size).add(pt); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV: function (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, 0.5), new Vector2(0.5, 0), new Vector2(0, -0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor(segments); // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = "ShapeGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // incides for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy == 0 && thetaStart == 0) { uOffset = 0.5 / widthSegments; } else if (iy == heightSegments && thetaEnd == Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) { super(); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = "WireframeGeometry"; this.parameters = { geometry: geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all eges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1, hash2); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); /** * parameters = { * color: * } */ class ShadowMaterial extends Material { constructor(parameters) { super(); this.type = "ShadowMaterial"; this.color = new Color(0x000000); this.transparent = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); return this; } } ShadowMaterial.prototype.isShadowMaterial = true; /** * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "STANDARD": "" }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * ior: , * reflectivity: , * * sheen: , * sheenColor: , * sheenColorMap: new THREE.Texture( ), * sheenRoughness: , * sheenRoughnessMap: new THREE.Texture( ), * * transmission: , * transmissionMap: new THREE.Texture( ), * * thickness: , * thicknessMap: new THREE.Texture( ), * attenuationDistance: , * attenuationColor: , * * specularIntensity: , * specularIntensityMap: new THREE.Texture( ), * specularColor: , * specularColorMap: new THREE.Texture( ) * } */ class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, "reflectivity", { get: function () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = 0.0; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._sheen = 0.0; this._clearcoat = 0; this._transmission = 0; this.setValues(parameters); } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { "STANDARD": "", "PHYSICAL": "" }; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; /** * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.MultiplyOperation, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * flatShading: * } */ class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.type = "MeshPhongMaterial"; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } } MeshPhongMaterial.prototype.isMeshPhongMaterial = true; /** * parameters = { * color: , * * map: new THREE.Texture( ), * gradientMap: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * wireframe: , * wireframeLinewidth: , * * } */ class MeshToonMaterial extends Material { constructor(parameters) { super(); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshToonMaterial.prototype.isMeshToonMaterial = true; /** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * flatShading: * } */ class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } MeshNormalMaterial.prototype.isMeshNormalMaterial = true; /** * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * } */ class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.type = "MeshLambertMaterial"; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; return this; } } MeshLambertMaterial.prototype.isMeshLambertMaterial = true; /** * parameters = { * color: , * opacity: , * * matcap: new THREE.Texture( ), * * map: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * alphaMap: new THREE.Texture( ), * * flatShading: * } */ class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.defines = { "MATCAP": "" }; this.type = "MeshMatcapMaterial"; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { "MATCAP": "" }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; return this; } } MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } LineDashedMaterial.prototype.isLineDashedMaterial = true; var Materials = /*#__PURE__*/Object.freeze({ __proto__: null, ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, MeshMatcapMaterial: MeshMatcapMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); const AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function (array, from, to) { if (AnimationUtils.isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); }, // converts an array to a specific type convertArray: function (array, type, forceClone) { if (!array || // let "undefined" and "null" pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === "number") { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array }, isTypedArray: function (object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); }, // returns an array by which times and values can be sorted getKeyframeOrder: function (times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function (values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function (jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } }, subclip: function (sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = AnimationUtils.convertArray(times, track.times.constructor); track.values = AnimationUtils.convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; }, makeClipAdditive: function (targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track"s values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it"s non-numeric if (referenceTrackType === "bool" || referenceTrackType === "string") continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === "quaternion") { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === "quaternion") { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t, t0); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.beforeStart_(0, t, t1); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_(i1 - 1, t0, t); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_() { throw new Error("call to abstract method"); // implementations shall return this.resultBuffer } intervalChanged_() {// empty } } // ALIAS DEFINITIONS Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */ ) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error("THREE.KeyframeTrack: track name is undefined"); if (times === undefined || times.length === 0) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + name); this.name = name; this.times = AnimationUtils.convertArray(times, this.TimeBufferType); this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray(track.times, Array), "values": AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn("THREE.KeyframeTrack:", message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = AnimationUtils.arraySlice(times, from, to); this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error("THREE.KeyframeTrack: Invalid value size in track.", this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error("THREE.KeyframeTrack: Track is empty.", this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === "number" && isNaN(currTime)) { console.error("THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error("THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error("THREE.KeyframeTrack: Value is not a valid number.", this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = AnimationUtils.arraySlice(this.times), values = AnimationUtils.arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = AnimationUtils.arraySlice(times, 0, writeIndex); this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = AnimationUtils.arraySlice(this.times, 0); const values = AnimationUtils.arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = "bool"; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = "color"; // ValueBufferType is inherited /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = "number"; // ValueBufferType is inherited /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = "quaternion"; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = "string"; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = "vector"; // ValueBufferType is inherited class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid, "blendMode": clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = AnimationUtils.getKeyframeOrder(times); times = AnimationUtils.sortedArray(times, 1, order); values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluences[" + morphTargetSequence[i].name + "]", times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error("THREE.AnimationClip: No animation in JSONLoader data."); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || "default"; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack(".morphTargetInfluence[" + morphTargetName + "]", times, values)); } duration = morphTargetNames.length * (fps || 1.0); } else { // ...assume skeletal animation const boneName = ".bones[" + bones[h].name + "]"; addNonemptyTrack(VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; AnimationUtils.flattenJSON(json.keys, times, values, "value"); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don"t set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = "anonymous"; this.withCredentials = false; this.path = ""; this.resourcePath = ""; this.requestHeader = {}; } load() {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse() {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? "include" : "same-origin" // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if (response.status === 0) { console.warn("THREE.FileLoader: HTTP Status 0 received."); } if (typeof ReadableStream === "undefined" || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get("Content-Length"); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent("progress", { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "document": return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case "json": return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS("img"); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener("load", onImageLoad, false); image.removeEventListener("error", onImageError, false); } image.addEventListener("load", onImageLoad, false); image.addEventListener("error", onImageError, false); if (url.substr(0, 5) !== "data:") { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.encoding !== undefined) { texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.type = "Light"; this.color = new Color(color); this.intensity = intensity; } dispose() {// Empty here in base class; some subclasses override. } copy(source) { super.copy(source); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } Light.prototype.isLight = true; class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.type = "HemisphereLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source) { Light.prototype.copy.call(this, source); this.groundColor.copy(source.groundColor); return this; } } HemisphereLight.prototype.isHemisphereLight = true; const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(shadowCamera.projectionMatrix); shadowMatrix.multiply(shadowCamera.matrixWorldInverse); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } SpotLightShadow.prototype.isSpotLightShadow = true; class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1) { super(color, intensity); this.type = "SpotLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } SpotLight.prototype.isSpotLight = true; const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [// These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } PointLightShadow.prototype.isPointLightShadow = true; class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 1) { super(color, intensity); this.type = "PointLight"; this.distance = distance; this.decay = decay; // for physically correct lights, should be 2. this.shadow = new PointLightShadow(); } get power() { // compute the light"s luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light"s intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } PointLight.prototype.isPointLight = true; class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); } } DirectionalLightShadow.prototype.isDirectionalLightShadow = true; class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "DirectionalLight"; this.position.copy(Object3D.DefaultUp); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } DirectionalLight.prototype.isDirectionalLight = true; class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.type = "AmbientLight"; } } AmbientLight.prototype.isAmbientLight = true; class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.type = "RectAreaLight"; this.width = width; this.height = height; } get power() { // compute the light"s luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light"s intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } RectAreaLight.prototype.isRectAreaLight = true; /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } LightProbe.prototype.isLightProbe = true; class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn("THREE.MaterialLoader: Undefined texture", name); } return textures[name]; } const material = new Materials[json.type](); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === "number") { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case "t": material.uniforms[name].value = getTexture(uniform.value); break; case "c": material.uniforms[name].value = new Color().setHex(uniform.value); break; case "v2": material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case "v3": material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case "v4": material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case "m3": material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case "m4": material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } // Deprecated if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ""; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf("/"); if (index === -1) return "./"; return url.substr(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== "string" || url === "") return ""; // Host Relative URL if (/^https?:///i.test(path) && /^//.test(url)) { path = path.replace(/(^https?://[^/]+).*/i, "$1"); } // Absolute URL http://,https://,// if (/^(https?:)?///i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.type = "InstancedBufferGeometry"; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } clone() { return new this.constructor().copy(this); } toJSON() { const data = super.toJSON(this); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error("THREE:ObjectLoader: Can"t parse " + url + ".", error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { console.error("THREE.ObjectLoader: Can"t load " + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } async loadAsync(url, onProgress) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); const text = await loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry") { throw new Error("THREE.ObjectLoader: Can"t load " + url); } return await scope.parseAsync(json); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid] instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } async parseAsync(json) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = await this.parseImagesAsync(json.images); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); return object; } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case "BufferGeometry": case "InstancedBufferGeometry": geometry = bufferGeometryLoader.parse(data); break; case "Geometry": console.error("THREE.ObjectLoader: The legacy Geometry type is no longer supported."); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.type === "MultiMaterial") { // Deprecated const array = []; for (let j = 0; j < data.materials.length; j++) { const material = data.materials[j]; if (cache[material.uuid] === undefined) { cache[material.uuid] = loader.parse(material); } array.push(cache[material.uuid]); } materials[data.uuid] = array; } else { if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } async parseImagesAsync(json) { const scope = this; const images = {}; let loader; async function deserializeImage(image) { if (typeof image === "string") { const url = image; const path = /^(//)|([a-z]+:(//)?)/i.test(url) ? url : scope.resourcePath + url; return await loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture images[image.uuid] = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = await deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { images[image.uuid].push(deserializedImage); } else { // special case: handle array of data textures for cube textures images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } } else { // load single image const deserializedImage = await deserializeImage(image.url); if (deserializedImage !== null) { images[image.uuid] = deserializedImage; } } } } return images; } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === "number") return value; console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn("THREE.ObjectLoader: No "image" specified for", data.uuid); } if (images[data.image] === undefined) { console.warn("THREE.ObjectLoader: Undefined image", data.image); } let texture; const image = images[data.image]; if (Array.isArray(image)) { texture = new CubeTexture(image); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(image.data, image.width, image.height); } else { texture = new Texture(image); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.type !== undefined) texture.type = data.type; if (data.encoding !== undefined) texture.encoding = data.encoding; if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined geometry", name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn("THREE.ObjectLoader: Undefined material", name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn("THREE.ObjectLoader: Undefined texture", uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case "Scene": object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === "Fog") { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === "FogExp2") { object.fog = new FogExp2(data.fog.color, data.fog.density); } } break; case "PerspectiveCamera": object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "OrthographicCamera": object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case "AmbientLight": object = new AmbientLight(data.color, data.intensity); break; case "DirectionalLight": object = new DirectionalLight(data.color, data.intensity); break; case "PointLight": object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case "RectAreaLight": object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case "SpotLight": object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case "HemisphereLight": object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case "LightProbe": object = new LightProbe().fromJSON(data); break; case "SkinnedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case "Mesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case "InstancedMesh": geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case "LOD": object = new LOD(); break; case "Line": object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineLoop": object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case "LineSegments": object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case "PointCloud": case "Points": object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case "Sprite": object = new Sprite(getMaterial(data.material)); break; case "Group": object = new Group(); break; case "Bone": object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === "LOD") { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty("uuid", level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn("THREE.ObjectLoader: No skeleton found with UUID:", child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } /* DEPRECATED */ setTexturePath(value) { console.warn("THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath()."); return this.setResourcePath(value); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); if (typeof createImageBitmap === "undefined") { console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); } if (typeof fetch === "undefined") { console.warn("THREE.ImageBitmapLoader: fetch() not supported."); } this.options = { premultiplyAlpha: "none" }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ""; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === "anonymous" ? "same-origin" : "include"; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: "none" })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; const AudioContext = { getContext: function () { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; }, setContext: function (value) { _context = value; } }; class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType("arraybuffer"); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } HemisphereLightProbe.prototype.isHemisphereLightProbe = true; class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } AmbientLightProbe.prototype.isAmbientLightProbe = true; const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === "undefined" ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = "Audio"; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = "empty"; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = "mediaStreamNode"; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = "buffer"; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn("THREE.Audio: Audio is already playing."); return; } if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn("THREE.Audio: this Audio has no playback control."); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = "HRTF"; this.panner.connect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property // // "add" is used for additive cumulative results // // "work" is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case "quaternion": mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case "string": case "bool": mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the "incoming" region into "accu" accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the "incoming" region into "add" accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of "accu" to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = "\[\]\.:\/"; const _reservedRe = new RegExp("[" + _RESERVED_CHARS_RE + "]", "g"); // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = "[^" + _RESERVED_CHARS_RE + "]"; const _wordCharOrDot = "[^" + _RESERVED_CHARS_RE.replace("\.", "") + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /((?:WC+[/:])*)/.source.replace("WC", _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". const _nodeRe = /(WCOD+)?/.source.replace("WCOD", _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC", _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /.(WC+)(?:[(.+)])?/.source.replace("WC", _wordChar); const _trackRe = new RegExp("" + "^" + _directoryRe + _nodeRe + _objectRe + _propertyRe + "$"); const _supportedObjectNames = ["material", "materials", "bones"]; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; this.rootNode = rootNode; // initial state of these methods that calls "bind" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/s/g, "_").replace(_reservedRe, ""); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (!matches) { throw new Error("PropertyBinding: Cannot parse trackName: " + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf("."); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error("PropertyBinding: can not parse propertyName from trackName: " + trackName); } return results; } static findNode(root, nodeName) { if (!nodeName || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error("THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found."); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case "materials": if (!targetObject.material) { console.error("THREE.PropertyBinding: Can not bind to material as node does not have a material.", this); return; } if (!targetObject.material.materials) { console.error("THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this); return; } targetObject = targetObject.material.materials; break; case "bones": if (!targetObject.skeleton) { console.error("THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; default: if (targetObject[objectName] === undefined) { console.error("THREE.PropertyBinding: Can not bind to objectName of node undefined.", this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error("THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error("THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === "morphTargetInfluences") { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this); return; } if (targetObject.geometry.isBufferGeometry) { if (!targetObject.geometry.morphAttributes) { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } else { console.error("THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.", this); return; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[// Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [// EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [// ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [// HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error("THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes."); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object"s place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } AnimationObjectGroup.prototype.isAnimationObjectGroup = true; class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: "finished", action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: "loop", action: this, loopDelta: loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer"s root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); class Uniform { constructor(value) { if (typeof value === "string") { console.warn("THREE.Uniform: Type parameter is no longer needed."); value = arguments[1]; } this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } GLBufferAttribute.prototype.isGLBufferAttribute = true; class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera && camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera && camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error("THREE.Raycaster: Unsupported camera type: " + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be betwee EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { const clampedPoint = _vector$4.copy(point).clamp(this.min, this.max); return clampedPoint.sub(point).length(); } intersect(box) { this.min.max(box.min); this.max.min(box.max); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } Box2.prototype.isBox2 = true; const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new BufferGeometry(); const positions = [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]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateMatrixWorld(); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.type = "SkeletonHelper"; this.isSkeletonHelper = true; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute("position"); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute("position").needsUpdate = true; super.updateMatrixWorld(force); } } function getBoneList(object) { const boneList = []; if (object && object.isBone) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.light.updateMatrixWorld(); this.color = color; this.type = "PointLightHelper"; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute("position"); const colors = new Float32Array(position.count * 3); geometry.setAttribute("color", new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute("color"); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "GridHelper"; } } class PolarGridHelper extends LineSegments { constructor(radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the radials for (let i = 0; i <= radials; i++) { const v = i / radials * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } // create the circles for (let i = 0; i <= circles; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / circles * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "PolarGridHelper"; } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); // near addLine("n1", "n2", colorFrustum); addLine("n2", "n4", colorFrustum); addLine("n4", "n3", colorFrustum); addLine("n3", "n1", colorFrustum); // far addLine("f1", "f2", colorFrustum); addLine("f2", "f4", colorFrustum); addLine("f4", "f3", colorFrustum); addLine("f3", "f1", colorFrustum); // sides addLine("n1", "f1", colorFrustum); addLine("n2", "f2", colorFrustum); addLine("n3", "f3", colorFrustum); addLine("n4", "f4", colorFrustum); // cone addLine("p", "n1", colorCone); addLine("p", "n2", colorCone); addLine("p", "n3", colorCone); addLine("p", "n4", colorCone); // up addLine("u1", "u2", colorUp); addLine("u2", "u3", colorUp); addLine("u3", "u1", colorUp); // target addLine("c", "t", colorTarget); addLine("p", "c", colorCross); // cross addLine("cn1", "cn2", colorCross); addLine("cn3", "cn4", colorCross); addLine("cf1", "cf2", colorCross); addLine("cf3", "cf4", colorCross); function addLine(a, b, color) { addPoint(a, color); addPoint(b, color); } function addPoint(id, color) { vertices.push(0, 0, 0); colors.push(color.r, color.g, color.b); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = "CameraHelper"; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint("c", pointMap, geometry, _camera, 0, 0, -1); setPoint("t", pointMap, geometry, _camera, 0, 0, 1); // near setPoint("n1", pointMap, geometry, _camera, -w, -h, -1); setPoint("n2", pointMap, geometry, _camera, w, -h, -1); setPoint("n3", pointMap, geometry, _camera, -w, h, -1); setPoint("n4", pointMap, geometry, _camera, w, h, -1); // far setPoint("f1", pointMap, geometry, _camera, -w, -h, 1); setPoint("f2", pointMap, geometry, _camera, w, -h, 1); setPoint("f3", pointMap, geometry, _camera, -w, h, 1); setPoint("f4", pointMap, geometry, _camera, w, h, 1); // up setPoint("u1", pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint("u2", pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint("u3", pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint("cf1", pointMap, geometry, _camera, -w, 0, 1); setPoint("cf2", pointMap, geometry, _camera, w, 0, 1); setPoint("cf3", pointMap, geometry, _camera, 0, -h, 1); setPoint("cf4", pointMap, geometry, _camera, 0, h, 1); setPoint("cn1", pointMap, geometry, _camera, -w, 0, -1); setPoint("cn2", pointMap, geometry, _camera, w, 0, -1); setPoint("cn3", pointMap, geometry, _camera, 0, -h, -1); setPoint("cn4", pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute("position").needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute("position"); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.object = object; this.type = "BoxHelper"; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn("THREE.BoxHelper: .update() has no longer arguments."); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source) { LineSegments.prototype.copy.call(this, source); this.object = source.object; return this; } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.box = box; this.type = "Box3Helper"; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [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, 0, 1, 0, 0, 0]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.type = "PlaneHelper"; this.plane = plane; this.size = size; const positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; const geometry2 = new BufferGeometry(); geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { let scale = -this.plane.constant; if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter this.scale.set(0.5 * this.size, 0.5 * this.size, scale); this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt(this.plane.normal); super.updateMatrixWorld(force); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = "ArrowHelper"; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute("position", new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setAttribute("color", new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = "AxesHelper"; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW, noHoles) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; if (noHoles === true) return toShapesNoHoles(subPaths); let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log("cw", i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; const toChange = []; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange.push({ froms: sIdx, tos: s2Idx, hole: hIdx }); if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } // console.log("ambiguous: ", ambiguous); if (toChange.length > 0) { // console.log("to change: ", toChange); if (!ambiguous) newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } const _floatView = new Float32Array(1); const _int32View = new Int32Array(_floatView.buffer); class DataUtils { // Converts float32 to float16 (stored as uint16 value). static toHalfFloat(val) { if (val > 65504) { console.warn("THREE.DataUtils.toHalfFloat(): value exceeds 65504."); val = 65504; // maximum representable value in float16 } // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[0] = val; const x = _int32View[0]; let bits = x >> 16 & 0x8000; /* Get the sign */ let m = x >> 12 & 0x07ff; /* Keep one extra bit for rounding */ const e = x >> 23 & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } } const LineStrip = 0; const LinePieces = 1; const NoColors = 0; const FaceColors = 1; const VertexColors = 2; function MeshFaceMaterial(materials) { console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."); return materials; } function MultiMaterial(materials = []) { console.warn("THREE.MultiMaterial has been removed. Use an Array instead."); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud(geometry, material) { console.warn("THREE.PointCloud has been renamed to THREE.Points."); return new Points(geometry, material); } function Particle(material) { console.warn("THREE.Particle has been renamed to THREE.Sprite."); return new Sprite(material); } function ParticleSystem(geometry, material) { console.warn("THREE.ParticleSystem has been renamed to THREE.Points."); return new Points(geometry, material); } function PointCloudMaterial(parameters) { console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleBasicMaterial(parameters) { console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function ParticleSystemMaterial(parameters) { console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."); return new PointsMaterial(parameters); } function Vertex(x, y, z) { console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."); return new Vector3(x, y, z); } // function DynamicBufferAttribute(array, itemSize) { console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."); return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); } function Int8Attribute(array, itemSize) { console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."); return new Int8BufferAttribute(array, itemSize); } function Uint8Attribute(array, itemSize) { console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."); return new Uint8BufferAttribute(array, itemSize); } function Uint8ClampedAttribute(array, itemSize) { console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."); return new Uint8ClampedBufferAttribute(array, itemSize); } function Int16Attribute(array, itemSize) { console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."); return new Int16BufferAttribute(array, itemSize); } function Uint16Attribute(array, itemSize) { console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."); return new Uint16BufferAttribute(array, itemSize); } function Int32Attribute(array, itemSize) { console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."); return new Int32BufferAttribute(array, itemSize); } function Uint32Attribute(array, itemSize) { console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."); return new Uint32BufferAttribute(array, itemSize); } function Float32Attribute(array, itemSize) { console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."); return new Float32BufferAttribute(array, itemSize); } function Float64Attribute(array, itemSize) { console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."); return new Float64BufferAttribute(array, itemSize); } // Curve.create = function (construct, getPoint) { console.log("THREE.Curve.create() has been deprecated"); construct.prototype = Object.create(Curve.prototype); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Path.prototype.fromPoints = function (points) { console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."); return this.setFromPoints(points); }; // function AxisHelper(size) { console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."); return new AxesHelper(size); } function BoundingBoxHelper(object, color) { console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."); return new BoxHelper(object, color); } function EdgesHelper(object, hex) { console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."); return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } GridHelper.prototype.setColors = function () { console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead."); }; SkeletonHelper.prototype.update = function () { console.error("THREE.SkeletonHelper: update() no longer needs to be called."); }; function WireframeHelper(object, hex) { console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."); return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ color: hex !== undefined ? hex : 0xffffff })); } // Loader.prototype.extractUrlBase = function (url) { console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."); return LoaderUtils.extractUrlBase(url); }; Loader.Handlers = { add: function /* regex, loader */ () { console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead."); }, get: function /* file */ () { console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead."); } }; function XHRLoader(manager) { console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."); return new FileLoader(manager); } function BinaryTextureLoader(manager) { console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."); return new DataTextureLoader(manager); } // Box2.prototype.center = function (optionalTarget) { console.warn("THREE.Box2: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box2.prototype.empty = function () { console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box2.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box2.prototype.size = function (optionalTarget) { console.warn("THREE.Box2: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Box3.prototype.center = function (optionalTarget) { console.warn("THREE.Box3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; Box3.prototype.empty = function () { console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; Box3.prototype.isIntersectionBox = function (box) { console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Box3.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; Box3.prototype.size = function (optionalTarget) { console.warn("THREE.Box3: .size() has been renamed to .getSize()."); return this.getSize(optionalTarget); }; // Sphere.prototype.empty = function () { console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."); return this.isEmpty(); }; // Frustum.prototype.setFromMatrix = function (m) { console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."); return this.setFromProjectionMatrix(m); }; // Line3.prototype.center = function (optionalTarget) { console.warn("THREE.Line3: .center() has been renamed to .getCenter()."); return this.getCenter(optionalTarget); }; // Matrix3.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix3.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."); return vector.applyMatrix3(this); }; Matrix3.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix3: .multiplyVector3Array() has been removed."); }; Matrix3.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."); return attribute.applyMatrix3(this); }; Matrix3.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix3: .applyToVector3Array() has been removed."); }; Matrix3.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Matrix4.prototype.extractPosition = function (m) { console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."); return this.copyPosition(m); }; Matrix4.prototype.flattenToArrayOffset = function (array, offset) { console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."); return this.toArray(array, offset); }; Matrix4.prototype.getPosition = function () { console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."); return new Vector3().setFromMatrixColumn(this, 3); }; Matrix4.prototype.setRotationFromQuaternion = function (q) { console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."); return this.makeRotationFromQuaternion(q); }; Matrix4.prototype.multiplyToArray = function () { console.warn("THREE.Matrix4: .multiplyToArray() has been removed."); }; Matrix4.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector4 = function (vector) { console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.multiplyVector3Array = function /* a */ () { console.error("THREE.Matrix4: .multiplyVector3Array() has been removed."); }; Matrix4.prototype.rotateAxis = function (v) { console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."); v.transformDirection(this); }; Matrix4.prototype.crossVector = function (vector) { console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."); return vector.applyMatrix4(this); }; Matrix4.prototype.translate = function () { console.error("THREE.Matrix4: .translate() has been removed."); }; Matrix4.prototype.rotateX = function () { console.error("THREE.Matrix4: .rotateX() has been removed."); }; Matrix4.prototype.rotateY = function () { console.error("THREE.Matrix4: .rotateY() has been removed."); }; Matrix4.prototype.rotateZ = function () { console.error("THREE.Matrix4: .rotateZ() has been removed."); }; Matrix4.prototype.rotateByAxis = function () { console.error("THREE.Matrix4: .rotateByAxis() has been removed."); }; Matrix4.prototype.applyToBufferAttribute = function (attribute) { console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."); return attribute.applyMatrix4(this); }; Matrix4.prototype.applyToVector3Array = function /* array, offset, length */ () { console.error("THREE.Matrix4: .applyToVector3Array() has been removed."); }; Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."); return this.makePerspective(left, right, top, bottom, near, far); }; Matrix4.prototype.getInverse = function (matrix) { console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."); return this.copy(matrix).invert(); }; // Plane.prototype.isIntersectionLine = function (line) { console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."); return this.intersectsLine(line); }; // Quaternion.prototype.multiplyVector3 = function (vector) { console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); return vector.applyQuaternion(this); }; Quaternion.prototype.inverse = function () { console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."); return this.invert(); }; // Ray.prototype.isIntersectionBox = function (box) { console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."); return this.intersectsBox(box); }; Ray.prototype.isIntersectionPlane = function (plane) { console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."); return this.intersectsPlane(plane); }; Ray.prototype.isIntersectionSphere = function (sphere) { console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."); return this.intersectsSphere(sphere); }; // Triangle.prototype.area = function () { console.warn("THREE.Triangle: .area() has been renamed to .getArea()."); return this.getArea(); }; Triangle.prototype.barycoordFromPoint = function (point, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return this.getBarycoord(point, target); }; Triangle.prototype.midpoint = function (target) { console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."); return this.getMidpoint(target); }; Triangle.prototypenormal = function (target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return this.getNormal(target); }; Triangle.prototype.plane = function (target) { console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."); return this.getPlane(target); }; Triangle.barycoordFromPoint = function (point, a, b, c, target) { console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."); return Triangle.getBarycoord(point, a, b, c, target); }; Triangle.normal = function (a, b, c, target) { console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."); return Triangle.getNormal(a, b, c, target); }; // Shape.prototype.extractAllPoints = function (divisions) { console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."); return this.extractPoints(divisions); }; Shape.prototype.extrude = function (options) { console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."); return new ExtrudeGeometry(this, options); }; Shape.prototype.makeGeometry = function (options) { console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."); return new ShapeGeometry(this, options); }; // Vector2.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector2.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector2.prototype.lengthManhattan = function () { console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector3.prototype.setEulerFromRotationMatrix = function () { console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead."); }; Vector3.prototype.setEulerFromQuaternion = function () { console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead."); }; Vector3.prototype.getPositionFromMatrix = function (m) { console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."); return this.setFromMatrixPosition(m); }; Vector3.prototype.getScaleFromMatrix = function (m) { console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."); return this.setFromMatrixScale(m); }; Vector3.prototype.getColumnFromMatrix = function (index, matrix) { console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."); return this.setFromMatrixColumn(matrix, index); }; Vector3.prototype.applyProjection = function (m) { console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."); return this.applyMatrix4(m); }; Vector3.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector3.prototype.distanceToManhattan = function (v) { console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."); return this.manhattanDistanceTo(v); }; Vector3.prototype.lengthManhattan = function () { console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Vector4.prototype.fromAttribute = function (attribute, index, offset) { console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."); return this.fromBufferAttribute(attribute, index, offset); }; Vector4.prototype.lengthManhattan = function () { console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."); return this.manhattanLength(); }; // Object3D.prototype.getChildByName = function (name) { console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."); return this.getObjectByName(name); }; Object3D.prototype.renderDepth = function () { console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead."); }; Object3D.prototype.translate = function (distance, axis) { console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."); return this.translateOnAxis(axis, distance); }; Object3D.prototype.getWorldRotation = function () { console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead."); }; Object3D.prototype.applyMatrix = function (matrix) { console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(Object3D.prototype, { eulerOrder: { get: function () { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); return this.rotation.order; }, set: function (value) { console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."); this.rotation.order = value; } }, 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."); } } }); Mesh.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(Mesh.prototype, { drawMode: { get: function () { console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."); return TrianglesDrawMode; }, 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."); } } }); SkinnedMesh.prototype.initBones = function () { console.error("THREE.SkinnedMesh: initBones() has been removed."); }; // PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup."); if (filmGauge !== undefined) this.filmGauge = filmGauge; this.setFocalLength(focalLength); }; // Object.defineProperties(Light.prototype, { onlyShadow: { set: function () { console.warn("THREE.Light: .onlyShadow has been removed."); } }, shadowCameraFov: { set: function (value) { console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function (value) { console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function (value) { console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function (value) { console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function (value) { console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function (value) { console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function (value) { console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead."); } }, shadowBias: { set: function (value) { console.warn("THREE.Light: .shadowBias is now .shadow.bias."); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn("THREE.Light: .shadowDarkness has been removed."); } }, shadowMapWidth: { set: function (value) { console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function (value) { console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."); this.shadow.mapSize.height = value; } } }); // Object.defineProperties(BufferAttribute.prototype, { length: { get: function () { console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."); return this.array.length; } }, dynamic: { get: function () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); return this.usage === DynamicDrawUsage; }, set: function /* value */ () { console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."); this.setUsage(DynamicDrawUsage); } } }); BufferAttribute.prototype.setDynamic = function (value) { console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; BufferAttribute.prototype.copyIndicesArray = function /* indices */ () { console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed."); }, BufferAttribute.prototype.setArray = function /* array */ () { console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // BufferGeometry.prototype.addIndex = function (index) { console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."); this.setIndex(index); }; BufferGeometry.prototype.addAttribute = function (name, attribute) { console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."); if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."); return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); } if (name === "index") { console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."); this.setIndex(attribute); return this; } return this.setAttribute(name, attribute); }; BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { if (indexOffset !== undefined) { console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."); } console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."); this.addGroup(start, count); }; BufferGeometry.prototype.clearDrawCalls = function () { console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."); this.clearGroups(); }; BufferGeometry.prototype.computeOffsets = function () { console.warn("THREE.BufferGeometry: .computeOffsets() has been removed."); }; BufferGeometry.prototype.removeAttribute = function (name) { console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."); return this.deleteAttribute(name); }; BufferGeometry.prototype.applyMatrix = function (matrix) { console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."); return this.applyMatrix4(matrix); }; Object.defineProperties(BufferGeometry.prototype, { drawcalls: { get: function () { console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."); return this.groups; } }, offsets: { get: function () { console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."); return this.groups; } } }); InterleavedBuffer.prototype.setDynamic = function (value) { console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."); this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); return this; }; InterleavedBuffer.prototype.setArray = function /* array */ () { console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers"); }; // ExtrudeGeometry.prototype.getArrays = function () { console.error("THREE.ExtrudeGeometry: .getArrays() has been removed."); }; ExtrudeGeometry.prototype.addShapeList = function () { console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed."); }; ExtrudeGeometry.prototype.addShape = function () { console.error("THREE.ExtrudeGeometry: .addShape() has been removed."); }; // Scene.prototype.dispose = function () { console.error("THREE.Scene: .dispose() has been removed."); }; // Uniform.prototype.onUpdate = function () { console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."); return this; }; // Object.defineProperties(Material.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 () { console.warn("THREE.Material: .wrapRGB has been removed."); return new Color(); } }, shading: { get: function () { console.error("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); }, set: function (value) { console.warn("THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead."); this.flatShading = value === FlatShading; } }, stencilMask: { get: function () { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); return this.stencilFuncMask; }, set: function (value) { console.warn("THREE." + this.type + ": .stencilMask has been removed. Use .stencilFuncMask instead."); this.stencilFuncMask = value; } }, vertexTangents: { get: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); }, set: function () { console.warn("THREE." + this.type + ": .vertexTangents has been removed."); } } }); Object.defineProperties(ShaderMaterial.prototype, { derivatives: { get: function () { console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); return this.extensions.derivatives; }, set: function (value) { console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."); this.extensions.derivatives = value; } } }); // WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."); this.setRenderTarget(renderTarget); this.clear(color, depth, stencil); }; WebGLRenderer.prototype.animate = function (callback) { console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."); this.setAnimationLoop(callback); }; WebGLRenderer.prototype.getCurrentRenderTarget = function () { console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."); return this.getRenderTarget(); }; WebGLRenderer.prototype.getMaxAnisotropy = function () { console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."); return this.capabilities.getMaxAnisotropy(); }; WebGLRenderer.prototype.getPrecision = function () { console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."); return this.capabilities.precision; }; WebGLRenderer.prototype.resetGLState = function () { console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."); return this.state.reset(); }; WebGLRenderer.prototype.supportsFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )."); return this.extensions.get("OES_texture_float"); }; WebGLRenderer.prototype.supportsHalfFloatTextures = function () { console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )."); return this.extensions.get("OES_texture_half_float"); }; WebGLRenderer.prototype.supportsStandardDerivatives = function () { console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )."); return this.extensions.get("OES_standard_derivatives"); }; WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )."); return this.extensions.get("WEBGL_compressed_texture_s3tc"); }; WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )."); return this.extensions.get("WEBGL_compressed_texture_pvrtc"); }; WebGLRenderer.prototype.supportsBlendMinMax = function () { console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )."); return this.extensions.get("EXT_blend_minmax"); }; WebGLRenderer.prototype.supportsVertexTextures = function () { console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."); return this.capabilities.vertexTextures; }; WebGLRenderer.prototype.supportsInstancedArrays = function () { console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )."); return this.extensions.get("ANGLE_instanced_arrays"); }; WebGLRenderer.prototype.enableScissorTest = function (boolean) { console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."); this.setScissorTest(boolean); }; WebGLRenderer.prototype.initMaterial = function () { console.warn("THREE.WebGLRenderer: .initMaterial() has been removed."); }; WebGLRenderer.prototype.addPrePlugin = function () { console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed."); }; WebGLRenderer.prototype.addPostPlugin = function () { console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed."); }; WebGLRenderer.prototype.updateShadowMap = function () { console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed."); }; WebGLRenderer.prototype.setFaceCulling = function () { console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed."); }; WebGLRenderer.prototype.allocTextureUnit = function () { console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed."); }; WebGLRenderer.prototype.setTexture = function () { console.warn("THREE.WebGLRenderer: .setTexture() has been removed."); }; WebGLRenderer.prototype.setTexture2D = function () { console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed."); }; WebGLRenderer.prototype.setTextureCube = function () { console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed."); }; WebGLRenderer.prototype.getActiveMipMapLevel = function () { console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."); return this.getActiveMipmapLevel(); }; Object.defineProperties(WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function (value) { console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* value */ () { console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead."); } }, context: { get: function () { console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."); return this.getContext(); } }, vr: { get: function () { console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"); return this.xr; } }, gammaInput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); return false; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."); } }, gammaOutput: { get: function () { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); return false; }, set: function (value) { console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."); this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; } }, toneMappingWhitePoint: { get: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); return 1.0; }, set: function () { console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."); } }, gammaFactor: { get: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); return 2; }, set: function () { console.warn("THREE.WebGLRenderer: .gammaFactor has been removed."); } } }); Object.defineProperties(WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead."); return undefined; }, set: function /* cullFace */ () { 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."); return undefined; }, 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."); return undefined; }, set: function () { console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead."); } } }); function WebGLRenderTargetCube(width, height, options) { console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."); return new WebGLCubeRenderTarget(width, options); } // Object.defineProperties(WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); return this.texture.wrapS; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); return this.texture.wrapT; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); return this.texture.magFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); return this.texture.minFilter; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); return this.texture.anisotropy; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); return this.texture.offset; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."); this.texture.offset = value; } }, repeat: { get: function () { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); return this.texture.repeat; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."); this.texture.repeat = value; } }, format: { get: function () { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); return this.texture.format; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."); this.texture.format = value; } }, type: { get: function () { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); return this.texture.type; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); return this.texture.generateMipmaps; }, set: function (value) { console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."); this.texture.generateMipmaps = value; } } }); // Audio.prototype.load = function (file) { console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead."); const scope = this; const audioLoader = new AudioLoader(); audioLoader.load(file, function (buffer) { scope.setBuffer(buffer); }); return this; }; AudioAnalyser.prototype.getData = function () { console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function (renderer, scene) { console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."); return this.update(renderer, scene); }; CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."); return this.renderTarget.clear(renderer, color, depth, stencil); }; ImageUtils.crossOrigin = undefined; ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."); const loader = new TextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(url, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead."); const loader = new CubeTextureLoader(); loader.setCrossOrigin(this.crossOrigin); const texture = loader.load(urls, onLoad, undefined, onError); if (mapping) texture.mapping = mapping; return texture; }; ImageUtils.loadCompressedTexture = function () { console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead."); }; ImageUtils.loadCompressedTextureCube = function () { console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead."); }; // function CanvasRenderer() { console.error("THREE.CanvasRenderer has been removed"); } // function JSONLoader() { console.error("THREE.JSONLoader has been removed."); } // const SceneUtils = { createMultiMaterialObject: function /* geometry, materials */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, detach: function /* child, parent, scene */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); }, attach: function /* child, scene, parent */ () { console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js"); } }; // function LensFlare() { console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js"); } // function ParametricGeometry() { console.error("THREE.ParametricGeometry has been moved to /examples/jsm/geometries/ParametricGeometry.js"); return new BufferGeometry(); } function TextGeometry() { console.error("THREE.TextGeometry has been moved to /examples/jsm/geometries/TextGeometry.js"); return new BufferGeometry(); } function FontLoader() { console.error("THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"); } function Font() { console.error("THREE.Font has been moved to /examples/jsm/loaders/FontLoader.js"); } function ImmediateRenderObject() { console.error("THREE.ImmediateRenderObject has been removed."); } if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: { revision: REVISION } })); } if (typeof window !== "undefined") { if (window.__THREE__) { console.warn("WARNING: Multiple instances of Three.js being imported."); } else { window.__THREE__ = REVISION; } } exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.AddEquation = AddEquation; exports.AddOperation = AddOperation; exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; exports.AdditiveBlending = AdditiveBlending; exports.AlphaFormat = AlphaFormat; exports.AlwaysDepth = AlwaysDepth; exports.AlwaysStencilFunc = AlwaysStencilFunc; exports.AmbientLight = AmbientLight; exports.AmbientLightProbe = AmbientLightProbe; exports.AnimationClip = AnimationClip; exports.AnimationLoader = AnimationLoader; exports.AnimationMixer = AnimationMixer; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationUtils = AnimationUtils; exports.ArcCurve = ArcCurve; exports.ArrayCamera = ArrayCamera; exports.ArrowHelper = ArrowHelper; exports.Audio = Audio; exports.AudioAnalyser = AudioAnalyser; exports.AudioContext = AudioContext; exports.AudioListener = AudioListener; exports.AudioLoader = AudioLoader; exports.AxesHelper = AxesHelper; exports.AxisHelper = AxisHelper; exports.BackSide = BackSide; exports.BasicDepthPacking = BasicDepthPacking; exports.BasicShadowMap = BasicShadowMap; exports.BinaryTextureLoader = BinaryTextureLoader; exports.Bone = Bone; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.BoundingBoxHelper = BoundingBoxHelper; exports.Box2 = Box2; exports.Box3 = Box3; exports.Box3Helper = Box3Helper; exports.BoxBufferGeometry = BoxGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxHelper = BoxHelper; exports.BufferAttribute = BufferAttribute; exports.BufferGeometry = BufferGeometry; exports.BufferGeometryLoader = BufferGeometryLoader; exports.ByteType = ByteType; exports.Cache = Cache; exports.Camera = Camera; exports.CameraHelper = CameraHelper; exports.CanvasRenderer = CanvasRenderer; exports.CanvasTexture = CanvasTexture; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CineonToneMapping = CineonToneMapping; exports.CircleBufferGeometry = CircleGeometry; exports.CircleGeometry = CircleGeometry; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.Clock = Clock; exports.Color = Color; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.CompressedTexture = CompressedTexture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.ConeBufferGeometry = ConeGeometry; exports.ConeGeometry = ConeGeometry; exports.CubeCamera = CubeCamera; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.CubeTexture = CubeTexture; exports.CubeTextureLoader = CubeTextureLoader; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.CubicInterpolant = CubicInterpolant; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.CullFaceNone = CullFaceNone; exports.Curve = Curve; exports.CurvePath = CurvePath; exports.CustomBlending = CustomBlending; exports.CustomToneMapping = CustomToneMapping; exports.CylinderBufferGeometry = CylinderGeometry; exports.CylinderGeometry = CylinderGeometry; exports.Cylindrical = Cylindrical; exports.DataTexture = DataTexture; exports.DataTexture2DArray = DataTexture2DArray; exports.DataTexture3D = DataTexture3D; exports.DataTextureLoader = DataTextureLoader; exports.DataUtils = DataUtils; exports.DecrementStencilOp = DecrementStencilOp; exports.DecrementWrapStencilOp = DecrementWrapStencilOp; exports.DefaultLoadingManager = DefaultLoadingManager; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.DepthTexture = DepthTexture; exports.DirectionalLight = DirectionalLight; exports.DirectionalLightHelper = DirectionalLightHelper; exports.DiscreteInterpolant = DiscreteInterpolant; exports.DodecahedronBufferGeometry = DodecahedronGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DoubleSide = DoubleSide; exports.DstAlphaFactor = DstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.DynamicCopyUsage = DynamicCopyUsage; exports.DynamicDrawUsage = DynamicDrawUsage; exports.DynamicReadUsage = DynamicReadUsage; exports.EdgesGeometry = EdgesGeometry; exports.EdgesHelper = EdgesHelper; exports.EllipseCurve = EllipseCurve; exports.EqualDepth = EqualDepth; exports.EqualStencilFunc = EqualStencilFunc; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.Euler = Euler; exports.EventDispatcher = EventDispatcher; exports.ExtrudeBufferGeometry = ExtrudeGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.FaceColors = FaceColors; exports.FileLoader = FileLoader; exports.FlatShading = FlatShading; exports.Float16BufferAttribute = Float16BufferAttribute; exports.Float32Attribute = Float32Attribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Float64Attribute = Float64Attribute; exports.Float64BufferAttribute = Float64BufferAttribute; exports.FloatType = FloatType; exports.Fog = Fog; exports.FogExp2 = FogExp2; exports.Font = Font; exports.FontLoader = FontLoader; exports.FramebufferTexture = FramebufferTexture; exports.FrontSide = FrontSide; exports.Frustum = Frustum; exports.GLBufferAttribute = GLBufferAttribute; exports.GLSL1 = GLSL1; exports.GLSL3 = GLSL3; exports.GreaterDepth = GreaterDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; exports.GreaterStencilFunc = GreaterStencilFunc; exports.GridHelper = GridHelper; exports.Group = Group; exports.HalfFloatType = HalfFloatType; exports.HemisphereLight = HemisphereLight; exports.HemisphereLightHelper = HemisphereLightHelper; exports.HemisphereLightProbe = HemisphereLightProbe; exports.IcosahedronBufferGeometry = IcosahedronGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.ImageBitmapLoader = ImageBitmapLoader; exports.ImageLoader = ImageLoader; exports.ImageUtils = ImageUtils; exports.ImmediateRenderObject = ImmediateRenderObject; exports.IncrementStencilOp = IncrementStencilOp; exports.IncrementWrapStencilOp = IncrementWrapStencilOp; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InstancedMesh = InstancedMesh; exports.Int16Attribute = Int16Attribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Int32Attribute = Int32Attribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.IntType = IntType; exports.InterleavedBuffer = InterleavedBuffer; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.Interpolant = Interpolant; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.InvertStencilOp = InvertStencilOp; exports.JSONLoader = JSONLoader; exports.KeepStencilOp = KeepStencilOp; exports.KeyframeTrack = KeyframeTrack; exports.LOD = LOD; exports.LatheBufferGeometry = LatheGeometry; exports.LatheGeometry = LatheGeometry; exports.Layers = Layers; exports.LensFlare = LensFlare; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.LessEqualStencilFunc = LessEqualStencilFunc; exports.LessStencilFunc = LessStencilFunc; exports.Light = Light; exports.LightProbe = LightProbe; exports.Line = Line; exports.Line3 = Line3; exports.LineBasicMaterial = LineBasicMaterial; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.LineDashedMaterial = LineDashedMaterial; exports.LineLoop = LineLoop; exports.LinePieces = LinePieces; exports.LineSegments = LineSegments; exports.LineStrip = LineStrip; exports.LinearEncoding = LinearEncoding; exports.LinearFilter = LinearFilter; exports.LinearInterpolant = LinearInterpolant; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; exports.LinearToneMapping = LinearToneMapping; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.LoadingManager = LoadingManager; exports.LoopOnce = LoopOnce; exports.LoopPingPong = LoopPingPong; exports.LoopRepeat = LoopRepeat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.LuminanceFormat = LuminanceFormat; exports.MOUSE = MOUSE; exports.Material = Material; exports.MaterialLoader = MaterialLoader; exports.Math = MathUtils; exports.MathUtils = MathUtils; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.MaxEquation = MaxEquation; exports.Mesh = Mesh; exports.MeshBasicMaterial = MeshBasicMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MinEquation = MinEquation; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.MixOperation = MixOperation; exports.MultiMaterial = MultiMaterial; exports.MultiplyBlending = MultiplyBlending; exports.MultiplyOperation = MultiplyOperation; exports.NearestFilter = NearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; exports.NeverDepth = NeverDepth; exports.NeverStencilFunc = NeverStencilFunc; exports.NoBlending = NoBlending; exports.NoColors = NoColors; exports.NoToneMapping = NoToneMapping; exports.NormalAnimationBlendMode = NormalAnimationBlendMode; exports.NormalBlending = NormalBlending; exports.NotEqualDepth = NotEqualDepth; exports.NotEqualStencilFunc = NotEqualStencilFunc; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.Object3D = Object3D; exports.ObjectLoader = ObjectLoader; exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.OctahedronBufferGeometry = OctahedronGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OneFactor = OneFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.OrthographicCamera = OrthographicCamera; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.PMREMGenerator = PMREMGenerator; exports.ParametricGeometry = ParametricGeometry; exports.Particle = Particle; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystem = ParticleSystem; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Path = Path; exports.PerspectiveCamera = PerspectiveCamera; exports.Plane = Plane; exports.PlaneBufferGeometry = PlaneGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneHelper = PlaneHelper; exports.PointCloud = PointCloud; exports.PointCloudMaterial = PointCloudMaterial; exports.PointLight = PointLight; exports.PointLightHelper = PointLightHelper; exports.Points = Points; exports.PointsMaterial = PointsMaterial; exports.PolarGridHelper = PolarGridHelper; exports.PolyhedronBufferGeometry = PolyhedronGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PositionalAudio = PositionalAudio; exports.PropertyBinding = PropertyBinding; exports.PropertyMixer = PropertyMixer; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.Quaternion = Quaternion; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.REVISION = REVISION; exports.RGBADepthPacking = RGBADepthPacking; exports.RGBAFormat = RGBAFormat; exports.RGBAIntegerFormat = RGBAIntegerFormat; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_BPTC_Format = RGBA_BPTC_Format; exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGBFormat = RGBFormat; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGB_ETC2_Format = RGB_ETC2_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGFormat = RGFormat; exports.RGIntegerFormat = RGIntegerFormat; exports.RawShaderMaterial = RawShaderMaterial; exports.Ray = Ray; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RedFormat = RedFormat; exports.RedIntegerFormat = RedIntegerFormat; exports.ReinhardToneMapping = ReinhardToneMapping; exports.RepeatWrapping = RepeatWrapping; exports.ReplaceStencilOp = ReplaceStencilOp; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.RingBufferGeometry = RingGeometry; exports.RingGeometry = RingGeometry; exports.Scene = Scene; exports.SceneUtils = SceneUtils; exports.ShaderChunk = ShaderChunk; exports.ShaderLib = ShaderLib; exports.ShaderMaterial = ShaderMaterial; exports.ShadowMaterial = ShadowMaterial; exports.Shape = Shape; exports.ShapeBufferGeometry = ShapeGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapePath = ShapePath; exports.ShapeUtils = ShapeUtils; exports.ShortType = ShortType; exports.Skeleton = Skeleton; exports.SkeletonHelper = SkeletonHelper; exports.SkinnedMesh = SkinnedMesh; exports.SmoothShading = SmoothShading; exports.Sphere = Sphere; exports.SphereBufferGeometry = SphereGeometry; exports.SphereGeometry = SphereGeometry; exports.Spherical = Spherical; exports.SphericalHarmonics3 = SphericalHarmonics3; exports.SplineCurve = SplineCurve; exports.SpotLight = SpotLight; exports.SpotLightHelper = SpotLightHelper; exports.Sprite = Sprite; exports.SpriteMaterial = SpriteMaterial; exports.SrcAlphaFactor = SrcAlphaFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.SrcColorFactor = SrcColorFactor; exports.StaticCopyUsage = StaticCopyUsage; exports.StaticDrawUsage = StaticDrawUsage; exports.StaticReadUsage = StaticReadUsage; exports.StereoCamera = StereoCamera; exports.StreamCopyUsage = StreamCopyUsage; exports.StreamDrawUsage = StreamDrawUsage; exports.StreamReadUsage = StreamReadUsage; exports.StringKeyframeTrack = StringKeyframeTrack; exports.SubtractEquation = SubtractEquation; exports.SubtractiveBlending = SubtractiveBlending; exports.TOUCH = TOUCH; exports.TangentSpaceNormalMap = TangentSpaceNormalMap; exports.TetrahedronBufferGeometry = TetrahedronGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TextGeometry = TextGeometry; exports.Texture = Texture; exports.TextureLoader = TextureLoader; exports.TorusBufferGeometry = TorusGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusKnotBufferGeometry = TorusKnotGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.Triangle = Triangle; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TubeBufferGeometry = TubeGeometry; exports.TubeGeometry = TubeGeometry; exports.UVMapping = UVMapping; exports.Uint16Attribute = Uint16Attribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Uint32Attribute = Uint32Attribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uniform = Uniform; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.UnsignedByteType = UnsignedByteType; exports.UnsignedInt248Type = UnsignedInt248Type; exports.UnsignedIntType = UnsignedIntType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShortType = UnsignedShortType; exports.VSMShadowMap = VSMShadowMap; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.Vertex = Vertex; exports.VertexColors = VertexColors; exports.VideoTexture = VideoTexture; exports.WebGL1Renderer = WebGL1Renderer; exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; exports.WebGLMultipleRenderTargets = WebGLMultipleRenderTargets; exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderer = WebGLRenderer; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.WireframeHelper = WireframeHelper; exports.WrapAroundEnding = WrapAroundEnding; exports.XHRLoader = XHRLoader; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroFactor = ZeroFactor; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.ZeroStencilOp = ZeroStencilOp; exports._SRGBAFormat = _SRGBAFormat; exports.sRGBEncoding = sRGBEncoding; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
mapillary",[],(function $module_mapillary(global,require,requireDynamic,requireLazy,module,exports){ (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.mapillary = {})); })(this, (function (exports) { "use strict"; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } function isFunction(value) { return typeof value === "function"; } function createErrorClass(createImpl) { var _super = function (instance) { Error.call(instance); instance.stack = new Error().stack; }; var ctorFunc = createImpl(_super); ctorFunc.prototype = Object.create(Error.prototype); ctorFunc.prototype.constructor = ctorFunc; return ctorFunc; } var UnsubscriptionError = createErrorClass(function (_super) { return function UnsubscriptionErrorImpl(errors) { _super(this); this.message = errors ? errors.length + " errors occurred during unsubscription: " + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join(" ") : ""; this.name = "UnsubscriptionError"; this.errors = errors; }; }); function arrRemove(arr, item) { if (arr) { var index = arr.indexOf(item); 0 <= index && arr.splice(index, 1); } } var Subscription = (function () { function Subscription(initialTeardown) { this.initialTeardown = initialTeardown; this.closed = false; this._parentage = null; this._finalizers = null; } Subscription.prototype.unsubscribe = function () { var e_1, _a, e_2, _b; var errors; if (!this.closed) { this.closed = true; var _parentage = this._parentage; if (_parentage) { this._parentage = null; if (Array.isArray(_parentage)) { try { for (var _parentage_1 = __values(_parentage), _parentage_1_1 = _parentage_1.next(); !_parentage_1_1.done; _parentage_1_1 = _parentage_1.next()) { var parent_1 = _parentage_1_1.value; parent_1.remove(this); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_parentage_1_1 && !_parentage_1_1.done && (_a = _parentage_1.return)) _a.call(_parentage_1); } finally { if (e_1) throw e_1.error; } } } else { _parentage.remove(this); } } var initialFinalizer = this.initialTeardown; if (isFunction(initialFinalizer)) { try { initialFinalizer(); } catch (e) { errors = e instanceof UnsubscriptionError ? e.errors : [e]; } } var _finalizers = this._finalizers; if (_finalizers) { this._finalizers = null; try { for (var _finalizers_1 = __values(_finalizers), _finalizers_1_1 = _finalizers_1.next(); !_finalizers_1_1.done; _finalizers_1_1 = _finalizers_1.next()) { var finalizer = _finalizers_1_1.value; try { execFinalizer(finalizer); } catch (err) { errors = errors !== null && errors !== void 0 ? errors : []; if (err instanceof UnsubscriptionError) { errors = __spreadArray(__spreadArray([], __read(errors)), __read(err.errors)); } else { errors.push(err); } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_finalizers_1_1 && !_finalizers_1_1.done && (_b = _finalizers_1.return)) _b.call(_finalizers_1); } finally { if (e_2) throw e_2.error; } } } if (errors) { throw new UnsubscriptionError(errors); } } }; Subscription.prototype.add = function (teardown) { var _a; if (teardown && teardown !== this) { if (this.closed) { execFinalizer(teardown); } else { if (teardown instanceof Subscription) { if (teardown.closed || teardown._hasParent(this)) { return; } teardown._addParent(this); } (this._finalizers = (_a = this._finalizers) !== null && _a !== void 0 ? _a : []).push(teardown); } } }; Subscription.prototype._hasParent = function (parent) { var _parentage = this._parentage; return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent)); }; Subscription.prototype._addParent = function (parent) { var _parentage = this._parentage; this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent; }; Subscription.prototype._removeParent = function (parent) { var _parentage = this._parentage; if (_parentage === parent) { this._parentage = null; } else if (Array.isArray(_parentage)) { arrRemove(_parentage, parent); } }; Subscription.prototype.remove = function (teardown) { var _finalizers = this._finalizers; _finalizers && arrRemove(_finalizers, teardown); if (teardown instanceof Subscription) { teardown._removeParent(this); } }; Subscription.EMPTY = (function () { var empty = new Subscription(); empty.closed = true; return empty; })(); return Subscription; }()); var EMPTY_SUBSCRIPTION = Subscription.EMPTY; function isSubscription(value) { return (value instanceof Subscription || (value && "closed" in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))); } function execFinalizer(finalizer) { if (isFunction(finalizer)) { finalizer(); } else { finalizer.unsubscribe(); } } var config = { onUnhandledError: null, onStoppedNotification: null, Promise: undefined, useDeprecatedSynchronousErrorHandling: false, useDeprecatedNextContext: false, }; var timeoutProvider = { setTimeout: function (handler, timeout) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } return setTimeout.apply(void 0, __spreadArray([handler, timeout], __read(args))); }, clearTimeout: function (handle) { return (clearTimeout)(handle); }, delegate: undefined, }; function reportUnhandledError(err) { timeoutProvider.setTimeout(function () { { throw err; } }); } function noop() { } function errorContext(cb) { { cb(); } } var Subscriber = (function (_super) { __extends(Subscriber, _super); function Subscriber(destination) { var _this = _super.call(this) || this; _this.isStopped = false; if (destination) { _this.destination = destination; if (isSubscription(destination)) { destination.add(_this); } } else { _this.destination = EMPTY_OBSERVER; } return _this; } Subscriber.create = function (next, error, complete) { return new SafeSubscriber(next, error, complete); }; Subscriber.prototype.next = function (value) { if (this.isStopped) ; else { this._next(value); } }; Subscriber.prototype.error = function (err) { if (this.isStopped) ; else { this.isStopped = true; this._error(err); } }; Subscriber.prototype.complete = function () { if (this.isStopped) ; else { this.isStopped = true; this._complete(); } }; Subscriber.prototype.unsubscribe = function () { if (!this.closed) { this.isStopped = true; _super.prototype.unsubscribe.call(this); this.destination = null; } }; Subscriber.prototype._next = function (value) { this.destination.next(value); }; Subscriber.prototype._error = function (err) { try { this.destination.error(err); } finally { this.unsubscribe(); } }; Subscriber.prototype._complete = function () { try { this.destination.complete(); } finally { this.unsubscribe(); } }; return Subscriber; }(Subscription)); var _bind = Function.prototype.bind; function bind(fn, thisArg) { return _bind.call(fn, thisArg); } var ConsumerObserver = (function () { function ConsumerObserver(partialObserver) { this.partialObserver = partialObserver; } ConsumerObserver.prototype.next = function (value) { var partialObserver = this.partialObserver; if (partialObserver.next) { try { partialObserver.next(value); } catch (error) { handleUnhandledError(error); } } }; ConsumerObserver.prototype.error = function (err) { var partialObserver = this.partialObserver; if (partialObserver.error) { try { partialObserver.error(err); } catch (error) { handleUnhandledError(error); } } else { handleUnhandledError(err); } }; ConsumerObserver.prototype.complete = function () { var partialObserver = this.partialObserver; if (partialObserver.complete) { try { partialObserver.complete(); } catch (error) { handleUnhandledError(error); } } }; return ConsumerObserver; }()); var SafeSubscriber = (function (_super) { __extends(SafeSubscriber, _super); function SafeSubscriber(observerOrNext, error, complete) { var _this = _super.call(this) || this; var partialObserver; if (isFunction(observerOrNext) || !observerOrNext) { partialObserver = { next: (observerOrNext !== null && observerOrNext !== void 0 ? observerOrNext : undefined), error: error !== null && error !== void 0 ? error : undefined, complete: complete !== null && complete !== void 0 ? complete : undefined, }; } else { var context_1; if (_this && config.useDeprecatedNextContext) { context_1 = Object.create(observerOrNext); context_1.unsubscribe = function () { return _this.unsubscribe(); }; partialObserver = { next: observerOrNext.next && bind(observerOrNext.next, context_1), error: observerOrNext.error && bind(observerOrNext.error, context_1), complete: observerOrNext.complete && bind(observerOrNext.complete, context_1), }; } else { partialObserver = observerOrNext; } } _this.destination = new ConsumerObserver(partialObserver); return _this; } return SafeSubscriber; }(Subscriber)); function handleUnhandledError(error) { { reportUnhandledError(error); } } function defaultErrorHandler(err) { throw err; } var EMPTY_OBSERVER = { closed: true, next: noop, error: defaultErrorHandler, complete: noop, }; var observable = (function () { return (typeof Symbol === "function" && Symbol.observable) || "@@observable"; })(); function identity(x) { return x; } function pipeFromArray(fns) { if (fns.length === 0) { return identity; } if (fns.length === 1) { return fns[0]; } return function piped(input) { return fns.reduce(function (prev, fn) { return fn(prev); }, input); }; } var Observable = (function () { function Observable(subscribe) { if (subscribe) { this._subscribe = subscribe; } } Observable.prototype.lift = function (operator) { var observable = new Observable(); observable.source = this; observable.operator = operator; return observable; }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { var _this = this; var subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete); errorContext(function () { var _a = _this, operator = _a.operator, source = _a.source; subscriber.add(operator ? operator.call(subscriber, source) : source ? _this._subscribe(subscriber) : _this._trySubscribe(subscriber)); }); return subscriber; }; Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe(sink); } catch (err) { sink.error(err); } }; Observable.prototype.forEach = function (next, promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var subscriber = new SafeSubscriber({ next: function (value) { try { next(value); } catch (err) { reject(err); subscriber.unsubscribe(); } }, error: reject, complete: resolve, }); _this.subscribe(subscriber); }); }; Observable.prototype._subscribe = function (subscriber) { var _a; return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber); }; Observable.prototype[observable] = function () { return this; }; Observable.prototype.pipe = function () { var operations = []; for (var _i = 0; _i < arguments.length; _i++) { operations[_i] = arguments[_i]; } return pipeFromArray(operations)(this); }; Observable.prototype.toPromise = function (promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var value; _this.subscribe(function (x) { return (value = x); }, function (err) { return reject(err); }, function () { return resolve(value); }); }); }; Observable.create = function (subscribe) { return new Observable(subscribe); }; return Observable; }()); function getPromiseCtor(promiseCtor) { var _a; return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : config.Promise) !== null && _a !== void 0 ? _a : Promise; } function isObserver(value) { return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete); } function isSubscriber(value) { return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value)); } function hasLift(source) { return isFunction(source === null || source === void 0 ? void 0 : source.lift); } function operate(init) { return function (source) { if (hasLift(source)) { return source.lift(function (liftedSource) { try { return init(liftedSource, this); } catch (err) { this.error(err); } }); } throw new TypeError("Unable to lift unknown Observable type"); }; } function createOperatorSubscriber(destination, onNext, onComplete, onError, onFinalize) { return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize); } var OperatorSubscriber = (function (_super) { __extends(OperatorSubscriber, _super); function OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize, shouldUnsubscribe) { var _this = _super.call(this, destination) || this; _this.onFinalize = onFinalize; _this.shouldUnsubscribe = shouldUnsubscribe; _this._next = onNext ? function (value) { try { onNext(value); } catch (err) { destination.error(err); } } : _super.prototype._next; _this._error = onError ? function (err) { try { onError(err); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : _super.prototype._error; _this._complete = onComplete ? function () { try { onComplete(); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : _super.prototype._complete; return _this; } OperatorSubscriber.prototype.unsubscribe = function () { var _a; if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { var closed_1 = this.closed; _super.prototype.unsubscribe.call(this); !closed_1 && ((_a = this.onFinalize) === null || _a === void 0 ? void 0 : _a.call(this)); } }; return OperatorSubscriber; }(Subscriber)); function refCount() { return operate(function (source, subscriber) { var connection = null; source._refCount++; var refCounter = createOperatorSubscriber(subscriber, undefined, undefined, undefined, function () { if (!source || source._refCount <= 0 || 0 < --source._refCount) { connection = null; return; } var sharedConnection = source._connection; var conn = connection; connection = null; if (sharedConnection && (!conn || sharedConnection === conn)) { sharedConnection.unsubscribe(); } subscriber.unsubscribe(); }); source.subscribe(refCounter); if (!refCounter.closed) { connection = source.connect(); } }); } var ConnectableObservable = (function (_super) { __extends(ConnectableObservable, _super); function ConnectableObservable(source, subjectFactory) { var _this = _super.call(this) || this; _this.source = source; _this.subjectFactory = subjectFactory; _this._subject = null; _this._refCount = 0; _this._connection = null; if (hasLift(source)) { _this.lift = source.lift; } return _this; } ConnectableObservable.prototype._subscribe = function (subscriber) { return this.getSubject().subscribe(subscriber); }; ConnectableObservable.prototype.getSubject = function () { var subject = this._subject; if (!subject || subject.isStopped) { this._subject = this.subjectFactory(); } return this._subject; }; ConnectableObservable.prototype._teardown = function () { this._refCount = 0; var _connection = this._connection; this._subject = this._connection = null; _connection === null || _connection === void 0 ? void 0 : _connection.unsubscribe(); }; ConnectableObservable.prototype.connect = function () { var _this = this; var connection = this._connection; if (!connection) { connection = this._connection = new Subscription(); var subject_1 = this.getSubject(); connection.add(this.source.subscribe(createOperatorSubscriber(subject_1, undefined, function () { _this._teardown(); subject_1.complete(); }, function (err) { _this._teardown(); subject_1.error(err); }, function () { return _this._teardown(); }))); if (connection.closed) { this._connection = null; connection = Subscription.EMPTY; } } return connection; }; ConnectableObservable.prototype.refCount = function () { return refCount()(this); }; return ConnectableObservable; }(Observable)); var performanceTimestampProvider = { now: function () { return (performanceTimestampProvider.delegate || performance).now(); }, delegate: undefined, }; var animationFrameProvider = { schedule: function (callback) { var request = requestAnimationFrame; var cancel = cancelAnimationFrame; var handle = request(function (timestamp) { cancel = undefined; callback(timestamp); }); return new Subscription(function () { return cancel === null || cancel === void 0 ? void 0 : cancel(handle); }); }, requestAnimationFrame: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var delegate = animationFrameProvider.delegate; return ((delegate === null || delegate === void 0 ? void 0 : delegate.requestAnimationFrame) || requestAnimationFrame).apply(void 0, __spreadArray([], __read(args))); }, cancelAnimationFrame: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return (cancelAnimationFrame).apply(void 0, __spreadArray([], __read(args))); }, delegate: undefined, }; function animationFramesFactory(timestampProvider) { var schedule = animationFrameProvider.schedule; return new Observable(function (subscriber) { var subscription = new Subscription(); var provider = timestampProvider || performanceTimestampProvider; var start = provider.now(); var run = function (timestamp) { var now = provider.now(); subscriber.next({ timestamp: timestampProvider ? now : timestamp, elapsed: now - start, }); if (!subscriber.closed) { subscription.add(schedule(run)); } }; subscription.add(schedule(run)); return subscription; }); } animationFramesFactory(); var ObjectUnsubscribedError = createErrorClass(function (_super) { return function ObjectUnsubscribedErrorImpl() { _super(this); this.name = "ObjectUnsubscribedError"; this.message = "object unsubscribed"; }; }); var Subject = (function (_super) { __extends(Subject, _super); function Subject() { var _this = _super.call(this) || this; _this.closed = false; _this.currentObservers = null; _this.observers = []; _this.isStopped = false; _this.hasError = false; _this.thrownError = null; return _this; } Subject.prototype.lift = function (operator) { var subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; }; Subject.prototype._throwIfClosed = function () { if (this.closed) { throw new ObjectUnsubscribedError(); } }; Subject.prototype.next = function (value) { var _this = this; errorContext(function () { var e_1, _a; _this._throwIfClosed(); if (!_this.isStopped) { if (!_this.currentObservers) { _this.currentObservers = Array.from(_this.observers); } try { for (var _b = __values(_this.currentObservers), _c = _b.next(); !_c.done; _c = _b.next()) { var observer = _c.value; observer.next(value); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } } }); }; Subject.prototype.error = function (err) { var _this = this; errorContext(function () { _this._throwIfClosed(); if (!_this.isStopped) { _this.hasError = _this.isStopped = true; _this.thrownError = err; var observers = _this.observers; while (observers.length) { observers.shift().error(err); } } }); }; Subject.prototype.complete = function () { var _this = this; errorContext(function () { _this._throwIfClosed(); if (!_this.isStopped) { _this.isStopped = true; var observers = _this.observers; while (observers.length) { observers.shift().complete(); } } }); }; Subject.prototype.unsubscribe = function () { this.isStopped = this.closed = true; this.observers = this.currentObservers = null; }; Object.defineProperty(Subject.prototype, "observed", { get: function () { var _a; return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0; }, enumerable: false, configurable: true }); Subject.prototype._trySubscribe = function (subscriber) { this._throwIfClosed(); return _super.prototype._trySubscribe.call(this, subscriber); }; Subject.prototype._subscribe = function (subscriber) { this._throwIfClosed(); this._checkFinalizedStatuses(subscriber); return this._innerSubscribe(subscriber); }; Subject.prototype._innerSubscribe = function (subscriber) { var _this = this; var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers; if (hasError || isStopped) { return EMPTY_SUBSCRIPTION; } this.currentObservers = null; observers.push(subscriber); return new Subscription(function () { _this.currentObservers = null; arrRemove(observers, subscriber); }); }; Subject.prototype._checkFinalizedStatuses = function (subscriber) { var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped; if (hasError) { subscriber.error(thrownError); } else if (isStopped) { subscriber.complete(); } }; Subject.prototype.asObservable = function () { var observable = new Observable(); observable.source = this; return observable; }; Subject.create = function (destination, source) { return new AnonymousSubject(destination, source); }; return Subject; }(Observable)); var AnonymousSubject = (function (_super) { __extends(AnonymousSubject, _super); function AnonymousSubject(destination, source) { var _this = _super.call(this) || this; _this.destination = destination; _this.source = source; return _this; } AnonymousSubject.prototype.next = function (value) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value); }; AnonymousSubject.prototype.error = function (err) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err); }; AnonymousSubject.prototype.complete = function () { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a); }; AnonymousSubject.prototype._subscribe = function (subscriber) { var _a, _b; return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : EMPTY_SUBSCRIPTION; }; return AnonymousSubject; }(Subject)); var BehaviorSubject = (function (_super) { __extends(BehaviorSubject, _super); function BehaviorSubject(_value) { var _this = _super.call(this) || this; _this._value = _value; return _this; } Object.defineProperty(BehaviorSubject.prototype, "value", { get: function () { return this.getValue(); }, enumerable: false, configurable: true }); BehaviorSubject.prototype._subscribe = function (subscriber) { var subscription = _super.prototype._subscribe.call(this, subscriber); !subscription.closed && subscriber.next(this._value); return subscription; }; BehaviorSubject.prototype.getValue = function () { var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, _value = _a._value; if (hasError) { throw thrownError; } this._throwIfClosed(); return _value; }; BehaviorSubject.prototype.next = function (value) { _super.prototype.next.call(this, (this._value = value)); }; return BehaviorSubject; }(Subject)); var dateTimestampProvider = { now: function () { return (dateTimestampProvider.delegate || Date).now(); }, delegate: undefined, }; var ReplaySubject = (function (_super) { __extends(ReplaySubject, _super); function ReplaySubject(_bufferSize, _windowTime, _timestampProvider) { if (_bufferSize === void 0) { _bufferSize = Infinity; } if (_windowTime === void 0) { _windowTime = Infinity; } if (_timestampProvider === void 0) { _timestampProvider = dateTimestampProvider; } var _this = _super.call(this) || this; _this._bufferSize = _bufferSize; _this._windowTime = _windowTime; _this._timestampProvider = _timestampProvider; _this._buffer = []; _this._infiniteTimeWindow = true; _this._infiniteTimeWindow = _windowTime === Infinity; _this._bufferSize = Math.max(1, _bufferSize); _this._windowTime = Math.max(1, _windowTime); return _this; } ReplaySubject.prototype.next = function (value) { var _a = this, isStopped = _a.isStopped, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow, _timestampProvider = _a._timestampProvider, _windowTime = _a._windowTime; if (!isStopped) { _buffer.push(value); !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime); } this._trimBuffer(); _super.prototype.next.call(this, value); }; ReplaySubject.prototype._subscribe = function (subscriber) { this._throwIfClosed(); this._trimBuffer(); var subscription = this._innerSubscribe(subscriber); var _a = this, _infiniteTimeWindow = _a._infiniteTimeWindow, _buffer = _a._buffer; var copy = _buffer.slice(); for (var i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) { subscriber.next(copy[i]); } this._checkFinalizedStatuses(subscriber); return subscription; }; ReplaySubject.prototype._trimBuffer = function () { var _a = this, _bufferSize = _a._bufferSize, _timestampProvider = _a._timestampProvider, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow; var adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize; _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize); if (!_infiniteTimeWindow) { var now = _timestampProvider.now(); var last = 0; for (var i = 1; i < _buffer.length && _buffer[i] <= now; i += 2) { last = i; } last && _buffer.splice(0, last + 1); } }; return ReplaySubject; }(Subject)); ((function (_super) { __extends(AsyncSubject, _super); function AsyncSubject() { var _this = _super !== null && _super.apply(this, arguments) || this; _this._value = null; _this._hasValue = false; _this._isComplete = false; return _this; } AsyncSubject.prototype._checkFinalizedStatuses = function (subscriber) { var _a = this, hasError = _a.hasError, _hasValue = _a._hasValue, _value = _a._value, thrownError = _a.thrownError, isStopped = _a.isStopped, _isComplete = _a._isComplete; if (hasError) { subscriber.error(thrownError); } else if (isStopped || _isComplete) { _hasValue && subscriber.next(_value); subscriber.complete(); } }; AsyncSubject.prototype.next = function (value) { if (!this.isStopped) { this._value = value; this._hasValue = true; } }; AsyncSubject.prototype.complete = function () { var _a = this, _hasValue = _a._hasValue, _value = _a._value, _isComplete = _a._isComplete; if (!_isComplete) { this._isComplete = true; _hasValue && _super.prototype.next.call(this, _value); _super.prototype.complete.call(this); } }; return AsyncSubject; })(Subject)); var Action = (function (_super) { __extends(Action, _super); function Action(scheduler, work) { return _super.call(this) || this; } Action.prototype.schedule = function (state, delay) { return this; }; return Action; }(Subscription)); var intervalProvider = { setInterval: function (handler, timeout) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } return setInterval.apply(void 0, __spreadArray([handler, timeout], __read(args))); }, clearInterval: function (handle) { return (clearInterval)(handle); }, delegate: undefined, }; var AsyncAction = (function (_super) { __extends(AsyncAction, _super); function AsyncAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; _this.pending = false; return _this; } AsyncAction.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } if (this.closed) { return this; } this.state = state; var id = this.id; var scheduler = this.scheduler; if (id != null) { this.id = this.recycleAsyncId(scheduler, id, delay); } this.pending = true; this.delay = delay; this.id = this.id || this.requestAsyncId(scheduler, this.id, delay); return this; }; AsyncAction.prototype.requestAsyncId = function (scheduler, _id, delay) { if (delay === void 0) { delay = 0; } return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay); }; AsyncAction.prototype.recycleAsyncId = function (_scheduler, id, delay) { if (delay === void 0) { delay = 0; } if (delay != null && this.delay === delay && this.pending === false) { return id; } intervalProvider.clearInterval(id); return undefined; }; AsyncAction.prototype.execute = function (state, delay) { if (this.closed) { return new Error("executing a cancelled action"); } this.pending = false; var error = this._execute(state, delay); if (error) { return error; } else if (this.pending === false && this.id != null) { this.id = this.recycleAsyncId(this.scheduler, this.id, null); } }; AsyncAction.prototype._execute = function (state, _delay) { var errored = false; var errorValue; try { this.work(state); } catch (e) { errored = true; errorValue = e ? e : new Error("Scheduled action threw falsy error"); } if (errored) { this.unsubscribe(); return errorValue; } }; AsyncAction.prototype.unsubscribe = function () { if (!this.closed) { var _a = this, id = _a.id, scheduler = _a.scheduler; var actions = scheduler.actions; this.work = this.state = this.scheduler = null; this.pending = false; arrRemove(actions, this); if (id != null) { this.id = this.recycleAsyncId(scheduler, id, null); } this.delay = null; _super.prototype.unsubscribe.call(this); } }; return AsyncAction; }(Action)); var nextHandle = 1; var resolved; var activeHandles = {}; function findAndClearHandle(handle) { if (handle in activeHandles) { delete activeHandles[handle]; return true; } return false; } var Immediate = { setImmediate: function (cb) { var handle = nextHandle++; activeHandles[handle] = true; if (!resolved) { resolved = Promise.resolve(); } resolved.then(function () { return findAndClearHandle(handle) && cb(); }); return handle; }, clearImmediate: function (handle) { findAndClearHandle(handle); }, }; var setImmediate = Immediate.setImmediate, clearImmediate = Immediate.clearImmediate; var immediateProvider = { setImmediate: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var delegate = immediateProvider.delegate; return ((delegate === null || delegate === void 0 ? void 0 : delegate.setImmediate) || setImmediate).apply(void 0, __spreadArray([], __read(args))); }, clearImmediate: function (handle) { return (clearImmediate)(handle); }, delegate: undefined, }; var AsapAction = (function (_super) { __extends(AsapAction, _super); function AsapAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; return _this; } AsapAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } if (delay !== null && delay > 0) { return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); } scheduler.actions.push(this); return scheduler._scheduled || (scheduler._scheduled = immediateProvider.setImmediate(scheduler.flush.bind(scheduler, undefined))); }; AsapAction.prototype.recycleAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) { return _super.prototype.recycleAsyncId.call(this, scheduler, id, delay); } if (!scheduler.actions.some(function (action) { return action.id === id; })) { immediateProvider.clearImmediate(id); scheduler._scheduled = undefined; } return undefined; }; return AsapAction; }(AsyncAction)); var Scheduler = (function () { function Scheduler(schedulerActionCtor, now) { if (now === void 0) { now = Scheduler.now; } this.schedulerActionCtor = schedulerActionCtor; this.now = now; } Scheduler.prototype.schedule = function (work, delay, state) { if (delay === void 0) { delay = 0; } return new this.schedulerActionCtor(this, work).schedule(state, delay); }; Scheduler.now = dateTimestampProvider.now; return Scheduler; }()); var AsyncScheduler = (function (_super) { __extends(AsyncScheduler, _super); function AsyncScheduler(SchedulerAction, now) { if (now === void 0) { now = Scheduler.now; } var _this = _super.call(this, SchedulerAction, now) || this; _this.actions = []; _this._active = false; _this._scheduled = undefined; return _this; } AsyncScheduler.prototype.flush = function (action) { var actions = this.actions; if (this._active) { actions.push(action); return; } var error; this._active = true; do { if ((error = action.execute(action.state, action.delay))) { break; } } while ((action = actions.shift())); this._active = false; if (error) { while ((action = actions.shift())) { action.unsubscribe(); } throw error; } }; return AsyncScheduler; }(Scheduler)); var AsapScheduler = (function (_super) { __extends(AsapScheduler, _super); function AsapScheduler() { return _super !== null && _super.apply(this, arguments) || this; } AsapScheduler.prototype.flush = function (action) { this._active = true; var flushId = this._scheduled; this._scheduled = undefined; var actions = this.actions; var error; action = action || actions.shift(); do { if ((error = action.execute(action.state, action.delay))) { break; } } while ((action = actions[0]) && action.id === flushId && actions.shift()); this._active = false; if (error) { while ((action = actions[0]) && action.id === flushId && actions.shift()) { action.unsubscribe(); } throw error; } }; return AsapScheduler; }(AsyncScheduler)); new AsapScheduler(AsapAction); var asyncScheduler = new AsyncScheduler(AsyncAction); var async = asyncScheduler; var QueueAction = (function (_super) { __extends(QueueAction, _super); function QueueAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; return _this; } QueueAction.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } if (delay > 0) { return _super.prototype.schedule.call(this, state, delay); } this.delay = delay; this.state = state; this.scheduler.flush(this); return this; }; QueueAction.prototype.execute = function (state, delay) { return (delay > 0 || this.closed) ? _super.prototype.execute.call(this, state, delay) : this._execute(state, delay); }; QueueAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) { return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); } return scheduler.flush(this); }; return QueueAction; }(AsyncAction)); var QueueScheduler = (function (_super) { __extends(QueueScheduler, _super); function QueueScheduler() { return _super !== null && _super.apply(this, arguments) || this; } return QueueScheduler; }(AsyncScheduler)); new QueueScheduler(QueueAction); var AnimationFrameAction = (function (_super) { __extends(AnimationFrameAction, _super); function AnimationFrameAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; return _this; } AnimationFrameAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } if (delay !== null && delay > 0) { return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); } scheduler.actions.push(this); return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(function () { return scheduler.flush(undefined); })); }; AnimationFrameAction.prototype.recycleAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) { return _super.prototype.recycleAsyncId.call(this, scheduler, id, delay); } if (!scheduler.actions.some(function (action) { return action.id === id; })) { animationFrameProvider.cancelAnimationFrame(id); scheduler._scheduled = undefined; } return undefined; }; return AnimationFrameAction; }(AsyncAction)); var AnimationFrameScheduler = (function (_super) { __extends(AnimationFrameScheduler, _super); function AnimationFrameScheduler() { return _super !== null && _super.apply(this, arguments) || this; } AnimationFrameScheduler.prototype.flush = function (action) { this._active = true; var flushId = this._scheduled; this._scheduled = undefined; var actions = this.actions; var error; action = action || actions.shift(); do { if ((error = action.execute(action.state, action.delay))) { break; } } while ((action = actions[0]) && action.id === flushId && actions.shift()); this._active = false; if (error) { while ((action = actions[0]) && action.id === flushId && actions.shift()) { action.unsubscribe(); } throw error; } }; return AnimationFrameScheduler; }(AsyncScheduler)); new AnimationFrameScheduler(AnimationFrameAction); ((function (_super) { __extends(VirtualTimeScheduler, _super); function VirtualTimeScheduler(schedulerActionCtor, maxFrames) { if (schedulerActionCtor === void 0) { schedulerActionCtor = VirtualAction; } if (maxFrames === void 0) { maxFrames = Infinity; } var _this = _super.call(this, schedulerActionCtor, function () { return _this.frame; }) || this; _this.maxFrames = maxFrames; _this.frame = 0; _this.index = -1; return _this; } VirtualTimeScheduler.prototype.flush = function () { var _a = this, actions = _a.actions, maxFrames = _a.maxFrames; var error; var action; while ((action = actions[0]) && action.delay <= maxFrames) { actions.shift(); this.frame = action.delay; if ((error = action.execute(action.state, action.delay))) { break; } } if (error) { while ((action = actions.shift())) { action.unsubscribe(); } throw error; } }; VirtualTimeScheduler.frameTimeFactor = 10; return VirtualTimeScheduler; })(AsyncScheduler)); var VirtualAction = (function (_super) { __extends(VirtualAction, _super); function VirtualAction(scheduler, work, index) { if (index === void 0) { index = (scheduler.index += 1); } var _this = _super.call(this, scheduler, work) || this; _this.scheduler = scheduler; _this.work = work; _this.index = index; _this.active = true; _this.index = scheduler.index = index; return _this; } VirtualAction.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } if (Number.isFinite(delay)) { if (!this.id) { return _super.prototype.schedule.call(this, state, delay); } this.active = false; var action = new VirtualAction(this.scheduler, this.work); this.add(action); return action.schedule(state, delay); } else { return Subscription.EMPTY; } }; VirtualAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } this.delay = scheduler.frame + delay; var actions = scheduler.actions; actions.push(this); actions.sort(VirtualAction.sortActions); return true; }; VirtualAction.prototype.recycleAsyncId = function (scheduler, id, delay) { return undefined; }; VirtualAction.prototype._execute = function (state, delay) { if (this.active === true) { return _super.prototype._execute.call(this, state, delay); } }; VirtualAction.sortActions = function (a, b) { if (a.delay === b.delay) { if (a.index === b.index) { return 0; } else if (a.index > b.index) { return 1; } else { return -1; } } else if (a.delay > b.delay) { return 1; } else { return -1; } }; return VirtualAction; }(AsyncAction)); var EMPTY$1 = new Observable(function (subscriber) { return subscriber.complete(); }); function empty(scheduler) { return scheduler ? emptyScheduled(scheduler) : EMPTY$1; } function emptyScheduled(scheduler) { return new Observable(function (subscriber) { return scheduler.schedule(function () { return subscriber.complete(); }); }); } function isScheduler(value) { return value && isFunction(value.schedule); } function last$1(arr) { return arr[arr.length - 1]; } function popResultSelector(args) { return isFunction(last$1(args)) ? args.pop() : undefined; } function popScheduler(args) { return isScheduler(last$1(args)) ? args.pop() : undefined; } function popNumber(args, defaultValue) { return typeof last$1(args) === "number" ? args.pop() : defaultValue; } var isArrayLike = (function (x) { return x && typeof x.length === "number" && typeof x !== "function"; }); function isPromise(value) { return isFunction(value === null || value === void 0 ? void 0 : value.then); } function isInteropObservable(input) { return isFunction(input[observable]); } function isAsyncIterable(obj) { return Symbol.asyncIterator && isFunction(obj === null || obj === void 0 ? void 0 : obj[Symbol.asyncIterator]); } function createInvalidObservableTypeError(input) { return new TypeError("You provided " + (input !== null && typeof input === "object" ? "an invalid object" : """ + input + """) + " where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable."); } function getSymbolIterator() { if (typeof Symbol !== "function" || !Symbol.iterator) { return "@@iterator"; } return Symbol.iterator; } var iterator = getSymbolIterator(); function isIterable(input) { return isFunction(input === null || input === void 0 ? void 0 : input[iterator]); } function readableStreamLikeToAsyncGenerator(readableStream) { return __asyncGenerator(this, arguments, function readableStreamLikeToAsyncGenerator_1() { var reader, _a, value, done; return __generator(this, function (_b) { switch (_b.label) { case 0: reader = readableStream.getReader(); _b.label = 1; case 1: _b.trys.push([1, , 9, 10]); _b.label = 2; case 2: return [4, __await(reader.read())]; case 3: _a = _b.sent(), value = _a.value, done = _a.done; if (!done) return [3, 5]; return [4, __await(void 0)]; case 4: return [2, _b.sent()]; case 5: return [4, __await(value)]; case 6: return [4, _b.sent()]; case 7: _b.sent(); return [3, 2]; case 8: return [3, 10]; case 9: reader.releaseLock(); return [7]; case 10: return [2]; } }); }); } function isReadableStreamLike(obj) { return isFunction(obj === null || obj === void 0 ? void 0 : obj.getReader); } function innerFrom(input) { if (input instanceof Observable) { return input; } if (input != null) { if (isInteropObservable(input)) { return fromInteropObservable(input); } if (isArrayLike(input)) { return fromArrayLike(input); } if (isPromise(input)) { return fromPromise(input); } if (isAsyncIterable(input)) { return fromAsyncIterable(input); } if (isIterable(input)) { return fromIterable(input); } if (isReadableStreamLike(input)) { return fromReadableStreamLike(input); } } throw createInvalidObservableTypeError(input); } function fromInteropObservable(obj) { return new Observable(function (subscriber) { var obs = obj[observable](); if (isFunction(obs.subscribe)) { return obs.subscribe(subscriber); } throw new TypeError("Provided object does not correctly implement Symbol.observable"); }); } function fromArrayLike(array) { return new Observable(function (subscriber) { for (var i = 0; i < array.length && !subscriber.closed; i++) { subscriber.next(array[i]); } subscriber.complete(); }); } function fromPromise(promise) { return new Observable(function (subscriber) { promise .then(function (value) { if (!subscriber.closed) { subscriber.next(value); subscriber.complete(); } }, function (err) { return subscriber.error(err); }) .then(null, reportUnhandledError); }); } function fromIterable(iterable) { return new Observable(function (subscriber) { var e_1, _a; try { for (var iterable_1 = __values(iterable), iterable_1_1 = iterable_1.next(); !iterable_1_1.done; iterable_1_1 = iterable_1.next()) { var value = iterable_1_1.value; subscriber.next(value); if (subscriber.closed) { return; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (iterable_1_1 && !iterable_1_1.done && (_a = iterable_1.return)) _a.call(iterable_1); } finally { if (e_1) throw e_1.error; } } subscriber.complete(); }); } function fromAsyncIterable(asyncIterable) { return new Observable(function (subscriber) { process(asyncIterable, subscriber).catch(function (err) { return subscriber.error(err); }); }); } function fromReadableStreamLike(readableStream) { return fromAsyncIterable(readableStreamLikeToAsyncGenerator(readableStream)); } function process(asyncIterable, subscriber) { var asyncIterable_1, asyncIterable_1_1; var e_2, _a; return __awaiter(this, void 0, void 0, function () { var value, e_2_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 5, 6, 11]); asyncIterable_1 = __asyncValues(asyncIterable); _b.label = 1; case 1: return [4, asyncIterable_1.next()]; case 2: if (!(asyncIterable_1_1 = _b.sent(), !asyncIterable_1_1.done)) return [3, 4]; value = asyncIterable_1_1.value; subscriber.next(value); if (subscriber.closed) { return [2]; } _b.label = 3; case 3: return [3, 1]; case 4: return [3, 11]; case 5: e_2_1 = _b.sent(); e_2 = { error: e_2_1 }; return [3, 11]; case 6: _b.trys.push([6, , 9, 10]); if (!(asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return))) return [3, 8]; return [4, _a.call(asyncIterable_1)]; case 7: _b.sent(); _b.label = 8; case 8: return [3, 10]; case 9: if (e_2) throw e_2.error; return [7]; case 10: return [7]; case 11: subscriber.complete(); return [2]; } }); }); } function executeSchedule(parentSubscription, scheduler, work, delay, repeat) { if (delay === void 0) { delay = 0; } if (repeat === void 0) { repeat = false; } var scheduleSubscription = scheduler.schedule(function () { work(); if (repeat) { parentSubscription.add(this.schedule(null, delay)); } else { this.unsubscribe(); } }, delay); parentSubscription.add(scheduleSubscription); if (!repeat) { return scheduleSubscription; } } function observeOn(scheduler, delay) { if (delay === void 0) { delay = 0; } return operate(function (source, subscriber) { source.subscribe(createOperatorSubscriber(subscriber, function (value) { return executeSchedule(subscriber, scheduler, function () { return subscriber.next(value); }, delay); }, function () { return executeSchedule(subscriber, scheduler, function () { return subscriber.complete(); }, delay); }, function (err) { return executeSchedule(subscriber, scheduler, function () { return subscriber.error(err); }, delay); })); }); } function subscribeOn(scheduler, delay) { if (delay === void 0) { delay = 0; } return operate(function (source, subscriber) { subscriber.add(scheduler.schedule(function () { return source.subscribe(subscriber); }, delay)); }); } function scheduleObservable(input, scheduler) { return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler)); } function schedulePromise(input, scheduler) { return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler)); } function scheduleArray(input, scheduler) { return new Observable(function (subscriber) { var i = 0; return scheduler.schedule(function () { if (i === input.length) { subscriber.complete(); } else { subscriber.next(input[i++]); if (!subscriber.closed) { this.schedule(); } } }); }); } function scheduleIterable(input, scheduler) { return new Observable(function (subscriber) { var iterator$1; executeSchedule(subscriber, scheduler, function () { iterator$1 = input[iterator](); executeSchedule(subscriber, scheduler, function () { var _a; var value; var done; try { (_a = iterator$1.next(), value = _a.value, done = _a.done); } catch (err) { subscriber.error(err); return; } if (done) { subscriber.complete(); } else { subscriber.next(value); } }, 0, true); }); return function () { return isFunction(iterator$1 === null || iterator$1 === void 0 ? void 0 : iterator$1.return) && iterator$1.return(); }; }); } function scheduleAsyncIterable(input, scheduler) { if (!input) { throw new Error("Iterable cannot be null"); } return new Observable(function (subscriber) { executeSchedule(subscriber, scheduler, function () { var iterator = input[Symbol.asyncIterator](); executeSchedule(subscriber, scheduler, function () { iterator.next().then(function (result) { if (result.done) { subscriber.complete(); } else { subscriber.next(result.value); } }); }, 0, true); }); }); } function scheduleReadableStreamLike(input, scheduler) { return scheduleAsyncIterable(readableStreamLikeToAsyncGenerator(input), scheduler); } function scheduled(input, scheduler) { if (input != null) { if (isInteropObservable(input)) { return scheduleObservable(input, scheduler); } if (isArrayLike(input)) { return scheduleArray(input, scheduler); } if (isPromise(input)) { return schedulePromise(input, scheduler); } if (isAsyncIterable(input)) { return scheduleAsyncIterable(input, scheduler); } if (isIterable(input)) { return scheduleIterable(input, scheduler); } if (isReadableStreamLike(input)) { return scheduleReadableStreamLike(input, scheduler); } } throw createInvalidObservableTypeError(input); } function from(input, scheduler) { return scheduler ? scheduled(input, scheduler) : innerFrom(input); } function of() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); return from(args, scheduler); } function throwError(errorOrErrorFactory, scheduler) { var errorFactory = isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function () { return errorOrErrorFactory; }; var init = function (subscriber) { return subscriber.error(errorFactory()); }; return new Observable(scheduler ? function (subscriber) { return scheduler.schedule(init, 0, subscriber); } : init); } var NotificationKind; (function (NotificationKind) { NotificationKind["NEXT"] = "N"; NotificationKind["ERROR"] = "E"; NotificationKind["COMPLETE"] = "C"; })(NotificationKind || (NotificationKind = {})); var EmptyError = createErrorClass(function (_super) { return function EmptyErrorImpl() { _super(this); this.name = "EmptyError"; this.message = "no elements in sequence"; }; }); createErrorClass(function (_super) { return function ArgumentOutOfRangeErrorImpl() { _super(this); this.name = "ArgumentOutOfRangeError"; this.message = "argument out of range"; }; }); createErrorClass(function (_super) { return function NotFoundErrorImpl(message) { _super(this); this.name = "NotFoundError"; this.message = message; }; }); createErrorClass(function (_super) { return function SequenceErrorImpl(message) { _super(this); this.name = "SequenceError"; this.message = message; }; }); function isValidDate(value) { return value instanceof Date && !isNaN(value); } var TimeoutError = createErrorClass(function (_super) { return function TimeoutErrorImpl(info) { if (info === void 0) { info = null; } _super(this); this.message = "Timeout has occurred"; this.name = "TimeoutError"; this.info = info; }; }); function timeout(config, schedulerArg) { var _a = (isValidDate(config) ? { first: config } : typeof config === "number" ? { each: config } : config), first = _a.first, each = _a.each, _b = _a.with, _with = _b === void 0 ? timeoutErrorFactory : _b, _c = _a.scheduler, scheduler = _c === void 0 ? schedulerArg !== null && schedulerArg !== void 0 ? schedulerArg : asyncScheduler : _c, _d = _a.meta, meta = _d === void 0 ? null : _d; if (first == null && each == null) { throw new TypeError("No timeout provided."); } return operate(function (source, subscriber) { var originalSourceSubscription; var timerSubscription; var lastValue = null; var seen = 0; var startTimer = function (delay) { timerSubscription = executeSchedule(subscriber, scheduler, function () { try { originalSourceSubscription.unsubscribe(); innerFrom(_with({ meta: meta, lastValue: lastValue, seen: seen, })).subscribe(subscriber); } catch (err) { subscriber.error(err); } }, delay); }; originalSourceSubscription = source.subscribe(createOperatorSubscriber(subscriber, function (value) { timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); seen++; subscriber.next((lastValue = value)); each > 0 && startTimer(each); }, undefined, undefined, function () { if (!(timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.closed)) { timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); } lastValue = null; })); !seen && startTimer(first != null ? (typeof first === "number" ? first : +first - scheduler.now()) : each); }); } function timeoutErrorFactory(info) { throw new TimeoutError(info); } function map(project, thisArg) { return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { subscriber.next(project.call(thisArg, value, index++)); })); }); } var isArray$6 = Array.isArray; function callOrApply(fn, args) { return isArray$6(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args); } function mapOneOrManyArgs(fn) { return map(function (args) { return callOrApply(fn, args); }); } var isArray$5 = Array.isArray; var getPrototypeOf = Object.getPrototypeOf, objectProto = Object.prototype, getKeys = Object.keys; function argsArgArrayOrObject(args) { if (args.length === 1) { var first_1 = args[0]; if (isArray$5(first_1)) { return { args: first_1, keys: null }; } if (isPOJO(first_1)) { var keys = getKeys(first_1); return { args: keys.map(function (key) { return first_1[key]; }), keys: keys, }; } } return { args: args, keys: null }; } function isPOJO(obj) { return obj && typeof obj === "object" && getPrototypeOf(obj) === objectProto; } function createObject(keys, values) { return keys.reduce(function (result, key, i) { return ((result[key] = values[i]), result); }, {}); } function combineLatest() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); var resultSelector = popResultSelector(args); var _a = argsArgArrayOrObject(args), observables = _a.args, keys = _a.keys; if (observables.length === 0) { return from([], scheduler); } var result = new Observable(combineLatestInit(observables, scheduler, keys ? function (values) { return createObject(keys, values); } : identity)); return resultSelector ? result.pipe(mapOneOrManyArgs(resultSelector)) : result; } function combineLatestInit(observables, scheduler, valueTransform) { if (valueTransform === void 0) { valueTransform = identity; } return function (subscriber) { maybeSchedule(scheduler, function () { var length = observables.length; var values = new Array(length); var active = length; var remainingFirstValues = length; var _loop_1 = function (i) { maybeSchedule(scheduler, function () { var source = from(observables[i], scheduler); var hasFirstValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { values[i] = value; if (!hasFirstValue) { hasFirstValue = true; remainingFirstValues--; } if (!remainingFirstValues) { subscriber.next(valueTransform(values.slice())); } }, function () { if (!--active) { subscriber.complete(); } })); }, subscriber); }; for (var i = 0; i < length; i++) { _loop_1(i); } }, subscriber); }; } function maybeSchedule(scheduler, execute, subscription) { if (scheduler) { executeSchedule(subscription, scheduler, execute); } else { execute(); } } function mergeInternals(source, subscriber, project, concurrent, onBeforeNext, expand, innerSubScheduler, additionalFinalizer) { var buffer = []; var active = 0; var index = 0; var isComplete = false; var checkComplete = function () { if (isComplete && !buffer.length && !active) { subscriber.complete(); } }; var outerNext = function (value) { return (active < concurrent ? doInnerSub(value) : buffer.push(value)); }; var doInnerSub = function (value) { expand && subscriber.next(value); active++; var innerComplete = false; innerFrom(project(value, index++)).subscribe(createOperatorSubscriber(subscriber, function (innerValue) { onBeforeNext === null || onBeforeNext === void 0 ? void 0 : onBeforeNext(innerValue); if (expand) { outerNext(innerValue); } else { subscriber.next(innerValue); } }, function () { innerComplete = true; }, undefined, function () { if (innerComplete) { try { active--; var _loop_1 = function () { var bufferedValue = buffer.shift(); if (innerSubScheduler) { executeSchedule(subscriber, innerSubScheduler, function () { return doInnerSub(bufferedValue); }); } else { doInnerSub(bufferedValue); } }; while (buffer.length && active < concurrent) { _loop_1(); } checkComplete(); } catch (err) { subscriber.error(err); } } })); }; source.subscribe(createOperatorSubscriber(subscriber, outerNext, function () { isComplete = true; checkComplete(); })); return function () { additionalFinalizer === null || additionalFinalizer === void 0 ? void 0 : additionalFinalizer(); }; } function mergeMap(project, resultSelector, concurrent) { if (concurrent === void 0) { concurrent = Infinity; } if (isFunction(resultSelector)) { return mergeMap(function (a, i) { return map(function (b, ii) { return resultSelector(a, b, i, ii); })(innerFrom(project(a, i))); }, concurrent); } else if (typeof resultSelector === "number") { concurrent = resultSelector; } return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent); }); } function mergeAll(concurrent) { if (concurrent === void 0) { concurrent = Infinity; } return mergeMap(identity, concurrent); } function concatAll() { return mergeAll(1); } function concat() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return concatAll()(from(args, popScheduler(args))); } var nodeEventEmitterMethods = ["addListener", "removeListener"]; var eventTargetMethods = ["addEventListener", "removeEventListener"]; var jqueryMethods = ["on", "off"]; function fromEvent(target, eventName, options, resultSelector) { if (isFunction(options)) { resultSelector = options; options = undefined; } if (resultSelector) { return fromEvent(target, eventName, options).pipe(mapOneOrManyArgs(resultSelector)); } var _a = __read(isEventTarget(target) ? eventTargetMethods.map(function (methodName) { return function (handler) { return target[methodName](eventName, handler, options); }; }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add = _a[0], remove = _a[1]; if (!add) { if (isArrayLike(target)) { return mergeMap(function (subTarget) { return fromEvent(subTarget, eventName, options); })(innerFrom(target)); } } if (!add) { throw new TypeError("Invalid event target"); } return new Observable(function (subscriber) { var handler = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return subscriber.next(1 < args.length ? args : args[0]); }; add(handler); return function () { return remove(handler); }; }); } function toCommonHandlerRegistry(target, eventName) { return function (methodName) { return function (handler) { return target[methodName](eventName, handler); }; }; } function isNodeStyleEventEmitter(target) { return isFunction(target.addListener) && isFunction(target.removeListener); } function isJQueryStyleEventEmitter(target) { return isFunction(target.on) && isFunction(target.off); } function isEventTarget(target) { return isFunction(target.addEventListener) && isFunction(target.removeEventListener); } function timer(dueTime, intervalOrScheduler, scheduler) { if (dueTime === void 0) { dueTime = 0; } if (scheduler === void 0) { scheduler = async; } var intervalDuration = -1; if (intervalOrScheduler != null) { if (isScheduler(intervalOrScheduler)) { scheduler = intervalOrScheduler; } else { intervalDuration = intervalOrScheduler; } } return new Observable(function (subscriber) { var due = isValidDate(dueTime) ? +dueTime - scheduler.now() : dueTime; if (due < 0) { due = 0; } var n = 0; return scheduler.schedule(function () { if (!subscriber.closed) { subscriber.next(n++); if (0 <= intervalDuration) { this.schedule(undefined, intervalDuration); } else { subscriber.complete(); } } }, due); }); } function merge() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var scheduler = popScheduler(args); var concurrent = popNumber(args, Infinity); var sources = args; return !sources.length ? EMPTY$1 : sources.length === 1 ? innerFrom(sources[0]) : mergeAll(concurrent)(from(sources, scheduler)); } new Observable(noop); var isArray$4 = Array.isArray; function argsOrArgArray(args) { return args.length === 1 && isArray$4(args[0]) ? args[0] : args; } function filter(predicate, thisArg) { return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { return predicate.call(thisArg, value, index++) && subscriber.next(value); })); }); } function zip() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var resultSelector = popResultSelector(args); var sources = argsOrArgArray(args); return sources.length ? new Observable(function (subscriber) { var buffers = sources.map(function () { return []; }); var completed = sources.map(function () { return false; }); subscriber.add(function () { buffers = completed = null; }); var _loop_1 = function (sourceIndex) { innerFrom(sources[sourceIndex]).subscribe(createOperatorSubscriber(subscriber, function (value) { buffers[sourceIndex].push(value); if (buffers.every(function (buffer) { return buffer.length; })) { var result = buffers.map(function (buffer) { return buffer.shift(); }); subscriber.next(resultSelector ? resultSelector.apply(void 0, __spreadArray([], __read(result))) : result); if (buffers.some(function (buffer, i) { return !buffer.length && completed[i]; })) { subscriber.complete(); } } }, function () { completed[sourceIndex] = true; !buffers[sourceIndex].length && subscriber.complete(); })); }; for (var sourceIndex = 0; !subscriber.closed && sourceIndex < sources.length; sourceIndex++) { _loop_1(sourceIndex); } return function () { buffers = completed = null; }; }) : EMPTY$1; } function audit(durationSelector) { return operate(function (source, subscriber) { var hasValue = false; var lastValue = null; var durationSubscriber = null; var isComplete = false; var endDuration = function () { durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe(); durationSubscriber = null; if (hasValue) { hasValue = false; var value = lastValue; lastValue = null; subscriber.next(value); } isComplete && subscriber.complete(); }; var cleanupDuration = function () { durationSubscriber = null; isComplete && subscriber.complete(); }; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; lastValue = value; if (!durationSubscriber) { innerFrom(durationSelector(value)).subscribe((durationSubscriber = createOperatorSubscriber(subscriber, endDuration, cleanupDuration))); } }, function () { isComplete = true; (!hasValue || !durationSubscriber || durationSubscriber.closed) && subscriber.complete(); })); }); } function auditTime(duration, scheduler) { if (scheduler === void 0) { scheduler = asyncScheduler; } return audit(function () { return timer(duration, scheduler); }); } function bufferCount(bufferSize, startBufferEvery) { if (startBufferEvery === void 0) { startBufferEvery = null; } startBufferEvery = startBufferEvery !== null && startBufferEvery !== void 0 ? startBufferEvery : bufferSize; return operate(function (source, subscriber) { var buffers = []; var count = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var e_1, _a, e_2, _b; var toEmit = null; if (count++ % startBufferEvery === 0) { buffers.push([]); } try { for (var buffers_1 = __values(buffers), buffers_1_1 = buffers_1.next(); !buffers_1_1.done; buffers_1_1 = buffers_1.next()) { var buffer = buffers_1_1.value; buffer.push(value); if (bufferSize <= buffer.length) { toEmit = toEmit !== null && toEmit !== void 0 ? toEmit : []; toEmit.push(buffer); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (buffers_1_1 && !buffers_1_1.done && (_a = buffers_1.return)) _a.call(buffers_1); } finally { if (e_1) throw e_1.error; } } if (toEmit) { try { for (var toEmit_1 = __values(toEmit), toEmit_1_1 = toEmit_1.next(); !toEmit_1_1.done; toEmit_1_1 = toEmit_1.next()) { var buffer = toEmit_1_1.value; arrRemove(buffers, buffer); subscriber.next(buffer); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (toEmit_1_1 && !toEmit_1_1.done && (_b = toEmit_1.return)) _b.call(toEmit_1); } finally { if (e_2) throw e_2.error; } } } }, function () { var e_3, _a; try { for (var buffers_2 = __values(buffers), buffers_2_1 = buffers_2.next(); !buffers_2_1.done; buffers_2_1 = buffers_2.next()) { var buffer = buffers_2_1.value; subscriber.next(buffer); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (buffers_2_1 && !buffers_2_1.done && (_a = buffers_2.return)) _a.call(buffers_2); } finally { if (e_3) throw e_3.error; } } subscriber.complete(); }, undefined, function () { buffers = null; })); }); } function bufferWhen(closingSelector) { return operate(function (source, subscriber) { var buffer = null; var closingSubscriber = null; var openBuffer = function () { closingSubscriber === null || closingSubscriber === void 0 ? void 0 : closingSubscriber.unsubscribe(); var b = buffer; buffer = []; b && subscriber.next(b); innerFrom(closingSelector()).subscribe((closingSubscriber = createOperatorSubscriber(subscriber, openBuffer, noop))); }; openBuffer(); source.subscribe(createOperatorSubscriber(subscriber, function (value) { return buffer === null || buffer === void 0 ? void 0 : buffer.push(value); }, function () { buffer && subscriber.next(buffer); subscriber.complete(); }, undefined, function () { return (buffer = closingSubscriber = null); })); }); } function catchError(selector) { return operate(function (source, subscriber) { var innerSub = null; var syncUnsub = false; var handledResult; innerSub = source.subscribe(createOperatorSubscriber(subscriber, undefined, undefined, function (err) { handledResult = innerFrom(selector(err, catchError(selector)(source))); if (innerSub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } else { syncUnsub = true; } })); if (syncUnsub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } }); } function scanInternals(accumulator, seed, hasSeed, emitOnNext, emitBeforeComplete) { return function (source, subscriber) { var hasState = hasSeed; var state = seed; var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var i = index++; state = hasState ? accumulator(state, value, i) : ((hasState = true), value); emitOnNext && subscriber.next(state); }, emitBeforeComplete && (function () { hasState && subscriber.next(state); subscriber.complete(); }))); }; } function reduce(accumulator, seed) { return operate(scanInternals(accumulator, seed, arguments.length >= 2, false, true)); } function concatMap(project, resultSelector) { return isFunction(resultSelector) ? mergeMap(project, resultSelector, 1) : mergeMap(project, 1); } function fromSubscribable(subscribable) { return new Observable(function (subscriber) { return subscribable.subscribe(subscriber); }); } var DEFAULT_CONFIG = { connector: function () { return new Subject(); }, }; function connect(selector, config) { if (config === void 0) { config = DEFAULT_CONFIG; } var connector = config.connector; return operate(function (source, subscriber) { var subject = connector(); innerFrom(selector(fromSubscribable(subject))).subscribe(subscriber); subscriber.add(source.subscribe(subject)); }); } function debounceTime(dueTime, scheduler) { if (scheduler === void 0) { scheduler = asyncScheduler; } return operate(function (source, subscriber) { var activeTask = null; var lastValue = null; var lastTime = null; var emit = function () { if (activeTask) { activeTask.unsubscribe(); activeTask = null; var value = lastValue; lastValue = null; subscriber.next(value); } }; function emitWhenIdle() { var targetTime = lastTime + dueTime; var now = scheduler.now(); if (now < targetTime) { activeTask = this.schedule(undefined, targetTime - now); subscriber.add(activeTask); return; } emit(); } source.subscribe(createOperatorSubscriber(subscriber, function (value) { lastValue = value; lastTime = scheduler.now(); if (!activeTask) { activeTask = scheduler.schedule(emitWhenIdle, dueTime); subscriber.add(activeTask); } }, function () { emit(); subscriber.complete(); }, undefined, function () { lastValue = activeTask = null; })); }); } function defaultIfEmpty(defaultValue) { return operate(function (source, subscriber) { var hasValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; subscriber.next(value); }, function () { if (!hasValue) { subscriber.next(defaultValue); } subscriber.complete(); })); }); } function take(count) { return count <= 0 ? function () { return EMPTY$1; } : operate(function (source, subscriber) { var seen = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (++seen <= count) { subscriber.next(value); if (count <= seen) { subscriber.complete(); } } })); }); } function distinctUntilChanged(comparator, keySelector) { if (keySelector === void 0) { keySelector = identity; } comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare$3; return operate(function (source, subscriber) { var previousKey; var first = true; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var currentKey = keySelector(value); if (first || !comparator(previousKey, currentKey)) { first = false; previousKey = currentKey; subscriber.next(value); } })); }); } function defaultCompare$3(a, b) { return a === b; } function throwIfEmpty(errorFactory) { if (errorFactory === void 0) { errorFactory = defaultErrorFactory; } return operate(function (source, subscriber) { var hasValue = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; subscriber.next(value); }, function () { return (hasValue ? subscriber.complete() : subscriber.error(errorFactory())); })); }); } function defaultErrorFactory() { return new EmptyError(); } function expand(project, concurrent, scheduler) { if (concurrent === void 0) { concurrent = Infinity; } concurrent = (concurrent || 0) < 1 ? Infinity : concurrent; return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent, undefined, true, scheduler); }); } function finalize(callback) { return operate(function (source, subscriber) { try { source.subscribe(subscriber); } finally { subscriber.add(callback); } }); } function first(predicate, defaultValue) { var hasDefaultValue = arguments.length >= 2; return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, take(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; } function takeLast(count) { return count <= 0 ? function () { return EMPTY$1; } : operate(function (source, subscriber) { var buffer = []; source.subscribe(createOperatorSubscriber(subscriber, function (value) { buffer.push(value); count < buffer.length && buffer.shift(); }, function () { var e_1, _a; try { for (var buffer_1 = __values(buffer), buffer_1_1 = buffer_1.next(); !buffer_1_1.done; buffer_1_1 = buffer_1.next()) { var value = buffer_1_1.value; subscriber.next(value); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (buffer_1_1 && !buffer_1_1.done && (_a = buffer_1.return)) _a.call(buffer_1); } finally { if (e_1) throw e_1.error; } } subscriber.complete(); }, undefined, function () { buffer = null; })); }); } function last(predicate, defaultValue) { var hasDefaultValue = arguments.length >= 2; return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, takeLast(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; } function multicast(subjectOrSubjectFactory, selector) { var subjectFactory = isFunction(subjectOrSubjectFactory) ? subjectOrSubjectFactory : function () { return subjectOrSubjectFactory; }; if (isFunction(selector)) { return connect(selector, { connector: subjectFactory, }); } return function (source) { return new ConnectableObservable(source, subjectFactory); }; } function pairwise() { return operate(function (source, subscriber) { var prev; var hasPrev = false; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var p = prev; prev = value; hasPrev && subscriber.next([p, value]); hasPrev = true; })); }); } function pluck() { var properties = []; for (var _i = 0; _i < arguments.length; _i++) { properties[_i] = arguments[_i]; } var length = properties.length; if (length === 0) { throw new Error("list of properties cannot be empty."); } return map(function (x) { var currentProp = x; for (var i = 0; i < length; i++) { var p = currentProp === null || currentProp === void 0 ? void 0 : currentProp[properties[i]]; if (typeof p !== "undefined") { currentProp = p; } else { return undefined; } } return currentProp; }); } function publish(selector) { return selector ? function (source) { return connect(selector)(source); } : function (source) { return multicast(new Subject())(source); }; } function publishReplay(bufferSize, windowTime, selectorOrScheduler, timestampProvider) { if (selectorOrScheduler && !isFunction(selectorOrScheduler)) { timestampProvider = selectorOrScheduler; } var selector = isFunction(selectorOrScheduler) ? selectorOrScheduler : undefined; return function (source) { return multicast(new ReplaySubject(bufferSize, windowTime, timestampProvider), selector)(source); }; } function retry(configOrCount) { if (configOrCount === void 0) { configOrCount = Infinity; } var config; if (configOrCount && typeof configOrCount === "object") { config = configOrCount; } else { config = { count: configOrCount, }; } var _a = config.count, count = _a === void 0 ? Infinity : _a, delay = config.delay, _b = config.resetOnSuccess, resetOnSuccess = _b === void 0 ? false : _b; return count <= 0 ? identity : operate(function (source, subscriber) { var soFar = 0; var innerSub; var subscribeForRetry = function () { var syncUnsub = false; innerSub = source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (resetOnSuccess) { soFar = 0; } subscriber.next(value); }, undefined, function (err) { if (soFar++ < count) { var resub_1 = function () { if (innerSub) { innerSub.unsubscribe(); innerSub = null; subscribeForRetry(); } else { syncUnsub = true; } }; if (delay != null) { var notifier = typeof delay === "number" ? timer(delay) : innerFrom(delay(err, soFar)); var notifierSubscriber_1 = createOperatorSubscriber(subscriber, function () { notifierSubscriber_1.unsubscribe(); resub_1(); }, function () { subscriber.complete(); }); notifier.subscribe(notifierSubscriber_1); } else { resub_1(); } } else { subscriber.error(err); } })); if (syncUnsub) { innerSub.unsubscribe(); innerSub = null; subscribeForRetry(); } }; subscribeForRetry(); }); } function sample(notifier) { return operate(function (source, subscriber) { var hasValue = false; var lastValue = null; source.subscribe(createOperatorSubscriber(subscriber, function (value) { hasValue = true; lastValue = value; })); notifier.subscribe(createOperatorSubscriber(subscriber, function () { if (hasValue) { hasValue = false; var value = lastValue; lastValue = null; subscriber.next(value); } }, noop)); }); } function scan(accumulator, seed) { return operate(scanInternals(accumulator, seed, arguments.length >= 2, true)); } function share(options) { if (options === void 0) { options = {}; } var _a = options.connector, connector = _a === void 0 ? function () { return new Subject(); } : _a, _b = options.resetOnError, resetOnError = _b === void 0 ? true : _b, _c = options.resetOnComplete, resetOnComplete = _c === void 0 ? true : _c, _d = options.resetOnRefCountZero, resetOnRefCountZero = _d === void 0 ? true : _d; return function (wrapperSource) { var connection; var resetConnection; var subject; var refCount = 0; var hasCompleted = false; var hasErrored = false; var cancelReset = function () { resetConnection === null || resetConnection === void 0 ? void 0 : resetConnection.unsubscribe(); resetConnection = undefined; }; var reset = function () { cancelReset(); connection = subject = undefined; hasCompleted = hasErrored = false; }; var resetAndUnsubscribe = function () { var conn = connection; reset(); conn === null || conn === void 0 ? void 0 : conn.unsubscribe(); }; return operate(function (source, subscriber) { refCount++; if (!hasErrored && !hasCompleted) { cancelReset(); } var dest = (subject = subject !== null && subject !== void 0 ? subject : connector()); subscriber.add(function () { refCount--; if (refCount === 0 && !hasErrored && !hasCompleted) { resetConnection = handleReset(resetAndUnsubscribe, resetOnRefCountZero); } }); dest.subscribe(subscriber); if (!connection && refCount > 0) { connection = new SafeSubscriber({ next: function (value) { return dest.next(value); }, error: function (err) { hasErrored = true; cancelReset(); resetConnection = handleReset(reset, resetOnError, err); dest.error(err); }, complete: function () { hasCompleted = true; cancelReset(); resetConnection = handleReset(reset, resetOnComplete); dest.complete(); }, }); innerFrom(source).subscribe(connection); } })(wrapperSource); }; } function handleReset(reset, on) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } if (on === true) { reset(); return; } if (on === false) { return; } var onSubscriber = new SafeSubscriber({ next: function () { onSubscriber.unsubscribe(); reset(); }, }); return on.apply(void 0, __spreadArray([], __read(args))).subscribe(onSubscriber); } function skip(count) { return filter(function (_, index) { return count <= index; }); } function skipWhile(predicate) { return operate(function (source, subscriber) { var taking = false; var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { return (taking || (taking = !predicate(value, index++))) && subscriber.next(value); })); }); } function startWith() { var values = []; for (var _i = 0; _i < arguments.length; _i++) { values[_i] = arguments[_i]; } var scheduler = popScheduler(values); return operate(function (source, subscriber) { (scheduler ? concat(values, source, scheduler) : concat(values, source)).subscribe(subscriber); }); } function switchMap(project, resultSelector) { return operate(function (source, subscriber) { var innerSubscriber = null; var index = 0; var isComplete = false; var checkComplete = function () { return isComplete && !innerSubscriber && subscriber.complete(); }; source.subscribe(createOperatorSubscriber(subscriber, function (value) { innerSubscriber === null || innerSubscriber === void 0 ? void 0 : innerSubscriber.unsubscribe(); var innerIndex = 0; var outerIndex = index++; innerFrom(project(value, outerIndex)).subscribe((innerSubscriber = createOperatorSubscriber(subscriber, function (innerValue) { return subscriber.next(resultSelector ? resultSelector(value, innerValue, outerIndex, innerIndex++) : innerValue); }, function () { innerSubscriber = null; checkComplete(); }))); }, function () { isComplete = true; checkComplete(); })); }); } function takeUntil(notifier) { return operate(function (source, subscriber) { innerFrom(notifier).subscribe(createOperatorSubscriber(subscriber, function () { return subscriber.complete(); }, noop)); !subscriber.closed && source.subscribe(subscriber); }); } function takeWhile(predicate, inclusive) { if (inclusive === void 0) { inclusive = false; } return operate(function (source, subscriber) { var index = 0; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var result = predicate(value, index++); (result || inclusive) && subscriber.next(value); !result && subscriber.complete(); })); }); } function tap(observerOrNext, error, complete) { var tapObserver = isFunction(observerOrNext) || error || complete ? { next: observerOrNext, error: error, complete: complete } : observerOrNext; return tapObserver ? operate(function (source, subscriber) { var _a; (_a = tapObserver.subscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); var isUnsub = true; source.subscribe(createOperatorSubscriber(subscriber, function (value) { var _a; (_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value); subscriber.next(value); }, function () { var _a; isUnsub = false; (_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver); subscriber.complete(); }, function (err) { var _a; isUnsub = false; (_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err); subscriber.error(err); }, function () { var _a, _b; if (isUnsub) { (_a = tapObserver.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); } (_b = tapObserver.finalize) === null || _b === void 0 ? void 0 : _b.call(tapObserver); })); }) : identity; } function withLatestFrom() { var inputs = []; for (var _i = 0; _i < arguments.length; _i++) { inputs[_i] = arguments[_i]; } var project = popResultSelector(inputs); return operate(function (source, subscriber) { var len = inputs.length; var otherValues = new Array(len); var hasValue = inputs.map(function () { return false; }); var ready = false; var _loop_1 = function (i) { innerFrom(inputs[i]).subscribe(createOperatorSubscriber(subscriber, function (value) { otherValues[i] = value; if (!ready && !hasValue[i]) { hasValue[i] = true; (ready = hasValue.every(identity)) && (hasValue = null); } }, noop)); }; for (var i = 0; i < len; i++) { _loop_1(i); } source.subscribe(createOperatorSubscriber(subscriber, function (value) { if (ready) { var values = __spreadArray([value], __read(otherValues)); subscriber.next(project ? project.apply(void 0, __spreadArray([], __read(values))) : values); } })); }); } /** * @class Filter * * @classdesc Represents a class for creating image filters. Implementation and * definitions based on https://github.com/mapbox/feature-filter. */ class FilterCreator { /** * Create a filter from a filter expression. * * @description The following filters are supported: * * Comparison * `==` * `!=` * `<` * `<=` * `>` * `>=` * * Set membership * `in` * `!in` * * Combining * `all` * * @param {FilterExpression} filter - Comparison, set membership or combinding filter * expression. * @returns {FilterFunction} Function taking a image and returning a boolean that * indicates whether the image passed the test or not. */ createFilter(filter) { return new Function("node", "return " + this._compile(filter) + ";"); } _compile(filter) { if (filter == null || filter.length <= 1) { return "true"; } const operator = filter[0]; const operation = operator === "==" ? this._compileComparisonOp("===", filter[1], filter[2], false) : operator === "!=" ? this._compileComparisonOp("!==", filter[1], filter[2], false) : operator === ">" || operator === ">=" || operator === "<" || operator === "<=" ? this._compileComparisonOp(operator, filter[1], filter[2], true) : operator === "in" ? this._compileInOp(filter[1], filter.slice(2)) : operator === "!in" ? this._compileNegation(this._compileInOp(filter[1], filter.slice(2))) : operator === "all" ? this._compileLogicalOp(filter.slice(1), "&&") : "true"; return "(" + operation + ")"; } _compare(a, b) { return a < b ? -1 : a > b ? 1 : 0; } _compileComparisonOp(operator, property, value, checkType) { const left = this._compilePropertyReference(property); const right = JSON.stringify(value); return (checkType ? "typeof " + left + "===typeof " + right + "&&" : "") + left + operator + right; } _compileInOp(property, values) { const compare = this._compare; const left = JSON.stringify(values.sort(compare)); const right = this._compilePropertyReference(property); return left + ".indexOf(" + right + ")!==-1"; } _compileLogicalOp(filters, operator) { const compile = this._compile.bind(this); return filters.map(compile).join(operator); } _compileNegation(expression) { return "!(" + expression + ")"; } _compilePropertyReference(property) { return "node[" + JSON.stringify(property) + "]"; } } /** * @license * Copyright 2010-2022 Three.js Authors * SPDX-License-Identifier: MIT */ const REVISION = "142"; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const FlatShading = 1; const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const LinearEncoding = 3000; const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; const SRGBColorSpace = "srgb"; const LinearSRGBColorSpace = "srgb-linear"; const KeepStencilOp = 7680; const AlwaysStencilFunc = 519; const StaticDrawUsage = 35044; const GLSL3 = "300 es"; const _SRGBAFormat = 1035; // fallback for WebGL 1 /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; const listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } } hasEventListener( type, listener ) { if ( this._listeners === undefined ) return false; const listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; } removeEventListener( type, listener ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { const index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } } dispatchEvent( event ) { if ( this._listeners === undefined ) return; const listeners = this._listeners; const listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice( 0 ); for ( let i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } event.target = null; } } } const _lut = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" ]; let _seed = 1234567; const DEG2RAD$1 = Math.PI / 180; const RAD2DEG$1 = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + "-" + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + "-" + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + "-" + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + "-" + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp$1( value, min, max ) { return Math.max( min, Math.min( max, value ) ); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { return ( ( n % m ) + m ) % m; } // Linear mapping from range to range function mapLinear( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp( x, y, value ) { if ( x !== y ) { return ( value - x ) / ( y - x ); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp( x, y, t ) { return ( 1 - t ) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp( x, y, lambda, dt ) { return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong( x, length = 1 ) { return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); } function smootherstep( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); } // Random integer from interval function randInt( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); } // Random float from interval function randFloat( low, high ) { return low + Math.random() * ( high - low ); } // Random float from <-range/2, range/2> interval function randFloatSpread( range ) { return range * ( 0.5 - Math.random() ); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { if ( s !== undefined ) _seed = s; // Mulberry32 generator let t = _seed += 0x6D2B79F5; t = Math.imul( t ^ t >>> 15, t | 1 ); t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } function degToRad( degrees ) { return degrees * DEG2RAD$1; } function radToDeg( radians ) { return radians * RAD2DEG$1; } function isPowerOfTwo( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; } function ceilPowerOfTwo( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); } function floorPowerOfTwo( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } function setQuaternionFromProperEuler( q, a, b, c, order ) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by "order" // rotation by angle "a" is applied first, then by angle "b", then by angle "c" // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos( b / 2 ); const s2 = sin( b / 2 ); const c13 = cos( ( a + c ) / 2 ); const s13 = sin( ( a + c ) / 2 ); const c1_3 = cos( ( a - c ) / 2 ); const s1_3 = sin( ( a - c ) / 2 ); const c3_1 = cos( ( c - a ) / 2 ); const s3_1 = sin( ( c - a ) / 2 ); switch ( order ) { case "XYX": q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); break; case "YZY": q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); break; case "ZXZ": q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); break; case "XZX": q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); break; case "YXY": q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); break; case "ZYZ": q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); break; default: console.warn( "THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: " + order ); } } function denormalize$1( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int16Array: return Math.max( value / 32767.0, - 1.0 ); case Int8Array: return Math.max( value / 127.0, - 1.0 ); default: throw new Error( "Invalid component type." ); } } function normalize( value, array ) { switch ( array.constructor ) { case Float32Array: return value; case Uint16Array: return Math.round( value * 65535.0 ); case Uint8Array: return Math.round( value * 255.0 ); case Int16Array: return Math.round( value * 32767.0 ); case Int8Array: return Math.round( value * 127.0 ); default: throw new Error( "Invalid component type." ); } } var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD$1, RAD2DEG: RAD2DEG$1, generateUUID: generateUUID, clamp: clamp$1, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize$1 }); class Vector2 { constructor( x = 0, y = 0 ) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width( value ) { this.x = value; } get height() { return this.y; } set height( value ) { this.y = value; } set( x, y ) { this.x = x; this.y = y; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y ); } copy( v ) { this.x = v.x; this.y = v.y; return this; } add( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; } addScalar( s ) { this.x += s; this.y += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; } sub( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; } subScalar( s ) { this.x -= s; this.y -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; return this; } divide( v ) { this.x /= v.x; this.y /= v.y; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } applyMatrix3( m ) { const x = this.x, y = this.y; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; } negate() { this.x = - this.x; this.y = - this.y; return this; } dot( v ) { return this.x * v.x + this.y * v.y; } cross( v ) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt( this.x * this.x + this.y * this.y ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ); } normalize() { return this.divideScalar( this.length() || 1 ); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2( - this.y, - this.x ) + Math.PI; return angle; } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; } fromBufferAttribute( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector2: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; } rotateAround( center, angle ) { const c = Math.cos( angle ), s = Math.sin( angle ); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; } } class Matrix3 { constructor() { Matrix3.prototype.isMatrix3 = true; this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix3: the constructor no longer reads arguments. use .set() instead." ); } } set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { const te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; } identity() { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrix3Column( this, 0 ); yAxis.setFromMatrix3Column( this, 1 ); zAxis.setFromMatrix3Column( this, 2 ); return this; } setFromMatrix4( m ) { const me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; } determinant() { const te = this.elements; const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; } getNormalMatrix( matrix4 ) { return this.setFromMatrix4( matrix4 ).invert().transpose(); } transposeIntoArray( r ) { const m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; } setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { const c = Math.cos( rotation ); const s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); return this; } scale( sx, sy ) { const te = this.elements; te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; return this; } rotate( theta ) { const c = Math.cos( theta ); const s = Math.sin( theta ); const te = this.elements; const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; te[ 0 ] = c * a11 + s * a21; te[ 3 ] = c * a12 + s * a22; te[ 6 ] = c * a13 + s * a23; te[ 1 ] = - s * a11 + c * a21; te[ 4 ] = - s * a12 + c * a22; te[ 7 ] = - s * a13 + c * a23; return this; } translate( tx, ty ) { const te = this.elements; te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; return this; } equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } clone() { return new this.constructor().fromArray( this.elements ); } } function arrayNeedsUint32( array ) { // assumes larger values usually on last for ( let i = array.length - 1; i >= 0; -- i ) { if ( array[ i ] > 65535 ) return true; } return false; } function createElementNS( name ) { return document.createElementNS( "http://www.w3.org/1999/xhtml", name ); } function SRGBToLinear( c ) { return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); } function LinearToSRGB( c ) { return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; } // JavaScript RGB-to-RGB transforms, defined as // FN[InputColorSpace][OutputColorSpace] callback functions. const FN = { [ SRGBColorSpace ]: { [ LinearSRGBColorSpace ]: SRGBToLinear }, [ LinearSRGBColorSpace ]: { [ SRGBColorSpace ]: LinearToSRGB }, }; const ColorManagement = { legacyMode: true, get workingColorSpace() { return LinearSRGBColorSpace; }, set workingColorSpace( colorSpace ) { console.warn( "THREE.ColorManagement: .workingColorSpace is readonly." ); }, convert: function ( color, sourceColorSpace, targetColorSpace ) { if ( this.legacyMode || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { return color; } if ( FN[ sourceColorSpace ] && FN[ sourceColorSpace ][ targetColorSpace ] !== undefined ) { const fn = FN[ sourceColorSpace ][ targetColorSpace ]; color.r = fn( color.r ); color.g = fn( color.g ); color.b = fn( color.b ); return color; } throw new Error( "Unsupported color space conversion." ); }, fromWorkingColorSpace: function ( color, targetColorSpace ) { return this.convert( color, this.workingColorSpace, targetColorSpace ); }, toWorkingColorSpace: function ( color, sourceColorSpace ) { return this.convert( color, sourceColorSpace, this.workingColorSpace ); }, }; const _colorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; const _rgb = { r: 0, g: 0, b: 0 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } function toComponents( source, target ) { target.r = source.r; target.g = source.g; target.b = source.b; return target; } class Color { constructor( r, g, b ) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } set( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } return this; } setScalar( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex( hex, colorSpace = SRGBColorSpace ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setRGB( r, g, b, colorSpace = LinearSRGBColorSpace ) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setHSL( h, s, l, colorSpace = LinearSRGBColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo( h, 1 ); s = clamp$1( s, 0, 1 ); l = clamp$1( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); const q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } setStyle( style, colorSpace = SRGBColorSpace ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } let m; if ( m = /^((?:rgb|hsl)a?)(([^)]*))/.exec( style ) ) { // rgb / hsl let color; const name = m[ 1 ]; const components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^s*(d+)s*,s*(d+)s*,s*(d+)s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); handleAlpha( color[ 4 ] ); return this; } if ( color = /^s*(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; ColorManagement.toWorkingColorSpace( this, colorSpace ); handleAlpha( color[ 4 ] ); return this; } break; case "hsl": case "hsla": if ( color = /^s*(d*.?d+)s*,s*(d+)\%s*,s*(d+)\%s*(?:,s*(d*.?d+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) const h = parseFloat( color[ 1 ] ) / 360; const s = parseInt( color[ 2 ], 10 ) / 100; const l = parseInt( color[ 3 ], 10 ) / 100; handleAlpha( color[ 4 ] ); return this.setHSL( h, s, l, colorSpace ); } break; } } else if ( m = /^#([A-Fa-fd]+)$/.exec( style ) ) { // hex color const hex = m[ 1 ]; const size = hex.length; if ( size === 3 ) { // #ff0 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } else if ( size === 6 ) { // #ff0000 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; ColorManagement.toWorkingColorSpace( this, colorSpace ); return this; } } if ( style && style.length > 0 ) { return this.setColorName( style, colorSpace ); } return this; } setColorName( style, colorSpace = SRGBColorSpace ) { // color keywords const hex = _colorKeywords[ style.toLowerCase() ]; if ( hex !== undefined ) { // red this.setHex( hex, colorSpace ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } return this; } clone() { return new this.constructor( this.r, this.g, this.b ); } copy( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear( color ) { this.r = SRGBToLinear( color.r ); this.g = SRGBToLinear( color.g ); this.b = SRGBToLinear( color.b ); return this; } copyLinearToSRGB( color ) { this.r = LinearToSRGB( color.r ); this.g = LinearToSRGB( color.g ); this.b = LinearToSRGB( color.b ); return this; } convertSRGBToLinear() { this.copySRGBToLinear( this ); return this; } convertLinearToSRGB() { this.copyLinearToSRGB( this ); return this; } getHex( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); return clamp$1( _rgb.r * 255, 0, 255 ) << 16 ^ clamp$1( _rgb.g * 255, 0, 255 ) << 8 ^ clamp$1( _rgb.b * 255, 0, 255 ) << 0; } getHexString( colorSpace = SRGBColorSpace ) { return ( "000000" + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } getHSL( target, colorSpace = LinearSRGBColorSpace ) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); const r = _rgb.r, g = _rgb.g, b = _rgb.b; const max = Math.max( r, g, b ); const min = Math.min( r, g, b ); let hue, saturation; const lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB( target, colorSpace = LinearSRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); target.r = _rgb.r; target.g = _rgb.g; target.b = _rgb.b; return target; } getStyle( colorSpace = SRGBColorSpace ) { ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); if ( colorSpace !== SRGBColorSpace ) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${ colorSpace } ${ _rgb.r } ${ _rgb.g } ${ _rgb.b })`; } return `rgb(${( _rgb.r * 255 ) | 0},${( _rgb.g * 255 ) | 0},${( _rgb.b * 255 ) | 0})`; } offsetHSL( h, s, l ) { this.getHSL( _hslA ); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } add( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar( s ) { this.r += s; this.g += s; this.b += s; return this; } sub( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; } multiply( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar( s ) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; } lerpColors( color1, color2, alpha ) { this.r = color1.r + ( color2.r - color1.r ) * alpha; this.g = color1.g + ( color2.g - color1.g ) * alpha; this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } lerpHSL( color, alpha ) { this.getHSL( _hslA ); color.getHSL( _hslB ); const h = lerp( _hslA.h, _hslB.h, alpha ); const s = lerp( _hslA.s, _hslB.s, alpha ); const l = lerp( _hslA.l, _hslB.l, alpha ); this.setHSL( h, s, l ); return this; } equals( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); } fromArray( array, offset = 0 ) { this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; } fromBufferAttribute( attribute, index ) { this.r = attribute.getX( index ); this.g = attribute.getY( index ); this.b = attribute.getZ( index ); if ( attribute.normalized === true ) { // assuming Uint8Array this.r /= 255; this.g /= 255; this.b /= 255; } return this; } toJSON() { return this.getHex(); } *[ Symbol.iterator ]() { yield this.r; yield this.g; yield this.b; } } Color.NAMES = _colorKeywords; let _canvas; class ImageUtils { static getDataURL( image ) { if ( /^data:/i.test( image.src ) ) { return image.src; } if ( typeof HTMLCanvasElement == "undefined" ) { return image.src; } let canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { if ( _canvas === undefined ) _canvas = createElementNS( "canvas" ); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } canvas = _canvas; } if ( canvas.width > 2048 || canvas.height > 2048 ) { console.warn( "THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons", image ); return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } static sRGBToLinear( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const canvas = createElementNS( "canvas" ); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height ); const imageData = context.getImageData( 0, 0, image.width, image.height ); const data = imageData.data; for ( let i = 0; i < data.length; i ++ ) { data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; } context.putImageData( imageData, 0, 0 ); return canvas; } else if ( image.data ) { const data = image.data.slice( 0 ); for ( let i = 0; i < data.length; i ++ ) { if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); } else { // assuming float data[ i ] = SRGBToLinear( data[ i ] ); } } return { data: data, width: image.width, height: image.height }; } else { console.warn( "THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied." ); return image; } } } class Source { constructor( data = null ) { this.isSource = true; this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate( value ) { if ( value === true ) this.version ++; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { return meta.images[ this.uuid ]; } const output = { uuid: this.uuid, url: "" }; const data = this.data; if ( data !== null ) { let url; if ( Array.isArray( data ) ) { // cube texture url = []; for ( let i = 0, l = data.length; i < l; i ++ ) { if ( data[ i ].isDataTexture ) { url.push( serializeImage( data[ i ].image ) ); } else { url.push( serializeImage( data[ i ] ) ); } } } else { // texture url = serializeImage( data ); } output.url = url; } if ( ! isRootObject ) { meta.images[ this.uuid ] = output; } return output; } } function serializeImage( image ) { if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { // default images return ImageUtils.getDataURL( image ); } else { if ( image.data ) { // images of DataTexture return { data: Array.from( image.data ), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn( "THREE.Texture: Unable to serialize Texture." ); return {}; } } } let textureId = 0; class Texture extends EventDispatcher { constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { super(); this.isTexture = true; Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = generateUUID(); this.name = ""; this.source = new Source( image ); this.mipmaps = []; this.mapping = mapping; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding; this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image( value ) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.needsUpdate = true; return this; } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } const output = { metadata: { version: 4.5, type: "Texture", generator: "Texture.toJSON" }, uuid: this.uuid, name: this.name, image: this.source.toJSON( meta ).uuid, 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 ( JSON.stringify( this.userData ) !== "{}" ) output.userData = this.userData; if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; } dispose() { this.dispatchEvent( { type: "dispose" } ); } transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } return uv; } set needsUpdate( value ) { if ( value === true ) { this.version ++; this.source.needsUpdate = true; } } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; class Vector4 { constructor( x = 0, y = 0, z = 0, w = 1 ) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width( value ) { this.z = value; } get height() { return this.w; } set height( value ) { this.w = value; } set( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setW( w ) { this.w = w; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z, this.w ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; } add( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply( v ) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } setAxisAngleFromQuaternion( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); const s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = ( m11 + 1 ) / 2; const yy = ( m22 + 1 ) / 2; const zz = ( m33 + 1 ) / 2; const xy = ( m12 + m21 ) / 4; const xz = ( m13 + m31 ) / 4; const yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; } fromBufferAttribute( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector4: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor( width, height, options = {} ) { super(); this.isWebGLRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); const image = { width: width, height: height, depth: 1 }; this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); this.texture.isRenderTargetTexture = true; this.texture.flipY = false; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize( width, height, depth = 1 ) { if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); } clone() { return new this.constructor().copy( this ); } copy( source ) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.texture.isRenderTargetTexture = true; // ensure image object is not shared, see #20328 const image = Object.assign( {}, source.texture.image ); this.texture.source = new Source( image ); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } class DataArrayTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { super( null ); this.isDataArrayTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class Data3DTexture extends Texture { constructor( data = null, width = 1, height = 1, depth = 1 ) { // We"re going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super( null ); this.isData3DTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerp( qa, qb, qm, t ) { console.warn( "THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead." ); return qm.slerpQuaternions( qa, qb, t ); } static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( t === 0 ) { dst[ dstOffset + 0 ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; return; } if ( t === 1 ) { dst[ dstOffset + 0 ] = x1; dst[ dstOffset + 1 ] = y1; dst[ dstOffset + 2 ] = z1; dst[ dstOffset + 3 ] = w1; return; } if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { const sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { const x0 = src0[ srcOffset0 ]; const y0 = src0[ srcOffset0 + 1 ]; const z0 = src0[ srcOffset0 + 2 ]; const w0 = src0[ srcOffset0 + 3 ]; const x1 = src1[ srcOffset1 ]; const y1 = src1[ srcOffset1 + 1 ]; const z1 = src1[ srcOffset1 + 2 ]; const w1 = src1[ srcOffset1 + 3 ]; dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w( value ) { this._w = value; this._onChangeCallback(); } set( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._w ); } copy( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler( euler, update ) { if ( ! ( euler && euler.isEuler ) ) { throw new Error( "THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order." ); } const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos( x / 2 ); const c2 = cos( y / 2 ); const c3 = cos( z / 2 ); const s1 = sin( x / 2 ); const s2 = sin( y / 2 ); const s3 = sin( z / 2 ); switch ( order ) { case "XYZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "YXZ": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "ZXY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "ZYX": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case "YZX": this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case "XZY": this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn( "THREE.Quaternion: .setFromEuler() encountered an unknown order: " + order ); } if ( update !== false ) this._onChangeCallback(); return this; } setFromAxisAngle( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this._onChangeCallback(); return this; } setFromRotationMatrix( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33; if ( trace > 0 ) { const s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors( vFrom, vTo ) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot( vTo ) + 1; if ( r < Number.EPSILON ) { // vFrom and vTo point in opposite directions r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { this._x = - vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = - vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo( q ) { return 2 * Math.acos( Math.abs( clamp$1( this.dot( q ), - 1, 1 ) ) ); } rotateTowards( q, step ) { const angle = this.angleTo( q ); if ( angle === 0 ) return this; const t = Math.min( 1, step / angle ); this.slerp( q, t ); return this; } identity() { return this.set( 0, 0, 0, 1 ); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= - 1; this._y *= - 1; this._z *= - 1; this._onChangeCallback(); return this; } dot( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } normalize() { let l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply( q, p ) { if ( p !== undefined ) { console.warn( "THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead." ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); } premultiply( q ) { return this.multiplyQuaternions( q, this ); } multiplyQuaternions( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if ( sqrSinHalfTheta <= Number.EPSILON ) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this._onChangeCallback(); return this; } slerpQuaternions( qa, qb, t ) { return this.copy( qa ).slerp( qb, t ); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt( 1 - u1 ); const sqrtu1 = Math.sqrt( u1 ); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set( sqrt1u1 * Math.cos( u2 ), sqrtu1 * Math.sin( u3 ), sqrtu1 * Math.cos( u3 ), sqrt1u1 * Math.sin( u2 ), ); } equals( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } fromArray( array, offset = 0 ) { this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; } fromBufferAttribute( attribute, index ) { this._x = attribute.getX( index ); this._y = attribute.getY( index ); this._z = attribute.getZ( index ); this._w = attribute.getW( index ); return this; } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor( x = 0, y = 0, z = 0 ) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set( x, y, z ) { if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX( x ) { this.x = x; return this; } setY( y ) { this.y = y; return this; } setZ( z ) { this.z = z; return this; } setComponent( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; } getComponent( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } } clone() { return new this.constructor( this.x, this.y, this.z ); } copy( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar( s ) { this.x += s; this.y += s; this.z += s; return this; } addVectors( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar( s ) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead." ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order." ); } return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } applyAxisAngle( axis, angle ) { return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); } applyMatrix3( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; } applyNormalMatrix( m ) { return this.applyMatrix3( m ).normalize(); } applyMatrix4( m ) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; } applyQuaternion( q ) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; } project( camera ) { return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } unproject( camera ) { return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); } transformDirection( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); } divide( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar( scalar ) { return this.multiplyScalar( 1 / scalar ); } min( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; } max( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; } clamp( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; } clampScalar( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); return this; } clampLength( min, max ) { const length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } floor() { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; } ceil() { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; } round() { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; } roundToZero() { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; } negate() { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; } dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } manhattanLength() { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); } normalize() { return this.divideScalar( this.length() || 1 ); } setLength( length ) { return this.normalize().multiplyScalar( length ); } lerp( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; } lerpVectors( v1, v2, alpha ) { this.x = v1.x + ( v2.x - v1.x ) * alpha; this.y = v1.y + ( v2.y - v1.y ) * alpha; this.z = v1.z + ( v2.z - v1.z ) * alpha; return this; } cross( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead." ); return this.crossVectors( v, w ); } return this.crossVectors( this, v ); } crossVectors( a, b ) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector( v ) { const denominator = v.lengthSq(); if ( denominator === 0 ) return this.set( 0, 0, 0 ); const scalar = v.dot( this ) / denominator; return this.copy( v ).multiplyScalar( scalar ); } projectOnPlane( planeNormal ) { _vector$c.copy( this ).projectOnVector( planeNormal ); return this.sub( _vector$c ); } reflect( normal ) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); } angleTo( v ) { const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); if ( denominator === 0 ) return Math.PI / 2; const theta = this.dot( v ) / denominator; // clamp, to handle numerical problems return Math.acos( clamp$1( theta, - 1, 1 ) ); } distanceTo( v ) { return Math.sqrt( this.distanceToSquared( v ) ); } distanceToSquared( v ) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); } setFromSpherical( s ) { return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } setFromSphericalCoords( radius, phi, theta ) { const sinPhiRadius = Math.sin( phi ) * radius; this.x = sinPhiRadius * Math.sin( theta ); this.y = Math.cos( phi ) * radius; this.z = sinPhiRadius * Math.cos( theta ); return this; } setFromCylindrical( c ) { return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); } setFromCylindricalCoords( radius, theta, y ) { this.x = radius * Math.sin( theta ); this.y = y; this.z = radius * Math.cos( theta ); return this; } setFromMatrixPosition( m ) { const e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; } setFromMatrixScale( m ) { const sx = this.setFromMatrixColumn( m, 0 ).length(); const sy = this.setFromMatrixColumn( m, 1 ).length(); const sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn( m, index ) { return this.fromArray( m.elements, index * 4 ); } setFromMatrix3Column( m, index ) { return this.fromArray( m.elements, index * 3 ); } setFromEuler( e ) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } equals( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); } fromArray( array, offset = 0 ) { this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; } fromBufferAttribute( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector3: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = ( Math.random() - 0.5 ) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt( 1 - u ** 2 ); this.x = f * Math.cos( t ); this.y = f * Math.sin( t ); this.z = u; return this; } *[ Symbol.iterator ]() { yield this.x; yield this.y; yield this.z; } } const _vector$c = /*@__PURE__*/ new Vector3(); const _quaternion$4 = /*@__PURE__*/ new Quaternion(); class Box3 { constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { this.isBox3 = true; this.min = min; this.max = max; } set( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; } setFromArray( array ) { let minX = + Infinity; let minY = + Infinity; let minZ = + Infinity; let maxX = - Infinity; let maxY = - Infinity; let maxZ = - Infinity; for ( let i = 0, l = array.length; i < l; i += 3 ) { const x = array[ i ]; const y = array[ i + 1 ]; const z = array[ i + 2 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; } setFromBufferAttribute( attribute ) { let minX = + Infinity; let minY = + Infinity; let minZ = + Infinity; let maxX = - Infinity; let maxY = - Infinity; let maxZ = - Infinity; for ( let i = 0, l = attribute.count; i < l; i ++ ) { const x = attribute.getX( i ); const y = attribute.getY( i ); const z = attribute.getZ( i ); if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; } setFromPoints( points ) { this.makeEmpty(); for ( let i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; } setFromCenterAndSize( center, size ) { const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; } setFromObject( object, precise = false ) { this.makeEmpty(); return this.expandByObject( object, precise ); } clone() { return new this.constructor().copy( this ); } copy( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); } getCenter( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); } expandByPoint( point ) { this.min.min( point ); this.max.max( point ); return this; } expandByVector( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; } expandByScalar( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; } expandByObject( object, precise = false ) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms object.updateWorldMatrix( false, false ); const geometry = object.geometry; if ( geometry !== undefined ) { if ( precise && geometry.attributes != undefined && geometry.attributes.position !== undefined ) { const position = geometry.attributes.position; for ( let i = 0, l = position.count; i < l; i ++ ) { _vector$b.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); this.expandByPoint( _vector$b ); } } else { if ( geometry.boundingBox === null ) { geometry.computeBoundingBox(); } _box$3.copy( geometry.boundingBox ); _box$3.applyMatrix4( object.matrixWorld ); this.union( _box$3 ); } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { this.expandByObject( children[ i ], precise ); } return this; } containsPoint( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); } intersectsBox( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, _vector$b ); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); } intersectsPlane( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= - plane.constant && max >= - plane.constant ); } intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( _center ); _extents.subVectors( this.max, _center ); // translate triangle to aabb origin _v0$2.subVectors( triangle.a, _center ); _v1$7.subVectors( triangle.b, _center ); _v2$3.subVectors( triangle.c, _center ); // compute edge vectors for triangle _f0.subVectors( _v1$7, _v0$2 ); _f1.subVectors( _v2$3, _v1$7 ); _f2.subVectors( _v0$2, _v2$3 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [ 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors( _f0, _f1 ); axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); } clampPoint( point, target ) { return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); } getBoundingSphere( target ) { this.getCenter( target.center ); target.radius = this.getSize( _vector$b ).length() * 0.5; return target; } intersect( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; } union( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; } applyMatrix4( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( _points ); return this; } translate( offset ) { this.min.add( offset ); this.max.add( offset ); return this; } equals( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } const _points = [ /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3(), /*@__PURE__*/ new Vector3() ]; const _vector$b = /*@__PURE__*/ new Vector3(); const _box$3 = /*@__PURE__*/ new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/ new Vector3(); const _v1$7 = /*@__PURE__*/ new Vector3(); const _v2$3 = /*@__PURE__*/ new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/ new Vector3(); const _f1 = /*@__PURE__*/ new Vector3(); const _f2 = /*@__PURE__*/ new Vector3(); const _center = /*@__PURE__*/ new Vector3(); const _extents = /*@__PURE__*/ new Vector3(); const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); function satForAxes( axes, v0, v1, v2, extents ) { for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { _testAxis.fromArray( axes, i ); // project the aabb onto the separating axis const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot( _testAxis ); const p1 = v1.dot( _testAxis ); const p2 = v2.dot( _testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/ new Box3(); const _v1$6 = /*@__PURE__*/ new Vector3(); const _toFarthestPoint = /*@__PURE__*/ new Vector3(); const _toPoint = /*@__PURE__*/ new Vector3(); class Sphere { constructor( center = new Vector3(), radius = - 1 ) { this.center = center; this.radius = radius; } set( center, radius ) { this.center.copy( center ); this.radius = radius; return this; } setFromPoints( points, optionalCenter ) { const center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { _box$2.setFromPoints( points ).getCenter( center ); } let maxRadiusSq = 0; for ( let i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; } copy( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; } isEmpty() { return ( this.radius < 0 ); } makeEmpty() { this.center.set( 0, 0, 0 ); this.radius = - 1; return this; } containsPoint( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); } distanceToPoint( point ) { return ( point.distanceTo( this.center ) - this.radius ); } intersectsSphere( sphere ) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } intersectsBox( box ) { return box.intersectsSphere( this ); } intersectsPlane( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } clampPoint( point, target ) { const deltaLengthSq = this.center.distanceToSquared( point ); target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; } getBoundingBox( target ) { if ( this.isEmpty() ) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; } applyMatrix4( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate( offset ) { this.center.add( offset ); return this; } expandByPoint( point ) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors( point, this.center ); const lengthSq = _toPoint.lengthSq(); if ( lengthSq > ( this.radius * this.radius ) ) { const length = Math.sqrt( lengthSq ); const missingRadiusHalf = ( length - this.radius ) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); this.radius += missingRadiusHalf; } return this; } union( sphere ) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if ( this.center.equals( sphere.center ) === true ) { _toFarthestPoint.set( 0, 0, 1 ).multiplyScalar( sphere.radius ); } else { _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); } this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); return this; } equals( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } clone() { return new this.constructor().copy( this ); } } const _vector$a = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { this.origin = origin; this.direction = direction; } set( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; } copy( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; } at( t, target ) { return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); } lookAt( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; } recast( t ) { this.origin.copy( this.at( t, _vector$a ) ); return this; } closestPointToPoint( point, target ) { target.subVectors( point, this.origin ); const directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); } distanceToPoint( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); } distanceSqToPoint( point ) { const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return _vector$a.distanceToSquared( point ); } distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); _segDir.copy( v1 ).sub( v0 ).normalize(); _diff.copy( this.origin ).sub( _segCenter ); const segExtent = v0.distanceTo( v1 ) * 0.5; const a01 = - this.direction.dot( _segDir ); const b0 = _diff.dot( this.direction ); const b1 = - _diff.dot( _segDir ); const c = _diff.lengthSq(); const det = Math.abs( 1 - a01 * a01 ); let s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); } return sqrDist; } intersectSphere( sphere, target ) { _vector$a.subVectors( sphere.center, this.origin ); const tca = _vector$a.dot( this.direction ); const d2 = _vector$a.dot( _vector$a ) - tca * tca; const radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; const thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if ( t0 < 0 && t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); } intersectsSphere( sphere ) { return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); } distanceToPlane( plane ) { const denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane( plane, target ) { const t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); } intersectsPlane( plane ) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } const denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox( box, target ) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if ( tymin > tmin || tmin !== tmin ) tmin = tymin; if ( tymax < tmax || tmax !== tmax ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); } intersectsBox( box ) { return this.intersectBox( box, _vector$a ) !== null; } intersectTriangle( a, b, c, backfaceCulling, target ) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors( b, a ); _edge2.subVectors( c, a ); _normal$1.crossVectors( _edge1, _edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot( _normal$1 ); let sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } _diff.subVectors( this.origin, a ); const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. const QdN = - sign * _diff.dot( _normal$1 ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); } applyMatrix4( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; } equals( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } clone() { return new this.constructor().copy( this ); } } class Matrix4 { constructor() { Matrix4.prototype.isMatrix4 = true; this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix4: the constructor no longer reads arguments. use .set() instead." ); } } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { const te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; } identity() { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } clone() { return new Matrix4().fromArray( this.elements ); } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; } copyPosition( m ) { const te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; } setFromMatrix3( m ) { const me = m.elements; this.set( me[ 0 ], me[ 3 ], me[ 6 ], 0, me[ 1 ], me[ 4 ], me[ 7 ], 0, me[ 2 ], me[ 5 ], me[ 8 ], 0, 0, 0, 0, 1 ); return this; } extractBasis( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; } makeBasis( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; } extractRotation( m ) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order." ); } const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos( x ), b = Math.sin( x ); const c = Math.cos( y ), d = Math.sin( y ); const e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; } makeRotationFromQuaternion( q ) { return this.compose( _zero, q, _one ); } lookAt( eye, target, up ) { const te = this.elements; _z.subVectors( eye, target ); if ( _z.lengthSq() === 0 ) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors( up, _z ); if ( _x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors( up, _z ); } _x.normalize(); _y.crossVectors( _z, _x ); te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; return this; } multiply( m, n ) { if ( n !== undefined ) { console.warn( "THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead." ); return this.multiplyMatrices( m, n ); } return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar( s ) { const te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; } determinant() { const te = this.elements; const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); } transpose() { const te = this.elements; let tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; } setPosition( x, y, z ) { const te = this.elements; if ( x.isVector3 ) { te[ 12 ] = x.x; te[ 13 ] = x.y; te[ 14 ] = x.z; } else { te[ 12 ] = x; te[ 13 ] = y; te[ 14 ] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; } scale( v ) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); } makeTranslation( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; } makeRotationX( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; } makeRotationY( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; } makeRotationZ( theta ) { const c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; } makeRotationAxis( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos( angle ); const s = Math.sin( angle ); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; } makeScale( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; } makeShear( xy, xz, yx, yz, zx, zy ) { this.set( 1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1 ); return this; } compose( position, quaternion, scale ) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; } decompose( position, quaternion, scale ) { const te = this.elements; let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part _m1$2.copy( this ); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[ 0 ] *= invSX; _m1$2.elements[ 1 ] *= invSX; _m1$2.elements[ 2 ] *= invSX; _m1$2.elements[ 4 ] *= invSY; _m1$2.elements[ 5 ] *= invSY; _m1$2.elements[ 6 ] *= invSY; _m1$2.elements[ 8 ] *= invSZ; _m1$2.elements[ 9 ] *= invSZ; _m1$2.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( _m1$2 ); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective( left, right, top, bottom, near, far ) { if ( far === undefined ) { console.warn( "THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs." ); } const te = this.elements; const x = 2 * near / ( right - left ); const y = 2 * near / ( top - bottom ); const a = ( right + left ) / ( right - left ); const b = ( top + bottom ) / ( top - bottom ); const c = - ( far + near ) / ( far - near ); const d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; } makeOrthographic( left, right, top, bottom, near, far ) { const te = this.elements; const w = 1.0 / ( right - left ); const h = 1.0 / ( top - bottom ); const p = 1.0 / ( far - near ); const x = ( right + left ) * w; const y = ( top + bottom ) * h; const z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; } equals( matrix ) { const te = this.elements; const me = matrix.elements; for ( let i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; } fromArray( array, offset = 0 ) { for ( let i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; } toArray( array = [], offset = 0 ) { const te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } const _v1$5 = /*@__PURE__*/ new Vector3(); const _m1$2 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); const _matrix$1 = /*@__PURE__*/ new Matrix4(); const _quaternion$3 = /*@__PURE__*/ new Quaternion(); class Euler { constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x( value ) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y( value ) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z( value ) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order( value ) { this._order = value; this._onChangeCallback(); } set( x, y, z, order = this._order ) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor( this._x, this._y, this._z, this._order ); } copy( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix( m, order = this._order, update = true ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; switch ( order ) { case "XYZ": this._y = Math.asin( clamp$1( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } break; case "YXZ": this._x = Math.asin( - clamp$1( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.9999999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } break; case "ZXY": this._x = Math.asin( clamp$1( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.9999999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } break; case "ZYX": this._y = Math.asin( - clamp$1( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.9999999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } break; case "YZX": this._z = Math.asin( clamp$1( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.9999999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } break; case "XZY": this._z = Math.asin( - clamp$1( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.9999999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } break; default: console.warn( "THREE.Euler: .setFromRotationMatrix() encountered an unknown order: " + order ); } this._order = order; if ( update === true ) this._onChangeCallback(); return this; } setFromQuaternion( q, order, update ) { _matrix$1.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( _matrix$1, order, update ); } setFromVector3( v, order = this._order ) { return this.set( v.x, v.y, v.z, order ); } reorder( newOrder ) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler( this ); return this.setFromQuaternion( _quaternion$3, newOrder ); } equals( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); } fromArray( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this._onChangeCallback(); return this; } toArray( array = [], offset = 0 ) { array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; } _onChange( callback ) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[ Symbol.iterator ]() { yield this._x; yield this._y; yield this._z; yield this._order; } // @deprecated since r138, 02cf0df1cb4575d5842fef9c85bb5a89fe020d53 toVector3() { console.error( "THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead" ); } } Euler.DefaultOrder = "XYZ"; Euler.RotationOrders = [ "XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX" ]; class Layers { constructor() { this.mask = 1 | 0; } set( channel ) { this.mask = ( 1 << channel | 0 ) >>> 0; } enable( channel ) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle( channel ) { this.mask ^= 1 << channel | 0; } disable( channel ) { this.mask &= ~ ( 1 << channel | 0 ); } disableAll() { this.mask = 0; } test( layers ) { return ( this.mask & layers.mask ) !== 0; } isEnabled( channel ) { return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/ new Vector3(); const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$1 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); const _position$3 = /*@__PURE__*/ new Vector3(); const _scale$2 = /*@__PURE__*/ new Vector3(); const _quaternion$2 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); const _addedEvent = { type: "added" }; const _removedEvent = { type: "removed" }; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty( this, "id", { value: _object3DId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation._onChange( onRotationChange ); quaternion._onChange( onQuaternionChange ); Object.defineProperties( this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} applyMatrix4( matrix ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); this.matrix.premultiply( matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); } applyQuaternion( q ) { this.quaternion.premultiply( q ); return this; } setRotationFromAxisAngle( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); } setRotationFromEuler( euler ) { this.quaternion.setFromEuler( euler, true ); } setRotationFromMatrix( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); } setRotationFromQuaternion( q ) { // assumes q is normalized this.quaternion.copy( q ); } rotateOnAxis( axis, angle ) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( _q1 ); return this; } rotateOnWorldAxis( axis, angle ) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( _q1 ); return this; } rotateX( angle ) { return this.rotateOnAxis( _xAxis, angle ); } rotateY( angle ) { return this.rotateOnAxis( _yAxis, angle ); } rotateZ( angle ) { return this.rotateOnAxis( _zAxis, angle ); } translateOnAxis( axis, distance ) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( _v1$4.multiplyScalar( distance ) ); return this; } translateX( distance ) { return this.translateOnAxis( _xAxis, distance ); } translateY( distance ) { return this.translateOnAxis( _yAxis, distance ); } translateZ( distance ) { return this.translateOnAxis( _zAxis, distance ); } localToWorld( vector ) { return vector.applyMatrix4( this.matrixWorld ); } worldToLocal( vector ) { return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } lookAt( x, y, z ) { // This method does not support objects having non-uniformly-scaled parent(s) if ( x.isVector3 ) { _target.copy( x ); } else { _target.set( x, y, z ); } const parent = this.parent; this.updateWorldMatrix( true, false ); _position$3.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { _m1$1.lookAt( _position$3, _target, this.up ); } else { _m1$1.lookAt( _target, _position$3, this.up ); } this.quaternion.setFromRotationMatrix( _m1$1 ); if ( parent ) { _m1$1.extractRotation( parent.matrixWorld ); _q1.setFromRotationMatrix( _m1$1 ); this.quaternion.premultiply( _q1.invert() ); } } add( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( object && object.isObject3D ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; this.children.push( object ); object.dispatchEvent( _addedEvent ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; } remove( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } const index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; this.children.splice( index, 1 ); object.dispatchEvent( _removedEvent ); } return this; } removeFromParent() { const parent = this.parent; if ( parent !== null ) { parent.remove( this ); } return this; } clear() { for ( let i = 0; i < this.children.length; i ++ ) { const object = this.children[ i ]; object.parent = null; object.dispatchEvent( _removedEvent ); } this.children.length = 0; return this; } attach( object ) { // adds object as a child of this, while maintaining the object"s world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix( true, false ); _m1$1.copy( this.matrixWorld ).invert(); if ( object.parent !== null ) { object.parent.updateWorldMatrix( true, false ); _m1$1.multiply( object.parent.matrixWorld ); } object.applyMatrix4( _m1$1 ); this.add( object ); object.updateWorldMatrix( false, true ); return this; } getObjectById( id ) { return this.getObjectByProperty( "id", id ); } getObjectByName( name ) { return this.getObjectByProperty( "name", name ); } getObjectByProperty( name, value ) { if ( this[ name ] === value ) return this; for ( let i = 0, l = this.children.length; i < l; i ++ ) { const child = this.children[ i ]; const object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; } getWorldPosition( target ) { this.updateWorldMatrix( true, false ); return target.setFromMatrixPosition( this.matrixWorld ); } getWorldQuaternion( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, target, _scale$2 ); return target; } getWorldScale( target ) { this.updateWorldMatrix( true, false ); this.matrixWorld.decompose( _position$3, _quaternion$2, target ); return target; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); } raycast( /* raycaster, intersects */ ) {} traverse( callback ) { callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } } traverseVisible( callback ) { if ( this.visible === false ) return; callback( this ); const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } } traverseAncestors( callback ) { const parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } } updateMatrix() { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( force ); } } updateWorldMatrix( updateParents, updateChildren ) { const parent = this.parent; if ( updateParents === true && parent !== null ) { parent.updateWorldMatrix( true, false ); } if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } // update children if ( updateChildren === true ) { const children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateWorldMatrix( false, true ); } } } toJSON( meta ) { // meta is a string when called from JSON.stringify const isRootObject = ( meta === undefined || typeof meta === "string" ); const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( JSON.stringify( this.userData ) !== "{}" ) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // object specific properties if ( this.isInstancedMesh ) { object.type = "InstancedMesh"; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); } // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.isScene ) { if ( this.background ) { if ( this.background.isColor ) { object.background = this.background.toJSON(); } else if ( this.background.isTexture ) { object.background = this.background.toJSON( meta ).uuid; } } if ( this.environment && this.environment.isTexture ) { object.environment = this.environment.toJSON( meta ).uuid; } } else if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); const parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { const shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( let i = 0, l = shapes.length; i < l; i ++ ) { const shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.isSkinnedMesh ) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if ( this.skeleton !== undefined ) { serialize( meta.skeletons, this.skeleton ); object.skeleton = this.skeleton.uuid; } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { const uuids = []; for ( let i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( let i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } // if ( this.animations.length > 0 ) { object.animations = []; for ( let i = 0; i < this.animations.length; i ++ ) { const animation = this.animations[ i ]; object.animations.push( serialize( meta.animations, animation ) ); } } if ( isRootObject ) { const geometries = extractFromCache( meta.geometries ); const materials = extractFromCache( meta.materials ); const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); const shapes = extractFromCache( meta.shapes ); const skeletons = extractFromCache( meta.skeletons ); const animations = extractFromCache( meta.animations ); const nodes = extractFromCache( meta.nodes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; if ( skeletons.length > 0 ) output.skeletons = skeletons; if ( animations.length > 0 ) output.animations = animations; if ( nodes.length > 0 ) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } } clone( recursive ) { return new this.constructor().copy( this, recursive ); } copy( source, recursive = true ) { this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.rotation.order = source.rotation.order; this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( let i = 0; i < source.children.length; i ++ ) { const child = source.children[ i ]; this.add( child.clone() ); } } return this; } } Object3D.DefaultUp = /*@__PURE__*/ new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); const _v2$2 = /*@__PURE__*/ new Vector3(); const _v3$1 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); const _vbc = /*@__PURE__*/ new Vector3(); const _vap = /*@__PURE__*/ new Vector3(); const _vbp = /*@__PURE__*/ new Vector3(); const _vcp = /*@__PURE__*/ new Vector3(); class Triangle { constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { this.a = a; this.b = b; this.c = c; } static getNormal( a, b, c, target ) { target.subVectors( c, b ); _v0$1.subVectors( a, b ); target.cross( _v0$1 ); const targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord( point, a, b, c, target ) { _v0$1.subVectors( c, a ); _v1$3.subVectors( b, a ); _v2$2.subVectors( point, a ); const dot00 = _v0$1.dot( _v0$1 ); const dot01 = _v0$1.dot( _v1$3 ); const dot02 = _v0$1.dot( _v2$2 ); const dot11 = _v1$3.dot( _v1$3 ); const dot12 = _v1$3.dot( _v2$2 ); const denom = ( dot00 * dot11 - dot01 * dot01 ); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } const invDenom = 1 / denom; const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); } static containsPoint( point, a, b, c ) { this.getBarycoord( point, a, b, c, _v3$1 ); return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { this.getBarycoord( point, p1, p2, p3, _v3$1 ); target.set( 0, 0 ); target.addScaledVector( uv1, _v3$1.x ); target.addScaledVector( uv2, _v3$1.y ); target.addScaledVector( uv3, _v3$1.z ); return target; } static isFrontFacing( a, b, c, direction ) { _v0$1.subVectors( c, b ); _v1$3.subVectors( a, b ); // strictly front facing return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; } set( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; } setFromPointsAndIndices( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; } setFromAttributeAndIndices( attribute, i0, i1, i2 ) { this.a.fromBufferAttribute( attribute, i0 ); this.b.fromBufferAttribute( attribute, i1 ); this.c.fromBufferAttribute( attribute, i2 ); return this; } clone() { return new this.constructor().copy( this ); } copy( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; } getArea() { _v0$1.subVectors( this.c, this.b ); _v1$3.subVectors( this.a, this.b ); return _v0$1.cross( _v1$3 ).length() * 0.5; } getMidpoint( target ) { return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } getNormal( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); } getPlane( target ) { return target.setFromCoplanarPoints( this.a, this.b, this.c ); } getBarycoord( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); } getUV( point, uv1, uv2, uv3, target ) { return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } containsPoint( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); } isFrontFacing( direction ) { return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); } intersectsBox( box ) { return box.intersectsTriangle( this ); } closestPointToPoint( p, target ) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we"re distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors( b, a ); _vac.subVectors( c, a ); _vap.subVectors( p, a ); const d1 = _vab.dot( _vap ); const d2 = _vac.dot( _vap ); if ( d1 <= 0 && d2 <= 0 ) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy( a ); } _vbp.subVectors( p, b ); const d3 = _vab.dot( _vbp ); const d4 = _vac.dot( _vbp ); if ( d3 >= 0 && d4 <= d3 ) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy( b ); } const vc = d1 * d4 - d3 * d2; if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { v = d1 / ( d1 - d3 ); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy( a ).addScaledVector( _vab, v ); } _vcp.subVectors( p, c ); const d5 = _vab.dot( _vcp ); const d6 = _vac.dot( _vcp ); if ( d6 >= 0 && d5 <= d6 ) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy( c ); } const vb = d5 * d2 - d1 * d6; if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { w = d2 / ( d2 - d6 ); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy( a ).addScaledVector( _vac, w ); } const va = d3 * d6 - d5 * d4; if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { _vbc.subVectors( c, b ); w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC } // face region const denom = 1 / ( va + vb + vc ); // u = va * denom v = vb * denom; w = vc * denom; return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); } equals( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "Material"; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest( value ) { if ( this._alphaTest > 0 !== value > 0 ) { this.version ++; } this._alphaTest = value; } onBuild( /* shaderobject, renderer */ ) {} onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} onBeforeCompile( /* shaderobject, renderer */ ) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues( values ) { if ( values === undefined ) return; for ( const key in values ) { const newValue = values[ key ]; if ( newValue === undefined ) { console.warn( "THREE.Material: "" + key + "" parameter is undefined." ); continue; } // for backward compatibility if shading is set in the constructor if ( key === "shading" ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( newValue === FlatShading ) ? true : false; continue; } const currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( "THREE." + this.type + ": "" + key + "" is not a property of this material." ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else { this[ key ] = newValue; } } } toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === "string" ); if ( isRootObject ) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.sheen !== undefined ) data.sheen = this.sheen; if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; } if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; } if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; } if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; } if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) { data.lightMap = this.lightMap.toJSON( meta ).uuid; data.lightMapIntensity = this.lightMapIntensity; } if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; if ( this.combine !== undefined ) data.combine = this.combine; } if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.transmission !== undefined ) data.transmission = this.transmission; if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; if ( this.thickness !== undefined ) data.thickness = this.thickness; if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); if ( this.size !== undefined ) data.size = this.size; if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors ) data.vertexColors = true; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; if ( this.polygonOffset === true ) data.polygonOffset = true; if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.visible === false ) data.visible = false; if ( this.toneMapped === false ) data.toneMapped = false; if ( this.fog === false ) data.fog = false; if ( JSON.stringify( this.userData ) !== "{}" ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { const values = []; for ( const key in cache ) { const data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRootObject ) { const textures = extractFromCache( meta.textures ); const images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if ( srcPlanes !== null ) { const n = srcPlanes.length; dstPlanes = new Array( n ); for ( let i = 0; i !== n; ++ i ) { dstPlanes[ i ] = srcPlanes[ i ].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse( JSON.stringify( source.userData ) ); return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } set needsUpdate( value ) { if ( value === true ) this.version ++; } } class MeshBasicMaterial extends Material { constructor( parameters ) { super(); this.isMeshBasicMaterial = true; this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } const _vector$9 = /*@__PURE__*/ new Vector3(); const _vector2$1 = /*@__PURE__*/ new Vector2(); class BufferAttribute { constructor( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.isBufferAttribute = true; this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( let i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } copyArray( array ) { this.array.set( array ); return this; } copyColorsArray( colors ) { const array = this.array; let offset = 0; for ( let i = 0, l = colors.length; i < l; i ++ ) { let color = colors[ i ]; if ( color === undefined ) { console.warn( "THREE.BufferAttribute.copyColorsArray(): color is undefined", i ); color = new Color(); } array[ offset ++ ] = color.r; array[ offset ++ ] = color.g; array[ offset ++ ] = color.b; } return this; } copyVector2sArray( vectors ) { const array = this.array; let offset = 0; for ( let i = 0, l = vectors.length; i < l; i ++ ) { let vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i ); vector = new Vector2(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; } return this; } copyVector3sArray( vectors ) { const array = this.array; let offset = 0; for ( let i = 0, l = vectors.length; i < l; i ++ ) { let vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i ); vector = new Vector3(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; } return this; } copyVector4sArray( vectors ) { const array = this.array; let offset = 0; for ( let i = 0, l = vectors.length; i < l; i ++ ) { let vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i ); vector = new Vector4(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; array[ offset ++ ] = vector.w; } return this; } applyMatrix3( m ) { if ( this.itemSize === 2 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector2$1.fromBufferAttribute( this, i ); _vector2$1.applyMatrix3( m ); this.setXY( i, _vector2$1.x, _vector2$1.y ); } } else if ( this.itemSize === 3 ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$9.fromBufferAttribute( this, i ); _vector$9.applyMatrix3( m ); this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } } return this; } applyMatrix4( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$9.fromBufferAttribute( this, i ); _vector$9.applyMatrix4( m ); this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$9.fromBufferAttribute( this, i ); _vector$9.applyNormalMatrix( m ); this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$9.fromBufferAttribute( this, i ); _vector$9.transformDirection( m ); this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; } set( value, offset = 0 ) { this.array.set( value, offset ); return this; } getX( index ) { return this.array[ index * this.itemSize ]; } setX( index, x ) { this.array[ index * this.itemSize ] = x; return this; } getY( index ) { return this.array[ index * this.itemSize + 1 ]; } setY( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; } getZ( index ) { return this.array[ index * this.itemSize + 2 ]; } setZ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; } getW( index ) { return this.array[ index * this.itemSize + 3 ]; } setW( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; } setXY( index, x, y ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; } onUpload( callback ) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor( this.array, this.itemSize ).copy( this ); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from( this.array ), normalized: this.normalized }; if ( this.name !== "" ) data.name = this.name; if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; return data; } } class Uint16BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint16Array( array ), itemSize, normalized ); } } class Uint32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Uint32Array( array ), itemSize, normalized ); } } class Float32BufferAttribute extends BufferAttribute { constructor( array, itemSize, normalized ) { super( new Float32Array( array ), itemSize, normalized ); } } let _id$1 = 0; const _m1 = /*@__PURE__*/ new Matrix4(); const _obj = /*@__PURE__*/ new Object3D(); const _offset = /*@__PURE__*/ new Vector3(); const _box$1 = /*@__PURE__*/ new Box3(); const _boxMorphTargets = /*@__PURE__*/ new Box3(); const _vector$8 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty( this, "id", { value: _id$1 ++ } ); this.uuid = generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } return this; } getAttribute( name ) { return this.attributes[ name ]; } setAttribute( name, attribute ) { this.attributes[ name ] = attribute; return this; } deleteAttribute( name ) { delete this.attributes[ name ]; return this; } hasAttribute( name ) { return this.attributes[ name ] !== undefined; } addGroup( start, count, materialIndex = 0 ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex } ); } clearGroups() { this.groups = []; } setDrawRange( start, count ) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4( matrix ) { const position = this.attributes.position; if ( position !== undefined ) { position.applyMatrix4( matrix ); position.needsUpdate = true; } const normal = this.attributes.normal; if ( normal !== undefined ) { const normalMatrix = new Matrix3().getNormalMatrix( matrix ); normal.applyNormalMatrix( normalMatrix ); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if ( tangent !== undefined ) { tangent.transformDirection( matrix ); tangent.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; } applyQuaternion( q ) { _m1.makeRotationFromQuaternion( q ); this.applyMatrix4( _m1 ); return this; } rotateX( angle ) { // rotate geometry around world x-axis _m1.makeRotationX( angle ); this.applyMatrix4( _m1 ); return this; } rotateY( angle ) { // rotate geometry around world y-axis _m1.makeRotationY( angle ); this.applyMatrix4( _m1 ); return this; } rotateZ( angle ) { // rotate geometry around world z-axis _m1.makeRotationZ( angle ); this.applyMatrix4( _m1 ); return this; } translate( x, y, z ) { // translate geometry _m1.makeTranslation( x, y, z ); this.applyMatrix4( _m1 ); return this; } scale( x, y, z ) { // scale geometry _m1.makeScale( x, y, z ); this.applyMatrix4( _m1 ); return this; } lookAt( vector ) { _obj.lookAt( vector ); _obj.updateMatrix(); this.applyMatrix4( _obj.matrix ); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter( _offset ).negate(); this.translate( _offset.x, _offset.y, _offset.z ); return this; } setFromPoints( points ) { const position = []; for ( let i = 0, l = points.length; i < l; i ++ ) { const point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.setAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; } computeBoundingBox() { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingBox.set( new Vector3( - Infinity, - Infinity, - Infinity ), new Vector3( + Infinity, + Infinity, + Infinity ) ); return; } if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _box$1.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$8.addVectors( this.boundingBox.min, _box$1.min ); this.boundingBox.expandByPoint( _vector$8 ); _vector$8.addVectors( this.boundingBox.max, _box$1.max ); this.boundingBox.expandByPoint( _vector$8 ); } else { this.boundingBox.expandByPoint( _box$1.min ); this.boundingBox.expandByPoint( _box$1.max ); } } } } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } } computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if ( position && position.isGLBufferAttribute ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".", this ); this.boundingSphere.set( new Vector3(), Infinity ); return; } if ( position ) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute( position ); // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; _boxMorphTargets.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { _vector$8.addVectors( _box$1.min, _boxMorphTargets.min ); _box$1.expandByPoint( _vector$8 ); _vector$8.addVectors( _box$1.max, _boxMorphTargets.max ); _box$1.expandByPoint( _vector$8 ); } else { _box$1.expandByPoint( _boxMorphTargets.min ); _box$1.expandByPoint( _boxMorphTargets.max ); } } } _box$1.getCenter( center ); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for ( let i = 0, il = position.count; i < il; i ++ ) { _vector$8.fromBufferAttribute( position, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); } // process morph attributes if present if ( morphAttributesPosition ) { for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; const morphTargetsRelative = this.morphTargetsRelative; for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { _vector$8.fromBufferAttribute( morphAttribute, j ); if ( morphTargetsRelative ) { _offset.fromBufferAttribute( position, j ); _vector$8.add( _offset ); } maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); } } } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined ) { console.error( "THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)" ); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if ( this.hasAttribute( "tangent" ) === false ) { this.setAttribute( "tangent", new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } const tangents = this.getAttribute( "tangent" ).array; const tan1 = [], tan2 = []; for ( let i = 0; i < nVertices; i ++ ) { tan1[ i ] = new Vector3(); tan2[ i ] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle( a, b, c ) { vA.fromArray( positions, a * 3 ); vB.fromArray( positions, b * 3 ); vC.fromArray( positions, c * 3 ); uvA.fromArray( uvs, a * 2 ); uvB.fromArray( uvs, b * 2 ); uvC.fromArray( uvs, c * 2 ); vB.sub( vA ); vC.sub( vA ); uvB.sub( uvA ); uvC.sub( uvA ); const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); // silently ignore degenerate uv triangles having coincident or colinear vertices if ( ! isFinite( r ) ) return; sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); } let groups = this.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.length } ]; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleTriangle( indices[ j + 0 ], indices[ j + 1 ], indices[ j + 2 ] ); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex( v ) { n.fromArray( normals, v * 3 ); n2.copy( n ); const t = tan1[ v ]; // Gram-Schmidt orthogonalize tmp.copy( t ); tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2.crossVectors( n2, t ); const test = tmp2.dot( tan2[ v ] ); const w = ( test < 0.0 ) ? - 1.0 : 1.0; tangents[ v * 4 ] = tmp.x; tangents[ v * 4 + 1 ] = tmp.y; tangents[ v * 4 + 2 ] = tmp.z; tangents[ v * 4 + 3 ] = w; } for ( let i = 0, il = groups.length; i < il; ++ i ) { const group = groups[ i ]; const start = group.start; const count = group.count; for ( let j = start, jl = start + count; j < jl; j += 3 ) { handleVertex( indices[ j + 0 ] ); handleVertex( indices[ j + 1 ] ); handleVertex( indices[ j + 2 ] ); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute( "position" ); if ( positionAttribute !== undefined ) { let normalAttribute = this.getAttribute( "normal" ); if ( normalAttribute === undefined ) { normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); this.setAttribute( "normal", normalAttribute ); } else { // reset existing normals to zero for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { normalAttribute.setXYZ( i, 0, 0, 0 ); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { for ( let i = 0, il = index.count; i < il; i += 3 ) { const vA = index.getX( i + 0 ); const vB = index.getX( i + 1 ); const vC = index.getX( i + 2 ); pA.fromBufferAttribute( positionAttribute, vA ); pB.fromBufferAttribute( positionAttribute, vB ); pC.fromBufferAttribute( positionAttribute, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); nA.fromBufferAttribute( normalAttribute, vA ); nB.fromBufferAttribute( normalAttribute, vB ); nC.fromBufferAttribute( normalAttribute, vC ); nA.add( cb ); nB.add( cb ); nC.add( cb ); normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); } } else { // non-indexed elements (unconnected triangle soup) for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { pA.fromBufferAttribute( positionAttribute, i + 0 ); pB.fromBufferAttribute( positionAttribute, i + 1 ); pC.fromBufferAttribute( positionAttribute, i + 2 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } merge( geometry, offset ) { if ( ! ( geometry && geometry.isBufferGeometry ) ) { console.error( "THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry ); return; } if ( offset === undefined ) { offset = 0; console.warn( "THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge." ); } const attributes = this.attributes; for ( const key in attributes ) { if ( geometry.attributes[ key ] === undefined ) continue; const attribute1 = attributes[ key ]; const attributeArray1 = attribute1.array; const attribute2 = geometry.attributes[ key ]; const attributeArray2 = attribute2.array; const attributeOffset = attribute2.itemSize * offset; const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; } } return this; } normalizeNormals() { const normals = this.attributes.normal; for ( let i = 0, il = normals.count; i < il; i ++ ) { _vector$8.fromBufferAttribute( normals, i ); _vector$8.normalize(); normals.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } } toNonIndexed() { function convertBufferAttribute( attribute, indices ) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor( indices.length * itemSize ); let index = 0, index2 = 0; for ( let i = 0, l = indices.length; i < l; i ++ ) { if ( attribute.isInterleavedBufferAttribute ) { index = indices[ i ] * attribute.data.stride + attribute.offset; } else { index = indices[ i ] * itemSize; } for ( let j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } return new BufferAttribute( array2, itemSize, normalized ); } // if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed." ); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for ( const name in attributes ) { const attribute = attributes[ name ]; const newAttribute = convertBufferAttribute( attribute, indices ); geometry2.setAttribute( name, newAttribute ); } // morph attributes const morphAttributes = this.morphAttributes; for ( const name in morphAttributes ) { const morphArray = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { const attribute = morphAttribute[ i ]; const newAttribute = convertBufferAttribute( attribute, indices ); morphArray.push( newAttribute ); } geometry2.morphAttributes[ name ] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; } toJSON() { const data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { const parameters = this.parameters; for ( const key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if ( index !== null ) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call( index.array ) }; } const attributes = this.attributes; for ( const key in attributes ) { const attribute = attributes[ key ]; data.data.attributes[ key ] = attribute.toJSON( data.data ); } const morphAttributes = {}; let hasMorphAttributes = false; for ( const key in this.morphAttributes ) { const attributeArray = this.morphAttributes[ key ]; const array = []; for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { const attribute = attributeArray[ i ]; array.push( attribute.toJSON( data.data ) ); } if ( array.length > 0 ) { morphAttributes[ key ] = array; hasMorphAttributes = true; } } if ( hasMorphAttributes ) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } const boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy( this ); } copy( source ) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if ( index !== null ) { this.setIndex( index.clone( data ) ); } // attributes const attributes = source.attributes; for ( const name in attributes ) { const attribute = attributes[ name ]; this.setAttribute( name, attribute.clone( data ) ); } // morph attributes const morphAttributes = source.morphAttributes; for ( const name in morphAttributes ) { const array = []; const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone( data ) ); } this.morphAttributes[ name ] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box const boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; // geometry generator parameters if ( source.parameters !== undefined ) this.parameters = Object.assign( {}, source.parameters ); return this; } dispose() { this.dispatchEvent( { type: "dispose" } ); } } const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); const _ray$2 = /*@__PURE__*/ new Ray(); const _sphere$3 = /*@__PURE__*/ new Sphere(); const _vA$1 = /*@__PURE__*/ new Vector3(); const _vB$1 = /*@__PURE__*/ new Vector3(); const _vC$1 = /*@__PURE__*/ new Vector3(); const _tempA = /*@__PURE__*/ new Vector3(); const _tempB = /*@__PURE__*/ new Vector3(); const _tempC = /*@__PURE__*/ new Vector3(); const _morphA = /*@__PURE__*/ new Vector3(); const _morphB = /*@__PURE__*/ new Vector3(); const _morphC = /*@__PURE__*/ new Vector3(); const _uvA$1 = /*@__PURE__*/ new Vector2(); const _uvB$1 = /*@__PURE__*/ new Vector2(); const _uvC$1 = /*@__PURE__*/ new Vector2(); const _intersectionPoint = /*@__PURE__*/ new Vector3(); const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); class Mesh extends Object3D { constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { super(); this.isMesh = true; this.type = "Mesh"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } raycast( raycaster, intersects ) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$3.copy( geometry.boundingSphere ); _sphere$3.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; // _inverseMatrix$2.copy( matrixWorld ).invert(); _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( _ray$2.intersectsBox( geometry.boundingBox ) === false ) return; } let intersection; const index = geometry.index; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const uv = geometry.attributes.uv; const uv2 = geometry.attributes.uv2; const groups = geometry.groups; const drawRange = geometry.drawRange; if ( index !== null ) { // indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = index.getX( j ); const b = index.getX( j + 1 ); const c = index.getX( j + 2 ); intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = index.getX( i ); const b = index.getX( i + 1 ); const c = index.getX( i + 2 ); intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } } else if ( position !== undefined ) { // non-indexed buffer geometry if ( Array.isArray( material ) ) { for ( let i = 0, il = groups.length; i < il; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { const a = j; const b = j + 1; const c = j + 2; intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push( intersection ); } } } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i += 3 ) { const a = i; const b = i + 1; const c = i + 2; intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } } } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { let intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); } if ( intersect === null ) return null; _intersectionPointWorld.copy( point ); _intersectionPointWorld.applyMatrix4( object.matrixWorld ); const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { _vA$1.fromBufferAttribute( position, a ); _vB$1.fromBufferAttribute( position, b ); _vC$1.fromBufferAttribute( position, c ); const morphInfluences = object.morphTargetInfluences; if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); _morphB.set( 0, 0, 0 ); _morphC.set( 0, 0, 0 ); for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { const influence = morphInfluences[ i ]; const morphAttribute = morphPosition[ i ]; if ( influence === 0 ) continue; _tempA.fromBufferAttribute( morphAttribute, a ); _tempB.fromBufferAttribute( morphAttribute, b ); _tempC.fromBufferAttribute( morphAttribute, c ); if ( morphTargetsRelative ) { _morphA.addScaledVector( _tempA, influence ); _morphB.addScaledVector( _tempB, influence ); _morphC.addScaledVector( _tempC, influence ); } else { _morphA.addScaledVector( _tempA.sub( _vA$1 ), influence ); _morphB.addScaledVector( _tempB.sub( _vB$1 ), influence ); _morphC.addScaledVector( _tempC.sub( _vC$1 ), influence ); } } _vA$1.add( _morphA ); _vB$1.add( _morphB ); _vC$1.add( _morphC ); } if ( object.isSkinnedMesh ) { object.boneTransform( a, _vA$1 ); object.boneTransform( b, _vB$1 ); object.boneTransform( c, _vC$1 ); } const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); if ( intersection ) { if ( uv ) { _uvA$1.fromBufferAttribute( uv, a ); _uvB$1.fromBufferAttribute( uv, b ); _uvC$1.fromBufferAttribute( uv, c ); intersection.uv = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } if ( uv2 ) { _uvA$1.fromBufferAttribute( uv2, a ); _uvB$1.fromBufferAttribute( uv2, b ); _uvC$1.fromBufferAttribute( uv2, c ); intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { super(); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor( widthSegments ); heightSegments = Math.floor( heightSegments ); depthSegments = Math.floor( depthSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segmentHeight - heightHalf; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } static fromJSON( data ) { return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); } } /** * Uniform Utilities */ function cloneUniforms( src ) { const dst = {}; for ( const u in src ) { dst[ u ] = {}; for ( const p in src[ u ] ) { const property = src[ u ][ p ]; if ( property && ( property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion ) ) { dst[ u ][ p ] = property.clone(); } else if ( Array.isArray( property ) ) { dst[ u ][ p ] = property.slice(); } else { dst[ u ][ p ] = property; } } } return dst; } function mergeUniforms( uniforms ) { const merged = {}; for ( let u = 0; u < uniforms.length; u ++ ) { const tmp = cloneUniforms( uniforms[ u ] ); for ( const p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; var default_fragment = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; class ShaderMaterial extends Material { constructor( parameters ) { super(); this.isShaderMaterial = true; this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv2": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if ( parameters !== undefined ) { if ( parameters.attributes !== undefined ) { console.error( "THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead." ); } this.setValues( parameters ); } } copy( source ) { super.copy( source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms( source.uniforms ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; return this; } toJSON( meta ) { const data = super.toJSON( meta ); data.glslVersion = this.glslVersion; data.uniforms = {}; for ( const name in this.uniforms ) { const uniform = this.uniforms[ name ]; const value = uniform.value; if ( value && value.isTexture ) { data.uniforms[ name ] = { type: "t", value: value.toJSON( meta ).uuid }; } else if ( value && value.isColor ) { data.uniforms[ name ] = { type: "c", value: value.getHex() }; } else if ( value && value.isVector2 ) { data.uniforms[ name ] = { type: "v2", value: value.toArray() }; } else if ( value && value.isVector3 ) { data.uniforms[ name ] = { type: "v3", value: value.toArray() }; } else if ( value && value.isVector4 ) { data.uniforms[ name ] = { type: "v4", value: value.toArray() }; } else if ( value && value.isMatrix3 ) { data.uniforms[ name ] = { type: "m3", value: value.toArray() }; } else if ( value && value.isMatrix4 ) { data.uniforms[ name ] = { type: "m4", value: value.toArray() }; } else { data.uniforms[ name ] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; const extensions = {}; for ( const key in this.extensions ) { if ( this.extensions[ key ] === true ) extensions[ key ] = true; } if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; return data; } } class Camera$2 extends Object3D { constructor() { super(); this.isCamera = true; this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); } copy( source, recursive ) { super.copy( source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); return this; } getWorldDirection( target ) { this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } updateWorldMatrix( updateParents, updateChildren ) { super.updateWorldMatrix( updateParents, updateChildren ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); } clone() { return new this.constructor().copy( this ); } } class PerspectiveCamera$1 extends Camera$2 { constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { super(); this.isPerspectiveCamera = true; this.type = "PerspectiveCamera"; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength( focalLength ) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG$1 * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan( DEG2RAD$1 * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG$1 * 2 * Math.atan( Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom ); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = - 0.5 * width; const view = this.view; if ( this.view !== null && this.view.enabled ) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = 90, aspect = 1; class CubeCamera extends Object3D { constructor( near, far, renderTarget ) { super(); this.type = "CubeCamera"; if ( renderTarget.isWebGLCubeRenderTarget !== true ) { console.error( "THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter." ); return; } this.renderTarget = renderTarget; const cameraPX = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPX.layers = this.layers; cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); this.add( cameraPX ); const cameraNX = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNX.layers = this.layers; cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); this.add( cameraNX ); const cameraPY = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPY.layers = this.layers; cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); this.add( cameraPY ); const cameraNY = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNY.layers = this.layers; cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); this.add( cameraNY ); const cameraPZ = new PerspectiveCamera$1( fov, aspect, near, far ); cameraPZ.layers = this.layers; cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); this.add( cameraPZ ); const cameraNZ = new PerspectiveCamera$1( fov, aspect, near, far ); cameraNZ.layers = this.layers; cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); } update( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); const renderTarget = this.renderTarget; const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentToneMapping = renderer.toneMapping; const currentXrEnabled = renderer.xr.enabled; renderer.toneMapping = NoToneMapping; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget( renderTarget, 0 ); renderer.render( scene, cameraPX ); renderer.setRenderTarget( renderTarget, 1 ); renderer.render( scene, cameraNX ); renderer.setRenderTarget( renderTarget, 2 ); renderer.render( scene, cameraPY ); renderer.setRenderTarget( renderTarget, 3 ); renderer.render( scene, cameraNY ); renderer.setRenderTarget( renderTarget, 4 ); renderer.render( scene, cameraPZ ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget( renderTarget, 5 ); renderer.render( scene, cameraNZ ); renderer.setRenderTarget( currentRenderTarget ); renderer.toneMapping = currentToneMapping; renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images( value ) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor( size, options = {} ) { super( size, size, options ); this.isWebGLCubeRenderTarget = true; const image = { width: size, height: size, depth: 1 }; const images = [ image, image, image, image, image, image ]; this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); // By convention -- likely based on the RenderMan spec from the 1990"s -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null }, }, vertexShader: /* glsl */` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include } `, fragmentShader: /* glsl */` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry( 5, 5, 5 ); const material = new ShaderMaterial( { name: "CubemapFromEquirect", uniforms: cloneUniforms( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending } ); material.uniforms.tEquirect.value = texture; const mesh = new Mesh( geometry, material ); const currentMinFilter = texture.minFilter; // Avoid blurred poles if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; const camera = new CubeCamera( 1, 10, this ); camera.update( renderer, mesh ); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear( renderer, color, depth, stencil ) { const currentRenderTarget = renderer.getRenderTarget(); for ( let i = 0; i < 6; i ++ ) { renderer.setRenderTarget( this, i ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( currentRenderTarget ); } } const _vector1 = /*@__PURE__*/ new Vector3(); const _vector2 = /*@__PURE__*/ new Vector3(); const _normalMatrix = /*@__PURE__*/ new Matrix3(); class Plane { constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; } setComponents( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; } setFromNormalAndCoplanarPoint( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; } setFromCoplanarPoints( a, b, c ) { const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; } copy( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= - 1; this.normal.negate(); return this; } distanceToPoint( point ) { return this.normal.dot( point ) + this.constant; } distanceToSphere( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; } projectPoint( point, target ) { return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); } intersectLine( line, target ) { const direction = line.delta( _vector1 ); const denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return null; } const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return null; } return target.copy( direction ).multiplyScalar( t ).add( line.start ); } intersectsLine( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint( line.start ); const endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); } intersectsBox( box ) { return box.intersectsPlane( this ); } intersectsSphere( sphere ) { return sphere.intersectsPlane( this ); } coplanarPoint( target ) { return target.copy( this.normal ).multiplyScalar( - this.constant ); } applyMatrix4( matrix, optionalNormalMatrix ) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; } translate( offset ) { this.constant -= offset.dot( this.normal ); return this; } equals( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } clone() { return new this.constructor().copy( this ); } } const _sphere$2 = /*@__PURE__*/ new Sphere(); const _vector$7 = /*@__PURE__*/ new Vector3(); class Frustum { constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { this.planes = [ p0, p1, p2, p3, p4, p5 ]; } set( p0, p1, p2, p3, p4, p5 ) { const planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; } copy( frustum ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; } setFromProjectionMatrix( m ) { const planes = this.planes; const me = m.elements; const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; } intersectsObject( object ) { const geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); return this.intersectsSphere( _sphere$2 ); } intersectsSprite( sprite ) { _sphere$2.center.set( 0, 0, 0 ); _sphere$2.radius = 0.7071067811865476; _sphere$2.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( _sphere$2 ); } intersectsSphere( sphere ) { const planes = this.planes; const center = sphere.center; const negRadius = - sphere.radius; for ( let i = 0; i < 6; i ++ ) { const distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; } intersectsBox( box ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { const plane = planes[ i ]; // corner at max distance _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; if ( plane.distanceToPoint( _vector$7 ) < 0 ) { return false; } } return true; } containsPoint( point ) { const planes = this.planes; for ( let i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } clone() { return new this.constructor().copy( this ); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame( time, frame ) { animationLoop( time, frame ); requestId = context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; requestId = context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { context.cancelAnimationFrame( requestId ); isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } function WebGLAttributes( gl, capabilities ) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); let type; if ( array instanceof Float32Array ) { type = 5126; } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { if ( isWebGL2 ) { type = 5131; } else { throw new Error( "THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2." ); } } else { type = 5123; } } else if ( array instanceof Int16Array ) { type = 5122; } else if ( array instanceof Uint32Array ) { type = 5125; } else if ( array instanceof Int32Array ) { type = 5124; } else if ( array instanceof Int8Array ) { type = 5120; } else if ( array instanceof Uint8Array ) { type = 5121; } else if ( array instanceof Uint8ClampedArray ) { type = 5121; } else { throw new Error( "THREE.WebGLAttributes: Unsupported buffer data format: " + array ); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else { if ( isWebGL2 ) { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); } updateRange.count = - 1; // reset range } } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isGLBufferAttribute ) { const cached = buffers.get( attribute ); if ( ! cached || cached.version < attribute.version ) { buffers.set( attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version } ); } return; } if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; const data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { super(); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor( widthSegments ); const gridY = Math.floor( heightSegments ); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for ( let iy = 0; iy < gridY1; iy ++ ) { const y = iy * segment_height - height_half; for ( let ix = 0; ix < gridX1; ix ++ ) { const x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } for ( let iy = 0; iy < gridY; iy ++ ) { for ( let ix = 0; ix < gridX; ix ++ ) { const a = ix + gridX1 * iy; const b = ix + gridX1 * ( iy + 1 ); const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); const d = ( ix + 1 ) + gridX1 * iy; indices.push( a, b, d ); indices.push( b, c, d ); } } this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } static fromJSON( data ) { return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); } } var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST uniform float alphaTest; #endif"; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( STANDARD ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif #endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif"; var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); } vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); float x2 = x * x; float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); } float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( f0, f90, dotVH ); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #ifdef USE_IRIDESCENCE vec3 BRDF_GGX_Iridescence( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float iridescence, const in vec3 iridescenceFresnel, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = mix(F_Schlick( f0, f90, dotVH ), iridescenceFresnel, iridescence); float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( V * D ); } #endif vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNH = saturate( dot( normal, halfDir ) ); float dotVH = saturate( dot( viewDir, halfDir ) ); vec3 F = F_Schlick( specularColor, 1.0, dotVH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } #if defined( USE_SHEEN ) float D_Charlie( float roughness, float dotNH ) { float alpha = pow2( roughness ); float invAlpha = 1.0 / alpha; float cos2h = dotNH * dotNH; float sin2h = max( 1.0 - cos2h, 0.0078125 ); return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); } float V_Neubelt( float dotNV, float dotNL ) { return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); } vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { vec3 halfDir = normalize( lightDir + viewDir ); float dotNL = saturate( dot( normal, lightDir ) ); float dotNV = saturate( dot( normal, viewDir ) ); float dotNH = saturate( dot( normal, halfDir ) ); float D = D_Charlie( sheenRoughness, dotNH ); float V = V_Neubelt( dotNV, dotNL ); return sheenColor * ( D * V ); } #endif"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252 ); vec3 Fresnel0ToIor( vec3 fresnel0 ) { vec3 sqrtF0 = sqrt( fresnel0 ); return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); } vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); } float IorToFresnel0( float transmittedIor, float incidentIor ) { return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); } vec3 evalSensitivity( float OPD, vec3 shift ) { float phase = 2.0 * PI * OPD * 1.0e-9; vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( -pow2( phase ) * var ); xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[0] ) * exp( -4.5282e+09 * pow2( phase ) ); xyz /= 1.0685e-7; vec3 srgb = XYZ_TO_REC709 * xyz; return srgb; } vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { vec3 I; float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); float cosTheta2Sq = 1.0 - sinTheta2Sq; if ( cosTheta2Sq < 0.0 ) { return vec3( 1.0 ); } float cosTheta2 = sqrt( cosTheta2Sq ); float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); float R12 = F_Schlick( R0, 1.0, cosTheta1 ); float R21 = R12; float T121 = 1.0 - R12; float phi12 = 0.0; if ( iridescenceIOR < outsideIOR ) phi12 = PI; float phi21 = PI - phi12; vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); vec3 phi23 = vec3( 0.0 ); if ( baseIOR[0] < iridescenceIOR ) phi23[0] = PI; if ( baseIOR[1] < iridescenceIOR ) phi23[1] = PI; if ( baseIOR[2] < iridescenceIOR ) phi23[2] = PI; float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; vec3 phi = vec3( phi21 ) + phi23; vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); vec3 r123 = sqrt( R123 ); vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); vec3 C0 = R12 + Rs; I = C0; vec3 Cm = Rs - T121; for ( int m = 1; m <= 2; ++m ) { Cm *= r123; vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); I += Cm * Sm; } return max( I, vec3( 0.0 ) ); } #endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ) * faceDirection; vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop_start for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; } #pragma unroll_loop_end #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop_start for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; } #pragma unroll_loop_end if ( clipped ) discard; #endif #endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; #endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; #endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; #elif defined( USE_COLOR ) diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) varying vec3 vColor; #endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA ) varying vec4 vColor; #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) varying vec3 vColor; #endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA ) vColor = vec4( 1.0 ); #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) vColor = vec3( 1.0 ); #endif #ifdef USE_COLOR vColor *= color; #endif #ifdef USE_INSTANCING_COLOR vColor.xyz *= instanceColor.xyz; #endif"; var common$2 = "#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 #define RECIPROCAL_PI2 0.15915494309189535 #define EPSILON 1e-6 #ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif #define whiteComplement( a ) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } vec3 pow2( const in vec3 x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract( sin( sn ) * c ); } #ifdef HIGH_PRECISION float precisionSafeLength( vec3 v ) { return length( v ); } #else float precisionSafeLength( vec3 v ) { float maxComponent = max3( abs( v ) ); return length( v / maxComponent ) * maxComponent; } #endif struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; #ifdef USE_CLEARCOAT vec3 clearcoatNormal; #endif }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } bool isPerspectiveMatrix( mat4 m ) { return m[ 2 ][ 3 ] == - 1.0; } vec2 equirectUv( in vec3 dir ) { float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; return vec2( u, v ); }"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { vec3 absDirection = abs( direction ); float face = - 1.0; if ( absDirection.x > absDirection.z ) { if ( absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0.0 : 3.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } else { if ( absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2.0 : 5.0; else face = direction.y > 0.0 ? 1.0 : 4.0; } return face; } vec2 getUV( vec3 direction, float face ) { vec2 uv; if ( face == 0.0 ) { uv = vec2( direction.z, direction.y ) / abs( direction.x ); } else if ( face == 1.0 ) { uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); } else if ( face == 2.0 ) { uv = vec2( - direction.x, direction.y ) / abs( direction.z ); } else if ( face == 3.0 ) { uv = vec2( - direction.z, direction.y ) / abs( direction.x ); } else if ( face == 4.0 ) { uv = vec2( - direction.x, direction.z ) / abs( direction.y ); } else { uv = vec2( direction.x, direction.y ) / abs( direction.z ); } return 0.5 * ( uv + 1.0 ); } vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { float face = getFace( direction ); float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); mipInt = max( mipInt, cubeUV_minMipLevel ); float faceSize = exp2( mipInt ); vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; if ( face > 2.0 ) { uv.y += faceSize; face -= 3.0; } uv.x += face * faceSize; uv.x += filterInt * 3.0 * cubeUV_minTileSize; uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); uv.x *= CUBEUV_TEXEL_WIDTH; uv.y *= CUBEUV_TEXEL_HEIGHT; #ifdef texture2DGradEXT return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; #else return texture2D( envMap, uv ).rgb; #endif } #define r0 1.0 #define v0 0.339 #define m0 - 2.0 #define r1 0.8 #define v1 0.276 #define m1 - 1.0 #define r4 0.4 #define v4 0.046 #define m4 2.0 #define r5 0.305 #define v5 0.016 #define m5 3.0 #define r6 0.21 #define v6 0.0038 #define m6 4.0 float roughnessToMip( float roughness ) { float mip = 0.0; if ( roughness >= r1 ) { mip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0; } else if ( roughness >= r4 ) { mip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1; } else if ( roughness >= r5 ) { mip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4; } else if ( roughness >= r6 ) { mip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5; } else { mip = - 2.0 * log2( 1.16 * roughness ); } return mip; } vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { float mip = clamp( roughnessToMip( roughness ), m0, CUBEUV_MAX_MIP ); float mipF = fract( mip ); float mipInt = floor( mip ); vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); if ( mipF == 0.0 ) { return vec4( color0, 1.0 ); } else { vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); return vec4( mix( color0, color1, mipF ), 1.0 ); } } #endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING mat3 m = mat3( instanceMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; #endif transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias ); #endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); totalEmissiveRadiance *= emissiveColor.rgb; #endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) { return value; } vec4 LinearTosRGB( in vec4 value ) { return 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 ); }"; var envmap_fragment = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToFrag = normalize( vWorldPosition - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToFrag, worldNormal ); #else vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); #else vec4 envColor = vec4( 0.0 ); #endif #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP uniform float envMapIntensity; uniform float flipEnvMap; #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif #endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG ) #define ENV_WORLDPOS #endif #ifdef ENV_WORLDPOS varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif"; var envmap_vertex = "#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex; if ( isOrthographic ) { cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); } else { cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); } vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif"; var fog_vertex = "#ifdef USE_FOG vFogDepth = - mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float vFogDepth; #endif"; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif"; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return vec3( texture2D( gradientMap, coord ).r ); #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif }"; var lightmap_fragment = "#ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; reflectedLight.indirectDiffuse += lightMapIrradiance; #endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); vIndirectFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); vIndirectBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; vIndirectFront += getAmbientLightIrradiance( ambientLightColor ); vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getAmbientLightIrradiance( ambientLightColor ); vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal ); #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointLightInfo( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotLightInfo( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalLightInfo( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( - dotNL ) * directLightColor_Diffuse; #endif } #pragma unroll_loop_end #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); #ifdef DOUBLE_SIDED vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal ); #endif } #pragma unroll_loop_end #endif"; var lights_pars_begin = "uniform bool receiveShadow; uniform vec3 ambientLightColor; uniform vec3 lightProbe[ 9 ]; vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { float x = normal.x, y = normal.y, z = normal.z; vec3 result = shCoefficients[ 0 ] * 0.886227; result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); return result; } vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); return irradiance; } vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; return irradiance; } float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); if ( cutoffDistance > 0.0 ) { distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); } return distanceFalloff; #else if ( cutoffDistance > 0.0 && decayExponent > 0.0 ) { return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent ); } return 1.0; #endif } float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { return smoothstep( coneCosine, penumbraCosine, angleCosine ); } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) { light.color = directionalLight.color; light.direction = directionalLight.direction; light.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = pointLight.position - geometry.position; light.direction = normalize( lVector ); float lightDistance = length( lVector ); light.color = pointLight.color; light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) { vec3 lVector = spotLight.position - geometry.position; light.direction = normalize( lVector ); float angleCos = dot( light.direction, spotLight.direction ); float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); if ( spotAttenuation > 0.0 ) { float lightDistance = length( lVector ); light.color = spotLight.color * spotAttenuation; light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); light.visible = ( light.color != vec3( 0.0 ) ); } else { light.color = vec3( 0.0 ); light.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { float dotNL = dot( normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); return irradiance; } #endif"; var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) vec3 getIBLIrradiance( const in vec3 normal ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); return PI * envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { #if defined( ENVMAP_TYPE_CUBE_UV ) vec3 reflectVec = reflect( - viewDir, normal ); reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); return envMapColor.rgb * envMapIntensity; #else return vec3( 0.0 ); #endif } #endif"; var lights_toon_fragment = "ToonMaterial material; material.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon #define RE_IndirectDiffuse RE_IndirectDiffuse_Toon #define Material_LightProbeLOD( material ) (0)"; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0)"; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) ); float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; material.roughness = min( material.roughness, 1.0 ); #ifdef IOR #ifdef SPECULAR float specularIntensityFactor = specularIntensity; vec3 specularColorFactor = specularColor; #ifdef USE_SPECULARINTENSITYMAP specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a; #endif #ifdef USE_SPECULARCOLORMAP specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); #else float specularIntensityFactor = 1.0; vec3 specularColorFactor = vec3( 1.0 ); material.specularF90 = 1.0; #endif material.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); material.specularF90 = 1.0; #endif #ifdef USE_CLEARCOAT material.clearcoat = clearcoat; material.clearcoatRoughness = clearcoatRoughness; material.clearcoatF0 = vec3( 0.04 ); material.clearcoatF90 = 1.0; #ifdef USE_CLEARCOATMAP material.clearcoat *= texture2D( clearcoatMap, vUv ).x; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y; #endif material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); material.clearcoatRoughness += geometryRoughness; material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); #endif #ifdef USE_IRIDESCENCE material.iridescence = iridescence; material.iridescenceIOR = iridescenceIOR; #ifdef USE_IRIDESCENCEMAP material.iridescence *= texture2D( iridescenceMap, vUv ).r; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vUv ).g + iridescenceThicknessMinimum; #else material.iridescenceThickness = iridescenceThicknessMaximum; #endif #endif #ifdef USE_SHEEN material.sheenColor = sheenColor; #ifdef USE_SHEENCOLORMAP material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); #ifdef USE_SHEENROUGHNESSMAP material.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a; #endif #endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float roughness; vec3 specularColor; float specularF90; #ifdef USE_CLEARCOAT float clearcoat; float clearcoatRoughness; vec3 clearcoatF0; float clearcoatF90; #endif #ifdef USE_IRIDESCENCE float iridescence; float iridescenceIOR; float iridescenceThickness; vec3 iridescenceFresnel; vec3 iridescenceF0; #endif #ifdef USE_SHEEN vec3 sheenColor; float sheenRoughness; #endif }; vec3 clearcoatSpecular = vec3( 0.0 ); vec3 sheenSpecular = vec3( 0.0 ); float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) { float dotNV = saturate( dot( normal, viewDir ) ); float r2 = roughness * roughness; float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); return saturate( DG * RECIPROCAL_PI ); } vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { float dotNV = saturate( dot( normal, viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; return fab; } vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { vec2 fab = DFGApprox( normal, viewDir, roughness ); return specularColor * fab.x + specularF90 * fab.y; } #ifdef USE_IRIDESCENCE void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #else void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { #endif vec2 fab = DFGApprox( normal, viewDir, roughness ); #ifdef USE_IRIDESCENCE vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); #else vec3 Fr = specularColor; #endif vec3 FssEss = Fr * fab.x + specularF90 * fab.y; float Ess = fab.x + fab.y; float Ems = 1.0 - Ess; vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); singleScatter += FssEss; multiScatter += Fms * Ems; } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.roughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifdef USE_CLEARCOAT float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) ); vec3 ccIrradiance = dotNLcc * directLight.color; clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness ); #endif #ifdef USE_IRIDESCENCE reflectedLight.directSpecular += irradiance * BRDF_GGX_Iridescence( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness ); #else reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness ); #endif reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } void 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) { #ifdef USE_CLEARCOAT clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); #endif #ifdef USE_SHEEN sheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness ); #endif vec3 singleScattering = vec3( 0.0 ); vec3 multiScattering = vec3( 0.0 ); vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; #ifdef USE_IRIDESCENCE computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); #else computeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); #endif vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); reflectedLight.indirectSpecular += radiance * singleScattering; reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); }"; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); #ifdef USE_CLEARCOAT geometry.clearcoatNormal = clearcoatNormal; #endif #ifdef USE_IRIDESCENCE float dotNVi = saturate( dot( normal, geometry.viewDir ) ); if ( material.iridescenceThickness == 0.0 ) { material.iridescence = 0.0; } else { material.iridescence = saturate( material.iridescence ); } if ( material.iridescence > 0.0 ) { material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); } #endif IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointLightInfo( pointLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) pointLightShadow = pointLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotLightInfo( spotLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) spotLightShadow = spotLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLightShadow; #endif #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalLightInfo( directionalLight, geometry, directLight ); #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) directionalLightShadow = directionalLightShadows[ i ]; directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #pragma unroll_loop_end #endif #if defined( RE_IndirectDiffuse ) vec3 iblIrradiance = vec3( 0.0 ); vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop_start for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); } #pragma unroll_loop_end #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); #endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) iblIrradiance += getIBLIrradiance( geometry.normal ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness ); #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness ); #endif #endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight ); #endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; #endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; varying float vIsPerspective; #else uniform float logDepthBufFC; #endif #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); #else if ( isPerspectiveMatrix( projectionMatrix ) ) { gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; } #endif #endif"; var map_fragment = "#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); #endif diffuseColor *= sampledDiffuseColor; #endif"; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; #endif #ifdef USE_MAP diffuseColor *= texture2D( map, uv ); #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; #endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) uniform mat3 uvTransform; #endif #ifdef USE_MAP uniform sampler2D map; #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif"; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; #elif defined( USE_COLOR ) if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } #else objectNormal += morphNormal0 * morphTargetInfluences[ 0 ]; objectNormal += morphNormal1 * morphTargetInfluences[ 1 ]; objectNormal += morphNormal2 * morphTargetInfluences[ 2 ]; objectNormal += morphNormal3 * morphTargetInfluences[ 3 ]; #endif #endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS uniform float morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; uniform sampler2DArray morphTargetsTexture; uniform ivec2 morphTargetsTextureSize; vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; int y = texelIndex / morphTargetsTextureSize.x; int x = texelIndex - y * morphTargetsTextureSize.x; ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } #else #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; #ifdef MORPHTARGETS_TEXTURE for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } #else transformed += morphTarget0 * morphTargetInfluences[ 0 ]; transformed += morphTarget1 * morphTargetInfluences[ 1 ]; transformed += morphTarget2 * morphTargetInfluences[ 2 ]; transformed += morphTarget3 * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += morphTarget4 * morphTargetInfluences[ 4 ]; transformed += morphTarget5 * morphTargetInfluences[ 5 ]; transformed += morphTarget6 * morphTargetInfluences[ 6 ]; transformed += morphTarget7 * morphTargetInfluences[ 7 ]; #endif #endif #endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif #ifdef USE_TANGENT vec3 tangent = normalize( vTangent ); vec3 bitangent = normalize( vBitangent ); #ifdef DOUBLE_SIDED tangent = tangent * faceDirection; bitangent = bitangent * faceDirection; #endif #if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) mat3 vTBN = mat3( tangent, bitangent, normal ); #endif #endif #endif vec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; #endif #ifdef DOUBLE_SIDED normal = normal * faceDirection; #endif normal = normalize( normalMatrix * normal ); #elif defined( TANGENTSPACE_NORMALMAP ) vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; #ifdef USE_TANGENT normal = normalize( vTBN * mapN ); #else normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); #endif #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); #endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif #endif"; var normal_vertex = "#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); #endif #endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif #ifdef OBJECTSPACE_NORMALMAP uniform mat3 normalMatrix; #endif #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 N = surf_norm; vec3 q1perp = cross( q1, N ); vec3 q0perp = cross( N, q0 ); vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; float det = max( dot( T, T ), dot( B, B ) ); float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); } #endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT vec3 clearcoatNormal = geometryNormal; #endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; #ifdef USE_TANGENT clearcoatNormal = normalize( vTBN * clearcoatMapN ); #else clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection ); #endif #endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP uniform sampler2D clearcoatNormalMap; uniform vec2 clearcoatNormalScale; #endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; #endif"; var output_fragment = "#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= transmissionAlpha + 0.1; #endif gl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } vec4 pack2HalfToRGBA( vec2 v ) { vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); } vec2 unpackRGBATo2Half( vec4 v ) { return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); }"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif"; var dithering_pars_fragment = "#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif"; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { return unpackRGBATo2Half( texture2D( shadow, uv ) ); } float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ float occlusion = 1.0; vec2 distribution = texture2DDistribution( shadow, uv ); float hard_shadow = step( compare , distribution.x ); if (hard_shadow != 1.0 ) { float distance = compare - distribution.x ; float variance = max( 0.00000, distribution.y * distribution.y ); float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); } return occlusion; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; float dx2 = dx0 / 2.0; float dy2 = dy0 / 2.0; float dx3 = dx1 / 2.0; float dy3 = dy1 / 2.0; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 17.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx = texelSize.x; float dy = texelSize.y; vec2 uv = shadowCoord.xy; vec2 f = fract( uv * shadowMapSize + 0.5 ); uv -= f * texelSize; shadow = ( texture2DCompare( shadowMap, uv, shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), f.x ) + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), f.y ) + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), f.x ), mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), f.x ), f.y ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_VSM ) shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; struct DirectionalLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ]; struct SpotLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; #endif #if NUM_POINT_LIGHT_SHADOWS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; struct PointLightShadow { float shadowBias; float shadowNormalBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif #endif"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); vec4 shadowWorldPosition; #endif #if NUM_DIR_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 ); vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end #endif #endif"; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 DirectionalLightShadow directionalLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { directionalLight = directionalLightShadows[ i ]; shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_SPOT_LIGHT_SHADOWS > 0 SpotLightShadow spotLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { spotLight = spotLightShadows[ i ]; shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #pragma unroll_loop_end #endif #if NUM_POINT_LIGHT_SHADOWS > 0 PointLightShadow pointLight; #pragma unroll_loop_start for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { pointLight = pointLightShadows[ i ]; shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #pragma unroll_loop_end #endif #endif return shadow; }"; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #endif"; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif"; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif"; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif"; var tonemapping_pars_fragment = "#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } vec3 RRTAndODTFit( vec3 v ) { vec3 a = v * ( v + 0.0245786 ) - 0.000090537; vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; return a / b; } vec3 ACESFilmicToneMapping( vec3 color ) { const mat3 ACESInputMat = mat3( vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), vec3( 0.04823, 0.01566, 0.83777 ) ); const mat3 ACESOutputMat = mat3( vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), vec3( -0.07367, -0.00605, 1.07602 ) ); color *= toneMappingExposure / 0.6; color = ACESInputMat * color; color = RRTAndODTFit( color ); color = ACESOutputMat * color; return saturate( color ); } vec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION float transmissionAlpha = 1.0; float transmissionFactor = transmission; float thicknessFactor = thickness; #ifdef USE_TRANSMISSIONMAP transmissionFactor *= texture2D( transmissionMap, vUv ).r; #endif #ifdef USE_THICKNESSMAP thicknessFactor *= texture2D( thicknessMap, vUv ).g; #endif vec3 pos = vWorldPosition; vec3 v = normalize( cameraPosition - pos ); vec3 n = inverseTransformDirection( normal, viewMatrix ); vec4 transmission = getIBLVolumeRefraction( n, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90, pos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor, attenuationColor, attenuationDistance ); totalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor ); transmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor ); #endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; uniform vec3 attenuationColor; #ifdef USE_TRANSMISSIONMAP uniform sampler2D transmissionMap; #endif #ifdef USE_THICKNESSMAP uniform sampler2D thicknessMap; #endif uniform vec2 transmissionSamplerSize; uniform sampler2D transmissionSamplerMap; uniform mat4 modelMatrix; uniform mat4 projectionMatrix; varying vec3 vWorldPosition; vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); vec3 modelScale; modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); return normalize( refractionVector ) * thickness * modelScale; } float applyIorToRoughness( const in float roughness, const in float ior ) { return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); } vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { float framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); #ifdef texture2DLodEXT return texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #else return texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod ); #endif } vec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { if ( attenuationDistance == 0.0 ) { return radiance; } else { vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance * radiance; } } vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, const in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness, const in vec3 attenuationColor, const in float attenuationDistance ) { vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); vec3 refractedRayExit = position + transmissionRay; vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); vec2 refractionCoords = ndcPos.xy / ndcPos.w; refractionCoords += 1.0; refractionCoords /= 2.0; vec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); vec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance ); vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); return vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a ); } #endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#ifdef USE_UV #ifdef UVS_VERTEX_ONLY vec2 vUv; #else varying vec2 vUv; #endif uniform mat3 uvTransform; #endif"; var uv_vertex = "#ifdef USE_UV vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; uniform mat3 uv2Transform; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_INSTANCING worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; #endif"; const vertex$g = "varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; gl_Position = vec4( position.xy, 1.0, 1.0 ); }"; const fragment$g = "uniform sampler2D t2D; varying vec2 vUv; void main() { gl_FragColor = texture2D( t2D, vUv ); #ifdef DECODE_VIDEO_TEXTURE gl_FragColor = vec4( mix( pow( gl_FragColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), gl_FragColor.rgb * 0.0773993808, vec3( lessThanEqual( gl_FragColor.rgb, vec3( 0.04045 ) ) ) ), gl_FragColor.w ); #endif #include #include }"; const vertex$f = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; }"; const fragment$f = "#include uniform float opacity; varying vec3 vWorldDirection; #include void main() { vec3 vReflect = vWorldDirection; #include gl_FragColor = envColor; gl_FragColor.a *= opacity; #include #include }"; const vertex$e = "#include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vHighPrecisionZW = gl_Position.zw; }"; const fragment$e = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include #include varying vec2 vHighPrecisionZW; void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( fragCoordZ ); #endif }"; const vertex$d = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; }"; const fragment$d = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); }"; const vertex$c = "varying vec3 vWorldDirection; #include void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include #include }"; const fragment$c = "uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include }"; const vertex$b = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include #include void main() { vLineDistance = scale * lineDistance; #include #include #include #include #include #include #include #include }"; const fragment$b = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$a = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include }"; const fragment$a = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include #include #include #include #include #include #include }"; const vertex$9 = "#define LAMBERT varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$9 = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; varying vec3 vIndirectFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; varying vec3 vIndirectBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #ifdef DOUBLE_SIDED reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack; #else reflectedLight.indirectDiffuse += vIndirectFront; #endif #include reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$8 = "#define MATCAP varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; }"; const fragment$8 = "#define MATCAP uniform vec3 diffuse; uniform float opacity; uniform sampler2D matcap; varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include #include vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); #else vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); #endif vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; #include #include #include #include #include #include }"; const vertex$7 = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif }"; const fragment$7 = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include #include #include #include #include #include #include void main() { #include #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif }"; const vertex$6 = "#define PHONG varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include }"; const fragment$6 = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include #include #include #include #include #include #include }"; const vertex$5 = "#define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif }"; const fragment$5 = "#define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat; #endif #include #include #include #include #include #include }"; const vertex$4 = "#define TOON varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include }"; const fragment$4 = "#define TOON uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include #include #include #include #include #include }"; const vertex$3 = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include #include gl_PointSize = size; #ifdef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); #endif #include #include #include #include }"; const fragment$3 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include #include }"; const vertex$2 = "#include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include }"; const fragment$2 = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include #include #include }"; const vertex$1 = "uniform float rotation; uniform vec2 center; #include #include #include #include #include void main() { #include vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ); vec2 scale; scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) ); scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) ); #ifndef USE_SIZEATTENUATION bool isPerspective = isPerspectiveMatrix( projectionMatrix ); if ( isPerspective ) scale *= - mvPosition.z; #endif vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; mvPosition.xy += rotatedPosition; gl_Position = projectionMatrix * mvPosition; #include #include #include }"; const fragment$1 = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; #include #include #include #include }"; const ShaderChunk$1 = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common$2, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$g, background_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: /*@__PURE__*/ new Matrix3() }, uv2Transform: { value: /*@__PURE__*/ new Matrix3() }, alphaMap: { value: null }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null }, }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // physical refractionRatio: { value: 0.98 } // basic, lambert, phong }, 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: /*@__PURE__*/ new Vector2( 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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) } }, 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: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/ new Matrix3() } }, sprite: { diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/ new Matrix3() } } }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.meshbasic_vert, fragmentShader: ShaderChunk$1.meshbasic_frag }, lambert: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk$1.meshlambert_vert, fragmentShader: ShaderChunk$1.meshlambert_frag }, phong: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specular: { value: /*@__PURE__*/ new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk$1.meshphong_vert, fragmentShader: ShaderChunk$1.meshphong_frag }, standard: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk$1.meshphysical_vert, fragmentShader: ShaderChunk$1.meshphysical_frag }, toon: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk$1.meshtoon_vert, fragmentShader: ShaderChunk$1.meshtoon_frag }, matcap: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } } ] ), vertexShader: ShaderChunk$1.meshmatcap_vert, fragmentShader: ShaderChunk$1.meshmatcap_frag }, points: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.points_vert, fragmentShader: ShaderChunk$1.points_frag }, dashed: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk$1.linedashed_vert, fragmentShader: ShaderChunk$1.linedashed_frag }, depth: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk$1.depth_vert, fragmentShader: ShaderChunk$1.depth_frag }, normal: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk$1.meshnormal_vert, fragmentShader: ShaderChunk$1.meshnormal_frag }, sprite: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.sprite, UniformsLib.fog ] ), vertexShader: ShaderChunk$1.sprite_vert, fragmentShader: ShaderChunk$1.sprite_frag }, background: { uniforms: { uvTransform: { value: /*@__PURE__*/ new Matrix3() }, t2D: { value: null }, }, vertexShader: ShaderChunk$1.background_vert, fragmentShader: ShaderChunk$1.background_frag }, cube: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.envmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk$1.cube_vert, fragmentShader: ShaderChunk$1.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk$1.equirect_vert, fragmentShader: ShaderChunk$1.equirect_frag }, distanceRGBA: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: /*@__PURE__*/ new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk$1.distanceRGBA_vert, fragmentShader: ShaderChunk$1.distanceRGBA_frag }, shadow: { uniforms: /*@__PURE__*/ mergeUniforms( [ UniformsLib.lights, UniformsLib.fog, { color: { value: /*@__PURE__*/ new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk$1.shadow_vert, fragmentShader: ShaderChunk$1.shadow_frag } }; ShaderLib.physical = { uniforms: /*@__PURE__*/ mergeUniforms( [ ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }, clearcoatNormalMap: { value: null }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceIOR: { value: 1.3 }, iridescenceThicknessMinimum: { value: 100 }, iridescenceThicknessMaximum: { value: 400 }, iridescenceThicknessMap: { value: null }, sheen: { value: 0 }, sheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, sheenColorMap: { value: null }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, attenuationDistance: { value: 0 }, attenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) }, specularColorMap: { value: null }, } ] ), vertexShader: ShaderChunk$1.meshphysical_vert, fragmentShader: ShaderChunk$1.meshphysical_frag }; function WebGLBackground( renderer, cubemaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render( renderList, scene ) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { background = cubemaps.get( background ); } // Ignore background in AR // TODO: Reconsider this. const xr = renderer.xr; const session = xr.getSession && xr.getSession(); if ( session && session.environmentBlendMode === "additive" ) { background = null; } if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), new ShaderMaterial( { name: "BackgroundCubeMaterial", uniforms: cloneUniforms( ShaderLib.cube.uniforms ), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false } ) ); boxMesh.geometry.deleteAttribute( "normal" ); boxMesh.geometry.deleteAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; // enable code injection for non-built-in material Object.defineProperty( boxMesh.material, "envMap", { get: function () { return this.uniforms.envMap.value; } } ); objects.update( boxMesh ); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); } else if ( background && background.isTexture ) { if ( planeMesh === undefined ) { planeMesh = new Mesh( new PlaneGeometry( 2, 2 ), new ShaderMaterial( { name: "BackgroundMaterial", uniforms: cloneUniforms( ShaderLib.background.uniforms ), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false } ) ); planeMesh.geometry.deleteAttribute( "normal" ); // enable code injection for non-built-in material Object.defineProperty( planeMesh.material, "map", { get: function () { return this.uniforms.t2D.value; } } ); objects.update( planeMesh ); } planeMesh.material.uniforms.t2D.value = background; if ( background.matrixAutoUpdate === true ) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); if ( currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping ) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); } } function setClear( color, alpha ) { state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha = 1 ) { clearColor.set( color ); clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const maxVertexAttributes = gl.getParameter( 34921 ); const extension = capabilities.isWebGL2 ? null : extensions.get( "OES_vertex_array_object" ); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState( null ); let currentState = defaultState; let forceUpdate = false; function setup( object, material, program, geometry, index ) { let updateBuffers = false; if ( vaoAvailable ) { const state = getBindingState( geometry, program, material ); if ( currentState !== state ) { currentState = state; bindVertexArrayObject( currentState.object ); } updateBuffers = needsUpdate( object, geometry, program, index ); if ( updateBuffers ) saveCache( object, geometry, program, index ); } else { const wireframe = ( material.wireframe === true ); if ( currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe ) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if ( index !== null ) { attributes.update( index, 34963 ); } if ( updateBuffers || forceUpdate ) { forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); if ( index !== null ) { gl.bindBuffer( 34963, attributes.get( index ).buffer ); } } } function createVertexArrayObject() { if ( capabilities.isWebGL2 ) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); return extension.bindVertexArrayOES( vao ); } function deleteVertexArrayObject( vao ) { if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); return extension.deleteVertexArrayOES( vao ); } function getBindingState( geometry, program, material ) { const wireframe = ( material.wireframe === true ); let programMap = bindingStates[ geometry.id ]; if ( programMap === undefined ) { programMap = {}; bindingStates[ geometry.id ] = programMap; } let stateMap = programMap[ program.id ]; if ( stateMap === undefined ) { stateMap = {}; programMap[ program.id ] = stateMap; } let state = stateMap[ wireframe ]; if ( state === undefined ) { state = createBindingState( createVertexArrayObject() ); stateMap[ wireframe ] = state; } return state; } function createBindingState( vao ) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for ( let i = 0; i < maxVertexAttributes; i ++ ) { newAttributes[ i ] = 0; enabledAttributes[ i ] = 0; attributeDivisors[ i ] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate( object, geometry, program, index ) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { const cachedAttribute = cachedAttributes[ name ]; let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( cachedAttribute === undefined ) return true; if ( cachedAttribute.attribute !== geometryAttribute ) return true; if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; attributesNum ++; } } if ( currentState.attributesNum !== attributesNum ) return true; if ( currentState.index !== index ) return true; return false; } function saveCache( object, geometry, program, index ) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let attribute = attributes[ name ]; if ( attribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) attribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if ( attribute && attribute.data ) { data.data = attribute.data; } cache[ name ] = data; attributesNum ++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { const extension = capabilities.isWebGL2 ? gl : extensions.get( "ANGLE_instanced_arrays" ); extension[ capabilities.isWebGL2 ? "vertexAttribDivisor" : "vertexAttribDivisorANGLE" ]( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function vertexAttribPointer( index, size, type, normalized, stride, offset ) { if ( capabilities.isWebGL2 === true && ( type === 5124 || type === 5125 ) ) { gl.vertexAttribIPointer( index, size, type, stride, offset ); } else { gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); } } function setupVertexAttributes( object, material, program, geometry ) { if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for ( const name in programAttributes ) { const programAttribute = programAttributes[ name ]; if ( programAttribute.location >= 0 ) { let geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute === undefined ) { if ( name === "instanceMatrix" && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; if ( name === "instanceColor" && object.instanceColor ) geometryAttribute = object.instanceColor; } if ( geometryAttribute !== undefined ) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if ( data.isInstancedInterleavedBuffer ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( 34962, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement ); } } else { if ( geometryAttribute.isInstancedBufferAttribute ) { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); } if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for ( let i = 0; i < programAttribute.locationSize; i ++ ) { enableAttribute( programAttribute.location + i ); } } gl.bindBuffer( 34962, buffer ); for ( let i = 0; i < programAttribute.locationSize; i ++ ) { vertexAttribPointer( programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, ( size / programAttribute.locationSize ) * i * bytesPerElement ); } } } else if ( materialDefaultAttributeValues !== undefined ) { const value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: gl.vertexAttrib4fv( programAttribute.location, value ); break; default: gl.vertexAttrib1fv( programAttribute.location, value ); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometryId ]; } } function releaseStatesOfGeometry( geometry ) { if ( bindingStates[ geometry.id ] === undefined ) return; const programMap = bindingStates[ geometry.id ]; for ( const programId in programMap ) { const stateMap = programMap[ programId ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ programId ]; } delete bindingStates[ geometry.id ]; } function releaseStatesOfProgram( program ) { for ( const geometryId in bindingStates ) { const programMap = bindingStates[ geometryId ]; if ( programMap[ program.id ] === undefined ) continue; const stateMap = programMap[ program.id ]; for ( const wireframe in stateMap ) { deleteVertexArrayObject( stateMap[ wireframe ].object ); delete stateMap[ wireframe ]; } delete programMap[ program.id ]; } } function reset() { resetDefaultState(); forceUpdate = true; if ( currentState === defaultState ) return; currentState = defaultState; bindVertexArrayObject( currentState.object ); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawArraysInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawArraysInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, start, count, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities( gl, extensions, parameters ) { let maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { return "mediump"; } } return "lowp"; } const isWebGL2 = ( typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext ) || ( typeof WebGL2ComputeRenderingContext !== "undefined" && gl instanceof WebGL2ComputeRenderingContext ); let precision = parameters.precision !== undefined ? parameters.precision : "highp"; const maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has( "WEBGL_draw_buffers" ); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( 34930 ); const maxVertexTextures = gl.getParameter( 35660 ); const maxTextureSize = gl.getParameter( 3379 ); const maxCubemapSize = gl.getParameter( 34076 ); const maxAttributes = gl.getParameter( 34921 ); const maxVertexUniforms = gl.getParameter( 36347 ); const maxVaryings = gl.getParameter( 36348 ); const maxFragmentUniforms = gl.getParameter( 36349 ); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has( "OES_texture_float" ); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping( properties ) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping, camera ) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes( planes, camera, 0 ); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function ( material, camera, useCache ) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get( material ); if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, useCache ); for ( let i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps( renderer ) { let cubemaps = new WeakMap(); function mapTextureMapping( texture, mapping ) { if ( mapping === EquirectangularReflectionMapping ) { texture.mapping = CubeReflectionMapping; } else if ( mapping === EquirectangularRefractionMapping ) { texture.mapping = CubeRefractionMapping; } return texture; } function get( texture ) { if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { if ( cubemaps.has( texture ) ) { const cubemap = cubemaps.get( texture ).texture; return mapTextureMapping( cubemap, texture.mapping ); } else { const image = texture.image; if ( image && image.height > 0 ) { const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemap = cubemaps.get( texture ); if ( cubemap !== undefined ) { cubemaps.delete( texture ); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera$2 { constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { super(); this.isOrthographicCamera = true; this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy( source, recursive ) { super.copy( source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; } setViewOffset( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = ( this.right - this.left ) / ( 2 * this.zoom ); const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); const cx = ( this.right + this.left ) / 2; const cy = ( this.top + this.bottom ) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); } toJSON( meta ) { const data = super.toJSON( meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; // Golden Ratio const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [ /*@__PURE__*/ new Vector3( 1, 1, 1 ), /*@__PURE__*/ new Vector3( - 1, 1, 1 ), /*@__PURE__*/ new Vector3( 1, 1, - 1 ), /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered "mips" at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor( renderer ) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial( this._blurMaterial ); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { _oldTarget = this._renderer.getRenderTarget(); this._setSize( 256 ); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); if ( sigma > 0 ) { this._blur( cubeUVRenderTarget, 0, 0, sigma ); } this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular( equirectangular, renderTarget = null ) { return this._fromTexture( equirectangular, renderTarget ); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap( cubemap, renderTarget = null ) { return this._fromTexture( cubemap, renderTarget ); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileCubemapShader() { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial( this._cubemapMaterial ); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture"s network fetch for increased concurrency. */ compileEquirectangularShader() { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial( this._equirectMaterial ); } } /** * Disposes of the PMREMGenerator"s internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } // private interface _setSize( cubeSize ) { this._lodMax = Math.floor( Math.log2( cubeSize ) ); this._cubeSize = Math.pow( 2, this._lodMax ); } _dispose() { if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); for ( let i = 0; i < this._lodPlanes.length; i ++ ) { this._lodPlanes[ i ].dispose(); } } _cleanup( outputTarget ) { this._renderer.setRenderTarget( _oldTarget ); outputTarget.scissorTest = false; _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); } _fromTexture( texture, renderTarget ) { if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); } else { // Equirectangular this._setSize( texture.image.width / 4 ); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV( texture, cubeUVRenderTarget ); this._applyPMREM( cubeUVRenderTarget ); this._cleanup( cubeUVRenderTarget ); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max( this._cubeSize, 16 * 7 ); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, encoding: LinearEncoding, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget( width, height, params ); if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width ) { if ( this._pingPongRenderTarget !== null ) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget( width, height, params ); const { _lodMax } = this; ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); this._blurMaterial = _getBlurShader( _lodMax, width, height ); } return cubeUVRenderTarget; } _compileMaterial( material ) { const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); this._renderer.compile( tmpMesh, _flatCamera ); } _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera$1( fov, aspect, near, far ); const upSign = [ 1, - 1, 1, 1, 1, 1 ]; const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor( _clearColor ); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial( { name: "PMREM.Background", side: BackSide, depthWrite: false, depthTest: false, } ); const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); let useSolidColor = false; const background = scene.background; if ( background ) { if ( background.isColor ) { backgroundMaterial.color.copy( background ); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy( _clearColor ); useSolidColor = true; } for ( let i = 0; i < 6; i ++ ) { const col = i % 3; if ( col === 0 ) { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); } else if ( col === 1 ) { cubeCamera.up.set( 0, 0, upSign[ i ] ); cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); } else { cubeCamera.up.set( 0, upSign[ i ], 0 ); cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); } const size = this._cubeSize; _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); renderer.setRenderTarget( cubeUVRenderTarget ); if ( useSolidColor ) { renderer.render( backgroundBox, cubeCamera ); } renderer.render( scene, cubeCamera ); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV( texture, cubeUVRenderTarget ) { const renderer = this._renderer; const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); if ( isCubeTexture ) { if ( this._cubemapMaterial === null ) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; } else { if ( this._equirectMaterial === null ) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh( this._lodPlanes[ 0 ], material ); const uniforms = material.uniforms; uniforms[ "envMap" ].value = texture; const size = this._cubeSize; _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); renderer.setRenderTarget( cubeUVRenderTarget ); renderer.render( mesh, _flatCamera ); } _applyPMREM( cubeUVRenderTarget ) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for ( let i = 1; i < this._lodPlanes.length; i ++ ) { const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, "latitudinal", poleAxis ); this._halfBlur( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, "longitudinal", poleAxis ); } _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if ( direction !== "latitudinal" && direction !== "longitudinal" ) { console.error( "blur direction must be either latitudinal or longitudinal!" ); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[ lodIn ] - 1; const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; if ( samples > MAX_SAMPLES ) { console.warn( `sigmaRadians, ${ sigmaRadians}, is too large and will clip, as it requested ${ samples} samples when the maximum is set to ${MAX_SAMPLES}` ); } const weights = []; let sum = 0; for ( let i = 0; i < MAX_SAMPLES; ++ i ) { const x = i / sigmaPixels; const weight = Math.exp( - x * x / 2 ); weights.push( weight ); if ( i === 0 ) { sum += weight; } else if ( i < samples ) { sum += 2 * weight; } } for ( let i = 0; i < weights.length; i ++ ) { weights[ i ] = weights[ i ] / sum; } blurUniforms[ "envMap" ].value = targetIn.texture; blurUniforms[ "samples" ].value = samples; blurUniforms[ "weights" ].value = weights; blurUniforms[ "latitudinal" ].value = direction === "latitudinal"; if ( poleAxis ) { blurUniforms[ "poleAxis" ].value = poleAxis; } const { _lodMax } = this; blurUniforms[ "dTheta" ].value = radiansPerPixel; blurUniforms[ "mipInt" ].value = _lodMax - lodIn; const outputSize = this._sizeLods[ lodOut ]; const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); const y = 4 * ( this._cubeSize - outputSize ); _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); renderer.setRenderTarget( targetOut ); renderer.render( blurMesh, _flatCamera ); } } function _createPlanes( lodMax ) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for ( let i = 0; i < totalLods; i ++ ) { const sizeLod = Math.pow( 2, lod ); sizeLods.push( sizeLod ); let sigma = 1.0 / sizeLod; if ( i > lodMax - LOD_MIN ) { sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; } else if ( i === 0 ) { sigma = 0; } sigmas.push( sigma ); const texelSize = 1.0 / ( sizeLod - 2 ); const min = - texelSize; const max = 1 + texelSize; const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array( positionSize * vertices * cubeFaces ); const uv = new Float32Array( uvSize * vertices * cubeFaces ); const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); for ( let face = 0; face < cubeFaces; face ++ ) { const x = ( face % 3 ) * 2 / 3 - 1; const y = face > 2 ? 0 : - 1; const coordinates = [ x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0 ]; position.set( coordinates, positionSize * vertices * face ); uv.set( uv1, uvSize * vertices * face ); const fill = [ face, face, face, face, face, face ]; faceIndex.set( fill, faceIndexSize * vertices * face ); } const planes = new BufferGeometry(); planes.setAttribute( "position", new BufferAttribute( position, positionSize ) ); planes.setAttribute( "uv", new BufferAttribute( uv, uvSize ) ); planes.setAttribute( "faceIndex", new BufferAttribute( faceIndex, faceIndexSize ) ); lodPlanes.push( planes ); if ( lod > LOD_MIN ) { lod --; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget( width, height, params ) { const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = "PMREM.cubeUv"; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport( target, x, y, width, height ) { target.viewport.set( x, y, width, height ); target.scissor.set( x, y, width, height ); } function _getBlurShader( lodMax, width, height ) { const weights = new Float32Array( MAX_SAMPLES ); const poleAxis = new Vector3( 0, 1, 0 ); const shaderMaterial = new ShaderMaterial( { name: "SphericalGaussianBlur", defines: { "n": MAX_SAMPLES, "CUBEUV_TEXEL_WIDTH": 1.0 / width, "CUBEUV_TEXEL_HEIGHT": 1.0 / height, "CUBEUV_MAX_MIP": `${lodMax}.0`, }, uniforms: { "envMap": { value: null }, "samples": { value: 1 }, "weights": { value: weights }, "latitudinal": { value: false }, "dTheta": { value: 0 }, "mipInt": { value: 0 }, "poleAxis": { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false } ); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial( { name: "EquirectangularToCubeUV", uniforms: { "envMap": { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCubemapMaterial() { return new ShaderMaterial( { name: "CubemapToCubeUV", uniforms: { "envMap": { value: null }, "flipEnvMap": { value: - 1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false } ); } function _getCommonVertexShader() { return /* glsl */` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } `; } function WebGLCubeUVMaps( renderer ) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get( texture ) { if ( texture && texture.isTexture ) { const mapping = texture.mapping; const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); // equirect/cube map to cubeUV conversion if ( isEquirectMap || isCubeMap ) { if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get( texture ); if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); cubeUVmaps.set( texture, renderTarget ); return renderTarget.texture; } else { if ( cubeUVmaps.has( texture ) ) { return cubeUVmaps.get( texture ).texture; } else { const image = texture.image; if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); cubeUVmaps.set( texture, renderTarget ); texture.addEventListener( "dispose", onTextureDispose ); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete( image ) { let count = 0; const length = 6; for ( let i = 0; i < length; i ++ ) { if ( image[ i ] !== undefined ) count ++; } return count === length; } function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); const cubemapUV = cubeUVmaps.get( texture ); if ( cubemapUV !== undefined ) { cubeUVmaps.delete( texture ); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if ( pmremGenerator !== null ) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions( gl ) { const extensions = {}; function getExtension( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } let extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } extensions[ name ] = extension; return extension; } return { has: function ( name ) { return getExtension( name ) !== null; }, init: function ( capabilities ) { if ( capabilities.isWebGL2 ) { getExtension( "EXT_color_buffer_float" ); } else { getExtension( "WEBGL_depth_texture" ); getExtension( "OES_texture_float" ); getExtension( "OES_texture_half_float" ); getExtension( "OES_texture_half_float_linear" ); getExtension( "OES_standard_derivatives" ); getExtension( "OES_element_index_uint" ); getExtension( "OES_vertex_array_object" ); getExtension( "ANGLE_instanced_arrays" ); } getExtension( "OES_texture_float_linear" ); getExtension( "EXT_color_buffer_half_float" ); getExtension( "WEBGL_multisampled_render_to_texture" ); }, get: function ( name ) { const extension = getExtension( name ); if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } return extension; } }; } function WebGLGeometries( gl, attributes, info, bindingStates ) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose( event ) { const geometry = event.target; if ( geometry.index !== null ) { attributes.remove( geometry.index ); } for ( const name in geometry.attributes ) { attributes.remove( geometry.attributes[ name ] ); } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; const attribute = wireframeAttributes.get( geometry ); if ( attribute ) { attributes.remove( attribute ); wireframeAttributes.delete( geometry ); } bindingStates.releaseStatesOfGeometry( geometry ); if ( geometry.isInstancedBufferGeometry === true ) { delete geometry._maxInstanceCount; } // info.memory.geometries --; } function get( object, geometry ) { if ( geometries[ geometry.id ] === true ) return geometry; geometry.addEventListener( "dispose", onGeometryDispose ); geometries[ geometry.id ] = true; info.memory.geometries ++; return geometry; } function update( geometry ) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for ( const name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], 34962 ); } // morph targets const morphAttributes = geometry.morphAttributes; for ( const name in morphAttributes ) { const array = morphAttributes[ name ]; for ( let i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], 34962 ); } } } function updateWireframeAttribute( geometry ) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if ( geometryIndex !== null ) { const array = geometryIndex.array; version = geometryIndex.version; for ( let i = 0, l = array.length; i < l; i += 3 ) { const a = array[ i + 0 ]; const b = array[ i + 1 ]; const c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { const array = geometryPosition.array; version = geometryPosition.version; for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push( a, b, b, c, c, a ); } } const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get( geometry ); if ( previousAttribute ) attributes.remove( previousAttribute ); // wireframeAttributes.set( geometry, attribute ); } function getWireframeAttribute( geometry ) { const currentAttribute = wireframeAttributes.get( geometry ); if ( currentAttribute ) { const geometryIndex = geometry.index; if ( geometryIndex !== null ) { // if the attribute is obsolete, create a new one if ( currentAttribute.version < geometryIndex.version ) { updateWireframeAttribute( geometry ); } } } else { updateWireframeAttribute( geometry ); } return wireframeAttributes.get( geometry ); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode( value ) { mode = value; } let type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode, 1 ); } function renderInstances( start, count, primcount ) { if ( primcount === 0 ) return; let extension, methodName; if ( isWebGL2 ) { extension = gl; methodName = "drawElementsInstanced"; } else { extension = extensions.get( "ANGLE_instanced_arrays" ); methodName = "drawElementsInstancedANGLE"; if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); info.update( count, mode, primcount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo( gl ) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { render.calls ++; switch ( mode ) { case 4: render.triangles += instanceCount * ( count / 3 ); break; case 1: render.lines += instanceCount * ( count / 2 ); break; case 3: render.lines += instanceCount * ( count - 1 ); break; case 2: render.lines += instanceCount * count; break; case 0: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.frame ++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } function numericalSort( a, b ) { return a[ 0 ] - b[ 0 ]; } function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function denormalize( morph, attribute ) { let denominator = 1; const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; if ( array instanceof Int8Array ) denominator = 127; else if ( array instanceof Uint8Array ) denominator = 255; else if ( array instanceof Uint16Array ) denominator = 65535; else if ( array instanceof Int16Array ) denominator = 32767; else if ( array instanceof Int32Array ) denominator = 2147483647; else console.error( "THREE.WebGLMorphtargets: Unsupported morph attribute data type: ", array ); morph.divideScalar( denominator ); } function WebGLMorphtargets( gl, capabilities, textures ) { const influencesList = {}; const morphInfluences = new Float32Array( 8 ); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for ( let i = 0; i < 8; i ++ ) { workInfluences[ i ] = [ i, 0 ]; } function update( object, geometry, material, program ) { const objectInfluences = object.morphTargetInfluences; if ( capabilities.isWebGL2 === true ) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let entry = morphTextures.get( geometry ); if ( entry === undefined || entry.count !== morphTargetsCount ) { if ( entry !== undefined ) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if ( hasMorphPosition === true ) vertexDataCount = 1; if ( hasMorphNormals === true ) vertexDataCount = 2; if ( hasMorphColors === true ) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if ( width > capabilities.maxTextureSize ) { height = Math.ceil( width / capabilities.maxTextureSize ); width = capabilities.maxTextureSize; } const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for ( let i = 0; i < morphTargetsCount; i ++ ) { const morphTarget = morphTargets[ i ]; const morphNormal = morphNormals[ i ]; const morphColor = morphColors[ i ]; const offset = width * height * 4 * i; for ( let j = 0; j < morphTarget.count; j ++ ) { const stride = j * vertexDataStride; if ( hasMorphPosition === true ) { morph.fromBufferAttribute( morphTarget, j ); if ( morphTarget.normalized === true ) denormalize( morph, morphTarget ); buffer[ offset + stride + 0 ] = morph.x; buffer[ offset + stride + 1 ] = morph.y; buffer[ offset + stride + 2 ] = morph.z; buffer[ offset + stride + 3 ] = 0; } if ( hasMorphNormals === true ) { morph.fromBufferAttribute( morphNormal, j ); if ( morphNormal.normalized === true ) denormalize( morph, morphNormal ); buffer[ offset + stride + 4 ] = morph.x; buffer[ offset + stride + 5 ] = morph.y; buffer[ offset + stride + 6 ] = morph.z; buffer[ offset + stride + 7 ] = 0; } if ( hasMorphColors === true ) { morph.fromBufferAttribute( morphColor, j ); if ( morphColor.normalized === true ) denormalize( morph, morphColor ); buffer[ offset + stride + 8 ] = morph.x; buffer[ offset + stride + 9 ] = morph.y; buffer[ offset + stride + 10 ] = morph.z; buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2( width, height ) }; morphTextures.set( geometry, entry ); function disposeTexture() { texture.dispose(); morphTextures.delete( geometry ); geometry.removeEventListener( "dispose", disposeTexture ); } geometry.addEventListener( "dispose", disposeTexture ); } // let morphInfluencesSum = 0; for ( let i = 0; i < objectInfluences.length; i ++ ) { morphInfluencesSum += objectInfluences[ i ]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", objectInfluences ); program.getUniforms().setValue( gl, "morphTargetsTexture", entry.texture, textures ); program.getUniforms().setValue( gl, "morphTargetsTextureSize", entry.size ); } else { // When object doesn"t have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[ geometry.id ]; if ( influences === undefined || influences.length !== length ) { // initialise list influences = []; for ( let i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } // Collect influences for ( let i = 0; i < length; i ++ ) { const influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); for ( let i = 0; i < 8; i ++ ) { if ( i < length && influences[ i ][ 1 ] ) { workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; } else { workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; workInfluences[ i ][ 1 ] = 0; } } workInfluences.sort( numericalSort ); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for ( let i = 0; i < 8; i ++ ) { const influence = workInfluences[ i ]; const index = influence[ 0 ]; const value = influence[ 1 ]; if ( index !== Number.MAX_SAFE_INTEGER && value ) { if ( morphTargets && geometry.getAttribute( "morphTarget" + i ) !== morphTargets[ index ] ) { geometry.setAttribute( "morphTarget" + i, morphTargets[ index ] ); } if ( morphNormals && geometry.getAttribute( "morphNormal" + i ) !== morphNormals[ index ] ) { geometry.setAttribute( "morphNormal" + i, morphNormals[ index ] ); } morphInfluences[ i ] = value; morphInfluencesSum += value; } else { if ( morphTargets && geometry.hasAttribute( "morphTarget" + i ) === true ) { geometry.deleteAttribute( "morphTarget" + i ); } if ( morphNormals && geometry.hasAttribute( "morphNormal" + i ) === true ) { geometry.deleteAttribute( "morphNormal" + i ); } morphInfluences[ i ] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue( gl, "morphTargetBaseInfluence", morphBaseInfluence ); program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } } return { update: update }; } function WebGLObjects( gl, geometries, attributes, info ) { let updateMap = new WeakMap(); function update( object ) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateMap.get( buffergeometry ) !== frame ) { geometries.update( buffergeometry ); updateMap.set( buffergeometry, frame ); } if ( object.isInstancedMesh ) { if ( object.hasEventListener( "dispose", onInstancedMeshDispose ) === false ) { object.addEventListener( "dispose", onInstancedMeshDispose ); } attributes.update( object.instanceMatrix, 34962 ); if ( object.instanceColor !== null ) { attributes.update( object.instanceColor, 34962 ); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose( event ) { const instancedMesh = event.target; instancedMesh.removeEventListener( "dispose", onInstancedMeshDispose ); attributes.remove( instancedMesh.instanceMatrix ); if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); } return { update: update, dispose: dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the "textures" parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name "name" to "value" * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array( 16 ); const mat3array = new Float32Array( 9 ); const mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { const firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( let i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( let i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( textures, n ) { let r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( let i = 0; i !== n; ++ i ) { r[ i ] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValueV3f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValueV4f( gl, v ) { const cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValueM3( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValueM4( gl, v ) { const cache = this.cache; const elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single integer / boolean function setValueV1i( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single integer / boolean vector (from flat array) function setValueV2i( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } function setValueV3i( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } function setValueV4i( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } // Single unsigned integer function setValueV1ui( gl, v ) { const cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1ui( this.addr, v ); cache[ 0 ] = v; } // Single unsigned integer vector (from flat array) function setValueV2ui( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform2uiv( this.addr, v ); copyArray( cache, v ); } function setValueV3ui( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform3uiv( this.addr, v ); copyArray( cache, v ); } function setValueV4ui( gl, v ) { const cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform4uiv( this.addr, v ); copyArray( cache, v ); } // Single texture (2D / Cube) function setValueT1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2D( v || emptyTexture, unit ); } function setValueT3D1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture3D( v || empty3dTexture, unit ); } function setValueT6( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTextureCube( v || emptyCubeTexture, unit ); } function setValueT2DArray1( gl, v, textures ) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } textures.setTexture2DArray( v || emptyArrayTexture, unit ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray( gl, v ) { gl.uniform1fv( this.addr, v ); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray( gl, v ) { const data = flatten( v, this.size, 2 ); gl.uniform2fv( this.addr, data ); } function setValueV3fArray( gl, v ) { const data = flatten( v, this.size, 3 ); gl.uniform3fv( this.addr, data ); } function setValueV4fArray( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniform4fv( this.addr, data ); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array( gl, v ) { const data = flatten( v, this.size, 4 ); gl.uniformMatrix2fv( this.addr, false, data ); } function setValueM3Array( gl, v ) { const data = flatten( v, this.size, 9 ); gl.uniformMatrix3fv( this.addr, false, data ); } function setValueM4Array( gl, v ) { const data = flatten( v, this.size, 16 ); gl.uniformMatrix4fv( this.addr, false, data ); } // Array of integer / boolean function setValueV1iArray( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValueV3iArray( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValueV4iArray( gl, v ) { gl.uniform4iv( this.addr, v ); } // Array of unsigned integer function setValueV1uiArray( gl, v ) { gl.uniform1uiv( this.addr, v ); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray( gl, v ) { gl.uniform2uiv( this.addr, v ); } function setValueV3uiArray( gl, v ) { gl.uniform3uiv( this.addr, v ); } function setValueV4uiArray( gl, v ) { gl.uniform4uiv( this.addr, v ); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array( gl, v, textures ) { const n = v.length; const units = allocTexUnits( textures, n ); gl.uniform1iv( this.addr, units ); for ( let i = 0; i !== n; ++ i ) { textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT3DArray( gl, v, textures ) { const n = v.length; const units = allocTexUnits( textures, n ); gl.uniform1iv( this.addr, units ); for ( let i = 0; i !== n; ++ i ) { textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); } } function setValueT6Array( gl, v, textures ) { const n = v.length; const units = allocTexUnits( textures, n ); gl.uniform1iv( this.addr, units ); for ( let i = 0; i !== n; ++ i ) { textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } function setValueT2DArrayArray( gl, v, textures ) { const n = v.length; const units = allocTexUnits( textures, n ); gl.uniform1iv( this.addr, units ); for ( let i = 0; i !== n; ++ i ) { textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor( id ) { this.id = id; this.seq = []; this.map = {}; } setValue( gl, value, textures ) { const seq = this.seq; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; u.setValue( gl, value[ u.id ], textures ); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(w+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { const match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex; let id = match[ 1 ]; const idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist const map = container.map; let next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container class WebGLUniforms { constructor( gl, program ) { this.seq = []; this.map = {}; const n = gl.getProgramParameter( program, 35718 ); for ( let i = 0; i < n; ++ i ) { const info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } setValue( gl, name, value, textures ) { const u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, textures ); } setOptional( gl, object, name ) { const v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); } static upload( gl, seq, values, textures ) { for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, textures ); } } } static seqWithValue( seq, values ) { const r = []; for ( let i = 0, n = seq.length; i !== n; ++ i ) { const u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; } } function WebGLShader( gl, type, string ) { const shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); return shader; } let programIdCount = 0; function handleSource( string, errorLine ) { const lines = string.split( " " ); const lines2 = []; const from = Math.max( errorLine - 6, 0 ); const to = Math.min( errorLine + 6, lines.length ); for ( let i = from; i < to; i ++ ) { const line = i + 1; lines2.push( `${line === errorLine ? ">" : " "} ${line}: ${lines[ i ]}` ); } return lines2.join( " " ); } function getEncodingComponents( encoding ) { switch ( encoding ) { case LinearEncoding: return [ "Linear", "( value )" ]; case sRGBEncoding: return [ "sRGB", "( value )" ]; default: console.warn( "THREE.WebGLProgram: Unsupported encoding:", encoding ); return [ "Linear", "( value )" ]; } } function getShaderErrors( gl, shader, type ) { const status = gl.getShaderParameter( shader, 35713 ); const errors = gl.getShaderInfoLog( shader ).trim(); if ( status && errors === "" ) return ""; const errorMatches = /ERROR: 0:(d+)/.exec( errors ); if ( errorMatches ) { // --enable-privileged-webgl-extension // console.log( "**" + type + "**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt( errorMatches[ 1 ] ); return type.toUpperCase() + " " + errors + " " + handleSource( gl.getShaderSource( shader ), errorLine ); } else { return errors; } } function getTexelEncodingFunction( functionName, encoding ) { const components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { let toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; case ACESFilmicToneMapping: toneMappingName = "ACESFilmic"; break; case CustomToneMapping: toneMappingName = "Custom"; break; default: console.warn( "THREE.WebGLProgram: Unsupported toneMapping:", toneMapping ); toneMappingName = "Linear"; } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( parameters ) { const chunks = [ ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === "physical" ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? "#extension GL_EXT_frag_depth : enable" : "", ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? "#extension GL_EXT_draw_buffers : require" : "", ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { const chunks = []; for ( const name in defines ) { const value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { const attributes = {}; const n = gl.getProgramParameter( program, 35721 ); for ( let i = 0; i < n; i ++ ) { const info = gl.getActiveAttrib( program, i ); const name = info.name; let locationSize = 1; if ( info.type === 35674 ) locationSize = 2; if ( info.type === 35675 ) locationSize = 3; if ( info.type === 35676 ) locationSize = 4; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = { type: info.type, location: gl.getAttribLocation( program, name ), locationSize: locationSize }; } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } // Resolve Includes const includePattern$1 = /^[ ]*#include +<([wd./]+)>/gm; function resolveIncludes$1( string ) { return string.replace( includePattern$1, includeReplacer$1 ); } function includeReplacer$1( match, include ) { const string = ShaderChunk$1[ include ]; if ( string === undefined ) { throw new Error( "Can not resolve #include <" + include + ">" ); } return resolveIncludes$1( string ); } // Unroll Loops const deprecatedUnrollLoopPattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; const unrollLoopPattern = /#pragma unroll_loop_starts+fors*(s*ints+is*=s*(d+)s*;s*is* 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ generatePrecision( parameters ), "#define SHADER_NAME " + parameters.shaderName, customDefines, parameters.instancing ? "#define USE_INSTANCING" : "", parameters.instancingColor ? "#define USE_INSTANCING_COLOR" : "", parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp2 ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? "#define OBJECTSPACE_NORMALMAP" : "", ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", ( parameters.morphColors && parameters.isWebGL2 ) ? "#define USE_MORPHCOLORS" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE" : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_TEXTURE_STRIDE " + parameters.morphTextureStride : "", ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? "#define MORPHTARGETS_COUNT " + parameters.morphTargetsCount : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.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", " attribute mat4 instanceMatrix;", "#endif", "#ifdef USE_INSTANCING_COLOR", " attribute vec3 instanceColor;", "#endif", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_TANGENT", " attribute vec4 tangent;", "#endif", "#if defined( USE_COLOR_ALPHA )", " attribute vec4 color;", "#elif defined( USE_COLOR )", " attribute vec3 color;", "#endif", "#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, generatePrecision( parameters ), "#define SHADER_NAME " + parameters.shaderName, customDefines, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp2 ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.matcap ? "#define USE_MATCAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_WIDTH " + envMapCubeUVSize.texelWidth : "", envMapCubeUVSize ? "#define CUBEUV_TEXEL_HEIGHT " + envMapCubeUVSize.texelHeight : "", envMapCubeUVSize ? "#define CUBEUV_MAX_MIP " + envMapCubeUVSize.maxMip + ".0" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? "#define OBJECTSPACE_NORMALMAP" : "", ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? "#define TANGENTSPACE_NORMALMAP" : "", parameters.clearcoat ? "#define USE_CLEARCOAT" : "", parameters.clearcoatMap ? "#define USE_CLEARCOATMAP" : "", parameters.clearcoatRoughnessMap ? "#define USE_CLEARCOAT_ROUGHNESSMAP" : "", parameters.clearcoatNormalMap ? "#define USE_CLEARCOAT_NORMALMAP" : "", parameters.iridescence ? "#define USE_IRIDESCENCE" : "", parameters.iridescenceMap ? "#define USE_IRIDESCENCEMAP" : "", parameters.iridescenceThicknessMap ? "#define USE_IRIDESCENCE_THICKNESSMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.specularIntensityMap ? "#define USE_SPECULARINTENSITYMAP" : "", parameters.specularColorMap ? "#define USE_SPECULARCOLORMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.alphaTest ? "#define USE_ALPHATEST" : "", parameters.sheen ? "#define USE_SHEEN" : "", parameters.sheenColorMap ? "#define USE_SHEENCOLORMAP" : "", parameters.sheenRoughnessMap ? "#define USE_SHEENROUGHNESSMAP" : "", parameters.transmission ? "#define USE_TRANSMISSION" : "", parameters.transmissionMap ? "#define USE_TRANSMISSIONMAP" : "", parameters.thicknessMap ? "#define USE_THICKNESSMAP" : "", parameters.decodeVideoTexture ? "#define DECODE_VIDEO_TEXTURE" : "", parameters.vertexTangents ? "#define USE_TANGENT" : "", parameters.vertexColors || parameters.instancingColor ? "#define USE_COLOR" : "", parameters.vertexAlphas ? "#define USE_COLOR_ALPHA" : "", parameters.vertexUvs ? "#define USE_UV" : "", parameters.uvsVertexOnly ? "#define UVS_VERTEX_ONLY" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "uniform bool isOrthographic;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk$1[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", parameters.opaque ? "#define OPAQUE" : "", ShaderChunk$1[ "encodings_pars_fragment" ], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ), parameters.useDepthPacking ? "#define DEPTH_PACKING " + parameters.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = resolveIncludes$1( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = resolveIncludes$1( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = "#version 300 es "; prefixVertex = [ "precision mediump sampler2DArray;", "#define attribute in", "#define varying out", "#define texture2D texture" ].join( " " ) + " " + prefixVertex; prefixFragment = [ "#define varying in", ( parameters.glslVersion === GLSL3 ) ? "" : "layout(location = 0) out highp vec4 pc_fragColor;", ( parameters.glslVersion === GLSL3 ) ? "" : "#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( " " ) + " " + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( parameters.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); // check for link errors if ( renderer.debug.checkShaderErrors ) { const programLog = gl.getProgramInfoLog( program ).trim(); const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); let runnable = true; let haveDiagnostics = true; if ( gl.getProgramParameter( program, 35714 ) === false ) { runnable = false; const vertexErrors = getShaderErrors( gl, glVertexShader, "vertex" ); const fragmentErrors = getShaderErrors( gl, glFragmentShader, "fragment" ); console.error( "THREE.WebGLProgram: Shader Error " + gl.getError() + " - " + "VALIDATE_STATUS " + gl.getProgramParameter( program, 35715 ) + " " + "Program Info Log: " + programLog + " " + vertexErrors + " " + fragmentErrors ); } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: Program Info Log:", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program ); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram( this ); gl.deleteProgram( program ); this.program = undefined; }; // this.name = parameters.shaderName; this.id = programIdCount ++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update( material ) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage( vertexShader ); const fragmentShaderStage = this._getShaderStage( fragmentShader ); const materialShaders = this._getShaderCacheForMaterial( material ); if ( materialShaders.has( vertexShaderStage ) === false ) { materialShaders.add( vertexShaderStage ); vertexShaderStage.usedTimes ++; } if ( materialShaders.has( fragmentShaderStage ) === false ) { materialShaders.add( fragmentShaderStage ); fragmentShaderStage.usedTimes ++; } return this; } remove( material ) { const materialShaders = this.materialCache.get( material ); for ( const shaderStage of materialShaders ) { shaderStage.usedTimes --; if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); } this.materialCache.delete( material ); return this; } getVertexShaderID( material ) { return this._getShaderStage( material.vertexShader ).id; } getFragmentShaderID( material ) { return this._getShaderStage( material.fragmentShader ).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial( material ) { const cache = this.materialCache; if ( cache.has( material ) === false ) { cache.set( material, new Set() ); } return cache.get( material ); } _getShaderStage( code ) { const cache = this.shaderCache; if ( cache.has( code ) === false ) { const stage = new WebGLShaderStage( code ); cache.set( code, stage ); } return cache.get( code ); } } class WebGLShaderStage { constructor( code ) { this.id = _id ++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const isWebGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const vertexTextures = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "toon", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", MeshMatcapMaterial: "matcap", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow", SpriteMaterial: "sprite" }; function getParameters( material, lights, shadows, scene, object ) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; const shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; let morphTextureStride = 0; if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update( material ); customVertexShaderID = _customShaders.getVertexShaderID( material ); customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } const currentRenderTarget = renderer.getRenderTarget(); const useAlphaTest = material.alphaTest > 0; const useClearcoat = material.clearcoat > 0; const useIridescence = material.iridescence > 0; const parameters = { isWebGL2: isWebGL2, shaderID: shaderID, shaderName: material.type, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: object.isInstancedMesh === true, instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, outputEncoding: ( currentRenderTarget === null ) ? renderer.outputEncoding : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding ), map: !! material.map, matcap: !! material.matcap, envMap: !! envMap, envMapMode: envMap && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, decodeVideoTexture: !! material.map && ( material.map.isVideoTexture === true ) && ( material.map.encoding === sRGBEncoding ), clearcoat: useClearcoat, clearcoatMap: useClearcoat && !! material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, clearcoatNormalMap: useClearcoat && !! material.clearcoatNormalMap, iridescence: useIridescence, iridescenceMap: useIridescence && !! material.iridescenceMap, iridescenceThicknessMap: useIridescence && !! material.iridescenceThicknessMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, specularIntensityMap: !! material.specularIntensityMap, specularColorMap: !! material.specularColorMap, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: !! material.alphaMap, alphaTest: useAlphaTest, gradientMap: !! material.gradientMap, sheen: material.sheen > 0, sheenColorMap: !! material.sheenColorMap, sheenRoughnessMap: !! material.sheenRoughnessMap, transmission: material.transmission > 0, transmissionMap: !! material.transmissionMap, thicknessMap: !! material.thicknessMap, combine: material.combine, vertexTangents: ( !! material.normalMap && !! geometry.attributes.tangent ), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.iridescenceMap || !! material.iridescenceThicknessMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || !! material.sheenColorMap || !! material.sheenRoughnessMap, uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.iridescenceMap || !! material.iridescenceThicknessMap || material.transmission > 0 || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || material.sheen > 0 || !! material.sheenColorMap || !! material.sheenRoughnessMap ) && !! material.displacementMap, fog: !! fog, useFog: material.fog === true, fogExp2: ( fog && fog.isFogExp2 ), flatShading: !! material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: !! material.depthPacking, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionDerivatives: material.extensions && material.extensions.derivatives, extensionFragDepth: material.extensions && material.extensions.fragDepth, extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, rendererExtensionFragDepth: isWebGL2 || extensions.has( "EXT_frag_depth" ), rendererExtensionDrawBuffers: isWebGL2 || extensions.has( "WEBGL_draw_buffers" ), rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( "EXT_shader_texture_lod" ), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey( parameters ) { const array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( parameters.customVertexShaderID ); array.push( parameters.customFragmentShaderID ); } if ( parameters.defines !== undefined ) { for ( const name in parameters.defines ) { array.push( name ); array.push( parameters.defines[ name ] ); } } if ( parameters.isRawShaderMaterial === false ) { getProgramCacheKeyParameters( array, parameters ); getProgramCacheKeyBooleans( array, parameters ); array.push( renderer.outputEncoding ); } array.push( parameters.customProgramCacheKey ); return array.join(); } function getProgramCacheKeyParameters( array, parameters ) { array.push( parameters.precision ); array.push( parameters.outputEncoding ); array.push( parameters.envMapMode ); array.push( parameters.envMapCubeUVHeight ); array.push( parameters.combine ); array.push( parameters.vertexUvs ); array.push( parameters.fogExp2 ); array.push( parameters.sizeAttenuation ); array.push( parameters.morphTargetsCount ); array.push( parameters.morphAttributeCount ); array.push( parameters.numDirLights ); array.push( parameters.numPointLights ); array.push( parameters.numSpotLights ); array.push( parameters.numHemiLights ); array.push( parameters.numRectAreaLights ); array.push( parameters.numDirLightShadows ); array.push( parameters.numPointLightShadows ); array.push( parameters.numSpotLightShadows ); array.push( parameters.shadowMapType ); array.push( parameters.toneMapping ); array.push( parameters.numClippingPlanes ); array.push( parameters.numClipIntersection ); array.push( parameters.depthPacking ); } function getProgramCacheKeyBooleans( array, parameters ) { _programLayers.disableAll(); if ( parameters.isWebGL2 ) _programLayers.enable( 0 ); if ( parameters.supportsVertexTextures ) _programLayers.enable( 1 ); if ( parameters.instancing ) _programLayers.enable( 2 ); if ( parameters.instancingColor ) _programLayers.enable( 3 ); if ( parameters.map ) _programLayers.enable( 4 ); if ( parameters.matcap ) _programLayers.enable( 5 ); if ( parameters.envMap ) _programLayers.enable( 6 ); if ( parameters.lightMap ) _programLayers.enable( 7 ); if ( parameters.aoMap ) _programLayers.enable( 8 ); if ( parameters.emissiveMap ) _programLayers.enable( 9 ); if ( parameters.bumpMap ) _programLayers.enable( 10 ); if ( parameters.normalMap ) _programLayers.enable( 11 ); if ( parameters.objectSpaceNormalMap ) _programLayers.enable( 12 ); if ( parameters.tangentSpaceNormalMap ) _programLayers.enable( 13 ); if ( parameters.clearcoat ) _programLayers.enable( 14 ); if ( parameters.clearcoatMap ) _programLayers.enable( 15 ); if ( parameters.clearcoatRoughnessMap ) _programLayers.enable( 16 ); if ( parameters.clearcoatNormalMap ) _programLayers.enable( 17 ); if ( parameters.iridescence ) _programLayers.enable( 18 ); if ( parameters.iridescenceMap ) _programLayers.enable( 19 ); if ( parameters.iridescenceThicknessMap ) _programLayers.enable( 20 ); if ( parameters.displacementMap ) _programLayers.enable( 21 ); if ( parameters.specularMap ) _programLayers.enable( 22 ); if ( parameters.roughnessMap ) _programLayers.enable( 23 ); if ( parameters.metalnessMap ) _programLayers.enable( 24 ); if ( parameters.gradientMap ) _programLayers.enable( 25 ); if ( parameters.alphaMap ) _programLayers.enable( 26 ); if ( parameters.alphaTest ) _programLayers.enable( 27 ); if ( parameters.vertexColors ) _programLayers.enable( 28 ); if ( parameters.vertexAlphas ) _programLayers.enable( 29 ); if ( parameters.vertexUvs ) _programLayers.enable( 30 ); if ( parameters.vertexTangents ) _programLayers.enable( 31 ); if ( parameters.uvsVertexOnly ) _programLayers.enable( 32 ); if ( parameters.fog ) _programLayers.enable( 33 ); array.push( _programLayers.mask ); _programLayers.disableAll(); if ( parameters.useFog ) _programLayers.enable( 0 ); if ( parameters.flatShading ) _programLayers.enable( 1 ); if ( parameters.logarithmicDepthBuffer ) _programLayers.enable( 2 ); if ( parameters.skinning ) _programLayers.enable( 3 ); if ( parameters.morphTargets ) _programLayers.enable( 4 ); if ( parameters.morphNormals ) _programLayers.enable( 5 ); if ( parameters.morphColors ) _programLayers.enable( 6 ); if ( parameters.premultipliedAlpha ) _programLayers.enable( 7 ); if ( parameters.shadowMapEnabled ) _programLayers.enable( 8 ); if ( parameters.physicallyCorrectLights ) _programLayers.enable( 9 ); if ( parameters.doubleSided ) _programLayers.enable( 10 ); if ( parameters.flipSided ) _programLayers.enable( 11 ); if ( parameters.useDepthPacking ) _programLayers.enable( 12 ); if ( parameters.dithering ) _programLayers.enable( 13 ); if ( parameters.specularIntensityMap ) _programLayers.enable( 14 ); if ( parameters.specularColorMap ) _programLayers.enable( 15 ); if ( parameters.transmission ) _programLayers.enable( 16 ); if ( parameters.transmissionMap ) _programLayers.enable( 17 ); if ( parameters.thicknessMap ) _programLayers.enable( 18 ); if ( parameters.sheen ) _programLayers.enable( 19 ); if ( parameters.sheenColorMap ) _programLayers.enable( 20 ); if ( parameters.sheenRoughnessMap ) _programLayers.enable( 21 ); if ( parameters.decodeVideoTexture ) _programLayers.enable( 22 ); if ( parameters.opaque ) _programLayers.enable( 23 ); array.push( _programLayers.mask ); } function getUniforms( material ) { const shaderID = shaderIDs[ material.type ]; let uniforms; if ( shaderID ) { const shader = ShaderLib[ shaderID ]; uniforms = UniformsUtils.clone( shader.uniforms ); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram( parameters, cacheKey ) { let program; // Check if code has been already compiled for ( let p = 0, pl = programs.length; p < pl; p ++ ) { const preexistingProgram = programs[ p ]; if ( preexistingProgram.cacheKey === cacheKey ) { program = preexistingProgram; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); programs.push( program ); } return program; } function releaseProgram( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set const i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache( material ) { _customShaders.remove( material ); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get( object ) { let map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.groupOrder !== b.groupOrder ) { return a.groupOrder - b.groupOrder; } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { let renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex ++; return renderItem; } function push( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.push( renderItem ); } else if ( material.transparent === true ) { transparent.push( renderItem ); } else { opaque.push( renderItem ); } } function unshift( object, geometry, material, groupOrder, z, group ) { const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); if ( material.transmission > 0.0 ) { transmissive.unshift( renderItem ); } else if ( material.transparent === true ) { transparent.unshift( renderItem ); } else { opaque.unshift( renderItem ); } } function sort( customOpaqueSort, customTransparentSort ) { if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); } function finish() { // Clear references from inactive renderItems in the list for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { const renderItem = renderItems[ i ]; if ( renderItem.id === null ) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get( scene, renderCallDepth ) { let list; if ( lists.has( scene ) === false ) { list = new WebGLRenderList(); lists.set( scene, [ list ] ); } else { if ( renderCallDepth >= lists.get( scene ).length ) { list = new WebGLRenderList(); lists.get( scene ).push( list ); } else { list = lists.get( scene )[ renderCallDepth ]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } let uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[ light.id ] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingLightsFirst( lightA, lightB ) { return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); } function WebGLLights( extensions, capabilities ) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { 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: [] }; for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup( lights, physicallyCorrectLights ) { let r = 0, g = 0, b = 0; for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; lights.sort( shadowCastingLightsFirst ); // artist-friendly light intensity scaling factor const scaleFactor = ( physicallyCorrectLights !== true ) ? Math.PI : 1; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if ( light.isLightProbe ) { for ( let j = 0; j < 9; j ++ ) { state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); } } else if ( light.isDirectionalLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[ directionalLength ] = shadowUniforms; state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; numDirectionalShadows ++; } state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); uniforms.distance = distance; uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = light.decay; if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[ spotLength ] = shadowUniforms; state.spotShadowMap[ spotLength ] = shadowMap; state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; numSpotShadows ++; } state.spot[ spotLength ] = uniforms; spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = cache.get( light ); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); uniforms.distance = light.distance; uniforms.decay = light.decay; if ( light.castShadow ) { const shadow = light.shadow; const shadowUniforms = shadowCache.get( light ); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[ pointLength ] = shadowUniforms; state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; numPointShadows ++; } state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = cache.get( light ); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } if ( rectAreaLength > 0 ) { if ( capabilities.isWebGL2 ) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if ( extensions.has( "OES_texture_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if ( extensions.has( "OES_texture_half_float_linear" ) === true ) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error( "THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions." ); } } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; const hash = state.hash; if ( hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows ) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotShadowMatrix.length = numSpotShadows; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; state.version = nextVersion ++; } } function setupView( lights, camera ) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; if ( light.isDirectionalLight ) { const uniforms = state.directional[ directionalLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); directionalLength ++; } else if ( light.isSpotLight ) { const uniforms = state.spot[ spotLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); spotLength ++; } else if ( light.isRectAreaLight ) { const uniforms = state.rectArea[ rectAreaLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); rectAreaLength ++; } else if ( light.isPointLight ) { const uniforms = state.point[ pointLength ]; uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); pointLength ++; } else if ( light.isHemisphereLight ) { const uniforms = state.hemi[ hemiLength ]; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); hemiLength ++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState( extensions, capabilities ) { const lights = new WebGLLights( extensions, capabilities ); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function setupLights( physicallyCorrectLights ) { lights.setup( lightsArray, physicallyCorrectLights ); } function setupLightsView( camera ) { lights.setupView( lightsArray, camera ); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates( extensions, capabilities ) { let renderStates = new WeakMap(); function get( scene, renderCallDepth = 0 ) { let renderState; if ( renderStates.has( scene ) === false ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStates.set( scene, [ renderState ] ); } else { if ( renderCallDepth >= renderStates.get( scene ).length ) { renderState = new WebGLRenderState( extensions, capabilities ); renderStates.get( scene ).push( renderState ); } else { renderState = renderStates.get( scene )[ renderCallDepth ]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } class MeshDepthMaterial extends Material { constructor( parameters ) { super(); this.isMeshDepthMaterial = true; this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor( parameters ) { super(); this.isMeshDistanceMaterial = true; this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.referencePosition.copy( source.referencePosition ); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex$h = "void main() { gl_Position = vec4( position, 1.0 ); }"; const fragment$h = "uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; #include void main() { const float samples = float( VSM_SAMPLES ); float mean = 0.0; float squared_mean = 0.0; float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); float uvStart = samples <= 1.0 ? 0.0 : - 1.0; for ( float i = 0.0; i < samples; i ++ ) { float uvOffset = uvStart + i * uvStride; #ifdef HORIZONTAL_PASS vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); mean += distribution.x; squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; #else float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); mean += depth; squared_mean += depth * depth; #endif } mean = mean / samples; squared_mean = squared_mean / samples; float std_dev = sqrt( squared_mean - mean * mean ); gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); }"; function WebGLShadowMap( _renderer, _objects, _capabilities ) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial( { defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex$h, fragmentShader: fragment$h } ); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute( "position", new BufferAttribute( new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), 3 ) ); const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending( NoBlending ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // render depth map for ( let i = 0, il = lights.length; i < il; i ++ ) { const light = lights[ i ]; const shadow = light.shadow; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; _shadowMapSize.copy( shadow.mapSize ); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply( shadowFrameExtents ); _viewportSize.copy( shadow.mapSize ); if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { if ( _shadowMapSize.x > _maxTextureSize ) { _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if ( _shadowMapSize.y > _maxTextureSize ) { _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if ( shadow.map === null ) { const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget( shadow.map ); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for ( let vp = 0; vp < viewportCount; vp ++ ) { const viewport = shadow.getViewport( vp ); _viewport.set( _viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w ); _state.viewport( _viewport ); shadow.updateMatrices( light, vp ); _frustum = shadow.getFrustum(); renderObject( scene, camera, shadow.camera, light, this.type ); } // do blur pass for VSM if ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) { VSMPass( shadow, camera ); } shadow.needsUpdate = false; } scope.needsUpdate = false; _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); }; function VSMPass( shadow, camera ) { const geometry = _objects.update( fullScreenMesh ); if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if ( shadow.mapPass === null ) { shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.mapPass ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget( shadow.map ); _renderer.clear(); _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); } function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar, type ) { let result = null; const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; if ( customMaterial !== undefined ) { result = customMaterial; } else { result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; } if ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) || ( material.displacementMap && material.displacementScale !== 0 ) || ( material.alphaMap && material.alphaTest > 0 ) ) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } let cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; if ( type === VSMShadowMap ) { result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; } else { result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { result.referencePosition.setFromMatrixPosition( light.matrixWorld ); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject( object, camera, shadowCamera, light, type ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); const geometry = _objects.update( object ); const material = object.material; if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let k = 0, kl = groups.length; k < kl; k ++ ) { const group = groups[ k ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { const depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { const depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far, type ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, light, type ); } } } function WebGLState( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( 2929 ); } else { disable( 2929 ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { if ( depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( 512 ); break; case AlwaysDepth: gl.depthFunc( 519 ); break; case LessDepth: gl.depthFunc( 513 ); break; case LessEqualDepth: gl.depthFunc( 515 ); break; case EqualDepth: gl.depthFunc( 514 ); break; case GreaterEqualDepth: gl.depthFunc( 518 ); break; case GreaterDepth: gl.depthFunc( 516 ); break; case NotEqualDepth: gl.depthFunc( 517 ); break; default: gl.depthFunc( 515 ); } } else { gl.depthFunc( 515 ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( ! locked ) { if ( stencilTest ) { enable( 2960 ); } else { disable( 2960 ); } } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter( 35661 ); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter( 7938 ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES (d)/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter( 3088 ); const viewportParam = gl.getParameter( 2978 ); const currentScissor = new Vector4().fromArray( scissorParam ); const currentViewport = new Vector4().fromArray( viewportParam ); function createTexture( type, target, count ) { const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, 10241, 9728 ); gl.texParameteri( type, 10240, 9728 ); for ( let i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); } return texture; } const emptyTextures = {}; emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( 2929 ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( 2884 ); setBlending( NoBlending ); // function enable( id ) { if ( enabledCapabilities[ id ] !== true ) { gl.enable( id ); enabledCapabilities[ id ] = true; } } function disable( id ) { if ( enabledCapabilities[ id ] !== false ) { gl.disable( id ); enabledCapabilities[ id ] = false; } } function bindFramebuffer( target, framebuffer ) { if ( currentBoundFramebuffers[ target ] !== framebuffer ) { gl.bindFramebuffer( target, framebuffer ); currentBoundFramebuffers[ target ] = framebuffer; if ( isWebGL2 ) { // 36009 is equivalent to 36160 if ( target === 36009 ) { currentBoundFramebuffers[ 36160 ] = framebuffer; } if ( target === 36160 ) { currentBoundFramebuffers[ 36009 ] = framebuffer; } } return true; } return false; } function drawBuffers( renderTarget, framebuffer ) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if ( renderTarget ) { drawBuffers = currentDrawbuffers.get( framebuffer ); if ( drawBuffers === undefined ) { drawBuffers = []; currentDrawbuffers.set( framebuffer, drawBuffers ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { const textures = renderTarget.texture; if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== 36064 ) { for ( let i = 0, il = textures.length; i < il; i ++ ) { drawBuffers[ i ] = 36064 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if ( drawBuffers[ 0 ] !== 36064 ) { drawBuffers[ 0 ] = 36064; needsUpdate = true; } } } else { if ( drawBuffers[ 0 ] !== 1029 ) { drawBuffers[ 0 ] = 1029; needsUpdate = true; } } if ( needsUpdate ) { if ( capabilities.isWebGL2 ) { gl.drawBuffers( drawBuffers ); } else { extensions.get( "WEBGL_draw_buffers" ).drawBuffersWEBGL( drawBuffers ); } } } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } const equationToGL = { [ AddEquation ]: 32774, [ SubtractEquation ]: 32778, [ ReverseSubtractEquation ]: 32779 }; if ( isWebGL2 ) { equationToGL[ MinEquation ] = 32775; equationToGL[ MaxEquation ] = 32776; } else { const extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { equationToGL[ MinEquation ] = extension.MIN_EXT; equationToGL[ MaxEquation ] = extension.MAX_EXT; } } const factorToGL = { [ ZeroFactor ]: 0, [ OneFactor ]: 1, [ SrcColorFactor ]: 768, [ SrcAlphaFactor ]: 770, [ SrcAlphaSaturateFactor ]: 776, [ DstColorFactor ]: 774, [ DstAlphaFactor ]: 772, [ OneMinusSrcColorFactor ]: 769, [ OneMinusSrcAlphaFactor ]: 771, [ OneMinusDstColorFactor ]: 775, [ OneMinusDstAlphaFactor ]: 773 }; function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending === NoBlending ) { if ( currentBlendingEnabled === true ) { disable( 3042 ); currentBlendingEnabled = false; } return; } if ( currentBlendingEnabled === false ) { enable( 3042 ); currentBlendingEnabled = true; } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { gl.blendEquation( 32774 ); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if ( premultipliedAlpha ) { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( 1, 771, 1, 771 ); break; case AdditiveBlending: gl.blendFunc( 1, 1 ); break; case SubtractiveBlending: gl.blendFuncSeparate( 0, 769, 0, 1 ); break; case MultiplyBlending: gl.blendFuncSeparate( 0, 768, 0, 770 ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } else { switch ( blending ) { case NormalBlending: gl.blendFuncSeparate( 770, 771, 1, 771 ); break; case AdditiveBlending: gl.blendFunc( 770, 1 ); break; case SubtractiveBlending: gl.blendFuncSeparate( 0, 769, 0, 1 ); break; case MultiplyBlending: gl.blendFunc( 0, 768 ); break; default: console.error( "THREE.WebGLState: Invalid blending: ", blending ); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = null; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( 2884 ) : enable( 2884 ); let flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); ( material.blending === NormalBlending && material.transparent === false ) ? setBlending( NoBlending ) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); const stencilWrite = material.stencilWrite; stencilBuffer.setTest( stencilWrite ); if ( stencilWrite ) { stencilBuffer.setMask( material.stencilWriteMask ); stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); material.alphaToCoverage === true ? enable( 32926 ) : disable( 32926 ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( 2304 ); } else { gl.frontFace( 2305 ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( 2884 ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( 1029 ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( 1028 ); } else { gl.cullFace( 1032 ); } } } else { disable( 2884 ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( 32823 ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( 32823 ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( 3089 ); } else { disable( 3089 ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { if ( currentTextureSlot === null ) { activeTexture(); } let boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ currentTextureSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture !== undefined && boundTexture.type !== undefined ) { gl.bindTexture( boundTexture.type, null ); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage2D() { try { gl.texSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texSubImage3D() { try { gl.texSubImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage2D() { try { gl.texStorage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texStorage3D() { try { gl.texStorage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage3D() { try { gl.texImage3D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } // function reset() { // reset state gl.disable( 3042 ); gl.disable( 2884 ); gl.disable( 2929 ); gl.disable( 32823 ); gl.disable( 3089 ); gl.disable( 2960 ); gl.disable( 32926 ); gl.blendEquation( 32774 ); gl.blendFunc( 1, 0 ); gl.blendFuncSeparate( 1, 0, 1, 0 ); gl.colorMask( true, true, true, true ); gl.clearColor( 0, 0, 0, 0 ); gl.depthMask( true ); gl.depthFunc( 513 ); gl.clearDepth( 1 ); gl.stencilMask( 0xffffffff ); gl.stencilFunc( 519, 0, 0xffffffff ); gl.stencilOp( 7680, 7680, 7680 ); gl.clearStencil( 0 ); gl.cullFace( 1029 ); gl.frontFace( 2305 ); gl.polygonOffset( 0, 0 ); gl.activeTexture( 33984 ); gl.bindFramebuffer( 36160, null ); if ( isWebGL2 === true ) { gl.bindFramebuffer( 36009, null ); gl.bindFramebuffer( 36008, null ); } gl.useProgram( null ); gl.lineWidth( 1 ); gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, texImage3D: texImage3D, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has( "WEBGL_multisampled_render_to_texture" ) ? extensions.get( "WEBGL_multisampled_render_to_texture" ) : null; const supportsInvalidateFramebuffer = /OculusBrowser/g.test( navigator.userAgent ); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== "undefined" // eslint-disable-next-line compat/compat && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; } catch ( err ) { // Ignore any errors } function createCanvas( width, height ) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas( width, height ) : createElementNS( "canvas" ); } function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { let scale = 1; // handle case if texture exceeds max size if ( image.width > maxSize || image.height > maxSize ) { scale = maxSize / Math.max( image.width, image.height ); } // only perform resize if necessary if ( scale < 1 || needsPowerOfTwo === true ) { // only perform resize for certain image types if ( ( typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement ) || ( typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap ) ) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor( scale * image.width ); const height = floor( scale * image.height ); if ( _canvas === undefined ) _canvas = createCanvas( width, height ); // cube textures can"t reuse the same canvas const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, width, height ); console.warn( "THREE.WebGLRenderer: Texture has been resized from (" + image.width + "x" + image.height + ") to (" + width + "x" + height + ")." ); return canvas; } else { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: Image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); } return image; } } return image; } function isPowerOfTwo$1( image ) { return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); } function textureNeedsPowerOfTwo( texture ) { if ( isWebGL2 ) return false; return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, supportsMips ) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target ) { _gl.generateMipmap( target ); } function getInternalFormat( internalFormatName, glFormat, glType, encoding, isVideoTexture = false ) { if ( isWebGL2 === false ) return glFormat; if ( internalFormatName !== null ) { if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; console.warn( "THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format "" + internalFormatName + """ ); } let internalFormat = glFormat; if ( glFormat === 6403 ) { if ( glType === 5126 ) internalFormat = 33326; if ( glType === 5131 ) internalFormat = 33325; if ( glType === 5121 ) internalFormat = 33321; } if ( glFormat === 33319 ) { if ( glType === 5126 ) internalFormat = 33328; if ( glType === 5131 ) internalFormat = 33327; if ( glType === 5121 ) internalFormat = 33323; } if ( glFormat === 6408 ) { if ( glType === 5126 ) internalFormat = 34836; if ( glType === 5131 ) internalFormat = 34842; if ( glType === 5121 ) internalFormat = ( encoding === sRGBEncoding && isVideoTexture === false ) ? 35907 : 32856; if ( glType === 32819 ) internalFormat = 32854; if ( glType === 32820 ) internalFormat = 32855; } if ( internalFormat === 33325 || internalFormat === 33326 || internalFormat === 33327 || internalFormat === 33328 || internalFormat === 34842 || internalFormat === 34836 ) { extensions.get( "EXT_color_buffer_float" ); } return internalFormat; } function getMipLevels( texture, image, supportsMips ) { if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { return Math.log2( Math.max( image.width, image.height ) ) + 1; } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { // user-defined mipmaps return texture.mipmaps.length; } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { return 9728; } return 9729; } // function onTextureDispose( event ) { const texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { _videoTextures.delete( texture ); } } function onRenderTargetDispose( event ) { const renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); } // function deallocateTexture( texture ) { const textureProperties = properties.get( texture ); if ( textureProperties.__webglInit === undefined ) return; // check if it"s necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get( source ); if ( webglTextures ) { const webglTexture = webglTextures[ textureProperties.__cacheKey ]; webglTexture.usedTimes --; // the WebGLTexture object is not used anymore, remove it if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } // remove the weak map entry if no WebGLTexture uses the source anymore if ( Object.keys( webglTextures ).length === 0 ) { _sources.delete( source ); } } properties.remove( texture ); } function deleteTexture( texture ) { const textureProperties = properties.get( texture ); _gl.deleteTexture( textureProperties.__webglTexture ); const source = texture.source; const webglTextures = _sources.get( source ); delete webglTextures[ textureProperties.__cacheKey ]; info.memory.textures --; } function deallocateRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); info.memory.textures --; } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLCubeRenderTarget ) { for ( let i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); if ( renderTargetProperties.__webglColorRenderbuffer ) { for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) { if ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] ); } } if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } if ( renderTarget.isWebGLMultipleRenderTargets ) { for ( let i = 0, il = texture.length; i < il; i ++ ) { const attachmentProperties = properties.get( texture[ i ] ); if ( attachmentProperties.__webglTexture ) { _gl.deleteTexture( attachmentProperties.__webglTexture ); info.memory.textures --; } properties.remove( texture[ i ] ); } } properties.remove( texture ); properties.remove( renderTarget ); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if ( textureUnit >= maxTextures ) { console.warn( "THREE.WebGLTextures: Trying to use " + textureUnit + " texture units while this GPU supports only " + maxTextures ); } textureUnits += 1; return textureUnit; } function getTextureCacheKey( texture ) { const array = []; array.push( texture.wrapS ); array.push( texture.wrapT ); array.push( texture.magFilter ); array.push( texture.minFilter ); array.push( texture.anisotropy ); array.push( texture.internalFormat ); array.push( texture.format ); array.push( texture.type ); array.push( texture.generateMipmaps ); array.push( texture.premultiplyAlpha ); array.push( texture.flipY ); array.push( texture.unpackAlignment ); array.push( texture.encoding ); return array.join(); } // function setTexture2D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { const image = texture.image; if ( image === null ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but no image data found." ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete" ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.activeTexture( 33984 + slot ); state.bindTexture( 3553, textureProperties.__webglTexture ); } function setTexture2DArray( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.activeTexture( 33984 + slot ); state.bindTexture( 35866, textureProperties.__webglTexture ); } function setTexture3D( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadTexture( textureProperties, texture, slot ); return; } state.activeTexture( 33984 + slot ); state.bindTexture( 32879, textureProperties.__webglTexture ); } function setTextureCube( texture, slot ) { const textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture( textureProperties, texture, slot ); return; } state.activeTexture( 33984 + slot ); state.bindTexture( 34067, textureProperties.__webglTexture ); } const wrappingToGL = { [ RepeatWrapping ]: 10497, [ ClampToEdgeWrapping ]: 33071, [ MirroredRepeatWrapping ]: 33648 }; const filterToGL = { [ NearestFilter ]: 9728, [ NearestMipmapNearestFilter ]: 9984, [ NearestMipmapLinearFilter ]: 9986, [ LinearFilter ]: 9729, [ LinearMipmapNearestFilter ]: 9985, [ LinearMipmapLinearFilter ]: 9987 }; function setTextureParameters( textureType, texture, supportsMips ) { if ( supportsMips ) { _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); if ( textureType === 32879 || textureType === 35866 ) { _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); } _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); } else { _gl.texParameteri( textureType, 10242, 33071 ); _gl.texParameteri( textureType, 10243, 33071 ); if ( textureType === 32879 || textureType === 35866 ) { _gl.texParameteri( textureType, 32882, 33071 ); } if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping." ); } _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter." ); } } if ( extensions.has( "EXT_texture_filter_anisotropic" ) === true ) { const extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( texture.type === FloatType && extensions.has( "OES_texture_float_linear" ) === false ) return; // verify extension for WebGL 1 and WebGL 2 if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( "OES_texture_half_float_linear" ) === false ) ) return; // verify extension for WebGL 1 only if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function initTexture( textureProperties, texture ) { let forceUpload = false; if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get( source ); if ( webglTextures === undefined ) { webglTextures = {}; _sources.set( source, webglTextures ); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey( texture ); if ( textureCacheKey !== textureProperties.__cacheKey ) { // if not, create a new instance of WebGLTexture if ( webglTextures[ textureCacheKey ] === undefined ) { // create new entry webglTextures[ textureCacheKey ] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures ++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[ textureCacheKey ].usedTimes ++; // every time the texture cache key changes, it"s necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[ textureProperties.__cacheKey ]; if ( webglTexture !== undefined ) { webglTextures[ textureProperties.__cacheKey ].usedTimes --; if ( webglTexture.usedTimes === 0 ) { deleteTexture( texture ); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; } return forceUpload; } function uploadTexture( textureProperties, texture, slot ) { let textureType = 3553; if ( texture.isDataArrayTexture ) textureType = 35866; if ( texture.isData3DTexture ) textureType = 32879; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.activeTexture( 33984 + slot ); state.bindTexture( textureType, textureProperties.__webglTexture ); if ( source.version !== source.__currentVersion || forceUpload === true ) { _gl.pixelStorei( 37440, texture.flipY ); _gl.pixelStorei( 37441, texture.premultiplyAlpha ); _gl.pixelStorei( 3317, texture.unpackAlignment ); _gl.pixelStorei( 37443, 0 ); const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); image = verifyColorSpace( texture, image ); const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.encoding ); let glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture ); setTextureParameters( textureType, texture, supportsMips ); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( source.__currentVersion === undefined ) || ( forceUpload === true ); const levels = getMipLevels( texture, image, supportsMips ); if ( texture.isDepthTexture ) { // populate depth texture with dummy data glInternalFormat = 6402; if ( isWebGL2 ) { if ( texture.type === FloatType ) { glInternalFormat = 36012; } else if ( texture.type === UnsignedIntType ) { glInternalFormat = 33190; } else if ( texture.type === UnsignedInt248Type ) { glInternalFormat = 35056; } else { glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D } } else { if ( texture.type === FloatType ) { console.error( "WebGLRenderer: Floating point depth texture requires WebGL2." ); } } // validation checks for WebGL 1 if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedIntType; glType = utils.convert( texture.type ); } } if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = 34041; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } // if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( 3553, 1, glInternalFormat, image.width, image.height ); } else { state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } } } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( 3553, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); } else { state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } } else if ( texture.isCompressedTexture ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } else if ( texture.isDataArrayTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( 35866, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( 35866, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isData3DTexture ) { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage3D( 32879, levels, glInternalFormat, image.width, image.height, image.depth ); } state.texSubImage3D( 32879, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); } else { state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); } } else if ( texture.isFramebufferTexture ) { if ( allocateMemory ) { if ( useTexStorage ) { state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); } else { let width = image.width, height = image.height; for ( let i = 0; i < levels; i ++ ) { state.texImage2D( 3553, i, glInternalFormat, width, height, 0, glFormat, glType, null ); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && supportsMips ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( useTexStorage ) { state.texSubImage2D( 3553, i, 0, 0, glFormat, glType, mipmap ); } else { state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); } } texture.generateMipmaps = false; } else { if ( useTexStorage ) { if ( allocateMemory ) { state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); } state.texSubImage2D( 3553, 0, 0, 0, glFormat, glType, image ); } else { state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( textureType ); } source.__currentVersion = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } function uploadCubeTexture( textureProperties, texture, slot ) { if ( texture.image.length !== 6 ) return; const forceUpload = initTexture( textureProperties, texture ); const source = texture.source; state.activeTexture( 33984 + slot ); state.bindTexture( 34067, textureProperties.__webglTexture ); if ( source.version !== source.__currentVersion || forceUpload === true ) { _gl.pixelStorei( 37440, texture.flipY ); _gl.pixelStorei( 37441, texture.premultiplyAlpha ); _gl.pixelStorei( 3317, texture.unpackAlignment ); _gl.pixelStorei( 37443, 0 ); const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); const cubeImage = []; for ( let i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); } const image = cubeImage[ 0 ], supportsMips = isPowerOfTwo$1( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.encoding ), glType = utils.convert( texture.type ), glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); const allocateMemory = ( source.__currentVersion === undefined ) || ( forceUpload === true ); let levels = getMipLevels( texture, image, supportsMips ); setTextureParameters( 34067, texture, supportsMips ); let mipmaps; if ( isCompressed ) { if ( useTexStorage && allocateMemory ) { state.texStorage2D( 34067, levels, glInternalFormat, image.width, image.height ); } for ( let i = 0; i < 6; i ++ ) { mipmaps = cubeImage[ i ].mipmaps; for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat ) { if ( glFormat !== null ) { if ( useTexStorage ) { state.compressedTexSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); } else { state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { if ( useTexStorage ) { state.texSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); } else { state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } } else { mipmaps = texture.mipmaps; if ( useTexStorage && allocateMemory ) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if ( mipmaps.length > 0 ) levels ++; state.texStorage2D( 34067, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); } for ( let i = 0; i < 6; i ++ ) { if ( isDataTexture ) { if ( useTexStorage ) { state.texSubImage2D( 34069 + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; const mipmapImage = mipmap.image[ i ].image; if ( useTexStorage ) { state.texSubImage2D( 34069 + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); } else { state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); } } } else { if ( useTexStorage ) { state.texSubImage2D( 34069 + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); } else { state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); } for ( let j = 0; j < mipmaps.length; j ++ ) { const mipmap = mipmaps[ j ]; if ( useTexStorage ) { state.texSubImage2D( 34069 + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); } else { state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); } } } } } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { // We assume images for cube map have the same size. generateMipmap( 34067 ); } source.__currentVersion = source.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { const glFormat = utils.convert( texture.format, texture.encoding ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); const renderTargetProperties = properties.get( renderTarget ); if ( ! renderTargetProperties.__hasExternalTextures ) { if ( textureTarget === 32879 || textureTarget === 35866 ) { state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); } else { state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); } } state.bindFramebuffer( 36160, framebuffer ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); } else { _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); } state.bindFramebuffer( 36160, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { _gl.bindRenderbuffer( 36161, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { let glInternalFormat = 33189; if ( isMultisample || useMultisampledRTT( renderTarget ) ) { const depthTexture = renderTarget.depthTexture; if ( depthTexture && depthTexture.isDepthTexture ) { if ( depthTexture.type === FloatType ) { glInternalFormat = 36012; } else if ( depthTexture.type === UnsignedIntType ) { glInternalFormat = 33190; } } const samples = getRenderTargetSamples( renderTarget ); if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } } else { _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, 35056, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); } _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); } else { const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; const glFormat = utils.convert( texture.format, texture.encoding ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); const samples = getRenderTargetSamples( renderTarget ); if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } else { _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); } } } _gl.bindRenderbuffer( 36161, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); state.bindFramebuffer( 36160, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; const samples = getRenderTargetSamples( renderTarget ); if ( renderTarget.depthTexture.format === DepthFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 36096, 3553, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); } } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { if ( useMultisampledRTT( renderTarget ) ) { multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 33306, 3553, webglDepthTexture, 0, samples ); } else { _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); } } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( let i = 0; i < 6; i ++ ) { state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); } } else { state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); } } state.bindFramebuffer( 36160, null ); } // rebind framebuffer with external textures function rebindTextures( renderTarget, colorTexture, depthTexture ) { const renderTargetProperties = properties.get( renderTarget ); if ( colorTexture !== undefined ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, 36064, 3553 ); } if ( depthTexture !== undefined ) { setupDepthRenderbuffer( renderTarget ); } } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { const texture = renderTarget.texture; const renderTargetProperties = properties.get( renderTarget ); const textureProperties = properties.get( texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { if ( textureProperties.__webglTexture === undefined ) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures ++; } const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( let i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if ( isMultipleRenderTargets ) { if ( capabilities.drawBuffers ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachmentProperties = properties.get( textures[ i ] ); if ( attachmentProperties.__webglTexture === undefined ) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } } } else { console.warn( "THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension." ); } } if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = isMultipleRenderTargets ? texture : [ texture ]; renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { const texture = textures[ i ]; renderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer(); _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const glFormat = utils.convert( texture.format, texture.encoding ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); const samples = getRenderTargetSamples( renderTarget ); _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( 36160, 36064 + i, 36161, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } _gl.bindRenderbuffer( 36161, null ); if ( renderTarget.depthBuffer ) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); } state.bindFramebuffer( 36160, null ); } } // Setup color buffer if ( isCube ) { state.bindTexture( 34067, textureProperties.__webglTexture ); setTextureParameters( 34067, texture, supportsMips ); for ( let i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, 36064, 34069 + i ); } if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( 34067 ); } state.unbindTexture(); } else if ( isMultipleRenderTargets ) { const textures = renderTarget.texture; for ( let i = 0, il = textures.length; i < il; i ++ ) { const attachment = textures[ i ]; const attachmentProperties = properties.get( attachment ); state.bindTexture( 3553, attachmentProperties.__webglTexture ); setTextureParameters( 3553, attachment, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, 36064 + i, 3553 ); if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { generateMipmap( 3553 ); } } state.unbindTexture(); } else { let glTextureType = 3553; if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { if ( isWebGL2 ) { glTextureType = renderTarget.isWebGL3DRenderTarget ? 32879 : 35866; } else { console.error( "THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2." ); } } state.bindTexture( glTextureType, textureProperties.__webglTexture ); setTextureParameters( glTextureType, texture, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, 36064, glTextureType ); if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { generateMipmap( glTextureType ); } state.unbindTexture(); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; for ( let i = 0, il = textures.length; i < il; i ++ ) { const texture = textures[ i ]; if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; const webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target ); state.unbindTexture(); } } } function updateMultisampleRenderTarget( renderTarget ) { if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ]; const width = renderTarget.width; const height = renderTarget.height; let mask = 16384; const invalidationArray = []; const depthStyle = renderTarget.stencilBuffer ? 33306 : 36096; const renderTargetProperties = properties.get( renderTarget ); const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); // If MRT we need to remove FBO attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( 36160, 36064 + i, 36161, null ); state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( 36009, 36064 + i, 3553, null, 0 ); } } state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); for ( let i = 0; i < textures.length; i ++ ) { invalidationArray.push( 36064 + i ); if ( renderTarget.depthBuffer ) { invalidationArray.push( depthStyle ); } const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; if ( ignoreDepthValues === false ) { if ( renderTarget.depthBuffer ) mask |= 256; if ( renderTarget.stencilBuffer ) mask |= 1024; } if ( isMultipleRenderTargets ) { _gl.framebufferRenderbuffer( 36008, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer[ i ] ); } if ( ignoreDepthValues === true ) { _gl.invalidateFramebuffer( 36008, [ depthStyle ] ); _gl.invalidateFramebuffer( 36009, [ depthStyle ] ); } if ( isMultipleRenderTargets ) { const webglTexture = properties.get( textures[ i ] ).__webglTexture; _gl.framebufferTexture2D( 36009, 36064, 3553, webglTexture, 0 ); } _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); if ( supportsInvalidateFramebuffer ) { _gl.invalidateFramebuffer( 36008, invalidationArray ); } } state.bindFramebuffer( 36008, null ); state.bindFramebuffer( 36009, null ); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if ( isMultipleRenderTargets ) { for ( let i = 0; i < textures.length; i ++ ) { state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); _gl.framebufferRenderbuffer( 36160, 36064 + i, 36161, renderTargetProperties.__webglColorRenderbuffer[ i ] ); const webglTexture = properties.get( textures[ i ] ).__webglTexture; state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); _gl.framebufferTexture2D( 36009, 36064 + i, 3553, webglTexture, 0 ); } } state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); } } function getRenderTargetSamples( renderTarget ) { return Math.min( maxSamples, renderTarget.samples ); } function useMultisampledRTT( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); return isWebGL2 && renderTarget.samples > 0 && extensions.has( "WEBGL_multisampled_render_to_texture" ) === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture( texture ) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures.get( texture ) !== frame ) { _videoTextures.set( texture, frame ); texture.update(); } } function verifyColorSpace( texture, image ) { const encoding = texture.encoding; const format = texture.format; const type = texture.type; if ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image; if ( encoding !== LinearEncoding ) { // sRGB if ( encoding === sRGBEncoding ) { if ( isWebGL2 === false ) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if ( extensions.has( "EXT_sRGB" ) === true && format === RGBAFormat ) { texture.format = _SRGBAFormat; // it"s not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear( image ); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if ( format !== RGBAFormat || type !== UnsignedByteType ) { console.warn( "THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType." ); } } } else { console.error( "THREE.WebGLTextures: Unsupported texture encoding:", encoding ); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils( gl, extensions, capabilities ) { const isWebGL2 = capabilities.isWebGL2; function convert( p, encoding = null ) { let extension; if ( p === UnsignedByteType ) return 5121; if ( p === UnsignedShort4444Type ) return 32819; if ( p === UnsignedShort5551Type ) return 32820; if ( p === ByteType ) return 5120; if ( p === ShortType ) return 5122; if ( p === UnsignedShortType ) return 5123; if ( p === IntType ) return 5124; if ( p === UnsignedIntType ) return 5125; if ( p === FloatType ) return 5126; if ( p === HalfFloatType ) { if ( isWebGL2 ) return 5131; extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) { return extension.HALF_FLOAT_OES; } else { return null; } } if ( p === AlphaFormat ) return 6406; if ( p === RGBAFormat ) return 6408; if ( p === LuminanceFormat ) return 6409; if ( p === LuminanceAlphaFormat ) return 6410; if ( p === DepthFormat ) return 6402; if ( p === DepthStencilFormat ) return 34041; if ( p === RedFormat ) return 6403; if ( p === RGBFormat ) { console.warn( "THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228" ); return 6408; } // WebGL 1 sRGB fallback if ( p === _SRGBAFormat ) { extension = extensions.get( "EXT_sRGB" ); if ( extension !== null ) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if ( p === RedIntegerFormat ) return 36244; if ( p === RGFormat ) return 33319; if ( p === RGIntegerFormat ) return 33320; if ( p === RGBAIntegerFormat ) return 36249; // S3TC if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { if ( encoding === sRGBEncoding ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc_srgb" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc" ); if ( extension !== null ) { if ( p === RGB_ETC2_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if ( p === RGBA_ETC2_EAC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { if ( p === RGBA_ASTC_4x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if ( p === RGBA_ASTC_5x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if ( p === RGBA_ASTC_5x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if ( p === RGBA_ASTC_6x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if ( p === RGBA_ASTC_6x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if ( p === RGBA_ASTC_8x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if ( p === RGBA_ASTC_8x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if ( p === RGBA_ASTC_8x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if ( p === RGBA_ASTC_10x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if ( p === RGBA_ASTC_10x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if ( p === RGBA_ASTC_10x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if ( p === RGBA_ASTC_10x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if ( p === RGBA_ASTC_12x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if ( p === RGBA_ASTC_12x12_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if ( p === RGBA_BPTC_Format ) { extension = extensions.get( "EXT_texture_compression_bptc" ); if ( extension !== null ) { if ( p === RGBA_BPTC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // if ( p === UnsignedInt248Type ) { if ( isWebGL2 ) return 34042; extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } // if "p" can"t be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return ( gl[ p ] !== undefined ) ? gl[ p ] : null; } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera$1 { constructor( array = [] ) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = "Group"; } } const _moveEvent = { type: "move" }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if ( this._hand === null ) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if ( this._targetRay === null ) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if ( this._grip === null ) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent( event ) { if ( this._targetRay !== null ) { this._targetRay.dispatchEvent( event ); } if ( this._grip !== null ) { this._grip.dispatchEvent( event ); } if ( this._hand !== null ) { this._hand.dispatchEvent( event ); } return this; } disconnect( inputSource ) { this.dispatchEvent( { type: "disconnected", data: inputSource } ); if ( this._targetRay !== null ) { this._targetRay.visible = false; } if ( this._grip !== null ) { this._grip.visible = false; } if ( this._hand !== null ) { this._hand.visible = false; } return this; } update( inputSource, frame, referenceSpace ) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if ( inputSource && frame.session.visibilityState !== "visible-blurred" ) { if ( hand && inputSource.hand ) { handPose = true; for ( const inputjoint of inputSource.hand.values() ) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose( inputjoint, referenceSpace ); if ( hand.joints[ inputjoint.jointName ] === undefined ) { // The transform of this joint will be updated with the joint pose on each frame const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[ inputjoint.jointName ] = joint; // ?? hand.add( joint ); } const joint = hand.joints[ inputjoint.jointName ]; if ( jointPose !== null ) { joint.matrix.fromArray( jointPose.transform.matrix ); joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints[ "index-finger-tip" ]; const thumbTip = hand.joints[ "thumb-tip" ]; const distance = indexTip.position.distanceTo( thumbTip.position ); const distanceToPinch = 0.02; const threshold = 0.005; if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { hand.inputState.pinching = false; this.dispatchEvent( { type: "pinchend", handedness: inputSource.handedness, target: this } ); } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { hand.inputState.pinching = true; this.dispatchEvent( { type: "pinchstart", handedness: inputSource.handedness, target: this } ); } } else { if ( grip !== null && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { grip.matrix.fromArray( gripPose.transform.matrix ); grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); if ( gripPose.linearVelocity ) { grip.hasLinearVelocity = true; grip.linearVelocity.copy( gripPose.linearVelocity ); } else { grip.hasLinearVelocity = false; } if ( gripPose.angularVelocity ) { grip.hasAngularVelocity = true; grip.angularVelocity.copy( gripPose.angularVelocity ); } else { grip.hasAngularVelocity = false; } } } } if ( targetRay !== null ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if ( inputPose === null && gripPose !== null ) { inputPose = gripPose; } if ( inputPose !== null ) { targetRay.matrix.fromArray( inputPose.transform.matrix ); targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); if ( inputPose.linearVelocity ) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy( inputPose.linearVelocity ); } else { targetRay.hasLinearVelocity = false; } if ( inputPose.angularVelocity ) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy( inputPose.angularVelocity ); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent( _moveEvent ); } } } if ( targetRay !== null ) { targetRay.visible = ( inputPose !== null ); } if ( grip !== null ) { grip.visible = ( gripPose !== null ); } if ( hand !== null ) { hand.visible = ( handPose !== null ); } return this; } } class DepthTexture extends Texture { constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedIntType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.isDepthTexture = true; this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } } class WebXRManager extends EventDispatcher { constructor( renderer, gl ) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = "local-floor"; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; // const cameraL = new PerspectiveCamera$1(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera$1(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); const cameras = [ cameraL, cameraR ]; const cameraVR = new ArrayCamera(); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getGripSpace(); }; this.getHand = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); controllers[ index ] = controller; } return controller.getHandSpace(); }; // function onSessionEvent( event ) { const controllerIndex = controllerInputSources.indexOf( event.inputSource ); if ( controllerIndex === - 1 ) { return; } const controller = controllers[ controllerIndex ]; if ( controller !== undefined ) { controller.dispatchEvent( { type: event.type, data: event.inputSource } ); } } function onSessionEnd() { session.removeEventListener( "select", onSessionEvent ); session.removeEventListener( "selectstart", onSessionEvent ); session.removeEventListener( "selectend", onSessionEvent ); session.removeEventListener( "squeeze", onSessionEvent ); session.removeEventListener( "squeezestart", onSessionEvent ); session.removeEventListener( "squeezeend", onSessionEvent ); session.removeEventListener( "end", onSessionEnd ); session.removeEventListener( "inputsourceschange", onInputSourcesChange ); for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; if ( inputSource === null ) continue; controllerInputSources[ i ] = null; controllers[ i ].disconnect( inputSource ); } _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget( initialRenderTarget ); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change framebuffer scale while presenting." ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; if ( scope.isPresenting === true ) { console.warn( "THREE.WebXRManager: Cannot change reference space type while presenting." ); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function ( space ) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = async function ( value ) { session = value; if ( session !== null ) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); session.addEventListener( "inputsourceschange", onInputSourcesChange ); if ( attributes.xrCompatible !== true ) { await gl.makeXRCompatible(); } if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { const layerInit = { antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: glBaseLayer } ); newRenderTarget = new WebGLRenderTarget( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, encoding: renderer.outputEncoding } ); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if ( attributes.depth ) { glDepthFormat = attributes.stencil ? 35056 : 33190; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: 32856, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding( session, gl ); glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); session.updateRenderState( { layers: [ glProjLayer ] } ); newRenderTarget = new WebGLRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), stencilBuffer: attributes.stencil, encoding: renderer.outputEncoding, samples: attributes.antialias ? 4 : 0 } ); const renderTargetProperties = renderer.properties.get( newRenderTarget ); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 // Set foveation to maximum. this.setFoveation( 1.0 ); customReferenceSpace = null; referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); animation.setContext( session ); animation.start(); scope.isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } }; function onInputSourcesChange( event ) { // Notify disconnected for ( let i = 0; i < event.removed.length; i ++ ) { const inputSource = event.removed[ i ]; const index = controllerInputSources.indexOf( inputSource ); if ( index >= 0 ) { controllerInputSources[ index ] = null; controllers[ index ].dispatchEvent( { type: "disconnected", data: inputSource } ); } } // Notify connected for ( let i = 0; i < event.added.length; i ++ ) { const inputSource = event.added[ i ]; let controllerIndex = controllerInputSources.indexOf( inputSource ); if ( controllerIndex === - 1 ) { // Assign input source a controller that currently has no input source for ( let i = 0; i < controllers.length; i ++ ) { if ( i >= controllerInputSources.length ) { controllerInputSources.push( inputSource ); controllerIndex = i; break; } else if ( controllerInputSources[ i ] === null ) { controllerInputSources[ i ] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if ( controllerIndex === - 1 ) break; } const controller = controllers[ controllerIndex ]; if ( controller ) { controller.dispatchEvent( { type: "connected", data: inputSource } ); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); const ipd = cameraLPos.distanceTo( cameraRPos ); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / ( - leftFov + rightFov ); const xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + ( ipd - xOffset ); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); } this.updateCamera = function ( camera ) { if ( session === null ) return; cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraVR.near, depthFar: cameraVR.far } ); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } const parent = camera.parent; const cameras = cameraVR.cameras; updateCamera( cameraVR, parent ); for ( let i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } cameraVR.matrixWorld.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); // update user camera and its children camera.position.copy( cameraVR.position ); camera.quaternion.copy( cameraVR.quaternion ); camera.scale.copy( cameraVR.scale ); camera.matrix.copy( cameraVR.matrix ); camera.matrixWorld.copy( cameraVR.matrixWorld ); const children = camera.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } // update projection matrix for proper view frustum culling if ( cameras.length === 2 ) { setProjectionFromUnion( cameraVR, cameraL, cameraR ); } else { // assume single camera setup (AR) cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); } }; this.getCamera = function () { return cameraVR; }; this.getFoveation = function () { if ( glProjLayer !== null ) { return glProjLayer.fixedFoveation; } if ( glBaseLayer !== null ) { return glBaseLayer.fixedFoveation; } return undefined; }; this.setFoveation = function ( foveation ) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution if ( glProjLayer !== null ) { glProjLayer.fixedFoveation = foveation; } if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { glBaseLayer.fixedFoveation = foveation; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); xrFrame = frame; if ( pose !== null ) { const views = pose.views; if ( glBaseLayer !== null ) { renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); renderer.setRenderTarget( newRenderTarget ); } let cameraVRNeedsUpdate = false; // check if it"s necessary to rebuild cameraVR"s camera list if ( views.length !== cameraVR.cameras.length ) { cameraVR.cameras.length = 0; cameraVRNeedsUpdate = true; } for ( let i = 0; i < views.length; i ++ ) { const view = views[ i ]; let viewport = null; if ( glBaseLayer !== null ) { viewport = glBaseLayer.getViewport( view ); } else { const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if ( i === 0 ) { renderer.setRenderTargetTextures( newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); renderer.setRenderTarget( newRenderTarget ); } } let camera = cameras[ i ]; if ( camera === undefined ) { camera = new PerspectiveCamera$1(); camera.layers.enable( i ); camera.viewport = new Vector4(); cameras[ i ] = camera; } camera.matrix.fromArray( view.transform.matrix ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraVR.matrix.copy( camera.matrix ); } if ( cameraVRNeedsUpdate === true ) { cameraVR.cameras.push( camera ); } } } // for ( let i = 0; i < controllers.length; i ++ ) { const inputSource = controllerInputSources[ i ]; const controller = controllers[ i ]; if ( inputSource !== null && controller !== undefined ) { controller.update( inputSource, frame, customReferenceSpace || referenceSpace ); } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials( renderer, properties ) { function refreshFogUniforms( uniforms, fog ) { uniforms.fogColor.value.copy( fog.color ); if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshToonMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsToon( uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsPhong( uniforms, material ); } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsStandard( uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); } } else if ( material.isMeshMatcapMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsMatcap( uniforms, material ); } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( uniforms, material ); refreshUniformsDistance( uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( uniforms, material, pixelRatio, height ); } else if ( material.isSpriteMaterial ) { refreshUniformsSprites( uniforms, material ); } else if ( material.isShadowMaterial ) { uniforms.color.value.copy( material.color ); uniforms.opacity.value = material.opacity; } else if ( material.isShaderMaterial ) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value.copy( material.color ); } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get( material ).envMap; if ( envMap ) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; // artist-friendly light intensity scaling factor const scaleFactor = ( renderer.physicallyCorrectLights !== true ) ? Math.PI : 1; uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. displacementMap map // 4. normal map // 5. bump map // 6. roughnessMap map // 7. metalnessMap map // 8. alphaMap map // 9. emissiveMap map // 10. clearcoat map // 11. clearcoat normal map // 12. clearcoat roughnessMap map // 13. iridescence map // 14. iridescence thickness map // 15. specular intensity map // 16. specular tint map // 17. transmission map // 18. thickness map let uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.displacementMap ) { uvScaleMap = material.displacementMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.roughnessMap ) { uvScaleMap = material.roughnessMap; } else if ( material.metalnessMap ) { uvScaleMap = material.metalnessMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } else if ( material.emissiveMap ) { uvScaleMap = material.emissiveMap; } else if ( material.clearcoatMap ) { uvScaleMap = material.clearcoatMap; } else if ( material.clearcoatNormalMap ) { uvScaleMap = material.clearcoatNormalMap; } else if ( material.clearcoatRoughnessMap ) { uvScaleMap = material.clearcoatRoughnessMap; } else if ( material.iridescenceMap ) { uvScaleMap = material.iridescenceMap; } else if ( material.iridescenceThicknessMap ) { uvScaleMap = material.iridescenceThicknessMap; } else if ( material.specularIntensityMap ) { uvScaleMap = material.specularIntensityMap; } else if ( material.specularColorMap ) { uvScaleMap = material.specularColorMap; } else if ( material.transmissionMap ) { uvScaleMap = material.transmissionMap; } else if ( material.thicknessMap ) { uvScaleMap = material.thicknessMap; } else if ( material.sheenColorMap ) { uvScaleMap = material.sheenColorMap; } else if ( material.sheenRoughnessMap ) { uvScaleMap = material.sheenRoughnessMap; } if ( uvScaleMap !== undefined ) { // backwards compatibility if ( uvScaleMap.isWebGLRenderTarget ) { uvScaleMap = uvScaleMap.texture; } if ( uvScaleMap.matrixAutoUpdate === true ) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } // uv repeat and offset setting priorities for uv2 // 1. ao map // 2. light map let uv2ScaleMap; if ( material.aoMap ) { uv2ScaleMap = material.aoMap; } else if ( material.lightMap ) { uv2ScaleMap = material.lightMap; } if ( uv2ScaleMap !== undefined ) { // backwards compatibility if ( uv2ScaleMap.isWebGLRenderTarget ) { uv2ScaleMap = uv2ScaleMap.texture; } if ( uv2ScaleMap.matrixAutoUpdate === true ) { uv2ScaleMap.updateMatrix(); } uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } if ( uvScaleMap !== undefined ) { if ( uvScaleMap.matrixAutoUpdate === true ) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } function refreshUniformsSprites( uniforms, material ) { uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.alphaTest > 0 ) { uniforms.alphaTest.value = material.alphaTest; } // uv repeat and offset setting priorities // 1. color map // 2. alpha map let uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } if ( uvScaleMap !== undefined ) { if ( uvScaleMap.matrixAutoUpdate === true ) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value.copy( material.specular ); uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon( uniforms, material ) { if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; } if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; } const envMap = properties.get( material ).envMap; if ( envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { uniforms.ior.value = material.ior; // also part of uniforms common if ( material.sheen > 0 ) { uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); uniforms.sheenRoughness.value = material.sheenRoughness; if ( material.sheenColorMap ) { uniforms.sheenColorMap.value = material.sheenColorMap; } if ( material.sheenRoughnessMap ) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; } } if ( material.clearcoat > 0 ) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if ( material.clearcoatMap ) { uniforms.clearcoatMap.value = material.clearcoatMap; } if ( material.clearcoatRoughnessMap ) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; } if ( material.clearcoatNormalMap ) { uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if ( material.side === BackSide ) { uniforms.clearcoatNormalScale.value.negate(); } } } if ( material.iridescence > 0 ) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ]; if ( material.iridescenceMap ) { uniforms.iridescenceMap.value = material.iridescenceMap; } if ( material.iridescenceThicknessMap ) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; } } if ( material.transmission > 0 ) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); if ( material.transmissionMap ) { uniforms.transmissionMap.value = material.transmissionMap; } uniforms.thickness.value = material.thickness; if ( material.thicknessMap ) { uniforms.thicknessMap.value = material.thicknessMap; } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy( material.attenuationColor ); } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy( material.specularColor ); if ( material.specularIntensityMap ) { uniforms.specularIntensityMap.value = material.specularIntensityMap; } if ( material.specularColorMap ) { uniforms.specularColorMap.value = material.specularColorMap; } } function refreshUniformsMatcap( uniforms, material ) { if ( material.matcap ) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance( uniforms, material ) { uniforms.referencePosition.value.copy( material.referencePosition ); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function createCanvasElement() { const canvas = createElementNS( "canvas" ); canvas.style.display = "block"; return canvas; } function WebGLRenderer( parameters = {} ) { this.isWebGLRenderer = true; const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), _context = parameters.context !== undefined ? parameters.context : null, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; let _alpha; if ( _context !== null ) { _alpha = _context.getContextAttributes().alpha; } else { _alpha = parameters.alpha !== undefined ? parameters.alpha : false; } let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputEncoding = LinearEncoding; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // Object.defineProperties( this, { // @deprecated since r136, 0e21088102b4de7e0a0a33140620b7a3424b9e6d gammaFactor: { get: function () { console.warn( "THREE.WebGLRenderer: .gammaFactor has been removed." ); return 2; }, set: function () { console.warn( "THREE.WebGLRenderer: .gammaFactor has been removed." ); } } } ); // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = - 1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; // let _width = _canvas.width; let _height = _canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4( 0, 0, _width, _height ); const _scissor = new Vector4( 0, 0, _width, _height ); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = _context; function getContext( contextNames, contextAttributes ) { for ( let i = 0; i < contextNames.length; i ++ ) { const contextName = contextNames[ i ]; const context = _canvas.getContext( contextName, contextAttributes ); if ( context !== null ) return context; } return null; } try { const contextAttributes = { alpha: true, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ( "setAttribute" in _canvas ) _canvas.setAttribute( "data-engine", `three.js r${REVISION}` ); // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener( "webglcontextlost", onContextLost, false ); _canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); _canvas.addEventListener( "webglcontextcreationerror", onContextCreationError, false ); if ( _gl === null ) { const contextNames = [ "webgl2", "webgl", "experimental-webgl" ]; if ( _this.isWebGL1Renderer === true ) { contextNames.shift(); } _gl = getContext( contextNames, contextAttributes ); if ( _gl === null ) { if ( getContext( contextNames ) ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates; function initGLContext() { extensions = new WebGLExtensions( _gl ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); extensions.init( capabilities ); utils = new WebGLUtils( _gl, extensions, capabilities ); state = new WebGLState( _gl, extensions, capabilities ); info = new WebGLInfo(); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); cubemaps = new WebGLCubeMaps( _this ); cubeuvmaps = new WebGLCubeUVMaps( _this ); attributes = new WebGLAttributes( _gl, capabilities ); bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); objects = new WebGLObjects( _gl, geometries, attributes, info ); morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); clipping = new WebGLClipping( properties ); programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); materials = new WebGLMaterials( _this, properties ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions, capabilities ); background = new WebGLBackground( _this, cubemaps, state, objects, _alpha, _premultipliedAlpha ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager( _this, _gl ); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function ( target ) { return target.set( _width, _height ); }; this.setSize = function ( width, height, updateStyle ) { if ( xr.isPresenting ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; _canvas.width = Math.floor( width * _pixelRatio ); _canvas.height = Math.floor( height * _pixelRatio ); if ( updateStyle !== false ) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function ( target ) { return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor( width * pixelRatio ); _canvas.height = Math.floor( height * pixelRatio ); this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function ( target ) { return target.copy( _currentViewport ); }; this.getViewport = function ( target ) { return target.copy( _viewport ); }; this.setViewport = function ( x, y, width, height ) { if ( x.isVector4 ) { _viewport.set( x.x, x.y, x.z, x.w ); } else { _viewport.set( x, y, width, height ); } state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissor = function ( target ) { return target.copy( _scissor ); }; this.setScissor = function ( x, y, width, height ) { if ( x.isVector4 ) { _scissor.set( x.x, x.y, x.z, x.w ); } else { _scissor.set( x, y, width, height ); } state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; this.setOpaqueSort = function ( method ) { _opaqueSort = method; }; this.setTransparentSort = function ( method ) { _transparentSort = method; }; // Clearing this.getClearColor = function ( target ) { return target.copy( background.getClearColor() ); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color = true, depth = true, stencil = true ) { let bits = 0; if ( color ) bits |= 16384; if ( depth ) bits |= 256; if ( stencil ) bits |= 1024; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; // this.dispose = function () { _canvas.removeEventListener( "webglcontextlost", onContextLost, false ); _canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); _canvas.removeEventListener( "webglcontextcreationerror", onContextCreationError, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener( "sessionstart", onXRSessionStart ); xr.removeEventListener( "sessionend", onXRSessionEnd ); if ( _transmissionRenderTarget ) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError( event ) { console.error( "THREE.WebGLRenderer: A WebGL context could not be created. Reason: ", event.statusMessage ); } function onMaterialDispose( event ) { const material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReferences( material ); properties.remove( material ); } function releaseMaterialProgramReferences( material ) { const programs = properties.get( material ).programs; if ( programs !== undefined ) { programs.forEach( function ( program ) { programCache.releaseProgram( program ); } ); if ( material.isShaderMaterial ) { programCache.releaseShaderCache( material ); } } } // Buffer rendering this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); const program = setProgram( camera, scene, geometry, material, object ); state.setMaterial( material, frontFaceCW ); // let index = geometry.index; const position = geometry.attributes.position; // if ( index === null ) { if ( position === undefined || position.count === 0 ) return; } else if ( index.count === 0 ) { return; } // let rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); rangeFactor = 2; } bindingStates.setup( object, material, program, geometry, index ); let attribute; let renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } // const dataCount = ( index !== null ) ? index.count : position.count; const rangeStart = geometry.drawRange.start * rangeFactor; const rangeCount = geometry.drawRange.count * rangeFactor; const groupStart = group !== null ? group.start * rangeFactor : 0; const groupCount = group !== null ? group.count * rangeFactor : Infinity; const drawStart = Math.max( rangeStart, groupStart ); const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( 1 ); } else { renderer.setMode( 4 ); } } else if ( object.isLine ) { let lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( 1 ); } else if ( object.isLineLoop ) { renderer.setMode( 2 ); } else { renderer.setMode( 3 ); } } else if ( object.isPoints ) { renderer.setMode( 0 ); } else if ( object.isSprite ) { renderer.setMode( 4 ); } if ( object.isInstancedMesh ) { renderer.renderInstances( drawStart, drawCount, object.count ); } else if ( geometry.isInstancedBufferGeometry ) { const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); renderer.renderInstances( drawStart, drawCount, instanceCount ); } else { renderer.render( drawStart, drawCount ); } }; // Compile this.compile = function ( scene, camera ) { currentRenderState = renderStates.get( scene ); currentRenderState.init(); renderStateStack.push( currentRenderState ); scene.traverseVisible( function ( object ) { if ( object.isLight && object.layers.test( camera.layers ) ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( _this.physicallyCorrectLights ); scene.traverse( function ( object ) { const material = object.material; if ( material ) { if ( Array.isArray( material ) ) { for ( let i = 0; i < material.length; i ++ ) { const material2 = material[ i ]; getProgram( material2, scene, object ); } } else { getProgram( material, scene, object ); } } } ); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame( time ) { if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); if ( typeof self !== "undefined" ) animation.setContext( self ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; xr.setAnimationLoop( callback ); ( callback === null ) ? animation.stop() : animation.start(); }; xr.addEventListener( "sessionstart", onXRSessionStart ); xr.addEventListener( "sessionend", onXRSessionEnd ); // Rendering this.render = function ( scene, camera ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost === true ) return; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); if ( xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering } // if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); currentRenderState = renderStates.get( scene, renderStateStack.length ); currentRenderState.init(); renderStateStack.push( currentRenderState ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromProjectionMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); currentRenderList = renderLists.get( scene, renderListStack.length ); currentRenderList.init(); renderListStack.push( currentRenderList ); projectObject( scene, camera, 0, _this.sortObjects ); currentRenderList.finish(); if ( _this.sortObjects === true ) { currentRenderList.sort( _opaqueSort, _transparentSort ); } // if ( _clippingEnabled === true ) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); if ( _clippingEnabled === true ) clipping.endShadows(); // if ( this.info.autoReset === true ) this.info.reset(); // background.render( currentRenderList, scene ); // render scene currentRenderState.setupLights( _this.physicallyCorrectLights ); if ( camera.isArrayCamera ) { const cameras = camera.cameras; for ( let i = 0, l = cameras.length; i < l; i ++ ) { const camera2 = cameras[ i ]; renderScene( currentRenderList, scene, camera2, camera2.viewport ); } } else { renderScene( currentRenderList, scene, camera ); } // if ( _currentRenderTarget !== null ) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget( _currentRenderTarget ); // Generate mipmap if we"re using any kind of mipmap filtering textures.updateRenderTargetMipmap( _currentRenderTarget ); } // if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = - 1; _currentCamera = null; renderStateStack.pop(); if ( renderStateStack.length > 0 ) { currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; } else { currentRenderState = null; } renderListStack.pop(); if ( renderListStack.length > 0 ) { currentRenderList = renderListStack[ renderListStack.length - 1 ]; } else { currentRenderList = null; } }; function projectObject( object, camera, groupOrder, sortObjects ) { if ( object.visible === false ) return; const visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isGroup ) { groupOrder = object.renderOrder; } else if ( object.isLOD ) { if ( object.autoUpdate === true ) object.update( camera ); } else if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } const geometry = objects.update( object ); const material = object.material; if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( object.isSkinnedMesh ) { // update skeleton only once in a frame if ( object.skeleton.frame !== info.render.frame ) { object.skeleton.update(); object.skeleton.frame = info.render.frame; } } if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } const geometry = objects.update( object ); const material = object.material; if ( Array.isArray( material ) ) { const groups = geometry.groups; for ( let i = 0, l = groups.length; i < l; i ++ ) { const group = groups[ i ]; const groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } } } } const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, groupOrder, sortObjects ); } } function renderScene( currentRenderList, scene, camera, viewport ) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView( camera ); if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera ); if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); } function renderTransmissionPass( opaqueObjects, scene, camera ) { const isWebGL2 = capabilities.isWebGL2; if ( _transmissionRenderTarget === null ) { _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { generateMipmaps: true, type: extensions.has( "EXT_color_buffer_half_float" ) ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: ( isWebGL2 && _antialias === true ) ? 4 : 0 } ); } _this.getDrawingBufferSize( _vector2 ); if ( isWebGL2 ) { _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); } else { _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget( _transmissionRenderTarget ); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects( opaqueObjects, scene, camera ); _this.toneMapping = currentToneMapping; textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); textures.updateRenderTargetMipmap( _transmissionRenderTarget ); _this.setRenderTarget( currentRenderTarget ); } function renderObjects( renderList, scene, camera ) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for ( let i = 0, l = renderList.length; i < l; i ++ ) { const renderItem = renderList[ i ]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if ( object.layers.test( camera.layers ) ) { renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); material.onBeforeRender( _this, scene, camera, geometry, object, group ); if ( material.transparent === true && material.side === DoubleSide ) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect( camera, scene, geometry, material, object, group ); material.side = DoubleSide; } else { _this.renderBufferDirect( camera, scene, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } function getProgram( material, scene, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); const programCacheKey = programCache.getProgramCacheKey( parameters ); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it"s possible that the program doesn"t change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); if ( programs === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); programs = new Map(); materialProperties.programs = programs; } let program = programs.get( programCacheKey ); if ( program !== undefined ) { // early out if program and light state is identical if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { updateCommonMaterialProperties( material, parameters ); return program; } } else { parameters.uniforms = programCache.getUniforms( material ); material.onBuild( object, parameters, _this ); material.onBeforeCompile( parameters, _this ); program = programCache.acquireProgram( parameters, programCacheKey ); programs.set( programCacheKey, program ); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties( material, parameters ); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights( material ); materialProperties.lightsStateVersion = lightsStateVersion; if ( materialProperties.needsLights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties( material, parameters ) { const materialProperties = properties.get( material ); materialProperties.outputEncoding = parameters.outputEncoding; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram( camera, scene, geometry, material, object ) { if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding ); const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; const morphTargets = !! geometry.morphAttributes.position; const morphNormals = !! geometry.morphAttributes.normal; const morphColors = !! geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; const materialProperties = properties.get( material ); const lights = currentRenderState.state.lights; if ( _clippingEnabled === true ) { if ( _localClippingEnabled === true || camera !== _currentCamera ) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState( material, camera, useCache ); } } // let needsProgramChange = false; if ( material.version === materialProperties.__version ) { if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { needsProgramChange = true; } else if ( materialProperties.outputEncoding !== encoding ) { needsProgramChange = true; } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { needsProgramChange = true; } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { needsProgramChange = true; } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { needsProgramChange = true; } else if ( materialProperties.envMap !== envMap ) { needsProgramChange = true; } else if ( material.fog === true && materialProperties.fog !== fog ) { needsProgramChange = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection ) ) { needsProgramChange = true; } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { needsProgramChange = true; } else if ( materialProperties.vertexTangents !== vertexTangents ) { needsProgramChange = true; } else if ( materialProperties.morphTargets !== morphTargets ) { needsProgramChange = true; } else if ( materialProperties.morphNormals !== morphNormals ) { needsProgramChange = true; } else if ( materialProperties.morphColors !== morphColors ) { needsProgramChange = true; } else if ( materialProperties.toneMapping !== toneMapping ) { needsProgramChange = true; } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if ( needsProgramChange === true ) { program = getProgram( material, scene, object ); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || _currentCamera !== camera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( _currentCamera !== camera ) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap ) { const uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial ) { p_uniforms.setValue( _gl, "isOrthographic", camera.isOrthographicCamera === true ); } if ( material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning and morph target uniforms must be set even if material didn"t change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if ( object.isSkinnedMesh ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); const skeleton = object.skeleton; if ( skeleton ) { if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture, textures ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { console.warn( "THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required." ); } } } const morphAttributes = geometry.morphAttributes; if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { morphtargets.update( object, geometry, material, program ); } if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue( _gl, "receiveShadow", object.receiveShadow ); } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); if ( materialProperties.needsLights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog === true ) { materials.refreshFogUniforms( m_uniforms, fog ); } materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); material.uniformsNeedUpdate = false; } if ( material.isSpriteMaterial ) { p_uniforms.setValue( _gl, "center", object.center ); } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); return program; } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights( material ) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || ( material.isShaderMaterial && material.lights === true ); } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { properties.get( renderTarget.texture ).__webglTexture = colorTexture; properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__hasExternalTextures = true; if ( renderTargetProperties.__hasExternalTextures ) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { // The multisample_render_to_texture extension doesn"t work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if ( extensions.has( "WEBGL_multisampled_render_to_texture" ) === true ) { console.warn( "THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided" ); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; if ( renderTarget ) { const renderTargetProperties = properties.get( renderTarget ); if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer( 36160, null ); useDefaultFramebuffer = false; } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } else if ( renderTargetProperties.__hasExternalTextures ) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); } } let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if ( renderTarget ) { const texture = renderTarget.texture; if ( texture.isData3DTexture || texture.isDataArrayTexture ) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget ) { framebuffer = __webglFramebuffer[ activeCubeFace ]; isCube = true; } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer( 36160, framebuffer ); if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { state.drawBuffers( renderTarget, framebuffer ); } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { const textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); } else if ( isRenderTarget3D ) { const textureProperties = properties.get( renderTarget.texture ); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); } _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } let framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[ activeCubeFaceIndex ]; } if ( framebuffer ) { state.bindFramebuffer( 36160, framebuffer ); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( "EXT_color_buffer_half_float" ) || ( capabilities.isWebGL2 && extensions.has( "EXT_color_buffer_float" ) ) ); if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( "OES_texture_float" ) || extensions.has( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! halfFloatSupportedByExt ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } finally { // restore framebuffer of current render target if necessary const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; state.bindFramebuffer( 36160, framebuffer ); } } }; this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { const levelScale = Math.pow( 2, - level ); const width = Math.floor( texture.image.width * levelScale ); const height = Math.floor( texture.image.height * levelScale ); textures.setTexture2D( texture, 0 ); _gl.copyTexSubImage2D( 3553, level, 0, 0, position.x, position.y, width, height ); state.unbindTexture(); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); textures.setTexture2D( dstTexture, 0 ); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei( 37440, dstTexture.flipY ); _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { if ( srcTexture.isCompressedTexture ) { _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); } else { _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); } } // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); state.unbindTexture(); }; this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { if ( _this.isWebGL1Renderer ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2." ); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert( dstTexture.format ); const glType = utils.convert( dstTexture.type ); let glTarget; if ( dstTexture.isData3DTexture ) { textures.setTexture3D( dstTexture, 0 ); glTarget = 32879; } else if ( dstTexture.isDataArrayTexture ) { textures.setTexture2DArray( dstTexture, 0 ); glTarget = 35866; } else { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray." ); return; } _gl.pixelStorei( 37440, dstTexture.flipY ); _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); const unpackRowLen = _gl.getParameter( 3314 ); const unpackImageHeight = _gl.getParameter( 32878 ); const unpackSkipPixels = _gl.getParameter( 3316 ); const unpackSkipRows = _gl.getParameter( 3315 ); const unpackSkipImages = _gl.getParameter( 32877 ); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; _gl.pixelStorei( 3314, image.width ); _gl.pixelStorei( 32878, image.height ); _gl.pixelStorei( 3316, sourceBox.min.x ); _gl.pixelStorei( 3315, sourceBox.min.y ); _gl.pixelStorei( 32877, sourceBox.min.z ); if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); } else { if ( srcTexture.isCompressedTexture ) { console.warn( "THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture." ); _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); } else { _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } } _gl.pixelStorei( 3314, unpackRowLen ); _gl.pixelStorei( 32878, unpackImageHeight ); _gl.pixelStorei( 3316, unpackSkipPixels ); _gl.pixelStorei( 3315, unpackSkipRows ); _gl.pixelStorei( 32877, unpackSkipImages ); // Generate mipmaps only when copying level 0 if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); state.unbindTexture(); }; this.initTexture = function ( texture ) { if ( texture.isCubeTexture ) { textures.setTextureCube( texture, 0 ); } else if ( texture.isData3DTexture ) { textures.setTexture3D( texture, 0 ); } else if ( texture.isDataArrayTexture ) { textures.setTexture2DArray( texture, 0 ); } else { textures.setTexture2D( texture, 0 ); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = "Scene"; this.background = null; this.environment = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "observe", { detail: this } ) ); } } copy( source, recursive ) { super.copy( source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.environment !== null ) this.environment = source.environment.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON( meta ) { const data = super.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); return data; } } class InterleavedBuffer { constructor( array, stride ) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate( value ) { if ( value === true ) this.version ++; } setUsage( value ) { this.usage = value; return this; } copy( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( let i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; } set( value, offset = 0 ) { this.array.set( value, offset ); return this; } clone( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; } const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); const ib = new this.constructor( array, this.stride ); ib.setUsage( this.usage ); return ib; } onUpload( callback ) { this.onUploadCallback = callback; return this; } toJSON( data ) { if ( data.arrayBuffers === undefined ) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if ( this.array.buffer._uuid === undefined ) { this.array.buffer._uuid = generateUUID(); } if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } const _vector$6 = /*@__PURE__*/ new Vector3(); class InterleavedBufferAttribute { constructor( interleavedBuffer, itemSize, offset, normalized = false ) { this.isInterleavedBufferAttribute = true; this.name = ""; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate( value ) { this.data.needsUpdate = value; } applyMatrix4( m ) { for ( let i = 0, l = this.data.count; i < l; i ++ ) { _vector$6.fromBufferAttribute( this, i ); _vector$6.applyMatrix4( m ); this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); } return this; } applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$6.fromBufferAttribute( this, i ); _vector$6.applyNormalMatrix( m ); this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); } return this; } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { _vector$6.fromBufferAttribute( this, i ); _vector$6.transformDirection( m ); this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); } return this; } setX( index, x ) { this.data.array[ index * this.data.stride + this.offset ] = x; return this; } setY( index, y ) { this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; } setZ( index, z ) { this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; } setW( index, w ) { this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; } getX( index ) { return this.data.array[ index * this.data.stride + this.offset ]; } getY( index ) { return this.data.array[ index * this.data.stride + this.offset + 1 ]; } getZ( index ) { return this.data.array[ index * this.data.stride + this.offset + 2 ]; } getW( index ) { return this.data.array[ index * this.data.stride + this.offset + 3 ]; } setXY( index, x, y ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; } setXYZ( index, x, y, z ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; } setXYZW( index, x, y, z, w ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } clone( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will deinterleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); } return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); } } toJSON( data ) { if ( data === undefined ) { console.log( "THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will deinterleave buffer data." ); const array = []; for ( let i = 0; i < this.count; i ++ ) { const index = i * this.data.stride + this.offset; for ( let j = 0; j < this.itemSize; j ++ ) { array.push( this.data.array[ index + j ] ); } } // deinterleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interleaved attribtue if ( data.interleavedBuffers === undefined ) { data.interleavedBuffers = {}; } if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } class SpriteMaterial extends Material { constructor( parameters ) { super(); this.isSpriteMaterial = true; this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/ new Vector3(); const _worldScale = /*@__PURE__*/ new Vector3(); const _mvPosition = /*@__PURE__*/ new Vector3(); const _alignedPosition = /*@__PURE__*/ new Vector2(); const _rotatedPosition = /*@__PURE__*/ new Vector2(); const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); const _vA = /*@__PURE__*/ new Vector3(); const _vB = /*@__PURE__*/ new Vector3(); const _vC = /*@__PURE__*/ new Vector3(); const _uvA = /*@__PURE__*/ new Vector2(); const _uvB = /*@__PURE__*/ new Vector2(); const _uvC = /*@__PURE__*/ new Vector2(); class Sprite extends Object3D { constructor( material ) { super(); this.isSprite = true; this.type = "Sprite"; if ( _geometry === undefined ) { _geometry = new BufferGeometry(); const float32Array = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0, 0.5, - 0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, - 0.5, 0.5, 0, 0, 1 ] ); const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); _geometry.setAttribute( "position", new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); _geometry.setAttribute( "uv", new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); } this.geometry = _geometry; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } raycast( raycaster, intersects ) { if ( raycaster.camera === null ) { console.error( "THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites." ); } _worldScale.setFromMatrixScale( this.matrixWorld ); _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { _worldScale.multiplyScalar( - _mvPosition.z ); } const rotation = this.material.rotation; let sin, cos; if ( rotation !== 0 ) { cos = Math.cos( rotation ); sin = Math.sin( rotation ); } const center = this.center; transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvA.set( 0, 0 ); _uvB.set( 1, 0 ); _uvC.set( 1, 1 ); // check first triangle let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); if ( intersect === null ) { // check second triangle transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); _uvB.set( 0, 1 ); intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); if ( intersect === null ) { return; } } const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: _intersectPoint.clone(), uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), face: null, object: this } ); } copy( source, recursive ) { super.copy( source, recursive ); if ( source.center !== undefined ) this.center.copy( source.center ); this.material = source.material; return this; } } function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { // compute position in camera space _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); // to check if rotation is not zero if ( sin !== undefined ) { _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); } else { _rotatedPosition.copy( _alignedPosition ); } vertexPosition.copy( mvPosition ); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4( _viewWorldMatrix ); } class LineBasicMaterial extends Material { constructor( parameters ) { super(); this.isLineBasicMaterial = true; this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _start$1 = /*@__PURE__*/ new Vector3(); const _end$1 = /*@__PURE__*/ new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); const _ray$1 = /*@__PURE__*/ new Ray(); const _sphere$1 = /*@__PURE__*/ new Sphere(); class Line extends Object3D { constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { super(); this.isLine = true; this.type = "Line"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = [ 0 ]; for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { _start$1.fromBufferAttribute( positionAttribute, i - 1 ); _end$1.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += _start$1.distanceTo( _end$1 ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere$1.copy( geometry.boundingSphere ); _sphere$1.applyMatrix4( matrixWorld ); _sphere$1.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; // _inverseMatrix$1.copy( matrixWorld ).invert(); _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { const a = index.getX( i ); const b = index.getX( i + 1 ); vStart.fromBufferAttribute( positionAttribute, a ); vEnd.fromBufferAttribute( positionAttribute, b ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end - 1; i < l; i += step ) { vStart.fromBufferAttribute( positionAttribute, i ); vEnd.fromBufferAttribute( positionAttribute, i + 1 ); const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > localThresholdSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } const _start = /*@__PURE__*/ new Vector3(); const _end = /*@__PURE__*/ new Vector3(); class LineSegments extends Line { constructor( geometry, material ) { super( geometry, material ); this.isLineSegments = true; this.type = "LineSegments"; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if ( geometry.index === null ) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { _start.fromBufferAttribute( positionAttribute, i ); _end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); } geometry.setAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } return this; } } class PointsMaterial extends Material { constructor( parameters ) { super(); this.isPointsMaterial = true; this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues( parameters ); } copy( source ) { super.copy( source ); this.color.copy( source.color ); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); const _sphere = /*@__PURE__*/ new Sphere(); const _position$2 = /*@__PURE__*/ new Vector3(); class Points extends Object3D { constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); this.isPoints = true; this.type = "Points"; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy( source, recursive ) { super.copy( source, recursive ); this.material = source.material; this.geometry = source.geometry; return this; } raycast( raycaster, intersects ) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ); _sphere.applyMatrix4( matrixWorld ); _sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; // _inverseMatrix.copy( matrixWorld ).invert(); _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if ( index !== null ) { const start = Math.max( 0, drawRange.start ); const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, il = end; i < il; i ++ ) { const a = index.getX( i ); _position$2.fromBufferAttribute( positionAttribute, a ); testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } else { const start = Math.max( 0, drawRange.start ); const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( let i = start, l = end; i < l; i ++ ) { _position$2.fromBufferAttribute( positionAttribute, i ); testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { const morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { const name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } } function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { const rayPointDistanceSq = _ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { const intersectPoint = new Vector3(); _ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); const distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint, index: index, face: null, object: object } ); } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = "Curve"; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); } // Get sequence of points using getPoint( t ) getPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints( divisions = 5 ) { const points = []; for ( let d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; } // Get list of cumulative segment lengths getLengths( divisions = this.arcLengthDivisions ) { if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint( 0 ); let sum = 0; cache.push( 0 ); for ( let p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping( u, distance ) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[ i ]; const lengthAfter = arcLengths[ i + 1 ]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t const t = ( i + segmentFraction ) / ( il - 1 ); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent( t, optionalTarget ) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; const pt1 = this.getPoint( t1 ); const pt2 = this.getPoint( t2 ); const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); tangent.copy( pt2 ).sub( pt1 ).normalize(); return tangent; } getTangentAt( u, optionalTarget ) { const t = this.getUtoTmapping( u ); return this.getTangent( t, optionalTarget ); } computeFrenetFrames( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for ( let i = 0; i <= segments; i ++ ) { const u = i / segments; tangents[ i ] = this.getTangentAt( u, new Vector3() ); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs( tangents[ 0 ].x ); const ty = Math.abs( tangents[ 0 ].y ); const tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( let i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); const theta = Math.acos( clamp$1( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { let theta = Math.acos( clamp$1( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( let i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy( this ); } copy( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = /*@__PURE__*/ new Vector3(); const px = /*@__PURE__*/ new CubicPoly(); const py = /*@__PURE__*/ new CubicPoly(); const pz = /*@__PURE__*/ new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor( points = [], closed = false, curveType = "centripetal", tension = 0.5 ) { super(); this.isCatmullRomCurve3 = true; this.type = "CatmullRomCurve3"; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const points = this.points; const l = points.length; const p = ( l - ( this.closed ? 0 : 1 ) ) * t; let intPoint = Math.floor( p ); let weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } const p1 = points[ intPoint % l ]; const p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === "chordal" ? 0.5 : 0.25; let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; } copy( source ) { super.copy( source ); this.points = []; for ( let i = 0, l = source.points.length; i < l; i ++ ) { const point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for ( let i = 0, l = this.points.length; i < l; i ++ ) { const point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON( json ) { super.fromJSON( json ); this.points = []; for ( let i = 0, l = json.points.length; i < l; i ++ ) { const point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } class CircleGeometry extends BufferGeometry { constructor( radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2 ) { super(); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max( 3, segments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( let i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } static fromJSON( data ) { return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); } } class SphereGeometry extends BufferGeometry { constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { super(); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max( 3, Math.floor( widthSegments ) ); heightSegments = Math.max( 2, Math.floor( heightSegments ) ); const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for ( let iy = 0; iy <= heightSegments; iy ++ ) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if ( iy == 0 && thetaStart == 0 ) { uOffset = 0.5 / widthSegments; } else if ( iy == heightSegments && thetaEnd == Math.PI ) { uOffset = - 0.5 / widthSegments; } for ( let ix = 0; ix <= widthSegments; ix ++ ) { const u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.copy( vertex ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u + uOffset, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( let iy = 0; iy < heightSegments; iy ++ ) { for ( let ix = 0; ix < widthSegments; ix ++ ) { const a = grid[ iy ][ ix + 1 ]; const b = grid[ iy ][ ix ]; const c = grid[ iy + 1 ][ ix ]; const d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.setAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.setAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } static fromJSON( data ) { return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); } } class Clock { constructor( autoStart = true ) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { const newTime = now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 } class Raycaster { constructor( origin, direction, near = 0, far = Infinity ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); } setFromCamera( coords, camera ) { if ( camera.isPerspectiveCamera ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); this.camera = camera; } else if ( camera.isOrthographicCamera ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); this.camera = camera; } else { console.error( "THREE.Raycaster: Unsupported camera type: " + camera.type ); } } intersectObject( object, recursive = true, intersects = [] ) { intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; } intersectObjects( objects, recursive = true, intersects = [] ) { for ( let i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.layers.test( raycaster.layers ) ) { object.raycast( raycaster, intersects ); } if ( recursive === true ) { const children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } if ( typeof __THREE_DEVTOOLS__ !== "undefined" ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( "register", { detail: { revision: REVISION, } } ) ); } if ( typeof window !== "undefined" ) { if ( window.__THREE__ ) { console.warn( "WARNING: Multiple instances of Three.js being imported." ); } else { window.__THREE__ = REVISION; } } const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; const WGS84A = 6378137.0; const WGS84B = 6356752.31424518; /** * Convert coordinates from geodetic (WGS84) reference to local topocentric * (ENU) reference. * * @param {number} lng Longitude in degrees. * @param {number} lat Latitude in degrees. * @param {number} alt Altitude in meters. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The x, y, z local topocentric ENU coordinates. */ function geodeticToEnu(lng, lat, alt, refLng, refLat, refAlt) { const ecef = geodeticToEcef(lng, lat, alt); return ecefToEnu(ecef[0], ecef[1], ecef[2], refLng, refLat, refAlt); } /** * Convert coordinates from local topocentric (ENU) reference to * geodetic (WGS84) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The longitude, latitude in degrees * and altitude in meters. */ function enuToGeodetic(x, y, z, refLng, refLat, refAlt) { const ecef = enuToEcef(x, y, z, refLng, refLat, refAlt); return ecefToGeodetic(ecef[0], ecef[1], ecef[2]); } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to local topocentric (ENU) reference. * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The x, y, z topocentric ENU coordinates in East, North * and Up directions respectively. */ function ecefToEnu(X, Y, Z, refLng, refLat, refAlt) { const refEcef = geodeticToEcef(refLng, refLat, refAlt); const V = [ X - refEcef[0], Y - refEcef[1], Z - refEcef[2], ]; refLng = refLng * DEG2RAD; refLat = refLat * DEG2RAD; const cosLng = Math.cos(refLng); const sinLng = Math.sin(refLng); const cosLat = Math.cos(refLat); const sinLat = Math.sin(refLat); const x = -sinLng * V[0] + cosLng * V[1]; const y = -sinLat * cosLng * V[0] - sinLat * sinLng * V[1] + cosLat * V[2]; const z = cosLat * cosLng * V[0] + cosLat * sinLng * V[1] + sinLat * V[2]; return [x, y, z]; } /** * Convert coordinates from local topocentric (ENU) reference * to Earth-Centered, Earth-Fixed (ECEF) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLng Reference longitude in degrees. * @param {number} refLat Reference latitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array} The X, Y, Z ECEF coordinates. */ function enuToEcef(x, y, z, refLng, refLat, refAlt) { const refEcef = geodeticToEcef(refLng, refLat, refAlt); refLng = refLng * DEG2RAD; refLat = refLat * DEG2RAD; const cosLng = Math.cos(refLng); const sinLng = Math.sin(refLng); const cosLat = Math.cos(refLat); const sinLat = Math.sin(refLat); const X = -sinLng * x - sinLat * cosLng * y + cosLat * cosLng * z + refEcef[0]; const Y = cosLng * x - sinLat * sinLng * y + cosLat * sinLng * z + refEcef[1]; const Z = cosLat * y + sinLat * z + refEcef[2]; return [X, Y, Z]; } /** * Convert coordinates from geodetic reference (WGS84) to Earth-Centered, * Earth-Fixed (ECEF) reference. * * @param {number} lng Longitude in degrees. * @param {number} lat Latitude in degrees. * @param {number} alt Altitude in meters. * @returns {Array} The X, Y, Z ECEF coordinates. */ function geodeticToEcef(lng, lat, alt) { const a = WGS84A; const b = WGS84B; lng = lng * DEG2RAD; lat = lat * DEG2RAD; const cosLng = Math.cos(lng); const sinLng = Math.sin(lng); const cosLat = Math.cos(lat); const sinLat = Math.sin(lat); const a2 = a * a; const b2 = b * b; const L = 1.0 / Math.sqrt(a2 * cosLat * cosLat + b2 * sinLat * sinLat); const nhcl = (a2 * L + alt) * cosLat; const X = nhcl * cosLng; const Y = nhcl * sinLng; const Z = (b2 * L + alt) * sinLat; return [X, Y, Z]; } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to geodetic reference (WGS84). * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @returns {Array} The longitude, latitude in degrees * and altitude in meters. */ function ecefToGeodetic(X, Y, Z) { const a = WGS84A; const b = WGS84B; const a2 = a * a; const b2 = b * b; const a2mb2 = a2 - b2; const ea = Math.sqrt(a2mb2 / a2); const eb = Math.sqrt(a2mb2 / b2); const p = Math.sqrt(X * X + Y * Y); const theta = Math.atan2(Z * a, p * b); const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); const lng = Math.atan2(Y, X); const lat = Math.atan2(Z + eb * eb * b * sinTheta * sinTheta * sinTheta, p - ea * ea * a * cosTheta * cosTheta * cosTheta); const sinLat = Math.sin(lat); const cosLat = Math.cos(lat); const N = a / Math.sqrt(1 - ea * ea * sinLat * sinLat); const alt = p / cosLat - N; return [ lng * RAD2DEG, lat * RAD2DEG, alt ]; } /** * @class GraphCalculator * * @classdesc Represents a calculator for graph entities. */ class GraphCalculator { /** * Get the bounding box corners for a circle with radius of a threshold * with center in a geodetic position. * * @param {LngLat} lngLat - Longitude, latitude to encode. * @param {number} threshold - Threshold distance from the position in meters. * * @returns {Array} The south west and north east corners of the * bounding box. */ boundingBoxCorners(lngLat, threshold) { const sw = enuToGeodetic(-threshold, -threshold, 0, lngLat.lng, lngLat.lat, 0); const ne = enuToGeodetic(threshold, threshold, 0, lngLat.lng, lngLat.lat, 0); return [ { lat: sw[1], lng: sw[0] }, { lat: ne[1], lng: ne[0] }, ]; } /** * Convert a compass angle to an angle axis rotation vector. * * @param {number} compassAngle - The compass angle in degrees. * @param {number} orientation - The orientation of the original image. * * @returns {Array} Angle axis rotation vector. */ rotationFromCompass(compassAngle, orientation) { let x = 0; let y = 0; let z = 0; switch (orientation) { case 1: x = Math.PI / 2; break; case 3: x = -Math.PI / 2; z = Math.PI; break; case 6: y = -Math.PI / 2; z = -Math.PI / 2; break; case 8: y = Math.PI / 2; z = Math.PI / 2; break; } const rz = new Matrix4() .makeRotationZ(z); const euler = new Euler(x, y, compassAngle * Math.PI / 180, "XYZ"); const re = new Matrix4() .makeRotationFromEuler(euler); const rotation = new Vector4() .setAxisAngleFromRotationMatrix(re.multiply(rz)); return rotation .multiplyScalar(rotation.w) .toArray() .slice(0, 3); } } /** * @class Image * * @classdesc Represents a image in the navigation graph. * * Explanation of position and bearing properties: * * When images are uploaded they will have GPS information in the EXIF, this is what * is called `originalLngLat` {@link Image.originalLngLat}. * * When Structure from Motions has been run for a image a `computedLngLat` that * differs from the `originalLngLat` will be created. It is different because * GPS positions are not very exact and SfM aligns the camera positions according * to the 3D reconstruction {@link Image.computedLngLat}. * * At last there exist a `lngLat` property which evaluates to * the `computedLngLat` from SfM if it exists but falls back * to the `originalLngLat` from the EXIF GPS otherwise {@link Image.lngLat}. * * Everything that is done in in the Viewer is based on the SfM positions, * i.e. `computedLngLat`. That is why the smooth transitions go in the right * direction (nd not in strange directions because of bad GPS). * * E.g. when placing a marker in the Viewer it is relative to the SfM * position i.e. the `computedLngLat`. * * The same concept as above also applies to the compass angle (or bearing) properties * `originalCa`, `computedCa` and `ca`. */ class Image$1 { /** * Create a new image instance. * * @description Images are always created internally by the library. * Images can not be added to the library through any API method. * * @param {CoreImageEnt} core- Raw core image data. * @ignore */ constructor(core) { if (!core) { throw new Error(`Incorrect core image data ${core}`); } this._cache = null; this._core = core; this._spatial = null; } /** * Get assets cached. * * @description The assets that need to be cached for this property * to report true are the following: fill properties, image and mesh. * The library ensures that the current image will always have the * assets cached. * * @returns {boolean} Value indicating whether all assets have been * cached. * * @ignore */ get assetsCached() { return this._core != null && this._spatial != null && this._cache != null && this._cache.camera != null && this._cache.image != null && this._cache.mesh != null; } /** * Get camera. * * @returns {ICamera} Camera of the image * * @ignore */ get camera() { return this._cache.camera; } /** * Get cameraParameters. * * @description Will be undefined if SfM has * not been run. * * Camera type dependent parameters. * * For perspective and fisheye camera types, * the camera parameters array should be * constructed according to * * `[focal, k1, k2]` * * where focal is the camera focal length, * and k1, k2 are radial distortion parameters. * * For spherical camera type the camera * parameters are unset or emtpy array. * * @returns {Array} The parameters * related to the camera type. */ get cameraParameters() { return this._spatial.camera_parameters; } /** * Get cameraType. * * @description Will be undefined if SfM has not been run. * * @returns {string} The camera type that captured the image. */ get cameraType() { return this._spatial.camera_type; } /** * Get capturedAt. * * @description Timestamp of the image capture date * and time represented as a Unix epoch timestamp in milliseconds. * * @returns {number} Timestamp when the image was captured. */ get capturedAt() { return this._spatial.captured_at; } /** * Get clusterId. * * @returns {string} Globally unique id of the SfM cluster to which * the image belongs. */ get clusterId() { return !!this._spatial.cluster ? this._spatial.cluster.id : null; } /** * Get clusterUrl. * * @returns {string} Url to the cluster reconstruction file. * * @ignore */ get clusterUrl() { return !!this._spatial.cluster ? this._spatial.cluster.url : null; } /** * Get compassAngle. * * @description If the SfM computed compass angle exists it will * be returned, otherwise the original EXIF compass angle. * * @returns {number} Compass angle, measured in degrees * clockwise with respect to north. */ get compassAngle() { return this._spatial.computed_compass_angle != null ? this._spatial.computed_compass_angle : this._spatial.compass_angle; } /** * Get complete. * * @description The library ensures that the current image will * always be full. * * @returns {boolean} Value indicating whether the image has all * properties filled. * * @ignore */ get complete() { return this._spatial != null; } /** * Get computedAltitude. * * @description If SfM has not been run the computed altitude is * set to a default value of two meters. * * @returns {number} Altitude, in meters. */ get computedAltitude() { return this._spatial.computed_altitude; } /** * Get computedCompassAngle. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed compass angle, measured * in degrees clockwise with respect to north. */ get computedCompassAngle() { return this._spatial.computed_compass_angle; } /** * Get computedLngLat. * * @description Will not be set if SfM has not been run. * * @returns {LngLat} SfM computed longitude, latitude in WGS84 datum, * measured in degrees. */ get computedLngLat() { return this._core.computed_geometry; } /** * Get creatorId. * * @description Note that the creator ID will not be set when using * the Mapillary API. * * @returns {string} Globally unique id of the user who uploaded * the image. */ get creatorId() { return this._spatial.creator.id; } /** * Get creatorUsername. * * @description Note that the creator username will not be set when * using the Mapillary API. * * @returns {string} Username of the creator who uploaded * the image. */ get creatorUsername() { return this._spatial.creator.username; } /** * Get exifOrientation. * * @returns {number} EXIF orientation of original image. */ get exifOrientation() { return this._spatial.exif_orientation; } /** * Get height. * * @returns {number} Height of original image, not adjusted * for orientation. */ get height() { return this._spatial.height; } /** * Get image. * * @description The image will always be set on the current image. * * @returns {HTMLImageElement} Cached image element of the image. */ get image() { return this._cache.image; } /** * Get image$. * * @returns {Observable} Observable emitting * the cached image when it is updated. * * @ignore */ get image$() { return this._cache.image$; } /** * Get id. * * @returns {string} Globally unique id of the image. */ get id() { return this._core.id; } /** * Get is disposed. * * @description If the image has been disposed no properties * are acessible. Image are disposed when the {@link Viewer} * is reset or a new data provider is set. * * @returns {boolean} Value indicating that this image * has been disposed. */ get isDisposed() { return !this._core; } /** * Get lngLat. * * @description If the SfM computed longitude, latitude exist * it will be returned, otherwise the original EXIF latitude * longitude. * * @returns {LngLat} Longitude, latitude in WGS84 datum, * measured in degrees. */ get lngLat() { return this._core.computed_geometry != null ? this._core.computed_geometry : this._core.geometry; } /** * Get merged. * * @returns {boolean} Value indicating whether SfM has been * run on the image and the image has been merged into a * connected component. */ get merged() { return this._spatial != null && this._spatial.merge_id != null; } /** * Get mergeId. * * @description Will not be set if SfM has not yet been run on * image. * * @returns {stirng} Id of connected component to which image * belongs after the aligning merge. */ get mergeId() { return this._spatial.merge_id; } /** * Get mesh. * * @description The mesh will always be set on the current image. * * @returns {MeshContract} SfM triangulated mesh of reconstructed * atomic 3D points. */ get mesh() { return this._cache.mesh; } /** * Get originalAltitude. * * @returns {number} EXIF altitude, in meters, if available. */ get originalAltitude() { return this._spatial.altitude; } /** * Get originalCompassAngle. * * @returns {number} Original EXIF compass angle, measured in * degrees. */ get originalCompassAngle() { return this._spatial.compass_angle; } /** * Get originalLngLat. * * @returns {LngLat} Original EXIF longitude, latitude in * WGS84 datum, measured in degrees. */ get originalLngLat() { return this._core.geometry; } /** * Get ownerId. * * @returns {string} Globally unique id of the owner to which * the image belongs. If the image does not belong to an * owner the owner id will be undefined. */ get ownerId() { return !!this._spatial.owner ? this._spatial.owner.id : null; } /** * Get private. * * @returns {boolean} Value specifying if image is accessible to * organization members only or to everyone. */ get private() { return this._spatial.private; } /** * Get qualityScore. * * @returns {number} A number between zero and one * determining the quality of the image. Blurriness * (motion blur / out-of-focus), occlusion (camera * mount, ego vehicle, water-drops), windshield * reflections, bad illumination (exposure, glare), * and bad weather condition (fog, rain, snow) * affect the quality score. * * @description Value should be on the interval [0, 1]. */ get qualityScore() { return this._spatial.quality_score; } /** * Get rotation. * * @description Will not be set if SfM has not been run. * * @returns {Array} Rotation vector in angle axis representation. */ get rotation() { return this._spatial.computed_rotation; } /** * Get scale. * * @description Will not be set if SfM has not been run. * * @returns {number} Scale of reconstruction the image * belongs to. */ get scale() { return this._spatial.atomic_scale; } /** * Get sequenceId. * * @returns {string} Globally unique id of the sequence * to which the image belongs. */ get sequenceId() { return !!this._core.sequence ? this._core.sequence.id : null; } /** * Get sequenceEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * sequence edges. * * @ignore */ get sequenceEdges() { return this._cache.sequenceEdges; } /** * Get sequenceEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable} Observable emitting * values describing the status of the sequence edges. * * @ignore */ get sequenceEdges$() { return this._cache.sequenceEdges$; } /** * Get spatialEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * spatial edges. * * @ignore */ get spatialEdges() { return this._cache.spatialEdges; } /** * Get spatialEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable} Observable emitting * values describing the status of the spatial edges. * * @ignore */ get spatialEdges$() { return this._cache.spatialEdges$; } /** * Get width. * * @returns {number} Width of original image, not * adjusted for orientation. */ get width() { return this._spatial.width; } /** * Cache the image and mesh assets. * * @description The assets are always cached internally by the * library prior to setting a image as the current image. * * @returns {Observable} Observable emitting this image whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ cacheAssets$(factory) { this.cacheCamera(factory); return this._cache .cacheAssets$(this._spatial, this.merged) .pipe(map(() => { return this; })); } /** * Cache the image camera. * * @ignore */ cacheCamera(factory) { this._cache.cacheCamera(factory.makeCamera(this._spatial.camera_type, this._spatial.camera_parameters)); } /** * Cache the image asset. * * @description Use for caching a differently sized image than * the one currently held by the image. * * @returns {Observable} Observable emitting this image whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ cacheImage$() { return this._cache .cacheImage$(this._spatial) .pipe(map(() => { return this; })); } /** * Cache the sequence edges. * * @description The sequence edges are cached asynchronously * internally by the library. * * @param {Array} edges - Sequence edges to cache. * @ignore */ cacheSequenceEdges(edges) { this._cache.cacheSequenceEdges(edges); } /** * Cache the spatial edges. * * @description The spatial edges are cached asynchronously * internally by the library. * * @param {Array} edges - Spatial edges to cache. * @ignore */ cacheSpatialEdges(edges) { this._cache.cacheSpatialEdges(edges); } /** * Dispose the image. * * @description Disposes all cached assets. * @ignore */ dispose() { if (this._cache != null) { this._cache.dispose(); this._cache = null; } this._core = null; this._spatial = null; } /** * Returns a value indicating if the image has an initialized * image cache. * * @description The image cache is initialized internally by * the library. * * @ignore */ hasInitializedCache() { return this._cache != null; } /** * Initialize the image cache. * * @description The image cache is initialized internally by * the library. * * @param {ImageCache} cache - The image cache to set as cache. * @ignore */ initializeCache(cache) { if (this._cache != null) { throw new Error(`Image cache already initialized (${this.id}).`); } this._cache = cache; } /** * Complete an image with spatial properties. * * @description The image is completed internally by * the library. * * @param {SpatialImageEnt} fill - The spatial image struct. * @ignore */ makeComplete(fill) { if (fill == null) { throw new Error("Fill can not be null."); } this._spatial = fill; } /** * Reset the sequence edges. * * @ignore */ resetSequenceEdges() { this._cache.resetSequenceEdges(); } /** * Reset the spatial edges. * * @ignore */ resetSpatialEdges() { this._cache.resetSpatialEdges(); } /** * Clears the image and mesh assets, aborts * any outstanding requests and resets edges. * * @ignore */ uncache() { if (this._cache == null) { return; } this._cache.dispose(); this._cache = null; } } /** * @class ImageCache * * @classdesc Represents the cached properties of a image. */ class ImageCache { /** * Create a new image cache instance. */ constructor(provider) { this._disposed = false; this._provider = provider; this._camera = null; this._image = null; this._mesh = null; this._sequenceEdges = { cached: false, edges: [] }; this._spatialEdges = { cached: false, edges: [] }; this._imageChanged$ = new Subject(); this._image$ = this._imageChanged$.pipe(startWith(null), publishReplay(1), refCount()); this._iamgeSubscription = this._image$.subscribe(); this._sequenceEdgesChanged$ = new Subject(); this._sequenceEdges$ = this._sequenceEdgesChanged$.pipe(startWith(this._sequenceEdges), publishReplay(1), refCount()); this._sequenceEdgesSubscription = this._sequenceEdges$.subscribe(() => { }); this._spatialEdgesChanged$ = new Subject(); this._spatialEdges$ = this._spatialEdgesChanged$.pipe(startWith(this._spatialEdges), publishReplay(1), refCount()); this._spatialEdgesSubscription = this._spatialEdges$.subscribe(() => { }); this._cachingAssets$ = null; } get camera() { return this._camera; } /** * Get image. * * @description Will not be set when assets have not been cached * or when the object has been disposed. * * @returns {HTMLImageElement} Cached image element of the image. */ get image() { return this._image; } /** * Get image$. * * @returns {Observable} Observable emitting * the cached image when it is updated. */ get image$() { return this._image$; } /** * Get mesh. * * @description Will not be set when assets have not been cached * or when the object has been disposed. * * @returns {MeshContract} SfM triangulated mesh of reconstructed * atomic 3D points. */ get mesh() { return this._mesh; } /** * Get sequenceEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * sequence edges. */ get sequenceEdges() { return this._sequenceEdges; } /** * Get sequenceEdges$. * * @returns {Observable} Observable emitting * values describing the status of the sequence edges. */ get sequenceEdges$() { return this._sequenceEdges$; } /** * Get spatialEdges. * * @returns {NavigationEdgeStatus} Value describing the status of the * spatial edges. */ get spatialEdges() { return this._spatialEdges; } /** * Get spatialEdges$. * * @returns {Observable} Observable emitting * values describing the status of the spatial edges. */ get spatialEdges$() { return this._spatialEdges$; } cacheCamera(camera) { this._camera = camera; } /** * Cache the image and mesh assets. * * @param {SpatialImageEnt} spatial - Spatial props of the image to cache. * @param {boolean} spherical - Value indicating whether image is a spherical. * @param {boolean} merged - Value indicating whether image is merged. * @returns {Observable} Observable emitting this image * cache whenever the load status has changed and when the mesh or image * has been fully loaded. */ cacheAssets$(spatial, merged) { if (this._cachingAssets$ != null) { return this._cachingAssets$; } this._cachingAssets$ = combineLatest(this._cacheImage$(spatial), this._cacheMesh$(spatial, merged)).pipe(map(([image, mesh]) => { this._image = image; this._mesh = mesh; return this; }), finalize(() => { this._cachingAssets$ = null; }), publishReplay(1), refCount()); this._cachingAssets$.pipe(first((imageCache) => { return !!imageCache._image; })) .subscribe(() => { this._imageChanged$.next(this._image); }, () => { }); return this._cachingAssets$; } /** * Cache an image with a higher resolution than the current one. * * @param {SpatialImageEnt} spatial - Spatial props. * @returns {Observable} Observable emitting a single item, * the image cache, when the image has been cached. If supplied image * size is not larger than the current image size the image cache is * returned immediately. */ cacheImage$(spatial) { if (this._image != null) { return of(this); } const cacheImage$ = this._cacheImage$(spatial) .pipe(first((image) => { return !!image; }), tap((image) => { this._disposeImage(); this._image = image; }), map(() => { return this; }), publishReplay(1), refCount()); cacheImage$ .subscribe(() => { this._imageChanged$.next(this._image); }, () => { }); return cacheImage$; } /** * Cache the sequence edges. * * @param {Array} edges - Sequence edges to cache. */ cacheSequenceEdges(edges) { this._sequenceEdges = { cached: true, edges: edges }; this._sequenceEdgesChanged$.next(this._sequenceEdges); } /** * Cache the spatial edges. * * @param {Array} edges - Spatial edges to cache. */ cacheSpatialEdges(edges) { this._spatialEdges = { cached: true, edges: edges }; this._spatialEdgesChanged$.next(this._spatialEdges); } /** * Dispose the image cache. * * @description Disposes all cached assets and unsubscribes to * all streams. */ dispose() { this._iamgeSubscription.unsubscribe(); this._sequenceEdgesSubscription.unsubscribe(); this._spatialEdgesSubscription.unsubscribe(); this._disposeImage(); this._camera = null; this._mesh = null; this._sequenceEdges = { cached: false, edges: [] }; this._spatialEdges = { cached: false, edges: [] }; this._imageChanged$.next(null); this._sequenceEdgesChanged$.next(this._sequenceEdges); this._spatialEdgesChanged$.next(this._spatialEdges); this._disposed = true; if (this._imageAborter != null) { this._imageAborter(); this._imageAborter = null; } if (this._meshAborter != null) { this._meshAborter(); this._meshAborter = null; } } /** * Reset the sequence edges. */ resetSequenceEdges() { this._sequenceEdges = { cached: false, edges: [] }; this._sequenceEdgesChanged$.next(this._sequenceEdges); } /** * Reset the spatial edges. */ resetSpatialEdges() { this._spatialEdges = { cached: false, edges: [] }; this._spatialEdgesChanged$.next(this._spatialEdges); } /** * Cache the image. * * @param {SpatialImageEnt} spatial - Spatial image. * @param {boolean} spherical - Value indicating whether image is a spherical. * @returns {Observable>} Observable * emitting a load status object every time the load status changes * and completes when the image is fully loaded. */ _cacheImage$(spatial) { return Observable.create((subscriber) => { const abort = new Promise((_, reject) => { this._imageAborter = reject; }); const url = spatial.thumb.url; if (!url) { const thumbId = spatial.thumb.id; const message = `Incorrect thumb URL for ${spatial.id} ` + `(${thumbId}, ${url})`; subscriber.error(new Error(message)); return; } this._provider.getImageBuffer(url, abort) .then((buffer) => { this._imageAborter = null; const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { if (this._disposed) { window.URL.revokeObjectURL(image.src); const message = `Image load was aborted (${url})`; subscriber.error(new Error(message)); return; } subscriber.next(image); subscriber.complete(); }; image.onerror = () => { this._imageAborter = null; subscriber.error(new Error(`Failed to load image (${url})`)); }; const blob = new Blob([buffer]); image.src = window.URL.createObjectURL(blob); }, (error) => { this._imageAborter = null; subscriber.error(error); }); }); } /** * Cache the mesh. * * @param {SpatialImageEnt} spatial - Spatial props. * @param {boolean} merged - Value indicating whether image is merged. * @returns {Observable>} Observable emitting * a load status object every time the load status changes and completes * when the mesh is fully loaded. */ _cacheMesh$(spatial, merged) { return Observable.create((subscriber) => { if (!merged) { subscriber.next(this._createEmptyMesh()); subscriber.complete(); return; } const url = spatial.mesh.url; if (!url) { const meshId = spatial.mesh.id; const message = `Incorrect mesh URL for ${spatial.id} ` + `(${meshId}, ${url})`; console.warn(message); subscriber.next(this._createEmptyMesh()); subscriber.complete(); return; } const abort = new Promise((_, reject) => { this._meshAborter = reject; }); this._provider.getMesh(url, abort) .then((mesh) => { this._meshAborter = null; if (this._disposed) { return; } subscriber.next(mesh); subscriber.complete(); }, (error) => { this._meshAborter = null; console.error(error); subscriber.next(this._createEmptyMesh()); subscriber.complete(); }); }); } /** * Create a load status object with an empty mesh. * * @returns {ILoadStatusObject} Load status object * with empty mesh. */ _createEmptyMesh() { return { faces: [], vertices: [] }; } _disposeImage() { if (this._image != null) { window.URL.revokeObjectURL(this._image.src); } this._image = null; } } /** * @class Sequence * * @classdesc Represents a sequence of ordered images. */ class Sequence { /** * Create a new sequene instance. * * @param {SequenceEnt} sequence - Raw sequence data. */ constructor(sequence) { this._id = sequence.id; this._imageIds = sequence.image_ids; } /** * Get id. * * @returns {string} Unique sequence id. */ get id() { return this._id; } /** * Get ids. * * @returns {Array} Array of ordered image ids in the sequence. */ get imageIds() { return this._imageIds; } /** * Dispose the sequence. * * @description Disposes all cached assets. */ dispose() { this._id = null; this._imageIds = null; } /** * Find the next image id in the sequence with respect to * the provided image id. * * @param {string} id - Reference image id. * @returns {string} Next id in sequence if it exists, null otherwise. */ findNext(id) { let i = this._imageIds.indexOf(id); if ((i + 1) >= this._imageIds.length || i === -1) { return null; } else { return this._imageIds[i + 1]; } } /** * Find the previous image id in the sequence with respect to * the provided image id. * * @param {string} id - Reference image id. * @returns {string} Previous id in sequence if it exists, null otherwise. */ findPrev(id) { let i = this._imageIds.indexOf(id); if (i === 0 || i === -1) { return null; } else { return this._imageIds[i - 1]; } } } class EdgeCalculatorCoefficients { constructor() { this.sphericalPreferredDistance = 2; this.sphericalMotion = 2; this.sphericalSequencePenalty = 1; this.sphericalMergeCCPenalty = 4; this.stepPreferredDistance = 4; this.stepMotion = 3; this.stepRotation = 4; this.stepSequencePenalty = 2; this.stepMergeCCPenalty = 6; this.similarDistance = 2; this.similarRotation = 3; this.turnDistance = 4; this.turnMotion = 2; this.turnSequencePenalty = 1; this.turnMergeCCPenalty = 4; } } /** * Enumeration for edge directions * @enum {number} * @readonly * @description Directions for edges in image graph describing * sequence, spatial and image type relations between nodes. */ exports.NavigationDirection = void 0; (function (NavigationDirection) { /** * Next image in the sequence. */ NavigationDirection[NavigationDirection["Next"] = 0] = "Next"; /** * Previous image in the sequence. */ NavigationDirection[NavigationDirection["Prev"] = 1] = "Prev"; /** * Step to the left keeping viewing direction. */ NavigationDirection[NavigationDirection["StepLeft"] = 2] = "StepLeft"; /** * Step to the right keeping viewing direction. */ NavigationDirection[NavigationDirection["StepRight"] = 3] = "StepRight"; /** * Step forward keeping viewing direction. */ NavigationDirection[NavigationDirection["StepForward"] = 4] = "StepForward"; /** * Step backward keeping viewing direction. */ NavigationDirection[NavigationDirection["StepBackward"] = 5] = "StepBackward"; /** * Turn 90 degrees counter clockwise. */ NavigationDirection[NavigationDirection["TurnLeft"] = 6] = "TurnLeft"; /** * Turn 90 degrees clockwise. */ NavigationDirection[NavigationDirection["TurnRight"] = 7] = "TurnRight"; /** * Turn 180 degrees. */ NavigationDirection[NavigationDirection["TurnU"] = 8] = "TurnU"; /** * Spherical in general direction. */ NavigationDirection[NavigationDirection["Spherical"] = 9] = "Spherical"; /** * Looking in roughly the same direction at rougly the same position. */ NavigationDirection[NavigationDirection["Similar"] = 10] = "Similar"; })(exports.NavigationDirection || (exports.NavigationDirection = {})); class EdgeCalculatorDirections { constructor() { this.steps = {}; this.turns = {}; this.spherical = {}; this.steps[exports.NavigationDirection.StepForward] = { direction: exports.NavigationDirection.StepForward, motionChange: 0, useFallback: true, }; this.steps[exports.NavigationDirection.StepBackward] = { direction: exports.NavigationDirection.StepBackward, motionChange: Math.PI, useFallback: true, }; this.steps[exports.NavigationDirection.StepLeft] = { direction: exports.NavigationDirection.StepLeft, motionChange: Math.PI / 2, useFallback: false, }; this.steps[exports.NavigationDirection.StepRight] = { direction: exports.NavigationDirection.StepRight, motionChange: -Math.PI / 2, useFallback: false, }; this.turns[exports.NavigationDirection.TurnLeft] = { direction: exports.NavigationDirection.TurnLeft, directionChange: Math.PI / 2, motionChange: Math.PI / 4, }; this.turns[exports.NavigationDirection.TurnRight] = { direction: exports.NavigationDirection.TurnRight, directionChange: -Math.PI / 2, motionChange: -Math.PI / 4, }; this.turns[exports.NavigationDirection.TurnU] = { direction: exports.NavigationDirection.TurnU, directionChange: Math.PI, motionChange: null, }; this.spherical[exports.NavigationDirection.StepForward] = { direction: exports.NavigationDirection.StepForward, directionChange: 0, next: exports.NavigationDirection.StepLeft, prev: exports.NavigationDirection.StepRight, }; this.spherical[exports.NavigationDirection.StepBackward] = { direction: exports.NavigationDirection.StepBackward, directionChange: Math.PI, next: exports.NavigationDirection.StepRight, prev: exports.NavigationDirection.StepLeft, }; this.spherical[exports.NavigationDirection.StepLeft] = { direction: exports.NavigationDirection.StepLeft, directionChange: Math.PI / 2, next: exports.NavigationDirection.StepBackward, prev: exports.NavigationDirection.StepForward, }; this.spherical[exports.NavigationDirection.StepRight] = { direction: exports.NavigationDirection.StepRight, directionChange: -Math.PI / 2, next: exports.NavigationDirection.StepForward, prev: exports.NavigationDirection.StepBackward, }; } } class EdgeCalculatorSettings { constructor() { this.sphericalMinDistance = 0.1; this.sphericalMaxDistance = 20; this.sphericalPreferredDistance = 5; this.sphericalMaxItems = 4; this.sphericalMaxStepTurnChange = Math.PI / 8; this.rotationMaxDistance = this.turnMaxRigDistance; this.rotationMaxDirectionChange = Math.PI / 6; this.rotationMaxVerticalDirectionChange = Math.PI / 8; this.similarMaxDirectionChange = Math.PI / 8; this.similarMaxDistance = 12; this.similarMinTimeDifference = 12 * 3600 * 1000; this.stepMaxDistance = 20; this.stepMaxDirectionChange = Math.PI / 6; this.stepMaxDrift = Math.PI / 6; this.stepPreferredDistance = 4; this.turnMaxDistance = 15; this.turnMaxDirectionChange = 2 * Math.PI / 9; this.turnMaxRigDistance = 0.65; this.turnMinRigDirectionChange = Math.PI / 6; } get maxDistance() { return Math.max(this.sphericalMaxDistance, this.similarMaxDistance, this.stepMaxDistance, this.turnMaxDistance); } } /** * @class MapillaryError * * @classdesc Generic Mapillary error. */ class MapillaryError extends Error { constructor(message) { super(message); Object.setPrototypeOf(this, MapillaryError.prototype); this.name = "MapillaryError"; } } class ArgumentMapillaryError extends MapillaryError { constructor(message) { super(message != null ? message : "The argument is not valid."); Object.setPrototypeOf(this, ArgumentMapillaryError.prototype); this.name = "ArgumentMapillaryError"; } } /** * @class Spatial * * @classdesc Provides methods for scalar, vector and matrix calculations. */ class Spatial { constructor() { this._epsilon = 1e-9; } /** * Converts azimuthal phi rotation (counter-clockwise with origin on X-axis) to * bearing (clockwise with origin at north or Y-axis). * * @param {number} phi - Azimuthal phi angle in radians. * @returns {number} Bearing in radians. */ azimuthalToBearing(phi) { return -phi + Math.PI / 2; } /** * Converts degrees to radians. * * @param {number} deg - Degrees. * @returns {number} Radians. */ degToRad(deg) { return Math.PI * deg / 180; } /** * Converts radians to degrees. * * @param {number} rad - Radians. * @returns {number} Degrees. */ radToDeg(rad) { return 180 * rad / Math.PI; } /** * Creates a rotation matrix from an angle-axis vector. * * @param {Array} angleAxis - Angle-axis representation of a rotation. * @returns {THREE.Matrix4} Rotation matrix. */ rotationMatrix(angleAxis) { let axis = new Vector3(angleAxis[0], angleAxis[1], angleAxis[2]); let angle = axis.length(); if (angle > 0) { axis.normalize(); } return new Matrix4().makeRotationAxis(axis, angle); } /** * Rotates a vector according to a angle-axis rotation vector. * * @param {Array} vector - Vector to rotate. * @param {Array} angleAxis - Angle-axis representation of a rotation. * @returns {THREE.Vector3} Rotated vector. */ rotate(vector, angleAxis) { let v = new Vector3(vector[0], vector[1], vector[2]); let rotationMatrix = this.rotationMatrix(angleAxis); v.applyMatrix4(rotationMatrix); return v; } /** * Calculates the optical center from a rotation vector * on the angle-axis representation and a translation vector * according to C = -R^T t. * * @param {Array} rotation - Angle-axis representation of a rotation. * @param {Array} translation - Translation vector. * @returns {THREE.Vector3} Optical center. */ opticalCenter(rotation, translation) { let angleAxis = [-rotation[0], -rotation[1], -rotation[2]]; let vector = [-translation[0], -translation[1], -translation[2]]; return this.rotate(vector, angleAxis); } /** * Calculates the viewing direction from a rotation vector * on the angle-axis representation. * * @param {number[]} rotation - Angle-axis representation of a rotation. * @returns {THREE.Vector3} Viewing direction. */ viewingDirection(rotation) { let angleAxis = [-rotation[0], -rotation[1], -rotation[2]]; return this.rotate([0, 0, 1], angleAxis); } /** * Wrap a number on the interval [min, max]. * * @param {number} value - Value to wrap. * @param {number} min - Lower endpoint of interval. * @param {number} max - Upper endpoint of interval. * @returns {number} The wrapped number. */ wrap(value, min, max) { if (max < min) { throw new Error("Invalid arguments: max must be larger than min."); } let interval = (max - min); while (value > max || value < min) { if (value > max) { value = value - interval; } else if (value < min) { value = value + interval; } } return value; } /** * Wrap an angle on the interval [-Pi, Pi]. * * @param {number} angle - Value to wrap. * @returns {number} Wrapped angle. */ wrapAngle(angle) { return this.wrap(angle, -Math.PI, Math.PI); } /** * Limit the value to the interval [min, max] by changing the value to * the nearest available one when it is outside the interval. * * @param {number} value - Value to clamp. * @param {number} min - Minimum of the interval. * @param {number} max - Maximum of the interval. * @returns {number} Clamped value. */ clamp(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } /** * Calculates the counter-clockwise angle from the first * vector (x1, y1)^T to the second (x2, y2)^T. * * @param {number} x1 - X coordinate of first vector. * @param {number} y1 - Y coordinate of first vector. * @param {number} x2 - X coordinate of second vector. * @param {number} y2 - Y coordinate of second vector. * @returns {number} Counter clockwise angle between the vectors. */ angleBetweenVector2(x1, y1, x2, y2) { let angle = Math.atan2(y2, x2) - Math.atan2(y1, x1); return this.wrapAngle(angle); } /** * Calculates the minimum (absolute) angle change for rotation * from one angle to another on the [-Pi, Pi] interval. * * @param {number} angle1 - Start angle. * @param {number} angle2 - Destination angle. * @returns {number} Absolute angle change between angles. */ angleDifference(angle1, angle2) { let angle = angle2 - angle1; return this.wrapAngle(angle); } /** * Calculates the relative rotation angle between two * angle-axis vectors. * * @param {number} rotation1 - First angle-axis vector. * @param {number} rotation2 - Second angle-axis vector. * @returns {number} Relative rotation angle. */ relativeRotationAngle(rotation1, rotation2) { let R1T = this.rotationMatrix([-rotation1[0], -rotation1[1], -rotation1[2]]); let R2 = this.rotationMatrix(rotation2); let R = R1T.multiply(R2); let elements = R.elements; // from Tr(R) = 1 + 2 * cos(theta) let tr = elements[0] + elements[5] + elements[10]; let theta = Math.acos(Math.max(Math.min((tr - 1) / 2, 1), -1)); return theta; } /** * Calculates the angle from a vector to a plane. * * @param {Array} vector - The vector. * @param {Array} planeNormal - Normal of the plane. * @returns {number} Angle from between plane and vector. */ angleToPlane(vector, planeNormal) { let v = new Vector3().fromArray(vector); let norm = v.length(); if (norm < this._epsilon) { return 0; } let projection = v.dot(new Vector3().fromArray(planeNormal)); return Math.asin(projection / norm); } azimuthal(direction, up) { const directionVector = new Vector3().fromArray(direction); const upVector = new Vector3().fromArray(up); const upProjection = directionVector.clone().dot(upVector); const planeProjection = directionVector.clone().sub(upVector.clone().multiplyScalar(upProjection)); return Math.atan2(planeProjection.y, planeProjection.x); } /** * Calculates the distance between two coordinates * (longitude, latitude pairs) in meters according to * the haversine formula. * * @param {number} lat1 - Latitude of the first coordinate in degrees. * @param {number} lng1 - Longitude of the first coordinate in degrees. * @param {number} lat2 - Latitude of the second coordinate in degrees. * @param {number} lng2 - Longitude of the second coordinate in degrees. * @returns {number} Distance between lat lon positions in meters. */ distanceFromLngLat(lng1, lat1, lng2, lat2) { let r = 6371000; let dLat = this.degToRad(lat2 - lat1); let dLng = this.degToRad(lng2 - lng1); let hav = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.degToRad(lat1)) * Math.cos(this.degToRad(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2); let d = 2 * r * Math.atan2(Math.sqrt(hav), Math.sqrt(1 - hav)); return d; } } const spatial = new Spatial(); function isSpherical(cameraType) { return cameraType === "spherical"; } function isFisheye(cameraType) { return cameraType === "fisheye"; } function computeTranslation(position, rotation, reference) { const C = geodeticToEnu(position.lng, position.lat, position.alt, reference.lng, reference.lat, reference.alt); const RC = spatial.rotate(C, rotation); const translation = [-RC.x, -RC.y, -RC.z]; return translation; } function computeProjectedPoints(transform, basicVertices, basicDirections, pointsPerLine, viewportCoords) { const basicPoints = []; for (let side = 0; side < basicVertices.length; ++side) { const v = basicVertices[side]; const d = basicDirections[side]; for (let i = 0; i <= pointsPerLine; ++i) { basicPoints.push([ v[0] + d[0] * i / pointsPerLine, v[1] + d[1] * i / pointsPerLine, ]); } } const camera = new Camera$2(); camera.up.copy(transform.upVector()); camera.position.copy(new Vector3().fromArray(transform.unprojectSfM([0, 0], 0))); camera.lookAt(new Vector3().fromArray(transform.unprojectSfM([0, 0], 10))); camera.updateMatrix(); camera.updateMatrixWorld(true); const projectedPoints = basicPoints .map((basicPoint) => { const worldPoint = transform.unprojectBasic(basicPoint, 10000); const cameraPoint = viewportCoords.worldToCamera(worldPoint, camera); return [ cameraPoint[0] / cameraPoint[2], cameraPoint[1] / cameraPoint[2], ]; }); return projectedPoints; } /** * @class EdgeCalculator * * @classdesc Represents a class for calculating node edges. */ class EdgeCalculator { /** * Create a new edge calculator instance. * * @param {EdgeCalculatorSettings} settings - Settings struct. * @param {EdgeCalculatorDirections} directions - Directions struct. * @param {EdgeCalculatorCoefficients} coefficients - Coefficients struct. */ constructor(settings, directions, coefficients) { this._spatial = new Spatial(); this._settings = settings != null ? settings : new EdgeCalculatorSettings(); this._directions = directions != null ? directions : new EdgeCalculatorDirections(); this._coefficients = coefficients != null ? coefficients : new EdgeCalculatorCoefficients(); } /** * Returns the potential edges to destination nodes for a set * of nodes with respect to a source node. * * @param {Image} node - Source node. * @param {Array} nodes - Potential destination nodes. * @param {Array} fallbackIds - Ids for destination nodes * that should be returned even if they do not meet the * criteria for a potential edge. * @throws {ArgumentMapillaryError} If node is not full. */ getPotentialEdges(node, potentialImages, fallbackIds) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (!node.merged) { return []; } let currentDirection = this._spatial.viewingDirection(node.rotation); let currentVerticalDirection = this._spatial.angleToPlane(currentDirection.toArray(), [0, 0, 1]); let potentialEdges = []; for (let potential of potentialImages) { if (!potential.merged || potential.id === node.id) { continue; } let enu = geodeticToEnu(potential.lngLat.lng, potential.lngLat.lat, potential.computedAltitude, node.lngLat.lng, node.lngLat.lat, node.computedAltitude); let motion = new Vector3(enu[0], enu[1], enu[2]); let distance = motion.length(); if (distance > this._settings.maxDistance && fallbackIds.indexOf(potential.id) < 0) { continue; } let motionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, motion.x, motion.y); let verticalMotion = this._spatial.angleToPlane(motion.toArray(), [0, 0, 1]); let direction = this._spatial.viewingDirection(potential.rotation); let directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); let verticalDirection = this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); let verticalDirectionChange = verticalDirection - currentVerticalDirection; let rotation = this._spatial.relativeRotationAngle(node.rotation, potential.rotation); let worldMotionAzimuth = this._spatial.angleBetweenVector2(1, 0, motion.x, motion.y); let sameSequence = potential.sequenceId != null && node.sequenceId != null && potential.sequenceId === node.sequenceId; let sameMergeCC = potential.mergeId === node.mergeId; let sameUser = potential.creatorId === node.creatorId; let potentialEdge = { capturedAt: potential.capturedAt, directionChange: directionChange, distance: distance, spherical: isSpherical(potential.cameraType), id: potential.id, motionChange: motionChange, rotation: rotation, sameMergeCC: sameMergeCC, sameSequence: sameSequence, sameUser: sameUser, sequenceId: potential.sequenceId, verticalDirectionChange: verticalDirectionChange, verticalMotion: verticalMotion, worldMotionAzimuth: worldMotionAzimuth, }; potentialEdges.push(potentialEdge); } return potentialEdges; } /** * Computes the sequence edges for a node. * * @param {Image} node - Source node. * @throws {ArgumentMapillaryError} If node is not full. */ computeSequenceEdges(node, sequence) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (node.sequenceId !== sequence.id) { throw new ArgumentMapillaryError("Image and sequence does not correspond."); } let edges = []; let nextId = sequence.findNext(node.id); if (nextId != null) { edges.push({ data: { direction: exports.NavigationDirection.Next, worldMotionAzimuth: Number.NaN, }, source: node.id, target: nextId, }); } let prevId = sequence.findPrev(node.id); if (prevId != null) { edges.push({ data: { direction: exports.NavigationDirection.Prev, worldMotionAzimuth: Number.NaN, }, source: node.id, target: prevId, }); } return edges; } /** * Computes the similar edges for a node. * * @description Similar edges for perspective images * look roughly in the same direction and are positioned closed to the node. * Similar edges for spherical only target other spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeSimilarEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let nodeSpherical = isSpherical(node.cameraType); let sequenceGroups = {}; for (let potentialEdge of potentialEdges) { if (potentialEdge.sequenceId == null) { continue; } if (potentialEdge.sameSequence) { continue; } if (nodeSpherical) { if (!potentialEdge.spherical) { continue; } } else { if (!potentialEdge.spherical && Math.abs(potentialEdge.directionChange) > this._settings.similarMaxDirectionChange) { continue; } } if (potentialEdge.distance > this._settings.similarMaxDistance) { continue; } if (potentialEdge.sameUser && Math.abs(potentialEdge.capturedAt - node.capturedAt) < this._settings.similarMinTimeDifference) { continue; } if (sequenceGroups[potentialEdge.sequenceId] == null) { sequenceGroups[potentialEdge.sequenceId] = []; } sequenceGroups[potentialEdge.sequenceId].push(potentialEdge); } let similarEdges = []; let calculateScore = isSpherical(node.cameraType) ? (potentialEdge) => { return potentialEdge.distance; } : (potentialEdge) => { return this._coefficients.similarDistance * potentialEdge.distance + this._coefficients.similarRotation * potentialEdge.rotation; }; for (let sequenceId in sequenceGroups) { if (!sequenceGroups.hasOwnProperty(sequenceId)) { continue; } let lowestScore = Number.MAX_VALUE; let similarEdge = null; for (let potentialEdge of sequenceGroups[sequenceId]) { let score = calculateScore(potentialEdge); if (score < lowestScore) { lowestScore = score; similarEdge = potentialEdge; } } if (similarEdge == null) { continue; } similarEdges.push(similarEdge); } return similarEdges .map((potentialEdge) => { return { data: { direction: exports.NavigationDirection.Similar, worldMotionAzimuth: potentialEdge.worldMotionAzimuth, }, source: node.id, target: potentialEdge.id, }; }); } /** * Computes the step edges for a perspective node. * * @description Step edge targets can only be other perspective nodes. * Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @param {string} prevId - Id of previous node in sequence. * @param {string} nextId - Id of next node in sequence. * @throws {ArgumentMapillaryError} If node is not full. */ computeStepEdges(node, potentialEdges, prevId, nextId) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let edges = []; if (isSpherical(node.cameraType)) { return edges; } for (let k in this._directions.steps) { if (!this._directions.steps.hasOwnProperty(k)) { continue; } let step = this._directions.steps[k]; let lowestScore = Number.MAX_VALUE; let edge = null; let fallback = null; for (let potential of potentialEdges) { if (potential.spherical) { continue; } if (Math.abs(potential.directionChange) > this._settings.stepMaxDirectionChange) { continue; } let motionDifference = this._spatial.angleDifference(step.motionChange, potential.motionChange); let directionMotionDifference = this._spatial.angleDifference(potential.directionChange, motionDifference); let drift = Math.max(Math.abs(motionDifference), Math.abs(directionMotionDifference)); if (Math.abs(drift) > this._settings.stepMaxDrift) { continue; } let potentialId = potential.id; if (step.useFallback && (potentialId === prevId || potentialId === nextId)) { fallback = potential; } if (potential.distance > this._settings.stepMaxDistance) { continue; } motionDifference = Math.sqrt(motionDifference * motionDifference + potential.verticalMotion * potential.verticalMotion); let score = this._coefficients.stepPreferredDistance * Math.abs(potential.distance - this._settings.stepPreferredDistance) / this._settings.stepMaxDistance + this._coefficients.stepMotion * motionDifference / this._settings.stepMaxDrift + this._coefficients.stepRotation * potential.rotation / this._settings.stepMaxDirectionChange + this._coefficients.stepSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.stepMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } edge = edge == null ? fallback : edge; if (edge != null) { edges.push({ data: { direction: step.direction, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } } return edges; } /** * Computes the turn edges for a perspective node. * * @description Turn edge targets can only be other perspective images. * Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeTurnEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } let edges = []; if (isSpherical(node.cameraType)) { return edges; } for (let k in this._directions.turns) { if (!this._directions.turns.hasOwnProperty(k)) { continue; } let turn = this._directions.turns[k]; let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialEdges) { if (potential.spherical) { continue; } if (potential.distance > this._settings.turnMaxDistance) { continue; } let rig = turn.direction !== exports.NavigationDirection.TurnU && potential.distance < this._settings.turnMaxRigDistance && Math.abs(potential.directionChange) > this._settings.turnMinRigDirectionChange; let directionDifference = this._spatial.angleDifference(turn.directionChange, potential.directionChange); let score; if (rig && potential.directionChange * turn.directionChange > 0 && Math.abs(potential.directionChange) < Math.abs(turn.directionChange)) { score = -Math.PI / 2 + Math.abs(potential.directionChange); } else { if (Math.abs(directionDifference) > this._settings.turnMaxDirectionChange) { continue; } let motionDifference = turn.motionChange ? this._spatial.angleDifference(turn.motionChange, potential.motionChange) : 0; motionDifference = Math.sqrt(motionDifference * motionDifference + potential.verticalMotion * potential.verticalMotion); score = this._coefficients.turnDistance * potential.distance / this._settings.turnMaxDistance + this._coefficients.turnMotion * motionDifference / Math.PI + this._coefficients.turnSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.turnMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); } if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { edges.push({ data: { direction: turn.direction, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } } return edges; } /** * Computes the spherical edges for a perspective node. * * @description Perspective to spherical edge targets can only be * spherical nodes. Returns an empty array for spherical. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computePerspectiveToSphericalEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (isSpherical(node.cameraType)) { return []; } let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialEdges) { if (!potential.spherical) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential.distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(potential.motionChange) / Math.PI + this._coefficients.sphericalMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge == null) { return []; } return [ { data: { direction: exports.NavigationDirection.Spherical, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }, ]; } /** * Computes the spherical and step edges for a spherical node. * * @description Spherical to spherical edge targets can only be * spherical nodes. spherical to step edge targets can only be perspective * nodes. * * @param {Image} node - Source node. * @param {Array} potentialEdges - Potential edges. * @throws {ArgumentMapillaryError} If node is not full. */ computeSphericalEdges(node, potentialEdges) { if (!node.complete) { throw new ArgumentMapillaryError("Image has to be full."); } if (!isSpherical(node.cameraType)) { return []; } let sphericalEdges = []; let potentialSpherical = []; let potentialSteps = []; for (let potential of potentialEdges) { if (potential.distance > this._settings.sphericalMaxDistance) { continue; } if (potential.spherical) { if (potential.distance < this._settings.sphericalMinDistance) { continue; } potentialSpherical.push(potential); } else { for (let k in this._directions.spherical) { if (!this._directions.spherical.hasOwnProperty(k)) { continue; } let spherical = this._directions.spherical[k]; let turn = this._spatial.angleDifference(potential.directionChange, potential.motionChange); let turnChange = this._spatial.angleDifference(spherical.directionChange, turn); if (Math.abs(turnChange) > this._settings.sphericalMaxStepTurnChange) { continue; } potentialSteps.push([spherical.direction, potential]); // break if step direction found break; } } } let maxRotationDifference = Math.PI / this._settings.sphericalMaxItems; let occupiedAngles = []; let stepAngles = []; for (let index = 0; index < this._settings.sphericalMaxItems; index++) { let rotation = index / this._settings.sphericalMaxItems * 2 * Math.PI; let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialSpherical) { let motionDifference = this._spatial.angleDifference(rotation, potential.motionChange); if (Math.abs(motionDifference) > maxRotationDifference) { continue; } let occupiedDifference = Number.MAX_VALUE; for (let occupiedAngle of occupiedAngles) { let difference = Math.abs(this._spatial.angleDifference(occupiedAngle, potential.motionChange)); if (difference < occupiedDifference) { occupiedDifference = difference; } } if (occupiedDifference <= maxRotationDifference) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential.distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(motionDifference) / maxRotationDifference + this._coefficients.sphericalSequencePenalty * (potential.sameSequence ? 0 : 1) + this._coefficients.sphericalMergeCCPenalty * (potential.sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { occupiedAngles.push(edge.motionChange); sphericalEdges.push({ data: { direction: exports.NavigationDirection.Spherical, worldMotionAzimuth: edge.worldMotionAzimuth, }, source: node.id, target: edge.id, }); } else { stepAngles.push(rotation); } } let occupiedStepAngles = {}; occupiedStepAngles[exports.NavigationDirection.Spherical] = occupiedAngles; occupiedStepAngles[exports.NavigationDirection.StepForward] = []; occupiedStepAngles[exports.NavigationDirection.StepLeft] = []; occupiedStepAngles[exports.NavigationDirection.StepBackward] = []; occupiedStepAngles[exports.NavigationDirection.StepRight] = []; for (let stepAngle of stepAngles) { let occupations = []; for (let k in this._directions.spherical) { if (!this._directions.spherical.hasOwnProperty(k)) { continue; } let spherical = this._directions.spherical[k]; let allOccupiedAngles = occupiedStepAngles[exports.NavigationDirection.Spherical] .concat(occupiedStepAngles[spherical.direction]) .concat(occupiedStepAngles[spherical.prev]) .concat(occupiedStepAngles[spherical.next]); let lowestScore = Number.MAX_VALUE; let edge = null; for (let potential of potentialSteps) { if (potential[0] !== spherical.direction) { continue; } let motionChange = this._spatial.angleDifference(stepAngle, potential[1].motionChange); if (Math.abs(motionChange) > maxRotationDifference) { continue; } let minOccupiedDifference = Number.MAX_VALUE; for (let occupiedAngle of allOccupiedAngles) { let occupiedDifference = Math.abs(this._spatial.angleDifference(occupiedAngle, potential[1].motionChange)); if (occupiedDifference < minOccupiedDifference) { minOccupiedDifference = occupiedDifference; } } if (minOccupiedDifference <= maxRotationDifference) { continue; } let score = this._coefficients.sphericalPreferredDistance * Math.abs(potential[1].distance - this._settings.sphericalPreferredDistance) / this._settings.sphericalMaxDistance + this._coefficients.sphericalMotion * Math.abs(motionChange) / maxRotationDifference + this._coefficients.sphericalMergeCCPenalty * (potential[1].sameMergeCC ? 0 : 1); if (score < lowestScore) { lowestScore = score; edge = potential; } } if (edge != null) { occupations.push(edge); sphericalEdges.push({ data: { direction: edge[0], worldMotionAzimuth: edge[1].worldMotionAzimuth, }, source: node.id, target: edge[1].id, }); } } for (let occupation of occupations) { occupiedStepAngles[occupation[0]].push(occupation[1].motionChange); } } return sphericalEdges; } } class GraphMapillaryError extends MapillaryError { constructor(message) { super(message); Object.setPrototypeOf(this, GraphMapillaryError.prototype); this.name = "GraphMapillaryError"; } } /** * @class Graph * * @classdesc Represents a graph of nodes with edges. */ class Graph { /** * Create a new graph instance. * * @param {APIWrapper} [api] - API instance for retrieving data. * @param {rbush.RBush} [nodeIndex] - Node index for fast spatial retreival. * @param {GraphCalculator} [graphCalculator] - Instance for graph calculations. * @param {EdgeCalculator} [edgeCalculator] - Instance for edge calculations. * @param {FilterCreator} [filterCreator] - Instance for filter creation. * @param {GraphConfiguration} [configuration] - Configuration struct. */ constructor(api, nodeIndex, graphCalculator, edgeCalculator, filterCreator, configuration) { this._api = api; this._cachedNodes = {}; this._cachedNodeTiles = {}; this._cachedSequenceNodes = {}; this._cachedSpatialEdges = {}; this._cachedTiles = {}; this._cachingFill$ = {}; this._cachingFull$ = {}; this._cachingSequenceNodes$ = {}; this._cachingSequences$ = {}; this._cachingSpatialArea$ = {}; this._cachingTiles$ = {}; this._changed$ = new Subject(); this._filterCreator = filterCreator !== null && filterCreator !== void 0 ? filterCreator : new FilterCreator(); this._filter = this._filterCreator.createFilter(undefined); this._filterSubject$ = new Subject(); this._filter$ = concat(of(this._filter), this._filterSubject$).pipe(publishReplay(1), refCount()); this._filterSubscription = this._filter$.subscribe(() => { }); this._defaultAlt = 2; this._edgeCalculator = edgeCalculator !== null && edgeCalculator !== void 0 ? edgeCalculator : new EdgeCalculator(); this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); this._configuration = configuration !== null && configuration !== void 0 ? configuration : { maxSequences: 50, maxUnusedImages: 100, maxUnusedPreStoredImages: 30, maxUnusedTiles: 20, }; this._nodes = {}; this._nodeIndex = nodeIndex !== null && nodeIndex !== void 0 ? nodeIndex : new Graph._spatialIndex(16); this._nodeIndexTiles = {}; this._nodeToTile = {}; this._preStored = {}; this._requiredNodeTiles = {}; this._requiredSpatialArea = {}; this._sequences = {}; this._tileThreshold = 20; } static register(spatialIndex) { Graph._spatialIndex = spatialIndex; } /** * Get api. * * @returns {APIWrapper} The API instance used by * the graph. */ get api() { return this._api; } /** * Get changed$. * * @returns {Observable} Observable emitting * the graph every time it has changed. */ get changed$() { return this._changed$; } /** * Get filter$. * * @returns {Observable} Observable emitting * the filter every time it has changed. */ get filter$() { return this._filter$; } /** * Caches the full node data for all images within a bounding * box. * * @description The node assets are not cached. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * @returns {Observable>} Observable emitting * the full nodes in the bounding box. */ cacheBoundingBox$(sw, ne) { const cacheTiles$ = this._api.data.geometry.bboxToCellIds(sw, ne) .filter((h) => { return !(h in this._cachedTiles); }) .map((h) => { return h in this._cachingTiles$ ? this._cachingTiles$[h] : this._cacheTile$(h); }); if (cacheTiles$.length === 0) { cacheTiles$.push(of(this)); } return from(cacheTiles$).pipe(mergeAll(), last(), mergeMap(() => { const nodes = this._nodeIndex .search({ maxX: ne.lng, maxY: ne.lat, minX: sw.lng, minY: sw.lat, }) .map((item) => { return item.node; }); const fullNodes = []; const coreNodes = []; for (const node of nodes) { if (node.complete) { fullNodes.push(node); } else { coreNodes.push(node.id); } } const coreNodeBatches = []; const batchSize = 200; while (coreNodes.length > 0) { coreNodeBatches.push(coreNodes.splice(0, batchSize)); } const fullNodes$ = of(fullNodes); const fillNodes$ = coreNodeBatches .map((batch) => { return this._api .getSpatialImages$(batch) .pipe(map((items) => { const result = []; for (const item of items) { const exists = this .hasNode(item.node_id); if (!exists) { continue; } const node = this .getNode(item.node_id); if (!node.complete) { this._makeFull(node, item.node); } result.push(node); } return result; })); }); return merge(fullNodes$, from(fillNodes$).pipe(mergeAll())); }), reduce((acc, value) => { return acc.concat(value); })); } /** * Caches the full node data for all images of a cell. * * @description The node assets are not cached. * * @param {string} cellId - Cell id. * @returns {Observable>} Observable * emitting the full nodes of the cell. */ cacheCell$(cellId) { const cacheCell$ = cellId in this._cachedTiles ? of(this) : cellId in this._cachingTiles$ ? this._cachingTiles$[cellId] : this._cacheTile$(cellId); return cacheCell$.pipe(mergeMap(() => { const cachedCell = this._cachedTiles[cellId]; cachedCell.accessed = new Date().getTime(); const cellNodes = cachedCell.nodes; const fullNodes = []; const coreNodes = []; for (const node of cellNodes) { if (node.complete) { fullNodes.push(node); } else { coreNodes.push(node.id); } } const coreNodeBatches = []; const batchSize = 200; while (coreNodes.length > 0) { coreNodeBatches.push(coreNodes.splice(0, batchSize)); } const fullNodes$ = of(fullNodes); const fillNodes$ = coreNodeBatches .map((batch) => { return this._api.getSpatialImages$(batch).pipe(map((items) => { const filled = []; for (const item of items) { if (!item.node) { console.warn(`Image is empty (${item.node})`); continue; } const id = item.node_id; if (!this.hasNode(id)) { continue; } const node = this.getNode(id); if (!node.complete) { this._makeFull(node, item.node); } filled.push(node); } return filled; })); }); return merge(fullNodes$, from(fillNodes$).pipe(mergeAll())); }), reduce((acc, value) => { return acc.concat(value); })); } /** * Retrieve and cache node fill properties. * * @param {string} key - Key of node to fill. * @returns {Observable} Observable emitting the graph * when the node has been updated. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheFill$(key) { if (key in this._cachingFull$) { throw new GraphMapillaryError(`Cannot fill node while caching full (${key}).`); } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot fill node that does not exist in graph (${key}).`); } if (key in this._cachingFill$) { return this._cachingFill$[key]; } const node = this.getNode(key); if (node.complete) { throw new GraphMapillaryError(`Cannot fill node that is already full (${key}).`); } this._cachingFill$[key] = this._api.getSpatialImages$([key]).pipe(tap((items) => { for (const item of items) { if (!item.node) { console.warn(`Image is empty ${item.node_id}`); } if (!node.complete) { this._makeFull(node, item.node); } delete this._cachingFill$[item.node_id]; } }), map(() => { return this; }), finalize(() => { if (key in this._cachingFill$) { delete this._cachingFill$[key]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingFill$[key]; } /** * Retrieve and cache full node properties. * * @param {string} key - Key of node to fill. * @returns {Observable} Observable emitting the graph * when the node has been updated. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheFull$(key) { if (key in this._cachingFull$) { return this._cachingFull$[key]; } if (this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache full node that already exist in graph (${key}).`); } this._cachingFull$[key] = this._api.getImages$([key]).pipe(tap((items) => { for (const item of items) { if (!item.node) { throw new GraphMapillaryError(`Image does not exist (${key}, ${item.node}).`); } const id = item.node_id; if (this.hasNode(id)) { const node = this.getNode(key); if (!node.complete) { this._makeFull(node, item.node); } } else { if (item.node.sequence.id == null) { throw new GraphMapillaryError(`Image has no sequence key (${key}).`); } const node = new Image$1(item.node); this._makeFull(node, item.node); const cellId = this._api.data.geometry .lngLatToCellId(node.originalLngLat); this._preStore(cellId, node); this._setNode(node); delete this._cachingFull$[id]; } } }), map(() => this), finalize(() => { if (key in this._cachingFull$) { delete this._cachingFull$[key]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingFull$[key]; } /** * Retrieve and cache a node sequence. * * @param {string} key - Key of node for which to retrieve sequence. * @returns {Observable} Observable emitting the graph * when the sequence has been retrieved. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheNodeSequence$(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache sequence edges of node that does not exist in graph (${key}).`); } let node = this.getNode(key); if (node.sequenceId in this._sequences) { throw new GraphMapillaryError(`Sequence already cached (${key}), (${node.sequenceId}).`); } return this._cacheSequence$(node.sequenceId); } /** * Retrieve and cache a sequence. * * @param {string} sequenceKey - Key of sequence to cache. * @returns {Observable} Observable emitting the graph * when the sequence has been retrieved. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSequence$(sequenceKey) { if (sequenceKey in this._sequences) { throw new GraphMapillaryError(`Sequence already cached (${sequenceKey})`); } return this._cacheSequence$(sequenceKey); } /** * Cache sequence edges for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSequenceEdges(key) { let node = this.getNode(key); if (!(node.sequenceId in this._sequences)) { throw new GraphMapillaryError(`Sequence is not cached (${key}), (${node.sequenceId})`); } let sequence = this._sequences[node.sequenceId].sequence; let edges = this._edgeCalculator.computeSequenceEdges(node, sequence); node.cacheSequenceEdges(edges); } /** * Retrieve and cache full nodes for all keys in a sequence. * * @param {string} sequenceKey - Key of sequence. * @param {string} referenceNodeKey - Key of node to use as reference * for optimized caching. * @returns {Observable} Observable emitting the graph * when the nodes of the sequence has been cached. */ cacheSequenceNodes$(sequenceKey, referenceNodeKey) { if (!this.hasSequence(sequenceKey)) { throw new GraphMapillaryError(`Cannot cache sequence nodes of sequence that does not exist in graph (${sequenceKey}).`); } if (this.hasSequenceNodes(sequenceKey)) { throw new GraphMapillaryError(`Sequence nodes already cached (${sequenceKey}).`); } const sequence = this.getSequence(sequenceKey); if (sequence.id in this._cachingSequenceNodes$) { return this._cachingSequenceNodes$[sequence.id]; } const batches = []; const keys = sequence.imageIds.slice(); const referenceBatchSize = 50; if (!!referenceNodeKey && keys.length > referenceBatchSize) { const referenceIndex = keys.indexOf(referenceNodeKey); const startIndex = Math.max(0, Math.min(referenceIndex - referenceBatchSize / 2, keys.length - referenceBatchSize)); batches.push(keys.splice(startIndex, referenceBatchSize)); } const batchSize = 200; while (keys.length > 0) { batches.push(keys.splice(0, batchSize)); } let batchesToCache = batches.length; const sequenceNodes$ = from(batches).pipe(mergeMap((batch) => { return this._api.getImages$(batch).pipe(tap((items) => { for (const item of items) { if (!item.node) { console.warn(`Image empty (${item.node_id})`); continue; } const id = item.node_id; if (this.hasNode(id)) { const node = this.getNode(id); if (!node.complete) { this._makeFull(node, item.node); } } else { if (item.node.sequence.id == null) { console.warn(`Sequence missing, discarding node (${item.node_id})`); } const node = new Image$1(item.node); this._makeFull(node, item.node); const cellId = this._api.data.geometry .lngLatToCellId(node.originalLngLat); this._preStore(cellId, node); this._setNode(node); } } batchesToCache--; }), map(() => this)); }, 6), last(), finalize(() => { delete this._cachingSequenceNodes$[sequence.id]; if (batchesToCache === 0) { this._cachedSequenceNodes[sequence.id] = true; } }), publish(), refCount()); this._cachingSequenceNodes$[sequence.id] = sequenceNodes$; return sequenceNodes$; } /** * Retrieve and cache full nodes for a node spatial area. * * @param {string} key - Key of node for which to retrieve sequence. * @returns {Observable} Observable emitting the graph * when the nodes in the spatial area has been made full. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSpatialArea$(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache spatial area of node that does not exist in graph (${key}).`); } if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Image already spatially cached (${key}).`); } if (!(key in this._requiredSpatialArea)) { throw new GraphMapillaryError(`Spatial area not determined (${key}).`); } let spatialArea = this._requiredSpatialArea[key]; if (Object.keys(spatialArea.cacheNodes).length === 0) { throw new GraphMapillaryError(`Spatial nodes already cached (${key}).`); } if (key in this._cachingSpatialArea$) { return this._cachingSpatialArea$[key]; } let batches = []; while (spatialArea.cacheKeys.length > 0) { batches.push(spatialArea.cacheKeys.splice(0, 200)); } let batchesToCache = batches.length; let spatialNodes$ = []; for (let batch of batches) { let spatialNodeBatch$ = this._api.getSpatialImages$(batch).pipe(tap((items) => { for (const item of items) { if (!item.node) { console.warn(`Image is empty (${item.node_id})`); continue; } const id = item.node_id; const spatialNode = spatialArea.cacheNodes[id]; if (spatialNode.complete) { delete spatialArea.cacheNodes[id]; continue; } this._makeFull(spatialNode, item.node); delete spatialArea.cacheNodes[id]; } if (--batchesToCache === 0) { delete this._cachingSpatialArea$[key]; } }), map(() => { return this; }), catchError((error) => { for (let batchKey of batch) { if (batchKey in spatialArea.all) { delete spatialArea.all[batchKey]; } if (batchKey in spatialArea.cacheNodes) { delete spatialArea.cacheNodes[batchKey]; } } if (--batchesToCache === 0) { delete this._cachingSpatialArea$[key]; } throw error; }), finalize(() => { if (Object.keys(spatialArea.cacheNodes).length === 0) { this._changed$.next(this); } }), publish(), refCount()); spatialNodes$.push(spatialNodeBatch$); } this._cachingSpatialArea$[key] = spatialNodes$; return spatialNodes$; } /** * Cache spatial edges for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheSpatialEdges(key) { if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Spatial edges already cached (${key}).`); } let node = this.getNode(key); let sequence = this._sequences[node.sequenceId].sequence; let fallbackKeys = []; let prevKey = sequence.findPrev(node.id); if (prevKey != null) { fallbackKeys.push(prevKey); } let nextKey = sequence.findNext(node.id); if (nextKey != null) { fallbackKeys.push(nextKey); } let allSpatialNodes = this._requiredSpatialArea[key].all; let potentialNodes = []; let filter = this._filter; for (let spatialNodeKey in allSpatialNodes) { if (!allSpatialNodes.hasOwnProperty(spatialNodeKey)) { continue; } let spatialNode = allSpatialNodes[spatialNodeKey]; if (filter(spatialNode)) { potentialNodes.push(spatialNode); } } let potentialEdges = this._edgeCalculator.getPotentialEdges(node, potentialNodes, fallbackKeys); let edges = this._edgeCalculator.computeStepEdges(node, potentialEdges, prevKey, nextKey); edges = edges.concat(this._edgeCalculator.computeTurnEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computeSphericalEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computePerspectiveToSphericalEdges(node, potentialEdges)); edges = edges.concat(this._edgeCalculator.computeSimilarEdges(node, potentialEdges)); node.cacheSpatialEdges(edges); this._cachedSpatialEdges[key] = node; delete this._requiredSpatialArea[key]; delete this._cachedNodeTiles[key]; } /** * Retrieve and cache tiles for a node. * * @param {string} key - Key of node for which to retrieve tiles. * @returns {Array>} Array of observables emitting * the graph for each tile required for the node has been cached. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ cacheTiles$(key) { if (key in this._cachedNodeTiles) { throw new GraphMapillaryError(`Tiles already cached (${key}).`); } if (key in this._cachedSpatialEdges) { throw new GraphMapillaryError(`Spatial edges already cached so tiles considered cached (${key}).`); } if (!(key in this._requiredNodeTiles)) { throw new GraphMapillaryError(`Tiles have not been determined (${key}).`); } let nodeTiles = this._requiredNodeTiles[key]; if (nodeTiles.cache.length === 0 && nodeTiles.caching.length === 0) { throw new GraphMapillaryError(`Tiles already cached (${key}).`); } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Cannot cache tiles of node that does not exist in graph (${key}).`); } let hs = nodeTiles.cache.slice(); nodeTiles.caching = this._requiredNodeTiles[key].caching.concat(hs); nodeTiles.cache = []; let cacheTiles$ = []; for (let h of nodeTiles.caching) { const cacheTile$ = h in this._cachingTiles$ ? this._cachingTiles$[h] : this._cacheTile$(h); cacheTiles$.push(cacheTile$.pipe(tap((graph) => { let index = nodeTiles.caching.indexOf(h); if (index > -1) { nodeTiles.caching.splice(index, 1); } if (nodeTiles.caching.length === 0 && nodeTiles.cache.length === 0) { delete this._requiredNodeTiles[key]; this._cachedNodeTiles[key] = true; } }), catchError((error) => { let index = nodeTiles.caching.indexOf(h); if (index > -1) { nodeTiles.caching.splice(index, 1); } if (nodeTiles.caching.length === 0 && nodeTiles.cache.length === 0) { delete this._requiredNodeTiles[key]; this._cachedNodeTiles[key] = true; } throw error; }), finalize(() => { this._changed$.next(this); }), publish(), refCount())); } return cacheTiles$; } /** * Initialize the cache for a node. * * @param {string} key - Key of node. * @throws {GraphMapillaryError} When the operation is not valid on the * current graph. */ initializeCache(key) { if (key in this._cachedNodes) { throw new GraphMapillaryError(`Image already in cache (${key}).`); } const node = this.getNode(key); const provider = this._api.data; node.initializeCache(new ImageCache(provider)); const accessed = new Date().getTime(); this._cachedNodes[key] = { accessed: accessed, node: node }; this._updateCachedTileAccess(key, accessed); } /** * Get a value indicating if the graph is fill caching a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the node is being fill cached. */ isCachingFill(key) { return key in this._cachingFill$; } /** * Get a value indicating if the graph is fully caching a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the node is being fully cached. */ isCachingFull(key) { return key in this._cachingFull$; } /** * Get a value indicating if the graph is caching a sequence of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the sequence of a node is * being cached. */ isCachingNodeSequence(key) { let node = this.getNode(key); return node.sequenceId in this._cachingSequences$; } /** * Get a value indicating if the graph is caching a sequence. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if the sequence is * being cached. */ isCachingSequence(sequenceKey) { return sequenceKey in this._cachingSequences$; } /** * Get a value indicating if the graph is caching sequence nodes. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if the sequence nodes are * being cached. */ isCachingSequenceNodes(sequenceKey) { return sequenceKey in this._cachingSequenceNodes$; } /** * Get a value indicating if the graph is caching the tiles * required for calculating spatial edges of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the tiles of * a node are being cached. */ isCachingTiles(key) { return key in this._requiredNodeTiles && this._requiredNodeTiles[key].cache.length === 0 && this._requiredNodeTiles[key].caching.length > 0; } /** * Get a value indicating if the cache has been initialized * for a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the cache has been * initialized for a node. */ hasInitializedCache(key) { return key in this._cachedNodes; } /** * Get a value indicating if a node exist in the graph. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if a node exist in the graph. */ hasNode(key) { let accessed = new Date().getTime(); this._updateCachedNodeAccess(key, accessed); this._updateCachedTileAccess(key, accessed); return key in this._nodes; } /** * Get a value indicating if a node sequence exist in the graph. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if a node sequence exist * in the graph. */ hasNodeSequence(key) { let node = this.getNode(key); let sequenceKey = node.sequenceId; let hasNodeSequence = sequenceKey in this._sequences; if (hasNodeSequence) { this._sequences[sequenceKey].accessed = new Date().getTime(); } return hasNodeSequence; } /** * Get a value indicating if a sequence exist in the graph. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if a sequence exist * in the graph. */ hasSequence(sequenceKey) { let hasSequence = sequenceKey in this._sequences; if (hasSequence) { this._sequences[sequenceKey].accessed = new Date().getTime(); } return hasSequence; } /** * Get a value indicating if sequence nodes has been cached in the graph. * * @param {string} sequenceKey - Key of sequence. * @returns {boolean} Value indicating if a sequence nodes has been * cached in the graph. */ hasSequenceNodes(sequenceKey) { return sequenceKey in this._cachedSequenceNodes; } /** * Get a value indicating if the graph has fully cached * all nodes in the spatial area of a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the spatial area * of a node has been cached. */ hasSpatialArea(key) { if (!this.hasNode(key)) { throw new GraphMapillaryError(`Spatial area nodes cannot be determined if node not in graph (${key}).`); } if (key in this._cachedSpatialEdges) { return true; } if (key in this._requiredSpatialArea) { return Object .keys(this._requiredSpatialArea[key].cacheNodes) .length === 0; } let node = this.getNode(key); let bbox = this._graphCalculator .boundingBoxCorners(node.lngLat, this._tileThreshold); let spatialItems = this._nodeIndex .search({ maxX: bbox[1].lng, maxY: bbox[1].lat, minX: bbox[0].lng, minY: bbox[0].lat, }); let spatialNodes = { all: {}, cacheKeys: [], cacheNodes: {}, }; for (let spatialItem of spatialItems) { spatialNodes.all[spatialItem.node.id] = spatialItem.node; if (!spatialItem.node.complete) { spatialNodes.cacheKeys.push(spatialItem.node.id); spatialNodes.cacheNodes[spatialItem.node.id] = spatialItem.node; } } this._requiredSpatialArea[key] = spatialNodes; return spatialNodes.cacheKeys.length === 0; } /** * Get a value indicating if the graph has a tiles required * for a node. * * @param {string} key - Key of node. * @returns {boolean} Value indicating if the the tiles required * by a node has been cached. */ hasTiles(key) { if (key in this._cachedNodeTiles) { return true; } if (key in this._cachedSpatialEdges) { return true; } if (!this.hasNode(key)) { throw new GraphMapillaryError(`Image does not exist in graph (${key}).`); } let nodeTiles = { cache: [], caching: [] }; if (!(key in this._requiredNodeTiles)) { const node = this.getNode(key); const [sw, ne] = this._graphCalculator .boundingBoxCorners(node.lngLat, this._tileThreshold); nodeTiles.cache = this._api.data.geometry .bboxToCellIds(sw, ne) .filter((h) => { return !(h in this._cachedTiles); }); if (nodeTiles.cache.length > 0) { this._requiredNodeTiles[key] = nodeTiles; } } else { nodeTiles = this._requiredNodeTiles[key]; } return nodeTiles.cache.length === 0 && nodeTiles.caching.length === 0; } /** * Get a node. * * @param {string} key - Key of node. * @returns {Image} Retrieved node. */ getNode(key) { let accessed = new Date().getTime(); this._updateCachedNodeAccess(key, accessed); this._updateCachedTileAccess(key, accessed); return this._nodes[key]; } /** * Get a sequence. * * @param {string} sequenceKey - Key of sequence. * @returns {Image} Retrieved sequence. */ getSequence(sequenceKey) { let sequenceAccess = this._sequences[sequenceKey]; sequenceAccess.accessed = new Date().getTime(); return sequenceAccess.sequence; } /** * Reset all spatial edges of the graph nodes. */ resetSpatialEdges() { let cachedKeys = Object.keys(this._cachedSpatialEdges); for (let cachedKey of cachedKeys) { let node = this._cachedSpatialEdges[cachedKey]; node.resetSpatialEdges(); delete this._cachedSpatialEdges[cachedKey]; } } /** * Reset the complete graph but keep the nodes corresponding * to the supplied keys. All other nodes will be disposed. * * @param {Array} keepKeys - Keys for nodes to keep * in graph after reset. */ reset(keepKeys) { const nodes = []; for (const key of keepKeys) { if (!this.hasNode(key)) { throw new Error(`Image does not exist ${key}`); } const node = this.getNode(key); node.resetSequenceEdges(); node.resetSpatialEdges(); nodes.push(node); } for (let cachedKey of Object.keys(this._cachedNodes)) { if (keepKeys.indexOf(cachedKey) !== -1) { continue; } this._cachedNodes[cachedKey].node.dispose(); delete this._cachedNodes[cachedKey]; } this._cachedNodeTiles = {}; this._cachedSpatialEdges = {}; this._cachedTiles = {}; this._cachingFill$ = {}; this._cachingFull$ = {}; this._cachingSequences$ = {}; this._cachingSpatialArea$ = {}; this._cachingTiles$ = {}; this._nodes = {}; this._nodeToTile = {}; this._preStored = {}; for (const node of nodes) { this._nodes[node.id] = node; const h = this._api.data.geometry.lngLatToCellId(node.originalLngLat); this._preStore(h, node); } this._requiredNodeTiles = {}; this._requiredSpatialArea = {}; this._sequences = {}; this._nodeIndexTiles = {}; this._nodeIndex.clear(); } /** * Set the spatial node filter. * * @emits FilterFunction The filter function to the {@link Graph.filter$} * observable. * * @param {FilterExpression} filter - Filter expression to be applied * when calculating spatial edges. */ setFilter(filter) { this._filter = this._filterCreator.createFilter(filter); this._filterSubject$.next(this._filter); } /** * Uncache the graph according to the graph configuration. * * @description Uncaches unused tiles, unused nodes and * sequences according to the numbers specified in the * graph configuration. Sequences does not have a direct * reference to either tiles or nodes and may be uncached * even if they are related to the nodes that should be kept. * * @param {Array} keepIds - Ids of nodes to keep in * graph unrelated to last access. Tiles related to those keys * will also be kept in graph. * @param {Array} keepCellIds - Ids of cells to keep in * graph unrelated to last access. The nodes of the cells may * still be uncached if not specified in the keep ids param * but are guaranteed to not be disposed. * @param {string} keepSequenceId - Optional id of sequence * for which the belonging nodes should not be disposed or * removed from the graph. These nodes may still be uncached if * not specified in keep ids param but are guaranteed to not * be disposed. */ uncache(keepIds, keepCellIds, keepSequenceId) { const idsInUse = {}; this._addNewKeys(idsInUse, this._cachingFull$); this._addNewKeys(idsInUse, this._cachingFill$); this._addNewKeys(idsInUse, this._cachingSpatialArea$); this._addNewKeys(idsInUse, this._requiredNodeTiles); this._addNewKeys(idsInUse, this._requiredSpatialArea); for (const key of keepIds) { if (key in idsInUse) { continue; } idsInUse[key] = true; } const geometry = this._api.data.geometry; const keepCells = new Set(keepCellIds); const potentialCells = []; for (const cellId in this._cachedTiles) { if (!this._cachedTiles.hasOwnProperty(cellId) || keepCells.has(cellId)) { continue; } potentialCells.push([cellId, this._cachedTiles[cellId]]); } const sortedPotentialCells = potentialCells .sort((h1, h2) => { return h2[1].accessed - h1[1].accessed; }); const keepPotentialCells = sortedPotentialCells .slice(0, this._configuration.maxUnusedTiles) .map((h) => { return h[0]; }); for (const potentialCell of keepPotentialCells) { keepCells.add(potentialCell); } for (const id in idsInUse) { if (!idsInUse.hasOwnProperty(id)) { continue; } const node = this._nodes[id]; const nodeCellId = geometry.lngLatToCellId(node.lngLat); if (!keepCells.has(nodeCellId)) { if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._nodeToTile) { delete this._nodeToTile[id]; } if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[id]; } if (node.hasInitializedCache()) { node.resetSpatialEdges(); } if (nodeCellId in this._cachedTiles) { const index = this._cachedTiles[nodeCellId].nodes .findIndex(n => n.id === id); if (index !== -1) { this._cachedTiles[nodeCellId].nodes.splice(index, 1); } } this._preStore(nodeCellId, node); } } const uncacheCells = sortedPotentialCells .slice(this._configuration.maxUnusedTiles) .map((h) => { return h[0]; }); for (let uncacheCell of uncacheCells) { this._uncacheTile(uncacheCell, keepSequenceId); } const potentialPreStored = []; const nonCachedPreStored = []; for (let cellId in this._preStored) { if (!this._preStored.hasOwnProperty(cellId) || cellId in this._cachingTiles$) { continue; } const prestoredNodes = this._preStored[cellId]; for (let id in prestoredNodes) { if (!prestoredNodes.hasOwnProperty(id) || id in idsInUse) { continue; } if (prestoredNodes[id].sequenceId === keepSequenceId) { continue; } if (id in this._cachedNodes) { potentialPreStored.push([this._cachedNodes[id], cellId]); } else { nonCachedPreStored.push([id, cellId]); } } } const uncachePreStored = potentialPreStored .sort(([na1], [na2]) => { return na2.accessed - na1.accessed; }) .slice(this._configuration.maxUnusedPreStoredImages) .map(([na, h]) => { return [na.node.id, h]; }); this._uncachePreStored(nonCachedPreStored); this._uncachePreStored(uncachePreStored); const potentialNodes = []; for (let id in this._cachedNodes) { if (!this._cachedNodes.hasOwnProperty(id) || id in idsInUse) { continue; } potentialNodes.push(this._cachedNodes[id]); } const uncacheNodes = potentialNodes .sort((n1, n2) => { return n2.accessed - n1.accessed; }) .slice(this._configuration.maxUnusedImages); for (const nodeAccess of uncacheNodes) { nodeAccess.node.uncache(); const id = nodeAccess.node.id; delete this._cachedNodes[id]; if (id in this._cachedNodeTiles) { delete this._cachedNodeTiles[id]; } if (id in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[id]; } } const potentialSequences = []; for (let sequenceId in this._sequences) { if (!this._sequences.hasOwnProperty(sequenceId) || sequenceId in this._cachingSequences$ || sequenceId === keepSequenceId) { continue; } potentialSequences.push(this._sequences[sequenceId]); } const uncacheSequences = potentialSequences .sort((s1, s2) => { return s2.accessed - s1.accessed; }) .slice(this._configuration.maxSequences); for (const sequenceAccess of uncacheSequences) { const sequenceId = sequenceAccess.sequence.id; delete this._sequences[sequenceId]; if (sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[sequenceId]; } sequenceAccess.sequence.dispose(); } } /** * Updates existing cells with new core nodes. * * @description Non-existing cells are discarded * and not requested at all. * * Existing nodes are not changed. * * New nodes are not made full or getting assets * cached. * * @param {Array} cellIds - Cell ids. * @returns {Observable>} Observable * emitting the updated cells. */ updateCells$(cellIds) { const cachedCells = this._cachedTiles; const cachingCells = this._cachingTiles$; return from(cellIds) .pipe(mergeMap((cellId) => { if (cellId in cachedCells) { return this._updateCell$(cellId); } if (cellId in cachingCells) { return cachingCells[cellId] .pipe(catchError(() => { return of(this); }), mergeMap(() => this._updateCell$(cellId))); } return empty(); })); } /** * Unsubscribes all subscriptions. * * @description Afterwards, you must not call any other methods * on the graph instance. */ unsubscribe() { this._filterSubscription.unsubscribe(); } _addNewKeys(keys, dict) { for (let key in dict) { if (!dict.hasOwnProperty(key) || !this.hasNode(key)) { continue; } if (!(key in keys)) { keys[key] = true; } } } _cacheSequence$(sequenceId) { if (sequenceId in this._cachingSequences$) { return this._cachingSequences$[sequenceId]; } this._cachingSequences$[sequenceId] = this._api .getSequence$(sequenceId) .pipe(tap((sequence) => { if (!sequence) { console.warn(`Sequence does not exist ` + `(${sequenceId})`); } else { if (!(sequence.id in this._sequences)) { this._sequences[sequence.id] = { accessed: new Date().getTime(), sequence: new Sequence(sequence), }; } delete this._cachingSequences$[sequenceId]; } }), map(() => { return this; }), finalize(() => { if (sequenceId in this._cachingSequences$) { delete this._cachingSequences$[sequenceId]; } this._changed$.next(this); }), publish(), refCount()); return this._cachingSequences$[sequenceId]; } _cacheTile$(cellId) { this._cachingTiles$[cellId] = this._api .getCoreImages$(cellId) .pipe(tap((contract) => { if (cellId in this._cachedTiles) { return; } const cores = contract.images; this._nodeIndexTiles[cellId] = []; this._cachedTiles[cellId] = { accessed: new Date().getTime(), nodes: [], }; const hCache = this._cachedTiles[cellId].nodes; const preStored = this._removeFromPreStore(cellId); for (const core of cores) { if (!core) { break; } if (core.sequence.id == null) { console.warn(`Sequence missing, discarding ` + `node (${core.id})`); continue; } if (preStored != null && core.id in preStored) { const preStoredNode = preStored[core.id]; delete preStored[core.id]; hCache.push(preStoredNode); const preStoredNodeIndexItem = { lat: preStoredNode.lngLat.lat, lng: preStoredNode.lngLat.lng, node: preStoredNode, }; this._nodeIndex.insert(preStoredNodeIndexItem); this._nodeIndexTiles[cellId] .push(preStoredNodeIndexItem); this._nodeToTile[preStoredNode.id] = cellId; continue; } const node = new Image$1(core); hCache.push(node); const nodeIndexItem = { lat: node.lngLat.lat, lng: node.lngLat.lng, node: node, }; this._nodeIndex.insert(nodeIndexItem); this._nodeIndexTiles[cellId].push(nodeIndexItem); this._nodeToTile[node.id] = cellId; this._setNode(node); } delete this._cachingTiles$[cellId]; }), map(() => this), catchError((error) => { delete this._cachingTiles$[cellId]; throw error; }), publish(), refCount()); return this._cachingTiles$[cellId]; } _makeFull(node, fillNode) { if (fillNode.computed_altitude == null) { fillNode.computed_altitude = this._defaultAlt; } if (fillNode.computed_rotation == null) { fillNode.computed_rotation = this._graphCalculator.rotationFromCompass(fillNode.compass_angle, fillNode.exif_orientation); } node.makeComplete(fillNode); } _preStore(h, node) { if (!(h in this._preStored)) { this._preStored[h] = {}; } this._preStored[h][node.id] = node; } _removeFromPreStore(h) { let preStored = null; if (h in this._preStored) { preStored = this._preStored[h]; delete this._preStored[h]; } return preStored; } _setNode(node) { let key = node.id; if (this.hasNode(key)) { throw new GraphMapillaryError(`Image already exist (${key}).`); } this._nodes[key] = node; } _uncacheTile(h, keepSequenceKey) { for (let node of this._cachedTiles[h].nodes) { let key = node.id; delete this._nodeToTile[key]; if (key in this._cachedNodes) { delete this._cachedNodes[key]; } if (key in this._cachedNodeTiles) { delete this._cachedNodeTiles[key]; } if (key in this._cachedSpatialEdges) { delete this._cachedSpatialEdges[key]; } if (node.sequenceId === keepSequenceKey) { this._preStore(h, node); node.uncache(); } else { delete this._nodes[key]; if (node.sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[node.sequenceId]; } node.dispose(); } } for (let nodeIndexItem of this._nodeIndexTiles[h]) { this._nodeIndex.remove(nodeIndexItem); } delete this._nodeIndexTiles[h]; delete this._cachedTiles[h]; } _uncachePreStored(preStored) { let hs = {}; for (let [key, h] of preStored) { if (key in this._nodes) { delete this._nodes[key]; } if (key in this._cachedNodes) { delete this._cachedNodes[key]; } let node = this._preStored[h][key]; if (node.sequenceId in this._cachedSequenceNodes) { delete this._cachedSequenceNodes[node.sequenceId]; } delete this._preStored[h][key]; node.dispose(); hs[h] = true; } for (let h in hs) { if (!hs.hasOwnProperty(h)) { continue; } if (Object.keys(this._preStored[h]).length === 0) { delete this._preStored[h]; } } } _updateCachedTileAccess(key, accessed) { if (key in this._nodeToTile) { this._cachedTiles[this._nodeToTile[key]].accessed = accessed; } } _updateCachedNodeAccess(key, accessed) { if (key in this._cachedNodes) { this._cachedNodes[key].accessed = accessed; } } _updateCell$(cellId) { return this._api.getCoreImages$(cellId).pipe(mergeMap((contract) => { if (!(cellId in this._cachedTiles)) { return empty(); } const nodeIndex = this._nodeIndex; const nodeIndexCell = this._nodeIndexTiles[cellId]; const nodeToCell = this._nodeToTile; const cell = this._cachedTiles[cellId]; cell.accessed = new Date().getTime(); const cellNodes = cell.nodes; const cores = contract.images; for (const core of cores) { if (core == null) { break; } if (this.hasNode(core.id)) { continue; } if (core.sequence.id == null) { console.warn(`Sequence missing, discarding ` + `node (${core.id})`); continue; } const node = new Image$1(core); cellNodes.push(node); const nodeIndexItem = { lat: node.lngLat.lat, lng: node.lngLat.lng, node: node, }; nodeIndex.insert(nodeIndexItem); nodeIndexCell.push(nodeIndexItem); nodeToCell[node.id] = cellId; this._setNode(node); } return of(cellId); }), catchError((error) => { console.error(error); return empty(); })); } } class MarkerSet { constructor() { this._hash = {}; this._index = new MarkerSet._spatialIndex(16); this._indexChanged$ = new Subject(); this._updated$ = new Subject(); } static register(spatialIndex) { MarkerSet._spatialIndex = spatialIndex; } get changed$() { return this._indexChanged$; } get updated$() { return this._updated$; } add(markers) { const updated = []; const hash = this._hash; const index = this._index; for (const marker of markers) { const id = marker.id; if (id in hash) { index.remove(hash[id]); updated.push(marker); } const item = { lat: marker.lngLat.lat, lng: marker.lngLat.lng, marker: marker, }; hash[id] = item; index.insert(item); } if (updated.length > 0) { this._updated$.next(updated); } if (markers.length > updated.length) { this._indexChanged$.next(this); } } has(id) { return id in this._hash; } get(id) { return this.has(id) ? this._hash[id].marker : undefined; } getAll() { return this._index .all() .map((indexItem) => { return indexItem.marker; }); } remove(ids) { const hash = this._hash; const index = this._index; let changed = false; for (const id of ids) { if (!(id in hash)) { continue; } const item = hash[id]; index.remove(item); delete hash[id]; changed = true; } if (changed) { this._indexChanged$.next(this); } } removeAll() { this._hash = {}; this._index.clear(); this._indexChanged$.next(this); } search([sw, ne]) { return this._index .search({ maxX: ne.lng, maxY: ne.lat, minX: sw.lng, minY: sw.lat, }) .map((indexItem) => { return indexItem.marker; }); } update(marker) { const hash = this._hash; const index = this._index; const id = marker.id; if (!(id in hash)) { return; } index.remove(hash[id]); const item = { lat: marker.lngLat.lat, lng: marker.lngLat.lng, marker: marker, }; hash[id] = item; index.insert(item); } } function quickselect(arr, k, left, right, compare) { quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare$2); } function quickselectStep(arr, k, left, right, compare) { while (right > left) { if (right - left > 600) { var n = right - left + 1; var m = k - left + 1; var z = Math.log(n); var s = 0.5 * Math.exp(2 * z / 3); var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); quickselectStep(arr, k, newLeft, newRight, compare); } var t = arr[k]; var i = left; var j = right; swap(arr, left, k); if (compare(arr[right], t) > 0) swap(arr, left, right); while (i < j) { swap(arr, i, j); i++; j--; while (compare(arr[i], t) < 0) i++; while (compare(arr[j], t) > 0) j--; } if (compare(arr[left], t) === 0) swap(arr, left, j); else { j++; swap(arr, j, right); } if (j <= k) left = j + 1; if (k <= j) right = j - 1; } } function swap(arr, i, j) { var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } function defaultCompare$2(a, b) { return a < b ? -1 : a > b ? 1 : 0; } class RBush { constructor(maxEntries = 9) { // max entries in a node is 9 by default; min node fill is 40% for best performance this._maxEntries = Math.max(4, maxEntries); this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); this.clear(); } all() { return this._all(this.data, []); } search(bbox) { let node = this.data; const result = []; if (!intersects$1(bbox, node)) return result; const toBBox = this.toBBox; const nodesToSearch = []; while (node) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const childBBox = node.leaf ? toBBox(child) : child; if (intersects$1(bbox, childBBox)) { if (node.leaf) result.push(child); else if (contains(bbox, childBBox)) this._all(child, result); else nodesToSearch.push(child); } } node = nodesToSearch.pop(); } return result; } collides(bbox) { let node = this.data; if (!intersects$1(bbox, node)) return false; const nodesToSearch = []; while (node) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const childBBox = node.leaf ? this.toBBox(child) : child; if (intersects$1(bbox, childBBox)) { if (node.leaf || contains(bbox, childBBox)) return true; nodesToSearch.push(child); } } node = nodesToSearch.pop(); } return false; } load(data) { if (!(data && data.length)) return this; if (data.length < this._minEntries) { for (let i = 0; i < data.length; i++) { this.insert(data[i]); } return this; } // recursively build the tree with the given data from scratch using OMT algorithm let node = this._build(data.slice(), 0, data.length - 1, 0); if (!this.data.children.length) { // save as is if tree is empty this.data = node; } else if (this.data.height === node.height) { // split root if trees have the same height this._splitRoot(this.data, node); } else { if (this.data.height < node.height) { // swap trees if inserted one is bigger const tmpNode = this.data; this.data = node; node = tmpNode; } // insert the small tree into the large tree at appropriate level this._insert(node, this.data.height - node.height - 1, true); } return this; } insert(item) { if (item) this._insert(item, this.data.height - 1); return this; } clear() { this.data = createNode([]); return this; } remove(item, equalsFn) { if (!item) return this; let node = this.data; const bbox = this.toBBox(item); const path = []; const indexes = []; let i, parent, goingUp; // depth-first iterative tree traversal while (node || path.length) { if (!node) { // go up node = path.pop(); parent = path[path.length - 1]; i = indexes.pop(); goingUp = true; } if (node.leaf) { // check current node const index = findItem(item, node.children, equalsFn); if (index !== -1) { // item found, remove the item and condense tree upwards node.children.splice(index, 1); path.push(node); this._condense(path); return this; } } if (!goingUp && !node.leaf && contains(node, bbox)) { // go down path.push(node); indexes.push(i); i = 0; parent = node; node = node.children[0]; } else if (parent) { // go right i++; node = parent.children[i]; goingUp = false; } else node = null; // nothing found } return this; } toBBox(item) { return item; } compareMinX(a, b) { return a.minX - b.minX; } compareMinY(a, b) { return a.minY - b.minY; } toJSON() { return this.data; } fromJSON(data) { this.data = data; return this; } _all(node, result) { const nodesToSearch = []; while (node) { if (node.leaf) result.push(...node.children); else nodesToSearch.push(...node.children); node = nodesToSearch.pop(); } return result; } _build(items, left, right, height) { const N = right - left + 1; let M = this._maxEntries; let node; if (N <= M) { // reached leaf level; return leaf node = createNode(items.slice(left, right + 1)); calcBBox(node, this.toBBox); return node; } if (!height) { // target height of the bulk-loaded tree height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization M = Math.ceil(N / Math.pow(M, height - 1)); } node = createNode([]); node.leaf = false; node.height = height; // split the items into M mostly square tiles const N2 = Math.ceil(N / M); const N1 = N2 * Math.ceil(Math.sqrt(M)); multiSelect(items, left, right, N1, this.compareMinX); for (let i = left; i <= right; i += N1) { const right2 = Math.min(i + N1 - 1, right); multiSelect(items, i, right2, N2, this.compareMinY); for (let j = i; j <= right2; j += N2) { const right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively node.children.push(this._build(items, j, right3, height - 1)); } } calcBBox(node, this.toBBox); return node; } _chooseSubtree(bbox, node, level, path) { while (true) { path.push(node); if (node.leaf || path.length - 1 === level) break; let minArea = Infinity; let minEnlargement = Infinity; let targetNode; for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; const area = bboxArea(child); const enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement if (enlargement < minEnlargement) { minEnlargement = enlargement; minArea = area < minArea ? area : minArea; targetNode = child; } else if (enlargement === minEnlargement) { // otherwise choose one with the smallest area if (area < minArea) { minArea = area; targetNode = child; } } } node = targetNode || node.children[0]; } return node; } _insert(item, level, isNode) { const bbox = isNode ? item : this.toBBox(item); const insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too const node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node node.children.push(item); extend(node, bbox); // split on node overflow; propagate upwards if necessary while (level >= 0) { if (insertPath[level].children.length > this._maxEntries) { this._split(insertPath, level); level--; } else break; } // adjust bboxes along the insertion path this._adjustParentBBoxes(bbox, insertPath, level); } // split overflowed node into two _split(insertPath, level) { const node = insertPath[level]; const M = node.children.length; const m = this._minEntries; this._chooseSplitAxis(node, m, M); const splitIndex = this._chooseSplitIndex(node, m, M); const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); newNode.height = node.height; newNode.leaf = node.leaf; calcBBox(node, this.toBBox); calcBBox(newNode, this.toBBox); if (level) insertPath[level - 1].children.push(newNode); else this._splitRoot(node, newNode); } _splitRoot(node, newNode) { // split root node this.data = createNode([node, newNode]); this.data.height = node.height + 1; this.data.leaf = false; calcBBox(this.data, this.toBBox); } _chooseSplitIndex(node, m, M) { let index; let minOverlap = Infinity; let minArea = Infinity; for (let i = m; i <= M - m; i++) { const bbox1 = distBBox(node, 0, i, this.toBBox); const bbox2 = distBBox(node, i, M, this.toBBox); const overlap = intersectionArea(bbox1, bbox2); const area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap if (overlap < minOverlap) { minOverlap = overlap; index = i; minArea = area < minArea ? area : minArea; } else if (overlap === minOverlap) { // otherwise choose distribution with minimum area if (area < minArea) { minArea = area; index = i; } } } return index || M - m; } // sorts node children by the best axis for split _chooseSplitAxis(node, m, M) { const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; const xMargin = this._allDistMargin(node, m, M, compareMinX); const yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX, // otherwise it"s already sorted by minY if (xMargin < yMargin) node.children.sort(compareMinX); } // total margin of all possible split distributions where each node is at least m full _allDistMargin(node, m, M, compare) { node.children.sort(compare); const toBBox = this.toBBox; const leftBBox = distBBox(node, 0, m, toBBox); const rightBBox = distBBox(node, M - m, M, toBBox); let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); for (let i = m; i < M - m; i++) { const child = node.children[i]; extend(leftBBox, node.leaf ? toBBox(child) : child); margin += bboxMargin(leftBBox); } for (let i = M - m - 1; i >= m; i--) { const child = node.children[i]; extend(rightBBox, node.leaf ? toBBox(child) : child); margin += bboxMargin(rightBBox); } return margin; } _adjustParentBBoxes(bbox, path, level) { // adjust bboxes along the given tree path for (let i = level; i >= 0; i--) { extend(path[i], bbox); } } _condense(path) { // go through the path, removing empty nodes and updating bboxes for (let i = path.length - 1, siblings; i >= 0; i--) { if (path[i].children.length === 0) { if (i > 0) { siblings = path[i - 1].children; siblings.splice(siblings.indexOf(path[i]), 1); } else this.clear(); } else calcBBox(path[i], this.toBBox); } } } function findItem(item, items, equalsFn) { if (!equalsFn) return items.indexOf(item); for (let i = 0; i < items.length; i++) { if (equalsFn(item, items[i])) return i; } return -1; } // calculate node"s bbox from bboxes of its children function calcBBox(node, toBBox) { distBBox(node, 0, node.children.length, toBBox, node); } // min bounding rectangle of node children from k to p-1 function distBBox(node, k, p, toBBox, destNode) { if (!destNode) destNode = createNode(null); destNode.minX = Infinity; destNode.minY = Infinity; destNode.maxX = -Infinity; destNode.maxY = -Infinity; for (let i = k; i < p; i++) { const child = node.children[i]; extend(destNode, node.leaf ? toBBox(child) : child); } return destNode; } function extend(a, b) { a.minX = Math.min(a.minX, b.minX); a.minY = Math.min(a.minY, b.minY); a.maxX = Math.max(a.maxX, b.maxX); a.maxY = Math.max(a.maxY, b.maxY); return a; } function compareNodeMinX(a, b) { return a.minX - b.minX; } function compareNodeMinY(a, b) { return a.minY - b.minY; } function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } function enlargedArea(a, b) { return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); } function intersectionArea(a, b) { const minX = Math.max(a.minX, b.minX); const minY = Math.max(a.minY, b.minY); const maxX = Math.min(a.maxX, b.maxX); const maxY = Math.min(a.maxY, b.maxY); return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); } function contains(a, b) { return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY; } function intersects$1(a, b) { return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY; } function createNode(children) { return { children, height: 1, leaf: true, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; // combines selection algorithm with binary divide & conquer approach function multiSelect(arr, left, right, n, compare) { const stack = [left, right]; while (stack.length) { right = stack.pop(); left = stack.pop(); if (right - left <= n) continue; const mid = left + Math.ceil((right - left) / n / 2) * n; quickselect(arr, mid, left, right, compare); stack.push(left, mid, mid, right); } } class GeoRBush extends RBush { compareMinX(a, b) { return a.lng - b.lng; } compareMinY(a, b) { return a.lat - b.lat; } toBBox(item) { return { minX: item.lng, minY: item.lat, maxX: item.lng, maxY: item.lat, }; } } class ComponentService { constructor(container, navigator) { this._components = {}; for (const componentName in ComponentService.registeredComponents) { if (!ComponentService.registeredComponents.hasOwnProperty(componentName)) { continue; } const component = ComponentService.registeredComponents[componentName]; this._components[componentName] = { active: false, component: new component(componentName, container, navigator), }; } this._coverComponent = new ComponentService.registeredCoverComponent("cover", container, navigator); this._coverComponent.activate(); this._coverActivated = true; } static register(component) { if (ComponentService.registeredComponents[component.componentName] === undefined) { ComponentService.registeredComponents[component.componentName] = component; } } static registerCover(coverComponent) { ComponentService.registeredCoverComponent = coverComponent; } get coverActivated() { return this._coverActivated; } activateCover() { if (this._coverActivated) { return; } this._coverActivated = true; for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } const component = this._components[componentName]; if (component.active) { component.component.deactivate(); } } } deactivateCover() { if (!this._coverActivated) { return; } this._coverActivated = false; for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } const component = this._components[componentName]; if (component.active) { component.component.activate(); } } } activate(name) { this._checkName(name); this._components[name].active = true; if (!this._coverActivated) { this.get(name).activate(); } } configure(name, conf) { this._checkName(name); this.get(name).configure(conf); } deactivate(name) { this._checkName(name); this._components[name].active = false; if (!this._coverActivated) { this.get(name).deactivate(); } } get(name) { return this._components[name].component; } getCover() { return this._coverComponent; } remove() { this._coverComponent.deactivate(); for (const componentName in this._components) { if (!this._components.hasOwnProperty(componentName)) { continue; } this._components[componentName].component.deactivate(); } } _checkName(name) { if (!(name in this._components)) { throw new ArgumentMapillaryError(`Component does not exist: ${name}`); } } } ComponentService.registeredComponents = {}; var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; function getAugmentedNamespace(n) { var f = n.default; if (typeof f == "function") { var a = function () { return f.apply(this, arguments); }; a.prototype = f.prototype; } else a = {}; Object.defineProperty(a, "__esModule", {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } var nativeIsArray = Array.isArray; var toString$2 = Object.prototype.toString; var xIsArray = nativeIsArray || isArray$3; function isArray$3(obj) { return toString$2.call(obj) === "[object Array]" } var version$5 = "2"; var version$4 = version$5; VirtualPatch.NONE = 0; VirtualPatch.VTEXT = 1; VirtualPatch.VNODE = 2; VirtualPatch.WIDGET = 3; VirtualPatch.PROPS = 4; VirtualPatch.ORDER = 5; VirtualPatch.INSERT = 6; VirtualPatch.REMOVE = 7; VirtualPatch.THUNK = 8; var vpatch = VirtualPatch; function VirtualPatch(type, vNode, patch) { this.type = Number(type); this.vNode = vNode; this.patch = patch; } VirtualPatch.prototype.version = version$4; VirtualPatch.prototype.type = "VirtualPatch"; var version$3 = version$5; var isVnode = isVirtualNode; function isVirtualNode(x) { return x && x.type === "VirtualNode" && x.version === version$3 } var version$2 = version$5; var isVtext = isVirtualText; function isVirtualText(x) { return x && x.type === "VirtualText" && x.version === version$2 } var isWidget_1 = isWidget$7; function isWidget$7(w) { return w && w.type === "Widget" } var isThunk_1 = isThunk$3; function isThunk$3(t) { return t && t.type === "Thunk" } var isVNode$4 = isVnode; var isVText$3 = isVtext; var isWidget$6 = isWidget_1; var isThunk$2 = isThunk_1; var handleThunk_1 = handleThunk$2; function handleThunk$2(a, b) { var renderedA = a; var renderedB = b; if (isThunk$2(b)) { renderedB = renderThunk(b, a); } if (isThunk$2(a)) { renderedA = renderThunk(a, null); } return { a: renderedA, b: renderedB } } function renderThunk(thunk, previous) { var renderedThunk = thunk.vnode; if (!renderedThunk) { renderedThunk = thunk.vnode = thunk.render(previous); } if (!(isVNode$4(renderedThunk) || isVText$3(renderedThunk) || isWidget$6(renderedThunk))) { throw new Error("thunk did not return a valid node"); } return renderedThunk } var isObject$2 = function isObject(x) { return typeof x === "object" && x !== null; }; var isVhook = isHook$3; function isHook$3(hook) { return hook && (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) } var isObject$1 = isObject$2; var isHook$2 = isVhook; var diffProps_1 = diffProps$1; function diffProps$1(a, b) { var diff; for (var aKey in a) { if (!(aKey in b)) { diff = diff || {}; diff[aKey] = undefined; } var aValue = a[aKey]; var bValue = b[aKey]; if (aValue === bValue) { continue } else if (isObject$1(aValue) && isObject$1(bValue)) { if (getPrototype$1(bValue) !== getPrototype$1(aValue)) { diff = diff || {}; diff[aKey] = bValue; } else if (isHook$2(bValue)) { diff = diff || {}; diff[aKey] = bValue; } else { var objectDiff = diffProps$1(aValue, bValue); if (objectDiff) { diff = diff || {}; diff[aKey] = objectDiff; } } } else { diff = diff || {}; diff[aKey] = bValue; } } for (var bKey in b) { if (!(bKey in a)) { diff = diff || {}; diff[bKey] = b[bKey]; } } return diff } function getPrototype$1(value) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(value) } else if (value.__proto__) { return value.__proto__ } else if (value.constructor) { return value.constructor.prototype } } var isArray$2 = xIsArray; var VPatch$1 = vpatch; var isVNode$3 = isVnode; var isVText$2 = isVtext; var isWidget$5 = isWidget_1; var isThunk$1 = isThunk_1; var handleThunk$1 = handleThunk_1; var diffProps = diffProps_1; var diff_1$1 = diff$2; function diff$2(a, b) { var patch = { a: a }; walk(a, b, patch, 0); return patch } function walk(a, b, patch, index) { if (a === b) { return } var apply = patch[index]; var applyClear = false; if (isThunk$1(a) || isThunk$1(b)) { thunks(a, b, patch, index); } else if (b == null) { // If a is a widget we will add a remove patch for it // Otherwise any child widgets/hooks must be destroyed. // This prevents adding two remove patches for a widget. if (!isWidget$5(a)) { clearState(a, patch, index); apply = patch[index]; } apply = appendPatch(apply, new VPatch$1(VPatch$1.REMOVE, a, b)); } else if (isVNode$3(b)) { if (isVNode$3(a)) { if (a.tagName === b.tagName && a.namespace === b.namespace && a.key === b.key) { var propsPatch = diffProps(a.properties, b.properties); if (propsPatch) { apply = appendPatch(apply, new VPatch$1(VPatch$1.PROPS, a, propsPatch)); } apply = diffChildren(a, b, patch, apply, index); } else { apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); applyClear = true; } } else { apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); applyClear = true; } } else if (isVText$2(b)) { if (!isVText$2(a)) { apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); applyClear = true; } else if (a.text !== b.text) { apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); } } else if (isWidget$5(b)) { if (!isWidget$5(a)) { applyClear = true; } apply = appendPatch(apply, new VPatch$1(VPatch$1.WIDGET, a, b)); } if (apply) { patch[index] = apply; } if (applyClear) { clearState(a, patch, index); } } function diffChildren(a, b, patch, apply, index) { var aChildren = a.children; var orderedSet = reorder(aChildren, b.children); var bChildren = orderedSet.children; var aLen = aChildren.length; var bLen = bChildren.length; var len = aLen > bLen ? aLen : bLen; for (var i = 0; i < len; i++) { var leftNode = aChildren[i]; var rightNode = bChildren[i]; index += 1; if (!leftNode) { if (rightNode) { // Excess nodes in b need to be added apply = appendPatch(apply, new VPatch$1(VPatch$1.INSERT, null, rightNode)); } } else { walk(leftNode, rightNode, patch, index); } if (isVNode$3(leftNode) && leftNode.count) { index += leftNode.count; } } if (orderedSet.moves) { // Reorder nodes last apply = appendPatch(apply, new VPatch$1( VPatch$1.ORDER, a, orderedSet.moves )); } return apply } function clearState(vNode, patch, index) { // TODO: Make this a single walk, not two unhook(vNode, patch, index); destroyWidgets(vNode, patch, index); } // Patch records for all destroyed widgets must be added because we need // a DOM node reference for the destroy function function destroyWidgets(vNode, patch, index) { if (isWidget$5(vNode)) { if (typeof vNode.destroy === "function") { patch[index] = appendPatch( patch[index], new VPatch$1(VPatch$1.REMOVE, vNode, null) ); } } else if (isVNode$3(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { var children = vNode.children; var len = children.length; for (var i = 0; i < len; i++) { var child = children[i]; index += 1; destroyWidgets(child, patch, index); if (isVNode$3(child) && child.count) { index += child.count; } } } else if (isThunk$1(vNode)) { thunks(vNode, null, patch, index); } } // Create a sub-patch for thunks function thunks(a, b, patch, index) { var nodes = handleThunk$1(a, b); var thunkPatch = diff$2(nodes.a, nodes.b); if (hasPatches(thunkPatch)) { patch[index] = new VPatch$1(VPatch$1.THUNK, null, thunkPatch); } } function hasPatches(patch) { for (var index in patch) { if (index !== "a") { return true } } return false } // Execute hooks when two nodes are identical function unhook(vNode, patch, index) { if (isVNode$3(vNode)) { if (vNode.hooks) { patch[index] = appendPatch( patch[index], new VPatch$1( VPatch$1.PROPS, vNode, undefinedKeys(vNode.hooks) ) ); } if (vNode.descendantHooks || vNode.hasThunks) { var children = vNode.children; var len = children.length; for (var i = 0; i < len; i++) { var child = children[i]; index += 1; unhook(child, patch, index); if (isVNode$3(child) && child.count) { index += child.count; } } } } else if (isThunk$1(vNode)) { thunks(vNode, null, patch, index); } } function undefinedKeys(obj) { var result = {}; for (var key in obj) { result[key] = undefined; } return result } // List diff, naive left to right reordering function reorder(aChildren, bChildren) { // O(M) time, O(M) memory var bChildIndex = keyIndex(bChildren); var bKeys = bChildIndex.keys; var bFree = bChildIndex.free; if (bFree.length === bChildren.length) { return { children: bChildren, moves: null } } // O(N) time, O(N) memory var aChildIndex = keyIndex(aChildren); var aKeys = aChildIndex.keys; var aFree = aChildIndex.free; if (aFree.length === aChildren.length) { return { children: bChildren, moves: null } } // O(MAX(N, M)) memory var newChildren = []; var freeIndex = 0; var freeCount = bFree.length; var deletedItems = 0; // Iterate through a and match a node in b // O(N) time, for (var i = 0 ; i < aChildren.length; i++) { var aItem = aChildren[i]; var itemIndex; if (aItem.key) { if (bKeys.hasOwnProperty(aItem.key)) { // Match up the old keys itemIndex = bKeys[aItem.key]; newChildren.push(bChildren[itemIndex]); } else { // Remove old keyed items itemIndex = i - deletedItems++; newChildren.push(null); } } else { // Match the item in a with the next free item in b if (freeIndex < freeCount) { itemIndex = bFree[freeIndex++]; newChildren.push(bChildren[itemIndex]); } else { // There are no free items in b to match with // the free items in a, so the extra free nodes // are deleted. itemIndex = i - deletedItems++; newChildren.push(null); } } } var lastFreeIndex = freeIndex >= bFree.length ? bChildren.length : bFree[freeIndex]; // Iterate through b and append any new keys // O(M) time for (var j = 0; j < bChildren.length; j++) { var newItem = bChildren[j]; if (newItem.key) { if (!aKeys.hasOwnProperty(newItem.key)) { // Add any new keyed items // We are adding new items to the end and then sorting them // in place. In future we should insert new items in place. newChildren.push(newItem); } } else if (j >= lastFreeIndex) { // Add any leftover non-keyed items newChildren.push(newItem); } } var simulate = newChildren.slice(); var simulateIndex = 0; var removes = []; var inserts = []; var simulateItem; for (var k = 0; k < bChildren.length;) { var wantedItem = bChildren[k]; simulateItem = simulate[simulateIndex]; // remove items while (simulateItem === null && simulate.length) { removes.push(remove(simulate, simulateIndex, null)); simulateItem = simulate[simulateIndex]; } if (!simulateItem || simulateItem.key !== wantedItem.key) { // if we need a key in this position... if (wantedItem.key) { if (simulateItem && simulateItem.key) { // if an insert doesn"t put this key in place, it needs to move if (bKeys[simulateItem.key] !== k + 1) { removes.push(remove(simulate, simulateIndex, simulateItem.key)); simulateItem = simulate[simulateIndex]; // if the remove didn"t put the wanted item in place, we need to insert it if (!simulateItem || simulateItem.key !== wantedItem.key) { inserts.push({key: wantedItem.key, to: k}); } // items are matching, so skip ahead else { simulateIndex++; } } else { inserts.push({key: wantedItem.key, to: k}); } } else { inserts.push({key: wantedItem.key, to: k}); } k++; } // a key in simulate has no matching wanted key, remove it else if (simulateItem && simulateItem.key) { removes.push(remove(simulate, simulateIndex, simulateItem.key)); } } else { simulateIndex++; k++; } } // remove all the remaining nodes from simulate while(simulateIndex < simulate.length) { simulateItem = simulate[simulateIndex]; removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)); } // If the only moves we have are deletes then we can just // let the delete patch remove these items. if (removes.length === deletedItems && !inserts.length) { return { children: newChildren, moves: null } } return { children: newChildren, moves: { removes: removes, inserts: inserts } } } function remove(arr, index, key) { arr.splice(index, 1); return { from: index, key: key } } function keyIndex(children) { var keys = {}; var free = []; var length = children.length; for (var i = 0; i < length; i++) { var child = children[i]; if (child.key) { keys[child.key] = i; } else { free.push(i); } } return { keys: keys, // A hash of key name to index free: free // An array of unkeyed item indices } } function appendPatch(apply, patch) { if (apply) { if (isArray$2(apply)) { apply.push(patch); } else { apply = [apply, patch]; } return apply } else { return patch } } var diff$1 = diff_1$1; var diff_1 = diff$1; var slice = Array.prototype.slice; var domWalk$2 = iterativelyWalk; function iterativelyWalk(nodes, cb) { if (!("length" in nodes)) { nodes = [nodes]; } nodes = slice.call(nodes); while(nodes.length) { var node = nodes.shift(), ret = cb(node); if (ret) { return ret } if (node.childNodes && node.childNodes.length) { nodes = slice.call(node.childNodes).concat(nodes); } } } var domComment = Comment$1; function Comment$1(data, owner) { if (!(this instanceof Comment$1)) { return new Comment$1(data, owner) } this.data = data; this.nodeValue = data; this.length = data.length; this.ownerDocument = owner || null; } Comment$1.prototype.nodeType = 8; Comment$1.prototype.nodeName = "#comment"; Comment$1.prototype.toString = function _Comment_toString() { return "[object Comment]" }; var domText = DOMText$1; function DOMText$1(value, owner) { if (!(this instanceof DOMText$1)) { return new DOMText$1(value) } this.data = value || ""; this.length = this.data.length; this.ownerDocument = owner || null; } DOMText$1.prototype.type = "DOMTextNode"; DOMText$1.prototype.nodeType = 3; DOMText$1.prototype.nodeName = "#text"; DOMText$1.prototype.toString = function _Text_toString() { return this.data }; DOMText$1.prototype.replaceData = function replaceData(index, length, value) { var current = this.data; var left = current.substring(0, index); var right = current.substring(index + length, current.length); this.data = left + value + right; this.length = this.data.length; }; var dispatchEvent_1 = dispatchEvent$2; function dispatchEvent$2(ev) { var elem = this; var type = ev.type; if (!ev.target) { ev.target = elem; } if (!elem.listeners) { elem.listeners = {}; } var listeners = elem.listeners[type]; if (listeners) { return listeners.forEach(function (listener) { ev.currentTarget = elem; if (typeof listener === "function") { listener(ev); } else { listener.handleEvent(ev); } }) } if (elem.parentNode) { elem.parentNode.dispatchEvent(ev); } } var addEventListener_1 = addEventListener$2; function addEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { elem.listeners = {}; } if (!elem.listeners[type]) { elem.listeners[type] = []; } if (elem.listeners[type].indexOf(listener) === -1) { elem.listeners[type].push(listener); } } var removeEventListener_1 = removeEventListener$2; function removeEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { return } if (!elem.listeners[type]) { return } var list = elem.listeners[type]; var index = list.indexOf(listener); if (index !== -1) { list.splice(index, 1); } } var serialize = serializeNode$1; var voidElements = ["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]; function serializeNode$1(node) { switch (node.nodeType) { case 3: return escapeText(node.data) case 8: return "" default: return serializeElement(node) } } function serializeElement(elem) { var strings = []; var tagname = elem.tagName; if (elem.namespaceURI === "http://www.w3.org/1999/xhtml") { tagname = tagname.toLowerCase(); } strings.push("<" + tagname + properties(elem) + datasetify(elem)); if (voidElements.indexOf(tagname) > -1) { strings.push(" />"); } else { strings.push(">"); if (elem.childNodes.length) { strings.push.apply(strings, elem.childNodes.map(serializeNode$1)); } else if (elem.textContent || elem.innerText) { strings.push(escapeText(elem.textContent || elem.innerText)); } else if (elem.innerHTML) { strings.push(elem.innerHTML); } strings.push(""); } return strings.join("") } function isProperty(elem, key) { var type = typeof elem[key]; if (key === "style" && Object.keys(elem.style).length > 0) { return true } return elem.hasOwnProperty(key) && (type === "string" || type === "boolean" || type === "number") && key !== "nodeName" && key !== "className" && key !== "tagName" && key !== "textContent" && key !== "innerText" && key !== "namespaceURI" && key !== "innerHTML" } function stylify(styles) { if (typeof styles === "string") return styles var attr = ""; Object.keys(styles).forEach(function (key) { var value = styles[key]; key = key.replace(/[A-Z]/g, function(c) { return "-" + c.toLowerCase(); }); attr += key + ":" + value + ";"; }); return attr } function datasetify(elem) { var ds = elem.dataset; var props = []; for (var key in ds) { props.push({ name: "data-" + key, value: ds[key] }); } return props.length ? stringify(props) : "" } function stringify(list) { var attributes = []; list.forEach(function (tuple) { var name = tuple.name; var value = tuple.value; if (name === "style") { value = stylify(value); } attributes.push(name + "=" + """ + escapeAttributeValue(value) + """); }); return attributes.length ? " " + attributes.join(" ") : "" } function properties(elem) { var props = []; for (var key in elem) { if (isProperty(elem, key)) { props.push({ name: key, value: elem[key] }); } } for (var ns in elem._attributes) { for (var attribute in elem._attributes[ns]) { var prop = elem._attributes[ns][attribute]; var name = (prop.prefix ? prop.prefix + ":" : "") + attribute; props.push({ name: name, value: prop.value }); } } if (elem.className) { props.push({ name: "class", value: elem.className }); } return props.length ? stringify(props) : "" } function escapeText(s) { var str = ""; if (typeof(s) === "string") { str = s; } else if (s) { str = s.toString(); } return str .replace(/&/g, "&") .replace(//g, ">") } function escapeAttributeValue(str) { return escapeText(str).replace(/"/g, """) } var domWalk$1 = domWalk$2; var dispatchEvent$1 = dispatchEvent_1; var addEventListener$1 = addEventListener_1; var removeEventListener$1 = removeEventListener_1; var serializeNode = serialize; var htmlns = "http://www.w3.org/1999/xhtml"; var domElement = DOMElement$2; function DOMElement$2(tagName, owner, namespace) { if (!(this instanceof DOMElement$2)) { return new DOMElement$2(tagName) } var ns = namespace === undefined ? htmlns : (namespace || null); this.tagName = ns === htmlns ? String(tagName).toUpperCase() : tagName; this.nodeName = this.tagName; this.className = ""; this.dataset = {}; this.childNodes = []; this.parentNode = null; this.style = {}; this.ownerDocument = owner || null; this.namespaceURI = ns; this._attributes = {}; if (this.tagName === "INPUT") { this.type = "text"; } } DOMElement$2.prototype.type = "DOMElement"; DOMElement$2.prototype.nodeType = 1; DOMElement$2.prototype.appendChild = function _Element_appendChild(child) { if (child.parentNode) { child.parentNode.removeChild(child); } this.childNodes.push(child); child.parentNode = this; return child }; DOMElement$2.prototype.replaceChild = function _Element_replaceChild(elem, needle) { // TODO: Throw NotFoundError if needle.parentNode !== this if (elem.parentNode) { elem.parentNode.removeChild(elem); } var index = this.childNodes.indexOf(needle); needle.parentNode = null; this.childNodes[index] = elem; elem.parentNode = this; return needle }; DOMElement$2.prototype.removeChild = function _Element_removeChild(elem) { // TODO: Throw NotFoundError if elem.parentNode !== this var index = this.childNodes.indexOf(elem); this.childNodes.splice(index, 1); elem.parentNode = null; return elem }; DOMElement$2.prototype.insertBefore = function _Element_insertBefore(elem, needle) { // TODO: Throw NotFoundError if referenceElement is a dom node // and parentNode !== this if (elem.parentNode) { elem.parentNode.removeChild(elem); } var index = needle === null || needle === undefined ? -1 : this.childNodes.indexOf(needle); if (index > -1) { this.childNodes.splice(index, 0, elem); } else { this.childNodes.push(elem); } elem.parentNode = this; return elem }; DOMElement$2.prototype.setAttributeNS = function _Element_setAttributeNS(namespace, name, value) { var prefix = null; var localName = name; var colonPosition = name.indexOf(":"); if (colonPosition > -1) { prefix = name.substr(0, colonPosition); localName = name.substr(colonPosition + 1); } if (this.tagName === "INPUT" && name === "type") { this.type = value; } else { var attributes = this._attributes[namespace] || (this._attributes[namespace] = {}); attributes[localName] = {value: value, prefix: prefix}; } }; DOMElement$2.prototype.getAttributeNS = function _Element_getAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; var value = attributes && attributes[name] && attributes[name].value; if (this.tagName === "INPUT" && name === "type") { return this.type; } if (typeof value !== "string") { return null } return value }; DOMElement$2.prototype.removeAttributeNS = function _Element_removeAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; if (attributes) { delete attributes[name]; } }; DOMElement$2.prototype.hasAttributeNS = function _Element_hasAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; return !!attributes && name in attributes; }; DOMElement$2.prototype.setAttribute = function _Element_setAttribute(name, value) { return this.setAttributeNS(null, name, value) }; DOMElement$2.prototype.getAttribute = function _Element_getAttribute(name) { return this.getAttributeNS(null, name) }; DOMElement$2.prototype.removeAttribute = function _Element_removeAttribute(name) { return this.removeAttributeNS(null, name) }; DOMElement$2.prototype.hasAttribute = function _Element_hasAttribute(name) { return this.hasAttributeNS(null, name) }; DOMElement$2.prototype.removeEventListener = removeEventListener$1; DOMElement$2.prototype.addEventListener = addEventListener$1; DOMElement$2.prototype.dispatchEvent = dispatchEvent$1; // Un-implemented DOMElement$2.prototype.focus = function _Element_focus() { return void 0 }; DOMElement$2.prototype.toString = function _Element_toString() { return serializeNode(this) }; DOMElement$2.prototype.getElementsByClassName = function _Element_getElementsByClassName(classNames) { var classes = classNames.split(" "); var elems = []; domWalk$1(this, function (node) { if (node.nodeType === 1) { var nodeClassName = node.className || ""; var nodeClasses = nodeClassName.split(" "); if (classes.every(function (item) { return nodeClasses.indexOf(item) !== -1 })) { elems.push(node); } } }); return elems }; DOMElement$2.prototype.getElementsByTagName = function _Element_getElementsByTagName(tagName) { tagName = tagName.toLowerCase(); var elems = []; domWalk$1(this.childNodes, function (node) { if (node.nodeType === 1 && (tagName === "*" || node.tagName.toLowerCase() === tagName)) { elems.push(node); } }); return elems }; DOMElement$2.prototype.contains = function _Element_contains(element) { return domWalk$1(this, function (node) { return element === node }) || false }; var DOMElement$1 = domElement; var domFragment = DocumentFragment$1; function DocumentFragment$1(owner) { if (!(this instanceof DocumentFragment$1)) { return new DocumentFragment$1() } this.childNodes = []; this.parentNode = null; this.ownerDocument = owner || null; } DocumentFragment$1.prototype.type = "DocumentFragment"; DocumentFragment$1.prototype.nodeType = 11; DocumentFragment$1.prototype.nodeName = "#document-fragment"; DocumentFragment$1.prototype.appendChild = DOMElement$1.prototype.appendChild; DocumentFragment$1.prototype.replaceChild = DOMElement$1.prototype.replaceChild; DocumentFragment$1.prototype.removeChild = DOMElement$1.prototype.removeChild; DocumentFragment$1.prototype.toString = function _DocumentFragment_toString() { return this.childNodes.map(function (node) { return String(node) }).join("") }; var event = Event$1; function Event$1(family) {} Event$1.prototype.initEvent = function _Event_initEvent(type, bubbles, cancelable) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; }; Event$1.prototype.preventDefault = function _Event_preventDefault() { }; var domWalk = domWalk$2; var Comment = domComment; var DOMText = domText; var DOMElement = domElement; var DocumentFragment = domFragment; var Event = event; var dispatchEvent = dispatchEvent_1; var addEventListener = addEventListener_1; var removeEventListener = removeEventListener_1; var document$3 = Document$1; function Document$1() { if (!(this instanceof Document$1)) { return new Document$1(); } this.head = this.createElement("head"); this.body = this.createElement("body"); this.documentElement = this.createElement("html"); this.documentElement.appendChild(this.head); this.documentElement.appendChild(this.body); this.childNodes = [this.documentElement]; this.nodeType = 9; } var proto = Document$1.prototype; proto.createTextNode = function createTextNode(value) { return new DOMText(value, this) }; proto.createElementNS = function createElementNS(namespace, tagName) { var ns = namespace === null ? null : String(namespace); return new DOMElement(tagName, this, ns) }; proto.createElement = function createElement(tagName) { return new DOMElement(tagName, this) }; proto.createDocumentFragment = function createDocumentFragment() { return new DocumentFragment(this) }; proto.createEvent = function createEvent(family) { return new Event() }; proto.createComment = function createComment(data) { return new Comment(data, this) }; proto.getElementById = function getElementById(id) { id = String(id); var result = domWalk(this.childNodes, function (node) { if (String(node.id) === id) { return node } }); return result || null }; proto.getElementsByClassName = DOMElement.prototype.getElementsByClassName; proto.getElementsByTagName = DOMElement.prototype.getElementsByTagName; proto.contains = DOMElement.prototype.contains; proto.removeEventListener = removeEventListener; proto.addEventListener = addEventListener; proto.dispatchEvent = dispatchEvent; var Document = document$3; var minDocument = new Document(); var topLevel = typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof window !== "undefined" ? window : {}; var minDoc = minDocument; var doccy; if (typeof document !== "undefined") { doccy = document; } else { doccy = topLevel["__GLOBAL_DOCUMENT_CACHE@4"]; if (!doccy) { doccy = topLevel["__GLOBAL_DOCUMENT_CACHE@4"] = minDoc; } } var document_1 = doccy; var isObject = isObject$2; var isHook$1 = isVhook; var applyProperties_1 = applyProperties$2; function applyProperties$2(node, props, previous) { for (var propName in props) { var propValue = props[propName]; if (propValue === undefined) { removeProperty(node, propName, propValue, previous); } else if (isHook$1(propValue)) { removeProperty(node, propName, propValue, previous); if (propValue.hook) { propValue.hook(node, propName, previous ? previous[propName] : undefined); } } else { if (isObject(propValue)) { patchObject(node, props, previous, propName, propValue); } else { node[propName] = propValue; } } } } function removeProperty(node, propName, propValue, previous) { if (previous) { var previousValue = previous[propName]; if (!isHook$1(previousValue)) { if (propName === "attributes") { for (var attrName in previousValue) { node.removeAttribute(attrName); } } else if (propName === "style") { for (var i in previousValue) { node.style[i] = ""; } } else if (typeof previousValue === "string") { node[propName] = ""; } else { node[propName] = null; } } else if (previousValue.unhook) { previousValue.unhook(node, propName, propValue); } } } function patchObject(node, props, previous, propName, propValue) { var previousValue = previous ? previous[propName] : undefined; // Set attributes if (propName === "attributes") { for (var attrName in propValue) { var attrValue = propValue[attrName]; if (attrValue === undefined) { node.removeAttribute(attrName); } else { node.setAttribute(attrName, attrValue); } } return } if(previousValue && isObject(previousValue) && getPrototype(previousValue) !== getPrototype(propValue)) { node[propName] = propValue; return } if (!isObject(node[propName])) { node[propName] = {}; } var replacer = propName === "style" ? "" : undefined; for (var k in propValue) { var value = propValue[k]; node[propName][k] = (value === undefined) ? replacer : value; } } function getPrototype(value) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(value) } else if (value.__proto__) { return value.__proto__ } else if (value.constructor) { return value.constructor.prototype } } var document$2 = document_1; var applyProperties$1 = applyProperties_1; var isVNode$2 = isVnode; var isVText$1 = isVtext; var isWidget$4 = isWidget_1; var handleThunk = handleThunk_1; var createElement_1$1 = createElement$1; function createElement$1(vnode, opts) { var doc = opts ? opts.document || document$2 : document$2; var warn = opts ? opts.warn : null; vnode = handleThunk(vnode).a; if (isWidget$4(vnode)) { return vnode.init() } else if (isVText$1(vnode)) { return doc.createTextNode(vnode.text) } else if (!isVNode$2(vnode)) { if (warn) { warn("Item is not a valid virtual dom node", vnode); } return null } var node = (vnode.namespace === null) ? doc.createElement(vnode.tagName) : doc.createElementNS(vnode.namespace, vnode.tagName); var props = vnode.properties; applyProperties$1(node, props); var children = vnode.children; for (var i = 0; i < children.length; i++) { var childNode = createElement$1(children[i], opts); if (childNode) { node.appendChild(childNode); } } return node } // Maps a virtual DOM tree onto a real DOM tree in an efficient manner. // We don"t want to read all of the DOM nodes in the tree so we use // the in-order tree indexing to eliminate recursion down certain branches. // We only recurse into a DOM node if we know that it contains a child of // interest. var noChild = {}; var domIndex_1 = domIndex$1; function domIndex$1(rootNode, tree, indices, nodes) { if (!indices || indices.length === 0) { return {} } else { indices.sort(ascending); return recurse(rootNode, tree, indices, nodes, 0) } } function recurse(rootNode, tree, indices, nodes, rootIndex) { nodes = nodes || {}; if (rootNode) { if (indexInRange(indices, rootIndex, rootIndex)) { nodes[rootIndex] = rootNode; } var vChildren = tree.children; if (vChildren) { var childNodes = rootNode.childNodes; for (var i = 0; i < tree.children.length; i++) { rootIndex += 1; var vChild = vChildren[i] || noChild; var nextIndex = rootIndex + (vChild.count || 0); // skip recursion down the tree if there are no nodes down here if (indexInRange(indices, rootIndex, nextIndex)) { recurse(childNodes[i], vChild, indices, nodes, rootIndex); } rootIndex = nextIndex; } } } return nodes } // Binary search for an index in the interval [left, right] function indexInRange(indices, left, right) { if (indices.length === 0) { return false } var minIndex = 0; var maxIndex = indices.length - 1; var currentIndex; var currentItem; while (minIndex <= maxIndex) { currentIndex = ((maxIndex + minIndex) / 2) >> 0; currentItem = indices[currentIndex]; if (minIndex === maxIndex) { return currentItem >= left && currentItem <= right } else if (currentItem < left) { minIndex = currentIndex + 1; } else if (currentItem > right) { maxIndex = currentIndex - 1; } else { return true } } return false; } function ascending(a, b) { return a > b ? 1 : -1 } var isWidget$3 = isWidget_1; var updateWidget_1 = updateWidget$1; function updateWidget$1(a, b) { if (isWidget$3(a) && isWidget$3(b)) { if ("name" in a && "name" in b) { return a.id === b.id } else { return a.init === b.init } } return false } var applyProperties = applyProperties_1; var isWidget$2 = isWidget_1; var VPatch = vpatch; var updateWidget = updateWidget_1; var patchOp$1 = applyPatch$1; function applyPatch$1(vpatch, domNode, renderOptions) { var type = vpatch.type; var vNode = vpatch.vNode; var patch = vpatch.patch; switch (type) { case VPatch.REMOVE: return removeNode$1(domNode, vNode) case VPatch.INSERT: return insertNode$1(domNode, patch, renderOptions) case VPatch.VTEXT: return stringPatch(domNode, vNode, patch, renderOptions) case VPatch.WIDGET: return widgetPatch(domNode, vNode, patch, renderOptions) case VPatch.VNODE: return vNodePatch(domNode, vNode, patch, renderOptions) case VPatch.ORDER: reorderChildren(domNode, patch); return domNode case VPatch.PROPS: applyProperties(domNode, patch, vNode.properties); return domNode case VPatch.THUNK: return replaceRoot(domNode, renderOptions.patch(domNode, patch, renderOptions)) default: return domNode } } function removeNode$1(domNode, vNode) { var parentNode = domNode.parentNode; if (parentNode) { parentNode.removeChild(domNode); } destroyWidget(domNode, vNode); return null } function insertNode$1(parentNode, vNode, renderOptions) { var newNode = renderOptions.render(vNode, renderOptions); if (parentNode) { parentNode.appendChild(newNode); } return parentNode } function stringPatch(domNode, leftVNode, vText, renderOptions) { var newNode; if (domNode.nodeType === 3) { domNode.replaceData(0, domNode.length, vText.text); newNode = domNode; } else { var parentNode = domNode.parentNode; newNode = renderOptions.render(vText, renderOptions); if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } } return newNode } function widgetPatch(domNode, leftVNode, widget, renderOptions) { var updating = updateWidget(leftVNode, widget); var newNode; if (updating) { newNode = widget.update(leftVNode, domNode) || domNode; } else { newNode = renderOptions.render(widget, renderOptions); } var parentNode = domNode.parentNode; if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } if (!updating) { destroyWidget(domNode, leftVNode); } return newNode } function vNodePatch(domNode, leftVNode, vNode, renderOptions) { var parentNode = domNode.parentNode; var newNode = renderOptions.render(vNode, renderOptions); if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } return newNode } function destroyWidget(domNode, w) { if (typeof w.destroy === "function" && isWidget$2(w)) { w.destroy(domNode); } } function reorderChildren(domNode, moves) { var childNodes = domNode.childNodes; var keyMap = {}; var node; var remove; var insert; for (var i = 0; i < moves.removes.length; i++) { remove = moves.removes[i]; node = childNodes[remove.from]; if (remove.key) { keyMap[remove.key] = node; } domNode.removeChild(node); } var length = childNodes.length; for (var j = 0; j < moves.inserts.length; j++) { insert = moves.inserts[j]; node = keyMap[insert.key]; // this is the weirdest bug i"ve ever seen in webkit domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]); } } function replaceRoot(oldRoot, newRoot) { if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) { oldRoot.parentNode.replaceChild(newRoot, oldRoot); } return newRoot; } var document$1 = document_1; var isArray$1 = xIsArray; var render = createElement_1$1; var domIndex = domIndex_1; var patchOp = patchOp$1; var patch_1$1 = patch$2; function patch$2(rootNode, patches, renderOptions) { renderOptions = renderOptions || {}; renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch$2 ? renderOptions.patch : patchRecursive; renderOptions.render = renderOptions.render || render; return renderOptions.patch(rootNode, patches, renderOptions) } function patchRecursive(rootNode, patches, renderOptions) { var indices = patchIndices(patches); if (indices.length === 0) { return rootNode } var index = domIndex(rootNode, patches.a, indices); var ownerDocument = rootNode.ownerDocument; if (!renderOptions.document && ownerDocument !== document$1) { renderOptions.document = ownerDocument; } for (var i = 0; i < indices.length; i++) { var nodeIndex = indices[i]; rootNode = applyPatch(rootNode, index[nodeIndex], patches[nodeIndex], renderOptions); } return rootNode } function applyPatch(rootNode, domNode, patchList, renderOptions) { if (!domNode) { return rootNode } var newNode; if (isArray$1(patchList)) { for (var i = 0; i < patchList.length; i++) { newNode = patchOp(patchList[i], domNode, renderOptions); if (domNode === rootNode) { rootNode = newNode; } } } else { newNode = patchOp(patchList, domNode, renderOptions); if (domNode === rootNode) { rootNode = newNode; } } return rootNode } function patchIndices(patches) { var indices = []; for (var key in patches) { if (key !== "a") { indices.push(Number(key)); } } return indices } var patch$1 = patch_1$1; var patch_1 = patch$1; var version$1 = version$5; var isVNode$1 = isVnode; var isWidget$1 = isWidget_1; var isThunk = isThunk_1; var isVHook = isVhook; var vnode = VirtualNode; var noProperties = {}; var noChildren = []; function VirtualNode(tagName, properties, children, key, namespace) { this.tagName = tagName; this.properties = properties || noProperties; this.children = children || noChildren; this.key = key != null ? String(key) : undefined; this.namespace = (typeof namespace === "string") ? namespace : null; var count = (children && children.length) || 0; var descendants = 0; var hasWidgets = false; var hasThunks = false; var descendantHooks = false; var hooks; for (var propName in properties) { if (properties.hasOwnProperty(propName)) { var property = properties[propName]; if (isVHook(property) && property.unhook) { if (!hooks) { hooks = {}; } hooks[propName] = property; } } } for (var i = 0; i < count; i++) { var child = children[i]; if (isVNode$1(child)) { descendants += child.count || 0; if (!hasWidgets && child.hasWidgets) { hasWidgets = true; } if (!hasThunks && child.hasThunks) { hasThunks = true; } if (!descendantHooks && (child.hooks || child.descendantHooks)) { descendantHooks = true; } } else if (!hasWidgets && isWidget$1(child)) { if (typeof child.destroy === "function") { hasWidgets = true; } } else if (!hasThunks && isThunk(child)) { hasThunks = true; } } this.count = count + descendants; this.hasWidgets = hasWidgets; this.hasThunks = hasThunks; this.hooks = hooks; this.descendantHooks = descendantHooks; } VirtualNode.prototype.version = version$1; VirtualNode.prototype.type = "VirtualNode"; var version = version$5; var vtext = VirtualText; function VirtualText(text) { this.text = String(text); } VirtualText.prototype.version = version; VirtualText.prototype.type = "VirtualText"; /*! * Cross-Browser Split 1.1.1 * Copyright 2007-2012 Steven Levithan * Available under the MIT License * ECMAScript compliant, uniform cross-browser split method */ /** * Splits a string into an array of strings using a regex or string separator. Matches of the * separator are not included in the result array. However, if `separator` is a regex that contains * capturing groups, backreferences are spliced into the result each time `separator` is matched. * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably * cross-browser. * @param {String} str String to split. * @param {RegExp|String} separator Regex or string to use for separating the string. * @param {Number} [limit] Maximum number of items to include in the result array. * @returns {Array} Array of substrings. * @example * * // Basic use * split("a b c d", " "); * // -> ["a", "b", "c", "d"] * * // With limit * split("a b c d", " ", 2); * // -> ["a", "b"] * * // Backreferences in result array * split("..word1 word2..", /([a-z]+)(d+)/i); * // -> ["..", "word", "1", " ", "word", "2", ".."] */ var browserSplit = (function split(undef) { var nativeSplit = String.prototype.split, compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group self; self = function(str, separator, limit) { // If `separator` is not a regex, use `nativeSplit` if (Object.prototype.toString.call(separator) !== "[object RegExp]") { return nativeSplit.call(str, separator, limit); } var output = [], flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6 (separator.sticky ? "y" : ""), // Firefox 3+ lastLastIndex = 0, // Make `global` and avoid `lastIndex` issues by working with a copy separator = new RegExp(separator.source, flags + "g"), separator2, match, lastIndex, lastLength; str += ""; // Type-convert if (!compliantExecNpcg) { // Doesn"t need flags gy, but they don"t hurt separator2 = new RegExp("^" + separator.source + "$(?!\s)", flags); } /* Values for `limit`, per the spec: * If undefined: 4294967295 // Math.pow(2, 32) - 1 * If 0, Infinity, or NaN: 0 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; * If negative number: 4294967296 - Math.floor(Math.abs(limit)) * If other: Type-convert, then use the above rules */ limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1 limit >>> 0; // ToUint32(limit) while (match = separator.exec(str)) { // `separator.lastIndex` is not reliable cross-browser lastIndex = match.index + match[0].length; if (lastIndex > lastLastIndex) { output.push(str.slice(lastLastIndex, match.index)); // Fix browsers whose `exec` methods don"t consistently return `undefined` for // nonparticipating capturing groups if (!compliantExecNpcg && match.length > 1) { match[0].replace(separator2, function() { for (var i = 1; i < arguments.length - 2; i++) { if (arguments[i] === undef) { match[i] = undef; } } }); } if (match.length > 1 && match.index < str.length) { Array.prototype.push.apply(output, match.slice(1)); } lastLength = match[0].length; lastLastIndex = lastIndex; if (output.length >= limit) { break; } } if (separator.lastIndex === match.index) { separator.lastIndex++; // Avoid an infinite loop } } if (lastLastIndex === str.length) { if (lastLength || !separator.test("")) { output.push(""); } } else { output.push(str.slice(lastLastIndex)); } return output.length > limit ? output.slice(0, limit) : output; }; return self; })(); var split = browserSplit; var classIdSplit = /([.#]?[a-zA-Z0-9u007F-uFFFF_:-]+)/; var notClassId = /^.|#/; var parseTag_1 = parseTag$1; function parseTag$1(tag, props) { if (!tag) { return "DIV"; } var noId = !(props.hasOwnProperty("id")); var tagParts = split(tag, classIdSplit); var tagName = null; if (notClassId.test(tagParts[1])) { tagName = "DIV"; } var classes, part, type, i; for (i = 0; i < tagParts.length; i++) { part = tagParts[i]; if (!part) { continue; } type = part.charAt(0); if (!tagName) { tagName = part; } else if (type === ".") { classes = classes || []; classes.push(part.substring(1, part.length)); } else if (type === "#" && noId) { props.id = part.substring(1, part.length); } } if (classes) { if (props.className) { classes.push(props.className); } props.className = classes.join(" "); } return props.namespace ? tagName : tagName.toUpperCase(); } var softSetHook$1 = SoftSetHook; function SoftSetHook(value) { if (!(this instanceof SoftSetHook)) { return new SoftSetHook(value); } this.value = value; } SoftSetHook.prototype.hook = function (node, propertyName) { if (node[propertyName] !== this.value) { node[propertyName] = this.value; } }; /*global window, global*/ var root = typeof window !== "undefined" ? window : typeof commonjsGlobal !== "undefined" ? commonjsGlobal : {}; var individual = Individual$1; function Individual$1(key, value) { if (key in root) { return root[key]; } root[key] = value; return value; } var Individual = individual; var oneVersion = OneVersion; function OneVersion(moduleName, version, defaultValue) { var key = "__INDIVIDUAL_ONE_VERSION_" + moduleName; var enforceKey = key + "_ENFORCE_SINGLETON"; var versionValue = Individual(enforceKey, version); if (versionValue !== version) { throw new Error("Can only have one copy of " + moduleName + ". " + "You already have version " + versionValue + " installed. " + "This means you cannot install version " + version); } return Individual(key, defaultValue); } var OneVersionConstraint = oneVersion; var MY_VERSION = "7"; OneVersionConstraint("ev-store", MY_VERSION); var hashKey = "__EV_STORE_KEY@" + MY_VERSION; var evStore = EvStore$1; function EvStore$1(elem) { var hash = elem[hashKey]; if (!hash) { hash = elem[hashKey] = {}; } return hash; } var EvStore = evStore; var evHook$1 = EvHook; function EvHook(value) { if (!(this instanceof EvHook)) { return new EvHook(value); } this.value = value; } EvHook.prototype.hook = function (node, propertyName) { var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = this.value; }; EvHook.prototype.unhook = function(node, propertyName) { var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = undefined; }; var isArray = xIsArray; var VNode$1 = vnode; var VText$1 = vtext; var isVNode = isVnode; var isVText = isVtext; var isWidget = isWidget_1; var isHook = isVhook; var isVThunk = isThunk_1; var parseTag = parseTag_1; var softSetHook = softSetHook$1; var evHook = evHook$1; var virtualHyperscript = h$2; function h$2(tagName, properties, children) { var childNodes = []; var tag, props, key, namespace; if (!children && isChildren(properties)) { children = properties; props = {}; } props = props || properties || {}; tag = parseTag(tagName, props); // support keys if (props.hasOwnProperty("key")) { key = props.key; props.key = undefined; } // support namespace if (props.hasOwnProperty("namespace")) { namespace = props.namespace; props.namespace = undefined; } // fix cursor bug if (tag === "INPUT" && !namespace && props.hasOwnProperty("value") && props.value !== undefined && !isHook(props.value) ) { props.value = softSetHook(props.value); } transformProperties(props); if (children !== undefined && children !== null) { addChild(children, childNodes, tag, props); } return new VNode$1(tag, props, childNodes, key, namespace); } function addChild(c, childNodes, tag, props) { if (typeof c === "string") { childNodes.push(new VText$1(c)); } else if (typeof c === "number") { childNodes.push(new VText$1(String(c))); } else if (isChild(c)) { childNodes.push(c); } else if (isArray(c)) { for (var i = 0; i < c.length; i++) { addChild(c[i], childNodes, tag, props); } } else if (c === null || c === undefined) { return; } else { throw UnexpectedVirtualElement({ foreignObject: c, parentVnode: { tagName: tag, properties: props } }); } } function transformProperties(props) { for (var propName in props) { if (props.hasOwnProperty(propName)) { var value = props[propName]; if (isHook(value)) { continue; } if (propName.substr(0, 3) === "ev-") { // add ev-foo support props[propName] = evHook(value); } } } } function isChild(x) { return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x); } function isChildren(x) { return typeof x === "string" || isArray(x) || isChild(x); } function UnexpectedVirtualElement(data) { var err = new Error(); err.type = "virtual-hyperscript.unexpected.virtual-element"; err.message = "Unexpected virtual child passed to h(). " + "Expected a VNode / Vthunk / VWidget / string but: " + "got: " + errorString(data.foreignObject) + ". " + "The parent vnode is: " + errorString(data.parentVnode); err.foreignObject = data.foreignObject; err.parentVnode = data.parentVnode; return err; } function errorString(obj) { try { return JSON.stringify(obj, null, " "); } catch (e) { return String(obj); } } var h$1 = virtualHyperscript; var h_1 = h$1; var createElement = createElement_1$1; var createElement_1 = createElement; var diff = diff_1; var patch = patch_1; var h = h_1; var create = createElement_1; var VNode = vnode; var VText = vtext; var virtualDom = { diff: diff, patch: patch, h: h, create: create, VNode: VNode, VText: VText }; class EventEmitter { constructor() { this._events = {}; } /** * @ignore */ fire(type, event) { if (!this._listens(type)) { return; } for (const handler of this._events[type]) { handler(event); } } /** * Unsubscribe from an event by its name. * @param {string} type - The name of the event * to unsubscribe from. * @param {(event: T) => void} handler - The * handler to remove. */ off(type, handler) { if (!type) { this._events = {}; return; } if (this._listens(type)) { const index = this._events[type].indexOf(handler); if (index >= 0) { this._events[type].splice(index, 1); } if (!this._events[type].length) { delete this._events[type]; } } } /** * Subscribe to an event by its name. * @param {string} type - The name of the event * to subscribe to. * @param {(event: T) => void} handler - The * handler called when the event occurs. */ on(type, handler) { this._events[type] = this._events[type] || []; this._events[type].push(handler); } _listens(eventType) { return eventType in this._events; } } class SubscriptionHolder { constructor() { this._subscriptions = []; } push(subscription) { this._subscriptions.push(subscription); } unsubscribe() { for (const sub of this._subscriptions) { sub.unsubscribe(); } this._subscriptions = []; } } class Component extends EventEmitter { constructor(name, container, navigator) { super(); this._activated$ = new BehaviorSubject(false); this._configurationSubject$ = new Subject(); this._activated = false; this._container = container; this._name = name; this._navigator = navigator; this._subscriptions = new SubscriptionHolder(); this._configuration$ = this._configurationSubject$.pipe(startWith(this.defaultConfiguration), scan((conf, newConf) => { for (let key in newConf) { if (newConf.hasOwnProperty(key)) { conf[key] = newConf[key]; } } return conf; }), publishReplay(1), refCount()); this._configuration$.subscribe(() => { }); } /** * Get activated. * * @returns {boolean} Value indicating if the component is * currently active. */ get activated() { return this._activated; } /** @ignore */ get activated$() { return this._activated$; } /** * Get default configuration. * * @returns {TConfiguration} Default configuration for component. */ get defaultConfiguration() { return this._getDefaultConfiguration(); } /** @ignore */ get configuration$() { return this._configuration$; } /** * Get name. * * @description The name of the component. Used when interacting with the * component through the Viewer"s API. */ get name() { return this._name; } /** @ignore */ activate(conf) { if (this._activated) { return; } if (conf !== undefined) { this._configurationSubject$.next(conf); } this._activated = true; this._activate(); this._activated$.next(true); } /** * Configure the component. * * @param configuration Component configuration. */ configure(configuration) { this._configurationSubject$.next(configuration); } /** @ignore */ deactivate() { if (!this._activated) { return; } this._activated = false; this._deactivate(); this._container.domRenderer.clear(this._name); this._container.glRenderer.clear(this._name); this._activated$.next(false); } /** @inheritdoc */ fire(type, event) { super.fire(type, event); } /** @inheritdoc */ off(type, handler) { super.off(type, handler); } /** @inheritdoc */ on(type, handler) { super.on(type, handler); } /** * Detect the viewer"s new width and height and resize the component"s * rendered elements accordingly if applicable. * * @ignore */ resize() { return; } } var CoverState; (function (CoverState) { CoverState[CoverState["Hidden"] = 0] = "Hidden"; CoverState[CoverState["Loading"] = 1] = "Loading"; CoverState[CoverState["Visible"] = 2] = "Visible"; })(CoverState || (CoverState = {})); class CoverComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); } _activate() { const originalSrc$ = this.configuration$.pipe(first((c) => { return !!c.id; }), filter((c) => { return !c.src; }), switchMap((c) => { return this._getImageSrc$(c.id).pipe(catchError((error) => { console.error(error); return empty(); })); }), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(originalSrc$.pipe(map((src) => { return { src: src }; })) .subscribe((c) => { this._configurationSubject$.next(c); })); subs.push(combineLatest(this.configuration$, originalSrc$).pipe(filter(([c, src]) => { return !!c.src && c.src !== src; }), first()) .subscribe(([, src]) => { window.URL.revokeObjectURL(src); })); subs.push(this._configuration$.pipe(distinctUntilChanged(undefined, (configuration) => { return configuration.state; }), switchMap((configuration) => { return combineLatest(of(configuration.state), this._navigator.stateService.currentImage$); }), switchMap(([state, image]) => { const keySrc$ = combineLatest(of(image.id), image.image$.pipe(filter((imageElement) => { return !!imageElement; }), map((imageElement) => { return imageElement.src; }))); return state === CoverState.Visible ? keySrc$.pipe(first()) : keySrc$; }), distinctUntilChanged(([k1, s1], [k2, s2]) => { return k1 === k2 && s1 === s2; }), map(([key, src]) => { return { id: key, src: src }; })) .subscribe(this._configurationSubject$)); subs.push(combineLatest(this._configuration$, this._container.configurationService.exploreUrl$, this._container.renderService.size$).pipe(map(([configuration, exploreUrl, size]) => { if (!configuration.src) { return { name: this._name, vNode: virtualDom.h("div", []) }; } const compactClass = size.width <= 640 || size.height <= 480 ? ".mapillary-cover-compact" : ""; if (configuration.state === CoverState.Hidden) { const doneContainer = virtualDom.h("div.mapillary-cover-container.mapillary-cover-done" + compactClass, [this._getCoverBackgroundVNode(configuration)]); return { name: this._name, vNode: doneContainer }; } const container = virtualDom.h("div.mapillary-cover-container" + compactClass, [this._getCoverButtonVNode(configuration, exploreUrl)]); return { name: this._name, vNode: container }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { state: CoverState.Visible }; } _getCoverButtonVNode(configuration, exploreUrl) { const cover = configuration.state === CoverState.Loading ? "div.mapillary-cover.mapillary-cover-loading" : "div.mapillary-cover"; const coverButton = virtualDom.h("div.mapillary-cover-button", [virtualDom.h("div.mapillary-cover-button-icon", [])]); const coverLogo = virtualDom.h("a.mapillary-cover-logo", { href: exploreUrl, target: "_blank" }, []); const coverIndicator = virtualDom.h("div.mapillary-cover-indicator", { onclick: () => { this.configure({ state: CoverState.Loading }); } }, []); return virtualDom.h(cover, [ this._getCoverBackgroundVNode(configuration), coverIndicator, coverButton, coverLogo, ]); } _getCoverBackgroundVNode(conf) { const properties = { style: { backgroundImage: `url(${conf.src})` }, }; const children = []; if (conf.state === CoverState.Loading) { children.push(virtualDom.h("div.mapillary-cover-spinner", {}, [])); } return virtualDom.h("div.mapillary-cover-background", properties, children); } _getImageSrc$(id) { return Observable.create((subscriber) => { this._navigator.api.getImages$([id]) .subscribe((items) => { for (const item of items) { const imageId = typeof id === "number" ? id.toString() : id; if (item.node_id !== imageId) { continue; } this._navigator.api.data .getImageBuffer(item.node.thumb.url) .then((buffer) => { const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { subscriber.next(image.src); subscriber.complete(); }; image.onerror = () => { subscriber.error(new Error(`Failed to load cover ` + `image (${id})`)); }; const blob = new Blob([buffer]); image.src = window.URL .createObjectURL(blob); }, (error) => { subscriber.error(error); }); return; } subscriber.error(new MapillaryError(`Non existent cover key: ${id}`)); }, (error) => { subscriber.error(error); }); }); } } CoverComponent.componentName = "cover"; class AttributionComponent extends Component { _activate() { this._subscriptions.push(combineLatest(this._container.configurationService.exploreUrl$, this._navigator.stateService.currentImage$, this._container.renderService.size$).pipe(map(([exploreUrl, image, size]) => { const attribution = this._makeAttribution(image.creatorUsername, exploreUrl, image.id, image.capturedAt, size.width); return { name: this._name, vNode: attribution, }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } makeImageUrl(exploreUrl, id) { return `${exploreUrl}/app/?pKey=${id}&focus=photo`; } _makeAttribution(creatorUsername, exploreUrl, imageId, capturedAt, viewportWidth) { const compact = viewportWidth <= 640; const date = this._makeDate(capturedAt, compact); const by = this._makeBy(creatorUsername, exploreUrl, imageId, compact); const compactClass = compact ? ".mapillary-attribution-compact" : ""; return virtualDom.h("div.mapillary-attribution-container" + compactClass, {}, [...by, date]); } _makeBy(creatorUsername, exploreUrl, imageId, compact) { const icon = virtualDom.h("div.mapillary-attribution-logo", []); return creatorUsername ? this._makeCreatorBy(icon, creatorUsername, exploreUrl, imageId, compact) : this._makeGeneralBy(icon, exploreUrl, imageId, compact); } _makeCreatorBy(icon, creatorUsername, exploreUrl, imageId, compact) { const mapillary = virtualDom.h("a.mapillary-attribution-icon-container", { href: exploreUrl, rel: "noreferrer", target: "_blank" }, [icon]); const content = compact ? `${creatorUsername}` : `image by ${creatorUsername}`; const imageBy = virtualDom.h("div.mapillary-attribution-username", { textContent: content }, []); const image = virtualDom.h("a.mapillary-attribution-image-container", { href: this.makeImageUrl(exploreUrl, imageId), rel: "noreferrer", target: "_blank", }, [imageBy]); return [mapillary, image]; } _makeGeneralBy(icon, exploreUrl, imageId, compact) { const imagesBy = virtualDom.h("div.mapillary-attribution-username", { textContent: "images by" }, []); const mapillary = virtualDom.h("div.mapillary-attribution-icon-container", {}, [icon]); const contributors = virtualDom.h("div.mapillary-attribution-username", { textContent: "contributors" }, []); const children = [mapillary, contributors]; if (!compact) { children.unshift(imagesBy); } const image = virtualDom.h("a.mapillary-attribution-image-container", { href: this.makeImageUrl(exploreUrl, imageId), rel: "noreferrer", target: "_blank", }, children); return [image]; } _makeDate(capturedAt, compact) { const date = new Date(capturedAt) .toDateString() .split(" "); const formatted = (date.length > 3 ? compact ? [date[3]] : [date[1], date[2] + ",", date[3]] : date).join(" "); return virtualDom.h("div.mapillary-attribution-date", { textContent: formatted }, []); } } AttributionComponent.componentName = "attribution"; /** * @class ViewportCoords * * @classdesc Provides methods for calculating 2D coordinate conversions * as well as 3D projection and unprojection. * * Basic coordinates are 2D coordinates on the [0, 1] interval and * have the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * Viewport coordinates are 2D coordinates on the [-1, 1] interval and * have the origin point in the center. The bottom left corner point is * (-1, -1) and the top right corner point is (1, 1). * * Canvas coordiantes are 2D pixel coordinates on the [0, canvasWidth] and * [0, canvasHeight] intervals. The origin point (0, 0) is in the top left * corner and the maximum value is (canvasWidth, canvasHeight) is in the * bottom right corner. * * 3D coordinates are in the topocentric world reference frame. */ class ViewportCoords { constructor() { this._unprojectDepth = 200; } /** * Convert basic coordinates to canvas coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ basicToCanvas(basicX, basicY, container, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const canvas = this.projectToCanvas(point3d, container, camera); return canvas; } /** * Convert basic coordinates to canvas coordinates safely. If 3D point is * behind camera null will be returned. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates if the basic point represents a 3D point * in front of the camera, otherwise null. */ basicToCanvasSafe(basicX, basicY, container, transform, camera) { const viewport = this.basicToViewportSafe(basicX, basicY, transform, camera); if (viewport === null) { return null; } const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Convert basic coordinates to viewport coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ basicToViewport(basicX, basicY, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const viewport = this.projectToViewport(point3d, camera); return viewport; } /** * Convert basic coordinates to viewport coordinates safely. If 3D point is * behind camera null will be returned. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} basicX - Basic X coordinate. * @param {number} basicY - Basic Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ basicToViewportSafe(basicX, basicY, transform, camera) { const point3d = transform.unprojectBasic([basicX, basicY], this._unprojectDepth); const pointCamera = this.worldToCamera(point3d, camera); if (pointCamera[2] > 0) { return null; } const viewport = this.projectToViewport(point3d, camera); return viewport; } /** * Convert camera 3D coordinates to viewport coordinates. * * @param {number} pointCamera - 3D point in camera coordinate system. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ cameraToViewport(pointCamera, camera) { const viewport = new Vector3().fromArray(pointCamera) .applyMatrix4(camera.projectionMatrix); return [viewport.x, viewport.y]; } /** * Get canvas pixel position from event. * * @param {Event} event - Event containing clientX and clientY properties. * @param {HTMLElement} element - HTML element. * @returns {Array} 2D canvas coordinates. */ canvasPosition(event, element) { const clientRect = element.getBoundingClientRect(); const canvasX = event.clientX - clientRect.left - element.clientLeft; const canvasY = event.clientY - clientRect.top - element.clientTop; return [canvasX, canvasY]; } /** * Convert canvas coordinates to basic coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D basic coordinates. */ canvasToBasic(canvasX, canvasY, container, transform, camera) { const point3d = this.unprojectFromCanvas(canvasX, canvasY, container, camera) .toArray(); const basic = transform.projectBasic(point3d); return basic; } /** * Convert canvas coordinates to viewport coordinates. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D viewport coordinates. */ canvasToViewport(canvasX, canvasY, container) { const [canvasWidth, canvasHeight] = this.containerToCanvas(container); const viewportX = 2 * canvasX / canvasWidth - 1; const viewportY = 1 - 2 * canvasY / canvasHeight; return [viewportX, viewportY]; } /** * Determines the width and height of the container in canvas coordinates. * * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D canvas coordinates. */ containerToCanvas(container) { return [container.offsetWidth, container.offsetHeight]; } /** * Determine basic distances from image to canvas corners. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * Determines the smallest basic distance for every side of the canvas. * * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} Array of basic distances as [top, right, bottom, left]. */ getBasicDistances(transform, camera) { const topLeftBasic = this.viewportToBasic(-1, 1, transform, camera); const topRightBasic = this.viewportToBasic(1, 1, transform, camera); const bottomRightBasic = this.viewportToBasic(1, -1, transform, camera); const bottomLeftBasic = this.viewportToBasic(-1, -1, transform, camera); let topBasicDistance = 0; let rightBasicDistance = 0; let bottomBasicDistance = 0; let leftBasicDistance = 0; if (topLeftBasic[1] < 0 && topRightBasic[1] < 0) { topBasicDistance = topLeftBasic[1] > topRightBasic[1] ? -topLeftBasic[1] : -topRightBasic[1]; } if (topRightBasic[0] > 1 && bottomRightBasic[0] > 1) { rightBasicDistance = topRightBasic[0] < bottomRightBasic[0] ? topRightBasic[0] - 1 : bottomRightBasic[0] - 1; } if (bottomRightBasic[1] > 1 && bottomLeftBasic[1] > 1) { bottomBasicDistance = bottomRightBasic[1] < bottomLeftBasic[1] ? bottomRightBasic[1] - 1 : bottomLeftBasic[1] - 1; } if (bottomLeftBasic[0] < 0 && topLeftBasic[0] < 0) { leftBasicDistance = bottomLeftBasic[0] > topLeftBasic[0] ? -bottomLeftBasic[0] : -topLeftBasic[0]; } return [topBasicDistance, rightBasicDistance, bottomBasicDistance, leftBasicDistance]; } /** * Determine pixel distances from image to canvas corners. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * Determines the smallest pixel distance for every side of the canvas. * * @param {HTMLElement} container - The viewer container. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} Array of pixel distances as [top, right, bottom, left]. */ getPixelDistances(container, transform, camera) { const topLeftBasic = this.viewportToBasic(-1, 1, transform, camera); const topRightBasic = this.viewportToBasic(1, 1, transform, camera); const bottomRightBasic = this.viewportToBasic(1, -1, transform, camera); const bottomLeftBasic = this.viewportToBasic(-1, -1, transform, camera); let topPixelDistance = 0; let rightPixelDistance = 0; let bottomPixelDistance = 0; let leftPixelDistance = 0; const [canvasWidth, canvasHeight] = this.containerToCanvas(container); if (topLeftBasic[1] < 0 && topRightBasic[1] < 0) { const basicX = topLeftBasic[1] > topRightBasic[1] ? topLeftBasic[0] : topRightBasic[0]; const canvas = this.basicToCanvas(basicX, 0, container, transform, camera); topPixelDistance = canvas[1] > 0 ? canvas[1] : 0; } if (topRightBasic[0] > 1 && bottomRightBasic[0] > 1) { const basicY = topRightBasic[0] < bottomRightBasic[0] ? topRightBasic[1] : bottomRightBasic[1]; const canvas = this.basicToCanvas(1, basicY, container, transform, camera); rightPixelDistance = canvas[0] < canvasWidth ? canvasWidth - canvas[0] : 0; } if (bottomRightBasic[1] > 1 && bottomLeftBasic[1] > 1) { const basicX = bottomRightBasic[1] < bottomLeftBasic[1] ? bottomRightBasic[0] : bottomLeftBasic[0]; const canvas = this.basicToCanvas(basicX, 1, container, transform, camera); bottomPixelDistance = canvas[1] < canvasHeight ? canvasHeight - canvas[1] : 0; } if (bottomLeftBasic[0] < 0 && topLeftBasic[0] < 0) { const basicY = bottomLeftBasic[0] > topLeftBasic[0] ? bottomLeftBasic[1] : topLeftBasic[1]; const canvas = this.basicToCanvas(0, basicY, container, transform, camera); leftPixelDistance = canvas[0] > 0 ? canvas[0] : 0; } return [topPixelDistance, rightPixelDistance, bottomPixelDistance, leftPixelDistance]; } /** * Determine if an event occured inside an element. * * @param {Event} event - Event containing clientX and clientY properties. * @param {HTMLElement} element - HTML element. * @returns {boolean} Value indicating if the event occured inside the element or not. */ insideElement(event, element) { const clientRect = element.getBoundingClientRect(); const minX = clientRect.left + element.clientLeft; const maxX = minX + element.clientWidth; const minY = clientRect.top + element.clientTop; const maxY = minY + element.clientHeight; return event.clientX > minX && event.clientX < maxX && event.clientY > minY && event.clientY < maxY; } /** * Project 3D world coordinates to canvas coordinates. * * @param {Array} point3D - 3D world coordinates. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ projectToCanvas(point3d, container, camera) { const viewport = this.projectToViewport(point3d, camera); const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Project 3D world coordinates to canvas coordinates safely. If 3D * point is behind camera null will be returned. * * @param {Array} point3D - 3D world coordinates. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D canvas coordinates. */ projectToCanvasSafe(point3d, container, camera) { const pointCamera = this.worldToCamera(point3d, camera); if (pointCamera[2] > 0) { return null; } const viewport = this.projectToViewport(point3d, camera); const canvas = this.viewportToCanvas(viewport[0], viewport[1], container); return canvas; } /** * Project 3D world coordinates to viewport coordinates. * * @param {Array} point3D - 3D world coordinates. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D viewport coordinates. */ projectToViewport(point3d, camera) { const viewport = new Vector3(point3d[0], point3d[1], point3d[2]) .project(camera); return [viewport.x, viewport.y]; } /** * Uproject canvas coordinates to 3D world coordinates. * * @param {number} canvasX - Canvas X coordinate. * @param {number} canvasY - Canvas Y coordinate. * @param {HTMLElement} container - The viewer container. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D world coordinates. */ unprojectFromCanvas(canvasX, canvasY, container, camera) { const viewport = this.canvasToViewport(canvasX, canvasY, container); const point3d = this.unprojectFromViewport(viewport[0], viewport[1], camera); return point3d; } /** * Unproject viewport coordinates to 3D world coordinates. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D world coordinates. */ unprojectFromViewport(viewportX, viewportY, camera) { const point3d = new Vector3(viewportX, viewportY, 1) .unproject(camera); return point3d; } /** * Convert viewport coordinates to basic coordinates. * * @description Transform origin and camera position needs to be the * equal for reliable return value. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {Transform} transform - Transform of the image to unproject from. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 2D basic coordinates. */ viewportToBasic(viewportX, viewportY, transform, camera) { const point3d = new Vector3(viewportX, viewportY, 1) .unproject(camera) .toArray(); const basic = transform.projectBasic(point3d); return basic; } /** * Convert viewport coordinates to canvas coordinates. * * @param {number} viewportX - Viewport X coordinate. * @param {number} viewportY - Viewport Y coordinate. * @param {HTMLElement} container - The viewer container. * @returns {Array} 2D canvas coordinates. */ viewportToCanvas(viewportX, viewportY, container) { const [canvasWidth, canvasHeight] = this.containerToCanvas(container); const canvasX = canvasWidth * (viewportX + 1) / 2; const canvasY = -canvasHeight * (viewportY - 1) / 2; return [canvasX, canvasY]; } /** * Convert 3D world coordinates to 3D camera coordinates. * * @param {number} point3D - 3D point in world coordinate system. * @param {THREE.Camera} camera - Camera used in rendering. * @returns {Array} 3D camera coordinates. */ worldToCamera(point3d, camera) { const pointCamera = new Vector3(point3d[0], point3d[1], point3d[2]) .applyMatrix4(camera.matrixWorldInverse); return pointCamera.toArray(); } } /** * Enumeration for component size. * @enum {number} * @readonly * @description May be used by a component to allow for resizing * of the UI elements rendered by the component. */ exports.ComponentSize = void 0; (function (ComponentSize) { /** * Automatic size. The size of the elements will automatically * change at a predefined threshold. */ ComponentSize[ComponentSize["Automatic"] = 0] = "Automatic"; /** * Large size. The size of the elements will be fixed until another * component size is configured. */ ComponentSize[ComponentSize["Large"] = 1] = "Large"; /** * Small size. The size of the elements will be fixed until another * component size is configured. */ ComponentSize[ComponentSize["Small"] = 2] = "Small"; })(exports.ComponentSize || (exports.ComponentSize = {})); /** * @class BearingComponent * * @classdesc Component for indicating bearing and field of view. * * @example * ```js * var viewer = new Viewer({ ... }); * var bearingComponent = viewer.getComponent("bearing"); * bearingComponent.configure({ size: ComponentSize.Small }); * ``` */ class BearingComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._spatial = new Spatial(); this._viewportCoords = new ViewportCoords(); this._svgNamespace = "http://www.w3.org/2000/svg"; this._distinctThreshold = Math.PI / 360; this._animationSpeed = 0.075; } _activate() { const subs = this._subscriptions; const cameraBearingFov$ = this._container.renderService.renderCamera$.pipe(map((rc) => { let vFov = this._spatial.degToRad(rc.perspective.fov); let hFov = rc.perspective.aspect === Number.POSITIVE_INFINITY ? Math.PI : Math.atan(rc.perspective.aspect * Math.tan(0.5 * vFov)) * 2; return [this._spatial.azimuthalToBearing(rc.rotation.phi), hFov]; }), distinctUntilChanged((a1, a2) => { return Math.abs(a2[0] - a1[0]) < this._distinctThreshold && Math.abs(a2[1] - a1[1]) < this._distinctThreshold; })); const imageFov$ = combineLatest(this._navigator.stateService.currentState$.pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; })), this._navigator.panService.panImages$).pipe(map(([frame, panImages]) => { const image = frame.state.currentImage; const transform = frame.state.currentTransform; if (isSpherical(image.cameraType)) { return [Math.PI, Math.PI]; } const currentProjectedPoints = this._computeProjectedPoints(transform); const hFov = this._spatial .degToRad(this._computeHorizontalFov(currentProjectedPoints)); let hFovLeft = hFov / 2; let hFovRight = hFov / 2; for (const [n, , f] of panImages) { const diff = this._spatial.wrap(n.compassAngle - image.compassAngle, -180, 180); if (diff < 0) { hFovLeft = this._spatial.degToRad(Math.abs(diff)) + f / 2; } else { hFovRight = this._spatial.degToRad(Math.abs(diff)) + f / 2; } } return [hFovLeft, hFovRight]; }), distinctUntilChanged(([hFovLeft1, hFovRight1], [hFovLeft2, hFovRight2]) => { return Math.abs(hFovLeft2 - hFovLeft1) < this._distinctThreshold && Math.abs(hFovRight2 - hFovRight1) < this._distinctThreshold; })); const offset$ = combineLatest(this._navigator.stateService.currentState$.pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; })), this._container.renderService.bearing$).pipe(map(([frame, bearing]) => { const offset = this._spatial.degToRad(frame.state.currentImage.compassAngle - bearing); return offset; })); const imageFovOperation$ = new Subject(); const smoothImageFov$ = imageFovOperation$.pipe(scan((state, operation) => { return operation(state); }, { alpha: 0, curr: [0, 0, 0], prev: [0, 0, 0] }), map((state) => { const alpha = MathUtils.smootherstep(state.alpha, 0, 1); const curr = state.curr; const prev = state.prev; return [ this._interpolate(prev[0], curr[0], alpha), this._interpolate(prev[1], curr[1], alpha), ]; })); subs.push(imageFov$.pipe(map((nbf) => { return (state) => { const a = MathUtils.smootherstep(state.alpha, 0, 1); const c = state.curr; const p = state.prev; const prev = [ this._interpolate(p[0], c[0], a), this._interpolate(p[1], c[1], a), ]; const curr = nbf.slice(); return { alpha: 0, curr: curr, prev: prev, }; }; })) .subscribe(imageFovOperation$)); subs.push(imageFov$.pipe(switchMap(() => { return this._container.renderService.renderCameraFrame$.pipe(skip(1), scan((alpha) => { return alpha + this._animationSpeed; }, 0), takeWhile((alpha) => { return alpha <= 1 + this._animationSpeed; }), map((alpha) => { return Math.min(alpha, 1); })); }), map((alpha) => { return (nbfState) => { return { alpha: alpha, curr: nbfState.curr.slice(), prev: nbfState.prev.slice(), }; }; })) .subscribe(imageFovOperation$)); const imageBearingFov$ = combineLatest(offset$, smoothImageFov$).pipe(map(([offset, fov]) => { return [offset, fov[0], fov[1]]; })); subs.push(combineLatest(cameraBearingFov$, imageBearingFov$, this._configuration$, this._container.renderService.size$).pipe(map(([[cb, cf], [no, nfl, nfr], configuration, size]) => { const background = this._createBackground(cb); const fovIndicator = this._createFovIndicator(nfl, nfr, no); const north = this._createNorth(cb); const cameraSector = this._createCircleSectorCompass(this._createCircleSector(Math.max(Math.PI / 20, cf), "#FFF")); const compact = configuration.size === exports.ComponentSize.Small || configuration.size === exports.ComponentSize.Automatic && size.width < 640 ? ".mapillary-bearing-compact" : ""; return { name: this._name, vNode: virtualDom.h("div.mapillary-bearing-indicator-container" + compact, { oncontextmenu: (event) => { event.preventDefault(); } }, [ background, fovIndicator, north, cameraSector, ]), }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { size: exports.ComponentSize.Automatic }; } _createFovIndicator(fovLeft, fovRigth, offset) { const arc = this._createFovArc(fovLeft, fovRigth); const group = virtualDom.h("g", { attributes: { transform: "translate(18,18)" }, namespace: this._svgNamespace, }, [arc]); const svg = virtualDom.h("svg", { attributes: { viewBox: "0 0 36 36" }, namespace: this._svgNamespace, style: { height: "36px", left: "2px", position: "absolute", top: "2px", transform: `rotateZ(${this._spatial.radToDeg(offset)}deg)`, width: "36px", }, }, [group]); return svg; } _createFovArc(fovLeft, fovRigth) { const radius = 16.75; const strokeWidth = 2.5; const fov = fovLeft + fovRigth; if (fov > 2 * Math.PI - Math.PI / 90) { return virtualDom.h("circle", { attributes: { cx: "0", cy: "0", "fill-opacity": "0", r: `${radius}`, stroke: "#FFF", "stroke-width": `${strokeWidth}`, }, namespace: this._svgNamespace, }, []); } let arcStart = -Math.PI / 2 - fovLeft; let arcEnd = arcStart + fov; let startX = radius * Math.cos(arcStart); let startY = radius * Math.sin(arcStart); let endX = radius * Math.cos(arcEnd); let endY = radius * Math.sin(arcEnd); let largeArc = fov >= Math.PI ? 1 : 0; let description = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArc} 1 ${endX} ${endY}`; return virtualDom.h("path", { attributes: { d: description, "fill-opacity": "0", stroke: "#FFF", "stroke-width": `${strokeWidth}`, }, namespace: this._svgNamespace, }, []); } _createCircleSectorCompass(cameraSector) { let group = virtualDom.h("g", { attributes: { transform: "translate(1,1)" }, namespace: this._svgNamespace, }, [cameraSector]); let svg = virtualDom.h("svg", { attributes: { viewBox: "0 0 2 2" }, namespace: this._svgNamespace, style: { height: "26px", left: "7px", position: "absolute", top: "7px", width: "26px", }, }, [group]); return svg; } _createCircleSector(fov, fill) { if (fov > 2 * Math.PI - Math.PI / 90) { return virtualDom.h("circle", { attributes: { cx: "0", cy: "0", fill: fill, r: "1" }, namespace: this._svgNamespace, }, []); } let arcStart = -Math.PI / 2 - fov / 2; let arcEnd = arcStart + fov; let startX = Math.cos(arcStart); let startY = Math.sin(arcStart); let endX = Math.cos(arcEnd); let endY = Math.sin(arcEnd); let largeArc = fov >= Math.PI ? 1 : 0; let description = `M 0 0 ${startX} ${startY} A 1 1 0 ${largeArc} 1 ${endX} ${endY}`; return virtualDom.h("path", { attributes: { d: description, fill: fill }, namespace: this._svgNamespace, }, []); } _createNorth(bearing) { const north = virtualDom.h("div.mapillary-bearing-north", []); const container = virtualDom.h("div.mapillary-bearing-north-container", { style: { transform: `rotateZ(${this._spatial.radToDeg(-bearing)}deg)` } }, [north]); return container; } _createBackground(bearing) { return virtualDom.h("div.mapillary-bearing-indicator-background", { style: { transform: `rotateZ(${this._spatial.radToDeg(-bearing)}deg)` } }, [ virtualDom.h("div.mapillary-bearing-indicator-background-circle", []), virtualDom.h("div.mapillary-bearing-indicator-background-arrow-container", [ virtualDom.h("div.mapillary-bearing-indicator-background-arrow", []), ]), ]); } _computeProjectedPoints(transform) { const vertices = [[1, 0]]; const directions = [[0, 0.5]]; const pointsPerLine = 12; return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords) .map(([x, y]) => [Math.abs(x), Math.abs(y)]); } _computeHorizontalFov(projectedPoints) { const fovs = projectedPoints .map((projectedPoint) => { return this._coordToFov(projectedPoint[0]); }); const fov = Math.min(...fovs); return fov; } _coordToFov(x) { return this._spatial.radToDeg(2 * Math.atan(x)); } _interpolate(x1, x2, alpha) { return (1 - alpha) * x1 + alpha * x2; } } BearingComponent.componentName = "bearing"; class CacheComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); } _activate() { const subs = this._subscriptions; subs.push(combineLatest(this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; }), filter((status) => { return status.cached; })), this._configuration$).pipe(switchMap((nc) => { let status = nc[0]; let configuration = nc[1]; let sequenceDepth = Math.max(0, Math.min(4, configuration.depth.sequence)); let next$ = this._cache$(status.edges, exports.NavigationDirection.Next, sequenceDepth); let prev$ = this._cache$(status.edges, exports.NavigationDirection.Prev, sequenceDepth); return merge(next$, prev$).pipe(catchError((error) => { console.error("Failed to cache sequence edges.", error); return empty(); })); })) .subscribe(() => { })); subs.push(combineLatest(this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return combineLatest(of(image), image.spatialEdges$.pipe(filter((status) => { return status.cached; }))); })), this._configuration$).pipe(switchMap(([[image, edgeStatus], configuration]) => { let edges = edgeStatus.edges; let depth = configuration.depth; let sphericalDepth = Math.max(0, Math.min(2, depth.spherical)); let stepDepth = isSpherical(image.cameraType) ? 0 : Math.max(0, Math.min(3, depth.step)); let turnDepth = isSpherical(image.cameraType) ? 0 : Math.max(0, Math.min(1, depth.turn)); let spherical$ = this._cache$(edges, exports.NavigationDirection.Spherical, sphericalDepth); let forward$ = this._cache$(edges, exports.NavigationDirection.StepForward, stepDepth); let backward$ = this._cache$(edges, exports.NavigationDirection.StepBackward, stepDepth); let left$ = this._cache$(edges, exports.NavigationDirection.StepLeft, stepDepth); let right$ = this._cache$(edges, exports.NavigationDirection.StepRight, stepDepth); let turnLeft$ = this._cache$(edges, exports.NavigationDirection.TurnLeft, turnDepth); let turnRight$ = this._cache$(edges, exports.NavigationDirection.TurnRight, turnDepth); let turnU$ = this._cache$(edges, exports.NavigationDirection.TurnU, turnDepth); return merge(forward$, backward$, left$, right$, spherical$, turnLeft$, turnRight$, turnU$).pipe(catchError((error) => { console.error("Failed to cache spatial edges.", error); return empty(); })); })) .subscribe(() => { })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { depth: { spherical: 1, sequence: 2, step: 1, turn: 0 } }; } _cache$(edges, direction, depth) { return zip(of(edges), of(depth)).pipe(expand((ed) => { let es = ed[0]; let d = ed[1]; let edgesDepths$ = []; if (d > 0) { for (let edge of es) { if (edge.data.direction === direction) { edgesDepths$.push(zip(this._navigator.graphService.cacheImage$(edge.target).pipe(mergeMap((n) => { return this._imageToEdges$(n, direction); })), of(d - 1))); } } } return from(edgesDepths$).pipe(mergeAll()); }), skip(1)); } _imageToEdges$(image, direction) { return ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), map((status) => { return status.edges; })); } } CacheComponent.componentName = "cache"; /** * @class CancelMapillaryError * * @classdesc Error thrown when a move to request has been * cancelled before completing because of a subsequent request. */ class CancelMapillaryError extends MapillaryError { constructor(message) { super(message != null ? message : "The request was cancelled."); Object.setPrototypeOf(this, CancelMapillaryError.prototype); this.name = "CancelMapillaryError"; } } /** * @class DirectionDOMCalculator * @classdesc Helper class for calculating DOM CSS properties. */ class DirectionDOMCalculator { constructor(configuration, size) { this._spatial = new Spatial(); this._minThresholdWidth = 320; this._maxThresholdWidth = 1480; this._minThresholdHeight = 240; this._maxThresholdHeight = 820; this._configure(configuration); this._resize(size); this._reset(); } get minWidth() { return this._minWidth; } get maxWidth() { return this._maxWidth; } get containerWidth() { return this._containerWidth; } get containerWidthCss() { return this._containerWidthCss; } get containerMarginCss() { return this._containerMarginCss; } get containerLeftCss() { return this._containerLeftCss; } get containerHeight() { return this._containerHeight; } get containerHeightCss() { return this._containerHeightCss; } get containerBottomCss() { return this._containerBottomCss; } get stepCircleSize() { return this._stepCircleSize; } get stepCircleSizeCss() { return this._stepCircleSizeCss; } get stepCircleMarginCss() { return this._stepCircleMarginCss; } get turnCircleSize() { return this._turnCircleSize; } get turnCircleSizeCss() { return this._turnCircleSizeCss; } get outerRadius() { return this._outerRadius; } get innerRadius() { return this._innerRadius; } get shadowOffset() { return this._shadowOffset; } /** * Configures the min and max width values. * * @param {DirectionConfiguration} configuration Configuration * with min and max width values. */ configure(configuration) { this._configure(configuration); this._reset(); } /** * Resizes all properties according to the width and height * of the size object. * * @param {ViewportSize} size The size of the container element. */ resize(size) { this._resize(size); this._reset(); } /** * Calculates the coordinates on the unit circle for an angle. * * @param {number} angle Angle in radians. * @returns {Array} The x and y coordinates on the unit circle. */ angleToCoordinates(angle) { return [Math.cos(angle), Math.sin(angle)]; } /** * Calculates the coordinates on the unit circle for the * relative angle between the first and second angle. * * @param {number} first Angle in radians. * @param {number} second Angle in radians. * @returns {Array} The x and y coordinates on the unit circle * for the relative angle between the first and second angle. */ relativeAngleToCoordiantes(first, second) { let relativeAngle = this._spatial.wrapAngle(first - second); return this.angleToCoordinates(relativeAngle); } _configure(configuration) { this._minWidth = configuration.minWidth; this._maxWidth = this._getMaxWidth(configuration.minWidth, configuration.maxWidth); } _resize(size) { this._elementWidth = size.width; this._elementHeight = size.height; } _reset() { this._containerWidth = this._getContainerWidth(this._elementWidth, this._elementHeight); this._containerHeight = this._getContainerHeight(this.containerWidth); this._stepCircleSize = this._getStepCircleDiameter(this._containerHeight); this._turnCircleSize = this._getTurnCircleDiameter(this.containerHeight); this._outerRadius = this._getOuterRadius(this._containerHeight); this._innerRadius = this._getInnerRadius(this._containerHeight); this._shadowOffset = 3; this._containerWidthCss = this._numberToCssPixels(this._containerWidth); this._containerMarginCss = this._numberToCssPixels(-0.5 * this._containerWidth); this._containerLeftCss = this._numberToCssPixels(Math.floor(0.5 * this._elementWidth)); this._containerHeightCss = this._numberToCssPixels(this._containerHeight); this._containerBottomCss = this._numberToCssPixels(Math.floor(-0.08 * this._containerHeight)); this._stepCircleSizeCss = this._numberToCssPixels(this._stepCircleSize); this._stepCircleMarginCss = this._numberToCssPixels(-0.5 * this._stepCircleSize); this._turnCircleSizeCss = this._numberToCssPixels(this._turnCircleSize); } _getContainerWidth(elementWidth, elementHeight) { let relativeWidth = (elementWidth - this._minThresholdWidth) / (this._maxThresholdWidth - this._minThresholdWidth); let relativeHeight = (elementHeight - this._minThresholdHeight) / (this._maxThresholdHeight - this._minThresholdHeight); let coeff = Math.max(0, Math.min(1, Math.min(relativeWidth, relativeHeight))); coeff = 0.04 * Math.round(25 * coeff); return this._minWidth + coeff * (this._maxWidth - this._minWidth); } _getContainerHeight(containerWidth) { return 0.77 * containerWidth; } _getStepCircleDiameter(containerHeight) { return 0.34 * containerHeight; } _getTurnCircleDiameter(containerHeight) { return 0.3 * containerHeight; } _getOuterRadius(containerHeight) { return 0.31 * containerHeight; } _getInnerRadius(containerHeight) { return 0.125 * containerHeight; } _numberToCssPixels(value) { return value + "px"; } _getMaxWidth(value, minWidth) { return value > minWidth ? value : minWidth; } } /** * @class DirectionDOMRenderer * @classdesc DOM renderer for direction arrows. */ class DirectionDOMRenderer { constructor(configuration, size) { this._isEdge = false; this._spatial = new Spatial(); this._calculator = new DirectionDOMCalculator(configuration, size); this._image = null; this._rotation = { phi: 0, theta: 0 }; this._epsilon = 0.5 * Math.PI / 180; this._highlightKey = null; this._distinguishSequence = false; this._needsRender = false; this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; this._stepDirections = [ exports.NavigationDirection.StepForward, exports.NavigationDirection.StepBackward, exports.NavigationDirection.StepLeft, exports.NavigationDirection.StepRight, ]; this._turnDirections = [ exports.NavigationDirection.TurnLeft, exports.NavigationDirection.TurnRight, exports.NavigationDirection.TurnU, ]; this._turnNames = {}; this._turnNames[exports.NavigationDirection.TurnLeft] = "mapillary-direction-turn-left"; this._turnNames[exports.NavigationDirection.TurnRight] = "mapillary-direction-turn-right"; this._turnNames[exports.NavigationDirection.TurnU] = "mapillary-direction-turn-around"; // detects IE 8-11, then Edge 20+. let isIE = !!document.documentMode; this._isEdge = !isIE && !!window.StyleMedia; } /** * Get needs render. * * @returns {boolean} Value indicating whether render should be called. */ get needsRender() { return this._needsRender; } /** * Renders virtual DOM elements. * * @description Calling render resets the needs render property. */ render(navigator) { this._needsRender = false; let rotation = this._rotation; let steps = []; let turns = []; if (isSpherical(this._image.cameraType)) { steps = steps.concat(this._createSphericalArrows(navigator, rotation)); } else { steps = steps.concat(this._createPerspectiveToSphericalArrows(navigator, rotation)); steps = steps.concat(this._createStepArrows(navigator, rotation)); turns = turns.concat(this._createTurnArrows(navigator)); } return this._getContainer(steps, turns, rotation); } setEdges(edgeStatus, sequence) { this._setEdges(edgeStatus, sequence); this._setNeedsRender(); } /** * Set image for which to show edges. * * @param {Image} image */ setImage(image) { this._image = image; this._clearEdges(); this._setNeedsRender(); } /** * Set the render camera to use for calculating rotations. * * @param {RenderCamera} renderCamera */ setRenderCamera(renderCamera) { let rotation = renderCamera.rotation; if (Math.abs(rotation.phi - this._rotation.phi) < this._epsilon) { return; } this._rotation = rotation; this._setNeedsRender(); } /** * Set configuration values. * * @param {DirectionConfiguration} configuration */ setConfiguration(configuration) { let needsRender = false; if (this._highlightKey !== configuration.highlightId || this._distinguishSequence !== configuration.distinguishSequence) { this._highlightKey = configuration.highlightId; this._distinguishSequence = configuration.distinguishSequence; needsRender = true; } if (this._calculator.minWidth !== configuration.minWidth || this._calculator.maxWidth !== configuration.maxWidth) { this._calculator.configure(configuration); needsRender = true; } if (needsRender) { this._setNeedsRender(); } } /** * Detect the element"s width and height and resize * elements accordingly. * * @param {ViewportSize} size Size of vßiewer container element. */ resize(size) { this._calculator.resize(size); this._setNeedsRender(); } _setNeedsRender() { if (this._image != null) { this._needsRender = true; } } _clearEdges() { this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; } _setEdges(edgeStatus, sequence) { this._stepEdges = []; this._turnEdges = []; this._sphericalEdges = []; this._sequenceEdgeKeys = []; for (let edge of edgeStatus.edges) { let direction = edge.data.direction; if (this._stepDirections.indexOf(direction) > -1) { this._stepEdges.push(edge); continue; } if (this._turnDirections.indexOf(direction) > -1) { this._turnEdges.push(edge); continue; } if (edge.data.direction === exports.NavigationDirection.Spherical) { this._sphericalEdges.push(edge); } } if (this._distinguishSequence && sequence != null) { let edges = this._sphericalEdges .concat(this._stepEdges) .concat(this._turnEdges); for (let edge of edges) { let edgeKey = edge.target; for (let sequenceKey of sequence.imageIds) { if (sequenceKey === edgeKey) { this._sequenceEdgeKeys.push(edgeKey); break; } } } } } _createSphericalArrows(navigator, rotation) { let arrows = []; for (let sphericalEdge of this._sphericalEdges) { arrows.push(this._createVNodeByKey(navigator, sphericalEdge.target, sphericalEdge.data.worldMotionAzimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-spherical")); } for (let stepEdge of this._stepEdges) { arrows.push(this._createSphericalToPerspectiveArrow(navigator, stepEdge.target, stepEdge.data.worldMotionAzimuth, rotation, stepEdge.data.direction)); } return arrows; } _createSphericalToPerspectiveArrow(navigator, key, azimuth, rotation, direction) { let threshold = Math.PI / 8; let relativePhi = rotation.phi; switch (direction) { case exports.NavigationDirection.StepBackward: relativePhi = rotation.phi - Math.PI; break; case exports.NavigationDirection.StepLeft: relativePhi = rotation.phi + Math.PI / 2; break; case exports.NavigationDirection.StepRight: relativePhi = rotation.phi - Math.PI / 2; break; } if (Math.abs(this._spatial.wrapAngle(azimuth - relativePhi)) < threshold) { return this._createVNodeByKey(navigator, key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-step"); } return this._createVNodeInactive(key, azimuth, rotation); } _createPerspectiveToSphericalArrows(navigator, rotation) { let arrows = []; for (let sphericalEdge of this._sphericalEdges) { arrows.push(this._createVNodeByKey(navigator, sphericalEdge.target, sphericalEdge.data.worldMotionAzimuth, rotation, this._calculator.innerRadius, "mapillary-direction-arrow-spherical", true)); } return arrows; } _createStepArrows(navigator, rotation) { let arrows = []; for (let stepEdge of this._stepEdges) { arrows.push(this._createVNodeByDirection(navigator, stepEdge.target, stepEdge.data.worldMotionAzimuth, rotation, stepEdge.data.direction)); } return arrows; } _createTurnArrows(navigator) { let turns = []; for (let turnEdge of this._turnEdges) { let direction = turnEdge.data.direction; let name = this._turnNames[direction]; turns.push(this._createVNodeByTurn(navigator, turnEdge.target, name, direction)); } return turns; } _createVNodeByKey(navigator, key, azimuth, rotation, offset, className, shiftVertically) { let onClick = (e) => { navigator.moveTo$(key) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; return this._createVNode(key, azimuth, rotation, offset, className, "mapillary-direction-circle", onClick, shiftVertically); } _createVNodeByDirection(navigator, key, azimuth, rotation, direction) { let onClick = (e) => { navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; return this._createVNode(key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-step", "mapillary-direction-circle", onClick); } _createVNodeByTurn(navigator, key, className, direction) { let onClick = (e) => { navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }; let style = { height: this._calculator.turnCircleSizeCss, transform: "rotate(0)", width: this._calculator.turnCircleSizeCss, }; switch (direction) { case exports.NavigationDirection.TurnLeft: style.left = "5px"; style.top = "5px"; break; case exports.NavigationDirection.TurnRight: style.right = "5px"; style.top = "5px"; break; case exports.NavigationDirection.TurnU: style.left = "5px"; style.bottom = "5px"; break; } let circleProperties = { attributes: { "data-id": key, }, onclick: onClick, style: style, }; let circleClassName = "mapillary-direction-turn-circle"; if (this._sequenceEdgeKeys.indexOf(key) > -1) { circleClassName += "-sequence"; } if (this._highlightKey === key) { circleClassName += "-highlight"; } let turn = virtualDom.h(`div.${className}`, {}, []); return virtualDom.h("div." + circleClassName, circleProperties, [turn]); } _createVNodeInactive(key, azimuth, rotation) { return this._createVNode(key, azimuth, rotation, this._calculator.outerRadius, "mapillary-direction-arrow-inactive", "mapillary-direction-circle-inactive"); } _createVNode(key, azimuth, rotation, radius, className, circleClassName, onClick, shiftVertically) { let translation = this._calculator.angleToCoordinates(azimuth - rotation.phi); // rotate 90 degrees clockwise and flip over X-axis let translationX = Math.round(-radius * translation[1] + 0.5 * this._calculator.containerWidth); let translationY = Math.round(-radius * translation[0] + 0.5 * this._calculator.containerHeight); let shadowTranslation = this._calculator.relativeAngleToCoordiantes(azimuth, rotation.phi); let shadowOffset = this._calculator.shadowOffset; let shadowTranslationX = -shadowOffset * shadowTranslation[1]; let shadowTranslationY = shadowOffset * shadowTranslation[0]; let filter = `drop-shadow(${shadowTranslationX}px ${shadowTranslationY}px 1px rgba(0,0,0,0.8))`; let properties = { style: { "-webkit-filter": filter, filter: filter, }, }; let chevron = virtualDom.h("div." + className, properties, []); let azimuthDeg = -this._spatial.radToDeg(azimuth - rotation.phi); let circleTransform = shiftVertically ? `translate(${translationX}px, ${translationY}px) rotate(${azimuthDeg}deg) translateZ(-0.01px)` : `translate(${translationX}px, ${translationY}px) rotate(${azimuthDeg}deg)`; let circleProperties = { attributes: { "data-id": key }, onclick: onClick, style: { height: this._calculator.stepCircleSizeCss, marginLeft: this._calculator.stepCircleMarginCss, marginTop: this._calculator.stepCircleMarginCss, transform: circleTransform, width: this._calculator.stepCircleSizeCss, }, }; if (this._sequenceEdgeKeys.indexOf(key) > -1) { circleClassName += "-sequence"; } if (this._highlightKey === key) { circleClassName += "-highlight"; } return virtualDom.h("div." + circleClassName, circleProperties, [chevron]); } _getContainer(steps, turns, rotation) { // edge does not handle hover on perspective transforms. let transform = this._isEdge ? "rotateX(60deg)" : `perspective(${this._calculator.containerWidthCss}) rotateX(60deg)`; let properties = { oncontextmenu: (event) => { event.preventDefault(); }, style: { bottom: this._calculator.containerBottomCss, height: this._calculator.containerHeightCss, left: this._calculator.containerLeftCss, marginLeft: this._calculator.containerMarginCss, transform: transform, width: this._calculator.containerWidthCss, }, }; return virtualDom.h("div.mapillary-direction-perspective", properties, turns.concat(steps)); } } /** * @class DirectionComponent * @classdesc Component showing navigation arrows for steps and turns. */ class DirectionComponent extends Component { /** @ignore */ constructor(name, container, navigator, directionDOMRenderer) { super(name, container, navigator); this._renderer = !!directionDOMRenderer ? directionDOMRenderer : new DirectionDOMRenderer(this.defaultConfiguration, { height: container.container.offsetHeight, width: container.container.offsetWidth }); this._hoveredIdSubject$ = new Subject(); this._hoveredId$ = this._hoveredIdSubject$.pipe(share()); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } _activate() { const subs = this._subscriptions; subs.push(this._configuration$ .subscribe((configuration) => { this._renderer.setConfiguration(configuration); })); subs.push(this._container.renderService.size$ .subscribe((size) => { this._renderer.resize(size); })); subs.push(this._navigator.stateService.currentImage$.pipe(tap((image) => { this._container.domRenderer.render$.next({ name: this._name, vNode: virtualDom.h("div", {}, []) }); this._renderer.setImage(image); }), withLatestFrom(this._configuration$), switchMap(([image, configuration]) => { return combineLatest(image.spatialEdges$, configuration.distinguishSequence ? this._navigator.graphService .cacheSequence$(image.sequenceId).pipe(catchError((error) => { console.error(`Failed to cache sequence (${image.sequenceId})`, error); return of(null); })) : of(null)); })) .subscribe(([edgeStatus, sequence]) => { this._renderer.setEdges(edgeStatus, sequence); })); subs.push(this._container.renderService.renderCameraFrame$.pipe(tap((renderCamera) => { this._renderer.setRenderCamera(renderCamera); }), map(() => { return this._renderer; }), filter((renderer) => { return renderer.needsRender; }), map((renderer) => { return { name: this._name, vNode: renderer.render(this._navigator) }; })) .subscribe(this._container.domRenderer.render$)); subs.push(combineLatest(this._container.domRenderer.element$, this._container.renderService.renderCamera$, this._container.mouseService.mouseMove$.pipe(startWith(null)), this._container.mouseService.mouseUp$.pipe(startWith(null))).pipe(map(([element]) => { let elements = element.getElementsByClassName("mapillary-direction-perspective"); for (let i = 0; i < elements.length; i++) { let hovered = elements.item(i).querySelector(":hover"); if (hovered != null && hovered.hasAttribute("data-id")) { return hovered.getAttribute("data-id"); } } return null; }), distinctUntilChanged()) .subscribe(this._hoveredIdSubject$)); subs.push(this._hoveredId$ .subscribe((id) => { const type = "hover"; const event = { id, target: this, type, }; this.fire(type, event); })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { distinguishSequence: false, maxWidth: 460, minWidth: 260, }; } } /** @inheritdoc */ DirectionComponent.componentName = "direction"; var common$1 = /* glsl */ ` #define PI 3.141592653589793 #define PI2 6.283185307179586 #define POSITIVE_INFINITY 3.402823466e+38 `; var coordinates = /* glsl */ ` vec2 sfmToUv(const in vec2 sfm, const in vec2 scale) { float u = scale.x * sfm.x + 0.5; float v = - scale.y * sfm.y + 0.5; return vec2(u, v); } `; var bearing_fragment = /* glsl */ ` vec3 bearing = normalize(positionExtrinsic.xyz); `; var map_color_fragment = /* glsl */ ` vec2 uv = sfmToUv(sfm, scale); float u = uv.x; float v = uv.y; vec4 mapColor; if (u >= 0. && u <= 1. && v >= 0. && v <= 1.) { mapColor = texture2D(map, vec2(u, v)); mapColor.a = opacity; } else { mapColor = vec4(0.0, 0.0, 0.0, 0.0); } `; var gl_frag_color_fragment = /* glsl */ ` gl_FragColor = mapColor; `; var precision_fragment = /* glsl */ ` #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif `; var uniforms_fragment = /* glsl */ ` uniform sampler2D map; uniform float opacity; uniform vec2 scale; `; var varyings_fragment = /* glsl */ ` varying vec4 positionExtrinsic; `; var extrinsic_vertex = /* glsl */ ` positionExtrinsic = extrinsicMatrix * vec4(position, 1.0); `; var gl_position_vertex = /* glsl */ ` gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); `; var uniforms_vertex = /* glsl */ ` uniform mat4 extrinsicMatrix; `; var varyings_vertex = /* glsl */ ` varying vec4 positionExtrinsic; `; // tslint:disable-next-line:variable-name const ShaderChunk = { // Definitions and functions common: common$1, coordinates, // Fragment bearing_fragment, map_color_fragment, gl_frag_color_fragment, precision_fragment, uniforms_fragment, varyings_fragment, // Vertex extrinsic_vertex, gl_position_vertex, uniforms_vertex, varyings_vertex, }; const expandPattern = /^[ ]*#expand +<([wd./]+)>/gm; const includePattern = /^[ ]*#include +<([wd./]+)>/gm; function expandParameters(parameters) { const keys = Object.keys(parameters); if (keys.length === 0) { return ""; } const variables = keys .map(key => `float ${key};`); const expansion = ` ${variables.map(v => `uniform ${v}`).join(" ")} struct Parameters { ${variables.map(v => ` ${v}`).join(" ")} }; `; return expansion; } function expandUniforms(uniforms) { const keys = Object.keys(uniforms); if (keys.length === 0) { return ""; } const variables = []; for (const key of keys) { const value = uniforms[key]; if (typeof value === "boolean") { variables.push(`bool ${key};`); } else if (typeof value === "number") { variables.push(`float ${key};`); } else if (value instanceof Array) { switch (value.length) { case 2: variables.push(`vec2 ${key};`); break; case 3: variables.push(`vec3 ${key};`); break; case 4: variables.push(`vec4 ${key};`); break; case 9: variables.push(`mat3 ${key};`); break; case 16: variables.push(`mat4 ${key};`); break; default: throw new Error("Can not #expand vector of length <" + value.length + ">"); } } else { throw new Error("Can not #expand instance <" + value + ">"); } } const expansion = ` ${variables.map(v => `uniform ${v}`).join(" ")} struct Uniforms { ${variables.map(v => ` ${v}`).join(" ")} }; `; return expansion; } function expandProjectToSfmDefinition(definition) { return definition; } function expandProjectToSfmInvocation(parameters, uniforms) { const parameterKeys = Object.keys(parameters); const uniformKeys = Object.keys(uniforms); const p = parameterKeys.length > 0 ? `Parameters parameters = Parameters(${parameterKeys.join(", ")});` : ""; const u = uniformKeys.length > 0 ? `Uniforms uniforms = Uniforms(${uniformKeys.join(", ")});` : ""; const project = `vec2 sfm = projectToSfm(bearing${parameterKeys.length > 0 ? ", parameters" : ""}${uniformKeys.length > 0 ? ", uniforms" : ""});`; const expansion = ` ${p} ${u} ${project} `; return expansion; } function includeReplacer(_match, include) { const chunk = ShaderChunk[include]; if (chunk === undefined) { throw new Error("Can not resolve #include <" + include + ">"); } return resolveIncludes(chunk); } function resolveIncludes(shader) { return shader.replace(includePattern, includeReplacer); } function resolveExpands(shader, projectToSfmFunction, parameters, uniforms) { function expandReplacer(_match, expand) { switch (expand) { case "parameters": return expandParameters(parameters); case "uniforms": return expandUniforms(uniforms); case "project_to_sfm_definition": return expandProjectToSfmDefinition(projectToSfmFunction); case "project_to_sfm_invocation": return expandProjectToSfmInvocation(parameters, uniforms); default: throw new Error("Can not resolve #expand <" + expand + ">"); } } return shader.replace(expandPattern, expandReplacer); } function resolveShader(shader, camera) { return resolveExpands(resolveIncludes(shader), camera.projectToSfmFunction, camera.parameters, camera.uniforms); } function makeCameraUniforms(camera) { const cameraUniforms = {}; const { parameters, uniforms } = camera; for (const key in parameters) { if (parameters.hasOwnProperty(key)) { cameraUniforms[key] = { value: parameters[key] }; } } for (const key in uniforms) { if (!uniforms.hasOwnProperty(key)) { continue; } const value = uniforms[key]; if (value instanceof Array) { switch (value.length) { case 2: cameraUniforms[key] = { value: new Vector2().fromArray(value), }; break; case 3: cameraUniforms[key] = { value: new Vector3().fromArray(value), }; break; case 4: cameraUniforms[key] = { value: new Vector4().fromArray(value), }; break; case 9: cameraUniforms[key] = { value: new Matrix3().fromArray(value), }; break; case 16: cameraUniforms[key] = { value: new Matrix4().fromArray(value), }; break; default: throw new Error("Uniform vector of length <" + value.length + "> not supported"); } } else { cameraUniforms[key] = { value: uniforms[key] }; } } return cameraUniforms; } class MeshFactory { constructor(imagePlaneDepth, imageSphereRadius) { this._imagePlaneDepth = imagePlaneDepth != null ? imagePlaneDepth : 200; this._imageSphereRadius = imageSphereRadius != null ? imageSphereRadius : 200; } createMesh(image, transform, shader) { const texture = this._createTexture(image.image); const materialParameters = this._createMaterialParameters(transform, texture, shader); const material = new ShaderMaterial(materialParameters); if (isSpherical(transform.cameraType)) { return this._createImageSphere(image, transform, material); } else if (isFisheye(transform.cameraType)) { return this._createImagePlaneFisheye(image, transform, material); } else { return this._createImagePlane(image, transform, material); } } _createImageSphere(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImageSphereGeo(transform, image) : this._getFlatImageSphereGeo(transform); return new Mesh(geometry, material); } _createImagePlane(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImagePlaneGeo(transform, image) : this._getRegularFlatImagePlaneGeo(transform); return new Mesh(geometry, material); } _createImagePlaneFisheye(image, transform, material) { const geometry = this._useMesh(transform, image) ? this._getImagePlaneGeoFisheye(transform, image) : this._getRegularFlatImagePlaneGeoFisheye(transform); return new Mesh(geometry, material); } _createMaterialParameters(transform, texture, shader) { const scaleX = Math.max(transform.basicHeight, transform.basicWidth) / transform.basicWidth; const scaleY = Math.max(transform.basicWidth, transform.basicHeight) / transform.basicHeight; return { depthWrite: false, fragmentShader: resolveShader(shader.fragment, transform.camera), side: DoubleSide, transparent: true, uniforms: Object.assign({ extrinsicMatrix: { value: transform.rt }, map: { value: texture }, opacity: { value: 1.0 }, scale: { value: new Vector2(scaleX, scaleY) } }, makeCameraUniforms(transform.camera)), vertexShader: resolveShader(shader.vertex, transform.camera), }; } _createTexture(image) { let texture = new Texture(image); texture.minFilter = LinearFilter; texture.needsUpdate = true; return texture; } _useMesh(transform, image) { return image.mesh.vertices.length && transform.hasValidScale; } _getImageSphereGeo(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getImagePlaneGeo(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getImagePlaneGeoFisheye(transform, image) { const t = transform.rtInverse; let vertices = image.mesh.vertices; let numVertices = vertices.length / 3; let positions = new Float32Array(vertices.length); for (let i = 0; i < numVertices; ++i) { let index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; let p = new Vector3(x, y, z); p.applyMatrix4(t); positions[index + 0] = p.x; positions[index + 1] = p.y; positions[index + 2] = p.z; } let faces = image.mesh.faces; let indices = new Uint16Array(faces.length); for (let i = 0; i < faces.length; ++i) { indices[i] = faces[i]; } let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } _getFlatImageSphereGeo(transform) { const geometry = new SphereGeometry(this._imageSphereRadius, 20, 40); const t = transform.rt .clone() .invert(); geometry.applyMatrix4(t); return geometry; } _getRegularFlatImagePlaneGeo(transform) { let width = transform.width; let height = transform.height; let size = Math.max(width, height); let dx = width / 2.0 / size; let dy = height / 2.0 / size; return this._getFlatImagePlaneGeo(transform, dx, dy); } _getFlatImagePlaneGeo(transform, dx, dy) { let vertices = []; vertices.push(transform.unprojectSfM([-dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([-dx, dy], this._imagePlaneDepth)); return this._createFlatGeometry(vertices); } _getRegularFlatImagePlaneGeoFisheye(transform) { let width = transform.width; let height = transform.height; let size = Math.max(width, height); let dx = width / 2.0 / size; let dy = height / 2.0 / size; return this._getFlatImagePlaneGeoFisheye(transform, dx, dy); } _getFlatImagePlaneGeoFisheye(transform, dx, dy) { let vertices = []; vertices.push(transform.unprojectSfM([-dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, -dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([dx, dy], this._imagePlaneDepth)); vertices.push(transform.unprojectSfM([-dx, dy], this._imagePlaneDepth)); return this._createFlatGeometry(vertices); } _createFlatGeometry(vertices) { let positions = new Float32Array(12); for (let i = 0; i < vertices.length; i++) { let index = 3 * i; positions[index + 0] = vertices[i][0]; positions[index + 1] = vertices[i][1]; positions[index + 2] = vertices[i][2]; } let indices = new Uint16Array(6); indices[0] = 0; indices[1] = 1; indices[2] = 3; indices[3] = 1; indices[4] = 2; indices[5] = 3; let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } } class MeshScene { constructor() { this._planes = []; this._planesOld = []; this._planesPeriphery = []; this._scene = new Scene(); this._sceneOld = new Scene(); this._scenePeriphery = new Scene(); } get planes() { return this._planes; } get planesOld() { return this._planesOld; } get planesPeriphery() { return this._planesPeriphery; } get scene() { return this._scene; } get sceneOld() { return this._sceneOld; } get scenePeriphery() { return this._scenePeriphery; } updateImagePlanes(planes) { this._dispose(this._planesOld, this.sceneOld); for (const plane of this._planes) { this._scene.remove(plane.mesh); this._sceneOld.add(plane.mesh); } for (const plane of planes) { this._scene.add(plane.mesh); } this._planesOld = this._planes; this._planes = planes; } addImagePlanes(planes) { for (const plane of planes) { this._scene.add(plane.mesh); this._planes.push(plane); } } addImagePlanesOld(planes) { for (const plane of planes) { this._sceneOld.add(plane.mesh); this._planesOld.push(plane); } } setImagePlanes(planes) { this._clear(); this.addImagePlanes(planes); } addPeripheryPlanes(planes) { for (const plane of planes) { this._scenePeriphery.add(plane.mesh); this._planesPeriphery.push(plane); } } setPeripheryPlanes(planes) { this._clearPeriphery(); this.addPeripheryPlanes(planes); } setImagePlanesOld(planes) { this._clearOld(); this.addImagePlanesOld(planes); } clear() { this._clear(); this._clearOld(); } _clear() { this._dispose(this._planes, this._scene); this._planes = []; } _clearOld() { this._dispose(this._planesOld, this._sceneOld); this._planesOld = []; } _clearPeriphery() { this._dispose(this._planesPeriphery, this._scenePeriphery); this._planesPeriphery = []; } _dispose(planes, scene) { for (const plane of planes) { const { mesh } = plane; scene.remove(mesh); mesh.geometry.dispose(); mesh.material.dispose(); const texture = mesh.material .uniforms.map.value; if (texture != null) { texture.dispose(); } } } } class ImageGLRenderer { constructor() { this._factory = new MeshFactory(); this._scene = new MeshScene(); this._alpha = 0; this._alphaOld = 0; this._fadeOutSpeed = 0.05; this._currentKey = null; this._previousKey = null; this._providerDisposers = {}; this._frameId = 0; this._needsRender = false; } get frameId() { return this._frameId; } get needsRender() { return this._needsRender; } indicateNeedsRender() { this._needsRender = true; } addPeripheryPlane(image, transform, shader) { const mesh = this._factory.createMesh(image, transform, shader); const plane = { mesh, imageId: image.id, camera: image.camera, }; this._scene.addPeripheryPlanes([plane]); this._needsRender = true; } clearPeripheryPlanes() { this._scene.setPeripheryPlanes([]); this._needsRender = true; } setShader(shader) { const planes = [ ...this._scene.planes, ...this._scene.planesOld, ...this._scene.planesPeriphery, ]; this._setShader(shader, planes); this._needsRender = true; } updateFrame(frame, shader) { this._updateFrameId(frame.id); this._needsRender = this._updateAlpha(frame.state.alpha) || this._needsRender; this._needsRender = this._updateAlphaOld(frame.state.alpha) || this._needsRender; this._needsRender = this._updateImagePlanes(frame.state, shader) || this._needsRender; } setTextureProvider(key, provider) { if (key !== this._currentKey) { return; } const createdSubscription = provider.textureCreated$ .subscribe((texture) => { this._updateTexture(texture); }); const updatedSubscription = provider.textureUpdated$ .subscribe((updated) => { this._needsRender = true; }); const dispose = () => { createdSubscription.unsubscribe(); updatedSubscription.unsubscribe(); provider.dispose(); }; if (key in this._providerDisposers) { const disposeProvider = this._providerDisposers[key]; disposeProvider(); delete this._providerDisposers[key]; } this._providerDisposers[key] = dispose; } updateTextureImage(imageElement, image) { this._needsRender = true; const planes = [ ...this._scene.planes, ...this._scene.planesOld, ...this._scene.planesPeriphery, ]; for (const plane of planes) { if (plane.imageId !== image.id) { continue; } const material = plane.mesh.material; const texture = material.uniforms.map.value; texture.image = imageElement; texture.needsUpdate = true; } } render(perspectiveCamera, renderer) { const planes = this._scene.planes; const planesOld = this._scene.planesOld; const planesPeriphery = this._scene.planesPeriphery; const planeAlpha = Object.keys(planesOld).length ? 1 : this._alpha; const peripheryAlpha = Object.keys(planesOld).length ? 1 : Math.floor(this._alpha); for (const plane of planes) { plane.mesh.material.uniforms.opacity.value = planeAlpha; } for (const plane of planesOld) { plane.mesh.material.uniforms.opacity.value = this._alphaOld; } for (const plane of planesPeriphery) { plane.mesh.material.uniforms.opacity.value = peripheryAlpha; } renderer.render(this._scene.scenePeriphery, perspectiveCamera); renderer.render(this._scene.scene, perspectiveCamera); renderer.render(this._scene.sceneOld, perspectiveCamera); for (const plane of planes) { plane.mesh.material.uniforms.opacity.value = this._alpha; } renderer.render(this._scene.scene, perspectiveCamera); } clearNeedsRender() { this._needsRender = false; } reset() { this._scene.clear(); for (const disposeProvider of Object.values(this._providerDisposers)) { disposeProvider(); } this._needsRender = true; } _setShader(shader, planes) { for (const plane of planes) { const material = plane.mesh.material; material.fragmentShader = resolveShader(shader.fragment, plane.camera); material.vertexShader = resolveShader(shader.vertex, plane.camera); } } _updateFrameId(frameId) { this._frameId = frameId; } _updateAlpha(alpha) { if (alpha === this._alpha) { return false; } this._alpha = alpha; return true; } _updateAlphaOld(alpha) { if (alpha < 1 || this._alphaOld === 0) { return false; } this._alphaOld = Math.max(0, this._alphaOld - this._fadeOutSpeed); return true; } _updateImagePlanes(state, shader) { if (state.currentImage == null || state.currentImage.id === this._currentKey) { return false; } const previousKey = state.previousImage != null ? state.previousImage.id : null; const currentKey = state.currentImage.id; if (this._previousKey !== previousKey && this._previousKey !== currentKey && this._previousKey in this._providerDisposers) { const disposeProvider = this._providerDisposers[this._previousKey]; disposeProvider(); delete this._providerDisposers[this._previousKey]; } if (previousKey != null) { if (previousKey !== this._currentKey && previousKey !== this._previousKey) { const previousMesh = this._factory.createMesh(state.previousImage, state.previousTransform, shader); const previousPlane = { mesh: previousMesh, imageId: previousKey, camera: state.previousImage.camera, }; this._scene.updateImagePlanes([previousPlane]); } this._previousKey = previousKey; } this._currentKey = currentKey; const currentMesh = this._factory.createMesh(state.currentImage, state.currentTransform, shader); const plane = { mesh: currentMesh, imageId: currentKey, camera: state.currentImage.camera, }; this._scene.updateImagePlanes([plane]); this._alphaOld = 1; return true; } _updateTexture(texture) { this._needsRender = true; const planes = this._scene.planes; for (const plane of planes) { const material = plane.mesh.material; const oldTexture = material.uniforms.map.value; material.uniforms.map.value = null; oldTexture.dispose(); material.uniforms.map.value = texture; } } } var RenderPass; (function (RenderPass) { RenderPass[RenderPass["Background"] = 0] = "Background"; RenderPass[RenderPass["Opaque"] = 1] = "Opaque"; })(RenderPass || (RenderPass = {})); /** * @class ImageTileLoader * * @classdesc Represents a loader of image tiles. */ class TileLoader { /** * Create a new image image tile loader instance. * * @param {APIWrapper} _api - API wrapper. */ constructor(_api) { this._api = _api; this._urls$ = new Map(); } /** * Retrieve an image tile. * * @param {string} url - URL to the image tile resource */ getImage$(url) { let aborter; const abort = new Promise((_, reject) => { aborter = reject; }); return [Observable.create((subscriber) => { this._api.data .getImageBuffer(url, abort) .then((buffer) => { aborter = null; const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => { window.URL.revokeObjectURL(image.src); subscriber.next(image); subscriber.complete(); }; image.onerror = () => { aborter = null; window.URL.revokeObjectURL(image.src); subscriber.error(new Error(`Failed to load image tile`)); }; const blob = new Blob([buffer]); image.src = window.URL.createObjectURL(blob); }, (error) => { aborter = null; subscriber.error(error); }); }), () => { if (!!aborter) { aborter(); } }]; } getURLs$(imageId, level) { const uniqueId = this._inventId(imageId, level); if (this._urls$.has(uniqueId)) { return this._urls$.get(uniqueId); } const request = { imageId, z: level }; const urls$ = this._api .getImageTiles$(request) .pipe(map(contract => contract.node), finalize(() => { this._urls$.delete(uniqueId); }), publish(), refCount()); this._urls$.set(uniqueId, urls$); return urls$; } _inventId(imageId, level) { return `${imageId}-${level}`; } } /** * @class ImageTileStore * * @classdesc Represents a store for image tiles. */ class TileStore { /** * Create a new image image tile store instance. */ constructor() { this._tiles = new Map(); this._urlLevels = new Set(); this._urls = new Map(); } /** * Add an image tile to the store. * * @param {string} id - The identifier for the image tile. * @param {HTMLImageElement} image - The image tile. */ add(id, image) { if (this._tiles.has(id)) { throw new Error(`Image tile already stored (${id})`); } this._tiles.set(id, image); } addURLs(level, ents) { const urls = this._urls; for (const ent of ents) { const id = this.inventId(ent); if (this._urls.has(id)) { throw new Error(`URL already stored (${id})`); } urls.set(id, ent.url); } this._urlLevels.add(level); } /** * Dispose the store. * * @description Disposes all cached assets. */ dispose() { this._tiles .forEach(image => window.URL.revokeObjectURL(image.src)); this._tiles.clear(); this._urls.clear(); this._urlLevels.clear(); } /** * Get an image tile from the store. * * @param {string} id - The identifier for the tile. * @param {number} level - The level of the tile. */ get(id) { return this._tiles.get(id); } getURL(id) { return this._urls.get(id); } /** * Check if an image tile exist in the store. * * @param {string} id - The identifier for the tile. * @param {number} level - The level of the tile. */ has(id) { return this._tiles.has(id); } hasURL(id) { return this._urls.has(id); } hasURLLevel(level) { return this._urlLevels.has(level); } /** * Create a unique tile id from a tile. * * @description Tile ids are used as a hash for * storing the tile in a dictionary. * * @param {ImageTileEnt} tile - The tile. * @returns {string} Unique id. */ inventId(tile) { return `${tile.z}-${tile.x}-${tile.y}`; } } /** * @class RegionOfInterestCalculator * * @classdesc Represents a calculator for regions of interest. */ class RegionOfInterestCalculator { constructor() { this._viewportCoords = new ViewportCoords(); } /** * Compute a region of interest based on the current render camera * and the viewport size. * * @param {RenderCamera} renderCamera - Render camera used for unprojections. * @param {ViewportSize} size - Viewport size in pixels. * @param {Transform} transform - Transform used for projections. * * @returns {TileRegionOfInterest} A region of interest. */ computeRegionOfInterest(renderCamera, size, transform) { const viewportBoundaryPoints = this._viewportBoundaryPoints(4); const bbox = this._viewportPointsBoundingBox(viewportBoundaryPoints, renderCamera, transform); this._clipBoundingBox(bbox); const viewportPixelWidth = 2 / size.width; const viewportPixelHeight = 2 / size.height; const centralViewportPixel = [ [-0.5 * viewportPixelWidth, 0.5 * viewportPixelHeight], [0.5 * viewportPixelWidth, 0.5 * viewportPixelHeight], [0.5 * viewportPixelWidth, -0.5 * viewportPixelHeight], [-0.5 * viewportPixelWidth, -0.5 * viewportPixelHeight], ]; const cpbox = this._viewportPointsBoundingBox(centralViewportPixel, renderCamera, transform); const inverted = cpbox.minX < cpbox.maxX; return { bbox: bbox, pixelHeight: cpbox.maxY - cpbox.minY, pixelWidth: cpbox.maxX - cpbox.minX + (inverted ? 0 : 1), }; } _viewportBoundaryPoints(pointsPerSide) { const points = []; const os = [[-1, 1], [1, 1], [1, -1], [-1, -1]]; const ds = [[2, 0], [0, -2], [-2, 0], [0, 2]]; for (let side = 0; side < 4; ++side) { const o = os[side]; const d = ds[side]; for (let i = 0; i < pointsPerSide; ++i) { points.push([o[0] + d[0] * i / pointsPerSide, o[1] + d[1] * i / pointsPerSide]); } } return points; } _viewportPointsBoundingBox(viewportPoints, renderCamera, transform) { const basicPoints = viewportPoints .map((point) => { return this._viewportCoords .viewportToBasic(point[0], point[1], transform, renderCamera.perspective); }); if (isSpherical(transform.cameraType)) { return this._boundingBoxSpherical(basicPoints); } else { return this._boundingBox(basicPoints); } } _boundingBox(points) { const bbox = { maxX: Number.NEGATIVE_INFINITY, maxY: Number.NEGATIVE_INFINITY, minX: Number.POSITIVE_INFINITY, minY: Number.POSITIVE_INFINITY, }; for (let i = 0; i < points.length; ++i) { bbox.minX = Math.min(bbox.minX, points[i][0]); bbox.maxX = Math.max(bbox.maxX, points[i][0]); bbox.minY = Math.min(bbox.minY, points[i][1]); bbox.maxY = Math.max(bbox.maxY, points[i][1]); } return bbox; } _boundingBoxSpherical(points) { const xs = []; const ys = []; for (let i = 0; i < points.length; ++i) { xs.push(points[i][0]); ys.push(points[i][1]); } xs.sort((a, b) => { return this._sign(a - b); }); ys.sort((a, b) => { return this._sign(a - b); }); const intervalX = this._intervalSpherical(xs); return { maxX: intervalX[1], maxY: ys[ys.length - 1], minX: intervalX[0], minY: ys[0], }; } /** * Find the max interval between consecutive numbers. * Assumes numbers are between 0 and 1, sorted and that * x is equivalent to x + 1. */ _intervalSpherical(xs) { let maxdx = 0; let maxi = -1; for (let i = 0; i < xs.length - 1; ++i) { const dx = xs[i + 1] - xs[i]; if (dx > maxdx) { maxdx = dx; maxi = i; } } const loopdx = xs[0] + 1 - xs[xs.length - 1]; if (loopdx > maxdx) { return [xs[0], xs[xs.length - 1]]; } else { return [xs[maxi + 1], xs[maxi]]; } } _clipBoundingBox(bbox) { bbox.minX = Math.max(0, Math.min(1, bbox.minX)); bbox.maxX = Math.max(0, Math.min(1, bbox.maxX)); bbox.minY = Math.max(0, Math.min(1, bbox.minY)); bbox.maxY = Math.max(0, Math.min(1, bbox.maxY)); } _sign(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; } } const TILE_MIN_REQUEST_LEVEL = 11; const TILE_SIZE = 1024; function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function levelTilePixelSize(level) { return TILE_SIZE / levelScale(level); } function levelScale(level) { return Math.pow(2, level.z - level.max); } function rawImageLevel(size) { const s = Math.max(size.w, size.h); return Math.log(s) / Math.log(2); } function baseImageLevel(size) { return Math.ceil(rawImageLevel(size)); } function clampedImageLevel(size, min, max) { return Math.max(min, Math.min(max, baseImageLevel(size))); } function basicToTileCoords2D(basic, size, level) { const tilePixelSize = levelTilePixelSize(level); const w = size.w; const h = size.h; const maxX = Math.ceil(w / tilePixelSize) - 1; const maxY = Math.ceil(h / tilePixelSize) - 1; const x = clamp(Math.floor(w * basic[0] / tilePixelSize), 0, maxX); const y = clamp(Math.floor(h * basic[1] / tilePixelSize), 0, maxY); return { x, y }; } function tileToPixelCoords2D(tile, size, level) { const scale = 1 / levelScale(level); const scaledTS = scale * TILE_SIZE; const x = scaledTS * tile.x; const y = scaledTS * tile.y; const w = Math.min(scaledTS, size.w - x); const h = Math.min(scaledTS, size.h - y); return { h, x, y, w }; } function hasOverlap1D(low, base, scale) { return (scale * low <= base && base < scale * (low + 1)); } function hasOverlap2D(tile1, tile2) { if (tile1.z === tile2.z) { return tile1.x === tile2.x && tile1.y === tile2.y; } const low = tile1.z < tile2.z ? tile1 : tile2; const base = tile1.z < tile2.z ? tile2 : tile1; const scale = 1 / levelScale({ max: base.z, z: low.z }); const overlapX = hasOverlap1D(low.x, base.x, scale); const overlapY = hasOverlap1D(low.y, base.y, scale); return overlapX && overlapY; } function cornersToTilesCoords2D(topLeft, bottomRight, size, level) { const xs = []; if (topLeft.x > bottomRight.x) { const tilePixelSize = levelTilePixelSize(level); const maxX = Math.ceil(size.w / tilePixelSize) - 1; for (let x = topLeft.x; x <= maxX; x++) { xs.push(x); } for (let x = 0; x <= bottomRight.x; x++) { xs.push(x); } } else { for (let x = topLeft.x; x <= bottomRight.x; x++) { xs.push(x); } } const tiles = []; for (const x of xs) { for (let y = topLeft.y; y <= bottomRight.y; y++) { tiles.push({ x, y }); } } return tiles; } function verifySize(size) { return size.w > 0 && size.h > 0; } /** * @class TextureProvider * * @classdesc Represents a provider of textures. */ class TextureProvider { /** * Create a new image texture provider instance. * * @param {string} imageId - The identifier of the image for which to request tiles. * @param {number} width - The full width of the original image. * @param {number} height - The full height of the original image. * @param {HTMLImageElement} background - Image to use as background. * @param {TileLoader} loader - Loader for retrieving tiles. * @param {TileStore} store - Store for saving tiles. * @param {THREE.WebGLRenderer} renderer - Renderer used for rendering tiles to texture. */ constructor(imageId, width, height, background, loader, store, renderer) { const size = { h: height, w: width }; if (!verifySize(size)) { console.warn(`Original image size (${width}, ${height}) ` + `is invalid (${imageId}). Tiles will not be loaded.`); } this._imageId = imageId; this._size = size; this._level = { max: baseImageLevel(this._size), z: -1, }; this._holder = new SubscriptionHolder(); this._updated$ = new Subject(); this._createdSubject$ = new Subject(); this._created$ = this._createdSubject$ .pipe(publishReplay(1), refCount()); this._holder.push(this._created$.subscribe(() => { })); this._hasSubject$ = new Subject(); this._has$ = this._hasSubject$ .pipe(startWith(false), publishReplay(1), refCount()); this._holder.push(this._has$.subscribe(() => { })); this._renderedLevel = new Set(); this._rendered = new Map(); this._subscriptions = new Map(); this._urlSubscriptions = new Map(); this._loader = loader; this._store = store; this._background = background; this._renderer = renderer; this._aborts = []; this._render = null; this._disposed = false; } /** * Get disposed. * * @returns {boolean} Value indicating whether provider has * been disposed. */ get disposed() { return this._disposed; } /** * Get hasTexture$. * * @returns {Observable} Observable emitting * values indicating when the existance of a texture * changes. */ get hasTexture$() { return this._has$; } /** * Get id. * * @returns {boolean} The identifier of the image for * which to render textures. */ get id() { return this._imageId; } /** * Get textureUpdated$. * * @returns {Observable} Observable emitting * values when an existing texture has been updated. */ get textureUpdated$() { return this._updated$; } /** * Get textureCreated$. * * @returns {Observable} Observable emitting * values when a new texture has been created. */ get textureCreated$() { return this._created$; } /** * Abort all outstanding image tile requests. */ abort() { this._subscriptions.forEach(sub => sub.unsubscribe()); this._subscriptions.clear(); for (const abort of this._aborts) { abort(); } this._aborts = []; } /** * Dispose the provider. * * @description Disposes all cached assets and * aborts all outstanding image tile requests. */ dispose() { if (this._disposed) { console.warn(`Texture already disposed (${this._imageId})`); return; } this._urlSubscriptions.forEach(sub => sub.unsubscribe()); this._urlSubscriptions.clear(); this.abort(); if (this._render != null) { this._render.target.dispose(); this._render.target = null; this._render.camera = null; this._render = null; } this._store.dispose(); this._holder.unsubscribe(); this._renderedLevel.clear(); this._background = null; this._renderer = null; this._disposed = true; } /** * Set the region of interest. * * @description When the region of interest is set the * the tile level is determined and tiles for the region * are fetched from the store or the loader and renderedLevel * to the texture. * * @param {TileRegionOfInterest} roi - Spatial edges to cache. */ setRegionOfInterest(roi) { if (!verifySize(this._size)) { return; } const virtualWidth = 1 / roi.pixelWidth; const virtualHeight = 1 / roi.pixelHeight; const level = clampedImageLevel({ h: virtualHeight, w: virtualWidth }, TILE_MIN_REQUEST_LEVEL, this._level.max); if (level !== this._level.z) { this.abort(); this._level.z = level; this._renderedLevel.clear(); this._rendered .forEach((tile, id) => { if (tile.z !== level) { return; } this._renderedLevel.add(id); }); } if (this._render == null) { this._initRender(); } const topLeft = basicToTileCoords2D([roi.bbox.minX, roi.bbox.minY], this._size, this._level); const bottomRight = basicToTileCoords2D([roi.bbox.maxX, roi.bbox.maxY], this._size, this._level); const tiles = cornersToTilesCoords2D(topLeft, bottomRight, this._size, this._level); this._fetchTiles(level, tiles); } /** * Retrieve an image tile. * * @description Retrieve an image tile and render it to the * texture. Add the tile to the store and emit to the updated * observable. * * @param {ImageTileEnt} tile - The tile ent. */ _fetchTile(tile) { const getTile = this._loader.getImage$(tile.url); const tile$ = getTile[0]; const abort = getTile[1]; this._aborts.push(abort); const tileId = this._store.inventId(tile); const subscription = tile$.subscribe((image) => { const pixels = tileToPixelCoords2D(tile, this._size, this._level); this._renderToTarget(pixels, image); this._subscriptions.delete(tileId); this._removeFromArray(abort, this._aborts); this._markRendered(tile); this._store.add(tileId, image); this._updated$.next(true); }, (error) => { this._subscriptions.delete(tileId); this._removeFromArray(abort, this._aborts); console.error(error); }); if (!subscription.closed) { this._subscriptions.set(tileId, subscription); } } /** * Fetch image tiles. * * @description Retrieve a image tiles and render them to the * texture. Retrieve from store if it exists, otherwise retrieve * from loader. * * @param {Array} tiles - Array of tile coordinates to * retrieve. */ _fetchTiles(level, tiles) { const urls$ = this._store.hasURLLevel(level) ? of(undefined) : this._loader .getURLs$(this._imageId, level) .pipe(tap(ents => { if (!this._store.hasURLLevel(level)) { this._store.addURLs(level, ents); } })); const subscription = urls$.subscribe(() => { if (level !== this._level.z) { return; } for (const tile of tiles) { const ent = { x: tile.x, y: tile.y, z: level, url: null, }; const id = this._store.inventId(ent); if (this._renderedLevel.has(id) || this._subscriptions.has(id)) { continue; } if (this._store.has(id)) { const pixels = tileToPixelCoords2D(tile, this._size, this._level); this._renderToTarget(pixels, this._store.get(id)); this._markRendered(ent); this._updated$.next(true); continue; } ent.url = this._store.getURL(id); this._fetchTile(ent); } this._urlSubscriptions.delete(level); }, (error) => { this._urlSubscriptions.delete(level); console.error(error); }); if (!subscription.closed) { this._urlSubscriptions.set(level, subscription); } } _initRender() { const dx = this._size.w / 2; const dy = this._size.h / 2; const near = -1; const far = 1; const camera = new OrthographicCamera(-dx, dx, dy, -dy, near, far); camera.position.z = 1; const gl = this._renderer.getContext(); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const backgroundSize = Math.max(this._size.w, this._size.h); const scale = maxTextureSize > backgroundSize ? 1 : maxTextureSize / backgroundSize; const targetWidth = Math.floor(scale * this._size.w); const targetHeight = Math.floor(scale * this._size.h); const target = new WebGLRenderTarget(targetWidth, targetHeight, { depthBuffer: false, format: RGBAFormat, magFilter: LinearFilter, minFilter: LinearFilter, stencilBuffer: false, }); this._render = { camera, target }; const pixels = tileToPixelCoords2D({ x: 0, y: 0 }, this._size, { max: this._level.max, z: 0 }); this._renderToTarget(pixels, this._background); this._createdSubject$.next(target.texture); this._hasSubject$.next(true); } /** * Mark a tile as rendered. * * @description Clears tiles marked as rendered in other * levels of the tile pyramid if they overlap the * newly rendered tile. * * @param {Arrary} tile - The tile ent. */ _markRendered(tile) { const others = Array.from(this._rendered.entries()) .filter(([_, t]) => { return t.z !== tile.z; }); for (const [otherId, other] of others) { if (hasOverlap2D(tile, other)) { this._rendered.delete(otherId); } } const id = this._store.inventId(tile); this._rendered.set(id, tile); this._renderedLevel.add(id); } /** * Remove an item from an array if it exists in array. * * @param {T} item - Item to remove. * @param {Array} array - Array from which item should be removed. */ _removeFromArray(item, array) { const index = array.indexOf(item); if (index !== -1) { array.splice(index, 1); } } /** * Render an image tile to the target texture. * * @param {ImageTileEnt} tile - Tile ent. * @param {HTMLImageElement} image - The image tile to render. */ _renderToTarget(pixel, image) { const texture = new Texture(image); texture.minFilter = LinearFilter; texture.needsUpdate = true; const geometry = new PlaneGeometry(pixel.w, pixel.h); const material = new MeshBasicMaterial({ map: texture, side: FrontSide, }); const mesh = new Mesh(geometry, material); mesh.position.x = -this._size.w / 2 + pixel.x + pixel.w / 2; mesh.position.y = this._size.h / 2 - pixel.y - pixel.h / 2; const scene = new Scene(); scene.add(mesh); const target = this._renderer.getRenderTarget(); this._renderer.resetState(); this._renderer.setRenderTarget(this._render.target); this._renderer.render(scene, this._render.camera); this._renderer.setRenderTarget(target); scene.remove(mesh); geometry.dispose(); material.dispose(); texture.dispose(); } } var State; (function (State) { State[State["Custom"] = 0] = "Custom"; State[State["Earth"] = 1] = "Earth"; State[State["GravityTraversing"] = 2] = "GravityTraversing"; State[State["Traversing"] = 3] = "Traversing"; State[State["Waiting"] = 4] = "Waiting"; State[State["WaitingInteractively"] = 5] = "WaitingInteractively"; })(State || (State = {})); class ImageComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); this._imageTileLoader = new TileLoader(navigator.api); this._roiCalculator = new RegionOfInterestCalculator(); this._rendererOperation$ = new Subject(); this._rendererCreator$ = new Subject(); this._rendererDisposer$ = new Subject(); this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { return operation(renderer); }, null), filter((renderer) => { return renderer != null; }), distinctUntilChanged(undefined, (renderer) => { return renderer.frameId; })); this._rendererCreator$.pipe(map(() => { return (renderer) => { if (renderer != null) { throw new Error("Multiple image plane states can not be created at the same time"); } return new ImageGLRenderer(); }; })) .subscribe(this._rendererOperation$); this._rendererDisposer$.pipe(map(() => { return (renderer) => { renderer.reset(); renderer.clearNeedsRender(); return null; }; })) .subscribe(this._rendererOperation$); } _activate() { const subs = this._subscriptions; subs.push(this._renderer$.pipe(map((renderer) => { const renderHash = { name: this._name, renderer: { frameId: renderer.frameId, needsRender: renderer.needsRender, render: renderer.render.bind(renderer), pass: RenderPass.Background, }, }; renderer.clearNeedsRender(); return renderHash; })) .subscribe(this._container.glRenderer.render$)); this._rendererCreator$.next(null); subs.push(this._navigator.graphService.dataReset$.pipe(map(() => { return (renderer) => { renderer.reset(); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(this._navigator.stateService.currentState$.pipe(withLatestFrom(this._navigator.projectionService.shader$), map(([frame, shader]) => { return (renderer) => { renderer.updateFrame(frame, shader); return renderer; }; })) .subscribe(this._rendererOperation$)); const textureProvider$ = this._container.configurationService.imageTiling$.pipe(switchMap((active) => { return active ? this._navigator.stateService.currentState$ : new Subject(); }), distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; }), withLatestFrom(this._container.glRenderer.webGLRenderer$), map(([frame, renderer]) => { const state = frame.state; const currentNode = state.currentImage; const currentTransform = state.currentTransform; return new TextureProvider(currentNode.id, currentTransform.basicWidth, currentTransform.basicHeight, currentNode.image, this._imageTileLoader, new TileStore(), renderer); }), publishReplay(1), refCount()); subs.push(this._navigator.projectionService.shader$.pipe(map((shader) => { return (renderer) => { renderer.setShader(shader); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(textureProvider$.subscribe(() => { })); subs.push(textureProvider$.pipe(map((provider) => { return (renderer) => { renderer.setTextureProvider(provider.id, provider); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(textureProvider$.pipe(pairwise()) .subscribe((pair) => { const previous = pair[0]; previous.abort(); })); const roiTrigger$ = this._container.configurationService.imageTiling$.pipe(switchMap((active) => { return active ? combineLatest(this._navigator.stateService.state$, this._navigator.stateService.inTranslation$) : new Subject(); }), switchMap(([state, inTranslation]) => { const streetState = state === State.Traversing || state === State.Waiting || state === State.WaitingInteractively; const active = streetState && !inTranslation; return active ? this._container.renderService.renderCameraFrame$ : empty(); }), map((camera) => { return { camera, height: camera.size.height.valueOf(), lookat: camera.camera.lookat.clone(), width: camera.size.width.valueOf(), zoom: camera.zoom.valueOf(), }; }), pairwise(), map(([pl0, pl1]) => { const stalled = pl0.width === pl1.width && pl0.height === pl1.height && pl0.zoom === pl1.zoom && pl0.lookat.equals(pl1.lookat); return { camera: pl1.camera, stalled }; }), distinctUntilChanged((x, y) => { return x.stalled === y.stalled; }), filter((camera) => { return camera.stalled; }), withLatestFrom(this._container.renderService.size$, this._navigator.stateService.currentTransform$)); subs.push(textureProvider$.pipe(switchMap((provider) => { return roiTrigger$.pipe(map(([stalled, size, transform]) => { const camera = stalled.camera; const basic = new ViewportCoords() .viewportToBasic(0, 0, transform, camera.perspective); if (basic[0] < 0 || basic[1] < 0 || basic[0] > 1 || basic[1] > 1) { return undefined; } return [ this._roiCalculator .computeRegionOfInterest(camera, size, transform), provider, ]; }), filter((args) => { return !!args; })); }), filter((args) => { return !args[1].disposed; })) .subscribe(([roi, provider]) => { provider.setRegionOfInterest(roi); })); const hasTexture$ = textureProvider$ .pipe(switchMap((provider) => { return provider.hasTexture$; }), startWith(false), publishReplay(1), refCount()); subs.push(hasTexture$.subscribe(() => { })); subs.push(this._navigator.panService.panImages$.pipe(map(() => { return (renderer) => { renderer.clearPeripheryPlanes(); return renderer; }; })) .subscribe(this._rendererOperation$)); const cachedPanNodes$ = this._navigator.panService.panImages$.pipe(switchMap((nts) => { return from(nts).pipe(mergeMap(([n, t]) => { return combineLatest(this._navigator.graphService.cacheImage$(n.id).pipe(catchError((error) => { console.error(`Failed to cache periphery image (${n.id})`, error); return empty(); })), of(t)); })); }), share()); subs.push(cachedPanNodes$.pipe(withLatestFrom(this._navigator.projectionService.shader$), map(([[n, t], s]) => { return (renderer) => { renderer.addPeripheryPlane(n, t, s); return renderer; }; })) .subscribe(this._rendererOperation$)); subs.push(cachedPanNodes$.pipe(mergeMap(([n]) => { return n.cacheImage$().pipe(catchError(() => { return empty(); })); }), map((n) => { return (renderer) => { renderer.updateTextureImage(n.image, n); return renderer; }; })) .subscribe(this._rendererOperation$)); const inTransition$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.alpha < 1; }), distinctUntilChanged()); const panTrigger$ = combineLatest(this._container.mouseService.active$, this._container.touchService.active$, this._navigator.stateService.inMotion$, inTransition$).pipe(map(([mouseActive, touchActive, inMotion, inTransition]) => { return !(mouseActive || touchActive || inMotion || inTransition); }), filter((trigger) => { return trigger; })); subs.push(this._navigator.panService.panImages$ .pipe(switchMap((nts) => { return nts.length === 0 ? empty() : panTrigger$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentImage$, this._navigator.stateService.currentTransform$), mergeMap(([, renderCamera, currentNode, currentTransform]) => { return of([ renderCamera, currentNode, currentTransform, nts, ]); })); }), switchMap(([camera, cn, ct, nts]) => { const direction = camera.camera.lookat.clone().sub(camera.camera.position); const cd = new Spatial().viewingDirection(cn.rotation); const ca = cd.angleTo(direction); const closest = [ca, undefined]; const basic = new ViewportCoords().viewportToBasic(0, 0, ct, camera.perspective); if (basic[0] >= 0 && basic[0] <= 1 && basic[1] >= 0 && basic[1] <= 1) { closest[0] = Number.NEGATIVE_INFINITY; } for (const [n] of nts) { const d = new Spatial().viewingDirection(n.rotation); const a = d.angleTo(direction); if (a < closest[0]) { closest[0] = a; closest[1] = n.id; } } if (!closest[1]) { return empty(); } return this._navigator.moveTo$(closest[1]).pipe(catchError(() => { return empty(); })); })) .subscribe()); } _deactivate() { this._rendererDisposer$.next(null); this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } } ImageComponent.componentName = "image"; class HandlerBase { /** @ignore */ constructor(component, container, navigator) { this._component = component; this._container = container; this._navigator = navigator; this._enabled = false; } /** * Returns a Boolean indicating whether the interaction is enabled. * * @returns {boolean} `true` if the interaction is enabled. */ get isEnabled() { return this._enabled; } /** * Enables the interaction. * * @example * ```js * ..enable(); * ``` */ enable() { if (this._enabled || !this._component.activated) { return; } this._enable(); this._enabled = true; this._component.configure(this._getConfiguration(true)); } /** * Disables the interaction. * * @example * ```js * ..disable(); * ``` */ disable() { if (!this._enabled) { return; } this._disable(); this._enabled = false; if (this._component.activated) { this._component.configure(this._getConfiguration(false)); } } } /** * The `KeySequenceNavigationHandler` allows the user to navigate through a sequence using the * following key commands: * * `ALT` + `Up Arrow`: Navigate to next image in the sequence. * `ALT` + `Down Arrow`: Navigate to previous image in sequence. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keySequenceNavigation.disable(); * keyboardComponent.keySequenceNavigation.enable(); * * var isEnabled = keyboardComponent.keySequenceNavigation.isEnabled; * ``` */ class KeySequenceNavigationHandler extends HandlerBase { _enable() { const sequenceEdges$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; })); this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(sequenceEdges$)) .subscribe(([event, edgeStatus]) => { let direction = null; switch (event.keyCode) { case 38: // up direction = exports.NavigationDirection.Next; break; case 40: // down direction = exports.NavigationDirection.Prev; break; default: return; } event.preventDefault(); if (!event.altKey || event.shiftKey || !edgeStatus.cached) { return; } for (const edge of edgeStatus.edges) { if (edge.data.direction === direction) { this._navigator.moveTo$(edge.target) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); return; } } }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keySequenceNavigation: enable }; } } /** * The `KeySpatialNavigationHandler` allows the user to navigate through a sequence using the * following key commands: * * `Up Arrow`: Step forward. * `Down Arrow`: Step backward. * `Left Arrow`: Step to the left. * `Rigth Arrow`: Step to the right. * `SHIFT` + `Down Arrow`: Turn around. * `SHIFT` + `Left Arrow`: Turn to the left. * `SHIFT` + `Rigth Arrow`: Turn to the right. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keySpatialNavigation.disable(); * keyboardComponent.keySpatialNavigation.enable(); * * var isEnabled = keyboardComponent.keySpatialNavigation.isEnabled; * ``` */ class KeySpatialNavigationHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, spatial) { super(component, container, navigator); this._spatial = spatial; } _enable() { const spatialEdges$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.spatialEdges$; })); this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(spatialEdges$, this._navigator.stateService.currentState$)) .subscribe(([event, edgeStatus, frame]) => { let spherical = isSpherical(frame.state.currentImage.cameraType); let direction = null; switch (event.keyCode) { case 37: // left direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnLeft : exports.NavigationDirection.StepLeft; break; case 38: // up direction = event.shiftKey && !spherical ? exports.NavigationDirection.Spherical : exports.NavigationDirection.StepForward; break; case 39: // right direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnRight : exports.NavigationDirection.StepRight; break; case 40: // down direction = event.shiftKey && !spherical ? exports.NavigationDirection.TurnU : exports.NavigationDirection.StepBackward; break; default: return; } event.preventDefault(); if (event.altKey || !edgeStatus.cached || (event.shiftKey && spherical)) { return; } if (!spherical) { this._moveDir(direction, edgeStatus); } else { const shifts = {}; shifts[exports.NavigationDirection.StepBackward] = Math.PI; shifts[exports.NavigationDirection.StepForward] = 0; shifts[exports.NavigationDirection.StepLeft] = Math.PI / 2; shifts[exports.NavigationDirection.StepRight] = -Math.PI / 2; const phi = this._rotationFromCamera(frame.state.camera).phi; const navigationAngle = this._spatial.wrapAngle(phi + shifts[direction]); const threshold = Math.PI / 4; const edges = edgeStatus.edges.filter((e) => { return e.data.direction === exports.NavigationDirection.Spherical || e.data.direction === direction; }); let smallestAngle = Number.MAX_VALUE; let toKey = null; for (const edge of edges) { const angle = Math.abs(this._spatial.wrapAngle(edge.data.worldMotionAzimuth - navigationAngle)); if (angle < Math.min(smallestAngle, threshold)) { smallestAngle = angle; toKey = edge.target; } } if (toKey == null) { return; } this._moveTo(toKey); } }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keySpatialNavigation: enable }; } _moveDir(direction, edgeStatus) { for (const edge of edgeStatus.edges) { if (edge.data.direction === direction) { this._moveTo(edge.target); return; } } } _moveTo(id) { this._navigator.moveTo$(id) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } _rotationFromCamera(camera) { let direction = camera.lookat.clone().sub(camera.position); let upProjection = direction.clone().dot(camera.up); let planeProjection = direction.clone().sub(camera.up.clone().multiplyScalar(upProjection)); let phi = Math.atan2(planeProjection.y, planeProjection.x); let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); return { phi: phi, theta: theta }; } } /** * The `KeyZoomHandler` allows the user to zoom in and out using the * following key commands: * * `+`: Zoom in. * `-`: Zoom out. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keyZoom.disable(); * keyboardComponent.keyZoom.enable(); * * var isEnabled = keyboardComponent.keyZoom.isEnabled; * ``` */ class KeyZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([event, render, transform]) => { if (event.altKey || event.ctrlKey || event.metaKey) { return; } let delta = 0; switch (event.key) { case "+": delta = 1; break; case "-": delta = -1; break; default: return; } event.preventDefault(); const unprojected = this._viewportCoords.unprojectFromViewport(0, 0, render.perspective); const reference = transform.projectBasic(unprojected.toArray()); this._navigator.stateService.zoomIn(delta, reference); }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keyZoom: enable }; } } /** * The `KeyPlayHandler` allows the user to control the play behavior * using the following key commands: * * `Spacebar`: Start or stop playing. * `SHIFT` + `D`: Switch direction. * `<`: Decrease speed. * `>`: Increase speed. * * @example * ```js * var keyboardComponent = viewer.getComponent("keyboard"); * * keyboardComponent.keyPlay.disable(); * keyboardComponent.keyPlay.enable(); * * var isEnabled = keyboardComponent.keyPlay.isEnabled; * ``` */ class KeyPlayHandler extends HandlerBase { _enable() { this._keyDownSubscription = this._container.keyboardService.keyDown$.pipe(withLatestFrom(this._navigator.playService.playing$, this._navigator.playService.direction$, this._navigator.playService.speed$, this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; })), this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged()))) .subscribe(([event, playing, direction, speed, status, earth]) => { if (event.altKey || event.ctrlKey || event.metaKey) { return; } switch (event.key) { case "D": if (!event.shiftKey) { return; } const newDirection = playing ? null : direction === exports.NavigationDirection.Next ? exports.NavigationDirection.Prev : direction === exports.NavigationDirection.Prev ? exports.NavigationDirection.Next : null; if (newDirection != null) { this._navigator.playService.setDirection(newDirection); } break; case " ": if (event.shiftKey) { return; } if (!earth) { if (playing) { this._navigator.playService.stop(); } else { for (let edge of status.edges) { if (edge.data.direction === direction) { this._navigator.playService.play(); } } } } break; case "<": this._navigator.playService.setSpeed(speed - 0.05); break; case ">": this._navigator.playService.setSpeed(speed + 0.05); break; default: return; } event.preventDefault(); }); } _disable() { this._keyDownSubscription.unsubscribe(); } _getConfiguration(enable) { return { keyPlay: enable }; } } /** * @class KeyboardComponent * * @classdesc Component for keyboard event handling. * * To retrive and use the keyboard component * * @example * ```js * var viewer = new Viewer({ ... }); * * var keyboardComponent = viewer.getComponent("keyboard"); * ``` */ class KeyboardComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._keyPlayHandler = new KeyPlayHandler(this, container, navigator); this._keySequenceNavigationHandler = new KeySequenceNavigationHandler(this, container, navigator); this._keySpatialNavigationHandler = new KeySpatialNavigationHandler(this, container, navigator, new Spatial()); this._keyZoomHandler = new KeyZoomHandler(this, container, navigator, new ViewportCoords()); } /** * Get key play. * * @returns {KeyPlayHandler} The key play handler. */ get keyPlay() { return this._keyPlayHandler; } /** * Get key sequence navigation. * * @returns {KeySequenceNavigationHandler} The key sequence navigation handler. */ get keySequenceNavigation() { return this._keySequenceNavigationHandler; } /** * Get spatial. * * @returns {KeySpatialNavigationHandler} The spatial handler. */ get keySpatialNavigation() { return this._keySpatialNavigationHandler; } /** * Get key zoom. * * @returns {KeyZoomHandler} The key zoom handler. */ get keyZoom() { return this._keyZoomHandler; } _activate() { this._subscriptions.push(this._configuration$ .subscribe((configuration) => { if (configuration.keyPlay) { this._keyPlayHandler.enable(); } else { this._keyPlayHandler.disable(); } if (configuration.keySequenceNavigation) { this._keySequenceNavigationHandler.enable(); } else { this._keySequenceNavigationHandler.disable(); } if (configuration.keySpatialNavigation) { this._keySpatialNavigationHandler.enable(); } else { this._keySpatialNavigationHandler.disable(); } if (configuration.keyZoom) { this._keyZoomHandler.enable(); } else { this._keyZoomHandler.disable(); } })); } _deactivate() { this._subscriptions.unsubscribe(); this._keyPlayHandler.disable(); this._keySequenceNavigationHandler.disable(); this._keySpatialNavigationHandler.disable(); this._keyZoomHandler.disable(); } _getDefaultConfiguration() { return { keyPlay: true, keySequenceNavigation: true, keySpatialNavigation: true, keyZoom: true }; } } KeyboardComponent.componentName = "keyboard"; class MarkerScene { constructor(scene, raycaster) { this._needsRender = false; this._interactiveObjects = []; this._markers = {}; this._objectMarkers = {}; this._raycaster = !!raycaster ? raycaster : new Raycaster(); this._scene = !!scene ? scene : new Scene(); } get markers() { return this._markers; } get needsRender() { return this._needsRender; } add(marker, position) { if (marker.id in this._markers) { this._dispose(marker.id); } marker.createGeometry(position); this._scene.add(marker.geometry); this._markers[marker.id] = marker; for (let interactiveObject of marker.getInteractiveObjects()) { this._interactiveObjects.push(interactiveObject); this._objectMarkers[interactiveObject.uuid] = marker.id; } this._needsRender = true; } clear() { for (const id in this._markers) { if (!this._markers.hasOwnProperty) { continue; } this._dispose(id); } this._needsRender = true; } get(id) { return this._markers[id]; } getAll() { return Object .keys(this._markers) .map((id) => { return this._markers[id]; }); } has(id) { return id in this._markers; } intersectObjects([viewportX, viewportY], camera) { this._raycaster.setFromCamera(new Vector2(viewportX, viewportY), camera); const intersects = this._raycaster.intersectObjects(this._interactiveObjects); for (const intersect of intersects) { if (intersect.object.uuid in this._objectMarkers) { return this._objectMarkers[intersect.object.uuid]; } } return null; } lerpAltitude(id, alt, alpha) { if (!(id in this._markers)) { return; } this._markers[id].lerpAltitude(alt, alpha); this._needsRender = true; } remove(id) { if (!(id in this._markers)) { return; } this._dispose(id); this._needsRender = true; } render(perspectiveCamera, renderer) { renderer.render(this._scene, perspectiveCamera); this._needsRender = false; } update(id, position, lngLat) { if (!(id in this._markers)) { return; } const marker = this._markers[id]; marker.updatePosition(position, lngLat); this._needsRender = true; } _dispose(id) { const marker = this._markers[id]; this._scene.remove(marker.geometry); for (let interactiveObject of marker.getInteractiveObjects()) { const index = this._interactiveObjects.indexOf(interactiveObject); if (index !== -1) { this._interactiveObjects.splice(index, 1); } else { console.warn(`Object does not exist (${interactiveObject.id}) for ${id}`); } delete this._objectMarkers[interactiveObject.uuid]; } marker.disposeGeometry(); delete this._markers[id]; } } /** * @class MarkerComponent * * @classdesc Component for showing and editing 3D marker objects. * * The `add` method is used for adding new markers or replacing * markers already in the set. * * If a marker already in the set has the same * id as one of the markers added, the old marker will be removed and * the added marker will take its place. * * It is not possible to update markers in the set by updating any properties * directly on the marker object. Markers need to be replaced by * re-adding them for updates to geographic position or configuration * to be reflected. * * Markers added to the marker component can be either interactive * or non-interactive. Different marker types define their behavior. * Markers with interaction support can be configured with options * to respond to dragging inside the viewer and be detected when * retrieving markers from pixel points with the `getMarkerIdAt` method. * * To retrive and use the marker component * * @example * ```js * var viewer = new Viewer({ component: { marker: true }, ... }); * * var markerComponent = viewer.getComponent("marker"); * ``` */ class MarkerComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._graphCalculator = new GraphCalculator(); this._markerScene = new MarkerScene(); this._markerSet = new MarkerSet(); this._viewportCoords = new ViewportCoords(); this._relativeGroundAltitude = -2; } /** * Add markers to the marker set or replace markers in the marker set. * * @description If a marker already in the set has the same * id as one of the markers added, the old marker will be removed * the added marker will take its place. * * Any marker inside the visible bounding bbox * will be initialized and placed in the viewer. * * @param {Array} markers - Markers to add. * * @example * ```js * markerComponent.add([marker1, marker2]); * ``` */ add(markers) { this._markerSet.add(markers); } fire(type, event) { super.fire(type, event); } /** * Returns the marker in the marker set with the specified id, or * undefined if the id matches no marker. * * @param {string} markerId - Id of the marker. * * @example * ```js * var marker = markerComponent.get("markerId"); * ``` * */ get(markerId) { return this._markerSet.get(markerId); } /** * Returns an array of all markers. * * @example * ```js * var markers = markerComponent.getAll(); * ``` */ getAll() { return this._markerSet.getAll(); } /** * Returns the id of the interactive marker closest to the current camera * position at the specified point. * * @description Notice that the pixelPoint argument requires x, y * coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the marker component. * * If no interactive geometry of an interactive marker exist at the pixel * point, `null` will be returned. * * @param {Array} pixelPoint - Pixel coordinates on the viewer element. * @returns {string} Id of the interactive marker closest to the camera. If no * interactive marker exist at the pixel point, `null` will be returned. * * @example * ```js * markerComponent.getMarkerIdAt([100, 100]) * .then((markerId) => { console.log(markerId); }); * ``` */ getMarkerIdAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const id = this._markerScene.intersectObjects(viewport, render.perspective); return id; })) .subscribe((id) => { resolve(id); }, (error) => { reject(error); }); }); } /** * Check if a marker exist in the marker set. * * @param {string} markerId - Id of the marker. * * @example * ```js * var markerExists = markerComponent.has("markerId"); * ``` */ has(markerId) { return this._markerSet.has(markerId); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Remove markers with the specified ids from the marker set. * * @param {Array} markerIds - Ids for markers to remove. * * @example * ```js * markerComponent.remove(["id-1", "id-2"]); * ``` */ remove(markerIds) { this._markerSet.remove(markerIds); } /** * Remove all markers from the marker set. * * @example * ```js * markerComponent.removeAll(); * ``` */ removeAll() { this._markerSet.removeAll(); } _activate() { const groundAltitude$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.camera.position.z + this._relativeGroundAltitude; }), distinctUntilChanged((a1, a2) => { return Math.abs(a1 - a2) < 0.01; }), publishReplay(1), refCount()); const geoInitiated$ = combineLatest(groundAltitude$, this._navigator.stateService.reference$).pipe(first(), map(() => { }), publishReplay(1), refCount()); const clampedConfiguration$ = this._configuration$.pipe(map((configuration) => { return { visibleBBoxSize: Math.max(1, Math.min(200, configuration.visibleBBoxSize)) }; })); const currentLngLat$ = this._navigator.stateService.currentImage$.pipe(map((image) => { return image.lngLat; }), publishReplay(1), refCount()); const visibleBBox$ = combineLatest(clampedConfiguration$, currentLngLat$).pipe(map(([configuration, lngLat]) => { return this._graphCalculator .boundingBoxCorners(lngLat, configuration.visibleBBoxSize / 2); }), publishReplay(1), refCount()); const visibleMarkers$ = combineLatest(concat(of(this._markerSet), this._markerSet.changed$), visibleBBox$).pipe(map(([set, bbox]) => { return set.search(bbox); })); const subs = this._subscriptions; subs.push(geoInitiated$.pipe(switchMap(() => { return visibleMarkers$.pipe(withLatestFrom(this._navigator.stateService.reference$, groundAltitude$)); })) .subscribe(([markers, reference, alt]) => { const markerScene = this._markerScene; const sceneMarkers = markerScene.markers; const markersToRemove = Object.assign({}, sceneMarkers); for (const marker of markers) { if (marker.id in sceneMarkers) { delete markersToRemove[marker.id]; } else { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.add(marker, point3d); } } for (const id in markersToRemove) { if (!markersToRemove.hasOwnProperty(id)) { continue; } markerScene.remove(id); } })); subs.push(geoInitiated$.pipe(switchMap(() => { return this._markerSet.updated$.pipe(withLatestFrom(visibleBBox$, this._navigator.stateService.reference$, groundAltitude$)); })) .subscribe(([markers, [sw, ne], reference, alt]) => { const markerScene = this._markerScene; for (const marker of markers) { const exists = markerScene.has(marker.id); const visible = marker.lngLat.lat > sw.lat && marker.lngLat.lat < ne.lat && marker.lngLat.lng > sw.lng && marker.lngLat.lng < ne.lng; if (visible) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.add(marker, point3d); } else if (!visible && exists) { markerScene.remove(marker.id); } } })); subs.push(this._navigator.stateService.reference$.pipe(skip(1), withLatestFrom(groundAltitude$)) .subscribe(([reference, alt]) => { const markerScene = this._markerScene; for (const marker of markerScene.getAll()) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); markerScene.update(marker.id, point3d); } })); subs.push(groundAltitude$.pipe(skip(1), withLatestFrom(this._navigator.stateService.reference$, currentLngLat$)) .subscribe(([alt, reference, lngLat]) => { const markerScene = this._markerScene; const position = geodeticToEnu(lngLat.lng, lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); for (const marker of markerScene.getAll()) { const point3d = geodeticToEnu(marker.lngLat.lng, marker.lngLat.lat, reference.alt + alt, reference.lng, reference.lat, reference.alt); const distanceX = point3d[0] - position[0]; const distanceY = point3d[1] - position[1]; const groundDistance = Math .sqrt(distanceX * distanceX + distanceY * distanceY); if (groundDistance > 50) { continue; } markerScene.lerpAltitude(marker.id, alt, Math.min(1, Math.max(0, 1.2 - 1.2 * groundDistance / 50))); } })); subs.push(this._navigator.stateService.currentState$ .pipe(map((frame) => { const scene = this._markerScene; return { name: this._name, renderer: { frameId: frame.id, needsRender: scene.needsRender, render: scene.render.bind(scene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); const hoveredMarkerId$ = combineLatest(this._container.renderService.renderCamera$, this._container.mouseService.mouseMove$) .pipe(map(([render, event]) => { const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const viewport = this._viewportCoords .canvasToViewport(canvasX, canvasY, element); const markerId = this._markerScene.intersectObjects(viewport, render.perspective); return markerId; }), publishReplay(1), refCount()); const draggingStarted$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragStart$).pipe(map(() => { return true; })); const draggingStopped$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragEnd$).pipe(map(() => { return false; })); const filteredDragging$ = merge(draggingStarted$, draggingStopped$) .pipe(startWith(false)); subs.push(merge(draggingStarted$.pipe(withLatestFrom(hoveredMarkerId$)), combineLatest(draggingStopped$, of(null))).pipe(startWith([false, null]), pairwise()) .subscribe(([previous, current]) => { const dragging = current[0]; const type = dragging ? "markerdragstart" : "markerdragend"; const id = dragging ? current[1] : previous[1]; const marker = this._markerScene.get(id); const event = { marker, target: this, type, }; this.fire(type, event); })); const mouseDown$ = merge(this._container.mouseService.mouseDown$.pipe(map(() => { return true; })), this._container.mouseService.documentMouseUp$.pipe(map(() => { return false; }))).pipe(startWith(false)); subs.push(combineLatest(this._container.mouseService.active$, hoveredMarkerId$.pipe(distinctUntilChanged()), mouseDown$, filteredDragging$) .pipe(map(([active, markerId, mouseDown, filteredDragging]) => { return (!active && markerId != null && mouseDown) || filteredDragging; }), distinctUntilChanged()) .subscribe((claim) => { if (claim) { this._container.mouseService.claimMouse(this._name, 1); this._container.mouseService.claimWheel(this._name, 1); } else { this._container.mouseService.unclaimMouse(this._name); this._container.mouseService.unclaimWheel(this._name); } })); const offset$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDragStart$).pipe(withLatestFrom(hoveredMarkerId$, this._container.renderService.renderCamera$), map(([e, id, r]) => { const marker = this._markerScene.get(id); const element = this._container.container; const [groundCanvasX, groundCanvasY] = this._viewportCoords .projectToCanvas(marker.geometry.position .toArray(), element, r.perspective); const [canvasX, canvasY] = this._viewportCoords .canvasPosition(e, element); const offset = [canvasX - groundCanvasX, canvasY - groundCanvasY]; return [marker, offset, r]; }), publishReplay(1), refCount()); subs.push(this._container.mouseService .filtered$(this._name, this._container.mouseService.mouseDrag$) .pipe(withLatestFrom(offset$, this._navigator.stateService.reference$, clampedConfiguration$)) .subscribe(([event, [marker, offset, render], reference, configuration]) => { if (!this._markerScene.has(marker.id)) { return; } const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords .canvasPosition(event, element); const groundX = canvasX - offset[0]; const groundY = canvasY - offset[1]; const [viewportX, viewportY] = this._viewportCoords .canvasToViewport(groundX, groundY, element); const direction = new Vector3(viewportX, viewportY, 1) .unproject(render.perspective) .sub(render.perspective.position) .normalize(); const distance = Math.min(this._relativeGroundAltitude / direction.z, configuration.visibleBBoxSize / 2 - 0.1); if (distance < 0) { return; } const intersection = direction .clone() .multiplyScalar(distance) .add(render.perspective.position); intersection.z = render.perspective.position.z + this._relativeGroundAltitude; const [lng, lat] = enuToGeodetic(intersection.x, intersection.y, intersection.z, reference.lng, reference.lat, reference.alt); this._markerScene .update(marker.id, intersection.toArray(), { lat, lng }); this._markerSet.update(marker); const type = "markerposition"; const markerEvent = { marker, target: this, type, }; this.fire(type, markerEvent); })); } _deactivate() { this._subscriptions.unsubscribe(); this._markerScene.clear(); } _getDefaultConfiguration() { return { visibleBBoxSize: 100 }; } } MarkerComponent.componentName = "marker"; function sign$1(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; } function colinearPointOnSegment(p, s) { return p.x <= Math.max(s.p1.x, s.p2.x) && p.x >= Math.min(s.p1.x, s.p2.x) && p.y >= Math.max(s.p1.y, s.p2.y) && p.y >= Math.min(s.p1.y, s.p2.y); } function parallel(s1, s2) { const ux = s1.p2.x - s1.p1.x; const uy = s1.p2.y - s1.p1.y; const vx = s2.p2.x - s2.p1.x; const vy = s2.p2.y - s2.p1.y; const cross = ux * vy - uy * vx; const u2 = ux * ux + uy * uy; const v2 = vx * vx + vy * vy; const epsilon2 = 1e-10; return cross * cross < epsilon2 * u2 * v2; } function tripletOrientation(p1, p2, p3) { const orientation = (p2.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p2.x - p1.x); return sign$1(orientation); } function segmentsIntersect(s1, s2) { if (parallel(s1, s2)) { return false; } const o1 = tripletOrientation(s1.p1, s1.p2, s2.p1); const o2 = tripletOrientation(s1.p1, s1.p2, s2.p2); const o3 = tripletOrientation(s2.p1, s2.p2, s1.p1); const o4 = tripletOrientation(s2.p1, s2.p2, s1.p2); if (o1 !== o2 && o3 !== o4) { return true; } if (o1 === 0 && colinearPointOnSegment(s2.p1, s1)) { return true; } if (o2 === 0 && colinearPointOnSegment(s2.p2, s1)) { return true; } if (o3 === 0 && colinearPointOnSegment(s1.p1, s2)) { return true; } if (o4 === 0 && colinearPointOnSegment(s1.p2, s2)) { return true; } return false; } function segmentIntersection(s1, s2) { if (parallel(s1, s2)) { return undefined; } const x1 = s1.p1.x; const x2 = s1.p2.x; const y1 = s1.p1.y; const y2 = s1.p2.y; const x3 = s2.p1.x; const x4 = s2.p2.x; const y3 = s2.p1.y; const y4 = s2.p2.y; const den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); const xNum = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4); const yNum = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4); return { x: xNum / den, y: yNum / den }; } function basicBoundaryPoints(pointsPerSide) { let points = []; let os = [[0, 0], [1, 0], [1, 1], [0, 1]]; let ds = [[1, 0], [0, 1], [-1, 0], [0, -1]]; for (let side = 0; side < 4; ++side) { let o = os[side]; let d = ds[side]; for (let i = 0; i < pointsPerSide; ++i) { points.push([o[0] + d[0] * i / pointsPerSide, o[1] + d[1] * i / pointsPerSide]); } } return points; } function insideViewport(x, y) { return x >= -1 && x <= 1 && y >= -1 && y <= 1; } function insideBasic(x, y) { return x >= 0 && x <= 1 && y >= 0 && y <= 1; } function viewportDistances(transform, perspective, viewportCoords) { const boundaryPointsBasic = basicBoundaryPoints(100); const boundaryPointsViewport = boundaryPointsBasic .map((basic) => { return viewportCoords.basicToViewportSafe(basic[0], basic[1], transform, perspective); }); const visibleBoundaryPoints = []; const viewportSides = [ { x: -1, y: 1 }, { x: 1, y: 1 }, { x: 1, y: -1 }, { x: -1, y: -1 } ]; const intersections = [false, false, false, false]; for (let i = 0; i < boundaryPointsViewport.length; i++) { const p1 = boundaryPointsViewport[i]; const p2 = boundaryPointsViewport[(i + 1) % boundaryPointsViewport.length]; if (p1 === null) { continue; } if (p2 === null) { if (insideViewport(p1[0], p1[1])) { visibleBoundaryPoints.push(p1); } continue; } const [x1, y1] = p1; const [x2, y2] = p2; if (insideViewport(x1, y1)) { if (insideViewport(x2, y2)) { visibleBoundaryPoints.push(p1); } else { for (let side = 0; side < 4; side++) { const s1 = { p1: { x: x1, y: y1 }, p2: { x: x2, y: y2 } }; const s2 = { p1: viewportSides[side], p2: viewportSides[(side + 1) % 4] }; const intersecting = segmentsIntersect(s1, s2); if (intersecting) { const intersection = segmentIntersection(s1, s2); visibleBoundaryPoints.push(p1, [intersection.x, intersection.y]); intersections[side] = true; } } } } } const [topLeftBasicX, topLeftBasicY] = viewportCoords.viewportToBasic(-1, 1, transform, perspective); const [topRightBasicX, topRightBasicY] = viewportCoords.viewportToBasic(1, 1, transform, perspective); const [bottomRightBasicX, bottomRightBasicY] = viewportCoords.viewportToBasic(1, -1, transform, perspective); const [bottomLeftBasicX, bottomLeftBasicY] = viewportCoords.viewportToBasic(-1, -1, transform, perspective); if (insideBasic(topLeftBasicX, topLeftBasicY)) { intersections[3] = intersections[0] = true; } if (insideBasic(topRightBasicX, topRightBasicY)) { intersections[0] = intersections[1] = true; } if (insideBasic(bottomRightBasicX, bottomRightBasicY)) { intersections[1] = intersections[2] = true; } if (insideBasic(bottomLeftBasicX, bottomLeftBasicY)) { intersections[2] = intersections[3] = true; } const maximums = [-1, -1, 1, 1]; for (let visibleBoundaryPoint of visibleBoundaryPoints) { const x = visibleBoundaryPoint[0]; const y = visibleBoundaryPoint[1]; if (x > maximums[1]) { maximums[1] = x; } if (x < maximums[3]) { maximums[3] = x; } if (y > maximums[0]) { maximums[0] = y; } if (y < maximums[2]) { maximums[2] = y; } } const boundary = [1, 1, -1, -1]; const distances = []; for (let side = 0; side < 4; side++) { if (intersections[side]) { distances.push(0); continue; } distances.push(Math.abs(boundary[side] - maximums[side])); } return distances; } /** * The `BounceHandler` ensures that the viewer bounces back to the image * when drag panning outside of the image edge. */ class BounceHandler extends HandlerBase { constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; } _enable() { const inTransition$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return frame.state.alpha < 1; }), distinctUntilChanged()); this._bounceSubscription = combineLatest(inTransition$, this._navigator.stateService.inTranslation$, this._container.mouseService.active$, this._container.touchService.active$).pipe(map((noForce) => { return noForce[0] || noForce[1] || noForce[2] || noForce[3]; }), distinctUntilChanged(), switchMap((noForce) => { return noForce ? empty() : combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$.pipe(first())); }), withLatestFrom(this._navigator.panService.panImages$)) .subscribe(([[render, transform], nts]) => { if (!transform.hasValidScale && render.camera.focal < 0.1) { return; } if (render.perspective.aspect === 0 || render.perspective.aspect === Number.POSITIVE_INFINITY) { return; } const distances = viewportDistances(transform, render.perspective, this._viewportCoords); const basic = this._viewportCoords.viewportToBasic(0, 0, transform, render.perspective); if ((basic[0] < 0 || basic[0] > 1) && nts.length > 0) { distances[0] = distances[2] = 0; } for (const [, t] of nts) { const d = viewportDistances(t, render.perspective, this._viewportCoords); for (let i = 1; i < distances.length; i += 2) { if (d[i] < distances[i]) { distances[i] = d[i]; } } } if (Math.max(...distances) < 0.01) { return; } const horizontalDistance = distances[1] - distances[3]; const verticalDistance = distances[0] - distances[2]; const currentDirection = this._viewportCoords .unprojectFromViewport(0, 0, render.perspective) .sub(render.perspective.position); const directionPhi = this._viewportCoords .unprojectFromViewport(horizontalDistance, 0, render.perspective) .sub(render.perspective.position); const directionTheta = this._viewportCoords .unprojectFromViewport(0, verticalDistance, render.perspective) .sub(render.perspective.position); let phi = (horizontalDistance > 0 ? 1 : -1) * directionPhi.angleTo(currentDirection); let theta = (verticalDistance > 0 ? 1 : -1) * directionTheta.angleTo(currentDirection); const threshold = Math.PI / 60; const coeff = 1e-1; phi = this._spatial.clamp(coeff * phi, -threshold, threshold); theta = this._spatial.clamp(coeff * theta, -threshold, threshold); this._navigator.stateService.rotateUnbounded({ phi: phi, theta: theta }); }); } _disable() { this._bounceSubscription.unsubscribe(); } _getConfiguration() { return {}; } } class MouseOperator { static filteredPairwiseMouseDrag$(name, mouseService) { return this._filteredPairwiseMouseDrag$(name, mouseService, mouseService.mouseDragStart$, mouseService.mouseDrag$, mouseService.mouseDragEnd$); } static filteredPairwiseMouseRightDrag$(name, mouseService) { return this._filteredPairwiseMouseDrag$(name, mouseService, mouseService.mouseRightDragStart$, mouseService.mouseRightDrag$, mouseService.mouseRightDragEnd$); } static _filteredPairwiseMouseDrag$(name, mouseService, mouseDragStart$, mouseDrag$, mouseDragEnd$) { return mouseService .filtered$(name, mouseDragStart$).pipe(switchMap((mouseDragStart) => { const dragging$ = concat(of(mouseDragStart), mouseService .filtered$(name, mouseDrag$)); const dragEnd$ = mouseService .filtered$(name, mouseDragEnd$).pipe(map(() => { return null; })); return merge(dragging$, dragEnd$).pipe(takeWhile((e) => { return !!e; }), startWith(null)); }), pairwise(), filter((pair) => { return pair[0] != null && pair[1] != null; })); } } /** * The `DragPanHandler` allows the user to pan the viewer image by clicking and dragging the cursor. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.dragPan.disable(); * pointerComponent.dragPan.enable(); * * var isEnabled = pointerComponent.dragPan.isEnabled; * ``` */ class DragPanHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; } _enable() { let draggingStarted$ = this._container.mouseService .filtered$(this._component.name, this._container.mouseService.mouseDragStart$).pipe(map(() => { return true; }), share()); let draggingStopped$ = this._container.mouseService .filtered$(this._component.name, this._container.mouseService.mouseDragEnd$).pipe(map(() => { return false; }), share()); this._activeMouseSubscription = merge(draggingStarted$, draggingStopped$) .subscribe(this._container.mouseService.activate$); const documentMouseMove$ = merge(draggingStarted$, draggingStopped$).pipe(switchMap((dragging) => { return dragging ? this._container.mouseService.documentMouseMove$ : empty(); })); this._preventDefaultSubscription = merge(documentMouseMove$, this._container.touchService.touchMove$) .subscribe((event) => { event.preventDefault(); // prevent selection of content outside the viewer }); let touchMovingStarted$ = this._container.touchService.singleTouchDragStart$.pipe(map(() => { return true; })); let touchMovingStopped$ = this._container.touchService.singleTouchDragEnd$.pipe(map(() => { return false; })); this._activeTouchSubscription = merge(touchMovingStarted$, touchMovingStopped$) .subscribe(this._container.touchService.activate$); const rotation$ = this._navigator.stateService.currentState$.pipe(map((frame) => { return isSpherical(frame.state.currentImage.cameraType) || frame.state.imagesAhead < 1; }), distinctUntilChanged(), switchMap((enable) => { if (!enable) { return empty(); } const mouseDrag$ = MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService); const singleTouchDrag$ = merge(this._container.touchService.singleTouchDragStart$, this._container.touchService.singleTouchDrag$, this._container.touchService.singleTouchDragEnd$.pipe(map(() => { return null; }))).pipe(map((event) => { return event != null && event.touches.length > 0 ? event.touches[0] : null; }), pairwise(), filter((pair) => { return pair[0] != null && pair[1] != null; })); return merge(mouseDrag$, singleTouchDrag$); }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$, this._navigator.panService.panImages$), map(([events, render, transform, nts]) => { let previousEvent = events[0]; let event = events[1]; let movementX = event.clientX - previousEvent.clientX; let movementY = event.clientY - previousEvent.clientY; let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); let currentDirection = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective) .sub(render.perspective.position); let directionX = this._viewportCoords.unprojectFromCanvas(canvasX - movementX, canvasY, element, render.perspective) .sub(render.perspective.position); let directionY = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY - movementY, element, render.perspective) .sub(render.perspective.position); let phi = (movementX > 0 ? 1 : -1) * directionX.angleTo(currentDirection); let theta = (movementY > 0 ? -1 : 1) * directionY.angleTo(currentDirection); const distances = viewportDistances(transform, render.perspective, this._viewportCoords); for (const [, t] of nts) { const d = viewportDistances(t, render.perspective, this._viewportCoords); for (let i = 0; i < distances.length; i++) { if (d[i] < distances[i]) { distances[i] = d[i]; } } } if (distances[0] > 0 && theta < 0) { theta /= Math.max(1, 2e2 * distances[0]); } if (distances[2] > 0 && theta > 0) { theta /= Math.max(1, 2e2 * distances[2]); } if (distances[1] > 0 && phi < 0) { phi /= Math.max(1, 2e2 * distances[1]); } if (distances[3] > 0 && phi > 0) { phi /= Math.max(1, 2e2 * distances[3]); } return { phi: phi, theta: theta }; }), share()); this._rotateWithoutInertiaSubscription = rotation$ .subscribe((rotation) => { this._navigator.stateService.rotateWithoutInertia(rotation); }); this._rotateSubscription = rotation$.pipe(scan((rotationBuffer, rotation) => { this._drainBuffer(rotationBuffer); rotationBuffer.push([Date.now(), rotation]); return rotationBuffer; }, []), sample(merge(this._container.mouseService.filtered$(this._component.name, this._container.mouseService.mouseDragEnd$), this._container.touchService.singleTouchDragEnd$)), map((rotationBuffer) => { const drainedBuffer = this._drainBuffer(rotationBuffer.slice()); const rotation = { phi: 0, theta: 0 }; for (const bufferedRotation of drainedBuffer) { rotation.phi += bufferedRotation[1].phi; rotation.theta += bufferedRotation[1].theta; } const count = drainedBuffer.length; if (count > 0) { rotation.phi /= count; rotation.theta /= count; } const threshold = Math.PI / 18; rotation.phi = this._spatial.clamp(rotation.phi, -threshold, threshold); rotation.theta = this._spatial.clamp(rotation.theta, -threshold, threshold); return rotation; })) .subscribe((rotation) => { this._navigator.stateService.rotate(rotation); }); } _disable() { this._activeMouseSubscription.unsubscribe(); this._activeTouchSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._rotateSubscription.unsubscribe(); this._rotateWithoutInertiaSubscription.unsubscribe(); this._activeMouseSubscription = null; this._activeTouchSubscription = null; this._preventDefaultSubscription = null; this._rotateSubscription = null; } _getConfiguration(enable) { return { dragPan: enable }; } _drainBuffer(buffer) { const cutoff = 50; const now = Date.now(); while (buffer.length > 0 && now - buffer[0][0] > cutoff) { buffer.shift(); } return buffer; } } class EarthControlHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords, spatial) { super(component, container, navigator); this._spatial = spatial; this._viewportCoords = viewportCoords; this._subscriptions = new SubscriptionHolder(); } _enable() { const earth$ = this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(earth$.pipe(switchMap((earth) => { return earth ? this._container.mouseService.mouseWheel$ : empty(); })) .subscribe((event) => { event.preventDefault(); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return !(e1.ctrlKey && e2.ctrlKey); })); }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$), map(([[previous, current], render, transform]) => { const planeNormal = [0, 0, 1]; const planePoint = [0, 0, -2]; const currentIntersection = this._planeIntersection(current, planeNormal, planePoint, render.perspective, this._container.container); const previousIntersection = this._planeIntersection(previous, planeNormal, planePoint, render.perspective, this._container.container); if (!currentIntersection || !previousIntersection) { return null; } const direction = new Vector3() .subVectors(currentIntersection, previousIntersection) .multiplyScalar(-1) .toArray(); return direction; }), filter((direction) => { return !!direction; })) .subscribe((direction) => { this._navigator.stateService.truck(direction); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return e1.ctrlKey && e2.ctrlKey; })); }), map(([previous, current]) => { return this._mousePairToRotation(previous, current); })) .subscribe((rotation) => { this._navigator.stateService.orbit(rotation); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return MouseOperator.filteredPairwiseMouseRightDrag$(this._component.name, this._container.mouseService).pipe(filter(([e1, e2]) => { return !e1.ctrlKey && !e2.ctrlKey; })); }), map(([previous, current]) => { return this._mousePairToRotation(previous, current); })) .subscribe((rotation) => { this._navigator.stateService.orbit(rotation); })); subs.push(earth$.pipe(switchMap((earth) => { if (!earth) { return empty(); } return this._container.mouseService .filteredWheel$(this._component.name, this._container.mouseService.mouseWheel$); }), map((event) => { let delta = event.deltaY; if (event.deltaMode === 1) { delta = 40 * delta; } else if (event.deltaMode === 2) { delta = 800 * delta; } const canvasSize = this._viewportCoords.containerToCanvas(this._container.container); return -delta / canvasSize[1]; })) .subscribe((delta) => { this._navigator.stateService.dolly(delta); })); } _disable() { this._subscriptions.unsubscribe(); } _getConfiguration() { return {}; } _eventToViewport(event, element) { const previousCanvas = this._viewportCoords.canvasPosition(event, element); return this._viewportCoords.canvasToViewport(previousCanvas[0], previousCanvas[1], element); } _mousePairToRotation(previous, current) { const [currentX, currentY] = this._eventToViewport(current, this._container.container); const [previousX, previousY] = this._eventToViewport(previous, this._container.container); const phi = (previousX - currentX) * Math.PI; const theta = (currentY - previousY) * Math.PI / 2; return { phi: phi, theta: theta }; } _planeIntersection(event, planeNormal, planePoint, camera, element) { const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const direction = this._viewportCoords .unprojectFromCanvas(canvasX, canvasY, element, camera) .sub(camera.position) .normalize(); if (Math.abs(this._spatial.angleToPlane(direction.toArray(), planeNormal)) < Math.PI / 90) { return null; } const l0 = camera.position.clone(); const n = new Vector3().fromArray(planeNormal); const p0 = new Vector3().fromArray(planePoint); const d = new Vector3().subVectors(p0, l0).dot(n) / direction.clone().dot(n); const intersection = new Vector3().addVectors(l0, direction.multiplyScalar(d)); if (this._viewportCoords.worldToCamera(intersection.toArray(), camera)[2] > 0) { return null; } return intersection; } } /** * The `ScrollZoomHandler` allows the user to zoom the viewer image by scrolling. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.scrollZoom.disable(); * pointerComponent.scrollZoom.enable(); * * var isEnabled = pointerComponent.scrollZoom.isEnabled; * ``` */ class ScrollZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._container.mouseService.claimWheel(this._component.name, 0); this._preventDefaultSubscription = this._container.mouseService.mouseWheel$ .subscribe((event) => { event.preventDefault(); }); this._zoomSubscription = this._container.mouseService .filteredWheel$(this._component.name, this._container.mouseService.mouseWheel$).pipe(withLatestFrom(this._navigator.stateService.currentState$, (w, f) => { return [w, f]; }), filter((args) => { let state = args[1].state; return isSpherical(state.currentImage.cameraType) || state.imagesAhead < 1; }), map((args) => { return args[0]; }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$, (w, r, t) => { return [w, r, t]; })) .subscribe((args) => { let event = args[0]; let render = args[1]; let transform = args[2]; let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); let unprojected = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective); let reference = transform.projectBasic(unprojected.toArray()); let deltaY = event.deltaY; if (event.deltaMode === 1) { deltaY = 40 * deltaY; } else if (event.deltaMode === 2) { deltaY = 800 * deltaY; } const canvasSize = this._viewportCoords.containerToCanvas(element); let zoom = -3 * deltaY / canvasSize[1]; this._navigator.stateService.zoomIn(zoom, reference); }); } _disable() { this._container.mouseService.unclaimWheel(this._component.name); this._preventDefaultSubscription.unsubscribe(); this._zoomSubscription.unsubscribe(); this._preventDefaultSubscription = null; this._zoomSubscription = null; } _getConfiguration(enable) { return { scrollZoom: enable }; } } /** * The `TouchZoomHandler` allows the user to zoom the viewer image by pinching on a touchscreen. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * * pointerComponent.touchZoom.disable(); * pointerComponent.touchZoom.enable(); * * var isEnabled = pointerComponent.touchZoom.isEnabled; * ``` */ class TouchZoomHandler extends HandlerBase { /** @ignore */ constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._viewportCoords = viewportCoords; } _enable() { this._preventDefaultSubscription = this._container.touchService.pinch$ .subscribe((pinch) => { pinch.originalEvent.preventDefault(); }); let pinchStarted$ = this._container.touchService.pinchStart$.pipe(map((event) => { return true; })); let pinchStopped$ = this._container.touchService.pinchEnd$.pipe(map((event) => { return false; })); this._activeSubscription = merge(pinchStarted$, pinchStopped$) .subscribe(this._container.touchService.activate$); this._zoomSubscription = this._container.touchService.pinch$.pipe(withLatestFrom(this._navigator.stateService.currentState$), filter((args) => { let state = args[1].state; return isSpherical(state.currentImage.cameraType) || state.imagesAhead < 1; }), map((args) => { return args[0]; }), withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([pinch, render, transform]) => { let element = this._container.container; let [canvasX, canvasY] = this._viewportCoords.canvasPosition(pinch, element); let unprojected = this._viewportCoords.unprojectFromCanvas(canvasX, canvasY, element, render.perspective); let reference = transform.projectBasic(unprojected.toArray()); const [canvasWidth, canvasHeight] = this._viewportCoords.containerToCanvas(element); let zoom = 3 * pinch.distanceChange / Math.min(canvasWidth, canvasHeight); this._navigator.stateService.zoomIn(zoom, reference); }); } _disable() { this._activeSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._zoomSubscription.unsubscribe(); this._preventDefaultSubscription = null; this._zoomSubscription = null; } _getConfiguration(enable) { return { touchZoom: enable }; } } /** * @class PointerComponent * * @classdesc Component handling mouse, pen, and touch events for camera movement. * * To retrive and use the mouse component * * @example * ```js * var viewer = new Viewer({ ... }); * * var pointerComponent = viewer.getComponent("pointer"); * ``` */ class PointerComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); const spatial = new Spatial(); const viewportCoords = new ViewportCoords(); this._bounceHandler = new BounceHandler(this, container, navigator, viewportCoords, spatial); this._dragPanHandler = new DragPanHandler(this, container, navigator, viewportCoords, spatial); this._earthControlHandler = new EarthControlHandler(this, container, navigator, viewportCoords, spatial); this._scrollZoomHandler = new ScrollZoomHandler(this, container, navigator, viewportCoords); this._touchZoomHandler = new TouchZoomHandler(this, container, navigator, viewportCoords); } /** * Get drag pan. * * @returns {DragPanHandler} The drag pan handler. */ get dragPan() { return this._dragPanHandler; } /** * Get earth control. * * @returns {EarthControlHandler} The earth control handler. */ get earthControl() { return this._earthControlHandler; } /** * Get scroll zoom. * * @returns {ScrollZoomHandler} The scroll zoom handler. */ get scrollZoom() { return this._scrollZoomHandler; } /** * Get touch zoom. * * @returns {TouchZoomHandler} The touch zoom handler. */ get touchZoom() { return this._touchZoomHandler; } _activate() { this._bounceHandler.enable(); this._subscriptions.push(this._configuration$ .subscribe((configuration) => { if (configuration.dragPan) { this._dragPanHandler.enable(); } else { this._dragPanHandler.disable(); } if (configuration.earthControl) { this._earthControlHandler.enable(); } else { this._earthControlHandler.disable(); } if (configuration.scrollZoom) { this._scrollZoomHandler.enable(); } else { this._scrollZoomHandler.disable(); } if (configuration.touchZoom) { this._touchZoomHandler.enable(); } else { this._touchZoomHandler.disable(); } })); this._container.mouseService.claimMouse(this._name, 0); } _deactivate() { this._container.mouseService.unclaimMouse(this._name); this._subscriptions.unsubscribe(); this._bounceHandler.disable(); this._dragPanHandler.disable(); this._earthControlHandler.disable(); this._scrollZoomHandler.disable(); this._touchZoomHandler.disable(); } _getDefaultConfiguration() { return { dragPan: true, earthControl: true, scrollZoom: true, touchZoom: true, }; } } /** @inheritdoc */ PointerComponent.componentName = "pointer"; class DOM { constructor(doc) { this._document = !!doc ? doc : document; } get document() { return this._document; } createElement(tagName, className, container) { const element = this._document.createElement(tagName); if (!!className) { element.className = className; } if (!!container) { container.appendChild(element); } return element; } } /** * @class PopupComponent * * @classdesc Component for showing HTML popup objects. * * The `add` method is used for adding new popups. Popups are removed by reference. * * It is not possible to update popups in the set by updating any properties * directly on the popup object. Popups need to be replaced by * removing them and creating new ones with relevant changed properties and * adding those instead. * * Popups are only relevant to a single image because they are based on * 2D basic image coordinates. Popups related to a certain image should * be removed when the viewer is moved to another image. * * To retrive and use the popup component * * @example * ```js * var viewer = new Viewer({ component: { popup: true }, ... }); * * var popupComponent = viewer.getComponent("popup"); * ``` */ class PopupComponent extends Component { /** @ignore */ constructor(name, container, navigator, dom) { super(name, container, navigator); this._dom = !!dom ? dom : new DOM(); this._popups = []; this._added$ = new Subject(); this._popups$ = new Subject(); } /** * Add popups to the popups set. * * @description Adding a new popup never replaces an old one * because they are stored by reference. Adding an already * existing popup has no effect. * * @param {Array} popups - Popups to add. * * @example * ```js * popupComponent.add([popup1, popup2]); * ``` */ add(popups) { for (const popup of popups) { if (this._popups.indexOf(popup) !== -1) { continue; } this._popups.push(popup); if (this._activated) { popup.setParentContainer(this._popupContainer); } } this._added$.next(popups); this._popups$.next(this._popups); } /** * Returns an array of all popups. * * @example * ```js * var popups = popupComponent.getAll(); * ``` */ getAll() { return this._popups.slice(); } /** * Remove popups based on reference from the popup set. * * @param {Array} popups - Popups to remove. * * @example * ```js * popupComponent.remove([popup1, popup2]); * ``` */ remove(popups) { for (const popup of popups) { this._remove(popup); } this._popups$.next(this._popups); } /** * Remove all popups from the popup set. * * @example * ```js * popupComponent.removeAll(); * ``` */ removeAll() { for (const popup of this._popups.slice()) { this._remove(popup); } this._popups$.next(this._popups); } _activate() { this._popupContainer = this._dom.createElement("div", "mapillary-popup-container", this._container.container); for (const popup of this._popups) { popup.setParentContainer(this._popupContainer); } const subs = this._subscriptions; subs.push(combineLatest(this._container.renderService.renderCamera$, this._container.renderService.size$, this._navigator.stateService.currentTransform$) .subscribe(([renderCamera, size, transform]) => { for (const popup of this._popups) { popup.update(renderCamera, size, transform); } })); const changed$ = this._popups$.pipe(startWith(this._popups), switchMap((popups) => { return from(popups).pipe(mergeMap((popup) => { return popup.changed$; })); }), map((popup) => { return [popup]; })); subs.push(merge(this._added$, changed$).pipe(withLatestFrom(this._container.renderService.renderCamera$, this._container.renderService.size$, this._navigator.stateService.currentTransform$)) .subscribe(([popups, renderCamera, size, transform]) => { for (const popup of popups) { popup.update(renderCamera, size, transform); } })); } _deactivate() { this._subscriptions.unsubscribe(); for (const popup of this._popups) { popup.remove(); } this._container.container.removeChild(this._popupContainer); delete this._popupContainer; } _getDefaultConfiguration() { return {}; } _remove(popup) { const index = this._popups.indexOf(popup); if (index === -1) { return; } const removed = this._popups.splice(index, 1)[0]; if (this._activated) { removed.remove(); } } } PopupComponent.componentName = "popup"; /** * Enumeration for graph modes. * @enum {number} * @readonly * @description Modes for the retrieval and caching performed * by the graph service on the graph. */ var GraphMode; (function (GraphMode) { /** * Caching is performed on sequences only and sequence edges are * calculated. Spatial tiles * are not retrieved and spatial edges are not calculated when * caching nodes. Complete sequences are being cached for requested * nodes within the graph. */ GraphMode[GraphMode["Sequence"] = 0] = "Sequence"; /** * Caching is performed with emphasis on spatial data. Sequence edges * as well as spatial edges are cached. Sequence data * is still requested but complete sequences are not being cached * for requested nodes. * * This is the initial mode of the graph service. */ GraphMode[GraphMode["Spatial"] = 1] = "Spatial"; })(GraphMode || (GraphMode = {})); var SequenceMode; (function (SequenceMode) { SequenceMode[SequenceMode["Default"] = 0] = "Default"; SequenceMode[SequenceMode["Playback"] = 1] = "Playback"; SequenceMode[SequenceMode["Timeline"] = 2] = "Timeline"; })(SequenceMode || (SequenceMode = {})); class SequenceDOMRenderer { constructor(container) { this._container = container; this._minThresholdWidth = 320; this._maxThresholdWidth = 1480; this._minThresholdHeight = 240; this._maxThresholdHeight = 820; this._stepperDefaultWidth = 108; this._controlsDefaultWidth = 88; this._defaultHeight = 30; this._expandControls = false; this._mode = SequenceMode.Default; this._speed = 0.5; this._changingSpeed = false; this._index = null; this._changingPosition = false; this._mouseEnterDirection$ = new Subject(); this._mouseLeaveDirection$ = new Subject(); this._notifyChanged$ = new Subject(); this._notifyChangingPositionChanged$ = new Subject(); this._notifySpeedChanged$ = new Subject(); this._notifyIndexChanged$ = new Subject(); } get changed$() { return this._notifyChanged$; } get changingPositionChanged$() { return this._notifyChangingPositionChanged$; } get speed$() { return this._notifySpeedChanged$; } get index$() { return this._notifyIndexChanged$; } get mouseEnterDirection$() { return this._mouseEnterDirection$; } get mouseLeaveDirection$() { return this._mouseLeaveDirection$; } activate() { if (!!this._changingSubscription) { return; } this._changingSubscription = merge(this._container.mouseService.documentMouseUp$, this._container.touchService.touchEnd$.pipe(filter((touchEvent) => { return touchEvent.touches.length === 0; }))) .subscribe(() => { if (this._changingSpeed) { this._changingSpeed = false; } if (this._changingPosition) { this._setChangingPosition(false); } }); } deactivate() { if (!this._changingSubscription) { return; } this._changingSpeed = false; this._changingPosition = false; this._expandControls = false; this._mode = SequenceMode.Default; this._changingSubscription.unsubscribe(); this._changingSubscription = null; } render(edgeStatus, configuration, containerWidth, speed, index, max, playEnabled, component, navigator) { if (configuration.visible === false) { return virtualDom.h("div.mapillary-sequence-container", {}, []); } const stepper = this._createStepper(edgeStatus, configuration, playEnabled, containerWidth, component, navigator); const controls = this._createSequenceControls(containerWidth); const playback = this._createPlaybackControls(containerWidth, speed, component, configuration); const timeline = this._createTimelineControls(containerWidth, index, max); return virtualDom.h("div.mapillary-sequence-container", [stepper, controls, playback, timeline]); } getContainerWidth(size, configuration) { let minWidth = configuration.minWidth; let maxWidth = configuration.maxWidth; if (maxWidth < minWidth) { maxWidth = minWidth; } let relativeWidth = (size.width - this._minThresholdWidth) / (this._maxThresholdWidth - this._minThresholdWidth); let relativeHeight = (size.height - this._minThresholdHeight) / (this._maxThresholdHeight - this._minThresholdHeight); let coeff = Math.max(0, Math.min(1, Math.min(relativeWidth, relativeHeight))); return minWidth + coeff * (maxWidth - minWidth); } _createPositionInput(index, max) { this._index = index; const onPosition = (e) => { this._index = Number(e.target.value); this._notifyIndexChanged$.next(this._index); }; const boundingRect = this._container.domContainer.getBoundingClientRect(); const width = Math.max(276, Math.min(410, 5 + 0.8 * boundingRect.width)) - 65; const onStart = (e) => { e.stopPropagation(); this._setChangingPosition(true); }; const onMove = (e) => { if (this._changingPosition === true) { e.stopPropagation(); } }; const onKeyDown = (e) => { if (e.key === "ArrowDown" || e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); } }; const positionInputProperties = { max: max != null ? max : 1, min: 0, onchange: onPosition, oninput: onPosition, onkeydown: onKeyDown, onpointerdown: onStart, onpointermove: onMove, ontouchmove: onMove, ontouchstart: onStart, style: { width: `${width}px`, }, type: "range", value: index != null ? index : 0, }; const disabled = index == null || max == null || max <= 1; if (disabled) { positionInputProperties.disabled = "true"; } const positionInput = virtualDom.h("input.mapillary-sequence-position", positionInputProperties, []); const positionContainerClass = disabled ? ".mapillary-sequence-position-container-inactive" : ".mapillary-sequence-position-container"; return virtualDom.h("div" + positionContainerClass, [positionInput]); } _createSpeedInput(speed) { this._speed = speed; const onSpeed = (e) => { this._speed = Number(e.target.value) / 1000; this._notifySpeedChanged$.next(this._speed); }; const boundingRect = this._container.domContainer.getBoundingClientRect(); const width = Math.max(276, Math.min(410, 5 + 0.8 * boundingRect.width)) - 160; const onStart = (e) => { this._changingSpeed = true; e.stopPropagation(); }; const onMove = (e) => { if (this._changingSpeed === true) { e.stopPropagation(); } }; const onKeyDown = (e) => { if (e.key === "ArrowDown" || e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); } }; const speedInput = virtualDom.h("input.mapillary-sequence-speed", { max: 1000, min: 0, onchange: onSpeed, oninput: onSpeed, onkeydown: onKeyDown, onpointerdown: onStart, onpointermove: onMove, ontouchmove: onMove, ontouchstart: onStart, style: { width: `${width}px`, }, type: "range", value: 1000 * speed, }, []); return virtualDom.h("div.mapillary-sequence-speed-container", [speedInput]); } _createPlaybackControls(containerWidth, speed, component, configuration) { if (this._mode !== SequenceMode.Playback) { return virtualDom.h("div.mapillary-sequence-playback", []); } const switchIcon = virtualDom.h("div.mapillary-sequence-switch-icon.mapillary-sequence-icon-visible", []); const direction = configuration.direction === exports.NavigationDirection.Next ? exports.NavigationDirection.Prev : exports.NavigationDirection.Next; const playing = configuration.playing; const switchButtonProperties = { onclick: () => { if (!playing) { component.configure({ direction }); } }, }; const switchButtonClassName = configuration.playing ? ".mapillary-sequence-switch-button-inactive" : ".mapillary-sequence-switch-button"; const switchButton = virtualDom.h("div" + switchButtonClassName, switchButtonProperties, [switchIcon]); const slowIcon = virtualDom.h("div.mapillary-sequence-slow-icon.mapillary-sequence-icon-visible", []); const slowContainer = virtualDom.h("div.mapillary-sequence-slow-container", [slowIcon]); const fastIcon = virtualDom.h("div.mapillary-sequence-fast-icon.mapillary-sequence-icon-visible", []); const fastContainer = virtualDom.h("div.mapillary-sequence-fast-container", [fastIcon]); const closeIcon = virtualDom.h("div.mapillary-sequence-close-icon.mapillary-sequence-icon-visible", []); const closeButtonProperties = { onclick: () => { this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, }; const closeButton = virtualDom.h("div.mapillary-sequence-close-button", closeButtonProperties, [closeIcon]); const speedInput = this._createSpeedInput(speed); const playbackChildren = [switchButton, slowContainer, speedInput, fastContainer, closeButton]; const top = Math.round(containerWidth / this._stepperDefaultWidth * this._defaultHeight + 10); const playbackProperties = { style: { top: `${top}px` } }; return virtualDom.h("div.mapillary-sequence-playback", playbackProperties, playbackChildren); } _createPlayingButton(nextId, prevId, playEnabled, configuration, component) { let canPlay = (configuration.direction === exports.NavigationDirection.Next && nextId != null) || (configuration.direction === exports.NavigationDirection.Prev && prevId != null); canPlay = canPlay && playEnabled; let onclick = configuration.playing ? () => { component.stop(); } : canPlay ? () => { component.play(); } : null; let buttonProperties = { onclick: onclick }; let iconProperties = {}; if (configuration.direction === exports.NavigationDirection.Prev) { iconProperties.style = { transform: "rotate(180deg) translate(50%, 50%)", }; } let icon = virtualDom.h("div.mapillary-sequence-icon", iconProperties, []); let buttonClass = configuration.playing ? "mapillary-sequence-stop" : canPlay ? "mapillary-sequence-play" : "mapillary-sequence-play-inactive"; return virtualDom.h("div." + buttonClass, buttonProperties, [icon]); } _createSequenceControls(containerWidth) { const borderRadius = Math.round(8 / this._stepperDefaultWidth * containerWidth); const expanderProperties = { onclick: () => { this._expandControls = !this._expandControls; this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, style: { "border-bottom-right-radius": `${borderRadius}px`, "border-top-right-radius": `${borderRadius}px`, }, }; const expanderBar = virtualDom.h("div.mapillary-sequence-expander-bar", []); const expander = virtualDom.h("div.mapillary-sequence-expander-button", expanderProperties, [expanderBar]); const fastIconClassName = this._mode === SequenceMode.Playback ? ".mapillary-sequence-fast-icon-gray.mapillary-sequence-icon-visible" : ".mapillary-sequence-fast-icon"; const fastIcon = virtualDom.h("div" + fastIconClassName, []); const playbackProperties = { onclick: () => { this._mode = this._mode === SequenceMode.Playback ? SequenceMode.Default : SequenceMode.Playback; this._notifyChanged$.next(this); }, }; const playback = virtualDom.h("div.mapillary-sequence-playback-button", playbackProperties, [fastIcon]); const timelineIconClassName = this._mode === SequenceMode.Timeline ? ".mapillary-sequence-timeline-icon-gray.mapillary-sequence-icon-visible" : ".mapillary-sequence-timeline-icon"; const timelineIcon = virtualDom.h("div" + timelineIconClassName, []); const timelineProperties = { onclick: () => { this._mode = this._mode === SequenceMode.Timeline ? SequenceMode.Default : SequenceMode.Timeline; this._notifyChanged$.next(this); }, }; const timeline = virtualDom.h("div.mapillary-sequence-timeline-button", timelineProperties, [timelineIcon]); const properties = { style: { height: (this._defaultHeight / this._stepperDefaultWidth * containerWidth) + "px", transform: `translate(${containerWidth / 2 + 2}px, 0)`, width: (this._controlsDefaultWidth / this._stepperDefaultWidth * containerWidth) + "px", }, }; const className = ".mapillary-sequence-controls" + (this._expandControls ? ".mapillary-sequence-controls-expanded" : ""); return virtualDom.h("div" + className, properties, [playback, timeline, expander]); } _createSequenceArrows(nextId, prevId, containerWidth, configuration, navigator) { let nextProperties = { onclick: nextId != null ? () => { navigator.moveDir$(exports.NavigationDirection.Next) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } : null, onpointerenter: () => { this._mouseEnterDirection$.next(exports.NavigationDirection.Next); }, onpointerleave: () => { this._mouseLeaveDirection$.next(exports.NavigationDirection.Next); }, }; const borderRadius = Math.round(8 / this._stepperDefaultWidth * containerWidth); let prevProperties = { onclick: prevId != null ? () => { navigator.moveDir$(exports.NavigationDirection.Prev) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); } : null, onpointerenter: () => { this._mouseEnterDirection$.next(exports.NavigationDirection.Prev); }, onpointerleave: () => { this._mouseLeaveDirection$.next(exports.NavigationDirection.Prev); }, style: { "border-bottom-left-radius": `${borderRadius}px`, "border-top-left-radius": `${borderRadius}px`, }, }; let nextClass = this._getStepClassName(exports.NavigationDirection.Next, nextId, configuration.highlightId); let prevClass = this._getStepClassName(exports.NavigationDirection.Prev, prevId, configuration.highlightId); let nextIcon = virtualDom.h("div.mapillary-sequence-icon", []); let prevIcon = virtualDom.h("div.mapillary-sequence-icon", []); return [ virtualDom.h("div." + prevClass, prevProperties, [prevIcon]), virtualDom.h("div." + nextClass, nextProperties, [nextIcon]), ]; } _createStepper(edgeStatus, configuration, playEnabled, containerWidth, component, navigator) { let nextId = null; let prevId = null; for (let edge of edgeStatus.edges) { if (edge.data.direction === exports.NavigationDirection.Next) { nextId = edge.target; } if (edge.data.direction === exports.NavigationDirection.Prev) { prevId = edge.target; } } const playingButton = this._createPlayingButton(nextId, prevId, playEnabled, configuration, component); const buttons = this._createSequenceArrows(nextId, prevId, containerWidth, configuration, navigator); buttons.splice(1, 0, playingButton); const containerProperties = { oncontextmenu: (event) => { event.preventDefault(); }, style: { height: (this._defaultHeight / this._stepperDefaultWidth * containerWidth) + "px", width: containerWidth + "px", }, }; return virtualDom.h("div.mapillary-sequence-stepper", containerProperties, buttons); } _createTimelineControls(containerWidth, index, max) { if (this._mode !== SequenceMode.Timeline) { return virtualDom.h("div.mapillary-sequence-timeline", []); } const positionInput = this._createPositionInput(index, max); const closeIcon = virtualDom.h("div.mapillary-sequence-close-icon.mapillary-sequence-icon-visible", []); const closeButtonProperties = { onclick: () => { this._mode = SequenceMode.Default; this._notifyChanged$.next(this); }, }; const closeButton = virtualDom.h("div.mapillary-sequence-close-button", closeButtonProperties, [closeIcon]); const top = Math.round(containerWidth / this._stepperDefaultWidth * this._defaultHeight + 10); const playbackProperties = { style: { top: `${top}px` } }; return virtualDom.h("div.mapillary-sequence-timeline", playbackProperties, [positionInput, closeButton]); } _getStepClassName(direction, imageId, highlightId) { let className = direction === exports.NavigationDirection.Next ? "mapillary-sequence-step-next" : "mapillary-sequence-step-prev"; if (imageId == null) { className += "-inactive"; } else { if (highlightId === imageId) { className += "-highlight"; } } return className; } _setChangingPosition(value) { this._changingPosition = value; this._notifyChangingPositionChanged$.next(value); } } /** * @class DataProviderBase * * @classdesc Base class to extend if implementing a data provider * class. * * @fires datacreate * * @example * ```js * class MyDataProvider extends DataProviderBase { * constructor() { * super(new S2GeometryProvider()); * } * ... * } * ``` */ class DataProviderBase extends EventEmitter { /** * Create a new data provider base instance. * * @param {IGeometryProvider} geometry - Geometry * provider instance. */ constructor(_geometry) { super(); this._geometry = _geometry; } /** * Get geometry property. * * @returns {IGeometryProvider} Geometry provider instance. */ get geometry() { return this._geometry; } fire(type, event) { super.fire(type, event); } /** * Get core images in a geometry cell. * * @param {string} cellId - The id of the geometry cell. * @returns {Promise} Promise to * the core images of the requested geometry cell id. * @throws Rejects the promise on errors. */ getCoreImages(cellId) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get a cluster reconstruction. * * @param {string} url - URL for the cluster reconstruction * to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the * cluster reconstruction. * @throws Rejects the promise on errors. */ getCluster(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get spatial images. * * @param {Array} imageIds - The ids for the * images to retrieve. * @returns {Promise} Promise to * the spatial images of the requested image ids. * @throws Rejects the promise on errors. */ getSpatialImages(imageIds) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get complete images. * * @param {Array} imageIds - The ids for the * images to retrieve. * @returns {Promise} Promise to the images of the * requested image ids. * @throws Rejects the promise on errors. */ getImages(imageIds) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get an image as an array buffer. * * @param {string} url - URL for image to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the array * buffer containing the image. * @throws Rejects the promise on errors. */ getImageBuffer(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get image tiles urls for a tile level. * * @param {ImageTilesRequestContract} tiles - Tiles to request * @returns {Promise} Promise to the * image tiles response contract * * @throws Rejects the promise on errors. * * @example * ```js * var tileRequest = { imageId: "image-id", z: 12 }; * provider.getImageTiles(tileRequest) * .then((response) => console.log(response)); * ``` */ getImageTiles(tiles) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get a mesh. * * @param {string} url - URL for mesh to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the mesh. * @throws Rejects the promise on errors. */ getMesh(url, abort) { return Promise.reject(new MapillaryError("Not implemented")); } /** * Get sequence. * * @param {Array} sequenceId - The id for the * sequence to retrieve. * @returns {Promise} Promise to the sequences of the * requested image ids. * @throws Rejects the promise on errors. */ getSequence(sequenceId) { return Promise.reject(new MapillaryError("Not implemented")); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Set an access token for authenticated API requests of * protected resources. * * @param {string} [accessToken] accessToken - User access * token or client access token. */ setAccessToken(accessToken) { throw new MapillaryError("Not implemented"); } } var s2geometry = {exports: {}}; function commonjsRequire(path) { throw new Error("Could not dynamically require "" + path + "". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work."); } var long = {exports: {}}; /* Copyright 2013 Daniel Wirtz Copyright 2009 The Closure Library Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ var hasRequiredLong; function requireLong () { if (hasRequiredLong) return long.exports; hasRequiredLong = 1; (function (module) { /** * @license long.js (c) 2013 Daniel Wirtz * Released under the Apache License, Version 2.0 * see: https://github.com/dcodeIO/long.js for details */ (function(global, factory) { /* AMD */ if (typeof commonjsRequire === "function" && "object" === "object" && module && module["exports"]) module["exports"] = factory(); /* Global */ else (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = factory(); })(commonjsGlobal, function() { /** * Constructs a 64 bit two"s-complement integer, given its low and high 32 bit values as *signed* integers. * See the from* functions below for more convenient ways of constructing Longs. * @exports Long * @class A Long class for representing a 64 bit two"s-complement integer value. * @param {number} low The low (signed) 32 bits of the long * @param {number} high The high (signed) 32 bits of the long * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @constructor */ function Long(low, high, unsigned) { /** * The low 32 bits as a signed value. * @type {number} */ this.low = low | 0; /** * The high 32 bits as a signed value. * @type {number} */ this.high = high | 0; /** * Whether unsigned or not. * @type {boolean} */ this.unsigned = !!unsigned; } // The internal representation of a long is the two given signed, 32-bit values. // We use 32-bit pieces because these are the size of integers on which // Javascript performs bit-operations. For operations like addition and // multiplication, we split each number into 16 bit pieces, which can easily be // multiplied within Javascript"s floating-point representation without overflow // or change in sign. // // In the algorithms below, we frequently reduce the negative case to the // positive case by negating the input(s) and then post-processing the result. // Note that we must ALWAYS check specially whether those values are MIN_VALUE // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as // a positive number, it overflows back into a negative). Not handling this // case would often result in infinite recursion. // // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* // methods on which they depend. /** * An indicator used to reliably determine if an object is a Long or not. * @type {boolean} * @const * @private */ Long.prototype.__isLong__; Object.defineProperty(Long.prototype, "__isLong__", { value: true, enumerable: false, configurable: false }); /** * @function * @param {*} obj Object * @returns {boolean} * @inner */ function isLong(obj) { return (obj && obj["__isLong__"]) === true; } /** * Tests if the specified object is a Long. * @function * @param {*} obj Object * @returns {boolean} */ Long.isLong = isLong; /** * A cache of the Long representations of small integer values. * @type {!Object} * @inner */ var INT_CACHE = {}; /** * A cache of the Long representations of small unsigned integer values. * @type {!Object} * @inner */ var UINT_CACHE = {}; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromInt(value, unsigned) { var obj, cachedObj, cache; if (unsigned) { value >>>= 0; if (cache = (0 <= value && value < 256)) { cachedObj = UINT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); if (cache) UINT_CACHE[value] = obj; return obj; } else { value |= 0; if (cache = (-128 <= value && value < 128)) { cachedObj = INT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, value < 0 ? -1 : 0, false); if (cache) INT_CACHE[value] = obj; return obj; } } /** * Returns a Long representing the given 32 bit integer value. * @function * @param {number} value The 32 bit integer in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromInt = fromInt; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromNumber(value, unsigned) { if (isNaN(value) || !isFinite(value)) return unsigned ? UZERO : ZERO; if (unsigned) { if (value < 0) return UZERO; if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE; } else { if (value <= -TWO_PWR_63_DBL) return MIN_VALUE; if (value + 1 >= TWO_PWR_63_DBL) return MAX_VALUE; } if (value < 0) return fromNumber(-value, unsigned).neg(); return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); } /** * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. * @function * @param {number} value The number in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromNumber = fromNumber; /** * @param {number} lowBits * @param {number} highBits * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromBits(lowBits, highBits, unsigned) { return new Long(lowBits, highBits, unsigned); } /** * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is * assumed to use 32 bits. * @function * @param {number} lowBits The low 32 bits * @param {number} highBits The high 32 bits * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value */ Long.fromBits = fromBits; /** * @function * @param {number} base * @param {number} exponent * @returns {number} * @inner */ var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4) /** * @param {string} str * @param {(boolean|number)=} unsigned * @param {number=} radix * @returns {!Long} * @inner */ function fromString(str, unsigned, radix) { if (str.length === 0) throw Error("empty string"); if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") return ZERO; if (typeof unsigned === "number") { // For goog.math.long compatibility radix = unsigned, unsigned = false; } else { unsigned = !! unsigned; } radix = radix || 10; if (radix < 2 || 36 < radix) throw RangeError("radix"); var p; if ((p = str.indexOf("-")) > 0) throw Error("interior hyphen"); else if (p === 0) { return fromString(str.substring(1), unsigned, radix).neg(); } // Do several (8) digits each time through the loop, so as to // minimize the calls to the very expensive emulated div. var radixToPower = fromNumber(pow_dbl(radix, 8)); var result = ZERO; for (var i = 0; i < str.length; i += 8) { var size = Math.min(8, str.length - i), value = parseInt(str.substring(i, i + size), radix); if (size < 8) { var power = fromNumber(pow_dbl(radix, size)); result = result.mul(power).add(fromNumber(value)); } else { result = result.mul(radixToPower); result = result.add(fromNumber(value)); } } result.unsigned = unsigned; return result; } /** * Returns a Long representation of the given string, written using the specified radix. * @function * @param {string} str The textual representation of the Long * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 * @returns {!Long} The corresponding Long value */ Long.fromString = fromString; /** * @function * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val * @returns {!Long} * @inner */ function fromValue(val) { if (val /* is compatible */ instanceof Long) return val; if (typeof val === "number") return fromNumber(val); if (typeof val === "string") return fromString(val); // Throws for non-objects, converts non-instanceof Long: return fromBits(val.low, val.high, val.unsigned); } /** * Converts the specified value to a Long. * @function * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value * @returns {!Long} */ Long.fromValue = fromValue; // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be // no runtime penalty for these. /** * @type {number} * @const * @inner */ var TWO_PWR_16_DBL = 1 << 16; /** * @type {number} * @const * @inner */ var TWO_PWR_24_DBL = 1 << 24; /** * @type {number} * @const * @inner */ var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; /** * @type {number} * @const * @inner */ var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; /** * @type {number} * @const * @inner */ var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; /** * @type {!Long} * @const * @inner */ var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL); /** * @type {!Long} * @inner */ var ZERO = fromInt(0); /** * Signed zero. * @type {!Long} */ Long.ZERO = ZERO; /** * @type {!Long} * @inner */ var UZERO = fromInt(0, true); /** * Unsigned zero. * @type {!Long} */ Long.UZERO = UZERO; /** * @type {!Long} * @inner */ var ONE = fromInt(1); /** * Signed one. * @type {!Long} */ Long.ONE = ONE; /** * @type {!Long} * @inner */ var UONE = fromInt(1, true); /** * Unsigned one. * @type {!Long} */ Long.UONE = UONE; /** * @type {!Long} * @inner */ var NEG_ONE = fromInt(-1); /** * Signed negative one. * @type {!Long} */ Long.NEG_ONE = NEG_ONE; /** * @type {!Long} * @inner */ var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); /** * Maximum signed value. * @type {!Long} */ Long.MAX_VALUE = MAX_VALUE; /** * @type {!Long} * @inner */ var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); /** * Maximum unsigned value. * @type {!Long} */ Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE; /** * @type {!Long} * @inner */ var MIN_VALUE = fromBits(0, 0x80000000|0, false); /** * Minimum signed value. * @type {!Long} */ Long.MIN_VALUE = MIN_VALUE; /** * @alias Long.prototype * @inner */ var LongPrototype = Long.prototype; /** * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. * @returns {number} */ LongPrototype.toInt = function toInt() { return this.unsigned ? this.low >>> 0 : this.low; }; /** * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). * @returns {number} */ LongPrototype.toNumber = function toNumber() { if (this.unsigned) return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); return this.high * TWO_PWR_32_DBL + (this.low >>> 0); }; /** * Converts the Long to a string written in the specified radix. * @param {number=} radix Radix (2-36), defaults to 10 * @returns {string} * @override * @throws {RangeError} If `radix` is out of range */ LongPrototype.toString = function toString(radix) { radix = radix || 10; if (radix < 2 || 36 < radix) throw RangeError("radix"); if (this.isZero()) return "0"; if (this.isNegative()) { // Unsigned Longs are never negative if (this.eq(MIN_VALUE)) { // We need to change the Long value before it can be negated, so we remove // the bottom-most digit in this base and then recurse to do the rest. var radixLong = fromNumber(radix), div = this.div(radixLong), rem1 = div.mul(radixLong).sub(this); return div.toString(radix) + rem1.toInt().toString(radix); } else return "-" + this.neg().toString(radix); } // Do several (6) digits each time through the loop, so as to // minimize the calls to the very expensive emulated div. var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned), rem = this; var result = ""; while (true) { var remDiv = rem.div(radixToPower), intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0, digits = intval.toString(radix); rem = remDiv; if (rem.isZero()) return digits + result; else { while (digits.length < 6) digits = "0" + digits; result = "" + digits + result; } } }; /** * Gets the high 32 bits as a signed integer. * @returns {number} Signed high bits */ LongPrototype.getHighBits = function getHighBits() { return this.high; }; /** * Gets the high 32 bits as an unsigned integer. * @returns {number} Unsigned high bits */ LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() { return this.high >>> 0; }; /** * Gets the low 32 bits as a signed integer. * @returns {number} Signed low bits */ LongPrototype.getLowBits = function getLowBits() { return this.low; }; /** * Gets the low 32 bits as an unsigned integer. * @returns {number} Unsigned low bits */ LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() { return this.low >>> 0; }; /** * Gets the number of bits needed to represent the absolute value of this Long. * @returns {number} */ LongPrototype.getNumBitsAbs = function getNumBitsAbs() { if (this.isNegative()) // Unsigned Longs are never negative return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs(); var val = this.high != 0 ? this.high : this.low; for (var bit = 31; bit > 0; bit--) if ((val & (1 << bit)) != 0) break; return this.high != 0 ? bit + 33 : bit + 1; }; /** * Tests if this Long"s value equals zero. * @returns {boolean} */ LongPrototype.isZero = function isZero() { return this.high === 0 && this.low === 0; }; /** * Tests if this Long"s value is negative. * @returns {boolean} */ LongPrototype.isNegative = function isNegative() { return !this.unsigned && this.high < 0; }; /** * Tests if this Long"s value is positive. * @returns {boolean} */ LongPrototype.isPositive = function isPositive() { return this.unsigned || this.high >= 0; }; /** * Tests if this Long"s value is odd. * @returns {boolean} */ LongPrototype.isOdd = function isOdd() { return (this.low & 1) === 1; }; /** * Tests if this Long"s value is even. * @returns {boolean} */ LongPrototype.isEven = function isEven() { return (this.low & 1) === 0; }; /** * Tests if this Long"s value equals the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.equals = function equals(other) { if (!isLong(other)) other = fromValue(other); if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) return false; return this.high === other.high && this.low === other.low; }; /** * Tests if this Long"s value equals the specified"s. This is an alias of {@link Long#equals}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.eq = LongPrototype.equals; /** * Tests if this Long"s value differs from the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.notEquals = function notEquals(other) { return !this.eq(/* validates */ other); }; /** * Tests if this Long"s value differs from the specified"s. This is an alias of {@link Long#notEquals}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.neq = LongPrototype.notEquals; /** * Tests if this Long"s value is less than the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lessThan = function lessThan(other) { return this.comp(/* validates */ other) < 0; }; /** * Tests if this Long"s value is less than the specified"s. This is an alias of {@link Long#lessThan}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lt = LongPrototype.lessThan; /** * Tests if this Long"s value is less than or equal the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) { return this.comp(/* validates */ other) <= 0; }; /** * Tests if this Long"s value is less than or equal the specified"s. This is an alias of {@link Long#lessThanOrEqual}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.lte = LongPrototype.lessThanOrEqual; /** * Tests if this Long"s value is greater than the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.greaterThan = function greaterThan(other) { return this.comp(/* validates */ other) > 0; }; /** * Tests if this Long"s value is greater than the specified"s. This is an alias of {@link Long#greaterThan}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.gt = LongPrototype.greaterThan; /** * Tests if this Long"s value is greater than or equal the specified"s. * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) { return this.comp(/* validates */ other) >= 0; }; /** * Tests if this Long"s value is greater than or equal the specified"s. This is an alias of {@link Long#greaterThanOrEqual}. * @function * @param {!Long|number|string} other Other value * @returns {boolean} */ LongPrototype.gte = LongPrototype.greaterThanOrEqual; /** * Compares this Long"s value with the specified"s. * @param {!Long|number|string} other Other value * @returns {number} 0 if they are the same, 1 if the this is greater and -1 * if the given one is greater */ LongPrototype.compare = function compare(other) { if (!isLong(other)) other = fromValue(other); if (this.eq(other)) return 0; var thisNeg = this.isNegative(), otherNeg = other.isNegative(); if (thisNeg && !otherNeg) return -1; if (!thisNeg && otherNeg) return 1; // At this point the sign bits are the same if (!this.unsigned) return this.sub(other).isNegative() ? -1 : 1; // Both are positive if at least one is unsigned return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; }; /** * Compares this Long"s value with the specified"s. This is an alias of {@link Long#compare}. * @function * @param {!Long|number|string} other Other value * @returns {number} 0 if they are the same, 1 if the this is greater and -1 * if the given one is greater */ LongPrototype.comp = LongPrototype.compare; /** * Negates this Long"s value. * @returns {!Long} Negated Long */ LongPrototype.negate = function negate() { if (!this.unsigned && this.eq(MIN_VALUE)) return MIN_VALUE; return this.not().add(ONE); }; /** * Negates this Long"s value. This is an alias of {@link Long#negate}. * @function * @returns {!Long} Negated Long */ LongPrototype.neg = LongPrototype.negate; /** * Returns the sum of this and the specified Long. * @param {!Long|number|string} addend Addend * @returns {!Long} Sum */ LongPrototype.add = function add(addend) { if (!isLong(addend)) addend = fromValue(addend); // Divide each number into 4 chunks of 16 bits, and then sum the chunks. var a48 = this.high >>> 16; var a32 = this.high & 0xFFFF; var a16 = this.low >>> 16; var a00 = this.low & 0xFFFF; var b48 = addend.high >>> 16; var b32 = addend.high & 0xFFFF; var b16 = addend.low >>> 16; var b00 = addend.low & 0xFFFF; var c48 = 0, c32 = 0, c16 = 0, c00 = 0; c00 += a00 + b00; c16 += c00 >>> 16; c00 &= 0xFFFF; c16 += a16 + b16; c32 += c16 >>> 16; c16 &= 0xFFFF; c32 += a32 + b32; c48 += c32 >>> 16; c32 &= 0xFFFF; c48 += a48 + b48; c48 &= 0xFFFF; return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); }; /** * Returns the difference of this and the specified Long. * @param {!Long|number|string} subtrahend Subtrahend * @returns {!Long} Difference */ LongPrototype.subtract = function subtract(subtrahend) { if (!isLong(subtrahend)) subtrahend = fromValue(subtrahend); return this.add(subtrahend.neg()); }; /** * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}. * @function * @param {!Long|number|string} subtrahend Subtrahend * @returns {!Long} Difference */ LongPrototype.sub = LongPrototype.subtract; /** * Returns the product of this and the specified Long. * @param {!Long|number|string} multiplier Multiplier * @returns {!Long} Product */ LongPrototype.multiply = function multiply(multiplier) { if (this.isZero()) return ZERO; if (!isLong(multiplier)) multiplier = fromValue(multiplier); if (multiplier.isZero()) return ZERO; if (this.eq(MIN_VALUE)) return multiplier.isOdd() ? MIN_VALUE : ZERO; if (multiplier.eq(MIN_VALUE)) return this.isOdd() ? MIN_VALUE : ZERO; if (this.isNegative()) { if (multiplier.isNegative()) return this.neg().mul(multiplier.neg()); else return this.neg().mul(multiplier).neg(); } else if (multiplier.isNegative()) return this.mul(multiplier.neg()).neg(); // If both longs are small, use float multiplication if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24)) return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. // We can skip products that would overflow. var a48 = this.high >>> 16; var a32 = this.high & 0xFFFF; var a16 = this.low >>> 16; var a00 = this.low & 0xFFFF; var b48 = multiplier.high >>> 16; var b32 = multiplier.high & 0xFFFF; var b16 = multiplier.low >>> 16; var b00 = multiplier.low & 0xFFFF; var c48 = 0, c32 = 0, c16 = 0, c00 = 0; c00 += a00 * b00; c16 += c00 >>> 16; c00 &= 0xFFFF; c16 += a16 * b00; c32 += c16 >>> 16; c16 &= 0xFFFF; c16 += a00 * b16; c32 += c16 >>> 16; c16 &= 0xFFFF; c32 += a32 * b00; c48 += c32 >>> 16; c32 &= 0xFFFF; c32 += a16 * b16; c48 += c32 >>> 16; c32 &= 0xFFFF; c32 += a00 * b32; c48 += c32 >>> 16; c32 &= 0xFFFF; c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; c48 &= 0xFFFF; return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); }; /** * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}. * @function * @param {!Long|number|string} multiplier Multiplier * @returns {!Long} Product */ LongPrototype.mul = LongPrototype.multiply; /** * Returns this Long divided by the specified. The result is signed if this Long is signed or * unsigned if this Long is unsigned. * @param {!Long|number|string} divisor Divisor * @returns {!Long} Quotient */ LongPrototype.divide = function divide(divisor) { if (!isLong(divisor)) divisor = fromValue(divisor); if (divisor.isZero()) throw Error("division by zero"); if (this.isZero()) return this.unsigned ? UZERO : ZERO; var approx, rem, res; if (!this.unsigned) { // This section is only relevant for signed longs and is derived from the // closure library as a whole. if (this.eq(MIN_VALUE)) { if (divisor.eq(ONE) || divisor.eq(NEG_ONE)) return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE else if (divisor.eq(MIN_VALUE)) return ONE; else { // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. var halfThis = this.shr(1); approx = halfThis.div(divisor).shl(1); if (approx.eq(ZERO)) { return divisor.isNegative() ? ONE : NEG_ONE; } else { rem = this.sub(divisor.mul(approx)); res = approx.add(rem.div(divisor)); return res; } } } else if (divisor.eq(MIN_VALUE)) return this.unsigned ? UZERO : ZERO; if (this.isNegative()) { if (divisor.isNegative()) return this.neg().div(divisor.neg()); return this.neg().div(divisor).neg(); } else if (divisor.isNegative()) return this.div(divisor.neg()).neg(); res = ZERO; } else { // The algorithm below has not been made for unsigned longs. It"s therefore // required to take special care of the MSB prior to running it. if (!divisor.unsigned) divisor = divisor.toUnsigned(); if (divisor.gt(this)) return UZERO; if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true return UONE; res = UZERO; } // Repeat the following until the remainder is less than other: find a // floating-point that approximates remainder / other *from below*, add this // into the result, and subtract it from the remainder. It is critical that // the approximate value is less than or equal to the real value so that the // remainder never becomes negative. rem = this; while (rem.gte(divisor)) { // Approximate the result of division. This may be a little greater or // smaller than the actual value. approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); // We will tweak the approximate result by changing it in the 48-th digit or // the smallest non-fractional digit, whichever is larger. var log2 = Math.ceil(Math.log(approx) / Math.LN2), delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48), // Decrease the approximation until it is smaller than the remainder. Note // that if it is too large, the product overflows and is negative. approxRes = fromNumber(approx), approxRem = approxRes.mul(divisor); while (approxRem.isNegative() || approxRem.gt(rem)) { approx -= delta; approxRes = fromNumber(approx, this.unsigned); approxRem = approxRes.mul(divisor); } // We know the answer can"t be zero... and actually, zero would cause // infinite recursion since we would make no progress. if (approxRes.isZero()) approxRes = ONE; res = res.add(approxRes); rem = rem.sub(approxRem); } return res; }; /** * Returns this Long divided by the specified. This is an alias of {@link Long#divide}. * @function * @param {!Long|number|string} divisor Divisor * @returns {!Long} Quotient */ LongPrototype.div = LongPrototype.divide; /** * Returns this Long modulo the specified. * @param {!Long|number|string} divisor Divisor * @returns {!Long} Remainder */ LongPrototype.modulo = function modulo(divisor) { if (!isLong(divisor)) divisor = fromValue(divisor); return this.sub(this.div(divisor).mul(divisor)); }; /** * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. * @function * @param {!Long|number|string} divisor Divisor * @returns {!Long} Remainder */ LongPrototype.mod = LongPrototype.modulo; /** * Returns the bitwise NOT of this Long. * @returns {!Long} */ LongPrototype.not = function not() { return fromBits(~this.low, ~this.high, this.unsigned); }; /** * Returns the bitwise AND of this Long and the specified. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.and = function and(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low & other.low, this.high & other.high, this.unsigned); }; /** * Returns the bitwise OR of this Long and the specified. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.or = function or(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low | other.low, this.high | other.high, this.unsigned); }; /** * Returns the bitwise XOR of this Long and the given one. * @param {!Long|number|string} other Other Long * @returns {!Long} */ LongPrototype.xor = function xor(other) { if (!isLong(other)) other = fromValue(other); return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); }; /** * Returns this Long with bits shifted to the left by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftLeft = function shiftLeft(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); if ((numBits &= 63) === 0) return this; else if (numBits < 32) return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); else return fromBits(0, this.low << (numBits - 32), this.unsigned); }; /** * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shl = LongPrototype.shiftLeft; /** * Returns this Long with bits arithmetically shifted to the right by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftRight = function shiftRight(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); if ((numBits &= 63) === 0) return this; else if (numBits < 32) return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); else return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); }; /** * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shr = LongPrototype.shiftRight; /** * Returns this Long with bits logically shifted to the right by the given amount. * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) { if (isLong(numBits)) numBits = numBits.toInt(); numBits &= 63; if (numBits === 0) return this; else { var high = this.high; if (numBits < 32) { var low = this.low; return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); } else if (numBits === 32) return fromBits(high, 0, this.unsigned); else return fromBits(high >>> (numBits - 32), 0, this.unsigned); } }; /** * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. * @function * @param {number|!Long} numBits Number of bits * @returns {!Long} Shifted Long */ LongPrototype.shru = LongPrototype.shiftRightUnsigned; /** * Converts this Long to signed. * @returns {!Long} Signed long */ LongPrototype.toSigned = function toSigned() { if (!this.unsigned) return this; return fromBits(this.low, this.high, false); }; /** * Converts this Long to unsigned. * @returns {!Long} Unsigned long */ LongPrototype.toUnsigned = function toUnsigned() { if (this.unsigned) return this; return fromBits(this.low, this.high, true); }; /** * Converts this Long to its byte representation. * @param {boolean=} le Whether little or big endian, defaults to big endian * @returns {!Array.} Byte representation */ LongPrototype.toBytes = function(le) { return le ? this.toBytesLE() : this.toBytesBE(); }; /** * Converts this Long to its little endian byte representation. * @returns {!Array.} Little endian byte representation */ LongPrototype.toBytesLE = function() { var hi = this.high, lo = this.low; return [ lo & 0xff, (lo >>> 8) & 0xff, (lo >>> 16) & 0xff, (lo >>> 24) & 0xff, hi & 0xff, (hi >>> 8) & 0xff, (hi >>> 16) & 0xff, (hi >>> 24) & 0xff ]; }; /** * Converts this Long to its big endian byte representation. * @returns {!Array.} Big endian byte representation */ LongPrototype.toBytesBE = function() { var hi = this.high, lo = this.low; return [ (hi >>> 24) & 0xff, (hi >>> 16) & 0xff, (hi >>> 8) & 0xff, hi & 0xff, (lo >>> 24) & 0xff, (lo >>> 16) & 0xff, (lo >>> 8) & 0xff, lo & 0xff ]; }; return Long; }); } (long)); return long.exports; } (function (module) { /// S2 Geometry functions // the regional scoreboard is based on a level 6 S2 Cell // - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22 // at the time of writing there"s no actual API for the intel map to retrieve scoreboard data, // but it"s still useful to plot the score cells on the intel map // the S2 geometry is based on projecting the earth sphere onto a cube, with some scaling of face coordinates to // keep things close to approximate equal area for adjacent cells // to convert a lat,lng into a cell id: // - convert lat,lng to x,y,z // - convert x,y,z into face,u,v // - u,v scaled to s,t with quadratic formula // - s,t converted to integer i,j offsets // - i,j converted to a position along a Hubbert space-filling curve // - combine face,position to get the cell id //NOTE: compared to the google S2 geometry library, we vary from their code in the following ways // - cell IDs: they combine face and the hilbert curve position into a single 64 bit number. this gives efficient space // and speed. javascript doesn"t have appropriate data types, and speed is not cricical, so we use // as [face,[bitpair,bitpair,...]] instead // - i,j: they always use 30 bits, adjusting as needed. we use 0 to (1< temp[1]) { if (temp[0] > temp[2]) { return 0; } else { return 2; } } else { if (temp[1] > temp[2]) { return 1; } else { return 2; } } }; var faceXYZToUV = function(face,xyz) { var u,v; switch (face) { case 0: u = xyz[1]/xyz[0]; v = xyz[2]/xyz[0]; break; case 1: u = -xyz[0]/xyz[1]; v = xyz[2]/xyz[1]; break; case 2: u = -xyz[0]/xyz[2]; v = -xyz[1]/xyz[2]; break; case 3: u = xyz[2]/xyz[0]; v = xyz[1]/xyz[0]; break; case 4: u = xyz[2]/xyz[1]; v = -xyz[0]/xyz[1]; break; case 5: u = -xyz[1]/xyz[2]; v = -xyz[0]/xyz[2]; break; default: throw {error: "Invalid face"}; } return [u,v]; }; S2.XYZToFaceUV = function(xyz) { var face = largestAbsComponent(xyz); if (xyz[face] < 0) { face += 3; } var uv = faceXYZToUV (face,xyz); return [face, uv]; }; S2.FaceUVToXYZ = function(face,uv) { var u = uv[0]; var v = uv[1]; switch (face) { case 0: return [ 1, u, v]; case 1: return [-u, 1, v]; case 2: return [-u,-v, 1]; case 3: return [-1,-v,-u]; case 4: return [ v,-1,-u]; case 5: return [ v, u,-1]; default: throw {error: "Invalid face"}; } }; var singleSTtoUV = function(st) { if (st >= 0.5) { return (1/3.0) * (4*st*st - 1); } else { return (1/3.0) * (1 - (4*(1-st)*(1-st))); } }; S2.STToUV = function(st) { return [singleSTtoUV(st[0]), singleSTtoUV(st[1])]; }; var singleUVtoST = function(uv) { if (uv >= 0) { return 0.5 * Math.sqrt (1 + 3*uv); } else { return 1 - 0.5 * Math.sqrt (1 - 3*uv); } }; S2.UVToST = function(uv) { return [singleUVtoST(uv[0]), singleUVtoST(uv[1])]; }; S2.STToIJ = function(st,order) { var maxSize = (1<=0; i--) { var mask = 1<= 0; i--) { level = maxLevel - i; bit = position[i]; rx = 0; ry = 0; if (bit === "1") { ry = 1; } else if (bit === "2") { rx = 1; ry = 1; } else if (bit === "3") { rx = 1; } val = Math.pow(2, level - 1); rotateAndFlipQuadrant(val, point, rx, ry); point.x += val * rx; point.y += val * ry; } if (face % 2 === 1) { var t = point.x; point.x = point.y; point.y = t; } return S2.S2Cell.FromFaceIJ(parseInt(face), [point.x, point.y], level); }; //static method to construct S2.S2Cell.FromLatLng = function(latLng, level) { if ((!latLng.lat && latLng.lat !== 0) || (!latLng.lng && latLng.lng !== 0)) { throw new Error("Pass { lat: lat, lng: lng } to S2.S2Cell.FromLatLng"); } var xyz = S2.LatLngToXYZ(latLng); var faceuv = S2.XYZToFaceUV(xyz); var st = S2.UVToST(faceuv[1]); var ij = S2.STToIJ(st,level); return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level); }; /* S2.faceIjLevelToXyz = function (face, ij, level) { var st = S2.IJToST(ij, level, [0.5, 0.5]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(face, uv); return S2.XYZToLatLng(xyz); return xyz; }; */ S2.S2Cell.FromFaceIJ = function(face,ij,level) { var cell = new S2.S2Cell(); cell.face = face; cell.ij = ij; cell.level = level; return cell; }; S2.S2Cell.prototype.toString = function() { return "F"+this.face+"ij["+this.ij[0]+","+this.ij[1]+"]@"+this.level; }; S2.S2Cell.prototype.getLatLng = function() { var st = S2.IJToST(this.ij,this.level, [0.5,0.5]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(this.face, uv); return S2.XYZToLatLng(xyz); }; S2.S2Cell.prototype.getCornerLatLngs = function() { var result = []; var offsets = [ [ 0.0, 0.0 ], [ 0.0, 1.0 ], [ 1.0, 1.0 ], [ 1.0, 0.0 ] ]; for (var i=0; i<4; i++) { var st = S2.IJToST(this.ij, this.level, offsets[i]); var uv = S2.STToUV(st); var xyz = S2.FaceUVToXYZ(this.face, uv); result.push ( S2.XYZToLatLng(xyz) ); } return result; }; S2.S2Cell.prototype.getFaceAndQuads = function () { var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face); return [this.face,quads]; }; S2.S2Cell.prototype.toHilbertQuadkey = function () { var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face); return this.face.toString(10) + "/" + quads.join(""); }; S2.latLngToNeighborKeys = S2.S2Cell.latLngToNeighborKeys = function (lat, lng, level) { return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).getNeighbors().map(function (cell) { return cell.toHilbertQuadkey(); }); }; S2.S2Cell.prototype.getNeighbors = function() { var fromFaceIJWrap = function(face,ij,level) { var maxSize = (1<=0 && ij[1]>=0 && ij[0] levelN) { posS = posS.substr(0, levelN); } // 3-bit face value faceB = Long.fromString(faceN.toString(10), true, 10).toString(2); while (faceB.length < S2.FACE_BITS) { faceB = "0" + faceB; } // 60-bit position value posB = Long.fromString(posS, true, 4).toString(2); while (posB.length < (2 * levelN)) { posB = "0" + posB; } bin = faceB + posB; // 1-bit lsb marker bin += "1"; // n-bit padding to 64-bits while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { bin += "0"; } return Long.fromString(bin, true, 2).toString(10); }; S2.keyToId = S2.S2Cell.keyToId = S2.toId = S2.toCellId = S2.fromKey = function (key) { var parts = key.split("/"); return S2.fromFacePosLevel(parts[0], parts[1], parts[1].length); }; S2.idToKey = S2.S2Cell.idToKey = S2.S2Cell.toKey = S2.toKey = S2.fromId = S2.fromCellId = S2.S2Cell.toHilbertQuadkey = S2.toHilbertQuadkey = function (idS) { var Long = exports.dcodeIO && exports.dcodeIO.Long || requireLong(); var bin = Long.fromString(idS, true, 10).toString(2); while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { bin = "0" + bin; } // MUST come AFTER binstr has been left-padded with "0"s var lsbIndex = bin.lastIndexOf("1"); // substr(start, len) // substring(start, end) // includes start, does not include end var faceB = bin.substring(0, 3); // posB will always be a multiple of 2 (or it"s invalid) var posB = bin.substring(3, lsbIndex); var levelN = posB.length / 2; var faceS = Long.fromString(faceB, true, 2).toString(10); var posS = Long.fromString(posB, true, 2).toString(4); while (posS.length < levelN) { posS = "0" + posS; } return faceS + "/" + posS; }; S2.keyToLatLng = S2.S2Cell.keyToLatLng = function (key) { var cell2 = S2.S2Cell.FromHilbertQuadKey(key); return cell2.getLatLng(); }; S2.idToLatLng = S2.S2Cell.idToLatLng = function (id) { var key = S2.idToKey(id); return S2.keyToLatLng(key); }; S2.S2Cell.latLngToKey = S2.latLngToKey = S2.latLngToQuadkey = function (lat, lng, level) { if (isNaN(level) || level < 1 || level > 30) { throw new Error(""level" is not a number between 1 and 30 (but it should be)"); } // TODO // // S2.idToLatLng(id) // S2.keyToLatLng(key) // S2.nextFace(key) // prevent wrapping on nextKey // S2.prevFace(key) // prevent wrapping on prevKey // // .toKeyArray(id) // face,quadtree // .toKey(id) // hilbert // .toPoint(id) // ij // .toId(key) // uint64 (as string) // .toLong(key) // long.js // .toLatLng(id) // object? or array?, or string (with comma)? // // maybe S2.HQ.x, S2.GPS.x, S2.CI.x? return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).toHilbertQuadkey(); }; S2.stepKey = function (key, num) { var Long = exports.dcodeIO && exports.dcodeIO.Long || requireLong(); var parts = key.split("/"); var faceS = parts[0]; var posS = parts[1]; var level = parts[1].length; var posL = Long.fromString(posS, true, 4); // TODO handle wrapping (0 === pos + 1) // (only on the 12 edges of the globe) var otherL; if (num > 0) { otherL = posL.add(Math.abs(num)); } else if (num < 0) { otherL = posL.subtract(Math.abs(num)); } var otherS = otherL.toString(4); if ("0" === otherS) { console.warning(new Error("face/position wrapping is not yet supported")); } while (otherS.length < level) { otherS = "0" + otherS; } return faceS + "/" + otherS; }; S2.S2Cell.prevKey = S2.prevKey = function (key) { return S2.stepKey(key, -1); }; S2.S2Cell.nextKey = S2.nextKey = function (key) { return S2.stepKey(key, 1); }; })(module.exports ); } (s2geometry)); /** * @class GeometryProviderBase * * @classdesc Base class to extend if implementing a geometry * provider class. * * @example * ```js * class MyGeometryProvider extends GeometryProviderBase { * ... * } * ``` */ class GeometryProviderBase { /** * Create a new geometry provider base instance. */ constructor() { } /** * Convert a geodetic bounding box to the the minimum set * of cell ids containing the bounding box. * * @description The bounding box needs * to be sufficiently small to be contained in an area with the size * of maximally four tiles. Up to nine adjacent tiles may be returned. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * * @returns {Array} Array of cell ids. */ bboxToCellIds(sw, ne) { throw new MapillaryError("Not implemented"); } /** * Get the cell ids of all adjacent cells. * * @description In the case of approximately rectangular cells * this is typically the eight orthogonally and diagonally adjacent * cells. * * @param {string} cellId - Id of cell. * @returns {Array} Array of cell ids. No specific * order is guaranteed. */ getAdjacent(cellId) { throw new MapillaryError("Not implemented"); } /** * Get the vertices of a cell. * * @description The vertices form an unclosed * clockwise polygon in the 2D longitude, latitude * space. No assumption on the position of the first * vertex relative to the others can be made. * * @param {string} cellId - Id of cell. * @returns {Array} Unclosed clockwise polygon. */ getVertices(cellId) { throw new MapillaryError("Not implemented"); } /** * Convert geodetic coordinates to a cell id. * * @param {LngLat} lngLat - Longitude, latitude to convert. * @returns {string} Cell id for the longitude, latitude. */ lngLatToCellId(lngLat) { throw new MapillaryError("Not implemented"); } /** @ignore */ _approxBboxToCellIds(sw, ne) { if (ne.lat <= sw.lat || ne.lng <= sw.lng) { throw new MapillaryError("North east needs to be top right of south west"); } const centerLat = (sw.lat + ne.lat) / 2; const centerLng = (sw.lng + ne.lng) / 2; const enu = geodeticToEnu(ne.lng, ne.lat, 0, centerLng, centerLat, 0); const threshold = Math.max(enu[0], enu[1]); return this._lngLatToCellIds({ lat: centerLat, lng: centerLng }, threshold); } /** @ignore */ _enuToGeodetic(point, reference) { const [lng, lat] = enuToGeodetic(point[0], point[1], point[2], reference.lng, reference.lat, 0); return { lat, lng }; } /** @ignore */ _getLngLatBoundingBoxCorners(lngLat, threshold) { return [ [-threshold, threshold, 0], [threshold, threshold, 0], [threshold, -threshold, 0], [-threshold, -threshold, 0], ].map((point) => { return this._enuToGeodetic(point, lngLat); }); } /** * Convert a geodetic square to cell ids. * * The square is specified as a longitude, latitude * and a threshold from the position using Manhattan distance. * * @param {LngLat} lngLat - Longitude, latitude. * @param {number} threshold - Threshold of the conversion in meters. * * @returns {Array} Array of cell ids reachable within * the threshold. * * @ignore */ _lngLatToCellIds(lngLat, threshold) { const cellId = this.lngLatToCellId(lngLat); const bboxCorners = this._getLngLatBoundingBoxCorners(lngLat, threshold); for (const corner of bboxCorners) { const cid = this.lngLatToCellId(corner); if (cid !== cellId) { return [cellId, ...this.getAdjacent(cellId)]; } } return [cellId]; } } /** * @class S2GeometryProvider * * @classdesc Geometry provider based on S2 cells. * * @example * ```js * class MyDataProvider extends DataProviderBase { * ... * } * * const geometryProvider = new S2GeometryProvider(); * const dataProvider = new MyDataProvider(geometryProvider); * ``` */ class S2GeometryProvider extends GeometryProviderBase { /** * Create a new S2 geometry provider instance. */ constructor(_level = 17) { super(); this._level = _level; } /** @inheritdoc */ bboxToCellIds(sw, ne) { return this._approxBboxToCellIds(sw, ne); } /** @inheritdoc */ getAdjacent(cellId) { const k = s2geometry.exports.S2.idToKey(cellId); const position = k.split("/")[1]; const level = position.length; const [a0, a1, a2, a3] = this._getNeighbors(k, level); const existing = [k, a0, a1, a2, a3]; const others = Array .from(new Set([ ...this._getNeighbors(a0, level), ...this._getNeighbors(a1, level), ...this._getNeighbors(a2, level), ...this._getNeighbors(a3, level), ].filter((o) => { return !existing.includes(o); }))); const adjacent = [a0, a1, a2, a3]; for (const other of others) { let count = 0; for (const n of this._getNeighbors(other, level)) { if (existing.includes(n)) { count++; } } if (count === 2) { adjacent.push(other); } } return adjacent.map((a) => s2geometry.exports.S2.keyToId(a)); } /** @inheritdoc */ getVertices(cellId) { const key = s2geometry.exports.S2.idToKey(cellId); const cell = s2geometry.exports.S2.S2Cell.FromHilbertQuadKey(key); return cell .getCornerLatLngs() .map((c) => { return { lat: c.lat, lng: c.lng }; }); } /** @inheritdoc */ lngLatToCellId(lngLat) { return this._lngLatToId(lngLat, this._level); } _getNeighbors(s2key, level) { const latlng = s2geometry.exports.S2.keyToLatLng(s2key); const neighbors = s2geometry.exports.S2.latLngToNeighborKeys(latlng.lat, latlng.lng, level); return neighbors; } _lngLatToId(lngLat, level) { const s2key = s2geometry.exports.S2.latLngToKey(lngLat.lat, lngLat.lng, level); return s2geometry.exports.S2.keyToId(s2key); } } class Camera$1 { constructor(type, projectToSfmFunction) { this.type = type; this.projectToSfmFunction = projectToSfmFunction; this.parameters = {}; this.uniforms = {}; } } /** * Compute distortion given the distorted radius. * * Solves for d in the equation * * y = d(x, k1, k2) * x * * given the distorted radius, y. */ function distortionFromDistortedRadius(distortedRadius, k1, k2, radialPeak) { let d = 1.0; for (let i = 0; i < 10; i++) { let radius = distortedRadius / d; if (radius > radialPeak) { radius = radialPeak; } d = 1 + k1 * Math.pow(radius, 2) + k2 * Math.pow(radius, 4); } return d; } function makeRadialPeak(k1, k2) { const a = 5 * k2; const b = 3 * k1; const c = 1; const d = Math.pow(b, 2) - 4 * a * c; if (d < 0) { return Number.POSITIVE_INFINITY; } const root1 = (-b - Math.sqrt(d)) / 2 / a; const root2 = (-b + Math.sqrt(d)) / 2 / a; const minRoot = Math.min(root1, root2); const maxRoot = Math.max(root1, root2); return minRoot > 0 ? Math.sqrt(minRoot) : maxRoot > 0 ? Math.sqrt(maxRoot) : Number.POSITIVE_INFINITY; } function bearing$2(point, parameters, uniforms) { const [x, y] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Transformation const [xd, yd] = [x / focal, y / focal]; // Undistortion const dr = Math.sqrt(xd * xd + yd * yd); const d = distortionFromDistortedRadius(dr, k1, k2, radialPeak); const xp = xd / d; const yp = yd / d; // Unprojection const zp = 1; const length = Math.sqrt(xp * xp + yp * yp + zp * zp); const xb = xp / length; const yb = yp / length; const zb = zp / length; return [xb, yb, zb]; } function project$2(point, parameters, uniforms) { const [x, y, z] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Projection if (z <= 0) { return [ x < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, y < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, ]; } const xp = x / z; const yp = y / z; // Distortion let r2 = xp * xp + yp * yp; if (r2 > radialPeak * Math.sqrt(r2)) { r2 = Math.pow(radialPeak, 2); } const distortion = 1 + r2 * (k1 + r2 * k2); const xd = xp * distortion; const yd = yp * distortion; // Transformation const xt = focal * xd; const yt = focal * yd; return [xt, yt]; } const PERSPECTIVE_CAMERA_TYPE = "perspective"; const PERSPECTIVE_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing, Parameters parameters, Uniforms uniforms) { float x = bearing.x; float y = bearing.y; float z = bearing.z; float focal = parameters.focal; float k1 = parameters.k1; float k2 = parameters.k2; float radialPeak = uniforms.radialPeak; // Projection if (z < 0.) { return vec2(POSITIVE_INFINITY, POSITIVE_INFINITY); } float xp = x / z; float yp = y / z; // Distortion float r2 = xp * xp + yp * yp; if (r2 > radialPeak * sqrt(r2)) { r2 = radialPeak * radialPeak; } float distortion = 1.0 + r2 * (k1 + r2 * k2); float xd = xp * distortion; float yd = yp * distortion; // Transformation float xt = focal * xd; float yt = focal * yd; return vec2(xt, yt); } `; class PerspectiveCamera extends Camera$1 { constructor(parameters) { super(PERSPECTIVE_CAMERA_TYPE, PERSPECTIVE_PROJECT_FUNCTION); const [focal, k1, k2] = parameters; this.parameters.focal = focal; this.parameters.k1 = k1; this.parameters.k2 = k2; const radialPeak = makeRadialPeak(k1, k2); this.uniforms.radialPeak = radialPeak; } bearingFromSfm(point) { return bearing$2(point, this.parameters, this.uniforms); } projectToSfm(point) { return project$2(point, this.parameters, this.uniforms); } } const NULL_IMAGE_ID = "null-image-id"; const NULL_SEQUENCE_ID = "null-sequence-id"; class NullCameraFactory { makeCamera(_type, _parameters) { return new PerspectiveCamera([0.85, 0, 0]); } } class NullImageDataProvider extends DataProviderBase { constructor() { super(new S2GeometryProvider()); } getImageBuffer() { return generateImageBuffer(); } getMesh() { return Promise.resolve({ faces: [], vertices: [] }); } } function generateImageBuffer() { const canvas = document.createElement("canvas"); const w = 1; const h = 1; canvas.width = w; canvas.height = h; const ctx = canvas.getContext("2d"); ctx.fillStyle = `rgb(0 0 0)`; ctx.fillRect(0, 0, w, h); return new Promise((resolve) => { canvas.toBlob((blob) => { blob.arrayBuffer() .then(buffer => resolve(buffer)); }, "image/jpeg", 1); }); } function isNullImageId(imageId) { return imageId === NULL_IMAGE_ID; } function isNullSequenceId(sequenceId) { return sequenceId === NULL_SEQUENCE_ID; } function makeNullImage$() { const core = { computed_geometry: null, geometry: { lat: 90, lng: 0 }, id: NULL_IMAGE_ID, sequence: { id: NULL_SEQUENCE_ID, }, }; const image = new Image$1(core); const spatial = { altitude: 0, camera_parameters: [], camera_type: "null-camera-type", captured_at: 0, cluster: { id: "null-cluster-id", url: "null-cluster-url" }, compass_angle: 0, creator: { id: "null-creator-id", username: "null-creator-username" }, exif_orientation: 0, height: 0, id: NULL_IMAGE_ID, mesh: { id: "null-mesh-id", url: "null-mesh-url" }, owner: { id: "null-owner-id" }, thumb: { id: "null-thumb-id", url: "null-thumb-url" }, width: 0, atomic_scale: 0, computed_altitude: 0, computed_compass_angle: 0, computed_rotation: [0, 0, 0], merge_id: "null-merge-id", private: false, quality_score: 0, }; image.makeComplete(spatial); image.initializeCache(new ImageCache(new NullImageDataProvider())); image.cacheSequenceEdges([]); image.cacheSpatialEdges([]); return image.cacheAssets$(new NullCameraFactory()); } /** * @class SequenceComponent * @classdesc Component showing navigation arrows for sequence directions * as well as playing button. Exposes an API to start and stop play. */ class SequenceComponent extends Component { constructor(name, container, navigator, renderer, scheduler) { super(name, container, navigator); this._sequenceDOMRenderer = !!renderer ? renderer : new SequenceDOMRenderer(container); this._scheduler = scheduler; this._containerWidth$ = new Subject(); this._hoveredIdSubject$ = new Subject(); this._hoveredId$ = this._hoveredIdSubject$.pipe(share()); this._navigator.playService.playing$.pipe(skip(1), withLatestFrom(this._configuration$)) .subscribe(([playing, configuration]) => { const type = "playing"; const event = { playing, target: this, type, }; this.fire(type, event); if (playing === configuration.playing) { return; } if (playing) { this.play(); } else { this.stop(); } }); this._navigator.playService.direction$.pipe(skip(1), withLatestFrom(this._configuration$)) .subscribe(([direction, configuration]) => { if (direction !== configuration.direction) { this.configure({ direction }); } }); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Start playing. * * @fires playing */ play() { this.configure({ playing: true }); } /** * Stop playing. * * @fires playing */ stop() { this.configure({ playing: false }); } _activate() { this._sequenceDOMRenderer.activate(); const edgeStatus$ = this._navigator.stateService.currentImage$.pipe(switchMap((image) => { return image.sequenceEdges$; }), publishReplay(1), refCount()); const sequence$ = this._navigator.stateService.currentImage$.pipe(distinctUntilChanged(undefined, (image) => { return image.sequenceId; }), switchMap((image) => { return concat(of(null), this._navigator.graphService.cacheSequence$(image.sequenceId).pipe(retry(3), catchError((e) => { if (!isNullSequenceId(image.sequenceId)) { console.error("Failed to cache sequence", e); } return of(null); }))); }), startWith(null), publishReplay(1), refCount()); const subs = this._subscriptions; subs.push(sequence$.subscribe()); const rendererId$ = this._sequenceDOMRenderer.index$.pipe(withLatestFrom(sequence$), map(([index, sequence]) => { return sequence != null && !isNullSequenceId(sequence.id) ? sequence.imageIds[index] : null; }), filter((id) => { return !!id; }), distinctUntilChanged(), publish(), refCount()); subs.push(merge(rendererId$.pipe(debounceTime(100, this._scheduler)), rendererId$.pipe(auditTime(400, this._scheduler))).pipe(distinctUntilChanged(), switchMap((id) => { return this._navigator.moveTo$(id).pipe(catchError(() => { return empty(); })); })) .subscribe()); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return changing; })) .subscribe(() => { this._navigator.graphService.setGraphMode(GraphMode.Sequence); })); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return !changing; })) .subscribe(() => { this._navigator.graphService.setGraphMode(GraphMode.Spatial); })); this._navigator.graphService.graphMode$.pipe(switchMap((mode) => { return mode === GraphMode.Spatial ? this._navigator.stateService.currentImage$.pipe(take(2)) : empty(); }), filter((image) => { return !image.spatialEdges.cached; }), switchMap((image) => { return this._navigator.graphService.cacheImage$(image.id).pipe(catchError(() => { return empty(); })); })) .subscribe(); subs.push(this._sequenceDOMRenderer.changingPositionChanged$.pipe(filter((changing) => { return changing; })) .subscribe(() => { this._navigator.playService.stop(); })); subs.push(combineLatest(this._navigator.graphService.graphMode$, this._sequenceDOMRenderer.changingPositionChanged$.pipe(startWith(false), distinctUntilChanged())).pipe(withLatestFrom(this._navigator.stateService.currentImage$), switchMap(([[mode, changing], image]) => { return changing && mode === GraphMode.Sequence ? this._navigator.graphService.cacheSequenceImages$(image.sequenceId, image.id).pipe(retry(3), catchError((error) => { console.error("Failed to cache sequence images.", error); return empty(); })) : empty(); })) .subscribe()); const position$ = sequence$.pipe(switchMap((sequence) => { if (!sequence) { return of({ index: null, max: null }); } let firstCurrentId = true; return this._sequenceDOMRenderer.changingPositionChanged$.pipe(startWith(false), distinctUntilChanged(), switchMap((changingPosition) => { const skipCount = !changingPosition && firstCurrentId ? 0 : 1; firstCurrentId = false; return changingPosition ? rendererId$ : this._navigator.stateService.currentImage$.pipe(map((image) => { return image.id; }), distinctUntilChanged(), skip(skipCount)); }), map((imageId) => { if (isNullSequenceId(sequence.id)) { return { index: null, max: null }; } const index = sequence.imageIds.indexOf(imageId); if (index === -1) { return { index: null, max: null }; } return { index: index, max: sequence.imageIds.length - 1 }; })); })); const earth$ = this._navigator.stateService.state$.pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged()); subs.push(combineLatest(edgeStatus$, this._configuration$, this._containerWidth$, this._sequenceDOMRenderer.changed$.pipe(startWith(this._sequenceDOMRenderer)), this._navigator.playService.speed$, position$, earth$).pipe(map(([edgeStatus, configuration, containerWidth, , speed, position, earth]) => { const vNode = this._sequenceDOMRenderer .render(edgeStatus, configuration, containerWidth, speed, position.index, position.max, !earth, this, this._navigator); return { name: this._name, vNode: vNode }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._sequenceDOMRenderer.speed$ .subscribe((speed) => { this._navigator.playService.setSpeed(speed); })); subs.push(this._configuration$.pipe(map((configuration) => { return configuration.direction; }), distinctUntilChanged()) .subscribe((direction) => { this._navigator.playService.setDirection(direction); })); subs.push(combineLatest(this._container.renderService.size$, this._configuration$.pipe(distinctUntilChanged((value1, value2) => { return value1[0] === value2[0] && value1[1] === value2[1]; }, (configuration) => { return [configuration.minWidth, configuration.maxWidth]; }))).pipe(map(([size, configuration]) => { return this._sequenceDOMRenderer.getContainerWidth(size, configuration); })) .subscribe(this._containerWidth$)); subs.push(this._configuration$.pipe(map((configuration) => { return configuration.playing; }), distinctUntilChanged()) .subscribe((playing) => { if (playing) { this._navigator.playService.play(); } else { this._navigator.playService.stop(); } })); subs.push(this._sequenceDOMRenderer.mouseEnterDirection$.pipe(switchMap((direction) => { const edgeTo$ = edgeStatus$.pipe(map((edgeStatus) => { for (let edge of edgeStatus.edges) { if (edge.data.direction === direction) { return edge.target; } } return null; }), takeUntil(this._sequenceDOMRenderer.mouseLeaveDirection$)); return concat(edgeTo$, of(null)); }), distinctUntilChanged()) .subscribe(this._hoveredIdSubject$)); subs.push(this._hoveredId$ .subscribe((id) => { const type = "hover"; const event = { id, target: this, type, }; this.fire(type, event); })); } _deactivate() { this._subscriptions.unsubscribe(); this._sequenceDOMRenderer.deactivate(); } _getDefaultConfiguration() { return { direction: exports.NavigationDirection.Next, maxWidth: 108, minWidth: 70, playing: false, visible: true, }; } } /** @inheritdoc */ SequenceComponent.componentName = "sequence"; /** * @class Transform * * @classdesc Class used for calculating coordinate transformations * and projections. */ class Transform { /** * Create a new transform instance. * @param {number} orientation - Image orientation. * @param {number} width - Image height. * @param {number} height - Image width. * @param {number} focal - Focal length. * @param {number} scale - Atomic scale. * @param {Array} rotation - Rotation vector in three dimensions. * @param {Array} translation - Translation vector in three dimensions. * @param {HTMLImageElement} image - Image for fallback size calculations. */ constructor(orientation, width, height, scale, rotation, translation, image, camera) { this.camera = camera; this._orientation = this._getValue(orientation, 1); let imageWidth = image != null ? image.width : 4; let imageHeight = image != null ? image.height : 3; let keepOrientation = this._orientation < 5; this._width = this._getValue(width, keepOrientation ? imageWidth : imageHeight); this._height = this._getValue(height, keepOrientation ? imageHeight : imageWidth); this._basicAspect = keepOrientation ? this._width / this._height : this._height / this._width; this._basicWidth = keepOrientation ? width : height; this._basicHeight = keepOrientation ? height : width; this._focal = this._getValue(camera.parameters.focal, 1); this._scale = this._getValue(scale, 0); this._worldToCamera = this.createWorldToCamera(rotation, translation); this._worldToCameraInverse = new Matrix4() .copy(this._worldToCamera) .invert(); this._scaledWorldToCamera = this._createScaledWorldToCamera(this._worldToCamera, this._scale); this._scaledWorldToCameraInverse = new Matrix4() .copy(this._scaledWorldToCamera) .invert(); this._basicWorldToCamera = this._createBasicWorldToCamera(this._worldToCamera, orientation); } get cameraType() { return this.camera.type; } /** * Get basic aspect. * @returns {number} The orientation adjusted aspect ratio. */ get basicAspect() { return this._basicAspect; } /** * Get basic height. * * @description Does not fall back to image image height but * uses original value from API so can be faulty. * * @returns {number} The height of the basic version image * (adjusted for orientation). */ get basicHeight() { return this._basicHeight; } get basicRt() { return this._basicWorldToCamera; } /** * Get basic width. * * @description Does not fall back to image image width but * uses original value from API so can be faulty. * * @returns {number} The width of the basic version image * (adjusted for orientation). */ get basicWidth() { return this._basicWidth; } /** * Get focal. * @returns {number} The image focal length. */ get focal() { return this._focal; } /** * Get height. * * @description Falls back to the image image height if * the API data is faulty. * * @returns {number} The orientation adjusted image height. */ get height() { return this._height; } /** * Get orientation. * @returns {number} The image orientation. */ get orientation() { return this._orientation; } /** * Get rt. * @returns {THREE.Matrix4} The extrinsic camera matrix. */ get rt() { return this._worldToCamera; } /** * Get rtInverse. * @returns {THREE.Matrix4} The inverse of the extrinsic camera matrix. */ get rtInverse() { return this._worldToCameraInverse; } /** * Get srt. * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. */ get srt() { return this._scaledWorldToCamera; } /** * Get srtInverse. * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. */ get srtInverse() { return this._scaledWorldToCameraInverse; } /** * Get scale. * @returns {number} The image atomic reconstruction scale. */ get scale() { return this._scale; } /** * Get has valid scale. * @returns {boolean} Value indicating if the scale of the transform is valid. */ get hasValidScale() { return this._scale > 1e-2 && this._scale < 50; } /** * Get width. * * @description Falls back to the image image width if * the API data is faulty. * * @returns {number} The orientation adjusted image width. */ get width() { return this._width; } /** * Calculate the up vector for the image transform. * * @returns {THREE.Vector3} Normalized and orientation adjusted up vector. */ upVector() { let rte = this._worldToCamera.elements; switch (this._orientation) { case 1: return new Vector3(-rte[1], -rte[5], -rte[9]); case 3: return new Vector3(rte[1], rte[5], rte[9]); case 6: return new Vector3(-rte[0], -rte[4], -rte[8]); case 8: return new Vector3(rte[0], rte[4], rte[8]); default: return new Vector3(-rte[1], -rte[5], -rte[9]); } } /** * Project 3D world coordinates to basic coordinates. * * @param {Array} point3d - 3D world coordinates. * @return {Array} 2D basic coordinates. */ projectBasic(point3d) { let sfm = this.projectSfM(point3d); return this._sfmToBasic(sfm); } /** * Unproject basic coordinates to 3D world coordinates. * * @param {Array} basic - 2D basic coordinates. * @param {Array} distance - Distance to unproject from camera center. * @param {boolean} [depth] - Treat the distance value as depth from camera center. * Only applicable for perspective images. Will be * ignored for spherical. * @returns {Array} Unprojected 3D world coordinates. */ unprojectBasic(basic, distance, depth) { let sfm = this._basicToSfm(basic); return this.unprojectSfM(sfm, distance, depth); } /** * Project 3D world coordinates to SfM coordinates. * * @param {Array} point3d - 3D world coordinates. * @return {Array} 2D SfM coordinates. */ projectSfM(point3d) { let v = new Vector4(point3d[0], point3d[1], point3d[2], 1); v.applyMatrix4(this._worldToCamera); return this._bearingToSfm([v.x, v.y, v.z]); } /** * Unproject SfM coordinates to a 3D world coordinates. * * @param {Array} sfm - 2D SfM coordinates. * @param {Array} distance - Distance to unproject * from camera center. * @param {boolean} [depth] - Treat the distance value as * depth from camera center. Only applicable for perspective * images. Will be ignored for spherical. * @returns {Array} Unprojected 3D world coordinates. */ unprojectSfM(sfm, distance, depth) { const bearing = this._sfmToBearing(sfm); const unprojectedCamera = depth && !isSpherical(this.camera.type) ? new Vector4(distance * bearing[0] / bearing[2], distance * bearing[1] / bearing[2], distance, 1) : new Vector4(distance * bearing[0], distance * bearing[1], distance * bearing[2], 1); const unprojectedWorld = unprojectedCamera .applyMatrix4(this._worldToCameraInverse); return [ unprojectedWorld.x / unprojectedWorld.w, unprojectedWorld.y / unprojectedWorld.w, unprojectedWorld.z / unprojectedWorld.w, ]; } /** * Transform SfM coordinates to bearing vector (3D cartesian * coordinates on the unit sphere). * * @param {Array} sfm - 2D SfM coordinates. * @returns {Array} Bearing vector (3D cartesian coordinates * on the unit sphere). */ _sfmToBearing(sfm) { return this.camera.bearingFromSfm(sfm); } /** * Transform bearing vector (3D cartesian coordiantes on the unit sphere) to * SfM coordinates. * * @param {Array} bearing - Bearing vector (3D cartesian coordinates on the * unit sphere). * @returns {Array} 2D SfM coordinates. */ _bearingToSfm(bearing) { return this.camera.projectToSfm(bearing); } /** * Convert basic coordinates to SfM coordinates. * * @param {Array} basic - 2D basic coordinates. * @returns {Array} 2D SfM coordinates. */ _basicToSfm(basic) { let rotatedX; let rotatedY; switch (this._orientation) { case 1: rotatedX = basic[0]; rotatedY = basic[1]; break; case 3: rotatedX = 1 - basic[0]; rotatedY = 1 - basic[1]; break; case 6: rotatedX = basic[1]; rotatedY = 1 - basic[0]; break; case 8: rotatedX = 1 - basic[1]; rotatedY = basic[0]; break; default: rotatedX = basic[0]; rotatedY = basic[1]; break; } let w = this._width; let h = this._height; let s = Math.max(w, h); let sfmX = rotatedX * w / s - w / s / 2; let sfmY = rotatedY * h / s - h / s / 2; return [sfmX, sfmY]; } /** * Convert SfM coordinates to basic coordinates. * * @param {Array} sfm - 2D SfM coordinates. * @returns {Array} 2D basic coordinates. */ _sfmToBasic(sfm) { let w = this._width; let h = this._height; let s = Math.max(w, h); let rotatedX = (sfm[0] + w / s / 2) / w * s; let rotatedY = (sfm[1] + h / s / 2) / h * s; let basicX; let basicY; switch (this._orientation) { case 1: basicX = rotatedX; basicY = rotatedY; break; case 3: basicX = 1 - rotatedX; basicY = 1 - rotatedY; break; case 6: basicX = 1 - rotatedY; basicY = rotatedX; break; case 8: basicX = rotatedY; basicY = 1 - rotatedX; break; default: basicX = rotatedX; basicY = rotatedY; break; } return [basicX, basicY]; } /** * Checks a value and returns it if it exists and is larger than 0. * Fallbacks if it is null. * * @param {number} value - Value to check. * @param {number} fallback - Value to fall back to. * @returns {number} The value or its fallback value if it is not defined or negative. */ _getValue(value, fallback) { return value != null && value > 0 ? value : fallback; } _getCameraParameters(value, cameraType) { if (isSpherical(cameraType)) { return []; } if (!value || value.length === 0) { return [1, 0, 0]; } const padding = 3 - value.length; if (padding <= 0) { return value; } return value .concat(new Array(padding) .fill(0)); } /** * Creates the extrinsic camera matrix [ R | t ]. * * @param {Array} rotation - Rotation vector in angle axis representation. * @param {Array} translation - Translation vector. * @returns {THREE.Matrix4} Extrisic camera matrix. */ createWorldToCamera(rotation, translation) { const axis = new Vector3(rotation[0], rotation[1], rotation[2]); const angle = axis.length(); if (angle > 0) { axis.normalize(); } const worldToCamera = new Matrix4(); worldToCamera.makeRotationAxis(axis, angle); worldToCamera.setPosition(new Vector3(translation[0], translation[1], translation[2])); return worldToCamera; } /** * Calculates the scaled extrinsic camera matrix scale * [ R | t ]. * * @param {THREE.Matrix4} worldToCamera - Extrisic camera matrix. * @param {number} scale - Scale factor. * @returns {THREE.Matrix4} Scaled extrisic camera matrix. */ _createScaledWorldToCamera(worldToCamera, scale) { const scaledWorldToCamera = worldToCamera.clone(); const elements = scaledWorldToCamera.elements; elements[12] = scale * elements[12]; elements[13] = scale * elements[13]; elements[14] = scale * elements[14]; scaledWorldToCamera.scale(new Vector3(scale, scale, scale)); return scaledWorldToCamera; } _createBasicWorldToCamera(rt, orientation) { const axis = new Vector3(0, 0, 1); let angle = 0; switch (orientation) { case 3: angle = Math.PI; break; case 6: angle = Math.PI / 2; break; case 8: angle = 3 * Math.PI / 2; break; } return new Matrix4() .makeRotationAxis(axis, angle) .multiply(rt); } /** * Calculate a transformation matrix from normalized coordinates for * texture map coordinates. * * @returns {THREE.Matrix4} Normalized coordinates to texture map * coordinates transformation matrix. */ _normalizedToTextureMatrix() { const size = Math.max(this._width, this._height); const w = size / this._width; const h = size / this._height; switch (this._orientation) { case 1: return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 3: return new Matrix4().set(-w, 0, 0, 0.5, 0, h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 6: return new Matrix4().set(0, -h, 0, 0.5, -w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); case 8: return new Matrix4().set(0, h, 0, 0.5, w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); default: return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); } } } class PlayService { constructor(graphService, stateService) { this._subscriptions = new SubscriptionHolder(); this._graphService = graphService; this._stateService = stateService; const subs = this._subscriptions; this._directionSubject$ = new Subject(); this._direction$ = this._directionSubject$.pipe(startWith(exports.NavigationDirection.Next), publishReplay(1), refCount()); subs.push(this._direction$.subscribe()); this._playing = false; this._playingSubject$ = new Subject(); this._playing$ = this._playingSubject$.pipe(startWith(this._playing), publishReplay(1), refCount()); subs.push(this._playing$.subscribe()); this._speed = 0.5; this._speedSubject$ = new Subject(); this._speed$ = this._speedSubject$.pipe(startWith(this._speed), publishReplay(1), refCount()); subs.push(this._speed$.subscribe()); this._imagesAhead = this._mapImagesAhead(this._mapSpeed(this._speed)); this._bridging$ = null; } get playing() { return this._playing; } get direction$() { return this._direction$; } get playing$() { return this._playing$; } get speed$() { return this._speed$; } play() { if (this._playing) { return; } this._stateService.cutImages(); const stateSpeed = this._setSpeed(this._speed); this._stateService.setSpeed(stateSpeed); this._graphModeSubscription = this._speed$.pipe(map((speed) => { return speed > PlayService.sequenceSpeed ? GraphMode.Sequence : GraphMode.Spatial; }), distinctUntilChanged()) .subscribe((mode) => { this._graphService.setGraphMode(mode); }); this._cacheSubscription = combineLatest(this._stateService.currentImage$.pipe(map((image) => { return [image.sequenceId, image.id]; }), distinctUntilChanged(undefined, ([sequenceId]) => { return sequenceId; })), this._graphService.graphMode$, this._direction$).pipe(switchMap(([[sequenceId, imageId], mode, direction]) => { if (direction !== exports.NavigationDirection.Next && direction !== exports.NavigationDirection.Prev) { return of([undefined, direction]); } const sequence$ = (mode === GraphMode.Sequence ? this._graphService.cacheSequenceImages$(sequenceId, imageId) : this._graphService.cacheSequence$(sequenceId)).pipe(retry(3), catchError((error) => { console.error(error); return of(undefined); })); return combineLatest(sequence$, of(direction)); }), switchMap(([sequence, direction]) => { if (sequence === undefined) { return empty(); } const imageIds = sequence.imageIds.slice(); if (direction === exports.NavigationDirection.Prev) { imageIds.reverse(); } return this._stateService.currentState$.pipe(map((frame) => { return [frame.state.trajectory[frame.state.trajectory.length - 1].id, frame.state.imagesAhead]; }), scan(([lastRequestKey, previousRequestKeys], [lastTrajectoryKey, imagesAhead]) => { if (lastRequestKey === undefined) { lastRequestKey = lastTrajectoryKey; } const lastIndex = imageIds.length - 1; if (imagesAhead >= this._imagesAhead || imageIds[lastIndex] === lastRequestKey) { return [lastRequestKey, []]; } const current = imageIds.indexOf(lastTrajectoryKey); const start = imageIds.indexOf(lastRequestKey) + 1; const end = Math.min(lastIndex, current + this._imagesAhead - imagesAhead) + 1; if (end <= start) { return [lastRequestKey, []]; } return [imageIds[end - 1], imageIds.slice(start, end)]; }, [undefined, []]), mergeMap(([lastRequestKey, newRequestKeys]) => { return from(newRequestKeys); })); }), mergeMap((key) => { return this._graphService.cacheImage$(key).pipe(catchError(() => { return empty(); })); }, 6)) .subscribe(); this._playingSubscription = this._stateService.currentState$.pipe(filter((frame) => { return frame.state.imagesAhead < this._imagesAhead; }), distinctUntilChanged(undefined, (frame) => { return frame.state.lastImage.id; }), map((frame) => { const lastImage = frame.state.lastImage; const trajectory = frame.state.trajectory; let increasingTime = undefined; for (let i = trajectory.length - 2; i >= 0; i--) { const image = trajectory[i]; if (image.sequenceId !== lastImage.sequenceId) { break; } if (image.capturedAt !== lastImage.capturedAt) { increasingTime = image.capturedAt < lastImage.capturedAt; break; } } return [frame.state.lastImage, increasingTime]; }), withLatestFrom(this._direction$), switchMap(([[image, increasingTime], direction]) => { return zip(([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), timeout(15000)), of(direction)).pipe(map(([s, d]) => { for (let edge of s.edges) { if (edge.data.direction === d) { return edge.target; } } return null; }), switchMap((key) => { return key != null ? this._graphService.cacheImage$(key) : empty(); })); })) .subscribe((image) => { this._stateService.appendImagess([image]); }, (error) => { console.error(error); this.stop(); }); this._clearSubscription = this._stateService.currentImage$.pipe(bufferCount(1, 10)) .subscribe((images) => { this._stateService.clearPriorImages(); }); this._setPlaying(true); const currentLastImages$ = this._stateService.currentState$.pipe(map((frame) => { return frame.state; }), distinctUntilChanged(([kc1, kl1], [kc2, kl2]) => { return kc1 === kc2 && kl1 === kl2; }, (state) => { return [state.currentImage.id, state.lastImage.id]; }), filter((state) => { return state.currentImage.id === state.lastImage.id && state.currentIndex === state.trajectory.length - 1; }), map((state) => { return state.currentImage; })); this._stopSubscription = combineLatest(currentLastImages$, this._direction$).pipe(switchMap(([image, direction]) => { const edgeStatus$ = ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first((status) => { return status.cached; }), timeout(15000), catchError((error) => { console.error(error); return of({ cached: false, edges: [] }); })); return combineLatest(of(direction), edgeStatus$).pipe(map(([d, es]) => { for (const edge of es.edges) { if (edge.data.direction === d) { return true; } } return false; })); }), mergeMap((hasEdge) => { if (hasEdge || !this._bridging$) { return of(hasEdge); } return this._bridging$.pipe(map((image) => { return image != null; }), catchError((error) => { console.error(error); return of(false); })); }), first((hasEdge) => { return !hasEdge; })) .subscribe(undefined, undefined, () => { this.stop(); }); if (this._stopSubscription.closed) { this._stopSubscription = null; } this._earthSubscription = this._stateService.state$ .pipe(map((state) => { return state === State.Earth; }), distinctUntilChanged(), first((earth) => { return earth; })) .subscribe(undefined, undefined, () => { this.stop(); }); if (this._earthSubscription.closed) { this._earthSubscription = null; } } dispose() { this.stop(); this._subscriptions.unsubscribe(); } setDirection(direction) { this._directionSubject$.next(direction); } setSpeed(speed) { speed = Math.max(0, Math.min(1, speed)); if (speed === this._speed) { return; } const stateSpeed = this._setSpeed(speed); if (this._playing) { this._stateService.setSpeed(stateSpeed); } this._speedSubject$.next(this._speed); } stop() { if (!this._playing) { return; } if (!!this._stopSubscription) { if (!this._stopSubscription.closed) { this._stopSubscription.unsubscribe(); } this._stopSubscription = null; } if (!!this._earthSubscription) { if (!this._earthSubscription.closed) { this._earthSubscription.unsubscribe(); } this._earthSubscription = null; } this._graphModeSubscription.unsubscribe(); this._graphModeSubscription = null; this._cacheSubscription.unsubscribe(); this._cacheSubscription = null; this._playingSubscription.unsubscribe(); this._playingSubscription = null; this._clearSubscription.unsubscribe(); this._clearSubscription = null; this._stateService.setSpeed(1); this._stateService.cutImages(); this._graphService.setGraphMode(GraphMode.Spatial); this._setPlaying(false); } _mapSpeed(speed) { const x = 2 * speed - 1; return Math.pow(10, x) - 0.2 * x; } _mapImagesAhead(stateSpeed) { return Math.round(Math.max(10, Math.min(50, 8 + 6 * stateSpeed))); } _setPlaying(playing) { this._playing = playing; this._playingSubject$.next(playing); } _setSpeed(speed) { this._speed = speed; const stateSpeed = this._mapSpeed(this._speed); this._imagesAhead = this._mapImagesAhead(stateSpeed); return stateSpeed; } } PlayService.sequenceSpeed = 0.54; const MAX_CAMERA_SIZE = 1; const MAX_POINT_SIZE = 1; const MIN_CAMERA_SIZE = 1e-3; const MIN_POINT_SIZE = 1e-3; exports.CameraVisualizationMode = void 0; (function (CameraVisualizationMode) { /** * Cameras are hidden. */ CameraVisualizationMode[CameraVisualizationMode["Hidden"] = 0] = "Hidden"; /** * Cameras are shown, all with the same color. */ CameraVisualizationMode[CameraVisualizationMode["Homogeneous"] = 1] = "Homogeneous"; /** * Cameras are shown with colors based on the * their clusters. */ CameraVisualizationMode[CameraVisualizationMode["Cluster"] = 2] = "Cluster"; /** * Cameras are shown with colors based on the * their connected components. */ CameraVisualizationMode[CameraVisualizationMode["ConnectedComponent"] = 3] = "ConnectedComponent"; /** * Cameras are shown, with colors based on the * their sequence. */ CameraVisualizationMode[CameraVisualizationMode["Sequence"] = 4] = "Sequence"; })(exports.CameraVisualizationMode || (exports.CameraVisualizationMode = {})); exports.OriginalPositionMode = void 0; (function (OriginalPositionMode) { /** * Original positions are hidden. */ OriginalPositionMode[OriginalPositionMode["Hidden"] = 0] = "Hidden"; /** * Visualize original positions with altitude change. */ OriginalPositionMode[OriginalPositionMode["Altitude"] = 1] = "Altitude"; /** * Visualize original positions without altitude change, * i.e. as flat lines from the camera origin. */ OriginalPositionMode[OriginalPositionMode["Flat"] = 2] = "Flat"; })(exports.OriginalPositionMode || (exports.OriginalPositionMode = {})); class ClusterPoints extends Points { constructor(parameters) { super(); this._originalSize = parameters.originalSize; const { cluster, color, scale, translation } = parameters; this._makeAttributes(cluster); this.material.size = scale * this._originalSize; this.setColor(color); this.matrixAutoUpdate = false; this.position.fromArray(translation); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setColor(color) { this.material.vertexColors = color == null; this.material.color = new Color(color); this.material.needsUpdate = true; } resize(scale) { this.material.size = scale * this._originalSize; this.material.needsUpdate = true; } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(cluster) { const geometry = this.geometry; geometry.setAttribute("position", new BufferAttribute(new Float32Array(cluster.coordinates), 3)); const colorSize = cluster.colors.length / cluster.pointIds.length; geometry.setAttribute("color", new BufferAttribute(new Float32Array(cluster.colors), colorSize)); } } class CellLine extends Line { constructor(vertices) { super(); this._makeAttributes(vertices); this.matrixAutoUpdate = false; this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(vertices) { const closedPolygon = vertices.slice(); closedPolygon.push(vertices[0]); let index = 0; const positions = new Float32Array(3 * (vertices.length + 1)); for (const vertex of closedPolygon) { positions[index++] = vertex[0]; positions[index++] = vertex[1]; positions[index++] = vertex[2]; } this.geometry.setAttribute("position", new BufferAttribute(positions, 3)); } } // Level 0: 1 x 1 x 1 meter cubes const OCTREE_ROOT_LEVEL = 14; // 16384 meters const OCTREE_LEAF_LEVEL = 6; // 64 meters function isLeafLevel(level, leafLevel) { return level === leafLevel; } function levelToSize(level) { return Math.pow(2, level); } function levelToRootBoundingBox(level) { const size = levelToSize(level); const half = size / 2; const min = [-half, -half, -half]; const max = [half, half, half]; return { min, max }; } class SpatialOctreeNode { constructor(level, leafLevel, boundingBox, parent) { this.level = level; this.leafLevel = leafLevel; this.boundingBox = boundingBox; this.parent = parent; this.children = []; this.items = []; if (parent) { parent.children.push(this); } } get isEmpty() { return !(this.children.length || this.items.length); } add(object) { const self = this; if (!self.boundingBox.containsPoint(object.position)) { throw new Error(`Item not contained in node`); } if (isLeafLevel(self.level, self.leafLevel)) { self.items.push(object); return this; } for (const child of self.children) { if (child.boundingBox.containsPoint(object.position)) { return child.add(object); } } for (const boundingBox of self._generateBoundingBoxes()) { if (boundingBox.containsPoint(object.position)) { const child = new SpatialOctreeNode(self.level - 1, self.leafLevel, boundingBox, self); return child.add(object); } } throw new Error(`Item not contained in children`); } intersect(ray, target, nodes) { if (!ray.intersectBox(this.boundingBox, target)) { return; } if (isLeafLevel(this.level, this.leafLevel)) { nodes.push(this); return; } for (const child of this.children) { child.intersect(ray, target, nodes); } } remove(object) { const index = this.items.indexOf(object); if (index < 0) { throw new Error(`Item does not exist ${object.uuid}`); } this.items.splice(index, 1); } traverse() { const self = this; if (!self.isEmpty) { return; } const parent = self.parent; if (!parent) { return; } const index = parent.children.indexOf(self); if (index < 0) { throw new Error(`Corrupt octree`); } parent.children.splice(index, 1); this.parent = null; parent.traverse(); } _generateBoundingBoxes() { const self = this; const min = self.boundingBox.min; const max = self.boundingBox.max; const size = (max.x - min.x) / 2; const mins = [ [min.x, min.y + size, min.z + size], [min.x + size, min.y + size, min.z + size], [min.x, min.y, min.z + size], [min.x + size, min.y, min.z + size], [min.x, min.y + size, min.z], [min.x + size, min.y + size, min.z], [min.x, min.y, min.z], [min.x + size, min.y, min.z], ]; const boundingBoxes = []; for (const [minX, minY, minZ] of mins) { boundingBoxes.push(new Box3(new Vector3(minX, minY, minZ), new Vector3(minX + size, minY + size, minZ + size))); } return boundingBoxes; } } class SpatialOctree { constructor(rootLevel, leafLevel) { this.rootLevel = rootLevel; this.leafLevel = leafLevel; if (leafLevel > rootLevel) { throw new Error(); } this._index = new Map(); this._root = this._makeRoot(); } get root() { return this._root; } add(object) { if (!this.root.boundingBox.containsPoint(object.position)) { console.warn(`Object outside bounding box ${object.uuid}`); return; } const leaf = this._root.add(object); this._index.set(object.uuid, leaf); } has(object) { return this._index.has(object.uuid); } intersect(ray) { const leaves = []; const target = new Vector3(); this._root.intersect(ray, target, leaves); return leaves .map(leaf => leaf.items) .reduce((acc, items) => { acc.push(...items); return acc; }, []); } reset() { this._root = this._makeRoot(); this._index.clear(); } remove(object) { if (!this.has(object)) { throw new Error(`Frame does not exist ${object.uuid}`); } const leaf = this._index.get(object.uuid); leaf.remove(object); leaf.traverse(); this._index.delete(object.uuid); } _makeRoot() { const level = this.rootLevel; const bbox = levelToRootBoundingBox(level); const box = new Box3(new Vector3().fromArray(bbox.min), new Vector3().fromArray(bbox.max)); return new SpatialOctreeNode(level, this.leafLevel, box); } } const MAX_THRESHOLD = 5e-2; const MIN_THRESHOLD = 5e-3; class SpatialIntersection { constructor(octree, raycaster) { this._objects = []; this._objectImageMap = new Map(); this._octree = octree !== null && octree !== void 0 ? octree : new SpatialOctree(OCTREE_ROOT_LEVEL, OCTREE_LEAF_LEVEL); this._raycaster = raycaster !== null && raycaster !== void 0 ? raycaster : new Raycaster(); this._interactiveLayer = 1; this._raycaster = !!raycaster ? raycaster : new Raycaster(undefined, undefined, 1, 10000); this._raycaster.layers.set(this._interactiveLayer); } get interactiveLayer() { return this._interactiveLayer; } get octree() { return this._octree; } get raycaster() { return this._raycaster; } add(object, imageId) { const uuid = object.uuid; this._objectImageMap.set(uuid, imageId); this._objects.push(object); this._octree.add(object); } intersectObjects(viewport, camera) { this._raycaster.setFromCamera(new Vector2().fromArray(viewport), camera); const objects = this._octree.intersect(this.raycaster.ray); const intersects = this._raycaster.intersectObjects(objects); const onMap = this._objectImageMap; for (const intersect of intersects) { const uuid = intersect.object.uuid; if (!onMap.has(uuid)) { continue; } return onMap.get(uuid); } return null; } remove(object) { const objects = this._objects; const index = objects.indexOf(object); if (index !== -1) { const deleted = objects.splice(index, 1); for (const d of deleted) { this._objectImageMap.delete(d.uuid); } this._octree.remove(object); } else { console.warn(`Object does not exist`); } } setIntersectionThreshold(cameraSize) { const threshold = Math.min(Math.max(MIN_THRESHOLD, 2e-1 * cameraSize), MAX_THRESHOLD); this._raycaster.params.Line.threshold = threshold; } } class PositionLine extends Line { constructor(parameters) { super(parameters.geometry, parameters.material); const mode = parameters.mode; const originalOrigin = parameters.originalOrigin; const transform = parameters.transform; const origin = transform.unprojectBasic([0, 0], 0); this._relativeAltitude = originalOrigin[2] - origin[2]; this._makeAttributes(origin, originalOrigin, mode); this.matrixAutoUpdate = false; this.position.fromArray(origin); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setMode(mode) { const positionAttribute = this.geometry.attributes.position; const positions = positionAttribute.array; positions[5] = this._modeToAltitude(mode); positionAttribute.needsUpdate = true; this.geometry.computeBoundingSphere(); } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(origin, originalOrigin, mode) { const positions = new Float32Array(6); positions[0] = 0; positions[1] = 0; positions[2] = 0; positions[3] = originalOrigin[0] - origin[0]; positions[4] = originalOrigin[1] - origin[1]; positions[5] = this._modeToAltitude(mode); const attribute = new BufferAttribute(positions, 3); this.geometry.setAttribute("position", attribute); attribute.needsUpdate = true; this.geometry.computeBoundingSphere(); } _modeToAltitude(mode) { return mode === exports.OriginalPositionMode.Altitude ? this._relativeAltitude : 0; } } class CameraFrameBase extends LineSegments { constructor(parameters) { super(parameters.geometry, parameters.material); const color = parameters.color; const size = parameters.size; const scale = parameters.scale; const transform = parameters.transform; const origin = transform.unprojectBasic([0, 0], 0); const positions = this._makePositions(size, transform, origin); this._makeAttributes(positions, color); this.geometry.computeBoundingSphere(); this.geometry.computeBoundingBox(); this.matrixAutoUpdate = false; this.position.fromArray(origin); this.scale.set(scale, scale, scale); this.update(); } dispose() { this.geometry.dispose(); this.material.dispose(); } setColor(color) { this._updateColorAttribute(color); return this; } resize(scale) { this.scale.set(scale, scale, scale); this.updateMatrix(); this.updateMatrixWorld(false); return this; } update() { this.updateMatrix(); this.updateMatrixWorld(false); } _makeAttributes(positions, color) { const geometry = this.geometry; const positionAttribute = new BufferAttribute(new Float32Array(positions), 3); geometry.setAttribute("position", positionAttribute); positionAttribute.needsUpdate = true; const colorAttribute = new BufferAttribute(new Float32Array(positions.length), 3); geometry.setAttribute("color", colorAttribute); this._updateColorAttribute(color); } _updateColorAttribute(color) { const [r, g, b] = new Color(color).toArray(); const colorAttribute = this.geometry.attributes.color; const colors = colorAttribute.array; const length = colors.length; let index = 0; for (let i = 0; i < length; i++) { colors[index++] = r; colors[index++] = g; colors[index++] = b; } colorAttribute.needsUpdate = true; } } class SphericalCameraFrame extends CameraFrameBase { _makePositions(size, transform, origin) { const vs = 10; const positions = []; positions.push(...this._makeAxis(size, transform, origin)); positions.push(...this._makeLat(0.5, vs, size, transform, origin)); for (const lat of [0, 0.25, 0.5, 0.75]) { positions .push(...this._makeLng(lat, vs, size, transform, origin)); } return positions; } _makeAxis(size, transform, origin) { const south = transform.unprojectBasic([0.5, 1], 0.8 * size); const north = transform.unprojectBasic([0.5, 0], 1.2 * size); return [ south[0] - origin[0], south[1] - origin[1], south[2] - origin[2], north[0] - origin[0], north[1] - origin[1], north[2] - origin[2], ]; } _makeLat(basicY, numVertices, size, transform, origin) { const dist = 0.8 * size; const [originX, originY, originZ] = origin; const positions = []; const first = transform.unprojectBasic([0, basicY], dist); first[0] -= originX; first[1] -= originY; first[2] -= originZ; positions.push(...first); for (let i = 1; i <= numVertices; i++) { const position = transform.unprojectBasic([i / numVertices, basicY], dist); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position, ...position); } positions.push(...first); return positions; } _makeLng(basicX, numVertices, size, transform, origin) { const dist = 0.8 * size; const [originX, originY, originZ] = origin; const positions = []; const first = transform.unprojectBasic([basicX, 0], dist); first[0] -= originX; first[1] -= originY; first[2] -= originZ; positions.push(...first); for (let i = 0; i <= numVertices; i++) { const position = transform.unprojectBasic([basicX, i / numVertices], dist); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position, ...position); } positions.push(...first); return positions; } } class PerspectiveCameraFrame extends CameraFrameBase { _makePositions(size, transform, origin) { const samples = 8; const positions = []; positions.push(...this._makeDiags(size, transform, origin)); positions.push(...this._makeFrame(size, samples, transform, origin)); return positions; } _makeDiags(size, transform, origin) { const [originX, originY, originZ] = origin; const cameraCenter = [0, 0, 0]; const positions = []; for (const vertex2d of [[0, 0], [1, 0], [1, 1], [0, 1]]) { const corner = transform.unprojectBasic(vertex2d, size); corner[0] -= originX; corner[1] -= originY; corner[2] -= originZ; positions.push(...cameraCenter, ...corner); } return positions; } _makeFrame(size, samples, transform, origin) { const vertices2d = []; vertices2d.push(...this._subsample([0, 1], [0, 0], samples)); vertices2d.push(...this._subsample([0, 0], [1, 0], samples)); vertices2d.push(...this._subsample([1, 0], [1, 1], samples)); const [originX, originY, originZ] = origin; const positions = []; for (const vertex2d of vertices2d) { const position = transform.unprojectBasic(vertex2d, size); position[0] -= originX; position[1] -= originY; position[2] -= originZ; positions.push(...position); } return positions; } _interpolate(a, b, alpha) { return a + alpha * (b - a); } _subsample(p1, p2, subsamples) { if (subsamples < 1) { return [p1, p2]; } const samples = []; samples.push(p1); for (let i = 0; i <= subsamples; i++) { const p = []; for (let j = 0; j < 3; j++) { p.push(this._interpolate(p1[j], p2[j], i / (subsamples + 1))); } samples.push(p); samples.push(p); } samples.push(p2); return samples; } } const SPATIAL_DEFAULT_COLOR = 0xFFFFFF; function resetEnu(reference, prevEnu, prevReference) { const [prevX, prevY, prevZ] = prevEnu; const [lng, lat, alt] = enuToGeodetic(prevX, prevY, prevZ, prevReference.lng, prevReference.lat, prevReference.alt); return geodeticToEnu(lng, lat, alt, reference.lng, reference.lat, reference.alt); } const DEFAULT_ID = exports.CameraVisualizationMode[exports.CameraVisualizationMode.Homogeneous]; class SpatialCell { constructor(id, _scene, _intersection) { this.id = id; this._scene = _scene; this._intersection = _intersection; this.cameras = new Object3D(); this.keys = []; this._positionLines = {}; this._positions = new Object3D(); this._cameraFrames = {}; this._clusters = new Map(); this._connectedComponents = new Map(); this._defaults = new Map(); this._sequences = new Map(); this._props = {}; this.clusterVisibles = {}; this._frameMaterial = new LineBasicMaterial({ vertexColors: true, }); this._positionMaterial = new LineBasicMaterial({ color: 0xff0000, }); this._scene.add(this.cameras, this._positions); } addImage(props) { const image = props.image; const id = image.id; if (this.hasImage(id)) { throw new Error(`Image exists ${id}`); } const cId = props.idMap.clusterId; if (!this._clusters.has(cId)) { this._clusters.set(cId, []); } const ccId = props.idMap.ccId; if (!(this._connectedComponents.has(ccId))) { this._connectedComponents.set(ccId, []); } if (!(this._defaults.has(DEFAULT_ID))) { this._defaults.set(DEFAULT_ID, []); } const sId = props.idMap.sequenceId; if (!this._sequences.has(sId)) { this._sequences.set(sId, []); } this._props[id] = { image: image, ids: { ccId, clusterId: cId, sequenceId: sId }, }; this.keys.push(id); } applyCameraColor(imageId, color) { this._cameraFrames[imageId].setColor(color); } applyCameraSize(size) { for (const camera of this.cameras.children) { camera.resize(size); } } applyFilter(filter) { var _a; const clusterVisibles = this.clusterVisibles; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; } clusterVisibles[clusterId] = false; } const cameraFrames = this._cameraFrames; const positionLines = this._positionLines; const interactiveLayer = this._intersection.interactiveLayer; for (const props of Object.values(this._props)) { const image = props.image; const visible = filter(image); const key = image.id; positionLines[key].visible = visible; const camera = cameraFrames[key]; this._setCameraVisibility(camera, visible, interactiveLayer); clusterVisibles[_a = props.ids.clusterId] || (clusterVisibles[_a] = visible); } } applyPositionMode(mode) { this._positions.visible = mode !== exports.OriginalPositionMode.Hidden; for (const position of this._positions.children) { position.setMode(mode); } } dispose() { this._disposeCameras(); this._disposePositions(); this._scene = null; this._intersection = null; } getCamerasByMode(mode) { switch (mode) { case exports.CameraVisualizationMode.Cluster: return this._clusters; case exports.CameraVisualizationMode.ConnectedComponent: return this._connectedComponents; case exports.CameraVisualizationMode.Sequence: return this._sequences; default: return this._defaults; } } getColorId(imageId, mode) { const props = this._props[imageId]; const cvm = exports.CameraVisualizationMode; switch (mode) { case cvm.Cluster: return props.ids.clusterId; case cvm.ConnectedComponent: return props.ids.ccId; case cvm.Sequence: return props.ids.sequenceId; default: return DEFAULT_ID; } } hasImage(key) { return this.keys.indexOf(key) !== -1; } getCluster(imageId) { if (!this.hasImage(imageId)) { throw new Error(`Image does not exist (${imageId})`); } return this._props[imageId].ids.clusterId; } resetReference(reference, prevReference) { const frames = this._cameraFrames; for (const frameId in frames) { if (!frames.hasOwnProperty(frameId)) { continue; } const frame = frames[frameId]; frame.position.fromArray(resetEnu(reference, frame.position.toArray(), prevReference)); frame.update(); } const lines = this._positionLines; for (const lineId in lines) { if (!lines.hasOwnProperty(lineId)) { continue; } const line = lines[lineId]; line.position.fromArray(resetEnu(reference, line.position.toArray(), prevReference)); line.update(); } } visualize(props) { var _a, _b; const id = props.id; const visible = props.visible; const transform = props.transform; const cameraParameters = { color: props.color, material: this._frameMaterial, scale: props.scale, size: props.maxSize, transform, }; const camera = isSpherical(transform.cameraType) ? new SphericalCameraFrame(cameraParameters) : new PerspectiveCameraFrame(cameraParameters); const interactiveLayer = this._intersection.interactiveLayer; this._setCameraVisibility(camera, visible, interactiveLayer); this.cameras.add(camera); this._cameraFrames[id] = camera; const intersection = this._intersection; intersection.add(camera, id); const ids = this._props[id].ids; (_a = this.clusterVisibles)[_b = ids.clusterId] || (_a[_b] = visible); const idCamera = { camera, clusterId: ids.clusterId }; this._clusters.get(ids.clusterId).push(idCamera); this._connectedComponents.get(ids.ccId).push(idCamera); this._defaults.get(DEFAULT_ID).push(idCamera); this._sequences.get(ids.sequenceId).push(idCamera); const positionParameters = { material: this._positionMaterial, mode: props.positionMode, originalOrigin: props.originalPosition, transform, }; const position = new PositionLine(positionParameters); position.visible = visible; this._positions.add(position); this._positionLines[id] = position; } _disposeCameras() { const intersection = this._intersection; const cameras = this.cameras; for (const camera of cameras.children.slice()) { camera.dispose(); intersection.remove(camera); cameras.remove(camera); } this._scene.remove(this.cameras); } _disposePositions() { const positions = this._positions; for (const position of positions.children.slice()) { position.dispose(); positions.remove(position); } this._scene.remove(this._positions); } _setCameraVisibility(camera, visible, layer) { camera.visible = visible; if (visible) { camera.layers.enable(layer); } else { camera.layers.disable(layer); } } } class SpatialAssets { constructor() { this._colors = new Map(); const cvm = exports.CameraVisualizationMode; this._colors.set(cvm[cvm.Homogeneous], "#FFFFFF"); } getColor(id) { const colors = this._colors; if (!colors.has(id)) { colors.set(id, this._randomColor()); } return colors.get(id); } _randomColor() { return `hsl(${Math.floor(360 * Math.random())}, 100%, 60%)`; } } function isModeVisible(mode) { return mode !== exports.CameraVisualizationMode.Hidden; } function isOverviewState(state) { return state === State.Custom || state === State.Earth; } exports.PointVisualizationMode = void 0; (function (PointVisualizationMode) { /** * Points are hidden. */ PointVisualizationMode[PointVisualizationMode["Hidden"] = 0] = "Hidden"; /** * Visualize points with original colors. */ PointVisualizationMode[PointVisualizationMode["Original"] = 1] = "Original"; /** * Paint all points belonging to a specific * cluster with the same random color. */ PointVisualizationMode[PointVisualizationMode["Cluster"] = 2] = "Cluster"; })(exports.PointVisualizationMode || (exports.PointVisualizationMode = {})); const NO_CLUSTER_ID = "NO_CLUSTER_ID"; const NO_MERGE_ID = "NO_MERGE_ID"; const NO_SEQUENCE_ID = "NO_SEQUENCE_ID"; const RAY_NEAR_SCALE = 1.2; const ORIGINAL_CAMERA_SIZE = 1; const ORIGINAL_POINT_SIZE = 2; class SpatialScene { constructor(configuration, scene) { this._imageCellMap = new Map(); this._clusterCellMap = new Map(); this._scene = !!scene ? scene : new Scene(); this._scene.autoUpdate = false; this._intersection = new SpatialIntersection(); this._assets = new SpatialAssets(); this._needsRender = false; this._images = {}; this._cells = {}; this._cellClusters = {}; this._clusters = {}; this._cameraVisualizationMode = !!configuration.cameraVisualizationMode ? configuration.cameraVisualizationMode : exports.CameraVisualizationMode.Homogeneous; this._cameraSize = configuration.cameraSize; this._pointSize = configuration.pointSize; this._pointVisualizationMode = !!configuration.pointVisualizationMode ? configuration.pointVisualizationMode : exports.PointVisualizationMode.Original; this._positionMode = configuration.originalPositionMode; this._cellsVisible = configuration.cellsVisible; this._hoveredId = null; this._selectedId = null; this._colors = { hover: "#FF0000", select: "#FF8000" }; this._cameraOverrideColors = new Map(); this._pointOverrideColors = new Map(); this._filter = () => true; } get needsRender() { return this._needsRender; } get intersection() { return this._intersection; } addCluster(reconstruction, translation, cellId) { if (this.hasCluster(reconstruction.id, cellId)) { return; } const clusterId = reconstruction.id; if (!(clusterId in this._clusters)) { const color = this._getPointColor(clusterId); const points = new ClusterPoints({ cluster: reconstruction, color, originalSize: ORIGINAL_POINT_SIZE, scale: this._pointSize, translation, }); points.visible = this._getClusterVisible(clusterId); this._scene.add(points); this._clusters[clusterId] = { points: points, cellIds: [], }; } if (this._clusters[clusterId].cellIds.indexOf(cellId) === -1) { this._clusters[clusterId].cellIds.push(cellId); } if (!(cellId in this._cellClusters)) { this._cellClusters[cellId] = { keys: [] }; } if (this._cellClusters[cellId].keys.indexOf(clusterId) === -1) { this._cellClusters[cellId].keys.push(clusterId); } this._needsRender = true; } addImage(image, transform, originalPosition, cellId) { var _a, _b, _c; const imageId = image.id; const idMap = { clusterId: (_a = image.clusterId) !== null && _a !== void 0 ? _a : NO_CLUSTER_ID, sequenceId: (_b = image.sequenceId) !== null && _b !== void 0 ? _b : NO_SEQUENCE_ID, ccId: (_c = image.mergeId) !== null && _c !== void 0 ? _c : NO_MERGE_ID, }; if (!(cellId in this._images)) { const created = new SpatialCell(cellId, this._scene, this._intersection); created.cameras.visible = isModeVisible(this._cameraVisualizationMode); created.applyPositionMode(this._positionMode); this._images[cellId] = created; } const cell = this._images[cellId]; if (cell.hasImage(imageId)) { return; } cell.addImage({ idMap, image: image }); const colorId = cell.getColorId(imageId, this._cameraVisualizationMode); let color = this._cameraOverrideColors.has(idMap.clusterId) ? this._cameraOverrideColors.get(idMap.clusterId) : this._assets.getColor(colorId); const visible = this._filter(image); cell.visualize({ id: imageId, color, positionMode: this._positionMode, scale: this._cameraSize, transform, visible, maxSize: ORIGINAL_CAMERA_SIZE, originalPosition }); if (!this._clusterCellMap.has(idMap.clusterId)) { this._clusterCellMap.set(idMap.clusterId, new Set()); } const clusterCells = this._clusterCellMap.get(idMap.clusterId); if (!clusterCells.has(cellId)) { clusterCells.add(cellId); } this._imageCellMap.set(imageId, cellId); if (imageId === this._selectedId) { this._highlight(imageId, this._colors.select, this._cameraVisualizationMode); } if (idMap.clusterId in this._clusters) { const clusterVisible = this._getClusterVisible(idMap.clusterId); this._clusters[idMap.clusterId].points.visible = clusterVisible; } this._needsRender = true; } addCell(vertices, cellId) { if (this.hasCell(cellId)) { return; } const cell = new CellLine(vertices); this._cells[cellId] = cell; this._cells[cellId].visible = this._cellsVisible; this._scene.add(this._cells[cellId]); this._needsRender = true; } deactivate() { this._filter = () => true; this._selectedId = null; this._hoveredId = null; this.uncache(); } getCameraOverrideColor(clusterId) { return this._cameraOverrideColors.get(clusterId); } getPointOverrideColor(clusterId) { return this._pointOverrideColors.get(clusterId); } hasCell(cellId) { return cellId in this._cells; } hasCluster(clusterId, cellId) { return clusterId in this._clusters && this._clusters[clusterId].cellIds.indexOf(cellId) !== -1; } hasImage(imageId, cellId) { return cellId in this._images && this._images[cellId].hasImage(imageId); } render(camera, renderer) { renderer.render(this._scene, camera); this._needsRender = false; } resetReference(reference, prevReference) { const clusters = this._clusters; for (const clusterId in clusters) { if (!clusters.hasOwnProperty(clusterId)) { continue; } const cluster = clusters[clusterId]; cluster.points.position.fromArray(resetEnu(reference, cluster.points.position.toArray(), prevReference)); cluster.points.update(); } const cells = this._cells; for (const cellId in cells) { if (!cells.hasOwnProperty(cellId)) { continue; } const cell = cells[cellId]; cell.position.clone(); cell.position.fromArray(resetEnu(reference, cell.position.toArray(), prevReference)); cell.update(); } const images = this._images; for (const cellId in images) { if (!images.hasOwnProperty(cellId)) { continue; } const spatialCell = images[cellId]; spatialCell.resetReference(reference, prevReference); } } setCameraOverrideColor(clusterId, color) { if (color != null) { this._cameraOverrideColors.set(clusterId, color); } else { this._cameraOverrideColors.delete(clusterId); } if (!this._clusterCellMap.has(clusterId)) { return; } const cellIds = this._clusterCellMap.get(clusterId); this._applyCameraColor([...cellIds.keys()]); this._needsRender = true; } setCameraSize(cameraSize) { if (Math.abs(cameraSize - this._cameraSize) < 1e-4) { return; } const imageCells = this._images; for (const cellId of Object.keys(imageCells)) { imageCells[cellId].applyCameraSize(cameraSize); } this._intersection.raycaster.near = this._getNear(cameraSize); this._intersection.setIntersectionThreshold(cameraSize); this._cameraSize = cameraSize; this._needsRender = true; } setCameraVisualizationMode(mode) { if (mode === this._cameraVisualizationMode) { return; } this._cameraVisualizationMode = mode; this._applyCameraColor(Object.keys(this._images)); this._needsRender = true; } setCellVisibility(visible) { if (visible === this._cellsVisible) { return; } for (const cellId in this._cells) { if (!this._cells.hasOwnProperty(cellId)) { continue; } this._cells[cellId].visible = visible; } this._cellsVisible = visible; this._needsRender = true; } setFilter(filter) { this._filter = filter; const clusterVisibles = {}; for (const imageCell of Object.values(this._images)) { imageCell.applyFilter(filter); const imageCV = imageCell.clusterVisibles; for (const clusterId in imageCV) { if (!imageCV.hasOwnProperty(clusterId)) { continue; } if (!(clusterId in clusterVisibles)) { clusterVisibles[clusterId] = false; } clusterVisibles[clusterId] || (clusterVisibles[clusterId] = imageCV[clusterId]); } } const pointsVisible = this._pointVisualizationMode !== exports.PointVisualizationMode.Hidden; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; } clusterVisibles[clusterId] && (clusterVisibles[clusterId] = pointsVisible); const visible = clusterVisibles[clusterId]; if (clusterId in this._clusters) { this._clusters[clusterId].points.visible = visible; } } this._needsRender = true; } setHoveredImage(imageId) { if (imageId != null && !this._imageCellMap.has(imageId)) { throw new MapillaryError(`Image does not exist: ${imageId}`); } if (this._hoveredId === imageId) { return; } this._needsRender = true; if (this._hoveredId != null) { if (this._hoveredId === this._selectedId) { this._highlight(this._hoveredId, this._colors.select, this._cameraVisualizationMode); } else { this._resetCameraColor(this._hoveredId); } } this._highlight(imageId, this._colors.hover, this._cameraVisualizationMode); this._hoveredId = imageId; } setPointOverrideColor(clusterId, color) { if (color != null) { this._pointOverrideColors.set(clusterId, color); } else { this._pointOverrideColors.delete(clusterId); } this._applyPointColor(clusterId); this._needsRender = true; } setPointSize(pointSize) { if (Math.abs(pointSize - this._pointSize) < 1e-4) { return; } const clusters = this._clusters; for (const key in clusters) { if (!clusters.hasOwnProperty(key)) { continue; } clusters[key].points.resize(pointSize); } this._pointSize = pointSize; this._needsRender = true; } setPointVisualizationMode(mode) { if (mode === this._pointVisualizationMode) { return; } this._pointVisualizationMode = mode; for (const clusterId in this._clusters) { if (!this._clusters.hasOwnProperty(clusterId)) { continue; } this._applyPointColor(clusterId); } this._needsRender = true; } setPositionMode(mode) { if (mode === this._positionMode) { return; } for (const cell of Object.values(this._images)) { cell.applyPositionMode(mode); } this._positionMode = mode; this._needsRender = true; } setSelectedImage(id) { if (this._selectedId === id) { return; } if (this._selectedId != null) { this._resetCameraColor(this._selectedId); } this._highlight(id, this._colors.select, this._cameraVisualizationMode); this._selectedId = id; this._needsRender = true; } uncache(keepCellIds) { for (const cellId of Object.keys(this._cellClusters)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._disposeReconstruction(cellId); } for (const cellId of Object.keys(this._images)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } const nceMap = this._imageCellMap; const keys = this._images[cellId].keys; for (const key of keys) { nceMap.delete(key); } this._images[cellId].dispose(); delete this._images[cellId]; } for (const cellId of Object.keys(this._cells)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } this._disposeCell(cellId); } this._needsRender = true; } _applyCameraColor(cellIds) { const mode = this._cameraVisualizationMode; const visible = isModeVisible(mode); const assets = this._assets; const overrides = this._cameraOverrideColors; const images = this._images; for (const cellId of cellIds) { if (!(cellId in images)) { continue; } const cell = images[cellId]; cell.cameras.visible = visible; const cameraMap = cell.getCamerasByMode(mode); cameraMap.forEach((cameras, colorId) => { let color = assets.getColor(colorId); for (const camera of cameras) { if (overrides.has(camera.clusterId)) { camera.camera.setColor(overrides.get(camera.clusterId)); } else { camera.camera.setColor(color); } } }); } this._highlight(this._hoveredId, this._colors.hover, mode); this._highlight(this._selectedId, this._colors.select, mode); } _applyPointColor(clusterId) { if (!(clusterId in this._clusters)) { return; } const cluster = this._clusters[clusterId]; cluster.points.visible = this._getClusterVisible(clusterId); const color = this._getPointColor(clusterId); cluster.points.setColor(color); } _getClusterVisible(clusterId) { if (this._pointVisualizationMode === exports.PointVisualizationMode.Hidden) { return false; } let visible = false; for (const imageCell of Object.values(this._images)) { const imageCV = imageCell.clusterVisibles; if (!(clusterId in imageCV)) { continue; } visible || (visible = imageCV[clusterId]); } return visible; } _disposeCell(cellId) { const cell = this._cells[cellId]; for (const line of cell.children.slice()) { line.dispose(); cell.remove(line); } this._scene.remove(cell); delete this._cells[cellId]; } _disposePoints(cellId) { for (const clusterId of this._cellClusters[cellId].keys) { if (!(clusterId in this._clusters)) { continue; } const index = this._clusters[clusterId].cellIds.indexOf(cellId); if (index === -1) { continue; } const cluster = this._clusters[clusterId]; cluster.cellIds.splice(index, 1); if (cluster.cellIds.length > 0) { continue; } this._scene.remove(cluster.points); cluster.points.dispose(); delete this._clusters[clusterId]; } } _disposeReconstruction(cellId) { this._disposePoints(cellId); delete this._cellClusters[cellId]; } _getNear(cameraSize) { const near = RAY_NEAR_SCALE * ORIGINAL_CAMERA_SIZE * cameraSize; return Math.max(0.01, near); } _getPointColor(clusterId) { let color = null; if (this._pointVisualizationMode === exports.PointVisualizationMode.Cluster) { color = this._assets.getColor(clusterId); } if (this._pointOverrideColors.has(clusterId)) { color = this._pointOverrideColors.get(clusterId); } return color; } _highlight(imageId, color, mode) { const nceMap = this._imageCellMap; if (imageId == null || !nceMap.has(imageId)) { return; } const cellId = nceMap.get(imageId); const cell = this._images[cellId]; const clusterId = cell.getCluster(imageId); const overridden = this._cameraOverrideColors.get(clusterId); color = mode === exports.CameraVisualizationMode.Homogeneous && !overridden ? color : SPATIAL_DEFAULT_COLOR; this._images[cellId].applyCameraColor(imageId, color); } _resetCameraColor(imageId) { const nceMap = this._imageCellMap; if (imageId == null || !nceMap.has(imageId)) { return; } const cellId = nceMap.get(imageId); const cell = this._images[cellId]; const colorId = cell.getColorId(imageId, this._cameraVisualizationMode); let color = this._assets.getColor(colorId); const clusterId = cell.getCluster(imageId); if (this._cameraOverrideColors.has(clusterId)) { color = this._cameraOverrideColors.get(clusterId); } cell.applyCameraColor(imageId, color); } } class SpatialCache { constructor(graphService, api) { this._graphService = graphService; this._api = api; this._cells = {}; this._cacheRequests = {}; this._clusters = {}; this._clusterCells = {}; this._cellClusters = {}; this._cachingCells$ = {}; this._cachingClusters$ = {}; } cacheClusters$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot cache reconstructions of a non-existing cell."); } if (this.hasClusters(cellId)) { throw new Error("Cannot cache reconstructions that already exists."); } if (this.isCachingClusters(cellId)) { return this._cachingClusters$[cellId]; } const duplicatedClusters = this.getCell(cellId) .filter((n) => { return !!n.clusterId && !!n.clusterUrl; }) .map((n) => { return { key: n.clusterId, url: n.clusterUrl }; }); const clusters = Array .from(new Map(duplicatedClusters.map((cd) => { return [cd.key, cd]; })) .values()); this._cellClusters[cellId] = clusters; this._cacheRequests[cellId] = []; let aborter; const abort = new Promise((_, reject) => { aborter = reject; }); this._cacheRequests[cellId].push(aborter); this._cachingClusters$[cellId] = this._cacheClusters$(clusters, cellId, abort).pipe(finalize(() => { if (cellId in this._cachingClusters$) { delete this._cachingClusters$[cellId]; } if (cellId in this._cacheRequests) { delete this._cacheRequests[cellId]; } }), publish(), refCount()); return this._cachingClusters$[cellId]; } cacheCell$(cellId) { if (this.hasCell(cellId)) { throw new Error("Cannot cache cell that already exists."); } if (this.isCachingCell(cellId)) { return this._cachingCells$[cellId]; } this._cachingCells$[cellId] = this._graphService.cacheCell$(cellId).pipe(catchError((error) => { console.error(error); return empty(); }), filter(() => { return !(cellId in this._cells); }), tap((images) => { this._cells[cellId] = []; this._cells[cellId].push(...images); delete this._cachingCells$[cellId]; }), finalize(() => { if (cellId in this._cachingCells$) { delete this._cachingCells$[cellId]; } }), publish(), refCount()); return this._cachingCells$[cellId]; } isCachingClusters(cellId) { return cellId in this._cachingClusters$; } isCachingCell(cellId) { return cellId in this._cachingCells$; } hasClusters(cellId) { if (cellId in this._cachingClusters$ || !(cellId in this._cellClusters)) { return false; } for (const cd of this._cellClusters[cellId]) { if (!(cd.key in this._clusters)) { return false; } } return true; } hasCell(cellId) { return !(cellId in this._cachingCells$) && cellId in this._cells; } getClusters(cellId) { return cellId in this._cellClusters ? this._cellClusters[cellId] .map((cd) => { return this._clusters[cd.key]; }) .filter((reconstruction) => { return !!reconstruction; }) : []; } getCell(cellId) { return cellId in this._cells ? this._cells[cellId] : []; } uncache(keepCellIds) { for (let cellId of Object.keys(this._cacheRequests)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } for (const aborter of this._cacheRequests[cellId]) { aborter(); } delete this._cacheRequests[cellId]; } for (let cellId of Object.keys(this._cellClusters)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } for (const cd of this._cellClusters[cellId]) { if (!(cd.key in this._clusterCells)) { continue; } const index = this._clusterCells[cd.key].indexOf(cellId); if (index === -1) { continue; } this._clusterCells[cd.key].splice(index, 1); if (this._clusterCells[cd.key].length > 0) { continue; } delete this._clusterCells[cd.key]; delete this._clusters[cd.key]; } delete this._cellClusters[cellId]; } for (let cellId of Object.keys(this._cells)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { continue; } delete this._cells[cellId]; } } updateCell$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot update cell that does not exists."); } return this._graphService.cacheCell$(cellId).pipe(catchError((error) => { console.error(error); return empty(); }), filter(() => { return cellId in this._cells; }), tap((images) => { this._cells[cellId] = []; this._cells[cellId].push(...images); }), publish(), refCount()); } updateClusters$(cellId) { if (!this.hasCell(cellId)) { throw new Error("Cannot update reconstructions of a non-existing cell."); } if (!this.hasClusters(cellId)) { throw new Error("Cannot update reconstructions for cell that is not cached."); } const duplicatedClusters = this.getCell(cellId) .filter((n) => { return !!n.clusterId && !!n.clusterUrl; }) .map((n) => { return { key: n.clusterId, url: n.clusterUrl }; }); const clusters = Array .from(new Map(duplicatedClusters.map((cd) => { return [cd.key, cd]; })) .values()) .filter(cd => { return !(cd.key in this._clusters); }); this._cellClusters[cellId].push(...clusters); return this._cacheClusters$(clusters, cellId, null); } _cacheClusters$(clusters, cellId, cancellation) { return from(clusters).pipe(mergeMap((cd) => { if (this._hasCluster(cd.key)) { return of(this._getCluster(cd.key)); } return this._getCluster$(cd.url, cd.key, cancellation) .pipe(catchError((error) => { if (error instanceof CancelMapillaryError) { return empty(); } console.error(error); return empty(); })); }, 6), filter(() => { return cellId in this._cellClusters; }), tap((reconstruction) => { if (!this._hasCluster(reconstruction.id)) { this._clusters[reconstruction.id] = reconstruction; } if (!(reconstruction.id in this._clusterCells)) { this._clusterCells[reconstruction.id] = []; } if (this._clusterCells[reconstruction.id].indexOf(cellId) === -1) { this._clusterCells[reconstruction.id].push(cellId); } })); } _getCluster(id) { return this._clusters[id]; } _getCluster$(url, clusterId, abort) { return Observable.create((subscriber) => { this._api.data.getCluster(url, abort) .then((reconstruction) => { reconstruction.id = clusterId; subscriber.next(reconstruction); subscriber.complete(); }, (error) => { subscriber.error(error); }); }); } _hasCluster(id) { return id in this._clusters; } } function connectedComponent(cellId, depth, geometry) { const cells = new Set(); cells.add(cellId); connectedComponentRecursive(cells, [cellId], 0, depth, geometry); return Array.from(cells); } function connectedComponentRecursive(cells, current, currentDepth, maxDepth, geometry) { if (currentDepth >= maxDepth) { return; } const adjacent = []; for (const cellId of current) { const aCells = geometry.getAdjacent(cellId); adjacent.push(...aCells); } const newCells = []; for (const a of adjacent) { if (cells.has(a)) { continue; } cells.add(a); newCells.push(a); } connectedComponentRecursive(cells, newCells, currentDepth + 1, maxDepth, geometry); } class SpatialComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._cache = new SpatialCache(navigator.graphService, navigator.api); this._scene = new SpatialScene(this._getDefaultConfiguration()); this._viewportCoords = new ViewportCoords(); this._spatial = new Spatial(); } /** * Get the currently set camera frame override color, or null if * no color is set. * * @param {string} clusterId - Id of the cluster. * * @returns {string | number | null} The current override color * for the cluster. */ getCameraOverrideColor(clusterId) { return this._scene.getCameraOverrideColor(clusterId); } /** * Returns the image id of the camera frame closest to the current * render camera position at the specified point. * * @description Notice that the pixelPoint argument requires x, y * coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the spatial component. * * If no camera frame exist at the pixel * point, `null` will be returned. * * @param {Array} pixelPoint - Pixel coordinates on * the viewer element. * @returns {string} Image id of the camera frame closest to * the camera. If no camera frame is intersected at the * pixel point, `null` will be returned. * * @example * ```js * spatialComponent.getFrameIdAt([100, 125]) * .then((imageId) => { console.log(imageId); }); * ``` */ getFrameIdAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const id = this._scene.intersection .intersectObjects(viewport, render.perspective); return id; })) .subscribe((id) => { resolve(id); }, (error) => { reject(error); }); }); } /** * Get the currently set point override color, or null if * no color is set. * * @param {string} clusterId - Id of the cluster. * * @returns {string | number | null} The current override color * for the cluster. */ getPointOverrideColor(clusterId) { return this._scene.getPointOverrideColor(clusterId); } /** * Override the camera color for a cluster. * * @description The configured color is applied for all visible * visualization modes, overriding the color of the currently * selected mode. * * @param {string} clusterId - Id of the cluster to configure. * @param {number | string} color - The color to paint the cameras with. * * @example * ```js * spatialComponent.setCameraOverrideColor("my-cluster-id", 0x00ff00); * ``` */ setCameraOverrideColor(clusterId, color) { this._scene.setCameraOverrideColor(clusterId, color); } /** * Override the point color for a cluster. * * @description The configured color is applied for all visible * visualization modes, overriding the color of the currently * selected mode. * * @param {string} clusterId - Id of the cluster to configure. * @param {number | string} color - The color to paint the points with. * * @example * ```js * spatialComponent.setPointOverrideColor("my-cluster-id", 0x00ff00); * ``` */ setPointOverrideColor(clusterId, color) { this._scene.setPointOverrideColor(clusterId, color); } _activate() { this._navigator.cacheService.configure({ cellDepth: 3 }); const subs = this._subscriptions; subs.push(this._configuration$ .pipe(map((c) => { var _a; c.cameraSize = this._spatial.clamp(c.cameraSize, MIN_CAMERA_SIZE, MAX_CAMERA_SIZE); c.pointSize = this._spatial.clamp(c.pointSize, MIN_POINT_SIZE, MAX_POINT_SIZE); c.cellGridDepth = this._spatial.clamp(c.cellGridDepth, 1, 3); const pointVisualizationMode = c.pointsVisible ? (_a = c.pointVisualizationMode) !== null && _a !== void 0 ? _a : exports.PointVisualizationMode.Original : exports.PointVisualizationMode.Hidden; return { cameraSize: c.cameraSize, cameraVisualizationMode: c.cameraVisualizationMode, cellsVisible: c.cellsVisible, originalPositionMode: c.originalPositionMode, pointSize: c.pointSize, pointVisualizationMode, }; }), distinctUntilChanged((c1, c2) => { return c1.cameraSize === c2.cameraSize && c1.cameraVisualizationMode === c2.cameraVisualizationMode && c1.cellsVisible === c2.cellsVisible && c1.originalPositionMode === c2.originalPositionMode && c1.pointSize === c2.pointSize && c1.pointVisualizationMode === c2.pointVisualizationMode; })) .subscribe((c) => { this._scene.setCameraSize(c.cameraSize); const cvm = c.cameraVisualizationMode; this._scene.setCameraVisualizationMode(cvm); this._scene.setCellVisibility(c.cellsVisible); this._scene.setPointSize(c.pointSize); const pvm = c.pointVisualizationMode; this._scene.setPointVisualizationMode(pvm); const opm = c.originalPositionMode; this._scene.setPositionMode(opm); })); subs.push(this._navigator.graphService.dataReset$ .subscribe(() => { this._cache.uncache(); this._scene.uncache(); })); subs.push(this._navigator.stateService.reference$ .pipe(pairwise()) .subscribe(([prevReference, reference]) => { this._scene.resetReference(reference, prevReference); })); subs.push(this._navigator.graphService.filter$ .subscribe(imageFilter => { this._scene.setFilter(imageFilter); })); const bearing$ = this._container.renderService.bearing$.pipe(map((bearing) => { const interval = 6; const discrete = interval * Math.floor(bearing / interval); return discrete; }), distinctUntilChanged(), publishReplay(1), refCount()); const currentImage$ = this._navigator.stateService.currentImage$; const cellId$ = currentImage$ .pipe(map((image) => { return this._navigator.api.data.geometry .lngLatToCellId(image.originalLngLat); }), distinctUntilChanged(), publishReplay(1), refCount()); const cellGridDepth$ = this._configuration$ .pipe(map((c) => { return c.cellGridDepth; }), distinctUntilChanged(), publishReplay(1), refCount()); const sequencePlay$ = combineLatest(this._navigator.playService.playing$, this._navigator.playService.speed$).pipe(map(([playing, speed]) => { return playing && speed > PlayService.sequenceSpeed; }), distinctUntilChanged(), publishReplay(1), refCount()); const isOverview$ = this._navigator.stateService.state$.pipe(map((state) => { return isOverviewState(state); }), distinctUntilChanged(), publishReplay(1), refCount()); const cell$ = combineLatest(isOverview$, sequencePlay$, bearing$, cellGridDepth$, currentImage$) .pipe(distinctUntilChanged(([o1, s1, b1, d1, i1], [o2, s2, b2, d2, i2]) => { if (!i1.assetsCached) { return false; } if (o1 !== o2) { return false; } const isd = i1.id === i2.id && s1 === s2 && d1 === d2; if (o1) { return isd; } return isd && b1 === b2; }), concatMap(([isOverview, sequencePlay, bearing, depth, image]) => { if (isOverview) { const geometry = this._navigator.api.data.geometry; const cellId = geometry .lngLatToCellId(image.originalLngLat); const cells = sequencePlay ? [cellId] : connectedComponent(cellId, depth, geometry); return of(cells); } const fov = sequencePlay ? 30 : 90; return of(this._cellsInFov(image, bearing, fov)); }), switchMap((cellIds) => { return from(cellIds).pipe(mergeMap((cellId) => { const t$ = this._cache.hasCell(cellId) ? of(this._cache.getCell(cellId)) : this._cache.cacheCell$(cellId); return t$.pipe(map((images) => ({ id: cellId, images }))); }, 6)); })); subs.push(cell$.pipe(withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([cell, reference]) => { if (this._scene.hasCell(cell.id)) { return; } this._scene.addCell(this._cellToTopocentric(cell.id, reference), cell.id); })); subs.push(cell$.pipe(withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([cell, reference]) => { this._addSceneImages(cell, reference); })); subs.push(cell$.pipe(concatMap((cell) => { const cellId = cell.id; let reconstructions$; if (this._cache.hasClusters(cellId)) { reconstructions$ = from(this._cache.getClusters(cellId)); } else if (this._cache.isCachingClusters(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId).pipe(last(null, {}), switchMap(() => { return from(this._cache.getClusters(cellId)); })); } else if (this._cache.hasCell(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId); } else { reconstructions$ = empty(); } return combineLatest(of(cellId), reconstructions$); }), withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([[cellId, reconstruction], reference]) => { if (this._scene .hasCluster(reconstruction.id, cellId)) { return; } this._scene.addCluster(reconstruction, this._computeTranslation(reconstruction, reference), cellId); })); subs.push(combineLatest(cellId$, cellGridDepth$) .subscribe(([cellId, depth]) => { const keepCells = connectedComponent(cellId, depth, this._navigator.api.data.geometry); this._scene.uncache(keepCells); this._cache.uncache(keepCells); })); subs.push(this._navigator.playService.playing$.pipe(switchMap((playing) => { return playing ? empty() : this._container.mouseService.dblClick$; }), withLatestFrom(this._container.renderService.renderCamera$), switchMap(([event, render]) => { const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords .canvasPosition(event, element); const viewport = this._viewportCoords.canvasToViewport(canvasX, canvasY, element); const id = this._scene.intersection .intersectObjects(viewport, render.perspective); return !!id ? this._navigator.moveTo$(id).pipe(catchError(() => { return empty(); })) : empty(); })) .subscribe()); const intersectChange$ = combineLatest(this._configuration$, this._navigator.stateService.state$).pipe(map(([c, state]) => { return { size: c.cameraSize, visible: isModeVisible(c.cameraVisualizationMode), state, }; }), distinctUntilChanged((c1, c2) => { return c1.size === c2.size && c1.visible === c2.visible && c1.state === c2.state; })); const mouseMove$ = this._container.mouseService.mouseMove$.pipe(publishReplay(1), refCount()); subs.push(mouseMove$.subscribe()); const mouseHover$ = merge(this._container.mouseService.mouseEnter$, this._container.mouseService.mouseLeave$, this._container.mouseService.windowBlur$); subs.push(combineLatest(this._navigator.playService.playing$, mouseHover$, isOverview$, this._navigator.graphService.filter$) .pipe(switchMap(([playing, mouseHover]) => { return !playing && mouseHover.type === "pointerenter" ? combineLatest(concat(mouseMove$.pipe(take(1)), this._container.mouseService.mouseMove$), this._container.renderService.renderCamera$, intersectChange$) : combineLatest(of(mouseHover), of(null), of(null)); })) .subscribe(([event, render]) => { if (event.type !== "pointermove") { this._scene.setHoveredImage(null); return; } const element = this._container.container; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const viewport = this._viewportCoords.canvasToViewport(canvasX, canvasY, element); const key = this._scene.intersection .intersectObjects(viewport, render.perspective); this._scene.setHoveredImage(key); })); subs.push(this._navigator.stateService.currentId$ .subscribe((id) => { this._scene.setSelectedImage(id); })); subs.push(this._navigator.stateService.currentState$ .pipe(map((frame) => { const scene = this._scene; return { name: this._name, renderer: { frameId: frame.id, needsRender: scene.needsRender, render: scene.render.bind(scene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); const updatedCell$ = this._navigator.graphService.dataAdded$ .pipe(filter((cellId) => { return this._cache.hasCell(cellId); }), mergeMap((cellId) => { return this._cache.updateCell$(cellId).pipe(map((images) => ({ id: cellId, images })), withLatestFrom(this._navigator.stateService.reference$)); }), publish(), refCount()); subs.push(updatedCell$ .subscribe(([cell, reference]) => { this._addSceneImages(cell, reference); })); subs.push(updatedCell$ .pipe(concatMap(([cell]) => { const cellId = cell.id; const cache = this._cache; let reconstructions$; if (cache.hasClusters(cellId)) { reconstructions$ = cache.updateClusters$(cellId); } else if (cache.isCachingClusters(cellId)) { reconstructions$ = this._cache.cacheClusters$(cellId).pipe(last(null, {}), switchMap(() => { return from(cache.updateClusters$(cellId)); })); } else { reconstructions$ = empty(); } return combineLatest(of(cellId), reconstructions$); }), withLatestFrom(this._navigator.stateService.reference$)) .subscribe(([[cellId, reconstruction], reference]) => { if (this._scene.hasCluster(reconstruction.id, cellId)) { return; } this._scene.addCluster(reconstruction, this._computeTranslation(reconstruction, reference), cellId); })); } _deactivate() { this._subscriptions.unsubscribe(); this._cache.uncache(); this._scene.deactivate(); this._navigator.cacheService.configure(); } _getDefaultConfiguration() { return { cameraSize: 0.1, cameraVisualizationMode: exports.CameraVisualizationMode.Homogeneous, cellGridDepth: 1, originalPositionMode: exports.OriginalPositionMode.Hidden, pointSize: 0.05, pointsVisible: true, pointVisualizationMode: exports.PointVisualizationMode.Original, cellsVisible: false, }; } _addSceneImages(cell, reference) { const cellId = cell.id; const images = cell.images; for (const image of images) { if (this._scene.hasImage(image.id, cellId)) { continue; } this._scene.addImage(image, this._createTransform(image, reference), this._computeOriginalPosition(image, reference), cellId); } } _cellsInFov(image, bearing, fov) { const spatial = this._spatial; const geometry = this._navigator.api.data.geometry; const cell = geometry.lngLatToCellId(image.originalLngLat); const cells = [cell]; const threshold = fov / 2; const adjacent = geometry.getAdjacent(cell); for (const a of adjacent) { const vertices = geometry.getVertices(a); for (const vertex of vertices) { const [x, y] = geodeticToEnu(vertex.lng, vertex.lat, 0, image.lngLat.lng, image.lngLat.lat, 0); const azimuthal = Math.atan2(y, x); const vertexBearing = spatial.radToDeg(spatial.azimuthalToBearing(azimuthal)); if (Math.abs(vertexBearing - bearing) < threshold) { cells.push(a); } } } return cells; } _computeOriginalPosition(image, reference) { return geodeticToEnu(image.originalLngLat.lng, image.originalLngLat.lat, image.originalAltitude != null ? image.originalAltitude : image.computedAltitude, reference.lng, reference.lat, reference.alt); } _cellToTopocentric(cellId, reference) { const vertices = this._navigator.api.data.geometry .getVertices(cellId) .map((vertex) => { return geodeticToEnu(vertex.lng, vertex.lat, -2, reference.lng, reference.lat, reference.alt); }); return vertices; } _computeTranslation(reconstruction, reference) { return geodeticToEnu(reconstruction.reference.lng, reconstruction.reference.lat, reconstruction.reference.alt, reference.lng, reference.lat, reference.alt); } _createTransform(image, reference) { const translation = computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); const transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, undefined, image.assetsCached ? image.camera : this._navigator.projectionService.makeCamera(image.cameraType, image.cameraParameters)); return transform; } } SpatialComponent.componentName = "spatial"; /** * @class Geometry * @abstract * @classdesc Represents a geometry. */ class Geometry { /** * Create a geometry. * * @constructor * @ignore */ constructor() { this._notifyChanged$ = new Subject(); } /** * Get changed observable. * * @description Emits the geometry itself every time the geometry * has changed. * * @returns {Observable} Observable emitting the geometry instance. * @ignore */ get changed$() { return this._notifyChanged$; } } class GeometryTagError extends MapillaryError { constructor(message) { super(message != null ? message : "The provided geometry value is incorrect"); Object.setPrototypeOf(this, GeometryTagError.prototype); this.name = "GeometryTagError"; } } /** * @class PointsGeometry * * @classdesc Represents a point set in the 2D basic image coordinate system. * * @example * ```js * var points = [[0.5, 0.3], [0.7, 0.3], [0.6, 0.5]]; * var pointsGeometry = new PointsGeometry(points); * ``` */ class PointsGeometry extends Geometry { /** * Create a points geometry. * * @constructor * @param {Array>} points - Array of 2D points on the basic coordinate * system. The number of points must be greater than or equal to two. * * @throws {GeometryTagError} Point coordinates must be valid basic coordinates. */ constructor(points) { super(); const pointsLength = points.length; if (pointsLength < 2) { throw new GeometryTagError("A points geometry must have two or more positions."); } this._points = []; for (const point of points) { if (point[0] < 0 || point[0] > 1 || point[1] < 0 || point[1] > 1) { throw new GeometryTagError("Basic coordinates of points must be on the interval [0, 1]."); } this._points.push(point.slice()); } } /** * Get points property. * @returns {Array>} Array of 2d points. */ get points() { return this._points; } /** * Add a point to the point set. * * @param {Array} point - Point to add. * @ignore */ addPoint2d(point) { const clamped = [ Math.max(0, Math.min(1, point[0])), Math.max(0, Math.min(1, point[1])), ]; this._points.push(clamped); this._notifyChanged$.next(this); } /** * Get the coordinates of a point from the point set representation of the geometry. * * @param {number} index - Point index. * @returns {Array} Array representing the 2D basic coordinates of the point. * @ignore */ getPoint2d(index) { return this._points[index].slice(); } /** * Remove a point from the point set. * * @param {number} index - The index of the point to remove. * @ignore */ removePoint2d(index) { if (index < 0 || index >= this._points.length || this._points.length < 3) { throw new GeometryTagError("Index for removed point must be valid."); } this._points.splice(index, 1); this._notifyChanged$.next(this); } /** @ignore */ setVertex2d(index, value, transform) { this.setPoint2d(index, value, transform); } /** @ignore */ setPoint2d(index, value, transform) { const changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; this._points[index] = changed; this._notifyChanged$.next(this); } /** @ignore */ getPoints3d(transform) { return this._getPoints3d(this._points, transform); } /** @ignore */ getPoint3d(index, transform) { return transform.unprojectBasic(this._points[index], 200); } /** @ignore */ getPoints2d() { return this._points.slice(); } /** @ignore */ getCentroid2d(transform) { if (!transform) { throw new GeometryTagError("Get centroid must be called with a transform for points geometries."); } const [minX, minY, maxX, maxY] = this.getRect2d(transform); const centroidX = minX < maxX ? (minX + maxX) / 2 : ((minX + maxX + 1) / 2) % 1; const centroidY = (minY + maxY) / 2; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { let centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** @ignore */ getRect2d(transform) { let minX = 1; let maxX = 0; let minY = 1; let maxY = 0; const points = this._points; for (const point of points) { if (point[0] < minX) { minX = point[0]; } if (point[0] > maxX) { maxX = point[0]; } if (point[1] < minY) { minY = point[1]; } if (point[1] > maxY) { maxY = point[1]; } } if (isSpherical(transform.cameraType)) { const indices = []; for (let i = 0; i < points.length; i++) { indices[i] = i; } indices.sort((a, b) => { return points[a][0] < points[b][0] ? -1 : points[a][0] > points[b][0] ? 1 : a < b ? -1 : 1; }); let maxDistanceX = points[indices[0]][0] + 1 - points[indices[indices.length - 1]][0]; let leftMostIndex = 0; for (let i = 0; i < indices.length - 1; i++) { const index1 = indices[i]; const index2 = indices[i + 1]; const distanceX = points[index2][0] - points[index1][0]; if (distanceX > maxDistanceX) { maxDistanceX = distanceX; leftMostIndex = i + 1; } } if (leftMostIndex > 0) { minX = points[indices[leftMostIndex]][0]; maxX = points[indices[leftMostIndex - 1]][0]; } } return [minX, minY, maxX, maxY]; } /** @ignore */ setCentroid2d(value, transform) { throw new Error("Not implemented"); } _getPoints3d(points2d, transform) { return points2d .map((point) => { return transform.unprojectBasic(point, 200); }); } } class CreateTag { constructor(geometry, transform, viewportCoords) { this._geometry = geometry; this._transform = transform; this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._aborted$ = new Subject(); this._created$ = new Subject(); this._glObjectsChanged$ = new Subject(); this._geometryChangedSubscription = this._geometry.changed$ .subscribe(() => { this._onGeometryChanged(); this._glObjectsChanged$.next(this); }); } get geometry() { return this._geometry; } get glObjects() { return this._glObjects; } get aborted$() { return this._aborted$; } get created$() { return this._created$; } get glObjectsChanged$() { return this._glObjectsChanged$; } get geometryChanged$() { return this._geometry.changed$.pipe(map(() => { return this; })); } dispose() { this._geometryChangedSubscription.unsubscribe(); } _canvasToTransform(canvas) { const canvasX = Math.round(canvas[0]); const canvasY = Math.round(canvas[1]); const transform = `translate(-50%,-50%) translate(${canvasX}px,${canvasY}px)`; return transform; } _colorToBackground(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _createOutine(polygon3d, color) { const positions = this._getLinePositions(polygon3d); const geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ color: color, linewidth: 1, }); return new Line(geometry, material); } _disposeLine(line) { if (line == null) { return; } line.geometry.dispose(); line.material.dispose(); } _getLinePositions(polygon3d) { const length = polygon3d.length; const positions = new Float32Array(length * 3); for (let i = 0; i < length; ++i) { const index = 3 * i; const position = polygon3d[i]; positions[index] = position[0]; positions[index + 1] = position[1]; positions[index + 2] = position[2]; } return positions; } } var earcut$1 = {exports: {}}; earcut$1.exports = earcut; earcut$1.exports.default = earcut; function earcut(data, holeIndices, dim) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, outerNode = linkedList(data, 0, outerLen, dim, true), triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (var i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); return triangles; } // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { var i, last; if (clockwise === (signedArea$1(data, start, end, dim) > 0)) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals$1(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; var p = start, again; do { again = false; if (!p.steiner && (equals$1(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); var stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim | 0); triangles.push(ear.i / dim | 0); triangles.push(next.i / dim | 0); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn"t work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); var p = c.next; while (p !== a) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can"t be an ear var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); // z-order range for the current triangle bbox; var minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); var p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { var p = start; do { var a = p.prev, b = p.next.next; if (!equals$1(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim | 0); triangles.push(p.i / dim | 0); triangles.push(b.i / dim | 0); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal var c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize, 0); earcutLinked(c, triangles, dim, minX, minY, invSize, 0); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { var queue = [], i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { outerNode = eliminateHole(queue[i], outerNode); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { var bridge = findHoleBridge(hole, outerNode); if (!bridge) { return outerNode; } var bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts filterPoints(bridgeReverse, bridgeReverse.next); return filterPoints(bridge, bridge.next); } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { var p = outerNode, hx = hole.x, hy = hole.y, qx = -Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while (p !== outerNode); if (!m) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { var p = start; do { if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || (qSize > 0 && q)) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = (x - minX) * invSize | 0; y = (y - minY) * invSize | 0; x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; y = (y | (y << 8)) & 0x00FF00FF; y = (y | (y << 4)) & 0x0F0F0F0F; y = (y | (y << 2)) & 0x33333333; y = (y | (y << 1)) & 0x55555555; return x | (y << 1); } // find the leftmost node of a polygon ring function getLeftmost(start) { var p = start, leftmost = start; do { if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones"t intersect other edges (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals$1(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals$1(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { var o1 = sign(area(p1, q1, p2)); var o2 = sign(area(p1, q1, q2)); var o3 = sign(area(p2, q2, p1)); var o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { var p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { var p = a, inside = false, px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { var a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { var p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } // return a percentage difference between the polygon area and its triangulation area; // used to verify correctness of triangulation earcut.deviation = function (data, holeIndices, dim, triangles) { var hasHoles = holeIndices && holeIndices.length; var outerLen = hasHoles ? holeIndices[0] * dim : data.length; var polygonArea = Math.abs(signedArea$1(data, 0, outerLen, dim)); if (hasHoles) { for (var i = 0, len = holeIndices.length; i < len; i++) { var start = holeIndices[i] * dim; var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; polygonArea -= Math.abs(signedArea$1(data, start, end, dim)); } } var trianglesArea = 0; for (i = 0; i < triangles.length; i += 3) { var a = triangles[i] * dim; var b = triangles[i + 1] * dim; var c = triangles[i + 2] * dim; trianglesArea += Math.abs( (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1])); } return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea); }; function signedArea$1(data, start, end, dim) { var sum = 0; for (var i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts earcut.flatten = function (data) { var dim = data[0][0].length, result = {vertices: [], holes: [], dimensions: dim}, holeIndex = 0; for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j++) { for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); } if (i > 0) { holeIndex += data[i - 1].length; result.holes.push(holeIndex); } } return result; }; var polylabel$1 = {exports: {}}; class TinyQueue$1 { constructor(data = [], compare = defaultCompare$1) { this.data = data; this.length = this.data.length; this.compare = compare; if (this.length > 0) { for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i); } } push(item) { this.data.push(item); this.length++; this._up(this.length - 1); } pop() { if (this.length === 0) return undefined; const top = this.data[0]; const bottom = this.data.pop(); this.length--; if (this.length > 0) { this.data[0] = bottom; this._down(0); } return top; } peek() { return this.data[0]; } _up(pos) { const {data, compare} = this; const item = data[pos]; while (pos > 0) { const parent = (pos - 1) >> 1; const current = data[parent]; if (compare(item, current) >= 0) break; data[pos] = current; pos = parent; } data[pos] = item; } _down(pos) { const {data, compare} = this; const halfLength = this.length >> 1; const item = data[pos]; while (pos < halfLength) { let left = (pos << 1) + 1; let best = data[left]; const right = left + 1; if (right < this.length && compare(data[right], best) < 0) { left = right; best = data[right]; } if (compare(best, item) >= 0) break; data[pos] = best; pos = left; } data[pos] = item; } } function defaultCompare$1(a, b) { return a < b ? -1 : a > b ? 1 : 0; } var tinyqueue$1 = /*#__PURE__*/Object.freeze({ __proto__: null, "default": TinyQueue$1 }); var require$$0 = /*@__PURE__*/getAugmentedNamespace(tinyqueue$1); var Queue = require$$0; if (Queue.default) Queue = Queue.default; // temporary webpack fix polylabel$1.exports = polylabel; polylabel$1.exports.default = polylabel; function polylabel(polygon, precision, debug) { precision = precision || 1.0; // find the bounding box of the outer ring var minX, minY, maxX, maxY; for (var i = 0; i < polygon[0].length; i++) { var p = polygon[0][i]; if (!i || p[0] < minX) minX = p[0]; if (!i || p[1] < minY) minY = p[1]; if (!i || p[0] > maxX) maxX = p[0]; if (!i || p[1] > maxY) maxY = p[1]; } var width = maxX - minX; var height = maxY - minY; var cellSize = Math.min(width, height); var h = cellSize / 2; if (cellSize === 0) { var degeneratePoleOfInaccessibility = [minX, minY]; degeneratePoleOfInaccessibility.distance = 0; return degeneratePoleOfInaccessibility; } // a priority queue of cells in order of their "potential" (max distance to polygon) var cellQueue = new Queue(undefined, compareMax); // cover polygon with initial cells for (var x = minX; x < maxX; x += cellSize) { for (var y = minY; y < maxY; y += cellSize) { cellQueue.push(new Cell(x + h, y + h, h, polygon)); } } // take centroid as the first best guess var bestCell = getCentroidCell(polygon); // special case for rectangular polygons var bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, polygon); if (bboxCell.d > bestCell.d) bestCell = bboxCell; var numProbes = cellQueue.length; while (cellQueue.length) { // pick the most promising cell from the queue var cell = cellQueue.pop(); // update the best cell if we found a better one if (cell.d > bestCell.d) { bestCell = cell; if (debug) console.log("found best %d after %d probes", Math.round(1e4 * cell.d) / 1e4, numProbes); } // do not drill down further if there"s no chance of a better solution if (cell.max - bestCell.d <= precision) continue; // split the cell into four cells h = cell.h / 2; cellQueue.push(new Cell(cell.x - h, cell.y - h, h, polygon)); cellQueue.push(new Cell(cell.x + h, cell.y - h, h, polygon)); cellQueue.push(new Cell(cell.x - h, cell.y + h, h, polygon)); cellQueue.push(new Cell(cell.x + h, cell.y + h, h, polygon)); numProbes += 4; } if (debug) { console.log("num probes: " + numProbes); console.log("best distance: " + bestCell.d); } var poleOfInaccessibility = [bestCell.x, bestCell.y]; poleOfInaccessibility.distance = bestCell.d; return poleOfInaccessibility; } function compareMax(a, b) { return b.max - a.max; } function Cell(x, y, h, polygon) { this.x = x; // cell center x this.y = y; // cell center y this.h = h; // half the cell size this.d = pointToPolygonDist(x, y, polygon); // distance from cell center to polygon this.max = this.d + this.h * Math.SQRT2; // max distance to polygon within a cell } // signed distance from point to polygon outline (negative if point is outside) function pointToPolygonDist(x, y, polygon) { var inside = false; var minDistSq = Infinity; for (var k = 0; k < polygon.length; k++) { var ring = polygon[k]; for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) { var a = ring[i]; var b = ring[j]; if ((a[1] > y !== b[1] > y) && (x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0])) inside = !inside; minDistSq = Math.min(minDistSq, getSegDistSq(x, y, a, b)); } } return minDistSq === 0 ? 0 : (inside ? 1 : -1) * Math.sqrt(minDistSq); } // get polygon centroid function getCentroidCell(polygon) { var area = 0; var x = 0; var y = 0; var points = polygon[0]; for (var i = 0, len = points.length, j = len - 1; i < len; j = i++) { var a = points[i]; var b = points[j]; var f = a[0] * b[1] - b[0] * a[1]; x += (a[0] + b[0]) * f; y += (a[1] + b[1]) * f; area += f * 3; } if (area === 0) return new Cell(points[0][0], points[0][1], 0, polygon); return new Cell(x / area, y / area, 0, polygon); } // get squared distance from a point to a segment function getSegDistSq(px, py, a, b) { var x = a[0]; var y = a[1]; var dx = b[0] - x; var dy = b[1] - y; if (dx !== 0 || dy !== 0) { var t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); if (t > 1) { x = b[0]; y = b[1]; } else if (t > 0) { x += dx * t; y += dy * t; } } dx = px - x; dy = py - y; return dx * dx + dy * dy; } function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } class SplayTree { constructor(compare = DEFAULT_COMPARE, noDuplicates = false) { this._compare = compare; this._root = null; this._size = 0; this._noDuplicates = !!noDuplicates; } rotateLeft(x) { var y = x.right; if (y) { x.right = y.left; if (y.left) y.left.parent = x; y.parent = x.parent; } if (!x.parent) this._root = y; else if (x === x.parent.left) x.parent.left = y; else x.parent.right = y; if (y) y.left = x; x.parent = y; } rotateRight(x) { var y = x.left; if (y) { x.left = y.right; if (y.right) y.right.parent = x; y.parent = x.parent; } if (!x.parent) this._root = y; else if(x === x.parent.left) x.parent.left = y; else x.parent.right = y; if (y) y.right = x; x.parent = y; } _splay(x) { while (x.parent) { var p = x.parent; if (!p.parent) { if (p.left === x) this.rotateRight(p); else this.rotateLeft(p); } else if (p.left === x && p.parent.left === p) { this.rotateRight(p.parent); this.rotateRight(p); } else if (p.right === x && p.parent.right === p) { this.rotateLeft(p.parent); this.rotateLeft(p); } else if (p.left === x && p.parent.right === p) { this.rotateRight(p); this.rotateLeft(p); } else { this.rotateLeft(p); this.rotateRight(p); } } } splay(x) { var p, gp, ggp, l, r; while (x.parent) { p = x.parent; gp = p.parent; if (gp && gp.parent) { ggp = gp.parent; if (ggp.left === gp) ggp.left = x; else ggp.right = x; x.parent = ggp; } else { x.parent = null; this._root = x; } l = x.left; r = x.right; if (x === p.left) { // left if (gp) { if (gp.left === p) { /* zig-zig */ if (p.right) { gp.left = p.right; gp.left.parent = gp; } else gp.left = null; p.right = gp; gp.parent = p; } else { /* zig-zag */ if (l) { gp.right = l; l.parent = gp; } else gp.right = null; x.left = gp; gp.parent = x; } } if (r) { p.left = r; r.parent = p; } else p.left = null; x.right = p; p.parent = x; } else { // right if (gp) { if (gp.right === p) { /* zig-zig */ if (p.left) { gp.right = p.left; gp.right.parent = gp; } else gp.right = null; p.left = gp; gp.parent = p; } else { /* zig-zag */ if (r) { gp.left = r; r.parent = gp; } else gp.left = null; x.right = gp; gp.parent = x; } } if (l) { p.right = l; l.parent = p; } else p.right = null; x.left = p; p.parent = x; } } } replace(u, v) { if (!u.parent) this._root = v; else if (u === u.parent.left) u.parent.left = v; else u.parent.right = v; if (v) v.parent = u.parent; } minNode(u = this._root) { if (u) while (u.left) u = u.left; return u; } maxNode(u = this._root) { if (u) while (u.right) u = u.right; return u; } insert(key, data) { var z = this._root; var p = null; var comp = this._compare; var cmp; if (this._noDuplicates) { while (z) { p = z; cmp = comp(z.key, key); if (cmp === 0) return; else if (comp(z.key, key) < 0) z = z.right; else z = z.left; } } else { while (z) { p = z; if (comp(z.key, key) < 0) z = z.right; else z = z.left; } } z = { key, data, left: null, right: null, parent: p }; if (!p) this._root = z; else if (comp(p.key, z.key) < 0) p.right = z; else p.left = z; this.splay(z); this._size++; return z; } find (key) { var z = this._root; var comp = this._compare; while (z) { var cmp = comp(z.key, key); if (cmp < 0) z = z.right; else if (cmp > 0) z = z.left; else return z; } return null; } /** * Whether the tree contains a node with the given key * @param {Key} key * @return {boolean} true/false */ contains (key) { var node = this._root; var comparator = this._compare; while (node) { var cmp = comparator(key, node.key); if (cmp === 0) return true; else if (cmp < 0) node = node.left; else node = node.right; } return false; } remove (key) { var z = this.find(key); if (!z) return false; this.splay(z); if (!z.left) this.replace(z, z.right); else if (!z.right) this.replace(z, z.left); else { var y = this.minNode(z.right); if (y.parent !== z) { this.replace(y, y.right); y.right = z.right; y.right.parent = y; } this.replace(z, y); y.left = z.left; y.left.parent = y; } this._size--; return true; } removeNode(z) { if (!z) return false; this.splay(z); if (!z.left) this.replace(z, z.right); else if (!z.right) this.replace(z, z.left); else { var y = this.minNode(z.right); if (y.parent !== z) { this.replace(y, y.right); y.right = z.right; y.right.parent = y; } this.replace(z, y); y.left = z.left; y.left.parent = y; } this._size--; return true; } erase (key) { var z = this.find(key); if (!z) return; this.splay(z); var s = z.left; var t = z.right; var sMax = null; if (s) { s.parent = null; sMax = this.maxNode(s); this.splay(sMax); this._root = sMax; } if (t) { if (s) sMax.right = t; else this._root = t; t.parent = sMax; } this._size--; } /** * Removes and returns the node with smallest key * @return {?Node} */ pop () { var node = this._root, returnValue = null; if (node) { while (node.left) node = node.left; returnValue = { key: node.key, data: node.data }; this.remove(node.key); } return returnValue; } /* eslint-disable class-methods-use-this */ /** * Successor node * @param {Node} node * @return {?Node} */ next (node) { var successor = node; if (successor) { if (successor.right) { successor = successor.right; while (successor && successor.left) successor = successor.left; } else { successor = node.parent; while (successor && successor.right === node) { node = successor; successor = successor.parent; } } } return successor; } /** * Predecessor node * @param {Node} node * @return {?Node} */ prev (node) { var predecessor = node; if (predecessor) { if (predecessor.left) { predecessor = predecessor.left; while (predecessor && predecessor.right) predecessor = predecessor.right; } else { predecessor = node.parent; while (predecessor && predecessor.left === node) { node = predecessor; predecessor = predecessor.parent; } } } return predecessor; } /* eslint-enable class-methods-use-this */ /** * @param {forEachCallback} callback * @return {SplayTree} */ forEach(callback) { var current = this._root; var s = [], done = false, i = 0; while (!done) { // Reach the left most Node of the current Node if (current) { // Place pointer to a tree node on the stack // before traversing the node"s left subtree s.push(current); current = current.left; } else { // BackTrack from the empty subtree and visit the Node // at the top of the stack; however, if the stack is // empty you are done if (s.length > 0) { current = s.pop(); callback(current, i++); // We have visited the node and its left // subtree. Now, it"s right subtree"s turn current = current.right; } else done = true; } } return this; } /** * Walk key range from `low` to `high`. Stops if `fn` returns a value. * @param {Key} low * @param {Key} high * @param {Function} fn * @param {*?} ctx * @return {SplayTree} */ range(low, high, fn, ctx) { const Q = []; const compare = this._compare; let node = this._root, cmp; while (Q.length !== 0 || node) { if (node) { Q.push(node); node = node.left; } else { node = Q.pop(); cmp = compare(node.key, high); if (cmp > 0) { break; } else if (compare(node.key, low) >= 0) { if (fn.call(ctx, node)) return this; // stop if smth is returned } node = node.right; } } return this; } /** * Returns all keys in order * @return {Array} */ keys () { var current = this._root; var s = [], r = [], done = false; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); r.push(current.key); current = current.right; } else done = true; } } return r; } /** * Returns `data` fields of all nodes in order. * @return {Array} */ values () { var current = this._root; var s = [], r = [], done = false; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); r.push(current.data); current = current.right; } else done = true; } } return r; } /** * Returns node at given index * @param {number} index * @return {?Node} */ at (index) { // removed after a consideration, more misleading than useful // index = index % this.size; // if (index < 0) index = this.size - index; var current = this._root; var s = [], done = false, i = 0; while (!done) { if (current) { s.push(current); current = current.left; } else { if (s.length > 0) { current = s.pop(); if (i === index) return current; i++; current = current.right; } else done = true; } } return null; } /** * Bulk-load items. Both array have to be same size * @param {Array} keys * @param {Array} [values] * @param {Boolean} [presort=false] Pre-sort keys and values, using * tree"s comparator. Sorting is done * in-place * @return {AVLTree} */ load(keys = [], values = [], presort = false) { if (this._size !== 0) throw new Error("bulk-load: tree is not empty"); const size = keys.length; if (presort) sort(keys, values, 0, size - 1, this._compare); this._root = loadRecursive(null, keys, values, 0, size); this._size = size; return this; } min() { var node = this.minNode(this._root); if (node) return node.key; else return null; } max() { var node = this.maxNode(this._root); if (node) return node.key; else return null; } isEmpty() { return this._root === null; } get size() { return this._size; } /** * Create a tree and load it with items * @param {Array} keys * @param {Array?} [values] * @param {Function?} [comparator] * @param {Boolean?} [presort=false] Pre-sort keys and values, using * tree"s comparator. Sorting is done * in-place * @param {Boolean?} [noDuplicates=false] Allow duplicates * @return {SplayTree} */ static createTree(keys, values, comparator, presort, noDuplicates) { return new SplayTree(comparator, noDuplicates).load(keys, values, presort); } } function loadRecursive (parent, keys, values, start, end) { const size = end - start; if (size > 0) { const middle = start + Math.floor(size / 2); const key = keys[middle]; const data = values[middle]; const node = { key, data, parent }; node.left = loadRecursive(node, keys, values, start, middle); node.right = loadRecursive(node, keys, values, middle + 1, end); return node; } return null; } function sort(keys, values, left, right, compare) { if (left >= right) return; const pivot = keys[(left + right) >> 1]; let i = left - 1; let j = right + 1; while (true) { do i++; while (compare(keys[i], pivot) < 0); do j--; while (compare(keys[j], pivot) > 0); if (i >= j) break; let tmp = keys[i]; keys[i] = keys[j]; keys[j] = tmp; tmp = values[i]; values[i] = values[j]; values[j] = tmp; } sort(keys, values, left, j, compare); sort(keys, values, j + 1, right, compare); } const NORMAL = 0; const NON_CONTRIBUTING = 1; const SAME_TRANSITION = 2; const DIFFERENT_TRANSITION = 3; const INTERSECTION = 0; const UNION = 1; const DIFFERENCE = 2; const XOR = 3; /** * @param {SweepEvent} event * @param {SweepEvent} prev * @param {Operation} operation */ function computeFields (event, prev, operation) { // compute inOut and otherInOut fields if (prev === null) { event.inOut = false; event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon } else { if (event.isSubject === prev.isSubject) { event.inOut = !prev.inOut; event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon } else { event.inOut = !prev.otherInOut; event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut; } // compute prevInResult field if (prev) { event.prevInResult = (!inResult(prev, operation) || prev.isVertical()) ? prev.prevInResult : prev; } } // check if the line segment belongs to the Boolean operation let isInResult = inResult(event, operation); if (isInResult) { event.resultTransition = determineResultTransition(event, operation); } else { event.resultTransition = 0; } } /* eslint-disable indent */ function inResult(event, operation) { switch (event.type) { case NORMAL: switch (operation) { case INTERSECTION: return !event.otherInOut; case UNION: return event.otherInOut; case DIFFERENCE: // return (event.isSubject && !event.otherInOut) || // (!event.isSubject && event.otherInOut); return (event.isSubject && event.otherInOut) || (!event.isSubject && !event.otherInOut); case XOR: return true; } break; case SAME_TRANSITION: return operation === INTERSECTION || operation === UNION; case DIFFERENT_TRANSITION: return operation === DIFFERENCE; case NON_CONTRIBUTING: return false; } return false; } /* eslint-enable indent */ function determineResultTransition(event, operation) { let thisIn = !event.inOut; let thatIn = !event.otherInOut; let isIn; switch (operation) { case INTERSECTION: isIn = thisIn && thatIn; break; case UNION: isIn = thisIn || thatIn; break; case XOR: isIn = thisIn ^ thatIn; break; case DIFFERENCE: if (event.isSubject) { isIn = thisIn && !thatIn; } else { isIn = thatIn && !thisIn; } break; } return isIn ? +1 : -1; } class SweepEvent { /** * Sweepline event * * @class {SweepEvent} * @param {Array.} point * @param {Boolean} left * @param {SweepEvent=} otherEvent * @param {Boolean} isSubject * @param {Number} edgeType */ constructor (point, left, otherEvent, isSubject, edgeType) { /** * Is left endpoint? * @type {Boolean} */ this.left = left; /** * @type {Array.} */ this.point = point; /** * Other edge reference * @type {SweepEvent} */ this.otherEvent = otherEvent; /** * Belongs to source or clipping polygon * @type {Boolean} */ this.isSubject = isSubject; /** * Edge contribution type * @type {Number} */ this.type = edgeType || NORMAL; /** * In-out transition for the sweepline crossing polygon * @type {Boolean} */ this.inOut = false; /** * @type {Boolean} */ this.otherInOut = false; /** * Previous event in result? * @type {SweepEvent} */ this.prevInResult = null; /** * Type of result transition (0 = not in result, +1 = out-in, -1, in-out) * @type {Number} */ this.resultTransition = 0; // connection step /** * @type {Number} */ this.otherPos = -1; /** * @type {Number} */ this.outputContourId = -1; this.isExteriorRing = true; // TODO: Looks unused, remove? } /** * @param {Array.} p * @return {Boolean} */ isBelow (p) { const p0 = this.point, p1 = this.otherEvent.point; return this.left ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0 // signedArea(this.point, this.otherEvent.point, p) > 0 : : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0; //signedArea(this.otherEvent.point, this.point, p) > 0; } /** * @param {Array.} p * @return {Boolean} */ isAbove (p) { return !this.isBelow(p); } /** * @return {Boolean} */ isVertical () { return this.point[0] === this.otherEvent.point[0]; } /** * Does event belong to result? * @return {Boolean} */ get inResult() { return this.resultTransition !== 0; } clone () { const copy = new SweepEvent( this.point, this.left, this.otherEvent, this.isSubject, this.type); copy.contourId = this.contourId; copy.resultTransition = this.resultTransition; copy.prevInResult = this.prevInResult; copy.isExteriorRing = this.isExteriorRing; copy.inOut = this.inOut; copy.otherInOut = this.otherInOut; return copy; } } function equals(p1, p2) { if (p1[0] === p2[0]) { if (p1[1] === p2[1]) { return true; } else { return false; } } return false; } // const EPSILON = 1e-9; // const abs = Math.abs; // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164 // Precision problem. // // module.exports = function equals(p1, p2) { // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON; // }; const epsilon = 1.1102230246251565e-16; const splitter = 134217729; const resulterrbound = (3 + 8 * epsilon) * epsilon; // fast_expansion_sum_zeroelim routine from oritinal code function sum(elen, e, flen, f, h) { let Q, Qnew, hh, bvirt; let enow = e[0]; let fnow = f[0]; let eindex = 0; let findex = 0; if ((fnow > enow) === (fnow > -enow)) { Q = enow; enow = e[++eindex]; } else { Q = fnow; fnow = f[++findex]; } let hindex = 0; if (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = enow + Q; hh = Q - (Qnew - enow); enow = e[++eindex]; } else { Qnew = fnow + Q; hh = Q - (Qnew - fnow); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } while (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; } else { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } } while (eindex < elen) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } while (findex < flen) { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } if (Q !== 0 || hindex === 0) { h[hindex++] = Q; } return hindex; } function estimate(elen, e) { let Q = e[0]; for (let i = 1; i < elen; i++) Q += e[i]; return Q; } function vec(n) { return new Float64Array(n); } const ccwerrboundA = (3 + 16 * epsilon) * epsilon; const ccwerrboundB = (2 + 12 * epsilon) * epsilon; const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon; const B = vec(4); const C1 = vec(8); const C2 = vec(12); const D = vec(16); const u = vec(4); function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) { let acxtail, acytail, bcxtail, bcytail; let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3; const acx = ax - cx; const bcx = bx - cx; const acy = ay - cy; const bcy = by - cy; s1 = acx * bcy; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcx; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; B[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; B[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; B[2] = _j - (u3 - bvirt) + (_i - bvirt); B[3] = u3; let det = estimate(4, B); let errbound = ccwerrboundB * detsum; if (det >= errbound || -det >= errbound) { return det; } bvirt = ax - acx; acxtail = ax - (acx + bvirt) + (bvirt - cx); bvirt = bx - bcx; bcxtail = bx - (bcx + bvirt) + (bvirt - cx); bvirt = ay - acy; acytail = ay - (acy + bvirt) + (bvirt - cy); bvirt = by - bcy; bcytail = by - (bcy + bvirt) + (bvirt - cy); if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) { return det; } errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det); det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); if (det >= errbound || -det >= errbound) return det; s1 = acxtail * bcy; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcx; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C1len = sum(4, B, 4, u, C1); s1 = acx * bcytail; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcxtail; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C2len = sum(C1len, C1, 4, u, C2); s1 = acxtail * bcytail; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcxtail; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const Dlen = sum(C2len, C2, 4, u, D); return D[Dlen - 1]; } function orient2d(ax, ay, bx, by, cx, cy) { const detleft = (ay - cy) * (bx - cx); const detright = (ax - cx) * (by - cy); const det = detleft - detright; if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det; const detsum = Math.abs(detleft + detright); if (Math.abs(det) >= ccwerrboundA * detsum) return det; return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum); } /** * Signed area of the triangle (p0, p1, p2) * @param {Array.} p0 * @param {Array.} p1 * @param {Array.} p2 * @return {Number} */ function signedArea(p0, p1, p2) { const res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]); if (res > 0) return -1; if (res < 0) return 1; return 0; } /** * @param {SweepEvent} e1 * @param {SweepEvent} e2 * @return {Number} */ function compareEvents(e1, e2) { const p1 = e1.point; const p2 = e2.point; // Different x-coordinate if (p1[0] > p2[0]) return 1; if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate // Event with lower y-coordinate is processed first if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1; return specialCases(e1, e2, p1); } /* eslint-disable no-unused-vars */ function specialCases(e1, e2, p1, p2) { // Same coordinates, but one is a left endpoint and the other is // a right endpoint. The right endpoint is processed first if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point; // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) // Same coordinates, both events // are left endpoints or right endpoints. // not collinear if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) { // the event associate to the bottom segment is processed first return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1; } return (!e1.isSubject && e2.isSubject) ? 1 : -1; } /* eslint-enable no-unused-vars */ /** * @param {SweepEvent} se * @param {Array.} p * @param {Queue} queue * @return {Queue} */ function divideSegment(se, p, queue) { const r = new SweepEvent(p, false, se, se.isSubject); const l = new SweepEvent(p, true, se.otherEvent, se.isSubject); /* eslint-disable no-console */ if (equals(se.point, se.otherEvent.point)) { console.warn("what is that, a collapsed segment?", se); } /* eslint-enable no-console */ r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event if (compareEvents(l, se.otherEvent) > 0) { se.otherEvent.left = true; l.left = false; } // avoid a rounding error. The left event would be processed after the right event // if (compareEvents(se, r) > 0) {} se.otherEvent.otherEvent = l; se.otherEvent = r; queue.push(l); queue.push(r); return queue; } //const EPS = 1e-9; /** * Finds the magnitude of the cross product of two vectors (if we pretend * they"re in three dimensions) * * @param {Object} a First vector * @param {Object} b Second vector * @private * @returns {Number} The magnitude of the cross product */ function crossProduct(a, b) { return (a[0] * b[1]) - (a[1] * b[0]); } /** * Finds the dot product of two vectors. * * @param {Object} a First vector * @param {Object} b Second vector * @private * @returns {Number} The dot product */ function dotProduct(a, b) { return (a[0] * b[0]) + (a[1] * b[1]); } /** * Finds the intersection (if any) between two line segments a and b, given the * line segments" end points a1, a2 and b1, b2. * * This algorithm is based on Schneider and Eberly. * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf * Page 244. * * @param {Array.} a1 point of first line * @param {Array.} a2 point of first line * @param {Array.} b1 point of second line * @param {Array.} b2 point of second line * @param {Boolean=} noEndpointTouch whether to skip single touchpoints * (meaning connected segments) as * intersections * @returns {Array.>|Null} If the lines intersect, the point of * intersection. If they overlap, the two end points of the overlapping segment. * Otherwise, null. */ function intersection$1 (a1, a2, b1, b2, noEndpointTouch) { // The algorithm expects our lines in the form P + sd, where P is a point, // s is on the interval [0, 1], and d is a vector. // We are passed two points. P can be the first point of each pair. The // vector, then, could be thought of as the distance (in x and y components) // from the first point to the second point. // So first, let"s make our vectors: const va = [a2[0] - a1[0], a2[1] - a1[1]]; const vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form: /* eslint-disable arrow-body-style */ function toPoint(p, s, d) { return [ p[0] + s * d[0], p[1] + s * d[1] ]; } /* eslint-enable arrow-body-style */ // The rest is pretty much a straight port of the algorithm. const e = [b1[0] - a1[0], b1[1] - a1[1]]; let kross = crossProduct(va, vb); let sqrKross = kross * kross; const sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb); // Check for line intersection. This works because of the properties of the // cross product -- specifically, two vectors are parallel if and only if the // cross product is the 0 vector. The full calculation involves relative error // to account for possible very small line segments. See Schneider & Eberly // for details. if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) { // If they"re not parallel, then (because these are line segments) they // still might not actually intersect. This code checks that the // intersection point of the lines is actually on both line segments. const s = crossProduct(e, vb) / kross; if (s < 0 || s > 1) { // not on line segment a return null; } const t = crossProduct(e, va) / kross; if (t < 0 || t > 1) { // not on line segment b return null; } if (s === 0 || s === 1) { // on an endpoint of line segment a return noEndpointTouch ? null : [toPoint(a1, s, va)]; } if (t === 0 || t === 1) { // on an endpoint of line segment b return noEndpointTouch ? null : [toPoint(b1, t, vb)]; } return [toPoint(a1, s, va)]; } // If we"ve reached this point, then the lines are either parallel or the // same, but the segments could overlap partially or fully, or not at all. // So we need to find the overlap, if any. To do that, we can use e, which is // the (vector) difference between the two initial points. If this is parallel // with the line itself, then the two lines are the same line, and there will // be overlap. //const sqrLenE = dotProduct(e, e); kross = crossProduct(e, va); sqrKross = kross * kross; if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) { // Lines are just parallel, not the same. No overlap. return null; } const sa = dotProduct(va, e) / sqrLenA; const sb = sa + dotProduct(va, vb) / sqrLenA; const smin = Math.min(sa, sb); const smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from // Schneider & Eberly, just inlined into this function. if (smin <= 1 && smax >= 0) { // overlap on an end point if (smin === 1) { return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)]; } if (smax === 0) { return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)]; } if (noEndpointTouch && smin === 0 && smax === 1) return null; // There"s overlap on a segment -- two points of intersection. Return both. return [ toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va) ]; } return null; } /** * @param {SweepEvent} se1 * @param {SweepEvent} se2 * @param {Queue} queue * @return {Number} */ function possibleIntersection (se1, se2, queue) { // that disallows self-intersecting polygons, // did cost us half a day, so I"ll leave it // out of respect // if (se1.isSubject === se2.isSubject) return; const inter = intersection$1( se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point ); const nintersections = inter ? inter.length : 0; if (nintersections === 0) return 0; // no intersection // the line segments intersect at an endpoint of both line segments if ((nintersections === 1) && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) { return 0; } if (nintersections === 2 && se1.isSubject === se2.isSubject) { // if(se1.contourId === se2.contourId){ // console.warn("Edges of the same polygon overlap", // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); // } //throw new Error("Edges of the same polygon overlap"); return 0; } // The line segments associated to se1 and se2 intersect if (nintersections === 1) { // if the intersection point is not an endpoint of se1 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) { divideSegment(se1, inter[0], queue); } // if the intersection point is not an endpoint of se2 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) { divideSegment(se2, inter[0], queue); } return 1; } // The line segments associated to se1 and se2 overlap const events = []; let leftCoincide = false; let rightCoincide = false; if (equals(se1.point, se2.point)) { leftCoincide = true; // linked } else if (compareEvents(se1, se2) === 1) { events.push(se2, se1); } else { events.push(se1, se2); } if (equals(se1.otherEvent.point, se2.otherEvent.point)) { rightCoincide = true; } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) { events.push(se2.otherEvent, se1.otherEvent); } else { events.push(se1.otherEvent, se2.otherEvent); } if ((leftCoincide && rightCoincide) || leftCoincide) { // both line segments are equal or share the left endpoint se2.type = NON_CONTRIBUTING; se1.type = (se2.inOut === se1.inOut) ? SAME_TRANSITION : DIFFERENT_TRANSITION; if (leftCoincide && !rightCoincide) { // honestly no idea, but changing events selection from [2, 1] // to [0, 1] fixes the overlapping self-intersecting polygons issue divideSegment(events[1].otherEvent, events[0].point, queue); } return 2; } // the line segments share the right endpoint if (rightCoincide) { divideSegment(events[0], events[1].point, queue); return 3; } // no line segment includes totally the other one if (events[0] !== events[3].otherEvent) { divideSegment(events[0], events[1].point, queue); divideSegment(events[1], events[2].point, queue); return 3; } // one line segment includes the other one divideSegment(events[0], events[1].point, queue); divideSegment(events[3].otherEvent, events[2].point, queue); return 3; } /** * @param {SweepEvent} le1 * @param {SweepEvent} le2 * @return {Number} */ function compareSegments(le1, le2) { if (le1 === le2) return 0; // Segments are not collinear if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) { // If they share their left endpoint use the right endpoint to sort if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1; // has the line segment associated to e1 been inserted // into S after the line segment associated to e2 ? if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted // into S after the line segment associated to e1 return le1.isBelow(le2.point) ? -1 : 1; } if (le1.isSubject === le2.isSubject) { // same polygon let p1 = le1.point, p2 = le2.point; if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) { p1 = le1.otherEvent.point; p2 = le2.otherEvent.point; if (p1[0] === p2[0] && p1[1] === p2[1]) return 0; else return le1.contourId > le2.contourId ? 1 : -1; } } else { // Segments are collinear, but belong to separate polygons return le1.isSubject ? -1 : 1; } return compareEvents(le1, le2) === 1 ? 1 : -1; } function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) { const sweepLine = new SplayTree(compareSegments); const sortedEvents = []; const rightbound = Math.min(sbbox[2], cbbox[2]); let prev, next, begin; while (eventQueue.length !== 0) { let event = eventQueue.pop(); sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here if ((operation === INTERSECTION && event.point[0] > rightbound) || (operation === DIFFERENCE && event.point[0] > sbbox[2])) { break; } if (event.left) { next = prev = sweepLine.insert(event); begin = sweepLine.minNode(); if (prev !== begin) prev = sweepLine.prev(prev); else prev = null; next = sweepLine.next(next); const prevEvent = prev ? prev.key : null; let prevprevEvent; computeFields(event, prevEvent, operation); if (next) { if (possibleIntersection(event, next.key, eventQueue) === 2) { computeFields(event, prevEvent, operation); computeFields(next.key, event, operation); } } if (prev) { if (possibleIntersection(prev.key, event, eventQueue) === 2) { let prevprev = prev; if (prevprev !== begin) prevprev = sweepLine.prev(prevprev); else prevprev = null; prevprevEvent = prevprev ? prevprev.key : null; computeFields(prevEvent, prevprevEvent, operation); computeFields(event, prevEvent, operation); } } } else { event = event.otherEvent; next = prev = sweepLine.find(event); if (prev && next) { if (prev !== begin) prev = sweepLine.prev(prev); else prev = null; next = sweepLine.next(next); sweepLine.remove(event); if (next && prev) { possibleIntersection(prev.key, next.key, eventQueue); } } } } return sortedEvents; } class Contour { /** * Contour * * @class {Contour} */ constructor() { this.points = []; this.holeIds = []; this.holeOf = null; this.depth = null; } isExterior() { return this.holeOf == null; } } /** * @param {Array.} sortedEvents * @return {Array.} */ function orderEvents(sortedEvents) { let event, i, len, tmp; const resultEvents = []; for (i = 0, len = sortedEvents.length; i < len; i++) { event = sortedEvents[i]; if ((event.left && event.inResult) || (!event.left && event.otherEvent.inResult)) { resultEvents.push(event); } } // Due to overlapping edges the resultEvents array can be not wholly sorted let sorted = false; while (!sorted) { sorted = true; for (i = 0, len = resultEvents.length; i < len; i++) { if ((i + 1) < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) { tmp = resultEvents[i]; resultEvents[i] = resultEvents[i + 1]; resultEvents[i + 1] = tmp; sorted = false; } } } for (i = 0, len = resultEvents.length; i < len; i++) { event = resultEvents[i]; event.otherPos = i; } // imagine, the right event is found in the beginning of the queue, // when his left counterpart is not marked yet for (i = 0, len = resultEvents.length; i < len; i++) { event = resultEvents[i]; if (!event.left) { tmp = event.otherPos; event.otherPos = event.otherEvent.otherPos; event.otherEvent.otherPos = tmp; } } return resultEvents; } /** * @param {Number} pos * @param {Array.} resultEvents * @param {Object>} processed * @return {Number} */ function nextPos(pos, resultEvents, processed, origPos) { let newPos = pos + 1, p = resultEvents[pos].point, p1; const length = resultEvents.length; if (newPos < length) p1 = resultEvents[newPos].point; while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) { if (!processed[newPos]) { return newPos; } else { newPos++; } if (newPos < length) { p1 = resultEvents[newPos].point; } } newPos = pos - 1; while (processed[newPos] && newPos > origPos) { newPos--; } return newPos; } function initializeContourFromContext(event, contours, contourId) { const contour = new Contour(); if (event.prevInResult != null) { const prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id, // because we must have already processed it (i.e., assigned an output contour id) // in an earlier iteration, otherwise it wouldn"t be possible that it is "previous in // result". const lowerContourId = prevInResult.outputContourId; const lowerResultTransition = prevInResult.resultTransition; if (lowerResultTransition > 0) { // We are inside. Now we have to check if the thing below us is another hole or // an exterior contour. const lowerContour = contours[lowerContourId]; if (lowerContour.holeOf != null) { // The lower contour is a hole => Connect the new contour as a hole to its parent, // and use same depth. const parentContourId = lowerContour.holeOf; contours[parentContourId].holeIds.push(contourId); contour.holeOf = parentContourId; contour.depth = contours[lowerContourId].depth; } else { // The lower contour is an exterior contour => Connect the new contour as a hole, // and increment depth. contours[lowerContourId].holeIds.push(contourId); contour.holeOf = lowerContourId; contour.depth = contours[lowerContourId].depth + 1; } } else { // We are outside => this contour is an exterior contour of same depth. contour.holeOf = null; contour.depth = contours[lowerContourId].depth; } } else { // There is no lower/previous contour => this contour is an exterior contour of depth 0. contour.holeOf = null; contour.depth = 0; } return contour; } /** * @param {Array.} sortedEvents * @return {Array.<*>} polygons */ function connectEdges(sortedEvents) { let i, len; const resultEvents = orderEvents(sortedEvents); // "false"-filled array const processed = {}; const contours = []; for (i = 0, len = resultEvents.length; i < len; i++) { if (processed[i]) { continue; } const contourId = contours.length; const contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID const markAsProcessed = (pos) => { processed[pos] = true; if (pos < resultEvents.length && resultEvents[pos]) { resultEvents[pos].outputContourId = contourId; } }; let pos = i; let origPos = i; const initial = resultEvents[i].point; contour.points.push(initial); /* eslint no-constant-condition: "off" */ while (true) { markAsProcessed(pos); pos = resultEvents[pos].otherPos; markAsProcessed(pos); contour.points.push(resultEvents[pos].point); pos = nextPos(pos, resultEvents, processed, origPos); if (pos == origPos || pos >= resultEvents.length || !resultEvents[pos]) { break; } } contours.push(contour); } return contours; } var tinyqueue = {exports: {}}; tinyqueue.exports = TinyQueue; tinyqueue.exports.default = TinyQueue; function TinyQueue(data, compare) { if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare); this.data = data || []; this.length = this.data.length; this.compare = compare || defaultCompare; if (this.length > 0) { for (var i = (this.length >> 1) - 1; i >= 0; i--) this._down(i); } } function defaultCompare(a, b) { return a < b ? -1 : a > b ? 1 : 0; } TinyQueue.prototype = { push: function (item) { this.data.push(item); this.length++; this._up(this.length - 1); }, pop: function () { if (this.length === 0) return undefined; var top = this.data[0]; this.length--; if (this.length > 0) { this.data[0] = this.data[this.length]; this._down(0); } this.data.pop(); return top; }, peek: function () { return this.data[0]; }, _up: function (pos) { var data = this.data; var compare = this.compare; var item = data[pos]; while (pos > 0) { var parent = (pos - 1) >> 1; var current = data[parent]; if (compare(item, current) >= 0) break; data[pos] = current; pos = parent; } data[pos] = item; }, _down: function (pos) { var data = this.data; var compare = this.compare; var halfLength = this.length >> 1; var item = data[pos]; while (pos < halfLength) { var left = (pos << 1) + 1; var right = left + 1; var best = data[left]; if (right < this.length && compare(data[right], best) < 0) { left = right; best = data[right]; } if (compare(best, item) >= 0) break; data[pos] = best; pos = left; } data[pos] = item; } }; const max = Math.max; const min = Math.min; let contourId = 0; function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) { let i, len, s1, s2, e1, e2; for (i = 0, len = contourOrHole.length - 1; i < len; i++) { s1 = contourOrHole[i]; s2 = contourOrHole[i + 1]; e1 = new SweepEvent(s1, false, undefined, isSubject); e2 = new SweepEvent(s2, false, e1, isSubject); e1.otherEvent = e2; if (s1[0] === s2[0] && s1[1] === s2[1]) { continue; // skip collapsed edges, or it breaks } e1.contourId = e2.contourId = depth; if (!isExteriorRing) { e1.isExteriorRing = false; e2.isExteriorRing = false; } if (compareEvents(e1, e2) > 0) { e2.left = true; } else { e1.left = true; } const x = s1[0], y = s1[1]; bbox[0] = min(bbox[0], x); bbox[1] = min(bbox[1], y); bbox[2] = max(bbox[2], x); bbox[3] = max(bbox[3], y); // Pushing it so the queue is sorted from left to right, // with object on the left having the highest priority. Q.push(e1); Q.push(e2); } } function fillQueue(subject, clipping, sbbox, cbbox, operation) { const eventQueue = new tinyqueue.exports(null, compareEvents); let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk; for (i = 0, ii = subject.length; i < ii; i++) { polygonSet = subject[i]; for (j = 0, jj = polygonSet.length; j < jj; j++) { isExteriorRing = j === 0; if (isExteriorRing) contourId++; processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing); } } for (i = 0, ii = clipping.length; i < ii; i++) { polygonSet = clipping[i]; for (j = 0, jj = polygonSet.length; j < jj; j++) { isExteriorRing = j === 0; if (operation === DIFFERENCE) isExteriorRing = false; if (isExteriorRing) contourId++; processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing); } } return eventQueue; } const EMPTY = []; function trivialOperation(subject, clipping, operation) { let result = null; if (subject.length * clipping.length === 0) { if (operation === INTERSECTION) { result = EMPTY; } else if (operation === DIFFERENCE) { result = subject; } else if (operation === UNION || operation === XOR) { result = (subject.length === 0) ? clipping : subject; } } return result; } function compareBBoxes(subject, clipping, sbbox, cbbox, operation) { let result = null; if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) { if (operation === INTERSECTION) { result = EMPTY; } else if (operation === DIFFERENCE) { result = subject; } else if (operation === UNION || operation === XOR) { result = subject.concat(clipping); } } return result; } function boolean(subject, clipping, operation) { if (typeof subject[0][0][0] === "number") { subject = [subject]; } if (typeof clipping[0][0][0] === "number") { clipping = [clipping]; } let trivial = trivialOperation(subject, clipping, operation); if (trivial) { return trivial === EMPTY ? null : trivial; } const sbbox = [Infinity, Infinity, -Infinity, -Infinity]; const cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time("fill queue"); const eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd("fill queue"); trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation); if (trivial) { return trivial === EMPTY ? null : trivial; } // console.time("subdivide edges"); const sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd("subdivide edges"); // console.time("connect vertices"); const contours = connectEdges(sortedEvents); //console.timeEnd("connect vertices"); // Convert contours to polygons const polygons = []; for (let i = 0; i < contours.length; i++) { let contour = contours[i]; if (contour.isExterior()) { // The exterior ring goes first let rings = [contour.points]; // Followed by holes if any for (let j = 0; j < contour.holeIds.length; j++) { let holeId = contour.holeIds[j]; rings.push(contours[holeId].points); } polygons.push(rings); } } return polygons; } function intersection (subject, clipping) { return boolean(subject, clipping, INTERSECTION); } /** * @class VertexGeometry * @abstract * @classdesc Represents a vertex geometry. */ class VertexGeometry extends Geometry { /** * Create a vertex geometry. * * @constructor * @ignore */ constructor() { super(); this._subsampleThreshold = 0.005; } /** * Finds the polygon pole of inaccessibility, the most distant internal * point from the polygon outline. * * @param {Array>} points2d - 2d points of outline to triangulate. * @returns {Array} Point of inaccessibility. * @ignore */ _getPoleOfInaccessibility2d(points2d) { let pole2d = polylabel$1.exports([points2d], 3e-2); return pole2d; } _project(points2d, transform) { const camera = this._createCamera(transform.upVector().toArray(), transform.unprojectSfM([0, 0], 0), transform.unprojectSfM([0, 0], 10)); return this._deunproject(points2d, transform, camera); } _subsample(points2d, threshold = this._subsampleThreshold) { const subsampled = []; const length = points2d.length; for (let index = 0; index < length; index++) { const p1 = points2d[index]; const p2 = points2d[(index + 1) % length]; subsampled.push(p1); const dist = Math.sqrt(Math.pow((p2[0] - p1[0]), 2) + Math.pow((p2[1] - p1[1]), 2)); const subsamples = Math.floor(dist / threshold); const coeff = 1 / (subsamples + 1); for (let i = 1; i <= subsamples; i++) { const alpha = i * coeff; const subsample = [ (1 - alpha) * p1[0] + alpha * p2[0], (1 - alpha) * p1[1] + alpha * p2[1], ]; subsampled.push(subsample); } } return subsampled; } /** * Triangulates a 2d polygon and returns the triangle * representation as a flattened array of 3d points. * * @param {Array>} points2d - 2d points of outline to triangulate. * @param {Array>} points3d - 3d points of outline corresponding to the 2d points. * @param {Array>>} [holes2d] - 2d points of holes to triangulate. * @param {Array>>} [holes3d] - 3d points of holes corresponding to the 2d points. * @returns {Array} Flattened array of 3d points ordered based on the triangles. * @ignore */ _triangulate(points2d, points3d, holes2d, holes3d) { let data = [points2d.slice(0, -1)]; for (let hole2d of holes2d != null ? holes2d : []) { data.push(hole2d.slice(0, -1)); } let points = points3d.slice(0, -1); for (let hole3d of holes3d != null ? holes3d : []) { points = points.concat(hole3d.slice(0, -1)); } let flattened = earcut$1.exports.flatten(data); let indices = earcut$1.exports(flattened.vertices, flattened.holes, flattened.dimensions); let triangles = []; for (let i = 0; i < indices.length; ++i) { let point = points[indices[i]]; triangles.push(point[0]); triangles.push(point[1]); triangles.push(point[2]); } return triangles; } _triangulateSpherical(points2d, holes2d, transform) { const triangles = []; const epsilon = 1e-9; const subareasX = 3; const subareasY = 3; for (let x = 0; x < subareasX; x++) { for (let y = 0; y < subareasY; y++) { const epsilonX0 = x === 0 ? -epsilon : epsilon; const epsilonY0 = y === 0 ? -epsilon : epsilon; const x0 = x / subareasX + epsilonX0; const y0 = y / subareasY + epsilonY0; const x1 = (x + 1) / subareasX + epsilon; const y1 = (y + 1) / subareasY + epsilon; const bbox2d = [ [x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0], ]; const lookat2d = [ (2 * x + 1) / (2 * subareasX), (2 * y + 1) / (2 * subareasY), ]; triangles.push(...this._triangulateSubarea(points2d, holes2d, bbox2d, lookat2d, transform)); } } return triangles; } _unproject(points2d, transform, distance = 200) { return points2d .map((point) => { return transform.unprojectBasic(point, distance); }); } _createCamera(upVector, position, lookAt) { const camera = new Camera$2(); camera.up.copy(new Vector3().fromArray(upVector)); camera.position.copy(new Vector3().fromArray(position)); camera.lookAt(new Vector3().fromArray(lookAt)); camera.updateMatrix(); camera.updateMatrixWorld(true); return camera; } _deunproject(points2d, transform, camera) { return points2d .map((point2d) => { const pointWorld = transform.unprojectBasic(point2d, 10000); const pointCamera = new Vector3(pointWorld[0], pointWorld[1], pointWorld[2]) .applyMatrix4(camera.matrixWorldInverse); return [pointCamera.x / pointCamera.z, pointCamera.y / pointCamera.z]; }); } _triangulateSubarea(points2d, holes2d, bbox2d, lookat2d, transform) { const intersections = intersection([points2d, ...holes2d], [bbox2d]); if (!intersections) { return []; } const triangles = []; const threshold = this._subsampleThreshold; const camera = this._createCamera(transform.upVector().toArray(), transform.unprojectSfM([0, 0], 0), transform.unprojectBasic(lookat2d, 10)); for (const intersection of intersections) { const subsampledPolygon2d = this._subsample(intersection[0], threshold); const polygon2d = this._deunproject(subsampledPolygon2d, transform, camera); const polygon3d = this._unproject(subsampledPolygon2d, transform); const polygonHoles2d = []; const polygonHoles3d = []; for (let i = 1; i < intersection.length; i++) { let subsampledHole2d = this._subsample(intersection[i], threshold); const hole2d = this._deunproject(subsampledHole2d, transform, camera); const hole3d = this._unproject(subsampledHole2d, transform); polygonHoles2d.push(hole2d); polygonHoles3d.push(hole3d); } triangles.push(...this._triangulate(polygon2d, polygon3d, polygonHoles2d, polygonHoles3d)); } return triangles; } } /** * @class RectGeometry * * @classdesc Represents a rectangle geometry in the 2D basic image coordinate system. * * @example * ```js * var basicRect = [0.5, 0.3, 0.7, 0.4]; * var rectGeometry = new RectGeometry(basicRect); * ``` */ class RectGeometry extends VertexGeometry { /** * Create a rectangle geometry. * * @constructor * @param {Array} rect - An array representing the top-left and bottom-right * corners of the rectangle in basic coordinates. Ordered according to [x0, y0, x1, y1]. * * @throws {GeometryTagError} Rectangle coordinates must be valid basic coordinates. */ constructor(rect) { super(); if (rect.length !== 4) { throw new GeometryTagError("Rectangle needs to have four values."); } if (rect[1] > rect[3]) { throw new GeometryTagError("Basic Y coordinates values can not be inverted."); } for (let coord of rect) { if (coord < 0 || coord > 1) { throw new GeometryTagError("Basic coordinates must be on the interval [0, 1]."); } } this._anchorIndex = undefined; this._rect = rect.slice(0, 4); this._inverted = this._rect[0] > this._rect[2]; } /** * Get anchor index property. * * @returns {number} Index representing the current anchor property if * achoring indexing has been initialized. If anchor indexing has not been * initialized or has been terminated undefined will be returned. * @ignore */ get anchorIndex() { return this._anchorIndex; } /** * Get inverted property. * * @returns {boolean} Boolean determining whether the rect geometry is * inverted. For spherical the rect geometrye may be inverted. * @ignore */ get inverted() { return this._inverted; } /** * Get rect property. * * @returns {Array} Array representing the top-left and bottom-right * corners of the rectangle in basic coordinates. */ get rect() { return this._rect; } /** * Initialize anchor indexing to enable setting opposite vertex. * * @param {number} [index] - The index of the vertex to use as anchor. * * @throws {GeometryTagError} If anchor indexing has already been initialized. * @throws {GeometryTagError} If index is not valid (0 to 3). * @ignore */ initializeAnchorIndexing(index) { if (this._anchorIndex !== undefined) { throw new GeometryTagError("Anchor indexing is already initialized."); } if (index < 0 || index > 3) { throw new GeometryTagError(`Invalid anchor index: ${index}.`); } this._anchorIndex = index === undefined ? 0 : index; } /** * Terminate anchor indexing to disable setting pposite vertex. * @ignore */ terminateAnchorIndexing() { this._anchorIndex = undefined; } /** * Set the value of the vertex opposite to the anchor in the polygon * representation of the rectangle. * * @description Setting the opposite vertex may change the anchor index. * * @param {Array} opposite - The new value of the vertex opposite to the anchor. * @param {Transform} transform - The transform of the image related to the rectangle. * * @throws {GeometryTagError} When anchor indexing has not been initialized. * @ignore */ setOppositeVertex2d(opposite, transform) { if (this._anchorIndex === undefined) { throw new GeometryTagError("Anchor indexing needs to be initialized."); } const changed = [ Math.max(0, Math.min(1, opposite[0])), Math.max(0, Math.min(1, opposite[1])), ]; const original = this._rect.slice(); const anchor = this._anchorIndex === 0 ? [original[0], original[3]] : this._anchorIndex === 1 ? [original[0], original[1]] : this._anchorIndex === 2 ? [original[2], original[1]] : [original[2], original[3]]; if (isSpherical(transform.cameraType)) { const deltaX = this._anchorIndex < 2 ? changed[0] - original[2] : changed[0] - original[0]; if (!this._inverted && this._anchorIndex < 2 && changed[0] < 0.25 && original[2] > 0.75 && deltaX < -0.5) { // right side passes boundary rightward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (!this._inverted && this._anchorIndex >= 2 && changed[0] < 0.25 && original[2] > 0.75 && deltaX < -0.5) { // left side passes right side and boundary rightward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (this._inverted && this._anchorIndex >= 2 && changed[0] < 0.25 && original[0] > 0.75 && deltaX < -0.5) { this._inverted = false; if (anchor[0] > changed[0]) { // left side passes boundary rightward this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else { // left side passes right side and boundary rightward this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } } else if (!this._inverted && this._anchorIndex >= 2 && changed[0] > 0.75 && original[0] < 0.25 && deltaX > 0.5) { // left side passes boundary leftward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (!this._inverted && this._anchorIndex < 2 && changed[0] > 0.75 && original[0] < 0.25 && deltaX > 0.5) { // right side passes left side and boundary leftward this._inverted = true; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (this._inverted && this._anchorIndex < 2 && changed[0] > 0.75 && original[2] < 0.25 && deltaX > 0.5) { this._inverted = false; if (anchor[0] > changed[0]) { // right side passes boundary leftward this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else { // right side passes left side and boundary leftward this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } } else if (this._inverted && this._anchorIndex < 2 && changed[0] > original[0]) { // inverted and right side passes left side completing a loop this._inverted = false; this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else if (this._inverted && this._anchorIndex >= 2 && changed[0] < original[2]) { // inverted and left side passes right side completing a loop this._inverted = false; this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } else if (this._inverted) { // if still inverted only top and bottom can switch if (this._anchorIndex < 2) { this._anchorIndex = anchor[1] > changed[1] ? 0 : 1; } else { this._anchorIndex = anchor[1] > changed[1] ? 3 : 2; } } else { // if still not inverted treat as non spherical if (anchor[0] <= changed[0] && anchor[1] > changed[1]) { this._anchorIndex = 0; } else if (anchor[0] <= changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 1; } else if (anchor[0] > changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 2; } else { this._anchorIndex = 3; } } const rect = []; if (this._anchorIndex === 0) { rect[0] = anchor[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = anchor[1]; } else if (this._anchorIndex === 1) { rect[0] = anchor[0]; rect[1] = anchor[1]; rect[2] = changed[0]; rect[3] = changed[1]; } else if (this._anchorIndex === 2) { rect[0] = changed[0]; rect[1] = anchor[1]; rect[2] = anchor[0]; rect[3] = changed[1]; } else { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = anchor[0]; rect[3] = anchor[1]; } if (!this._inverted && rect[0] > rect[2] || this._inverted && rect[0] < rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; } else { if (anchor[0] <= changed[0] && anchor[1] > changed[1]) { this._anchorIndex = 0; } else if (anchor[0] <= changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 1; } else if (anchor[0] > changed[0] && anchor[1] <= changed[1]) { this._anchorIndex = 2; } else { this._anchorIndex = 3; } const rect = []; if (this._anchorIndex === 0) { rect[0] = anchor[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = anchor[1]; } else if (this._anchorIndex === 1) { rect[0] = anchor[0]; rect[1] = anchor[1]; rect[2] = changed[0]; rect[3] = changed[1]; } else if (this._anchorIndex === 2) { rect[0] = changed[0]; rect[1] = anchor[1]; rect[2] = anchor[0]; rect[3] = changed[1]; } else { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = anchor[0]; rect[3] = anchor[1]; } if (rect[0] > rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; } this._notifyChanged$.next(this); } /** * Set the value of a vertex in the polygon representation of the rectangle. * * @description The polygon is defined to have the first vertex at the * bottom-left corner with the rest of the vertices following in clockwise order. * * @param {number} index - The index of the vertex to be set. * @param {Array} value - The new value of the vertex. * @param {Transform} transform - The transform of the image related to the rectangle. * @ignore */ setVertex2d(index, value, transform) { let original = this._rect.slice(); let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; let rect = []; if (index === 0) { rect[0] = changed[0]; rect[1] = original[1]; rect[2] = original[2]; rect[3] = changed[1]; } else if (index === 1) { rect[0] = changed[0]; rect[1] = changed[1]; rect[2] = original[2]; rect[3] = original[3]; } else if (index === 2) { rect[0] = original[0]; rect[1] = changed[1]; rect[2] = changed[0]; rect[3] = original[3]; } else if (index === 3) { rect[0] = original[0]; rect[1] = original[1]; rect[2] = changed[0]; rect[3] = changed[1]; } if (isSpherical(transform.cameraType)) { let passingBoundaryLeftward = index < 2 && changed[0] > 0.75 && original[0] < 0.25 || index >= 2 && this._inverted && changed[0] > 0.75 && original[2] < 0.25; let passingBoundaryRightward = index < 2 && this._inverted && changed[0] < 0.25 && original[0] > 0.75 || index >= 2 && changed[0] < 0.25 && original[2] > 0.75; if (passingBoundaryLeftward || passingBoundaryRightward) { this._inverted = !this._inverted; } else { if (rect[0] - original[0] < -0.25) { rect[0] = original[0]; } if (rect[2] - original[2] > 0.25) { rect[2] = original[2]; } } if (!this._inverted && rect[0] > rect[2] || this._inverted && rect[0] < rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } } else { if (rect[0] > rect[2]) { rect[0] = original[0]; rect[2] = original[2]; } } if (rect[1] > rect[3]) { rect[1] = original[1]; rect[3] = original[3]; } this._rect[0] = rect[0]; this._rect[1] = rect[1]; this._rect[2] = rect[2]; this._rect[3] = rect[3]; this._notifyChanged$.next(this); } /** @ignore */ setCentroid2d(value, transform) { let original = this._rect.slice(); let x0 = original[0]; let x1 = this._inverted ? original[2] + 1 : original[2]; let y0 = original[1]; let y1 = original[3]; let centerX = x0 + (x1 - x0) / 2; let centerY = y0 + (y1 - y0) / 2; let translationX = 0; if (isSpherical(transform.cameraType)) { translationX = this._inverted ? value[0] + 1 - centerX : value[0] - centerX; } else { let minTranslationX = -x0; let maxTranslationX = 1 - x1; translationX = Math.max(minTranslationX, Math.min(maxTranslationX, value[0] - centerX)); } let minTranslationY = -y0; let maxTranslationY = 1 - y1; let translationY = Math.max(minTranslationY, Math.min(maxTranslationY, value[1] - centerY)); this._rect[0] = original[0] + translationX; this._rect[1] = original[1] + translationY; this._rect[2] = original[2] + translationX; this._rect[3] = original[3] + translationY; if (this._rect[0] < 0) { this._rect[0] += 1; this._inverted = !this._inverted; } else if (this._rect[0] > 1) { this._rect[0] -= 1; this._inverted = !this._inverted; } if (this._rect[2] < 0) { this._rect[2] += 1; this._inverted = !this._inverted; } else if (this._rect[2] > 1) { this._rect[2] -= 1; this._inverted = !this._inverted; } this._notifyChanged$.next(this); } /** * Get the 3D coordinates for the vertices of the rectangle with * interpolated points along the lines. * * @param {Transform} transform - The transform of the image related to * the rectangle. * @returns {Array>} Polygon array of 3D world coordinates * representing the rectangle. * @ignore */ getPoints3d(transform) { return this._getPoints2d() .map((point) => { return transform.unprojectBasic(point, 200); }); } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The method shifts the right side * coordinates of the rectangle by one unit to ensure that the vertices are ordered * clockwise. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getVertex2d(index) { return this._rectToVertices2d(this._rect)[index]; } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The coordinates will not be shifted * so they may not appear in clockwise order when layed out on the plane. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getNonAdjustedVertex2d(index) { return this._rectToNonAdjustedVertices2d(this._rect)[index]; } /** * Get a vertex from the polygon representation of the 3D coordinates for the * vertices of the geometry. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @param {number} index - Vertex index. * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>} Polygon array of 3D world coordinates representing * the vertices of the geometry. * @ignore */ getVertex3d(index, transform) { return transform.unprojectBasic(this._rectToVertices2d(this._rect)[index], 200); } /** * Get a polygon representation of the 2D basic coordinates for the vertices of the rectangle. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @returns {Array>} Polygon array of 2D basic coordinates representing * the rectangle vertices. * @ignore */ getVertices2d() { return this._rectToVertices2d(this._rect); } /** * Get a polygon representation of the 3D coordinates for the vertices of the rectangle. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. * * @param {Transform} transform - The transform of the image related to the rectangle. * @returns {Array>} Polygon array of 3D world coordinates representing * the rectangle vertices. * @ignore */ getVertices3d(transform) { return this._rectToVertices2d(this._rect) .map((vertex) => { return transform.unprojectBasic(vertex, 200); }); } /** @ignore */ getCentroid2d() { const rect = this._rect; const x0 = rect[0]; const x1 = this._inverted ? rect[2] + 1 : rect[2]; const y0 = rect[1]; const y1 = rect[3]; const centroidX = (x0 + x1) / 2; const centroidY = (y0 + y1) / 2; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { const centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** * @ignore */ getPoleOfInaccessibility2d() { return this._getPoleOfInaccessibility2d(this._rectToVertices2d(this._rect)); } /** @ignore */ getPoleOfInaccessibility3d(transform) { let pole2d = this._getPoleOfInaccessibility2d(this._rectToVertices2d(this._rect)); return transform.unprojectBasic(pole2d, 200); } /** @ignore */ getTriangles3d(transform) { return isSpherical(transform.cameraType) ? [] : this._triangulate(this._project(this._getPoints2d(), transform), this.getPoints3d(transform)); } /** * Check if a particular bottom-right value is valid according to the current * rectangle coordinates. * * @param {Array} bottomRight - The bottom-right coordinates to validate * @returns {boolean} Value indicating whether the provided bottom-right coordinates * are valid. * @ignore */ validate(bottomRight) { let rect = this._rect; if (!this._inverted && bottomRight[0] < rect[0] || bottomRight[0] - rect[2] > 0.25 || bottomRight[1] < rect[1]) { return false; } return true; } /** * Get the 2D coordinates for the vertices of the rectangle with * interpolated points along the lines. * * @returns {Array>} Polygon array of 2D basic coordinates * representing the rectangle. */ _getPoints2d() { let vertices2d = this._rectToVertices2d(this._rect); let sides = vertices2d.length - 1; let sections = 10; let points2d = []; for (let i = 0; i < sides; ++i) { let startX = vertices2d[i][0]; let startY = vertices2d[i][1]; let endX = vertices2d[i + 1][0]; let endY = vertices2d[i + 1][1]; let intervalX = (endX - startX) / (sections - 1); let intervalY = (endY - startY) / (sections - 1); for (let j = 0; j < sections; ++j) { let point = [ startX + j * intervalX, startY + j * intervalY, ]; points2d.push(point); } } return points2d; } /** * Convert the top-left, bottom-right representation of a rectangle to a polygon * representation of the vertices starting at the bottom-left corner going * clockwise. * * @description The method shifts the right side coordinates of the rectangle * by one unit to ensure that the vertices are ordered clockwise. * * @param {Array} rect - Top-left, bottom-right representation of a * rectangle. * @returns {Array>} Polygon representation of the vertices of the * rectangle. */ _rectToVertices2d(rect) { return [ [rect[0], rect[3]], [rect[0], rect[1]], [this._inverted ? rect[2] + 1 : rect[2], rect[1]], [this._inverted ? rect[2] + 1 : rect[2], rect[3]], [rect[0], rect[3]], ]; } /** * Convert the top-left, bottom-right representation of a rectangle to a polygon * representation of the vertices starting at the bottom-left corner going * clockwise. * * @description The first vertex represents the bottom-left corner with the rest of * the vertices following in clockwise order. The coordinates will not be shifted * to ensure that the vertices are ordered clockwise when layed out on the plane. * * @param {Array} rect - Top-left, bottom-right representation of a * rectangle. * @returns {Array>} Polygon representation of the vertices of the * rectangle. */ _rectToNonAdjustedVertices2d(rect) { return [ [rect[0], rect[3]], [rect[0], rect[1]], [rect[2], rect[1]], [rect[2], rect[3]], [rect[0], rect[3]], ]; } } class ExtremePointCreateTag extends CreateTag { constructor(geometry, options, transform, viewportCoords) { super(geometry, transform, viewportCoords); this._options = { color: options.color == null ? 0xFFFFFF : options.color, indicateCompleter: options.indicateCompleter == null ? true : options.indicateCompleter, }; this._rectGeometry = new RectGeometry(this._geometry.getRect2d(transform)); this._createGlObjects(); } create() { if (this._geometry.points.length < 3) { return; } this._geometry.removePoint2d(this._geometry.points.length - 1); this._created$.next(this); } dispose() { super.dispose(); this._disposeObjects(); } getDOMObjects(camera, size) { const container = { offsetHeight: size.height, offsetWidth: size.width, }; const vNodes = []; const points2d = this._geometry.getPoints2d(); const length = points2d.length; for (let index = 0; index < length - 1; index++) { const nonModifiedIndex = index; const [pointX, pointY] = points2d[index]; const pointCanvas = this._viewportCoords.basicToCanvasSafe(pointX, pointY, container, this._transform, camera); if (!pointCanvas) { continue; } const abort = (e) => { e.stopPropagation(); this._aborted$.next(this); }; const remove = (e) => { e.stopPropagation(); this._geometry.removePoint2d(nonModifiedIndex); }; const transform = this._canvasToTransform(pointCanvas); const completerProperties = { onclick: index === 0 && length < 3 ? abort : remove, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); const background = this._colorToBackground(this._options.color); const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } if (length > 2 && this._options.indicateCompleter === true) { const [centroidX, centroidY] = this._geometry.getCentroid2d(this._transform); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidX, centroidY, container, this._transform, camera); if (!!centroidCanvas) { const complete = (e) => { e.stopPropagation(); this._geometry.removePoint2d(this._geometry.points.length - 1); this._created$.next(this); }; const transform = this._canvasToTransform(centroidCanvas); const completerProperties = { onclick: complete, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-completer.mapillary-tag-larger", completerProperties, [])); const pointProperties = { style: { background: this._colorToBackground(this._options.color), transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex.mapillary-tag-larger", pointProperties, [])); const dotProperties = { style: { transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-dot", dotProperties, [])); } } return vNodes; } _onGeometryChanged() { this._disposeObjects(); this._rectGeometry = new RectGeometry(this._geometry.getRect2d(this._transform)); this._createGlObjects(); } _createGlObjects() { this._glObjects = []; const polygon3d = this._rectGeometry.getPoints3d(this._transform); this._outline = this._createOutine(polygon3d, this._options.color); this._glObjects.push(this._outline); } _disposeObjects() { this._disposeLine(this._outline); this._outline = null; this._glObjects = null; } } /** * @class PolygonGeometry * * @classdesc Represents a polygon geometry in the 2D basic image coordinate system. * All polygons and holes provided to the constructor needs to be closed. * * @example * ```js * var basicPolygon = [[0.5, 0.3], [0.7, 0.3], [0.6, 0.5], [0.5, 0.3]]; * var polygonGeometry = new PolygonGeometry(basicPolygon); * ``` */ class PolygonGeometry extends VertexGeometry { /** * Create a polygon geometry. * * @constructor * @param {Array>} polygon - Array of polygon vertices. Must be closed. * @param {Array>>} [holes] - Array of arrays of hole vertices. * Each array of holes vertices must be closed. * * @throws {GeometryTagError} Polygon coordinates must be valid basic coordinates. */ constructor(polygon, holes) { super(); let polygonLength = polygon.length; if (polygonLength < 3) { throw new GeometryTagError("A polygon must have three or more positions."); } if (polygon[0][0] !== polygon[polygonLength - 1][0] || polygon[0][1] !== polygon[polygonLength - 1][1]) { throw new GeometryTagError("First and last positions must be equivalent."); } this._polygon = []; for (let vertex of polygon) { if (vertex[0] < 0 || vertex[0] > 1 || vertex[1] < 0 || vertex[1] > 1) { throw new GeometryTagError("Basic coordinates of polygon must be on the interval [0, 1]."); } this._polygon.push(vertex.slice()); } this._holes = []; if (holes == null) { return; } for (let i = 0; i < holes.length; i++) { let hole = holes[i]; let holeLength = hole.length; if (holeLength < 3) { throw new GeometryTagError("A polygon hole must have three or more positions."); } if (hole[0][0] !== hole[holeLength - 1][0] || hole[0][1] !== hole[holeLength - 1][1]) { throw new GeometryTagError("First and last positions of hole must be equivalent."); } this._holes.push([]); for (let vertex of hole) { if (vertex[0] < 0 || vertex[0] > 1 || vertex[1] < 0 || vertex[1] > 1) { throw new GeometryTagError("Basic coordinates of hole must be on the interval [0, 1]."); } this._holes[i].push(vertex.slice()); } } } /** * Get polygon property. * @returns {Array>} Closed 2d polygon. */ get polygon() { return this._polygon; } /** * Get holes property. * @returns {Array>>} Holes of 2d polygon. */ get holes() { return this._holes; } /** * Add a vertex to the polygon by appending it after the last vertex. * * @param {Array} vertex - Vertex to add. * @ignore */ addVertex2d(vertex) { let clamped = [ Math.max(0, Math.min(1, vertex[0])), Math.max(0, Math.min(1, vertex[1])), ]; this._polygon.splice(this._polygon.length - 1, 0, clamped); this._notifyChanged$.next(this); } /** * Get the coordinates of a vertex from the polygon representation of the geometry. * * @param {number} index - Vertex index. * @returns {Array} Array representing the 2D basic coordinates of the vertex. * @ignore */ getVertex2d(index) { return this._polygon[index].slice(); } /** * Remove a vertex from the polygon. * * @param {number} index - The index of the vertex to remove. * @ignore */ removeVertex2d(index) { if (index < 0 || index >= this._polygon.length || this._polygon.length < 4) { throw new GeometryTagError("Index for removed vertex must be valid."); } if (index > 0 && index < this._polygon.length - 1) { this._polygon.splice(index, 1); } else { this._polygon.splice(0, 1); this._polygon.pop(); let closing = this._polygon[0].slice(); this._polygon.push(closing); } this._notifyChanged$.next(this); } /** @ignore */ setVertex2d(index, value, transform) { let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; if (index === 0 || index === this._polygon.length - 1) { this._polygon[0] = changed.slice(); this._polygon[this._polygon.length - 1] = changed.slice(); } else { this._polygon[index] = changed.slice(); } this._notifyChanged$.next(this); } /** @ignore */ setCentroid2d(value, transform) { let xs = this._polygon.map((point) => { return point[0]; }); let ys = this._polygon.map((point) => { return point[1]; }); let minX = Math.min.apply(Math, xs); let maxX = Math.max.apply(Math, xs); let minY = Math.min.apply(Math, ys); let maxY = Math.max.apply(Math, ys); let centroid = this.getCentroid2d(); let minTranslationX = -minX; let maxTranslationX = 1 - maxX; let minTranslationY = -minY; let maxTranslationY = 1 - maxY; let translationX = Math.max(minTranslationX, Math.min(maxTranslationX, value[0] - centroid[0])); let translationY = Math.max(minTranslationY, Math.min(maxTranslationY, value[1] - centroid[1])); for (let point of this._polygon) { point[0] += translationX; point[1] += translationY; } this._notifyChanged$.next(this); } /** @ignore */ getPoints3d(transform) { return this._getPoints3d(this._subsample(this._polygon), transform); } /** @ignore */ getVertex3d(index, transform) { return transform.unprojectBasic(this._polygon[index], 200); } /** @ignore */ getVertices2d() { return this._polygon.slice(); } /** @ignore */ getVertices3d(transform) { return this._getPoints3d(this._polygon, transform); } /** * Get a polygon representation of the 3D coordinates for the vertices of each hole * of the geometry. Line segments between vertices will possibly be subsampled * resulting in a larger number of points than the total number of vertices. * * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>>} Array of hole polygons in 3D world coordinates * representing the vertices of each hole of the geometry. * @ignore */ getHolePoints3d(transform) { return this._holes .map((hole2d) => { return this._getPoints3d(this._subsample(hole2d), transform); }); } /** * Get a polygon representation of the 3D coordinates for the vertices of each hole * of the geometry. * * @param {Transform} transform - The transform of the image related to the geometry. * @returns {Array>>} Array of hole polygons in 3D world coordinates * representing the vertices of each hole of the geometry. * @ignore */ getHoleVertices3d(transform) { return this._holes .map((hole2d) => { return this._getPoints3d(hole2d, transform); }); } /** @ignore */ getCentroid2d() { let polygon = this._polygon; let area = 0; let centroidX = 0; let centroidY = 0; for (let i = 0; i < polygon.length - 1; i++) { let xi = polygon[i][0]; let yi = polygon[i][1]; let xi1 = polygon[i + 1][0]; let yi1 = polygon[i + 1][1]; let a = xi * yi1 - xi1 * yi; area += a; centroidX += (xi + xi1) * a; centroidY += (yi + yi1) * a; } area /= 2; centroidX /= 6 * area; centroidY /= 6 * area; return [centroidX, centroidY]; } /** @ignore */ getCentroid3d(transform) { let centroid2d = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** @ignore */ get3dDomainTriangles3d(transform) { return this._triangulate(this._project(this._polygon, transform), this.getVertices3d(transform), this._holes .map((hole2d) => { return this._project(hole2d, transform); }), this.getHoleVertices3d(transform)); } /** @ignore */ getTriangles3d(transform) { if (isSpherical(transform.cameraType)) { return this._triangulateSpherical(this._polygon.slice(), this.holes.slice(), transform); } const points2d = this._project(this._subsample(this._polygon), transform); const points3d = this.getPoints3d(transform); const holes2d = this._holes .map((hole) => { return this._project(this._subsample(hole), transform); }); const holes3d = this.getHolePoints3d(transform); return this._triangulate(points2d, points3d, holes2d, holes3d); } /** @ignore */ getPoleOfInaccessibility2d() { return this._getPoleOfInaccessibility2d(this._polygon.slice()); } /** @ignore */ getPoleOfInaccessibility3d(transform) { let pole2d = this._getPoleOfInaccessibility2d(this._polygon.slice()); return transform.unprojectBasic(pole2d, 200); } _getPoints3d(points2d, transform) { return points2d .map((point) => { return transform.unprojectBasic(point, 200); }); } } class OutlineCreateTag extends CreateTag { constructor(geometry, options, transform, viewportCoords) { super(geometry, transform, viewportCoords); this._options = { color: options.color == null ? 0xFFFFFF : options.color }; this._createGlObjects(); } create() { if (this._geometry instanceof RectGeometry) { this._created$.next(this); } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 2); this._created$.next(this); } } dispose() { super.dispose(); this._disposeLine(this._outline); this._disposeObjects(); } getDOMObjects(camera, size) { const vNodes = []; const container = { offsetHeight: size.height, offsetWidth: size.width, }; const abort = (e) => { e.stopPropagation(); this._aborted$.next(this); }; if (this._geometry instanceof RectGeometry) { const anchorIndex = this._geometry.anchorIndex; const vertexIndex = anchorIndex === undefined ? 1 : anchorIndex; const [basicX, basicY] = this._geometry.getVertex2d(vertexIndex); const canvasPoint = this._viewportCoords.basicToCanvasSafe(basicX, basicY, container, this._transform, camera); if (canvasPoint != null) { const background = this._colorToBackground(this._options.color); const transform = this._canvasToTransform(canvasPoint); const pointProperties = { style: { background: background, transform: transform }, }; const completerProperties = { onclick: abort, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; const [firstVertexBasicX, firstVertexBasicY] = polygonGeometry.getVertex2d(0); const firstVertexCanvas = this._viewportCoords.basicToCanvasSafe(firstVertexBasicX, firstVertexBasicY, container, this._transform, camera); if (firstVertexCanvas != null) { const firstOnclick = polygonGeometry.polygon.length > 4 ? (e) => { e.stopPropagation(); polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 2); this._created$.next(this); } : abort; const transform = this._canvasToTransform(firstVertexCanvas); const completerProperties = { onclick: firstOnclick, style: { transform: transform }, }; const firstClass = polygonGeometry.polygon.length > 4 ? "mapillary-tag-completer" : "mapillary-tag-interactor"; vNodes.push(virtualDom.h("div." + firstClass, completerProperties, [])); } if (polygonGeometry.polygon.length > 3) { const [lastVertexBasicX, lastVertexBasicY] = polygonGeometry.getVertex2d(polygonGeometry.polygon.length - 3); const lastVertexCanvas = this._viewportCoords.basicToCanvasSafe(lastVertexBasicX, lastVertexBasicY, container, this._transform, camera); if (lastVertexCanvas != null) { const remove = (e) => { e.stopPropagation(); polygonGeometry.removeVertex2d(polygonGeometry.polygon.length - 3); }; const transform = this._canvasToTransform(lastVertexCanvas); const completerProperties = { onclick: remove, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-interactor", completerProperties, [])); } } const verticesBasic = polygonGeometry.polygon.slice(); verticesBasic.splice(-2, 2); for (const vertexBasic of verticesBasic) { const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasic[0], vertexBasic[1], container, this._transform, camera); if (vertexCanvas != null) { const background = this._colorToBackground(this._options.color); const transform = this._canvasToTransform(vertexCanvas); const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } } } return vNodes; } addPoint(point) { if (this._geometry instanceof RectGeometry) { const rectGeometry = this._geometry; if (!rectGeometry.validate(point)) { return; } this._created$.next(this); } else if (this._geometry instanceof PolygonGeometry) { const polygonGeometry = this._geometry; polygonGeometry.addVertex2d(point); } } _onGeometryChanged() { this._disposeLine(this._outline); this._disposeObjects(); this._createGlObjects(); } _disposeObjects() { this._outline = null; this._glObjects = []; } _createGlObjects() { const polygon3d = this._geometry instanceof RectGeometry ? this._geometry.getPoints3d(this._transform) : this._geometry.getVertices3d(this._transform); this._outline = this._createOutine(polygon3d, this._options.color); this._glObjects = [this._outline]; } } class TagCreator { constructor(component, navigator) { this._component = component; this._navigator = navigator; this._tagOperation$ = new Subject(); this._createPoints$ = new Subject(); this._createPolygon$ = new Subject(); this._createRect$ = new Subject(); this._delete$ = new Subject(); this._tag$ = this._tagOperation$.pipe(scan((tag, operation) => { return operation(tag); }, null), share()); this._replayedTag$ = this._tag$.pipe(publishReplay(1), refCount()); this._replayedTag$.subscribe(); this._createPoints$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new PointsGeometry([ [coord[0], coord[1]], [coord[0], coord[1]], ]); return new ExtremePointCreateTag(geometry, { color: conf.createColor, indicateCompleter: conf.indicatePointsCompleter, }, transform); }; })) .subscribe(this._tagOperation$); this._createRect$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new RectGeometry([ coord[0], coord[1], coord[0], coord[1], ]); return new OutlineCreateTag(geometry, { color: conf.createColor }, transform); }; })) .subscribe(this._tagOperation$); this._createPolygon$.pipe(withLatestFrom(this._component.configuration$, this._navigator.stateService.currentTransform$), map(([coord, conf, transform]) => { return () => { const geometry = new PolygonGeometry([ [coord[0], coord[1]], [coord[0], coord[1]], [coord[0], coord[1]], ]); return new OutlineCreateTag(geometry, { color: conf.createColor }, transform); }; })) .subscribe(this._tagOperation$); this._delete$.pipe(map(() => { return () => { return null; }; })) .subscribe(this._tagOperation$); } get createRect$() { return this._createRect$; } get createPolygon$() { return this._createPolygon$; } get createPoints$() { return this._createPoints$; } get delete$() { return this._delete$; } get tag$() { return this._tag$; } get replayedTag$() { return this._replayedTag$; } } class TagDOMRenderer { render(tags, createTag, atlas, camera, size) { let vNodes = []; for (const tag of tags) { vNodes = vNodes.concat(tag.getDOMObjects(atlas, camera, size)); } if (createTag != null) { vNodes = vNodes.concat(createTag.getDOMObjects(camera, size)); } return virtualDom.h("div.mapillary-tag-container", {}, vNodes); } clear() { return virtualDom.h("div", {}, []); } } class TagScene { constructor(scene, raycaster) { this._createTag = null; this._needsRender = false; this._raycaster = !!raycaster ? raycaster : new Raycaster(); this._scene = !!scene ? scene : new Scene(); this._objectTags = {}; this._retrievableObjects = []; this._tags = {}; } get needsRender() { return this._needsRender; } add(tags) { for (let tag of tags) { if (tag.tag.id in this._tags) { this._remove(tag.tag.id); } this._add(tag); } this._needsRender = true; } addCreateTag(tag) { for (const object of tag.glObjects) { this._scene.add(object); } this._createTag = { tag: tag, objects: tag.glObjects }; this._needsRender = true; } clear() { for (const id of Object.keys(this._tags)) { this._remove(id); } this._needsRender = false; } get(id) { return this.has(id) ? this._tags[id].tag : undefined; } has(id) { return id in this._tags; } hasCreateTag() { return this._createTag != null; } intersectObjects([viewportX, viewportY], camera) { this._raycaster.setFromCamera(new Vector2(viewportX, viewportY), camera); const intersects = this._raycaster.intersectObjects(this._retrievableObjects); const intersectedIds = []; for (const intersect of intersects) { if (intersect.object.uuid in this._objectTags) { intersectedIds.push(this._objectTags[intersect.object.uuid]); } } return intersectedIds; } remove(ids) { for (const id of ids) { this._remove(id); } this._needsRender = true; } removeAll() { for (const id of Object.keys(this._tags)) { this._remove(id); } this._needsRender = true; } removeCreateTag() { if (this._createTag == null) { return; } for (const object of this._createTag.objects) { this._scene.remove(object); } this._createTag.tag.dispose(); this._createTag = null; this._needsRender = true; } render(perspectiveCamera, renderer) { renderer.render(this._scene, perspectiveCamera); this._needsRender = false; } update() { this._needsRender = true; } updateCreateTagObjects(tag) { if (this._createTag.tag !== tag) { throw new Error("Create tags do not have the same reference."); } for (let object of this._createTag.objects) { this._scene.remove(object); } for (const object of tag.glObjects) { this._scene.add(object); } this._createTag.objects = tag.glObjects; this._needsRender = true; } updateObjects(tag) { const id = tag.tag.id; if (this._tags[id].tag !== tag) { throw new Error("Tags do not have the same reference."); } const tagObjects = this._tags[id]; this._removeObjects(tagObjects); delete this._tags[id]; this._add(tag); this._needsRender = true; } _add(tag) { const id = tag.tag.id; const tagObjects = { tag: tag, objects: [], retrievableObjects: [] }; this._tags[id] = tagObjects; for (const object of tag.getGLObjects()) { tagObjects.objects.push(object); this._scene.add(object); } for (const retrievableObject of tag.getRetrievableObjects()) { tagObjects.retrievableObjects.push(retrievableObject); this._retrievableObjects.push(retrievableObject); this._objectTags[retrievableObject.uuid] = tag.tag.id; } } _remove(id) { const tagObjects = this._tags[id]; this._removeObjects(tagObjects); tagObjects.tag.dispose(); delete this._tags[id]; } _removeObjects(tagObjects) { for (const object of tagObjects.objects) { this._scene.remove(object); } for (const retrievableObject of tagObjects.retrievableObjects) { const index = this._retrievableObjects.indexOf(retrievableObject); if (index !== -1) { this._retrievableObjects.splice(index, 1); } } } } /** * Enumeration for tag modes * @enum {number} * @readonly * @description Modes for the interaction in the tag component. */ exports.TagMode = void 0; (function (TagMode) { /** * Disables creating tags. */ TagMode[TagMode["Default"] = 0] = "Default"; /** * Create a point geometry through a click. */ TagMode[TagMode["CreatePoint"] = 1] = "CreatePoint"; /** * Create a points geometry through clicks. */ TagMode[TagMode["CreatePoints"] = 2] = "CreatePoints"; /** * Create a polygon geometry through clicks. */ TagMode[TagMode["CreatePolygon"] = 3] = "CreatePolygon"; /** * Create a rect geometry through clicks. */ TagMode[TagMode["CreateRect"] = 4] = "CreateRect"; /** * Create a rect geometry through drag. * * @description Claims the mouse which results in mouse handlers like * drag pan and scroll zoom becoming inactive. */ TagMode[TagMode["CreateRectDrag"] = 5] = "CreateRectDrag"; })(exports.TagMode || (exports.TagMode = {})); var TagOperation; (function (TagOperation) { TagOperation[TagOperation["None"] = 0] = "None"; TagOperation[TagOperation["Centroid"] = 1] = "Centroid"; TagOperation[TagOperation["Vertex"] = 2] = "Vertex"; })(TagOperation || (TagOperation = {})); class RenderTag { constructor(tag, transform, viewportCoords) { this._tag = tag; this._transform = transform; this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._glObjectsChanged$ = new Subject(); this._interact$ = new Subject(); } get glObjectsChanged$() { return this._glObjectsChanged$; } get interact$() { return this._interact$; } get tag() { return this._tag; } } class OutlineRenderTagBase extends RenderTag { constructor(tag, transform) { super(tag, transform); this._geometryChangedSubscription = this._tag.geometry.changed$ .subscribe(() => { this._onGeometryChanged(); }); this._changedSubscription = this._tag.changed$ .subscribe(() => { const glObjectsChanged = this._onTagChanged(); if (glObjectsChanged) { this._glObjectsChanged$.next(this); } }); } dispose() { this._changedSubscription.unsubscribe(); this._geometryChangedSubscription.unsubscribe(); } _colorToCss(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _createFill() { let triangles = this._getTriangles(); let positions = new Float32Array(triangles); let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); let material = new MeshBasicMaterial({ side: DoubleSide, transparent: true }); this._updateFillMaterial(material); return new Mesh(geometry, material); } _createLine(points3d) { let positions = this._getLinePositions(points3d); let geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); let material = new LineBasicMaterial(); this._updateLineBasicMaterial(material); const line = new Line(geometry, material); line.renderOrder = 1; return line; } _createOutline() { return this._createLine(this._getPoints3d()); } _disposeFill() { if (this._fill == null) { return; } this._fill.geometry.dispose(); this._fill.material.dispose(); this._fill = null; } _disposeOutline() { if (this._outline == null) { return; } this._outline.geometry.dispose(); this._outline.material.dispose(); this._outline = null; } _getLinePositions(points3d) { let length = points3d.length; let positions = new Float32Array(length * 3); for (let i = 0; i < length; ++i) { let index = 3 * i; let position = points3d[i]; positions[index + 0] = position[0]; positions[index + 1] = position[1]; positions[index + 2] = position[2]; } return positions; } _interact(operation, cursor, vertexIndex) { return (e) => { let offsetX = e.offsetX - e.target.offsetWidth / 2; let offsetY = e.offsetY - e.target.offsetHeight / 2; this._interact$.next({ cursor: cursor, offsetX: offsetX, offsetY: offsetY, operation: operation, tag: this._tag, vertexIndex: vertexIndex, }); }; } _updateFillGeometry() { let triangles = this._getTriangles(); let positions = new Float32Array(triangles); let geometry = this._fill.geometry; let attribute = geometry.getAttribute("position"); if (attribute.array.length === positions.length) { attribute.set(positions); attribute.needsUpdate = true; } else { geometry.deleteAttribute("position"); geometry.setAttribute("position", new BufferAttribute(positions, 3)); } geometry.computeBoundingSphere(); } _updateLine(line, points3d) { let positions = this._getLinePositions(points3d); let geometry = line.geometry; let attribute = geometry.getAttribute("position"); attribute.set(positions); attribute.needsUpdate = true; geometry.computeBoundingSphere(); } _updateOulineGeometry() { this._updateLine(this._outline, this._getPoints3d()); } } /** * @class OutlineRenderTag * @classdesc Tag visualizing the properties of an OutlineTag. */ class ExtremePointRenderTag extends OutlineRenderTagBase { constructor(tag, transform) { super(tag, transform); this._rectGeometry = new RectGeometry(this._tag.geometry.getRect2d(transform)); this._fill = !isSpherical(transform.cameraType) ? this._createFill() : null; this._outline = this._tag.lineWidth >= 1 ? this._createOutline() : null; } dispose() { super.dispose(); this._disposeFill(); this._disposeOutline(); } getDOMObjects(atlas, camera, size) { const vNodes = []; const container = { offsetHeight: size.height, offsetWidth: size.width, }; if (!this._tag.editable) { return vNodes; } const lineColor = this._colorToCss(this._tag.lineColor); const points2d = this._tag.geometry.getPoints2d(); for (let i = 0; i < points2d.length; i++) { const [vertexBasicX, vertexBasicY] = points2d[i]; const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasicX, vertexBasicY, container, this._transform, camera); if (vertexCanvas == null) { continue; } const cursor = "crosshair"; const interact = this._interact(TagOperation.Vertex, cursor, i); const vertexCanvasX = Math.round(vertexCanvas[0]); const vertexCanvasY = Math.round(vertexCanvas[1]); const transform = `translate(-50%, -50%) translate(${vertexCanvasX}px,${vertexCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform, cursor: cursor }, }; vNodes.push(virtualDom.h("div.mapillary-tag-resizer", properties, [])); if (!this._tag.indicateVertices) { continue; } const pointProperties = { style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { const glObjects = []; if (this._fill != null) { glObjects.push(this._fill); } if (this._outline != null) { glObjects.push(this._outline); } return glObjects; } getRetrievableObjects() { return this._fill != null ? [this._fill] : []; } _onGeometryChanged() { this._rectGeometry = new RectGeometry(this._tag.geometry.getRect2d(this._transform)); if (this._fill != null) { this._updateFillGeometry(); } if (this._outline != null) { this._updateOulineGeometry(); } } _onTagChanged() { let glObjectsChanged = false; if (this._fill != null) { this._updateFillMaterial(this._fill.material); } if (this._outline == null) { if (this._tag.lineWidth >= 1) { this._outline = this._createOutline(); glObjectsChanged = true; } } else { this._updateOutlineMaterial(); } return glObjectsChanged; } _getPoints3d() { return this._rectGeometry.getPoints3d(this._transform); } _getTriangles() { return this._rectGeometry.getTriangles3d(this._transform); } _updateFillMaterial(material) { material.color = new Color(this._tag.fillColor); material.opacity = this._tag.fillOpacity; material.needsUpdate = true; } _updateLineBasicMaterial(material) { material.color = new Color(this._tag.lineColor); material.linewidth = Math.max(this._tag.lineWidth, 1); material.visible = this._tag.lineWidth >= 1 && this._tag.lineOpacity > 0; material.opacity = this._tag.lineOpacity; material.transparent = this._tag.lineOpacity < 1; material.needsUpdate = true; } _updateOutlineMaterial() { let material = this._outline.material; this._updateLineBasicMaterial(material); } } /** * @class Tag * @abstract * @classdesc Abstract class representing the basic functionality of for a tag. */ class Tag extends EventEmitter { /** * Create a tag. * * @constructor * @param {string} id * @param {Geometry} geometry */ constructor(id, geometry) { super(); this._id = id; this._geometry = geometry; this._notifyChanged$ = new Subject(); this._notifyChanged$ .subscribe((t) => { const type = "tag"; const event = { target: this, type, }; this.fire(type, event); }); this._geometry.changed$ .subscribe((g) => { const type = "geometry"; const event = { target: this, type, }; this.fire(type, event); }); } /** * Get id property. * @returns {string} */ get id() { return this._id; } /** * Get geometry property. * @returns {Geometry} The geometry of the tag. */ get geometry() { return this._geometry; } /** * Get changed observable. * @returns {Observable} * @ignore */ get changed$() { return this._notifyChanged$; } /** * Get geometry changed observable. * @returns {Observable} * @ignore */ get geometryChanged$() { return this._geometry.changed$.pipe(map(() => { return this; }), share()); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } } /** * @class ExtremePointTag * * @classdesc Tag holding properties for visualizing a extreme points * and their outline. * * @example * ```js * var geometry = new PointsGeometry([[0.3, 0.3], [0.5, 0.4]]); * var tag = new ExtremePointTag( * "id-1", * geometry * { editable: true, lineColor: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class ExtremePointTag extends Tag { /** * Create an extreme point tag. * * @override * @constructor * @param {string} id - Unique identifier of the tag. * @param {PointsGeometry} geometry - Geometry defining points of tag. * @param {ExtremePointTagOptions} options - Options defining the visual appearance and * behavior of the extreme point tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; this._editable = options.editable == null ? false : options.editable; this._fillColor = options.fillColor == null ? 0xFFFFFF : options.fillColor; this._fillOpacity = options.fillOpacity == null ? 0.0 : options.fillOpacity; this._indicateVertices = options.indicateVertices == null ? true : options.indicateVertices; this._lineColor = options.lineColor == null ? 0xFFFFFF : options.lineColor; this._lineOpacity = options.lineOpacity == null ? 1 : options.lineOpacity; this._lineWidth = options.lineWidth == null ? 1 : options.lineWidth; } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { this._editable = value; this._notifyChanged$.next(this); } /** * Get fill color property. * @returns {number} */ get fillColor() { return this._fillColor; } /** * Set fill color property. * @param {number} * * @fires changed */ set fillColor(value) { this._fillColor = value; this._notifyChanged$.next(this); } /** * Get fill opacity property. * @returns {number} */ get fillOpacity() { return this._fillOpacity; } /** * Set fill opacity property. * @param {number} * * @fires changed */ set fillOpacity(value) { this._fillOpacity = value; this._notifyChanged$.next(this); } /** @inheritdoc */ get geometry() { return this._geometry; } /** * Get indicate vertices property. * @returns {boolean} Value indicating if vertices should be indicated * when tag is editable. */ get indicateVertices() { return this._indicateVertices; } /** * Set indicate vertices property. * @param {boolean} * * @fires changed */ set indicateVertices(value) { this._indicateVertices = value; this._notifyChanged$.next(this); } /** * Get line color property. * @returns {number} */ get lineColor() { return this._lineColor; } /** * Set line color property. * @param {number} * * @fires changed */ set lineColor(value) { this._lineColor = value; this._notifyChanged$.next(this); } /** * Get line opacity property. * @returns {number} */ get lineOpacity() { return this._lineOpacity; } /** * Set line opacity property. * @param {number} * * @fires changed */ set lineOpacity(value) { this._lineOpacity = value; this._notifyChanged$.next(this); } /** * Get line width property. * @returns {number} */ get lineWidth() { return this._lineWidth; } /** * Set line width property. * @param {number} * * @fires changed */ set lineWidth(value) { this._lineWidth = value; this._notifyChanged$.next(this); } /** * Set options for tag. * * @description Sets all the option properties provided and keeps * the rest of the values as is. * * @param {ExtremePointTagOptions} options - Extreme point tag options * * @fires changed */ setOptions(options) { this._editable = options.editable == null ? this._editable : options.editable; this._indicateVertices = options.indicateVertices == null ? this._indicateVertices : options.indicateVertices; this._lineColor = options.lineColor == null ? this._lineColor : options.lineColor; this._lineWidth = options.lineWidth == null ? this._lineWidth : options.lineWidth; this._fillColor = options.fillColor == null ? this._fillColor : options.fillColor; this._fillOpacity = options.fillOpacity == null ? this._fillOpacity : options.fillOpacity; this._notifyChanged$.next(this); } } /** * Enumeration for tag domains. * @enum {number} * @readonly * @description Defines where lines between two vertices are treated * as straight. * * Only applicable for polygons. For rectangles lines between * vertices are always treated as straight in the distorted 2D * projection and bended in the undistorted 3D space. */ exports.TagDomain = void 0; (function (TagDomain) { /** * Treats lines between two vertices as straight in the * distorted 2D projection, i.e. on the image. If the image * is distorted this will result in bended lines when rendered * in the undistorted 3D space. */ TagDomain[TagDomain["TwoDimensional"] = 0] = "TwoDimensional"; /** * Treats lines as straight in the undistorted 3D space. If the * image is distorted this will result in bended lines when rendered * on the distorted 2D projection of the image. */ TagDomain[TagDomain["ThreeDimensional"] = 1] = "ThreeDimensional"; })(exports.TagDomain || (exports.TagDomain = {})); /** * @class OutlineRenderTag * @classdesc Tag visualizing the properties of an OutlineTag. */ class OutlineRenderTag extends OutlineRenderTagBase { constructor(tag, transform) { super(tag, transform); this._fill = !isSpherical(transform.cameraType) ? this._createFill() : tag.domain === exports.TagDomain.TwoDimensional && tag.geometry instanceof PolygonGeometry ? this._createFill() : null; this._holes = this._tag.lineWidth >= 1 ? this._createHoles() : []; this._outline = this._tag.lineWidth >= 1 ? this._createOutline() : null; } dispose() { super.dispose(); this._disposeFill(); this._disposeHoles(); this._disposeOutline(); } getDOMObjects(atlas, camera, size) { const vNodes = []; const isRect = this._tag.geometry instanceof RectGeometry; const isPerspective = !isSpherical(this._transform.cameraType); const container = { offsetHeight: size.height, offsetWidth: size.width, }; if (this._tag.icon != null && (isRect || isPerspective)) { const [iconBasicX, iconBasicY] = this._tag.geometry instanceof RectGeometry ? this._tag.geometry.getVertex2d(this._tag.iconIndex) : this._tag.geometry.getPoleOfInaccessibility2d(); const iconCanvas = this._viewportCoords.basicToCanvasSafe(iconBasicX, iconBasicY, container, this._transform, camera); if (iconCanvas != null) { const interact = () => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: this._tag }); }; if (atlas.loaded) { const sprite = atlas.getDOMSprite(this._tag.icon, this._tag.iconFloat); const iconCanvasX = Math.round(iconCanvas[0]); const iconCanvasY = Math.round(iconCanvas[1]); const transform = `translate(${iconCanvasX}px,${iconCanvasY}px)`; const click = (e) => { e.stopPropagation(); this._tag.click$.next(this._tag); }; const properties = { onclick: click, onpointerdown: interact, style: { transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-symbol", properties, [sprite])); } } } else if (this._tag.text != null && (isRect || isPerspective)) { const [textBasicX, textBasicY] = this._tag.geometry instanceof RectGeometry ? this._tag.geometry.getVertex2d(3) : this._tag.geometry.getPoleOfInaccessibility2d(); const textCanvas = this._viewportCoords.basicToCanvasSafe(textBasicX, textBasicY, container, this._transform, camera); if (textCanvas != null) { const textCanvasX = Math.round(textCanvas[0]); const textCanvasY = Math.round(textCanvas[1]); const transform = this._tag.geometry instanceof RectGeometry ? `translate(${textCanvasX}px,${textCanvasY}px)` : `translate(-50%, -50%) translate(${textCanvasX}px,${textCanvasY}px)`; const interact = () => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: this._tag }); }; const properties = { onpointerdown: interact, style: { color: this._colorToCss(this._tag.textColor), transform: transform, }, textContent: this._tag.text, }; vNodes.push(virtualDom.h("span.mapillary-tag-symbol", properties, [])); } } if (!this._tag.editable) { return vNodes; } const lineColor = this._colorToCss(this._tag.lineColor); if (this._tag.geometry instanceof RectGeometry) { const [centroidBasicX, centroidBasicY] = this._tag.geometry.getCentroid2d(); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidBasicX, centroidBasicY, container, this._transform, camera); if (centroidCanvas != null) { const interact = this._interact(TagOperation.Centroid, "move"); const centroidCanvasX = Math.round(centroidCanvas[0]); const centroidCanvasY = Math.round(centroidCanvas[1]); const transform = `translate(-50%, -50%) translate(${centroidCanvasX}px,${centroidCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-mover", properties, [])); } } const vertices2d = this._tag.geometry.getVertices2d(); for (let i = 0; i < vertices2d.length - 1; i++) { if (isRect && ((this._tag.icon != null && i === this._tag.iconIndex) || (this._tag.icon == null && this._tag.text != null && i === 3))) { continue; } const [vertexBasicX, vertexBasicY] = vertices2d[i]; const vertexCanvas = this._viewportCoords.basicToCanvasSafe(vertexBasicX, vertexBasicY, container, this._transform, camera); if (vertexCanvas == null) { continue; } const cursor = isRect ? i % 2 === 0 ? "nesw-resize" : "nwse-resize" : "crosshair"; const interact = this._interact(TagOperation.Vertex, cursor, i); const vertexCanvasX = Math.round(vertexCanvas[0]); const vertexCanvasY = Math.round(vertexCanvas[1]); const transform = `translate(-50%, -50%) translate(${vertexCanvasX}px,${vertexCanvasY}px)`; const properties = { onpointerdown: interact, style: { background: lineColor, transform: transform, cursor: cursor }, }; vNodes.push(virtualDom.h("div.mapillary-tag-resizer", properties, [])); if (!this._tag.indicateVertices) { continue; } const pointProperties = { style: { background: lineColor, transform: transform }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { const glObjects = []; if (this._fill != null) { glObjects.push(this._fill); } for (const hole of this._holes) { glObjects.push(hole); } if (this._outline != null) { glObjects.push(this._outline); } return glObjects; } getRetrievableObjects() { return this._fill != null ? [this._fill] : []; } _onGeometryChanged() { if (this._fill != null) { this._updateFillGeometry(); } if (this._holes.length > 0) { this._updateHoleGeometries(); } if (this._outline != null) { this._updateOulineGeometry(); } } _onTagChanged() { let glObjectsChanged = false; if (this._fill != null) { this._updateFillMaterial(this._fill.material); } if (this._outline == null) { if (this._tag.lineWidth >= 1) { this._holes = this._createHoles(); this._outline = this._createOutline(); glObjectsChanged = true; } } else { this._updateHoleMaterials(); this._updateOutlineMaterial(); } return glObjectsChanged; } _getPoints3d() { return this._in3dDomain() ? this._tag.geometry.getVertices3d(this._transform) : this._tag.geometry.getPoints3d(this._transform); } _getTriangles() { return this._in3dDomain() ? this._tag.geometry.get3dDomainTriangles3d(this._transform) : this._tag.geometry.getTriangles3d(this._transform); } _updateFillMaterial(material) { material.color = new Color(this._tag.fillColor); material.opacity = this._tag.fillOpacity; material.needsUpdate = true; } _updateLineBasicMaterial(material) { material.color = new Color(this._tag.lineColor); material.linewidth = Math.max(this._tag.lineWidth, 1); material.visible = this._tag.lineWidth >= 1 && this._tag.lineOpacity > 0; material.opacity = this._tag.lineOpacity; material.transparent = this._tag.lineOpacity < 1; material.needsUpdate = true; } _createHoles() { let holes = []; if (this._tag.geometry instanceof PolygonGeometry) { let holes3d = this._getHoles3d(); for (let holePoints3d of holes3d) { let hole = this._createLine(holePoints3d); holes.push(hole); } } return holes; } _disposeHoles() { for (let hole of this._holes) { hole.geometry.dispose(); hole.material.dispose(); } this._holes = []; } _getHoles3d() { const polygonGeometry = this._tag.geometry; return this._in3dDomain() ? polygonGeometry.getHoleVertices3d(this._transform) : polygonGeometry.getHolePoints3d(this._transform); } _in3dDomain() { return this._tag.geometry instanceof PolygonGeometry && this._tag.domain === exports.TagDomain.ThreeDimensional; } _updateHoleGeometries() { let holes3d = this._getHoles3d(); if (holes3d.length !== this._holes.length) { throw new Error("Changing the number of holes is not supported."); } for (let i = 0; i < this._holes.length; i++) { let holePoints3d = holes3d[i]; let hole = this._holes[i]; this._updateLine(hole, holePoints3d); } } _updateHoleMaterials() { for (const hole of this._holes) { this._updateLineBasicMaterial(hole.material); } } _updateOutlineMaterial() { this._updateLineBasicMaterial(this._outline.material); } } /** * Enumeration for alignments * @enum {number} * @readonly */ exports.Alignment = void 0; (function (Alignment) { /** * Align to bottom */ Alignment[Alignment["Bottom"] = 0] = "Bottom"; /** * Align to bottom left */ Alignment[Alignment["BottomLeft"] = 1] = "BottomLeft"; /** * Align to bottom right */ Alignment[Alignment["BottomRight"] = 2] = "BottomRight"; /** * Align to center */ Alignment[Alignment["Center"] = 3] = "Center"; /** * Align to left */ Alignment[Alignment["Left"] = 4] = "Left"; /** * Align to right */ Alignment[Alignment["Right"] = 5] = "Right"; /** * Align to top */ Alignment[Alignment["Top"] = 6] = "Top"; /** * Align to top left */ Alignment[Alignment["TopLeft"] = 7] = "TopLeft"; /** * Align to top right */ Alignment[Alignment["TopRight"] = 8] = "TopRight"; })(exports.Alignment || (exports.Alignment = {})); /** * @class OutlineTag * * @classdesc Tag holding properties for visualizing a geometry outline. * * @example * ```js * var geometry = new RectGeometry([0.3, 0.3, 0.5, 0.4]); * var tag = new OutlineTag( * "id-1", * geometry * { editable: true, lineColor: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class OutlineTag extends Tag { /** * Create an outline tag. * * @override * @constructor * @param {string} id - Unique identifier of the tag. * @param {VertexGeometry} geometry - Geometry defining vertices of tag. * @param {OutlineTagOptions} options - Options defining the visual appearance and * behavior of the outline tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; const domain = options.domain != null && geometry instanceof PolygonGeometry ? options.domain : exports.TagDomain.TwoDimensional; const twoDimensionalPolygon = this._twoDimensionalPolygon(domain, geometry); this._domain = domain; this._editable = options.editable == null || twoDimensionalPolygon ? false : options.editable; this._fillColor = options.fillColor == null ? 0xFFFFFF : options.fillColor; this._fillOpacity = options.fillOpacity == null ? 0.0 : options.fillOpacity; this._icon = options.icon === undefined ? null : options.icon; this._iconFloat = options.iconFloat == null ? exports.Alignment.Center : options.iconFloat; this._iconIndex = options.iconIndex == null ? 3 : options.iconIndex; this._indicateVertices = options.indicateVertices == null ? true : options.indicateVertices; this._lineColor = options.lineColor == null ? 0xFFFFFF : options.lineColor; this._lineOpacity = options.lineOpacity == null ? 1 : options.lineOpacity; this._lineWidth = options.lineWidth == null ? 1 : options.lineWidth; this._text = options.text === undefined ? null : options.text; this._textColor = options.textColor == null ? 0xFFFFFF : options.textColor; this._click$ = new Subject(); this._click$ .subscribe(() => { const type = "click"; const event = { target: this, type, }; this.fire(type, event); }); } /** * Click observable. * * @description An observable emitting the tag when the icon of the * tag has been clicked. * * @returns {Observable} */ get click$() { return this._click$; } /** * Get domain property. * * @description Readonly property that can only be set in constructor. * * @returns Value indicating the domain of the tag. */ get domain() { return this._domain; } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { if (this._twoDimensionalPolygon(this._domain, this._geometry)) { return; } this._editable = value; this._notifyChanged$.next(this); } /** * Get fill color property. * @returns {number} */ get fillColor() { return this._fillColor; } /** * Set fill color property. * @param {number} * * @fires changed */ set fillColor(value) { this._fillColor = value; this._notifyChanged$.next(this); } /** * Get fill opacity property. * @returns {number} */ get fillOpacity() { return this._fillOpacity; } /** * Set fill opacity property. * @param {number} * * @fires changed */ set fillOpacity(value) { this._fillOpacity = value; this._notifyChanged$.next(this); } /** @inheritdoc */ get geometry() { return this._geometry; } /** * Get icon property. * @returns {string} */ get icon() { return this._icon; } /** * Set icon property. * @param {string} * * @fires changed */ set icon(value) { this._icon = value; this._notifyChanged$.next(this); } /** * Get icon float property. * @returns {Alignment} */ get iconFloat() { return this._iconFloat; } /** * Set icon float property. * @param {Alignment} * * @fires changed */ set iconFloat(value) { this._iconFloat = value; this._notifyChanged$.next(this); } /** * Get icon index property. * @returns {number} */ get iconIndex() { return this._iconIndex; } /** * Set icon index property. * @param {number} * * @fires changed */ set iconIndex(value) { this._iconIndex = value; this._notifyChanged$.next(this); } /** * Get indicate vertices property. * @returns {boolean} Value indicating if vertices should be indicated * when tag is editable. */ get indicateVertices() { return this._indicateVertices; } /** * Set indicate vertices property. * @param {boolean} * * @fires changed */ set indicateVertices(value) { this._indicateVertices = value; this._notifyChanged$.next(this); } /** * Get line color property. * @returns {number} */ get lineColor() { return this._lineColor; } /** * Set line color property. * @param {number} * * @fires changed */ set lineColor(value) { this._lineColor = value; this._notifyChanged$.next(this); } /** * Get line opacity property. * @returns {number} */ get lineOpacity() { return this._lineOpacity; } /** * Set line opacity property. * @param {number} * * @fires changed */ set lineOpacity(value) { this._lineOpacity = value; this._notifyChanged$.next(this); } /** * Get line width property. * @returns {number} */ get lineWidth() { return this._lineWidth; } /** * Set line width property. * @param {number} * * @fires changed */ set lineWidth(value) { this._lineWidth = value; this._notifyChanged$.next(this); } /** * Get text property. * @returns {string} */ get text() { return this._text; } /** * Set text property. * @param {string} * * @fires changed */ set text(value) { this._text = value; this._notifyChanged$.next(this); } /** * Get text color property. * @returns {number} */ get textColor() { return this._textColor; } /** * Set text color property. * @param {number} * * @fires changed */ set textColor(value) { this._textColor = value; this._notifyChanged$.next(this); } fire(type, event) { super.fire(type, event); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Set options for tag. * * @description Sets all the option properties provided and keeps * the rest of the values as is. * * @param {OutlineTagOptions} options - Outline tag options * * @fires changed */ setOptions(options) { const twoDimensionalPolygon = this._twoDimensionalPolygon(this._domain, this._geometry); this._editable = twoDimensionalPolygon || options.editable == null ? this._editable : options.editable; this._icon = options.icon === undefined ? this._icon : options.icon; this._iconFloat = options.iconFloat == null ? this._iconFloat : options.iconFloat; this._iconIndex = options.iconIndex == null ? this._iconIndex : options.iconIndex; this._indicateVertices = options.indicateVertices == null ? this._indicateVertices : options.indicateVertices; this._lineColor = options.lineColor == null ? this._lineColor : options.lineColor; this._lineWidth = options.lineWidth == null ? this._lineWidth : options.lineWidth; this._fillColor = options.fillColor == null ? this._fillColor : options.fillColor; this._fillOpacity = options.fillOpacity == null ? this._fillOpacity : options.fillOpacity; this._text = options.text === undefined ? this._text : options.text; this._textColor = options.textColor == null ? this._textColor : options.textColor; this._notifyChanged$.next(this); } _twoDimensionalPolygon(domain, geometry) { return domain !== exports.TagDomain.ThreeDimensional && geometry instanceof PolygonGeometry; } } /** * @class SpotRenderTag * @classdesc Tag visualizing the properties of a SpotTag. */ class SpotRenderTag extends RenderTag { dispose() { } getDOMObjects(atlas, camera, size) { const tag = this._tag; const container = { offsetHeight: size.height, offsetWidth: size.width, }; const vNodes = []; const [centroidBasicX, centroidBasicY] = tag.geometry.getCentroid2d(); const centroidCanvas = this._viewportCoords.basicToCanvasSafe(centroidBasicX, centroidBasicY, container, this._transform, camera); if (centroidCanvas != null) { const interactNone = (e) => { this._interact$.next({ offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: tag }); }; const canvasX = Math.round(centroidCanvas[0]); const canvasY = Math.round(centroidCanvas[1]); if (tag.icon != null) { if (atlas.loaded) { const sprite = atlas.getDOMSprite(tag.icon, exports.Alignment.Bottom); const iconTransform = `translate(${canvasX}px,${canvasY + 8}px)`; const properties = { onpointerdown: interactNone, style: { pointerEvents: "all", transform: iconTransform, }, }; vNodes.push(virtualDom.h("div", properties, [sprite])); } } else if (tag.text != null) { const textTransform = `translate(-50%,0%) translate(${canvasX}px,${canvasY + 8}px)`; const properties = { onpointerdown: interactNone, style: { color: this._colorToCss(tag.textColor), transform: textTransform, }, textContent: tag.text, }; vNodes.push(virtualDom.h("span.mapillary-tag-symbol", properties, [])); } const interact = this._interact(TagOperation.Centroid, tag, "move"); const background = this._colorToCss(tag.color); const transform = `translate(-50%,-50%) translate(${canvasX}px,${canvasY}px)`; if (tag.editable) { let interactorProperties = { onpointerdown: interact, style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-spot-interactor", interactorProperties, [])); } const pointProperties = { style: { background: background, transform: transform, }, }; vNodes.push(virtualDom.h("div.mapillary-tag-vertex", pointProperties, [])); } return vNodes; } getGLObjects() { return []; } getRetrievableObjects() { return []; } _colorToCss(color) { return "#" + ("000000" + color.toString(16)).substr(-6); } _interact(operation, tag, cursor, vertexIndex) { return (e) => { const offsetX = e.offsetX - e.target.offsetWidth / 2; const offsetY = e.offsetY - e.target.offsetHeight / 2; this._interact$.next({ cursor: cursor, offsetX: offsetX, offsetY: offsetY, operation: operation, tag: tag, vertexIndex: vertexIndex, }); }; } } /** * @class SpotTag * * @classdesc Tag holding properties for visualizing the centroid of a geometry. * * @example * ```js * var geometry = new PointGeometry([0.3, 0.3]); * var tag = new SpotTag( * "id-1", * geometry * { editable: true, color: 0xff0000 }); * * tagComponent.add([tag]); * ``` */ class SpotTag extends Tag { /** * Create a spot tag. * * @override * @constructor * @param {string} id * @param {Geometry} geometry * @param {IOutlineTagOptions} options - Options defining the visual appearance and * behavior of the spot tag. */ constructor(id, geometry, options) { super(id, geometry); options = !!options ? options : {}; this._color = options.color == null ? 0xFFFFFF : options.color; this._editable = options.editable == null ? false : options.editable; this._icon = options.icon === undefined ? null : options.icon; this._text = options.text === undefined ? null : options.text; this._textColor = options.textColor == null ? 0xFFFFFF : options.textColor; } /** * Get color property. * @returns {number} The color of the spot as a hexagonal number; */ get color() { return this._color; } /** * Set color property. * @param {number} * * @fires changed */ set color(value) { this._color = value; this._notifyChanged$.next(this); } /** * Get editable property. * @returns {boolean} Value indicating if tag is editable. */ get editable() { return this._editable; } /** * Set editable property. * @param {boolean} * * @fires changed */ set editable(value) { this._editable = value; this._notifyChanged$.next(this); } /** * Get icon property. * @returns {string} */ get icon() { return this._icon; } /** * Set icon property. * @param {string} * * @fires changed */ set icon(value) { this._icon = value; this._notifyChanged$.next(this); } /** * Get text property. * @returns {string} */ get text() { return this._text; } /** * Set text property. * @param {string} * * @fires changed */ set text(value) { this._text = value; this._notifyChanged$.next(this); } /** * Get text color property. * @returns {number} */ get textColor() { return this._textColor; } /** * Set text color property. * @param {number} * * @fires changed */ set textColor(value) { this._textColor = value; this._notifyChanged$.next(this); } /** * Set options for tag. * * @description Sets all the option properties provided and keps * the rest of the values as is. * * @param {SpotTagOptions} options - Spot tag options * * @fires changed */ setOptions(options) { this._color = options.color == null ? this._color : options.color; this._editable = options.editable == null ? this._editable : options.editable; this._icon = options.icon === undefined ? this._icon : options.icon; this._text = options.text === undefined ? this._text : options.text; this._textColor = options.textColor == null ? this._textColor : options.textColor; this._notifyChanged$.next(this); } } class TagSet { constructor() { this._active = false; this._hash = {}; this._hashDeactivated = {}; this._notifyChanged$ = new Subject(); } get active() { return this._active; } get changed$() { return this._notifyChanged$; } activate(transform) { if (this._active) { return; } for (const id in this._hashDeactivated) { if (!this._hashDeactivated.hasOwnProperty(id)) { continue; } const tag = this._hashDeactivated[id]; this._add(tag, transform); } this._hashDeactivated = {}; this._active = true; this._notifyChanged$.next(this); } deactivate() { if (!this._active) { return; } for (const id in this._hash) { if (!this._hash.hasOwnProperty(id)) { continue; } this._hashDeactivated[id] = this._hash[id].tag; } this._hash = {}; this._active = false; } add(tags, transform) { this._assertActivationState(true); for (const tag of tags) { this._add(tag, transform); } this._notifyChanged$.next(this); } addDeactivated(tags) { this._assertActivationState(false); for (const tag of tags) { if (!(tag instanceof OutlineTag || tag instanceof SpotTag || tag instanceof ExtremePointTag)) { throw new Error("Tag type not supported"); } this._hashDeactivated[tag.id] = tag; } } get(id) { return this.has(id) ? this._hash[id] : undefined; } getAll() { const hash = this._hash; return Object.keys(hash) .map((id) => { return hash[id]; }); } getAllDeactivated() { const hashDeactivated = this._hashDeactivated; return Object.keys(hashDeactivated) .map((id) => { return hashDeactivated[id]; }); } getDeactivated(id) { return this.hasDeactivated(id) ? this._hashDeactivated[id] : undefined; } has(id) { return id in this._hash; } hasDeactivated(id) { return id in this._hashDeactivated; } remove(ids) { this._assertActivationState(true); const hash = this._hash; for (const id of ids) { if (!(id in hash)) { continue; } delete hash[id]; } this._notifyChanged$.next(this); } removeAll() { this._assertActivationState(true); this._hash = {}; this._notifyChanged$.next(this); } removeAllDeactivated() { this._assertActivationState(false); this._hashDeactivated = {}; } removeDeactivated(ids) { this._assertActivationState(false); const hashDeactivated = this._hashDeactivated; for (const id of ids) { if (!(id in hashDeactivated)) { continue; } delete hashDeactivated[id]; } } _add(tag, transform) { if (tag instanceof OutlineTag) { this._hash[tag.id] = new OutlineRenderTag(tag, transform); } else if (tag instanceof SpotTag) { this._hash[tag.id] = new SpotRenderTag(tag, transform); } else if (tag instanceof ExtremePointTag) { this._hash[tag.id] = new ExtremePointRenderTag(tag, transform); } else { throw new Error("Tag type not supported"); } } _assertActivationState(should) { if (should !== this._active) { throw new Error("Tag set not in correct state for operation."); } } } /** * @class PointGeometry * * @classdesc Represents a point geometry in the 2D basic image coordinate system. * * @example * ```js * var basicPoint = [0.5, 0.7]; * var pointGeometry = new PointGeometry(basicPoint); * ``` */ class PointGeometry extends Geometry { /** * Create a point geometry. * * @constructor * @param {Array} point - An array representing the basic coordinates of * the point. * * @throws {GeometryTagError} Point coordinates must be valid basic coordinates. */ constructor(point) { super(); let x = point[0]; let y = point[1]; if (x < 0 || x > 1 || y < 0 || y > 1) { throw new GeometryTagError("Basic coordinates must be on the interval [0, 1]."); } this._point = point.slice(); } /** * Get point property. * @returns {Array} Array representing the basic coordinates of the point. */ get point() { return this._point; } /** * Get the 2D basic coordinates for the centroid of the point, i.e. the 2D * basic coordinates of the point itself. * * @returns {Array} 2D basic coordinates representing the centroid. * @ignore */ getCentroid2d() { return this._point.slice(); } /** * Get the 3D world coordinates for the centroid of the point, i.e. the 3D * world coordinates of the point itself. * * @param {Transform} transform - The transform of the image related to the point. * @returns {Array} 3D world coordinates representing the centroid. * @ignore */ getCentroid3d(transform) { return transform.unprojectBasic(this._point, 200); } /** * Set the centroid of the point, i.e. the point coordinates. * * @param {Array} value - The new value of the centroid. * @param {Transform} transform - The transform of the image related to the point. * @ignore */ setCentroid2d(value, transform) { let changed = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; this._point[0] = changed[0]; this._point[1] = changed[1]; this._notifyChanged$.next(this); } } class TagHandlerBase extends HandlerBase { constructor(component, container, navigator, viewportCoords) { super(component, container, navigator); this._name = `${this._component.name}-${this._getNameExtension()}`; this._viewportCoords = viewportCoords; } _getConfiguration(enable) { return {}; } _mouseEventToBasic(event, element, camera, transform, offsetX, offsetY) { offsetX = offsetX != null ? offsetX : 0; offsetY = offsetY != null ? offsetY : 0; const [canvasX, canvasY] = this._viewportCoords.canvasPosition(event, element); const basic = this._viewportCoords.canvasToBasic(canvasX - offsetX, canvasY - offsetY, element, transform, camera.perspective); return basic; } } class CreateHandlerBase extends TagHandlerBase { constructor(component, container, navigator, viewportCoords, tagCreator) { super(component, container, navigator, viewportCoords); this._tagCreator = tagCreator; this._geometryCreated$ = new Subject(); } get geometryCreated$() { return this._geometryCreated$; } _enable() { this._enableCreate(); this._container.container.classList.add("component-tag-create"); } _disable() { this._container.container.classList.remove("component-tag-create"); this._disableCreate(); } _validateBasic(basic) { const x = basic[0]; const y = basic[1]; return 0 <= x && x <= 1 && 0 <= y && y <= 1; } _mouseEventToBasic$(mouseEvent$) { return mouseEvent$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$), map(([event, camera, transform]) => { return this._mouseEventToBasic(event, this._container.container, camera, transform); })); } } class CreatePointHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.deferPixels(this._name, 4); this._geometryCreatedSubscription = this._mouseEventToBasic$(this._container.mouseService.proximateClick$).pipe(filter(this._validateBasic), map((basic) => { return new PointGeometry(basic); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.undeferPixels(this._name); this._geometryCreatedSubscription.unsubscribe(); } _getNameExtension() { return "create-point"; } } class CreateVertexHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.deferPixels(this._name, 4); const transformChanged$ = this._navigator.stateService.currentTransform$.pipe(map(() => { }), publishReplay(1), refCount()); this._deleteSubscription = transformChanged$.pipe(skip(1)) .subscribe(this._tagCreator.delete$); const basicClick$ = this._mouseEventToBasic$(this._container.mouseService.proximateClick$).pipe(share()); this._createSubscription = transformChanged$.pipe(switchMap(() => { return basicClick$.pipe(filter(this._validateBasic), take(1)); })) .subscribe(this._create$); this._setVertexSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), merge(this._container.mouseService.mouseMove$, this._container.mouseService.domMouseMove$), this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$) : empty(); })) .subscribe(([tag, event, camera, transform]) => { const basicPoint = this._mouseEventToBasic(event, this._container.container, camera, transform); this._setVertex2d(tag, basicPoint, transform); }); this._addPointSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicClick$) : empty(); })) .subscribe(([tag, basicPoint]) => { this._addPoint(tag, basicPoint); }); this._geometryCreateSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? tag.created$.pipe(map((t) => { return t.geometry; })) : empty(); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.undeferPixels(this._name); this._tagCreator.delete$.next(null); this._addPointSubscription.unsubscribe(); this._createSubscription.unsubscribe(); this._deleteSubscription.unsubscribe(); this._geometryCreateSubscription.unsubscribe(); this._setVertexSubscription.unsubscribe(); } } class CreatePointsHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createPoints$; } _addPoint(tag, basicPoint) { tag.geometry.addPoint2d(basicPoint); } _getNameExtension() { return "create-points"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setPoint2d((tag.geometry).points.length - 1, basicPoint, transform); } } class CreatePolygonHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createPolygon$; } _addPoint(tag, basicPoint) { tag.addPoint(basicPoint); } _getNameExtension() { return "create-polygon"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setVertex2d(tag.geometry.polygon.length - 2, basicPoint, transform); } } class CreateRectHandler extends CreateVertexHandler { get _create$() { return this._tagCreator.createRect$; } _addPoint(tag, basicPoint) { const rectGeometry = tag.geometry; if (!rectGeometry.validate(basicPoint)) { basicPoint = rectGeometry.getNonAdjustedVertex2d(3); } tag.addPoint(basicPoint); } _enable() { super._enable(); this._initializeAnchorIndexingSubscription = this._tagCreator.tag$.pipe(filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.geometry.initializeAnchorIndexing(); }); } _disable() { super._disable(); this._initializeAnchorIndexingSubscription.unsubscribe(); } _getNameExtension() { return "create-rect"; } _setVertex2d(tag, basicPoint, transform) { tag.geometry.setOppositeVertex2d(basicPoint, transform); } } class CreateRectDragHandler extends CreateHandlerBase { _enableCreate() { this._container.mouseService.claimMouse(this._name, 2); this._deleteSubscription = this._navigator.stateService.currentTransform$.pipe(map((transform) => { return null; }), skip(1)) .subscribe(this._tagCreator.delete$); this._createSubscription = this._mouseEventToBasic$(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseDragStart$)).pipe(filter(this._validateBasic)) .subscribe(this._tagCreator.createRect$); this._initializeAnchorIndexingSubscription = this._tagCreator.tag$.pipe(filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.geometry.initializeAnchorIndexing(); }); const basicMouse$ = combineLatest(merge(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseMove$), this._container.mouseService.filtered$(this._name, this._container.mouseService.domMouseMove$)), this._container.renderService.renderCamera$).pipe(withLatestFrom(this._navigator.stateService.currentTransform$), map(([[event, camera], transform]) => { return this._mouseEventToBasic(event, this._container.container, camera, transform); })); this._setVertexSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicMouse$, this._navigator.stateService.currentTransform$) : empty(); })) .subscribe(([tag, basicPoint, transform]) => { tag.geometry.setOppositeVertex2d(basicPoint, transform); }); const basicMouseDragEnd$ = this._container.mouseService.mouseDragEnd$.pipe(withLatestFrom(this._mouseEventToBasic$(this._container.mouseService.filtered$(this._name, this._container.mouseService.mouseDrag$)).pipe(filter(this._validateBasic)), (event, basicPoint) => { return basicPoint; }), share()); this._addPointSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? combineLatest(of(tag), basicMouseDragEnd$) : empty(); })) .subscribe(([tag, basicPoint]) => { const rectGeometry = tag.geometry; if (!rectGeometry.validate(basicPoint)) { basicPoint = rectGeometry.getNonAdjustedVertex2d(3); } tag.addPoint(basicPoint); }); this._geometryCreatedSubscription = this._tagCreator.tag$.pipe(switchMap((tag) => { return !!tag ? tag.created$.pipe(map((t) => { return t.geometry; })) : empty(); })) .subscribe(this._geometryCreated$); } _disableCreate() { this._container.mouseService.unclaimMouse(this._name); this._tagCreator.delete$.next(null); this._addPointSubscription.unsubscribe(); this._createSubscription.unsubscribe(); this._deleteSubscription.unsubscribe(); this._geometryCreatedSubscription.unsubscribe(); this._initializeAnchorIndexingSubscription.unsubscribe(); this._setVertexSubscription.unsubscribe(); } _getNameExtension() { return "create-rect-drag"; } } class EditVertexHandler extends TagHandlerBase { constructor(component, container, navigator, viewportCoords, tagSet) { super(component, container, navigator, viewportCoords); this._tagSet = tagSet; } _enable() { const interaction$ = this._tagSet.changed$.pipe(map((tagSet) => { return tagSet.getAll(); }), switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return tag.interact$; })); }), switchMap((interaction) => { return concat(of(interaction), this._container.mouseService.documentMouseUp$.pipe(map(() => { return { offsetX: 0, offsetY: 0, operation: TagOperation.None, tag: null }; }), first())); }), share()); merge(this._container.mouseService.mouseMove$, this._container.mouseService.domMouseMove$).pipe(share()); this._claimMouseSubscription = interaction$.pipe(switchMap((interaction) => { return !!interaction.tag ? this._container.mouseService.domMouseDragStart$ : empty(); })) .subscribe(() => { this._container.mouseService.claimMouse(this._name, 3); }); this._cursorSubscription = interaction$.pipe(map((interaction) => { return interaction.cursor; }), distinctUntilChanged()) .subscribe((cursor) => { const interactionCursors = ["crosshair", "move", "nesw-resize", "nwse-resize"]; for (const interactionCursor of interactionCursors) { this._container.container.classList.remove(`component-tag-edit-${interactionCursor}`); } if (!!cursor) { this._container.container.classList.add(`component-tag-edit-${cursor}`); } }); this._unclaimMouseSubscription = this._container.mouseService .filtered$(this._name, this._container.mouseService.domMouseDragEnd$) .subscribe((e) => { this._container.mouseService.unclaimMouse(this._name); }); this._preventDefaultSubscription = interaction$.pipe(switchMap((interaction) => { return !!interaction.tag ? this._container.mouseService.documentMouseMove$ : empty(); })) .subscribe((event) => { event.preventDefault(); // prevent selection of content outside the viewer }); this._updateGeometrySubscription = interaction$.pipe(switchMap((interaction) => { if (interaction.operation === TagOperation.None || !interaction.tag) { return empty(); } const mouseDrag$ = this._container.mouseService .filtered$(this._name, this._container.mouseService.domMouseDrag$).pipe(filter((event) => { return this._viewportCoords.insideElement(event, this._container.container); })); return combineLatest(mouseDrag$, this._container.renderService.renderCamera$).pipe(withLatestFrom(of(interaction), this._navigator.stateService.currentTransform$, ([event, render], i, transform) => { return [event, render, i, transform]; })); })) .subscribe(([mouseEvent, renderCamera, interaction, transform]) => { const basic = this._mouseEventToBasic(mouseEvent, this._container.container, renderCamera, transform, interaction.offsetX, interaction.offsetY); const geometry = interaction.tag.geometry; if (interaction.operation === TagOperation.Centroid) { geometry.setCentroid2d(basic, transform); } else if (interaction.operation === TagOperation.Vertex) { geometry.setVertex2d(interaction.vertexIndex, basic, transform); } }); } _disable() { this._claimMouseSubscription.unsubscribe(); this._cursorSubscription.unsubscribe(); this._preventDefaultSubscription.unsubscribe(); this._unclaimMouseSubscription.unsubscribe(); this._updateGeometrySubscription.unsubscribe(); } _getNameExtension() { return "edit-vertex"; } } /** * @class TagComponent * * @classdesc Component for showing and editing tags with different * geometries composed from 2D basic image coordinates (see the * {@link Viewer} class documentation for more information about coordinate * systems). * * The `add` method is used for adding new tags or replacing * tags already in the set. Tags are removed by id. * * If a tag already in the set has the same * id as one of the tags added, the old tag will be removed and * the added tag will take its place. * * The tag component mode can be set to either be non interactive or * to be in creating mode of a certain geometry type. * * The tag properties can be updated at any time and the change will * be visibile immediately. * * Tags are only relevant to a single image because they are based on * 2D basic image coordinates. Tags related to a certain image should * be removed when the viewer is moved to another image. * * To retrive and use the tag component * * @example * ```js * var viewer = new Viewer({ component: { tag: true } }, ...); * * var tagComponent = viewer.getComponent("tag"); * ``` */ class TagComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._tagDomRenderer = new TagDOMRenderer(); this._tagScene = new TagScene(); this._tagSet = new TagSet(); this._tagCreator = new TagCreator(this, navigator); this._viewportCoords = new ViewportCoords(); this._createHandlers = { "CreatePoint": new CreatePointHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreatePoints": new CreatePointsHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreatePolygon": new CreatePolygonHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreateRect": new CreateRectHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "CreateRectDrag": new CreateRectDragHandler(this, container, navigator, this._viewportCoords, this._tagCreator), "Default": undefined, }; this._editVertexHandler = new EditVertexHandler(this, container, navigator, this._viewportCoords, this._tagSet); this._renderTags$ = this._tagSet.changed$.pipe(map((tagSet) => { const tags = tagSet.getAll(); // ensure that tags are always rendered in the same order // to avoid hover tracking problems on first resize. tags.sort((t1, t2) => { const id1 = t1.tag.id; const id2 = t2.tag.id; if (id1 < id2) { return -1; } if (id1 > id2) { return 1; } return 0; }); return tags; }), share()); this._tagChanged$ = this._renderTags$.pipe(switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return merge(tag.tag.changed$, tag.tag.geometryChanged$); })); }), share()); this._renderTagGLChanged$ = this._renderTags$.pipe(switchMap((tags) => { return from(tags).pipe(mergeMap((tag) => { return tag.glObjectsChanged$; })); }), share()); this._createGeometryChanged$ = this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.geometryChanged$ : empty(); }), share()); this._createGLObjectsChanged$ = this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.glObjectsChanged$ : empty(); }), share()); this._creatingConfiguration$ = this._configuration$.pipe(distinctUntilChanged((c1, c2) => { return c1.mode === c2.mode; }, (configuration) => { return { createColor: configuration.createColor, mode: configuration.mode, }; }), publishReplay(1), refCount()); this._creatingConfiguration$ .subscribe((configuration) => { const type = "tagmode"; const event = { mode: configuration.mode, target: this, type, }; this.fire(type, event); }); } /** * Add tags to the tag set or replace tags in the tag set. * * @description If a tag already in the set has the same * id as one of the tags added, the old tag will be removed * the added tag will take its place. * * @param {Array} tags - Tags to add. * * @example * ```js * tagComponent.add([tag1, tag2]); * ``` */ add(tags) { if (this._activated) { this._navigator.stateService.currentTransform$.pipe(first()) .subscribe((transform) => { this._tagSet.add(tags, transform); const renderTags = tags .map((tag) => { return this._tagSet.get(tag.id); }); this._tagScene.add(renderTags); }); } else { this._tagSet.addDeactivated(tags); } } /** * Calculate the smallest rectangle containing all the points * in the points geometry. * * @description The result may be different depending on if the * current image is an spherical or not. If the * current image is an spherical the rectangle may * wrap the horizontal border of the image. * * @returns {Promise>} Promise to the rectangle * on the format specified for the {@link RectGeometry} in basic * coordinates. */ calculateRect(geometry) { return new Promise((resolve, reject) => { this._navigator.stateService.currentTransform$.pipe(first(), map((transform) => { return geometry.getRect2d(transform); })) .subscribe((rect) => { resolve(rect); }, (error) => { reject(error); }); }); } /** * Force the creation of a geometry programatically using its * current vertices. * * @description The method only has an effect when the tag * mode is either of the following modes: * * {@link TagMode.CreatePoints} * {@link TagMode.CreatePolygon} * {@link TagMode.CreateRect} * {@link TagMode.CreateRectDrag} * * In the case of points or polygon creation, only the created * vertices are used, i.e. the mouse position is disregarded. * * In the case of rectangle creation the position of the mouse * at the time of the method call is used as one of the vertices * defining the rectangle. * * @fires geometrycreate * * @example * ```js * tagComponent.on("geometrycreate", function(geometry) { * console.log(geometry); * }); * * tagComponent.create(); * ``` */ create() { this._tagCreator.replayedTag$.pipe(first(), filter((tag) => { return !!tag; })) .subscribe((tag) => { tag.create(); }); } /** * Change the current tag mode. * * @description Change the tag mode to one of the create modes for creating new geometries. * * @param {TagMode} mode - New tag mode. * * @fires tagmode * * @example * ```js * tagComponent.changeMode(TagMode.CreateRect); * ``` */ changeMode(mode) { this.configure({ mode: mode }); } fire(type, event) { super.fire(type, event); } /** * Returns the tag in the tag set with the specified id, or * undefined if the id matches no tag. * * @param {string} tagId - Id of the tag. * * @example * ```js * var tag = tagComponent.get("tagId"); * ``` */ get(tagId) { if (this._activated) { const renderTag = this._tagSet.get(tagId); return renderTag !== undefined ? renderTag.tag : undefined; } else { return this._tagSet.getDeactivated(tagId); } } /** * Returns an array of all tags. * * @example * ```js * var tags = tagComponent.getAll(); * ``` */ getAll() { if (this.activated) { return this._tagSet .getAll() .map((renderTag) => { return renderTag.tag; }); } else { return this._tagSet.getAllDeactivated(); } } /** * Returns an array of tag ids for tags that contain the specified point. * * @description The pixel point must lie inside the polygon or rectangle * of an added tag for the tag id to be returned. Tag ids for * tags that do not have a fill will also be returned if the point is inside * the geometry of the tag. Tags with point geometries can not be retrieved. * * No tag ids will be returned for polygons rendered in cropped spherical or * rectangles rendered in spherical. * * Notice that the pixelPoint argument requires x, y coordinates from pixel space. * * With this function, you can use the coordinates provided by mouse * events to get information out of the tag component. * * If no tag at exist the pixel point, an empty array will be returned. * * @param {Array} pixelPoint - Pixel coordinates on the viewer element. * @returns {Promise>} Promise to the ids of the tags that * contain the specified pixel point. * * @example * ```js * tagComponent.getTagIdsAt([100, 100]) * .then((tagIds) => { console.log(tagIds); }); * ``` */ getTagIdsAt(pixelPoint) { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first(), map((render) => { const viewport = this._viewportCoords .canvasToViewport(pixelPoint[0], pixelPoint[1], this._container.container); const ids = this._tagScene.intersectObjects(viewport, render.perspective); return ids; })) .subscribe((ids) => { resolve(ids); }, (error) => { reject(error); }); }); } /** * Check if a tag exist in the tag set. * * @param {string} tagId - Id of the tag. * * @example * ```js * var tagExists = tagComponent.has("tagId"); * ``` */ has(tagId) { return this._activated ? this._tagSet.has(tagId) : this._tagSet.hasDeactivated(tagId); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Remove tags with the specified ids from the tag set. * * @param {Array} tagIds - Ids for tags to remove. * * @example * ```js * tagComponent.remove(["id-1", "id-2"]); * ``` */ remove(tagIds) { if (this._activated) { this._tagSet.remove(tagIds); this._tagScene.remove(tagIds); } else { this._tagSet.removeDeactivated(tagIds); } } /** * Remove all tags from the tag set. * * @example * ```js * tagComponent.removeAll(); * ``` */ removeAll() { if (this._activated) { this._tagSet.removeAll(); this._tagScene.removeAll(); } else { this._tagSet.removeAllDeactivated(); } } _activate() { this._editVertexHandler.enable(); const handlerGeometryCreated$ = from(Object.keys(this._createHandlers)).pipe(map((key) => { return this._createHandlers[key]; }), filter((handler) => { return !!handler; }), mergeMap((handler) => { return handler.geometryCreated$; }), share()); const subs = this._subscriptions; subs.push(handlerGeometryCreated$ .subscribe((geometry) => { const type = "geometrycreate"; const event = { geometry, target: this, type, }; this.fire(type, event); })); subs.push(this._tagCreator.tag$.pipe(skipWhile((tag) => { return tag == null; }), distinctUntilChanged()) .subscribe((tag) => { const type = tag != null ? "tagcreatestart" : "tagcreateend"; const event = { target: this, type, }; this.fire(type, event); })); subs.push(handlerGeometryCreated$ .subscribe(() => { this.changeMode(exports.TagMode.Default); })); subs.push(this._creatingConfiguration$ .subscribe((configuration) => { this._disableCreateHandlers(); const mode = exports.TagMode[configuration.mode]; const handler = this._createHandlers[mode]; if (!!handler) { handler.enable(); } })); subs.push(this._renderTags$ .subscribe(() => { const type = "tags"; const event = { target: this, type, }; this.fire(type, event); })); subs.push(this._tagCreator.tag$.pipe(switchMap((tag) => { return tag != null ? tag.aborted$.pipe(map(() => { return null; })) : empty(); })) .subscribe(() => { this.changeMode(exports.TagMode.Default); })); subs.push(this._tagCreator.tag$ .subscribe((tag) => { if (this._tagScene.hasCreateTag()) { this._tagScene.removeCreateTag(); } if (tag != null) { this._tagScene.addCreateTag(tag); } })); subs.push(this._createGLObjectsChanged$ .subscribe((tag) => { this._tagScene.updateCreateTagObjects(tag); })); subs.push(this._renderTagGLChanged$ .subscribe((tag) => { this._tagScene.updateObjects(tag); })); subs.push(this._tagChanged$ .subscribe(() => { this._tagScene.update(); })); subs.push(combineLatest(this._renderTags$.pipe(startWith([]), tap(() => { this._container.domRenderer.render$.next({ name: this._name, vNode: this._tagDomRenderer.clear(), }); })), this._container.renderService.renderCamera$, this._container.spriteService.spriteAtlas$, this._container.renderService.size$, this._tagChanged$.pipe(startWith(null)), merge(this._tagCreator.tag$, this._createGeometryChanged$).pipe(startWith(null))).pipe(map(([renderTags, rc, atlas, size, , ct]) => { return { name: this._name, vNode: this._tagDomRenderer.render(renderTags, ct, atlas, rc.perspective, size), }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._navigator.stateService.currentState$.pipe(map((frame) => { const tagScene = this._tagScene; return { name: this._name, renderer: { frameId: frame.id, needsRender: tagScene.needsRender, render: tagScene.render.bind(tagScene), pass: RenderPass.Opaque, }, }; })) .subscribe(this._container.glRenderer.render$)); this._navigator.stateService.currentTransform$.pipe(first()) .subscribe((transform) => { this._tagSet.activate(transform); this._tagScene.add(this._tagSet.getAll()); }); } _deactivate() { this._editVertexHandler.disable(); this._disableCreateHandlers(); this._tagScene.clear(); this._tagSet.deactivate(); this._tagCreator.delete$.next(null); this._subscriptions.unsubscribe(); this._container.container.classList.remove("component-tag-create"); } _getDefaultConfiguration() { return { createColor: 0xFFFFFF, indicatePointsCompleter: true, mode: exports.TagMode.Default, }; } _disableCreateHandlers() { const createHandlers = this._createHandlers; for (const key in createHandlers) { if (!createHandlers.hasOwnProperty(key)) { continue; } const handler = createHandlers[key]; if (!!handler) { handler.disable(); } } } } /** @inheritdoc */ TagComponent.componentName = "tag"; /** * @class ZoomComponent * * @classdesc Component rendering UI elements used for zooming. * * @example * ```js * var viewer = new Viewer({ ... }); * * var zoomComponent = viewer.getComponent("zoom"); * zoomComponent.configure({ size: ComponentSize.Small }); * ``` */ class ZoomComponent extends Component { constructor(name, container, navigator) { super(name, container, navigator); this._viewportCoords = new ViewportCoords(); this._zoomDelta$ = new Subject(); } _activate() { const subs = this._subscriptions; subs.push(combineLatest(this._navigator.stateService.currentState$, this._navigator.stateService.state$, this._configuration$, this._container.renderService.size$).pipe(map(([frame, state, configuration, size]) => { const zoom = frame.state.zoom; const zoomInIcon = virtualDom.h("div.mapillary-zoom-in-icon", []); const zoomInButton = zoom >= 3 || state === State.Waiting ? virtualDom.h("div.mapillary-zoom-in-button-inactive", [zoomInIcon]) : virtualDom.h("div.mapillary-zoom-in-button", { onclick: () => { this._zoomDelta$.next(1); } }, [zoomInIcon]); const zoomOutIcon = virtualDom.h("div.mapillary-zoom-out-icon", []); const zoomOutButton = zoom <= 0 || state === State.Waiting ? virtualDom.h("div.mapillary-zoom-out-button-inactive", [zoomOutIcon]) : virtualDom.h("div.mapillary-zoom-out-button", { onclick: () => { this._zoomDelta$.next(-1); } }, [zoomOutIcon]); const compact = configuration.size === exports.ComponentSize.Small || configuration.size === exports.ComponentSize.Automatic && size.width < 640 ? ".mapillary-zoom-compact" : ""; return { name: this._name, vNode: virtualDom.h("div.mapillary-zoom-container" + compact, { oncontextmenu: (event) => { event.preventDefault(); } }, [zoomInButton, zoomOutButton]), }; })) .subscribe(this._container.domRenderer.render$)); subs.push(this._zoomDelta$.pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$)) .subscribe(([zoomDelta, render, transform]) => { const unprojected = this._viewportCoords.unprojectFromViewport(0, 0, render.perspective); const reference = transform.projectBasic(unprojected.toArray()); this._navigator.stateService.zoomIn(zoomDelta, reference); })); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { size: exports.ComponentSize.Automatic }; } } ZoomComponent.componentName = "zoom"; class ImageFallbackComponent extends Component { constructor(name, container, navigator, dom) { super(name, container, navigator); this._canvasId = `${container.id}-${this._name}`; this._dom = !!dom ? dom : new DOM(); } _activate() { const canvasSize$ = this._container.domRenderer.element$.pipe(map(() => { return this._dom.document.getElementById(this._canvasId); }), filter((canvas) => { return !!canvas; }), map((canvas) => { const adaptableDomRenderer = canvas.parentElement; const width = adaptableDomRenderer.offsetWidth; const height = adaptableDomRenderer.offsetHeight; return [canvas, { height: height, width: width }]; }), distinctUntilChanged((s1, s2) => { return s1.height === s2.height && s1.width === s2.width; }, ([, size]) => { return size; })); this._subscriptions.push(combineLatest(canvasSize$, this._navigator.stateService.currentImage$) .subscribe(([[canvas, size], image]) => { canvas.width = size.width; canvas.height = size.height; canvas .getContext("2d") .drawImage(image.image, 0, 0, size.width, size.height); })); this._container.domRenderer.renderAdaptive$.next({ name: this._name, vNode: virtualDom.h(`canvas#${this._canvasId}`, []) }); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return {}; } } ImageFallbackComponent.componentName = "imagefallback"; /** * @class NavigationFallbackComponent * * @classdesc Fallback navigation component for environments without WebGL support. * * Replaces the functionality in the Direction and Sequence components. */ class NavigationFallbackComponent extends Component { /** @ignore */ constructor(name, container, navigator) { super(name, container, navigator); this._seqNames = {}; this._seqNames[exports.NavigationDirection[exports.NavigationDirection.Prev]] = "-prev"; this._seqNames[exports.NavigationDirection[exports.NavigationDirection.Next]] = "-next"; this._spaTopNames = {}; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.TurnLeft]] = "-turn-left"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepLeft]] = "-left"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepForward]] = "-forward"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.StepRight]] = "-right"; this._spaTopNames[exports.NavigationDirection[exports.NavigationDirection.TurnRight]] = "-turn-right"; this._spaBottomNames = {}; this._spaBottomNames[exports.NavigationDirection[exports.NavigationDirection.TurnU]] = "-turn-around"; this._spaBottomNames[exports.NavigationDirection[exports.NavigationDirection.StepBackward]] = "-backward"; } _activate() { this._subscriptions.push(combineLatest(this._navigator.stateService.currentImage$, this._configuration$).pipe(switchMap(([image, configuration]) => { const sequenceEdges$ = configuration.sequence ? image.sequenceEdges$.pipe(map((status) => { return status.edges .map((edge) => { return edge.data.direction; }); })) : of([]); const spatialEdges$ = !isSpherical(image.cameraType) && configuration.spatial ? image.spatialEdges$.pipe(map((status) => { return status.edges .map((edge) => { return edge.data.direction; }); })) : of([]); return combineLatest(sequenceEdges$, spatialEdges$).pipe(map(([seq, spa]) => { return seq.concat(spa); })); }), map((edgeDirections) => { const seqs = this._createArrowRow(this._seqNames, edgeDirections); const spaTops = this._createArrowRow(this._spaTopNames, edgeDirections); const spaBottoms = this._createArrowRow(this._spaBottomNames, edgeDirections); const seqContainer = virtualDom.h(`div.mapillary-navigation-sequence`, seqs); const spaTopContainer = virtualDom.h(`div.NavigationSpatialTop`, spaTops); const spaBottomContainer = virtualDom.h(`div.mapillary-navigation-spatial-bottom`, spaBottoms); const spaContainer = virtualDom.h(`div.mapillary-navigation-spatial`, [spaTopContainer, spaBottomContainer]); return { name: this._name, vNode: virtualDom.h(`div.NavigationContainer`, [seqContainer, spaContainer]) }; })) .subscribe(this._container.domRenderer.render$)); } _deactivate() { this._subscriptions.unsubscribe(); } _getDefaultConfiguration() { return { sequence: true, spatial: true }; } _createArrowRow(arrowNames, edgeDirections) { const arrows = []; for (const arrowName in arrowNames) { if (!(arrowNames.hasOwnProperty(arrowName))) { continue; } const direction = exports.NavigationDirection[arrowName]; if (edgeDirections.indexOf(direction) !== -1) { arrows.push(this._createVNode(direction, arrowNames[arrowName], "visible")); } else { arrows.push(this._createVNode(direction, arrowNames[arrowName], "hidden")); } } return arrows; } _createVNode(direction, name, visibility) { return virtualDom.h(`span.mapillary-navigation-button.mapillary-navigation${name}`, { onclick: () => { this._navigator.moveDir$(direction) .subscribe(undefined, (error) => { if (!(error instanceof CancelMapillaryError)) { console.error(error); } }); }, style: { visibility: visibility, }, }, []); } } NavigationFallbackComponent.componentName = "navigationfallback"; /*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */ // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. /* eslint-disable space-unary-ops */ /* Public constants ==========================================================*/ /* ===========================================================================*/ //const Z_FILTERED = 1; //const Z_HUFFMAN_ONLY = 2; //const Z_RLE = 3; const Z_FIXED$1 = 4; //const Z_DEFAULT_STRATEGY = 0; /* Possible values of the data_type field (though see inflate()) */ const Z_BINARY = 0; const Z_TEXT = 1; //const Z_ASCII = 1; // = Z_TEXT const Z_UNKNOWN$1 = 2; /*============================================================================*/ function zero$1(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } // From zutil.h const STORED_BLOCK = 0; const STATIC_TREES = 1; const DYN_TREES = 2; /* The three kinds of block type */ const MIN_MATCH$1 = 3; const MAX_MATCH$1 = 258; /* The minimum and maximum match lengths */ // From deflate.h /* =========================================================================== * Internal compression state. */ const LENGTH_CODES$1 = 29; /* number of length codes, not counting the special END_BLOCK code */ const LITERALS$1 = 256; /* number of literal bytes 0..255 */ const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; /* number of Literal or Length codes, including the END_BLOCK code */ const D_CODES$1 = 30; /* number of distance codes */ const BL_CODES$1 = 19; /* number of codes used to transfer the bit lengths */ const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; /* maximum heap size */ const MAX_BITS$1 = 15; /* All codes must not exceed MAX_BITS bits */ const Buf_size = 16; /* size of bit buffer in bi_buf */ /* =========================================================================== * Constants */ const MAX_BL_BITS = 7; /* Bit length codes must not exceed MAX_BL_BITS bits */ const END_BLOCK = 256; /* end of block literal code */ const REP_3_6 = 16; /* repeat previous bit length 3-6 times (2 bits of repeat count) */ const REPZ_3_10 = 17; /* repeat a zero length 3-10 times (3 bits of repeat count) */ const REPZ_11_138 = 18; /* repeat a zero length 11-138 times (7 bits of repeat count) */ /* eslint-disable comma-spacing,array-bracket-spacing */ const extra_lbits = /* extra bits for each length code */ new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]); const extra_dbits = /* extra bits for each distance code */ new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]); const extra_blbits = /* extra bits for each bit length code */ new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]); const bl_order = new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]); /* eslint-enable comma-spacing,array-bracket-spacing */ /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ // We pre-fill arrays with 0 to avoid uninitialized gaps const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 const static_ltree = new Array((L_CODES$1 + 2) * 2); zero$1(static_ltree); /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ const static_dtree = new Array(D_CODES$1 * 2); zero$1(static_dtree); /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ const _dist_code = new Array(DIST_CODE_LEN); zero$1(_dist_code); /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1); zero$1(_length_code); /* length code for each normalized match length (0 == MIN_MATCH) */ const base_length = new Array(LENGTH_CODES$1); zero$1(base_length); /* First normalized length for each code (0 = MIN_MATCH) */ const base_dist = new Array(D_CODES$1); zero$1(base_dist); /* First normalized distance for each code (0 = distance of 1) */ function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { this.static_tree = static_tree; /* static tree or NULL */ this.extra_bits = extra_bits; /* extra bits for each code or NULL */ this.extra_base = extra_base; /* base index for extra_bits */ this.elems = elems; /* max number of elements in the tree */ this.max_length = max_length; /* max bit length for the codes */ // show if `static_tree` has data or dummy - needed for monomorphic objects this.has_stree = static_tree && static_tree.length; } let static_l_desc; let static_d_desc; let static_bl_desc; function TreeDesc(dyn_tree, stat_desc) { this.dyn_tree = dyn_tree; /* the dynamic tree */ this.max_code = 0; /* largest code with non zero frequency */ this.stat_desc = stat_desc; /* the corresponding static tree */ } const d_code = (dist) => { return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; }; /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ const put_short = (s, w) => { // put_byte(s, (uch)((w) & 0xff)); // put_byte(s, (uch)((ush)(w) >> 8)); s.pending_buf[s.pending++] = (w) & 0xff; s.pending_buf[s.pending++] = (w >>> 8) & 0xff; }; /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ const send_bits = (s, value, length) => { if (s.bi_valid > (Buf_size - length)) { s.bi_buf |= (value << s.bi_valid) & 0xffff; put_short(s, s.bi_buf); s.bi_buf = value >> (Buf_size - s.bi_valid); s.bi_valid += length - Buf_size; } else { s.bi_buf |= (value << s.bi_valid) & 0xffff; s.bi_valid += length; } }; const send_code = (s, c, tree) => { send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); }; /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ const bi_reverse = (code, len) => { let res = 0; do { res |= code & 1; code >>>= 1; res <<= 1; } while (--len > 0); return res >>> 1; }; /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ const bi_flush = (s) => { if (s.bi_valid === 16) { put_short(s, s.bi_buf); s.bi_buf = 0; s.bi_valid = 0; } else if (s.bi_valid >= 8) { s.pending_buf[s.pending++] = s.bi_buf & 0xff; s.bi_buf >>= 8; s.bi_valid -= 8; } }; /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ const gen_bitlen = (s, desc) => // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ { const tree = desc.dyn_tree; const max_code = desc.max_code; const stree = desc.stat_desc.static_tree; const has_stree = desc.stat_desc.has_stree; const extra = desc.stat_desc.extra_bits; const base = desc.stat_desc.extra_base; const max_length = desc.stat_desc.max_length; let h; /* heap index */ let n, m; /* iterate over the tree elements */ let bits; /* bit length */ let xbits; /* extra bits */ let f; /* frequency */ let overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS$1; bits++) { s.bl_count[bits] = 0; } /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) { n = s.heap[h]; bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; if (bits > max_length) { bits = max_length; overflow++; } tree[n * 2 + 1]/*.Len*/ = bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) { continue; } /* not a leaf node */ s.bl_count[bits]++; xbits = 0; if (n >= base) { xbits = extra[n - base]; } f = tree[n * 2]/*.Freq*/; s.opt_len += f * (bits + xbits); if (has_stree) { s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); } } if (overflow === 0) { return; } // Trace((stderr," bit length overflow ")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length - 1; while (s.bl_count[bits] === 0) { bits--; } s.bl_count[bits]--; /* move one leaf down the tree */ s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ s.bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from "ar" written by Haruhiko Okumura.) */ for (bits = max_length; bits !== 0; bits--) { n = s.bl_count[bits]; while (n !== 0) { m = s.heap[--h]; if (m > max_code) { continue; } if (tree[m * 2 + 1]/*.Len*/ !== bits) { // Trace((stderr,"code %d bits %d->%d ", m, tree[m].Len, bits)); s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; tree[m * 2 + 1]/*.Len*/ = bits; } n--; } } }; /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ const gen_codes = (tree, max_code, bl_count) => // ct_data *tree; /* the tree to decorate */ // int max_code; /* largest code with non zero frequency */ // ushf *bl_count; /* number of codes at each bit length */ { const next_code = new Array(MAX_BITS$1 + 1); /* next code value for each bit length */ let code = 0; /* running code value */ let bits; /* bit index */ let n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS$1; bits++) { next_code[bits] = code = (code + bl_count[bits - 1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ //Assert (code + bl_count[MAX_BITS]-1 == (1< { let n; /* iterates over tree elements */ let bits; /* bit counter */ let length; /* length value */ let code; /* code value */ let dist; /* distance index */ const bl_count = new Array(MAX_BITS$1 + 1); /* number of codes at each bit length for an optimal tree */ // do check in _tr_init() //if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ /*#ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif*/ /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES$1 - 1; code++) { base_length[code] = length; for (n = 0; n < (1 << extra_lbits[code]); n++) { _length_code[length++] = code; } } //Assert (length == 256, "tr_static_init: length != 256"); /* Note that the length 255 (match length 258) can be represented * in two different ways: code 284 + 5 bits or code 285, so we * overwrite length_code[255] to use the best encoding: */ _length_code[length - 1] = code; /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ dist = 0; for (code = 0; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1 << extra_dbits[code]); n++) { _dist_code[dist++] = code; } } //Assert (dist == 256, "tr_static_init: dist != 256"); dist >>= 7; /* from now on, all distances are divided by 128 */ for (; code < D_CODES$1; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = code; } } //Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS$1; bits++) { bl_count[bits] = 0; } n = 0; while (n <= 143) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } while (n <= 255) { static_ltree[n * 2 + 1]/*.Len*/ = 9; n++; bl_count[9]++; } while (n <= 279) { static_ltree[n * 2 + 1]/*.Len*/ = 7; n++; bl_count[7]++; } while (n <= 287) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes(static_ltree, L_CODES$1 + 1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES$1; n++) { static_dtree[n * 2 + 1]/*.Len*/ = 5; static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); } // Now data ready and we can init static trees static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1); static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1); static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS); //static_init_done = true; }; /* =========================================================================== * Initialize a new block. */ const init_block = (s) => { let n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES$1; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < D_CODES$1; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < BL_CODES$1; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; s.opt_len = s.static_len = 0; s.last_lit = s.matches = 0; }; /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ const bi_windup = (s) => { if (s.bi_valid > 8) { put_short(s, s.bi_buf); } else if (s.bi_valid > 0) { //put_byte(s, (Byte)s->bi_buf); s.pending_buf[s.pending++] = s.bi_buf; } s.bi_buf = 0; s.bi_valid = 0; }; /* =========================================================================== * Copy a stored block, storing first the length and its * one"s complement if requested. */ const copy_block = (s, buf, len, header) => //DeflateState *s; //charf *buf; /* the input data */ //unsigned len; /* its length */ //int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ if (header) { put_short(s, len); put_short(s, ~len); } // while (len--) { // put_byte(s, *buf++); // } s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending); s.pending += len; }; /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ const smaller = (tree, n, m, depth) => { const _n2 = n * 2; const _m2 = m * 2; return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); }; /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ const pqdownheap = (s, tree, k) => // deflate_state *s; // ct_data *tree; /* the tree to restore */ // int k; /* node to move down */ { const v = s.heap[k]; let j = k << 1; /* left son of k */ while (j <= s.heap_len) { /* Set j to the smallest of the two sons: */ if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s.heap[j], s.depth)) { break; } /* Exchange v with the smallest son */ s.heap[k] = s.heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s.heap[k] = v; }; // inlined manually // const SMALLEST = 1; /* =========================================================================== * Send the block data compressed using the given Huffman trees */ const compress_block = (s, ltree, dtree) => // deflate_state *s; // const ct_data *ltree; /* literal tree */ // const ct_data *dtree; /* distance tree */ { let dist; /* distance of matched string */ let lc; /* match length or unmatched char (if dist == 0) */ let lx = 0; /* running index in l_buf */ let code; /* the code to send */ let extra; /* number of extra bits to send */ if (s.last_lit !== 0) { do { dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); lc = s.pending_buf[s.l_buf + lx]; lx++; if (dist === 0) { send_code(s, lc, ltree); /* send a literal byte */ //Tracecv(isgraph(lc), (stderr," "%c" ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra !== 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); //Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra !== 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, // "pendingBuf overflow"); } while (lx < s.last_lit); } send_code(s, END_BLOCK, ltree); }; /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. * Update the total bit length for the current block. * IN assertion: the field freq is set for all tree elements. * OUT assertions: the fields len and code are set to the optimal bit length * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ const build_tree = (s, desc) => // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ { const tree = desc.dyn_tree; const stree = desc.stat_desc.static_tree; const has_stree = desc.stat_desc.has_stree; const elems = desc.stat_desc.elems; let n, m; /* iterate over heap elements */ let max_code = -1; /* largest code with non zero frequency */ let node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s.heap_len = 0; s.heap_max = HEAP_SIZE$1; for (n = 0; n < elems; n++) { if (tree[n * 2]/*.Freq*/ !== 0) { s.heap[++s.heap_len] = max_code = n; s.depth[n] = 0; } else { tree[n * 2 + 1]/*.Len*/ = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s.heap_len < 2) { node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); tree[node * 2]/*.Freq*/ = 1; s.depth[node] = 0; s.opt_len--; if (has_stree) { s.static_len -= stree[node * 2 + 1]/*.Len*/; } /* node is 0 or 1 so it does not have extra bits */ } desc.max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { //pqremove(s, tree, n); /* n = node of least frequency */ /*** pqremove ***/ n = s.heap[1/*SMALLEST*/]; s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; pqdownheap(s, tree, 1/*SMALLEST*/); /***/ m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ s.heap[--s.heap_max] = m; /* Create a new node father of n and m */ tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; /* and insert the new node in the heap */ s.heap[1/*SMALLEST*/] = node++; pqdownheap(s, tree, 1/*SMALLEST*/); } while (s.heap_len >= 2); s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, desc); /* The field len is now set, we can generate the bit codes */ gen_codes(tree, max_code, s.bl_count); }; /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ const scan_tree = (s, tree, max_code) => // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ { let n; /* iterates over all tree elements */ let prevlen = -1; /* last emitted length */ let curlen; /* length of current code */ let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ let count = 0; /* repeat count of the current code */ let max_count = 7; /* max repeat count */ let min_count = 4; /* min repeat count */ if (nextlen === 0) { max_count = 138; min_count = 3; } tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { s.bl_tree[curlen * 2]/*.Freq*/ += count; } else if (curlen !== 0) { if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } s.bl_tree[REP_3_6 * 2]/*.Freq*/++; } else if (count <= 10) { s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; } else { s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } }; /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ const send_tree = (s, tree, max_code) => // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ { let n; /* iterates over all tree elements */ let prevlen = -1; /* last emitted length */ let curlen; /* length of current code */ let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ let count = 0; /* repeat count of the current code */ let max_count = 7; /* max repeat count */ let min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen === 0) { max_count = 138; min_count = 3; } for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); } else if (curlen !== 0) { if (curlen !== prevlen) { send_code(s, curlen, s.bl_tree); count--; } //Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s.bl_tree); send_bits(s, count - 3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s.bl_tree); send_bits(s, count - 3, 3); } else { send_code(s, REPZ_11_138, s.bl_tree); send_bits(s, count - 11, 7); } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } }; /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ const build_bl_tree = (s) => { let max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, s.dyn_ltree, s.l_desc.max_code); scan_tree(s, s.dyn_dtree, s.d_desc.max_code); /* Build the bit length tree: */ build_tree(s, s.bl_desc); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) { if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { break; } } /* Update opt_len to include the bit length tree and counts */ s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; //Tracev((stderr, " dyn trees: dyn %ld, stat %ld", // s->opt_len, s->static_len)); return max_blindex; }; /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ const send_all_trees = (s, lcodes, dcodes, blcodes) => // deflate_state *s; // int lcodes, dcodes, blcodes; /* number of codes for each tree */ { let rank; /* index in bl_order */ //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, // "too many codes"); //Tracev((stderr, " bl counts: ")); send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes - 1, 5); send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { //Tracev((stderr, " bl code %2d ", bl_order[rank])); send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); } //Tracev((stderr, " bl tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ //Tracev((stderr, " lit tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ //Tracev((stderr, " dist tree: sent %ld", s->bits_sent)); }; /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ const detect_data_type = (s) => { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ let black_mask = 0xf3ffc07f; let n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>>= 1) { if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { return Z_BINARY; } } /* Check for textual ("white-listed") bytes. */ if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { return Z_TEXT; } for (n = 32; n < LITERALS$1; n++) { if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { return Z_TEXT; } } /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; }; let static_init_done = false; /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ const _tr_init$1 = (s) => { if (!static_init_done) { tr_static_init(); static_init_done = true; } s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); s.bi_buf = 0; s.bi_valid = 0; /* Initialize the first block of the first file: */ init_block(s); }; /* =========================================================================== * Send a stored block */ const _tr_stored_block$1 = (s, buf, stored_len, last) => //DeflateState *s; //charf *buf; /* input block */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ copy_block(s, buf, stored_len, true); /* with header */ }; /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ const _tr_align$1 = (s) => { send_bits(s, STATIC_TREES << 1, 3); send_code(s, END_BLOCK, static_ltree); bi_flush(s); }; /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ const _tr_flush_block$1 = (s, buf, stored_len, last) => //DeflateState *s; //charf *buf; /* input block, or NULL if too old */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ { let opt_lenb, static_lenb; /* opt_len and static_len in bytes */ let max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s.level > 0) { /* Check if the file is binary or text */ if (s.strm.data_type === Z_UNKNOWN$1) { s.strm.data_type = detect_data_type(s); } /* Construct the literal and distance trees */ build_tree(s, s.l_desc); // Tracev((stderr, " lit data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); build_tree(s, s.d_desc); // Tracev((stderr, " dist data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s.opt_len + 3 + 7) >>> 3; static_lenb = (s.static_len + 3 + 7) >>> 3; // Tracev((stderr, " opt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, // s->last_lit)); if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } } else { // Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { /* 4: two words for the lengths */ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can"t have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block$1(s, buf, stored_len, last); } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) { send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); compress_block(s, static_ltree, static_dtree); } else { send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); compress_block(s, s.dyn_ltree, s.dyn_dtree); } // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); } // Tracev((stderr," comprlen %lu(%lu) ", s->compressed_len>>3, // s->compressed_len-7*last)); }; /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ const _tr_tally$1 = (s, dist, lc) => // deflate_state *s; // unsigned dist; /* distance of matched string */ // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { //let out_length, in_length, dcode; s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; s.last_lit++; if (dist === 0) { /* lc is the unmatched char */ s.dyn_ltree[lc * 2]/*.Freq*/++; } else { s.matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ //Assert((ush)dist < (ush)MAX_DIST(s) && // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2]/*.Freq*/++; s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; } // (!) This block is disabled in zlib defaults, // don"t enable it for binary compatibility //#ifdef TRUNCATE_BLOCK // /* Try to guess if it is profitable to stop the current block here */ // if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { // /* Compute an upper bound for the compressed length */ // out_length = s.last_lit*8; // in_length = s.strstart - s.block_start; // // for (dcode = 0; dcode < D_CODES; dcode++) { // out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); // } // out_length >>>= 3; // //Tracev((stderr," last_lit %u, in %ld, out ~%ld(%ld%%) ", // // s->last_lit, in_length, out_length, // // 100L - out_length*100L/in_length)); // if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { // return true; // } // } //#endif return (s.last_lit === s.lit_bufsize - 1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ }; var _tr_init_1 = _tr_init$1; var _tr_stored_block_1 = _tr_stored_block$1; var _tr_flush_block_1 = _tr_flush_block$1; var _tr_tally_1 = _tr_tally$1; var _tr_align_1 = _tr_align$1; var trees = { _tr_init: _tr_init_1, _tr_stored_block: _tr_stored_block_1, _tr_flush_block: _tr_flush_block_1, _tr_tally: _tr_tally_1, _tr_align: _tr_align_1 }; // Note: adler32 takes 12% for level 0 and 2% for level 6. // It isn"t worth it to make additional optimizations as in original. // Small size is preferable. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const adler32 = (adler, buf, len, pos) => { let s1 = (adler & 0xffff) |0, s2 = ((adler >>> 16) & 0xffff) |0, n = 0; while (len !== 0) { // Set limit ~ twice less than 5552, to keep // s2 in 31-bits, because we force signed ints. // in other case %= will fail. n = len > 2000 ? 2000 : len; len -= n; do { s1 = (s1 + buf[pos++]) |0; s2 = (s2 + s1) |0; } while (--n); s1 %= 65521; s2 %= 65521; } return (s1 | (s2 << 16)) |0; }; var adler32_1 = adler32; // Note: we can"t get significant speed boost here. // So write code to minimize size - no pregenerated tables // and array tools dependencies. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // Use ordinary array, since untyped makes no boost here const makeTable = () => { let c, table = []; for (var n = 0; n < 256; n++) { c = n; for (var k = 0; k < 8; k++) { c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); } table[n] = c; } return table; }; // Create table on load. Just 255 signed longs. Not a problem. const crcTable = new Uint32Array(makeTable()); const crc32 = (crc, buf, len, pos) => { const t = crcTable; const end = pos + len; crc ^= -1; for (let i = pos; i < end; i++) { crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; } return (crc ^ (-1)); // >>> 0; }; var crc32_1 = crc32; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var messages = { 2: "need dictionary", /* Z_NEED_DICT 2 */ 1: "stream end", /* Z_STREAM_END 1 */ 0: "", /* Z_OK 0 */ "-1": "file error", /* Z_ERRNO (-1) */ "-2": "stream error", /* Z_STREAM_ERROR (-2) */ "-3": "data error", /* Z_DATA_ERROR (-3) */ "-4": "insufficient memory", /* Z_MEM_ERROR (-4) */ "-5": "buffer error", /* Z_BUF_ERROR (-5) */ "-6": "incompatible version" /* Z_VERSION_ERROR (-6) */ }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var constants$2 = { /* Allowed flush values; see deflate() and inflate() below for details */ Z_NO_FLUSH: 0, Z_PARTIAL_FLUSH: 1, Z_SYNC_FLUSH: 2, Z_FULL_FLUSH: 3, Z_FINISH: 4, Z_BLOCK: 5, Z_TREES: 6, /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ Z_OK: 0, Z_STREAM_END: 1, Z_NEED_DICT: 2, Z_ERRNO: -1, Z_STREAM_ERROR: -2, Z_DATA_ERROR: -3, Z_MEM_ERROR: -4, Z_BUF_ERROR: -5, //Z_VERSION_ERROR: -6, /* compression levels */ Z_NO_COMPRESSION: 0, Z_BEST_SPEED: 1, Z_BEST_COMPRESSION: 9, Z_DEFAULT_COMPRESSION: -1, Z_FILTERED: 1, Z_HUFFMAN_ONLY: 2, Z_RLE: 3, Z_FIXED: 4, Z_DEFAULT_STRATEGY: 0, /* Possible values of the data_type field (though see inflate()) */ Z_BINARY: 0, Z_TEXT: 1, //Z_ASCII: 1, // = Z_TEXT (deprecated) Z_UNKNOWN: 2, /* The deflate compression method */ Z_DEFLATED: 8 //Z_NULL: null // Use -1 or null inline, depending on var type }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } = trees; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH: Z_NO_FLUSH$2, Z_PARTIAL_FLUSH, Z_FULL_FLUSH: Z_FULL_FLUSH$1, Z_FINISH: Z_FINISH$3, Z_BLOCK: Z_BLOCK$1, Z_OK: Z_OK$3, Z_STREAM_END: Z_STREAM_END$3, Z_STREAM_ERROR: Z_STREAM_ERROR$2, Z_DATA_ERROR: Z_DATA_ERROR$2, Z_BUF_ERROR: Z_BUF_ERROR$1, Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, Z_UNKNOWN, Z_DEFLATED: Z_DEFLATED$2 } = constants$2; /*============================================================================*/ const MAX_MEM_LEVEL = 9; /* Maximum value for memLevel in deflateInit2 */ const MAX_WBITS$1 = 15; /* 32K LZ77 window */ const DEF_MEM_LEVEL = 8; const LENGTH_CODES = 29; /* number of length codes, not counting the special END_BLOCK code */ const LITERALS = 256; /* number of literal bytes 0..255 */ const L_CODES = LITERALS + 1 + LENGTH_CODES; /* number of Literal or Length codes, including the END_BLOCK code */ const D_CODES = 30; /* number of distance codes */ const BL_CODES = 19; /* number of codes used to transfer the bit lengths */ const HEAP_SIZE = 2 * L_CODES + 1; /* maximum heap size */ const MAX_BITS = 15; /* All codes must not exceed MAX_BITS bits */ const MIN_MATCH = 3; const MAX_MATCH = 258; const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); const PRESET_DICT = 0x20; const INIT_STATE = 42; const EXTRA_STATE = 69; const NAME_STATE = 73; const COMMENT_STATE = 91; const HCRC_STATE = 103; const BUSY_STATE = 113; const FINISH_STATE = 666; const BS_NEED_MORE = 1; /* block not completed, need more input or more output */ const BS_BLOCK_DONE = 2; /* block flush performed */ const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ const OS_CODE = 0x03; // Unix :) . Don"t detect, use this default. const err = (strm, errorCode) => { strm.msg = messages[errorCode]; return errorCode; }; const rank = (f) => { return ((f) << 1) - ((f) > 4 ? 9 : 0); }; const zero = (buf) => { let len = buf.length; while (--len >= 0) { buf[len] = 0; } }; /* eslint-disable new-cap */ let HASH_ZLIB = (s, prev, data) => ((prev << s.hash_shift) ^ data) & s.hash_mask; // This hash causes less collisions, https://github.com/nodeca/pako/issues/135 // But breaks binary compatibility //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask; let HASH = HASH_ZLIB; /* ========================================================================= * Flush as much pending output as possible. All deflate() output goes * through this function so some applications may wish to modify it * to avoid allocating a large strm->output buffer and copying into it. * (See also read_buf()). */ const flush_pending = (strm) => { const s = strm.state; //_tr_flush_bits(s); let len = s.pending; if (len > strm.avail_out) { len = strm.avail_out; } if (len === 0) { return; } strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out); strm.next_out += len; s.pending_out += len; strm.total_out += len; strm.avail_out -= len; s.pending -= len; if (s.pending === 0) { s.pending_out = 0; } }; const flush_block_only = (s, last) => { _tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); s.block_start = s.strstart; flush_pending(s.strm); }; const put_byte = (s, b) => { s.pending_buf[s.pending++] = b; }; /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ const putShortMSB = (s, b) => { // put_byte(s, (Byte)(b >> 8)); // put_byte(s, (Byte)(b & 0xff)); s.pending_buf[s.pending++] = (b >>> 8) & 0xff; s.pending_buf[s.pending++] = b & 0xff; }; /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->input buffer and copying from it. * (See also flush_pending()). */ const read_buf = (strm, buf, start, size) => { let len = strm.avail_in; if (len > size) { len = size; } if (len === 0) { return 0; } strm.avail_in -= len; // zmemcpy(buf, strm->next_in, len); buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start); if (strm.state.wrap === 1) { strm.adler = adler32_1(strm.adler, buf, len, start); } else if (strm.state.wrap === 2) { strm.adler = crc32_1(strm.adler, buf, len, start); } strm.next_in += len; strm.total_in += len; return len; }; /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ const longest_match = (s, cur_match) => { let chain_length = s.max_chain_length; /* max hash chain length */ let scan = s.strstart; /* current string */ let match; /* matched string */ let len; /* length of current match */ let best_len = s.prev_length; /* best match length so far */ let nice_match = s.nice_match; /* stop if match long enough */ const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; const _win = s.window; // shortcut const wmask = s.w_mask; const prev = s.prev; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ const strend = s.strstart + MAX_MATCH; let scan_end1 = _win[scan + best_len - 1]; let scan_end = _win[scan + best_len]; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s.prev_length >= s.good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if (nice_match > s.lookahead) { nice_match = s.lookahead; } // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { // Assert(cur_match < s->strstart, "no future"); match = cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) { continue; } /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2; match++; // Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { /*jshint noempty:false*/ } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend); // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (strend - scan); scan = strend - MAX_MATCH; if (len > best_len) { s.match_start = cur_match; best_len = len; if (len >= nice_match) { break; } scan_end1 = _win[scan + best_len - 1]; scan_end = _win[scan + best_len]; } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); if (best_len <= s.lookahead) { return best_len; } return s.lookahead; }; /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ const fill_window = (s) => { const _w_size = s.w_size; let p, n, m, more, str; //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = s.window_size - s.lookahead - s.strstart; // JS ints have 32 bit, block below not needed /* Deal with !@#$% 64K limit: */ //if (sizeof(int) <= 2) { // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { // more = wsize; // // } else if (more == (unsigned)(-1)) { // /* Very unlikely, but possible on 16 bit machine if // * strstart == 0 && lookahead == 1 (input done a byte at time) // */ // more--; // } //} /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0); s.match_start -= _w_size; s.strstart -= _w_size; /* we now have strstart >= MAX_DIST */ s.block_start -= _w_size; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage). We slide even when level == 0 to keep the hash table consistent if we switch back to level > 0 later. (Using level 0 permanently is not an optimal usage of zlib, so we don"t care about this pathological case.) */ n = s.hash_size; p = n; do { m = s.head[--p]; s.head[p] = (m >= _w_size ? m - _w_size : 0); } while (--n); n = _w_size; p = n; do { m = s.prev[--p]; s.prev[p] = (m >= _w_size ? m - _w_size : 0); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); more += _w_size; } if (s.strm.avail_in === 0) { break; } /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ //Assert(more >= 2, "more < 2"); n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); s.lookahead += n; /* Initialize the hash value now that we have some input: */ if (s.lookahead + s.insert >= MIN_MATCH) { str = s.strstart - s.insert; s.ins_h = s.window[str]; /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); //#if MIN_MATCH != 3 // Call update_hash() MIN_MATCH-3 more times //#endif while (s.insert) { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; s.insert--; if (s.lookahead + s.insert < MIN_MATCH) { break; } } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ // if (s.high_water < s.window_size) { // const curr = s.strstart + s.lookahead; // let init = 0; // // if (s.high_water < curr) { // /* Previous high water mark below current data -- zero WIN_INIT // * bytes or up to end of window, whichever is less. // */ // init = s.window_size - curr; // if (init > WIN_INIT) // init = WIN_INIT; // zmemzero(s->window + curr, (unsigned)init); // s->high_water = curr + init; // } // else if (s->high_water < (ulg)curr + WIN_INIT) { // /* High water mark at or above current data, but below current data // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up // * to end of window, whichever is less. // */ // init = (ulg)curr + WIN_INIT - s->high_water; // if (init > s->window_size - s->high_water) // init = s->window_size - s->high_water; // zmemzero(s->window + s->high_water, (unsigned)init); // s->high_water += init; // } // } // // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, // "not enough room for search"); }; /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * This function does not insert new strings in the dictionary since * uncompressible data is probably not useful. This function is used * only for the level=0 compression option. * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ const deflate_stored = (s, flush) => { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: */ let max_block_size = 0xffff; if (max_block_size > s.pending_buf_size - 5) { max_block_size = s.pending_buf_size - 5; } /* Copy as much as possible from input to output: */ for (;;) { /* Fill the window as much as possible: */ if (s.lookahead <= 1) { //Assert(s->strstart < s->w_size+MAX_DIST(s) || // s->block_start >= (long)s->w_size, "slide too late"); // if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || // s.block_start >= s.w_size)) { // throw new Error("slide too late"); // } fill_window(s); if (s.lookahead === 0 && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } //Assert(s->block_start >= 0L, "block gone"); // if (s.block_start < 0) throw new Error("block gone"); s.strstart += s.lookahead; s.lookahead = 0; /* Emit a stored block if pending_buf will be full: */ const max_start = s.block_start + max_block_size; if (s.strstart === 0 || s.strstart >= max_start) { /* strstart == 0 is possible when wraparound on 16-bit machine */ s.lookahead = s.strstart - max_start; s.strstart = max_start; /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } /* Flush if we may have to slide, otherwise block_start may become * negative and the data will be gone: */ if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.strstart > s.block_start) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_NEED_MORE; }; /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ const deflate_fast = (s, flush) => { let hash_head; /* head of the hash chain */ let bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; /* flush the current block */ } } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ } if (s.match_length >= MIN_MATCH) { // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only /*** _tr_tally_dist(s, s.strstart - s.match_start, s.match_length - MIN_MATCH, bflush); ***/ bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { s.match_length--; /* string at strstart already in table */ do { s.strstart++; /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s.match_length !== 0); s.strstart++; } else { s.strstart += s.match_length; s.match_length = 0; s.ins_h = s.window[s.strstart]; /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); //#if MIN_MATCH != 3 // Call UPDATE_HASH() MIN_MATCH-3 more times //#endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s.window[s.strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ const deflate_slow = (s, flush) => { let hash_head; /* head of hash chain */ let bflush; /* set if current block must be flushed */ let max_insert; /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. */ s.prev_length = s.match_length; s.prev_match = s.match_start; s.match_length = MIN_MATCH - 1; if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ if (s.match_length <= 5 && (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s.match_length = MIN_MATCH - 1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { max_insert = s.strstart + s.lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ //check_match(s, s.strstart-1, s.prev_match, s.prev_length); /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH, bflush);***/ bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s.lookahead -= s.prev_length - 1; s.prev_length -= 2; do { if (++s.strstart <= max_insert) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } } while (--s.prev_length !== 0); s.match_available = 0; s.match_length = MIN_MATCH - 1; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } else if (s.match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); if (bflush) { /*** FLUSH_BLOCK_ONLY(s, 0) ***/ flush_block_only(s, false); /***/ } s.strstart++; s.lookahead--; if (s.strm.avail_out === 0) { return BS_NEED_MORE; } } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s.match_available = 1; s.strstart++; s.lookahead--; } } //Assert (flush != Z_NO_FLUSH, "no flush?"); if (s.match_available) { //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); s.match_available = 0; } s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ const deflate_rle = (s, flush) => { let bflush; /* set if current block must be flushed */ let prev; /* byte at distance one to match */ let scan, strend; /* scan goes up to strend for length of run */ const _win = s.window; for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s.lookahead <= MAX_MATCH) { fill_window(s); if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* See how many times the previous byte repeats */ s.match_length = 0; if (s.lookahead >= MIN_MATCH && s.strstart > 0) { scan = s.strstart - 1; prev = _win[scan]; if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { strend = s.strstart + MAX_MATCH; do { /*jshint noempty:false*/ } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend); s.match_length = MAX_MATCH - (strend - scan); if (s.match_length > s.lookahead) { s.match_length = s.lookahead; } } //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s.match_length >= MIN_MATCH) { //check_match(s, s.strstart, s.strstart - 1, s.match_length); /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; s.strstart += s.match_length; s.match_length = 0; } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ const deflate_huff = (s, flush) => { let bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s.lookahead === 0) { fill_window(s); if (s.lookahead === 0) { if (flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } break; /* flush the current block */ } } /* Output a literal byte */ s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; }; /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ function Config(good_length, max_lazy, nice_length, max_chain, func) { this.good_length = good_length; this.max_lazy = max_lazy; this.nice_length = nice_length; this.max_chain = max_chain; this.func = func; } const configuration_table = [ /* good lazy nice chain */ new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ new Config(4, 5, 16, 8, deflate_fast), /* 2 */ new Config(4, 6, 32, 32, deflate_fast), /* 3 */ new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ new Config(8, 16, 32, 32, deflate_slow), /* 5 */ new Config(8, 16, 128, 128, deflate_slow), /* 6 */ new Config(8, 32, 128, 256, deflate_slow), /* 7 */ new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ ]; /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ const lm_init = (s) => { s.window_size = 2 * s.w_size; /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); /* Set the default configuration parameters: */ s.max_lazy_match = configuration_table[s.level].max_lazy; s.good_match = configuration_table[s.level].good_length; s.nice_match = configuration_table[s.level].nice_length; s.max_chain_length = configuration_table[s.level].max_chain; s.strstart = 0; s.block_start = 0; s.lookahead = 0; s.insert = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; s.ins_h = 0; }; function DeflateState() { this.strm = null; /* pointer back to this zlib stream */ this.status = 0; /* as the name implies */ this.pending_buf = null; /* output still pending */ this.pending_buf_size = 0; /* size of pending_buf */ this.pending_out = 0; /* next pending byte to output to the stream */ this.pending = 0; /* nb of bytes in the pending buffer */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.gzhead = null; /* gzip header information to write */ this.gzindex = 0; /* where in extra, name, or comment */ this.method = Z_DEFLATED$2; /* can only be DEFLATED */ this.last_flush = -1; /* value of flush param for previous deflate call */ this.w_size = 0; /* LZ77 window size (32K by default) */ this.w_bits = 0; /* log2(w_size) (8..16) */ this.w_mask = 0; /* w_size - 1 */ this.window = null; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. */ this.window_size = 0; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ this.prev = null; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ this.head = null; /* Heads of the hash chains or NIL. */ this.ins_h = 0; /* hash index of string to be inserted */ this.hash_size = 0; /* number of elements in hash table */ this.hash_bits = 0; /* log2(hash_size) */ this.hash_mask = 0; /* hash_size-1 */ this.hash_shift = 0; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ this.block_start = 0; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ this.match_length = 0; /* length of best match */ this.prev_match = 0; /* previous match */ this.match_available = 0; /* set if previous match exists */ this.strstart = 0; /* start of string to insert */ this.match_start = 0; /* start of matching string */ this.lookahead = 0; /* number of valid bytes ahead in window */ this.prev_length = 0; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ this.max_chain_length = 0; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ this.max_lazy_match = 0; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ // That"s alias to max_lazy_match, don"t use directly //this.max_insert_length = 0; /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ this.level = 0; /* compression level (1..9) */ this.strategy = 0; /* favor or force Huffman coding*/ this.good_match = 0; /* Use a faster search when the previous match is longer than this */ this.nice_match = 0; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn"t use ct_data typedef below to suppress compiler warning */ // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ // Use flat array of DOUBLE size, with interleaved fata, // because JS does not support effective this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2); this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2); this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2); zero(this.dyn_ltree); zero(this.dyn_dtree); zero(this.bl_tree); this.l_desc = null; /* desc. for literal tree */ this.d_desc = null; /* desc. for distance tree */ this.bl_desc = null; /* desc. for bit length tree */ //ush bl_count[MAX_BITS+1]; this.bl_count = new Uint16Array(MAX_BITS + 1); /* number of codes at each bit length for an optimal tree */ //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */ zero(this.heap); this.heap_len = 0; /* number of elements in the heap */ this.heap_max = 0; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; zero(this.depth); /* Depth of each subtree used as tie breaker for trees of equal frequency */ this.l_buf = 0; /* buffer index for literals or lengths */ this.lit_bufsize = 0; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can"t count above 4 */ this.last_lit = 0; /* running index in l_buf */ this.d_buf = 0; /* Buffer index for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ this.opt_len = 0; /* bit length of current block with optimal trees */ this.static_len = 0; /* bit length of current block with static trees */ this.matches = 0; /* number of string matches in current block */ this.insert = 0; /* bytes at end of window left to insert */ this.bi_buf = 0; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ this.bi_valid = 0; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ // Used for window memory init. We safely ignore it for JS. That makes // sense only for pointers and memory check tools. //this.high_water = 0; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } const deflateResetKeep = (strm) => { if (!strm || !strm.state) { return err(strm, Z_STREAM_ERROR$2); } strm.total_in = strm.total_out = 0; strm.data_type = Z_UNKNOWN; const s = strm.state; s.pending = 0; s.pending_out = 0; if (s.wrap < 0) { s.wrap = -s.wrap; /* was made negative by deflate(..., Z_FINISH); */ } s.status = (s.wrap ? INIT_STATE : BUSY_STATE); strm.adler = (s.wrap === 2) ? 0 // crc32(0, Z_NULL, 0) : 1; // adler32(0, Z_NULL, 0) s.last_flush = Z_NO_FLUSH$2; _tr_init(s); return Z_OK$3; }; const deflateReset = (strm) => { const ret = deflateResetKeep(strm); if (ret === Z_OK$3) { lm_init(strm.state); } return ret; }; const deflateSetHeader = (strm, head) => { if (!strm || !strm.state) { return Z_STREAM_ERROR$2; } if (strm.state.wrap !== 2) { return Z_STREAM_ERROR$2; } strm.state.gzhead = head; return Z_OK$3; }; const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { if (!strm) { // === Z_NULL return Z_STREAM_ERROR$2; } let wrap = 1; if (level === Z_DEFAULT_COMPRESSION$1) { level = 6; } if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return err(strm, Z_STREAM_ERROR$2); } if (windowBits === 8) { windowBits = 9; } /* until 256-byte window bug fixed */ const s = new DeflateState(); strm.state = s; s.strm = strm; s.wrap = wrap; s.gzhead = null; s.w_bits = windowBits; s.w_size = 1 << s.w_bits; s.w_mask = s.w_size - 1; s.hash_bits = memLevel + 7; s.hash_size = 1 << s.hash_bits; s.hash_mask = s.hash_size - 1; s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); s.window = new Uint8Array(s.w_size * 2); s.head = new Uint16Array(s.hash_size); s.prev = new Uint16Array(s.w_size); // Don"t need mem init magic for JS. //s.high_water = 0; /* nothing written to s->window yet */ s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ s.pending_buf_size = s.lit_bufsize * 4; //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); //s->pending_buf = (uchf *) overlay; s.pending_buf = new Uint8Array(s.pending_buf_size); // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s.d_buf = 1 * s.lit_bufsize; //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s.l_buf = (1 + 2) * s.lit_bufsize; s.level = level; s.strategy = strategy; s.method = method; return deflateReset(strm); }; const deflateInit = (strm, level) => { return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1); }; const deflate$2 = (strm, flush) => { let beg, val; // for gzip header write only if (!strm || !strm.state || flush > Z_BLOCK$1 || flush < 0) { return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2; } const s = strm.state; if (!strm.output || (!strm.input && strm.avail_in !== 0) || (s.status === FINISH_STATE && flush !== Z_FINISH$3)) { return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2); } s.strm = strm; /* just in case */ const old_flush = s.last_flush; s.last_flush = flush; /* Write the header */ if (s.status === INIT_STATE) { if (s.wrap === 2) { // GZIP header strm.adler = 0; //crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (!s.gzhead) { // s->gzhead == Z_NULL put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s.status = BUSY_STATE; } else { put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16) ); put_byte(s, s.gzhead.time & 0xff); put_byte(s, (s.gzhead.time >> 8) & 0xff); put_byte(s, (s.gzhead.time >> 16) & 0xff); put_byte(s, (s.gzhead.time >> 24) & 0xff); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, s.gzhead.os & 0xff); if (s.gzhead.extra && s.gzhead.extra.length) { put_byte(s, s.gzhead.extra.length & 0xff); put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); } if (s.gzhead.hcrc) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0); } s.gzindex = 0; s.status = EXTRA_STATE; } } else // DEFLATE header { let header = (Z_DEFLATED$2 + ((s.w_bits - 8) << 4)) << 8; let level_flags = -1; if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { level_flags = 0; } else if (s.level < 6) { level_flags = 1; } else if (s.level === 6) { level_flags = 2; } else { level_flags = 3; } header |= (level_flags << 6); if (s.strstart !== 0) { header |= PRESET_DICT; } header += 31 - (header % 31); s.status = BUSY_STATE; putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s.strstart !== 0) { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } strm.adler = 1; // adler32(0L, Z_NULL, 0); } } //#ifdef GZIP if (s.status === EXTRA_STATE) { if (s.gzhead.extra/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { break; } } put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); s.gzindex++; } if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } if (s.gzindex === s.gzhead.extra.length) { s.gzindex = 0; s.status = NAME_STATE; } } else { s.status = NAME_STATE; } } if (s.status === NAME_STATE) { if (s.gzhead.name/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ //int val; do { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { val = 1; break; } } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.name.length) { val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } if (val === 0) { s.gzindex = 0; s.status = COMMENT_STATE; } } else { s.status = COMMENT_STATE; } } if (s.status === COMMENT_STATE) { if (s.gzhead.comment/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ //int val; do { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { val = 1; break; } } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.comment.length) { val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); } if (val === 0) { s.status = HCRC_STATE; } } else { s.status = HCRC_STATE; } } if (s.status === HCRC_STATE) { if (s.gzhead.hcrc) { if (s.pending + 2 > s.pending_buf_size) { flush_pending(strm); } if (s.pending + 2 <= s.pending_buf_size) { put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); strm.adler = 0; //crc32(0L, Z_NULL, 0); s.status = BUSY_STATE; } } else { s.status = BUSY_STATE; } } //#endif /* Flush as much pending output as possible */ if (s.pending !== 0) { flush_pending(strm); if (strm.avail_out === 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won"t be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s.last_flush = -1; return Z_OK$3; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) { return err(strm, Z_BUF_ERROR$1); } /* User must not provide more input after the first FINISH: */ if (s.status === FINISH_STATE && strm.avail_in !== 0) { return err(strm, Z_BUF_ERROR$1); } /* Start a new block or continue the current one. */ if (strm.avail_in !== 0 || s.lookahead !== 0 || (flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE)) { let bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : (s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush)); if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { s.status = FINISH_STATE; } if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK$3; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don"t have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate === BS_BLOCK_DONE) { if (flush === Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush !== Z_BLOCK$1) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, 0, 0, false); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush === Z_FULL_FLUSH$1) { /*** CLEAR_HASH(s); ***/ /* forget history */ zero(s.head); // Fill with NIL (= 0); if (s.lookahead === 0) { s.strstart = 0; s.block_start = 0; s.insert = 0; } } } flush_pending(strm); if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK$3; } } } //Assert(strm->avail_out > 0, "bug2"); //if (strm.avail_out <= 0) { throw new Error("bug2");} if (flush !== Z_FINISH$3) { return Z_OK$3; } if (s.wrap <= 0) { return Z_STREAM_END$3; } /* Write the trailer */ if (s.wrap === 2) { put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); put_byte(s, (strm.adler >> 16) & 0xff); put_byte(s, (strm.adler >> 24) & 0xff); put_byte(s, strm.total_in & 0xff); put_byte(s, (strm.total_in >> 8) & 0xff); put_byte(s, (strm.total_in >> 16) & 0xff); put_byte(s, (strm.total_in >> 24) & 0xff); } else { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s.wrap > 0) { s.wrap = -s.wrap; } /* write the trailer only once! */ return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3; }; const deflateEnd = (strm) => { if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { return Z_STREAM_ERROR$2; } const status = strm.state.status; if (status !== INIT_STATE && status !== EXTRA_STATE && status !== NAME_STATE && status !== COMMENT_STATE && status !== HCRC_STATE && status !== BUSY_STATE && status !== FINISH_STATE ) { return err(strm, Z_STREAM_ERROR$2); } strm.state = null; return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3; }; /* ========================================================================= * Initializes the compression dictionary from the given byte * sequence without producing any compressed output. */ const deflateSetDictionary = (strm, dictionary) => { let dictLength = dictionary.length; if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { return Z_STREAM_ERROR$2; } const s = strm.state; const wrap = s.wrap; if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { return Z_STREAM_ERROR$2; } /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap === 1) { /* adler32(strm->adler, dictionary, dictLength); */ strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0); } s.wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s.w_size) { if (wrap === 0) { /* already empty otherwise */ /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); s.strstart = 0; s.block_start = 0; s.insert = 0; } /* use the tail */ // dictionary = dictionary.slice(dictLength - s.w_size); let tmpDict = new Uint8Array(s.w_size); tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0); dictionary = tmpDict; dictLength = s.w_size; } /* insert dictionary into window and hash */ const avail = strm.avail_in; const next = strm.next_in; const input = strm.input; strm.avail_in = dictLength; strm.next_in = 0; strm.input = dictionary; fill_window(s); while (s.lookahead >= MIN_MATCH) { let str = s.strstart; let n = s.lookahead - (MIN_MATCH - 1); do { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; } while (--n); s.strstart = str; s.lookahead = MIN_MATCH - 1; fill_window(s); } s.strstart += s.lookahead; s.block_start = s.strstart; s.insert = s.lookahead; s.lookahead = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; strm.next_in = next; strm.input = input; strm.avail_in = avail; s.wrap = wrap; return Z_OK$3; }; var deflateInit_1 = deflateInit; var deflateInit2_1 = deflateInit2; var deflateReset_1 = deflateReset; var deflateResetKeep_1 = deflateResetKeep; var deflateSetHeader_1 = deflateSetHeader; var deflate_2$1 = deflate$2; var deflateEnd_1 = deflateEnd; var deflateSetDictionary_1 = deflateSetDictionary; var deflateInfo = "pako deflate (from Nodeca project)"; /* Not implemented module.exports.deflateBound = deflateBound; module.exports.deflateCopy = deflateCopy; module.exports.deflateParams = deflateParams; module.exports.deflatePending = deflatePending; module.exports.deflatePrime = deflatePrime; module.exports.deflateTune = deflateTune; */ var deflate_1$2 = { deflateInit: deflateInit_1, deflateInit2: deflateInit2_1, deflateReset: deflateReset_1, deflateResetKeep: deflateResetKeep_1, deflateSetHeader: deflateSetHeader_1, deflate: deflate_2$1, deflateEnd: deflateEnd_1, deflateSetDictionary: deflateSetDictionary_1, deflateInfo: deflateInfo }; const _has = (obj, key) => { return Object.prototype.hasOwnProperty.call(obj, key); }; var assign = function (obj /*from1, from2, from3, ...*/) { const sources = Array.prototype.slice.call(arguments, 1); while (sources.length) { const source = sources.shift(); if (!source) { continue; } if (typeof source !== "object") { throw new TypeError(source + "must be non-object"); } for (const p in source) { if (_has(source, p)) { obj[p] = source[p]; } } } return obj; }; // Join array of chunks to single array. var flattenChunks = (chunks) => { // calculate data length let len = 0; for (let i = 0, l = chunks.length; i < l; i++) { len += chunks[i].length; } // join chunks const result = new Uint8Array(len); for (let i = 0, pos = 0, l = chunks.length; i < l; i++) { let chunk = chunks[i]; result.set(chunk, pos); pos += chunk.length; } return result; }; var common = { assign: assign, flattenChunks: flattenChunks }; // String encode/decode helpers // Quick check if we can use fast array to bin string conversion // // - apply(Array) can fail on Android 2.2 // - apply(Uint8Array) can fail on iOS 5.1 Safari // let STR_APPLY_UIA_OK = true; try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; } // Table with utf8 lengths (calculated by first byte of sequence) // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, // because max possible codepoint is 0x10ffff const _utf8len = new Uint8Array(256); for (let q = 0; q < 256; q++) { _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); } _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start // convert string to array (typed, when possible) var string2buf = (str) => { if (typeof TextEncoder === "function" && TextEncoder.prototype.encode) { return new TextEncoder().encode(str); } let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; // count binary size for (m_pos = 0; m_pos < str_len; m_pos++) { c = str.charCodeAt(m_pos); if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { c2 = str.charCodeAt(m_pos + 1); if ((c2 & 0xfc00) === 0xdc00) { c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); m_pos++; } } buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; } // allocate buffer buf = new Uint8Array(buf_len); // convert for (i = 0, m_pos = 0; i < buf_len; m_pos++) { c = str.charCodeAt(m_pos); if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { c2 = str.charCodeAt(m_pos + 1); if ((c2 & 0xfc00) === 0xdc00) { c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); m_pos++; } } if (c < 0x80) { /* one byte */ buf[i++] = c; } else if (c < 0x800) { /* two bytes */ buf[i++] = 0xC0 | (c >>> 6); buf[i++] = 0x80 | (c & 0x3f); } else if (c < 0x10000) { /* three bytes */ buf[i++] = 0xE0 | (c >>> 12); buf[i++] = 0x80 | (c >>> 6 & 0x3f); buf[i++] = 0x80 | (c & 0x3f); } else { /* four bytes */ buf[i++] = 0xf0 | (c >>> 18); buf[i++] = 0x80 | (c >>> 12 & 0x3f); buf[i++] = 0x80 | (c >>> 6 & 0x3f); buf[i++] = 0x80 | (c & 0x3f); } } return buf; }; // Helper const buf2binstring = (buf, len) => { // On Chrome, the arguments in a function call that are allowed is `65534`. // If the length of the buffer is smaller than that, we can use this optimization, // otherwise we will take a slower path. if (len < 65534) { if (buf.subarray && STR_APPLY_UIA_OK) { return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len)); } } let result = ""; for (let i = 0; i < len; i++) { result += String.fromCharCode(buf[i]); } return result; }; // convert array to string var buf2string = (buf, max) => { const len = max || buf.length; if (typeof TextDecoder === "function" && TextDecoder.prototype.decode) { return new TextDecoder().decode(buf.subarray(0, max)); } let i, out; // Reserve max possible length (2 words per char) // NB: by unknown reasons, Array is significantly faster for // String.fromCharCode.apply than Uint16Array. const utf16buf = new Array(len * 2); for (out = 0, i = 0; i < len;) { let c = buf[i++]; // quick process ascii if (c < 0x80) { utf16buf[out++] = c; continue; } let c_len = _utf8len[c]; // skip 5 & 6 byte codes if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; } // apply mask on first byte c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; // join the rest while (c_len > 1 && i < len) { c = (c << 6) | (buf[i++] & 0x3f); c_len--; } // terminated by end of string? if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } if (c < 0x10000) { utf16buf[out++] = c; } else { c -= 0x10000; utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); utf16buf[out++] = 0xdc00 | (c & 0x3ff); } } return buf2binstring(utf16buf, out); }; // Calculate max possible position in utf8 buffer, // that will not break sequence. If that"s not possible // - (very small limits) return max size as is. // // buf[] - utf8 bytes array // max - length limit (mandatory); var utf8border = (buf, max) => { max = max || buf.length; if (max > buf.length) { max = buf.length; } // go back from last position, until start of sequence found let pos = max - 1; while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } // Very small and broken sequence, // return max, because we should return something anyway. if (pos < 0) { return max; } // If we came to start of buffer - that means buffer is too small, // return max too. if (pos === 0) { return max; } return (pos + _utf8len[buf[pos]] > max) ? pos : max; }; var strings = { string2buf: string2buf, buf2string: buf2string, utf8border: utf8border }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function ZStream() { /* next input byte */ this.input = null; // JS specific, because we have no pointers this.next_in = 0; /* number of bytes available at input */ this.avail_in = 0; /* total number of input bytes read so far */ this.total_in = 0; /* next output byte should be put there */ this.output = null; // JS specific, because we have no pointers this.next_out = 0; /* remaining free space at output */ this.avail_out = 0; /* total number of bytes output so far */ this.total_out = 0; /* last error message, NULL if no error */ this.msg = ""/*Z_NULL*/; /* not visible by applications */ this.state = null; /* best guess about the data type: binary or text */ this.data_type = 2/*Z_UNKNOWN*/; /* adler32 value of the uncompressed data */ this.adler = 0; } var zstream = ZStream; const toString$1 = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH: Z_NO_FLUSH$1, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH: Z_FINISH$2, Z_OK: Z_OK$2, Z_STREAM_END: Z_STREAM_END$2, Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY, Z_DEFLATED: Z_DEFLATED$1 } = constants$2; /* ===========================================================================*/ /** * class Deflate * * Generic JS-style wrapper for zlib calls. If you don"t need * streaming behaviour - use more simple functions: [[deflate]], * [[deflateRaw]] and [[gzip]]. **/ /* internal * Deflate.chunks -> Array * * Chunks of output data, if [[Deflate#onData]] not overridden. **/ /** * Deflate.result -> Uint8Array * * Compressed result, generated by default [[Deflate#onData]] * and [[Deflate#onEnd]] handlers. Filled after you push last chunk * (call [[Deflate#push]] with `Z_FINISH` / `true` param). **/ /** * Deflate.err -> Number * * Error code after deflate finished. 0 (Z_OK) on success. * You will not need it in real life, because deflate errors * are possible only on wrong options or bad `onData` / `onEnd` * custom handlers. **/ /** * Deflate.msg -> String * * Error message, if [[Deflate.err]] != 0 **/ /** * new Deflate(options) * - options (Object): zlib deflate options. * * Creates new deflator instance with specified params. Throws exception * on bad params. Supported options: * * - `level` * - `windowBits` * - `memLevel` * - `strategy` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw deflate * - `gzip` (Boolean) - create gzip wrapper * - `header` (Object) - custom header for gzip * - `text` (Boolean) - true if compressed data believed to be text * - `time` (Number) - modification time, unix timestamp * - `os` (Number) - operation system code * - `extra` (Array) - array of bytes with extra data (max 65536) * - `name` (String) - file name (binary string) * - `comment` (String) - comment (binary string) * - `hcrc` (Boolean) - true if header crc should be added * * ##### Example: * * ```javascript * const pako = require("pako") * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * const deflate = new pako.Deflate({ level: 3}); * * deflate.push(chunk1, false); * deflate.push(chunk2, true); // true -> last chunk * * if (deflate.err) { throw new Error(deflate.err); } * * console.log(deflate.result); * ``` **/ function Deflate$1(options) { this.options = common.assign({ level: Z_DEFAULT_COMPRESSION, method: Z_DEFLATED$1, chunkSize: 16384, windowBits: 15, memLevel: 8, strategy: Z_DEFAULT_STRATEGY }, options || {}); let opt = this.options; if (opt.raw && (opt.windowBits > 0)) { opt.windowBits = -opt.windowBits; } else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { opt.windowBits += 16; } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ""; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new zstream(); this.strm.avail_out = 0; let status = deflate_1$2.deflateInit2( this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy ); if (status !== Z_OK$2) { throw new Error(messages[status]); } if (opt.header) { deflate_1$2.deflateSetHeader(this.strm, opt.header); } if (opt.dictionary) { let dict; // Convert data if needed if (typeof opt.dictionary === "string") { // If we need to compress text, change encoding to utf8. dict = strings.string2buf(opt.dictionary); } else if (toString$1.call(opt.dictionary) === "[object ArrayBuffer]") { dict = new Uint8Array(opt.dictionary); } else { dict = opt.dictionary; } status = deflate_1$2.deflateSetDictionary(this.strm, dict); if (status !== Z_OK$2) { throw new Error(messages[status]); } this._dict_set = true; } } /** * Deflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be * converted to utf8 byte sequence. * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. * * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with * new compressed chunks. Returns `true` on success. The last data block must * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending * buffers and call [[Deflate#onEnd]]. * * On fail call [[Deflate#onEnd]] with error code and return false. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Deflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; let status, _flush_mode; if (this.ended) { return false; } if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; // Convert data if needed if (typeof data === "string") { // If we need to compress text, change encoding to utf8. strm.input = strings.string2buf(data); } else if (toString$1.call(data) === "[object ArrayBuffer]") { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } // Make sure avail_out > 6 to avoid repeating markers if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) { this.onData(strm.output.subarray(0, strm.next_out)); strm.avail_out = 0; continue; } status = deflate_1$2.deflate(strm, _flush_mode); // Ended => flush and finish if (status === Z_STREAM_END$2) { if (strm.next_out > 0) { this.onData(strm.output.subarray(0, strm.next_out)); } status = deflate_1$2.deflateEnd(this.strm); this.onEnd(status); this.ended = true; return status === Z_OK$2; } // Flush if out buffer full if (strm.avail_out === 0) { this.onData(strm.output); continue; } // Flush if requested and has data if (_flush_mode > 0 && strm.next_out > 0) { this.onData(strm.output.subarray(0, strm.next_out)); strm.avail_out = 0; continue; } if (strm.avail_in === 0) break; } return true; }; /** * Deflate#onData(chunk) -> Void * - chunk (Uint8Array): output data. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Deflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Deflate#onEnd(status) -> Void * - status (Number): deflate status. 0 (Z_OK) on success, * other if not. * * Called once after you tell deflate that the input stream is * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Deflate$1.prototype.onEnd = function (status) { // On success - join if (status === Z_OK$2) { this.result = common.flattenChunks(this.chunks); } this.chunks = []; this.err = status; this.msg = this.strm.msg; }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // See state defs from inflate.js const BAD$1 = 30; /* got a data error -- remain here until reset */ const TYPE$1 = 12; /* i: waiting for type bits, including last-flag bit */ /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state.mode === LEN strm.avail_in >= 6 strm.avail_out >= 258 start >= strm.avail_out state.bits < 8 On return, state.mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm.avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm.avail_out >= 258 for each loop to avoid checking for output space. */ var inffast = function inflate_fast(strm, start) { let _in; /* local strm.input */ let last; /* have enough input while in < last */ let _out; /* local strm.output */ let beg; /* inflate()"s initial strm.output */ let end; /* while out < end, enough space available */ //#ifdef INFLATE_STRICT let dmax; /* maximum distance from zlib header */ //#endif let wsize; /* window size or zero if not using window */ let whave; /* valid bytes in the window */ let wnext; /* window write index */ // Use `s_window` instead `window`, avoid conflict with instrumentation tools let s_window; /* allocated sliding window, if wsize != 0 */ let hold; /* local strm.hold */ let bits; /* local strm.bits */ let lcode; /* local strm.lencode */ let dcode; /* local strm.distcode */ let lmask; /* mask for first level of length codes */ let dmask; /* mask for first level of distance codes */ let here; /* retrieved table entry */ let op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ let len; /* match length, unused bytes */ let dist; /* match distance */ let from; /* where to copy match from */ let from_source; let input, output; // JS specific, because we have no pointers /* copy state to local variables */ const state = strm.state; //here = state.here; _in = strm.next_in; input = strm.input; last = _in + (strm.avail_in - 5); _out = strm.next_out; output = strm.output; beg = _out - (start - strm.avail_out); end = _out + (strm.avail_out - 257); //#ifdef INFLATE_STRICT dmax = state.dmax; //#endif wsize = state.wsize; whave = state.whave; wnext = state.wnext; s_window = state.window; hold = state.hold; bits = state.bits; lcode = state.lencode; dcode = state.distcode; lmask = (1 << state.lenbits) - 1; dmask = (1 << state.distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ top: do { if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = lcode[hold & lmask]; dolen: for (;;) { // Goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op === 0) { /* literal */ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal "%c" " : // "inflate: literal 0x%02x ", here.val)); output[_out++] = here & 0xffff/*here.val*/; } else if (op & 16) { /* length base */ len = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += input[_in++] << bits; bits += 8; } len += hold & ((1 << op) - 1); hold >>>= op; bits -= op; } //Tracevv((stderr, "inflate: length %u ", len)); if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = dcode[hold & dmask]; dodist: for (;;) { // goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op & 16) { /* distance base */ dist = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (bits < op) { hold += input[_in++] << bits; bits += 8; if (bits < op) { hold += input[_in++] << bits; bits += 8; } } dist += hold & ((1 << op) - 1); //#ifdef INFLATE_STRICT if (dist > dmax) { strm.msg = "invalid distance too far back"; state.mode = BAD$1; break top; } //#endif hold >>>= op; bits -= op; //Tracevv((stderr, "inflate: distance %u ", dist)); op = _out - beg; /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state.sane) { strm.msg = "invalid distance too far back"; state.mode = BAD$1; break top; } // (!) This block is disabled in zlib defaults, // don"t enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // if (len <= op - whave) { // do { // output[_out++] = 0; // } while (--len); // continue top; // } // len -= op - whave; // do { // output[_out++] = 0; // } while (--op > whave); // if (op === 0) { // from = _out - dist; // do { // output[_out++] = output[from++]; // } while (--len); // continue top; // } //#endif } from = 0; // window index from_source = s_window; if (wnext === 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = 0; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } while (len > 2) { output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; len -= 3; } if (len) { output[_out++] = from_source[from++]; if (len > 1) { output[_out++] = from_source[from++]; } } } else { from = _out - dist; /* copy direct from output */ do { /* minimum length is three */ output[_out++] = output[from++]; output[_out++] = output[from++]; output[_out++] = output[from++]; len -= 3; } while (len > 2); if (len) { output[_out++] = output[from++]; if (len > 1) { output[_out++] = output[from++]; } } } } else if ((op & 64) === 0) { /* 2nd level distance code */ here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dodist; } else { strm.msg = "invalid distance code"; state.mode = BAD$1; break top; } break; // need to emulate goto via "continue" } } else if ((op & 64) === 0) { /* 2nd level length code */ here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dolen; } else if (op & 32) { /* end-of-block */ //Tracevv((stderr, "inflate: end of block ")); state.mode = TYPE$1; break top; } else { strm.msg = "invalid literal/length code"; state.mode = BAD$1; break top; } break; // need to emulate goto via "continue" } } while (_in < last && _out < end); /* return unused bytes (on entry, bits < 8, so in won"t go too far back) */ len = bits >> 3; _in -= len; bits -= len << 3; hold &= (1 << bits) - 1; /* update state and return */ strm.next_in = _in; strm.next_out = _out; strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); state.hold = hold; state.bits = bits; return; }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const MAXBITS = 15; const ENOUGH_LENS$1 = 852; const ENOUGH_DISTS$1 = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); const CODES$1 = 0; const LENS$1 = 1; const DISTS$1 = 2; const lbase = new Uint16Array([ /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ]); const lext = new Uint8Array([ /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 ]); const dbase = new Uint16Array([ /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 ]); const dext = new Uint8Array([ /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64 ]); const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => { const bits = opts.bits; //here = opts.here; /* table entry for duplication */ let len = 0; /* a code"s length in bits */ let sym = 0; /* index of code symbols */ let min = 0, max = 0; /* minimum and maximum code lengths */ let root = 0; /* number of index bits for root table */ let curr = 0; /* number of index bits for current table */ let drop = 0; /* code bits to drop for sub-table */ let left = 0; /* number of prefix codes available */ let used = 0; /* code entries in table used */ let huff = 0; /* Huffman code */ let incr; /* for incrementing code, index */ let fill; /* index for replicating entries */ let low; /* low bits for current root entry */ let mask; /* mask for low root bits */ let next; /* next available space in table */ let base = null; /* base value table to use */ let base_index = 0; // let shoextra; /* extra bits table to use */ let end; /* use base and extra for symbol > end */ const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ let extra = null; let extra_index = 0; let here_bits, here_op, here_val; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) { count[len] = 0; } for (sym = 0; sym < codes; sym++) { count[lens[lens_index + sym]]++; } /* bound code lengths, force root to be within code lengths */ root = bits; for (max = MAXBITS; max >= 1; max--) { if (count[max] !== 0) { break; } } if (root > max) { root = max; } if (max === 0) { /* no symbols to code at all */ //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ //table.bits[opts.table_index] = 1; //here.bits = (var char)1; //table.val[opts.table_index++] = 0; //here.val = (var short)0; table[table_index++] = (1 << 24) | (64 << 16) | 0; //table.op[opts.table_index] = 64; //table.bits[opts.table_index] = 1; //table.val[opts.table_index++] = 0; table[table_index++] = (1 << 24) | (64 << 16) | 0; opts.bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) { if (count[min] !== 0) { break; } } if (root < min) { root = min; } /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) { return -1; } /* over-subscribed */ } if (left > 0 && (type === CODES$1 || max !== 1)) { return -1; /* incomplete set */ } /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) { offs[len + 1] = offs[len] + count[len]; } /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) { if (lens[lens_index + sym] !== 0) { work[offs[lens[lens_index + sym]]++] = sym; } } /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ // poor man optimization - use if-else instead of switch, // to avoid deopts in old v8 if (type === CODES$1) { base = extra = work; /* dummy value--not used */ end = 19; } else if (type === LENS$1) { base = lbase; base_index -= 257; extra = lext; extra_index -= 257; end = 256; } else { /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize opts for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = table_index; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = -1; /* trigger new sub-table when len > root */ used = 1 << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type === LENS$1 && used > ENOUGH_LENS$1) || (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } /* process all codes and make table entries */ for (;;) { /* create table entry */ here_bits = len - drop; if (work[sym] < end) { here_op = 0; here_val = work[sym]; } else if (work[sym] > end) { here_op = extra[extra_index + work[sym]]; here_val = base[base_index + work[sym]]; } else { here_op = 32 + 64; /* end of block */ here_val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1 << (len - drop); fill = 1 << curr; min = fill; /* save offset to next table */ do { fill -= incr; table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; } while (fill !== 0); /* backwards increment the len-bit code huff */ incr = 1 << (len - 1); while (huff & incr) { incr >>= 1; } if (incr !== 0) { huff &= incr - 1; huff += incr; } else { huff = 0; } /* go to next symbol, update count, len */ sym++; if (--count[len] === 0) { if (len === max) { break; } len = lens[lens_index + work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) !== low) { /* if first time, transition to sub-tables */ if (drop === 0) { drop = root; } /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = 1 << curr; while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) { break; } curr++; left <<= 1; } /* check for enough space */ used += 1 << curr; if ((type === LENS$1 && used > ENOUGH_LENS$1) || (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } /* point entry in root table to sub-table */ low = huff & mask; /*table.op[low] = curr; table.bits[low] = root; table.val[low] = next - opts.table_index;*/ table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff !== 0) { //table.op[next + huff] = 64; /* invalid code marker */ //table.bits[next + huff] = len - drop; //table.val[next + huff] = 0; table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; } /* set return parameters */ //opts.table_index += used; opts.bits = root; return 0; }; var inftrees = inflate_table; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. const CODES = 0; const LENS = 1; const DISTS = 2; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_FINISH: Z_FINISH$1, Z_BLOCK, Z_TREES, Z_OK: Z_OK$1, Z_STREAM_END: Z_STREAM_END$1, Z_NEED_DICT: Z_NEED_DICT$1, Z_STREAM_ERROR: Z_STREAM_ERROR$1, Z_DATA_ERROR: Z_DATA_ERROR$1, Z_MEM_ERROR: Z_MEM_ERROR$1, Z_BUF_ERROR, Z_DEFLATED } = constants$2; /* STATES ====================================================================*/ /* ===========================================================================*/ const HEAD = 1; /* i: waiting for magic header */ const FLAGS = 2; /* i: waiting for method and flags (gzip) */ const TIME = 3; /* i: waiting for modification time (gzip) */ const OS = 4; /* i: waiting for extra flags and operating system (gzip) */ const EXLEN = 5; /* i: waiting for extra length (gzip) */ const EXTRA = 6; /* i: waiting for extra bytes (gzip) */ const NAME = 7; /* i: waiting for end of file name (gzip) */ const COMMENT = 8; /* i: waiting for end of comment (gzip) */ const HCRC = 9; /* i: waiting for header crc (gzip) */ const DICTID = 10; /* i: waiting for dictionary check value */ const DICT = 11; /* waiting for inflateSetDictionary() call */ const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ const TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ const STORED = 14; /* i: waiting for stored size (length and complement) */ const COPY_ = 15; /* i/o: same as COPY below, but only first time in */ const COPY = 16; /* i/o: waiting for input or output to copy stored block */ const TABLE = 17; /* i: waiting for dynamic block table lengths */ const LENLENS = 18; /* i: waiting for code length code lengths */ const CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ const LEN_ = 20; /* i: same as LEN below, but only first time in */ const LEN = 21; /* i: waiting for length/lit/eob code */ const LENEXT = 22; /* i: waiting for length extra bits */ const DIST = 23; /* i: waiting for distance code */ const DISTEXT = 24; /* i: waiting for distance extra bits */ const MATCH = 25; /* o: waiting for output space to copy string */ const LIT = 26; /* o: waiting for output space to write literal */ const CHECK = 27; /* i: waiting for 32-bit check value */ const LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ const DONE = 29; /* finished check, done -- remain here until reset */ const BAD = 30; /* got a data error -- remain here until reset */ const MEM = 31; /* got an inflate() memory error -- remain here until reset */ const SYNC = 32; /* looking for synchronization bytes to restart inflate() */ /* ===========================================================================*/ const ENOUGH_LENS = 852; const ENOUGH_DISTS = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); const MAX_WBITS = 15; /* 32K LZ77 window */ const DEF_WBITS = MAX_WBITS; const zswap32 = (q) => { return (((q >>> 24) & 0xff) + ((q >>> 8) & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24)); }; function InflateState() { this.mode = 0; /* current inflate mode */ this.last = false; /* true if processing last block */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.havedict = false; /* true if dictionary provided */ this.flags = 0; /* gzip header method and flags (0 if zlib) */ this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ this.check = 0; /* protected copy of check value */ this.total = 0; /* protected copy of output count */ // TODO: may be {} this.head = null; /* where to save gzip header information */ /* sliding window */ this.wbits = 0; /* log base 2 of requested window size */ this.wsize = 0; /* window size or zero if not using window */ this.whave = 0; /* valid bytes in the window */ this.wnext = 0; /* window write index */ this.window = null; /* allocated sliding window, if needed */ /* bit accumulator */ this.hold = 0; /* input bit accumulator */ this.bits = 0; /* number of bits in "in" */ /* for string and stored block copying */ this.length = 0; /* literal or length of data to copy */ this.offset = 0; /* distance back to copy string from */ /* for table and code decoding */ this.extra = 0; /* extra bits needed */ /* fixed and dynamic code tables */ this.lencode = null; /* starting table for length/literal codes */ this.distcode = null; /* starting table for distance codes */ this.lenbits = 0; /* index bits for lencode */ this.distbits = 0; /* index bits for distcode */ /* dynamic table building */ this.ncode = 0; /* number of code length code lengths */ this.nlen = 0; /* number of length code lengths */ this.ndist = 0; /* number of distance code lengths */ this.have = 0; /* number of code lengths in lens[] */ this.next = null; /* next available space in codes[] */ this.lens = new Uint16Array(320); /* temporary storage for code lengths */ this.work = new Uint16Array(288); /* work area for code table building */ /* because we don"t have pointers in js, we use lencode and distcode directly as buffers so we don"t need codes */ //this.codes = new Int32Array(ENOUGH); /* space for code tables */ this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ this.distdyn = null; /* dynamic table for distance codes (JS specific) */ this.sane = 0; /* if false, allow invalid distance too far */ this.back = 0; /* bits back of last unprocessed length/lit */ this.was = 0; /* initial length of match */ } const inflateResetKeep = (strm) => { if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } const state = strm.state; strm.total_in = strm.total_out = state.total = 0; strm.msg = ""; /*Z_NULL*/ if (state.wrap) { /* to support ill-conceived Java test suite */ strm.adler = state.wrap & 1; } state.mode = HEAD; state.last = 0; state.havedict = 0; state.dmax = 32768; state.head = null/*Z_NULL*/; state.hold = 0; state.bits = 0; //state.lencode = state.distcode = state.next = state.codes; state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); state.sane = 1; state.back = -1; //Tracev((stderr, "inflate: reset ")); return Z_OK$1; }; const inflateReset = (strm) => { if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } const state = strm.state; state.wsize = 0; state.whave = 0; state.wnext = 0; return inflateResetKeep(strm); }; const inflateReset2 = (strm, windowBits) => { let wrap; /* get the state */ if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } const state = strm.state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 1; if (windowBits < 48) { windowBits &= 15; } } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) { return Z_STREAM_ERROR$1; } if (state.window !== null && state.wbits !== windowBits) { state.window = null; } /* update state and reset the rest of it */ state.wrap = wrap; state.wbits = windowBits; return inflateReset(strm); }; const inflateInit2 = (strm, windowBits) => { if (!strm) { return Z_STREAM_ERROR$1; } //strm.msg = Z_NULL; /* in case we return an error */ const state = new InflateState(); //if (state === Z_NULL) return Z_MEM_ERROR; //Tracev((stderr, "inflate: allocated ")); strm.state = state; state.window = null/*Z_NULL*/; const ret = inflateReset2(strm, windowBits); if (ret !== Z_OK$1) { strm.state = null/*Z_NULL*/; } return ret; }; const inflateInit = (strm) => { return inflateInit2(strm, DEF_WBITS); }; /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it"s called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ let virgin = true; let lenfix, distfix; // We have no pointers in JS, so keep tables separate const fixedtables = (state) => { /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { lenfix = new Int32Array(512); distfix = new Int32Array(32); /* literal/length table */ let sym = 0; while (sym < 144) { state.lens[sym++] = 8; } while (sym < 256) { state.lens[sym++] = 9; } while (sym < 280) { state.lens[sym++] = 7; } while (sym < 288) { state.lens[sym++] = 8; } inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); /* distance table */ sym = 0; while (sym < 32) { state.lens[sym++] = 5; } inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); /* do this just once */ virgin = false; } state.lencode = lenfix; state.lenbits = 9; state.distcode = distfix; state.distbits = 5; }; /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor"s data caches. */ const updatewindow = (strm, src, end, copy) => { let dist; const state = strm.state; /* if it hasn"t been done already, allocate space for the window */ if (state.window === null) { state.wsize = 1 << state.wbits; state.wnext = 0; state.whave = 0; state.window = new Uint8Array(state.wsize); } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state.wsize) { state.window.set(src.subarray(end - state.wsize, end), 0); state.wnext = 0; state.whave = state.wsize; } else { dist = state.wsize - state.wnext; if (dist > copy) { dist = copy; } //zmemcpy(state->window + state->wnext, end - copy, dist); state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); copy -= dist; if (copy) { //zmemcpy(state->window, end - copy, copy); state.window.set(src.subarray(end - copy, end), 0); state.wnext = copy; state.whave = state.wsize; } else { state.wnext += dist; if (state.wnext === state.wsize) { state.wnext = 0; } if (state.whave < state.wsize) { state.whave += dist; } } } return 0; }; const inflate$2 = (strm, flush) => { let state; let input, output; // input/output buffers let next; /* next input INDEX */ let put; /* next output INDEX */ let have, left; /* available input and output */ let hold; /* bit buffer */ let bits; /* bits in bit buffer */ let _in, _out; /* save starting available input and output */ let copy; /* number of stored or match bytes to copy */ let from; /* where to copy match bytes from */ let from_source; let here = 0; /* current decoding table entry */ let here_bits, here_op, here_val; // paked "here" denormalized (JS specific) //let last; /* parent table entry */ let last_bits, last_op, last_val; // paked "last" denormalized (JS specific) let len; /* length to copy for repeats, bits to drop */ let ret; /* return code */ const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */ let opts; let n; // temporary variable for NEED_BITS const order = /* permutation of code lengths */ new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); if (!strm || !strm.state || !strm.output || (!strm.input && strm.avail_in !== 0)) { return Z_STREAM_ERROR$1; } state = strm.state; if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- _in = have; _out = left; ret = Z_OK$1; inf_leave: // goto emulation for (;;) { switch (state.mode) { case HEAD: if (state.wrap === 0) { state.mode = TYPEDO; break; } //=== NEEDBITS(16); while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ state.check = 0/*crc32(0L, Z_NULL, 0)*/; //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = FLAGS; break; } state.flags = 0; /* expect zlib header */ if (state.head) { state.head.done = false; } if (!(state.wrap & 1) || /* check if zlib header allowed */ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { strm.msg = "incorrect header check"; state.mode = BAD; break; } if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { strm.msg = "unknown compression method"; state.mode = BAD; break; } //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// len = (hold & 0x0f)/*BITS(4)*/ + 8; if (state.wbits === 0) { state.wbits = len; } else if (len > state.wbits) { strm.msg = "invalid window size"; state.mode = BAD; break; } // !!! pako patch. Force use `options.windowBits` if passed. // Required to always use max window size by default. state.dmax = 1 << state.wbits; //state.dmax = 1 << len; //Tracev((stderr, "inflate: zlib header ok ")); strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS(); hold = 0; bits = 0; //===// break; case FLAGS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.flags = hold; if ((state.flags & 0xff) !== Z_DEFLATED) { strm.msg = "unknown compression method"; state.mode = BAD; break; } if (state.flags & 0xe000) { strm.msg = "unknown header flags set"; state.mode = BAD; break; } if (state.head) { state.head.text = ((hold >> 8) & 1); } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = TIME; /* falls through */ case TIME: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.time = hold; } if (state.flags & 0x0200) { //=== CRC4(state.check, hold) hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; hbuf[2] = (hold >>> 16) & 0xff; hbuf[3] = (hold >>> 24) & 0xff; state.check = crc32_1(state.check, hbuf, 4, 0); //=== } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = OS; /* falls through */ case OS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.xflags = (hold & 0xff); state.head.os = (hold >> 8); } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = EXLEN; /* falls through */ case EXLEN: if (state.flags & 0x0400) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length = hold; if (state.head) { state.head.extra_len = hold; } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32_1(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// } else if (state.head) { state.head.extra = null/*Z_NULL*/; } state.mode = EXTRA; /* falls through */ case EXTRA: if (state.flags & 0x0400) { copy = state.length; if (copy > have) { copy = have; } if (copy) { if (state.head) { len = state.head.extra_len - state.length; if (!state.head.extra) { // Use untyped array for more convenient processing later state.head.extra = new Uint8Array(state.head.extra_len); } state.head.extra.set( input.subarray( next, // extra field is limited to 65536 bytes // - no need for additional size check next + copy ), /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ len ); //zmemcpy(state.head.extra + len, next, // len + copy > state.head.extra_max ? // state.head.extra_max - len : copy); } if (state.flags & 0x0200) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; state.length -= copy; } if (state.length) { break inf_leave; } } state.length = 0; state.mode = NAME; /* falls through */ case NAME: if (state.flags & 0x0800) { if (have === 0) { break inf_leave; } copy = 0; do { // TODO: 2 or 1 bytes? len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.name_max*/)) { state.head.name += String.fromCharCode(len); } } while (len && copy < have); if (state.flags & 0x0200) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.name = null; } state.length = 0; state.mode = COMMENT; /* falls through */ case COMMENT: if (state.flags & 0x1000) { if (have === 0) { break inf_leave; } copy = 0; do { len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.comm_max*/)) { state.head.comment += String.fromCharCode(len); } } while (len && copy < have); if (state.flags & 0x0200) { state.check = crc32_1(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.comment = null; } state.mode = HCRC; /* falls through */ case HCRC: if (state.flags & 0x0200) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (hold !== (state.check & 0xffff)) { strm.msg = "header crc mismatch"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// } if (state.head) { state.head.hcrc = ((state.flags >> 9) & 1); state.head.done = true; } strm.adler = state.check = 0; state.mode = TYPE; break; case DICTID: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// strm.adler = state.check = zswap32(hold); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = DICT; /* falls through */ case DICT: if (state.havedict === 0) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- return Z_NEED_DICT$1; } strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = TYPE; /* falls through */ case TYPE: if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } /* falls through */ case TYPEDO: if (state.last) { //--- BYTEBITS() ---// hold >>>= bits & 7; bits -= bits & 7; //---// state.mode = CHECK; break; } //=== NEEDBITS(3); */ while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.last = (hold & 0x01)/*BITS(1)*/; //--- DROPBITS(1) ---// hold >>>= 1; bits -= 1; //---// switch ((hold & 0x03)/*BITS(2)*/) { case 0: /* stored block */ //Tracev((stderr, "inflate: stored block%s ", // state.last ? " (last)" : "")); state.mode = STORED; break; case 1: /* fixed block */ fixedtables(state); //Tracev((stderr, "inflate: fixed codes block%s ", // state.last ? " (last)" : "")); state.mode = LEN_; /* decode codes */ if (flush === Z_TREES) { //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break inf_leave; } break; case 2: /* dynamic block */ //Tracev((stderr, "inflate: dynamic codes block%s ", // state.last ? " (last)" : "")); state.mode = TABLE; break; case 3: strm.msg = "invalid block type"; state.mode = BAD; } //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break; case STORED: //--- BYTEBITS() ---// /* go to byte boundary */ hold >>>= bits & 7; bits -= bits & 7; //---// //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { strm.msg = "invalid stored block lengths"; state.mode = BAD; break; } state.length = hold & 0xffff; //Tracev((stderr, "inflate: stored length %u ", // state.length)); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = COPY_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case COPY_: state.mode = COPY; /* falls through */ case COPY: copy = state.length; if (copy) { if (copy > have) { copy = have; } if (copy > left) { copy = left; } if (copy === 0) { break inf_leave; } //--- zmemcpy(put, next, copy); --- output.set(input.subarray(next, next + copy), put); //---// have -= copy; next += copy; left -= copy; put += copy; state.length -= copy; break; } //Tracev((stderr, "inflate: stored end ")); state.mode = TYPE; break; case TABLE: //=== NEEDBITS(14); */ while (bits < 14) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// //#ifndef PKZIP_BUG_WORKAROUND if (state.nlen > 286 || state.ndist > 30) { strm.msg = "too many length or distance symbols"; state.mode = BAD; break; } //#endif //Tracev((stderr, "inflate: table sizes ok ")); state.have = 0; state.mode = LENLENS; /* falls through */ case LENLENS: while (state.have < state.ncode) { //=== NEEDBITS(3); while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } while (state.have < 19) { state.lens[order[state.have++]] = 0; } // We have separate tables & no pointers. 2 commented lines below not needed. //state.next = state.codes; //state.lencode = state.next; // Switch to use dynamic table state.lencode = state.lendyn; state.lenbits = 7; opts = { bits: state.lenbits }; ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); state.lenbits = opts.bits; if (ret) { strm.msg = "invalid code lengths set"; state.mode = BAD; break; } //Tracev((stderr, "inflate: code lengths ok ")); state.have = 0; state.mode = CODELENS; /* falls through */ case CODELENS: while (state.have < state.nlen + state.ndist) { for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_val < 16) { //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.lens[state.have++] = here_val; } else { if (here_val === 16) { //=== NEEDBITS(here.bits + 2); n = here_bits + 2; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// if (state.have === 0) { strm.msg = "invalid bit length repeat"; state.mode = BAD; break; } len = state.lens[state.have - 1]; copy = 3 + (hold & 0x03);//BITS(2); //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// } else if (here_val === 17) { //=== NEEDBITS(here.bits + 3); n = here_bits + 3; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 3 + (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } else { //=== NEEDBITS(here.bits + 7); n = here_bits + 7; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 11 + (hold & 0x7f);//BITS(7); //--- DROPBITS(7) ---// hold >>>= 7; bits -= 7; //---// } if (state.have + copy > state.nlen + state.ndist) { strm.msg = "invalid bit length repeat"; state.mode = BAD; break; } while (copy--) { state.lens[state.have++] = len; } } } /* handle error breaks in while */ if (state.mode === BAD) { break; } /* check for end-of-block code (better have one) */ if (state.lens[256] === 0) { strm.msg = "invalid code -- missing end-of-block"; state.mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state.lenbits = 9; opts = { bits: state.lenbits }; ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.lenbits = opts.bits; // state.lencode = state.next; if (ret) { strm.msg = "invalid literal/lengths set"; state.mode = BAD; break; } state.distbits = 6; //state.distcode.copy(state.codes); // Switch to use dynamic table state.distcode = state.distdyn; opts = { bits: state.distbits }; ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.distbits = opts.bits; // state.distcode = state.next; if (ret) { strm.msg = "invalid distances set"; state.mode = BAD; break; } //Tracev((stderr, "inflate: codes ok ")); state.mode = LEN_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case LEN_: state.mode = LEN; /* falls through */ case LEN: if (have >= 6 && left >= 258) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- inffast(strm, _out); //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- if (state.mode === TYPE) { state.back = -1; } break; } state.back = 0; for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if (here_bits <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_op && (here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.lencode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; state.length = here_val; if (here_op === 0) { //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal "%c" " : // "inflate: literal 0x%02x ", here.val)); state.mode = LIT; break; } if (here_op & 32) { //Tracevv((stderr, "inflate: end of block ")); state.back = -1; state.mode = TYPE; break; } if (here_op & 64) { strm.msg = "invalid literal/length code"; state.mode = BAD; break; } state.extra = here_op & 15; state.mode = LENEXT; /* falls through */ case LENEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //Tracevv((stderr, "inflate: length %u ", state.length)); state.was = state.length; state.mode = DIST; /* falls through */ case DIST: for (;;) { here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if ((here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.distcode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; if (here_op & 64) { strm.msg = "invalid distance code"; state.mode = BAD; break; } state.offset = here_val; state.extra = (here_op) & 15; state.mode = DISTEXT; /* falls through */ case DISTEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //#ifdef INFLATE_STRICT if (state.offset > state.dmax) { strm.msg = "invalid distance too far back"; state.mode = BAD; break; } //#endif //Tracevv((stderr, "inflate: distance %u ", state.offset)); state.mode = MATCH; /* falls through */ case MATCH: if (left === 0) { break inf_leave; } copy = _out - left; if (state.offset > copy) { /* copy from window */ copy = state.offset - copy; if (copy > state.whave) { if (state.sane) { strm.msg = "invalid distance too far back"; state.mode = BAD; break; } // (!) This block is disabled in zlib defaults, // don"t enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // Trace((stderr, "inflate.c too far ")); // copy -= state.whave; // if (copy > state.length) { copy = state.length; } // if (copy > left) { copy = left; } // left -= copy; // state.length -= copy; // do { // output[put++] = 0; // } while (--copy); // if (state.length === 0) { state.mode = LEN; } // break; //#endif } if (copy > state.wnext) { copy -= state.wnext; from = state.wsize - copy; } else { from = state.wnext - copy; } if (copy > state.length) { copy = state.length; } from_source = state.window; } else { /* copy from output */ from_source = output; from = put - state.offset; copy = state.length; } if (copy > left) { copy = left; } left -= copy; state.length -= copy; do { output[put++] = from_source[from++]; } while (--copy); if (state.length === 0) { state.mode = LEN; } break; case LIT: if (left === 0) { break inf_leave; } output[put++] = state.length; left--; state.mode = LEN; break; case CHECK: if (state.wrap) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; // Use "|" instead of "+" to make sure that result is signed hold |= input[next++] << bits; bits += 8; } //===// _out -= left; strm.total_out += _out; state.total += _out; if (_out) { strm.adler = state.check = /*UPDATE(state.check, put - _out, _out);*/ (state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out)); } _out = left; // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too if ((state.flags ? hold : zswap32(hold)) !== state.check) { strm.msg = "incorrect data check"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: check matches trailer ")); } state.mode = LENGTH; /* falls through */ case LENGTH: if (state.wrap && state.flags) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (hold !== (state.total & 0xffffffff)) { strm.msg = "incorrect length check"; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: length matches trailer ")); } state.mode = DONE; /* falls through */ case DONE: ret = Z_STREAM_END$1; break inf_leave; case BAD: ret = Z_DATA_ERROR$1; break inf_leave; case MEM: return Z_MEM_ERROR$1; case SYNC: /* falls through */ default: return Z_STREAM_ERROR$1; } } // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1))) { if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ; } _in -= strm.avail_in; _out -= strm.avail_out; strm.total_in += _in; strm.total_out += _out; state.total += _out; if (state.wrap && _out) { strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ (state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out)); } strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); if (((_in === 0 && _out === 0) || flush === Z_FINISH$1) && ret === Z_OK$1) { ret = Z_BUF_ERROR; } return ret; }; const inflateEnd = (strm) => { if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { return Z_STREAM_ERROR$1; } let state = strm.state; if (state.window) { state.window = null; } strm.state = null; return Z_OK$1; }; const inflateGetHeader = (strm, head) => { /* check state */ if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } const state = strm.state; if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR$1; } /* save header structure */ state.head = head; head.done = false; return Z_OK$1; }; const inflateSetDictionary = (strm, dictionary) => { const dictLength = dictionary.length; let state; let dictid; let ret; /* check state */ if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR$1; } state = strm.state; if (state.wrap !== 0 && state.mode !== DICT) { return Z_STREAM_ERROR$1; } /* check for correct dictionary identifier */ if (state.mode === DICT) { dictid = 1; /* adler32(0, null, 0)*/ /* dictid = adler32(dictid, dictionary, dictLength); */ dictid = adler32_1(dictid, dictionary, dictLength, 0); if (dictid !== state.check) { return Z_DATA_ERROR$1; } } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary, dictLength, dictLength); if (ret) { state.mode = MEM; return Z_MEM_ERROR$1; } state.havedict = 1; // Tracev((stderr, "inflate: dictionary set ")); return Z_OK$1; }; var inflateReset_1 = inflateReset; var inflateReset2_1 = inflateReset2; var inflateResetKeep_1 = inflateResetKeep; var inflateInit_1 = inflateInit; var inflateInit2_1 = inflateInit2; var inflate_2$1 = inflate$2; var inflateEnd_1 = inflateEnd; var inflateGetHeader_1 = inflateGetHeader; var inflateSetDictionary_1 = inflateSetDictionary; var inflateInfo = "pako inflate (from Nodeca project)"; /* Not implemented module.exports.inflateCopy = inflateCopy; module.exports.inflateGetDictionary = inflateGetDictionary; module.exports.inflateMark = inflateMark; module.exports.inflatePrime = inflatePrime; module.exports.inflateSync = inflateSync; module.exports.inflateSyncPoint = inflateSyncPoint; module.exports.inflateUndermine = inflateUndermine; */ var inflate_1$2 = { inflateReset: inflateReset_1, inflateReset2: inflateReset2_1, inflateResetKeep: inflateResetKeep_1, inflateInit: inflateInit_1, inflateInit2: inflateInit2_1, inflate: inflate_2$1, inflateEnd: inflateEnd_1, inflateGetHeader: inflateGetHeader_1, inflateSetDictionary: inflateSetDictionary_1, inflateInfo: inflateInfo }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided "as-is", without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function GZheader() { /* true if compressed data believed to be text */ this.text = 0; /* modification time */ this.time = 0; /* extra flags (not used when writing a gzip file) */ this.xflags = 0; /* operating system */ this.os = 0; /* pointer to extra field or Z_NULL if none */ this.extra = null; /* extra field length (valid if extra != Z_NULL) */ this.extra_len = 0; // Actually, we don"t need it in JS, // but leave for few code modifications // // Setup limits is not necessary because in js we should not preallocate memory // for inflate use constant limit in 65536 bytes // /* space at extra (only when reading header) */ // this.extra_max = 0; /* pointer to zero-terminated file name or Z_NULL */ this.name = ""; /* space at name (only when reading header) */ // this.name_max = 0; /* pointer to zero-terminated comment or Z_NULL */ this.comment = ""; /* space at comment (only when reading header) */ // this.comm_max = 0; /* true if there was or will be a header crc */ this.hcrc = 0; /* true when done reading gzip header (not used when writing a gzip file) */ this.done = false; } var gzheader = GZheader; const toString = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { Z_NO_FLUSH, Z_FINISH, Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR } = constants$2; /* ===========================================================================*/ /** * class Inflate * * Generic JS-style wrapper for zlib calls. If you don"t need * streaming behaviour - use more simple functions: [[inflate]] * and [[inflateRaw]]. **/ /* internal * inflate.chunks -> Array * * Chunks of output data, if [[Inflate#onData]] not overridden. **/ /** * Inflate.result -> Uint8Array|String * * Uncompressed result, generated by default [[Inflate#onData]] * and [[Inflate#onEnd]] handlers. Filled after you push last chunk * (call [[Inflate#push]] with `Z_FINISH` / `true` param). **/ /** * Inflate.err -> Number * * Error code after inflate finished. 0 (Z_OK) on success. * Should be checked if broken data possible. **/ /** * Inflate.msg -> String * * Error message, if [[Inflate.err]] != 0 **/ /** * new Inflate(options) * - options (Object): zlib inflate options. * * Creates new inflator instance with specified params. Throws exception * on bad params. Supported options: * * - `windowBits` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw inflate * - `to` (String) - if equal to "string", then result will be converted * from utf8 to utf16 (javascript) string. When string output requested, * chunk length can differ from `chunkSize`, depending on content. * * By default, when no options set, autodetect deflate/gzip data format via * wrapper header. * * ##### Example: * * ```javascript * const pako = require("pako") * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * const inflate = new pako.Inflate({ level: 3}); * * inflate.push(chunk1, false); * inflate.push(chunk2, true); // true -> last chunk * * if (inflate.err) { throw new Error(inflate.err); } * * console.log(inflate.result); * ``` **/ function Inflate$1(options) { this.options = common.assign({ chunkSize: 1024 * 64, windowBits: 15, to: "" }, options || {}); const opt = this.options; // Force window size for `raw` data, if not set directly, // because we have no header for autodetect. if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { opt.windowBits = -opt.windowBits; if (opt.windowBits === 0) { opt.windowBits = -15; } } // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate if ((opt.windowBits >= 0) && (opt.windowBits < 16) && !(options && options.windowBits)) { opt.windowBits += 32; } // Gzip header has no info about windows size, we can do autodetect only // for deflate. So, if window size not set, force it to max when gzip possible if ((opt.windowBits > 15) && (opt.windowBits < 48)) { // bit 3 (16) -> gzipped data // bit 4 (32) -> autodetect gzip/deflate if ((opt.windowBits & 15) === 0) { opt.windowBits |= 15; } } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ""; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new zstream(); this.strm.avail_out = 0; let status = inflate_1$2.inflateInit2( this.strm, opt.windowBits ); if (status !== Z_OK) { throw new Error(messages[status]); } this.header = new gzheader(); inflate_1$2.inflateGetHeader(this.strm, this.header); // Setup dictionary if (opt.dictionary) { // Convert data if needed if (typeof opt.dictionary === "string") { opt.dictionary = strings.string2buf(opt.dictionary); } else if (toString.call(opt.dictionary) === "[object ArrayBuffer]") { opt.dictionary = new Uint8Array(opt.dictionary); } if (opt.raw) { //In raw mode we need to set the dictionary early status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary); if (status !== Z_OK) { throw new Error(messages[status]); } } } } /** * Inflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer): input data * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, * `true` means Z_FINISH. * * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with * new output chunks. Returns `true` on success. If end of stream detected, * [[Inflate#onEnd]] will be called. * * `flush_mode` is not needed for normal operation, because end of stream * detected automatically. You may try to use it for advanced things, but * this functionality was not tested. * * On fail call [[Inflate#onEnd]] with error code and return false. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Inflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; const dictionary = this.options.dictionary; let status, _flush_mode, last_avail_out; if (this.ended) return false; if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed if (toString.call(data) === "[object ArrayBuffer]") { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } status = inflate_1$2.inflate(strm, _flush_mode); if (status === Z_NEED_DICT && dictionary) { status = inflate_1$2.inflateSetDictionary(strm, dictionary); if (status === Z_OK) { status = inflate_1$2.inflate(strm, _flush_mode); } else if (status === Z_DATA_ERROR) { // Replace code with more verbose status = Z_NEED_DICT; } } // Skip snyc markers if more data follows and not raw mode while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) { inflate_1$2.inflateReset(strm); status = inflate_1$2.inflate(strm, _flush_mode); } switch (status) { case Z_STREAM_ERROR: case Z_DATA_ERROR: case Z_NEED_DICT: case Z_MEM_ERROR: this.onEnd(status); this.ended = true; return false; } // Remember real `avail_out` value, because we may patch out buffer content // to align utf8 strings boundaries. last_avail_out = strm.avail_out; if (strm.next_out) { if (strm.avail_out === 0 || status === Z_STREAM_END) { if (this.options.to === "string") { let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); let tail = strm.next_out - next_out_utf8; let utf8str = strings.buf2string(strm.output, next_out_utf8); // move tail & realign counters strm.next_out = tail; strm.avail_out = chunkSize - tail; if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); this.onData(utf8str); } else { this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out)); } } } // Must repeat iteration if out buffer is full if (status === Z_OK && last_avail_out === 0) continue; // Finalize if end of stream reached. if (status === Z_STREAM_END) { status = inflate_1$2.inflateEnd(this.strm); this.onEnd(status); this.ended = true; return true; } if (strm.avail_in === 0) break; } return true; }; /** * Inflate#onData(chunk) -> Void * - chunk (Uint8Array|String): output data. When string output requested, * each chunk will be string. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Inflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Inflate#onEnd(status) -> Void * - status (Number): inflate status. 0 (Z_OK) on success, * other if not. * * Called either after you tell inflate that the input stream is * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Inflate$1.prototype.onEnd = function (status) { // On success - join if (status === Z_OK) { if (this.options.to === "string") { this.result = this.chunks.join(""); } else { this.result = common.flattenChunks(this.chunks); } } this.chunks = []; this.err = status; this.msg = this.strm.msg; }; /** * inflate(data[, options]) -> Uint8Array|String * - data (Uint8Array): input data to decompress. * - options (Object): zlib inflate options. * * Decompress `data` with inflate/ungzip and `options`. Autodetect * format via wrapper header by default. That"s why we don"t provide * separate `ungzip` method. * * Supported options are: * * - windowBits * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information. * * Sugar (options): * * - `raw` (Boolean) - say that we work with raw stream, if you don"t wish to specify * negative windowBits implicitly. * - `to` (String) - if equal to "string", then result will be converted * from utf8 to utf16 (javascript) string. When string output requested, * chunk length can differ from `chunkSize`, depending on content. * * * ##### Example: * * ```javascript * const pako = require("pako"); * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9])); * let output; * * try { * output = pako.inflate(input); * } catch (err) { * console.log(err); * } * ``` **/ function inflate$1(input, options) { const inflator = new Inflate$1(options); inflator.push(input); // That will never happens, if you don"t cheat with options :) if (inflator.err) throw inflator.msg || messages[inflator.err]; return inflator.result; } /** * inflateRaw(data[, options]) -> Uint8Array|String * - data (Uint8Array): input data to decompress. * - options (Object): zlib inflate options. * * The same as [[inflate]], but creates raw data, without wrapper * (header and adler32 crc). **/ function inflateRaw$1(input, options) { options = options || {}; options.raw = true; return inflate$1(input, options); } /** * ungzip(data[, options]) -> Uint8Array|String * - data (Uint8Array): input data to decompress. * - options (Object): zlib inflate options. * * Just shortcut to [[inflate]], because it autodetects format * by header.content. Done for convenience. **/ var Inflate_1$1 = Inflate$1; var inflate_2 = inflate$1; var inflateRaw_1$1 = inflateRaw$1; var ungzip$1 = inflate$1; var constants = constants$2; var inflate_1$1 = { Inflate: Inflate_1$1, inflate: inflate_2, inflateRaw: inflateRaw_1$1, ungzip: ungzip$1, constants: constants }; const { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1; var inflate_1 = inflate; var ieee754$1 = {}; /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ ieee754$1.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; var eBias = eMax >> 1; var nBits = -7; var i = isLE ? (nBytes - 1) : 0; var d = isLE ? -1 : 1; var s = buffer[offset + i]; i += d; e = s & ((1 << (-nBits)) - 1); s >>= (-nBits); nBits += eLen; for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} m = e & ((1 << (-nBits)) - 1); e >>= (-nBits); nBits += mLen; for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} if (e === 0) { e = 1 - eBias; } else if (e === eMax) { return m ? NaN : ((s ? -1 : 1) * Infinity) } else { m = m + Math.pow(2, mLen); e = e - eBias; } return (s ? -1 : 1) * m * Math.pow(2, e - mLen) }; ieee754$1.write = function (buffer, value, offset, isLE, mLen, nBytes) { var e, m, c; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; var eBias = eMax >> 1; var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0); var i = isLE ? 0 : (nBytes - 1); var d = isLE ? 1 : -1; var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; value = Math.abs(value); if (isNaN(value) || value === Infinity) { m = isNaN(value) ? 1 : 0; e = eMax; } else { e = Math.floor(Math.log(value) / Math.LN2); if (value * (c = Math.pow(2, -e)) < 1) { e--; c *= 2; } if (e + eBias >= 1) { value += rt / c; } else { value += rt * Math.pow(2, 1 - eBias); } if (value * c >= 2) { e++; c /= 2; } if (e + eBias >= eMax) { m = 0; e = eMax; } else if (e + eBias >= 1) { m = ((value * c) - 1) * Math.pow(2, mLen); e = e + eBias; } else { m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); e = 0; } } for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} e = (e << mLen) | m; eLen += mLen; for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} buffer[offset + i - d] |= s * 128; }; var pbf = Pbf; var ieee754 = ieee754$1; function Pbf(buf) { this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); this.pos = 0; this.type = 0; this.length = this.buf.length; } Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string // data structures (which currently switch structure types at 12 bytes or more) var TEXT_DECODER_MIN_LENGTH = 12; var utf8TextDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf8"); Pbf.prototype = { destroy: function() { this.buf = null; }, // === READING ================================================================= readFields: function(readField, result, end) { end = end || this.length; while (this.pos < end) { var val = this.readVarint(), tag = val >> 3, startPos = this.pos; this.type = val & 0x7; readField(tag, result, this); if (this.pos === startPos) this.skip(val); } return result; }, readMessage: function(readField, result) { return this.readFields(readField, result, this.readVarint() + this.pos); }, readFixed32: function() { var val = readUInt32(this.buf, this.pos); this.pos += 4; return val; }, readSFixed32: function() { var val = readInt32(this.buf, this.pos); this.pos += 4; return val; }, // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed) readFixed64: function() { var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; this.pos += 8; return val; }, readSFixed64: function() { var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; this.pos += 8; return val; }, readFloat: function() { var val = ieee754.read(this.buf, this.pos, true, 23, 4); this.pos += 4; return val; }, readDouble: function() { var val = ieee754.read(this.buf, this.pos, true, 52, 8); this.pos += 8; return val; }, readVarint: function(isSigned) { var buf = this.buf, val, b; b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val; b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val; b = buf[this.pos]; val |= (b & 0x0f) << 28; return readVarintRemainder(val, isSigned, this); }, readVarint64: function() { // for compatibility with v2.0.1 return this.readVarint(true); }, readSVarint: function() { var num = this.readVarint(); return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding }, readBoolean: function() { return Boolean(this.readVarint()); }, readString: function() { var end = this.readVarint() + this.pos; var pos = this.pos; this.pos = end; if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) { // longer strings are fast with the built-in browser TextDecoder API return readUtf8TextDecoder(this.buf, pos, end); } // short strings are fast with our custom implementation return readUtf8(this.buf, pos, end); }, readBytes: function() { var end = this.readVarint() + this.pos, buffer = this.buf.subarray(this.pos, end); this.pos = end; return buffer; }, // verbose for performance reasons; doesn"t affect gzipped size readPackedVarint: function(arr, isSigned) { if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned)); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readVarint(isSigned)); return arr; }, readPackedSVarint: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSVarint()); return arr; }, readPackedBoolean: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readBoolean()); return arr; }, readPackedFloat: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFloat()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFloat()); return arr; }, readPackedDouble: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readDouble()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readDouble()); return arr; }, readPackedFixed32: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFixed32()); return arr; }, readPackedSFixed32: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSFixed32()); return arr; }, readPackedFixed64: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readFixed64()); return arr; }, readPackedSFixed64: function(arr) { if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64()); var end = readPackedEnd(this); arr = arr || []; while (this.pos < end) arr.push(this.readSFixed64()); return arr; }, skip: function(val) { var type = val & 0x7; if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; else if (type === Pbf.Fixed32) this.pos += 4; else if (type === Pbf.Fixed64) this.pos += 8; else throw new Error("Unimplemented type: " + type); }, // === WRITING ================================================================= writeTag: function(tag, type) { this.writeVarint((tag << 3) | type); }, realloc: function(min) { var length = this.length || 16; while (length < this.pos + min) length *= 2; if (length !== this.length) { var buf = new Uint8Array(length); buf.set(this.buf); this.buf = buf; this.length = length; } }, finish: function() { this.length = this.pos; this.pos = 0; return this.buf.subarray(0, this.length); }, writeFixed32: function(val) { this.realloc(4); writeInt32(this.buf, val, this.pos); this.pos += 4; }, writeSFixed32: function(val) { this.realloc(4); writeInt32(this.buf, val, this.pos); this.pos += 4; }, writeFixed64: function(val) { this.realloc(8); writeInt32(this.buf, val & -1, this.pos); writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); this.pos += 8; }, writeSFixed64: function(val) { this.realloc(8); writeInt32(this.buf, val & -1, this.pos); writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); this.pos += 8; }, writeVarint: function(val) { val = +val || 0; if (val > 0xfffffff || val < 0) { writeBigVarint(val, this); return; } this.realloc(4); this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; this.buf[this.pos++] = (val >>> 7) & 0x7f; }, writeSVarint: function(val) { this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); }, writeBoolean: function(val) { this.writeVarint(Boolean(val)); }, writeString: function(str) { str = String(str); this.realloc(str.length * 4); this.pos++; // reserve 1 byte for short string length var startPos = this.pos; // write the string directly to the buffer and see how much was written this.pos = writeUtf8(this.buf, str, this.pos); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position this.pos = startPos - 1; this.writeVarint(len); this.pos += len; }, writeFloat: function(val) { this.realloc(4); ieee754.write(this.buf, val, this.pos, true, 23, 4); this.pos += 4; }, writeDouble: function(val) { this.realloc(8); ieee754.write(this.buf, val, this.pos, true, 52, 8); this.pos += 8; }, writeBytes: function(buffer) { var len = buffer.length; this.writeVarint(len); this.realloc(len); for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i]; }, writeRawMessage: function(fn, obj) { this.pos++; // reserve 1 byte for short message length // write the message directly to the buffer and see how much was written var startPos = this.pos; fn(obj, this); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position this.pos = startPos - 1; this.writeVarint(len); this.pos += len; }, writeMessage: function(tag, fn, obj) { this.writeTag(tag, Pbf.Bytes); this.writeRawMessage(fn, obj); }, writePackedVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr); }, writePackedSVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr); }, writePackedBoolean: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr); }, writePackedFloat: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr); }, writePackedDouble: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr); }, writePackedFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr); }, writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); }, writePackedFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr); }, writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); }, writeBytesField: function(tag, buffer) { this.writeTag(tag, Pbf.Bytes); this.writeBytes(buffer); }, writeFixed32Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeFixed32(val); }, writeSFixed32Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeSFixed32(val); }, writeFixed64Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeFixed64(val); }, writeSFixed64Field: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeSFixed64(val); }, writeVarintField: function(tag, val) { this.writeTag(tag, Pbf.Varint); this.writeVarint(val); }, writeSVarintField: function(tag, val) { this.writeTag(tag, Pbf.Varint); this.writeSVarint(val); }, writeStringField: function(tag, str) { this.writeTag(tag, Pbf.Bytes); this.writeString(str); }, writeFloatField: function(tag, val) { this.writeTag(tag, Pbf.Fixed32); this.writeFloat(val); }, writeDoubleField: function(tag, val) { this.writeTag(tag, Pbf.Fixed64); this.writeDouble(val); }, writeBooleanField: function(tag, val) { this.writeVarintField(tag, Boolean(val)); } }; function readVarintRemainder(l, s, p) { var buf = p.buf, h, b; b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s); b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s); throw new Error("Expected varint not more than 10 bytes"); } function readPackedEnd(pbf) { return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; } function toNum(low, high, isSigned) { if (isSigned) { return high * 0x100000000 + (low >>> 0); } return ((high >>> 0) * 0x100000000) + (low >>> 0); } function writeBigVarint(val, pbf) { var low, high; if (val >= 0) { low = (val % 0x100000000) | 0; high = (val / 0x100000000) | 0; } else { low = ~(-val % 0x100000000); high = ~(-val / 0x100000000); if (low ^ 0xffffffff) { low = (low + 1) | 0; } else { low = 0; high = (high + 1) | 0; } } if (val >= 0x10000000000000000 || val < -0x10000000000000000) { throw new Error("Given varint doesn"t fit into 10 bytes"); } pbf.realloc(10); writeBigVarintLow(low, high, pbf); writeBigVarintHigh(high, pbf); } function writeBigVarintLow(low, high, pbf) { pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; pbf.buf[pbf.pos] = low & 0x7f; } function writeBigVarintHigh(high, pbf) { var lsb = (high & 0x07) << 4; pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; pbf.buf[pbf.pos++] = high & 0x7f; } function makeRoomForExtraLength(startPos, len, pbf) { var extraLen = len <= 0x3fff ? 1 : len <= 0x1fffff ? 2 : len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7)); // if 1 byte isn"t enough for encoding message length, shift the data to the right pbf.realloc(extraLen); for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i]; } function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); } function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); } function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); } function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); } function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); } function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); } function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); } function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); } function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); } // Buffer code below from https://github.com/feross/buffer, MIT-licensed function readUInt32(buf, pos) { return ((buf[pos]) | (buf[pos + 1] << 8) | (buf[pos + 2] << 16)) + (buf[pos + 3] * 0x1000000); } function writeInt32(buf, val, pos) { buf[pos] = val; buf[pos + 1] = (val >>> 8); buf[pos + 2] = (val >>> 16); buf[pos + 3] = (val >>> 24); } function readInt32(buf, pos) { return ((buf[pos]) | (buf[pos + 1] << 8) | (buf[pos + 2] << 16)) + (buf[pos + 3] << 24); } function readUtf8(buf, pos, end) { var str = ""; var i = pos; while (i < end) { var b0 = buf[i]; var c = null; // codepoint var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1; if (i + bytesPerSequence > end) break; var b1, b2, b3; if (bytesPerSequence === 1) { if (b0 < 0x80) { c = b0; } } else if (bytesPerSequence === 2) { b1 = buf[i + 1]; if ((b1 & 0xC0) === 0x80) { c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F); if (c <= 0x7F) { c = null; } } } else if (bytesPerSequence === 3) { b1 = buf[i + 1]; b2 = buf[i + 2]; if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) { c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F); if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) { c = null; } } } else if (bytesPerSequence === 4) { b1 = buf[i + 1]; b2 = buf[i + 2]; b3 = buf[i + 3]; if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F); if (c <= 0xFFFF || c >= 0x110000) { c = null; } } } if (c === null) { c = 0xFFFD; bytesPerSequence = 1; } else if (c > 0xFFFF) { c -= 0x10000; str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800); c = 0xDC00 | c & 0x3FF; } str += String.fromCharCode(c); i += bytesPerSequence; } return str; } function readUtf8TextDecoder(buf, pos, end) { return utf8TextDecoder.decode(buf.subarray(pos, end)); } function writeUtf8(buf, str, pos) { for (var i = 0, c, lead; i < str.length; i++) { c = str.charCodeAt(i); // code point if (c > 0xD7FF && c < 0xE000) { if (lead) { if (c < 0xDC00) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; lead = c; continue; } else { c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; lead = null; } } else { if (c > 0xDBFF || (i + 1 === str.length)) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; } else { lead = c; } continue; } } else if (lead) { buf[pos++] = 0xEF; buf[pos++] = 0xBF; buf[pos++] = 0xBD; lead = null; } if (c < 0x80) { buf[pos++] = c; } else { if (c < 0x800) { buf[pos++] = c >> 0x6 | 0xC0; } else { if (c < 0x10000) { buf[pos++] = c >> 0xC | 0xE0; } else { buf[pos++] = c >> 0x12 | 0xF0; buf[pos++] = c >> 0xC & 0x3F | 0x80; } buf[pos++] = c >> 0x6 & 0x3F | 0x80; } buf[pos++] = c & 0x3F | 0x80; } } return pos; } /** * Decompress and parse an array buffer containing zipped * json data and return as a json object. * * @description Handles array buffers continaing zipped json * data. * * @param {ArrayBuffer} buffer - Array buffer to decompress. * @returns {Object} Parsed object. */ function decompress(buffer) { const inflated = inflate_1(buffer, { to: "string" }); return JSON.parse(inflated); } /** * Retrieves a resource as an array buffer and returns a promise * to the buffer. * * @description Rejects the promise on request failure. * * @param {string} url - URL for resource to retrieve. * @param {Promise} [abort] - Optional promise for aborting * the request through rejection. * @returns {Promise} Promise to the array buffer * resource. */ function fetchArrayBuffer(url, abort) { const method = "GET"; const responseType = "arraybuffer"; return xhrFetch(url, method, responseType, [], null, abort); } function xhrFetch(url, method, responseType, headers, body, abort) { const xhr = new XMLHttpRequest(); const promise = new Promise((resolve, reject) => { xhr.open(method, url, true); for (const header of headers) { xhr.setRequestHeader(header.name, header.value); } xhr.responseType = responseType; xhr.timeout = 15000; xhr.onload = () => { var _a; if (xhr.status !== 200) { const error = (_a = xhr.response) !== null && _a !== void 0 ? _a : new MapillaryError(`Response status error: ${url}`); reject(error); } if (!xhr.response) { reject(new MapillaryError(`Response empty: ${url}`)); } resolve(xhr.response); }; xhr.onerror = () => { reject(new MapillaryError(`Request error: ${url}`)); }; xhr.ontimeout = () => { reject(new MapillaryError(`Request timeout: ${url}`)); }; xhr.onabort = () => { reject(new MapillaryError(`Request aborted: ${url}`)); }; xhr.send(method === "POST" ? body : null); }); if (!!abort) { abort.catch(() => { xhr.abort(); }); } return promise; } /** * Read the fields of a protobuf array buffer into a mesh * object. * * @param {ArrayBuffer} buffer - Protobuf array buffer * to read from. * @returns {MeshContract} Mesh object. */ function readMeshPbf(buffer) { const pbf$1 = new pbf(buffer); const mesh = { faces: [], vertices: [] }; return pbf$1.readFields(readMeshPbfField, mesh); } function readMeshPbfField(tag, mesh, pbf) { if (tag === 1) { mesh.vertices.push(pbf.readFloat()); } else if (tag === 2) { mesh.faces.push(pbf.readVarint()); } else { console.warn(`Unsupported pbf tag (${tag})`); } } const UNDISTORTION_MARGIN_FACTOR = 3; const MIN_DEPTH = 5; const MAX_DEPTH = 200; function convertCameraType(graphCameraType) { switch (graphCameraType) { case "equirectangular": case "spherical": return "spherical"; case "fisheye": return "fisheye"; default: return "perspective"; } } class GraphConverter { clusterReconstruction(source) { const id = null; const colors = []; const coordinates = []; const pointIds = []; const points = source.points; const normalizer = 1 / 255; for (const pointId in points) { if (!points.hasOwnProperty(pointId)) { continue; } pointIds.push(pointId); const point = points[pointId]; const color = point.color; colors.push(color[0] * normalizer); colors.push(color[1] * normalizer); colors.push(color[2] * normalizer); coordinates.push(...point.coordinates); } const lla = source.reference_lla; const reference = { alt: lla.altitude, lat: lla.latitude, lng: lla.longitude, }; return { colors, coordinates, id, pointIds, reference, rotation: [0, 0, 0], }; } coreImage(source) { const geometry = this._geometry(source.geometry); const computedGeometry = this._geometry(source.computed_geometry); const sequence = { id: source.sequence }; const id = source.id; return { computed_geometry: computedGeometry, geometry, id, sequence, }; } /** * Clamps the depth of the points to the [5, 200] meters interval to avoid * strange appearance. * * @param source Source mesh. * @param params Parameters. * @returns Converted mesh. */ mesh(source, params) { const { vertices } = source; const scale = params && params.scale != null ? params.scale : 1; const scaleInv = 1 / scale; const perspective = params ? params.perspective : true; const zMin = scale * MIN_DEPTH; const zMax = scale * MAX_DEPTH; const numVertices = vertices.length / 3; for (let i = 0; i < numVertices; ++i) { const index = 3 * i; let x = vertices[index + 0]; let y = vertices[index + 1]; let z = vertices[index + 2]; if (perspective) { // Workaround for corner points not being undistorted // during processing for perspective cameras. if (i < 4) { x *= UNDISTORTION_MARGIN_FACTOR; y *= UNDISTORTION_MARGIN_FACTOR; } const zBounded = Math.max(zMin, Math.min(z, zMax)); const factor = zBounded / z; vertices[index + 0] = factor * x * scaleInv; vertices[index + 1] = factor * y * scaleInv; vertices[index + 2] = zBounded * scaleInv; } else { const l = Math.sqrt(x * x + y * y + z * z); const lBounded = Math.max(zMin, Math.min(l, zMax)); const factor = lBounded / l; vertices[index + 0] = factor * x * scaleInv; vertices[index + 1] = factor * y * scaleInv; vertices[index + 2] = factor * z * scaleInv; } } return source; } spatialImage(source) { var _a, _b, _c, _d; source.camera_type = convertCameraType(source.camera_type); source.merge_id = source.merge_cc ? source.merge_cc.toString() : null; source.private = null; const thumbUrl = source.camera_type === "spherical" ? source.thumb_2048_url : source.thumb_1024_url; source.thumb = (_a = source.thumb) !== null && _a !== void 0 ? _a : { id: null, url: thumbUrl }; source.cluster = (_b = source.sfm_cluster) !== null && _b !== void 0 ? _b : { id: null, url: null }; source.creator = { id: null, username: null }; source.owner = (_c = source.organization) !== null && _c !== void 0 ? _c : { id: null }; source.mesh = (_d = source.mesh) !== null && _d !== void 0 ? _d : { id: null, url: null }; return source; } _geometry(geometry) { const coords = geometry === null || geometry === void 0 ? void 0 : geometry.coordinates; const lngLat = coords ? { lat: coords[1], lng: coords[0], } : null; return lngLat; } } class GraphQueryCreator { constructor() { this.imagesPath = "images"; this.sequencePath = "image_ids"; this._imageTilesPath = "tiles"; this.coreFields = ["computed_geometry", "geometry", "sequence"]; this.idFields = ["id"]; this.spatialFields = [ "altitude", "atomic_scale", "camera_parameters", "camera_type", "captured_at", "compass_angle", "computed_altitude", "computed_compass_angle", "computed_rotation", "exif_orientation", "height", "merge_cc", "mesh", "organization", "quality_score", "sfm_cluster", "thumb_1024_url", "thumb_2048_url", "width", ]; this.imageTileFields = ["url", "z", "x", "y"]; } images(imageIds, fields) { return `image_ids=${imageIds.join(",")}&fields=${fields.join(",")}`; } imagesS2(cellId, fields) { return `s2=${cellId}&fields=${fields.join(",")}`; } imageTiles(z, fields) { return `z=${z}&fields=${fields.join(",")}`; } imageTilesPath(imageId) { return `${imageId}/${this._imageTilesPath}`; } sequence(sequenceId) { return `sequence_id=${sequenceId}`; } } class GraphDataProvider extends DataProviderBase { constructor(options, geometry, converter, queryCreator) { var _a; super(geometry !== null && geometry !== void 0 ? geometry : new S2GeometryProvider()); this._convert = converter !== null && converter !== void 0 ? converter : new GraphConverter(); this._query = queryCreator !== null && queryCreator !== void 0 ? queryCreator : new GraphQueryCreator(); this._meshParameters = new Map(); this._method = "GET"; const opts = options !== null && options !== void 0 ? options : {}; this._endpoint = (_a = opts.endpoint) !== null && _a !== void 0 ? _a : "https://graph.mapillary.com"; this._accessToken = opts.accessToken; } getCluster(url, abort) { return fetchArrayBuffer(url, abort) .then((buffer) => { const reconstructions = decompress(buffer); if (reconstructions.length < 1) { throw new Error("Cluster reconstruction empty"); } return this._convert .clusterReconstruction(reconstructions[0]); }); } getCoreImages(cellId) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ]; const query = this._query.imagesS2(cellId, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { cell_id: cellId, images: [], }; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); result.images.push(coreImage); } return result; }); } getImageBuffer(url, abort) { return fetchArrayBuffer(url, abort); } getImages(imageIds) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = []; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); const spatialImage = this._convert.spatialImage(item); this._setMeshParameters(spatialImage); const image = Object.assign({}, spatialImage, coreImage); const contract = { node: image, node_id: item.id, }; result.push(contract); } return result; }); } getImageTiles(request) { const fields = [ ...this._query.imageTileFields, ]; const query = this._query.imageTiles(request.z, fields); const url = new URL(this._query.imageTilesPath(request.imageId), this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { node: r.data, node_id: request.imageId, }; return result; }); } getMesh(url, abort) { return fetchArrayBuffer(url, abort) .then((buffer) => { const mesh = readMeshPbf(buffer); return this._convert.mesh(mesh, this._meshParameters.get(url)); }); } getSequence(sequenceId) { const query = this._query.sequence(sequenceId); const url = new URL(this._query.sequencePath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = { id: sequenceId, image_ids: r.data.map(item => item.id), }; return result; }); } getSpatialImages(imageIds) { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract(query, url) .then(r => { const result = []; const items = r.data; for (const item of items) { const spatialImage = this._convert.spatialImage(item); this._setMeshParameters(spatialImage); const contract = { node: spatialImage, node_id: item.id, }; result.push(contract); } return result; }); } setAccessToken(accessToken) { this._accessToken = accessToken; } _createHeaders() { const headers = [ { name: "Accept", value: "application/json" }, { name: "Content-Type", value: "application/x-www-form-urlencoded", }, ]; if (this._accessToken) { headers.push({ name: "Authorization", value: `OAuth ${this._accessToken}`, }); } return headers; } _fetchGraphContract(body, url) { const method = this._method; const headers = this._createHeaders(); const query = `${url}?${body}`; return xhrFetch(query, method, "json", headers, null, null) .catch((error) => { const message = this._makeErrorMessage(error); throw new MapillaryError(message); }); } _makeErrorMessage(graphError) { const error = graphError.error; const message = error ? `${error.code} (${error.type}, ${error.fbtrace_id}): ${error.message}` : "Failed to fetch data"; return message; } _setMeshParameters(spatialImage) { var _a; this._meshParameters.set(spatialImage.mesh.url, { perspective: spatialImage.camera_type === "perspective", scale: (_a = spatialImage.atomic_scale) !== null && _a !== void 0 ? _a : 1 }); } } /** * @class Marker * * @classdesc Represents an abstract marker class that should be extended * by marker implementations used in the marker component. */ class Marker { constructor(id, lngLat) { this._id = id; this._lngLat = lngLat; } /** * Get id. * @returns {string} The id of the marker. */ get id() { return this._id; } /** * Get geometry. * * @ignore */ get geometry() { return this._geometry; } /** * Get lngLat. * @returns {LngLat} The geographic coordinates of the marker. */ get lngLat() { return this._lngLat; } /** @ignore */ createGeometry(position) { if (!!this._geometry) { return; } this._createGeometry(position); // update matrix world if raycasting occurs before first render this._geometry.updateMatrixWorld(true); } /** @ignore */ disposeGeometry() { if (!this._geometry) { return; } this._disposeGeometry(); this._geometry = undefined; } /** @ignore */ getInteractiveObjects() { if (!this._geometry) { return []; } return this._getInteractiveObjects(); } /** @ignore */ lerpAltitude(alt, alpha) { if (!this._geometry) { return; } this._geometry.position.z = (1 - alpha) * this._geometry.position.z + alpha * alt; } /** @ignore */ updatePosition(position, lngLat) { if (!!lngLat) { this._lngLat.lat = lngLat.lat; this._lngLat.lng = lngLat.lng; } if (!this._geometry) { return; } this._geometry.position.fromArray(position); this._geometry.updateMatrixWorld(true); } } /** * @class CircleMarker * * @classdesc Non-interactive marker with a flat circle shape. The circle * marker can not be configured to be interactive. * * Circle marker properties can not be updated after creation. * * To create and add one `CircleMarker` with default configuration * and one with configuration use * * @example * ```js * var defaultMarker = new CircleMarker( * "id-1", * { lat: 0, lng: 0, }); * * var configuredMarker = new CircleMarker( * "id-2", * { lat: 0, lng: 0, }, * { * color: "#0ff", * opacity: 0.3, * radius: 0.7, * }); * * markerComponent.add([defaultMarker, configuredMarker]); * ``` */ class CircleMarker extends Marker { constructor(id, lngLat, options) { super(id, lngLat); options = !!options ? options : {}; this._color = options.color != null ? options.color : 0xffffff; this._opacity = options.opacity != null ? options.opacity : 0.4; this._radius = options.radius != null ? options.radius : 1; } _createGeometry(position) { const circle = new Mesh(new CircleGeometry(this._radius, 16), new MeshBasicMaterial({ color: this._color, opacity: this._opacity, transparent: true, })); circle.up.fromArray([0, 0, 1]); circle.renderOrder = -1; const group = new Object3D(); group.add(circle); group.position.fromArray(position); this._geometry = group; } _disposeGeometry() { for (let mesh of this._geometry.children) { mesh.geometry.dispose(); mesh.material.dispose(); } } _getInteractiveObjects() { return []; } } /** * @class SimpleMarker * * @classdesc Interactive marker with ice cream shape. The sphere * inside the ice cream can be configured to be interactive. * * Simple marker properties can not be updated after creation. * * To create and add one `SimpleMarker` with default configuration * (non-interactive) and one interactive with configuration use * * @example * ```js * var defaultMarker = new SimpleMarker( * "id-1", * { lat: 0, lng: 0, }); * * var interactiveMarker = new SimpleMarker( * "id-2", * { lat: 0, lng: 0, }, * { * ballColor: "#00f", * ballOpacity: 0.5, * color: "#00f", * interactive: true, * opacity: 0.3, * radius: 0.7, * }); * * markerComponent.add([defaultMarker, interactiveMarker]); * ``` */ class SimpleMarker extends Marker { constructor(id, lngLat, options) { super(id, lngLat); options = !!options ? options : {}; this._ballColor = options.ballColor != null ? options.ballColor : 0xff0000; this._ballOpacity = options.ballOpacity != null ? options.ballOpacity : 0.8; this._circleToRayAngle = 2; this._color = options.color != null ? options.color : 0xff0000; this._interactive = !!options.interactive; this._opacity = options.opacity != null ? options.opacity : 0.4; this._radius = options.radius != null ? options.radius : 1; } _createGeometry(position) { const radius = this._radius; const height = this._markerHeight(radius); const markerMaterial = new MeshBasicMaterial({ color: this._color, opacity: this._opacity, transparent: true, depthWrite: false, }); const marker = new Mesh(this._createMarkerGeometry(radius, 8, 8), markerMaterial); const interactive = new Mesh(new SphereGeometry(radius / 2, 8, 8), new MeshBasicMaterial({ color: this._ballColor, opacity: this._ballOpacity, transparent: true, })); interactive.position.z = height; interactive.renderOrder = 1; const group = new Object3D(); group.add(interactive); group.add(marker); group.position.fromArray(position); this._geometry = group; } _disposeGeometry() { for (const mesh of this._geometry.children) { mesh.geometry.dispose(); mesh.material.dispose(); } } _getInteractiveObjects() { return this._interactive ? [this._geometry.children[0]] : []; } _markerHeight(radius) { const t = Math.tan(Math.PI - this._circleToRayAngle); return radius * Math.sqrt(1 + t * t); } _createMarkerGeometry(radius, widthSegments, heightSegments) { const height = this._markerHeight(radius); const circleToRayAngle = this._circleToRayAngle; const indexRows = []; const positions = new Float32Array(3 * (widthSegments + 1) * (heightSegments + 1)); let positionIndex = 0; for (let y = 0; y <= heightSegments; ++y) { const indexRow = []; for (let x = 0; x <= widthSegments; ++x) { const u = x / widthSegments * Math.PI * 2; const v = y / heightSegments * Math.PI; let r = radius; if (v > circleToRayAngle) { const t = Math.tan(v - circleToRayAngle); r = radius * Math.sqrt(1 + t * t); } const arrayIndex = 3 * positionIndex; const sinv = Math.sin(v); positions[arrayIndex + 0] = r * Math.cos(u) * sinv; positions[arrayIndex + 1] = r * Math.sin(u) * sinv; positions[arrayIndex + 2] = r * Math.cos(v) + height; indexRow.push(positionIndex++); } indexRows.push(indexRow); } const indices = new Uint16Array(6 * widthSegments * heightSegments); let index = 0; for (let y = 0; y < heightSegments; ++y) { for (let x = 0; x < widthSegments; ++x) { const pi1 = indexRows[y][x + 1]; const pi2 = indexRows[y][x]; const pi3 = indexRows[y + 1][x]; const pi4 = indexRows[y + 1][x + 1]; indices[index++] = pi1; indices[index++] = pi2; indices[index++] = pi4; indices[index++] = pi2; indices[index++] = pi3; indices[index++] = pi4; } } const geometry = new BufferGeometry(); const positionAttribute = new BufferAttribute(positions, 3); geometry.setAttribute("position", positionAttribute); geometry.setIndex(new BufferAttribute(indices, 1)); return geometry; } } /** * @class Popup * * @classdesc Popup instance for rendering custom HTML content * on top of images. Popups are based on 2D basic image coordinates * (see the {@link Viewer} class documentation for more information about coordinate * systems) and a certain popup is therefore only relevant to a single image. * Popups related to a certain image should be removed when moving * to another image. * * A popup must have both its content and its point or rect set to be * rendered. Popup options can not be updated after creation but the * basic point or rect as well as its content can be changed by calling * the appropriate methods. * * To create and add one `Popup` with default configuration * (tooltip visuals and automatic float) and one with specific options * use * * @example * ```js * var defaultSpan = document.createElement("span"); * defaultSpan.innerHTML = "hello default"; * * var defaultPopup = new Popup(); * defaultPopup.setDOMContent(defaultSpan); * defaultPopup.setBasicPoint([0.3, 0.3]); * * var cleanSpan = document.createElement("span"); * cleanSpan.innerHTML = "hello clean"; * * var cleanPopup = new Popup({ * clean: true, * float: Alignment.Top, * offset: 10, * opacity: 0.7, * }); * * cleanPopup.setDOMContent(cleanSpan); * cleanPopup.setBasicPoint([0.6, 0.6]); * * popupComponent.add([defaultPopup, cleanPopup]); * ``` * * @description Implementation of API methods and API documentation inspired * by/used from https://github.com/mapbox/mapbox-gl-js/blob/v0.38.0/src/ui/popup.js */ class Popup { constructor(options, viewportCoords, dom) { this._options = {}; options = !!options ? options : {}; this._options.capturePointer = options.capturePointer === false ? options.capturePointer : true; this._options.clean = options.clean; this._options.float = options.float; this._options.offset = options.offset; this._options.opacity = options.opacity; this._options.position = options.position; this._dom = !!dom ? dom : new DOM(); this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); this._notifyChanged$ = new Subject(); } /** * @description Internal observable used by the component to * render the popup when its position or content has changed. * @ignore */ get changed$() { return this._notifyChanged$; } /** * @description Internal method used by the component to * remove all references to the popup. * @ignore */ remove() { if (this._content && this._content.parentNode) { this._content.parentNode.removeChild(this._content); } if (this._container) { this._container.parentNode.removeChild(this._container); delete this._container; } if (this._parentContainer) { delete this._parentContainer; } } /** * Sets a 2D basic image coordinates point to the popup"s anchor, and * moves the popup to it. * * @description Overwrites any previously set point or rect. * * @param {Array} basicPoint - Point in 2D basic image coordinates. * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setBasicPoint(basicPoint) { this._point = basicPoint.slice(); this._rect = null; this._notifyChanged$.next(this); } /** * Sets a 2D basic image coordinates rect to the popup"s anchor, and * moves the popup to it. * * @description Overwrites any previously set point or rect. * * @param {Array} basicRect - Rect in 2D basic image * coordinates ([topLeftX, topLeftY, bottomRightX, bottomRightY]) . * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicRect([0.3, 0.3, 0.5, 0.6]); * * popupComponent.add([popup]); * ``` */ setBasicRect(basicRect) { this._rect = basicRect.slice(); this._point = null; this._notifyChanged$.next(this); } /** * Sets the popup"s content to the element provided as a DOM node. * * @param {Node} htmlNode - A DOM node to be used as content for the popup. * * @example * ```js * var div = document.createElement("div"); * div.innerHTML = "hello image"; * * var popup = new Popup(); * popup.setDOMContent(div); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setDOMContent(htmlNode) { if (this._content && this._content.parentNode) { this._content.parentNode.removeChild(this._content); } const className = "mapillary-popup-content" + (this._options.clean === true ? "-clean" : "") + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); this._content = this._dom.createElement("div", className, this._container); this._content.appendChild(htmlNode); this._notifyChanged$.next(this); } /** * Sets the popup"s content to the HTML provided as a string. * * @description This method does not perform HTML filtering or sanitization, * and must be used only with trusted content. Consider * {@link Popup.setText} if the * content is an untrusted text string. * * @param {string} html - A string representing HTML content for the popup. * * @example * ```js * var popup = new Popup(); * popup.setHTML("
hello image
"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setHTML(html) { const frag = this._dom.document.createDocumentFragment(); const temp = this._dom.createElement("body"); let child; temp.innerHTML = html; while (true) { child = temp.firstChild; if (!child) { break; } frag.appendChild(child); } this.setDOMContent(frag); } /** * Sets the popup"s content to a string of text. * * @description This function creates a Text node in the DOM, so it cannot insert raw HTML. * Use this method for security against XSS if the popup content is user-provided. * * @param {string} text - Textual content for the popup. * * @example * ```js * var popup = new Popup(); * popup.setText("hello image"); * popup.setBasicPoint([0.3, 0.3]); * * popupComponent.add([popup]); * ``` */ setText(text) { this.setDOMContent(this._dom.document.createTextNode(text)); } /** * @description Internal method for attaching the popup to * its parent container so that it is rendered in the DOM tree. * @ignore */ setParentContainer(parentContainer) { this._parentContainer = parentContainer; } /** * @description Internal method for updating the rendered * position of the popup called by the popup component. * @ignore */ update(renderCamera, size, transform) { if (!this._parentContainer || !this._content) { return; } if (!this._point && !this._rect) { return; } if (!this._container) { this._container = this._dom.createElement("div", "mapillary-popup", this._parentContainer); const showTip = this._options.clean !== true && this._options.float !== exports.Alignment.Center; if (showTip) { const tipClassName = "mapillary-popup-tip" + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); this._tip = this._dom.createElement("div", tipClassName, this._container); this._dom.createElement("div", "mapillary-popup-tip-inner", this._tip); } this._container.appendChild(this._content); this._parentContainer.appendChild(this._container); if (this._options.opacity != null) { this._container.style.opacity = this._options.opacity.toString(); } } let pointPixel = null; let position = this._alignmentToPopupAligment(this._options.position); let float = this._alignmentToPopupAligment(this._options.float); const classList = this._container.classList; if (this._point != null) { pointPixel = this._viewportCoords.basicToCanvasSafe(this._point[0], this._point[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); } else { const alignments = ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"]; let appliedPosition = null; for (const alignment of alignments) { if (classList.contains(`mapillary-popup-float-${alignment}`)) { appliedPosition = alignment; break; } } [pointPixel, position] = this._rectToPixel(this._rect, position, appliedPosition, renderCamera, size, transform); if (!float) { float = position; } } if (pointPixel == null) { this._container.style.display = "none"; return; } this._container.style.display = ""; if (!float) { const width = this._container.offsetWidth; const height = this._container.offsetHeight; const floats = this._pixelToFloats(pointPixel, size, width, height); float = floats.length === 0 ? "top" : floats.join("-"); } const offset = this._normalizeOffset(this._options.offset); pointPixel = [pointPixel[0] + offset[float][0], pointPixel[1] + offset[float][1]]; pointPixel = [Math.round(pointPixel[0]), Math.round(pointPixel[1])]; const floatTranslate = { "bottom": "translate(-50%,0)", "bottom-left": "translate(-100%,0)", "bottom-right": "translate(0,0)", "center": "translate(-50%,-50%)", "left": "translate(-100%,-50%)", "right": "translate(0,-50%)", "top": "translate(-50%,-100%)", "top-left": "translate(-100%,-100%)", "top-right": "translate(0,-100%)", }; for (const key in floatTranslate) { if (!floatTranslate.hasOwnProperty(key)) { continue; } classList.remove(`mapillary-popup-float-${key}`); } classList.add(`mapillary-popup-float-${float}`); this._container.style.transform = `${floatTranslate[float]} translate(${pointPixel[0]}px,${pointPixel[1]}px)`; } _rectToPixel(rect, position, appliedPosition, renderCamera, size, transform) { if (!position) { const width = this._container.offsetWidth; const height = this._container.offsetHeight; const floatOffsets = { "bottom": [0, height / 2], "bottom-left": [-width / 2, height / 2], "bottom-right": [width / 2, height / 2], "left": [-width / 2, 0], "right": [width / 2, 0], "top": [0, -height / 2], "top-left": [-width / 2, -height / 2], "top-right": [width / 2, -height / 2], }; const automaticPositions = ["top", "bottom", "left", "right"]; let largestVisibleArea = [0, null, null]; for (const automaticPosition of automaticPositions) { const autoPointBasic = this._pointFromRectPosition(rect, automaticPosition); const autoPointPixel = this._viewportCoords.basicToCanvasSafe(autoPointBasic[0], autoPointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); if (autoPointPixel == null) { continue; } const floatOffset = floatOffsets[automaticPosition]; const offsetedPosition = [autoPointPixel[0] + floatOffset[0], autoPointPixel[1] + floatOffset[1]]; const staticCoeff = appliedPosition != null && appliedPosition === automaticPosition ? 1 : 0.7; const floats = this._pixelToFloats(offsetedPosition, size, width / staticCoeff, height / (2 * staticCoeff)); if (floats.length === 0 && autoPointPixel[0] > 0 && autoPointPixel[0] < size.width && autoPointPixel[1] > 0 && autoPointPixel[1] < size.height) { return [autoPointPixel, automaticPosition]; } const minX = Math.max(offsetedPosition[0] - width / 2, 0); const maxX = Math.min(offsetedPosition[0] + width / 2, size.width); const minY = Math.max(offsetedPosition[1] - height / 2, 0); const maxY = Math.min(offsetedPosition[1] + height / 2, size.height); const visibleX = Math.max(0, maxX - minX); const visibleY = Math.max(0, maxY - minY); const visibleArea = staticCoeff * visibleX * visibleY; if (visibleArea > largestVisibleArea[0]) { largestVisibleArea[0] = visibleArea; largestVisibleArea[1] = autoPointPixel; largestVisibleArea[2] = automaticPosition; } } if (largestVisibleArea[0] > 0) { return [largestVisibleArea[1], largestVisibleArea[2]]; } } const pointBasic = this._pointFromRectPosition(rect, position); const pointPixel = this._viewportCoords.basicToCanvasSafe(pointBasic[0], pointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); return [pointPixel, position != null ? position : "top"]; } _alignmentToPopupAligment(float) { switch (float) { case exports.Alignment.Bottom: return "bottom"; case exports.Alignment.BottomLeft: return "bottom-left"; case exports.Alignment.BottomRight: return "bottom-right"; case exports.Alignment.Center: return "center"; case exports.Alignment.Left: return "left"; case exports.Alignment.Right: return "right"; case exports.Alignment.Top: return "top"; case exports.Alignment.TopLeft: return "top-left"; case exports.Alignment.TopRight: return "top-right"; default: return null; } } _normalizeOffset(offset) { if (offset == null) { return this._normalizeOffset(0); } if (typeof offset === "number") { // input specifies a radius const sideOffset = offset; const sign = sideOffset >= 0 ? 1 : -1; const cornerOffset = sign * Math.round(Math.sqrt(0.5 * Math.pow(sideOffset, 2))); return { "bottom": [0, sideOffset], "bottom-left": [-cornerOffset, cornerOffset], "bottom-right": [cornerOffset, cornerOffset], "center": [0, 0], "left": [-sideOffset, 0], "right": [sideOffset, 0], "top": [0, -sideOffset], "top-left": [-cornerOffset, -cornerOffset], "top-right": [cornerOffset, -cornerOffset], }; } else { // input specifes a value for each position return { "bottom": offset.bottom || [0, 0], "bottom-left": offset.bottomLeft || [0, 0], "bottom-right": offset.bottomRight || [0, 0], "center": offset.center || [0, 0], "left": offset.left || [0, 0], "right": offset.right || [0, 0], "top": offset.top || [0, 0], "top-left": offset.topLeft || [0, 0], "top-right": offset.topRight || [0, 0], }; } } _pixelToFloats(pointPixel, size, width, height) { const floats = []; if (pointPixel[1] < height) { floats.push("bottom"); } else if (pointPixel[1] > size.height - height) { floats.push("top"); } if (pointPixel[0] < width / 2) { floats.push("right"); } else if (pointPixel[0] > size.width - width / 2) { floats.push("left"); } return floats; } _pointFromRectPosition(rect, position) { const x0 = rect[0]; const x1 = rect[0] < rect[2] ? rect[2] : rect[2] + 1; const y0 = rect[1]; const y1 = rect[3]; switch (position) { case "bottom": return [(x0 + x1) / 2, y1]; case "bottom-left": return [x0, y1]; case "bottom-right": return [x1, y1]; case "center": return [(x0 + x1) / 2, (y0 + y1) / 2]; case "left": return [x0, (y0 + y1) / 2]; case "right": return [x1, (y0 + y1) / 2]; case "top": return [(x0 + x1) / 2, y0]; case "top-left": return [x0, y0]; case "top-right": return [x1, y0]; default: return [(x0 + x1) / 2, y1]; } } } function isBrowser() { return (typeof window !== "undefined" && typeof document !== "undefined"); } function isArraySupported() { return !!(Array.prototype && Array.prototype.concat && Array.prototype.filter && Array.prototype.includes && Array.prototype.indexOf && Array.prototype.join && Array.prototype.map && Array.prototype.push && Array.prototype.pop && Array.prototype.reverse && Array.prototype.shift && Array.prototype.slice && Array.prototype.splice && Array.prototype.sort && Array.prototype.unshift); } function isBlobSupported() { return ("Blob" in window && "URL" in window); } function isFunctionSupported() { return !!(Function.prototype && Function.prototype.apply && Function.prototype.bind); } function isJSONSupported() { return ("JSON" in window && "parse" in JSON && "stringify" in JSON); } function isMapSupported() { return "Map" in window; } function isObjectSupported() { return !!(Object.assign && Object.keys && Object.values); } function isPromiseSupported() { return !!("Promise" in window && Promise.resolve && Promise.reject && Promise.prototype && Promise.prototype.catch && Promise.prototype.then); } function isSetSupported() { return "Set" in window; } let isWebGLSupportedCache = undefined; function isWebGLSupportedCached() { if (isWebGLSupportedCache === undefined) { isWebGLSupportedCache = isWebGLSupported(); } return isWebGLSupportedCache; } function isWebGLSupported() { const attributes = { alpha: false, antialias: false, depth: true, failIfMajorPerformanceCaveat: false, premultipliedAlpha: true, preserveDrawingBuffer: false, stencil: true, }; const canvas = document.createElement("canvas"); const webGL2Context = canvas.getContext("webgl2", attributes); if (!!webGL2Context) { return true; } const context = canvas.getContext("webgl", attributes) || canvas .getContext("experimental-webgl", attributes); if (!context) { return false; } const requiredExtensions = ["OES_standard_derivatives"]; const supportedExtensions = context.getSupportedExtensions(); for (const requiredExtension of requiredExtensions) { if (supportedExtensions.indexOf(requiredExtension) === -1) { return false; } } return true; } /** * Test whether the current browser supports the full * functionality of MapillaryJS. * * @description The full functionality includes WebGL rendering. * * @return {boolean} * * @example `var supported = isSupported();` */ function isSupported() { return isFallbackSupported() && isWebGLSupportedCached(); } /** * Test whether the current browser supports the fallback * functionality of MapillaryJS. * * @description The fallback functionality does not include WebGL * rendering, only 2D canvas rendering. * * @return {boolean} * * @example `var fallbackSupported = isFallbackSupported();` */ function isFallbackSupported() { return isBrowser() && isArraySupported() && isBlobSupported() && isFunctionSupported() && isJSONSupported() && isMapSupported() && isObjectSupported() && isPromiseSupported() && isSetSupported(); } /** * Enumeration for camera controls. * * @description Specifies different modes for how the * camera is controlled through pointer, keyboard or * other modes of input. * * @enum {number} * @readonly */ exports.CameraControls = void 0; (function (CameraControls) { /** * Control the camera with custom logic by * attaching a custom camera controls * instance to the {@link Viewer}. */ CameraControls[CameraControls["Custom"] = 0] = "Custom"; /** * Control the camera from a birds perspective * to get an overview. */ CameraControls[CameraControls["Earth"] = 1] = "Earth"; /** * Control the camera in a first person view * from the street level perspective. * * @description The virtual viewer camera will * be rotated according to the orientation of * the images. */ CameraControls[CameraControls["Street"] = 2] = "Street"; /** * Control the camera in a first person view * from the street level perspective. * * @description The virtual viewer camera will * maintain gravity alignment for its up vector * at all times. */ CameraControls[CameraControls["Gravity"] = 3] = "Gravity"; })(exports.CameraControls || (exports.CameraControls = {})); /** * Enumeration for render mode * @enum {number} * @readonly * @description Modes for specifying how rendering is done * in the viewer. All modes preserves the original aspect * ratio of the images. */ exports.RenderMode = void 0; (function (RenderMode) { /** * Displays all content within the viewer. * * @description Black bars shown on both * sides of the content. Bars are shown * either below and above or to the left * and right of the content depending on * the aspect ratio relation between the * image and the viewer. */ RenderMode[RenderMode["Letterbox"] = 0] = "Letterbox"; /** * Fills the viewer by cropping content. * * @description Cropping is done either * in horizontal or vertical direction * depending on the aspect ratio relation * between the image and the viewer. */ RenderMode[RenderMode["Fill"] = 1] = "Fill"; })(exports.RenderMode || (exports.RenderMode = {})); exports.RenderPass = void 0; (function (RenderPass) { /** * Occurs after the background render pass. */ RenderPass[RenderPass["Opaque"] = 0] = "Opaque"; })(exports.RenderPass || (exports.RenderPass = {})); /** * Enumeration for transition mode * @enum {number} * @readonly * @description Modes for specifying how transitions * between images are performed. */ exports.TransitionMode = void 0; (function (TransitionMode) { /** * Default transitions. * * @description The viewer dynamically determines * whether transitions should be performed with or * without motion and blending for each transition * based on the underlying data. */ TransitionMode[TransitionMode["Default"] = 0] = "Default"; /** * Instantaneous transitions. * * @description All transitions are performed * without motion or blending. */ TransitionMode[TransitionMode["Instantaneous"] = 1] = "Instantaneous"; })(exports.TransitionMode || (exports.TransitionMode = {})); class ComponentController { constructor(container, navigator, observer, key, options, componentService) { this._container = container; this._observer = observer; this._navigator = navigator; this._options = options != null ? options : {}; this._key = key; this._navigable = key == null; this._componentService = !!componentService ? componentService : new ComponentService(this._container, this._navigator); this._coverComponent = this._componentService.getCover(); this._initializeComponents(); if (key) { this._initilizeCoverComponent(); this._subscribeCoverComponent(); } else { this._navigator.movedToId$.pipe(first((k) => { return k != null; })) .subscribe((k) => { this._key = k; this._componentService.deactivateCover(); this._coverComponent.configure({ id: this._key, state: CoverState.Hidden, }); this._subscribeCoverComponent(); this._navigator.stateService.start(); this._navigator.cacheService.start(); this._navigator.panService.start(); this._observer.startEmit(); }); } } get navigable() { return this._navigable; } get(name) { return this._componentService.get(name); } activate(name) { this._componentService.activate(name); } activateCover() { this._coverComponent.configure({ state: CoverState.Visible }); } deactivate(name) { this._componentService.deactivate(name); } deactivateCover() { this._coverComponent.configure({ state: CoverState.Loading }); } remove() { this._componentService.remove(); if (this._configurationSubscription != null) { this._configurationSubscription.unsubscribe(); } } _initializeComponents() { var _a, _b; const options = this._options; this._uFalse((_a = options.fallback) === null || _a === void 0 ? void 0 : _a.image, "imagefallback"); this._uFalse((_b = options.fallback) === null || _b === void 0 ? void 0 : _b.navigation, "navigationfallback"); this._uFalse(options.marker, "marker"); this._uFalse(options.popup, "popup"); this._uFalse(options.spatial, "spatial"); this._uFalse(options.tag, "tag"); this._uTrue(options.attribution, "attribution"); this._uTrue(options.bearing, "bearing"); this._uTrue(options.cache, "cache"); this._uTrue(options.direction, "direction"); this._uTrue(options.image, "image"); this._uTrue(options.keyboard, "keyboard"); this._uTrue(options.pointer, "pointer"); this._uTrue(options.sequence, "sequence"); this._uTrue(options.zoom, "zoom"); } _initilizeCoverComponent() { let options = this._options; this._coverComponent.configure({ id: this._key }); if (options.cover === undefined || options.cover) { this.activateCover(); } else { this.deactivateCover(); } } _setNavigable(navigable) { if (this._navigable === navigable) { return; } this._navigable = navigable; this._observer.navigable$.next(navigable); } _subscribeCoverComponent() { this._configurationSubscription = this._coverComponent.configuration$.pipe(distinctUntilChanged(undefined, (c) => { return c.state; })) .subscribe((conf) => { if (conf.state === CoverState.Loading) { this._navigator.stateService.currentId$.pipe(first(), switchMap((key) => { const keyChanged = key == null || key !== conf.id; if (keyChanged) { this._setNavigable(false); } return keyChanged ? this._navigator.moveTo$(conf.id) : this._navigator.stateService.currentImage$.pipe(first()); })) .subscribe(() => { this._navigator.stateService.start(); this._navigator.cacheService.start(); this._navigator.panService.start(); this._observer.startEmit(); this._coverComponent.configure({ state: CoverState.Hidden }); this._componentService.deactivateCover(); this._setNavigable(true); }, (error) => { console.error("Failed to deactivate cover.", error); this._coverComponent.configure({ state: CoverState.Visible }); }); } else if (conf.state === CoverState.Visible) { this._observer.stopEmit(); this._navigator.stateService.stop(); this._navigator.cacheService.stop(); this._navigator.playService.stop(); this._navigator.panService.stop(); this._componentService.activateCover(); this._setNavigable(conf.id == null); } }); } _uFalse(option, name) { if (option === undefined) { this._componentService.deactivate(name); return; } if (typeof option === "boolean") { if (option) { this._componentService.activate(name); } else { this._componentService.deactivate(name); } return; } this._componentService.configure(name, option); this._componentService.activate(name); } _uTrue(option, name) { if (option === undefined) { this._componentService.activate(name); return; } if (typeof option === "boolean") { if (option) { this._componentService.activate(name); } else { this._componentService.deactivate(name); } return; } this._componentService.configure(name, option); this._componentService.activate(name); } } class DOMRenderer { constructor(element, renderService, currentFrame$) { this._adaptiveOperation$ = new Subject(); this._render$ = new Subject(); this._renderAdaptive$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._renderService = renderService; this._currentFrame$ = currentFrame$; const subs = this._subscriptions; const rootNode = virtualDom.create(virtualDom.h("div.mapillary-dom-renderer", [])); element.appendChild(rootNode); this._offset$ = this._adaptiveOperation$.pipe(scan((adaptive, operation) => { return operation(adaptive); }, { elementHeight: element.offsetHeight, elementWidth: element.offsetWidth, imageAspect: 0, renderMode: exports.RenderMode.Fill, }), filter((adaptive) => { return adaptive.imageAspect > 0 && adaptive.elementWidth > 0 && adaptive.elementHeight > 0; }), map((adaptive) => { const elementAspect = adaptive.elementWidth / adaptive.elementHeight; const ratio = adaptive.imageAspect / elementAspect; let verticalOffset = 0; let horizontalOffset = 0; if (adaptive.renderMode === exports.RenderMode.Letterbox) { if (adaptive.imageAspect > elementAspect) { verticalOffset = adaptive.elementHeight * (1 - 1 / ratio) / 2; } else { horizontalOffset = adaptive.elementWidth * (1 - ratio) / 2; } } else { if (adaptive.imageAspect > elementAspect) { horizontalOffset = -adaptive.elementWidth * (ratio - 1) / 2; } else { verticalOffset = -adaptive.elementHeight * (1 / ratio - 1) / 2; } } return { bottom: verticalOffset, left: horizontalOffset, right: horizontalOffset, top: verticalOffset, }; })); const imageAspectSubscription = this._currentFrame$.pipe(filter((frame) => { return frame.state.currentImage != null; }), distinctUntilChanged((k1, k2) => { return k1 === k2; }, (frame) => { return frame.state.currentImage.id; }), map((frame) => { return frame.state.currentTransform.basicAspect; }), map((aspect) => { return (adaptive) => { adaptive.imageAspect = aspect; return adaptive; }; })) .subscribe(this._adaptiveOperation$); const renderAdaptiveSubscription = combineLatest(this._renderAdaptive$.pipe(scan((vNodeHashes, vNodeHash) => { if (vNodeHash.vNode == null) { delete vNodeHashes[vNodeHash.name]; } else { vNodeHashes[vNodeHash.name] = vNodeHash.vNode; } return vNodeHashes; }, {})), this._offset$).pipe(map((vo) => { const vNodes = []; const hashes = vo[0]; for (const name in hashes) { if (!hashes.hasOwnProperty(name)) { continue; } vNodes.push(hashes[name]); } const offset = vo[1]; const properties = { style: { bottom: offset.bottom + "px", left: offset.left + "px", "pointer-events": "none", position: "absolute", right: offset.right + "px", top: offset.top + "px", }, }; return { name: "mapillary-dom-adaptive-renderer", vNode: virtualDom.h("div.mapillary-dom-adaptive-renderer", properties, vNodes), }; })) .subscribe(this._render$); this._vNode$ = this._render$.pipe(scan((vNodeHashes, vNodeHash) => { if (vNodeHash.vNode == null) { delete vNodeHashes[vNodeHash.name]; } else { vNodeHashes[vNodeHash.name] = vNodeHash.vNode; } return vNodeHashes; }, {}), map((hashes) => { const vNodes = []; for (const name in hashes) { if (!hashes.hasOwnProperty(name)) { continue; } vNodes.push(hashes[name]); } return virtualDom.h("div.mapillary-dom-renderer", vNodes); })); this._vPatch$ = this._vNode$.pipe(scan((nodePatch, vNode) => { nodePatch.vpatch = virtualDom.diff(nodePatch.vNode, vNode); nodePatch.vNode = vNode; return nodePatch; }, { vNode: virtualDom.h("div.mapillary-dom-renderer", []), vpatch: null }), pluck("vpatch")); this._element$ = this._vPatch$.pipe(scan((oldElement, vPatch) => { return virtualDom.patch(oldElement, vPatch); }, rootNode), publishReplay(1), refCount()); subs.push(imageAspectSubscription); subs.push(renderAdaptiveSubscription); subs.push(this._element$.subscribe(() => { })); subs.push(this._renderService.size$.pipe(map((size) => { return (adaptive) => { adaptive.elementWidth = size.width; adaptive.elementHeight = size.height; return adaptive; }; })) .subscribe(this._adaptiveOperation$)); subs.push(this._renderService.renderMode$.pipe(map((renderMode) => { return (adaptive) => { adaptive.renderMode = renderMode; return adaptive; }; })) .subscribe(this._adaptiveOperation$)); } get element$() { return this._element$; } get render$() { return this._render$; } get renderAdaptive$() { return this._renderAdaptive$; } clear(name) { this._renderAdaptive$.next({ name: name, vNode: null }); this._render$.next({ name: name, vNode: null }); } remove() { this._subscriptions.unsubscribe(); } } class GLRenderer { constructor(canvas, canvasContainer, renderService) { this._renderFrame$ = new Subject(); this._renderCameraOperation$ = new Subject(); this._render$ = new Subject(); this._clear$ = new Subject(); this._renderOperation$ = new Subject(); this._rendererOperation$ = new Subject(); this._eraserOperation$ = new Subject(); this._triggerOperation$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._opaqueRender$ = new Subject(); this._renderService = renderService; const subs = this._subscriptions; this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { return operation(renderer); }, { needsRender: false, renderer: null }), filter((renderer) => { return !!renderer.renderer; })); this._renderCollection$ = this._renderOperation$.pipe(scan((hashes, operation) => { return operation(hashes); }, {}), share()); this._renderCamera$ = this._renderCameraOperation$.pipe(scan((rc, operation) => { return operation(rc); }, { frameId: -1, needsRender: false, perspective: null })); this._eraser$ = this._eraserOperation$.pipe(startWith((eraser) => { return eraser; }), scan((eraser, operation) => { return operation(eraser); }, { needsRender: false })); const trigger$ = this._triggerOperation$.pipe(startWith((trigger) => { return trigger; }), scan((trigger, operation) => { return operation(trigger); }, { needsRender: false })); const clearColor = new Color(0x0F0F0F); const renderSubscription = combineLatest(this._renderer$, this._renderCollection$, this._renderCamera$, this._eraser$, trigger$).pipe(map(([renderer, hashes, rc, eraser, trigger]) => { const renders = Object.keys(hashes) .map((key) => { return hashes[key]; }); return { camera: rc, eraser: eraser, trigger: trigger, renderer: renderer, renders: renders }; }), filter((co) => { let needsRender = co.renderer.needsRender || co.camera.needsRender || co.eraser.needsRender || co.trigger.needsRender; const frameId = co.camera.frameId; for (const render of co.renders) { if (render.frameId !== frameId) { return false; } needsRender = needsRender || render.needsRender; } return needsRender; }), distinctUntilChanged((n1, n2) => { return n1 === n2; }, (co) => { return co.eraser.needsRender || co.trigger.needsRender ? -co.camera.frameId : co.camera.frameId; })) .subscribe((co) => { co.renderer.needsRender = false; co.camera.needsRender = false; co.eraser.needsRender = false; co.trigger.needsRender = false; const perspectiveCamera = co.camera.perspective; const backgroundRenders = []; const opaqueRenders = []; for (const render of co.renders) { if (render.pass === RenderPass.Background) { backgroundRenders.push(render.render); } else if (render.pass === RenderPass.Opaque) { opaqueRenders.push(render.render); } } const renderer = co.renderer.renderer; renderer.resetState(); renderer.setClearColor(clearColor, 1.0); renderer.clear(); for (const renderBackground of backgroundRenders) { renderBackground(perspectiveCamera, renderer); } renderer.clearDepth(); for (const renderOpaque of opaqueRenders) { renderOpaque(perspectiveCamera, renderer); } renderer.resetState(); this._opaqueRender$.next(); }); subs.push(renderSubscription); subs.push(this._renderFrame$.pipe(map((rc) => { return (irc) => { irc.frameId = rc.frameId; irc.perspective = rc.perspective; if (rc.changed === true) { irc.needsRender = true; } return irc; }; })) .subscribe(this._renderCameraOperation$)); this._renderFrameSubscribe(); const renderHash$ = this._render$.pipe(map((hash) => { return (hashes) => { hashes[hash.name] = hash.renderer; return hashes; }; })); const clearHash$ = this._clear$.pipe(map((name) => { return (hashes) => { delete hashes[name]; return hashes; }; })); subs.push(merge(renderHash$, clearHash$) .subscribe(this._renderOperation$)); this._webGLRenderer$ = this._render$.pipe(first(), map(() => { canvasContainer.appendChild(canvas); const element = renderService.element; const webGLRenderer = new WebGLRenderer({ antialias: true, canvas: canvas, }); webGLRenderer.setPixelRatio(window.devicePixelRatio); webGLRenderer.setSize(element.offsetWidth, element.offsetHeight); webGLRenderer.autoClear = false; return webGLRenderer; }), publishReplay(1), refCount()); subs.push(this._webGLRenderer$ .subscribe(() => { })); const createRenderer$ = this._webGLRenderer$.pipe(first(), map((webGLRenderer) => { return (renderer) => { renderer.needsRender = true; renderer.renderer = webGLRenderer; return renderer; }; })); const resizeRenderer$ = this._renderService.size$.pipe(map((size) => { return (renderer) => { if (renderer.renderer == null) { return renderer; } renderer.renderer.setSize(size.width, size.height); renderer.needsRender = true; return renderer; }; })); const clearRenderer$ = this._clear$.pipe(map(() => { return (renderer) => { if (renderer.renderer == null) { return renderer; } renderer.needsRender = true; return renderer; }; })); subs.push(merge(createRenderer$, resizeRenderer$, clearRenderer$) .subscribe(this._rendererOperation$)); const renderCollectionEmpty$ = this._renderCollection$.pipe(filter((hashes) => { return Object.keys(hashes).length === 0; }), share()); subs.push(renderCollectionEmpty$ .subscribe(() => { if (this._renderFrameSubscription == null) { return; } this._renderFrameSubscription.unsubscribe(); this._renderFrameSubscription = null; this._renderFrameSubscribe(); })); subs.push(renderCollectionEmpty$.pipe(map(() => { return (eraser) => { eraser.needsRender = true; return eraser; }; })) .subscribe(this._eraserOperation$)); } get render$() { return this._render$; } get opaqueRender$() { return this._opaqueRender$; } get webGLRenderer$() { return this._webGLRenderer$; } clear(name) { this._clear$.next(name); } remove() { this._rendererOperation$.next((renderer) => { if (renderer.renderer != null) { const extension = renderer.renderer .getContext() .getExtension("WEBGL_lose_context"); if (!!extension) { extension.loseContext(); } renderer.renderer = null; } return renderer; }); if (this._renderFrameSubscription != null) { this._renderFrameSubscription.unsubscribe(); } this._subscriptions.unsubscribe(); } triggerRerender() { this._renderService.renderCameraFrame$ .pipe(skip(1), first()) .subscribe(() => { this._triggerOperation$.next((trigger) => { trigger.needsRender = true; return trigger; }); }); } _renderFrameSubscribe() { this._render$.pipe(first(), map(() => { return (irc) => { irc.needsRender = true; return irc; }; })) .subscribe((operation) => { this._renderCameraOperation$.next(operation); }); this._renderFrameSubscription = this._render$.pipe(first(), mergeMap(() => { return this._renderService.renderCameraFrame$; })) .subscribe(this._renderFrame$); } } /** * @class Camera * * @classdesc Holds information about a camera. */ class Camera { /** * Create a new camera instance. * @param {Transform} [transform] - Optional transform instance. */ constructor(transform) { if (transform != null) { this._position = new Vector3().fromArray(transform.unprojectSfM([0, 0], 0)); this._lookat = new Vector3().fromArray(transform.unprojectSfM([0, 0], 10)); this._up = transform.upVector(); this._focal = this._getFocal(transform); } else { this._position = new Vector3(0, 0, 0); this._lookat = new Vector3(1, 0, 0); this._up = new Vector3(0, 0, 1); this._focal = 1; } } /** * Get position. * @returns {THREE.Vector3} The position vector. */ get position() { return this._position; } /** * Get lookat. * @returns {THREE.Vector3} The lookat vector. */ get lookat() { return this._lookat; } /** * Get up. * @returns {THREE.Vector3} The up vector. */ get up() { return this._up; } /** * Get focal. * @returns {number} The focal length. */ get focal() { return this._focal; } /** * Set focal. */ set focal(value) { this._focal = value; } /** * Update this camera to the linearly interpolated value of two other cameras. * * @param {Camera} a - First camera. * @param {Camera} b - Second camera. * @param {number} alpha - Interpolation value on the interval [0, 1]. */ lerpCameras(a, b, alpha) { this._position.subVectors(b.position, a.position).multiplyScalar(alpha).add(a.position); this._lookat.subVectors(b.lookat, a.lookat).multiplyScalar(alpha).add(a.lookat); this._up.subVectors(b.up, a.up).multiplyScalar(alpha).add(a.up); this._focal = (1 - alpha) * a.focal + alpha * b.focal; } /** * Copy the properties of another camera to this camera. * * @param {Camera} other - Another camera. */ copy(other) { this._position.copy(other.position); this._lookat.copy(other.lookat); this._up.copy(other.up); this._focal = other.focal; } /** * Clone this camera. * * @returns {Camera} A camera with cloned properties equal to this camera. */ clone() { let camera = new Camera(); camera.position.copy(this._position); camera.lookat.copy(this._lookat); camera.up.copy(this._up); camera.focal = this._focal; return camera; } /** * Determine the distance between this camera and another camera. * * @param {Camera} other - Another camera. * @returns {number} The distance between the cameras. */ diff(other) { let pd = this._position.distanceToSquared(other.position); let ld = this._lookat.distanceToSquared(other.lookat); let ud = this._up.distanceToSquared(other.up); let fd = 100 * Math.abs(this._focal - other.focal); return Math.max(pd, ld, ud, fd); } /** * Get the focal length based on the transform. * * @description Returns the focal length corresponding * to a 90 degree field of view for spherical * transforms. * * Returns the transform focal length for other * projection types. * * @returns {number} Focal length. */ _getFocal(transform) { if (!isSpherical(transform.cameraType)) { return transform.focal; } return 0.5 / Math.tan(Math.PI / 2); } } class RenderCamera { constructor(elementWidth, elementHeight, renderMode) { this._spatial = new Spatial(); this._viewportCoords = new ViewportCoords(); this._size = { width: elementWidth, height: elementHeight }; this._initialFov = 60; this._alpha = -1; this._stateTransitionAlpha = -1; this._stateTransitionFov = -1; this._renderMode = renderMode; this._zoom = 0; this._frameId = -1; this._changed = false; this._changedForFrame = -1; this._currentImageId = null; this._previousImageId = null; this._currentSpherical = false; this._previousSpherical = false; this._state = null; this._currentProjectedPoints = []; this._previousProjectedPoints = []; this._currentFov = this._initialFov; this._previousFov = this._initialFov; this._camera = new Camera(); this._currentCameraUp = new Vector3(); this._previousCameraUp = new Vector3(); this._currentTransformUp = new Vector3(); this._previousTransformUp = new Vector3(); this._currentTransformForward = new Vector3(); this._previousTransformForward = new Vector3(); this._perspective = new PerspectiveCamera$1(this._initialFov, this._computeAspect(elementWidth, elementHeight), 0.1, 10000); this._perspective.position.copy(this._camera.position); this._perspective.up.copy(this._camera.up); this._perspective.lookAt(this._camera.lookat); this._perspective.updateMatrixWorld(true); this._perspective.matrixAutoUpdate = false; this._rotation = { phi: 0, theta: 0 }; } get alpha() { return this._alpha; } get camera() { return this._camera; } get changed() { return this._frameId === this._changedForFrame; } get frameId() { return this._frameId; } get perspective() { return this._perspective; } get renderMode() { return this._renderMode; } get rotation() { return this._rotation; } get zoom() { return this._zoom; } get size() { return this._size; } getTilt() { return 90 - this._spatial.radToDeg(this._rotation.theta); } fovToZoom(fov) { fov = Math.min(90, Math.max(0, fov)); const currentFov = this._computeCurrentFov(0); const actualFov = this._alpha === 1 ? currentFov : this._interpolateFov(currentFov, this._computePreviousFov(0), this._alpha); const y0 = Math.tan(actualFov / 2 * Math.PI / 180); const y1 = Math.tan(fov / 2 * Math.PI / 180); const zoom = Math.log(y0 / y1) / Math.log(2); return zoom; } setFrame(frame) { const state = frame.state; if (state.state !== this._state) { this._state = state.state; if (this._state !== State.Custom) { this.setRenderMode(this._renderMode); this.setSize(this._size); } if (this._state === State.Earth) { const y = this._fovToY(this._perspective.fov, this._zoom); this._stateTransitionFov = this._yToFov(y, 0); } this._changed = true; } const currentImageId = state.currentImage.id; const previousImageId = !!state.previousImage ? state.previousImage.id : null; if (currentImageId !== this._currentImageId || this._changed) { this._currentImageId = currentImageId; this._currentSpherical = isSpherical(state.currentTransform.cameraType); this._currentProjectedPoints = this._computeProjectedPoints(state.currentTransform); this._currentCameraUp.copy(state.currentCamera.up); this._currentTransformUp.copy(state.currentTransform.upVector()); this._currentTransformForward.copy(new Vector3().fromArray(state.currentTransform.unprojectSfM([0, 0], 10)) .sub(new Vector3().fromArray(state.currentTransform. unprojectSfM([0, 0], 0)))); this._changed = true; } if (previousImageId !== this._previousImageId) { this._previousImageId = previousImageId; this._previousSpherical = isSpherical(state.previousTransform.cameraType); this._previousProjectedPoints = this._computeProjectedPoints(state.previousTransform); this._previousCameraUp.copy(state.previousCamera.up); this._previousTransformUp.copy(state.previousTransform.upVector()); this._previousTransformForward.copy(new Vector3().fromArray(state.previousTransform.unprojectSfM([0, 0], 10)) .sub(new Vector3().fromArray(state.previousTransform. unprojectSfM([0, 0], 0)))); this._changed = true; } const zoom = state.zoom; if (zoom !== this._zoom) { this._changed = true; } const camera = state.camera; if (this._changed) { this._currentFov = this._computeCurrentFov(zoom); this._previousFov = this._computePreviousFov(zoom); } const alpha = state.alpha; const sta = state.stateTransitionAlpha; if (this._changed || alpha !== this._alpha || sta !== this._stateTransitionAlpha) { this._alpha = alpha; this._stateTransitionAlpha = sta; switch (this._state) { case State.Earth: { const startFov = this._stateTransitionFov; const endFov = this._focalToFov(state.camera.focal); const fov = MathUtils.lerp(startFov, endFov, sta); const y = this._fovToY(fov, 0); this._perspective.fov = this._yToFov(y, zoom); break; } case State.Custom: break; default: this._perspective.fov = this._interpolateFov(this._currentFov, this._previousFov, this._alpha); this._changed = true; break; } this._zoom = zoom; if (this._state !== State.Custom) { this._perspective.updateProjectionMatrix(); } } if (this._camera.diff(camera) > 1e-9) { this._camera.copy(camera); this._rotation = this._computeRotation(camera); this._perspective.up.copy(camera.up); this._perspective.position.copy(camera.position); // Workaround for shaking camera this._perspective.matrixAutoUpdate = true; this._perspective.lookAt(camera.lookat); this._perspective.matrixAutoUpdate = false; this._perspective.updateMatrix(); this._perspective.updateMatrixWorld(false); this._changed = true; } this._setFrameId(frame.id); } setProjectionMatrix(matrix) { this._perspective.fov = this._focalToFov(matrix[5] / 2); this._perspective.projectionMatrix.fromArray(matrix); this._perspective.projectionMatrixInverse .copy(this._perspective.projectionMatrix) .invert(); this._changed = true; } setRenderMode(renderMode) { this._renderMode = renderMode; if (this._state === State.Custom) { return; } this._perspective.fov = this._computeFov(); this._perspective.updateProjectionMatrix(); this._changed = true; } setSize(size) { this._size = size; if (this._state === State.Custom) { return; } this._perspective.aspect = this._computeAspect(size.width, size.height); this._perspective.fov = this._computeFov(); this._perspective.updateProjectionMatrix(); this._changed = true; } _computeAspect(elementWidth, elementHeight) { return elementWidth === 0 ? 0 : elementWidth / elementHeight; } _computeUpRotation(up, refForward, refUp) { const right = new Vector3().crossVectors(refForward, refUp).normalize(); const normal = new Vector3().crossVectors(refUp, right).normalize(); const xzFromNormal = new Quaternion() .setFromUnitVectors(normal, new Vector3(0, 1, 0)); const upXz = up.clone().applyQuaternion(xzFromNormal); const refUpXz = refUp.clone().applyQuaternion(xzFromNormal); const upRad = Math.acos(new Vector2(upXz.x, upXz.z).normalize().x); const refUpRad = Math.acos(new Vector2(refUpXz.x, refUpXz.z).normalize().x); return refUpRad - upRad; } _computeCurrentFov(zoom) { if (this._perspective.aspect === 0) { return 0; } if (!this._currentImageId) { return this._initialFov; } return this._currentSpherical ? this._yToFov(1, zoom) : this._computeVerticalFov(this._currentProjectedPoints, this._renderMode, zoom, this.perspective.aspect, this._computeUpRotation(this._currentTransformUp, this._currentTransformForward, this._currentCameraUp)); } _computeFov() { this._currentFov = this._computeCurrentFov(this._zoom); this._previousFov = this._computePreviousFov(this._zoom); return this._interpolateFov(this._currentFov, this._previousFov, this._alpha); } _computePreviousFov(zoom) { if (this._perspective.aspect === 0) { return 0; } if (!this._currentImageId) { return this._initialFov; } return !this._previousImageId ? this._currentFov : this._previousSpherical ? this._yToFov(1, zoom) : this._computeVerticalFov(this._previousProjectedPoints, this._renderMode, zoom, this.perspective.aspect, this._computeUpRotation(this._previousTransformUp, this._previousTransformForward, this._previousCameraUp)); } _computeProjectedPoints(transform) { const vertices = [[0, 0], [1, 0]]; const directions = [[1, 0], [0, 1]]; const pointsPerLine = 50; return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords); } _computeRequiredVerticalFov(projectedPoint, zoom, aspect) { const maxY = Math.max(Math.abs(projectedPoint[0]) / aspect, Math.abs(projectedPoint[1])); return this._yToFov(maxY, zoom); } _computeRotation(camera) { let direction = camera.lookat.clone().sub(camera.position); let up = camera.up.clone(); let phi = this._spatial.azimuthal(direction.toArray(), up.toArray()); let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); return { phi: phi, theta: theta }; } _computeVerticalFov(projectedPoints, renderMode, zoom, aspect, theta) { const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); const fovs = projectedPoints .map(([x, y]) => { const rotatedPoint = [ cosTheta * x - sinTheta * y, sinTheta * x + cosTheta * y, ]; return this._computeRequiredVerticalFov(rotatedPoint, zoom, aspect); }); const vFovFill = Math.min(...fovs) * 0.995; if (renderMode === exports.RenderMode.Fill) { return vFovFill; } const vFovLetterbox = Math.max(...fovs); const hFovLetterbox = aspect * vFovLetterbox; const fovCoeff = 1.25; const hFovFill = aspect * vFovFill; const hFovMax = fovCoeff * hFovFill; let vFov = vFovLetterbox; if (hFovLetterbox > hFovMax) { vFov *= hFovMax / hFovLetterbox; } const fovMax = 135; const vFovMax = aspect > 2 ? vFovFill : fovCoeff * vFovFill; vFov = Math.min(vFov, vFovMax, fovMax); vFov = Math.max(vFov, vFovFill); return vFov; } _yToFov(y, zoom) { return 2 * Math.atan(y / Math.pow(2, zoom)) * 180 / Math.PI; } _focalToFov(focal) { return 2 * Math.atan2(1, 2 * focal) * 180 / Math.PI; } _fovToY(fov, zoom) { return Math.pow(2, zoom) * Math.tan(Math.PI * fov / 360); } _interpolateFov(v1, v2, alpha) { return alpha * v1 + (1 - alpha) * v2; } _setFrameId(frameId) { this._frameId = frameId; if (this._changed) { this._changed = false; this._changedForFrame = frameId; } } } class RenderService { constructor(element, currentFrame$, renderMode, renderCamera) { this._subscriptions = new SubscriptionHolder(); this._element = element; this._currentFrame$ = currentFrame$; this._spatial = new Spatial(); renderMode = renderMode != null ? renderMode : exports.RenderMode.Fill; this._resize$ = new Subject(); this._projectionMatrix$ = new Subject(); this._renderCameraOperation$ = new Subject(); this._size$ = new BehaviorSubject({ height: this._element.offsetHeight, width: this._element.offsetWidth, }); const subs = this._subscriptions; subs.push(this._resize$.pipe(map(() => { return { height: this._element.offsetHeight, width: this._element.offsetWidth, }; })) .subscribe(this._size$)); this._renderMode$ = new BehaviorSubject(renderMode); this._renderCameraHolder$ = this._renderCameraOperation$.pipe(startWith((rc) => { return rc; }), scan((rc, operation) => { return operation(rc); }, renderCamera !== null && renderCamera !== void 0 ? renderCamera : new RenderCamera(this._element.offsetWidth, this._element.offsetHeight, renderMode)), publishReplay(1), refCount()); this._renderCameraFrame$ = this._currentFrame$.pipe(withLatestFrom(this._renderCameraHolder$), tap(([frame, rc]) => { rc.setFrame(frame); }), map((args) => { return args[1]; }), publishReplay(1), refCount()); this._renderCamera$ = this._renderCameraFrame$.pipe(filter((rc) => { return rc.changed; }), publishReplay(1), refCount()); this._bearing$ = this._renderCamera$.pipe(map((rc) => { let bearing = this._spatial.radToDeg(this._spatial.azimuthalToBearing(rc.rotation.phi)); return this._spatial.wrap(bearing, 0, 360); }), publishReplay(1), refCount()); subs.push(this._size$.pipe(skip(1), map((size) => { return (rc) => { rc.setSize(size); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._renderMode$.pipe(skip(1), map((rm) => { return (rc) => { rc.setRenderMode(rm); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._projectionMatrix$.pipe(map((projectionMatrix) => { return (rc) => { rc.setProjectionMatrix(projectionMatrix); return rc; }; })) .subscribe(this._renderCameraOperation$)); subs.push(this._bearing$.subscribe(() => { })); subs.push(this._renderCameraHolder$.subscribe(() => { })); subs.push(this._size$.subscribe(() => { })); subs.push(this._renderMode$.subscribe(() => { })); subs.push(this._renderCamera$.subscribe(() => { })); subs.push(this._renderCameraFrame$.subscribe(() => { })); } get bearing$() { return this._bearing$; } get element() { return this._element; } get projectionMatrix$() { return this._projectionMatrix$; } get renderCamera$() { return this._renderCamera$; } get renderCameraFrame$() { return this._renderCameraFrame$; } get renderMode$() { return this._renderMode$; } get resize$() { return this._resize$; } get size$() { return this._size$; } dispose() { this._subscriptions.unsubscribe(); } } class KeyboardService { constructor(canvasContainer) { this._keyDown$ = fromEvent(canvasContainer, "keydown"); this._keyUp$ = fromEvent(canvasContainer, "keyup"); } get keyDown$() { return this._keyDown$; } get keyUp$() { return this._keyUp$; } } // MouseEvent.button const LEFT_BUTTON = 0; const RIGHT_BUTTON = 2; // MouseEvent.buttons const BUTTONS_MAP = { [LEFT_BUTTON]: 1, [RIGHT_BUTTON]: 2 }; class MouseService { constructor(container, canvasContainer, domContainer, doc) { this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._activeSubject$ = new BehaviorSubject(false); this._active$ = this._activeSubject$ .pipe(distinctUntilChanged(), publishReplay(1), refCount()); this._claimMouse$ = new Subject(); this._claimWheel$ = new Subject(); this._deferPixelClaims$ = new Subject(); this._deferPixels$ = this._deferPixelClaims$ .pipe(scan((claims, claim) => { if (claim.deferPixels == null) { delete claims[claim.name]; } else { claims[claim.name] = claim.deferPixels; } return claims; }, {}), map((claims) => { let deferPixelMax = -1; for (const key in claims) { if (!claims.hasOwnProperty(key)) { continue; } const deferPixels = claims[key]; if (deferPixels > deferPixelMax) { deferPixelMax = deferPixels; } } return deferPixelMax; }), startWith(-1), publishReplay(1), refCount()); subs.push(this._deferPixels$.subscribe(() => { })); this._documentMouseMove$ = fromEvent(doc, "pointermove") .pipe(filter(this._isMousePen)); this._documentMouseUp$ = fromEvent(doc, "pointerup") .pipe(filter(this._isMousePen)); this._mouseDown$ = fromEvent(canvasContainer, "pointerdown") .pipe(filter(this._isMousePen)); this._mouseEnter$ = fromEvent(canvasContainer, "pointerenter") .pipe(filter(this._isMousePen)); this._mouseLeave$ = fromEvent(canvasContainer, "pointerleave") .pipe(filter(this._isMousePen)); this._mouseMove$ = fromEvent(canvasContainer, "pointermove") .pipe(filter(this._isMousePen)); this._mouseUp$ = fromEvent(canvasContainer, "pointerup") .pipe(filter(this._isMousePen)); this._mouseOut$ = fromEvent(canvasContainer, "pointerout") .pipe(filter(this._isMousePen)); this._mouseOver$ = fromEvent(canvasContainer, "pointerover") .pipe(filter(this._isMousePen)); this._domMouseDown$ = fromEvent(domContainer, "pointerdown") .pipe(filter(this._isMousePen)); this._domMouseMove$ = fromEvent(domContainer, "pointermove") .pipe(filter(this._isMousePen)); this._click$ = fromEvent(canvasContainer, "click"); this._contextMenu$ = fromEvent(canvasContainer, "contextmenu"); this._windowBlur$ = fromEvent(window, "blur"); this._dblClick$ = merge(fromEvent(container, "click"), fromEvent(canvasContainer, "dblclick")) .pipe(bufferCount(3, 1), filter((events) => { const event1 = events[0]; const event2 = events[1]; const event3 = events[2]; return event1.type === "click" && event2.type === "click" && event3.type === "dblclick" && event1.target.parentNode === canvasContainer && event2.target.parentNode === canvasContainer; }), map((events) => { return events[2]; }), share()); subs.push(merge(this._domMouseDown$, this._domMouseMove$, this._dblClick$, this._contextMenu$) .subscribe((event) => { event.preventDefault(); })); this._mouseWheel$ = merge(fromEvent(canvasContainer, "wheel"), fromEvent(domContainer, "wheel")) .pipe(share()); this._consistentContextMenu$ = merge(this._mouseDown$, this._mouseMove$, this._mouseOut$, this._mouseUp$, this._contextMenu$) .pipe(bufferCount(3, 1), filter((events) => { // fire context menu on mouse up both on mac and windows return events[0].type === "pointerdown" && events[1].type === "contextmenu" && events[2].type === "pointerup"; }), map((events) => { return events[1]; }), share()); const dragStop$ = merge(this._windowBlur$, this._documentMouseMove$ .pipe(filter((e) => { return this._buttonReleased(e, LEFT_BUTTON); })), this._documentMouseUp$ .pipe(filter((e) => { return this._mouseButton(e) === LEFT_BUTTON; }))) .pipe(share()); const mouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._mouseDown$, dragStop$, true) .pipe(share()); this._mouseDragStart$ = this._createMouseDragStart$(mouseDragInitiate$) .pipe(share()); this._mouseDrag$ = this._createMouseDrag$(mouseDragInitiate$, dragStop$) .pipe(share()); this._mouseDragEnd$ = this._createMouseDragEnd$(this._mouseDragStart$, dragStop$) .pipe(share()); const domMouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._domMouseDown$, dragStop$, false) .pipe(share()); this._domMouseDragStart$ = this._createMouseDragStart$(domMouseDragInitiate$) .pipe(share()); this._domMouseDrag$ = this._createMouseDrag$(domMouseDragInitiate$, dragStop$) .pipe(share()); this._domMouseDragEnd$ = this._createMouseDragEnd$(this._domMouseDragStart$, dragStop$) .pipe(share()); const rightDragStop$ = merge(this._windowBlur$, this._documentMouseMove$.pipe(filter((e) => { return this._buttonReleased(e, RIGHT_BUTTON); })), this._documentMouseUp$.pipe(filter((e) => { return this._mouseButton(e) === RIGHT_BUTTON; }))) .pipe(share()); const mouseRightDragInitiate$ = this._createMouseDragInitiate$(RIGHT_BUTTON, this._mouseDown$, rightDragStop$, true) .pipe(share()); this._mouseRightDragStart$ = this._createMouseDragStart$(mouseRightDragInitiate$) .pipe(share()); this._mouseRightDrag$ = this._createMouseDrag$(mouseRightDragInitiate$, rightDragStop$) .pipe(share()); this._mouseRightDragEnd$ = this._createMouseDragEnd$(this._mouseRightDragStart$, rightDragStop$) .pipe(share()); this._proximateClick$ = this._mouseDown$ .pipe(switchMap((mouseDown) => { return this._click$.pipe(takeUntil(this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$)), take(1)); }), share()); this._staticClick$ = this._mouseDown$ .pipe(switchMap(() => { return this._click$.pipe(takeUntil(this._documentMouseMove$), take(1)); }), share()); subs.push(this._mouseDragStart$.subscribe()); subs.push(this._mouseDrag$.subscribe()); subs.push(this._mouseDragEnd$.subscribe()); subs.push(this._domMouseDragStart$.subscribe()); subs.push(this._domMouseDrag$.subscribe()); subs.push(this._domMouseDragEnd$.subscribe()); subs.push(this._mouseRightDragStart$.subscribe()); subs.push(this._mouseRightDrag$.subscribe()); subs.push(this._mouseRightDragEnd$.subscribe()); subs.push(this._staticClick$.subscribe()); this._mouseOwner$ = this._createOwner$(this._claimMouse$) .pipe(publishReplay(1), refCount()); this._wheelOwner$ = this._createOwner$(this._claimWheel$) .pipe(publishReplay(1), refCount()); subs.push(this._mouseOwner$.subscribe(() => { })); subs.push(this._wheelOwner$.subscribe(() => { })); } get active$() { return this._active$; } get activate$() { return this._activeSubject$; } get documentMouseMove$() { return this._documentMouseMove$; } get documentMouseUp$() { return this._documentMouseUp$; } get domMouseDragStart$() { return this._domMouseDragStart$; } get domMouseDrag$() { return this._domMouseDrag$; } get domMouseDragEnd$() { return this._domMouseDragEnd$; } get domMouseDown$() { return this._domMouseDown$; } get domMouseMove$() { return this._domMouseMove$; } get mouseOwner$() { return this._mouseOwner$; } get mouseDown$() { return this._mouseDown$; } get mouseEnter$() { return this._mouseEnter$; } get mouseMove$() { return this._mouseMove$; } get mouseLeave$() { return this._mouseLeave$; } get mouseOut$() { return this._mouseOut$; } get mouseOver$() { return this._mouseOver$; } get mouseUp$() { return this._mouseUp$; } get click$() { return this._click$; } get dblClick$() { return this._dblClick$; } get contextMenu$() { return this._consistentContextMenu$; } get mouseWheel$() { return this._mouseWheel$; } get mouseDragStart$() { return this._mouseDragStart$; } get mouseDrag$() { return this._mouseDrag$; } get mouseDragEnd$() { return this._mouseDragEnd$; } get mouseRightDragStart$() { return this._mouseRightDragStart$; } get mouseRightDrag$() { return this._mouseRightDrag$; } get mouseRightDragEnd$() { return this._mouseRightDragEnd$; } get proximateClick$() { return this._proximateClick$; } get staticClick$() { return this._staticClick$; } get windowBlur$() { return this._windowBlur$; } dispose() { this._subscriptions.unsubscribe(); } claimMouse(name, zindex) { this._claimMouse$.next({ name: name, zindex: zindex }); } unclaimMouse(name) { this._claimMouse$.next({ name: name, zindex: null }); } deferPixels(name, deferPixels) { this._deferPixelClaims$.next({ name: name, deferPixels: deferPixels }); } undeferPixels(name) { this._deferPixelClaims$.next({ name: name, deferPixels: null }); } claimWheel(name, zindex) { this._claimWheel$.next({ name: name, zindex: zindex }); } unclaimWheel(name) { this._claimWheel$.next({ name: name, zindex: null }); } filtered$(name, observable$) { return this._filtered(name, observable$, this._mouseOwner$); } filteredWheel$(name, observable$) { return this._filtered(name, observable$, this._wheelOwner$); } _createDeferredMouseMove$(origin, mouseMove$) { return mouseMove$.pipe(map((mouseMove) => { const deltaX = mouseMove.clientX - origin.clientX; const deltaY = mouseMove.clientY - origin.clientY; return [mouseMove, Math.sqrt(deltaX * deltaX + deltaY * deltaY)]; }), withLatestFrom(this._deferPixels$), filter(([[, delta], deferPixels]) => { return delta > deferPixels; }), map(([[mouseMove]]) => { return mouseMove; })); } _createMouseDrag$(mouseDragStartInitiate$, stop$) { return mouseDragStartInitiate$.pipe(map(([, mouseMove]) => { return mouseMove; }), switchMap((mouseMove) => { return concat(of(mouseMove), this._documentMouseMove$).pipe(takeUntil(stop$)); })); } _createMouseDragEnd$(mouseDragStart$, stop$) { return mouseDragStart$.pipe(switchMap(() => { return stop$.pipe(first()); })); } _createMouseDragStart$(mouseDragStartInitiate$) { return mouseDragStartInitiate$.pipe(map(([mouseDown]) => { return mouseDown; })); } _createMouseDragInitiate$(button, mouseDown$, stop$, defer) { return mouseDown$.pipe(filter((mouseDown) => { return this._mouseButton(mouseDown) === button; }), switchMap((mouseDown) => { return combineLatest(of(mouseDown), defer ? this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$) : this._documentMouseMove$).pipe(takeUntil(stop$), take(1)); })); } _createOwner$(claim$) { return claim$.pipe(scan((claims, claim) => { if (claim.zindex == null) { delete claims[claim.name]; } else { claims[claim.name] = claim.zindex; } return claims; }, {}), map((claims) => { let owner = null; let zIndexMax = -1; for (const name in claims) { if (!claims.hasOwnProperty(name)) { continue; } if (claims[name] > zIndexMax) { zIndexMax = claims[name]; owner = name; } } return owner; }), startWith(null)); } _filtered(name, observable$, owner$) { return observable$.pipe(withLatestFrom(owner$), filter(([, owner]) => { return owner === name; }), map(([item]) => { return item; })); } _mouseButton(event) { const upOrDown = event.type === "pointerdown" || event.type === "pointerup"; const InstallTrigger = window.InstallTrigger; if (upOrDown && typeof InstallTrigger !== "undefined" && event.button === RIGHT_BUTTON && event.ctrlKey && window.navigator.platform.toUpperCase().indexOf("MAC") >= 0) { // Fix for the fact that Firefox (detected by InstallTrigger) // on Mac determines e.button = 2 when using Control + left click. return LEFT_BUTTON; } return event.button; } _buttonReleased(event, button) { // Right button `mouseup` is not fired in // Chrome on Mac outside the window or iframe. If // the button is no longer pressed during move // it may have been released and drag stop // should be emitted. const flag = BUTTONS_MAP[button]; return event.buttons === undefined || (event.buttons & flag) !== flag; } _isMousePen(event) { const type = event.pointerType; return type === "mouse" || type === "pen"; } } class SpriteAtlas { set json(value) { this._json = value; } set image(value) { this._image = value; this._texture = new Texture(this._image); this._texture.minFilter = NearestFilter; } get loaded() { return !!(this._image && this._json); } getGLSprite(name) { if (!this.loaded) { throw new Error("Sprites cannot be retrieved before the atlas is loaded."); } let definition = this._json[name]; if (!definition) { console.warn("Sprite with key" + name + "does not exist in sprite definition."); return new Object3D(); } let texture = this._texture.clone(); texture.needsUpdate = true; let width = this._image.width; let height = this._image.height; texture.offset.x = definition.x / width; texture.offset.y = (height - definition.y - definition.height) / height; texture.repeat.x = definition.width / width; texture.repeat.y = definition.height / height; let material = new SpriteMaterial({ map: texture }); return new Sprite(material); } getDOMSprite(name, float) { if (!this.loaded) { throw new Error("Sprites cannot be retrieved before the atlas is loaded."); } if (float == null) { float = exports.Alignment.Center; } let definition = this._json[name]; if (!definition) { console.warn("Sprite with key" + name + "does not exist in sprite definition."); return virtualDom.h("div", {}, []); } let clipTop = definition.y; let clipRigth = definition.x + definition.width; let clipBottom = definition.y + definition.height; let clipLeft = definition.x; let left = -definition.x; let top = -definition.y; let height = this._image.height; let width = this._image.width; switch (float) { case exports.Alignment.Bottom: case exports.Alignment.Center: case exports.Alignment.Top: left -= definition.width / 2; break; case exports.Alignment.BottomLeft: case exports.Alignment.Left: case exports.Alignment.TopLeft: left -= definition.width; break; case exports.Alignment.BottomRight: case exports.Alignment.Right: case exports.Alignment.TopRight: } switch (float) { case exports.Alignment.Center: case exports.Alignment.Left: case exports.Alignment.Right: top -= definition.height / 2; break; case exports.Alignment.Top: case exports.Alignment.TopLeft: case exports.Alignment.TopRight: top -= definition.height; break; case exports.Alignment.Bottom: case exports.Alignment.BottomLeft: case exports.Alignment.BottomRight: } let pixelRatioInverse = 1 / definition.pixelRatio; clipTop *= pixelRatioInverse; clipRigth *= pixelRatioInverse; clipBottom *= pixelRatioInverse; clipLeft *= pixelRatioInverse; left *= pixelRatioInverse; top *= pixelRatioInverse; height *= pixelRatioInverse; width *= pixelRatioInverse; let properties = { src: this._image.src, style: { clip: `rect(${clipTop}px, ${clipRigth}px, ${clipBottom}px, ${clipLeft}px)`, height: `${height}px`, left: `${left}px`, position: "absolute", top: `${top}px`, width: `${width}px`, }, }; return virtualDom.h("img", properties, []); } } class SpriteService { constructor(sprite) { this._retina = window.devicePixelRatio > 1; this._spriteAtlasOperation$ = new Subject(); this._spriteAtlas$ = this._spriteAtlasOperation$.pipe(startWith((atlas) => { return atlas; }), scan((atlas, operation) => { return operation(atlas); }, new SpriteAtlas()), publishReplay(1), refCount()); this._atlasSubscription = this._spriteAtlas$ .subscribe(() => { }); if (sprite == null) { return; } let format = this._retina ? "@2x" : ""; let imageXmlHTTP = new XMLHttpRequest(); imageXmlHTTP.open("GET", sprite + format + ".png", true); imageXmlHTTP.responseType = "arraybuffer"; imageXmlHTTP.onload = () => { let image = new Image(); image.onload = () => { this._spriteAtlasOperation$.next((atlas) => { atlas.image = image; return atlas; }); }; let blob = new Blob([imageXmlHTTP.response]); image.src = window.URL.createObjectURL(blob); }; imageXmlHTTP.onerror = (error) => { console.error(new Error(`Failed to fetch sprite sheet (${sprite}${format}.png)`)); }; imageXmlHTTP.send(); let jsonXmlHTTP = new XMLHttpRequest(); jsonXmlHTTP.open("GET", sprite + format + ".json", true); jsonXmlHTTP.responseType = "text"; jsonXmlHTTP.onload = () => { let json = JSON.parse(jsonXmlHTTP.response); this._spriteAtlasOperation$.next((atlas) => { atlas.json = json; return atlas; }); }; jsonXmlHTTP.onerror = (error) => { console.error(new Error(`Failed to fetch sheet (${sprite}${format}.json)`)); }; jsonXmlHTTP.send(); } get spriteAtlas$() { return this._spriteAtlas$; } dispose() { this._atlasSubscription.unsubscribe(); } } class TouchService { constructor(canvasContainer, domContainer) { this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._activeSubject$ = new BehaviorSubject(false); this._active$ = this._activeSubject$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); subs.push(fromEvent(domContainer, "touchmove") .subscribe((event) => { event.preventDefault(); })); this._touchStart$ = fromEvent(canvasContainer, "touchstart"); this._touchMove$ = fromEvent(canvasContainer, "touchmove"); this._touchEnd$ = fromEvent(canvasContainer, "touchend"); this._touchCancel$ = fromEvent(canvasContainer, "touchcancel"); const tapStart$ = this._touchStart$.pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; }), share()); this._doubleTap$ = tapStart$.pipe(bufferWhen(() => { return tapStart$.pipe(first(), switchMap(() => { return merge(timer(300), tapStart$).pipe(take(1)); })); }), filter((events) => { return events.length === 2; }), map((events) => { return events[events.length - 1]; }), share()); subs.push(this._doubleTap$ .subscribe((event) => { event.preventDefault(); })); this._singleTouchMove$ = this._touchMove$.pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; }), share()); let singleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length === 1 && te.targetTouches.length === 1; })); let multipleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length >= 1; })); let touchStop$ = merge(this._touchEnd$, this._touchCancel$).pipe(filter((te) => { return te.touches.length === 0; })); this._singleTouchDragStart$ = singleTouchStart$.pipe(mergeMap(() => { return this._singleTouchMove$.pipe(takeUntil(merge(touchStop$, multipleTouchStart$)), take(1)); })); this._singleTouchDragEnd$ = singleTouchStart$.pipe(mergeMap(() => { return merge(touchStop$, multipleTouchStart$).pipe(first()); })); this._singleTouchDrag$ = singleTouchStart$.pipe(switchMap(() => { return this._singleTouchMove$.pipe(skip(1), takeUntil(merge(multipleTouchStart$, touchStop$))); })); let touchesChanged$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$); this._pinchStart$ = touchesChanged$.pipe(filter((te) => { return te.touches.length === 2 && te.targetTouches.length === 2; })); this._pinchEnd$ = touchesChanged$.pipe(filter((te) => { return te.touches.length !== 2 || te.targetTouches.length !== 2; })); this._pinchOperation$ = new Subject(); this._pinch$ = this._pinchOperation$.pipe(scan((pinch, operation) => { return operation(pinch); }, { changeX: 0, changeY: 0, clientX: 0, clientY: 0, distance: 0, distanceChange: 0, distanceX: 0, distanceY: 0, originalEvent: null, pageX: 0, pageY: 0, screenX: 0, screenY: 0, touch1: null, touch2: null, })); const pinchSubscription = this._touchMove$.pipe(filter((te) => { return te.touches.length === 2 && te.targetTouches.length === 2; }), map((te) => { return (previous) => { let touch1 = te.touches[0]; let touch2 = te.touches[1]; let minX = Math.min(touch1.clientX, touch2.clientX); let maxX = Math.max(touch1.clientX, touch2.clientX); let minY = Math.min(touch1.clientY, touch2.clientY); let maxY = Math.max(touch1.clientY, touch2.clientY); let centerClientX = minX + (maxX - minX) / 2; let centerClientY = minY + (maxY - minY) / 2; let centerPageX = centerClientX + touch1.pageX - touch1.clientX; let centerPageY = centerClientY + touch1.pageY - touch1.clientY; let centerScreenX = centerClientX + touch1.screenX - touch1.clientX; let centerScreenY = centerClientY + touch1.screenY - touch1.clientY; let distanceX = Math.abs(touch1.clientX - touch2.clientX); let distanceY = Math.abs(touch1.clientY - touch2.clientY); let distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); let distanceChange = distance - previous.distance; let changeX = distanceX - previous.distanceX; let changeY = distanceY - previous.distanceY; let current = { changeX: changeX, changeY: changeY, clientX: centerClientX, clientY: centerClientY, distance: distance, distanceChange: distanceChange, distanceX: distanceX, distanceY: distanceY, originalEvent: te, pageX: centerPageX, pageY: centerPageY, screenX: centerScreenX, screenY: centerScreenY, touch1: touch1, touch2: touch2, }; return current; }; })) .subscribe(this._pinchOperation$); subs.push(pinchSubscription); this._pinchChange$ = this._pinchStart$.pipe(switchMap(() => { return this._pinch$.pipe(skip(1), takeUntil(this._pinchEnd$)); })); } get active$() { return this._active$; } get activate$() { return this._activeSubject$; } get doubleTap$() { return this._doubleTap$; } get touchStart$() { return this._touchStart$; } get touchMove$() { return this._touchMove$; } get touchEnd$() { return this._touchEnd$; } get touchCancel$() { return this._touchCancel$; } get singleTouchDragStart$() { return this._singleTouchDragStart$; } get singleTouchDrag$() { return this._singleTouchDrag$; } get singleTouchDragEnd$() { return this._singleTouchDragEnd$; } get pinch$() { return this._pinchChange$; } get pinchStart$() { return this._pinchStart$; } get pinchEnd$() { return this._pinchEnd$; } dispose() { this._subscriptions.unsubscribe(); } } class ConfigurationService { constructor(options) { var _a, _b, _c, _d; const host = (_b = (_a = options === null || options === void 0 ? void 0 : options.url) === null || _a === void 0 ? void 0 : _a.exploreHost) !== null && _b !== void 0 ? _b : "www.mapillary.com"; const scheme = (_d = (_c = options === null || options === void 0 ? void 0 : options.url) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : "https"; const exploreUrl = `${scheme}://${host}`; this._exploreUrl$ = of(exploreUrl); const imageTiling = (options === null || options === void 0 ? void 0 : options.imageTiling) === false ? false : true; this._imageTiling$ = of(imageTiling); } get exploreUrl$() { return this._exploreUrl$; } get imageTiling$() { return this._imageTiling$; } } class Container { constructor(options, stateService, dom) { var _a; this._onWindowResize = () => { if (this._trackResize) { this.renderService.resize$.next(); } }; this._dom = dom !== null && dom !== void 0 ? dom : new DOM(); if (typeof options.container === "string") { this._container = this._dom.document .getElementById(options.container); if (!this._container) { throw new Error(`Container "${options.container}" not found.`); } } else if (options.container instanceof HTMLElement) { this._container = options.container; } else { throw new Error(`Invalid type: "container" must be ` + `a String or HTMLElement.`); } this._trackResize = options.trackResize === false ? false : true; this.id = (_a = this._container.id) !== null && _a !== void 0 ? _a : "mapillary-fallback-container-id"; this._container.classList .add("mapillary-viewer"); this._canvasContainer = this._dom .createElement("div", "mapillary-interactive", this._container); this._canvas = this._dom .createElement("canvas", "mapillary-canvas"); this._canvas.style.position = "absolute"; this._canvas.setAttribute("tabindex", "0"); // Add DOM container after canvas container to // render DOM elements on top of the interactive // canvas. this._domContainer = this._dom .createElement("div", "mapillary-dom", this._container); this.configurationService = new ConfigurationService(options); this.renderService = new RenderService(this._container, stateService.currentState$, options.renderMode); this.glRenderer = new GLRenderer(this._canvas, this._canvasContainer, this.renderService); this.domRenderer = new DOMRenderer(this._domContainer, this.renderService, stateService.currentState$); this.keyboardService = new KeyboardService(this._canvasContainer); this.mouseService = new MouseService(this._container, this._canvasContainer, this._domContainer, document); this.touchService = new TouchService(this._canvasContainer, this._domContainer); this.spriteService = new SpriteService(options.sprite); window.addEventListener("resize", this._onWindowResize, false); } get canvas() { return !!this._canvas.parentNode ? this._canvas : null; } get canvasContainer() { return this._canvasContainer; } get container() { return this._container; } get domContainer() { return this._domContainer; } remove() { window.removeEventListener("resize", this._onWindowResize, false); this.spriteService.dispose(); this.touchService.dispose(); this.mouseService.dispose(); this.glRenderer.remove(); this.domRenderer.remove(); this.renderService.dispose(); this._removeNode(this._canvasContainer); this._removeNode(this._domContainer); this._container.classList .remove("mapillary-viewer"); } _removeNode(node) { if (node.parentNode) { node.parentNode.removeChild(node); } } } class CacheService { constructor(_graphService, _stateService, _api) { this._graphService = _graphService; this._stateService = _stateService; this._api = _api; this._subscriptions = new SubscriptionHolder(); this._started = false; this._cellDepth = 1; } get started() { return this._started; } configure(configuration) { if (!configuration) { this._cellDepth = 1; return; } this._cellDepth = Math.max(1, Math.min(3, configuration.cellDepth)); } start() { if (this._started) { return; } const subs = this._subscriptions; subs.push(this._stateService.reference$ .pipe(withLatestFrom(this._stateService.currentState$, this._graphService.graphMode$), map(([, frame, mode]) => { const state = this._frameToState(frame); return this._makeKeepers(state, mode); }), switchMap((keepers) => { return this._graphService .uncache$(keepers.imageIds, keepers.cellIds, keepers.sequenceId); })) .subscribe(() => { })); subs.push(this._stateService.currentState$ .pipe(distinctUntilChanged(undefined, (frame) => { return frame.state.currentImage.id; }), map((frame) => { return this._frameToState(frame); }), bufferCount(1, 5), withLatestFrom(this._graphService.graphMode$), switchMap(([stateBuffer, graphMode]) => { const keepers = this._makeKeepers(stateBuffer[0], graphMode); return this._graphService .uncache$(keepers.imageIds, keepers.cellIds, keepers.sequenceId); })) .subscribe(() => { })); subs.push(this._graphService.graphMode$ .pipe(skip(1), withLatestFrom(this._stateService.currentState$), switchMap(([mode, frame]) => { return mode === GraphMode.Sequence ? this._keyToEdges(frame.state.currentImage.id, (image) => { return image.sequenceEdges$; }) : from(frame.state.trajectory .map((image) => { return image.id; }) .slice(frame.state.currentIndex)).pipe(mergeMap((key) => { return this._keyToEdges(key, (image) => { return image.spatialEdges$; }); }, 6)); })) .subscribe(() => { })); subs.push(this._graphService.dataAdded$ .pipe(withLatestFrom(this._stateService.currentId$), switchMap(([_, imageId]) => { return this._graphService.cacheImage$(imageId); })) .subscribe(() => { })); this._started = true; } stop() { if (!this._started) { return; } this._subscriptions.unsubscribe(); this._started = false; } _keyToEdges(key, imageToEdgeMap) { return this._graphService.cacheImage$(key).pipe(switchMap(imageToEdgeMap), first((status) => { return status.cached; }), timeout(15000), catchError((error) => { console.error(`Failed to cache edges (${key}).`, error); return empty(); })); } _frameToState(frame) { const state = frame.state; const trajectory = state.trajectory; const trajectoryIds = trajectory .map((n) => { return n.id; }); const sequenceId = trajectory[trajectory.length - 1].sequenceId; return { lngLat: state.currentImage.originalLngLat, sequenceId, trajectoryIds, }; } _makeKeepers(state, graphMode) { const imageIds = state.trajectoryIds.filter(id => !isNullImageId(id)); const lngLat = state.lngLat; const geometry = this._api.data.geometry; const cellId = geometry.lngLatToCellId(lngLat); const cellIds = connectedComponent(cellId, this._cellDepth, geometry); const sequenceId = graphMode === GraphMode.Sequence ? state.sequenceId : undefined; return { cellIds, imageIds, sequenceId }; } } class LoadingService { constructor() { this._loadersSubject$ = new Subject(); this._loaders$ = this._loadersSubject$.pipe(scan((loaders, loader) => { if (loader.task !== undefined) { loaders[loader.task] = loader.loading; } return loaders; }, {}), startWith({}), publishReplay(1), refCount()); } get loading$() { return this._loaders$.pipe(map((loaders) => { for (const key in loaders) { if (!loaders.hasOwnProperty(key)) { continue; } if (loaders[key]) { return true; } } return false; }), debounceTime(100), distinctUntilChanged()); } taskLoading$(task) { return this._loaders$.pipe(map((loaders) => { return !!loaders[task]; }), debounceTime(100), distinctUntilChanged()); } startLoading(task) { this._loadersSubject$.next({ loading: true, task: task }); } stopLoading(task) { this._loadersSubject$.next({ loading: false, task: task }); } } var PanMode; (function (PanMode) { PanMode[PanMode["Disabled"] = 0] = "Disabled"; PanMode[PanMode["Enabled"] = 1] = "Enabled"; PanMode[PanMode["Started"] = 2] = "Started"; })(PanMode || (PanMode = {})); class PanService { constructor(graphService, stateService, cameraFactory, enabled, graphCalculator, spatial, viewportCoords) { this._subscriptions = new SubscriptionHolder(); this._graphService = graphService; this._stateService = stateService; this._cameraFactory = cameraFactory; this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); this._mode = enabled !== false ? PanMode.Enabled : PanMode.Disabled; this._panImagesSubject$ = new Subject(); this._panImages$ = this._panImagesSubject$.pipe(startWith([]), publishReplay(1), refCount()); this._subscriptions.push(this._panImages$.subscribe()); } get panImages$() { return this._panImages$; } dispose() { this.stop(); if (this._panImagesSubscription != null) { this._panImagesSubscription.unsubscribe(); } this._subscriptions.unsubscribe(); } enable() { if (this._mode !== PanMode.Disabled) { return; } this._mode = PanMode.Enabled; this.start(); } disable() { if (this._mode === PanMode.Disabled) { return; } this.stop(); this._mode = PanMode.Disabled; } start() { if (this._mode !== PanMode.Enabled) { return; } const panImages$ = this._stateService.currentImage$.pipe(filter((image) => { return !isNullImageId(image.id); }), switchMap((current) => { if (!current.merged || isSpherical(current.cameraType)) { return of([]); } const current$ = of(current); const bounds = this._graphCalculator.boundingBoxCorners(current.lngLat, 20); const adjacent$ = this._graphService .cacheBoundingBox$(bounds[0], bounds[1]).pipe(catchError((error) => { console.error(`Failed to cache periphery bounding box (${current.id})`, error); return empty(); }), map((images) => { if (isSpherical(current.cameraType)) { return []; } const potential = []; for (const image of images) { if (image.id === current.id) { continue; } if (image.mergeId !== current.mergeId) { continue; } if (isSpherical(image.cameraType)) { continue; } if (this._distance(image, current) > 4) { continue; } potential.push(image); } return potential; })); return combineLatest(current$, adjacent$).pipe(withLatestFrom(this._stateService.reference$), map(([[cn, adjacent], reference]) => { const currentDirection = this._spatial.viewingDirection(cn.rotation); const currentTranslation = computeTranslation({ lat: cn.lngLat.lat, lng: cn.lngLat.lng, alt: cn.computedAltitude }, cn.rotation, reference); const currentTransform = this._createTransform(cn, currentTranslation); const currentAzimuthal = this._spatial.wrap(this._spatial.azimuthal(currentDirection.toArray(), currentTransform.upVector().toArray()), 0, 2 * Math.PI); const currentProjectedPoints = this._computeProjectedPoints(currentTransform); const currentHFov = this._computeHorizontalFov(currentProjectedPoints) / 180 * Math.PI; const preferredOverlap = Math.PI / 8; let left = undefined; let right = undefined; for (const a of adjacent) { const translation = computeTranslation({ lat: a.lngLat.lat, lng: a.lngLat.lng, alt: a.computedAltitude }, a.rotation, reference); const transform = this._createTransform(a, translation); const projectedPoints = this._computeProjectedPoints(transform); const hFov = this._computeHorizontalFov(projectedPoints) / 180 * Math.PI; const direction = this._spatial.viewingDirection(a.rotation); const azimuthal = this._spatial.wrap(this._spatial.azimuthal(direction.toArray(), transform.upVector().toArray()), 0, 2 * Math.PI); const directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); let overlap = Number.NEGATIVE_INFINITY; if (directionChange > 0) { if (currentAzimuthal > azimuthal) { overlap = currentAzimuthal - 2 * Math.PI + currentHFov / 2 - (azimuthal - hFov / 2); } else { overlap = currentAzimuthal + currentHFov / 2 - (azimuthal - hFov / 2); } } else { if (currentAzimuthal < azimuthal) { overlap = azimuthal + hFov / 2 - (currentAzimuthal + 2 * Math.PI - currentHFov / 2); } else { overlap = azimuthal + hFov / 2 - (currentAzimuthal - currentHFov / 2); } } const nonOverlap = Math.abs(hFov - overlap); const distanceCost = this._distance(a, cn); const timeCost = Math.min(this._timeDifference(a, cn), 4); const overlapCost = 20 * Math.abs(overlap - preferredOverlap); const fovCost = Math.min(5, 1 / Math.min(hFov / currentHFov, 1)); const nonOverlapCost = overlap > 0 ? -2 * nonOverlap : 0; const cost = distanceCost + timeCost + overlapCost + fovCost + nonOverlapCost; if (overlap > 0 && overlap < 0.5 * currentHFov && overlap < 0.5 * hFov && nonOverlap > 0.5 * currentHFov) { if (directionChange > 0) { if (!left) { left = [cost, a, transform, hFov]; } else { if (cost < left[0]) { left = [cost, a, transform, hFov]; } } } else { if (!right) { right = [cost, a, transform, hFov]; } else { if (cost < right[0]) { right = [cost, a, transform, hFov]; } } } } } const panImagess = []; if (!!left) { panImagess.push([left[1], left[2], left[3]]); } if (!!right) { panImagess.push([right[1], right[2], right[3]]); } return panImagess; }), startWith([])); })); const traversing$ = this._stateService.state$.pipe(map((state) => { return state === State.Traversing || state === State.GravityTraversing; }), distinctUntilChanged()); const imagesAhead$ = this._stateService.currentState$.pipe(map((frame) => { return frame.state.imagesAhead > 0; }), distinctUntilChanged()); this._panImagesSubscription = combineLatest(traversing$, imagesAhead$) .pipe(switchMap(([traversing, imagesAhead]) => { return traversing && !imagesAhead ? panImages$ : of([]); })) .subscribe((panImages) => { this._panImagesSubject$.next(panImages); }); this._mode = PanMode.Started; } stop() { if (this._mode !== PanMode.Started) { return; } this._panImagesSubscription.unsubscribe(); this._panImagesSubject$.next([]); this._mode = PanMode.Enabled; } _distance(image, reference) { const [x, y, z] = geodeticToEnu(image.lngLat.lng, image.lngLat.lat, image.computedAltitude, reference.lngLat.lng, reference.lngLat.lat, reference.computedAltitude); return Math.sqrt(x * x + y * y + z * z); } _timeDifference(image, reference) { const milliSecond = (1000 * 60 * 60 * 24 * 30); return Math.abs(image.capturedAt - reference.capturedAt) / milliSecond; } _createTransform(image, translation) { return new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.assetsCached ? image.image : undefined, image.assetsCached ? image.camera : this._cameraFactory.makeCamera(image.cameraType, image.cameraParameters)); } _computeProjectedPoints(transform) { const vertices = [[1, 0]]; const directions = [[0, 0.5]]; const pointsPerLine = 20; return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords) .map(([x, y]) => [Math.abs(x), Math.abs(y)]); } _computeHorizontalFov(projectedPoints) { const fovs = projectedPoints .map((projectedPoint) => { return this._coordToFov(projectedPoint[0]); }); const fov = Math.min(...fovs); return fov; } _coordToFov(x) { return 2 * Math.atan(x) * 180 / Math.PI; } } /** * @class API * * @classdesc Provides methods for access to the API. */ class APIWrapper { constructor(_data) { this._data = _data; } get data() { return this._data; } getCoreImages$(cellId) { return this._wrap$(this._data.getCoreImages(cellId)); } getImages$(imageIds) { return this._wrap$(this._data.getImages(imageIds)); } getImageTiles$(tiles) { return this._wrap$(this._data.getImageTiles(tiles)); } getSequence$(sequenceId) { return this._wrap$(this._data.getSequence(sequenceId)); } getSpatialImages$(imageIds) { return this._wrap$(this._data.getSpatialImages(imageIds)); } setAccessToken(accessToken) { this._data.setAccessToken(accessToken); } setDataProvider(data) { this._data = data; } _wrap$(promise) { return Observable.create((subscriber) => { promise.then((value) => { subscriber.next(value); subscriber.complete(); }, (error) => { subscriber.error(error); }); }); } } const EPSILON = 1e-8; function bearing$1(point, parameters, uniforms) { const [x, y] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Transformation const [xd, yd] = [x / focal, y / focal]; // Undistortion const dTheta = Math.sqrt(xd * xd + yd * yd); const d = distortionFromDistortedRadius(dTheta, k1, k2, radialPeak); const theta = dTheta / d; // Unprojection const r = Math.sin(theta); const denomTheta = dTheta > EPSILON ? 1 / dTheta : 1; const xb = r * xd * denomTheta; const yb = r * yd * denomTheta; const zb = Math.cos(theta); return [xb, yb, zb]; } function project$1(point, parameters, uniforms) { const [x, y, z] = point; const { focal, k1, k2 } = parameters; const radialPeak = uniforms.radialPeak; // Projection const r = Math.sqrt(x * x + y * y); let theta = Math.atan2(r, z); if (theta > radialPeak) { theta = radialPeak; } const xp = theta / r * x; const yp = theta / r * y; // Distortion const theta2 = Math.pow(theta, 2); const distortion = 1.0 + theta2 * (k1 + theta2 * k2); const xd = xp * distortion; const yd = yp * distortion; // Transformation const xt = focal * xd; const yt = focal * yd; return [xt, yt]; } const FISHEYE_CAMERA_TYPE = "fisheye"; const FISHEYE_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing, Parameters parameters, Uniforms uniforms) { float x = bearing.x; float y = bearing.y; float z = bearing.z; float focal = parameters.focal; float k1 = parameters.k1; float k2 = parameters.k2; float radialPeak = uniforms.radialPeak; // Projection float r = sqrt(x * x + y * y); float theta = atan(r, z); if (theta > radialPeak) { theta = radialPeak; } float xp = theta / r * x; float yp = theta / r * y; // Distortion float theta2 = theta * theta; float distortion = 1.0 + theta2 * (k1 + theta2 * k2); float xd = xp * distortion; float yd = yp * distortion; // Transformation float xt = focal * xd; float yt = focal * yd; return vec2(xt, yt); } `; class FisheyeCamera extends Camera$1 { constructor(parameters) { super(FISHEYE_CAMERA_TYPE, FISHEYE_PROJECT_FUNCTION); const [focal, k1, k2] = parameters; this.parameters.focal = focal; this.parameters.k1 = k1; this.parameters.k2 = k2; const radialPeak = makeRadialPeak(k1, k2); this.uniforms.radialPeak = radialPeak; } bearingFromSfm(point) { return bearing$1(point, this.parameters, this.uniforms); } projectToSfm(point) { return project$1(point, this.parameters, this.uniforms); } } function bearing(point) { const [x, y] = point; // Unprojection const lng = x * 2 * Math.PI; const lat = -y * 2 * Math.PI; const xb = Math.cos(lat) * Math.sin(lng); const yb = -Math.sin(lat); const zb = Math.cos(lat) * Math.cos(lng); return [xb, yb, zb]; } function project(point) { const [x, y, z] = point; // Projection const lng = Math.atan2(x, z); const lat = Math.atan2(-y, Math.sqrt(x * x + z * z)); const xp = lng / (2 * Math.PI); const yp = -lat / (2 * Math.PI); return [xp, yp]; } const SPHERICAL_CAMERA_TYPE = "spherical"; const SPHERICAL_PROJECT_FUNCTION = /* glsl */ ` vec2 projectToSfm(vec3 bearing) { float x = bearing.x; float y = bearing.y; float z = bearing.z; // Projection float lat = -asin(y); float lng = atan(x, z); float xn = lng / PI2; float yn = -lat / PI2; return vec2(xn, yn); } `; class SphericalCamera extends Camera$1 { constructor() { super(SPHERICAL_CAMERA_TYPE, SPHERICAL_PROJECT_FUNCTION); } bearingFromSfm(point) { return bearing(point); } projectToSfm(point) { return project(point); } } const vertex = /* glsl */ ` #include #include void main() { #include #include } `; const fragment = /* glsl */ ` #include #include #include #include #include #expand #expand #expand void main() { #include #expand #include #include } `; // tslint:disable-next-line:variable-name const Shader = { texture: { fragment: fragment, vertex: vertex, }, }; class ProjectionService { constructor() { this._cameraFactory = new Map(); this.registerCamera(FISHEYE_CAMERA_TYPE, FisheyeCamera); this.registerCamera(PERSPECTIVE_CAMERA_TYPE, PerspectiveCamera); this.registerCamera(SPHERICAL_CAMERA_TYPE, SphericalCamera); this._shader = Shader.texture; this._shaderChanged$ = new Subject(); this._shader$ = this._shaderChanged$.pipe(startWith(this._shader), publishReplay(1), refCount()); this._shaderSubscription = this._shader$.subscribe(); } get shader$() { return this._shader$; } dispose() { this._shaderSubscription.unsubscribe(); } hasCamera(type) { return this._cameraFactory.has(type); } getShader() { return this._shader; } makeCamera(type, parameters) { if (!this.hasCamera(type)) { return new PerspectiveCamera([0.85, 0, 0]); } return new (this._cameraFactory.get(type))(parameters); } registerCamera(type, ctor) { this._cameraFactory.set(type, ctor); } setShader(shader) { this._shader = shader ? { fragment: `${shader.fragment}`, vertex: `${shader.vertex}`, } : Shader.texture; this._shaderChanged$.next(this._shader); } } /** * @class GraphService * * @classdesc Represents a service for graph operations. */ class GraphService { /** * Create a new graph service instance. * * @param {Graph} graph - Graph instance to be operated on. */ constructor(graph, cameraFactory) { this._dataAdded$ = new Subject(); this._dataReset$ = new Subject(); this._subscriptions = new SubscriptionHolder(); this._onDataAdded = (event) => { this._graph$ .pipe(first(), mergeMap(graph => { return graph.updateCells$(event.cellIds).pipe(tap(() => { graph.resetSpatialEdges(); })); })) .subscribe(cellId => { this._dataAdded$.next(cellId); }); }; this._cameraFactory = cameraFactory !== null && cameraFactory !== void 0 ? cameraFactory : new ProjectionService(); const subs = this._subscriptions; this._graph$ = concat(of(graph), graph.changed$).pipe(publishReplay(1), refCount()); subs.push(this._graph$.subscribe(() => { })); this._graphMode = GraphMode.Spatial; this._graphModeSubject$ = new Subject(); this._graphMode$ = this._graphModeSubject$.pipe(startWith(this._graphMode), publishReplay(1), refCount()); subs.push(this._graphMode$.subscribe(() => { })); this._firstGraphSubjects$ = []; this._initializeCacheSubscriptions = []; this._sequenceSubscriptions = []; this._spatialSubscriptions = []; graph.api.data.on("datacreate", this._onDataAdded); } /** * Get dataAdded$. * * @returns {Observable} Observable emitting * a cell id every time data has been added to a cell. */ get dataAdded$() { return this._dataAdded$; } get dataReset$() { return this._dataReset$; } /** * Get filter observable. * * @desciption Emits the filter every time it has changed. * * @returns {Observable} Observable * emitting the filter function every time it is set. */ get filter$() { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.filter$; })); } /** * Get graph mode observable. * * @description Emits the current graph mode. * * @returns {Observable} Observable * emitting the current graph mode when it changes. */ get graphMode$() { return this._graphMode$; } /** * Cache full images in a bounding box. * * @description When called, the full properties of * the image are retrieved. The image cache is not initialized * for any new images retrieved and the image assets are not * retrieved, {@link cacheImage$} needs to be called for caching * assets. * * @param {LngLat} sw - South west corner of bounding box. * @param {LngLat} ne - North east corner of bounding box. * @return {Observable>} Observable emitting a single item, * the images of the bounding box, when they have all been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheBoundingBox$(sw, ne) { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.cacheBoundingBox$(sw, ne); })); } /** * Cache full images in a cell. * * @description When called, the full properties of * the image are retrieved. The image cache is not initialized * for any new images retrieved and the image assets are not * retrieved, {@link cacheImage$} needs to be called for caching * assets. * * @param {string} cellId - Id of the cell. * @return {Observable>} Observable emitting a single item, * the images of the cell, when they have all been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheCell$(cellId) { return this._graph$.pipe(first(), mergeMap((graph) => { return graph.cacheCell$(cellId); })); } /** * Cache a image in the graph and retrieve it. * * @description When called, the full properties of * the image are retrieved and the image cache is initialized. * After that the image assets are cached and the image * is emitted to the observable when. * In parallel to caching the image assets, the sequence and * spatial edges of the image are cached. For this, the sequence * of the image and the required tiles and spatial images are * retrieved. The sequence and spatial edges may be set before * or after the image is returned. * * @param {string} id - Id of the image to cache. * @return {Observable} Observable emitting a single item, * the image, when it has been retrieved and its assets are cached. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheImage$(id) { const firstGraphSubject$ = new Subject(); this._firstGraphSubjects$.push(firstGraphSubject$); const firstGraph$ = firstGraphSubject$.pipe(publishReplay(1), refCount()); const image$ = firstGraph$.pipe(map((graph) => { return graph.getNode(id); }), mergeMap((image) => { return image.assetsCached ? of(image) : image.cacheAssets$(this._cameraFactory); }), publishReplay(1), refCount()); image$.subscribe(undefined, (error) => { console.error(`Failed to cache image (${id}).`, error); }); let initializeCacheSubscription; initializeCacheSubscription = this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingFull(id) || !graph.hasNode(id)) { return graph.cacheFull$(id); } if (graph.isCachingFill(id) || !graph.getNode(id).complete) { return graph.cacheFill$(id); } return of(graph); }), tap((graph) => { if (!graph.hasNode(id)) { throw new GraphMapillaryError(`Failed to cache image (${id})`); } if (!graph.hasInitializedCache(id)) { graph.initializeCache(id); } }), finalize(() => { if (initializeCacheSubscription == null) { return; } this._removeFromArray(initializeCacheSubscription, this._initializeCacheSubscriptions); this._removeFromArray(firstGraphSubject$, this._firstGraphSubjects$); })) .subscribe((graph) => { firstGraphSubject$.next(graph); firstGraphSubject$.complete(); }, (error) => { firstGraphSubject$.error(error); }); if (!initializeCacheSubscription.closed) { this._initializeCacheSubscriptions.push(initializeCacheSubscription); } const graphSequence$ = firstGraph$.pipe(catchError(() => { return empty(); }), mergeMap((graph) => { if (graph.isCachingNodeSequence(id) || !graph.hasNodeSequence(id)) { return graph.cacheNodeSequence$(id); } return of(graph); }), publishReplay(1), refCount()); let sequenceSubscription; sequenceSubscription = graphSequence$.pipe(tap((graph) => { if (!graph.getNode(id).sequenceEdges.cached) { graph.cacheSequenceEdges(id); } }), finalize(() => { if (sequenceSubscription == null) { return; } this._removeFromArray(sequenceSubscription, this._sequenceSubscriptions); })) .subscribe(() => { return; }, (error) => { console.error(`Failed to cache sequence edges (${id}).`, error); }); if (!sequenceSubscription.closed) { this._sequenceSubscriptions.push(sequenceSubscription); } if (this._graphMode === GraphMode.Spatial) { let spatialSubscription; spatialSubscription = firstGraph$.pipe(catchError(() => { return empty(); }), expand((graph) => { if (graph.hasTiles(id)) { return empty(); } return from(graph.cacheTiles$(id)).pipe(mergeMap((graph$) => { return graph$.pipe(mergeMap((g) => { if (g.isCachingTiles(id)) { return empty(); } return of(g); }), catchError((error) => { console.error(`Failed to cache tile data (${id}).`, error); return empty(); })); })); }), takeLast(1), mergeMap((graph) => { if (graph.hasSpatialArea(id)) { return of(graph); } return from(graph.cacheSpatialArea$(id)).pipe(mergeMap((graph$) => { return graph$.pipe(catchError((error) => { console.error(`Failed to cache spatial images (${id}).`, error); return empty(); })); })); }), takeLast(1), mergeMap((graph) => { return graph.hasNodeSequence(id) ? of(graph) : graph.cacheNodeSequence$(id); }), tap((graph) => { if (!graph.getNode(id).spatialEdges.cached) { graph.cacheSpatialEdges(id); } }), finalize(() => { if (spatialSubscription == null) { return; } this._removeFromArray(spatialSubscription, this._spatialSubscriptions); })) .subscribe(() => { return; }, (error) => { const message = `Failed to cache spatial edges (${id}).`; console.error(message, error); }); if (!spatialSubscription.closed) { this._spatialSubscriptions.push(spatialSubscription); } } return image$.pipe(first((image) => { return image.assetsCached; })); } /** * Cache a sequence in the graph and retrieve it. * * @param {string} sequenceId - Sequence id. * @returns {Observable} Observable emitting a single item, * the sequence, when it has been retrieved and its assets are cached. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheSequence$(sequenceId) { return this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { return graph.cacheSequence$(sequenceId); } return of(graph); }), map((graph) => { return graph.getSequence(sequenceId); })); } /** * Cache a sequence and its images in the graph and retrieve the sequence. * * @description Caches a sequence and its assets are cached and * retrieves all images belonging to the sequence. The image assets * or edges will not be cached. * * @param {string} sequenceId - Sequence id. * @param {string} referenceImageId - Id of image to use as reference * for optimized caching. * @returns {Observable} Observable emitting a single item, * the sequence, when it has been retrieved, its assets are cached and * all images belonging to the sequence has been retrieved. * @throws {Error} Propagates any IO image caching errors to the caller. */ cacheSequenceImages$(sequenceId, referenceImageId) { return this._graph$.pipe(first(), mergeMap((graph) => { if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { return graph.cacheSequence$(sequenceId); } return of(graph); }), mergeMap((graph) => { if (graph.isCachingSequenceNodes(sequenceId) || !graph.hasSequenceNodes(sequenceId)) { return graph.cacheSequenceNodes$(sequenceId, referenceImageId); } return of(graph); }), map((graph) => { return graph.getSequence(sequenceId); })); } /** * Dispose the graph service and its children. */ dispose() { this._graph$ .pipe(first()) .subscribe((graph) => { graph.unsubscribe(); }); this._subscriptions.unsubscribe(); } /** * Set a spatial edge filter on the graph. * * @description Resets the spatial edges of all cached images. * * @param {FilterExpression} filter - Filter expression to be applied. * @return {Observable} Observable emitting a single item, * the graph, when the spatial edges have been reset. */ setFilter$(filter) { this._resetSubscriptions(this._spatialSubscriptions); return this._graph$.pipe(first(), tap((graph) => { graph.resetSpatialEdges(); graph.setFilter(filter); }), map(() => { return undefined; })); } /** * Set the graph mode. * * @description If graph mode is set to spatial, caching * is performed with emphasis on spatial edges. If graph * mode is set to sequence no tile data is requested and * no spatial edges are computed. * * When setting graph mode to sequence all spatial * subscriptions are aborted. * * @param {GraphMode} mode - Graph mode to set. */ setGraphMode(mode) { if (this._graphMode === mode) { return; } if (mode === GraphMode.Sequence) { this._resetSubscriptions(this._spatialSubscriptions); } this._graphMode = mode; this._graphModeSubject$.next(this._graphMode); } /** * Reset the graph. * * @description Resets the graph but keeps the images of the * supplied ids. * * @param {Array} keepIds - Ids of images to keep in graph. * @return {Observable} Observable emitting a single item, * the graph, when it has been reset. */ reset$(keepIds) { this._abortSubjects(this._firstGraphSubjects$); this._resetSubscriptions(this._initializeCacheSubscriptions); this._resetSubscriptions(this._sequenceSubscriptions); this._resetSubscriptions(this._spatialSubscriptions); return this._graph$.pipe(first(), tap((graph) => { graph.reset(keepIds); this._dataReset$.next(); }), map(() => { return undefined; })); } /** * Uncache the graph. * * @description Uncaches the graph by removing tiles, images and * sequences. Keeps the images of the supplied ids and the tiles * related to those images. * * @param {Array} keepIds - Ids of images to keep in graph. * @param {Array} keepCellIds - Ids of cells to keep in graph. * @param {string} keepSequenceId - Optional id of sequence * for which the belonging images should not be disposed or * removed from the graph. These images may still be uncached if * not specified in keep ids param. * @return {Observable} Observable emitting a single item, * the graph, when the graph has been uncached. */ uncache$(keepIds, keepCellIds, keepSequenceId) { return this._graph$.pipe(first(), tap((graph) => { graph.uncache(keepIds, keepCellIds, keepSequenceId); }), map(() => { return undefined; })); } _abortSubjects(subjects) { for (const subject of subjects.slice()) { this._removeFromArray(subject, subjects); subject.error(new Error("Cache image request was aborted.")); } } _removeFromArray(object, objects) { const index = objects.indexOf(object); if (index !== -1) { objects.splice(index, 1); } } _resetSubscriptions(subscriptions) { for (const subscription of subscriptions.slice()) { this._removeFromArray(subscription, subscriptions); if (!subscription.closed) { subscription.unsubscribe(); } } } } class FrameGenerator { constructor(root) { if (root.requestAnimationFrame) { this._cancelAnimationFrame = root.cancelAnimationFrame.bind(root); this._requestAnimationFrame = root.requestAnimationFrame.bind(root); } else if (root.mozRequestAnimationFrame) { this._cancelAnimationFrame = root.mozCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.mozRequestAnimationFrame.bind(root); } else if (root.webkitRequestAnimationFrame) { this._cancelAnimationFrame = root.webkitCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.webkitRequestAnimationFrame.bind(root); } else if (root.msRequestAnimationFrame) { this._cancelAnimationFrame = root.msCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.msRequestAnimationFrame.bind(root); } else if (root.oRequestAnimationFrame) { this._cancelAnimationFrame = root.oCancelAnimationFrame.bind(root); this._requestAnimationFrame = root.oRequestAnimationFrame.bind(root); } else { this._cancelAnimationFrame = root.clearTimeout.bind(root); this._requestAnimationFrame = (cb) => { return root.setTimeout(cb, 1000 / 60); }; } } get cancelAnimationFrame() { return this._cancelAnimationFrame; } get requestAnimationFrame() { return this._requestAnimationFrame; } } class StateBase { constructor(state) { this._spatial = new Spatial(); this._referenceThreshold = 250; this._transitionThreshold = 62.5; this._transitionMode = state.transitionMode; this._reference = state.reference; this._alpha = state.alpha; this._stateTransitionAlpha = 0; this._camera = state.camera.clone(); this._zoom = state.zoom; this._currentIndex = state.currentIndex; this._trajectory = state.trajectory.slice(); this._trajectoryTransforms = []; this._trajectoryCameras = []; for (let image of this._trajectory) { let translation = this._imageToTranslation(image, this._reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.push(transform); this._trajectoryCameras.push(new Camera(transform)); } this._currentImage = this._trajectory.length > 0 ? this._trajectory[this._currentIndex] : null; this._previousImage = this._trajectory.length > 1 && this.currentIndex > 0 ? this._trajectory[this._currentIndex - 1] : null; this._currentCamera = this._trajectoryCameras.length > 0 ? this._trajectoryCameras[this._currentIndex].clone() : new Camera(); this._previousCamera = this._trajectoryCameras.length > 1 && this.currentIndex > 0 ? this._trajectoryCameras[this._currentIndex - 1].clone() : this._currentCamera.clone(); } get reference() { return this._reference; } get alpha() { return this._getAlpha(); } get stateTransitionAlpha() { return this._getStateTransitionAlpha(); } get camera() { return this._camera; } get zoom() { return this._zoom; } get trajectory() { return this._trajectory; } get currentIndex() { return this._currentIndex; } get currentImage() { return this._currentImage; } get previousImage() { return this._previousImage; } get currentCamera() { return this._currentCamera; } get previousCamera() { return this._previousCamera; } get currentTransform() { return this._trajectoryTransforms.length > 0 ? this._trajectoryTransforms[this.currentIndex] : null; } get previousTransform() { return this._trajectoryTransforms.length > 1 && this.currentIndex > 0 ? this._trajectoryTransforms[this.currentIndex - 1] : null; } get motionless() { return this._motionless; } get transitionMode() { return this._transitionMode; } move(delta) { } moveTo(position) { } rotate(delta) { } rotateUnbounded(delta) { } rotateWithoutInertia(delta) { } rotateBasic(basicRotation) { } rotateBasicUnbounded(basicRotation) { } rotateBasicWithoutInertia(basicRotation) { } rotateToBasic(basic) { } setSpeed(speed) { } zoomIn(delta, reference) { } update(delta) { } setCenter(center) { } setZoom(zoom) { } dolly(delta) { } orbit(rotation) { } setViewMatrix(matrix) { } truck(direction) { } append(images) { if (images.length < 1) { throw Error("Trajectory can not be empty"); } if (this._currentIndex < 0) { this.set(images); } else { this._trajectory = this._trajectory.concat(images); this._appendToTrajectories(images); } } prepend(images) { if (images.length < 1) { throw Error("Trajectory can not be empty"); } this._trajectory = images.slice().concat(this._trajectory); this._currentIndex += images.length; this._setCurrentImage(); let referenceReset = this._setReference(); if (referenceReset) { this._setTrajectories(); } else { this._prependToTrajectories(images); } this._setCurrentCamera(); } remove(n) { if (n < 0) { throw Error("n must be a positive integer"); } if (this._currentIndex - 1 < n) { throw Error("Current and previous images can not be removed"); } for (let i = 0; i < n; i++) { this._trajectory.shift(); this._trajectoryTransforms.shift(); this._trajectoryCameras.shift(); this._currentIndex--; } this._setCurrentImage(); } clearPrior() { if (this._currentIndex > 0) { this.remove(this._currentIndex - 1); } } clear() { this.cut(); if (this._currentIndex > 0) { this.remove(this._currentIndex - 1); } } cut() { while (this._trajectory.length - 1 > this._currentIndex) { this._trajectory.pop(); this._trajectoryTransforms.pop(); this._trajectoryCameras.pop(); } } set(images) { this._setTrajectory(images); this._setCurrentImage(); this._setReference(); this._setTrajectories(); this._setCurrentCamera(); } getCenter() { return this._currentImage != null ? this.currentTransform.projectBasic(this._camera.lookat.toArray()) : [0.5, 0.5]; } setTransitionMode(mode) { this._transitionMode = mode; } _getAlpha() { return 1; } _getStateTransitionAlpha() { return 1; } _setCurrent() { this._setCurrentImage(); let referenceReset = this._setReference(); if (referenceReset) { this._setTrajectories(); } this._setCurrentCamera(); } _setCurrentCamera() { this._currentCamera = this._trajectoryCameras[this._currentIndex].clone(); this._previousCamera = this._currentIndex > 0 ? this._trajectoryCameras[this._currentIndex - 1].clone() : this._currentCamera.clone(); } _motionlessTransition() { let imagesSet = this._currentImage != null && this._previousImage != null; return imagesSet && (this._transitionMode === exports.TransitionMode.Instantaneous || !(this._currentImage.merged && this._previousImage.merged && this._withinOriginalDistance() && this._sameConnectedComponent())); } _setReference() { const { currentImage, previousImage, reference } = this; const referenceDistance = this._spatial.distanceFromLngLat(currentImage.lngLat.lng, currentImage.lngLat.lat, reference.lng, reference.lat); // do not reset reference if image is within threshold distance if (referenceDistance < this._referenceThreshold) { return false; } if (previousImage != null) { const transitionDistance = this._spatial.distanceFromLngLat(currentImage.lngLat.lng, currentImage.lngLat.lat, previousImage.lngLat.lng, previousImage.lngLat.lat); if (transitionDistance < this._transitionThreshold) { return false; } } // do not reset reference if previous image exist and // transition is with motion if (previousImage != null && !this._motionlessTransition()) { return false; } this._reference.lat = currentImage.lngLat.lat; this._reference.lng = currentImage.lngLat.lng; this._reference.alt = currentImage.computedAltitude; return true; } _setCurrentImage() { this._currentImage = this._trajectory.length > 0 ? this._trajectory[this._currentIndex] : null; this._previousImage = this._currentIndex > 0 ? this._trajectory[this._currentIndex - 1] : null; } _setTrajectory(images) { if (images.length < 1) { throw new ArgumentMapillaryError("Trajectory can not be empty"); } if (this._currentImage != null) { this._trajectory = [this._currentImage].concat(images); this._currentIndex = 1; } else { this._trajectory = images.slice(); this._currentIndex = 0; } } _setTrajectories() { this._trajectoryTransforms.length = 0; this._trajectoryCameras.length = 0; this._appendToTrajectories(this._trajectory); } _appendToTrajectories(images) { for (let image of images) { if (!image.assetsCached) { throw new ArgumentMapillaryError("Assets must be cached when image is added to trajectory"); } let translation = this._imageToTranslation(image, this.reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.push(transform); this._trajectoryCameras.push(new Camera(transform)); } } _prependToTrajectories(images) { for (let image of images.reverse()) { if (!image.assetsCached) { throw new ArgumentMapillaryError("Assets must be cached when added to trajectory"); } let translation = this._imageToTranslation(image, this.reference); let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, image.camera); this._trajectoryTransforms.unshift(transform); this._trajectoryCameras.unshift(new Camera(transform)); } } _imageToTranslation(image, reference) { return computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); } _sameConnectedComponent() { let current = this._currentImage; let previous = this._previousImage; return !!current && !!previous && current.mergeId === previous.mergeId; } _withinOriginalDistance() { let current = this._currentImage; let previous = this._previousImage; if (!current || !previous) { return true; } // 50 km/h moves 28m in 2s let distance = this._spatial.distanceFromLngLat(current.originalLngLat.lng, current.originalLngLat.lat, previous.originalLngLat.lng, previous.originalLngLat.lat); return distance < 25; } } class CustomState extends StateBase { constructor(state) { super(state); } setViewMatrix(viewMatrix) { const viewMatrixInverse = new Matrix4() .fromArray(viewMatrix) .invert(); const me = viewMatrixInverse.elements; const eye = new Vector3(me[12], me[13], me[14]); const forward = new Vector3(-me[8], -me[9], -me[10]); const up = new Vector3(me[4], me[5], me[6]); const camera = this._camera; camera.position.copy(eye); camera.lookat.copy(eye .clone() .add(forward)); camera.up.copy(up); const focal = 0.5 / Math.tan(Math.PI / 3); camera.focal = focal; } } class EarthState extends StateBase { constructor(state) { super(state); this._transition = 0; const eye = this._camera.position.clone(); const forward = this._camera.lookat .clone() .sub(eye) .normalize(); const xy = Math.sqrt(forward.x * forward.x + forward.y * forward.y); const angle = Math.atan2(forward.z, xy); const lookat = new Vector3(); if (angle > -Math.PI / 45) { lookat.copy(eye); eye.add(new Vector3(forward.x, forward.y, 0) .multiplyScalar(-50)); eye.z = 30; } else { // Target a point on invented ground and keep forward direction const l0 = eye.clone(); const n = new Vector3(0, 0, 1); const p0 = new Vector3(0, 0, -2); const d = new Vector3().subVectors(p0, l0).dot(n) / forward.dot(n); const maxDistance = 10000; const intersection = l0 .clone() .add(forward. clone() .multiplyScalar(Math.min(maxDistance, d))); lookat.copy(intersection); const t = eye .clone() .sub(intersection) .normalize(); eye.copy(intersection.add(t.multiplyScalar(Math.max(50, t.length())))); } const eye1 = this._camera.position.clone(); const lookat1 = eye1.clone().add(forward.clone().normalize().multiplyScalar(10)); const up1 = this._camera.up.clone(); const eye0 = lookat1.clone(); const lookat0 = eye0.clone().add(forward.clone().normalize().multiplyScalar(10)); const up0 = up1.clone(); const eye2 = eye.clone(); const lookat2 = lookat.clone(); const up2 = new Vector3(0, 0, 1); const eye3 = eye.clone().add(lookat2.clone().sub(eye2).normalize().multiplyScalar(-10)); const lookat3 = lookat2.clone(); const up3 = up2.clone(); this._curveE = new CatmullRomCurve3([eye0, eye1, eye2, eye3]); this._curveL = new CatmullRomCurve3([lookat0, lookat1, lookat2, lookat3]); this._curveU = new CatmullRomCurve3([up0, up1, up2, up3]); this._zoom0 = this._zoom; this._zoom1 = 0; this._camera.focal = 0.5 / Math.tan(Math.PI / 4); } get _isTransitioning() { return this._transition < 1; } dolly(delta) { if (this._isTransitioning) { return; } const camera = this._camera; const offset = camera.position .clone() .sub(camera.lookat); const length = offset.length(); const scaled = length * Math.pow(2, -delta); const clipped = Math.max(1, Math.min(scaled, 4000)); offset.normalize(); offset.multiplyScalar(clipped); camera.position .copy(camera.lookat) .add(offset); } orbit(rotation) { if (this._isTransitioning) { return; } const camera = this._camera; const q = new Quaternion() .setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); const qInverse = q .clone() .invert(); const offset = camera.position .clone() .sub(camera.lookat); offset.applyQuaternion(q); const length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += rotation.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += rotation.theta; const threshold = Math.PI / 36; theta = Math.max(threshold, Math.min(Math.PI / 2 - threshold, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); camera.position .copy(camera.lookat) .add(offset.multiplyScalar(length)); } truck(direction) { if (this._isTransitioning) { return; } const camera = this._camera; camera.position .add(new Vector3().fromArray(direction)); camera.lookat .add(new Vector3().fromArray(direction)); } update(delta) { if (!this._isTransitioning) { return; } this._transition = Math.min(this._transition + 2 * delta / 3, 1); const sta = MathUtils.smootherstep(this._transition, 0, 1); const t = (sta + 1) / 3; const eye = this._curveE.getPoint(t); const lookat = this._curveL.getPoint(t); const up = this._curveU.getPoint(t); this._camera.position.copy(eye); this._camera.lookat.copy(lookat); this._camera.up.copy(up); this._zoom = MathUtils.lerp(this._zoom0, this._zoom1, sta); this._stateTransitionAlpha = sta; } _getStateTransitionAlpha() { return this._stateTransitionAlpha; } } class EulerRotationDelta { constructor(phi, theta) { this._phi = phi; this._theta = theta; } get phi() { return this._phi; } set phi(value) { this._phi = value; } get theta() { return this._theta; } set theta(value) { this._theta = value; } get isZero() { return this._phi === 0 && this._theta === 0; } copy(delta) { this._phi = delta.phi; this._theta = delta.theta; } lerp(other, alpha) { this._phi = (1 - alpha) * this._phi + alpha * other.phi; this._theta = (1 - alpha) * this._theta + alpha * other.theta; } multiply(value) { this._phi *= value; this._theta *= value; } threshold(value) { this._phi = Math.abs(this._phi) > value ? this._phi : 0; this._theta = Math.abs(this._theta) > value ? this._theta : 0; } lengthSquared() { return this._phi * this._phi + this._theta * this._theta; } reset() { this._phi = 0; this._theta = 0; } } class InteractiveStateBase extends StateBase { constructor(state) { super(state); this._animationSpeed = 1 / 40; this._rotationDelta = new EulerRotationDelta(0, 0); this._requestedRotationDelta = null; this._basicRotation = [0, 0]; this._requestedBasicRotation = null; this._requestedBasicRotationUnbounded = null; this._rotationAcceleration = 0.86; this._rotationIncreaseAlpha = 0.97; this._rotationDecreaseAlpha = 0.9; this._rotationThreshold = 1e-3; this._unboundedRotationAlpha = 0.8; this._desiredZoom = state.zoom; this._minZoom = 0; this._maxZoom = 3; this._lookatDepth = 10; this._desiredLookat = null; this._desiredCenter = null; } rotate(rotationDelta) { if (this._currentImage == null) { return; } if (rotationDelta.phi === 0 && rotationDelta.theta === 0) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedBasicRotation = null; if (this._requestedRotationDelta != null) { this._requestedRotationDelta.phi = this._requestedRotationDelta.phi + rotationDelta.phi; this._requestedRotationDelta.theta = this._requestedRotationDelta.theta + rotationDelta.theta; } else { this._requestedRotationDelta = new EulerRotationDelta(rotationDelta.phi, rotationDelta.theta); } } rotateUnbounded(delta) { if (this._currentImage == null) { return; } this._requestedBasicRotation = null; this._requestedRotationDelta = null; this._applyRotation(delta, this._currentCamera); this._applyRotation(delta, this._previousCamera); if (!this._desiredLookat) { return; } const q = new Quaternion().setFromUnitVectors(this._currentCamera.up, new Vector3(0, 0, 1)); const qInverse = q.clone().invert(); const offset = new Vector3() .copy(this._desiredLookat) .sub(this._camera.position) .applyQuaternion(q); const length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += delta.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += delta.theta; theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); this._desiredLookat .copy(this._camera.position) .add(offset.multiplyScalar(length)); } rotateWithoutInertia(rotationDelta) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedBasicRotation = null; this._requestedRotationDelta = null; const threshold = Math.PI / (10 * Math.pow(2, this._zoom)); const delta = { phi: this._spatial.clamp(rotationDelta.phi, -threshold, threshold), theta: this._spatial.clamp(rotationDelta.theta, -threshold, threshold), }; this._applyRotation(delta, this._currentCamera); this._applyRotation(delta, this._previousCamera); } rotateBasic(basicRotation) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedRotationDelta = null; if (this._requestedBasicRotation != null) { this._requestedBasicRotation[0] += basicRotation[0]; this._requestedBasicRotation[1] += basicRotation[1]; let threshold = 0.05 / Math.pow(2, this._zoom); this._requestedBasicRotation[0] = this._spatial.clamp(this._requestedBasicRotation[0], -threshold, threshold); this._requestedBasicRotation[1] = this._spatial.clamp(this._requestedBasicRotation[1], -threshold, threshold); } else { this._requestedBasicRotation = basicRotation.slice(); } } rotateBasicUnbounded(basicRotation) { if (this._currentImage == null) { return; } if (this._requestedBasicRotationUnbounded != null) { this._requestedBasicRotationUnbounded[0] += basicRotation[0]; this._requestedBasicRotationUnbounded[1] += basicRotation[1]; } else { this._requestedBasicRotationUnbounded = basicRotation.slice(); } } rotateBasicWithoutInertia(basic) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; const threshold = 0.05 / Math.pow(2, this._zoom); const basicRotation = basic.slice(); basicRotation[0] = this._spatial.clamp(basicRotation[0], -threshold, threshold); basicRotation[1] = this._spatial.clamp(basicRotation[1], -threshold, threshold); this._applyRotationBasic(basicRotation); } rotateToBasic(basic) { if (this._currentImage == null) { return; } this._desiredZoom = this._zoom; this._desiredLookat = null; basic[0] = this._spatial.clamp(basic[0], 0, 1); basic[1] = this._spatial.clamp(basic[1], 0, 1); let lookat = this.currentTransform.unprojectBasic(basic, this._lookatDepth); this._currentCamera.lookat.fromArray(lookat); } zoomIn(delta, reference) { if (this._currentImage == null) { return; } this._desiredZoom = Math.max(this._minZoom, Math.min(this._maxZoom, this._desiredZoom + delta)); let currentCenter = this.currentTransform.projectBasic(this._currentCamera.lookat.toArray()); let currentCenterX = currentCenter[0]; let currentCenterY = currentCenter[1]; let zoom0 = Math.pow(2, this._zoom); let zoom1 = Math.pow(2, this._desiredZoom); let refX = reference[0]; let refY = reference[1]; if (isSpherical(this.currentTransform.cameraType)) { if (refX - currentCenterX > 0.5) { refX = refX - 1; } else if (currentCenterX - refX > 0.5) { refX = 1 + refX; } } let newCenterX = refX - zoom0 / zoom1 * (refX - currentCenterX); let newCenterY = refY - zoom0 / zoom1 * (refY - currentCenterY); if (isSpherical(this._currentImage.cameraType)) { newCenterX = this._spatial .wrap(newCenterX + this._basicRotation[0], 0, 1); newCenterY = this._spatial .clamp(newCenterY + this._basicRotation[1], 0.05, 0.95); } else { newCenterX = this._spatial.clamp(newCenterX, 0, 1); newCenterY = this._spatial.clamp(newCenterY, 0, 1); } this._desiredLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic([newCenterX, newCenterY], this._lookatDepth)); } setCenter(center) { this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; this._desiredZoom = this._zoom; let clamped = [ this._spatial.clamp(center[0], 0, 1), this._spatial.clamp(center[1], 0, 1), ]; if (this._currentImage == null) { this._desiredCenter = clamped; return; } this._desiredCenter = null; let currentLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic(clamped, this._lookatDepth)); let previousTransform = this.previousTransform != null ? this.previousTransform : this.currentTransform; let previousLookat = new Vector3() .fromArray(previousTransform.unprojectBasic(clamped, this._lookatDepth)); this._currentCamera.lookat.copy(currentLookat); this._previousCamera.lookat.copy(previousLookat); } setZoom(zoom) { this._desiredLookat = null; this._requestedRotationDelta = null; this._requestedBasicRotation = null; this._zoom = this._spatial.clamp(zoom, this._minZoom, this._maxZoom); this._desiredZoom = this._zoom; } _applyRotation(delta, camera) { if (camera == null) { return; } let q = new Quaternion().setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); let qInverse = q.clone().invert(); let offset = new Vector3(); offset.copy(camera.lookat).sub(camera.position); offset.applyQuaternion(q); let length = offset.length(); let phi = Math.atan2(offset.y, offset.x); phi += delta.phi; let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); theta += delta.theta; theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); offset.x = Math.sin(theta) * Math.cos(phi); offset.y = Math.sin(theta) * Math.sin(phi); offset.z = Math.cos(theta); offset.applyQuaternion(qInverse); camera.lookat.copy(camera.position).add(offset.multiplyScalar(length)); } _applyRotationBasic(basicRotation) { let currentImage = this._currentImage; let previousImage = this._previousImage != null ? this.previousImage : this.currentImage; let currentCamera = this._currentCamera; let previousCamera = this._previousCamera; let currentTransform = this.currentTransform; let previousTransform = this.previousTransform != null ? this.previousTransform : this.currentTransform; let currentBasic = currentTransform.projectBasic(currentCamera.lookat.toArray()); let previousBasic = previousTransform.projectBasic(previousCamera.lookat.toArray()); if (isSpherical(currentImage.cameraType)) { currentBasic[0] = this._spatial.wrap(currentBasic[0] + basicRotation[0], 0, 1); currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0.05, 0.95); } else { currentBasic[0] = this._spatial.clamp(currentBasic[0] + basicRotation[0], 0, 1); currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } if (isSpherical(previousImage.cameraType)) { previousBasic[0] = this._spatial.wrap(previousBasic[0] + basicRotation[0], 0, 1); previousBasic[1] = this._spatial.clamp(previousBasic[1] + basicRotation[1], 0.05, 0.95); } else { previousBasic[0] = this._spatial.clamp(previousBasic[0] + basicRotation[0], 0, 1); previousBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } let currentLookat = currentTransform.unprojectBasic(currentBasic, this._lookatDepth); currentCamera.lookat.fromArray(currentLookat); let previousLookat = previousTransform.unprojectBasic(previousBasic, this._lookatDepth); previousCamera.lookat.fromArray(previousLookat); } _updateZoom(animationSpeed) { let diff = this._desiredZoom - this._zoom; let sign = diff > 0 ? 1 : diff < 0 ? -1 : 0; if (diff === 0) { return; } else if (Math.abs(diff) < 2e-3) { this._zoom = this._desiredZoom; if (this._desiredLookat != null) { this._desiredLookat = null; } } else { this._zoom += sign * Math.max(Math.abs(5 * animationSpeed * diff), 2e-3); } } _updateLookat(animationSpeed) { if (this._desiredLookat === null) { return; } let diff = this._desiredLookat.distanceToSquared(this._currentCamera.lookat); if (Math.abs(diff) < 1e-6) { this._currentCamera.lookat.copy(this._desiredLookat); this._desiredLookat = null; } else { this._currentCamera.lookat.lerp(this._desiredLookat, 5 * animationSpeed); } } _updateRotation() { if (this._requestedRotationDelta != null) { let length = this._rotationDelta.lengthSquared(); let requestedLength = this._requestedRotationDelta.lengthSquared(); if (requestedLength > length) { this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationIncreaseAlpha); } else { this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationDecreaseAlpha); } this._requestedRotationDelta = null; return; } if (this._rotationDelta.isZero) { return; } const alpha = isSpherical(this.currentImage.cameraType) ? 1 : this._alpha; this._rotationDelta.multiply(this._rotationAcceleration * alpha); this._rotationDelta.threshold(this._rotationThreshold); } _updateRotationBasic() { if (this._requestedBasicRotation != null) { let x = this._basicRotation[0]; let y = this._basicRotation[1]; let reqX = this._requestedBasicRotation[0]; let reqY = this._requestedBasicRotation[1]; if (Math.abs(reqX) > Math.abs(x)) { this._basicRotation[0] = (1 - this._rotationIncreaseAlpha) * x + this._rotationIncreaseAlpha * reqX; } else { this._basicRotation[0] = (1 - this._rotationDecreaseAlpha) * x + this._rotationDecreaseAlpha * reqX; } if (Math.abs(reqY) > Math.abs(y)) { this._basicRotation[1] = (1 - this._rotationIncreaseAlpha) * y + this._rotationIncreaseAlpha * reqY; } else { this._basicRotation[1] = (1 - this._rotationDecreaseAlpha) * y + this._rotationDecreaseAlpha * reqY; } this._requestedBasicRotation = null; return; } if (this._requestedBasicRotationUnbounded != null) { let reqX = this._requestedBasicRotationUnbounded[0]; let reqY = this._requestedBasicRotationUnbounded[1]; if (Math.abs(reqX) > 0) { this._basicRotation[0] = (1 - this._unboundedRotationAlpha) * this._basicRotation[0] + this._unboundedRotationAlpha * reqX; } if (Math.abs(reqY) > 0) { this._basicRotation[1] = (1 - this._unboundedRotationAlpha) * this._basicRotation[1] + this._unboundedRotationAlpha * reqY; } if (this._desiredLookat != null) { let desiredBasicLookat = this.currentTransform.projectBasic(this._desiredLookat.toArray()); desiredBasicLookat[0] += reqX; desiredBasicLookat[1] += reqY; this._desiredLookat = new Vector3() .fromArray(this.currentTransform.unprojectBasic(desiredBasicLookat, this._lookatDepth)); } this._requestedBasicRotationUnbounded = null; } if (this._basicRotation[0] === 0 && this._basicRotation[1] === 0) { return; } this._basicRotation[0] = this._rotationAcceleration * this._basicRotation[0]; this._basicRotation[1] = this._rotationAcceleration * this._basicRotation[1]; if (Math.abs(this._basicRotation[0]) < this._rotationThreshold / Math.pow(2, this._zoom) && Math.abs(this._basicRotation[1]) < this._rotationThreshold / Math.pow(2, this._zoom)) { this._basicRotation = [0, 0]; } } _clearRotation() { if (isSpherical(this._currentImage.cameraType)) { return; } if (this._requestedRotationDelta != null) { this._requestedRotationDelta = null; } if (!this._rotationDelta.isZero) { this._rotationDelta.reset(); } if (this._requestedBasicRotation != null) { this._requestedBasicRotation = null; } if (this._basicRotation[0] > 0 || this._basicRotation[1] > 0) { this._basicRotation = [0, 0]; } } _setDesiredCenter() { if (this._desiredCenter == null) { return; } let lookatDirection = new Vector3() .fromArray(this.currentTransform.unprojectBasic(this._desiredCenter, this._lookatDepth)) .sub(this._currentCamera.position); this._currentCamera.lookat.copy(this._currentCamera.position.clone().add(lookatDirection)); this._previousCamera.lookat.copy(this._previousCamera.position.clone().add(lookatDirection)); this._desiredCenter = null; } _setDesiredZoom() { this._desiredZoom = isSpherical(this._currentImage.cameraType) || this._previousImage == null ? this._zoom : 0; } } class TraversingState extends InteractiveStateBase { constructor(state) { super(state); this._adjustCameras(); this._motionless = this._motionlessTransition(); this._baseAlpha = this._alpha; this._speedCoefficient = 1; this._smoothing = false; } append(images) { let emptyTrajectory = this._trajectory.length === 0; if (emptyTrajectory) { this._resetTransition(); } super.append(images); if (emptyTrajectory) { this._setDesiredCenter(); this._setDesiredZoom(); } } prepend(images) { let emptyTrajectory = this._trajectory.length === 0; if (emptyTrajectory) { this._resetTransition(); } super.prepend(images); if (emptyTrajectory) { this._setDesiredCenter(); this._setDesiredZoom(); } } set(images) { super.set(images); this._desiredLookat = null; this._resetTransition(); this._clearRotation(); this._setDesiredCenter(); this._setDesiredZoom(); if (this._trajectory.length < 3) { this._smoothing = true; } } setSpeed(speed) { this._speedCoefficient = this._spatial.clamp(speed, 0, 10); } update(delta) { if (this._alpha === 1 && this._currentIndex + this._alpha < this._trajectory.length) { this._currentIndex += 1; this._smoothing = this._trajectory.length < 3 && this._currentIndex + 1 === this._trajectory.length; this._setCurrent(); this._resetTransition(); this._clearRotation(); this._desiredZoom = isSpherical(this._currentImage.cameraType) ? this._zoom : 0; this._desiredLookat = null; } let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; this._baseAlpha = Math.min(1, this._baseAlpha + this._speedCoefficient * animationSpeed); if (this._smoothing) { this._alpha = MathUtils.smootherstep(this._baseAlpha, 0, 1); } else { this._alpha = this._baseAlpha; } this._updateRotation(); if (!this._rotationDelta.isZero) { this._applyRotation(this._rotationDelta, this._previousCamera); this._applyRotation(this._rotationDelta, this._currentCamera); } this._updateRotationBasic(); if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { this._applyRotationBasic(this._basicRotation); } this._updateZoom(animationSpeed); this._updateLookat(animationSpeed); this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.ceil(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } let lookat = this._camera.lookat.clone().sub(this._camera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); if (isSpherical(this._currentImage.cameraType) && !isNullImageId(this._previousImage.id)) { this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } } _resetTransition() { this._alpha = 0; this._baseAlpha = 0; this._motionless = this._motionlessTransition(); } } const UP = new Vector3(0, 0, 1); class GravityTraversingState extends TraversingState { constructor(state) { super(state); this._camera.up.copy(UP); this._alignCameras(); this._alignTrajectory(); } append(images) { super.append(images); this._alignTrajectory(); } prepend(images) { super.prepend(images); this._alignTrajectory(); } set(images) { super.set(images); this._alignCameras(); this._alignTrajectory(); } update(delta) { super.update(delta); } _alignTrajectory() { for (const camera of this._trajectoryCameras) { camera.up.copy(UP); } } _alignCameras() { var _a, _b; (_a = this._previousCamera) === null || _a === void 0 ? void 0 : _a.up.copy(UP); (_b = this._currentCamera) === null || _b === void 0 ? void 0 : _b.up.copy(UP); } } class InteractiveWaitingState extends InteractiveStateBase { constructor(state) { super(state); this._adjustCameras(); this._motionless = this._motionlessTransition(); } prepend(images) { super.prepend(images); this._motionless = this._motionlessTransition(); } set(images) { super.set(images); this._motionless = this._motionlessTransition(); } move(delta) { this._alpha = Math.max(0, Math.min(1, this._alpha + delta)); } moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } update(delta) { this._updateRotation(); if (!this._rotationDelta.isZero) { this._applyRotation(this._rotationDelta, this._previousCamera); this._applyRotation(this._rotationDelta, this._currentCamera); } this._updateRotationBasic(); if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { this._applyRotationBasic(this._basicRotation); } let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; this._updateZoom(animationSpeed); this._updateLookat(animationSpeed); this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.round(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } if (isSpherical(this._currentImage.cameraType)) { let lookat = this._camera.lookat.clone().sub(this._camera.position); this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } if (isSpherical(this._previousImage.cameraType)) { let lookat = this._currentCamera.lookat.clone().sub(this._currentCamera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); } } } class WaitingState extends StateBase { constructor(state) { super(state); this._zoom = 0; this._adjustCameras(); this._motionless = this._motionlessTransition(); } prepend(images) { super.prepend(images); this._motionless = this._motionlessTransition(); } set(images) { super.set(images); this._motionless = this._motionlessTransition(); } move(delta) { this._alpha = Math.max(0, Math.min(1, this._alpha + delta)); } moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } update() { this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { return this._motionless ? Math.round(this._alpha) : this._alpha; } _setCurrentCamera() { super._setCurrentCamera(); this._adjustCameras(); } _adjustCameras() { if (this._previousImage == null) { return; } if (isSpherical(this._currentImage.cameraType)) { let lookat = this._camera.lookat.clone().sub(this._camera.position); this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); } if (isSpherical(this._previousImage.cameraType)) { let lookat = this._currentCamera.lookat.clone().sub(this._currentCamera.position); this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); } } } class StateTransitionMatrix { constructor() { const custom = State[State.Custom]; const earth = State[State.Earth]; const gravityTraverse = State[State.GravityTraversing]; const traverse = State[State.Traversing]; const wait = State[State.Waiting]; const waitInteractively = State[State.WaitingInteractively]; this._creators = new Map(); const creator = this._creators; creator.set(custom, CustomState); creator.set(earth, EarthState); creator.set(gravityTraverse, GravityTraversingState); creator.set(traverse, TraversingState); creator.set(wait, WaitingState); creator.set(waitInteractively, InteractiveWaitingState); this._transitions = new Map(); const transitions = this._transitions; transitions.set(custom, [earth, gravityTraverse, traverse]); transitions.set(earth, [custom, gravityTraverse, traverse]); transitions.set(gravityTraverse, [custom, earth, traverse, wait, waitInteractively]); transitions.set(traverse, [custom, earth, gravityTraverse, wait, waitInteractively]); transitions.set(wait, [gravityTraverse, traverse, waitInteractively]); transitions.set(waitInteractively, [gravityTraverse, traverse, wait]); } getState(state) { if (state instanceof CustomState) { return State.Custom; } else if (state instanceof EarthState) { return State.Earth; } else if (state instanceof GravityTraversingState) { return State.GravityTraversing; } else if (state instanceof TraversingState) { return State.Traversing; } else if (state instanceof WaitingState) { return State.Waiting; } else if (state instanceof InteractiveWaitingState) { return State.WaitingInteractively; } throw new Error("Invalid state instance"); } generate(state, options) { const concreteState = this._creators.get(State[state]); return new concreteState(options); } transition(state, to) { if (!this.validate(state, to)) { throw new Error("Invalid transition"); } return this.generate(to, state); } validate(state, to) { const source = State[this.getState(state)]; const target = State[to]; const transitions = this._transitions; return transitions.has(source) && transitions.get(source).includes(target); } } class StateContext { constructor(state, transitionMode) { this._transitions = new StateTransitionMatrix(); this._state = this._transitions.generate(state, { alpha: 1, camera: new Camera(), currentIndex: -1, reference: { alt: 0, lat: 0, lng: 0 }, trajectory: [], transitionMode: transitionMode == null ? exports.TransitionMode.Default : transitionMode, zoom: 0, }); } get state() { return this._transitions.getState(this._state); } get reference() { return this._state.reference; } get alpha() { return this._state.alpha; } get stateTransitionAlpha() { return this._state.stateTransitionAlpha; } get camera() { return this._state.camera; } get zoom() { return this._state.zoom; } get currentImage() { return this._state.currentImage; } get previousImage() { return this._state.previousImage; } get currentCamera() { return this._state.currentCamera; } get previousCamera() { return this._state.previousCamera; } get currentTransform() { return this._state.currentTransform; } get previousTransform() { return this._state.previousTransform; } get trajectory() { return this._state.trajectory; } get currentIndex() { return this._state.currentIndex; } get lastImage() { return this._state.trajectory[this._state.trajectory.length - 1]; } get imagesAhead() { return this._state.trajectory.length - 1 - this._state.currentIndex; } get motionless() { return this._state.motionless; } custom() { this._transition(State.Custom); } earth() { this._transition(State.Earth); } gravityTraverse() { this._transition(State.GravityTraversing); } traverse() { this._transition(State.Traversing); } wait() { this._transition(State.Waiting); } waitInteractively() { this._transition(State.WaitingInteractively); } getCenter() { return this._state.getCenter(); } setCenter(center) { this._state.setCenter(center); } setZoom(zoom) { this._state.setZoom(zoom); } update(delta) { this._state.update(delta); } append(images) { this._state.append(images); } prepend(images) { this._state.prepend(images); } remove(n) { this._state.remove(n); } clear() { this._state.clear(); } clearPrior() { this._state.clearPrior(); } cut() { this._state.cut(); } set(images) { this._state.set(images); } setViewMatrix(matrix) { this._state.setViewMatrix(matrix); } rotate(delta) { this._state.rotate(delta); } rotateUnbounded(delta) { this._state.rotateUnbounded(delta); } rotateWithoutInertia(delta) { this._state.rotateWithoutInertia(delta); } rotateBasic(basicRotation) { this._state.rotateBasic(basicRotation); } rotateBasicUnbounded(basicRotation) { this._state.rotateBasicUnbounded(basicRotation); } rotateBasicWithoutInertia(basicRotation) { this._state.rotateBasicWithoutInertia(basicRotation); } rotateToBasic(basic) { this._state.rotateToBasic(basic); } move(delta) { this._state.move(delta); } moveTo(delta) { this._state.moveTo(delta); } zoomIn(delta, reference) { this._state.zoomIn(delta, reference); } setSpeed(speed) { this._state.setSpeed(speed); } setTransitionMode(mode) { this._state.setTransitionMode(mode); } dolly(delta) { this._state.dolly(delta); } orbit(rotation) { this._state.orbit(rotation); } truck(direction) { this._state.truck(direction); } _transition(to) { if (!this._transitions.validate(this._state, to)) { const from = this._transitions.getState(this._state); console.warn(`Transition not valid (${State[from]} - ${State[to]})`); return; } const state = this._transitions.transition(this._state, to); this._state = state; } } class StateService { constructor(initialState, transitionMode) { this._appendImage$ = new Subject(); this._clock = new Clock(); this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._start$ = new Subject(); this._frame$ = new Subject(); this._contextOperation$ = new BehaviorSubject((context) => { return context; }); this._context$ = this._contextOperation$.pipe(scan((context, operation) => { return operation(context); }, new StateContext(initialState, transitionMode)), publishReplay(1), refCount()); this._state$ = this._context$.pipe(map((context) => { return context.state; }), distinctUntilChanged(), publishReplay(1), refCount()); this._currentState$ = this._frame$.pipe(withLatestFrom(this._context$, (frameId, context) => { return [frameId, context]; }), filter((fc) => { return fc[1].currentImage != null; }), tap((fc) => { fc[1].update(this._clock.getDelta()); }), map((fc) => { return { fps: 60, id: fc[0], state: fc[1] }; }), share()); this._lastState$ = this._currentState$.pipe(publishReplay(1), refCount()); let imageChanged$ = this._currentState$.pipe(distinctUntilChanged(undefined, (f) => { return f.state.currentImage.id; }), publishReplay(1), refCount()); let imageChangedSubject$ = new Subject(); subs.push(imageChanged$ .subscribe(imageChangedSubject$)); this._reference$ = imageChangedSubject$.pipe(map((f) => { const { reference } = f.state; return { lng: reference.lng, lat: reference.lat, alt: reference.alt, }; }), distinctUntilChanged((r1, r2) => { return r1.lat === r2.lat && r1.lng === r2.lng; }), publishReplay(1), refCount()); this._currentId$ = new BehaviorSubject(null); subs.push(imageChangedSubject$.pipe(map((f) => { return f.state.currentImage.id; })) .subscribe(this._currentId$)); this._currentImage$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentImage; }), publishReplay(1), refCount()); this._currentCamera$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentCamera; }), publishReplay(1), refCount()); this._currentTransform$ = imageChangedSubject$.pipe(map((f) => { return f.state.currentTransform; }), publishReplay(1), refCount()); this._currentImageExternal$ = imageChanged$.pipe(map((f) => { return f.state.currentImage; }), publishReplay(1), refCount()); subs.push(this._appendImage$.pipe(map((image) => { return (context) => { context.append([image]); return context; }; })) .subscribe(this._contextOperation$)); this._inMotionOperation$ = new Subject(); subs.push(imageChanged$.pipe(map(() => { return true; })) .subscribe(this._inMotionOperation$)); subs.push(this._inMotionOperation$.pipe(distinctUntilChanged(), filter((moving) => { return moving; }), switchMap(() => { return this._currentState$.pipe(filter((frame) => { return frame.state.imagesAhead === 0; }), map((frame) => { return [frame.state.camera.clone(), frame.state.zoom]; }), pairwise(), map((pair) => { let c1 = pair[0][0]; let c2 = pair[1][0]; let z1 = pair[0][1]; let z2 = pair[1][1]; return c1.diff(c2) > 1e-5 || Math.abs(z1 - z2) > 1e-5; }), first((changed) => { return !changed; })); })) .subscribe(this._inMotionOperation$)); this._inMotion$ = this._inMotionOperation$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); this._inTranslationOperation$ = new Subject(); subs.push(imageChanged$.pipe(map(() => { return true; })) .subscribe(this._inTranslationOperation$)); subs.push(this._inTranslationOperation$.pipe(distinctUntilChanged(), filter((inTranslation) => { return inTranslation; }), switchMap(() => { return this._currentState$.pipe(filter((frame) => { return frame.state.imagesAhead === 0; }), map((frame) => { return frame.state.camera.position.clone(); }), pairwise(), map((pair) => { return pair[0].distanceToSquared(pair[1]) !== 0; }), first((changed) => { return !changed; })); })) .subscribe(this._inTranslationOperation$)); this._inTranslation$ = this._inTranslationOperation$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); subs.push(this._reference$.subscribe(() => { })); subs.push(this._state$.subscribe(() => { })); subs.push(this._currentImage$.subscribe(() => { })); subs.push(this._currentCamera$.subscribe(() => { })); subs.push(this._currentTransform$.subscribe(() => { })); subs.push(this._currentImageExternal$.subscribe(() => { })); subs.push(this._lastState$.subscribe(() => { })); subs.push(this._inMotion$.subscribe(() => { })); subs.push(this._inTranslation$.subscribe(() => { })); this._frameId = null; this._frameGenerator = new FrameGenerator(window); } get currentState$() { return this._currentState$; } get currentImage$() { return this._currentImage$; } get currentId$() { return this._currentId$; } get currentImageExternal$() { return this._currentImageExternal$; } get currentCamera$() { return this._currentCamera$; } get currentTransform$() { return this._currentTransform$; } get state$() { return this._state$; } get reference$() { return this._reference$; } get inMotion$() { return this._inMotion$; } get inTranslation$() { return this._inTranslation$; } get appendImage$() { return this._appendImage$; } dispose() { this.stop(); this._subscriptions.unsubscribe(); } custom() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.custom(); }); } earth() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.earth(); }); } gravityTraverse() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.gravityTraverse(); }); } traverse() { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.traverse(); }); } wait() { this._invokeContextOperation((context) => { context.wait(); }); } waitInteractively() { this._invokeContextOperation((context) => { context.waitInteractively(); }); } appendImagess(images) { this._invokeContextOperation((context) => { context.append(images); }); } prependImages(images) { this._invokeContextOperation((context) => { context.prepend(images); }); } removeImages(n) { this._invokeContextOperation((context) => { context.remove(n); }); } clearImages() { this._invokeContextOperation((context) => { context.clear(); }); } clearPriorImages() { this._invokeContextOperation((context) => { context.clearPrior(); }); } cutImages() { this._invokeContextOperation((context) => { context.cut(); }); } setImages(images) { this._invokeContextOperation((context) => { context.set(images); }); } setViewMatrix(matrix) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setViewMatrix(matrix); }); } rotate(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotate(delta); }); } rotateUnbounded(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateUnbounded(delta); }); } rotateWithoutInertia(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateWithoutInertia(delta); }); } rotateBasic(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasic(basicRotation); }); } rotateBasicUnbounded(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasicUnbounded(basicRotation); }); } rotateBasicWithoutInertia(basicRotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateBasicWithoutInertia(basicRotation); }); } rotateToBasic(basic) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.rotateToBasic(basic); }); } move(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.move(delta); }); } moveTo(position) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.moveTo(position); }); } dolly(delta) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.dolly(delta); }); } orbit(rotation) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.orbit(rotation); }); } truck(direction) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.truck(direction); }); } /** * Change zoom level while keeping the reference point position approximately static. * * @parameter {number} delta - Change in zoom level. * @parameter {Array} reference - Reference point in basic coordinates. */ zoomIn(delta, reference) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.zoomIn(delta, reference); }); } getCenter() { return this._lastState$.pipe(first(), map((frame) => { return frame.state.getCenter(); })); } getZoom() { return this._lastState$.pipe(first(), map((frame) => { return frame.state.zoom; })); } setCenter(center) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setCenter(center); }); } setSpeed(speed) { this._invokeContextOperation((context) => { context.setSpeed(speed); }); } setTransitionMode(mode) { this._invokeContextOperation((context) => { context.setTransitionMode(mode); }); } setZoom(zoom) { this._inMotionOperation$.next(true); this._invokeContextOperation((context) => { context.setZoom(zoom); }); } start() { this._clock.start(); if (this._frameId == null) { this._start$.next(null); this._frameId = this._frameGenerator.requestAnimationFrame(this._frame.bind(this)); this._frame$.next(this._frameId); } } stop() { this._clock.stop(); if (this._frameId != null) { this._frameGenerator.cancelAnimationFrame(this._frameId); this._frameId = null; } } _invokeContextOperation(action) { this._contextOperation$ .next((context) => { action(context); return context; }); } _frame() { this._frameId = this._frameGenerator.requestAnimationFrame(this._frame.bind(this)); this._frame$.next(this._frameId); } } function cameraControlsToState(cameraControls) { switch (cameraControls) { case exports.CameraControls.Custom: return State.Custom; case exports.CameraControls.Earth: return State.Earth; case exports.CameraControls.Gravity: return State.GravityTraversing; case exports.CameraControls.Street: return State.Traversing; default: return null; } } class Navigator { constructor(options, api, graphService, loadingService, stateService, cacheService, playService, panService) { var _a; if (api) { this._api = api; } else if (options.dataProvider) { this._api = new APIWrapper(options.dataProvider); } else { this._api = new APIWrapper(new GraphDataProvider({ accessToken: options.accessToken, })); } this._projectionService = new ProjectionService(); this._graphService = graphService !== null && graphService !== void 0 ? graphService : new GraphService(new Graph(this.api), this._projectionService); this._loadingName = "navigator"; this._loadingService = loadingService !== null && loadingService !== void 0 ? loadingService : new LoadingService(); const cameraControls = (_a = options.cameraControls) !== null && _a !== void 0 ? _a : exports.CameraControls.Street; this._stateService = stateService !== null && stateService !== void 0 ? stateService : new StateService(cameraControlsToState(cameraControls), options.transitionMode); this._cacheService = cacheService !== null && cacheService !== void 0 ? cacheService : new CacheService(this._graphService, this._stateService, this._api); this._playService = playService !== null && playService !== void 0 ? playService : new PlayService(this._graphService, this._stateService); this._panService = panService !== null && panService !== void 0 ? panService : new PanService(this._graphService, this._stateService, this._projectionService, options.combinedPanning); this._idRequested$ = new BehaviorSubject(null); this._movedToId$ = new BehaviorSubject(null); this._request$ = null; this._requestSubscription = null; this._imageRequestSubscription = null; } get api() { return this._api; } get cacheService() { return this._cacheService; } get graphService() { return this._graphService; } get loadingService() { return this._loadingService; } get movedToId$() { return this._movedToId$; } get panService() { return this._panService; } get playService() { return this._playService; } get projectionService() { return this._projectionService; } get stateService() { return this._stateService; } dispose() { this._abortRequest("viewer removed"); this._cacheService.stop(); this._graphService.dispose(); this._panService.dispose(); this._playService.dispose(); this._stateService.dispose(); } moveDir$(direction) { this._abortRequest(`in dir ${exports.NavigationDirection[direction]}`); this._loadingService.startLoading(this._loadingName); const image$ = this.stateService.currentImage$.pipe(first(), mergeMap((image) => { return ([exports.NavigationDirection.Next, exports.NavigationDirection.Prev].indexOf(direction) > -1 ? image.sequenceEdges$ : image.spatialEdges$).pipe(first(), map((status) => { for (let edge of status.edges) { if (edge.data.direction === direction) { return edge.target; } } return null; })); }), mergeMap((directionId) => { if (directionId == null) { this._loadingService.stopLoading(this._loadingName); return throwError(new Error(`Direction (${direction}) does not exist for current image.`)); } return this._moveTo$(directionId); })); return this._makeRequest$(image$); } moveTo$(id) { this._abortRequest(`to id ${id}`); this._loadingService.startLoading(this._loadingName); const image$ = this._moveTo$(id); return this._makeRequest$(image$); } reset$() { this._abortRequest("to reset"); return this._reset$() .pipe(map(() => undefined)); } setFilter$(filter) { this._stateService.clearImages(); return this._movedToId$.pipe(first(), mergeMap((id) => { if (id != null) { return this._trajectoryIds$().pipe(mergeMap((ids) => { return this._graphService.setFilter$(filter).pipe(mergeMap(() => { return this._cacheIds$(ids); })); }), last()); } return this._idRequested$.pipe(first(), mergeMap((requestedId) => { if (requestedId != null) { return this._graphService.setFilter$(filter).pipe(mergeMap(() => { return this._graphService.cacheImage$(requestedId); })); } return this._graphService.setFilter$(filter).pipe(map(() => { return undefined; })); })); }), map(() => { return undefined; })); } setAccessToken$(accessToken) { this._abortRequest("to set user token"); return this._reset$(() => this._api.setAccessToken(accessToken)); } _cacheIds$(ids) { const cacheImages$ = ids .map((id) => { return this._graphService.cacheImage$(id); }); return from(cacheImages$).pipe(mergeAll()); } _abortRequest(reason) { if (this._requestSubscription != null) { this._requestSubscription.unsubscribe(); this._requestSubscription = null; } if (this._imageRequestSubscription != null) { this._imageRequestSubscription.unsubscribe(); this._imageRequestSubscription = null; } this._idRequested$.next(null); if (this._request$ != null) { if (!(this._request$.isStopped || this._request$.hasError)) { this._request$.error(new CancelMapillaryError(`Request aborted by a subsequent request ${reason}.`)); } this._request$ = null; } } _makeRequest$(image$) { const request$ = new ReplaySubject(1); this._requestSubscription = request$ .subscribe(undefined, () => { }); this._request$ = request$; this._imageRequestSubscription = image$ .subscribe((image) => { this._request$ = null; request$.next(image); request$.complete(); }, (error) => { this._request$ = null; request$.error(error); }); return request$; } _moveTo$(id) { this._idRequested$.next(id); return this._graphService.cacheImage$(id).pipe(tap((image) => { this._stateService.setImages([image]); this._movedToId$.next(image.id); }), finalize(() => { this._loadingService.stopLoading(this._loadingName); })); } _reset$(preCallback) { this._movedToId$.next(null); return makeNullImage$().pipe(tap((image) => { this._stateService.setImages([image]); this._stateService.setImages([image]); this._stateService.clearImages(); }), first(), tap(() => { if (preCallback) { preCallback(); } }), mergeMap(() => { return this._graphService.reset$([]); })); } _trajectoryIds$() { return this._stateService.currentState$.pipe(first(), map((frame) => { return frame.state.trajectory .map((image) => { return image.id; }) .filter((id) => { return !isNullImageId(id); }); })); } } class Projection { constructor(viewportCoords, spatial) { this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); } basicToCanvas(basicPoint, container, render, transform) { return this._viewportCoords .basicToCanvasSafe(basicPoint[0], basicPoint[1], container, transform, render.perspective); } canvasToBasic(canvasPoint, container, render, transform) { let basicPoint = this._viewportCoords .canvasToBasic(canvasPoint[0], canvasPoint[1], container, transform, render.perspective); if (basicPoint[0] < 0 || basicPoint[0] > 1 || basicPoint[1] < 0 || basicPoint[1] > 1) { basicPoint = null; } return basicPoint; } eventToUnprojection(event, container, render, reference, transform) { const pixelPoint = this._viewportCoords .canvasPosition(event, container); return this.canvasToUnprojection(pixelPoint, container, render, reference, transform); } canvasToUnprojection(canvasPoint, container, render, reference, transform) { const canvasX = canvasPoint[0]; const canvasY = canvasPoint[1]; const [viewportX, viewportY] = this._viewportCoords .canvasToViewport(canvasX, canvasY, container); const point3d = new Vector3(viewportX, viewportY, 1) .unproject(render.perspective); let basicPoint = transform .projectBasic(point3d.toArray()); if (basicPoint[0] < 0 || basicPoint[0] > 1 || basicPoint[1] < 0 || basicPoint[1] > 1) { basicPoint = null; } const direction3d = point3d .clone() .sub(render.camera.position) .normalize(); const dist = -2 / direction3d.z; let lngLat = null; if (dist > 0 && dist < 100 && !!basicPoint) { const point = direction3d .clone() .multiplyScalar(dist) .add(render.camera.position); const [lng, lat] = enuToGeodetic(point.x, point.y, point.z, reference.lng, reference.lat, reference.alt); lngLat = { lat, lng }; } const unprojection = { basicPoint: basicPoint, lngLat: lngLat, pixelPoint: [canvasX, canvasY], }; return unprojection; } cameraToLngLat(render, reference) { const position = render.camera.position; const [lng, lat] = enuToGeodetic(position.x, position.y, position.z, reference.lng, reference.lat, reference.alt); return { lat, lng }; } lngLatToCanvas(lngLat, container, render, reference) { const point3d = geodeticToEnu(lngLat.lng, lngLat.lat, 0, reference.lng, reference.lat, reference.alt); const canvas = this._viewportCoords .projectToCanvasSafe(point3d, container, render.perspective); return canvas; } distanceBetweenLngLats(lngLat1, lngLat2) { return this._spatial .distanceFromLngLat(lngLat1.lng, lngLat1.lat, lngLat2.lng, lngLat2.lat); } } class Observer { constructor(viewer, navigator, container) { this._subscriptions = new SubscriptionHolder(); this._emitSubscriptions = new SubscriptionHolder(); this._container = container; this._viewer = viewer; this._navigator = navigator; this._projection = new Projection(); this._started = false; this._navigable$ = new Subject(); const subs = this._subscriptions; // load, navigable, dataloading should always emit, // also when cover is activated. subs.push(this._navigable$ .subscribe((navigable) => { const type = "navigable"; const event = { navigable, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.loadingService.loading$ .subscribe((loading) => { const type = "dataloading"; const event = { loading, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.glRenderer.opaqueRender$ .pipe(first()) .subscribe(() => { const type = "load"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); } get started() { return this._started; } get navigable$() { return this._navigable$; } get projection() { return this._projection; } dispose() { this.stopEmit(); this._subscriptions.unsubscribe(); } project$(lngLat) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentImage$, this._navigator.stateService.reference$).pipe(first(), map(([render, image, reference]) => { if (this._projection .distanceBetweenLngLats(lngLat, image.lngLat) > 1000) { return null; } const canvasPoint = this._projection.lngLatToCanvas(lngLat, this._container.container, render, reference); return !!canvasPoint ? [Math.round(canvasPoint[0]), Math.round(canvasPoint[1])] : null; })); } projectBasic$(basicPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, transform]) => { const canvasPoint = this._projection.basicToCanvas(basicPoint, this._container.container, render, transform); return !!canvasPoint ? [Math.round(canvasPoint[0]), Math.round(canvasPoint[1])] : null; })); } startEmit() { if (this._started) { return; } this._started = true; const subs = this._emitSubscriptions; subs.push(this._navigator.stateService.currentImageExternal$ .subscribe((image) => { const type = "image"; const event = { image: isNullImageId(image.id) ? null : image, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.currentImageExternal$.pipe(switchMap((image) => { return image.sequenceEdges$; })) .subscribe((status) => { const type = "sequenceedges"; const event = { status, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.currentImageExternal$.pipe(switchMap((image) => { return image.spatialEdges$; })) .subscribe((status) => { const type = "spatialedges"; const event = { status, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.reference$ .subscribe((reference) => { const type = "reference"; const event = { reference, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.graphService.dataReset$ .subscribe(() => { const type = "reset"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(combineLatest(this._navigator.stateService.inMotion$, this._container.mouseService.active$, this._container.touchService.active$).pipe(map((values) => { return values[0] || values[1] || values[2]; }), distinctUntilChanged()) .subscribe((started) => { const type = started ? "movestart" : "moveend"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.bearing$.pipe(auditTime(100), distinctUntilChanged((b1, b2) => { return Math.abs(b2 - b1) < 1; })) .subscribe((bearing) => { const type = "bearing"; const event = { bearing, target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.mouseService.mouseDragEnd$.subscribe((originalEvent) => { const type = "dragend"; const event = { originalEvent, target: this._viewer, type, }; this._viewer.fire(type, event); })); const mouseMove$ = this._container.mouseService.active$.pipe(switchMap((active) => { return active ? empty() : this._container.mouseService.mouseMove$; })); subs.push(merge(this._mapMouseEvent$("click", this._container.mouseService.staticClick$), this._mapMouseEvent$("contextmenu", this._container.mouseService.contextMenu$), this._mapMouseEvent$("dblclick", this._container.mouseService.dblClick$), this._mapMouseEvent$("drag", this._container.mouseService.mouseDrag$), this._mapMouseEvent$("dragstart", this._container.mouseService.mouseDragStart$), this._mapMouseEvent$("mousedown", this._container.mouseService.mouseDown$), this._mapMouseEvent$("mousemove", mouseMove$), this._mapMouseEvent$("mouseout", this._container.mouseService.mouseOut$), this._mapMouseEvent$("mouseover", this._container.mouseService.mouseOver$), this._mapMouseEvent$("mouseup", this._container.mouseService.mouseUp$)) .pipe(withLatestFrom(this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.currentTransform$, this._navigator.stateService.state$), map(([[type, event], render, reference, transform, state]) => { const unprojection = this._projection.eventToUnprojection(event, this._container.container, render, reference, transform); const basicPoint = state === State.Traversing ? unprojection.basicPoint : null; return { basicPoint, lngLat: unprojection.lngLat, originalEvent: event, pixelPoint: unprojection.pixelPoint, target: this._viewer, type: type, }; })) .subscribe((event) => { this._viewer.fire(event.type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged(([x1, y1], [x2, y2]) => { return this._closeTo(x1, x2, 1e-2) && this._closeTo(y1, y2, 1e-2); }, (rc) => { return rc.camera.position.toArray(); })) .subscribe(() => { const type = "position"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._navigator.stateService.reference$ .subscribe(() => { const type = "position"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged(([phi1, theta1], [phi2, theta2]) => { return this._closeTo(phi1, phi2, 1e-3) && this._closeTo(theta1, theta2, 1e-3); }, (rc) => { return [rc.rotation.phi, rc.rotation.theta]; })) .subscribe(() => { const type = "pov"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); subs.push(this._container.renderService.renderCamera$.pipe(distinctUntilChanged((fov1, fov2) => { return this._closeTo(fov1, fov2, 1e-2); }, (rc) => { return rc.perspective.fov; })) .subscribe(() => { const type = "fov"; const event = { target: this._viewer, type, }; this._viewer.fire(type, event); })); } stopEmit() { if (!this.started) { return; } this._emitSubscriptions.unsubscribe(); this._started = false; } unproject$(canvasPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, reference, transform]) => { const unprojection = this._projection.canvasToUnprojection(canvasPoint, this._container.container, render, reference, transform); return unprojection.lngLat; })); } unprojectBasic$(canvasPoint) { return combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.currentTransform$).pipe(first(), map(([render, transform]) => { return this._projection.canvasToBasic(canvasPoint, this._container.container, render, transform); })); } _closeTo(v1, v2, absoluteTolerance) { return Math.abs(v1 - v2) <= absoluteTolerance; } _mapMouseEvent$(type, mouseEvent$) { return mouseEvent$.pipe(map((event) => { return [type, event]; })); } } class CustomRenderer { constructor(_container, _navigator) { this._container = _container; this._navigator = _navigator; this._renderers = {}; } add(renderer, viewer) { const subs = new SubscriptionHolder(); this._renderers[renderer.id] = { subs, renderer }; subs.push(combineLatest([ this._container.glRenderer.webGLRenderer$, this._navigator.stateService.reference$, ]) .pipe(take(1)) .subscribe(([gl, reference]) => { renderer.onAdd(viewer, reference, gl.getContext()); })); subs.push(this._container.glRenderer.opaqueRender$ .pipe(withLatestFrom(this._container.renderService.renderCamera$, this._container.glRenderer.webGLRenderer$)) .subscribe(([, renderCamera, glRenderer]) => { const context = glRenderer.getContext(); const viewMatrix = renderCamera.perspective.matrixWorldInverse; const projectionMatrix = renderCamera.perspective.projectionMatrix; renderer.render(context, viewMatrix.toArray(), projectionMatrix.toArray()); })); subs.push(this._navigator.stateService.reference$ .pipe(skip(1)) .subscribe((reference) => { renderer.onReference(viewer, reference); })); } dispose(viewer) { for (const id of Object.keys(this._renderers)) { this.remove(id, viewer); } } has(id) { return id in this._renderers; } remove(id, viewer) { this._renderers[id].subs.unsubscribe(); const renderer = this._renderers[id].renderer; delete this._renderers[id]; this._container.glRenderer.webGLRenderer$ .subscribe((gl) => { renderer.onRemove(viewer, gl.getContext()); }); } } class CustomCameraControls { constructor(_container, _navigator) { this._container = _container; this._navigator = _navigator; this._controls = null; this._subscriptions = new SubscriptionHolder(); } attach(controls, viewer) { if (this._controls) { throw new MapillaryError("Custom camera controls already attached"); } this._controls = controls; const attach$ = new Subject(); const active$ = attach$ .pipe(switchMap(() => { return this._navigator.stateService.state$; }), map((state) => { return state === State.Custom; }), distinctUntilChanged()); const subs = this._subscriptions; subs.push(active$ .pipe(startWith(false), pairwise(), withLatestFrom(this._navigator.stateService.reference$, this._container.renderService.renderCamera$)) .subscribe(([[deactivate, activate], ref, cam]) => { if (activate) { controls.onActivate(viewer, cam.perspective.matrixWorldInverse.toArray(), cam.perspective.projectionMatrix.toArray(), ref); } else if (deactivate) { controls.onDeactivate(viewer); } })); subs.push(active$ .pipe(switchMap(active => { return active ? this._navigator.stateService.currentState$ .pipe(skip(1)) : empty(); })) .subscribe(frame => { controls.onAnimationFrame(viewer, frame.id); })); subs.push(active$ .pipe(switchMap(active => { return active ? this._navigator.stateService.reference$ .pipe(skip(1)) : empty(); })) .subscribe(ref => controls.onReference(viewer, ref))); subs.push(active$ .pipe(switchMap(active => { return active ? this._container.renderService.size$ .pipe(skip(1)) : empty(); })) .subscribe(() => controls.onResize(viewer))); subs.push(combineLatest([ // Include to ensure GL renderer has been initialized this._container.glRenderer.webGLRenderer$, this._container.renderService.renderCamera$, this._navigator.stateService.reference$, this._navigator.stateService.state$, ]) .pipe(first()) .subscribe(() => { const projectionMatrixCallback = (projectionMatrix) => { if (!this._controls || controls !== this._controls) { return; } this._updateProjectionMatrix(projectionMatrix); }; const viewMatrixCallback = (viewMatrix) => { if (!this._controls || controls !== this._controls) { return; } this._updateViewMatrix(viewMatrix); }; controls.onAttach(viewer, viewMatrixCallback, projectionMatrixCallback); attach$.next(); attach$.complete(); })); } detach(viewer) { const controls = this._controls; this._controls = null; this._subscriptions.unsubscribe(); return new Promise(resolve => { this._navigator.stateService.state$ .pipe(take(1)) .subscribe(state => { if (!controls) { resolve(null); return; } if (state === State.Custom) { controls.onDeactivate(viewer); } controls.onDetach(viewer); resolve(controls); }); }); } dispose(viewer) { this.detach(viewer); } has(controls) { return !!this._controls && controls === this._controls; } _updateProjectionMatrix(projectionMatrix) { this._navigator.stateService.state$ .pipe(first()) .subscribe(state => { if (state !== State.Custom) { const message = "Incorrect camera control mode for " + "projection matrix update"; console.warn(message); return; } this._container.renderService.projectionMatrix$ .next(projectionMatrix); }); } _updateViewMatrix(viewMatrix) { this._navigator.stateService.state$ .pipe(first()) .subscribe(state => { if (state !== State.Custom) { const message = "Incorrect camera control mode for " + "view matrix update"; console.warn(message); return; } this._navigator.stateService.setViewMatrix(viewMatrix); }); } } /** * @class Viewer * * @classdesc The Viewer object represents the navigable image viewer. * Create a Viewer by specifying a container, client ID, image ID and * other options. The viewer exposes methods and events for programmatic * interaction. * * In the case of asynchronous methods, MapillaryJS returns promises to * the results. Notifications are always emitted through JavaScript events. */ class Viewer extends EventEmitter { /** * Create a new viewer instance. * * @description The `Viewer` object represents the street imagery * viewer on your web page. It exposes methods and properties that * you can use to programatically change the view, and fires * events as users interact with it. * * It is possible to initialize the viewer with or * without a ID. * * When you want to show a specific image in the viewer from * the start you should initialize it with a ID. * * When you do not know the first image ID at implementation * time, e.g. in a map-viewer application you should initialize * the viewer without a ID and call `moveTo` instead. * * When initializing with an ID the viewer is bound to that ID * until the image for that ID has been successfully loaded. * Also, a cover with the image of the ID will be shown. * If the data for that ID can not be loaded because the ID is * faulty or other errors occur it is not possible to navigate * to another ID because the viewer is not navigable. The viewer * becomes navigable when the data for the ID has been loaded and * the image is shown in the viewer. This way of initializing * the viewer is mostly for embedding in blog posts and similar * where one wants to show a specific image initially. * * If the viewer is initialized without a ID (with null or * undefined) it is not bound to any particular ID and it is * possible to move to any ID with `viewer.moveTo("")`. * If the first move to a ID fails it is possible to move to another * ID. The viewer will show a black background until a move * succeeds. This way of intitializing is suited for a map-viewer * application when the initial ID is not known at implementation * time. * * @param {ViewerOptions} options - Optional configuration object * specifying Viewer"s and the components" initial setup. * * @example * ```js * var viewer = new Viewer({ * accessToken: "", * container: "", * }); * ``` */ constructor(options) { super(); this._navigator = new Navigator(options); this._container = new Container(options, this._navigator.stateService); this._observer = new Observer(this, this._navigator, this._container); this._componentController = new ComponentController(this._container, this._navigator, this._observer, options.imageId, options.component); this._customRenderer = new CustomRenderer(this._container, this._navigator); this._customCameraControls = new CustomCameraControls(this._container, this._navigator); } /** * Returns the data provider used by the viewer to fetch * all contracts, ents, and buffers. * * @description The viewer"s data provider can be set * upon initialization through the {@link ViewerOptions.dataProvider} * property. * * @returns {IDataProvider} The viewer"s data provider. */ get dataProvider() { return this._navigator.api.data; } /** * Return a boolean indicating if the viewer is in a navigable state. * * @description The navigable state indicates if the viewer supports * moving, i.e. calling the {@link moveTo} and {@link moveDir} * methods or changing the authentication state, * i.e. calling {@link setAccessToken}. The viewer will not be in a navigable * state if the cover is activated and the viewer has been supplied a ID. * When the cover is deactivated or the viewer is activated without being * supplied a ID it will be navigable. * * @returns {boolean} Boolean indicating whether the viewer is navigable. */ get isNavigable() { return this._componentController.navigable; } /** * Activate the combined panning functionality. * * @description The combined panning functionality is active by default. */ activateCombinedPanning() { this._navigator.panService.enable(); } /** * Activate a component. * * @param {ComponentName | FallbackComponentName} name - Name of * the component which will become active. * * @example * ```js * viewer.activateComponent("marker"); * ``` */ activateComponent(name) { this._componentController.activate(name); } /** * Activate the cover (deactivates all other components). */ activateCover() { this._componentController.activateCover(); } /** * Add a custom renderer to the viewer"s rendering pipeline. * * @description During a render pass, custom renderers * are called in the order they were added. * * @param renderer - The custom renderer implementation. */ addCustomRenderer(renderer) { this._customRenderer.add(renderer, this); } /** * Attach custom camera controls to control the viewer"s * camera pose and projection. * * @description Custom camera controls allow the API user * to move the viewer"s camera freely and define the camera * projection. These camera properties are used * to render the viewer 3D scene directly into the * viewer"s GL context. * * Only a single custom camera control instance can be * attached to the viewer. A new custom camera control * instance can be attached after detaching a previous * one. * * Set the viewer"s camera controls to * {@link CameraControls.Custom} to activate attached * camera controls. If {@link CameraControls.Custom} * has already been set when a custom camera control * instance is attached, it will be activated immediately. * * Set the viewer"s camera controls to any other * {@link CameraControls} mode to deactivate the * custom camera controls. * * @param controls - The custom camera controls implementation. * * @throws {MapillaryError} When camera controls attached * are already attached to the viewer. */ attachCustomCameraControls(controls) { this._customCameraControls.attach(controls, this); } /** * Deactivate the combined panning functionality. * * @description Deactivating the combined panning functionality * could be needed in scenarios involving sequence only navigation. */ deactivateCombinedPanning() { this._navigator.panService.disable(); } /** * Deactivate a component. * * @param {ComponentName | FallbackComponentName} name - Name * of component which become inactive. * * @example * ```js * viewer.deactivateComponent("pointer"); * ``` */ deactivateComponent(name) { this._componentController.deactivate(name); } /** * Deactivate the cover (activates all components marked as active). */ deactivateCover() { this._componentController.deactivateCover(); } /** * Detach a previously attached custom camera control * instance from the viewer. * * @description If no custom camera control instance * has previously been attached, calling this method * has no effect. * * Already attached custom camera controls need to * be detached before attaching another custom camera * control instance. */ detachCustomCameraControls() { return this._customCameraControls.detach(this); } fire(type, event) { super.fire(type, event); } /** * Get the bearing of the current viewer camera. * * @description The bearing depends on how the camera * is currently rotated and does not correspond * to the compass angle of the current image if the view * has been panned. * * Bearing is measured in degrees clockwise with respect to * north. * * @returns {Promise} Promise to the bearing * of the current viewer camera. * * @example * ```js * viewer.getBearing().then(b => { console.log(b); }); * ``` */ getBearing() { return new Promise((resolve, reject) => { this._container.renderService.bearing$.pipe(first()) .subscribe((bearing) => { resolve(bearing); }, (error) => { reject(error); }); }); } /** * Get the viewer"s camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * @returns {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.getCameraControls().then(c => { console.log(c); }); * ``` */ getCameraControls() { return new Promise((resolve, reject) => { this._navigator.stateService.state$.pipe(first()) .subscribe((state) => { switch (state) { case State.Custom: resolve(exports.CameraControls.Custom); break; case State.Earth: resolve(exports.CameraControls.Earth); break; default: resolve(exports.CameraControls.Street); break; } }, (error) => { reject(error); }); }); } /** * Returns the viewer"s canvas element. * * @description This is the element onto which the viewer renders * the WebGL content. * * @returns {HTMLCanvasElement} The viewer"s canvas element, or * null or not initialized. */ getCanvas() { return this._container.canvas; } /** * Returns the HTML element containing the viewer"s canvas element. * * @description This is the element to which event bindings for viewer * interactivity (such as panning and zooming) are attached. * * @returns {HTMLDivElement} The container for the viewer"s * canvas element. */ getCanvasContainer() { return this._container.canvasContainer; } /** * Get the basic coordinates of the current image that is * at the center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and have the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @returns {Promise} Promise to the basic coordinates * of the current image at the center for the viewport. * * @example * ```js * viewer.getCenter().then(c => { console.log(c); }); * ``` */ getCenter() { return new Promise((resolve, reject) => { this._navigator.stateService.getCenter() .subscribe((center) => { resolve(center); }, (error) => { reject(error); }); }); } /** * Get a component. * * @param {string} name - Name of component. * @returns {Component} The requested component. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * ``` */ getComponent(name) { return this._componentController.get(name); } /** * Returns the viewer"s containing HTML element. * * @returns {HTMLElement} The viewer"s container. */ getContainer() { return this._container.container; } /** * Get the viewer"s current vertical field of view. * * @description The vertical field of view rendered on the viewer canvas * measured in degrees. * * @returns {Promise} Promise to the current field of view * of the viewer camera. * * @example * ```js * viewer.getFieldOfView().then(fov => { console.log(fov); }); * ``` */ getFieldOfView() { return new Promise((resolve, reject) => { this._container.renderService.renderCamera$.pipe(first()) .subscribe((rc) => { resolve(rc.perspective.fov); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current image. * * @returns {Promise} Promise to the current image. * * @example * ```js * viewer.getImage().then(image => { console.log(image.id); }); * ``` */ getImage() { return new Promise((resolve, reject) => { this._navigator.stateService.currentImage$.pipe(first()) .subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current point of view. * * @returns {Promise} Promise to the current point of view * of the viewer camera. * * @example * ```js * viewer.getPointOfView().then(pov => { console.log(pov); }); * ``` */ getPointOfView() { return new Promise((resolve, reject) => { combineLatest(this._container.renderService.renderCamera$, this._container.renderService.bearing$).pipe(first()) .subscribe(([rc, bearing]) => { resolve({ bearing: bearing, tilt: rc.getTilt(), }); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current position * * @returns {Promise} Promise to the viewers"s current * position. * * @example * ```js * viewer.getPosition().then(pos => { console.log(pos); }); * ``` */ getPosition() { return new Promise((resolve, reject) => { combineLatest(this._container.renderService.renderCamera$, this._navigator.stateService.reference$).pipe(first()) .subscribe(([render, reference]) => { resolve(this._observer.projection.cameraToLngLat(render, reference)); }, (error) => { reject(error); }); }); } /** * Get the viewer"s current reference position. * * @description The reference position specifies the origin in * the viewer"s topocentric coordinate system. * * @returns {Promise} Promise to the reference position. * * @example * ```js * viewer.getReference().then(reference => { console.log(reference); }); * ``` */ getReference() { return new Promise((resolve, reject) => { this._navigator.stateService.reference$.pipe(first()) .subscribe((reference) => { resolve(reference); }, (error) => { reject(error); }); }); } /** * Get the image"s current zoom level. * * @returns {Promise} Promise to the viewers"s current * zoom level. * * @example * ```js * viewer.getZoom().then(z => { console.log(z); }); * ``` */ getZoom() { return new Promise((resolve, reject) => { this._navigator.stateService.getZoom() .subscribe((zoom) => { resolve(zoom); }, (error) => { reject(error); }); }); } /** * Check if a controls instance is the camera controls that are * currently attached to the viewer. * * @param {ICustomCameraControls} controls - Camera controls instance. * @returns {boolean} Value indicating whether the controls instance * is currently attached. */ hasCustomCameraControls(controls) { return this._customCameraControls.has(controls); } /** * Check if a custom renderer has been added to the viewer"s * rendering pipeline. * * @param {string} id - Unique ID of the custom renderer. * @returns {boolean} Value indicating whether the customer * renderer has been added. */ hasCustomRenderer(rendererId) { return this._customRenderer.has(rendererId); } /** * Navigate in a given direction. * * @param {NavigationDirection} direction - Direction in which which to move. * @returns {Promise} Promise to the image that was navigated to. * @throws If the current image does not have the edge direction * or the edges has not yet been cached. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent move request * is made before the move dir call has completed. * * @example * ```js * viewer.moveDir(NavigationDirection.Next).then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ moveDir(direction) { const moveDir$ = this.isNavigable ? this._navigator.moveDir$(direction) : throwError(new Error("Calling moveDir is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { moveDir$.subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } /** * Navigate to a given image ID. * * @param {string} imageId - Id of the image to move to. * @returns {Promise} Promise to the image that was navigated to. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent * move request is made before the move to ID call has completed. * * @example * ```js * viewer.moveTo("").then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ moveTo(imageId) { const moveTo$ = this.isNavigable ? this._navigator.moveTo$(imageId) : throwError(new Error("Calling moveTo is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { moveTo$.subscribe((image) => { resolve(image); }, (error) => { reject(error); }); }); } off(type, handler) { super.off(type, handler); } on(type, handler) { super.on(type, handler); } /** * Project geodetic coordinates to canvas pixel coordinates. * * @description The geodetic coordinates may not always correspond to pixel * coordinates, e.g. if the geodetic coordinates have a position behind the * viewer camera. In the case of no correspondence the returned value will * be `null`. * * If the distance from the viewer camera position to the provided * longitude-latitude is more than 1000 meters `null` will be returned. * * The projection is performed from the ground plane, i.e. * the altitude with respect to the ground plane for the geodetic * point is zero. * * Note that whenever the camera moves, the result of the method will be * different. * * @param {LngLat} lngLat - Geographical coordinates to project. * @returns {Promise>} Promise to the pixel coordinates corresponding * to the lngLat. * * @example * ```js * viewer.project({ lat: 0, lng: 0 }) * .then(pixelPoint => { * if (!pixelPoint) { * console.log("no correspondence"); * } * * console.log(pixelPoint); * }); * ``` */ project(lngLat) { return new Promise((resolve, reject) => { this._observer.project$(lngLat) .subscribe((pixelPoint) => { resolve(pixelPoint); }, (error) => { reject(error); }); }); } /** * Project basic image coordinates for the current image to canvas pixel * coordinates. * * @description The basic image coordinates may not always correspond to a * pixel point that lies in the visible area of the viewer container. In the * case of no correspondence the returned value can be `null`. * * * @param {Array} basicPoint - Basic images coordinates to project. * @returns {Promise>} Promise to the pixel coordinates corresponding * to the basic image point. * * @example * ```js * viewer.projectFromBasic([0.3, 0.7]) * .then(pixelPoint => { console.log(pixelPoint); }); * ``` */ projectFromBasic(basicPoint) { return new Promise((resolve, reject) => { this._observer.projectBasic$(basicPoint) .subscribe((pixelPoint) => { resolve(pixelPoint); }, (error) => { reject(error); }); }); } /** * Clean up and release all internal resources associated with * this viewer. * * @description This includes DOM elements, event bindings, and * WebGL resources. * * Use this method when you are done using the viewer and wish to * ensure that it no longer consumes browser resources. Afterwards, * you must not call any other methods on the viewer. * * @fires remove * * @example * ```js * viewer.remove(); * ``` */ remove() { this._customRenderer.dispose(this); this._customCameraControls.dispose(this); this._observer.dispose(); this._componentController.remove(); this._navigator.dispose(); this._container.remove(); const type = "remove"; const event = { target: this, type, }; this.fire(type, event); } /** * Remove a custom renderer from the viewer"s rendering pipeline. * * @param id - Unique ID of the custom renderer. */ removeCustomRenderer(rendererId) { this._customRenderer.remove(rendererId, this); } /** * Reset the viewer"s cache. * * @description All images in the viewer"s cache at the moment of the * reset will be disposed. * * @returns {Promise} Promise that resolves when viewer"s cache * has been reset. * * @throws When viewer is not navigable. * * @example * ```js * viewer.reset() * .then(() => { console.log("viewer reset"); }); * ``` */ reset() { const reset$ = this.isNavigable ? this._navigator.reset$() : throwError(new Error("Calling reset is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { reset$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Detect the viewer"s new width and height and resize it * manually. * * @description The components will also detect the viewer"s * new size and resize their rendered elements if needed. * * When the {@link ViewerOptions.trackResize} option is * set to true, the viewer will automatically resize * when the browser window is resized. If any other * custom behavior is preferred, the option should be set * to false and the {@link Viewer.resize} method should * be called on demand. * * @example * ```js * viewer.resize(); * ``` */ resize() { this._container.renderService.resize$.next(); } /** * Register a class constructor for a camera type. * * @description The Viewer will invoke the camera * constructor each time that camera type should be * instantiated. * * @param {string} type - Camera type. * @param ctor - Contructor for Camera class implementing the * {@link ICamera} interface. * * @example * ```js * import {MyCamera} from "./MyCameraClasses"; * viewer.registerCamera("my-camera-type", MyCamera); * ``` */ registerCamera(type, ctor) { this._navigator.projectionService.registerCamera(type, ctor); } /** * Set the viewer"s camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * @param {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.setCameraControls(CameraControls.Street); * ``` */ setCameraControls(controls) { const state = cameraControlsToState(controls); if (state === State.Custom) { this._navigator.stateService.custom(); } else if (state === State.Earth) { this._navigator.stateService.earth(); } else if (state === State.GravityTraversing) { this._navigator.stateService.gravityTraverse(); } else if (state === State.Traversing) { this._navigator.stateService.traverse(); } else { console.warn(`Unsupported camera control transition (${controls})`); } } /** * Set the basic coordinates of the current image to be in the * center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and has the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @param {number[]} The basic coordinates of the current * image to be at the center for the viewport. * * @example * ```js * viewer.setCenter([0.5, 0.5]); * ``` */ setCenter(center) { this._navigator.stateService.setCenter(center); } /** * Set a new data provider instance. * * @description Resets the viewer"s cache (see {@link Viewer.reset}). * * @returns {Promise} Promise that resolves when viewer"s data * provider has been set. * * @throws When viewer is not navigable. * * @example * ```js * const myDataProvider = new MyDataProvider(); * viewer.setDataProvider(myDataProvider) * .then(() => { console.log("data provider set"); }); * ``` */ setDataProvider(provider) { const reset$ = this.isNavigable ? this._navigator.reset$() .pipe(tap(() => { this._navigator.api.setDataProvider(provider); const type = "dataprovider"; const event = { target: this, type, }; this.fire(type, event); })) : throwError(new Error("Calling setDataProvider is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { reset$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Set the viewer"s current vertical field of view. * * @description Sets the vertical field of view rendered * on the viewer canvas measured in degrees. The value * will be clamped to be able to set a valid zoom level * based on the projection model of the current image and * the viewer"s current render mode. * * @param {number} fov - Vertical field of view in degrees. * * @example * ```js * viewer.setFieldOfView(45); * ``` */ setFieldOfView(fov) { this._container.renderService.renderCamera$.pipe(first()) .subscribe((rc) => { const zoom = rc.fovToZoom(fov); this._navigator.stateService.setZoom(zoom); }); } /** * Set the filter selecting images to use when calculating * the spatial edges. * * @description The following filter types are supported: * * Comparison * * `["==", key, value]` equality: `image[key] = value` * * `["!=", key, value]` inequality: `image[key] ≠ value` * * `["<", key, value]` less than: `image[key] < value` * * `["<=", key, value]` less than or equal: `image[key] ≤ value` * * `[">", key, value]` greater than: `image[key] > value` * * `[">=", key, value]` greater than or equal: `image[key] ≥ value` * * Set membership * * `["in", key, v0, ..., vn]` set inclusion: `image[key] ∈ {v0, ..., vn}` * * `["!in", key, v0, ..., vn]` set exclusion: `image[key] ∉ {v0, ..., vn}` * * Combining * * `["all", f0, ..., fn]` logical `AND`: `f0 ∧ ... ∧ fn` * * A key must be a string that identifies a property name of a * simple {@link Image} property, i.e. a key of the {@link FilterKey} * type. A value must be a string, number, or * boolean. Strictly-typed comparisons are used. The values * `f0, ..., fn` of the combining filter must be filter expressions. * * Clear the filter by setting it to null or empty array. * * Commonly used filter properties (see the {@link Image} class * documentation for a full list of properties that can be used * in a filter) are shown the the example code. * * @param {FilterExpression} [filter] - The filter expression. * Applied filter is cleared if omitted. * @returns {Promise} Promise that resolves after filter is applied. * * @example * ```js * // Examples * viewer.setFilter(["==", "cameraType", "spherical"]); * viewer.setFilter([">=", "capturedAt", ]); * viewer.setFilter(["in", "sequenceId", "", ""]); * // Clear filter * viewer.setFilter([]); * ``` */ setFilter(filter) { return new Promise((resolve, reject) => { this._navigator.setFilter$(filter) .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Set the viewer"s render mode. * * @param {RenderMode} renderMode - Render mode. * * @example * ```js * viewer.setRenderMode(RenderMode.Letterbox); * ``` */ setRenderMode(renderMode) { this._container.renderService.renderMode$.next(renderMode); } /** * Set the viewer"s texture shader. * * @description The shader will be used for all registered projection * models. * * @param {GLShader} shader - Texture shader. * * @example * ```js * let myShader = { * fragment: "", * vertex: "", * }; * viewer.setShader(myShader); * ``` */ setShader(shader) { this._navigator.projectionService.setShader(shader); } /** * Set the viewer"s transition mode. * * @param {TransitionMode} transitionMode - Transition mode. * * @example * ```js * viewer.setTransitionMode(TransitionMode.Instantaneous); * ``` */ setTransitionMode(transitionMode) { this._navigator.stateService.setTransitionMode(transitionMode); } /** * Set an access token for authenticated API requests of protected * resources. * * The token may be a user access token or a client access token. * * @description When the supplied user token is null or undefined, * any previously set user bearer token will be cleared and the * viewer will make unauthenticated requests. * * Calling setAccessToken aborts all outstanding move requests. * The promises of those move requests will be rejected with a * {@link CancelMapillaryError} the rejections need to be caught. * * Calling setAccessToken also resets the complete viewer cache * so it should not be called repeatedly. * * @param {string} [accessToken] accessToken - Optional user * access token or client access token. * @returns {Promise} Promise that resolves after token * is set. * * @throws When viewer is not navigable. * * @example * ```js * viewer.setAccessToken("") * .then(() => { console.log("user token set"); }); * ``` */ setAccessToken(accessToken) { const setAccessToken$ = this.isNavigable ? this._navigator.setAccessToken$(accessToken) : throwError(new Error("Calling setAccessToken is not supported when viewer is not navigable.")); return new Promise((resolve, reject) => { setAccessToken$ .subscribe(() => { resolve(undefined); }, (error) => { reject(error); }); }); } /** * Set the image"s current zoom level. * * @description Possible zoom level values are on the [0, 3] interval. * Zero means zooming out to fit the image to the view whereas three * shows the highest level of detail. * * @param {number} The image"s current zoom level. * * @example * ```js * viewer.setZoom(2); * ``` */ setZoom(zoom) { this._navigator.stateService.setZoom(zoom); } /** * Trigger the rendering of a single frame. * * @description Use this method with custom renderers to * force the viewer to rerender when the custom content * changes. Calling this multiple times before the next * frame is rendered will still result in only a single * frame being rendered. */ triggerRerender() { this._container.glRenderer.triggerRerender(); } /** * Unproject canvas pixel coordinates to geodetic * coordinates. * * @description The pixel point may not always correspond to geodetic * coordinates. In the case of no correspondence the returned value will * be `null`. * * The unprojection to a lngLat will be performed towards the ground plane, i.e. * the altitude with respect to the ground plane for the returned lngLat is zero. * * @param {Array} pixelPoint - Pixel coordinates to unproject. * @returns {Promise} Promise to the lngLat corresponding to the pixel point. * * @example * ```js * viewer.unproject([100, 100]) * .then(lngLat => { console.log(lngLat); }); * ``` */ unproject(pixelPoint) { return new Promise((resolve, reject) => { this._observer.unproject$(pixelPoint) .subscribe((lngLat) => { resolve(lngLat); }, (error) => { reject(error); }); }); } /** * Unproject canvas pixel coordinates to basic image coordinates for the * current image. * * @description The pixel point may not always correspond to basic image * coordinates. In the case of no correspondence the returned value will * be `null`. * * @param {Array} pixelPoint - Pixel coordinates to unproject. * @returns {Promise} Promise to the basic coordinates corresponding * to the pixel point. * * @example * ```js * viewer.unprojectToBasic([100, 100]) * .then(basicPoint => { console.log(basicPoint); }); * ``` */ unprojectToBasic(pixelPoint) { return new Promise((resolve, reject) => { this._observer.unprojectBasic$(pixelPoint) .subscribe((basicPoint) => { resolve(basicPoint); }, (error) => { reject(error); }); }); } } /** * Internal bootstrap * * This is a workaround to make the CommonJS unit testing * work with Jest. Once Jest/Node supports ES6 modules * fully this should be removed. GeoRBush is registered * here only to avoid loading it during * unit tests. */ Graph.register(GeoRBush); MarkerSet.register(GeoRBush); ComponentService.registerCover(CoverComponent); ComponentService.register(AttributionComponent); ComponentService.register(BearingComponent); ComponentService.register(CacheComponent); ComponentService.register(DirectionComponent); ComponentService.register(ImageComponent); ComponentService.register(KeyboardComponent); ComponentService.register(MarkerComponent); ComponentService.register(PointerComponent); ComponentService.register(PopupComponent); ComponentService.register(SequenceComponent); ComponentService.register(SpatialComponent); ComponentService.register(TagComponent); ComponentService.register(ZoomComponent); ComponentService.register(ImageFallbackComponent); ComponentService.register(NavigationFallbackComponent); exports.ArgumentMapillaryError = ArgumentMapillaryError; exports.BearingComponent = BearingComponent; exports.CacheComponent = CacheComponent; exports.CancelMapillaryError = CancelMapillaryError; exports.CircleMarker = CircleMarker; exports.Component = Component; exports.DataProviderBase = DataProviderBase; exports.DirectionComponent = DirectionComponent; exports.DragPanHandler = DragPanHandler; exports.EventEmitter = EventEmitter; exports.ExtremePointTag = ExtremePointTag; exports.FISHEYE_CAMERA_TYPE = FISHEYE_CAMERA_TYPE; exports.FisheyeCamera = FisheyeCamera; exports.Geometry = Geometry; exports.GeometryProviderBase = GeometryProviderBase; exports.GeometryTagError = GeometryTagError; exports.GraphDataProvider = GraphDataProvider; exports.GraphMapillaryError = GraphMapillaryError; exports.Image = Image$1; exports.KeyPlayHandler = KeyPlayHandler; exports.KeySequenceNavigationHandler = KeySequenceNavigationHandler; exports.KeySpatialNavigationHandler = KeySpatialNavigationHandler; exports.KeyZoomHandler = KeyZoomHandler; exports.KeyboardComponent = KeyboardComponent; exports.MapillaryError = MapillaryError; exports.Marker = Marker; exports.MarkerComponent = MarkerComponent; exports.OutlineTag = OutlineTag; exports.PERSPECTIVE_CAMERA_TYPE = PERSPECTIVE_CAMERA_TYPE; exports.PerspectiveCamera = PerspectiveCamera; exports.PointGeometry = PointGeometry; exports.PointerComponent = PointerComponent; exports.PointsGeometry = PointsGeometry; exports.PolygonGeometry = PolygonGeometry; exports.Popup = Popup; exports.PopupComponent = PopupComponent; exports.RectGeometry = RectGeometry; exports.S2GeometryProvider = S2GeometryProvider; exports.SPHERICAL_CAMERA_TYPE = SPHERICAL_CAMERA_TYPE; exports.ScrollZoomHandler = ScrollZoomHandler; exports.SequenceComponent = SequenceComponent; exports.Shader = Shader; exports.ShaderChunk = ShaderChunk; exports.SimpleMarker = SimpleMarker; exports.SpatialComponent = SpatialComponent; exports.SphericalCamera = SphericalCamera; exports.SpotTag = SpotTag; exports.Tag = Tag; exports.TagComponent = TagComponent; exports.TouchZoomHandler = TouchZoomHandler; exports.VertexGeometry = VertexGeometry; exports.Viewer = Viewer; exports.ZoomComponent = ZoomComponent; exports.decompress = decompress; exports.ecefToEnu = ecefToEnu; exports.ecefToGeodetic = ecefToGeodetic; exports.enuToEcef = enuToEcef; exports.enuToGeodetic = enuToGeodetic; exports.fetchArrayBuffer = fetchArrayBuffer; exports.geodeticToEcef = geodeticToEcef; exports.geodeticToEnu = geodeticToEnu; exports.isFallbackSupported = isFallbackSupported; exports.isSupported = isSupported; exports.readMeshPbf = readMeshPbf; exports.resolveShader = resolveShader; Object.defineProperty(exports, "__esModule", { value: true }); })); /* */}),null);
-----
ThreeJS.r89",[],(function $module_ThreeJS_r89(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; var exports = {}; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (factory((global.THREE = {}))); }(this, (function (exports) { "use strict"; // Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } if ( Number.isInteger === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger Number.isInteger = function ( value ) { return typeof value === "number" && isFinite( value ) && Math.floor( value ) === value; }; } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( "name" in Function.prototype === false ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, "name", { get: function () { return this.toString().match( /^s*functions*([^(s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( "Cannot convert undefined or null to object" ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; var listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } }, hasEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return false; var listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { var index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; var array = listenerArray.slice( 0 ); for ( var i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } } } } ); var REVISION = "89"; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; var CullFaceFront = 2; var CullFaceFrontBack = 3; var FrontFaceDirectionCW = 0; var FrontFaceDirectionCCW = 1; var BasicShadowMap = 0; var PCFShadowMap = 1; var PCFSoftShadowMap = 2; var FrontSide = 0; var BackSide = 1; var DoubleSide = 2; var FlatShading = 1; var SmoothShading = 2; var NoColors = 0; var FaceColors = 1; var VertexColors = 2; var NoBlending = 0; var NormalBlending = 1; var AdditiveBlending = 2; var SubtractiveBlending = 3; var MultiplyBlending = 4; var CustomBlending = 5; var AddEquation = 100; var SubtractEquation = 101; var ReverseSubtractEquation = 102; var MinEquation = 103; var MaxEquation = 104; var ZeroFactor = 200; var OneFactor = 201; var SrcColorFactor = 202; var OneMinusSrcColorFactor = 203; var SrcAlphaFactor = 204; var OneMinusSrcAlphaFactor = 205; var DstAlphaFactor = 206; var OneMinusDstAlphaFactor = 207; var DstColorFactor = 208; var OneMinusDstColorFactor = 209; var SrcAlphaSaturateFactor = 210; var NeverDepth = 0; var AlwaysDepth = 1; var LessDepth = 2; var LessEqualDepth = 3; var EqualDepth = 4; var GreaterEqualDepth = 5; var GreaterDepth = 6; var NotEqualDepth = 7; var MultiplyOperation = 0; var MixOperation = 1; var AddOperation = 2; var NoToneMapping = 0; var LinearToneMapping = 1; var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; var EquirectangularReflectionMapping = 303; var EquirectangularRefractionMapping = 304; var SphericalReflectionMapping = 305; var CubeUVReflectionMapping = 306; var CubeUVRefractionMapping = 307; var RepeatWrapping = 1000; var ClampToEdgeWrapping = 1001; var MirroredRepeatWrapping = 1002; var NearestFilter = 1003; var NearestMipMapNearestFilter = 1004; var NearestMipMapLinearFilter = 1005; var LinearFilter = 1006; var LinearMipMapNearestFilter = 1007; var LinearMipMapLinearFilter = 1008; var UnsignedByteType = 1009; var ByteType = 1010; var ShortType = 1011; var UnsignedShortType = 1012; var IntType = 1013; var UnsignedIntType = 1014; var FloatType = 1015; var HalfFloatType = 1016; var UnsignedShort4444Type = 1017; var UnsignedShort5551Type = 1018; var UnsignedShort565Type = 1019; var UnsignedInt248Type = 1020; var AlphaFormat = 1021; var RGBFormat = 1022; var RGBAFormat = 1023; var LuminanceFormat = 1024; var LuminanceAlphaFormat = 1025; var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; var RGB_S3TC_DXT1_Format = 2001; var RGBA_S3TC_DXT1_Format = 2002; var RGBA_S3TC_DXT3_Format = 2003; var RGBA_S3TC_DXT5_Format = 2004; var RGB_PVRTC_4BPPV1_Format = 2100; var RGB_PVRTC_2BPPV1_Format = 2101; var RGBA_PVRTC_4BPPV1_Format = 2102; var RGBA_PVRTC_2BPPV1_Format = 2103; var RGB_ETC1_Format = 2151; var LoopOnce = 2200; var LoopRepeat = 2201; var LoopPingPong = 2202; var InterpolateDiscrete = 2300; var InterpolateLinear = 2301; var InterpolateSmooth = 2302; var ZeroCurvatureEnding = 2400; var ZeroSlopeEnding = 2401; var WrapAroundEnding = 2402; var TrianglesDrawMode = 0; var TriangleStripDrawMode = 1; var TriangleFanDrawMode = 2; var LinearEncoding = 3000; var sRGBEncoding = 3001; var GammaEncoding = 3007; var RGBEEncoding = 3002; var LogLuvEncoding = 3003; var RGBM7Encoding = 3004; var RGBM16Encoding = 3005; var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var _Math = { DEG2RAD: Math.PI / 180, RAD2DEG: 180 / Math.PI, generateUUID: ( function () { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 var lut = []; for ( var i = 0; i < 256; i ++ ) { lut[ i ] = ( i < 16 ? "0" : "" ) + ( i ).toString( 16 ).toUpperCase(); } return function () { var d0 = Math.random() * 0xffffffff | 0; var d1 = Math.random() * 0xffffffff | 0; var d2 = Math.random() * 0xffffffff | 0; var d3 = Math.random() * 0xffffffff | 0; return lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + "-" + lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + "-" + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + "-" + lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + "-" + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] + lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ]; }; } )(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, // Linear mapping from range to range mapLinear: function ( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); }, // https://en.wikipedia.org/wiki/Linear_interpolation lerp: function ( x, y, t ) { return ( 1 - t ) * x + t * y; }, // http://en.wikipedia.org/wiki/Smoothstep smoothstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); }, smootherstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); }, // Random integer from interval randInt: function ( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, // Random float from interval randFloat: function ( low, high ) { return low + Math.random() * ( high - low ); }, // Random float from <-range/2, range/2> interval randFloatSpread: function ( range ) { return range * ( 0.5 - Math.random() ); }, degToRad: function ( degrees ) { return degrees * _Math.DEG2RAD; }, radToDeg: function ( radians ) { return radians * _Math.RAD2DEG; }, isPowerOfTwo: function ( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; }, ceilPowerOfTwo: function ( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); }, floorPowerOfTwo: function ( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author philogb / http://blog.thejit.org/ * @author egraether / http://egraether.com/ * @author zz85 / http://www.lab4games.net/zz85/blog */ function Vector2( x, y ) { this.x = x || 0; this.y = y || 0; } Object.defineProperties( Vector2.prototype, { "width": { get: function () { return this.x; }, set: function ( value ) { this.x = value; } }, "height": { get: function () { return this.y; }, set: function ( value ) { this.y = value; } } } ); Object.assign( Vector2.prototype, { isVector2: true, set: function ( x, y ) { this.x = x; this.y = y; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, applyMatrix3: function ( m ) { var x = this.x, y = this.y; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; }, clampScalar: function () { var min = new Vector2(); var max = new Vector2(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal ); max.set( maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y; }, lengthSq: function () { return this.x * this.x + this.y * this.y; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, angle: function () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector2: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; }, rotateAround: function ( center, angle ) { var c = Math.cos( angle ), s = Math.sin( angle ); var x = this.x - center.x; var y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author jordi_ros / http://plattsoft.com * @author D1plo1d / http://github.com/D1plo1d * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Matrix4() { this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix4: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix4.prototype, { isMatrix4: true, set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { var te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; }, identity: function () { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, clone: function () { return new Matrix4().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; }, copyPosition: function ( m ) { var te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; }, extractBasis: function ( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; }, makeBasis: function ( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; }, extractRotation: function () { var v1 = new Vector3(); return function extractRotation( m ) { var te = this.elements; var me = m.elements; var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; return this; }; }(), makeRotationFromEuler: function ( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order." ); } var te = this.elements; var x = euler.x, y = euler.y, z = euler.z; var a = Math.cos( x ), b = Math.sin( x ); var c = Math.cos( y ), d = Math.sin( y ); var e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // last column te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // bottom row te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, makeRotationFromQuaternion: function ( q ) { var te = this.elements; var x = q._x, y = q._y, z = q._z, w = q._w; var x2 = x + x, y2 = y + y, z2 = z + z; var xx = x * x2, xy = x * y2, xz = x * z2; var yy = y * y2, yz = y * z2, zz = z * z2; var wx = w * x2, wy = w * y2, wz = w * z2; te[ 0 ] = 1 - ( yy + zz ); te[ 4 ] = xy - wz; te[ 8 ] = xz + wy; te[ 1 ] = xy + wz; te[ 5 ] = 1 - ( xx + zz ); te[ 9 ] = yz - wx; te[ 2 ] = xz - wy; te[ 6 ] = yz + wx; te[ 10 ] = 1 - ( xx + yy ); // last column te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // bottom row te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, lookAt: function () { var x = new Vector3(); var y = new Vector3(); var z = new Vector3(); return function lookAt( eye, target, up ) { var te = this.elements; z.subVectors( eye, target ); if ( z.lengthSq() === 0 ) { // eye and target are in the same position z.z = 1; } z.normalize(); x.crossVectors( up, z ); if ( x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { z.x += 0.0001; } else { z.z += 0.0001; } z.normalize(); x.crossVectors( up, z ); } x.normalize(); y.crossVectors( z, x ); te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; return this; }; }(), multiply: function ( m, n ) { if ( n !== undefined ) { console.warn( "THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead." ); return this.multiplyMatrices( m, n ); } return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix4( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), determinant: function () { var te = this.elements; var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); }, transpose: function () { var te = this.elements; var tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; }, setPosition: function ( v ) { var te = this.elements; te[ 12 ] = v.x; te[ 13 ] = v.y; te[ 14 ] = v.z; return this; }, getInverse: function ( m, throwOnDegenerate ) { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm var te = this.elements, me = m.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) { var msg = "THREE.Matrix4: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; }, scale: function ( v ) { var te = this.elements; var x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; }, getMaxScaleOnAxis: function () { var te = this.elements; var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, makeTranslation: function ( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; }, makeRotationX: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationY: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationZ: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, makeRotationAxis: function ( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp var c = Math.cos( angle ); var s = Math.sin( angle ); var t = 1 - c; var x = axis.x, y = axis.y, z = axis.z; var tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; }, makeScale: function ( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; }, makeShear: function ( x, y, z ) { this.set( 1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1 ); return this; }, compose: function ( position, quaternion, scale ) { this.makeRotationFromQuaternion( quaternion ); this.scale( scale ); this.setPosition( position ); return this; }, decompose: function () { var vector = new Vector3(); var matrix = new Matrix4(); return function decompose( position, quaternion, scale ) { var te = this.elements; var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale var det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part matrix.copy( this ); var invSX = 1 / sx; var invSY = 1 / sy; var invSZ = 1 / sz; matrix.elements[ 0 ] *= invSX; matrix.elements[ 1 ] *= invSX; matrix.elements[ 2 ] *= invSX; matrix.elements[ 4 ] *= invSY; matrix.elements[ 5 ] *= invSY; matrix.elements[ 6 ] *= invSY; matrix.elements[ 8 ] *= invSZ; matrix.elements[ 9 ] *= invSZ; matrix.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( matrix ); scale.x = sx; scale.y = sy; scale.z = sz; return this; }; }(), makePerspective: function ( left, right, top, bottom, near, far ) { if ( far === undefined ) { console.warn( "THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs." ); } var te = this.elements; var x = 2 * near / ( right - left ); var y = 2 * near / ( top - bottom ); var a = ( right + left ) / ( right - left ); var b = ( top + bottom ) / ( top - bottom ); var c = - ( far + near ) / ( far - near ); var d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; }, makeOrthographic: function ( left, right, top, bottom, near, far ) { var te = this.elements; var w = 1.0 / ( right - left ); var h = 1.0 / ( top - bottom ); var p = 1.0 / ( far - near ); var x = ( right + left ) * w; var y = ( top + bottom ) * h; var z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Object.assign( Quaternion, { slerp: function ( qa, qb, qm, t ) { return qm.copy( qa ).slerp( qb, t ); }, slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation var x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ], x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { var s = 1 - t, cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { var sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } var tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } } ); Object.defineProperties( Quaternion.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, w: { get: function () { return this._w; }, set: function ( value ) { this._w = value; this.onChangeCallback(); } } } ); Object.assign( Quaternion.prototype, { set: function ( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._w ); }, copy: function ( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this.onChangeCallback(); return this; }, setFromEuler: function ( euler, update ) { if ( ! ( euler && euler.isEuler ) ) { throw new Error( "THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order." ); } var x = euler._x, y = euler._y, z = euler._z, order = euler.order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m var cos = Math.cos; var sin = Math.sin; var c1 = cos( x / 2 ); var c2 = cos( y / 2 ); var c3 = cos( z / 2 ); var s1 = sin( x / 2 ); var s2 = sin( y / 2 ); var s3 = sin( z / 2 ); if ( order === "XYZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "YXZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "ZXY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "ZYX" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "YZX" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "XZY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } if ( update !== false ) this.onChangeCallback(); return this; }, setFromAxisAngle: function ( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this.onChangeCallback(); return this; }, setFromUnitVectors: function () { // assumes direction vectors vFrom and vTo are normalized var v1 = new Vector3(); var r; var EPS = 0.000001; return function setFromUnitVectors( vFrom, vTo ) { if ( v1 === undefined ) v1 = new Vector3(); r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { v1.set( - vFrom.y, vFrom.x, 0 ); } else { v1.set( 0, - vFrom.z, vFrom.y ); } } else { v1.crossVectors( vFrom, vTo ); } this._x = v1.x; this._y = v1.y; this._z = v1.z; this._w = r; return this.normalize(); }; }(), inverse: function () { return this.conjugate().normalize(); }, conjugate: function () { this._x *= - 1; this._y *= - 1; this._z *= - 1; this.onChangeCallback(); return this; }, dot: function ( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; }, lengthSq: function () { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { var l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this.onChangeCallback(); return this; }, multiply: function ( q, p ) { if ( p !== undefined ) { console.warn( "THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead." ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); }, premultiply: function ( q ) { return this.multiplyQuaternions( q, this ); }, multiplyQuaternions: function ( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this.onChangeCallback(); return this; }, slerp: function ( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); var x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); if ( Math.abs( sinHalfTheta ) < 0.001 ) { this._w = 0.5 * ( w + this._w ); this._x = 0.5 * ( x + this._x ); this._y = 0.5 * ( y + this._y ); this._z = 0.5 * ( z + this._z ); return this; } var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this.onChangeCallback(); return this; }, equals: function ( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Object.assign( Vector3.prototype, { isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiply: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead." ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; }, multiplyVectors: function ( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; }, applyEuler: function () { var quaternion = new Quaternion(); return function applyEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order." ); } return this.applyQuaternion( quaternion.setFromEuler( euler ) ); }; }(), applyAxisAngle: function () { var quaternion = new Quaternion(); return function applyAxisAngle( axis, angle ) { return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); }; }(), applyMatrix3: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; }, applyQuaternion: function ( q ) { var x = this.x, y = this.y, z = this.z; var qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; }, project: function () { var matrix = new Matrix4(); return function project( camera ) { matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); return this.applyMatrix4( matrix ); }; }(), unproject: function () { var matrix = new Matrix4(); return function unproject( camera ) { matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); return this.applyMatrix4( matrix ); }; }(), transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, clampScalar: function () { var min = new Vector3(); var max = new Vector3(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, // TODO lengthSquared? lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, cross: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead." ); return this.crossVectors( v, w ); } return this.crossVectors( this, v ); }, crossVectors: function ( a, b ) { var ax = a.x, ay = a.y, az = a.z; var bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; }, projectOnVector: function ( vector ) { var scalar = vector.dot( this ) / vector.lengthSq(); return this.copy( vector ).multiplyScalar( scalar ); }, projectOnPlane: function () { var v1 = new Vector3(); return function projectOnPlane( planeNormal ) { v1.copy( this ).projectOnVector( planeNormal ); return this.sub( v1 ); }; }(), reflect: function () { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length var v1 = new Vector3(); return function reflect( normal ) { return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); }; }(), angleTo: function ( v ) { var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); // clamp, to handle numerical problems return Math.acos( _Math.clamp( theta, - 1, 1 ) ); }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); }, setFromSpherical: function ( s ) { var sinPhiRadius = Math.sin( s.phi ) * s.radius; this.x = sinPhiRadius * Math.sin( s.theta ); this.y = Math.cos( s.phi ) * s.radius; this.z = sinPhiRadius * Math.cos( s.theta ); return this; }, setFromCylindrical: function ( c ) { this.x = c.radius * Math.sin( c.theta ); this.y = c.y; this.z = c.radius * Math.cos( c.theta ); return this; }, setFromMatrixPosition: function ( m ) { var e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; }, setFromMatrixScale: function ( m ) { var sx = this.setFromMatrixColumn( m, 0 ).length(); var sy = this.setFromMatrixColumn( m, 1 ).length(); var sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; }, setFromMatrixColumn: function ( m, index ) { return this.fromArray( m.elements, index * 4 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector3: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } } ); /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io * @author tschw */ function Matrix3() { this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix3: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix3.prototype, { isMatrix3: true, set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { var te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; }, identity: function () { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; }, clone: function () { return new this.constructor().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; }, setFromMatrix4: function ( m ) { var me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix3( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), multiply: function ( m ) { return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; }, determinant: function () { var te = this.elements; var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; }, getInverse: function ( matrix, throwOnDegenerate ) { if ( matrix && matrix.isMatrix4 ) { console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); } var me = matrix.elements, te = this.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) { var msg = "THREE.Matrix3: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; }, transpose: function () { var tmp, m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; }, getNormalMatrix: function ( matrix4 ) { return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); }, transposeIntoArray: function ( r ) { var m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; }, setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { var c = Math.cos( rotation ); var s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); }, scale: function ( sx, sy ) { var te = this.elements; te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; return this; }, rotate: function ( theta ) { var c = Math.cos( theta ); var s = Math.sin( theta ); var te = this.elements; var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; te[ 0 ] = c * a11 + s * a21; te[ 3 ] = c * a12 + s * a22; te[ 6 ] = c * a13 + s * a23; te[ 1 ] = - s * a11 + c * a21; te[ 4 ] = - s * a12 + c * a22; te[ 7 ] = - s * a13 + c * a23; return this; }, translate: function ( tx, ty ) { var te = this.elements; te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ var textureId = 0; function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; this.mipmaps = []; this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; this.anisotropy = anisotropy !== undefined ? anisotropy : 1; this.format = format !== undefined ? format : RGBAFormat; this.type = type !== undefined ? type : UnsignedByteType; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding !== undefined ? encoding : LinearEncoding; this.version = 0; this.onUpdate = null; } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Texture, isTexture: true, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; return this; }, toJSON: function ( meta ) { var isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } function getDataURL( image ) { var canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } } if ( canvas.width > 2048 || canvas.height > 2048 ) { return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } var output = { metadata: { version: 4.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 ], minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY }; if ( this.image !== undefined ) { // TODO: Move to THREE.Image var image = this.image; if ( image.uuid === undefined ) { image.uuid = _Math.generateUUID(); // UGH } if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { meta.images[ image.uuid ] = { uuid: image.uuid, url: getDataURL( image ) }; } output.image = image.uuid; } if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); }, transformUv: function ( uv ) { if ( this.mapping !== UVMapping ) return; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } } } ); Object.defineProperty( Texture.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector4( x, y, z, w ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = ( w !== undefined ) ? w : 1; } Object.assign( Vector4.prototype, { isVector4: true, set: function ( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setW: function ( w ) { this.w = w; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z, this.w ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z, w = this.w; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, setAxisAngleFromQuaternion: function ( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); var s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; }, setAxisAngleFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var angle, x, y, z, // variables for result epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; var xx = ( m11 + 1 ) / 2; var yy = ( m22 + 1 ) / 2; var zz = ( m33 + 1 ) / 2; var xy = ( m12 + m21 ) / 4; var xz = ( m13 + m31 ) / 4; var yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector4(); max = new Vector4(); } min.set( minVal, minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector4: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } } ); /** * @author szimek / https://github.com/szimek/ * @author alteredq / http://alteredqualia.com/ * @author Marius Kintel / https://github.com/kintel */ /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ function WebGLRenderTarget( width, height, options ) { this.uuid = _Math.generateUUID(); this.width = width; this.height = height; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); options = options || {}; if ( options.minFilter === undefined ) options.minFilter = LinearFilter; this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: WebGLRenderTarget, isWebGLRenderTarget: true, setSize: function ( width, height ) { if ( this.width !== width || this.height !== height ) { this.width = width; this.height = height; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.width = source.width; this.height = source.height; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author alteredq / http://alteredqualia.com */ function WebGLRenderTargetCube( width, height, options ) { WebGLRenderTarget.call( this, width, height, options ); this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 this.activeMipMapLevel = 0; } WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; /** * @author alteredq / http://alteredqualia.com/ */ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } DataTexture.prototype = Object.create( Texture.prototype ); DataTexture.prototype.constructor = DataTexture; DataTexture.prototype.isDataTexture = true; /** * @author mrdoob / http://mrdoob.com/ */ function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.flipY = false; } CubeTexture.prototype = Object.create( Texture.prototype ); CubeTexture.prototype.constructor = CubeTexture; CubeTexture.prototype.isCubeTexture = true; Object.defineProperty( CubeTexture.prototype, "images", { get: function () { return this.image; }, set: function ( value ) { this.image = value; } } ); /** * @author tschw * * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program, renderer )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [renderer] ) * * uploads a uniform value(s) * the "renderer" parameter is needed for sampler uniforms * * * Static methods of the top-level container (renderer factorizations): * * .upload( gl, seq, values, renderer ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (renderer factorizations): * * .setValue( gl, name, value ) * * sets uniform with name "name" to "value" * * .set( gl, obj, prop ) * * sets uniform from object and property with same name than uniform * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ var emptyTexture = new Texture(); var emptyCubeTexture = new CubeTexture(); // --- Base for inner nodes (including the root) --- function UniformContainer() { this.seq = []; this.map = {}; } // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) var arrayCacheF32 = []; var arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms var mat4array = new Float32Array( 16 ); var mat3array = new Float32Array( 9 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { var firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 var n = nBlocks * blockSize, r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } // Texture unit allocation function allocTexUnits( renderer, n ) { var r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( var i = 0; i !== n; ++ i ) r[ i ] = renderer.allocTextureUnit(); return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); } function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); } // Single float vector (from flat array or THREE.VectorN) function setValue2fv( gl, v ) { if ( v.x === undefined ) { gl.uniform2fv( this.addr, v ); } else { gl.uniform2f( this.addr, v.x, v.y ); } } function setValue3fv( gl, v ) { if ( v.x !== undefined ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); } else if ( v.r !== undefined ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); } else { gl.uniform3fv( this.addr, v ); } } function setValue4fv( gl, v ) { if ( v.x === undefined ) { gl.uniform4fv( this.addr, v ); } else { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); } } // Single matrix (from flat array or MatrixN) function setValue2fm( gl, v ) { gl.uniformMatrix2fv( this.addr, false, v.elements || v ); } function setValue3fm( gl, v ) { if ( v.elements === undefined ) { gl.uniformMatrix3fv( this.addr, false, v ); } else { mat3array.set( v.elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); } } function setValue4fm( gl, v ) { if ( v.elements === undefined ) { gl.uniformMatrix4fv( this.addr, false, v ); } else { mat4array.set( v.elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); } } // Single texture (2D / Cube) function setValueT1( gl, v, renderer ) { var unit = renderer.allocTextureUnit(); gl.uniform1i( this.addr, unit ); renderer.setTexture2D( v || emptyTexture, unit ); } function setValueT6( gl, v, renderer ) { var unit = renderer.allocTextureUnit(); gl.uniform1i( this.addr, unit ); renderer.setTextureCube( v || emptyCubeTexture, unit ); } // Integer / Boolean vectors or arrays thereof (always flat arrays) function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValue1f; // FLOAT case 0x8b50: return setValue2fv; // _VEC2 case 0x8b51: return setValue3fv; // _VEC3 case 0x8b52: return setValue4fv; // _VEC4 case 0x8b5a: return setValue2fm; // _MAT2 case 0x8b5b: return setValue3fm; // _MAT3 case 0x8b5c: return setValue4fm; // _MAT4 case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES case 0x8b60: return setValueT6; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // Array of scalars function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ); } function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of vectors (flat or from THREE classes) function setValueV2a( gl, v ) { gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); } function setValueV3a( gl, v ) { gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); } function setValueV4a( gl, v ) { gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); } // Array of matrices (flat or from THREE clases) function setValueM2a( gl, v ) { gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); } function setValueM3a( gl, v ) { gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); } function setValueM4a( gl, v ) { gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); } // Array of textures (2D / Cube) function setValueT1a( gl, v, renderer ) { var n = v.length, units = allocTexUnits( renderer, n ); gl.uniform1iv( this.addr, units ); for ( var i = 0; i !== n; ++ i ) { renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT6a( gl, v, renderer ) { var n = v.length, units = allocTexUnits( renderer, n ); gl.uniform1iv( this.addr, units ); for ( var i = 0; i !== n; ++ i ) { renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValue1fv; // FLOAT case 0x8b50: return setValueV2a; // _VEC2 case 0x8b51: return setValueV3a; // _VEC3 case 0x8b52: return setValueV4a; // _VEC4 case 0x8b5a: return setValueM2a; // _MAT2 case 0x8b5b: return setValueM3a; // _MAT3 case 0x8b5c: return setValueM4a; // _MAT4 case 0x8b5e: return setValueT1a; // SAMPLER_2D case 0x8b60: return setValueT6a; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // --- Uniform Classes --- function SingleUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function StructuredUniform( id ) { this.id = id; UniformContainer.call( this ); // mix-in } StructuredUniform.prototype.setValue = function ( gl, value ) { // Note: Don"t need an extra "renderer" parameter, since samplers // are not allowed in structured uniforms. var seq = this.seq; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; u.setValue( gl, value[ u.id ] ); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings var RePathPart = /([wd_]+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { var path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; for ( ; ; ) { var match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex, id = match[ 1 ], idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist var map = container.map, next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container function WebGLUniforms( gl, program, renderer ) { UniformContainer.call( this ); this.renderer = renderer; var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( var i = 0; i < n; ++ i ) { var info = gl.getActiveUniform( program, i ), path = info.name, addr = gl.getUniformLocation( program, path ); parseUniform( info, addr, this ); } } WebGLUniforms.prototype.setValue = function ( gl, name, value ) { var u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, this.renderer ); }; WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { var v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); }; // Static interface WebGLUniforms.upload = function ( gl, seq, values, renderer ) { for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, renderer ); } } }; WebGLUniforms.seqWithValue = function ( seq, values ) { var r = []; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; }; /** * @author mrdoob / http://mrdoob.com/ */ var ColorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; function Color( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } Object.assign( Color.prototype, { isColor: true, r: 1, g: 1, b: 1, set: function ( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } return this; }, setScalar: function ( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; }, setHex: function ( hex ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; return this; }, setRGB: function ( r, g, b ) { this.r = r; this.g = g; this.b = b; return this; }, setHSL: function () { function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } return function setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 h = _Math.euclideanModulo( h, 1 ); s = _Math.clamp( s, 0, 1 ); l = _Math.clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); var q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } return this; }; }(), setStyle: function ( style ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } var m; if ( m = /^((?:rgb|hsl)a?)(s*([^)]*))/.exec( style ) ) { // rgb / hsl var color; var name = m[ 1 ]; var components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^(d+)s*,s*(d+)s*,s*(d+)s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; handleAlpha( color[ 5 ] ); return this; } if ( color = /^(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; handleAlpha( color[ 5 ] ); return this; } break; case "hsl": case "hsla": if ( color = /^([0-9]*.?[0-9]+)s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) var h = parseFloat( color[ 1 ] ) / 360; var s = parseInt( color[ 2 ], 10 ) / 100; var l = parseInt( color[ 3 ], 10 ) / 100; handleAlpha( color[ 5 ] ); return this.setHSL( h, s, l ); } break; } } else if ( m = /^#([A-Fa-f0-9]+)$/.exec( style ) ) { // hex color var hex = m[ 1 ]; var size = hex.length; if ( size === 3 ) { // #ff0 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; return this; } else if ( size === 6 ) { // #ff0000 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; return this; } } if ( style && style.length > 0 ) { // color keywords var hex = ColorKeywords[ style ]; if ( hex !== undefined ) { // red this.setHex( hex ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } } return this; }, clone: function () { return new this.constructor( this.r, this.g, this.b ); }, copy: function ( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; }, copyGammaToLinear: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; this.r = Math.pow( color.r, gammaFactor ); this.g = Math.pow( color.g, gammaFactor ); this.b = Math.pow( color.b, gammaFactor ); return this; }, copyLinearToGamma: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; this.r = Math.pow( color.r, safeInverse ); this.g = Math.pow( color.g, safeInverse ); this.b = Math.pow( color.b, safeInverse ); return this; }, convertGammaToLinear: function () { var r = this.r, g = this.g, b = this.b; this.r = r * r; this.g = g * g; this.b = b * b; return this; }, convertLinearToGamma: function () { this.r = Math.sqrt( this.r ); this.g = Math.sqrt( this.g ); this.b = Math.sqrt( this.b ); return this; }, getHex: function () { return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; }, getHexString: function () { return ( "000000" + this.getHex().toString( 16 ) ).slice( - 6 ); }, getHSL: function ( optionalTarget ) { // h,s,l ranges are in 0.0 - 1.0 var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; var r = this.r, g = this.g, b = this.b; var max = Math.max( r, g, b ); var min = Math.min( r, g, b ); var hue, saturation; var lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { var delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } hsl.h = hue; hsl.s = saturation; hsl.l = lightness; return hsl; }, getStyle: function () { return "rgb(" + ( ( this.r * 255 ) | 0 ) + "," + ( ( this.g * 255 ) | 0 ) + "," + ( ( this.b * 255 ) | 0 ) + ")"; }, offsetHSL: function ( h, s, l ) { var hsl = this.getHSL(); hsl.h += h; hsl.s += s; hsl.l += l; this.setHSL( hsl.h, hsl.s, hsl.l ); return this; }, add: function ( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; }, addColors: function ( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; }, addScalar: function ( s ) { this.r += s; this.g += s; this.b += s; return this; }, sub: function ( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; }, multiply: function ( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; }, multiplyScalar: function ( s ) { this.r *= s; this.g *= s; this.b *= s; return this; }, lerp: function ( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; }, equals: function ( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; }, toJSON: function () { return this.getHex(); } } ); /** * Uniforms library for shared webgl shaders */ var UniformsLib = { common: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, alphaMap: { value: null }, }, specularmap: { specularMap: { value: null }, }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, refractionRatio: { value: 0.98 } }, 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 Vector2( 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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color( 0xffffff ) } }, lights: { ambientLightColor: { value: [] }, directionalLights: { value: [], properties: { direction: {}, color: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, directionalShadowMap: { value: [] }, directionalShadowMatrix: { value: [] }, spotLights: { value: [], properties: { color: {}, position: {}, direction: {}, distance: {}, coneCos: {}, penumbraCos: {}, decay: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, spotShadowMap: { value: [] }, spotShadowMatrix: { value: [] }, pointLights: { value: [], properties: { color: {}, position: {}, decay: {}, distance: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {}, shadowCameraNear: {}, shadowCameraFar: {} } }, pointShadowMap: { value: [] }, pointShadowMatrix: { value: [] }, hemisphereLights: { value: [], properties: { direction: {}, skyColor: {}, groundColor: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } } }, points: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() } } }; /** * Uniform Utilities */ var UniformsUtils = { merge: function ( uniforms ) { var merged = {}; for ( var u = 0; u < uniforms.length; u ++ ) { var tmp = this.clone( uniforms[ u ] ); for ( var p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; }, clone: function ( uniforms_src ) { var uniforms_dst = {}; for ( var u in uniforms_src ) { uniforms_dst[ u ] = {}; for ( var p in uniforms_src[ u ] ) { var parameter_src = uniforms_src[ u ][ p ]; if ( parameter_src && ( parameter_src.isColor || parameter_src.isMatrix3 || parameter_src.isMatrix4 || parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || parameter_src.isTexture ) ) { uniforms_dst[ u ][ p ] = parameter_src.clone(); } else if ( Array.isArray( parameter_src ) ) { uniforms_dst[ u ][ p ] = parameter_src.slice(); } else { uniforms_dst[ u ][ p ] = parameter_src; } } } return uniforms_dst; } }; var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif "; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif "; var alphatest_fragment = "#ifdef ALPHATEST if ( diffuseColor.a < ALPHATEST ) discard; #endif "; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( PHYSICAL ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness ); #endif #endif "; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = " vec3 transformed = vec3( position ); "; var beginnormal_vertex = " vec3 objectNormal = vec3( normal ); "; var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { if( decayExponent > 0.0 ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); float maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); return distanceFalloff * maxDistanceCutoffFactor; #else return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent ); #endif } return 1.0; } vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) { float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH ); return ( 1.0 - specularColor ) * fresnel + specularColor; } float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); return 1.0 / ( gl * gv ); } float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) ); float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( G * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float theta = acos( dot( N, V ) ); vec2 uv = vec2( sqrt( saturate( roughness ) ), saturate( theta / ( 0.5 * PI ) ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.86267 + (0.49788 + 0.01436 * y ) * y; float b = 3.45068 + (4.18814 + y) * y; float v = a / b; float theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt( 1.0 - x * x ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); vec3 result = vec3( LTC_ClippedSphereFormFactor( vectorFormFactor ) ); return result; } vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; return specularColor * AB.x + AB.y; } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) { return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 ); } float BlinnExponentToGGXRoughness( const in float blinnExponent ) { return sqrt( 2.0 / ( blinnExponent + 2.0 ) ); } "; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ); vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif "; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 for ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) { vec4 plane = clippingPlanes[ i ]; if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard; } #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) { vec4 plane = clippingPlanes[ i ]; clipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped; } if ( clipped ) discard; #endif #endif "; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 #if ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif "; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif "; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) vViewPosition = - mvPosition.xyz; #endif "; var color_fragment = "#ifdef USE_COLOR diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#ifdef USE_COLOR varying vec3 vColor; #endif "; var color_pars_vertex = "#ifdef USE_COLOR varying vec3 vColor; #endif"; var color_vertex = "#ifdef USE_COLOR vColor.xyz = color.xyz; #endif"; var common = "#define PI 3.14159265359 #define PI2 6.28318530718 #define PI_HALF 1.5707963267949 #define RECIPROCAL_PI 0.31830988618 #define RECIPROCAL_PI2 0.15915494 #define LOG2 1.442695 #define EPSILON 1e-6 #define saturate(a) clamp( a, 0.0, 1.0 ) #define whiteCompliment(a) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract(sin(sn) * c); } struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { float distance = dot( planeNormal, point - pointOnPlane ); return - distance * planeNormal + point; } float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { return sign( dot( point - pointOnPlane, planeNormal ) ); } vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) { return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine; } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } "; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_textureSize (1024.0) int getFaceFromDirection(vec3 direction) { vec3 absDirection = abs(direction); int face = -1; if( absDirection.x > absDirection.z ) { if(absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0 : 3; else face = direction.y > 0.0 ? 1 : 4; } else { if(absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2 : 5; else face = direction.y > 0.0 ? 1 : 4; } return face; } #define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0) #define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0)) vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) { float scale = exp2(cubeUV_maxLods1 - roughnessLevel); float dxRoughness = dFdx(roughness); float dyRoughness = dFdy(roughness); vec3 dx = dFdx( vec * scale * dxRoughness ); vec3 dy = dFdy( vec * scale * dyRoughness ); float d = max( dot( dx, dx ), dot( dy, dy ) ); d = clamp(d, 1.0, cubeUV_rangeClamp); float mipLevel = 0.5 * log2(d); return vec2(floor(mipLevel), fract(mipLevel)); } #define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0) #define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize) vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) { mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel; float a = 16.0 * cubeUV_rcpTextureSize; vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) ); vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed; float powScale = exp2_packed.x * exp2_packed.y; float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25; float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x; bool bRes = mipLevel == 0.0; scale = bRes && (scale < a) ? a : scale; vec3 r; vec2 offset; int face = getFaceFromDirection(direction); float rcpPowScale = 1.0 / powScale; if( face == 0) { r = vec3(direction.x, -direction.z, direction.y); offset = vec2(0.0+mipOffset,0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 1) { r = vec3(direction.y, direction.x, direction.z); offset = vec2(scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 2) { r = vec3(direction.z, direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 3) { r = vec3(direction.x, direction.z, direction.y); offset = vec2(0.0+mipOffset,0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else if( face == 4) { r = vec3(direction.y, direction.x, -direction.z); offset = vec2(scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else { r = vec3(direction.z, -direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } r = normalize(r); float texelOffset = 0.5 * cubeUV_rcpTextureSize; vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5; vec2 base = offset + vec2( texelOffset ); return base + s * ( scale - 2.0 * texelOffset ); } #define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0) vec4 textureCubeUV(vec3 reflectedDirection, float roughness ) { float roughnessVal = roughness* cubeUV_maxLods3; float r1 = floor(roughnessVal); float r2 = r1 + 1.0; float t = fract(roughnessVal); vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness); float s = mipInfo.y; float level0 = mipInfo.x; float level1 = level0 + 1.0; level1 = level1 > 5.0 ? 5.0 : level1; level0 += min( floor( s + 0.5 ), 5.0 ); vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0); vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10)); vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0); vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20)); vec4 result = mix(color10, color20, t); return vec4(result.rgb, 1.0); } #endif "; var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif "; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif "; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias ); #endif "; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb; totalEmissiveRadiance *= emissiveColor.rgb; #endif "; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif "; var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor ); "; var encodings_pars_fragment = " vec4 LinearToLinear( in vec4 value ) { return value; } vec4 GammaToLinear( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w ); } vec4 LinearToGamma( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w ); } vec4 sRGBToLinear( in vec4 value ) { return 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.w ); } vec4 LinearTosRGB( in vec4 value ) { return 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.w ); } vec4 RGBEToLinear( in vec4 value ) { return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 ); } vec4 LinearToRGBE( in vec4 value ) { float maxComponent = max( max( value.r, value.g ), value.b ); float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 ); return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 ); } vec4 RGBMToLinear( in vec4 value, in float maxRange ) { return vec4( value.xyz * value.w * maxRange, 1.0 ); } vec4 LinearToRGBM( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float M = clamp( maxRGB / maxRange, 0.0, 1.0 ); M = ceil( M * 255.0 ) / 255.0; return vec4( value.rgb / ( M * maxRange ), M ); } vec4 RGBDToLinear( in vec4 value, in float maxRange ) { return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 ); } vec4 LinearToRGBD( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float D = max( maxRange / maxRGB, 1.0 ); D = min( floor( D ) / 255.0, 1.0 ); return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D ); } const mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 ); vec4 LinearToLogLuv( in vec4 value ) { vec3 Xp_Y_XYZp = value.rgb * cLogLuvM; Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6)); vec4 vResult; vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0; vResult.w = fract(Le); vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0; return vResult; } const mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 ); vec4 LogLuvToLinear( in vec4 value ) { float Le = value.z * 255.0 + value.w; vec3 Xp_Y_XYZp; Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0); Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y; Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z; vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM; return vec4( max(vRGB, 0.0), 1.0 ); } "; var envmap_fragment = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition ); vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToVertex, worldNormal ); #else vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; reflectVec = normalize( reflectVec ); sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; vec4 envColor = texture2D( envMap, sampleUV ); #elif defined( ENVMAP_TYPE_SPHERE ) reflectVec = normalize( reflectVec ); vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ); vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 ); #else vec4 envColor = vec4( 0.0 ); #endif envColor = envMapTexelToLinear( envColor ); #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif "; var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL ) uniform float reflectivity; uniform float envMapIntensity; #endif #ifdef USE_ENVMAP #if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) ) varying vec3 vWorldPosition; #endif #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif uniform float flipEnvMap; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL ) uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif "; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif "; var envmap_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif "; var fog_vertex = " #ifdef USE_FOG fogDepth = -mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float fogDepth; #endif "; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) ); #else float fogFactor = smoothstep( fogNear, fogFar, fogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif "; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float fogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif "; var gradientmap_pars_fragment = "#ifdef TOON uniform sampler2D gradientMap; vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return texture2D( gradientMap, coord ).rgb; #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif } #endif "; var lightmap_fragment = "#ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #endif "; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; #if NUM_POINT_LIGHTS > 0 for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointDirectLightIrradiance( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_SPOT_LIGHTS > 0 for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_DIR_LIGHTS > 0 for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_HEMI_LIGHTS > 0 for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); #ifdef DOUBLE_SIDED vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry ); #endif } #endif "; var lights_pars = "uniform vec3 ambientLightColor; vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) { directLight.color = directionalLight.color; directLight.direction = directionalLight.direction; directLight.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = pointLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); directLight.color = pointLight.color; directLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay ); directLight.visible = ( directLight.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = spotLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); float angleCos = dot( directLight.direction, spotLight.direction ); if ( angleCos > spotLight.coneCos ) { float spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos ); directLight.color = spotLight.color; directLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay ); directLight.visible = true; } else { directLight.color = vec3( 0.0 ); directLight.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltcMat; uniform sampler2D ltcMag; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) { float dotNL = dot( geometry.normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #endif #if defined( USE_ENVMAP ) && defined( PHYSICAL ) vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) { vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix ); #ifdef ENVMAP_TYPE_CUBE vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) ); #else vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); vec4 envMapColor = textureCubeUV( queryVec, 1.0 ); #else vec4 envMapColor = vec4( 0.0 ); #endif return PI * envMapColor.rgb * envMapIntensity; } float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) { float maxMIPLevelScalar = float( maxMIPLevel ); float desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 ); return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar ); } vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) { #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal ); #else vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel ); #ifdef ENVMAP_TYPE_CUBE vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel ); #else vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent)); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_SPHERE ) vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #endif return envMapColor.rgb * envMapIntensity; } #endif "; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength; "; var lights_phong_pars_fragment = "varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { #ifdef TOON vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; #else float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #endif #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0) "; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 ); #ifdef STANDARD material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor ); material.clearCoat = saturate( clearCoat ); material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 ); #endif "; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float specularRoughness; vec3 specularColor; #ifndef STANDARD float clearCoat; float clearCoatRoughness; #endif }; #define MAXIMUM_SPECULAR_COEFFICIENT 0.16 #define DEFAULT_SPECULAR_COEFFICIENT 0.04 float clearCoatDHRApprox( const in float roughness, const in float dotNL ) { return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) ); } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.specularRoughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos - halfWidth - halfHeight; rectCoords[ 1 ] = lightPos + halfWidth - halfHeight; rectCoords[ 2 ] = lightPos + halfWidth + halfHeight; rectCoords[ 3 ] = lightPos - halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); float norm = texture2D( ltcMag, uv ).a; vec4 t = texture2D( ltcMat, uv ); mat3 mInv = mat3( vec3( 1, 0, t.y ), vec3( 0, t.z, 0 ), vec3( t.w, 0, t.x ) ); reflectedLight.directSpecular += lightColor * material.specularColor * norm * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif #ifndef STANDARD float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness ); reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); #ifndef STANDARD reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { #ifndef STANDARD float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNL = dotNV; float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness ); #ifndef STANDARD reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical #define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness ) #define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness ) float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); } "; var lights_template = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = normalize( vViewPosition ); IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointDirectLightIrradiance( pointLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotDirectLightIrradiance( spotLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #endif #if defined( RE_IndirectDiffuse ) vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); #ifdef USE_LIGHTMAP vec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if ( NUM_HEMI_LIGHTS > 0 ) for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); } #endif #if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV ) irradiance += getLightProbeIndirectIrradiance( geometry, 8 ); #endif RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) vec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 ); #ifndef STANDARD vec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 ); #else vec3 clearCoatRadiance = vec3( 0.0 ); #endif RE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight ); #endif "; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF uniform float logDepthBufFC; #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif #endif "; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif uniform float logDepthBufFC; #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; #else gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; #endif #endif "; var map_fragment = "#ifdef USE_MAP vec4 texelColor = texture2D( map, vUv ); texelColor = mapTexelToLinear( texelColor ); diffuseColor *= texelColor; #endif "; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif "; var map_particle_fragment = "#ifdef USE_MAP vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; vec4 mapTexel = texture2D( map, uv ); diffuseColor *= mapTexelToLinear( mapTexel ); #endif "; var map_particle_pars_fragment = "#ifdef USE_MAP uniform mat3 uvTransform; uniform sampler2D map; #endif "; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif "; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ]; objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ]; objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ]; objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ]; #endif "; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ]; transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ]; transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ]; transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ]; transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ]; transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ]; transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ]; #endif #endif "; var normal_fragment = "#ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 ); #endif #endif #ifdef USE_NORMALMAP normal = perturbNormal2Arb( -vViewPosition, normal ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() ); #endif "; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); vec3 S = normalize( q0 * st1.t - q1 * st0.t ); vec3 T = normalize( -q0 * st1.s + q1 * st0.s ); vec3 N = normalize( surf_norm ); vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy = normalScale * mapN.xy; mat3 tsn = mat3( S, T, N ); return normalize( tsn * mapN ); } #endif "; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return (( near + viewZ ) * far ) / (( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); } "; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif "; var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); gl_Position = projectionMatrix * mvPosition; "; var dithering_fragment = "#if defined( DITHERING ) gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif "; var dithering_pars_fragment = "#if defined( DITHERING ) vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif "; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif "; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) { const vec2 offset = vec2( 0.0, 1.0 ); vec2 texelSize = vec2( 1.0 ) / size; vec2 centroidUV = floor( uv * size + 0.5 ) / size; float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare ); float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare ); float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare ); float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare ); vec2 f = fract( uv * size + 0.5 ); float a = mix( lb, lt, f.y ); float b = mix( rb, rt, f.y ); float c = mix( a, b, f.x ); return c; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif "; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif #endif "; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition; } #endif #if NUM_SPOT_LIGHTS > 0 for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition; } #endif #if NUM_POINT_LIGHTS > 0 for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition; } #endif #endif "; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 DirectionalLight directionalLight; for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #endif #if NUM_SPOT_LIGHTS > 0 SpotLight spotLight; for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #endif #if NUM_POINT_LIGHTS > 0 PointLight pointLight; for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #endif #endif return shadow; } "; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif "; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif "; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #endif "; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif "; var tonemapping_pars_fragment = "#ifndef saturate #define saturate(a) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; uniform float toneMappingWhitePoint; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } #define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) ) vec3 Uncharted2ToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } "; var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; uniform mat3 uvTransform; #endif "; var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = uv2; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 ); #endif "; var cube_frag = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldPosition; void main() { gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) ); gl_FragColor.a *= opacity; } "; var cube_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; } "; var depth_frag = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( gl_FragCoord.z ); #endif } "; var depth_vert = "#include #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include } "; var distanceRGBA_frag = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); } "; var distanceRGBA_vert = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; } "; var equirect_frag = "uniform sampler2D tEquirect; varying vec3 vWorldPosition; #include void main() { vec3 direction = normalize( vWorldPosition ); vec2 sampleUV; sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5; gl_FragColor = texture2D( tEquirect, sampleUV ); } "; var equirect_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include } "; var linedashed_frag = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var linedashed_vert = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include void main() { #include vLineDistance = scale * lineDistance; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; #include #include #include } "; var meshbasic_frag = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var meshbasic_vert = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_ENVMAP #include #include #include #include #endif #include #include #include #include #include #include #include #include #include } "; var meshlambert_frag = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor ); #include reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshlambert_vert = "#define LAMBERT varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } "; var meshphong_frag = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphong_vert = "#define PHONG varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include } "; var meshphysical_frag = "#define PHYSICAL uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifndef STANDARD uniform float clearCoat; uniform float clearCoatRoughness; #endif varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphysical_vert = "#define PHYSICAL varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include } "; var normal_frag = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); } "; var normal_vert = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif } "; var points_frag = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var points_vert = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #ifdef USE_SIZEATTENUATION gl_PointSize = size * ( scale / - mvPosition.z ); #else gl_PointSize = size; #endif #include #include #include #include #include } "; var shadow_frag = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include } "; var shadow_vert = "#include #include void main() { #include #include #include #include #include } "; var ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars: lights_pars, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_template: lights_template, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment: normal_fragment, normalmap_pars_fragment: normalmap_pars_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, cube_frag: cube_frag, cube_vert: cube_vert, depth_frag: depth_frag, depth_vert: depth_vert, distanceRGBA_frag: distanceRGBA_frag, distanceRGBA_vert: distanceRGBA_vert, equirect_frag: equirect_frag, equirect_vert: equirect_vert, linedashed_frag: linedashed_frag, linedashed_vert: linedashed_vert, meshbasic_frag: meshbasic_frag, meshbasic_vert: meshbasic_vert, meshlambert_frag: meshlambert_frag, meshlambert_vert: meshlambert_vert, meshphong_frag: meshphong_frag, meshphong_vert: meshphong_vert, meshphysical_frag: meshphysical_frag, meshphysical_vert: meshphysical_vert, normal_frag: normal_frag, normal_vert: normal_vert, points_frag: points_frag, points_vert: points_vert, shadow_frag: shadow_frag, shadow_vert: shadow_vert }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ */ var ShaderLib = { basic: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, specular: { value: new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, roughness: { value: 0.5 }, metalness: { value: 0.5 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, points: { uniforms: UniformsUtils.merge( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.normal_vert, fragmentShader: ShaderChunk.normal_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: UniformsUtils.merge( [ UniformsLib.lights, UniformsLib.fog, { color: { value: new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: UniformsUtils.merge( [ ShaderLib.standard.uniforms, { clearCoat: { value: 0 }, clearCoatRoughness: { value: 0 } } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; /** * @author bhouston / http://clara.io */ function Box2( min, max ) { this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); } Object.assign( Box2.prototype, { set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector2(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector2(); return this.isEmpty() ? result.set( 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( optionalTarget ) { var result = optionalTarget || new Vector2(); return this.isEmpty() ? result.set( 0, 0 ) : result.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; }, getParameter: function ( point, optionalTarget ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. var result = optionalTarget || new Vector2(); return result.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); }, intersectsBox: function ( box ) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; }, clampPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector2(); return result.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector2(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function WebGLFlareRenderer( renderer, gl, state, textures, capabilities ) { var vertexBuffer, elementBuffer; var shader, program, attributes, uniforms; var tempTexture, occlusionTexture; function init() { var vertices = new Float32Array( [ - 1, - 1, 0, 0, 1, - 1, 1, 0, 1, 1, 1, 1, - 1, 1, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); // buffers vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); // textures tempTexture = gl.createTexture(); occlusionTexture = gl.createTexture(); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); shader = { vertexShader: [ "uniform lowp int renderType;", "uniform vec3 screenPosition;", "uniform vec2 scale;", "uniform float rotation;", "uniform sampler2D occlusionMap;", "attribute vec2 position;", "attribute vec2 uv;", "varying vec2 vUV;", "varying float vVisibility;", "void main() {", " vUV = uv;", " vec2 pos = position;", " if ( renderType == 2 ) {", " vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", " visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", " visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", " visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", " visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", " visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", " visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", " visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", " visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", " vVisibility = visibility.r / 9.0;", " vVisibility *= 1.0 - visibility.g / 9.0;", " vVisibility *= visibility.b / 9.0;", " vVisibility *= 1.0 - visibility.a / 9.0;", " pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", " pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", " }", " gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", "}" ].join( " " ), fragmentShader: [ "uniform lowp int renderType;", "uniform sampler2D map;", "uniform float opacity;", "uniform vec3 color;", "varying vec2 vUV;", "varying float vVisibility;", "void main() {", // pink square " if ( renderType == 0 ) {", " gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", // restore " } else if ( renderType == 1 ) {", " gl_FragColor = texture2D( map, vUV );", // flare " } else {", " vec4 texture = texture2D( map, vUV );", " texture.a *= opacity * vVisibility;", " gl_FragColor = texture;", " gl_FragColor.rgb *= color;", " }", "}" ].join( " " ) }; program = createProgram( shader ); attributes = { vertex: gl.getAttribLocation( program, "position" ), uv: gl.getAttribLocation( program, "uv" ) }; uniforms = { renderType: gl.getUniformLocation( program, "renderType" ), map: gl.getUniformLocation( program, "map" ), occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), opacity: gl.getUniformLocation( program, "opacity" ), color: gl.getUniformLocation( program, "color" ), scale: gl.getUniformLocation( program, "scale" ), rotation: gl.getUniformLocation( program, "rotation" ), screenPosition: gl.getUniformLocation( program, "screenPosition" ) }; } /* * Render lens flares * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, * reads these back and calculates occlusion. */ this.render = function ( flares, scene, camera, viewport ) { if ( flares.length === 0 ) return; var tempPosition = new Vector3(); var invAspect = viewport.w / viewport.z, halfViewportWidth = viewport.z * 0.5, halfViewportHeight = viewport.w * 0.5; var size = 16 / viewport.w, scale = new Vector2( size * invAspect, size ); var screenPosition = new Vector3( 1, 1, 0 ), screenPositionPixels = new Vector2( 1, 1 ); var validArea = new Box2(); validArea.min.set( viewport.x, viewport.y ); validArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) ); if ( program === undefined ) { init(); } state.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.vertex ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); // loop through all lens flares to update their occlusion and positions // setup gl and common used attribs/uniforms gl.uniform1i( uniforms.occlusionMap, 0 ); gl.uniform1i( uniforms.map, 1 ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); state.disable( gl.CULL_FACE ); state.buffers.depth.setMask( false ); for ( var i = 0, l = flares.length; i < l; i ++ ) { size = 16 / viewport.w; scale.set( size * invAspect, size ); // calc object screen position var flare = flares[ i ]; tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); tempPosition.applyMatrix4( camera.matrixWorldInverse ); tempPosition.applyMatrix4( camera.projectionMatrix ); // setup arrays for gl programs screenPosition.copy( tempPosition ); // horizontal and vertical coordinate of the lower left corner of the pixels to copy screenPositionPixels.x = viewport.x + ( screenPosition.x * halfViewportWidth ) + halfViewportWidth - 8; screenPositionPixels.y = viewport.y + ( screenPosition.y * halfViewportHeight ) + halfViewportHeight - 8; // screen cull if ( validArea.containsPoint( screenPositionPixels ) === true ) { // save current RGB to temp texture state.activeTexture( gl.TEXTURE0 ); state.bindTexture( gl.TEXTURE_2D, null ); state.activeTexture( gl.TEXTURE1 ); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); // render pink quad gl.uniform1i( uniforms.renderType, 0 ); gl.uniform2f( uniforms.scale, scale.x, scale.y ); gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); state.disable( gl.BLEND ); state.enable( gl.DEPTH_TEST ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // copy result to occlusionMap state.activeTexture( gl.TEXTURE0 ); state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); // restore graphics gl.uniform1i( uniforms.renderType, 1 ); state.disable( gl.DEPTH_TEST ); state.activeTexture( gl.TEXTURE1 ); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // update object positions flare.positionScreen.copy( screenPosition ); if ( flare.customUpdateCallback ) { flare.customUpdateCallback( flare ); } else { flare.updateLensFlares(); } // render flares gl.uniform1i( uniforms.renderType, 2 ); state.enable( gl.BLEND ); for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { var sprite = flare.lensFlares[ j ]; if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { screenPosition.x = sprite.x; screenPosition.y = sprite.y; screenPosition.z = sprite.z; size = sprite.size * sprite.scale / viewport.w; scale.x = size * invAspect; scale.y = size; gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); gl.uniform2f( uniforms.scale, scale.x, scale.y ); gl.uniform1f( uniforms.rotation, sprite.rotation ); gl.uniform1f( uniforms.opacity, sprite.opacity ); gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); textures.setTexture2D( sprite.texture, 1 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); } } } } // restore gl state.enable( gl.CULL_FACE ); state.enable( gl.DEPTH_TEST ); state.buffers.depth.setMask( true ); state.reset(); }; function createProgram( shader ) { var program = gl.createProgram(); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var prefix = "precision " + capabilities.precision + " float; "; gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); gl.shaderSource( vertexShader, prefix + shader.vertexShader ); gl.compileShader( fragmentShader ); gl.compileShader( vertexShader ); gl.attachShader( program, fragmentShader ); gl.attachShader( program, vertexShader ); gl.linkProgram( program ); return program; } } /** * @author mrdoob / http://mrdoob.com/ */ function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.needsUpdate = true; } CanvasTexture.prototype = Object.create( Texture.prototype ); CanvasTexture.prototype.constructor = CanvasTexture; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) { var vertexBuffer, elementBuffer; var program, attributes, uniforms; var texture; // decompose matrixWorld var spritePosition = new Vector3(); var spriteRotation = new Quaternion(); var spriteScale = new Vector3(); function init() { var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0.5, - 0.5, 1, 0, 0.5, 0.5, 1, 1, - 0.5, 0.5, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); program = createProgram(); attributes = { position: gl.getAttribLocation( program, "position" ), uv: gl.getAttribLocation( program, "uv" ) }; uniforms = { uvOffset: gl.getUniformLocation( program, "uvOffset" ), uvScale: gl.getUniformLocation( program, "uvScale" ), rotation: gl.getUniformLocation( program, "rotation" ), scale: gl.getUniformLocation( program, "scale" ), color: gl.getUniformLocation( program, "color" ), map: gl.getUniformLocation( program, "map" ), opacity: gl.getUniformLocation( program, "opacity" ), modelViewMatrix: gl.getUniformLocation( program, "modelViewMatrix" ), projectionMatrix: gl.getUniformLocation( program, "projectionMatrix" ), fogType: gl.getUniformLocation( program, "fogType" ), fogDensity: gl.getUniformLocation( program, "fogDensity" ), fogNear: gl.getUniformLocation( program, "fogNear" ), fogFar: gl.getUniformLocation( program, "fogFar" ), fogColor: gl.getUniformLocation( program, "fogColor" ), fogDepth: gl.getUniformLocation( program, "fogDepth" ), alphaTest: gl.getUniformLocation( program, "alphaTest" ) }; var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = 8; canvas.height = 8; var context = canvas.getContext( "2d" ); context.fillStyle = "white"; context.fillRect( 0, 0, 8, 8 ); texture = new CanvasTexture( canvas ); } this.render = function ( sprites, scene, camera ) { if ( sprites.length === 0 ) return; // setup gl if ( program === undefined ) { init(); } state.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.position ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); state.disable( gl.CULL_FACE ); state.enable( gl.BLEND ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); state.activeTexture( gl.TEXTURE0 ); gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; var sceneFogType = 0; var fog = scene.fog; if ( fog ) { gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); if ( fog.isFog ) { gl.uniform1f( uniforms.fogNear, fog.near ); gl.uniform1f( uniforms.fogFar, fog.far ); gl.uniform1i( uniforms.fogType, 1 ); oldFogType = 1; sceneFogType = 1; } else if ( fog.isFogExp2 ) { gl.uniform1f( uniforms.fogDensity, fog.density ); gl.uniform1i( uniforms.fogType, 2 ); oldFogType = 2; sceneFogType = 2; } } else { gl.uniform1i( uniforms.fogType, 0 ); oldFogType = 0; sceneFogType = 0; } // update positions and sort for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; } sprites.sort( painterSortStable ); // render all sprites var scale = []; for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; var material = sprite.material; if ( material.visible === false ) continue; sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined ); gl.uniform1f( uniforms.alphaTest, material.alphaTest ); gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); scale[ 0 ] = spriteScale.x; scale[ 1 ] = spriteScale.y; var fogType = 0; if ( scene.fog && material.fog ) { fogType = sceneFogType; } if ( oldFogType !== fogType ) { gl.uniform1i( uniforms.fogType, fogType ); oldFogType = fogType; } if ( material.map !== null ) { gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { gl.uniform2f( uniforms.uvOffset, 0, 0 ); gl.uniform2f( uniforms.uvScale, 1, 1 ); } gl.uniform1f( uniforms.opacity, material.opacity ); gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); gl.uniform1f( uniforms.rotation, material.rotation ); gl.uniform2fv( uniforms.scale, scale ); state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); state.buffers.depth.setTest( material.depthTest ); state.buffers.depth.setMask( material.depthWrite ); state.buffers.color.setMask( material.colorWrite ); textures.setTexture2D( material.map || texture, 0 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined ); } // restore gl state.enable( gl.CULL_FACE ); state.reset(); }; function createProgram() { var program = gl.createProgram(); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); gl.shaderSource( vertexShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform float rotation;", "uniform vec2 scale;", "uniform vec2 uvOffset;", "uniform vec2 uvScale;", "attribute vec2 position;", "attribute vec2 uv;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vUV = uvOffset + uv * uvScale;", " vec2 alignedPosition = position * scale;", " vec2 rotatedPosition;", " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", " vec4 mvPosition;", " mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", " mvPosition.xy += rotatedPosition;", " gl_Position = projectionMatrix * mvPosition;", " fogDepth = - mvPosition.z;", "}" ].join( " " ) ); gl.shaderSource( fragmentShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform vec3 color;", "uniform sampler2D map;", "uniform float opacity;", "uniform int fogType;", "uniform vec3 fogColor;", "uniform float fogDensity;", "uniform float fogNear;", "uniform float fogFar;", "uniform float alphaTest;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vec4 texture = texture2D( map, vUV );", " gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", " if ( gl_FragColor.a < alphaTest ) discard;", " if ( fogType > 0 ) {", " float fogFactor = 0.0;", " if ( fogType == 1 ) {", " fogFactor = smoothstep( fogNear, fogFar, fogDepth );", " } else {", " const float LOG2 = 1.442695;", " fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );", " fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", " }", " gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );", " }", "}" ].join( " " ) ); gl.compileShader( vertexShader ); gl.compileShader( fragmentShader ); gl.attachShader( program, vertexShader ); gl.attachShader( program, fragmentShader ); gl.linkProgram( program ); return program; } function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return b.id - a.id; } } } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ var materialId = 0; function Material() { Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.lights = true; this.blending = NormalBlending; this.side = FrontSide; this.flatShading = false; this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaTest = 0; this.premultipliedAlpha = false; this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer this.visible = true; this.userData = {}; this.needsUpdate = true; } Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Material, isMaterial: true, onBeforeCompile: function () {}, setValues: function ( values ) { if ( values === undefined ) return; for ( var key in values ) { var newValue = values[ key ]; if ( newValue === undefined ) { console.warn( "THREE.Material: "" + key + "" parameter is undefined." ); continue; } // for backward compatability if shading is set in the constructor if ( key === "shading" ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( newValue === FlatShading ) ? true : false; continue; } var currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( "THREE." + this.type + ": "" + key + "" is not a property of this material." ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else if ( key === "overdraw" ) { // ensure overdraw is backwards-compatible with legacy boolean type this[ key ] = Number( newValue ); } else { this[ key ] = newValue; } } }, toJSON: function ( meta ) { var isRoot = ( meta === undefined || typeof meta === "string" ); if ( isRoot ) { meta = { textures: {}, images: {} }; } var data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; data.reflectivity = this.reflectivity; // Scale behind envMap } if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.size !== undefined ) data.size = this.size; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; // rotation (SpriteMaterial) if ( this.rotation !== 0 ) data.rotation = this.rotation; if ( this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.morphTargets === true ) data.morphTargets = true; if ( this.skinning === true ) data.skinning = true; if ( this.visible === false ) data.visible = false; if ( JSON.stringify( this.userData ) !== "{}" ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRoot ) { var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.fog = source.fog; this.lights = source.lights; this.blending = source.blending; this.side = source.side; this.flatShading = source.flatShading; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.premultipliedAlpha = source.premultipliedAlpha; this.overdraw = source.overdraw; this.visible = source.visible; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.clipShadows = source.clipShadows; this.clipIntersection = source.clipIntersection; var srcPlanes = source.clippingPlanes, dstPlanes = null; if ( srcPlanes !== null ) { var n = srcPlanes.length; dstPlanes = new Array( n ); for ( var i = 0; i !== n; ++ i ) dstPlanes[ i ] = srcPlanes[ i ].clone(); } this.clippingPlanes = dstPlanes; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / https://clara.io * @author WestLangley / http://github.com/WestLangley * * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ function MeshDepthMaterial( parameters ) { Material.call( this ); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDepthMaterial.prototype = Object.create( Material.prototype ); MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; MeshDepthMaterial.prototype.isMeshDepthMaterial = true; MeshDepthMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.depthPacking = source.depthPacking; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * skinning: , * morphTargets: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ function MeshDistanceMaterial( parameters ) { Material.call( this ); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDistanceMaterial.prototype = Object.create( Material.prototype ); MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; MeshDistanceMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.referencePosition.copy( source.referencePosition ); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; }; /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Box3( min, max ) { this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); } Object.assign( Box3.prototype, { isBox3: true, set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromArray: function ( array ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = array.length; i < l; i += 3 ) { var x = array[ i ]; var y = array[ i + 1 ]; var z = array[ i + 2 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromBufferAttribute: function ( attribute ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = attribute.count; i < l; i ++ ) { var x = attribute.getX( i ); var y = attribute.getY( i ); var z = attribute.getZ( i ); if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector3(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), setFromObject: function ( object ) { this.makeEmpty(); return this.expandByObject( object ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return this.isEmpty() ? result.set( 0, 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return this.isEmpty() ? result.set( 0, 0, 0 ) : result.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, expandByObject: function () { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms var scope, i, l; var v1 = new Vector3(); function traverse( node ) { var geometry = node.geometry; if ( geometry !== undefined ) { if ( geometry.isGeometry ) { var vertices = geometry.vertices; for ( i = 0, l = vertices.length; i < l; i ++ ) { v1.copy( vertices[ i ] ); v1.applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } else if ( geometry.isBufferGeometry ) { var attribute = geometry.attributes.position; if ( attribute !== undefined ) { for ( i = 0, l = attribute.count; i < l; i ++ ) { v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } } } } return function expandByObject( object ) { scope = this; object.updateMatrixWorld( true ); object.traverse( traverse ); return this; }; }(), containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; }, getParameter: function ( point, optionalTarget ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. var result = optionalTarget || new Vector3(); return result.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); }, intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; }, intersectsSphere: ( function () { var closestPoint = new Vector3(); return function intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, closestPoint ); // If that point is inside the sphere, the AABB and sphere intersect. return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); }; } )(), intersectsPlane: function ( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. var min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= plane.constant && max >= plane.constant ); }, clampPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector3(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), getBoundingSphere: function () { var v1 = new Vector3(); return function getBoundingSphere( optionalTarget ) { var result = optionalTarget || new Sphere(); this.getCenter( result.center ); result.radius = this.getSize( v1 ).length() * 0.5; return result; }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, applyMatrix4: function () { var points = [ new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3() ]; return function applyMatrix4( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( points ); return this; }; }(), translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Sphere( center, radius ) { this.center = ( center !== undefined ) ? center : new Vector3(); this.radius = ( radius !== undefined ) ? radius : 0; } Object.assign( Sphere.prototype, { set: function ( center, radius ) { this.center.copy( center ); this.radius = radius; return this; }, setFromPoints: function () { var box = new Box3(); return function setFromPoints( points, optionalCenter ) { var center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { box.setFromPoints( points ).getCenter( center ); } var maxRadiusSq = 0; for ( var i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; }, empty: function () { return ( this.radius <= 0 ); }, containsPoint: function ( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); }, distanceToPoint: function ( point ) { return ( point.distanceTo( this.center ) - this.radius ); }, intersectsSphere: function ( sphere ) { var radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); }, intersectsBox: function ( box ) { return box.intersectsSphere( this ); }, intersectsPlane: function ( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; }, clampPoint: function ( point, optionalTarget ) { var deltaLengthSq = this.center.distanceToSquared( point ); var result = optionalTarget || new Vector3(); result.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { result.sub( this.center ).normalize(); result.multiplyScalar( this.radius ).add( this.center ); } return result; }, getBoundingBox: function ( optionalTarget ) { var box = optionalTarget || new Box3(); box.set( this.center, this.center ); box.expandByScalar( this.radius ); return box; }, applyMatrix4: function ( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; }, translate: function ( offset ) { this.center.add( offset ); return this; }, equals: function ( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } } ); /** * @author bhouston / http://clara.io */ function Plane( normal, constant ) { // normal is assumed to be normalized this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); this.constant = ( constant !== undefined ) ? constant : 0; } Object.assign( Plane.prototype, { set: function ( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; }, setComponents: function ( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; }, setFromNormalAndCoplanarPoint: function ( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; }, setFromCoplanarPoints: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function setFromCoplanarPoints( a, b, c ) { var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; }, normalize: function () { // Note: will lead to a divide by zero if the plane is invalid. var inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; }, negate: function () { this.constant *= - 1; this.normal.negate(); return this; }, distanceToPoint: function ( point ) { return this.normal.dot( point ) + this.constant; }, distanceToSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; }, projectPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); }, intersectLine: function () { var v1 = new Vector3(); return function intersectLine( line, optionalTarget ) { var result = optionalTarget || new Vector3(); var direction = line.delta( v1 ); var denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return result.copy( line.start ); } // Unsure if this is the correct method to handle this case. return undefined; } var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return undefined; } return result.copy( direction ).multiplyScalar( t ).add( line.start ); }; }(), intersectsLine: function ( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. var startSign = this.distanceToPoint( line.start ); var endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); }, intersectsBox: function ( box ) { return box.intersectsPlane( this ); }, intersectsSphere: function ( sphere ) { return sphere.intersectsPlane( this ); }, coplanarPoint: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( this.normal ).multiplyScalar( - this.constant ); }, applyMatrix4: function () { var v1 = new Vector3(); var m1 = new Matrix3(); return function applyMatrix4( matrix, optionalNormalMatrix ) { var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; }; }(), translate: function ( offset ) { this.constant -= offset.dot( this.normal ); return this; }, equals: function ( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / http://clara.io */ function Frustum( p0, p1, p2, p3, p4, p5 ) { this.planes = [ ( p0 !== undefined ) ? p0 : new Plane(), ( p1 !== undefined ) ? p1 : new Plane(), ( p2 !== undefined ) ? p2 : new Plane(), ( p3 !== undefined ) ? p3 : new Plane(), ( p4 !== undefined ) ? p4 : new Plane(), ( p5 !== undefined ) ? p5 : new Plane() ]; } Object.assign( Frustum.prototype, { set: function ( p0, p1, p2, p3, p4, p5 ) { var planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( frustum ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; }, setFromMatrix: function ( m ) { var planes = this.planes; var me = m.elements; var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; }, intersectsObject: function () { var sphere = new Sphere(); return function intersectsObject( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ) .applyMatrix4( object.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSprite: function () { var sphere = new Sphere(); return function intersectsSprite( sprite ) { sphere.center.set( 0, 0, 0 ); sphere.radius = 0.7071067811865476; sphere.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSphere: function ( sphere ) { var planes = this.planes; var center = sphere.center; var negRadius = - sphere.radius; for ( var i = 0; i < 6; i ++ ) { var distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; }, intersectsBox: function () { var p1 = new Vector3(), p2 = new Vector3(); return function intersectsBox( box ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { var plane = planes[ i ]; p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; var d1 = plane.distanceToPoint( p1 ); var d2 = plane.distanceToPoint( p2 ); // if both outside plane, no intersection if ( d1 < 0 && d2 < 0 ) { return false; } } return true; }; }(), containsPoint: function ( point ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } } ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { var _frustum = new Frustum(), _projScreenMatrix = new Matrix4(), _shadowMapSize = new Vector2(), _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ), _lookTarget = new Vector3(), _lightPositionWorld = new Vector3(), _MorphingFlag = 1, _SkinningFlag = 2, _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, _depthMaterials = new Array( _NumberOfMaterialVariants ), _distanceMaterials = new Array( _NumberOfMaterialVariants ), _materialCache = {}; var cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; var cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; var cube2DViewPorts = [ new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() ]; // init for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { var useMorphing = ( i & _MorphingFlag ) !== 0; var useSkinning = ( i & _SkinningFlag ) !== 0; var depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking, morphTargets: useMorphing, skinning: useSkinning } ); _depthMaterials[ i ] = depthMaterial; // var distanceMaterial = new MeshDistanceMaterial( { morphTargets: useMorphing, skinning: useSkinning } ); _distanceMaterials[ i ] = distanceMaterial; } // var scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.renderReverseSided = true; this.renderSingleSided = true; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; // TODO Clean up (needed in case of contextlost) var _gl = _renderer.context; var _state = _renderer.state; // Set GL state for depth map. _state.disable( _gl.BLEND ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // render depth map var faceCount; for ( var i = 0, il = lights.length; i < il; i ++ ) { var light = lights[ i ]; var shadow = light.shadow; var isPointLight = light && light.isPointLight; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } var shadowCamera = shadow.camera; _shadowMapSize.copy( shadow.mapSize ); _shadowMapSize.min( _maxShadowMapSize ); if ( isPointLight ) { var vpWidth = _shadowMapSize.x; var vpHeight = _shadowMapSize.y; // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); // negative X cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); // positive Z cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); // negative Z cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); // positive Y cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); // negative Y cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); _shadowMapSize.x *= 4.0; _shadowMapSize.y *= 2.0; } if ( shadow.map === null ) { var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadowCamera.updateProjectionMatrix(); } if ( shadow.isSpotLightShadow ) { shadow.update( light ); } var shadowMap = shadow.map; var shadowMatrix = shadow.matrix; _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld ); if ( isPointLight ) { faceCount = 6; // for point lights we set the shadow matrix to be a translation-only matrix // equal to inverse of the light"s position shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); } else { faceCount = 1; _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); // compute shadow matrix shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( shadowCamera.projectionMatrix ); shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); } _renderer.setRenderTarget( shadowMap ); _renderer.clear(); // render shadow map for each cube face (if omni-directional) or // run a single pass if not for ( var face = 0; face < faceCount; face ++ ) { if ( isPointLight ) { _lookTarget.copy( shadowCamera.position ); _lookTarget.add( cubeDirections[ face ] ); shadowCamera.up.copy( cubeUps[ face ] ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); var vpDimensions = cube2DViewPorts[ face ]; _state.viewport( vpDimensions ); } // update camera matrices and frustum _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); // set object matrices & frustum culling renderObject( scene, camera, shadowCamera, isPointLight ); } } scope.needsUpdate = false; }; function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) { var geometry = object.geometry; var result = null; var materialVariants = _depthMaterials; var customMaterial = object.customDepthMaterial; if ( isPointLight ) { materialVariants = _distanceMaterials; customMaterial = object.customDistanceMaterial; } if ( ! customMaterial ) { var useMorphing = false; if ( material.morphTargets ) { if ( geometry && geometry.isBufferGeometry ) { useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; } else if ( geometry && geometry.isGeometry ) { useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; } } if ( object.isSkinnedMesh && material.skinning === false ) { console.warn( "THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:", object ); } var useSkinning = object.isSkinnedMesh && material.skinning; var variantIndex = 0; if ( useMorphing ) variantIndex |= _MorphingFlag; if ( useSkinning ) variantIndex |= _SkinningFlag; result = materialVariants[ variantIndex ]; } else { result = customMaterial; } if ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) { // in this case we need a unique material instance reflecting the // appropriate state var keyA = result.uuid, keyB = material.uuid; var materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } var cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; var side = material.side; if ( scope.renderSingleSided && side == DoubleSide ) { side = FrontSide; } if ( scope.renderReverseSided ) { if ( side === FrontSide ) side = BackSide; else if ( side === BackSide ) side = FrontSide; } result.side = side; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( isPointLight && result.isMeshDistanceMaterial ) { result.referencePosition.copy( lightPositionWorld ); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject( object, camera, shadowCamera, isPointLight ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); var geometry = _objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var k = 0, kl = groups.length; k < kl; k ++ ) { var group = groups[ k ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, isPointLight ); } } } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLAttributes( gl ) { var buffers = {}; function createBuffer( attribute, bufferType ) { var array = attribute.array; var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; var buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); var type = gl.FLOAT; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Float64Array ) { console.warn( "THREE.WebGLAttributes: Unsupported data buffer format: Float64Array." ); } else if ( array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { var array = attribute.array; var updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( attribute.dynamic === false ) { gl.bufferData( bufferType, array, gl.STATIC_DRAW ); } else if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else if ( updateRange.count === 0 ) { console.error( "THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually." ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); updateRange.count = - 1; // reset range } } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers[ attribute.uuid ]; } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers[ attribute.uuid ]; if ( data ) { gl.deleteBuffer( data.buffer ); delete buffers[ attribute.uuid ]; } } function update( attribute, bufferType ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers[ attribute.uuid ]; if ( data === undefined ) { buffers[ attribute.uuid ] = createBuffer( attribute, bufferType ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Euler( x, y, z, order ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._order = order || Euler.DefaultOrder; } Euler.RotationOrders = [ "XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX" ]; Euler.DefaultOrder = "XYZ"; Object.defineProperties( Euler.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, order: { get: function () { return this._order; }, set: function ( value ) { this._order = value; this.onChangeCallback(); } } } ); Object.assign( Euler.prototype, { isEuler: true, set: function ( x, y, z, order ) { this._x = x; this._y = y; this._z = z; this._order = order || this._order; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._order ); }, copy: function ( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m, order, update ) { var clamp = _Math.clamp; // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements; var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; order = order || this._order; if ( order === "XYZ" ) { this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.99999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } } else if ( order === "YXZ" ) { this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.99999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } } else if ( order === "ZXY" ) { this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.99999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } } else if ( order === "ZYX" ) { this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.99999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } } else if ( order === "YZX" ) { this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.99999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } } else if ( order === "XZY" ) { this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.99999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } } else { console.warn( "THREE.Euler: .setFromRotationMatrix() given unsupported order: " + order ); } this._order = order; if ( update !== false ) this.onChangeCallback(); return this; }, setFromQuaternion: function () { var matrix = new Matrix4(); return function setFromQuaternion( q, order, update ) { matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( matrix, order, update ); }; }(), setFromVector3: function ( v, order ) { return this.set( v.x, v.y, v.z, order || this._order ); }, reorder: function () { // WARNING: this discards revolution information -bhouston var q = new Quaternion(); return function reorder( newOrder ) { q.setFromEuler( this ); return this.setFromQuaternion( q, newOrder ); }; }(), equals: function ( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); }, fromArray: function ( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; }, toVector3: function ( optionalResult ) { if ( optionalResult ) { return optionalResult.set( this._x, this._y, this._z ); } else { return new Vector3( this._x, this._y, this._z ); } }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ */ function Layers() { this.mask = 1 | 0; } Object.assign( Layers.prototype, { set: function ( channel ) { this.mask = 1 << channel | 0; }, enable: function ( channel ) { this.mask |= 1 << channel | 0; }, toggle: function ( channel ) { this.mask ^= 1 << channel | 0; }, disable: function ( channel ) { this.mask &= ~ ( 1 << channel | 0 ); }, test: function ( layers ) { return ( this.mask & layers.mask ) !== 0; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author elephantatwork / www.elephantatwork.ch */ var object3DId = 0; function Object3D() { Object.defineProperty( this, "id", { value: object3DId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); var position = new Vector3(); var rotation = new Euler(); var quaternion = new Quaternion(); var scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation.onChange( onRotationChange ); quaternion.onChange( onQuaternionChange ); Object.defineProperties( this, { position: { enumerable: true, value: position }, rotation: { enumerable: true, value: rotation }, quaternion: { enumerable: true, value: quaternion }, scale: { enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.userData = {}; } Object3D.DefaultUp = new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Object3D, isObject3D: true, onBeforeRender: function () {}, onAfterRender: function () {}, applyMatrix: function ( matrix ) { this.matrix.multiplyMatrices( matrix, this.matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); }, applyQuaternion: function ( q ) { this.quaternion.premultiply( q ); return this; }, setRotationFromAxisAngle: function ( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); }, setRotationFromEuler: function ( euler ) { this.quaternion.setFromEuler( euler, true ); }, setRotationFromMatrix: function ( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); }, setRotationFromQuaternion: function ( q ) { // assumes q is normalized this.quaternion.copy( q ); }, rotateOnAxis: function () { // rotate object on axis in object space // axis is assumed to be normalized var q1 = new Quaternion(); return function rotateOnAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( q1 ); return this; }; }(), rotateOnWorldAxis: function () { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent var q1 = new Quaternion(); return function rotateOnWorldAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( q1 ); return this; }; }(), rotateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function rotateX( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function rotateY( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function rotateZ( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), translateOnAxis: function () { // translate object by distance along axis in object space // axis is assumed to be normalized var v1 = new Vector3(); return function translateOnAxis( axis, distance ) { v1.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( v1.multiplyScalar( distance ) ); return this; }; }(), translateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function translateX( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function translateY( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function translateZ( distance ) { return this.translateOnAxis( v1, distance ); }; }(), localToWorld: function ( vector ) { return vector.applyMatrix4( this.matrixWorld ); }, worldToLocal: function () { var m1 = new Matrix4(); return function worldToLocal( vector ) { return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); }; }(), lookAt: function () { // This method does not support objects with rotated and/or translated parent(s) var m1 = new Matrix4(); var vector = new Vector3(); return function lookAt( x, y, z ) { if ( x.isVector3 ) { vector.copy( x ); } else { vector.set( x, y, z ); } if ( this.isCamera ) { m1.lookAt( this.position, vector, this.up ); } else { m1.lookAt( vector, this.position, this.up ); } this.quaternion.setFromRotationMatrix( m1 ); }; }(), add: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( ( object && object.isObject3D ) ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; object.dispatchEvent( { type: "added" } ); this.children.push( object ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; }, remove: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } var index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; object.dispatchEvent( { type: "removed" } ); this.children.splice( index, 1 ); } return this; }, getObjectById: function ( id ) { return this.getObjectByProperty( "id", id ); }, getObjectByName: function ( name ) { return this.getObjectByProperty( "name", name ); }, getObjectByProperty: function ( name, value ) { if ( this[ name ] === value ) return this; for ( var i = 0, l = this.children.length; i < l; i ++ ) { var child = this.children[ i ]; var object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; }, getWorldPosition: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); this.updateMatrixWorld( true ); return result.setFromMatrixPosition( this.matrixWorld ); }, getWorldQuaternion: function () { var position = new Vector3(); var scale = new Vector3(); return function getWorldQuaternion( optionalTarget ) { var result = optionalTarget || new Quaternion(); this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, result, scale ); return result; }; }(), getWorldRotation: function () { var quaternion = new Quaternion(); return function getWorldRotation( optionalTarget ) { var result = optionalTarget || new Euler(); this.getWorldQuaternion( quaternion ); return result.setFromQuaternion( quaternion, this.rotation.order, false ); }; }(), getWorldScale: function () { var position = new Vector3(); var quaternion = new Quaternion(); return function getWorldScale( optionalTarget ) { var result = optionalTarget || new Vector3(); this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, quaternion, result ); return result; }; }(), getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( optionalTarget ) { var result = optionalTarget || new Vector3(); this.getWorldQuaternion( quaternion ); return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); }; }(), raycast: function () {}, traverse: function ( callback ) { callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } }, traverseVisible: function ( callback ) { if ( this.visible === false ) return; callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } }, traverseAncestors: function ( callback ) { var parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } }, updateMatrix: function () { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; }, updateMatrixWorld: function ( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( force ); } }, toJSON: function ( meta ) { // meta is a string when called from JSON.stringify var isRootObject = ( meta === undefined || typeof meta === "string" ); var output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization var object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( JSON.stringify( this.userData ) !== "{}" ) object.userData = this.userData; object.matrix = this.matrix.toArray(); // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.geometry !== undefined ) { object.geometry = serialize( meta.geometries, this.geometry ); var parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { var shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { var uuids = []; for ( var i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( var i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } if ( isRootObject ) { var geometries = extractFromCache( meta.geometries ); var materials = extractFromCache( meta.materials ); var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); var shapes = extractFromCache( meta.shapes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } }, clone: function ( recursive ) { return new this.constructor().copy( this, recursive ); }, copy: function ( source, recursive ) { if ( recursive === undefined ) recursive = true; this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( var i = 0; i < source.children.length; i ++ ) { var child = source.children[ i ]; this.add( child.clone() ); } } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author WestLangley / http://github.com/WestLangley */ function Camera() { Object3D.call( this ); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); } Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Camera, isCamera: true, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); return this; }, getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( optionalTarget ) { var result = optionalTarget || new Vector3(); this.getWorldQuaternion( quaternion ); return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); }; }(), updateMatrixWorld: function ( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); this.matrixWorldInverse.getInverse( this.matrixWorld ); }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author alteredq / http://alteredqualia.com/ * @author arose / http://github.com/arose */ function OrthographicCamera( left, right, top, bottom, near, far ) { Camera.call( this ); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = ( near !== undefined ) ? near : 0.1; this.far = ( far !== undefined ) ? far : 2000; this.updateProjectionMatrix(); } OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: OrthographicCamera, isOrthographicCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; }, setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var dx = ( this.right - this.left ) / ( 2 * this.zoom ); var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); var cx = ( this.right + this.left ) / 2; var cy = ( this.top + this.bottom ) / 2; var left = cx - dx; var right = cx + dx; var top = cy + dy; var bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); var scaleW = ( this.right - this.left ) / this.view.width; var scaleH = ( this.top - this.bottom ) / this.view.height; left += scaleW * ( this.view.offsetX / zoomW ); right = left + scaleW * ( this.view.width / zoomW ); top -= scaleH * ( this.view.offsetY / zoomH ); bottom = top - scaleH * ( this.view.height / zoomH ); } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Face3( a, b, c, normal, color, materialIndex ) { this.a = a; this.b = b; this.c = c; this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); this.vertexNormals = Array.isArray( normal ) ? normal : []; this.color = ( color && color.isColor ) ? color : new Color(); this.vertexColors = Array.isArray( color ) ? color : []; this.materialIndex = materialIndex !== undefined ? materialIndex : 0; } Object.assign( Face3.prototype, { clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.a = source.a; this.b = source.b; this.c = source.c; this.normal.copy( source.normal ); this.color.copy( source.color ); this.materialIndex = source.materialIndex; for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { this.vertexColors[ i ] = source.vertexColors[ i ].clone(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author bhouston / http://clara.io */ var geometryId = 0; // Geometry uses even numbers as Id function Geometry() { Object.defineProperty( this, "id", { value: geometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Geometry"; this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.elementsNeedUpdate = false; this.verticesNeedUpdate = false; this.uvsNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; this.groupsNeedUpdate = false; } Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Geometry, isGeometry: true, applyMatrix: function ( matrix ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { var vertex = this.vertices[ i ]; vertex.applyMatrix4( matrix ); } for ( var i = 0, il = this.faces.length; i < il; i ++ ) { var face = this.faces[ i ]; face.normal.applyMatrix3( normalMatrix ).normalize(); for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); } } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), fromBufferGeometry: function ( geometry ) { var scope = this; var indices = geometry.index !== null ? geometry.index.array : undefined; var attributes = geometry.attributes; var positions = attributes.position.array; var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; var colors = attributes.color !== undefined ? attributes.color.array : undefined; var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; var tempNormals = []; var tempUVs = []; var tempUVs2 = []; for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); if ( normals !== undefined ) { tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); } if ( colors !== undefined ) { scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); } if ( uvs !== undefined ) { tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); } if ( uvs2 !== undefined ) { tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); } } function addFace( a, b, c, materialIndex ) { var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); scope.faces.push( face ); if ( uvs !== undefined ) { scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); } if ( uvs2 !== undefined ) { scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); } } var groups = geometry.groups; if ( groups.length > 0 ) { for ( var i = 0; i < groups.length; i ++ ) { var group = groups[ i ]; var start = group.start; var count = group.count; for ( var j = start, jl = start + count; j < jl; j += 3 ) { if ( indices !== undefined ) { addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); } else { addFace( j, j + 1, j + 2, group.materialIndex ); } } } } else { if ( indices !== undefined ) { for ( var i = 0; i < indices.length; i += 3 ) { addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } } else { for ( var i = 0; i < positions.length / 3; i += 3 ) { addFace( i, i + 1, i + 2 ); } } } this.computeFaceNormals(); if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } return this; }, center: function () { this.computeBoundingBox(); var offset = this.boundingBox.getCenter().negate(); this.translate( offset.x, offset.y, offset.z ); return offset; }, normalize: function () { this.computeBoundingSphere(); var center = this.boundingSphere.center; var radius = this.boundingSphere.radius; var s = radius === 0 ? 1 : 1.0 / radius; var matrix = new Matrix4(); matrix.set( s, 0, 0, - s * center.x, 0, s, 0, - s * center.y, 0, 0, s, - s * center.z, 0, 0, 0, 1 ); this.applyMatrix( matrix ); return this; }, computeFaceNormals: function () { var cb = new Vector3(), ab = new Vector3(); for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { var face = this.faces[ f ]; var vA = this.vertices[ face.a ]; var vB = this.vertices[ face.b ]; var vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); cb.normalize(); face.normal.copy( cb ); } }, computeVertexNormals: function ( areaWeighted ) { if ( areaWeighted === undefined ) areaWeighted = true; var v, vl, f, fl, face, vertices; vertices = new Array( this.vertices.length ); for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ] = new Vector3(); } if ( areaWeighted ) { // vertex normals weighted by triangle areas // http://www.iquilezles.org/www/articles/normals/normals.htm var vA, vB, vC; var cb = new Vector3(), ab = new Vector3(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vA = this.vertices[ face.a ]; vB = this.vertices[ face.b ]; vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); vertices[ face.a ].add( cb ); vertices[ face.b ].add( cb ); vertices[ face.c ].add( cb ); } } else { this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vertices[ face.a ].add( face.normal ); vertices[ face.b ].add( face.normal ); vertices[ face.c ].add( face.normal ); } } for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ].normalize(); } for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( vertices[ face.a ] ); vertexNormals[ 1 ].copy( vertices[ face.b ] ); vertexNormals[ 2 ].copy( vertices[ face.c ] ); } else { vertexNormals[ 0 ] = vertices[ face.a ].clone(); vertexNormals[ 1 ] = vertices[ face.b ].clone(); vertexNormals[ 2 ] = vertices[ face.c ].clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeFlatVertexNormals: function () { var f, fl, face; this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( face.normal ); vertexNormals[ 1 ].copy( face.normal ); vertexNormals[ 2 ].copy( face.normal ); } else { vertexNormals[ 0 ] = face.normal.clone(); vertexNormals[ 1 ] = face.normal.clone(); vertexNormals[ 2 ] = face.normal.clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeMorphNormals: function () { var i, il, f, fl, face; // save original normals // - create temp variables on first access // otherwise just copy (for faster repeated calls) for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; if ( ! face.__originalFaceNormal ) { face.__originalFaceNormal = face.normal.clone(); } else { face.__originalFaceNormal.copy( face.normal ); } if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { if ( ! face.__originalVertexNormals[ i ] ) { face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); } else { face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } } // use temp geometry to compute face and vertex normals for each morph var tmpGeo = new Geometry(); tmpGeo.faces = this.faces; for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { // create on first access if ( ! this.morphNormals[ i ] ) { this.morphNormals[ i ] = {}; this.morphNormals[ i ].faceNormals = []; this.morphNormals[ i ].vertexNormals = []; var dstNormalsFace = this.morphNormals[ i ].faceNormals; var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { faceNormal = new Vector3(); vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; dstNormalsFace.push( faceNormal ); dstNormalsVertex.push( vertexNormals ); } } var morphNormals = this.morphNormals[ i ]; // set vertices to morph target tmpGeo.vertices = this.morphTargets[ i ].vertices; // compute morph normals tmpGeo.computeFaceNormals(); tmpGeo.computeVertexNormals(); // store morph normals var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; faceNormal = morphNormals.faceNormals[ f ]; vertexNormals = morphNormals.vertexNormals[ f ]; faceNormal.copy( face.normal ); vertexNormals.a.copy( face.vertexNormals[ 0 ] ); vertexNormals.b.copy( face.vertexNormals[ 1 ] ); vertexNormals.c.copy( face.vertexNormals[ 2 ] ); } } // restore original normals for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; face.normal = face.__originalFaceNormal; face.vertexNormals = face.__originalVertexNormals; } }, computeLineDistances: function () { var d = 0; var vertices = this.vertices; for ( var i = 0, il = vertices.length; i < il; i ++ ) { if ( i > 0 ) { d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); } this.lineDistances[ i ] = d; } }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.setFromPoints( this.vertices ); }, computeBoundingSphere: function () { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.setFromPoints( this.vertices ); }, merge: function ( geometry, matrix, materialIndexOffset ) { if ( ! ( geometry && geometry.isGeometry ) ) { console.error( "THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.", geometry ); return; } var normalMatrix, vertexOffset = this.vertices.length, vertices1 = this.vertices, vertices2 = geometry.vertices, faces1 = this.faces, faces2 = geometry.faces, uvs1 = this.faceVertexUvs[ 0 ], uvs2 = geometry.faceVertexUvs[ 0 ], colors1 = this.colors, colors2 = geometry.colors; if ( materialIndexOffset === undefined ) materialIndexOffset = 0; if ( matrix !== undefined ) { normalMatrix = new Matrix3().getNormalMatrix( matrix ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); vertices1.push( vertexCopy ); } // colors for ( var i = 0, il = colors2.length; i < il; i ++ ) { colors1.push( colors2[ i ].clone() ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); faceCopy.normal.copy( face.normal ); if ( normalMatrix !== undefined ) { faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); } for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( normalMatrix !== undefined ) { normal.applyMatrix3( normalMatrix ).normalize(); } faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex + materialIndexOffset; faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; if ( uv === undefined ) { continue; } for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( uv[ j ].clone() ); } uvs1.push( uvCopy ); } }, mergeMesh: function ( mesh ) { if ( ! ( mesh && mesh.isMesh ) ) { console.error( "THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.", mesh ); return; } mesh.matrixAutoUpdate && mesh.updateMatrix(); this.merge( mesh.geometry, mesh.matrix ); }, /* * Checks for duplicate vertices with hashmap. * Duplicated vertices are removed * and faces" vertices are updated. */ mergeVertices: function () { var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique = [], changes = []; var v, key; var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 var precision = Math.pow( 10, precisionPoints ); var i, il, face; var indices, j, jl; for ( i = 0, il = this.vertices.length; i < il; i ++ ) { v = this.vertices[ i ]; key = Math.round( v.x * precision ) + "_" + Math.round( v.y * precision ) + "_" + Math.round( v.z * precision ); if ( verticesMap[ key ] === undefined ) { verticesMap[ key ] = i; unique.push( this.vertices[ i ] ); changes[ i ] = unique.length - 1; } else { //console.log("Duplicate vertex found. ", i, " could be using ", verticesMap[key]); changes[ i ] = changes[ verticesMap[ key ] ]; } } // if faces are completely degenerate after merging vertices, we // have to remove them from the geometry. var faceIndicesToRemove = []; for ( i = 0, il = this.faces.length; i < il; i ++ ) { face = this.faces[ i ]; face.a = changes[ face.a ]; face.b = changes[ face.b ]; face.c = changes[ face.c ]; indices = [ face.a, face.b, face.c ]; // if any duplicate vertices are found in a Face3 // we have to remove the face as nothing can be saved for ( var n = 0; n < 3; n ++ ) { if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { faceIndicesToRemove.push( i ); break; } } } for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { var idx = faceIndicesToRemove[ i ]; this.faces.splice( idx, 1 ); for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { this.faceVertexUvs[ j ].splice( idx, 1 ); } } // Use unique set of vertices var diff = this.vertices.length - unique.length; this.vertices = unique; return diff; }, setFromPoints: function ( points ) { this.vertices = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return this; }, sortFacesByMaterialIndex: function () { var faces = this.faces; var length = faces.length; // tag faces for ( var i = 0; i < length; i ++ ) { faces[ i ]._id = i; } // sort faces function materialIndexSort( a, b ) { return a.materialIndex - b.materialIndex; } faces.sort( materialIndexSort ); // sort uvs var uvs1 = this.faceVertexUvs[ 0 ]; var uvs2 = this.faceVertexUvs[ 1 ]; var newUvs1, newUvs2; if ( uvs1 && uvs1.length === length ) newUvs1 = []; if ( uvs2 && uvs2.length === length ) newUvs2 = []; for ( var i = 0; i < length; i ++ ) { var id = faces[ i ]._id; if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); } if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Geometry", generator: "Geometry.toJSON" } }; // standard Geometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } var vertices = []; for ( var i = 0; i < this.vertices.length; i ++ ) { var vertex = this.vertices[ i ]; vertices.push( vertex.x, vertex.y, vertex.z ); } var faces = []; var normals = []; var normalsHash = {}; var colors = []; var colorsHash = {}; var uvs = []; var uvsHash = {}; for ( var i = 0; i < this.faces.length; i ++ ) { var face = this.faces[ i ]; var hasMaterial = true; var hasFaceUv = false; // deprecated var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; var hasFaceNormal = face.normal.length() > 0; var hasFaceVertexNormal = face.vertexNormals.length > 0; var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; var hasFaceVertexColor = face.vertexColors.length > 0; var faceType = 0; faceType = setBit( faceType, 0, 0 ); // isQuad faceType = setBit( faceType, 1, hasMaterial ); faceType = setBit( faceType, 2, hasFaceUv ); faceType = setBit( faceType, 3, hasFaceVertexUv ); faceType = setBit( faceType, 4, hasFaceNormal ); faceType = setBit( faceType, 5, hasFaceVertexNormal ); faceType = setBit( faceType, 6, hasFaceColor ); faceType = setBit( faceType, 7, hasFaceVertexColor ); faces.push( faceType ); faces.push( face.a, face.b, face.c ); faces.push( face.materialIndex ); if ( hasFaceVertexUv ) { var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; faces.push( getUvIndex( faceVertexUvs[ 0 ] ), getUvIndex( faceVertexUvs[ 1 ] ), getUvIndex( faceVertexUvs[ 2 ] ) ); } if ( hasFaceNormal ) { faces.push( getNormalIndex( face.normal ) ); } if ( hasFaceVertexNormal ) { var vertexNormals = face.vertexNormals; faces.push( getNormalIndex( vertexNormals[ 0 ] ), getNormalIndex( vertexNormals[ 1 ] ), getNormalIndex( vertexNormals[ 2 ] ) ); } if ( hasFaceColor ) { faces.push( getColorIndex( face.color ) ); } if ( hasFaceVertexColor ) { var vertexColors = face.vertexColors; faces.push( getColorIndex( vertexColors[ 0 ] ), getColorIndex( vertexColors[ 1 ] ), getColorIndex( vertexColors[ 2 ] ) ); } } function setBit( value, position, enabled ) { return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); } function getNormalIndex( normal ) { var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); if ( normalsHash[ hash ] !== undefined ) { return normalsHash[ hash ]; } normalsHash[ hash ] = normals.length / 3; normals.push( normal.x, normal.y, normal.z ); return normalsHash[ hash ]; } function getColorIndex( color ) { var hash = color.r.toString() + color.g.toString() + color.b.toString(); if ( colorsHash[ hash ] !== undefined ) { return colorsHash[ hash ]; } colorsHash[ hash ] = colors.length; colors.push( color.getHex() ); return colorsHash[ hash ]; } function getUvIndex( uv ) { var hash = uv.x.toString() + uv.y.toString(); if ( uvsHash[ hash ] !== undefined ) { return uvsHash[ hash ]; } uvsHash[ hash ] = uvs.length / 2; uvs.push( uv.x, uv.y ); return uvsHash[ hash ]; } data.data = {}; data.data.vertices = vertices; data.data.normals = normals; if ( colors.length > 0 ) data.data.colors = colors; if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility data.data.faces = faces; return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new Geometry().copy( this ); }, copy: function ( source ) { var i, il, j, jl, k, kl; // reset this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // vertices var vertices = source.vertices; for ( i = 0, il = vertices.length; i < il; i ++ ) { this.vertices.push( vertices[ i ].clone() ); } // colors var colors = source.colors; for ( i = 0, il = colors.length; i < il; i ++ ) { this.colors.push( colors[ i ].clone() ); } // faces var faces = source.faces; for ( i = 0, il = faces.length; i < il; i ++ ) { this.faces.push( faces[ i ].clone() ); } // face vertex uvs for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { var faceVertexUvs = source.faceVertexUvs[ i ]; if ( this.faceVertexUvs[ i ] === undefined ) { this.faceVertexUvs[ i ] = []; } for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { var uvs = faceVertexUvs[ j ], uvsCopy = []; for ( k = 0, kl = uvs.length; k < kl; k ++ ) { var uv = uvs[ k ]; uvsCopy.push( uv.clone() ); } this.faceVertexUvs[ i ].push( uvsCopy ); } } // morph targets var morphTargets = source.morphTargets; for ( i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = {}; morphTarget.name = morphTargets[ i ].name; // vertices if ( morphTargets[ i ].vertices !== undefined ) { morphTarget.vertices = []; for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); } } // normals if ( morphTargets[ i ].normals !== undefined ) { morphTarget.normals = []; for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); } } this.morphTargets.push( morphTarget ); } // morph normals var morphNormals = source.morphNormals; for ( i = 0, il = morphNormals.length; i < il; i ++ ) { var morphNormal = {}; // vertex normals if ( morphNormals[ i ].vertexNormals !== undefined ) { morphNormal.vertexNormals = []; for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; var destVertexNormal = {}; destVertexNormal.a = srcVertexNormal.a.clone(); destVertexNormal.b = srcVertexNormal.b.clone(); destVertexNormal.c = srcVertexNormal.c.clone(); morphNormal.vertexNormals.push( destVertexNormal ); } } // face normals if ( morphNormals[ i ].faceNormals !== undefined ) { morphNormal.faceNormals = []; for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); } } this.morphNormals.push( morphNormal ); } // skin weights var skinWeights = source.skinWeights; for ( i = 0, il = skinWeights.length; i < il; i ++ ) { this.skinWeights.push( skinWeights[ i ].clone() ); } // skin indices var skinIndices = source.skinIndices; for ( i = 0, il = skinIndices.length; i < il; i ++ ) { this.skinIndices.push( skinIndices[ i ].clone() ); } // line distances var lineDistances = source.lineDistances; for ( i = 0, il = lineDistances.length; i < il; i ++ ) { this.lineDistances.push( lineDistances[ i ] ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // update flags this.elementsNeedUpdate = source.elementsNeedUpdate; this.verticesNeedUpdate = source.verticesNeedUpdate; this.uvsNeedUpdate = source.uvsNeedUpdate; this.normalsNeedUpdate = source.normalsNeedUpdate; this.colorsNeedUpdate = source.colorsNeedUpdate; this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; this.groupsNeedUpdate = source.groupsNeedUpdate; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferAttribute( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.uuid = _Math.generateUUID(); this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.onUploadCallback = function () {}; this.version = 0; } Object.defineProperty( BufferAttribute.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( BufferAttribute.prototype, { isBufferAttribute: true, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.itemSize : 0; this.array = array; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( var i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, copyArray: function ( array ) { this.array.set( array ); return this; }, copyColorsArray: function ( colors ) { var array = this.array, offset = 0; for ( var i = 0, l = colors.length; i < l; i ++ ) { var color = colors[ i ]; if ( color === undefined ) { console.warn( "THREE.BufferAttribute.copyColorsArray(): color is undefined", i ); color = new Color(); } array[ offset ++ ] = color.r; array[ offset ++ ] = color.g; array[ offset ++ ] = color.b; } return this; }, copyIndicesArray: function ( indices ) { var array = this.array, offset = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { var index = indices[ i ]; array[ offset ++ ] = index.a; array[ offset ++ ] = index.b; array[ offset ++ ] = index.c; } return this; }, copyVector2sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i ); vector = new Vector2(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; } return this; }, copyVector3sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i ); vector = new Vector3(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; } return this; }, copyVector4sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i ); vector = new Vector4(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; array[ offset ++ ] = vector.w; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, getX: function ( index ) { return this.array[ index * this.itemSize ]; }, setX: function ( index, x ) { this.array[ index * this.itemSize ] = x; return this; }, getY: function ( index ) { return this.array[ index * this.itemSize + 1 ]; }, setY: function ( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; }, getZ: function ( index ) { return this.array[ index * this.itemSize + 2 ]; }, setZ: function ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; }, getW: function ( index ) { return this.array[ index * this.itemSize + 3 ]; }, setW: function ( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; }, setXY: function ( index, x, y ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; }, clone: function () { return new this.constructor( this.array, this.itemSize ).copy( this ); } } ); // function Int8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); } Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; function Uint8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); } Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); } Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; function Int16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); } Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; function Uint16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); } Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; function Int32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); } Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; function Uint32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); } Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; function Float32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); } Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; function Float64BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); } Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; /** * @author mrdoob / http://mrdoob.com/ */ function DirectGeometry() { this.indices = []; this.vertices = []; this.normals = []; this.colors = []; this.uvs = []; this.uvs2 = []; this.groups = []; this.morphTargets = {}; this.skinWeights = []; this.skinIndices = []; // this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.verticesNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.uvsNeedUpdate = false; this.groupsNeedUpdate = false; } Object.assign( DirectGeometry.prototype, { computeGroups: function ( geometry ) { var group; var groups = []; var materialIndex = undefined; var faces = geometry.faces; for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; // materials if ( face.materialIndex !== materialIndex ) { materialIndex = face.materialIndex; if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } group = { start: i * 3, materialIndex: materialIndex }; } } if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } this.groups = groups; }, fromGeometry: function ( geometry ) { var faces = geometry.faces; var vertices = geometry.vertices; var faceVertexUvs = geometry.faceVertexUvs; var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; // morphs var morphTargets = geometry.morphTargets; var morphTargetsLength = morphTargets.length; var morphTargetsPosition; if ( morphTargetsLength > 0 ) { morphTargetsPosition = []; for ( var i = 0; i < morphTargetsLength; i ++ ) { morphTargetsPosition[ i ] = []; } this.morphTargets.position = morphTargetsPosition; } var morphNormals = geometry.morphNormals; var morphNormalsLength = morphNormals.length; var morphTargetsNormal; if ( morphNormalsLength > 0 ) { morphTargetsNormal = []; for ( var i = 0; i < morphNormalsLength; i ++ ) { morphTargetsNormal[ i ] = []; } this.morphTargets.normal = morphTargetsNormal; } // skins var skinIndices = geometry.skinIndices; var skinWeights = geometry.skinWeights; var hasSkinIndices = skinIndices.length === vertices.length; var hasSkinWeights = skinWeights.length === vertices.length; // for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); } else { var normal = face.normal; this.normals.push( normal, normal, normal ); } var vertexColors = face.vertexColors; if ( vertexColors.length === 3 ) { this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); } else { var color = face.color; this.colors.push( color, color, color ); } if ( hasFaceVertexUv === true ) { var vertexUvs = faceVertexUvs[ 0 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ", i ); this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); } } if ( hasFaceVertexUv2 === true ) { var vertexUvs = faceVertexUvs[ 1 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ", i ); this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); } } // morphs for ( var j = 0; j < morphTargetsLength; j ++ ) { var morphTarget = morphTargets[ j ].vertices; morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); } for ( var j = 0; j < morphNormalsLength; j ++ ) { var morphNormal = morphNormals[ j ].vertexNormals[ i ]; morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); } // skins if ( hasSkinIndices ) { this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); } if ( hasSkinWeights ) { this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); } } this.computeGroups( geometry ); this.verticesNeedUpdate = geometry.verticesNeedUpdate; this.normalsNeedUpdate = geometry.normalsNeedUpdate; this.colorsNeedUpdate = geometry.colorsNeedUpdate; this.uvsNeedUpdate = geometry.uvsNeedUpdate; this.groupsNeedUpdate = geometry.groupsNeedUpdate; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function arrayMax( array ) { if ( array.length === 0 ) return - Infinity; var max = array[ 0 ]; for ( var i = 1, l = array.length; i < l; ++ i ) { if ( array[ i ] > max ) max = array[ i ]; } return max; } /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id function BufferGeometry() { Object.defineProperty( this, "id", { value: bufferGeometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; } BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: BufferGeometry, isBufferGeometry: true, getIndex: function () { return this.index; }, setIndex: function ( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } }, addAttribute: function ( name, attribute ) { if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { console.warn( "THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )." ); this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); return; } if ( name === "index" ) { console.warn( "THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute." ); this.setIndex( attribute ); return; } this.attributes[ name ] = attribute; return this; }, getAttribute: function ( name ) { return this.attributes[ name ]; }, removeAttribute: function ( name ) { delete this.attributes[ name ]; return this; }, addGroup: function ( start, count, materialIndex ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex !== undefined ? materialIndex : 0 } ); }, clearGroups: function () { this.groups = []; }, setDrawRange: function ( start, count ) { this.drawRange.start = start; this.drawRange.count = count; }, applyMatrix: function ( matrix ) { var position = this.attributes.position; if ( position !== undefined ) { matrix.applyToBufferAttribute( position ); position.needsUpdate = true; } var normal = this.attributes.normal; if ( normal !== undefined ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); normalMatrix.applyToBufferAttribute( normal ); normal.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), center: function () { this.computeBoundingBox(); var offset = this.boundingBox.getCenter().negate(); this.translate( offset.x, offset.y, offset.z ); return offset; }, setFromObject: function ( object ) { // console.log( "THREE.BufferGeometry.setFromObject(). Converting", object, this ); var geometry = object.geometry; if ( object.isPoints || object.isLine ) { var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); this.addAttribute( "position", positions.copyVector3sArray( geometry.vertices ) ); this.addAttribute( "color", colors.copyColorsArray( geometry.colors ) ); if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); this.addAttribute( "lineDistance", lineDistances.copyArray( geometry.lineDistances ) ); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } } else if ( object.isMesh ) { if ( geometry && geometry.isGeometry ) { this.fromGeometry( geometry ); } } return this; }, setFromPoints: function ( points ) { var position = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.addAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; }, updateFromObject: function ( object ) { var geometry = object.geometry; if ( object.isMesh ) { var direct = geometry.__directGeometry; if ( geometry.elementsNeedUpdate === true ) { direct = undefined; geometry.elementsNeedUpdate = false; } if ( direct === undefined ) { return this.fromGeometry( geometry ); } direct.verticesNeedUpdate = geometry.verticesNeedUpdate; direct.normalsNeedUpdate = geometry.normalsNeedUpdate; direct.colorsNeedUpdate = geometry.colorsNeedUpdate; direct.uvsNeedUpdate = geometry.uvsNeedUpdate; direct.groupsNeedUpdate = geometry.groupsNeedUpdate; geometry.verticesNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.groupsNeedUpdate = false; geometry = direct; } var attribute; if ( geometry.verticesNeedUpdate === true ) { attribute = this.attributes.position; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.vertices ); attribute.needsUpdate = true; } geometry.verticesNeedUpdate = false; } if ( geometry.normalsNeedUpdate === true ) { attribute = this.attributes.normal; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.normals ); attribute.needsUpdate = true; } geometry.normalsNeedUpdate = false; } if ( geometry.colorsNeedUpdate === true ) { attribute = this.attributes.color; if ( attribute !== undefined ) { attribute.copyColorsArray( geometry.colors ); attribute.needsUpdate = true; } geometry.colorsNeedUpdate = false; } if ( geometry.uvsNeedUpdate ) { attribute = this.attributes.uv; if ( attribute !== undefined ) { attribute.copyVector2sArray( geometry.uvs ); attribute.needsUpdate = true; } geometry.uvsNeedUpdate = false; } if ( geometry.lineDistancesNeedUpdate ) { attribute = this.attributes.lineDistance; if ( attribute !== undefined ) { attribute.copyArray( geometry.lineDistances ); attribute.needsUpdate = true; } geometry.lineDistancesNeedUpdate = false; } if ( geometry.groupsNeedUpdate ) { geometry.computeGroups( object.geometry ); this.groups = geometry.groups; geometry.groupsNeedUpdate = false; } return this; }, fromGeometry: function ( geometry ) { geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); return this.fromDirectGeometry( geometry.__directGeometry ); }, fromDirectGeometry: function ( geometry ) { var positions = new Float32Array( geometry.vertices.length * 3 ); this.addAttribute( "position", new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); if ( geometry.normals.length > 0 ) { var normals = new Float32Array( geometry.normals.length * 3 ); this.addAttribute( "normal", new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); } if ( geometry.colors.length > 0 ) { var colors = new Float32Array( geometry.colors.length * 3 ); this.addAttribute( "color", new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); } if ( geometry.uvs.length > 0 ) { var uvs = new Float32Array( geometry.uvs.length * 2 ); this.addAttribute( "uv", new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); } if ( geometry.uvs2.length > 0 ) { var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); this.addAttribute( "uv2", new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); } if ( geometry.indices.length > 0 ) { var TypeArray = arrayMax( geometry.indices ) > 65535 ? Uint32Array : Uint16Array; var indices = new TypeArray( geometry.indices.length * 3 ); this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); } // groups this.groups = geometry.groups; // morphs for ( var name in geometry.morphTargets ) { var array = []; var morphTargets = geometry.morphTargets[ name ]; for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { var morphTarget = morphTargets[ i ]; var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); array.push( attribute.copyVector3sArray( morphTarget ) ); } this.morphAttributes[ name ] = array; } // skinning if ( geometry.skinIndices.length > 0 ) { var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); this.addAttribute( "skinIndex", skinIndices.copyVector4sArray( geometry.skinIndices ) ); } if ( geometry.skinWeights.length > 0 ) { var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); this.addAttribute( "skinWeight", skinWeights.copyVector4sArray( geometry.skinWeights ) ); } // if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } return this; }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } var position = this.attributes.position; if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } }, computeBoundingSphere: function () { var box = new Box3(); var vector = new Vector3(); return function computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } var position = this.attributes.position; if ( position ) { var center = this.boundingSphere.center; box.setFromBufferAttribute( position ); box.getCenter( center ); // hoping to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case var maxRadiusSq = 0; for ( var i = 0, il = position.count; i < il; i ++ ) { vector.x = position.getX( i ); vector.y = position.getY( i ); vector.z = position.getZ( i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } }; }(), computeFaceNormals: function () { // backwards compatibility }, computeVertexNormals: function () { var index = this.index; var attributes = this.attributes; var groups = this.groups; if ( attributes.position ) { var positions = attributes.position.array; if ( attributes.normal === undefined ) { this.addAttribute( "normal", new BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { // reset existing normals to zero var array = attributes.normal.array; for ( var i = 0, il = array.length; i < il; i ++ ) { array[ i ] = 0; } } var normals = attributes.normal.array; var vA, vB, vC; var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); var cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { var indices = index.array; if ( groups.length === 0 ) { this.addGroup( 0, indices.length ); } for ( var j = 0, jl = groups.length; j < jl; ++ j ) { var group = groups[ j ]; var start = group.start; var count = group.count; for ( var i = start, il = start + count; i < il; i += 3 ) { vA = indices[ i + 0 ] * 3; vB = indices[ i + 1 ] * 3; vC = indices[ i + 2 ] * 3; pA.fromArray( positions, vA ); pB.fromArray( positions, vB ); pC.fromArray( positions, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ vA ] += cb.x; normals[ vA + 1 ] += cb.y; normals[ vA + 2 ] += cb.z; normals[ vB ] += cb.x; normals[ vB + 1 ] += cb.y; normals[ vB + 2 ] += cb.z; normals[ vC ] += cb.x; normals[ vC + 1 ] += cb.y; normals[ vC + 2 ] += cb.z; } } } else { // non-indexed elements (unconnected triangle soup) for ( var i = 0, il = positions.length; i < il; i += 9 ) { pA.fromArray( positions, i ); pB.fromArray( positions, i + 3 ); pC.fromArray( positions, i + 6 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ i ] = cb.x; normals[ i + 1 ] = cb.y; normals[ i + 2 ] = cb.z; normals[ i + 3 ] = cb.x; normals[ i + 4 ] = cb.y; normals[ i + 5 ] = cb.z; normals[ i + 6 ] = cb.x; normals[ i + 7 ] = cb.y; normals[ i + 8 ] = cb.z; } } this.normalizeNormals(); attributes.normal.needsUpdate = true; } }, merge: function ( geometry, offset ) { if ( ! ( geometry && geometry.isBufferGeometry ) ) { console.error( "THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry ); return; } if ( offset === undefined ) offset = 0; var attributes = this.attributes; for ( var key in attributes ) { if ( geometry.attributes[ key ] === undefined ) continue; var attribute1 = attributes[ key ]; var attributeArray1 = attribute1.array; var attribute2 = geometry.attributes[ key ]; var attributeArray2 = attribute2.array; var attributeSize = attribute2.itemSize; for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; } } return this; }, normalizeNormals: function () { var vector = new Vector3(); return function normalizeNormals() { var normals = this.attributes.normal; for ( var i = 0, il = normals.count; i < il; i ++ ) { vector.x = normals.getX( i ); vector.y = normals.getY( i ); vector.z = normals.getZ( i ); vector.normalize(); normals.setXYZ( i, vector.x, vector.y, vector.z ); } }; }(), toNonIndexed: function () { if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed." ); return this; } var geometry2 = new BufferGeometry(); var indices = this.index.array; var attributes = this.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; var array = attribute.array; var itemSize = attribute.itemSize; var array2 = new array.constructor( indices.length * itemSize ); var index = 0, index2 = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { index = indices[ i ] * itemSize; for ( var j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); } return geometry2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } data.data = { attributes: {} }; var index = this.index; if ( index !== null ) { var array = Array.prototype.slice.call( index.array ); data.data.index = { type: index.array.constructor.name, array: array }; } var attributes = this.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var array = Array.prototype.slice.call( attribute.array ); data.data.attributes[ key ] = { itemSize: attribute.itemSize, type: attribute.array.constructor.name, array: array, normalized: attribute.normalized }; } var groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } var boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new BufferGeometry().copy( this ); }, copy: function ( source ) { var name, i, l; // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // index var index = source.index; if ( index !== null ) { this.setIndex( index.clone() ); } // attributes var attributes = source.attributes; for ( name in attributes ) { var attribute = attributes[ name ]; this.addAttribute( name, attribute.clone() ); } // morph attributes var morphAttributes = source.morphAttributes; for ( name in morphAttributes ) { var array = []; var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone() ); } this.morphAttributes[ name ] = array; } // groups var groups = source.groups; for ( i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // BoxGeometry function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { Geometry.call( this ); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); this.mergeVertices(); } BoxGeometry.prototype = Object.create( Geometry.prototype ); BoxGeometry.prototype.constructor = BoxGeometry; // BoxBufferGeometry function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { BufferGeometry.call( this ); this.type = "BoxBufferGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; var scope = this; width = width || 1; height = height || 1; depth = depth || 1; // segments widthSegments = Math.floor( widthSegments ) || 1; heightSegments = Math.floor( heightSegments ) || 1; depthSegments = Math.floor( depthSegments ) || 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var numberOfVertices = 0; var groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { var segmentWidth = width / gridX; var segmentHeight = height / gridY; var widthHalf = width / 2; var heightHalf = height / 2; var depthHalf = depth / 2; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var vertexCounter = 0; var groupCount = 0; var ix, iy; var vector = new Vector3(); // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segmentHeight - heightHalf; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = numberOfVertices + ix + gridX1 * iy; var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // PlaneGeometry function PlaneGeometry( width, height, widthSegments, heightSegments ) { Geometry.call( this ); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); this.mergeVertices(); } PlaneGeometry.prototype = Object.create( Geometry.prototype ); PlaneGeometry.prototype.constructor = PlaneGeometry; // PlaneBufferGeometry function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { BufferGeometry.call( this ); this.type = "PlaneBufferGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; width = width || 1; height = height || 1; var width_half = width / 2; var height_half = height / 2; var gridX = Math.floor( widthSegments ) || 1; var gridY = Math.floor( heightSegments ) || 1; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var segment_width = width / gridX; var segment_height = height / gridY; var ix, iy; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segment_height - height_half; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } // indices for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = ix + gridX1 * iy; var b = ix + gridX1 * ( iy + 1 ); var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: * } */ function MeshBasicMaterial( parameters ) { Material.call( this ); this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } MeshBasicMaterial.prototype = Object.create( Material.prototype ); MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; MeshBasicMaterial.prototype.isMeshBasicMaterial = true; MeshBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: , * * skinning: , * morphTargets: , * morphNormals: * } */ function ShaderMaterial( parameters ) { Material.call( this ); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; this.fragmentShader = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.skinning = false; // set to use skinning attribute streams this.morphTargets = false; // set to use morph targets this.morphNormals = false; // set to use morph normals this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv2": [ 0, 0 ] }; this.index0AttributeName = undefined; if ( parameters !== undefined ) { if ( parameters.attributes !== undefined ) { console.error( "THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead." ); } this.setValues( parameters ); } } ShaderMaterial.prototype = Object.create( Material.prototype ); ShaderMaterial.prototype.constructor = ShaderMaterial; ShaderMaterial.prototype.isShaderMaterial = true; ShaderMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = UniformsUtils.clone( source.uniforms ); this.defines = source.defines; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; this.extensions = source.extensions; return this; }; ShaderMaterial.prototype.toJSON = function ( meta ) { var data = Material.prototype.toJSON.call( this, meta ); data.uniforms = this.uniforms; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; return data; }; /** * @author bhouston / http://clara.io */ function Ray( origin, direction ) { this.origin = ( origin !== undefined ) ? origin : new Vector3(); this.direction = ( direction !== undefined ) ? direction : new Vector3(); } Object.assign( Ray.prototype, { set: function ( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; }, at: function ( t, optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); }, lookAt: function ( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; }, recast: function () { var v1 = new Vector3(); return function recast( t ) { this.origin.copy( this.at( t, v1 ) ); return this; }; }(), closestPointToPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector3(); result.subVectors( point, this.origin ); var directionDistance = result.dot( this.direction ); if ( directionDistance < 0 ) { return result.copy( this.origin ); } return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); }, distanceToPoint: function ( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); }, distanceSqToPoint: function () { var v1 = new Vector3(); return function distanceSqToPoint( point ) { var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return v1.distanceToSquared( point ); }; }(), distanceSqToSegment: function () { var segCenter = new Vector3(); var segDir = new Vector3(); var diff = new Vector3(); return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); segDir.copy( v1 ).sub( v0 ).normalize(); diff.copy( this.origin ).sub( segCenter ); var segExtent = v0.distanceTo( v1 ) * 0.5; var a01 = - this.direction.dot( segDir ); var b0 = diff.dot( this.direction ); var b1 = - diff.dot( segDir ); var c = diff.lengthSq(); var det = Math.abs( 1 - a01 * a01 ); var s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. var invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); } return sqrDist; }; }(), intersectSphere: function () { var v1 = new Vector3(); return function intersectSphere( sphere, optionalTarget ) { v1.subVectors( sphere.center, this.origin ); var tca = v1.dot( this.direction ); var d2 = v1.dot( v1 ) - tca * tca; var radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; var thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere var t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere var t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if ( t0 < 0 && t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, optionalTarget ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, optionalTarget ); }; }(), intersectsSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) <= sphere.radius; }, distanceToPlane: function ( plane ) { var denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; }, intersectPlane: function ( plane, optionalTarget ) { var t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, optionalTarget ); }, intersectsPlane: function ( plane ) { // check if the ray lies on the plane first var distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } var denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; }, intersectBox: function ( box, optionalTarget ) { var tmin, tmax, tymin, tymax, tzmin, tzmax; var invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; var origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if ( tymin > tmin || tmin !== tmin ) tmin = tymin; if ( tymax < tmax || tmax !== tmax ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); }, intersectsBox: ( function () { var v = new Vector3(); return function intersectsBox( box ) { return this.intersectBox( box, v ) !== null; }; } )(), intersectTriangle: function () { // Compute the offset origin, edges, and normal. var diff = new Vector3(); var edge1 = new Vector3(); var edge2 = new Vector3(); var normal = new Vector3(); return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h edge1.subVectors( b, a ); edge2.subVectors( c, a ); normal.crossVectors( edge1, edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) var DdN = this.direction.dot( normal ); var sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } diff.subVectors( this.origin, a ); var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. var QdN = - sign * diff.dot( normal ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, optionalTarget ); }; }(), applyMatrix4: function ( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; }, equals: function ( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } } ); /** * @author bhouston / http://clara.io */ function Line3( start, end ) { this.start = ( start !== undefined ) ? start : new Vector3(); this.end = ( end !== undefined ) ? end : new Vector3(); } Object.assign( Line3.prototype, { set: function ( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); }, delta: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.subVectors( this.end, this.start ); }, distanceSq: function () { return this.start.distanceToSquared( this.end ); }, distance: function () { return this.start.distanceTo( this.end ); }, at: function ( t, optionalTarget ) { var result = optionalTarget || new Vector3(); return this.delta( result ).multiplyScalar( t ).add( this.start ); }, closestPointToPointParameter: function () { var startP = new Vector3(); var startEnd = new Vector3(); return function closestPointToPointParameter( point, clampToLine ) { startP.subVectors( point, this.start ); startEnd.subVectors( this.end, this.start ); var startEnd2 = startEnd.dot( startEnd ); var startEnd_startP = startEnd.dot( startP ); var t = startEnd_startP / startEnd2; if ( clampToLine ) { t = _Math.clamp( t, 0, 1 ); } return t; }; }(), closestPointToPoint: function ( point, clampToLine, optionalTarget ) { var t = this.closestPointToPointParameter( point, clampToLine ); var result = optionalTarget || new Vector3(); return this.delta( result ).multiplyScalar( t ).add( this.start ); }, applyMatrix4: function ( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; }, equals: function ( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Triangle( a, b, c ) { this.a = ( a !== undefined ) ? a : new Vector3(); this.b = ( b !== undefined ) ? b : new Vector3(); this.c = ( c !== undefined ) ? c : new Vector3(); } Object.assign( Triangle, { normal: function () { var v0 = new Vector3(); return function normal( a, b, c, optionalTarget ) { var result = optionalTarget || new Vector3(); result.subVectors( c, b ); v0.subVectors( a, b ); result.cross( v0 ); var resultLengthSq = result.lengthSq(); if ( resultLengthSq > 0 ) { return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); } return result.set( 0, 0, 0 ); }; }(), // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html barycoordFromPoint: function () { var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); return function barycoordFromPoint( point, a, b, c, optionalTarget ) { v0.subVectors( c, a ); v1.subVectors( b, a ); v2.subVectors( point, a ); var dot00 = v0.dot( v0 ); var dot01 = v0.dot( v1 ); var dot02 = v0.dot( v2 ); var dot11 = v1.dot( v1 ); var dot12 = v1.dot( v2 ); var denom = ( dot00 * dot11 - dot01 * dot01 ); var result = optionalTarget || new Vector3(); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return result.set( - 2, - 1, - 1 ); } var invDenom = 1 / denom; var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return result.set( 1 - u - v, v, u ); }; }(), containsPoint: function () { var v1 = new Vector3(); return function containsPoint( point, a, b, c ) { var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); }; }() } ); Object.assign( Triangle.prototype, { set: function ( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; }, setFromPointsAndIndices: function ( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; }, area: function () { var v0 = new Vector3(); var v1 = new Vector3(); return function area() { v0.subVectors( this.c, this.b ); v1.subVectors( this.a, this.b ); return v0.cross( v1 ).length() * 0.5; }; }(), midpoint: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); }, normal: function ( optionalTarget ) { return Triangle.normal( this.a, this.b, this.c, optionalTarget ); }, plane: function ( optionalTarget ) { var result = optionalTarget || new Plane(); return result.setFromCoplanarPoints( this.a, this.b, this.c ); }, barycoordFromPoint: function ( point, optionalTarget ) { return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); }, containsPoint: function ( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); }, closestPointToPoint: function () { var plane = new Plane(); var edgeList = [ new Line3(), new Line3(), new Line3() ]; var projectedPoint = new Vector3(); var closestPoint = new Vector3(); return function closestPointToPoint( point, optionalTarget ) { var result = optionalTarget || new Vector3(); var minDistance = Infinity; // project the point onto the plane of the triangle plane.setFromCoplanarPoints( this.a, this.b, this.c ); plane.projectPoint( point, projectedPoint ); // check if the projection lies within the triangle if ( this.containsPoint( projectedPoint ) === true ) { // if so, this is the closest point result.copy( projectedPoint ); } else { // if not, the point falls outside the triangle. the result is the closest point to the triangle"s edges or vertices edgeList[ 0 ].set( this.a, this.b ); edgeList[ 1 ].set( this.b, this.c ); edgeList[ 2 ].set( this.c, this.a ); for ( var i = 0; i < edgeList.length; i ++ ) { edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); var distance = projectedPoint.distanceToSquared( closestPoint ); if ( distance < minDistance ) { minDistance = distance; result.copy( closestPoint ); } } } return result; }; }(), equals: function ( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ */ function Mesh( geometry, material ) { Object3D.call( this ); this.type = "Mesh"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); this.drawMode = TrianglesDrawMode; this.updateMorphTargets(); } Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Mesh, isMesh: true, setDrawMode: function ( value ) { this.drawMode = value; }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.drawMode = source.drawMode; if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } return this; }, updateMorphTargets: function () { var geometry = this.geometry; var m, ml, name; if ( geometry.isBufferGeometry ) { var morphAttributes = geometry.morphAttributes; var keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { var morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } else { var morphTargets = geometry.morphTargets; if ( morphTargets !== undefined && morphTargets.length > 0 ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) { name = morphTargets[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } }, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); var vA = new Vector3(); var vB = new Vector3(); var vC = new Vector3(); var tempA = new Vector3(); var tempB = new Vector3(); var tempC = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); var barycoord = new Vector3(); var intersectionPoint = new Vector3(); var intersectionPointWorld = new Vector3(); function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); uv1.multiplyScalar( barycoord.x ); uv2.multiplyScalar( barycoord.y ); uv3.multiplyScalar( barycoord.z ); uv1.add( uv2 ).add( uv3 ); return uv1.clone(); } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { var intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); } if ( intersect === null ) return null; intersectionPointWorld.copy( point ); intersectionPointWorld.applyMatrix4( object.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) { vA.fromBufferAttribute( position, a ); vB.fromBufferAttribute( position, b ); vC.fromBufferAttribute( position, c ); var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint ); if ( intersection ) { if ( uv ) { uvA.fromBufferAttribute( uv, a ); uvB.fromBufferAttribute( uv, b ); uvC.fromBufferAttribute( uv, c ); intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); } intersection.face = new Face3( a, b, c, Triangle.normal( vA, vB, vC ) ); intersection.faceIndex = a; } return intersection; } return function raycast( raycaster, intersects ) { var geometry = this.geometry; var material = this.material; var matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; } var intersection; if ( geometry.isBufferGeometry ) { var a, b, c; var index = geometry.index; var position = geometry.attributes.position; var uv = geometry.attributes.uv; var i, l; if ( index !== null ) { // indexed buffer geometry for ( i = 0, l = index.count; i < l; i += 3 ) { a = index.getX( i ); b = index.getX( i + 1 ); c = index.getX( i + 2 ); intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics intersects.push( intersection ); } } } else if ( position !== undefined ) { // non-indexed buffer geometry for ( i = 0, l = position.count; i < l; i += 3 ) { a = i; b = i + 1; c = i + 2; intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.index = a; // triangle number in positions buffer semantics intersects.push( intersection ); } } } } else if ( geometry.isGeometry ) { var fvA, fvB, fvC; var isMultiMaterial = Array.isArray( material ); var vertices = geometry.vertices; var faces = geometry.faces; var uvs; var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; for ( var f = 0, fl = faces.length; f < fl; f ++ ) { var face = faces[ f ]; var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; if ( faceMaterial === undefined ) continue; fvA = vertices[ face.a ]; fvB = vertices[ face.b ]; fvC = vertices[ face.c ]; if ( faceMaterial.morphTargets === true ) { var morphTargets = geometry.morphTargets; var morphInfluences = this.morphTargetInfluences; vA.set( 0, 0, 0 ); vB.set( 0, 0, 0 ); vC.set( 0, 0, 0 ); for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { var influence = morphInfluences[ t ]; if ( influence === 0 ) continue; var targets = morphTargets[ t ].vertices; vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); } vA.add( fvA ); vB.add( fvB ); vC.add( fvC ); fvA = vA; fvB = vB; fvC = vC; } intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); if ( intersection ) { if ( uvs && uvs[ f ] ) { var uvs_f = uvs[ f ]; uvA.copy( uvs_f[ 0 ] ); uvB.copy( uvs_f[ 1 ] ); uvC.copy( uvs_f[ 2 ] ); intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); } intersection.face = face; intersection.faceIndex = f; intersects.push( intersection ); } } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) { var clearColor = new Color( 0x000000 ); var clearAlpha = 0; var planeCamera, planeMesh; var boxMesh; function render( renderList, scene, camera, forceClear ) { var background = scene.background; if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && background.isCubeTexture ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), new ShaderMaterial( { uniforms: ShaderLib.cube.uniforms, vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: true, depthWrite: false, fog: false } ) ); boxMesh.geometry.removeAttribute( "normal" ); boxMesh.geometry.removeAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; geometries.update( boxMesh.geometry ); } boxMesh.material.uniforms.tCube.value = background; renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null ); } else if ( background && background.isTexture ) { if ( planeCamera === undefined ) { planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) ); geometries.update( planeMesh.geometry ); } planeMesh.material.map = background; // TODO Push this to renderList renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null ); } } function setClear( color, alpha ) { state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha ) { clearColor.set( color ); clearAlpha = alpha !== undefined ? alpha : 1; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } /** * @author mrdoob / http://mrdoob.com/ */ function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.program && b.program && a.program !== b.program ) { return a.program.id - b.program.id; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { var renderItems = []; var renderItemsIndex = 0; var opaque = []; var transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transparent.length = 0; } function push( object, geometry, material, z, group ) { var renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, program: material.program, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.program = material.program; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } ( material.transparent === true ? transparent : opaque ).push( renderItem ); renderItemsIndex ++; } function sort() { if ( opaque.length > 1 ) opaque.sort( painterSortStable ); if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable ); } return { opaque: opaque, transparent: transparent, init: init, push: push, sort: sort }; } function WebGLRenderLists() { var lists = {}; function get( scene, camera ) { var hash = scene.id + "," + camera.id; var list = lists[ hash ]; if ( list === undefined ) { // console.log( "THREE.WebGLRenderLists:", hash ); list = new WebGLRenderList(); lists[ hash ] = list; } return list; } function dispose() { lists = {}; } return { get: get, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl ) { var influencesList = {}; var morphInfluences = new Float32Array( 8 ); function update( object, geometry, material, program ) { var objectInfluences = object.morphTargetInfluences; var length = objectInfluences.length; var influences = influencesList[ geometry.id ]; if ( influences === undefined ) { // initialise list influences = []; for ( var i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } var morphTargets = material.morphTargets && geometry.morphAttributes.position; var morphNormals = material.morphNormals && geometry.morphAttributes.normal; // Remove current morphAttributes for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; if ( influence[ 1 ] !== 0 ) { if ( morphTargets ) geometry.removeAttribute( "morphTarget" + i ); if ( morphNormals ) geometry.removeAttribute( "morphNormal" + i ); } } // Collect influences for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); // Add morphAttributes for ( var i = 0; i < 8; i ++ ) { var influence = influences[ i ]; if ( influence ) { var index = influence[ 0 ]; var value = influence[ 1 ]; if ( value ) { if ( morphTargets ) geometry.addAttribute( "morphTarget" + i, morphTargets[ index ] ); if ( morphNormals ) geometry.addAttribute( "morphNormal" + i, morphNormals[ index ] ); morphInfluences[ i ] = value; continue; } } morphInfluences[ i ] = 0; } program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } return { update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLIndexedBufferRenderer( gl, extensions, infoRender ) { var mode; function setMode( value ) { mode = value; } var type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; else if ( mode === gl.POINTS ) infoRender.points += count; } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); infoRender.calls ++; infoRender.vertices += count * geometry.maxInstancedCount; if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; else if ( mode === gl.POINTS ) infoRender.points += geometry.maxInstancedCount * count; } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBufferRenderer( gl, extensions, infoRender ) { var mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; else if ( mode === gl.POINTS ) infoRender.points += count; } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } var position = geometry.attributes.position; if ( position.isInterleavedBufferAttribute ) { count = position.data.count; extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); } else { extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount ); } infoRender.calls ++; infoRender.vertices += count * geometry.maxInstancedCount; if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; else if ( mode === gl.POINTS ) infoRender.points += geometry.maxInstancedCount * count; } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLGeometries( gl, attributes, infoMemory ) { var geometries = {}; var wireframeAttributes = {}; function onGeometryDispose( event ) { var geometry = event.target; var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry.index !== null ) { attributes.remove( buffergeometry.index ); } for ( var name in buffergeometry.attributes ) { attributes.remove( buffergeometry.attributes[ name ] ); } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; // TODO Remove duplicate code var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ geometry.id ]; } attribute = wireframeAttributes[ buffergeometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ buffergeometry.id ]; } // infoMemory.geometries --; } function get( object, geometry ) { var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry ) return buffergeometry; geometry.addEventListener( "dispose", onGeometryDispose ); if ( geometry.isBufferGeometry ) { buffergeometry = geometry; } else if ( geometry.isGeometry ) { if ( geometry._bufferGeometry === undefined ) { geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); } buffergeometry = geometry._bufferGeometry; } geometries[ geometry.id ] = buffergeometry; infoMemory.geometries ++; return buffergeometry; } function update( geometry ) { var index = geometry.index; var geometryAttributes = geometry.attributes; if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } for ( var name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets var morphAttributes = geometry.morphAttributes; for ( var name in morphAttributes ) { var array = morphAttributes[ name ]; for ( var i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function getWireframeAttribute( geometry ) { var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) return attribute; var indices = []; var geometryIndex = geometry.index; var geometryAttributes = geometry.attributes; // console.time( "wireframe" ); if ( geometryIndex !== null ) { var array = geometryIndex.array; for ( var i = 0, l = array.length; i < l; i += 3 ) { var a = array[ i + 0 ]; var b = array[ i + 1 ]; var c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { var array = geometryAttributes.position.array; for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { var a = i + 0; var b = i + 1; var c = i + 2; indices.push( a, b, b, c, c, a ); } } // console.timeEnd( "wireframe" ); attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER ); wireframeAttributes[ geometry.id ] = attribute; return attribute; } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } /** * @author mrdoob / http://mrdoob.com/ */ function UniformsCache() { var lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } var uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color(), shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() // TODO (abelnation): set RectAreaLight shadow uniforms }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } function WebGLLights() { var cache = new UniformsCache(); var state = { hash: "", ambient: [ 0, 0, 0 ], directional: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotShadowMap: [], spotShadowMatrix: [], rectArea: [], point: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [] }; var vector3 = new Vector3(); var matrix4 = new Matrix4(); var matrix42 = new Matrix4(); function setup( lights, shadows, camera ) { var r = 0, g = 0, b = 0; var directionalLength = 0; var pointLength = 0; var spotLength = 0; var rectAreaLength = 0; var hemiLength = 0; var viewMatrix = camera.matrixWorldInverse; for ( var i = 0, l = lights.length; i < l; i ++ ) { var light = lights[ i ]; var color = light.color; var intensity = light.intensity; var distance = light.distance; var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if ( light.isDirectionalLight ) { var uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.distance = distance; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.spotShadowMap[ spotLength ] = shadowMap; state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; state.spot[ spotLength ] = uniforms; spotLength ++; } else if ( light.isRectAreaLight ) { var uniforms = cache.get( light ); // (a) intensity controls irradiance of entire light uniforms.color .copy( color ) .multiplyScalar( intensity / ( light.width * light.height ) ); // (b) intensity controls the radiance per light area // uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); // TODO (abelnation): RectAreaLight distance? // uniforms.distance = distance; state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.distance = light.distance; uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; uniforms.shadowCameraNear = shadow.camera.near; uniforms.shadowCameraFar = shadow.camera.far; } state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { var uniforms = cache.get( light ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); uniforms.direction.normalize(); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; // TODO (sam-g-steel) why aren"t we using join state.hash = directionalLength + "," + pointLength + "," + spotLength + "," + rectAreaLength + "," + hemiLength + "," + shadows.length; } return { setup: setup, state: state }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLObjects( geometries, infoRender ) { var updateList = {}; function update( object ) { var frame = infoRender.frame; var geometry = object.geometry; var buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateList[ buffergeometry.id ] !== frame ) { if ( geometry.isGeometry ) { buffergeometry.updateFromObject( object ); } geometries.update( buffergeometry ); updateList[ buffergeometry.id ] = frame; } return buffergeometry; } function clear() { updateList = {}; } return { update: update, clear: clear }; } /** * @author mrdoob / http://mrdoob.com/ */ function addLineNumbers( string ) { var lines = string.split( " " ); for ( var i = 0; i < lines.length; i ++ ) { lines[ i ] = ( i + 1 ) + ": " + lines[ i ]; } return lines.join( " " ); } function WebGLShader( gl, type, string ) { var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( "THREE.WebGLShader: Shader couldn"t compile." ); } if ( gl.getShaderInfoLog( shader ) !== "" ) { console.warn( "THREE.WebGLShader: gl.getShaderInfoLog()", type === gl.VERTEX_SHADER ? "vertex" : "fragment", gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); } // --enable-privileged-webgl-extension // console.log( type, gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return shader; } /** * @author mrdoob / http://mrdoob.com/ */ var programIdCount = 0; function getEncodingComponents( encoding ) { switch ( encoding ) { case LinearEncoding: return [ "Linear", "( value )" ]; case sRGBEncoding: return [ "sRGB", "( value )" ]; case RGBEEncoding: return [ "RGBE", "( value )" ]; case RGBM7Encoding: return [ "RGBM", "( value, 7.0 )" ]; case RGBM16Encoding: return [ "RGBM", "( value, 16.0 )" ]; case RGBDEncoding: return [ "RGBD", "( value, 256.0 )" ]; case GammaEncoding: return [ "Gamma", "( value, float( GAMMA_FACTOR ) )" ]; default: throw new Error( "unsupported encoding: " + encoding ); } } function getTexelDecodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; } function getTexelEncodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { var toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case Uncharted2ToneMapping: toneMappingName = "Uncharted2"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; default: throw new Error( "unsupported toneMapping: " + toneMapping ); } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; var chunks = [ ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( "EXT_frag_depth" ) ? "#extension GL_EXT_frag_depth : enable" : "", ( extensions.drawBuffers ) && rendererExtensions.get( "WEBGL_draw_buffers" ) ? "#extension GL_EXT_draw_buffers : require" : "", ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( "EXT_shader_texture_lod" ) ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { var chunks = []; for ( var name in defines ) { var value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { var attributes = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveAttrib( program, i ); var name = info.name; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); } function parseIncludes( string ) { var pattern = /^[ ]*#include +<([wd.]+)>/gm; function replace( match, include ) { var replace = ShaderChunk[ include ]; if ( replace === undefined ) { throw new Error( "Can not resolve #include <" + include + ">" ); } return parseIncludes( replace ); } return string.replace( pattern, replace ); } function unrollLoops( string ) { var pattern = /for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; function replace( match, start, end, snippet ) { var unroll = ""; for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { unroll += snippet.replace( /[ i ]/g, "[ " + i + " ]" ); } return unroll; } return string.replace( pattern, replace ); } function WebGLProgram( renderer, extensions, code, material, shader, parameters ) { var gl = renderer.context; var defines = material.defines; var vertexShader = shader.vertexShader; var fragmentShader = shader.fragmentShader; var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; if ( parameters.shadowMapType === PCFShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; } var envMapTypeDefine = "ENVMAP_TYPE_CUBE"; var envMapModeDefine = "ENVMAP_MODE_REFLECTION"; var envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; if ( parameters.envMap ) { switch ( material.envMap.mapping ) { case CubeReflectionMapping: case CubeRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE"; break; case CubeUVReflectionMapping: case CubeUVRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE_UV"; break; case EquirectangularReflectionMapping: case EquirectangularRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_EQUIREC"; break; case SphericalReflectionMapping: envMapTypeDefine = "ENVMAP_TYPE_SPHERE"; break; } switch ( material.envMap.mapping ) { case CubeRefractionMapping: case EquirectangularRefractionMapping: envMapModeDefine = "ENVMAP_MODE_REFRACTION"; break; } switch ( material.combine ) { case MultiplyOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; break; case MixOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MIX"; break; case AddOperation: envMapBlendingDefine = "ENVMAP_BLENDING_ADD"; break; } } var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; // console.log( "building new program " ); // var customExtensions = generateExtensions( material.extensions, parameters, extensions ); var customDefines = generateDefines( defines ); // var program = gl.createProgram(); var prefixVertex, prefixFragment; if ( material.isRawShaderMaterial ) { prefixVertex = [ customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixVertex.length > 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define GAMMA_FACTOR " + gammaFactorDefine, "#define MAX_BONES " + parameters.maxBones, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", "#define NUM_CLIPPING_PLANES " + parameters.numClippingPlanes, parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 modelMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_COLOR", " attribute vec3 color;", "#endif", "#ifdef USE_MORPHTARGETS", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest : "", "#define GAMMA_FACTOR " + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", "#define NUM_CLIPPING_PLANES " + parameters.numClippingPlanes, "#define UNION_CLIPPING_PLANES " + ( parameters.numClippingPlanes - parameters.numClipIntersection ), parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", parameters.envMap && extensions.get( "EXT_shader_texture_lod" ) ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ "encodings_pars_fragment" ] : "", // this code is required here because it is used by the various encoding/decoding function defined below parameters.mapEncoding ? getTexelDecodingFunction( "mapTexelToLinear", parameters.mapEncoding ) : "", parameters.envMapEncoding ? getTexelDecodingFunction( "envMapTexelToLinear", parameters.envMapEncoding ) : "", parameters.emissiveMapEncoding ? getTexelDecodingFunction( "emissiveMapTexelToLinear", parameters.emissiveMapEncoding ) : "", parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : "", parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = parseIncludes( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); fragmentShader = parseIncludes( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); if ( ! material.isShaderMaterial ) { vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); } var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( material.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, material.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); var programLog = gl.getProgramInfoLog( program ); var vertexLog = gl.getShaderInfoLog( glVertexShader ); var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); var runnable = true; var haveDiagnostics = true; // console.log( "**VERTEX**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glVertexShader ) ); // console.log( "**FRAGMENT**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glFragmentShader ) ); if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; console.error( "THREE.WebGLProgram: shader error: ", gl.getError(), "gl.VALIDATE_STATUS", gl.getProgramParameter( program, gl.VALIDATE_STATUS ), "gl.getProgramInfoLog", programLog, vertexLog, fragmentLog ); } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: gl.getProgramInfoLog()", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, material: material, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations var cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program, renderer ); } return cachedUniforms; }; // set up caching for attribute locations var cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { gl.deleteProgram( program ); this.program = undefined; }; // DEPRECATED Object.defineProperties( this, { uniforms: { get: function () { console.warn( "THREE.WebGLProgram: .uniforms is now .getUniforms()." ); return this.getUniforms(); } }, attributes: { get: function () { console.warn( "THREE.WebGLProgram: .attributes is now .getAttributes()." ); return this.getAttributes(); } } } ); // this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLPrograms( renderer, extensions, capabilities ) { var programs = []; var shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "phong", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow" }; var parameterNames = [ "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "shadowMapEnabled", "shadowMapType", "toneMapping", "physicallyCorrectLights", "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering" ]; function allocateBones( object ) { var skeleton = object.skeleton; var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = capabilities.maxVertexUniforms; var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = Math.min( nVertexMatrices, bones.length ); if ( maxBones < bones.length ) { console.warn( "THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "." ); return 0; } return maxBones; } } function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; if ( ! map ) { encoding = LinearEncoding; } else if ( map.isTexture ) { encoding = map.encoding; } else if ( map.isWebGLRenderTarget ) { console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don"t use render targets as textures. Use their .texture property instead." ); encoding = map.texture.encoding; } // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. if ( encoding === LinearEncoding && gammaOverrideLinear ) { encoding = GammaEncoding; } return encoding; } this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; var precision = capabilities.precision; if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } var currentRenderTarget = renderer.getRenderTarget(); var parameters = { shaderID: shaderID, precision: precision, supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, gradientMap: !! material.gradientMap, combine: material.combine, vertexColors: material.vertexColors, fog: !! fog, useFog: material.fog, fogExp: ( fog && fog.isFogExp2 ), flatShading: material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, skinning: material.skinning && maxBones > 0, maxBones: maxBones, useVertexTexture: capabilities.floatVertexTextures, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: renderer.maxMorphTargets, maxMorphNormals: renderer.maxMorphNormals, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numClippingPlanes: nClipPlanes, numClipIntersection: nClipIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, alphaTest: material.alphaTest, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false }; return parameters; }; this.getProgramCode = function ( material, parameters ) { var array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( material.fragmentShader ); array.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { array.push( name ); array.push( material.defines[ name ] ); } } for ( var i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[ parameterNames[ i ] ] ); } array.push( material.onBeforeCompile.toString() ); array.push( renderer.gammaOutput ); return array.join(); }; this.acquireProgram = function ( material, shader, parameters, code ) { var program; // Check if code has been already compiled for ( var p = 0, pl = programs.length; p < pl; p ++ ) { var programInfo = programs[ p ]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, extensions, code, material, shader, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function ( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set var i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } }; // Exposed for resource monitoring & error feedback via renderer.info: this.programs = programs; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, infoMemory ) { var _isWebGL2 = ( typeof WebGL2RenderingContext !== "undefined" && _gl instanceof window.WebGL2RenderingContext ); var _videoTextures = {}; // function clampToMaxSize( image, maxSize ) { if ( image.width > maxSize || image.height > maxSize ) { // Warning: Scaling through the canvas will only work with images that use // premultiplied alpha. var scale = maxSize / Math.max( image.width, image.height ); var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = Math.floor( image.width * scale ); canvas.height = Math.floor( image.height * scale ); var context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); console.warn( "THREE.WebGLRenderer: image is too big (" + image.width + "x" + image.height + "). Resized to " + canvas.width + "x" + canvas.height, image ); return canvas; } return image; } function isPowerOfTwo( image ) { return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); } function makePowerOfTwo( image ) { if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) { var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = _Math.floorPowerOfTwo( image.width ); canvas.height = _Math.floorPowerOfTwo( image.height ); var context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, canvas.width, canvas.height ); console.warn( "THREE.WebGLRenderer: image is not power of two (" + image.width + "x" + image.height + "). Resized to " + canvas.width + "x" + canvas.height, image ); return canvas; } return image; } function textureNeedsPowerOfTwo( texture ) { return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) { return texture.generateMipmaps && isPowerOfTwo && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { var texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { delete _videoTextures[ texture.id ]; } infoMemory.textures --; } function onRenderTargetDispose( event ) { var renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); infoMemory.textures --; } // function deallocateTexture( texture ) { var textureProperties = properties.get( texture ); if ( texture.image && textureProperties.__image__webglTextureCube ) { // cube texture _gl.deleteTexture( textureProperties.__image__webglTextureCube ); } else { // 2D texture if ( textureProperties.__webglInit === undefined ) return; _gl.deleteTexture( textureProperties.__webglTexture ); } // remove all webgl properties properties.remove( texture ); } function deallocateRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); if ( ! renderTarget ) return; if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } properties.remove( renderTarget.texture ); properties.remove( renderTarget ); } // function setTexture2D( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { var image = texture.image; if ( image === undefined ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is undefined", texture ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete", texture ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); } function setTextureCube( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.image.length === 6 ) { if ( texture.version > 0 && textureProperties.__version !== texture.version ) { if ( ! textureProperties.__image__webglTextureCube ) { texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__image__webglTextureCube = _gl.createTexture(); infoMemory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = ( texture && texture.isCompressedTexture ); var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } } var image = cubeImage[ 0 ], isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed ) { if ( isDataTexture ) { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); } } else { var mipmap, mipmaps = cubeImage[ i ].mipmaps; for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } else { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); } } } function setTextureCubeDynamic( texture, slot ) { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); } function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { var extension; if ( isPowerOfTwoImage ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.", texture ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.", texture ); } } extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension ) { if ( texture.type === FloatType && extensions.get( "OES_texture_float_linear" ) === null ) return; if ( texture.type === HalfFloatType && extensions.get( "OES_texture_half_float_linear" ) === null ) return; if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function uploadTexture( textureProperties, texture, slot ) { if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__webglTexture = _gl.createTexture(); if ( texture.isVideoTexture ) { _videoTextures[ texture.id ] = texture; } infoMemory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { image = makePowerOfTwo( image ); } var isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); var mipmap, mipmaps = texture.mipmaps; if ( texture.isDepthTexture ) { // populate depth texture with dummy data var internalFormat = _gl.DEPTH_COMPONENT; if ( texture.type === FloatType ) { if ( ! _isWebGL2 ) throw new Error( "Float Depth Texture only supported in WebGL2.0" ); internalFormat = _gl.DEPTH_COMPONENT32F; } else if ( _isWebGL2 ) { // WebGL 2.0 requires signed internalformat for glTexImage2D internalFormat = _gl.DEPTH_COMPONENT16; } if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedShortType; glType = utils.convert( texture.type ); } } // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.format === DepthStencilFormat ) { internalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } texture.generateMipmaps = false; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } else if ( texture.isCompressedTexture ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } texture.generateMipmaps = false; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); } } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) _gl.generateMipmap( _gl.TEXTURE_2D ); textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { var glFormat = utils.convert( renderTarget.texture.format ); var glType = utils.convert( renderTarget.texture.type ); state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { // FIXME: We don"t support !depth !stencil _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; if ( renderTarget.depthTexture.format === DepthFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); if ( renderTarget.depthTexture ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( var i = 0; i < 6; i ++ ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); } } else { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); } } _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); textureProperties.__webglTexture = _gl.createTexture(); infoMemory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( var i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); } if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) _gl.generateMipmap( _gl.TEXTURE_2D ); state.bindTexture( _gl.TEXTURE_2D, null ); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { var texture = renderTarget.texture; var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) { var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; var webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); _gl.generateMipmap( target ); state.bindTexture( target, null ); } } function updateVideoTextures() { for ( var id in _videoTextures ) { _videoTextures[ id ].update(); } } this.setTexture2D = setTexture2D; this.setTextureCube = setTextureCube; this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateVideoTextures = updateVideoTextures; } /** * @author fordacious / fordacious.github.io */ function WebGLProperties() { var properties = {}; function get( object ) { var uuid = object.uuid; var map = properties[ uuid ]; if ( map === undefined ) { map = {}; properties[ uuid ] = map; } return map; } function remove( object ) { delete properties[ object.uuid ]; } function clear() { properties = {}; } return { get: get, remove: remove, clear: clear }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLState( gl, extensions, utils ) { function ColorBuffer() { var locked = false; var color = new Vector4(); var currentColorMask = null; var currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { var locked = false; var currentDepthMask = null; var currentDepthFunc = null; var currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { if ( depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } } else { gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { var locked = false; var currentStencilMask = null; var currentStencilFunc = null; var currentStencilRef = null; var currentStencilFuncMask = null; var currentStencilFail = null; var currentStencilZFail = null; var currentStencilZPass = null; var currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // var colorBuffer = new ColorBuffer(); var depthBuffer = new DepthBuffer(); var stencilBuffer = new StencilBuffer(); var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var newAttributes = new Uint8Array( maxVertexAttributes ); var enabledAttributes = new Uint8Array( maxVertexAttributes ); var attributeDivisors = new Uint8Array( maxVertexAttributes ); var capabilities = {}; var compressedTextureFormats = null; var currentProgram = null; var currentBlending = null; var currentBlendEquation = null; var currentBlendSrc = null; var currentBlendDst = null; var currentBlendEquationAlpha = null; var currentBlendSrcAlpha = null; var currentBlendDstAlpha = null; var currentPremultipledAlpha = false; var currentFlipSided = null; var currentCullFace = null; var currentLineWidth = null; var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); var version = parseFloat( /^WebGL ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] ); var lineWidthAvailable = parseFloat( version ) >= 1.0; var currentTextureSlot = null; var currentBoundTextures = {}; var currentScissor = new Vector4(); var currentViewport = new Vector4(); function createTexture( type, target, count ) { var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. var texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( var i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } return texture; } var emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); enable( gl.BLEND ); setBlending( NormalBlending ); // function initAttributes() { for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== 0 ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); extension.vertexAttribDivisorANGLE( attribute, 0 ); attributeDivisors[ attribute ] = 0; } } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function enable( id ) { if ( capabilities[ id ] !== true ) { gl.enable( id ); capabilities[ id ] = true; } } function disable( id ) { if ( capabilities[ id ] !== false ) { gl.disable( id ); capabilities[ id ] = false; } } function getCompressedTextureFormats() { if ( compressedTextureFormats === null ) { compressedTextureFormats = []; if ( extensions.get( "WEBGL_compressed_texture_pvrtc" ) || extensions.get( "WEBGL_compressed_texture_s3tc" ) || extensions.get( "WEBGL_compressed_texture_etc1" ) ) { var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); for ( var i = 0; i < formats.length; i ++ ) { compressedTextureFormats.push( formats[ i ] ); } } } return compressedTextureFormats; } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending !== NoBlending ) { enable( gl.BLEND ); } else { disable( gl.BLEND ); } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { switch ( blending ) { case AdditiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); } break; case SubtractiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); } break; case MultiplyBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); } break; default: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } } } currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; } else { blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } } currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); var flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); material.transparent === true ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) : setBlending( NoBlending ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { if ( currentTextureSlot === null ) { activeTexture(); } var boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ currentTextureSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } // function reset() { for ( var i = 0; i < enabledAttributes.length; i ++ ) { if ( enabledAttributes[ i ] === 1 ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } capabilities = {}; compressedTextureFormats = null; currentTextureSlot = null; currentBoundTextures = {}; currentProgram = null; currentBlending = null; currentFlipSided = null; currentCullFace = null; colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, initAttributes: initAttributes, enableAttribute: enableAttribute, enableAttributeAndDivisor: enableAttributeAndDivisor, disableUnusedAttributes: disableUnusedAttributes, enable: enable, disable: disable, getCompressedTextureFormats: getCompressedTextureFormats, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, scissor: scissor, viewport: viewport, reset: reset }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLCapabilities( gl, extensions, parameters ) { var maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; var extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension !== null ) { maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } var precision = parameters.precision !== undefined ? parameters.precision : "highp"; var maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); var vertexTextures = maxVertexTextures > 0; var floatFragmentTextures = !! extensions.get( "OES_texture_float" ); var floatVertexTextures = vertexTextures && floatFragmentTextures; return { getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures }; } /** * @author mrdoob / http://mrdoob.com/ * @author greggman / http://games.greggman.com/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author tschw */ function PerspectiveCamera( fov, aspect, near, far ) { Camera.call( this ); this.type = "PerspectiveCamera"; this.fov = fov !== undefined ? fov : 50; this.zoom = 1; this.near = near !== undefined ? near : 0.1; this.far = far !== undefined ? far : 2000; this.focus = 10; this.aspect = aspect !== undefined ? aspect : 1; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: PerspectiveCamera, isPerspectiveCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; }, /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength: function ( focalLength ) { // see http://www.bobatkins.com/photography/technical/field_of_view.html var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); }, /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength: function () { var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; }, getEffectiveFOV: function () { return _Math.RAD2DEG * 2 * Math.atan( Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); }, getFilmWidth: function () { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); }, getFilmHeight: function () { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); }, /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * var w = 1920; * var h = 1080; * var fullWidth = w * 3; * var fullHeight = h * 2; * * --A-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var near = this.near, top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, height = 2 * top, width = this.aspect * height, left = - 0.5 * width, view = this.view; if ( this.view !== null && this.view.enabled ) { var fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } var skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ArrayCamera( array ) { PerspectiveCamera.call( this ); this.cameras = array || []; } ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { constructor: ArrayCamera, isArrayCamera: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebVRManager( renderer ) { var scope = this; var device = null; var frameData = null; var poseTarget = null; if ( typeof window !== "undefined" && "VRFrameData" in window ) { frameData = new window.VRFrameData(); } var matrixWorldInverse = new Matrix4(); var cameraL = new PerspectiveCamera(); cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 ); cameraL.layers.enable( 1 ); var cameraR = new PerspectiveCamera(); cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 ); cameraR.layers.enable( 2 ); var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); // var currentSize, currentPixelRatio; function onVRDisplayPresentChange() { if ( device !== null && device.isPresenting ) { var eyeParameters = device.getEyeParameters( "left" ); var renderWidth = eyeParameters.renderWidth; var renderHeight = eyeParameters.renderHeight; currentPixelRatio = renderer.getPixelRatio(); currentSize = renderer.getSize(); renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 ); } else if ( scope.enabled ) { renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); } } if ( typeof window !== "undefined" ) { window.addEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange, false ); } // this.enabled = false; this.getDevice = function () { return device; }; this.setDevice = function ( value ) { if ( value !== undefined ) device = value; }; this.setPoseTarget = function ( object ) { if ( object !== undefined ) poseTarget = object; }; this.getCamera = function ( camera ) { if ( device === null ) return camera; device.depthNear = camera.near; device.depthFar = camera.far; device.getFrameData( frameData ); // var pose = frameData.pose; var poseObject = poseTarget !== null ? poseTarget : camera; if ( pose.position !== null ) { poseObject.position.fromArray( pose.position ); } else { poseObject.position.set( 0, 0, 0 ); } if ( pose.orientation !== null ) { poseObject.quaternion.fromArray( pose.orientation ); } poseObject.updateMatrixWorld(); if ( device.isPresenting === false ) return camera; // cameraL.near = camera.near; cameraR.near = camera.near; cameraL.far = camera.far; cameraR.far = camera.far; cameraVR.matrixWorld.copy( camera.matrixWorld ); cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); var parent = poseObject.parent; if ( parent !== null ) { matrixWorldInverse.getInverse( parent.matrixWorld ); cameraL.matrixWorldInverse.multiply( matrixWorldInverse ); cameraR.matrixWorldInverse.multiply( matrixWorldInverse ); } // envMap and Mirror needs camera.matrixWorld cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse ); cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse ); cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); // HACK @mrdoob // https://github.com/w3c/webvr/issues/203 cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); // var layers = device.getLayers(); if ( layers.length ) { var layer = layers[ 0 ]; if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) { cameraL.bounds.fromArray( layer.leftBounds ); } if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) { cameraR.bounds.fromArray( layer.rightBounds ); } } return cameraVR; }; this.submitFrame = function () { if ( device && device.isPresenting ) device.submitFrame(); }; this.dispose = function () { if ( typeof window !== "undefined" ) { window.removeEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange ); } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLExtensions( gl ) { var extensions = {}; return { get: function ( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } var extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; case "WEBGL_compressed_texture_etc1": extension = gl.getExtension( "WEBGL_compressed_texture_etc1" ); break; default: extension = gl.getExtension( name ); } if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } extensions[ name ] = extension; return extension; } }; } /** * @author tschw */ function WebGLClipping() { var scope = this, globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false, plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping, camera ) { var enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes( planes, camera, 0 ); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { var nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4, dstArray = cache.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); for ( var i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } cache.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { var nPlanes = planes !== null ? planes.length : 0, dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { var flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; return dstArray; } } /** * @author thespite / http://www.twitter.com/thespite */ function WebGLUtils( gl, extensions ) { function convert( p ) { var extension; if ( p === RepeatWrapping ) return gl.REPEAT; if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE; if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT; if ( p === NearestFilter ) return gl.NEAREST; if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST; if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR; if ( p === LinearFilter ) return gl.LINEAR; if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST; if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) return extension.HALF_FLOAT_OES; } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBFormat ) return gl.RGB; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; if ( p === AddEquation ) return gl.FUNC_ADD; if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT; if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT; if ( p === ZeroFactor ) return gl.ZERO; if ( p === OneFactor ) return gl.ONE; if ( p === SrcColorFactor ) return gl.SRC_COLOR; if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR; if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA; if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA; if ( p === DstAlphaFactor ) return gl.DST_ALPHA; if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA; if ( p === DstColorFactor ) return gl.DST_COLOR; if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR; if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE; if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } } if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } } if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; } if ( p === MinEquation || p === MaxEquation ) { extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { if ( p === MinEquation ) return extension.MIN_EXT; if ( p === MaxEquation ) return extension.MAX_EXT; } } if ( p === UnsignedInt248Type ) { extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; } return 0; } return { convert: convert }; } /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ * @author tschw */ function WebGLRenderer( parameters ) { console.log( "THREE.WebGLRenderer", REVISION ); parameters = parameters || {}; var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default"; var lightsArray = []; var shadowsArray = []; var currentRenderList = null; var spritesArray = []; var flaresArray = []; // public properties this.domElement = _canvas; this.context = null; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.gammaFactor = 2.0; // for backwards compatibility this.gammaInput = false; this.gammaOutput = false; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = LinearToneMapping; this.toneMappingExposure = 1.0; this.toneMappingWhitePoint = 1.0; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // internal properties var _this = this, _isContextLost = false, // internal state cache _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = "", _currentCamera = null, _currentArrayCamera = null, _currentViewport = new Vector4(), _currentScissor = new Vector4(), _currentScissorTest = null, // _usedTextureUnits = 0, // _width = _canvas.width, _height = _canvas.height, _pixelRatio = 1, _viewport = new Vector4( 0, 0, _width, _height ), _scissor = new Vector4( 0, 0, _width, _height ), _scissorTest = false, // frustum _frustum = new Frustum(), // clipping _clipping = new WebGLClipping(), _clippingEnabled = false, _localClippingEnabled = false, // camera matrices cache _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(), // info _infoMemory = { geometries: 0, textures: 0 }, _infoRender = { frame: 0, calls: 0, vertices: 0, faces: 0, points: 0 }; this.info = { render: _infoRender, memory: _infoMemory, programs: null }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize var _gl; try { var contextAttributes = { alpha: _alpha, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference }; // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener( "webglcontextlost", onContextLost, false ); _canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); _gl = _context || _canvas.getContext( "webgl", contextAttributes ) || _canvas.getContext( "experimental-webgl", contextAttributes ); if ( _gl === null ) { if ( _canvas.getContext( "webgl" ) !== null ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); } var extensions, capabilities, state; var properties, textures, attributes, geometries, objects, lights; var programCache, renderLists; var background, morphtargets, bufferRenderer, indexedBufferRenderer; var flareRenderer, spriteRenderer; var utils; function initGLContext() { extensions = new WebGLExtensions( _gl ); extensions.get( "WEBGL_depth_texture" ); extensions.get( "OES_texture_float" ); extensions.get( "OES_texture_float_linear" ); extensions.get( "OES_texture_half_float" ); extensions.get( "OES_texture_half_float_linear" ); extensions.get( "OES_standard_derivatives" ); extensions.get( "OES_element_index_uint" ); extensions.get( "ANGLE_instanced_arrays" ); utils = new WebGLUtils( _gl, extensions ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); state = new WebGLState( _gl, extensions, utils ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, _infoMemory ); attributes = new WebGLAttributes( _gl ); geometries = new WebGLGeometries( _gl, attributes, _infoMemory ); objects = new WebGLObjects( geometries, _infoRender ); morphtargets = new WebGLMorphtargets( _gl ); programCache = new WebGLPrograms( _this, extensions, capabilities ); lights = new WebGLLights(); renderLists = new WebGLRenderLists(); background = new WebGLBackground( _this, state, geometries, _premultipliedAlpha ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, _infoRender ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); flareRenderer = new WebGLFlareRenderer( _this, _gl, state, textures, capabilities ); spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities ); _this.info.programs = programCache.programs; _this.context = _gl; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.state = state; } initGLContext(); // vr var vr = new WebVRManager( _this ); this.vr = vr; // shadow map var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); this.shadowMap = shadowMap; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function () { return { width: _width, height: _height }; }; this.setSize = function ( width, height, updateStyle ) { var device = vr.getDevice(); if ( device && device.isPresenting ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; _canvas.width = width * _pixelRatio; _canvas.height = height * _pixelRatio; if ( updateStyle !== false ) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function () { return { width: _width * _pixelRatio, height: _height * _pixelRatio }; }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = width * pixelRatio; _canvas.height = height * pixelRatio; this.setViewport( 0, 0, width, height ); }; this.setViewport = function ( x, y, width, height ) { _viewport.set( x, _height - y - height, width, height ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); }; this.setScissor = function ( x, y, width, height ) { _scissor.set( x, _height - y - height, width, height ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; // Clearing this.getClearColor = function () { return background.getClearColor(); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color, depth, stencil ) { var bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // this.dispose = function () { _canvas.removeEventListener( "webglcontextlost", onContextLost, false ); _canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); renderLists.dispose(); vr.dispose(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; initGLContext(); } function onMaterialDispose( event ) { var material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReference( material ); properties.remove( material ); } function releaseMaterialProgramReference( material ) { var programInfo = properties.get( material ).program; material.program = undefined; if ( programInfo !== undefined ) { programCache.releaseProgram( programInfo ); } } // Buffer rendering function renderObjectImmediate( object, program, material ) { object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); } this.renderBufferImmediate = function ( object, program, material ) { state.initAttributes(); var buffers = properties.get( object ); if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); var programAttributes = program.getAttributes(); if ( object.hasPositions ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.position ); _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); if ( ! material.isMeshPhongMaterial && ! material.isMeshStandardMaterial && ! material.isMeshNormalMaterial && material.flatShading === true ) { for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { var array = object.normalArray; var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; array[ i + 0 ] = nx; array[ i + 1 ] = ny; array[ i + 2 ] = nz; array[ i + 3 ] = nx; array[ i + 4 ] = ny; array[ i + 5 ] = nz; array[ i + 6 ] = nx; array[ i + 7 ] = ny; array[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.normal ); _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.uv ); _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== NoColors ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.color ); _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); } state.disableUnusedAttributes(); _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, fog, material, object ); var geometryProgram = geometry.id + "_" + program.id + "_" + ( material.wireframe === true ); var updateBuffers = false; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } if ( object.morphTargetInfluences ) { morphtargets.update( object, geometry, material, program ); updateBuffers = true; } // var index = geometry.index; var position = geometry.attributes.position; var rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); rangeFactor = 2; } var attribute; var renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } if ( updateBuffers ) { setupVertexAttributes( material, program, geometry ); if ( index !== null ) { _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); } } // var dataCount = 0; if ( index !== null ) { dataCount = index.count; } else if ( position !== undefined ) { dataCount = position.count; } var rangeStart = geometry.drawRange.start * rangeFactor; var rangeCount = geometry.drawRange.count * rangeFactor; var groupStart = group !== null ? group.start * rangeFactor : 0; var groupCount = group !== null ? group.count * rangeFactor : Infinity; var drawStart = Math.max( rangeStart, groupStart ); var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { switch ( object.drawMode ) { case TrianglesDrawMode: renderer.setMode( _gl.TRIANGLES ); break; case TriangleStripDrawMode: renderer.setMode( _gl.TRIANGLE_STRIP ); break; case TriangleFanDrawMode: renderer.setMode( _gl.TRIANGLE_FAN ); break; } } } else if ( object.isLine ) { var lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } if ( geometry && geometry.isInstancedBufferGeometry ) { if ( geometry.maxInstancedCount > 0 ) { renderer.renderInstances( geometry, drawStart, drawCount ); } } else { renderer.render( drawStart, drawCount ); } }; function setupVertexAttributes( material, program, geometry, startIndex ) { if ( geometry && geometry.isInstancedBufferGeometry ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) { console.error( "THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } if ( startIndex === undefined ) startIndex = 0; state.initAttributes(); var geometryAttributes = geometry.attributes; var programAttributes = program.getAttributes(); var materialDefaultAttributeValues = material.defaultAttributeValues; for ( var name in programAttributes ) { var programAttribute = programAttributes[ name ]; if ( programAttribute >= 0 ) { var geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute !== undefined ) { var normalized = geometryAttribute.normalized; var size = geometryAttribute.itemSize; var attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; var buffer = attribute.buffer; var type = attribute.type; var bytesPerElement = attribute.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { var data = geometryAttribute.data; var stride = data.stride; var offset = geometryAttribute.offset; if ( data && data.isInstancedInterleavedBuffer ) { state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = data.meshPerAttribute * data.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement ); } else { if ( geometryAttribute.isInstancedBufferAttribute ) { state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement ); } } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: _gl.vertexAttrib2fv( programAttribute, value ); break; case 3: _gl.vertexAttrib3fv( programAttribute, value ); break; case 4: _gl.vertexAttrib4fv( programAttribute, value ); break; default: _gl.vertexAttrib1fv( programAttribute, value ); } } } } } state.disableUnusedAttributes(); } // Compile this.compile = function ( scene, camera ) { lightsArray.length = 0; shadowsArray.length = 0; scene.traverse( function ( object ) { if ( object.isLight ) { lightsArray.push( object ); if ( object.castShadow ) { shadowsArray.push( object ); } } } ); lights.setup( lightsArray, shadowsArray, camera ); scene.traverse( function ( object ) { if ( object.material ) { if ( Array.isArray( object.material ) ) { for ( var i = 0; i < object.material.length; i ++ ) { initMaterial( object.material[ i ], scene.fog, object ); } } else { initMaterial( object.material, scene.fog, object ); } } } ); }; // Animation Loop var isAnimating = false; var onAnimationFrame = null; function start() { if ( isAnimating ) return; var device = vr.getDevice(); if ( device && device.isPresenting ) { device.requestAnimationFrame( loop ); } else { window.requestAnimationFrame( loop ); } isAnimating = true; } function loop( time ) { if ( onAnimationFrame !== null ) onAnimationFrame( time ); var device = vr.getDevice(); if ( device && device.isPresenting ) { device.requestAnimationFrame( loop ); } else { window.requestAnimationFrame( loop ); } } this.animate = function ( callback ) { onAnimationFrame = callback; start(); }; // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( ! ( camera && camera.isCamera ) ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost ) return; // reset caching for this frame _currentGeometryProgram = ""; _currentMaterialId = - 1; _currentCamera = null; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); if ( vr.enabled ) { camera = vr.getCamera( camera ); } _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); lightsArray.length = 0; shadowsArray.length = 0; spritesArray.length = 0; flaresArray.length = 0; _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); currentRenderList = renderLists.get( scene, camera ); currentRenderList.init(); projectObject( scene, camera, _this.sortObjects ); if ( _this.sortObjects === true ) { currentRenderList.sort(); } // textures.updateVideoTextures(); // if ( _clippingEnabled ) _clipping.beginShadows(); shadowMap.render( shadowsArray, scene, camera ); lights.setup( lightsArray, shadowsArray, camera ); if ( _clippingEnabled ) _clipping.endShadows(); // _infoRender.frame ++; _infoRender.calls = 0; _infoRender.vertices = 0; _infoRender.faces = 0; _infoRender.points = 0; if ( renderTarget === undefined ) { renderTarget = null; } this.setRenderTarget( renderTarget ); // background.render( currentRenderList, scene, camera, forceClear ); // render scene var opaqueObjects = currentRenderList.opaque; var transparentObjects = currentRenderList.transparent; if ( scene.overrideMaterial ) { var overrideMaterial = scene.overrideMaterial; if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); } else { // opaque pass (front-to-back order) if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); // transparent pass (back-to-front order) if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); } // custom renderers spriteRenderer.render( spritesArray, scene, camera ); flareRenderer.render( flaresArray, scene, camera, _currentViewport ); // Generate mipmap if we"re using any kind of mipmap filtering if ( renderTarget ) { textures.updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); if ( vr.enabled ) { vr.submitFrame(); } // _gl.finish(); }; /* // TODO Duplicated code (Frustum) var _sphere = new Sphere(); function isObjectViewable( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ). applyMatrix4( object.matrixWorld ); return isSphereViewable( _sphere ); } function isSpriteViewable( sprite ) { _sphere.center.set( 0, 0, 0 ); _sphere.radius = 0.7071067811865476; _sphere.applyMatrix4( sprite.matrixWorld ); return isSphereViewable( _sphere ); } function isSphereViewable( sphere ) { if ( ! _frustum.intersectsSphere( sphere ) ) return false; var numPlanes = _clipping.numPlanes; if ( numPlanes === 0 ) return true; var planes = _this.clippingPlanes, center = sphere.center, negRad = - sphere.radius, i = 0; do { // out when deeper than radius in the negative halfspace if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; } while ( ++ i !== numPlanes ); return true; } */ function projectObject( object, camera, sortObjects ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isLight ) { lightsArray.push( object ); if ( object.castShadow ) { shadowsArray.push( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { spritesArray.push( object ); } } else if ( object.isLensFlare ) { flaresArray.push( object ); } else if ( object.isImmediateRenderObject ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } currentRenderList.push( object, null, object.material, _vector3.z, null ); } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( object.isSkinnedMesh ) { object.skeleton.update(); } if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } var geometry = objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, _vector3.z, null ); } } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, sortObjects ); } } function renderObjects( renderList, scene, camera, overrideMaterial ) { for ( var i = 0, l = renderList.length; i < l; i ++ ) { var renderItem = renderList[ i ]; var object = renderItem.object; var geometry = renderItem.geometry; var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; var group = renderItem.group; if ( camera.isArrayCamera ) { _currentArrayCamera = camera; var cameras = camera.cameras; for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { var camera2 = cameras[ j ]; if ( object.layers.test( camera2.layers ) ) { var bounds = camera2.bounds; var x = bounds.x * _width; var y = bounds.y * _height; var width = bounds.z * _width; var height = bounds.w * _height; state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); renderObject( object, scene, camera2, geometry, material, group ); } } } else { _currentArrayCamera = null; renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); if ( object.isImmediateRenderObject ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, scene.fog, material, object ); _currentGeometryProgram = ""; renderObjectImmediate( object, program, material ); } else { _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } function initMaterial( material, fog, object ) { var materialProperties = properties.get( material ); var parameters = programCache.getParameters( material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); var code = programCache.getProgramCode( material, parameters ); var program = materialProperties.program; var programChange = true; if ( program === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); } else if ( program.code !== code ) { // changed glsl or parameters releaseMaterialProgramReference( material ); } else if ( parameters.shaderID !== undefined ) { // same glsl and uniform list return; } else { // only rebuild uniform list programChange = false; } if ( programChange ) { if ( parameters.shaderID ) { var shader = ShaderLib[ parameters.shaderID ]; materialProperties.shader = { name: material.type, uniforms: UniformsUtils.clone( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; } else { materialProperties.shader = { name: material.type, uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader }; } material.onBeforeCompile( materialProperties.shader ); program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); materialProperties.program = program; material.program = program; } var programAttributes = program.getAttributes(); if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { if ( programAttributes[ "morphTarget" + i ] >= 0 ) { material.numSupportedMorphTargets ++; } } } if ( material.morphNormals ) { material.numSupportedMorphNormals = 0; for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { if ( programAttributes[ "morphNormal" + i ] >= 0 ) { material.numSupportedMorphNormals ++; } } } var uniforms = materialProperties.shader.uniforms; if ( ! material.isShaderMaterial && ! material.isRawShaderMaterial || material.clipping === true ) { materialProperties.numClippingPlanes = _clipping.numPlanes; materialProperties.numIntersection = _clipping.numIntersection; uniforms.clippingPlanes = _clipping.uniform; } materialProperties.fog = fog; // store the light setup it was created for materialProperties.lightsHash = lights.state.hash; if ( material.lights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.directionalLights.value = lights.state.directional; uniforms.spotLights.value = lights.state.spot; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.pointLights.value = lights.state.point; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } var progUniforms = materialProperties.program.getUniforms(), uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.uniformsList = uniformsList; } function setProgram( camera, fog, material, object ) { _usedTextureUnits = 0; var materialProperties = properties.get( material ); if ( _clippingEnabled ) { if ( _localClippingEnabled || camera !== _currentCamera ) { var useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) _clipping.setState( material.clippingPlanes, material.clipIntersection, material.clipShadows, camera, materialProperties, useCache ); } } if ( material.needsUpdate === false ) { if ( materialProperties.program === undefined ) { material.needsUpdate = true; } else if ( material.fog && materialProperties.fog !== fog ) { material.needsUpdate = true; } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { material.needsUpdate = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== _clipping.numPlanes || materialProperties.numIntersection !== _clipping.numIntersection ) ) { material.needsUpdate = true; } } if ( material.needsUpdate ) { initMaterial( material, fog, object ); material.needsUpdate = false; } var refreshProgram = false; var refreshMaterial = false; var refreshLights = false; var program = materialProperties.program, p_uniforms = program.getUniforms(), m_uniforms = materialProperties.shader.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } // Avoid unneeded uniform updates per ArrayCamera"s sub-camera if ( _currentCamera !== ( _currentArrayCamera || camera ) ) { _currentCamera = ( _currentArrayCamera || camera ); // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.envMap ) { var uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.skinning ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning uniforms must be set even if material didn"t change // auto-setting of texture unit for bone texture must go before other textures // not sure why, but otherwise weird things happen if ( material.skinning ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); var skeleton = object.skeleton; if ( skeleton ) { var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === undefined ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix size = _Math.ceilPowerOfTwo( size ); size = Math.max( size, 4 ); var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( skeleton.boneMatrices ); // copy current values var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); skeleton.boneMatrices = boneMatrices; skeleton.boneTexture = boneTexture; skeleton.boneTextureSize = size; } p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { p_uniforms.setOptional( _gl, skeleton, "boneMatrices" ); } } } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); p_uniforms.setValue( _gl, "toneMappingWhitePoint", _this.toneMappingWhitePoint ); if ( material.lights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( m_uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsLambert( m_uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshToonMaterial ) { refreshUniformsToon( m_uniforms, material ); } else { refreshUniformsPhong( m_uniforms, material ); } } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( m_uniforms, material ); } else { refreshUniformsStandard( m_uniforms, material ); } } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDepth( m_uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDistance( m_uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsNormal( m_uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( m_uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( m_uniforms, material ); } else if ( material.isShadowMaterial ) { m_uniforms.color.value = material.color; m_uniforms.opacity.value = material.opacity; } // RectAreaLight Texture // TODO (mrdoob): Find a nicer implementation if ( m_uniforms.ltcMat !== undefined ) m_uniforms.ltcMat.value = UniformsLib.LTC_MAT_TEXTURE; if ( m_uniforms.ltcMag !== undefined ) m_uniforms.ltcMag.value = UniformsLib.LTC_MAG_TEXTURE; WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); return program; } // Uniforms (refresh uniforms objects) function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value = material.color; } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; } if ( material.envMap ) { uniforms.envMap.value = material.envMap; // don"t flip CubeTexture envMaps, flip everything else: // WebGLRenderTargetCube will be flipped for backwards compatibility // WebGLRenderTargetCube.texture will be flipped because it"s a Texture and NOT a CubeTexture // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; uniforms.reflectivity.value = material.reflectivity; uniforms.refractionRatio.value = material.refractionRatio; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. normal map // 4. bump map // 5. alpha map // 6. emissive map var uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.displacementMap ) { uvScaleMap = material.displacementMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.roughnessMap ) { uvScaleMap = material.roughnessMap; } else if ( material.metalnessMap ) { uvScaleMap = material.metalnessMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } else if ( material.emissiveMap ) { uvScaleMap = material.emissiveMap; } if ( uvScaleMap !== undefined ) { // backwards compatibility if ( uvScaleMap.isWebGLRenderTarget ) { uvScaleMap = uvScaleMap.texture; } if ( uvScaleMap.matrixAutoUpdate === true ) { var offset = uvScaleMap.offset; var repeat = uvScaleMap.repeat; var rotation = uvScaleMap.rotation; var center = uvScaleMap.center; uvScaleMap.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y ); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * _pixelRatio; uniforms.scale.value = _height * 0.5; uniforms.map.value = material.map; if ( material.map !== null ) { if ( material.map.matrixAutoUpdate === true ) { var offset = material.map.offset; var repeat = material.map.repeat; var rotation = material.map.rotation; var center = material.map.center; material.map.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y ); } uniforms.uvTransform.value.copy( material.map.matrix ); } } function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshUniformsLambert( uniforms, material ) { if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value = material.specular; uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon( uniforms, material ) { refreshUniformsPhong( uniforms, material ); if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; } if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material ) { uniforms.clearCoat.value = material.clearCoat; uniforms.clearCoatRoughness.value = material.clearCoatRoughness; refreshUniformsStandard( uniforms, material ); } function refreshUniformsDepth( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy( material.referencePosition ); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal( uniforms, material ) { if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } // GL state setting this.setFaceCulling = function ( cullFace, frontFaceDirection ) { state.setCullFace( cullFace ); state.setFlipSided( frontFaceDirection === FrontFaceDirectionCW ); }; // Textures function allocTextureUnit() { var textureUnit = _usedTextureUnits; if ( textureUnit >= capabilities.maxTextures ) { console.warn( "THREE.WebGLRenderer: Trying to use " + textureUnit + " texture units while this GPU supports only " + capabilities.maxTextures ); } _usedTextureUnits += 1; return textureUnit; } this.allocTextureUnit = allocTextureUnit; // this.setTexture2D = setTexture2D; this.setTexture2D = ( function () { var warned = false; // backwards compatibility: peel texture.texture return function setTexture2D( texture, slot ) { if ( texture && texture.isWebGLRenderTarget ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTexture2D: don"t use render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } textures.setTexture2D( texture, slot ); }; }() ); this.setTexture = ( function () { var warned = false; return function setTexture( texture, slot ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); warned = true; } textures.setTexture2D( texture, slot ); }; }() ); this.setTextureCube = ( function () { var warned = false; return function setTextureCube( texture, slot ) { // backwards compatibility: peel texture.texture if ( texture && texture.isWebGLRenderTargetCube ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTextureCube: don"t use cube render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture // TODO: unify these code paths if ( ( texture && texture.isCubeTexture ) || ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ // this function alone should take care of cube textures textures.setTextureCube( texture, slot ); } else { // assumed: texture property of THREE.WebGLRenderTargetCube textures.setTextureCubeDynamic( texture, slot ); } }; }() ); this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTarget = function ( renderTarget ) { _currentRenderTarget = renderTarget; if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } var framebuffer = null; var isCube = false; if ( renderTarget ) { var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLRenderTargetCube ) { framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; isCube = true; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); _currentScissorTest = _scissorTest; } if ( _currentFramebuffer !== framebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _currentFramebuffer = framebuffer; } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { var textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); } }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } var framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( framebuffer ) { var restore = false; if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); restore = true; } try { var texture = renderTarget.texture; var textureFormat = texture.format; var textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( extensions.get( "OES_texture_float" ) || extensions.get( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! ( textureType === HalfFloatType && extensions.get( "EXT_color_buffer_half_float" ) ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } else { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete." ); } } finally { if ( restore ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); } } } }; } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function FogExp2( color, density ) { this.name = ""; this.color = new Color( color ); this.density = ( density !== undefined ) ? density : 0.00025; } FogExp2.prototype.isFogExp2 = true; FogExp2.prototype.clone = function () { return new FogExp2( this.color.getHex(), this.density ); }; FogExp2.prototype.toJSON = function ( /* meta */ ) { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Fog( color, near, far ) { this.name = ""; this.color = new Color( color ); this.near = ( near !== undefined ) ? near : 1; this.far = ( far !== undefined ) ? far : 1000; } Fog.prototype.isFog = true; Fog.prototype.clone = function () { return new Fog( this.color.getHex(), this.near, this.far ); }; Fog.prototype.toJSON = function ( /* meta */ ) { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; }; /** * @author mrdoob / http://mrdoob.com/ */ function Scene() { Object3D.call( this ); this.type = "Scene"; this.background = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer } Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Scene, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); return data; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function LensFlare( texture, size, distance, blending, color ) { Object3D.call( this ); this.lensFlares = []; this.positionScreen = new Vector3(); this.customUpdateCallback = undefined; if ( texture !== undefined ) { this.add( texture, size, distance, blending, color ); } } LensFlare.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LensFlare, isLensFlare: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.positionScreen.copy( source.positionScreen ); this.customUpdateCallback = source.customUpdateCallback; for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { this.lensFlares.push( source.lensFlares[ i ] ); } return this; }, add: function ( texture, size, distance, blending, color, opacity ) { if ( size === undefined ) size = - 1; if ( distance === undefined ) distance = 0; if ( opacity === undefined ) opacity = 1; if ( color === undefined ) color = new Color( 0xffffff ); if ( blending === undefined ) blending = NormalBlending; distance = Math.min( distance, Math.max( 0, distance ) ); this.lensFlares.push( { texture: texture, // THREE.Texture size: size, // size in pixels (-1 = use texture.width) distance: distance, // distance (0-1) from light source (0=at light source) x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back scale: 1, // scale rotation: 0, // rotation opacity: opacity, // opacity color: color, // color blending: blending // blending } ); }, /* * Update lens flares update positions on all flares based on the screen position * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. */ updateLensFlares: function () { var f, fl = this.lensFlares.length; var flare; var vecX = - this.positionScreen.x * 2; var vecY = - this.positionScreen.y * 2; for ( f = 0; f < fl; f ++ ) { flare = this.lensFlares[ f ]; flare.x = this.positionScreen.x + vecX * flare.distance; flare.y = this.positionScreen.y + vecY * flare.distance; flare.wantedRotation = flare.x * Math.PI * 0.25; flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; } } } ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * uvOffset: new THREE.Vector2(), * uvScale: new THREE.Vector2() * } */ function SpriteMaterial( parameters ) { Material.call( this ); this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.rotation = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } SpriteMaterial.prototype = Object.create( Material.prototype ); SpriteMaterial.prototype.constructor = SpriteMaterial; SpriteMaterial.prototype.isSpriteMaterial = true; SpriteMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.rotation = source.rotation; return this; }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function Sprite( material ) { Object3D.call( this ); this.type = "Sprite"; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); } Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Sprite, isSprite: true, raycast: ( function () { var intersectPoint = new Vector3(); var worldPosition = new Vector3(); var worldScale = new Vector3(); return function raycast( raycaster, intersects ) { worldPosition.setFromMatrixPosition( this.matrixWorld ); raycaster.ray.closestPointToPoint( worldPosition, intersectPoint ); worldScale.setFromMatrixScale( this.matrixWorld ); var guessSizeSq = worldScale.x * worldScale.y / 4; if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return; var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: intersectPoint.clone(), face: null, object: this } ); }; }() ), clone: function () { return new this.constructor( this.material ).copy( this ); } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function LOD() { Object3D.call( this ); this.type = "LOD"; Object.defineProperties( this, { levels: { enumerable: true, value: [] } } ); } LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LOD, copy: function ( source ) { Object3D.prototype.copy.call( this, source, false ); var levels = source.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; this.addLevel( level.object.clone(), level.distance ); } return this; }, addLevel: function ( object, distance ) { if ( distance === undefined ) distance = 0; distance = Math.abs( distance ); var levels = this.levels; for ( var l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, object: object } ); this.add( object ); }, getObjectForDistance: function ( distance ) { var levels = this.levels; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance < levels[ i ].distance ) { break; } } return levels[ i - 1 ].object; }, raycast: ( function () { var matrixPosition = new Vector3(); return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( matrixPosition ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); }; }() ), update: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function update( camera ) { var levels = this.levels; if ( levels.length > 1 ) { v1.setFromMatrixPosition( camera.matrixWorld ); v2.setFromMatrixPosition( this.matrixWorld ); var distance = v1.distanceTo( v2 ); levels[ 0 ].object.visible = true; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance >= levels[ i ].distance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } }; }(), toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.levels = []; var levels = this.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance } ); } return data; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author michael guerrero / http://realitymeltdown.com * @author ikerr / http://verold.com */ function Skeleton( bones, boneInverses ) { // copy the bone array bones = bones || []; this.bones = bones.slice( 0 ); this.boneMatrices = new Float32Array( this.bones.length * 16 ); // use the supplied bone inverses or calculate the inverses if ( boneInverses === undefined ) { this.calculateInverses(); } else { if ( this.bones.length === boneInverses.length ) { this.boneInverses = boneInverses.slice( 0 ); } else { console.warn( "THREE.Skeleton boneInverses is the wrong length." ); this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { this.boneInverses.push( new Matrix4() ); } } } } Object.assign( Skeleton.prototype, { calculateInverses: function () { this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { var inverse = new Matrix4(); if ( this.bones[ i ] ) { inverse.getInverse( this.bones[ i ].matrixWorld ); } this.boneInverses.push( inverse ); } }, pose: function () { var bone, i, il; // recover the bind-time world matrices for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { bone.matrixWorld.getInverse( this.boneInverses[ i ] ); } } // compute the local matrices, positions, rotations and scales for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.getInverse( bone.parent.matrixWorld ); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } }, update: ( function () { var offsetMatrix = new Matrix4(); var identityMatrix = new Matrix4(); return function update() { var bones = this.bones; var boneInverses = this.boneInverses; var boneMatrices = this.boneMatrices; var boneTexture = this.boneTexture; // flatten bone matrices to array for ( var i = 0, il = bones.length; i < il; i ++ ) { // compute the offset between the current and the original transform var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix; offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); offsetMatrix.toArray( boneMatrices, i * 16 ); } if ( boneTexture !== undefined ) { boneTexture.needsUpdate = true; } }; } )(), clone: function () { return new Skeleton( this.bones, this.boneInverses ); } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function Bone() { Object3D.call( this ); this.type = "Bone"; } Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Bone, isBone: true } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function SkinnedMesh( geometry, material ) { Mesh.call( this, geometry, material ); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); var bones = this.initBones(); var skeleton = new Skeleton( bones ); this.bind( skeleton, this.matrixWorld ); this.normalizeSkinWeights(); } SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { constructor: SkinnedMesh, isSkinnedMesh: true, initBones: function () { var bones = [], bone, gbone; var i, il; if ( this.geometry && this.geometry.bones !== undefined ) { // first, create array of "Bone" objects from geometry data for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; // create new "Bone" object bone = new Bone(); bones.push( bone ); // apply values bone.name = gbone.name; bone.position.fromArray( gbone.pos ); bone.quaternion.fromArray( gbone.rotq ); if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); } // second, create bone hierarchy for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { // subsequent bones in the hierarchy bones[ gbone.parent ].add( bones[ i ] ); } else { // topmost bone, immediate child of the skinned mesh this.add( bones[ i ] ); } } } // now the bones are part of the scene graph and children of the skinned mesh. // let"s update the corresponding matrices this.updateMatrixWorld( true ); return bones; }, bind: function ( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.getInverse( bindMatrix ); }, pose: function () { this.skeleton.pose(); }, normalizeSkinWeights: function () { var scale, i; if ( this.geometry && this.geometry.isGeometry ) { for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) { var sw = this.geometry.skinWeights[ i ]; scale = 1.0 / sw.manhattanLength(); if ( scale !== Infinity ) { sw.multiplyScalar( scale ); } else { sw.set( 1, 0, 0, 0 ); // do something reasonable } } } else if ( this.geometry && this.geometry.isBufferGeometry ) { var vec = new Vector4(); var skinWeight = this.geometry.attributes.skinWeight; for ( i = 0; i < skinWeight.count; i ++ ) { vec.x = skinWeight.getX( i ); vec.y = skinWeight.getY( i ); vec.z = skinWeight.getZ( i ); vec.w = skinWeight.getW( i ); scale = 1.0 / vec.manhattanLength(); if ( scale !== Infinity ) { vec.multiplyScalar( scale ); } else { vec.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); } } }, updateMatrixWorld: function ( force ) { Mesh.prototype.updateMatrixWorld.call( this, force ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.getInverse( this.matrixWorld ); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.getInverse( this.bindMatrix ); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode ); } }, clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ function LineBasicMaterial( parameters ) { Material.call( this ); this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.lights = false; this.setValues( parameters ); } LineBasicMaterial.prototype = Object.create( Material.prototype ); LineBasicMaterial.prototype.constructor = LineBasicMaterial; LineBasicMaterial.prototype.isLineBasicMaterial = true; LineBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function Line( geometry, material, mode ) { if ( mode === 1 ) { console.warn( "THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead." ); return new LineSegments( geometry, material ); } Object3D.call( this ); this.type = "Line"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); } Line.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Line, isLine: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; var precisionSq = precision * precision; var geometry = this.geometry; var matrixWorld = this.matrixWorld; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var vStart = new Vector3(); var vEnd = new Vector3(); var interSegment = new Vector3(); var interRay = new Vector3(); var step = ( this && this.isLineSegments ) ? 2 : 1; if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, l = indices.length - 1; i < l; i += step ) { var a = indices[ i ]; var b = indices[ i + 1 ]; vStart.fromArray( positions, a * 3 ); vEnd.fromArray( positions, b * 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { vStart.fromArray( positions, 3 * i ); vEnd.fromArray( positions, 3 * i + 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var nbVertices = vertices.length; for ( var i = 0; i < nbVertices - 1; i += step ) { var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LineSegments( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineSegments"; } LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineSegments, isLineSegments: true } ); /** * @author mgreter / http://github.com/mgreter */ function LineLoop( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineLoop"; } LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineLoop, isLineLoop: true, } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * size: , * sizeAttenuation: * } */ function PointsMaterial( parameters ) { Material.call( this ); this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.size = 1; this.sizeAttenuation = true; this.lights = false; this.setValues( parameters ); } PointsMaterial.prototype = Object.create( Material.prototype ); PointsMaterial.prototype.constructor = PointsMaterial; PointsMaterial.prototype.isPointsMaterial = true; PointsMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; }; /** * @author alteredq / http://alteredqualia.com/ */ function Points( geometry, material ) { Object3D.call( this ); this.type = "Points"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); } Points.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Points, isPoints: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var object = this; var geometry = this.geometry; var matrixWorld = this.matrixWorld; var threshold = raycaster.params.Points.threshold; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); var localThresholdSq = localThreshold * localThreshold; var position = new Vector3(); function testPoint( point, index ) { var rayPointDistanceSq = ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { var intersectPoint = ray.closestPointToPoint( point ); intersectPoint.applyMatrix4( matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint.clone(), index: index, face: null, object: object } ); } } if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, il = indices.length; i < il; i ++ ) { var a = indices[ i ]; position.fromArray( positions, a * 3 ); testPoint( position, a ); } } else { for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { position.fromArray( positions, i * 3 ); testPoint( position, i ); } } } else { var vertices = geometry.vertices; for ( var i = 0, l = vertices.length; i < l; i ++ ) { testPoint( vertices[ i ], i ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Group() { Object3D.call( this ); this.type = "Group"; } Group.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Group, isGroup: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.generateMipmaps = false; // Set needsUpdate when first frame is ready var scope = this; function onLoaded() { video.removeEventListener( "loadeddata", onLoaded, false ); scope.needsUpdate = true; } video.addEventListener( "loadeddata", onLoaded, false ); } VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { constructor: VideoTexture, isVideoTexture: true, update: function () { var video = this.image; if ( video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } CompressedTexture.prototype = Object.create( Texture.prototype ); CompressedTexture.prototype.constructor = CompressedTexture; CompressedTexture.prototype.isCompressedTexture = true; /** * @author Matt DesLauriers / @mattdesl * @author atix / arthursilber.de */ function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } DepthTexture.prototype = Object.create( Texture.prototype ); DepthTexture.prototype.constructor = DepthTexture; DepthTexture.prototype.isDepthTexture = true; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function WireframeGeometry( geometry ) { BufferGeometry.call( this ); this.type = "WireframeGeometry"; // buffer var vertices = []; // helper variables var i, j, l, o, ol; var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; var key, keys = [ "a", "b", "c" ]; var vertex; // different logic for Geometry and BufferGeometry if ( geometry && geometry.isGeometry ) { // create a data structure that contains all edges without duplicates var faces = geometry.faces; for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex = geometry.vertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = geometry.vertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } else if ( geometry && geometry.isBufferGeometry ) { var position, indices, groups; var group, start, count; var index1, index2; vertex = new Vector3(); if ( geometry.index !== null ) { // indexed BufferGeometry position = geometry.attributes.position; indices = geometry.index; groups = geometry.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; } // create a data structure that contains all eges without duplicates for ( o = 0, ol = groups.length; o < ol; ++ o ) { group = groups[ o ]; start = group.start; count = group.count; for ( i = start, l = ( start + count ); i < l; i += 3 ) { for ( j = 0; j < 3; j ++ ) { edge1 = indices.getX( i + j ); edge2 = indices.getX( i + ( j + 1 ) % 3 ); edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex.fromBufferAttribute( position, e.index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); vertex.fromBufferAttribute( position, e.index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } else { // non-indexed BufferGeometry position = geometry.attributes.position; for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { for ( j = 0; j < 3; j ++ ) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) index1 = 3 * i + j; vertex.fromBufferAttribute( position, index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); index2 = 3 * i + ( ( j + 1 ) % 3 ); vertex.fromBufferAttribute( position, index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); WireframeGeometry.prototype.constructor = WireframeGeometry; /** * @author zz85 / https://github.com/zz85 * @author Mugen87 / https://github.com/Mugen87 * * Parametric Surfaces Geometry * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 */ // ParametricGeometry function ParametricGeometry( func, slices, stacks ) { Geometry.call( this ); this.type = "ParametricGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); this.mergeVertices(); } ParametricGeometry.prototype = Object.create( Geometry.prototype ); ParametricGeometry.prototype.constructor = ParametricGeometry; // ParametricBufferGeometry function ParametricBufferGeometry( func, slices, stacks ) { BufferGeometry.call( this ); this.type = "ParametricBufferGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; var EPS = 0.00001; var normal = new Vector3(); var p0 = new Vector3(), p1 = new Vector3(); var pu = new Vector3(), pv = new Vector3(); var i, j; // generate vertices, normals and uvs var sliceCount = slices + 1; for ( i = 0; i <= stacks; i ++ ) { var v = i / stacks; for ( j = 0; j <= slices; j ++ ) { var u = j / slices; // vertex p0 = func( u, v, p0 ); vertices.push( p0.x, p0.y, p0.z ); // normal // approximate tangent vectors via finite differences if ( u - EPS >= 0 ) { p1 = func( u - EPS, v, p1 ); pu.subVectors( p0, p1 ); } else { p1 = func( u + EPS, v, p1 ); pu.subVectors( p1, p0 ); } if ( v - EPS >= 0 ) { p1 = func( u, v - EPS, p1 ); pv.subVectors( p0, p1 ); } else { p1 = func( u, v + EPS, p1 ); pv.subVectors( p1, p0 ); } // cross product of tangent vectors returns surface normal normal.crossVectors( pu, pv ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, v ); } } // generate indices for ( i = 0; i < stacks; i ++ ) { for ( j = 0; j < slices; j ++ ) { var a = i * sliceCount + j; var b = i * sliceCount + j + 1; var c = ( i + 1 ) * sliceCount + j + 1; var d = ( i + 1 ) * sliceCount + j; // faces one and two indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; /** * @author clockworkgeek / https://github.com/clockworkgeek * @author timothypratley / https://github.com/timothypratley * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ // PolyhedronGeometry function PolyhedronGeometry( vertices, indices, radius, detail ) { Geometry.call( this ); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); this.mergeVertices(); } PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; // PolyhedronBufferGeometry function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { BufferGeometry.call( this ); this.type = "PolyhedronBufferGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; radius = radius || 1; detail = detail || 0; // default buffer data var vertexBuffer = []; var uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius appplyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.addAttribute( "position", new Float32BufferAttribute( vertexBuffer, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvBuffer, 2 ) ); if ( detail === 0 ) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide( detail ) { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for ( var i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { var cols = Math.pow( 2, detail ); // we use this multidimensional array as a data structure for creating the subdivision var v = []; var i, j; // construct all of the vertices for this subdivision for ( i = 0; i <= cols; i ++ ) { v[ i ] = []; var aj = a.clone().lerp( c, i / cols ); var bj = b.clone().lerp( c, i / cols ); var rows = cols - i; for ( j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( i = 0; i < cols; i ++ ) { for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { var k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function appplyRadius( radius ) { var vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { var vertex = new Vector3(); for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; var u = azimuth( vertex ) / 2 / Math.PI + 0.5; var v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( var i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face var x0 = uvBuffer[ i + 0 ]; var x1 = uvBuffer[ i + 2 ]; var x2 = uvBuffer[ i + 4 ]; var max = Math.max( x0, x1, x2 ); var min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { var stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); var centroid = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); var azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // TetrahedronGeometry function TetrahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; // TetrahedronBufferGeometry function TetrahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; var indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "TetrahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // OctahedronGeometry function OctahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } OctahedronGeometry.prototype = Object.create( Geometry.prototype ); OctahedronGeometry.prototype.constructor = OctahedronGeometry; // OctahedronBufferGeometry function OctahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; var indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "OctahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // IcosahedronGeometry function IcosahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; // IcosahedronBufferGeometry function IcosahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; var indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "IcosahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; /** * @author Abe Pazos / https://hamoid.com * @author Mugen87 / https://github.com/Mugen87 */ // DodecahedronGeometry function DodecahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; // DodecahedronBufferGeometry function DodecahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var r = 1 / t; var vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; var indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "DodecahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; /** * @author oosmoxiecode / https://github.com/oosmoxiecode * @author WestLangley / https://github.com/WestLangley * @author zz85 / https://github.com/zz85 * @author miningold / https://github.com/miningold * @author jonobr1 / https://github.com/jonobr1 * @author Mugen87 / https://github.com/Mugen87 * */ // TubeGeometry function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { Geometry.call( this ); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; if ( taper !== undefined ) console.warn( "THREE.TubeGeometry: taper has been removed." ); var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); // expose internals this.tangents = bufferGeometry.tangents; this.normals = bufferGeometry.normals; this.binormals = bufferGeometry.binormals; // create geometry this.fromBufferGeometry( bufferGeometry ); this.mergeVertices(); } TubeGeometry.prototype = Object.create( Geometry.prototype ); TubeGeometry.prototype.constructor = TubeGeometry; // TubeBufferGeometry function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { BufferGeometry.call( this ); this.type = "TubeBufferGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; tubularSegments = tubularSegments || 64; radius = radius || 1; radialSegments = radialSegments || 8; closed = closed || false; var frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables var vertex = new Vector3(); var normal = new Vector3(); var uv = new Vector2(); var P = new Vector3(); var i, j; // buffer var vertices = []; var normals = []; var uvs = []; var indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt( i / tubularSegments, P ); // retrieve corresponding normal and binormal var N = frames.normals[ i ]; var B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( j = 0; j <= radialSegments; j ++ ) { var v = j / radialSegments * Math.PI * 2; var sin = Math.sin( v ); var cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( i = 0; i <= tubularSegments; i ++ ) { for ( j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; /** * @author oosmoxiecode * @author Mugen87 / https://github.com/Mugen87 * * based on http://www.blackpawn.com/texts/pqtorus/ */ // TorusKnotGeometry function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { Geometry.call( this ); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; if ( heightScale !== undefined ) console.warn( "THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead." ); this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); this.mergeVertices(); } TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; // TorusKnotBufferGeometry function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { BufferGeometry.call( this ); this.type = "TorusKnotBufferGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; radius = radius || 1; tube = tube || 0.4; tubularSegments = Math.floor( tubularSegments ) || 64; radialSegments = Math.floor( radialSegments ) || 8; p = p || 2; q = q || 3; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, j; var vertex = new Vector3(); var normal = new Vector3(); var P1 = new Vector3(); var P2 = new Vector3(); var B = new Vector3(); var T = new Vector3(); var N = new Vector3(); // generate vertices, normals and uvs for ( i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement var u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for ( j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. var v = j / radialSegments * Math.PI * 2; var cx = - tube * Math.cos( v ); var cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { // indices var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { var cu = Math.cos( u ); var su = Math.sin( u ); var quOverP = q / p * u; var cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; /** * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // TorusGeometry function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { Geometry.call( this ); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); this.mergeVertices(); } TorusGeometry.prototype = Object.create( Geometry.prototype ); TorusGeometry.prototype.constructor = TorusGeometry; // TorusBufferGeometry function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { BufferGeometry.call( this ); this.type = "TorusBufferGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radius = radius || 1; tube = tube || 0.4; radialSegments = Math.floor( radialSegments ) || 8; tubularSegments = Math.floor( tubularSegments ) || 6; arc = arc || Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var center = new Vector3(); var vertex = new Vector3(); var normal = new Vector3(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= radialSegments; j ++ ) { for ( i = 0; i <= tubularSegments; i ++ ) { var u = i / tubularSegments * arc; var v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); normal.subVectors( vertex, center ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= radialSegments; j ++ ) { for ( i = 1; i <= tubularSegments; i ++ ) { // indices var a = ( tubularSegments + 1 ) * j + i - 1; var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; var d = ( tubularSegments + 1 ) * j + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * Port from https://github.com/mapbox/earcut (v2.1.2) */ var Earcut = { triangulate: function ( data, holeIndices, dim ) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, outerNode = linkedList( data, 0, outerLen, dim, true ), triangles = []; if ( ! outerNode ) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if ( data.length > 80 * dim ) { minX = maxX = data[ 0 ]; minY = maxY = data[ 1 ]; for ( var i = dim; i < outerLen; i += dim ) { x = data[ i ]; y = data[ i + 1 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max( maxX - minX, maxY - minY ); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList( data, start, end, dim, clockwise ) { var i, last; if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } else { for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } if ( last && equals( last, last.next ) ) { removeNode( last ); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints( start, end ) { if ( ! start ) return start; if ( ! end ) end = start; var p = start, again; do { again = false; if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { removeNode( p ); p = end = p.prev; if ( p === p.next ) break; again = true; } else { p = p.next; } } while ( again || p !== end ); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { if ( ! ear ) return; // interlink polygon nodes in z-order if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); var stop = ear, prev, next; // iterate through ears, slicing them one by one while ( ear.prev !== ear.next ) { prev = ear.prev; next = ear.next; if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { // cut off the triangle triangles.push( prev.i / dim ); triangles.push( ear.i / dim ); triangles.push( next.i / dim ); removeNode( ear ); // skipping the next vertice leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if ( ear === stop ) { // try filtering points and slicing again if ( ! pass ) { earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); // if this didn"t work, try curing all small self-intersections locally } else if ( pass === 1 ) { ear = cureLocalIntersections( ear, triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { splitEarcut( ear, triangles, dim, minX, minY, invSize ); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar( ear ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear var p = ear.next.next; while ( p !== ear.prev ) { if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) { return false; } p = p.next; } return true; } function isEarHashed( ear, minX, minY, invSize ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); // z-order range for the current triangle bbox; var minZ = zOrder( minTX, minTY, minX, minY, invSize ), maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); // first look for points inside the triangle in increasing z-order var p = ear.nextZ; while ( p && p.z <= maxZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.nextZ; } // then look for points in decreasing z-order p = ear.prevZ; while ( p && p.z >= minZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections( start, triangles, dim ) { var p = start; do { var a = p.prev, b = p.next.next; if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { triangles.push( a.i / dim ); triangles.push( p.i / dim ); triangles.push( b.i / dim ); // remove two nodes involved removeNode( p ); removeNode( p.next ); p = start = b; } p = p.next; } while ( p !== start ); return p; } // try splitting polygon into two and triangulate them independently function splitEarcut( start, triangles, dim, minX, minY, invSize ) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while ( b !== a.prev ) { if ( a.i !== b.i && isValidDiagonal( a, b ) ) { // split the polygon in two by the diagonal var c = splitPolygon( a, b ); // filter colinear points around the cuts a = filterPoints( a, a.next ); c = filterPoints( c, c.next ); // run earcut on each half earcutLinked( a, triangles, dim, minX, minY, invSize ); earcutLinked( c, triangles, dim, minX, minY, invSize ); return; } b = b.next; } a = a.next; } while ( a !== start ); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles( data, holeIndices, outerNode, dim ) { var queue = [], i, len, start, end, list; for ( i = 0, len = holeIndices.length; i < len; i ++ ) { start = holeIndices[ i ] * dim; end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; list = linkedList( data, start, end, dim, false ); if ( list === list.next ) list.steiner = true; queue.push( getLeftmost( list ) ); } queue.sort( compareX ); // process holes from left to right for ( i = 0; i < queue.length; i ++ ) { eliminateHole( queue[ i ], outerNode ); outerNode = filterPoints( outerNode, outerNode.next ); } return outerNode; } function compareX( a, b ) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole( hole, outerNode ) { outerNode = findHoleBridge( hole, outerNode ); if ( outerNode ) { var b = splitPolygon( outerNode, hole ); filterPoints( b, b.next ); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge( hole, outerNode ) { var p = outerNode, hx = hole.x, hy = hole.y, qx = - Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); if ( x <= hx && x > qx ) { qx = x; if ( x === hx ) { if ( hy === p.y ) return p; if ( hy === p.next.y ) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while ( p !== outerNode ); if ( ! m ) return null; if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m.next; while ( p !== stop ) { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) { m = p; tanMin = tan; } } p = p.next; } return m; } // interlink polygon nodes in z-order function indexCurve( start, minX, minY, invSize ) { var p = start; do { if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while ( p !== start ); p.prevZ.nextZ = null; p.prevZ = null; sortLinked( p ); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked( list ) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while ( p ) { numMerges ++; q = p; pSize = 0; for ( i = 0; i < inSize; i ++ ) { pSize ++; q = q.nextZ; if ( ! q ) break; } qSize = inSize; while ( pSize > 0 || ( qSize > 0 && q ) ) { if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { e = p; p = p.nextZ; pSize --; } else { e = q; q = q.nextZ; qSize --; } if ( tail ) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while ( numMerges > 1 ); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder( x, y, minX, minY, invSize ) { // coords are transformed into non-negative 15-bit integer range x = 32767 * ( x - minX ) * invSize; y = 32767 * ( y - minY ) * invSize; x = ( x | ( x << 8 ) ) & 0x00FF00FF; x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; x = ( x | ( x << 2 ) ) & 0x33333333; x = ( x | ( x << 1 ) ) & 0x55555555; y = ( y | ( y << 8 ) ) & 0x00FF00FF; y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; y = ( y | ( y << 2 ) ) & 0x33333333; y = ( y | ( y << 1 ) ) & 0x55555555; return x | ( y << 1 ); } // find the leftmost node of a polygon ring function getLeftmost( start ) { var p = start, leftmost = start; do { if ( p.x < leftmost.x ) leftmost = p; p = p.next; } while ( p !== start ); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal( a, b ) { return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ); } // signed area of a triangle function area( p, q, r ) { return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); } // check if two points are equal function equals( p1, p2 ) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects( p1, q1, p2, q2 ) { if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) || ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true; return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 && area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon( a, b ) { var p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects( p, p.next, a, b ) ) { return true; } p = p.next; } while ( p !== a ); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside( a, b ) { return area( a.prev, a, a.next ) < 0 ? area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside( a, b ) { var p = a, inside = false, px = ( a.x + b.x ) / 2, py = ( a.y + b.y ) / 2; do { if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) { inside = ! inside; } p = p.next; } while ( p !== a ); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon( a, b ) { var a2 = new Node( a.i, a.x, a.y ), b2 = new Node( b.i, b.x, b.y ), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode( i, x, y, last ) { var p = new Node( i, x, y ); if ( ! last ) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode( p ) { p.next.prev = p.prev; p.prev.next = p.next; if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; } function Node( i, x, y ) { // vertice index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertice nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea( data, start, end, dim ) { var sum = 0; for ( var i = start, j = end - dim; i < end; i += dim ) { sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); j = i; } return sum; } /** * @author zz85 / http://www.lab4games.net/zz85/blog */ var ShapeUtils = { // calculate area of the contour polygon area: function ( contour ) { var n = contour.length; var a = 0.0; for ( var p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; }, isClockWise: function ( pts ) { return ShapeUtils.area( pts ) < 0; }, triangulateShape: function ( contour, holes ) { function removeDupEndPts( points ) { var l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } function addContour( vertices, contour ) { for ( var i = 0; i < contour.length; i ++ ) { vertices.push( contour[ i ].x ); vertices.push( contour[ i ].y ); } } var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] var holeIndices = []; // array of hole indices var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts( contour ); addContour( vertices, contour ); // var holeIndex = contour.length; holes.forEach( removeDupEndPts ); for ( i = 0; i < holes.length; i ++ ) { holeIndices.push( holeIndex ); holeIndex += holes[ i ].length; addContour( vertices, holes[ i ] ); } // var triangles = Earcut.triangulate( vertices, holeIndices ); // for ( var i = 0; i < triangles.length; i += 3 ) { faces.push( triangles.slice( i, i + 3 ) ); } return faces; } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * amount: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline is bevel * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * frames: // containing arrays of tangents, normals, binormals * * UVGenerator: // object that provides UV generator functions * * } */ // ExtrudeGeometry function ExtrudeGeometry( shapes, options ) { Geometry.call( this ); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); this.mergeVertices(); } ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; // ExtrudeBufferGeometry function ExtrudeBufferGeometry( shapes, options ) { if ( typeof ( shapes ) === "undefined" ) { return; } BufferGeometry.call( this ); this.type = "ExtrudeBufferGeometry"; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; this.addShapeList( shapes, options ); this.computeVertexNormals(); // can"t really use automatic vertex normals // as then front and back sides get smoothed too // should do separate smoothing just for sides //this.computeVertexNormals(); //console.log( "took", ( Date.now() - startTime ) ); } ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; ExtrudeBufferGeometry.prototype.getArrays = function () { var positionAttribute = this.getAttribute( "position" ); var verticesArray = positionAttribute ? Array.prototype.slice.call( positionAttribute.array ) : []; var uvAttribute = this.getAttribute( "uv" ); var uvArray = uvAttribute ? Array.prototype.slice.call( uvAttribute.array ) : []; var IndexAttribute = this.index; var indicesArray = IndexAttribute ? Array.prototype.slice.call( IndexAttribute.array ) : []; return { position: verticesArray, uv: uvArray, index: indicesArray }; }; ExtrudeBufferGeometry.prototype.addShapeList = function ( shapes, options ) { var sl = shapes.length; options.arrays = this.getArrays(); for ( var s = 0; s < sl; s ++ ) { var shape = shapes[ s ]; this.addShape( shape, options ); } this.setIndex( options.arrays.index ); this.addAttribute( "position", new Float32BufferAttribute( options.arrays.position, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( options.arrays.uv, 2 ) ); }; ExtrudeBufferGeometry.prototype.addShape = function ( shape, options ) { var arrays = options.arrays ? options.arrays : this.getArrays(); var verticesArray = arrays.position; var indicesArray = arrays.index; var uvArray = arrays.uv; var placeholder = []; var amount = options.amount !== undefined ? options.amount : 100; var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; var steps = options.steps !== undefined ? options.steps : 1; var extrudePath = options.extrudePath; var extrudePts, extrudeByPath = false; // Use default WorldUVGenerator if no UV generators are specified. var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; var splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initialization var ahole, h, hl; // looping of holes var scope = this; var shapePoints = shape.extractPoints( curveSegments ); var vertices = shapePoints.shape; var holes = shapePoints.holes; var reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } } var faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return vec.clone().multiplyScalar( size ).add( pt ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing var v_prev_len = Math.sqrt( v_prev_lensq ); var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges var direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } var contourMovements = []; for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = []; for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= steps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, amount / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, amount + z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, amount + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { var start = verticesArray.length / 3; if ( bevelEnabled ) { var layer = 0; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } scope.addGroup( start, verticesArray.length / 3 - start, options.material !== undefined ? options.material : 0 ); } // Create faces for the z-sides of the shape function buildSideFaces() { var start = verticesArray.length / 3; var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } scope.addGroup( start, verticesArray.length / 3 - start, options.extrudeMaterial !== undefined ? options.extrudeMaterial : 1 ); } function sidewalls( contour, layeroffset ) { var j, k; i = contour.length; while ( -- i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); var s = 0, sl = steps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d ); } } } function v( x, y, z ) { placeholder.push( x ); placeholder.push( y ); placeholder.push( z ); } function f3( a, b, c ) { addVertex( a ); addVertex( b ); addVertex( c ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); } function f4( a, b, c, d ) { addVertex( a ); addVertex( b ); addVertex( d ); addVertex( b ); addVertex( c ); addVertex( d ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 3 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); addUV( uvs[ 3 ] ); } function addVertex( index ) { indicesArray.push( verticesArray.length / 3 ); verticesArray.push( placeholder[ index * 3 + 0 ] ); verticesArray.push( placeholder[ index * 3 + 1 ] ); verticesArray.push( placeholder[ index * 3 + 2 ] ); } function addUV( vector2 ) { uvArray.push( vector2.x ); uvArray.push( vector2.y ); } if ( ! options.arrays ) { this.setIndex( indicesArray ); this.addAttribute( "position", new Float32BufferAttribute( verticesArray, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvArray, 2 ) ); } }; ExtrudeGeometry.WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; return [ new Vector2( a_x, a_y ), new Vector2( b_x, b_y ), new Vector2( c_x, c_y ) ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var a_z = vertices[ indexA * 3 + 2 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var b_z = vertices[ indexB * 3 + 2 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; var c_z = vertices[ indexC * 3 + 2 ]; var d_x = vertices[ indexD * 3 ]; var d_y = vertices[ indexD * 3 + 1 ]; var d_z = vertices[ indexD * 3 + 2 ]; if ( Math.abs( a_y - b_y ) < 0.01 ) { return [ new Vector2( a_x, 1 - a_z ), new Vector2( b_x, 1 - b_z ), new Vector2( c_x, 1 - c_z ), new Vector2( d_x, 1 - d_z ) ]; } else { return [ new Vector2( a_y, 1 - a_z ), new Vector2( b_y, 1 - b_z ), new Vector2( c_y, 1 - c_z ), new Vector2( d_y, 1 - d_z ) ]; } } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ * * Text = 3D Text * * parameters = { * font: , // font * * size: , // size of the text * height: , // thickness to extrude text * curveSegments: , // number of points on the curves * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into text bevel goes * bevelSize: // how far from text outline is bevel * } */ // TextGeometry function TextGeometry( text, parameters ) { Geometry.call( this ); this.type = "TextGeometry"; this.parameters = { text: text, parameters: parameters }; this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); this.mergeVertices(); } TextGeometry.prototype = Object.create( Geometry.prototype ); TextGeometry.prototype.constructor = TextGeometry; // TextBufferGeometry function TextBufferGeometry( text, parameters ) { parameters = parameters || {}; var font = parameters.font; if ( ! ( font && font.isFont ) ) { console.error( "THREE.TextGeometry: font parameter is not an instance of THREE.Font." ); return new Geometry(); } var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); // translate parameters to ExtrudeGeometry API parameters.amount = parameters.height !== undefined ? parameters.height : 50; // defaults if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; ExtrudeBufferGeometry.call( this, shapes, parameters ); this.type = "TextBufferGeometry"; } TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); TextBufferGeometry.prototype.constructor = TextBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 */ // SphereGeometry function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); this.mergeVertices(); } SphereGeometry.prototype = Object.create( Geometry.prototype ); SphereGeometry.prototype.constructor = SphereGeometry; // SphereBufferGeometry function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "SphereBufferGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); phiStart = phiStart !== undefined ? phiStart : 0; phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; var thetaEnd = thetaStart + thetaLength; var ix, iy; var index = 0; var grid = []; var vertex = new Vector3(); var normal = new Vector3(); // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy <= heightSegments; iy ++ ) { var verticesRow = []; var v = iy / heightSegments; for ( ix = 0; ix <= widthSegments; ix ++ ) { var u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( vertex.x, vertex.y, vertex.z ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( iy = 0; iy < heightSegments; iy ++ ) { for ( ix = 0; ix < widthSegments; ix ++ ) { var a = grid[ iy ][ ix + 1 ]; var b = grid[ iy ][ ix ]; var c = grid[ iy + 1 ][ ix ]; var d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; /** * @author Kaleb Murphy * @author Mugen87 / https://github.com/Mugen87 */ // RingGeometry function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); this.mergeVertices(); } RingGeometry.prototype = Object.create( Geometry.prototype ); RingGeometry.prototype.constructor = RingGeometry; // RingBufferGeometry function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "RingBufferGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; innerRadius = innerRadius || 0.5; outerRadius = outerRadius || 1; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // some helper variables var segment; var radius = innerRadius; var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); var vertex = new Vector3(); var uv = new Vector2(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= phiSegments; j ++ ) { for ( i = 0; i <= thetaSegments; i ++ ) { // values are generate from the inside of the ring to the outside segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // increase the radius for next row of vertices radius += radiusStep; } // indices for ( j = 0; j < phiSegments; j ++ ) { var thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( i = 0; i < thetaSegments; i ++ ) { segment = i + thetaSegmentLevel; var a = segment; var b = segment + thetaSegments + 1; var c = segment + thetaSegments + 2; var d = segment + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); RingBufferGeometry.prototype.constructor = RingBufferGeometry; /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 * @author bhouston / http://clara.io * @author Mugen87 / https://github.com/Mugen87 */ // LatheGeometry function LatheGeometry( points, segments, phiStart, phiLength ) { Geometry.call( this ); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); this.mergeVertices(); } LatheGeometry.prototype = Object.create( Geometry.prototype ); LatheGeometry.prototype.constructor = LatheGeometry; // LatheBufferGeometry function LatheBufferGeometry( points, segments, phiStart, phiLength ) { BufferGeometry.call( this ); this.type = "LatheBufferGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ) || 12; phiStart = phiStart || 0; phiLength = phiLength || Math.PI * 2; // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); // buffers var indices = []; var vertices = []; var uvs = []; // helper variables var base; var inverseSegments = 1.0 / segments; var vertex = new Vector3(); var uv = new Vector2(); var i, j; // generate vertices and uvs for ( i = 0; i <= segments; i ++ ) { var phi = phiStart + i * inverseSegments * phiLength; var sin = Math.sin( phi ); var cos = Math.cos( phi ); for ( j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); } } // indices for ( i = 0; i < segments; i ++ ) { for ( j = 0; j < ( points.length - 1 ); j ++ ) { base = j + i * points.length; var a = base; var b = base + points.length; var c = base + points.length + 1; var d = base + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // generate normals this.computeVertexNormals(); // if the geometry is closed, we need to average the normals along the seam. // because the corresponding vertices are identical (but still have different UVs). if ( phiLength === Math.PI * 2 ) { var normals = this.attributes.normal.array; var n1 = new Vector3(); var n2 = new Vector3(); var n = new Vector3(); // this is the buffer offset for the last line of vertices base = segments * points.length * 3; for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { // select the normal of the vertex in the first line n1.x = normals[ j + 0 ]; n1.y = normals[ j + 1 ]; n1.z = normals[ j + 2 ]; // select the normal of the vertex in the last line n2.x = normals[ base + j + 0 ]; n2.y = normals[ base + j + 1 ]; n2.z = normals[ base + j + 2 ]; // average normals n.addVectors( n1, n2 ).normalize(); // assign the new values to both normals normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; } } } LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; /** * @author jonobr1 / http://jonobr1.com * @author Mugen87 / https://github.com/Mugen87 */ // ShapeGeometry function ShapeGeometry( shapes, curveSegments ) { Geometry.call( this ); this.type = "ShapeGeometry"; if ( typeof curveSegments === "object" ) { console.warn( "THREE.ShapeGeometry: Options parameter has been removed." ); curveSegments = curveSegments.curveSegments; } this.parameters = { shapes: shapes, curveSegments: curveSegments }; this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); this.mergeVertices(); } ShapeGeometry.prototype = Object.create( Geometry.prototype ); ShapeGeometry.prototype.constructor = ShapeGeometry; ShapeGeometry.prototype.toJSON = function () { var data = Geometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON( shapes, data ); }; // ShapeBufferGeometry function ShapeBufferGeometry( shapes, curveSegments ) { BufferGeometry.call( this ); this.type = "ShapeBufferGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; curveSegments = curveSegments || 12; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var groupStart = 0; var groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( var i = 0; i < shapes.length; i ++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { var i, l, shapeHole; var indexOffset = vertices.length / 3; var points = shape.extractPoints( curveSegments ); var shapeVertices = points.shape; var shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); // also check if holes are in the opposite direction for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } } var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { var vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // incides for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var a = face[ 0 ] + indexOffset; var b = face[ 1 ] + indexOffset; var c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; ShapeBufferGeometry.prototype.toJSON = function () { var data = BufferGeometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON( shapes, data ); }; // function toJSON( shapes, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } return data; } /** * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ function EdgesGeometry( geometry, thresholdAngle ) { BufferGeometry.call( this ); this.type = "EdgesGeometry"; this.parameters = { thresholdAngle: thresholdAngle }; thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; // buffer var vertices = []; // helper variables var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); var edge = [ 0, 0 ], edges = {}, edge1, edge2; var key, keys = [ "a", "b", "c" ]; // prepare source geometry var geometry2; if ( geometry.isBufferGeometry ) { geometry2 = new Geometry(); geometry2.fromBufferGeometry( geometry ); } else { geometry2 = geometry.clone(); } geometry2.mergeVertices(); geometry2.computeFaceNormals(); var sourceVertices = geometry2.vertices; var faces = geometry2.faces; // now create a data structure where each entry represents an edge with its adjoining faces for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; } else { edges[ key ].face2 = i; } } } // generate vertices for ( key in edges ) { var e = edges[ key ]; // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { var vertex = sourceVertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = sourceVertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); EdgesGeometry.prototype.constructor = EdgesGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // CylinderGeometry function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); this.mergeVertices(); } CylinderGeometry.prototype = Object.create( Geometry.prototype ); CylinderGeometry.prototype.constructor = CylinderGeometry; // CylinderBufferGeometry function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CylinderBufferGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; var scope = this; radiusTop = radiusTop !== undefined ? radiusTop : 1; radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; height = height || 1; radialSegments = Math.floor( radialSegments ) || 8; heightSegments = Math.floor( heightSegments ) || 1; openEnded = openEnded !== undefined ? openEnded : false; thetaStart = thetaStart !== undefined ? thetaStart : 0.0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var index = 0; var indexArray = []; var halfHeight = height / 2; var groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function generateTorso() { var x, y; var normal = new Vector3(); var vertex = new Vector3(); var groupCount = 0; // this will be used to calculate the normal var slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( y = 0; y <= heightSegments; y ++ ) { var indexRow = []; var v = y / heightSegments; // calculate the radius of the current row var radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var sinTheta = Math.sin( theta ); var cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( x = 0; x < radialSegments; x ++ ) { for ( y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices var a = indexArray[ y ][ x ]; var b = indexArray[ y + 1 ][ x ]; var c = indexArray[ y + 1 ][ x + 1 ]; var d = indexArray[ y ][ x + 1 ]; // faces indices.push( a, b, d ); indices.push( b, c, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { var x, centerIndexStart, centerIndexEnd; var uv = new Vector2(); var vertex = new Vector3(); var groupCount = 0; var radius = ( top === true ) ? radiusTop : radiusBottom; var sign = ( top === true ) ? 1 : - 1; // save the index of the first center vertex centerIndexStart = index; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, halfHeight * sign, 0 ); // normal normals.push( 0, sign, 0 ); // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var cosTheta = Math.cos( theta ); var sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( x = 0; x < radialSegments; x ++ ) { var c = centerIndexStart + x; var i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; /** * @author abelnation / http://github.com/abelnation */ // ConeGeometry function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); ConeGeometry.prototype.constructor = ConeGeometry; // ConeBufferGeometry function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeBufferGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; /** * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 * @author hughes */ // CircleGeometry function CircleGeometry( radius, segments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); this.mergeVertices(); } CircleGeometry.prototype = Object.create( Geometry.prototype ); CircleGeometry.prototype.constructor = CircleGeometry; // CircleBufferGeometry function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CircleBufferGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; segments = segments !== undefined ? Math.max( 3, segments ) : 8; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, s; var vertex = new Vector3(); var uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { var segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; var Geometries = Object.freeze({ WireframeGeometry: WireframeGeometry, ParametricGeometry: ParametricGeometry, ParametricBufferGeometry: ParametricBufferGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronBufferGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronBufferGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronBufferGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronBufferGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronBufferGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeBufferGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotBufferGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusBufferGeometry, TextGeometry: TextGeometry, TextBufferGeometry: TextBufferGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereBufferGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingBufferGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneBufferGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheBufferGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeBufferGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeBufferGeometry, EdgesGeometry: EdgesGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeBufferGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderBufferGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleBufferGeometry, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxBufferGeometry }); /** * @author mrdoob / http://mrdoob.com/ * * parameters = { * color: , * opacity: * } */ function ShadowMaterial( parameters ) { Material.call( this ); this.type = "ShadowMaterial"; this.color = new Color( 0x000000 ); this.opacity = 1.0; this.lights = true; this.transparent = true; this.setValues( parameters ); } ShadowMaterial.prototype = Object.create( Material.prototype ); ShadowMaterial.prototype.constructor = ShadowMaterial; ShadowMaterial.prototype.isShadowMaterial = true; /** * @author mrdoob / http://mrdoob.com/ */ function RawShaderMaterial( parameters ) { ShaderMaterial.call( this, parameters ); this.type = "RawShaderMaterial"; } RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); RawShaderMaterial.prototype.constructor = RawShaderMaterial; RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshStandardMaterial( parameters ) { Material.call( this ); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color( 0xffffff ); // diffuse this.roughness = 0.5; this.metalness = 0.5; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshStandardMaterial.prototype = Object.create( Material.prototype ); MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; MeshStandardMaterial.prototype.isMeshStandardMaterial = true; MeshStandardMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.defines = { "STANDARD": "" }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * reflectivity: * } */ function MeshPhysicalMaterial( parameters ) { MeshStandardMaterial.call( this ); this.defines = { "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.reflectivity = 0.5; // maps to F0 = 0.04 this.clearCoat = 0.0; this.clearCoatRoughness = 0.0; this.setValues( parameters ); } MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; MeshPhysicalMaterial.prototype.copy = function ( source ) { MeshStandardMaterial.prototype.copy.call( this, source ); this.defines = { "PHYSICAL": "" }; this.reflectivity = source.reflectivity; this.clearCoat = source.clearCoat; this.clearCoatRoughness = source.clearCoatRoughness; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshPhongMaterial( parameters ) { Material.call( this ); this.type = "MeshPhongMaterial"; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshPhongMaterial.prototype = Object.create( Material.prototype ); MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; MeshPhongMaterial.prototype.isMeshPhongMaterial = true; MeshPhongMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author takahirox / http://github.com/takahirox * * parameters = { * gradientMap: new THREE.Texture( ) * } */ function MeshToonMaterial( parameters ) { MeshPhongMaterial.call( this ); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.gradientMap = null; this.setValues( parameters ); } MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); MeshToonMaterial.prototype.constructor = MeshToonMaterial; MeshToonMaterial.prototype.isMeshToonMaterial = true; MeshToonMaterial.prototype.copy = function ( source ) { MeshPhongMaterial.prototype.copy.call( this, source ); this.gradientMap = source.gradientMap; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshNormalMaterial( parameters ) { Material.call( this ); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshNormalMaterial.prototype = Object.create( Material.prototype ); MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; MeshNormalMaterial.prototype.isMeshNormalMaterial = true; MeshNormalMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshLambertMaterial( parameters ) { Material.call( this ); this.type = "MeshLambertMaterial"; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshLambertMaterial.prototype = Object.create( Material.prototype ); MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; MeshLambertMaterial.prototype.isMeshLambertMaterial = true; MeshLambertMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ function LineDashedMaterial( parameters ) { LineBasicMaterial.call( this ); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues( parameters ); } LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); LineDashedMaterial.prototype.constructor = LineDashedMaterial; LineDashedMaterial.prototype.isLineDashedMaterial = true; LineDashedMaterial.prototype.copy = function ( source ) { LineBasicMaterial.prototype.copy.call( this, source ); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; }; var Materials = Object.freeze({ ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); /** * @author mrdoob / http://mrdoob.com/ */ var Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; /** * @author mrdoob / http://mrdoob.com/ */ function LoadingManager( onLoad, onProgress, onError ) { var scope = this; var isLoading = false; var itemsLoaded = 0; var itemsTotal = 0; var urlModifier = undefined; this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; this.resolveURL = function ( url ) { if ( urlModifier ) { return urlModifier( url ); } return url; }; this.setURLModifier = function ( transform ) { urlModifier = transform; return this; }; } var DefaultLoadingManager = new LoadingManager(); /** * @author mrdoob / http://mrdoob.com/ */ var loading = {}; function FileLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FileLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } // Check if request is duplicate if ( loading[ url ] !== undefined ) { loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); return; } // Check for data: URI var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; var dataUriRegexResult = url.match( dataUriRegex ); // Safari can not handle Data URIs through XMLHttpRequest so process manually if ( dataUriRegexResult ) { var mimeType = dataUriRegexResult[ 1 ]; var isBase64 = !! dataUriRegexResult[ 2 ]; var data = dataUriRegexResult[ 3 ]; data = window.decodeURIComponent( data ); if ( isBase64 ) data = window.atob( data ); try { var response; var responseType = ( this.responseType || "" ).toLowerCase(); switch ( responseType ) { case "arraybuffer": case "blob": var view = new Uint8Array( data.length ); for ( var i = 0; i < data.length; i ++ ) { view[ i ] = data.charCodeAt( i ); } if ( responseType === "blob" ) { response = new Blob( [ view.buffer ], { type: mimeType } ); } else { response = view.buffer; } break; case "document": var parser = new DOMParser(); response = parser.parseFromString( data, mimeType ); break; case "json": response = JSON.parse( data ); break; default: // "text" or other response = data; break; } // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); }, 0 ); } catch ( error ) { // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onError ) onError( error ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, 0 ); } } else { // Initialise array for duplicate requests loading[ url ] = []; loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); var request = new XMLHttpRequest(); request.open( "GET", url, true ); request.addEventListener( "load", function ( event ) { var response = this.response; Cache.add( url, response ); var callbacks = loading[ url ]; delete loading[ url ]; if ( this.status === 200 ) { for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( response ); } scope.manager.itemEnd( url ); } else if ( this.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. console.warn( "THREE.FileLoader: HTTP Status 0 received." ); for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( response ); } scope.manager.itemEnd( url ); } else { for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); } }, false ); request.addEventListener( "progress", function ( event ) { var callbacks = loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onProgress ) callback.onProgress( event ); } }, false ); request.addEventListener( "error", function ( event ) { var callbacks = loading[ url ]; delete loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, false ); if ( this.responseType !== undefined ) request.responseType = this.responseType; if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : "text/plain" ); for ( var header in this.requestHeader ) { request.setRequestHeader( header, this.requestHeader[ header ] ); } request.send( null ); } scope.manager.itemStart( url ); return request; }, setPath: function ( value ) { this.path = value; return this; }, setResponseType: function ( value ) { this.responseType = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setMimeType: function ( value ) { this.mimeType = value; return this; }, setRequestHeader: function ( value ) { this.requestHeader = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * * Abstract Base class to block based textures loader (dds, pvr, ...) */ function CompressedTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( CompressedTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var images = []; var texture = new CompressedTexture(); texture.image = images; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( "arraybuffer" ); function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { var texDatas = scope._parser( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { var loaded = 0; for ( var i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { var texDatas = scope._parser( buffer, true ); if ( texDatas.isCubemap ) { var faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( var f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps: [] }; for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author Nikos M. / https://github.com/foo123/ * * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ function DataTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( DataTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texture = new DataTexture(); var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var texData = scope._parser( buffer ); if ( ! texData ) return; if ( undefined !== texData.image ) { texture.image = texData.image; } else if ( undefined !== texData.data ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; if ( undefined !== texData.format ) { texture.format = texData.format; } if ( undefined !== texData.type ) { texture.type = texData.type; } if ( undefined !== texData.mipmaps ) { texture.mipmaps = texData.mipmaps; } if ( 1 === texData.mipmapCount ) { texture.minFilter = LinearFilter; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ImageLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( ImageLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } var image = document.createElementNS( "http://www.w3.org/1999/xhtml", "img" ); image.addEventListener( "load", function () { Cache.add( url, this ); if ( onLoad ) onLoad( this ); scope.manager.itemEnd( url ); }, false ); /* image.addEventListener( "progress", function ( event ) { if ( onProgress ) onProgress( event ); }, false ); */ image.addEventListener( "error", function ( event ) { if ( onError ) onError( event ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, false ); if ( url.substr( 0, 5 ) !== "data:" ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart( url ); image.src = url; return image; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function CubeTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( CubeTextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( urls, onLoad, onProgress, onError ) { var texture = new CubeTexture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); var loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( var i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function TextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( TextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { var texture = new Texture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); loader.load( url, function ( image ) { texture.image = image; // JPEGs can"t have an alpha channel, so memory can be saved by storing them as RGB. var isJPEG = url.search( /.(jpg|jpeg)$/ ) > 0 || url.search( /^data:image/jpeg/ ) === 0; texture.format = isJPEG ? RGBFormat : RGBAFormat; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Extensible curve object * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t ) * .getPointAt( u, optionalTarget ), .getTangentAt( u ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ /************************************************************** * Abstract Curve base class **************************************************************/ function Curve() { this.type = "Curve"; this.arcLengthDivisions = 200; } Object.assign( Curve.prototype, { // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint: function ( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; }, // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt: function ( u, optionalTarget ) { var t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); }, // Get sequence of points using getPoint( t ) getPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; }, // Get sequence of points using getPointAt( u ) getSpacedPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; }, // Get total curve arc length getLength: function () { var lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; }, // Get list of cumulative segment lengths getLengths: function ( divisions ) { if ( divisions === undefined ) divisions = this.arcLengthDivisions; if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; var cache = []; var current, last = this.getPoint( 0 ); var p, sum = 0; cache.push( 0 ); for ( p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. }, updateArcLengths: function () { this.needsUpdate = true; this.getLengths(); }, // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping: function ( u, distance ) { var arcLengths = this.getLengths(); var i = 0, il = arcLengths.length; var targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance var low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points var lengthBefore = arcLengths[ i ]; var lengthAfter = arcLengths[ i + 1 ]; var segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t var t = ( i + segmentFraction ) / ( il - 1 ); return t; }, // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent: function ( t ) { var delta = 0.0001; var t1 = t - delta; var t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; var pt1 = this.getPoint( t1 ); var pt2 = this.getPoint( t2 ); var vec = pt2.clone().sub( pt1 ); return vec.normalize(); }, getTangentAt: function ( u ) { var t = this.getUtoTmapping( u ); return this.getTangent( t ); }, computeFrenetFrames: function ( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf var normal = new Vector3(); var tangents = []; var normals = []; var binormals = []; var vec = new Vector3(); var mat = new Matrix4(); var i, u, theta; // compute the tangent vectors for each segment on the curve for ( i = 0; i <= segments; i ++ ) { u = i / segments; tangents[ i ] = this.getTangentAt( u ); tangents[ i ].normalize(); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); var min = Number.MAX_VALUE; var tx = Math.abs( tangents[ 0 ].x ); var ty = Math.abs( tangents[ 0 ].y ); var tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; }, fromJSON: function ( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } ); function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { Curve.call( this ); this.type = "EllipseCurve"; this.aX = aX || 0; this.aY = aY || 0; this.xRadius = xRadius || 1; this.yRadius = yRadius || 1; this.aStartAngle = aStartAngle || 0; this.aEndAngle = aEndAngle || 2 * Math.PI; this.aClockwise = aClockwise || false; this.aRotation = aRotation || 0; } EllipseCurve.prototype = Object.create( Curve.prototype ); EllipseCurve.prototype.constructor = EllipseCurve; EllipseCurve.prototype.isEllipseCurve = true; EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var twoPi = Math.PI * 2; var deltaAngle = this.aEndAngle - this.aStartAngle; var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } var angle = this.aStartAngle + t * deltaAngle; var x = this.aX + this.xRadius * Math.cos( angle ); var y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { var cos = Math.cos( this.aRotation ); var sin = Math.sin( this.aRotation ); var tx = x - this.aX; var ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set( x, y ); }; EllipseCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; }; EllipseCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; }; EllipseCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; }; function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); this.type = "ArcCurve"; } ArcCurve.prototype = Object.create( EllipseCurve.prototype ); ArcCurve.prototype.constructor = ArcCurve; ArcCurve.prototype.isArcCurve = true; /** * @author zz85 https://github.com/zz85 * * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { var c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { var t2 = t * t; var t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // var tmp = new Vector3(); var px = new CubicPoly(); var py = new CubicPoly(); var pz = new CubicPoly(); function CatmullRomCurve3( points, closed, curveType, tension ) { Curve.call( this ); this.type = "CatmullRomCurve3"; this.points = points || []; this.closed = closed || false; this.curveType = curveType || "centripetal"; this.tension = tension || 0.5; } CatmullRomCurve3.prototype = Object.create( Curve.prototype ); CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var points = this.points; var l = points.length; var p = ( l - ( this.closed ? 0 : 1 ) ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } var p0, p1, p2, p3; // 4 points if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } p1 = points[ intPoint % l ]; p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom var pow = this.curveType === "chordal" ? 0.5 : 0.25; var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; }; CatmullRomCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; }; CatmullRomCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; }; CatmullRomCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Bezier Curves formulas obtained from * http://en.wikipedia.org/wiki/Bézier_curve */ function CatmullRom( t, p0, p1, p2, p3 ) { var v0 = ( p2 - p0 ) * 0.5; var v1 = ( p3 - p1 ) * 0.5; var t2 = t * t; var t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } // function QuadraticBezierP0( t, p ) { var k = 1 - t; return k * k * p; } function QuadraticBezierP1( t, p ) { return 2 * ( 1 - t ) * t * p; } function QuadraticBezierP2( t, p ) { return t * t * p; } function QuadraticBezier( t, p0, p1, p2 ) { return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + QuadraticBezierP2( t, p2 ); } // function CubicBezierP0( t, p ) { var k = 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { var k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + CubicBezierP3( t, p3 ); } function CubicBezierCurve( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); this.v3 = v3 || new Vector2(); } CubicBezierCurve.prototype = Object.create( Curve.prototype ); CubicBezierCurve.prototype.constructor = CubicBezierCurve; CubicBezierCurve.prototype.isCubicBezierCurve = true; CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; }; CubicBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function CubicBezierCurve3( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); this.v3 = v3 || new Vector3(); } CubicBezierCurve3.prototype = Object.create( Curve.prototype ); CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; }; CubicBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function LineCurve( v1, v2 ) { Curve.call( this ); this.type = "LineCurve"; this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } LineCurve.prototype = Object.create( Curve.prototype ); LineCurve.prototype.constructor = LineCurve; LineCurve.prototype.isLineCurve = true; LineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve.prototype.getTangent = function ( /* t */ ) { var tangent = this.v2.clone().sub( this.v1 ); return tangent.normalize(); }; LineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function LineCurve3( v1, v2 ) { Curve.call( this ); this.type = "LineCurve3"; this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } LineCurve3.prototype = Object.create( Curve.prototype ); LineCurve3.prototype.constructor = LineCurve3; LineCurve3.prototype.isLineCurve3 = true; LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ) ); return point; }; QuadraticBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve3( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ), QuadraticBezier( t, v0.z, v1.z, v2.z ) ); return point; }; QuadraticBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function SplineCurve( points /* array of Vector2 */ ) { Curve.call( this ); this.type = "SplineCurve"; this.points = points || []; } SplineCurve.prototype = Object.create( Curve.prototype ); SplineCurve.prototype.constructor = SplineCurve; SplineCurve.prototype.isSplineCurve = true; SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var points = this.points; var p = ( points.length - 1 ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; var p1 = points[ intPoint ]; var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; point.set( CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) ); return point; }; SplineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } return this; }; SplineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } return data; }; SplineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector2().fromArray( point ) ); } return this; }; var Curves = Object.freeze({ ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /** * @author zz85 / http://www.lab4games.net/zz85/blog * **/ /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ function CurvePath() { Curve.call( this ); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { constructor: CurvePath, add: function ( curve ) { this.curves.push( curve ); }, closePath: function () { // Add a line curve if start and end of lines are not connected var startPoint = this.curves[ 0 ].getPoint( 0 ); var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } }, // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint: function ( t ) { var d = t * this.getLength(); var curveLengths = this.getCurveLengths(); var i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { var diff = curveLengths[ i ] - d; var curve = this.curves[ i ]; var segmentLength = curve.getLength(); var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; }, copy: function ( source ) { Curve.prototype.copy.call( this, source ); this.curves = []; for ( var i = 0, l = source.curves.length; i < l; i ++ ) { var curve = source.curves[ i ]; this.curves.push( curve.clone() ); } this.autoClose = source.autoClose; return this; }, toJSON: function () { var data = Curve.prototype.toJSON.call( this ); data.autoClose = this.autoClose; data.curves = []; for ( var i = 0, l = this.curves.length; i < l; i ++ ) { var curve = this.curves[ i ]; data.curves.push( curve.toJSON() ); } return data; }, fromJSON: function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.autoClose = json.autoClose; this.curves = []; for ( var i = 0, l = json.curves.length; i < l; i ++ ) { var curve = json.curves[ i ]; this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Creates free form 2d path using series of points, lines or curves. **/ function Path( points ) { CurvePath.call( this ); this.type = "Path"; this.currentPoint = new Vector2(); if ( points ) { this.setFromPoints( points ); } } Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { constructor: Path, setFromPoints: function ( points ) { this.moveTo( points[ 0 ].x, points[ 0 ].y ); for ( var i = 1, l = points.length; i < l; i ++ ) { this.lineTo( points[ i ].x, points[ i ].y ); } }, moveTo: function ( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? }, lineTo: function ( x, y ) { var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { var curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { var curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, splineThru: function ( pts /*Array of Vector*/ ) { var npts = [ this.currentPoint.clone() ].concat( pts ); var curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); }, arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); }, absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }, ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); }, absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join var firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); var lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); }, copy: function ( source ) { CurvePath.prototype.copy.call( this, source ); this.currentPoint.copy( source.currentPoint ); return this; }, toJSON: function () { var data = CurvePath.prototype.toJSON.call( this ); data.currentPoint = this.currentPoint.toArray(); return data; }, fromJSON: function ( json ) { CurvePath.prototype.fromJSON.call( this, json ); this.currentPoint.fromArray( json.currentPoint ); return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Defines a 2d shape plane using paths. **/ // STEP 1 Create a path. // STEP 2 Turn path into shape. // STEP 3 ExtrudeGeometry takes in Shape/Shapes // STEP 3a - Extract points from each shape, turn to vertices // STEP 3b - Triangulate each shape, add faces. function Shape( points ) { Path.call( this, points ); this.uuid = _Math.generateUUID(); this.type = "Shape"; this.holes = []; } Shape.prototype = Object.assign( Object.create( Path.prototype ), { constructor: Shape, getPointsHoles: function ( divisions ) { var holesPts = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; }, // get points of shape and holes (keypoints based on segments parameter) extractPoints: function ( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; }, copy: function ( source ) { Path.prototype.copy.call( this, source ); this.holes = []; for ( var i = 0, l = source.holes.length; i < l; i ++ ) { var hole = source.holes[ i ]; this.holes.push( hole.clone() ); } return this; }, toJSON: function () { var data = Path.prototype.toJSON.call( this ); data.uuid = this.uuid; data.holes = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { var hole = this.holes[ i ]; data.holes.push( hole.toJSON() ); } return data; }, fromJSON: function ( json ) { Path.prototype.fromJSON.call( this, json ); this.uuid = json.uuid; this.holes = []; for ( var i = 0, l = json.holes.length; i < l; i ++ ) { var hole = json.holes[ i ]; this.holes.push( new Path().fromJSON( hole ) ); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Light( color, intensity ) { Object3D.call( this ); this.type = "Light"; this.color = new Color( color ); this.intensity = intensity !== undefined ? intensity : 1; this.receiveShadow = undefined; } Light.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Light, isLight: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.color.copy( source.color ); this.intensity = source.intensity; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function HemisphereLight( skyColor, groundColor, intensity ) { Light.call( this, skyColor, intensity ); this.type = "HemisphereLight"; this.castShadow = undefined; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: HemisphereLight, isHemisphereLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.groundColor.copy( source.groundColor ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LightShadow( camera ) { this.camera = camera; this.bias = 0; this.radius = 1; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.matrix = new Matrix4(); } Object.assign( LightShadow.prototype, { copy: function ( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; }, clone: function () { return new this.constructor().copy( this ); }, toJSON: function () { var object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function SpotLightShadow() { LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); } SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: SpotLightShadow, isSpotLightShadow: true, update: function ( light ) { var camera = this.camera; var fov = _Math.RAD2DEG * 2 * light.angle; var aspect = this.mapSize.width / this.mapSize.height; var far = light.distance || camera.far; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function SpotLight( color, intensity, distance, angle, penumbra, decay ) { Light.call( this, color, intensity ); this.type = "SpotLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf return this.intensity * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf this.intensity = power / Math.PI; } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: SpotLight, isSpotLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PointLight( color, intensity, distance, decay ) { Light.call( this, color, intensity ); this.type = "PointLight"; Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf return this.intensity * 4 * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf this.intensity = power / ( 4 * Math.PI ); } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); } PointLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: PointLight, isPointLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function DirectionalLightShadow( ) { LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: DirectionalLightShadow } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function DirectionalLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "DirectionalLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: DirectionalLight, isDirectionalLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function AmbientLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "AmbientLight"; this.castShadow = undefined; } AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: AmbientLight, isAmbientLight: true } ); /** * @author abelnation / http://github.com/abelnation */ function RectAreaLight( color, intensity, width, height ) { Light.call( this, color, intensity ); this.type = "RectAreaLight"; this.position.set( 0, 1, 0 ); this.updateMatrix(); this.width = ( width !== undefined ) ? width : 10; this.height = ( height !== undefined ) ? height : 10; // TODO (abelnation): distance/decay // TODO (abelnation): update method for RectAreaLight to update transform to lookat target // TODO (abelnation): shadows } // TODO (abelnation): RectAreaLight update when light shape is changed RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: RectAreaLight, isRectAreaLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.width = source.width; this.height = source.height; return this; }, toJSON: function ( meta ) { var data = Light.prototype.toJSON.call( this, meta ); data.object.width = this.width; data.object.height = this.height; return data; } } ); /** * * A Track that interpolates Strings * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function StringKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: StringKeyframeTrack, ValueTypeName: "string", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined } ); /** * * A Track of Boolean keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function BooleanKeyframeTrack( name, times, values ) { KeyframeTrack.call( this, name, times, values ); } BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: BooleanKeyframeTrack, ValueTypeName: "bool", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined // Note: Actually this track could have a optimized / compressed // representation of a single value and a custom interpolant that // computes "firstValue ^ isOdd( index )". } ); /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * * @author tschw */ function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; } Object.assign( Interpolant.prototype, { evaluate: function ( t ) { var pp = this.parameterPositions, i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { var right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( var giveUpAt = i1 + 2; ; ) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t, t0 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? var t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( var giveUpAt = i1 - 2; ; ) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { var mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t0, t ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); }, settings: null, // optional, subclass-specific settings structure // Note: The indirection allows central control of many interpolants. // --- Protected interface DefaultSettings_: {}, getSettings_: function () { return this.settings || this.DefaultSettings_; }, copySampleValue_: function ( index ) { // copies a sample value to the result buffer var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; }, // Template methods for derived classes: interpolate_: function ( /* i1, t0, t, t1 */ ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer }, intervalChanged_: function ( /* i1, t0, t1 */ ) { // empty } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( Interpolant.prototype, { //( 0, t, t0 ), returns this.resultBuffer beforeStart_: Interpolant.prototype.copySampleValue_, //( N-1, tN-1, t ), returns this.resultBuffer afterEnd_: Interpolant.prototype.copySampleValue_, } ); /** * Spherical linear unit quaternion interpolant. * * @author tschw */ function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: QuaternionLinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = i1 * stride, alpha = ( t - t0 ) / ( t1 - t0 ); for ( var end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } ); /** * * A Track of quaternion keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function QuaternionKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: QuaternionKeyframeTrack, ValueTypeName: "quaternion", // ValueBufferType is inherited DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodLinear: function ( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: undefined // not yet implemented } ); /** * * A Track of keyframe values that represent color. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function ColorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: ColorKeyframeTrack, ValueTypeName: "color" // ValueBufferType is inherited // DefaultInterpolation is inherited // Note: Very basic implementation and nothing special yet. // However, this is the place for color space parameterization. } ); /** * * A Track of numeric keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function NumberKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: NumberKeyframeTrack, ValueTypeName: "number" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. * * @author tschw */ function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = - 0; this._offsetPrev = - 0; this._weightNext = - 0; this._offsetNext = - 0; } CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: CubicInterpolant, DefaultSettings_: { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }, intervalChanged_: function ( i1, t0, t1 ) { var pp = this.parameterPositions, iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } var halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; }, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials var sP = - wP * ppp + 2 * wP * pp - wP * p; var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; var sN = wN * ppp - wN * pp; // combine data linearly for ( var i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } ); /** * @author tschw */ function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: LinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } ); /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. * * @author tschw */ function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: DiscreteInterpolant, interpolate_: function ( i1 /*, t0, t, t1 */ ) { return this.copySampleValue_( i1 - 1 ); } } ); /** * @author tschw * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ var AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function ( array, from, to ) { if ( AnimationUtils.isTypedArray( array ) ) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } return array.slice( from, to ); }, // converts an array to a specific type convertArray: function ( array, type, forceClone ) { if ( ! array || // let "undefined" and "null" pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === "number" ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array }, isTypedArray: function ( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); }, // returns an array by which times and values can be sorted getKeyframeOrder: function ( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } var n = times.length; var result = new Array( n ); for ( var i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function ( values, stride, order ) { var nValues = values.length; var result = new values.constructor( nValues ); for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { var srcOffset = order[ i ] * stride; for ( var j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { var i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data var value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } }; /** * * A timed sequence of keyframes for a specific property. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function KeyframeTrack( name, times, values, interpolation ) { if ( name === undefined ) throw new Error( "THREE.KeyframeTrack: track name is undefined" ); if ( times === undefined || times.length === 0 ) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name ); this.name = name; this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); this.validate(); this.optimize(); } // Static methods: Object.assign( KeyframeTrack, { // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): parse: function ( json ) { if ( json.type === undefined ) { throw new Error( "THREE.KeyframeTrack: track type undefined, can not parse" ); } var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { var times = [], values = []; AnimationUtils.flattenJSON( json.keys, times, values, "value" ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } }, toJSON: function ( track ) { var trackType = track.constructor; var json; // derived classes can define a static toJSON method if ( trackType.toJSON !== undefined ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray( track.times, Array ), "values": AnimationUtils.convertArray( track.values, Array ) }; var interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; }, _getTrackTypeForValueTypeName: function ( typeName ) { switch ( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "THREE.KeyframeTrack: Unsupported typeName: " + typeName ); } } ); Object.assign( KeyframeTrack.prototype, { constructor: KeyframeTrack, TimeBufferType: Float32Array, ValueBufferType: Float32Array, DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodDiscrete: function ( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodLinear: function ( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: function ( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); }, setInterpolation: function ( interpolation ) { var factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { var message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( "THREE.KeyframeTrack:", message ); return; } this.createInterpolant = factoryMethod; }, getInterpolation: function () { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } }, getValueSize: function () { return this.values.length / this.times.length; }, // move all keyframes either forwards or backwards in time shift: function ( timeOffset ) { if ( timeOffset !== 0.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; }, // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale: function ( timeScale ) { if ( timeScale !== 1.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; }, // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim: function ( startTime, endTime ) { var times = this.times, nKeys = times.length, from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) { ++ from; } while ( to !== - 1 && times[ to ] > endTime ) { -- to; } ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; var stride = this.getValueSize(); this.times = AnimationUtils.arraySlice( times, from, to ); this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); } return this; }, // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate: function () { var valid = true; var valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "THREE.KeyframeTrack: Invalid value size in track.", this ); valid = false; } var times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "THREE.KeyframeTrack: Track is empty.", this ); valid = false; } var prevTime = null; for ( var i = 0; i !== nKeys; i ++ ) { var currTime = times[ i ]; if ( typeof currTime === "number" && isNaN( currTime ) ) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( AnimationUtils.isTypedArray( values ) ) { for ( var i = 0, n = values.length; i !== n; ++ i ) { var value = values[ i ]; if ( isNaN( value ) ) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value ); valid = false; break; } } } } return valid; }, // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize: function () { var times = this.times, values = this.values, stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, writeIndex = 1, lastIndex = times.length - 1; for ( var i = 1; i < lastIndex; ++ i ) { var keep = false; var time = times[ i ]; var timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors var offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( var j = 0; j !== stride; ++ j ) { var value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; var readOffset = i * stride, writeOffset = writeIndex * stride; for ( var j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); } return this; } } ); /** * * A Track of vectored keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function VectorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: VectorKeyframeTrack, ValueTypeName: "vector" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * * Reusable set of Tracks that represent an animation. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ function AnimationClip( name, duration, tracks ) { this.name = name; this.tracks = tracks; this.duration = ( duration !== undefined ) ? duration : - 1; this.uuid = _Math.generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } this.optimize(); } Object.assign( AnimationClip, { parse: function ( json ) { var tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); } return new AnimationClip( json.name, json.duration, tracks ); }, toJSON: function ( clip ) { var tracks = [], clipTracks = clip.tracks; var json = { "name": clip.name, "duration": clip.duration, "tracks": tracks }; for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; }, CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { var numMorphTargets = morphTargetSequence.length; var tracks = []; for ( var i = 0; i < numMorphTargets; i ++ ) { var times = []; var values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); var order = AnimationUtils.getKeyframeOrder( times ); times = AnimationUtils.sortedArray( times, 1, order ); values = AnimationUtils.sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[ i ].name + "]", times, values ).scale( 1.0 / fps ) ); } return new AnimationClip( name, - 1, tracks ); }, findByName: function ( objectOrClipArray, name ) { var clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { var o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( var i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; }, CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { var animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 var pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = morphTargets[ i ]; var parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { var name = parts[ 1 ]; var animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } var clips = []; for ( var name in animationToMorphTargets ) { clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; }, // parse the animation.hierarchy format parseAnimation: function ( animation, bones ) { if ( ! animation ) { console.error( "THREE.AnimationClip: No animation in JSONLoader data." ); return null; } var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { var times = []; var values = []; AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; var tracks = []; var clipName = animation.name || "default"; // automatic length determination in AnimationClip. var duration = animation.length || - 1; var fps = animation.fps || 30; var hierarchyTracks = animation.hierarchy || []; for ( var h = 0; h < hierarchyTracks.length; h ++ ) { var animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets if ( animationKeys[ 0 ].morphTargets ) { // figure out all morph targets used in this track var morphTargetNames = {}; for ( var k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[ k ].morphTargets ) { for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( var morphTargetName in morphTargetNames ) { var times = []; var values = []; for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { var animationKey = animationKeys[ k ]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values ) ); } duration = morphTargetNames.length * ( fps || 1.0 ); } else { // ...assume skeletal animation var boneName = ".bones[" + bones[ h ].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks ); } } if ( tracks.length === 0 ) { return null; } var clip = new AnimationClip( clipName, duration, tracks ); return clip; } } ); Object.assign( AnimationClip.prototype, { resetDuration: function () { var tracks = this.tracks, duration = 0; for ( var i = 0, n = tracks.length; i !== n; ++ i ) { var track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; }, trim: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; }, optimize: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function MaterialLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.textures = {}; } Object.assign( MaterialLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, setTextures: function ( value ) { this.textures = value; }, parse: function ( json ) { var textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( "THREE.MaterialLoader: Undefined texture", name ); } return textures[ name ]; } var material = new Materials[ json.type ](); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.rotation !== undefined ) material.rotation = json.rotation; if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; if ( json.scale !== undefined ) material.scale = json.scale; if ( json.skinning !== undefined ) material.skinning = json.skinning; if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; if ( json.dithering !== undefined ) material.dithering = json.dithering; if ( json.visible !== undefined ) material.visible = json.visible; if ( json.userData !== undefined ) material.userData = json.userData; // Deprecated if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.alphaMap !== undefined ) { material.alphaMap = getTexture( json.alphaMap ); material.transparent = true; } if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalScale !== undefined ) { var normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); return material; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferGeometryLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( BufferGeometryLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, parse: function ( json ) { var geometry = new BufferGeometry(); var index = json.data.index; if ( index !== undefined ) { var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } var attributes = json.data.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); } var groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( var i = 0, n = groups.length; i !== n; ++ i ) { var group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } var boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { var center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } return geometry; } } ); var TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, // Workaround for IE11 pre KB2929437. See #11440 Uint8ClampedArray: typeof Uint8ClampedArray !== "undefined" ? Uint8ClampedArray : Uint8Array, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; /** * @author alteredq / http://alteredqualia.com/ */ function Loader() { this.onLoadStart = function () {}; this.onLoadProgress = function () {}; this.onLoadComplete = function () {}; } Loader.Handlers = { handlers: [], add: function ( regex, loader ) { this.handlers.push( regex, loader ); }, get: function ( file ) { var handlers = this.handlers; for ( var i = 0, l = handlers.length; i < l; i += 2 ) { var regex = handlers[ i ]; var loader = handlers[ i + 1 ]; if ( regex.test( file ) ) { return loader; } } return null; } }; Object.assign( Loader.prototype, { crossOrigin: undefined, initMaterials: function ( materials, texturePath, crossOrigin ) { var array = []; for ( var i = 0; i < materials.length; ++ i ) { array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } return array; }, createMaterial: ( function () { var BlendingMode = { NoBlending: NoBlending, NormalBlending: NormalBlending, AdditiveBlending: AdditiveBlending, SubtractiveBlending: SubtractiveBlending, MultiplyBlending: MultiplyBlending, CustomBlending: CustomBlending }; var color = new Color(); var textureLoader = new TextureLoader(); var materialLoader = new MaterialLoader(); return function createMaterial( m, texturePath, crossOrigin ) { // convert from old material format var textures = {}; function loadTexture( path, repeat, offset, wrap, anisotropy ) { var fullPath = texturePath + path; var loader = Loader.Handlers.get( fullPath ); var texture; if ( loader !== null ) { texture = loader.load( fullPath ); } else { textureLoader.setCrossOrigin( crossOrigin ); texture = textureLoader.load( fullPath ); } if ( repeat !== undefined ) { texture.repeat.fromArray( repeat ); if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; } if ( offset !== undefined ) { texture.offset.fromArray( offset ); } if ( wrap !== undefined ) { if ( wrap[ 0 ] === "repeat" ) texture.wrapS = RepeatWrapping; if ( wrap[ 0 ] === "mirror" ) texture.wrapS = MirroredRepeatWrapping; if ( wrap[ 1 ] === "repeat" ) texture.wrapT = RepeatWrapping; if ( wrap[ 1 ] === "mirror" ) texture.wrapT = MirroredRepeatWrapping; } if ( anisotropy !== undefined ) { texture.anisotropy = anisotropy; } var uuid = _Math.generateUUID(); textures[ uuid ] = texture; return uuid; } // var json = { uuid: _Math.generateUUID(), type: "MeshLambertMaterial" }; for ( var name in m ) { var value = m[ name ]; switch ( name ) { case "DbgColor": case "DbgIndex": case "opticalDensity": case "illumination": break; case "DbgName": json.name = value; break; case "blending": json.blending = BlendingMode[ value ]; break; case "colorAmbient": case "mapAmbient": console.warn( "THREE.Loader.createMaterial:", name, "is no longer supported." ); break; case "colorDiffuse": json.color = color.fromArray( value ).getHex(); break; case "colorSpecular": json.specular = color.fromArray( value ).getHex(); break; case "colorEmissive": json.emissive = color.fromArray( value ).getHex(); break; case "specularCoef": json.shininess = value; break; case "shading": if ( value.toLowerCase() === "basic" ) json.type = "MeshBasicMaterial"; if ( value.toLowerCase() === "phong" ) json.type = "MeshPhongMaterial"; if ( value.toLowerCase() === "standard" ) json.type = "MeshStandardMaterial"; break; case "mapDiffuse": json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); break; case "mapDiffuseRepeat": case "mapDiffuseOffset": case "mapDiffuseWrap": case "mapDiffuseAnisotropy": break; case "mapEmissive": json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); break; case "mapEmissiveRepeat": case "mapEmissiveOffset": case "mapEmissiveWrap": case "mapEmissiveAnisotropy": break; case "mapLight": json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); break; case "mapLightRepeat": case "mapLightOffset": case "mapLightWrap": case "mapLightAnisotropy": break; case "mapAO": json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); break; case "mapAORepeat": case "mapAOOffset": case "mapAOWrap": case "mapAOAnisotropy": break; case "mapBump": json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); break; case "mapBumpScale": json.bumpScale = value; break; case "mapBumpRepeat": case "mapBumpOffset": case "mapBumpWrap": case "mapBumpAnisotropy": break; case "mapNormal": json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); break; case "mapNormalFactor": json.normalScale = [ value, value ]; break; case "mapNormalRepeat": case "mapNormalOffset": case "mapNormalWrap": case "mapNormalAnisotropy": break; case "mapSpecular": json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); break; case "mapSpecularRepeat": case "mapSpecularOffset": case "mapSpecularWrap": case "mapSpecularAnisotropy": break; case "mapMetalness": json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); break; case "mapMetalnessRepeat": case "mapMetalnessOffset": case "mapMetalnessWrap": case "mapMetalnessAnisotropy": break; case "mapRoughness": json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); break; case "mapRoughnessRepeat": case "mapRoughnessOffset": case "mapRoughnessWrap": case "mapRoughnessAnisotropy": break; case "mapAlpha": json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); break; case "mapAlphaRepeat": case "mapAlphaOffset": case "mapAlphaWrap": case "mapAlphaAnisotropy": break; case "flipSided": json.side = BackSide; break; case "doubleSided": json.side = DoubleSide; break; case "transparency": console.warn( "THREE.Loader.createMaterial: transparency has been renamed to opacity" ); json.opacity = value; break; case "depthTest": case "depthWrite": case "colorWrite": case "opacity": case "reflectivity": case "transparent": case "visible": case "wireframe": json[ name ] = value; break; case "vertexColors": if ( value === true ) json.vertexColors = VertexColors; if ( value === "face" ) json.vertexColors = FaceColors; break; default: console.error( "THREE.Loader.createMaterial: Unsupported", name, value ); break; } } if ( json.type === "MeshBasicMaterial" ) delete json.emissive; if ( json.type !== "MeshPhongMaterial" ) delete json.specular; if ( json.opacity < 1 ) json.transparent = true; materialLoader.setTextures( textures ); return materialLoader.parse( json ); }; } )() } ); /** * @author Don McCurdy / https://www.donmccurdy.com */ var LoaderUtils = { decodeText: function ( array ) { if ( typeof TextDecoder !== "undefined" ) { return new TextDecoder().decode( array ); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. var s = ""; for ( var i = 0, il = array.length; i < il; i ++ ) { // Implicitly assumes little-endian. s += String.fromCharCode( array[ i ] ); } return s; }, extractUrlBase: function ( url ) { var parts = url.split( "/" ); if ( parts.length === 1 ) return "./"; parts.pop(); return parts.join( "/" ) + "/"; } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function JSONLoader( manager ) { if ( typeof manager === "boolean" ) { console.warn( "THREE.JSONLoader: showStatus parameter has been removed from constructor." ); manager = undefined; } this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.withCredentials = false; } Object.assign( JSONLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : LoaderUtils.extractUrlBase( url ); var loader = new FileLoader( this.manager ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { var json = JSON.parse( text ); var metadata = json.metadata; if ( metadata !== undefined ) { var type = metadata.type; if ( type !== undefined ) { if ( type.toLowerCase() === "object" ) { console.error( "THREE.JSONLoader: " + url + " should be loaded with THREE.ObjectLoader instead." ); return; } if ( type.toLowerCase() === "scene" ) { console.error( "THREE.JSONLoader: " + url + " should be loaded with THREE.SceneLoader instead." ); return; } } } var object = scope.parse( json, texturePath ); onLoad( object.geometry, object.materials ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, parse: ( function () { function parseModel( json, geometry ) { function isBitSet( value, position ) { return value & ( 1 << position ); } var i, j, fi, offset, zLength, colorIndex, normalIndex, uvIndex, materialIndex, type, isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor, vertex, face, faceA, faceB, hex, normal, uvLayer, uv, u, v, faces = json.faces, vertices = json.vertices, normals = json.normals, colors = json.colors, scale = json.scale, nUvLayers = 0; if ( json.uvs !== undefined ) { // disregard empty arrays for ( i = 0; i < json.uvs.length; i ++ ) { if ( json.uvs[ i ].length ) nUvLayers ++; } for ( i = 0; i < nUvLayers; i ++ ) { geometry.faceVertexUvs[ i ] = []; } } offset = 0; zLength = vertices.length; while ( offset < zLength ) { vertex = new Vector3(); vertex.x = vertices[ offset ++ ] * scale; vertex.y = vertices[ offset ++ ] * scale; vertex.z = vertices[ offset ++ ] * scale; geometry.vertices.push( vertex ); } offset = 0; zLength = faces.length; while ( offset < zLength ) { type = faces[ offset ++ ]; isQuad = isBitSet( type, 0 ); hasMaterial = isBitSet( type, 1 ); hasFaceVertexUv = isBitSet( type, 3 ); hasFaceNormal = isBitSet( type, 4 ); hasFaceVertexNormal = isBitSet( type, 5 ); hasFaceColor = isBitSet( type, 6 ); hasFaceVertexColor = isBitSet( type, 7 ); // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); if ( isQuad ) { faceA = new Face3(); faceA.a = faces[ offset ]; faceA.b = faces[ offset + 1 ]; faceA.c = faces[ offset + 3 ]; faceB = new Face3(); faceB.a = faces[ offset + 1 ]; faceB.b = faces[ offset + 2 ]; faceB.c = faces[ offset + 3 ]; offset += 4; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; faceA.materialIndex = materialIndex; faceB.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; geometry.faceVertexUvs[ i ][ fi + 1 ] = []; for ( j = 0; j < 4; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; faceA.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); faceB.normal.copy( faceA.normal ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 4; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); if ( i !== 2 ) faceA.vertexNormals.push( normal ); if ( i !== 0 ) faceB.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; faceA.color.setHex( hex ); faceB.color.setHex( hex ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 4; i ++ ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); } } geometry.faces.push( faceA ); geometry.faces.push( faceB ); } else { face = new Face3(); face.a = faces[ offset ++ ]; face.b = faces[ offset ++ ]; face.c = faces[ offset ++ ]; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; face.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; for ( j = 0; j < 3; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); geometry.faceVertexUvs[ i ][ fi ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; face.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 3; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); face.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; face.color.setHex( colors[ colorIndex ] ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 3; i ++ ) { colorIndex = faces[ offset ++ ]; face.vertexColors.push( new Color( colors[ colorIndex ] ) ); } } geometry.faces.push( face ); } } } function parseSkin( json, geometry ) { var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; if ( json.skinWeights ) { for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { var x = json.skinWeights[ i ]; var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; geometry.skinWeights.push( new Vector4( x, y, z, w ) ); } } if ( json.skinIndices ) { for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { var a = json.skinIndices[ i ]; var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; geometry.skinIndices.push( new Vector4( a, b, c, d ) ); } } geometry.bones = json.bones; if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { console.warn( "When skinning, number of vertices (" + geometry.vertices.length + "), skinIndices (" + geometry.skinIndices.length + "), and skinWeights (" + geometry.skinWeights.length + ") should match." ); } } function parseMorphing( json, geometry ) { var scale = json.scale; if ( json.morphTargets !== undefined ) { for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { geometry.morphTargets[ i ] = {}; geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; geometry.morphTargets[ i ].vertices = []; var dstVertices = geometry.morphTargets[ i ].vertices; var srcVertices = json.morphTargets[ i ].vertices; for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { var vertex = new Vector3(); vertex.x = srcVertices[ v ] * scale; vertex.y = srcVertices[ v + 1 ] * scale; vertex.z = srcVertices[ v + 2 ] * scale; dstVertices.push( vertex ); } } } if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { console.warn( "THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors." ); var faces = geometry.faces; var morphColors = json.morphColors[ 0 ].colors; for ( var i = 0, l = faces.length; i < l; i ++ ) { faces[ i ].color.fromArray( morphColors, i * 3 ); } } } function parseAnimations( json, geometry ) { var outputAnimations = []; // parse old style Bone/Hierarchy animations var animations = []; if ( json.animation !== undefined ) { animations.push( json.animation ); } if ( json.animations !== undefined ) { if ( json.animations.length ) { animations = animations.concat( json.animations ); } else { animations.push( json.animations ); } } for ( var i = 0; i < animations.length; i ++ ) { var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); if ( clip ) outputAnimations.push( clip ); } // parse implicit morph animations if ( geometry.morphTargets ) { // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); outputAnimations = outputAnimations.concat( morphAnimationClips ); } if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; } return function ( json, texturePath ) { if ( json.data !== undefined ) { // Geometry 4.0 spec json = json.data; } if ( json.scale !== undefined ) { json.scale = 1.0 / json.scale; } else { json.scale = 1.0; } var geometry = new Geometry(); parseModel( json, geometry ); parseSkin( json, geometry ); parseMorphing( json, geometry ); parseAnimations( json, geometry ); geometry.computeFaceNormals(); geometry.computeBoundingSphere(); if ( json.materials === undefined || json.materials.length === 0 ) { return { geometry: geometry }; } else { var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); return { geometry: geometry, materials: materials }; } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function ObjectLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.texturePath = ""; } Object.assign( ObjectLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( this.texturePath === "" ) { this.texturePath = url.substring( 0, url.lastIndexOf( "/" ) + 1 ); } var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { var json = null; try { json = JSON.parse( text ); } catch ( error ) { if ( onError !== undefined ) onError( error ); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message ); return; } var metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { console.error( "THREE.ObjectLoader: Can"t load " + url + ". Use THREE.JSONLoader instead." ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, parse: function ( json, onLoad ) { var shapes = this.parseShape( json.shapes ); var geometries = this.parseGeometries( json.geometries, shapes ); var images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); var textures = this.parseTextures( json.textures, images ); var materials = this.parseMaterials( json.materials, textures ); var object = this.parseObject( json.object, geometries, materials ); if ( json.animations ) { object.animations = this.parseAnimations( json.animations ); } if ( json.images === undefined || json.images.length === 0 ) { if ( onLoad !== undefined ) onLoad( object ); } return object; }, parseShape: function ( json ) { var shapes = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var shape = new Shape().fromJSON( json[ i ] ); shapes[ shape.uuid ] = shape; } } return shapes; }, parseGeometries: function ( json, shapes ) { var geometries = {}; if ( json !== undefined ) { var geometryLoader = new JSONLoader(); var bufferGeometryLoader = new BufferGeometryLoader(); for ( var i = 0, l = json.length; i < l; i ++ ) { var geometry; var data = json[ i ]; switch ( data.type ) { case "PlaneGeometry": case "PlaneBufferGeometry": geometry = new Geometries[ data.type ]( data.width, data.height, data.widthSegments, data.heightSegments ); break; case "BoxGeometry": case "BoxBufferGeometry": case "CubeGeometry": // backwards compatible geometry = new Geometries[ data.type ]( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); break; case "CircleGeometry": case "CircleBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.segments, data.thetaStart, data.thetaLength ); break; case "CylinderGeometry": case "CylinderBufferGeometry": geometry = new Geometries[ data.type ]( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "ConeGeometry": case "ConeBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "SphereGeometry": case "SphereBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); break; case "DodecahedronGeometry": case "DodecahedronBufferGeometry": case "IcosahedronGeometry": case "IcosahedronBufferGeometry": case "OctahedronGeometry": case "OctahedronBufferGeometry": case "TetrahedronGeometry": case "TetrahedronBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.detail ); break; case "RingGeometry": case "RingBufferGeometry": geometry = new Geometries[ data.type ]( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); break; case "TorusGeometry": case "TorusBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); break; case "TorusKnotGeometry": case "TorusKnotBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); break; case "LatheGeometry": case "LatheBufferGeometry": geometry = new Geometries[ data.type ]( data.points, data.segments, data.phiStart, data.phiLength ); break; case "PolyhedronGeometry": case "PolyhedronBufferGeometry": geometry = new Geometries[ data.type ]( data.vertices, data.indices, data.radius, data.details ); break; case "ShapeGeometry": case "ShapeBufferGeometry": var geometryShapes = []; for ( var i = 0, l = data.shapes.length; i < l; i ++ ) { var shape = shapes[ data.shapes[ i ] ]; geometryShapes.push( shape ); } geometry = new Geometries[ data.type ]( geometryShapes, data.curveSegments ); break; case "BufferGeometry": geometry = bufferGeometryLoader.parse( data ); break; case "Geometry": geometry = geometryLoader.parse( data, this.texturePath ).geometry; break; default: console.warn( "THREE.ObjectLoader: Unsupported geometry type "" + data.type + """ ); continue; } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; geometries[ data.uuid ] = geometry; } } return geometries; }, parseMaterials: function ( json, textures ) { var materials = {}; if ( json !== undefined ) { var loader = new MaterialLoader(); loader.setTextures( textures ); for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.type === "MultiMaterial" ) { // Deprecated var array = []; for ( var j = 0; j < data.materials.length; j ++ ) { array.push( loader.parse( data.materials[ j ] ) ); } materials[ data.uuid ] = array; } else { materials[ data.uuid ] = loader.parse( data ); } } } return materials; }, parseAnimations: function ( json ) { var animations = []; for ( var i = 0; i < json.length; i ++ ) { var clip = AnimationClip.parse( json[ i ] ); animations.push( clip ); } return animations; }, parseImages: function ( json, onLoad ) { var scope = this; var images = {}; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); } if ( json !== undefined && json.length > 0 ) { var manager = new LoadingManager( onLoad ); var loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( var i = 0, l = json.length; i < l; i ++ ) { var image = json[ i ]; var path = /^(//)|([a-z]+:(//)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; images[ image.uuid ] = loadImage( path ); } } return images; }, parseTextures: function ( json, images ) { function parseConstant( value, type ) { if ( typeof value === "number" ) return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value ); return type[ value ]; } var textures = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.image === undefined ) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined image", data.image ); } var texture = new Texture( images[ data.image ] ); texture.needsUpdate = true; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.center !== undefined ) texture.center.fromArray( data.center ); if ( data.rotation !== undefined ) texture.rotation = data.rotation; if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); } if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; textures[ data.uuid ] = texture; } } return textures; }, parseObject: function () { var matrix = new Matrix4(); return function parseObject( data, geometries, materials ) { var object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined geometry", name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( Array.isArray( name ) ) { var array = []; for ( var i = 0, l = name.length; i < l; i ++ ) { var uuid = name[ i ]; if ( materials[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", uuid ); } array.push( materials[ uuid ] ); } return array; } if ( materials[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", name ); } return materials[ name ]; } switch ( data.type ) { case "Scene": object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } } if ( data.fog !== undefined ) { if ( data.fog.type === "Fog" ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === "FogExp2" ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); break; case "AmbientLight": object = new AmbientLight( data.color, data.intensity ); break; case "DirectionalLight": object = new DirectionalLight( data.color, data.intensity ); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case "SkinnedMesh": console.warn( "THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet." ); case "Mesh": var geometry = getGeometry( data.geometry ); var material = getMaterial( data.material ); if ( geometry.bones && geometry.bones.length > 0 ) { object = new SkinnedMesh( geometry, material ); } else { object = new Mesh( geometry, material ); } break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); break; case "LineLoop": object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineSegments": object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "PointCloud": case "Points": object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "Sprite": object = new Sprite( getMaterial( data.material ) ); break; case "Group": object = new Group(); break; default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { matrix.fromArray( data.matrix ); matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.children !== undefined ) { var children = data.children; for ( var i = 0; i < children.length; i ++ ) { object.add( this.parseObject( children[ i ], geometries, materials ) ); } } if ( data.type === "LOD" ) { var levels = data.levels; for ( var l = 0; l < levels.length; l ++ ) { var level = levels[ l ]; var child = object.getObjectByProperty( "uuid", level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance ); } } } return object; }; }() } ); var TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, SphericalReflectionMapping: SphericalReflectionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; var TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; var TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipMapNearestFilter: NearestMipMapNearestFilter, NearestMipMapLinearFilter: NearestMipMapLinearFilter, LinearFilter: LinearFilter, LinearMipMapNearestFilter: LinearMipMapNearestFilter, LinearMipMapLinearFilter: LinearMipMapLinearFilter }; /** * @author thespite / http://clicktorelease.com/ */ function ImageBitmapLoader( manager ) { if ( typeof createImageBitmap === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported." ); } if ( typeof fetch === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: fetch() not supported." ); } this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.options = undefined; } ImageBitmapLoader.prototype = { constructor: ImageBitmapLoader, setOptions: function setOptions( options ) { this.options = options; return this; }, load: function load( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } fetch( url ).then( function ( res ) { return res.blob(); } ).then( function ( blob ) { return createImageBitmap( blob, scope.options ); } ).then( function ( imageBitmap ) { Cache.add( url, imageBitmap ); if ( onLoad ) onLoad( imageBitmap ); scope.manager.itemEnd( url ); } ).catch( function ( e ) { if ( onError ) onError( e ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); }, setCrossOrigin: function ( /* value */ ) { return this; }, setPath: function ( value ) { this.path = value; return this; } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" **/ function ShapePath() { this.type = "ShapePath"; this.subPaths = []; this.currentPath = null; } Object.assign( ShapePath.prototype, { moveTo: function ( x, y ) { this.currentPath = new Path(); this.subPaths.push( this.currentPath ); this.currentPath.moveTo( x, y ); }, lineTo: function ( x, y ) { this.currentPath.lineTo( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); }, splineThru: function ( pts ) { this.currentPath.splineThru( pts ); }, toShapes: function ( isCCW, noHoles ) { function toShapesNoHoles( inSubpaths ) { var shapes = []; for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { var tmpPath = inSubpaths[ i ]; var tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { var polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line var inside = false; for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { var edgeLowPt = inPolygon[ p ]; var edgeHighPt = inPolygon[ q ]; var edgeDx = edgeHighPt.x - edgeLowPt.x; var edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } var isClockWise = ShapeUtils.isClockWise; var subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; if ( noHoles === true ) return toShapesNoHoles( subPaths ); var solid, tmpPath, tmpShape, shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); var betterShapeHoles = []; var newShapes = []; var newShapeHoles = []; var mainIdx = 0; var tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( var i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log("cw", i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { var ambiguous = false; var toChange = []; for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { var sho = newShapeHoles[ sIdx ]; for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { var ho = sho[ hIdx ]; var hole_unassigned = true; for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { // console.log("to change: ", toChange); if ( ! ambiguous ) newShapeHoles = betterShapeHoles; } } var tmpHoles; for ( var i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author mrdoob / http://mrdoob.com/ */ function Font( data ) { this.type = "Font"; this.data = data; } Object.assign( Font.prototype, { isFont: true, generateShapes: function ( text, size, divisions ) { function createPaths( text ) { var chars = String( text ).split( "" ); var scale = size / data.resolution; var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; var offsetX = 0, offsetY = 0; var paths = []; for ( var i = 0; i < chars.length; i ++ ) { var char = chars[ i ]; if ( char === " " ) { offsetX = 0; offsetY -= line_height; } else { var ret = createPath( char, scale, offsetX, offsetY ); offsetX += ret.offsetX; paths.push( ret.path ); } } return paths; } function createPath( c, scale, offsetX, offsetY ) { var glyph = data.glyphs[ c ] || data.glyphs[ "?" ]; if ( ! glyph ) return; var path = new ShapePath(); var pts = []; var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste; if ( glyph.o ) { var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( " " ) ); for ( var i = 0, l = outline.length; i < l; ) { var action = outline[ i ++ ]; switch ( action ) { case "m": // moveTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.moveTo( x, y ); break; case "l": // lineTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.lineTo( x, y ); break; case "q": // quadraticCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); laste = pts[ pts.length - 1 ]; if ( laste ) { cpx0 = laste.x; cpy0 = laste.y; } break; case "b": // bezierCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; cpx2 = outline[ i ++ ] * scale + offsetX; cpy2 = outline[ i ++ ] * scale + offsetY; path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); laste = pts[ pts.length - 1 ]; if ( laste ) { cpx0 = laste.x; cpy0 = laste.y; } break; } } } return { offsetX: glyph.ha * scale, path: path }; } // if ( size === undefined ) size = 100; if ( divisions === undefined ) divisions = 4; var data = this.data; var paths = createPaths( text ); var shapes = []; for ( var p = 0, pl = paths.length; p < pl; p ++ ) { Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); } return shapes; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function FontLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FontLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { var json; try { json = JSON.parse( text ); } catch ( e ) { console.warn( "THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead." ); json = JSON.parse( text.substring( 65, text.length - 2 ) ); } var font = scope.parse( json ); if ( onLoad ) onLoad( font ); }, onProgress, onError ); }, parse: function ( json ) { return new Font( json ); }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ var context; var AudioContext = { getContext: function () { if ( context === undefined ) { context = new ( window.AudioContext || window.webkitAudioContext )(); } return context; }, setContext: function ( value ) { context = value; } }; /** * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function AudioLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( AudioLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var context = AudioContext.getContext(); context.decodeAudioData( buffer, function ( audioBuffer ) { onLoad( audioBuffer ); } ); }, onProgress, onError ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function StereoCamera() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; } Object.assign( StereoCamera.prototype, { update: ( function () { var instance, focus, fov, aspect, near, far, zoom, eyeSep; var eyeRight = new Matrix4(); var eyeLeft = new Matrix4(); return function update( camera ) { var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || aspect !== camera.aspect * this.aspect || near !== camera.near || far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep; if ( needsUpdate ) { instance = this; focus = camera.focus; fov = camera.fov; aspect = camera.aspect * this.aspect; near = camera.near; far = camera.far; zoom = camera.zoom; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ var projectionMatrix = camera.projectionMatrix.clone(); eyeSep = this.eyeSep / 2; var eyeSepOnProjection = eyeSep * near / focus; var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; var xmin, xmax; // translate xOffset eyeLeft.elements[ 12 ] = - eyeSep; eyeRight.elements[ 12 ] = eyeSep; // for left eye xmin = - ymax * aspect + eyeSepOnProjection; xmax = ymax * aspect + eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( projectionMatrix ); // for right eye xmin = - ymax * aspect - eyeSepOnProjection; xmax = ymax * aspect - eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); }; } )() } ); /** * Camera for rendering cube maps * - renders scene into axis-aligned cube * * @author alteredq / http://alteredqualia.com/ */ function CubeCamera( near, far, cubeResolution ) { Object3D.call( this ); this.type = "CubeCamera"; var fov = 90, aspect = 1; var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); this.add( cameraPX ); var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); this.add( cameraNX ); var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); this.add( cameraPY ); var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); this.add( cameraNY ); var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); this.add( cameraPZ ); var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.renderTarget.texture.name = "CubeCamera"; this.update = function ( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); var renderTarget = this.renderTarget; var generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderTarget.activeCubeFace = 0; renderer.render( scene, cameraPX, renderTarget ); renderTarget.activeCubeFace = 1; renderer.render( scene, cameraNX, renderTarget ); renderTarget.activeCubeFace = 2; renderer.render( scene, cameraPY, renderTarget ); renderTarget.activeCubeFace = 3; renderer.render( scene, cameraNY, renderTarget ); renderTarget.activeCubeFace = 4; renderer.render( scene, cameraPZ, renderTarget ); renderTarget.texture.generateMipmaps = generateMipmaps; renderTarget.activeCubeFace = 5; renderer.render( scene, cameraNZ, renderTarget ); renderer.setRenderTarget( null ); }; this.clear = function ( renderer, color, depth, stencil ) { var renderTarget = this.renderTarget; for ( var i = 0; i < 6; i ++ ) { renderTarget.activeCubeFace = i; renderer.setRenderTarget( renderTarget ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( null ); }; } CubeCamera.prototype = Object.create( Object3D.prototype ); CubeCamera.prototype.constructor = CubeCamera; /** * @author mrdoob / http://mrdoob.com/ */ function AudioListener() { Object3D.call( this ); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; } AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: AudioListener, getInput: function () { return this.gain; }, removeFilter: function ( ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } }, getFilter: function () { return this.filter; }, setFilter: function ( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); }, getMasterVolume: function () { return this.gain.gain.value; }, setMasterVolume: function ( value ) { this.gain.gain.value = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); var quaternion = new Quaternion(); var scale = new Vector3(); var orientation = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); var listener = this.context.listener; var up = this.up; this.matrixWorld.decompose( position, quaternion, scale ); orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); if ( listener.positionX ) { listener.positionX.setValueAtTime( position.x, this.context.currentTime ); listener.positionY.setValueAtTime( position.y, this.context.currentTime ); listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); listener.upX.setValueAtTime( up.x, this.context.currentTime ); listener.upY.setValueAtTime( up.y, this.context.currentTime ); listener.upZ.setValueAtTime( up.z, this.context.currentTime ); } else { listener.setPosition( position.x, position.y, position.z ); listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function Audio( listener ) { Object3D.call( this ); this.type = "Audio"; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.loop = false; this.startTime = 0; this.offset = 0; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.sourceType = "empty"; this.filters = []; } Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Audio, getOutput: function () { return this.gain; }, setNodeSource: function ( audioNode ) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; }, setBuffer: function ( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = "buffer"; if ( this.autoplay ) this.play(); return this; }, play: function () { if ( this.isPlaying === true ) { console.warn( "THREE.Audio: Audio is already playing." ); return; } if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } var source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.onended = this.onEnded.bind( this ); source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); this.startTime = this.context.currentTime; source.start( this.startTime, this.offset ); this.isPlaying = true; this.source = source; return this.connect(); }, pause: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } if ( this.isPlaying === true ) { this.source.stop(); this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; this.isPlaying = false; } return this; }, stop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.source.stop(); this.offset = 0; this.isPlaying = false; return this; }, connect: function () { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } return this; }, disconnect: function () { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } return this; }, getFilters: function () { return this.filters; }, setFilters: function ( value ) { if ( ! value ) value = []; if ( this.isPlaying === true ) { this.disconnect(); this.filters = value; this.connect(); } else { this.filters = value; } return this; }, getFilter: function () { return this.getFilters()[ 0 ]; }, setFilter: function ( filter ) { return this.setFilters( filter ? [ filter ] : [] ); }, setPlaybackRate: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); } return this; }, getPlaybackRate: function () { return this.playbackRate; }, onEnded: function () { this.isPlaying = false; }, getLoop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return false; } return this.loop; }, setLoop: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; }, getVolume: function () { return this.gain.gain.value; }, setVolume: function ( value ) { this.gain.gain.value = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PositionalAudio( listener ) { Audio.call( this, listener ); this.panner = this.context.createPanner(); this.panner.connect( this.gain ); } PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { constructor: PositionalAudio, getOutput: function () { return this.panner; }, getRefDistance: function () { return this.panner.refDistance; }, setRefDistance: function ( value ) { this.panner.refDistance = value; }, getRolloffFactor: function () { return this.panner.rolloffFactor; }, setRolloffFactor: function ( value ) { this.panner.rolloffFactor = value; }, getDistanceModel: function () { return this.panner.distanceModel; }, setDistanceModel: function ( value ) { this.panner.distanceModel = value; }, getMaxDistance: function () { return this.panner.maxDistance; }, setMaxDistance: function ( value ) { this.panner.maxDistance = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); position.setFromMatrixPosition( this.matrixWorld ); this.panner.setPosition( position.x, position.y, position.z ); }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function AudioAnalyser( audio, fftSize ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } Object.assign( AudioAnalyser.prototype, { getFrequencyData: function () { this.analyser.getByteFrequencyData( this.data ); return this.data; }, getAverageFrequency: function () { var value = 0, data = this.getFrequencyData(); for ( var i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } ); /** * * Buffered scene graph property that allows weighted accumulation. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function PropertyMixer( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; var bufferType = Float64Array, mixFunction; switch ( typeName ) { case "quaternion": mixFunction = this._slerp; break; case "string": case "bool": bufferType = Array; mixFunction = this._select; break; default: mixFunction = this._lerp; } this.buffer = new bufferType( valueSize * 4 ); // layout: [ incoming | accu0 | accu1 | orig ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property this._mixBufferRegion = mixFunction; this.cumulativeWeight = 0; this.useCount = 0; this.referenceCount = 0; } Object.assign( PropertyMixer.prototype, { // accumulate data in the "incoming" region into "accu" accumulate: function ( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place var buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride, currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( var i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; var mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; }, // apply the state of "accu" to the binding when accus differ apply: function ( accuIndex ) { var stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, binding = this.binding; this.cumulativeWeight = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) var originalValueOffset = stride * 3; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } for ( var i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } }, // remember the state of the bound property and copy it to both accus saveOriginalState: function () { var binding = this.binding; var buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * 3; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } this.cumulativeWeight = 0; }, // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState: function () { var originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); }, // mix functions _select: function ( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( var i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } }, _slerp: function ( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); }, _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { var s = 1 - t; for ( var i = 0; i !== stride; ++ i ) { var j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } } ); /** * * A reference to a real property in the scene graph. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function Composite( targetGroup, path, optionalParsedPath ) { var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); } Object.assign( Composite.prototype, { getValue: function ( array, offset ) { this.bind(); // bind all binding var firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); }, setValue: function ( array, offset ) { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } }, bind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } }, unbind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } } ); function PropertyBinding( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; this.rootNode = rootNode; } Object.assign( PropertyBinding, { Composite: Composite, create: function ( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } }, /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ sanitizeNodeName: function ( name ) { return name.replace( /s/g, "_" ).replace( /[^w-]/g, "" ); }, parseTrackName: function () { // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. var directoryRe = /((?:[w-]+[/:])*)/; // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". var nodeRe = /([w-.]+)?/; // Object on target node, and accessor. Name may contain only word // characters. Accessor may contain any character except closing bracket. var objectRe = /(?:.([w-]+)(?:[(.+)])?)?/; // Property and accessor. May contain only word characters. Accessor may // contain any non-bracket characters. var propertyRe = /.([w-]+)(?:[(.+)])?/; var trackRe = new RegExp( "" + "^" + directoryRe.source + nodeRe.source + objectRe.source + propertyRe.source + "$" ); var supportedObjectNames = [ "material", "materials", "bones" ]; return function ( trackName ) { var matches = trackRe.exec( trackName ); if ( ! matches ) { throw new Error( "PropertyBinding: Cannot parse trackName: " + trackName ); } var results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], // required propertyIndex: matches[ 6 ] }; var lastDot = results.nodeName && results.nodeName.lastIndexOf( "." ); if ( lastDot !== undefined && lastDot !== - 1 ) { var objectName = results.nodeName.substring( lastDot + 1 ); // Object names must be checked against a whitelist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) { results.nodeName = results.nodeName.substring( 0, lastDot ); results.objectName = objectName; } } if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName ); } return results; }; }(), findNode: function ( root, nodeName ) { if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { var searchSkeleton = function ( skeleton ) { for ( var i = 0; i < skeleton.bones.length; i ++ ) { var bone = skeleton.bones[ i ]; if ( bone.name === nodeName ) { return bone; } } return null; }; var bone = searchSkeleton( root.skeleton ); if ( bone ) { return bone; } } // search into node subtree. if ( root.children ) { var searchNodeSubtree = function ( children ) { for ( var i = 0; i < children.length; i ++ ) { var childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } var result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; var subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; } } ); Object.assign( PropertyBinding.prototype, { // prototype, continued // these are used to "bind" a nonexistent property _getValue_unavailable: function () {}, _setValue_unavailable: function () {}, BindingType: { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }, Versioning: { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }, GetterByBindingType: [ function getValue_direct( buffer, offset ) { buffer[ offset ] = this.node[ this.propertyName ]; }, function getValue_array( buffer, offset ) { var source = this.resolvedProperty; for ( var i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } }, function getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; }, function getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } ], SetterByBindingTypeAndVersioning: [ [ // Direct function setValue_direct( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; }, function setValue_direct_setNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // EntireArray function setValue_array( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } }, function setValue_array_setNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; }, function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // ArrayElement function setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; }, function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // HasToFromArray function setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); }, function setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; }, function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } ] ], getValue: function getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. }, setValue: function getValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); }, // create getter / setter pair for a property in the scene graph bind: function () { var targetObject = this.node, parsedPath = this.parsedPath, objectName = parsedPath.objectName, propertyName = parsedPath.propertyName, propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.error( "THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found." ); return; } if ( objectName ) { var objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case "materials": if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.materials ) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this ); return; } targetObject = targetObject.material.materials; break; case "bones": if ( ! targetObject.skeleton ) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( var i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; default: if ( targetObject[ objectName ] === undefined ) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property var nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { var nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject ); return; } // determine versioning scheme var versioning = this.Versioning.None; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; this.targetObject = targetObject; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; this.targetObject = targetObject; } // determine how the property gets bound var bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this ); return; } if ( targetObject.geometry.isBufferGeometry ) { if ( ! targetObject.geometry.morphAttributes ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this ); return; } for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) { if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } else { if ( ! targetObject.geometry.morphTargets ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.", this ); return; } for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( Array.isArray( nodeProperty ) ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; }, unbind: function () { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( PropertyBinding.prototype, { // initial state of these methods that calls "bind" _getValue_unbound: PropertyBinding.prototype.getValue, _setValue_unbound: PropertyBinding.prototype.setValue, } ); /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. * * @author tschw */ function AnimationObjectGroup() { this.uuid = _Math.generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite var indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( var i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays var scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } Object.assign( AnimationObjectGroup.prototype, { isAnimationObjectGroup: true, add: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ], knownObject = undefined; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject ) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; }, remove: function () { var objects = this._objects, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region var lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; }, // remove & forget uncache: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object"s place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop var lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; indicesByUUID[ lastObject.uuid ] = index; objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; }, // Internal interface used by befriended PropertyBinding.Composite: subscribe_: function ( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ], bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; var paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { var object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; }, unsubscribe_: function ( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { var paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } } ); /** * * Action provided by AnimationMixer for scheduling clip playback on specific * objects. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw * */ function AnimationAction( mixer, clip, localRoot ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot || null; var tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); var interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( var i = 0; i !== nTracks; ++ i ) { var interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = - 1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } Object.assign( AnimationAction.prototype, { // State & Scheduling play: function () { this._mixer._activateAction( this ); return this; }, stop: function () { this._mixer._deactivateAction( this ); return this.reset(); }, reset: function () { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = - 1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); }, isRunning: function () { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); }, // return true when play has been called isScheduled: function () { return this._mixer._isActiveAction( this ); }, startAt: function ( time ) { this._startTime = time; return this; }, setLoop: function ( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; }, // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight: function ( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); }, // return the weight considering fading and .enabled getEffectiveWeight: function () { return this._effectiveWeight; }, fadeIn: function ( duration ) { return this._scheduleFading( duration, 0, 1 ); }, fadeOut: function ( duration ) { return this._scheduleFading( duration, 1, 0 ); }, crossFadeFrom: function ( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if ( warp ) { var fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; }, crossFadeTo: function ( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); }, stopFading: function () { var weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; }, // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale: function ( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); }, // return the time scale considering warping and .paused getEffectiveTimeScale: function () { return this._effectiveTimeScale; }, setDuration: function ( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); }, syncWith: function ( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); }, halt: function ( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); }, warp: function ( startTimeScale, endTimeScale, duration ) { var mixer = this._mixer, now = mixer.time, interpolant = this._timeScaleInterpolant, timeScale = this.timeScale; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; }, stopWarping: function () { var timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; }, // Object Accessors getMixer: function () { return this._mixer; }, getClip: function () { return this._clip; }, getRoot: function () { return this._localRoot || this._mixer._root; }, // Interna _update: function ( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer if ( ! this.enabled ) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight( time ); return; } var startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action var timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); var clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 var weight = this._updateWeight( time ); if ( weight > 0 ) { var interpolants = this._interpolants; var propertyMixers = this._propertyBindings; for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } }, _updateWeight: function ( time ) { var weight = 0; if ( this.enabled ) { weight = this.weight; var interpolant = this._weightInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; }, _updateTimeScale: function ( time ) { var timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; var interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; }, _updateTime: function ( deltaTime ) { var time = this.time + deltaTime; if ( deltaTime === 0 ) return time; var duration = this._clip.duration, loop = this.loop, loopCount = this._loopCount; if ( loop === LoopOnce ) { if ( loopCount === - 1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else break handle_stop; if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime < 0 ? - 1 : 1 } ); } } else { // repetitive Repeat or PingPong var pingPong = ( loop === LoopPingPong ); if ( loopCount === - 1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around var loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); var pending = this.repetitions - loopCount; if ( pending < 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime > 0 ? 1 : - 1 } ); } else { // keep running if ( pending === 0 ) { // entering the last round var atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this._mixer.dispatchEvent( { type: "loop", action: this, loopDelta: loopDelta } ); } } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" this.time = time; return duration - time; } } this.time = time; return time; }, _setEndings: function ( atStart, atEnd, pingPong ) { var settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } }, _scheduleFading: function ( duration, weightNow, weightThen ) { var mixer = this._mixer, now = mixer.time, interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } } ); /** * * Player for AnimationClips. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function AnimationMixer( root ) { this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: AnimationMixer, _bindAction: function ( action, prototypeAction ) { var root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName, bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( var i = 0; i !== nTracks; ++ i ) { var track = tracks[ i ], trackName = track.name, binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } var path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } }, _activateAction: function ( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind var rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } var bindings = action._propertyBindings; // increment reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } }, _deactivateAction: function ( action ) { if ( this._isActiveAction( action ) ) { var bindings = action._propertyBindings; // decrement reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } }, // Memory manager _initMemoryManager: function () { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; var scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; }, // Memory management for AnimationAction objects _isActiveAction: function ( action ) { var index = action._cacheIndex; return index !== null && index < this._nActiveActions; }, _addInactiveAction: function ( action, clipUuid, rootUuid ) { var actions = this._actions, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { var knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; }, _removeInactiveAction: function ( action ) { var actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; var clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; var actionByRoot = actionsForClip.actionByRoot, rootUuid = ( action._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); }, _removeInactiveBindingsForAction: function ( action ) { var bindings = action._propertyBindings; for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } }, _lendAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s var actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; }, _takeBackAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a var actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; }, // Memory management for PropertyMixer objects _addInactiveBinding: function ( binding, rootUuid, trackName ) { var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], bindings = this._bindings; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); }, _removeInactiveBinding: function ( binding ) { var bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; remove_empty_map: { for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars delete bindingsByRoot[ rootUuid ]; } }, _lendBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; }, _takeBackBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; }, // Memory management of Interpolants for weight and time scale _lendControlInterpolant: function () { var interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++, interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, this._controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; }, _takeBackControlInterpolant: function ( interpolant ) { var interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; }, _controlInterpolantsResultBuffer: new Float32Array( 1 ), // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject !== null ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ], prototypeAction = null; if ( actionsForClip !== undefined ) { var existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it var newAction = new AnimationAction( this, clipObject, optionalRoot ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; }, // get an existing action existingAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; }, // deactivates all previously scheduled actions stopAllAction: function () { var actions = this._actions, nActions = this._nActiveActions, bindings = this._bindings, nBindings = this._nActiveBindings; this._nActiveActions = 0; this._nActiveBindings = 0; for ( var i = 0; i !== nActions; ++ i ) { actions[ i ].reset(); } for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].useCount = 0; } return this; }, // advance the time and update apply the animation update: function ( deltaTime ) { deltaTime *= this.timeScale; var actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( var i = 0; i !== nActions; ++ i ) { var action = actions[ i ]; action._update( time, deltaTime, timeDirection, accuIndex ); } // update scene graph var bindings = this._bindings, nBindings = this._nActiveBindings; for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; }, // return this mixer"s root target object getRoot: function () { return this._root; }, // free all resources specific to a particular clip uncacheClip: function ( clip ) { var actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away var actionsToRemove = actionsForClip.knownActions; for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { var action = actionsToRemove[ i ]; this._deactivateAction( action ); var cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } }, // free all resources specific to a particular root target object uncacheRoot: function ( root ) { var rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( var clipUuid in actionsByClip ) { var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( var trackName in bindingByName ) { var binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } }, // remove a targeted clip from the cache uncacheAction: function ( clip, optionalRoot ) { var action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Uniform( value ) { if ( typeof value === "string" ) { console.warn( "THREE.Uniform: Type parameter is no longer needed." ); value = arguments[ 1 ]; } this.value = value; } Uniform.prototype.clone = function () { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferGeometry() { BufferGeometry.call( this ); this.type = "InstancedBufferGeometry"; this.maxInstancedCount = undefined; } InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { constructor: InstancedBufferGeometry, isInstancedBufferGeometry: true, copy: function ( source ) { BufferGeometry.prototype.copy.call( this, source ); this.maxInstancedCount = source.maxInstancedCount; return this; }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { this.uuid = _Math.generateUUID(); this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } Object.defineProperties( InterleavedBufferAttribute.prototype, { count: { get: function () { return this.data.count; } }, array: { get: function () { return this.data.array; } } } ); Object.assign( InterleavedBufferAttribute.prototype, { isInterleavedBufferAttribute: true, setX: function ( index, x ) { this.data.array[ index * this.data.stride + this.offset ] = x; return this; }, setY: function ( index, y ) { this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; }, setZ: function ( index, z ) { this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; }, setW: function ( index, w ) { this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; }, getX: function ( index ) { return this.data.array[ index * this.data.stride + this.offset ]; }, getY: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 1 ]; }, getZ: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 2 ]; }, getW: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 3 ]; }, setXY: function ( index, x, y ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBuffer( array, stride ) { this.uuid = _Math.generateUUID(); this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.onUploadCallback = function () {}; this.version = 0; } Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( InterleavedBuffer.prototype, { isInterleavedBuffer: true, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.stride : 0; this.array = array; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( var i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, clone: function () { return new this.constructor().copy( this ); }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { InterleavedBuffer.call( this, array, stride ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { constructor: InstancedInterleavedBuffer, isInstancedInterleavedBuffer: true, copy: function ( source ) { InterleavedBuffer.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { BufferAttribute.call( this, array, itemSize ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { constructor: InstancedBufferAttribute, isInstancedBufferAttribute: true, copy: function ( source ) { BufferAttribute.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ function Raycaster( origin, direction, near, far ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near || 0; this.far = far || Infinity; this.params = { Mesh: {}, Line: {}, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; Object.defineProperties( this.params, { PointCloud: { get: function () { console.warn( "THREE.Raycaster: params.PointCloud has been renamed to params.Points." ); return this.Points; } } } ); } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.visible === false ) return; object.raycast( raycaster, intersects ); if ( recursive === true ) { var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } Object.assign( Raycaster.prototype, { linePrecision: 1, set: function ( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); }, setFromCamera: function ( coords, camera ) { if ( ( camera && camera.isPerspectiveCamera ) ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); } else if ( ( camera && camera.isOrthographicCamera ) ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); } else { console.error( "THREE.Raycaster: Unsupported camera type." ); } }, intersectObject: function ( object, recursive ) { var intersects = []; intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; }, intersectObjects: function ( objects, recursive ) { var intersects = []; if ( Array.isArray( objects ) === false ) { console.warn( "THREE.Raycaster.intersectObjects: objects is not an Array." ); return intersects; } for ( var i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function Clock( autoStart ) { this.autoStart = ( autoStart !== undefined ) ? autoStart : true; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } Object.assign( Clock.prototype, { start: function () { this.startTime = ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; }, stop: function () { this.getElapsedTime(); this.running = false; this.autoStart = false; }, getElapsedTime: function () { this.getDelta(); return this.elapsedTime; }, getDelta: function () { var diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { var newTime = ( typeof performance === "undefined" ? Date : performance ).now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } ); /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley * * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The poles (phi) are at the positive and negative y axis. * The equator starts at positive z. */ function Spherical( radius, phi, theta ) { this.radius = ( radius !== undefined ) ? radius : 1.0; this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere return this; } Object.assign( Spherical.prototype, { set: function ( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; }, // restrict phi to be betwee EPS and PI-EPS makeSafe: function () { var EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; }, setFromVector3: function ( vec3 ) { this.radius = vec3.length(); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle } return this; } } ); /** * @author Mugen87 / https://github.com/Mugen87 * * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system * */ function Cylindrical( radius, theta, y ) { this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane return this; } Object.assign( Cylindrical.prototype, { set: function ( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; }, setFromVector3: function ( vec3 ) { this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); this.theta = Math.atan2( vec3.x, vec3.z ); this.y = vec3.y; return this; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function ImmediateRenderObject( material ) { Object3D.call( this ); this.material = material; this.render = function ( /* renderCallback */ ) {}; } ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; ImmediateRenderObject.prototype.isImmediateRenderObject = true; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function VertexNormalsHelper( object, size, hex, linewidth ) { this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xff0000; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length * 3; } else if ( objGeometry && objGeometry.isBufferGeometry ) { nNormals = objGeometry.attributes.normal.count; } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; VertexNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { var keys = [ "a", "b", "c" ]; this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { var vertex = vertices[ face[ keys[ j ] ] ]; var normal = face.vertexNormals[ j ]; v1.copy( vertex ).applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } } else if ( objGeometry && objGeometry.isBufferGeometry ) { var objPos = objGeometry.attributes.position; var objNorm = objGeometry.attributes.normal; var idx = 0; // for simplicity, ignore index and drawcalls, and render every normal for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function SpotLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new BufferGeometry(); var positions = [ 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 ]; for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { var p1 = ( i / l ) * Math.PI * 2; var p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } SpotLightHelper.prototype = Object.create( Object3D.prototype ); SpotLightHelper.prototype.constructor = SpotLightHelper; SpotLightHelper.prototype.dispose = function () { this.cone.geometry.dispose(); this.cone.material.dispose(); }; SpotLightHelper.prototype.update = function () { var vector = new Vector3(); var vector2 = new Vector3(); return function update() { this.light.updateMatrixWorld(); var coneLength = this.light.distance ? this.light.distance : 1000; var coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); vector.setFromMatrixPosition( this.light.matrixWorld ); vector2.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( vector2.sub( vector ) ); if ( this.color !== undefined ) { this.cone.material.color.set( this.color ); } else { this.cone.material.color.copy( this.light.color ); } }; }(); /** * @author Sean Griffin / http://twitter.com/sgrif * @author Michael Guerrero / http://realitymeltdown.com * @author mrdoob / http://mrdoob.com/ * @author ikerr / http://verold.com * @author Mugen87 / https://github.com/Mugen87 */ function getBoneList( object ) { var boneList = []; if ( object && object.isBone ) { boneList.push( object ); } for ( var i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } return boneList; } function SkeletonHelper( object ) { var bones = getBoneList( object ); var geometry = new BufferGeometry(); var vertices = []; var colors = []; var color1 = new Color( 0, 0, 1 ); var color2 = new Color( 0, 1, 0 ); for ( var i = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); LineSegments.call( this, geometry, material ); this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } SkeletonHelper.prototype = Object.create( LineSegments.prototype ); SkeletonHelper.prototype.constructor = SkeletonHelper; SkeletonHelper.prototype.updateMatrixWorld = function () { var vector = new Vector3(); var boneMatrix = new Matrix4(); var matrixWorldInv = new Matrix4(); return function updateMatrixWorld( force ) { var bones = this.bones; var geometry = this.geometry; var position = geometry.getAttribute( "position" ); matrixWorldInv.getInverse( this.root.matrixWorld ); for ( var i = 0, j = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j, vector.x, vector.y, vector.z ); boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j + 1, vector.x, vector.y, vector.z ); j += 2; } } geometry.getAttribute( "position" ).needsUpdate = true; Object3D.prototype.updateMatrixWorld.call( this, force ); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function PointLightHelper( light, sphereSize, color ) { this.light = light; this.light.updateMatrixWorld(); this.color = color; var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); Mesh.call( this, geometry, material ); this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); var d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } PointLightHelper.prototype = Object.create( Mesh.prototype ); PointLightHelper.prototype.constructor = PointLightHelper; PointLightHelper.prototype.dispose = function () { this.geometry.dispose(); this.material.dispose(); }; PointLightHelper.prototype.update = function () { if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { this.material.color.copy( this.light.color ); } /* var d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ }; /** * @author abelnation / http://github.com/abelnation * @author Mugen87 / http://github.com/Mugen87 * @author WestLangley / http://github.com/WestLangley */ function RectAreaLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var material = new LineBasicMaterial( { fog: false } ); var geometry = new BufferGeometry(); geometry.addAttribute( "position", new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) ); this.line = new Line( geometry, material ); this.add( this.line ); this.update(); } RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; RectAreaLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; RectAreaLightHelper.prototype.update = function () { // calculate new dimensions of the helper var hx = this.light.width * 0.5; var hy = this.light.height * 0.5; var position = this.line.geometry.attributes.position; var array = position.array; // update vertices array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0; array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0; position.needsUpdate = true; if ( this.color !== undefined ) { this.line.material.color.set( this.color ); } else { this.line.material.color.copy( this.light.color ); } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function HemisphereLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new OctahedronBufferGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); this.material = new MeshBasicMaterial( { wireframe: true, fog: false } ); if ( this.color === undefined ) this.material.vertexColors = VertexColors; var position = geometry.getAttribute( "position" ); var colors = new Float32Array( position.count * 3 ); geometry.addAttribute( "color", new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, this.material ) ); this.update(); } HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; HemisphereLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; HemisphereLightHelper.prototype.update = function () { var vector = new Vector3(); var color1 = new Color(); var color2 = new Color(); return function update() { var mesh = this.children[ 0 ]; if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { var colors = mesh.geometry.getAttribute( "color" ); color1.copy( this.light.color ); color2.copy( this.light.groundColor ); for ( var i = 0, l = colors.count; i < l; i ++ ) { var color = ( i < ( l / 2 ) ) ? color1 : color2; colors.setXYZ( i, color.r, color.g, color.b ); } colors.needsUpdate = true; } mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); }; }(); /** * @author mrdoob / http://mrdoob.com/ */ function GridHelper( size, divisions, color1, color2 ) { size = size || 10; divisions = divisions || 10; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var center = divisions / 2; var step = size / divisions; var halfSize = size / 2; var vertices = [], colors = []; for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { vertices.push( - halfSize, 0, k, halfSize, 0, k ); vertices.push( k, 0, - halfSize, k, 0, halfSize ); var color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } GridHelper.prototype = Object.create( LineSegments.prototype ); GridHelper.prototype.constructor = GridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 * @author Hectate / http://www.github.com/Hectate */ function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { radius = radius || 10; radials = radials || 16; circles = circles || 8; divisions = divisions || 64; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var vertices = []; var colors = []; var x, z; var v, i, j, r, color; // create the radials for ( i = 0; i <= radials; i ++ ) { v = ( i / radials ) * ( Math.PI * 2 ); x = Math.sin( v ) * radius; z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } // create the circles for ( i = 0; i <= circles; i ++ ) { color = ( i & 1 ) ? color1 : color2; r = radius - ( radius / circles * i ); for ( j = 0; j < divisions; j ++ ) { // first vertex v = ( j / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } PolarGridHelper.prototype = Object.create( LineSegments.prototype ); PolarGridHelper.prototype.constructor = PolarGridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function FaceNormalsHelper( object, size, hex, linewidth ) { // FaceNormalsHelper only supports THREE.Geometry this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xffff00; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length; } else { console.warn( "THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead." ); } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; FaceNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var normal = face.normal; v1.copy( vertices[ face.a ] ) .add( vertices[ face.b ] ) .add( vertices[ face.c ] ) .divideScalar( 3 ) .applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function DirectionalLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if ( size === undefined ) size = 1; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.lightPlane = new Line( geometry, material ); this.add( this.lightPlane ); geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.targetLine = new Line( geometry, material ); this.add( this.targetLine ); this.update(); } DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; DirectionalLightHelper.prototype.dispose = function () { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); }; DirectionalLightHelper.prototype.update = function () { var v1 = new Vector3(); var v2 = new Vector3(); var v3 = new Vector3(); return function update() { v1.setFromMatrixPosition( this.light.matrixWorld ); v2.setFromMatrixPosition( this.light.target.matrixWorld ); v3.subVectors( v2, v1 ); this.lightPlane.lookAt( v3 ); if ( this.color !== undefined ) { this.lightPlane.material.color.set( this.color ); this.targetLine.material.color.set( this.color ); } else { this.lightPlane.material.color.copy( this.light.color ); this.targetLine.material.color.copy( this.light.color ); } this.targetLine.lookAt( v3 ); this.targetLine.scale.z = v3.length(); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author Mugen87 / https://github.com/Mugen87 * * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * http://evanw.github.com/lightgl.js/tests/shadowmap.html */ function CameraHelper( camera ) { var geometry = new BufferGeometry(); var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); var vertices = []; var colors = []; var pointMap = {}; // colors var colorFrustum = new Color( 0xffaa00 ); var colorCone = new Color( 0xff0000 ); var colorUp = new Color( 0x00aaff ); var colorTarget = new Color( 0xffffff ); var colorCross = new Color( 0x333333 ); // near addLine( "n1", "n2", colorFrustum ); addLine( "n2", "n4", colorFrustum ); addLine( "n4", "n3", colorFrustum ); addLine( "n3", "n1", colorFrustum ); // far addLine( "f1", "f2", colorFrustum ); addLine( "f2", "f4", colorFrustum ); addLine( "f4", "f3", colorFrustum ); addLine( "f3", "f1", colorFrustum ); // sides addLine( "n1", "f1", colorFrustum ); addLine( "n2", "f2", colorFrustum ); addLine( "n3", "f3", colorFrustum ); addLine( "n4", "f4", colorFrustum ); // cone addLine( "p", "n1", colorCone ); addLine( "p", "n2", colorCone ); addLine( "p", "n3", colorCone ); addLine( "p", "n4", colorCone ); // up addLine( "u1", "u2", colorUp ); addLine( "u2", "u3", colorUp ); addLine( "u3", "u1", colorUp ); // target addLine( "c", "t", colorTarget ); addLine( "p", "c", colorCross ); // cross addLine( "cn1", "cn2", colorCross ); addLine( "cn3", "cn4", colorCross ); addLine( "cf1", "cf2", colorCross ); addLine( "cf3", "cf4", colorCross ); function addLine( a, b, color ) { addPoint( a, color ); addPoint( b, color ); } function addPoint( id, color ) { vertices.push( 0, 0, 0 ); colors.push( color.r, color.g, color.b ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); LineSegments.call( this, geometry, material ); this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } CameraHelper.prototype = Object.create( LineSegments.prototype ); CameraHelper.prototype.constructor = CameraHelper; CameraHelper.prototype.update = function () { var geometry, pointMap; var vector = new Vector3(); var camera = new Camera(); function setPoint( point, x, y, z ) { vector.set( x, y, z ).unproject( camera ); var points = pointMap[ point ]; if ( points !== undefined ) { var position = geometry.getAttribute( "position" ); for ( var i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], vector.x, vector.y, vector.z ); } } } return function update() { geometry = this.geometry; pointMap = this.pointMap; var w = 1, h = 1; // we need just camera projection matrix // world matrix must be identity camera.projectionMatrix.copy( this.camera.projectionMatrix ); // center / target setPoint( "c", 0, 0, - 1 ); setPoint( "t", 0, 0, 1 ); // near setPoint( "n1", - w, - h, - 1 ); setPoint( "n2", w, - h, - 1 ); setPoint( "n3", - w, h, - 1 ); setPoint( "n4", w, h, - 1 ); // far setPoint( "f1", - w, - h, 1 ); setPoint( "f2", w, - h, 1 ); setPoint( "f3", - w, h, 1 ); setPoint( "f4", w, h, 1 ); // up setPoint( "u1", w * 0.7, h * 1.1, - 1 ); setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", 0, h * 2, - 1 ); // cross setPoint( "cf1", - w, 0, 1 ); setPoint( "cf2", w, 0, 1 ); setPoint( "cf3", 0, - h, 1 ); setPoint( "cf4", 0, h, 1 ); setPoint( "cn1", - w, 0, - 1 ); setPoint( "cn2", w, 0, - 1 ); setPoint( "cn3", 0, - h, - 1 ); setPoint( "cn4", 0, h, - 1 ); geometry.getAttribute( "position" ).needsUpdate = true; }; }(); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 */ function BoxHelper( object, color ) { this.object = object; if ( color === undefined ) color = 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = new Float32Array( 8 * 3 ); var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.matrixAutoUpdate = false; this.update(); } BoxHelper.prototype = Object.create( LineSegments.prototype ); BoxHelper.prototype.constructor = BoxHelper; BoxHelper.prototype.update = ( function () { var box = new Box3(); return function update( object ) { if ( object !== undefined ) { console.warn( "THREE.BoxHelper: .update() has no longer arguments." ); } if ( this.object !== undefined ) { box.setFromObject( this.object ); } if ( box.isEmpty() ) return; var min = box.min; var max = box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ var position = this.geometry.attributes.position; var array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); }; } )(); BoxHelper.prototype.setFromObject = function ( object ) { this.object = object; this.update(); return this; }; /** * @author WestLangley / http://github.com/WestLangley */ function Box3Helper( box, hex ) { this.type = "Box3Helper"; this.box = box; var color = ( hex !== undefined ) ? hex : 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.geometry.computeBoundingSphere(); } Box3Helper.prototype = Object.create( LineSegments.prototype ); Box3Helper.prototype.constructor = Box3Helper; Box3Helper.prototype.updateMatrixWorld = function ( force ) { var box = this.box; if ( box.isEmpty() ) return; box.getCenter( this.position ); box.getSize( this.scale ); this.scale.multiplyScalar( 0.5 ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley */ function PlaneHelper( plane, size, hex ) { this.type = "PlaneHelper"; this.plane = plane; this.size = ( size === undefined ) ? 1 : size; var color = ( hex !== undefined ) ? hex : 0xffff00; var positions = [ 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, 0, 1, 0, 0, 0 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); geometry.computeBoundingSphere(); Line.call( this, geometry, new LineBasicMaterial( { color: color } ) ); // var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ]; var geometry2 = new BufferGeometry(); geometry2.addAttribute( "position", new Float32BufferAttribute( positions2, 3 ) ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) ); } PlaneHelper.prototype = Object.create( Line.prototype ); PlaneHelper.prototype.constructor = PlaneHelper; PlaneHelper.prototype.updateMatrixWorld = function ( force ) { var scale = - this.plane.constant; if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); this.lookAt( this.plane.normal ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * * Parameters: * dir - Vector3 * origin - Vector3 * length - Number * color - color in hex value * headLength - Number * headWidth - Number */ var lineGeometry; var coneGeometry; function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { // dir is assumed to be normalized Object3D.call( this ); if ( color === undefined ) color = 0xffff00; if ( length === undefined ) length = 1; if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; if ( lineGeometry === undefined ) { lineGeometry = new BufferGeometry(); lineGeometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); coneGeometry.translate( 0, - 0.5, 0 ); } this.position.copy( origin ); this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } ArrowHelper.prototype = Object.create( Object3D.prototype ); ArrowHelper.prototype.constructor = ArrowHelper; ArrowHelper.prototype.setDirection = ( function () { var axis = new Vector3(); var radians; return function setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { axis.set( dir.z, 0, - dir.x ).normalize(); radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( axis, radians ); } }; }() ); ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); }; ArrowHelper.prototype.setColor = function ( color ) { this.line.material.color.copy( color ); this.cone.material.color.copy( color ); }; /** * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ */ function AxesHelper( size ) { size = size || 1; var vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; var colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } AxesHelper.prototype = Object.create( LineSegments.prototype ); AxesHelper.prototype.constructor = AxesHelper; /** * @author alteredq / http://alteredqualia.com/ */ var SceneUtils = { createMultiMaterialObject: function ( geometry, materials ) { var group = new Group(); for ( var i = 0, l = materials.length; i < l; i ++ ) { group.add( new Mesh( geometry, materials[ i ] ) ); } return group; }, detach: function ( child, parent, scene ) { child.applyMatrix( parent.matrixWorld ); parent.remove( child ); scene.add( child ); }, attach: function ( child, scene, parent ) { child.applyMatrix( new Matrix4().getInverse( parent.matrixWorld ) ); scene.remove( child ); parent.add( child ); } }; /** * @author mrdoob / http://mrdoob.com/ */ function Face4( a, b, c, d, normal, color, materialIndex ) { console.warn( "THREE.Face4 has been removed. A THREE.Face3 will be created instead." ); return new Face3( a, b, c, normal, color, materialIndex ); } var LineStrip = 0; var LinePieces = 1; function MeshFaceMaterial( materials ) { console.warn( "THREE.MeshFaceMaterial has been removed. Use an Array instead." ); return materials; } function MultiMaterial( materials ) { if ( materials === undefined ) materials = []; console.warn( "THREE.MultiMaterial has been removed. Use an Array instead." ); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud( geometry, material ) { console.warn( "THREE.PointCloud has been renamed to THREE.Points." ); return new Points( geometry, material ); } function Particle( material ) { console.warn( "THREE.Particle has been renamed to THREE.Sprite." ); return new Sprite( material ); } function ParticleSystem( geometry, material ) { console.warn( "THREE.ParticleSystem has been renamed to THREE.Points." ); return new Points( geometry, material ); } function PointCloudMaterial( parameters ) { console.warn( "THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleBasicMaterial( parameters ) { console.warn( "THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleSystemMaterial( parameters ) { console.warn( "THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function Vertex( x, y, z ) { console.warn( "THREE.Vertex has been removed. Use THREE.Vector3 instead." ); return new Vector3( x, y, z ); } // function DynamicBufferAttribute( array, itemSize ) { console.warn( "THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead." ); return new BufferAttribute( array, itemSize ).setDynamic( true ); } function Int8Attribute( array, itemSize ) { console.warn( "THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead." ); return new Int8BufferAttribute( array, itemSize ); } function Uint8Attribute( array, itemSize ) { console.warn( "THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead." ); return new Uint8BufferAttribute( array, itemSize ); } function Uint8ClampedAttribute( array, itemSize ) { console.warn( "THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead." ); return new Uint8ClampedBufferAttribute( array, itemSize ); } function Int16Attribute( array, itemSize ) { console.warn( "THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead." ); return new Int16BufferAttribute( array, itemSize ); } function Uint16Attribute( array, itemSize ) { console.warn( "THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead." ); return new Uint16BufferAttribute( array, itemSize ); } function Int32Attribute( array, itemSize ) { console.warn( "THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead." ); return new Int32BufferAttribute( array, itemSize ); } function Uint32Attribute( array, itemSize ) { console.warn( "THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead." ); return new Uint32BufferAttribute( array, itemSize ); } function Float32Attribute( array, itemSize ) { console.warn( "THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead." ); return new Float32BufferAttribute( array, itemSize ); } function Float64Attribute( array, itemSize ) { console.warn( "THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead." ); return new Float64BufferAttribute( array, itemSize ); } // Curve.create = function ( construct, getPoint ) { console.log( "THREE.Curve.create() has been deprecated" ); construct.prototype = Object.create( Curve.prototype ); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Object.assign( CurvePath.prototype, { createPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from path points (for Line or Points objects) var pts = this.getPoints( divisions ); return this.createGeometry( pts ); }, createSpacedPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from equidistant sampling along the path var pts = this.getSpacedPoints( divisions ); return this.createGeometry( pts ); }, createGeometry: function ( points ) { console.warn( "THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); var geometry = new Geometry(); for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return geometry; } } ); // Object.assign( Path.prototype, { fromPoints: function ( points ) { console.warn( "THREE.Path: .fromPoints() has been renamed to .setFromPoints()." ); this.setFromPoints( points ); } } ); // function ClosedSplineCurve3( points ) { console.warn( "THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; this.closed = true; } ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function SplineCurve3( points ) { console.warn( "THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function Spline( points ) { console.warn( "THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } Spline.prototype = Object.create( CatmullRomCurve3.prototype ); Object.assign( Spline.prototype, { initFromArray: function ( /* a */ ) { console.error( "THREE.Spline: .initFromArray() has been removed." ); }, getControlPointsArray: function ( /* optionalTarget */ ) { console.error( "THREE.Spline: .getControlPointsArray() has been removed." ); }, reparametrizeByArcLength: function ( /* samplingCoef */ ) { console.error( "THREE.Spline: .reparametrizeByArcLength() has been removed." ); } } ); // function AxisHelper( size ) { console.warn( "THREE.AxisHelper has been renamed to THREE.AxesHelper." ); return new AxesHelper( size ); } function BoundingBoxHelper( object, color ) { console.warn( "THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead." ); return new BoxHelper( object, color ); } function EdgesHelper( object, hex ) { console.warn( "THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead." ); return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } GridHelper.prototype.setColors = function () { console.error( "THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead." ); }; SkeletonHelper.prototype.update = function () { console.error( "THREE.SkeletonHelper: update() no longer needs to be called." ); }; function WireframeHelper( object, hex ) { console.warn( "THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead." ); return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } // Object.assign( Loader.prototype, { extractUrlBase: function ( url ) { console.warn( "THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead." ); return LoaderUtils.extractUrlBase( url ); } } ); function XHRLoader( manager ) { console.warn( "THREE.XHRLoader has been renamed to THREE.FileLoader." ); return new FileLoader( manager ); } function BinaryTextureLoader( manager ) { console.warn( "THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader." ); return new DataTextureLoader( manager ); } // Object.assign( Box2.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box2: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box2: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box2: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Object.assign( Box3.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box3: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box3: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Line3.prototype.center = function ( optionalTarget ) { console.warn( "THREE.Line3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }; Object.assign( _Math, { random16: function () { console.warn( "THREE.Math: .random16() has been deprecated. Use Math.random() instead." ); return Math.random(); }, nearestPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo()." ); return _Math.floorPowerOfTwo( value ); }, nextPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo()." ); return _Math.ceilPowerOfTwo( value ); } } ); Object.assign( Matrix3.prototype, { flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead." ); return vector.applyMatrix3( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix3: .multiplyVector3Array() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix3: .applyToVector3Array() has been removed." ); } } ); Object.assign( Matrix4.prototype, { extractPosition: function ( m ) { console.warn( "THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()." ); return this.copyPosition( m ); }, flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, getPosition: function () { var v1; return function getPosition() { if ( v1 === undefined ) v1 = new Vector3(); console.warn( "THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead." ); return v1.setFromMatrixColumn( this, 3 ); }; }(), setRotationFromQuaternion: function ( q ) { console.warn( "THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()." ); return this.makeRotationFromQuaternion( q ); }, multiplyToArray: function () { console.warn( "THREE.Matrix4: .multiplyToArray() has been removed." ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector4: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix4: .multiplyVector3Array() has been removed." ); }, rotateAxis: function ( v ) { console.warn( "THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead." ); v.transformDirection( this ); }, crossVector: function ( vector ) { console.warn( "THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, translate: function () { console.error( "THREE.Matrix4: .translate() has been removed." ); }, rotateX: function () { console.error( "THREE.Matrix4: .rotateX() has been removed." ); }, rotateY: function () { console.error( "THREE.Matrix4: .rotateY() has been removed." ); }, rotateZ: function () { console.error( "THREE.Matrix4: .rotateZ() has been removed." ); }, rotateByAxis: function () { console.error( "THREE.Matrix4: .rotateByAxis() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix4: .applyToVector3Array() has been removed." ); }, makeFrustum: function ( left, right, bottom, top, near, far ) { console.warn( "THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead." ); return this.makePerspective( left, right, top, bottom, near, far ); } } ); Plane.prototype.isIntersectionLine = function ( line ) { console.warn( "THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()." ); return this.intersectsLine( line ); }; Quaternion.prototype.multiplyVector3 = function ( vector ) { console.warn( "THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead." ); return vector.applyQuaternion( this ); }; Object.assign( Ray.prototype, { isIntersectionBox: function ( box ) { console.warn( "THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionPlane: function ( plane ) { console.warn( "THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()." ); return this.intersectsPlane( plane ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); } } ); Object.assign( Shape.prototype, { extractAllPoints: function ( divisions ) { console.warn( "THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead." ); return this.extractPoints( divisions ); }, extrude: function ( options ) { console.warn( "THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead." ); return new ExtrudeGeometry( this, options ); }, makeGeometry: function ( options ) { console.warn( "THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead." ); return new ShapeGeometry( this, options ); } } ); Object.assign( Vector2.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector3.prototype, { setEulerFromRotationMatrix: function () { console.error( "THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead." ); }, setEulerFromQuaternion: function () { console.error( "THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead." ); }, getPositionFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()." ); return this.setFromMatrixPosition( m ); }, getScaleFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()." ); return this.setFromMatrixScale( m ); }, getColumnFromMatrix: function ( index, matrix ) { console.warn( "THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()." ); return this.setFromMatrixColumn( matrix, index ); }, applyProjection: function ( m ) { console.warn( "THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead." ); return this.applyMatrix4( m ); }, fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector4.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, lengthManhattan: function () { console.warn( "THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); // Geometry.prototype.computeTangents = function () { console.warn( "THREE.Geometry: .computeTangents() has been removed." ); }; Object.assign( Object3D.prototype, { getChildByName: function ( name ) { console.warn( "THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()." ); return this.getObjectByName( name ); }, renderDepth: function () { console.warn( "THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead." ); }, translate: function ( distance, axis ) { console.warn( "THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead." ); return this.translateOnAxis( axis, distance ); } } ); Object.defineProperties( Object3D.prototype, { eulerOrder: { get: function () { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); return this.rotation.order; }, set: function ( value ) { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); this.rotation.order = value; } }, 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." ); } } } ); Object.defineProperties( LOD.prototype, { objects: { get: function () { console.warn( "THREE.LOD: .objects has been renamed to .levels." ); return this.levels; } } } ); Object.defineProperty( Skeleton.prototype, "useVertexTexture", { get: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); }, set: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); } } ); Object.defineProperty( Curve.prototype, "__arcLengthDivisions", { get: function () { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); return this.arcLengthDivisions; }, set: function ( value ) { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); this.arcLengthDivisions = value; } } ); // PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup." ); if ( filmGauge !== undefined ) this.filmGauge = filmGauge; this.setFocalLength( focalLength ); }; // Object.defineProperties( Light.prototype, { onlyShadow: { set: function () { console.warn( "THREE.Light: .onlyShadow has been removed." ); } }, shadowCameraFov: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFov is now .shadow.camera.fov." ); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraLeft is now .shadow.camera.left." ); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraRight is now .shadow.camera.right." ); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraTop is now .shadow.camera.top." ); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom." ); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraNear is now .shadow.camera.near." ); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFar is now .shadow.camera.far." ); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn( "THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead." ); } }, shadowBias: { set: function ( value ) { console.warn( "THREE.Light: .shadowBias is now .shadow.bias." ); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn( "THREE.Light: .shadowDarkness has been removed." ); } }, shadowMapWidth: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapWidth is now .shadow.mapSize.width." ); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapHeight is now .shadow.mapSize.height." ); this.shadow.mapSize.height = value; } } } ); // Object.defineProperties( BufferAttribute.prototype, { length: { get: function () { console.warn( "THREE.BufferAttribute: .length has been deprecated. Use .count instead." ); return this.array.length; } } } ); Object.assign( BufferGeometry.prototype, { addIndex: function ( index ) { console.warn( "THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()." ); this.setIndex( index ); }, addDrawCall: function ( start, count, indexOffset ) { if ( indexOffset !== undefined ) { console.warn( "THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset." ); } console.warn( "THREE.BufferGeometry: .addDrawCall() is now .addGroup()." ); this.addGroup( start, count ); }, clearDrawCalls: function () { console.warn( "THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()." ); this.clearGroups(); }, computeTangents: function () { console.warn( "THREE.BufferGeometry: .computeTangents() has been removed." ); }, computeOffsets: function () { console.warn( "THREE.BufferGeometry: .computeOffsets() has been removed." ); } } ); Object.defineProperties( BufferGeometry.prototype, { drawcalls: { get: function () { console.error( "THREE.BufferGeometry: .drawcalls has been renamed to .groups." ); return this.groups; } }, offsets: { get: function () { console.warn( "THREE.BufferGeometry: .offsets has been renamed to .groups." ); return this.groups; } } } ); // Object.defineProperties( Uniform.prototype, { dynamic: { set: function () { console.warn( "THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead." ); } }, onUpdate: { value: function () { console.warn( "THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead." ); return this; } } } ); // Object.defineProperties( Material.prototype, { wrapAround: { get: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); }, set: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); } }, wrapRGB: { get: function () { console.warn( "THREE.Material: .wrapRGB has been removed." ); return new Color(); } }, shading: { get: function () { console.error( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); }, set: function ( value ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( value === FlatShading ); } } } ); Object.defineProperties( MeshPhongMaterial.prototype, { metal: { get: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead." ); return false; }, set: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead" ); } } } ); Object.defineProperties( ShaderMaterial.prototype, { derivatives: { get: function () { console.warn( "THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); return this.extensions.derivatives; }, set: function ( value ) { console.warn( "THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); this.extensions.derivatives = value; } } } ); // Object.assign( WebGLRenderer.prototype, { getCurrentRenderTarget: function () { console.warn( "THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()." ); return this.getRenderTarget(); }, getMaxAnisotropy: function () { console.warn( "THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()." ); return this.capabilities.getMaxAnisotropy(); }, getPrecision: function () { console.warn( "THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision." ); return this.capabilities.precision; }, resetGLState: function () { console.warn( "THREE.WebGLRenderer: .resetGLState() is now .state.reset()." ); return this.state.reset(); }, supportsFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )." ); return this.extensions.get( "OES_texture_float" ); }, supportsHalfFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )." ); return this.extensions.get( "OES_texture_half_float" ); }, supportsStandardDerivatives: function () { console.warn( "THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )." ); return this.extensions.get( "OES_standard_derivatives" ); }, supportsCompressedTextureS3TC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )." ); return this.extensions.get( "WEBGL_compressed_texture_s3tc" ); }, supportsCompressedTexturePVRTC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )." ); return this.extensions.get( "WEBGL_compressed_texture_pvrtc" ); }, supportsBlendMinMax: function () { console.warn( "THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )." ); return this.extensions.get( "EXT_blend_minmax" ); }, supportsVertexTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures." ); return this.capabilities.vertexTextures; }, supportsInstancedArrays: function () { console.warn( "THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )." ); return this.extensions.get( "ANGLE_instanced_arrays" ); }, enableScissorTest: function ( boolean ) { console.warn( "THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()." ); this.setScissorTest( boolean ); }, initMaterial: function () { console.warn( "THREE.WebGLRenderer: .initMaterial() has been removed." ); }, addPrePlugin: function () { console.warn( "THREE.WebGLRenderer: .addPrePlugin() has been removed." ); }, addPostPlugin: function () { console.warn( "THREE.WebGLRenderer: .addPostPlugin() has been removed." ); }, updateShadowMap: function () { console.warn( "THREE.WebGLRenderer: .updateShadowMap() has been removed." ); } } ); Object.defineProperties( WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled." ); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type." ); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { return this.shadowMap.cullFace; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace." ); this.shadowMap.cullFace = value; } } } ); Object.defineProperties( WebGLShadowMap.prototype, { cullFace: { get: function () { return this.renderReverseSided ? CullFaceFront : CullFaceBack; }, set: function ( cullFace ) { var value = ( cullFace !== CullFaceBack ); console.warn( "WebGLRenderer: .shadowMap.cullFace is deprecated. Set .shadowMap.renderReverseSided to " + value + "." ); this.renderReverseSided = value; } } } ); // Object.defineProperties( WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); return this.texture.wrapS; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); return this.texture.wrapT; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); return this.texture.magFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); return this.texture.minFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); return this.texture.anisotropy; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); return this.texture.offset; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); this.texture.offset = value; } }, repeat: { get: function () { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); return this.texture.repeat; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); this.texture.repeat = value; } }, format: { get: function () { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); return this.texture.format; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); this.texture.format = value; } }, type: { get: function () { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); return this.texture.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); return this.texture.generateMipmaps; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); this.texture.generateMipmaps = value; } } } ); // Object.assign( WebVRManager.prototype, { getStandingMatrix: function () { console.warn( "THREE.WebVRManager: .getStandingMatrix() has been removed." ); } } ); Object.defineProperties( WebVRManager.prototype, { standing: { set: function ( /* value */ ) { console.warn( "THREE.WebVRManager: .standing has been removed." ); } } } ); // Audio.prototype.load = function ( file ) { console.warn( "THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead." ); var scope = this; var audioLoader = new AudioLoader(); audioLoader.load( file, function ( buffer ) { scope.setBuffer( buffer ); } ); return this; }; AudioAnalyser.prototype.getData = function () { console.warn( "THREE.AudioAnalyser: .getData() is now .getFrequencyData()." ); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { console.warn( "THREE.CubeCamera: .updateCubeMap() is now .update()." ); return this.update( renderer, scene ); }; // var GeometryUtils = { merge: function ( geometry1, geometry2, materialIndexOffset ) { console.warn( "THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead." ); var matrix; if ( geometry2.isMesh ) { geometry2.matrixAutoUpdate && geometry2.updateMatrix(); matrix = geometry2.matrix; geometry2 = geometry2.geometry; } geometry1.merge( geometry2, matrix, materialIndexOffset ); }, center: function ( geometry ) { console.warn( "THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead." ); return geometry.center(); } }; var ImageUtils = { crossOrigin: undefined, loadTexture: function ( url, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead." ); var loader = new TextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( url, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadTextureCube: function ( urls, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead." ); var loader = new CubeTextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( urls, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadCompressedTexture: function () { console.error( "THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead." ); }, loadCompressedTextureCube: function () { console.error( "THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead." ); } }; // function Projector() { console.error( "THREE.Projector has been moved to /examples/js/renderers/Projector.js." ); this.projectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .projectVector() is now vector.project()." ); vector.project( camera ); }; this.unprojectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .unprojectVector() is now vector.unproject()." ); vector.unproject( camera ); }; this.pickingRay = function () { console.error( "THREE.Projector: .pickingRay() is now raycaster.setFromCamera()." ); }; } // function CanvasRenderer() { console.error( "THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js" ); this.domElement = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); this.clear = function () {}; this.render = function () {}; this.setClearColor = function () {}; this.setSize = function () {}; } exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.ShaderLib = ShaderLib; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.ShaderChunk = ShaderChunk; exports.FogExp2 = FogExp2; exports.Fog = Fog; exports.Scene = Scene; exports.LensFlare = LensFlare; exports.Sprite = Sprite; exports.LOD = LOD; exports.SkinnedMesh = SkinnedMesh; exports.Skeleton = Skeleton; exports.Bone = Bone; exports.Mesh = Mesh; exports.LineSegments = LineSegments; exports.LineLoop = LineLoop; exports.Line = Line; exports.Points = Points; exports.Group = Group; exports.VideoTexture = VideoTexture; exports.DataTexture = DataTexture; exports.CompressedTexture = CompressedTexture; exports.CubeTexture = CubeTexture; exports.CanvasTexture = CanvasTexture; exports.DepthTexture = DepthTexture; exports.Texture = Texture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.DataTextureLoader = DataTextureLoader; exports.CubeTextureLoader = CubeTextureLoader; exports.TextureLoader = TextureLoader; exports.ObjectLoader = ObjectLoader; exports.MaterialLoader = MaterialLoader; exports.BufferGeometryLoader = BufferGeometryLoader; exports.DefaultLoadingManager = DefaultLoadingManager; exports.LoadingManager = LoadingManager; exports.JSONLoader = JSONLoader; exports.ImageLoader = ImageLoader; exports.ImageBitmapLoader = ImageBitmapLoader; exports.FontLoader = FontLoader; exports.FileLoader = FileLoader; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.Cache = Cache; exports.AudioLoader = AudioLoader; exports.SpotLightShadow = SpotLightShadow; exports.SpotLight = SpotLight; exports.PointLight = PointLight; exports.RectAreaLight = RectAreaLight; exports.HemisphereLight = HemisphereLight; exports.DirectionalLightShadow = DirectionalLightShadow; exports.DirectionalLight = DirectionalLight; exports.AmbientLight = AmbientLight; exports.LightShadow = LightShadow; exports.Light = Light; exports.StereoCamera = StereoCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.OrthographicCamera = OrthographicCamera; exports.CubeCamera = CubeCamera; exports.ArrayCamera = ArrayCamera; exports.Camera = Camera; exports.AudioListener = AudioListener; exports.PositionalAudio = PositionalAudio; exports.AudioContext = AudioContext; exports.AudioAnalyser = AudioAnalyser; exports.Audio = Audio; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.StringKeyframeTrack = StringKeyframeTrack; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.PropertyMixer = PropertyMixer; exports.PropertyBinding = PropertyBinding; exports.KeyframeTrack = KeyframeTrack; exports.AnimationUtils = AnimationUtils; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationMixer = AnimationMixer; exports.AnimationClip = AnimationClip; exports.Uniform = Uniform; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.BufferGeometry = BufferGeometry; exports.Geometry = Geometry; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InterleavedBuffer = InterleavedBuffer; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.Face3 = Face3; exports.Object3D = Object3D; exports.Raycaster = Raycaster; exports.Layers = Layers; exports.EventDispatcher = EventDispatcher; exports.Clock = Clock; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.LinearInterpolant = LinearInterpolant; exports.DiscreteInterpolant = DiscreteInterpolant; exports.CubicInterpolant = CubicInterpolant; exports.Interpolant = Interpolant; exports.Triangle = Triangle; exports.Math = _Math; exports.Spherical = Spherical; exports.Cylindrical = Cylindrical; exports.Plane = Plane; exports.Frustum = Frustum; exports.Sphere = Sphere; exports.Ray = Ray; exports.Matrix4 = Matrix4; exports.Matrix3 = Matrix3; exports.Box3 = Box3; exports.Box2 = Box2; exports.Line3 = Line3; exports.Euler = Euler; exports.Vector4 = Vector4; exports.Vector3 = Vector3; exports.Vector2 = Vector2; exports.Quaternion = Quaternion; exports.Color = Color; exports.ImmediateRenderObject = ImmediateRenderObject; exports.VertexNormalsHelper = VertexNormalsHelper; exports.SpotLightHelper = SpotLightHelper; exports.SkeletonHelper = SkeletonHelper; exports.PointLightHelper = PointLightHelper; exports.RectAreaLightHelper = RectAreaLightHelper; exports.HemisphereLightHelper = HemisphereLightHelper; exports.GridHelper = GridHelper; exports.PolarGridHelper = PolarGridHelper; exports.FaceNormalsHelper = FaceNormalsHelper; exports.DirectionalLightHelper = DirectionalLightHelper; exports.CameraHelper = CameraHelper; exports.BoxHelper = BoxHelper; exports.Box3Helper = Box3Helper; exports.PlaneHelper = PlaneHelper; exports.ArrowHelper = ArrowHelper; exports.AxesHelper = AxesHelper; exports.Shape = Shape; exports.Path = Path; exports.ShapePath = ShapePath; exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; exports.ShapeUtils = ShapeUtils; exports.SceneUtils = SceneUtils; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.ParametricGeometry = ParametricGeometry; exports.ParametricBufferGeometry = ParametricBufferGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OctahedronBufferGeometry = OctahedronBufferGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; exports.TubeGeometry = TubeGeometry; exports.TubeBufferGeometry = TubeBufferGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusBufferGeometry = TorusBufferGeometry; exports.TextGeometry = TextGeometry; exports.TextBufferGeometry = TextBufferGeometry; exports.SphereGeometry = SphereGeometry; exports.SphereBufferGeometry = SphereBufferGeometry; exports.RingGeometry = RingGeometry; exports.RingBufferGeometry = RingBufferGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneBufferGeometry = PlaneBufferGeometry; exports.LatheGeometry = LatheGeometry; exports.LatheBufferGeometry = LatheBufferGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapeBufferGeometry = ShapeBufferGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry; exports.EdgesGeometry = EdgesGeometry; exports.ConeGeometry = ConeGeometry; exports.ConeBufferGeometry = ConeBufferGeometry; exports.CylinderGeometry = CylinderGeometry; exports.CylinderBufferGeometry = CylinderBufferGeometry; exports.CircleGeometry = CircleGeometry; exports.CircleBufferGeometry = CircleBufferGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.ShadowMaterial = ShadowMaterial; exports.SpriteMaterial = SpriteMaterial; exports.RawShaderMaterial = RawShaderMaterial; exports.ShaderMaterial = ShaderMaterial; exports.PointsMaterial = PointsMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshBasicMaterial = MeshBasicMaterial; exports.LineDashedMaterial = LineDashedMaterial; exports.LineBasicMaterial = LineBasicMaterial; exports.Material = Material; exports.Float64BufferAttribute = Float64BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.BufferAttribute = BufferAttribute; exports.ArcCurve = ArcCurve; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.EllipseCurve = EllipseCurve; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.SplineCurve = SplineCurve; exports.REVISION = REVISION; exports.MOUSE = MOUSE; exports.CullFaceNone = CullFaceNone; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.FrontFaceDirectionCW = FrontFaceDirectionCW; exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; exports.BasicShadowMap = BasicShadowMap; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.FrontSide = FrontSide; exports.BackSide = BackSide; exports.DoubleSide = DoubleSide; exports.FlatShading = FlatShading; exports.SmoothShading = SmoothShading; exports.NoColors = NoColors; exports.FaceColors = FaceColors; exports.VertexColors = VertexColors; exports.NoBlending = NoBlending; exports.NormalBlending = NormalBlending; exports.AdditiveBlending = AdditiveBlending; exports.SubtractiveBlending = SubtractiveBlending; exports.MultiplyBlending = MultiplyBlending; exports.CustomBlending = CustomBlending; exports.AddEquation = AddEquation; exports.SubtractEquation = SubtractEquation; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.MinEquation = MinEquation; exports.MaxEquation = MaxEquation; exports.ZeroFactor = ZeroFactor; exports.OneFactor = OneFactor; exports.SrcColorFactor = SrcColorFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.SrcAlphaFactor = SrcAlphaFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.DstAlphaFactor = DstAlphaFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.NeverDepth = NeverDepth; exports.AlwaysDepth = AlwaysDepth; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.EqualDepth = EqualDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterDepth = GreaterDepth; exports.NotEqualDepth = NotEqualDepth; exports.MultiplyOperation = MultiplyOperation; exports.MixOperation = MixOperation; exports.AddOperation = AddOperation; exports.NoToneMapping = NoToneMapping; exports.LinearToneMapping = LinearToneMapping; exports.ReinhardToneMapping = ReinhardToneMapping; exports.Uncharted2ToneMapping = Uncharted2ToneMapping; exports.CineonToneMapping = CineonToneMapping; exports.UVMapping = UVMapping; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.SphericalReflectionMapping = SphericalReflectionMapping; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.RepeatWrapping = RepeatWrapping; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.NearestFilter = NearestFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.LinearFilter = LinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.UnsignedByteType = UnsignedByteType; exports.ByteType = ByteType; exports.ShortType = ShortType; exports.UnsignedShortType = UnsignedShortType; exports.IntType = IntType; exports.UnsignedIntType = UnsignedIntType; exports.FloatType = FloatType; exports.HalfFloatType = HalfFloatType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShort565Type = UnsignedShort565Type; exports.UnsignedInt248Type = UnsignedInt248Type; exports.AlphaFormat = AlphaFormat; exports.RGBFormat = RGBFormat; exports.RGBAFormat = RGBAFormat; exports.LuminanceFormat = LuminanceFormat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.RGBEFormat = RGBEFormat; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.LoopOnce = LoopOnce; exports.LoopRepeat = LoopRepeat; exports.LoopPingPong = LoopPingPong; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.WrapAroundEnding = WrapAroundEnding; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.LinearEncoding = LinearEncoding; exports.sRGBEncoding = sRGBEncoding; exports.GammaEncoding = GammaEncoding; exports.RGBEEncoding = RGBEEncoding; exports.LogLuvEncoding = LogLuvEncoding; exports.RGBM7Encoding = RGBM7Encoding; exports.RGBM16Encoding = RGBM16Encoding; exports.RGBDEncoding = RGBDEncoding; exports.BasicDepthPacking = BasicDepthPacking; exports.RGBADepthPacking = RGBADepthPacking; exports.CubeGeometry = BoxGeometry; exports.Face4 = Face4; exports.LineStrip = LineStrip; exports.LinePieces = LinePieces; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MultiMaterial = MultiMaterial; exports.PointCloud = PointCloud; exports.Particle = Particle; exports.ParticleSystem = ParticleSystem; exports.PointCloudMaterial = PointCloudMaterial; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Vertex = Vertex; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Int16Attribute = Int16Attribute; exports.Uint16Attribute = Uint16Attribute; exports.Int32Attribute = Int32Attribute; exports.Uint32Attribute = Uint32Attribute; exports.Float32Attribute = Float32Attribute; exports.Float64Attribute = Float64Attribute; exports.ClosedSplineCurve3 = ClosedSplineCurve3; exports.SplineCurve3 = SplineCurve3; exports.Spline = Spline; exports.AxisHelper = AxisHelper; exports.BoundingBoxHelper = BoundingBoxHelper; exports.EdgesHelper = EdgesHelper; exports.WireframeHelper = WireframeHelper; exports.XHRLoader = XHRLoader; exports.BinaryTextureLoader = BinaryTextureLoader; exports.GeometryUtils = GeometryUtils; exports.ImageUtils = ImageUtils; exports.Projector = Projector; exports.CanvasRenderer = CanvasRenderer; Object.defineProperty(exports, "__esModule", { value: true }); }))); module.exports = exports; /* */}),null);
-----
BaseProgressRing.react",["BaseLoadingStateElement.react","CometProgressRingUtils","performanceNavigationStart","react","recoverableViolation","useBaseProgressRingSvg.react"],(function(a,b,c,d,e,f,g){"use strict";var h=d("react");b=d("react");var i=b.useCallback,j=b.useEffect,k=b.useLayoutEffect,l=b.useRef,m=b.useState,n={rootDiv:{position:"x1n2onr6",$$css:!0}},o=d("CometProgressRingUtils").getCubicBezierPercentageFunc(.45,0,.25,.85);function a(a){var b=a.backgroundColor,d=a.beginningPercentage,e=a.duration,f=a.explicitStartTime,g=a.foregroundColor,r=a.onComplete,s=a.pause,t=a.percentage,u=a.size,v=a.type;v!=="default"&&t!=null&&c("recoverableViolation")("percentage should only be applied to default type","comet_ui");var w=l(null),x=l(null);a=m(!0);var y=a[0],z=a[1];a=p(v,t,d);d=a.completedPercentage;var A=a.finalPercentage;a=a.startPercentage;var B=l(a),C=l(A),D=l(d);a=c("useBaseProgressRingSvg.react")({backgroundColor:b,foregroundColor:g,isHidden:y,size:u});var E=a.backgroundRef,F=a.leftCircleRef;A=a.progressRing;var G=a.rightCircleRef,H=i(function(a){var b=G.current,d=F.current;if(b==null||d==null)return;if(!(b instanceof SVGSVGElement)||!(d instanceof SVGSVGElement))return;if((v==="default"||v==="count-down")&&s===!0)return;x.current==null&&(x.current=f!=null?f-c("performanceNavigationStart")():a);if(v==="estimated")if(D.current>75)D.current+=.3*Math.pow((100-D.current)/50,2);else{var g=(a-x.current)/e/1e3;g=o(g);D.current=g*75}else if(v==="default"&&D.currentC.current){g=(a-x.current)/1e3/e*100;D.current=B.current-g;D.current=Math.max(D.current,0)}a=Math.max(0,Math.min(100,D.current))/100*360;b.style.transform="rotate("+Math.min(a,180)+"deg)";d.style.transform="rotate("+Math.max(a-180,0)+"deg)";q(v,D.current,t)?(window.cancelAnimationFrame(w.current),b.style.opacity="0",d.style.opacity="0",x.current=null,w.current=null,E.current instanceof SVGSVGElement&&(E.current.style.opacity="0"),r&&r()):w.current=window.requestAnimationFrame(H)},[E,e,f,F,r,s,t,G,v]);k(function(){y&&z(!y)},[y]);j(function(){if(window.requestAnimationFrame!=null){w.current=window.requestAnimationFrame(H);return function(){w.current!=null&&(x.current=null,window.cancelAnimationFrame(w.current),w.current=null)}}},[u,H]);return h.jsx(c("BaseLoadingStateElement.react"),{style:{height:u,width:u},xstyle:n.rootDiv,children:A})}a.displayName=a.name+" [from "+f.id+"]";function p(a,b,c){switch(a){case"default":return{completedPercentage:(a=c)!=null?a:0,finalPercentage:(a=b)!=null?a:100,startPercentage:(b=c)!=null?b:0};case"count-down":return{completedPercentage:100,finalPercentage:0,startPercentage:100};case"estimated":return{completedPercentage:0,finalPercentage:75,startPercentage:0};default:return{completedPercentage:0,finalPercentage:0,startPercentage:0}}}function q(a,b,c){return a==="count-down"?b<=0||b>100:b>=((a=c)!=null?a:100)}g["default"]=a}),98);
-----
three-0.116.1",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g},i;function j(){(function(b,c){typeof g==="object"&&typeof h!=="undefined"?c(g):typeof i==="function"&&i.amd?i(["exports"],c):(b=b||self,c(b.THREE={}))})(this,function(a){Number.EPSILON===void 0&&(Number.EPSILON=Math.pow(2,-52));Number.isInteger===void 0&&(Number.isInteger=function(a){return typeof a==="number"&&isFinite(a)&&Math.floor(a)===a});Math.sign===void 0&&(Math.sign=function(a){return a<0?-1:a>0?1:+a});"name"in Function.prototype===!1&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^s*functions*([^(s]*)/)[1]}});Object.assign===void 0&&(Object.assign=function(a){if(a===void 0||a===null)throw new TypeError("Cannot convert undefined or null to object");var b=Object(a);for(var c=1;c>8&255]+ad[a>>16&255]+ad[a>>24&255]+"-"+ad[b&255]+ad[b>>8&255]+"-"+ad[b>>16&15|64]+ad[b>>24&255]+"-"+ad[c&63|128]+ad[c>>8&255]+"-"+ad[c>>16&255]+ad[c>>24&255]+ad[d&255]+ad[d>>8&255]+ad[d>>16&255]+ad[d>>24&255];return a.toUpperCase()},clamp:function(a,b,c){return Math.max(b,Math.min(c,a))},euclideanModulo:function(a,b){return(a%b+b)%b},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},lerp:function(a,b,c){return(1-c)*a+c*b},smoothstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(a*6-15)+10)},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*N.DEG2RAD},radToDeg:function(a){return a*N.RAD2DEG},isPowerOfTwo:function(a){return(a&a-1)===0&&a!==0},ceilPowerOfTwo:function(a){return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))},floorPowerOfTwo:function(a){return Math.pow(2,Math.floor(Math.log(a)/Math.LN2))},setQuaternionFromProperEuler:function(a,b,c,d,e){var f=Math.cos,g=Math.sin,h=f(c/2);c=g(c/2);var i=f((b+d)/2),j=g((b+d)/2),k=f((b-d)/2),l=g((b-d)/2);f=f((d-b)/2);g=g((d-b)/2);switch(e){case"XYX":a.set(h*j,c*k,c*l,h*i);break;case"YZY":a.set(c*l,h*j,c*k,h*i);break;case"ZXZ":a.set(c*k,c*l,h*j,h*i);break;case"XZX":a.set(h*j,c*g,c*f,h*i);break;case"YXY":a.set(c*f,h*j,c*g,h*i);break;case"ZYZ":a.set(c*g,c*f,h*j,h*i);break;default:}}};function O(a,b){this.x=a||0,this.y=b||0}Object.defineProperties(O.prototype,{width:{get:function(){return this.x},set:function(a){this.x=a}},height:{get:function(){return this.y},set:function(a){this.y=a}}});Object.assign(O.prototype,{isVector2:!0,set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.x=a;this.y=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+a)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},applyMatrix3:function(a){var b=this.x,c=this.y;a=a.elements;this.x=a[0]*b+a[3]*c+a[6];this.y=a[1]*b+a[4]*c+a[7];return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(a,b){this.x=Math.max(a,Math.min(b,this.x));this.y=Math.max(a,Math.min(b,this.y));return this},clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this},roundToZero:function(){this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x);this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},cross:function(a){return this.x*a.y-this.y*a.x},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length()||1)},angle:function(){var a=Math.atan2(-this.y,-this.x)+Math.PI;return a},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x;a=this.y-a.y;return b*b+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){b===void 0&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},fromBufferAttribute:function(a,b,c){c!==void 0;this.x=a.getX(b);this.y=a.getY(b);return this},rotateAround:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=this.x-a.x,e=this.y-a.y;this.x=d*c-e*b+a.x;this.y=d*b+e*c+a.y;return this},random:function(){this.x=Math.random();this.y=Math.random();return this}});function cd(){this.elements=[1,0,0,0,1,0,0,0,1],arguments.length>0}Object.assign(cd.prototype,{isMatrix3:!0,set:function(a,b,c,d,e,f,g,h,i){var j=this.elements;j[0]=a;j[1]=d;j[2]=g;j[3]=b;j[4]=e;j[5]=h;j[6]=c;j[7]=f;j[8]=i;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},clone:function(){return new this.constructor().fromArray(this.elements)},copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return this},extractBasis:function(a,b,c){a.setFromMatrix3Column(this,0);b.setFromMatrix3Column(this,1);c.setFromMatrix3Column(this,2);return this},setFromMatrix4:function(a){a=a.elements;this.set(a[0],a[4],a[8],a[1],a[5],a[9],a[2],a[6],a[10]);return this},multiply:function(a){return this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){a=a.elements;b=b.elements;var c=this.elements,d=a[0],e=a[3],f=a[6],g=a[1],h=a[4],i=a[7],j=a[2],k=a[5];a=a[8];var l=b[0],m=b[3],n=b[6],o=b[1],p=b[4],q=b[7],r=b[2],s=b[5];b=b[8];c[0]=d*l+e*o+f*r;c[3]=d*m+e*p+f*s;c[6]=d*n+e*q+f*b;c[1]=g*l+h*o+i*r;c[4]=g*m+h*p+i*s;c[7]=g*n+h*q+i*b;c[2]=j*l+k*o+a*r;c[5]=j*m+k*p+a*s;c[8]=j*n+k*q+a*b;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7];a=a[8];return b*f*a-b*g*i-c*e*a+c*g*h+d*e*i-d*f*h},getInverse:function(a,b){b!==void 0;b=a.elements;a=this.elements;var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7];b=b[8];var k=b*g-h*j,l=h*i-b*f,m=j*f-g*i,n=c*k+d*l+e*m;if(n===0)return this.set(0,0,0,0,0,0,0,0,0);n=1/n;a[0]=k*n;a[1]=(e*j-b*d)*n;a[2]=(h*d-e*g)*n;a[3]=l*n;a[4]=(b*c-e*i)*n;a[5]=(e*f-h*c)*n;a[6]=m*n;a[7]=(d*i-j*c)*n;a[8]=(g*c-d*f)*n;return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},getNormalMatrix:function(a){return this.setFromMatrix4(a).getInverse(this).transpose()},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},setUvTransform:function(a,b,c,d,e,f,g){var h=Math.cos(e);e=Math.sin(e);this.set(c*h,c*e,-c*(h*f+e*g)+f+a,-d*e,d*h,-d*(-e*f+h*g)+g+b,0,0,1)},scale:function(a,b){var c=this.elements;c[0]*=a;c[3]*=a;c[6]*=a;c[1]*=b;c[4]*=b;c[7]*=b;return this},rotate:function(a){var b=Math.cos(a);a=Math.sin(a);var c=this.elements,d=c[0],e=c[3],f=c[6],g=c[1],h=c[4],i=c[7];c[0]=b*d+a*g;c[3]=b*e+a*h;c[6]=b*f+a*i;c[1]=-a*d+b*g;c[4]=-a*e+b*h;c[7]=-a*f+b*i;return this},translate:function(a,b){var c=this.elements;c[0]+=a*c[2];c[3]+=a*c[5];c[6]+=a*c[8];c[1]+=b*c[2];c[4]+=b*c[5];c[7]+=b*c[8];return this},equals:function(b){var c=this.elements;b=b.elements;for(var a=0;a<9;a++)if(c[a]!==b[a])return!1;return!0},fromArray:function(b,c){c===void 0&&(c=0);for(var a=0;a<9;a++)this.elements[a]=b[a+c];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a}});var dd,ed={getDataURL:function(a){var b;if(typeof HTMLCanvasElement=="undefined")return a.src;else if(a instanceof HTMLCanvasElement)b=a;else{dd===void 0&&(dd=document.createElementNS("http://www.w3.org/1999/xhtml","canvas"));dd.width=a.width;dd.height=a.height;var c=dd.getContext("2d");a instanceof ImageData?c.putImageData(a,0,0):c.drawImage(a,0,0,a.width,a.height);b=dd}if(b.width>2048||b.height>2048)return b.toDataURL("image/jpeg",.6);else return b.toDataURL("image/png")}},fd=0;function gd(a,b,c,d,e,f,g,h,i,j){Object.defineProperty(this,"id",{value:fd++}),this.uuid=N.generateUUID(),this.name="",this.image=a!==void 0?a:gd.DEFAULT_IMAGE,this.mipmaps=[],this.mapping=b!==void 0?b:gd.DEFAULT_MAPPING,this.wrapS=c!==void 0?c:Da,this.wrapT=d!==void 0?d:Da,this.magFilter=e!==void 0?e:Ka,this.minFilter=f!==void 0?f:Na,this.anisotropy=i!==void 0?i:1,this.format=g!==void 0?g:cb,this.internalFormat=null,this.type=h!==void 0?h:Pa,this.offset=new O(0,0),this.repeat=new O(1,1),this.center=new O(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new cd(),this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.encoding=j!==void 0?j:pc,this.version=0,this.onUpdate=null}gd.DEFAULT_IMAGE=void 0;gd.DEFAULT_MAPPING=ua;gd.prototype=Object.assign(Object.create($c.prototype),{constructor:gd,isTexture:!0,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)},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.name=a.name;this.image=a.image;this.mipmaps=a.mipmaps.slice(0);this.mapping=a.mapping;this.wrapS=a.wrapS;this.wrapT=a.wrapT;this.magFilter=a.magFilter;this.minFilter=a.minFilter;this.anisotropy=a.anisotropy;this.format=a.format;this.internalFormat=a.internalFormat;this.type=a.type;this.offset.copy(a.offset);this.repeat.copy(a.repeat);this.center.copy(a.center);this.rotation=a.rotation;this.matrixAutoUpdate=a.matrixAutoUpdate;this.matrix.copy(a.matrix);this.generateMipmaps=a.generateMipmaps;this.premultiplyAlpha=a.premultiplyAlpha;this.flipY=a.flipY;this.unpackAlignment=a.unpackAlignment;this.encoding=a.encoding;return this},toJSON:function(b){var c=b===void 0||typeof b==="string";if(!c&&b.textures[this.uuid]!==void 0)return b.textures[this.uuid];var d={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(this.image!==void 0){var e=this.image;e.uuid===void 0&&(e.uuid=N.generateUUID());if(!c&&b.images[e.uuid]===void 0){var f;if(Array.isArray(e)){f=[];for(var a=0,g=e.length;a1)switch(this.wrapS){case Ca:a.x=a.x-Math.floor(a.x);break;case Da:a.x=a.x<0?0:1;break;case Ea:Math.abs(Math.floor(a.x)%2)===1?a.x=Math.ceil(a.x)-a.x:a.x=a.x-Math.floor(a.x);break}if(a.y<0||a.y>1)switch(this.wrapT){case Ca:a.y=a.y-Math.floor(a.y);break;case Da:a.y=a.y<0?0:1;break;case Ea:Math.abs(Math.floor(a.y)%2)===1?a.y=Math.ceil(a.y)-a.y:a.y=a.y-Math.floor(a.y);break}this.flipY&&(a.y=1-a.y);return a}});Object.defineProperty(gd.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});function hd(a,b,c,d){this.x=a||0,this.y=b||0,this.z=c||0,this.w=d!==void 0?d:1}Object.defineProperties(hd.prototype,{width:{get:function(){return this.z},set:function(a){this.z=a}},height:{get:function(){return this.w},set:function(a){this.w=a}}});Object.assign(hd.prototype,{isVector4:!0,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setScalar:function(a){this.x=a;this.y=a;this.z=a;this.w=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){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: "+a)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=a.w!==void 0?a.w:1;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;this.w+=a.w*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;this.w-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);b<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){var b,c,d,e=.01,f=.1;a=a.elements;var g=a[0],h=a[4],i=a[8],j=a[1],k=a[5],l=a[9],m=a[2],n=a[6];a=a[10];if(Math.abs(h-j)p&&o>q?oq?p=0?1:-1,o=1-m*m;if(o>Number.EPSILON){o=Math.sqrt(o);m=Math.atan2(o,m*n);f=Math.sin(f*m)/o;g=Math.sin(g*m)/o}m=g*n;h=h*f+d*m;i=i*f+k*m;j=j*f+l*m;c=c*f+e*m;if(f===1-g){o=1/Math.sqrt(h*h+i*i+j*j+c*c);h*=o;i*=o;j*=o;c*=o}}a[b]=h;a[b+1]=i;a[b+2]=j;a[b+3]=c},multiplyQuaternionsFlat:function(a,b,c,d,e,f){var g=c[d],h=c[d+1],i=c[d+2];c=c[d+3];d=e[f];var j=e[f+1],k=e[f+2];e=e[f+3];a[b]=g*e+c*d+h*k-i*j;a[b+1]=h*e+c*j+i*d-g*k;a[b+2]=i*e+c*k+g*j-h*d;a[b+3]=c*e-g*d-h*j-i*k;return a}});Object.defineProperties(kd.prototype,{x:{get:function(){return this._x},set:function(a){this._x=a,this._onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a,this._onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a,this._onChangeCallback()}},w:{get:function(){return this._w},set:function(a){this._w=a,this._onChangeCallback()}}});Object.assign(kd.prototype,{isQuaternion:!0,set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this._onChangeCallback();return this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;this._w=a.w;this._onChangeCallback();return this},setFromEuler:function(a,b){if(!(a&&a.isEuler))throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");var c=a._x,d=a._y,e=a._z;a=a.order;var f=Math.cos,g=Math.sin,h=f(c/2),i=f(d/2);f=f(e/2);c=g(c/2);d=g(d/2);g=g(e/2);switch(a){case"XYZ":this._x=c*i*f+h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f-c*d*g;break;case"YXZ":this._x=c*i*f+h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f+c*d*g;break;case"ZXY":this._x=c*i*f-h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f-c*d*g;break;case"ZYX":this._x=c*i*f-h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f+c*d*g;break;case"YZX":this._x=c*i*f+h*d*g;this._y=h*d*f+c*i*g;this._z=h*i*g-c*d*f;this._w=h*i*f-c*d*g;break;case"XZY":this._x=c*i*f-h*d*g;this._y=h*d*f-c*i*g;this._z=h*i*g+c*d*f;this._w=h*i*f+c*d*g;break;default:}b!==!1&&this._onChangeCallback();return this},setFromAxisAngle:function(a,b){b=b/2;var c=Math.sin(b);this._x=a.x*c;this._y=a.y*c;this._z=a.z*c;this._w=Math.cos(b);this._onChangeCallback();return this},setFromRotationMatrix:function(a){a=a.elements;var b=a[0],c=a[4],d=a[8],e=a[1],f=a[5],g=a[9],h=a[2],i=a[6];a=a[10];var j=b+f+a;j>0?(j=.5/Math.sqrt(j+1),this._w=.25/j,this._x=(i-g)*j,this._y=(d-h)*j,this._z=(e-c)*j):b>f&&b>a?(j=2*Math.sqrt(1+b-f-a),this._w=(i-g)/j,this._x=.25*j,this._y=(c+e)/j,this._z=(d+h)/j):f>a?(j=2*Math.sqrt(1+f-b-a),this._w=(d-h)/j,this._x=(c+e)/j,this._y=.25*j,this._z=(g+i)/j):(j=2*Math.sqrt(1+a-b-f),this._w=(e-c)/j,this._x=(d+h)/j,this._y=(g+i)/j,this._z=.25*j);this._onChangeCallback();return this},setFromUnitVectors:function(a,b){var c=1e-6,d=a.dot(b)+1;dMath.abs(a.z)?(this._x=-a.y,this._y=a.x,this._z=0,this._w=d):(this._x=0,this._y=-a.z,this._z=a.y,this._w=d)):(this._x=a.y*b.z-a.z*b.y,this._y=a.z*b.x-a.x*b.z,this._z=a.x*b.y-a.y*b.x,this._w=d);return this.normalize()},angleTo:function(a){return 2*Math.acos(Math.abs(N.clamp(this.dot(a),-1,1)))},rotateTowards:function(a,b){var c=this.angleTo(a);if(c===0)return this;b=Math.min(1,b/c);this.slerp(a,b);return this},inverse:function(){return this.conjugate()},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this._onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();a===0?(this._x=0,this._y=0,this._z=0,this._w=1):(a=1/a,this._x=this._x*a,this._y=this._y*a,this._z=this._z*a,this._w=this._w*a);this._onChangeCallback();return this},multiply:function(a,b){return b!==void 0?this.multiplyQuaternions(a,b):this.multiplyQuaternions(this,a)},premultiply:function(a){return this.multiplyQuaternions(a,this)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z;a=a._w;var f=b._x,g=b._y,h=b._z;b=b._w;this._x=c*b+a*f+d*h-e*g;this._y=d*b+a*g+e*f-c*h;this._z=e*b+a*h+c*g-d*f;this._w=a*b-c*f-d*g-e*h;this._onChangeCallback();return this},slerp:function(a,b){if(b===0)return this;if(b===1)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;g<0?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(g>=1){this._w=f;this._x=c;this._y=d;this._z=e;return this}a=1-g*g;if(a<=Number.EPSILON){var h=1-b;this._w=h*f+b*this._w;this._x=h*c+b*this._x;this._y=h*d+b*this._y;this._z=h*e+b*this._z;this.normalize();this._onChangeCallback();return this}h=Math.sqrt(a);a=Math.atan2(h,g);g=Math.sin((1-b)*a)/h;b=Math.sin(b*a)/h;this._w=f*g+this._w*b;this._x=c*g+this._x*b;this._y=d*g+this._y*b;this._z=e*g+this._z*b;this._onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){b===void 0&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this._onChangeCallback();return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},fromBufferAttribute:function(a,b){this._x=a.getX(b);this._y=a.getY(b);this._z=a.getZ(b);this._w=a.getW(b);return this},_onChange:function(a){this._onChangeCallback=a;return this},_onChangeCallback:function(){}});var ld=new P(),md=new kd();function P(a,b,c){this.x=a||0,this.y=b||0,this.z=c||0}Object.assign(P.prototype,{isVector3:!0,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setScalar:function(a){this.x=a;this.y=a;this.z=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw new Error("index is out of range: "+a)}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+a)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(b!==void 0)return this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;return this},sub:function(a,b){if(b!==void 0)return this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(b!==void 0)return this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(a){!(a&&a.isEuler);return this.applyQuaternion(md.setFromEuler(a))},applyAxisAngle:function(a,b){return this.applyQuaternion(md.setFromAxisAngle(a,b))},applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyNormalMatrix:function(a){return this.applyMatrix3(a).normalize()},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,i=a*c+g*b-e*d,j=a*d+e*c-f*b;b=-e*b-f*c-g*d;this.x=h*a+b*-e+i*-g-j*-f;this.y=i*a+b*-f+j*-e-h*-g;this.z=j*a+b*-g+h*-f-i*-e;return this},project:function(a){return this.applyMatrix4(a.matrixWorldInverse).applyMatrix4(a.projectionMatrix)},unproject:function(a){return this.applyMatrix4(a.projectionMatrixInverse).applyMatrix4(a.matrixWorld)},transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;return this.normalize()},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));return this},clampScalar:function(a,b){this.x=Math.max(a,Math.min(b,this.x));this.y=Math.max(a,Math.min(b,this.y));this.z=Math.max(a,Math.min(b,this.z));return this},clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x);this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y);this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length()||1)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},cross:function(a,b){return b!==void 0?this.crossVectors(a,b):this.crossVectors(this,a)},crossVectors:function(a,b){var c=a.x,d=a.y;a=a.z;var e=b.x,f=b.y;b=b.z;this.x=d*b-a*f;this.y=a*e-c*b;this.z=c*f-d*e;return this},projectOnVector:function(a){var b=a.lengthSq();if(b===0)return this.set(0,0,0);b=a.dot(this)/b;return this.copy(a).multiplyScalar(b)},projectOnPlane:function(a){ld.copy(this).projectOnVector(a);return this.sub(ld)},reflect:function(a){return this.sub(ld.copy(a).multiplyScalar(2*this.dot(a)))},angleTo:function(a){var b=Math.sqrt(this.lengthSq()*a.lengthSq());if(b===0)return Math.PI/2;a=this.dot(a)/b;return Math.acos(N.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)+Math.abs(this.z-a.z)},setFromSpherical:function(a){return this.setFromSphericalCoords(a.radius,a.phi,a.theta)},setFromSphericalCoords:function(a,b,c){var d=Math.sin(b)*a;this.x=d*Math.sin(c);this.y=Math.cos(b)*a;this.z=d*Math.cos(c);return this},setFromCylindrical:function(a){return this.setFromCylindricalCoords(a.radius,a.theta,a.y)},setFromCylindricalCoords:function(a,b,c){this.x=a*Math.sin(b);this.y=c;this.z=a*Math.cos(b);return this},setFromMatrixPosition:function(a){a=a.elements;this.x=a[12];this.y=a[13];this.z=a[14];return this},setFromMatrixScale:function(a){var b=this.setFromMatrixColumn(a,0).length(),c=this.setFromMatrixColumn(a,1).length();a=this.setFromMatrixColumn(a,2).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){return this.fromArray(a.elements,b*4)},setFromMatrix3Column:function(a,b){return this.fromArray(a.elements,b*3)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){b===void 0&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromBufferAttribute:function(a,b,c){c!==void 0;this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);return this},random:function(){this.x=Math.random();this.y=Math.random();this.z=Math.random();return this}});var nd=new P(),od=new Q(),pd=new P(0,0,0),qd=new P(1,1,1),rd=new P(),sd=new P(),td=new P();function Q(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],arguments.length>0}Object.assign(Q.prototype,{isMatrix4:!0,set:function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){var q=this.elements;q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=h;q[2]=i;q[6]=j;q[10]=k;q[14]=l;q[3]=m;q[7]=n;q[11]=o;q[15]=p;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},clone:function(){return new Q().fromArray(this.elements)},copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return this},copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x,b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(a){var b=this.elements,c=a.elements,d=1/nd.setFromMatrixColumn(a,0).length(),e=1/nd.setFromMatrixColumn(a,1).length();a=1/nd.setFromMatrixColumn(a,2).length();b[0]=c[0]*d;b[1]=c[1]*d;b[2]=c[2]*d;b[3]=0;b[4]=c[4]*e;b[5]=c[5]*e;b[6]=c[6]*e;b[7]=0;b[8]=c[8]*a;b[9]=c[9]*a;b[10]=c[10]*a;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromEuler:function(a){!(a&&a.isEuler);var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c);c=Math.sin(c);var g=Math.cos(d);d=Math.sin(d);var h=Math.cos(e);e=Math.sin(e);if(a.order==="XYZ"){var i=f*h,j=f*e,k=c*h,l=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=j+k*d;b[5]=i-l*d;b[9]=-c*g;b[2]=l-i*d;b[6]=k+j*d;b[10]=f*g}else if(a.order==="YXZ"){var m=g*h,n=g*e,o=d*h,p=d*e;b[0]=m+p*c;b[4]=o*c-n;b[8]=f*d;b[1]=f*e;b[5]=f*h;b[9]=-c;b[2]=n*c-o;b[6]=p+m*c;b[10]=f*g}else if(a.order==="ZXY"){var m=g*h,n=g*e,o=d*h,p=d*e;b[0]=m-p*c;b[4]=-f*e;b[8]=o+n*c;b[1]=n+o*c;b[5]=f*h;b[9]=p-m*c;b[2]=-f*d;b[6]=c;b[10]=f*g}else if(a.order==="ZYX"){var i=f*h,j=f*e,k=c*h,l=c*e;b[0]=g*h;b[4]=k*d-j;b[8]=i*d+l;b[1]=g*e;b[5]=l*d+i;b[9]=j*d-k;b[2]=-d;b[6]=c*g;b[10]=f*g}else if(a.order==="YZX"){n=f*g;o=f*d;p=c*g;m=c*d;b[0]=g*h;b[4]=m-n*e;b[8]=p*e+o;b[1]=e;b[5]=f*h;b[9]=-c*h;b[2]=-d*h;b[6]=o*e+p;b[10]=n-m*e}else if(a.order==="XZY"){n=f*g;o=f*d;p=c*g;m=c*d;b[0]=g*h;b[4]=-e;b[8]=d*h;b[1]=n*e+m;b[5]=f*h;b[9]=o*e-p;b[2]=p*e-o;b[6]=c*h;b[10]=m*e+n}b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromQuaternion:function(a){return this.compose(pd,a,qd)},lookAt:function(a,b,c){var d=this.elements;td.subVectors(a,b);td.lengthSq()===0&&(td.z=1);td.normalize();rd.crossVectors(c,td);rd.lengthSq()===0&&(Math.abs(c.z)===1?td.x+=1e-4:td.z+=1e-4,td.normalize(),rd.crossVectors(c,td));rd.normalize();sd.crossVectors(td,rd);d[0]=rd.x;d[4]=sd.x;d[8]=td.x;d[1]=rd.y;d[5]=sd.y;d[9]=td.y;d[2]=rd.z;d[6]=sd.z;d[10]=td.z;return this},multiply:function(a,b){return b!==void 0?this.multiplyMatrices(a,b):this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){a=a.elements;b=b.elements;var c=this.elements,d=a[0],e=a[4],f=a[8],g=a[12],h=a[1],i=a[5],j=a[9],k=a[13],l=a[2],m=a[6],n=a[10],o=a[14],p=a[3],q=a[7],r=a[11];a=a[15];var s=b[0],t=b[4],u=b[8],v=b[12],w=b[1],x=b[5],y=b[9],z=b[13],A=b[2],B=b[6],C=b[10],D=b[14],E=b[3],F=b[7],aa=b[11];b=b[15];c[0]=d*s+e*w+f*A+g*E;c[4]=d*t+e*x+f*B+g*F;c[8]=d*u+e*y+f*C+g*aa;c[12]=d*v+e*z+f*D+g*b;c[1]=h*s+i*w+j*A+k*E;c[5]=h*t+i*x+j*B+k*F;c[9]=h*u+i*y+j*C+k*aa;c[13]=h*v+i*z+j*D+k*b;c[2]=l*s+m*w+n*A+o*E;c[6]=l*t+m*x+n*B+o*F;c[10]=l*u+m*y+n*C+o*aa;c[14]=l*v+m*z+n*D+o*b;c[3]=p*s+q*w+r*A+a*E;c[7]=p*t+q*x+r*B+a*F;c[11]=p*u+q*y+r*C+a*aa;c[15]=p*v+q*z+r*D+a*b;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[4],d=a[8],e=a[12],f=a[1],g=a[5],h=a[9],i=a[13],j=a[2],k=a[6],l=a[10],m=a[14],n=a[3],o=a[7],p=a[11];a=a[15];return n*(+e*h*k-d*i*k-e*g*l+c*i*l+d*g*m-c*h*m)+o*(+b*h*m-b*i*l+e*f*l-d*f*m+d*i*j-e*h*j)+p*(+b*i*k-b*g*m-e*f*k+c*f*m+e*g*j-c*i*j)+a*(-d*g*j-b*h*k+b*g*l+d*f*k-c*f*l+c*h*j)},transpose:function(){var b=this.elements,a;a=b[1];b[1]=b[4];b[4]=a;a=b[2];b[2]=b[8];b[8]=a;a=b[6];b[6]=b[9];b[9]=a;a=b[3];b[3]=b[12];b[12]=a;a=b[7];b[7]=b[13];b[13]=a;a=b[11];b[11]=b[14];b[14]=a;return this},setPosition:function(a,b,c){var d=this.elements;a.isVector3?(d[12]=a.x,d[13]=a.y,d[14]=a.z):(d[12]=a,d[13]=b,d[14]=c);return this},getInverse:function(a,b){b!==void 0;b=this.elements;a=a.elements;var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],m=a[10],n=a[11],o=a[12],p=a[13],q=a[14];a=a[15];var r=l*q*j-p*m*j+p*i*n-h*q*n-l*i*a+h*m*a,s=o*m*j-k*q*j-o*i*n+g*q*n+k*i*a-g*m*a,t=k*p*j-o*l*j+o*h*n-g*p*n-k*h*a+g*l*a,u=o*l*i-k*p*i-o*h*m+g*p*m+k*h*q-g*l*q,v=c*r+d*s+e*t+f*u;if(v===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);v=1/v;b[0]=r*v;b[1]=(p*m*f-l*q*f-p*e*n+d*q*n+l*e*a-d*m*a)*v;b[2]=(h*q*f-p*i*f+p*e*j-d*q*j-h*e*a+d*i*a)*v;b[3]=(l*i*f-h*m*f-l*e*j+d*m*j+h*e*n-d*i*n)*v;b[4]=s*v;b[5]=(k*q*f-o*m*f+o*e*n-c*q*n-k*e*a+c*m*a)*v;b[6]=(o*i*f-g*q*f-o*e*j+c*q*j+g*e*a-c*i*a)*v;b[7]=(g*m*f-k*i*f+k*e*j-c*m*j-g*e*n+c*i*n)*v;b[8]=t*v;b[9]=(o*l*f-k*p*f-o*d*n+c*p*n+k*d*a-c*l*a)*v;b[10]=(g*p*f-o*h*f+o*d*j-c*p*j-g*d*a+c*h*a)*v;b[11]=(k*h*f-g*l*f-k*d*j+c*l*j+g*d*n-c*h*n)*v;b[12]=u*v;b[13]=(k*p*e-o*l*e+o*d*m-c*p*m-k*d*q+c*l*q)*v;b[14]=(o*h*e-g*p*e-o*d*i+c*p*i+g*d*q-c*h*q)*v;b[15]=(g*l*e-k*h*e+k*d*i-c*l*i-g*d*m+c*h*m)*v;return this},scale:function(a){var b=this.elements,c=a.x,d=a.y;a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements,b=a[0]*a[0]+a[1]*a[1]+a[2]*a[2],c=a[4]*a[4]+a[5]*a[5]+a[6]*a[6];a=a[8]*a[8]+a[9]*a[9]+a[10]*a[10];return Math.sqrt(Math.max(b,c,a))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(1,0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=1-c,e=a.x,f=a.y;a=a.z;var g=d*e,h=d*f;this.set(g*e+c,g*f-b*a,g*a+b*f,0,g*f+b*a,h*f+c,h*a-b*e,0,g*a-b*f,h*a+b*e,d*a*a+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},makeShear:function(a,b,c){this.set(1,b,c,0,a,1,c,0,a,b,1,0,0,0,0,1);return this},compose:function(a,b,c){var d=this.elements,e=b._x,f=b._y,g=b._z;b=b._w;var h=e+e,i=f+f,j=g+g,k=e*h,l=e*i;e=e*j;var m=f*i;f=f*j;g=g*j;h=b*h;i=b*i;b=b*j;j=c.x;var n=c.y;c=c.z;d[0]=(1-(m+g))*j;d[1]=(l+b)*j;d[2]=(e-i)*j;d[3]=0;d[4]=(l-b)*n;d[5]=(1-(k+g))*n;d[6]=(f+h)*n;d[7]=0;d[8]=(e+i)*c;d[9]=(f-h)*c;d[10]=(1-(k+m))*c;d[11]=0;d[12]=a.x;d[13]=a.y;d[14]=a.z;d[15]=1;return this},decompose:function(a,b,c){var d=this.elements,e=nd.set(d[0],d[1],d[2]).length(),f=nd.set(d[4],d[5],d[6]).length(),g=nd.set(d[8],d[9],d[10]).length(),h=this.determinant();h<0&&(e=-e);a.x=d[12];a.y=d[13];a.z=d[14];od.copy(this);h=1/e;a=1/f;d=1/g;od.elements[0]*=h;od.elements[1]*=h;od.elements[2]*=h;od.elements[4]*=a;od.elements[5]*=a;od.elements[6]*=a;od.elements[8]*=d;od.elements[9]*=d;od.elements[10]*=d;b.setFromRotationMatrix(od);c.x=e;c.y=f;c.z=g;return this},makePerspective:function(a,b,c,d,e,f){f===void 0;var g=this.elements,h=2*e/(b-a),i=2*e/(c-d);b=(b+a)/(b-a);a=(c+d)/(c-d);c=-(f+e)/(f-e);d=-2*f*e/(f-e);g[0]=h;g[4]=0;g[8]=b;g[12]=0;g[1]=0;g[5]=i;g[9]=a;g[13]=0;g[2]=0;g[6]=0;g[10]=c;g[14]=d;g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=1/(b-a),i=1/(c-d),j=1/(f-e);b=(b+a)*h;a=(c+d)*i;c=(f+e)*j;g[0]=2*h;g[4]=0;g[8]=0;g[12]=-b;g[1]=0;g[5]=2*i;g[9]=0;g[13]=-a;g[2]=0;g[6]=0;g[10]=-2*j;g[14]=-c;g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},equals:function(b){var c=this.elements;b=b.elements;for(var a=0;a<16;a++)if(c[a]!==b[a])return!1;return!0},fromArray:function(b,c){c===void 0&&(c=0);for(var a=0;a<16;a++)this.elements[a]=b[a+c];return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a}});var ud=new Q(),vd=new kd();function wd(a,b,c,d){this._x=a||0,this._y=b||0,this._z=c||0,this._order=d||wd.DefaultOrder}wd.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];wd.DefaultOrder="XYZ";Object.defineProperties(wd.prototype,{x:{get:function(){return this._x},set:function(a){this._x=a,this._onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a,this._onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a,this._onChangeCallback()}},order:{get:function(){return this._order},set:function(a){this._order=a,this._onChangeCallback()}}});Object.assign(wd.prototype,{isEuler:!0,set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this._onChangeCallback();return this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._order=a._order;this._onChangeCallback();return this},setFromRotationMatrix:function(a,b,c){var d=N.clamp;a=a.elements;var e=a[0],f=a[4],g=a[8],h=a[1],i=a[5],j=a[9],k=a[2],l=a[6];a=a[10];b=b||this._order;switch(b){case"XYZ":this._y=Math.asin(d(g,-1,1));Math.abs(g)<.9999999?(this._x=Math.atan2(-j,a),this._z=Math.atan2(-f,e)):(this._x=Math.atan2(l,i),this._z=0);break;case"YXZ":this._x=Math.asin(-d(j,-1,1));Math.abs(j)<.9999999?(this._y=Math.atan2(g,a),this._z=Math.atan2(h,i)):(this._y=Math.atan2(-k,e),this._z=0);break;case"ZXY":this._x=Math.asin(d(l,-1,1));Math.abs(l)<.9999999?(this._y=Math.atan2(-k,a),this._z=Math.atan2(-f,i)):(this._y=0,this._z=Math.atan2(h,e));break;case"ZYX":this._y=Math.asin(-d(k,-1,1));Math.abs(k)<.9999999?(this._x=Math.atan2(l,a),this._z=Math.atan2(h,e)):(this._x=0,this._z=Math.atan2(-f,i));break;case"YZX":this._z=Math.asin(d(h,-1,1));Math.abs(h)<.9999999?(this._x=Math.atan2(-j,i),this._y=Math.atan2(-k,e)):(this._x=0,this._y=Math.atan2(g,a));break;case"XZY":this._z=Math.asin(-d(f,-1,1));Math.abs(f)<.9999999?(this._x=Math.atan2(l,i),this._y=Math.atan2(g,e)):(this._x=Math.atan2(-j,a),this._y=0);break;default:}this._order=b;c!==!1&&this._onChangeCallback();return this},setFromQuaternion:function(a,b,c){ud.makeRotationFromQuaternion(a);return this.setFromRotationMatrix(ud,b,c)},setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(a){vd.setFromEuler(this);return this.setFromQuaternion(vd,a)},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];a[3]!==void 0&&(this._order=a[3]);this._onChangeCallback();return this},toArray:function(a,b){a===void 0&&(a=[]);b===void 0&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){if(a)return a.set(this._x,this._y,this._z);else return new P(this._x,this._y,this._z)},_onChange:function(a){this._onChangeCallback=a;return this},_onChangeCallback:function(){}});function xd(){this.mask=1|0}Object.assign(xd.prototype,{set:function(a){this.mask=1<1){for(var a=0;a1){for(var a=0;a0){e.children=[];for(var a=0;a0&&(d.geometries=h);i.length>0&&(d.materials=i);f.length>0&&(d.textures=f);a.length>0&&(d.images=a);g.length>0&&(d.shapes=g)}d.object=e;return d;function j(a){var b=[];for(var c in a){var d=a[c];delete d.metadata;b.push(d)}return b}},clone:function(a){return new this.constructor().copy(this,a)},copy:function(a,b){b===void 0&&(b=!0);this.name=a.name;this.up.copy(a.up);this.position.copy(a.position);this.quaternion.copy(a.quaternion);this.scale.copy(a.scale);this.matrix.copy(a.matrix);this.matrixWorld.copy(a.matrixWorld);this.matrixAutoUpdate=a.matrixAutoUpdate;this.matrixWorldNeedsUpdate=a.matrixWorldNeedsUpdate;this.layers.mask=a.layers.mask;this.visible=a.visible;this.castShadow=a.castShadow;this.receiveShadow=a.receiveShadow;this.frustumCulled=a.frustumCulled;this.renderOrder=a.renderOrder;this.userData=JSON.parse(JSON.stringify(a.userData));if(b===!0)for(b=0;bf&&(f=j);k>g&&(g=k);l>h&&(h=l)}this.min.set(c,d,e);this.max.set(f,g,h);return this},setFromBufferAttribute:function(b){var c=+Infinity,d=+Infinity,e=+Infinity,f=-Infinity,g=-Infinity,h=-Infinity;for(var a=0,i=b.count;af&&(f=j);k>g&&(g=k);l>h&&(h=l)}this.min.set(c,d,e);this.max.set(f,g,h);return this},setFromPoints:function(b){this.makeEmpty();for(var a=0,c=b.length;athis.max.x||a.ythis.max.y||a.zthis.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z},getParameter:function(a,b){b===void 0&&(b=new P());return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y||a.max.zthis.max.z?!1:!0},intersectsSphere:function(a){this.clampPoint(a.center,Nd);return Nd.distanceToSquared(a.center)<=a.radius*a.radius},intersectsPlane:function(a){var b,c;a.normal.x>0?(b=a.normal.x*this.min.x,c=a.normal.x*this.max.x):(b=a.normal.x*this.max.x,c=a.normal.x*this.min.x);a.normal.y>0?(b+=a.normal.y*this.min.y,c+=a.normal.y*this.max.y):(b+=a.normal.y*this.max.y,c+=a.normal.y*this.min.y);a.normal.z>0?(b+=a.normal.z*this.min.z,c+=a.normal.z*this.max.z):(b+=a.normal.z*this.max.z,c+=a.normal.z*this.min.z);return b<=-a.constant&&c>=-a.constant},intersectsTriangle:function(a){if(this.isEmpty())return!1;this.getCenter(Vd);Wd.subVectors(this.max,Vd);Pd.subVectors(a.a,Vd);Qd.subVectors(a.b,Vd);Rd.subVectors(a.c,Vd);Sd.subVectors(Qd,Pd);Td.subVectors(Rd,Qd);Ud.subVectors(Pd,Rd);a=[0,-Sd.z,Sd.y,0,-Td.z,Td.y,0,-Ud.z,Ud.y,Sd.z,0,-Sd.x,Td.z,0,-Td.x,Ud.z,0,-Ud.x,-Sd.y,Sd.x,0,-Td.y,Td.x,0,-Ud.y,Ud.x,0];if(!$d(a,Pd,Qd,Rd,Wd))return!1;a=[1,0,0,0,1,0,0,0,1];if(!$d(a,Pd,Qd,Rd,Wd))return!1;Xd.crossVectors(Sd,Td);a=[Xd.x,Xd.y,Xd.z];return $d(a,Pd,Qd,Rd,Wd)},clampPoint:function(a,b){b===void 0&&(b=new P());return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(a){var b=Nd.copy(a).clamp(this.min,this.max);return b.sub(a).length()},getBoundingSphere:function(a){a===void 0;this.getCenter(a.center);a.radius=this.getSize(Nd).length()*.5;return a},intersect:function(a){this.min.max(a.min);this.max.min(a.max);this.isEmpty()&&this.makeEmpty();return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(a){if(this.isEmpty())return this;Md[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(a);Md[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(a);Md[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(a);Md[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(a);Md[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(a);Md[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(a);Md[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(a);Md[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(a);this.setFromPoints(Md);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});function $d(b,c,d,e,f){var a,g;for(a=0,g=b.length-3;a<=g;a+=3){Yd.fromArray(b,a);var h=f.x*Math.abs(Yd.x)+f.y*Math.abs(Yd.y)+f.z*Math.abs(Yd.z),i=c.dot(Yd),j=d.dot(Yd),k=e.dot(Yd);if(Math.max(-Math.max(i,j,k),Math.min(i,j,k))>h)return!1}return!0}var ae=new Zd();function be(a,b){this.center=a!==void 0?a:new P(),this.radius=b!==void 0?b:-1}Object.assign(be.prototype,{set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(b,c){var d=this.center;c!==void 0?d.copy(c):ae.setFromPoints(b).getCenter(d);c=0;for(var a=0,e=b.length;athis.radius*this.radius&&(b.sub(this.center).normalize(),b.multiplyScalar(this.radius).add(this.center));return b},getBoundingBox:function(a){a===void 0&&(a=new Zd());if(this.isEmpty()){a.makeEmpty();return a}a.set(this.center,this.center);a.expandByScalar(this.radius);return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius=this.radius*a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius}});var ce=new P(),de=new P(),ee=new P(),fe=new P(),ge=new P(),he=new P(),ie=new P();function je(a,b){this.origin=a!==void 0?a:new P(),this.direction=b!==void 0?b:new P(0,0,-1)}Object.assign(je.prototype,{set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){b===void 0&&(b=new P());return b.copy(this.direction).multiplyScalar(a).add(this.origin)},lookAt:function(a){this.direction.copy(a).sub(this.origin).normalize();return this},recast:function(a){this.origin.copy(this.at(a,ce));return this},closestPointToPoint:function(a,b){b===void 0&&(b=new P());b.subVectors(a,this.origin);a=b.dot(this.direction);return a<0?b.copy(this.origin):b.copy(this.direction).multiplyScalar(a).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))},distanceSqToPoint:function(a){var b=ce.subVectors(a,this.origin).dot(this.direction);if(b<0)return this.origin.distanceToSquared(a);ce.copy(this.direction).multiplyScalar(b).add(this.origin);return ce.distanceToSquared(a)},distanceSqToSegment:function(a,b,c,d){de.copy(a).add(b).multiplyScalar(.5);ee.copy(b).sub(a).normalize();fe.copy(this.origin).sub(de);a=a.distanceTo(b)*.5;b=-this.direction.dot(ee);var e=fe.dot(this.direction),f=-fe.dot(ee),g=fe.lengthSq(),h=Math.abs(1-b*b),i,j,k;if(h>0){i=b*f-e;j=b*e-f;k=a*h;if(i>=0)if(j>=-k)if(j<=k){h=1/h;i*=h;j*=h;h=i*(i+b*j+2*e)+j*(b*i+j+2*f)+g}else j=a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;else j=-a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;else j<=-k?(i=Math.max(0,-(-b*a+e)),j=i>0?-a:Math.min(Math.max(-a,-f),a),h=-i*i+j*(j+2*f)+g):j<=k?(i=0,j=Math.min(Math.max(-a,-f),a),h=j*(j+2*f)+g):(i=Math.max(0,-(b*a+e)),j=i>0?a:Math.min(Math.max(-a,-f),a),h=-i*i+j*(j+2*f)+g)}else j=b>0?-a:a,i=Math.max(0,-(b*j+e)),h=-i*i+j*(j+2*f)+g;c&&c.copy(this.direction).multiplyScalar(i).add(this.origin);d&&d.copy(ee).multiplyScalar(j).add(de);return h},intersectSphere:function(a,b){ce.subVectors(a.center,this.origin);var c=ce.dot(this.direction),d=ce.dot(ce)-c*c;a=a.radius*a.radius;if(d>a)return null;a=Math.sqrt(a-d);d=c-a;c=c+a;if(d<0&&c<0)return null;return d<0?this.at(c,b):this.at(d,b)},intersectsSphere:function(a){return this.distanceSqToPoint(a.center)<=a.radius*a.radius},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(b===0)return a.distanceToPoint(this.origin)===0?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return a>=0?a:null},intersectPlane:function(a,b){a=this.distanceToPlane(a);return a===null?null:this.at(a,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin);if(b===0)return!0;a=a.normal.dot(this.direction);return a*b<0?!0:!1},intersectBox:function(a,b){var c,d,e,f=1/this.direction.x,g=1/this.direction.y,h=1/this.direction.z,i=this.origin;f>=0?(c=(a.min.x-i.x)*f,d=(a.max.x-i.x)*f):(c=(a.max.x-i.x)*f,d=(a.min.x-i.x)*f);g>=0?(f=(a.min.y-i.y)*g,e=(a.max.y-i.y)*g):(f=(a.max.y-i.y)*g,e=(a.min.y-i.y)*g);if(c>e||f>d)return null;(f>c||c!==c)&&(c=f);(e=0?(g=(a.min.z-i.z)*h,f=(a.max.z-i.z)*h):(g=(a.max.z-i.z)*h,f=(a.min.z-i.z)*h);if(c>f||g>d)return null;(g>c||c!==c)&&(c=g);(f=0?c:d,b)},intersectsBox:function(a){return this.intersectBox(a,ce)!==null},intersectTriangle:function(a,b,c,d,e){ge.subVectors(b,a);he.subVectors(c,a);ie.crossVectors(ge,he);b=this.direction.dot(ie);if(b>0){if(d)return null;c=1}else if(b<0)c=-1,b=-b;else return null;fe.subVectors(this.origin,a);d=c*this.direction.dot(he.crossVectors(fe,he));if(d<0)return null;a=c*this.direction.dot(ge.cross(fe));if(a<0)return null;if(d+a>b)return null;d=-c*fe.dot(ie);return d<0?null:this.at(d/b,e)},applyMatrix4:function(a){this.origin.applyMatrix4(a);this.direction.transformDirection(a);return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}});var ke=new P(),le=new P(),me=new cd();function ne(a,b){this.normal=a!==void 0?a:new P(1,0,0),this.constant=b!==void 0?b:0}Object.assign(ne.prototype,{isPlane:!0,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(a,b,c){c=ke.subVectors(c,b).cross(le.subVectors(a,b)).normalize();this.setFromNormalAndCoplanarPoint(c,a);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){b===void 0&&(b=new P());return b.copy(this.normal).multiplyScalar(-this.distanceToPoint(a)).add(a)},intersectLine:function(a,b){b===void 0&&(b=new P());var c=a.delta(ke),d=this.normal.dot(c);if(d===0)return this.distanceToPoint(a.start)===0?b.copy(a.start):void 0;d=-(a.start.dot(this.normal)+this.constant)/d;return d<0||d>1?void 0:b.copy(c).multiplyScalar(d).add(a.start)},intersectsLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return b<0&&a>0||a<0&&b>0},intersectsBox:function(a){return a.intersectsPlane(this)},intersectsSphere:function(a){return a.intersectsPlane(this)},coplanarPoint:function(a){a===void 0&&(a=new P());return a.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(a,b){b=b||me.getNormalMatrix(a);a=this.coplanarPoint(ke).applyMatrix4(a);b=this.normal.applyMatrix3(b).normalize();this.constant=-a.dot(b);return this},translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&a.constant===this.constant}});var oe=new P(),pe=new P(),qe=new P(),re=new P(),se=new P(),te=new P(),ue=new P(),ve=new P(),we=new P(),xe=new P();function ye(a,b,c){this.a=a!==void 0?a:new P(),this.b=b!==void 0?b:new P(),this.c=c!==void 0?c:new P()}Object.assign(ye,{getNormal:function(a,b,c,d){d===void 0&&(d=new P());d.subVectors(c,b);oe.subVectors(a,b);d.cross(oe);c=d.lengthSq();return c>0?d.multiplyScalar(1/Math.sqrt(c)):d.set(0,0,0)},getBarycoord:function(a,b,c,d,e){oe.subVectors(d,b);pe.subVectors(c,b);qe.subVectors(a,b);d=oe.dot(oe);c=oe.dot(pe);a=oe.dot(qe);b=pe.dot(pe);var f=pe.dot(qe),g=d*b-c*c;e===void 0&&(e=new P());if(g===0)return e.set(-2,-1,-1);g=1/g;b=(b*a-c*f)*g;d=(d*f-c*a)*g;return e.set(1-b-d,d,b)},containsPoint:function(a,b,c,d){ye.getBarycoord(a,b,c,d,re);return re.x>=0&&re.y>=0&&re.x+re.y<=1},getUV:function(a,b,c,d,e,f,g,h){this.getBarycoord(a,b,c,d,re);h.set(0,0);h.addScaledVector(e,re.x);h.addScaledVector(f,re.y);h.addScaledVector(g,re.z);return h},isFrontFacing:function(a,b,c,d){oe.subVectors(c,b);pe.subVectors(a,b);return oe.cross(pe).dot(d)<0?!0:!1}});Object.assign(ye.prototype,{set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},getArea:function(){oe.subVectors(this.c,this.b);pe.subVectors(this.a,this.b);return oe.cross(pe).length()*.5},getMidpoint:function(a){a===void 0&&(a=new P());return a.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},getNormal:function(a){return ye.getNormal(this.a,this.b,this.c,a)},getPlane:function(a){a===void 0&&(a=new ne());return a.setFromCoplanarPoints(this.a,this.b,this.c)},getBarycoord:function(a,b){return ye.getBarycoord(a,this.a,this.b,this.c,b)},getUV:function(a,b,c,d,e){return ye.getUV(a,this.a,this.b,this.c,b,c,d,e)},containsPoint:function(a){return ye.containsPoint(a,this.a,this.b,this.c)},isFrontFacing:function(a){return ye.isFrontFacing(this.a,this.b,this.c,a)},intersectsBox:function(a){return a.intersectsTriangle(this)},closestPointToPoint:function(a,b){b===void 0&&(b=new P());var c=this.a,d=this.b,e=this.c,f;se.subVectors(d,c);te.subVectors(e,c);ve.subVectors(a,c);var g=se.dot(ve),h=te.dot(ve);if(g<=0&&h<=0)return b.copy(c);we.subVectors(a,d);var i=se.dot(we),j=te.dot(we);if(i>=0&&j<=i)return b.copy(d);var k=g*j-i*h;if(k<=0&&g>=0&&i<=0){f=g/(g-i);return b.copy(c).addScaledVector(se,f)}xe.subVectors(a,e);a=se.dot(xe);var l=te.dot(xe);if(l>=0&&a<=l)return b.copy(e);g=a*h-g*l;if(g<=0&&h>=0&&l<=0){h=h/(h-l);return b.copy(c).addScaledVector(te,h)}var m=i*l-a*j;if(m<=0&&j-i>=0&&a-l>=0){ue.subVectors(e,d);h=(j-i)/(j-i+(a-l));return b.copy(d).addScaledVector(ue,h)}e=1/(m+g+k);f=g*e;h=k*e;return b.copy(c).addScaledVector(se,f).addScaledVector(te,h)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)}});var ze={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},Ae={h:0,s:0,l:0},Be={h:0,s:0,l:0};function S(a,b,c){return b===void 0&&c===void 0?this.set(a):this.setRGB(a,b,c)}function Ce(a,b,c){c<0&&(c+=1);c>1&&(c-=1);if(c<1/6)return a+(b-a)*6*c;if(c<1/2)return b;return c<2/3?a+(b-a)*6*(2/3-c):a}function De(a){return a<.04045?a*.0773993808:Math.pow(a*.9478672986+.0521327014,2.4)}function Ee(a){return a<.0031308?a*12.92:1.055*Math.pow(a,.41666)-.055}Object.assign(S.prototype,{isColor:!0,r:1,g:1,b:1,set:function(a){a&&a.isColor?this.copy(a):typeof a==="number"?this.setHex(a):typeof a==="string"&&this.setStyle(a);return this},setScalar:function(a){this.r=a;this.g=a;this.b=a;return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){a=N.euclideanModulo(a,1);b=N.clamp(b,0,1);c=N.clamp(c,0,1);if(b===0)this.r=this.g=this.b=c;else{b=c<=.5?c*(1+b):c+b-c*b;c=2*c-b;this.r=Ce(c,b,a+1/3);this.g=Ce(c,b,a);this.b=Ce(c,b,a-1/3)}return this},setStyle:function(a){function b(a){if(a===void 0)return;parseFloat(a)<1}var c;if(c=/^((?:rgb|hsl)a?)(s*([^)]*))/.exec(a)){var d=c[1],e=c[2];switch(d){case"rgb":case"rgba":if(d=/^(d+)s*,s*(d+)s*,s*(d+)s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){this.r=Math.min(255,parseInt(d[1],10))/255;this.g=Math.min(255,parseInt(d[2],10))/255;this.b=Math.min(255,parseInt(d[3],10))/255;b(d[5]);return this}if(d=/^(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){this.r=Math.min(100,parseInt(d[1],10))/100;this.g=Math.min(100,parseInt(d[2],10))/100;this.b=Math.min(100,parseInt(d[3],10))/100;b(d[5]);return this}break;case"hsl":case"hsla":if(d=/^([0-9]*.?[0-9]+)s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec(e)){e=parseFloat(d[1])/360;var f=parseInt(d[2],10)/100,g=parseInt(d[3],10)/100;b(d[5]);return this.setHSL(e,f,g)}break}}else if(c=/^#([A-Fa-f0-9]+)$/.exec(a)){b=c[1];d=b.length;if(d===3){this.r=parseInt(b.charAt(0)+b.charAt(0),16)/255;this.g=parseInt(b.charAt(1)+b.charAt(1),16)/255;this.b=parseInt(b.charAt(2)+b.charAt(2),16)/255;return this}else if(d===6){this.r=parseInt(b.charAt(0)+b.charAt(1),16)/255;this.g=parseInt(b.charAt(2)+b.charAt(3),16)/255;this.b=parseInt(b.charAt(4)+b.charAt(5),16)/255;return this}}return a&&a.length>0?this.setColorName(a):this},setColorName:function(a){var b=ze[a];b!==void 0?this.setHex(b):console.warn("THREE.Color: Unknown color "+a);return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(a){this.r=a.r;this.g=a.g;this.b=a.b;return this},copyGammaToLinear:function(a,b){b===void 0&&(b=2);this.r=Math.pow(a.r,b);this.g=Math.pow(a.g,b);this.b=Math.pow(a.b,b);return this},copyLinearToGamma:function(a,b){b===void 0&&(b=2);b=b>0?1/b:1;this.r=Math.pow(a.r,b);this.g=Math.pow(a.g,b);this.b=Math.pow(a.b,b);return this},convertGammaToLinear:function(a){this.copyGammaToLinear(this,a);return this},convertLinearToGamma:function(a){this.copyLinearToGamma(this,a);return this},copySRGBToLinear:function(a){this.r=De(a.r);this.g=De(a.g);this.b=De(a.b);return this},copyLinearToSRGB:function(a){this.r=Ee(a.r);this.g=Ee(a.g);this.b=Ee(a.b);return this},convertSRGBToLinear:function(){this.copySRGBToLinear(this);return this},convertLinearToSRGB:function(){this.copyLinearToSRGB(this);return this},getHex:function(){return this.r*255<<16^this.g*255<<8^this.b*255<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a===void 0&&(a={h:0,s:0,l:0});var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h,i=(f+e)/2;if(f===e)g=0,h=0;else{var j=e-f;h=i<=.5?j/(e+f):j/(2-e-f);switch(e){case b:g=(c-d)/j+(c0&&(c.alphaTest=this.alphaTest);this.premultipliedAlpha===!0&&(c.premultipliedAlpha=this.premultipliedAlpha);this.wireframe===!0&&(c.wireframe=this.wireframe);this.wireframeLinewidth>1&&(c.wireframeLinewidth=this.wireframeLinewidth);this.wireframeLinecap!=="round"&&(c.wireframeLinecap=this.wireframeLinecap);this.wireframeLinejoin!=="round"&&(c.wireframeLinejoin=this.wireframeLinejoin);this.morphTargets===!0&&(c.morphTargets=!0);this.morphNormals===!0&&(c.morphNormals=!0);this.skinning===!0&&(c.skinning=!0);this.visible===!1&&(c.visible=!1);this.toneMapped===!1&&(c.toneMapped=!1);JSON.stringify(this.userData)!=="{}"&&(c.userData=this.userData);function d(b){var c=[];for(var d in b){var a=b[d];delete a.metadata;c.push(a)}return c}if(b){b=d(a.textures);d=d(a.images);b.length>0&&(c.textures=b);d.length>0&&(c.images=d)}return c},clone:function(){return new this.constructor().copy(this)},copy:function(b){this.name=b.name;this.fog=b.fog;this.blending=b.blending;this.side=b.side;this.flatShading=b.flatShading;this.vertexColors=b.vertexColors;this.opacity=b.opacity;this.transparent=b.transparent;this.blendSrc=b.blendSrc;this.blendDst=b.blendDst;this.blendEquation=b.blendEquation;this.blendSrcAlpha=b.blendSrcAlpha;this.blendDstAlpha=b.blendDstAlpha;this.blendEquationAlpha=b.blendEquationAlpha;this.depthFunc=b.depthFunc;this.depthTest=b.depthTest;this.depthWrite=b.depthWrite;this.stencilWriteMask=b.stencilWriteMask;this.stencilFunc=b.stencilFunc;this.stencilRef=b.stencilRef;this.stencilFuncMask=b.stencilFuncMask;this.stencilFail=b.stencilFail;this.stencilZFail=b.stencilZFail;this.stencilZPass=b.stencilZPass;this.stencilWrite=b.stencilWrite;var c=b.clippingPlanes,d=null;if(c!==null){var e=c.length;d=new Array(e);for(var a=0;a!==e;++a)d[a]=c[a].clone()}this.clippingPlanes=d;this.clipIntersection=b.clipIntersection;this.clipShadows=b.clipShadows;this.shadowSide=b.shadowSide;this.colorWrite=b.colorWrite;this.precision=b.precision;this.polygonOffset=b.polygonOffset;this.polygonOffsetFactor=b.polygonOffsetFactor;this.polygonOffsetUnits=b.polygonOffsetUnits;this.dithering=b.dithering;this.alphaTest=b.alphaTest;this.premultipliedAlpha=b.premultipliedAlpha;this.visible=b.visible;this.toneMapped=b.toneMapped;this.userData=JSON.parse(JSON.stringify(b.userData));return this},dispose:function(){this.dispatchEvent({type:"dispose"})}});Object.defineProperty(T.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});function He(a){T.call(this),this.type="MeshBasicMaterial",this.color=new S(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=la,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.skinning=!1,this.morphTargets=!1,this.setValues(a)}He.prototype=Object.create(T.prototype);He.prototype.constructor=He;He.prototype.isMeshBasicMaterial=!0;He.prototype.copy=function(a){T.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;return this};var Ie=new P();function U(a,b,c){if(Array.isArray(a))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.name="";this.array=a;this.itemSize=b;this.count=a!==void 0?a.length/b:0;this.normalized=c===!0;this.usage=Rc;this.updateRange={offset:0,count:-1};this.version=0}Object.defineProperty(U.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});Object.assign(U.prototype,{isBufferAttribute:!0,onUploadCallback:function(){},setUsage:function(a){this.usage=a;return this},copy:function(a){this.name=a.name;this.array=new a.array.constructor(a.array);this.itemSize=a.itemSize;this.count=a.count;this.normalized=a.normalized;this.usage=a.usage;return this},copyAt:function(b,c,d){b*=this.itemSize;d*=c.itemSize;for(var a=0,e=this.itemSize;a0,g=e[1]&&e[1].length>0,h=b.morphTargets,i=h.length,j;if(i>0){j=[];for(var a=0;a0){m=[];for(var a=0;a0&&c.length===0;for(var a=0;ac&&(c=b[a]);return c}var Te=1,Ue=new Q(),Ve=new R(),We=new P(),Xe=new Zd(),Ye=new Zd(),Ze=new P();function W(){Object.defineProperty(this,"id",{value:Te+=2}),this.uuid=N.generateUUID(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:Infinity},this.userData={}}W.prototype=Object.assign(Object.create($c.prototype),{constructor:W,isBufferGeometry:!0,getIndex:function(){return this.index},setIndex:function(a){Array.isArray(a)?this.index=new(Se(a)>65535?Pe:Ne)(a,1):this.index=a},getAttribute:function(a){return this.attributes[a]},setAttribute:function(a,b){this.attributes[a]=b;return this},deleteAttribute:function(a){delete this.attributes[a];return this},addGroup:function(a,b,c){this.groups.push({start:a,count:b,materialIndex:c!==void 0?c:0})},clearGroups:function(){this.groups=[]},setDrawRange:function(a,b){this.drawRange.start=a,this.drawRange.count=b},applyMatrix4:function(a){var b=this.attributes.position;b!==void 0&&(b.applyMatrix4(a),b.needsUpdate=!0);b=this.attributes.normal;if(b!==void 0){var c=new cd().getNormalMatrix(a);b.applyNormalMatrix(c);b.needsUpdate=!0}c=this.attributes.tangent;c!==void 0&&(c.transformDirection(a),c.needsUpdate=!0);this.boundingBox!==null&&this.computeBoundingBox();this.boundingSphere!==null&&this.computeBoundingSphere();return this},rotateX:function(a){Ue.makeRotationX(a);this.applyMatrix4(Ue);return this},rotateY:function(a){Ue.makeRotationY(a);this.applyMatrix4(Ue);return this},rotateZ:function(a){Ue.makeRotationZ(a);this.applyMatrix4(Ue);return this},translate:function(a,b,c){Ue.makeTranslation(a,b,c);this.applyMatrix4(Ue);return this},scale:function(a,b,c){Ue.makeScale(a,b,c);this.applyMatrix4(Ue);return this},lookAt:function(a){Ve.lookAt(a);Ve.updateMatrix();this.applyMatrix4(Ve.matrix);return this},center:function(){this.computeBoundingBox();this.boundingBox.getCenter(We).negate();this.translate(We.x,We.y,We.z);return this},setFromObject:function(a){var b=a.geometry;if(a.isPoints||a.isLine){var c=new V(b.vertices.length*3,3),d=new V(b.colors.length*3,3);this.setAttribute("position",c.copyVector3sArray(b.vertices));this.setAttribute("color",d.copyColorsArray(b.colors));if(b.lineDistances&&b.lineDistances.length===b.vertices.length){c=new V(b.lineDistances.length,1);this.setAttribute("lineDistance",c.copyArray(b.lineDistances))}b.boundingSphere!==null&&(this.boundingSphere=b.boundingSphere.clone());b.boundingBox!==null&&(this.boundingBox=b.boundingBox.clone())}else a.isMesh&&(b&&b.isGeometry&&this.fromGeometry(b));return this},setFromPoints:function(b){var c=[];for(var a=0,d=b.length;a0){c=new Float32Array(b.normals.length*3);this.setAttribute("normal",new U(c,3).copyVector3sArray(b.normals))}if(b.colors.length>0){c=new Float32Array(b.colors.length*3);this.setAttribute("color",new U(c,3).copyColorsArray(b.colors))}if(b.uvs.length>0){c=new Float32Array(b.uvs.length*2);this.setAttribute("uv",new U(c,2).copyVector2sArray(b.uvs))}if(b.uvs2.length>0){c=new Float32Array(b.uvs2.length*2);this.setAttribute("uv2",new U(c,2).copyVector2sArray(b.uvs2))}this.groups=b.groups;for(c in b.morphTargets){var d=[],e=b.morphTargets[c];for(var a=0,f=e.length;a0){h=new V(b.skinIndices.length*4,4);this.setAttribute("skinIndex",h.copyVector4sArray(b.skinIndices))}if(b.skinWeights.length>0){g=new V(b.skinWeights.length*4,4);this.setAttribute("skinWeight",g.copyVector4sArray(b.skinWeights))}b.boundingSphere!==null&&(this.boundingSphere=b.boundingSphere.clone());b.boundingBox!==null&&(this.boundingBox=b.boundingBox.clone());return this},computeBoundingBox:function(){this.boundingBox===null&&(this.boundingBox=new Zd());var a=this.attributes.position,b=this.morphAttributes.position;if(a!==void 0){this.boundingBox.setFromBufferAttribute(a);if(b)for(var a=0,c=b.length;a0&&(b.userData=this.userData);if(this.parameters!==void 0){var c=this.parameters;for(var d in c)c[d]!==void 0&&(b[d]=c[d]);return b}b.data={attributes:{}};c=this.index;c!==null&&(b.data.index={type:c.array.constructor.name,array:Array.prototype.slice.call(c.array)});c=this.attributes;for(var d in c){var e=c[d],f=e.toJSON();e.name!==""&&(f.name=e.name);b.data.attributes[d]=f}c={};var g=!1;for(var d in this.morphAttributes){var h=this.morphAttributes[d],i=[];for(var a=0,j=h.length;a0&&(c[d]=i,g=!0)}g&&(b.data.morphAttributes=c,b.data.morphTargetsRelative=this.morphTargetsRelative);f=this.groups;f.length>0&&(b.data.groups=JSON.parse(JSON.stringify(f)));h=this.boundingSphere;h!==null&&(b.data.boundingSphere={center:h.center.toArray(),radius:h.radius});return b},clone:function(){return new W().copy(this)},copy:function(b){var c,a,d;this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingBox=null;this.boundingSphere=null;this.name=b.name;var e=b.index;e!==null&&this.setIndex(e.clone());e=b.attributes;for(c in e){var f=e[c];this.setAttribute(c,f.clone())}f=b.morphAttributes;for(c in f){e=[];var g=f[c];for(a=0,d=g.length;a0){d=d[e[0]];if(d!==void 0){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(e=0,b=d.length;e0}},raycast:function(c,b){var d=this.geometry,e=this.material,f=this.matrixWorld;if(e===void 0)return;d.boundingSphere===null&&d.computeBoundingSphere();bf.copy(d.boundingSphere);bf.applyMatrix4(f);if(c.ray.intersectsSphere(bf)===!1)return;$e.getInverse(f);af.copy(c.ray).applyMatrix4($e);if(d.boundingBox!==null&&af.intersectsBox(d.boundingBox)===!1)return;var g;if(d.isBufferGeometry){var h,i,j;f=d.index;var k=d.attributes.position,l=d.morphAttributes.position,m=d.morphTargetsRelative,n=d.attributes.uv,o=d.attributes.uv2,p=d.groups,q=d.drawRange,a,r,s,t,u,v,w,x;if(f!==null)if(Array.isArray(e))for(a=0,s=p.length;a0&&(y=p);for(r=0,t=v.length;rc.far?null:{distance:d,point:qf.clone(),object:a}}function tf(b,c,d,e,f,g,h,i,j,k,l,m){cf.fromBufferAttribute(f,k);df.fromBufferAttribute(f,l);ef.fromBufferAttribute(f,m);f=b.morphTargetInfluences;if(c.morphTargets&&g&&f){jf.set(0,0,0);kf.set(0,0,0);lf.set(0,0,0);for(var a=0,n=g.length;a0)for(d=0;d0&&(this.normalsNeedUpdate=!0)},computeFlatVertexNormals:function(){var a,b,c;this.computeFaceNormals();for(a=0,b=this.faces.length;a0&&(this.normalsNeedUpdate=!0)},computeMorphNormals:function(){var a,b,c,d,e;for(c=0,d=this.faces.length;c=0;a--){b=e[a];this.faces.splice(b,1);for(f=0,d=this.faceVertexUvs.length;f0,p=k.vertexNormals.length>0,q=k.color.r!==1||k.color.g!==1||k.color.b!==1,r=k.vertexColors.length>0,s=0;s=t(s,0,0);s=t(s,1,l);s=t(s,2,m);s=t(s,3,n);s=t(s,4,o);s=t(s,5,p);s=t(s,6,q);s=t(s,7,r);d.push(s);d.push(k.a,k.b,k.c);d.push(k.materialIndex);if(n){l=this.faceVertexUvs[0][b];d.push(w(l[0]),w(l[1]),w(l[2]))}o&&d.push(u(k.normal));if(p){m=k.vertexNormals;d.push(u(m[0]),u(m[1]),u(m[2]))}q&&d.push(v(k.color));if(r){s=k.vertexColors;d.push(v(s[0]),v(s[1]),v(s[2]))}}function t(a,b,c){return c?a|1<0&&(a.data.colors=g);i.length>0&&(a.data.uvs=[i]);a.data.faces=d;return a},clone:function(){return new X().copy(this)},copy:function(b){var a,c,d,e,f,g;this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingBox=null;this.boundingSphere=null;this.name=b.name;var h=b.vertices;for(a=0,c=h.length;a0?1:-1;k.push(B.x,B.y,B.z);l.push(z/p);l.push(1-A/q);x+=1}}for(A=0;A0&&(b.defines=this.defines);b.vertexShader=this.vertexShader;b.fragmentShader=this.fragmentShader;d={};for(a in this.extensions)this.extensions[a]===!0&&(d[a]=!0);Object.keys(d).length>0&&(b.extensions=d);return b};function Ff(){R.call(this),this.type="Camera",this.matrixWorldInverse=new Q(),this.projectionMatrix=new Q(),this.projectionMatrixInverse=new Q()}Ff.prototype=Object.assign(Object.create(R.prototype),{constructor:Ff,isCamera:!0,copy:function(a,b){R.prototype.copy.call(this,a,b);this.matrixWorldInverse.copy(a.matrixWorldInverse);this.projectionMatrix.copy(a.projectionMatrix);this.projectionMatrixInverse.copy(a.projectionMatrixInverse);return this},getWorldDirection:function(a){a===void 0&&(a=new P());this.updateMatrixWorld(!0);var b=this.matrixWorld.elements;return a.set(-b[8],-b[9],-b[10]).normalize()},updateMatrixWorld:function(a){R.prototype.updateMatrixWorld.call(this,a),this.matrixWorldInverse.getInverse(this.matrixWorld)},updateWorldMatrix:function(a,b){R.prototype.updateWorldMatrix.call(this,a,b),this.matrixWorldInverse.getInverse(this.matrixWorld)},clone:function(){return new this.constructor().copy(this)}});function Gf(a,b,c,d){Ff.call(this),this.type="PerspectiveCamera",this.fov=a!==void 0?a:50,this.zoom=1,this.near=c!==void 0?c:.1,this.far=d!==void 0?d:2e3,this.focus=10,this.aspect=b!==void 0?b:1,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}Gf.prototype=Object.assign(Object.create(Ff.prototype),{constructor:Gf,isPerspectiveCamera:!0,copy:function(a,b){Ff.prototype.copy.call(this,a,b);this.fov=a.fov;this.zoom=a.zoom;this.near=a.near;this.far=a.far;this.focus=a.focus;this.aspect=a.aspect;this.view=a.view===null?null:Object.assign({},a.view);this.filmGauge=a.filmGauge;this.filmOffset=a.filmOffset;return this},setFocalLength:function(a){a=.5*this.getFilmHeight()/a;this.fov=N.RAD2DEG*2*Math.atan(a);this.updateProjectionMatrix()},getFocalLength:function(){var a=Math.tan(N.DEG2RAD*.5*this.fov);return.5*this.getFilmHeight()/a},getEffectiveFOV:function(){return N.RAD2DEG*2*Math.atan(Math.tan(N.DEG2RAD*.5*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(a,b,c,d,e,f){this.aspect=a/b,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=a,this.view.fullHeight=b,this.view.offsetX=c,this.view.offsetY=d,this.view.width=e,this.view.height=f,this.updateProjectionMatrix()},clearViewOffset:function(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()},updateProjectionMatrix:function(){var a=this.near,b=a*Math.tan(N.DEG2RAD*.5*this.fov)/this.zoom,c=2*b,d=this.aspect*c,e=-.5*d,f=this.view;if(this.view!==null&&this.view.enabled){var g=f.fullWidth,h=f.fullHeight;e+=f.offsetX*d/g;b-=f.offsetY*c/h;d*=f.width/g;c*=f.height/h}g=this.filmOffset;g!==0&&(e+=a*g/this.getFilmWidth());this.projectionMatrix.makePerspective(e,e+d,b,b-c,a,this.far);this.projectionMatrixInverse.getInverse(this.projectionMatrix)},toJSON:function(a){a=R.prototype.toJSON.call(this,a);a.object.fov=this.fov;a.object.zoom=this.zoom;a.object.near=this.near;a.object.far=this.far;a.object.focus=this.focus;a.object.aspect=this.aspect;this.view!==null&&(a.object.view=Object.assign({},this.view));a.object.filmGauge=this.filmGauge;a.object.filmOffset=this.filmOffset;return a}});var Hf=90,If=1;function Jf(a,b,c,d){R.call(this);this.type="CubeCamera";var e=new Gf(Hf,If,a,b);e.up.set(0,-1,0);e.lookAt(new P(1,0,0));this.add(e);var f=new Gf(Hf,If,a,b);f.up.set(0,-1,0);f.lookAt(new P(-1,0,0));this.add(f);var g=new Gf(Hf,If,a,b);g.up.set(0,0,1);g.lookAt(new P(0,1,0));this.add(g);var h=new Gf(Hf,If,a,b);h.up.set(0,0,-1);h.lookAt(new P(0,-1,0));this.add(h);var i=new Gf(Hf,If,a,b);i.up.set(0,-1,0);i.lookAt(new P(0,0,1));this.add(i);var j=new Gf(Hf,If,a,b);j.up.set(0,-1,0);j.lookAt(new P(0,0,-1));this.add(j);d=d||{format:bb,magFilter:Ka,minFilter:Ka};this.renderTarget=new Kf(c,d);this.renderTarget.texture.name="CubeCamera";this.update=function(a,b){this.parent===null&&this.updateMatrixWorld();var c=a.getRenderTarget(),d=this.renderTarget,k=d.texture.generateMipmaps;d.texture.generateMipmaps=!1;a.setRenderTarget(d,0);a.render(b,e);a.setRenderTarget(d,1);a.render(b,f);a.setRenderTarget(d,2);a.render(b,g);a.setRenderTarget(d,3);a.render(b,h);a.setRenderTarget(d,4);a.render(b,i);d.texture.generateMipmaps=k;a.setRenderTarget(d,5);a.render(b,j);a.setRenderTarget(c)};this.clear=function(b,c,d,e){var f=b.getRenderTarget(),g=this.renderTarget;for(var a=0;a<6;a++)b.setRenderTarget(g,a),b.clear(c,d,e);b.setRenderTarget(f)}}Jf.prototype=Object.create(R.prototype);Jf.prototype.constructor=Jf;function Kf(a,b,c){Number.isInteger(b)&&(b=c),id.call(this,a,a,b)}Kf.prototype=Object.create(id.prototype);Kf.prototype.constructor=Kf;Kf.prototype.isWebGLCubeRenderTarget=!0;Kf.prototype.fromEquirectangularTexture=function(a,b){this.texture.type=b.type;this.texture.format=b.format;this.texture.encoding=b.encoding;var c=new Ld(),d={uniforms:{tEquirect:{value:null}},vertexShader:["varying vec3 vWorldDirection;","vec3 transformDirection( in vec3 dir, in mat4 matrix ) {"," return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );","}","void main() {"," vWorldDirection = transformDirection( position, modelMatrix );"," #include "," #include ","}"].join(" "),fragmentShader:["uniform sampler2D tEquirect;","varying vec3 vWorldDirection;","#define RECIPROCAL_PI 0.31830988618","#define RECIPROCAL_PI2 0.15915494","void main() {"," vec3 direction = normalize( vWorldDirection );"," vec2 sampleUV;"," sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;"," sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;"," gl_FragColor = texture2D( tEquirect, sampleUV );","}"].join(" ")};d=new Ef({type:"CubemapFromEquirect",uniforms:zf(d.uniforms),vertexShader:d.vertexShader,fragmentShader:d.fragmentShader,side:q,blending:u});d.uniforms.tEquirect.value=b;b=new rf(new yf(5,5,5),d);c.add(b);d=new Jf(1,10,1);d.renderTarget=this;d.renderTarget.texture.name="CubeCameraTexture";d.update(a,c);b.geometry.dispose();b.material.dispose();return this};function Lf(a,b,c,d,e,f,g,h,i,j,k,l){gd.call(this,null,f,g,h,i,j,d,e,k,l),this.image={data:a||null,width:b||1,height:c||1},this.magFilter=i!==void 0?i:Fa,this.minFilter=j!==void 0?j:Fa,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.needsUpdate=!0}Lf.prototype=Object.create(gd.prototype);Lf.prototype.constructor=Lf;Lf.prototype.isDataTexture=!0;var Mf=new be(),Nf=new P();function Of(a,b,c,d,e,f){this.planes=[a!==void 0?a:new ne(),b!==void 0?b:new ne(),c!==void 0?c:new ne(),d!==void 0?d:new ne(),e!==void 0?e:new ne(),f!==void 0?f:new ne()]}Object.assign(Of.prototype,{set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},clone:function(){return new this.constructor().copy(this)},copy:function(b){var c=this.planes;for(var a=0;a<6;a++)c[a].copy(b.planes[a]);return this},setFromProjectionMatrix:function(a){var b=this.planes;a=a.elements;var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],m=a[10],n=a[11],o=a[12],p=a[13],q=a[14];a=a[15];b[0].setComponents(f-c,j-g,n-k,a-o).normalize();b[1].setComponents(f+c,j+g,n+k,a+o).normalize();b[2].setComponents(f+d,j+h,n+l,a+p).normalize();b[3].setComponents(f-d,j-h,n-l,a-p).normalize();b[4].setComponents(f-e,j-i,n-m,a-q).normalize();b[5].setComponents(f+e,j+i,n+m,a+q).normalize();return this},intersectsObject:function(a){var b=a.geometry;b.boundingSphere===null&&b.computeBoundingSphere();Mf.copy(b.boundingSphere).applyMatrix4(a.matrixWorld);return this.intersectsSphere(Mf)},intersectsSprite:function(a){Mf.center.set(0,0,0);Mf.radius=.7071067811865476;Mf.applyMatrix4(a.matrixWorld);return this.intersectsSphere(Mf)},intersectsSphere:function(b){var c=this.planes,d=b.center;b=-b.radius;for(var a=0;a<6;a++){var e=c[a].distanceToPoint(d);if(e0?b.max.x:b.min.x;Nf.y=d.normal.y>0?b.max.y:b.min.y;Nf.z=d.normal.z>0?b.max.z:b.min.z;if(d.distanceToPoint(Nf)<0)return!1}return!0},containsPoint:function(b){var c=this.planes;for(var a=0;a<6;a++)if(c[a].distanceToPoint(b)<0)return!1;return!0}});var Y={common:{diffuse:{value:new S(15658734)},opacity:{value:1},map:{value:null},uvTransform:{value:new cd()},uv2Transform:{value:new cd()},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 O(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 S(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{}}},spotShadowMap:{value:[]},spotShadowMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}}},points:{diffuse:{value:new S(15658734)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},uvTransform:{value:new cd()}},sprite:{diffuse:{value:new S(15658734)},opacity:{value:1},center:{value:new O(.5,.5)},rotation:{value:0},map:{value:null},alphaMap:{value:null},uvTransform:{value:new cd()}}};function Pf(){var a=null,b=!1,c=null;function d(e,f){if(b===!1)return;c(e,f);a.requestAnimationFrame(d)}return{start:function(){if(b===!0)return;if(c===null)return;a.requestAnimationFrame(d);b=!0},stop:function(){b=!1},setAnimationLoop:function(a){c=a},setContext:function(b){a=b}}}function Qf(a,b){var c=b.isWebGL2,d=new WeakMap();function e(b,c){var d=b.array,e=b.usage,f=a.createBuffer();a.bindBuffer(c,f);a.bufferData(c,d,e);b.onUploadCallback();c=5126;d instanceof Float32Array?c=5126:d instanceof Float64Array||(d instanceof Uint16Array?c=5123:d instanceof Int16Array?c=5122:d instanceof Uint32Array?c=5125:d instanceof Int32Array?c=5124:d instanceof Int8Array?c=5120:d instanceof Uint8Array&&(c=5121));return{buffer:f,type:c,bytesPerElement:d.BYTES_PER_ELEMENT,version:b.version}}function f(b,d,e){var f=d.array;d=d.updateRange;a.bindBuffer(e,b);d.count===-1?a.bufferSubData(e,0,f):(c?a.bufferSubData(e,d.offset*f.BYTES_PER_ELEMENT,f,d.offset,d.count):a.bufferSubData(e,d.offset*f.BYTES_PER_ELEMENT,f.subarray(d.offset,d.offset+d.count)),d.count=-1)}function g(a){a.isInterleavedBufferAttribute&&(a=a.data);return d.get(a)}function h(b){b.isInterleavedBufferAttribute&&(b=b.data);var c=d.get(b);c&&(a.deleteBuffer(c.buffer),d["delete"](b))}function i(a,b){a.isInterleavedBufferAttribute&&(a=a.data);var c=d.get(a);c===void 0?d.set(a,e(a,b)):c.version0&&a.getShaderPrecisionFormat(35632,36338).precision>0)return"highp";b="mediump"}return b==="mediump"&&(a.getShaderPrecisionFormat(35633,36337).precision>0&&a.getShaderPrecisionFormat(35632,36337).precision>0)?"mediump":"lowp"}var g=typeof WebGL2RenderingContext!=="undefined"&&a instanceof WebGL2RenderingContext||typeof WebGL2ComputeRenderingContext!=="undefined"&&a instanceof WebGL2ComputeRenderingContext,h=c.precision!==void 0?c.precision:"highp",i=f(h);i!==h&&(h=i);i=c.logarithmicDepthBuffer===!0;c=a.getParameter(34930);var j=a.getParameter(35660),k=a.getParameter(3379),l=a.getParameter(34076),m=a.getParameter(34921),n=a.getParameter(36347),o=a.getParameter(36348),p=a.getParameter(36349),q=j>0,r=g||!!b.get("OES_texture_float"),s=q&&r,t=g?a.getParameter(36183):0;return{isWebGL2:g,getMaxAnisotropy:e,getMaxPrecision:f,precision:h,logarithmicDepthBuffer:i,maxTextures:c,maxVertexTextures:j,maxTextureSize:k,maxCubemapSize:l,maxAttributes:m,maxVertexUniforms:n,maxVaryings:o,maxFragmentUniforms:p,vertexTextures:q,floatFragmentTextures:r,floatVertexTextures:s,maxSamples:t}}function ri(){var a=this,b=null,c=0,d=!1,e=!1,f=new ne(),g=new cd(),h={value:null,needsUpdate:!1};this.uniform=h;this.numPlanes=0;this.numIntersection=0;this.init=function(a,e,f){var g=a.length!==0||e||c!==0||d;d=e;b=j(a,f,0);c=a.length;return g};this.beginShadows=function(){e=!0,j(null)};this.endShadows=function(){e=!1,i()};this.setState=function(a,f,g,k,l,m){if(!d||a===null||a.length===0||e&&!g)e?j(null):i();else{g=e?0:c;var n=g*4,o=l.clippingState||null;h.value=o;o=j(a,k,n,m);for(a=0;a!==n;++a)o[a]=b[a];l.clippingState=o;this.numIntersection=f?this.numPlanes:0;this.numPlanes+=g}};function i(){h.value!==b&&(h.value=b,h.needsUpdate=c>0),a.numPlanes=c,a.numIntersection=0}function j(b,c,d,e){var i=b!==null?b.length:0,j=null;if(i!==0){j=h.value;if(e!==!0||j===null){e=d+i*4;c=c.matrixWorldInverse;g.getNormalMatrix(c);(j===null||j.length65535?Pe:Ne)(c,1);f.version=g;b.update(f,34963);h=e.get(a);h&&b.remove(h);e.set(a,f)}function i(a){var b=e.get(a);if(b){var c=a.index;c!==null&&(b.version0)return a;var e=b*c,f=Gi[e];f===void 0&&(f=new Float32Array(e),Gi[e]=f);if(b!==0){d.toArray(f,0);for(e=1,d=0;e!==b;++e)d+=c,a[e].toArray(f,d)}return f}function Mi(b,c){if(b.length!==c.length)return!1;for(var a=0,d=b.length;a/gm;function Pj(a){return a.replace(Oj,Qj)}function Qj(a,b){a=Z[b];if(a===void 0)throw new Error("Can not resolve #include <"+b+">");return Pj(a)}var Rj=/#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g,Sj=/#pragma unroll_loop_start[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}[s]+?#pragma unroll_loop_end/g;function Tj(a){return a.replace(Sj,Vj).replace(Rj,Uj)}function Uj(a,b,c,d){return Vj(a,b,c,d)}function Vj(a,b,c,d){a="";for(b=parseInt(b);b0?a.gammaFactor:1,m=c.isWebGL2?"":Ij(c);e=Jj(e);var n=d.createProgram(),o,p;c.isRawShaderMaterial?(o=[e].filter(Lj).join(" "),o.length>0&&(o+=" "),p=[m,e].filter(Lj).join(" "),p.length>0&&(p+=" ")):(o=[Wj(c),"#define SHADER_NAME "+c.shaderName,e,c.instancing?"#define USE_INSTANCING":"",c.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+l,"#define MAX_BONES "+c.maxBones,c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp2?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.envMap?"#define "+j:"",c.lightMap?"#define USE_LIGHTMAP":"",c.aoMap?"#define USE_AOMAP":"",c.emissiveMap?"#define USE_EMISSIVEMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.normalMap&&c.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",c.normalMap&&c.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",c.clearcoatMap?"#define USE_CLEARCOATMAP":"",c.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",c.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",c.displacementMap&&c.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.roughnessMap?"#define USE_ROUGHNESSMAP":"",c.metalnessMap?"#define USE_METALNESSMAP":"",c.alphaMap?"#define USE_ALPHAMAP":"",c.vertexTangents?"#define USE_TANGENT":"",c.vertexColors?"#define USE_COLOR":"",c.vertexUvs?"#define USE_UV":"",c.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",c.flatShading?"#define FLAT_SHADED":"",c.skinning?"#define USE_SKINNING":"",c.useVertexTexture?"#define BONE_TEXTURE":"",c.morphTargets?"#define USE_MORPHTARGETS":"",c.morphNormals&&c.flatShading===!1?"#define USE_MORPHNORMALS":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+h:"",c.sizeAttenuation?"#define USE_SIZEATTENUATION":"",c.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",c.logarithmicDepthBuffer&&c.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"," attribute mat4 instanceMatrix;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#ifdef USE_COLOR"," attribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS"," attribute vec3 morphTarget0;"," attribute vec3 morphTarget1;"," attribute vec3 morphTarget2;"," attribute vec3 morphTarget3;"," #ifdef USE_MORPHNORMALS"," attribute vec3 morphNormal0;"," attribute vec3 morphNormal1;"," attribute vec3 morphNormal2;"," attribute vec3 morphNormal3;"," #else"," attribute vec3 morphTarget4;"," attribute vec3 morphTarget5;"," attribute vec3 morphTarget6;"," attribute vec3 morphTarget7;"," #endif","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif"," "].filter(Lj).join(" "),p=[m,Wj(c),"#define SHADER_NAME "+c.shaderName,e,c.alphaTest?"#define ALPHATEST "+c.alphaTest+(c.alphaTest%1?"":".0"):"","#define GAMMA_FACTOR "+l,c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp2?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.matcap?"#define USE_MATCAP":"",c.envMap?"#define USE_ENVMAP":"",c.envMap?"#define "+i:"",c.envMap?"#define "+j:"",c.envMap?"#define "+k:"",c.lightMap?"#define USE_LIGHTMAP":"",c.aoMap?"#define USE_AOMAP":"",c.emissiveMap?"#define USE_EMISSIVEMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.normalMap&&c.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",c.normalMap&&c.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",c.clearcoatMap?"#define USE_CLEARCOATMAP":"",c.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",c.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.roughnessMap?"#define USE_ROUGHNESSMAP":"",c.metalnessMap?"#define USE_METALNESSMAP":"",c.alphaMap?"#define USE_ALPHAMAP":"",c.sheen?"#define USE_SHEEN":"",c.vertexTangents?"#define USE_TANGENT":"",c.vertexColors?"#define USE_COLOR":"",c.vertexUvs?"#define USE_UV":"",c.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",c.gradientMap?"#define USE_GRADIENTMAP":"",c.flatShading?"#define FLAT_SHADED":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+h:"",c.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",c.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",c.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",c.logarithmicDepthBuffer&&c.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"",(c.extensionShaderTextureLOD||c.envMap)&&c.rendererExtensionShaderTextureLod?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",c.toneMapping!==oa?"#define TONE_MAPPING":"",c.toneMapping!==oa?Z.tonemapping_pars_fragment:"",c.toneMapping!==oa?Hj("toneMapping",c.toneMapping):"",c.dithering?"#define DITHERING":"",c.outputEncoding||c.mapEncoding||c.matcapEncoding||c.envMapEncoding||c.emissiveMapEncoding||c.lightMapEncoding?Z.encodings_pars_fragment:"",c.mapEncoding?Fj("mapTexelToLinear",c.mapEncoding):"",c.matcapEncoding?Fj("matcapTexelToLinear",c.matcapEncoding):"",c.envMapEncoding?Fj("envMapTexelToLinear",c.envMapEncoding):"",c.emissiveMapEncoding?Fj("emissiveMapTexelToLinear",c.emissiveMapEncoding):"",c.lightMapEncoding?Fj("lightMapTexelToLinear",c.lightMapEncoding):"",c.outputEncoding?Gj("linearToOutputTexel",c.outputEncoding):"",c.depthPacking?"#define DEPTH_PACKING "+c.depthPacking:""," "].filter(Lj).join(" "));f=Pj(f);f=Mj(f,c);f=Nj(f,c);g=Pj(g);g=Mj(g,c);g=Nj(g,c);f=Tj(f);g=Tj(g);if(c.isWebGL2&&!c.isRawShaderMaterial){m=!1;e=/^s*#versions+300s+ess* /;c.isShaderMaterial&&f.match(e)!==null&&g.match(e)!==null&&(m=!0,f=f.replace(e,""),g=g.replace(e,""));o=["#version 300 es ","#define attribute in","#define varying out","#define texture2D texture"].join(" ")+" "+o;p=["#version 300 es ","#define varying in",m?"":"out highp vec4 pc_fragColor;",m?"":"#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(" ")+" "+p}l=o+f;i=p+g;j=Aj(d,35633,l);k=Aj(d,35632,i);d.attachShader(n,j);d.attachShader(n,k);c.index0AttributeName!==void 0?d.bindAttribLocation(n,0,c.index0AttributeName):c.morphTargets===!0&&d.bindAttribLocation(n,0,"position");d.linkProgram(n);if(a.debug.checkShaderErrors){h=d.getProgramInfoLog(n).trim();e=d.getShaderInfoLog(j).trim();m=d.getShaderInfoLog(k).trim();f=!0;g=!0;if(d.getProgramParameter(n,35714)===!1){f=!1;l=Ej(d,j,"vertex");i=Ej(d,k,"fragment")}else h!==""||(e===""||m==="")&&(g=!1);g&&(this.diagnostics={runnable:f,programLog:h,vertexShader:{log:e,prefix:o},fragmentShader:{log:m,prefix:p}})}d.deleteShader(j);d.deleteShader(k);var q;this.getUniforms=function(){q===void 0&&(q=new zj(d,n));return q};var r;this.getAttributes=function(){r===void 0&&(r=Kj(d,n));return r};this.destroy=function(){d.deleteProgram(n),this.program=void 0};this.name=c.shaderName;this.id=Bj++;this.cacheKey=b;this.usedTimes=1;this.program=n;this.vertexShader=j;this.fragmentShader=k;return this}function bk(a,b,c){var d=[],e=c.isWebGL2,f=c.logarithmicDepthBuffer,g=c.floatVertexTextures,h=c.precision,i=c.maxVertexUniforms,j=c.vertexTextures,k={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"},l=["precision","isWebGL2","supportsVertexTextures","outputEncoding","instancing","map","mapEncoding","matcap","matcapEncoding","envMap","envMapMode","envMapEncoding","envMapCubeUV","lightMap","lightMapEncoding","aoMap","emissiveMap","emissiveMapEncoding","bumpMap","normalMap","objectSpaceNormalMap","tangentSpaceNormalMap","clearcoatMap","clearcoatRoughnessMap","clearcoatNormalMap","displacementMap","specularMap","roughnessMap","metalnessMap","gradientMap","alphaMap","combine","vertexColors","vertexTangents","vertexUvs","uvsVertexOnly","fog","useFog","fogExp2","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","maxMorphTargets","maxMorphNormals","premultipliedAlpha","numDirLights","numPointLights","numSpotLights","numHemiLights","numRectAreaLights","numDirLightShadows","numPointLightShadows","numSpotLightShadows","shadowMapEnabled","shadowMapType","toneMapping","physicallyCorrectLights","alphaTest","doubleSided","flipSided","numClippingPlanes","numClipIntersection","depthPacking","dithering","sheen"];function m(a,b){if(b){b=ni[b];b={name:a.type,uniforms:Bf.clone(b.uniforms),vertexShader:b.vertexShader,fragmentShader:b.fragmentShader}}else b={name:a.type,uniforms:a.uniforms,vertexShader:a.vertexShader,fragmentShader:a.fragmentShader};return b}function n(a){a=a.skeleton;a=a.bones;if(g)return 1024;else{var b=i;b=Math.floor((b-20)/4);b=Math.min(b,a.length);return b0,maxBones:x,useVertexTexture:g,morphTargets:d.morphTargets,morphNormals:d.morphNormals,maxMorphTargets:a.maxMorphTargets,maxMorphNormals:a.maxMorphNormals,numDirLights:i.directional.length,numPointLights:i.point.length,numSpotLights:i.spot.length,numRectAreaLights:i.rectArea.length,numHemiLights:i.hemi.length,numDirLightShadows:i.directionalShadowMap.length,numPointLightShadows:i.pointShadowMap.length,numSpotLightShadows:i.spotShadowMap.length,numClippingPlanes:s,numClipIntersection:t,dithering:d.dithering,shadowMapEnabled:a.shadowMap.enabled&&l.length>0,shadowMapType:a.shadowMap.type,toneMapping:d.toneMapped?a.toneMapping:oa,physicallyCorrectLights:a.physicallyCorrectLights,premultipliedAlpha:d.premultipliedAlpha,alphaTest:d.alphaTest,doubleSided:d.side===r,flipSided:d.side===q,depthPacking:d.depthPacking!==void 0?d.depthPacking:!1,index0AttributeName:d.index0AttributeName,extensionDerivatives:d.extensions&&d.extensions.derivatives,extensionFragDepth:d.extensions&&d.extensions.fragDepth,extensionDrawBuffers:d.extensions&&d.extensions.drawBuffers,extensionShaderTextureLOD:d.extensions&&d.extensions.shaderTextureLOD,rendererExtensionFragDepth:e||b.get("EXT_frag_depth")!==null,rendererExtensionDrawBuffers:e||b.get("WEBGL_draw_buffers")!==null,rendererExtensionShaderTextureLod:e||b.get("EXT_shader_texture_lod")!==null,onBeforeCompile:d.onBeforeCompile};return w};this.getProgramCacheKey=function(b){var c=[];b.shaderID?c.push(b.shaderID):(c.push(b.fragmentShader),c.push(b.vertexShader));if(b.defines!==void 0)for(var d in b.defines)c.push(d),c.push(b.defines[d]);if(b.isRawShaderMaterial===void 0){for(d=0;d1&&c.sort(a||dk),d.length>1&&d.sort(b||ek)}function k(){for(var c=b,d=a.length;cc||e.y>c)&&(e.x>c&&(f.x=Math.floor(c/s.x),e.x=f.x*s.x,r.mapSize.x=f.x),e.y>c&&(f.y=Math.floor(c/s.y),e.y=f.y*s.y,r.mapSize.y=f.y));if(r.map===null&&!r.isPointLightShadow&&this.type===o){s={minFilter:Ka,magFilter:Ka,format:cb};r.map=new id(e.x,e.y,s);r.map.texture.name=q.name+".shadowMap";r.mapPass=new id(e.x,e.y,s);r.camera.updateProjectionMatrix()}if(r.map===null){s={minFilter:Fa,magFilter:Fa,format:cb};r.map=new id(e.x,e.y,s);r.map.texture.name=q.name+".shadowMap";r.camera.updateProjectionMatrix()}a.setRenderTarget(r.map);a.clear();s=r.getViewportCount();for(var t=0;t0);c=!1;b.isSkinnedMesh===!0&&(d.skinning===!0&&(c=!0));b=b.isInstancedMesh===!0;i=l(n,c,b)}else i=m;if(a.localClippingEnabled&&d.clipShadows===!0&&d.clippingPlanes.length!==0){l=i.uuid;n=d.uuid;c=j[l];c===void 0&&(c={},j[l]=c);b=c[n];b===void 0&&(b=i.clone(),c[n]=b);i=b}i.visible=d.visible;i.wireframe=d.wireframe;h===o?i.side=d.shadowSide!==null?d.shadowSide:d.side:i.side=d.shadowSide!==null?d.shadowSide:k[d.side];i.clipShadows=d.clipShadows;i.clippingPlanes=d.clippingPlanes;i.clipIntersection=d.clipIntersection;i.wireframeLinewidth=d.wireframeLinewidth;i.linewidth=d.linewidth;e.isPointLight===!0&&i.isMeshDistanceMaterial===!0&&(i.referencePosition.setFromMatrixPosition(e.matrixWorld),i.nearDistance=f,i.farDistance=g);return i}function A(c,e,f,g,h){if(c.visible===!1)return;var i=c.layers.test(e.layers);if(i&&(c.isMesh||c.isLine||c.isPoints)&&((c.castShadow||c.receiveShadow&&h===o)&&(!c.frustumCulled||d.intersectsObject(c)))){c.modelViewMatrix.multiplyMatrices(f.matrixWorldInverse,c.matrixWorld);i=b.update(c);var j=c.material;if(Array.isArray(j)){var k=i.groups;for(var l=0,m=k.length;l=1):f.indexOf("OpenGL ES")!==-1&&(i=parseFloat(/^OpenGL ES ([0-9])/.exec(f)[1]),Aa=i>=2);var Ba=null,Ca={},Da=new hd(),Ea=new hd();function Fa(b,c,d){var e=new Uint8Array(4),f=a.createTexture();a.bindTexture(b,f);a.texParameteri(b,10241,9728);a.texParameteri(b,10240,9728);for(b=0;be||b.height>e)&&(f=e/Math.max(b.width,b.height));if(f<1||c===!0)if(typeof HTMLImageElement!=="undefined"&&b instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&b instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&b instanceof ImageBitmap){e=c?N.floorPowerOfTwo:Math.floor;c=e(f*b.width);e=e(f*b.height);a===void 0&&(a=p(c,e));f=d?p(c,e):a;f.width=c;f.height=e;d=f.getContext("2d");d.drawImage(b,0,0,c,e);return f}else{"data"in b;return b}return b}function r(a){return N.isPowerOfTwo(a.width)&&N.isPowerOfTwo(a.height)}function s(a){return i?!1:a.wrapS!==Da||a.wrapT!==Da||a.minFilter!==Fa&&a.minFilter!==Ka}function t(a,b){return a.generateMipmaps&&b&&a.minFilter!==Fa&&a.minFilter!==Ka}function u(a,c,d,f){b.generateMipmap(a);a=e.get(c);a.__maxMipLevel=Math.log(Math.max(d,f))*Math.LOG2E}function v(a,d,e){if(i===!1)return d;if(a!==null&&b[a]!==void 0)return b[a];a=d;d===6403&&(e===5126&&(a=33326),e===5131&&(a=33325),e===5121&&(a=33321));d===6407&&(e===5126&&(a=34837),e===5131&&(a=34843),e===5121&&(a=32849));d===6408&&(e===5126&&(a=34836),e===5131&&(a=34842),e===5121&&(a=32856));(a===33325||a===33326||a===34842||a===34836)&&c.get("EXT_color_buffer_float");return a}function w(a){return a===Fa||a===Ga||a===Ia?9728:9729}function x(a){a=a.target;a.removeEventListener("dispose",x);z(a);a.isVideoTexture&&n["delete"](a);h.memory.textures--}function y(a){a=a.target;a.removeEventListener("dispose",y);A(a);h.memory.textures--}function z(a){var c=e.get(a);if(c.__webglInit===void 0)return;b.deleteTexture(c.__webglTexture);e.remove(a)}function A(a){var c=e.get(a),d=e.get(a.texture);if(!a)return;d.__webglTexture!==void 0&&b.deleteTexture(d.__webglTexture);a.depthTexture&&a.depthTexture.dispose();if(a.isWebGLCubeRenderTarget)for(d=0;d<6;d++)b.deleteFramebuffer(c.__webglFramebuffer[d]),c.__webglDepthbuffer&&b.deleteRenderbuffer(c.__webglDepthbuffer[d]);else b.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&b.deleteRenderbuffer(c.__webglDepthbuffer),c.__webglMultisampledFramebuffer&&b.deleteFramebuffer(c.__webglMultisampledFramebuffer),c.__webglColorRenderbuffer&&b.deleteRenderbuffer(c.__webglColorRenderbuffer),c.__webglDepthRenderbuffer&&b.deleteRenderbuffer(c.__webglDepthRenderbuffer);e.remove(a.texture);e.remove(a)}var B=0;function C(){B=0}function D(){var a=B;a>=j;B+=1;return a}function E(a,b){var c=e.get(a);a.isVideoTexture&&M(a);if(a.version>0&&c.__version!==a.version){var f=a.image;if(!(f===void 0))if(!(f.complete===!1)){fa(c,a,b);return}}d.activeTexture(33984+b);d.bindTexture(3553,c.__webglTexture)}function F(a,b){var c=e.get(a);if(a.version>0&&c.__version!==a.version){fa(c,a,b);return}d.activeTexture(33984+b);d.bindTexture(35866,c.__webglTexture)}function aa(a,b){var c=e.get(a);if(a.version>0&&c.__version!==a.version){fa(c,a,b);return}d.activeTexture(33984+b);d.bindTexture(32879,c.__webglTexture)}function ba(c,f){if(c.image.length!==6)return;var h=e.get(c);if(c.version>0&&h.__version!==c.version){ea(h,c);d.activeTexture(33984+f);d.bindTexture(34067,h.__webglTexture);b.pixelStorei(37440,c.flipY);var j=c&&(c.isCompressedTexture||c.image[0].isCompressedTexture),l=c.image[0]&&c.image[0].isDataTexture,m=[];for(var a=0;a<6;a++)!j&&!l?m[a]=q(c.image[a],!1,!0,k):m[a]=l?c.image[a].image:c.image[a];var n=m[0],o=r(n)||i,p=g.convert(c.format),s=g.convert(c.type),w=v(c.internalFormat,p,s);H(34067,c,o);var x;if(j){for(var a=0;a<6;a++){x=m[a].mipmaps;for(j=0;j1||e.get(d).__currentAnisotropy)&&(b.texParameterf(a,g.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(d.anisotropy,f.getMaxAnisotropy())),e.get(d).__currentAnisotropy=d.anisotropy)}}function ea(a,c){a.__webglInit===void 0&&(a.__webglInit=!0,c.addEventListener("dispose",x),a.__webglTexture=b.createTexture(),h.memory.textures++)}function fa(c,e,f){var h=3553;e.isDataTexture2DArray&&(h=35866);e.isDataTexture3D&&(h=32879);ea(c,e);d.activeTexture(33984+f);d.bindTexture(h,c.__webglTexture);b.pixelStorei(37440,e.flipY);b.pixelStorei(37441,e.premultiplyAlpha);b.pixelStorei(3317,e.unpackAlignment);f=s(e)&&r(e.image)===!1;f=q(e.image,f,!1,l);var j=r(f)||i,k=g.convert(e.format),m=g.convert(e.type),n=v(e.internalFormat,k,m);H(h,e,j);var o,p=e.mipmaps;if(e.isDepthTexture)n=6402,i?e.type===Va?n=36012:e.type===Ua?n=33190:e.type===$a?n=35056:n=33189:e.type===Va,e.format===gb&&n===6402&&(e.type!==Sa&&e.type!==Ua&&(e.type=Sa,m=g.convert(e.type))),e.format===hb&&n===6402&&(n=34041,e.type!==$a&&(e.type=$a,m=g.convert(e.type))),d.texImage2D(3553,0,n,f.width,f.height,0,k,m,null);else if(e.isDataTexture)if(p.length>0&&j){for(var a=0,w=p.length;a0&&j){for(var a=0,w=p.length;a=0){var h=e[f];if(h!==void 0){var i=h.normalized,j=h.itemSize,k=la.get(h);if(k===void 0)continue;var l=k.buffer,m=k.type,n=k.bytesPerElement;if(h.isInterleavedBufferAttribute){var o=h.data,p=o.stride,q=h.offset;o&&o.isInstancedInterleavedBuffer?(L.enableAttributeAndDivisor(g,o.meshPerAttribute),b.maxInstancedCount===void 0&&(b.maxInstancedCount=o.meshPerAttribute*o.count)):L.enableAttribute(g);I.bindBuffer(34962,l);L.vertexAttribPointer(g,j,m,i,p*n,q*n)}else h.isInstancedBufferAttribute?(L.enableAttributeAndDivisor(g,h.meshPerAttribute),b.maxInstancedCount===void 0&&(b.maxInstancedCount=h.meshPerAttribute*h.count)):L.enableAttribute(g),I.bindBuffer(34962,l),L.vertexAttribPointer(g,j,m,i,0,0)}else if(f==="instanceMatrix"){var k=la.get(a.instanceMatrix);if(k===void 0)continue;var l=k.buffer,m=k.type;L.enableAttributeAndDivisor(g+0,1);L.enableAttributeAndDivisor(g+1,1);L.enableAttributeAndDivisor(g+2,1);L.enableAttributeAndDivisor(g+3,1);I.bindBuffer(34962,l);I.vertexAttribPointer(g+0,4,m,!1,64,0);I.vertexAttribPointer(g+1,4,m,!1,64,16);I.vertexAttribPointer(g+2,4,m,!1,64,32);I.vertexAttribPointer(g+3,4,m,!1,64,48)}else if(c!==void 0){o=c[f];if(o!==void 0)switch(o.length){case 2:I.vertexAttrib2fv(g,o);break;case 3:I.vertexAttrib3fv(g,o);break;case 4:I.vertexAttrib4fv(g,o);break;default:I.vertexAttrib1fv(g,o)}}}}L.disableUnusedAttributes()}this.compile=function(a,b){m=ra.get(a,b);m.init();a.traverse(function(a){a.isLight&&(m.pushLight(a),a.castShadow&&m.pushShadow(a))});m.setupLights(b);var c={};a.traverse(function(d){if(d.material)if(Array.isArray(d.material))for(var b=0;b=0&&a.numSupportedMorphTargets++}if(a.morphNormals){a.numSupportedMorphNormals=0;for(c=0;c=0&&a.numSupportedMorphNormals++}f=d.uniforms;(!a.isShaderMaterial&&!a.isRawShaderMaterial||a.clipping===!0)&&(d.numClippingPlanes=H.numPlanes,d.numIntersection=H.numIntersection,f.clippingPlanes=H.uniform);d.environment=a.isMeshStandardMaterial?b.environment:null;d.fog=b.fog;d.needsLights=jb(a);d.lightsStateVersion=g;d.needsLights&&(f.ambientLightColor.value=e.state.ambient,f.lightProbe.value=e.state.probe,f.directionalLights.value=e.state.directional,f.directionalLightShadows.value=e.state.directionalShadow,f.spotLights.value=e.state.spot,f.spotLightShadows.value=e.state.spotShadow,f.rectAreaLights.value=e.state.rectArea,f.pointLights.value=e.state.point,f.pointLightShadows.value=e.state.pointShadow,f.hemisphereLights.value=e.state.hemi,f.directionalShadowMap.value=e.state.directionalShadowMap,f.directionalShadowMatrix.value=e.state.directionalShadowMatrix,f.spotShadowMap.value=e.state.spotShadowMap,f.spotShadowMatrix.value=e.state.spotShadowMatrix,f.pointShadowMap.value=e.state.pointShadowMap,f.pointShadowMatrix.value=e.state.pointShadowMatrix);h=d.program.getUniforms();c=zj.seqWithValue(h.seq,f);d.uniformsList=c}function Qa(a,b,c,d){ka.resetTextureUnits();var e=b.fog,f=c.isMeshStandardMaterial?b.environment:null,g=t===null?n.outputEncoding:t.texture.encoding,h=M.get(c),i=m.state.lights;if(ea&&(fa||a!==x)){var j=a===x&&c.id===v;H.setState(c.clippingPlanes,c.clipIntersection,c.clipShadows,a,h,j)}c.version===h.__version?h.program===void 0?Oa(c,b,d):c.fog&&h.fog!==e?Oa(c,b,d):h.environment!==f?Oa(c,b,d):h.needsLights&&h.lightsStateVersion!==i.state.version?Oa(c,b,d):h.numClippingPlanes!==void 0&&(h.numClippingPlanes!==H.numPlanes||h.numIntersection!==H.numIntersection)?Oa(c,b,d):h.outputEncoding!==g&&Oa(c,b,d):(Oa(c,b,d),h.__version=c.version);j=!1;i=!1;g=!1;b=h.program;var k=b.getUniforms(),l=h.uniforms;L.useProgram(b.program)&&(j=!0,i=!0,g=!0);c.id!==v&&(v=c.id,i=!0);if(j||x!==a){k.setValue(I,"projectionMatrix",a.projectionMatrix);K.logarithmicDepthBuffer&&k.setValue(I,"logDepthBufFC",2/(Math.log(a.far+1)/Math.LN2));x!==a&&(x=a,i=!0,g=!0);if(c.isShaderMaterial||c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshStandardMaterial||c.envMap){j=k.map.cameraPosition;j!==void 0&&j.setValue(I,ha.setFromMatrixPosition(a.matrixWorld))}(c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshLambertMaterial||c.isMeshBasicMaterial||c.isMeshStandardMaterial||c.isShaderMaterial)&&k.setValue(I,"isOrthographic",a.isOrthographicCamera===!0);(c.isMeshPhongMaterial||c.isMeshToonMaterial||c.isMeshLambertMaterial||c.isMeshBasicMaterial||c.isMeshStandardMaterial||c.isShaderMaterial||c.skinning)&&k.setValue(I,"viewMatrix",a.matrixWorldInverse)}if(c.skinning){k.setOptional(I,d,"bindMatrix");k.setOptional(I,d,"bindMatrixInverse");j=d.skeleton;if(j){a=j.bones;if(K.floatVertexTextures){if(j.boneTexture===void 0){a=Math.sqrt(a.length*4);a=N.ceilPowerOfTwo(a);a=Math.max(a,4);var o=new Float32Array(a*a*4);o.set(j.boneMatrices);var p=new Lf(o,a,a,cb,Va);j.boneMatrices=o;j.boneTexture=p;j.boneTextureSize=a}k.setValue(I,"boneTexture",j.boneTexture,ka);k.setValue(I,"boneTextureSize",j.boneTextureSize)}else k.setOptional(I,j,"boneMatrices")}}(i||h.receiveShadow!==d.receiveShadow)&&(h.receiveShadow=d.receiveShadow,k.setValue(I,"receiveShadow",d.receiveShadow));i&&(k.setValue(I,"toneMappingExposure",n.toneMappingExposure),k.setValue(I,"toneMappingWhitePoint",n.toneMappingWhitePoint),h.needsLights&&ib(l,g),e&&c.fog&&Ya(l,e),c.isMeshBasicMaterial?Ra(l,c):c.isMeshLambertMaterial?(Ra(l,c),Za(l,c)):c.isMeshToonMaterial?(Ra(l,c),ab(l,c)):c.isMeshPhongMaterial?(Ra(l,c),$a(l,c)):c.isMeshStandardMaterial?(Ra(l,c,f),c.isMeshPhysicalMaterial?db(l,c,f):bb(l,c,f)):c.isMeshMatcapMaterial?(Ra(l,c),eb(l,c)):c.isMeshDepthMaterial?(Ra(l,c),fb(l,c)):c.isMeshDistanceMaterial?(Ra(l,c),gb(l,c)):c.isMeshNormalMaterial?(Ra(l,c),hb(l,c)):c.isLineBasicMaterial?(Sa(l,c),c.isLineDashedMaterial&&Ta(l,c)):c.isPointsMaterial?Ua(l,c):c.isSpriteMaterial?Xa(l,c):c.isShadowMaterial&&(l.color.value.copy(c.color),l.opacity.value=c.opacity),l.ltc_1!==void 0&&(l.ltc_1.value=Y.LTC_1),l.ltc_2!==void 0&&(l.ltc_2.value=Y.LTC_2),zj.upload(I,h.uniformsList,l,ka),c.isShaderMaterial&&(c.uniformsNeedUpdate=!1));c.isShaderMaterial&&c.uniformsNeedUpdate===!0&&(zj.upload(I,h.uniformsList,l,ka),c.uniformsNeedUpdate=!1);c.isSpriteMaterial&&k.setValue(I,"center",d.center);k.setValue(I,"modelViewMatrix",d.modelViewMatrix);k.setValue(I,"normalMatrix",d.normalMatrix);k.setValue(I,"modelMatrix",d.matrixWorld);return b}function Ra(a,b,c){a.opacity.value=b.opacity;b.color&&a.diffuse.value.copy(b.color);b.emissive&&a.emissive.value.copy(b.emissive).multiplyScalar(b.emissiveIntensity);b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);b.specularMap&&(a.specularMap.value=b.specularMap);c=b.envMap||c;c&&(a.envMap.value=c,a.flipEnvMap.value=c.isCubeTexture?-1:1,a.reflectivity.value=b.reflectivity,a.refractionRatio.value=b.refractionRatio,a.maxMipLevel.value=M.get(c).__maxMipLevel);b.lightMap&&(a.lightMap.value=b.lightMap,a.lightMapIntensity.value=b.lightMapIntensity);b.aoMap&&(a.aoMap.value=b.aoMap,a.aoMapIntensity.value=b.aoMapIntensity);var d;b.map?d=b.map:b.specularMap?d=b.specularMap:b.displacementMap?d=b.displacementMap:b.normalMap?d=b.normalMap:b.bumpMap?d=b.bumpMap:b.roughnessMap?d=b.roughnessMap:b.metalnessMap?d=b.metalnessMap:b.alphaMap?d=b.alphaMap:b.emissiveMap&&(d=b.emissiveMap);d!==void 0&&(d.isWebGLRenderTarget&&(d=d.texture),d.matrixAutoUpdate===!0&&d.updateMatrix(),a.uvTransform.value.copy(d.matrix));var e;b.aoMap?e=b.aoMap:b.lightMap&&(e=b.lightMap);e!==void 0&&(e.isWebGLRenderTarget&&(e=e.texture),e.matrixAutoUpdate===!0&&e.updateMatrix(),a.uv2Transform.value.copy(e.matrix))}function Sa(a,b){a.diffuse.value.copy(b.color),a.opacity.value=b.opacity}function Ta(a,b){a.dashSize.value=b.dashSize,a.totalSize.value=b.dashSize+b.gapSize,a.scale.value=b.scale}function Ua(a,b){a.diffuse.value.copy(b.color);a.opacity.value=b.opacity;a.size.value=b.size*E;a.scale.value=D*.5;b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);var c;b.map?c=b.map:b.alphaMap&&(c=b.alphaMap);c!==void 0&&(c.matrixAutoUpdate===!0&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function Xa(a,b){a.diffuse.value.copy(b.color);a.opacity.value=b.opacity;a.rotation.value=b.rotation;b.map&&(a.map.value=b.map);b.alphaMap&&(a.alphaMap.value=b.alphaMap);var c;b.map?c=b.map:b.alphaMap&&(c=b.alphaMap);c!==void 0&&(c.matrixAutoUpdate===!0&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function Ya(a,b){a.fogColor.value.copy(b.color),b.isFog?(a.fogNear.value=b.near,a.fogFar.value=b.far):b.isFogExp2&&(a.fogDensity.value=b.density)}function Za(a,b){b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap)}function $a(a,b){a.specular.value.copy(b.specular),a.shininess.value=Math.max(b.shininess,1e-4),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function ab(a,b){a.specular.value.copy(b.specular),a.shininess.value=Math.max(b.shininess,1e-4),b.gradientMap&&(a.gradientMap.value=b.gradientMap),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function bb(a,b,c){a.roughness.value=b.roughness,a.metalness.value=b.metalness,b.roughnessMap&&(a.roughnessMap.value=b.roughnessMap),b.metalnessMap&&(a.metalnessMap.value=b.metalnessMap),b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias),(b.envMap||c)&&(a.envMapIntensity.value=b.envMapIntensity)}function db(a,b,c){bb(a,b,c),a.reflectivity.value=b.reflectivity,a.clearcoat.value=b.clearcoat,a.clearcoatRoughness.value=b.clearcoatRoughness,b.sheen&&a.sheen.value.copy(b.sheen),b.clearcoatMap&&(a.clearcoatMap.value=b.clearcoatMap),b.clearcoatRoughnessMap&&(a.clearcoatRoughnessMap.value=b.clearcoatRoughnessMap),b.clearcoatNormalMap&&(a.clearcoatNormalScale.value.copy(b.clearcoatNormalScale),a.clearcoatNormalMap.value=b.clearcoatNormalMap,b.side===q&&a.clearcoatNormalScale.value.negate()),a.transparency.value=b.transparency}function eb(a,b){b.matcap&&(a.matcap.value=b.matcap),b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function fb(a,b){b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function gb(a,b){b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias),a.referencePosition.value.copy(b.referencePosition),a.nearDistance.value=b.nearDistance,a.farDistance.value=b.farDistance}function hb(a,b){b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,b.side===q&&(a.bumpScale.value*=-1)),b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),b.side===q&&a.normalScale.value.negate()),b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function ib(a,b){a.ambientLightColor.needsUpdate=b,a.lightProbe.needsUpdate=b,a.directionalLights.needsUpdate=b,a.directionalLightShadows.needsUpdate=b,a.pointLights.needsUpdate=b,a.pointLightShadows.needsUpdate=b,a.spotLights.needsUpdate=b,a.spotLightShadows.needsUpdate=b,a.rectAreaLights.needsUpdate=b,a.hemisphereLights.needsUpdate=b}function jb(a){return a.isMeshLambertMaterial||a.isMeshToonMaterial||a.isMeshPhongMaterial||a.isMeshStandardMaterial||a.isShadowMaterial||a.isShaderMaterial&&a.lights===!0}this.setFramebuffer=function(a){p!==a&&t===null&&I.bindFramebuffer(36160,a),p=a};this.getActiveCubeFace=function(){return r};this.getActiveMipmapLevel=function(){return s};this.getRenderTarget=function(){return t};this.setRenderTarget=function(a,b,c){t=a;r=b;s=c;a&&M.get(a).__webglFramebuffer===void 0&&ka.setupRenderTarget(a);var d=p,e=!1;if(a){var f=M.get(a).__webglFramebuffer;a.isWebGLCubeRenderTarget?(d=f[b||0],e=!0):a.isWebGLMultisampleRenderTarget?d=M.get(a).__webglMultisampledFramebuffer:d=f;z.copy(a.viewport);A.copy(a.scissor);B=a.scissorTest}else z.copy(ba).multiplyScalar(E).floor(),A.copy(ca).multiplyScalar(E).floor(),B=da;u!==d&&(I.bindFramebuffer(36160,d),u=d);L.viewport(z);L.scissor(A);L.setScissorTest(B);if(e){f=M.get(a.texture);I.framebufferTexture2D(36160,36064,34069+(b||0),f.__webglTexture,c||0)}};this.readRenderTargetPixels=function(a,b,c,d,e,f,g){if(!(a&&a.isWebGLRenderTarget))return;var h=M.get(a).__webglFramebuffer;a.isWebGLCubeRenderTarget&&g!==void 0&&(h=h[g]);if(h){g=!1;h!==u&&(I.bindFramebuffer(36160,h),g=!0);try{h=a.texture;var i=h.format;h=h.type;if(i!==cb&&wa.convert(i)!==I.getParameter(35739))return;if(h!==Pa&&wa.convert(h)!==I.getParameter(35738)&&!(h===Va&&(K.isWebGL2||J.get("OES_texture_float")||J.get("WEBGL_color_buffer_float")))&&!(h===Wa&&(K.isWebGL2?J.get("EXT_color_buffer_float"):J.get("EXT_color_buffer_half_float"))))return;I.checkFramebufferStatus(36160)===36053&&(b>=0&&b<=a.width-d&&c>=0&&c<=a.height-e&&I.readPixels(b,c,d,e,wa.convert(i),wa.convert(h),f))}finally{g&&I.bindFramebuffer(36160,u)}}};this.copyFramebufferToTexture=function(a,b,c){c===void 0&&(c=0);var d=Math.pow(2,-c),e=Math.floor(b.image.width*d);d=Math.floor(b.image.height*d);var f=wa.convert(b.format);ka.setTexture2D(b,0);I.copyTexImage2D(3553,c,f,a.x,a.y,e,d,0);L.unbindTexture()};this.copyTextureToTexture=function(a,b,c,d){d===void 0&&(d=0);var e=b.image.width,f=b.image.height,g=wa.convert(c.format),h=wa.convert(c.type);ka.setTexture2D(c,0);b.isDataTexture?I.texSubImage2D(3553,d,a.x,a.y,e,f,g,h,b.image.data):b.isCompressedTexture?I.compressedTexSubImage2D(3553,d,a.x,a.y,b.mipmaps[0].width,b.mipmaps[0].height,g,b.mipmaps[0].data):I.texSubImage2D(3553,d,a.x,a.y,g,h,b.image);d===0&&c.generateMipmaps&&I.generateMipmap(3553);L.unbindTexture()};this.initTexture=function(a){ka.setTexture2D(a,0),L.unbindTexture()};typeof __THREE_DEVTOOLS__!=="undefined"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}function Bk(a,b){this.name="",this.color=new S(a),this.density=b!==void 0?b:25e-5}Object.assign(Bk.prototype,{isFogExp2:!0,clone:function(){return new Bk(this.color,this.density)},toJSON:function(){return{type:"FogExp2",color:this.color.getHex(),density:this.density}}});function Ck(a,b,c){this.name="",this.color=new S(a),this.near=b!==void 0?b:1,this.far=c!==void 0?c:1e3}Object.assign(Ck.prototype,{isFog:!0,clone:function(){return new Ck(this.color,this.near,this.far)},toJSON:function(){return{type:"Fog",color:this.color.getHex(),near:this.near,far:this.far}}});function Dk(a,b){this.array=a,this.stride=b,this.count=a!==void 0?a.length/b:0,this.usage=Rc,this.updateRange={offset:0,count:-1},this.version=0}Object.defineProperty(Dk.prototype,"needsUpdate",{set:function(a){a===!0&&this.version++}});Object.assign(Dk.prototype,{isInterleavedBuffer:!0,onUploadCallback:function(){},setUsage:function(a){this.usage=a;return this},copy:function(a){this.array=new a.array.constructor(a.array);this.count=a.count;this.stride=a.stride;this.usage=a.usage;return this},copyAt:function(b,c,d){b*=this.stride;d*=c.stride;for(var a=0,e=this.stride;ab.far)return;a.push({distance:c,point:Ik.clone(),uv:ye.getUV(Ik,Ok,Pk,Qk,Rk,Sk,Tk,new O()),face:null,object:this})},clone:function(){return new this.constructor(this.material).copy(this)},copy:function(a){R.prototype.copy.call(this,a);a.center!==void 0&&this.center.copy(a.center);return this}});function Vk(a,b,c,d,e,f){Lk.subVectors(a,c).addScalar(.5).multiply(d),e!==void 0?(Mk.x=f*Lk.x-e*Lk.y,Mk.y=e*Lk.x+f*Lk.y):Mk.copy(Lk),a.copy(b),a.x+=Mk.x,a.y+=Mk.y,a.applyMatrix4(Nk)}var Wk=new P(),Xk=new P();function Yk(){R.call(this),this._currentLevel=0,this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]}}),this.autoUpdate=!0}Yk.prototype=Object.assign(Object.create(R.prototype),{constructor:Yk,isLOD:!0,copy:function(b){R.prototype.copy.call(this,b,!1);var c=b.levels;for(var a=0,d=c.length;a0){for(var a=1,d=c.length;a0){Wk.setFromMatrixPosition(this.matrixWorld);c=b.ray.origin.distanceTo(Wk);this.getObjectForDistance(c).raycast(b,a)}},update:function(b){var c=this.levels;if(c.length>1){Wk.setFromMatrixPosition(b.matrixWorld);Xk.setFromMatrixPosition(this.matrixWorld);b=Wk.distanceTo(Xk)/b.zoom;c[0].object.visible=!0;for(var a=1,d=c.length;a=c[a].distance)c[a-1].object.visible=!1,c[a].object.visible=!0;else break;this._currentLevel=a-1;for(;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}else for(var a=0,m=l.length/3-1;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}else if(d.isGeometry){k=d.vertices;m=k.length;for(var a=0;af)continue;i.applyMatrix4(this.matrixWorld);o=c.ray.origin.distanceTo(i);if(oc.far)continue;b.push({distance:o,point:h.clone().applyMatrix4(this.matrixWorld),index:a,face:null,faceIndex:null,object:this})}}},clone:function(){return new this.constructor(this.geometry,this.material).copy(this)}});var pl=new P(),ql=new P();function rl(a,b){ol.call(this,a,b),this.type="LineSegments"}rl.prototype=Object.assign(Object.create(ol.prototype),{constructor:rl,isLineSegments:!0,computeLineDistances:function(){var b=this.geometry;if(b.isBufferGeometry){if(b.index===null){var c=b.attributes.position,d=[];for(var a=0,e=c.count;a0){d=d[e[0]];if(d!==void 0){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(e=0,b=d.length;e0}},clone:function(){return new this.constructor(this.geometry,this.material).copy(this)}});function zl(b,c,d,e,f,a,g){var h=vl.distanceSqToPoint(b);if(hf.far)return;a.push({distance:b,distanceToRay:Math.sqrt(h),point:d,index:c,face:null,object:g})}}function Al(a,b,c,d,e,f,g,h,i){gd.call(this,a,b,c,d,e,f,g,h,i),this.format=g!==void 0?g:bb,this.minFilter=f!==void 0?f:Ka,this.magFilter=e!==void 0?e:Ka,this.generateMipmaps=!1}Al.prototype=Object.assign(Object.create(gd.prototype),{constructor:Al,isVideoTexture:!0,update:function(){var a=this.image;a.readyState>=a.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}});function Bl(a,b,c,d,e,f,g,h,i,j,k,l){gd.call(this,null,f,g,h,i,j,d,e,k,l),this.image={width:b,height:c},this.mipmaps=a,this.flipY=!1,this.generateMipmaps=!1}Bl.prototype=Object.create(gd.prototype);Bl.prototype.constructor=Bl;Bl.prototype.isCompressedTexture=!0;function Cl(a,b,c,d,e,f,g,h,i){gd.call(this,a,b,c,d,e,f,g,h,i),this.needsUpdate=!0}Cl.prototype=Object.create(gd.prototype);Cl.prototype.constructor=Cl;Cl.prototype.isCanvasTexture=!0;function Dl(a,b,c,d,e,f,g,h,i,j){j=j!==void 0?j:gb;if(j!==gb&&j!==hb)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");c===void 0&&j===gb&&(c=Sa);c===void 0&&j===hb&&(c=$a);gd.call(this,null,d,e,f,g,h,j,c,i);this.image={width:a,height:b};this.magFilter=g!==void 0?g:Fa;this.minFilter=h!==void 0?h:Fa;this.flipY=!1;this.generateMipmaps=!1}Dl.prototype=Object.create(gd.prototype);Dl.prototype.constructor=Dl;Dl.prototype.isDepthTexture=!0;function El(b){W.call(this);this.type="WireframeGeometry";var c=[],a,d,e,f,g,h=[0,0],i={},j,k,l,m=["a","b","c"];if(b&&b.isGeometry){var n=b.faces;for(a=0,e=n.length;a=0?(b(r-i,q,l),m.subVectors(k,l)):(b(r+i,q,l),m.subVectors(l,k));q-i>=0?(b(r,q-i,l),n.subVectors(k,l)):(b(r,q+i,l),n.subVectors(l,k));j.crossVectors(m,n).normalize();g.push(j.x,j.y,j.z);h.push(r,q)}}for(a=0;a.9&&g<.1&&(b<.2&&(f[a+0]+=1),c<.2&&(f[a+2]+=1),d<.2&&(f[a+4]+=1))}}function l(a){e.push(a.x,a.y,a.z)}function m(b,c){b=b*3;c.x=a[b+0];c.y=a[b+1];c.z=a[b+2]}function n(){var b=new P(),c=new P(),d=new P(),g=new P(),h=new O(),i=new O(),j=new O();for(var a=0,k=0;a80*d){i=e=b[0];j=c=b[1];for(var a=d;ae&&(e=k),l>c&&(c=l);m=Math.max(e-i,c-j);m=m!==0?1/m:0}$l(g,h,d,i,j,m);return h}};function Yl(a,b,c,d,e){var f;if(e===Bm(a,b,c,d)>0)for(e=b;e=b;e-=d)f=ym(e,a[e],a[e+1],f);f&&qm(f,f.next)&&(zm(f),f=f.next);return f}function Zl(a,b){if(!a)return a;b||(b=a);a=a;var c;do{c=!1;if(!a.steiner&&(qm(a,a.next)||pm(a.prev,a,a.next)===0)){zm(a);a=b=a.prev;if(a===a.next)break;c=!0}else a=a.next}while(c||a!==b);return b}function $l(a,b,c,d,e,f,g){if(!a)return;!g&&f&&jm(a,d,e,f);var h=a,i,j;while(a.prev!==a.next){i=a.prev;j=a.next;if(f?bm(a,d,e,f):am(a)){b.push(i.i/c);b.push(a.i/c);b.push(j.i/c);zm(a);a=j.next;h=j.next;continue}a=j;if(a===h){!g?$l(Zl(a),b,c,d,e,f,1):g===1?(a=cm(Zl(a),b,c),$l(a,b,c,d,e,f,2)):g===2&&dm(a,b,c,d,e,f);break}}}function am(a){var b=a.prev,c=a,d=a.next;if(pm(b,c,d)>=0)return!1;var e=a.next.next;while(e!==a.prev){if(nm(b.x,b.y,c.x,c.y,d.x,d.y,e.x,e.y)&&pm(e.prev,e,e.next)>=0)return!1;e=e.next}return!0}function bm(a,b,c,d){var e=a.prev,f=a,g=a.next;if(pm(e,f,g)>=0)return!1;var h=e.xf.x?e.x>g.x?e.x:g.x:f.x>g.x?f.x:g.x,k=e.y>f.y?e.y>g.y?e.y:g.y:f.y>g.y?f.y:g.y;h=lm(h,i,b,c,d);i=lm(j,k,b,c,d);j=a.prevZ;k=a.nextZ;while(j&&j.z>=h&&k&&k.z<=i){if(j!==a.prev&&j!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,j.x,j.y)&&pm(j.prev,j,j.next)>=0)return!1;j=j.prevZ;if(k!==a.prev&&k!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,k.x,k.y)&&pm(k.prev,k,k.next)>=0)return!1;k=k.nextZ}while(j&&j.z>=h){if(j!==a.prev&&j!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,j.x,j.y)&&pm(j.prev,j,j.next)>=0)return!1;j=j.prevZ}while(k&&k.z<=i){if(k!==a.prev&&k!==a.next&&nm(e.x,e.y,f.x,f.y,g.x,g.y,k.x,k.y)&&pm(k.prev,k,k.next)>=0)return!1;k=k.nextZ}return!0}function cm(a,b,c){var d=a;do{var e=d.prev,f=d.next.next;!qm(e,f)&&rm(e,d,d.next,f)&&vm(e,f)&&vm(f,e)&&(b.push(e.i/c),b.push(d.i/c),b.push(f.i/c),zm(d),zm(d.next),d=a=f);d=d.next}while(d!==a);return Zl(d)}function dm(a,b,c,d,e,f){var g=a;do{var h=g.next.next;while(h!==g.prev){if(g.i!==h.i&&om(g,h)){var i=xm(g,h);g=Zl(g,g.next);i=Zl(i,i.next);$l(g,b,c,d,e,f);$l(i,b,c,d,e,f);return}h=h.next}g=g.next}while(g!==a)}function em(b,c,d,e){var f=[],a,g,h,i;for(a=0,g=c.length;a=c.next.y&&c.next.y!==c.y){var h=c.x+(e-c.y)*(c.next.x-c.x)/(c.next.y-c.y);if(h<=d&&h>f){f=h;if(h===d){if(e===c.y)return c;if(e===c.next.y)return c.next}g=c.x=c.x&&c.x>=b&&d!==c.x&&nm(eg.x||c.x===g.x&&im(g,c)))&&(g=c,j=k)),c=c.next;while(c!==h);return g}function im(a,b){return pm(a.prev,a,b.prev)<0&&pm(b.next,a,a.next)<0}function jm(a,b,c,d){var e=a;do e.z===null&&(e.z=lm(e.x,e.y,b,c,d)),e.prevZ=e.prev,e.nextZ=e.next,e=e.next;while(e!==a);e.prevZ.nextZ=null;e.prevZ=null;km(e)}function km(b){var a,c,d,e,f,g,h,i=1;do{c=b;b=null;f=null;g=0;while(c){g++;d=c;h=0;for(a=0;a0||a>0&&d)h!==0&&(a===0||!d||c.z<=d.z)?(e=c,c=c.nextZ,h--):(e=d,d=d.nextZ,a--),f?f.nextZ=e:b=e,e.prevZ=f,f=e;c=d}f.nextZ=null;i*=2}while(g>1);return b}function lm(a,b,c,d,e){a=32767*(a-c)*e;b=32767*(b-d)*e;a=(a|a<<8)&16711935;a=(a|a<<4)&252645135;a=(a|a<<2)&858993459;a=(a|a<<1)&1431655765;b=(b|b<<8)&16711935;b=(b|b<<4)&252645135;b=(b|b<<2)&858993459;b=(b|b<<1)&1431655765;return a|b<<1}function mm(a){var b=a,c=a;do(b.x=0&&(c-a)*(f-b)-(e-a)*(d-b)>=0&&(e-a)*(h-b)-(g-a)*(f-b)>=0}function om(a,b){return a.next.i!==b.i&&a.prev.i!==b.i&&!um(a,b)&&(vm(a,b)&&vm(b,a)&&wm(a,b)&&(pm(a.prev,a,b.prev)||pm(a,b.prev,b))||qm(a,b)&&pm(a.prev,a,a.next)>0&&pm(b.prev,b,b.next)>0)}function pm(a,b,c){return(b.y-a.y)*(c.x-b.x)-(b.x-a.x)*(c.y-b.y)}function qm(a,b){return a.x===b.x&&a.y===b.y}function rm(a,b,c,d){var e=tm(pm(a,b,c)),f=tm(pm(a,b,d)),g=tm(pm(c,d,a)),h=tm(pm(c,d,b));if(e!==f&&g!==h)return!0;if(e===0&&sm(a,c,b))return!0;if(f===0&&sm(a,d,b))return!0;if(g===0&&sm(c,a,d))return!0;return h===0&&sm(c,b,d)?!0:!1}function sm(a,b,c){return b.x<=Math.max(a.x,c.x)&&b.x>=Math.min(a.x,c.x)&&b.y<=Math.max(a.y,c.y)&&b.y>=Math.min(a.y,c.y)}function tm(a){return a>0?1:a<0?-1:0}function um(a,b){var c=a;do{if(c.i!==a.i&&c.next.i!==a.i&&c.i!==b.i&&c.next.i!==b.i&&rm(c,c.next,a,b))return!0;c=c.next}while(c!==a);return!1}function vm(a,b){return pm(a.prev,a,a.next)<0?pm(a,b,a.next)>=0&&pm(a,a.prev,b)>=0:pm(a,b,a.prev)<0||pm(a,a.next,b)<0}function wm(b,c){var d=b,e=!1,a=(b.x+c.x)/2;c=(b.y+c.y)/2;do d.y>c!==d.next.y>c&&d.next.y!==d.y&&a<(d.next.x-d.x)*(c-d.y)/(d.next.y-d.y)+d.x&&(e=!e),d=d.next;while(d!==b);return e}function xm(a,b){var c=new Am(a.i,a.x,a.y),d=new Am(b.i,b.x,b.y),e=a.next,f=b.prev;a.next=b;b.prev=a;c.next=e;e.prev=c;d.next=c;c.prev=d;f.next=d;d.prev=f;return d}function ym(a,b,c,d){a=new Am(a,b,c);!d?(a.prev=a,a.next=a):(a.next=d.next,a.prev=d,d.next.prev=a,d.next=a);return a}function zm(a){a.next.prev=a.prev,a.prev.next=a.next,a.prevZ&&(a.prevZ.nextZ=a.nextZ),a.nextZ&&(a.nextZ.prevZ=a.prevZ)}function Am(a,b,c){this.i=a,this.x=b,this.y=c,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function Bm(a,b,c,d){var e=0;for(var b=b,f=c-d;b2&&a[b-1].equals(a[0])&&a.pop()}function Em(b,c){for(var a=0;aNumber.EPSILON){i=Math.sqrt(h);var j=Math.sqrt(f*f+g*g),k=b.x-e/i;b=b.y+d/i;i=c.x-g/j;c=c.y+f/j;j=((i-k)*g-(c-b)*f)/(d*g-e*f);i=k+d*j-a.x;c=b+e*j-a.y;k=i*i+c*c;if(k<=2)return new O(i,c);else b=Math.sqrt(k/2)}else{j=!1;d>Number.EPSILON?f>Number.EPSILON&&(j=!0):d<-Number.EPSILON?f<-Number.EPSILON&&(j=!0):Math.sign(e)===Math.sign(g)&&(j=!0);j?(i=-e,c=d,b=Math.sqrt(h)):(i=d,c=e,b=Math.sqrt(h/2))}return new O(i/b,c/b)}p=[];for(var a=0,h=C.length,G=h-1,H=a+1;a=0;da--){G=da/o;H=l*Math.cos(G*Math.PI/2);E=m*Math.sin(G*Math.PI/2)+n;for(a=0,h=C.length;a=0){c=a;d=a-1;d<0&&(d=b.length-1);var e=0,g=i+o*2;for(e=0;e0)&&o.push(v,u,s);(j!==c-1||h0&&s(!0),b>0&&s(!1));this.setIndex(j);this.setAttribute("position",new V(k,3));this.setAttribute("normal",new V(l,3));this.setAttribute("uv",new V(m,2));function r(){var f,r,s=new P(),t=new P(),u=0,v=(b-a)/c;for(r=0;r<=e;r++){var w=[],x=r/e,y=x*(b-a)+a;for(f=0;f<=d;f++){var z=f/d,A=z*h+g,B=Math.sin(A);A=Math.cos(A);t.x=y*B;t.y=-x*c+p;t.z=y*A;k.push(t.x,t.y,t.z);s.set(B,v,A).normalize();l.push(s.x,s.y,s.z);m.push(z,1-x);w.push(n++)}o.push(w)}for(f=0;f=e)continue;i.push(g.times[k]);for(l=0;lb.tracks[a].times[0]&&(l=b.tracks[a].times[0]);for(var a=0;a=f.times[j]){j=j*i;k=nn.arraySlice(f.values,j)}else{j=f.createInterpolant();j.evaluate(b);k=j.resultBuffer}if(g==="quaternion"){j=new kd(k[0],k[1],k[2],k[3]).normalize().conjugate();j.toArray(k)}j=h.times.length;for(var l=0;l=e)){var h=b[1];a=e)break seek}f=c;c=0;break linear_scan}break validate_interval}while(c>>1;ab)--f;++f;if(e!==0||f!==d){e>=f&&(f=Math.max(f,1),e=f-1);a=this.getValueSize();this.times=nn.arraySlice(c,e,f);this.values=nn.arraySlice(this.values,e*a,f*a)}return this},validate:function(){var b=!0,c=this.getValueSize();c-Math.floor(c)!==0&&(b=!1);c=this.times;var d=this.values,e=c.length;e===0&&(b=!1);var f=null;for(var a=0;a!==e;a++){var g=c[a];if(typeof g==="number"&&isNaN(g)){b=!1;break}if(f!==null&&f>g){b=!1;break}f=g}if(d!==void 0&&nn.isTypedArray(d))for(var a=0,g=d.length;a!==g;++a){e=d[a];if(isNaN(e)){b=!1;break}}return b},optimize:function(){var b=nn.arraySlice(this.times),c=nn.arraySlice(this.values),d=this.getValueSize(),e=this.getInterpolation()===gc,f=1,g=b.length-1;for(var a=1;a0){b[f]=b[g];for(var m=g*d,j=f*d,l=0;l!==d;++l)c[j+l]=c[m+l];++f}f!==b.length?(this.times=nn.arraySlice(b,0,f),this.values=nn.arraySlice(c,0,f*d)):(this.times=b,this.values=c);return this},clone:function(){var a=nn.arraySlice(this.times,0),b=nn.arraySlice(this.values,0),c=this.constructor;c=new c(this.name,a,b);c.createInterpolant=this.createInterpolant;return c}});function tn(a,b,c){sn.call(this,a,b,c)}tn.prototype=Object.assign(Object.create(sn.prototype),{constructor:tn,ValueTypeName:"bool",ValueBufferType:Array,DefaultInterpolation:ec,InterpolantFactoryMethodLinear:void 0,InterpolantFactoryMethodSmooth:void 0});function un(a,b,c,d){sn.call(this,a,b,c,d)}un.prototype=Object.assign(Object.create(sn.prototype),{constructor:un,ValueTypeName:"color"});function vn(a,b,c,d){sn.call(this,a,b,c,d)}vn.prototype=Object.assign(Object.create(sn.prototype),{constructor:vn,ValueTypeName:"number"});function wn(a,b,c,d){on.call(this,a,b,c,d)}wn.prototype=Object.assign(Object.create(on.prototype),{constructor:wn,interpolate_:function(a,b,c,d){var e=this.resultBuffer,f=this.sampleValues,g=this.valueSize;a=a*g;c=(c-b)/(d-b);for(d=a+g;a!==d;a+=4)kd.slerpFlat(e,0,f,a-g,f,a,c);return e}});function xn(a,b,c,d){sn.call(this,a,b,c,d)}xn.prototype=Object.assign(Object.create(sn.prototype),{constructor:xn,ValueTypeName:"quaternion",DefaultInterpolation:fc,InterpolantFactoryMethodLinear:function(a){return new wn(this.times,this.values,this.getValueSize(),a)},InterpolantFactoryMethodSmooth:void 0});function yn(a,b,c,d){sn.call(this,a,b,c,d)}yn.prototype=Object.assign(Object.create(sn.prototype),{constructor:yn,ValueTypeName:"string",ValueBufferType:Array,DefaultInterpolation:ec,InterpolantFactoryMethodLinear:void 0,InterpolantFactoryMethodSmooth:void 0});function zn(a,b,c,d){sn.call(this,a,b,c,d)}zn.prototype=Object.assign(Object.create(sn.prototype),{constructor:zn,ValueTypeName:"vector"});function An(a,b,c,d){this.name=a,this.tracks=c,this.duration=b!==void 0?b:-1,this.blendMode=d!==void 0?d:kc,this.uuid=N.generateUUID(),this.duration<0&&this.resetDuration()}function Bn(a){switch(a.toLowerCase()){case"scalar":case"double":case"float":case"number":case"integer":return vn;case"vector":case"vector2":case"vector3":case"vector4":return zn;case"color":return un;case"quaternion":return xn;case"bool":case"boolean":return tn;case"string":return yn}throw new Error("THREE.KeyframeTrack: Unsupported typeName: "+a)}function Cn(a){if(a.type===void 0)throw new Error("THREE.KeyframeTrack: track type undefined, can not parse");var b=Bn(a.type);if(a.times===void 0){var c=[],d=[];nn.flattenJSON(a.keys,c,d,"value");a.times=c;a.values=d}if(b.parse!==void 0)return b.parse(a);else return new b(a.name,a.times,a.values,a.interpolation)}Object.assign(An,{parse:function(b){var c=[],d=b.tracks,e=1/(b.fps||1);for(var a=0,f=d.length;a!==f;++a)c.push(Cn(d[a]).scale(e));return new An(b.name,b.duration,c,b.blendMode)},toJSON:function(b){var c=[],d=b.tracks;b={name:b.name,duration:b.duration,tracks:c,uuid:b.uuid,blendMode:b.blendMode};for(var a=0,e=d.length;a!==e;++a)c.push(sn.toJSON(d[a]));return b},CreateFromMorphTargetSequence:function(b,c,d,e){var f=c.length,g=[];for(var a=0;a1){i=i[1];var j=e[i];j||(e[i]=j=[]);j.push(h)}}j=[];for(i in e)j.push(An.CreateFromMorphTargetSequence(i,e[i],c,d));return j},parseAnimation:function(a,b){if(!a)return null;var c=function(d,e,a,f,g){if(a.length!==0){var b=[],c=[];nn.flattenJSON(a,b,c,f);b.length!==0&&g.push(new d(e,b,c))}},d=[],e=a.name||"default",f=a.length||-1,g=a.fps||30,h=a.blendMode;a=a.hierarchy||[];for(var i=0;i0||a.search(/^data:image/jpeg/)===0;e.format=c?bb:cb;e.needsUpdate=!0;b!==void 0&&b(e)},c,d);return e}});function $(){this.type="Curve",this.arcLengthDivisions=200}Object.assign($.prototype,{getPoint:function(){return null},getPointAt:function(a,b){a=this.getUtoTmapping(a);return this.getPoint(a,b)},getPoints:function(a){a===void 0&&(a=5);var b=[];for(var c=0;c<=a;c++)b.push(this.getPoint(c/a));return b},getSpacedPoints:function(a){a===void 0&&(a=5);var b=[];for(var c=0;c<=a;c++)b.push(this.getPointAt(c/a));return b},getLength:function(){var a=this.getLengths();return a[a.length-1]},getLengths:function(a){a===void 0&&(a=this.arcLengthDivisions);if(this.cacheArcLengths&&this.cacheArcLengths.length===a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;this.cacheArcLengths=b;return b},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(b,c){var d=this.getLengths(),a,e=d.length;c?c=c:c=b*d[e-1];b=0;var f=e-1,g;while(b<=f){a=Math.floor(b+(f-b)/2);g=d[a]-c;if(g<0)b=a+1;else if(g>0)f=a-1;else{f=a;break}}a=f;if(d[a]===c)return a/(e-1);g=d[a];b=d[a+1];f=b-g;d=(c-g)/f;b=(a+d)/(e-1);return b},getTangent:function(a,b){var c=1e-4,d=a-c;a=a+c;d<0&&(d=0);a>1&&(a=1);c=this.getPoint(d);d=this.getPoint(a);a=b||(c.isVector2?new O():new P());a.copy(d).sub(c).normalize();return a},getTangentAt:function(a,b){a=this.getUtoTmapping(a);return this.getTangent(a,b)},computeFrenetFrames:function(b,c){var d=new P(),e=[],f=[],g=[],h=new P(),i=new Q(),a,j;for(a=0;a<=b;a++)j=a/b,e[a]=this.getTangentAt(j,new P()),e[a].normalize();f[0]=new P();g[0]=new P();j=Number.MAX_VALUE;var k=Math.abs(e[0].x),l=Math.abs(e[0].y),m=Math.abs(e[0].z);k<=j&&(j=k,d.set(1,0,0));l<=j&&(j=l,d.set(0,1,0));m<=j&&d.set(0,0,1);h.crossVectors(e[0],d).normalize();f[0].crossVectors(e[0],h);g[0].crossVectors(e[0],f[0]);for(a=1;a<=b;a++)f[a]=f[a-1].clone(),g[a]=g[a-1].clone(),h.crossVectors(e[a-1],e[a]),h.length()>Number.EPSILON&&(h.normalize(),k=Math.acos(N.clamp(e[a-1].dot(e[a]),-1,1)),f[a].applyMatrix4(i.makeRotationAxis(h,k))),g[a].crossVectors(e[a],f[a]);if(c===!0){k=Math.acos(N.clamp(f[0].dot(f[b]),-1,1));k/=b;e[0].dot(h.crossVectors(f[0],f[b]))>0&&(k=-k);for(a=1;a<=b;a++)f[a].applyMatrix4(i.makeRotationAxis(e[a],k*a)),g[a].crossVectors(e[a],f[a])}return{tangents:e,normals:f,binormals:g}},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.arcLengthDivisions=a.arcLengthDivisions;return this},toJSON:function(){var a={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};a.arcLengthDivisions=this.arcLengthDivisions;a.type=this.type;return a},fromJSON:function(a){this.arcLengthDivisions=a.arcLengthDivisions;return this}});function Pn(a,b,c,d,e,f,g,h){$.call(this),this.type="EllipseCurve",this.aX=a||0,this.aY=b||0,this.xRadius=c||1,this.yRadius=d||1,this.aStartAngle=e||0,this.aEndAngle=f||2*Math.PI,this.aClockwise=g||!1,this.aRotation=h||0}Pn.prototype=Object.create($.prototype);Pn.prototype.constructor=Pn;Pn.prototype.isEllipseCurve=!0;Pn.prototype.getPoint=function(a,b){b=b||new O();var c=Math.PI*2,d=this.aEndAngle-this.aStartAngle,e=Math.abs(d)c)d-=c;d0?0:(Math.floor(Math.abs(e)/d)+1)*d:a===0&&e===d-1&&(e=d-2,a=1);var f,g,h;this.closed||e>0?f=c[(e-1)%d]:(Sn.subVectors(c[0],c[1]).add(c[0]),f=Sn);g=c[e%d];h=c[(e+1)%d];this.closed||e+2c.length-2?c.length-1:d+1];c=c[d>c.length-3?c.length-1:d+2];b.set(Xn(a,e.x,f.x,g.x,c.x),Xn(a,e.y,f.y,g.y,c.y));return b};no.prototype.copy=function(b){$.prototype.copy.call(this,b);this.points=[];for(var a=0,c=b.points.length;a=b){var d=c[a]-b,e=this.curves[a],f=e.getLength();d=f===0?0:1-d/f;return e.getPointAt(d)}a++}return null},getLength:function(){var a=this.getCurveLengths();return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;var b=[],c=0;for(var a=0,d=this.curves.length;a1&&!c[c.length-1].equals(c[0])&&c.push(c[0]);return c},copy:function(b){$.prototype.copy.call(this,b);this.curves=[];for(var a=0,c=b.curves.length;a0){b=a.getPoint(0);b.equals(this.currentPoint)||this.lineTo(b.x,b.y)}this.curves.push(a);c=a.getPoint(1);this.currentPoint.copy(c);return this},copy:function(a){po.prototype.copy.call(this,a);this.currentPoint.copy(a.currentPoint);return this},toJSON:function(){var a=po.prototype.toJSON.call(this);a.currentPoint=this.currentPoint.toArray();return a},fromJSON:function(a){po.prototype.fromJSON.call(this,a);this.currentPoint.fromArray(a.currentPoint);return this}});function ro(a){qo.call(this,a),this.uuid=N.generateUUID(),this.type="Shape",this.holes=[]}ro.prototype=Object.assign(Object.create(qo.prototype),{constructor:ro,getPointsHoles:function(b){var c=[];for(var a=0,d=this.holes.length;a0?!0:!1:d.vertexColors=a.vertexColors);if(a.uniforms!==void 0)for(var e in a.uniforms){var f=a.uniforms[e];d.uniforms[e]={};switch(f.type){case"t":d.uniforms[e].value=c(f.value);break;case"c":d.uniforms[e].value=new S().setHex(f.value);break;case"v2":d.uniforms[e].value=new O().fromArray(f.value);break;case"v3":d.uniforms[e].value=new P().fromArray(f.value);break;case"v4":d.uniforms[e].value=new hd().fromArray(f.value);break;case"m3":d.uniforms[e].value=new cd().fromArray(f.value);case"m4":d.uniforms[e].value=new Q().fromArray(f.value);break;default:d.uniforms[e].value=f.value}}a.defines!==void 0&&(d.defines=a.defines);a.vertexShader!==void 0&&(d.vertexShader=a.vertexShader);a.fragmentShader!==void 0&&(d.fragmentShader=a.fragmentShader);if(a.extensions!==void 0)for(f in a.extensions)d.extensions[f]=a.extensions[f];a.shading!==void 0&&(d.flatShading=a.shading===1);a.size!==void 0&&(d.size=a.size);a.sizeAttenuation!==void 0&&(d.sizeAttenuation=a.sizeAttenuation);a.map!==void 0&&(d.map=c(a.map));a.matcap!==void 0&&(d.matcap=c(a.matcap));a.alphaMap!==void 0&&(d.alphaMap=c(a.alphaMap));a.bumpMap!==void 0&&(d.bumpMap=c(a.bumpMap));a.bumpScale!==void 0&&(d.bumpScale=a.bumpScale);a.normalMap!==void 0&&(d.normalMap=c(a.normalMap));a.normalMapType!==void 0&&(d.normalMapType=a.normalMapType);if(a.normalScale!==void 0){e=a.normalScale;Array.isArray(e)===!1&&(e=[e,e]);d.normalScale=new O().fromArray(e)}a.displacementMap!==void 0&&(d.displacementMap=c(a.displacementMap));a.displacementScale!==void 0&&(d.displacementScale=a.displacementScale);a.displacementBias!==void 0&&(d.displacementBias=a.displacementBias);a.roughnessMap!==void 0&&(d.roughnessMap=c(a.roughnessMap));a.metalnessMap!==void 0&&(d.metalnessMap=c(a.metalnessMap));a.emissiveMap!==void 0&&(d.emissiveMap=c(a.emissiveMap));a.emissiveIntensity!==void 0&&(d.emissiveIntensity=a.emissiveIntensity);a.specularMap!==void 0&&(d.specularMap=c(a.specularMap));a.envMap!==void 0&&(d.envMap=c(a.envMap));a.envMapIntensity!==void 0&&(d.envMapIntensity=a.envMapIntensity);a.reflectivity!==void 0&&(d.reflectivity=a.reflectivity);a.refractionRatio!==void 0&&(d.refractionRatio=a.refractionRatio);a.lightMap!==void 0&&(d.lightMap=c(a.lightMap));a.lightMapIntensity!==void 0&&(d.lightMapIntensity=a.lightMapIntensity);a.aoMap!==void 0&&(d.aoMap=c(a.aoMap));a.aoMapIntensity!==void 0&&(d.aoMapIntensity=a.aoMapIntensity);a.gradientMap!==void 0&&(d.gradientMap=c(a.gradientMap));a.clearcoatMap!==void 0&&(d.clearcoatMap=c(a.clearcoatMap));a.clearcoatRoughnessMap!==void 0&&(d.clearcoatRoughnessMap=c(a.clearcoatRoughnessMap));a.clearcoatNormalMap!==void 0&&(d.clearcoatNormalMap=c(a.clearcoatNormalMap));a.clearcoatNormalScale!==void 0&&(d.clearcoatNormalScale=new O().fromArray(a.clearcoatNormalScale));return d},setTextures:function(a){this.textures=a;return this}});var Ho={decodeText:function(b){if(typeof TextDecoder!=="undefined")return new TextDecoder().decode(b);var c="";for(var a=0,d=b.length;a0){b=new En(b);var f=new Mn(b);f.setCrossOrigin(this.crossOrigin);for(var b=0,g=a.length;bNumber.EPSILON){j<0&&(g=b[f],i=-i,h=b[e],j=-j);if(a.yh.y)continue;if(a.y===g.y){if(a.x===g.x)return!0}else{j=j*(a.x-g.x)-i*(a.y-g.y);if(j===0)return!0;if(j<0)continue;d=!d}}else{if(a.y!==g.y)continue;if(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=h.x)return!0}}return d}var f=Cm.isClockWise,g=this.subPaths;if(g.length===0)return[];if(c===!0)return d(g);var h,i,j;c=[];if(g.length===1){i=g[0];j=new ro();j.curves=i.curves;c.push(j);return c}var k=!f(g[0].getPoints());k=b?!k:k;var l=[],m=[],n=[],o=0,p;m[o]=void 0;n[o]=[];for(var a=0,q=g.length;a1){h=!1;p=[];for(k=0,b=m.length;k0&&(h||(n=l))}for(var a=0,g=m.length;a0){this.source.connect(this.filters[0]);for(var a=1,b=this.filters.length;a0){this.source.disconnect(this.filters[0]);for(var a=1,b=this.filters.length;a0&&this._mixBufferRegionAdditive(c,a,this._addIndex*b,1,b);for(g=b,d=b+b;g!==d;++g)if(c[g]!==c[g+b]){f.setValue(c,a);break}},saveOriginalState:function(){var a=this.binding,b=this.buffer,c=this.valueSize,d=c*this._origIndex;a.getValue(b,d);for(var a=c,e=d;a!==e;++a)b[a]=b[d+a%c];this._setIdentity();this.cumulativeWeight=0;this.cumulativeWeightAdditive=0},restoreOriginalState:function(){var a=this.valueSize*3;this.binding.setValue(this.buffer,a)},_setAdditiveIdentityNumeric:function(){var a=this._addIndex*this.valueSize;this.buffer.fill(0,a,a+this.valueSize)},_setAdditiveIdentityQuaternion:function(){this._setAdditiveIdentityNumeric(),this.buffer[this._addIndex*4+3]=1},_setAdditiveIdentityOther:function(){var a=this._origIndex*this.valueSize,b=this._addIndex*this.valueSize;this.buffer.copyWithin(b,a,this.valueSize)},_select:function(a,b,c,d,e){if(d>=.5)for(d=0;d!==e;++d)a[b+d]=a[c+d]},_slerp:function(a,b,c,d){kd.slerpFlat(a,b,a,b,a,c,d)},_slerpAdditive:function(a,b,c,d,e){e=this._workIndex*e;kd.multiplyQuaternionsFlat(a,e,a,b,a,c);kd.slerpFlat(a,b,a,b,a,e,d)},_lerp:function(b,c,d,e,f){var g=1-e;for(var a=0;a!==f;++a){var h=c+a;b[h]=b[h]*g+b[d+a]*e}},_lerpAdditive:function(b,c,d,e,f){for(var a=0;a!==f;++a){var g=c+a;b[g]=b[g]+b[d+a]*e}}});Tf="\[\]\.:\/";var rp=new RegExp("["+Tf+"]","g");Uf="[^"+Tf+"]";Vf="[^"+Tf.replace("\.","")+"]";Wf=/((?:WC+[/:])*)/.source.replace("WC",Uf);Xf=/(WCOD+)?/.source.replace("WCOD",Vf);Yf=/(?:.(WC+)(?:[(.+)])?)?/.source.replace("WC",Uf);Zf=/.(WC+)(?:[(.+)])?/.source.replace("WC",Uf);var sp=new RegExp("^"+Wf+Xf+Yf+Zf+"$"),tp=["material","materials","bones"];function up(a,b,c){c=c||vp.parseTrackName(b);this._targetGroup=a;this._bindings=a.subscribe_(b,c)}Object.assign(up.prototype,{getValue:function(a,b){this.bind();var c=this._targetGroup.nCachedObjects_;c=this._bindings[c];c!==void 0&&c.getValue(a,b)},setValue:function(b,c){var d=this._bindings;for(var a=this._targetGroup.nCachedObjects_,e=d.length;a!==e;++a)d[a].setValue(b,c)},bind:function(){var b=this._bindings;for(var a=this._targetGroup.nCachedObjects_,c=b.length;a!==c;++a)b[a].bind()},unbind:function(){var b=this._bindings;for(var a=this._targetGroup.nCachedObjects_,c=b.length;a!==c;++a)b[a].unbind()}});function vp(a,b,c){this.path=b,this.parsedPath=c||vp.parseTrackName(b),this.node=vp.findNode(a,this.parsedPath.nodeName)||a,this.rootNode=a}Object.assign(vp,{Composite:up,create:function(a,b,c){if(!(a&&a.isAnimationObjectGroup))return new vp(a,b,c);else return new vp.Composite(a,b,c)},sanitizeNodeName:function(a){return a.replace(/s/g,"_").replace(rp,"")},parseTrackName:function(a){var b=sp.exec(a);if(!b)throw new Error("PropertyBinding: Cannot parse trackName: "+a);b={nodeName:b[2],objectName:b[3],objectIndex:b[4],propertyName:b[5],propertyIndex:b[6]};var c=b.nodeName&&b.nodeName.lastIndexOf(".");if(c!==void 0&&c!==-1){var d=b.nodeName.substring(c+1);tp.indexOf(d)!==-1&&(b.nodeName=b.nodeName.substring(0,c),b.objectName=d)}if(b.propertyName===null||b.propertyName.length===0)throw new Error("PropertyBinding: can not parse propertyName from trackName: "+a);return b},findNode:function(a,b){if(!b||b===""||b==="."||b===-1||b===a.name||b===a.uuid)return a;if(a.skeleton){var c=a.skeleton.getBoneByName(b);if(c!==void 0)return c}if(a.children){var d=function(c){for(var a=0;a=c){var k=c++,l=b[k];d[l.uuid]=j;b[j]=l;d[i]=k;b[k]=h;for(var m=0,n=f;m!==n;++m){var o=e[m],p=o[k],q=o[j];o[j]=p;o[k]=q}}}this.nCachedObjects_=c},uncache:function(){var b=this._objects,c=b.length,d=this.nCachedObjects_,e=this._indicesByUUID,f=this._bindings,g=f.length;for(var a=0,h=arguments.length;a!==h;++a){var i=arguments[a],j=i.uuid,k=e[j];if(k!==void 0){delete e[j];if(k0){b=this._interpolants;a=this._propertyBindings;switch(this.blendMode){case lc:for(var f=0,g=b.length;f!==g;++f)b[f].evaluate(c),a[f].accumulateAdditive(e);break;case kc:default:for(var f=0,g=b.length;f!==g;++f)b[f].evaluate(c),a[f].accumulate(d,e)}}},_updateWeight:function(a){var b=0;if(this.enabled){b=this.weight;var c=this._weightInterpolant;if(c!==null){var d=c.evaluate(a)[0];b*=d;a>c.parameterPositions[1]&&(this.stopFading(),d===0&&(this.enabled=!1))}}this._effectiveWeight=b;return b},_updateTimeScale:function(a){var b=0;if(!this.paused){b=this.timeScale;var c=this._timeScaleInterpolant;if(c!==null){var d=c.evaluate(a)[0];b*=d;a>c.parameterPositions[1]&&(this.stopWarping(),b===0?this.paused=!0:this.timeScale=b)}}this._effectiveTimeScale=b;return b},_updateTime:function(a){var b=this.time+a,c=this._clip.duration,d=this.loop,e=this._loopCount,f=d===dc;if(a===0)return e===-1?b:f&&(e&1)===1?c-b:b;if(d===bc){e===-1&&(this._loopCount=0,this._setEndings(!0,!0,!1));handle_stop:{if(b>=c)b=c;else if(b<0)b=0;else{this.time=b;break handle_stop}this.clampWhenFinished?this.paused=!0:this.enabled=!1;this.time=b;this._mixer.dispatchEvent({type:"finished",action:this,direction:a<0?-1:1})}}else{e===-1&&(a>=0?(e=0,this._setEndings(!0,this.repetitions===0,f)):this._setEndings(this.repetitions===0,!0,f));if(b>=c||b<0){d=Math.floor(b/c);b-=c*d;e+=Math.abs(d);var g=this.repetitions-e;if(g<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,b=a>0?c:0,this.time=b,this._mixer.dispatchEvent({type:"finished",action:this,direction:a>0?1:-1});else{if(g===1){g=a<0;this._setEndings(g,!g,f)}else this._setEndings(!1,!1,f);this._loopCount=e;this.time=b;this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:d})}}else this.time=b;if(f&&(e&1)===1)return c-b}return b},_setEndings:function(a,b,c){var d=this._interpolantSettings;c?(d.endingStart=ic,d.endingEnd=ic):(a?d.endingStart=this.zeroSlopeAtStart?ic:hc:d.endingStart=jc,b?d.endingEnd=this.zeroSlopeAtEnd?ic:hc:d.endingEnd=jc)},_scheduleFading:function(a,b,c){var d=this._mixer,e=d.time,f=this._weightInterpolant;f===null&&(f=d._lendControlInterpolant(),this._weightInterpolant=f);d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}});function yp(a){this._root=a,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1}yp.prototype=Object.assign(Object.create($c.prototype),{constructor:yp,_bindAction:function(a,b){var c=a._localRoot||this._root,d=a._clip.tracks,e=d.length,f=a._propertyBindings;a=a._interpolants;var g=c.uuid,h=this._bindingsByRootAndName,i=h[g];i===void 0&&(i={},h[g]=i);for(h=0;h!==e;++h){var j=d[h],k=j.name,l=i[k];if(l!==void 0)f[h]=l;else{l=f[h];if(l!==void 0){l._cacheIndex===null&&(++l.referenceCount,this._addInactiveBinding(l,g,k));continue}var m=b&&b._propertyBindings[h].binding.parsedPath;l=new qp(vp.create(c,k,m),j.ValueTypeName,j.getValueSize());++l.referenceCount;this._addInactiveBinding(l,g,k);f[h]=l}a[h].resultBuffer=l.buffer}},_activateAction:function(a){if(!this._isActiveAction(a)){if(a._cacheIndex===null){var b=(a._localRoot||this._root).uuid,c=a._clip.uuid,d=this._actionsByClip[c];this._bindAction(a,d&&d.knownActions[0]);this._addInactiveAction(a,c,b)}d=a._propertyBindings;for(c=0,b=d.length;c!==b;++c){var e=d[c];e.useCount++===0&&(this._lendBinding(e),e.saveOriginalState())}this._lendAction(a)}},_deactivateAction:function(b){if(this._isActiveAction(b)){var c=b._propertyBindings;for(var a=0,d=c.length;a!==d;++a){var e=c[a];--e.useCount===0&&(e.restoreOriginalState(),this._takeBackBinding(e))}this._takeBackAction(b)}},_initMemoryManager:function(){this._actions=[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;var a=this;this.stats={actions:{get total(){return a._actions.length},get inUse(){return a._nActiveActions}},bindings:{get total(){return a._bindings.length},get inUse(){return a._nActiveBindings}},controlInterpolants:{get total(){return a._controlInterpolants.length},get inUse(){return a._nActiveControlInterpolants}}}},_isActiveAction:function(a){a=a._cacheIndex;return a!==null&&athis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y},getParameter:function(a,b){b===void 0&&(b=new O());return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y?!1:!0},clampPoint:function(a,b){b===void 0&&(b=new O());return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(a){var b=Gp.copy(a).clamp(this.min,this.max);return b.sub(a).length()},intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});var Ip=new P(),Jp=new P();function Kp(a,b){this.start=a!==void 0?a:new P(),this.end=b!==void 0?b:new P()}Object.assign(Kp.prototype,{set:function(a,b){this.start.copy(a);this.end.copy(b);return this},clone:function(){return new this.constructor().copy(this)},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},getCenter:function(a){a===void 0&&(a=new P());return a.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){a===void 0&&(a=new P());return a.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,b){b===void 0&&(b=new P());return this.delta(b).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(a,b){Ip.subVectors(a,this.start);Jp.subVectors(this.end,this.start);a=Jp.dot(Jp);var c=Jp.dot(Ip);c=c/a;b&&(c=N.clamp(c,0,1));return c},closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c===void 0&&(c=new P());return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)}});function Lp(a){R.call(this),this.material=a,this.render=function(){}}Lp.prototype=Object.create(R.prototype);Lp.prototype.constructor=Lp;Lp.prototype.isImmediateRenderObject=!0;var Mp=new P();function Np(b,c){R.call(this);this.light=b;this.light.updateMatrixWorld();this.matrix=b.matrixWorld;this.matrixAutoUpdate=!1;this.color=c;b=new W();c=[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];for(var a=0,d=1,e=32;a.99999)this.quaternion.set(0,0,0,1);else if(a.y<-.99999)this.quaternion.set(1,0,0,0);else{lq.set(a.z,0,-a.x).normalize();a=Math.acos(a.y);this.quaternion.setFromAxisAngle(lq,a)}};oq.prototype.setLength=function(a,b,c){b===void 0&&(b=.2*a),c===void 0&&(c=.2*b),this.line.scale.set(1,Math.max(1e-4,a-b),1),this.line.updateMatrix(),this.cone.scale.set(c,b,c),this.cone.position.y=a,this.cone.updateMatrix()};oq.prototype.setColor=function(a){this.line.material.color.set(a),this.cone.material.color.set(a)};oq.prototype.copy=function(a){R.prototype.copy.call(this,a,!1);this.line.copy(a.line);this.cone.copy(a.cone);return this};oq.prototype.clone=function(){return new this.constructor().copy(this)};function pq(a){a=a||1;a=[0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a];var b=[1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],c=new W();c.setAttribute("position",new V(a,3));c.setAttribute("color",new V(b,3));a=new il({vertexColors:!0,toneMapped:!1});rl.call(this,c,a);this.type="AxesHelper"}pq.prototype=Object.create(rl.prototype);pq.prototype.constructor=pq;var qq=4,rq=8,sq=Math.pow(2,rq),tq=[.125,.215,.35,.446,.526,.582],uq=rq-qq+1+tq.length,vq=20,wq={};wq[pc]=0;wq[qc]=1;wq[sc]=2;wq[uc]=3;wq[vc]=4;wq[wc]=5;wq[rc]=6;var xq=new zo();$f=Fq();var yq=$f._lodPlanes,zq=$f._sizeLods,Aq=$f._sigmas,Bq=null;ag=(1+Math.sqrt(5))/2;bg=1/ag;var Cq=[new P(1,1,1),new P(-1,1,1),new P(1,1,-1),new P(-1,1,-1),new P(0,ag,bg),new P(0,ag,-bg),new P(bg,0,ag),new P(-bg,0,ag),new P(ag,bg,0),new P(-ag,bg,0)];function Dq(a){this._renderer=a,this._pingPongRenderTarget=null,this._blurMaterial=Iq(vq),this._equirectShader=null,this._cubemapShader=null,this._compileMaterial(this._blurMaterial)}Dq.prototype={constructor:Dq,fromScene:function(a,b,c,d){b===void 0&&(b=0);c===void 0&&(c=.1);d===void 0&&(d=100);Bq=this._renderer.getRenderTarget();var e=this._allocateTargets();this._sceneToCubeUV(a,c,d,e);b>0&&this._blur(e,0,0,b);this._applyPMREM(e);this._cleanup(e);return e},fromEquirectangular:function(a){a.magFilter=Fa;a.minFilter=Fa;a.generateMipmaps=!1;return this.fromCubemap(a)},fromCubemap:function(a){Bq=this._renderer.getRenderTarget();var b=this._allocateTargets(a);this._textureToCubeUV(a,b);this._applyPMREM(b);this._cleanup(b);return b},compileCubemapShader:function(){this._cubemapShader===null&&(this._cubemapShader=Kq(),this._compileMaterial(this._cubemapShader))},compileEquirectangularShader:function(){this._equirectShader===null&&(this._equirectShader=Jq(),this._compileMaterial(this._equirectShader))},dispose:function(){this._blurMaterial.dispose();this._cubemapShader!==null&&this._cubemapShader.dispose();this._equirectShader!==null&&this._equirectShader.dispose();for(var a=0;a2?sq:0,sq,sq);e.setRenderTarget(f);e.render(c,a)}e.toneMapping=h;e.toneMappingExposure=i;e.outputEncoding=g;e.setClearColor(j,k);c.scale.z*=-1},_textureToCubeUV:function(a,b){var c=new Ld(),d=this._renderer;a.isCubeTexture?this._cubemapShader==null&&(this._cubemapShader=Kq()):this._equirectShader==null&&(this._equirectShader=Jq());var e=a.isCubeTexture?this._cubemapShader:this._equirectShader;c.add(new rf(yq[0],e));e=e.uniforms;e.envMap.value=a;a.isCubeTexture||e.texelSize.value.set(1/a.image.width,1/a.image.height);e.inputEncoding.value=wq[a.encoding];e.outputEncoding.value=wq[b.texture.encoding];Hq(b,0,0,3*sq,2*sq);d.setRenderTarget(b);d.render(c,xq)},_applyPMREM:function(b){var c=this._renderer,d=c.autoClear;c.autoClear=!1;for(var a=1;avq;k=[];var o=0;for(var a=0;arq-qq?e-rq+qq:0);Hq(c,p,n,3*q,2*q);i.setRenderTarget(c);i.render(l,xq)}};function Eq(a){return a===void 0||a.type!==Pa?!1:a.encoding===pc||a.encoding===qc||a.encoding===rc}function Fq(){var b=[],c=[],d=[],e=rq;for(var a=0;arq-qq?g=tq[a-rq+qq-1]:a==0&&(g=0);d.push(g);g=1/(f-1);f=-g/2;g=1+g/2;f=[f,f,g,f,g,g,f,f,g,g,f,g];g=6;var h=6,i=3,j=2,k=1,l=new Float32Array(i*h*g),m=new Float32Array(j*h*g),n=new Float32Array(k*h*g);for(var o=0;o2?0:-1;p=[p,q,0,p+2/3,q,0,p+2/3,q+1,0,p,q,0,p+2/3,q+1,0,p,q+1,0];l.set(p,i*h*o);m.set(f,j*h*o);q=[o,o,o,o,o,o];n.set(q,k*h*o)}p=new W();p.setAttribute("position",new U(l,i));p.setAttribute("uv",new U(m,j));p.setAttribute("faceIndex",new U(n,k));b.push(p);e>qq&&e--}return{_lodPlanes:b,_sizeLods:c,_sigmas:d}}function Gq(a){a=new id(3*sq,3*sq,a);a.texture.mapping=Aa;a.texture.name="PMREM.cubeUv";a.scissorTest=!0;return a}function Hq(a,b,c,d,e){a.viewport.set(b,c,d,e),a.scissor.set(b,c,d,e)}function Iq(a){var b=new Float32Array(a),c=new P(0,1,0);a=new cn({defines:{n:a},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:b},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:c},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[n]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; "+Mq()+" #define ENVMAP_TYPE_CUBE_UV #include vec3 getSample(float theta, vec3 axis) { float cosTheta = cos(theta); // Rodrigues" axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross(axis, vOutputDirection) * sin(theta) + axis * dot(axis, vOutputDirection) * (1.0 - cosTheta); return bilinearCubeUV(envMap, sampleDirection, mipInt); } void main() { vec3 axis = latitudinal ? poleAxis : cross(poleAxis, vOutputDirection); if (all(equal(axis, vec3(0.0)))) axis = vec3(vOutputDirection.z, 0.0, - vOutputDirection.x); axis = normalize(axis); gl_FragColor = vec4(0.0); gl_FragColor.rgb += weights[0] * getSample(0.0, axis); for (int i = 1; i < n; i++) { if (i >= samples) break; float theta = dTheta * float(i); gl_FragColor.rgb += weights[i] * getSample(-1.0 * theta, axis); gl_FragColor.rgb += weights[i] * getSample(theta, axis); } gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="SphericalGaussianBlur";return a}function Jq(){var a=new O(1,1);a=new cn({uniforms:{envMap:{value:null},texelSize:{value:a},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform vec2 texelSize; "+Mq()+" #define RECIPROCAL_PI 0.31830988618 #define RECIPROCAL_PI2 0.15915494 void main() { gl_FragColor = vec4(0.0); vec3 outputDirection = normalize(vOutputDirection); vec2 uv; uv.y = asin(clamp(outputDirection.y, -1.0, 1.0)) * RECIPROCAL_PI + 0.5; uv.x = atan(outputDirection.z, outputDirection.x) * RECIPROCAL_PI2 + 0.5; vec2 f = fract(uv / texelSize - 0.5); uv -= f * texelSize; vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.x += texelSize.x; vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.y += texelSize.y; vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb; uv.x -= texelSize.x; vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb; vec3 tm = mix(tl, tr, f.x); vec3 bm = mix(bl, br, f.x); gl_FragColor.rgb = mix(tm, bm, f.y); gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="EquirectangularToCubeUV";return a}function Kq(){var a=new cn({uniforms:{envMap:{value:null},inputEncoding:{value:wq[pc]},outputEncoding:{value:wq[pc]}},vertexShader:Lq(),fragmentShader:" precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform samplerCube envMap; "+Mq()+" void main() { gl_FragColor = vec4(0.0); gl_FragColor.rgb = envMapTexelToLinear(textureCube(envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ))).rgb; gl_FragColor = linearToOutputTexel(gl_FragColor); } ",blending:u,depthTest:!1,depthWrite:!1});a.type="CubemapToCubeUV";return a}function Lq(){return" precision mediump float; precision mediump int; attribute vec3 position; attribute vec2 uv; attribute float faceIndex; varying vec3 vOutputDirection; vec3 getDirection(vec2 uv, float face) { uv = 2.0 * uv - 1.0; vec3 direction = vec3(uv, 1.0); if (face == 0.0) { direction = direction.zyx; direction.z *= -1.0; } else if (face == 1.0) { direction = direction.xzy; direction.z *= -1.0; } else if (face == 3.0) { direction = direction.zyx; direction.x *= -1.0; } else if (face == 4.0) { direction = direction.xzy; direction.y *= -1.0; } else if (face == 5.0) { direction.xz *= -1.0; } return direction; } void main() { vOutputDirection = getDirection(uv, faceIndex); gl_Position = vec4( position, 1.0 ); } "}function Mq(){return" uniform int inputEncoding; uniform int outputEncoding; #include vec4 inputTexelToLinear(vec4 value){ if(inputEncoding == 0){ return value; }else if(inputEncoding == 1){ return sRGBToLinear(value); }else if(inputEncoding == 2){ return RGBEToLinear(value); }else if(inputEncoding == 3){ return RGBMToLinear(value, 7.0); }else if(inputEncoding == 4){ return RGBMToLinear(value, 16.0); }else if(inputEncoding == 5){ return RGBDToLinear(value, 256.0); }else{ return GammaToLinear(value, 2.2); } } vec4 linearToOutputTexel(vec4 value){ if(outputEncoding == 0){ return value; }else if(outputEncoding == 1){ return LinearTosRGB(value); }else if(outputEncoding == 2){ return LinearToRGBE(value); }else if(outputEncoding == 3){ return LinearToRGBM(value, 7.0); }else if(outputEncoding == 4){ return LinearToRGBM(value, 16.0); }else if(outputEncoding == 5){ return LinearToRGBD(value, 256.0); }else{ return LinearToGamma(value, 2.2); } } vec4 envMapTexelToLinear(vec4 color) { return inputTexelToLinear(color); } "}function Nq(a,b,c,d,e,f,g){return new Fe(a,b,c,e,f,g)}cg=0;dg=1;eg=0;fg=1;gg=2;function Oq(a){return a}function Pq(a){a===void 0&&(a=[]);a.isMultiMaterial=!0;a.materials=a;a.clone=function(){return a.slice()};return a}function Qq(a,b){return new yl(a,b)}function Rq(a){return new Uk(a)}function Sq(a,b){return new yl(a,b)}function Tq(a){return new tl(a)}function Uq(a){return new tl(a)}function Vq(a){return new tl(a)}function Wq(a,b,c){return new P(a,b,c)}function Xq(a,b){return new U(a,b).setUsage(Sc)}function Yq(a,b){return new Je(a,b)}function Zq(a,b){return new Ke(a,b)}function $q(a,b){return new Le(a,b)}function ar(a,b){return new Me(a,b)}function br(a,b){return new Ne(a,b)}function cr(a,b){return new Oe(a,b)}function dr(a,b){return new Pe(a,b)}function er(a,b){return new V(a,b)}function fr(a,b){return new Qe(a,b)}$.create=function(a,b){a.prototype=Object.create($.prototype);a.prototype.constructor=a;a.prototype.getPoint=b;return a};Object.assign(po.prototype,{createPointsGeometry:function(a){a=this.getPoints(a);return this.createGeometry(a)},createSpacedPointsGeometry:function(a){a=this.getSpacedPoints(a);return this.createGeometry(a)},createGeometry:function(b){var c=new X();for(var a=0,d=b.length;a-----
ThreeJS.r93",[],(function $module_ThreeJS_r93(global,require,requireDynamic,requireLazy,module,exports){ "use strict"; var exports = {}; (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (factory((global.THREE = {}))); }(this, (function (exports) { "use strict"; // Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } if ( Number.isInteger === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger Number.isInteger = function ( value ) { return typeof value === "number" && isFinite( value ) && Math.floor( value ) === value; }; } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( "name" in Function.prototype === false ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, "name", { get: function () { return this.toString().match( /^s*functions*([^(s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( "Cannot convert undefined or null to object" ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; var listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } }, hasEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return false; var listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { var index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; var array = listenerArray.slice( 0 ); for ( var i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } } } } ); var REVISION = "93"; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; var CullFaceFront = 2; var CullFaceFrontBack = 3; var FrontFaceDirectionCW = 0; var FrontFaceDirectionCCW = 1; var BasicShadowMap = 0; var PCFShadowMap = 1; var PCFSoftShadowMap = 2; var FrontSide = 0; var BackSide = 1; var DoubleSide = 2; var FlatShading = 1; var SmoothShading = 2; var NoColors = 0; var FaceColors = 1; var VertexColors = 2; var NoBlending = 0; var NormalBlending = 1; var AdditiveBlending = 2; var SubtractiveBlending = 3; var MultiplyBlending = 4; var CustomBlending = 5; var AddEquation = 100; var SubtractEquation = 101; var ReverseSubtractEquation = 102; var MinEquation = 103; var MaxEquation = 104; var ZeroFactor = 200; var OneFactor = 201; var SrcColorFactor = 202; var OneMinusSrcColorFactor = 203; var SrcAlphaFactor = 204; var OneMinusSrcAlphaFactor = 205; var DstAlphaFactor = 206; var OneMinusDstAlphaFactor = 207; var DstColorFactor = 208; var OneMinusDstColorFactor = 209; var SrcAlphaSaturateFactor = 210; var NeverDepth = 0; var AlwaysDepth = 1; var LessDepth = 2; var LessEqualDepth = 3; var EqualDepth = 4; var GreaterEqualDepth = 5; var GreaterDepth = 6; var NotEqualDepth = 7; var MultiplyOperation = 0; var MixOperation = 1; var AddOperation = 2; var NoToneMapping = 0; var LinearToneMapping = 1; var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; var EquirectangularReflectionMapping = 303; var EquirectangularRefractionMapping = 304; var SphericalReflectionMapping = 305; var CubeUVReflectionMapping = 306; var CubeUVRefractionMapping = 307; var RepeatWrapping = 1000; var ClampToEdgeWrapping = 1001; var MirroredRepeatWrapping = 1002; var NearestFilter = 1003; var NearestMipMapNearestFilter = 1004; var NearestMipMapLinearFilter = 1005; var LinearFilter = 1006; var LinearMipMapNearestFilter = 1007; var LinearMipMapLinearFilter = 1008; var UnsignedByteType = 1009; var ByteType = 1010; var ShortType = 1011; var UnsignedShortType = 1012; var IntType = 1013; var UnsignedIntType = 1014; var FloatType = 1015; var HalfFloatType = 1016; var UnsignedShort4444Type = 1017; var UnsignedShort5551Type = 1018; var UnsignedShort565Type = 1019; var UnsignedInt248Type = 1020; var AlphaFormat = 1021; var RGBFormat = 1022; var RGBAFormat = 1023; var LuminanceFormat = 1024; var LuminanceAlphaFormat = 1025; var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; var RGB_S3TC_DXT1_Format = 33776; var RGBA_S3TC_DXT1_Format = 33777; var RGBA_S3TC_DXT3_Format = 33778; var RGBA_S3TC_DXT5_Format = 33779; var RGB_PVRTC_4BPPV1_Format = 35840; var RGB_PVRTC_2BPPV1_Format = 35841; var RGBA_PVRTC_4BPPV1_Format = 35842; var RGBA_PVRTC_2BPPV1_Format = 35843; var RGB_ETC1_Format = 36196; var RGBA_ASTC_4x4_Format = 37808; var RGBA_ASTC_5x4_Format = 37809; var RGBA_ASTC_5x5_Format = 37810; var RGBA_ASTC_6x5_Format = 37811; var RGBA_ASTC_6x6_Format = 37812; var RGBA_ASTC_8x5_Format = 37813; var RGBA_ASTC_8x6_Format = 37814; var RGBA_ASTC_8x8_Format = 37815; var RGBA_ASTC_10x5_Format = 37816; var RGBA_ASTC_10x6_Format = 37817; var RGBA_ASTC_10x8_Format = 37818; var RGBA_ASTC_10x10_Format = 37819; var RGBA_ASTC_12x10_Format = 37820; var RGBA_ASTC_12x12_Format = 37821; var LoopOnce = 2200; var LoopRepeat = 2201; var LoopPingPong = 2202; var InterpolateDiscrete = 2300; var InterpolateLinear = 2301; var InterpolateSmooth = 2302; var ZeroCurvatureEnding = 2400; var ZeroSlopeEnding = 2401; var WrapAroundEnding = 2402; var TrianglesDrawMode = 0; var TriangleStripDrawMode = 1; var TriangleFanDrawMode = 2; var LinearEncoding = 3000; var sRGBEncoding = 3001; var GammaEncoding = 3007; var RGBEEncoding = 3002; var LogLuvEncoding = 3003; var RGBM7Encoding = 3004; var RGBM16Encoding = 3005; var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var _Math = { DEG2RAD: Math.PI / 180, RAD2DEG: 180 / Math.PI, generateUUID: ( function () { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 var lut = []; for ( var i = 0; i < 256; i ++ ) { lut[ i ] = ( i < 16 ? "0" : "" ) + ( i ).toString( 16 ); } return function generateUUID() { var d0 = Math.random() * 0xffffffff | 0; var d1 = Math.random() * 0xffffffff | 0; var d2 = Math.random() * 0xffffffff | 0; var d3 = Math.random() * 0xffffffff | 0; var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + "-" + lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + "-" + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + "-" + lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + "-" + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] + lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); }; } )(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, // Linear mapping from range to range mapLinear: function ( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); }, // https://en.wikipedia.org/wiki/Linear_interpolation lerp: function ( x, y, t ) { return ( 1 - t ) * x + t * y; }, // http://en.wikipedia.org/wiki/Smoothstep smoothstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); }, smootherstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); }, // Random integer from interval randInt: function ( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, // Random float from interval randFloat: function ( low, high ) { return low + Math.random() * ( high - low ); }, // Random float from <-range/2, range/2> interval randFloatSpread: function ( range ) { return range * ( 0.5 - Math.random() ); }, degToRad: function ( degrees ) { return degrees * _Math.DEG2RAD; }, radToDeg: function ( radians ) { return radians * _Math.RAD2DEG; }, isPowerOfTwo: function ( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; }, ceilPowerOfTwo: function ( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); }, floorPowerOfTwo: function ( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author philogb / http://blog.thejit.org/ * @author egraether / http://egraether.com/ * @author zz85 / http://www.lab4games.net/zz85/blog */ function Vector2( x, y ) { this.x = x || 0; this.y = y || 0; } Object.defineProperties( Vector2.prototype, { "width": { get: function () { return this.x; }, set: function ( value ) { this.x = value; } }, "height": { get: function () { return this.y; }, set: function ( value ) { this.y = value; } } } ); Object.assign( Vector2.prototype, { isVector2: true, set: function ( x, y ) { this.x = x; this.y = y; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, applyMatrix3: function ( m ) { var x = this.x, y = this.y; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; }, clampScalar: function () { var min = new Vector2(); var max = new Vector2(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal ); max.set( maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y; }, lengthSq: function () { return this.x * this.x + this.y * this.y; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, angle: function () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector2: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; }, rotateAround: function ( center, angle ) { var c = Math.cos( angle ), s = Math.sin( angle ); var x = this.x - center.x; var y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author jordi_ros / http://plattsoft.com * @author D1plo1d / http://github.com/D1plo1d * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Matrix4() { this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix4: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix4.prototype, { isMatrix4: true, set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { var te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; }, identity: function () { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, clone: function () { return new Matrix4().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; return this; }, copyPosition: function ( m ) { var te = this.elements, me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; }, extractBasis: function ( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; }, makeBasis: function ( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; }, extractRotation: function () { var v1 = new Vector3(); return function extractRotation( m ) { // this method does not support reflection matrices var te = this.elements; var me = m.elements; var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 3 ] = 0; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 7 ] = 0; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; te[ 11 ] = 0; te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }; }(), makeRotationFromEuler: function ( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order." ); } var te = this.elements; var x = euler.x, y = euler.y, z = euler.z; var a = Math.cos( x ), b = Math.sin( x ); var c = Math.cos( y ), d = Math.sin( y ); var e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === "XYZ" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === "YXZ" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === "ZXY" ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === "ZYX" ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === "YZX" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === "XZY" ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // bottom row te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // last column te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, makeRotationFromQuaternion: function () { var zero = new Vector3( 0, 0, 0 ); var one = new Vector3( 1, 1, 1 ); return function makeRotationFromQuaternion( q ) { return this.compose( zero, q, one ); }; }(), lookAt: function () { var x = new Vector3(); var y = new Vector3(); var z = new Vector3(); return function lookAt( eye, target, up ) { var te = this.elements; z.subVectors( eye, target ); if ( z.lengthSq() === 0 ) { // eye and target are in the same position z.z = 1; } z.normalize(); x.crossVectors( up, z ); if ( x.lengthSq() === 0 ) { // up and z are parallel if ( Math.abs( up.z ) === 1 ) { z.x += 0.0001; } else { z.z += 0.0001; } z.normalize(); x.crossVectors( up, z ); } x.normalize(); y.crossVectors( z, x ); te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; return this; }; }(), multiply: function ( m, n ) { if ( n !== undefined ) { console.warn( "THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead." ); return this.multiplyMatrices( m, n ); } return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix4( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), determinant: function () { var te = this.elements; var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); }, transpose: function () { var te = this.elements; var tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; }, setPosition: function ( v ) { var te = this.elements; te[ 12 ] = v.x; te[ 13 ] = v.y; te[ 14 ] = v.z; return this; }, getInverse: function ( m, throwOnDegenerate ) { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm var te = this.elements, me = m.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) { var msg = "THREE.Matrix4: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; }, scale: function ( v ) { var te = this.elements; var x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; }, getMaxScaleOnAxis: function () { var te = this.elements; var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, makeTranslation: function ( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; }, makeRotationX: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationY: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationZ: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, makeRotationAxis: function ( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp var c = Math.cos( angle ); var s = Math.sin( angle ); var t = 1 - c; var x = axis.x, y = axis.y, z = axis.z; var tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; }, makeScale: function ( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; }, makeShear: function ( x, y, z ) { this.set( 1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1 ); return this; }, compose: function ( position, quaternion, scale ) { var te = this.elements; var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; var x2 = x + x, y2 = y + y, z2 = z + z; var xx = x * x2, xy = x * y2, xz = x * z2; var yy = y * y2, yz = y * z2, zz = z * z2; var wx = w * x2, wy = w * y2, wz = w * z2; var sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; }, decompose: function () { var vector = new Vector3(); var matrix = new Matrix4(); return function decompose( position, quaternion, scale ) { var te = this.elements; var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale var det = this.determinant(); if ( det < 0 ) sx = - sx; position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part matrix.copy( this ); var invSX = 1 / sx; var invSY = 1 / sy; var invSZ = 1 / sz; matrix.elements[ 0 ] *= invSX; matrix.elements[ 1 ] *= invSX; matrix.elements[ 2 ] *= invSX; matrix.elements[ 4 ] *= invSY; matrix.elements[ 5 ] *= invSY; matrix.elements[ 6 ] *= invSY; matrix.elements[ 8 ] *= invSZ; matrix.elements[ 9 ] *= invSZ; matrix.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( matrix ); scale.x = sx; scale.y = sy; scale.z = sz; return this; }; }(), makePerspective: function ( left, right, top, bottom, near, far ) { if ( far === undefined ) { console.warn( "THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs." ); } var te = this.elements; var x = 2 * near / ( right - left ); var y = 2 * near / ( top - bottom ); var a = ( right + left ) / ( right - left ); var b = ( top + bottom ) / ( top - bottom ); var c = - ( far + near ) / ( far - near ); var d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; }, makeOrthographic: function ( left, right, top, bottom, near, far ) { var te = this.elements; var w = 1.0 / ( right - left ); var h = 1.0 / ( top - bottom ); var p = 1.0 / ( far - near ); var x = ( right + left ) * w; var y = ( top + bottom ) * h; var z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Object.assign( Quaternion, { slerp: function ( qa, qb, qm, t ) { return qm.copy( qa ).slerp( qb, t ); }, slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation var x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ], x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { var s = 1 - t, cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { var sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } var tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } } ); Object.defineProperties( Quaternion.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, w: { get: function () { return this._w; }, set: function ( value ) { this._w = value; this.onChangeCallback(); } } } ); Object.assign( Quaternion.prototype, { set: function ( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._w ); }, copy: function ( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this.onChangeCallback(); return this; }, setFromEuler: function ( euler, update ) { if ( ! ( euler && euler.isEuler ) ) { throw new Error( "THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order." ); } var x = euler._x, y = euler._y, z = euler._z, order = euler.order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m var cos = Math.cos; var sin = Math.sin; var c1 = cos( x / 2 ); var c2 = cos( y / 2 ); var c3 = cos( z / 2 ); var s1 = sin( x / 2 ); var s2 = sin( y / 2 ); var s3 = sin( z / 2 ); if ( order === "XYZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "YXZ" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "ZXY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "ZYX" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === "YZX" ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === "XZY" ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } if ( update !== false ) this.onChangeCallback(); return this; }, setFromAxisAngle: function ( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this.onChangeCallback(); return this; }, setFromUnitVectors: function () { // assumes direction vectors vFrom and vTo are normalized var v1 = new Vector3(); var r; var EPS = 0.000001; return function setFromUnitVectors( vFrom, vTo ) { if ( v1 === undefined ) v1 = new Vector3(); r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { v1.set( - vFrom.y, vFrom.x, 0 ); } else { v1.set( 0, - vFrom.z, vFrom.y ); } } else { v1.crossVectors( vFrom, vTo ); } this._x = v1.x; this._y = v1.y; this._z = v1.z; this._w = r; return this.normalize(); }; }(), inverse: function () { // quaternion is assumed to have unit length return this.conjugate(); }, conjugate: function () { this._x *= - 1; this._y *= - 1; this._z *= - 1; this.onChangeCallback(); return this; }, dot: function ( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; }, lengthSq: function () { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { var l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this.onChangeCallback(); return this; }, multiply: function ( q, p ) { if ( p !== undefined ) { console.warn( "THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead." ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); }, premultiply: function ( q ) { return this.multiplyQuaternions( q, this ); }, multiplyQuaternions: function ( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this.onChangeCallback(); return this; }, slerp: function ( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); var x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); if ( Math.abs( sinHalfTheta ) < 0.001 ) { this._w = 0.5 * ( w + this._w ); this._x = 0.5 * ( x + this._x ); this._y = 0.5 * ( y + this._y ); this._z = 0.5 * ( z + this._z ); return this; } var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this.onChangeCallback(); return this; }, equals: function ( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Object.assign( Vector3.prototype, { isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiply: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead." ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; }, multiplyVectors: function ( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; }, applyEuler: function () { var quaternion = new Quaternion(); return function applyEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( "THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order." ); } return this.applyQuaternion( quaternion.setFromEuler( euler ) ); }; }(), applyAxisAngle: function () { var quaternion = new Quaternion(); return function applyAxisAngle( axis, angle ) { return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); }; }(), applyMatrix3: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; }, applyQuaternion: function ( q ) { var x = this.x, y = this.y, z = this.z; var qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; }, project: function () { var matrix = new Matrix4(); return function project( camera ) { matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); return this.applyMatrix4( matrix ); }; }(), unproject: function () { var matrix = new Matrix4(); return function unproject( camera ) { matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); return this.applyMatrix4( matrix ); }; }(), transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, clampScalar: function () { var min = new Vector3(); var max = new Vector3(); return function clampScalar( minVal, maxVal ) { min.set( minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, // TODO lengthSquared? lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, cross: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead." ); return this.crossVectors( v, w ); } return this.crossVectors( this, v ); }, crossVectors: function ( a, b ) { var ax = a.x, ay = a.y, az = a.z; var bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; }, projectOnVector: function ( vector ) { var scalar = vector.dot( this ) / vector.lengthSq(); return this.copy( vector ).multiplyScalar( scalar ); }, projectOnPlane: function () { var v1 = new Vector3(); return function projectOnPlane( planeNormal ) { v1.copy( this ).projectOnVector( planeNormal ); return this.sub( v1 ); }; }(), reflect: function () { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length var v1 = new Vector3(); return function reflect( normal ) { return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); }; }(), angleTo: function ( v ) { var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); // clamp, to handle numerical problems return Math.acos( _Math.clamp( theta, - 1, 1 ) ); }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); }, setFromSpherical: function ( s ) { var sinPhiRadius = Math.sin( s.phi ) * s.radius; this.x = sinPhiRadius * Math.sin( s.theta ); this.y = Math.cos( s.phi ) * s.radius; this.z = sinPhiRadius * Math.cos( s.theta ); return this; }, setFromCylindrical: function ( c ) { this.x = c.radius * Math.sin( c.theta ); this.y = c.y; this.z = c.radius * Math.cos( c.theta ); return this; }, setFromMatrixPosition: function ( m ) { var e = m.elements; this.x = e[ 12 ]; this.y = e[ 13 ]; this.z = e[ 14 ]; return this; }, setFromMatrixScale: function ( m ) { var sx = this.setFromMatrixColumn( m, 0 ).length(); var sy = this.setFromMatrixColumn( m, 1 ).length(); var sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; }, setFromMatrixColumn: function ( m, index ) { return this.fromArray( m.elements, index * 4 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector3: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } } ); /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io * @author tschw */ function Matrix3() { this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( "THREE.Matrix3: the constructor no longer reads arguments. use .set() instead." ); } } Object.assign( Matrix3.prototype, { isMatrix3: true, set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { var te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; }, identity: function () { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; }, clone: function () { return new this.constructor().fromArray( this.elements ); }, copy: function ( m ) { var te = this.elements; var me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; }, setFromMatrix4: function ( m ) { var me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; }, applyToBufferAttribute: function () { var v1 = new Vector3(); return function applyToBufferAttribute( attribute ) { for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix3( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), multiply: function ( m ) { return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; }, determinant: function () { var te = this.elements; var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; }, getInverse: function ( matrix, throwOnDegenerate ) { if ( matrix && matrix.isMatrix4 ) { console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); } var me = matrix.elements, te = this.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) { var msg = "THREE.Matrix3: .getInverse() can"t invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; }, transpose: function () { var tmp, m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; }, getNormalMatrix: function ( matrix4 ) { return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); }, transposeIntoArray: function ( r ) { var m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; }, setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { var c = Math.cos( rotation ); var s = Math.sin( rotation ); this.set( sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 0, 0, 1 ); }, scale: function ( sx, sy ) { var te = this.elements; te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; return this; }, rotate: function ( theta ) { var c = Math.cos( theta ); var s = Math.sin( theta ); var te = this.elements; var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; te[ 0 ] = c * a11 + s * a21; te[ 3 ] = c * a12 + s * a22; te[ 6 ] = c * a13 + s * a23; te[ 1 ] = - s * a11 + c * a21; te[ 4 ] = - s * a12 + c * a22; te[ 7 ] = - s * a13 + c * a23; return this; }, translate: function ( tx, ty ) { var te = this.elements; te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 9; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for ( var i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ var textureId = 0; function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { Object.defineProperty( this, "id", { value: textureId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; this.mipmaps = []; this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; this.anisotropy = anisotropy !== undefined ? anisotropy : 1; this.format = format !== undefined ? format : RGBAFormat; this.type = type !== undefined ? type : UnsignedByteType; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.center = new Vector2( 0, 0 ); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding !== undefined ? encoding : LinearEncoding; this.version = 0; this.onUpdate = null; } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Texture, isTexture: true, 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 ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.image = source.image; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; return this; }, toJSON: function ( meta ) { var isRootObject = ( meta === undefined || typeof meta === "string" ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } function getDataURL( image ) { var canvas; if ( image instanceof HTMLCanvasElement ) { canvas = image; } else { canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext( "2d" ); if ( image instanceof ImageData ) { context.putImageData( image, 0, 0 ); } else { context.drawImage( image, 0, 0, image.width, image.height ); } } if ( canvas.width > 2048 || canvas.height > 2048 ) { return canvas.toDataURL( "image/jpeg", 0.6 ); } else { return canvas.toDataURL( "image/png" ); } } var output = { metadata: { version: 4.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, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY }; if ( this.image !== undefined ) { // TODO: Move to THREE.Image var image = this.image; if ( image.uuid === undefined ) { image.uuid = _Math.generateUUID(); // UGH } if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { meta.images[ image.uuid ] = { uuid: image.uuid, url: getDataURL( image ) }; } output.image = image.uuid; } if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); }, transformUv: function ( uv ) { if ( this.mapping !== UVMapping ) return; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } } } ); Object.defineProperty( Texture.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector4( x, y, z, w ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = ( w !== undefined ) ? w : 1; } Object.assign( Vector4.prototype, { isVector4: true, set: function ( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setW: function ( w ) { this.w = w; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( "index is out of range: " + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( "index is out of range: " + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z, this.w ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead." ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( "THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead." ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z, w = this.w; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, setAxisAngleFromQuaternion: function ( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); var s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; }, setAxisAngleFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var angle, x, y, z, // variables for result epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; var xx = ( m11 + 1 ) / 2; var yy = ( m22 + 1 ) / 2; var zz = ( m33 + 1 ) / 2; var xy = ( m12 + m21 ) / 4; var xz = ( m13 + m31 ) / 4; var yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I"ve left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector4(); max = new Vector4(); } min.set( minVal, minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( "THREE.Vector4: offset has been removed from .fromBufferAttribute()." ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } } ); /** * @author szimek / https://github.com/szimek/ * @author alteredq / http://alteredqualia.com/ * @author Marius Kintel / https://github.com/kintel */ /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ function WebGLRenderTarget( width, height, options ) { this.width = width; this.height = height; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); options = options || {}; if ( options.minFilter === undefined ) options.minFilter = LinearFilter; this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: WebGLRenderTarget, isWebGLRenderTarget: true, setSize: function ( width, height ) { if ( this.width !== width || this.height !== height ) { this.width = width; this.height = height; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.width = source.width; this.height = source.height; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author alteredq / http://alteredqualia.com */ function WebGLRenderTargetCube( width, height, options ) { WebGLRenderTarget.call( this, width, height, options ); this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 this.activeMipMapLevel = 0; } WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; /** * @author alteredq / http://alteredqualia.com/ */ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } DataTexture.prototype = Object.create( Texture.prototype ); DataTexture.prototype.constructor = DataTexture; DataTexture.prototype.isDataTexture = true; /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Box3( min, max ) { this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); } Object.assign( Box3.prototype, { isBox3: true, set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromArray: function ( array ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = array.length; i < l; i += 3 ) { var x = array[ i ]; var y = array[ i + 1 ]; var z = array[ i + 2 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromBufferAttribute: function ( attribute ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = attribute.count; i < l; i ++ ) { var x = attribute.getX( i ); var y = attribute.getY( i ); var z = attribute.getZ( i ); if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector3(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), setFromObject: function ( object ) { this.makeEmpty(); return this.expandByObject( object ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getCenter() target is now required" ); target = new Vector3(); } return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getSize() target is now required" ); target = new Vector3(); } return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, expandByObject: function () { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object"s, and children"s, world transforms var scope, i, l; var v1 = new Vector3(); function traverse( node ) { var geometry = node.geometry; if ( geometry !== undefined ) { if ( geometry.isGeometry ) { var vertices = geometry.vertices; for ( i = 0, l = vertices.length; i < l; i ++ ) { v1.copy( vertices[ i ] ); v1.applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } else if ( geometry.isBufferGeometry ) { var attribute = geometry.attributes.position; if ( attribute !== undefined ) { for ( i = 0, l = attribute.count; i < l; i ++ ) { v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } } } } return function expandByObject( object ) { scope = this; object.updateMatrixWorld( true ); object.traverse( traverse ); return this; }; }(), containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; }, getParameter: function ( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. if ( target === undefined ) { console.warn( "THREE.Box3: .getParameter() target is now required" ); target = new Vector3(); } return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); }, intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; }, intersectsSphere: ( function () { var closestPoint = new Vector3(); return function intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, closestPoint ); // If that point is inside the sphere, the AABB and sphere intersect. return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); }; } )(), intersectsPlane: function ( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. var min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= plane.constant && max >= plane.constant ); }, intersectsTriangle: ( function () { // triangle centered vertices var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); // triangle edge vectors var f0 = new Vector3(); var f1 = new Vector3(); var f2 = new Vector3(); var testAxis = new Vector3(); var center = new Vector3(); var extents = new Vector3(); var triangleNormal = new Vector3(); function satForAxes( axes ) { var i, j; for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { testAxis.fromArray( axes, i ); // project the aabb onto the seperating axis var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z ); // project all 3 vertices of the triangle onto the seperating axis var p0 = v0.dot( testAxis ); var p1 = v1.dot( testAxis ); var p2 = v2.dot( testAxis ); // actual test, basically see if either of the most extreme of the triangle points intersects r if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; } return function intersectsTriangle( triangle ) { if ( this.isEmpty() ) { return false; } // compute box center and extents this.getCenter( center ); extents.subVectors( this.max, center ); // translate triangle to aabb origin v0.subVectors( triangle.a, center ); v1.subVectors( triangle.b, center ); v2.subVectors( triangle.c, center ); // compute edge vectors for triangle f0.subVectors( v1, v0 ); f1.subVectors( v2, v1 ); f2.subVectors( v0, v2 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) var axes = [ 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y, f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x, - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0 ]; if ( ! satForAxes( axes ) ) { return false; } // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( ! satForAxes( axes ) ) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here triangleNormal.crossVectors( f0, f1 ); axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ]; return satForAxes( axes ); }; } )(), clampPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .clampPoint() target is now required" ); target = new Vector3(); } return target.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector3(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), getBoundingSphere: function () { var v1 = new Vector3(); return function getBoundingSphere( target ) { if ( target === undefined ) { console.warn( "THREE.Box3: .getBoundingSphere() target is now required" ); target = new Sphere(); } this.getCenter( target.center ); target.radius = this.getSize( v1 ).length() * 0.5; return target; }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if ( this.isEmpty() ) this.makeEmpty(); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, applyMatrix4: function ( matrix ) { // transform of empty box is an empty box. if ( this.isEmpty( ) ) return this; var m = matrix.elements; var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x; var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x; var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y; var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y; var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z; var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z; this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ]; this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ]; this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ]; this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ]; this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ]; this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ]; return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Sphere( center, radius ) { this.center = ( center !== undefined ) ? center : new Vector3(); this.radius = ( radius !== undefined ) ? radius : 0; } Object.assign( Sphere.prototype, { set: function ( center, radius ) { this.center.copy( center ); this.radius = radius; return this; }, setFromPoints: function () { var box = new Box3(); return function setFromPoints( points, optionalCenter ) { var center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { box.setFromPoints( points ).getCenter( center ); } var maxRadiusSq = 0; for ( var i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; }, empty: function () { return ( this.radius <= 0 ); }, containsPoint: function ( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); }, distanceToPoint: function ( point ) { return ( point.distanceTo( this.center ) - this.radius ); }, intersectsSphere: function ( sphere ) { var radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); }, intersectsBox: function ( box ) { return box.intersectsSphere( this ); }, intersectsPlane: function ( plane ) { return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; }, clampPoint: function ( point, target ) { var deltaLengthSq = this.center.distanceToSquared( point ); if ( target === undefined ) { console.warn( "THREE.Sphere: .clampPoint() target is now required" ); target = new Vector3(); } target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { target.sub( this.center ).normalize(); target.multiplyScalar( this.radius ).add( this.center ); } return target; }, getBoundingBox: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Sphere: .getBoundingBox() target is now required" ); target = new Box3(); } target.set( this.center, this.center ); target.expandByScalar( this.radius ); return target; }, applyMatrix4: function ( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; }, translate: function ( offset ) { this.center.add( offset ); return this; }, equals: function ( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } } ); /** * @author bhouston / http://clara.io */ function Plane( normal, constant ) { // normal is assumed to be normalized this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); this.constant = ( constant !== undefined ) ? constant : 0; } Object.assign( Plane.prototype, { set: function ( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; }, setComponents: function ( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; }, setFromNormalAndCoplanarPoint: function ( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); return this; }, setFromCoplanarPoints: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function setFromCoplanarPoints( a, b, c ) { var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; }, normalize: function () { // Note: will lead to a divide by zero if the plane is invalid. var inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; }, negate: function () { this.constant *= - 1; this.normal.negate(); return this; }, distanceToPoint: function ( point ) { return this.normal.dot( point ) + this.constant; }, distanceToSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; }, projectPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .projectPoint() target is now required" ); target = new Vector3(); } return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); }, intersectLine: function () { var v1 = new Vector3(); return function intersectLine( line, target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .intersectLine() target is now required" ); target = new Vector3(); } var direction = line.delta( v1 ); var denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return target.copy( line.start ); } // Unsure if this is the correct method to handle this case. return undefined; } var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return undefined; } return target.copy( direction ).multiplyScalar( t ).add( line.start ); }; }(), intersectsLine: function ( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. var startSign = this.distanceToPoint( line.start ); var endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); }, intersectsBox: function ( box ) { return box.intersectsPlane( this ); }, intersectsSphere: function ( sphere ) { return sphere.intersectsPlane( this ); }, coplanarPoint: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Plane: .coplanarPoint() target is now required" ); target = new Vector3(); } return target.copy( this.normal ).multiplyScalar( - this.constant ); }, applyMatrix4: function () { var v1 = new Vector3(); var m1 = new Matrix3(); return function applyMatrix4( matrix, optionalNormalMatrix ) { var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); this.constant = - referencePoint.dot( normal ); return this; }; }(), translate: function ( offset ) { this.constant -= offset.dot( this.normal ); return this; }, equals: function ( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / http://clara.io */ function Frustum( p0, p1, p2, p3, p4, p5 ) { this.planes = [ ( p0 !== undefined ) ? p0 : new Plane(), ( p1 !== undefined ) ? p1 : new Plane(), ( p2 !== undefined ) ? p2 : new Plane(), ( p3 !== undefined ) ? p3 : new Plane(), ( p4 !== undefined ) ? p4 : new Plane(), ( p5 !== undefined ) ? p5 : new Plane() ]; } Object.assign( Frustum.prototype, { set: function ( p0, p1, p2, p3, p4, p5 ) { var planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( frustum ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; }, setFromMatrix: function ( m ) { var planes = this.planes; var me = m.elements; var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; }, intersectsObject: function () { var sphere = new Sphere(); return function intersectsObject( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ) .applyMatrix4( object.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSprite: function () { var sphere = new Sphere(); return function intersectsSprite( sprite ) { sphere.center.set( 0, 0, 0 ); sphere.radius = 0.7071067811865476; sphere.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSphere: function ( sphere ) { var planes = this.planes; var center = sphere.center; var negRadius = - sphere.radius; for ( var i = 0; i < 6; i ++ ) { var distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; }, intersectsBox: function () { var p1 = new Vector3(), p2 = new Vector3(); return function intersectsBox( box ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { var plane = planes[ i ]; p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; var d1 = plane.distanceToPoint( p1 ); var d2 = plane.distanceToPoint( p2 ); // if both outside plane, no intersection if ( d1 < 0 && d2 < 0 ) { return false; } } return true; }; }(), containsPoint: function ( point ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } } ); var alphamap_fragment = "#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vUv ).g; #endif "; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; #endif "; var alphatest_fragment = "#ifdef ALPHATEST if ( diffuseColor.a < ALPHATEST ) discard; #endif "; var aomap_fragment = "#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_ENVMAP ) && defined( PHYSICAL ) float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness ); #endif #endif "; var aomap_pars_fragment = "#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; #endif"; var begin_vertex = " vec3 transformed = vec3( position ); "; var beginnormal_vertex = " vec3 objectNormal = vec3( normal ); "; var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { if( decayExponent > 0.0 ) { #if defined ( PHYSICALLY_CORRECT_LIGHTS ) float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); float maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); return distanceFalloff * maxDistanceCutoffFactor; #else return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent ); #endif } return 1.0; } vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) { return RECIPROCAL_PI * diffuseColor; } vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) { float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH ); return ( 1.0 - specularColor ) * fresnel + specularColor; } float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); return 1.0 / ( gl * gv ); } float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { float a2 = pow2( alpha ); float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); return 0.5 / max( gv + gl, EPSILON ); } float D_GGX( const in float alpha, const in float dotNH ) { float a2 = pow2( alpha ); float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; return RECIPROCAL_PI * a2 / pow2( denom ); } vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) ); float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( G * D ); } vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { const float LUT_SIZE = 64.0; const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; const float LUT_BIAS = 0.5 / LUT_SIZE; float dotNV = saturate( dot( N, V ) ); vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); uv = uv * LUT_SCALE + LUT_BIAS; return uv; } float LTC_ClippedSphereFormFactor( const in vec3 f ) { float l = length( f ); return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); } vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { float x = dot( v1, v2 ); float y = abs( x ); float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; float b = 3.4175940 + ( 4.1616724 + y ) * y; float v = a / b; float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; return cross( v1, v2 ) * theta_sintheta; } vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; vec3 lightNormal = cross( v1, v2 ); if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); vec3 T1, T2; T1 = normalize( V - N * dot( V, N ) ); T2 = - cross( N, T1 ); mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); vec3 coords[ 4 ]; coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); coords[ 0 ] = normalize( coords[ 0 ] ); coords[ 1 ] = normalize( coords[ 1 ] ); coords[ 2 ] = normalize( coords[ 2 ] ); coords[ 3 ] = normalize( coords[ 3 ] ); vec3 vectorFormFactor = vec3( 0.0 ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); return vec3( result ); } vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) { float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; return specularColor * AB.x + AB.y; } float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) { vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir ); float dotNH = saturate( dot( geometry.normal, halfDir ) ); float dotLH = saturate( dot( incidentLight.direction, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); } float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) { return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 ); } float BlinnExponentToGGXRoughness( const in float blinnExponent ) { return sqrt( 2.0 / ( blinnExponent + 2.0 ) ); } "; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( vUv ); vec2 dSTdy = dFdy( vUv ); float Hll = bumpScale * texture2D( bumpMap, vUv ).x; float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll; float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll; return vec2( dBx, dBy ); } vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) { vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) ); vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) ); vec3 vN = surf_norm; vec3 R1 = cross( vSigmaY, vN ); vec3 R2 = cross( vN, vSigmaX ); float fDet = dot( vSigmaX, R1 ); fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 ); vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } #endif "; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0 vec4 plane; #pragma unroll_loop for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard; } #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES bool clipped = true; #pragma unroll_loop for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { plane = clippingPlanes[ i ]; clipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped; } if ( clipped ) discard; #endif #endif "; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0 #if ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; #endif "; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) varying vec3 vViewPosition; #endif "; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) vViewPosition = - mvPosition.xyz; #endif "; var color_fragment = "#ifdef USE_COLOR diffuseColor.rgb *= vColor; #endif"; var color_pars_fragment = "#ifdef USE_COLOR varying vec3 vColor; #endif "; var color_pars_vertex = "#ifdef USE_COLOR varying vec3 vColor; #endif"; var color_vertex = "#ifdef USE_COLOR vColor.xyz = color.xyz; #endif"; var common = "#define PI 3.14159265359 #define PI2 6.28318530718 #define PI_HALF 1.5707963267949 #define RECIPROCAL_PI 0.31830988618 #define RECIPROCAL_PI2 0.15915494 #define LOG2 1.442695 #define EPSILON 1e-6 #define saturate(a) clamp( a, 0.0, 1.0 ) #define whiteCompliment(a) ( 1.0 - saturate( a ) ) float pow2( const in float x ) { return x*x; } float pow3( const in float x ) { return x*x*x; } float pow4( const in float x ) { float x2 = x*x; return x2*x2; } float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } highp float rand( const in vec2 uv ) { const highp float a = 12.9898, b = 78.233, c = 43758.5453; highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); return fract(sin(sn) * c); } struct IncidentLight { vec3 color; vec3 direction; bool visible; }; struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; struct GeometricContext { vec3 position; vec3 normal; vec3 viewDir; }; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); } vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { float distance = dot( planeNormal, point - pointOnPlane ); return - distance * planeNormal + point; } float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { return sign( dot( point - pointOnPlane, planeNormal ) ); } vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) { return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine; } mat3 transposeMat3( const in mat3 m ) { mat3 tmp; tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); return tmp; } float linearToRelativeLuminance( const in vec3 color ) { vec3 weights = vec3( 0.2126, 0.7152, 0.0722 ); return dot( weights, color.rgb ); } "; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_textureSize (1024.0) int getFaceFromDirection(vec3 direction) { vec3 absDirection = abs(direction); int face = -1; if( absDirection.x > absDirection.z ) { if(absDirection.x > absDirection.y ) face = direction.x > 0.0 ? 0 : 3; else face = direction.y > 0.0 ? 1 : 4; } else { if(absDirection.z > absDirection.y ) face = direction.z > 0.0 ? 2 : 5; else face = direction.y > 0.0 ? 1 : 4; } return face; } #define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0) #define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0)) vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) { float scale = exp2(cubeUV_maxLods1 - roughnessLevel); float dxRoughness = dFdx(roughness); float dyRoughness = dFdy(roughness); vec3 dx = dFdx( vec * scale * dxRoughness ); vec3 dy = dFdy( vec * scale * dyRoughness ); float d = max( dot( dx, dx ), dot( dy, dy ) ); d = clamp(d, 1.0, cubeUV_rangeClamp); float mipLevel = 0.5 * log2(d); return vec2(floor(mipLevel), fract(mipLevel)); } #define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0) #define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize) vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) { mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel; float a = 16.0 * cubeUV_rcpTextureSize; vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) ); vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed; float powScale = exp2_packed.x * exp2_packed.y; float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25; float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x; bool bRes = mipLevel == 0.0; scale = bRes && (scale < a) ? a : scale; vec3 r; vec2 offset; int face = getFaceFromDirection(direction); float rcpPowScale = 1.0 / powScale; if( face == 0) { r = vec3(direction.x, -direction.z, direction.y); offset = vec2(0.0+mipOffset,0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 1) { r = vec3(direction.y, direction.x, direction.z); offset = vec2(scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 2) { r = vec3(direction.z, direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y; } else if( face == 3) { r = vec3(direction.x, direction.z, direction.y); offset = vec2(0.0+mipOffset,0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else if( face == 4) { r = vec3(direction.y, direction.x, -direction.z); offset = vec2(scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } else { r = vec3(direction.z, -direction.x, direction.y); offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale); offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; } r = normalize(r); float texelOffset = 0.5 * cubeUV_rcpTextureSize; vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5; vec2 base = offset + vec2( texelOffset ); return base + s * ( scale - 2.0 * texelOffset ); } #define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0) vec4 textureCubeUV(vec3 reflectedDirection, float roughness ) { float roughnessVal = roughness* cubeUV_maxLods3; float r1 = floor(roughnessVal); float r2 = r1 + 1.0; float t = fract(roughnessVal); vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness); float s = mipInfo.y; float level0 = mipInfo.x; float level1 = level0 + 1.0; level1 = level1 > 5.0 ? 5.0 : level1; level0 += min( floor( s + 0.5 ), 5.0 ); vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0); vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10)); vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0); vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20)); vec4 result = mix(color10, color20, t); return vec4(result.rgb, 1.0); } #endif "; var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif "; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; #endif "; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias ); #endif "; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vUv ); emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb; totalEmissiveRadiance *= emissiveColor.rgb; #endif "; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; #endif "; var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor ); "; var encodings_pars_fragment = " vec4 LinearToLinear( in vec4 value ) { return value; } vec4 GammaToLinear( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w ); } vec4 LinearToGamma( in vec4 value, in float gammaFactor ) { return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w ); } vec4 sRGBToLinear( in vec4 value ) { return 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.w ); } vec4 LinearTosRGB( in vec4 value ) { return 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.w ); } vec4 RGBEToLinear( in vec4 value ) { return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 ); } vec4 LinearToRGBE( in vec4 value ) { float maxComponent = max( max( value.r, value.g ), value.b ); float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 ); return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 ); } vec4 RGBMToLinear( in vec4 value, in float maxRange ) { return vec4( value.xyz * value.w * maxRange, 1.0 ); } vec4 LinearToRGBM( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float M = clamp( maxRGB / maxRange, 0.0, 1.0 ); M = ceil( M * 255.0 ) / 255.0; return vec4( value.rgb / ( M * maxRange ), M ); } vec4 RGBDToLinear( in vec4 value, in float maxRange ) { return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 ); } vec4 LinearToRGBD( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); float D = max( maxRange / maxRGB, 1.0 ); D = min( floor( D ) / 255.0, 1.0 ); return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D ); } const mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 ); vec4 LinearToLogLuv( in vec4 value ) { vec3 Xp_Y_XYZp = value.rgb * cLogLuvM; Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6)); vec4 vResult; vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0; vResult.w = fract(Le); vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0; return vResult; } const mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 ); vec4 LogLuvToLinear( in vec4 value ) { float Le = value.z * 255.0 + value.w; vec3 Xp_Y_XYZp; Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0); Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y; Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z; vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM; return vec4( max(vRGB, 0.0), 1.0 ); } "; var envmap_fragment = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition ); vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( cameraToVertex, worldNormal ); #else vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #else vec3 reflectVec = vReflect; #endif #ifdef ENVMAP_TYPE_CUBE vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; reflectVec = normalize( reflectVec ); sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; vec4 envColor = texture2D( envMap, sampleUV ); #elif defined( ENVMAP_TYPE_SPHERE ) reflectVec = normalize( reflectVec ); vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ); vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 ); #else vec4 envColor = vec4( 0.0 ); #endif envColor = envMapTexelToLinear( envColor ); #ifdef ENVMAP_BLENDING_MULTIPLY outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_MIX ) outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); #elif defined( ENVMAP_BLENDING_ADD ) outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif "; var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL ) uniform float reflectivity; uniform float envMapIntensity; #endif #ifdef USE_ENVMAP #if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) ) varying vec3 vWorldPosition; #endif #ifdef ENVMAP_TYPE_CUBE uniform samplerCube envMap; #else uniform sampler2D envMap; #endif uniform float flipEnvMap; uniform int maxMipLevel; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL ) uniform float refractionRatio; #else varying vec3 vReflect; #endif #endif "; var envmap_pars_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) varying vec3 vWorldPosition; #else varying vec3 vReflect; uniform float refractionRatio; #endif #endif "; var envmap_vertex = "#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) vWorldPosition = worldPosition.xyz; #else vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #endif "; var fog_vertex = " #ifdef USE_FOG fogDepth = -mvPosition.z; #endif"; var fog_pars_vertex = "#ifdef USE_FOG varying float fogDepth; #endif "; var fog_fragment = "#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) ); #else float fogFactor = smoothstep( fogNear, fogFar, fogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); #endif "; var fog_pars_fragment = "#ifdef USE_FOG uniform vec3 fogColor; varying float fogDepth; #ifdef FOG_EXP2 uniform float fogDensity; #else uniform float fogNear; uniform float fogFar; #endif #endif "; var gradientmap_pars_fragment = "#ifdef TOON uniform sampler2D gradientMap; vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { float dotNL = dot( normal, lightDirection ); vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); #ifdef USE_GRADIENTMAP return texture2D( gradientMap, coord ).rgb; #else return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 ); #endif } #endif "; var lightmap_fragment = "#ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #endif "; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; #endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 ); GeometricContext geometry; geometry.position = mvPosition.xyz; geometry.normal = normalize( transformedNormal ); geometry.viewDir = normalize( -mvPosition.xyz ); GeometricContext backGeometry; backGeometry.position = geometry.position; backGeometry.normal = -geometry.normal; backGeometry.viewDir = geometry.viewDir; vLightFront = vec3( 0.0 ); #ifdef DOUBLE_SIDED vLightBack = vec3( 0.0 ); #endif IncidentLight directLight; float dotNL; vec3 directLightColor_Diffuse; #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { getPointDirectLightIrradiance( pointLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { getSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { getDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight ); dotNL = dot( geometry.normal, directLight.direction ); directLightColor_Diffuse = PI * directLight.color; vLightFront += saturate( dotNL ) * directLightColor_Diffuse; #ifdef DOUBLE_SIDED vLightBack += saturate( -dotNL ) * directLightColor_Diffuse; #endif } #endif #if NUM_HEMI_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); #ifdef DOUBLE_SIDED vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry ); #endif } #endif "; var lights_pars_begin = "uniform vec3 ambientLightColor; vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { vec3 irradiance = ambientLightColor; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #if NUM_DIR_LIGHTS > 0 struct DirectionalLight { vec3 direction; vec3 color; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; void getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) { directLight.color = directionalLight.color; directLight.direction = directionalLight.direction; directLight.visible = true; } #endif #if NUM_POINT_LIGHTS > 0 struct PointLight { vec3 position; vec3 color; float distance; float decay; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; float shadowCameraNear; float shadowCameraFar; }; uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; void getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = pointLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); directLight.color = pointLight.color; directLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay ); directLight.visible = ( directLight.color != vec3( 0.0 ) ); } #endif #if NUM_SPOT_LIGHTS > 0 struct SpotLight { vec3 position; vec3 direction; vec3 color; float distance; float decay; float coneCos; float penumbraCos; int shadow; float shadowBias; float shadowRadius; vec2 shadowMapSize; }; uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; void getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) { vec3 lVector = spotLight.position - geometry.position; directLight.direction = normalize( lVector ); float lightDistance = length( lVector ); float angleCos = dot( directLight.direction, spotLight.direction ); if ( angleCos > spotLight.coneCos ) { float spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos ); directLight.color = spotLight.color; directLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay ); directLight.visible = true; } else { directLight.color = vec3( 0.0 ); directLight.visible = false; } } #endif #if NUM_RECT_AREA_LIGHTS > 0 struct RectAreaLight { vec3 color; vec3 position; vec3 halfWidth; vec3 halfHeight; }; uniform sampler2D ltc_1; uniform sampler2D ltc_2; uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; #endif #if NUM_HEMI_LIGHTS > 0 struct HemisphereLight { vec3 direction; vec3 skyColor; vec3 groundColor; }; uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) { float dotNL = dot( geometry.normal, hemiLight.direction ); float hemiDiffuseWeight = 0.5 * dotNL + 0.5; vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif return irradiance; } #endif "; var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL ) vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) { vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix ); #ifdef ENVMAP_TYPE_CUBE vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) ); #else vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); vec4 envMapColor = textureCubeUV( queryVec, 1.0 ); #else vec4 envMapColor = vec4( 0.0 ); #endif return PI * envMapColor.rgb * envMapIntensity; } float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) { float maxMIPLevelScalar = float( maxMIPLevel ); float desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 ); return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar ); } vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) { #ifdef ENVMAP_MODE_REFLECTION vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal ); #else vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio ); #endif reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel ); #ifdef ENVMAP_TYPE_CUBE vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel ); #else vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_CUBE_UV ) vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent)); #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #elif defined( ENVMAP_TYPE_SPHERE ) vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) ); #ifdef TEXTURE_LOD_EXT vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #else vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel ); #endif envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; #endif return envMapColor.rgb * envMapIntensity; } #endif "; var lights_phong_fragment = "BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; material.specularStrength = specularStrength; "; var lights_phong_pars_fragment = "varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; float specularShininess; float specularStrength; }; void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { #ifdef TOON vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color; #else float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #endif #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength; } void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong #define Material_LightProbeLOD( material ) (0) "; var lights_physical_fragment = "PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 ); #ifdef STANDARD material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor ); #else material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor ); material.clearCoat = saturate( clearCoat ); material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 ); #endif "; var lights_physical_pars_fragment = "struct PhysicalMaterial { vec3 diffuseColor; float specularRoughness; vec3 specularColor; #ifndef STANDARD float clearCoat; float clearCoatRoughness; #endif }; #define MAXIMUM_SPECULAR_COEFFICIENT 0.16 #define DEFAULT_SPECULAR_COEFFICIENT 0.04 float clearCoatDHRApprox( const in float roughness, const in float dotNL ) { return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) ); } #if NUM_RECT_AREA_LIGHTS > 0 void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { vec3 normal = geometry.normal; vec3 viewDir = geometry.viewDir; vec3 position = geometry.position; vec3 lightPos = rectAreaLight.position; vec3 halfWidth = rectAreaLight.halfWidth; vec3 halfHeight = rectAreaLight.halfHeight; vec3 lightColor = rectAreaLight.color; float roughness = material.specularRoughness; vec3 rectCoords[ 4 ]; rectCoords[ 0 ] = lightPos - halfWidth - halfHeight; rectCoords[ 1 ] = lightPos + halfWidth - halfHeight; rectCoords[ 2 ] = lightPos + halfWidth + halfHeight; rectCoords[ 3 ] = lightPos - halfWidth + halfHeight; vec2 uv = LTC_Uv( normal, viewDir, roughness ); vec4 t1 = texture2D( ltc_1, uv ); vec4 t2 = texture2D( ltc_2, uv ); mat3 mInv = mat3( vec3( t1.x, 0, t1.y ), vec3( 0, 1, 0 ), vec3( t1.z, 0, t1.w ) ); vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); } #endif void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); vec3 irradiance = dotNL * directLight.color; #ifndef PHYSICALLY_CORRECT_LIGHTS irradiance *= PI; #endif #ifndef STANDARD float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness ); reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); #ifndef STANDARD reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { #ifndef STANDARD float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); float dotNL = dotNV; float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL ); #else float clearCoatDHR = 0.0; #endif reflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness ); #ifndef STANDARD reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness ); #endif } #define RE_Direct RE_Direct_Physical #define RE_Direct_RectArea RE_Direct_RectArea_Physical #define RE_IndirectDiffuse RE_IndirectDiffuse_Physical #define RE_IndirectSpecular RE_IndirectSpecular_Physical #define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness ) #define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness ) float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); } "; var lights_fragment_begin = " GeometricContext geometry; geometry.position = - vViewPosition; geometry.normal = normal; geometry.viewDir = normalize( vViewPosition ); IncidentLight directLight; #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) PointLight pointLight; #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; getPointDirectLightIrradiance( pointLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) SpotLight spotLight; #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; getSpotDirectLightIrradiance( spotLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) DirectionalLight directionalLight; #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; #endif RE_Direct( directLight, geometry, material, reflectedLight ); } #endif #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) RectAreaLight rectAreaLight; #pragma unroll_loop for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { rectAreaLight = rectAreaLights[ i ]; RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); } #endif #if defined( RE_IndirectDiffuse ) vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); #if ( NUM_HEMI_LIGHTS > 0 ) #pragma unroll_loop for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry ); } #endif #endif #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearCoatRadiance = vec3( 0.0 ); #endif "; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS lightMapIrradiance *= PI; #endif irradiance += lightMapIrradiance; #endif #if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV ) irradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel ); #endif #endif #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) radiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel ); #ifndef STANDARD clearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel ); #endif #endif "; var lights_fragment_end = "#if defined( RE_IndirectDiffuse ) RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight ); #endif "; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT ) gl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5; #endif"; var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF uniform float logDepthBufFC; #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif #endif "; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif uniform float logDepthBufFC; #endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; #else gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0; gl_Position.z *= gl_Position.w; #endif #endif "; var map_fragment = "#ifdef USE_MAP vec4 texelColor = texture2D( map, vUv ); texelColor = mapTexelToLinear( texelColor ); diffuseColor *= texelColor; #endif "; var map_pars_fragment = "#ifdef USE_MAP uniform sampler2D map; #endif "; var map_particle_fragment = "#ifdef USE_MAP vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; vec4 mapTexel = texture2D( map, uv ); diffuseColor *= mapTexelToLinear( mapTexel ); #endif "; var map_particle_pars_fragment = "#ifdef USE_MAP uniform mat3 uvTransform; uniform sampler2D map; #endif "; var metalnessmap_fragment = "float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vUv ); metalnessFactor *= texelMetalness.b; #endif "; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; #endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ]; objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ]; objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ]; objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ]; #endif "; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ]; transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ]; transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ]; transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ]; transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ]; transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ]; transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ]; #endif #endif "; var normal_fragment_begin = "#ifdef FLAT_SHADED vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); vec3 normal = normalize( cross( fdx, fdy ) ); #else vec3 normal = normalize( vNormal ); #ifdef DOUBLE_SIDED normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 ); #endif #endif "; var normal_fragment_maps = "#ifdef USE_NORMALMAP normal = perturbNormal2Arb( -vViewPosition, normal ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() ); #endif "; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) { vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) ); vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) ); vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); float scale = sign( st1.t * st0.s - st0.t * st1.s ); vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale ); vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale ); vec3 N = normalize( surf_norm ); mat3 tsn = mat3( S, T, N ); vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; mapN.xy *= normalScale; mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 ); return normalize( tsn * mapN ); } #endif "; var packing = "vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { return 2.0 * rgb.xyz - 1.0; } const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.; const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. ); const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. ); const float ShiftRight8 = 1. / 256.; vec4 packDepthToRGBA( const in float v ) { vec4 r = vec4( fract( v * PackFactors ), v ); r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale; } float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { return ( viewZ + near ) / ( near - far ); } float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) { return linearClipZ * ( near - far ) - near; } float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { return (( near + viewZ ) * far ) / (( far - near ) * viewZ ); } float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) { return ( near * far ) / ( ( far - near ) * invClipZ - far ); } "; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; #endif "; var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); gl_Position = projectionMatrix * mvPosition; "; var dithering_fragment = "#if defined( DITHERING ) gl_FragColor.rgb = dithering( gl_FragColor.rgb ); #endif "; var dithering_pars_fragment = "#if defined( DITHERING ) vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } #endif "; var roughnessmap_fragment = "float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vUv ); roughnessFactor *= texelRoughness.g; #endif "; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; #endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); } float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) { const vec2 offset = vec2( 0.0, 1.0 ); vec2 texelSize = vec2( 1.0 ) / size; vec2 centroidUV = floor( uv * size + 0.5 ) / size; float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare ); float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare ); float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare ); float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare ); vec2 f = fract( uv * size + 0.5 ); float a = mix( lb, lt, f.y ); float b = mix( rb, rt, f.y ); float c = mix( a, b, f.x ); return c; } float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) { float shadow = 1.0; shadowCoord.xyz /= shadowCoord.w; shadowCoord.z += shadowBias; bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 ); bool frustumTest = all( frustumTestVec ); if ( frustumTest ) { #if defined( SHADOWMAP_TYPE_PCF ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 texelSize = vec2( 1.0 ) / shadowMapSize; float dx0 = - texelSize.x * shadowRadius; float dy0 = - texelSize.y * shadowRadius; float dx1 = + texelSize.x * shadowRadius; float dy1 = + texelSize.y * shadowRadius; shadow = ( texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); #else shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); #endif } return shadow; } vec2 cubeToUV( vec3 v, float texelSizeY ) { vec3 absV = abs( v ); float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); absV *= scaleToCube; v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); vec2 planar = v.xy; float almostATexel = 1.5 * texelSizeY; float almostOne = 1.0 - almostATexel; if ( absV.z >= almostOne ) { if ( v.z > 0.0 ) planar.x = 4.0 - v.x; } else if ( absV.x >= almostOne ) { float signX = sign( v.x ); planar.x = v.z * signX + 2.0 * signX; } else if ( absV.y >= almostOne ) { float signY = sign( v.y ); planar.x = v.x + 2.0 * signY + 2.0; planar.y = v.z * signY - 2.0; } return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); } float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); vec3 lightToPosition = shadowCoord.xyz; float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; vec3 bd3D = normalize( lightToPosition ); #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; return ( texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) ) * ( 1.0 / 9.0 ); #else return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); #endif } #endif "; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ]; varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ]; #endif #if NUM_SPOT_LIGHTS > 0 uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ]; varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ]; #endif #if NUM_POINT_LIGHTS > 0 uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ]; varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; #endif #endif "; var shadowmap_vertex = "#ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition; } #endif #if NUM_SPOT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition; } #endif #if NUM_POINT_LIGHTS > 0 #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition; } #endif #endif "; var shadowmask_pars_fragment = "float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHTS > 0 DirectionalLight directionalLight; #pragma unroll_loop for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { directionalLight = directionalLights[ i ]; shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; } #endif #if NUM_SPOT_LIGHTS > 0 SpotLight spotLight; #pragma unroll_loop for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { spotLight = spotLights[ i ]; shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; } #endif #if NUM_POINT_LIGHTS > 0 PointLight pointLight; #pragma unroll_loop for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { pointLight = pointLights[ i ]; shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; } #endif #endif return shadow; } "; var skinbase_vertex = "#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform sampler2D boneTexture; uniform int boneTextureSize; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureSize ) ); float y = floor( j / float( boneTextureSize ) ); float dx = 1.0 / float( boneTextureSize ); float dy = 1.0 / float( boneTextureSize ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneMatrices[ int(i) ]; return bone; } #endif #endif "; var skinning_vertex = "#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; #endif "; var skinnormal_vertex = "#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; #endif "; var specularmap_fragment = "float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; #endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP uniform sampler2D specularMap; #endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif "; var tonemapping_pars_fragment = "#ifndef saturate #define saturate(a) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; uniform float toneMappingWhitePoint; vec3 LinearToneMapping( vec3 color ) { return toneMappingExposure * color; } vec3 ReinhardToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( color / ( vec3( 1.0 ) + color ) ); } #define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) ) vec3 Uncharted2ToneMapping( vec3 color ) { color *= toneMappingExposure; return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) ); } vec3 OptimizedCineonToneMapping( vec3 color ) { color *= toneMappingExposure; color = max( vec3( 0.0 ), color - 0.004 ); return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); } "; var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; #endif"; var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) varying vec2 vUv; uniform mat3 uvTransform; #endif "; var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP ) vUv = ( uvTransform * vec3( uv, 1 ) ).xy; #endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) varying vec2 vUv2; #endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) attribute vec2 uv2; varying vec2 vUv2; #endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP ) vUv2 = uv2; #endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 ); #endif "; var cube_frag = "uniform samplerCube tCube; uniform float tFlip; uniform float opacity; varying vec3 vWorldPosition; void main() { gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) ); gl_FragColor.a *= opacity; } "; var cube_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include gl_Position.z = gl_Position.w; } "; var depth_frag = "#if DEPTH_PACKING == 3200 uniform float opacity; #endif #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( 1.0 ); #if DEPTH_PACKING == 3200 diffuseColor.a = opacity; #endif #include #include #include #include #if DEPTH_PACKING == 3200 gl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity ); #elif DEPTH_PACKING == 3201 gl_FragColor = packDepthToRGBA( gl_FragCoord.z ); #endif } "; var depth_vert = "#include #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include } "; var distanceRGBA_frag = "#define DISTANCE uniform vec3 referencePosition; uniform float nearDistance; uniform float farDistance; varying vec3 vWorldPosition; #include #include #include #include #include #include void main () { #include vec4 diffuseColor = vec4( 1.0 ); #include #include #include float dist = length( vWorldPosition - referencePosition ); dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); dist = saturate( dist ); gl_FragColor = packDepthToRGBA( dist ); } "; var distanceRGBA_vert = "#define DISTANCE varying vec3 vWorldPosition; #include #include #include #include #include #include void main() { #include #include #ifdef USE_DISPLACEMENTMAP #include #include #include #endif #include #include #include #include #include #include #include vWorldPosition = worldPosition.xyz; } "; var equirect_frag = "uniform sampler2D tEquirect; varying vec3 vWorldPosition; #include void main() { vec3 direction = normalize( vWorldPosition ); vec2 sampleUV; sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5; gl_FragColor = texture2D( tEquirect, sampleUV ); } "; var equirect_vert = "varying vec3 vWorldPosition; #include void main() { vWorldPosition = transformDirection( position, modelMatrix ); #include #include } "; var linedashed_frag = "uniform vec3 diffuse; uniform float opacity; uniform float dashSize; uniform float totalSize; varying float vLineDistance; #include #include #include #include #include void main() { #include if ( mod( vLineDistance, totalSize ) > dashSize ) { discard; } vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var linedashed_vert = "uniform float scale; attribute float lineDistance; varying float vLineDistance; #include #include #include #include #include void main() { #include vLineDistance = scale * lineDistance; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; #include #include #include } "; var meshbasic_frag = "uniform vec3 diffuse; uniform float opacity; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include #include #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); #ifdef USE_LIGHTMAP reflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity; #else reflectedLight.indirectDiffuse += vec3( 1.0 ); #endif #include reflectedLight.indirectDiffuse *= diffuseColor.rgb; vec3 outgoingLight = reflectedLight.indirectDiffuse; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var meshbasic_vert = "#include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_ENVMAP #include #include #include #include #endif #include #include #include #include #include #include #include #include #include } "; var meshlambert_frag = "uniform vec3 diffuse; uniform vec3 emissive; uniform float opacity; varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor ); #include reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ); #ifdef DOUBLE_SIDED reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; #else reflectedLight.directDiffuse = vLightFront; #endif reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask(); #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshlambert_vert = "#define LAMBERT varying vec3 vLightFront; #ifdef DOUBLE_SIDED varying vec3 vLightBack; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } "; var meshphong_frag = "#define PHONG uniform vec3 diffuse; uniform vec3 emissive; uniform vec3 specular; uniform float shininess; uniform float opacity; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; #include gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphong_vert = "#define PHONG varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include #include } "; var meshphysical_frag = "#define PHYSICAL uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifndef STANDARD uniform float clearCoat; uniform float clearCoatRoughness; #endif varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { #include vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include #include } "; var meshphysical_vert = "#define PHYSICAL varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include #include #include #include #include #include #include void main() { #include #include #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #include vViewPosition = - mvPosition.xyz; #include #include #include } "; var normal_frag = "#define NORMAL uniform float opacity; #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include #include gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); } "; var normal_vert = "#define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) varying vec3 vViewPosition; #endif #ifndef FLAT_SHADED varying vec3 vNormal; #endif #include #include #include #include #include void main() { #include #include #include #include #include #include #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #include #include #include #include #include #include #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) vViewPosition = - mvPosition.xyz; #endif } "; var points_frag = "uniform vec3 diffuse; uniform float opacity; #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vec4( diffuse, opacity ); #include #include #include #include outgoingLight = diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #include #include #include #include } "; var points_vert = "uniform float size; uniform float scale; #include #include #include #include #include #include void main() { #include #include #include #include #ifdef USE_SIZEATTENUATION gl_PointSize = size * ( scale / - mvPosition.z ); #else gl_PointSize = size; #endif #include #include #include #include } "; var shadow_frag = "uniform vec3 color; uniform float opacity; #include #include #include #include #include #include #include void main() { gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); #include } "; var shadow_vert = "#include #include void main() { #include #include #include #include #include } "; var ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, lights_pars_maps: lights_pars_maps, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normalmap_pars_fragment: normalmap_pars_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, cube_frag: cube_frag, cube_vert: cube_vert, depth_frag: depth_frag, depth_vert: depth_vert, distanceRGBA_frag: distanceRGBA_frag, distanceRGBA_vert: distanceRGBA_vert, equirect_frag: equirect_frag, equirect_vert: equirect_vert, linedashed_frag: linedashed_frag, linedashed_vert: linedashed_vert, meshbasic_frag: meshbasic_frag, meshbasic_vert: meshbasic_vert, meshlambert_frag: meshlambert_frag, meshlambert_vert: meshlambert_vert, meshphong_frag: meshphong_frag, meshphong_vert: meshphong_vert, meshphysical_frag: meshphysical_frag, meshphysical_vert: meshphysical_vert, normal_frag: normal_frag, normal_vert: normal_vert, points_frag: points_frag, points_vert: points_vert, shadow_frag: shadow_frag, shadow_vert: shadow_vert }; /** * Uniform Utilities */ var UniformsUtils = { merge: function ( uniforms ) { var merged = {}; for ( var u = 0; u < uniforms.length; u ++ ) { var tmp = this.clone( uniforms[ u ] ); for ( var p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; }, clone: function ( uniforms_src ) { var uniforms_dst = {}; for ( var u in uniforms_src ) { uniforms_dst[ u ] = {}; for ( var p in uniforms_src[ u ] ) { var parameter_src = uniforms_src[ u ][ p ]; if ( parameter_src && ( parameter_src.isColor || parameter_src.isMatrix3 || parameter_src.isMatrix4 || parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || parameter_src.isTexture ) ) { uniforms_dst[ u ][ p ] = parameter_src.clone(); } else if ( Array.isArray( parameter_src ) ) { uniforms_dst[ u ][ p ] = parameter_src.slice(); } else { uniforms_dst[ u ][ p ] = parameter_src; } } } return uniforms_dst; } }; /** * @author mrdoob / http://mrdoob.com/ */ var ColorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF, "beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2, "brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50, "cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B, "darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B, "darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F, "darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3, "deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222, "floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700, "goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4, "indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00, "lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3, "lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA, "lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32, "linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3, "mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC, "mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD, "navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6, "palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9, "peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "rebeccapurple": 0x663399, "red": 0xFF0000, "rosybrown": 0xBC8F8F, "royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE, "sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA, "springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0, "violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 }; function Color( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } Object.assign( Color.prototype, { isColor: true, r: 1, g: 1, b: 1, set: function ( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === "number" ) { this.setHex( value ); } else if ( typeof value === "string" ) { this.setStyle( value ); } return this; }, setScalar: function ( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; }, setHex: function ( hex ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; return this; }, setRGB: function ( r, g, b ) { this.r = r; this.g = g; this.b = b; return this; }, setHSL: function () { function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } return function setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 h = _Math.euclideanModulo( h, 1 ); s = _Math.clamp( s, 0, 1 ); l = _Math.clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); var q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } return this; }; }(), setStyle: function ( style ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( "THREE.Color: Alpha component of " + style + " will be ignored." ); } } var m; if ( m = /^((?:rgb|hsl)a?)(s*([^)]*))/.exec( style ) ) { // rgb / hsl var color; var name = m[ 1 ]; var components = m[ 2 ]; switch ( name ) { case "rgb": case "rgba": if ( color = /^(d+)s*,s*(d+)s*,s*(d+)s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; handleAlpha( color[ 5 ] ); return this; } if ( color = /^(d+)\%s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; handleAlpha( color[ 5 ] ); return this; } break; case "hsl": case "hsla": if ( color = /^([0-9]*.?[0-9]+)s*,s*(d+)\%s*,s*(d+)\%s*(,s*([0-9]*.?[0-9]+)s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) var h = parseFloat( color[ 1 ] ) / 360; var s = parseInt( color[ 2 ], 10 ) / 100; var l = parseInt( color[ 3 ], 10 ) / 100; handleAlpha( color[ 5 ] ); return this.setHSL( h, s, l ); } break; } } else if ( m = /^#([A-Fa-f0-9]+)$/.exec( style ) ) { // hex color var hex = m[ 1 ]; var size = hex.length; if ( size === 3 ) { // #ff0 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; return this; } else if ( size === 6 ) { // #ff0000 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; return this; } } if ( style && style.length > 0 ) { // color keywords var hex = ColorKeywords[ style ]; if ( hex !== undefined ) { // red this.setHex( hex ); } else { // unknown color console.warn( "THREE.Color: Unknown color " + style ); } } return this; }, clone: function () { return new this.constructor( this.r, this.g, this.b ); }, copy: function ( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; }, copyGammaToLinear: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; this.r = Math.pow( color.r, gammaFactor ); this.g = Math.pow( color.g, gammaFactor ); this.b = Math.pow( color.b, gammaFactor ); return this; }, copyLinearToGamma: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; this.r = Math.pow( color.r, safeInverse ); this.g = Math.pow( color.g, safeInverse ); this.b = Math.pow( color.b, safeInverse ); return this; }, convertGammaToLinear: function ( gammaFactor ) { this.copyGammaToLinear( this, gammaFactor ); return this; }, convertLinearToGamma: function ( gammaFactor ) { this.copyLinearToGamma( this, gammaFactor ); return this; }, getHex: function () { return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; }, getHexString: function () { return ( "000000" + this.getHex().toString( 16 ) ).slice( - 6 ); }, getHSL: function ( target ) { // h,s,l ranges are in 0.0 - 1.0 if ( target === undefined ) { console.warn( "THREE.Color: .getHSL() target is now required" ); target = { h: 0, s: 0, l: 0 }; } var r = this.r, g = this.g, b = this.b; var max = Math.max( r, g, b ); var min = Math.min( r, g, b ); var hue, saturation; var lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { var delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; }, getStyle: function () { return "rgb(" + ( ( this.r * 255 ) | 0 ) + "," + ( ( this.g * 255 ) | 0 ) + "," + ( ( this.b * 255 ) | 0 ) + ")"; }, offsetHSL: function () { var hsl = {}; return function ( h, s, l ) { this.getHSL( hsl ); hsl.h += h; hsl.s += s; hsl.l += l; this.setHSL( hsl.h, hsl.s, hsl.l ); return this; }; }(), add: function ( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; }, addColors: function ( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; }, addScalar: function ( s ) { this.r += s; this.g += s; this.b += s; return this; }, sub: function ( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; }, multiply: function ( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; }, multiplyScalar: function ( s ) { this.r *= s; this.g *= s; this.b *= s; return this; }, lerp: function ( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; }, equals: function ( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; }, toJSON: function () { return this.getHex(); } } ); /** * Uniforms library for shared webgl shaders */ var UniformsLib = { common: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() }, alphaMap: { value: null }, }, specularmap: { specularMap: { value: null }, }, envmap: { envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, refractionRatio: { value: 0.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 Vector2( 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: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color( 0xffffff ) } }, lights: { ambientLightColor: { value: [] }, directionalLights: { value: [], properties: { direction: {}, color: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, directionalShadowMap: { value: [] }, directionalShadowMatrix: { value: [] }, spotLights: { value: [], properties: { color: {}, position: {}, direction: {}, distance: {}, coneCos: {}, penumbraCos: {}, decay: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, spotShadowMap: { value: [] }, spotShadowMatrix: { value: [] }, pointLights: { value: [], properties: { color: {}, position: {}, decay: {}, distance: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {}, shadowCameraNear: {}, shadowCameraFar: {} } }, pointShadowMap: { value: [] }, pointShadowMatrix: { value: [] }, hemisphereLights: { value: [], properties: { direction: {}, skyColor: {}, groundColor: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } } }, points: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, uvTransform: { value: new Matrix3() } } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ */ var ShaderLib = { basic: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, specular: { value: new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, roughness: { value: 0.5 }, metalness: { value: 0.5 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, points: { uniforms: UniformsUtils.merge( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.normal_vert, fragmentShader: ShaderChunk.normal_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null }, }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } } ] ), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: UniformsUtils.merge( [ UniformsLib.lights, UniformsLib.fog, { color: { value: new Color( 0x00000 ) }, opacity: { value: 1.0 } }, ] ), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: UniformsUtils.merge( [ ShaderLib.standard.uniforms, { clearCoat: { value: 0 }, clearCoatRoughness: { value: 0 } } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; /** * @author mrdoob / http://mrdoob.com/ */ function WebGLAnimation() { var context = null; var isAnimating = false; var animationLoop = null; function onAnimationFrame( time, frame ) { if ( isAnimating === false ) return; animationLoop( time, frame ); context.requestAnimationFrame( onAnimationFrame ); } return { start: function () { if ( isAnimating === true ) return; if ( animationLoop === null ) return; context.requestAnimationFrame( onAnimationFrame ); isAnimating = true; }, stop: function () { isAnimating = false; }, setAnimationLoop: function ( callback ) { animationLoop = callback; }, setContext: function ( value ) { context = value; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLAttributes( gl ) { var buffers = new WeakMap(); function createBuffer( attribute, bufferType ) { var array = attribute.array; var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; var buffer = gl.createBuffer(); gl.bindBuffer( bufferType, buffer ); gl.bufferData( bufferType, array, usage ); attribute.onUploadCallback(); var type = gl.FLOAT; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Float64Array ) { console.warn( "THREE.WebGLAttributes: Unsupported data buffer format: Float64Array." ); } else if ( array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer( buffer, attribute, bufferType ) { var array = attribute.array; var updateRange = attribute.updateRange; gl.bindBuffer( bufferType, buffer ); if ( attribute.dynamic === false ) { gl.bufferData( bufferType, array, gl.STATIC_DRAW ); } else if ( updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, array ); } else if ( updateRange.count === 0 ) { console.error( "THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually." ); } else { gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); updateRange.count = - 1; // reset range } } // function get( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return buffers.get( attribute ); } function remove( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers.get( attribute ); if ( data ) { gl.deleteBuffer( data.buffer ); buffers.delete( attribute ); } } function update( attribute, bufferType ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; var data = buffers.get( attribute ); if ( data === undefined ) { buffers.set( attribute, createBuffer( attribute, bufferType ) ); } else if ( data.version < attribute.version ) { updateBuffer( data.buffer, attribute, bufferType ); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Euler( x, y, z, order ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._order = order || Euler.DefaultOrder; } Euler.RotationOrders = [ "XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX" ]; Euler.DefaultOrder = "XYZ"; Object.defineProperties( Euler.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, order: { get: function () { return this._order; }, set: function ( value ) { this._order = value; this.onChangeCallback(); } } } ); Object.assign( Euler.prototype, { isEuler: true, set: function ( x, y, z, order ) { this._x = x; this._y = y; this._z = z; this._order = order || this._order; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._order ); }, copy: function ( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m, order, update ) { var clamp = _Math.clamp; // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements; var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; order = order || this._order; if ( order === "XYZ" ) { this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.99999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } } else if ( order === "YXZ" ) { this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.99999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } } else if ( order === "ZXY" ) { this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.99999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } } else if ( order === "ZYX" ) { this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.99999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } } else if ( order === "YZX" ) { this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.99999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } } else if ( order === "XZY" ) { this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.99999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } } else { console.warn( "THREE.Euler: .setFromRotationMatrix() given unsupported order: " + order ); } this._order = order; if ( update !== false ) this.onChangeCallback(); return this; }, setFromQuaternion: function () { var matrix = new Matrix4(); return function setFromQuaternion( q, order, update ) { matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( matrix, order, update ); }; }(), setFromVector3: function ( v, order ) { return this.set( v.x, v.y, v.z, order || this._order ); }, reorder: function () { // WARNING: this discards revolution information -bhouston var q = new Quaternion(); return function reorder( newOrder ) { q.setFromEuler( this ); return this.setFromQuaternion( q, newOrder ); }; }(), equals: function ( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); }, fromArray: function ( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; }, toVector3: function ( optionalResult ) { if ( optionalResult ) { return optionalResult.set( this._x, this._y, this._z ); } else { return new Vector3( this._x, this._y, this._z ); } }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ */ function Layers() { this.mask = 1 | 0; } Object.assign( Layers.prototype, { set: function ( channel ) { this.mask = 1 << channel | 0; }, enable: function ( channel ) { this.mask |= 1 << channel | 0; }, toggle: function ( channel ) { this.mask ^= 1 << channel | 0; }, disable: function ( channel ) { this.mask &= ~ ( 1 << channel | 0 ); }, test: function ( layers ) { return ( this.mask & layers.mask ) !== 0; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author elephantatwork / www.elephantatwork.ch */ var object3DId = 0; function Object3D() { Object.defineProperty( this, "id", { value: object3DId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Object3D"; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); var position = new Vector3(); var rotation = new Euler(); var quaternion = new Quaternion(); var scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation.onChange( onRotationChange ); quaternion.onChange( onQuaternionChange ); Object.defineProperties( this, { position: { enumerable: true, value: position }, rotation: { enumerable: true, value: rotation }, quaternion: { enumerable: true, value: quaternion }, scale: { enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.userData = {}; } Object3D.DefaultUp = new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Object3D, isObject3D: true, onBeforeRender: function () {}, onAfterRender: function () {}, applyMatrix: function ( matrix ) { this.matrix.multiplyMatrices( matrix, this.matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); }, applyQuaternion: function ( q ) { this.quaternion.premultiply( q ); return this; }, setRotationFromAxisAngle: function ( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); }, setRotationFromEuler: function ( euler ) { this.quaternion.setFromEuler( euler, true ); }, setRotationFromMatrix: function ( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); }, setRotationFromQuaternion: function ( q ) { // assumes q is normalized this.quaternion.copy( q ); }, rotateOnAxis: function () { // rotate object on axis in object space // axis is assumed to be normalized var q1 = new Quaternion(); return function rotateOnAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( q1 ); return this; }; }(), rotateOnWorldAxis: function () { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent var q1 = new Quaternion(); return function rotateOnWorldAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.premultiply( q1 ); return this; }; }(), rotateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function rotateX( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function rotateY( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function rotateZ( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), translateOnAxis: function () { // translate object by distance along axis in object space // axis is assumed to be normalized var v1 = new Vector3(); return function translateOnAxis( axis, distance ) { v1.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( v1.multiplyScalar( distance ) ); return this; }; }(), translateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function translateX( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function translateY( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function translateZ( distance ) { return this.translateOnAxis( v1, distance ); }; }(), localToWorld: function ( vector ) { return vector.applyMatrix4( this.matrixWorld ); }, worldToLocal: function () { var m1 = new Matrix4(); return function worldToLocal( vector ) { return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); }; }(), lookAt: function () { // This method does not support objects with rotated and/or translated parent(s) var m1 = new Matrix4(); var vector = new Vector3(); return function lookAt( x, y, z ) { if ( x.isVector3 ) { vector.copy( x ); } else { vector.set( x, y, z ); } if ( this.isCamera ) { m1.lookAt( this.position, vector, this.up ); } else { m1.lookAt( vector, this.position, this.up ); } this.quaternion.setFromRotationMatrix( m1 ); }; }(), add: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can"t be added as a child of itself.", object ); return this; } if ( ( object && object.isObject3D ) ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; object.dispatchEvent( { type: "added" } ); this.children.push( object ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; }, remove: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } return this; } var index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; object.dispatchEvent( { type: "removed" } ); this.children.splice( index, 1 ); } return this; }, getObjectById: function ( id ) { return this.getObjectByProperty( "id", id ); }, getObjectByName: function ( name ) { return this.getObjectByProperty( "name", name ); }, getObjectByProperty: function ( name, value ) { if ( this[ name ] === value ) return this; for ( var i = 0, l = this.children.length; i < l; i ++ ) { var child = this.children[ i ]; var object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; }, getWorldPosition: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldPosition() target is now required" ); target = new Vector3(); } this.updateMatrixWorld( true ); return target.setFromMatrixPosition( this.matrixWorld ); }, getWorldQuaternion: function () { var position = new Vector3(); var scale = new Vector3(); return function getWorldQuaternion( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldQuaternion() target is now required" ); target = new Quaternion(); } this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, target, scale ); return target; }; }(), getWorldScale: function () { var position = new Vector3(); var quaternion = new Quaternion(); return function getWorldScale( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldScale() target is now required" ); target = new Vector3(); } this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, quaternion, target ); return target; }; }(), getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( target ) { if ( target === undefined ) { console.warn( "THREE.Object3D: .getWorldDirection() target is now required" ); target = new Vector3(); } this.getWorldQuaternion( quaternion ); return target.set( 0, 0, 1 ).applyQuaternion( quaternion ); }; }(), raycast: function () {}, traverse: function ( callback ) { callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } }, traverseVisible: function ( callback ) { if ( this.visible === false ) return; callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } }, traverseAncestors: function ( callback ) { var parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } }, updateMatrix: function () { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; }, updateMatrixWorld: function ( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate || force ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( force ); } }, toJSON: function ( meta ) { // meta is a string when called from JSON.stringify var isRootObject = ( meta === undefined || typeof meta === "string" ); var output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {} }; output.metadata = { version: 4.5, type: "Object", generator: "Object3D.toJSON" }; } // standard Object3D serialization var object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== "" ) object.name = this.name; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; if ( JSON.stringify( this.userData ) !== "{}" ) object.userData = this.userData; object.matrix = this.matrix.toArray(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; // function serialize( library, element ) { if ( library[ element.uuid ] === undefined ) { library[ element.uuid ] = element.toJSON( meta ); } return element.uuid; } if ( this.geometry !== undefined ) { object.geometry = serialize( meta.geometries, this.geometry ); var parameters = this.geometry.parameters; if ( parameters !== undefined && parameters.shapes !== undefined ) { var shapes = parameters.shapes; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; serialize( meta.shapes, shape ); } } else { serialize( meta.shapes, shapes ); } } } if ( this.material !== undefined ) { if ( Array.isArray( this.material ) ) { var uuids = []; for ( var i = 0, l = this.material.length; i < l; i ++ ) { uuids.push( serialize( meta.materials, this.material[ i ] ) ); } object.material = uuids; } else { object.material = serialize( meta.materials, this.material ); } } // if ( this.children.length > 0 ) { object.children = []; for ( var i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } if ( isRootObject ) { var geometries = extractFromCache( meta.geometries ); var materials = extractFromCache( meta.materials ); var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); var shapes = extractFromCache( meta.shapes ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; if ( shapes.length > 0 ) output.shapes = shapes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } }, clone: function ( recursive ) { return new this.constructor().copy( this, recursive ); }, copy: function ( source, recursive ) { if ( recursive === undefined ) recursive = true; this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( var i = 0; i < source.children.length; i ++ ) { var child = source.children[ i ]; this.add( child.clone() ); } } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author WestLangley / http://github.com/WestLangley */ function Camera() { Object3D.call( this ); this.type = "Camera"; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); } Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Camera, isCamera: true, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); return this; }, getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( target ) { if ( target === undefined ) { console.warn( "THREE.Camera: .getWorldDirection() target is now required" ); target = new Vector3(); } this.getWorldQuaternion( quaternion ); return target.set( 0, 0, - 1 ).applyQuaternion( quaternion ); }; }(), updateMatrixWorld: function ( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); this.matrixWorldInverse.getInverse( this.matrixWorld ); }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author alteredq / http://alteredqualia.com/ * @author arose / http://github.com/arose */ function OrthographicCamera( left, right, top, bottom, near, far ) { Camera.call( this ); this.type = "OrthographicCamera"; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = ( near !== undefined ) ? near : 0.1; this.far = ( far !== undefined ) ? far : 2000; this.updateProjectionMatrix(); } OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: OrthographicCamera, isOrthographicCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; }, setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var dx = ( this.right - this.left ) / ( 2 * this.zoom ); var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); var cx = ( this.right + this.left ) / 2; var cy = ( this.top + this.bottom ) / 2; var left = cx - dx; var right = cx + dx; var top = cy + dy; var bottom = cy - dy; if ( this.view !== null && this.view.enabled ) { var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); var scaleW = ( this.right - this.left ) / this.view.width; var scaleH = ( this.top - this.bottom ) / this.view.height; left += scaleW * ( this.view.offsetX / zoomW ); right = left + scaleW * ( this.view.width / zoomW ); top -= scaleH * ( this.view.offsetY / zoomH ); bottom = top - scaleH * ( this.view.height / zoomH ); } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Face3( a, b, c, normal, color, materialIndex ) { this.a = a; this.b = b; this.c = c; this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); this.vertexNormals = Array.isArray( normal ) ? normal : []; this.color = ( color && color.isColor ) ? color : new Color(); this.vertexColors = Array.isArray( color ) ? color : []; this.materialIndex = materialIndex !== undefined ? materialIndex : 0; } Object.assign( Face3.prototype, { clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.a = source.a; this.b = source.b; this.c = source.c; this.normal.copy( source.normal ); this.color.copy( source.color ); this.materialIndex = source.materialIndex; for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { this.vertexColors[ i ] = source.vertexColors[ i ].clone(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author bhouston / http://clara.io */ var geometryId = 0; // Geometry uses even numbers as Id function Geometry() { Object.defineProperty( this, "id", { value: geometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Geometry"; this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.elementsNeedUpdate = false; this.verticesNeedUpdate = false; this.uvsNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; this.groupsNeedUpdate = false; } Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Geometry, isGeometry: true, applyMatrix: function ( matrix ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { var vertex = this.vertices[ i ]; vertex.applyMatrix4( matrix ); } for ( var i = 0, il = this.faces.length; i < il; i ++ ) { var face = this.faces[ i ]; face.normal.applyMatrix3( normalMatrix ).normalize(); for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); } } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), fromBufferGeometry: function ( geometry ) { var scope = this; var indices = geometry.index !== null ? geometry.index.array : undefined; var attributes = geometry.attributes; var positions = attributes.position.array; var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; var colors = attributes.color !== undefined ? attributes.color.array : undefined; var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; var tempNormals = []; var tempUVs = []; var tempUVs2 = []; for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); if ( normals !== undefined ) { tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); } if ( colors !== undefined ) { scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); } if ( uvs !== undefined ) { tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); } if ( uvs2 !== undefined ) { tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); } } function addFace( a, b, c, materialIndex ) { var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); scope.faces.push( face ); if ( uvs !== undefined ) { scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); } if ( uvs2 !== undefined ) { scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); } } var groups = geometry.groups; if ( groups.length > 0 ) { for ( var i = 0; i < groups.length; i ++ ) { var group = groups[ i ]; var start = group.start; var count = group.count; for ( var j = start, jl = start + count; j < jl; j += 3 ) { if ( indices !== undefined ) { addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); } else { addFace( j, j + 1, j + 2, group.materialIndex ); } } } } else { if ( indices !== undefined ) { for ( var i = 0; i < indices.length; i += 3 ) { addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } } else { for ( var i = 0; i < positions.length / 3; i += 3 ) { addFace( i, i + 1, i + 2 ); } } } this.computeFaceNormals(); if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } return this; }, center: function () { var offset = new Vector3(); return function center() { this.computeBoundingBox(); this.boundingBox.getCenter( offset ).negate(); this.translate( offset.x, offset.y, offset.z ); return this; }; }(), normalize: function () { this.computeBoundingSphere(); var center = this.boundingSphere.center; var radius = this.boundingSphere.radius; var s = radius === 0 ? 1 : 1.0 / radius; var matrix = new Matrix4(); matrix.set( s, 0, 0, - s * center.x, 0, s, 0, - s * center.y, 0, 0, s, - s * center.z, 0, 0, 0, 1 ); this.applyMatrix( matrix ); return this; }, computeFaceNormals: function () { var cb = new Vector3(), ab = new Vector3(); for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { var face = this.faces[ f ]; var vA = this.vertices[ face.a ]; var vB = this.vertices[ face.b ]; var vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); cb.normalize(); face.normal.copy( cb ); } }, computeVertexNormals: function ( areaWeighted ) { if ( areaWeighted === undefined ) areaWeighted = true; var v, vl, f, fl, face, vertices; vertices = new Array( this.vertices.length ); for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ] = new Vector3(); } if ( areaWeighted ) { // vertex normals weighted by triangle areas // http://www.iquilezles.org/www/articles/normals/normals.htm var vA, vB, vC; var cb = new Vector3(), ab = new Vector3(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vA = this.vertices[ face.a ]; vB = this.vertices[ face.b ]; vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); vertices[ face.a ].add( cb ); vertices[ face.b ].add( cb ); vertices[ face.c ].add( cb ); } } else { this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vertices[ face.a ].add( face.normal ); vertices[ face.b ].add( face.normal ); vertices[ face.c ].add( face.normal ); } } for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ].normalize(); } for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( vertices[ face.a ] ); vertexNormals[ 1 ].copy( vertices[ face.b ] ); vertexNormals[ 2 ].copy( vertices[ face.c ] ); } else { vertexNormals[ 0 ] = vertices[ face.a ].clone(); vertexNormals[ 1 ] = vertices[ face.b ].clone(); vertexNormals[ 2 ] = vertices[ face.c ].clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeFlatVertexNormals: function () { var f, fl, face; this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( face.normal ); vertexNormals[ 1 ].copy( face.normal ); vertexNormals[ 2 ].copy( face.normal ); } else { vertexNormals[ 0 ] = face.normal.clone(); vertexNormals[ 1 ] = face.normal.clone(); vertexNormals[ 2 ] = face.normal.clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeMorphNormals: function () { var i, il, f, fl, face; // save original normals // - create temp variables on first access // otherwise just copy (for faster repeated calls) for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; if ( ! face.__originalFaceNormal ) { face.__originalFaceNormal = face.normal.clone(); } else { face.__originalFaceNormal.copy( face.normal ); } if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { if ( ! face.__originalVertexNormals[ i ] ) { face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); } else { face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } } // use temp geometry to compute face and vertex normals for each morph var tmpGeo = new Geometry(); tmpGeo.faces = this.faces; for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { // create on first access if ( ! this.morphNormals[ i ] ) { this.morphNormals[ i ] = {}; this.morphNormals[ i ].faceNormals = []; this.morphNormals[ i ].vertexNormals = []; var dstNormalsFace = this.morphNormals[ i ].faceNormals; var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { faceNormal = new Vector3(); vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; dstNormalsFace.push( faceNormal ); dstNormalsVertex.push( vertexNormals ); } } var morphNormals = this.morphNormals[ i ]; // set vertices to morph target tmpGeo.vertices = this.morphTargets[ i ].vertices; // compute morph normals tmpGeo.computeFaceNormals(); tmpGeo.computeVertexNormals(); // store morph normals var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; faceNormal = morphNormals.faceNormals[ f ]; vertexNormals = morphNormals.vertexNormals[ f ]; faceNormal.copy( face.normal ); vertexNormals.a.copy( face.vertexNormals[ 0 ] ); vertexNormals.b.copy( face.vertexNormals[ 1 ] ); vertexNormals.c.copy( face.vertexNormals[ 2 ] ); } } // restore original normals for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; face.normal = face.__originalFaceNormal; face.vertexNormals = face.__originalVertexNormals; } }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.setFromPoints( this.vertices ); }, computeBoundingSphere: function () { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.setFromPoints( this.vertices ); }, merge: function ( geometry, matrix, materialIndexOffset ) { if ( ! ( geometry && geometry.isGeometry ) ) { console.error( "THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.", geometry ); return; } var normalMatrix, vertexOffset = this.vertices.length, vertices1 = this.vertices, vertices2 = geometry.vertices, faces1 = this.faces, faces2 = geometry.faces, uvs1 = this.faceVertexUvs[ 0 ], uvs2 = geometry.faceVertexUvs[ 0 ], colors1 = this.colors, colors2 = geometry.colors; if ( materialIndexOffset === undefined ) materialIndexOffset = 0; if ( matrix !== undefined ) { normalMatrix = new Matrix3().getNormalMatrix( matrix ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); vertices1.push( vertexCopy ); } // colors for ( var i = 0, il = colors2.length; i < il; i ++ ) { colors1.push( colors2[ i ].clone() ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); faceCopy.normal.copy( face.normal ); if ( normalMatrix !== undefined ) { faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); } for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( normalMatrix !== undefined ) { normal.applyMatrix3( normalMatrix ).normalize(); } faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex + materialIndexOffset; faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; if ( uv === undefined ) { continue; } for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( uv[ j ].clone() ); } uvs1.push( uvCopy ); } }, mergeMesh: function ( mesh ) { if ( ! ( mesh && mesh.isMesh ) ) { console.error( "THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.", mesh ); return; } if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); this.merge( mesh.geometry, mesh.matrix ); }, /* * Checks for duplicate vertices with hashmap. * Duplicated vertices are removed * and faces" vertices are updated. */ mergeVertices: function () { var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique = [], changes = []; var v, key; var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 var precision = Math.pow( 10, precisionPoints ); var i, il, face; var indices, j, jl; for ( i = 0, il = this.vertices.length; i < il; i ++ ) { v = this.vertices[ i ]; key = Math.round( v.x * precision ) + "_" + Math.round( v.y * precision ) + "_" + Math.round( v.z * precision ); if ( verticesMap[ key ] === undefined ) { verticesMap[ key ] = i; unique.push( this.vertices[ i ] ); changes[ i ] = unique.length - 1; } else { //console.log("Duplicate vertex found. ", i, " could be using ", verticesMap[key]); changes[ i ] = changes[ verticesMap[ key ] ]; } } // if faces are completely degenerate after merging vertices, we // have to remove them from the geometry. var faceIndicesToRemove = []; for ( i = 0, il = this.faces.length; i < il; i ++ ) { face = this.faces[ i ]; face.a = changes[ face.a ]; face.b = changes[ face.b ]; face.c = changes[ face.c ]; indices = [ face.a, face.b, face.c ]; // if any duplicate vertices are found in a Face3 // we have to remove the face as nothing can be saved for ( var n = 0; n < 3; n ++ ) { if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { faceIndicesToRemove.push( i ); break; } } } for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { var idx = faceIndicesToRemove[ i ]; this.faces.splice( idx, 1 ); for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { this.faceVertexUvs[ j ].splice( idx, 1 ); } } // Use unique set of vertices var diff = this.vertices.length - unique.length; this.vertices = unique; return diff; }, setFromPoints: function ( points ) { this.vertices = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return this; }, sortFacesByMaterialIndex: function () { var faces = this.faces; var length = faces.length; // tag faces for ( var i = 0; i < length; i ++ ) { faces[ i ]._id = i; } // sort faces function materialIndexSort( a, b ) { return a.materialIndex - b.materialIndex; } faces.sort( materialIndexSort ); // sort uvs var uvs1 = this.faceVertexUvs[ 0 ]; var uvs2 = this.faceVertexUvs[ 1 ]; var newUvs1, newUvs2; if ( uvs1 && uvs1.length === length ) newUvs1 = []; if ( uvs2 && uvs2.length === length ) newUvs2 = []; for ( var i = 0; i < length; i ++ ) { var id = faces[ i ]._id; if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); } if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Geometry", generator: "Geometry.toJSON" } }; // standard Geometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } var vertices = []; for ( var i = 0; i < this.vertices.length; i ++ ) { var vertex = this.vertices[ i ]; vertices.push( vertex.x, vertex.y, vertex.z ); } var faces = []; var normals = []; var normalsHash = {}; var colors = []; var colorsHash = {}; var uvs = []; var uvsHash = {}; for ( var i = 0; i < this.faces.length; i ++ ) { var face = this.faces[ i ]; var hasMaterial = true; var hasFaceUv = false; // deprecated var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; var hasFaceNormal = face.normal.length() > 0; var hasFaceVertexNormal = face.vertexNormals.length > 0; var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; var hasFaceVertexColor = face.vertexColors.length > 0; var faceType = 0; faceType = setBit( faceType, 0, 0 ); // isQuad faceType = setBit( faceType, 1, hasMaterial ); faceType = setBit( faceType, 2, hasFaceUv ); faceType = setBit( faceType, 3, hasFaceVertexUv ); faceType = setBit( faceType, 4, hasFaceNormal ); faceType = setBit( faceType, 5, hasFaceVertexNormal ); faceType = setBit( faceType, 6, hasFaceColor ); faceType = setBit( faceType, 7, hasFaceVertexColor ); faces.push( faceType ); faces.push( face.a, face.b, face.c ); faces.push( face.materialIndex ); if ( hasFaceVertexUv ) { var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; faces.push( getUvIndex( faceVertexUvs[ 0 ] ), getUvIndex( faceVertexUvs[ 1 ] ), getUvIndex( faceVertexUvs[ 2 ] ) ); } if ( hasFaceNormal ) { faces.push( getNormalIndex( face.normal ) ); } if ( hasFaceVertexNormal ) { var vertexNormals = face.vertexNormals; faces.push( getNormalIndex( vertexNormals[ 0 ] ), getNormalIndex( vertexNormals[ 1 ] ), getNormalIndex( vertexNormals[ 2 ] ) ); } if ( hasFaceColor ) { faces.push( getColorIndex( face.color ) ); } if ( hasFaceVertexColor ) { var vertexColors = face.vertexColors; faces.push( getColorIndex( vertexColors[ 0 ] ), getColorIndex( vertexColors[ 1 ] ), getColorIndex( vertexColors[ 2 ] ) ); } } function setBit( value, position, enabled ) { return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); } function getNormalIndex( normal ) { var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); if ( normalsHash[ hash ] !== undefined ) { return normalsHash[ hash ]; } normalsHash[ hash ] = normals.length / 3; normals.push( normal.x, normal.y, normal.z ); return normalsHash[ hash ]; } function getColorIndex( color ) { var hash = color.r.toString() + color.g.toString() + color.b.toString(); if ( colorsHash[ hash ] !== undefined ) { return colorsHash[ hash ]; } colorsHash[ hash ] = colors.length; colors.push( color.getHex() ); return colorsHash[ hash ]; } function getUvIndex( uv ) { var hash = uv.x.toString() + uv.y.toString(); if ( uvsHash[ hash ] !== undefined ) { return uvsHash[ hash ]; } uvsHash[ hash ] = uvs.length / 2; uvs.push( uv.x, uv.y ); return uvsHash[ hash ]; } data.data = {}; data.data.vertices = vertices; data.data.normals = normals; if ( colors.length > 0 ) data.data.colors = colors; if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility data.data.faces = faces; return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new Geometry().copy( this ); }, copy: function ( source ) { var i, il, j, jl, k, kl; // reset this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [[]]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // vertices var vertices = source.vertices; for ( i = 0, il = vertices.length; i < il; i ++ ) { this.vertices.push( vertices[ i ].clone() ); } // colors var colors = source.colors; for ( i = 0, il = colors.length; i < il; i ++ ) { this.colors.push( colors[ i ].clone() ); } // faces var faces = source.faces; for ( i = 0, il = faces.length; i < il; i ++ ) { this.faces.push( faces[ i ].clone() ); } // face vertex uvs for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { var faceVertexUvs = source.faceVertexUvs[ i ]; if ( this.faceVertexUvs[ i ] === undefined ) { this.faceVertexUvs[ i ] = []; } for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { var uvs = faceVertexUvs[ j ], uvsCopy = []; for ( k = 0, kl = uvs.length; k < kl; k ++ ) { var uv = uvs[ k ]; uvsCopy.push( uv.clone() ); } this.faceVertexUvs[ i ].push( uvsCopy ); } } // morph targets var morphTargets = source.morphTargets; for ( i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = {}; morphTarget.name = morphTargets[ i ].name; // vertices if ( morphTargets[ i ].vertices !== undefined ) { morphTarget.vertices = []; for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); } } // normals if ( morphTargets[ i ].normals !== undefined ) { morphTarget.normals = []; for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); } } this.morphTargets.push( morphTarget ); } // morph normals var morphNormals = source.morphNormals; for ( i = 0, il = morphNormals.length; i < il; i ++ ) { var morphNormal = {}; // vertex normals if ( morphNormals[ i ].vertexNormals !== undefined ) { morphNormal.vertexNormals = []; for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; var destVertexNormal = {}; destVertexNormal.a = srcVertexNormal.a.clone(); destVertexNormal.b = srcVertexNormal.b.clone(); destVertexNormal.c = srcVertexNormal.c.clone(); morphNormal.vertexNormals.push( destVertexNormal ); } } // face normals if ( morphNormals[ i ].faceNormals !== undefined ) { morphNormal.faceNormals = []; for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); } } this.morphNormals.push( morphNormal ); } // skin weights var skinWeights = source.skinWeights; for ( i = 0, il = skinWeights.length; i < il; i ++ ) { this.skinWeights.push( skinWeights[ i ].clone() ); } // skin indices var skinIndices = source.skinIndices; for ( i = 0, il = skinIndices.length; i < il; i ++ ) { this.skinIndices.push( skinIndices[ i ].clone() ); } // line distances var lineDistances = source.lineDistances; for ( i = 0, il = lineDistances.length; i < il; i ++ ) { this.lineDistances.push( lineDistances[ i ] ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // update flags this.elementsNeedUpdate = source.elementsNeedUpdate; this.verticesNeedUpdate = source.verticesNeedUpdate; this.uvsNeedUpdate = source.uvsNeedUpdate; this.normalsNeedUpdate = source.normalsNeedUpdate; this.colorsNeedUpdate = source.colorsNeedUpdate; this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; this.groupsNeedUpdate = source.groupsNeedUpdate; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferAttribute( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.name = ""; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } Object.defineProperty( BufferAttribute.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( BufferAttribute.prototype, { isBufferAttribute: true, onUploadCallback: function () {}, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.itemSize : 0; this.array = array; return this; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.name = source.name; this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( var i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, copyArray: function ( array ) { this.array.set( array ); return this; }, copyColorsArray: function ( colors ) { var array = this.array, offset = 0; for ( var i = 0, l = colors.length; i < l; i ++ ) { var color = colors[ i ]; if ( color === undefined ) { console.warn( "THREE.BufferAttribute.copyColorsArray(): color is undefined", i ); color = new Color(); } array[ offset ++ ] = color.r; array[ offset ++ ] = color.g; array[ offset ++ ] = color.b; } return this; }, copyVector2sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector2sArray(): vector is undefined", i ); vector = new Vector2(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; } return this; }, copyVector3sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector3sArray(): vector is undefined", i ); vector = new Vector3(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; } return this; }, copyVector4sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( "THREE.BufferAttribute.copyVector4sArray(): vector is undefined", i ); vector = new Vector4(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; array[ offset ++ ] = vector.w; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, getX: function ( index ) { return this.array[ index * this.itemSize ]; }, setX: function ( index, x ) { this.array[ index * this.itemSize ] = x; return this; }, getY: function ( index ) { return this.array[ index * this.itemSize + 1 ]; }, setY: function ( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; }, getZ: function ( index ) { return this.array[ index * this.itemSize + 2 ]; }, setZ: function ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; }, getW: function ( index ) { return this.array[ index * this.itemSize + 3 ]; }, setW: function ( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; }, setXY: function ( index, x, y ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; }, clone: function () { return new this.constructor( this.array, this.itemSize ).copy( this ); } } ); // function Int8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); } Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; function Uint8BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); } Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); } Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; function Int16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); } Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; function Uint16BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); } Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; function Int32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); } Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; function Uint32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); } Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; function Float32BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); } Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; function Float64BufferAttribute( array, itemSize, normalized ) { BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); } Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; /** * @author mrdoob / http://mrdoob.com/ */ function DirectGeometry() { this.vertices = []; this.normals = []; this.colors = []; this.uvs = []; this.uvs2 = []; this.groups = []; this.morphTargets = {}; this.skinWeights = []; this.skinIndices = []; // this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.verticesNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.uvsNeedUpdate = false; this.groupsNeedUpdate = false; } Object.assign( DirectGeometry.prototype, { computeGroups: function ( geometry ) { var group; var groups = []; var materialIndex = undefined; var faces = geometry.faces; for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; // materials if ( face.materialIndex !== materialIndex ) { materialIndex = face.materialIndex; if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } group = { start: i * 3, materialIndex: materialIndex }; } } if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } this.groups = groups; }, fromGeometry: function ( geometry ) { var faces = geometry.faces; var vertices = geometry.vertices; var faceVertexUvs = geometry.faceVertexUvs; var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; // morphs var morphTargets = geometry.morphTargets; var morphTargetsLength = morphTargets.length; var morphTargetsPosition; if ( morphTargetsLength > 0 ) { morphTargetsPosition = []; for ( var i = 0; i < morphTargetsLength; i ++ ) { morphTargetsPosition[ i ] = []; } this.morphTargets.position = morphTargetsPosition; } var morphNormals = geometry.morphNormals; var morphNormalsLength = morphNormals.length; var morphTargetsNormal; if ( morphNormalsLength > 0 ) { morphTargetsNormal = []; for ( var i = 0; i < morphNormalsLength; i ++ ) { morphTargetsNormal[ i ] = []; } this.morphTargets.normal = morphTargetsNormal; } // skins var skinIndices = geometry.skinIndices; var skinWeights = geometry.skinWeights; var hasSkinIndices = skinIndices.length === vertices.length; var hasSkinWeights = skinWeights.length === vertices.length; // if ( faces.length === 0 ) { console.error( "THREE.DirectGeometry: Faceless geometries are not supported." ); } for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); } else { var normal = face.normal; this.normals.push( normal, normal, normal ); } var vertexColors = face.vertexColors; if ( vertexColors.length === 3 ) { this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); } else { var color = face.color; this.colors.push( color, color, color ); } if ( hasFaceVertexUv === true ) { var vertexUvs = faceVertexUvs[ 0 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ", i ); this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); } } if ( hasFaceVertexUv2 === true ) { var vertexUvs = faceVertexUvs[ 1 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ", i ); this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); } } // morphs for ( var j = 0; j < morphTargetsLength; j ++ ) { var morphTarget = morphTargets[ j ].vertices; morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); } for ( var j = 0; j < morphNormalsLength; j ++ ) { var morphNormal = morphNormals[ j ].vertexNormals[ i ]; morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); } // skins if ( hasSkinIndices ) { this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); } if ( hasSkinWeights ) { this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); } } this.computeGroups( geometry ); this.verticesNeedUpdate = geometry.verticesNeedUpdate; this.normalsNeedUpdate = geometry.normalsNeedUpdate; this.colorsNeedUpdate = geometry.colorsNeedUpdate; this.uvsNeedUpdate = geometry.uvsNeedUpdate; this.groupsNeedUpdate = geometry.groupsNeedUpdate; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function arrayMax( array ) { if ( array.length === 0 ) return - Infinity; var max = array[ 0 ]; for ( var i = 1, l = array.length; i < l; ++ i ) { if ( array[ i ] > max ) max = array[ i ]; } return max; } /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id function BufferGeometry() { Object.defineProperty( this, "id", { value: bufferGeometryId += 2 } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "BufferGeometry"; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: BufferGeometry, isBufferGeometry: true, getIndex: function () { return this.index; }, setIndex: function ( index ) { if ( Array.isArray( index ) ) { this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { this.index = index; } }, addAttribute: function ( name, attribute ) { if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { console.warn( "THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )." ); return this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); } if ( name === "index" ) { console.warn( "THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute." ); this.setIndex( attribute ); return this; } this.attributes[ name ] = attribute; return this; }, getAttribute: function ( name ) { return this.attributes[ name ]; }, removeAttribute: function ( name ) { delete this.attributes[ name ]; return this; }, addGroup: function ( start, count, materialIndex ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex !== undefined ? materialIndex : 0 } ); }, clearGroups: function () { this.groups = []; }, setDrawRange: function ( start, count ) { this.drawRange.start = start; this.drawRange.count = count; }, applyMatrix: function ( matrix ) { var position = this.attributes.position; if ( position !== undefined ) { matrix.applyToBufferAttribute( position ); position.needsUpdate = true; } var normal = this.attributes.normal; if ( normal !== undefined ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); normalMatrix.applyToBufferAttribute( normal ); normal.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; }, rotateX: function () { // rotate geometry around world x-axis var m1 = new Matrix4(); return function rotateX( angle ) { m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1 = new Matrix4(); return function rotateY( angle ) { m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1 = new Matrix4(); return function rotateZ( angle ) { m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1 = new Matrix4(); return function translate( x, y, z ) { m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1 = new Matrix4(); return function scale( x, y, z ) { m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj = new Object3D(); return function lookAt( vector ) { obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), center: function () { var offset = new Vector3(); return function center() { this.computeBoundingBox(); this.boundingBox.getCenter( offset ).negate(); this.translate( offset.x, offset.y, offset.z ); return this; }; }(), setFromObject: function ( object ) { // console.log( "THREE.BufferGeometry.setFromObject(). Converting", object, this ); var geometry = object.geometry; if ( object.isPoints || object.isLine ) { var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); this.addAttribute( "position", positions.copyVector3sArray( geometry.vertices ) ); this.addAttribute( "color", colors.copyColorsArray( geometry.colors ) ); if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); this.addAttribute( "lineDistance", lineDistances.copyArray( geometry.lineDistances ) ); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } } else if ( object.isMesh ) { if ( geometry && geometry.isGeometry ) { this.fromGeometry( geometry ); } } return this; }, setFromPoints: function ( points ) { var position = []; for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; position.push( point.x, point.y, point.z || 0 ); } this.addAttribute( "position", new Float32BufferAttribute( position, 3 ) ); return this; }, updateFromObject: function ( object ) { var geometry = object.geometry; if ( object.isMesh ) { var direct = geometry.__directGeometry; if ( geometry.elementsNeedUpdate === true ) { direct = undefined; geometry.elementsNeedUpdate = false; } if ( direct === undefined ) { return this.fromGeometry( geometry ); } direct.verticesNeedUpdate = geometry.verticesNeedUpdate; direct.normalsNeedUpdate = geometry.normalsNeedUpdate; direct.colorsNeedUpdate = geometry.colorsNeedUpdate; direct.uvsNeedUpdate = geometry.uvsNeedUpdate; direct.groupsNeedUpdate = geometry.groupsNeedUpdate; geometry.verticesNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.groupsNeedUpdate = false; geometry = direct; } var attribute; if ( geometry.verticesNeedUpdate === true ) { attribute = this.attributes.position; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.vertices ); attribute.needsUpdate = true; } geometry.verticesNeedUpdate = false; } if ( geometry.normalsNeedUpdate === true ) { attribute = this.attributes.normal; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.normals ); attribute.needsUpdate = true; } geometry.normalsNeedUpdate = false; } if ( geometry.colorsNeedUpdate === true ) { attribute = this.attributes.color; if ( attribute !== undefined ) { attribute.copyColorsArray( geometry.colors ); attribute.needsUpdate = true; } geometry.colorsNeedUpdate = false; } if ( geometry.uvsNeedUpdate ) { attribute = this.attributes.uv; if ( attribute !== undefined ) { attribute.copyVector2sArray( geometry.uvs ); attribute.needsUpdate = true; } geometry.uvsNeedUpdate = false; } if ( geometry.lineDistancesNeedUpdate ) { attribute = this.attributes.lineDistance; if ( attribute !== undefined ) { attribute.copyArray( geometry.lineDistances ); attribute.needsUpdate = true; } geometry.lineDistancesNeedUpdate = false; } if ( geometry.groupsNeedUpdate ) { geometry.computeGroups( object.geometry ); this.groups = geometry.groups; geometry.groupsNeedUpdate = false; } return this; }, fromGeometry: function ( geometry ) { geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); return this.fromDirectGeometry( geometry.__directGeometry ); }, fromDirectGeometry: function ( geometry ) { var positions = new Float32Array( geometry.vertices.length * 3 ); this.addAttribute( "position", new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); if ( geometry.normals.length > 0 ) { var normals = new Float32Array( geometry.normals.length * 3 ); this.addAttribute( "normal", new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); } if ( geometry.colors.length > 0 ) { var colors = new Float32Array( geometry.colors.length * 3 ); this.addAttribute( "color", new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); } if ( geometry.uvs.length > 0 ) { var uvs = new Float32Array( geometry.uvs.length * 2 ); this.addAttribute( "uv", new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); } if ( geometry.uvs2.length > 0 ) { var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); this.addAttribute( "uv2", new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); } // groups this.groups = geometry.groups; // morphs for ( var name in geometry.morphTargets ) { var array = []; var morphTargets = geometry.morphTargets[ name ]; for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { var morphTarget = morphTargets[ i ]; var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); array.push( attribute.copyVector3sArray( morphTarget ) ); } this.morphAttributes[ name ] = array; } // skinning if ( geometry.skinIndices.length > 0 ) { var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); this.addAttribute( "skinIndex", skinIndices.copyVector4sArray( geometry.skinIndices ) ); } if ( geometry.skinWeights.length > 0 ) { var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); this.addAttribute( "skinWeight", skinWeights.copyVector4sArray( geometry.skinWeights ) ); } // if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } return this; }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } var position = this.attributes.position; if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( "THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.", this ); } }, computeBoundingSphere: function () { var box = new Box3(); var vector = new Vector3(); return function computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } var position = this.attributes.position; if ( position ) { var center = this.boundingSphere.center; box.setFromBufferAttribute( position ); box.getCenter( center ); // hoping to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case var maxRadiusSq = 0; for ( var i = 0, il = position.count; i < il; i ++ ) { vector.x = position.getX( i ); vector.y = position.getY( i ); vector.z = position.getZ( i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( "THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.", this ); } } }; }(), computeFaceNormals: function () { // backwards compatibility }, computeVertexNormals: function () { var index = this.index; var attributes = this.attributes; var groups = this.groups; if ( attributes.position ) { var positions = attributes.position.array; if ( attributes.normal === undefined ) { this.addAttribute( "normal", new BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { // reset existing normals to zero var array = attributes.normal.array; for ( var i = 0, il = array.length; i < il; i ++ ) { array[ i ] = 0; } } var normals = attributes.normal.array; var vA, vB, vC; var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); var cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { var indices = index.array; if ( groups.length === 0 ) { this.addGroup( 0, indices.length ); } for ( var j = 0, jl = groups.length; j < jl; ++ j ) { var group = groups[ j ]; var start = group.start; var count = group.count; for ( var i = start, il = start + count; i < il; i += 3 ) { vA = indices[ i + 0 ] * 3; vB = indices[ i + 1 ] * 3; vC = indices[ i + 2 ] * 3; pA.fromArray( positions, vA ); pB.fromArray( positions, vB ); pC.fromArray( positions, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ vA ] += cb.x; normals[ vA + 1 ] += cb.y; normals[ vA + 2 ] += cb.z; normals[ vB ] += cb.x; normals[ vB + 1 ] += cb.y; normals[ vB + 2 ] += cb.z; normals[ vC ] += cb.x; normals[ vC + 1 ] += cb.y; normals[ vC + 2 ] += cb.z; } } } else { // non-indexed elements (unconnected triangle soup) for ( var i = 0, il = positions.length; i < il; i += 9 ) { pA.fromArray( positions, i ); pB.fromArray( positions, i + 3 ); pC.fromArray( positions, i + 6 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ i ] = cb.x; normals[ i + 1 ] = cb.y; normals[ i + 2 ] = cb.z; normals[ i + 3 ] = cb.x; normals[ i + 4 ] = cb.y; normals[ i + 5 ] = cb.z; normals[ i + 6 ] = cb.x; normals[ i + 7 ] = cb.y; normals[ i + 8 ] = cb.z; } } this.normalizeNormals(); attributes.normal.needsUpdate = true; } }, merge: function ( geometry, offset ) { if ( ! ( geometry && geometry.isBufferGeometry ) ) { console.error( "THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.", geometry ); return; } if ( offset === undefined ) { offset = 0; console.warn( "THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " + "Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge." ); } var attributes = this.attributes; for ( var key in attributes ) { if ( geometry.attributes[ key ] === undefined ) continue; var attribute1 = attributes[ key ]; var attributeArray1 = attribute1.array; var attribute2 = geometry.attributes[ key ]; var attributeArray2 = attribute2.array; var attributeSize = attribute2.itemSize; for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; } } return this; }, normalizeNormals: function () { var vector = new Vector3(); return function normalizeNormals() { var normals = this.attributes.normal; for ( var i = 0, il = normals.count; i < il; i ++ ) { vector.x = normals.getX( i ); vector.y = normals.getY( i ); vector.z = normals.getZ( i ); vector.normalize(); normals.setXYZ( i, vector.x, vector.y, vector.z ); } }; }(), toNonIndexed: function () { if ( this.index === null ) { console.warn( "THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed." ); return this; } var geometry2 = new BufferGeometry(); var indices = this.index.array; var attributes = this.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; var array = attribute.array; var itemSize = attribute.itemSize; var array2 = new array.constructor( indices.length * itemSize ); var index = 0, index2 = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { index = indices[ i ] * itemSize; for ( var j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); } var groups = this.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; geometry2.addGroup( group.start, group.count, group.materialIndex ); } return geometry2; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "BufferGeometry", generator: "BufferGeometry.toJSON" } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } data.data = { attributes: {} }; var index = this.index; if ( index !== null ) { var array = Array.prototype.slice.call( index.array ); data.data.index = { type: index.array.constructor.name, array: array }; } var attributes = this.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var array = Array.prototype.slice.call( attribute.array ); data.data.attributes[ key ] = { itemSize: attribute.itemSize, type: attribute.array.constructor.name, array: array, normalized: attribute.normalized }; } var groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } var boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new BufferGeometry().copy( this ); }, copy: function ( source ) { var name, i, l; // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // name this.name = source.name; // index var index = source.index; if ( index !== null ) { this.setIndex( index.clone() ); } // attributes var attributes = source.attributes; for ( name in attributes ) { var attribute = attributes[ name ]; this.addAttribute( name, attribute.clone() ); } // morph attributes var morphAttributes = source.morphAttributes; for ( name in morphAttributes ) { var array = []; var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { array.push( morphAttribute[ i ].clone() ); } this.morphAttributes[ name ] = array; } // groups var groups = source.groups; for ( i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } // bounding box var boundingBox = source.boundingBox; if ( boundingBox !== null ) { this.boundingBox = boundingBox.clone(); } // bounding sphere var boundingSphere = source.boundingSphere; if ( boundingSphere !== null ) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // BoxGeometry function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { Geometry.call( this ); this.type = "BoxGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); this.mergeVertices(); } BoxGeometry.prototype = Object.create( Geometry.prototype ); BoxGeometry.prototype.constructor = BoxGeometry; // BoxBufferGeometry function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { BufferGeometry.call( this ); this.type = "BoxBufferGeometry"; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; var scope = this; width = width || 1; height = height || 1; depth = depth || 1; // segments widthSegments = Math.floor( widthSegments ) || 1; heightSegments = Math.floor( heightSegments ) || 1; depthSegments = Math.floor( depthSegments ) || 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var numberOfVertices = 0; var groupStart = 0; // build each side of the box geometry buildPlane( "z", "y", "x", - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( "z", "y", "x", 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( "x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( "x", "z", "y", 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( "x", "y", "z", 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( "x", "y", "z", - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { var segmentWidth = width / gridX; var segmentHeight = height / gridY; var widthHalf = width / 2; var heightHalf = height / 2; var depthHalf = depth / 2; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var vertexCounter = 0; var groupCount = 0; var ix, iy; var vector = new Vector3(); // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segmentHeight - heightHalf; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices.push( vector.x, vector.y, vector.z ); // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals.push( vector.x, vector.y, vector.z ); // uvs uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = numberOfVertices + ix + gridX1 * iy; var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // PlaneGeometry function PlaneGeometry( width, height, widthSegments, heightSegments ) { Geometry.call( this ); this.type = "PlaneGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); this.mergeVertices(); } PlaneGeometry.prototype = Object.create( Geometry.prototype ); PlaneGeometry.prototype.constructor = PlaneGeometry; // PlaneBufferGeometry function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { BufferGeometry.call( this ); this.type = "PlaneBufferGeometry"; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; width = width || 1; height = height || 1; var width_half = width / 2; var height_half = height / 2; var gridX = Math.floor( widthSegments ) || 1; var gridY = Math.floor( heightSegments ) || 1; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var segment_width = width / gridX; var segment_height = height / gridY; var ix, iy; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy < gridY1; iy ++ ) { var y = iy * segment_height - height_half; for ( ix = 0; ix < gridX1; ix ++ ) { var x = ix * segment_width - width_half; vertices.push( x, - y, 0 ); normals.push( 0, 0, 1 ); uvs.push( ix / gridX ); uvs.push( 1 - ( iy / gridY ) ); } } // indices for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { var a = ix + gridX1 * iy; var b = ix + gridX1 * ( iy + 1 ); var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = ( ix + 1 ) + gridX1 * iy; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ var materialId = 0; function Material() { Object.defineProperty( this, "id", { value: materialId ++ } ); this.uuid = _Math.generateUUID(); this.name = ""; this.type = "Material"; this.fog = true; this.lights = true; this.blending = NormalBlending; this.side = FrontSide; this.flatShading = false; this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer"s default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaTest = 0; this.premultipliedAlpha = false; this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer this.visible = true; this.userData = {}; this.needsUpdate = true; } Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: Material, isMaterial: true, onBeforeCompile: function () {}, setValues: function ( values ) { if ( values === undefined ) return; for ( var key in values ) { var newValue = values[ key ]; if ( newValue === undefined ) { console.warn( "THREE.Material: "" + key + "" parameter is undefined." ); continue; } // for backward compatability if shading is set in the constructor if ( key === "shading" ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( newValue === FlatShading ) ? true : false; continue; } var currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( "THREE." + this.type + ": "" + key + "" is not a property of this material." ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { currentValue.copy( newValue ); } else if ( key === "overdraw" ) { // ensure overdraw is backwards-compatible with legacy boolean type this[ key ] = Number( newValue ); } else { this[ key ] = newValue; } } }, toJSON: function ( meta ) { var isRoot = ( meta === undefined || typeof meta === "string" ); if ( isRoot ) { meta = { textures: {}, images: {} }; } var data = { metadata: { version: 4.5, type: "Material", generator: "Material.toJSON" } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== "" ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; if ( this.aoMap && this.aoMap.isTexture ) { data.aoMap = this.aoMap.toJSON( meta ).uuid; data.aoMapIntensity = this.aoMapIntensity; } if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; data.reflectivity = this.reflectivity; // Scale behind envMap } if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.size !== undefined ) data.size = this.size; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.flatShading === true ) data.flatShading = this.flatShading; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; // rotation (SpriteMaterial) if ( this.rotation !== 0 ) data.rotation = this.rotation; if ( this.linewidth !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; if ( this.dithering === true ) data.dithering = true; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== "round" ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== "round" ) data.wireframeLinejoin = this.wireframeLinejoin; if ( this.morphTargets === true ) data.morphTargets = true; if ( this.skinning === true ) data.skinning = true; if ( this.visible === false ) data.visible = false; if ( JSON.stringify( this.userData ) !== "{}" ) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRoot ) { var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.fog = source.fog; this.lights = source.lights; this.blending = source.blending; this.side = source.side; this.flatShading = source.flatShading; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.premultipliedAlpha = source.premultipliedAlpha; this.overdraw = source.overdraw; this.visible = source.visible; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.clipShadows = source.clipShadows; this.clipIntersection = source.clipIntersection; var srcPlanes = source.clippingPlanes, dstPlanes = null; if ( srcPlanes !== null ) { var n = srcPlanes.length; dstPlanes = new Array( n ); for ( var i = 0; i !== n; ++ i ) dstPlanes[ i ] = srcPlanes[ i ].clone(); } this.clippingPlanes = dstPlanes; this.shadowSide = source.shadowSide; return this; }, dispose: function () { this.dispatchEvent( { type: "dispose" } ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: * } */ function MeshBasicMaterial( parameters ) { Material.call( this ); this.type = "MeshBasicMaterial"; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } MeshBasicMaterial.prototype = Object.create( Material.prototype ); MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; MeshBasicMaterial.prototype.isMeshBasicMaterial = true; MeshBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: , * * skinning: , * morphTargets: , * morphNormals: * } */ function ShaderMaterial( parameters ) { Material.call( this ); this.type = "ShaderMaterial"; this.defines = {}; this.uniforms = {}; this.vertexShader = "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }"; this.fragmentShader = "void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }"; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.skinning = false; // set to use skinning attribute streams this.morphTargets = false; // set to use morph targets this.morphNormals = false; // set to use morph normals this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn"t include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { "color": [ 1, 1, 1 ], "uv": [ 0, 0 ], "uv2": [ 0, 0 ] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; if ( parameters !== undefined ) { if ( parameters.attributes !== undefined ) { console.error( "THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead." ); } this.setValues( parameters ); } } ShaderMaterial.prototype = Object.create( Material.prototype ); ShaderMaterial.prototype.constructor = ShaderMaterial; ShaderMaterial.prototype.isShaderMaterial = true; ShaderMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = UniformsUtils.clone( source.uniforms ); this.defines = Object.assign( {}, source.defines ); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; this.extensions = source.extensions; return this; }; ShaderMaterial.prototype.toJSON = function ( meta ) { var data = Material.prototype.toJSON.call( this, meta ); data.uniforms = this.uniforms; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; return data; }; /** * @author bhouston / http://clara.io */ function Ray( origin, direction ) { this.origin = ( origin !== undefined ) ? origin : new Vector3(); this.direction = ( direction !== undefined ) ? direction : new Vector3(); } Object.assign( Ray.prototype, { set: function ( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; }, at: function ( t, target ) { if ( target === undefined ) { console.warn( "THREE.Ray: .at() target is now required" ); target = new Vector3(); } return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); }, lookAt: function ( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; }, recast: function () { var v1 = new Vector3(); return function recast( t ) { this.origin.copy( this.at( t, v1 ) ); return this; }; }(), closestPointToPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Ray: .closestPointToPoint() target is now required" ); target = new Vector3(); } target.subVectors( point, this.origin ); var directionDistance = target.dot( this.direction ); if ( directionDistance < 0 ) { return target.copy( this.origin ); } return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); }, distanceToPoint: function ( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); }, distanceSqToPoint: function () { var v1 = new Vector3(); return function distanceSqToPoint( point ) { var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return v1.distanceToSquared( point ); }; }(), distanceSqToSegment: function () { var segCenter = new Vector3(); var segDir = new Vector3(); var diff = new Vector3(); return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); segDir.copy( v1 ).sub( v0 ).normalize(); diff.copy( this.origin ).sub( segCenter ); var segExtent = v0.distanceTo( v1 ) * 0.5; var a01 = - this.direction.dot( segDir ); var b0 = diff.dot( this.direction ); var b1 = - diff.dot( segDir ); var c = diff.lengthSq(); var det = Math.abs( 1 - a01 * a01 ); var s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. var invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); } return sqrDist; }; }(), intersectSphere: function () { var v1 = new Vector3(); return function intersectSphere( sphere, target ) { v1.subVectors( sphere.center, this.origin ); var tca = v1.dot( this.direction ); var d2 = v1.dot( v1 ) - tca * tca; var radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; var thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere var t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere var t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if ( t0 < 0 && t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, target ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, target ); }; }(), intersectsSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) <= sphere.radius; }, distanceToPlane: function ( plane ) { var denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; }, intersectPlane: function ( plane, target ) { var t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, target ); }, intersectsPlane: function ( plane ) { // check if the ray lies on the plane first var distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } var denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; }, intersectBox: function ( box, target ) { var tmin, tmax, tymin, tymax, tzmin, tzmax; var invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; var origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if ( tymin > tmin || tmin !== tmin ) tmin = tymin; if ( tymax < tmax || tmax !== tmax ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, target ); }, intersectsBox: ( function () { var v = new Vector3(); return function intersectsBox( box ) { return this.intersectBox( box, v ) !== null; }; } )(), intersectTriangle: function () { // Compute the offset origin, edges, and normal. var diff = new Vector3(); var edge1 = new Vector3(); var edge2 = new Vector3(); var normal = new Vector3(); return function intersectTriangle( a, b, c, backfaceCulling, target ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h edge1.subVectors( b, a ); edge2.subVectors( c, a ); normal.crossVectors( edge1, edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) var DdN = this.direction.dot( normal ); var sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } diff.subVectors( this.origin, a ); var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. var QdN = - sign * diff.dot( normal ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, target ); }; }(), applyMatrix4: function ( matrix4 ) { this.origin.applyMatrix4( matrix4 ); this.direction.transformDirection( matrix4 ); return this; }, equals: function ( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } } ); /** * @author bhouston / http://clara.io */ function Line3( start, end ) { this.start = ( start !== undefined ) ? start : new Vector3(); this.end = ( end !== undefined ) ? end : new Vector3(); } Object.assign( Line3.prototype, { set: function ( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .getCenter() target is now required" ); target = new Vector3(); } return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); }, delta: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .delta() target is now required" ); target = new Vector3(); } return target.subVectors( this.end, this.start ); }, distanceSq: function () { return this.start.distanceToSquared( this.end ); }, distance: function () { return this.start.distanceTo( this.end ); }, at: function ( t, target ) { if ( target === undefined ) { console.warn( "THREE.Line3: .at() target is now required" ); target = new Vector3(); } return this.delta( target ).multiplyScalar( t ).add( this.start ); }, closestPointToPointParameter: function () { var startP = new Vector3(); var startEnd = new Vector3(); return function closestPointToPointParameter( point, clampToLine ) { startP.subVectors( point, this.start ); startEnd.subVectors( this.end, this.start ); var startEnd2 = startEnd.dot( startEnd ); var startEnd_startP = startEnd.dot( startP ); var t = startEnd_startP / startEnd2; if ( clampToLine ) { t = _Math.clamp( t, 0, 1 ); } return t; }; }(), closestPointToPoint: function ( point, clampToLine, target ) { var t = this.closestPointToPointParameter( point, clampToLine ); if ( target === undefined ) { console.warn( "THREE.Line3: .closestPointToPoint() target is now required" ); target = new Vector3(); } return this.delta( target ).multiplyScalar( t ).add( this.start ); }, applyMatrix4: function ( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; }, equals: function ( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } } ); /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Triangle( a, b, c ) { this.a = ( a !== undefined ) ? a : new Vector3(); this.b = ( b !== undefined ) ? b : new Vector3(); this.c = ( c !== undefined ) ? c : new Vector3(); } Object.assign( Triangle, { getNormal: function () { var v0 = new Vector3(); return function getNormal( a, b, c, target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getNormal() target is now required" ); target = new Vector3(); } target.subVectors( c, b ); v0.subVectors( a, b ); target.cross( v0 ); var targetLengthSq = target.lengthSq(); if ( targetLengthSq > 0 ) { return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); } return target.set( 0, 0, 0 ); }; }(), // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html getBarycoord: function () { var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); return function getBarycoord( point, a, b, c, target ) { v0.subVectors( c, a ); v1.subVectors( b, a ); v2.subVectors( point, a ); var dot00 = v0.dot( v0 ); var dot01 = v0.dot( v1 ); var dot02 = v0.dot( v2 ); var dot11 = v1.dot( v1 ); var dot12 = v1.dot( v2 ); var denom = ( dot00 * dot11 - dot01 * dot01 ); if ( target === undefined ) { console.warn( "THREE.Triangle: .getBarycoord() target is now required" ); target = new Vector3(); } // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set( - 2, - 1, - 1 ); } var invDenom = 1 / denom; var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return target.set( 1 - u - v, v, u ); }; }(), containsPoint: function () { var v1 = new Vector3(); return function containsPoint( point, a, b, c ) { Triangle.getBarycoord( point, a, b, c, v1 ); return ( v1.x >= 0 ) && ( v1.y >= 0 ) && ( ( v1.x + v1.y ) <= 1 ); }; }() } ); Object.assign( Triangle.prototype, { set: function ( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; }, setFromPointsAndIndices: function ( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; }, getArea: function () { var v0 = new Vector3(); var v1 = new Vector3(); return function getArea() { v0.subVectors( this.c, this.b ); v1.subVectors( this.a, this.b ); return v0.cross( v1 ).length() * 0.5; }; }(), getMidpoint: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getMidpoint() target is now required" ); target = new Vector3(); } return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); }, getNormal: function ( target ) { return Triangle.getNormal( this.a, this.b, this.c, target ); }, getPlane: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .getPlane() target is now required" ); target = new Vector3(); } return target.setFromCoplanarPoints( this.a, this.b, this.c ); }, getBarycoord: function ( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); }, containsPoint: function ( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); }, intersectsBox: function ( box ) { return box.intersectsTriangle( this ); }, closestPointToPoint: function () { var plane = new Plane(); var edgeList = [ new Line3(), new Line3(), new Line3() ]; var projectedPoint = new Vector3(); var closestPoint = new Vector3(); return function closestPointToPoint( point, target ) { if ( target === undefined ) { console.warn( "THREE.Triangle: .closestPointToPoint() target is now required" ); target = new Vector3(); } var minDistance = Infinity; // project the point onto the plane of the triangle plane.setFromCoplanarPoints( this.a, this.b, this.c ); plane.projectPoint( point, projectedPoint ); // check if the projection lies within the triangle if ( this.containsPoint( projectedPoint ) === true ) { // if so, this is the closest point target.copy( projectedPoint ); } else { // if not, the point falls outside the triangle. the target is the closest point to the triangle"s edges or vertices edgeList[ 0 ].set( this.a, this.b ); edgeList[ 1 ].set( this.b, this.c ); edgeList[ 2 ].set( this.c, this.a ); for ( var i = 0; i < edgeList.length; i ++ ) { edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); var distance = projectedPoint.distanceToSquared( closestPoint ); if ( distance < minDistance ) { minDistance = distance; target.copy( closestPoint ); } } } return target; }; }(), equals: function ( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ */ function Mesh( geometry, material ) { Object3D.call( this ); this.type = "Mesh"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); this.drawMode = TrianglesDrawMode; this.updateMorphTargets(); } Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Mesh, isMesh: true, setDrawMode: function ( value ) { this.drawMode = value; }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.drawMode = source.drawMode; if ( source.morphTargetInfluences !== undefined ) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if ( source.morphTargetDictionary !== undefined ) { this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); } return this; }, updateMorphTargets: function () { var geometry = this.geometry; var m, ml, name; if ( geometry.isBufferGeometry ) { var morphAttributes = geometry.morphAttributes; var keys = Object.keys( morphAttributes ); if ( keys.length > 0 ) { var morphAttribute = morphAttributes[ keys[ 0 ] ]; if ( morphAttribute !== undefined ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { name = morphAttribute[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } } else { var morphTargets = geometry.morphTargets; if ( morphTargets !== undefined && morphTargets.length > 0 ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) { name = morphTargets[ m ].name || String( m ); this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ name ] = m; } } } }, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); var vA = new Vector3(); var vB = new Vector3(); var vC = new Vector3(); var tempA = new Vector3(); var tempB = new Vector3(); var tempC = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); var barycoord = new Vector3(); var intersectionPoint = new Vector3(); var intersectionPointWorld = new Vector3(); function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { Triangle.getBarycoord( point, p1, p2, p3, barycoord ); uv1.multiplyScalar( barycoord.x ); uv2.multiplyScalar( barycoord.y ); uv3.multiplyScalar( barycoord.z ); uv1.add( uv2 ).add( uv3 ); return uv1.clone(); } function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { var intersect; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); } if ( intersect === null ) return null; intersectionPointWorld.copy( point ); intersectionPointWorld.applyMatrix4( object.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) { vA.fromBufferAttribute( position, a ); vB.fromBufferAttribute( position, b ); vC.fromBufferAttribute( position, c ); var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint ); if ( intersection ) { if ( uv ) { uvA.fromBufferAttribute( uv, a ); uvB.fromBufferAttribute( uv, b ); uvC.fromBufferAttribute( uv, c ); intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); } var face = new Face3( a, b, c ); Triangle.getNormal( vA, vB, vC, face.normal ); intersection.face = face; } return intersection; } return function raycast( raycaster, intersects ) { var geometry = this.geometry; var material = this.material; var matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; } var intersection; if ( geometry.isBufferGeometry ) { var a, b, c; var index = geometry.index; var position = geometry.attributes.position; var uv = geometry.attributes.uv; var i, l; if ( index !== null ) { // indexed buffer geometry for ( i = 0, l = index.count; i < l; i += 3 ) { a = index.getX( i ); b = index.getX( i + 1 ); c = index.getX( i + 2 ); intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics intersects.push( intersection ); } } } else if ( position !== undefined ) { // non-indexed buffer geometry for ( i = 0, l = position.count; i < l; i += 3 ) { a = i; b = i + 1; c = i + 2; intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics intersects.push( intersection ); } } } } else if ( geometry.isGeometry ) { var fvA, fvB, fvC; var isMultiMaterial = Array.isArray( material ); var vertices = geometry.vertices; var faces = geometry.faces; var uvs; var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; for ( var f = 0, fl = faces.length; f < fl; f ++ ) { var face = faces[ f ]; var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; if ( faceMaterial === undefined ) continue; fvA = vertices[ face.a ]; fvB = vertices[ face.b ]; fvC = vertices[ face.c ]; if ( faceMaterial.morphTargets === true ) { var morphTargets = geometry.morphTargets; var morphInfluences = this.morphTargetInfluences; vA.set( 0, 0, 0 ); vB.set( 0, 0, 0 ); vC.set( 0, 0, 0 ); for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { var influence = morphInfluences[ t ]; if ( influence === 0 ) continue; var targets = morphTargets[ t ].vertices; vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); } vA.add( fvA ); vB.add( fvB ); vC.add( fvC ); fvA = vA; fvB = vB; fvC = vC; } intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); if ( intersection ) { if ( uvs && uvs[ f ] ) { var uvs_f = uvs[ f ]; uvA.copy( uvs_f[ 0 ] ); uvB.copy( uvs_f[ 1 ] ); uvC.copy( uvs_f[ 2 ] ); intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); } intersection.face = face; intersection.faceIndex = f; intersects.push( intersection ); } } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) { var clearColor = new Color( 0x000000 ); var clearAlpha = 0; var planeCamera, planeMesh; var boxMesh; function render( renderList, scene, camera, forceClear ) { var background = scene.background; if ( background === null ) { setClear( clearColor, clearAlpha ); } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; } if ( renderer.autoClear || forceClear ) { renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); } if ( background && background.isCubeTexture ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), new ShaderMaterial( { uniforms: ShaderLib.cube.uniforms, vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, depthTest: true, depthWrite: false, fog: false } ) ); boxMesh.geometry.removeAttribute( "normal" ); boxMesh.geometry.removeAttribute( "uv" ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { this.matrixWorld.copyPosition( camera.matrixWorld ); }; objects.update( boxMesh ); } boxMesh.material.uniforms.tCube.value = background; renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null ); } else if ( background && background.isTexture ) { if ( planeCamera === undefined ) { planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) ); objects.update( planeMesh ); } planeMesh.material.map = background; // TODO Push this to renderList renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null ); } } function setClear( color, alpha ) { state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); } return { getClearColor: function () { return clearColor; }, setClearColor: function ( color, alpha ) { clearColor.set( color ); clearAlpha = alpha !== undefined ? alpha : 1; setClear( clearColor, clearAlpha ); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function ( alpha ) { clearAlpha = alpha; setClear( clearColor, clearAlpha ); }, render: render }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBufferRenderer( gl, extensions, info ) { var mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); info.update( count, mode ); } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount ); info.update( count, mode, geometry.maxInstancedCount ); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLCapabilities( gl, extensions, parameters ) { var maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; var extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension !== null ) { maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === "highp" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return "highp"; } precision = "mediump"; } if ( precision === "mediump" ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return "mediump"; } } return "lowp"; } var precision = parameters.precision !== undefined ? parameters.precision : "highp"; var maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( "THREE.WebGLRenderer:", precision, "not supported, using", maxPrecision, "instead." ); precision = maxPrecision; } var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); var vertexTextures = maxVertexTextures > 0; var floatFragmentTextures = !! extensions.get( "OES_texture_float" ); var floatVertexTextures = vertexTextures && floatFragmentTextures; return { getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures }; } /** * @author tschw */ function WebGLClipping() { var scope = this, globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false, plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function ( planes, enableLocalClipping, camera ) { var enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes( planes, camera, 0 ); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function () { renderingShadows = false; resetGlobalState(); }; this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there"s no local clipping if ( renderingShadows ) { // there"s no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { var nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4, dstArray = cache.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); for ( var i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } cache.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { var nPlanes = planes !== null ? planes.length : 0, dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { var flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; return dstArray; } } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLExtensions( gl ) { var extensions = {}; return { get: function ( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } var extension; switch ( name ) { case "WEBGL_depth_texture": extension = gl.getExtension( "WEBGL_depth_texture" ) || gl.getExtension( "MOZ_WEBGL_depth_texture" ) || gl.getExtension( "WEBKIT_WEBGL_depth_texture" ); break; case "EXT_texture_filter_anisotropic": extension = gl.getExtension( "EXT_texture_filter_anisotropic" ) || gl.getExtension( "MOZ_EXT_texture_filter_anisotropic" ) || gl.getExtension( "WEBKIT_EXT_texture_filter_anisotropic" ); break; case "WEBGL_compressed_texture_s3tc": extension = gl.getExtension( "WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "MOZ_WEBGL_compressed_texture_s3tc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_s3tc" ); break; case "WEBGL_compressed_texture_pvrtc": extension = gl.getExtension( "WEBGL_compressed_texture_pvrtc" ) || gl.getExtension( "WEBKIT_WEBGL_compressed_texture_pvrtc" ); break; default: extension = gl.getExtension( name ); } if ( extension === null ) { console.warn( "THREE.WebGLRenderer: " + name + " extension not supported." ); } extensions[ name ] = extension; return extension; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLGeometries( gl, attributes, info ) { var geometries = {}; var wireframeAttributes = {}; function onGeometryDispose( event ) { var geometry = event.target; var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry.index !== null ) { attributes.remove( buffergeometry.index ); } for ( var name in buffergeometry.attributes ) { attributes.remove( buffergeometry.attributes[ name ] ); } geometry.removeEventListener( "dispose", onGeometryDispose ); delete geometries[ geometry.id ]; // TODO Remove duplicate code var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ geometry.id ]; } attribute = wireframeAttributes[ buffergeometry.id ]; if ( attribute ) { attributes.remove( attribute ); delete wireframeAttributes[ buffergeometry.id ]; } // info.memory.geometries --; } function get( object, geometry ) { var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry ) return buffergeometry; geometry.addEventListener( "dispose", onGeometryDispose ); if ( geometry.isBufferGeometry ) { buffergeometry = geometry; } else if ( geometry.isGeometry ) { if ( geometry._bufferGeometry === undefined ) { geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); } buffergeometry = geometry._bufferGeometry; } geometries[ geometry.id ] = buffergeometry; info.memory.geometries ++; return buffergeometry; } function update( geometry ) { var index = geometry.index; var geometryAttributes = geometry.attributes; if ( index !== null ) { attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); } for ( var name in geometryAttributes ) { attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); } // morph targets var morphAttributes = geometry.morphAttributes; for ( var name in morphAttributes ) { var array = morphAttributes[ name ]; for ( var i = 0, l = array.length; i < l; i ++ ) { attributes.update( array[ i ], gl.ARRAY_BUFFER ); } } } function getWireframeAttribute( geometry ) { var attribute = wireframeAttributes[ geometry.id ]; if ( attribute ) return attribute; var indices = []; var geometryIndex = geometry.index; var geometryAttributes = geometry.attributes; // console.time( "wireframe" ); if ( geometryIndex !== null ) { var array = geometryIndex.array; for ( var i = 0, l = array.length; i < l; i += 3 ) { var a = array[ i + 0 ]; var b = array[ i + 1 ]; var c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { var array = geometryAttributes.position.array; for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { var a = i + 0; var b = i + 1; var c = i + 2; indices.push( a, b, b, c, c, a ); } } // console.timeEnd( "wireframe" ); attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER ); wireframeAttributes[ geometry.id ] = attribute; return attribute; } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLIndexedBufferRenderer( gl, extensions, info ) { var mode; function setMode( value ) { mode = value; } var type, bytesPerElement; function setIndex( value ) { type = value.type; bytesPerElement = value.bytesPerElement; } function render( start, count ) { gl.drawElements( mode, count, type, start * bytesPerElement ); info.update( count, mode ); } function renderInstances( geometry, start, count ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); if ( extension === null ) { console.error( "THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); info.update( count, mode, geometry.maxInstancedCount ); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } /** * @author Mugen87 / https://github.com/Mugen87 */ function WebGLInfo( gl ) { var memory = { geometries: 0, textures: 0 }; var render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update( count, mode, instanceCount ) { instanceCount = instanceCount || 1; render.calls ++; switch ( mode ) { case gl.TRIANGLES: render.triangles += instanceCount * ( count / 3 ); break; case gl.TRIANGLE_STRIP: case gl.TRIANGLE_FAN: render.triangles += instanceCount * ( count - 2 ); break; case gl.LINES: render.lines += instanceCount * ( count / 2 ); break; case gl.LINE_STRIP: render.lines += instanceCount * ( count - 1 ); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error( "THREE.WebGLInfo: Unknown draw mode:", mode ); break; } } function reset() { render.frame ++; render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function absNumericalSort( a, b ) { return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } function WebGLMorphtargets( gl ) { var influencesList = {}; var morphInfluences = new Float32Array( 8 ); function update( object, geometry, material, program ) { var objectInfluences = object.morphTargetInfluences; var length = objectInfluences.length; var influences = influencesList[ geometry.id ]; if ( influences === undefined ) { // initialise list influences = []; for ( var i = 0; i < length; i ++ ) { influences[ i ] = [ i, 0 ]; } influencesList[ geometry.id ] = influences; } var morphTargets = material.morphTargets && geometry.morphAttributes.position; var morphNormals = material.morphNormals && geometry.morphAttributes.normal; // Remove current morphAttributes for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; if ( influence[ 1 ] !== 0 ) { if ( morphTargets ) geometry.removeAttribute( "morphTarget" + i ); if ( morphNormals ) geometry.removeAttribute( "morphNormal" + i ); } } // Collect influences for ( var i = 0; i < length; i ++ ) { var influence = influences[ i ]; influence[ 0 ] = i; influence[ 1 ] = objectInfluences[ i ]; } influences.sort( absNumericalSort ); // Add morphAttributes for ( var i = 0; i < 8; i ++ ) { var influence = influences[ i ]; if ( influence ) { var index = influence[ 0 ]; var value = influence[ 1 ]; if ( value ) { if ( morphTargets ) geometry.addAttribute( "morphTarget" + i, morphTargets[ index ] ); if ( morphNormals ) geometry.addAttribute( "morphNormal" + i, morphNormals[ index ] ); morphInfluences[ i ] = value; continue; } } morphInfluences[ i ] = 0; } program.getUniforms().setValue( gl, "morphTargetInfluences", morphInfluences ); } return { update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLObjects( geometries, info ) { var updateList = {}; function update( object ) { var frame = info.render.frame; var geometry = object.geometry; var buffergeometry = geometries.get( object, geometry ); // Update once per frame if ( updateList[ buffergeometry.id ] !== frame ) { if ( geometry.isGeometry ) { buffergeometry.updateFromObject( object ); } geometries.update( buffergeometry ); updateList[ buffergeometry.id ] = frame; } return buffergeometry; } function dispose() { updateList = {}; } return { update: update, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.flipY = false; } CubeTexture.prototype = Object.create( Texture.prototype ); CubeTexture.prototype.constructor = CubeTexture; CubeTexture.prototype.isCubeTexture = true; Object.defineProperty( CubeTexture.prototype, "images", { get: function () { return this.image; }, set: function ( value ) { this.image = value; } } ); /** * @author tschw * * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling "new WebGLUniforms( gl, program, renderer )". * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [renderer] ) * * uploads a uniform value(s) * the "renderer" parameter is needed for sampler uniforms * * * Static methods of the top-level container (renderer factorizations): * * .upload( gl, seq, values, renderer ) * * sets uniforms in "seq" to "values[id].value" * * .seqWithValue( seq, values ) : filteredSeq * * filters "seq" entries with corresponding entry in values * * * Methods of the top-level container (renderer factorizations): * * .setValue( gl, name, value ) * * sets uniform with name "name" to "value" * * .set( gl, obj, prop ) * * sets uniform from object and property with same name than uniform * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ var emptyTexture = new Texture(); var emptyCubeTexture = new CubeTexture(); // --- Base for inner nodes (including the root) --- function UniformContainer() { this.seq = []; this.map = {}; } // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) var arrayCacheF32 = []; var arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms var mat4array = new Float32Array( 16 ); var mat3array = new Float32Array( 9 ); var mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { var firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 var n = nBlocks * blockSize, r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } function arraysEqual( a, b ) { if ( a.length !== b.length ) return false; for ( var i = 0, l = a.length; i < l; i ++ ) { if ( a[ i ] !== b[ i ] ) return false; } return true; } function copyArray( a, b ) { for ( var i = 0, l = b.length; i < l; i ++ ) { a[ i ] = b[ i ]; } } // Texture unit allocation function allocTexUnits( renderer, n ) { var r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( var i = 0; i !== n; ++ i ) r[ i ] = renderer.allocTextureUnit(); return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValue1f( gl, v ) { var cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1f( this.addr, v ); cache[ 0 ] = v; } function setValue1i( gl, v ) { var cache = this.cache; if ( cache[ 0 ] === v ) return; gl.uniform1i( this.addr, v ); cache[ 0 ] = v; } // Single float vector (from flat array or THREE.VectorN) function setValue2fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { gl.uniform2f( this.addr, v.x, v.y ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform2fv( this.addr, v ); copyArray( cache, v ); } } function setValue3fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { gl.uniform3f( this.addr, v.x, v.y, v.z ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; } } else if ( v.r !== undefined ) { if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { gl.uniform3f( this.addr, v.r, v.g, v.b ); cache[ 0 ] = v.r; cache[ 1 ] = v.g; cache[ 2 ] = v.b; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform3fv( this.addr, v ); copyArray( cache, v ); } } function setValue4fv( gl, v ) { var cache = this.cache; if ( v.x !== undefined ) { if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); cache[ 0 ] = v.x; cache[ 1 ] = v.y; cache[ 2 ] = v.z; cache[ 3 ] = v.w; } } else { if ( arraysEqual( cache, v ) ) return; gl.uniform4fv( this.addr, v ); copyArray( cache, v ); } } // Single matrix (from flat array or MatrixN) function setValue2fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix2fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat2array.set( elements ); gl.uniformMatrix2fv( this.addr, false, mat2array ); copyArray( cache, elements ); } } function setValue3fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat3array.set( elements ); gl.uniformMatrix3fv( this.addr, false, mat3array ); copyArray( cache, elements ); } } function setValue4fm( gl, v ) { var cache = this.cache; var elements = v.elements; if ( elements === undefined ) { if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); copyArray( cache, v ); } else { if ( arraysEqual( cache, elements ) ) return; mat4array.set( elements ); gl.uniformMatrix4fv( this.addr, false, mat4array ); copyArray( cache, elements ); } } // Single texture (2D / Cube) function setValueT1( gl, v, renderer ) { var cache = this.cache; var unit = renderer.allocTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } renderer.setTexture2D( v || emptyTexture, unit ); } function setValueT6( gl, v, renderer ) { var cache = this.cache; var unit = renderer.allocTextureUnit(); if ( cache[ 0 ] !== unit ) { gl.uniform1i( this.addr, unit ); cache[ 0 ] = unit; } renderer.setTextureCube( v || emptyCubeTexture, unit ); } // Integer / Boolean vectors or arrays thereof (always flat arrays) function setValue2iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform2iv( this.addr, v ); copyArray( cache, v ); } function setValue3iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform3iv( this.addr, v ); copyArray( cache, v ); } function setValue4iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform4iv( this.addr, v ); copyArray( cache, v ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValue1f; // FLOAT case 0x8b50: return setValue2fv; // _VEC2 case 0x8b51: return setValue3fv; // _VEC3 case 0x8b52: return setValue4fv; // _VEC4 case 0x8b5a: return setValue2fm; // _MAT2 case 0x8b5b: return setValue3fm; // _MAT3 case 0x8b5c: return setValue4fm; // _MAT4 case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES case 0x8b60: return setValueT6; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // Array of scalars function setValue1fv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform1fv( this.addr, v ); copyArray( cache, v ); } function setValue1iv( gl, v ) { var cache = this.cache; if ( arraysEqual( cache, v ) ) return; gl.uniform1iv( this.addr, v ); copyArray( cache, v ); } // Array of vectors (flat or from THREE classes) function setValueV2a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 2 ); if ( arraysEqual( cache, data ) ) return; gl.uniform2fv( this.addr, data ); this.updateCache( data ); } function setValueV3a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 3 ); if ( arraysEqual( cache, data ) ) return; gl.uniform3fv( this.addr, data ); this.updateCache( data ); } function setValueV4a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 4 ); if ( arraysEqual( cache, data ) ) return; gl.uniform4fv( this.addr, data ); this.updateCache( data ); } // Array of matrices (flat or from THREE clases) function setValueM2a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 4 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix2fv( this.addr, false, data ); this.updateCache( data ); } function setValueM3a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 9 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix3fv( this.addr, false, data ); this.updateCache( data ); } function setValueM4a( gl, v ) { var cache = this.cache; var data = flatten( v, this.size, 16 ); if ( arraysEqual( cache, data ) ) return; gl.uniformMatrix4fv( this.addr, false, data ); this.updateCache( data ); } // Array of textures (2D / Cube) function setValueT1a( gl, v, renderer ) { var cache = this.cache; var n = v.length; var units = allocTexUnits( renderer, n ); if ( arraysEqual( cache, units ) === false ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( var i = 0; i !== n; ++ i ) { renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT6a( gl, v, renderer ) { var cache = this.cache; var n = v.length; var units = allocTexUnits( renderer, n ); if ( arraysEqual( cache, units ) === false ) { gl.uniform1iv( this.addr, units ); copyArray( cache, units ); } for ( var i = 0; i !== n; ++ i ) { renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValue1fv; // FLOAT case 0x8b50: return setValueV2a; // _VEC2 case 0x8b51: return setValueV3a; // _VEC3 case 0x8b52: return setValueV4a; // _VEC4 case 0x8b5a: return setValueM2a; // _MAT2 case 0x8b5b: return setValueM3a; // _MAT3 case 0x8b5c: return setValueM4a; // _MAT4 case 0x8b5e: return setValueT1a; // SAMPLER_2D case 0x8b60: return setValueT6a; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // --- Uniform Classes --- function SingleUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } PureArrayUniform.prototype.updateCache = function ( data ) { var cache = this.cache; if ( data instanceof Float32Array && cache.length !== data.length ) { this.cache = new Float32Array( data.length ); } copyArray( cache, data ); }; function StructuredUniform( id ) { this.id = id; UniformContainer.call( this ); // mix-in } StructuredUniform.prototype.setValue = function ( gl, value ) { // Note: Don"t need an extra "renderer" parameter, since samplers // are not allowed in structured uniforms. var seq = this.seq; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; u.setValue( gl, value[ u.id ] ); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings var RePathPart = /([wd_]+)(])?([|.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { var path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while ( true ) { var match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex, id = match[ 1 ], idIsIndex = match[ 2 ] === "]", subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === "[" && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn"t exist var map = container.map, next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container function WebGLUniforms( gl, program, renderer ) { UniformContainer.call( this ); this.renderer = renderer; var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( var i = 0; i < n; ++ i ) { var info = gl.getActiveUniform( program, i ), addr = gl.getUniformLocation( program, info.name ); parseUniform( info, addr, this ); } } WebGLUniforms.prototype.setValue = function ( gl, name, value ) { var u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, this.renderer ); }; WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { var v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); }; // Static interface WebGLUniforms.upload = function ( gl, seq, values, renderer ) { for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, renderer ); } } }; WebGLUniforms.seqWithValue = function ( seq, values ) { var r = []; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; }; /** * @author mrdoob / http://mrdoob.com/ */ function addLineNumbers( string ) { var lines = string.split( " " ); for ( var i = 0; i < lines.length; i ++ ) { lines[ i ] = ( i + 1 ) + ": " + lines[ i ]; } return lines.join( " " ); } function WebGLShader( gl, type, string ) { var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( "THREE.WebGLShader: Shader couldn"t compile." ); } if ( gl.getShaderInfoLog( shader ) !== "" ) { console.warn( "THREE.WebGLShader: gl.getShaderInfoLog()", type === gl.VERTEX_SHADER ? "vertex" : "fragment", gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); } // --enable-privileged-webgl-extension // console.log( type, gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( shader ) ); return shader; } /** * @author mrdoob / http://mrdoob.com/ */ var programIdCount = 0; function getEncodingComponents( encoding ) { switch ( encoding ) { case LinearEncoding: return [ "Linear", "( value )" ]; case sRGBEncoding: return [ "sRGB", "( value )" ]; case RGBEEncoding: return [ "RGBE", "( value )" ]; case RGBM7Encoding: return [ "RGBM", "( value, 7.0 )" ]; case RGBM16Encoding: return [ "RGBM", "( value, 16.0 )" ]; case RGBDEncoding: return [ "RGBD", "( value, 256.0 )" ]; case GammaEncoding: return [ "Gamma", "( value, float( GAMMA_FACTOR ) )" ]; default: throw new Error( "unsupported encoding: " + encoding ); } } function getTexelDecodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; } function getTexelEncodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { var toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case Uncharted2ToneMapping: toneMappingName = "Uncharted2"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; default: throw new Error( "unsupported toneMapping: " + toneMapping ); } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; var chunks = [ ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? "#extension GL_OES_standard_derivatives : enable" : "", ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( "EXT_frag_depth" ) ? "#extension GL_EXT_frag_depth : enable" : "", ( extensions.drawBuffers ) && rendererExtensions.get( "WEBGL_draw_buffers" ) ? "#extension GL_EXT_draw_buffers : require" : "", ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( "EXT_shader_texture_lod" ) ? "#extension GL_EXT_shader_texture_lod : enable" : "" ]; return chunks.filter( filterEmptyLine ).join( " " ); } function generateDefines( defines ) { var chunks = []; for ( var name in defines ) { var value = defines[ name ]; if ( value === false ) continue; chunks.push( "#define " + name + " " + value ); } return chunks.join( " " ); } function fetchAttributeLocations( gl, program ) { var attributes = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveAttrib( program, i ); var name = info.name; // console.log( "THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; } function filterEmptyLine( string ) { return string !== ""; } function replaceLightNums( string, parameters ) { return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); } function replaceClippingPlaneNums( string, parameters ) { return string .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } function parseIncludes( string ) { var pattern = /^[ ]*#include +<([wd.]+)>/gm; function replace( match, include ) { var replace = ShaderChunk[ include ]; if ( replace === undefined ) { throw new Error( "Can not resolve #include <" + include + ">" ); } return parseIncludes( replace ); } return string.replace( pattern, replace ); } function unrollLoops( string ) { var pattern = /#pragma unroll_loop[s]+?for ( int i = (d+); i < (d+); i ++ ) {([sS]+?)(?=})}/g; function replace( match, start, end, snippet ) { var unroll = ""; for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { unroll += snippet.replace( /[ i ]/g, "[ " + i + " ]" ); } return unroll; } return string.replace( pattern, replace ); } function WebGLProgram( renderer, extensions, code, material, shader, parameters ) { var gl = renderer.context; var defines = material.defines; var vertexShader = shader.vertexShader; var fragmentShader = shader.fragmentShader; var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; if ( parameters.shadowMapType === PCFShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; } var envMapTypeDefine = "ENVMAP_TYPE_CUBE"; var envMapModeDefine = "ENVMAP_MODE_REFLECTION"; var envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; if ( parameters.envMap ) { switch ( material.envMap.mapping ) { case CubeReflectionMapping: case CubeRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE"; break; case CubeUVReflectionMapping: case CubeUVRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_CUBE_UV"; break; case EquirectangularReflectionMapping: case EquirectangularRefractionMapping: envMapTypeDefine = "ENVMAP_TYPE_EQUIREC"; break; case SphericalReflectionMapping: envMapTypeDefine = "ENVMAP_TYPE_SPHERE"; break; } switch ( material.envMap.mapping ) { case CubeRefractionMapping: case EquirectangularRefractionMapping: envMapModeDefine = "ENVMAP_MODE_REFRACTION"; break; } switch ( material.combine ) { case MultiplyOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MULTIPLY"; break; case MixOperation: envMapBlendingDefine = "ENVMAP_BLENDING_MIX"; break; case AddOperation: envMapBlendingDefine = "ENVMAP_BLENDING_ADD"; break; } } var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; // console.log( "building new program " ); // var customExtensions = generateExtensions( material.extensions, parameters, extensions ); var customDefines = generateDefines( defines ); // var program = gl.createProgram(); var prefixVertex, prefixFragment; if ( material.isRawShaderMaterial ) { prefixVertex = [ customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixVertex.length > 0 ) { prefixVertex += " "; } prefixFragment = [ customExtensions, customDefines ].filter( filterEmptyLine ).join( " " ); if ( prefixFragment.length > 0 ) { prefixFragment += " "; } } else { prefixVertex = [ "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", "#define GAMMA_FACTOR " + gammaFactorDefine, "#define MAX_BONES " + parameters.maxBones, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.displacementMap && parameters.supportsVertexTextures ? "#define USE_DISPLACEMENTMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.morphNormals && parameters.flatShading === false ? "#define USE_MORPHNORMALS" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", "uniform mat4 modelMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "#ifdef USE_COLOR", " attribute vec3 color;", "#endif", "#ifdef USE_MORPHTARGETS", " attribute vec3 morphTarget0;", " attribute vec3 morphTarget1;", " attribute vec3 morphTarget2;", " attribute vec3 morphTarget3;", " #ifdef USE_MORPHNORMALS", " attribute vec3 morphNormal0;", " attribute vec3 morphNormal1;", " attribute vec3 morphNormal2;", " attribute vec3 morphNormal3;", " #else", " attribute vec3 morphTarget4;", " attribute vec3 morphTarget5;", " attribute vec3 morphTarget6;", " attribute vec3 morphTarget7;", " #endif", "#endif", "#ifdef USE_SKINNING", " attribute vec4 skinIndex;", " attribute vec4 skinWeight;", "#endif", " " ].filter( filterEmptyLine ).join( " " ); prefixFragment = [ customExtensions, "precision " + parameters.precision + " float;", "precision " + parameters.precision + " int;", "#define SHADER_NAME " + shader.name, customDefines, parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest + ( parameters.alphaTest % 1 ? "" : ".0" ) : "", // add ".0" if integer "#define GAMMA_FACTOR " + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.envMap ? "#define " + envMapTypeDefine : "", parameters.envMap ? "#define " + envMapModeDefine : "", parameters.envMap ? "#define " + envMapBlendingDefine : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.aoMap ? "#define USE_AOMAP" : "", parameters.emissiveMap ? "#define USE_EMISSIVEMAP" : "", parameters.bumpMap ? "#define USE_BUMPMAP" : "", parameters.normalMap ? "#define USE_NORMALMAP" : "", parameters.specularMap ? "#define USE_SPECULARMAP" : "", parameters.roughnessMap ? "#define USE_ROUGHNESSMAP" : "", parameters.metalnessMap ? "#define USE_METALNESSMAP" : "", parameters.alphaMap ? "#define USE_ALPHAMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.gradientMap ? "#define USE_GRADIENTMAP" : "", parameters.flatShading ? "#define FLAT_SHADED" : "", parameters.doubleSided ? "#define DOUBLE_SIDED" : "", parameters.flipSided ? "#define FLIP_SIDED" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : "", parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : "", parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", parameters.logarithmicDepthBuffer && extensions.get( "EXT_frag_depth" ) ? "#define USE_LOGDEPTHBUF_EXT" : "", parameters.envMap && extensions.get( "EXT_shader_texture_lod" ) ? "#define TEXTURE_LOD_EXT" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : "", ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ "tonemapping_pars_fragment" ] : "", // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : "", parameters.dithering ? "#define DITHERING" : "", ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ "encodings_pars_fragment" ] : "", // this code is required here because it is used by the various encoding/decoding function defined below parameters.mapEncoding ? getTexelDecodingFunction( "mapTexelToLinear", parameters.mapEncoding ) : "", parameters.envMapEncoding ? getTexelDecodingFunction( "envMapTexelToLinear", parameters.envMapEncoding ) : "", parameters.emissiveMapEncoding ? getTexelDecodingFunction( "emissiveMapTexelToLinear", parameters.emissiveMapEncoding ) : "", parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : "", parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : "", " " ].filter( filterEmptyLine ).join( " " ); } vertexShader = parseIncludes( vertexShader ); vertexShader = replaceLightNums( vertexShader, parameters ); vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); fragmentShader = parseIncludes( fragmentShader ); fragmentShader = replaceLightNums( fragmentShader, parameters ); fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; // console.log( "*VERTEX*", vertexGlsl ); // console.log( "*FRAGMENT*", fragmentGlsl ); var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( material.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, material.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, "position" ); } gl.linkProgram( program ); var programLog = gl.getProgramInfoLog( program ).trim(); var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); var runnable = true; var haveDiagnostics = true; // console.log( "**VERTEX**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glVertexShader ) ); // console.log( "**FRAGMENT**", gl.getExtension( "WEBGL_debug_shaders" ).getTranslatedShaderSource( glFragmentShader ) ); if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; console.error( "THREE.WebGLProgram: shader error: ", gl.getError(), "gl.VALIDATE_STATUS", gl.getProgramParameter( program, gl.VALIDATE_STATUS ), "gl.getProgramInfoLog", programLog, vertexLog, fragmentLog ); } else if ( programLog !== "" ) { console.warn( "THREE.WebGLProgram: gl.getProgramInfoLog()", programLog ); } else if ( vertexLog === "" || fragmentLog === "" ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, material: material, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations var cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program, renderer ); } return cachedUniforms; }; // set up caching for attribute locations var cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { gl.deleteProgram( program ); this.program = undefined; }; // DEPRECATED Object.defineProperties( this, { uniforms: { get: function () { console.warn( "THREE.WebGLProgram: .uniforms is now .getUniforms()." ); return this.getUniforms(); } }, attributes: { get: function () { console.warn( "THREE.WebGLProgram: .attributes is now .getAttributes()." ); return this.getAttributes(); } } } ); // this.name = shader.name; this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLPrograms( renderer, extensions, capabilities ) { var programs = []; var shaderIDs = { MeshDepthMaterial: "depth", MeshDistanceMaterial: "distanceRGBA", MeshNormalMaterial: "normal", MeshBasicMaterial: "basic", MeshLambertMaterial: "lambert", MeshPhongMaterial: "phong", MeshToonMaterial: "phong", MeshStandardMaterial: "physical", MeshPhysicalMaterial: "physical", LineBasicMaterial: "basic", LineDashedMaterial: "dashed", PointsMaterial: "points", ShadowMaterial: "shadow" }; var parameterNames = [ "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "shadowMapEnabled", "shadowMapType", "toneMapping", "physicallyCorrectLights", "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering" ]; function allocateBones( object ) { var skeleton = object.skeleton; var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE"s 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = capabilities.maxVertexUniforms; var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = Math.min( nVertexMatrices, bones.length ); if ( maxBones < bones.length ) { console.warn( "THREE.WebGLRenderer: Skeleton has " + bones.length + " bones. This GPU supports " + maxBones + "." ); return 0; } return maxBones; } } function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; if ( ! map ) { encoding = LinearEncoding; } else if ( map.isTexture ) { encoding = map.encoding; } else if ( map.isWebGLRenderTarget ) { console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don"t use render targets as textures. Use their .texture property instead." ); encoding = map.texture.encoding; } // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. if ( encoding === LinearEncoding && gammaOverrideLinear ) { encoding = GammaEncoding; } return encoding; } this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; var precision = capabilities.precision; if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( "THREE.WebGLProgram.getParameters:", material.precision, "not supported, using", precision, "instead." ); } } var currentRenderTarget = renderer.getRenderTarget(); var parameters = { shaderID: shaderID, precision: precision, supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, gradientMap: !! material.gradientMap, combine: material.combine, vertexColors: material.vertexColors, fog: !! fog, useFog: material.fog, fogExp: ( fog && fog.isFogExp2 ), flatShading: material.flatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, skinning: material.skinning && maxBones > 0, maxBones: maxBones, useVertexTexture: capabilities.floatVertexTextures, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: renderer.maxMorphTargets, maxMorphNormals: renderer.maxMorphNormals, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numClippingPlanes: nClipPlanes, numClipIntersection: nClipIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, alphaTest: material.alphaTest, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false }; return parameters; }; this.getProgramCode = function ( material, parameters ) { var array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( material.fragmentShader ); array.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { array.push( name ); array.push( material.defines[ name ] ); } } for ( var i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[ parameterNames[ i ] ] ); } array.push( material.onBeforeCompile.toString() ); array.push( renderer.gammaOutput ); return array.join(); }; this.acquireProgram = function ( material, shader, parameters, code ) { var program; // Check if code has been already compiled for ( var p = 0, pl = programs.length; p < pl; p ++ ) { var programInfo = programs[ p ]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, extensions, code, material, shader, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function ( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set var i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } }; // Exposed for resource monitoring & error feedback via renderer.info: this.programs = programs; } /** * @author fordacious / fordacious.github.io */ function WebGLProperties() { var properties = new WeakMap(); function get( object ) { var map = properties.get( object ); if ( map === undefined ) { map = {}; properties.set( object, map ); } return map; } function remove( object ) { properties.delete( object ); } function update( object, key, value ) { properties.get( object )[ key ] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.program && b.program && a.program !== b.program ) { return a.program.id - b.program.id; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { var renderItems = []; var renderItemsIndex = 0; var opaque = []; var transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transparent.length = 0; } function push( object, geometry, material, z, group ) { var renderItem = renderItems[ renderItemsIndex ]; if ( renderItem === undefined ) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, program: material.program, renderOrder: object.renderOrder, z: z, group: group }; renderItems[ renderItemsIndex ] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.program = material.program; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } ( material.transparent === true ? transparent : opaque ).push( renderItem ); renderItemsIndex ++; } function sort() { if ( opaque.length > 1 ) opaque.sort( painterSortStable ); if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable ); } return { opaque: opaque, transparent: transparent, init: init, push: push, sort: sort }; } function WebGLRenderLists() { var lists = {}; function get( scene, camera ) { var hash = scene.id + "," + camera.id; var list = lists[ hash ]; if ( list === undefined ) { // console.log( "THREE.WebGLRenderLists:", hash ); list = new WebGLRenderList(); lists[ hash ] = list; } return list; } function dispose() { lists = {}; } return { get: get, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ */ function UniformsCache() { var lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } var uniforms; switch ( light.type ) { case "DirectionalLight": uniforms = { direction: new Vector3(), color: new Color(), shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "SpotLight": uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case "PointLight": uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; case "HemisphereLight": uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case "RectAreaLight": uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() // TODO (abelnation): set RectAreaLight shadow uniforms }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } var count = 0; function WebGLLights() { var cache = new UniformsCache(); var state = { id: count ++, hash: "", ambient: [ 0, 0, 0 ], directional: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotShadowMap: [], spotShadowMatrix: [], rectArea: [], point: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [] }; var vector3 = new Vector3(); var matrix4 = new Matrix4(); var matrix42 = new Matrix4(); function setup( lights, shadows, camera ) { var r = 0, g = 0, b = 0; var directionalLength = 0; var pointLength = 0; var spotLength = 0; var rectAreaLength = 0; var hemiLength = 0; var viewMatrix = camera.matrixWorldInverse; for ( var i = 0, l = lights.length; i < l; i ++ ) { var light = lights[ i ]; var color = light.color; var intensity = light.intensity; var distance = light.distance; var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if ( light.isDirectionalLight ) { var uniforms = cache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.directionalShadowMap[ directionalLength ] = shadowMap; state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; state.directional[ directionalLength ] = uniforms; directionalLength ++; } else if ( light.isSpotLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.distance = distance; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; } state.spotShadowMap[ spotLength ] = shadowMap; state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; state.spot[ spotLength ] = uniforms; spotLength ++; } else if ( light.isRectAreaLight ) { var uniforms = cache.get( light ); // (a) intensity is the total visible light emitted //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); // (b) intensity is the brightness of the light uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy( light.matrixWorld ); matrix4.premultiply( viewMatrix ); matrix42.extractRotation( matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( matrix42 ); uniforms.halfHeight.applyMatrix4( matrix42 ); // TODO (abelnation): RectAreaLight distance? // uniforms.distance = distance; state.rectArea[ rectAreaLength ] = uniforms; rectAreaLength ++; } else if ( light.isPointLight ) { var uniforms = cache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.distance = light.distance; uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { var shadow = light.shadow; uniforms.shadowBias = shadow.bias; uniforms.shadowRadius = shadow.radius; uniforms.shadowMapSize = shadow.mapSize; uniforms.shadowCameraNear = shadow.camera.near; uniforms.shadowCameraFar = shadow.camera.far; } state.pointShadowMap[ pointLength ] = shadowMap; state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; state.point[ pointLength ] = uniforms; pointLength ++; } else if ( light.isHemisphereLight ) { var uniforms = cache.get( light ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); uniforms.direction.normalize(); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); state.hemi[ hemiLength ] = uniforms; hemiLength ++; } } state.ambient[ 0 ] = r; state.ambient[ 1 ] = g; state.ambient[ 2 ] = b; state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.hash = state.id + "," + directionalLength + "," + pointLength + "," + spotLength + "," + rectAreaLength + "," + hemiLength + "," + shadows.length; } return { setup: setup, state: state }; } /** * @author Mugen87 / https://github.com/Mugen87 */ function WebGLRenderState() { var lights = new WebGLLights(); var lightsArray = []; var shadowsArray = []; var spritesArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; spritesArray.length = 0; } function pushLight( light ) { lightsArray.push( light ); } function pushShadow( shadowLight ) { shadowsArray.push( shadowLight ); } function pushSprite( shadowLight ) { spritesArray.push( shadowLight ); } function setupLights( camera ) { lights.setup( lightsArray, shadowsArray, camera ); } var state = { lightsArray: lightsArray, shadowsArray: shadowsArray, spritesArray: spritesArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, pushLight: pushLight, pushShadow: pushShadow, pushSprite: pushSprite }; } function WebGLRenderStates() { var renderStates = {}; function get( scene, camera ) { var hash = scene.id + "," + camera.id; var renderState = renderStates[ hash ]; if ( renderState === undefined ) { renderState = new WebGLRenderState(); renderStates[ hash ] = renderState; } return renderState; } function dispose() { renderStates = {}; } return { get: get, dispose: dispose }; } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / https://clara.io * @author WestLangley / http://github.com/WestLangley * * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ function MeshDepthMaterial( parameters ) { Material.call( this ); this.type = "MeshDepthMaterial"; this.depthPacking = BasicDepthPacking; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDepthMaterial.prototype = Object.create( Material.prototype ); MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; MeshDepthMaterial.prototype.isMeshDepthMaterial = true; MeshDepthMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.depthPacking = source.depthPacking; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * * referencePosition: , * nearDistance: , * farDistance: , * * skinning: , * morphTargets: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: * * } */ function MeshDistanceMaterial( parameters ) { Material.call( this ); this.type = "MeshDistanceMaterial"; this.referencePosition = new Vector3(); this.nearDistance = 1; this.farDistance = 1000; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDistanceMaterial.prototype = Object.create( Material.prototype ); MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; MeshDistanceMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.referencePosition.copy( source.referencePosition ); this.nearDistance = source.nearDistance; this.farDistance = source.farDistance; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { var _frustum = new Frustum(), _projScreenMatrix = new Matrix4(), _shadowMapSize = new Vector2(), _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ), _lookTarget = new Vector3(), _lightPositionWorld = new Vector3(), _MorphingFlag = 1, _SkinningFlag = 2, _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, _depthMaterials = new Array( _NumberOfMaterialVariants ), _distanceMaterials = new Array( _NumberOfMaterialVariants ), _materialCache = {}; var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; var cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; var cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; var cube2DViewPorts = [ new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() ]; // init for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { var useMorphing = ( i & _MorphingFlag ) !== 0; var useSkinning = ( i & _SkinningFlag ) !== 0; var depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking, morphTargets: useMorphing, skinning: useSkinning } ); _depthMaterials[ i ] = depthMaterial; // var distanceMaterial = new MeshDistanceMaterial( { morphTargets: useMorphing, skinning: useSkinning } ); _distanceMaterials[ i ] = distanceMaterial; } // var scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.render = function ( lights, scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( lights.length === 0 ) return; // TODO Clean up (needed in case of contextlost) var _gl = _renderer.context; var _state = _renderer.state; // Set GL state for depth map. _state.disable( _gl.BLEND ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); // render depth map var faceCount; for ( var i = 0, il = lights.length; i < il; i ++ ) { var light = lights[ i ]; var shadow = light.shadow; var isPointLight = light && light.isPointLight; if ( shadow === undefined ) { console.warn( "THREE.WebGLShadowMap:", light, "has no shadow." ); continue; } var shadowCamera = shadow.camera; _shadowMapSize.copy( shadow.mapSize ); _shadowMapSize.min( _maxShadowMapSize ); if ( isPointLight ) { var vpWidth = _shadowMapSize.x; var vpHeight = _shadowMapSize.y; // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); // negative X cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); // positive Z cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); // negative Z cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); // positive Y cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); // negative Y cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); _shadowMapSize.x *= 4.0; _shadowMapSize.y *= 2.0; } if ( shadow.map === null ) { var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadow.map.texture.name = light.name + ".shadowMap"; shadowCamera.updateProjectionMatrix(); } if ( shadow.isSpotLightShadow ) { shadow.update( light ); } var shadowMap = shadow.map; var shadowMatrix = shadow.matrix; _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld ); if ( isPointLight ) { faceCount = 6; // for point lights we set the shadow matrix to be a translation-only matrix // equal to inverse of the light"s position shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); } else { faceCount = 1; _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); // compute shadow matrix shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( shadowCamera.projectionMatrix ); shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); } _renderer.setRenderTarget( shadowMap ); _renderer.clear(); // render shadow map for each cube face (if omni-directional) or // run a single pass if not for ( var face = 0; face < faceCount; face ++ ) { if ( isPointLight ) { _lookTarget.copy( shadowCamera.position ); _lookTarget.add( cubeDirections[ face ] ); shadowCamera.up.copy( cubeUps[ face ] ); shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); var vpDimensions = cube2DViewPorts[ face ]; _state.viewport( vpDimensions ); } // update camera matrices and frustum _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); // set object matrices & frustum culling renderObject( scene, camera, shadowCamera, isPointLight ); } } scope.needsUpdate = false; }; function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) { var geometry = object.geometry; var result = null; var materialVariants = _depthMaterials; var customMaterial = object.customDepthMaterial; if ( isPointLight ) { materialVariants = _distanceMaterials; customMaterial = object.customDistanceMaterial; } if ( ! customMaterial ) { var useMorphing = false; if ( material.morphTargets ) { if ( geometry && geometry.isBufferGeometry ) { useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; } else if ( geometry && geometry.isGeometry ) { useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; } } if ( object.isSkinnedMesh && material.skinning === false ) { console.warn( "THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:", object ); } var useSkinning = object.isSkinnedMesh && material.skinning; var variantIndex = 0; if ( useMorphing ) variantIndex |= _MorphingFlag; if ( useSkinning ) variantIndex |= _SkinningFlag; result = materialVariants[ variantIndex ]; } else { result = customMaterial; } if ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) { // in this case we need a unique material instance reflecting the // appropriate state var keyA = result.uuid, keyB = material.uuid; var materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } var cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( isPointLight && result.isMeshDistanceMaterial ) { result.referencePosition.copy( lightPositionWorld ); result.nearDistance = shadowCameraNear; result.farDistance = shadowCameraFar; } return result; } function renderObject( object, camera, shadowCamera, isPointLight ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); var geometry = _objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var k = 0, kl = groups.length; k < kl; k ++ ) { var group = groups[ k ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else if ( material.visible ) { var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { renderObject( children[ i ], camera, shadowCamera, isPointLight ); } } } /** * @author mrdoob / http://mrdoob.com/ */ function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.needsUpdate = true; } CanvasTexture.prototype = Object.create( Texture.prototype ); CanvasTexture.prototype.constructor = CanvasTexture; CanvasTexture.prototype.isCanvasTexture = true; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) { var vertexBuffer, elementBuffer; var program, attributes, uniforms; var texture; // decompose matrixWorld var spritePosition = new Vector3(); var spriteRotation = new Quaternion(); var spriteScale = new Vector3(); function init() { var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0.5, - 0.5, 1, 0, 0.5, 0.5, 1, 1, - 0.5, 0.5, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); program = createProgram(); attributes = { position: gl.getAttribLocation( program, "position" ), uv: gl.getAttribLocation( program, "uv" ) }; uniforms = { uvOffset: gl.getUniformLocation( program, "uvOffset" ), uvScale: gl.getUniformLocation( program, "uvScale" ), rotation: gl.getUniformLocation( program, "rotation" ), center: gl.getUniformLocation( program, "center" ), scale: gl.getUniformLocation( program, "scale" ), color: gl.getUniformLocation( program, "color" ), map: gl.getUniformLocation( program, "map" ), opacity: gl.getUniformLocation( program, "opacity" ), modelViewMatrix: gl.getUniformLocation( program, "modelViewMatrix" ), projectionMatrix: gl.getUniformLocation( program, "projectionMatrix" ), fogType: gl.getUniformLocation( program, "fogType" ), fogDensity: gl.getUniformLocation( program, "fogDensity" ), fogNear: gl.getUniformLocation( program, "fogNear" ), fogFar: gl.getUniformLocation( program, "fogFar" ), fogColor: gl.getUniformLocation( program, "fogColor" ), fogDepth: gl.getUniformLocation( program, "fogDepth" ), alphaTest: gl.getUniformLocation( program, "alphaTest" ) }; var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = 8; canvas.height = 8; var context = canvas.getContext( "2d" ); context.fillStyle = "white"; context.fillRect( 0, 0, 8, 8 ); texture = new CanvasTexture( canvas ); } this.render = function ( sprites, scene, camera ) { if ( sprites.length === 0 ) return; // setup gl if ( program === undefined ) { init(); } state.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.position ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); state.disable( gl.CULL_FACE ); state.enable( gl.BLEND ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); state.activeTexture( gl.TEXTURE0 ); gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; var sceneFogType = 0; var fog = scene.fog; if ( fog ) { gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); if ( fog.isFog ) { gl.uniform1f( uniforms.fogNear, fog.near ); gl.uniform1f( uniforms.fogFar, fog.far ); gl.uniform1i( uniforms.fogType, 1 ); oldFogType = 1; sceneFogType = 1; } else if ( fog.isFogExp2 ) { gl.uniform1f( uniforms.fogDensity, fog.density ); gl.uniform1i( uniforms.fogType, 2 ); oldFogType = 2; sceneFogType = 2; } } else { gl.uniform1i( uniforms.fogType, 0 ); oldFogType = 0; sceneFogType = 0; } // update positions and sort for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; } sprites.sort( painterSortStable ); // render all sprites var scale = []; var center = []; for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; var material = sprite.material; if ( material.visible === false ) continue; sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined ); gl.uniform1f( uniforms.alphaTest, material.alphaTest ); gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); scale[ 0 ] = spriteScale.x; scale[ 1 ] = spriteScale.y; center[ 0 ] = sprite.center.x - 0.5; center[ 1 ] = sprite.center.y - 0.5; var fogType = 0; if ( scene.fog && material.fog ) { fogType = sceneFogType; } if ( oldFogType !== fogType ) { gl.uniform1i( uniforms.fogType, fogType ); oldFogType = fogType; } if ( material.map !== null ) { gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { gl.uniform2f( uniforms.uvOffset, 0, 0 ); gl.uniform2f( uniforms.uvScale, 1, 1 ); } gl.uniform1f( uniforms.opacity, material.opacity ); gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); gl.uniform1f( uniforms.rotation, material.rotation ); gl.uniform2fv( uniforms.center, center ); gl.uniform2fv( uniforms.scale, scale ); state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); state.buffers.depth.setTest( material.depthTest ); state.buffers.depth.setMask( material.depthWrite ); state.buffers.color.setMask( material.colorWrite ); textures.setTexture2D( material.map || texture, 0 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined ); } // restore gl state.enable( gl.CULL_FACE ); state.reset(); }; function createProgram() { var program = gl.createProgram(); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); gl.shaderSource( vertexShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform float rotation;", "uniform vec2 center;", "uniform vec2 scale;", "uniform vec2 uvOffset;", "uniform vec2 uvScale;", "attribute vec2 position;", "attribute vec2 uv;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vUV = uvOffset + uv * uvScale;", " vec2 alignedPosition = ( position - center ) * scale;", " vec2 rotatedPosition;", " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", " vec4 mvPosition;", " mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", " mvPosition.xy += rotatedPosition;", " gl_Position = projectionMatrix * mvPosition;", " fogDepth = - mvPosition.z;", "}" ].join( " " ) ); gl.shaderSource( fragmentShader, [ "precision " + capabilities.precision + " float;", "#define SHADER_NAME " + "SpriteMaterial", "uniform vec3 color;", "uniform sampler2D map;", "uniform float opacity;", "uniform int fogType;", "uniform vec3 fogColor;", "uniform float fogDensity;", "uniform float fogNear;", "uniform float fogFar;", "uniform float alphaTest;", "varying vec2 vUV;", "varying float fogDepth;", "void main() {", " vec4 texture = texture2D( map, vUV );", " gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", " if ( gl_FragColor.a < alphaTest ) discard;", " if ( fogType > 0 ) {", " float fogFactor = 0.0;", " if ( fogType == 1 ) {", " fogFactor = smoothstep( fogNear, fogFar, fogDepth );", " } else {", " const float LOG2 = 1.442695;", " fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );", " fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", " }", " gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );", " }", "}" ].join( " " ) ); gl.compileShader( vertexShader ); gl.compileShader( fragmentShader ); gl.attachShader( program, vertexShader ); gl.attachShader( program, fragmentShader ); gl.linkProgram( program ); return program; } function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return b.id - a.id; } } } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLState( gl, extensions, utils ) { function ColorBuffer() { var locked = false; var color = new Vector4(); var currentColorMask = null; var currentColorClear = new Vector4( 0, 0, 0, 0 ); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } }; } function DepthBuffer() { var locked = false; var currentDepthMask = null; var currentDepthFunc = null; var currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { if ( depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } } else { gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { var locked = false; var currentStencilMask = null; var currentStencilFunc = null; var currentStencilRef = null; var currentStencilFuncMask = null; var currentStencilFail = null; var currentStencilZFail = null; var currentStencilZPass = null; var currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // var colorBuffer = new ColorBuffer(); var depthBuffer = new DepthBuffer(); var stencilBuffer = new StencilBuffer(); var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var newAttributes = new Uint8Array( maxVertexAttributes ); var enabledAttributes = new Uint8Array( maxVertexAttributes ); var attributeDivisors = new Uint8Array( maxVertexAttributes ); var capabilities = {}; var compressedTextureFormats = null; var currentProgram = null; var currentBlending = null; var currentBlendEquation = null; var currentBlendSrc = null; var currentBlendDst = null; var currentBlendEquationAlpha = null; var currentBlendSrcAlpha = null; var currentBlendDstAlpha = null; var currentPremultipledAlpha = false; var currentFlipSided = null; var currentCullFace = null; var currentLineWidth = null; var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); var lineWidthAvailable = false; var version = 0; var glVersion = gl.getParameter( gl.VERSION ); if ( glVersion.indexOf( "WebGL" ) !== - 1 ) { version = parseFloat( /^WebGL ([0-9])/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 1.0 ); } else if ( glVersion.indexOf( "OpenGL ES" ) !== - 1 ) { version = parseFloat( /^OpenGL ES ([0-9])/.exec( glVersion )[ 1 ] ); lineWidthAvailable = ( version >= 2.0 ); } var currentTextureSlot = null; var currentBoundTextures = {}; var currentScissor = new Vector4(); var currentViewport = new Vector4(); function createTexture( type, target, count ) { var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. var texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( var i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } return texture; } var emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); // init colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); enable( gl.BLEND ); setBlending( NormalBlending ); // function initAttributes() { for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { enableAttributeAndDivisor( attribute, 0 ); } function enableAttributeAndDivisor( attribute, meshPerAttribute ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { var extension = extensions.get( "ANGLE_instanced_arrays" ); extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function enable( id ) { if ( capabilities[ id ] !== true ) { gl.enable( id ); capabilities[ id ] = true; } } function disable( id ) { if ( capabilities[ id ] !== false ) { gl.disable( id ); capabilities[ id ] = false; } } function getCompressedTextureFormats() { if ( compressedTextureFormats === null ) { compressedTextureFormats = []; if ( extensions.get( "WEBGL_compressed_texture_pvrtc" ) || extensions.get( "WEBGL_compressed_texture_s3tc" ) || extensions.get( "WEBGL_compressed_texture_etc1" ) || extensions.get( "WEBGL_compressed_texture_astc" ) ) { var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); for ( var i = 0; i < formats.length; i ++ ) { compressedTextureFormats.push( formats[ i ] ); } } } return compressedTextureFormats; } function useProgram( program ) { if ( currentProgram !== program ) { gl.useProgram( program ); currentProgram = program; return true; } return false; } function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending !== NoBlending ) { enable( gl.BLEND ); } else { disable( gl.BLEND ); } if ( blending !== CustomBlending ) { if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { switch ( blending ) { case AdditiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); } break; case SubtractiveBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); } break; case MultiplyBlending: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); } break; default: if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } } } currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; } else { blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } } currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE ); var flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); material.transparent === true ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) : setBlending( NoBlending ); depthBuffer.setFunc( material.depthFunc ); depthBuffer.setTest( material.depthTest ); depthBuffer.setMask( material.depthWrite ); colorBuffer.setMask( material.colorWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function setScissorTest( scissorTest ) { if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { if ( currentTextureSlot === null ) { activeTexture(); } var boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ currentTextureSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( "THREE.WebGLState:", error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } // function reset() { for ( var i = 0; i < enabledAttributes.length; i ++ ) { if ( enabledAttributes[ i ] === 1 ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } capabilities = {}; compressedTextureFormats = null; currentTextureSlot = null; currentBoundTextures = {}; currentProgram = null; currentBlending = null; currentFlipSided = null; currentCullFace = null; colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, initAttributes: initAttributes, enableAttribute: enableAttribute, enableAttributeAndDivisor: enableAttributeAndDivisor, disableUnusedAttributes: disableUnusedAttributes, enable: enable, disable: disable, getCompressedTextureFormats: getCompressedTextureFormats, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, scissor: scissor, viewport: viewport, reset: reset }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { var _isWebGL2 = ( typeof WebGL2RenderingContext !== "undefined" && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */ var _videoTextures = {}; var _canvas; // function clampToMaxSize( image, maxSize ) { if ( image.width > maxSize || image.height > maxSize ) { if ( "data" in image ) { console.warn( "THREE.WebGLRenderer: image in DataTexture is too big (" + image.width + "x" + image.height + ")." ); return; } // Warning: Scaling through the canvas will only work with images that use // premultiplied alpha. var scale = maxSize / Math.max( image.width, image.height ); var canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); canvas.width = Math.floor( image.width * scale ); canvas.height = Math.floor( image.height * scale ); var context = canvas.getContext( "2d" ); context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); console.warn( "THREE.WebGLRenderer: image is too big (" + image.width + "x" + image.height + "). Resized to " + canvas.width + "x" + canvas.height, image ); return canvas; } return image; } function isPowerOfTwo( image ) { return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); } function makePowerOfTwo( image ) { if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) { if ( _canvas === undefined ) _canvas = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); _canvas.width = _Math.floorPowerOfTwo( image.width ); _canvas.height = _Math.floorPowerOfTwo( image.height ); var context = _canvas.getContext( "2d" ); context.drawImage( image, 0, 0, _canvas.width, _canvas.height ); console.warn( "THREE.WebGLRenderer: image is not power of two (" + image.width + "x" + image.height + "). Resized to " + _canvas.width + "x" + _canvas.height, image ); return _canvas; } return image; } function textureNeedsPowerOfTwo( texture ) { return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) { return texture.generateMipmaps && isPowerOfTwo && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap( target, texture, width, height ) { _gl.generateMipmap( target ); var textureProperties = properties.get( texture ); // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { var texture = event.target; texture.removeEventListener( "dispose", onTextureDispose ); deallocateTexture( texture ); if ( texture.isVideoTexture ) { delete _videoTextures[ texture.id ]; } info.memory.textures --; } function onRenderTargetDispose( event ) { var renderTarget = event.target; renderTarget.removeEventListener( "dispose", onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); info.memory.textures --; } // function deallocateTexture( texture ) { var textureProperties = properties.get( texture ); if ( texture.image && textureProperties.__image__webglTextureCube ) { // cube texture _gl.deleteTexture( textureProperties.__image__webglTextureCube ); } else { // 2D texture if ( textureProperties.__webglInit === undefined ) return; _gl.deleteTexture( textureProperties.__webglTexture ); } // remove all webgl properties properties.remove( texture ); } function deallocateRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); if ( ! renderTarget ) return; if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } properties.remove( renderTarget.texture ); properties.remove( renderTarget ); } // function setTexture2D( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.isVideoTexture ) updateVideoTexture( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { var image = texture.image; if ( image === undefined ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is undefined", texture ); } else if ( image.complete === false ) { console.warn( "THREE.WebGLRenderer: Texture marked for update but image is incomplete", texture ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); } function setTextureCube( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.image.length === 6 ) { if ( texture.version > 0 && textureProperties.__version !== texture.version ) { if ( ! textureProperties.__image__webglTextureCube ) { texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__image__webglTextureCube = _gl.createTexture(); info.memory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = ( texture && texture.isCompressedTexture ); var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } } var image = cubeImage[ 0 ], isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed ) { if ( isDataTexture ) { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); } } else { var mipmap, mipmaps = cubeImage[ i ].mipmaps; for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } if ( ! isCompressed ) { textureProperties.__maxMipLevel = 0; } else { textureProperties.__maxMipLevel = mipmaps.length - 1; } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { // We assume images for cube map have the same size. generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } else { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); } } } function setTextureCubeDynamic( texture, slot ) { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); } function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { var extension; if ( isPowerOfTwoImage ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.", texture ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( "THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.", texture ); } } extension = extensions.get( "EXT_texture_filter_anisotropic" ); if ( extension ) { if ( texture.type === FloatType && extensions.get( "OES_texture_float_linear" ) === null ) return; if ( texture.type === HalfFloatType && extensions.get( "OES_texture_half_float_linear" ) === null ) return; if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function uploadTexture( textureProperties, texture, slot ) { if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( "dispose", onTextureDispose ); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { image = makePowerOfTwo( image ); } var isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = utils.convert( texture.format ), glType = utils.convert( texture.type ); setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); var mipmap, mipmaps = texture.mipmaps; if ( texture.isDepthTexture ) { // populate depth texture with dummy data var internalFormat = _gl.DEPTH_COMPONENT; if ( texture.type === FloatType ) { if ( ! _isWebGL2 ) throw new Error( "Float Depth Texture only supported in WebGL2.0" ); internalFormat = _gl.DEPTH_COMPONENT32F; } else if ( _isWebGL2 ) { // WebGL 2.0 requires signed internalformat for glTexImage2D internalFormat = _gl.DEPTH_COMPONENT16; } if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( "THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture." ); texture.type = UnsignedShortType; glType = utils.convert( texture.type ); } } // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.format === DepthStencilFormat ) { internalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( "THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture." ); texture.type = UnsignedInt248Type; glType = utils.convert( texture.type ); } } state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } texture.generateMipmaps = false; textureProperties.__maxMipLevel = mipmaps.length - 1; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); textureProperties.__maxMipLevel = 0; } } else if ( texture.isCompressedTexture ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } textureProperties.__maxMipLevel = mipmaps.length - 1; } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } texture.generateMipmaps = false; textureProperties.__maxMipLevel = mipmaps.length - 1; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); textureProperties.__maxMipLevel = 0; } } if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { var glFormat = utils.convert( renderTarget.texture.format ); var glType = utils.convert( renderTarget.texture.type ); state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { // FIXME: We don"t support !depth !stencil _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); if ( isCube ) throw new Error( "Depth Texture with cube render targets is not supported" ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error( "renderTarget.depthTexture must be an instance of THREE.DepthTexture" ); } // upload an empty depth texture with framebuffer size if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; if ( renderTarget.depthTexture.format === DepthFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else { throw new Error( "Unknown depthTexture format" ); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); if ( renderTarget.depthTexture ) { if ( isCube ) throw new Error( "target.depthTexture not supported in Cube render targets" ); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( var i = 0; i < 6; i ++ ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); } } else { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); } } _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); renderTarget.addEventListener( "dispose", onRenderTargetDispose ); textureProperties.__webglTexture = _gl.createTexture(); info.memory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( var i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); } if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height ); } state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height ); } state.bindTexture( _gl.TEXTURE_2D, null ); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { var texture = renderTarget.texture; var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) { var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; var webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); generateMipmap( target, texture, renderTarget.width, renderTarget.height ); state.bindTexture( target, null ); } } function updateVideoTexture( texture ) { var id = texture.id; var frame = info.render.frame; // Check the last frame we updated the VideoTexture if ( _videoTextures[ id ] !== frame ) { _videoTextures[ id ] = frame; texture.update(); } } this.setTexture2D = setTexture2D; this.setTextureCube = setTextureCube; this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; } /** * @author thespite / http://www.twitter.com/thespite */ function WebGLUtils( gl, extensions ) { function convert( p ) { var extension; if ( p === RepeatWrapping ) return gl.REPEAT; if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE; if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT; if ( p === NearestFilter ) return gl.NEAREST; if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST; if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR; if ( p === LinearFilter ) return gl.LINEAR; if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST; if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR; if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5; if ( p === ByteType ) return gl.BYTE; if ( p === ShortType ) return gl.SHORT; if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; if ( p === IntType ) return gl.INT; if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; if ( p === FloatType ) return gl.FLOAT; if ( p === HalfFloatType ) { extension = extensions.get( "OES_texture_half_float" ); if ( extension !== null ) return extension.HALF_FLOAT_OES; } if ( p === AlphaFormat ) return gl.ALPHA; if ( p === RGBFormat ) return gl.RGB; if ( p === RGBAFormat ) return gl.RGBA; if ( p === LuminanceFormat ) return gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; if ( p === AddEquation ) return gl.FUNC_ADD; if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT; if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT; if ( p === ZeroFactor ) return gl.ZERO; if ( p === OneFactor ) return gl.ONE; if ( p === SrcColorFactor ) return gl.SRC_COLOR; if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR; if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA; if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA; if ( p === DstAlphaFactor ) return gl.DST_ALPHA; if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA; if ( p === DstColorFactor ) return gl.DST_COLOR; if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR; if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE; if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { extension = extensions.get( "WEBGL_compressed_texture_s3tc" ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } } if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_pvrtc" ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } } if ( p === RGB_ETC1_Format ) { extension = extensions.get( "WEBGL_compressed_texture_etc1" ); if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; } if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { extension = extensions.get( "WEBGL_compressed_texture_astc" ); if ( extension !== null ) { return p; } } if ( p === MinEquation || p === MaxEquation ) { extension = extensions.get( "EXT_blend_minmax" ); if ( extension !== null ) { if ( p === MinEquation ) return extension.MIN_EXT; if ( p === MaxEquation ) return extension.MAX_EXT; } } if ( p === UnsignedInt248Type ) { extension = extensions.get( "WEBGL_depth_texture" ); if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; } return 0; } return { convert: convert }; } /** * @author mrdoob / http://mrdoob.com/ * @author greggman / http://games.greggman.com/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author tschw */ function PerspectiveCamera( fov, aspect, near, far ) { Camera.call( this ); this.type = "PerspectiveCamera"; this.fov = fov !== undefined ? fov : 50; this.zoom = 1; this.near = near !== undefined ? near : 0.1; this.far = far !== undefined ? far : 2000; this.focus = 10; this.aspect = aspect !== undefined ? aspect : 1; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: PerspectiveCamera, isPerspectiveCamera: true, copy: function ( source, recursive ) { Camera.prototype.copy.call( this, source, recursive ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; }, /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength: function ( focalLength ) { // see http://www.bobatkins.com/photography/technical/field_of_view.html var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); }, /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength: function () { var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; }, getEffectiveFOV: function () { return _Math.RAD2DEG * 2 * Math.atan( Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); }, getFilmWidth: function () { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); }, getFilmHeight: function () { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); }, /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * var w = 1920; * var h = 1080; * var fullWidth = w * 3; * var fullHeight = h * 2; * * --A-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; if ( this.view === null ) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); }, clearViewOffset: function () { if ( this.view !== null ) { this.view.enabled = false; } this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var near = this.near, top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, height = 2 * top, width = this.aspect * height, left = - 0.5 * width, view = this.view; if ( this.view !== null && this.view.enabled ) { var fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } var skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ArrayCamera( array ) { PerspectiveCamera.call( this ); this.cameras = array || []; } ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { constructor: ArrayCamera, isArrayCamera: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebVRManager( renderer ) { var scope = this; var device = null; var frameData = null; // This was added in the facebook copy in order to better render 3D photos // in vr on the feed. var renderScaling = 1.0; var poseTarget = null; var standingMatrix = new Matrix4(); var standingMatrixInverse = new Matrix4(); if ( typeof window !== "undefined" && "VRFrameData" in window ) { frameData = new window.VRFrameData(); window.addEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange, false ); } var matrixWorldInverse = new Matrix4(); var tempQuaternion = new Quaternion(); var tempPosition = new Vector3(); var cameraL = new PerspectiveCamera(); cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 ); cameraL.layers.enable( 1 ); var cameraR = new PerspectiveCamera(); cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 ); cameraR.layers.enable( 2 ); var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); // function isPresenting() { return device !== null && device.isPresenting === true; } var currentSize, currentPixelRatio; function onVRDisplayPresentChange() { if ( isPresenting() ) { var eyeParameters = device.getEyeParameters( "left" ); var renderWidth = eyeParameters.renderWidth * renderScaling; var renderHeight = eyeParameters.renderHeight * renderScaling; currentPixelRatio = renderer.getPixelRatio(); currentSize = renderer.getSize(); renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 ); animation.start(); } else if ( scope.enabled ) { renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); animation.stop(); } } // this.enabled = false; this.userHeight = 1.6; this.autoSubmitFrame = true; // FACEBOOK - we added this back in as it broke 3D photos this.standing = false; this.setScaling = function ( value ) { renderScaling = value; } this.getDevice = function () { return device; }; this.setDevice = function ( value ) { if ( value !== undefined ) device = value; animation.setContext( value ); }; this.setPoseTarget = function ( object ) { if ( object !== undefined ) poseTarget = object; }; this.getCamera = function ( camera ) { if ( device === null ) return camera; device.depthNear = camera.near; device.depthFar = camera.far; device.getFrameData( frameData ); // if ( this.standing ) { var stageParameters = device.stageParameters; if ( stageParameters ) { standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); } else { standingMatrix.makeTranslation( 0, scope.userHeight, 0 ); } } var pose = frameData.pose; var poseObject = poseTarget !== null ? poseTarget : camera; // We want to manipulate poseObject by its position and quaternion components since users may rely on them. poseObject.matrix.copy( standingMatrix ); poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale ); if ( pose.orientation !== null ) { tempQuaternion.fromArray( pose.orientation ); poseObject.quaternion.multiply( tempQuaternion ); } if ( pose.position !== null ) { tempQuaternion.setFromRotationMatrix( standingMatrix ); tempPosition.fromArray( pose.position ); tempPosition.applyQuaternion( tempQuaternion ); poseObject.position.add( tempPosition ); } poseObject.updateMatrixWorld(); if ( device.isPresenting === false ) return camera; // cameraL.near = camera.near; cameraR.near = camera.near; cameraL.far = camera.far; cameraR.far = camera.far; cameraVR.matrixWorld.copy( camera.matrixWorld ); cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); // TODO (mrdoob) Double check this code standingMatrixInverse.getInverse( standingMatrix ); if ( this.standing) { cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); } var parent = poseObject.parent; if ( parent !== null ) { matrixWorldInverse.getInverse( parent.matrixWorld ); cameraL.matrixWorldInverse.multiply( matrixWorldInverse ); cameraR.matrixWorldInverse.multiply( matrixWorldInverse ); } // envMap and Mirror needs camera.matrixWorld cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse ); cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse ); cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); // HACK (mrdoob) // https://github.com/w3c/webvr/issues/203 cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); // var layers = device.getLayers(); if ( layers.length ) { var layer = layers[ 0 ]; if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) { cameraL.bounds.fromArray( layer.leftBounds ); } if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) { cameraR.bounds.fromArray( layer.rightBounds ); } } return cameraVR; }; this.getStandingMatrix = function () { return standingMatrix; }; this.isPresenting = isPresenting; // Animation Loop var animation = new WebGLAnimation(); this.setAnimationLoop = function ( callback ) { animation.setAnimationLoop( callback ); }; this.submitFrame = function () { if ( isPresenting() ) device.submitFrame(); }; this.dispose = function () { if ( typeof window !== "undefined" ) { window.removeEventListener( "vrdisplaypresentchange", onVRDisplayPresentChange ); } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebXRManager( renderer, gl ) { var scope = this; var session = null; // This was added in the facebook copy in order to better render 3D photos // in vr on the feed. var renderScaling = 1.0; var framebufferScaleFactor = 1.0; var referenceSpace = null; var referenceSpaceType = "local"; var pose = null; var controllers = []; var inputSourcesMap = new Map(); // var cameraL = new PerspectiveCamera(); cameraL.layers.enable( 1 ); cameraL.viewport = new Vector4(); var cameraR = new PerspectiveCamera(); cameraR.layers.enable( 2 ); cameraR.viewport = new Vector4(); var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); cameraVR.layers.enable( 1 ); cameraVR.layers.enable( 2 ); var _currentDepthNear = null; var _currentDepthFar = null; // this.enabled = false; this._isPresenting = false; this.autoSubmitFrame = false; // FACEBOOK: keep compatibility with API of WebVRManager this.isPresenting = function () { return scope._isPresenting; } // FACEBOOK: Temporary, should remove once WebVRManager is dead this.setDevice = function ( value ) { // nothing happens }; this.getController = function ( id ) { var controller = controllers[ id ]; if ( controller === undefined ) { controller = {}; controllers[ id ] = controller; } if ( controller.targetRay === undefined ) { controller.targetRay = new Group(); controller.targetRay.matrixAutoUpdate = false; controller.targetRay.visible = false; } return controller.targetRay; }; this.getControllerGrip = function ( id ) { var controller = controllers[ id ]; if ( controller === undefined ) { controller = {}; controllers[ id ] = controller; } if ( controller.grip === undefined ) { controller.grip = new Group(); controller.grip.matrixAutoUpdate = false; controller.grip.visible = false; } return controller.grip; }; // function onSessionEvent( event ) { var controller = inputSourcesMap.get( event.inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: event.type } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: event.type } ); } } } function onSessionEnd() { inputSourcesMap.forEach( function ( controller, inputSource ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "disconnected", data: inputSource } ); controller.targetRay.visible = false; } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "disconnected", data: inputSource } ); controller.grip.visible = false; } } ); inputSourcesMap.clear(); // renderer.setFramebuffer( null ); renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 animation.stop(); scope._isPresenting = false; scope.dispatchEvent( { type: "sessionend" } ); } function onRequestReferenceSpace( value ) { referenceSpace = value; animation.setContext( session ); animation.start(); scope._isPresenting = true; scope.dispatchEvent( { type: "sessionstart" } ); } this.setFramebufferScaleFactor = function ( value ) { framebufferScaleFactor = value; // Warn if function is used while presenting if ( scope._isPresenting == true ) { console.warn( "WebXRManager: Cannot change framebuffer scale while presenting VR content" ); } }; this.setReferenceSpaceType = function ( value ) { referenceSpaceType = value; }; this.getReferenceSpace = function () { return referenceSpace; }; this.getSession = function () { return session; }; this.setSession = function ( value ) { session = value; if ( session !== null ) { session.addEventListener( "select", onSessionEvent ); session.addEventListener( "selectstart", onSessionEvent ); session.addEventListener( "selectend", onSessionEvent ); session.addEventListener( "squeeze", onSessionEvent ); session.addEventListener( "squeezestart", onSessionEvent ); session.addEventListener( "squeezeend", onSessionEvent ); session.addEventListener( "end", onSessionEnd ); var attributes = gl.getContextAttributes(); var layerInit = { antialias: attributes.antialias, alpha: attributes.alpha, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; // eslint-disable-next-line no-undef var baseLayer = new XRWebGLLayer( session, gl, layerInit ); session.updateRenderState( { baseLayer: baseLayer } ); session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); // session.addEventListener( "inputsourceschange", updateInputSources ); } }; function updateInputSources( event ) { var inputSources = session.inputSources; // Assign inputSources to available controllers for ( var i = 0; i < controllers.length; i ++ ) { inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); } // Notify disconnected for ( var i = 0; i < event.removed.length; i ++ ) { var inputSource = event.removed[ i ]; var controller = inputSourcesMap.get( inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "disconnected", data: inputSource } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "disconnected", data: inputSource } ); } inputSourcesMap.delete( inputSource ); } } // Notify connected for ( var i = 0; i < event.added.length; i ++ ) { var inputSource = event.added[ i ]; var controller = inputSourcesMap.get( inputSource ); if ( controller ) { if ( controller.targetRay ) { controller.targetRay.dispatchEvent( { type: "connected", data: inputSource } ); } if ( controller.grip ) { controller.grip.dispatchEvent( { type: "connected", data: inputSource } ); } } } } // var cameraLPos = new Vector3(); var cameraRPos = new Vector3(); /** * @author jsantell / https://www.jsantell.com/ * * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras" projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion( camera, cameraL, cameraR ) { cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); var ipd = cameraLPos.distanceTo( cameraRPos ); var projL = cameraL.projectionMatrix.elements; var projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; var left = near * leftFov; var right = near * rightFov; // Calculate the new camera"s position offset from the // left camera. xOffset should be roughly half `ipd`. var zOffset = ipd / ( - leftFov + rightFov ); var xOffset = zOffset * - leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); camera.translateX( xOffset ); camera.translateZ( zOffset ); camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); // Find the union of the frustum values of the cameras and scale // the values so that the near plane"s position does not change in world space, // although must now be relative to the new union camera. var near2 = near + zOffset; var far2 = far + zOffset; var left2 = left - xOffset; var right2 = right + ( ipd - xOffset ); var top2 = topFov * far / far2 * near2; var bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); } function updateCamera( camera, parent ) { if ( parent === null ) { camera.matrixWorld.copy( camera.matrix ); } else { camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } camera.matrixWorldInverse.getInverse( camera.matrixWorld ); } this.getCamera = function ( camera ) { cameraVR.near = cameraR.near = cameraL.near = camera.near; cameraVR.far = cameraR.far = cameraL.far = camera.far; if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { // Note that the new renderState won"t apply until the next frame. See #18320 session.updateRenderState( { depthNear: cameraVR.near, depthFar: cameraVR.far } ); _currentDepthNear = cameraVR.near; _currentDepthFar = cameraVR.far; } var parent = camera.parent; var cameras = cameraVR.cameras; updateCamera( cameraVR, parent ); for ( var i = 0; i < cameras.length; i ++ ) { updateCamera( cameras[ i ], parent ); } // update camera and its children camera.matrixWorld.copy( cameraVR.matrixWorld ); var children = camera.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( true ); } setProjectionFromUnion( cameraVR, cameraL, cameraR ); return cameraVR; }; // Animation Loop var onAnimationFrameCallback = null; function onAnimationFrame( time, frame ) { pose = frame.getViewerPose( referenceSpace ); if ( pose !== null ) { var views = pose.views; var baseLayer = session.renderState.baseLayer; renderer.setFramebuffer( baseLayer.framebuffer ); for ( var i = 0; i < views.length; i ++ ) { var view = views[ i ]; var viewport = baseLayer.getViewport( view ); var camera = cameraVR.cameras[ i ]; camera.matrix.fromArray( view.transform.matrix ); camera.projectionMatrix.fromArray( view.projectionMatrix ); camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); if ( i === 0 ) { cameraVR.matrix.copy( camera.matrix ); } } } // var inputSources = session.inputSources; for ( var i = 0; i < controllers.length; i ++ ) { var controller = controllers[ i ]; var inputSource = inputSources[ i ]; var inputPose = null; var gripPose = null; if ( inputSource ) { if ( controller.targetRay ) { inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); if ( inputPose !== null ) { controller.targetRay.matrix.fromArray( inputPose.transform.matrix ); controller.targetRay.matrix.decompose( controller.targetRay.position, controller.targetRay.rotation, controller.targetRay.scale ); } } if ( controller.grip && inputSource.gripSpace ) { gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); if ( gripPose !== null ) { controller.grip.matrix.fromArray( gripPose.transform.matrix ); controller.grip.matrix.decompose( controller.grip.position, controller.grip.rotation, controller.grip.scale ); } } } if ( controller.targetRay ) { controller.targetRay.visible = inputPose !== null; } if ( controller.grip ) { controller.grip.visible = gripPose !== null; } } if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); } var animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; this.submitFrame = function () {}; // FACEBOOK this.setScaling = function ( value ) { renderScaling = value; } } Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ * @author tschw */ function WebGLRenderer( parameters ) { console.log( "THREE.WebGLRenderer", REVISION ); parameters = parameters || {}; var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : "default"; var currentRenderList = null; var currentRenderState = null; // public properties this.domElement = _canvas; this.context = null; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.gammaFactor = 2.0; // for backwards compatibility this.gammaInput = false; this.gammaOutput = false; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = LinearToneMapping; this.toneMappingExposure = 1.0; this.toneMappingWhitePoint = 1.0; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // internal properties var _this = this, _isContextLost = false, // internal state cache _framebuffer = null, _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = "", _currentCamera = null, _currentArrayCamera = null, _currentViewport = new Vector4(), _currentScissor = new Vector4(), _currentScissorTest = null, // _usedTextureUnits = 0, // _width = _canvas.width, _height = _canvas.height, _pixelRatio = 1, _viewport = new Vector4( 0, 0, _width, _height ), _scissor = new Vector4( 0, 0, _width, _height ), _scissorTest = false, // frustum _frustum = new Frustum(), // clipping _clipping = new WebGLClipping(), _clippingEnabled = false, _localClippingEnabled = false, // camera matrices cache _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(); function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize var _gl; try { var contextAttributes = { alpha: _alpha, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer, powerPreference: _powerPreference, xrCompatible: true }; // event listeners must be registered before WebGL context is created, see #12753 _canvas.addEventListener( "webglcontextlost", onContextLost, false ); _canvas.addEventListener( "webglcontextrestored", onContextRestore, false ); _gl = _context || _canvas.getContext( "webgl", contextAttributes ) || _canvas.getContext( "experimental-webgl", contextAttributes ); if ( _gl === null ) { if ( _canvas.getContext( "webgl" ) !== null ) { throw new Error( "Error creating WebGL context with your selected attributes." ); } else { throw new Error( "Error creating WebGL context." ); } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { "rangeMin": 1, "rangeMax": 1, "precision": 1 }; }; } } catch ( error ) { console.error( "THREE.WebGLRenderer: " + error.message ); } var extensions, capabilities, state, info; var properties, textures, attributes, geometries, objects; var programCache, renderLists, renderStates; var background, morphtargets, bufferRenderer, indexedBufferRenderer; var spriteRenderer; var utils; function initGLContext() { extensions = new WebGLExtensions( _gl ); extensions.get( "WEBGL_depth_texture" ); extensions.get( "OES_texture_float" ); extensions.get( "OES_texture_float_linear" ); extensions.get( "OES_texture_half_float" ); extensions.get( "OES_texture_half_float_linear" ); extensions.get( "OES_standard_derivatives" ); extensions.get( "OES_element_index_uint" ); extensions.get( "ANGLE_instanced_arrays" ); utils = new WebGLUtils( _gl, extensions ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); state = new WebGLState( _gl, extensions, utils ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); attributes = new WebGLAttributes( _gl ); geometries = new WebGLGeometries( _gl, attributes, info ); objects = new WebGLObjects( geometries, info ); morphtargets = new WebGLMorphtargets( _gl ); programCache = new WebGLPrograms( _this, extensions, capabilities ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(); background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info ); indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info ); spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities ); info.programs = programCache.programs; _this.context = _gl; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.state = state; _this.info = info; } initGLContext(); // vr var vr = ( "xr" in navigator ) ? new WebXRManager( _this, _gl ) : new WebVRManager( _this ); this.vr = vr; // shadow map var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); this.shadowMap = shadowMap; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.loseContext(); }; this.forceContextRestore = function () { var extension = extensions.get( "WEBGL_lose_context" ); if ( extension ) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _width, _height, false ); }; this.getSize = function () { return { width: _width, height: _height }; }; this.setSize = function ( width, height, updateStyle ) { if ( vr.isPresenting() ) { console.warn( "THREE.WebGLRenderer: Can"t change size while VR device is presenting." ); return; } _width = width; _height = height; _canvas.width = width * _pixelRatio; _canvas.height = height * _pixelRatio; if ( updateStyle !== false ) { _canvas.style.width = width + "px"; _canvas.style.height = height + "px"; } this.setViewport( 0, 0, width, height ); }; this.getDrawingBufferSize = function () { return { width: _width * _pixelRatio, height: _height * _pixelRatio }; }; this.setDrawingBufferSize = function ( width, height, pixelRatio ) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = width * pixelRatio; _canvas.height = height * pixelRatio; this.setViewport( 0, 0, width, height ); }; this.getCurrentViewport = function () { return _currentViewport; }; this.setViewport = function ( x, y, width, height ) { _viewport.set( x, _height - y - height, width, height ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); }; this.setScissor = function ( x, y, width, height ) { _scissor.set( x, _height - y - height, width, height ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; // Clearing this.getClearColor = function () { return background.getClearColor(); }; this.setClearColor = function () { background.setClearColor.apply( background, arguments ); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply( background, arguments ); }; this.clear = function ( color, depth, stencil ) { var bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // this.dispose = function () { _canvas.removeEventListener( "webglcontextlost", onContextLost, false ); _canvas.removeEventListener( "webglcontextrestored", onContextRestore, false ); renderLists.dispose(); renderStates.dispose(); properties.dispose(); objects.dispose(); vr.dispose(); animation.stop(); }; // Events function onContextLost( event ) { event.preventDefault(); console.log( "THREE.WebGLRenderer: Context Lost." ); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log( "THREE.WebGLRenderer: Context Restored." ); _isContextLost = false; initGLContext(); } function onMaterialDispose( event ) { var material = event.target; material.removeEventListener( "dispose", onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReference( material ); properties.remove( material ); } function releaseMaterialProgramReference( material ) { var programInfo = properties.get( material ).program; material.program = undefined; if ( programInfo !== undefined ) { programCache.releaseProgram( programInfo ); } } // Buffer rendering function renderObjectImmediate( object, program, material ) { object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); } this.renderBufferImmediate = function ( object, program, material ) { state.initAttributes(); var buffers = properties.get( object ); if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); var programAttributes = program.getAttributes(); if ( object.hasPositions ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.position ); _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); if ( ! material.isMeshPhongMaterial && ! material.isMeshStandardMaterial && ! material.isMeshNormalMaterial && material.flatShading === true ) { for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { var array = object.normalArray; var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; array[ i + 0 ] = nx; array[ i + 1 ] = ny; array[ i + 2 ] = nz; array[ i + 3 ] = nx; array[ i + 4 ] = ny; array[ i + 5 ] = nz; array[ i + 6 ] = nx; array[ i + 7 ] = ny; array[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.normal ); _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.uv ); _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== NoColors ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( programAttributes.color ); _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); } state.disableUnusedAttributes(); _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, fog, material, object ); var geometryProgram = geometry.id + "_" + program.id + "_" + ( material.wireframe === true ); var updateBuffers = false; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } if ( object.morphTargetInfluences ) { morphtargets.update( object, geometry, material, program ); updateBuffers = true; } // var index = geometry.index; var position = geometry.attributes.position; var rangeFactor = 1; if ( material.wireframe === true ) { index = geometries.getWireframeAttribute( geometry ); rangeFactor = 2; } var attribute; var renderer = bufferRenderer; if ( index !== null ) { attribute = attributes.get( index ); renderer = indexedBufferRenderer; renderer.setIndex( attribute ); } if ( updateBuffers ) { setupVertexAttributes( material, program, geometry ); if ( index !== null ) { _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); } } // var dataCount = Infinity; if ( index !== null ) { dataCount = index.count; } else if ( position !== undefined ) { dataCount = position.count; } var rangeStart = geometry.drawRange.start * rangeFactor; var rangeCount = geometry.drawRange.count * rangeFactor; var groupStart = group !== null ? group.start * rangeFactor : 0; var groupCount = group !== null ? group.count * rangeFactor : Infinity; var drawStart = Math.max( rangeStart, groupStart ); var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { switch ( object.drawMode ) { case TrianglesDrawMode: renderer.setMode( _gl.TRIANGLES ); break; case TriangleStripDrawMode: renderer.setMode( _gl.TRIANGLE_STRIP ); break; case TriangleFanDrawMode: renderer.setMode( _gl.TRIANGLE_FAN ); break; } } } else if ( object.isLine ) { var lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else if ( object.isLineLoop ) { renderer.setMode( _gl.LINE_LOOP ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } if ( geometry && geometry.isInstancedBufferGeometry ) { if ( geometry.maxInstancedCount > 0 ) { renderer.renderInstances( geometry, drawStart, drawCount ); } } else { renderer.render( drawStart, drawCount ); } }; function setupVertexAttributes( material, program, geometry ) { if ( geometry && geometry.isInstancedBufferGeometry ) { if ( extensions.get( "ANGLE_instanced_arrays" ) === null ) { console.error( "THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays." ); return; } } state.initAttributes(); var geometryAttributes = geometry.attributes; var programAttributes = program.getAttributes(); var materialDefaultAttributeValues = material.defaultAttributeValues; for ( var name in programAttributes ) { var programAttribute = programAttributes[ name ]; if ( programAttribute >= 0 ) { var geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute !== undefined ) { var normalized = geometryAttribute.normalized; var size = geometryAttribute.itemSize; var attribute = attributes.get( geometryAttribute ); // TODO Attribute may not be available on context restore if ( attribute === undefined ) continue; var buffer = attribute.buffer; var type = attribute.type; var bytesPerElement = attribute.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { var data = geometryAttribute.data; var stride = data.stride; var offset = geometryAttribute.offset; if ( data && data.isInstancedInterleavedBuffer ) { state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = data.meshPerAttribute * data.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); } else { if ( geometryAttribute.isInstancedBufferAttribute ) { state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); } } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: _gl.vertexAttrib2fv( programAttribute, value ); break; case 3: _gl.vertexAttrib3fv( programAttribute, value ); break; case 4: _gl.vertexAttrib4fv( programAttribute, value ); break; default: _gl.vertexAttrib1fv( programAttribute, value ); } } } } } state.disableUnusedAttributes(); } // Compile this.compile = function ( scene, camera ) { currentRenderState = renderStates.get( scene, camera ); currentRenderState.init(); scene.traverse( function ( object ) { if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } } ); currentRenderState.setupLights( camera ); scene.traverse( function ( object ) { if ( object.material ) { if ( Array.isArray( object.material ) ) { for ( var i = 0; i < object.material.length; i ++ ) { initMaterial( object.material[ i ], scene.fog, object ); } } else { initMaterial( object.material, scene.fog, object ); } } } ); }; // Animation Loop var onAnimationFrameCallback = null; function onAnimationFrame() { if ( vr.isPresenting() ) return; if ( onAnimationFrameCallback ) onAnimationFrameCallback(); } var animation = new WebGLAnimation(); animation.setAnimationLoop( onAnimationFrame ); animation.setContext( window ); this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; vr.setAnimationLoop( callback ); animation.start(); }; // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( ! ( camera && camera.isCamera ) ) { console.error( "THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera." ); return; } if ( _isContextLost ) return; // reset caching for this frame _currentGeometryProgram = ""; _currentMaterialId = - 1; _currentCamera = null; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); if ( vr.enabled ) { camera = vr.getCamera( camera ); } // currentRenderState = renderStates.get( scene, camera ); currentRenderState.init(); scene.onBeforeRender( _this, scene, camera, renderTarget ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); currentRenderList = renderLists.get( scene, camera ); currentRenderList.init(); projectObject( scene, camera, _this.sortObjects ); if ( _this.sortObjects === true ) { currentRenderList.sort(); } // if ( _clippingEnabled ) _clipping.beginShadows(); var shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render( shadowsArray, scene, camera ); currentRenderState.setupLights( camera ); if ( _clippingEnabled ) _clipping.endShadows(); // if ( this.info.autoReset ) this.info.reset(); if ( renderTarget === undefined ) { renderTarget = null; } this.setRenderTarget( renderTarget ); // background.render( currentRenderList, scene, camera, forceClear ); // render scene var opaqueObjects = currentRenderList.opaque; var transparentObjects = currentRenderList.transparent; if ( scene.overrideMaterial ) { var overrideMaterial = scene.overrideMaterial; if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); } else { // opaque pass (front-to-back order) if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); // transparent pass (back-to-front order) if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); } // custom renderers var spritesArray = currentRenderState.state.spritesArray; spriteRenderer.render( spritesArray, scene, camera ); // Generate mipmap if we"re using any kind of mipmap filtering if ( renderTarget ) { textures.updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest( true ); state.buffers.depth.setMask( true ); state.buffers.color.setMask( true ); state.setPolygonOffset( false ); scene.onAfterRender( _this, scene, camera ); // The facebook 3D photos need to use the stencil buffer, and in order // do do that we can"t submit the frame here as it will clear the drawing // buffer. I"m making a fix to the ThreeJS code and opened issue // https://github.com/mrdoob/three.js/issues/13967 if ( vr.enabled && vr.autoSubmitFrame) { vr.submitFrame(); } // _gl.finish(); currentRenderList = null; currentRenderState = null; }; /* // TODO Duplicated code (Frustum) var _sphere = new Sphere(); function isObjectViewable( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ). applyMatrix4( object.matrixWorld ); return isSphereViewable( _sphere ); } function isSpriteViewable( sprite ) { _sphere.center.set( 0, 0, 0 ); _sphere.radius = 0.7071067811865476; _sphere.applyMatrix4( sprite.matrixWorld ); return isSphereViewable( _sphere ); } function isSphereViewable( sphere ) { if ( ! _frustum.intersectsSphere( sphere ) ) return false; var numPlanes = _clipping.numPlanes; if ( numPlanes === 0 ) return true; var planes = _this.clippingPlanes, center = sphere.center, negRad = - sphere.radius, i = 0; do { // out when deeper than radius in the negative halfspace if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; } while ( ++ i !== numPlanes ); return true; } */ function projectObject( object, camera, sortObjects ) { if ( object.visible === false ) return; var visible = object.layers.test( camera.layers ); if ( visible ) { if ( object.isLight ) { currentRenderState.pushLight( object ); if ( object.castShadow ) { currentRenderState.pushShadow( object ); } } else if ( object.isSprite ) { if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { currentRenderState.pushSprite( object ); } } else if ( object.isImmediateRenderObject ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } currentRenderList.push( object, null, object.material, _vector3.z, null ); } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( object.isSkinnedMesh ) { object.skeleton.update(); } if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { if ( sortObjects ) { _vector3.setFromMatrixPosition( object.matrixWorld ) .applyMatrix4( _projScreenMatrix ); } var geometry = objects.update( object ); var material = object.material; if ( Array.isArray( material ) ) { var groups = geometry.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; var groupMaterial = material[ group.materialIndex ]; if ( groupMaterial && groupMaterial.visible ) { currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group ); } } } else if ( material.visible ) { currentRenderList.push( object, geometry, material, _vector3.z, null ); } } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, sortObjects ); } } function renderObjects( renderList, scene, camera, overrideMaterial ) { for ( var i = 0, l = renderList.length; i < l; i ++ ) { var renderItem = renderList[ i ]; var object = renderItem.object; var geometry = renderItem.geometry; var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; var group = renderItem.group; if ( camera.isArrayCamera ) { _currentArrayCamera = camera; var cameras = camera.cameras; for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { var camera2 = cameras[ j ]; if ( object.layers.test( camera2.layers ) ) { if ( "viewport" in camera2 ) { // XR state.viewport( _currentViewport.copy( camera2.viewport ) ); } else { var bounds = camera2.bounds; var x = bounds.x * _width; var y = bounds.y * _height; var width = bounds.z * _width; var height = bounds.w * _height; state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); } renderObject( object, scene, camera2, geometry, material, group ); } } } else { _currentArrayCamera = null; renderObject( object, scene, camera, geometry, material, group ); } } } function renderObject( object, scene, camera, geometry, material, group ) { object.onBeforeRender( _this, scene, camera, geometry, material, group ); currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); if ( object.isImmediateRenderObject ) { var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, scene.fog, material, object ); _currentGeometryProgram = ""; renderObjectImmediate( object, program, material ); } else { _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); } function initMaterial( material, fog, object ) { var materialProperties = properties.get( material ); var lights = currentRenderState.state.lights; var shadowsArray = currentRenderState.state.shadowsArray; var parameters = programCache.getParameters( material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); var code = programCache.getProgramCode( material, parameters ); var program = materialProperties.program; var programChange = true; if ( program === undefined ) { // new material material.addEventListener( "dispose", onMaterialDispose ); } else if ( program.code !== code ) { // changed glsl or parameters releaseMaterialProgramReference( material ); } else if ( materialProperties.lightsHash !== lights.state.hash ) { properties.update( material, "lightsHash", lights.state.hash ); programChange = false; } else if ( parameters.shaderID !== undefined ) { // same glsl and uniform list return; } else { // only rebuild uniform list programChange = false; } if ( programChange ) { if ( parameters.shaderID ) { var shader = ShaderLib[ parameters.shaderID ]; materialProperties.shader = { name: material.type, uniforms: UniformsUtils.clone( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; } else { materialProperties.shader = { name: material.type, uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader }; } material.onBeforeCompile( materialProperties.shader, _this ); program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); materialProperties.program = program; material.program = program; } var programAttributes = program.getAttributes(); if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { if ( programAttributes[ "morphTarget" + i ] >= 0 ) { material.numSupportedMorphTargets ++; } } } if ( material.morphNormals ) { material.numSupportedMorphNormals = 0; for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { if ( programAttributes[ "morphNormal" + i ] >= 0 ) { material.numSupportedMorphNormals ++; } } } var uniforms = materialProperties.shader.uniforms; if ( ! material.isShaderMaterial && ! material.isRawShaderMaterial || material.clipping === true ) { materialProperties.numClippingPlanes = _clipping.numPlanes; materialProperties.numIntersection = _clipping.numIntersection; uniforms.clippingPlanes = _clipping.uniform; } materialProperties.fog = fog; // store the light setup it was created for materialProperties.lightsHash = lights.state.hash; if ( material.lights ) { // wire up the material to this renderer"s lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.directionalLights.value = lights.state.directional; uniforms.spotLights.value = lights.state.spot; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.pointLights.value = lights.state.point; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } var progUniforms = materialProperties.program.getUniforms(), uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.uniformsList = uniformsList; } function setProgram( camera, fog, material, object ) { _usedTextureUnits = 0; var materialProperties = properties.get( material ); var lights = currentRenderState.state.lights; if ( _clippingEnabled ) { if ( _localClippingEnabled || camera !== _currentCamera ) { var useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) _clipping.setState( material.clippingPlanes, material.clipIntersection, material.clipShadows, camera, materialProperties, useCache ); } } if ( material.needsUpdate === false ) { if ( materialProperties.program === undefined ) { material.needsUpdate = true; } else if ( material.fog && materialProperties.fog !== fog ) { material.needsUpdate = true; } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { material.needsUpdate = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== _clipping.numPlanes || materialProperties.numIntersection !== _clipping.numIntersection ) ) { material.needsUpdate = true; } } if ( material.needsUpdate ) { initMaterial( material, fog, object ); material.needsUpdate = false; } var refreshProgram = false; var refreshMaterial = false; var refreshLights = false; var program = materialProperties.program, p_uniforms = program.getUniforms(), m_uniforms = materialProperties.shader.uniforms; if ( state.useProgram( program.program ) ) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { p_uniforms.setValue( _gl, "projectionMatrix", camera.projectionMatrix ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, "logDepthBufFC", 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } // Avoid unneeded uniform updates per ArrayCamera"s sub-camera if ( _currentCamera !== ( _currentArrayCamera || camera ) ) { _currentCamera = ( _currentArrayCamera || camera ); // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.envMap ) { var uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.skinning ) { p_uniforms.setValue( _gl, "viewMatrix", camera.matrixWorldInverse ); } } // skinning uniforms must be set even if material didn"t change // auto-setting of texture unit for bone texture must go before other textures // not sure why, but otherwise weird things happen if ( material.skinning ) { p_uniforms.setOptional( _gl, object, "bindMatrix" ); p_uniforms.setOptional( _gl, object, "bindMatrixInverse" ); var skeleton = object.skeleton; if ( skeleton ) { var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === undefined ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix size = _Math.ceilPowerOfTwo( size ); size = Math.max( size, 4 ); var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( skeleton.boneMatrices ); // copy current values var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); boneTexture.needsUpdate = true; skeleton.boneMatrices = boneMatrices; skeleton.boneTexture = boneTexture; skeleton.boneTextureSize = size; } p_uniforms.setValue( _gl, "boneTexture", skeleton.boneTexture ); p_uniforms.setValue( _gl, "boneTextureSize", skeleton.boneTextureSize ); } else { p_uniforms.setOptional( _gl, skeleton, "boneMatrices" ); } } } if ( refreshMaterial ) { p_uniforms.setValue( _gl, "toneMappingExposure", _this.toneMappingExposure ); p_uniforms.setValue( _gl, "toneMappingWhitePoint", _this.toneMappingWhitePoint ); if ( material.lights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer"s state for their // values // // use the current material"s .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material.isMeshBasicMaterial ) { refreshUniformsCommon( m_uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsLambert( m_uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshToonMaterial ) { refreshUniformsToon( m_uniforms, material ); } else { refreshUniformsPhong( m_uniforms, material ); } } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( m_uniforms, material ); if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( m_uniforms, material ); } else { refreshUniformsStandard( m_uniforms, material ); } } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDepth( m_uniforms, material ); } else if ( material.isMeshDistanceMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsDistance( m_uniforms, material ); } else if ( material.isMeshNormalMaterial ) { refreshUniformsCommon( m_uniforms, material ); refreshUniformsNormal( m_uniforms, material ); } else if ( material.isLineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); if ( material.isLineDashedMaterial ) { refreshUniformsDash( m_uniforms, material ); } } else if ( material.isPointsMaterial ) { refreshUniformsPoints( m_uniforms, material ); } else if ( material.isShadowMaterial ) { m_uniforms.color.value = material.color; m_uniforms.opacity.value = material.opacity; } // RectAreaLight Texture // TODO (mrdoob): Find a nicer implementation if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); material.uniformsNeedUpdate = false; } // common matrices p_uniforms.setValue( _gl, "modelViewMatrix", object.modelViewMatrix ); p_uniforms.setValue( _gl, "normalMatrix", object.normalMatrix ); p_uniforms.setValue( _gl, "modelMatrix", object.matrixWorld ); return program; } // Uniforms (refresh uniforms objects) function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( material.color ) { uniforms.diffuse.value = material.color; } if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } if ( material.map ) { uniforms.map.value = material.map; } if ( material.alphaMap ) { uniforms.alphaMap.value = material.alphaMap; } if ( material.specularMap ) { uniforms.specularMap.value = material.specularMap; } if ( material.envMap ) { uniforms.envMap.value = material.envMap; // don"t flip CubeTexture envMaps, flip everything else: // WebGLRenderTargetCube will be flipped for backwards compatibility // WebGLRenderTargetCube.texture will be flipped because it"s a Texture and NOT a CubeTexture // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; uniforms.reflectivity.value = material.reflectivity; uniforms.refractionRatio.value = material.refractionRatio; uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel; } if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. normal map // 4. bump map // 5. alpha map // 6. emissive map var uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.displacementMap ) { uvScaleMap = material.displacementMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.roughnessMap ) { uvScaleMap = material.roughnessMap; } else if ( material.metalnessMap ) { uvScaleMap = material.metalnessMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } else if ( material.emissiveMap ) { uvScaleMap = material.emissiveMap; } if ( uvScaleMap !== undefined ) { // backwards compatibility if ( uvScaleMap.isWebGLRenderTarget ) { uvScaleMap = uvScaleMap.texture; } if ( uvScaleMap.matrixAutoUpdate === true ) { uvScaleMap.updateMatrix(); } uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * _pixelRatio; uniforms.scale.value = _height * 0.5; uniforms.map.value = material.map; if ( material.map !== null ) { if ( material.map.matrixAutoUpdate === true ) { material.map.updateMatrix(); } uniforms.uvTransform.value.copy( material.map.matrix ); } } function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshUniformsLambert( uniforms, material ) { if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value = material.specular; uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon( uniforms, material ) { refreshUniformsPhong( uniforms, material ); if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; } if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material ) { uniforms.clearCoat.value = material.clearCoat; uniforms.clearCoatRoughness.value = material.clearCoatRoughness; refreshUniformsStandard( uniforms, material ); } function refreshUniformsDepth( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsDistance( uniforms, material ) { if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } uniforms.referencePosition.value.copy( material.referencePosition ); uniforms.nearDistance.value = material.nearDistance; uniforms.farDistance.value = material.farDistance; } function refreshUniformsNormal( uniforms, material ) { if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } // If uniforms are marked as clean, they don"t need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } // Textures function allocTextureUnit() { var textureUnit = _usedTextureUnits; if ( textureUnit >= capabilities.maxTextures ) { console.warn( "THREE.WebGLRenderer: Trying to use " + textureUnit + " texture units while this GPU supports only " + capabilities.maxTextures ); } _usedTextureUnits += 1; return textureUnit; } this.allocTextureUnit = allocTextureUnit; // this.setTexture2D = setTexture2D; this.setTexture2D = ( function () { var warned = false; // backwards compatibility: peel texture.texture return function setTexture2D( texture, slot ) { if ( texture && texture.isWebGLRenderTarget ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTexture2D: don"t use render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } textures.setTexture2D( texture, slot ); }; }() ); this.setTexture = ( function () { var warned = false; return function setTexture( texture, slot ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); warned = true; } textures.setTexture2D( texture, slot ); }; }() ); this.setTextureCube = ( function () { var warned = false; return function setTextureCube( texture, slot ) { // backwards compatibility: peel texture.texture if ( texture && texture.isWebGLRenderTargetCube ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTextureCube: don"t use cube render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture // TODO: unify these code paths if ( ( texture && texture.isCubeTexture ) || ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ // this function alone should take care of cube textures textures.setTextureCube( texture, slot ); } else { // assumed: texture property of THREE.WebGLRenderTargetCube textures.setTextureCubeDynamic( texture, slot ); } }; }() ); // this.setFramebuffer = function ( value ) { _framebuffer = value; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTarget = function ( renderTarget ) { _currentRenderTarget = renderTarget; if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } var framebuffer = _framebuffer; var isCube = false; if ( renderTarget ) { var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( renderTarget.isWebGLRenderTargetCube ) { framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; isCube = true; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy( renderTarget.viewport ); _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); _currentScissorTest = _scissorTest; } if ( _currentFramebuffer !== framebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _currentFramebuffer = framebuffer; } state.viewport( _currentViewport ); state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); if ( isCube ) { var textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); } }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget." ); return; } var framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( framebuffer ) { var restore = false; if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); restore = true; } try { var texture = renderTarget.texture; var textureFormat = texture.format; var textureType = texture.type; if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format." ); return; } if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( extensions.get( "OES_texture_float" ) || extensions.get( "WEBGL_color_buffer_float" ) ) ) && // Chrome Mac >= 52 and Firefox ! ( textureType === HalfFloatType && extensions.get( "EXT_color_buffer_half_float" ) ) ) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type." ); return; } if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } } else { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete." ); } } finally { if ( restore ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); } } } }; this.copyFramebufferToTexture = function ( position, texture, level ) { var width = texture.image.width; var height = texture.image.height; var glFormat = utils.convert( texture.format ); this.setTexture2D( texture, 0 ); _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 ); }; this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { var width = srcTexture.image.width; var height = srcTexture.image.height; var glFormat = utils.convert( dstTexture.format ); var glType = utils.convert( dstTexture.type ); this.setTexture2D( dstTexture, 0 ); if ( srcTexture.isDataTexture ) { _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); } else { _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, glFormat, glType, srcTexture.image ); } }; } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function FogExp2( color, density ) { this.name = ""; this.color = new Color( color ); this.density = ( density !== undefined ) ? density : 0.00025; } FogExp2.prototype.isFogExp2 = true; FogExp2.prototype.clone = function () { return new FogExp2( this.color, this.density ); }; FogExp2.prototype.toJSON = function ( /* meta */ ) { return { type: "FogExp2", color: this.color.getHex(), density: this.density }; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Fog( color, near, far ) { this.name = ""; this.color = new Color( color ); this.near = ( near !== undefined ) ? near : 1; this.far = ( far !== undefined ) ? far : 1000; } Fog.prototype.isFog = true; Fog.prototype.clone = function () { return new Fog( this.color, this.near, this.far ); }; Fog.prototype.toJSON = function ( /* meta */ ) { return { type: "Fog", color: this.color.getHex(), near: this.near, far: this.far }; }; /** * @author mrdoob / http://mrdoob.com/ */ function Scene() { Object3D.call( this ); this.type = "Scene"; this.background = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer } Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Scene, copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * uvOffset: new THREE.Vector2(), * uvScale: new THREE.Vector2() * } */ function SpriteMaterial( parameters ) { Material.call( this ); this.type = "SpriteMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.rotation = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } SpriteMaterial.prototype = Object.create( Material.prototype ); SpriteMaterial.prototype.constructor = SpriteMaterial; SpriteMaterial.prototype.isSpriteMaterial = true; SpriteMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.rotation = source.rotation; return this; }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function Sprite( material ) { Object3D.call( this ); this.type = "Sprite"; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); } Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Sprite, isSprite: true, raycast: ( function () { var intersectPoint = new Vector3(); var worldPosition = new Vector3(); var worldScale = new Vector3(); return function raycast( raycaster, intersects ) { worldPosition.setFromMatrixPosition( this.matrixWorld ); raycaster.ray.closestPointToPoint( worldPosition, intersectPoint ); worldScale.setFromMatrixScale( this.matrixWorld ); var guessSizeSq = worldScale.x * worldScale.y / 4; if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return; var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, point: intersectPoint.clone(), face: null, object: this } ); }; }() ), clone: function () { return new this.constructor( this.material ).copy( this ); }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); if ( source.center !== undefined ) this.center.copy( source.center ); return this; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function LOD() { Object3D.call( this ); this.type = "LOD"; Object.defineProperties( this, { levels: { enumerable: true, value: [] } } ); } LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LOD, copy: function ( source ) { Object3D.prototype.copy.call( this, source, false ); var levels = source.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; this.addLevel( level.object.clone(), level.distance ); } return this; }, addLevel: function ( object, distance ) { if ( distance === undefined ) distance = 0; distance = Math.abs( distance ); var levels = this.levels; for ( var l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, object: object } ); this.add( object ); }, getObjectForDistance: function ( distance ) { var levels = this.levels; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance < levels[ i ].distance ) { break; } } return levels[ i - 1 ].object; }, raycast: ( function () { var matrixPosition = new Vector3(); return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( matrixPosition ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); }; }() ), update: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function update( camera ) { var levels = this.levels; if ( levels.length > 1 ) { v1.setFromMatrixPosition( camera.matrixWorld ); v2.setFromMatrixPosition( this.matrixWorld ); var distance = v1.distanceTo( v2 ); levels[ 0 ].object.visible = true; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance >= levels[ i ].distance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } }; }(), toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.levels = []; var levels = this.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance } ); } return data; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author michael guerrero / http://realitymeltdown.com * @author ikerr / http://verold.com */ function Skeleton( bones, boneInverses ) { // copy the bone array bones = bones || []; this.bones = bones.slice( 0 ); this.boneMatrices = new Float32Array( this.bones.length * 16 ); // use the supplied bone inverses or calculate the inverses if ( boneInverses === undefined ) { this.calculateInverses(); } else { if ( this.bones.length === boneInverses.length ) { this.boneInverses = boneInverses.slice( 0 ); } else { console.warn( "THREE.Skeleton boneInverses is the wrong length." ); this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { this.boneInverses.push( new Matrix4() ); } } } } Object.assign( Skeleton.prototype, { calculateInverses: function () { this.boneInverses = []; for ( var i = 0, il = this.bones.length; i < il; i ++ ) { var inverse = new Matrix4(); if ( this.bones[ i ] ) { inverse.getInverse( this.bones[ i ].matrixWorld ); } this.boneInverses.push( inverse ); } }, pose: function () { var bone, i, il; // recover the bind-time world matrices for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { bone.matrixWorld.getInverse( this.boneInverses[ i ] ); } } // compute the local matrices, positions, rotations and scales for ( i = 0, il = this.bones.length; i < il; i ++ ) { bone = this.bones[ i ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.getInverse( bone.parent.matrixWorld ); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } }, update: ( function () { var offsetMatrix = new Matrix4(); var identityMatrix = new Matrix4(); return function update() { var bones = this.bones; var boneInverses = this.boneInverses; var boneMatrices = this.boneMatrices; var boneTexture = this.boneTexture; // flatten bone matrices to array for ( var i = 0, il = bones.length; i < il; i ++ ) { // compute the offset between the current and the original transform var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix; offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); offsetMatrix.toArray( boneMatrices, i * 16 ); } if ( boneTexture !== undefined ) { boneTexture.needsUpdate = true; } }; } )(), clone: function () { return new Skeleton( this.bones, this.boneInverses ); }, getBoneByName: function ( name ) { for ( var i = 0, il = this.bones.length; i < il; i ++ ) { var bone = this.bones[ i ]; if ( bone.name === name ) { return bone; } } return undefined; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function Bone() { Object3D.call( this ); this.type = "Bone"; } Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Bone, isBone: true } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function SkinnedMesh( geometry, material ) { Mesh.call( this, geometry, material ); this.type = "SkinnedMesh"; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); var bones = this.initBones(); var skeleton = new Skeleton( bones ); this.bind( skeleton, this.matrixWorld ); this.normalizeSkinWeights(); } SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { constructor: SkinnedMesh, isSkinnedMesh: true, initBones: function () { var bones = [], bone, gbone; var i, il; if ( this.geometry && this.geometry.bones !== undefined ) { // first, create array of "Bone" objects from geometry data for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; // create new "Bone" object bone = new Bone(); bones.push( bone ); // apply values bone.name = gbone.name; bone.position.fromArray( gbone.pos ); bone.quaternion.fromArray( gbone.rotq ); if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); } // second, create bone hierarchy for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { gbone = this.geometry.bones[ i ]; if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { // subsequent bones in the hierarchy bones[ gbone.parent ].add( bones[ i ] ); } else { // topmost bone, immediate child of the skinned mesh this.add( bones[ i ] ); } } } // now the bones are part of the scene graph and children of the skinned mesh. // let"s update the corresponding matrices this.updateMatrixWorld( true ); return bones; }, bind: function ( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.getInverse( bindMatrix ); }, pose: function () { this.skeleton.pose(); }, normalizeSkinWeights: function () { var scale, i; if ( this.geometry && this.geometry.isGeometry ) { for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) { var sw = this.geometry.skinWeights[ i ]; scale = 1.0 / sw.manhattanLength(); if ( scale !== Infinity ) { sw.multiplyScalar( scale ); } else { sw.set( 1, 0, 0, 0 ); // do something reasonable } } } else if ( this.geometry && this.geometry.isBufferGeometry ) { var vec = new Vector4(); var skinWeight = this.geometry.attributes.skinWeight; for ( i = 0; i < skinWeight.count; i ++ ) { vec.x = skinWeight.getX( i ); vec.y = skinWeight.getY( i ); vec.z = skinWeight.getZ( i ); vec.w = skinWeight.getW( i ); scale = 1.0 / vec.manhattanLength(); if ( scale !== Infinity ) { vec.multiplyScalar( scale ); } else { vec.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); } } }, updateMatrixWorld: function ( force ) { Mesh.prototype.updateMatrixWorld.call( this, force ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.getInverse( this.matrixWorld ); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.getInverse( this.bindMatrix ); } else { console.warn( "THREE.SkinnedMesh: Unrecognized bindMode: " + this.bindMode ); } }, clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ function LineBasicMaterial( parameters ) { Material.call( this ); this.type = "LineBasicMaterial"; this.color = new Color( 0xffffff ); this.linewidth = 1; this.linecap = "round"; this.linejoin = "round"; this.lights = false; this.setValues( parameters ); } LineBasicMaterial.prototype = Object.create( Material.prototype ); LineBasicMaterial.prototype.constructor = LineBasicMaterial; LineBasicMaterial.prototype.isLineBasicMaterial = true; LineBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function Line( geometry, material, mode ) { if ( mode === 1 ) { console.warn( "THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead." ); return new LineSegments( geometry, material ); } Object3D.call( this ); this.type = "Line"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); } Line.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Line, isLine: true, computeLineDistances: ( function () { var start = new Vector3(); var end = new Vector3(); return function computeLineDistances() { var geometry = this.geometry; if ( geometry.isBufferGeometry ) { // we assume non-indexed geometry if ( geometry.index === null ) { var positionAttribute = geometry.attributes.position; var lineDistances = [ 0 ]; for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { start.fromBufferAttribute( positionAttribute, i - 1 ); end.fromBufferAttribute( positionAttribute, i ); lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += start.distanceTo( end ); } geometry.addAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var lineDistances = geometry.lineDistances; lineDistances[ 0 ] = 0; for ( var i = 1, l = vertices.length; i < l; i ++ ) { lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); } } return this; }; }() ), raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; var precisionSq = precision * precision; var geometry = this.geometry; var matrixWorld = this.matrixWorld; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var vStart = new Vector3(); var vEnd = new Vector3(); var interSegment = new Vector3(); var interRay = new Vector3(); var step = ( this && this.isLineSegments ) ? 2 : 1; if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, l = indices.length - 1; i < l; i += step ) { var a = indices[ i ]; var b = indices[ i + 1 ]; vStart.fromArray( positions, a * 3 ); vEnd.fromArray( positions, b * 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { vStart.fromArray( positions, 3 * i ); vEnd.fromArray( positions, 3 * i + 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var nbVertices = vertices.length; for ( var i = 0; i < nbVertices - 1; i += step ) { var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LineSegments( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineSegments"; } LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineSegments, isLineSegments: true, computeLineDistances: ( function () { var start = new Vector3(); var end = new Vector3(); return function computeLineDistances() { var geometry = this.geometry; if ( geometry.isBufferGeometry ) { // we assume non-indexed geometry if ( geometry.index === null ) { var positionAttribute = geometry.attributes.position; var lineDistances = []; for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) { start.fromBufferAttribute( positionAttribute, i ); end.fromBufferAttribute( positionAttribute, i + 1 ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); } geometry.addAttribute( "lineDistance", new Float32BufferAttribute( lineDistances, 1 ) ); } else { console.warn( "THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry." ); } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var lineDistances = geometry.lineDistances; for ( var i = 0, l = vertices.length; i < l; i += 2 ) { start.copy( vertices[ i ] ); end.copy( vertices[ i + 1 ] ); lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); } } return this; }; }() ) } ); /** * @author mgreter / http://github.com/mgreter */ function LineLoop( geometry, material ) { Line.call( this, geometry, material ); this.type = "LineLoop"; } LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineLoop, isLineLoop: true, } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * size: , * sizeAttenuation: * * morphTargets: * } */ function PointsMaterial( parameters ) { Material.call( this ); this.type = "PointsMaterial"; this.color = new Color( 0xffffff ); this.map = null; this.size = 1; this.sizeAttenuation = true; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } PointsMaterial.prototype = Object.create( Material.prototype ); PointsMaterial.prototype.constructor = PointsMaterial; PointsMaterial.prototype.isPointsMaterial = true; PointsMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.morphTargets = source.morphTargets; return this; }; /** * @author alteredq / http://alteredqualia.com/ */ function Points( geometry, material ) { Object3D.call( this ); this.type = "Points"; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); } Points.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Points, isPoints: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var object = this; var geometry = this.geometry; var matrixWorld = this.matrixWorld; var threshold = raycaster.params.Points.threshold; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); sphere.radius += threshold; if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); var localThresholdSq = localThreshold * localThreshold; var position = new Vector3(); var intersectPoint = new Vector3(); function testPoint( point, index ) { var rayPointDistanceSq = ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { ray.closestPointToPoint( point, intersectPoint ); intersectPoint.applyMatrix4( matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint.clone(), index: index, face: null, object: object } ); } } if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, il = indices.length; i < il; i ++ ) { var a = indices[ i ]; position.fromArray( positions, a * 3 ); testPoint( position, a ); } } else { for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { position.fromArray( positions, i * 3 ); testPoint( position, i ); } } } else { var vertices = geometry.vertices; for ( var i = 0, l = vertices.length; i < l; i ++ ) { testPoint( vertices[ i ], i ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Group() { Object3D.call( this ); this.type = "Group"; } Group.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Group, isGroup: true } ); /** * @author mrdoob / http://mrdoob.com/ */ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.generateMipmaps = false; } VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { constructor: VideoTexture, isVideoTexture: true, update: function () { var video = this.image; if ( video.readyState >= video.HAVE_CURRENT_DATA ) { this.needsUpdate = true; } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn"t work for compressed textures ) this.flipY = false; // can"t generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } CompressedTexture.prototype = Object.create( Texture.prototype ); CompressedTexture.prototype.constructor = CompressedTexture; CompressedTexture.prototype.isCompressedTexture = true; /** * @author Matt DesLauriers / @mattdesl * @author atix / arthursilber.de */ function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( "DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat" ); } if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } DepthTexture.prototype = Object.create( Texture.prototype ); DepthTexture.prototype.constructor = DepthTexture; DepthTexture.prototype.isDepthTexture = true; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function WireframeGeometry( geometry ) { BufferGeometry.call( this ); this.type = "WireframeGeometry"; // buffer var vertices = []; // helper variables var i, j, l, o, ol; var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; var key, keys = [ "a", "b", "c" ]; var vertex; // different logic for Geometry and BufferGeometry if ( geometry && geometry.isGeometry ) { // create a data structure that contains all edges without duplicates var faces = geometry.faces; for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex = geometry.vertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = geometry.vertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } else if ( geometry && geometry.isBufferGeometry ) { var position, indices, groups; var group, start, count; var index1, index2; vertex = new Vector3(); if ( geometry.index !== null ) { // indexed BufferGeometry position = geometry.attributes.position; indices = geometry.index; groups = geometry.groups; if ( groups.length === 0 ) { groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; } // create a data structure that contains all eges without duplicates for ( o = 0, ol = groups.length; o < ol; ++ o ) { group = groups[ o ]; start = group.start; count = group.count; for ( i = start, l = ( start + count ); i < l; i += 3 ) { for ( j = 0; j < 3; j ++ ) { edge1 = indices.getX( i + j ); edge2 = indices.getX( i + ( j + 1 ) % 3 ); edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; } } } } // generate vertices for ( key in edges ) { e = edges[ key ]; vertex.fromBufferAttribute( position, e.index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); vertex.fromBufferAttribute( position, e.index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } else { // non-indexed BufferGeometry position = geometry.attributes.position; for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { for ( j = 0; j < 3; j ++ ) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) index1 = 3 * i + j; vertex.fromBufferAttribute( position, index1 ); vertices.push( vertex.x, vertex.y, vertex.z ); index2 = 3 * i + ( ( j + 1 ) % 3 ); vertex.fromBufferAttribute( position, index2 ); vertices.push( vertex.x, vertex.y, vertex.z ); } } } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); WireframeGeometry.prototype.constructor = WireframeGeometry; /** * @author zz85 / https://github.com/zz85 * @author Mugen87 / https://github.com/Mugen87 * * Parametric Surfaces Geometry * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 */ // ParametricGeometry function ParametricGeometry( func, slices, stacks ) { Geometry.call( this ); this.type = "ParametricGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); this.mergeVertices(); } ParametricGeometry.prototype = Object.create( Geometry.prototype ); ParametricGeometry.prototype.constructor = ParametricGeometry; // ParametricBufferGeometry function ParametricBufferGeometry( func, slices, stacks ) { BufferGeometry.call( this ); this.type = "ParametricBufferGeometry"; this.parameters = { func: func, slices: slices, stacks: stacks }; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; var EPS = 0.00001; var normal = new Vector3(); var p0 = new Vector3(), p1 = new Vector3(); var pu = new Vector3(), pv = new Vector3(); var i, j; if ( func.length < 3 ) { console.error( "THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter." ); } // generate vertices, normals and uvs var sliceCount = slices + 1; for ( i = 0; i <= stacks; i ++ ) { var v = i / stacks; for ( j = 0; j <= slices; j ++ ) { var u = j / slices; // vertex func( u, v, p0 ); vertices.push( p0.x, p0.y, p0.z ); // normal // approximate tangent vectors via finite differences if ( u - EPS >= 0 ) { func( u - EPS, v, p1 ); pu.subVectors( p0, p1 ); } else { func( u + EPS, v, p1 ); pu.subVectors( p1, p0 ); } if ( v - EPS >= 0 ) { func( u, v - EPS, p1 ); pv.subVectors( p0, p1 ); } else { func( u, v + EPS, p1 ); pv.subVectors( p1, p0 ); } // cross product of tangent vectors returns surface normal normal.crossVectors( pu, pv ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, v ); } } // generate indices for ( i = 0; i < stacks; i ++ ) { for ( j = 0; j < slices; j ++ ) { var a = i * sliceCount + j; var b = i * sliceCount + j + 1; var c = ( i + 1 ) * sliceCount + j + 1; var d = ( i + 1 ) * sliceCount + j; // faces one and two indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; /** * @author clockworkgeek / https://github.com/clockworkgeek * @author timothypratley / https://github.com/timothypratley * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ // PolyhedronGeometry function PolyhedronGeometry( vertices, indices, radius, detail ) { Geometry.call( this ); this.type = "PolyhedronGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); this.mergeVertices(); } PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; // PolyhedronBufferGeometry function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { BufferGeometry.call( this ); this.type = "PolyhedronBufferGeometry"; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; radius = radius || 1; detail = detail || 0; // default buffer data var vertexBuffer = []; var uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius appplyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.addAttribute( "position", new Float32BufferAttribute( vertexBuffer, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvBuffer, 2 ) ); if ( detail === 0 ) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide( detail ) { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for ( var i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { var cols = Math.pow( 2, detail ); // we use this multidimensional array as a data structure for creating the subdivision var v = []; var i, j; // construct all of the vertices for this subdivision for ( i = 0; i <= cols; i ++ ) { v[ i ] = []; var aj = a.clone().lerp( c, i / cols ); var bj = b.clone().lerp( c, i / cols ); var rows = cols - i; for ( j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( i = 0; i < cols; i ++ ) { for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { var k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function appplyRadius( radius ) { var vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { var vertex = new Vector3(); for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; var u = azimuth( vertex ) / 2 / Math.PI + 0.5; var v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( var i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face var x0 = uvBuffer[ i + 0 ]; var x1 = uvBuffer[ i + 2 ]; var x2 = uvBuffer[ i + 4 ]; var max = Math.max( x0, x1, x2 ); var min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { var stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); var centroid = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); var azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // TetrahedronGeometry function TetrahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "TetrahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; // TetrahedronBufferGeometry function TetrahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; var indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "TetrahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // OctahedronGeometry function OctahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "OctahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } OctahedronGeometry.prototype = Object.create( Geometry.prototype ); OctahedronGeometry.prototype.constructor = OctahedronGeometry; // OctahedronBufferGeometry function OctahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; var indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "OctahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley * @author Mugen87 / https://github.com/Mugen87 */ // IcosahedronGeometry function IcosahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "IcosahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; // IcosahedronBufferGeometry function IcosahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; var indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "IcosahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; /** * @author Abe Pazos / https://hamoid.com * @author Mugen87 / https://github.com/Mugen87 */ // DodecahedronGeometry function DodecahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = "DodecahedronGeometry"; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; // DodecahedronBufferGeometry function DodecahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var r = 1 / t; var vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; var indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = "DodecahedronBufferGeometry"; this.parameters = { radius: radius, detail: detail }; } DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; /** * @author oosmoxiecode / https://github.com/oosmoxiecode * @author WestLangley / https://github.com/WestLangley * @author zz85 / https://github.com/zz85 * @author miningold / https://github.com/miningold * @author jonobr1 / https://github.com/jonobr1 * @author Mugen87 / https://github.com/Mugen87 * */ // TubeGeometry function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { Geometry.call( this ); this.type = "TubeGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; if ( taper !== undefined ) console.warn( "THREE.TubeGeometry: taper has been removed." ); var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); // expose internals this.tangents = bufferGeometry.tangents; this.normals = bufferGeometry.normals; this.binormals = bufferGeometry.binormals; // create geometry this.fromBufferGeometry( bufferGeometry ); this.mergeVertices(); } TubeGeometry.prototype = Object.create( Geometry.prototype ); TubeGeometry.prototype.constructor = TubeGeometry; // TubeBufferGeometry function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { BufferGeometry.call( this ); this.type = "TubeBufferGeometry"; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; tubularSegments = tubularSegments || 64; radius = radius || 1; radialSegments = radialSegments || 8; closed = closed || false; var frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables var vertex = new Vector3(); var normal = new Vector3(); var uv = new Vector2(); var P = new Vector3(); var i, j; // buffer var vertices = []; var normals = []; var uvs = []; var indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt( i / tubularSegments, P ); // retrieve corresponding normal and binormal var N = frames.normals[ i ]; var B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( j = 0; j <= radialSegments; j ++ ) { var v = j / radialSegments * Math.PI * 2; var sin = Math.sin( v ); var cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( i = 0; i <= tubularSegments; i ++ ) { for ( j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; /** * @author oosmoxiecode * @author Mugen87 / https://github.com/Mugen87 * * based on http://www.blackpawn.com/texts/pqtorus/ */ // TorusKnotGeometry function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { Geometry.call( this ); this.type = "TorusKnotGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; if ( heightScale !== undefined ) console.warn( "THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead." ); this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); this.mergeVertices(); } TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; // TorusKnotBufferGeometry function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { BufferGeometry.call( this ); this.type = "TorusKnotBufferGeometry"; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; radius = radius || 1; tube = tube || 0.4; tubularSegments = Math.floor( tubularSegments ) || 64; radialSegments = Math.floor( radialSegments ) || 8; p = p || 2; q = q || 3; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, j; var vertex = new Vector3(); var normal = new Vector3(); var P1 = new Vector3(); var P2 = new Vector3(); var B = new Vector3(); var T = new Vector3(); var N = new Vector3(); // generate vertices, normals and uvs for ( i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement var u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don"t use it B.normalize(); N.normalize(); for ( j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. var v = j / radialSegments * Math.PI * 2; var cx = - tube * Math.cos( v ); var cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { // indices var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { var cu = Math.cos( u ); var su = Math.sin( u ); var quOverP = q / p * u; var cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; /** * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // TorusGeometry function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { Geometry.call( this ); this.type = "TorusGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); this.mergeVertices(); } TorusGeometry.prototype = Object.create( Geometry.prototype ); TorusGeometry.prototype.constructor = TorusGeometry; // TorusBufferGeometry function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { BufferGeometry.call( this ); this.type = "TorusBufferGeometry"; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radius = radius || 1; tube = tube || 0.4; radialSegments = Math.floor( radialSegments ) || 8; tubularSegments = Math.floor( tubularSegments ) || 6; arc = arc || Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var center = new Vector3(); var vertex = new Vector3(); var normal = new Vector3(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= radialSegments; j ++ ) { for ( i = 0; i <= tubularSegments; i ++ ) { var u = i / tubularSegments * arc; var v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); normal.subVectors( vertex, center ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( i / tubularSegments ); uvs.push( j / radialSegments ); } } // generate indices for ( j = 1; j <= radialSegments; j ++ ) { for ( i = 1; i <= tubularSegments; i ++ ) { // indices var a = ( tubularSegments + 1 ) * j + i - 1; var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; var d = ( tubularSegments + 1 ) * j + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * Port from https://github.com/mapbox/earcut (v2.1.2) */ var Earcut = { triangulate: function ( data, holeIndices, dim ) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, outerNode = linkedList( data, 0, outerLen, dim, true ), triangles = []; if ( ! outerNode ) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); // if the shape is not too simple, we"ll use z-order curve hash later; calculate polygon bbox if ( data.length > 80 * dim ) { minX = maxX = data[ 0 ]; minY = maxY = data[ 1 ]; for ( var i = dim; i < outerLen; i += dim ) { x = data[ i ]; y = data[ i + 1 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max( maxX - minX, maxY - minY ); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList( data, start, end, dim, clockwise ) { var i, last; if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } else { for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } if ( last && equals( last, last.next ) ) { removeNode( last ); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints( start, end ) { if ( ! start ) return start; if ( ! end ) end = start; var p = start, again; do { again = false; if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { removeNode( p ); p = end = p.prev; if ( p === p.next ) break; again = true; } else { p = p.next; } } while ( again || p !== end ); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { if ( ! ear ) return; // interlink polygon nodes in z-order if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); var stop = ear, prev, next; // iterate through ears, slicing them one by one while ( ear.prev !== ear.next ) { prev = ear.prev; next = ear.next; if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { // cut off the triangle triangles.push( prev.i / dim ); triangles.push( ear.i / dim ); triangles.push( next.i / dim ); removeNode( ear ); // skipping the next vertice leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can"t find any more ears if ( ear === stop ) { // try filtering points and slicing again if ( ! pass ) { earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); // if this didn"t work, try curing all small self-intersections locally } else if ( pass === 1 ) { ear = cureLocalIntersections( ear, triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { splitEarcut( ear, triangles, dim, minX, minY, invSize ); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar( ear ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // now make sure we don"t have other points inside the potential ear var p = ear.next.next; while ( p !== ear.prev ) { if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) { return false; } p = p.next; } return true; } function isEarHashed( ear, minX, minY, invSize ) { var a = ear.prev, b = ear, c = ear.next; if ( area( a, b, c ) >= 0 ) return false; // reflex, can"t be an ear // triangle bbox; min & max are calculated like this for speed var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); // z-order range for the current triangle bbox; var minZ = zOrder( minTX, minTY, minX, minY, invSize ), maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); // first look for points inside the triangle in increasing z-order var p = ear.nextZ; while ( p && p.z <= maxZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.nextZ; } // then look for points in decreasing z-order p = ear.prevZ; while ( p && p.z >= minZ ) { if ( p !== ear.prev && p !== ear.next && pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; p = p.prevZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections( start, triangles, dim ) { var p = start; do { var a = p.prev, b = p.next.next; if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { triangles.push( a.i / dim ); triangles.push( p.i / dim ); triangles.push( b.i / dim ); // remove two nodes involved removeNode( p ); removeNode( p.next ); p = start = b; } p = p.next; } while ( p !== start ); return p; } // try splitting polygon into two and triangulate them independently function splitEarcut( start, triangles, dim, minX, minY, invSize ) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while ( b !== a.prev ) { if ( a.i !== b.i && isValidDiagonal( a, b ) ) { // split the polygon in two by the diagonal var c = splitPolygon( a, b ); // filter colinear points around the cuts a = filterPoints( a, a.next ); c = filterPoints( c, c.next ); // run earcut on each half earcutLinked( a, triangles, dim, minX, minY, invSize ); earcutLinked( c, triangles, dim, minX, minY, invSize ); return; } b = b.next; } a = a.next; } while ( a !== start ); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles( data, holeIndices, outerNode, dim ) { var queue = [], i, len, start, end, list; for ( i = 0, len = holeIndices.length; i < len; i ++ ) { start = holeIndices[ i ] * dim; end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; list = linkedList( data, start, end, dim, false ); if ( list === list.next ) list.steiner = true; queue.push( getLeftmost( list ) ); } queue.sort( compareX ); // process holes from left to right for ( i = 0; i < queue.length; i ++ ) { eliminateHole( queue[ i ], outerNode ); outerNode = filterPoints( outerNode, outerNode.next ); } return outerNode; } function compareX( a, b ) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole( hole, outerNode ) { outerNode = findHoleBridge( hole, outerNode ); if ( outerNode ) { var b = splitPolygon( outerNode, hole ); filterPoints( b, b.next ); } } // David Eberly"s algorithm for finding a bridge between hole and outer polygon function findHoleBridge( hole, outerNode ) { var p = outerNode, hx = hole.x, hy = hole.y, qx = - Infinity, m; // find a segment intersected by a ray from the hole"s leftmost point to the left; // segment"s endpoint with lesser x will be potential connection point do { if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); if ( x <= hx && x > qx ) { qx = x; if ( x === hx ) { if ( hy === p.y ) return p; if ( hy === p.next.y ) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while ( p !== outerNode ); if ( ! m ) return null; if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m.next; while ( p !== stop ) { if ( hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) { m = p; tanMin = tan; } } p = p.next; } return m; } // interlink polygon nodes in z-order function indexCurve( start, minX, minY, invSize ) { var p = start; do { if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while ( p !== start ); p.prevZ.nextZ = null; p.prevZ = null; sortLinked( p ); } // Simon Tatham"s linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked( list ) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while ( p ) { numMerges ++; q = p; pSize = 0; for ( i = 0; i < inSize; i ++ ) { pSize ++; q = q.nextZ; if ( ! q ) break; } qSize = inSize; while ( pSize > 0 || ( qSize > 0 && q ) ) { if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { e = p; p = p.nextZ; pSize --; } else { e = q; q = q.nextZ; qSize --; } if ( tail ) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while ( numMerges > 1 ); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder( x, y, minX, minY, invSize ) { // coords are transformed into non-negative 15-bit integer range x = 32767 * ( x - minX ) * invSize; y = 32767 * ( y - minY ) * invSize; x = ( x | ( x << 8 ) ) & 0x00FF00FF; x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; x = ( x | ( x << 2 ) ) & 0x33333333; x = ( x | ( x << 1 ) ) & 0x55555555; y = ( y | ( y << 8 ) ) & 0x00FF00FF; y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; y = ( y | ( y << 2 ) ) & 0x33333333; y = ( y | ( y << 1 ) ) & 0x55555555; return x | ( y << 1 ); } // find the leftmost node of a polygon ring function getLeftmost( start ) { var p = start, leftmost = start; do { if ( p.x < leftmost.x ) leftmost = p; p = p.next; } while ( p !== start ); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal( a, b ) { return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ); } // signed area of a triangle function area( p, q, r ) { return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); } // check if two points are equal function equals( p1, p2 ) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects( p1, q1, p2, q2 ) { if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) || ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true; return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 && area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon( a, b ) { var p = a; do { if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects( p, p.next, a, b ) ) { return true; } p = p.next; } while ( p !== a ); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside( a, b ) { return area( a.prev, a, a.next ) < 0 ? area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside( a, b ) { var p = a, inside = false, px = ( a.x + b.x ) / 2, py = ( a.y + b.y ) / 2; do { if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) { inside = ! inside; } p = p.next; } while ( p !== a ); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon( a, b ) { var a2 = new Node( a.i, a.x, a.y ), b2 = new Node( b.i, b.x, b.y ), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode( i, x, y, last ) { var p = new Node( i, x, y ); if ( ! last ) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode( p ) { p.next.prev = p.prev; p.prev.next = p.next; if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; } function Node( i, x, y ) { // vertice index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertice nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea( data, start, end, dim ) { var sum = 0; for ( var i = start, j = end - dim; i < end; i += dim ) { sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); j = i; } return sum; } /** * @author zz85 / http://www.lab4games.net/zz85/blog */ var ShapeUtils = { // calculate area of the contour polygon area: function ( contour ) { var n = contour.length; var a = 0.0; for ( var p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; }, isClockWise: function ( pts ) { return ShapeUtils.area( pts ) < 0; }, triangulateShape: function ( contour, holes ) { var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] var holeIndices = []; // array of hole indices var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts( contour ); addContour( vertices, contour ); // var holeIndex = contour.length; holes.forEach( removeDupEndPts ); for ( var i = 0; i < holes.length; i ++ ) { holeIndices.push( holeIndex ); holeIndex += holes[ i ].length; addContour( vertices, holes[ i ] ); } // var triangles = Earcut.triangulate( vertices, holeIndices ); // for ( var i = 0; i < triangles.length; i += 3 ) { faces.push( triangles.slice( i, i + 3 ) ); } return faces; } }; function removeDupEndPts( points ) { var l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } function addContour( vertices, contour ) { for ( var i = 0; i < contour.length; i ++ ) { vertices.push( contour[ i ].x ); vertices.push( contour[ i ].y ); } } /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline is bevel * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * * UVGenerator: // object that provides UV generator functions * * } */ // ExtrudeGeometry function ExtrudeGeometry( shapes, options ) { Geometry.call( this ); this.type = "ExtrudeGeometry"; this.parameters = { shapes: shapes, options: options }; this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); this.mergeVertices(); } ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; ExtrudeGeometry.prototype.toJSON = function () { var data = Geometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; var options = this.parameters.options; return toJSON( shapes, options, data ); }; // ExtrudeBufferGeometry function ExtrudeBufferGeometry( shapes, options ) { BufferGeometry.call( this ); this.type = "ExtrudeBufferGeometry"; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; var scope = this; var verticesArray = []; var uvArray = []; for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; addShape( shape ); } // build geometry this.addAttribute( "position", new Float32BufferAttribute( verticesArray, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvArray, 2 ) ); this.computeVertexNormals(); // functions function addShape( shape ) { var placeholder = []; // options var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; var steps = options.steps !== undefined ? options.steps : 1; var depth = options.depth !== undefined ? options.depth : 100; var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; var extrudePath = options.extrudePath; var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options if ( options.amount !== undefined ) { console.warn( "THREE.ExtrudeBufferGeometry: amount has been renamed to depth." ); depth = options.amount; } // var extrudePts, extrudeByPath = false; var splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, "splineTube", splineTube.normals.length, "steps", steps, "extrudePts", extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initialization var ahole, h, hl; // looping of holes var shapePoints = shape.extractPoints( curveSegments ); var vertices = shapePoints.shape; var holes = shapePoints.holes; var reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } } var faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return vec.clone().multiplyScalar( size ).add( pt ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt" on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt" is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing var v_prev_len = Math.sqrt( v_prev_lensq ); var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don"t normalize!, otherwise sharp corners become ugly // but prevent crazy spikes var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges var direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } var contourMovements = []; for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log("i,j,k", i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = []; for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= steps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, depth / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, depth + z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, depth + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { var start = verticesArray.length / 3; if ( bevelEnabled ) { var layer = 0; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } scope.addGroup( start, verticesArray.length / 3 - start, 0 ); } // Create faces for the z-sides of the shape function buildSideFaces() { var start = verticesArray.length / 3; var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } scope.addGroup( start, verticesArray.length / 3 - start, 1 ); } function sidewalls( contour, layeroffset ) { var j, k; i = contour.length; while ( -- i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log("b", i,j, i-1, k,vertices.length); var s = 0, sl = steps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d ); } } } function v( x, y, z ) { placeholder.push( x ); placeholder.push( y ); placeholder.push( z ); } function f3( a, b, c ) { addVertex( a ); addVertex( b ); addVertex( c ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); } function f4( a, b, c, d ) { addVertex( a ); addVertex( b ); addVertex( d ); addVertex( b ); addVertex( c ); addVertex( d ); var nextIndex = verticesArray.length / 3; var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); addUV( uvs[ 0 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 3 ] ); addUV( uvs[ 1 ] ); addUV( uvs[ 2 ] ); addUV( uvs[ 3 ] ); } function addVertex( index ) { verticesArray.push( placeholder[ index * 3 + 0 ] ); verticesArray.push( placeholder[ index * 3 + 1 ] ); verticesArray.push( placeholder[ index * 3 + 2 ] ); } function addUV( vector2 ) { uvArray.push( vector2.x ); uvArray.push( vector2.y ); } } } ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; ExtrudeBufferGeometry.prototype.toJSON = function () { var data = BufferGeometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; var options = this.parameters.options; return toJSON( shapes, options, data ); }; // var WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; return [ new Vector2( a_x, a_y ), new Vector2( b_x, b_y ), new Vector2( c_x, c_y ) ]; }, generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { var a_x = vertices[ indexA * 3 ]; var a_y = vertices[ indexA * 3 + 1 ]; var a_z = vertices[ indexA * 3 + 2 ]; var b_x = vertices[ indexB * 3 ]; var b_y = vertices[ indexB * 3 + 1 ]; var b_z = vertices[ indexB * 3 + 2 ]; var c_x = vertices[ indexC * 3 ]; var c_y = vertices[ indexC * 3 + 1 ]; var c_z = vertices[ indexC * 3 + 2 ]; var d_x = vertices[ indexD * 3 ]; var d_y = vertices[ indexD * 3 + 1 ]; var d_z = vertices[ indexD * 3 + 2 ]; if ( Math.abs( a_y - b_y ) < 0.01 ) { return [ new Vector2( a_x, 1 - a_z ), new Vector2( b_x, 1 - b_z ), new Vector2( c_x, 1 - c_z ), new Vector2( d_x, 1 - d_z ) ]; } else { return [ new Vector2( a_y, 1 - a_z ), new Vector2( b_y, 1 - b_z ), new Vector2( c_y, 1 - c_z ), new Vector2( d_y, 1 - d_z ) ]; } } }; function toJSON( shapes, options, data ) { // data.shapes = []; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } // if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); return data; } /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ * * Text = 3D Text * * parameters = { * font: , // font * * size: , // size of the text * height: , // thickness to extrude text * curveSegments: , // number of points on the curves * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into text bevel goes * bevelSize: // how far from text outline is bevel * } */ // TextGeometry function TextGeometry( text, parameters ) { Geometry.call( this ); this.type = "TextGeometry"; this.parameters = { text: text, parameters: parameters }; this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); this.mergeVertices(); } TextGeometry.prototype = Object.create( Geometry.prototype ); TextGeometry.prototype.constructor = TextGeometry; // TextBufferGeometry function TextBufferGeometry( text, parameters ) { parameters = parameters || {}; var font = parameters.font; if ( ! ( font && font.isFont ) ) { console.error( "THREE.TextGeometry: font parameter is not an instance of THREE.Font." ); return new Geometry(); } var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); // translate parameters to ExtrudeGeometry API parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; ExtrudeBufferGeometry.call( this, shapes, parameters ); this.type = "TextBufferGeometry"; } TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); TextBufferGeometry.prototype.constructor = TextBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 */ // SphereGeometry function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "SphereGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); this.mergeVertices(); } SphereGeometry.prototype = Object.create( Geometry.prototype ); SphereGeometry.prototype.constructor = SphereGeometry; // SphereBufferGeometry function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "SphereBufferGeometry"; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); phiStart = phiStart !== undefined ? phiStart : 0; phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; var thetaEnd = thetaStart + thetaLength; var ix, iy; var index = 0; var grid = []; var vertex = new Vector3(); var normal = new Vector3(); // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // generate vertices, normals and uvs for ( iy = 0; iy <= heightSegments; iy ++ ) { var verticesRow = []; var v = iy / heightSegments; for ( ix = 0; ix <= widthSegments; ix ++ ) { var u = ix / widthSegments; // vertex vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( vertex.x, vertex.y, vertex.z ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); verticesRow.push( index ++ ); } grid.push( verticesRow ); } // indices for ( iy = 0; iy < heightSegments; iy ++ ) { for ( ix = 0; ix < widthSegments; ix ++ ) { var a = grid[ iy ][ ix + 1 ]; var b = grid[ iy ][ ix ]; var c = grid[ iy + 1 ][ ix ]; var d = grid[ iy + 1 ][ ix + 1 ]; if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; /** * @author Kaleb Murphy * @author Mugen87 / https://github.com/Mugen87 */ // RingGeometry function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "RingGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); this.mergeVertices(); } RingGeometry.prototype = Object.create( Geometry.prototype ); RingGeometry.prototype.constructor = RingGeometry; // RingBufferGeometry function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "RingBufferGeometry"; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; innerRadius = innerRadius || 0.5; outerRadius = outerRadius || 1; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // some helper variables var segment; var radius = innerRadius; var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); var vertex = new Vector3(); var uv = new Vector2(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= phiSegments; j ++ ) { for ( i = 0; i <= thetaSegments; i ++ ) { // values are generate from the inside of the ring to the outside segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // increase the radius for next row of vertices radius += radiusStep; } // indices for ( j = 0; j < phiSegments; j ++ ) { var thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( i = 0; i < thetaSegments; i ++ ) { segment = i + thetaSegmentLevel; var a = segment; var b = segment + thetaSegments + 1; var c = segment + thetaSegments + 2; var d = segment + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); RingBufferGeometry.prototype.constructor = RingBufferGeometry; /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 * @author bhouston / http://clara.io * @author Mugen87 / https://github.com/Mugen87 */ // LatheGeometry function LatheGeometry( points, segments, phiStart, phiLength ) { Geometry.call( this ); this.type = "LatheGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); this.mergeVertices(); } LatheGeometry.prototype = Object.create( Geometry.prototype ); LatheGeometry.prototype.constructor = LatheGeometry; // LatheBufferGeometry function LatheBufferGeometry( points, segments, phiStart, phiLength ) { BufferGeometry.call( this ); this.type = "LatheBufferGeometry"; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ) || 12; phiStart = phiStart || 0; phiLength = phiLength || Math.PI * 2; // clamp phiLength so it"s in range of [ 0, 2PI ] phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); // buffers var indices = []; var vertices = []; var uvs = []; // helper variables var base; var inverseSegments = 1.0 / segments; var vertex = new Vector3(); var uv = new Vector2(); var i, j; // generate vertices and uvs for ( i = 0; i <= segments; i ++ ) { var phi = phiStart + i * inverseSegments * phiLength; var sin = Math.sin( phi ); var cos = Math.cos( phi ); for ( j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); } } // indices for ( i = 0; i < segments; i ++ ) { for ( j = 0; j < ( points.length - 1 ); j ++ ) { base = j + i * points.length; var a = base; var b = base + points.length; var c = base + points.length + 1; var d = base + 1; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // generate normals this.computeVertexNormals(); // if the geometry is closed, we need to average the normals along the seam. // because the corresponding vertices are identical (but still have different UVs). if ( phiLength === Math.PI * 2 ) { var normals = this.attributes.normal.array; var n1 = new Vector3(); var n2 = new Vector3(); var n = new Vector3(); // this is the buffer offset for the last line of vertices base = segments * points.length * 3; for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { // select the normal of the vertex in the first line n1.x = normals[ j + 0 ]; n1.y = normals[ j + 1 ]; n1.z = normals[ j + 2 ]; // select the normal of the vertex in the last line n2.x = normals[ base + j + 0 ]; n2.y = normals[ base + j + 1 ]; n2.z = normals[ base + j + 2 ]; // average normals n.addVectors( n1, n2 ).normalize(); // assign the new values to both normals normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; } } } LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; /** * @author jonobr1 / http://jonobr1.com * @author Mugen87 / https://github.com/Mugen87 */ // ShapeGeometry function ShapeGeometry( shapes, curveSegments ) { Geometry.call( this ); this.type = "ShapeGeometry"; if ( typeof curveSegments === "object" ) { console.warn( "THREE.ShapeGeometry: Options parameter has been removed." ); curveSegments = curveSegments.curveSegments; } this.parameters = { shapes: shapes, curveSegments: curveSegments }; this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); this.mergeVertices(); } ShapeGeometry.prototype = Object.create( Geometry.prototype ); ShapeGeometry.prototype.constructor = ShapeGeometry; ShapeGeometry.prototype.toJSON = function () { var data = Geometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON$1( shapes, data ); }; // ShapeBufferGeometry function ShapeBufferGeometry( shapes, curveSegments ) { BufferGeometry.call( this ); this.type = "ShapeBufferGeometry"; this.parameters = { shapes: shapes, curveSegments: curveSegments }; curveSegments = curveSegments || 12; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var groupStart = 0; var groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( var i = 0; i < shapes.length; i ++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { var i, l, shapeHole; var indexOffset = vertices.length / 3; var points = shape.extractPoints( curveSegments ); var shapeVertices = points.shape; var shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); // also check if holes are in the opposite direction for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } } var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { var vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // incides for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var a = face[ 0 ] + indexOffset; var b = face[ 1 ] + indexOffset; var c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; ShapeBufferGeometry.prototype.toJSON = function () { var data = BufferGeometry.prototype.toJSON.call( this ); var shapes = this.parameters.shapes; return toJSON$1( shapes, data ); }; // function toJSON$1( shapes, data ) { data.shapes = []; if ( Array.isArray( shapes ) ) { for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; data.shapes.push( shape.uuid ); } } else { data.shapes.push( shapes.uuid ); } return data; } /** * @author WestLangley / http://github.com/WestLangley * @author Mugen87 / https://github.com/Mugen87 */ function EdgesGeometry( geometry, thresholdAngle ) { BufferGeometry.call( this ); this.type = "EdgesGeometry"; this.parameters = { thresholdAngle: thresholdAngle }; thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; // buffer var vertices = []; // helper variables var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); var edge = [ 0, 0 ], edges = {}, edge1, edge2; var key, keys = [ "a", "b", "c" ]; // prepare source geometry var geometry2; if ( geometry.isBufferGeometry ) { geometry2 = new Geometry(); geometry2.fromBufferGeometry( geometry ); } else { geometry2 = geometry.clone(); } geometry2.mergeVertices(); geometry2.computeFaceNormals(); var sourceVertices = geometry2.vertices; var faces = geometry2.faces; // now create a data structure where each entry represents an edge with its adjoining faces for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0; j < 3; j ++ ) { edge1 = face[ keys[ j ] ]; edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; edge[ 0 ] = Math.min( edge1, edge2 ); edge[ 1 ] = Math.max( edge1, edge2 ); key = edge[ 0 ] + "," + edge[ 1 ]; if ( edges[ key ] === undefined ) { edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; } else { edges[ key ].face2 = i; } } } // generate vertices for ( key in edges ) { var e = edges[ key ]; // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { var vertex = sourceVertices[ e.index1 ]; vertices.push( vertex.x, vertex.y, vertex.z ); vertex = sourceVertices[ e.index2 ]; vertices.push( vertex.x, vertex.y, vertex.z ); } } // build geometry this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); } EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); EdgesGeometry.prototype.constructor = EdgesGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ // CylinderGeometry function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CylinderGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); this.mergeVertices(); } CylinderGeometry.prototype = Object.create( Geometry.prototype ); CylinderGeometry.prototype.constructor = CylinderGeometry; // CylinderBufferGeometry function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CylinderBufferGeometry"; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; var scope = this; radiusTop = radiusTop !== undefined ? radiusTop : 1; radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; height = height || 1; radialSegments = Math.floor( radialSegments ) || 8; heightSegments = Math.floor( heightSegments ) || 1; openEnded = openEnded !== undefined ? openEnded : false; thetaStart = thetaStart !== undefined ? thetaStart : 0.0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var index = 0; var indexArray = []; var halfHeight = height / 2; var groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); function generateTorso() { var x, y; var normal = new Vector3(); var vertex = new Vector3(); var groupCount = 0; // this will be used to calculate the normal var slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( y = 0; y <= heightSegments; y ++ ) { var indexRow = []; var v = y / heightSegments; // calculate the radius of the current row var radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var sinTheta = Math.sin( theta ); var cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( x = 0; x < radialSegments; x ++ ) { for ( y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices var a = indexArray[ y ][ x ]; var b = indexArray[ y + 1 ][ x ]; var c = indexArray[ y + 1 ][ x + 1 ]; var d = indexArray[ y ][ x + 1 ]; // faces indices.push( a, b, d ); indices.push( b, c, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { var x, centerIndexStart, centerIndexEnd; var uv = new Vector2(); var vertex = new Vector3(); var groupCount = 0; var radius = ( top === true ) ? radiusTop : radiusBottom; var sign = ( top === true ) ? 1 : - 1; // save the index of the first center vertex centerIndexStart = index; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, halfHeight * sign, 0 ); // normal normals.push( 0, sign, 0 ); // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var cosTheta = Math.cos( theta ); var sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( x = 0; x < radialSegments; x ++ ) { var c = centerIndexStart + x; var i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; /** * @author abelnation / http://github.com/abelnation */ // ConeGeometry function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); ConeGeometry.prototype.constructor = ConeGeometry; // ConeBufferGeometry function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = "ConeBufferGeometry"; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; /** * @author benaadams / https://twitter.com/ben_a_adams * @author Mugen87 / https://github.com/Mugen87 * @author hughes */ // CircleGeometry function CircleGeometry( radius, segments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = "CircleGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); this.mergeVertices(); } CircleGeometry.prototype = Object.create( Geometry.prototype ); CircleGeometry.prototype.constructor = CircleGeometry; // CircleBufferGeometry function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = "CircleBufferGeometry"; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 1; segments = segments !== undefined ? Math.max( 3, segments ) : 8; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; // buffers var indices = []; var vertices = []; var normals = []; var uvs = []; // helper variables var i, s; var vertex = new Vector3(); var uv = new Vector2(); // center point vertices.push( 0, 0, 0 ); normals.push( 0, 0, 1 ); uvs.push( 0.5, 0.5 ); for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { var segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.push( vertex.x, vertex.y, vertex.z ); // normal normals.push( 0, 0, 1 ); // uvs uv.x = ( vertices[ i ] / radius + 1 ) / 2; uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; uvs.push( uv.x, uv.y ); } // indices for ( i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } // build geometry this.setIndex( indices ); this.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( "normal", new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( "uv", new Float32BufferAttribute( uvs, 2 ) ); } CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; var Geometries = /*#__PURE__*/Object.freeze({ WireframeGeometry: WireframeGeometry, ParametricGeometry: ParametricGeometry, ParametricBufferGeometry: ParametricBufferGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronBufferGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronBufferGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronBufferGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronBufferGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronBufferGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeBufferGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotBufferGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusBufferGeometry, TextGeometry: TextGeometry, TextBufferGeometry: TextBufferGeometry, SphereGeometry: SphereGeometry, SphereBufferGeometry: SphereBufferGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingBufferGeometry, PlaneGeometry: PlaneGeometry, PlaneBufferGeometry: PlaneBufferGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheBufferGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeBufferGeometry, ExtrudeGeometry: ExtrudeGeometry, ExtrudeBufferGeometry: ExtrudeBufferGeometry, EdgesGeometry: EdgesGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeBufferGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderBufferGeometry, CircleGeometry: CircleGeometry, CircleBufferGeometry: CircleBufferGeometry, BoxGeometry: BoxGeometry, BoxBufferGeometry: BoxBufferGeometry }); /** * @author mrdoob / http://mrdoob.com/ * * parameters = { * color: * } */ function ShadowMaterial( parameters ) { Material.call( this ); this.type = "ShadowMaterial"; this.color = new Color( 0x000000 ); this.transparent = true; this.setValues( parameters ); } ShadowMaterial.prototype = Object.create( Material.prototype ); ShadowMaterial.prototype.constructor = ShadowMaterial; ShadowMaterial.prototype.isShadowMaterial = true; ShadowMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function RawShaderMaterial( parameters ) { ShaderMaterial.call( this, parameters ); this.type = "RawShaderMaterial"; } RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); RawShaderMaterial.prototype.constructor = RawShaderMaterial; RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshStandardMaterial( parameters ) { Material.call( this ); this.defines = { "STANDARD": "" }; this.type = "MeshStandardMaterial"; this.color = new Color( 0xffffff ); // diffuse this.roughness = 0.5; this.metalness = 0.5; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshStandardMaterial.prototype = Object.create( Material.prototype ); MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; MeshStandardMaterial.prototype.isMeshStandardMaterial = true; MeshStandardMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.defines = { "STANDARD": "" }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * reflectivity: * } */ function MeshPhysicalMaterial( parameters ) { MeshStandardMaterial.call( this ); this.defines = { "PHYSICAL": "" }; this.type = "MeshPhysicalMaterial"; this.reflectivity = 0.5; // maps to F0 = 0.04 this.clearCoat = 0.0; this.clearCoatRoughness = 0.0; this.setValues( parameters ); } MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; MeshPhysicalMaterial.prototype.copy = function ( source ) { MeshStandardMaterial.prototype.copy.call( this, source ); this.defines = { "PHYSICAL": "" }; this.reflectivity = source.reflectivity; this.clearCoat = source.clearCoat; this.clearCoatRoughness = source.clearCoatRoughness; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshPhongMaterial( parameters ) { Material.call( this ); this.type = "MeshPhongMaterial"; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshPhongMaterial.prototype = Object.create( Material.prototype ); MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; MeshPhongMaterial.prototype.isMeshPhongMaterial = true; MeshPhongMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author takahirox / http://github.com/takahirox * * parameters = { * gradientMap: new THREE.Texture( ) * } */ function MeshToonMaterial( parameters ) { MeshPhongMaterial.call( this ); this.defines = { "TOON": "" }; this.type = "MeshToonMaterial"; this.gradientMap = null; this.setValues( parameters ); } MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); MeshToonMaterial.prototype.constructor = MeshToonMaterial; MeshToonMaterial.prototype.isMeshToonMaterial = true; MeshToonMaterial.prototype.copy = function ( source ) { MeshPhongMaterial.prototype.copy.call( this, source ); this.gradientMap = source.gradientMap; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshNormalMaterial( parameters ) { Material.call( this ); this.type = "MeshNormalMaterial"; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshNormalMaterial.prototype = Object.create( Material.prototype ); MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; MeshNormalMaterial.prototype.isMeshNormalMaterial = true; MeshNormalMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshLambertMaterial( parameters ) { Material.call( this ); this.type = "MeshLambertMaterial"; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = "round"; this.wireframeLinejoin = "round"; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshLambertMaterial.prototype = Object.create( Material.prototype ); MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; MeshLambertMaterial.prototype.isMeshLambertMaterial = true; MeshLambertMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ function LineDashedMaterial( parameters ) { LineBasicMaterial.call( this ); this.type = "LineDashedMaterial"; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues( parameters ); } LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); LineDashedMaterial.prototype.constructor = LineDashedMaterial; LineDashedMaterial.prototype.isLineDashedMaterial = true; LineDashedMaterial.prototype.copy = function ( source ) { LineBasicMaterial.prototype.copy.call( this, source ); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; }; var Materials = /*#__PURE__*/Object.freeze({ ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); /** * @author mrdoob / http://mrdoob.com/ */ var Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Adding key:", key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( "THREE.Cache", "Checking key:", key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; /** * @author mrdoob / http://mrdoob.com/ */ function LoadingManager( onLoad, onProgress, onError ) { var scope = this; var isLoading = false; var itemsLoaded = 0; var itemsTotal = 0; var urlModifier = undefined; this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; this.resolveURL = function ( url ) { if ( urlModifier ) { return urlModifier( url ); } return url; }; this.setURLModifier = function ( transform ) { urlModifier = transform; return this; }; } var DefaultLoadingManager = new LoadingManager(); /** * @author mrdoob / http://mrdoob.com/ */ var loading = {}; function FileLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FileLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } // Check if request is duplicate if ( loading[ url ] !== undefined ) { loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); return; } // Check for data: URI var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; var dataUriRegexResult = url.match( dataUriRegex ); // Safari can not handle Data URIs through XMLHttpRequest so process manually if ( dataUriRegexResult ) { var mimeType = dataUriRegexResult[ 1 ]; var isBase64 = !! dataUriRegexResult[ 2 ]; var data = dataUriRegexResult[ 3 ]; data = window.decodeURIComponent( data ); if ( isBase64 ) data = window.atob( data ); try { var response; var responseType = ( this.responseType || "" ).toLowerCase(); switch ( responseType ) { case "arraybuffer": case "blob": var view = new Uint8Array( data.length ); for ( var i = 0; i < data.length; i ++ ) { view[ i ] = data.charCodeAt( i ); } if ( responseType === "blob" ) { response = new Blob( [ view.buffer ], { type: mimeType } ); } else { response = view.buffer; } break; case "document": var parser = new DOMParser(); response = parser.parseFromString( data, mimeType ); break; case "json": response = JSON.parse( data ); break; default: // "text" or other response = data; break; } // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); }, 0 ); } catch ( error ) { // Wait for next browser tick like standard XMLHttpRequest event dispatching does window.setTimeout( function () { if ( onError ) onError( error ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, 0 ); } } else { // Initialise array for duplicate requests loading[ url ] = []; loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError } ); var request = new XMLHttpRequest(); request.open( "GET", url, true ); request.addEventListener( "load", function ( event ) { var response = this.response; Cache.add( url, response ); var callbacks = loading[ url ]; delete loading[ url ]; if ( this.status === 200 || this.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. "file://" or "data://". Handle as success. if ( this.status === 0 ) console.warn( "THREE.FileLoader: HTTP Status 0 received." ); for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onLoad ) callback.onLoad( response ); } scope.manager.itemEnd( url ); } else { for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); } }, false ); request.addEventListener( "progress", function ( event ) { var callbacks = loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onProgress ) callback.onProgress( event ); } }, false ); request.addEventListener( "error", function ( event ) { var callbacks = loading[ url ]; delete loading[ url ]; for ( var i = 0, il = callbacks.length; i < il; i ++ ) { var callback = callbacks[ i ]; if ( callback.onError ) callback.onError( event ); } scope.manager.itemEnd( url ); scope.manager.itemError( url ); }, false ); if ( this.responseType !== undefined ) request.responseType = this.responseType; if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : "text/plain" ); for ( var header in this.requestHeader ) { request.setRequestHeader( header, this.requestHeader[ header ] ); } request.send( null ); } scope.manager.itemStart( url ); return request; }, setPath: function ( value ) { this.path = value; return this; }, setResponseType: function ( value ) { this.responseType = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setMimeType: function ( value ) { this.mimeType = value; return this; }, setRequestHeader: function ( value ) { this.requestHeader = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * * Abstract Base class to block based textures loader (dds, pvr, ...) */ function CompressedTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( CompressedTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var images = []; var texture = new CompressedTexture(); texture.image = images; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( "arraybuffer" ); function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { var texDatas = scope._parser( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { var loaded = 0; for ( var i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { var texDatas = scope._parser( buffer, true ); if ( texDatas.isCubemap ) { var faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( var f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps: [] }; for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author Nikos M. / https://github.com/foo123/ * * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ function DataTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( DataTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texture = new DataTexture(); var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var texData = scope._parser( buffer ); if ( ! texData ) return; if ( undefined !== texData.image ) { texture.image = texData.image; } else if ( undefined !== texData.data ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; if ( undefined !== texData.format ) { texture.format = texData.format; } if ( undefined !== texData.type ) { texture.type = texData.type; } if ( undefined !== texData.mipmaps ) { texture.mipmaps = texData.mipmaps; } if ( 1 === texData.mipmapCount ) { texture.minFilter = LinearFilter; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ImageLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( ImageLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } var image = document.createElementNS( "http://www.w3.org/1999/xhtml", "img" ); function onImageLoad() { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); Cache.add( url, this ); if ( onLoad ) onLoad( this ); scope.manager.itemEnd( url ); } function onImageError( event ) { image.removeEventListener( "load", onImageLoad, false ); image.removeEventListener( "error", onImageError, false ); if ( onError ) onError( event ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); } image.addEventListener( "load", onImageLoad, false ); image.addEventListener( "error", onImageError, false ); if ( url.substr( 0, 5 ) !== "data:" ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart( url ); image.src = url; return image; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function CubeTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( CubeTextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( urls, onLoad, onProgress, onError ) { var texture = new CubeTexture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); var loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( var i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function TextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( TextureLoader.prototype, { crossOrigin: "Anonymous", load: function ( url, onLoad, onProgress, onError ) { var texture = new Texture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); loader.load( url, function ( image ) { texture.image = image; // JPEGs can"t have an alpha channel, so memory can be saved by storing them as RGB. var isJPEG = url.search( /.(jpg|jpeg)$/ ) > 0 || url.search( /^data:image/jpeg/ ) === 0; texture.format = isJPEG ? RGBFormat : RGBAFormat; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Extensible curve object * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t ) * .getPointAt( u, optionalTarget ), .getTangentAt( u ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ /************************************************************** * Abstract Curve base class **************************************************************/ function Curve() { this.type = "Curve"; this.arcLengthDivisions = 200; } Object.assign( Curve.prototype, { // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint: function ( /* t, optionalTarget */ ) { console.warn( "THREE.Curve: .getPoint() not implemented." ); return null; }, // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt: function ( u, optionalTarget ) { var t = this.getUtoTmapping( u ); return this.getPoint( t, optionalTarget ); }, // Get sequence of points using getPoint( t ) getPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; }, // Get sequence of points using getPointAt( u ) getSpacedPoints: function ( divisions ) { if ( divisions === undefined ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; }, // Get total curve arc length getLength: function () { var lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; }, // Get list of cumulative segment lengths getLengths: function ( divisions ) { if ( divisions === undefined ) divisions = this.arcLengthDivisions; if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { return this.cacheArcLengths; } this.needsUpdate = false; var cache = []; var current, last = this.getPoint( 0 ); var p, sum = 0; cache.push( 0 ); for ( p = 1; p <= divisions; p ++ ) { current = this.getPoint( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. }, updateArcLengths: function () { this.needsUpdate = true; this.getLengths(); }, // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping: function ( u, distance ) { var arcLengths = this.getLengths(); var i = 0, il = arcLengths.length; var targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } // binary search for the index with largest value smaller than target u distance var low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn"t really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; if ( arcLengths[ i ] === targetArcLength ) { return i / ( il - 1 ); } // we could get finer grain at lengths, or use simple interpolation between two points var lengthBefore = arcLengths[ i ]; var lengthAfter = arcLengths[ i + 1 ]; var segmentLength = lengthAfter - lengthBefore; // determine where we are between the "before" and "after" points var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t var t = ( i + segmentFraction ) / ( il - 1 ); return t; }, // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent: function ( t ) { var delta = 0.0001; var t1 = t - delta; var t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; var pt1 = this.getPoint( t1 ); var pt2 = this.getPoint( t2 ); var vec = pt2.clone().sub( pt1 ); return vec.normalize(); }, getTangentAt: function ( u ) { var t = this.getUtoTmapping( u ); return this.getTangent( t ); }, computeFrenetFrames: function ( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf var normal = new Vector3(); var tangents = []; var normals = []; var binormals = []; var vec = new Vector3(); var mat = new Matrix4(); var i, u, theta; // compute the tangent vectors for each segment on the curve for ( i = 0; i <= segments; i ++ ) { u = i / segments; tangents[ i ] = this.getTangentAt( u ); tangents[ i ].normalize(); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); var min = Number.MAX_VALUE; var tx = Math.abs( tangents[ 0 ].x ); var ty = Math.abs( tangents[ 0 ].y ); var tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.arcLengthDivisions = source.arcLengthDivisions; return this; }, toJSON: function () { var data = { metadata: { version: 4.5, type: "Curve", generator: "Curve.toJSON" } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; }, fromJSON: function ( json ) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } ); function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { Curve.call( this ); this.type = "EllipseCurve"; this.aX = aX || 0; this.aY = aY || 0; this.xRadius = xRadius || 1; this.yRadius = yRadius || 1; this.aStartAngle = aStartAngle || 0; this.aEndAngle = aEndAngle || 2 * Math.PI; this.aClockwise = aClockwise || false; this.aRotation = aRotation || 0; } EllipseCurve.prototype = Object.create( Curve.prototype ); EllipseCurve.prototype.constructor = EllipseCurve; EllipseCurve.prototype.isEllipseCurve = true; EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var twoPi = Math.PI * 2; var deltaAngle = this.aEndAngle - this.aStartAngle; var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } var angle = this.aStartAngle + t * deltaAngle; var x = this.aX + this.xRadius * Math.cos( angle ); var y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { var cos = Math.cos( this.aRotation ); var sin = Math.sin( this.aRotation ); var tx = x - this.aX; var ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set( x, y ); }; EllipseCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; }; EllipseCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; }; EllipseCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; }; function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); this.type = "ArcCurve"; } ArcCurve.prototype = Object.create( EllipseCurve.prototype ); ArcCurve.prototype.constructor = ArcCurve; ArcCurve.prototype.isArcCurve = true; /** * @author zz85 https://github.com/zz85 * * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { var c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p"(0) = t0, p"(1) = t1. */ function init( x0, x1, t0, t1 ) { c0 = x0; c1 = t0; c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function ( x0, x1, x2, x3, tension ) { init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }, initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init( x1, x2, t1, t2 ); }, calc: function ( t ) { var t2 = t * t; var t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // var tmp = new Vector3(); var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); function CatmullRomCurve3( points, closed, curveType, tension ) { Curve.call( this ); this.type = "CatmullRomCurve3"; this.points = points || []; this.closed = closed || false; this.curveType = curveType || "centripetal"; this.tension = tension || 0.5; } CatmullRomCurve3.prototype = Object.create( Curve.prototype ); CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var points = this.points; var l = points.length; var p = ( l - ( this.closed ? 0 : 1 ) ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } var p0, p1, p2, p3; // 4 points if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } p1 = points[ intPoint % l ]; p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.curveType === "centripetal" || this.curveType === "chordal" ) { // init Centripetal / Chordal Catmull-Rom var pow = this.curveType === "chordal" ? 0.5 : 0.25; var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.curveType === "catmullrom" ) { px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); } point.set( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return point; }; CatmullRomCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; }; CatmullRomCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; }; CatmullRomCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector3().fromArray( point ) ); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Bezier Curves formulas obtained from * http://en.wikipedia.org/wiki/Bézier_curve */ function CatmullRom( t, p0, p1, p2, p3 ) { var v0 = ( p2 - p0 ) * 0.5; var v1 = ( p3 - p1 ) * 0.5; var t2 = t * t; var t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } // function QuadraticBezierP0( t, p ) { var k = 1 - t; return k * k * p; } function QuadraticBezierP1( t, p ) { return 2 * ( 1 - t ) * t * p; } function QuadraticBezierP2( t, p ) { return t * t * p; } function QuadraticBezier( t, p0, p1, p2 ) { return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + QuadraticBezierP2( t, p2 ); } // function CubicBezierP0( t, p ) { var k = 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { var k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + CubicBezierP3( t, p3 ); } function CubicBezierCurve( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); this.v3 = v3 || new Vector2(); } CubicBezierCurve.prototype = Object.create( Curve.prototype ); CubicBezierCurve.prototype.constructor = CubicBezierCurve; CubicBezierCurve.prototype.isCubicBezierCurve = true; CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; }; CubicBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function CubicBezierCurve3( v0, v1, v2, v3 ) { Curve.call( this ); this.type = "CubicBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); this.v3 = v3 || new Vector3(); } CubicBezierCurve3.prototype = Object.create( Curve.prototype ); CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; }; CubicBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; }; CubicBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; }; CubicBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }; function LineCurve( v1, v2 ) { Curve.call( this ); this.type = "LineCurve"; this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } LineCurve.prototype = Object.create( Curve.prototype ); LineCurve.prototype.constructor = LineCurve; LineCurve.prototype.isLineCurve = true; LineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve.prototype.getTangent = function ( /* t */ ) { var tangent = this.v2.clone().sub( this.v1 ); return tangent.normalize(); }; LineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function LineCurve3( v1, v2 ) { Curve.call( this ); this.type = "LineCurve3"; this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } LineCurve3.prototype = Object.create( Curve.prototype ); LineCurve3.prototype.constructor = LineCurve3; LineCurve3.prototype.isLineCurve3 = true; LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); if ( t === 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }; LineCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; LineCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; LineCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve"; this.v0 = v0 || new Vector2(); this.v1 = v1 || new Vector2(); this.v2 = v2 || new Vector2(); } QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ) ); return point; }; QuadraticBezierCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function QuadraticBezierCurve3( v0, v1, v2 ) { Curve.call( this ); this.type = "QuadraticBezierCurve3"; this.v0 = v0 || new Vector3(); this.v1 = v1 || new Vector3(); this.v2 = v2 || new Vector3(); } QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector3(); var v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set( QuadraticBezier( t, v0.x, v1.x, v2.x ), QuadraticBezier( t, v0.y, v1.y, v2.y ), QuadraticBezier( t, v0.z, v1.z, v2.z ) ); return point; }; QuadraticBezierCurve3.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }; QuadraticBezierCurve3.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; }; QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); return this; }; function SplineCurve( points /* array of Vector2 */ ) { Curve.call( this ); this.type = "SplineCurve"; this.points = points || []; } SplineCurve.prototype = Object.create( Curve.prototype ); SplineCurve.prototype.constructor = SplineCurve; SplineCurve.prototype.isSplineCurve = true; SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { var point = optionalTarget || new Vector2(); var points = this.points; var p = ( points.length - 1 ) * t; var intPoint = Math.floor( p ); var weight = p - intPoint; var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; var p1 = points[ intPoint ]; var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; point.set( CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) ); return point; }; SplineCurve.prototype.copy = function ( source ) { Curve.prototype.copy.call( this, source ); this.points = []; for ( var i = 0, l = source.points.length; i < l; i ++ ) { var point = source.points[ i ]; this.points.push( point.clone() ); } return this; }; SplineCurve.prototype.toJSON = function () { var data = Curve.prototype.toJSON.call( this ); data.points = []; for ( var i = 0, l = this.points.length; i < l; i ++ ) { var point = this.points[ i ]; data.points.push( point.toArray() ); } return data; }; SplineCurve.prototype.fromJSON = function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.points = []; for ( var i = 0, l = json.points.length; i < l; i ++ ) { var point = json.points[ i ]; this.points.push( new Vector2().fromArray( point ) ); } return this; }; var Curves = /*#__PURE__*/Object.freeze({ ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /** * @author zz85 / http://www.lab4games.net/zz85/blog * **/ /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ function CurvePath() { Curve.call( this ); this.type = "CurvePath"; this.curves = []; this.autoClose = false; // Automatically closes the path } CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { constructor: CurvePath, add: function ( curve ) { this.curves.push( curve ); }, closePath: function () { // Add a line curve if start and end of lines are not connected var startPoint = this.curves[ 0 ].getPoint( 0 ); var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } }, // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t") getPoint: function ( t ) { var d = t * this.getLength(); var curveLengths = this.getCurveLengths(); var i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { var diff = curveLengths[ i ] - d; var curve = this.curves[ i ]; var segmentLength = curve.getLength(); var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; }, copy: function ( source ) { Curve.prototype.copy.call( this, source ); this.curves = []; for ( var i = 0, l = source.curves.length; i < l; i ++ ) { var curve = source.curves[ i ]; this.curves.push( curve.clone() ); } this.autoClose = source.autoClose; return this; }, toJSON: function () { var data = Curve.prototype.toJSON.call( this ); data.autoClose = this.autoClose; data.curves = []; for ( var i = 0, l = this.curves.length; i < l; i ++ ) { var curve = this.curves[ i ]; data.curves.push( curve.toJSON() ); } return data; }, fromJSON: function ( json ) { Curve.prototype.fromJSON.call( this, json ); this.autoClose = json.autoClose; this.curves = []; for ( var i = 0, l = json.curves.length; i < l; i ++ ) { var curve = json.curves[ i ]; this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Creates free form 2d path using series of points, lines or curves. **/ function Path( points ) { CurvePath.call( this ); this.type = "Path"; this.currentPoint = new Vector2(); if ( points ) { this.setFromPoints( points ); } } Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { constructor: Path, setFromPoints: function ( points ) { this.moveTo( points[ 0 ].x, points[ 0 ].y ); for ( var i = 1, l = points.length; i < l; i ++ ) { this.lineTo( points[ i ].x, points[ i ].y ); } }, moveTo: function ( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? }, lineTo: function ( x, y ) { var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { var curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { var curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, splineThru: function ( pts /*Array of Vector*/ ) { var npts = [ this.currentPoint.clone() ].concat( pts ); var curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); }, arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); }, absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }, ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); }, absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join var firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); var lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); }, copy: function ( source ) { CurvePath.prototype.copy.call( this, source ); this.currentPoint.copy( source.currentPoint ); return this; }, toJSON: function () { var data = CurvePath.prototype.toJSON.call( this ); data.currentPoint = this.currentPoint.toArray(); return data; }, fromJSON: function ( json ) { CurvePath.prototype.fromJSON.call( this, json ); this.currentPoint.fromArray( json.currentPoint ); return this; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Defines a 2d shape plane using paths. **/ // STEP 1 Create a path. // STEP 2 Turn path into shape. // STEP 3 ExtrudeGeometry takes in Shape/Shapes // STEP 3a - Extract points from each shape, turn to vertices // STEP 3b - Triangulate each shape, add faces. function Shape( points ) { Path.call( this, points ); this.uuid = _Math.generateUUID(); this.type = "Shape"; this.holes = []; } Shape.prototype = Object.assign( Object.create( Path.prototype ), { constructor: Shape, getPointsHoles: function ( divisions ) { var holesPts = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; }, // get points of shape and holes (keypoints based on segments parameter) extractPoints: function ( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; }, copy: function ( source ) { Path.prototype.copy.call( this, source ); this.holes = []; for ( var i = 0, l = source.holes.length; i < l; i ++ ) { var hole = source.holes[ i ]; this.holes.push( hole.clone() ); } return this; }, toJSON: function () { var data = Path.prototype.toJSON.call( this ); data.uuid = this.uuid; data.holes = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { var hole = this.holes[ i ]; data.holes.push( hole.toJSON() ); } return data; }, fromJSON: function ( json ) { Path.prototype.fromJSON.call( this, json ); this.uuid = json.uuid; this.holes = []; for ( var i = 0, l = json.holes.length; i < l; i ++ ) { var hole = json.holes[ i ]; this.holes.push( new Path().fromJSON( hole ) ); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Light( color, intensity ) { Object3D.call( this ); this.type = "Light"; this.color = new Color( color ); this.intensity = intensity !== undefined ? intensity : 1; this.receiveShadow = undefined; } Light.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Light, isLight: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.color.copy( source.color ); this.intensity = source.intensity; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function HemisphereLight( skyColor, groundColor, intensity ) { Light.call( this, skyColor, intensity ); this.type = "HemisphereLight"; this.castShadow = undefined; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: HemisphereLight, isHemisphereLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.groundColor.copy( source.groundColor ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LightShadow( camera ) { this.camera = camera; this.bias = 0; this.radius = 1; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.matrix = new Matrix4(); } Object.assign( LightShadow.prototype, { copy: function ( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; }, clone: function () { return new this.constructor().copy( this ); }, toJSON: function () { var object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function SpotLightShadow() { LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); } SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: SpotLightShadow, isSpotLightShadow: true, update: function ( light ) { var camera = this.camera; var fov = _Math.RAD2DEG * 2 * light.angle; var aspect = this.mapSize.width / this.mapSize.height; var far = light.distance || camera.far; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function SpotLight( color, intensity, distance, angle, penumbra, decay ) { Light.call( this, color, intensity ); this.type = "SpotLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf return this.intensity * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf this.intensity = power / Math.PI; } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: SpotLight, isSpotLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PointLight( color, intensity, distance, decay ) { Light.call( this, color, intensity ); this.type = "PointLight"; Object.defineProperty( this, "power", { get: function () { // intensity = power per solid angle. // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf return this.intensity * 4 * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf this.intensity = power / ( 4 * Math.PI ); } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); } PointLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: PointLight, isPointLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function DirectionalLightShadow( ) { LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: DirectionalLightShadow } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function DirectionalLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "DirectionalLight"; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: DirectionalLight, isDirectionalLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function AmbientLight( color, intensity ) { Light.call( this, color, intensity ); this.type = "AmbientLight"; this.castShadow = undefined; } AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: AmbientLight, isAmbientLight: true } ); /** * @author abelnation / http://github.com/abelnation */ function RectAreaLight( color, intensity, width, height ) { Light.call( this, color, intensity ); this.type = "RectAreaLight"; this.width = ( width !== undefined ) ? width : 10; this.height = ( height !== undefined ) ? height : 10; } RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: RectAreaLight, isRectAreaLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.width = source.width; this.height = source.height; return this; }, toJSON: function ( meta ) { var data = Light.prototype.toJSON.call( this, meta ); data.object.width = this.width; data.object.height = this.height; return data; } } ); /** * * A Track that interpolates Strings * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function StringKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: StringKeyframeTrack, ValueTypeName: "string", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined } ); /** * * A Track of Boolean keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function BooleanKeyframeTrack( name, times, values ) { KeyframeTrack.call( this, name, times, values ); } BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: BooleanKeyframeTrack, ValueTypeName: "bool", ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined // Note: Actually this track could have a optimized / compressed // representation of a single value and a custom interpolant that // computes "firstValue ^ isOdd( index )". } ); /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * * @author tschw */ function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; } Object.assign( Interpolant.prototype, { evaluate: function ( t ) { var pp = this.parameterPositions, i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { var right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( var giveUpAt = i1 + 2; ; ) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t, t0 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? var t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( var giveUpAt = i1 - 2; ; ) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { var mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t0, t ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); }, settings: null, // optional, subclass-specific settings structure // Note: The indirection allows central control of many interpolants. // --- Protected interface DefaultSettings_: {}, getSettings_: function () { return this.settings || this.DefaultSettings_; }, copySampleValue_: function ( index ) { // copies a sample value to the result buffer var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; }, // Template methods for derived classes: interpolate_: function ( /* i1, t0, t, t1 */ ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer }, intervalChanged_: function ( /* i1, t0, t1 */ ) { // empty } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( Interpolant.prototype, { //( 0, t, t0 ), returns this.resultBuffer beforeStart_: Interpolant.prototype.copySampleValue_, //( N-1, tN-1, t ), returns this.resultBuffer afterEnd_: Interpolant.prototype.copySampleValue_, } ); /** * Spherical linear unit quaternion interpolant. * * @author tschw */ function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: QuaternionLinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = i1 * stride, alpha = ( t - t0 ) / ( t1 - t0 ); for ( var end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } ); /** * * A Track of quaternion keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function QuaternionKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: QuaternionKeyframeTrack, ValueTypeName: "quaternion", // ValueBufferType is inherited DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodLinear: function ( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: undefined // not yet implemented } ); /** * * A Track of keyframe values that represent color. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function ColorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: ColorKeyframeTrack, ValueTypeName: "color" // ValueBufferType is inherited // DefaultInterpolation is inherited // Note: Very basic implementation and nothing special yet. // However, this is the place for color space parameterization. } ); /** * * A Track of numeric keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function NumberKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: NumberKeyframeTrack, ValueTypeName: "number" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. * * @author tschw */ function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = - 0; this._offsetPrev = - 0; this._weightNext = - 0; this._offsetNext = - 0; } CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: CubicInterpolant, DefaultSettings_: { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }, intervalChanged_: function ( i1, t0, t1 ) { var pp = this.parameterPositions, iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f"(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f""(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f"(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f""(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } var halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; }, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials var sP = - wP * ppp + 2 * wP * pp - wP * p; var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; var sN = wN * ppp - wN * pp; // combine data linearly for ( var i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } ); /** * @author tschw */ function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: LinearInterpolant, interpolate_: function ( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } ); /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. * * @author tschw */ function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: DiscreteInterpolant, interpolate_: function ( i1 /*, t0, t, t1 */ ) { return this.copySampleValue_( i1 - 1 ); } } ); /** * @author tschw * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ var AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function ( array, from, to ) { if ( AnimationUtils.isTypedArray( array ) ) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } return array.slice( from, to ); }, // converts an array to a specific type convertArray: function ( array, type, forceClone ) { if ( ! array || // let "undefined" and "null" pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === "number" ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array }, isTypedArray: function ( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); }, // returns an array by which times and values can be sorted getKeyframeOrder: function ( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } var n = times.length; var result = new Array( n ); for ( var i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; }, // uses the array previously returned by "getKeyframeOrder" to sort data sortedArray: function ( values, stride, order ) { var nValues = values.length; var result = new values.constructor( nValues ); for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { var srcOffset = order[ i ] * stride; for ( var j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { var i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data var value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } }; /** * * A timed sequence of keyframes for a specific property. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function KeyframeTrack( name, times, values, interpolation ) { if ( name === undefined ) throw new Error( "THREE.KeyframeTrack: track name is undefined" ); if ( times === undefined || times.length === 0 ) throw new Error( "THREE.KeyframeTrack: no keyframes in track named " + name ); this.name = name; this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); this.validate(); this.optimize(); } // Static methods: Object.assign( KeyframeTrack, { // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): parse: function ( json ) { if ( json.type === undefined ) { throw new Error( "THREE.KeyframeTrack: track type undefined, can not parse" ); } var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { var times = [], values = []; AnimationUtils.flattenJSON( json.keys, times, values, "value" ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } }, toJSON: function ( track ) { var trackType = track.constructor; var json; // derived classes can define a static toJSON method if ( trackType.toJSON !== undefined ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { "name": track.name, "times": AnimationUtils.convertArray( track.times, Array ), "values": AnimationUtils.convertArray( track.values, Array ) }; var interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; }, _getTrackTypeForValueTypeName: function ( typeName ) { switch ( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "THREE.KeyframeTrack: Unsupported typeName: " + typeName ); } } ); Object.assign( KeyframeTrack.prototype, { constructor: KeyframeTrack, TimeBufferType: Float32Array, ValueBufferType: Float32Array, DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodDiscrete: function ( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodLinear: function ( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: function ( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); }, setInterpolation: function ( interpolation ) { var factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { var message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( "THREE.KeyframeTrack:", message ); return; } this.createInterpolant = factoryMethod; }, getInterpolation: function () { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } }, getValueSize: function () { return this.values.length / this.times.length; }, // move all keyframes either forwards or backwards in time shift: function ( timeOffset ) { if ( timeOffset !== 0.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; }, // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale: function ( timeScale ) { if ( timeScale !== 1.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; }, // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim: function ( startTime, endTime ) { var times = this.times, nKeys = times.length, from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) { ++ from; } while ( to !== - 1 && times[ to ] > endTime ) { -- to; } ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; var stride = this.getValueSize(); this.times = AnimationUtils.arraySlice( times, from, to ); this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); } return this; }, // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate: function () { var valid = true; var valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "THREE.KeyframeTrack: Invalid value size in track.", this ); valid = false; } var times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "THREE.KeyframeTrack: Track is empty.", this ); valid = false; } var prevTime = null; for ( var i = 0; i !== nKeys; i ++ ) { var currTime = times[ i ]; if ( typeof currTime === "number" && isNaN( currTime ) ) { console.error( "THREE.KeyframeTrack: Time is not a valid number.", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "THREE.KeyframeTrack: Out of order keys.", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( AnimationUtils.isTypedArray( values ) ) { for ( var i = 0, n = values.length; i !== n; ++ i ) { var value = values[ i ]; if ( isNaN( value ) ) { console.error( "THREE.KeyframeTrack: Value is not a valid number.", this, i, value ); valid = false; break; } } } } return valid; }, // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize: function () { var times = this.times, values = this.values, stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, writeIndex = 1, lastIndex = times.length - 1; for ( var i = 1; i < lastIndex; ++ i ) { var keep = false; var time = times[ i ]; var timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors var offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( var j = 0; j !== stride; ++ j ) { var value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else { keep = true; } } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; var readOffset = i * stride, writeOffset = writeIndex * stride; for ( var j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); } return this; } } ); /** * * A Track of vectored keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function VectorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrack.call( this, name, times, values, interpolation ); } VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { constructor: VectorKeyframeTrack, ValueTypeName: "vector" // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * * Reusable set of Tracks that represent an animation. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ function AnimationClip( name, duration, tracks ) { this.name = name; this.tracks = tracks; this.duration = ( duration !== undefined ) ? duration : - 1; this.uuid = _Math.generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } this.optimize(); } Object.assign( AnimationClip, { parse: function ( json ) { var tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); } return new AnimationClip( json.name, json.duration, tracks ); }, toJSON: function ( clip ) { var tracks = [], clipTracks = clip.tracks; var json = { "name": clip.name, "duration": clip.duration, "tracks": tracks, "uuid": clip.uuid }; for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; }, CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { var numMorphTargets = morphTargetSequence.length; var tracks = []; for ( var i = 0; i < numMorphTargets; i ++ ) { var times = []; var values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); var order = AnimationUtils.getKeyframeOrder( times ); times = AnimationUtils.sortedArray( times, 1, order ); values = AnimationUtils.sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluences[" + morphTargetSequence[ i ].name + "]", times, values ).scale( 1.0 / fps ) ); } return new AnimationClip( name, - 1, tracks ); }, findByName: function ( objectOrClipArray, name ) { var clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { var o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( var i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; }, CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { var animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 var pattern = /^([w-]*?)([d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = morphTargets[ i ]; var parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { var name = parts[ 1 ]; var animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } var clips = []; for ( var name in animationToMorphTargets ) { clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; }, // parse the animation.hierarchy format parseAnimation: function ( animation, bones ) { if ( ! animation ) { console.error( "THREE.AnimationClip: No animation in JSONLoader data." ); return null; } var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { var times = []; var values = []; AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; var tracks = []; var clipName = animation.name || "default"; // automatic length determination in AnimationClip. var duration = animation.length || - 1; var fps = animation.fps || 30; var hierarchyTracks = animation.hierarchy || []; for ( var h = 0; h < hierarchyTracks.length; h ++ ) { var animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets if ( animationKeys[ 0 ].morphTargets ) { // figure out all morph targets used in this track var morphTargetNames = {}; for ( var k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[ k ].morphTargets ) { for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( var morphTargetName in morphTargetNames ) { var times = []; var values = []; for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { var animationKey = animationKeys[ k ]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack( ".morphTargetInfluence[" + morphTargetName + "]", times, values ) ); } duration = morphTargetNames.length * ( fps || 1.0 ); } else { // ...assume skeletal animation var boneName = ".bones[" + bones[ h ].name + "]"; addNonemptyTrack( VectorKeyframeTrack, boneName + ".position", animationKeys, "pos", tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + ".quaternion", animationKeys, "rot", tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + ".scale", animationKeys, "scl", tracks ); } } if ( tracks.length === 0 ) { return null; } var clip = new AnimationClip( clipName, duration, tracks ); return clip; } } ); Object.assign( AnimationClip.prototype, { resetDuration: function () { var tracks = this.tracks, duration = 0; for ( var i = 0, n = tracks.length; i !== n; ++ i ) { var track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; }, trim: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; }, optimize: function () { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function MaterialLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.textures = {}; } Object.assign( MaterialLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, setTextures: function ( value ) { this.textures = value; }, parse: function ( json ) { var textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( "THREE.MaterialLoader: Undefined texture", name ); } return textures[ name ]; } var material = new Materials[ json.type ](); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.rotation !== undefined ) material.rotation = json.rotation; if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; if ( json.scale !== undefined ) material.scale = json.scale; if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; if ( json.skinning !== undefined ) material.skinning = json.skinning; if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; if ( json.dithering !== undefined ) material.dithering = json.dithering; if ( json.visible !== undefined ) material.visible = json.visible; if ( json.userData !== undefined ) material.userData = json.userData; // Deprecated if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.alphaMap !== undefined ) { material.alphaMap = getTexture( json.alphaMap ); material.transparent = true; } if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalScale !== undefined ) { var normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); return material; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferGeometryLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( BufferGeometryLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, parse: function ( json ) { var geometry = new BufferGeometry(); var index = json.data.index; if ( index !== undefined ) { var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } var attributes = json.data.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); } var groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( var i = 0, n = groups.length; i !== n; ++ i ) { var group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } var boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { var center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } return geometry; } } ); var TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, // Workaround for IE11 pre KB2929437. See #11440 Uint8ClampedArray: typeof Uint8ClampedArray !== "undefined" ? Uint8ClampedArray : Uint8Array, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; /** * @author alteredq / http://alteredqualia.com/ */ function Loader() {} Loader.Handlers = { handlers: [], add: function ( regex, loader ) { this.handlers.push( regex, loader ); }, get: function ( file ) { var handlers = this.handlers; for ( var i = 0, l = handlers.length; i < l; i += 2 ) { var regex = handlers[ i ]; var loader = handlers[ i + 1 ]; if ( regex.test( file ) ) { return loader; } } return null; } }; Object.assign( Loader.prototype, { crossOrigin: undefined, onLoadStart: function () {}, onLoadProgress: function () {}, onLoadComplete: function () {}, initMaterials: function ( materials, texturePath, crossOrigin ) { var array = []; for ( var i = 0; i < materials.length; ++ i ) { array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } return array; }, createMaterial: ( function () { var BlendingMode = { NoBlending: NoBlending, NormalBlending: NormalBlending, AdditiveBlending: AdditiveBlending, SubtractiveBlending: SubtractiveBlending, MultiplyBlending: MultiplyBlending, CustomBlending: CustomBlending }; var color = new Color(); var textureLoader = new TextureLoader(); var materialLoader = new MaterialLoader(); return function createMaterial( m, texturePath, crossOrigin ) { // convert from old material format var textures = {}; function loadTexture( path, repeat, offset, wrap, anisotropy ) { var fullPath = texturePath + path; var loader = Loader.Handlers.get( fullPath ); var texture; if ( loader !== null ) { texture = loader.load( fullPath ); } else { textureLoader.setCrossOrigin( crossOrigin ); texture = textureLoader.load( fullPath ); } if ( repeat !== undefined ) { texture.repeat.fromArray( repeat ); if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; } if ( offset !== undefined ) { texture.offset.fromArray( offset ); } if ( wrap !== undefined ) { if ( wrap[ 0 ] === "repeat" ) texture.wrapS = RepeatWrapping; if ( wrap[ 0 ] === "mirror" ) texture.wrapS = MirroredRepeatWrapping; if ( wrap[ 1 ] === "repeat" ) texture.wrapT = RepeatWrapping; if ( wrap[ 1 ] === "mirror" ) texture.wrapT = MirroredRepeatWrapping; } if ( anisotropy !== undefined ) { texture.anisotropy = anisotropy; } var uuid = _Math.generateUUID(); textures[ uuid ] = texture; return uuid; } // var json = { uuid: _Math.generateUUID(), type: "MeshLambertMaterial" }; for ( var name in m ) { var value = m[ name ]; switch ( name ) { case "DbgColor": case "DbgIndex": case "opticalDensity": case "illumination": break; case "DbgName": json.name = value; break; case "blending": json.blending = BlendingMode[ value ]; break; case "colorAmbient": case "mapAmbient": console.warn( "THREE.Loader.createMaterial:", name, "is no longer supported." ); break; case "colorDiffuse": json.color = color.fromArray( value ).getHex(); break; case "colorSpecular": json.specular = color.fromArray( value ).getHex(); break; case "colorEmissive": json.emissive = color.fromArray( value ).getHex(); break; case "specularCoef": json.shininess = value; break; case "shading": if ( value.toLowerCase() === "basic" ) json.type = "MeshBasicMaterial"; if ( value.toLowerCase() === "phong" ) json.type = "MeshPhongMaterial"; if ( value.toLowerCase() === "standard" ) json.type = "MeshStandardMaterial"; break; case "mapDiffuse": json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); break; case "mapDiffuseRepeat": case "mapDiffuseOffset": case "mapDiffuseWrap": case "mapDiffuseAnisotropy": break; case "mapEmissive": json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); break; case "mapEmissiveRepeat": case "mapEmissiveOffset": case "mapEmissiveWrap": case "mapEmissiveAnisotropy": break; case "mapLight": json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); break; case "mapLightRepeat": case "mapLightOffset": case "mapLightWrap": case "mapLightAnisotropy": break; case "mapAO": json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); break; case "mapAORepeat": case "mapAOOffset": case "mapAOWrap": case "mapAOAnisotropy": break; case "mapBump": json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); break; case "mapBumpScale": json.bumpScale = value; break; case "mapBumpRepeat": case "mapBumpOffset": case "mapBumpWrap": case "mapBumpAnisotropy": break; case "mapNormal": json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); break; case "mapNormalFactor": json.normalScale = value; break; case "mapNormalRepeat": case "mapNormalOffset": case "mapNormalWrap": case "mapNormalAnisotropy": break; case "mapSpecular": json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); break; case "mapSpecularRepeat": case "mapSpecularOffset": case "mapSpecularWrap": case "mapSpecularAnisotropy": break; case "mapMetalness": json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); break; case "mapMetalnessRepeat": case "mapMetalnessOffset": case "mapMetalnessWrap": case "mapMetalnessAnisotropy": break; case "mapRoughness": json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); break; case "mapRoughnessRepeat": case "mapRoughnessOffset": case "mapRoughnessWrap": case "mapRoughnessAnisotropy": break; case "mapAlpha": json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); break; case "mapAlphaRepeat": case "mapAlphaOffset": case "mapAlphaWrap": case "mapAlphaAnisotropy": break; case "flipSided": json.side = BackSide; break; case "doubleSided": json.side = DoubleSide; break; case "transparency": console.warn( "THREE.Loader.createMaterial: transparency has been renamed to opacity" ); json.opacity = value; break; case "depthTest": case "depthWrite": case "colorWrite": case "opacity": case "reflectivity": case "transparent": case "visible": case "wireframe": json[ name ] = value; break; case "vertexColors": if ( value === true ) json.vertexColors = VertexColors; if ( value === "face" ) json.vertexColors = FaceColors; break; default: console.error( "THREE.Loader.createMaterial: Unsupported", name, value ); break; } } if ( json.type === "MeshBasicMaterial" ) delete json.emissive; if ( json.type !== "MeshPhongMaterial" ) delete json.specular; if ( json.opacity < 1 ) json.transparent = true; materialLoader.setTextures( textures ); return materialLoader.parse( json ); }; } )() } ); /** * @author Don McCurdy / https://www.donmccurdy.com */ var LoaderUtils = { decodeText: function ( array ) { if ( typeof TextDecoder !== "undefined" ) { return new TextDecoder().decode( array ); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. var s = ""; for ( var i = 0, il = array.length; i < il; i ++ ) { // Implicitly assumes little-endian. s += String.fromCharCode( array[ i ] ); } // Merges multi-byte utf-8 characters. return decodeURIComponent( escape( s ) ); }, extractUrlBase: function ( url ) { var index = url.lastIndexOf( "/" ); if ( index === - 1 ) return "./"; return url.substr( 0, index + 1 ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function JSONLoader( manager ) { if ( typeof manager === "boolean" ) { console.warn( "THREE.JSONLoader: showStatus parameter has been removed from constructor." ); manager = undefined; } this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.withCredentials = false; } Object.assign( JSONLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : LoaderUtils.extractUrlBase( url ); var loader = new FileLoader( this.manager ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { var json = JSON.parse( text ); var metadata = json.metadata; if ( metadata !== undefined ) { var type = metadata.type; if ( type !== undefined ) { if ( type.toLowerCase() === "object" ) { console.error( "THREE.JSONLoader: " + url + " should be loaded with THREE.ObjectLoader instead." ); return; } } } var object = scope.parse( json, texturePath ); onLoad( object.geometry, object.materials ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, parse: ( function () { function parseModel( json, geometry ) { function isBitSet( value, position ) { return value & ( 1 << position ); } var i, j, fi, offset, zLength, colorIndex, normalIndex, uvIndex, materialIndex, type, isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor, vertex, face, faceA, faceB, hex, normal, uvLayer, uv, u, v, faces = json.faces, vertices = json.vertices, normals = json.normals, colors = json.colors, scale = json.scale, nUvLayers = 0; if ( json.uvs !== undefined ) { // disregard empty arrays for ( i = 0; i < json.uvs.length; i ++ ) { if ( json.uvs[ i ].length ) nUvLayers ++; } for ( i = 0; i < nUvLayers; i ++ ) { geometry.faceVertexUvs[ i ] = []; } } offset = 0; zLength = vertices.length; while ( offset < zLength ) { vertex = new Vector3(); vertex.x = vertices[ offset ++ ] * scale; vertex.y = vertices[ offset ++ ] * scale; vertex.z = vertices[ offset ++ ] * scale; geometry.vertices.push( vertex ); } offset = 0; zLength = faces.length; while ( offset < zLength ) { type = faces[ offset ++ ]; isQuad = isBitSet( type, 0 ); hasMaterial = isBitSet( type, 1 ); hasFaceVertexUv = isBitSet( type, 3 ); hasFaceNormal = isBitSet( type, 4 ); hasFaceVertexNormal = isBitSet( type, 5 ); hasFaceColor = isBitSet( type, 6 ); hasFaceVertexColor = isBitSet( type, 7 ); // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); if ( isQuad ) { faceA = new Face3(); faceA.a = faces[ offset ]; faceA.b = faces[ offset + 1 ]; faceA.c = faces[ offset + 3 ]; faceB = new Face3(); faceB.a = faces[ offset + 1 ]; faceB.b = faces[ offset + 2 ]; faceB.c = faces[ offset + 3 ]; offset += 4; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; faceA.materialIndex = materialIndex; faceB.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; geometry.faceVertexUvs[ i ][ fi + 1 ] = []; for ( j = 0; j < 4; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; faceA.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); faceB.normal.copy( faceA.normal ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 4; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); if ( i !== 2 ) faceA.vertexNormals.push( normal ); if ( i !== 0 ) faceB.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; faceA.color.setHex( hex ); faceB.color.setHex( hex ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 4; i ++ ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); } } geometry.faces.push( faceA ); geometry.faces.push( faceB ); } else { face = new Face3(); face.a = faces[ offset ++ ]; face.b = faces[ offset ++ ]; face.c = faces[ offset ++ ]; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; face.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; for ( j = 0; j < 3; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); geometry.faceVertexUvs[ i ][ fi ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; face.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 3; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); face.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; face.color.setHex( colors[ colorIndex ] ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 3; i ++ ) { colorIndex = faces[ offset ++ ]; face.vertexColors.push( new Color( colors[ colorIndex ] ) ); } } geometry.faces.push( face ); } } } function parseSkin( json, geometry ) { var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; if ( json.skinWeights ) { for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { var x = json.skinWeights[ i ]; var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; geometry.skinWeights.push( new Vector4( x, y, z, w ) ); } } if ( json.skinIndices ) { for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { var a = json.skinIndices[ i ]; var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; geometry.skinIndices.push( new Vector4( a, b, c, d ) ); } } geometry.bones = json.bones; if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { console.warn( "When skinning, number of vertices (" + geometry.vertices.length + "), skinIndices (" + geometry.skinIndices.length + "), and skinWeights (" + geometry.skinWeights.length + ") should match." ); } } function parseMorphing( json, geometry ) { var scale = json.scale; if ( json.morphTargets !== undefined ) { for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { geometry.morphTargets[ i ] = {}; geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; geometry.morphTargets[ i ].vertices = []; var dstVertices = geometry.morphTargets[ i ].vertices; var srcVertices = json.morphTargets[ i ].vertices; for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { var vertex = new Vector3(); vertex.x = srcVertices[ v ] * scale; vertex.y = srcVertices[ v + 1 ] * scale; vertex.z = srcVertices[ v + 2 ] * scale; dstVertices.push( vertex ); } } } if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { console.warn( "THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors." ); var faces = geometry.faces; var morphColors = json.morphColors[ 0 ].colors; for ( var i = 0, l = faces.length; i < l; i ++ ) { faces[ i ].color.fromArray( morphColors, i * 3 ); } } } function parseAnimations( json, geometry ) { var outputAnimations = []; // parse old style Bone/Hierarchy animations var animations = []; if ( json.animation !== undefined ) { animations.push( json.animation ); } if ( json.animations !== undefined ) { if ( json.animations.length ) { animations = animations.concat( json.animations ); } else { animations.push( json.animations ); } } for ( var i = 0; i < animations.length; i ++ ) { var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); if ( clip ) outputAnimations.push( clip ); } // parse implicit morph animations if ( geometry.morphTargets ) { // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); outputAnimations = outputAnimations.concat( morphAnimationClips ); } if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; } return function parse( json, texturePath ) { if ( json.data !== undefined ) { // Geometry 4.0 spec json = json.data; } if ( json.scale !== undefined ) { json.scale = 1.0 / json.scale; } else { json.scale = 1.0; } var geometry = new Geometry(); parseModel( json, geometry ); parseSkin( json, geometry ); parseMorphing( json, geometry ); parseAnimations( json, geometry ); geometry.computeFaceNormals(); geometry.computeBoundingSphere(); if ( json.materials === undefined || json.materials.length === 0 ) { return { geometry: geometry }; } else { var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); return { geometry: geometry, materials: materials }; } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function ObjectLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.texturePath = ""; } Object.assign( ObjectLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( this.texturePath === "" ) { this.texturePath = url.substring( 0, url.lastIndexOf( "/" ) + 1 ); } var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { var json = null; try { json = JSON.parse( text ); } catch ( error ) { if ( onError !== undefined ) onError( error ); console.error( "THREE:ObjectLoader: Can"t parse " + url + ".", error.message ); return; } var metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === "geometry" ) { console.error( "THREE.ObjectLoader: Can"t load " + url + ". Use THREE.JSONLoader instead." ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; return this; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, parse: function ( json, onLoad ) { var shapes = this.parseShape( json.shapes ); var geometries = this.parseGeometries( json.geometries, shapes ); var images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); var textures = this.parseTextures( json.textures, images ); var materials = this.parseMaterials( json.materials, textures ); var object = this.parseObject( json.object, geometries, materials ); if ( json.animations ) { object.animations = this.parseAnimations( json.animations ); } if ( json.images === undefined || json.images.length === 0 ) { if ( onLoad !== undefined ) onLoad( object ); } return object; }, parseShape: function ( json ) { var shapes = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var shape = new Shape().fromJSON( json[ i ] ); shapes[ shape.uuid ] = shape; } } return shapes; }, parseGeometries: function ( json, shapes ) { var geometries = {}; if ( json !== undefined ) { var geometryLoader = new JSONLoader(); var bufferGeometryLoader = new BufferGeometryLoader(); for ( var i = 0, l = json.length; i < l; i ++ ) { var geometry; var data = json[ i ]; switch ( data.type ) { case "PlaneGeometry": case "PlaneBufferGeometry": geometry = new Geometries[ data.type ]( data.width, data.height, data.widthSegments, data.heightSegments ); break; case "BoxGeometry": case "BoxBufferGeometry": case "CubeGeometry": // backwards compatible geometry = new Geometries[ data.type ]( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); break; case "CircleGeometry": case "CircleBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.segments, data.thetaStart, data.thetaLength ); break; case "CylinderGeometry": case "CylinderBufferGeometry": geometry = new Geometries[ data.type ]( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "ConeGeometry": case "ConeBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case "SphereGeometry": case "SphereBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); break; case "DodecahedronGeometry": case "DodecahedronBufferGeometry": case "IcosahedronGeometry": case "IcosahedronBufferGeometry": case "OctahedronGeometry": case "OctahedronBufferGeometry": case "TetrahedronGeometry": case "TetrahedronBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.detail ); break; case "RingGeometry": case "RingBufferGeometry": geometry = new Geometries[ data.type ]( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); break; case "TorusGeometry": case "TorusBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); break; case "TorusKnotGeometry": case "TorusKnotBufferGeometry": geometry = new Geometries[ data.type ]( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); break; case "LatheGeometry": case "LatheBufferGeometry": geometry = new Geometries[ data.type ]( data.points, data.segments, data.phiStart, data.phiLength ); break; case "PolyhedronGeometry": case "PolyhedronBufferGeometry": geometry = new Geometries[ data.type ]( data.vertices, data.indices, data.radius, data.details ); break; case "ShapeGeometry": case "ShapeBufferGeometry": var geometryShapes = []; for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { var shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } geometry = new Geometries[ data.type ]( geometryShapes, data.curveSegments ); break; case "ExtrudeGeometry": case "ExtrudeBufferGeometry": var geometryShapes = []; for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { var shape = shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } var extrudePath = data.options.extrudePath; if ( extrudePath !== undefined ) { data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); } geometry = new Geometries[ data.type ]( geometryShapes, data.options ); break; case "BufferGeometry": geometry = bufferGeometryLoader.parse( data ); break; case "Geometry": geometry = geometryLoader.parse( data, this.texturePath ).geometry; break; default: console.warn( "THREE.ObjectLoader: Unsupported geometry type "" + data.type + """ ); continue; } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; geometries[ data.uuid ] = geometry; } } return geometries; }, parseMaterials: function ( json, textures ) { var materials = {}; if ( json !== undefined ) { var loader = new MaterialLoader(); loader.setTextures( textures ); for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.type === "MultiMaterial" ) { // Deprecated var array = []; for ( var j = 0; j < data.materials.length; j ++ ) { array.push( loader.parse( data.materials[ j ] ) ); } materials[ data.uuid ] = array; } else { materials[ data.uuid ] = loader.parse( data ); } } } return materials; }, parseAnimations: function ( json ) { var animations = []; for ( var i = 0; i < json.length; i ++ ) { var data = json[ i ]; var clip = AnimationClip.parse( data ); if ( data.uuid !== undefined ) clip.uuid = data.uuid; animations.push( clip ); } return animations; }, parseImages: function ( json, onLoad ) { var scope = this; var images = {}; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); } if ( json !== undefined && json.length > 0 ) { var manager = new LoadingManager( onLoad ); var loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( var i = 0, l = json.length; i < l; i ++ ) { var image = json[ i ]; var path = /^(//)|([a-z]+:(//)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; images[ image.uuid ] = loadImage( path ); } } return images; }, parseTextures: function ( json, images ) { function parseConstant( value, type ) { if ( typeof value === "number" ) return value; console.warn( "THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value ); return type[ value ]; } var textures = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.image === undefined ) { console.warn( "THREE.ObjectLoader: No "image" specified for", data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined image", data.image ); } var texture = new Texture( images[ data.image ] ); texture.needsUpdate = true; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.center !== undefined ) texture.center.fromArray( data.center ); if ( data.rotation !== undefined ) texture.rotation = data.rotation; if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); } if ( data.format !== undefined ) texture.format = data.format; if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; textures[ data.uuid ] = texture; } } return textures; }, parseObject: function ( data, geometries, materials ) { var object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined geometry", name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( Array.isArray( name ) ) { var array = []; for ( var i = 0, l = name.length; i < l; i ++ ) { var uuid = name[ i ]; if ( materials[ uuid ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", uuid ); } array.push( materials[ uuid ] ); } return array; } if ( materials[ name ] === undefined ) { console.warn( "THREE.ObjectLoader: Undefined material", name ); } return materials[ name ]; } switch ( data.type ) { case "Scene": object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } } if ( data.fog !== undefined ) { if ( data.fog.type === "Fog" ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === "FogExp2" ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } break; case "PerspectiveCamera": object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "OrthographicCamera": object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case "AmbientLight": object = new AmbientLight( data.color, data.intensity ); break; case "DirectionalLight": object = new DirectionalLight( data.color, data.intensity ); break; case "PointLight": object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case "RectAreaLight": object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); break; case "SpotLight": object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case "HemisphereLight": object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case "SkinnedMesh": console.warn( "THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet." ); case "Mesh": var geometry = getGeometry( data.geometry ); var material = getMaterial( data.material ); if ( geometry.bones && geometry.bones.length > 0 ) { object = new SkinnedMesh( geometry, material ); } else { object = new Mesh( geometry, material ); } break; case "LOD": object = new LOD(); break; case "Line": object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); break; case "LineLoop": object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "LineSegments": object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "PointCloud": case "Points": object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case "Sprite": object = new Sprite( getMaterial( data.material ) ); break; case "Group": object = new Group(); break; default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { object.matrix.fromArray( data.matrix ); if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.children !== undefined ) { var children = data.children; for ( var i = 0; i < children.length; i ++ ) { object.add( this.parseObject( children[ i ], geometries, materials ) ); } } if ( data.type === "LOD" ) { var levels = data.levels; for ( var l = 0; l < levels.length; l ++ ) { var level = levels[ l ]; var child = object.getObjectByProperty( "uuid", level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance ); } } } return object; } } ); var TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, SphericalReflectionMapping: SphericalReflectionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; var TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; var TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipMapNearestFilter: NearestMipMapNearestFilter, NearestMipMapLinearFilter: NearestMipMapLinearFilter, LinearFilter: LinearFilter, LinearMipMapNearestFilter: LinearMipMapNearestFilter, LinearMipMapLinearFilter: LinearMipMapLinearFilter }; /** * @author thespite / http://clicktorelease.com/ */ function ImageBitmapLoader( manager ) { if ( typeof createImageBitmap === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: createImageBitmap() not supported." ); } if ( typeof fetch === "undefined" ) { console.warn( "THREE.ImageBitmapLoader: fetch() not supported." ); } this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.options = undefined; } ImageBitmapLoader.prototype = { constructor: ImageBitmapLoader, setOptions: function setOptions( options ) { this.options = options; return this; }, load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ""; if ( this.path !== undefined ) url = this.path + url; url = this.manager.resolveURL( url ); var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } fetch( url ).then( function ( res ) { return res.blob(); } ).then( function ( blob ) { return createImageBitmap( blob, scope.options ); } ).then( function ( imageBitmap ) { Cache.add( url, imageBitmap ); if ( onLoad ) onLoad( imageBitmap ); scope.manager.itemEnd( url ); } ).catch( function ( e ) { if ( onError ) onError( e ); scope.manager.itemEnd( url ); scope.manager.itemError( url ); } ); }, setCrossOrigin: function ( /* value */ ) { return this; }, setPath: function ( value ) { this.path = value; return this; } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" **/ function ShapePath() { this.type = "ShapePath"; this.color = new Color(); this.subPaths = []; this.currentPath = null; } Object.assign( ShapePath.prototype, { moveTo: function ( x, y ) { this.currentPath = new Path(); this.subPaths.push( this.currentPath ); this.currentPath.moveTo( x, y ); }, lineTo: function ( x, y ) { this.currentPath.lineTo( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); }, splineThru: function ( pts ) { this.currentPath.splineThru( pts ); }, toShapes: function ( isCCW, noHoles ) { function toShapesNoHoles( inSubpaths ) { var shapes = []; for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { var tmpPath = inSubpaths[ i ]; var tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { var polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line var inside = false; for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { var edgeLowPt = inPolygon[ p ]; var edgeHighPt = inPolygon[ q ]; var edgeDx = edgeHighPt.x - edgeLowPt.x; var edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn"t count !!! } else { var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } var isClockWise = ShapeUtils.isClockWise; var subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; if ( noHoles === true ) return toShapesNoHoles( subPaths ); var solid, tmpPath, tmpShape, shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); var betterShapeHoles = []; var newShapes = []; var newShapeHoles = []; var mainIdx = 0; var tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( var i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log("cw", i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log("ccw", i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { var ambiguous = false; var toChange = []; for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { var sho = newShapeHoles[ sIdx ]; for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { var ho = sho[ hIdx ]; var hole_unassigned = true; for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { // console.log("to change: ", toChange); if ( ! ambiguous ) newShapeHoles = betterShapeHoles; } } var tmpHoles; for ( var i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author mrdoob / http://mrdoob.com/ */ function Font( data ) { this.type = "Font"; this.data = data; } Object.assign( Font.prototype, { isFont: true, generateShapes: function ( text, size, divisions ) { if ( size === undefined ) size = 100; if ( divisions === undefined ) divisions = 4; var shapes = []; var paths = createPaths( text, size, divisions, this.data ); for ( var p = 0, pl = paths.length; p < pl; p ++ ) { Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); } return shapes; } } ); function createPaths( text, size, divisions, data ) { var chars = Array.from ? Array.from( text ) : String( text ).split( "" ); // see #13988 var scale = size / data.resolution; var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; var paths = []; var offsetX = 0, offsetY = 0; for ( var i = 0; i < chars.length; i ++ ) { var char = chars[ i ]; if ( char === " " ) { offsetX = 0; offsetY -= line_height; } else { var ret = createPath( char, divisions, scale, offsetX, offsetY, data ); offsetX += ret.offsetX; paths.push( ret.path ); } } return paths; } function createPath( char, divisions, scale, offsetX, offsetY, data ) { var glyph = data.glyphs[ char ] || data.glyphs[ "?" ]; if ( ! glyph ) return; var path = new ShapePath(); var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; if ( glyph.o ) { var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( " " ) ); for ( var i = 0, l = outline.length; i < l; ) { var action = outline[ i ++ ]; switch ( action ) { case "m": // moveTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.moveTo( x, y ); break; case "l": // lineTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.lineTo( x, y ); break; case "q": // quadraticCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); break; case "b": // bezierCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; cpx2 = outline[ i ++ ] * scale + offsetX; cpy2 = outline[ i ++ ] * scale + offsetY; path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); break; } } } return { offsetX: glyph.ha * scale, path: path }; } /** * @author mrdoob / http://mrdoob.com/ */ function FontLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FontLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { var json; try { json = JSON.parse( text ); } catch ( e ) { console.warn( "THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead." ); json = JSON.parse( text.substring( 65, text.length - 2 ) ); } var font = scope.parse( json ); if ( onLoad ) onLoad( font ); }, onProgress, onError ); }, parse: function ( json ) { return new Font( json ); }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ var context; var AudioContext = { getContext: function () { if ( context === undefined ) { context = new ( window.AudioContext || window.webkitAudioContext )(); } return context; }, setContext: function ( value ) { context = value; } }; /** * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function AudioLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( AudioLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var loader = new FileLoader( this.manager ); loader.setResponseType( "arraybuffer" ); loader.load( url, function ( buffer ) { var context = AudioContext.getContext(); context.decodeAudioData( buffer, function ( audioBuffer ) { onLoad( audioBuffer ); } ); }, onProgress, onError ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function StereoCamera() { this.type = "StereoCamera"; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; } Object.assign( StereoCamera.prototype, { update: ( function () { var instance, focus, fov, aspect, near, far, zoom, eyeSep; var eyeRight = new Matrix4(); var eyeLeft = new Matrix4(); return function update( camera ) { var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || aspect !== camera.aspect * this.aspect || near !== camera.near || far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep; if ( needsUpdate ) { instance = this; focus = camera.focus; fov = camera.fov; aspect = camera.aspect * this.aspect; near = camera.near; far = camera.far; zoom = camera.zoom; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ var projectionMatrix = camera.projectionMatrix.clone(); eyeSep = this.eyeSep / 2; var eyeSepOnProjection = eyeSep * near / focus; var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; var xmin, xmax; // translate xOffset eyeLeft.elements[ 12 ] = - eyeSep; eyeRight.elements[ 12 ] = eyeSep; // for left eye xmin = - ymax * aspect + eyeSepOnProjection; xmax = ymax * aspect + eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( projectionMatrix ); // for right eye xmin = - ymax * aspect - eyeSepOnProjection; xmax = ymax * aspect - eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); }; } )() } ); /** * Camera for rendering cube maps * - renders scene into axis-aligned cube * * @author alteredq / http://alteredqualia.com/ */ function CubeCamera( near, far, cubeResolution ) { Object3D.call( this ); this.type = "CubeCamera"; var fov = 90, aspect = 1; var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); this.add( cameraPX ); var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); this.add( cameraNX ); var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); this.add( cameraPY ); var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); this.add( cameraNY ); var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); this.add( cameraPZ ); var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.renderTarget.texture.name = "CubeCamera"; this.update = function ( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); var renderTarget = this.renderTarget; var generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderTarget.activeCubeFace = 0; renderer.render( scene, cameraPX, renderTarget ); renderTarget.activeCubeFace = 1; renderer.render( scene, cameraNX, renderTarget ); renderTarget.activeCubeFace = 2; renderer.render( scene, cameraPY, renderTarget ); renderTarget.activeCubeFace = 3; renderer.render( scene, cameraNY, renderTarget ); renderTarget.activeCubeFace = 4; renderer.render( scene, cameraPZ, renderTarget ); renderTarget.texture.generateMipmaps = generateMipmaps; renderTarget.activeCubeFace = 5; renderer.render( scene, cameraNZ, renderTarget ); renderer.setRenderTarget( null ); }; this.clear = function ( renderer, color, depth, stencil ) { var renderTarget = this.renderTarget; for ( var i = 0; i < 6; i ++ ) { renderTarget.activeCubeFace = i; renderer.setRenderTarget( renderTarget ); renderer.clear( color, depth, stencil ); } renderer.setRenderTarget( null ); }; } CubeCamera.prototype = Object.create( Object3D.prototype ); CubeCamera.prototype.constructor = CubeCamera; /** * @author mrdoob / http://mrdoob.com/ */ function AudioListener() { Object3D.call( this ); this.type = "AudioListener"; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; } AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: AudioListener, getInput: function () { return this.gain; }, removeFilter: function ( ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } }, getFilter: function () { return this.filter; }, setFilter: function ( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); }, getMasterVolume: function () { return this.gain.gain.value; }, setMasterVolume: function ( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); }, updateMatrixWorld: ( function () { var position = new Vector3(); var quaternion = new Quaternion(); var scale = new Vector3(); var orientation = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); var listener = this.context.listener; var up = this.up; this.matrixWorld.decompose( position, quaternion, scale ); orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); if ( listener.positionX ) { listener.positionX.setValueAtTime( position.x, this.context.currentTime ); listener.positionY.setValueAtTime( position.y, this.context.currentTime ); listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); listener.upX.setValueAtTime( up.x, this.context.currentTime ); listener.upY.setValueAtTime( up.y, this.context.currentTime ); listener.upZ.setValueAtTime( up.z, this.context.currentTime ); } else { listener.setPosition( position.x, position.y, position.z ); listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function Audio( listener ) { Object3D.call( this ); this.type = "Audio"; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.loop = false; this.startTime = 0; this.offset = 0; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.sourceType = "empty"; this.filters = []; } Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Audio, getOutput: function () { return this.gain; }, setNodeSource: function ( audioNode ) { this.hasPlaybackControl = false; this.sourceType = "audioNode"; this.source = audioNode; this.connect(); return this; }, setMediaElementSource: function ( mediaElement ) { this.hasPlaybackControl = false; this.sourceType = "mediaNode"; this.source = this.context.createMediaElementSource( mediaElement ); this.connect(); return this; }, setBuffer: function ( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = "buffer"; if ( this.autoplay ) this.play(); return this; }, play: function () { if ( this.isPlaying === true ) { console.warn( "THREE.Audio: Audio is already playing." ); return; } if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } var source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.onended = this.onEnded.bind( this ); source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); this.startTime = this.context.currentTime; source.start( this.startTime, this.offset ); this.isPlaying = true; this.source = source; return this.connect(); }, pause: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } if ( this.isPlaying === true ) { this.source.stop(); this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; this.isPlaying = false; } return this; }, stop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.source.stop(); this.offset = 0; this.isPlaying = false; return this; }, connect: function () { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } return this; }, disconnect: function () { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } return this; }, getFilters: function () { return this.filters; }, setFilters: function ( value ) { if ( ! value ) value = []; if ( this.isPlaying === true ) { this.disconnect(); this.filters = value; this.connect(); } else { this.filters = value; } return this; }, getFilter: function () { return this.getFilters()[ 0 ]; }, setFilter: function ( filter ) { return this.setFilters( filter ? [ filter ] : [] ); }, setPlaybackRate: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); } return this; }, getPlaybackRate: function () { return this.playbackRate; }, onEnded: function () { this.isPlaying = false; }, getLoop: function () { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return false; } return this.loop; }, setLoop: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( "THREE.Audio: this Audio has no playback control." ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; }, getVolume: function () { return this.gain.gain.value; }, setVolume: function ( value ) { this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PositionalAudio( listener ) { Audio.call( this, listener ); this.panner = this.context.createPanner(); this.panner.connect( this.gain ); } PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { constructor: PositionalAudio, getOutput: function () { return this.panner; }, getRefDistance: function () { return this.panner.refDistance; }, setRefDistance: function ( value ) { this.panner.refDistance = value; }, getRolloffFactor: function () { return this.panner.rolloffFactor; }, setRolloffFactor: function ( value ) { this.panner.rolloffFactor = value; }, getDistanceModel: function () { return this.panner.distanceModel; }, setDistanceModel: function ( value ) { this.panner.distanceModel = value; }, getMaxDistance: function () { return this.panner.maxDistance; }, setMaxDistance: function ( value ) { this.panner.maxDistance = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); position.setFromMatrixPosition( this.matrixWorld ); this.panner.setPosition( position.x, position.y, position.z ); }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function AudioAnalyser( audio, fftSize ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } Object.assign( AudioAnalyser.prototype, { getFrequencyData: function () { this.analyser.getByteFrequencyData( this.data ); return this.data; }, getAverageFrequency: function () { var value = 0, data = this.getFrequencyData(); for ( var i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } ); /** * * Buffered scene graph property that allows weighted accumulation. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function PropertyMixer( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; var bufferType = Float64Array, mixFunction; switch ( typeName ) { case "quaternion": mixFunction = this._slerp; break; case "string": case "bool": bufferType = Array; mixFunction = this._select; break; default: mixFunction = this._lerp; } this.buffer = new bufferType( valueSize * 4 ); // layout: [ incoming | accu0 | accu1 | orig ] // // interpolators can use .buffer as their .result // the data then goes to "incoming" // // "accu0" and "accu1" are used frame-interleaved for // the cumulative result and are compared to detect // changes // // "orig" stores the original state of the property this._mixBufferRegion = mixFunction; this.cumulativeWeight = 0; this.useCount = 0; this.referenceCount = 0; } Object.assign( PropertyMixer.prototype, { // accumulate data in the "incoming" region into "accu" accumulate: function ( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn"t have made the call in the first place var buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride, currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( var i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; var mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; }, // apply the state of "accu" to the binding when accus differ apply: function ( accuIndex ) { var stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, binding = this.binding; this.cumulativeWeight = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) var originalValueOffset = stride * 3; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } for ( var i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } }, // remember the state of the bound property and copy it to both accus saveOriginalState: function () { var binding = this.binding; var buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * 3; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } this.cumulativeWeight = 0; }, // apply the state previously taken via "saveOriginalState" to the binding restoreOriginalState: function () { var originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); }, // mix functions _select: function ( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( var i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } }, _slerp: function ( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); }, _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { var s = 1 - t; for ( var i = 0; i !== stride; ++ i ) { var j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } } ); /** * * A reference to a real property in the scene graph. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ // Characters [].:/ are reserved for track binding syntax. var RESERVED_CHARS_RE = "\[\]\.:\/"; function Composite( targetGroup, path, optionalParsedPath ) { var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); } Object.assign( Composite.prototype, { getValue: function ( array, offset ) { this.bind(); // bind all binding var firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); }, setValue: function ( array, offset ) { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } }, bind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } }, unbind: function () { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } } ); function PropertyBinding( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; this.rootNode = rootNode; } Object.assign( PropertyBinding, { Composite: Composite, create: function ( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } }, /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ sanitizeNodeName: ( function () { var reservedRe = new RegExp( "[" + RESERVED_CHARS_RE + "]", "g" ); return function sanitizeNodeName( name ) { return name.replace( /s/g, "_" ).replace( reservedRe, "" ); }; }() ), parseTrackName: function () { // Attempts to allow node names from any language. ES5"s `w` regexp matches // only latin characters, and the unicode p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. var wordChar = "[^" + RESERVED_CHARS_RE + "]"; var wordCharOrDot = "[^" + RESERVED_CHARS_RE.replace( "\.", "" ) + "]"; // Parent directories, delimited by "/" or ":". Currently unused, but must // be matched to parse the rest of the track name. var directoryRe = /((?:WC+[/:])*)/.source.replace( "WC", wordChar ); // Target node. May contain word characters (a-zA-Z0-9_) and "." or "-". var nodeRe = /(WCOD+)?/.source.replace( "WCOD", wordCharOrDot ); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. var objectRe = /(?:.(WC+)(?:[(.+)])?)?/.source.replace( "WC", wordChar ); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. var propertyRe = /.(WC+)(?:[(.+)])?/.source.replace( "WC", wordChar ); var trackRe = new RegExp( "" + "^" + directoryRe + nodeRe + objectRe + propertyRe + "$" ); var supportedObjectNames = [ "material", "materials", "bones" ]; return function parseTrackName( trackName ) { var matches = trackRe.exec( trackName ); if ( ! matches ) { throw new Error( "PropertyBinding: Cannot parse trackName: " + trackName ); } var results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], // required propertyIndex: matches[ 6 ] }; var lastDot = results.nodeName && results.nodeName.lastIndexOf( "." ); if ( lastDot !== undefined && lastDot !== - 1 ) { var objectName = results.nodeName.substring( lastDot + 1 ); // Object names must be checked against a whitelist. Otherwise, there // is no way to parse "foo.bar.baz": "baz" must be a property, but // "bar" could be the objectName, or part of a nodeName (which can // include "." characters). if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) { results.nodeName = results.nodeName.substring( 0, lastDot ); results.objectName = objectName; } } if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "PropertyBinding: can not parse propertyName from trackName: " + trackName ); } return results; }; }(), findNode: function ( root, nodeName ) { if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { var bone = root.skeleton.getBoneByName( nodeName ); if ( bone !== undefined ) { return bone; } } // search into node subtree. if ( root.children ) { var searchNodeSubtree = function ( children ) { for ( var i = 0; i < children.length; i ++ ) { var childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } var result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; var subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; } } ); Object.assign( PropertyBinding.prototype, { // prototype, continued // these are used to "bind" a nonexistent property _getValue_unavailable: function () {}, _setValue_unavailable: function () {}, BindingType: { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }, Versioning: { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }, GetterByBindingType: [ function getValue_direct( buffer, offset ) { buffer[ offset ] = this.node[ this.propertyName ]; }, function getValue_array( buffer, offset ) { var source = this.resolvedProperty; for ( var i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } }, function getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; }, function getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } ], SetterByBindingTypeAndVersioning: [ [ // Direct function setValue_direct( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; }, function setValue_direct_setNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.targetObject[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // EntireArray function setValue_array( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } }, function setValue_array_setNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; }, function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // ArrayElement function setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; }, function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // HasToFromArray function setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); }, function setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; }, function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } ] ], getValue: function getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); // Note: This class uses a State pattern on a per-method basis: // "bind" sets "this.getValue" / "setValue" and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. }, setValue: function getValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); }, // create getter / setter pair for a property in the scene graph bind: function () { var targetObject = this.node, parsedPath = this.parsedPath, objectName = parsedPath.objectName, propertyName = parsedPath.propertyName, propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; this.node = targetObject; } // set fail state so we can just "return" on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.error( "THREE.PropertyBinding: Trying to update node for track: " + this.path + " but it wasn"t found." ); return; } if ( objectName ) { var objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case "materials": if ( ! targetObject.material ) { console.error( "THREE.PropertyBinding: Can not bind to material as node does not have a material.", this ); return; } if ( ! targetObject.material.materials ) { console.error( "THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this ); return; } targetObject = targetObject.material.materials; break; case "bones": if ( ! targetObject.skeleton ) { console.error( "THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.", this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( var i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; default: if ( targetObject[ objectName ] === undefined ) { console.error( "THREE.PropertyBinding: Can not bind to objectName of node undefined.", this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( "THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property var nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { var nodeName = parsedPath.nodeName; console.error( "THREE.PropertyBinding: Trying to update property for track: " + nodeName + "." + propertyName + " but it wasn"t found.", targetObject ); return; } // determine versioning scheme var versioning = this.Versioning.None; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; this.targetObject = targetObject; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; this.targetObject = targetObject; } // determine how the property gets bound var bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.", this ); return; } if ( targetObject.geometry.isBufferGeometry ) { if ( ! targetObject.geometry.morphAttributes ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.", this ); return; } for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) { if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } else { if ( ! targetObject.geometry.morphTargets ) { console.error( "THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.", this ); return; } for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( Array.isArray( nodeProperty ) ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; }, unbind: function () { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of "this" via "delete" this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } ); //! DECLARE ALIAS AFTER assign prototype ! Object.assign( PropertyBinding.prototype, { // initial state of these methods that calls "bind" _getValue_unbound: PropertyBinding.prototype.getValue, _setValue_unbound: PropertyBinding.prototype.setValue, } ); /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as "root" to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as "root". * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. * * @author tschw */ function AnimationObjectGroup() { this.uuid = _Math.generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite var indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( var i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don"t care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays var scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } Object.assign( AnimationObjectGroup.prototype, { isAnimationObjectGroup: true, add: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length, knownObject = undefined; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject ) { console.error( "THREE.AnimationObjectGroup: Different objects with the same UUID " + "detected. Clean the caches or recreate your infrastructure when reloading scenes." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; }, remove: function () { var objects = this._objects, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region var lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; }, // remove & forget uncache: function () { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object"s place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop var lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; indicesByUUID[ lastObject.uuid ] = index; objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; }, // Internal interface used by befriended PropertyBinding.Composite: subscribe_: function ( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ], bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; var paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { var object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; }, unsubscribe_: function ( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with "subscribe_" var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { var paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } } ); /** * * Action provided by AnimationMixer for scheduling clip playback on specific * objects. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw * */ function AnimationAction( mixer, clip, localRoot ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot || null; var tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); var interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( var i = 0; i !== nTracks; ++ i ) { var interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = - 1; // global mixer time when the action is to be started // it"s set back to "null" upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } Object.assign( AnimationAction.prototype, { // State & Scheduling play: function () { this._mixer._activateAction( this ); return this; }, stop: function () { this._mixer._deactivateAction( this ); return this.reset(); }, reset: function () { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = - 1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); }, isRunning: function () { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); }, // return true when play has been called isScheduled: function () { return this._mixer._isActiveAction( this ); }, startAt: function ( time ) { this._startTime = time; return this; }, setLoop: function ( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; }, // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight: function ( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); }, // return the weight considering fading and .enabled getEffectiveWeight: function () { return this._effectiveWeight; }, fadeIn: function ( duration ) { return this._scheduleFading( duration, 0, 1 ); }, fadeOut: function ( duration ) { return this._scheduleFading( duration, 1, 0 ); }, crossFadeFrom: function ( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if ( warp ) { var fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; }, crossFadeTo: function ( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); }, stopFading: function () { var weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; }, // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale: function ( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); }, // return the time scale considering warping and .paused getEffectiveTimeScale: function () { return this._effectiveTimeScale; }, setDuration: function ( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); }, syncWith: function ( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); }, halt: function ( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); }, warp: function ( startTimeScale, endTimeScale, duration ) { var mixer = this._mixer, now = mixer.time, interpolant = this._timeScaleInterpolant, timeScale = this.timeScale; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; }, stopWarping: function () { var timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; }, // Object Accessors getMixer: function () { return this._mixer; }, getClip: function () { return this._clip; }, getRoot: function () { return this._localRoot || this._mixer._root; }, // Interna _update: function ( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer if ( ! this.enabled ) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight( time ); return; } var startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action var timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { return; // yet to come / don"t decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); var clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 var weight = this._updateWeight( time ); if ( weight > 0 ) { var interpolants = this._interpolants; var propertyMixers = this._propertyBindings; for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } }, _updateWeight: function ( time ) { var weight = 0; if ( this.enabled ) { weight = this.weight; var interpolant = this._weightInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; }, _updateTimeScale: function ( time ) { var timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; var interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; }, _updateTime: function ( deltaTime ) { var time = this.time + deltaTime; if ( deltaTime === 0 ) return time; var duration = this._clip.duration, loop = this.loop, loopCount = this._loopCount; if ( loop === LoopOnce ) { if ( loopCount === - 1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else break handle_stop; if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime < 0 ? - 1 : 1 } ); } } else { // repetitive Repeat or PingPong var pingPong = ( loop === LoopPingPong ); if ( loopCount === - 1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around var loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); var pending = this.repetitions - loopCount; if ( pending <= 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this._mixer.dispatchEvent( { type: "finished", action: this, direction: deltaTime > 0 ? 1 : - 1 } ); } else { // keep running if ( pending === 1 ) { // entering the last round var atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this._mixer.dispatchEvent( { type: "loop", action: this, loopDelta: loopDelta } ); } } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" this.time = time; return duration - time; } } this.time = time; return time; }, _setEndings: function ( atStart, atEnd, pingPong ) { var settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } }, _scheduleFading: function ( duration, weightNow, weightThen ) { var mixer = this._mixer, now = mixer.time, interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } } ); /** * * Player for AnimationClips. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function AnimationMixer( root ) { this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { constructor: AnimationMixer, _bindAction: function ( action, prototypeAction ) { var root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName, bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( var i = 0; i !== nTracks; ++ i ) { var track = tracks[ i ], trackName = track.name, binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } var path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } }, _activateAction: function ( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind var rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } var bindings = action._propertyBindings; // increment reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } }, _deactivateAction: function ( action ) { if ( this._isActiveAction( action ) ) { var bindings = action._propertyBindings; // decrement reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } }, // Memory manager _initMemoryManager: function () { this._actions = []; // "nActiveActions" followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // "nActiveBindings" followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; var scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; }, // Memory management for AnimationAction objects _isActiveAction: function ( action ) { var index = action._cacheIndex; return index !== null && index < this._nActiveActions; }, _addInactiveAction: function ( action, clipUuid, rootUuid ) { var actions = this._actions, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { var knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; }, _removeInactiveAction: function ( action ) { var actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; var clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; var actionByRoot = actionsForClip.actionByRoot, rootUuid = ( action._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); }, _removeInactiveBindingsForAction: function ( action ) { var bindings = action._propertyBindings; for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } }, _lendAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s var actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; }, _takeBackAction: function ( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a var actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; }, // Memory management for PropertyMixer objects _addInactiveBinding: function ( binding, rootUuid, trackName ) { var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], bindings = this._bindings; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); }, _removeInactiveBinding: function ( binding ) { var bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; remove_empty_map: { for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars delete bindingsByRoot[ rootUuid ]; } }, _lendBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; }, _takeBackBinding: function ( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; }, // Memory management of Interpolants for weight and time scale _lendControlInterpolant: function () { var interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++, interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, this._controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; }, _takeBackControlInterpolant: function ( interpolant ) { var interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; }, _controlInterpolantsResultBuffer: new Float32Array( 1 ), // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject !== null ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ], prototypeAction = null; if ( actionsForClip !== undefined ) { var existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined ) { return existingAction; } // we know the clip, so we don"t have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it var newAction = new AnimationAction( this, clipObject, optionalRoot ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; }, // get an existing action existingAction: function ( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === "string" ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; }, // deactivates all previously scheduled actions stopAllAction: function () { var actions = this._actions, nActions = this._nActiveActions, bindings = this._bindings, nBindings = this._nActiveBindings; this._nActiveActions = 0; this._nActiveBindings = 0; for ( var i = 0; i !== nActions; ++ i ) { actions[ i ].reset(); } for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].useCount = 0; } return this; }, // advance the time and update apply the animation update: function ( deltaTime ) { deltaTime *= this.timeScale; var actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( var i = 0; i !== nActions; ++ i ) { var action = actions[ i ]; action._update( time, deltaTime, timeDirection, accuIndex ); } // update scene graph var bindings = this._bindings, nBindings = this._nActiveBindings; for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; }, // return this mixer"s root target object getRoot: function () { return this._root; }, // free all resources specific to a particular clip uncacheClip: function ( clip ) { var actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away var actionsToRemove = actionsForClip.knownActions; for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { var action = actionsToRemove[ i ]; this._deactivateAction( action ); var cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } }, // free all resources specific to a particular root target object uncacheRoot: function ( root ) { var rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( var clipUuid in actionsByClip ) { var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( var trackName in bindingByName ) { var binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } }, // remove a targeted clip from the cache uncacheAction: function ( clip, optionalRoot ) { var action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Uniform( value ) { if ( typeof value === "string" ) { console.warn( "THREE.Uniform: Type parameter is no longer needed." ); value = arguments[ 1 ]; } this.value = value; } Uniform.prototype.clone = function () { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferGeometry() { BufferGeometry.call( this ); this.type = "InstancedBufferGeometry"; this.maxInstancedCount = undefined; } InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { constructor: InstancedBufferGeometry, isInstancedBufferGeometry: true, copy: function ( source ) { BufferGeometry.prototype.copy.call( this, source ); this.maxInstancedCount = source.maxInstancedCount; return this; }, clone: function () { return new this.constructor().copy( this ); } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } Object.defineProperties( InterleavedBufferAttribute.prototype, { count: { get: function () { return this.data.count; } }, array: { get: function () { return this.data.array; } } } ); Object.assign( InterleavedBufferAttribute.prototype, { isInterleavedBufferAttribute: true, setX: function ( index, x ) { this.data.array[ index * this.data.stride + this.offset ] = x; return this; }, setY: function ( index, y ) { this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; }, setZ: function ( index, z ) { this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; }, setW: function ( index, w ) { this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; }, getX: function ( index ) { return this.data.array[ index * this.data.stride + this.offset ]; }, getY: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 1 ]; }, getZ: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 2 ]; }, getW: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 3 ]; }, setXY: function ( index, x, y ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBuffer( array, stride ) { this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.version = 0; } Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", { set: function ( value ) { if ( value === true ) this.version ++; } } ); Object.assign( InterleavedBuffer.prototype, { isInterleavedBuffer: true, onUploadCallback: function () {}, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( "THREE.BufferAttribute: array should be a Typed Array." ); } this.count = array !== undefined ? array.length / this.stride : 0; this.array = array; return this; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( var i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, clone: function () { return new this.constructor().copy( this ); }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { InterleavedBuffer.call( this, array, stride ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { constructor: InstancedInterleavedBuffer, isInstancedInterleavedBuffer: true, copy: function ( source ) { InterleavedBuffer.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { BufferAttribute.call( this, array, itemSize ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { constructor: InstancedBufferAttribute, isInstancedBufferAttribute: true, copy: function ( source ) { BufferAttribute.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ function Raycaster( origin, direction, near, far ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near || 0; this.far = far || Infinity; this.params = { Mesh: {}, Line: {}, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; Object.defineProperties( this.params, { PointCloud: { get: function () { console.warn( "THREE.Raycaster: params.PointCloud has been renamed to params.Points." ); return this.Points; } } } ); } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.visible === false ) return; object.raycast( raycaster, intersects ); if ( recursive === true ) { var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } Object.assign( Raycaster.prototype, { linePrecision: 1, set: function ( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); }, setFromCamera: function ( coords, camera ) { if ( ( camera && camera.isPerspectiveCamera ) ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); } else if ( ( camera && camera.isOrthographicCamera ) ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); } else { console.error( "THREE.Raycaster: Unsupported camera type." ); } }, intersectObject: function ( object, recursive, optionalTarget ) { var intersects = optionalTarget || []; intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; }, intersectObjects: function ( objects, recursive, optionalTarget ) { var intersects = optionalTarget || []; if ( Array.isArray( objects ) === false ) { console.warn( "THREE.Raycaster.intersectObjects: objects is not an Array." ); return intersects; } for ( var i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function Clock( autoStart ) { this.autoStart = ( autoStart !== undefined ) ? autoStart : true; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } Object.assign( Clock.prototype, { start: function () { this.startTime = ( typeof performance === "undefined" ? Date : performance ).now(); // see #10732 this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; }, stop: function () { this.getElapsedTime(); this.running = false; this.autoStart = false; }, getElapsedTime: function () { this.getDelta(); return this.elapsedTime; }, getDelta: function () { var diff = 0; if ( this.autoStart && ! this.running ) { this.start(); return 0; } if ( this.running ) { var newTime = ( typeof performance === "undefined" ? Date : performance ).now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } ); /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley * * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The poles (phi) are at the positive and negative y axis. * The equator starts at positive z. */ function Spherical( radius, phi, theta ) { this.radius = ( radius !== undefined ) ? radius : 1.0; this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere return this; } Object.assign( Spherical.prototype, { set: function ( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; }, // restrict phi to be betwee EPS and PI-EPS makeSafe: function () { var EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; }, setFromVector3: function ( vec3 ) { this.radius = vec3.length(); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle } return this; } } ); /** * @author Mugen87 / https://github.com/Mugen87 * * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system * */ function Cylindrical( radius, theta, y ) { this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane return this; } Object.assign( Cylindrical.prototype, { set: function ( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; }, setFromVector3: function ( vec3 ) { this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); this.theta = Math.atan2( vec3.x, vec3.z ); this.y = vec3.y; return this; } } ); /** * @author bhouston / http://clara.io */ function Box2( min, max ) { this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); } Object.assign( Box2.prototype, { set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector2(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); }, getCenter: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .getCenter() target is now required" ); target = new Vector2(); } return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .getSize() target is now required" ); target = new Vector2(); } return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; }, getParameter: function ( point, target ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. if ( target === undefined ) { console.warn( "THREE.Box2: .getParameter() target is now required" ); target = new Vector2(); } return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); }, intersectsBox: function ( box ) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; }, clampPoint: function ( point, target ) { if ( target === undefined ) { console.warn( "THREE.Box2: .clampPoint() target is now required" ); target = new Vector2(); } return target.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector2(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** * @author alteredq / http://alteredqualia.com/ */ function ImmediateRenderObject( material ) { Object3D.call( this ); this.material = material; this.render = function ( /* renderCallback */ ) {}; } ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; ImmediateRenderObject.prototype.isImmediateRenderObject = true; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function VertexNormalsHelper( object, size, hex, linewidth ) { this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xff0000; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length * 3; } else if ( objGeometry && objGeometry.isBufferGeometry ) { nNormals = objGeometry.attributes.normal.count; } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; VertexNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { var keys = [ "a", "b", "c" ]; this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { var vertex = vertices[ face[ keys[ j ] ] ]; var normal = face.vertexNormals[ j ]; v1.copy( vertex ).applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } } else if ( objGeometry && objGeometry.isBufferGeometry ) { var objPos = objGeometry.attributes.position; var objNorm = objGeometry.attributes.normal; var idx = 0; // for simplicity, ignore index and drawcalls, and render every normal for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function SpotLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new BufferGeometry(); var positions = [ 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 ]; for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { var p1 = ( i / l ) * Math.PI * 2; var p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } SpotLightHelper.prototype = Object.create( Object3D.prototype ); SpotLightHelper.prototype.constructor = SpotLightHelper; SpotLightHelper.prototype.dispose = function () { this.cone.geometry.dispose(); this.cone.material.dispose(); }; SpotLightHelper.prototype.update = function () { var vector = new Vector3(); var vector2 = new Vector3(); return function update() { this.light.updateMatrixWorld(); var coneLength = this.light.distance ? this.light.distance : 1000; var coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); vector.setFromMatrixPosition( this.light.matrixWorld ); vector2.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( vector2.sub( vector ) ); if ( this.color !== undefined ) { this.cone.material.color.set( this.color ); } else { this.cone.material.color.copy( this.light.color ); } }; }(); /** * @author Sean Griffin / http://twitter.com/sgrif * @author Michael Guerrero / http://realitymeltdown.com * @author mrdoob / http://mrdoob.com/ * @author ikerr / http://verold.com * @author Mugen87 / https://github.com/Mugen87 */ function getBoneList( object ) { var boneList = []; if ( object && object.isBone ) { boneList.push( object ); } for ( var i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } return boneList; } function SkeletonHelper( object ) { var bones = getBoneList( object ); var geometry = new BufferGeometry(); var vertices = []; var colors = []; var color1 = new Color( 0, 0, 1 ); var color2 = new Color( 0, 1, 0 ); for ( var i = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); LineSegments.call( this, geometry, material ); this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } SkeletonHelper.prototype = Object.create( LineSegments.prototype ); SkeletonHelper.prototype.constructor = SkeletonHelper; SkeletonHelper.prototype.updateMatrixWorld = function () { var vector = new Vector3(); var boneMatrix = new Matrix4(); var matrixWorldInv = new Matrix4(); return function updateMatrixWorld( force ) { var bones = this.bones; var geometry = this.geometry; var position = geometry.getAttribute( "position" ); matrixWorldInv.getInverse( this.root.matrixWorld ); for ( var i = 0, j = 0; i < bones.length; i ++ ) { var bone = bones[ i ]; if ( bone.parent && bone.parent.isBone ) { boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j, vector.x, vector.y, vector.z ); boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j + 1, vector.x, vector.y, vector.z ); j += 2; } } geometry.getAttribute( "position" ).needsUpdate = true; Object3D.prototype.updateMatrixWorld.call( this, force ); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function PointLightHelper( light, sphereSize, color ) { this.light = light; this.light.updateMatrixWorld(); this.color = color; var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); Mesh.call( this, geometry, material ); this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); var d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } PointLightHelper.prototype = Object.create( Mesh.prototype ); PointLightHelper.prototype.constructor = PointLightHelper; PointLightHelper.prototype.dispose = function () { this.geometry.dispose(); this.material.dispose(); }; PointLightHelper.prototype.update = function () { if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { this.material.color.copy( this.light.color ); } /* var d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ }; /** * @author abelnation / http://github.com/abelnation * @author Mugen87 / http://github.com/Mugen87 * @author WestLangley / http://github.com/WestLangley */ function RectAreaLightHelper( light, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var material = new LineBasicMaterial( { fog: false } ); var geometry = new BufferGeometry(); geometry.addAttribute( "position", new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) ); this.line = new Line( geometry, material ); this.add( this.line ); this.update(); } RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; RectAreaLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; RectAreaLightHelper.prototype.update = function () { // calculate new dimensions of the helper var hx = this.light.width * 0.5; var hy = this.light.height * 0.5; var position = this.line.geometry.attributes.position; var array = position.array; // update vertices array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0; array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0; position.needsUpdate = true; if ( this.color !== undefined ) { this.line.material.color.set( this.color ); } else { this.line.material.color.copy( this.light.color ); } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function HemisphereLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; var geometry = new OctahedronBufferGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); this.material = new MeshBasicMaterial( { wireframe: true, fog: false } ); if ( this.color === undefined ) this.material.vertexColors = VertexColors; var position = geometry.getAttribute( "position" ); var colors = new Float32Array( position.count * 3 ); geometry.addAttribute( "color", new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, this.material ) ); this.update(); } HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; HemisphereLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; HemisphereLightHelper.prototype.update = function () { var vector = new Vector3(); var color1 = new Color(); var color2 = new Color(); return function update() { var mesh = this.children[ 0 ]; if ( this.color !== undefined ) { this.material.color.set( this.color ); } else { var colors = mesh.geometry.getAttribute( "color" ); color1.copy( this.light.color ); color2.copy( this.light.groundColor ); for ( var i = 0, l = colors.count; i < l; i ++ ) { var color = ( i < ( l / 2 ) ) ? color1 : color2; colors.setXYZ( i, color.r, color.g, color.b ); } colors.needsUpdate = true; } mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); }; }(); /** * @author mrdoob / http://mrdoob.com/ */ function GridHelper( size, divisions, color1, color2 ) { size = size || 10; divisions = divisions || 10; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var center = divisions / 2; var step = size / divisions; var halfSize = size / 2; var vertices = [], colors = []; for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { vertices.push( - halfSize, 0, k, halfSize, 0, k ); vertices.push( k, 0, - halfSize, k, 0, halfSize ); var color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } GridHelper.prototype = Object.create( LineSegments.prototype ); GridHelper.prototype.constructor = GridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 * @author Hectate / http://www.github.com/Hectate */ function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { radius = radius || 10; radials = radials || 16; circles = circles || 8; divisions = divisions || 64; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var vertices = []; var colors = []; var x, z; var v, i, j, r, color; // create the radials for ( i = 0; i <= radials; i ++ ) { v = ( i / radials ) * ( Math.PI * 2 ); x = Math.sin( v ) * radius; z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } // create the circles for ( i = 0; i <= circles; i ++ ) { color = ( i & 1 ) ? color1 : color2; r = radius - ( radius / circles * i ); for ( j = 0; j < divisions; j ++ ) { // first vertex v = ( j / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } PolarGridHelper.prototype = Object.create( LineSegments.prototype ); PolarGridHelper.prototype.constructor = PolarGridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function FaceNormalsHelper( object, size, hex, linewidth ) { // FaceNormalsHelper only supports THREE.Geometry this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xffff00; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length; } else { console.warn( "THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead." ); } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( "position", positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; FaceNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var normal = face.normal; v1.copy( vertices[ face.a ] ) .add( vertices[ face.b ] ) .add( vertices[ face.c ] ) .divideScalar( 3 ) .applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } position.needsUpdate = true; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function DirectionalLightHelper( light, size, color ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; if ( size === undefined ) size = 1; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.lightPlane = new Line( geometry, material ); this.add( this.lightPlane ); geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.targetLine = new Line( geometry, material ); this.add( this.targetLine ); this.update(); } DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; DirectionalLightHelper.prototype.dispose = function () { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); }; DirectionalLightHelper.prototype.update = function () { var v1 = new Vector3(); var v2 = new Vector3(); var v3 = new Vector3(); return function update() { v1.setFromMatrixPosition( this.light.matrixWorld ); v2.setFromMatrixPosition( this.light.target.matrixWorld ); v3.subVectors( v2, v1 ); this.lightPlane.lookAt( v3 ); if ( this.color !== undefined ) { this.lightPlane.material.color.set( this.color ); this.targetLine.material.color.set( this.color ); } else { this.lightPlane.material.color.copy( this.light.color ); this.targetLine.material.color.copy( this.light.color ); } this.targetLine.lookAt( v3 ); this.targetLine.scale.z = v3.length(); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author Mugen87 / https://github.com/Mugen87 * * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * http://evanw.github.com/lightgl.js/tests/shadowmap.html */ function CameraHelper( camera ) { var geometry = new BufferGeometry(); var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); var vertices = []; var colors = []; var pointMap = {}; // colors var colorFrustum = new Color( 0xffaa00 ); var colorCone = new Color( 0xff0000 ); var colorUp = new Color( 0x00aaff ); var colorTarget = new Color( 0xffffff ); var colorCross = new Color( 0x333333 ); // near addLine( "n1", "n2", colorFrustum ); addLine( "n2", "n4", colorFrustum ); addLine( "n4", "n3", colorFrustum ); addLine( "n3", "n1", colorFrustum ); // far addLine( "f1", "f2", colorFrustum ); addLine( "f2", "f4", colorFrustum ); addLine( "f4", "f3", colorFrustum ); addLine( "f3", "f1", colorFrustum ); // sides addLine( "n1", "f1", colorFrustum ); addLine( "n2", "f2", colorFrustum ); addLine( "n3", "f3", colorFrustum ); addLine( "n4", "f4", colorFrustum ); // cone addLine( "p", "n1", colorCone ); addLine( "p", "n2", colorCone ); addLine( "p", "n3", colorCone ); addLine( "p", "n4", colorCone ); // up addLine( "u1", "u2", colorUp ); addLine( "u2", "u3", colorUp ); addLine( "u3", "u1", colorUp ); // target addLine( "c", "t", colorTarget ); addLine( "p", "c", colorCross ); // cross addLine( "cn1", "cn2", colorCross ); addLine( "cn3", "cn4", colorCross ); addLine( "cf1", "cf2", colorCross ); addLine( "cf3", "cf4", colorCross ); function addLine( a, b, color ) { addPoint( a, color ); addPoint( b, color ); } function addPoint( id, color ) { vertices.push( 0, 0, 0 ); colors.push( color.r, color.g, color.b ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); LineSegments.call( this, geometry, material ); this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } CameraHelper.prototype = Object.create( LineSegments.prototype ); CameraHelper.prototype.constructor = CameraHelper; CameraHelper.prototype.update = function () { var geometry, pointMap; var vector = new Vector3(); var camera = new Camera(); function setPoint( point, x, y, z ) { vector.set( x, y, z ).unproject( camera ); var points = pointMap[ point ]; if ( points !== undefined ) { var position = geometry.getAttribute( "position" ); for ( var i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], vector.x, vector.y, vector.z ); } } } return function update() { geometry = this.geometry; pointMap = this.pointMap; var w = 1, h = 1; // we need just camera projection matrix // world matrix must be identity camera.projectionMatrix.copy( this.camera.projectionMatrix ); // center / target setPoint( "c", 0, 0, - 1 ); setPoint( "t", 0, 0, 1 ); // near setPoint( "n1", - w, - h, - 1 ); setPoint( "n2", w, - h, - 1 ); setPoint( "n3", - w, h, - 1 ); setPoint( "n4", w, h, - 1 ); // far setPoint( "f1", - w, - h, 1 ); setPoint( "f2", w, - h, 1 ); setPoint( "f3", - w, h, 1 ); setPoint( "f4", w, h, 1 ); // up setPoint( "u1", w * 0.7, h * 1.1, - 1 ); setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", 0, h * 2, - 1 ); // cross setPoint( "cf1", - w, 0, 1 ); setPoint( "cf2", w, 0, 1 ); setPoint( "cf3", 0, - h, 1 ); setPoint( "cf4", 0, h, 1 ); setPoint( "cn1", - w, 0, - 1 ); setPoint( "cn2", w, 0, - 1 ); setPoint( "cn3", 0, - h, - 1 ); setPoint( "cn4", 0, h, - 1 ); geometry.getAttribute( "position" ).needsUpdate = true; }; }(); /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 */ function BoxHelper( object, color ) { this.object = object; if ( color === undefined ) color = 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = new Float32Array( 8 * 3 ); var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.matrixAutoUpdate = false; this.update(); } BoxHelper.prototype = Object.create( LineSegments.prototype ); BoxHelper.prototype.constructor = BoxHelper; BoxHelper.prototype.update = ( function () { var box = new Box3(); return function update( object ) { if ( object !== undefined ) { console.warn( "THREE.BoxHelper: .update() has no longer arguments." ); } if ( this.object !== undefined ) { box.setFromObject( this.object ); } if ( box.isEmpty() ) return; var min = box.min; var max = box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ var position = this.geometry.attributes.position; var array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); }; } )(); BoxHelper.prototype.setFromObject = function ( object ) { this.object = object; this.update(); return this; }; /** * @author WestLangley / http://github.com/WestLangley */ function Box3Helper( box, hex ) { this.type = "Box3Helper"; this.box = box; var color = ( hex !== undefined ) ? hex : 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); this.geometry.computeBoundingSphere(); } Box3Helper.prototype = Object.create( LineSegments.prototype ); Box3Helper.prototype.constructor = Box3Helper; Box3Helper.prototype.updateMatrixWorld = function ( force ) { var box = this.box; if ( box.isEmpty() ) return; box.getCenter( this.position ); box.getSize( this.scale ); this.scale.multiplyScalar( 0.5 ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley */ function PlaneHelper( plane, size, hex ) { this.type = "PlaneHelper"; this.plane = plane; this.size = ( size === undefined ) ? 1 : size; var color = ( hex !== undefined ) ? hex : 0xffff00; var positions = [ 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, 0, 1, 0, 0, 0 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( positions, 3 ) ); geometry.computeBoundingSphere(); Line.call( this, geometry, new LineBasicMaterial( { color: color } ) ); // var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ]; var geometry2 = new BufferGeometry(); geometry2.addAttribute( "position", new Float32BufferAttribute( positions2, 3 ) ); geometry2.computeBoundingSphere(); this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) ); } PlaneHelper.prototype = Object.create( Line.prototype ); PlaneHelper.prototype.constructor = PlaneHelper; PlaneHelper.prototype.updateMatrixWorld = function ( force ) { var scale = - this.plane.constant; if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here this.lookAt( this.plane.normal ); Object3D.prototype.updateMatrixWorld.call( this, force ); }; /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * * Parameters: * dir - Vector3 * origin - Vector3 * length - Number * color - color in hex value * headLength - Number * headWidth - Number */ var lineGeometry, coneGeometry; function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { // dir is assumed to be normalized Object3D.call( this ); if ( color === undefined ) color = 0xffff00; if ( length === undefined ) length = 1; if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; if ( lineGeometry === undefined ) { lineGeometry = new BufferGeometry(); lineGeometry.addAttribute( "position", new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); coneGeometry.translate( 0, - 0.5, 0 ); } this.position.copy( origin ); this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } ArrowHelper.prototype = Object.create( Object3D.prototype ); ArrowHelper.prototype.constructor = ArrowHelper; ArrowHelper.prototype.setDirection = ( function () { var axis = new Vector3(); var radians; return function setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { axis.set( dir.z, 0, - dir.x ).normalize(); radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( axis, radians ); } }; }() ); ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); }; ArrowHelper.prototype.setColor = function ( color ) { this.line.material.color.copy( color ); this.cone.material.color.copy( color ); }; /** * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ */ function AxesHelper( size ) { size = size || 1; var vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; var colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; var geometry = new BufferGeometry(); geometry.addAttribute( "position", new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( "color", new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } AxesHelper.prototype = Object.create( LineSegments.prototype ); AxesHelper.prototype.constructor = AxesHelper; /** * @author mrdoob / http://mrdoob.com/ */ function Face4( a, b, c, d, normal, color, materialIndex ) { console.warn( "THREE.Face4 has been removed. A THREE.Face3 will be created instead." ); return new Face3( a, b, c, normal, color, materialIndex ); } var LineStrip = 0; var LinePieces = 1; function MeshFaceMaterial( materials ) { console.warn( "THREE.MeshFaceMaterial has been removed. Use an Array instead." ); return materials; } function MultiMaterial( materials ) { if ( materials === undefined ) materials = []; console.warn( "THREE.MultiMaterial has been removed. Use an Array instead." ); materials.isMultiMaterial = true; materials.materials = materials; materials.clone = function () { return materials.slice(); }; return materials; } function PointCloud( geometry, material ) { console.warn( "THREE.PointCloud has been renamed to THREE.Points." ); return new Points( geometry, material ); } function Particle( material ) { console.warn( "THREE.Particle has been renamed to THREE.Sprite." ); return new Sprite( material ); } function ParticleSystem( geometry, material ) { console.warn( "THREE.ParticleSystem has been renamed to THREE.Points." ); return new Points( geometry, material ); } function PointCloudMaterial( parameters ) { console.warn( "THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleBasicMaterial( parameters ) { console.warn( "THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function ParticleSystemMaterial( parameters ) { console.warn( "THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial." ); return new PointsMaterial( parameters ); } function Vertex( x, y, z ) { console.warn( "THREE.Vertex has been removed. Use THREE.Vector3 instead." ); return new Vector3( x, y, z ); } // function DynamicBufferAttribute( array, itemSize ) { console.warn( "THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead." ); return new BufferAttribute( array, itemSize ).setDynamic( true ); } function Int8Attribute( array, itemSize ) { console.warn( "THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead." ); return new Int8BufferAttribute( array, itemSize ); } function Uint8Attribute( array, itemSize ) { console.warn( "THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead." ); return new Uint8BufferAttribute( array, itemSize ); } function Uint8ClampedAttribute( array, itemSize ) { console.warn( "THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead." ); return new Uint8ClampedBufferAttribute( array, itemSize ); } function Int16Attribute( array, itemSize ) { console.warn( "THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead." ); return new Int16BufferAttribute( array, itemSize ); } function Uint16Attribute( array, itemSize ) { console.warn( "THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead." ); return new Uint16BufferAttribute( array, itemSize ); } function Int32Attribute( array, itemSize ) { console.warn( "THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead." ); return new Int32BufferAttribute( array, itemSize ); } function Uint32Attribute( array, itemSize ) { console.warn( "THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead." ); return new Uint32BufferAttribute( array, itemSize ); } function Float32Attribute( array, itemSize ) { console.warn( "THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead." ); return new Float32BufferAttribute( array, itemSize ); } function Float64Attribute( array, itemSize ) { console.warn( "THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead." ); return new Float64BufferAttribute( array, itemSize ); } // Curve.create = function ( construct, getPoint ) { console.log( "THREE.Curve.create() has been deprecated" ); construct.prototype = Object.create( Curve.prototype ); construct.prototype.constructor = construct; construct.prototype.getPoint = getPoint; return construct; }; // Object.assign( CurvePath.prototype, { createPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from path points (for Line or Points objects) var pts = this.getPoints( divisions ); return this.createGeometry( pts ); }, createSpacedPointsGeometry: function ( divisions ) { console.warn( "THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); // generate geometry from equidistant sampling along the path var pts = this.getSpacedPoints( divisions ); return this.createGeometry( pts ); }, createGeometry: function ( points ) { console.warn( "THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead." ); var geometry = new Geometry(); for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return geometry; } } ); // Object.assign( Path.prototype, { fromPoints: function ( points ) { console.warn( "THREE.Path: .fromPoints() has been renamed to .setFromPoints()." ); this.setFromPoints( points ); } } ); // function ClosedSplineCurve3( points ) { console.warn( "THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; this.closed = true; } ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function SplineCurve3( points ) { console.warn( "THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function Spline( points ) { console.warn( "THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead." ); CatmullRomCurve3.call( this, points ); this.type = "catmullrom"; } Spline.prototype = Object.create( CatmullRomCurve3.prototype ); Object.assign( Spline.prototype, { initFromArray: function ( /* a */ ) { console.error( "THREE.Spline: .initFromArray() has been removed." ); }, getControlPointsArray: function ( /* optionalTarget */ ) { console.error( "THREE.Spline: .getControlPointsArray() has been removed." ); }, reparametrizeByArcLength: function ( /* samplingCoef */ ) { console.error( "THREE.Spline: .reparametrizeByArcLength() has been removed." ); } } ); // function AxisHelper( size ) { console.warn( "THREE.AxisHelper has been renamed to THREE.AxesHelper." ); return new AxesHelper( size ); } function BoundingBoxHelper( object, color ) { console.warn( "THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead." ); return new BoxHelper( object, color ); } function EdgesHelper( object, hex ) { console.warn( "THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead." ); return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } GridHelper.prototype.setColors = function () { console.error( "THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead." ); }; SkeletonHelper.prototype.update = function () { console.error( "THREE.SkeletonHelper: update() no longer needs to be called." ); }; function WireframeHelper( object, hex ) { console.warn( "THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead." ); return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } // Object.assign( Loader.prototype, { extractUrlBase: function ( url ) { console.warn( "THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead." ); return LoaderUtils.extractUrlBase( url ); } } ); function XHRLoader( manager ) { console.warn( "THREE.XHRLoader has been renamed to THREE.FileLoader." ); return new FileLoader( manager ); } function BinaryTextureLoader( manager ) { console.warn( "THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader." ); return new DataTextureLoader( manager ); } // Object.assign( Box2.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box2: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box2: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box2: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Object.assign( Box3.prototype, { center: function ( optionalTarget ) { console.warn( "THREE.Box3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( "THREE.Box3: .empty() has been renamed to .isEmpty()." ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( "THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); }, size: function ( optionalTarget ) { console.warn( "THREE.Box3: .size() has been renamed to .getSize()." ); return this.getSize( optionalTarget ); } } ); Line3.prototype.center = function ( optionalTarget ) { console.warn( "THREE.Line3: .center() has been renamed to .getCenter()." ); return this.getCenter( optionalTarget ); }; Object.assign( _Math, { random16: function () { console.warn( "THREE.Math: .random16() has been deprecated. Use Math.random() instead." ); return Math.random(); }, nearestPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo()." ); return _Math.floorPowerOfTwo( value ); }, nextPowerOfTwo: function ( value ) { console.warn( "THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo()." ); return _Math.ceilPowerOfTwo( value ); } } ); Object.assign( Matrix3.prototype, { flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead." ); return vector.applyMatrix3( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix3: .multiplyVector3Array() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix3: .applyToVector3Array() has been removed." ); } } ); Object.assign( Matrix4.prototype, { extractPosition: function ( m ) { console.warn( "THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()." ); return this.copyPosition( m ); }, flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, getPosition: function () { var v1; return function getPosition() { if ( v1 === undefined ) v1 = new Vector3(); console.warn( "THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead." ); return v1.setFromMatrixColumn( this, 3 ); }; }(), setRotationFromQuaternion: function ( q ) { console.warn( "THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()." ); return this.makeRotationFromQuaternion( q ); }, multiplyToArray: function () { console.warn( "THREE.Matrix4: .multiplyToArray() has been removed." ); }, multiplyVector3: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector4: function ( vector ) { console.warn( "THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, multiplyVector3Array: function ( /* a */ ) { console.error( "THREE.Matrix4: .multiplyVector3Array() has been removed." ); }, rotateAxis: function ( v ) { console.warn( "THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead." ); v.transformDirection( this ); }, crossVector: function ( vector ) { console.warn( "THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead." ); return vector.applyMatrix4( this ); }, translate: function () { console.error( "THREE.Matrix4: .translate() has been removed." ); }, rotateX: function () { console.error( "THREE.Matrix4: .rotateX() has been removed." ); }, rotateY: function () { console.error( "THREE.Matrix4: .rotateY() has been removed." ); }, rotateZ: function () { console.error( "THREE.Matrix4: .rotateZ() has been removed." ); }, rotateByAxis: function () { console.error( "THREE.Matrix4: .rotateByAxis() has been removed." ); }, applyToBuffer: function ( buffer /*, offset, length */ ) { console.warn( "THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead." ); return this.applyToBufferAttribute( buffer ); }, applyToVector3Array: function ( /* array, offset, length */ ) { console.error( "THREE.Matrix4: .applyToVector3Array() has been removed." ); }, makeFrustum: function ( left, right, bottom, top, near, far ) { console.warn( "THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead." ); return this.makePerspective( left, right, top, bottom, near, far ); } } ); Plane.prototype.isIntersectionLine = function ( line ) { console.warn( "THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()." ); return this.intersectsLine( line ); }; Quaternion.prototype.multiplyVector3 = function ( vector ) { console.warn( "THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead." ); return vector.applyQuaternion( this ); }; Object.assign( Ray.prototype, { isIntersectionBox: function ( box ) { console.warn( "THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()." ); return this.intersectsBox( box ); }, isIntersectionPlane: function ( plane ) { console.warn( "THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()." ); return this.intersectsPlane( plane ); }, isIntersectionSphere: function ( sphere ) { console.warn( "THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()." ); return this.intersectsSphere( sphere ); } } ); Object.assign( Triangle.prototype, { area: function () { console.warn( "THREE.Triangle: .area() has been renamed to .getArea()." ); return this.getArea(); }, barycoordFromPoint: function ( point, target ) { console.warn( "THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()." ); return this.getBarycoord( point, target ); }, midpoint: function ( target ) { console.warn( "THREE.Triangle: .midpoint() has been renamed to .getMidpoint()." ); return this.getMidpoint( target ); }, normal: function ( target ) { console.warn( "THREE.Triangle: .normal() has been renamed to .getNormal()." ); return this.getNormal( target ); }, plane: function ( target ) { console.warn( "THREE.Triangle: .plane() has been renamed to .getPlane()." ); return this.getPlane( target ); } } ); Object.assign( Triangle, { barycoordFromPoint: function ( point, a, b, c, target ) { console.warn( "THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()." ); return Triangle.getBarycoord( point, a, b, c, target ); }, normal: function ( a, b, c, target ) { console.warn( "THREE.Triangle: .normal() has been renamed to .getNormal()." ); return Triangle.getNormal( a, b, c, target ); } } ); Object.assign( Shape.prototype, { extractAllPoints: function ( divisions ) { console.warn( "THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead." ); return this.extractPoints( divisions ); }, extrude: function ( options ) { console.warn( "THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead." ); return new ExtrudeGeometry( this, options ); }, makeGeometry: function ( options ) { console.warn( "THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead." ); return new ShapeGeometry( this, options ); } } ); Object.assign( Vector2.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector3.prototype, { setEulerFromRotationMatrix: function () { console.error( "THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead." ); }, setEulerFromQuaternion: function () { console.error( "THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead." ); }, getPositionFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()." ); return this.setFromMatrixPosition( m ); }, getScaleFromMatrix: function ( m ) { console.warn( "THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()." ); return this.setFromMatrixScale( m ); }, getColumnFromMatrix: function ( index, matrix ) { console.warn( "THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()." ); return this.setFromMatrixColumn( matrix, index ); }, applyProjection: function ( m ) { console.warn( "THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead." ); return this.applyMatrix4( m ); }, fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, distanceToManhattan: function ( v ) { console.warn( "THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()." ); return this.manhattanDistanceTo( v ); }, lengthManhattan: function () { console.warn( "THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); Object.assign( Vector4.prototype, { fromAttribute: function ( attribute, index, offset ) { console.warn( "THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()." ); return this.fromBufferAttribute( attribute, index, offset ); }, lengthManhattan: function () { console.warn( "THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()." ); return this.manhattanLength(); } } ); // Object.assign( Geometry.prototype, { computeTangents: function () { console.error( "THREE.Geometry: .computeTangents() has been removed." ); }, computeLineDistances: function () { console.error( "THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead." ); } } ); Object.assign( Object3D.prototype, { getChildByName: function ( name ) { console.warn( "THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()." ); return this.getObjectByName( name ); }, renderDepth: function () { console.warn( "THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead." ); }, translate: function ( distance, axis ) { console.warn( "THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead." ); return this.translateOnAxis( axis, distance ); }, getWorldRotation: function () { console.error( "THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead." ); } } ); Object.defineProperties( Object3D.prototype, { eulerOrder: { get: function () { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); return this.rotation.order; }, set: function ( value ) { console.warn( "THREE.Object3D: .eulerOrder is now .rotation.order." ); this.rotation.order = value; } }, 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." ); } } } ); Object.defineProperties( LOD.prototype, { objects: { get: function () { console.warn( "THREE.LOD: .objects has been renamed to .levels." ); return this.levels; } } } ); Object.defineProperty( Skeleton.prototype, "useVertexTexture", { get: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); }, set: function () { console.warn( "THREE.Skeleton: useVertexTexture has been removed." ); } } ); Object.defineProperty( Curve.prototype, "__arcLengthDivisions", { get: function () { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); return this.arcLengthDivisions; }, set: function ( value ) { console.warn( "THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions." ); this.arcLengthDivisions = value; } } ); // PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup." ); if ( filmGauge !== undefined ) this.filmGauge = filmGauge; this.setFocalLength( focalLength ); }; // Object.defineProperties( Light.prototype, { onlyShadow: { set: function () { console.warn( "THREE.Light: .onlyShadow has been removed." ); } }, shadowCameraFov: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFov is now .shadow.camera.fov." ); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraLeft is now .shadow.camera.left." ); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraRight is now .shadow.camera.right." ); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraTop is now .shadow.camera.top." ); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom." ); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraNear is now .shadow.camera.near." ); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function ( value ) { console.warn( "THREE.Light: .shadowCameraFar is now .shadow.camera.far." ); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn( "THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead." ); } }, shadowBias: { set: function ( value ) { console.warn( "THREE.Light: .shadowBias is now .shadow.bias." ); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn( "THREE.Light: .shadowDarkness has been removed." ); } }, shadowMapWidth: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapWidth is now .shadow.mapSize.width." ); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function ( value ) { console.warn( "THREE.Light: .shadowMapHeight is now .shadow.mapSize.height." ); this.shadow.mapSize.height = value; } } } ); // Object.defineProperties( BufferAttribute.prototype, { length: { get: function () { console.warn( "THREE.BufferAttribute: .length has been deprecated. Use .count instead." ); return this.array.length; } }, copyIndicesArray: function ( /* indices */ ) { console.error( "THREE.BufferAttribute: .copyIndicesArray() has been removed." ); } } ); Object.assign( BufferGeometry.prototype, { addIndex: function ( index ) { console.warn( "THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()." ); this.setIndex( index ); }, addDrawCall: function ( start, count, indexOffset ) { if ( indexOffset !== undefined ) { console.warn( "THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset." ); } console.warn( "THREE.BufferGeometry: .addDrawCall() is now .addGroup()." ); this.addGroup( start, count ); }, clearDrawCalls: function () { console.warn( "THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()." ); this.clearGroups(); }, computeTangents: function () { console.warn( "THREE.BufferGeometry: .computeTangents() has been removed." ); }, computeOffsets: function () { console.warn( "THREE.BufferGeometry: .computeOffsets() has been removed." ); } } ); Object.defineProperties( BufferGeometry.prototype, { drawcalls: { get: function () { console.error( "THREE.BufferGeometry: .drawcalls has been renamed to .groups." ); return this.groups; } }, offsets: { get: function () { console.warn( "THREE.BufferGeometry: .offsets has been renamed to .groups." ); return this.groups; } } } ); // Object.assign( ExtrudeBufferGeometry.prototype, { getArrays: function () { console.error( "THREE.ExtrudeBufferGeometry: .getArrays() has been removed." ); }, addShapeList: function () { console.error( "THREE.ExtrudeBufferGeometry: .addShapeList() has been removed." ); }, addShape: function () { console.error( "THREE.ExtrudeBufferGeometry: .addShape() has been removed." ); } } ); // Object.defineProperties( Uniform.prototype, { dynamic: { set: function () { console.warn( "THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead." ); } }, onUpdate: { value: function () { console.warn( "THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead." ); return this; } } } ); // Object.defineProperties( Material.prototype, { wrapAround: { get: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); }, set: function () { console.warn( "THREE.Material: .wrapAround has been removed." ); } }, wrapRGB: { get: function () { console.warn( "THREE.Material: .wrapRGB has been removed." ); return new Color(); } }, shading: { get: function () { console.error( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); }, set: function ( value ) { console.warn( "THREE." + this.type + ": .shading has been removed. Use the boolean .flatShading instead." ); this.flatShading = ( value === FlatShading ); } } } ); Object.defineProperties( MeshPhongMaterial.prototype, { metal: { get: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead." ); return false; }, set: function () { console.warn( "THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead" ); } } } ); Object.defineProperties( ShaderMaterial.prototype, { derivatives: { get: function () { console.warn( "THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); return this.extensions.derivatives; }, set: function ( value ) { console.warn( "THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives." ); this.extensions.derivatives = value; } } } ); // Object.assign( WebGLRenderer.prototype, { animate: function ( callback ) { console.warn( "THREE.WebGLRenderer: .animate() is now .setAnimationLoop()." ); this.setAnimationLoop( callback ); }, getCurrentRenderTarget: function () { console.warn( "THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()." ); return this.getRenderTarget(); }, getMaxAnisotropy: function () { console.warn( "THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()." ); return this.capabilities.getMaxAnisotropy(); }, getPrecision: function () { console.warn( "THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision." ); return this.capabilities.precision; }, resetGLState: function () { console.warn( "THREE.WebGLRenderer: .resetGLState() is now .state.reset()." ); return this.state.reset(); }, supportsFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( "OES_texture_float" )." ); return this.extensions.get( "OES_texture_float" ); }, supportsHalfFloatTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( "OES_texture_half_float" )." ); return this.extensions.get( "OES_texture_half_float" ); }, supportsStandardDerivatives: function () { console.warn( "THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( "OES_standard_derivatives" )." ); return this.extensions.get( "OES_standard_derivatives" ); }, supportsCompressedTextureS3TC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( "WEBGL_compressed_texture_s3tc" )." ); return this.extensions.get( "WEBGL_compressed_texture_s3tc" ); }, supportsCompressedTexturePVRTC: function () { console.warn( "THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( "WEBGL_compressed_texture_pvrtc" )." ); return this.extensions.get( "WEBGL_compressed_texture_pvrtc" ); }, supportsBlendMinMax: function () { console.warn( "THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( "EXT_blend_minmax" )." ); return this.extensions.get( "EXT_blend_minmax" ); }, supportsVertexTextures: function () { console.warn( "THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures." ); return this.capabilities.vertexTextures; }, supportsInstancedArrays: function () { console.warn( "THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( "ANGLE_instanced_arrays" )." ); return this.extensions.get( "ANGLE_instanced_arrays" ); }, enableScissorTest: function ( boolean ) { console.warn( "THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()." ); this.setScissorTest( boolean ); }, initMaterial: function () { console.warn( "THREE.WebGLRenderer: .initMaterial() has been removed." ); }, addPrePlugin: function () { console.warn( "THREE.WebGLRenderer: .addPrePlugin() has been removed." ); }, addPostPlugin: function () { console.warn( "THREE.WebGLRenderer: .addPostPlugin() has been removed." ); }, updateShadowMap: function () { console.warn( "THREE.WebGLRenderer: .updateShadowMap() has been removed." ); }, setFaceCulling: function () { console.warn( "THREE.WebGLRenderer: .setFaceCulling() has been removed." ); } } ); Object.defineProperties( WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled." ); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type." ); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { console.warn( "THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead." ); return undefined; }, set: function ( /* value */ ) { console.warn( "THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead." ); } } } ); Object.defineProperties( WebGLShadowMap.prototype, { cullFace: { get: function () { console.warn( "THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead." ); return undefined; }, set: function ( /* cullFace */ ) { 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." ); return undefined; }, 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." ); return undefined; }, set: function () { console.warn( "THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead." ); } } } ); // Object.defineProperties( WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); return this.texture.wrapS; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS." ); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); return this.texture.wrapT; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT." ); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); return this.texture.magFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter." ); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); return this.texture.minFilter; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter." ); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); return this.texture.anisotropy; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy." ); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); return this.texture.offset; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .offset is now .texture.offset." ); this.texture.offset = value; } }, repeat: { get: function () { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); return this.texture.repeat; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .repeat is now .texture.repeat." ); this.texture.repeat = value; } }, format: { get: function () { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); return this.texture.format; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .format is now .texture.format." ); this.texture.format = value; } }, type: { get: function () { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); return this.texture.type; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .type is now .texture.type." ); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); return this.texture.generateMipmaps; }, set: function ( value ) { console.warn( "THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps." ); this.texture.generateMipmaps = value; } } } ); // Audio.prototype.load = function ( file ) { console.warn( "THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead." ); var scope = this; var audioLoader = new AudioLoader(); audioLoader.load( file, function ( buffer ) { scope.setBuffer( buffer ); } ); return this; }; AudioAnalyser.prototype.getData = function () { console.warn( "THREE.AudioAnalyser: .getData() is now .getFrequencyData()." ); return this.getFrequencyData(); }; // CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { console.warn( "THREE.CubeCamera: .updateCubeMap() is now .update()." ); return this.update( renderer, scene ); }; // var GeometryUtils = { merge: function ( geometry1, geometry2, materialIndexOffset ) { console.warn( "THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead." ); var matrix; if ( geometry2.isMesh ) { geometry2.matrixAutoUpdate && geometry2.updateMatrix(); matrix = geometry2.matrix; geometry2 = geometry2.geometry; } geometry1.merge( geometry2, matrix, materialIndexOffset ); }, center: function ( geometry ) { console.warn( "THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead." ); return geometry.center(); } }; var ImageUtils = { crossOrigin: undefined, loadTexture: function ( url, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead." ); var loader = new TextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( url, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadTextureCube: function ( urls, mapping, onLoad, onError ) { console.warn( "THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead." ); var loader = new CubeTextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( urls, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadCompressedTexture: function () { console.error( "THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead." ); }, loadCompressedTextureCube: function () { console.error( "THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead." ); } }; // function Projector() { console.error( "THREE.Projector has been moved to /examples/js/renderers/Projector.js." ); this.projectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .projectVector() is now vector.project()." ); vector.project( camera ); }; this.unprojectVector = function ( vector, camera ) { console.warn( "THREE.Projector: .unprojectVector() is now vector.unproject()." ); vector.unproject( camera ); }; this.pickingRay = function () { console.error( "THREE.Projector: .pickingRay() is now raycaster.setFromCamera()." ); }; } // function CanvasRenderer() { console.error( "THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js" ); this.domElement = document.createElementNS( "http://www.w3.org/1999/xhtml", "canvas" ); this.clear = function () {}; this.render = function () {}; this.setClearColor = function () {}; this.setSize = function () {}; } // var SceneUtils = { createMultiMaterialObject: function ( /* geometry, materials */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); }, detach: function ( /* child, parent, scene */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); }, attach: function ( /* child, scene, parent */ ) { console.error( "THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js" ); } }; // function LensFlare() { console.error( "THREE.LensFlare has been moved to /examples/js/objects/Lensflare.js" ); } exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.ShaderLib = ShaderLib; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.ShaderChunk = ShaderChunk; exports.FogExp2 = FogExp2; exports.Fog = Fog; exports.Scene = Scene; exports.Sprite = Sprite; exports.LOD = LOD; exports.SkinnedMesh = SkinnedMesh; exports.Skeleton = Skeleton; exports.Bone = Bone; exports.Mesh = Mesh; exports.LineSegments = LineSegments; exports.LineLoop = LineLoop; exports.Line = Line; exports.Points = Points; exports.Group = Group; exports.VideoTexture = VideoTexture; exports.DataTexture = DataTexture; exports.CompressedTexture = CompressedTexture; exports.CubeTexture = CubeTexture; exports.CanvasTexture = CanvasTexture; exports.DepthTexture = DepthTexture; exports.Texture = Texture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.DataTextureLoader = DataTextureLoader; exports.CubeTextureLoader = CubeTextureLoader; exports.TextureLoader = TextureLoader; exports.ObjectLoader = ObjectLoader; exports.MaterialLoader = MaterialLoader; exports.BufferGeometryLoader = BufferGeometryLoader; exports.DefaultLoadingManager = DefaultLoadingManager; exports.LoadingManager = LoadingManager; exports.JSONLoader = JSONLoader; exports.ImageLoader = ImageLoader; exports.ImageBitmapLoader = ImageBitmapLoader; exports.FontLoader = FontLoader; exports.FileLoader = FileLoader; exports.Loader = Loader; exports.LoaderUtils = LoaderUtils; exports.Cache = Cache; exports.AudioLoader = AudioLoader; exports.SpotLightShadow = SpotLightShadow; exports.SpotLight = SpotLight; exports.PointLight = PointLight; exports.RectAreaLight = RectAreaLight; exports.HemisphereLight = HemisphereLight; exports.DirectionalLightShadow = DirectionalLightShadow; exports.DirectionalLight = DirectionalLight; exports.AmbientLight = AmbientLight; exports.LightShadow = LightShadow; exports.Light = Light; exports.StereoCamera = StereoCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.OrthographicCamera = OrthographicCamera; exports.CubeCamera = CubeCamera; exports.ArrayCamera = ArrayCamera; exports.Camera = Camera; exports.AudioListener = AudioListener; exports.PositionalAudio = PositionalAudio; exports.AudioContext = AudioContext; exports.AudioAnalyser = AudioAnalyser; exports.Audio = Audio; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.StringKeyframeTrack = StringKeyframeTrack; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.PropertyMixer = PropertyMixer; exports.PropertyBinding = PropertyBinding; exports.KeyframeTrack = KeyframeTrack; exports.AnimationUtils = AnimationUtils; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationMixer = AnimationMixer; exports.AnimationClip = AnimationClip; exports.Uniform = Uniform; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.BufferGeometry = BufferGeometry; exports.Geometry = Geometry; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InterleavedBuffer = InterleavedBuffer; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.Face3 = Face3; exports.Object3D = Object3D; exports.Raycaster = Raycaster; exports.Layers = Layers; exports.EventDispatcher = EventDispatcher; exports.Clock = Clock; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.LinearInterpolant = LinearInterpolant; exports.DiscreteInterpolant = DiscreteInterpolant; exports.CubicInterpolant = CubicInterpolant; exports.Interpolant = Interpolant; exports.Triangle = Triangle; exports.Math = _Math; exports.Spherical = Spherical; exports.Cylindrical = Cylindrical; exports.Plane = Plane; exports.Frustum = Frustum; exports.Sphere = Sphere; exports.Ray = Ray; exports.Matrix4 = Matrix4; exports.Matrix3 = Matrix3; exports.Box3 = Box3; exports.Box2 = Box2; exports.Line3 = Line3; exports.Euler = Euler; exports.Vector4 = Vector4; exports.Vector3 = Vector3; exports.Vector2 = Vector2; exports.Quaternion = Quaternion; exports.Color = Color; exports.ImmediateRenderObject = ImmediateRenderObject; exports.VertexNormalsHelper = VertexNormalsHelper; exports.SpotLightHelper = SpotLightHelper; exports.SkeletonHelper = SkeletonHelper; exports.PointLightHelper = PointLightHelper; exports.RectAreaLightHelper = RectAreaLightHelper; exports.HemisphereLightHelper = HemisphereLightHelper; exports.GridHelper = GridHelper; exports.PolarGridHelper = PolarGridHelper; exports.FaceNormalsHelper = FaceNormalsHelper; exports.DirectionalLightHelper = DirectionalLightHelper; exports.CameraHelper = CameraHelper; exports.BoxHelper = BoxHelper; exports.Box3Helper = Box3Helper; exports.PlaneHelper = PlaneHelper; exports.ArrowHelper = ArrowHelper; exports.AxesHelper = AxesHelper; exports.Shape = Shape; exports.Path = Path; exports.ShapePath = ShapePath; exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; exports.ShapeUtils = ShapeUtils; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; exports.ParametricGeometry = ParametricGeometry; exports.ParametricBufferGeometry = ParametricBufferGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OctahedronBufferGeometry = OctahedronBufferGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; exports.TubeGeometry = TubeGeometry; exports.TubeBufferGeometry = TubeBufferGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusBufferGeometry = TorusBufferGeometry; exports.TextGeometry = TextGeometry; exports.TextBufferGeometry = TextBufferGeometry; exports.SphereGeometry = SphereGeometry; exports.SphereBufferGeometry = SphereBufferGeometry; exports.RingGeometry = RingGeometry; exports.RingBufferGeometry = RingBufferGeometry; exports.PlaneGeometry = PlaneGeometry; exports.PlaneBufferGeometry = PlaneBufferGeometry; exports.LatheGeometry = LatheGeometry; exports.LatheBufferGeometry = LatheBufferGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapeBufferGeometry = ShapeBufferGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry; exports.EdgesGeometry = EdgesGeometry; exports.ConeGeometry = ConeGeometry; exports.ConeBufferGeometry = ConeBufferGeometry; exports.CylinderGeometry = CylinderGeometry; exports.CylinderBufferGeometry = CylinderBufferGeometry; exports.CircleGeometry = CircleGeometry; exports.CircleBufferGeometry = CircleBufferGeometry; exports.BoxGeometry = BoxGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.ShadowMaterial = ShadowMaterial; exports.SpriteMaterial = SpriteMaterial; exports.RawShaderMaterial = RawShaderMaterial; exports.ShaderMaterial = ShaderMaterial; exports.PointsMaterial = PointsMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshBasicMaterial = MeshBasicMaterial; exports.LineDashedMaterial = LineDashedMaterial; exports.LineBasicMaterial = LineBasicMaterial; exports.Material = Material; exports.Float64BufferAttribute = Float64BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.BufferAttribute = BufferAttribute; exports.ArcCurve = ArcCurve; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.CubicBezierCurve = CubicBezierCurve; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.EllipseCurve = EllipseCurve; exports.LineCurve = LineCurve; exports.LineCurve3 = LineCurve3; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.SplineCurve = SplineCurve; exports.REVISION = REVISION; exports.MOUSE = MOUSE; exports.CullFaceNone = CullFaceNone; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.FrontFaceDirectionCW = FrontFaceDirectionCW; exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; exports.BasicShadowMap = BasicShadowMap; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.FrontSide = FrontSide; exports.BackSide = BackSide; exports.DoubleSide = DoubleSide; exports.FlatShading = FlatShading; exports.SmoothShading = SmoothShading; exports.NoColors = NoColors; exports.FaceColors = FaceColors; exports.VertexColors = VertexColors; exports.NoBlending = NoBlending; exports.NormalBlending = NormalBlending; exports.AdditiveBlending = AdditiveBlending; exports.SubtractiveBlending = SubtractiveBlending; exports.MultiplyBlending = MultiplyBlending; exports.CustomBlending = CustomBlending; exports.AddEquation = AddEquation; exports.SubtractEquation = SubtractEquation; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.MinEquation = MinEquation; exports.MaxEquation = MaxEquation; exports.ZeroFactor = ZeroFactor; exports.OneFactor = OneFactor; exports.SrcColorFactor = SrcColorFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.SrcAlphaFactor = SrcAlphaFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.DstAlphaFactor = DstAlphaFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.NeverDepth = NeverDepth; exports.AlwaysDepth = AlwaysDepth; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.EqualDepth = EqualDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterDepth = GreaterDepth; exports.NotEqualDepth = NotEqualDepth; exports.MultiplyOperation = MultiplyOperation; exports.MixOperation = MixOperation; exports.AddOperation = AddOperation; exports.NoToneMapping = NoToneMapping; exports.LinearToneMapping = LinearToneMapping; exports.ReinhardToneMapping = ReinhardToneMapping; exports.Uncharted2ToneMapping = Uncharted2ToneMapping; exports.CineonToneMapping = CineonToneMapping; exports.UVMapping = UVMapping; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.SphericalReflectionMapping = SphericalReflectionMapping; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.RepeatWrapping = RepeatWrapping; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.NearestFilter = NearestFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.LinearFilter = LinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.UnsignedByteType = UnsignedByteType; exports.ByteType = ByteType; exports.ShortType = ShortType; exports.UnsignedShortType = UnsignedShortType; exports.IntType = IntType; exports.UnsignedIntType = UnsignedIntType; exports.FloatType = FloatType; exports.HalfFloatType = HalfFloatType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShort565Type = UnsignedShort565Type; exports.UnsignedInt248Type = UnsignedInt248Type; exports.AlphaFormat = AlphaFormat; exports.RGBFormat = RGBFormat; exports.RGBAFormat = RGBAFormat; exports.LuminanceFormat = LuminanceFormat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.RGBEFormat = RGBEFormat; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; exports.LoopOnce = LoopOnce; exports.LoopRepeat = LoopRepeat; exports.LoopPingPong = LoopPingPong; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.WrapAroundEnding = WrapAroundEnding; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.LinearEncoding = LinearEncoding; exports.sRGBEncoding = sRGBEncoding; exports.GammaEncoding = GammaEncoding; exports.RGBEEncoding = RGBEEncoding; exports.LogLuvEncoding = LogLuvEncoding; exports.RGBM7Encoding = RGBM7Encoding; exports.RGBM16Encoding = RGBM16Encoding; exports.RGBDEncoding = RGBDEncoding; exports.BasicDepthPacking = BasicDepthPacking; exports.RGBADepthPacking = RGBADepthPacking; exports.CubeGeometry = BoxGeometry; exports.Face4 = Face4; exports.LineStrip = LineStrip; exports.LinePieces = LinePieces; exports.MeshFaceMaterial = MeshFaceMaterial; exports.MultiMaterial = MultiMaterial; exports.PointCloud = PointCloud; exports.Particle = Particle; exports.ParticleSystem = ParticleSystem; exports.PointCloudMaterial = PointCloudMaterial; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Vertex = Vertex; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Int16Attribute = Int16Attribute; exports.Uint16Attribute = Uint16Attribute; exports.Int32Attribute = Int32Attribute; exports.Uint32Attribute = Uint32Attribute; exports.Float32Attribute = Float32Attribute; exports.Float64Attribute = Float64Attribute; exports.ClosedSplineCurve3 = ClosedSplineCurve3; exports.SplineCurve3 = SplineCurve3; exports.Spline = Spline; exports.AxisHelper = AxisHelper; exports.BoundingBoxHelper = BoundingBoxHelper; exports.EdgesHelper = EdgesHelper; exports.WireframeHelper = WireframeHelper; exports.XHRLoader = XHRLoader; exports.BinaryTextureLoader = BinaryTextureLoader; exports.GeometryUtils = GeometryUtils; exports.ImageUtils = ImageUtils; exports.Projector = Projector; exports.CanvasRenderer = CanvasRenderer; exports.SceneUtils = SceneUtils; exports.LensFlare = LensFlare; Object.defineProperty(exports, "__esModule", { value: true }); }))); module.exports = exports; /* */}),null);
-----
BloksPrimitives",["BloksAction","BloksAddChild","BloksAnimatedCreate","BloksAnimatedCreateColor","BloksAnimatedCreateCubicBezier","BloksAnimatedCreateDimension","BloksAnimatedGetCurrentColorValue","BloksAnimatedGetCurrentDimensionValue","BloksAnimatedGetCurrentValue","BloksAnimatedParallel","BloksAnimatedStart","BloksApply","BloksArrayAppend","BloksArrayClone","BloksArrayGet","BloksArrayIndexOf","BloksArrayLength","BloksArrayMake","BloksArrayPut","BloksArrayPutAndGet","BloksArrayRemove","BloksArrayRemoveAndGet","BloksArrayUpdate","BloksAsyncAction","BloksAsyncActionWithDataManifest","BloksAsyncLoadV2","BloksBindWithArrayV2","BloksBoolAnd","BloksBoolNot","BloksBoolOr","BloksChildAtIndex","BloksClipboardSetString","BloksCollection","BloksConstNumber","BloksCurrentTimeMillis","BloksDangerouslyGetTreeFromParseResult","BloksDatetimeTextProvider","BloksDefault","BloksDelay","BloksDismissBottomSheet","BloksDismissKeyboard","BloksDummy","BloksDummyComponent","BloksFindComponentContext","BloksFlexbox","BloksGetAttr","BloksGetState","BloksGetVariable2","BloksImage","BloksIndexOfChild","BloksInflateSync","BloksInsertChildrenAfter","BloksInternalMerge","BloksInternalShadow","BloksJsonEncode","BloksLogEvent","BloksMakeFlat","BloksMapClone","BloksMapGet","BloksMapKeys","BloksMapMake","BloksMapUpdate","BloksMatch","BloksMatchesRegex","BloksNumberAnd","BloksNumberEq","BloksNumberGt","BloksNumberLt","BloksNumberMod","BloksNumberOr","BloksNumberRand","BloksOnMount","BloksOpenBottomSheetV2","BloksOpenScreen","BloksOpenSendMessage","BloksOpenUrl","BloksOpenUrlV2","BloksParseEmbedded","BloksPattern","BloksReduce","BloksReflow","BloksRemoveChild","BloksRemoveChildrenBetween","BloksRenderLifecycleExtension","BloksReplaceChild","BloksReplaceChildren","BloksReplaceEmbeddedChild","BloksRichText","BloksRotate3D","BloksScale3D","BloksSessionStoreGet","BloksSetAttr","BloksShareText","BloksStringConcat","BloksStringLength","BloksStringValueOfNumber","BloksT3DFromArray","BloksTakeLast","BloksText","BloksTextInput","BloksTextInputGetText","BloksTextSpan","BloksToast","BloksTranslate3D","BloksVisibilityContextGetTimeSinceLastImpressionInMS","BloksVisibilityContextHasSeenBefore","BloksWhile","BloksWriteGlobalConsistencyStore","BloksWriteLocalState","Bloksf32Convert","Bloksf32NumberAdd","Bloksf32NumberDiv","Bloksf32NumberMul","Bloksf32NumberSub","Bloksi32Convert","Bloksi32NumberAdd","Bloksi32NumberDiv","Bloksi32NumberMul","Bloksi32NumberSub","Bloksi64ConstNumber","Bloksi64NumberAdd","Bloksi64NumberEq","Bloksi64NumberGt","Bloksi64NumberLt","Bloksi64NumberMod","Bloksi64NumberSub"],(function(a,b,c,d,e,f,g){a={"bk.components.internal.Action":c("BloksAction"),"bk.components.internal.Merge":c("BloksInternalMerge"),"bk.components.internal.Shadow":c("BloksInternalShadow"),"bk.components.Collection":c("BloksCollection"),"bk.components.DatetimeTextProvider":c("BloksDatetimeTextProvider"),"bk.components.Flexbox":c("BloksFlexbox"),"bk.components.Image":c("BloksImage"),"bk.components.OnMount":c("BloksOnMount"),"bk.components.RenderLifecycleExtension":c("BloksRenderLifecycleExtension"),"bk.components.RichText":c("BloksRichText"),"bk.components.Text":c("BloksText"),"bk.components.TextInput":c("BloksTextInput"),"bk.components.TextSpan":c("BloksTextSpan"),"bk.components.Video":c("BloksDummyComponent"),"bk.components.VideoVersion":c("BloksDummyComponent"),"bk.type.VideoIdentifier":c("BloksDummyComponent")};b={"bk.action.animated.Create":c("BloksAnimatedCreate"),"bk.action.animated.CreateColor":c("BloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("BloksAnimatedCreateDimension"),"bk.action.animated.easing.CreateCubicBezier":c("BloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("BloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("BloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentValue":c("BloksAnimatedGetCurrentValue"),"bk.action.animated.Parallel":c("BloksAnimatedParallel"),"bk.action.animated.Start":c("BloksAnimatedStart"),"bk.action.array.Append":c("BloksArrayAppend"),"bk.action.array.Clone":c("BloksArrayClone"),"bk.action.array.Get":c("BloksArrayGet"),"bk.action.array.IndexOf":c("BloksArrayIndexOf"),"bk.action.array.Length":c("BloksArrayLength"),"bk.action.array.Make":c("BloksArrayMake"),"bk.action.array.Put":c("BloksArrayPut"),"bk.action.array.PutAndGet":c("BloksArrayPutAndGet"),"bk.action.array.Remove":c("BloksArrayRemove"),"bk.action.array.RemoveAndGet":c("BloksArrayRemoveAndGet"),"bk.action.array.Update":c("BloksArrayUpdate"),"bk.action.bloks.AddChild":c("BloksAddChild"),"bk.action.bloks.AsyncAction":c("BloksAsyncAction"),"bk.action.bloks.AsyncActionWithDataManifest":c("BloksAsyncActionWithDataManifest"),"bk.action.bloks.AsyncLoadV2":c("BloksAsyncLoadV2"),"bk.action.bloks.ChildAtIndex":c("BloksChildAtIndex"),"bk.action.bloks.DangerouslyGetTreeFromParseResult":c("BloksDangerouslyGetTreeFromParseResult"),"bk.action.bloks.DismissBottomSheet":c("BloksDismissBottomSheet"),"bk.action.bloks.DismissKeyboard":c("BloksDismissKeyboard"),"bk.action.bloks.Find":c("BloksFindComponentContext"),"bk.action.bloks.FindContainer":c("BloksFindComponentContext"),"bk.action.bloks.GetState":c("BloksGetState"),"bk.action.bloks.GetVariable2":c("BloksGetVariable2"),"bk.action.bloks.IndexOfChild":c("BloksIndexOfChild"),"bk.action.bloks.InflateSync":c("BloksInflateSync"),"bk.action.bloks.InsertChildrenAfter":c("BloksInsertChildrenAfter"),"bk.action.bloks.OpenBottomSheetV2":c("BloksOpenBottomSheetV2"),"bk.action.bloks.OpenScreen":c("BloksOpenScreen"),"bk.action.bloks.ParseEmbedded":c("BloksParseEmbedded"),"bk.action.bloks.Reduce":c("BloksReduce"),"bk.action.bloks.Reflow":c("BloksReflow"),"bk.action.bloks.RemoveChild":c("BloksRemoveChild"),"bk.action.bloks.RemoveChildrenBetween":c("BloksRemoveChildrenBetween"),"bk.action.bloks.ReplaceChild":c("BloksReplaceChild"),"bk.action.bloks.ReplaceChildren":c("BloksReplaceChildren"),"bk.action.bloks.ReplaceEmbeddedChild":c("BloksReplaceEmbeddedChild"),"bk.action.bloks.WriteGlobalConsistencyStore":c("BloksWriteGlobalConsistencyStore"),"bk.action.bloks.WriteLocalState":c("BloksWriteLocalState"),"bk.action.bool.And":c("BloksBoolAnd"),"bk.action.bool.Not":c("BloksBoolNot"),"bk.action.bool.Or":c("BloksBoolOr"),"bk.action.component.GetAttr":c("BloksGetAttr"),"bk.action.component.SetAttr":c("BloksSetAttr"),"bk.action.core.Apply":c("BloksApply"),"bk.action.core.Default":c("BloksDefault"),"bk.action.core.Delay":c("BloksDelay"),"bk.action.core.FuncConst":c("BloksDummy"),"bk.action.core.GetArg":c("BloksDummy"),"bk.action.core.Match":c("BloksMatch"),"bk.action.core.Pattern":c("BloksPattern"),"bk.action.core.TakeLast":c("BloksTakeLast"),"bk.action.core.While":c("BloksWhile"),"bk.action.f32.Add":c("Bloksf32NumberAdd"),"bk.action.f32.Const":c("BloksConstNumber"),"bk.action.f32.Convert":c("Bloksf32Convert"),"bk.action.f32.Div":c("Bloksf32NumberDiv"),"bk.action.f32.Eq":c("BloksNumberEq"),"bk.action.f32.Gt":c("BloksNumberGt"),"bk.action.f32.Lt":c("BloksNumberLt"),"bk.action.f32.Mul":c("Bloksf32NumberMul"),"bk.action.f32.Sub":c("Bloksf32NumberSub"),"bk.action.flipper.SendData":c("BloksDummy"),"bk.action.function.BindWithArrayV2":c("BloksBindWithArrayV2"),"bk.action.i32.Add":c("Bloksi32NumberAdd"),"bk.action.i32.And":c("BloksNumberAnd"),"bk.action.i32.Const":c("BloksConstNumber"),"bk.action.i32.Convert":c("Bloksi32Convert"),"bk.action.i32.Div":c("Bloksi32NumberDiv"),"bk.action.i32.Eq":c("BloksNumberEq"),"bk.action.i32.Gt":c("BloksNumberGt"),"bk.action.i32.Lt":c("BloksNumberLt"),"bk.action.i32.Mod":c("BloksNumberMod"),"bk.action.i32.Mul":c("Bloksi32NumberMul"),"bk.action.i32.Or":c("BloksNumberOr"),"bk.action.i32.Rand":c("BloksNumberRand"),"bk.action.i32.Sub":c("Bloksi32NumberSub"),"bk.action.i64.Add":c("Bloksi64NumberAdd"),"bk.action.i64.Const":c("Bloksi64ConstNumber"),"bk.action.i64.Eq":c("Bloksi64NumberEq"),"bk.action.i64.Gt":c("Bloksi64NumberGt"),"bk.action.i64.Lt":c("Bloksi64NumberLt"),"bk.action.i64.Mod":c("Bloksi64NumberMod"),"bk.action.i64.Sub":c("Bloksi64NumberSub"),"bk.action.io.clipboard.SetString":c("BloksClipboardSetString"),"bk.action.io.CurrentTimeMillis":c("BloksCurrentTimeMillis"),"bk.action.io.Toast":c("BloksToast"),"bk.action.logging.LogEvent":c("BloksLogEvent"),"bk.action.map.Append":c("BloksDummy"),"bk.action.map.Clone":c("BloksMapClone"),"bk.action.map.Get":c("BloksMapGet"),"bk.action.map.Keys":c("BloksMapKeys"),"bk.action.map.Make":c("BloksMapMake"),"bk.action.map.MakeFlat":c("BloksMakeFlat"),"bk.action.map.Update":c("BloksMapUpdate"),"bk.action.navigation.OpenSendMessage":c("BloksOpenSendMessage"),"bk.action.navigation.OpenUrl":c("BloksOpenUrl"),"bk.action.navigation.OpenUrlV2":c("BloksOpenUrlV2"),"bk.action.navigation.SetNavBar":c("BloksDummy"),"bk.action.session_store.Get":c("BloksSessionStoreGet"),"bk.action.share.Text":c("BloksShareText"),"bk.action.string.Concat":c("BloksStringConcat"),"bk.action.string.JsonEncode":c("BloksJsonEncode"),"bk.action.string.JsonEncodeV2":c("BloksJsonEncode"),"bk.action.string.Length":c("BloksStringLength"),"bk.action.string.MatchesRegex":c("BloksMatchesRegex"),"bk.action.string.ValueOfNumber":c("BloksStringValueOfNumber"),"bk.action.t3d.FromArray":c("BloksT3DFromArray"),"bk.action.t3d.Rotate":c("BloksRotate3D"),"bk.action.t3d.Scale":c("BloksScale3D"),"bk.action.t3d.Translate":c("BloksTranslate3D"),"bk.action.textinput.GetText":c("BloksTextInputGetText"),"bk.action.visibility_context.GetTimeSinceLastImpressionInMS":c("BloksVisibilityContextGetTimeSinceLastImpressionInMS"),"bk.action.visibility_context.HasSeenBefore":c("BloksVisibilityContextHasSeenBefore")};g.COMPONENTS=a;g.ACTIONS=b}),98);
-----
BloksAnimationFunctions",[],(function(a,b,c,d,e,f){function g(a,b,c,d){var e=1e-6,f=3*a-3*c+1,g=3*c-6*a,h=3*a,i=3*b-3*d+1,j=3*d-6*b,k=3*b;function l(a){return(3*f*a+2*g)*a+h}function m(a){return((f*a+g)*a+h)*a}function n(a){return((i*a+j)*a+k)*a}function o(a){var b=a,c,d;for(var f=0;f<8;f++){d=m(b)-a;if(Math.abs(d)f){d=m(b)-a;if(Math.abs(d)0?c=b:f=b;b=(c+f)/2}return b}function p(a){return n(o(a))}return p}function a(){return g(0,0,1,1)}f.cubicBezier=g;f.linearInterpolator=a}),66);
-----
BloksAnimatedCreateCubicBezier",["BloksAnimationFunctions"],(function(a,b,c,d,e,f,g){function a(a,b,c,e,f){return d("BloksAnimationFunctions").cubicBezier(b,c,e,f)}g["default"]=a}),98);
-----
UFIReactionsVectorIcon",["CubicBezier"],(function(a,b,c,d,e,f,g){a=function(){var a=b.prototype;a.buildKeyFrames=function(a){var b=a.key_frames,d=a.timing_curves;return Object.keys(b).map(function(a){return parseInt(a,10)}).sort(function(a,b){return a-b}).reduce(function(a,e,f,g){var h=b[e];if(f===0){for(var i=0;i<=e;i++)a.push(h);return a}var j=null;i=d[f-1];Array.isArray(i)&&(j=new(c("CubicBezier"))(i[0],i[1]));var k=g[f-1],l=b[k];for(var m=k+1;m<=e;m++)m===e?a.push(h):(function(){var b=(m-k)/(e-k);j&&(b=j.solve(b));a.push(h.map(function(a,c){if(Array.isArray(a))return[a[0],a[1].map(function(a,d){d=l[c][1][d];return d+(a-d)*b})];if(isNaN(parseFloat(a)))return a;var d=l[c];return d+(a-d)*b}))})();return a},[],this)};function b(a){var b=this;this.buildAnimation=function(a){var b=a.anchor,c=a.property;a.key_frames=a.key_frames.map(function(a,d,e){if(b&&c==="ROTATION")return"rotate("+a[0]+" "+b.join(" ")+")";if(c==="POSITION"){d=a[0]-e[0][0];e=a[1]-e[0][1];return"translate("+d+" "+e+")"}if(c==="SCALE"){d=a.map(function(a){return a/100}).join(" ");return b?"translate("+b.join(" ")+") "+("scale("+d+") ")+("translate("+b.map(function(a){return-a}).join(" ")+")"):"scale("+d+")"}return null});return a};this.buildEffect=function(a){a.property==="gradient"&&(a.start.key_frames=a.start.key_frames.map(function(a){var b=Math.floor(a[0]),c=Math.floor(a[1]);a=Math.floor(a[2]);return"rgb("+b+","+c+","+a+")"}),a.end.key_frames=a.end.key_frames.map(function(a){var b=Math.floor(a[0]),c=Math.floor(a[1]);a=Math.floor(a[2]);return"rgb("+b+","+c+","+a+")"}));return a};this.buildFeature=function(a){a.key_frames=a.key_frames.map(function(a){return a.map(function(a){return a[0]+a[1].join(",")}).join(" ")});return a};this.buildData=function(a){a.animations&&(a.animations=a.animations.map(b.buildData,b).map(b.buildAnimation,b));a.effects&&(a.effects=a.effects.map(b.buildData,b).map(b.buildEffect,b));a.end&&(a.end.key_frames=b.buildKeyFrames(a.end));a.features&&(a.features=a.features.map(b.buildData,b).map(b.buildFeature,b));a.key_frames&&(a.key_frames=b.buildKeyFrames(a));a.start&&(a.start.key_frames=b.buildKeyFrames(a.start));a.groups&&(a.groups=a.groups.map(b.buildData,b));return a};this.$2=a.frameCount;this.$1=this.buildData(a)}a.getData=function(){return this.$1};a.getFrameCount=function(){return this.$2};return b}();g["default"]=a}),98);
-----
CometProgressRing.react",["BaseLoadingStateElement.react","CometProgressRingUtils","performanceNavigationStart","react","recoverableViolation","useCometProgressRingSvg.react"],(function(a,b,c,d,e,f,g){"use strict";var h=d("react");b=d("react");var i=b.useCallback,j=b.useEffect,k=b.useLayoutEffect,l=b.useRef,m=b.useState,n={rootDiv:{position:"x1n2onr6",$$css:!0}},o=d("CometProgressRingUtils").getCubicBezierPercentageFunc(.45,0,.25,.85);function a(a){var b=a.beginningPercentage,d=a.color,e=a.duration,f=a.explicitStartTime,g=a.onComplete,r=a.pause,s=a.percentage,t=a.size,u=a.type;u!=="default"&&s!=null&&c("recoverableViolation")("percentage should only be applied to default type","comet_ui");var v=l(null),w=l(null);a=m(!0);var x=a[0],y=a[1];a=p(u,s,b);b=a.completedPercentage;var z=a.finalPercentage;a=a.startPercentage;var A=l(a),B=l(z),C=l(b);a=c("useCometProgressRingSvg.react")({color:d,isHidden:x,size:t});var D=a.backgroundRef,E=a.leftCircleRef;z=a.progressRing;var F=a.rightCircleRef,G=i(function(a){var b=F.current,d=E.current;if(b==null||d==null)return;if(!(b instanceof SVGSVGElement)||!(d instanceof SVGSVGElement))return;if((u==="default"||u==="count-down")&&r===!0)return;w.current==null&&(w.current=f!=null?f-c("performanceNavigationStart")():a);if(u==="estimated")if(C.current>75)C.current+=.3*Math.pow((100-C.current)/50,2);else{var h=(a-w.current)/e/1e3;h=o(h);C.current=h*75}else if(u==="default"&&C.currentB.current){h=(a-w.current)/1e3/e*100;C.current=A.current-h;C.current=Math.max(C.current,0)}a=Math.max(0,Math.min(100,C.current))/100*360;b.style.transform="rotate("+Math.min(a,180)+"deg)";d.style.transform="rotate("+Math.max(a-180,0)+"deg)";q(u,C.current,s)?(window.cancelAnimationFrame(v.current),b.style.opacity="0",d.style.opacity="0",w.current=null,v.current=null,D.current instanceof SVGSVGElement&&(D.current.style.opacity="0"),g&&g()):v.current=window.requestAnimationFrame(G)},[D,e,f,E,g,r,s,F,u]);k(function(){x&&y(!x)},[x]);j(function(){if(window.requestAnimationFrame!=null){v.current=window.requestAnimationFrame(G);return function(){v.current!=null&&(w.current=null,window.cancelAnimationFrame(v.current),v.current=null)}}},[t,G]);return h.jsx(c("BaseLoadingStateElement.react"),{style:{height:t,width:t},xstyle:n.rootDiv,children:z})}a.displayName=a.name+" [from "+f.id+"]";function p(a,b,c){switch(a){case"default":return{completedPercentage:(a=c)!=null?a:0,finalPercentage:(a=b)!=null?a:100,startPercentage:(b=c)!=null?b:0};case"count-down":return{completedPercentage:100,finalPercentage:0,startPercentage:100};case"estimated":return{completedPercentage:0,finalPercentage:75,startPercentage:0};default:return{completedPercentage:0,finalPercentage:0,startPercentage:0}}}function q(a,b,c){return a==="count-down"?b<=0||b>100:b>=((a=c)!=null?a:100)}g["default"]=a}),98);
-----
PolarisBloksPrimitives",["PolarisBloksAction","PolarisBloksAddChild","PolarisBloksAnimatedCreate","PolarisBloksAnimatedCreateColor","PolarisBloksAnimatedCreateCubicBezier","PolarisBloksAnimatedCreateDimension","PolarisBloksAnimatedGetCurrentColorValue","PolarisBloksAnimatedGetCurrentDimensionValue","PolarisBloksAnimatedGetCurrentValue","PolarisBloksAnimatedParallel","PolarisBloksAnimatedStart","PolarisBloksApply","PolarisBloksArrayAppend","PolarisBloksArrayClone","PolarisBloksArrayConcat","PolarisBloksArrayGet","PolarisBloksArrayIndexOf","PolarisBloksArrayLength","PolarisBloksArrayMake","PolarisBloksArrayPut","PolarisBloksArrayPutAndGet","PolarisBloksArrayRemove","PolarisBloksArrayRemoveAndGet","PolarisBloksArrayUpdate","PolarisBloksAsyncAction","PolarisBloksAsyncActionWithDataManifest","PolarisBloksAsyncLoadV2","PolarisBloksBindWithArrayV2","PolarisBloksBoolAnd","PolarisBloksBoolNot","PolarisBloksBoolOr","PolarisBloksChildAtIndex","PolarisBloksClipboardSetString","PolarisBloksCollection","PolarisBloksConstNumber","PolarisBloksCurrentTimeMillis","PolarisBloksDangerouslyGetTreeFromParseResult","PolarisBloksDatetimeTextProvider","PolarisBloksDefault","PolarisBloksDelay","PolarisBloksDismissBottomSheet","PolarisBloksDismissKeyboard","PolarisBloksDummy","PolarisBloksDummyComponent","PolarisBloksFindComponentContext","PolarisBloksFlexbox","PolarisBloksGetAttr","PolarisBloksGetState","PolarisBloksGetVariable2","PolarisBloksImage","PolarisBloksIndexOfChild","PolarisBloksInflateSync","PolarisBloksInsertChildrenAfter","PolarisBloksInternalMerge","PolarisBloksInternalShadow","PolarisBloksJsonEncode","PolarisBloksLogEvent","PolarisBloksMakeFlat","PolarisBloksMapClone","PolarisBloksMapGet","PolarisBloksMapKeys","PolarisBloksMapMake","PolarisBloksMapUpdate","PolarisBloksMatch","PolarisBloksMatchesRegex","PolarisBloksNumberAnd","PolarisBloksNumberEq","PolarisBloksNumberGt","PolarisBloksNumberLt","PolarisBloksNumberMod","PolarisBloksNumberOr","PolarisBloksNumberRand","PolarisBloksOnMount","PolarisBloksOpenBottomSheetV2","PolarisBloksOpenScreen","PolarisBloksOpenSendMessage","PolarisBloksOpenUrl","PolarisBloksOpenUrlV2","PolarisBloksParseEmbedded","PolarisBloksPattern","PolarisBloksReduce","PolarisBloksReflow","PolarisBloksRemoveChild","PolarisBloksRemoveChildrenBetween","PolarisBloksRenderLifecycleExtension","PolarisBloksReplaceChild","PolarisBloksReplaceChildren","PolarisBloksReplaceEmbeddedChild","PolarisBloksRichText","PolarisBloksRotate3D","PolarisBloksScale3D","PolarisBloksScreenWrapper","PolarisBloksSessionStoreGet","PolarisBloksSetAttr","PolarisBloksShareText","PolarisBloksStringConcat","PolarisBloksStringLength","PolarisBloksStringValueOfNumber","PolarisBloksT3DFromArray","PolarisBloksTakeLast","PolarisBloksText","PolarisBloksTextInput","PolarisBloksTextInputGetText","PolarisBloksTextSpan","PolarisBloksToast","PolarisBloksTranslate3D","PolarisBloksVisibilityContextGetTimeSinceLastImpressionInMS","PolarisBloksVisibilityContextHasSeenBefore","PolarisBloksWhile","PolarisBloksWriteGlobalConsistencyStore","PolarisBloksWriteLocalState","PolarisBloksf32Convert","PolarisBloksf32NumberAdd","PolarisBloksf32NumberDiv","PolarisBloksf32NumberMul","PolarisBloksf32NumberSub","PolarisBloksi32Convert","PolarisBloksi32NumberAdd","PolarisBloksi32NumberDiv","PolarisBloksi32NumberMul","PolarisBloksi32NumberSub","PolarisBloksi64ConstNumber","PolarisBloksi64NumberAdd","PolarisBloksi64NumberEq","PolarisBloksi64NumberGt","PolarisBloksi64NumberLt","PolarisBloksi64NumberMod","PolarisBloksi64NumberSub"],(function(a,b,c,d,e,f,g){"use strict";a={"bk.components.internal.Action":c("PolarisBloksAction"),"bk.components.internal.Merge":c("PolarisBloksInternalMerge"),"bk.components.internal.Shadow":c("PolarisBloksInternalShadow"),"bk.components.Collection":c("PolarisBloksCollection"),"bk.components.DatetimeTextProvider":c("PolarisBloksDatetimeTextProvider"),"bk.components.Flexbox":c("PolarisBloksFlexbox"),"bk.components.Image":c("PolarisBloksImage"),"bk.components.OnMount":c("PolarisBloksOnMount"),"bk.components.RenderLifecycleExtension":c("PolarisBloksRenderLifecycleExtension"),"bk.components.RichText":c("PolarisBloksRichText"),"bk.components.screen.Wrapper":c("PolarisBloksScreenWrapper"),"bk.components.Text":c("PolarisBloksText"),"bk.components.TextInput":c("PolarisBloksTextInput"),"bk.components.TextSpan":c("PolarisBloksTextSpan"),"bk.components.Video":c("PolarisBloksDummyComponent"),"bk.components.VideoVersion":c("PolarisBloksDummyComponent"),"bk.type.VideoIdentifier":c("PolarisBloksDummyComponent")};b={"bk.action.animated.Create":c("PolarisBloksAnimatedCreate"),"bk.action.animated.CreateColor":c("PolarisBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("PolarisBloksAnimatedCreateDimension"),"bk.action.animated.easing.CreateCubicBezier":c("PolarisBloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("PolarisBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("PolarisBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentValue":c("PolarisBloksAnimatedGetCurrentValue"),"bk.action.animated.Parallel":c("PolarisBloksAnimatedParallel"),"bk.action.animated.Start":c("PolarisBloksAnimatedStart"),"bk.action.array.Append":c("PolarisBloksArrayAppend"),"bk.action.array.Clone":c("PolarisBloksArrayClone"),"bk.action.array.Concat":c("PolarisBloksArrayConcat"),"bk.action.array.Get":c("PolarisBloksArrayGet"),"bk.action.array.IndexOf":c("PolarisBloksArrayIndexOf"),"bk.action.array.Length":c("PolarisBloksArrayLength"),"bk.action.array.Make":c("PolarisBloksArrayMake"),"bk.action.array.Put":c("PolarisBloksArrayPut"),"bk.action.array.PutAndGet":c("PolarisBloksArrayPutAndGet"),"bk.action.array.Remove":c("PolarisBloksArrayRemove"),"bk.action.array.RemoveAndGet":c("PolarisBloksArrayRemoveAndGet"),"bk.action.array.Update":c("PolarisBloksArrayUpdate"),"bk.action.bloks.AddChild":c("PolarisBloksAddChild"),"bk.action.bloks.AsyncAction":c("PolarisBloksAsyncAction"),"bk.action.bloks.AsyncActionWithDataManifest":c("PolarisBloksAsyncActionWithDataManifest"),"bk.action.bloks.AsyncLoadV2":c("PolarisBloksAsyncLoadV2"),"bk.action.bloks.ChildAtIndex":c("PolarisBloksChildAtIndex"),"bk.action.bloks.DangerouslyGetTreeFromParseResult":c("PolarisBloksDangerouslyGetTreeFromParseResult"),"bk.action.bloks.DismissBottomSheet":c("PolarisBloksDismissBottomSheet"),"bk.action.bloks.DismissKeyboard":c("PolarisBloksDismissKeyboard"),"bk.action.bloks.Find":c("PolarisBloksFindComponentContext"),"bk.action.bloks.FindContainer":c("PolarisBloksFindComponentContext"),"bk.action.bloks.GetState":c("PolarisBloksGetState"),"bk.action.bloks.GetVariable2":c("PolarisBloksGetVariable2"),"bk.action.bloks.IndexOfChild":c("PolarisBloksIndexOfChild"),"bk.action.bloks.InflateSync":c("PolarisBloksInflateSync"),"bk.action.bloks.InsertChildrenAfter":c("PolarisBloksInsertChildrenAfter"),"bk.action.bloks.OpenBottomSheetV2":c("PolarisBloksOpenBottomSheetV2"),"bk.action.bloks.OpenScreen":c("PolarisBloksOpenScreen"),"bk.action.bloks.ParseEmbedded":c("PolarisBloksParseEmbedded"),"bk.action.bloks.Reduce":c("PolarisBloksReduce"),"bk.action.bloks.Reflow":c("PolarisBloksReflow"),"bk.action.bloks.RemoveChild":c("PolarisBloksRemoveChild"),"bk.action.bloks.RemoveChildrenBetween":c("PolarisBloksRemoveChildrenBetween"),"bk.action.bloks.ReplaceChild":c("PolarisBloksReplaceChild"),"bk.action.bloks.ReplaceChildren":c("PolarisBloksReplaceChildren"),"bk.action.bloks.ReplaceEmbeddedChild":c("PolarisBloksReplaceEmbeddedChild"),"bk.action.bloks.WriteGlobalConsistencyStore":c("PolarisBloksWriteGlobalConsistencyStore"),"bk.action.bloks.WriteLocalState":c("PolarisBloksWriteLocalState"),"bk.action.bool.And":c("PolarisBloksBoolAnd"),"bk.action.bool.Not":c("PolarisBloksBoolNot"),"bk.action.bool.Or":c("PolarisBloksBoolOr"),"bk.action.component.GetAttr":c("PolarisBloksGetAttr"),"bk.action.component.SetAttr":c("PolarisBloksSetAttr"),"bk.action.core.Apply":c("PolarisBloksApply"),"bk.action.core.Default":c("PolarisBloksDefault"),"bk.action.core.Delay":c("PolarisBloksDelay"),"bk.action.core.FuncConst":c("PolarisBloksDummy"),"bk.action.core.GetArg":c("PolarisBloksDummy"),"bk.action.core.Match":c("PolarisBloksMatch"),"bk.action.core.Pattern":c("PolarisBloksPattern"),"bk.action.core.TakeLast":c("PolarisBloksTakeLast"),"bk.action.core.While":c("PolarisBloksWhile"),"bk.action.f32.Add":c("PolarisBloksf32NumberAdd"),"bk.action.f32.Const":c("PolarisBloksConstNumber"),"bk.action.f32.Convert":c("PolarisBloksf32Convert"),"bk.action.f32.Div":c("PolarisBloksf32NumberDiv"),"bk.action.f32.Eq":c("PolarisBloksNumberEq"),"bk.action.f32.Gt":c("PolarisBloksNumberGt"),"bk.action.f32.Lt":c("PolarisBloksNumberLt"),"bk.action.f32.Mul":c("PolarisBloksf32NumberMul"),"bk.action.f32.Sub":c("PolarisBloksf32NumberSub"),"bk.action.flipper.SendData":c("PolarisBloksDummy"),"bk.action.function.BindWithArrayV2":c("PolarisBloksBindWithArrayV2"),"bk.action.i32.Add":c("PolarisBloksi32NumberAdd"),"bk.action.i32.And":c("PolarisBloksNumberAnd"),"bk.action.i32.Const":c("PolarisBloksConstNumber"),"bk.action.i32.Convert":c("PolarisBloksi32Convert"),"bk.action.i32.Div":c("PolarisBloksi32NumberDiv"),"bk.action.i32.Eq":c("PolarisBloksNumberEq"),"bk.action.i32.Gt":c("PolarisBloksNumberGt"),"bk.action.i32.Lt":c("PolarisBloksNumberLt"),"bk.action.i32.Mod":c("PolarisBloksNumberMod"),"bk.action.i32.Mul":c("PolarisBloksi32NumberMul"),"bk.action.i32.Or":c("PolarisBloksNumberOr"),"bk.action.i32.Rand":c("PolarisBloksNumberRand"),"bk.action.i32.Sub":c("PolarisBloksi32NumberSub"),"bk.action.i64.Add":c("PolarisBloksi64NumberAdd"),"bk.action.i64.Const":c("PolarisBloksi64ConstNumber"),"bk.action.i64.Eq":c("PolarisBloksi64NumberEq"),"bk.action.i64.Gt":c("PolarisBloksi64NumberGt"),"bk.action.i64.Lt":c("PolarisBloksi64NumberLt"),"bk.action.i64.Mod":c("PolarisBloksi64NumberMod"),"bk.action.i64.Sub":c("PolarisBloksi64NumberSub"),"bk.action.io.clipboard.SetString":c("PolarisBloksClipboardSetString"),"bk.action.io.CurrentTimeMillis":c("PolarisBloksCurrentTimeMillis"),"bk.action.io.Toast":c("PolarisBloksToast"),"bk.action.logging.LogEvent":c("PolarisBloksLogEvent"),"bk.action.map.Append":c("PolarisBloksDummy"),"bk.action.map.Clone":c("PolarisBloksMapClone"),"bk.action.map.Get":c("PolarisBloksMapGet"),"bk.action.map.Keys":c("PolarisBloksMapKeys"),"bk.action.map.Make":c("PolarisBloksMapMake"),"bk.action.map.MakeFlat":c("PolarisBloksMakeFlat"),"bk.action.map.Update":c("PolarisBloksMapUpdate"),"bk.action.navigation.OpenSendMessage":c("PolarisBloksOpenSendMessage"),"bk.action.navigation.OpenUrl":c("PolarisBloksOpenUrl"),"bk.action.navigation.OpenUrlV2":c("PolarisBloksOpenUrlV2"),"bk.action.navigation.SetNavBar":c("PolarisBloksDummy"),"bk.action.session_store.Get":c("PolarisBloksSessionStoreGet"),"bk.action.share.Text":c("PolarisBloksShareText"),"bk.action.string.Concat":c("PolarisBloksStringConcat"),"bk.action.string.JsonEncode":c("PolarisBloksJsonEncode"),"bk.action.string.JsonEncodeV2":c("PolarisBloksJsonEncode"),"bk.action.string.Length":c("PolarisBloksStringLength"),"bk.action.string.MatchesRegex":c("PolarisBloksMatchesRegex"),"bk.action.string.ValueOfNumber":c("PolarisBloksStringValueOfNumber"),"bk.action.t3d.FromArray":c("PolarisBloksT3DFromArray"),"bk.action.t3d.Rotate":c("PolarisBloksRotate3D"),"bk.action.t3d.Scale":c("PolarisBloksScale3D"),"bk.action.t3d.Translate":c("PolarisBloksTranslate3D"),"bk.action.textinput.GetText":c("PolarisBloksTextInputGetText"),"bk.action.visibility_context.GetTimeSinceLastImpressionInMS":c("PolarisBloksVisibilityContextGetTimeSinceLastImpressionInMS"),"bk.action.visibility_context.HasSeenBefore":c("PolarisBloksVisibilityContextHasSeenBefore"),"bk.versioning.bloks.GlobalStateWithInitialLispy":c("PolarisBloksDummy")};g.COMPONENTS=a;g.ACTIONS=b}),98);
-----
PolarisBloksAnimationFunctions",[],(function(a,b,c,d,e,f){"use strict";function g(a,b,c,d){var e=1e-6,f=3*a-3*c+1,g=3*c-6*a,h=3*a,i=3*b-3*d+1,j=3*d-6*b,k=3*b;function l(a){return(3*f*a+2*g)*a+h}function m(a){return((f*a+g)*a+h)*a}function n(a){return((i*a+j)*a+k)*a}function o(a){var b=a,c,d;for(var f=0;f<8;f++){d=m(b)-a;if(Math.abs(d)f){d=m(b)-a;if(Math.abs(d)0?c=b:f=b;b=(c+f)/2}return b}function p(a){return n(o(a))}return p}function a(){return g(0,0,1,1)}f.cubicBezier=g;f.linearInterpolator=a}),66);
-----
PolarisBloksAnimatedCreateCubicBezier",["PolarisBloksAnimationFunctions"],(function(a,b,c,d,e,f,g){"use strict";function a(a,b,c,e,f){return d("PolarisBloksAnimationFunctions").cubicBezier(b,c,e,f)}g["default"]=a}),98);
-----
babylonjs-5.0.0-alpha.56",[],(function(a,b,c,d,e,f){"use strict";var g={},h={exports:g},i;function j(){!function(a,b){"object"==typeof g&&"object"==typeof h?h.exports=b():"function"==typeof i&&i.amd?i("babylonjs",[],b):"object"==typeof g?g.babylonjs=b():a.BABYLON=b()}("undefined"!=typeof self?self:"undefined"!=typeof a?a:this,function(){return function(a){var b={};function c(d){if(b[d])return b[d].exports;var e=b[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,c),e.l=!0,e.exports}return c.m=a,c.c=b,c.d=function(a,b,d){c.o(a,b)||Object.defineProperty(a,b,{enumerable:!0,get:d})},c.r=function(a){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})},c.t=function(a,b){if(1&b&&(a=c(a)),8&b)return a;if(4&b&&"object"==typeof a&&a&&a.__esModule)return a;var d=Object.create(null);if(c.r(d),Object.defineProperty(d,"default",{enumerable:!0,value:a}),2&b&&"string"!=typeof a)for(b in a)c.d(d,b,function(b){return a[b]}.bind(null,b));return d},c.n=function(a){var b=a&&a.__esModule?function(){return a["default"]}:function(){return a};return c.d(b,"a",b),b},c.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},c.p="",c(c.s=202)}([function(a,b,c){c.d(b,"d",function(){return h}),c.d(b,"e",function(){return i}),c.d(b,"f",function(){return j}),c.d(b,"b",function(){return k}),c.d(b,"a",function(){return l}),c.d(b,"c",function(){return n});var d=c(14),e=c(22),f=c(51);a=c(10);var g=c(81),h=function(){function a(a,b){void 0===a&&(a=0),void 0===b&&(b=0),this.x=a,this.y=b}return a.prototype.toString=function(){return"{X: "+this.x+" Y: "+this.y+"}"},a.prototype.getClassName=function(){return"Vector2"},a.prototype.getHashCode=function(){var a=0|this.x;return 397*a^(0|this.y)},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.x,a[b+1]=this.y,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.copyFrom=function(a){return this.x=a.x,this.y=a.y,this},a.prototype.copyFromFloats=function(a,b){return this.x=a,this.y=b,this},a.prototype.set=function(a,b){return this.copyFromFloats(a,b)},a.prototype.add=function(b){return new a(this.x+b.x,this.y+b.y)},a.prototype.addToRef=function(a,b){return b.x=this.x+a.x,b.y=this.y+a.y,this},a.prototype.addInPlace=function(a){return this.x+=a.x,this.y+=a.y,this},a.prototype.addVector3=function(b){return new a(this.x+b.x,this.y+b.y)},a.prototype.subtract=function(b){return new a(this.x-b.x,this.y-b.y)},a.prototype.subtractToRef=function(a,b){return b.x=this.x-a.x,b.y=this.y-a.y,this},a.prototype.subtractInPlace=function(a){return this.x-=a.x,this.y-=a.y,this},a.prototype.multiplyInPlace=function(a){return this.x*=a.x,this.y*=a.y,this},a.prototype.multiply=function(b){return new a(this.x*b.x,this.y*b.y)},a.prototype.multiplyToRef=function(a,b){return b.x=this.x*a.x,b.y=this.y*a.y,this},a.prototype.multiplyByFloats=function(b,c){return new a(this.x*b,this.y*c)},a.prototype.divide=function(b){return new a(this.x/b.x,this.y/b.y)},a.prototype.divideToRef=function(a,b){return b.x=this.x/a.x,b.y=this.y/a.y,this},a.prototype.divideInPlace=function(a){return this.divideToRef(a,this)},a.prototype.negate=function(){return new a(-this.x,-this.y)},a.prototype.negateInPlace=function(){return this.x*=-1,this.y*=-1,this},a.prototype.negateToRef=function(a){return a.copyFromFloats(-1*this.x,-1*this.y)},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this},a.prototype.scale=function(b){var c=new a(0,0);return this.scaleToRef(b,c),c},a.prototype.scaleToRef=function(a,b){return b.x=this.x*a,b.y=this.y*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.x+=this.x*a,b.y+=this.y*a,this},a.prototype.equals=function(a){return a&&this.x===a.x&&this.y===a.y},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this.x,a.x,b)&&d.a.WithinEpsilon(this.y,a.y,b)},a.prototype.floor=function(){return new a(Math.floor(this.x),Math.floor(this.y))},a.prototype.fract=function(){return new a(this.x-Math.floor(this.x),this.y-Math.floor(this.y))},a.prototype.rotateToRef=function(a,b){var c=Math.cos(a);a=Math.sin(a);return b.x=c*this.x-a*this.y,b.y=a*this.x+c*this.y,this},a.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},a.prototype.lengthSquared=function(){return this.x*this.x+this.y*this.y},a.prototype.normalize=function(){return a.NormalizeToRef(this,this),this},a.prototype.clone=function(){return new a(this.x,this.y)},a.Zero=function(){return new a(0,0)},a.One=function(){return new a(1,1)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1]},a.CatmullRom=function(b,c,d,e,f){var h=f*f,g=f*h;return new a(.5*(2*c.x+(-b.x+d.x)*f+(2*b.x-5*c.x+4*d.x-e.x)*h+(-b.x+3*c.x-3*d.x+e.x)*g),.5*(2*c.y+(-b.y+d.y)*f+(2*b.y-5*c.y+4*d.y-e.y)*h+(-b.y+3*c.y-3*d.y+e.y)*g))},a.Clamp=function(b,c,d){var e=b.x;e=(e=e>d.x?d.x:e)d.y?d.y:b)c.x?b.x:c.x,b.y>c.y?b.y:c.y)},a.Transform=function(b,c){var d=a.Zero();return a.TransformToRef(b,c,d),d},a.TransformToRef=function(a,b,c){b=b.m;var d=a.x*b[0]+a.y*b[4]+b[12];a=a.x*b[1]+a.y*b[5]+b[13];c.x=d,c.y=a},a.PointInTriangle=function(a,b,c,d){var e=.5*(-c.y*d.x+b.y*(-c.x+d.x)+b.x*(c.y-d.y)+c.x*d.y),f=e<0?-1:1;d=(b.y*d.x-b.x*d.y+(d.y-b.y)*a.x+(b.x-d.x)*a.y)*f;c=(b.x*c.y-b.y*c.x+(b.y-c.y)*a.x+(c.x-b.x)*a.y)*f;return d>0&&c>0&&d+c<2*e*f},a.Distance=function(b,c){return Math.sqrt(a.DistanceSquared(b,c))},a.DistanceSquared=function(a,b){var c=a.x-b.x;a=a.y-b.y;return c*c+a*a},a.Center=function(b,c){return a.CenterToRef(b,c,a.Zero())},a.CenterToRef=function(a,b,c){return c.copyFromFloats((a.x+b.x)/2,(a.y+b.y)/2)},a.DistanceOfPointFromSegment=function(b,c,d){var e=a.DistanceSquared(c,d);if(0===e)return a.Distance(b,c);d=d.subtract(c);e=Math.max(0,Math.min(1,a.Dot(b.subtract(c),d)/e));c=c.add(d.multiplyByFloats(e,e));return a.Distance(b,c)},a}(),i=function(){function a(a,b,c){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),this._isDirty=!0,this._x=a,this._y=b,this._z=c}return Object.defineProperty(a.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"z",{get:function(){return this._z},set:function(a){this._z=a,this._isDirty=!0},enumerable:!1,configurable:!0}),a.prototype.toString=function(){return"{X: "+this._x+" Y: "+this._y+" Z: "+this._z+"}"},a.prototype.getClassName=function(){return"Vector3"},a.prototype.getHashCode=function(){var a=0|this._x;return 397*(397*a^(0|this._y))^(0|this._z)},a.prototype.asArray=function(){var a=[];return this.toArray(a,0),a},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this._x,a[b+1]=this._y,a[b+2]=this._z,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.toQuaternion=function(){return k.RotationYawPitchRoll(this._y,this._x,this._z)},a.prototype.addInPlace=function(a){return this.addInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.addInPlaceFromFloats=function(a,b,c){return this.x+=a,this.y+=b,this.z+=c,this},a.prototype.add=function(b){return new a(this._x+b._x,this._y+b._y,this._z+b._z)},a.prototype.addToRef=function(a,b){return b.copyFromFloats(this._x+a._x,this._y+a._y,this._z+a._z)},a.prototype.subtractInPlace=function(a){return this.x-=a._x,this.y-=a._y,this.z-=a._z,this},a.prototype.subtract=function(b){return new a(this._x-b._x,this._y-b._y,this._z-b._z)},a.prototype.subtractToRef=function(a,b){return this.subtractFromFloatsToRef(a._x,a._y,a._z,b)},a.prototype.subtractFromFloats=function(b,c,d){return new a(this._x-b,this._y-c,this._z-d)},a.prototype.subtractFromFloatsToRef=function(a,b,c,d){return d.copyFromFloats(this._x-a,this._y-b,this._z-c)},a.prototype.negate=function(){return new a(-this._x,-this._y,-this._z)},a.prototype.negateInPlace=function(){return this.x*=-1,this.y*=-1,this.z*=-1,this},a.prototype.negateToRef=function(a){return a.copyFromFloats(-1*this._x,-1*this._y,-1*this._z)},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this.z*=a,this},a.prototype.scale=function(b){return new a(this._x*b,this._y*b,this._z*b)},a.prototype.scaleToRef=function(a,b){return b.copyFromFloats(this._x*a,this._y*a,this._z*a)},a.prototype.scaleAndAddToRef=function(a,b){return b.addInPlaceFromFloats(this._x*a,this._y*a,this._z*a)},a.prototype.projectOnPlane=function(b,c){var d=a.Zero();return this.projectOnPlaneToRef(b,c,d),d},a.prototype.projectOnPlaneToRef=function(b,c,d){var e=b.normal;b=b.d;var g=m.Vector3[0];this.subtractToRef(c,g),g.normalize();var f=a.Dot(g,e);e=-(a.Dot(c,e)+b)/f;b=g.scaleInPlace(e);c.addToRef(b,d)},a.prototype.equals=function(a){return a&&this._x===a._x&&this._y===a._y&&this._z===a._z},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this._x,a._x,b)&&d.a.WithinEpsilon(this._y,a._y,b)&&d.a.WithinEpsilon(this._z,a._z,b)},a.prototype.equalsToFloats=function(a,b,c){return this._x===a&&this._y===b&&this._z===c},a.prototype.multiplyInPlace=function(a){return this.x*=a._x,this.y*=a._y,this.z*=a._z,this},a.prototype.multiply=function(a){return this.multiplyByFloats(a._x,a._y,a._z)},a.prototype.multiplyToRef=function(a,b){return b.copyFromFloats(this._x*a._x,this._y*a._y,this._z*a._z)},a.prototype.multiplyByFloats=function(b,c,d){return new a(this._x*b,this._y*c,this._z*d)},a.prototype.divide=function(b){return new a(this._x/b._x,this._y/b._y,this._z/b._z)},a.prototype.divideToRef=function(a,b){return b.copyFromFloats(this._x/a._x,this._y/a._y,this._z/a._z)},a.prototype.divideInPlace=function(a){return this.divideToRef(a,this)},a.prototype.minimizeInPlace=function(a){return this.minimizeInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.maximizeInPlace=function(a){return this.maximizeInPlaceFromFloats(a._x,a._y,a._z)},a.prototype.minimizeInPlaceFromFloats=function(a,b,c){return athis._x&&(this.x=a),b>this._y&&(this.y=b),c>this._z&&(this.z=c),this},a.prototype.isNonUniformWithinEpsilon=function(a){var b=Math.abs(this._x),c=Math.abs(this._y);if(!d.a.WithinEpsilon(b,c,a))return!0;var e=Math.abs(this._z);return!d.a.WithinEpsilon(b,e,a)||!d.a.WithinEpsilon(c,e,a)},Object.defineProperty(a.prototype,"isNonUniform",{get:function(){var a=Math.abs(this._x);return a!==Math.abs(this._y)||a!==Math.abs(this._z)},enumerable:!1,configurable:!0}),a.prototype.floor=function(){return new a(Math.floor(this._x),Math.floor(this._y),Math.floor(this._z))},a.prototype.fract=function(){return new a(this._x-Math.floor(this._x),this._y-Math.floor(this._y),this._z-Math.floor(this._z))},a.prototype.length=function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z)},a.prototype.lengthSquared=function(){return this._x*this._x+this._y*this._y+this._z*this._z},a.prototype.normalize=function(){return this.normalizeFromLength(this.length())},a.prototype.reorderInPlace=function(a){var b=this;return"xyz"===(a=a.toLowerCase())||(m.Vector3[0].copyFrom(this),["x","y","z"].forEach(function(c,d){b[c]=m.Vector3[0][a[d]]})),this},a.prototype.rotateByQuaternionToRef=function(b,c){return b.toRotationMatrix(m.Matrix[0]),a.TransformCoordinatesToRef(this,m.Matrix[0],c),c},a.prototype.rotateByQuaternionAroundPointToRef=function(a,b,c){return this.subtractToRef(b,m.Vector3[0]),m.Vector3[0].rotateByQuaternionToRef(a,m.Vector3[0]),b.addToRef(m.Vector3[0],c),c},a.prototype.cross=function(b){return a.Cross(this,b)},a.prototype.normalizeFromLength=function(a){return 0===a||1===a?this:this.scaleInPlace(1/a)},a.prototype.normalizeToNew=function(){var b=new a(0,0,0);return this.normalizeToRef(b),b},a.prototype.normalizeToRef=function(a){var b=this.length();return 0===b||1===b?a.copyFromFloats(this._x,this._y,this._z):this.scaleToRef(1/b,a)},a.prototype.clone=function(){return new a(this._x,this._y,this._z)},a.prototype.copyFrom=function(a){return this.copyFromFloats(a._x,a._y,a._z)},a.prototype.copyFromFloats=function(a,b,c){return this.x=a,this.y=b,this.z=c,this},a.prototype.set=function(a,b,c){return this.copyFromFloats(a,b,c)},a.prototype.setAll=function(a){return this.x=this.y=this.z=a,this},a.GetClipFactor=function(b,c,d,e){b=a.Dot(b,d)-e;return b/(b-(a.Dot(c,d)-e))},a.GetAngleBetweenVectors=function(b,c,d){b=b.normalizeToRef(m.Vector3[1]);c=c.normalizeToRef(m.Vector3[2]);var g=a.Dot(b,c),e=Math.acos(g),f=m.Vector3[3];return a.CrossToRef(b,c,f),a.Dot(f,d)>0?isNaN(e)?0:e:isNaN(e)?-Math.PI:-Math.acos(g)},a.GetAngleBetweenVectorsOnPlane=function(b,c,e){m.Vector3[0].copyFrom(b);b=m.Vector3[0];m.Vector3[1].copyFrom(c);c=m.Vector3[1];m.Vector3[2].copyFrom(e);e=m.Vector3[2];var f=m.Vector3[3],g=m.Vector3[4];b.normalize(),c.normalize(),e.normalize(),a.CrossToRef(e,b,f),a.CrossToRef(f,e,g);b=Math.atan2(a.Dot(c,f),a.Dot(c,g));return d.a.NormalizeRadians(b)},a.SlerpToRef=function(b,c,f,k){f=d.a.Clamp(f,0,1);var g=m.Vector3[0],h=m.Vector3[1];g.copyFrom(b),b=g.length(),g.normalizeFromLength(b),h.copyFrom(c),c=h.length(),h.normalizeFromLength(c);var i,j=a.Dot(g,h);if(j<1-e.a){j=Math.acos(j);var l=1/Math.sin(j);i=Math.sin((1-f)*j)*l,j=Math.sin(f*j)*l}else i=1-f,j=f;g.scaleInPlace(i),h.scaleInPlace(j),k.copyFrom(g).addInPlace(h),k.scaleInPlace(d.a.Lerp(b,c,f))},a.SmoothToRef=function(b,c,d,e,f){a.SlerpToRef(b,c,0===e?1:d/e,f)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2])},a.FromFloatArray=function(b,c){return a.FromArray(b,c)},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2]},a.FromFloatArrayToRef=function(b,c,d){return a.FromArrayToRef(b,c,d)},a.FromFloatsToRef=function(a,b,c,d){d.copyFromFloats(a,b,c)},a.Zero=function(){return new a(0,0,0)},a.One=function(){return new a(1,1,1)},a.Up=function(){return new a(0,1,0)},Object.defineProperty(a,"UpReadOnly",{get:function(){return a._UpReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RightReadOnly",{get:function(){return a._RightReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"LeftHandedForwardReadOnly",{get:function(){return a._LeftHandedForwardReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RightHandedForwardReadOnly",{get:function(){return a._RightHandedForwardReadOnly},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ZeroReadOnly",{get:function(){return a._ZeroReadOnly},enumerable:!1,configurable:!0}),a.Down=function(){return new a(0,-1,0)},a.Forward=function(b){return void 0===b&&(b=!1),new a(0,0,b?-1:1)},a.Backward=function(b){return void 0===b&&(b=!1),new a(0,0,b?1:-1)},a.Right=function(){return new a(1,0,0)},a.Left=function(){return new a(-1,0,0)},a.TransformCoordinates=function(b,c){var d=a.Zero();return a.TransformCoordinatesToRef(b,c,d),d},a.TransformCoordinatesToRef=function(b,c,d){a.TransformCoordinatesFromFloatsToRef(b._x,b._y,b._z,c,d)},a.TransformCoordinatesFromFloatsToRef=function(a,b,c,d,e){d=d.m;var h=a*d[0]+b*d[4]+c*d[8]+d[12],f=a*d[1]+b*d[5]+c*d[9]+d[13],g=a*d[2]+b*d[6]+c*d[10]+d[14];a=1/(a*d[3]+b*d[7]+c*d[11]+d[15]);e.x=h*a,e.y=f*a,e.z=g*a},a.TransformNormal=function(b,c){var d=a.Zero();return a.TransformNormalToRef(b,c,d),d},a.TransformNormalToRef=function(a,b,c){this.TransformNormalFromFloatsToRef(a._x,a._y,a._z,b,c)},a.TransformNormalFromFloatsToRef=function(a,b,c,d,e){d=d.m;e.x=a*d[0]+b*d[4]+c*d[8],e.y=a*d[1]+b*d[5]+c*d[9],e.z=a*d[2]+b*d[6]+c*d[10]},a.CatmullRom=function(b,c,d,e,f){var h=f*f,g=f*h;return new a(.5*(2*c._x+(-b._x+d._x)*f+(2*b._x-5*c._x+4*d._x-e._x)*h+(-b._x+3*c._x-3*d._x+e._x)*g),.5*(2*c._y+(-b._y+d._y)*f+(2*b._y-5*c._y+4*d._y-e._y)*h+(-b._y+3*c._y-3*d._y+e._y)*g),.5*(2*c._z+(-b._z+d._z)*f+(2*b._z-5*c._z+4*d._z-e._z)*h+(-b._z+3*c._z-3*d._z+e._z)*g))},a.Clamp=function(b,c,d){var e=new a;return a.ClampToRef(b,c,d,e),e},a.ClampToRef=function(a,b,c,d){var e=a._x;e=(e=e>c._x?c._x:e)c._y?c._y:f)c._z?c._z:a)0&&o<0?(u.copyFrom(h),v=c,n=f):o>0&&s<0?(u.copyFrom(j),v=f,n=p):(u.copyFrom(i).scaleInPlace(-1),v=p,n=c);q=m.Vector3[9];h=m.Vector3[4];if(v.subtractToRef(t,w),n.subtractToRef(t,q),a.CrossToRef(w,q,h),!(a.Dot(h,k)<0))return g.copyFrom(t),Math.abs(r*l);o=m.Vector3[5];a.CrossToRef(u,h,o),o.normalize();s=m.Vector3[9];s.copyFrom(v).subtractInPlace(t);j=s.length();if(jthis.x&&(this.x=a.x),a.y>this.y&&(this.y=a.y),a.z>this.z&&(this.z=a.z),a.w>this.w&&(this.w=a.w),this},a.prototype.floor=function(){return new a(Math.floor(this.x),Math.floor(this.y),Math.floor(this.z),Math.floor(this.w))},a.prototype.fract=function(){return new a(this.x-Math.floor(this.x),this.y-Math.floor(this.y),this.z-Math.floor(this.z),this.w-Math.floor(this.w))},a.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},a.prototype.lengthSquared=function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},a.prototype.normalize=function(){var a=this.length();return 0===a?this:this.scaleInPlace(1/a)},a.prototype.toVector3=function(){return new i(this.x,this.y,this.z)},a.prototype.clone=function(){return new a(this.x,this.y,this.z,this.w)},a.prototype.copyFrom=function(a){return this.x=a.x,this.y=a.y,this.z=a.z,this.w=a.w,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.x=a,this.y=b,this.z=c,this.w=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.setAll=function(a){return this.x=this.y=this.z=this.w=a,this},a.FromArray=function(b,c){return c||(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2],c.w=a[b+3]},a.FromFloatArrayToRef=function(b,c,d){a.FromArrayToRef(b,c,d)},a.FromFloatsToRef=function(a,b,c,d,e){e.x=a,e.y=b,e.z=c,e.w=d},a.Zero=function(){return new a(0,0,0,0)},a.One=function(){return new a(1,1,1,1)},a.Normalize=function(b){var c=a.Zero();return a.NormalizeToRef(b,c),c},a.NormalizeToRef=function(a,b){b.copyFrom(a),b.normalize()},a.Minimize=function(a,b){a=a.clone();return a.minimizeInPlace(b),a},a.Maximize=function(a,b){a=a.clone();return a.maximizeInPlace(b),a},a.Distance=function(b,c){return Math.sqrt(a.DistanceSquared(b,c))},a.DistanceSquared=function(a,b){var c=a.x-b.x,d=a.y-b.y,e=a.z-b.z;a=a.w-b.w;return c*c+d*d+e*e+a*a},a.Center=function(b,c){return a.CenterToRef(b,c,a.Zero())},a.CenterToRef=function(a,b,c){return c.copyFromFloats((a.x+b.x)/2,(a.y+b.y)/2,(a.z+b.z)/2,(a.w+b.w)/2)},a.TransformCoordinates=function(b,c){var d=a.Zero();return a.TransformCoordinatesToRef(b,c,d),d},a.TransformCoordinatesToRef=function(b,c,d){a.TransformCoordinatesFromFloatsToRef(b._x,b._y,b._z,c,d)},a.TransformCoordinatesFromFloatsToRef=function(a,b,c,d,e){d=d.m;var h=a*d[0]+b*d[4]+c*d[8]+d[12],f=a*d[1]+b*d[5]+c*d[9]+d[13],g=a*d[2]+b*d[6]+c*d[10]+d[14];a=a*d[3]+b*d[7]+c*d[11]+d[15];e.x=h,e.y=f,e.z=g,e.w=a},a.TransformNormal=function(b,c){var d=a.Zero();return a.TransformNormalToRef(b,c,d),d},a.TransformNormalToRef=function(a,b,c){b=b.m;var d=a.x*b[0]+a.y*b[4]+a.z*b[8],e=a.x*b[1]+a.y*b[5]+a.z*b[9];b=a.x*b[2]+a.y*b[6]+a.z*b[10];c.x=d,c.y=e,c.z=b,c.w=a.w},a.TransformNormalFromFloatsToRef=function(a,b,c,d,e,f){e=e.m;f.x=a*e[0]+b*e[4]+c*e[8],f.y=a*e[1]+b*e[5]+c*e[9],f.z=a*e[2]+b*e[6]+c*e[10],f.w=d},a.FromVector3=function(b,c){return void 0===c&&(c=0),new a(b._x,b._y,b._z,c)},a}(),k=function(){function a(a,b,c,d){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),this._isDirty=!0,this._x=a,this._y=b,this._z=c,this._w=d}return Object.defineProperty(a.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"z",{get:function(){return this._z},set:function(a){this._z=a,this._isDirty=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"w",{get:function(){return this._w},set:function(a){this._w=a,this._isDirty=!0},enumerable:!1,configurable:!0}),a.prototype.toString=function(){return"{X: "+this._x+" Y: "+this._y+" Z: "+this._z+" W: "+this._w+"}"},a.prototype.getClassName=function(){return"Quaternion"},a.prototype.getHashCode=function(){var a=0|this._x;return 397*(397*(397*a^(0|this._y))^(0|this._z))^(0|this._w)},a.prototype.asArray=function(){return[this._x,this._y,this._z,this._w]},a.prototype.equals=function(a){return a&&this._x===a._x&&this._y===a._y&&this._z===a._z&&this._w===a._w},a.prototype.equalsWithEpsilon=function(a,b){return void 0===b&&(b=e.a),a&&d.a.WithinEpsilon(this._x,a._x,b)&&d.a.WithinEpsilon(this._y,a._y,b)&&d.a.WithinEpsilon(this._z,a._z,b)&&d.a.WithinEpsilon(this._w,a._w,b)},a.prototype.clone=function(){return new a(this._x,this._y,this._z,this._w)},a.prototype.copyFrom=function(a){return this.x=a._x,this.y=a._y,this.z=a._z,this.w=a._w,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.x=a,this.y=b,this.z=c,this.w=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.add=function(b){return new a(this._x+b._x,this._y+b._y,this._z+b._z,this._w+b._w)},a.prototype.addInPlace=function(a){return this._x+=a._x,this._y+=a._y,this._z+=a._z,this._w+=a._w,this},a.prototype.subtract=function(b){return new a(this._x-b._x,this._y-b._y,this._z-b._z,this._w-b._w)},a.prototype.scale=function(b){return new a(this._x*b,this._y*b,this._z*b,this._w*b)},a.prototype.scaleToRef=function(a,b){return b.x=this._x*a,b.y=this._y*a,b.z=this._z*a,b.w=this._w*a,this},a.prototype.scaleInPlace=function(a){return this.x*=a,this.y*=a,this.z*=a,this.w*=a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.x+=this._x*a,b.y+=this._y*a,b.z+=this._z*a,b.w+=this._w*a,this},a.prototype.multiply=function(b){var c=new a(0,0,0,1);return this.multiplyToRef(b,c),c},a.prototype.multiplyToRef=function(a,b){var c=this._x*a._w+this._y*a._z-this._z*a._y+this._w*a._x,d=-this._x*a._z+this._y*a._w+this._z*a._x+this._w*a._y,e=this._x*a._y-this._y*a._x+this._z*a._w+this._w*a._z;a=-this._x*a._x-this._y*a._y-this._z*a._z+this._w*a._w;return b.copyFromFloats(c,d,e,a),this},a.prototype.multiplyInPlace=function(a){return this.multiplyToRef(a,this),this},a.prototype.conjugateToRef=function(a){return a.copyFromFloats(-this._x,-this._y,-this._z,this._w),this},a.prototype.conjugateInPlace=function(){return this.x*=-1,this.y*=-1,this.z*=-1,this},a.prototype.conjugate=function(){return new a(-this._x,-this._y,-this._z,this._w)},a.prototype.length=function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},a.prototype.normalize=function(){var a=this.length();if(0===a)return this;a=1/a;return this.x*=a,this.y*=a,this.z*=a,this.w*=a,this},a.prototype.toEulerAngles=function(){var a=i.Zero();return this.toEulerAnglesToRef(a),a},a.prototype.toEulerAnglesToRef=function(a){var b=this._z,c=this._x,d=this._y,e=this._w,f=e*e,j=b*b,g=c*c,h=d*d,i=d*b-c*e;return i<-.4999999?(a.y=2*Math.atan2(d,e),a.x=Math.PI/2,a.z=0):i>.4999999?(a.y=2*Math.atan2(d,e),a.x=-Math.PI/2,a.z=0):(a.z=Math.atan2(2*(c*d+b*e),-j-g+h+f),a.x=Math.asin(-2*(b*d-c*e)),a.y=Math.atan2(2*(b*c+d*e),j-g-h+f)),this},a.prototype.toRotationMatrix=function(a){return l.FromQuaternionToRef(this,a),this},a.prototype.fromRotationMatrix=function(b){return a.FromRotationMatrixToRef(b,this),this},a.FromRotationMatrix=function(b){var c=new a;return a.FromRotationMatrixToRef(b,c),c},a.FromRotationMatrixToRef=function(a,b){a=a.m;var c=a[0],d=a[4],k=a[8],e=a[1],f=a[5],g=a[9],h=a[2],i=a[6];a=a[10];var j=c+f+a;j>0?(j=.5/Math.sqrt(j+1),b.w=.25/j,b.x=(i-g)*j,b.y=(k-h)*j,b.z=(e-d)*j):c>f&&c>a?(j=2*Math.sqrt(1+c-f-a),b.w=(i-g)/j,b.x=.25*j,b.y=(d+e)/j,b.z=(k+h)/j):f>a?(j=2*Math.sqrt(1+f-c-a),b.w=(k-h)/j,b.x=(d+e)/j,b.y=.25*j,b.z=(g+i)/j):(j=2*Math.sqrt(1+a-c-f),b.w=(e-d)/j,b.x=(k+h)/j,b.y=(g+i)/j,b.z=.25*j)},a.Dot=function(a,b){return a._x*b._x+a._y*b._y+a._z*b._z+a._w*b._w},a.AreClose=function(b,c){return a.Dot(b,c)>=0},a.SmoothToRef=function(b,c,e,f,g){e=0===f?1:e/f;e=d.a.Clamp(e,0,1),a.SlerpToRef(b,c,e,g)},a.Zero=function(){return new a(0,0,0,0)},a.Inverse=function(b){return new a(-b._x,-b._y,-b._z,b._w)},a.InverseToRef=function(a,b){return b.set(-a._x,-a._y,-a._z,a._w),b},a.Identity=function(){return new a(0,0,0,1)},a.IsIdentity=function(a){return a&&0===a._x&&0===a._y&&0===a._z&&1===a._w},a.RotationAxis=function(b,c){return a.RotationAxisToRef(b,c,new a)},a.RotationAxisToRef=function(a,b,c){var d=Math.sin(b/2);return a.normalize(),c.w=Math.cos(b/2),c.x=a._x*d,c.y=a._y*d,c.z=a._z*d,c},a.FromArray=function(b,c){return c||(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){c.x=a[b],c.y=a[b+1],c.z=a[b+2],c.w=a[b+3]},a.FromEulerAngles=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(c,b,d,e),e},a.FromEulerAnglesToRef=function(b,c,d,e){return a.RotationYawPitchRollToRef(c,b,d,e),e},a.FromEulerVector=function(b){var c=new a;return a.RotationYawPitchRollToRef(b._y,b._x,b._z,c),c},a.FromEulerVectorToRef=function(b,c){return a.RotationYawPitchRollToRef(b._y,b._x,b._z,c),c},a.FromUnitVectorsToRef=function(a,b,c){var d=i.Dot(a,b)+1;return dMath.abs(a.z)?c.set(-a.y,a.x,0,0):c.set(0,-a.z,a.y,0):(i.CrossToRef(a,b,n.Vector3[0]),c.set(n.Vector3[0].x,n.Vector3[0].y,n.Vector3[0].z,d)),c.normalize()},a.RotationYawPitchRoll=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(b,c,d,e),e},a.RotationYawPitchRollToRef=function(a,b,c,d){c=.5*c;b=.5*b;a=.5*a;var e=Math.sin(c);c=Math.cos(c);var f=Math.sin(b);b=Math.cos(b);var g=Math.sin(a);a=Math.cos(a);d.x=a*f*c+g*b*e,d.y=g*b*c-a*f*e,d.z=a*b*e-g*f*c,d.w=a*b*c+g*f*e},a.RotationAlphaBetaGamma=function(b,c,d){var e=new a;return a.RotationAlphaBetaGammaToRef(b,c,d,e),e},a.RotationAlphaBetaGammaToRef=function(a,b,c,d){var e=.5*(c+a);c=.5*(c-a);a=.5*b;d.x=Math.cos(c)*Math.sin(a),d.y=Math.sin(c)*Math.sin(a),d.z=Math.sin(e)*Math.cos(a),d.w=Math.cos(e)*Math.cos(a)},a.RotationQuaternionFromAxis=function(b,c,d){var e=new a(0,0,0,0);return a.RotationQuaternionFromAxisToRef(b,c,d,e),e},a.RotationQuaternionFromAxisToRef=function(b,c,d,e){var f=m.Matrix[0];l.FromXYZAxesToRef(b.normalize(),c.normalize(),d.normalize(),f),a.FromRotationMatrixToRef(f,e)},a.FromLookDirectionLH=function(b,c){var d=new a;return a.FromLookDirectionLHToRef(b,c,d),d},a.FromLookDirectionLHToRef=function(b,c,d){var e=m.Matrix[0];l.LookDirectionLHToRef(b,c,e),a.FromRotationMatrixToRef(e,d)},a.FromLookDirectionRH=function(b,c){var d=new a;return a.FromLookDirectionRHToRef(b,c,d),d},a.FromLookDirectionRHToRef=function(b,c,d){var e=m.Matrix[0];return l.LookDirectionRHToRef(b,c,e),a.FromRotationMatrixToRef(e,d)},a.Slerp=function(b,c,d){var e=a.Identity();return a.SlerpToRef(b,c,d,e),e},a.SlerpToRef=function(a,b,c,d){var e,f,i=a._x*b._x+a._y*b._y+a._z*b._z+a._w*b._w,g=!1;if(i<0&&(g=!0,i=-i),i>.999999)f=1-c,e=g?-c:c;else{i=Math.acos(i);var h=1/Math.sin(i);f=Math.sin((1-c)*i)*h,e=g?-Math.sin(c*i)*h:Math.sin(c*i)*h}d.x=f*a._x+e*b._x,d.y=f*a._y+e*b._y,d.z=f*a._z+e*b._z,d.w=f*a._w+e*b._w},a.Hermite=function(b,c,d,e,f){var j=f*f,g=f*j,h=2*g-3*j+1,i=-2*g+3*j;f=g-2*j+f;g=g-j;return new a(b._x*h+d._x*i+c._x*f+e._x*g,b._y*h+d._y*i+c._y*f+e._y*g,b._z*h+d._z*i+c._z*f+e._z*g,b._w*h+d._w*i+c._w*f+e._w*g)},a.Hermite1stDerivative=function(b,c,d,e,f){var g=a.Zero();return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.x=6*(g-e)*a.x+(3*g-4*e+1)*b.x+6*(-g+e)*c.x+(3*g-2*e)*d.x,f.y=6*(g-e)*a.y+(3*g-4*e+1)*b.y+6*(-g+e)*c.y+(3*g-2*e)*d.y,f.z=6*(g-e)*a.z+(3*g-4*e+1)*b.z+6*(-g+e)*c.z+(3*g-2*e)*d.z,f.w=6*(g-e)*a.w+(3*g-4*e+1)*b.w+6*(-g+e)*c.w+(3*g-2*e)*d.w},a}(),l=function(){function a(){this._isIdentity=!1,this._isIdentityDirty=!0,this._isIdentity3x2=!0,this._isIdentity3x2Dirty=!0,this.updateFlag=-1,g.a.MatrixTrackPrecisionChange&&g.a.MatrixTrackedMatrices.push(this),this._m=new g.a.MatrixCurrentType(16),this._markAsUpdated()}return Object.defineProperty(a,"Use64Bits",{get:function(){return g.a.MatrixUse64Bits},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"m",{get:function(){return this._m},enumerable:!1,configurable:!0}),a.prototype._markAsUpdated=function(){this.updateFlag=a._updateFlagSeed++,this._isIdentity=!1,this._isIdentity3x2=!1,this._isIdentityDirty=!0,this._isIdentity3x2Dirty=!0},a.prototype._updateIdentityStatus=function(a,b,c,d){void 0===b&&(b=!1),void 0===c&&(c=!1),void 0===d&&(d=!0),this._isIdentity=a,this._isIdentity3x2=a||c,this._isIdentityDirty=!this._isIdentity&&b,this._isIdentity3x2Dirty=!this._isIdentity3x2&&d},a.prototype.isIdentity=function(){if(this._isIdentityDirty){this._isIdentityDirty=!1;var a=this._m;this._isIdentity=1===a[0]&&0===a[1]&&0===a[2]&&0===a[3]&&0===a[4]&&1===a[5]&&0===a[6]&&0===a[7]&&0===a[8]&&0===a[9]&&1===a[10]&&0===a[11]&&0===a[12]&&0===a[13]&&0===a[14]&&1===a[15]}return this._isIdentity},a.prototype.isIdentityAs3x2=function(){return this._isIdentity3x2Dirty&&(this._isIdentity3x2Dirty=!1,1!==this._m[0]||1!==this._m[5]||1!==this._m[15]||0!==this._m[1]||0!==this._m[2]||0!==this._m[3]||0!==this._m[4]||0!==this._m[6]||0!==this._m[7]||0!==this._m[8]||0!==this._m[9]||0!==this._m[10]||0!==this._m[11]||0!==this._m[12]||0!==this._m[13]||0!==this._m[14]?this._isIdentity3x2=!1:this._isIdentity3x2=!0),this._isIdentity3x2},a.prototype.determinant=function(){if(!0===this._isIdentity)return 1;var a=this._m,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],p=a[5],g=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11],m=a[12],n=a[13],o=a[14];a=a[15];var q=k*a-o*l,r=j*a-n*l,s=j*o-n*k;a=i*a-m*l;l=i*o-k*m;o=i*n-m*j;return b*+(p*q-g*r+h*s)+c*-(f*q-g*a+h*l)+d*+(f*r-p*a+h*o)+e*-(f*s-p*l+g*o)},a.prototype.toArray=function(){return this._m},a.prototype.asArray=function(){return this._m},a.prototype.invert=function(){return this.invertToRef(this),this},a.prototype.reset=function(){return a.FromValuesToRef(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,this),this._updateIdentityStatus(!1),this},a.prototype.add=function(b){var c=new a;return this.addToRef(b,c),c},a.prototype.addToRef=function(a,b){for(var c=this._m,d=b._m,a=a.m,e=0;e<16;e++)d[e]=c[e]+a[e];return b._markAsUpdated(),this},a.prototype.addToSelf=function(a){for(var b=this._m,a=a.m,c=0;c<16;c++)b[c]+=a[c];return this._markAsUpdated(),this},a.prototype.invertToRef=function(b){if(!0===this._isIdentity)return a.IdentityToRef(b),this;var c=this._m,d=c[0],e=c[1],f=c[2],p=c[3],g=c[4],h=c[5],i=c[6],j=c[7],k=c[8],l=c[9],m=c[10],n=c[11],o=c[12],q=c[13],r=c[14];c=c[15];var s=m*c-r*n,t=l*c-q*n,u=l*r-q*m,v=k*c-o*n,w=k*r-m*o,x=k*q-o*l,y=+(h*s-i*t+j*u),z=-(g*s-i*v+j*w),A=+(g*t-h*v+j*x),B=-(g*u-h*w+i*x),C=d*y+e*z+f*A+p*B;if(0===C)return b.copyFrom(this),this;C=1/C;var D=i*c-r*j,E=h*c-q*j,F=h*r-q*i;c=g*c-o*j;r=g*r-o*i;q=g*q-o*h;o=i*n-m*j;var aa=h*n-l*j,G=h*m-l*i;n=g*n-k*j;j=g*m-k*i;m=g*l-k*h;i=-(e*s-f*t+p*u);g=+(d*s-f*v+p*w);l=-(d*t-e*v+p*x);k=+(d*u-e*w+f*x);h=+(e*D-f*E+p*F);s=-(d*D-f*c+p*r);t=+(d*E-e*c+p*q);v=-(d*F-e*r+f*q);u=-(e*o-f*aa+p*G);w=+(d*o-f*n+p*j);x=-(d*aa-e*n+p*m);D=+(d*G-e*j+f*m);return a.FromValuesToRef(y*C,i*C,h*C,u*C,z*C,g*C,s*C,w*C,A*C,l*C,t*C,x*C,B*C,k*C,v*C,D*C,b),this},a.prototype.addAtIndex=function(a,b){return this._m[a]+=b,this._markAsUpdated(),this},a.prototype.multiplyAtIndex=function(a,b){return this._m[a]*=b,this._markAsUpdated(),this},a.prototype.setTranslationFromFloats=function(a,b,c){return this._m[12]=a,this._m[13]=b,this._m[14]=c,this._markAsUpdated(),this},a.prototype.addTranslationFromFloats=function(a,b,c){return this._m[12]+=a,this._m[13]+=b,this._m[14]+=c,this._markAsUpdated(),this},a.prototype.setTranslation=function(a){return this.setTranslationFromFloats(a._x,a._y,a._z)},a.prototype.getTranslation=function(){return new i(this._m[12],this._m[13],this._m[14])},a.prototype.getTranslationToRef=function(a){return a.x=this._m[12],a.y=this._m[13],a.z=this._m[14],this},a.prototype.removeRotationAndScaling=function(){var b=this.m;return a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,b[12],b[13],b[14],b[15],this),this._updateIdentityStatus(0===b[12]&&0===b[13]&&0===b[14]&&1===b[15]),this},a.prototype.multiply=function(b){var c=new a;return this.multiplyToRef(b,c),c},a.prototype.copyFrom=function(a){a.copyToArray(this._m);a=a;return this.updateFlag=a.updateFlag,this._updateIdentityStatus(a._isIdentity,a._isIdentityDirty,a._isIdentity3x2,a._isIdentity3x2Dirty),this},a.prototype.copyToArray=function(a,b){void 0===b&&(b=0);var c=this._m;return a[b]=c[0],a[b+1]=c[1],a[b+2]=c[2],a[b+3]=c[3],a[b+4]=c[4],a[b+5]=c[5],a[b+6]=c[6],a[b+7]=c[7],a[b+8]=c[8],a[b+9]=c[9],a[b+10]=c[10],a[b+11]=c[11],a[b+12]=c[12],a[b+13]=c[13],a[b+14]=c[14],a[b+15]=c[15],this},a.prototype.multiplyToRef=function(a,b){return this._isIdentity?(b.copyFrom(a),this):a._isIdentity?(b.copyFrom(this),this):(this.multiplyToArray(a,b._m,0),b._markAsUpdated(),this)},a.prototype.multiplyToArray=function(a,b,c){var d=this._m;a=a.m;var e=d[0],p=d[1],f=d[2],g=d[3],h=d[4],i=d[5],j=d[6],k=d[7],l=d[8],n=d[9],o=d[10],q=d[11],r=d[12],s=d[13],t=d[14];d=d[15];var u=a[0],v=a[1],w=a[2],x=a[3],y=a[4],z=a[5],A=a[6],B=a[7],C=a[8],D=a[9],E=a[10],F=a[11],aa=a[12],G=a[13],H=a[14];a=a[15];return b[c]=e*u+p*y+f*C+g*aa,b[c+1]=e*v+p*z+f*D+g*G,b[c+2]=e*w+p*A+f*E+g*H,b[c+3]=e*x+p*B+f*F+g*a,b[c+4]=h*u+i*y+j*C+k*aa,b[c+5]=h*v+i*z+j*D+k*G,b[c+6]=h*w+i*A+j*E+k*H,b[c+7]=h*x+i*B+j*F+k*a,b[c+8]=l*u+n*y+o*C+q*aa,b[c+9]=l*v+n*z+o*D+q*G,b[c+10]=l*w+n*A+o*E+q*H,b[c+11]=l*x+n*B+o*F+q*a,b[c+12]=r*u+s*y+t*C+d*aa,b[c+13]=r*v+s*z+t*D+d*G,b[c+14]=r*w+s*A+t*E+d*H,b[c+15]=r*x+s*B+t*F+d*a,this},a.prototype.equals=function(a){a=a;if(!a)return!1;if((this._isIdentity||a._isIdentity)&&!this._isIdentityDirty&&!a._isIdentityDirty)return this._isIdentity&&a._isIdentity;var b=this.m;a=a.m;return b[0]===a[0]&&b[1]===a[1]&&b[2]===a[2]&&b[3]===a[3]&&b[4]===a[4]&&b[5]===a[5]&&b[6]===a[6]&&b[7]===a[7]&&b[8]===a[8]&&b[9]===a[9]&&b[10]===a[10]&&b[11]===a[11]&&b[12]===a[12]&&b[13]===a[13]&&b[14]===a[14]&&b[15]===a[15]},a.prototype.clone=function(){var b=new a;return b.copyFrom(this),b},a.prototype.getClassName=function(){return"Matrix"},a.prototype.getHashCode=function(){for(var a=0|this._m[0],b=1;b<16;b++)a=397*a^(0|this._m[b]);return a},a.prototype.decomposeToTransformNode=function(a){return a.rotationQuaternion=a.rotationQuaternion||new k,this.decompose(a.scaling,a.rotationQuaternion,a.position)},a.prototype.decompose=function(b,c,d){if(this._isIdentity)return d&&d.setAll(0),b&&b.setAll(1),c&&c.copyFromFloats(0,0,0,1),!0;var e=this._m;if(d&&d.copyFromFloats(e[12],e[13],e[14]),(b=b||m.Vector3[0]).x=Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]),b.y=Math.sqrt(e[4]*e[4]+e[5]*e[5]+e[6]*e[6]),b.z=Math.sqrt(e[8]*e[8]+e[9]*e[9]+e[10]*e[10]),this.determinant()<=0&&(b.y*=-1),0===b._x||0===b._y||0===b._z)return c&&c.copyFromFloats(0,0,0,1),!1;if(c){d=1/b._x;var f=1/b._y;b=1/b._z;a.FromValuesToRef(e[0]*d,e[1]*d,e[2]*d,0,e[4]*f,e[5]*f,e[6]*f,0,e[8]*b,e[9]*b,e[10]*b,0,0,0,0,1,m.Matrix[0]),k.FromRotationMatrixToRef(m.Matrix[0],c)}return!0},a.prototype.getRow=function(a){if(a<0||a>3)return null;a=4*a;return new j(this._m[a+0],this._m[a+1],this._m[a+2],this._m[a+3])},a.prototype.setRow=function(a,b){return this.setRowFromFloats(a,b.x,b.y,b.z,b.w)},a.prototype.transpose=function(){return a.Transpose(this)},a.prototype.transposeToRef=function(b){return a.TransposeToRef(this,b),this},a.prototype.setRowFromFloats=function(a,b,c,d,e){if(a<0||a>3)return this;a=4*a;return this._m[a+0]=b,this._m[a+1]=c,this._m[a+2]=d,this._m[a+3]=e,this._markAsUpdated(),this},a.prototype.scale=function(b){var c=new a;return this.scaleToRef(b,c),c},a.prototype.scaleToRef=function(a,b){for(var c=0;c<16;c++)b._m[c]=this._m[c]*a;return b._markAsUpdated(),this},a.prototype.scaleAndAddToRef=function(a,b){for(var c=0;c<16;c++)b._m[c]+=this._m[c]*a;return b._markAsUpdated(),this},a.prototype.toNormalMatrix=function(b){var c=m.Matrix[0];this.invertToRef(c),c.transposeToRef(b);c=b._m;a.FromValuesToRef(c[0],c[1],c[2],0,c[4],c[5],c[6],0,c[8],c[9],c[10],0,0,0,0,1,b)},a.prototype.getRotationMatrix=function(){var b=new a;return this.getRotationMatrixToRef(b),b},a.prototype.getRotationMatrixToRef=function(b){var c=m.Vector3[0];if(!this.decompose(c))return a.IdentityToRef(b),this;var d=this._m,e=1/c._x,f=1/c._y;c=1/c._z;return a.FromValuesToRef(d[0]*e,d[1]*e,d[2]*e,0,d[4]*f,d[5]*f,d[6]*f,0,d[8]*c,d[9]*c,d[10]*c,0,0,0,0,1,b),this},a.prototype.toggleModelMatrixHandInPlace=function(){var a=this._m;a[2]*=-1,a[6]*=-1,a[8]*=-1,a[9]*=-1,a[14]*=-1,this._markAsUpdated()},a.prototype.toggleProjectionMatrixHandInPlace=function(){var a=this._m;a[8]*=-1,a[9]*=-1,a[10]*=-1,a[11]*=-1,this._markAsUpdated()},a.FromArray=function(b,c){void 0===c&&(c=0);var d=new a;return a.FromArrayToRef(b,c,d),d},a.FromArrayToRef=function(a,b,c){for(var d=0;d<16;d++)c._m[d]=a[d+b];c._markAsUpdated()},a.FromFloat32ArrayToRefScaled=function(a,b,c,d){for(var e=0;e<16;e++)d._m[e]=a[e+b]*c;d._markAsUpdated()},Object.defineProperty(a,"IdentityReadOnly",{get:function(){return a._identityReadOnly},enumerable:!1,configurable:!0}),a.FromValuesToRef=function(a,b,c,d,e,f,p,g,h,i,j,k,l,m,n,o,q){var r=q._m;r[0]=a,r[1]=b,r[2]=c,r[3]=d,r[4]=e,r[5]=f,r[6]=p,r[7]=g,r[8]=h,r[9]=i,r[10]=j,r[11]=k,r[12]=l,r[13]=m,r[14]=n,r[15]=o,q._markAsUpdated()},a.FromValues=function(b,c,d,e,f,p,g,h,i,j,k,l,m,n,o,q){var r=new a,s=r._m;return s[0]=b,s[1]=c,s[2]=d,s[3]=e,s[4]=f,s[5]=p,s[6]=g,s[7]=h,s[8]=i,s[9]=j,s[10]=k,s[11]=l,s[12]=m,s[13]=n,s[14]=o,s[15]=q,r._markAsUpdated(),r},a.Compose=function(b,c,d){var e=new a;return a.ComposeToRef(b,c,d,e),e},a.ComposeToRef=function(a,b,c,d){var e=d._m,f=b._x,p=b._y,g=b._z;b=b._w;var h=f+f,i=p+p,j=g+g,k=f*h,l=f*i;f=f*j;var n=p*i;p=p*j;g=g*j;h=b*h;i=b*i;b=b*j;j=a._x;var q=a._y;a=a._z;e[0]=(1-(n+g))*j,e[1]=(l+b)*j,e[2]=(f-i)*j,e[3]=0,e[4]=(l-b)*q,e[5]=(1-(k+g))*q,e[6]=(p+h)*q,e[7]=0,e[8]=(f+i)*a,e[9]=(p-h)*a,e[10]=(1-(k+n))*a,e[11]=0,e[12]=c._x,e[13]=c._y,e[14]=c._z,e[15]=1,d._markAsUpdated()},a.Identity=function(){var b=a.FromValues(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return b._updateIdentityStatus(!0),b},a.IdentityToRef=function(b){a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,b),b._updateIdentityStatus(!0)},a.Zero=function(){var b=a.FromValues(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);return b._updateIdentityStatus(!1),b},a.RotationX=function(b){var c=new a;return a.RotationXToRef(b,c),c},a.Invert=function(b){var c=new a;return b.invertToRef(c),c},a.RotationXToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(1,0,0,0,0,b,d,0,0,-d,b,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationY=function(b){var c=new a;return a.RotationYToRef(b,c),c},a.RotationYToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(b,0,-d,0,0,1,0,0,d,0,b,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationZ=function(b){var c=new a;return a.RotationZToRef(b,c),c},a.RotationZToRef=function(b,c){var d=Math.sin(b);b=Math.cos(b);a.FromValuesToRef(b,d,0,0,-d,b,0,0,0,0,1,0,0,0,0,1,c),c._updateIdentityStatus(1===b&&0===d)},a.RotationAxis=function(b,c){var d=new a;return a.RotationAxisToRef(b,c,d),d},a.RotationAxisToRef=function(a,b,c){var d=Math.sin(-b);b=Math.cos(-b);var e=1-b;a.normalize();var f=c._m;f[0]=a._x*a._x*e+b,f[1]=a._x*a._y*e-a._z*d,f[2]=a._x*a._z*e+a._y*d,f[3]=0,f[4]=a._y*a._x*e+a._z*d,f[5]=a._y*a._y*e+b,f[6]=a._y*a._z*e-a._x*d,f[7]=0,f[8]=a._z*a._x*e-a._y*d,f[9]=a._z*a._y*e+a._x*d,f[10]=a._z*a._z*e+b,f[11]=0,f[12]=0,f[13]=0,f[14]=0,f[15]=1,c._markAsUpdated()},a.RotationAlignToRef=function(a,b,c){var d=i.Dot(b,a),f=c._m;if(d<-1+e.a)f[0]=-1,f[1]=0,f[2]=0,f[3]=0,f[4]=0,f[5]=-1,f[6]=0,f[7]=0,f[8]=0,f[9]=0,f[10]=1,f[11]=0;else{b=i.Cross(b,a);a=1/(1+d);f[0]=b._x*b._x*a+d,f[1]=b._y*b._x*a-b._z,f[2]=b._z*b._x*a+b._y,f[3]=0,f[4]=b._x*b._y*a+b._z,f[5]=b._y*b._y*a+d,f[6]=b._z*b._y*a-b._x,f[7]=0,f[8]=b._x*b._z*a-b._y,f[9]=b._y*b._z*a+b._x,f[10]=b._z*b._z*a+d,f[11]=0}f[12]=0,f[13]=0,f[14]=0,f[15]=1,c._markAsUpdated()},a.RotationYawPitchRoll=function(b,c,d){var e=new a;return a.RotationYawPitchRollToRef(b,c,d,e),e},a.RotationYawPitchRollToRef=function(a,b,c,d){k.RotationYawPitchRollToRef(a,b,c,m.Quaternion[0]),m.Quaternion[0].toRotationMatrix(d)},a.Scaling=function(b,c,d){var e=new a;return a.ScalingToRef(b,c,d,e),e},a.ScalingToRef=function(b,c,d,e){a.FromValuesToRef(b,0,0,0,0,c,0,0,0,0,d,0,0,0,0,1,e),e._updateIdentityStatus(1===b&&1===c&&1===d)},a.Translation=function(b,c,d){var e=new a;return a.TranslationToRef(b,c,d,e),e},a.TranslationToRef=function(b,c,d,e){a.FromValuesToRef(1,0,0,0,0,1,0,0,0,0,1,0,b,c,d,1,e),e._updateIdentityStatus(0===b&&0===c&&0===d)},a.Lerp=function(b,c,d){var e=new a;return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){for(var e=d._m,a=a.m,b=b.m,f=0;f<16;f++)e[f]=a[f]*(1-c)+b[f]*c;d._markAsUpdated()},a.DecomposeLerp=function(b,c,d){var e=new a;return a.DecomposeLerpToRef(b,c,d,e),e},a.DecomposeLerpToRef=function(b,c,d,e){var f=m.Vector3[0],l=m.Quaternion[0],g=m.Vector3[1];b.decompose(f,l,g);b=m.Vector3[2];var h=m.Quaternion[1],j=m.Vector3[3];c.decompose(b,h,j);c=m.Vector3[4];i.LerpToRef(f,b,d,c);f=m.Quaternion[2];k.SlerpToRef(l,h,d,f);b=m.Vector3[5];i.LerpToRef(g,j,d,b),a.ComposeToRef(c,f,b,e)},a.LookAtLH=function(b,c,d){var e=new a;return a.LookAtLHToRef(b,c,d,e),e},a.LookAtLHToRef=function(b,c,d,e){var f=m.Vector3[0],h=m.Vector3[1],g=m.Vector3[2];c.subtractToRef(b,g),g.normalize(),i.CrossToRef(d,g,f);c=f.lengthSquared();0===c?f.x=1:f.normalizeFromLength(Math.sqrt(c)),i.CrossToRef(g,f,h),h.normalize();d=-i.Dot(f,b);c=-i.Dot(h,b);b=-i.Dot(g,b);a.FromValuesToRef(f._x,h._x,g._x,0,f._y,h._y,g._y,0,f._z,h._z,g._z,0,d,c,b,1,e)},a.LookAtRH=function(b,c,d){var e=new a;return a.LookAtRHToRef(b,c,d,e),e},a.LookAtRHToRef=function(b,c,d,e){var f=m.Vector3[0],h=m.Vector3[1],g=m.Vector3[2];b.subtractToRef(c,g),g.normalize(),i.CrossToRef(d,g,f);c=f.lengthSquared();0===c?f.x=1:f.normalizeFromLength(Math.sqrt(c)),i.CrossToRef(g,f,h),h.normalize();d=-i.Dot(f,b);c=-i.Dot(h,b);b=-i.Dot(g,b);a.FromValuesToRef(f._x,h._x,g._x,0,f._y,h._y,g._y,0,f._z,h._z,g._z,0,d,c,b,1,e)},a.LookDirectionLH=function(b,c){var d=new a;return a.LookDirectionLHToRef(b,c,d),d},a.LookDirectionLHToRef=function(b,c,d){var e=m.Vector3[0];e.copyFrom(b),e.scaleInPlace(-1);b=m.Vector3[1];i.CrossToRef(c,e,b),a.FromValuesToRef(b._x,b._y,b._z,0,c._x,c._y,c._z,0,e._x,e._y,e._z,0,0,0,0,1,d)},a.LookDirectionRH=function(b,c){var d=new a;return a.LookDirectionRHToRef(b,c,d),d},a.LookDirectionRHToRef=function(b,c,d){var e=m.Vector3[2];i.CrossToRef(c,b,e),a.FromValuesToRef(e._x,e._y,e._z,0,c._x,c._y,c._z,0,b._x,b._y,b._z,0,0,0,0,1,d)},a.OrthoLH=function(b,c,d,e,f){var g=new a;return a.OrthoLHToRef(b,c,d,e,g,f),g},a.OrthoLHToRef=function(b,c,d,e,f,h){b=2/b;c=2/c;var g=2/(e-d);e=-(e+d)/(e-d);a.FromValuesToRef(b,0,0,0,0,c,0,0,0,0,g,0,0,0,e,1,f),h&&f.multiplyToRef(o,f),f._updateIdentityStatus(1===b&&1===c&&1===g&&0===e)},a.OrthoOffCenterLH=function(b,c,d,e,f,i,g){var h=new a;return a.OrthoOffCenterLHToRef(b,c,d,e,f,i,h,g),h},a.OrthoOffCenterLHToRef=function(b,c,d,e,f,l,g,h){var i=2/(c-b),j=2/(e-d),k=2/(l-f);l=-(l+f)/(l-f);f=(b+c)/(b-c);b=(e+d)/(d-e);a.FromValuesToRef(i,0,0,0,0,j,0,0,0,0,k,0,f,b,l,1,g),h&&g.multiplyToRef(o,g),g._markAsUpdated()},a.OrthoOffCenterRH=function(b,c,d,e,f,i,g){var h=new a;return a.OrthoOffCenterRHToRef(b,c,d,e,f,i,h,g),h},a.OrthoOffCenterRHToRef=function(b,c,d,e,f,i,g,h){a.OrthoOffCenterLHToRef(b,c,d,e,f,i,g,h),g._m[10]*=-1},a.PerspectiveLH=function(b,c,d,e,f,i){void 0===i&&(i=0);var g=new a;b=2*d/b;c=2*d/c;var h=(e+d)/(e-d);e=-2*e*d/(e-d);d=Math.tan(i);return a.FromValuesToRef(b,0,0,0,0,c,0,d,0,0,h,1,0,0,e,0,g),f&&g.multiplyToRef(o,g),g._updateIdentityStatus(!1),g},a.PerspectiveFovLH=function(b,c,d,e,f,h){void 0===h&&(h=0);var g=new a;return a.PerspectiveFovLHToRef(b,c,d,e,g,!0,f,h),g},a.PerspectiveFovLHToRef=function(b,c,d,e,f,j,g,h){void 0===j&&(j=!0),void 0===h&&(h=0);d=d;e=e;b=1/Math.tan(.5*b);var i=j?b/c:b;j=j?b:b*c;b=0!==e?(e+d)/(e-d):1;c=0!==e?-2*e*d/(e-d):-2*d;e=Math.tan(h);a.FromValuesToRef(i,0,0,0,0,j,0,e,0,0,b,1,0,0,c,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovReverseLHToRef=function(b,c,d,e,f,i,g,h){void 0===i&&(i=!0),void 0===h&&(h=0);e=1/Math.tan(.5*b);b=i?e/c:e;i=i?e:e*c;e=Math.tan(h);a.FromValuesToRef(b,0,0,0,0,i,0,e,0,0,-d,1,0,0,1,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovRH=function(b,c,d,e,f,h){void 0===h&&(h=0);var g=new a;return a.PerspectiveFovRHToRef(b,c,d,e,g,!0,f,h),g},a.PerspectiveFovRHToRef=function(b,c,d,e,f,j,g,h){void 0===j&&(j=!0),void 0===h&&(h=0);d=d;e=e;b=1/Math.tan(.5*b);var i=j?b/c:b;j=j?b:b*c;b=0!==e?-(e+d)/(e-d):-1;c=0!==e?-2*e*d/(e-d):-2*d;e=Math.tan(h);a.FromValuesToRef(i,0,0,0,0,j,0,e,0,0,b,-1,0,0,c,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovReverseRHToRef=function(b,c,d,e,f,i,g,h){void 0===i&&(i=!0),void 0===h&&(h=0);e=1/Math.tan(.5*b);b=i?e/c:e;i=i?e:e*c;e=Math.tan(h);a.FromValuesToRef(b,0,0,0,0,i,0,e,0,0,-d,-1,0,0,-1,0,f),g&&f.multiplyToRef(o,f),f._updateIdentityStatus(!1)},a.PerspectiveFovWebVRToRef=function(a,b,c,d,e,f,l){void 0===e&&(e=!1),void 0===l&&(l=0);e=e?-1:1;var g=Math.tan(a.upDegrees*Math.PI/180),h=Math.tan(a.downDegrees*Math.PI/180),i=Math.tan(a.leftDegrees*Math.PI/180);a=Math.tan(a.rightDegrees*Math.PI/180);var j=2/(i+a),k=2/(g+h);l=Math.tan(l);var n=d._m;n[0]=j,n[1]=n[2]=n[3]=n[4]=0,n[5]=k,n[6]=0,n[7]=l,n[8]=(i-a)*j*.5,n[9]=-(g-h)*k*.5,n[10]=-c/(b-c),n[11]=1*e,n[12]=n[13]=n[15]=0,n[14]=-2*c*b/(c-b),f&&d.multiplyToRef(o,d),d._markAsUpdated()},a.GetFinalMatrix=function(b,c,d,e,f,j){var g=b.width,h=b.height,i=b.x;b=b.y;j=a.FromValues(g/2,0,0,0,0,-h/2,0,0,0,0,j-f,0,i+g/2,h/2+b,f,1);i=m.Matrix[0];return c.multiplyToRef(d,i),i.multiplyToRef(e,i),i.multiply(j)},a.GetAsMatrix2x2=function(a){a=a.m;a=[a[0],a[1],a[4],a[5]];return g.a.MatrixUse64Bits?a:new Float32Array(a)},a.GetAsMatrix3x3=function(a){a=a.m;a=[a[0],a[1],a[2],a[4],a[5],a[6],a[8],a[9],a[10]];return g.a.MatrixUse64Bits?a:new Float32Array(a)},a.Transpose=function(b){var c=new a;return a.TransposeToRef(b,c),c},a.TransposeToRef=function(a,b){var c=b._m,d=a.m;c[0]=d[0],c[1]=d[4],c[2]=d[8],c[3]=d[12],c[4]=d[1],c[5]=d[5],c[6]=d[9],c[7]=d[13],c[8]=d[2],c[9]=d[6],c[10]=d[10],c[11]=d[14],c[12]=d[3],c[13]=d[7],c[14]=d[11],c[15]=d[15],b._markAsUpdated(),b._updateIdentityStatus(a._isIdentity,a._isIdentityDirty)},a.Reflection=function(b){var c=new a;return a.ReflectionToRef(b,c),c},a.ReflectionToRef=function(b,c){b.normalize();var d=b.normal.x,e=b.normal.y,f=b.normal.z,i=-2*d,g=-2*e,h=-2*f;a.FromValuesToRef(i*d+1,g*d,h*d,0,i*e,g*e+1,h*e,0,i*f,g*f,h*f+1,0,i*b.d,g*b.d,h*b.d,1,c)},a.FromXYZAxesToRef=function(b,c,d,e){a.FromValuesToRef(b._x,b._y,b._z,0,c._x,c._y,c._z,0,d._x,d._y,d._z,0,0,0,0,1,e)},a.FromQuaternionToRef=function(a,b){var c=a._x*a._x,d=a._y*a._y,e=a._z*a._z,f=a._x*a._y,j=a._z*a._w,g=a._z*a._x,h=a._y*a._w,i=a._y*a._z;a=a._x*a._w;b._m[0]=1-2*(d+e),b._m[1]=2*(f+j),b._m[2]=2*(g-h),b._m[3]=0,b._m[4]=2*(f-j),b._m[5]=1-2*(e+c),b._m[6]=2*(i+a),b._m[7]=0,b._m[8]=2*(g+h),b._m[9]=2*(i-a),b._m[10]=1-2*(d+c),b._m[11]=0,b._m[12]=0,b._m[13]=0,b._m[14]=0,b._m[15]=1,b._markAsUpdated()},a._updateFlagSeed=0,a._identityReadOnly=a.Identity(),a}(),m=function(){function a(){}return a.Vector3=f.a.BuildTuple(11,i.Zero),a.Matrix=f.a.BuildTuple(2,l.Identity),a.Quaternion=f.a.BuildTuple(3,k.Zero),a}(),n=function(){function a(){}return a.Vector2=f.a.BuildTuple(3,h.Zero),a.Vector3=f.a.BuildTuple(13,i.Zero),a.Vector4=f.a.BuildTuple(3,j.Zero),a.Quaternion=f.a.BuildTuple(2,k.Zero),a.Matrix=f.a.BuildTuple(8,l.Identity),a}();Object(a.b)("BABYLON.Vector2",h),Object(a.b)("BABYLON.Vector3",i),Object(a.b)("BABYLON.Vector4",j),Object(a.b)("BABYLON.Matrix",l);var o=l.FromValues(1,0,0,0,0,1,0,0,0,0,.5,0,0,0,.5,1)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.ALPHA_DISABLE=0,a.ALPHA_ADD=1,a.ALPHA_COMBINE=2,a.ALPHA_SUBTRACT=3,a.ALPHA_MULTIPLY=4,a.ALPHA_MAXIMIZED=5,a.ALPHA_ONEONE=6,a.ALPHA_PREMULTIPLIED=7,a.ALPHA_PREMULTIPLIED_PORTERDUFF=8,a.ALPHA_INTERPOLATE=9,a.ALPHA_SCREENMODE=10,a.ALPHA_ONEONE_ONEONE=11,a.ALPHA_ALPHATOCOLOR=12,a.ALPHA_REVERSEONEMINUS=13,a.ALPHA_SRC_DSTONEMINUSSRCALPHA=14,a.ALPHA_ONEONE_ONEZERO=15,a.ALPHA_EXCLUSION=16,a.ALPHA_LAYER_ACCUMULATE=17,a.ALPHA_EQUATION_ADD=0,a.ALPHA_EQUATION_SUBSTRACT=1,a.ALPHA_EQUATION_REVERSE_SUBTRACT=2,a.ALPHA_EQUATION_MAX=3,a.ALPHA_EQUATION_MIN=4,a.ALPHA_EQUATION_DARKEN=5,a.DELAYLOADSTATE_NONE=0,a.DELAYLOADSTATE_LOADED=1,a.DELAYLOADSTATE_LOADING=2,a.DELAYLOADSTATE_NOTLOADED=4,a.NEVER=512,a.ALWAYS=519,a.LESS=513,a.EQUAL=514,a.LEQUAL=515,a.GREATER=516,a.GEQUAL=518,a.NOTEQUAL=517,a.KEEP=7680,a.ZERO=0,a.REPLACE=7681,a.INCR=7682,a.DECR=7683,a.INVERT=5386,a.INCR_WRAP=34055,a.DECR_WRAP=34056,a.TEXTURE_CLAMP_ADDRESSMODE=0,a.TEXTURE_WRAP_ADDRESSMODE=1,a.TEXTURE_MIRROR_ADDRESSMODE=2,a.TEXTURE_CREATIONFLAG_STORAGE=1,a.TEXTUREFORMAT_ALPHA=0,a.TEXTUREFORMAT_LUMINANCE=1,a.TEXTUREFORMAT_LUMINANCE_ALPHA=2,a.TEXTUREFORMAT_RGB=4,a.TEXTUREFORMAT_RGBA=5,a.TEXTUREFORMAT_RED=6,a.TEXTUREFORMAT_R=6,a.TEXTUREFORMAT_RG=7,a.TEXTUREFORMAT_RED_INTEGER=8,a.TEXTUREFORMAT_R_INTEGER=8,a.TEXTUREFORMAT_RG_INTEGER=9,a.TEXTUREFORMAT_RGB_INTEGER=10,a.TEXTUREFORMAT_RGBA_INTEGER=11,a.TEXTUREFORMAT_BGRA=12,a.TEXTUREFORMAT_DEPTH24_STENCIL8=13,a.TEXTUREFORMAT_DEPTH32_FLOAT=14,a.TEXTUREFORMAT_DEPTH16=15,a.TEXTUREFORMAT_DEPTH24=16,a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM=36492,a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT=36495,a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT=36494,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5=33779,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3=33778,a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1=33777,a.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1=33776,a.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4=37808,a.TEXTURETYPE_UNSIGNED_BYTE=0,a.TEXTURETYPE_UNSIGNED_INT=0,a.TEXTURETYPE_FLOAT=1,a.TEXTURETYPE_HALF_FLOAT=2,a.TEXTURETYPE_BYTE=3,a.TEXTURETYPE_SHORT=4,a.TEXTURETYPE_UNSIGNED_SHORT=5,a.TEXTURETYPE_INT=6,a.TEXTURETYPE_UNSIGNED_INTEGER=7,a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4=8,a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1=9,a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5=10,a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV=11,a.TEXTURETYPE_UNSIGNED_INT_24_8=12,a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV=13,a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV=14,a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV=15,a.TEXTURETYPE_UNDEFINED=16,a.TEXTURE_NEAREST_SAMPLINGMODE=1,a.TEXTURE_NEAREST_NEAREST=1,a.TEXTURE_BILINEAR_SAMPLINGMODE=2,a.TEXTURE_LINEAR_LINEAR=2,a.TEXTURE_TRILINEAR_SAMPLINGMODE=3,a.TEXTURE_LINEAR_LINEAR_MIPLINEAR=3,a.TEXTURE_NEAREST_NEAREST_MIPNEAREST=4,a.TEXTURE_NEAREST_LINEAR_MIPNEAREST=5,a.TEXTURE_NEAREST_LINEAR_MIPLINEAR=6,a.TEXTURE_NEAREST_LINEAR=7,a.TEXTURE_NEAREST_NEAREST_MIPLINEAR=8,a.TEXTURE_LINEAR_NEAREST_MIPNEAREST=9,a.TEXTURE_LINEAR_NEAREST_MIPLINEAR=10,a.TEXTURE_LINEAR_LINEAR_MIPNEAREST=11,a.TEXTURE_LINEAR_NEAREST=12,a.TEXTURE_EXPLICIT_MODE=0,a.TEXTURE_SPHERICAL_MODE=1,a.TEXTURE_PLANAR_MODE=2,a.TEXTURE_CUBIC_MODE=3,a.TEXTURE_PROJECTION_MODE=4,a.TEXTURE_SKYBOX_MODE=5,a.TEXTURE_INVCUBIC_MODE=6,a.TEXTURE_EQUIRECTANGULAR_MODE=7,a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE=8,a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE=9,a.TEXTURE_FILTERING_QUALITY_OFFLINE=4096,a.TEXTURE_FILTERING_QUALITY_HIGH=64,a.TEXTURE_FILTERING_QUALITY_MEDIUM=16,a.TEXTURE_FILTERING_QUALITY_LOW=8,a.SCALEMODE_FLOOR=1,a.SCALEMODE_NEAREST=2,a.SCALEMODE_CEILING=3,a.MATERIAL_TextureDirtyFlag=1,a.MATERIAL_LightDirtyFlag=2,a.MATERIAL_FresnelDirtyFlag=4,a.MATERIAL_AttributesDirtyFlag=8,a.MATERIAL_MiscDirtyFlag=16,a.MATERIAL_PrePassDirtyFlag=32,a.MATERIAL_AllDirtyFlag=63,a.MATERIAL_TriangleFillMode=0,a.MATERIAL_WireFrameFillMode=1,a.MATERIAL_PointFillMode=2,a.MATERIAL_PointListDrawMode=3,a.MATERIAL_LineListDrawMode=4,a.MATERIAL_LineLoopDrawMode=5,a.MATERIAL_LineStripDrawMode=6,a.MATERIAL_TriangleStripDrawMode=7,a.MATERIAL_TriangleFanDrawMode=8,a.MATERIAL_ClockWiseSideOrientation=0,a.MATERIAL_CounterClockWiseSideOrientation=1,a.ACTION_NothingTrigger=0,a.ACTION_OnPickTrigger=1,a.ACTION_OnLeftPickTrigger=2,a.ACTION_OnRightPickTrigger=3,a.ACTION_OnCenterPickTrigger=4,a.ACTION_OnPickDownTrigger=5,a.ACTION_OnDoublePickTrigger=6,a.ACTION_OnPickUpTrigger=7,a.ACTION_OnPickOutTrigger=16,a.ACTION_OnLongPressTrigger=8,a.ACTION_OnPointerOverTrigger=9,a.ACTION_OnPointerOutTrigger=10,a.ACTION_OnEveryFrameTrigger=11,a.ACTION_OnIntersectionEnterTrigger=12,a.ACTION_OnIntersectionExitTrigger=13,a.ACTION_OnKeyDownTrigger=14,a.ACTION_OnKeyUpTrigger=15,a.PARTICLES_BILLBOARDMODE_Y=2,a.PARTICLES_BILLBOARDMODE_ALL=7,a.PARTICLES_BILLBOARDMODE_STRETCHED=8,a.MESHES_CULLINGSTRATEGY_STANDARD=0,a.MESHES_CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY=1,a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION=2,a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY=3,a.SCENELOADER_NO_LOGGING=0,a.SCENELOADER_MINIMAL_LOGGING=1,a.SCENELOADER_SUMMARY_LOGGING=2,a.SCENELOADER_DETAILED_LOGGING=3,a.PREPASS_IRRADIANCE_TEXTURE_TYPE=0,a.PREPASS_POSITION_TEXTURE_TYPE=1,a.PREPASS_VELOCITY_TEXTURE_TYPE=2,a.PREPASS_REFLECTIVITY_TEXTURE_TYPE=3,a.PREPASS_COLOR_TEXTURE_TYPE=4,a.PREPASS_DEPTH_TEXTURE_TYPE=5,a.PREPASS_NORMAL_TEXTURE_TYPE=6,a.PREPASS_ALBEDO_SQRT_TEXTURE_TYPE=7,a.BUFFER_CREATIONFLAG_READ=1,a.BUFFER_CREATIONFLAG_WRITE=2,a.BUFFER_CREATIONFLAG_READWRITE=3,a.BUFFER_CREATIONFLAG_UNIFORM=4,a.BUFFER_CREATIONFLAG_VERTEX=8,a.BUFFER_CREATIONFLAG_INDEX=16,a.BUFFER_CREATIONFLAG_STORAGE=32,a.SUBMESH_DRAWWRAPPER_MAINPASS="bjs_mainpass",a.SUBMESH_DRAWWRAPPER_SHADOWGENERATOR_PREFIX="bjs_shadowgenerator_",a.SUBMESH_DRAWWRAPPER_DEPTHRENDERER_PREFIX="bjs_depthrenderer_",a.SUBMESH_DRAWWRAPPER_OUTLINERENDERER_PREFIX="bjs_outlinerenderer_",a.INPUT_ALT_KEY=18,a.INPUT_CTRL_KEY=17,a.INPUT_META_KEY1=91,a.INPUT_META_KEY2=92,a.INPUT_META_KEY3=93,a.INPUT_SHIFT_KEY=16,a.SNAPSHOTRENDERING_STANDARD=0,a.SNAPSHOTRENDERING_FAST=1,a.PERSPECTIVE_CAMERA=0,a.ORTHOGRAPHIC_CAMERA=1,a.FOVMODE_VERTICAL_FIXED=0,a.FOVMODE_HORIZONTAL_FIXED=1,a.RIG_MODE_NONE=0,a.RIG_MODE_STEREOSCOPIC_ANAGLYPH=10,a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL=11,a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED=12,a.RIG_MODE_STEREOSCOPIC_OVERUNDER=13,a.RIG_MODE_STEREOSCOPIC_INTERLACED=14,a.RIG_MODE_VR=20,a.RIG_MODE_WEBVR=21,a.RIG_MODE_CUSTOM=22,a.MAX_SUPPORTED_UV_SETS=6,a}()},function(a,b,c){c.d(b,"d",function(){return e}),c.d(b,"a",function(){return f}),c.d(b,"c",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"e",function(){return i}),c.d(b,"f",function(){return j});var d=function(a,b){return(d=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])})(a,b)};function e(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Class extends value "+String(b)+" is not a constructor or null");function c(){this.constructor=a}d(a,b),a.prototype=null===b?Object.create(b):(c.prototype=b.prototype,new c)}var f=function(){return(f=Object.assign||function(a){for(var b,c=1,d=arguments.length;c=0;h--)(e=a[h])&&(g=(f<3?e(g):f>3?e(b,c,g):e(b,c))||g);return f>3&&g&&Object.defineProperty(b,c,g),g}function h(a,b,c,d){return new(c||(c=Promise))(function(e,f){function g(a){try{i(d.next(a))}catch(a){f(a)}}function h(a){try{i(d["throw"](a))}catch(a){f(a)}}function i(a){var b;a.done?e(a.value):(b=a.value,b instanceof c?b:new c(function(a){a(b)})).then(g,h)}i((d=d.apply(a,b||[])).next())})}function i(a,b){var c,d,e,f={label:0,sent:function(){if(1&e[0])throw e[1];return e[1]},trys:[],ops:[]};return g={next:g(0),"throw":g(1),"return":g(2)},"function"==typeof Symbol&&(g[Symbol.iterator]=function(){return this}),g;function g(g){return function(h){return function(g){if(c)throw new TypeError("Generator is already executing.");for(;f;)try{if(c=1,d&&(e=2&g[0]?d["return"]:g[0]?d["throw"]||((e=d["return"])&&e.call(d),0):d.next)&&!(e=e.call(d,g[1])).done)return e;switch(d=0,e&&(g=[2&g[0],e.value]),g[0]){case 0:case 1:e=g;break;case 4:return f.label++,{value:g[1],done:!1};case 5:f.label++,d=g[1],g=[0];continue;case 7:g=f.ops.pop(),f.trys.pop();continue;default:if(!(e=f.trys,(e=e.length>0&&e[e.length-1])||6!==g[0]&&2!==g[0])){f=0;continue}if(3===g[0]&&(!e||g[1]>e[0]&&g[1]0},a.prototype.clear=function(){this._observers=new Array,this._onObserverAdded=null},a.prototype.clone=function(){var b=new a;return b._observers=this._observers.slice(0),b},a.prototype.hasSpecificMask=function(a){void 0===a&&(a=-1);for(var b=0,c=this._observers;b"+c+"
";a._AddLogEntry(c),a._GenerateLimitMessage(b,0)}},a._WarnDisabled=function(a,b){},a._WarnEnabled=function(b,c){if(void 0===c||a._CheckLimit(b,c)){c=a._FormatMessage(b);c="
"+b+"

";a._AddLogEntry(c),a._GenerateLimitMessage(b,1)}},a._ErrorDisabled=function(a,b){},a._ErrorEnabled=function(b,c){if(void 0===c||a._CheckLimit(b,c)){c=a._FormatMessage(b);a.errorsCount++,!1;c="
"+c+"

";a._AddLogEntry(c),a._GenerateLimitMessage(b,2)}},Object.defineProperty(a,"LogCache",{get:function(){return a._LogCache},enumerable:!1,configurable:!0}),a.ClearLogCache=function(){a._LogCache="",a._LogLimitOutputs={},a.errorsCount=0},Object.defineProperty(a,"LogLevels",{set:function(b){(b&a.MessageLogLevel)===a.MessageLogLevel?a.Log=a._LogEnabled:a.Log=a._LogDisabled,(b&a.WarningLogLevel)===a.WarningLogLevel?a.Warn=a._WarnEnabled:a.Warn=a._WarnDisabled,(b&a.ErrorLogLevel)===a.ErrorLogLevel?a.Error=a._ErrorEnabled:a.Error=a._ErrorDisabled},enumerable:!1,configurable:!0}),a.NoneLogLevel=0,a.MessageLogLevel=1,a.WarningLogLevel=2,a.ErrorLogLevel=4,a.AllLogLevel=7,a.MessageLimitReached="Too many %TYPE%s (%LIMIT%), no more %TYPE%s will be reported for this message.",a._LogCache="",a._LogLimitOutputs={},a.errorsCount=0,a.Log=a._LogEnabled,a.Warn=a._WarnEnabled,a.Error=a._ErrorEnabled,a}()},function(a,b,c){c.d(b,"a",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"c",function(){return i});var d=c(14),e=c(22),f=c(51);a=c(10);var g=function(){function a(a,b,c){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),this.r=a,this.g=b,this.b=c}return a.prototype.toString=function(){return"{R: "+this.r+" G:"+this.g+" B:"+this.b+"}"},a.prototype.getClassName=function(){return"Color3"},a.prototype.getHashCode=function(){var a=255*this.r|0;return 397*(397*a^(255*this.g|0))^(255*this.b|0)},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.r,a[b+1]=this.g,a[b+2]=this.b,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.toColor4=function(a){return void 0===a&&(a=1),new h(this.r,this.g,this.b,a)},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.toLuminance=function(){return.3*this.r+.59*this.g+.11*this.b},a.prototype.multiply=function(b){return new a(this.r*b.r,this.g*b.g,this.b*b.b)},a.prototype.multiplyToRef=function(a,b){return b.r=this.r*a.r,b.g=this.g*a.g,b.b=this.b*a.b,this},a.prototype.equals=function(a){return a&&this.r===a.r&&this.g===a.g&&this.b===a.b},a.prototype.equalsFloats=function(a,b,c){return this.r===a&&this.g===b&&this.b===c},a.prototype.scale=function(b){return new a(this.r*b,this.g*b,this.b*b)},a.prototype.scaleToRef=function(a,b){return b.r=this.r*a,b.g=this.g*a,b.b=this.b*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.r+=this.r*a,b.g+=this.g*a,b.b+=this.b*a,this},a.prototype.clampToRef=function(a,b,c){return void 0===a&&(a=0),void 0===b&&(b=1),c.r=d.a.Clamp(this.r,a,b),c.g=d.a.Clamp(this.g,a,b),c.b=d.a.Clamp(this.b,a,b),this},a.prototype.add=function(b){return new a(this.r+b.r,this.g+b.g,this.b+b.b)},a.prototype.addToRef=function(a,b){return b.r=this.r+a.r,b.g=this.g+a.g,b.b=this.b+a.b,this},a.prototype.subtract=function(b){return new a(this.r-b.r,this.g-b.g,this.b-b.b)},a.prototype.subtractToRef=function(a,b){return b.r=this.r-a.r,b.g=this.g-a.g,b.b=this.b-a.b,this},a.prototype.clone=function(){return new a(this.r,this.g,this.b)},a.prototype.copyFrom=function(a){return this.r=a.r,this.g=a.g,this.b=a.b,this},a.prototype.copyFromFloats=function(a,b,c){return this.r=a,this.g=b,this.b=c,this},a.prototype.set=function(a,b,c){return this.copyFromFloats(a,b,c)},a.prototype.toHexString=function(){var a=Math.round(255*this.r),b=Math.round(255*this.g),c=Math.round(255*this.b);return"#"+d.a.ToHex(a)+d.a.ToHex(b)+d.a.ToHex(c)},a.prototype.toLinearSpace=function(){var b=new a;return this.toLinearSpaceToRef(b),b},a.prototype.toHSV=function(){var b=new a;return this.toHSVToRef(b),b},a.prototype.toHSVToRef=function(a){var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),j=0,g=0,h=e,i=e-f;0!==e&&(g=i/e),e!=f&&(e==b?(j=(c-d)/i,c=0&&a<=1?(e=b,f=h):a>=1&&a<=2?(e=h,f=b):a>=2&&a<=3?(f=b,g=h):a>=3&&a<=4?(f=h,g=b):a>=4&&a<=5?(e=h,g=b):a>=5&&a<=6&&(e=b,g=h);a=c-b;d.set(e+a,f+a,g+a)},a.FromHexString=function(b){if("#"!==b.substring(0,1)||7!==b.length)return new a(0,0,0);var c=parseInt(b.substring(1,3),16),d=parseInt(b.substring(3,5),16);b=parseInt(b.substring(5,7),16);return a.FromInts(c,d,b)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2])},a.FromArrayToRef=function(a,b,c){void 0===b&&(b=0),c.r=a[b],c.g=a[b+1],c.b=a[b+2]},a.FromInts=function(b,c,d){return new a(b/255,c/255,d/255)},a.Lerp=function(b,c,d){var e=new a(0,0,0);return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){d.r=a.r+(b.r-a.r)*c,d.g=a.g+(b.g-a.g)*c,d.b=a.b+(b.b-a.b)*c},a.Hermite1stDerivative=function(b,c,d,e,f){var g=a.Black();return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.r=6*(g-e)*a.r+(3*g-4*e+1)*b.r+6*(-g+e)*c.r+(3*g-2*e)*d.r,f.g=6*(g-e)*a.g+(3*g-4*e+1)*b.g+6*(-g+e)*c.g+(3*g-2*e)*d.g,f.b=6*(g-e)*a.b+(3*g-4*e+1)*b.b+6*(-g+e)*c.b+(3*g-2*e)*d.b},a.Red=function(){return new a(1,0,0)},a.Green=function(){return new a(0,1,0)},a.Blue=function(){return new a(0,0,1)},a.Black=function(){return new a(0,0,0)},Object.defineProperty(a,"BlackReadOnly",{get:function(){return a._BlackReadOnly},enumerable:!1,configurable:!0}),a.White=function(){return new a(1,1,1)},a.Purple=function(){return new a(.5,0,.5)},a.Magenta=function(){return new a(1,0,1)},a.Yellow=function(){return new a(1,1,0)},a.Gray=function(){return new a(.5,.5,.5)},a.Teal=function(){return new a(0,1,1)},a.Random=function(){return new a(Math.random(),Math.random(),Math.random())},a._BlackReadOnly=a.Black(),a}(),h=function(){function a(a,b,c,d){void 0===a&&(a=0),void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),this.r=a,this.g=b,this.b=c,this.a=d}return a.prototype.addInPlace=function(a){return this.r+=a.r,this.g+=a.g,this.b+=a.b,this.a+=a.a,this},a.prototype.asArray=function(){var a=new Array;return this.toArray(a,0),a},a.prototype.toArray=function(a,b){return void 0===b&&(b=0),a[b]=this.r,a[b+1]=this.g,a[b+2]=this.b,a[b+3]=this.a,this},a.prototype.fromArray=function(b,c){return void 0===c&&(c=0),a.FromArrayToRef(b,c,this),this},a.prototype.equals=function(a){return a&&this.r===a.r&&this.g===a.g&&this.b===a.b&&this.a===a.a},a.prototype.add=function(b){return new a(this.r+b.r,this.g+b.g,this.b+b.b,this.a+b.a)},a.prototype.subtract=function(b){return new a(this.r-b.r,this.g-b.g,this.b-b.b,this.a-b.a)},a.prototype.subtractToRef=function(a,b){return b.r=this.r-a.r,b.g=this.g-a.g,b.b=this.b-a.b,b.a=this.a-a.a,this},a.prototype.scale=function(b){return new a(this.r*b,this.g*b,this.b*b,this.a*b)},a.prototype.scaleToRef=function(a,b){return b.r=this.r*a,b.g=this.g*a,b.b=this.b*a,b.a=this.a*a,this},a.prototype.scaleAndAddToRef=function(a,b){return b.r+=this.r*a,b.g+=this.g*a,b.b+=this.b*a,b.a+=this.a*a,this},a.prototype.clampToRef=function(a,b,c){return void 0===a&&(a=0),void 0===b&&(b=1),c.r=d.a.Clamp(this.r,a,b),c.g=d.a.Clamp(this.g,a,b),c.b=d.a.Clamp(this.b,a,b),c.a=d.a.Clamp(this.a,a,b),this},a.prototype.multiply=function(b){return new a(this.r*b.r,this.g*b.g,this.b*b.b,this.a*b.a)},a.prototype.multiplyToRef=function(a,b){return b.r=this.r*a.r,b.g=this.g*a.g,b.b=this.b*a.b,b.a=this.a*a.a,b},a.prototype.toString=function(){return"{R: "+this.r+" G:"+this.g+" B:"+this.b+" A:"+this.a+"}"},a.prototype.getClassName=function(){return"Color4"},a.prototype.getHashCode=function(){var a=255*this.r|0;return 397*(397*(397*a^(255*this.g|0))^(255*this.b|0))^(255*this.a|0)},a.prototype.clone=function(){return new a(this.r,this.g,this.b,this.a)},a.prototype.copyFrom=function(a){return this.r=a.r,this.g=a.g,this.b=a.b,this.a=a.a,this},a.prototype.copyFromFloats=function(a,b,c,d){return this.r=a,this.g=b,this.b=c,this.a=d,this},a.prototype.set=function(a,b,c,d){return this.copyFromFloats(a,b,c,d)},a.prototype.toHexString=function(a){void 0===a&&(a=!1);var b=Math.round(255*this.r),c=Math.round(255*this.g),e=Math.round(255*this.b);if(a)return"#"+d.a.ToHex(b)+d.a.ToHex(c)+d.a.ToHex(e);a=Math.round(255*this.a);return"#"+d.a.ToHex(b)+d.a.ToHex(c)+d.a.ToHex(e)+d.a.ToHex(a)},a.prototype.toLinearSpace=function(){var b=new a;return this.toLinearSpaceToRef(b),b},a.prototype.toLinearSpaceToRef=function(a){return a.r=Math.pow(this.r,e.d),a.g=Math.pow(this.g,e.d),a.b=Math.pow(this.b,e.d),a.a=this.a,this},a.prototype.toGammaSpace=function(){var b=new a;return this.toGammaSpaceToRef(b),b},a.prototype.toGammaSpaceToRef=function(a){return a.r=Math.pow(this.r,e.c),a.g=Math.pow(this.g,e.c),a.b=Math.pow(this.b,e.c),a.a=this.a,this},a.FromHexString=function(b){if("#"!==b.substring(0,1)||9!==b.length)return new a(0,0,0,0);var c=parseInt(b.substring(1,3),16),d=parseInt(b.substring(3,5),16),e=parseInt(b.substring(5,7),16);b=parseInt(b.substring(7,9),16);return a.FromInts(c,d,e,b)},a.Lerp=function(b,c,d){var e=new a(0,0,0,0);return a.LerpToRef(b,c,d,e),e},a.LerpToRef=function(a,b,c,d){d.r=a.r+(b.r-a.r)*c,d.g=a.g+(b.g-a.g)*c,d.b=a.b+(b.b-a.b)*c,d.a=a.a+(b.a-a.a)*c},a.Hermite1stDerivative=function(b,c,d,e,f){var g=new a;return this.Hermite1stDerivativeToRef(b,c,d,e,f,g),g},a.Hermite1stDerivativeToRef=function(a,b,c,d,e,f){var g=e*e;f.r=6*(g-e)*a.r+(3*g-4*e+1)*b.r+6*(-g+e)*c.r+(3*g-2*e)*d.r,f.g=6*(g-e)*a.g+(3*g-4*e+1)*b.g+6*(-g+e)*c.g+(3*g-2*e)*d.g,f.b=6*(g-e)*a.b+(3*g-4*e+1)*b.b+6*(-g+e)*c.b+(3*g-2*e)*d.b,f.a=6*(g-e)*a.a+(3*g-4*e+1)*b.a+6*(-g+e)*c.a+(3*g-2*e)*d.a},a.FromColor3=function(b,c){return void 0===c&&(c=1),new a(b.r,b.g,b.b,c)},a.FromArray=function(b,c){return void 0===c&&(c=0),new a(b[c],b[c+1],b[c+2],b[c+3])},a.FromArrayToRef=function(a,b,c){void 0===b&&(b=0),c.r=a[b],c.g=a[b+1],c.b=a[b+2],c.a=a[b+3]},a.FromInts=function(b,c,d,e){return new a(b/255,c/255,d/255,e/255)},a.CheckColors4=function(a,b){if(a.length===3*b){for(var b=[],c=0;c0},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasThinInstances",{get:function(){var a;return(null!==(a=this._thinInstanceDataStorage.instancesCount)&&void 0!==a?a:0)>0},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcedInstanceCount",{get:function(){return this._internalMeshDataInfo._forcedInstanceCount},set:function(a){this._internalMeshDataInfo._forcedInstanceCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"source",{get:function(){return this._internalMeshDataInfo._source},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cloneMeshMap",{get:function(){return this._internalMeshDataInfo.meshMap},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isUnIndexed",{get:function(){return this._unIndexed},set:function(a){this._unIndexed!==a&&(this._unIndexed=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"worldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.instancesData},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"previousWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.instancesPreviousData},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manualUpdateOfWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.manualUpdate},set:function(a){this._instanceDataStorage.manualUpdate=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manualUpdateOfPreviousWorldMatrixInstancedBuffer",{get:function(){return this._instanceDataStorage.previousManualUpdate},set:function(a){this._instanceDataStorage.previousManualUpdate=a},enumerable:!1,configurable:!0}),b.prototype.instantiateHierarchy=function(a,b,c){void 0===a&&(a=null);var d=!(this.getTotalVertices()>0)||b&&b.doNotInstantiate?this.clone("Clone of "+(this.name||this.id),a||this.parent,!0):this.createInstance("instance of "+(this.name||this.id));d&&(d.parent=a||this.parent,d.position=this.position.clone(),d.scaling=this.scaling.clone(),this.rotationQuaternion?d.rotationQuaternion=this.rotationQuaternion.clone():d.rotation=this.rotation.clone(),c&&c(this,d));for(var a=0,e=this.getChildTransformNodes(!0);a0},enumerable:!1,configurable:!0}),b.prototype.getLODLevels=function(){return this._internalMeshDataInfo._LODLevels},b.prototype._sortLODLevels=function(){var a=this._internalMeshDataInfo._useLODScreenCoverage?-1:1;this._internalMeshDataInfo._LODLevels.sort(function(b,c){return b.distanceOrScreenCoveragec.distanceOrScreenCoverage?-a:0})},b.prototype.addLODLevel=function(a,b){if(b&&b._masterMesh)return u.a.Warn("You cannot use a mesh as LOD level twice"),this;a=new y.a(a,b);return this._internalMeshDataInfo._LODLevels.push(a),b&&(b._masterMesh=this),this._sortLODLevels(),this},b.prototype.getLODLevelAtDistance=function(a){for(var b=this._internalMeshDataInfo,c=0;cf*e)return this.onLODLevelSelection&&this.onLODLevelSelection(e,this,this),this;for(a=0;a0||this.hasThinInstances);this.computeWorldMatrix();b=this.material||d.defaultMaterial;if(b)if(b._storeEffectOnSubMeshes)for(var d=0,h=this.subMeshes;d0){var c=this.getIndices();if(!c)return null;c=c.length;var d=!1;if(a)d=!0;else for(var a=0,e=this.subMeshes;ac){d=!0;break}if(f.verticesStart+f.verticesCount>b){d=!0;break}}if(!d)return this.subMeshes[0]}return this.releaseSubMeshes(),new o.a(0,0,b,0,this.getTotalIndices(),this)},b.prototype.subdivide=function(a){if(!(a<1)){for(var b=this.getTotalIndices(),c=b/a|0,d=0;c%3!=0;)c++;this.releaseSubMeshes();for(var e=0;e=b);e++)o.a.CreateFromIndices(0,d,e===a-1?b-d:c,this),d+=c;this.synchronizeInstances()}},b.prototype.setVerticesData=function(a,b,c,d){if(void 0===c&&(c=!1),this._geometry)this._geometry.setVerticesData(a,b,c,d);else{d=new m.a;d.set(b,a);b=this.getScene();new n.a(n.a.RandomId(),b,d,c,this)}return this},b.prototype.removeVerticesData=function(a){this._geometry&&this._geometry.removeVerticesData(a)},b.prototype.markVerticesDataAsUpdatable=function(a,b){void 0===b&&(b=!0);var c=this.getVertexBuffer(a);c&&c.isUpdatable()!==b&&this.setVerticesData(a,this.getVerticesData(a),b)},b.prototype.setVerticesBuffer=function(a,b){return void 0===b&&(b=!0),this._geometry||(this._geometry=n.a.CreateGeometryForMesh(this)),this._geometry.setVerticesBuffer(a,null,b),this},b.prototype.updateVerticesData=function(a,b,c,d){return this._geometry?(d?(this.makeGeometryUnique(),this.updateVerticesData(a,b,c,!1)):this._geometry.updateVerticesData(a,b,c),this):this},b.prototype.updateMeshPositions=function(a,b){void 0===b&&(b=!0);var c=this.getVerticesData(l.b.PositionKind);if(!c)return this;if(a(c),this.updateVerticesData(l.b.PositionKind,c,!1,!1),b){a=this.getIndices();b=this.getVerticesData(l.b.NormalKind);if(!b)return this;m.a.ComputeNormals(c,a,b),this.updateVerticesData(l.b.NormalKind,b,!1,!1)}return this},b.prototype.makeGeometryUnique=function(){if(!this._geometry)return this;if(1===this._geometry.meshes.length)return this;var a=this._geometry,b=this._geometry.copy(n.a.RandomId());return a.releaseForMesh(this,!0),b.applyToMesh(this),this},b.prototype.setIndices=function(a,b,c){if(void 0===b&&(b=null),void 0===c&&(c=!1),this._geometry)this._geometry.setIndices(a,b,c);else{b=new m.a;b.indices=a;a=this.getScene();new n.a(n.a.RandomId(),a,b,c,this)}return this},b.prototype.updateIndices=function(a,b,c){return void 0===c&&(c=!1),this._geometry?(this._geometry.updateIndices(a,b,c),this):this},b.prototype.toLeftHanded=function(){return this._geometry?(this._geometry.toLeftHanded(),this):this},b.prototype._bind=function(a,b,c){if(!this._geometry)return this;var d,e=this.getScene().getEngine();if(this.morphTargetManager&&this.morphTargetManager.isUsingTextureForTargets&&this.morphTargetManager._bind(b),this._unIndexed)d=null;else switch(c){case p.a.PointFillMode:d=null;break;case p.a.WireFrameFillMode:d=a._getLinesIndexBuffer(this.getIndices(),e);break;default:case p.a.TriangleFillMode:d=this._geometry.getIndexBuffer()}return!this._userInstancedBuffersStorage||this.hasThinInstances?this._geometry._bind(b,d):this._geometry._bind(b,d,this._userInstancedBuffersStorage.vertexBuffers,this._userInstancedBuffersStorage.vertexArrayObjects),this},b.prototype._draw=function(a,b,c){if(!this._geometry||!this._geometry.getVertexBuffers()||!this._unIndexed&&!this._geometry.getIndexBuffer())return this;this._internalMeshDataInfo._onBeforeDrawObservable&&this._internalMeshDataInfo._onBeforeDrawObservable.notifyObservers(this);var d=this.getScene().getEngine();return this._unIndexed||b==p.a.PointFillMode?d.drawArraysType(b,a.verticesStart,a.verticesCount,this.forcedInstanceCount||c):b==p.a.WireFrameFillMode?d.drawElementsType(b,0,a._linesIndexCount,this.forcedInstanceCount||c):d.drawElementsType(b,a.indexStart,a.indexCount,this.forcedInstanceCount||c),this},b.prototype.registerBeforeRender=function(a){return this.onBeforeRenderObservable.add(a),this},b.prototype.unregisterBeforeRender=function(a){return this.onBeforeRenderObservable.removeCallback(a),this},b.prototype.registerAfterRender=function(a){return this.onAfterRenderObservable.add(a),this},b.prototype.unregisterAfterRender=function(a){return this.onAfterRenderObservable.removeCallback(a),this},b.prototype._getInstancesRenderList=function(a,b){if(void 0===b&&(b=!1),this._instanceDataStorage.isFrozen){if(b)return this._instanceDataStorage.batchCacheReplacementModeInFrozenMode.hardwareInstancedRendering[a]=!1,this._instanceDataStorage.batchCacheReplacementModeInFrozenMode.renderSelf[a]=!0,this._instanceDataStorage.batchCacheReplacementModeInFrozenMode;if(this._instanceDataStorage.previousBatch)return this._instanceDataStorage.previousBatch}var c=this.getScene(),d=c._isInIntermediateRendering(),e=d?this._internalAbstractMeshDataInfo._onlyForInstancesIntermediate:this._internalAbstractMeshDataInfo._onlyForInstances,f=this._instanceDataStorage.batchCache;if(f.mustReturn=!1,f.renderSelf[a]=b||!e&&this.isEnabled()&&this.isVisible,f.visibleInstances[a]=null,this._instanceDataStorage.visibleInstances&&!b){e=this._instanceDataStorage.visibleInstances;c=c.getRenderId();d=d?e.intermediateDefaultRenderId:e.defaultRenderId;f.visibleInstances[a]=e[c],!f.visibleInstances[a]&&d&&(f.visibleInstances[a]=e[d])}return f.hardwareInstancedRendering[a]=!b&&this._instanceDataStorage.hardwareInstancedRendering&&null!==f.visibleInstances[a]&&void 0!==f.visibleInstances[a],this._instanceDataStorage.previousBatch=f,f},b.prototype._renderWithInstances=function(a,c,d,e,f){var g=d.visibleInstances[a._id];if(!g)return this;for(var h=this._instanceDataStorage,j=h.instancesBufferSize,k=h.instancesBuffer,o=h.instancesPreviousBuffer,x=16*(g.length+1)*4;h.instancesBufferSizeb._distanceToCamera?-1:a._distanceToCamerak&&d++,0!==r&&q++,p+=r,k=r}if(i[q]++,q>f&&(f=q),0===p)e++;else{r=1/p;k=0;for(s=0;s.001&&g++}}q=this.skeleton.bones.length;p=this.getVerticesData(l.b.MatricesIndicesKind);r=this.getVerticesData(l.b.MatricesIndicesExtraKind);k=0;for(j=0;j=q||a<0)&&k++}return{skinned:!0,valid:0===e&&0===g&&0===k,report:"Number of Weights = "+c/4+" Maximum influences = "+f+" Missing Weights = "+e+" Not Sorted = "+d+" Not Normalized = "+g+" WeightCounts = ["+i+"] Number of bones = "+q+" Bad Bone Indices = "+k}},b.prototype._checkDelayState=function(){var a=this.getScene();return this._geometry?this._geometry.load(a):this.delayLoadState===s.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=s.a.DELAYLOADSTATE_LOADING,this._queueLoad(a)),this},b.prototype._queueLoad=function(a){var b=this;a._addPendingData(this);var c=-1!==this.delayLoadingFile.indexOf(".babylonbinarymeshdata");return f.b.LoadFile(this.delayLoadingFile,function(c){c instanceof ArrayBuffer?b._delayLoadingFunction(c,b):b._delayLoadingFunction(JSON.parse(c),b),b.instances.forEach(function(a){a.refreshBoundingInfo(),a._syncSubMeshes()}),b.delayLoadState=s.a.DELAYLOADSTATE_LOADED,a._removePendingData(b)},function(){},a.offlineProvider,c),this},b.prototype.isInFrustum=function(b){return this.delayLoadState!==s.a.DELAYLOADSTATE_LOADING&&!!a.prototype.isInFrustum.call(this,b)&&(this._checkDelayState(),!0)},b.prototype.setMaterialById=function(a){var b,c=this.getScene().materials;for(b=c.length-1;b>-1;b--)if(c[b].id===a)return this.material=c[b],this;c=this.getScene().multiMaterials;for(b=c.length-1;b>-1;b--)if(c[b].id===a)return this.material=c[b],this;return this},b.prototype.getAnimatables=function(){var a=new Array;return this.material&&a.push(this.material),this.skeleton&&a.push(this.skeleton),a},b.prototype.bakeTransformIntoVertices=function(a){if(!this.isVerticesDataPresent(l.b.PositionKind))return this;var b=this.subMeshes.splice(0);this._resetPointsArrayCache();var c,d=this.getVerticesData(l.b.PositionKind),e=new Array;for(c=0;c-1&&(e.morphTargetManager=c.getMorphTargetManagerById(a.morphTargetManagerId)),void 0!==a.skeletonId&&null!==a.skeletonId&&(e.skeleton=c.getLastSkeletonById(a.skeletonId),a.numBoneInfluencers&&(e.numBoneInfluencers=a.numBoneInfluencers)),a.animations){for(d=0;d4,k=j?this.getVerticesData(l.b.MatricesIndicesExtraKind):null,u=j?this.getVerticesData(l.b.MatricesWeightsExtraKind):null,a=a.getTransformMatrices(this),v=i.e.Zero(),w=new i.a,x=new i.a,y=0,z=0;z0&&(i.a.FromFloat32ArrayToRefScaled(a,Math.floor(16*f[y+h]),A,x),w.addToSelf(x));if(j)for(h=0;h<4;h++)(A=u[y+h])>0&&(i.a.FromFloat32ArrayToRefScaled(a,Math.floor(16*k[y+h]),A,x),w.addToSelf(x));i.e.TransformCoordinatesFromFloatsToRef(c._sourcePositions[z],c._sourcePositions[z+1],c._sourcePositions[z+2],w,v),v.toArray(d,z),b&&(i.e.TransformNormalFromFloatsToRef(c._sourceNormals[z],c._sourceNormals[z+1],c._sourceNormals[z+2],w,v),v.toArray(e,z)),w.reset()}return this.updateVerticesData(l.b.PositionKind,d),b&&this.updateVerticesData(l.b.NormalKind,e),this},b.MinMax=function(a){var b=null,c=null;return a.forEach(function(a){a=a.getBoundingInfo().boundingBox;b&&c?(b.minimizeInPlace(a.minimumWorld),c.maximizeInPlace(a.maximumWorld)):(b=a.minimumWorld,c=a.maximumWorld)}),b&&c?{min:b,max:c}:{min:i.e.Zero(),max:i.e.Zero()}},b.Center=function(a){a=a instanceof Array?b.MinMax(a):a;return i.e.Center(a.min,a.max)},b.MergeMeshes=function(a,c,d,e,f,g){if(void 0===c&&(c=!0),0===(a=a.filter(Boolean)).length)return null;var h;if(!d){var i=0;for(h=0;h=65536)return u.a.Warn("Cannot merge meshes because resulting mesh will have more than 65536 vertices. Please use allow32BitsIndices = true to use 32 bits indices"),null}if(g){var j;f=!1}i=new Array;var w=new Array,k=new Array;for(h=0;h0?a.name:d+a.name,(Object(n.g)(a.url,"data:")||b.UseSerializedUrlIfAny&&a.url)&&(h=a.url),f=new b(h,c,!e,a.invertY,a.samplingMode,g)}return f},a,c);return h},b.CreateFromBase64String=function(a,c,d,e,f,g,l,i,j,k){return void 0===g&&(g=b.TRILINEAR_SAMPLINGMODE),void 0===l&&(l=null),void 0===i&&(i=null),void 0===j&&(j=h.a.TEXTUREFORMAT_RGBA),new b("data:"+c,d,e,f,g,l,i,a,!1,j,void 0,void 0,k)},b.LoadFromDataString=function(a,c,d,e,f,g,m,i,j,k,l){return void 0===e&&(e=!1),void 0===g&&(g=!0),void 0===m&&(m=b.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=null),void 0===j&&(j=null),void 0===k&&(k=h.a.TEXTUREFORMAT_RGBA),"data:"!==a.substr(0,5)&&(a="data:"+a),new b(a,d,f,g,m,i,j,c,e,k,void 0,void 0,l)},b.SerializeBuffers=!0,b.ForceSerializeBuffers=!1,b.OnTextureLoadErrorObservable=new f.c,b._CubeTextureParser=function(a,b,c){throw Object(j.a)("CubeTexture")},b._CreateMirror=function(a,b,c,d){throw Object(j.a)("MirrorTexture")},b._CreateRenderTargetTexture=function(a,b,c,d,e){throw Object(j.a)("RenderTargetTexture")},b.NEAREST_SAMPLINGMODE=h.a.TEXTURE_NEAREST_SAMPLINGMODE,b.NEAREST_NEAREST_MIPLINEAR=h.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR,b.BILINEAR_SAMPLINGMODE=h.a.TEXTURE_BILINEAR_SAMPLINGMODE,b.LINEAR_LINEAR_MIPNEAREST=h.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,b.TRILINEAR_SAMPLINGMODE=h.a.TEXTURE_TRILINEAR_SAMPLINGMODE,b.LINEAR_LINEAR_MIPLINEAR=h.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR,b.NEAREST_NEAREST_MIPNEAREST=h.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST,b.NEAREST_LINEAR_MIPNEAREST=h.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST,b.NEAREST_LINEAR_MIPLINEAR=h.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR,b.NEAREST_LINEAR=h.a.TEXTURE_NEAREST_LINEAR,b.NEAREST_NEAREST=h.a.TEXTURE_NEAREST_NEAREST,b.LINEAR_NEAREST_MIPNEAREST=h.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST,b.LINEAR_NEAREST_MIPLINEAR=h.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR,b.LINEAR_LINEAR=h.a.TEXTURE_LINEAR_LINEAR,b.LINEAR_NEAREST=h.a.TEXTURE_LINEAR_NEAREST,b.EXPLICIT_MODE=h.a.TEXTURE_EXPLICIT_MODE,b.SPHERICAL_MODE=h.a.TEXTURE_SPHERICAL_MODE,b.PLANAR_MODE=h.a.TEXTURE_PLANAR_MODE,b.CUBIC_MODE=h.a.TEXTURE_CUBIC_MODE,b.PROJECTION_MODE=h.a.TEXTURE_PROJECTION_MODE,b.SKYBOX_MODE=h.a.TEXTURE_SKYBOX_MODE,b.INVCUBIC_MODE=h.a.TEXTURE_INVCUBIC_MODE,b.EQUIRECTANGULAR_MODE=h.a.TEXTURE_EQUIRECTANGULAR_MODE,b.FIXED_EQUIRECTANGULAR_MODE=h.a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE,b.FIXED_EQUIRECTANGULAR_MIRRORED_MODE=h.a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE,b.CLAMP_ADDRESSMODE=h.a.TEXTURE_CLAMP_ADDRESSMODE,b.WRAP_ADDRESSMODE=h.a.TEXTURE_WRAP_ADDRESSMODE,b.MIRROR_ADDRESSMODE=h.a.TEXTURE_MIRROR_ADDRESSMODE,b.UseSerializedUrlIfAny=!1,Object(d.c)([Object(e.d)()],b.prototype,"url",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uOffset",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vOffset",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uScale",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vScale",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"wAng",void 0),Object(d.c)([Object(e.d)()],b.prototype,"uRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"vRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"wRotationCenter",void 0),Object(d.c)([Object(e.d)()],b.prototype,"homogeneousRotationInUVTransform",void 0),Object(d.c)([Object(e.d)()],b.prototype,"isBlocking",null),b}(a.a);Object(i.b)("BABYLON.Texture",p),e.a._TextureParser=p.Parse},function(a,b,c){c.d(b,"b",function(){return r}),c.d(b,"c",function(){return s}),c.d(b,"a",function(){return t});var d=c(2),e=c(6),f=c(35),g=c(7),h=c(47),i=c(34),j=c(26),k=c(57),l=c(23),m=c(54);a=c(168);var n=c(99),o=c(138),p=c(115),q=c(139),r=function(){function a(){}return Object.defineProperty(a,"BaseUrl",{get:function(){return m.d.BaseUrl},set:function(a){m.d.BaseUrl=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"DefaultRetryStrategy",{get:function(){return m.d.DefaultRetryStrategy},set:function(a){m.d.DefaultRetryStrategy=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"CorsBehavior",{get:function(){return m.d.CorsBehavior},set:function(a){m.d.CorsBehavior=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"UseFallbackTexture",{get:function(){return l.a.UseFallbackTexture},set:function(a){l.a.UseFallbackTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"RegisteredExternalClasses",{get:function(){return o.a.RegisteredExternalClasses},set:function(a){o.a.RegisteredExternalClasses=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"fallbackTexture",{get:function(){return l.a.FallbackTexture},set:function(a){l.a.FallbackTexture=a},enumerable:!1,configurable:!0}),a.FetchToRef=function(a,b,c,d,e,f){a=4*((Math.abs(a)*c%c|0)+(Math.abs(b)*d%d|0)*c);f.r=e[a]/255,f.g=e[a+1]/255,f.b=e[a+2]/255,f.a=e[a+3]/255},a.Mix=function(a,b,c){return a*(1-c)+b*c},a.Instantiate=function(a){return o.a.Instantiate(a)},a.Slice=function(a,b,c){return q.a.Slice(a,b,c)},a.SliceToArray=function(a,b,c){return q.a.SliceToArray(a,b,c)},a.SetImmediate=function(a){n.a.SetImmediate(a)},a.IsExponentOfTwo=function(a){var b=1;do b*=2;while(b1?1:Math.round(255*o)}d=l}o=k.createImageData(b,c);o.data.set(d),k.putImageData(o,0,0);n=a._ScreenshotCanvas;if(h){l=document.createElement("canvas");l.width=b,l.height=c;d=l.getContext("2d");if(!d)return;d.translate(0,c),d.scale(1,-1),d.drawImage(a._ScreenshotCanvas,0,0),n=l}i?a.ToBlob(n,function(a){var b=new FileReader;b.onload=function(a){a=a.target.result;e&&e(a)},b.readAsArrayBuffer(a)},f,j):a.EncodeScreenshotCanvasData(e,f,g,n,j)}},a.DumpDataAsync=function(b,c,d,e,f,g,h,i){return void 0===e&&(e="image/png"),void 0===g&&(g=!1),void 0===h&&(h=!1),new Promise(function(j){a.DumpData(b,c,d,function(a){return j(a)},e,f,g,h,i)})},a.ToBlob=function(a,b,c,d){void 0===c&&(c="image/png"),a.toBlob||(a.toBlob=function(a,b,c){var d=this;setTimeout(function(){for(var e=atob(d.toDataURL(b,c).split(",")[1]),f=e.length,g=new Uint8Array(f),h=0;h=b)break;if(d(g),f&&f()){a.breakLoop();break}}a.executeNext()},g)},e)},a}();l.a.FallbackTexture="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAAABgAAAAAQAAAGAAAAABcGFpbnQubmV0IDQuMC41AP/bAEMABAIDAwMCBAMDAwQEBAQFCQYFBQUFCwgIBgkNCw0NDQsMDA4QFBEODxMPDAwSGBITFRYXFxcOERkbGRYaFBYXFv/bAEMBBAQEBQUFCgYGChYPDA8WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFv/AABEIAQABAAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APH6KKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76P//Z",a.a.Apply()},function(a,b,c){c.d(b,"a",function(){return o});var d=c(2),e=c(6),f=c(35),g=c(23),h=c(26),i=c(27),j=c(1),k=c(173),l=c(60),m=c(95),n=c(7),o=(c(140),c(124),c(141),function(a){function b(c,d,g,h){void 0===h&&(h=!1);var i=a.call(this,c,d,g,h)||this;if(i.enableOfflineSupport=!1,i.disableManifestCheck=!1,i.scenes=new Array,i._virtualScenes=new Array,i.onNewSceneAddedObservable=new e.c,i.postProcesses=new Array,i.isPointerLock=!1,i.onResizeObservable=new e.c,i.onCanvasBlurObservable=new e.c,i.onCanvasFocusObservable=new e.c,i.onCanvasPointerOutObservable=new e.c,i.onBeginFrameObservable=new e.c,i.customAnimationFrameRequester=null,i.onEndFrameObservable=new e.c,i.onBeforeShaderCompilationObservable=new e.c,i.onAfterShaderCompilationObservable=new e.c,i._deterministicLockstep=!1,i._lockstepMaxSteps=4,i._timeStep=1/60,i._fps=60,i._deltaTime=0,i._drawCalls=new l.a,i.canvasTabIndex=1,i.disablePerformanceMonitorInBackground=!1,i._performanceMonitor=new k.a,b.Instances.push(i),!c)return i;if(g=i._creationOptions,c.getContext){var j=c;if(i._sharedInit(j,!!g.doNotHandleTouchAction,g.audioEngine),Object(f.e)()){var n=document;i._onFullscreenChange=function(){void 0!==n.fullscreen?i.isFullscreen=n.fullscreen:void 0!==n.mozFullScreen?i.isFullscreen=n.mozFullScreen:void 0!==n.webkitIsFullScreen?i.isFullscreen=n.webkitIsFullScreen:void 0!==n.msIsFullScreen&&(i.isFullscreen=n.msIsFullScreen),i.isFullscreen&&i._pointerLockRequested&&j&&b._RequestPointerlock(j)},document.addEventListener("fullscreenchange",i._onFullscreenChange,!1),document.addEventListener("mozfullscreenchange",i._onFullscreenChange,!1),document.addEventListener("webkitfullscreenchange",i._onFullscreenChange,!1),document.addEventListener("msfullscreenchange",i._onFullscreenChange,!1),i._onPointerLockChange=function(){i.isPointerLock=n.mozPointerLockElement===j||n.webkitPointerLockElement===j||n.msPointerLockElement===j||n.pointerLockElement===j},document.addEventListener("pointerlockchange",i._onPointerLockChange,!1),document.addEventListener("mspointerlockchange",i._onPointerLockChange,!1),document.addEventListener("mozpointerlockchange",i._onPointerLockChange,!1),document.addEventListener("webkitpointerlockchange",i._onPointerLockChange,!1),!b.audioEngine&&g.audioEngine&&b.AudioEngineFactory&&(b.audioEngine=b.AudioEngineFactory(i.getRenderingCanvas(),i.getAudioContext(),i.getAudioDestination()))}i._connectVREvents(),i.enableOfflineSupport=void 0!==b.OfflineProviderFactory,i._deterministicLockstep=!!g.deterministicLockstep,i._lockstepMaxSteps=g.lockstepMaxSteps||0,i._timeStep=g.timeStep||1/60}return i._prepareVRComponent(),g.autoEnableWebVR&&i.initWebVR(),i}return Object(d.d)(b,a),Object.defineProperty(b,"NpmPackage",{get:function(){return i.a.NpmPackage},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Version",{get:function(){return i.a.Version},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Instances",{get:function(){return g.a.Instances},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LastCreatedEngine",{get:function(){return g.a.LastCreatedEngine},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LastCreatedScene",{get:function(){return g.a.LastCreatedScene},enumerable:!1,configurable:!0}),b.prototype.createImageBitmap=function(a,b){return createImageBitmap(a,b)},b.prototype.resizeImageBitmap=function(a,b,c){var d=this.createCanvas(b,c).getContext("2d");if(!d)throw new Error("Unable to get 2d context for resizeImageBitmap");return d.drawImage(a,0,0),d.getImageData(0,0,b,c).data},b.MarkAllMaterialsAsDirty=function(a,c){for(var d=0;d0?this.customAnimationFrameRequester?(this.customAnimationFrameRequester.requestID=this._queueNewFrame(this.customAnimationFrameRequester.renderFunction||this._boundRenderFunction,this.customAnimationFrameRequester),this._frameHandler=this.customAnimationFrameRequester.requestID):this.isVRPresenting()?this._requestVRFrame():this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow()):this._renderingQueueLaunched=!1},b.prototype._renderViews=function(){return!1},b.prototype.switchFullscreen=function(a){this.isFullscreen?this.exitFullscreen():this.enterFullscreen(a)},b.prototype.enterFullscreen=function(a){this.isFullscreen||(this._pointerLockRequested=a,this._renderingCanvas&&b._RequestFullscreen(this._renderingCanvas))},b.prototype.exitFullscreen=function(){this.isFullscreen&&b._ExitFullscreen()},b.prototype.enterPointerlock=function(){this._renderingCanvas&&b._RequestPointerlock(this._renderingCanvas)},b.prototype.exitPointerlock=function(){b._ExitPointerlock()},b.prototype.beginFrame=function(){this._measureFps(),this.onBeginFrameObservable.notifyObservers(this),a.prototype.beginFrame.call(this)},b.prototype.endFrame=function(){a.prototype.endFrame.call(this),this._submitVRFrame(),this.onEndFrameObservable.notifyObservers(this)},b.prototype.resize=function(b){void 0===b&&(b=!1),this.isVRPresenting()||a.prototype.resize.call(this,b)},b.prototype.setSize=function(b,c,d){if(void 0===d&&(d=!1),!this._renderingCanvas)return!1;if(!a.prototype.setSize.call(this,b,c,d))return!1;if(this.scenes){for(b=0;b1&&e){var g=this.createTransformFeedback();this.bindTransformFeedback(g),this.setTranformFeedbackVaryings(f,e),a.transformFeedback=g}return d.linkProgram(f),this.webGLVersion>1&&e&&this.bindTransformFeedback(null),a.context=d,a.vertexShader=b,a.fragmentShader=c,a.isParallelCompiled||this._finalizePipelineContext(a),f},b.prototype._releaseTexture=function(b){a.prototype._releaseTexture.call(this,b)},b.prototype._releaseRenderTargetWrapper=function(b){a.prototype._releaseRenderTargetWrapper.call(this,b),this.scenes.forEach(function(a){a.postProcesses.forEach(function(a){a._outputTexture===b&&(a._outputTexture=null)}),a.cameras.forEach(function(a){a._postProcesses.forEach(function(a){a&&a._outputTexture===b&&(a._outputTexture=null)})})})},b.prototype._rescaleTexture=function(a,c,d,e,f){var g=this;this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_MAG_FILTER,this._gl.LINEAR),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_MIN_FILTER,this._gl.LINEAR),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_WRAP_S,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(this._gl.TEXTURE_2D,this._gl.TEXTURE_WRAP_T,this._gl.CLAMP_TO_EDGE);var h=this.createRenderTargetTexture({width:c.width,height:c.height},{generateMipMaps:!1,type:j.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:j.a.TEXTURE_BILINEAR_SAMPLINGMODE,generateDepthBuffer:!1,generateStencilBuffer:!1});!this._rescalePostProcess&&b._RescalePostProcessFactory&&(this._rescalePostProcess=b._RescalePostProcessFactory(this)),this._rescalePostProcess.getEffect().executeWhenCompiled(function(){g._rescalePostProcess.onApply=function(b){b._bindTexture("textureSampler",a)};var b=d;b||(b=g.scenes[g.scenes.length-1]),b.postProcessManager.directRender([g._rescalePostProcess],h,!0),g._bindTextureDirectly(g._gl.TEXTURE_2D,c,!0),g._gl.copyTexImage2D(g._gl.TEXTURE_2D,0,e,0,0,c.width,c.height,0),g.unBindFramebuffer(h),h.dispose(),f&&f()})},b.prototype.getFps=function(){return this._fps},b.prototype.getDeltaTime=function(){return this._deltaTime},b.prototype._measureFps=function(){this._performanceMonitor.sampleFrame(),this._fps=this._performanceMonitor.averageFPS,this._deltaTime=this._performanceMonitor.instantaneousFrameTime||0},b.prototype._uploadImageToTexture=function(a,b,c,d){void 0===c&&(c=0),void 0===d&&(d=0);var e=this._gl,f=this._getWebGLTextureType(a.type),g=this._getInternalFormat(a.format),h=this._getRGBABufferInternalSizedFormat(a.type,g),i=a.isCube?e.TEXTURE_CUBE_MAP:e.TEXTURE_2D;this._bindTextureDirectly(i,a,!0),this._unpackFlipY(a.invertY);var j=e.TEXTURE_2D;a.isCube&&(j=e.TEXTURE_CUBE_MAP_POSITIVE_X+c),e.texImage2D(j,d,h,g,f,b),this._bindTextureDirectly(i,null,!0)},b.prototype.updateTextureComparisonFunction=function(a,b){if(1!==this.webGLVersion){var c=this._gl;a.isCube?(this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,a,!0),0===b?(c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_FUNC,j.a.LEQUAL),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_FUNC,b),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE)),this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,null)):(this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0),0===b?(c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_FUNC,j.a.LEQUAL),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_FUNC,b),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE)),this._bindTextureDirectly(this._gl.TEXTURE_2D,null)),a._comparisonFunction=b}else n.a.Error("WebGL 1 does not support texture comparison.")},b.prototype.createInstancesBuffer=function(a){var b=this._gl.createBuffer();if(!b)throw new Error("Unable to create instance buffer");b=new m.a(b);return b.capacity=a,this.bindArrayBuffer(b),this._gl.bufferData(this._gl.ARRAY_BUFFER,a,this._gl.DYNAMIC_DRAW),b.references=1,b},b.prototype.deleteInstancesBuffer=function(a){this._gl.deleteBuffer(a)},b.prototype._clientWaitAsync=function(a,b,c){void 0===b&&(b=0),void 0===c&&(c=10);var d=this._gl;return new Promise(function(e,f){var g=function(){var h=d.clientWaitSync(a,b,0);h!=d.WAIT_FAILED?h!=d.TIMEOUT_EXPIRED?e():setTimeout(g,c):f()};g()})},b.prototype._readPixelsAsync=function(a,b,c,d,e,f,g){if(this._webGLVersion<2)throw new Error("_readPixelsAsync only work on WebGL2+");var h=this._gl,i=h.createBuffer();h.bindBuffer(h.PIXEL_PACK_BUFFER,i),h.bufferData(h.PIXEL_PACK_BUFFER,g.byteLength,h.STREAM_READ),h.readPixels(a,b,c,d,e,f,0),h.bindBuffer(h.PIXEL_PACK_BUFFER,null);var j=h.fenceSync(h.SYNC_GPU_COMMANDS_COMPLETE,0);return j?(h.flush(),this._clientWaitAsync(j,0,10).then(function(){return h.deleteSync(j),h.bindBuffer(h.PIXEL_PACK_BUFFER,i),h.getBufferSubData(h.PIXEL_PACK_BUFFER,0,g),h.bindBuffer(h.PIXEL_PACK_BUFFER,null),h.deleteBuffer(i),g})):null},b.prototype.dispose=function(){for(this.hideLoadingUI(),this.onNewSceneAddedObservable.clear();this.postProcesses.length;)this.postProcesses[0].dispose();for(this._rescalePostProcess&&this._rescalePostProcess.dispose();this.scenes.length;)this.scenes[0].dispose();for(;this._virtualScenes.length;)this._virtualScenes[0].dispose();1===b.Instances.length&&b.audioEngine&&(b.audioEngine.dispose(),b.audioEngine=null),this.disableVR(),this.deviceInputSystem&&this.deviceInputSystem.dispose(),Object(f.e)()&&(window.removeEventListener("blur",this._onBlur),window.removeEventListener("focus",this._onFocus),this._renderingCanvas&&(this._renderingCanvas.removeEventListener("focus",this._onCanvasFocus),this._renderingCanvas.removeEventListener("blur",this._onCanvasBlur),this._renderingCanvas.removeEventListener("pointerout",this._onCanvasPointerOut)),Object(f.c)()&&(document.removeEventListener("fullscreenchange",this._onFullscreenChange),document.removeEventListener("mozfullscreenchange",this._onFullscreenChange),document.removeEventListener("webkitfullscreenchange",this._onFullscreenChange),document.removeEventListener("msfullscreenchange",this._onFullscreenChange),document.removeEventListener("pointerlockchange",this._onPointerLockChange),document.removeEventListener("mspointerlockchange",this._onPointerLockChange),document.removeEventListener("mozpointerlockchange",this._onPointerLockChange),document.removeEventListener("webkitpointerlockchange",this._onPointerLockChange))),a.prototype.dispose.call(this);var c=b.Instances.indexOf(this);c>=0&&b.Instances.splice(c,1),this.onResizeObservable.clear(),this.onCanvasBlurObservable.clear(),this.onCanvasFocusObservable.clear(),this.onCanvasPointerOutObservable.clear(),this.onBeginFrameObservable.clear(),this.onEndFrameObservable.clear()},b.prototype._disableTouchAction=function(){this._renderingCanvas&&this._renderingCanvas.setAttribute&&(this._renderingCanvas.setAttribute("touch-action","none"),this._renderingCanvas.style.touchAction="none",this._renderingCanvas.style.msTouchAction="none")},b.prototype.displayLoadingUI=function(){if(Object(f.e)()){var a=this.loadingScreen;a&&a.displayLoadingUI()}},b.prototype.hideLoadingUI=function(){if(Object(f.e)()){var a=this._loadingScreen;a&&a.hideLoadingUI()}},Object.defineProperty(b.prototype,"loadingScreen",{get:function(){return!this._loadingScreen&&this._renderingCanvas&&(this._loadingScreen=b.DefaultLoadingScreenFactory(this._renderingCanvas)),this._loadingScreen},set:function(a){this._loadingScreen=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loadingUIText",{set:function(a){this.loadingScreen.loadingUIText=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loadingUIBackgroundColor",{set:function(a){this.loadingScreen.loadingUIBackgroundColor=a},enumerable:!1,configurable:!0}),b.prototype.createVideoElement=function(a){return document.createElement("video")},b._RequestPointerlock=function(a){a.requestPointerLock=a.requestPointerLock||a.msRequestPointerLock||a.mozRequestPointerLock||a.webkitRequestPointerLock,a.requestPointerLock&&a.requestPointerLock()},b._ExitPointerlock=function(){var a=document;document.exitPointerLock=document.exitPointerLock||a.msExitPointerLock||a.mozExitPointerLock||a.webkitExitPointerLock,document.exitPointerLock&&document.exitPointerLock()},b._RequestFullscreen=function(a){var b=a.requestFullscreen||a.msRequestFullscreen||a.webkitRequestFullscreen||a.mozRequestFullScreen;b&&b.call(a)},b._ExitFullscreen=function(){var a=document;document.exitFullscreen?document.exitFullscreen():a.mozCancelFullScreen?a.mozCancelFullScreen():a.webkitCancelFullScreen?a.webkitCancelFullScreen():a.msCancelFullScreen&&a.msCancelFullScreen()},b.ALPHA_DISABLE=j.a.ALPHA_DISABLE,b.ALPHA_ADD=j.a.ALPHA_ADD,b.ALPHA_COMBINE=j.a.ALPHA_COMBINE,b.ALPHA_SUBTRACT=j.a.ALPHA_SUBTRACT,b.ALPHA_MULTIPLY=j.a.ALPHA_MULTIPLY,b.ALPHA_MAXIMIZED=j.a.ALPHA_MAXIMIZED,b.ALPHA_ONEONE=j.a.ALPHA_ONEONE,b.ALPHA_PREMULTIPLIED=j.a.ALPHA_PREMULTIPLIED,b.ALPHA_PREMULTIPLIED_PORTERDUFF=j.a.ALPHA_PREMULTIPLIED_PORTERDUFF,b.ALPHA_INTERPOLATE=j.a.ALPHA_INTERPOLATE,b.ALPHA_SCREENMODE=j.a.ALPHA_SCREENMODE,b.DELAYLOADSTATE_NONE=j.a.DELAYLOADSTATE_NONE,b.DELAYLOADSTATE_LOADED=j.a.DELAYLOADSTATE_LOADED,b.DELAYLOADSTATE_LOADING=j.a.DELAYLOADSTATE_LOADING,b.DELAYLOADSTATE_NOTLOADED=j.a.DELAYLOADSTATE_NOTLOADED,b.NEVER=j.a.NEVER,b.ALWAYS=j.a.ALWAYS,b.LESS=j.a.LESS,b.EQUAL=j.a.EQUAL,b.LEQUAL=j.a.LEQUAL,b.GREATER=j.a.GREATER,b.GEQUAL=j.a.GEQUAL,b.NOTEQUAL=j.a.NOTEQUAL,b.KEEP=j.a.KEEP,b.REPLACE=j.a.REPLACE,b.INCR=j.a.INCR,b.DECR=j.a.DECR,b.INVERT=j.a.INVERT,b.INCR_WRAP=j.a.INCR_WRAP,b.DECR_WRAP=j.a.DECR_WRAP,b.TEXTURE_CLAMP_ADDRESSMODE=j.a.TEXTURE_CLAMP_ADDRESSMODE,b.TEXTURE_WRAP_ADDRESSMODE=j.a.TEXTURE_WRAP_ADDRESSMODE,b.TEXTURE_MIRROR_ADDRESSMODE=j.a.TEXTURE_MIRROR_ADDRESSMODE,b.TEXTUREFORMAT_ALPHA=j.a.TEXTUREFORMAT_ALPHA,b.TEXTUREFORMAT_LUMINANCE=j.a.TEXTUREFORMAT_LUMINANCE,b.TEXTUREFORMAT_LUMINANCE_ALPHA=j.a.TEXTUREFORMAT_LUMINANCE_ALPHA,b.TEXTUREFORMAT_RGB=j.a.TEXTUREFORMAT_RGB,b.TEXTUREFORMAT_RGBA=j.a.TEXTUREFORMAT_RGBA,b.TEXTUREFORMAT_RED=j.a.TEXTUREFORMAT_RED,b.TEXTUREFORMAT_R=j.a.TEXTUREFORMAT_R,b.TEXTUREFORMAT_RG=j.a.TEXTUREFORMAT_RG,b.TEXTUREFORMAT_RED_INTEGER=j.a.TEXTUREFORMAT_RED_INTEGER,b.TEXTUREFORMAT_R_INTEGER=j.a.TEXTUREFORMAT_R_INTEGER,b.TEXTUREFORMAT_RG_INTEGER=j.a.TEXTUREFORMAT_RG_INTEGER,b.TEXTUREFORMAT_RGB_INTEGER=j.a.TEXTUREFORMAT_RGB_INTEGER,b.TEXTUREFORMAT_RGBA_INTEGER=j.a.TEXTUREFORMAT_RGBA_INTEGER,b.TEXTURETYPE_UNSIGNED_BYTE=j.a.TEXTURETYPE_UNSIGNED_BYTE,b.TEXTURETYPE_UNSIGNED_INT=j.a.TEXTURETYPE_UNSIGNED_INT,b.TEXTURETYPE_FLOAT=j.a.TEXTURETYPE_FLOAT,b.TEXTURETYPE_HALF_FLOAT=j.a.TEXTURETYPE_HALF_FLOAT,b.TEXTURETYPE_BYTE=j.a.TEXTURETYPE_BYTE,b.TEXTURETYPE_SHORT=j.a.TEXTURETYPE_SHORT,b.TEXTURETYPE_UNSIGNED_SHORT=j.a.TEXTURETYPE_UNSIGNED_SHORT,b.TEXTURETYPE_INT=j.a.TEXTURETYPE_INT,b.TEXTURETYPE_UNSIGNED_INTEGER=j.a.TEXTURETYPE_UNSIGNED_INTEGER,b.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4=j.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4,b.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1=j.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1,b.TEXTURETYPE_UNSIGNED_SHORT_5_6_5=j.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5,b.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV=j.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV,b.TEXTURETYPE_UNSIGNED_INT_24_8=j.a.TEXTURETYPE_UNSIGNED_INT_24_8,b.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV=j.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV,b.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV=j.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV,b.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV=j.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV,b.TEXTURE_NEAREST_SAMPLINGMODE=j.a.TEXTURE_NEAREST_SAMPLINGMODE,b.TEXTURE_BILINEAR_SAMPLINGMODE=j.a.TEXTURE_BILINEAR_SAMPLINGMODE,b.TEXTURE_TRILINEAR_SAMPLINGMODE=j.a.TEXTURE_TRILINEAR_SAMPLINGMODE,b.TEXTURE_NEAREST_NEAREST_MIPLINEAR=j.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR,b.TEXTURE_LINEAR_LINEAR_MIPNEAREST=j.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,b.TEXTURE_LINEAR_LINEAR_MIPLINEAR=j.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR,b.TEXTURE_NEAREST_NEAREST_MIPNEAREST=j.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST,b.TEXTURE_NEAREST_LINEAR_MIPNEAREST=j.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST,b.TEXTURE_NEAREST_LINEAR_MIPLINEAR=j.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR,b.TEXTURE_NEAREST_LINEAR=j.a.TEXTURE_NEAREST_LINEAR,b.TEXTURE_NEAREST_NEAREST=j.a.TEXTURE_NEAREST_NEAREST,b.TEXTURE_LINEAR_NEAREST_MIPNEAREST=j.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST,b.TEXTURE_LINEAR_NEAREST_MIPLINEAR=j.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR,b.TEXTURE_LINEAR_LINEAR=j.a.TEXTURE_LINEAR_LINEAR,b.TEXTURE_LINEAR_NEAREST=j.a.TEXTURE_LINEAR_NEAREST,b.TEXTURE_EXPLICIT_MODE=j.a.TEXTURE_EXPLICIT_MODE,b.TEXTURE_SPHERICAL_MODE=j.a.TEXTURE_SPHERICAL_MODE,b.TEXTURE_PLANAR_MODE=j.a.TEXTURE_PLANAR_MODE,b.TEXTURE_CUBIC_MODE=j.a.TEXTURE_CUBIC_MODE,b.TEXTURE_PROJECTION_MODE=j.a.TEXTURE_PROJECTION_MODE,b.TEXTURE_SKYBOX_MODE=j.a.TEXTURE_SKYBOX_MODE,b.TEXTURE_INVCUBIC_MODE=j.a.TEXTURE_INVCUBIC_MODE,b.TEXTURE_EQUIRECTANGULAR_MODE=j.a.TEXTURE_EQUIRECTANGULAR_MODE,b.TEXTURE_FIXED_EQUIRECTANGULAR_MODE=j.a.TEXTURE_FIXED_EQUIRECTANGULAR_MODE,b.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE=j.a.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE,b.SCALEMODE_FLOOR=j.a.SCALEMODE_FLOOR,b.SCALEMODE_NEAREST=j.a.SCALEMODE_NEAREST,b.SCALEMODE_CEILING=j.a.SCALEMODE_CEILING,b._RescalePostProcessFactory=null,b}(i.a))},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.WithinEpsilon=function(a,b,c){return void 0===c&&(c=1401298e-51),Math.abs(a-b)<=c},a.ToHex=function(a){var b=a.toString(16);return a<=15?("0"+b).toUpperCase():b.toUpperCase()},a.Sign=function(a){return 0===(a=+a)||isNaN(a)?a:a>0?1:-1},a.Clamp=function(a,b,c){return void 0===b&&(b=0),void 0===c&&(c=1),Math.min(c,Math.max(b,a))},a.Log2=function(a){return Math.log(a)*Math.LOG2E},a.ILog2=function(a){if(Math.log2)return Math.floor(Math.log2(a));if(a<0)return NaN;if(0===a)return-1/0;var b=0;if(a<1){for(;a<1;)b++,a*=2;b=-b}else if(a>1)for(;a>1;)b++,a=Math.floor(a/2);return b},a.Repeat=function(a,b){return a-Math.floor(a/b)*b},a.Normalize=function(a,b,c){return(a-b)/(c-b)},a.Denormalize=function(a,b,c){return a*(c-b)+b},a.DeltaAngle=function(b,c){c=a.Repeat(c-b,360);return c>180&&(c-=360),c},a.PingPong=function(b,c){b=a.Repeat(b,2*c);return c-Math.abs(b-c)},a.SmoothStep=function(b,c,d){d=a.Clamp(d);return c*(d=-2*d*d*d+3*d*d)+b*(1-d)},a.MoveTowards=function(b,c,d){return Math.abs(c-b)<=d?c:b+a.Sign(c-b)*d},a.MoveTowardsAngle=function(b,c,d){var e=a.DeltaAngle(b,c),f;return-d180&&(c-=360),b+c*a.Clamp(d)},a.InverseLerp=function(b,c,d){return b!=c?a.Clamp((d-b)/(c-b)):0},a.Hermite=function(a,b,c,d,e){var f=e*e,g=e*f;return a*(2*g-3*f+1)+c*(-2*g+3*f)+b*(g-2*f+e)+d*(g-f)},a.Hermite1stDerivative=function(a,b,c,d,e){var f=e*e;return 6*(f-e)*a+(3*f-4*e+1)*b+6*(-f+e)*c+(3*f-2*e)*d},a.RandomRange=function(a,b){return a===b?a:Math.random()*(b-a)+a},a.RangeToPercent=function(a,b,c){return(a-b)/(c-b)},a.PercentToRange=function(a,b,c){return(c-b)*a+b},a.NormalizeRadians=function(b){return b-=a.TwoPi*Math.floor((b+Math.PI)/a.TwoPi)},a.HCF=function(b,c){b=b%c;return 0===b?c:a.HCF(c,b)},a.TwoPi=2*Math.PI,a}()},function(a,b,c){c.d(b,"a",function(){return l});var d=c(7),e=c(21),f=c(23),g=c(4),h=c(52),i=c(1),j=c(8),k=c(134),l=function(){function a(){}return a.BindSceneUniformBuffer=function(a,b){b.bindToEffect(a,"Scene")},a.PrepareDefinesForMergedUV=function(a,b,c){b._needUVs=!0,b[c]=!0,a.getTextureMatrix().isIdentityAs3x2()?(b[c+"DIRECTUV"]=a.coordinatesIndex+1,b["MAINUV"+(a.coordinatesIndex+1)]=!0):b[c+"DIRECTUV"]=0},a.BindTextureMatrix=function(a,b,c){a=a.getTextureMatrix();b.updateMatrix(c+"Matrix",a)},a.GetFogState=function(a,b){return b.fogEnabled&&a.applyFog&&b.fogMode!==e.a.FOGMODE_NONE},a.PrepareDefinesForMisc=function(a,b,c,d,e,f,g){g._areMiscDirty&&(g.LOGARITHMICDEPTH=c,g.POINTSIZE=d,g.FOG=e&&this.GetFogState(a,b),g.NONUNIFORMSCALING=a.nonUniformScaling,g.ALPHATEST=f)},a.PrepareDefinesForFrameBoundValues=function(a,b,c,d,e,f){void 0===e&&(e=null),void 0===f&&(f=!1);var g,h,i,j,k,l=!1;g=null==e?void 0!==a.clipPlane&&null!==a.clipPlane:e,h=null==e?void 0!==a.clipPlane2&&null!==a.clipPlane2:e,i=null==e?void 0!==a.clipPlane3&&null!==a.clipPlane3:e,j=null==e?void 0!==a.clipPlane4&&null!==a.clipPlane4:e,k=null==e?void 0!==a.clipPlane5&&null!==a.clipPlane5:e,a=null==e?void 0!==a.clipPlane6&&null!==a.clipPlane6:e,c.CLIPPLANE!==g&&(c.CLIPPLANE=g,l=!0),c.CLIPPLANE2!==h&&(c.CLIPPLANE2=h,l=!0),c.CLIPPLANE3!==i&&(c.CLIPPLANE3=i,l=!0),c.CLIPPLANE4!==j&&(c.CLIPPLANE4=j,l=!0),c.CLIPPLANE5!==k&&(c.CLIPPLANE5=k,l=!0),c.CLIPPLANE6!==a&&(c.CLIPPLANE6=a,l=!0),c.DEPTHPREPASS!==!b.getColorWrite()&&(c.DEPTHPREPASS=!c.DEPTHPREPASS,l=!0),c.INSTANCES!==d&&(c.INSTANCES=d,l=!0),c.THIN_INSTANCES!==f&&(c.THIN_INSTANCES=f,l=!0),l&&c.markAsUnprocessed()},a.PrepareDefinesForBones=function(a,b){if(a.useBones&&a.computeBonesUsingShaders&&a.skeleton){b.NUM_BONE_INFLUENCERS=a.numBoneInfluencers;var c=void 0!==b.BONETEXTURE;if(a.skeleton.isUsingTextureForMatrices&&c)b.BONETEXTURE=!0;else{b.BonesPerMesh=a.skeleton.bones.length+1,b.BONETEXTURE=!c&&void 0;c=a.getScene().prePassRenderer;if(c&&c.enabled){c=-1===c.excludedSkinnedMesh.indexOf(a);b.BONES_VELOCITY_ENABLED=c}}}else b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0},a.PrepareDefinesForMorphTargets=function(a,b){a=a.morphTargetManager;a?(b.MORPHTARGETS_UV=a.supportsUVs&&b.UV1,b.MORPHTARGETS_TANGENT=a.supportsTangents&&b.TANGENT,b.MORPHTARGETS_NORMAL=a.supportsNormals&&b.NORMAL,b.MORPHTARGETS=a.numInfluencers>0,b.NUM_MORPH_INFLUENCERS=a.numInfluencers,b.MORPHTARGETS_TEXTURE=a.isUsingTextureForTargets):(b.MORPHTARGETS_UV=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS=!1,b.NUM_MORPH_INFLUENCERS=0)},a.PrepareDefinesForAttributes=function(a,b,c,d,e,f){if(void 0===e&&(e=!1),void 0===f&&(f=!0),!b._areAttributesDirty&&b._needNormals===b._normals&&b._needUVs===b._uvs)return!1;b._normals=b._needNormals,b._uvs=b._needUVs,b.NORMAL=b._needNormals&&a.isVerticesDataPresent(g.b.NormalKind),b._needNormals&&a.isVerticesDataPresent(g.b.TangentKind)&&(b.TANGENT=!0);for(var h=1;h<=i.a.MAX_SUPPORTED_UV_SETS;++h)b["UV"+h]=!!b._needUVs&&a.isVerticesDataPresent("uv"+(1===h?"":h));if(c){h=a.useVertexColors&&a.isVerticesDataPresent(g.b.ColorKind);b.VERTEXCOLOR=h,b.VERTEXALPHA=a.hasVertexAlpha&&h&&f}return d&&this.PrepareDefinesForBones(a,b),e&&this.PrepareDefinesForMorphTargets(a,b),!0},a.PrepareDefinesForMultiview=function(a,b){if(a.activeCamera){var c=b.MULTIVIEW;b.MULTIVIEW=null!==a.activeCamera.outputRenderTarget&&a.activeCamera.outputRenderTarget.getViewCount()>1,b.MULTIVIEW!=c&&b.markAsUnprocessed()}},a.PrepareDefinesForOIT=function(a,b,c){var d=b.ORDER_INDEPENDENT_TRANSPARENCY;b.ORDER_INDEPENDENT_TRANSPARENCY=a.useOrderIndependentTransparency&&c,d!==b.ORDER_INDEPENDENT_TRANSPARENCY&&b.markAsUnprocessed()},a.PrepareDefinesForPrePass=function(a,b,c){var d=b.PREPASS;if(b._arePrePassDirty){var e=[{type:i.a.PREPASS_POSITION_TEXTURE_TYPE,define:"PREPASS_POSITION",index:"PREPASS_POSITION_INDEX"},{type:i.a.PREPASS_VELOCITY_TEXTURE_TYPE,define:"PREPASS_VELOCITY",index:"PREPASS_VELOCITY_INDEX"},{type:i.a.PREPASS_REFLECTIVITY_TEXTURE_TYPE,define:"PREPASS_REFLECTIVITY",index:"PREPASS_REFLECTIVITY_INDEX"},{type:i.a.PREPASS_IRRADIANCE_TEXTURE_TYPE,define:"PREPASS_IRRADIANCE",index:"PREPASS_IRRADIANCE_INDEX"},{type:i.a.PREPASS_ALBEDO_SQRT_TEXTURE_TYPE,define:"PREPASS_ALBEDO_SQRT",index:"PREPASS_ALBEDO_SQRT_INDEX"},{type:i.a.PREPASS_DEPTH_TEXTURE_TYPE,define:"PREPASS_DEPTH",index:"PREPASS_DEPTH_INDEX"},{type:i.a.PREPASS_NORMAL_TEXTURE_TYPE,define:"PREPASS_NORMAL",index:"PREPASS_NORMAL_INDEX"}];if(a.prePassRenderer&&a.prePassRenderer.enabled&&c){b.PREPASS=!0,b.SCENE_MRT_COUNT=a.prePassRenderer.mrtCount;for(c=0;c0&&(g.shadowEnabled=!0,f.prepareDefines(e,d))}}c.lightmapMode!=h.a.LIGHTMAP_DEFAULT?(g.lightmapMode=!0,e["LIGHTMAPEXCLUDED"+d]=!0,e["LIGHTMAPNOSPECULAR"+d]=c.lightmapMode==h.a.LIGHTMAP_SHADOWSONLY):(e["LIGHTMAPEXCLUDED"+d]=!1,e["LIGHTMAPNOSPECULAR"+d]=!1)},a.PrepareDefinesForLights=function(a,b,c,d,e,f){if(void 0===e&&(e=4),void 0===f&&(f=!1),!c._areLightsDirty)return c._needNormals;var g=0,h={needNormals:!1,needRebuild:!1,lightmapMode:!1,shadowEnabled:!1,specularEnabled:!1};if(a.lightsEnabled&&!f)for(var f=0,i=b.lightSources;f0&&(e=d+f,b.addFallback(e,"LIGHT"+f)),a.SHADOWS||(a["SHADOW"+f]&&b.addFallback(d,"SHADOW"+f),a["SHADOWPCF"+f]&&b.addFallback(d,"SHADOWPCF"+f),a["SHADOWPCSS"+f]&&b.addFallback(d,"SHADOWPCSS"+f),a["SHADOWPOISSON"+f]&&b.addFallback(d,"SHADOWPOISSON"+f),a["SHADOWESM"+f]&&b.addFallback(d,"SHADOWESM"+f),a["SHADOWCLOSEESM"+f]&&b.addFallback(d,"SHADOWCLOSEESM"+f));return e++},a.PrepareAttributesForMorphTargetsInfluencers=function(a,b,c){this._TmpMorphInfluencers.NUM_MORPH_INFLUENCERS=c,this.PrepareAttributesForMorphTargets(a,b,this._TmpMorphInfluencers)},a.PrepareAttributesForMorphTargets=function(a,b,c){var e=c.NUM_MORPH_INFLUENCERS;if(e>0&&f.a.LastCreatedEngine){var h=f.a.LastCreatedEngine.getCaps().maxVertexAttribs,i=b.morphTargetManager;if(null==i?void 0:i.isUsingTextureForTargets)return;for(var j=i&&i.supportsNormals&&c.NORMAL,k=i&&i.supportsTangents&&c.TANGENT,i=i&&i.supportsUVs&&c.UV1,c=0;ch&&d.a.Error("Cannot add more vertex attributes for mesh "+b.name)}},a.PrepareAttributesForBones=function(a,b,c,d){c.NUM_BONE_INFLUENCERS>0&&(d.addCPUSkinningFallback(0,b),a.push(g.b.MatricesIndicesKind),a.push(g.b.MatricesWeightsKind),c.NUM_BONE_INFLUENCERS>4&&(a.push(g.b.MatricesIndicesExtraKind),a.push(g.b.MatricesWeightsExtraKind)))},a.PrepareAttributesForInstances=function(a,b){(b.INSTANCES||b.THIN_INSTANCES)&&this.PushAttributesForInstances(a,!!b.PREPASS_VELOCITY)},a.PushAttributesForInstances=function(a,b){void 0===b&&(b=!1),a.push("world0"),a.push("world1"),a.push("world2"),a.push("world3"),b&&(a.push("previousWorld0"),a.push("previousWorld1"),a.push("previousWorld2"),a.push("previousWorld3"))},a.BindLightProperties=function(a,b,c){a.transferToEffect(b,c+"")},a.BindLight=function(a,b,c,d,e,f){void 0===f&&(f=!0),a._bindLight(b,c,d,e,f)},a.BindLights=function(a,b,c,d,e){void 0===e&&(e=4);for(var e=Math.min(b.lightSources.length,e),f=0;f-1){var f=e.getTransformMatrixTexture(b);c.setTexture("boneSampler",f),c.setFloat("boneTextureWidth",4*(e.bones.length+1))}else{f=e.getTransformMatrices(b);f&&(c.setMatrices("mBones",f),d&&b.getScene().prePassRenderer&&b.getScene().prePassRenderer.getIndex(i.a.PREPASS_VELOCITY_TEXTURE_TYPE)&&(d.previousBones[b.uniqueId]||(d.previousBones[b.uniqueId]=f.slice()),c.setMatrices("mPreviousBones",d.previousBones[b.uniqueId]),a._CopyBonesTransformationMatrices(f,d.previousBones[b.uniqueId])))}}},a._CopyBonesTransformationMatrices=function(a,b){return b.set(a),b},a.BindMorphTargetParameters=function(a,b){var c=a.morphTargetManager;a&&c&&b.setFloatArray("morphTargetInfluences",c.influences)},a.BindLogDepth=function(a,b,c){a.LOGARITHMICDEPTH&&b.setFloat("logarithmicDepthConstant",2/(Math.log(c.activeCamera.maxZ+1)/Math.LN2))},a.BindClipPlane=function(a,b){k.a.BindClipPlane(a,b)},a._TmpMorphInfluencers={NUM_MORPH_INFLUENCERS:0},a._tempFogColor=j.a.Black(),a}()},function(a,b,c){var d,e,f,g,h;c.d(b,"a",function(){return d}),c.d(b,"c",function(){return e}),c.d(b,"b",function(){return f}),c.d(b,"e",function(){return g}),c.d(b,"d",function(){return h}),(function(a){a[a.Generic=0]="Generic",a[a.Keyboard=1]="Keyboard",a[a.Mouse=2]="Mouse",a[a.Touch=3]="Touch",a[a.DualShock=4]="DualShock",a[a.Xbox=5]="Xbox",a[a.Switch=6]="Switch"})(d||(d={})),(function(a){a[a.Horizontal=0]="Horizontal",a[a.Vertical=1]="Vertical",a[a.LeftClick=2]="LeftClick",a[a.MiddleClick=3]="MiddleClick",a[a.RightClick=4]="RightClick",a[a.BrowserBack=5]="BrowserBack",a[a.BrowserForward=6]="BrowserForward",a[a.MouseWheelX=7]="MouseWheelX",a[a.MouseWheelY=8]="MouseWheelY",a[a.MouseWheelZ=9]="MouseWheelZ",a[a.DeltaHorizontal=10]="DeltaHorizontal",a[a.DeltaVertical=11]="DeltaVertical",a[a.FakeMove=12]="FakeMove"})(e||(e={})),(function(a){a[a.Cross=0]="Cross",a[a.Circle=1]="Circle",a[a.Square=2]="Square",a[a.Triangle=3]="Triangle",a[a.L1=4]="L1",a[a.R1=5]="R1",a[a.L2=6]="L2",a[a.R2=7]="R2",a[a.Share=8]="Share",a[a.Options=9]="Options",a[a.L3=10]="L3",a[a.R3=11]="R3",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.TouchPad=17]="TouchPad",a[a.LStickXAxis=18]="LStickXAxis",a[a.LStickYAxis=19]="LStickYAxis",a[a.RStickXAxis=20]="RStickXAxis",a[a.RStickYAxis=21]="RStickYAxis"})(f||(f={})),(function(a){a[a.A=0]="A",a[a.B=1]="B",a[a.X=2]="X",a[a.Y=3]="Y",a[a.LB=4]="LB",a[a.RB=5]="RB",a[a.LT=6]="LT",a[a.RT=7]="RT",a[a.Back=8]="Back",a[a.Start=9]="Start",a[a.LS=10]="LS",a[a.RS=11]="RS",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.LStickXAxis=17]="LStickXAxis",a[a.LStickYAxis=18]="LStickYAxis",a[a.RStickXAxis=19]="RStickXAxis",a[a.RStickYAxis=20]="RStickYAxis"})(g||(g={})),(function(a){a[a.B=0]="B",a[a.A=1]="A",a[a.Y=2]="Y",a[a.X=3]="X",a[a.L=4]="L",a[a.R=5]="R",a[a.ZL=6]="ZL",a[a.ZR=7]="ZR",a[a.Minus=8]="Minus",a[a.Plus=9]="Plus",a[a.LS=10]="LS",a[a.RS=11]="RS",a[a.DPadUp=12]="DPadUp",a[a.DPadDown=13]="DPadDown",a[a.DPadLeft=14]="DPadLeft",a[a.DPadRight=15]="DPadRight",a[a.Home=16]="Home",a[a.Capture=17]="Capture",a[a.LStickXAxis=18]="LStickXAxis",a[a.LStickYAxis=19]="LStickYAxis",a[a.RStickXAxis=20]="RStickXAxis",a[a.RStickYAxis=21]="RStickYAxis"})(h||(h={}))},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f});var d=c(2),e=function(){function a(){}return a.NAME_EFFECTLAYER="EffectLayer",a.NAME_LAYER="Layer",a.NAME_LENSFLARESYSTEM="LensFlareSystem",a.NAME_BOUNDINGBOXRENDERER="BoundingBoxRenderer",a.NAME_PARTICLESYSTEM="ParticleSystem",a.NAME_GAMEPAD="Gamepad",a.NAME_SIMPLIFICATIONQUEUE="SimplificationQueue",a.NAME_GEOMETRYBUFFERRENDERER="GeometryBufferRenderer",a.NAME_PREPASSRENDERER="PrePassRenderer",a.NAME_DEPTHRENDERER="DepthRenderer",a.NAME_DEPTHPEELINGRENDERER="DepthPeelingRenderer",a.NAME_POSTPROCESSRENDERPIPELINEMANAGER="PostProcessRenderPipelineManager",a.NAME_SPRITE="Sprite",a.NAME_SUBSURFACE="SubSurface",a.NAME_OUTLINERENDERER="Outline",a.NAME_PROCEDURALTEXTURE="ProceduralTexture",a.NAME_SHADOWGENERATOR="ShadowGenerator",a.NAME_OCTREE="Octree",a.NAME_PHYSICSENGINE="PhysicsEngine",a.NAME_AUDIO="Audio",a.STEP_ISREADYFORMESH_EFFECTLAYER=0,a.STEP_BEFOREEVALUATEACTIVEMESH_BOUNDINGBOXRENDERER=0,a.STEP_EVALUATESUBMESH_BOUNDINGBOXRENDERER=0,a.STEP_PREACTIVEMESH_BOUNDINGBOXRENDERER=0,a.STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER=1,a.STEP_BEFORECAMERADRAW_PREPASS=0,a.STEP_BEFORECAMERADRAW_EFFECTLAYER=1,a.STEP_BEFORECAMERADRAW_LAYER=2,a.STEP_BEFORERENDERTARGETDRAW_PREPASS=0,a.STEP_BEFORERENDERTARGETDRAW_LAYER=1,a.STEP_BEFORERENDERINGMESH_PREPASS=0,a.STEP_BEFORERENDERINGMESH_OUTLINE=1,a.STEP_AFTERRENDERINGMESH_PREPASS=0,a.STEP_AFTERRENDERINGMESH_OUTLINE=1,a.STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW=0,a.STEP_AFTERRENDERINGGROUPDRAW_BOUNDINGBOXRENDERER=1,a.STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE=0,a.STEP_BEFORECAMERAUPDATE_GAMEPAD=1,a.STEP_BEFORECLEAR_PROCEDURALTEXTURE=0,a.STEP_AFTERRENDERTARGETDRAW_PREPASS=0,a.STEP_AFTERRENDERTARGETDRAW_LAYER=1,a.STEP_AFTERCAMERADRAW_PREPASS=0,a.STEP_AFTERCAMERADRAW_EFFECTLAYER=1,a.STEP_AFTERCAMERADRAW_LENSFLARESYSTEM=2,a.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW=3,a.STEP_AFTERCAMERADRAW_LAYER=4,a.STEP_AFTERRENDER_AUDIO=0,a.STEP_GATHERRENDERTARGETS_DEPTHRENDERER=0,a.STEP_GATHERRENDERTARGETS_GEOMETRYBUFFERRENDERER=1,a.STEP_GATHERRENDERTARGETS_SHADOWGENERATOR=2,a.STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER=3,a.STEP_GATHERACTIVECAMERARENDERTARGETS_DEPTHRENDERER=0,a.STEP_BEFORECLEARSTAGE_PREPASS=0,a.STEP_BEFORERENDERTARGETCLEARSTAGE_PREPASS=0,a.STEP_POINTERMOVE_SPRITE=0,a.STEP_POINTERDOWN_SPRITE=0,a.STEP_POINTERUP_SPRITE=0,a}(),f=function(a){function b(b){return a.apply(this,b)||this}return Object(d.d)(b,a),b.Create=function(){return Object.create(b.prototype)},b.prototype.registerStep=function(a,b,c){var d=0;for(Number.MAX_VALUE;d0){d=null!==(d=null===(d=this.indices)||void 0===d?void 0:d.length)&&void 0!==d?d:0;if(this.indices||(this.indices=new Array(e)),this.indices.length!==e)if(Array.isArray(this.indices))this.indices.length=e;else{c=c||this.indices instanceof Uint32Array?new Uint32Array(e):new Uint16Array(e);c.set(this.indices),this.indices=c}for(var e=this.positions?this.positions.length/3:0,c=0,f=b;cd.bbSize.y?d.bbSize.x:d.bbSize.y;ja>d.bbSize.z?ja:d.bbSize.z,aa=d.subDiv.X*D/d.bbSize.x,G=d.subDiv.Y*D/d.bbSize.y,H=d.subDiv.Z*D/d.bbSize.z,I=d.subDiv.max*d.subDiv.max,d.facetPartitioning.length=0}for(f=0;fa.LongPressDelay&&!d._isPointerSwiping()&&(d._startingPointerTime=0,f.processTrigger(s.a.ACTION_OnLongPressTrigger,o.a.CreateNew(b.pickedMesh,c)))},a.LongPressDelay)}}else for(var g=0,h=e._pointerDownStage;ga.DragMovementThreshold||Math.abs(this._startingPointerPosition.y-this._pointerY)>a.DragMovementThreshold},a.prototype.simulatePointerUp=function(a,b,c){b=new PointerEvent("pointerup",b);var d=new B;c?d.doubleClick=!0:d.singleClick=!0,this._checkPrePointerObservable(a,b,w.a.POINTERUP)||this._processPointerUp(a,b,d)},a.prototype._processPointerUp=function(a,b,c){var d=this._scene;if(a&&a&&a.pickedMesh){if(this._pickedUpMesh=a.pickedMesh,this._pickedDownMesh===this._pickedUpMesh&&(d.onPointerPick&&d.onPointerPick(b,a),c.singleClick&&!c.ignore&&d.onPointerObservable.hasObservers())){var e=w.a.POINTERPICK,f=new w.b(e,b,a);this._setRayOnPointerInfo(f),d.onPointerObservable.notifyObservers(f,e)}e=a.pickedMesh._getActionManagerForTrigger();if(e&&!c.ignore){e.processTrigger(s.a.ACTION_OnPickUpTrigger,o.a.CreateNew(a.pickedMesh,b,a)),!c.hasSwiped&&c.singleClick&&e.processTrigger(s.a.ACTION_OnPickTrigger,o.a.CreateNew(a.pickedMesh,b,a));e=a.pickedMesh._getActionManagerForTrigger(s.a.ACTION_OnDoublePickTrigger);c.doubleClick&&e&&e.processTrigger(s.a.ACTION_OnDoublePickTrigger,o.a.CreateNew(a.pickedMesh,b,a))}}else if(!c.ignore)for(var e=0,g=d._pointerUpStage;ea.DoubleClickDelay&&!f._doubleClickOccured||b!==f._previousButtonPressed)&&(f._doubleClickOccured=!1,c.singleClick=!0,c.ignore=!1,d(c,f._currentPickResult))},this._initClickEvent=function(b,c,d,e){var g=new B;f._currentPickResult=null;var h=null,i=b.hasSpecificMask(w.a.POINTERPICK)||c.hasSpecificMask(w.a.POINTERPICK)||b.hasSpecificMask(w.a.POINTERTAP)||c.hasSpecificMask(w.a.POINTERTAP)||b.hasSpecificMask(w.a.POINTERDOUBLETAP)||c.hasSpecificMask(w.a.POINTERDOUBLETAP);!i&&x.a&&(h=f._initActionManager(h,g))&&(i=h.hasPickTriggers);var j=!1;if(i){i=d.button;if(g.hasSwiped=f._isPointerSwiping(),!g.hasSwiped){d=!a.ExclusiveDoubleClickMode;d||(d=!b.hasSpecificMask(w.a.POINTERDOUBLETAP)&&!c.hasSpecificMask(w.a.POINTERDOUBLETAP))&&!x.a.HasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)&&(h=f._initActionManager(h,g))&&(d=!h.hasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)),d?(Date.now()-f._previousStartingPointerTime>a.DoubleClickDelay||i!==f._previousButtonPressed)&&(g.singleClick=!0,e(g,f._currentPickResult),j=!0):(f._previousDelayedSimpleClickTimeout=f._delayedSimpleClickTimeout,f._delayedSimpleClickTimeout=window.setTimeout(f._delayedSimpleClick.bind(f,i,g,e),a.DoubleClickDelay));d=b.hasSpecificMask(w.a.POINTERDOUBLETAP)||c.hasSpecificMask(w.a.POINTERDOUBLETAP);!d&&x.a.HasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)&&(h=f._initActionManager(h,g))&&(d=h.hasSpecificTrigger(s.a.ACTION_OnDoublePickTrigger)),d&&(i===f._previousButtonPressed&&Date.now()-f._previousStartingPointerTime=z.c.LeftClick&&a.inputIndex<=z.c.RightClick&&1===a.currentState&&f._onPointerDown(e),b&&a.inputIndex>=z.c.LeftClick&&a.inputIndex<=z.c.RightClick&&0===a.currentState&&f._onPointerUp(e),d&&(a.inputIndex===z.c.Horizontal||a.inputIndex===z.c.Vertical||a.inputIndex===z.c.DeltaHorizontal||a.inputIndex===z.c.DeltaVertical||a.inputIndex===z.c.FakeMove?f._onPointerMove(e):a.inputIndex!==z.c.MouseWheelX&&a.inputIndex!==z.c.MouseWheelY&&a.inputIndex!==z.c.MouseWheelZ||f._onPointerMove(e)))}),this._alreadyAttached=!0}},a.prototype.detachControl=function(){this._alreadyAttachedTo&&this._alreadyAttached&&(this._deviceInputSystem.onInputChangedObservable.remove(this._onInputObserver),this._scene.doNotHandleCursors||(this._alreadyAttachedTo.style.cursor=this._scene.defaultCursor),this._alreadyAttached=!1)},a.prototype.setPointerOverMesh=function(a,b,c){if(void 0===b&&(b=0),this._meshUnderPointerId[b]!==a){var d,e=this._meshUnderPointerId[b];e&&(d=e._getActionManagerForTrigger(s.a.ACTION_OnPointerOutTrigger))&&d.processTrigger(s.a.ACTION_OnPointerOutTrigger,o.a.CreateNew(e,void 0,{pointerId:b})),a?(this._meshUnderPointerId[b]=a,this._pointerOverMesh=a,(d=a._getActionManagerForTrigger(s.a.ACTION_OnPointerOverTrigger))&&d.processTrigger(s.a.ACTION_OnPointerOverTrigger,o.a.CreateNew(a,void 0,{pointerId:b,pickResult:c}))):(delete this._meshUnderPointerId[b],this._pointerOverMesh=null)}},a.prototype.getPointerOverMesh=function(){return this._pointerOverMesh},a.prototype._invalidateMesh=function(a){for(var b in this._pointerOverMesh===a&&(this._pointerOverMesh=null),this._pickedDownMesh===a&&(this._pickedDownMesh=null),this._pickedUpMesh===a&&(this._pickedUpMesh=null),this._meshUnderPointerId)this._meshUnderPointerId[b]===a&&delete this._meshUnderPointerId[b]},a.DragMovementThreshold=10,a.LongPressDelay=500,a.DoubleClickDelay=300,a.ExclusiveDoubleClickMode=!1,a}(),D=c(60),E=c(8),F=c(96),aa=c(137),G=c(54),H=c(65),I=c(146);b=c(172);var J=function(a){function b(c,e){var f=a.call(this)||this;f._inputManager=new C(f),f.cameraToUseForPointers=null,f._isScene=!0,f._blockEntityCollection=!1,f.autoClear=!0,f.autoClearDepthAndStencil=!0,f.clearColor=new E.b(.2,.2,.3,1),f.ambientColor=new E.a(0,0,0),f._environmentIntensity=1,f._forceWireframe=!1,f._skipFrustumClipping=!1,f._forcePointsCloud=!1,f.animationsEnabled=!0,f._animationPropertiesOverride=null,f.useConstantAnimationDeltaTime=!1,f.constantlyUpdateMeshUnderPointer=!1,f.hoverCursor="pointer",f.defaultCursor="",f.doNotHandleCursors=!1,f.preventDefaultOnPointerDown=!0,f.preventDefaultOnPointerUp=!0,f.metadata=null,f.reservedDataStore=null,f.disableOfflineSupportExceptionRules=new Array,f.onDisposeObservable=new g.c,f._onDisposeObserver=null,f.onBeforeRenderObservable=new g.c,f._onBeforeRenderObserver=null,f.onAfterRenderObservable=new g.c,f.onAfterRenderCameraObservable=new g.c,f._onAfterRenderObserver=null,f.onBeforeAnimationsObservable=new g.c,f.onAfterAnimationsObservable=new g.c,f.onBeforeDrawPhaseObservable=new g.c,f.onAfterDrawPhaseObservable=new g.c,f.onReadyObservable=new g.c,f.onBeforeCameraRenderObservable=new g.c,f._onBeforeCameraRenderObserver=null,f.onAfterCameraRenderObservable=new g.c,f._onAfterCameraRenderObserver=null,f.onBeforeActiveMeshesEvaluationObservable=new g.c,f.onAfterActiveMeshesEvaluationObservable=new g.c,f.onBeforeParticlesRenderingObservable=new g.c,f.onAfterParticlesRenderingObservable=new g.c,f.onDataLoadedObservable=new g.c,f.onNewCameraAddedObservable=new g.c,f.onCameraRemovedObservable=new g.c,f.onNewLightAddedObservable=new g.c,f.onLightRemovedObservable=new g.c,f.onNewGeometryAddedObservable=new g.c,f.onGeometryRemovedObservable=new g.c,f.onNewTransformNodeAddedObservable=new g.c,f.onTransformNodeRemovedObservable=new g.c,f.onNewMeshAddedObservable=new g.c,f.onMeshRemovedObservable=new g.c,f.onNewSkeletonAddedObservable=new g.c,f.onSkeletonRemovedObservable=new g.c,f.onNewMaterialAddedObservable=new g.c,f.onNewMultiMaterialAddedObservable=new g.c,f.onMaterialRemovedObservable=new g.c,f.onMultiMaterialRemovedObservable=new g.c,f.onNewTextureAddedObservable=new g.c,f.onTextureRemovedObservable=new g.c,f.onBeforeRenderTargetsRenderObservable=new g.c,f.onAfterRenderTargetsRenderObservable=new g.c,f.onBeforeStepObservable=new g.c,f.onAfterStepObservable=new g.c,f.onActiveCameraChanged=new g.c,f.onBeforeRenderingGroupObservable=new g.c,f.onAfterRenderingGroupObservable=new g.c,f.onMeshImportedObservable=new g.c,f.onAnimationFileImportedObservable=new g.c,f._registeredForLateAnimationBindings=new h.b(256),f.onPrePointerObservable=new g.c,f.onPointerObservable=new g.c,f.onPreKeyboardObservable=new g.c,f.onKeyboardObservable=new g.c,f._useRightHandedSystem=!1,f._timeAccumulator=0,f._currentStepId=0,f._currentInternalStep=0,f._fogEnabled=!0,f._fogMode=b.FOGMODE_NONE,f.fogColor=new E.a(.2,.2,.3),f.fogDensity=.1,f.fogStart=0,f.fogEnd=1e3,f.needsPreviousWorldMatrices=!1,f._shadowsEnabled=!0,f._lightsEnabled=!0,f.activeCameras=new Array,f._texturesEnabled=!0,f.physicsEnabled=!0,f.particlesEnabled=!0,f.spritesEnabled=!0,f._skeletonsEnabled=!0,f.lensFlaresEnabled=!0,f.collisionsEnabled=!0,f.gravity=new k.e(0,-9.807,0),f.postProcessesEnabled=!0,f.renderTargetsEnabled=!0,f.dumpNextRenderTargets=!1,f.customRenderTargets=new Array,f.importedMeshesFiles=new Array,f.probesEnabled=!0,f._meshesForIntersections=new h.b(256),f.proceduralTexturesEnabled=!0,f._totalVertices=new D.a,f._activeIndices=new D.a,f._activeParticles=new D.a,f._activeBones=new D.a,f._animationTime=0,f.animationTimeScale=1,f._renderId=0,f._frameId=0,f._executeWhenReadyTimeoutId=-1,f._intermediateRendering=!1,f._defaultFrameBufferCleared=!1,f._viewUpdateFlag=-1,f._projectionUpdateFlag=-1,f._toBeDisposed=new Array(256),f._activeRequests=new Array,f._pendingData=new Array,f._isDisposed=!1,f.dispatchAllSubMeshesOfActiveMeshes=!1,f._activeMeshes=new h.a(256),f._processedMaterials=new h.a(256),f._renderTargets=new h.b(256),f._activeParticleSystems=new h.a(256),f._activeSkeletons=new h.b(32),f._softwareSkinnedMeshes=new h.b(32),f._activeAnimatables=new Array,f._transformMatrix=k.a.Zero(),f.requireLightSorting=!1,f._components=[],f._serializableComponents=[],f._transientComponents=[],f._beforeCameraUpdateStage=r.b.Create(),f._beforeClearStage=r.b.Create(),f._beforeRenderTargetClearStage=r.b.Create(),f._gatherRenderTargetsStage=r.b.Create(),f._gatherActiveCameraRenderTargetsStage=r.b.Create(),f._isReadyForMeshStage=r.b.Create(),f._beforeEvaluateActiveMeshStage=r.b.Create(),f._evaluateSubMeshStage=r.b.Create(),f._preActiveMeshStage=r.b.Create(),f._cameraDrawRenderTargetStage=r.b.Create(),f._beforeCameraDrawStage=r.b.Create(),f._beforeRenderTargetDrawStage=r.b.Create(),f._beforeRenderingGroupDrawStage=r.b.Create(),f._beforeRenderingMeshStage=r.b.Create(),f._afterRenderingMeshStage=r.b.Create(),f._afterRenderingGroupDrawStage=r.b.Create(),f._afterCameraDrawStage=r.b.Create(),f._afterRenderTargetDrawStage=r.b.Create(),f._afterRenderStage=r.b.Create(),f._pointerMoveStage=r.b.Create(),f._pointerDownStage=r.b.Create(),f._pointerUpStage=r.b.Create(),f.geometriesByUniqueId=null,f._defaultMeshCandidates={data:[],length:0},f._defaultSubMeshCandidates={data:[],length:0},f._preventFreeActiveMeshesAndRenderingGroups=!1,f._activeMeshesFrozen=!1,f._skipEvaluateActiveMeshesCompletely=!1,f._allowPostProcessClearColor=!0,f.getDeterministicFrameTime=function(){return f._engine.getTimeStep()},f._blockMaterialDirtyMechanism=!1,f._perfCollector=null,f.onComputePressureChanged=new g.c;var i=Object(d.a)({useGeometryUniqueIdsMap:!0,useMaterialMeshMap:!0,useClonedMeshMap:!0,virtual:!1},e);return f._engine=c||u.a.LastCreatedEngine,i.virtual?f._engine._virtualScenes.push(f):(u.a._LastCreatedScene=f,f._engine.scenes.push(f)),f._uid=null,f._renderingManager=new q.b(f),p.a&&(f.postProcessManager=new p.a(f)),Object(t.e)()&&f.attachControl(),f._createUbo(),l.a&&(f._imageProcessingConfiguration=new l.a),f.setDefaultCandidateProviders(),i.useGeometryUniqueIdsMap&&(f.geometriesByUniqueId={}),f.useMaterialMeshMap=i.useMaterialMeshMap,f.useClonedMeshMap=i.useClonedMeshMap,e&&e.virtual||f._engine.onNewSceneAddedObservable.notifyObservers(f),I.a.IsAvailable&&(f._computePressureObserver=new I.a(function(a){f.onComputePressureChanged.notifyObservers(a)},{cpuUtilizationThresholds:[.25,.5,.75,.9],cpuSpeedThresholds:[.5]}),f._computePressureObserver.observe()),f}return Object(d.d)(b,a),b.DefaultMaterialFactory=function(a){throw Object(v.a)("StandardMaterial")},b.CollisionCoordinatorFactory=function(){throw Object(v.a)("DefaultCollisionCoordinator")},Object.defineProperty(b.prototype,"environmentTexture",{get:function(){return this._environmentTexture},set:function(a){this._environmentTexture!==a&&(this._environmentTexture=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"environmentIntensity",{get:function(){return this._environmentIntensity},set:function(a){this._environmentIntensity!==a&&(this._environmentIntensity=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forceWireframe",{get:function(){return this._forceWireframe},set:function(a){this._forceWireframe!==a&&(this._forceWireframe=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skipFrustumClipping",{get:function(){return this._skipFrustumClipping},set:function(a){this._skipFrustumClipping!==a&&(this._skipFrustumClipping=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcePointsCloud",{get:function(){return this._forcePointsCloud},set:function(a){this._forcePointsCloud!==a&&(this._forcePointsCloud=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"animationPropertiesOverride",{get:function(){return this._animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"onDispose",{set:function(a){this._onDisposeObserver&&this.onDisposeObservable.remove(this._onDisposeObserver),this._onDisposeObserver=this.onDisposeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"beforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),a&&(this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"afterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),a&&(this._onAfterRenderObserver=this.onAfterRenderObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"beforeCameraRender",{set:function(a){this._onBeforeCameraRenderObserver&&this.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=this.onBeforeCameraRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"afterCameraRender",{set:function(a){this._onAfterCameraRenderObserver&&this.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=this.onAfterCameraRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"unTranslatedPointer",{get:function(){return this._inputManager.unTranslatedPointer},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DragMovementThreshold",{get:function(){return C.DragMovementThreshold},set:function(a){C.DragMovementThreshold=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LongPressDelay",{get:function(){return C.LongPressDelay},set:function(a){C.LongPressDelay=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DoubleClickDelay",{get:function(){return C.DoubleClickDelay},set:function(a){C.DoubleClickDelay=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ExclusiveDoubleClickMode",{get:function(){return C.ExclusiveDoubleClickMode},set:function(a){C.ExclusiveDoubleClickMode=a},enumerable:!1,configurable:!0}),b.prototype.bindEyePosition=function(a,b,c){var d;void 0===b&&(b="vEyePosition"),void 0===c&&(c=!1);d=this._forcedViewPosition?this._forcedViewPosition:this._mirroredCameraPosition?this._mirroredCameraPosition:null!==(d=this.activeCamera.globalPosition)&&void 0!==d?d:this.activeCamera.devicePosition;var e=this.useRightHandedSystem===(null!=this._mirroredCameraPosition);return k.c.Vector4[0].set(d.x,d.y,d.z,e?-1:1),a&&(c?a.setFloat3(b,k.c.Vector4[0].x,k.c.Vector4[0].y,k.c.Vector4[0].z):a.setVector4(b,k.c.Vector4[0])),k.c.Vector4[0]},b.prototype.finalizeSceneUbo=function(){var a=this.getSceneUniformBuffer(),b=this.bindEyePosition(null);return a.updateFloat4("vEyePosition",b.x,b.y,b.z,b.w),a.update(),a},Object.defineProperty(b.prototype,"useRightHandedSystem",{get:function(){return this._useRightHandedSystem},set:function(a){this._useRightHandedSystem!==a&&(this._useRightHandedSystem=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),b.prototype.setStepId=function(a){this._currentStepId=a},b.prototype.getStepId=function(){return this._currentStepId},b.prototype.getInternalStep=function(){return this._currentInternalStep},Object.defineProperty(b.prototype,"fogEnabled",{get:function(){return this._fogEnabled},set:function(a){this._fogEnabled!==a&&(this._fogEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fogMode",{get:function(){return this._fogMode},set:function(a){this._fogMode!==a&&(this._fogMode=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_MiscDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"prePass",{get:function(){return!!this.prePassRenderer&&this.prePassRenderer.defaultRT.enabled},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowsEnabled",{get:function(){return this._shadowsEnabled},set:function(a){this._shadowsEnabled!==a&&(this._shadowsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_LightDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lightsEnabled",{get:function(){return this._lightsEnabled},set:function(a){this._lightsEnabled!==a&&(this._lightsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_LightDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"activeCamera",{get:function(){return this._activeCamera},set:function(a){a!==this._activeCamera&&(this._activeCamera=a,this.onActiveCameraChanged.notifyObservers(this))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"defaultMaterial",{get:function(){return this._defaultMaterial||(this._defaultMaterial=b.DefaultMaterialFactory(this)),this._defaultMaterial},set:function(a){this._defaultMaterial=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"texturesEnabled",{get:function(){return this._texturesEnabled},set:function(a){this._texturesEnabled!==a&&(this._texturesEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_TextureDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skeletonsEnabled",{get:function(){return this._skeletonsEnabled},set:function(a){this._skeletonsEnabled!==a&&(this._skeletonsEnabled=a,this.markAllMaterialsAsDirty(s.a.MATERIAL_AttributesDirtyFlag))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionCoordinator",{get:function(){return this._collisionCoordinator||(this._collisionCoordinator=b.CollisionCoordinatorFactory(),this._collisionCoordinator.init(this)),this._collisionCoordinator},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"frustumPlanes",{get:function(){return this._frustumPlanes},enumerable:!1,configurable:!0}),b.prototype._registerTransientComponents=function(){if(this._transientComponents.length>0){for(var a=0,b=this._transientComponents;a0)return!1;for(a=0;a0,e=0,f=this._isReadyForMeshStage;e0)for(c=0,d=this.activeCameras;c0},enumerable:!1,configurable:!0}),b.prototype.executeWhenReady=function(a){var b=this;this.onReadyObservable.add(a),-1===this._executeWhenReadyTimeoutId&&(this._executeWhenReadyTimeoutId=setTimeout(function(){b._checkIsReady()},150))},b.prototype.whenReadyAsync=function(){var a=this;return new Promise(function(b){a.executeWhenReady(function(){b()})})},b.prototype._checkIsReady=function(){var a=this;return this._registerTransientComponents(),this.isReady()?(this.onReadyObservable.notifyObservers(this),this.onReadyObservable.clear(),void (this._executeWhenReadyTimeoutId=-1)):this._isDisposed?(this.onReadyObservable.clear(),void (this._executeWhenReadyTimeoutId=-1)):void (this._executeWhenReadyTimeoutId=setTimeout(function(){a._checkIsReady()},150))},Object.defineProperty(b.prototype,"animatables",{get:function(){return this._activeAnimatables},enumerable:!1,configurable:!0}),b.prototype.resetLastAnimationTimeFrame=function(){this._animationTimeLast=f.a.Now},b.prototype.getViewMatrix=function(){return this._viewMatrix},b.prototype.getProjectionMatrix=function(){return this._projectionMatrix},b.prototype.getTransformMatrix=function(){return this._transformMatrix},b.prototype.setTransformMatrix=function(a,b,c,d){this._viewUpdateFlag===a.updateFlag&&this._projectionUpdateFlag===b.updateFlag||(this._viewUpdateFlag=a.updateFlag,this._projectionUpdateFlag=b.updateFlag,this._viewMatrix=a,this._projectionMatrix=b,this._viewMatrix.multiplyToRef(this._projectionMatrix,this._transformMatrix),this._frustumPlanes?F.a.GetPlanesToRef(this._transformMatrix,this._frustumPlanes):this._frustumPlanes=F.a.GetPlanes(this._transformMatrix),this._multiviewSceneUbo&&this._multiviewSceneUbo.useUbo?this._updateMultiviewUbo(c,d):this._sceneUbo.useUbo&&(this._sceneUbo.updateMatrix("viewProjection",this._transformMatrix),this._sceneUbo.updateMatrix("view",this._viewMatrix),this._sceneUbo.updateMatrix("projection",this._projectionMatrix)))},b.prototype.getSceneUniformBuffer=function(){return this._multiviewSceneUbo?this._multiviewSceneUbo:this._sceneUbo},b.prototype.getUniqueId=function(){return aa.a.UniqueId},b.prototype.addMesh=function(a,b){var c=this;void 0===b&&(b=!1),this._blockEntityCollection||(this.meshes.push(a),a._resyncLightSources(),a.parent||a._addToSceneRootNodes(),this.onNewMeshAddedObservable.notifyObservers(a),b&&a.getChildMeshes().forEach(function(a){c.addMesh(a)}))},b.prototype.removeMesh=function(a,b){var c=this;void 0===b&&(b=!1);var d=this.meshes.indexOf(a);return-1!==d&&(this.meshes[d]=this.meshes[this.meshes.length-1],this.meshes.pop(),a.parent||a._removeFromSceneRootNodes()),this._inputManager._invalidateMesh(a),this.onMeshRemovedObservable.notifyObservers(a),b&&a.getChildMeshes().forEach(function(a){c.removeMesh(a)}),d},b.prototype.addTransformNode=function(a){this._blockEntityCollection||(a._indexInSceneTransformNodesArray=this.transformNodes.length,this.transformNodes.push(a),a.parent||a._addToSceneRootNodes(),this.onNewTransformNodeAddedObservable.notifyObservers(a))},b.prototype.removeTransformNode=function(a){var b=a._indexInSceneTransformNodesArray;if(-1!==b){if(b!==this.transformNodes.length-1){var c=this.transformNodes[this.transformNodes.length-1];this.transformNodes[b]=c,c._indexInSceneTransformNodesArray=b}a._indexInSceneTransformNodesArray=-1,this.transformNodes.pop(),a.parent||a._removeFromSceneRootNodes()}return this.onTransformNodeRemovedObservable.notifyObservers(a),b},b.prototype.removeSkeleton=function(a){var b=this.skeletons.indexOf(a);return-1!==b&&(this.skeletons.splice(b,1),this.onSkeletonRemovedObservable.notifyObservers(a)),b},b.prototype.removeMorphTargetManager=function(a){a=this.morphTargetManagers.indexOf(a);return-1!==a&&this.morphTargetManagers.splice(a,1),a},b.prototype.removeLight=function(a){var b=this.lights.indexOf(a);if(-1!==b){for(var c=0,d=this.meshes;c0?this.activeCamera=this.cameras[0]:this.activeCamera=null),this.onCameraRemovedObservable.notifyObservers(a),b},b.prototype.removeParticleSystem=function(a){a=this.particleSystems.indexOf(a);return-1!==a&&this.particleSystems.splice(a,1),a},b.prototype.removeAnimation=function(a){a=this.animations.indexOf(a);return-1!==a&&this.animations.splice(a,1),a},b.prototype.stopAnimation=function(a,b,c){},b.prototype.removeAnimationGroup=function(a){a=this.animationGroups.indexOf(a);return-1!==a&&this.animationGroups.splice(a,1),a},b.prototype.removeMultiMaterial=function(a){var b=this.multiMaterials.indexOf(a);return-1!==b&&this.multiMaterials.splice(b,1),this.onMultiMaterialRemovedObservable.notifyObservers(a),b},b.prototype.removeMaterial=function(a){var b=a._indexInSceneMaterialArray;if(-1!==b&&b=0;b--)if(this.materials[b].id===a)return this.materials[b];return null},b.prototype.getMaterialByName=function(a){for(var b=0;b=0;b--)if(this.meshes[b].id===a)return this.meshes[b];return null},b.prototype.getLastEntryById=function(a){var b;for(b=this.meshes.length-1;b>=0;b--)if(this.meshes[b].id===a)return this.meshes[b];for(b=this.transformNodes.length-1;b>=0;b--)if(this.transformNodes[b].id===a)return this.transformNodes[b];for(b=this.cameras.length-1;b>=0;b--)if(this.cameras[b].id===a)return this.cameras[b];for(b=this.lights.length-1;b>=0;b--)if(this.lights[b].id===a)return this.lights[b];return null},b.prototype.getNodeById=function(a){var b=this.getMeshById(a);if(b)return b;b=this.getTransformNodeById(a);if(b)return b;b=this.getLightById(a);if(b)return b;b=this.getCameraById(a);if(b)return b;b=this.getBoneById(a);return b||null},b.prototype.getNodeByName=function(a){var b=this.getMeshByName(a);if(b)return b;b=this.getTransformNodeByName(a);if(b)return b;b=this.getLightByName(a);if(b)return b;b=this.getCameraByName(a);if(b)return b;b=this.getBoneByName(a);return b||null},b.prototype.getMeshByName=function(a){for(var b=0;b=0;b--)if(this.skeletons[b].id===a)return this.skeletons[b];return null},b.prototype.getSkeletonByUniqueId=function(a){for(var b=0;b0&&(null===(a=this.activeCamera)||void 0===a||a._activeMeshes.reset(),this._activeMeshes.reset(),this._renderingManager.reset(),this._processedMaterials.reset(),this._activeParticleSystems.reset(),this._activeSkeletons.reset(),this._softwareSkinnedMeshes.reset());else if(this._activeMeshesFrozen&&this._activeMeshes.length){if(!this._skipEvaluateActiveMeshesCompletely)for(var a=this._activeMeshes.length,b=0;b0&&0!=(d.layerMask&this.activeCamera.layerMask)&&(this._skipFrustumClipping||d.alwaysSelectAsActiveMesh||d.isInFrustum(this._frustumPlanes)))){this._activeMeshes.push(d),this.activeCamera._activeMeshes.push(d),e!==d&&e._activate(this._renderId,!1);for(var f=0,g=this._preActiveMeshStage;f0)for(var c=this.getActiveSubMeshCandidates(b),d=c.length,e=0;e1?this.setTransformMatrix(a._rigCameras[0].getViewMatrix(),a._rigCameras[0].getProjectionMatrix(),a._rigCameras[1].getViewMatrix(),a._rigCameras[1].getProjectionMatrix()):this.updateTransformMatrix(),this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera),this._evaluateActiveMeshes();for(c=0;c0&&this._renderTargets.concatWithNoDuplicate(a.customRenderTargets),b&&b.customRenderTargets&&b.customRenderTargets.length>0&&this._renderTargets.concatWithNoDuplicate(b.customRenderTargets),this.environmentTexture&&this.environmentTexture.isRenderTarget&&this._renderTargets.pushNoDuplicate(this.environmentTexture);for(f=0,c=this._gatherActiveCameraRenderTargetsStage;f0){e.b.StartPerformanceCounter("Render targets",this._renderTargets.length>0);for(c=0;c0),this._renderId++}for(f=0,g=this._cameraDrawRenderTargetStage;f1&&this.getEngine().getCaps().multiview)return this._renderForCamera(a,void 0,b),void this.onAfterRenderCameraObservable.notifyObservers(a);if(a._useMultiviewToSingleView)this._renderMultiviewToSingleView(a);else{this.onBeforeCameraRenderObservable.notifyObservers(a);for(b=0;b-1&&(d.trigger===s.a.ACTION_OnIntersectionExitTrigger&&d._executeCurrent(o.a.CreateNew(b,void 0,f)),b.actionManager.hasSpecificTrigger(s.a.ACTION_OnIntersectionExitTrigger,function(a){a=a.mesh?a.mesh:a;return f===a})&&d.trigger!==s.a.ACTION_OnIntersectionExitTrigger||b._intersectionsInProgress.splice(g,1))}}}},b.prototype._advancePhysicsEngineStep=function(a){},b.prototype._animate=function(){},b.prototype.animate=function(){if(this._engine.isDeterministicLockStep()){var a=Math.max(b.MinDeltaTime,Math.min(this._engine.getDeltaTime(),b.MaxDeltaTime))+this._timeAccumulator,c=this._engine.getTimeStep(),d=1e3/c/1e3,e=0,f=this._engine.getLockstepMaxSteps(),g=Math.floor(a/c);for(g=Math.min(g,f);a>0&&e0)for(b=0;b0),this._intermediateRendering=!0;for(var f=0;f0),this._intermediateRendering=!1,this._renderId++}this.activeCamera=d,this._activeCamera&&this._activeCamera.cameraRigMode!==s.a.RIG_MODE_CUSTOM&&!this.prePass&&this._bindFrameBuffer(this._activeCamera,!1),this.onAfterRenderTargetsRenderObservable.notifyObservers(this);for(g=0,f=this._beforeClearStage;g0)for(b=0;b0);else{if(!this.activeCamera)throw new Error("No camera defined");this._processSubCameras(this.activeCamera,!1)}this._checkIntersections();for(f=0,g=this._afterRenderStage;f-1&&this._engine.scenes.splice(a,1),u.a._LastCreatedScene===this&&(this._engine.scenes.length>0?u.a._LastCreatedScene=this._engine.scenes[this._engine.scenes.length-1]:u.a._LastCreatedScene=null),(a=this._engine._virtualScenes.indexOf(this))>-1&&this._engine._virtualScenes.splice(a,1),this._engine.wipeCaches(!0),this._isDisposed=!0}},Object.defineProperty(b.prototype,"isDisposed",{get:function(){return this._isDisposed},enumerable:!1,configurable:!0}),b.prototype.clearCachedVertexData=function(){for(var a=0;a0&&parseInt(v[v.length-1])>=A)continue}for(v=0,A=w;v1?new r.a:new q,this._badOS=/iPad/i.test(navigator.userAgent)||/iPhone/i.test(navigator.userAgent),this._creationOptions=d,!1}}return Object.defineProperty(a,"NpmPackage",{get:function(){return"babylonjs@5.0.0-alpha.56"},enumerable:!1,configurable:!0}),Object.defineProperty(a,"Version",{get:function(){return"5.0.0-alpha.56"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"description",{get:function(){var a=this.name+this.webGLVersion;return this._caps.parallelShaderCompile&&(a+=" - Parallel shader compilation"),a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"WebGL"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"version",{get:function(){return this._webGLVersion},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ShadersRepository",{get:function(){return f.a.ShadersRepository},set:function(a){f.a.ShadersRepository=a},enumerable:!1,configurable:!0}),a.prototype._getShaderProcessor=function(a){return this._shaderProcessor},Object.defineProperty(a.prototype,"useReverseDepthBuffer",{get:function(){return this._useReverseDepthBuffer},set:function(a){a!==this._useReverseDepthBuffer&&(this._useReverseDepthBuffer=a,this._depthCullingState.depthFunc=a?l.a.GEQUAL:l.a.LEQUAL)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frameId",{get:function(){return this._frameId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsUniformBuffers",{get:function(){return this.webGLVersion>1&&!this.disableUniformBuffers},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_shouldUseHighPrecisionShader",{get:function(){return!(!this._caps.highPrecisionShaderSupported||!this._highPrecisionShadersAllowed)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"needPOTTextures",{get:function(){return this._webGLVersion<2||this.forcePOTTextures},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"activeRenderLoops",{get:function(){return this._activeRenderLoops},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"doNotHandleContextLost",{get:function(){return this._doNotHandleContextLost},set:function(a){this._doNotHandleContextLost=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_supportsHardwareTextureRescaling",{get:function(){return!1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"framebufferDimensionsObject",{set:function(a){this._framebufferDimensionsObject=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentViewport",{get:function(){return this._cachedViewport},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture",{get:function(){return this._emptyTexture||(this._emptyTexture=this.createRawTexture(new Uint8Array(4),1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture3D",{get:function(){return this._emptyTexture3D||(this._emptyTexture3D=this.createRawTexture3D(new Uint8Array(4),1,1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture3D},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyTexture2DArray",{get:function(){return this._emptyTexture2DArray||(this._emptyTexture2DArray=this.createRawTexture2DArray(new Uint8Array(4),1,1,1,l.a.TEXTUREFORMAT_RGBA,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)),this._emptyTexture2DArray},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"emptyCubeTexture",{get:function(){if(!this._emptyCubeTexture){var a=new Uint8Array(4);a=[a,a,a,a,a,a];this._emptyCubeTexture=this.createRawCubeTexture(a,1,l.a.TEXTUREFORMAT_RGBA,l.a.TEXTURETYPE_UNSIGNED_INT,!1,!1,l.a.TEXTURE_NEAREST_SAMPLINGMODE)}return this._emptyCubeTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isWebGPU",{get:function(){return this._isWebGPU},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderPlatformName",{get:function(){return this._shaderPlatformName},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"snapshotRendering",{get:function(){return this._snapshotRenderingEnabled},set:function(a){this._snapshotRenderingEnabled=!1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"snapshotRenderingMode",{get:function(){return this._snapshotRenderingMode},set:function(a){this._snapshotRenderingMode=a},enumerable:!1,configurable:!0}),a.prototype.snapshotRenderingReset=function(){this.snapshotRendering=!1},a._createCanvas=function(a,b){if("undefined"==typeof document)return new OffscreenCanvas(a,b);var c=document.createElement("canvas");return c.width=a,c.height=b,c},a.prototype.createCanvas=function(b,c){return a._createCanvas(b,c)},a.prototype.createCanvasImage=function(){return document.createElement("img")},a.prototype._restoreEngineAfterContextLost=function(a){var b=this;setTimeout(function(){return Object(d.b)(b,void 0,void 0,function(){var b,c,e,f,g;return Object(d.e)(this,function(d){switch(d.label){case 0:return this._dummyFramebuffer=null,b=this._depthCullingState.depthTest,c=this._depthCullingState.depthFunc,e=this._depthCullingState.depthMask,f=this._stencilState.stencilTest,[4,a()];case 1:return d.sent(),this._rebuildEffects(),null===(g=this._rebuildComputeEffects)||void 0===g||g.call(this),this._rebuildInternalTextures(),this._rebuildRenderTargetWrappers(),this._rebuildBuffers(),this.wipeCaches(!0),this._depthCullingState.depthTest=b,this._depthCullingState.depthFunc=c,this._depthCullingState.depthMask=e,this._stencilState.stencilTest=f,n.a.Warn(this.name+" context successfully restored."),this.onContextRestoredObservable.notifyObservers(this),this._contextWasLost=!1,[2]}})})},0)},a.prototype._sharedInit=function(a,b,c){this._renderingCanvas=a},a.prototype._getShaderProcessingContext=function(a){return null},a.prototype._rebuildInternalTextures=function(){for(var a=0,b=this._internalTexturesCache.slice();a1?this._gl.getParameter(this._gl.MAX_SAMPLES):1,maxCubemapTextureSize:this._gl.getParameter(this._gl.MAX_CUBE_MAP_TEXTURE_SIZE),maxRenderTextureSize:this._gl.getParameter(this._gl.MAX_RENDERBUFFER_SIZE),maxVertexAttribs:this._gl.getParameter(this._gl.MAX_VERTEX_ATTRIBS),maxVaryingVectors:this._gl.getParameter(this._gl.MAX_VARYING_VECTORS),maxFragmentUniformVectors:this._gl.getParameter(this._gl.MAX_FRAGMENT_UNIFORM_VECTORS),maxVertexUniformVectors:this._gl.getParameter(this._gl.MAX_VERTEX_UNIFORM_VECTORS),parallelShaderCompile:this._gl.getExtension("KHR_parallel_shader_compile")||void 0,standardDerivatives:this._webGLVersion>1||null!==this._gl.getExtension("OES_standard_derivatives"),maxAnisotropy:1,astc:this._gl.getExtension("WEBGL_compressed_texture_astc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_astc"),bptc:this._gl.getExtension("EXT_texture_compression_bptc")||this._gl.getExtension("WEBKIT_EXT_texture_compression_bptc"),s3tc:this._gl.getExtension("WEBGL_compressed_texture_s3tc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"),s3tc_srgb:this._gl.getExtension("WEBGL_compressed_texture_s3tc_srgb")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc_srgb"),pvrtc:this._gl.getExtension("WEBGL_compressed_texture_pvrtc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"),etc1:this._gl.getExtension("WEBGL_compressed_texture_etc1")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_etc1"),etc2:this._gl.getExtension("WEBGL_compressed_texture_etc")||this._gl.getExtension("WEBKIT_WEBGL_compressed_texture_etc")||this._gl.getExtension("WEBGL_compressed_texture_es3_0"),textureAnisotropicFilterExtension:this._gl.getExtension("EXT_texture_filter_anisotropic")||this._gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic")||this._gl.getExtension("MOZ_EXT_texture_filter_anisotropic"),uintIndices:this._webGLVersion>1||null!==this._gl.getExtension("OES_element_index_uint"),fragmentDepthSupported:this._webGLVersion>1||null!==this._gl.getExtension("EXT_frag_depth"),highPrecisionShaderSupported:!1,timerQuery:this._gl.getExtension("EXT_disjoint_timer_query_webgl2")||this._gl.getExtension("EXT_disjoint_timer_query"),supportOcclusionQuery:this._webGLVersion>1,canUseTimestampForTimerQuery:!1,drawBuffersExtension:!1,maxMSAASamples:1,colorBufferFloat:!!(this._webGLVersion>1&&this._gl.getExtension("EXT_color_buffer_float")),textureFloat:!!(this._webGLVersion>1||this._gl.getExtension("OES_texture_float")),textureHalfFloat:!!(this._webGLVersion>1||this._gl.getExtension("OES_texture_half_float")),textureHalfFloatRender:!1,textureFloatLinearFiltering:!1,textureFloatRender:!1,textureHalfFloatLinearFiltering:!1,vertexArrayObject:!1,instancedArrays:!1,textureLOD:!!(this._webGLVersion>1||this._gl.getExtension("EXT_shader_texture_lod")),blendMinMax:!1,multiview:this._gl.getExtension("OVR_multiview2"),oculusMultiview:this._gl.getExtension("OCULUS_multiview"),depthTextureExtension:!1,canUseGLInstanceID:this._webGLVersion>1,canUseGLVertexID:this._webGLVersion>1,supportComputeShaders:!1,supportSRGBBuffers:!1},this._glVersion=this._gl.getParameter(this._gl.VERSION);var a=this._gl.getExtension("WEBGL_debug_renderer_info");if(null!=a&&(this._glRenderer=this._gl.getParameter(a.UNMASKED_RENDERER_WEBGL),this._glVendor=this._gl.getParameter(a.UNMASKED_VENDOR_WEBGL)),this._glVendor||(this._glVendor="Unknown vendor"),this._glRenderer||(this._glRenderer="Unknown renderer"),36193!==this._gl.HALF_FLOAT_OES&&(this._gl.HALF_FLOAT_OES=36193),34842!==this._gl.RGBA16F&&(this._gl.RGBA16F=34842),34836!==this._gl.RGBA32F&&(this._gl.RGBA32F=34836),35056!==this._gl.DEPTH24_STENCIL8&&(this._gl.DEPTH24_STENCIL8=35056),this._caps.timerQuery&&(1===this._webGLVersion&&(this._gl.getQuery=this._caps.timerQuery.getQueryEXT.bind(this._caps.timerQuery)),this._caps.canUseTimestampForTimerQuery=this._gl.getQuery(this._caps.timerQuery.TIMESTAMP_EXT,this._caps.timerQuery.QUERY_COUNTER_BITS_EXT)>0),this._caps.maxAnisotropy=this._caps.textureAnisotropicFilterExtension?this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0,this._caps.textureFloatLinearFiltering=!(!this._caps.textureFloat||!this._gl.getExtension("OES_texture_float_linear")),this._caps.textureFloatRender=!(!this._caps.textureFloat||!this._canRenderToFloatFramebuffer()),this._caps.textureHalfFloatLinearFiltering=!!(this._webGLVersion>1||this._caps.textureHalfFloat&&this._gl.getExtension("OES_texture_half_float_linear")),this._caps.astc&&(this._gl.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR=this._caps.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR),this._caps.bptc&&(this._gl.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT=this._caps.bptc.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT),this._caps.s3tc_srgb&&(this._gl.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT=this._caps.s3tc_srgb.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,this._gl.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT=this._caps.s3tc_srgb.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT),this._webGLVersion>1&&5131!==this._gl.HALF_FLOAT_OES&&(this._gl.HALF_FLOAT_OES=5131),this._caps.textureHalfFloatRender=this._caps.textureHalfFloat&&this._canRenderToHalfFloatFramebuffer(),this._webGLVersion>1)this._caps.drawBuffersExtension=!0,this._caps.maxMSAASamples=this._gl.getParameter(this._gl.MAX_SAMPLES);else{a=this._gl.getExtension("WEBGL_draw_buffers");if(null!==a){this._caps.drawBuffersExtension=!0,this._gl.drawBuffers=a.drawBuffersWEBGL.bind(a),this._gl.DRAW_FRAMEBUFFER=this._gl.FRAMEBUFFER;for(var b=0;b<16;b++)this._gl["COLOR_ATTACHMENT"+b+"_WEBGL"]=a["COLOR_ATTACHMENT"+b+"_WEBGL"]}}if(this._webGLVersion>1)this._caps.depthTextureExtension=!0;else{b=this._gl.getExtension("WEBGL_depth_texture");null!=b&&(this._caps.depthTextureExtension=!0,this._gl.UNSIGNED_INT_24_8=b.UNSIGNED_INT_24_8_WEBGL)}if(this.disableVertexArrayObjects)this._caps.vertexArrayObject=!1;else if(this._webGLVersion>1)this._caps.vertexArrayObject=!0;else{a=this._gl.getExtension("OES_vertex_array_object");null!=a&&(this._caps.vertexArrayObject=!0,this._gl.createVertexArray=a.createVertexArrayOES.bind(a),this._gl.bindVertexArray=a.bindVertexArrayOES.bind(a),this._gl.deleteVertexArray=a.deleteVertexArrayOES.bind(a))}if(this._webGLVersion>1)this._caps.instancedArrays=!0;else{b=this._gl.getExtension("ANGLE_instanced_arrays");null!=b?(this._caps.instancedArrays=!0,this._gl.drawArraysInstanced=b.drawArraysInstancedANGLE.bind(b),this._gl.drawElementsInstanced=b.drawElementsInstancedANGLE.bind(b),this._gl.vertexAttribDivisor=b.vertexAttribDivisorANGLE.bind(b)):this._caps.instancedArrays=!1}if(this._gl.getShaderPrecisionFormat){a=this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER,this._gl.HIGH_FLOAT);b=this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER,this._gl.HIGH_FLOAT);a&&b&&(this._caps.highPrecisionShaderSupported=0!==a.precision&&0!==b.precision)}if(this._webGLVersion>1)this._caps.blendMinMax=!0;else{a=this._gl.getExtension("EXT_blend_minmax");null!=a&&(this._caps.blendMinMax=!0,this._gl.MAX=a.MAX_EXT,this._gl.MIN=a.MIN_EXT)}if(this._webGLVersion>1)this._caps.supportSRGBBuffers=!0;else{b=this._gl.getExtension("EXT_sRGB");null!=b&&(this._caps.supportSRGBBuffers=!0,this._gl.SRGB=b.SRGB_EXT,this._gl.SRGB8=b.SRGB_ALPHA_EXT,this._gl.SRGB8_ALPHA8=b.SRGB_ALPHA_EXT)}this._depthCullingState.depthTest=!0,this._depthCullingState.depthFunc=this._gl.LEQUAL,this._depthCullingState.depthMask=!0,this._maxSimultaneousTextures=this._caps.maxCombinedTexturesImageUnits;for(a=0;a=0&&this._activeRenderLoops.splice(a,1)}else this._activeRenderLoops=[]},a.prototype._renderLoop=function(){if(!this._contextWasLost){var a=!0;if(!this.renderEvenInBackground&&this._windowIsBackground&&(a=!1),a){this.beginFrame();for(a=0;a0?this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow()):this._renderingQueueLaunched=!1},a.prototype.getRenderingCanvas=function(){return this._renderingCanvas},a.prototype.getAudioContext=function(){return this._audioContext},a.prototype.getAudioDestination=function(){return this._audioDestination},a.prototype.getHostWindow=function(){return Object(o.e)()?this._renderingCanvas&&this._renderingCanvas.ownerDocument&&this._renderingCanvas.ownerDocument.defaultView?this._renderingCanvas.ownerDocument.defaultView:window:null},a.prototype.getRenderWidth=function(a){return void 0===a&&(a=!1),!a&&this._currentRenderTarget?this._currentRenderTarget.width:this._framebufferDimensionsObject?this._framebufferDimensionsObject.framebufferWidth:this._gl.drawingBufferWidth},a.prototype.getRenderHeight=function(a){return void 0===a&&(a=!1),!a&&this._currentRenderTarget?this._currentRenderTarget.height:this._framebufferDimensionsObject?this._framebufferDimensionsObject.framebufferHeight:this._gl.drawingBufferHeight},a.prototype._queueNewFrame=function(b,c){return a.QueueNewFrame(b,c)},a.prototype.runRenderLoop=function(a){-1===this._activeRenderLoops.indexOf(a)&&(this._activeRenderLoops.push(a),this._renderingQueueLaunched||(this._renderingQueueLaunched=!0,this._boundRenderFunction=this._renderLoop.bind(this),this._frameHandler=this._queueNewFrame(this._boundRenderFunction,this.getHostWindow())))},a.prototype.clear=function(a,b,c,d){void 0===d&&(d=!1);var e=this.stencilStateComposer.useStencilGlobalOnly;this.stencilStateComposer.useStencilGlobalOnly=!0,this.applyStates(),this.stencilStateComposer.useStencilGlobalOnly=e;e=0;b&&a&&(this._gl.clearColor(a.r,a.g,a.b,void 0!==a.a?a.a:1),e|=this._gl.COLOR_BUFFER_BIT),c&&(this.useReverseDepthBuffer?(this._depthCullingState.depthFunc=this._gl.GEQUAL,this._gl.clearDepth(0)):this._gl.clearDepth(1),e|=this._gl.DEPTH_BUFFER_BIT),d&&(this._gl.clearStencil(0),e|=this._gl.STENCIL_BUFFER_BIT),this._gl.clear(e)},a.prototype._viewport=function(a,b,c,d){a===this._viewportCached.x&&b===this._viewportCached.y&&c===this._viewportCached.z&&d===this._viewportCached.w||(this._viewportCached.x=a,this._viewportCached.y=b,this._viewportCached.z=c,this._viewportCached.w=d,this._gl.viewport(a,b,c,d))},a.prototype.setViewport=function(a,b,c){b=b||this.getRenderWidth();c=c||this.getRenderHeight();var d=a.x||0,e=a.y||0;this._cachedViewport=a,this._viewport(d*b,e*c,b*a.width,c*a.height)},a.prototype.beginFrame=function(){},a.prototype.endFrame=function(){this._badOS&&this.flushFramebuffer(),this._frameId++},a.prototype.resize=function(a){var b,c;void 0===a&&(a=!1),Object(o.e)()?(b=this._renderingCanvas?this._renderingCanvas.clientWidth||this._renderingCanvas.width:window.innerWidth,c=this._renderingCanvas?this._renderingCanvas.clientHeight||this._renderingCanvas.height:window.innerHeight):(b=this._renderingCanvas?this._renderingCanvas.width:100,c=this._renderingCanvas?this._renderingCanvas.height:100),this.setSize(b/this._hardwareScalingLevel,c/this._hardwareScalingLevel,a)},a.prototype.setSize=function(a,b,c){return void 0===c&&(c=!1),!!this._renderingCanvas&&(a|=0,b|=0,!(!c&&this._renderingCanvas.width===a&&this._renderingCanvas.height===b)&&(this._renderingCanvas.width=a,this._renderingCanvas.height=b,!0))},a.prototype.bindFramebuffer=function(a,b,c,d,e,f,g){var h,i;void 0===b&&(b=0),void 0===f&&(f=0),void 0===g&&(g=0);var j=a;this._currentRenderTarget&&this.unBindFramebuffer(this._currentRenderTarget),this._currentRenderTarget=a,this._bindUnboundFramebuffer(j._MSAAFramebuffer?j._MSAAFramebuffer:j._framebuffer);j=this._gl;a.is2DArray?j.framebufferTextureLayer(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,null===(h=a.texture._hardwareTexture)||void 0===h?void 0:h.underlyingResource,f,g):a.isCube&&j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_CUBE_MAP_POSITIVE_X+b,null===(h=a.texture._hardwareTexture)||void 0===h?void 0:h.underlyingResource,f);h=a._depthStencilTexture;if(h){var k=a._depthStencilTextureWithStencil?j.DEPTH_STENCIL_ATTACHMENT:j.DEPTH_ATTACHMENT;a.is2DArray?j.framebufferTextureLayer(j.FRAMEBUFFER,k,null===(i=h._hardwareTexture)||void 0===i?void 0:i.underlyingResource,f,g):a.isCube?j.framebufferTexture2D(j.FRAMEBUFFER,k,j.TEXTURE_CUBE_MAP_POSITIVE_X+b,null===(i=h._hardwareTexture)||void 0===i?void 0:i.underlyingResource,f):j.framebufferTexture2D(j.FRAMEBUFFER,k,j.TEXTURE_2D,null===(g=h._hardwareTexture)||void 0===g?void 0:g.underlyingResource,f)}this._cachedViewport&&!e?this.setViewport(this._cachedViewport,c,d):(c||(c=a.width,f&&(c/=Math.pow(2,f))),d||(d=a.height,f&&(d/=Math.pow(2,f))),this._viewport(0,0,c,d)),this.wipeCaches()},a.prototype.setState=function(a,b,c,d,e,f,g){void 0===b&&(b=0),void 0===d&&(d=!1),void 0===g&&(g=0),(this._depthCullingState.cull!==a||c)&&(this._depthCullingState.cull=a);e=null===(a=null!==(a=this.cullBackFaces)&&void 0!==a?a:e)||void 0===a||a?this._gl.BACK:this._gl.FRONT;(this._depthCullingState.cullFace!==e||c)&&(this._depthCullingState.cullFace=e),this.setZOffset(b),this.setZOffsetUnits(g);a=d?this._gl.CW:this._gl.CCW;(this._depthCullingState.frontFace!==a||c)&&(this._depthCullingState.frontFace=a),this._stencilStateComposer.stencilMaterial=f},a.prototype.setZOffset=function(a){this._depthCullingState.zOffset=this.useReverseDepthBuffer?-a:a},a.prototype.getZOffset=function(){var a=this._depthCullingState.zOffset;return this.useReverseDepthBuffer?-a:a},a.prototype.setZOffsetUnits=function(a){this._depthCullingState.zOffsetUnits=this.useReverseDepthBuffer?-a:a},a.prototype.getZOffsetUnits=function(){var a=this._depthCullingState.zOffsetUnits;return this.useReverseDepthBuffer?-a:a},a.prototype._bindUnboundFramebuffer=function(a){this._currentFramebuffer!==a&&(this._gl.bindFramebuffer(this._gl.FRAMEBUFFER,a),this._currentFramebuffer=a)},a.prototype._currentFrameBufferIsDefaultFrameBuffer=function(){return null===this._currentFramebuffer},a.prototype.generateMipmaps=function(a){this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0),this._gl.generateMipmap(this._gl.TEXTURE_2D),this._bindTextureDirectly(this._gl.TEXTURE_2D,null)},a.prototype.unBindFramebuffer=function(a,b,c){void 0===b&&(b=!1);var d=a;this._currentRenderTarget=null;var e=this._gl;if(d._MSAAFramebuffer){if(a.isMulti)return void this.unBindMultiColorAttachmentFramebuffer(a,b,c);e.bindFramebuffer(e.READ_FRAMEBUFFER,d._MSAAFramebuffer),e.bindFramebuffer(e.DRAW_FRAMEBUFFER,d._framebuffer),e.blitFramebuffer(0,0,a.width,a.height,0,0,a.width,a.height,e.COLOR_BUFFER_BIT,e.NEAREST)}!(null===(e=a.texture)||void 0===e?void 0:e.generateMipMaps)||b||a.isCube||this.generateMipmaps(a.texture),c&&(d._MSAAFramebuffer&&this._bindUnboundFramebuffer(d._framebuffer),c()),this._bindUnboundFramebuffer(null)},a.prototype.flushFramebuffer=function(){this._gl.flush()},a.prototype.restoreDefaultFramebuffer=function(){this._currentRenderTarget?this.unBindFramebuffer(this._currentRenderTarget):this._bindUnboundFramebuffer(null),this._cachedViewport&&this.setViewport(this._cachedViewport),this.wipeCaches()},a.prototype._resetVertexBufferBinding=function(){this.bindArrayBuffer(null),this._cachedVertexBuffers=null},a.prototype.createVertexBuffer=function(a){return this._createVertexBuffer(a,this._gl.STATIC_DRAW)},a.prototype._createVertexBuffer=function(a,b){var c=this._gl.createBuffer();if(!c)throw new Error("Unable to create vertex buffer");c=new s.a(c);return this.bindArrayBuffer(c),a instanceof Array?this._gl.bufferData(this._gl.ARRAY_BUFFER,new Float32Array(a),b):this._gl.bufferData(this._gl.ARRAY_BUFFER,a,b),this._resetVertexBufferBinding(),c.references=1,c},a.prototype.createDynamicVertexBuffer=function(a){return this._createVertexBuffer(a,this._gl.DYNAMIC_DRAW)},a.prototype._resetIndexBufferBinding=function(){this.bindIndexBuffer(null),this._cachedIndexBuffer=null},a.prototype.createIndexBuffer=function(a,b){var c=this._gl.createBuffer(),d=new s.a(c);if(!c)throw new Error("Unable to create index buffer");this.bindIndexBuffer(d);c=this._normalizeIndexData(a);return this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER,c,b?this._gl.DYNAMIC_DRAW:this._gl.STATIC_DRAW),this._resetIndexBufferBinding(),d.references=1,d.is32Bits=4===c.BYTES_PER_ELEMENT,d},a.prototype._normalizeIndexData=function(a){if(2===a.BYTES_PER_ELEMENT)return a;if(this._caps.uintIndices){if(a instanceof Uint32Array)return a;for(var b=0;b=65535)return new Uint32Array(a);return new Uint16Array(a)}return new Uint16Array(a)},a.prototype.bindArrayBuffer=function(a){this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.bindBuffer(a,this._gl.ARRAY_BUFFER)},a.prototype.bindUniformBlock=function(a,b,c){a=a.program;b=this._gl.getUniformBlockIndex(a,b);this._gl.uniformBlockBinding(a,b,c)},a.prototype.bindIndexBuffer=function(a){this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.bindBuffer(a,this._gl.ELEMENT_ARRAY_BUFFER)},a.prototype.bindBuffer=function(a,b){(this._vaoRecordInProgress||this._currentBoundBuffer[b]!==a)&&(this._gl.bindBuffer(b,a?a.underlyingResource:null),this._currentBoundBuffer[b]=a)},a.prototype.updateArrayBuffer=function(a){this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,a)},a.prototype._vertexAttribPointer=function(a,b,c,d,e,f,g){var h=this._currentBufferPointers[b];if(h){var i=!1;h.active?(h.buffer!==a&&(h.buffer=a,i=!0),h.size!==c&&(h.size=c,i=!0),h.type!==d&&(h.type=d,i=!0),h.normalized!==e&&(h.normalized=e,i=!0),h.stride!==f&&(h.stride=f,i=!0),h.offset!==g&&(h.offset=g,i=!0)):(i=!0,h.active=!0,h.index=b,h.size=c,h.type=d,h.normalized=e,h.stride=f,h.offset=g,h.buffer=a),(i||this._vaoRecordInProgress)&&(this.bindArrayBuffer(a),this._gl.vertexAttribPointer(b,c,d,e,f,g))}},a.prototype._bindIndexBufferWithCache=function(a){null!=a&&this._cachedIndexBuffer!==a&&(this._cachedIndexBuffer=a,this.bindIndexBuffer(a),this._uintIndicesCurrentlySet=a.is32Bits)},a.prototype._bindVertexBuffersAttributes=function(a,b,c){var d=b.getAttributesNames();this._vaoRecordInProgress||this._unbindVertexArrayObject(),this.unbindAllAttributes();for(var e=0;e=0){var g=d[e],h=null;if(c&&(h=c[g]),h||(h=a[g]),!h)continue;this._gl.enableVertexAttribArray(f),this._vaoRecordInProgress||(this._vertexAttribArraysEnabled[f]=!0);g=h.getBuffer();g&&(this._vertexAttribPointer(g,f,h.getSize(),h.type,h.normalized,h.byteStride,h.byteOffset),h.getIsInstanced()&&(this._gl.vertexAttribDivisor(f,h.getInstanceDivisor()),this._vaoRecordInProgress||(this._currentInstanceLocations.push(f),this._currentInstanceBuffers.push(g))))}}},a.prototype.recordVertexArrayObject=function(a,b,c,d){var e=this._gl.createVertexArray();return this._vaoRecordInProgress=!0,this._gl.bindVertexArray(e),this._mustWipeVertexAttributes=!0,this._bindVertexBuffersAttributes(a,c,d),this.bindIndexBuffer(b),this._vaoRecordInProgress=!1,this._gl.bindVertexArray(null),e},a.prototype.bindVertexArrayObject=function(a,b){this._cachedVertexArrayObject!==a&&(this._cachedVertexArrayObject=a,this._gl.bindVertexArray(a),this._cachedVertexBuffers=null,this._cachedIndexBuffer=null,this._uintIndicesCurrentlySet=null!=b&&b.is32Bits,this._mustWipeVertexAttributes=!0)},a.prototype.bindBuffersDirectly=function(a,b,c,d,e){if(this._cachedVertexBuffers!==a||this._cachedEffectForVertexBuffers!==e){this._cachedVertexBuffers=a,this._cachedEffectForVertexBuffers=e;var f=e.getAttributesCount();this._unbindVertexArrayObject(),this.unbindAllAttributes();for(var g=0,h=0;h=0&&(this._gl.enableVertexAttribArray(i),this._vertexAttribArraysEnabled[i]=!0,this._vertexAttribPointer(a,i,c[h],this._gl.FLOAT,!1,d,g)),g+=4*c[h]}}this._bindIndexBufferWithCache(b)},a.prototype._unbindVertexArrayObject=function(){this._cachedVertexArrayObject&&(this._cachedVertexArrayObject=null,this._gl.bindVertexArray(null))},a.prototype.bindBuffers=function(a,b,c,d){this._cachedVertexBuffers===a&&this._cachedEffectForVertexBuffers===c||(this._cachedVertexBuffers=a,this._cachedEffectForVertexBuffers=c,this._bindVertexBuffersAttributes(a,c,d)),this._bindIndexBufferWithCache(b)},a.prototype.unbindInstanceAttributes=function(){for(var a,b=0,c=this._currentInstanceLocations.length;b1?"#version 300 es #define WEBGL2 ":"";b=this._compileShader(b,"vertex",d,g);c=this._compileShader(c,"fragment",d,g);return this._createShaderProgram(a,b,c,e,f)},a.prototype.inlineShaderCode=function(a){return a},a.prototype.createPipelineContext=function(a){a=new t.a;return a.engine=this,this._caps.parallelShaderCompile&&(a.isParallelCompiled=!0),a},a.prototype.createMaterialContext=function(){},a.prototype.createDrawContext=function(){},a.prototype._createShaderProgram=function(a,b,c,d,e){void 0===e&&null;e=d.createProgram();if(a.program=e,!e)throw new Error("Unable to create program");return d.attachShader(e,b),d.attachShader(e,c),d.linkProgram(e),a.context=d,a.vertexShader=b,a.fragmentShader=c,a.isParallelCompiled||this._finalizePipelineContext(a),e},a.prototype._finalizePipelineContext=function(a){var b=a.context,c=a.vertexShader,d=a.fragmentShader,e=a.program;if(!b.getProgramParameter(e,b.LINK_STATUS)){var f,g;if(!this._gl.getShaderParameter(c,this._gl.COMPILE_STATUS)&&(f=this._gl.getShaderInfoLog(c)))throw a.vertexCompilationError=f,new Error("VERTEX SHADER "+f);if(!this._gl.getShaderParameter(d,this._gl.COMPILE_STATUS)&&(f=this._gl.getShaderInfoLog(d)))throw a.fragmentCompilationError=f,new Error("FRAGMENT SHADER "+f);if(g=b.getProgramInfoLog(e))throw a.programLinkError=g,new Error(g)}if(this.validateShaderPrograms&&(b.validateProgram(e),!b.getProgramParameter(e,b.VALIDATE_STATUS)&&(g=b.getProgramInfoLog(e))))throw a.programValidationError=g,new Error(g);b.deleteShader(c),b.deleteShader(d),a.vertexShader=void 0,a.fragmentShader=void 0,a.onCompiled&&(a.onCompiled(),a.onCompiled=void 0)},a.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i,j){e=a;e.program=d?this.createRawShaderProgram(e,b,c,void 0,i):this.createShaderProgram(e,b,c,h,void 0,i),e.program.__SPECTOR_rebuildProgram=g},a.prototype._isRenderingStateCompiled=function(a){a=a;return!!this._gl.getProgramParameter(a.program,this._caps.parallelShaderCompile.COMPLETION_STATUS_KHR)&&(this._finalizePipelineContext(a),!0)},a.prototype._executeWhenRenderingStateIsCompiled=function(a,b){a=a;if(a.isParallelCompiled){var c=a.onCompiled;a.onCompiled=c?function(){c(),b()}:b}else b()},a.prototype.getUniforms=function(a,b){for(var c=new Array,a=a,d=0;d1||this.isWebGPU||b)},a.prototype._createTextureBase=function(b,c,d,f,g,h,i,j,k,o,p,q,r,s,t,u){var v=this;void 0===g&&(g=l.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===h&&(h=null),void 0===i&&(i=null),void 0===o&&(o=null),void 0===p&&(p=null),void 0===q&&(q=null),void 0===r&&(r=null);var w="data:"===(b=b||"").substr(0,5),x="blob:"===b.substr(0,5),y=w&&-1!==b.indexOf(";base64,"),z=p||new m.a(this,m.b.Url),A=b;!this._transformTextureUrl||y||p||o||(b=this._transformTextureUrl(b)),A!==b&&(z._originalUrl=A);var B=b.lastIndexOf("."),C=r||(B>-1?b.substring(B).toLowerCase():""),D=null;C.indexOf("?")>-1&&(C=C.split("?")[0]);for(var B=0,E=a._TextureLoaders;Bi||c.height>i||!w._supportsHardwareTextureRescaling)return w._prepareWorkingCanvas(),!(!w._workingCanvas||!w._workingContext)&&(w._workingCanvas.width=a,w._workingCanvas.height=b,w._workingContext.drawImage(c,0,0,c.width,c.height,0,0,a,b),h.texImage2D(h.TEXTURE_2D,0,k,e,h.UNSIGNED_BYTE,w._workingCanvas),f.width=a,f.height=b,!1);var l=new m.a(w,m.b.Temp);return w._bindTextureDirectly(h.TEXTURE_2D,l,!0),h.texImage2D(h.TEXTURE_2D,0,k,e,h.UNSIGNED_BYTE,c),w._rescaleTexture(l,f,d,k,function(){w._releaseTexture(l),w._bindTextureDirectly(h.TEXTURE_2D,f,!0),g()}),!0},h,i,j,k,s,t,v)},a._FileToolsLoadImage=function(a,b,c,d,e,f){throw Object(g.a)("FileTools")},a.prototype._rescaleTexture=function(a,b,c,d,e){},a.prototype.createRawTexture=function(a,b,c,d,e,f,h,i,j){throw void 0===i&&null,void 0===j&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype.createRawCubeTexture=function(a,b,c,d,e,f,h,i){throw void 0===i&&null,Object(g.a)("Engine.RawTexture")},a.prototype.createRawTexture3D=function(a,b,c,d,e,f,h,i,j,k){throw void 0===j&&null,void 0===k&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype.createRawTexture2DArray=function(a,b,c,d,e,f,h,i,j,k){throw void 0===j&&null,void 0===k&&l.a.TEXTURETYPE_UNSIGNED_INT,Object(g.a)("Engine.RawTexture")},a.prototype._unpackFlipY=function(a){this._unpackFlipYCached!==a&&(this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL,a?1:0),this.enableUnpackFlipYCached&&(this._unpackFlipYCached=a))},a.prototype._getUnpackAlignement=function(){return this._gl.getParameter(this._gl.UNPACK_ALIGNMENT)},a.prototype._getTextureTarget=function(a){return a.isCube?this._gl.TEXTURE_CUBE_MAP:a.is3D?this._gl.TEXTURE_3D:a.is2DArray||a.isMultiview?this._gl.TEXTURE_2D_ARRAY:this._gl.TEXTURE_2D},a.prototype.updateTextureSamplingMode=function(a,b,c){void 0===c&&(c=!1);var d=this._getTextureTarget(b),e=this._getSamplingParameters(a,b.generateMipMaps||c);this._setTextureParameterInteger(d,this._gl.TEXTURE_MAG_FILTER,e.mag,b),this._setTextureParameterInteger(d,this._gl.TEXTURE_MIN_FILTER,e.min),c&&(b.generateMipMaps=!0,this._gl.generateMipmap(d)),this._bindTextureDirectly(d,null),b.samplingMode=a},a.prototype.updateTextureDimensions=function(a,b,c,d){void 0===d&&1},a.prototype.updateTextureWrappingMode=function(a,b,c,d){void 0===c&&(c=null),void 0===d&&(d=null);var e=this._getTextureTarget(a);null!==b&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_S,this._getTextureWrapMode(b),a),a._cachedWrapU=b),null!==c&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_T,this._getTextureWrapMode(c),a),a._cachedWrapV=c),(a.is2DArray||a.is3D)&&null!==d&&(this._setTextureParameterInteger(e,this._gl.TEXTURE_WRAP_R,this._getTextureWrapMode(d),a),a._cachedWrapR=d),this._bindTextureDirectly(e,null)},a.prototype._setupDepthStencilTexture=function(a,b,c,d,e,f){void 0===f&&(f=1);c=b.width||b;var g=b.height||b;b=b.layers||0;a.baseWidth=c,a.baseHeight=g,a.width=c,a.height=g,a.is2DArray=b>0,a.depth=b,a.isReady=!0,a.samples=f,a.generateMipMaps=!1,a.samplingMode=d?l.a.TEXTURE_BILINEAR_SAMPLINGMODE:l.a.TEXTURE_NEAREST_SAMPLINGMODE,a.type=l.a.TEXTURETYPE_UNSIGNED_INT,a._comparisonFunction=e;c=this._gl;g=this._getTextureTarget(a);b=this._getSamplingParameters(a.samplingMode,!1);c.texParameteri(g,c.TEXTURE_MAG_FILTER,b.mag),c.texParameteri(g,c.TEXTURE_MIN_FILTER,b.min),c.texParameteri(g,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(g,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),0===e?(c.texParameteri(g,c.TEXTURE_COMPARE_FUNC,l.a.LEQUAL),c.texParameteri(g,c.TEXTURE_COMPARE_MODE,c.NONE)):(c.texParameteri(g,c.TEXTURE_COMPARE_FUNC,e),c.texParameteri(g,c.TEXTURE_COMPARE_MODE,c.COMPARE_REF_TO_TEXTURE))},a.prototype._uploadCompressedDataToTextureDirectly=function(a,b,c,d,e,f,g){void 0===f&&(f=0),void 0===g&&(g=0);var h=this._gl,i=h.TEXTURE_2D;if(a.isCube&&(i=h.TEXTURE_CUBE_MAP_POSITIVE_X+f),a._useSRGBBuffer)switch(b){case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:b=h.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4:b=h.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1:this._caps.s3tc_srgb?b=h.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:a._useSRGBBuffer=!1;break;case l.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:this._caps.s3tc_srgb?b=h.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:a._useSRGBBuffer=!1;break;default:a._useSRGBBuffer=!1}this._gl.compressedTexImage2D(i,g,b,c,d,0,e)},a.prototype._uploadDataToTextureDirectly=function(a,b,c,d,e,f){void 0===c&&(c=0),void 0===d&&(d=0),void 0===f&&(f=!1);var g=this._gl,h=this._getWebGLTextureType(a.type),i=this._getInternalFormat(a.format);e=void 0===e?this._getRGBABufferInternalSizedFormat(a.type,a.format,a._useSRGBBuffer):this._getInternalFormat(e,a._useSRGBBuffer);this._unpackFlipY(a.invertY);var j=g.TEXTURE_2D;a.isCube&&(j=g.TEXTURE_CUBE_MAP_POSITIVE_X+c);c=Math.round(Math.log(a.width)*Math.LOG2E);var k=Math.round(Math.log(a.height)*Math.LOG2E);c=f?a.width:Math.pow(2,Math.max(c-d,0));f=f?a.height:Math.pow(2,Math.max(k-d,0));g.texImage2D(j,d,e,c,f,0,i,h,b)},a.prototype.updateTextureData=function(a,b,c,d,e,f,g,h){void 0===g&&(g=0),void 0===h&&(h=0);var i=this._gl,j=this._getWebGLTextureType(a.type),k=this._getInternalFormat(a.format);this._unpackFlipY(a.invertY);var l=i.TEXTURE_2D;a.isCube&&(l=i.TEXTURE_CUBE_MAP_POSITIVE_X+g),i.texSubImage2D(l,h,c,d,e,f,k,j,b)},a.prototype._uploadArrayBufferViewToTexture=function(a,b,c,d){void 0===c&&(c=0),void 0===d&&(d=0);var e=this._gl;e=a.isCube?e.TEXTURE_CUBE_MAP:e.TEXTURE_2D;this._bindTextureDirectly(e,a,!0),this._uploadDataToTextureDirectly(a,b,c,d),this._bindTextureDirectly(e,null,!0)},a.prototype._prepareWebGLTextureContinuation=function(a,b,c,d,e){var f=this._gl;if(f){e=this._getSamplingParameters(e,!c);f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MAG_FILTER,e.mag),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MIN_FILTER,e.min),c||d||f.generateMipmap(f.TEXTURE_2D),this._bindTextureDirectly(f.TEXTURE_2D,null),b&&b._removePendingData(a),a.onLoadedObservable.notifyObservers(a),a.onLoadedObservable.clear()}},a.prototype._prepareWebGLTexture=function(b,c,d,e,f,g,h,i,j){var k=this;void 0===j&&(j=l.a.TEXTURE_TRILINEAR_SAMPLINGMODE);var o=this.getCaps().maxTextureSize,q=Math.min(o,this.needPOTTextures?a.GetExponentOfTwo(e.width,o):e.width);o=Math.min(o,this.needPOTTextures?a.GetExponentOfTwo(e.height,o):e.height);var r=this._gl;r&&(b._hardwareTexture?(this._bindTextureDirectly(r.TEXTURE_2D,b,!0),this._unpackFlipY(void 0===f||!!f),b.baseWidth=e.width,b.baseHeight=e.height,b.width=q,b.height=o,b.isReady=!0,i(q,o,e,c,b,function(){k._prepareWebGLTextureContinuation(b,d,g,h,j)})||this._prepareWebGLTextureContinuation(b,d,g,h,j)):d&&d._removePendingData(b))},a.prototype._setupFramebufferDepthAttachments=function(a,b,c,d,e){void 0===e&&(e=1);var f=this._gl;if(a&&b)return this._createRenderBuffer(c,d,e,f.DEPTH_STENCIL,f.DEPTH24_STENCIL8,f.DEPTH_STENCIL_ATTACHMENT);if(b){b=f.DEPTH_COMPONENT16;return this._webGLVersion>1&&(b=f.DEPTH_COMPONENT32F),this._createRenderBuffer(c,d,e,b,b,f.DEPTH_ATTACHMENT)}return a?this._createRenderBuffer(c,d,e,f.STENCIL_INDEX8,f.STENCIL_INDEX8,f.STENCIL_ATTACHMENT):null},a.prototype._createRenderBuffer=function(a,b,c,d,e,f,g){void 0===g&&(g=!0);var h=this._gl,i=h.createRenderbuffer();return h.bindRenderbuffer(h.RENDERBUFFER,i),c>1&&h.renderbufferStorageMultisample?h.renderbufferStorageMultisample(h.RENDERBUFFER,c,e,a,b):h.renderbufferStorage(h.RENDERBUFFER,d,a,b),h.framebufferRenderbuffer(h.FRAMEBUFFER,f,h.RENDERBUFFER,i),g&&h.bindRenderbuffer(h.RENDERBUFFER,null),i},a.prototype._releaseTexture=function(a){var b;this._deleteTexture(null===(b=a._hardwareTexture)||void 0===b?void 0:b.underlyingResource),this.unbindAllTextures();b=this._internalTexturesCache.indexOf(a);-1!==b&&this._internalTexturesCache.splice(b,1),a._lodTextureHigh&&a._lodTextureHigh.dispose(),a._lodTextureMid&&a._lodTextureMid.dispose(),a._lodTextureLow&&a._lodTextureLow.dispose(),a._irradianceTexture&&a._irradianceTexture.dispose()},a.prototype._releaseRenderTargetWrapper=function(a){a=this._renderTargetWrapperCache.indexOf(a);-1!==a&&this._renderTargetWrapperCache.splice(a,1)},a.prototype._deleteTexture=function(a){a&&this._gl.deleteTexture(a)},a.prototype._setProgram=function(a){this._currentProgram!==a&&(this._gl.useProgram(a),this._currentProgram=a)},a.prototype.bindSamplers=function(a){var b=a.getPipelineContext();this._setProgram(b.program);for(var b=a.getSamplers(),c=0;c-1;if(c&&f&&(this._activeChannel=b._associatedChannel),this._boundTexturesCache[this._activeChannel]!==b||d){if(this._activateCurrentTexture(),b&&b.isMultiview)throw!1,"_bindTextureDirectly called with a multiview texture!";this._gl.bindTexture(a,null!==(a=null===(d=null==b?void 0:b._hardwareTexture)||void 0===d?void 0:d.underlyingResource)&&void 0!==a?a:null),this._boundTexturesCache[this._activeChannel]=b,b&&(b._associatedChannel=this._activeChannel)}else c&&(e=!0,this._activateCurrentTexture());return f&&!c&&this._bindSamplerUniformToChannel(b._associatedChannel,this._activeChannel),e},a.prototype._bindTexture=function(a,b,c){if(void 0!==a){b&&(b._associatedChannel=a),this._activeChannel=a;c=b?this._getTextureTarget(b):this._gl.TEXTURE_2D;this._bindTextureDirectly(c,b)}},a.prototype.unbindAllTextures=function(){for(var a=0;a1&&(this._bindTextureDirectly(this._gl.TEXTURE_3D,null),this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY,null))},a.prototype.setTexture=function(a,b,c,d){void 0!==a&&(b&&(this._boundUniforms[a]=b),this._setTexture(a,c))},a.prototype._bindSamplerUniformToChannel=function(a,b){a=this._boundUniforms[a];a&&a._currentState!==b&&(this._gl.uniform1i(a,b),a._currentState=b)},a.prototype._getTextureWrapMode=function(a){switch(a){case l.a.TEXTURE_WRAP_ADDRESSMODE:return this._gl.REPEAT;case l.a.TEXTURE_CLAMP_ADDRESSMODE:return this._gl.CLAMP_TO_EDGE;case l.a.TEXTURE_MIRROR_ADDRESSMODE:return this._gl.MIRRORED_REPEAT}return this._gl.REPEAT},a.prototype._setTexture=function(a,b,c,d,e){if(void 0===c&&(c=!1),void 0===d&&(d=!1),void 0===e&&"",!b)return null!=this._boundTexturesCache[a]&&(this._activeChannel=a,this._bindTextureDirectly(this._gl.TEXTURE_2D,null),this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP,null),this.webGLVersion>1&&(this._bindTextureDirectly(this._gl.TEXTURE_3D,null),this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY,null))),!1;if(b.video)this._activeChannel=a,b.update();else if(b.delayLoadState===l.a.DELAYLOADSTATE_NOTLOADED)return b.delayLoad(),!1;e=d?b.depthStencilTexture:b.isReady()?b.getInternalTexture():b.isCube?this.emptyCubeTexture:b.is3D?this.emptyTexture3D:b.is2DArray?this.emptyTexture2DArray:this.emptyTexture,!c&&e&&(e._associatedChannel=a);d=!0;this._boundTexturesCache[a]===e&&(c||this._bindSamplerUniformToChannel(e._associatedChannel,a),d=!1),this._activeChannel=a;a=this._getTextureTarget(e);if(d&&this._bindTextureDirectly(a,e,c),e&&!e.isMultiview){if(e.isCube&&e._cachedCoordinatesMode!==b.coordinatesMode){e._cachedCoordinatesMode=b.coordinatesMode;d=b.coordinatesMode!==l.a.TEXTURE_CUBIC_MODE&&b.coordinatesMode!==l.a.TEXTURE_SKYBOX_MODE?l.a.TEXTURE_WRAP_ADDRESSMODE:l.a.TEXTURE_CLAMP_ADDRESSMODE;b.wrapU=d,b.wrapV=d}e._cachedWrapU!==b.wrapU&&(e._cachedWrapU=b.wrapU,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_S,this._getTextureWrapMode(b.wrapU),e)),e._cachedWrapV!==b.wrapV&&(e._cachedWrapV=b.wrapV,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_T,this._getTextureWrapMode(b.wrapV),e)),e.is3D&&e._cachedWrapR!==b.wrapR&&(e._cachedWrapR=b.wrapR,this._setTextureParameterInteger(a,this._gl.TEXTURE_WRAP_R,this._getTextureWrapMode(b.wrapR),e)),this._setAnisotropicLevel(a,e,b.anisotropicFilteringLevel)}return!0},a.prototype.setTextureArray=function(a,b,c,d){if(void 0!==a&&b){this._textureUnits&&this._textureUnits.length===c.length||(this._textureUnits=new Int32Array(c.length));for(d=0;d=this._caps.maxVertexAttribs||!this._vertexAttribArraysEnabled[a]||this.disableAttributeByIndex(a)}},a.prototype.releaseEffects=function(){for(var a in this._compiledEffects){var b=this._compiledEffects[a].getPipelineContext();this._deletePipelineContext(b)}this._compiledEffects={}},a.prototype.dispose=function(){var a;this.stopRenderLoop(),this.onBeforeTextureInitObservable&&this.onBeforeTextureInitObservable.clear(),this._emptyTexture&&(this._releaseTexture(this._emptyTexture),this._emptyTexture=null),this._emptyCubeTexture&&(this._releaseTexture(this._emptyCubeTexture),this._emptyCubeTexture=null),this._dummyFramebuffer&&this._gl.deleteFramebuffer(this._dummyFramebuffer),this.releaseEffects(),null===(a=this.releaseComputeEffects)||void 0===a||a.call(this),this.unbindAllAttributes(),this._boundUniforms=[],Object(o.e)()&&this._renderingCanvas&&(this._doNotHandleContextLost||(this._renderingCanvas.removeEventListener("webglcontextlost",this._onContextLost),this._renderingCanvas.removeEventListener("webglcontextrestored",this._onContextRestored)),window.removeEventListener("resize",this._checkForMobile)),this._workingCanvas=null,this._workingContext=null,this._currentBufferPointers=[],this._renderingCanvas=null,this._currentProgram=null,this._boundRenderFunction=null,f.a.ResetCache();for(var a=0,b=this._activeRequests;a1?this._caps.colorBufferFloat:this._canRenderToFramebuffer(l.a.TEXTURETYPE_FLOAT)},a.prototype._canRenderToHalfFloatFramebuffer=function(){return this._webGLVersion>1?this._caps.colorBufferFloat:this._canRenderToFramebuffer(l.a.TEXTURETYPE_HALF_FLOAT)},a.prototype._canRenderToFramebuffer=function(a){for(var b=this._gl;b.getError()!==b.NO_ERROR;);var c=!0,d=b.createTexture();b.bindTexture(b.TEXTURE_2D,d),b.texImage2D(b.TEXTURE_2D,0,this._getRGBABufferInternalSizedFormat(a),1,1,0,b.RGBA,this._getWebGLTextureType(a),null),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);a=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,a),b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,d,0);var e=b.checkFramebufferStatus(b.FRAMEBUFFER);if((c=(c=c&&e===b.FRAMEBUFFER_COMPLETE)&&b.getError()===b.NO_ERROR)&&(b.clear(b.COLOR_BUFFER_BIT),c=c&&b.getError()===b.NO_ERROR),c){b.bindFramebuffer(b.FRAMEBUFFER,null);e=b.RGBA;var f=b.UNSIGNED_BYTE,g=new Uint8Array(4);b.readPixels(0,0,1,1,e,f,g),c=c&&b.getError()===b.NO_ERROR}for(b.deleteTexture(d),b.deleteFramebuffer(a),b.bindFramebuffer(b.FRAMEBUFFER,null);!c&&b.getError()!==b.NO_ERROR;);return c},a.prototype._getWebGLTextureType=function(a){if(1===this._webGLVersion){switch(a){case l.a.TEXTURETYPE_FLOAT:return this._gl.FLOAT;case l.a.TEXTURETYPE_HALF_FLOAT:return this._gl.HALF_FLOAT_OES;case l.a.TEXTURETYPE_UNSIGNED_BYTE:return this._gl.UNSIGNED_BYTE;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.UNSIGNED_SHORT_4_4_4_4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.UNSIGNED_SHORT_5_5_5_1;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.UNSIGNED_SHORT_5_6_5}return this._gl.UNSIGNED_BYTE}switch(a){case l.a.TEXTURETYPE_BYTE:return this._gl.BYTE;case l.a.TEXTURETYPE_UNSIGNED_BYTE:return this._gl.UNSIGNED_BYTE;case l.a.TEXTURETYPE_SHORT:return this._gl.SHORT;case l.a.TEXTURETYPE_UNSIGNED_SHORT:return this._gl.UNSIGNED_SHORT;case l.a.TEXTURETYPE_INT:return this._gl.INT;case l.a.TEXTURETYPE_UNSIGNED_INTEGER:return this._gl.UNSIGNED_INT;case l.a.TEXTURETYPE_FLOAT:return this._gl.FLOAT;case l.a.TEXTURETYPE_HALF_FLOAT:return this._gl.HALF_FLOAT;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.UNSIGNED_SHORT_4_4_4_4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.UNSIGNED_SHORT_5_5_5_1;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.UNSIGNED_SHORT_5_6_5;case l.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:return this._gl.UNSIGNED_INT_2_10_10_10_REV;case l.a.TEXTURETYPE_UNSIGNED_INT_24_8:return this._gl.UNSIGNED_INT_24_8;case l.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:return this._gl.UNSIGNED_INT_10F_11F_11F_REV;case l.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:return this._gl.UNSIGNED_INT_5_9_9_9_REV;case l.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV:return this._gl.FLOAT_32_UNSIGNED_INT_24_8_REV}return this._gl.UNSIGNED_BYTE},a.prototype._getInternalFormat=function(a,b){void 0===b&&(b=!1);var c=b?this._gl.SRGB8_ALPHA8:this._gl.RGBA;switch(a){case l.a.TEXTUREFORMAT_ALPHA:c=this._gl.ALPHA;break;case l.a.TEXTUREFORMAT_LUMINANCE:c=this._gl.LUMINANCE;break;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:c=this._gl.LUMINANCE_ALPHA;break;case l.a.TEXTUREFORMAT_RED:c=this._gl.RED;break;case l.a.TEXTUREFORMAT_RG:c=this._gl.RG;break;case l.a.TEXTUREFORMAT_RGB:c=b?this._gl.SRGB:this._gl.RGB;break;case l.a.TEXTUREFORMAT_RGBA:c=b?this._gl.SRGB8_ALPHA8:this._gl.RGBA}if(this._webGLVersion>1)switch(a){case l.a.TEXTUREFORMAT_RED_INTEGER:c=this._gl.RED_INTEGER;break;case l.a.TEXTUREFORMAT_RG_INTEGER:c=this._gl.RG_INTEGER;break;case l.a.TEXTUREFORMAT_RGB_INTEGER:c=this._gl.RGB_INTEGER;break;case l.a.TEXTUREFORMAT_RGBA_INTEGER:c=this._gl.RGBA_INTEGER}return c},a.prototype._getRGBABufferInternalSizedFormat=function(a,b,c){if(void 0===c&&(c=!1),1===this._webGLVersion){if(void 0!==b)switch(b){case l.a.TEXTUREFORMAT_ALPHA:return this._gl.ALPHA;case l.a.TEXTUREFORMAT_LUMINANCE:return this._gl.LUMINANCE;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:return this._gl.LUMINANCE_ALPHA;case l.a.TEXTUREFORMAT_RGB:return c?this._gl.SRGB:this._gl.RGB}return this._gl.RGBA}switch(a){case l.a.TEXTURETYPE_BYTE:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R8_SNORM;case l.a.TEXTUREFORMAT_RG:return this._gl.RG8_SNORM;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB8_SNORM;case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R8I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG8I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB8I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGBA8I;default:return this._gl.RGBA8_SNORM}case l.a.TEXTURETYPE_UNSIGNED_BYTE:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R8;case l.a.TEXTUREFORMAT_RG:return this._gl.RG8;case l.a.TEXTUREFORMAT_RGB:return c?this._gl.SRGB8:this._gl.RGB8;case l.a.TEXTUREFORMAT_RGBA:return c?this._gl.SRGB8_ALPHA8:this._gl.RGBA8;case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R8UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG8UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB8UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGBA8UI;case l.a.TEXTUREFORMAT_ALPHA:return this._gl.ALPHA;case l.a.TEXTUREFORMAT_LUMINANCE:return this._gl.LUMINANCE;case l.a.TEXTUREFORMAT_LUMINANCE_ALPHA:return this._gl.LUMINANCE_ALPHA;default:return this._gl.RGBA8}case l.a.TEXTURETYPE_SHORT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R16I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG16I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB16I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA16I}case l.a.TEXTURETYPE_UNSIGNED_SHORT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R16UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG16UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB16UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA16UI}case l.a.TEXTURETYPE_INT:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R32I;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG32I;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB32I;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA32I}case l.a.TEXTURETYPE_UNSIGNED_INTEGER:switch(b){case l.a.TEXTUREFORMAT_RED_INTEGER:return this._gl.R32UI;case l.a.TEXTUREFORMAT_RG_INTEGER:return this._gl.RG32UI;case l.a.TEXTUREFORMAT_RGB_INTEGER:return this._gl.RGB32UI;case l.a.TEXTUREFORMAT_RGBA_INTEGER:default:return this._gl.RGBA32UI}case l.a.TEXTURETYPE_FLOAT:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R32F;case l.a.TEXTUREFORMAT_RG:return this._gl.RG32F;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB32F;case l.a.TEXTUREFORMAT_RGBA:default:return this._gl.RGBA32F}case l.a.TEXTURETYPE_HALF_FLOAT:switch(b){case l.a.TEXTUREFORMAT_RED:return this._gl.R16F;case l.a.TEXTUREFORMAT_RG:return this._gl.RG16F;case l.a.TEXTUREFORMAT_RGB:return this._gl.RGB16F;case l.a.TEXTUREFORMAT_RGBA:default:return this._gl.RGBA16F}case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:return this._gl.RGB565;case l.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:return this._gl.R11F_G11F_B10F;case l.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:return this._gl.RGB9_E5;case l.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:return this._gl.RGBA4;case l.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:return this._gl.RGB5_A1;case l.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:switch(b){case l.a.TEXTUREFORMAT_RGBA:return this._gl.RGB10_A2;case l.a.TEXTUREFORMAT_RGBA_INTEGER:return this._gl.RGB10_A2UI;default:return this._gl.RGB10_A2}}return c?this._gl.SRGB8_ALPHA8:this._gl.RGBA8},a.prototype._getRGBAMultiSampleBufferFormat=function(a){return a===l.a.TEXTURETYPE_FLOAT?this._gl.RGBA32F:a===l.a.TEXTURETYPE_HALF_FLOAT?this._gl.RGBA16F:this._gl.RGBA8},a.prototype._loadFile=function(b,c,d,e,f,g){var h=this;b=a._FileToolsLoadFile(b,c,d,e,f,g);return this._activeRequests.push(b),b.onCompleteObservable.add(function(a){h._activeRequests.splice(h._activeRequests.indexOf(a),1)}),b},a._FileToolsLoadFile=function(a,b,c,d,e,f){throw Object(g.a)("FileTools")},a.prototype.readPixels=function(a,b,c,d,e,f){void 0===e&&(e=!0),void 0===f&&(f=!0);var g=e?4:3;e=e?this._gl.RGBA:this._gl.RGB;g=new Uint8Array(d*c*g);return f&&this.flushFramebuffer(),this._gl.readPixels(a,b,c,d,e,this._gl.UNSIGNED_BYTE,g),Promise.resolve(g)},Object.defineProperty(a,"IsSupportedAsync",{get:function(){return Promise.resolve(this.isSupported())},enumerable:!1,configurable:!0}),Object.defineProperty(a,"IsSupported",{get:function(){return this.isSupported()},enumerable:!1,configurable:!0}),a.isSupported=function(){if(null!==this._HasMajorPerformanceCaveat)return!this._HasMajorPerformanceCaveat;if(null===this._IsSupported)try{var a=this._createCanvas(1,1);a=a.getContext("webgl")||a.getContext("experimental-webgl");this._IsSupported=null!=a&&!!window.WebGLRenderingContext}catch(a){this._IsSupported=!1}return this._IsSupported},Object.defineProperty(a,"HasMajorPerformanceCaveat",{get:function(){if(null===this._HasMajorPerformanceCaveat)try{var a=this._createCanvas(1,1);a=a.getContext("webgl",{failIfMajorPerformanceCaveat:!0})||a.getContext("experimental-webgl",{failIfMajorPerformanceCaveat:!0});this._HasMajorPerformanceCaveat=!a}catch(a){this._HasMajorPerformanceCaveat=!1}return this._HasMajorPerformanceCaveat},enumerable:!1,configurable:!0}),a.CeilingPOT=function(a){return a--,a|=a>>1,a|=a>>2,a|=a>>4,a|=a>>8,a|=a>>16,++a},a.FloorPOT=function(a){return a|=a>>1,a|=a>>2,a|=a>>4,a|=a>>8,(a|=a>>16)-(a>>1)},a.NearestPOT=function(b){var c=a.CeilingPOT(b),d=a.FloorPOT(b);return c-b>b-d?d:c},a.GetExponentOfTwo=function(b,c,d){var e;switch(void 0===d&&(d=l.a.SCALEMODE_NEAREST),d){case l.a.SCALEMODE_FLOOR:e=a.FloorPOT(b);break;case l.a.SCALEMODE_NEAREST:e=a.NearestPOT(b);break;case l.a.SCALEMODE_CEILING:default:e=a.CeilingPOT(b)}return Math.min(e,c)},a.QueueNewFrame=function(a,b){return Object(o.e)()?(b||(b=window),b.requestPostAnimationFrame?b.requestPostAnimationFrame(a):b.requestAnimationFrame?b.requestAnimationFrame(a):b.msRequestAnimationFrame?b.msRequestAnimationFrame(a):b.webkitRequestAnimationFrame?b.webkitRequestAnimationFrame(a):b.mozRequestAnimationFrame?b.mozRequestAnimationFrame(a):b.oRequestAnimationFrame?b.oRequestAnimationFrame(a):window.setTimeout(a,16)):"undefined"!=typeof requestAnimationFrame?requestAnimationFrame(a):setTimeout(a,16)},a.prototype.getHostDocument=function(){return this._renderingCanvas&&this._renderingCanvas.ownerDocument?this._renderingCanvas.ownerDocument:document},a.prototype.getFontOffset=function(a){var b=document.createElement("span");b.innerHTML="Hg",b.setAttribute("style","font: "+a+" !important");a=document.createElement("div");a.style.display="inline-block",a.style.width="1px",a.style.height="0px",a.style.verticalAlign="bottom";var c=document.createElement("div");c.style.whiteSpace="nowrap",c.appendChild(b),c.appendChild(a),document.body.appendChild(c);var d=0,e=0;try{e=a.getBoundingClientRect().top-b.getBoundingClientRect().top,a.style.verticalAlign="baseline",d=a.getBoundingClientRect().top-b.getBoundingClientRect().top}finally{document.body.removeChild(c)}return{ascent:d,height:e,descent:e-d}},a.ExceptionList=[{key:"Chrome/63.0",capture:"63\.0\.3239\.(\d+)",captureConstraint:108,targets:["uniformBuffer"]},{key:"Firefox/58",capture:null,captureConstraint:null,targets:["uniformBuffer"]},{key:"Firefox/59",capture:null,captureConstraint:null,targets:["uniformBuffer"]},{key:"Chrome/72.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Chrome/73.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Chrome/74.+?Mobile",capture:null,captureConstraint:null,targets:["vao"]},{key:"Mac OS.+Chrome/71",capture:null,captureConstraint:null,targets:["vao"]},{key:"Mac OS.+Chrome/72",capture:null,captureConstraint:null,targets:["vao"]}],a._TextureLoaders=[],a.CollisionsEpsilon=.001,a._IsSupported=null,a._HasMajorPerformanceCaveat=null,a}()},function(a,b,c){c.d(b,"a",function(){return q});var d=c(2),e=c(3),f=c(44),g=c(12),h=c(6),i=c(0),j=c(30),k=c(7),l=c(10),m=c(26),n=c(61),o=c(96),p=c(1),q=function(a){function b(c,d,e,g){void 0===g&&(g=!0);c=a.call(this,c,e)||this;return c._position=i.e.Zero(),c._upVector=i.e.Up(),c.orthoLeft=null,c.orthoRight=null,c.orthoBottom=null,c.orthoTop=null,c.fov=.8,c.projectionPlaneTilt=0,c.minZ=1,c.maxZ=1e4,c.inertia=.9,c.mode=b.PERSPECTIVE_CAMERA,c.isIntermediate=!1,c.viewport=new n.a(0,0,1,1),c.layerMask=268435455,c.fovMode=b.FOVMODE_VERTICAL_FIXED,c.cameraRigMode=b.RIG_MODE_NONE,c.customRenderTargets=new Array,c.outputRenderTarget=null,c.onViewMatrixChangedObservable=new h.c,c.onProjectionMatrixChangedObservable=new h.c,c.onAfterCheckInputsObservable=new h.c,c.onRestoreStateObservable=new h.c,c.isRigCamera=!1,c._rigCameras=new Array,c._webvrViewMatrix=i.a.Identity(),c._skipRendering=!1,c._projectionMatrix=new i.a,c._postProcesses=new Array,c._activeMeshes=new f.a(256),c._globalPosition=i.e.Zero(),c._computedViewMatrix=i.a.Identity(),c._doNotComputeProjectionMatrix=!1,c._transformMatrix=i.a.Zero(),c._refreshFrustumPlanes=!0,c._absoluteRotation=i.b.Identity(),c._isCamera=!0,c._isLeftCamera=!1,c._isRightCamera=!1,c.getScene().addCamera(c),g&&!c.getScene().activeCamera&&(c.getScene().activeCamera=c),c.position=d,c}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this._position=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"upVector",{get:function(){return this._upVector},set:function(a){this._upVector=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"screenArea",{get:function(){var a,c=0,d=0;if(this.mode===b.PERSPECTIVE_CAMERA)this.fovMode===b.FOVMODE_VERTICAL_FIXED?(d=2*this.minZ*Math.tan(this.fov/2),c=this.getEngine().getAspectRatio(this)*d):d=(c=2*this.minZ*Math.tan(this.fov/2))/this.getEngine().getAspectRatio(this);else{var e=this.getEngine().getRenderWidth()/2,f=this.getEngine().getRenderHeight()/2;c=(null!==(a=this.orthoRight)&&void 0!==a?a:e)-(null!==(a=this.orthoLeft)&&void 0!==a?a:-e),d=(null!==(a=this.orthoTop)&&void 0!==a?a:f)-(null!==(e=this.orthoBottom)&&void 0!==e?e:-f)}return c*d},enumerable:!1,configurable:!0}),b.prototype.storeState=function(){return this._stateStored=!0,this._storedFov=this.fov,this},b.prototype._restoreStateValues=function(){return!!this._stateStored&&(this.fov=this._storedFov,!0)},b.prototype.restoreState=function(){return!!this._restoreStateValues()&&(this.onRestoreStateObservable.notifyObservers(this),!0)},b.prototype.getClassName=function(){return"Camera"},b.prototype.toString=function(a){var b="Name: "+this.name;if(b+=", type: "+this.getClassName(),this.animations)for(var c=0;c-1?(k.a.Error("You"re trying to reuse a post process not defined as reusable."),0):(null==b||b<0?this._postProcesses.push(a):null===this._postProcesses[b]?this._postProcesses[b]=a:this._postProcesses.splice(b,0,a),this._cascadePostProcessesToRigCams(),this._scene.prePassRenderer&&this._scene.prePassRenderer.markAsDirty(),this._postProcesses.indexOf(a))},b.prototype.detachPostProcess=function(a){a=this._postProcesses.indexOf(a);-1!==a&&(this._postProcesses[a]=null),this._scene.prePassRenderer&&this._scene.prePassRenderer.markAsDirty(),this._cascadePostProcessesToRigCams()},b.prototype.getWorldMatrix=function(){return this._isSynchronizedViewMatrix()||this.getViewMatrix(),this._worldMatrix},b.prototype._getViewMatrix=function(){return i.a.Identity()},b.prototype.getViewMatrix=function(a){return!a&&this._isSynchronizedViewMatrix()||(this.updateCache(),this._computedViewMatrix=this._getViewMatrix(),this._currentRenderId=this.getScene().getRenderId(),this._childUpdateId++,this._refreshFrustumPlanes=!0,this._cameraRigParams&&this._cameraRigParams.vrPreViewMatrix&&this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix,this._computedViewMatrix),this.parent&&this.parent.onViewMatrixChangedObservable&&this.parent.onViewMatrixChangedObservable.notifyObservers(this.parent),this.onViewMatrixChangedObservable.notifyObservers(this),this._computedViewMatrix.invertToRef(this._worldMatrix)),this._computedViewMatrix},b.prototype.freezeProjectionMatrix=function(a){this._doNotComputeProjectionMatrix=!0,void 0!==a&&(this._projectionMatrix=a)},b.prototype.unfreezeProjectionMatrix=function(){this._doNotComputeProjectionMatrix=!1},b.prototype.getProjectionMatrix=function(a){if(this._doNotComputeProjectionMatrix||!a&&this._isSynchronizedProjectionMatrix())return this._projectionMatrix;this._cache.mode=this.mode,this._cache.minZ=this.minZ,this._cache.maxZ=this.maxZ,this._refreshFrustumPlanes=!0;a=this.getEngine();var c=this.getScene();if(this.mode===b.PERSPECTIVE_CAMERA){this._cache.fov=this.fov,this._cache.fovMode=this.fovMode,this._cache.aspectRatio=a.getAspectRatio(this),this._cache.projectionPlaneTilt=this.projectionPlaneTilt,this.minZ<=0&&(this.minZ=.1);var d=a.useReverseDepthBuffer;(c.useRightHandedSystem?i.a.PerspectiveFovRHToRef:i.a.PerspectiveFovLHToRef)(this.fov,a.getAspectRatio(this),d?this.maxZ:this.minZ,d?this.minZ:this.maxZ,this._projectionMatrix,this.fovMode===b.FOVMODE_VERTICAL_FIXED,a.isNDCHalfZRange,this.projectionPlaneTilt)}else{d=a.getRenderWidth()/2;var e=a.getRenderHeight()/2;c.useRightHandedSystem?i.a.OrthoOffCenterRHToRef(null!==(c=this.orthoLeft)&&void 0!==c?c:-d,null!==(c=this.orthoRight)&&void 0!==c?c:d,null!==(c=this.orthoBottom)&&void 0!==c?c:-e,null!==(c=this.orthoTop)&&void 0!==c?c:e,this.minZ,this.maxZ,this._projectionMatrix,a.isNDCHalfZRange):i.a.OrthoOffCenterLHToRef(null!==(c=this.orthoLeft)&&void 0!==c?c:-d,null!==(c=this.orthoRight)&&void 0!==c?c:d,null!==(c=this.orthoBottom)&&void 0!==c?c:-e,null!==(d=this.orthoTop)&&void 0!==d?d:e,this.minZ,this.maxZ,this._projectionMatrix,a.isNDCHalfZRange),this._cache.orthoLeft=this.orthoLeft,this._cache.orthoRight=this.orthoRight,this._cache.orthoBottom=this.orthoBottom,this._cache.orthoTop=this.orthoTop,this._cache.renderWidth=a.getRenderWidth(),this._cache.renderHeight=a.getRenderHeight()}return this.onProjectionMatrixChangedObservable.notifyObservers(this),this._projectionMatrix},b.prototype.getTransformationMatrix=function(){return this._computedViewMatrix.multiplyToRef(this._projectionMatrix,this._transformMatrix),this._transformMatrix},b.prototype._updateFrustumPlanes=function(){this._refreshFrustumPlanes&&(this.getTransformationMatrix(),this._frustumPlanes?o.a.GetPlanesToRef(this._transformMatrix,this._frustumPlanes):this._frustumPlanes=o.a.GetPlanes(this._transformMatrix),this._refreshFrustumPlanes=!1)},b.prototype.isInFrustum=function(a,b){if(void 0===b&&(b=!1),this._updateFrustumPlanes(),b&&this.rigCameras.length>0){var c=!1;return this.rigCameras.forEach(function(b){b._updateFrustumPlanes(),c=c||a.isInFrustum(b._frustumPlanes)}),c}return a.isInFrustum(this._frustumPlanes)},b.prototype.isCompletelyInFrustum=function(a){return this._updateFrustumPlanes(),a.isCompletelyInFrustum(this._frustumPlanes)},b.prototype.getForwardRay=function(a,b,c){throw void 0===a&&100,Object(m.a)("Ray")},b.prototype.getForwardRayToRef=function(a,b,c,d){throw void 0===b&&100,Object(m.a)("Ray")},b.prototype.dispose=function(c,d){for(void 0===d&&(d=!1),this.onViewMatrixChangedObservable.clear(),this.onProjectionMatrixChangedObservable.clear(),this.onAfterCheckInputsObservable.clear(),this.onRestoreStateObservable.clear(),this.inputs&&this.inputs.clear(),this.getScene().stopAnimation(this),this.getScene().removeCamera(this);this._rigCameras.length>0;){var e=this._rigCameras.pop();e&&e.dispose()}if(this._parentContainer){e=this._parentContainer.cameras.indexOf(this);e>-1&&this._parentContainer.cameras.splice(e,1),this._parentContainer=null}if(this._rigPostProcess)this._rigPostProcess.dispose(this),this._rigPostProcess=null,this._postProcesses=[];else if(this.cameraRigMode!==b.RIG_MODE_NONE)this._rigPostProcess=null,this._postProcesses=[];else for(e=this._postProcesses.length;--e>=0;){var f=this._postProcesses[e];f&&f.dispose(this)}for(e=this.customRenderTargets.length;--e>=0;)this.customRenderTargets[e].dispose();this.customRenderTargets=[],this._activeMeshes.dispose(),a.prototype.dispose.call(this,c,d)},Object.defineProperty(b.prototype,"isLeftCamera",{get:function(){return this._isLeftCamera},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isRightCamera",{get:function(){return this._isRightCamera},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"leftCamera",{get:function(){return this._rigCameras.length<1?null:this._rigCameras[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightCamera",{get:function(){return this._rigCameras.length<2?null:this._rigCameras[1]},enumerable:!1,configurable:!0}),b.prototype.getLeftTarget=function(){return this._rigCameras.length<1?null:this._rigCameras[0].getTarget()},b.prototype.getRightTarget=function(){return this._rigCameras.length<2?null:this._rigCameras[1].getTarget()},b.prototype.setCameraRigMode=function(a,c){if(this.cameraRigMode!==a){for(;this._rigCameras.length>0;){var d=this._rigCameras.pop();d&&d.dispose()}if(this.cameraRigMode=a,this._cameraRigParams={},this._cameraRigParams.interaxialDistance=c.interaxialDistance||.0637,this._cameraRigParams.stereoHalfAngle=g.b.ToRadians(this._cameraRigParams.interaxialDistance/.0637),this.cameraRigMode!==b.RIG_MODE_NONE){d=this.createRigCamera(this.name+"_L",0);d&&(d._isLeftCamera=!0);a=this.createRigCamera(this.name+"_R",1);a&&(a._isRightCamera=!0),d&&a&&(this._rigCameras.push(d),this._rigCameras.push(a))}this._setRigMode(c),this._cascadePostProcessesToRigCams(),this.update()}},b.prototype._setRigMode=function(a){},b.prototype._getVRProjectionMatrix=function(){return i.a.PerspectiveFovLHToRef(this._cameraRigParams.vrMetrics.aspectRatioFov,this._cameraRigParams.vrMetrics.aspectRatio,this.minZ,this.maxZ,this._cameraRigParams.vrWorkMatrix,!0,this.getEngine().isNDCHalfZRange),this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix,this._projectionMatrix),this._projectionMatrix},b.prototype._updateCameraRotationMatrix=function(){},b.prototype._updateWebVRCameraRotationMatrix=function(){},b.prototype._getWebVRProjectionMatrix=function(){return i.a.Identity()},b.prototype._getWebVRViewMatrix=function(){return i.a.Identity()},b.prototype.setCameraRigParameter=function(a,b){this._cameraRigParams||(this._cameraRigParams={}),this._cameraRigParams[a]=b,"interaxialDistance"===a&&(this._cameraRigParams.stereoHalfAngle=g.b.ToRadians(b/.0637))},b.prototype.createRigCamera=function(a,b){return null},b.prototype._updateRigCameras=function(){for(var a=0;a=1)&&(this.needAlphaBlending()||a.visibility<1||a.hasVertexAlpha)},a.prototype.needAlphaTesting=function(){return!!this._forceAlphaTest},a.prototype._shouldTurnAlphaTestOn=function(a){return!this.needAlphaBlendingForMesh(a)&&this.needAlphaTesting()},a.prototype.getAlphaTestTexture=function(){return null},a.prototype.markDirty=function(){for(var a=0,b=this.getScene().meshes;a-1&&this._parentContainer.materials.splice(d,1),this._parentContainer=null}if(!0!==c)if(this.meshMap)for(d in this.meshMap)(c=this.meshMap[d])&&(c.material=null,this.releaseVertexArrayObject(c,a));else for(d=0,b=b.meshes;d100&&(this.soft=!0),this._physicsEngine=this._scene.getPhysicsEngine(),this._physicsEngine?(this.object.rotationQuaternion||(this.object.rotation?this.object.rotationQuaternion=f.b.RotationYawPitchRoll(this.object.rotation.y,this.object.rotation.x,this.object.rotation.z):this.object.rotationQuaternion=new f.b),this._options.mass=void 0===c.mass?0:c.mass,this._options.friction=void 0===c.friction?.2:c.friction,this._options.restitution=void 0===c.restitution?.2:c.restitution,this.soft&&(this._options.mass=this._options.mass>0?this._options.mass:1,this._options.pressure=void 0===c.pressure?200:c.pressure,this._options.stiffness=void 0===c.stiffness?1:c.stiffness,this._options.velocityIterations=void 0===c.velocityIterations?20:c.velocityIterations,this._options.positionIterations=void 0===c.positionIterations?20:c.positionIterations,this._options.fixedPoints=void 0===c.fixedPoints?0:c.fixedPoints,this._options.margin=void 0===c.margin?0:c.margin,this._options.damping=void 0===c.damping?0:c.damping,this._options.path=void 0===c.path?null:c.path,this._options.shape=void 0===c.shape?null:c.shape),this._joints=[],!this.object.parent||this._options.ignoreParent?this._init():this.object.parent.physicsImpostor&&d.a.Warn("You must affect impostors to children before affecting impostor to parent.")):d.a.Error("Physics not enabled. Please use scene.enablePhysics(...) before creating impostors."))):d.a.Error("No object was provided. A physics object is obligatory")}return Object.defineProperty(a.prototype,"isDisposed",{get:function(){return this._isDisposed},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mass",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyMass(this):0},set:function(a){this.setMass(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"friction",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyFriction(this):0},set:function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyFriction(this,a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"restitution",{get:function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getBodyRestitution(this):0},set:function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyRestitution(this,a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"pressure",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.setBodyPressure?a.getBodyPressure(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyPressure&&b.setBodyPressure(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stiffness",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyStiffness?a.getBodyStiffness(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyStiffness&&b.setBodyStiffness(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"velocityIterations",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyVelocityIterations?a.getBodyVelocityIterations(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyVelocityIterations&&b.setBodyVelocityIterations(this,a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"positionIterations",{get:function(){if(!this._physicsEngine)return 0;var a=this._physicsEngine.getPhysicsPlugin();return a.getBodyPositionIterations?a.getBodyPositionIterations(this):0},set:function(a){if(this._physicsEngine){var b=this._physicsEngine.getPhysicsPlugin();b.setBodyPositionIterations&&b.setBodyPositionIterations(this,a)}},enumerable:!1,configurable:!0}),a.prototype._init=function(){this._physicsEngine&&(this._physicsEngine.removeImpostor(this),this.physicsBody=null,this._parent=this._parent||this._getPhysicsParent(),this._isDisposed||this.parent&&!this._options.ignoreParent||this._physicsEngine.addImpostor(this))},a.prototype._getPhysicsParent=function(){return this.object.parent instanceof g.a?this.object.parent.physicsImpostor:null},a.prototype.isBodyInitRequired=function(){return this._bodyUpdateRequired||!this._physicsBody&&!this._parent},a.prototype.setScalingUpdated=function(){this.forceUpdate()},a.prototype.forceUpdate=function(){this._init(),this.parent&&!this._options.ignoreParent&&this.parent.forceUpdate()},Object.defineProperty(a.prototype,"physicsBody",{get:function(){return this._parent&&!this._options.ignoreParent?this._parent.physicsBody:this._physicsBody},set:function(a){this._physicsBody&&this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().removePhysicsBody(this),this._physicsBody=a,this.resetUpdateFlags()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"parent",{get:function(){return!this._options.ignoreParent&&this._parent?this._parent:null},set:function(a){this._parent=a},enumerable:!1,configurable:!0}),a.prototype.resetUpdateFlags=function(){this._bodyUpdateRequired=!1},a.prototype.getObjectExtendSize=function(){if(this.object.getBoundingInfo){var b=this.object.rotationQuaternion,c=this.object.scaling.clone();this.object.rotationQuaternion=a.IDENTITY_QUATERNION;var d=this.object.computeWorldMatrix&&this.object.computeWorldMatrix(!0);d&&d.decompose(c,void 0,void 0);d=this.object.getBoundingInfo().boundingBox.extendSize.scale(2).multiplyInPlace(c);return d.x=Math.abs(d.x),d.y=Math.abs(d.y),d.z=Math.abs(d.z),this.object.rotationQuaternion=b,this.object.computeWorldMatrix&&this.object.computeWorldMatrix(!0),d}return a.DEFAULT_OBJECT_SIZE},a.prototype.getObjectCenter=function(){return this.object.getBoundingInfo?this.object.getBoundingInfo().boundingBox.centerWorld:this.object.position},a.prototype.getParam=function(a){return this._options[a]},a.prototype.setParam=function(a,b){this._options[a]=b,this._bodyUpdateRequired=!0},a.prototype.setMass=function(a){this.getParam("mass")!==a&&this.setParam("mass",a),this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setBodyMass(this,a)},a.prototype.getLinearVelocity=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getLinearVelocity(this):f.e.Zero()},a.prototype.setLinearVelocity=function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setLinearVelocity(this,a)},a.prototype.getAngularVelocity=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getAngularVelocity(this):f.e.Zero()},a.prototype.setAngularVelocity=function(a){this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().setAngularVelocity(this,a)},a.prototype.executeNativeFunction=function(a){this._physicsEngine&&a(this._physicsEngine.getPhysicsPlugin().world,this.physicsBody)},a.prototype.registerBeforePhysicsStep=function(a){this._onBeforePhysicsStepCallbacks.push(a)},a.prototype.unregisterBeforePhysicsStep=function(a){a=this._onBeforePhysicsStepCallbacks.indexOf(a);a>-1?this._onBeforePhysicsStepCallbacks.splice(a,1):d.a.Warn("Function to remove was not found")},a.prototype.registerAfterPhysicsStep=function(a){this._onAfterPhysicsStepCallbacks.push(a)},a.prototype.unregisterAfterPhysicsStep=function(a){a=this._onAfterPhysicsStepCallbacks.indexOf(a);a>-1?this._onAfterPhysicsStepCallbacks.splice(a,1):d.a.Warn("Function to remove was not found")},a.prototype.registerOnPhysicsCollide=function(a,b){a=a instanceof Array?a:[a];this._onPhysicsCollideCallbacks.push({callback:b,otherImpostors:a})},a.prototype.unregisterOnPhysicsCollide=function(a,b){var c=a instanceof Array?a:[a];a=-1;this._onPhysicsCollideCallbacks.some(function(a,d){if(a.callback===b&&a.otherImpostors.length===c.length){a=a.otherImpostors.every(function(a){return c.indexOf(a)>-1});return a&&(a=d),a}return!1})?this._onPhysicsCollideCallbacks.splice(a,1):d.a.Warn("Function to remove was not found")},a.prototype.getParentsRotation=function(){var a=this.object.parent;for(this._tmpQuat.copyFromFloats(0,0,0,1);a;)a.rotationQuaternion?this._tmpQuat2.copyFrom(a.rotationQuaternion):f.b.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,a.rotation.z,this._tmpQuat2),this._tmpQuat.multiplyToRef(this._tmpQuat2,this._tmpQuat),a=a.parent;return this._tmpQuat},a.prototype.applyForce=function(a,b){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().applyForce(this,a,b),this},a.prototype.applyImpulse=function(a,b){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().applyImpulse(this,a,b),this},a.prototype.createJoint=function(a,b,c){b=new h.e(b,c);return this.addJoint(a,b),this},a.prototype.addJoint=function(a,b){return this._joints.push({otherImpostor:a,joint:b}),this._physicsEngine&&this._physicsEngine.addJoint(this,a,b),this},a.prototype.addAnchor=function(a,b,c,d,e){if(!this._physicsEngine)return this;var f=this._physicsEngine.getPhysicsPlugin();return f.appendAnchor?(this._physicsEngine&&f.appendAnchor(this,a,b,c,d,e),this):this},a.prototype.addHook=function(a,b,c,d){if(!this._physicsEngine)return this;var e=this._physicsEngine.getPhysicsPlugin();return e.appendAnchor?(this._physicsEngine&&e.appendHook(this,a,b,c,d),this):this},a.prototype.sleep=function(){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().sleepBody(this),this},a.prototype.wakeUp=function(){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().wakeUpBody(this),this},a.prototype.clone=function(b){return b?new a(b,this.type,this._options,this._scene):null},a.prototype.dispose=function(){var a=this;this._physicsEngine&&(this._joints.forEach(function(b){a._physicsEngine&&a._physicsEngine.removeJoint(a,b.otherImpostor,b.joint)}),this._physicsEngine.removeImpostor(this),this.parent&&this.parent.forceUpdate(),this._isDisposed=!0)},a.prototype.setDeltaPosition=function(a){this._deltaPosition.copyFrom(a)},a.prototype.setDeltaRotation=function(a){this._deltaRotation||(this._deltaRotation=new f.b),this._deltaRotation.copyFrom(a),this._deltaRotationConjugated=this._deltaRotation.conjugate()},a.prototype.getBoxSizeToRef=function(a){return this._physicsEngine&&this._physicsEngine.getPhysicsPlugin().getBoxSizeToRef(this,a),this},a.prototype.getRadius=function(){return this._physicsEngine?this._physicsEngine.getPhysicsPlugin().getRadius(this):0},a.prototype.syncBoneWithImpostor=function(b,c,d,e,f){var g=a._tmpVecs[0],j=this.object;if(j.rotationQuaternion)if(f){var h=a._tmpQuat;j.rotationQuaternion.multiplyToRef(f,h),b.setRotationQuaternion(h,i.c.WORLD,c)}else b.setRotationQuaternion(j.rotationQuaternion,i.c.WORLD,c);g.x=0,g.y=0,g.z=0,d&&(g.x=d.x,g.y=d.y,g.z=d.z,b.getDirectionToRef(g,c,g),null==e&&(e=d.length()),g.x*=e,g.y*=e,g.z*=e),b.getParent()?(g.addInPlace(j.getAbsolutePosition()),b.setAbsolutePosition(g,c)):(c.setAbsolutePosition(j.getAbsolutePosition()),c.position.x-=g.x,c.position.y-=g.y,c.position.z-=g.z)},a.prototype.syncImpostorWithBone=function(b,c,d,e,f,g){var j=this.object;if(j.rotationQuaternion)if(f){var h=a._tmpQuat;b.getRotationQuaternionToRef(i.c.WORLD,c,h),h.multiplyToRef(f,j.rotationQuaternion)}else b.getRotationQuaternionToRef(i.c.WORLD,c,j.rotationQuaternion);h=a._tmpVecs[0];f=a._tmpVecs[1];g||((g=a._tmpVecs[2]).x=0,g.y=1,g.z=0),b.getDirectionToRef(g,c,f),b.getAbsolutePositionToRef(c,h),null==e&&d&&(e=d.length()),null!=e&&(h.x+=f.x*e,h.y+=f.y*e,h.z+=f.z*e),j.setAbsolutePosition(h)},a.DEFAULT_OBJECT_SIZE=new f.e(1,1,1),a.IDENTITY_QUATERNION=f.b.Identity(),a._tmpVecs=e.a.BuildArray(3,f.e.Zero),a._tmpQuat=f.b.Identity(),a.NoImpostor=0,a.SphereImpostor=1,a.BoxImpostor=2,a.PlaneImpostor=3,a.MeshImpostor=4,a.CapsuleImpostor=6,a.CylinderImpostor=7,a.ParticleImpostor=8,a.HeightmapImpostor=9,a.ConvexHullImpostor=10,a.CustomImpostor=100,a.RopeImpostor=101,a.ClothImpostor=102,a.SoftbodyImpostor=103,a}()},function(a,b,c){c.d(b,"b",function(){return x}),c.d(b,"a",function(){return y});var d=c(2),e=c(3),f=c(44),g=c(21),h=c(0),i=c(8),j=c(4),k=c(119),l=c(49),m=c(29);a=c(83);b=c(94);var n=c(15),o=c(11),p=c(10),q=c(20),r=c(5),s="uniform vec4 vEyePosition; uniform vec4 vDiffuseColor; #ifdef SPECULARTERM uniform vec4 vSpecularColor; #endif uniform vec3 vEmissiveColor; uniform vec3 vAmbientColor; uniform float visibility; #ifdef DIFFUSE uniform vec2 vDiffuseInfos; #endif #ifdef AMBIENT uniform vec2 vAmbientInfos; #endif #ifdef OPACITY uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #ifdef ALPHATEST uniform float alphaCutOff; #endif #if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(REFRACTION) uniform mat4 view; #endif #ifdef REFRACTION uniform vec4 vRefractionInfos; #ifndef REFRACTIONMAP_3D uniform mat4 refractionMatrix; #endif #ifdef REFRACTIONFRESNEL uniform vec4 refractionLeftColor; uniform vec4 refractionRightColor; #endif #if defined(USE_LOCAL_REFRACTIONMAP_CUBIC) && defined(REFRACTIONMAP_3D) uniform vec3 vRefractionPosition; uniform vec3 vRefractionSize; #endif #endif #if defined(SPECULAR) && defined(SPECULARTERM) uniform vec2 vSpecularInfos; #endif #ifdef DIFFUSEFRESNEL uniform vec4 diffuseLeftColor; uniform vec4 diffuseRightColor; #endif #ifdef OPACITYFRESNEL uniform vec4 opacityParts; #endif #ifdef EMISSIVEFRESNEL uniform vec4 emissiveLeftColor; uniform vec4 emissiveRightColor; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; #if defined(REFLECTIONMAP_PLANAR) || defined(REFLECTIONMAP_CUBIC) || defined(REFLECTIONMAP_PROJECTION) || defined(REFLECTIONMAP_EQUIRECTANGULAR) || defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_SKYBOX) uniform mat4 reflectionMatrix; #endif #ifndef REFLECTIONMAP_SKYBOX #if defined(USE_LOCAL_REFLECTIONMAP_CUBIC) && defined(REFLECTIONMAP_CUBIC) uniform vec3 vReflectionPosition; uniform vec3 vReflectionSize; #endif #endif #ifdef REFLECTIONFRESNEL uniform vec4 reflectionLeftColor; uniform vec4 reflectionRightColor; #endif #endif #ifdef DETAIL uniform vec4 vDetailInfos; #endif";r.a.IncludesShadersStore.defaultFragmentDeclaration=s;c(142),c(180);s="layout(std140,column_major) uniform; uniform Material { vec4 diffuseLeftColor; vec4 diffuseRightColor; vec4 opacityParts; vec4 reflectionLeftColor; vec4 reflectionRightColor; vec4 refractionLeftColor; vec4 refractionRightColor; vec4 emissiveLeftColor; vec4 emissiveRightColor; vec2 vDiffuseInfos; vec2 vAmbientInfos; vec2 vOpacityInfos; vec2 vReflectionInfos; vec3 vReflectionPosition; vec3 vReflectionSize; vec2 vEmissiveInfos; vec2 vLightmapInfos; vec2 vSpecularInfos; vec3 vBumpInfos; mat4 diffuseMatrix; mat4 ambientMatrix; mat4 opacityMatrix; mat4 reflectionMatrix; mat4 emissiveMatrix; mat4 lightmapMatrix; mat4 specularMatrix; mat4 bumpMatrix; vec2 vTangentSpaceParams; float pointSize; float alphaCutOff; mat4 refractionMatrix; vec4 vRefractionInfos; vec3 vRefractionPosition; vec3 vRefractionSize; vec4 vSpecularColor; vec3 vEmissiveColor; vec4 vDiffuseColor; vec3 vAmbientColor; vec4 vDetailInfos; mat4 detailMatrix; }; #include #include ";r.a.IncludesShadersStore.defaultUboDeclaration=s;c(190),c(191),c(149),c(64),c(150),c(151),c(181),c(152),c(182),c(192),c(153),c(120),c(121),c(154),c(155),c(122),c(127),c(143),c(193),c(109),c(156),c(194),c(157),c(183),c(158);s="#include<__decl__defaultFragment> #if defined(BUMP) || !defined(NORMAL) #extension GL_OES_standard_derivatives : enable #endif #include[SCENE_MRT_COUNT] #include #define CUSTOM_FRAGMENT_BEGIN #ifdef LOGARITHMICDEPTH #extension GL_EXT_frag_depth : enable #endif #define RECIPROCAL_PI2 0.15915494 varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include[1..7] #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse,_SAMPLERNAME_,diffuse) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_SAMPLERNAME_,lightmap) #ifdef REFRACTION #ifdef REFRACTIONMAP_3D uniform samplerCube refractionCubeSampler; #else uniform sampler2D refraction2DSampler; #endif #endif #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular,_SAMPLERNAME_,specular) #endif #include #ifdef REFLECTION #ifdef REFLECTIONMAP_3D uniform samplerCube reflectionCubeSampler; #else uniform sampler2D reflection2DSampler; #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #include #endif #include #include #include #include #include #include #include #define CUSTOM_FRAGMENT_DEFINITIONS void main(void) { #define CUSTOM_FRAGMENT_MAIN_BEGIN #include #include vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); vec4 baseColor=vec4(1.,1.,1.,1.); vec3 diffuseColor=vDiffuseColor.rgb; float alpha=vDiffuseColor.a; #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=normalize(-cross(dFdx(vPositionW),dFdy(vPositionW))); #endif #include #ifdef TWOSIDEDLIGHTING normalW=gl_FrontFacing ? normalW : -normalW; #endif #ifdef DIFFUSE baseColor=texture2D(diffuseSampler,vDiffuseUV+uvOffset); #if defined(ALPHATEST) && !defined(ALPHATEST_AFTERALLALPHACOMPUTATIONS) if (baseColor.a #ifdef VERTEXCOLOR baseColor.rgb*=vColor.rgb; #endif #ifdef DETAIL baseColor.rgb=baseColor.rgb*2.0*mix(0.5,detailColor.r,vDetailInfos.y); #endif #define CUSTOM_FRAGMENT_UPDATE_DIFFUSE vec3 baseAmbientColor=vec3(1.,1.,1.); #ifdef AMBIENT baseAmbientColor=texture2D(ambientSampler,vAmbientUV+uvOffset).rgb*vAmbientInfos.y; #endif #define CUSTOM_FRAGMENT_BEFORE_LIGHTS #ifdef SPECULARTERM float glossiness=vSpecularColor.a; vec3 specularColor=vSpecularColor.rgb; #ifdef SPECULAR vec4 specularMapColor=texture2D(specularSampler,vSpecularUV+uvOffset); specularColor=specularMapColor.rgb; #ifdef GLOSSINESS glossiness=glossiness*specularMapColor.a; #endif #endif #else float glossiness=0.; #endif vec3 diffuseBase=vec3(0.,0.,0.); lightingInfo info; #ifdef SPECULARTERM vec3 specularBase=vec3(0.,0.,0.); #endif float shadow=1.; #ifdef LIGHTMAP vec4 lightmapColor=texture2D(lightmapSampler,vLightmapUV+uvOffset); #ifdef RGBDLIGHTMAP lightmapColor.rgb=fromRGBD(lightmapColor); #endif lightmapColor.rgb*=vLightmapInfos.y; #endif #include[0..maxSimultaneousLights] vec4 refractionColor=vec4(0.,0.,0.,1.); #ifdef REFRACTION vec3 refractionVector=normalize(refract(-viewDirectionW,normalW,vRefractionInfos.y)); #ifdef REFRACTIONMAP_3D #ifdef USE_LOCAL_REFRACTIONMAP_CUBIC refractionVector=parallaxCorrectNormal(vPositionW,refractionVector,vRefractionSize,vRefractionPosition); #endif refractionVector.y=refractionVector.y*vRefractionInfos.w; if (dot(refractionVector,viewDirectionW)<1.0) { refractionColor=textureCube(refractionCubeSampler,refractionVector); } #else vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*vRefractionInfos.z,1.0))); vec2 refractionCoords=vRefractionUVW.xy/vRefractionUVW.z; refractionCoords.y=1.0-refractionCoords.y; refractionColor=texture2D(refraction2DSampler,refractionCoords); #endif #ifdef RGBDREFRACTION refractionColor.rgb=fromRGBD(refractionColor); #endif #ifdef IS_REFRACTION_LINEAR refractionColor.rgb=toGammaSpace(refractionColor.rgb); #endif refractionColor.rgb*=vRefractionInfos.x; #endif vec4 reflectionColor=vec4(0.,0.,0.,1.); #ifdef REFLECTION vec3 vReflectionUVW=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #ifdef REFLECTIONMAP_OPPOSITEZ vReflectionUVW.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D #ifdef ROUGHNESS float bias=vReflectionInfos.y; #ifdef SPECULARTERM #ifdef SPECULAR #ifdef GLOSSINESS bias*=(1.0-specularMapColor.a); #endif #endif #endif reflectionColor=textureCube(reflectionCubeSampler,vReflectionUVW,bias); #else reflectionColor=textureCube(reflectionCubeSampler,vReflectionUVW); #endif #else vec2 coords=vReflectionUVW.xy; #ifdef REFLECTIONMAP_PROJECTION coords/=vReflectionUVW.z; #endif coords.y=1.0-coords.y; reflectionColor=texture2D(reflection2DSampler,coords); #endif #ifdef RGBDREFLECTION reflectionColor.rgb=fromRGBD(reflectionColor); #endif #ifdef IS_REFLECTION_LINEAR reflectionColor.rgb=toGammaSpace(reflectionColor.rgb); #endif reflectionColor.rgb*=vReflectionInfos.x; #ifdef REFLECTIONFRESNEL float reflectionFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,reflectionRightColor.a,reflectionLeftColor.a); #ifdef REFLECTIONFRESNELFROMSPECULAR #ifdef SPECULARTERM reflectionColor.rgb*=specularColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #else reflectionColor.rgb*=reflectionLeftColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #endif #else reflectionColor.rgb*=reflectionLeftColor.rgb*(1.0-reflectionFresnelTerm)+reflectionFresnelTerm*reflectionRightColor.rgb; #endif #endif #endif #ifdef REFRACTIONFRESNEL float refractionFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,refractionRightColor.a,refractionLeftColor.a); refractionColor.rgb*=refractionLeftColor.rgb*(1.0-refractionFresnelTerm)+refractionFresnelTerm*refractionRightColor.rgb; #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vOpacityUV+uvOffset); #ifdef OPACITYRGB opacityMap.rgb=opacityMap.rgb*vec3(0.3,0.59,0.11); alpha*=(opacityMap.x+opacityMap.y+opacityMap.z)* vOpacityInfos.y; #else alpha*=opacityMap.a*vOpacityInfos.y; #endif #endif #ifdef VERTEXALPHA alpha*=vColor.a; #endif #ifdef OPACITYFRESNEL float opacityFresnelTerm=computeFresnelTerm(viewDirectionW,normalW,opacityParts.z,opacityParts.w); alpha+=opacityParts.x*(1.0-opacityFresnelTerm)+opacityFresnelTerm*opacityParts.y; #endif #ifdef ALPHATEST #ifdef ALPHATEST_AFTERALLALPHACOMPUTATIONS if (alpha #include #ifdef IMAGEPROCESSINGPOSTPROCESS color.rgb=toLinearSpace(color.rgb); #else #ifdef IMAGEPROCESSING color.rgb=toLinearSpace(color.rgb); color=applyImageProcessing(color); #endif #endif color.a*=visibility; #ifdef PREMULTIPLYALPHA color.rgb*=color.a; #endif #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR #ifdef PREPASS float writeGeometryInfo=color.a>0.4 ? 1.0 : 0.0; gl_FragData[0]=color; #ifdef PREPASS_POSITION gl_FragData[PREPASS_POSITION_INDEX]=vec4(vPositionW,writeGeometryInfo); #endif #ifdef PREPASS_VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[PREPASS_VELOCITY_INDEX]=vec4(velocity,0.0,writeGeometryInfo); #endif #ifdef PREPASS_IRRADIANCE gl_FragData[PREPASS_IRRADIANCE_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_DEPTH gl_FragData[PREPASS_DEPTH_INDEX]=vec4(vViewPos.z,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_NORMAL gl_FragData[PREPASS_NORMAL_INDEX]=vec4((view*vec4(normalW,0.0)).rgb,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT gl_FragData[PREPASS_ALBEDO_SQRT_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_REFLECTIVITY #if defined(SPECULAR) gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(specularMapColor.rgb,writeGeometryInfo); #else gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #endif #endif #if !defined(PREPASS) || defined(WEBGL2) gl_FragColor=color; #endif #if ORDER_INDEPENDENT_TRANSPARENCY if (fragDepth == nearestDepth) { frontColor.rgb+=color.rgb*color.a*alphaMultiplier; frontColor.a=1.0-alphaMultiplier*(1.0-color.a); } else { backColor+=color; } #endif } ";r.a.ShadersStore.defaultPixelShader=s;s=" uniform mat4 viewProjection; uniform mat4 view; #ifdef DIFFUSE uniform mat4 diffuseMatrix; uniform vec2 vDiffuseInfos; #endif #ifdef AMBIENT uniform mat4 ambientMatrix; uniform vec2 vAmbientInfos; #endif #ifdef OPACITY uniform mat4 opacityMatrix; uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; uniform mat4 emissiveMatrix; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; uniform mat4 lightmapMatrix; #endif #if defined(SPECULAR) && defined(SPECULARTERM) uniform vec2 vSpecularInfos; uniform mat4 specularMatrix; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform mat4 bumpMatrix; #endif #ifdef REFLECTION uniform mat4 reflectionMatrix; #endif #ifdef POINTSIZE uniform float pointSize; #endif #ifdef DETAIL uniform vec4 vDetailInfos; uniform mat4 detailMatrix; #endif";r.a.IncludesShadersStore.defaultVertexDeclaration=s;c(195),c(87),c(92),c(196),c(197),c(198),c(123),c(159),c(160),c(161),c(101),c(102),c(110),c(111),c(88),c(89),c(199),c(200),c(201),c(184),c(112),c(185),c(162);r.a.IncludesShadersStore.pointCloudVertex="#ifdef POINTSIZE gl_PointSize=pointSize; #endif";c(186);s="#include<__decl__defaultVertex> #define CUSTOM_VERTEX_BEGIN attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #ifdef TANGENT attribute vec4 tangent; #endif #ifdef UV1 attribute vec2 uv; #endif #include[2..7] #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include #include #include[1..7] #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap) #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular) #endif #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump) varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #include #define CUSTOM_VERTEX_DEFINITIONS void main(void) { #define CUSTOM_VERTEX_MAIN_BEGIN vec3 positionUpdated=position; #ifdef NORMAL vec3 normalUpdated=normal; #endif #ifdef TANGENT vec4 tangentUpdated=tangent; #endif #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=positionUpdated; #endif #define CUSTOM_VERTEX_UPDATE_POSITION #define CUSTOM_VERTEX_UPDATE_NORMAL #include #if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*finalPreviousWorld*vec4(positionUpdated,1.0); #endif #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vNormalW=normalUpdated/vec3(dot(normalWorld[0],normalWorld[0]),dot(normalWorld[1],normalWorld[1]),dot(normalWorld[2],normalWorld[2])); vNormalW=normalize(normalWorld*vNormalW); #else #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normalUpdated); #endif #endif #define CUSTOM_VERTEX_UPDATE_WORLDPOS #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif vPositionW=vec3(worldPos); #include #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(positionUpdated,0.0))); #endif #ifndef UV1 vec2 uvUpdated=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uvUpdated; #endif #include[2..7] #include(_DEFINENAME_,DIFFUSE,_VARYINGNAME_,Diffuse,_MATRIXNAME_,diffuse,_INFONAME_,DiffuseInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_MATRIXNAME_,emissive,_INFONAME_,EmissiveInfos.x) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_MATRIXNAME_,lightmap,_INFONAME_,LightmapInfos.x) #if defined(SPECULARTERM) #include(_DEFINENAME_,SPECULAR,_VARYINGNAME_,Specular,_MATRIXNAME_,specular,_INFONAME_,SpecularInfos.x) #endif #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_MATRIXNAME_,bump,_INFONAME_,BumpInfos.x) #include #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #include #include #define CUSTOM_VERTEX_MAIN_END } ";r.a.ShadersStore.defaultVertexShader=s;var t=c(1),u=c(74),v=c(100),w={effect:null,subMesh:null},x=function(a){function b(){var b=a.call(this)||this;return b.MAINUV1=!1,b.MAINUV2=!1,b.MAINUV3=!1,b.MAINUV4=!1,b.MAINUV5=!1,b.MAINUV6=!1,b.DIFFUSE=!1,b.DIFFUSEDIRECTUV=0,b.DETAIL=!1,b.DETAILDIRECTUV=0,b.DETAIL_NORMALBLENDMETHOD=0,b.AMBIENT=!1,b.AMBIENTDIRECTUV=0,b.OPACITY=!1,b.OPACITYDIRECTUV=0,b.OPACITYRGB=!1,b.REFLECTION=!1,b.EMISSIVE=!1,b.EMISSIVEDIRECTUV=0,b.SPECULAR=!1,b.SPECULARDIRECTUV=0,b.BUMP=!1,b.BUMPDIRECTUV=0,b.PARALLAX=!1,b.PARALLAXOCCLUSION=!1,b.SPECULAROVERALPHA=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.ALPHATEST=!1,b.DEPTHPREPASS=!1,b.ALPHAFROMDIFFUSE=!1,b.POINTSIZE=!1,b.FOG=!1,b.SPECULARTERM=!1,b.DIFFUSEFRESNEL=!1,b.OPACITYFRESNEL=!1,b.REFLECTIONFRESNEL=!1,b.REFRACTIONFRESNEL=!1,b.EMISSIVEFRESNEL=!1,b.FRESNEL=!1,b.NORMAL=!1,b.TANGENT=!1,b.UV1=!1,b.UV2=!1,b.UV3=!1,b.UV4=!1,b.UV5=!1,b.UV6=!1,b.VERTEXCOLOR=!1,b.VERTEXALPHA=!1,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.BONETEXTURE=!1,b.BONES_VELOCITY_ENABLED=!1,b.INSTANCES=!1,b.THIN_INSTANCES=!1,b.GLOSSINESS=!1,b.ROUGHNESS=!1,b.EMISSIVEASILLUMINATION=!1,b.LINKEMISSIVEWITHDIFFUSE=!1,b.REFLECTIONFRESNELFROMSPECULAR=!1,b.LIGHTMAP=!1,b.LIGHTMAPDIRECTUV=0,b.OBJECTSPACE_NORMALMAP=!1,b.USELIGHTMAPASSHADOWMAP=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFRACTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.INVERTCUBICMAP=!1,b.LOGARITHMICDEPTH=!1,b.REFRACTION=!1,b.REFRACTIONMAP_3D=!1,b.REFLECTIONOVERALPHA=!1,b.TWOSIDEDLIGHTING=!1,b.SHADOWFLOAT=!1,b.MORPHTARGETS=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_UV=!1,b.NUM_MORPH_INFLUENCERS=0,b.MORPHTARGETS_TEXTURE=!1,b.NONUNIFORMSCALING=!1,b.PREMULTIPLYALPHA=!1,b.ALPHATEST_AFTERALLALPHACOMPUTATIONS=!1,b.ALPHABLEND=!0,b.PREPASS=!1,b.PREPASS_IRRADIANCE=!1,b.PREPASS_IRRADIANCE_INDEX=-1,b.PREPASS_ALBEDO_SQRT=!1,b.PREPASS_ALBEDO_SQRT_INDEX=-1,b.PREPASS_DEPTH=!1,b.PREPASS_DEPTH_INDEX=-1,b.PREPASS_NORMAL=!1,b.PREPASS_NORMAL_INDEX=-1,b.PREPASS_POSITION=!1,b.PREPASS_POSITION_INDEX=-1,b.PREPASS_VELOCITY=!1,b.PREPASS_VELOCITY_INDEX=-1,b.PREPASS_REFLECTIVITY=!1,b.PREPASS_REFLECTIVITY_INDEX=-1,b.SCENE_MRT_COUNT=0,b.RGBDLIGHTMAP=!1,b.RGBDREFLECTION=!1,b.RGBDREFRACTION=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.MULTIVIEW=!1,b.ORDER_INDEPENDENT_TRANSPARENCY=!1,b.IS_REFLECTION_LINEAR=!1,b.IS_REFRACTION_LINEAR=!1,b.EXPOSURE=!1,b.rebuild(),b}return Object(d.d)(b,a),b.prototype.setReflectionMode=function(a){for(var b=0,c=["REFLECTIONMAP_CUBIC","REFLECTIONMAP_EXPLICIT","REFLECTIONMAP_PLANAR","REFLECTIONMAP_PROJECTION","REFLECTIONMAP_PROJECTION","REFLECTIONMAP_SKYBOX","REFLECTIONMAP_SPHERICAL","REFLECTIONMAP_EQUIRECTANGULAR","REFLECTIONMAP_EQUIRECTANGULAR_FIXED","REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED"];b0,f.REFLECTIONOVERALPHA=this._useReflectionOverAlpha,f.INVERTCUBICMAP=this._reflectionTexture.coordinatesMode===o.a.INVCUBIC_MODE,f.REFLECTIONMAP_3D=this._reflectionTexture.isCube,f.RGBDREFLECTION=this._reflectionTexture.isRGBD,f.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!this._reflectionTexture.invertZ:this._reflectionTexture.invertZ,this._reflectionTexture.coordinatesMode){case o.a.EXPLICIT_MODE:f.setReflectionMode("REFLECTIONMAP_EXPLICIT");break;case o.a.PLANAR_MODE:f.setReflectionMode("REFLECTIONMAP_PLANAR");break;case o.a.PROJECTION_MODE:f.setReflectionMode("REFLECTIONMAP_PROJECTION");break;case o.a.SKYBOX_MODE:f.setReflectionMode("REFLECTIONMAP_SKYBOX");break;case o.a.SPHERICAL_MODE:f.setReflectionMode("REFLECTIONMAP_SPHERICAL");break;case o.a.EQUIRECTANGULAR_MODE:f.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR");break;case o.a.FIXED_EQUIRECTANGULAR_MODE:f.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR_FIXED");break;case o.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:f.setReflectionMode("REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED");break;case o.a.CUBIC_MODE:case o.a.INVCUBIC_MODE:default:f.setReflectionMode("REFLECTIONMAP_CUBIC")}f.USE_LOCAL_REFLECTIONMAP_CUBIC=!!this._reflectionTexture.boundingBoxSize}else f.REFLECTION=!1,f.REFLECTIONMAP_OPPOSITEZ=!1;if(this._emissiveTexture&&b.EmissiveTextureEnabled){if(!this._emissiveTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._emissiveTexture,f,"EMISSIVE")}else f.EMISSIVE=!1;if(this._lightmapTexture&&b.LightmapTextureEnabled){if(!this._lightmapTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._lightmapTexture,f,"LIGHTMAP"),f.USELIGHTMAPASSHADOWMAP=this._useLightmapAsShadowmap,f.RGBDLIGHTMAP=this._lightmapTexture.isRGBD}else f.LIGHTMAP=!1;if(this._specularTexture&&b.SpecularTextureEnabled){if(!this._specularTexture.isReadyOrNotBlocking())return!1;n.a.PrepareDefinesForMergedUV(this._specularTexture,f,"SPECULAR"),f.GLOSSINESS=this._useGlossinessFromSpecularMapAlpha}else f.SPECULAR=!1;if(e.getEngine().getCaps().standardDerivatives&&this._bumpTexture&&b.BumpTextureEnabled){if(!this._bumpTexture.isReady())return!1;n.a.PrepareDefinesForMergedUV(this._bumpTexture,f,"BUMP"),f.PARALLAX=this._useParallax,f.PARALLAXOCCLUSION=this._useParallaxOcclusion,f.OBJECTSPACE_NORMALMAP=this._useObjectSpaceNormalMap}else f.BUMP=!1;if(this._refractionTexture&&b.RefractionTextureEnabled){if(!this._refractionTexture.isReadyOrNotBlocking())return!1;f._needUVs=!0,f.REFRACTION=!0,f.REFRACTIONMAP_3D=this._refractionTexture.isCube,f.RGBDREFRACTION=this._refractionTexture.isRGBD,f.USE_LOCAL_REFRACTIONMAP_CUBIC=!!this._refractionTexture.boundingBoxSize}else f.REFRACTION=!1;f.TWOSIDEDLIGHTING=!this._backFaceCulling&&this._twoSidedLighting}else f.DIFFUSE=!1,f.AMBIENT=!1,f.OPACITY=!1,f.REFLECTION=!1,f.EMISSIVE=!1,f.LIGHTMAP=!1,f.BUMP=!1,f.REFRACTION=!1;f.ALPHAFROMDIFFUSE=this._shouldUseAlphaFromDiffuseTexture(),f.EMISSIVEASILLUMINATION=this._useEmissiveAsIllumination,f.LINKEMISSIVEWITHDIFFUSE=this._linkEmissiveWithDiffuse,f.SPECULAROVERALPHA=this._useSpecularOverAlpha,f.PREMULTIPLYALPHA=this.alphaMode===t.a.ALPHA_PREMULTIPLIED||this.alphaMode===t.a.ALPHA_PREMULTIPLIED_PORTERDUFF,f.ALPHATEST_AFTERALLALPHACOMPUTATIONS=null!==this.transparencyMode,f.ALPHABLEND=null===this.transparencyMode||this.needAlphaBlendingForMesh(a)}if(!this.detailMap.isReadyForSubMesh(f,e))return!1;if(f._areImageProcessingDirty&&this._imageProcessingConfiguration){if(!this._imageProcessingConfiguration.isReady())return!1;this._imageProcessingConfiguration.prepareDefines(f),f.IS_REFLECTION_LINEAR=null!=this.reflectionTexture&&!this.reflectionTexture.gammaSpace,f.IS_REFRACTION_LINEAR=null!=this.refractionTexture&&!this.refractionTexture.gammaSpace}if(f._areFresnelDirty&&(b.FresnelEnabled?(this._diffuseFresnelParameters||this._opacityFresnelParameters||this._emissiveFresnelParameters||this._refractionFresnelParameters||this._reflectionFresnelParameters)&&(f.DIFFUSEFRESNEL=this._diffuseFresnelParameters&&this._diffuseFresnelParameters.isEnabled,f.OPACITYFRESNEL=this._opacityFresnelParameters&&this._opacityFresnelParameters.isEnabled,f.REFLECTIONFRESNEL=this._reflectionFresnelParameters&&this._reflectionFresnelParameters.isEnabled,f.REFLECTIONFRESNELFROMSPECULAR=this._useReflectionFresnelFromSpecular,f.REFRACTIONFRESNEL=this._refractionFresnelParameters&&this._refractionFresnelParameters.isEnabled,f.EMISSIVEFRESNEL=this._emissiveFresnelParameters&&this._emissiveFresnelParameters.isEnabled,f._needNormals=!0,f.FRESNEL=!0):f.FRESNEL=!1),n.a.PrepareDefinesForMisc(a,e,this._useLogarithmicDepth,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a)||this._forceAlphaTest,f),n.a.PrepareDefinesForAttributes(a,f,!0,!0,!0),n.a.PrepareDefinesForFrameBoundValues(e,g,f,d,null,c.getRenderingMesh().hasThinInstances),this.detailMap.prepareDefines(f,e),f.isDirty){d=f._areLightsDisposed;f.markAsProcessed();var p=new u.a;f.REFLECTION&&p.addFallback(0,"REFLECTION"),f.SPECULAR&&p.addFallback(0,"SPECULAR"),f.BUMP&&p.addFallback(0,"BUMP"),f.PARALLAX&&p.addFallback(1,"PARALLAX"),f.PARALLAXOCCLUSION&&p.addFallback(0,"PARALLAXOCCLUSION"),f.SPECULAROVERALPHA&&p.addFallback(0,"SPECULAROVERALPHA"),f.FOG&&p.addFallback(1,"FOG"),f.POINTSIZE&&p.addFallback(0,"POINTSIZE"),f.LOGARITHMICDEPTH&&p.addFallback(0,"LOGARITHMICDEPTH"),n.a.HandleFallbacksForShadows(f,p,this._maxSimultaneousLights),f.SPECULARTERM&&p.addFallback(0,"SPECULARTERM"),f.DIFFUSEFRESNEL&&p.addFallback(1,"DIFFUSEFRESNEL"),f.OPACITYFRESNEL&&p.addFallback(2,"OPACITYFRESNEL"),f.REFLECTIONFRESNEL&&p.addFallback(3,"REFLECTIONFRESNEL"),f.EMISSIVEFRESNEL&&p.addFallback(4,"EMISSIVEFRESNEL"),f.FRESNEL&&p.addFallback(4,"FRESNEL"),f.MULTIVIEW&&p.addFallback(0,"MULTIVIEW");var q=[j.b.PositionKind];f.NORMAL&&q.push(j.b.NormalKind),f.TANGENT&&q.push(j.b.TangentKind);for(h=1;h<=t.a.MAX_SUPPORTED_UV_SETS;++h)f["UV"+h]&&q.push("uv"+(1===h?"":h));f.VERTEXCOLOR&&q.push(j.b.ColorKind),n.a.PrepareAttributesForBones(q,a,f,p),n.a.PrepareAttributesForInstances(q,f),n.a.PrepareAttributesForMorphTargets(q,a,f);h="default";a=["world","view","viewProjection","vEyePosition","vLightsType","vAmbientColor","vDiffuseColor","vSpecularColor","vEmissiveColor","visibility","vFogInfos","vFogColor","pointSize","vDiffuseInfos","vAmbientInfos","vOpacityInfos","vReflectionInfos","vEmissiveInfos","vSpecularInfos","vBumpInfos","vLightmapInfos","vRefractionInfos","mBones","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","diffuseMatrix","ambientMatrix","opacityMatrix","reflectionMatrix","emissiveMatrix","specularMatrix","bumpMatrix","normalMatrix","lightmapMatrix","refractionMatrix","diffuseLeftColor","diffuseRightColor","opacityParts","reflectionLeftColor","reflectionRightColor","emissiveLeftColor","emissiveRightColor","refractionLeftColor","refractionRightColor","vReflectionPosition","vReflectionSize","vRefractionPosition","vRefractionSize","logarithmicDepthConstant","vTangentSpaceParams","alphaCutOff","boneTextureWidth","morphTargetTextureInfo","morphTargetTextureIndices"];var i=["diffuseSampler","ambientSampler","opacitySampler","reflectionCubeSampler","reflection2DSampler","emissiveSampler","specularSampler","bumpSampler","lightmapSampler","refractionCubeSampler","refraction2DSampler","boneSampler","morphTargets","oitDepthSampler","oitFrontColorSampler"],m=["Material","Scene","Mesh"];v.a.AddUniforms(a),v.a.AddSamplers(i),k.a.AddUniforms(a),k.a.AddSamplers(i),l.a&&(l.a.PrepareUniforms(a,f),l.a.PrepareSamplers(i,f)),n.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:m,samplers:i,defines:f,maxSimultaneousLights:this._maxSimultaneousLights});var y={};this.customShaderNameResolve&&(h=this.customShaderNameResolve(h,a,m,i,f,q,y));var z=f.toString(),A=c.effect;h=e.getEngine().createEffect(h,{attributes:q,uniformsNames:a,uniformBuffersNames:m,samplers:i,defines:z,fallbacks:p,onCompiled:this.onCompiled,onError:this.onError,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights,maxSimultaneousMorphTargets:f.NUM_MORPH_INFLUENCERS},processFinalCode:y.processFinalCode,multiTarget:f.PREPASS},g);if(h)if(this._onEffectCreatedObservable&&(w.effect=h,w.subMesh=c,this._onEffectCreatedObservable.notifyObservers(w)),this.allowShaderHotSwapping&&A&&!h.isReady()){if(h=A,f.markAsUnprocessed(),d)return f._areLightsDisposed=!0,!1}else e.resetCachedMaterial(),c.setEffect(h,f,this._materialContext),this.buildUniformLayout()}return!(!c.effect||!c.effect.isReady())&&(f._renderId=e.getRenderId(),c.effect._wasPreviouslyReady=!0,!0)},b.prototype.buildUniformLayout=function(){var a=this._uniformBuffer;a.addUniform("diffuseLeftColor",4),a.addUniform("diffuseRightColor",4),a.addUniform("opacityParts",4),a.addUniform("reflectionLeftColor",4),a.addUniform("reflectionRightColor",4),a.addUniform("refractionLeftColor",4),a.addUniform("refractionRightColor",4),a.addUniform("emissiveLeftColor",4),a.addUniform("emissiveRightColor",4),a.addUniform("vDiffuseInfos",2),a.addUniform("vAmbientInfos",2),a.addUniform("vOpacityInfos",2),a.addUniform("vReflectionInfos",2),a.addUniform("vReflectionPosition",3),a.addUniform("vReflectionSize",3),a.addUniform("vEmissiveInfos",2),a.addUniform("vLightmapInfos",2),a.addUniform("vSpecularInfos",2),a.addUniform("vBumpInfos",3),a.addUniform("diffuseMatrix",16),a.addUniform("ambientMatrix",16),a.addUniform("opacityMatrix",16),a.addUniform("reflectionMatrix",16),a.addUniform("emissiveMatrix",16),a.addUniform("lightmapMatrix",16),a.addUniform("specularMatrix",16),a.addUniform("bumpMatrix",16),a.addUniform("vTangentSpaceParams",2),a.addUniform("pointSize",1),a.addUniform("alphaCutOff",1),a.addUniform("refractionMatrix",16),a.addUniform("vRefractionInfos",4),a.addUniform("vRefractionPosition",3),a.addUniform("vRefractionSize",3),a.addUniform("vSpecularColor",4),a.addUniform("vEmissiveColor",3),a.addUniform("vDiffuseColor",4),a.addUniform("vAmbientColor",3),v.a.PrepareUniformBuffer(a),a.create()},b.prototype.unbind=function(){if(this._activeEffect){var b=!1;this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&(this._activeEffect.setTexture("reflection2DSampler",null),b=!0),this._refractionTexture&&this._refractionTexture.isRenderTarget&&(this._activeEffect.setTexture("refraction2DSampler",null),b=!0),b&&this._markAllSubMeshesAsTexturesDirty()}a.prototype.unbind.call(this)},b.prototype.bindForSubMesh=function(a,c,d){var e=this.getScene(),f=d._materialDefines;if(f){d=d.effect;if(d){this._activeEffect=d,c.getMeshUniformBuffer().bindToEffect(d,"Mesh"),c.transferToEffect(a),this.prePassConfiguration.bindForSubMesh(this._activeEffect,e,c,a,this.isFrozen),f.OBJECTSPACE_NORMALMAP&&(a.toNormalMatrix(this._normalMatrix),this.bindOnlyNormalMatrix(this._normalMatrix));a=this._mustRebind(e,d,c.visibility);n.a.BindBonesParameters(c,d);var h=this._uniformBuffer;if(a){if(h.bindToEffect(d,"Material"),this.bindViewProjection(d),!h.useUbo||!this.isFrozen||!h.isSync){if(b.FresnelEnabled&&f.FRESNEL&&(this.diffuseFresnelParameters&&this.diffuseFresnelParameters.isEnabled&&(h.updateColor4("diffuseLeftColor",this.diffuseFresnelParameters.leftColor,this.diffuseFresnelParameters.power),h.updateColor4("diffuseRightColor",this.diffuseFresnelParameters.rightColor,this.diffuseFresnelParameters.bias)),this.opacityFresnelParameters&&this.opacityFresnelParameters.isEnabled&&h.updateColor4("opacityParts",new i.a(this.opacityFresnelParameters.leftColor.toLuminance(),this.opacityFresnelParameters.rightColor.toLuminance(),this.opacityFresnelParameters.bias),this.opacityFresnelParameters.power),this.reflectionFresnelParameters&&this.reflectionFresnelParameters.isEnabled&&(h.updateColor4("reflectionLeftColor",this.reflectionFresnelParameters.leftColor,this.reflectionFresnelParameters.power),h.updateColor4("reflectionRightColor",this.reflectionFresnelParameters.rightColor,this.reflectionFresnelParameters.bias)),this.refractionFresnelParameters&&this.refractionFresnelParameters.isEnabled&&(h.updateColor4("refractionLeftColor",this.refractionFresnelParameters.leftColor,this.refractionFresnelParameters.power),h.updateColor4("refractionRightColor",this.refractionFresnelParameters.rightColor,this.refractionFresnelParameters.bias)),this.emissiveFresnelParameters&&this.emissiveFresnelParameters.isEnabled&&(h.updateColor4("emissiveLeftColor",this.emissiveFresnelParameters.leftColor,this.emissiveFresnelParameters.power),h.updateColor4("emissiveRightColor",this.emissiveFresnelParameters.rightColor,this.emissiveFresnelParameters.bias))),e.texturesEnabled){if(this._diffuseTexture&&b.DiffuseTextureEnabled&&(h.updateFloat2("vDiffuseInfos",this._diffuseTexture.coordinatesIndex,this._diffuseTexture.level),n.a.BindTextureMatrix(this._diffuseTexture,h,"diffuse")),this._ambientTexture&&b.AmbientTextureEnabled&&(h.updateFloat2("vAmbientInfos",this._ambientTexture.coordinatesIndex,this._ambientTexture.level),n.a.BindTextureMatrix(this._ambientTexture,h,"ambient")),this._opacityTexture&&b.OpacityTextureEnabled&&(h.updateFloat2("vOpacityInfos",this._opacityTexture.coordinatesIndex,this._opacityTexture.level),n.a.BindTextureMatrix(this._opacityTexture,h,"opacity")),this._hasAlphaChannel()&&h.updateFloat("alphaCutOff",this.alphaCutOff),this._reflectionTexture&&b.ReflectionTextureEnabled&&(h.updateFloat2("vReflectionInfos",this._reflectionTexture.level,this.roughness),h.updateMatrix("reflectionMatrix",this._reflectionTexture.getReflectionTextureMatrix()),this._reflectionTexture.boundingBoxSize)){var j=this._reflectionTexture;h.updateVector3("vReflectionPosition",j.boundingBoxPosition),h.updateVector3("vReflectionSize",j.boundingBoxSize)}if(this._emissiveTexture&&b.EmissiveTextureEnabled&&(h.updateFloat2("vEmissiveInfos",this._emissiveTexture.coordinatesIndex,this._emissiveTexture.level),n.a.BindTextureMatrix(this._emissiveTexture,h,"emissive")),this._lightmapTexture&&b.LightmapTextureEnabled&&(h.updateFloat2("vLightmapInfos",this._lightmapTexture.coordinatesIndex,this._lightmapTexture.level),n.a.BindTextureMatrix(this._lightmapTexture,h,"lightmap")),this._specularTexture&&b.SpecularTextureEnabled&&(h.updateFloat2("vSpecularInfos",this._specularTexture.coordinatesIndex,this._specularTexture.level),n.a.BindTextureMatrix(this._specularTexture,h,"specular")),this._bumpTexture&&e.getEngine().getCaps().standardDerivatives&&b.BumpTextureEnabled&&(h.updateFloat3("vBumpInfos",this._bumpTexture.coordinatesIndex,1/this._bumpTexture.level,this.parallaxScaleBias),n.a.BindTextureMatrix(this._bumpTexture,h,"bump"),e._mirroredCameraPosition?h.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?1:-1,this._invertNormalMapY?1:-1):h.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?-1:1,this._invertNormalMapY?-1:1)),this._refractionTexture&&b.RefractionTextureEnabled){var k=1;(this._refractionTexture.isCube||(h.updateMatrix("refractionMatrix",this._refractionTexture.getReflectionTextureMatrix()),this._refractionTexture.depth&&(k=this._refractionTexture.depth)),h.updateFloat4("vRefractionInfos",this._refractionTexture.level,this.indexOfRefraction,k,this.invertRefractionY?-1:1),this._refractionTexture.boundingBoxSize)&&(j=this._refractionTexture,(h.updateVector3("vRefractionPosition",j.boundingBoxPosition),h.updateVector3("vRefractionSize",j.boundingBoxSize)))}}this.pointsCloud&&h.updateFloat("pointSize",this.pointSize),f.SPECULARTERM&&h.updateColor4("vSpecularColor",this.specularColor,this.specularPower),h.updateColor3("vEmissiveColor",b.EmissiveTextureEnabled?this.emissiveColor:i.a.BlackReadOnly),h.updateColor4("vDiffuseColor",this.diffuseColor,this.alpha),e.ambientColor.multiplyToRef(this.ambientColor,this._globalAmbientColor),h.updateColor3("vAmbientColor",this._globalAmbientColor)}e.texturesEnabled&&(this._diffuseTexture&&b.DiffuseTextureEnabled&&d.setTexture("diffuseSampler",this._diffuseTexture),this._ambientTexture&&b.AmbientTextureEnabled&&d.setTexture("ambientSampler",this._ambientTexture),this._opacityTexture&&b.OpacityTextureEnabled&&d.setTexture("opacitySampler",this._opacityTexture),this._reflectionTexture&&b.ReflectionTextureEnabled&&(this._reflectionTexture.isCube?d.setTexture("reflectionCubeSampler",this._reflectionTexture):d.setTexture("reflection2DSampler",this._reflectionTexture)),this._emissiveTexture&&b.EmissiveTextureEnabled&&d.setTexture("emissiveSampler",this._emissiveTexture),this._lightmapTexture&&b.LightmapTextureEnabled&&d.setTexture("lightmapSampler",this._lightmapTexture),this._specularTexture&&b.SpecularTextureEnabled&&d.setTexture("specularSampler",this._specularTexture),this._bumpTexture&&e.getEngine().getCaps().standardDerivatives&&b.BumpTextureEnabled&&d.setTexture("bumpSampler",this._bumpTexture),this._refractionTexture&&b.RefractionTextureEnabled)&&(k=1,this._refractionTexture.isCube?d.setTexture("refractionCubeSampler",this._refractionTexture):d.setTexture("refraction2DSampler",this._refractionTexture));this.getScene().useOrderIndependentTransparency&&this.needAlphaBlendingForMesh(c)&&this.getScene().depthPeelingRenderer.bind(d),this.detailMap.bindForSubMesh(h,e,this.isFrozen),n.a.BindClipPlane(d,e),this.bindEyePosition(d)}!a&&this.isFrozen||(e.lightsEnabled&&!this._disableLighting&&n.a.BindLights(e,c,d,f,this._maxSimultaneousLights),(e.fogEnabled&&c.applyFog&&e.fogMode!==g.a.FOGMODE_NONE||this._reflectionTexture||this._refractionTexture||c.receiveShadows)&&this.bindView(d),n.a.BindFogParameters(e,c,d),f.NUM_MORPH_INFLUENCERS&&n.a.BindMorphTargetParameters(c,d),this.useLogarithmicDepth&&n.a.BindLogDepth(f,d,e),this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(this._activeEffect)),this._afterBind(c,this._activeEffect),h.update()}}},b.prototype.getAnimatables=function(){var a=[];return this._diffuseTexture&&this._diffuseTexture.animations&&this._diffuseTexture.animations.length>0&&a.push(this._diffuseTexture),this._ambientTexture&&this._ambientTexture.animations&&this._ambientTexture.animations.length>0&&a.push(this._ambientTexture),this._opacityTexture&&this._opacityTexture.animations&&this._opacityTexture.animations.length>0&&a.push(this._opacityTexture),this._reflectionTexture&&this._reflectionTexture.animations&&this._reflectionTexture.animations.length>0&&a.push(this._reflectionTexture),this._emissiveTexture&&this._emissiveTexture.animations&&this._emissiveTexture.animations.length>0&&a.push(this._emissiveTexture),this._specularTexture&&this._specularTexture.animations&&this._specularTexture.animations.length>0&&a.push(this._specularTexture),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._lightmapTexture&&this._lightmapTexture.animations&&this._lightmapTexture.animations.length>0&&a.push(this._lightmapTexture),this._refractionTexture&&this._refractionTexture.animations&&this._refractionTexture.animations.length>0&&a.push(this._refractionTexture),this.detailMap.getAnimatables(a),a},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);return this._diffuseTexture&&b.push(this._diffuseTexture),this._ambientTexture&&b.push(this._ambientTexture),this._opacityTexture&&b.push(this._opacityTexture),this._reflectionTexture&&b.push(this._reflectionTexture),this._emissiveTexture&&b.push(this._emissiveTexture),this._specularTexture&&b.push(this._specularTexture),this._bumpTexture&&b.push(this._bumpTexture),this._lightmapTexture&&b.push(this._lightmapTexture),this._refractionTexture&&b.push(this._refractionTexture),this.detailMap.getActiveTextures(b),b},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._diffuseTexture===b||this._ambientTexture===b||this._opacityTexture===b||this._reflectionTexture===b||this._emissiveTexture===b||this._specularTexture===b||this._bumpTexture===b||this._lightmapTexture===b||this._refractionTexture===b||this.detailMap.hasTexture(b)},b.prototype.dispose=function(b,c){var d;c&&(null===(d=this._diffuseTexture)||void 0===d||d.dispose(),null===(d=this._ambientTexture)||void 0===d||d.dispose(),null===(d=this._opacityTexture)||void 0===d||d.dispose(),null===(d=this._reflectionTexture)||void 0===d||d.dispose(),null===(d=this._emissiveTexture)||void 0===d||d.dispose(),null===(d=this._specularTexture)||void 0===d||d.dispose(),null===(d=this._bumpTexture)||void 0===d||d.dispose(),null===(d=this._lightmapTexture)||void 0===d||d.dispose(),null===(d=this._refractionTexture)||void 0===d||d.dispose()),this.detailMap.dispose(c),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b,c)},b.prototype.clone=function(a){var c=this,d=e.a.Clone(function(){return new b(a,c.getScene())},this);return d.name=a,d.id=a,this.stencil.copyTo(d.stencil),d},b.prototype.serialize=function(){var a=e.a.Serialize(this);return a.stencil=this.stencil.serialize(),a},b.Parse=function(a,c,d){var f=e.a.Parse(function(){return new b(a.name,c)},a,c,d);return a.stencil&&f.stencil.parse(a.stencil,c,d),f},Object.defineProperty(b,"DiffuseTextureEnabled",{get:function(){return q.a.DiffuseTextureEnabled},set:function(a){q.a.DiffuseTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"DetailTextureEnabled",{get:function(){return q.a.DetailTextureEnabled},set:function(a){q.a.DetailTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"AmbientTextureEnabled",{get:function(){return q.a.AmbientTextureEnabled},set:function(a){q.a.AmbientTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"OpacityTextureEnabled",{get:function(){return q.a.OpacityTextureEnabled},set:function(a){q.a.OpacityTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ReflectionTextureEnabled",{get:function(){return q.a.ReflectionTextureEnabled},set:function(a){q.a.ReflectionTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"EmissiveTextureEnabled",{get:function(){return q.a.EmissiveTextureEnabled},set:function(a){q.a.EmissiveTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"SpecularTextureEnabled",{get:function(){return q.a.SpecularTextureEnabled},set:function(a){q.a.SpecularTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BumpTextureEnabled",{get:function(){return q.a.BumpTextureEnabled},set:function(a){q.a.BumpTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"LightmapTextureEnabled",{get:function(){return q.a.LightmapTextureEnabled},set:function(a){q.a.LightmapTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"RefractionTextureEnabled",{get:function(){return q.a.RefractionTextureEnabled},set:function(a){q.a.RefractionTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"ColorGradingTextureEnabled",{get:function(){return q.a.ColorGradingTextureEnabled},set:function(a){q.a.ColorGradingTextureEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b,"FresnelEnabled",{get:function(){return q.a.FresnelEnabled},set:function(a){q.a.FresnelEnabled=a},enumerable:!1,configurable:!0}),Object(d.c)([Object(e.n)("diffuseTexture")],b.prototype,"_diffuseTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"diffuseTexture",void 0),Object(d.c)([Object(e.n)("ambientTexture")],b.prototype,"_ambientTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTexture",void 0),Object(d.c)([Object(e.n)("opacityTexture")],b.prototype,"_opacityTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"opacityTexture",void 0),Object(d.c)([Object(e.n)("reflectionTexture")],b.prototype,"_reflectionTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(d.c)([Object(e.n)("emissiveTexture")],b.prototype,"_emissiveTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveTexture",void 0),Object(d.c)([Object(e.n)("specularTexture")],b.prototype,"_specularTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"specularTexture",void 0),Object(d.c)([Object(e.n)("bumpTexture")],b.prototype,"_bumpTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"bumpTexture",void 0),Object(d.c)([Object(e.n)("lightmapTexture")],b.prototype,"_lightmapTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"lightmapTexture",void 0),Object(d.c)([Object(e.n)("refractionTexture")],b.prototype,"_refractionTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"refractionTexture",void 0),Object(d.c)([Object(e.f)("ambient")],b.prototype,"ambientColor",void 0),Object(d.c)([Object(e.f)("diffuse")],b.prototype,"diffuseColor",void 0),Object(d.c)([Object(e.f)("specular")],b.prototype,"specularColor",void 0),Object(d.c)([Object(e.f)("emissive")],b.prototype,"emissiveColor",void 0),Object(d.c)([Object(e.d)()],b.prototype,"specularPower",void 0),Object(d.c)([Object(e.d)("useAlphaFromDiffuseTexture")],b.prototype,"_useAlphaFromDiffuseTexture",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"useAlphaFromDiffuseTexture",void 0),Object(d.c)([Object(e.d)("useEmissiveAsIllumination")],b.prototype,"_useEmissiveAsIllumination",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useEmissiveAsIllumination",void 0),Object(d.c)([Object(e.d)("linkEmissiveWithDiffuse")],b.prototype,"_linkEmissiveWithDiffuse",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"linkEmissiveWithDiffuse",void 0),Object(d.c)([Object(e.d)("useSpecularOverAlpha")],b.prototype,"_useSpecularOverAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useSpecularOverAlpha",void 0),Object(d.c)([Object(e.d)("useReflectionOverAlpha")],b.prototype,"_useReflectionOverAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useReflectionOverAlpha",void 0),Object(d.c)([Object(e.d)("disableLighting")],b.prototype,"_disableLighting",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"disableLighting",void 0),Object(d.c)([Object(e.d)("useObjectSpaceNormalMap")],b.prototype,"_useObjectSpaceNormalMap",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useObjectSpaceNormalMap",void 0),Object(d.c)([Object(e.d)("useParallax")],b.prototype,"_useParallax",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallax",void 0),Object(d.c)([Object(e.d)("useParallaxOcclusion")],b.prototype,"_useParallaxOcclusion",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallaxOcclusion",void 0),Object(d.c)([Object(e.d)()],b.prototype,"parallaxScaleBias",void 0),Object(d.c)([Object(e.d)("roughness")],b.prototype,"_roughness",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"roughness",void 0),Object(d.c)([Object(e.d)()],b.prototype,"indexOfRefraction",void 0),Object(d.c)([Object(e.d)()],b.prototype,"invertRefractionY",void 0),Object(d.c)([Object(e.d)()],b.prototype,"alphaCutOff",void 0),Object(d.c)([Object(e.d)("useLightmapAsShadowmap")],b.prototype,"_useLightmapAsShadowmap",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLightmapAsShadowmap",void 0),Object(d.c)([Object(e.i)("diffuseFresnelParameters")],b.prototype,"_diffuseFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"diffuseFresnelParameters",void 0),Object(d.c)([Object(e.i)("opacityFresnelParameters")],b.prototype,"_opacityFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelAndMiscDirty")],b.prototype,"opacityFresnelParameters",void 0),Object(d.c)([Object(e.i)("reflectionFresnelParameters")],b.prototype,"_reflectionFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"reflectionFresnelParameters",void 0),Object(d.c)([Object(e.i)("refractionFresnelParameters")],b.prototype,"_refractionFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"refractionFresnelParameters",void 0),Object(d.c)([Object(e.i)("emissiveFresnelParameters")],b.prototype,"_emissiveFresnelParameters",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"emissiveFresnelParameters",void 0),Object(d.c)([Object(e.d)("useReflectionFresnelFromSpecular")],b.prototype,"_useReflectionFresnelFromSpecular",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsFresnelDirty")],b.prototype,"useReflectionFresnelFromSpecular",void 0),Object(d.c)([Object(e.d)("useGlossinessFromSpecularMapAlpha")],b.prototype,"_useGlossinessFromSpecularMapAlpha",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useGlossinessFromSpecularMapAlpha",void 0),Object(d.c)([Object(e.d)("maxSimultaneousLights")],b.prototype,"_maxSimultaneousLights",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(d.c)([Object(e.d)("invertNormalMapX")],b.prototype,"_invertNormalMapX",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapX",void 0),Object(d.c)([Object(e.d)("invertNormalMapY")],b.prototype,"_invertNormalMapY",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapY",void 0),Object(d.c)([Object(e.d)("twoSidedLighting")],b.prototype,"_twoSidedLighting",void 0),Object(d.c)([Object(e.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"twoSidedLighting",void 0),Object(d.c)([Object(e.d)()],b.prototype,"useLogarithmicDepth",null),b}(b.a);Object(p.b)("BABYLON.StandardMaterial",y),g.a.DefaultMaterialFactory=function(a){return new y("default material",a)}},function(a,b,c){c.d(b,"a",function(){return x});var d=c(2),e=c(12),f=c(6),g=c(0),h=c(13),i=c(4),j=c(18),k=c(46),l=c(50),m=c(70),n=c(1),o=c(67),p=c(174),q=c(26),r=c(114),s=c(8),t=c(22),u=c(25);a=c(10);var v=function(){this.facetNb=0,this.partitioningSubdivisions=10,this.partitioningBBoxRatio=1.01,this.facetDataEnabled=!1,this.facetParameters={},this.bbSize=g.e.Zero(),this.subDiv={max:1,X:1,Y:1,Z:1},this.facetDepthSort=!1,this.facetDepthSortEnabled=!1},w=function(){this._hasVertexAlpha=!1,this._useVertexColors=!0,this._numBoneInfluencers=4,this._applyFog=!0,this._receiveShadows=!1,this._facetData=new v,this._visibility=1,this._skeleton=null,this._layerMask=268435455,this._computeBonesUsingShaders=!0,this._isActive=!1,this._onlyForInstances=!1,this._isActiveIntermediate=!1,this._onlyForInstancesIntermediate=!1,this._actAsRegularMesh=!1,this._currentLOD=null,this._currentLODIsUpToDate=!1,this._collisionRetryCount=3,this._morphTargetManager=null,this._renderingGroupId=0,this._material=null,this._positions=null,this._meshCollisionData=new p.a},x=function(a){function b(c,d){void 0===d&&(d=null);var e=a.call(this,c,d,!1)||this;return e._internalAbstractMeshDataInfo=new w,e.cullingStrategy=b.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY,e.onCollideObservable=new f.c,e.onCollisionPositionChangeObservable=new f.c,e.onMaterialChangedObservable=new f.c,e.definedFacingForward=!0,e._occlusionQuery=null,e._renderingGroup=null,e.alphaIndex=Number.MAX_VALUE,e.isVisible=!0,e.isPickable=!0,e.isNearPickable=!1,e.isNearGrabbable=!1,e.showSubMeshesBoundingBox=!1,e.isBlocker=!1,e.enablePointerMoveEvents=!1,e.outlineColor=s.a.Red(),e.outlineWidth=.02,e.overlayColor=s.a.Red(),e.overlayAlpha=.5,e.useOctreeForRenderingSelection=!0,e.useOctreeForPicking=!0,e.useOctreeForCollisions=!0,e.alwaysSelectAsActiveMesh=!1,e.doNotSyncBoundingInfo=!1,e.actionManager=null,e.ellipsoid=new g.e(.5,1,.5),e.ellipsoidOffset=new g.e(0,0,0),e.edgesWidth=1,e.edgesColor=new s.b(1,0,0,1),e._edgesRenderer=null,e._masterMesh=null,e._boundingInfo=null,e._boundingInfoIsDirty=!0,e._renderId=0,e._intersectionsInProgress=new Array,e._unIndexed=!1,e._lightSources=new Array,e._waitingData={lods:null,actions:null,freezeWorldMatrix:null},e._bonesTransformMatrices=null,e._transformMatrixTexture=null,e.onRebuildObservable=new f.c,e._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null),b.subtractToRef(e._internalAbstractMeshDataInfo._meshCollisionData._oldPositionForCollisions,e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions),e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions.length()>h.a.CollisionsEpsilon&&e.position.addInPlace(e._internalAbstractMeshDataInfo._meshCollisionData._diffPositionForCollisions),c&&e.onCollideObservable.notifyObservers(c),e.onCollisionPositionChangeObservable.notifyObservers(e.position)},e.getScene().addMesh(e),e._resyncLightSources(),e._uniformBuffer=new o.a(e.getScene().getEngine(),void 0,void 0,c),e._buildUniformLayout(),e}return Object(d.d)(b,a),Object.defineProperty(b,"BILLBOARDMODE_NONE",{get:function(){return k.a.BILLBOARDMODE_NONE},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_X",{get:function(){return k.a.BILLBOARDMODE_X},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_Y",{get:function(){return k.a.BILLBOARDMODE_Y},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_Z",{get:function(){return k.a.BILLBOARDMODE_Z},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_ALL",{get:function(){return k.a.BILLBOARDMODE_ALL},enumerable:!1,configurable:!0}),Object.defineProperty(b,"BILLBOARDMODE_USE_POSITION",{get:function(){return k.a.BILLBOARDMODE_USE_POSITION},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"facetNb",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetNb},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"partitioningSubdivisions",{get:function(){return this._internalAbstractMeshDataInfo._facetData.partitioningSubdivisions},set:function(a){this._internalAbstractMeshDataInfo._facetData.partitioningSubdivisions=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"partitioningBBoxRatio",{get:function(){return this._internalAbstractMeshDataInfo._facetData.partitioningBBoxRatio},set:function(a){this._internalAbstractMeshDataInfo._facetData.partitioningBBoxRatio=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"mustDepthSortFacets",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDepthSort},set:function(a){this._internalAbstractMeshDataInfo._facetData.facetDepthSort=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"facetDepthSortFrom",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDepthSortFrom},set:function(a){this._internalAbstractMeshDataInfo._facetData.facetDepthSortFrom=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionRetryCount",{get:function(){return this._internalAbstractMeshDataInfo._collisionRetryCount},set:function(a){this._internalAbstractMeshDataInfo._collisionRetryCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isFacetDataEnabled",{get:function(){return this._internalAbstractMeshDataInfo._facetData.facetDataEnabled},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"morphTargetManager",{get:function(){return this._internalAbstractMeshDataInfo._morphTargetManager},set:function(a){this._internalAbstractMeshDataInfo._morphTargetManager!==a&&(this._internalAbstractMeshDataInfo._morphTargetManager=a,this._syncGeometryWithMorphTargetManager())},enumerable:!1,configurable:!0}),b.prototype._syncGeometryWithMorphTargetManager=function(){},b.prototype._updateNonUniformScalingState=function(b){return!!a.prototype._updateNonUniformScalingState.call(this,b)&&(this._markSubMeshesAsMiscDirty(),!0)},Object.defineProperty(b.prototype,"onCollide",{set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver&&this.onCollideObservable.remove(this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver),this._internalAbstractMeshDataInfo._meshCollisionData._onCollideObserver=this.onCollideObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"onCollisionPositionChange",{set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver&&this.onCollisionPositionChangeObservable.remove(this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver),this._internalAbstractMeshDataInfo._meshCollisionData._onCollisionPositionChangeObserver=this.onCollisionPositionChangeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"visibility",{get:function(){return this._internalAbstractMeshDataInfo._visibility},set:function(a){if(this._internalAbstractMeshDataInfo._visibility!==a){var b=this._internalAbstractMeshDataInfo._visibility;this._internalAbstractMeshDataInfo._visibility=a,(1===b&&1!==a||1!==b&&1===a)&&this._markSubMeshesAsMiscDirty()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"renderingGroupId",{get:function(){return this._internalAbstractMeshDataInfo._renderingGroupId},set:function(a){this._internalAbstractMeshDataInfo._renderingGroupId=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"material",{get:function(){return this._internalAbstractMeshDataInfo._material},set:function(a){this._internalAbstractMeshDataInfo._material!==a&&(this._internalAbstractMeshDataInfo._material&&this._internalAbstractMeshDataInfo._material.meshMap&&(this._internalAbstractMeshDataInfo._material.meshMap[this.uniqueId]=void 0),this._internalAbstractMeshDataInfo._material=a,a&&a.meshMap&&(a.meshMap[this.uniqueId]=this),this.onMaterialChangedObservable.hasObservers()&&this.onMaterialChangedObservable.notifyObservers(this),this.subMeshes&&this._unBindEffect())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"receiveShadows",{get:function(){return this._internalAbstractMeshDataInfo._receiveShadows},set:function(a){this._internalAbstractMeshDataInfo._receiveShadows!==a&&(this._internalAbstractMeshDataInfo._receiveShadows=a,this._markSubMeshesAsLightDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasVertexAlpha",{get:function(){return this._internalAbstractMeshDataInfo._hasVertexAlpha},set:function(a){this._internalAbstractMeshDataInfo._hasVertexAlpha!==a&&(this._internalAbstractMeshDataInfo._hasVertexAlpha=a,this._markSubMeshesAsAttributesDirty(),this._markSubMeshesAsMiscDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useVertexColors",{get:function(){return this._internalAbstractMeshDataInfo._useVertexColors},set:function(a){this._internalAbstractMeshDataInfo._useVertexColors!==a&&(this._internalAbstractMeshDataInfo._useVertexColors=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"computeBonesUsingShaders",{get:function(){return this._internalAbstractMeshDataInfo._computeBonesUsingShaders},set:function(a){this._internalAbstractMeshDataInfo._computeBonesUsingShaders!==a&&(this._internalAbstractMeshDataInfo._computeBonesUsingShaders=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"numBoneInfluencers",{get:function(){return this._internalAbstractMeshDataInfo._numBoneInfluencers},set:function(a){this._internalAbstractMeshDataInfo._numBoneInfluencers!==a&&(this._internalAbstractMeshDataInfo._numBoneInfluencers=a,this._markSubMeshesAsAttributesDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"applyFog",{get:function(){return this._internalAbstractMeshDataInfo._applyFog},set:function(a){this._internalAbstractMeshDataInfo._applyFog!==a&&(this._internalAbstractMeshDataInfo._applyFog=a,this._markSubMeshesAsMiscDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"layerMask",{get:function(){return this._internalAbstractMeshDataInfo._layerMask},set:function(a){a!==this._internalAbstractMeshDataInfo._layerMask&&(this._internalAbstractMeshDataInfo._layerMask=a,this._resyncLightSources())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionMask},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionResponse",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionResponse},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionResponse=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"collisionGroup",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._collisionGroup},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._collisionGroup=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"surroundingMeshes",{get:function(){return this._internalAbstractMeshDataInfo._meshCollisionData._surroundingMeshes},set:function(a){this._internalAbstractMeshDataInfo._meshCollisionData._surroundingMeshes=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lightSources",{get:function(){return this._lightSources},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"_positions",{get:function(){return null},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"skeleton",{get:function(){return this._internalAbstractMeshDataInfo._skeleton},set:function(a){var b=this._internalAbstractMeshDataInfo._skeleton;b&&b.needInitialSkinMatrix&&b._unregisterMeshWithPoseMatrix(this),a&&a.needInitialSkinMatrix&&a._registerMeshWithPoseMatrix(this),this._internalAbstractMeshDataInfo._skeleton=a,this._internalAbstractMeshDataInfo._skeleton||(this._bonesTransformMatrices=null),this._markSubMeshesAsAttributesDirty()},enumerable:!1,configurable:!0}),b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("world",16),this._uniformBuffer.addUniform("visibility",1),this._uniformBuffer.create()},b.prototype.transferToEffect=function(a){var b=this._uniformBuffer;b.updateMatrix("world",a),b.updateFloat("visibility",this._internalAbstractMeshDataInfo._visibility),b.update()},b.prototype.getMeshUniformBuffer=function(){return this._uniformBuffer},b.prototype.getClassName=function(){return"AbstractMesh"},b.prototype.toString=function(a){var b="Name: "+this.name+", isInstance: "+("InstancedMesh"!==this.getClassName()?"YES":"NO");b+=", # of submeshes: "+(this.subMeshes?this.subMeshes.length:0);var c=this._internalAbstractMeshDataInfo._skeleton;return c&&(b+=", skeleton: "+c.name),a&&(b+=", billboard mode: "+["NONE","X","Y",null,"Z",null,null,"ALL"][this.billboardMode],b+=", freeze wrld mat: "+(this._isWorldMatrixFrozen||this._waitingData.freezeWorldMatrix?"YES":"NO")),b},b.prototype._getEffectiveParent=function(){return this._masterMesh&&this.billboardMode!==k.a.BILLBOARDMODE_NONE?this._masterMesh:a.prototype._getEffectiveParent.call(this)},b.prototype._getActionManagerForTrigger=function(a,b){if(void 0===b&&(b=!0),this.actionManager&&(b||this.actionManager.isRecursive)){if(!a)return this.actionManager;if(this.actionManager.hasSpecificTrigger(a))return this.actionManager}return this.parent?this.parent._getActionManagerForTrigger(a,!1):null},b.prototype._rebuild=function(a){if(void 0===a&&!1,this.onRebuildObservable.notifyObservers(this),null!==this._occlusionQuery&&(this._occlusionQuery=null),this.subMeshes)for(var a=0,b=this.subMeshes;a4;a=h?this.getVerticesData(i.b.MatricesIndicesExtraKind):null;var j=h?this.getVerticesData(i.b.MatricesWeightsExtraKind):null;this.skeleton.prepare();for(var k=this.skeleton.getTransformMatrices(this),l=g.c.Vector3[0],m=g.c.Matrix[0],v=g.c.Matrix[1],w=0,x=0;x0&&(g.a.FromFloat32ArrayToRefScaled(k,Math.floor(16*c[w+u]),y,v),m.addToSelf(v));if(h)for(u=0;u<4;u++)(y=j[w+u])>0&&(g.a.FromFloat32ArrayToRefScaled(k,Math.floor(16*a[w+u]),y,v),m.addToSelf(v));g.e.TransformCoordinatesFromFloatsToRef(d[x],d[x+1],d[x+2],m,l),l.toArray(d,x),this._positions&&this._positions[x/3].copyFrom(l)}}}if(d&&b&&this.morphTargetManager)for(y=0,u=0,l=0;l0){v=a.getPositions();v&&(d[l]+=(v[l]-d[l])*k)}}if(y++,this._positions&&3===y){y=0;h=3*u;this._positions[u++].copyFromFloats(d[h],d[h+1],d[h+2])}}return d},b.prototype._updateBoundingInfo=function(){var a=this._effectiveMesh;return this._boundingInfo?this._boundingInfo.update(a.worldMatrixFromCache):this._boundingInfo=new m.a(this.position,this.position,a.worldMatrixFromCache),this._updateSubMeshesBoundingInfo(a.worldMatrixFromCache),this},b.prototype._updateSubMeshesBoundingInfo=function(a){if(!this.subMeshes)return this;for(var b=this.subMeshes.length,c=0;c1||!d.IsGlobal)&&d.updateBoundingInfo(a)}return this},b.prototype._afterComputeWorldMatrix=function(){this.doNotSyncBoundingInfo||(this._boundingInfoIsDirty=!0)},Object.defineProperty(b.prototype,"_effectiveMesh",{get:function(){return this.skeleton&&this.skeleton.overrideMesh||this},enumerable:!1,configurable:!0}),b.prototype.isInFrustum=function(a){return this.getBoundingInfo().isInFrustum(a,this.cullingStrategy)},b.prototype.isCompletelyInFrustum=function(a){return this.getBoundingInfo().isCompletelyInFrustum(a)},b.prototype.intersectsMesh=function(a,b,c){void 0===b&&(b=!1);var d=this.getBoundingInfo(),e=a.getBoundingInfo();if(d.intersects(e,b))return!0;if(c)for(d=0,e=this.getChildMeshes();d1&&!f._checkCollision(a)||this._collideForSubMesh(f,b,a)}return this},b.prototype._checkCollision=function(a){if(!this.getBoundingInfo()._checkCollision(a))return this;var b=g.c.Matrix[0],c=g.c.Matrix[1];return g.a.ScalingToRef(1/a._radius.x,1/a._radius.y,1/a._radius.z,b),this.worldMatrixFromCache.multiplyToRef(b,c),this._processCollisionsForSubMeshes(a,c),this},b.prototype._generatePointsArray=function(){return!1},b.prototype.intersects=function(a,b,c,d,e,f){void 0===d&&(d=!1),void 0===f&&(f=!1);var h=new l.a,i="InstancedLinesMesh"===this.getClassName()||"LinesMesh"===this.getClassName()?this.intersectionThreshold:0,j=this.getBoundingInfo();if(!this.subMeshes)return h;if(!(f||a.intersectsSphere(j.boundingSphere,i)&&a.intersectsBox(j.boundingBox,i)))return h;if(d)return h.hit=!f,h.pickedMesh=f?null:this,h.distance=f?0:g.e.Distance(a.origin,j.boundingSphere.center),h.subMeshId=0,h;if(!this._generatePointsArray())return h;for(var i=null,d=this._scene.getIntersectingSubMeshCandidates(this,a),f=d.length,k=!1,m=0;m1)||t.canIntersects(a)){o=t.intersects(a,this._positions,this.getIndices(),b,c);if(o&&(b||!i||o.distance-1&&this._parentContainer.meshes.splice(g,1),this._parentContainer=null}if(c&&this.material&&("MultiMaterial"===this.material.getClassName()?this.material.dispose(!1,!0,!0):this.material.dispose(!1,!0)),!b)for(d=0;d65535){f=!0;break}a.depthSortedIndices=f?new Uint32Array(c):new Uint16Array(c)}if(a.facetDepthSortFunction=function(a,b){return b.sqDistance-a.sqDistance},!a.facetDepthSortFrom){h=this.getScene().activeCamera;a.facetDepthSortFrom=h?h.position:g.e.Zero()}a.depthSortedFacets=[];for(f=0;ft.a?e.maximum.x-e.minimum.x:t.a,a.bbSize.y=e.maximum.y-e.minimum.y>t.a?e.maximum.y-e.minimum.y:t.a,a.bbSize.z=e.maximum.z-e.minimum.z>t.a?e.maximum.z-e.minimum.z:t.a;h=a.bbSize.x>a.bbSize.y?a.bbSize.x:a.bbSize.y;if(h=h>a.bbSize.z?h:a.bbSize.z,a.subDiv.max=a.partitioningSubdivisions,a.subDiv.X=Math.floor(a.subDiv.max*a.bbSize.x/h),a.subDiv.Y=Math.floor(a.subDiv.max*a.bbSize.y/h),a.subDiv.Z=Math.floor(a.subDiv.max*a.bbSize.z/h),a.subDiv.X=a.subDiv.X<1?1:a.subDiv.X,a.subDiv.Y=a.subDiv.Y<1?1:a.subDiv.Y,a.subDiv.Z=a.subDiv.Z<1?1:a.subDiv.Z,a.facetParameters.facetNormals=this.getFacetLocalNormals(),a.facetParameters.facetPositions=this.getFacetLocalPositions(),a.facetParameters.facetPartitioning=this.getFacetLocalPartitioning(),a.facetParameters.bInfo=e,a.facetParameters.bbSize=a.bbSize,a.facetParameters.subDiv=a.subDiv,a.facetParameters.ratio=this.partitioningBBoxRatio,a.facetParameters.depthSort=a.facetDepthSort,a.facetDepthSort&&a.facetDepthSortEnabled&&(this.computeWorldMatrix(!0),this._worldMatrix.invertToRef(a.invertedMatrix),g.e.TransformCoordinatesToRef(a.facetDepthSortFrom,a.invertedMatrix,a.facetDepthSortOrigin),a.facetParameters.distanceTo=a.facetDepthSortOrigin),a.facetParameters.depthSortedFacets=a.depthSortedFacets,d&&j.a.ComputeNormals(b,c,d,a.facetParameters),a.facetDepthSort&&a.facetDepthSortEnabled){a.depthSortedFacets.sort(a.facetDepthSortFunction);h=a.depthSortedIndices.length/3|0;for(f=0;fe.subDiv.max||b<0||b>e.subDiv.max||c<0||c>e.subDiv.max?null:e.facetPartitioning[a+e.subDiv.max*b+e.subDiv.max*e.subDiv.max*c]},b.prototype.getClosestFacetAtCoordinates=function(a,b,c,d,e,f){void 0===e&&(e=!1),void 0===f&&(f=!0);var h=this.getWorldMatrix(),i=g.c.Matrix[5];h.invertToRef(i);var j=g.c.Vector3[8];g.e.TransformCoordinatesFromFloatsToRef(a,b,c,i,j);a=this.getClosestFacetAtLocalCoordinates(j.x,j.y,j.z,d,e,f);return d&&g.e.TransformCoordinatesFromFloatsToRef(d.x,d.y,d.z,h,d),a},b.prototype.getClosestFacetAtLocalCoordinates=function(a,b,c,d,e,f){void 0===e&&(e=!1),void 0===f&&(f=!0);var g=null,h,i,j,k=this.getFacetLocalPositions(),l=this.getFacetLocalNormals(),m=this.getFacetsAtLocalCoordinates(a,b,c);if(!m)return null;for(var n,o,x,y=Number.MAX_VALUE,v=y,w=0;w=0||e&&!f&&j<=0)&&(j=o.x*x.x+o.y*x.y+o.z*x.z,x=-(o.x*a+o.y*b+o.z*c-j)/(o.x*o.x+o.y*o.y+o.z*o.z),(v=(h=(j=a+o.x*x)-a)*h+(i=(h=b+o.y*x)-b)*i+(o=(i=c+o.z*x)-c)*o)-1?a:i.a.GetShadersRepository(this._shaderLanguage)+a,this._engine._loadFile(e+"."+b.toLowerCase()+".fx",d))}else d(window.atob(a.substr(7)));else d(a.substr(7))},Object.defineProperty(a.prototype,"vertexSourceCode",{get:function(){var a;return this._vertexSourceCodeOverride&&this._fragmentSourceCodeOverride?this._vertexSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getVertexShaderCode())&&void 0!==a?a:this._vertexSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"fragmentSourceCode",{get:function(){var a;return this._vertexSourceCodeOverride&&this._fragmentSourceCodeOverride?this._fragmentSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getFragmentShaderCode())&&void 0!==a?a:this._fragmentSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawVertexSourceCode",{get:function(){return this._rawVertexSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawFragmentSourceCode",{get:function(){return this._rawFragmentSourceCode},enumerable:!1,configurable:!0}),a.prototype._rebuildProgram=function(a,b,c,d){var f=this;this._isReady=!1,this._vertexSourceCodeOverride=a,this._fragmentSourceCodeOverride=b,this.onError=function(a,b){d&&d(b)},this.onCompiled=function(){var a=f.getEngine().scenes;if(a)for(var b=0;b=d&&(e="Offending line ["+d+"] in "+(c?"fragment":"vertex")+" code: "+b[d-1])}}return[a,e]},a.prototype._processCompilationErrors=function(b,c){var d;void 0===c&&(c=null),this._compilationError=b.message;b=this._attributesNames;var e=this._fallbacks;if(g.a.Error("Unable to compile effect:"),g.a.Error("Uniforms: "+this._uniformsNames.map(function(a){return" "+a})),g.a.Error("Attributes: "+b.map(function(a){return" "+a})),g.a.Error("Defines: "+this.defines),a.LogShaderCodeOnCompilationError){b=null;var f=null,h=null;(null===(d=this._pipelineContext)||void 0===d?void 0:d._getVertexShaderCode())&&(h=(d=this._getShaderCodeAndErrorLine(this._pipelineContext._getVertexShaderCode(),this._compilationError,!1))[0],b=d[1],h&&(g.a.Error("Vertex code:"),g.a.Error(h))),(null===(d=this._pipelineContext)||void 0===d?void 0:d._getFragmentShaderCode())&&(h=(d=this._getShaderCodeAndErrorLine(null===(d=this._pipelineContext)||void 0===d?void 0:d._getFragmentShaderCode(),this._compilationError,!0))[0],f=d[1],h&&(g.a.Error("Fragment code:"),g.a.Error(h))),b&&g.a.Error(b),f&&g.a.Error(f)}g.a.Error("Error: "+this._compilationError),c&&(this._pipelineContext=c,this._isReady=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this)),e?(this._pipelineContext=null,e.hasMoreFallbacks?(this._allFallbacksProcessed=!1,g.a.Error("Trying next fallback."),this.defines=e.reduce(this.defines,this),this._prepareEffect()):(this._allFallbacksProcessed=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this),this.onErrorObservable.clear(),this._fallbacks&&this._fallbacks.unBindMesh())):this._allFallbacksProcessed=!0},Object.defineProperty(a.prototype,"isSupported",{get:function(){return""===this._compilationError},enumerable:!1,configurable:!0}),a.prototype._bindTexture=function(a,b){this._engine._bindTexture(this._samplers[a],b,a)},a.prototype.setTexture=function(a,b){this._engine.setTexture(this._samplers[a],this._uniforms[a],b,a)},a.prototype.setDepthStencilTexture=function(a,b){this._engine.setDepthStencilTexture(this._samplers[a],this._uniforms[a],b,a)},a.prototype.setTextureArray=function(a,b){var c=a+"Ex";if(-1===this._samplerList.indexOf(c+"0")){for(var d=this._samplerList.indexOf(a),e=1;e0&&(b.push(g-1),b.push(g)),g++}k=new f.a;return k.indices=b,k.positions=c,a&&(k.colors=e),k}function j(a){var b=a.dashSize||3,c=a.gapSize||1,e=a.dashNb||200;a=a.points;var g=new Array,h=new Array,i=d.e.Zero(),j=0,k=0,l=0,o=0,p=0;for(p=0;p>2,e=(3&b)<<4|(b=j>4,f=(15&b)<<2|(c=j>6,g=63&c,isNaN(b)?f=g=64:isNaN(c)&&(g=64),i+=h.charAt(d)+h.charAt(e)+h.charAt(f)+h.charAt(g);return i},h=function(a){return atob(a)},i=function(a){for(var a=h(a),b=a.length,c=new Uint8Array(new ArrayBuffer(b)),d=0;d0)):!c._pointerCaptures[e.pointerId]&&h.distance>d.distance&&(c.mainSceneTrackerPredicate&&c.mainSceneTrackerPredicate(d.pickedMesh)?(c._notifyObservers(b,d,e),b.skipOnPointerObservable=!0):b.type!==f.a.POINTERMOVE&&b.type!==f.a.POINTERUP||(c._lastPointerEvents[e.pointerId]&&(c.onPointerOutObservable.notifyObservers(e.pointerId),delete c._lastPointerEvents[e.pointerId]),c._notifyObservers(b,h,e))),b.type===f.a.POINTERUP&&c._pointerCaptures[e.pointerId]&&(c._pointerCaptures[e.pointerId]=!1))}}}}),this._originalPointerObserver&&a.onPrePointerObservable.makeObserverTopPriority(this._originalPointerObserver)),this.utilityLayerScene.autoClear=!1,this._afterRenderObserver=this.originalScene.onAfterCameraRenderObservable.add(function(a){c.shouldRender&&a==c.getRenderCamera()&&c.render()}),this._sceneDisposeObserver=this.originalScene.onDisposeObservable.add(function(){c.dispose()}),this._updateCamera()}return a.prototype.getRenderCamera=function(a){if(this._renderCamera)return this._renderCamera;var b;return b=this.originalScene.activeCameras&&this.originalScene.activeCameras.length>1?this.originalScene.activeCameras[this.originalScene.activeCameras.length-1]:this.originalScene.activeCamera,a&&b&&b.isRigCamera?b.rigParent:b},a.prototype.setRenderCamera=function(a){this._renderCamera=a},a.prototype._getSharedGizmoLight=function(){return this._sharedGizmoLight||(this._sharedGizmoLight=new i.a("shared gizmo light",new j.e(0,1,0),this.utilityLayerScene),this._sharedGizmoLight.intensity=2,this._sharedGizmoLight.groundColor=k.a.Gray()),this._sharedGizmoLight},Object.defineProperty(a,"DefaultUtilityLayer",{get:function(){return null==a._DefaultUtilityLayer?a._CreateDefaultUtilityLayerFromScene(h.a.LastCreatedScene):a._DefaultUtilityLayer},enumerable:!1,configurable:!0}),a._CreateDefaultUtilityLayerFromScene=function(b){return a._DefaultUtilityLayer=new a(b),a._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(function(){a._DefaultUtilityLayer=null}),a._DefaultUtilityLayer},Object.defineProperty(a,"DefaultKeepDepthUtilityLayer",{get:function(){return null==a._DefaultKeepDepthUtilityLayer&&(a._DefaultKeepDepthUtilityLayer=new a(h.a.LastCreatedScene),a._DefaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil=!1,a._DefaultKeepDepthUtilityLayer.originalScene.onDisposeObservable.addOnce(function(){a._DefaultKeepDepthUtilityLayer=null})),a._DefaultKeepDepthUtilityLayer},enumerable:!1,configurable:!0}),a.prototype._notifyObservers=function(a,b,c){a.skipOnPointerObservable||(this.utilityLayerScene.onPointerObservable.notifyObservers(new f.b(a.type,a.event,b),a.type),this._lastPointerEvents[c.pointerId]=!0)},a.prototype.render=function(){if(this._updateCamera(),this.utilityLayerScene.activeCamera){var a=this.utilityLayerScene.activeCamera.getScene(),b=this.utilityLayerScene.activeCamera;b._scene=this.utilityLayerScene,b.leftCamera&&(b.leftCamera._scene=this.utilityLayerScene),b.rightCamera&&(b.rightCamera._scene=this.utilityLayerScene),this.utilityLayerScene.render(!1),b._scene=a,b.leftCamera&&(b.leftCamera._scene=a),b.rightCamera&&(b.rightCamera._scene=a)}},a.prototype.dispose=function(){this.onPointerOutObservable.clear(),this._afterRenderObserver&&this.originalScene.onAfterCameraRenderObservable.remove(this._afterRenderObserver),this._sceneDisposeObserver&&this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver),this._originalPointerObserver&&this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver),this.utilityLayerScene.dispose()},a.prototype._updateCamera=function(){this.utilityLayerScene.cameraToUseForPointers=this.getRenderCamera(),this.utilityLayerScene.activeCamera=this.getRenderCamera()},a._DefaultUtilityLayer=null,a._DefaultKeepDepthUtilityLayer=null,a}()},function(a,b,c){var d;c.d(b,"a",function(){return d}),(function(a){a[a.GLSL=0]="GLSL",a[a.WGSL=1]="WGSL"})(d||(d={}))},function(a,b,c){c.d(b,"a",function(){return h});var d=c(51),e=c(0),f=c(50),g=c(126);a=c(21);b=c(28);var h=function(){function a(a,b,c){void 0===c&&(c=Number.MAX_VALUE),this.origin=a,this.direction=b,this.length=c}return a.prototype.clone=function(){return new a(this.origin.clone(),this.direction.clone(),this.length)},a.prototype.intersectsBoxMinMax=function(b,c,d){void 0===d&&(d=0);var e,f,g,h;b=a._TmpVector3[0].copyFromFloats(b.x-d,b.y-d,b.z-d);c=a._TmpVector3[1].copyFromFloats(c.x+d,c.y+d,c.z+d);d=0;var i=Number.MAX_VALUE;if(Math.abs(this.direction.x)<1e-7){if(this.origin.xc.x)return!1}else if(e=1/this.direction.x,f=(b.x-this.origin.x)*e,(g=(c.x-this.origin.x)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;if(Math.abs(this.direction.y)<1e-7){if(this.origin.yc.y)return!1}else if(e=1/this.direction.y,f=(b.y-this.origin.y)*e,(g=(c.y-this.origin.y)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;if(Math.abs(this.direction.z)<1e-7){if(this.origin.zc.z)return!1}else if(e=1/this.direction.z,f=(b.z-this.origin.z)*e,(g=(c.z-this.origin.z)*e)===-1/0&&(g=1/0),f>g&&(h=f,f=g,g=h),(d=Math.max(f,d))>(i=Math.min(g,i)))return!1;return!0},a.prototype.intersectsBox=function(a,b){return void 0===b&&(b=0),this.intersectsBoxMinMax(a.minimum,a.maximum,b)},a.prototype.intersectsSphere=function(a,b){void 0===b&&(b=0);var c=a.center.x-this.origin.x,d=a.center.y-this.origin.y,e=a.center.z-this.origin.z,f=c*c+d*d+e*e;a=a.radius+b;b=a*a;if(f<=b)return!0;a=c*this.direction.x+d*this.direction.y+e*this.direction.z;return!(a<0)&&f-a*a<=b},a.prototype.intersectsTriangle=function(b,c,d){var f=a._TmpVector3[0],i=a._TmpVector3[1],j=a._TmpVector3[2],h=a._TmpVector3[3],k=a._TmpVector3[4];c.subtractToRef(b,f),d.subtractToRef(b,i),e.e.CrossToRef(this.direction,i,j);c=e.e.Dot(f,j);if(0===c)return null;d=1/c;this.origin.subtractToRef(b,h);c=e.e.Dot(h,j)*d;if(c<0||c>1)return null;e.e.CrossToRef(h,f,k);b=e.e.Dot(this.direction,k)*d;if(b<0||c+b>1)return null;j=e.e.Dot(i,k)*d;return j>this.length?null:new g.a(1-c-b,c,j)},a.prototype.intersectsPlane=function(a){var b=e.e.Dot(a.normal,this.direction);if(Math.abs(b)<999999997475243e-21)return null;var c=e.e.Dot(a.normal,this.origin);return(a=(-a.d-c)/b)<0?a<-999999997475243e-21?null:0:a},a.prototype.intersectsAxis=function(a,b){switch(void 0===b&&(b=0),a){case"y":return(a=(this.origin.y-b)/this.direction.y)>0?null:new e.e(this.origin.x+this.direction.x*-a,b,this.origin.z+this.direction.z*-a);case"x":return(a=(this.origin.x-b)/this.direction.x)>0?null:new e.e(b,this.origin.y+this.direction.y*-a,this.origin.z+this.direction.z*-a);case"z":return(a=(this.origin.z-b)/this.direction.z)>0?null:new e.e(this.origin.x+this.direction.x*-a,this.origin.y+this.direction.y*-a,b);default:return null}},a.prototype.intersectsMesh=function(b,c){var d=e.c.Matrix[0];return b.getWorldMatrix().invertToRef(d),this._tmpRay?a.TransformToRef(this,d,this._tmpRay):this._tmpRay=a.Transform(this,d),b.intersects(this._tmpRay,c)},a.prototype.intersectsMeshes=function(a,b,c){c?c.length=0:c=[];for(var d=0;db.distance?1:0},a.prototype.intersectionSegment=function(b,c,d){var f=this.origin,g=e.c.Vector3[0],i=e.c.Vector3[1],j=e.c.Vector3[2],h=e.c.Vector3[3];c.subtractToRef(b,g),this.direction.scaleToRef(a.rayl,j),f.addToRef(j,i),b.subtractToRef(f,h);var k;c=e.e.Dot(g,g);i=e.e.Dot(g,j);b=e.e.Dot(j,j);f=e.e.Dot(g,h);var l=e.e.Dot(j,h),m=c*b-i*i,n=m,o=m;mn&&(m=n,k=l+i,o=b)),k<0?(k=0,-f<0?m=0:-f>c?m=n:(m=-f,n=c)):k>o&&(k=o,-f+i<0?m=0:-f+i>c?m=n:(m=-f+i,n=c)),l=Math.abs(m)0&&b<=this.length&&c.lengthSquared()=a.distance?null:c:null},a.a.prototype._internalPick=function(a,b,c,d,g){if(!f.a)return null;for(var i=null,j=0;jthis.data.length&&(this.data.length*=2)},a.prototype.forEach=function(a){for(var b=0;bthis.data.length&&(this.data.length=2*(this.length+a.length));for(var b=0;b=this.length?-1:a},a.prototype.contains=function(a){return-1!==this.indexOf(a)},a._GlobalId=0,a}(),f=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._duplicateId=0,b}return Object(d.d)(b,a),b.prototype.push=function(b){a.prototype.push.call(this,b),b.__smartArrayFlags||(b.__smartArrayFlags={}),b.__smartArrayFlags[this._id]=this._duplicateId},b.prototype.pushNoDuplicate=function(a){return(!a.__smartArrayFlags||a.__smartArrayFlags[this._id]!==this._duplicateId)&&(this.push(a),!0)},b.prototype.reset=function(){a.prototype.reset.call(this),this._duplicateId++},b.prototype.concatWithNoDuplicate=function(a){if(0!==a.length){this.length+a.length>this.data.length&&(this.data.length=2*(this.length+a.length));for(var b=0;b-1&&this._parentContainer.transformNodes.splice(d,1),this._parentContainer=null}if(this.onAfterWorldMatrixUpdateObservable.clear(),b)for(var d=0,e=this.getChildTransformNodes(!0);d0)if("object"==typeof k[0])for(l=0;l1)?1:a.arc||1,i=a.slice&&a.slice<=0?1:a.slice||1,j=0===a.sideOrientation?0:a.sideOrientation||f.a.DEFAULTSIDE,k=!!a.dedupTopBottomIndices,c=new d.e(c/2,e/2,g/2),e=2+b,g=2*e,b=[],l=[],m=[],n=[],o=0;o<=e;o++){for(var p=o/e,q=p*Math.PI*i,r=0;r<=g;r++){var s=r/g,t=s*Math.PI*2*h,u=d.a.RotationZ(-q);t=d.a.RotationY(t);u=d.e.TransformCoordinates(d.e.Up(),u);u=d.e.TransformCoordinates(u,t);t=u.multiply(c);u=u.divide(c).normalize();l.push(t.x,t.y,t.z),m.push(u.x,u.y,u.z),n.push(s,p)}if(o>0)for(t=l.length/3,u=t-2*(g+1);u+g+21&&(b.push(u),b.push(u+1),b.push(u+g+1)),(o0&&-1===this.includedOnlyMeshes.indexOf(a))&&!(this.excludedMeshes&&this.excludedMeshes.length>0&&-1!==this.excludedMeshes.indexOf(a))&&(0===this.includeOnlyWithLayerMask||0!=(this.includeOnlyWithLayerMask&a.layerMask))&&!(0!==this.excludeWithLayerMask&&this.excludeWithLayerMask&a.layerMask)},b.prototype.dispose=function(b,c){if(void 0===c&&(c=!1),this._shadowGenerator&&(this._shadowGenerator.dispose(),this._shadowGenerator=null),this.getScene().stopAnimation(this),this._parentContainer){var d=this._parentContainer.lights.indexOf(this);d>-1&&this._parentContainer.lights.splice(d,1),this._parentContainer=null}for(var d=0,e=this.getScene().meshes;d0&&(a.excludedMeshesIds=[],this.excludedMeshes.forEach(function(b){a.excludedMeshesIds.push(b.id)})),this.includedOnlyMeshes.length>0&&(a.includedOnlyMeshesIds=[],this.includedOnlyMeshes.forEach(function(b){a.includedOnlyMeshesIds.push(b.id)})),e.a.AppendSerializedAnimations(this,a),a.ranges=this.serializeAnimationRanges(),a},b.GetConstructorFromName=function(a,b,c){a=h.a.Construct("Light_Type_"+a,b,c);return a||null},b.Parse=function(a,c){var d=b.GetConstructorFromName(a.type,a.name,c);if(!d)return null;d=e.a.Parse(d,a,c);if(a.excludedMeshesIds&&(d._excludedMeshesIds=a.excludedMeshesIds),a.includedOnlyMeshesIds&&(d._includedOnlyMeshesIds=a.includedOnlyMeshesIds),a.parentId&&(d._waitingParentId=a.parentId),void 0!==a.falloffType&&(d.falloffType=a.falloffType),void 0!==a.lightmapMode&&(d.lightmapMode=a.lightmapMode),a.animations){for(var f=0;f=0&&this._scene.textures.splice(b,1),this._scene.onTextureRemovedObservable.notifyObservers(this),this._scene=null,this._parentContainer){b=this._parentContainer.textures.indexOf(this);b>-1&&this._parentContainer.textures.splice(b,1),this._parentContainer=null}}this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.metadata=null,a.prototype.dispose.call(this)},b.prototype.serialize=function(){if(!this.name)return null;var a=e.a.Serialize(this);return e.a.AppendSerializedAnimations(this,a),a},b.WhenAllReady=function(a,b){var c=a.length;if(0!==c)for(var d=0;d=200&&a.status<300||0===a.status&&(!Object(f.e)()||A())){try{b(h?a.response:a.responseText,a)}catch(a){g(a)}return}var c=t.DefaultRetryStrategy;if(c){c=c(j,a,p);if(-1!==c)return a.removeEventListener("loadend",u),a=new e.a,void (d=setTimeout(function(){return l(p+1)},c))}c=new r("Error status: "+a.status+" "+a.statusText+" - Unable to load "+j,a);q&&q(c)}};a.addEventListener("readystatechange",v),a.send()};l(0)};if(d&&d.enableSceneOffline){var p=function(a){a&&a.status>400?q&&q(a):l()};d.open(function(){d&&d.loadFile(t.BaseUrl+a,function(a){s||b(a),k.onCompleteObservable.notifyObservers(k)},c?function(a){s||c(a)}:void 0,p,h)},p)}else l();return k},A=function(){return"undefined"!=typeof location&&"file:"===location.protocol},B=function(a){return p.test(a)};function C(a){return Object(j.b)(a.split(",")[1])}var D=function(a){return Object(j.c)(a.split(",")[1])};k.a._FileToolsLoadImage=w,k.a._FileToolsLoadFile=y,b.a._FileToolsLoadFile=y,Object(o.b)(C,D,t,B,A,y,w,x,z,v)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b,c,d,e,f){this.source=a,this.pointerX=b,this.pointerY=c,this.meshUnderPointer=d,this.sourceEvent=e,this.additionalData=f}return a.CreateNew=function(b,c,d){var e=b.getScene();return new a(b,e.pointerX,e.pointerY,e.meshUnderPointer||b,c,d)},a.CreateNewFromSprite=function(b,c,d,e){return new a(b,c.pointerX,c.pointerY,c.meshUnderPointer,d,e)},a.CreateNewFromScene=function(b,c){return new a(null,b.pointerX,b.pointerY,b.meshUnderPointer,c)},a.CreateNewFromPrimitive=function(b,c,d,e){return new a(b,c.x,c.y,null,d,e)},a}()},function(a,b,c){c.d(b,"b",function(){return j}),c.d(b,"a",function(){return k}),c.d(b,"c",function(){return l});var d=c(0),e=c(8),f=c(9),g=c(18),h=c(21),i=c(25);function j(a){var b=a.height||2,c=0===a.diameterTop?0:a.diameterTop||a.diameter||1,h=0===a.diameterBottom?0:a.diameterBottom||a.diameter||1;c=c||1e-5,h=h||1e-5;var j,k=a.tessellation||24,l=a.subdivisions||1,m=!!a.hasRings,n=!!a.enclose,o=0===a.cap?0:a.cap||f.a.CAP_ALL,p=a.arc&&(a.arc<=0||a.arc>1)?1:a.arc||1,q=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE,r=a.faceUV||new Array(3),s=a.faceColors,t=2+(1+(1!==p&&n?2:0))*(m?l:1);for(j=0;j1e3&&(this._lastSecAverage=this._lastSecAccumulated/this._lastSecValueCount,this._lastSecTime=a,this._lastSecAccumulated=0,this._lastSecValueCount=0)},a.Enabled=!0,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b,c,d){this.x=a,this.y=b,this.width=c,this.height=d}return a.prototype.toGlobal=function(b,c){return new a(this.x*b,this.y*c,this.width*b,this.height*c)},a.prototype.toGlobalToRef=function(a,b,c){return c.x=this.x*a,c.y=this.y*b,c.width=this.width*a,c.height=this.height*b,this},a.prototype.clone=function(){return new a(this.x,this.y,this.width,this.height)},a}()},function(a,b,c){c.d(b,"c",function(){return h}),c.d(b,"b",function(){return i}),c.d(b,"a",function(){return j});var d=c(0),e=c(8),f=c(9),g=c(18);function h(a){var b=[0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23],c=[0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0],f=[],h=a.width||a.size||1,i=a.height||a.size||1,j=a.depth||a.size||1,k=a.wrap||!1,l=void 0===a.topBaseAt?1:a.topBaseAt,m=void 0===a.bottomBaseAt?0:a.bottomBaseAt;l=[2,0,3,1][(l+4)%4];m=[2,0,1,3][(m+4)%4];var n=[1,-1,1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,1];if(k){b=[2,3,0,2,0,1,4,5,6,4,6,7,9,10,11,9,11,8,12,14,15,12,13,14],n=[-1,1,1,1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,1,1,1,1,-1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1];for(var k=[[1,1,1],[-1,1,1],[-1,1,-1],[1,1,-1]],o=[[-1,-1,1],[1,-1,1],[1,-1,-1],[-1,-1,-1]],p=[17,18,19,16],q=[22,23,20,21];l>0;)k.unshift(k.pop()),p.unshift(p.pop()),l--;for(;m>0;)o.unshift(o.pop()),q.unshift(q.pop()),m--;k=k.flat(),o=o.flat(),n=n.concat(k).concat(o),b.push(p[0],p[2],p[3],p[0],p[1],p[2]),b.push(q[0],q[2],q[3],q[0],q[1],q[2])}var r=[h/2,i/2,j/2];l=n.reduce(function(a,b,c){return a.concat(b*r[c%3])},[]);for(m=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE,k=a.faceUV||new Array(6),o=a.faceColors,p=[],q=0;q<6;q++)void 0===k[q]&&(k[q]=new d.f(0,0,1,1)),o&&void 0===o[q]&&(o[q]=new e.b(1,1,1,1));for(h=0;h<6;h++)if(f.push(k[h].z,k[h].w),f.push(k[h].x,k[h].w),f.push(k[h].x,k[h].y),f.push(k[h].z,k[h].y),o)for(i=0;i<4;i++)p.push(o[h].r,o[h].g,o[h].b,o[h].a);g.a._ComputeSides(m,l,b,c,f,a.frontUVs,a.backUVs);j=new g.a;if(j.indices=b,j.positions=l,j.normals=c,j.uvs=f,o){n=m===g.a.DOUBLESIDE?p.concat(p):p;j.colors=n}return j}function i(a,b,c){void 0===c&&(c=null);a=new f.a(a,c);return b.sideOrientation=f.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,h(b).applyToMesh(a,b.updatable),a}var j={CreateBox:i};g.a.CreateBox=h,f.a.CreateBox=function(a,b,c,d,e){return void 0===c&&(c=null),i(a,{size:b,sideOrientation:e,updatable:d},c)}},function(a,b,c){c.d(b,"a",function(){return j});var d=c(4),e=c(126),f=c(70),g=c(1),h=c(114),i=c(37),j=function(){function a(a,b,c,d,e,f,h,j,k){void 0===j&&(j=!0),void 0===k&&(k=!0),this.materialIndex=a,this.verticesStart=b,this.verticesCount=c,this.indexStart=d,this.indexCount=e,this._materialDefines=null,this._materialEffect=null,this._mainDrawWrapperOverride=null,this._linesIndexCount=0,this._linesIndexBuffer=null,this._lastColliderWorldVertices=null,this._lastColliderTransformMatrix=null,this._renderId=0,this._alphaIndex=0,this._distanceToCamera=0,this._currentMaterial=null,this._mesh=f,this._renderingMesh=h||f,k&&f.subMeshes.push(this),this._drawWrappers={},this._mainDrawWrapper=new i.a(this._mesh.getScene().getEngine(),!1),this._drawWrappers[g.a.SUBMESH_DRAWWRAPPER_MAINPASS]=this._mainDrawWrapper,this._trianglePlanes=[],this._id=f.subMeshes.length-1,j&&(this.refreshBoundingInfo(),f.computeWorldMatrix(!0))}return Object.defineProperty(a.prototype,"materialDefines",{get:function(){return this._mainDrawWrapperOverride?this._mainDrawWrapperOverride.defines:this._mainDrawWrapper.defines},set:function(a){var b;(null!==(b=this._mainDrawWrapperOverride)&&void 0!==b?b:this._mainDrawWrapper).defines=a,this._materialDefines=a},enumerable:!1,configurable:!0}),a.prototype._getDrawWrapper=function(a,b){if(void 0===b&&(b=!1),a===g.a.SUBMESH_DRAWWRAPPER_MAINPASS)return this._mainDrawWrapper;var c=this._drawWrappers[a];return!c&&b&&(this._drawWrappers[a]=c=new i.a(this._mesh.getScene().getEngine())),c},a.prototype._removeEffect=function(a){delete this._drawWrappers[a]},Object.defineProperty(a.prototype,"effect",{get:function(){return this._mainDrawWrapperOverride?this._mainDrawWrapperOverride.effect:this._mainDrawWrapper.effect},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_drawWrapper",{get:function(){var a;return null!==(a=this._mainDrawWrapperOverride)&&void 0!==a?a:this._mainDrawWrapper},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_drawWrapperOverride",{get:function(){return this._mainDrawWrapperOverride},enumerable:!1,configurable:!0}),a.prototype._setMainDrawWrapperOverride=function(a){this._mainDrawWrapperOverride=a;a=null!==(a=this._mainDrawWrapperOverride)&&void 0!==a?a:this._mainDrawWrapper;this._materialEffect=a.effect,this._materialDefines=a.defines},a.prototype.setEffect=function(a,b,c,d){var e;void 0===b&&(b=null),void 0===d&&(d=!0);e=null!==(e=this._mainDrawWrapperOverride)&&void 0!==e?e:this._mainDrawWrapper;e.setEffect(a,b),void 0!==c&&(e.materialContext=c,d&&c.reset()),a!==this._materialEffect?(this._materialEffect=a,this._materialDefines=b):a||(this._materialDefines=null,e.materialContext=void 0)},a.AddToMesh=function(b,c,d,e,f,g,h,i){return void 0===i&&(i=!0),new a(b,c,d,e,f,g,h,i)},Object.defineProperty(a.prototype,"IsGlobal",{get:function(){return 0===this.verticesStart&&this.verticesCount===this._mesh.getTotalVertices()},enumerable:!1,configurable:!0}),a.prototype.getBoundingInfo=function(){return this.IsGlobal?this._mesh.getBoundingInfo():this._boundingInfo},a.prototype.setBoundingInfo=function(a){return this._boundingInfo=a,this},a.prototype.getMesh=function(){return this._mesh},a.prototype.getRenderingMesh=function(){return this._renderingMesh},a.prototype.getReplacementMesh=function(){return this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:null},a.prototype.getEffectiveMesh=function(){var a=this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:null;return a||this._renderingMesh},a.prototype.getMaterial=function(){var a=this._renderingMesh.material;if(null==a)return this._mesh.getScene().defaultMaterial;if(this._IsMultiMaterial(a)){var b=a.getSubMaterial(this.materialIndex);return this._currentMaterial!==b&&(this._currentMaterial=b,this._mainDrawWrapper.defines=null),b}return a},a.prototype._IsMultiMaterial=function(a){return void 0!==a.getSubMaterial},a.prototype.refreshBoundingInfo=function(a){if(void 0===a&&(a=null),this._lastColliderWorldVertices=null,this.IsGlobal||!this._renderingMesh||!this._renderingMesh.geometry)return this;if(a||(a=this._renderingMesh.getVerticesData(d.b.PositionKind)),!a)return this._boundingInfo=this._mesh.getBoundingInfo(),this;var b=this._renderingMesh.getIndices();if(0===this.indexStart&&this.indexCount===b.length){var c=this._renderingMesh.getBoundingInfo();c={minimum:c.minimum.clone(),maximum:c.maximum.clone()}}else c=Object(h.b)(a,b,this.indexStart,this.indexCount,this._renderingMesh.geometry.boundingBias);return this._boundingInfo?this._boundingInfo.reConstruct(c.minimum,c.maximum):this._boundingInfo=new f.a(c.minimum,c.maximum),this},a.prototype._checkCollision=function(a){return this.getBoundingInfo()._checkCollision(a)},a.prototype.updateBoundingInfo=function(a){var b=this.getBoundingInfo();return b||(this.refreshBoundingInfo(),b=this.getBoundingInfo()),b&&b.update(a),this},a.prototype.isInFrustum=function(a){var b=this.getBoundingInfo();return!!b&&b.isInFrustum(a,this._mesh.cullingStrategy)},a.prototype.isCompletelyInFrustum=function(a){var b=this.getBoundingInfo();return!!b&&b.isCompletelyInFrustum(a)},a.prototype.render=function(a){return this._renderingMesh.render(this,a,this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh?this._mesh:void 0),this},a.prototype._getLinesIndexBuffer=function(a,b){if(!this._linesIndexBuffer){for(var c=[],d=this.indexStart;di&&(i=l)}return new a(b,h,i-h+1,c,d,e,f,g)},a}()},function(a,b,c){a="helperFunctions";b="const float PI=3.1415926535897932384626433832795; const float HALF_MIN=5.96046448e-08; const float LinearEncodePowerApprox=2.2; const float GammaEncodePowerApprox=1.0/LinearEncodePowerApprox; const vec3 LuminanceEncodeApprox=vec3(0.2126,0.7152,0.0722); const float Epsilon=0.0000001; #define saturate(x) clamp(x,0.0,1.0) #define absEps(x) abs(x)+Epsilon #define maxEps(x) max(x,Epsilon) #define saturateEps(x) clamp(x,Epsilon,1.0) mat3 transposeMat3(mat3 inMatrix) { vec3 i0=inMatrix[0]; vec3 i1=inMatrix[1]; vec3 i2=inMatrix[2]; mat3 outMatrix=mat3( vec3(i0.x,i1.x,i2.x), vec3(i0.y,i1.y,i2.y), vec3(i0.z,i1.z,i2.z) ); return outMatrix; } mat3 inverseMat3(mat3 inMatrix) { float a00=inMatrix[0][0],a01=inMatrix[0][1],a02=inMatrix[0][2]; float a10=inMatrix[1][0],a11=inMatrix[1][1],a12=inMatrix[1][2]; float a20=inMatrix[2][0],a21=inMatrix[2][1],a22=inMatrix[2][2]; float b01=a22*a11-a12*a21; float b11=-a22*a10+a12*a20; float b21=a21*a10-a11*a20; float det=a00*b01+a01*b11+a02*b21; return mat3(b01,(-a22*a01+a02*a21),(a12*a01-a02*a11), b11,(a22*a00-a02*a20),(-a12*a00+a02*a10), b21,(-a21*a00+a01*a20),(a11*a00-a01*a10))/det; } float toLinearSpace(float color) { return pow(color,LinearEncodePowerApprox); } vec3 toLinearSpace(vec3 color) { return pow(color,vec3(LinearEncodePowerApprox)); } vec4 toLinearSpace(vec4 color) { return vec4(pow(color.rgb,vec3(LinearEncodePowerApprox)),color.a); } vec3 toGammaSpace(vec3 color) { return pow(color,vec3(GammaEncodePowerApprox)); } vec4 toGammaSpace(vec4 color) { return vec4(pow(color.rgb,vec3(GammaEncodePowerApprox)),color.a); } float toGammaSpace(float color) { return pow(color,GammaEncodePowerApprox); } float square(float value) { return value*value; } float pow5(float value) { float sq=value*value; return sq*sq*value; } float getLuminance(vec3 color) { return clamp(dot(color,LuminanceEncodeApprox),0.,1.); } float getRand(vec2 seed) { return fract(sin(dot(seed.xy ,vec2(12.9898,78.233)))*43758.5453); } float dither(vec2 seed,float varianceAmount) { float rand=getRand(seed); float dither=mix(-varianceAmount/255.0,varianceAmount/255.0,rand); return dither; } const float rgbdMaxRange=255.0; vec4 toRGBD(vec3 color) { float maxRGB=maxEps(max(color.r,max(color.g,color.b))); float D=max(rgbdMaxRange/maxRGB,1.); D=clamp(floor(D)/255.0,0.,1.); vec3 rgb=color.rgb*D; rgb=toGammaSpace(rgb); return vec4(clamp(rgb,0.,1.),D); } vec3 fromRGBD(vec4 rgbd) { rgbd.rgb=toLinearSpace(rgbd.rgb); return rgbd.rgb/rgbd.a; } vec3 parallaxCorrectNormal( vec3 vertexPos,vec3 origVec,vec3 cubeSize,vec3 cubePos ) { vec3 invOrigVec=vec3(1.0,1.0,1.0)/origVec; vec3 halfSize=cubeSize*0.5; vec3 intersecAtMaxPlane=(cubePos+halfSize-vertexPos)*invOrigVec; vec3 intersecAtMinPlane=(cubePos-halfSize-vertexPos)*invOrigVec; vec3 largestIntersec=max(intersecAtMaxPlane,intersecAtMinPlane); float distance=min(min(largestIntersec.x,largestIntersec.y),largestIntersec.z); vec3 intersectPositionWS=vertexPos+origVec*distance; return intersectPositionWS-cubePos; } ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.CompareLightsPriority=function(a,b){return a.shadowEnabled!==b.shadowEnabled?(b.shadowEnabled?1:0)-(a.shadowEnabled?1:0):b.renderPriority-a.renderPriority},a.FALLOFF_DEFAULT=0,a.FALLOFF_PHYSICAL=1,a.FALLOFF_GLTF=2,a.FALLOFF_STANDARD=3,a.LIGHTMAP_DEFAULT=0,a.LIGHTMAP_SPECULAR=1,a.LIGHTMAP_SHADOWSONLY=2,a.INTENSITYMODE_AUTOMATIC=0,a.INTENSITYMODE_LUMINOUSPOWER=1,a.INTENSITYMODE_LUMINOUSINTENSITY=2,a.INTENSITYMODE_ILLUMINANCE=3,a.INTENSITYMODE_LUMINANCE=4,a.LIGHTTYPEID_POINTLIGHT=0,a.LIGHTTYPEID_DIRECTIONALLIGHT=1,a.LIGHTTYPEID_SPOTLIGHT=2,a.LIGHTTYPEID_HEMISPHERICLIGHT=3,a}()},function(a,b,c){c.d(b,"b",function(){return f}),c.d(b,"a",function(){return g}),c.d(b,"c",function(){return h});var d=c(9),e=c(18);function f(a){var b=[],c=[],d=[],f=[],g=a.width||a.size||1,h=a.height||a.size||1,i=0===a.sideOrientation?0:a.sideOrientation||e.a.DEFAULTSIDE;g=g/2;h=h/2;c.push(-g,-h,0),d.push(0,0,-1),f.push(0,0),c.push(g,-h,0),d.push(0,0,-1),f.push(1,0),c.push(g,h,0),d.push(0,0,-1),f.push(1,1),c.push(-g,h,0),d.push(0,0,-1),f.push(0,1),b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),e.a._ComputeSides(i,c,b,d,f,a.frontUVs,a.backUVs);g=new e.a;return g.indices=b,g.positions=c,g.normals=d,g.uvs=f,g}function g(a,b,c){void 0===c&&(c=null);a=new d.a(a,c);return b.sideOrientation=d.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,f(b).applyToMesh(a,b.updatable),b.sourcePlane&&(a.translate(b.sourcePlane.normal,-b.sourcePlane.d),a.setDirection(b.sourcePlane.normal.scale(-1))),a}var h={CreatePlane:g};e.a.CreatePlane=f,d.a.CreatePlane=function(a,b,c,d,e){return g(a,{size:b,width:b,height:b,sideOrientation:e,updatable:d},c)}},function(a,b,c){c.d(b,"a",function(){return f});var d=c(7),e=c(12),f=(c(179),function(){function a(a,b,c,d){this._valueCache={},this._engine=a,this._noUBO=!a.supportsUniformBuffers,this._dynamic=c,this._name=null!=d?d:"no-name",this._data=b||[],this._uniformLocations={},this._uniformSizes={},this._uniformArraySizes={},this._uniformLocationPointer=0,this._needSync=!1,this._engine._features.trackUbosInFrame&&(this._buffers=[],this._bufferIndex=-1,this._createBufferOnWrite=!1,this._currentFrameId=0),this._noUBO?(this.updateMatrix3x3=this._updateMatrix3x3ForEffect,this.updateMatrix2x2=this._updateMatrix2x2ForEffect,this.updateFloat=this._updateFloatForEffect,this.updateFloat2=this._updateFloat2ForEffect,this.updateFloat3=this._updateFloat3ForEffect,this.updateFloat4=this._updateFloat4ForEffect,this.updateFloatArray=this._updateFloatArrayForEffect,this.updateArray=this._updateArrayForEffect,this.updateIntArray=this._updateIntArrayForEffect,this.updateMatrix=this._updateMatrixForEffect,this.updateMatrices=this._updateMatricesForEffect,this.updateVector3=this._updateVector3ForEffect,this.updateVector4=this._updateVector4ForEffect,this.updateColor3=this._updateColor3ForEffect,this.updateColor4=this._updateColor4ForEffect,this.updateDirectColor4=this._updateDirectColor4ForEffect,this.updateInt=this._updateIntForEffect,this.updateInt2=this._updateInt2ForEffect,this.updateInt3=this._updateInt3ForEffect,this.updateInt4=this._updateInt4ForEffect):(this._engine._uniformBuffers.push(this),this.updateMatrix3x3=this._updateMatrix3x3ForUniform,this.updateMatrix2x2=this._updateMatrix2x2ForUniform,this.updateFloat=this._updateFloatForUniform,this.updateFloat2=this._updateFloat2ForUniform,this.updateFloat3=this._updateFloat3ForUniform,this.updateFloat4=this._updateFloat4ForUniform,this.updateFloatArray=this._updateFloatArrayForUniform,this.updateArray=this._updateArrayForUniform,this.updateIntArray=this._updateIntArrayForUniform,this.updateMatrix=this._updateMatrixForUniform,this.updateMatrices=this._updateMatricesForUniform,this.updateVector3=this._updateVector3ForUniform,this.updateVector4=this._updateVector4ForUniform,this.updateColor3=this._updateColor3ForUniform,this.updateColor4=this._updateColor4ForUniform,this.updateDirectColor4=this._updateDirectColor4ForUniform,this.updateInt=this._updateIntForUniform,this.updateInt2=this._updateInt2ForUniform,this.updateInt3=this._updateInt3ForUniform,this.updateInt4=this._updateInt4ForUniform)}return Object.defineProperty(a.prototype,"useUbo",{get:function(){return!this._noUBO},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSync",{get:function(){return!this._needSync},enumerable:!1,configurable:!0}),a.prototype.isDynamic=function(){return void 0!==this._dynamic},a.prototype.getData=function(){return this._bufferData},a.prototype.getBuffer=function(){return this._buffer},a.prototype._fillAlignment=function(a){if(a=a<=2?a:4,this._uniformLocationPointer%a!=0){var b=this._uniformLocationPointer;this._uniformLocationPointer+=a-this._uniformLocationPointer%a;for(a=this._uniformLocationPointer-b,b=0;b0){if(b instanceof Array)throw"addUniform should not be use with Array in UBO: "+a;(this._fillAlignment(4),this._uniformArraySizes[a]={strideSize:b,arraySize:c},16==b)?b*=c:b=b*c+(4-b)*c;c=[];for(var d=0;d0?(this._needSync=0!==this._bufferIndex,this._bufferIndex=0,this._buffer=this._buffers[this._bufferIndex][0]):this._bufferIndex=-1,this._currentEffect&&this._buffer&&this._currentEffect.bindUniformBuffer(this._buffer,this._currentEffectName))},a.prototype.updateUniform=function(a,b,c){this._checkNewFrame();var f=this._uniformLocations[a];if(void 0===f){if(this._buffer)return void d.a.Error("Cannot add an uniform after UBO has been created.");this.addUniform(a,c),f=this._uniformLocations[a]}if(this._buffer||this.create(),this._dynamic)for(a=0;aj.max||j.min>i.max)},m=function(){function a(a,b,c){this._isLocked=!1,this.boundingBox=new g.a(a,b,c),this.boundingSphere=new h.a(a,b,c)}return a.prototype.reConstruct=function(a,b,c){this.boundingBox.reConstruct(a,b,c),this.boundingSphere.reConstruct(a,b,c)},Object.defineProperty(a.prototype,"minimum",{get:function(){return this.boundingBox.minimum},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maximum",{get:function(){return this.boundingBox.maximum},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isLocked",{get:function(){return this._isLocked},set:function(a){this._isLocked=a},enumerable:!1,configurable:!0}),a.prototype.update=function(a){this._isLocked||(this.boundingBox._update(a),this.boundingSphere._update(a))},a.prototype.centerOn=function(b,c){var d=a.TmpVector3[0].copyFrom(b).subtractInPlace(c);b=a.TmpVector3[1].copyFrom(b).addInPlace(c);return this.boundingBox.reConstruct(d,b,this.boundingBox.getWorldMatrix()),this.boundingSphere.reConstruct(d,b,this.boundingBox.getWorldMatrix()),this},a.prototype.encapsulate=function(a){var b=e.e.Minimize(this.minimum,a);a=e.e.Maximize(this.maximum,a);return this.reConstruct(b,a,this.boundingBox.getWorldMatrix()),this},a.prototype.encapsulateBoundingInfo=function(a){return this.encapsulate(a.boundingBox.centerWorld.subtract(a.boundingBox.extendSizeWorld)),this.encapsulate(a.boundingBox.centerWorld.add(a.boundingBox.extendSizeWorld)),this},a.prototype.scale=function(a){return this.boundingBox.scale(a),this.boundingSphere.scale(a),this},a.prototype.isInFrustum=function(a,b){return void 0===b&&(b=f.a.MESHES_CULLINGSTRATEGY_STANDARD),!(b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION&&b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY||!this.boundingSphere.isCenterInFrustum(a))||!!this.boundingSphere.isInFrustum(a)&&(!(b!==f.a.MESHES_CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY&&b!==f.a.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY)||this.boundingBox.isInFrustum(a))},Object.defineProperty(a.prototype,"diagonalLength",{get:function(){var b=this.boundingBox;return b.maximumWorld.subtractToRef(b.minimumWorld,a.TmpVector3[0]).length()},enumerable:!1,configurable:!0}),a.prototype.isCompletelyInFrustum=function(a){return this.boundingBox.isCompletelyInFrustum(a)},a.prototype._checkCollision=function(a){return a._canDoCollision(this.boundingSphere.centerWorld,this.boundingSphere.radiusWorld,this.boundingBox.minimumWorld,this.boundingBox.maximumWorld)},a.prototype.intersectsPoint=function(a){return!!this.boundingSphere.centerWorld&&!!this.boundingSphere.intersectsPoint(a)&&!!this.boundingBox.intersectsPoint(a)},a.prototype.intersects=function(a,b){if(!h.a.Intersects(this.boundingSphere,a.boundingSphere))return!1;if(!g.a.Intersects(this.boundingBox,a.boundingBox))return!1;if(!b)return!0;b=this.boundingBox;a=a.boundingBox;return!!l(b.directions[0],b,a)&&!!l(b.directions[1],b,a)&&!!l(b.directions[2],b,a)&&!!l(a.directions[0],b,a)&&!!l(a.directions[1],b,a)&&!!l(a.directions[2],b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[0],a.directions[2]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[1],a.directions[2]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[0]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[1]),b,a)&&!!l(e.e.Cross(b.directions[2],a.directions[2]),b,a)},a.TmpVector3=d.a.BuildArray(2,e.e.Zero),a}()},function(a,b,c){c.d(b,"a",function(){return o});var d=c(2),e=c(3),f=c(0),g=c(4),h=c(11),i=c(15);a=c(29);b=c(10);var j=c(8),k=c(74),l=c(57),m=c(13),n={effect:null,subMesh:null},o=function(a){function b(b,c,e,g){void 0===g&&(g={});b=a.call(this,b,c)||this;return b._textures={},b._textureArrays={},b._externalTextures={},b._floats={},b._ints={},b._floatsArrays={},b._colors3={},b._colors3Arrays={},b._colors4={},b._colors4Arrays={},b._vectors2={},b._vectors3={},b._vectors4={},b._matrices={},b._matrixArrays={},b._matrices3x3={},b._matrices2x2={},b._vectors2Arrays={},b._vectors3Arrays={},b._vectors4Arrays={},b._uniformBuffers={},b._textureSamplers={},b._storageBuffers={},b._cachedWorldViewMatrix=new f.a,b._cachedWorldViewProjectionMatrix=new f.a,b._multiview=!1,b._shaderPath=e,b._options=Object(d.a)({needAlphaBlending:!1,needAlphaTesting:!1,attributes:["position","normal","uv"],uniforms:["worldViewProjection"],uniformBuffers:[],samplers:[],externalTextures:[],samplerObjects:[],storageBuffers:[],defines:[],useClipPlane:!1},g),b}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"shaderPath",{get:function(){return this._shaderPath},set:function(a){this._shaderPath=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"options",{get:function(){return this._options},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"ShaderMaterial"},b.prototype.needAlphaBlending=function(){return this.alpha<1||this._options.needAlphaBlending},b.prototype.needAlphaTesting=function(){return this._options.needAlphaTesting},b.prototype._checkUniform=function(a){-1===this._options.uniforms.indexOf(a)&&this._options.uniforms.push(a)},b.prototype.setTexture=function(a,b){return-1===this._options.samplers.indexOf(a)&&this._options.samplers.push(a),this._textures[a]=b,this},b.prototype.setTextureArray=function(a,b){return-1===this._options.samplers.indexOf(a)&&this._options.samplers.push(a),this._checkUniform(a),this._textureArrays[a]=b,this},b.prototype.setExternalTexture=function(a,b){return-1===this._options.externalTextures.indexOf(a)&&this._options.externalTextures.push(a),this._externalTextures[a]=b,this},b.prototype.setFloat=function(a,b){return this._checkUniform(a),this._floats[a]=b,this},b.prototype.setInt=function(a,b){return this._checkUniform(a),this._ints[a]=b,this},b.prototype.setFloats=function(a,b){return this._checkUniform(a),this._floatsArrays[a]=b,this},b.prototype.setColor3=function(a,b){return this._checkUniform(a),this._colors3[a]=b,this},b.prototype.setColor3Array=function(a,b){return this._checkUniform(a),this._colors3Arrays[a]=b.reduce(function(a,b){return b.toArray(a,a.length),a},[]),this},b.prototype.setColor4=function(a,b){return this._checkUniform(a),this._colors4[a]=b,this},b.prototype.setColor4Array=function(a,b){return this._checkUniform(a),this._colors4Arrays[a]=b.reduce(function(a,b){return b.toArray(a,a.length),a},[]),this},b.prototype.setVector2=function(a,b){return this._checkUniform(a),this._vectors2[a]=b,this},b.prototype.setVector3=function(a,b){return this._checkUniform(a),this._vectors3[a]=b,this},b.prototype.setVector4=function(a,b){return this._checkUniform(a),this._vectors4[a]=b,this},b.prototype.setMatrix=function(a,b){return this._checkUniform(a),this._matrices[a]=b,this},b.prototype.setMatrices=function(a,b){this._checkUniform(a);for(var c=new Float32Array(16*b.length),d=0;d1&&(this._multiview=!0,q.push("#define MULTIVIEW"),-1!==this._options.uniforms.indexOf("viewProjection")&&-1===this._options.uniforms.indexOf("viewProjectionR")&&this._options.uniforms.push("viewProjectionR"));for(var t=0;t4&&(f.push(g.b.MatricesIndicesExtraKind),f.push(g.b.MatricesWeightsExtraKind));u=a.skeleton;q.push("#define NUM_BONE_INFLUENCERS "+a.numBoneInfluencers),h.addCPUSkinningFallback(0,a),u.isUsingTextureForMatrices?(q.push("#define BONETEXTURE"),-1===this._options.uniforms.indexOf("boneTextureWidth")&&this._options.uniforms.push("boneTextureWidth"),-1===this._options.samplers.indexOf("boneSampler")&&this._options.samplers.push("boneSampler")):(q.push("#define BonesPerMesh "+(u.bones.length+1)),-1===this._options.uniforms.indexOf("mBones")&&this._options.uniforms.push("mBones"))}else q.push("#define NUM_BONE_INFLUENCERS 0");u=0;var v=a?a.morphTargetManager:null;if(v){var w=v.supportsUVs&&-1!==q.indexOf("#define UV1"),x=v.supportsTangents&&-1!==q.indexOf("#define TANGENT"),y=v.supportsNormals&&-1!==q.indexOf("#define NORMAL");u=v.numInfluencers,w&&q.push("#define MORPHTARGETS_UV"),x&&q.push("#define MORPHTARGETS_TANGENT"),y&&q.push("#define MORPHTARGETS_NORMAL"),u>0&&q.push("#define MORPHTARGETS"),v.isUsingTextureForTargets&&(q.push("#define MORPHTARGETS_TEXTURE"),-1===this._options.uniforms.indexOf("morphTargetTextureIndices")&&this._options.uniforms.push("morphTargetTextureIndices"),-1===this._options.samplers.indexOf("morphTargets")&&this._options.samplers.push("morphTargets")),q.push("#define NUM_MORPH_INFLUENCERS "+u);for(t=0;t0&&((l=l.slice()).push("morphTargetInfluences"),l.push("morphTargetTextureInfo"),l.push("morphTargetTextureIndices"))}else q.push("#define NUM_MORPH_INFLUENCERS 0");for(v in this._textures)if(!this._textures[v].isReady())return!1;a&&this._shouldTurnAlphaTestOn(a)&&q.push("#define ALPHATEST"),(null===this._options.useClipPlane&&e.clipPlane||this._options.useClipPlane)&&(q.push("#define CLIPPLANE"),-1===l.indexOf("vClipPlane")&&l.push("vClipPlane")),(null===this._options.useClipPlane&&e.clipPlane2||this._options.useClipPlane)&&(q.push("#define CLIPPLANE2"),-1===l.indexOf("vClipPlane2")&&l.push("vClipPlane2")),(null===this._options.useClipPlane&&e.clipPlane3||this._options.useClipPlane)&&(q.push("#define CLIPPLANE3"),-1===l.indexOf("vClipPlane3")&&l.push("vClipPlane3")),(null===this._options.useClipPlane&&e.clipPlane4||this._options.useClipPlane)&&(q.push("#define CLIPPLANE4"),-1===l.indexOf("vClipPlane4")&&l.push("vClipPlane4")),(null===this._options.useClipPlane&&e.clipPlane5||this._options.useClipPlane)&&(q.push("#define CLIPPLANE5"),-1===l.indexOf("vClipPlane5")&&l.push("vClipPlane5")),(null===this._options.useClipPlane&&e.clipPlane6||this._options.useClipPlane)&&(q.push("#define CLIPPLANE6"),-1===l.indexOf("vClipPlane6")&&l.push("vClipPlane6")),this.customShaderNameResolve&&(l=l.slice(),r=r.slice(),s=s.slice(),j=this.customShaderNameResolve(j,l,r,s,q,f));x=d;y=q.join(" ");return this._cachedDefines!==y&&(this._cachedDefines=y,d=p.createEffect(j,{attributes:f,uniformsNames:l,uniformBuffersNames:r,samplers:s,defines:y,fallbacks:h,onCompiled:this.onCompiled,onError:this.onError,indexParameters:{maxSimultaneousMorphTargets:u},shaderLanguage:this._options.shaderLanguage},p),this._drawWrapper.effect=d,this._onEffectCreatedObservable&&(n.effect=d,n.subMesh=null!==(t=null!=c?c:null==a?void 0:a.subMeshes[0])&&void 0!==t?t:null,this._onEffectCreatedObservable.notifyObservers(n))),this._effectUsesInstances=!!b,null!==(w=!(null==d?void 0:d.isReady()))&&void 0!==w&&!w&&(x!==d&&e.resetCachedMaterial(),d._wasPreviouslyReady=!0,!0)},b.prototype.bindOnlyWorldMatrix=function(a,b){var c=this.getScene();b=null!=b?b:this.getEffect();b&&(-1!==this._options.uniforms.indexOf("world")&&b.setMatrix("world",a),-1!==this._options.uniforms.indexOf("worldView")&&(a.multiplyToRef(c.getViewMatrix(),this._cachedWorldViewMatrix),b.setMatrix("worldView",this._cachedWorldViewMatrix)),-1!==this._options.uniforms.indexOf("worldViewProjection")&&(a.multiplyToRef(c.getTransformMatrix(),this._cachedWorldViewProjectionMatrix),b.setMatrix("worldViewProjection",this._cachedWorldViewProjectionMatrix)))},b.prototype.bindForSubMesh=function(a,b,c){this.bind(a,b,null===(a=c._drawWrapperOverride)||void 0===a?void 0:a.effect)},b.prototype.bind=function(a,b,c){this.bindOnlyWorldMatrix(a,c);c=null!=c?c:this.getEffect();var d=this._options.uniformBuffers,e=!1;if(c&&d&&d.length>0&&this.getScene().getEngine().supportsUniformBuffers)for(var f=0;f0&&i.a.BindMorphTargetParameters(b,c)}e=this.getEffect();this._drawWrapper.effect=c,this._afterBind(b,c),this._drawWrapper.effect=e},b.prototype._afterBind=function(b,c){void 0===c&&(c=null),a.prototype._afterBind.call(this,b,c),this.getScene()._cachedEffect=c},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);for(var c in this._textures)b.push(this._textures[c]);for(var c in this._textureArrays)for(var d=this._textureArrays[c],e=0;e1)throw"Multiple drag modes specified in dragBehavior options. Only one expected"}return Object.defineProperty(a.prototype,"currentDraggingPointerID",{get:function(){return this.currentDraggingPointerId},set:function(a){this.currentDraggingPointerId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enabled",{get:function(){return this._enabled},set:function(a){a!=this._enabled&&this.onEnabledObservable.notifyObservers(a),this._enabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"options",{get:function(){return this._options},set:function(a){this._options=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"PointerDrag"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(b,c){var f=this;this._scene=b.getScene(),b.isNearGrabbable=!0,this.attachedNode=b,a._planeScene||(this._debugMode?a._planeScene=this._scene:(a._planeScene=new e.a(this._scene.getEngine(),{virtual:!0}),a._planeScene.detachControl(),this._scene.onDisposeObservable.addOnce(function(){a._planeScene.dispose(),a._planeScene=null}))),this._dragPlane=Object(k.a)("pointerDragPlane",{size:this._debugMode?1:1e4,updatable:!1,sideOrientation:d.a.DOUBLESIDE},a._planeScene),this.lastDragPosition=new g.e(0,0,0);var l=c||function(a){return f.attachedNode==a||a.isDescendantOf(f.attachedNode)};this._pointerObserver=this._scene.onPointerObservable.add(function(b,c){if(f.enabled){if(b.type==h.a.POINTERDOWN)f.startAndReleaseDragOnPointerEvents&&!f.dragging&&b.pickInfo&&b.pickInfo.hit&&b.pickInfo.pickedMesh&&b.pickInfo.pickedPoint&&b.pickInfo.ray&&l(b.pickInfo.pickedMesh)&&f._startDrag(b.event.pointerId,b.pickInfo.ray,b.pickInfo.pickedPoint);else if(b.type==h.a.POINTERUP)f.startAndReleaseDragOnPointerEvents&&f.currentDraggingPointerId==b.event.pointerId&&f.releaseDrag();else if(b.type==h.a.POINTERMOVE){c=b.event.pointerId;if(f.currentDraggingPointerId===a._AnyMouseId&&c!==a._AnyMouseId){var d=b.event;("mouse"===d.pointerType||!f._scene.getEngine().hostInformation.isMobile&&d instanceof MouseEvent)&&(f._lastPointerRay[f.currentDraggingPointerId]&&(f._lastPointerRay[c]=f._lastPointerRay[f.currentDraggingPointerId],delete f._lastPointerRay[f.currentDraggingPointerId]),f.currentDraggingPointerId=c)}f._lastPointerRay[c]||(f._lastPointerRay[c]=new i.a(new g.e,new g.e)),b.pickInfo&&b.pickInfo.ray&&(f._lastPointerRay[c].origin.copyFrom(b.pickInfo.ray.origin),f._lastPointerRay[c].direction.copyFrom(b.pickInfo.ray.direction),f.currentDraggingPointerId==c&&f.dragging&&f._moveDrag(b.pickInfo.ray))}}else f._attachedToElement&&f.releaseDrag()}),this._beforeRenderObserver=this._scene.onBeforeRenderObservable.add(function(){f._moving&&f.moveAttached&&(j.a._RemoveAndStorePivotPoint(f.attachedNode),f._targetPosition.subtractToRef(f.attachedNode.absolutePosition,f._tmpVector),f._tmpVector.scaleInPlace(f.dragDeltaRatio),f.attachedNode.getAbsolutePosition().addToRef(f._tmpVector,f._tmpVector),f.validateDrag(f._tmpVector)&&f.attachedNode.setAbsolutePosition(f._tmpVector),j.a._RestorePivotPoint(f.attachedNode))})},a.prototype.releaseDrag=function(){if(this.dragging&&(this.dragging=!1,this.onDragEndObservable.notifyObservers({dragPlanePoint:this.lastDragPosition,pointerId:this.currentDraggingPointerId})),this.currentDraggingPointerId=-1,this._moving=!1,this.detachCameraControls&&this._attachedToElement&&this._scene.activeCamera&&!this._scene.activeCamera.leftCamera){if("ArcRotateCamera"===this._scene.activeCamera.getClassName()){var a=this._scene.activeCamera;a.attachControl(!a.inputs||a.inputs.noPreventDefault,a._useCtrlForPanning,a._panningMouseButton)}else this._scene.activeCamera.attachControl(!this._scene.activeCamera.inputs||this._scene.activeCamera.inputs.noPreventDefault);this._attachedToElement=!1}},a.prototype.startDrag=function(b,c,d){void 0===b&&(b=a._AnyMouseId),this._startDrag(b,c,d);c=this._lastPointerRay[b];b===a._AnyMouseId&&(c=this._lastPointerRay[Object.keys(this._lastPointerRay)[0]]),c&&this._moveDrag(c)},a.prototype._startDrag=function(a,b,c){if(this._scene.activeCamera&&!this.dragging&&this.attachedNode){j.a._RemoveAndStorePivotPoint(this.attachedNode),b?(this._startDragRay.direction.copyFrom(b.direction),this._startDragRay.origin.copyFrom(b.origin)):(this._startDragRay.origin.copyFrom(this._scene.activeCamera.position),this.attachedNode.getWorldMatrix().getTranslationToRef(this._tmpVector),this._tmpVector.subtractToRef(this._scene.activeCamera.position,this._startDragRay.direction)),this._updateDragPlanePosition(this._startDragRay,c||this._tmpVector);b=this._pickWithRayOnDragPlane(this._startDragRay);b&&(this.dragging=!0,this.currentDraggingPointerId=a,this.lastDragPosition.copyFrom(b),this.onDragStartObservable.notifyObservers({dragPlanePoint:b,pointerId:this.currentDraggingPointerId}),this._targetPosition.copyFrom(this.attachedNode.getAbsolutePosition()),this.detachCameraControls&&this._scene.activeCamera&&this._scene.activeCamera.inputs&&!this._scene.activeCamera.leftCamera&&(this._scene.activeCamera.inputs.attachedToElement?(this._scene.activeCamera.detachControl(),this._attachedToElement=!0):this._attachedToElement=!1)),j.a._RestorePivotPoint(this.attachedNode)}},a.prototype._moveDrag=function(a){this._moving=!0;var b=this._pickWithRayOnDragPlane(a);if(b){this.updateDragPlane&&this._updateDragPlanePosition(a,b);a=0;this._options.dragAxis?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragAxis,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._worldDragAxis):this._worldDragAxis.copyFrom(this._options.dragAxis),b.subtractToRef(this.lastDragPosition,this._tmpVector),a=g.e.Dot(this._tmpVector,this._worldDragAxis),this._worldDragAxis.scaleToRef(a,this._dragDelta)):(a=this._dragDelta.length(),b.subtractToRef(this.lastDragPosition,this._dragDelta)),this._targetPosition.addInPlace(this._dragDelta),this.onDragObservable.notifyObservers({dragDistance:a,delta:this._dragDelta,dragPlanePoint:b,dragPlaneNormal:this._dragPlane.forward,pointerId:this.currentDraggingPointerId}),this.lastDragPosition.copyFrom(b)}},a.prototype._pickWithRayOnDragPlane=function(b){var c=this;if(!b)return null;var d=Math.acos(g.e.Dot(this._dragPlane.forward,b.direction));if(d>Math.PI/2&&(d=Math.PI-d),this.maxDragAngle>0&&d>this.maxDragAngle){if(this._useAlternatePickedPointAboveMaxDragAngle){this._tmpVector.copyFrom(b.direction),this.attachedNode.absolutePosition.subtractToRef(b.origin,this._alternatePickedPoint),this._alternatePickedPoint.normalize(),this._alternatePickedPoint.scaleInPlace(this._useAlternatePickedPointAboveMaxDragAngleDragSpeed*g.e.Dot(this._alternatePickedPoint,this._tmpVector)),this._tmpVector.addInPlace(this._alternatePickedPoint);d=g.e.Dot(this._dragPlane.forward,this._tmpVector);return this._dragPlane.forward.scaleToRef(-d,this._alternatePickedPoint),this._alternatePickedPoint.addInPlace(this._tmpVector),this._alternatePickedPoint.addInPlace(this.attachedNode.absolutePosition),this._alternatePickedPoint}return null}d=a._planeScene.pickWithRay(b,function(a){return a==c._dragPlane});return d&&d.hit&&d.pickedMesh&&d.pickedPoint?d.pickedPoint:null},a.prototype._updateDragPlanePosition=function(a,b){this._pointA.copyFrom(b),this._options.dragAxis?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragAxis,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis):this._localAxis.copyFrom(this._options.dragAxis),a.origin.subtractToRef(this._pointA,this._pointC),this._pointC.normalize(),Math.abs(g.e.Dot(this._localAxis,this._pointC))>.999?Math.abs(g.e.Dot(g.e.UpReadOnly,this._pointC))>.999?this._lookAt.copyFrom(g.e.Right()):this._lookAt.copyFrom(g.e.UpReadOnly):(g.e.CrossToRef(this._localAxis,this._pointC,this._lookAt),g.e.CrossToRef(this._localAxis,this._lookAt,this._lookAt),this._lookAt.normalize()),this._dragPlane.position.copyFrom(this._pointA),this._pointA.addToRef(this._lookAt,this._lookAt),this._dragPlane.lookAt(this._lookAt)):this._options.dragPlaneNormal?(this.useObjectOrientationForDragging?g.e.TransformCoordinatesToRef(this._options.dragPlaneNormal,this.attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis):this._localAxis.copyFrom(this._options.dragPlaneNormal),this._dragPlane.position.copyFrom(this._pointA),this._pointA.addToRef(this._localAxis,this._lookAt),this._dragPlane.lookAt(this._lookAt)):(this._dragPlane.position.copyFrom(this._pointA),this._dragPlane.lookAt(a.origin)),this._dragPlane.position.copyFrom(this.attachedNode.getAbsolutePosition()),this._dragPlane.computeWorldMatrix(!0)},a.prototype.detach=function(){this.attachedNode.isNearGrabbable=!1,this._pointerObserver&&this._scene.onPointerObservable.remove(this._pointerObserver),this._beforeRenderObserver&&this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver),this._dragPlane.dispose(),this.releaseDrag()},a._AnyMouseId=-2,a}()},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f}),c.d(b,"c",function(){return g});var d=c(2),e=function(){function a(){}return a.KEYDOWN=1,a.KEYUP=2,a}(),f=function(a,b){this.type=a,this.event=b},g=function(a){function b(b,c){var d=a.call(this,b,c)||this;return d.type=b,d.event=c,d.skipOnPointerObservable=!1,d}return Object(d.d)(b,a),b}(f)},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._defines={},this._currentRank=32,this._maxRank=-1,this._mesh=null}return a.prototype.unBindMesh=function(){this._mesh=null},a.prototype.addFallback=function(a,b){this._defines[a]||(athis._maxRank&&(this._maxRank=a),this._defines[a]=new Array),this._defines[a].push(b)},a.prototype.addCPUSkinningFallback=function(a,b){this._mesh=b,athis._maxRank&&(this._maxRank=a)},Object.defineProperty(a.prototype,"hasMoreFallbacks",{get:function(){return this._currentRank<=this._maxRank},enumerable:!1,configurable:!0}),a.prototype.reduce=function(a,b){if(this._mesh&&this._mesh.computeBonesUsingShaders&&this._mesh.numBoneInfluencers>0){this._mesh.computeBonesUsingShaders=!1,a=a.replace("#define NUM_BONE_INFLUENCERS "+this._mesh.numBoneInfluencers,"#define NUM_BONE_INFLUENCERS 0"),b._bonesComputationForcedToCPU=!0;for(var c=this._mesh.getScene(),d=0;d0&&(e.computeBonesUsingShaders=!1)}}else{g=this._defines[this._currentRank];if(g)for(d=0;d=this.subMaterials.length?this.getScene().defaultMaterial:this.subMaterials[a]},b.prototype.getActiveTextures=function(){var b;return(b=a.prototype.getActiveTextures.call(this)).concat.apply(b,this.subMaterials.map(function(a){return a?a.getActiveTextures():[]}))},b.prototype.hasTexture=function(b){var c;if(a.prototype.hasTexture.call(this,b))return!0;for(var d=0;d=0&&e.multiMaterials.splice(d,1),a.prototype.dispose.call(this,b,c)}},b.ParseMultiMaterial=function(a,c){var d=new b(a.name,c);d.id=a.id,e.a&&e.a.AddTagsTo(d,a.tags);for(var f=0;f0&&(this._indexBuffer=this._engine.createIndexBuffer(this._indices,this._updatable)),a._syncGeometryWithMorphTargetManager(),a.synchronizeInstances()},a.prototype.notifyUpdate=function(a){this.onGeometryUpdated&&this.onGeometryUpdated(this,a),this._vertexArrayObjects&&this._disposeVertexArrayObjects();for(var a=0,b=this._meshes;a0){for(var b=0;b0){for(b=0;b0){for(b=0;b-1&&this._parentContainer.geometries.splice(c,1),this._parentContainer=null}this._isDisposed=!0},a.prototype.copy=function(b){var c=new f.a;c.indices=[];var d=this.getIndices();if(d)for(var e=0;e0){var i=new Float32Array(b,f.positionsAttrDesc.offset,f.positionsAttrDesc.count);c.setVerticesData(g.b.PositionKind,i,!1)}if(f.normalsAttrDesc&&f.normalsAttrDesc.count>0){i=new Float32Array(b,f.normalsAttrDesc.offset,f.normalsAttrDesc.count);c.setVerticesData(g.b.NormalKind,i,!1)}if(f.tangetsAttrDesc&&f.tangetsAttrDesc.count>0){i=new Float32Array(b,f.tangetsAttrDesc.offset,f.tangetsAttrDesc.count);c.setVerticesData(g.b.TangentKind,i,!1)}if(f.uvsAttrDesc&&f.uvsAttrDesc.count>0){i=new Float32Array(b,f.uvsAttrDesc.offset,f.uvsAttrDesc.count);c.setVerticesData(g.b.UVKind,i,!1)}if(f.uvs2AttrDesc&&f.uvs2AttrDesc.count>0){i=new Float32Array(b,f.uvs2AttrDesc.offset,f.uvs2AttrDesc.count);c.setVerticesData(g.b.UV2Kind,i,!1)}if(f.uvs3AttrDesc&&f.uvs3AttrDesc.count>0){i=new Float32Array(b,f.uvs3AttrDesc.offset,f.uvs3AttrDesc.count);c.setVerticesData(g.b.UV3Kind,i,!1)}if(f.uvs4AttrDesc&&f.uvs4AttrDesc.count>0){i=new Float32Array(b,f.uvs4AttrDesc.offset,f.uvs4AttrDesc.count);c.setVerticesData(g.b.UV4Kind,i,!1)}if(f.uvs5AttrDesc&&f.uvs5AttrDesc.count>0){i=new Float32Array(b,f.uvs5AttrDesc.offset,f.uvs5AttrDesc.count);c.setVerticesData(g.b.UV5Kind,i,!1)}if(f.uvs6AttrDesc&&f.uvs6AttrDesc.count>0){i=new Float32Array(b,f.uvs6AttrDesc.offset,f.uvs6AttrDesc.count);c.setVerticesData(g.b.UV6Kind,i,!1)}if(f.colorsAttrDesc&&f.colorsAttrDesc.count>0){i=new Float32Array(b,f.colorsAttrDesc.offset,f.colorsAttrDesc.count);c.setVerticesData(g.b.ColorKind,i,!1,f.colorsAttrDesc.stride)}if(f.matricesIndicesAttrDesc&&f.matricesIndicesAttrDesc.count>0){for(var i=new Int32Array(b,f.matricesIndicesAttrDesc.offset,f.matricesIndicesAttrDesc.count),j=[],k=0;k>8),j.push((16711680&l)>>16),j.push(l>>24&255)}c.setVerticesData(g.b.MatricesIndicesKind,j,!1)}if(f.matricesIndicesExtraAttrDesc&&f.matricesIndicesExtraAttrDesc.count>0){for(i=new Int32Array(b,f.matricesIndicesExtraAttrDesc.offset,f.matricesIndicesExtraAttrDesc.count),j=[],k=0;k>8),j.push((16711680&l)>>16),j.push(l>>24&255));c.setVerticesData(g.b.MatricesIndicesExtraKind,j,!1)}if(f.matricesWeightsAttrDesc&&f.matricesWeightsAttrDesc.count>0){l=new Float32Array(b,f.matricesWeightsAttrDesc.offset,f.matricesWeightsAttrDesc.count);c.setVerticesData(g.b.MatricesWeightsKind,l,!1)}if(f.indicesAttrDesc&&f.indicesAttrDesc.count>0){i=new Int32Array(b,f.indicesAttrDesc.offset,f.indicesAttrDesc.count);c.setIndices(i,null)}if(f.subMeshesAttrDesc&&f.subMeshesAttrDesc.count>0){l=new Int32Array(b,f.subMeshesAttrDesc.offset,5*f.subMeshesAttrDesc.count);c.subMeshes=[];for(k=0;k>8),j.push((16711680&i)>>16),j.push(i>>24&255)}c.setVerticesData(g.b.MatricesIndicesKind,j,b.matricesIndices._updatable)}if(b.matricesIndicesExtra)if(b.matricesIndicesExtra._isExpanded)delete b.matricesIndices._isExpanded,c.setVerticesData(g.b.MatricesIndicesExtraKind,b.matricesIndicesExtra,b.matricesIndicesExtra._updatable);else{for(j=[],k=0;k>8),j.push((16711680&i)>>16),j.push(i>>24&255));c.setVerticesData(g.b.MatricesIndicesExtraKind,j,b.matricesIndicesExtra._updatable)}b.matricesWeights&&(a._CleanMatricesWeights(b,c),c.setVerticesData(g.b.MatricesWeightsKind,b.matricesWeights,b.matricesWeights._updatable)),b.matricesWeightsExtra&&c.setVerticesData(g.b.MatricesWeightsExtraKind,b.matricesWeightsExtra,b.matricesWeights._updatable),c.setIndices(b.indices,null)}if(b.subMeshes){c.subMeshes=[];for(m=0;m-1){var d=b.getScene().getLastSkeletonById(a.skeletonId);if(d){c=d.bones.length;for(var d=b.getVerticesData(g.b.MatricesIndicesKind),e=b.getVerticesData(g.b.MatricesIndicesExtraKind),f=a.matricesWeights,h=a.matricesWeightsExtra,j=a.numBoneInfluencer,k=f.length,l=0;lj-1)&&(o=j-1),n>.001){q=1/n;for(p=0;p<4;p++)f[l+p]*=q;if(h)for(p=0;p<4;p++)h[l+p]*=q}else o>=4?(h[l+o-4]=1-n,e[l+o-4]=c):(f[l+o]=1-n,d[l+o]=c)}b.setVerticesData(g.b.MatricesIndicesKind,d),a.matricesWeightsExtra&&b.setVerticesData(g.b.MatricesIndicesExtraKind,e)}}}},a.Parse=function(b,c,e){if(c.getGeometryById(b.id))return null;var h=new a(b.id,c,void 0,b.updatable);return m.a&&m.a.AddTagsTo(h,b.tags),b.delayLoadingFile?(h.delayLoadState=k.a.DELAYLOADSTATE_NOTLOADED,h.delayLoadingFile=e+b.delayLoadingFile,h._boundingInfo=new j.a(d.e.FromArray(b.boundingBoxMinimum),d.e.FromArray(b.boundingBoxMaximum)),h._delayInfo=[],b.hasUVs&&h._delayInfo.push(g.b.UVKind),b.hasUVs2&&h._delayInfo.push(g.b.UV2Kind),b.hasUVs3&&h._delayInfo.push(g.b.UV3Kind),b.hasUVs4&&h._delayInfo.push(g.b.UV4Kind),b.hasUVs5&&h._delayInfo.push(g.b.UV5Kind),b.hasUVs6&&h._delayInfo.push(g.b.UV6Kind),b.hasColors&&h._delayInfo.push(g.b.ColorKind),b.hasMatricesIndices&&h._delayInfo.push(g.b.MatricesIndicesKind),b.hasMatricesWeights&&h._delayInfo.push(g.b.MatricesWeightsKind),h._delayLoadingFunction=f.a.ImportVertexData):f.a.ImportVertexData(b,h),c.pushGeometry(h,!0),h},a}()},function(a,b,c){c.d(b,"e",function(){return d}),c.d(b,"c",function(){return h}),c.d(b,"a",function(){return i}),c.d(b,"b",function(){return j}),c.d(b,"f",function(){return k}),c.d(b,"g",function(){return l}),c.d(b,"d",function(){return m});var d,e=c(14),f=c(0),g=c(22);!function(a){a[a.CW=0]="CW",a[a.CCW=1]="CCW"}(d||(d={}));var h=function(){function a(){}return a.Interpolate=function(a,b,c,d,e){for(var f=1-3*d+3*b,d=3*d-6*b,b=3*b,g=a,h=0;h<5;h++){var i=g*g;g-=(f*(i*g)+d*i+b*g-a)*(1/(3*f*i+2*d*g+b)),g=Math.min(1,Math.max(0,g))}return 3*Math.pow(1-g,2)*g*c+3*(1-g)*Math.pow(g,2)*e+Math.pow(g,3)},a}(),i=function(){function a(a){this._radians=a,this._radians<0&&(this._radians+=2*Math.PI)}return a.prototype.degrees=function(){return 180*this._radians/Math.PI},a.prototype.radians=function(){return this._radians},a.BetweenTwoPoints=function(b,c){c=c.subtract(b);return new a(Math.atan2(c.y,c.x))},a.FromRadians=function(b){return new a(b)},a.FromDegrees=function(b){return new a(b*Math.PI/180)},a}(),j=function(a,b,c){this.startPoint=a,this.midPoint=b,this.endPoint=c;var e=Math.pow(b.x,2)+Math.pow(b.y,2),g=(Math.pow(a.x,2)+Math.pow(a.y,2)-e)/2;e=(e-Math.pow(c.x,2)-Math.pow(c.y,2))/2;var h=(a.x-b.x)*(b.y-c.y)-(b.x-c.x)*(a.y-b.y);this.centerPoint=new f.d((g*(b.y-c.y)-e*(a.y-b.y))/h,((a.x-b.x)*e-(b.x-c.x)*g)/h),this.radius=this.centerPoint.subtract(this.startPoint).length(),this.startAngle=i.BetweenTwoPoints(this.centerPoint,this.startPoint);a=this.startAngle.degrees();e=i.BetweenTwoPoints(this.centerPoint,this.midPoint).degrees();b=i.BetweenTwoPoints(this.centerPoint,this.endPoint).degrees();e-a>180&&(e-=360),e-a<-180&&(e+=360),b-e>180&&(b-=360),b-e<-180&&(b+=360),this.orientation=e-a<0?d.CW:d.CCW,this.angle=i.FromDegrees(this.orientation===d.CW?a-b:b-a)},k=function(){function a(a,b){this._points=new Array,this._length=0,this.closed=!1,this._points.push(new f.d(a,b))}return a.prototype.addLineTo=function(a,b){if(this.closed)return this;a=new f.d(a,b);b=this._points[this._points.length-1];return this._points.push(a),this._length+=a.subtract(b).length(),this},a.prototype.addArcTo=function(a,b,c,e,g){if(void 0===g&&(g=36),this.closed)return this;var h=this._points[this._points.length-1];a=new f.d(a,b);b=new f.d(c,e);c=new j(h,a,b);e=c.angle.radians()/g;c.orientation===d.CW&&(e*=-1);for(h=c.startAngle.radians()+e,a=0;a1)return f.d.Zero();for(var a=a*this.length(),b=0,c=0;c=b&&a<=g){d=d.normalize();var h=a-b;return new f.d(e.x+d.x*h,e.y+d.y*h)}b=g}return f.d.Zero()},a.StartingAt=function(b,c){return new a(b,c)},a}(),l=function(){function a(a,b,c,d){void 0===b&&(b=null),void 0===d&&(d=!1),this.path=a,this._curve=new Array,this._distances=new Array,this._tangents=new Array,this._normals=new Array,this._binormals=new Array,this._pointAtData={id:0,point:f.e.Zero(),previousPointArrayIndex:0,position:0,subPosition:0,interpolateReady:!1,interpolationMatrix:f.a.Identity()};for(var e=0;ec){var d=b;b=c,c=d}d=this.getCurve();var e=this.getPointAt(b),f=this.getPreviousPointIndexAt(b),g=this.getPointAt(c),h=this.getPreviousPointIndexAt(c)+1,i=[];return 0!==b&&(f++,i.push(e)),i.push.apply(i,d.slice(f,h)),1===c&&1!==b||i.push(g),new a(i,this.getNormalAt(b),this._raw,this._alignTangentsWithPath)},a.prototype.update=function(a,b,c){void 0===b&&(b=null),void 0===c&&(c=!1);for(var d=0;db+1;)b++,c=this._curve[a].subtract(this._curve[a-b]);return c},a.prototype._normalVector=function(a,b){var c=a.length();(0===c&&(c=1),null==b)?(c=e.a.WithinEpsilon(Math.abs(a.y)/c,1,g.a)?e.a.WithinEpsilon(Math.abs(a.x)/c,1,g.a)?e.a.WithinEpsilon(Math.abs(a.z)/c,1,g.a)?f.e.Zero():new f.e(0,0,1):new f.e(1,0,0):new f.e(0,-1,0),c=f.e.Cross(a,c)):(c=f.e.Cross(a,b),f.e.CrossToRef(c,a,c));return c.normalize(),c},a.prototype._updatePointAtData=function(a,b){if(void 0===b&&(b=!1),this._pointAtData.id===a)return this._pointAtData.interpolateReady||this._updateInterpolationMatrix(),this._pointAtData;this._pointAtData.id=a;var c=this.getPoints();if(a<=0)return this._setPointAtData(0,0,c[0],0,b);if(a>=1)return this._setPointAtData(1,1,c[c.length-1],c.length-1,b);for(var d,e=c[0],g=0,h=a*this.length(),i=1;ih){j=(g-h)/j;var k=e.subtract(d);k=d.add(k.scaleInPlace(j));return this._setPointAtData(a,1-j,k,i-1,b)}e=d}return this._pointAtData},a.prototype._setPointAtData=function(a,b,c,d,e){return this._pointAtData.point=c,this._pointAtData.position=a,this._pointAtData.subPosition=b,this._pointAtData.previousPointArrayIndex=d,this._pointAtData.interpolateReady=e,e&&this._updateInterpolationMatrix(),this._pointAtData},a.prototype._updateInterpolationMatrix=function(){this._pointAtData.interpolationMatrix=f.a.Identity();var a=this._pointAtData.previousPointArrayIndex;if(a!==this._tangents.length-1){var b=a+1,c=this._tangents[a].clone(),d=this._normals[a].clone();a=this._binormals[a].clone();var e=this._tangents[b].clone(),g=this._normals[b].clone();b=this._binormals[b].clone();d=f.b.RotationQuaternionFromAxis(d,a,c);a=f.b.RotationQuaternionFromAxis(g,b,e);f.b.Slerp(d,a,this._pointAtData.subPosition).toRotationMatrix(this._pointAtData.interpolationMatrix)}},a}(),m=function(){function a(a){this._length=0,this._points=a,this._length=this._computeLength(a)}return a.CreateQuadraticBezier=function(b,c,d,e){e=e>2?e:3;for(var g=new Array,h=function(a,b,c,d){return(1-a)*(1-a)*b+2*a*(1-a)*c+a*a*d},i=0;i<=e;i++)g.push(new f.e(h(i/e,b.x,c.x,d.x),h(i/e,b.y,c.y,d.y),h(i/e,b.z,c.z,d.z)));return new a(g)},a.CreateCubicBezier=function(b,c,d,e,g){g=g>3?g:4;for(var h=new Array,i=function(a,b,c,d,e){return(1-a)*(1-a)*(1-a)*b+3*a*(1-a)*(1-a)*c+3*a*a*(1-a)*d+a*a*a*e},j=0;j<=g;j++)h.push(new f.e(i(j/g,b.x,c.x,d.x,e.x),i(j/g,b.y,c.y,d.y,e.y),i(j/g,b.z,c.z,d.z,e.z)));return new a(h)},a.CreateHermiteSpline=function(b,c,d,e,g){for(var h=new Array,i=1/g,j=0;j<=g;j++)h.push(f.e.Hermite(b,c,d,e,j*i));return new a(h)},a.CreateCatmullRomSpline=function(b,c,d){var e=new Array,g=1/c,h=0;if(d){for(var d=b.length,i=0;io.snapDistance){var c=Math.floor(Math.abs(p)/o.snapDistance);p%=o.snapDistance,a.delta.normalizeToRef(q),q.scaleInPlace(o.snapDistance*c),o.attachedNode.getWorldMatrix().getTranslationToRef(r),r.addInPlace(q),o.dragBehavior.validateDrag(r)&&(o.attachedNode.getWorldMatrix().addTranslationFromFloats(q.x,q.y,q.z),o.attachedNode.updateCache(),s.snapDistance=o.snapDistance*c,o.onSnapObservable.notifyObservers(s),b=!0)}b&&o._matrixChanged()}}),o.dragBehavior.onDragStartObservable.add(function(){o._dragging=!0}),o.dragBehavior.onDragEndObservable.add(function(){o._dragging=!1});n=g._getSharedGizmoLight();n.includedOnlyMeshes=n.includedOnlyMeshes.concat(o._rootMesh.getChildMeshes(!1));var t={gizmoMeshes:i.getChildMeshes(),colliderMeshes:d.getChildMeshes(),material:o._coloredMaterial,hoverMaterial:o._hoverMaterial,disableMaterial:o._disableMaterial,active:!1,dragBehavior:o.dragBehavior};return null===(c=o._parent)||void 0===c||c.addToAxisCache(d,t),o._pointerObserver=g.utilityLayerScene.onPointerObservable.add(function(a){if(!o._customMeshSet&&(o._isHovered=!(-1==t.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!o._parent)){a=o.dragBehavior.enabled?o._isHovered||o._dragging?o._hoverMaterial:o._coloredMaterial:o._disableMaterial;o._setGizmoMeshMaterial(t.gizmoMeshes,a)}}),o.dragBehavior.onEnabledObservable.add(function(a){o._setGizmoMeshMaterial(t.gizmoMeshes,a?t.material:t.disableMaterial)}),o}return Object(d.d)(b,a),b._CreateArrow=function(a,b,c,d){void 0===c&&(c=1),void 0===d&&(d=!1);var e=new g.a("arrow",a),f=Object(i.a)("cylinder",{diameterTop:0,height:.075,diameterBottom:.0375*(1+(c-1)/4),tessellation:96},a);c=Object(i.a)("cylinder",{diameterTop:.005*c,height:.275,diameterBottom:.005*c,tessellation:96},a);return f.parent=e,f.material=b,f.rotation.x=Math.PI/2,f.position.z+=.3,c.parent=e,c.material=b,c.position.z+=.1375,c.rotation.x=Math.PI/2,d&&(c.visibility=0,f.visibility=0),e},b._CreateArrowInstance=function(a,b){for(var a=new g.a("arrow",a),c=0,b=b.getChildMeshes();ch?h:Math.floor(i);var j,k,l;h=0===a.sideOrientation?0:a.sideOrientation||g.a.DEFAULTSIDE;var m=a.uvs,n=a.colors,o=[],p=[],q=[],r=[],s=[],t=[],u=[],v=[],w=[],x=[];if(b.length<2){var y=[],z=[];for(k=0;k0&&(C=A[l].subtract(A[l-1]).length()+u[j],s[j].push(C),u[j]=C),l++;d&&(l--,o.push(A[0].x,A[0].y,A[0].z),C=A[l].subtract(A[0]).length()+u[j],s[j].push(C),u[j]=C),w[j]=B+y,x[j]=i,i+=B+y}l=null;A=null;for(k=0;k0 #ifdef BONETEXTURE uniform sampler2D boneSampler; uniform float boneTextureWidth; #else uniform mat4 mBones[BonesPerMesh]; #ifdef BONES_VELOCITY_ENABLED uniform mat4 mPreviousBones[BonesPerMesh]; #endif #endif attribute vec4 matricesIndices; attribute vec4 matricesWeights; #if NUM_BONE_INFLUENCERS>4 attribute vec4 matricesIndicesExtra; attribute vec4 matricesWeightsExtra; #endif #ifdef BONETEXTURE #define inline mat4 readMatrixFromRawSampler(sampler2D smp,float index) { float offset=index*4.0; float dx=1.0/boneTextureWidth; vec4 m0=texture2D(smp,vec2(dx*(offset+0.5),0.)); vec4 m1=texture2D(smp,vec2(dx*(offset+1.5),0.)); vec4 m2=texture2D(smp,vec2(dx*(offset+2.5),0.)); vec4 m3=texture2D(smp,vec2(dx*(offset+3.5),0.)); return mat4(m0,m1,m2,m3); } #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="instancesVertex";b="#ifdef INSTANCES mat4 finalWorld=mat4(world0,world1,world2,world3); #if defined(PREPASS_VELOCITY) || defined(VELOCITY) mat4 finalPreviousWorld=mat4(previousWorld0,previousWorld1,previousWorld2,previousWorld3); #endif #ifdef THIN_INSTANCES finalWorld=world*finalWorld; #if defined(PREPASS_VELOCITY) || defined(VELOCITY) finalPreviousWorld=previousWorld*finalPreviousWorld; #endif #endif #else mat4 finalWorld=world; #if defined(PREPASS_VELOCITY) || defined(VELOCITY) mat4 finalPreviousWorld=previousWorld; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#if NUM_BONE_INFLUENCERS>0 mat4 influence; #ifdef BONETEXTURE influence=readMatrixFromRawSampler(boneSampler,matricesIndices[0])*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[1])*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[2])*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence+=readMatrixFromRawSampler(boneSampler,matricesIndices[3])*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[0])*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[1])*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[2])*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence+=readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[3])*matricesWeightsExtra[3]; #endif #else influence=mBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence+=mBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence+=mBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence+=mBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence+=mBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence+=mBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence+=mBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence+=mBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif #endif finalWorld=finalWorld*influence; #endif";c(5).a.IncludesShadersStore.bonesVertex=a},function(a,b,c){c.d(b,"a",function(){return f});var d=c(1),e=c(84),f=function(){function a(a,b){void 0===b&&(b=!0),this._wrapU=d.a.TEXTURE_WRAP_ADDRESSMODE,this._wrapV=d.a.TEXTURE_WRAP_ADDRESSMODE,this.wrapR=d.a.TEXTURE_WRAP_ADDRESSMODE,this.anisotropicFilteringLevel=4,this.delayLoadState=d.a.DELAYLOADSTATE_NONE,this._texture=null,this._engine=null,this._cachedSize=e.a.Zero(),this._cachedBaseSize=e.a.Zero(),this._initialSamplingMode=d.a.TEXTURE_BILINEAR_SAMPLINGMODE,this._texture=a,this._texture&&b&&(this._engine=this._texture.getEngine())}return Object.defineProperty(a.prototype,"wrapU",{get:function(){return this._wrapU},set:function(a){this._wrapU=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapV",{get:function(){return this._wrapV},set:function(a){this._wrapV=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"coordinatesMode",{get:function(){return 0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isCube",{get:function(){return!!this._texture&&this._texture.isCube},set:function(a){this._texture&&(this._texture.isCube=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is3D",{get:function(){return!!this._texture&&this._texture.is3D},set:function(a){this._texture&&(this._texture.is3D=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is2DArray",{get:function(){return!!this._texture&&this._texture.is2DArray},set:function(a){this._texture&&(this._texture.is2DArray=a)},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"ThinTexture"},a.prototype.isReady=function(){return this.delayLoadState===d.a.DELAYLOADSTATE_NOTLOADED?(this.delayLoad(),!1):!!this._texture&&this._texture.isReady},a.prototype.delayLoad=function(){},a.prototype.getInternalTexture=function(){return this._texture},a.prototype.getSize=function(){if(this._texture){if(this._texture.width)return this._cachedSize.width=this._texture.width,this._cachedSize.height=this._texture.height,this._cachedSize;if(this._texture._size)return this._cachedSize.width=this._texture._size,this._cachedSize.height=this._texture._size,this._cachedSize}return this._cachedSize},a.prototype.getBaseSize=function(){return this.isReady()&&this._texture?this._texture._size?(this._cachedBaseSize.width=this._texture._size,this._cachedBaseSize.height=this._texture._size,this._cachedBaseSize):(this._cachedBaseSize.width=this._texture.baseWidth,this._cachedBaseSize.height=this._texture.baseHeight,this._cachedBaseSize):(this._cachedBaseSize.width=0,this._cachedBaseSize.height=0,this._cachedBaseSize)},Object.defineProperty(a.prototype,"samplingMode",{get:function(){return this._texture?this._texture.samplingMode:this._initialSamplingMode},enumerable:!1,configurable:!0}),a.prototype.updateSamplingMode=function(a){this._texture&&this._engine&&this._engine.updateTextureSamplingMode(a,this._texture)},a.prototype.releaseInternalTexture=function(){this._texture&&(this._texture.dispose(),this._texture=null)},a.prototype.dispose=function(){this._texture&&(this.releaseInternalTexture(),this._engine=null)},a}()},function(a,b,c){c.d(b,"a",function(){return s});var d=c(39),e=function(){function a(){this.children=[]}return a.prototype.isValid=function(a){return!0},a.prototype.process=function(a,b){var c="";if(this.line){var e=this.line,f=b.processor;f&&((f.lineProcessor&&(e=f.lineProcessor(e,b.isFragment,b.processingContext)),f.attributeProcessor&&Object(d.g)(this.line,"attribute"))?e=f.attributeProcessor(this.line,a,b.processingContext):f.varyingProcessor&&Object(d.g)(this.line,"varying")?e=f.varyingProcessor(this.line,b.isFragment,a,b.processingContext):f.uniformProcessor&&f.uniformRegexp&&f.uniformRegexp.test(this.line)?b.lookForClosingBracketForUniformBuffer||(e=f.uniformProcessor(this.line,b.isFragment,a,b.processingContext)):f.uniformBufferProcessor&&f.uniformBufferRegexp&&f.uniformBufferRegexp.test(this.line)?b.lookForClosingBracketForUniformBuffer||(e=f.uniformBufferProcessor(this.line,b.isFragment,b.processingContext),b.lookForClosingBracketForUniformBuffer=!0):f.textureProcessor&&f.textureRegexp&&f.textureRegexp.test(this.line)?e=f.textureProcessor(this.line,b.isFragment,a,b.processingContext):(f.uniformProcessor||f.uniformBufferProcessor)&&Object(d.g)(this.line,"uniform")&&!b.lookForClosingBracketForUniformBuffer&&(/uniforms+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/.test(this.line)?f.uniformProcessor&&(e=f.uniformProcessor(this.line,b.isFragment,a,b.processingContext)):f.uniformBufferProcessor&&(e=f.uniformBufferProcessor(this.line,b.isFragment,b.processingContext),b.lookForClosingBracketForUniformBuffer=!0)),b.lookForClosingBracketForUniformBuffer&&-1!==this.line.indexOf("}")&&(b.lookForClosingBracketForUniformBuffer=!1,f.endOfUniformBufferProcessor&&(e=f.endOfUniformBufferProcessor(this.line,b.isFragment,b.processingContext))));c+=e+" "}return this.children.forEach(function(d){c+=d.process(a,b)}),this.additionalDefineKey&&(a[this.additionalDefineKey]=this.additionalDefineValue||"true"),c},a}(),f=function(){function a(){}return Object.defineProperty(a.prototype,"currentLine",{get:function(){return this._lines[this.lineIndex]},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"canRead",{get:function(){return this.lineIndex1){for(e();-1!==d&&a._OperatorPriority[g()]>=a._OperatorPriority[l];)c.push(h());f(l),i++}else j+=k;i++}for(e();-1!==d;)"("===g()?h():c.push(h());return c},a._OperatorPriority={")":0,"(":1,"||":2,"&&":3},a._Stack=["","","","","","","","","","","","","","","","","","","",""],a}(),k=function(a){function b(b,c){void 0===c&&(c=!1);var d=a.call(this)||this;return d.define=b,d.not=c,d}return Object(g.d)(b,a),b.prototype.isTrue=function(a){a=void 0!==a[this.define];return this.not&&(a=!a),a},b}(j),l=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(g.d)(b,a),b.prototype.isTrue=function(a){return this.leftOperand.isTrue(a)||this.rightOperand.isTrue(a)},b}(j),m=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(g.d)(b,a),b.prototype.isTrue=function(a){return this.leftOperand.isTrue(a)&&this.rightOperand.isTrue(a)},b}(j),n=function(a){function b(b,c,d){var e=a.call(this)||this;return e.define=b,e.operand=c,e.testValue=d,e}return Object(g.d)(b,a),b.prototype.isTrue=function(a){a=a[this.define];void 0===a&&(a=this.define);var b=!1;a=parseInt(a);var c=parseInt(this.testValue);switch(this.operand){case">":b=a>c;break;case"<":b=a=":b=a>=c;break;case"==":b=a===c}return b},b}(j),o=c(26),p=c(41),q=/defineds*?((.+?))/g,r=/defineds*?[(.+?)]/g,s=function(){function a(){}return a.Initialize=function(a){a.processor&&a.processor.initializeShaders&&a.processor.initializeShaders(a.processingContext)},a.Process=function(a,b,c,d){var e,f=this;(null===(e=b.processor)||void 0===e?void 0:e.preProcessShaderCode)&&(a=b.processor.preProcessShaderCode(a)),this._ProcessIncludes(a,b,function(a){a=f._ProcessShaderConversion(a,b,d);c(a)})},a.PreProcess=function(a,b,c,d){var e,f=this;(null===(e=b.processor)||void 0===e?void 0:e.preProcessShaderCode)&&(a=b.processor.preProcessShaderCode(a)),this._ProcessIncludes(a,b,function(a){a=f._ApplyPreProcessing(a,b,d);c(a)})},a.Finalize=function(a,b,c){return c.processor&&c.processor.finalizeShaders?c.processor.finalizeShaders(a,b,c.processingContext):{vertexCode:a,fragmentCode:b}},a._ProcessPrecision=function(a,b){var c;if(null===(c=b.processor)||void 0===c?void 0:c.noPrecision)return a;c=b.shouldUseHighPrecisionShader;return-1===a.indexOf("precision highp float")?a=c?"precision highp float; "+a:"precision mediump float; "+a:c||(a=a.replace("precision highp float","precision mediump float")),a},a._ExtractOperation=function(a){var b=/defined((.+))/.exec(a);if(b&&b.length)return new k(b[1].trim(),"!"===a[0]);for(var b="",c=0,d=0,e=["==",">=","<=","<",">"];d-1));d++);if(-1===c)return new k(a);e=a.substring(0,c).trim();d=a.substring(c+b.length).trim();return new n(e,b,d)},a._BuildSubExpression=function(a){a=a.replace(q,"defined[$1]");for(var b=[],c=0,a=j.infixToPostfix(a);c=2){var e=b[b.length-1],f=b[b.length-2];b.length-=2;d="&&"==d?new m:new l;"string"==typeof e&&(e=e.replace(r,"defined($1)")),"string"==typeof f&&(f=f.replace(r,"defined($1)")),d.leftOperand="string"==typeof f?this._ExtractOperation(f):f,d.rightOperand="string"==typeof e?this._ExtractOperation(e):e,b.push(d)}}f=b[b.length-1];return"string"==typeof f&&(f=f.replace(r,"defined($1)")),"string"==typeof f?this._ExtractOperation(f):f},a._BuildExpression=function(a,b){var c=new i,d=a.substring(0,b);a=a.substring(b);return a=a.substring(0,(a.indexOf("//")+1||a.length+1)-1).trim(),c.testExpression="#ifdef"===d?new k(a):"#ifndef"===d?new k(a,!0):this._BuildSubExpression(a),c},a._MoveCursorWithinIf=function(a,b,c){for(var d=a.currentLine;this._MoveCursor(a,c);){var f=(d=a.currentLine).substring(0,5).toLowerCase();if("#else"===f){var g=new e;return b.children.push(g),void this._MoveCursor(a,g)}if("#elif"===f){g=this._BuildExpression(d,5);b.children.push(g),c=g}}},a._MoveCursor=function(a,b){for(;a.canRead;){a.lineIndex++;var c=a.currentLine,d=/(#ifdef)|(#else)|(#elif)|(#endif)|(#ifndef)|(#if)/.exec(c);if(d&&d.length)switch(d[0]){case"#ifdef":d=new h;b.children.push(d);var f=this._BuildExpression(c,6);d.children.push(f),this._MoveCursorWithinIf(a,d,f);break;case"#else":case"#elif":return!0;case"#endif":return!1;case"#ifndef":d=new h;b.children.push(d);f=this._BuildExpression(c,7);d.children.push(f),this._MoveCursorWithinIf(a,d,f);break;case"#if":(d=new h,f=this._BuildExpression(c,3)),(b.children.push(d),d.children.push(f),this._MoveCursorWithinIf(a,d,f))}else{d=new e;if(d.line=c,b.children.push(d),"#"===c[0]&&"d"===c[1]){f=c.replace(";","").split(" ");d.additionalDefineKey=f[1],3===f.length&&(d.additionalDefineValue=f[2])}}}return!1},a._EvaluatePreProcessors=function(a,b,c){var d=new e,g=new f;return g.lineIndex=-1,g.lines=a.split(" "),this._MoveCursor(g,d),d.process(b,c)},a._PreparePreProcessors=function(a,b){for(var f,c={},d=0,e=a.defines;d1?f[1]:""}return(null===(f=a.processor)||void 0===f?void 0:f.shaderLanguage)===p.a.GLSL&&(c.GL_ES="true"),c.__VERSION__=a.version,c[a.platformName]="true",b._getGlobalDefines(c),c},a._ProcessShaderConversion=function(a,b,c){a=this._ProcessPrecision(a,b);if(!b.processor)return a;if(b.processor.shaderLanguage===p.a.GLSL&&-1!==a.indexOf("#version 3"))return a.replace("#version 300 es","");var d=b.defines,e=this._PreparePreProcessors(b,c);return b.processor.preProcessor&&(a=b.processor.preProcessor(a,d,b.isFragment,b.processingContext)),a=this._EvaluatePreProcessors(a,e,b),b.processor.postProcessor&&(a=b.processor.postProcessor(a,d,b.isFragment,b.processingContext,c)),c._features.needShaderCodeInlining&&(a=c.inlineShaderCode(a)),a},a._ApplyPreProcessing=function(a,b,c){var d;a=a;var e=b.defines,f=this._PreparePreProcessors(b,c);return(null===(d=b.processor)||void 0===d?void 0:d.preProcessor)&&(a=b.processor.preProcessor(a,e,b.isFragment,b.processingContext)),a=this._EvaluatePreProcessors(a,f,b),(null===(d=b.processor)||void 0===d?void 0:d.postProcessor)&&(a=b.processor.postProcessor(a,e,b.isFragment,b.processingContext,c)),c._features.needShaderCodeInlining&&(a=c.inlineShaderCode(a)),a},a._ProcessIncludes=function(b,c,d){for(var e=this,f=/#includes?<(.+)>(((.*)))*([(.*)])*/g,g=f.exec(b),h=new String(b),i=!1;null!=g;){var j=g[1];if(-1!==j.indexOf("__decl__")&&(j=j.replace(/__decl__/,""),c.supportsUniformBuffers&&(j=(j=j.replace(/Vertex/,"Ubo")).replace(/Fragment/,"Ubo")),j+="Declaration"),!c.includesShadersStore[j]){var k=c.shadersRepository+"ShadersInclude/"+j+".fx";return void a._FileToolsLoadFile(k,function(a){c.includesShadersStore[j]=a,e._ProcessIncludes(h,c,d)})}k=c.includesShadersStore[j];if(g[2])for(var l=g[3].split(","),q=0;q=0||k.indexOf("#include <")>=0,g=f.exec(b)}i?this._ProcessIncludes(h.toString(),c,d):d(h)},a._FileToolsLoadFile=function(a,b,c,d,e,f){throw Object(o.a)("FileTools")},a}()},function(a,b,c){a="instancesDeclaration";b="#ifdef INSTANCES attribute vec4 world0; attribute vec4 world1; attribute vec4 world2; attribute vec4 world3; #if defined(THIN_INSTANCES) && !defined(WORLD_UBO) uniform mat4 world; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) attribute vec4 previousWorld0; attribute vec4 previousWorld1; attribute vec4 previousWorld2; attribute vec4 previousWorld3; #ifdef THIN_INSTANCES uniform mat4 previousWorld; #endif #endif #else #if !defined(WORLD_UBO) uniform mat4 world; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) uniform mat4 previousWorld; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return i});var d=c(2),e=c(3),f=c(0),g=c(8);a=c(30);var h=c(52);a.a.AddNodeConstructor("Light_Type_3",function(a,b){return function(){return new i(a,f.e.Zero(),b)}});var i=function(a){function b(b,c,d){b=a.call(this,b,d)||this;return b.groundColor=new g.a(0,0,0),b.direction=c||f.e.Up(),b}return Object(d.d)(b,a),b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("vLightData",4),this._uniformBuffer.addUniform("vLightDiffuse",4),this._uniformBuffer.addUniform("vLightSpecular",4),this._uniformBuffer.addUniform("vLightGround",3),this._uniformBuffer.addUniform("shadowsInfo",3),this._uniformBuffer.addUniform("depthValues",2),this._uniformBuffer.create()},b.prototype.getClassName=function(){return"HemisphericLight"},b.prototype.setDirectionToTarget=function(a){return this.direction=f.e.Normalize(a.subtract(f.e.Zero())),this.direction},b.prototype.getShadowGenerator=function(){return null},b.prototype.transferToEffect=function(a,b){a=f.e.Normalize(this.direction);return this._uniformBuffer.updateFloat4("vLightData",a.x,a.y,a.z,0,b),this._uniformBuffer.updateColor3("vLightGround",this.groundColor.scale(this.intensity),b),this},b.prototype.transferToNodeMaterialEffect=function(a,b){var c=f.e.Normalize(this.direction);return a.setFloat3(b,c.x,c.y,c.z),this},b.prototype.computeWorldMatrix=function(){return this._worldMatrix||(this._worldMatrix=f.a.Identity()),this._worldMatrix},b.prototype.getTypeID=function(){return h.a.LIGHTTYPEID_HEMISPHERICLIGHT},b.prototype.prepareLightSpecificDefines=function(a,b){a["HEMILIGHT"+b]=!0},Object(d.c)([Object(e.f)()],b.prototype,"groundColor",void 0),Object(d.c)([Object(e.p)()],b.prototype,"direction",void 0),b}(h.a)},function(a,b,c){c.d(b,"a",function(){return f});var d=c(2),e=c(0),f=function(a){function b(b,c){b=a.call(this,b,c)||this;return b._normalMatrix=new e.a,b._storeEffectOnSubMeshes=!0,b}return Object(d.d)(b,a),b.prototype.getEffect=function(){return this._activeEffect},b.prototype.isReady=function(a,b){return!!a&&(!a.subMeshes||0===a.subMeshes.length||this.isReadyForSubMesh(a,a.subMeshes[0],b))},b.prototype._isReadyForSubMesh=function(a){var b=a._materialDefines;return!(this.checkReadyOnEveryCall||!a.effect||!b||b._renderId!==this.getScene().getRenderId())},b.prototype.bindOnlyWorldMatrix=function(a){this._activeEffect.setMatrix("world",a)},b.prototype.bindOnlyNormalMatrix=function(a){this._activeEffect.setMatrix("normalMatrix",a)},b.prototype.bind=function(a,b){b&&this.bindForSubMesh(a,b,b.subMeshes[0])},b.prototype._afterBind=function(b,c){void 0===c&&(c=null),a.prototype._afterBind.call(this,b,c),this.getScene()._cachedEffect=c},b.prototype._mustRebind=function(a,b,c){return void 0===c&&(c=1),a.isCachedMaterialInvalid(this,b,c)},b}(c(29).a)},function(a,b,c){c.d(b,"a",function(){return e});var d=c(2),e=function(a){function b(b){var c=a.call(this)||this;return c._buffer=b,c}return Object(d.d)(b,a),Object.defineProperty(b.prototype,"underlyingResource",{get:function(){return this._buffer},enumerable:!1,configurable:!0}),b}(c(78).a)},function(a,b,c){c.d(b,"a",function(){return e});var d=c(68),e=function(){function a(){}return a.GetPlanes=function(b){for(var c=[],e=0;e<6;e++)c.push(new d.a(0,0,0,0));return a.GetPlanesToRef(b,c),c},a.GetNearPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[2],b.normal.y=a[7]+a[6],b.normal.z=a[11]+a[10],b.d=a[15]+a[14],b.normalize()},a.GetFarPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[2],b.normal.y=a[7]-a[6],b.normal.z=a[11]-a[10],b.d=a[15]-a[14],b.normalize()},a.GetLeftPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[0],b.normal.y=a[7]+a[4],b.normal.z=a[11]+a[8],b.d=a[15]+a[12],b.normalize()},a.GetRightPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[0],b.normal.y=a[7]-a[4],b.normal.z=a[11]-a[8],b.d=a[15]-a[12],b.normalize()},a.GetTopPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]-a[1],b.normal.y=a[7]-a[5],b.normal.z=a[11]-a[9],b.d=a[15]-a[13],b.normalize()},a.GetBottomPlaneToRef=function(a,b){a=a.m;b.normal.x=a[3]+a[1],b.normal.y=a[7]+a[5],b.normal.z=a[11]+a[9],b.d=a[15]+a[13],b.normalize()},a.GetPlanesToRef=function(b,c){a.GetNearPlaneToRef(b,c[0]),a.GetFarPlaneToRef(b,c[1]),a.GetLeftPlaneToRef(b,c[2]),a.GetRightPlaneToRef(b,c[3]),a.GetTopPlaneToRef(b,c[4]),a.GetBottomPlaneToRef(b,c[5])},a}()},function(a,b,c){c.d(b,"a",function(){return f});var d=c(51),e=c(0),f=function(){function a(a,b,c){this.center=e.e.Zero(),this.centerWorld=e.e.Zero(),this.minimum=e.e.Zero(),this.maximum=e.e.Zero(),this.reConstruct(a,b,c)}return a.prototype.reConstruct=function(a,b,c){this.minimum.copyFrom(a),this.maximum.copyFrom(b);var d=e.e.Distance(a,b);b.addToRef(a,this.center).scaleInPlace(.5),this.radius=.5*d,this._update(c||e.a.IdentityReadOnly)},a.prototype.scale=function(b){b=this.radius*b;var c=a.TmpVector3;b=c[0].setAll(b);var d=this.center.subtractToRef(b,c[1]);b=this.center.addToRef(b,c[2]);return this.reConstruct(d,b,this._worldMatrix),this},a.prototype.getWorldMatrix=function(){return this._worldMatrix},a.prototype._update=function(b){if(b.isIdentity())this.centerWorld.copyFrom(this.center),this.radiusWorld=this.radius;else{e.e.TransformCoordinatesToRef(this.center,b,this.centerWorld);var c=a.TmpVector3[0];e.e.TransformNormalFromFloatsToRef(1,1,1,b,c),this.radiusWorld=Math.max(Math.abs(c.x),Math.abs(c.y),Math.abs(c.z))*this.radius}},a.prototype.isInFrustum=function(a){for(var b=this.centerWorld,c=this.radiusWorld,d=0;d<6;d++)if(a[d].dotCoordinate(b)<=-c)return!1;return!0},a.prototype.isCenterInFrustum=function(a){for(var b=this.centerWorld,c=0;c<6;c++)if(a[c].dotCoordinate(b)<0)return!1;return!0},a.prototype.intersectsPoint=function(a){a=e.e.DistanceSquared(this.centerWorld,a);return!(this.radiusWorld*this.radiusWorld=d.a.ACTION_OnPickTrigger&&c<=d.a.ACTION_OnPickUpTrigger)return!0}return!1},enumerable:!1,configurable:!0}),a.HasSpecificTrigger=function(b){for(var c in a.Triggers)if(a.Triggers.hasOwnProperty(c)&&parseInt(c)===b)return!0;return!1},a.Triggers={},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(35),e=function(){function a(){}return a.SetImmediate=function(a){Object(d.e)()&&window.setImmediate?window.setImmediate(a):setTimeout(a,1)},a}()},function(a,b,c){c.d(b,"a",function(){return i});var d=c(2),e=c(29),f=c(3),g=c(20),h=c(15),i=function(){function a(a){this._texture=null,this.diffuseBlendLevel=1,this.roughnessBlendLevel=1,this.bumpLevel=1,this._normalBlendMethod=e.a.MATERIAL_NORMALBLENDMETHOD_WHITEOUT,this._isEnabled=!1,this.isEnabled=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isEnabled)return!0;var c=b.getEngine();return!(a._areTexturesDirty&&b.texturesEnabled&&c.getCaps().standardDerivatives&&this._texture&&g.a.DetailTextureEnabled&&!this._texture.isReady())},a.prototype.prepareDefines=function(a,b){if(this._isEnabled){a.DETAIL_NORMALBLENDMETHOD=this._normalBlendMethod;b=b.getEngine();a._areTexturesDirty&&(b.getCaps().standardDerivatives&&this._texture&&g.a.DetailTextureEnabled&&this._isEnabled?(h.a.PrepareDefinesForMergedUV(this._texture,a,"DETAIL"),a.DETAIL_NORMALBLENDMETHOD=this._normalBlendMethod):a.DETAIL=!1)}else a.DETAIL=!1},a.prototype.bindForSubMesh=function(a,b,c){this._isEnabled&&(a.useUbo&&c&&a.isSync||this._texture&&g.a.DetailTextureEnabled&&(a.updateFloat4("vDetailInfos",this._texture.coordinatesIndex,this.diffuseBlendLevel,this.bumpLevel,this.roughnessBlendLevel),h.a.BindTextureMatrix(this._texture,a,"detail")),b.texturesEnabled&&this._texture&&g.a.DetailTextureEnabled&&a.setTexture("detailSampler",this._texture))},a.prototype.hasTexture=function(a){return this._texture===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"DetailMap"},a.AddUniforms=function(a){a.push("vDetailInfos"),a.push("detailMatrix")},a.AddSamplers=function(a){a.push("detailSampler")},a.PrepareUniformBuffer=function(a){a.addUniform("vDetailInfos",4),a.addUniform("detailMatrix",16)},a.prototype.copyTo=function(a){f.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return f.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;f.a.Parse(function(){return d},a,b,c)},Object(d.c)([Object(f.n)("detailTexture"),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(d.c)([Object(f.d)()],a.prototype,"diffuseBlendLevel",void 0),Object(d.c)([Object(f.d)()],a.prototype,"roughnessBlendLevel",void 0),Object(d.c)([Object(f.d)()],a.prototype,"bumpLevel",void 0),Object(d.c)([Object(f.d)(),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"normalBlendMethod",void 0),Object(d.c)([Object(f.d)(),Object(f.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),a}()},function(a,b,c){a="morphTargetsVertexGlobalDeclaration";b="#ifdef MORPHTARGETS uniform float morphTargetInfluences[NUM_MORPH_INFLUENCERS]; #ifdef MORPHTARGETS_TEXTURE precision mediump sampler2DArray; uniform float morphTargetTextureIndices[NUM_MORPH_INFLUENCERS]; uniform vec3 morphTargetTextureInfo; uniform sampler2DArray morphTargets; vec3 readVector3FromRawSampler(int targetIndex,float vertexIndex) { float y=floor(vertexIndex/morphTargetTextureInfo.y); float x=vertexIndex-y*morphTargetTextureInfo.y; vec3 textureUV=vec3((x+0.5)/morphTargetTextureInfo.y,(y+0.5)/morphTargetTextureInfo.z,morphTargetTextureIndices[targetIndex]); return texture(morphTargets,textureUV).xyz; } #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertexDeclaration";b="#ifdef MORPHTARGETS #ifndef MORPHTARGETS_TEXTURE attribute vec3 position{X}; #ifdef MORPHTARGETS_NORMAL attribute vec3 normal{X}; #endif #ifdef MORPHTARGETS_TANGENT attribute vec3 tangent{X}; #endif #ifdef MORPHTARGETS_UV attribute vec2 uv_{X}; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return f});var d=c(4),e=c(1),f=function(){function a(a){this._vertexBuffers={},this._scene=a}return a.prototype._prepareBuffers=function(){if(!this._vertexBuffers[d.b.PositionKind]){var a=[];a.push(1,1),a.push(-1,1),a.push(-1,-1),a.push(1,-1),this._vertexBuffers[d.b.PositionKind]=new d.b(this._scene.getEngine(),a,d.b.PositionKind,!1,!1,2),this._buildIndexBuffer()}},a.prototype._buildIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._scene.getEngine().createIndexBuffer(a)},a.prototype._rebuild=function(){var a=this._vertexBuffers[d.b.PositionKind];a&&(a._rebuild(),this._buildIndexBuffer())},a.prototype._prepareFrame=function(a,b){void 0===a&&(a=null),void 0===b&&(b=null);var c=this._scene.activeCamera;return!!c&&!(!(b=b||c._postProcesses.filter(function(a){return null!=a}))||0===b.length||!this._scene.postProcessesEnabled)&&(b[0].activate(c,a,null!=b),!0)},a.prototype.directRender=function(a,b,c,d,f,g){var h;void 0===b&&(b=null),void 0===c&&(c=!1),void 0===d&&(d=0),void 0===f&&(f=0),void 0===g&&(g=!1);for(var i=this._scene.getEngine(),j=0;j3?0:n,o);s=Object(f.a)(a,{pathArray:y,closeArray:l,closePath:m,updatable:q,sideOrientation:r,invertUV:t,frontUVs:u||void 0,backUVs:v||void 0},p);return s._creationDataStorage.pathArray=y,s._creationDataStorage.path3D=w,s._creationDataStorage.cap=n,s}var k={ExtrudeShape:h,ExtrudeShapeCustom:i};e.a.ExtrudeShape=function(a,b,c,d,f,g,i,j,k,l){return void 0===i&&(i=null),h(a,{shape:b,path:c,scale:d,rotation:f,cap:0===g?0:g||e.a.NO_CAP,sideOrientation:k,instance:l,updatable:j},i)},e.a.ExtrudeShapeCustom=function(a,b,c,d,f,g,h,j,k,l,m,n){return i(a,{shape:b,path:c,scaleFunction:d,rotationFunction:f,ribbonCloseArray:g,ribbonClosePath:h,cap:0===j?0:j||e.a.NO_CAP,sideOrientation:m,instance:n,updatable:l},k)}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.FilesToLoad={},a}()},function(a,b,c){c.d(b,"b",function(){return j}),c.d(b,"a",function(){return k});var d=c(2),e=c(8),f=c(4);a=c(9);b=c(177);var g=c(29),h=c(71),i=c(15);c(187),c(188);a.a._LinesMeshParser=function(a,b){return j.Parse(a,b)};var j=function(a){function b(b,c,d,k,l,g,i,j){void 0===c&&(c=null),void 0===d&&(d=null),void 0===k&&(k=null);b=a.call(this,b,c,d,k,l)||this;b.useVertexColor=g,b.useVertexAlpha=i,b.color=new e.a(1,1,1),b.alpha=1,k&&(b.color=k.color.clone(),b.alpha=k.alpha,b.useVertexColor=k.useVertexColor,b.useVertexAlpha=k.useVertexAlpha),b.intersectionThreshold=.1;c={attributes:[f.b.PositionKind],uniforms:["vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","world","viewProjection"],needAlphaBlending:!0,defines:[]};return!1===i&&(c.needAlphaBlending=!1),g?(c.defines.push("#define VERTEXCOLOR"),c.attributes.push(f.b.ColorKind)):(c.uniforms.push("color"),b.color4=new e.b),j?b.material=j:b._lineMaterial=new h.a("colorShader",b.getScene(),"color",c),b}return Object(d.d)(b,a),b.prototype._isShaderMaterial=function(a){return"ShaderMaterial"===a.getClassName()},b.prototype._addClipPlaneDefine=function(a){if(this._isShaderMaterial(this._lineMaterial)){a="#define "+a;-1===this._lineMaterial.options.defines.indexOf(a)&&this._lineMaterial.options.defines.push(a)}},b.prototype._removeClipPlaneDefine=function(a){if(this._isShaderMaterial(this._lineMaterial)){a="#define "+a;a=this._lineMaterial.options.defines.indexOf(a);-1!==a&&this._lineMaterial.options.defines.splice(a,1)}},b.prototype.isReady=function(){var b=this.getScene();return b.clipPlane?this._addClipPlaneDefine("CLIPPLANE"):this._removeClipPlaneDefine("CLIPPLANE"),b.clipPlane2?this._addClipPlaneDefine("CLIPPLANE2"):this._removeClipPlaneDefine("CLIPPLANE2"),b.clipPlane3?this._addClipPlaneDefine("CLIPPLANE3"):this._removeClipPlaneDefine("CLIPPLANE3"),b.clipPlane4?this._addClipPlaneDefine("CLIPPLANE4"):this._removeClipPlaneDefine("CLIPPLANE4"),b.clipPlane5?this._addClipPlaneDefine("CLIPPLANE5"):this._removeClipPlaneDefine("CLIPPLANE5"),b.clipPlane6?this._addClipPlaneDefine("CLIPPLANE6"):this._removeClipPlaneDefine("CLIPPLANE6"),!!this._lineMaterial.isReady(this)&&a.prototype.isReady.call(this)},b.prototype.getClassName=function(){return"LinesMesh"},Object.defineProperty(b.prototype,"material",{get:function(){return this._lineMaterial},set:function(a){this._lineMaterial=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"checkCollisions",{get:function(){return!1},set:function(a){},enumerable:!1,configurable:!0}),b.prototype._bind=function(a,b,c){if(!this._geometry)return this;a=this._lineMaterial.getEffect();b=this.isUnIndexed?null:this._geometry.getIndexBuffer();if(this._userInstancedBuffersStorage?this._geometry._bind(a,b,this._userInstancedBuffersStorage.vertexBuffers,this._userInstancedBuffersStorage.vertexArrayObjects):this._geometry._bind(a,b),!this.useVertexColor&&this._isShaderMaterial(this._lineMaterial)){c=this.color;b=c.r;var d=c.g;c=c.b;this.color4.set(b,d,c,this.alpha),this._lineMaterial.setColor4("color",this.color4)}return i.a.BindClipPlane(a,this.getScene()),this},b.prototype._draw=function(a,b,c){if(!this._geometry||!this._geometry.getVertexBuffers()||!this._unIndexed&&!this._geometry.getIndexBuffer())return this;b=this.getScene().getEngine();return this._unIndexed?b.drawArraysType(g.a.LineListDrawMode,a.verticesStart,a.verticesCount,c):b.drawElementsType(g.a.LineListDrawMode,a.indexStart,a.indexCount,c),this},b.prototype.dispose=function(b){this._lineMaterial.dispose(!1,!1,!0),a.prototype.dispose.call(this,b)},b.prototype.clone=function(a,c,d){return void 0===c&&(c=null),new b(a,this.getScene(),c,this,d)},b.prototype.createInstance=function(a){a=new k(a,this);if(this.instancedBuffers)for(var b in a.instancedBuffers={},this.instancedBuffers)a.instancedBuffers[b]=this.instancedBuffers[b];return a},b.prototype.serialize=function(b){a.prototype.serialize.call(this,b),b.color=this.color.asArray(),b.alpha=this.alpha},b.Parse=function(a,c){c=new b(a.name,c);return c.color=e.a.FromArray(a.color),c.alpha=a.alpha,c},b}(a.a),k=function(a){function b(b,c){b=a.call(this,b,c)||this;return b.intersectionThreshold=c.intersectionThreshold,b}return Object(d.d)(b,a),b.prototype.getClassName=function(){return"InstancedLinesMesh"},b}(b.a)},function(a,b,c){c.r(b),c.d(b,"AxesViewer",function(){return i}),c.d(b,"BoneAxesViewer",function(){return l}),c.d(b,"DebugLayerTab",function(){return d}),c.d(b,"DebugLayer",function(){return p}),c.d(b,"PhysicsViewer",function(){return y}),c.d(b,"RayHelper",function(){return A}),c.d(b,"SkeletonViewer",function(){return G}),c.d(b,"DirectionalLightFrustumViewer",function(){return J});var d,e=c(0),f=c(32),g=c(82),h=c(8),i=function(){function a(b,c,d,i,j,k){if(void 0===c&&(c=1),void 0===d&&(d=2),this._scaleLinesFactor=4,this._instanced=!1,this.scene=null,this.scaleLines=1,this.scaleLines=c,!i){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Red().scale(.5),i=g.a._CreateArrow(b,c)}if(!j){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Green().scale(.5),j=g.a._CreateArrow(b,c)}if(!k){c=new f.a("",b);c.disableLighting=!0,c.emissiveColor=h.a.Blue().scale(.5),k=g.a._CreateArrow(b,c)}this._xAxis=i,this._xAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._yAxis=j,this._yAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._zAxis=k,this._zAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),null!=d&&(a._SetRenderingGroupId(this._xAxis,d),a._SetRenderingGroupId(this._yAxis,d),a._SetRenderingGroupId(this._zAxis,d)),this.scene=b,this.update(new e.e,e.e.Right(),e.e.Up(),e.e.Forward())}return Object.defineProperty(a.prototype,"xAxis",{get:function(){return this._xAxis},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"yAxis",{get:function(){return this._yAxis},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zAxis",{get:function(){return this._zAxis},enumerable:!1,configurable:!0}),a.prototype.update=function(a,b,c,d){this._xAxis.position.copyFrom(a),this._xAxis.setDirection(b),this._xAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._yAxis.position.copyFrom(a),this._yAxis.setDirection(c),this._yAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor),this._zAxis.position.copyFrom(a),this._zAxis.setDirection(d),this._zAxis.scaling.setAll(this.scaleLines*this._scaleLinesFactor)},a.prototype.createInstance=function(){var b=g.a._CreateArrowInstance(this.scene,this._xAxis),c=g.a._CreateArrowInstance(this.scene,this._yAxis),d=g.a._CreateArrowInstance(this.scene,this._zAxis);b=new a(this.scene,this.scaleLines,null,b,c,d);return b._instanced=!0,b},a.prototype.dispose=function(){this._xAxis&&this._xAxis.dispose(!1,!this._instanced),this._yAxis&&this._yAxis.dispose(!1,!this._instanced),this._zAxis&&this._zAxis.dispose(!1,!this._instanced),this.scene=null},a._SetRenderingGroupId=function(a,b){a.getChildMeshes().forEach(function(a){a.renderingGroupId=b})},a}(),j=c(2),k=c(25),l=function(a){function b(b,c,d,f){void 0===f&&(f=1);b=a.call(this,b,f)||this;return b.pos=e.e.Zero(),b.xaxis=e.e.Zero(),b.yaxis=e.e.Zero(),b.zaxis=e.e.Zero(),b.mesh=d,b.bone=c,b}return Object(j.d)(b,a),b.prototype.update=function(){if(this.mesh&&this.bone){var b=this.bone;b._markAsDirtyAndCompose(),b.getAbsolutePositionToRef(this.mesh,this.pos),b.getDirectionToRef(k.a.X,this.mesh,this.xaxis),b.getDirectionToRef(k.a.Y,this.mesh,this.yaxis),b.getDirectionToRef(k.a.Z,this.mesh,this.zaxis),a.prototype.update.call(this,this.pos,this.xaxis,this.yaxis,this.zaxis)}},b.prototype.dispose=function(){this.mesh&&(this.mesh=null,this.bone=null,a.prototype.dispose.call(this))},b}(i),m=c(12),n=c(6);a=c(21);var o=c(13);Object.defineProperty(a.a.prototype,"debugLayer",{get:function(){return this._debugLayer||(this._debugLayer=new p(this)),this._debugLayer},enumerable:!0,configurable:!0}),(function(a){a[a.Properties=0]="Properties",a[a.Debug=1]="Debug",a[a.Statistics=2]="Statistics",a[a.Tools=3]="Tools",a[a.Settings=4]="Settings"})(d||(d={}));var p=function(){function a(a){var b=this;this.BJSINSPECTOR=this._getGlobalInspector(),this._scene=a,this._scene.onDisposeObservable.add(function(){b._scene._debugLayer&&b._scene._debugLayer.hide()})}return Object.defineProperty(a.prototype,"onPropertyChangedObservable",{get:function(){return this.BJSINSPECTOR&&this.BJSINSPECTOR.Inspector?this.BJSINSPECTOR.Inspector.OnPropertyChangedObservable:(this._onPropertyChangedObservable||(this._onPropertyChangedObservable=new n.c),this._onPropertyChangedObservable)},enumerable:!1,configurable:!0}),a.prototype._createInspector=function(a){if(!this.isVisible()){if(this._onPropertyChangedObservable){for(var b=0,c=this._onPropertyChangedObservable.observers;b-1&&this._debugMeshMeshes.splice(e,1),this._numMeshes--,this._numMeshes>0?(this._meshes[d]=this._meshes[this._numMeshes],this._impostors[d]=this._impostors[this._numMeshes],this._meshes[this._numMeshes]=null,this._impostors[this._numMeshes]=null):(this._meshes[0]=null,this._impostors[0]=null),b=!0;break}b&&0===this._numMeshes&&this._scene.unregisterBeforeRender(this._renderFunction)}},a.prototype._getDebugMaterial=function(a){return this._debugMaterial||(this._debugMaterial=new f.a("",a),this._debugMaterial.wireframe=!0,this._debugMaterial.emissiveColor=h.a.White(),this._debugMaterial.disableLighting=!0),this._debugMaterial},a.prototype._getDebugBoxMesh=function(a){return this._debugBoxMesh||(this._debugBoxMesh=Object(r.b)("physicsBodyBoxViewMesh",{size:1},a),this._debugBoxMesh.rotationQuaternion=e.b.Identity(),this._debugBoxMesh.material=this._getDebugMaterial(a),this._debugBoxMesh.setEnabled(!1)),this._debugBoxMesh.createInstance("physicsBodyBoxViewInstance")},a.prototype._getDebugSphereMesh=function(a){return this._debugSphereMesh||(this._debugSphereMesh=Object(s.a)("physicsBodySphereViewMesh",{diameter:1},a),this._debugSphereMesh.rotationQuaternion=e.b.Identity(),this._debugSphereMesh.material=this._getDebugMaterial(a),this._debugSphereMesh.setEnabled(!1)),this._debugSphereMesh.createInstance("physicsBodySphereViewInstance")},a.prototype._getDebugCapsuleMesh=function(a){return this._debugCapsuleMesh||(this._debugCapsuleMesh=Object(x.b)("physicsBodyCapsuleViewMesh",{height:1},a),this._debugCapsuleMesh.rotationQuaternion=e.b.Identity(),this._debugCapsuleMesh.material=this._getDebugMaterial(a),this._debugCapsuleMesh.setEnabled(!1)),this._debugCapsuleMesh.createInstance("physicsBodyCapsuleViewInstance")},a.prototype._getDebugCylinderMesh=function(a){return this._debugCylinderMesh||(this._debugCylinderMesh=Object(w.a)("physicsBodyCylinderViewMesh",{diameterTop:1,diameterBottom:1,height:1},a),this._debugCylinderMesh.rotationQuaternion=e.b.Identity(),this._debugCylinderMesh.material=this._getDebugMaterial(a),this._debugCylinderMesh.setEnabled(!1)),this._debugCylinderMesh.createInstance("physicsBodyCylinderViewInstance")},a.prototype._getDebugMeshMesh=function(a,b){var c=new q.a(a.name,b,null,a);return c.position=e.e.Zero(),c.setParent(a),c.material=this._getDebugMaterial(b),this._debugMeshMeshes.push(c),c},a.prototype._getDebugMesh=function(a,b){var c=this;if(!this._utilityLayer)return null;if(b&&b.parent&&b.parent.physicsImpostor)return null;var d=null,e=this._utilityLayer.utilityLayerScene;switch(a.type){case u.a.BoxImpostor:d=this._getDebugBoxMesh(e),a.getBoxSizeToRef(d.scaling);break;case u.a.SphereImpostor:d=this._getDebugSphereMesh(e);var f=a.getRadius();d.scaling.x=2*f,d.scaling.y=2*f,d.scaling.z=2*f;break;case u.a.CapsuleImpostor:d=this._getDebugCapsuleMesh(e);f=a.object.getBoundingInfo();d.scaling.x=2*(f.boundingBox.maximum.x-f.boundingBox.minimum.x)*a.object.scaling.x,d.scaling.y=(f.boundingBox.maximum.y-f.boundingBox.minimum.y)*a.object.scaling.y,d.scaling.z=2*(f.boundingBox.maximum.z-f.boundingBox.minimum.z)*a.object.scaling.z;break;case u.a.MeshImpostor:b&&(d=this._getDebugMeshMesh(b,e));break;case u.a.NoImpostor:b&&b.getChildMeshes().filter(function(a){return a.physicsImpostor?1:0}).forEach(function(a){if(a.physicsImpostor&&"Mesh"===a.getClassName()){var b=a.getBoundingInfo(),f=b.boundingBox.minimum;b=b.boundingBox.maximum;switch(a.physicsImpostor.type){case u.a.BoxImpostor:(d=c._getDebugBoxMesh(e)).position.copyFrom(f),d.position.addInPlace(b),d.position.scaleInPlace(.5);break;case u.a.SphereImpostor:d=c._getDebugSphereMesh(e);break;case u.a.CylinderImpostor:d=c._getDebugCylinderMesh(e);break;default:d=null}d&&(d.scaling.x=b.x-f.x,d.scaling.y=b.y-f.y,d.scaling.z=b.z-f.z,d.parent=a)}});d=null;break;case u.a.CylinderImpostor:d=this._getDebugCylinderMesh(e),f=a.object.getBoundingInfo(),(d.scaling.x=(f.boundingBox.maximum.x-f.boundingBox.minimum.x)*a.object.scaling.x,d.scaling.y=(f.boundingBox.maximum.y-f.boundingBox.minimum.y)*a.object.scaling.y,d.scaling.z=(f.boundingBox.maximum.z-f.boundingBox.minimum.z)*a.object.scaling.z)}return d},a.prototype.dispose=function(){for(var a=this._numMeshes,b=0;ba.DISPLAY_SPHERE_AND_SPURS&&(c=a.DISPLAY_LINES),this.displayMode=c,this.update(),this._bindObs()}return a.CreateBoneWeightShader=function(a,b){var c,d,e,f,g,i=a.skeleton;c=null!==(c=a.colorBase)&&void 0!==c?c:h.a.Black();d=null!==(d=a.colorZero)&&void 0!==d?d:h.a.Blue();e=null!==(e=a.colorQuarter)&&void 0!==e?e:h.a.Green();f=null!==(f=a.colorHalf)&&void 0!==f?f:h.a.Yellow();g=null!==(g=a.colorFull)&&void 0!==g?g:h.a.Red();a=null!==(a=a.targetBoneIndex)&&void 0!==a?a:0;F.a.ShadersStore["boneWeights:"+i.name+"VertexShader"]="precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 view; uniform mat4 projection; uniform mat4 worldViewProjection; #include #if NUM_BONE_INFLUENCERS == 0 attribute vec4 matricesIndices; attribute vec4 matricesWeights; #endif #include varying vec3 vColor; uniform vec3 colorBase; uniform vec3 colorZero; uniform vec3 colorQuarter; uniform vec3 colorHalf; uniform vec3 colorFull; uniform float targetBoneIndex; void main() { vec3 positionUpdated = position; #include #include vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0); vec3 color = colorBase; float totalWeight = 0.; if(matricesIndices[0] == targetBoneIndex && matricesWeights[0] > 0.){ totalWeight += matricesWeights[0]; } if(matricesIndices[1] == targetBoneIndex && matricesWeights[1] > 0.){ totalWeight += matricesWeights[1]; } if(matricesIndices[2] == targetBoneIndex && matricesWeights[2] > 0.){ totalWeight += matricesWeights[2]; } if(matricesIndices[3] == targetBoneIndex && matricesWeights[3] > 0.){ totalWeight += matricesWeights[3]; } color = mix(color, colorZero, smoothstep(0., 0.25, totalWeight)); color = mix(color, colorQuarter, smoothstep(0.25, 0.5, totalWeight)); color = mix(color, colorHalf, smoothstep(0.5, 0.75, totalWeight)); color = mix(color, colorFull, smoothstep(0.75, 1.0, totalWeight)); vColor = color; gl_Position = projection * view * worldPos; }",F.a.ShadersStore["boneWeights:"+i.name+"FragmentShader"]=" precision highp float; varying vec3 vPosition; varying vec3 vColor; void main() { vec4 color = vec4(vColor, 1.0); gl_FragColor = color; } ";b=new C.a("boneWeight:"+i.name,b,{vertex:"boneWeights:"+i.name,fragment:"boneWeights:"+i.name},{attributes:["position","normal","matricesIndices","matricesWeights"],uniforms:["world","worldView","worldViewProjection","view","projection","viewProjection","colorBase","colorZero","colorQuarter","colorHalf","colorFull","targetBoneIndex"]});return b.setColor3("colorBase",c),b.setColor3("colorZero",d),b.setColor3("colorQuarter",e),b.setColor3("colorHalf",f),b.setColor3("colorFull",g),b.setFloat("targetBoneIndex",a),b.getClassName=function(){return"BoneWeightShader"},b.transparencyMode=B.a.MATERIAL_OPAQUE,b},a.CreateSkeletonMapShader=function(b,c){var d=b.skeleton;b=null!==(b=b.colorMap)&&void 0!==b?b:[{color:new h.a(1,.38,.18),location:0},{color:new h.a(.59,.18,1),location:.2},{color:new h.a(.59,1,.18),location:.4},{color:new h.a(1,.87,.17),location:.6},{color:new h.a(1,.17,.42),location:.8},{color:new h.a(.17,.68,1),location:1}];var e=d.bones.length+1;e=a._CreateBoneMapColorBuffer(e,b,c);b=new C.a("boneWeights:"+d.name,c,{vertexSource:"precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 view; uniform mat4 projection; uniform mat4 worldViewProjection; uniform float colorMap["+4*d.bones.length+"]; #include #if NUM_BONE_INFLUENCERS == 0 attribute vec4 matricesIndices; attribute vec4 matricesWeights; #endif #include varying vec3 vColor; void main() { vec3 positionUpdated = position; #include #include vec3 color = vec3(0.); bool first = true; for (int i = 0; i < 4; i++) { int boneIdx = int(matricesIndices[i]); float boneWgt = matricesWeights[i]; vec3 c = vec3(colorMap[boneIdx * 4 + 0], colorMap[boneIdx * 4 + 1], colorMap[boneIdx * 4 + 2]); if (boneWgt > 0.) { if (first) { first = false; color = c; } else { color = mix(color, c, boneWgt); } } } vColor = color; vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0); gl_Position = projection * view * worldPos; }",fragmentSource:" precision highp float; varying vec3 vColor; void main() { vec4 color = vec4( vColor, 1.0 ); gl_FragColor = color; } "},{attributes:["position","normal","matricesIndices","matricesWeights"],uniforms:["world","worldView","worldViewProjection","view","projection","viewProjection","colorMap"]});return b.setFloats("colorMap",e),b.getClassName=function(){return"SkeletonMapShader"},b.transparencyMode=B.a.MATERIAL_OPAQUE,b},a._CreateBoneMapColorBuffer=function(a,b,c){c=new D.a("temp",{width:a,height:1},c,!1);var d=c.getContext(),e=d.createLinearGradient(0,0,a,0);b.forEach(function(a){e.addColorStop(a.location,a.color.toHexString())}),d.fillStyle=e,d.fillRect(0,0,a,1),c.update();for(b=[],d=d.getImageData(0,0,a,1).data,a=0;aa.DISPLAY_SPHERE_AND_SPURS&&(b=a.DISPLAY_LINES),this.options.displayMode=b},enumerable:!1,configurable:!0}),a.prototype._bindObs=function(){var b=this;switch(this.displayMode){case a.DISPLAY_LINES:this._obs=this.scene.onBeforeRenderObservable.add(function(){b._displayLinesUpdate()})}},a.prototype.update=function(){switch(this.displayMode){case a.DISPLAY_LINES:this._displayLinesUpdate();break;case a.DISPLAY_SPHERES:this._buildSpheresAndSpurs(!0);break;case a.DISPLAY_SPHERE_AND_SPURS:this._buildSpheresAndSpurs(!1)}this._buildLocalAxes()},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this.isEnabled!==a&&(this._isEnabled=a,this.debugMesh&&this.debugMesh.setEnabled(a),a&&!this._obs?this._bindObs():!a&&this._obs&&(this.scene.onBeforeRenderObservable.remove(this._obs),this._obs=null))},enumerable:!1,configurable:!0}),a.prototype._getBonePosition=function(a,b,c,d,f,g){void 0===d&&(d=0),void 0===f&&(f=0),void 0===g&&(g=0);var h=e.c.Matrix[0],i=b.getParent();if(h.copyFrom(b.getLocalMatrix()),0!==d||0!==f||0!==g){b=e.c.Matrix[1];e.a.IdentityToRef(b),b.setTranslationFromFloats(d,f,g),b.multiplyToRef(h,h)}i&&h.multiplyToRef(i.getAbsoluteTransform(),h),h.multiplyToRef(c,h),a.x=h.m[12],a.y=h.m[13],a.z=h.m[14]},a.prototype._getLinesForBonesWithLength=function(a,b){for(var c=a.length,d=this.mesh._effectiveMesh.position,f=0,g=0;g=0;b--){var g=a[b],h=g.getParent();if(h&&(this._boneIndices.has(g.getIndex())||this.options.useAllBones)){var i=this._debugLines[c];i||(i=[e.e.Zero(),e.e.Zero()],this._debugLines[c]=i),g.getAbsolutePositionToRef(d,i[0]),h.getAbsolutePositionToRef(d,i[1]),i[0].subtractInPlace(f),i[1].subtractInPlace(f),c++}}},a.prototype._revert=function(a){this.options.pauseAnimations&&(this.scene.animationsEnabled=a,this.utilityLayer.utilityLayerScene.animationsEnabled=a)},a.prototype._getAbsoluteBindPoseToRef=function(a,b){null!==a&&-1!==a._index?(this._getAbsoluteBindPoseToRef(a.getParent(),b),a.getBaseMatrix().multiplyToRef(b,b)):b.copyFrom(e.a.Identity())},a.prototype._buildSpheresAndSpurs=function(a){var b;void 0===a&&(a=!0),this._debugMesh&&(this._debugMesh.dispose(),this._debugMesh=null,this.ready=!1),this._ready=!1;var c=null===(b=this.utilityLayer)||void 0===b?void 0:b.utilityLayerScene,d=this.skeleton.bones,f=[],g=[];b=this.scene.animationsEnabled;try{this.options.pauseAnimations&&(this.scene.animationsEnabled=!1,c.animationsEnabled=!1),this.options.returnToRest&&this.skeleton.returnToRest(),this.autoUpdateBonesMatrices&&this.skeleton.computeAbsoluteTransforms();for(var h=Number.NEGATIVE_INFINITY,i=this.options.displayOptions||{},j=function(b){var j=d[b];if(-1===j._index||!k._boneIndices.has(j.getIndex())&&!k.options.useAllBones)return"continue";var l=new e.a;k._getAbsoluteBindPoseToRef(j,l);var n=new e.e;l.decompose(void 0,void 0,n),j.children.forEach(function(b,d){d=new e.a;b.getBaseMatrix().multiplyToRef(l,d);var f=new e.e;d.decompose(void 0,void 0,f);d=e.e.Distance(n,f);if(d>h&&(h=d),!a){for(var d=f.clone().subtract(n.clone()),k=d.length(),f=d.normalize().scale(k),d=i.midStep||.165,p=i.midStepFactor||.215,d=f.scale(d),d=Object(aa.b)("skeletonViewer",{shape:[new e.e(1,-1,0),new e.e(1,1,0),new e.e(-1,1,0),new e.e(-1,-1,0),new e.e(1,-1,0)],path:[e.e.Zero(),d,f],scaleFunction:function(a){switch(a){case 0:case 2:return 0;case 1:return k*p}return 0},sideOrientation:q.a.DEFAULTSIDE,updatable:!1},c),f=d.getTotalVertices(),r=[],t=[],u=0;u9?t.push(b.getIndex(),0,0,0):t.push(j.getIndex(),0,0,0);d.position=n.clone(),d.setVerticesData(E.b.MatricesWeightsKind,r,!1),d.setVerticesData(E.b.MatricesIndicesKind,t,!1),d.convertToFlatShadedMesh(),g.push(d)}});for(var b=i.sphereBaseSize||.2,b=Object(s.a)("skeletonViewer",{segments:6,diameter:b,updatable:!0},c),u=b.getTotalVertices(),v=[],z=[],A=0;A=6&&a._showPlanes)}),this._oldPosition.set(Number.NaN,Number.NaN,Number.NaN),this._visible=!0},a.prototype.hide=function(){this._lightHelperFrustumMeshes.forEach(function(a){a.setEnabled(!1)}),this._visible=!1},a.prototype.update=function(){if(this._visible&&(!this._oldPosition.equals(this._light.position)||!this._oldDirection.equals(this._light.direction)||this._oldAutoCalc!==this._light.autoCalcShadowZBounds||this._oldMinZ!==this._light.shadowMinZ||this._oldMaxZ!==this._light.shadowMaxZ)){this._oldPosition.copyFrom(this._light.position),this._oldDirection.copyFrom(this._light.direction),this._oldAutoCalc=this._light.autoCalcShadowZBounds,this._oldMinZ=this._light.shadowMinZ,this._oldMaxZ=this._light.shadowMaxZ,e.c.Vector3[0].set(this._light.orthoLeft,this._light.orthoBottom,void 0!==this._light.shadowMinZ?this._light.shadowMinZ:this._camera.minZ),e.c.Vector3[1].set(this._light.orthoRight,this._light.orthoTop,void 0!==this._light.shadowMaxZ?this._light.shadowMaxZ:this._camera.maxZ);var a=this._getInvertViewMatrix();e.c.Vector3[2].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[1].y,e.c.Vector3[0].z),e.c.Vector3[3].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[0].y,e.c.Vector3[0].z),e.c.Vector3[4].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[0].y,e.c.Vector3[0].z),e.c.Vector3[5].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[1].y,e.c.Vector3[0].z),e.e.TransformCoordinatesToRef(e.c.Vector3[2],a,e.c.Vector3[2]),e.e.TransformCoordinatesToRef(e.c.Vector3[3],a,e.c.Vector3[3]),e.e.TransformCoordinatesToRef(e.c.Vector3[4],a,e.c.Vector3[4]),e.e.TransformCoordinatesToRef(e.c.Vector3[5],a,e.c.Vector3[5]),e.c.Vector3[6].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[1].y,e.c.Vector3[1].z),e.c.Vector3[7].copyFromFloats(e.c.Vector3[1].x,e.c.Vector3[0].y,e.c.Vector3[1].z),e.c.Vector3[8].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[0].y,e.c.Vector3[1].z),e.c.Vector3[9].copyFromFloats(e.c.Vector3[0].x,e.c.Vector3[1].y,e.c.Vector3[1].z),e.e.TransformCoordinatesToRef(e.c.Vector3[6],a,e.c.Vector3[6]),e.e.TransformCoordinatesToRef(e.c.Vector3[7],a,e.c.Vector3[7]),e.e.TransformCoordinatesToRef(e.c.Vector3[8],a,e.c.Vector3[8]),e.e.TransformCoordinatesToRef(e.c.Vector3[9],a,e.c.Vector3[9]),Object(z.e)("nearlines",{updatable:!0,points:this._nearLinesPoints,instance:this._lightHelperFrustumMeshes[0]},this._scene),Object(z.e)("farlines",{updatable:!0,points:this._farLinesPoints,instance:this._lightHelperFrustumMeshes[1]},this._scene),Object(z.e)("trlines",{updatable:!0,points:this._trLinesPoints,instance:this._lightHelperFrustumMeshes[2]},this._scene),Object(z.e)("brlines",{updatable:!0,points:this._brLinesPoints,instance:this._lightHelperFrustumMeshes[3]},this._scene),Object(z.e)("tllines",{updatable:!0,points:this._tlLinesPoints,instance:this._lightHelperFrustumMeshes[4]},this._scene),Object(z.e)("bllines",{updatable:!0,points:this._blLinesPoints,instance:this._lightHelperFrustumMeshes[5]},this._scene),e.c.Vector3[2].toArray(this._nearPlaneVertices,0),e.c.Vector3[3].toArray(this._nearPlaneVertices,3),e.c.Vector3[4].toArray(this._nearPlaneVertices,6),e.c.Vector3[5].toArray(this._nearPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[6].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._nearPlaneVertices,0),e.c.Vector3[6].toArray(this._farPlaneVertices,0),e.c.Vector3[7].toArray(this._farPlaneVertices,3),e.c.Vector3[8].toArray(this._farPlaneVertices,6),e.c.Vector3[9].toArray(this._farPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[7].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._farPlaneVertices,0),e.c.Vector3[2].toArray(this._rightPlaneVertices,0),e.c.Vector3[6].toArray(this._rightPlaneVertices,3),e.c.Vector3[7].toArray(this._rightPlaneVertices,6),e.c.Vector3[3].toArray(this._rightPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[8].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._rightPlaneVertices,0),e.c.Vector3[5].toArray(this._leftPlaneVertices,0),e.c.Vector3[9].toArray(this._leftPlaneVertices,3),e.c.Vector3[8].toArray(this._leftPlaneVertices,6),e.c.Vector3[4].toArray(this._leftPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[9].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._leftPlaneVertices,0),e.c.Vector3[2].toArray(this._topPlaneVertices,0),e.c.Vector3[6].toArray(this._topPlaneVertices,3),e.c.Vector3[9].toArray(this._topPlaneVertices,6),e.c.Vector3[5].toArray(this._topPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[10].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._topPlaneVertices,0),e.c.Vector3[3].toArray(this._bottomPlaneVertices,0),e.c.Vector3[7].toArray(this._bottomPlaneVertices,3),e.c.Vector3[8].toArray(this._bottomPlaneVertices,6),e.c.Vector3[4].toArray(this._bottomPlaneVertices,9),null===(a=this._lightHelperFrustumMeshes[11].geometry)||void 0===a||a.updateVerticesDataDirectly("position",this._bottomPlaneVertices,0)}},a.prototype.dispose=function(){this._lightHelperFrustumMeshes.forEach(function(a){var b;null===(b=a.material)||void 0===b||b.dispose(),a.dispose()}),this._rootNode.dispose()},a.prototype._createGeometry=function(){var a=this;this._rootNode=new I.a("directionalLightHelperRoot_"+this._light.name,this._scene),this._rootNode.parent=this._light.parent,this._nearLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly];var b=Object(z.e)("nearlines",{updatable:!0,points:this._nearLinesPoints},this._scene);b.parent=this._rootNode,b.alwaysSelectAsActiveMesh=!0,this._farLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly,e.e.ZeroReadOnly];var c=Object(z.e)("farlines",{updatable:!0,points:this._farLinesPoints},this._scene);c.parent=this._rootNode,c.alwaysSelectAsActiveMesh=!0,this._trLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var d=Object(z.e)("trlines",{updatable:!0,points:this._trLinesPoints},this._scene);d.parent=this._rootNode,d.alwaysSelectAsActiveMesh=!0,this._brLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var g=Object(z.e)("brlines",{updatable:!0,points:this._brLinesPoints},this._scene);g.parent=this._rootNode,g.alwaysSelectAsActiveMesh=!0,this._tlLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var i=Object(z.e)("tllines",{updatable:!0,points:this._tlLinesPoints},this._scene);i.parent=this._rootNode,i.alwaysSelectAsActiveMesh=!0,this._blLinesPoints=[e.e.ZeroReadOnly,e.e.ZeroReadOnly];var j=Object(z.e)("bllines",{updatable:!0,points:this._blLinesPoints},this._scene);j.parent=this._rootNode,j.alwaysSelectAsActiveMesh=!0,this._lightHelperFrustumMeshes.push(b,c,d,g,i,j);b=function(b,c,d){var e=new q.a(b+"plane",a._scene);b=new f.a(b+"PlaneMat",a._scene);e.material=b,e.parent=a._rootNode,e.alwaysSelectAsActiveMesh=!0,b.emissiveColor=c,b.alpha=a.transparency,b.backFaceCulling=!1,b.disableLighting=!0;c=new H.a;c.positions=d,c.indices=[0,1,2,0,2,3],c.applyToMesh(e,!0),a._lightHelperFrustumMeshes.push(e)};this._nearPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._farPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._rightPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._leftPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._topPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],this._bottomPlaneVertices=[0,0,0,0,0,0,0,0,0,0,0,0],b("near",new h.a(1,0,0),this._nearPlaneVertices),b("far",new h.a(.3,0,0),this._farPlaneVertices),b("right",new h.a(0,1,0),this._rightPlaneVertices),b("left",new h.a(0,.3,0),this._leftPlaneVertices),b("top",new h.a(0,0,1),this._topPlaneVertices),b("bottom",new h.a(0,0,.3),this._bottomPlaneVertices),this._nearLinesPoints[0]=e.c.Vector3[2],this._nearLinesPoints[1]=e.c.Vector3[3],this._nearLinesPoints[2]=e.c.Vector3[4],this._nearLinesPoints[3]=e.c.Vector3[5],this._nearLinesPoints[4]=e.c.Vector3[2],this._farLinesPoints[0]=e.c.Vector3[6],this._farLinesPoints[1]=e.c.Vector3[7],this._farLinesPoints[2]=e.c.Vector3[8],this._farLinesPoints[3]=e.c.Vector3[9],this._farLinesPoints[4]=e.c.Vector3[6],this._trLinesPoints[0]=e.c.Vector3[2],this._trLinesPoints[1]=e.c.Vector3[6],this._brLinesPoints[0]=e.c.Vector3[3],this._brLinesPoints[1]=e.c.Vector3[7],this._tlLinesPoints[0]=e.c.Vector3[4],this._tlLinesPoints[1]=e.c.Vector3[8],this._blLinesPoints[0]=e.c.Vector3[5],this._blLinesPoints[1]=e.c.Vector3[9]},a.prototype._getInvertViewMatrix=function(){return e.a.LookAtLHToRef(this._light.position,this._light.position.add(this._light.direction),e.e.UpReadOnly,this._inverseViewMatrix),this._inverseViewMatrix.invertToRef(this._inverseViewMatrix),this._inverseViewMatrix},a}()},function(a,b,c){a="clipPlaneFragment";b="#if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) if (false) {} #endif #ifdef CLIPPLANE else if (fClipDistance>0.0) { discard; } #endif #ifdef CLIPPLANE2 else if (fClipDistance2>0.0) { discard; } #endif #ifdef CLIPPLANE3 else if (fClipDistance3>0.0) { discard; } #endif #ifdef CLIPPLANE4 else if (fClipDistance4>0.0) { discard; } #endif #ifdef CLIPPLANE5 else if (fClipDistance5>0.0) { discard; } #endif #ifdef CLIPPLANE6 else if (fClipDistance6>0.0) { discard; } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertexGlobal";b="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE float vertexID; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="morphTargetsVertex";b="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE vertexID=float(gl_VertexID)*morphTargetTextureInfo.x; positionUpdated+=(readVector3FromRawSampler({X},vertexID)-position)*morphTargetInfluences[{X}]; vertexID+=1.0; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(readVector3FromRawSampler({X},vertexID)-normal)*morphTargetInfluences[{X}]; vertexID+=1.0; #endif #ifdef MORPHTARGETS_UV uvUpdated+=(readVector3FromRawSampler({X},vertexID).xy-uv)*morphTargetInfluences[{X}]; vertexID+=1.0; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz+=(readVector3FromRawSampler({X},vertexID)-tangent.xyz)*morphTargetInfluences[{X}]; #endif #else positionUpdated+=(position{X}-position)*morphTargetInfluences[{X}]; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(normal{X}-normal)*morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz+=(tangent{X}-tangent.xyz)*morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_UV uvUpdated+=(uv_{X}-uv)*morphTargetInfluences[{X}]; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneVertex";b="#ifdef CLIPPLANE fClipDistance=dot(worldPos,vClipPlane); #endif #ifdef CLIPPLANE2 fClipDistance2=dot(worldPos,vClipPlane2); #endif #ifdef CLIPPLANE3 fClipDistance3=dot(worldPos,vClipPlane3); #endif #ifdef CLIPPLANE4 fClipDistance4=dot(worldPos,vClipPlane4); #endif #ifdef CLIPPLANE5 fClipDistance5=dot(worldPos,vClipPlane5); #endif #ifdef CLIPPLANE6 fClipDistance6=dot(worldPos,vClipPlane6); #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){var d;c.d(b,"a",function(){return d}),c.d(b,"b",function(){return e}),(function(a){a[a.PointerMove=0]="PointerMove",a[a.PointerDown=1]="PointerDown",a[a.PointerUp=2]="PointerUp"})(d||(d={}));var e=function(){function a(){}return a.DOM_DELTA_PIXEL=0,a.DOM_DELTA_LINE=1,a.DOM_DELTA_PAGE=2,a}()},function(a,b,c){c.d(b,"b",function(){return h}),c.d(b,"a",function(){return i});var d=c(2),e=c(0),f=c(3),g=function(){function a(){}return a.extractMinAndMaxIndexed=function(a,b,c,d,e,f){for(var g=c;g=f.c.LeftClick&&c<=f.c.RightClick&&(e.type=1===d?"pointerdown":"pointerup",e.button=c-2),e},a._createWheelEvent=function(a,b,c,d,g,h){h=this._createMouseEvent(a,b,c,d,g,h);return h.type="wheel",h.deltaMode=e.b.DOM_DELTA_PIXEL,h.deltaX=c===f.c.MouseWheelX?d:g.pollInput(a,b,f.c.MouseWheelX),h.deltaY=c===f.c.MouseWheelY?d:g.pollInput(a,b,f.c.MouseWheelY),h.deltaZ=c===f.c.MouseWheelZ?d:g.pollInput(a,b,f.c.MouseWheelZ),h},a._createMouseEvent=function(a,b,c,d,e,g){var h=this._createEvent(g),i=e.pollInput(a,b,f.c.Horizontal);a=e.pollInput(a,b,f.c.Vertical);b=c===f.c.DeltaHorizontal?d:0;d=c===f.c.DeltaVertical?d:0;var j=c===f.c.DeltaHorizontal&&g?b-g.getBoundingClientRect().x:0;c=c===f.c.DeltaVertical&&g?d-g.getBoundingClientRect().y:0;return this._checkNonCharacterKeys(h,e),h.clientX=i,h.clientY=a,h.movementX=b,h.movementY=d,h.offsetX=j,h.offsetY=c,h.x=i,h.y=a,h},a._createKeyboardEvent=function(a,b,c,d){d=this._createEvent(d);return this._checkNonCharacterKeys(d,c),d.type=1===b?"keydown":"keyup",d.key=String.fromCharCode(a),d.keyCode=a,d},a._checkNonCharacterKeys=function(a,b){var c=b.isDeviceAvailable(f.a.Keyboard),e=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_ALT_KEY),g=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_CTRL_KEY),h=c&&(1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY1)||1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY2)||1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_META_KEY3));c=c&&1===b.pollInput(f.a.Keyboard,0,d.a.INPUT_SHIFT_KEY);a.altKey=e,a.ctrlKey=g,a.metaKey=h,a.shiftKey=c},a._createEvent=function(a){var b={preventDefault:function(){}};return b.target=a,b},a}()},function(a,b,c){c.d(b,"a",function(){return g});var d=c(51),e=c(0),f=c(22),g=function(){function a(a,b,c){this.vectors=d.a.BuildArray(8,e.e.Zero),this.center=e.e.Zero(),this.centerWorld=e.e.Zero(),this.extendSize=e.e.Zero(),this.extendSizeWorld=e.e.Zero(),this.directions=d.a.BuildArray(3,e.e.Zero),this.vectorsWorld=d.a.BuildArray(8,e.e.Zero),this.minimumWorld=e.e.Zero(),this.maximumWorld=e.e.Zero(),this.minimum=e.e.Zero(),this.maximum=e.e.Zero(),this.reConstruct(a,b,c)}return a.prototype.reConstruct=function(a,b,c){var d=a.x,f=a.y,g=a.z,h=b.x,i=b.y,j=b.z,k=this.vectors;this.minimum.copyFromFloats(d,f,g),this.maximum.copyFromFloats(h,i,j),k[0].copyFromFloats(d,f,g),k[1].copyFromFloats(h,i,j),k[2].copyFromFloats(h,f,g),k[3].copyFromFloats(d,i,g),k[4].copyFromFloats(d,f,j),k[5].copyFromFloats(h,i,g),k[6].copyFromFloats(d,i,j),k[7].copyFromFloats(h,f,j),b.addToRef(a,this.center).scaleInPlace(.5),b.subtractToRef(a,this.extendSize).scaleInPlace(.5),this._worldMatrix=c||e.a.IdentityReadOnly,this._update(this._worldMatrix)},a.prototype.scale=function(b){var c=a.TmpVector3,d=this.maximum.subtractToRef(this.minimum,c[0]),e=d.length();d.normalizeFromLength(e);e=e*b;b=d.scaleInPlace(.5*e);d=this.center.subtractToRef(b,c[1]);e=this.center.addToRef(b,c[2]);return this.reConstruct(d,e,this._worldMatrix),this},a.prototype.getWorldMatrix=function(){return this._worldMatrix},a.prototype._update=function(a){var b=this.minimumWorld,c=this.maximumWorld,d=this.directions,f=this.vectorsWorld,g=this.vectors;if(a.isIdentity()){b.copyFrom(this.minimum),c.copyFrom(this.maximum);for(h=0;h<8;++h)f[h].copyFrom(g[h]);this.extendSizeWorld.copyFrom(this.extendSize),this.centerWorld.copyFrom(this.center)}else{b.setAll(Number.MAX_VALUE),c.setAll(-Number.MAX_VALUE);for(var h=0;h<8;++h){var i=f[h];e.e.TransformCoordinatesToRef(g[h],a,i),b.minimizeInPlace(i),c.maximizeInPlace(i)}c.subtractToRef(b,this.extendSizeWorld).scaleInPlace(.5),c.addToRef(b,this.centerWorld).scaleInPlace(.5)}e.e.FromArrayToRef(a.m,0,d[0]),e.e.FromArrayToRef(a.m,4,d[1]),e.e.FromArrayToRef(a.m,8,d[2]),this._worldMatrix=a},a.prototype.isInFrustum=function(b){return a.IsInFrustum(this.vectorsWorld,b)},a.prototype.isCompletelyInFrustum=function(b){return a.IsCompletelyInFrustum(this.vectorsWorld,b)},a.prototype.intersectsPoint=function(a){var b=this.minimumWorld,c=this.maximumWorld,d=b.x,e=b.y;b=b.z;var g=c.x,h=c.y;c=c.z;var i=a.x,j=a.y;a=a.z;var k=-f.a;return!(g-ii-d)&&!(h-jj-e)&&!(c-aa-b)},a.prototype.intersectsSphere=function(b){return a.IntersectsSphere(this.minimumWorld,this.maximumWorld,b.centerWorld,b.radiusWorld)},a.prototype.intersectsMinMax=function(a,b){var c=this.minimumWorld,d=this.maximumWorld,e=c.x,f=c.y;c=c.z;var g=d.x,h=d.y;d=d.z;var i=a.x,j=a.y;a=a.z;var k=b.x,l=b.y;b=b.z;return!(gk)&&!(hl)&&!(db)},a.Intersects=function(a,b){return a.intersectsMinMax(b.minimumWorld,b.maximumWorld)},a.IntersectsSphere=function(b,c,d,f){var g=a.TmpVector3[0];return e.e.ClampToRef(d,b,c,g),e.e.DistanceSquared(d,g)<=f*f},a.IsCompletelyInFrustum=function(a,b){for(var c=0;c<6;++c)for(var d=b[c],e=0;e<8;++e)if(d.dotCoordinate(a[e])<0)return!1;return!0},a.IsInFrustum=function(a,b){for(var c=0;c<6;++c){for(var d=!0,e=b[c],f=0;f<8;++f)if(e.dotCoordinate(a[f])>=0){d=!1;break}if(d)return!1}return!0},a.TmpVector3=d.a.BuildArray(3,e.e.Zero),a}()},function(a,b,c){c.d(b,"a",function(){return g});var d=c(24),e=c(85),f=c(1),g=function(){function a(a,b,c,d){this._textures=null,this._attachments=null,this._generateStencilBuffer=!1,this._generateDepthBuffer=!1,this._depthStencilTextureWithStencil=!1,this._isMulti=a,this._isCube=b,this._size=c,this._engine=d,this._depthStencilTexture=null}return Object.defineProperty(a.prototype,"isCube",{get:function(){return this._isCube},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isMulti",{get:function(){return this._isMulti},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"is2DArray",{get:function(){return this.layers>0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"size",{get:function(){return this.width},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"width",{get:function(){return this._size.width||this._size},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"height",{get:function(){return this._size.height||this._size},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"layers",{get:function(){return this._size.layers||0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"texture",{get:function(){var a;return null!==(a=null===(a=this._textures)||void 0===a?void 0:a[0])&&void 0!==a?a:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"textures",{get:function(){return this._textures},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"samples",{get:function(){var a;return null!==(a=null===(a=this.texture)||void 0===a?void 0:a.samples)&&void 0!==a?a:1},enumerable:!1,configurable:!0}),a.prototype.setSamples=function(a,b,c){return void 0===b&&(b=!0),void 0===c&&(c=!1),this.samples!==a||c?this._isMulti?this._engine.updateMultipleRenderTargetTextureSampleCount(this,a,b):this._engine.updateRenderTargetTextureSampleCount(this,a):a},a.prototype.setTextures=function(a){Array.isArray(a)?this._textures=a:this._textures=a?[a]:null},a.prototype.setTexture=function(a,b,c){void 0===b&&(b=0),void 0===c&&(c=!0),this._textures||(this._textures=[]),this._textures[b]&&c&&this._textures[b].dispose(),this._textures[b]=a},a.prototype.createDepthStencilTexture=function(a,b,c,d,e){var g;return void 0===a&&(a=0),void 0===b&&(b=!0),void 0===c&&(c=!1),void 0===d&&(d=1),void 0===e&&(e=f.a.TEXTUREFORMAT_DEPTH16),null===(g=this._depthStencilTexture)||void 0===g||g.dispose(),this._depthStencilTextureWithStencil=c,this._depthStencilTexture=this._engine.createDepthStencilTexture(this._size,{bilinearFiltering:b,comparisonFunction:a,generateStencil:c,isCube:this._isCube,samples:d,depthTextureFormat:e},this),this._depthStencilTexture},a.prototype._shareDepth=function(a){this._depthStencilTexture&&(a._depthStencilTexture&&a._depthStencilTexture.dispose(),a._depthStencilTexture=this._depthStencilTexture,this._depthStencilTexture.incrementReferences())},a.prototype._swapAndDie=function(a){a&&this.texture&&this.texture._swapAndDie(a),this._textures=null,this.dispose(!0)},a.prototype._cloneRenderTargetWrapper=function(){var a=null;if(this._isMulti){var b=this.textures;if(b&&b.length>0){var c=!1,f=b.length,g=b[b.length-1]._source;g!==d.b.Depth&&g!==d.b.DepthStencil||(c=!0,f--);for(var g=[],h=[],i=0;i1&&a.setSamples(this.samples),a._swapRenderTargetWrapper(this),a.dispose()}},a.prototype.releaseTextures=function(){var a;if(this._textures)for(var b=0;null!==(a=b<(null===(a=this._textures)||void 0===a?void 0:a.length))&&void 0!==a&&a;++b)this._textures[b].dispose();this._textures=null},a.prototype.dispose=function(a){void 0===a&&(a=!1),a||(null===(a=this._depthStencilTexture)||void 0===a||a.dispose(),this._depthStencilTexture=null,this.releaseTextures()),this._engine._releaseRenderTargetWrapper(this)},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.previousWorldMatrices={},this.previousBones={}}return a.AddUniforms=function(a){a.push("previousWorld","previousViewProjection","mPreviousBones")},a.AddSamplers=function(a){},a.prototype.bindForSubMesh=function(a,b,c,e,f){if(b.prePassRenderer&&b.prePassRenderer.enabled&&b.prePassRenderer.currentRTisSceneRT&&-1!==b.prePassRenderer.getIndex(d.a.PREPASS_VELOCITY_TEXTURE_TYPE)){this.previousWorldMatrices[c.uniqueId]||(this.previousWorldMatrices[c.uniqueId]=e.clone()),this.previousViewProjection||(this.previousViewProjection=b.getTransformMatrix().clone(),this.currentViewProjection=b.getTransformMatrix().clone());f=b.getEngine();this.currentViewProjection.updateFlag!==b.getTransformMatrix().updateFlag?(this._lastUpdateFrameId=f.frameId,this.previousViewProjection.copyFrom(this.currentViewProjection),this.currentViewProjection.copyFrom(b.getTransformMatrix())):this._lastUpdateFrameId!==f.frameId&&(this._lastUpdateFrameId=f.frameId,this.previousViewProjection.copyFrom(this.currentViewProjection)),a.setMatrix("previousWorld",this.previousWorldMatrices[c.uniqueId]),a.setMatrix("previousViewProjection",this.previousViewProjection),this.previousWorldMatrices[c.uniqueId]=e.clone()}},a}()},function(a,b,c){a="imageProcessingDeclaration";b="#ifdef EXPOSURE uniform float exposureLinear; #endif #ifdef CONTRAST uniform float contrast; #endif #ifdef VIGNETTE uniform vec2 vInverseScreenSize; uniform vec4 vignetteSettings1; uniform vec4 vignetteSettings2; #endif #ifdef COLORCURVES uniform vec4 vCameraColorCurveNegative; uniform vec4 vCameraColorCurveNeutral; uniform vec4 vCameraColorCurvePositive; #endif #ifdef COLORGRADING #ifdef COLORGRADING3D uniform highp sampler3D txColorTransform; #else uniform sampler2D txColorTransform; #endif uniform vec4 colorTransformSettings; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="imageProcessingFunctions";b="#if defined(COLORGRADING) && !defined(COLORGRADING3D) #define inline vec3 sampleTexture3D(sampler2D colorTransform,vec3 color,vec2 sampler3dSetting) { float sliceSize=2.0*sampler3dSetting.x; #ifdef SAMPLER3DGREENDEPTH float sliceContinuous=(color.g-sampler3dSetting.x)*sampler3dSetting.y; #else float sliceContinuous=(color.b-sampler3dSetting.x)*sampler3dSetting.y; #endif float sliceInteger=floor(sliceContinuous); float sliceFraction=sliceContinuous-sliceInteger; #ifdef SAMPLER3DGREENDEPTH vec2 sliceUV=color.rb; #else vec2 sliceUV=color.rg; #endif sliceUV.x*=sliceSize; sliceUV.x+=sliceInteger*sliceSize; sliceUV=saturate(sliceUV); vec4 slice0Color=texture2D(colorTransform,sliceUV); sliceUV.x+=sliceSize; sliceUV=saturate(sliceUV); vec4 slice1Color=texture2D(colorTransform,sliceUV); vec3 result=mix(slice0Color.rgb,slice1Color.rgb,sliceFraction); #ifdef SAMPLER3DBGRMAP color.rgb=result.rgb; #else color.rgb=result.bgr; #endif return color; } #endif #ifdef TONEMAPPING_ACES const mat3 ACESInputMat=mat3( vec3(0.59719,0.07600,0.02840), vec3(0.35458,0.90834,0.13383), vec3(0.04823,0.01566,0.83777) ); const mat3 ACESOutputMat=mat3( vec3( 1.60475,-0.10208,-0.00327), vec3(-0.53108,1.10813,-0.07276), vec3(-0.07367,-0.00605,1.07602) ); vec3 RRTAndODTFit(vec3 v) { vec3 a=v*(v+0.0245786)-0.000090537; vec3 b=v*(0.983729*v+0.4329510)+0.238081; return a/b; } vec3 ACESFitted(vec3 color) { color=ACESInputMat*color; color=RRTAndODTFit(color); color=ACESOutputMat*color; color=saturate(color); return color; } #endif vec4 applyImageProcessing(vec4 result) { #ifdef EXPOSURE result.rgb*=exposureLinear; #endif #ifdef VIGNETTE vec2 viewportXY=gl_FragCoord.xy*vInverseScreenSize; viewportXY=viewportXY*2.0-1.0; vec3 vignetteXY1=vec3(viewportXY*vignetteSettings1.xy+vignetteSettings1.zw,1.0); float vignetteTerm=dot(vignetteXY1,vignetteXY1); float vignette=pow(vignetteTerm,vignetteSettings2.w); vec3 vignetteColor=vignetteSettings2.rgb; #ifdef VIGNETTEBLENDMODEMULTIPLY vec3 vignetteColorMultiplier=mix(vignetteColor,vec3(1,1,1),vignette); result.rgb*=vignetteColorMultiplier; #endif #ifdef VIGNETTEBLENDMODEOPAQUE result.rgb=mix(vignetteColor,result.rgb,vignette); #endif #endif #ifdef TONEMAPPING #ifdef TONEMAPPING_ACES result.rgb=ACESFitted(result.rgb); #else const float tonemappingCalibration=1.590579; result.rgb=1.0-exp2(-tonemappingCalibration*result.rgb); #endif #endif result.rgb=toGammaSpace(result.rgb); result.rgb=saturate(result.rgb); #ifdef CONTRAST vec3 resultHighContrast=result.rgb*result.rgb*(3.0-2.0*result.rgb); if (contrast<1.0) { result.rgb=mix(vec3(0.5,0.5,0.5),result.rgb,contrast); } else { result.rgb=mix(result.rgb,resultHighContrast,contrast-1.0); } #endif #ifdef COLORGRADING vec3 colorTransformInput=result.rgb*colorTransformSettings.xxx+colorTransformSettings.yyy; #ifdef COLORGRADING3D vec3 colorTransformOutput=texture(txColorTransform,colorTransformInput).rgb; #else vec3 colorTransformOutput=sampleTexture3D(txColorTransform,colorTransformInput,colorTransformSettings.yz).rgb; #endif result.rgb=mix(result.rgb,colorTransformOutput,colorTransformSettings.www); #endif #ifdef COLORCURVES float luma=getLuminance(result.rgb); vec2 curveMix=clamp(vec2(luma*3.0-1.5,luma*-3.0+1.5),vec2(0.0),vec2(1.0)); vec4 colorCurve=vCameraColorCurveNeutral+curveMix.x*vCameraColorCurvePositive-curveMix.y*vCameraColorCurveNegative; result.rgb*=colorCurve.rgb; result.rgb=mix(vec3(luma),result.rgb,colorCurve.a); #endif return result; }";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneFragmentDeclaration";b="#ifdef CLIPPLANE varying float fClipDistance; #endif #ifdef CLIPPLANE2 varying float fClipDistance2; #endif #ifdef CLIPPLANE3 varying float fClipDistance3; #endif #ifdef CLIPPLANE4 varying float fClipDistance4; #endif #ifdef CLIPPLANE5 varying float fClipDistance5; #endif #ifdef CLIPPLANE6 varying float fClipDistance6; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="clipPlaneVertexDeclaration";b="#ifdef CLIPPLANE uniform vec4 vClipPlane; varying float fClipDistance; #endif #ifdef CLIPPLANE2 uniform vec4 vClipPlane2; varying float fClipDistance2; #endif #ifdef CLIPPLANE3 uniform vec4 vClipPlane3; varying float fClipDistance3; #endif #ifdef CLIPPLANE4 uniform vec4 vClipPlane4; varying float fClipDistance4; #endif #ifdef CLIPPLANE5 uniform vec4 vClipPlane5; varying float fClipDistance5; #endif #ifdef CLIPPLANE6 uniform vec4 vClipPlane6; varying float fClipDistance6; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return e});a=c(27);var d=c(1);function e(a,b,c,e){switch(void 0===c&&(c=!1),a){case d.a.TEXTURETYPE_BYTE:a=new Int8Array(b);return e&&a.set(new Int8Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_BYTE:a=new Uint8Array(b);return e&&a.set(new Uint8Array(e)),a;case d.a.TEXTURETYPE_SHORT:a=b instanceof ArrayBuffer?new Int16Array(b):new Int16Array(c?b/2:b);return e&&a.set(new Int16Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_SHORT:case d.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:case d.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:case d.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:case d.a.TEXTURETYPE_HALF_FLOAT:a=b instanceof ArrayBuffer?new Uint16Array(b):new Uint16Array(c?b/2:b);return e&&a.set(new Uint16Array(e)),a;case d.a.TEXTURETYPE_INT:a=b instanceof ArrayBuffer?new Int32Array(b):new Int32Array(c?b/4:b);return e&&a.set(new Int32Array(e)),a;case d.a.TEXTURETYPE_UNSIGNED_INTEGER:case d.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:case d.a.TEXTURETYPE_UNSIGNED_INT_24_8:case d.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:case d.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:case d.a.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV:a=b instanceof ArrayBuffer?new Uint32Array(b):new Uint32Array(c?b/4:b);return e&&a.set(new Uint32Array(e)),a;case d.a.TEXTURETYPE_FLOAT:a=b instanceof ArrayBuffer?new Float32Array(b):new Float32Array(c?b/4:b);return e&&a.set(new Float32Array(e)),a}c=new Uint8Array(b);return e&&c.set(new Uint8Array(e)),c}a.a.prototype._readTexturePixelsSync=function(a,b,c,f,d,g,h,i){void 0===f&&(f=-1),void 0===d&&(d=0),void 0===g&&(g=null),void 0===h&&(h=!0),void 0===i&&(i=!1);var j=this._gl;if(!j)throw new Error("Engine does not have gl rendering context.");if(!this._dummyFramebuffer){var k=j.createFramebuffer();if(!k)throw new Error("Unable to create dummy framebuffer");this._dummyFramebuffer=k}j.bindFramebuffer(j.FRAMEBUFFER,this._dummyFramebuffer),f>-1?j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_CUBE_MAP_POSITIVE_X+f,null===(k=a._hardwareTexture)||void 0===k?void 0:k.underlyingResource,d):j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,j.TEXTURE_2D,null===(f=a._hardwareTexture)||void 0===f?void 0:f.underlyingResource,d);k=void 0!==a.type?this._getWebGLTextureType(a.type):j.UNSIGNED_BYTE;if(i)g||(g=e(a.type,4*b*c));else switch(k){case j.UNSIGNED_BYTE:g||(g=new Uint8Array(4*b*c)),k=j.UNSIGNED_BYTE;break;default:g||(g=new Float32Array(4*b*c)),k=j.FLOAT}return h&&this.flushFramebuffer(),j.readPixels(0,0,b,c,j.RGBA,k,g),j.bindFramebuffer(j.FRAMEBUFFER,this._currentFramebuffer),g},a.a.prototype._readTexturePixels=function(a,b,c,f,d,e,g,h){return void 0===f&&(f=-1),void 0===d&&(d=0),void 0===e&&(e=null),void 0===g&&(g=!0),void 0===h&&(h=!1),Promise.resolve(this._readTexturePixelsSync(a,b,c,f,d,e,g,h))}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._count=0,this._data={}}return a.prototype.copyFrom=function(a){var b=this;this.clear(),a.forEach(function(a,c){return b.add(a,c)})},a.prototype.get=function(a){a=this._data[a];if(void 0!==a)return a},a.prototype.getOrAddWithFactory=function(a,b){var c=this.get(a);return void 0!==c||(c=b(a))&&this.add(a,c),c},a.prototype.getOrAdd=function(a,b){var c=this.get(a);return void 0!==c?c:(this.add(a,b),b)},a.prototype.contains=function(a){return void 0!==this._data[a]},a.prototype.add=function(a,b){return void 0===this._data[a]&&(this._data[a]=b,++this._count,!0)},a.prototype.set=function(a,b){return void 0!==this._data[a]&&(this._data[a]=b,!0)},a.prototype.getAndRemove=function(a){var b=this.get(a);return void 0!==b?(delete this._data[a],--this._count,b):null},a.prototype.remove=function(a){return!!this.contains(a)&&(delete this._data[a],--this._count,!0)},a.prototype.clear=function(){this._data={},this._count=0},Object.defineProperty(a.prototype,"count",{get:function(){return this._count},enumerable:!1,configurable:!0}),a.prototype.forEach=function(a){for(var b in this._data)a(b,this._data[b])},a.prototype.first=function(a){for(var b in this._data){var c=a(b,this._data[b]);if(c)return c}return null},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a,b,c){this.bu=a,this.bv=b,this.distance=c,this.faceId=0,this.subMeshId=0}},function(a,b,c){a="logDepthDeclaration";b="#ifdef LOGARITHMICDEPTH uniform float logarithmicDepthConstant; varying float vFragmentDepth; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a){void 0===a&&(a=!0),this._isDepthTestDirty=!1,this._isDepthMaskDirty=!1,this._isDepthFuncDirty=!1,this._isCullFaceDirty=!1,this._isCullDirty=!1,this._isZOffsetDirty=!1,this._isFrontFaceDirty=!1,a&&this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isDepthFuncDirty||this._isDepthTestDirty||this._isDepthMaskDirty||this._isCullFaceDirty||this._isCullDirty||this._isZOffsetDirty||this._isFrontFaceDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zOffset",{get:function(){return this._zOffset},set:function(a){this._zOffset!==a&&(this._zOffset=a,this._isZOffsetDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zOffsetUnits",{get:function(){return this._zOffsetUnits},set:function(a){this._zOffsetUnits!==a&&(this._zOffsetUnits=a,this._isZOffsetDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cullFace",{get:function(){return this._cullFace},set:function(a){this._cullFace!==a&&(this._cullFace=a,this._isCullFaceDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cull",{get:function(){return this._cull},set:function(a){this._cull!==a&&(this._cull=a,this._isCullDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthFunc",{get:function(){return this._depthFunc},set:function(a){this._depthFunc!==a&&(this._depthFunc=a,this._isDepthFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthMask",{get:function(){return this._depthMask},set:function(a){this._depthMask!==a&&(this._depthMask=a,this._isDepthMaskDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthTest",{get:function(){return this._depthTest},set:function(a){this._depthTest!==a&&(this._depthTest=a,this._isDepthTestDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frontFace",{get:function(){return this._frontFace},set:function(a){this._frontFace!==a&&(this._frontFace=a,this._isFrontFaceDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this._depthMask=!0,this._depthTest=!0,this._depthFunc=null,this._cullFace=null,this._cull=null,this._zOffset=0,this._zOffsetUnits=0,this._frontFace=null,this._isDepthTestDirty=!0,this._isDepthMaskDirty=!0,this._isDepthFuncDirty=!1,this._isCullFaceDirty=!1,this._isCullDirty=!1,this._isZOffsetDirty=!0,this._isFrontFaceDirty=!1},a.prototype.apply=function(a){this.isDirty&&(this._isCullDirty&&(this.cull?a.enable(a.CULL_FACE):a.disable(a.CULL_FACE),this._isCullDirty=!1),this._isCullFaceDirty&&(a.cullFace(this.cullFace),this._isCullFaceDirty=!1),this._isDepthMaskDirty&&(a.depthMask(this.depthMask),this._isDepthMaskDirty=!1),this._isDepthTestDirty&&(this.depthTest?a.enable(a.DEPTH_TEST):a.disable(a.DEPTH_TEST),this._isDepthTestDirty=!1),this._isDepthFuncDirty&&(a.depthFunc(this.depthFunc),this._isDepthFuncDirty=!1),this._isZOffsetDirty&&(this.zOffset||this.zOffsetUnits?(a.enable(a.POLYGON_OFFSET_FILL),a.polygonOffset(this.zOffset,this.zOffsetUnits)):a.disable(a.POLYGON_OFFSET_FILL),this._isZOffsetDirty=!1),this._isFrontFaceDirty&&(a.frontFace(this.frontFace),this._isFrontFaceDirty=!1))},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.samplingMode=-1,this._useMipMaps=!0,this._cachedWrapU=null,this._cachedWrapV=null,this._cachedWrapR=null,this._cachedAnisotropicFilteringLevel=null,this._comparisonFunction=0}return Object.defineProperty(a.prototype,"wrapU",{get:function(){return this._cachedWrapU},set:function(a){this._cachedWrapU=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapV",{get:function(){return this._cachedWrapV},set:function(a){this._cachedWrapV=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"wrapR",{get:function(){return this._cachedWrapR},set:function(a){this._cachedWrapR=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"anisotropicFilteringLevel",{get:function(){return this._cachedAnisotropicFilteringLevel},set:function(a){this._cachedAnisotropicFilteringLevel=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"comparisonFunction",{get:function(){return this._comparisonFunction},set:function(a){this._comparisonFunction=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useMipMaps",{get:function(){return this._useMipMaps},set:function(a){this._useMipMaps=a},enumerable:!1,configurable:!0}),a.prototype.setParameters=function(a,b,c,e,f,g){return void 0===a&&(a=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===b&&(b=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===c&&(c=d.a.TEXTURE_WRAP_ADDRESSMODE),void 0===e&&(e=1),void 0===f&&(f=d.a.TEXTURE_BILINEAR_SAMPLINGMODE),void 0===g&&(g=0),this._cachedWrapU=a,this._cachedWrapV=b,this._cachedWrapR=c,this._cachedAnisotropicFilteringLevel=e,this.samplingMode=f,this._comparisonFunction=g,this},a.prototype.compareSampler=function(a){return this._cachedWrapU===a._cachedWrapU&&this._cachedWrapV===a._cachedWrapV&&this._cachedWrapR===a._cachedWrapR&&this._cachedAnisotropicFilteringLevel===a._cachedAnisotropicFilteringLevel&&this.samplingMode===a.samplingMode&&this._comparisonFunction===a._comparisonFunction&&this._useMipMaps===a._useMipMaps},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(41),e=function(){function a(){this.shaderLanguage=d.a.GLSL}return a.prototype.attributeProcessor=function(a){return a.replace("attribute","in")},a.prototype.varyingProcessor=function(a,b){return a.replace("varying",b?"in":"out")},a.prototype.postProcessor=function(a,b,c,d,e){d=-1!==a.search(/#extension.+GL_EXT_draw_buffers.+require/);if(a=(a=a.replace(/#extension.+(GL_OVR_multiview2|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g,"")).replace(/texture2Ds*(/g,"texture("),c)a=(a=(a=(a=(a=(a=(a=a.replace(/texture2DLodEXTs*(/g,"textureLod(")).replace(/textureCubeLodEXTs*(/g,"textureLod(")).replace(/textureCubes*(/g,"texture(")).replace(/gl_FragDepthEXT/g,"gl_FragDepth")).replace(/gl_FragColor/g,"glFragColor")).replace(/gl_FragData/g,"glFragData")).replace(/voids+?mains*(/g,(d?"":"out vec4 glFragColor; ")+"void main(");else if(-1!==b.indexOf("#define MULTIVIEW"))return"#extension GL_OVR_multiview2 : require layout (num_views = 2) in; "+a;return a},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a,b){if(void 0===a&&(a=null),this._MSAARenderBuffer=null,this._context=b,!a&&!(a=b.createTexture()))throw new Error("Unable to create webGL texture");this.set(a)}return Object.defineProperty(a.prototype,"underlyingResource",{get:function(){return this._webGLTexture},enumerable:!1,configurable:!0}),a.prototype.setUsage=function(a,b,c,d,e){},a.prototype.set=function(a){this._webGLTexture=a},a.prototype.reset=function(){this._webGLTexture=null,this._MSAARenderBuffer=null},a.prototype.release=function(){this._MSAARenderBuffer&&(this._context.deleteRenderbuffer(this._MSAARenderBuffer),this._MSAARenderBuffer=null),this._webGLTexture&&this._context.deleteTexture(this._webGLTexture),this.reset()},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(a){void 0===a&&(a=!0),this._isStencilTestDirty=!1,this._isStencilMaskDirty=!1,this._isStencilFuncDirty=!1,this._isStencilOpDirty=!1,this.useStencilGlobalOnly=!1,a&&this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isStencilTestDirty||this._isStencilMaskDirty||this._isStencilFuncDirty||this._isStencilOpDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"func",{get:function(){return this._func},set:function(a){this._func!==a&&(this._func=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"funcRef",{get:function(){return this._funcRef},set:function(a){this._funcRef!==a&&(this._funcRef=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"funcMask",{get:function(){return this._funcMask},set:function(a){this._funcMask!==a&&(this._funcMask=a,this._isStencilFuncDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opStencilFail",{get:function(){return this._opStencilFail},set:function(a){this._opStencilFail!==a&&(this._opStencilFail=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opDepthFail",{get:function(){return this._opDepthFail},set:function(a){this._opDepthFail!==a&&(this._opDepthFail=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"opStencilDepthPass",{get:function(){return this._opStencilDepthPass},set:function(a){this._opStencilDepthPass!==a&&(this._opStencilDepthPass=a,this._isStencilOpDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mask",{get:function(){return this._mask},set:function(a){this._mask!==a&&(this._mask=a,this._isStencilMaskDirty=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enabled",{get:function(){return this._enabled},set:function(a){this._enabled!==a&&(this._enabled=a,this._isStencilTestDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.reset=function(){var a;this.stencilMaterial=void 0,null===(a=this.stencilGlobal)||void 0===a||a.reset(),this._isStencilTestDirty=!0,this._isStencilMaskDirty=!0,this._isStencilFuncDirty=!0,this._isStencilOpDirty=!0},a.prototype.apply=function(a){var b;if(a){b=!this.useStencilGlobalOnly&&!!(null===(b=this.stencilMaterial)||void 0===b?void 0:b.enabled);this.enabled=b?this.stencilMaterial.enabled:this.stencilGlobal.enabled,this.func=b?this.stencilMaterial.func:this.stencilGlobal.func,this.funcRef=b?this.stencilMaterial.funcRef:this.stencilGlobal.funcRef,this.funcMask=b?this.stencilMaterial.funcMask:this.stencilGlobal.funcMask,this.opStencilFail=b?this.stencilMaterial.opStencilFail:this.stencilGlobal.opStencilFail,this.opDepthFail=b?this.stencilMaterial.opDepthFail:this.stencilGlobal.opDepthFail,this.opStencilDepthPass=b?this.stencilMaterial.opStencilDepthPass:this.stencilGlobal.opStencilDepthPass,this.mask=b?this.stencilMaterial.mask:this.stencilGlobal.mask,this.isDirty&&(this._isStencilTestDirty&&(this.enabled?a.enable(a.STENCIL_TEST):a.disable(a.STENCIL_TEST),this._isStencilTestDirty=!1),this._isStencilMaskDirty&&(a.stencilMask(this.mask),this._isStencilMaskDirty=!1),this._isStencilFuncDirty&&(a.stencilFunc(this.func,this.funcRef,this.funcMask),this._isStencilFuncDirty=!1),this._isStencilOpDirty&&(a.stencilOp(this.opStencilFail,this.opDepthFail,this.opStencilDepthPass),this._isStencilOpDirty=!1))}},a}()},function(a,b,c){c.d(b,"a",function(){return f});var d=c(170),e=c(171),f=function(){function a(){}return a.Create=function(a){return a.deviceInputSystem||(a.deviceInputSystem="undefined"!=typeof _native&&_native.DeviceInputSystem?new d.a(new _native.DeviceInputSystem):new e.a(a)),a.deviceInputSystem},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){}return a.BindClipPlane=function(a,b){if(b.clipPlane){var c=b.clipPlane;a.setFloat4("vClipPlane",c.normal.x,c.normal.y,c.normal.z,c.d)}b.clipPlane2&&(c=b.clipPlane2,a.setFloat4("vClipPlane2",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane3&&(c=b.clipPlane3,a.setFloat4("vClipPlane3",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane4&&(c=b.clipPlane4,a.setFloat4("vClipPlane4",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane5&&(c=b.clipPlane5,a.setFloat4("vClipPlane5",c.normal.x,c.normal.y,c.normal.z,c.d));b.clipPlane6&&(c=b.clipPlane6,a.setFloat4("vClipPlane6",c.normal.x,c.normal.y,c.normal.z,c.d))},a}()},function(a,b,c){c.d(b,"c",function(){return g}),c.d(b,"b",function(){return h}),c.d(b,"a",function(){return i});var d=c(18),e=c(0),f=c(9);function g(a){void 0===a&&(a={subdivisions:2,tessellation:16,height:1,radius:.25,capSubdivisions:6});var b,c,f=Math.max(a.subdivisions?a.subdivisions:2,1),g=Math.max(a.tessellation?a.tessellation:16,3),h=Math.max(a.height?a.height:1,0),i=Math.max(a.radius?a.radius:.25,0),j=Math.max(a.capSubdivisions?a.capSubdivisions:6,1);g=g;f=f;var k=Math.max(a.radiusTop?a.radiusTop:i,0);i=Math.max(a.radiusBottom?a.radiusBottom:i,0);var l=h-(k+i),m=2*Math.PI,n=Math.max(a.topCapSubdivisions?a.topCapSubdivisions:j,1);j=Math.max(a.bottomCapSubdivisions?a.bottomCapSubdivisions:j,1);var o=Math.acos((i-k)/h),p=[],q=[],r=[],s=[],t=0,u=[];l=.5*l;var v=.5*Math.PI,w=e.e.Zero(),x=e.e.Zero(),y=Math.cos(o),z=Math.sin(o),A=new e.d(k*z,l+k*y).subtract(new e.d(i*z,i*y-l)).length(),B=k*o+A+i*(v-o),C=0;for(c=0;c<=n;c++){var D=[],E=v-o*(c/n);C+=k*o/n;var F=Math.cos(E),aa=Math.sin(E),G=F*k;for(b=0;b<=g;b++){var H=(ba=b/g)*m+0,I=Math.sin(H),J=Math.cos(H);x.x=G*I,x.y=l+aa*k,x.z=G*J,q.push(x.x,x.y,x.z),w.set(F*I,aa,F*J),r.push(w.x,w.y,w.z),s.push(ba,1-C/B),D.push(t),t++}u.push(D)}h=h-k-i+y*k-y*i;var K=z*(i-k)/h;for(c=1;c<=f;c++){D=[];C+=A/f;G=z*(c*(i-k)/f+k);for(b=0;b<=g;b++)(H=(ba=b/g)*m+0,I=Math.sin(H),J=Math.cos(H)),(x.x=G*I,x.y=l+y*k-c*h/f,x.z=G*J,q.push(x.x,x.y,x.z),w.set(I,K,J).normalize(),r.push(w.x,w.y,w.z),s.push(ba,1-C/B),D.push(t),t++);u.push(D)}for(c=1;c<=j;c++){D=[],E=v-o-(Math.PI-o)*(c/j);C+=i*o/j;F=Math.cos(E),aa=Math.sin(E),G=F*i;for(b=0;b<=g;b++){var ba;H=(ba=b/g)*m+0,I=Math.sin(H),J=Math.cos(H);x.x=G*I,x.y=aa*i-l,x.z=G*J,q.push(x.x,x.y,x.z),w.set(F*I,aa,F*J),r.push(w.x,w.y,w.z),s.push(ba,1-C/B),D.push(t),t++}u.push(D)}for(b=0;b=a&&0===c?b instanceof Array?this._gl.bufferSubData(this._gl.ARRAY_BUFFER,c,new Float32Array(b)):this._gl.bufferSubData(this._gl.ARRAY_BUFFER,c,b):b instanceof Array?this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,new Float32Array(b).subarray(c,c+d)):(b=b instanceof ArrayBuffer?new Uint8Array(b,c,d):new Uint8Array(b.buffer,b.byteOffset+c,d),this._gl.bufferSubData(this._gl.ARRAY_BUFFER,0,b)),this._resetVertexBufferBinding()}},function(a,b,c){a="sceneUboDeclaration";b="layout(std140,column_major) uniform; uniform Scene { mat4 viewProjection; #ifdef MULTIVIEW mat4 viewProjectionR; #endif mat4 view; mat4 projection; vec4 vEyePosition; }; ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="fogFragmentDeclaration";b="#ifdef FOG #define FOGMODE_NONE 0. #define FOGMODE_EXP 1. #define FOGMODE_EXP2 2. #define FOGMODE_LINEAR 3. #define E 2.71828 uniform vec4 vFogInfos; uniform vec3 vFogColor; varying vec3 vFogDistance; float CalcFogFactor() { float fogCoeff=1.0; float fogStart=vFogInfos.y; float fogEnd=vFogInfos.z; float fogDensity=vFogInfos.w; float fogDistance=length(vFogDistance); if (FOGMODE_LINEAR == vFogInfos.x) { fogCoeff=(fogEnd-fogDistance)/(fogEnd-fogStart); } else if (FOGMODE_EXP == vFogInfos.x) { fogCoeff=1.0/pow(E,fogDistance*fogDensity); } else if (FOGMODE_EXP2 == vFogInfos.x) { fogCoeff=1.0/pow(E,fogDistance*fogDistance*fogDensity*fogDensity); } return clamp(fogCoeff,0.0,1.0); } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){var d=c(27),e=c(24);d.a.prototype.createDynamicTexture=function(a,b,c,f){var g=new e.a(this,e.b.Dynamic);return g.baseWidth=a,g.baseHeight=b,c&&(a=this.needPOTTextures?d.a.GetExponentOfTwo(a,this._caps.maxTextureSize):a,b=this.needPOTTextures?d.a.GetExponentOfTwo(b,this._caps.maxTextureSize):b),g.width=a,g.height=b,g.isReady=!1,g.generateMipMaps=c,g.samplingMode=f,this.updateTextureSamplingMode(f,g),this._internalTexturesCache.push(g),g},d.a.prototype.updateDynamicTexture=function(a,b,c,d,e,f){if(void 0===d&&(d=!1),void 0===f&&(f=!1),a){var g=this._gl,h=g.TEXTURE_2D;f=this._bindTextureDirectly(h,a,!0,f);this._unpackFlipY(void 0===c?a.invertY:c),d&&g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL,1);c=this._getWebGLTextureType(a.type);e=this._getInternalFormat(e||a.format);var i=this._getRGBABufferInternalSizedFormat(a.type,e);g.texImage2D(h,0,i,e,c,b),a.generateMipMaps&&g.generateMipmap(h),f||this._bindTextureDirectly(h,null),d&&g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL,0),a.isReady=!0}}},function(a,b,c){var d;c.d(b,"a",function(){return d}),c.d(b,"b",function(){return e});var e=function(a,b,c,e,f,g,h,i,j,k){d={DecodeBase64UrlToBinary:a,DecodeBase64UrlToString:b,DefaultRetryStrategy:c.DefaultRetryStrategy,BaseUrl:c.BaseUrl,CorsBehavior:c.CorsBehavior,PreprocessUrl:c.PreprocessUrl,IsBase64DataUrl:e,IsFileURL:f,LoadFile:g,LoadImage:h,ReadFile:i,RequestFile:j,SetCorsBehavior:k},Object.defineProperty(d,"DefaultRetryStrategy",{get:function(){return c.DefaultRetryStrategy},set:function(a){c.DefaultRetryStrategy=a}}),Object.defineProperty(d,"BaseUrl",{get:function(){return c.BaseUrl},set:function(a){c.BaseUrl=a}}),Object.defineProperty(d,"PreprocessUrl",{get:function(){return c.PreprocessUrl},set:function(a){c.PreprocessUrl=a}}),Object.defineProperty(d,"CorsBehavior",{get:function(){return c.CorsBehavior},set:function(a){c.CorsBehavior=a}})}},function(a,b,c){c.d(b,"a",function(){return e});var d=c(35),e=function(){function a(b,c){a.IsAvailable&&(this._observer=new window.ComputePressureObserver(b,c))}return Object.defineProperty(a,"IsAvailable",{get:function(){return Object(d.e)()&&"ComputePressureObserver"in window},enumerable:!1,configurable:!0}),a.prototype.observe=function(){var a;(null===(a=this._observer)||void 0===a?void 0:a.observe)&&(null===(a=this._observer)||void 0===a||a.observe())},a.prototype.unobserve=function(){var a;(null===(a=this._observer)||void 0===a?void 0:a.unobserve)&&(null===(a=this._observer)||void 0===a||a.unobserve())},a}()},function(a,b,c){c.d(b,"b",function(){return e}),c.d(b,"c",function(){return f}),c.d(b,"d",function(){return g}),c.d(b,"a",function(){return h});var d=c(2);function e(a,b,c){void 0===c&&(c=!1);var d=b.width;b=b.height;if(a instanceof Float32Array){for(var e=a.byteLength/a.BYTES_PER_ELEMENT,f=new Uint8Array(e);--e>=0;){var g=a[e];g<0?g=0:g>1&&(g=1),f[e]=255*g}a=f}g=document.createElement("canvas");g.width=d,g.height=b;e=g.getContext("2d");if(!e)return null;f=e.createImageData(d,b);if(f.data.set(a),e.putImageData(f,0,0),c){a=document.createElement("canvas");a.width=d,a.height=b;e=a.getContext("2d");return e?(e.translate(0,b),e.scale(1,-1),e.drawImage(g,0,0),a.toDataURL("image/png")):null}return g.toDataURL("image/png")}function f(a,b,c){void 0===b&&(b=0),void 0===c&&(c=0);var d=a.getInternalTexture();if(!d)return null;b=a._readPixelsSync(b,c);return b?e(b,a.getSize(),d.invertY):null}function g(a,b,c){return void 0===b&&(b=0),void 0===c&&(c=0),Object(d.b)(this,void 0,void 0,function(){var f,g;return Object(d.e)(this,function(d){switch(d.label){case 0:return(f=a.getInternalTexture())?[4,a.readPixels(b,c)]:[2,null];case 1:return(g=d.sent())?[2,e(g,a.getSize(),f.invertY)]:[2,null]}})})}var h={GenerateBase64StringFromPixelData:e,GenerateBase64StringFromTexture:f,GenerateBase64StringFromTextureAsync:g}},function(a,b,c){c.r(b),c.d(b,"AbstractScene",function(){return d.a}),c.d(b,"AbstractActionManager",function(){return e.a}),c.d(b,"Action",function(){return j}),c.d(b,"ActionEvent",function(){return k.a}),c.d(b,"ActionManager",function(){return E}),c.d(b,"Condition",function(){return m}),c.d(b,"ValueCondition",function(){return n}),c.d(b,"PredicateCondition",function(){return o}),c.d(b,"StateCondition",function(){return p}),c.d(b,"SwitchBooleanAction",function(){return s}),c.d(b,"SetStateAction",function(){return t}),c.d(b,"SetValueAction",function(){return u}),c.d(b,"IncrementValueAction",function(){return v}),c.d(b,"PlayAnimationAction",function(){return w}),c.d(b,"StopAnimationAction",function(){return x}),c.d(b,"DoNothingAction",function(){return y}),c.d(b,"CombineAction",function(){return z}),c.d(b,"ExecuteCodeAction",function(){return A}),c.d(b,"SetParentAction",function(){return B}),c.d(b,"PlaySoundAction",function(){return F}),c.d(b,"StopSoundAction",function(){return aa}),c.d(b,"InterpolateValueAction",function(){return ca}),c.d(b,"Animatable",function(){return la}),c.d(b,"_IAnimationState",function(){return M}),c.d(b,"Animation",function(){return N}),c.d(b,"TargetedAnimation",function(){return ma}),c.d(b,"AnimationGroup",function(){return na}),c.d(b,"AnimationPropertiesOverride",function(){return oa}),c.d(b,"EasingFunction",function(){return qa}),c.d(b,"CircleEase",function(){return ra}),c.d(b,"BackEase",function(){return sa}),c.d(b,"BounceEase",function(){return ta}),c.d(b,"CubicEase",function(){return ua}),c.d(b,"ElasticEase",function(){return va}),c.d(b,"ExponentialEase",function(){return wa}),c.d(b,"PowerEase",function(){return xa}),c.d(b,"QuadraticEase",function(){return ya}),c.d(b,"QuarticEase",function(){return za}),c.d(b,"QuinticEase",function(){return Aa}),c.d(b,"SineEase",function(){return Ba}),c.d(b,"BezierCurveEase",function(){return Ca}),c.d(b,"RuntimeAnimation",function(){return ia}),c.d(b,"AnimationEvent",function(){return Da}),c.d(b,"AnimationKeyInterpolation",function(){return G}),c.d(b,"AnimationRange",function(){return J}),c.d(b,"PathCursor",function(){return Ea}),c.d(b,"KeepAssets",function(){return Fa}),c.d(b,"InstantiatedEntries",function(){return Ga}),c.d(b,"AssetContainer",function(){return Ha}),c.d(b,"Analyser",function(){return Ia}),c.d(b,"AudioEngine",function(){return Ka}),c.d(b,"AudioSceneComponent",function(){return Pa}),c.d(b,"Sound",function(){return Ma}),c.d(b,"SoundTrack",function(){return Na}),c.d(b,"WeightedSound",function(){return Ta}),c.d(b,"AutoRotationBehavior",function(){return Va}),c.d(b,"BouncingBehavior",function(){return Wa}),c.d(b,"FramingBehavior",function(){return Xa}),c.d(b,"AttachToBoxBehavior",function(){return Za}),c.d(b,"FadeInOutBehavior",function(){return $a}),c.d(b,"MultiPointerScaleBehavior",function(){return bb}),c.d(b,"PointerDragBehavior",function(){return ab.a}),c.d(b,"SixDofDragBehavior",function(){return gb}),c.d(b,"SurfaceMagnetismBehavior",function(){return hb}),c.d(b,"BaseSixDofDragBehavior",function(){return eb}),c.d(b,"FollowBehavior",function(){return jb}),c.d(b,"HandConstraintZone",function(){return Qa}),c.d(b,"HandConstraintOrientation",function(){return Ra}),c.d(b,"HandConstraintVisibility",function(){return Sa}),c.d(b,"HandConstraintBehavior",function(){return mb}),c.d(b,"Bone",function(){return ka}),c.d(b,"BoneIKController",function(){return nb}),c.d(b,"BoneLookController",function(){return ob}),c.d(b,"Skeleton",function(){return tb}),c.d(b,"Buffer",function(){return W.a}),c.d(b,"VertexBuffer",function(){return W.b}),c.d(b,"DataBuffer",function(){return ub.a}),c.d(b,"StorageBuffer",function(){return vb}),c.d(b,"BaseCameraMouseWheelInput",function(){return xb}),c.d(b,"BaseCameraPointersInput",function(){return yb}),c.d(b,"ArcRotateCameraGamepadInput",function(){return Eb}),c.d(b,"ArcRotateCameraKeyboardMoveInput",function(){return Gb}),c.d(b,"ArcRotateCameraMouseWheelInput",function(){return Ib}),c.d(b,"ArcRotateCameraPointersInput",function(){return Jb}),c.d(b,"ArcRotateCameraVRDeviceOrientationInput",function(){return Lb}),c.d(b,"FlyCameraKeyboardInput",function(){return Mb}),c.d(b,"FlyCameraMouseInput",function(){return Nb}),c.d(b,"FollowCameraKeyboardMoveInput",function(){return Ob}),c.d(b,"FollowCameraMouseWheelInput",function(){return Pb}),c.d(b,"FollowCameraPointersInput",function(){return Qb}),c.d(b,"FreeCameraDeviceOrientationInput",function(){return Xb}),c.d(b,"FreeCameraGamepadInput",function(){return Yb}),c.d(b,"FreeCameraKeyboardMoveInput",function(){return Rb}),c.d(b,"FreeCameraMouseInput",function(){return Tb}),c.d(b,"FreeCameraMouseWheelInput",function(){return Ub}),c.d(b,"FreeCameraTouchInput",function(){return Vb}),c.d(b,"FreeCameraVirtualJoystickInput",function(){return bc}),c.d(b,"CameraInputTypes",function(){return zb}),c.d(b,"CameraInputsManager",function(){return Ab}),c.d(b,"Camera",function(){return db.a}),c.d(b,"TargetCamera",function(){return cc}),c.d(b,"FreeCamera",function(){return dc}),c.d(b,"FreeCameraInputsManager",function(){return Wb}),c.d(b,"TouchCamera",function(){return ec}),c.d(b,"ArcRotateCamera",function(){return fc}),c.d(b,"ArcRotateCameraInputsManager",function(){return Kb}),c.d(b,"DeviceOrientationCamera",function(){return gc}),c.d(b,"FlyCamera",function(){return ic}),c.d(b,"FlyCameraInputsManager",function(){return hc}),c.d(b,"FollowCamera",function(){return lc}),c.d(b,"ArcFollowCamera",function(){return mc}),c.d(b,"FollowCameraInputsManager",function(){return jc}),c.d(b,"GamepadCamera",function(){return zc}),c.d(b,"AnaglyphArcRotateCamera",function(){return Kc}),c.d(b,"AnaglyphFreeCamera",function(){return Lc}),c.d(b,"AnaglyphGamepadCamera",function(){return Mc}),c.d(b,"AnaglyphUniversalCamera",function(){return Nc}),c.d(b,"StereoscopicArcRotateCamera",function(){return Sc}),c.d(b,"StereoscopicFreeCamera",function(){return Tc}),c.d(b,"StereoscopicGamepadCamera",function(){return Uc}),c.d(b,"StereoscopicUniversalCamera",function(){return Vc}),c.d(b,"StereoscopicScreenUniversalCamera",function(){return Wc}),c.d(b,"UniversalCamera",function(){return yc}),c.d(b,"VirtualJoysticksCamera",function(){return Xc}),c.d(b,"VRCameraMetrics",function(){return Yc}),c.d(b,"VRDeviceOrientationArcRotateCamera",function(){return hd}),c.d(b,"VRDeviceOrientationFreeCamera",function(){return id}),c.d(b,"VRDeviceOrientationGamepadCamera",function(){return jd}),c.d(b,"OnAfterEnteringVRObservableEvent",function(){return Nd}),c.d(b,"VRExperienceHelper",function(){return Od}),c.d(b,"WebVRFreeCamera",function(){return md}),c.d(b,"setStereoscopicAnaglyphRigMode",function(){return Jc}),c.d(b,"setStereoscopicRigMode",function(){return Rc}),c.d(b,"setVRRigMode",function(){return gd}),c.d(b,"setWebVRRigMode",function(){return ld}),c.d(b,"Collider",function(){return Qd}),c.d(b,"DefaultCollisionCoordinator",function(){return Rd}),c.d(b,"PickingInfo",function(){return Td.a}),c.d(b,"IntersectionInfo",function(){return Ud.a}),c.d(b,"_MeshCollisionData",function(){return Vd.a}),c.d(b,"ComputeEffect",function(){return Yd}),c.d(b,"ComputeShader",function(){return ae}),c.d(b,"BoundingBox",function(){return be.a}),c.d(b,"BoundingInfo",function(){return ce.a}),c.d(b,"BoundingSphere",function(){return de.a}),c.d(b,"Octree",function(){return fe}),c.d(b,"OctreeBlock",function(){return ee}),c.d(b,"OctreeSceneComponent",function(){return ge}),c.d(b,"Ray",function(){return nc.a}),c.d(b,"AxesViewer",function(){return he.AxesViewer}),c.d(b,"BoneAxesViewer",function(){return he.BoneAxesViewer}),c.d(b,"DebugLayerTab",function(){return he.DebugLayerTab}),c.d(b,"DebugLayer",function(){return he.DebugLayer}),c.d(b,"PhysicsViewer",function(){return he.PhysicsViewer}),c.d(b,"RayHelper",function(){return he.RayHelper}),c.d(b,"SkeletonViewer",function(){return he.SkeletonViewer}),c.d(b,"DirectionalLightFrustumViewer",function(){return he.DirectionalLightFrustumViewer}),c.d(b,"DeviceInputSystem",function(){return ie.a}),c.d(b,"DeviceType",function(){return je.a}),c.d(b,"PointerInput",function(){return je.c}),c.d(b,"DualShockInput",function(){return je.b}),c.d(b,"XboxInput",function(){return je.e}),c.d(b,"SwitchInput",function(){return je.d}),c.d(b,"DeviceEventFactory",function(){return ke.a}),c.d(b,"NativeDeviceInputWrapper",function(){return le.a}),c.d(b,"WebDeviceInputSystem",function(){return me.a}),c.d(b,"DeviceSource",function(){return ne}),c.d(b,"DeviceSourceManager",function(){return oe}),c.d(b,"Constants",function(){return r.a}),c.d(b,"ThinEngine",function(){return qb.a}),c.d(b,"Engine",function(){return S.a}),c.d(b,"EngineStore",function(){return C.a}),c.d(b,"NullEngineOptions",function(){return pe.b}),c.d(b,"NullEngine",function(){return pe.a}),c.d(b,"_OcclusionDataStorage",function(){return se}),c.d(b,"_forceTransformFeedbackToBundle",function(){return te}),c.d(b,"EngineView",function(){return we}),c.d(b,"allocateAndCopyTypedBuffer",function(){return xe.a}),c.d(b,"ComputeBindingType",function(){return Sd}),c.d(b,"NativeDataStream",function(){return ze}),c.d(b,"WebGLPipelineContext",function(){return $g.a}),c.d(b,"WebGLHardwareTexture",function(){return ah.a}),c.d(b,"PowerPreference",function(){return rf}),c.d(b,"FeatureName",function(){return sf}),c.d(b,"BufferUsage",function(){return tf}),c.d(b,"MapMode",function(){return uf}),c.d(b,"TextureDimension",function(){return vf}),c.d(b,"TextureUsage",function(){return wf}),c.d(b,"TextureViewDimension",function(){return xf}),c.d(b,"TextureAspect",function(){return yf}),c.d(b,"TextureFormat",function(){return Y}),c.d(b,"AddressMode",function(){return zf}),c.d(b,"FilterMode",function(){return Af}),c.d(b,"CompareFunction",function(){return Bf}),c.d(b,"ShaderStage",function(){return Cf}),c.d(b,"BufferBindingType",function(){return Df}),c.d(b,"SamplerBindingType",function(){return Ef}),c.d(b,"TextureSampleType",function(){return Ff}),c.d(b,"StorageTextureAccess",function(){return Gf}),c.d(b,"CompilationMessageType",function(){return Hf}),c.d(b,"PrimitiveTopology",function(){return If}),c.d(b,"FrontFace",function(){return Jf}),c.d(b,"CullMode",function(){return Kf}),c.d(b,"ColorWrite",function(){return Lf}),c.d(b,"BlendFactor",function(){return Mf}),c.d(b,"BlendOperation",function(){return Nf}),c.d(b,"StencilOperation",function(){return Of}),c.d(b,"IndexFormat",function(){return Pf}),c.d(b,"VertexFormat",function(){return Qf}),c.d(b,"InputStepMode",function(){return Rf}),c.d(b,"LoadOp",function(){return Sf}),c.d(b,"StoreOp",function(){return Tf}),c.d(b,"QueryType",function(){return Uf}),c.d(b,"PipelineStatisticName",function(){return Vf}),c.d(b,"CanvasCompositingAlphaMode",function(){return Wf}),c.d(b,"DeviceLostReason",function(){return Xf}),c.d(b,"ErrorFilter",function(){return Yf}),c.d(b,"WebGPUEngine",function(){return Ug}),c.d(b,"WebGPUCacheRenderPipeline",function(){return wg}),c.d(b,"WebGPUCacheRenderPipelineTree",function(){return yg}),c.d(b,"WebGPUCacheBindGroups",function(){return Hg}),c.d(b,"WebGPUCacheSampler",function(){return sg}),c.d(b,"WebGPUTintWASM",function(){return Tg}),c.d(b,"WebGL2ShaderProcessor",function(){return lf.a}),c.d(b,"NativeEngine",function(){return qf}),c.d(b,"ShaderCodeInliner",function(){return kf}),c.d(b,"PerformanceConfigurator",function(){return bh.a}),c.d(b,"EngineFactory",function(){return ch}),c.d(b,"ShaderStore",function(){return X.a}),c.d(b,"KeyboardEventTypes",function(){return Fb.a}),c.d(b,"KeyboardInfo",function(){return Fb.b}),c.d(b,"KeyboardInfoPre",function(){return Fb.c}),c.d(b,"PointerEventTypes",function(){return Ua.a}),c.d(b,"PointerInfoBase",function(){return Ua.c}),c.d(b,"PointerInfoPre",function(){return Ua.d}),c.d(b,"PointerInfo",function(){return Ua.b}),c.d(b,"ClipboardEventTypes",function(){return dh}),c.d(b,"ClipboardInfo",function(){return eh}),c.d(b,"DeviceInputEventType",function(){return wb.a}),c.d(b,"EventConstants",function(){return wb.b}),c.d(b,"DaydreamController",function(){return ih}),c.d(b,"GearVRController",function(){return jh}),c.d(b,"GenericController",function(){return kh}),c.d(b,"OculusTouchController",function(){return lh}),c.d(b,"PoseEnabledControllerType",function(){return kc}),c.d(b,"PoseEnabledControllerHelper",function(){return qc}),c.d(b,"PoseEnabledController",function(){return rc}),c.d(b,"ViveController",function(){return mh}),c.d(b,"WebVRController",function(){return nd}),c.d(b,"WindowsMotionController",function(){return oh}),c.d(b,"XRWindowsMotionController",function(){return ph}),c.d(b,"StickValues",function(){return Bb}),c.d(b,"Gamepad",function(){return Cb}),c.d(b,"GenericPad",function(){return Db}),c.d(b,"GamepadManager",function(){return wc}),c.d(b,"GamepadSystemSceneComponent",function(){return xc}),c.d(b,"Xbox360Button",function(){return oc}),c.d(b,"Xbox360Dpad",function(){return pc}),c.d(b,"Xbox360Pad",function(){return uc}),c.d(b,"DualShockButton",function(){return sc}),c.d(b,"DualShockDpad",function(){return tc}),c.d(b,"DualShockPad",function(){return vc}),c.d(b,"AxisDragGizmo",function(){return qh.a}),c.d(b,"AxisScaleGizmo",function(){return uh}),c.d(b,"BoundingBoxGizmo",function(){return yh}),c.d(b,"Gizmo",function(){return sh.a}),c.d(b,"GizmoManager",function(){return Jh}),c.d(b,"PlaneRotationGizmo",function(){return Bh}),c.d(b,"PositionGizmo",function(){return Eh}),c.d(b,"RotationGizmo",function(){return Ch}),c.d(b,"ScaleGizmo",function(){return Ih}),c.d(b,"LightGizmo",function(){return Th}),c.d(b,"CameraGizmo",function(){return Uh}),c.d(b,"PlaneDragGizmo",function(){return Dh}),c.d(b,"EnvironmentHelper",function(){return ei}),c.d(b,"PhotoDome",function(){return gi}),c.d(b,"_forceSceneHelpersToBundle",function(){return gj}),c.d(b,"VideoDome",function(){return ij}),c.d(b,"EngineInstrumentation",function(){return jj}),c.d(b,"SceneInstrumentation",function(){return kj}),c.d(b,"_TimeToken",function(){return qe}),c.d(b,"EffectLayer",function(){return lj}),c.d(b,"EffectLayerSceneComponent",function(){return mj}),c.d(b,"GlowLayer",function(){return nj}),c.d(b,"HighlightLayer",function(){return pj}),c.d(b,"Layer",function(){return rj}),c.d(b,"LayerSceneComponent",function(){return qj}),c.d(b,"LensFlare",function(){return sj}),c.d(b,"LensFlareSystem",function(){return tj}),c.d(b,"LensFlareSystemSceneComponent",function(){return uj}),c.d(b,"Light",function(){return Kh.a}),c.d(b,"ShadowLight",function(){return Lh}),c.d(b,"ShadowGenerator",function(){return vj}),c.d(b,"CascadedShadowGenerator",function(){return Ej}),c.d(b,"ShadowGeneratorSceneComponent",function(){return Fj}),c.d(b,"DirectionalLight",function(){return Mh}),c.d(b,"HemisphericLight",function(){return kd.a}),c.d(b,"PointLight",function(){return Gj}),c.d(b,"SpotLight",function(){return Sh}),c.d(b,"DefaultLoadingScreen",function(){return Hj}),c.d(b,"_BabylonLoaderRegistered",function(){return bk}),c.d(b,"BabylonFileLoaderConfiguration",function(){return ck}),c.d(b,"SceneLoaderAnimationGroupLoadingMode",function(){return Zg}),c.d(b,"SceneLoader",function(){return hh}),c.d(b,"SceneLoaderFlags",function(){return fh.a}),c.d(b,"BackgroundMaterial",function(){return di}),c.d(b,"ColorCurves",function(){return ik.a}),c.d(b,"EffectFallbacks",function(){return bi.a}),c.d(b,"Effect",function(){return $f.a}),c.d(b,"FresnelParameters",function(){return jk}),c.d(b,"ImageProcessingConfigurationDefines",function(){return od.b}),c.d(b,"ImageProcessingConfiguration",function(){return od.a}),c.d(b,"Material",function(){return qi.a}),c.d(b,"MaterialDefines",function(){return Zh.a}),c.d(b,"ThinMaterialHelper",function(){return kk.a}),c.d(b,"MaterialHelper",function(){return Yh.a}),c.d(b,"MultiMaterial",function(){return Jj.a}),c.d(b,"OcclusionMaterial",function(){return lk}),c.d(b,"PBRMaterialDefines",function(){return ti}),c.d(b,"PBRBaseMaterial",function(){return ui}),c.d(b,"PBRBaseSimpleMaterial",function(){return mk}),c.d(b,"PBRMaterial",function(){return vi}),c.d(b,"PBRMetallicRoughnessMaterial",function(){return nk}),c.d(b,"PBRSpecularGlossinessMaterial",function(){return ok}),c.d(b,"PushMaterial",function(){return $h.a}),c.d(b,"ShaderLanguage",function(){return Xd.a}),c.d(b,"ShaderMaterial",function(){return zh.a}),c.d(b,"StandardMaterialDefines",function(){return pd.b}),c.d(b,"StandardMaterial",function(){return pd.a}),c.d(b,"BaseTexture",function(){return Ie.a}),c.d(b,"ColorGradingTexture",function(){return pk}),c.d(b,"CubeTexture",function(){return Xh}),c.d(b,"DynamicTexture",function(){return qd.a}),c.d(b,"EquiRectangularCubeTexture",function(){return qk}),c.d(b,"ExternalTexture",function(){return Dg}),c.d(b,"HDRFiltering",function(){return Oj}),c.d(b,"HDRCubeTexture",function(){return Pj}),c.d(b,"HtmlElementTexture",function(){return rk}),c.d(b,"InternalTextureSource",function(){return pb.b}),c.d(b,"InternalTexture",function(){return pb.a}),c.d(b,"_DDSTextureLoader",function(){return Ci}),c.d(b,"_ENVTextureLoader",function(){return Di}),c.d(b,"_KTXTextureLoader",function(){return Ii}),c.d(b,"_TGATextureLoader",function(){return vk}),c.d(b,"_HDRTextureLoader",function(){return wk}),c.d(b,"_BasisTextureLoader",function(){return Ik}),c.d(b,"MirrorTexture",function(){return Wh}),c.d(b,"MultiRenderTarget",function(){return Jk}),c.d(b,"TexturePacker",function(){return Lk}),c.d(b,"TexturePackerFrame",function(){return Kk}),c.d(b,"CustomProceduralTexture",function(){return Ok}),c.d(b,"NoiseProceduralTexture",function(){return Pk}),c.d(b,"ProceduralTexture",function(){return Nk}),c.d(b,"ProceduralTextureSceneComponent",function(){return Mk}),c.d(b,"RawCubeTexture",function(){return Vk}),c.d(b,"RawTexture",function(){return sb}),c.d(b,"RawTexture2DArray",function(){return Rj}),c.d(b,"RawTexture3D",function(){return Wk}),c.d(b,"RefractionTexture",function(){return Xk}),c.d(b,"RenderTargetTexture",function(){return cd}),c.d(b,"TextureSampler",function(){return $d.a}),c.d(b,"Texture",function(){return U.a}),c.d(b,"ThinTexture",function(){return Yk.a}),c.d(b,"ThinRenderTargetTexture",function(){return Zk}),c.d(b,"VideoTexture",function(){return hj}),c.d(b,"UniformBuffer",function(){return $c.a}),c.d(b,"MaterialFlags",function(){return ai.a}),c.d(b,"NodeMaterialBlockTargets",function(){return Z}),c.d(b,"NodeMaterialBlockConnectionPointTypes",function(){return $}),c.d(b,"NodeMaterialBlockConnectionPointMode",function(){return Qk}),c.d(b,"NodeMaterialSystemValues",function(){return Rk}),c.d(b,"NodeMaterialModes",function(){return Sk}),c.d(b,"NodeMaterialConnectionPointCompatibilityStates",function(){return Tk}),c.d(b,"NodeMaterialConnectionPointDirection",function(){return Uk}),c.d(b,"NodeMaterialConnectionPoint",function(){return $k}),c.d(b,"NodeMaterialBlock",function(){return al}),c.d(b,"NodeMaterialDefines",function(){return Kl}),c.d(b,"NodeMaterial",function(){return Ll}),c.d(b,"VertexOutputBlock",function(){return fl}),c.d(b,"BonesBlock",function(){return Ml}),c.d(b,"InstancesBlock",function(){return Nl}),c.d(b,"MorphTargetsBlock",function(){return Ol}),c.d(b,"LightInformationBlock",function(){return Pl}),c.d(b,"FragmentOutputBlock",function(){return il}),c.d(b,"ImageProcessingBlock",function(){return Ql}),c.d(b,"PerturbNormalBlock",function(){return Rl}),c.d(b,"DiscardBlock",function(){return Sl}),c.d(b,"FrontFacingBlock",function(){return Tl}),c.d(b,"DerivativeBlock",function(){return Ul}),c.d(b,"FragCoordBlock",function(){return Vl}),c.d(b,"ScreenSizeBlock",function(){return Wl}),c.d(b,"FogBlock",function(){return Xl}),c.d(b,"LightBlock",function(){return Yl}),c.d(b,"TextureBlock",function(){return am}),c.d(b,"ReflectionTextureBlock",function(){return bm}),c.d(b,"CurrentScreenBlock",function(){return nl}),c.d(b,"SceneDepthBlock",function(){return cm}),c.d(b,"ImageSourceBlock",function(){return $l}),c.d(b,"InputBlock",function(){return ml}),c.d(b,"AnimatedInputBlockTypes",function(){return hl}),c.d(b,"MultiplyBlock",function(){return tl}),c.d(b,"AddBlock",function(){return dm}),c.d(b,"ScaleBlock",function(){return em}),c.d(b,"ClampBlock",function(){return fm}),c.d(b,"CrossBlock",function(){return gm}),c.d(b,"DotBlock",function(){return hm}),c.d(b,"TransformBlock",function(){return dl}),c.d(b,"RemapBlock",function(){return sl}),c.d(b,"NormalizeBlock",function(){return im}),c.d(b,"TrigonometryBlockOperations",function(){return Gl}),c.d(b,"TrigonometryBlock",function(){return Il}),c.d(b,"ColorMergerBlock",function(){return jm}),c.d(b,"VectorMergerBlock",function(){return rl}),c.d(b,"ColorSplitterBlock",function(){return Fl}),c.d(b,"VectorSplitterBlock",function(){return km}),c.d(b,"LerpBlock",function(){return lm}),c.d(b,"DivideBlock",function(){return mm}),c.d(b,"SubtractBlock",function(){return nm}),c.d(b,"StepBlock",function(){return om}),c.d(b,"OneMinusBlock",function(){return pm}),c.d(b,"ViewDirectionBlock",function(){return qm}),c.d(b,"FresnelBlock",function(){return rm}),c.d(b,"MaxBlock",function(){return sm}),c.d(b,"MinBlock",function(){return tm}),c.d(b,"DistanceBlock",function(){return um}),c.d(b,"LengthBlock",function(){return vm}),c.d(b,"NegateBlock",function(){return wm}),c.d(b,"PowBlock",function(){return xm}),c.d(b,"RandomNumberBlock",function(){return ym}),c.d(b,"ArcTan2Block",function(){return zm}),c.d(b,"SmoothStepBlock",function(){return Am}),c.d(b,"ReciprocalBlock",function(){return Bm}),c.d(b,"ReplaceColorBlock",function(){return Cm}),c.d(b,"PosterizeBlock",function(){return Em}),c.d(b,"WaveBlockKind",function(){return Dm}),c.d(b,"WaveBlock",function(){return Fm}),c.d(b,"GradientBlockColorStep",function(){return Gm}),c.d(b,"GradientBlock",function(){return Hm}),c.d(b,"NLerpBlock",function(){return Im}),c.d(b,"WorleyNoise3DBlock",function(){return Jm}),c.d(b,"SimplexPerlin3DBlock",function(){return Km}),c.d(b,"NormalBlendBlock",function(){return Lm}),c.d(b,"Rotate2dBlock",function(){return Mm}),c.d(b,"ReflectBlock",function(){return Nm}),c.d(b,"RefractBlock",function(){return Om}),c.d(b,"DesaturateBlock",function(){return Pm}),c.d(b,"PBRMetallicRoughnessBlock",function(){return Xm}),c.d(b,"SheenBlock",function(){return Qm}),c.d(b,"AnisotropyBlock",function(){return Rm}),c.d(b,"ReflectionBlock",function(){return Sm}),c.d(b,"ClearCoatBlock",function(){return Tm}),c.d(b,"RefractionBlock",function(){return Um}),c.d(b,"SubSurfaceBlock",function(){return Vm}),c.d(b,"ParticleTextureBlock",function(){return ol}),c.d(b,"ParticleRampGradientBlock",function(){return pl}),c.d(b,"ParticleBlendMultiplyBlock",function(){return ql}),c.d(b,"ModBlock",function(){return Ym}),c.d(b,"MatrixBuilderBlock",function(){return $m}),c.d(b,"ConditionalBlockConditions",function(){return Zm}),c.d(b,"ConditionalBlock",function(){return an}),c.d(b,"CloudBlock",function(){return bn}),c.d(b,"NodeMaterialOptimizer",function(){return cn}),c.d(b,"PropertyTypeForEdition",function(){return el}),c.d(b,"editableInPropertyPage",function(){return gl}),c.d(b,"EffectRenderer",function(){return Mj}),c.d(b,"EffectWrapper",function(){return Nj}),c.d(b,"ShadowDepthWrapper",function(){return en}),c.d(b,"DrawWrapper",function(){return Ec.a}),c.d(b,"Scalar",function(){return H.a}),c.d(b,"extractMinAndMaxIndexed",function(){return fn.b}),c.d(b,"extractMinAndMax",function(){return fn.a}),c.d(b,"Space",function(){return Q.c}),c.d(b,"Axis",function(){return Q.a}),c.d(b,"Coordinate",function(){return Q.b}),c.d(b,"Color3",function(){return h.a}),c.d(b,"Color4",function(){return h.b}),c.d(b,"TmpColors",function(){return h.c}),c.d(b,"ToGammaSpace",function(){return ib.c}),c.d(b,"ToLinearSpace",function(){return ib.d}),c.d(b,"PHI",function(){return ib.b}),c.d(b,"Epsilon",function(){return ib.a}),c.d(b,"Frustum",function(){return ed.a}),c.d(b,"Orientation",function(){return pa.e}),c.d(b,"BezierCurve",function(){return pa.c}),c.d(b,"Angle",function(){return pa.a}),c.d(b,"Arc2",function(){return pa.b}),c.d(b,"Path2",function(){return pa.f}),c.d(b,"Path3D",function(){return pa.g}),c.d(b,"Curve3",function(){return pa.d}),c.d(b,"Plane",function(){return Hb.a}),c.d(b,"Size",function(){return ba.a}),c.d(b,"Vector2",function(){return g.d}),c.d(b,"Vector3",function(){return g.e}),c.d(b,"Vector4",function(){return g.f}),c.d(b,"Quaternion",function(){return g.b}),c.d(b,"Matrix",function(){return g.a}),c.d(b,"TmpVectors",function(){return g.c}),c.d(b,"PositionNormalVertex",function(){return Ae}),c.d(b,"PositionNormalTextureVertex",function(){return Be}),c.d(b,"Viewport",function(){return Oc.a}),c.d(b,"SphericalHarmonics",function(){return Ge}),c.d(b,"SphericalPolynomial",function(){return He}),c.d(b,"AbstractMesh",function(){return cb.a}),c.d(b,"DracoCompression",function(){return jn}),c.d(b,"MeshoptCompression",function(){return kn}),c.d(b,"CSG",function(){return rn}),c.d(b,"Geometry",function(){return Ij.a}),c.d(b,"GroundMesh",function(){return Cd}),c.d(b,"TrailMesh",function(){return sn}),c.d(b,"InstancedMesh",function(){return tn.a}),c.d(b,"LinesMesh",function(){return un.b}),c.d(b,"InstancedLinesMesh",function(){return un.a}),c.d(b,"_CreationDataStorage",function(){return R.b}),c.d(b,"_InstancesBatch",function(){return R.c}),c.d(b,"Mesh",function(){return R.a}),c.d(b,"_injectLTSMesh",function(){return vn.a}),c.d(b,"VertexData",function(){return yd.a}),c.d(b,"MeshBuilder",function(){return fo}),c.d(b,"SimplificationSettings",function(){return go}),c.d(b,"SimplificationQueue",function(){return ho}),c.d(b,"SimplificationType",function(){return co}),c.d(b,"QuadraticErrorSimplification",function(){return mo}),c.d(b,"SimplicationQueueSceneComponent",function(){return no}),c.d(b,"Polygon",function(){return In}),c.d(b,"PolygonMeshBuilder",function(){return Jn}),c.d(b,"_PrimaryIsoTriangle",function(){return Zn}),c.d(b,"PolyhedronData",function(){return $n}),c.d(b,"GeodesicData",function(){return ao}),c.d(b,"SubMesh",function(){return ln.a}),c.d(b,"MeshLODLevel",function(){return oo.a}),c.d(b,"TransformNode",function(){return fb.a}),c.d(b,"CreateBoxVertexData",function(){return rh.c}),c.d(b,"CreateBox",function(){return rh.b}),c.d(b,"BoxBuilder",function(){return rh.a}),c.d(b,"CreateTiledBoxVertexData",function(){return An}),c.d(b,"CreateTiledBox",function(){return Bn}),c.d(b,"TiledBoxBuilder",function(){return Cn}),c.d(b,"CreateDisc",function(){return Oh}),c.d(b,"DiscBuilder",function(){return Ph}),c.d(b,"CreateRibbonVertexData",function(){return wn.b}),c.d(b,"CreateRibbon",function(){return wn.a}),c.d(b,"RibbonBuilder",function(){return wn.c}),c.d(b,"CreateSphereVertexData",function(){return vh.b}),c.d(b,"CreateSphere",function(){return vh.a}),c.d(b,"SphereBuilder",function(){return vh.c}),c.d(b,"CreateHemisphere",function(){return Qh}),c.d(b,"HemisphereBuilder",function(){return Rh}),c.d(b,"CreateCylinderVertexData",function(){return xd.b}),c.d(b,"CreateCylinder",function(){return xd.a}),c.d(b,"CylinderBuilder",function(){return xd.c}),c.d(b,"CreateTorusVertexData",function(){return zd}),c.d(b,"CreateTorus",function(){return Ad}),c.d(b,"TorusBuilder",function(){return Bd}),c.d(b,"CreateTorusKnotVertexData",function(){return Dn}),c.d(b,"CreateTorusKnot",function(){return En}),c.d(b,"TorusKnotBuilder",function(){return Fn}),c.d(b,"CreateLineSystemVertexData",function(){return wh.d}),c.d(b,"CreateDashedLinesVertexData",function(){return wh.b}),c.d(b,"CreateLineSystem",function(){return wh.c}),c.d(b,"CreateLines",function(){return wh.e}),c.d(b,"CreateDashedLines",function(){return wh.a}),c.d(b,"LinesBuilder",function(){return wh.f}),c.d(b,"CreatePolygonVertexData",function(){return Kn}),c.d(b,"CreatePolygon",function(){return Ln}),c.d(b,"ExtrudePolygon",function(){return Mn}),c.d(b,"PolygonBuilder",function(){return Nn}),c.d(b,"ExtrudeShape",function(){return Zj.a}),c.d(b,"ExtrudeShapeCustom",function(){return Zj.b}),c.d(b,"ShapeBuilder",function(){return Zj.c}),c.d(b,"CreateLathe",function(){return On}),c.d(b,"LatheBuilder",function(){return Pn}),c.d(b,"CreatePlaneVertexData",function(){return Ah.b}),c.d(b,"CreatePlane",function(){return Ah.a}),c.d(b,"PlaneBuilder",function(){return Ah.c}),c.d(b,"CreateTiledPlaneVertexData",function(){return xn}),c.d(b,"CreateTiledPlane",function(){return yn}),c.d(b,"TiledPlaneBuilder",function(){return zn}),c.d(b,"CreateGroundVertexData",function(){return Dd}),c.d(b,"CreateTiledGroundVertexData",function(){return Ed}),c.d(b,"CreateGroundFromHeightMapVertexData",function(){return Fd}),c.d(b,"CreateGround",function(){return Gd}),c.d(b,"CreateTiledGround",function(){return Hd}),c.d(b,"CreateGroundFromHeightMap",function(){return Id}),c.d(b,"GroundBuilder",function(){return Jd}),c.d(b,"CreateTube",function(){return Qn}),c.d(b,"TubeBuilder",function(){return Rn}),c.d(b,"CreatePolyhedronVertexData",function(){return Fh}),c.d(b,"CreatePolyhedron",function(){return Gh}),c.d(b,"PolyhedronBuilder",function(){return Hh}),c.d(b,"CreateIcoSphereVertexData",function(){return Sn}),c.d(b,"CreateIcoSphere",function(){return Tn}),c.d(b,"IcoSphereBuilder",function(){return Un}),c.d(b,"CreateGeodesic",function(){return bo}),c.d(b,"CreateDecal",function(){return Vn}),c.d(b,"DecalBuilder",function(){return Wn}),c.d(b,"CreateCapsuleVertexData",function(){return Xn.c}),c.d(b,"CreateCapsule",function(){return Xn.b}),c.d(b,"CapsuleBuilder",function(){return Xn.a}),c.d(b,"WebGLDataBuffer",function(){return po.a}),c.d(b,"WebGPUDataBuffer",function(){return mg}),c.d(b,"MorphTarget",function(){return Qj}),c.d(b,"MorphTargetManager",function(){return Sj}),c.d(b,"RecastJSPlugin",function(){return qo}),c.d(b,"RecastJSCrowd",function(){return ro}),c.d(b,"Node",function(){return K.a}),c.d(b,"Database",function(){return so}),c.d(b,"BaseParticleSystem",function(){return El}),c.d(b,"BoxParticleEmitter",function(){return ul}),c.d(b,"ConeParticleEmitter",function(){return vl}),c.d(b,"CylinderParticleEmitter",function(){return wl}),c.d(b,"CylinderDirectedParticleEmitter",function(){return xl}),c.d(b,"HemisphericParticleEmitter",function(){return yl}),c.d(b,"PointParticleEmitter",function(){return zl}),c.d(b,"SphereParticleEmitter",function(){return Al}),c.d(b,"SphereDirectedParticleEmitter",function(){return Bl}),c.d(b,"CustomParticleEmitter",function(){return Cl}),c.d(b,"MeshParticleEmitter",function(){return Dl}),c.d(b,"WebGL2ParticleSystem",function(){return uo}),c.d(b,"ComputeShaderParticleSystem",function(){return vo}),c.d(b,"GPUParticleSystem",function(){return Eo}),c.d(b,"Particle",function(){return Bo}),c.d(b,"ParticleHelper",function(){return Go}),c.d(b,"ParticleSystem",function(){return Do}),c.d(b,"ParticleSystemSet",function(){return Fo}),c.d(b,"SolidParticle",function(){return Io}),c.d(b,"ModelShape",function(){return Jo}),c.d(b,"DepthSortedParticle",function(){return Ko}),c.d(b,"SolidParticleVertex",function(){return Lo}),c.d(b,"SolidParticleSystem",function(){return Mo}),c.d(b,"CloudPoint",function(){return No}),c.d(b,"PointsGroup",function(){return Oo}),c.d(b,"PointColor",function(){return Ho}),c.d(b,"PointsCloudSystem",function(){return Po}),c.d(b,"SubEmitterType",function(){return wo}),c.d(b,"SubEmitter",function(){return Co}),c.d(b,"PhysicsEngine",function(){return Vj}),c.d(b,"PhysicsEngineSceneComponent",function(){return So}),c.d(b,"PhysicsHelper",function(){return To}),c.d(b,"PhysicsRadialExplosionEventOptions",function(){return Yo}),c.d(b,"PhysicsUpdraftEventOptions",function(){return Zo}),c.d(b,"PhysicsVortexEventOptions",function(){return $o}),c.d(b,"PhysicsRadialImpulseFalloff",function(){return Qo}),c.d(b,"PhysicsUpdraftMode",function(){return Ro}),c.d(b,"PhysicsImpostor",function(){return Tj.a}),c.d(b,"PhysicsJoint",function(){return Uj.e}),c.d(b,"DistanceJoint",function(){return Uj.a}),c.d(b,"MotorEnabledJoint",function(){return Uj.d}),c.d(b,"HingeJoint",function(){return Uj.c}),c.d(b,"Hinge2Joint",function(){return Uj.b}),c.d(b,"CannonJSPlugin",function(){return Xj}),c.d(b,"AmmoJSPlugin",function(){return $j}),c.d(b,"OimoJSPlugin",function(){return Yj}),c.d(b,"AnaglyphPostProcess",function(){return Ic}),c.d(b,"BlackAndWhitePostProcess",function(){return ap}),c.d(b,"BloomEffect",function(){return ep}),c.d(b,"BloomMergePostProcess",function(){return dp}),c.d(b,"BlurPostProcess",function(){return Vh}),c.d(b,"ChromaticAberrationPostProcess",function(){return fp}),c.d(b,"CircleOfConfusionPostProcess",function(){return gp}),c.d(b,"ColorCorrectionPostProcess",function(){return hp}),c.d(b,"ConvolutionPostProcess",function(){return ip}),c.d(b,"DepthOfFieldBlurPostProcess",function(){return jp}),c.d(b,"DepthOfFieldEffectBlurLevel",function(){return kp}),c.d(b,"DepthOfFieldEffect",function(){return np}),c.d(b,"DepthOfFieldMergePostProcessOptions",function(){return lp}),c.d(b,"DepthOfFieldMergePostProcess",function(){return mp}),c.d(b,"DisplayPassPostProcess",function(){return op}),c.d(b,"ExtractHighlightsPostProcess",function(){return cp}),c.d(b,"FilterPostProcess",function(){return pp}),c.d(b,"FxaaPostProcess",function(){return qp}),c.d(b,"GrainPostProcess",function(){return rp}),c.d(b,"HighlightsPostProcess",function(){return sp}),c.d(b,"ImageProcessingPostProcess",function(){return td}),c.d(b,"MotionBlurPostProcess",function(){return wp}),c.d(b,"PassPostProcess",function(){return Gc}),c.d(b,"PassCubePostProcess",function(){return Hc}),c.d(b,"PostProcess",function(){return Fc}),c.d(b,"PostProcessManager",function(){return ad.a}),c.d(b,"RefractionPostProcess",function(){return xp}),c.d(b,"DefaultRenderingPipeline",function(){return Cp}),c.d(b,"LensRenderingPipeline",function(){return Dp}),c.d(b,"SSAO2RenderingPipeline",function(){return Fp}),c.d(b,"SSAORenderingPipeline",function(){return Gp}),c.d(b,"StandardRenderingPipeline",function(){return Jp}),c.d(b,"PostProcessRenderEffect",function(){return bp}),c.d(b,"PostProcessRenderPipeline",function(){return zp}),c.d(b,"PostProcessRenderPipelineManager",function(){return Ap}),c.d(b,"PostProcessRenderPipelineManagerSceneComponent",function(){return Bp}),c.d(b,"SharpenPostProcess",function(){return yp}),c.d(b,"StereoscopicInterlacePostProcessI",function(){return Pc}),c.d(b,"StereoscopicInterlacePostProcess",function(){return Qc}),c.d(b,"TonemappingOperator",function(){return Kp}),c.d(b,"TonemapPostProcess",function(){return Lp}),c.d(b,"VolumetricLightScatteringPostProcess",function(){return Mp}),c.d(b,"VRDistortionCorrectionPostProcess",function(){return Zc}),c.d(b,"VRMultiviewToSingleviewPostProcess",function(){return fd}),c.d(b,"ScreenSpaceReflectionPostProcess",function(){return Ip}),c.d(b,"ScreenSpaceCurvaturePostProcess",function(){return Np}),c.d(b,"ReflectionProbe",function(){return ak}),c.d(b,"BoundingBoxRenderer",function(){return Op}),c.d(b,"DepthRenderer",function(){return wj}),c.d(b,"DepthRendererSceneComponent",function(){return Pp}),c.d(b,"DepthPeelingRenderer",function(){return Rp}),c.d(b,"DepthPeelingSceneComponent",function(){return Sp}),c.d(b,"EdgesRenderer",function(){return Up}),c.d(b,"LineEdgesRenderer",function(){return Vp}),c.d(b,"GeometryBufferRenderer",function(){return tp}),c.d(b,"GeometryBufferRendererSceneComponent",function(){return vp}),c.d(b,"PrePassRenderer",function(){return Xp}),c.d(b,"PrePassRendererSceneComponent",function(){return Yp}),c.d(b,"SubSurfaceSceneComponent",function(){return aq}),c.d(b,"OutlineRenderer",function(){return bq}),c.d(b,"RenderingGroup",function(){return cq.a}),c.d(b,"RenderingGroupInfo",function(){return bd.a}),c.d(b,"RenderingManager",function(){return bd.b}),c.d(b,"UtilityLayerRenderer",function(){return th.a}),c.d(b,"Scene",function(){return O.a}),c.d(b,"_injectLTSScene",function(){return dq.a}),c.d(b,"SceneComponentConstants",function(){return Oa.a}),c.d(b,"Stage",function(){return Oa.b}),c.d(b,"Sprite",function(){return eq}),c.d(b,"SpriteManager",function(){return hq}),c.d(b,"SpriteMap",function(){return jq}),c.d(b,"SpritePackedManager",function(){return kq}),c.d(b,"SpriteSceneComponent",function(){return fq}),c.d(b,"AlphaState",function(){return lq.a}),c.d(b,"DepthCullingState",function(){return Bg.a}),c.d(b,"StencilState",function(){return mq.a}),c.d(b,"StencilStateComposer",function(){return zg.a}),c.d(b,"AndOrNotEvaluator",function(){return nq.a}),c.d(b,"AssetTaskState",function(){return iq}),c.d(b,"AbstractAssetTask",function(){return oq}),c.d(b,"AssetsProgressEvent",function(){return pq}),c.d(b,"ContainerAssetTask",function(){return qq}),c.d(b,"MeshAssetTask",function(){return rq}),c.d(b,"TextFileAssetTask",function(){return sq}),c.d(b,"BinaryFileAssetTask",function(){return tq}),c.d(b,"ImageAssetTask",function(){return uq}),c.d(b,"TextureAssetTask",function(){return vq}),c.d(b,"CubeTextureAssetTask",function(){return wq}),c.d(b,"HDRCubeTextureAssetTask",function(){return xq}),c.d(b,"EquiRectangularCubeTextureAssetTask",function(){return yq}),c.d(b,"AssetsManager",function(){return zq}),c.d(b,"BasisTranscodeConfiguration",function(){return yk}),c.d(b,"BasisToolsOptions",function(){return zk}),c.d(b,"GetInternalFormatFromBasisFormat",function(){return Ak}),c.d(b,"TranscodeAsync",function(){return Ek}),c.d(b,"LoadTextureFromTranscodeResult",function(){return Fk}),c.d(b,"BasisTools",function(){return Gk}),c.d(b,"DDSTools",function(){return Bi}),c.d(b,"expandToProperty",function(){return I.b}),c.d(b,"serialize",function(){return I.d}),c.d(b,"serializeAsTexture",function(){return I.n}),c.d(b,"serializeAsColor3",function(){return I.f}),c.d(b,"serializeAsFresnelParameters",function(){return I.i}),c.d(b,"serializeAsVector2",function(){return I.o}),c.d(b,"serializeAsVector3",function(){return I.p}),c.d(b,"serializeAsMeshReference",function(){return I.l}),c.d(b,"serializeAsColorCurves",function(){return I.h}),c.d(b,"serializeAsColor4",function(){return I.g}),c.d(b,"serializeAsImageProcessingConfiguration",function(){return I.j}),c.d(b,"serializeAsQuaternion",function(){return I.m}),c.d(b,"serializeAsMatrix",function(){return I.k}),c.d(b,"serializeAsCameraReference",function(){return I.e}),c.d(b,"SerializationHelper",function(){return I.a}),c.d(b,"nativeOverride",function(){return I.c}),c.d(b,"Deferred",function(){return Aq}),c.d(b,"GetEnvInfo",function(){return Ue}),c.d(b,"normalizeEnvInfo",function(){return Ve}),c.d(b,"CreateEnvTextureAsync",function(){return We}),c.d(b,"CreateImageDataArrayBufferViews",function(){return Ye}),c.d(b,"UploadEnvLevelsAsync",function(){return Ze}),c.d(b,"UploadLevelsAsync",function(){return af}),c.d(b,"UploadEnvSpherical",function(){return bf}),c.d(b,"_UpdateRGBDAsync",function(){return cf}),c.d(b,"EnvironmentTextureTools",function(){return df}),c.d(b,"MeshExploder",function(){return Bq}),c.d(b,"FilesInput",function(){return Dq}),c.d(b,"CubeMapToSphericalPolynomialTools",function(){return Se}),c.d(b,"HDRTools",function(){return Lj}),c.d(b,"PanoramaToCubeMapTools",function(){return Kj}),c.d(b,"KhronosTextureContainer",function(){return Ei}),c.d(b,"EventState",function(){return f.a}),c.d(b,"Observer",function(){return f.d}),c.d(b,"MultiObserver",function(){return f.b}),c.d(b,"Observable",function(){return f.c}),c.d(b,"PerformanceMonitor",function(){return Eq.a}),c.d(b,"RollingAverage",function(){return Eq.b}),c.d(b,"PromisePolyfill",function(){return Fq.a}),c.d(b,"SceneOptimization",function(){return Gq}),c.d(b,"TextureOptimization",function(){return Hq}),c.d(b,"HardwareScalingOptimization",function(){return Iq}),c.d(b,"ShadowsOptimization",function(){return Jq}),c.d(b,"PostProcessesOptimization",function(){return Kq}),c.d(b,"LensFlaresOptimization",function(){return Lq}),c.d(b,"CustomOptimization",function(){return Mq}),c.d(b,"ParticlesOptimization",function(){return Nq}),c.d(b,"RenderTargetsOptimization",function(){return Oq}),c.d(b,"MergeMeshesOptimization",function(){return Pq});c.d(b,"SceneOptimizerOptions",function(){return Qq}),c.d(b,"SceneOptimizer",function(){return Rq}),c.d(b,"SceneSerializer",function(){return Vq}),c.d(b,"SmartArray",function(){return Ac.a}),c.d(b,"SmartArrayNoDuplicate",function(){return Ac.b}),c.d(b,"StringDictionary",function(){return $b.a}),c.d(b,"Tags",function(){return Wq.a}),c.d(b,"CreateResizedCopy",function(){return Le}),c.d(b,"ApplyPostProcess",function(){return Me}),c.d(b,"ToHalfFloat",function(){return Ne}),c.d(b,"FromHalfFloat",function(){return Oe}),c.d(b,"TextureTools",function(){return Pe}),c.d(b,"GetTGAHeader",function(){return sk}),c.d(b,"UploadContent",function(){return tk}),c.d(b,"TGATools",function(){return uk}),c.d(b,"Tools",function(){return T.b}),c.d(b,"className",function(){return T.c}),c.d(b,"AsyncLoop",function(){return T.a}),c.d(b,"VideoRecorder",function(){return Xq}),c.d(b,"JoystickAxis",function(){return Zb}),c.d(b,"VirtualJoystick",function(){return ac}),c.d(b,"WorkerPool",function(){return Fi}),c.d(b,"Logger",function(){return q.a}),c.d(b,"RegisterClass",function(){return i.b}),c.d(b,"GetClass",function(){return i.a}),c.d(b,"FilesInputStore",function(){return Cq.a}),c.d(b,"DeepCopier",function(){return D.a}),c.d(b,"PivotTools",function(){return xh.a}),c.d(b,"PrecisionDate",function(){return P.a}),c.d(b,"CreateScreenshot",function(){return Yq}),c.d(b,"CreateScreenshotAsync",function(){return Zq}),c.d(b,"CreateScreenshotWithResizeAsync",function(){return $q}),c.d(b,"CreateScreenshotUsingRenderTarget",function(){return ar}),c.d(b,"CreateScreenshotUsingRenderTargetAsync",function(){return br}),c.d(b,"ScreenshotTools",function(){return er}),c.d(b,"WebRequest",function(){return L.a}),c.d(b,"InspectableType",function(){return dr}),c.d(b,"GetEnvironmentBRDFTexture",function(){return ii}),c.d(b,"BRDFTextureTools",function(){return ji}),c.d(b,"RGBDTextureTools",function(){return Qe}),c.d(b,"ColorGradient",function(){return xo}),c.d(b,"Color3Gradient",function(){return yo}),c.d(b,"FactorGradient",function(){return zo}),c.d(b,"GradientHelper",function(){return Ao}),c.d(b,"PerfCounter",function(){return re.a}),c.d(b,"RetryStrategy",function(){return gr.a}),c.d(b,"LoadFileError",function(){return ue.h}),c.d(b,"RequestFileError",function(){return ue.m}),c.d(b,"ReadFileError",function(){return ue.k}),c.d(b,"FileToolsOptions",function(){return ue.d}),c.d(b,"SetCorsBehavior",function(){return ue.n}),c.d(b,"LoadImage",function(){return ue.i}),c.d(b,"ReadFile",function(){return ue.j}),c.d(b,"LoadFile",function(){return ue.g}),c.d(b,"RequestFile",function(){return ue.l}),c.d(b,"IsFileURL",function(){return ue.f}),c.d(b,"IsBase64DataUrl",function(){return ue.e}),c.d(b,"DecodeBase64UrlToBinary",function(){return ue.a}),c.d(b,"DecodeBase64UrlToString",function(){return ue.b}),c.d(b,"FileTools",function(){return ue.c}),c.d(b,"_injectLTSFileTools",function(){return hr.b}),c.d(b,"EndsWith",function(){return gh.e}),c.d(b,"StartsWith",function(){return gh.g}),c.d(b,"Decode",function(){return gh.a}),c.d(b,"EncodeArrayBufferToBase64",function(){return gh.d}),c.d(b,"DecodeBase64ToString",function(){return gh.c}),c.d(b,"DecodeBase64ToBinary",function(){return gh.b}),c.d(b,"PadNumber",function(){return gh.f}),c.d(b,"StringTools",function(){return gh.h}),c.d(b,"DataReader",function(){return ir}),c.d(b,"MinMaxReducer",function(){return xj}),c.d(b,"DepthReducer",function(){return yj}),c.d(b,"DataStorage",function(){return jr}),c.d(b,"SceneRecorder",function(){return kr}),c.d(b,"KhronosTextureContainer2",function(){return Gi}),c.d(b,"Trajectory",function(){return lr}),c.d(b,"TrajectoryClassifier",function(){return pr}),c.d(b,"TimerState",function(){return Yi}),c.d(b,"setAndStartTimer",function(){return bj}),c.d(b,"AdvancedTimer",function(){return cj}),c.d(b,"GenerateBase64StringFromPixelData",function(){return qr.b}),c.d(b,"GenerateBase64StringFromTexture",function(){return qr.c}),c.d(b,"GenerateBase64StringFromTextureAsync",function(){return qr.d}),c.d(b,"CopyTools",function(){return qr.a}),c.d(b,"Reflector",function(){return rr}),c.d(b,"IsWindowObjectExist",function(){return Ja.e}),c.d(b,"IsNavigatorAvailable",function(){return Ja.d}),c.d(b,"IsDocumentAvailable",function(){return Ja.c}),c.d(b,"GetDOMTextContent",function(){return Ja.b}),c.d(b,"DomManagement",function(){return Ja.a}),c.d(b,"ComputePressureObserverWrapper",function(){return sr.a}),c.d(b,"PerformanceViewerCollector",function(){return vr}),c.d(b,"PerfCollectionStrategy",function(){return xr}),c.d(b,"DynamicFloat32Array",function(){return tr}),c.d(b,"WebXRCamera",function(){return Ji}),c.d(b,"WebXREnterExitUIButton",function(){return Zi}),c.d(b,"WebXREnterExitUIOptions",function(){return $i}),c.d(b,"WebXREnterExitUI",function(){return aj}),c.d(b,"WebXRExperienceHelper",function(){return Ki}),c.d(b,"WebXRInput",function(){return Ui}),c.d(b,"WebXRInputSource",function(){return Ti}),c.d(b,"WebXRManagedOutputCanvasOptions",function(){return ud}),c.d(b,"WebXRManagedOutputCanvas",function(){return vd}),c.d(b,"WebXRState",function(){return rd}),c.d(b,"WebXRTrackingState",function(){return sd}),c.d(b,"WebXRSessionManager",function(){return wd}),c.d(b,"WebXRDefaultExperienceOptions",function(){return ej}),c.d(b,"WebXRDefaultExperience",function(){return fj}),c.d(b,"WebXRFeatureName",function(){return kb}),c.d(b,"WebXRFeaturesManager",function(){return lb}),c.d(b,"WebXRAbstractFeature",function(){return Vi}),c.d(b,"WebXRHitTestLegacy",function(){return yr}),c.d(b,"WebXRAnchorSystem",function(){return Ar}),c.d(b,"WebXRPlaneDetector",function(){return Cr}),c.d(b,"WebXRBackgroundRemover",function(){return Dr}),c.d(b,"WebXRMotionControllerTeleportation",function(){return dj}),c.d(b,"WebXRControllerPointerSelection",function(){return Wi}),c.d(b,"IWebXRControllerPhysicsOptions",function(){return Er}),c.d(b,"WebXRControllerPhysics",function(){return Fr}),c.d(b,"WebXRHitTest",function(){return Gr}),c.d(b,"WebXRFeaturePointSystem",function(){return Hr}),c.d(b,"WebXRHand",function(){return Kr}),c.d(b,"WebXRHandTracking",function(){return Lr}),c.d(b,"WebXRMeshDetector",function(){return Nr}),c.d(b,"WebXRImageTracking",function(){return Or}),c.d(b,"WebXRNearInteraction",function(){return Xi}),c.d(b,"WebXRDomOverlay",function(){return Pr}),c.d(b,"WebXRControllerMovement",function(){return Qr}),c.d(b,"WebXRLightEstimation",function(){return Sr}),c.d(b,"WebXREyeTracking",function(){return Tr}),c.d(b,"WebXRWalkingLocomotion",function(){return Yr}),c.d(b,"WebXRAbstractMotionController",function(){return Mi}),c.d(b,"WebXRControllerComponent",function(){return Li}),c.d(b,"WebXRGenericHandController",function(){return Zr}),c.d(b,"WebXRGenericTriggerMotionController",function(){return Ni}),c.d(b,"WebXRMicrosoftMixedRealityController",function(){return as}),c.d(b,"WebXRMotionControllerManager",function(){return Ri}),c.d(b,"WebXROculusTouchMotionController",function(){return cs}),c.d(b,"WebXRHTCViveMotionController",function(){return es}),c.d(b,"WebXRProfiledMotionController",function(){return Pi});var d=c(43),e=c(98),f=c(6),g=c(0),h=c(8),i=c(10),j=function(){function a(a,b){this.triggerOptions=a,this.onBeforeExecuteObservable=new f.c,a.parameter?(this.trigger=a.trigger,this._triggerParameter=a.parameter):a.trigger?this.trigger=a.trigger:this.trigger=a,this._nextActiveAction=this,this._condition=b}return a.prototype._prepare=function(){},a.prototype.getTriggerParameter=function(){return this._triggerParameter},a.prototype.setTriggerParameter=function(a){this._triggerParameter=a},a.prototype._evaluateConditionForCurrentFrame=function(){var a=this._condition;if(!a)return!0;var b=this._actionManager.getScene().getRenderId();return a._evaluationId!==b&&(a._evaluationId=b,a._currentResult=a.isValid()),a._currentResult},a.prototype._executeCurrent=function(a){this._evaluateConditionForCurrentFrame()&&(this.onBeforeExecuteObservable.notifyObservers(this),this._nextActiveAction.execute(a),this.skipToNextActiveAction())},a.prototype.execute=function(a){},a.prototype.skipToNextActiveAction=function(){this._nextActiveAction._child?(this._nextActiveAction._child._actionManager||(this._nextActiveAction._child._actionManager=this._actionManager),this._nextActiveAction=this._nextActiveAction._child):this._nextActiveAction=this},a.prototype.then=function(a){return this._child=a,a._actionManager=this._actionManager,a._prepare(),a},a.prototype._getProperty=function(a){return this._actionManager._getProperty(a)},a.prototype._getEffectiveTarget=function(a,b){return this._actionManager._getEffectiveTarget(a,b)},a.prototype.serialize=function(a){},a.prototype._serialize=function(a,b){a={type:1,children:[],name:a.name,properties:a.properties||[]};if(this._child&&this._child.serialize(a),this._condition){var c=this._condition.serialize();return c.children.push(a),b&&b.children.push(c),c}return b&&b.children.push(a),a},a._SerializeValueAsString=function(a){return"number"==typeof a?a.toString():"boolean"==typeof a?a?"true":"false":a instanceof g.d?a.x+", "+a.y:a instanceof g.e?a.x+", "+a.y+", "+a.z:a instanceof h.a?a.r+", "+a.g+", "+a.b:a instanceof h.b?a.r+", "+a.g+", "+a.b+", "+a.a:a},a._GetTargetProperty=function(a){return{name:"target",targetType:a._isMesh?"MeshProperties":a._isLight?"LightProperties":a._isCamera?"CameraProperties":"SceneProperties",value:a._isScene?"Scene":a.name}},a}();Object(i.b)("BABYLON.Action",j);var k=c(55),l=c(2),m=function(){function a(a){this._actionManager=a}return a.prototype.isValid=function(){return!0},a.prototype._getProperty=function(a){return this._actionManager._getProperty(a)},a.prototype._getEffectiveTarget=function(a,b){return this._actionManager._getEffectiveTarget(a,b)},a.prototype.serialize=function(){},a.prototype._serialize=function(a){return{type:2,children:[],name:a.name,properties:a.properties}},a}(),n=function(a){function b(c,d,e,f,g){void 0===g&&(g=b.IsEqual);c=a.call(this,c)||this;return c.propertyPath=e,c.value=f,c.operator=g,c._target=d,c._effectiveTarget=c._getEffectiveTarget(d,c.propertyPath),c._property=c._getProperty(c.propertyPath),c}return Object(l.d)(b,a),Object.defineProperty(b,"IsEqual",{get:function(){return b._IsEqual},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsDifferent",{get:function(){return b._IsDifferent},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsGreater",{get:function(){return b._IsGreater},enumerable:!1,configurable:!0}),Object.defineProperty(b,"IsLesser",{get:function(){return b._IsLesser},enumerable:!1,configurable:!0}),b.prototype.isValid=function(){switch(this.operator){case b.IsGreater:return this._effectiveTarget[this._property]>this.value;case b.IsLesser:return this._effectiveTarget[this._property]-1&&this._scene.actionManagers.splice(a,1)},b.prototype.getScene=function(){return this._scene},b.prototype.hasSpecificTriggers=function(a){for(var b=0;b-1)return!0}return!1},b.prototype.hasSpecificTriggers2=function(a,b){for(var c=0;c=b.OnPickTrigger&&c.trigger<=b.OnPointerOutTrigger)return!0}return!1},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasPickTriggers",{get:function(){for(var a=0;a=b.OnPickTrigger&&c.trigger<=b.OnPickUpTrigger)return!0}return!1},enumerable:!1,configurable:!0}),b.prototype.registerAction=function(a){return a.trigger===b.OnEveryFrameTrigger&&this.getScene().actionManager!==this?(q.a.Warn("OnEveryFrameTrigger can only be used with scene.actionManager"),null):(this.actions.push(a),b.Triggers[a.trigger]?b.Triggers[a.trigger]++:b.Triggers[a.trigger]=1,a._actionManager=this,a._prepare(),a)},b.prototype.unregisterAction=function(a){var c=this.actions.indexOf(a);return-1!==c&&(this.actions.splice(c,1),b.Triggers[a.trigger]-=1,0===b.Triggers[a.trigger]&&delete b.Triggers[a.trigger],a._actionManager=null,!0)},b.prototype.processTrigger=function(a,c){for(var d=0;d0;if(2===a.type?l.push(e):l.push(c),u){for(var u=new Array,x=0;x0){var l=k.properties[0].value;l=null===k.properties[0].targetType?l:d.getMeshByName(l);l._meshId&&(l.mesh=d.getMeshById(l._meshId)),l={trigger:b[k.name],parameter:l}}else l=b[k.name];for(var t=0;t=0?c:0;e=0;b=h._keys[0];f=h._keys.length-1;var i=h._keys[f],j={referenceValue:b.value,referencePosition:g.c.Vector3[0],referenceQuaternion:g.c.Quaternion[0],referenceScaling:g.c.Vector3[1],keyPosition:g.c.Vector3[2],keyQuaternion:g.c.Quaternion[1],keyScaling:g.c.Vector3[3]},k=!1,l=b.frame,q=i.frame;if(d){d=h.getRange(d);d&&(l=d.from,q=d.to)}d=b.frame===l;var r=i.frame===q;if(1===h._keys.length){var u=h._getKeyValue(h._keys[0]);j.referenceValue=u.clone?u.clone():u,k=!0}else c<=b.frame?(u=h._getKeyValue(b.value),(j.referenceValue=u.clone?u.clone():u,k=!0)):c>=i.frame&&(u=h._getKeyValue(i.value),(j.referenceValue=u.clone?u.clone():u,k=!0));for(i=0;!k||!d||!r&&i=z.frame&&c<=A.frame){if(c===z.frame)u=h._getKeyValue(z.value);else if(c===A.frame)u=h._getKeyValue(A.value);else{var B={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT};u=h._interpolate(c,B)}j.referenceValue=u.clone?u.clone():u,k=!0}if(!d&&l>=z.frame&&l<=A.frame){if(l===z.frame)e=i;else if(l===A.frame)e=i+1;else{B={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT};var C={frame:l,value:(u=h._interpolate(l,B)).clone?u.clone():u};h._keys.splice(i+1,0,C),e=i+1}d=!0}!r&&q>=z.frame&&q<=A.frame&&(q===z.frame?f=i:q===A.frame?f=i+1:((B={key:i,repeatCount:0,loopMode:this.ANIMATIONLOOPMODE_CONSTANT},C={frame:q,value:(u=h._interpolate(q,B)).clone?u.clone():u}),(h._keys.splice(i+1,0,C),f=i+1)),r=!0);i++}h.dataType===a.ANIMATIONTYPE_QUATERNION?j.referenceValue.normalize().conjugateInPlace():h.dataType===a.ANIMATIONTYPE_MATRIX&&(j.referenceValue.decompose(j.referenceScaling,j.referenceQuaternion,j.referencePosition),j.referenceQuaternion.normalize().conjugateInPlace());for(i=e;i<=f;i++){C=h._keys[i];if(!i||h.dataType===a.ANIMATIONTYPE_FLOAT||C.value!==b.value)switch(h.dataType){case a.ANIMATIONTYPE_MATRIX:C.value.decompose(j.keyScaling,j.keyQuaternion,j.keyPosition),j.keyPosition.subtractInPlace(j.referencePosition),j.keyScaling.divideInPlace(j.referenceScaling),j.referenceQuaternion.multiplyToRef(j.keyQuaternion,j.keyQuaternion),g.a.ComposeToRef(j.keyScaling,j.keyQuaternion,j.keyPosition,C.value);break;case a.ANIMATIONTYPE_QUATERNION:j.referenceValue.multiplyToRef(C.value,C.value);break;case a.ANIMATIONTYPE_VECTOR2:case a.ANIMATIONTYPE_VECTOR3:case a.ANIMATIONTYPE_COLOR3:case a.ANIMATIONTYPE_COLOR4:C.value.subtractToRef(j.referenceValue,C.value);break;case a.ANIMATIONTYPE_SIZE:C.value.width-=j.referenceValue.width,C.value.height-=j.referenceValue.height;break;default:C.value-=j.referenceValue}}return h},a.TransitionTo=function(a,b,c,d,e,f,g,h){if(void 0===h&&(h=null),g<=0)return c[a]=b,h&&h(),null;e=e*(g/1e3);f.setKeys([{frame:0,value:c[a].clone?c[a].clone():c[a]},{frame:e,value:b}]),c.animations||(c.animations=[]),c.animations.push(f);g=d.beginAnimation(c,0,e,!1);return g.onAnimationEnd=h,g},Object.defineProperty(a.prototype,"runtimeAnimations",{get:function(){return this._runtimeAnimations},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasRunningRuntimeAnimations",{get:function(){for(var a=0,b=this._runtimeAnimations;a=0;d--)this._keys[d].frame>=b&&this._keys[d].frame<=c&&this._keys.splice(d,1);this._ranges[a]=null}},a.prototype.getRange=function(a){return this._ranges[a]},a.prototype.getKeys=function(){return this._keys},a.prototype.getHighestFrame=function(){for(var a=0,b=0,c=this._keys.length;b0)return c.highLimitValue.clone?c.highLimitValue.clone():c.highLimitValue;var d=this._keys;if(1===d.length)return this._getKeyValue(d[0].value);var e=c.key;if(d[e].frame>=b)for(;e-1>=0&&d[e].frame>=b;)e--;for(e=e;e=b){c.key=e;var g=d[e],h=this._getKeyValue(g.value);if(g.interpolation===G.STEP)return h;var i=this._getKeyValue(f.value),j=void 0!==g.outTangent&&void 0!==f.inTangent,k=f.frame-g.frame,l=(b-g.frame)/k,o=this.getEasingFunction();switch(null!=o&&(l=o.ease(l)),this.dataType){case a.ANIMATIONTYPE_FLOAT:o=j?this.floatInterpolateFunctionWithTangents(h,g.outTangent*k,i,f.inTangent*k,l):this.floatInterpolateFunction(h,i,l);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return o;case a.ANIMATIONLOOPMODE_RELATIVE:return c.offsetValue*c.repeatCount+o}break;case a.ANIMATIONTYPE_QUATERNION:o=j?this.quaternionInterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),l):this.quaternionInterpolateFunction(h,i,l);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return o;case a.ANIMATIONLOOPMODE_RELATIVE:return o.addInPlace(c.offsetValue.scale(c.repeatCount))}return o;case a.ANIMATIONTYPE_VECTOR3:o=j?this.vector3InterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),l):this.vector3InterpolateFunction(h,i,l);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return o;case a.ANIMATIONLOOPMODE_RELATIVE:return o.add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_VECTOR2:o=j?this.vector2InterpolateFunctionWithTangents(h,g.outTangent.scale(k),i,f.inTangent.scale(k),l):this.vector2InterpolateFunction(h,i,l);switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return o;case a.ANIMATIONLOOPMODE_RELATIVE:return o.add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_SIZE:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.sizeInterpolateFunction(h,i,l);case a.ANIMATIONLOOPMODE_RELATIVE:return this.sizeInterpolateFunction(h,i,l).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_COLOR3:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.color3InterpolateFunction(h,i,l);case a.ANIMATIONLOOPMODE_RELATIVE:return this.color3InterpolateFunction(h,i,l).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_COLOR4:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:return this.color4InterpolateFunction(h,i,l);case a.ANIMATIONLOOPMODE_RELATIVE:return this.color4InterpolateFunction(h,i,l).add(c.offsetValue.scale(c.repeatCount))}case a.ANIMATIONTYPE_MATRIX:switch(c.loopMode){case a.ANIMATIONLOOPMODE_CYCLE:case a.ANIMATIONLOOPMODE_CONSTANT:if(a.AllowMatricesInterpolation)return this.matrixInterpolateFunction(h,i,l,c.workValue);case a.ANIMATIONLOOPMODE_RELATIVE:return h}}break}}return this._getKeyValue(d[d.length-1].value)},a.prototype.matrixInterpolateFunction=function(b,c,d,e){return a.AllowMatrixDecomposeForInterpolation?e?(g.a.DecomposeLerpToRef(b,c,d,e),e):g.a.DecomposeLerp(b,c,d):e?(g.a.LerpToRef(b,c,d,e),e):g.a.Lerp(b,c,d)},a.prototype.clone=function(){var b=new a(this.name,this.targetPropertyPath.join("."),this.framePerSecond,this.dataType,this.loopMode);if(b.enableBlending=this.enableBlending,b.blendingSpeed=this.blendingSpeed,this._keys&&b.setKeys(this._keys),this._ranges)for(var c in b._ranges={},this._ranges){var d=this._ranges[c];d&&(b._ranges[c]=d.clone())}return b},a.prototype.setKeys=function(a){this._keys=a.slice(0)},a.prototype.serialize=function(){var b={};b.name=this.name,b.property=this.targetProperty,b.framePerSecond=this.framePerSecond,b.dataType=this.dataType,b.loopBehavior=this.loopMode,b.enableBlending=this.enableBlending,b.blendingSpeed=this.blendingSpeed;var c=this.dataType;b.keys=[];for(var d=this.getKeys(),e=0;e=1&&(j=l.values[1]),l.values.length>=2&&(k=l.values[2]);break;case a.ANIMATIONTYPE_QUATERNION:if(c=g.b.FromArray(l.values),l.values.length>=8){var n=g.b.FromArray(l.values.slice(4,8));n.equals(g.b.Zero())||(j=n)}if(l.values.length>=12){n=g.b.FromArray(l.values.slice(8,12));n.equals(g.b.Zero())||(k=n)}break;case a.ANIMATIONTYPE_MATRIX:c=g.a.FromArray(l.values);break;case a.ANIMATIONTYPE_COLOR3:c=h.a.FromArray(l.values);break;case a.ANIMATIONTYPE_COLOR4:c=h.b.FromArray(l.values);break;case a.ANIMATIONTYPE_VECTOR3:default:c=g.e.FromArray(l.values)}n={};n.frame=l.frame,n.value=c,null!=j&&(n.inTangent=j),null!=k&&(n.outTangent=k),i.push(n)}if(e.setKeys(i),b.ranges)for(d=0;d0&&h.forEach(function(a){e._events.push(a._clone())}),this._enableBlending=a&&a.animationPropertiesOverride?a.animationPropertiesOverride.enableBlending:this._animation.enableBlending}return Object.defineProperty(a.prototype,"currentFrame",{get:function(){return this._currentFrame},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"weight",{get:function(){return this._weight},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentValue",{get:function(){return this._currentValue},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"targetPath",{get:function(){return this._targetPath},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"target",{get:function(){return this._currentActiveTarget},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isAdditive",{get:function(){return this._host&&this._host.isAdditive},enumerable:!1,configurable:!0}),a.prototype._preparePath=function(a,b){void 0===b&&(b=0);var c=this._animation.targetPropertyPath;if(c.length>1){for(var d=a[c[0]],e=1;e-1&&this._animation.runtimeAnimations.splice(a,1)},a.prototype.setValue=function(a,b){if(this._targetIsArray)for(var c=0;cb[b.length-1].frame&&(a=b[b.length-1].frame);b=this._events;if(b.length)for(var c=0;cthis._maxFrame)&&(b=this._minFrame),(cthis._maxFrame)&&(c=this._maxFrame);var i,j=c-b,k=a*(g.framePerSecond*e)/1e3+this._ratioOffset,l=0;if(this._previousDelay=a,this._previousRatio=k,!d&&c>=b&&k>=j)h=!1,l=g._getKeyValue(this._maxValue);else if(!d&&b>=c&&k<=j)h=!1,l=g._getKeyValue(this._minValue);else if(this._animationState.loopMode!==N.ANIMATIONLOOPMODE_CYCLE){a=c.toString()+b.toString();if(!this._offsetsCache[a]){this._animationState.repeatCount=0,this._animationState.loopMode=N.ANIMATIONLOOPMODE_CYCLE;d=g._interpolate(b,this._animationState);var o=g._interpolate(c,this._animationState);switch(this._animationState.loopMode=this._getCorrectLoopMode(),g.dataType){case N.ANIMATIONTYPE_FLOAT:this._offsetsCache[a]=o-d;break;case N.ANIMATIONTYPE_QUATERNION:case N.ANIMATIONTYPE_VECTOR3:case N.ANIMATIONTYPE_VECTOR2:case N.ANIMATIONTYPE_SIZE:case N.ANIMATIONTYPE_COLOR3:this._offsetsCache[a]=o.subtract(d)}this._highLimitsCache[a]=o}l=this._highLimitsCache[a],i=this._offsetsCache[a]}if(void 0===i)switch(g.dataType){case N.ANIMATIONTYPE_FLOAT:i=0;break;case N.ANIMATIONTYPE_QUATERNION:i=da;break;case N.ANIMATIONTYPE_VECTOR3:i=ea;break;case N.ANIMATIONTYPE_VECTOR2:i=fa;break;case N.ANIMATIONTYPE_SIZE:i=ga;break;case N.ANIMATIONTYPE_COLOR3:i=ha}if(this._host&&this._host.syncRoot){d=this._host.syncRoot;o=b+(c-b)*((d.masterFrame-d.fromFrame)/(d.toFrame-d.fromFrame))}else o=k>0&&b>c||k<0&&b0&&this.currentFrame>o||e<0&&this.currentFrame>0,this._animationState.highLimitValue=l,this._animationState.offsetValue=i;c=g._interpolate(o,this._animationState);if(this.setValue(c,f),a.length)for(d=0;d0&&o>=a[d].frame&&a[d].frame>=b||j<0&&o<=a[d].frame&&a[d].frame<=b){e=a[d];e.isDone||(e.onlyOnce&&(a.splice(d,1),d--),e.isDone=!0,e.action(o))}return h||(this._stopped=!0),h},a}(),O=c(21),P=c(34),ja=c(51),Q=c(25),ka=function(a){function b(b,c,d,e,f,h,i){void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=null);var j=a.call(this,b,c.getScene())||this;return j.name=b,j.children=new Array,j.animations=new Array,j._index=null,j._absoluteTransform=new g.a,j._invertedAbsoluteTransform=new g.a,j._scalingDeterminant=1,j._worldTransform=new g.a,j._needToDecompose=!0,j._needToCompose=!1,j._linkedTransformNode=null,j._waitingTransformNodeId=null,j._skeleton=c,j._localMatrix=e?e.clone():g.a.Identity(),j._restPose=f||j._localMatrix.clone(),j._baseMatrix=h||j._localMatrix.clone(),j._index=i,c.bones.push(j),j.setParent(d,!1),(h||e)&&j._updateDifferenceMatrix(),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"_matrix",{get:function(){return this._compose(),this._localMatrix},set:function(a){this._needToCompose=!1,a.updateFlag!==this._localMatrix.updateFlag&&(this._localMatrix.copyFrom(a),this._markAsDirtyAndDecompose())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"Bone"},b.prototype.getSkeleton=function(){return this._skeleton},b.prototype.getParent=function(){return this._parent},b.prototype.getChildren=function(){return this.children},b.prototype.getIndex=function(){return null===this._index?this.getSkeleton().bones.indexOf(this):this._index},b.prototype.setParent=function(a,b){if(void 0===b&&(b=!0),this._parent!==a){if(this._parent){var c=this._parent.children.indexOf(this);-1!==c&&this._parent.children.splice(c,1)}this._parent=a,this._parent&&this._parent.children.push(this),b&&this._updateDifferenceMatrix(),this.markAsDirty()}},b.prototype.getLocalMatrix=function(){return this._compose(),this._localMatrix},b.prototype.getBaseMatrix=function(){return this._baseMatrix},b.prototype.getRestPose=function(){return this._restPose},b.prototype.setRestPose=function(a){this._restPose.copyFrom(a)},b.prototype.getBindPose=function(){return this._baseMatrix},b.prototype.setBindPose=function(a){this.updateMatrix(a)},b.prototype.getWorldMatrix=function(){return this._worldTransform},b.prototype.returnToRest=function(){if(this._linkedTransformNode){var a=g.c.Vector3[0],b=g.c.Quaternion[0],c=g.c.Vector3[1];this.getRestPose().decompose(a,b,c),this._linkedTransformNode.position.copyFrom(c),this._linkedTransformNode.rotationQuaternion=null!==(c=this._linkedTransformNode.rotationQuaternion)&&void 0!==c?c:g.b.Identity(),this._linkedTransformNode.rotationQuaternion.copyFrom(b),this._linkedTransformNode.scaling.copyFrom(a)}else this._matrix=this._restPose},b.prototype.getInvertedAbsoluteTransform=function(){return this._invertedAbsoluteTransform},b.prototype.getAbsoluteTransform=function(){return this._absoluteTransform},b.prototype.linkTransformNode=function(a){this._linkedTransformNode&&this._skeleton._numBonesWithLinkedTransformNode--,this._linkedTransformNode=a,this._linkedTransformNode&&this._skeleton._numBonesWithLinkedTransformNode++},b.prototype.getTransformNode=function(){return this._linkedTransformNode},Object.defineProperty(b.prototype,"position",{get:function(){return this._decompose(),this._localPosition},set:function(a){this._decompose(),this._localPosition.copyFrom(a),this._markAsDirtyAndCompose()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotation",{get:function(){return this.getRotation()},set:function(a){this.setRotation(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotationQuaternion",{get:function(){return this._decompose(),this._localRotation},set:function(a){this.setRotationQuaternion(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaling",{get:function(){return this.getScale()},set:function(a){this.setScale(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"animationPropertiesOverride",{get:function(){return this._skeleton.animationPropertiesOverride},enumerable:!1,configurable:!0}),b.prototype._decompose=function(){this._needToDecompose&&(this._needToDecompose=!1,this._localScaling||(this._localScaling=g.e.Zero(),this._localRotation=g.b.Zero(),this._localPosition=g.e.Zero()),this._localMatrix.decompose(this._localScaling,this._localRotation,this._localPosition))},b.prototype._compose=function(){this._needToCompose&&(this._localScaling?(this._needToCompose=!1,g.a.ComposeToRef(this._localScaling,this._localRotation,this._localPosition,this._localMatrix)):this._needToCompose=!1)},b.prototype.updateMatrix=function(a,b,c){void 0===b&&(b=!0),void 0===c&&(c=!0),this._baseMatrix.copyFrom(a),b&&this._updateDifferenceMatrix(),c?this._matrix=a:this.markAsDirty()},b.prototype._updateDifferenceMatrix=function(a,b){if(void 0===b&&(b=!0),a||(a=this._baseMatrix),this._parent?a.multiplyToRef(this._parent._absoluteTransform,this._absoluteTransform):this._absoluteTransform.copyFrom(a),this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform),b)for(a=0;a-1&&(this._scene._activeAnimatables.splice(a,1),this._scene._activeAnimatables.push(this))}return this},a.prototype.getAnimations=function(){return this._runtimeAnimations},a.prototype.appendAnimations=function(a,b){for(var c=this,d=0;d-1){for(var d=(f=this._runtimeAnimations).length-1;d>=0;d--){var e=f[d];a&&e.animation.name!=a||b&&!b(e.target)||(e.dispose(),f.splice(d,1))}0==f.length&&(this._scene._activeAnimatables.splice(c,1),this._raiseOnAnimationEnd())}}else if((d=this._scene._activeAnimatables.indexOf(this))>-1){this._scene._activeAnimatables.splice(d,1);var f=this._runtimeAnimations;for(d=0;d0)return;this._animationTimeLast=a}this.deltaTime=this.useConstantAnimationDeltaTime?16:(a-this._animationTimeLast)*this.animationTimeScale,this._animationTimeLast=a;a=this._activeAnimatables;if(0!==a.length){this._animationTime+=this.deltaTime;for(var b=this._animationTime,c=0;cc&&e>0&&(e*=-1),h&&this.stopAnimation(a,void 0,i),g||(g=new la(this,a,b,c,d,e,f,void 0,j,k));k=!i||i(a);if(a.animations&&k&&g.appendAnimations(a,a.animations),a.getAnimatables)for(k=a.getAnimatables(),a=0;ad&&f>0)f*=-1;else if(d>c&&f<0){var j=d;d=c,c=j}return new la(this,a,c,d,e,f,g,b,h,i)},O.a.prototype.beginDirectHierarchyAnimation=function(a,b,c,d,e,f,g,h,i,j){void 0===j&&(j=!1);b=a.getDescendants(b);var k=[];k.push(this.beginDirectAnimation(a,c,d,e,f,g,h,i,j));for(a=0,b=b;a0)e.copyFrom(d);else if(1===a.animations.length){if(g.b.SlerpToRef(d,c.currentValue,Math.min(1,a.totalWeight),e),0===a.totalAdditiveWeight)return e}else if(a.animations.length>1){c=1;var f=void 0,h=void 0;if(a.totalWeight<1){var i=1-a.totalWeight;h=[],(f=[]).push(d),h.push(i)}else{if(2===a.animations.length&&(g.b.SlerpToRef(a.animations[0].currentValue,a.animations[1].currentValue,a.animations[1].weight/a.totalWeight,b),0===a.totalAdditiveWeight))return b;f=[],h=[],c=a.totalWeight}for(d=0;d=j&&g.frame<=f&&(d?(i=g.value.clone(),l?(h=i.getTranslation(),i.setTranslation(h.scaleInPlace(a))):n&&e?(h=i.getTranslation(),i.setTranslation(h.multiplyInPlace(e))):i=g.value):i=g.value,p.push({frame:g.frame+c,value:i}));return this.animations[0].createRange(b,j+c,f+c),!0};var ma=function(){function a(){}return a.prototype.getClassName=function(){return"TargetedAnimation"},a.prototype.serialize=function(){var a={};return a.animation=this.animation.serialize(),a.targetId=this.target.id,a},a}(),na=function(){function a(a,b){void 0===b&&(b=null),this.name=a,this._targetedAnimations=new Array,this._animatables=new Array,this._from=Number.MAX_VALUE,this._to=-Number.MAX_VALUE,this._speedRatio=1,this._loopAnimation=!1,this._isAdditive=!1,this._parentContainer=null,this.onAnimationEndObservable=new f.c,this.onAnimationLoopObservable=new f.c,this.onAnimationGroupLoopObservable=new f.c,this.onAnimationGroupEndObservable=new f.c,this.onAnimationGroupPauseObservable=new f.c,this.onAnimationGroupPlayObservable=new f.c,this._scene=b||C.a.LastCreatedScene,this.uniqueId=this._scene.getUniqueId(),this._scene.addAnimationGroup(this)}return Object.defineProperty(a.prototype,"from",{get:function(){return this._from},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"to",{get:function(){return this._to},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isStarted",{get:function(){return this._isStarted},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isPlaying",{get:function(){return this._isStarted&&!this._isPaused},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"speedRatio",{get:function(){return this._speedRatio},set:function(a){if(this._speedRatio!==a){this._speedRatio=a;for(a=0;ab[0].frame&&(this._from=b[0].frame),this._toa){e={frame:a,value:e.value,inTangent:e.inTangent,outTangent:e.outTangent,interpolation:e.interpolation};d.splice(0,0,e)}f.frame-1&&this._scene.animationGroups.splice(a,1),this._parentContainer){a=this._parentContainer.animationGroups.indexOf(this);a>-1&&this._parentContainer.animationGroups.splice(a,1),this._parentContainer=null}this.onAnimationEndObservable.clear(),this.onAnimationGroupEndObservable.clear(),this.onAnimationGroupPauseObservable.clear(),this.onAnimationGroupPlayObservable.clear(),this.onAnimationLoopObservable.clear(),this.onAnimationGroupLoopObservable.clear()},a.prototype._checkAnimationGroupEnded=function(a){a=this._animatables.indexOf(a);a>-1&&this._animatables.splice(a,1),0===this._animatables.length&&(this._isStarted=!1,this.onAnimationGroupEndObservable.notifyObservers(this))},a.prototype.clone=function(b,c,d){void 0===d&&(d=!1);for(var b=new a(b||this.name,this._scene),e=0,f=this._targetedAnimations;e=.5?.5*(1-this.easeInCore(2*(1-b)))+.5:.5*this.easeInCore(2*b)},a.EASINGMODE_EASEIN=0,a.EASINGMODE_EASEOUT=1,a.EASINGMODE_EASEINOUT=2,a}(),ra=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a=Math.max(0,Math.min(1,a)),1-Math.sqrt(1-a*a)},b}(qa),sa=function(a){function b(b){void 0===b&&(b=1);var c=a.call(this)||this;return c.amplitude=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.amplitude);return Math.pow(a,3)-a*b*Math.sin(3.141592653589793*a)},b}(qa),ta=function(a){function b(b,c){void 0===b&&(b=3),void 0===c&&(c=2);var d=a.call(this)||this;return d.bounces=b,d.bounciness=c,d}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.bounces),c=this.bounciness;c<=1&&(c=1.001);var d=Math.pow(c,b),e=1-c;d=(1-d)/e+.5*d;var f=a*d;f=Math.log(-f*(1-c)+1)/Math.log(c);f=Math.floor(f);var g=f+1,h=(1-Math.pow(c,f))/(e*d);g=.5*(h+(1-Math.pow(c,g))/(e*d));e=a-g;d=g-h;return-Math.pow(1/c,b-f)/(d*d)*(e-d)*(e+d)},b}(qa),ua=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a},b}(qa),va=function(a){function b(b,c){void 0===b&&(b=3),void 0===c&&(c=3);var d=a.call(this)||this;return d.oscillations=b,d.springiness=c,d}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.oscillations),c=Math.max(0,this.springiness);return(0==c?a:(Math.exp(c*a)-1)/(Math.exp(c)-1))*Math.sin((6.283185307179586*b+1.5707963267948966)*a)},b}(qa),wa=function(a){function b(b){void 0===b&&(b=2);var c=a.call(this)||this;return c.exponent=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return this.exponent<=0?a:(Math.exp(this.exponent*a)-1)/(Math.exp(this.exponent)-1)},b}(qa),xa=function(a){function b(b){void 0===b&&(b=2);var c=a.call(this)||this;return c.power=b,c}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){var b=Math.max(0,this.power);return Math.pow(a,b)},b}(qa),ya=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a},b}(qa),za=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a*a},b}(qa),Aa=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return a*a*a*a*a},b}(qa),Ba=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return 1-Math.sin(1.5707963267948966*(1-a))},b}(qa),Ca=function(a){function b(b,c,d,e){void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=1),void 0===e&&(e=1);var f=a.call(this)||this;return f.x1=b,f.y1=c,f.x2=d,f.y2=e,f}return Object(l.d)(b,a),b.prototype.easeInCore=function(a){return pa.c.Interpolate(a,this.x1,this.y1,this.x2,this.y2)},b}(qa),Da=function(){function a(a,b,c){this.frame=a,this.action=b,this.onlyOnce=c,this.isDone=!1}return a.prototype._clone=function(){return new a(this.frame,this.action,this.onlyOnce)},a}(),Ea=function(){function a(a){this.path=a,this._onchange=new Array,this.value=0,this.animations=new Array}return a.prototype.getPoint=function(){var a=this.path.getPointAtLengthPosition(this.value);return new g.e(a.x,0,a.y)},a.prototype.moveAhead=function(a){return void 0===a&&(a=.002),this.move(a),this},a.prototype.moveBack=function(a){return void 0===a&&(a=.002),this.move(-a),this},a.prototype.move=function(a){if(Math.abs(a)>1)throw"step size should be less than 1.";return this.value+=a,this.ensureLimits(),this.raiseOnChange(),this},a.prototype.ensureLimits=function(){for(;this.value>1;)this.value-=1;for(;this.value<0;)this.value+=1;return this},a.prototype.raiseOnChange=function(){var a=this;return this._onchange.forEach(function(b){return b(a)}),this},a.prototype.onchange=function(a){return this._onchange.push(a),this},a}(),R=c(9),Fa=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b}(d.a),Ga=function(){this.rootNodes=[],this.skeletons=[],this.animationGroups=[]},Ha=function(a){function b(b){var c=a.call(this)||this;return c._wasAddedToScene=!1,c.scene=b,c.sounds=[],c.effectLayers=[],c.layers=[],c.lensFlareSystems=[],c.proceduralTextures=[],c.reflectionProbes=[],b.onDisposeObservable.add(function(){c._wasAddedToScene||c.dispose()}),c._onContextRestoredObserver=b.getEngine().onContextRestoredObservable.add(function(){for(var a=0,b=c.geometries;a-1&&b.animations.splice(e,1)}},e=0,f=a.animations;e0&&(d=!0,this._soundLoaded(c));break;case"String":b.push(c);case"Array":0===b.length&&(b=c);for(c=0;c0&&(this._htmlAudioElement.currentTime=0)):this._streamingSource.disconnect(),this.isPlaying=!1;else if((null===(b=S.a.audioEngine)||void 0===b?void 0:b.audioContext)&&this._soundSource){b=a?S.a.audioEngine.audioContext.currentTime+a:void 0;this._soundSource.stop(b),void 0===b?(this.isPlaying=!1,this._soundSource.onended=function(){}):this._soundSource.onended=function(){c.isPlaying=!1},this.isPaused||(this._startOffset=0)}},a.prototype.pause=function(){var a;this.isPlaying&&(this.isPaused=!0,this._streaming?(this._htmlAudioElement?this._htmlAudioElement.pause():this._streamingSource.disconnect(),this.isPlaying=!1):(null===(a=S.a.audioEngine)||void 0===a?void 0:a.audioContext)&&(this.stop(0),this._startOffset+=S.a.audioEngine.audioContext.currentTime-this._startTime))},a.prototype.setVolume=function(a,b){var c;(null===(c=S.a.audioEngine)||void 0===c?void 0:c.canUseWebAudio)&&this._soundGain&&(b&&S.a.audioEngine.audioContext?(this._soundGain.gain.cancelScheduledValues(S.a.audioEngine.audioContext.currentTime),this._soundGain.gain.setValueAtTime(this._soundGain.gain.value,S.a.audioEngine.audioContext.currentTime),this._soundGain.gain.linearRampToValueAtTime(a,S.a.audioEngine.audioContext.currentTime+b)):this._soundGain.gain.value=a),this._volume=a},a.prototype.setPlaybackRate=function(a){this._playbackRate=a,this.isPlaying&&(this._streaming&&this._htmlAudioElement?this._htmlAudioElement.playbackRate=this._playbackRate:this._soundSource&&(this._soundSource.playbackRate.value=this._playbackRate))},a.prototype.getVolume=function(){return this._volume},a.prototype.attachToMesh=function(a){var b=this;this._connectedTransformNode&&this._registerFunc&&(this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc),this._registerFunc=null),this._connectedTransformNode=a,this.spatialSound||(this.spatialSound=!0,this._createSpatialParameters(),this.isPlaying&&this.loop&&(this.stop(),this.play(0,this._offset,this._length))),this._onRegisterAfterWorldMatrixUpdate(this._connectedTransformNode),this._registerFunc=function(a){return b._onRegisterAfterWorldMatrixUpdate(a)},this._connectedTransformNode.registerAfterWorldMatrixUpdate(this._registerFunc)},a.prototype.detachFromMesh=function(){this._connectedTransformNode&&this._registerFunc&&(this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc),this._registerFunc=null,this._connectedTransformNode=null)},a.prototype._onRegisterAfterWorldMatrixUpdate=function(a){if(a.getBoundingInfo){var b=a.getBoundingInfo();this.setPosition(b.boundingSphere.centerWorld)}else this.setPosition(a.absolutePosition);(null===(b=S.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._isDirectional&&this.isPlaying&&this._updateDirection()},a.prototype.clone=function(){var b=this;if(this._streaming)return null;var c=function(){b._isReadyToPlay?(e._audioBuffer=b.getAudioBuffer(),e._isReadyToPlay=!0,e.autoplay&&e.play(0,b._offset,b._length)):window.setTimeout(c,300)},d={autoplay:this.autoplay,loop:this.loop,volume:this._volume,spatialSound:this.spatialSound,maxDistance:this.maxDistance,useCustomAttenuation:this.useCustomAttenuation,rolloffFactor:this.rolloffFactor,refDistance:this.refDistance,distanceModel:this.distanceModel},e=new a(this.name+"_cloned",new ArrayBuffer(0),this._scene,null,d);return this.useCustomAttenuation&&e.setAttenuationFunction(this._customAttenuationFunction),e.setPosition(this._position),e.setPlaybackRate(this._playbackRate),c(),e},a.prototype.getAudioBuffer=function(){return this._audioBuffer},a.prototype.getSoundSource=function(){return this._soundSource},a.prototype.getSoundGain=function(){return this._soundGain},a.prototype.serialize=function(){var a={name:this.name,url:this.name,autoplay:this.autoplay,loop:this.loop,volume:this._volume,spatialSound:this.spatialSound,maxDistance:this.maxDistance,rolloffFactor:this.rolloffFactor,refDistance:this.refDistance,distanceModel:this.distanceModel,playbackRate:this._playbackRate,panningModel:this._panningModel,soundTrackId:this.soundTrackId,metadata:this.metadata};return this.spatialSound&&(this._connectedTransformNode&&(a.connectedMeshId=this._connectedTransformNode.id),a.position=this._position.asArray(),a.refDistance=this.refDistance,a.distanceModel=this.distanceModel,a.isDirectional=this._isDirectional,a.localDirectionToMesh=this._localDirection.asArray(),a.coneInnerAngle=this._coneInnerAngle,a.coneOuterAngle=this._coneOuterAngle,a.coneOuterGain=this._coneOuterGain),a},a.Parse=function(b,c,d,e){var f=b.name;d=b.url?d+b.url:d+f;var h,i={autoplay:b.autoplay,loop:b.loop,volume:b.volume,spatialSound:b.spatialSound,maxDistance:b.maxDistance,rolloffFactor:b.rolloffFactor,refDistance:b.refDistance,distanceModel:b.distanceModel,playbackRate:b.playbackRate};if(e){var j=function(){e._isReadyToPlay?(h._audioBuffer=e.getAudioBuffer(),h._isReadyToPlay=!0,h.autoplay&&h.play(0,h._offset,h._length)):window.setTimeout(j,300)};h=new a(f,new ArrayBuffer(0),c,null,i),j()}else h=new a(f,d,c,function(){c._removePendingData(h)},i),c._addPendingData(h);if(b.position){f=g.e.FromArray(b.position);h.setPosition(f)}if(b.isDirectional&&(h.setDirectionalCone(b.coneInnerAngle||360,b.coneOuterAngle||360,b.coneOuterGain||0),b.localDirectionToMesh)){d=g.e.FromArray(b.localDirectionToMesh);h.setLocalDirectionToMesh(d)}if(b.connectedMeshId){i=c.getMeshById(b.connectedMeshId);i&&h.attachToMesh(i)}return b.metadata&&(h.metadata=b.metadata),h},a._SceneComponentInitialization=function(a){throw Object(La.a)("AudioSceneComponent")},a}(),Na=function(){function a(a,b){void 0===b&&(b={}),this.id=-1,this._isInitialized=!1,this._scene=a,this.soundCollection=new Array,this._options=b,!this._options.mainTrack&&this._scene.soundTracks&&(this._scene.soundTracks.push(this),this.id=this._scene.soundTracks.length-1)}return a.prototype._initializeSoundTrackAudioGraph=function(){var a;(null===(a=S.a.audioEngine)||void 0===a?void 0:a.canUseWebAudio)&&S.a.audioEngine.audioContext&&(this._outputAudioNode=S.a.audioEngine.audioContext.createGain(),this._outputAudioNode.connect(S.a.audioEngine.masterGain),this._options&&this._options.volume&&(this._outputAudioNode.gain.value=this._options.volume),this._isInitialized=!0)},a.prototype.dispose=function(){if(S.a.audioEngine&&S.a.audioEngine.canUseWebAudio){for(this._connectedAnalyser&&this._connectedAnalyser.stopDebugCanvas();this.soundCollection.length;)this.soundCollection[0].dispose();this._outputAudioNode&&this._outputAudioNode.disconnect(),this._outputAudioNode=null}},a.prototype.addSound=function(a){var b;this._isInitialized||this._initializeSoundTrackAudioGraph(),(null===(b=S.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._outputAudioNode&&a.connectToSoundTrackAudioNode(this._outputAudioNode),a.soundTrackId&&(-1===a.soundTrackId?this._scene.mainSoundTrack.removeSound(a):this._scene.soundTracks&&this._scene.soundTracks[a.soundTrackId].removeSound(a)),this.soundCollection.push(a),a.soundTrackId=this.id},a.prototype.removeSound=function(a){a=this.soundCollection.indexOf(a);-1!==a&&this.soundCollection.splice(a,1)},a.prototype.setVolume=function(a){var b;(null===(b=S.a.audioEngine)||void 0===b?void 0:b.canUseWebAudio)&&this._outputAudioNode&&(this._outputAudioNode.gain.value=a)},a.prototype.switchPanningModelToHRTF=function(){var a;if(null===(a=S.a.audioEngine)||void 0===a?void 0:a.canUseWebAudio)for(a=0;a0?b.activeCameras[0]:b.activeCamera){this._cachedCameraPosition.equals(d.globalPosition)||(this._cachedCameraPosition.copyFrom(d.globalPosition),c.audioContext.listener.setPosition(d.globalPosition.x,d.globalPosition.y,d.globalPosition.z)),d.rigCameras&&d.rigCameras.length>0&&(d=d.rigCameras[0]);d=g.a.Invert(d.getViewMatrix());d=g.e.TransformNormal(a._CameraDirection,d);d.normalize(),isNaN(d.x)||isNaN(d.y)||isNaN(d.z)||this._cachedCameraDirection.equals(d)||(this._cachedCameraDirection.copyFrom(d),c.audioContext.listener.setOrientation(d.x,d.y,d.z,0,1,0))}else c.audioContext.listener.setPosition(0,0,0)}for(d=0;d0?1/a:0,e=0;e0},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){var b=this;this._attachedCamera=a;var c=this._attachedCamera.getScene();this._onPrePointerObservableObserver=c.onPrePointerObservable.add(function(a){a.type!==Ua.a.POINTERDOWN?a.type===Ua.a.POINTERUP&&(b._isPointerDown=!1):b._isPointerDown=!0}),this._onAfterCheckInputsObserver=a.onAfterCheckInputsObservable.add(function(){var a=P.a.Now,c=0;null!=b._lastFrameTime&&(c=a-b._lastFrameTime),b._lastFrameTime=a,b._applyUserInteraction();a=a-b._lastInteractionTime-b._idleRotationWaitTime;a=Math.max(Math.min(a/b._idleRotationSpinupTime,1),0);b._cameraRotationSpeed=b._idleRotationSpeed*a,b._attachedCamera&&(b._attachedCamera.alpha-=b._cameraRotationSpeed*(c/1e3))})},a.prototype.detach=function(){if(this._attachedCamera){var a=this._attachedCamera.getScene();this._onPrePointerObservableObserver&&a.onPrePointerObservable.remove(this._onPrePointerObservableObserver),this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._attachedCamera=null}},a.prototype._userIsZooming=function(){return!!this._attachedCamera&&0!==this._attachedCamera.inertialRadiusOffset},a.prototype._shouldAnimationStopForInteraction=function(){if(!this._attachedCamera)return!1;var a=!1;return this._lastFrameRadius===this._attachedCamera.radius&&0!==this._attachedCamera.inertialRadiusOffset&&(a=!0),this._lastFrameRadius=this._attachedCamera.radius,this._zoomStopsAnimation?a:this._userIsZooming()},a.prototype._applyUserInteraction=function(){this._userIsMoving()&&!this._shouldAnimationStopForInteraction()&&(this._lastInteractionTime=P.a.Now)},a.prototype._userIsMoving=function(){return!!this._attachedCamera&&(0!==this._attachedCamera.inertialAlphaOffset||0!==this._attachedCamera.inertialBetaOffset||0!==this._attachedCamera.inertialRadiusOffset||0!==this._attachedCamera.inertialPanningX||0!==this._attachedCamera.inertialPanningY||this._isPointerDown)},a}(),Wa=function(){function a(){this.transitionDuration=450,this.lowerRadiusTransitionRange=2,this.upperRadiusTransitionRange=-2,this._autoTransitionRange=!1,this._radiusIsAnimating=!1,this._radiusBounceTransition=null,this._animatables=new Array}return Object.defineProperty(a.prototype,"name",{get:function(){return"Bouncing"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"autoTransitionRange",{get:function(){return this._autoTransitionRange},set:function(a){var b=this;if(this._autoTransitionRange!==a){this._autoTransitionRange=a;var c=this._attachedCamera;c&&(a?this._onMeshTargetChangedObserver=c.onMeshTargetChangedObservable.add(function(a){if(a){a.computeWorldMatrix(!0);a=a.getBoundingInfo().diagonalLength;b.lowerRadiusTransitionRange=.05*a,b.upperRadiusTransitionRange=.05*a}}):this._onMeshTargetChangedObserver&&c.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver))}},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){var b=this;this._attachedCamera=a,this._onAfterCheckInputsObserver=a.onAfterCheckInputsObservable.add(function(){b._attachedCamera&&(b._isRadiusAtLimit(b._attachedCamera.lowerRadiusLimit)&&b._applyBoundRadiusAnimation(b.lowerRadiusTransitionRange),b._isRadiusAtLimit(b._attachedCamera.upperRadiusLimit)&&b._applyBoundRadiusAnimation(b.upperRadiusTransitionRange))})},a.prototype.detach=function(){this._attachedCamera&&(this._onAfterCheckInputsObserver&&this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._onMeshTargetChangedObserver&&this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver),this._attachedCamera=null)},a.prototype._isRadiusAtLimit=function(a){return!!this._attachedCamera&&this._attachedCamera.radius===a&&!this._radiusIsAnimating},a.prototype._applyBoundRadiusAnimation=function(b){var c=this;if(this._attachedCamera){this._radiusBounceTransition||(a.EasingFunction.setEasingMode(a.EasingMode),this._radiusBounceTransition=N.CreateAnimation("radius",N.ANIMATIONTYPE_FLOAT,60,a.EasingFunction)),this._cachedWheelPrecision=this._attachedCamera.wheelPrecision,this._attachedCamera.wheelPrecision=1/0,this._attachedCamera.inertialRadiusOffset=0,this.stopAllAnimations(),this._radiusIsAnimating=!0;b=N.TransitionTo("radius",this._attachedCamera.radius+b,this._attachedCamera,this._attachedCamera.getScene(),60,this._radiusBounceTransition,this.transitionDuration,function(){return c._clearAnimationLocks()});b&&this._animatables.push(b)}},a.prototype._clearAnimationLocks=function(){this._radiusIsAnimating=!1,this._attachedCamera&&(this._attachedCamera.wheelPrecision=this._cachedWheelPrecision)},a.prototype.stopAllAnimations=function(){for(this._attachedCamera&&(this._attachedCamera.animations=[]);this._animatables.length;)this._animatables[0].onAnimationEnd=null,this._animatables[0].stop(),this._animatables.shift()},a.EasingFunction=new sa(.3),a.EasingMode=qa.EASINGMODE_EASEOUT,a}(),Xa=function(){function a(){this._mode=a.FitFrustumSidesMode,this._radiusScale=1,this._positionScale=.5,this._defaultElevation=.3,this._elevationReturnTime=1500,this._elevationReturnWaitTime=1e3,this._zoomStopsAnimation=!1,this._framingTime=1500,this.autoCorrectCameraLimitsAndSensibility=!0,this._isPointerDown=!1,this._lastInteractionTime=-1/0,this._animatables=new Array,this._betaIsAnimating=!1}return Object.defineProperty(a.prototype,"name",{get:function(){return"Framing"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"mode",{get:function(){return this._mode},set:function(a){this._mode=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"radiusScale",{get:function(){return this._radiusScale},set:function(a){this._radiusScale=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"positionScale",{get:function(){return this._positionScale},set:function(a){this._positionScale=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"defaultElevation",{get:function(){return this._defaultElevation},set:function(a){this._defaultElevation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"elevationReturnTime",{get:function(){return this._elevationReturnTime},set:function(a){this._elevationReturnTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"elevationReturnWaitTime",{get:function(){return this._elevationReturnWaitTime},set:function(a){this._elevationReturnWaitTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"zoomStopsAnimation",{get:function(){return this._zoomStopsAnimation},set:function(a){this._zoomStopsAnimation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"framingTime",{get:function(){return this._framingTime},set:function(a){this._framingTime=a},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(b){var c=this;this._attachedCamera=b;var d=this._attachedCamera.getScene();a.EasingFunction.setEasingMode(a.EasingMode),this._onPrePointerObservableObserver=d.onPrePointerObservable.add(function(a){a.type!==Ua.a.POINTERDOWN?a.type===Ua.a.POINTERUP&&(c._isPointerDown=!1):c._isPointerDown=!0}),this._onMeshTargetChangedObserver=b.onMeshTargetChangedObservable.add(function(a){a&&c.zoomOnMesh(a)}),this._onAfterCheckInputsObserver=b.onAfterCheckInputsObservable.add(function(){c._applyUserInteraction(),c._maintainCameraAboveGround()})},a.prototype.detach=function(){if(this._attachedCamera){var a=this._attachedCamera.getScene();this._onPrePointerObservableObserver&&a.onPrePointerObservable.remove(this._onPrePointerObservableObserver),this._onAfterCheckInputsObserver&&this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver),this._onMeshTargetChangedObserver&&this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver),this._attachedCamera=null}},a.prototype.zoomOnMesh=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null),a.computeWorldMatrix(!0);a=a.getBoundingInfo().boundingBox;this.zoomOnBoundingInfo(a.minimumWorld,a.maximumWorld,b,c)},a.prototype.zoomOnMeshHierarchy=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null),a.computeWorldMatrix(!0);a=a.getHierarchyBoundingVectors(!0);this.zoomOnBoundingInfo(a.min,a.max,b,c)},a.prototype.zoomOnMeshesHierarchy=function(a,b,c){void 0===b&&(b=!1),void 0===c&&(c=null);for(var d=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),e=new g.e(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),f=0;fd.upperRadiusLimit?d.upperRadiusLimit:b),b):0},a.prototype._maintainCameraAboveGround=function(){var b=this;if(!(this._elevationReturnTime<0)){var c=P.a.Now-this._lastInteractionTime,d=.5*Math.PI-this._defaultElevation,e=.5*Math.PI;if(this._attachedCamera&&!this._betaIsAnimating&&this._attachedCamera.beta>e&&c>=this._elevationReturnWaitTime){this._betaIsAnimating=!0,this.stopAllAnimations(),this._betaTransition||(this._betaTransition=N.CreateAnimation("beta",N.ANIMATIONTYPE_FLOAT,60,a.EasingFunction));e=N.TransitionTo("beta",d,this._attachedCamera,this._attachedCamera.getScene(),60,this._betaTransition,this._elevationReturnTime,function(){b._clearAnimationLocks(),b.stopAllAnimations()});e&&this._animatables.push(e)}}},a.prototype._getFrustumSlope=function(){var a=this._attachedCamera;if(!a)return g.d.Zero();var b=a.getScene().getEngine().getAspectRatio(a);a=Math.tan(a.fov/2);b=a*b;return new g.d(b,a)},a.prototype._clearAnimationLocks=function(){this._betaIsAnimating=!1},a.prototype._applyUserInteraction=function(){this.isUserIsMoving&&(this._lastInteractionTime=P.a.Now,this.stopAllAnimations(),this._clearAnimationLocks())},a.prototype.stopAllAnimations=function(){for(this._attachedCamera&&(this._attachedCamera.animations=[]);this._animatables.length;)this._animatables[0]&&(this._animatables[0].onAnimationEnd=null,this._animatables[0].stop()),this._animatables.shift()},Object.defineProperty(a.prototype,"isUserIsMoving",{get:function(){return!!this._attachedCamera&&(0!==this._attachedCamera.inertialAlphaOffset||0!==this._attachedCamera.inertialBetaOffset||0!==this._attachedCamera.inertialRadiusOffset||0!==this._attachedCamera.inertialPanningX||0!==this._attachedCamera.inertialPanningY||this._isPointerDown)},enumerable:!1,configurable:!0}),a.EasingFunction=new wa,a.EasingMode=qa.EASINGMODE_EASEINOUT,a.IgnoreBoundsSizeMode=0,a.FitFrustumSidesMode=1,a}(),Ya=function(a,b,c,d){void 0===b&&(b=new g.e),void 0===c&&(c=0),void 0===d&&(d=!1),this.direction=a,this.rotatedDirection=b,this.diff=c,this.ignore=d},Za=function(){function a(a){this.ui=a,this.name="AttachToBoxBehavior",this.distanceAwayFromFace=.15,this.distanceAwayFromBottomOfFace=.15,this._faceVectors=[new Ya(g.e.Up()),new Ya(g.e.Down()),new Ya(g.e.Left()),new Ya(g.e.Right()),new Ya(g.e.Forward()),new Ya(g.e.Forward().scaleInPlace(-1))],this._tmpMatrix=new g.a,this._tmpVector=new g.e,this._zeroVector=g.e.Zero(),this._lookAtTmpMatrix=new g.a}return a.prototype.init=function(){},a.prototype._closestFace=function(a){var b=this;return this._faceVectors.forEach(function(c){b._target.rotationQuaternion||(b._target.rotationQuaternion=g.b.RotationYawPitchRoll(b._target.rotation.y,b._target.rotation.x,b._target.rotation.z)),b._target.rotationQuaternion.toRotationMatrix(b._tmpMatrix),g.e.TransformCoordinatesToRef(c.direction,b._tmpMatrix,c.rotatedDirection),c.diff=g.e.GetAngleBetweenVectors(c.rotatedDirection,a,g.e.Cross(c.rotatedDirection,a))}),this._faceVectors.reduce(function(a,b){return a.ignore?b:b.ignore||a.diff1)return a._setAllVisibility(a._ownerNode,1),void (a._hoverValue=a.fadeInTime+a.delay);if(a._ownerNode.visibility<0&&(a._setAllVisibility(a._ownerNode,0),a._hoverValue<0))return void (a._hoverValue=0);setTimeout(a._update,a._millisecondsPerFrame)}}}return Object.defineProperty(a.prototype,"name",{get:function(){return"FadeInOut"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a){this._ownerNode=a,this._setAllVisibility(this._ownerNode,0)},a.prototype.detach=function(){this._ownerNode=null},a.prototype.fadeIn=function(a){this._hovered=a,this._update()},a.prototype._setAllVisibility=function(a,b){var c=this;a.visibility=b,a.getChildMeshes().forEach(function(a){c._setAllVisibility(a,b)})},a}(),ab=c(72),bb=function(){function a(){this._startDistance=0,this._initialScale=new g.e(0,0,0),this._targetScale=new g.e(0,0,0),this._sceneRenderObserver=null,this._dragBehaviorA=new ab.a({}),this._dragBehaviorA.moveAttached=!1,this._dragBehaviorB=new ab.a({}),this._dragBehaviorB.moveAttached=!1}return Object.defineProperty(a.prototype,"name",{get:function(){return"MultiPointerScale"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype._getCurrentDistance=function(){return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length()},a.prototype.attach=function(a){var b=this;this._ownerNode=a,this._dragBehaviorA.onDragStartObservable.add(function(c){b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging&&(b._dragBehaviorA.currentDraggingPointerId==b._dragBehaviorB.currentDraggingPointerId?b._dragBehaviorA.releaseDrag():(b._initialScale.copyFrom(a.scaling),b._startDistance=b._getCurrentDistance()))}),this._dragBehaviorB.onDragStartObservable.add(function(c){b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging&&(b._dragBehaviorA.currentDraggingPointerId==b._dragBehaviorB.currentDraggingPointerId?b._dragBehaviorB.releaseDrag():(b._initialScale.copyFrom(a.scaling),b._startDistance=b._getCurrentDistance()))}),[this._dragBehaviorA,this._dragBehaviorB].forEach(function(a){a.onDragObservable.add(function(){if(b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging){var a=b._getCurrentDistance()/b._startDistance;b._initialScale.scaleToRef(a,b._targetScale)}})}),a.addBehavior(this._dragBehaviorA),a.addBehavior(this._dragBehaviorB),this._sceneRenderObserver=a.getScene().onBeforeRenderObservable.add(function(){if(b._dragBehaviorA.dragging&&b._dragBehaviorB.dragging){var c=b._targetScale.subtract(a.scaling).scaleInPlace(.1);c.length()>.01&&a.scaling.addInPlace(c)}})},a.prototype.detach=function(){var a=this;this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver),[this._dragBehaviorA,this._dragBehaviorB].forEach(function(b){b.onDragStartObservable.clear(),b.onDragObservable.clear(),a._ownerNode.removeBehavior(b)})},a}(),cb=c(33),db=c(28),eb=function(){function a(){this._attachedToElement=!1,this._virtualMeshesInfo={},this._tmpVector=new g.e,this._tmpQuaternion=new g.b,this._dragType={NONE:0,DRAG:1,DRAG_WITH_CONTROLLER:2,NEAR_DRAG:3},this._moving=!1,this._dragging=this._dragType.NONE,this.draggableMeshes=null,this.zDragFactor=3,this.currentDraggingPointerIds=[],this.detachCameraControls=!0,this.onDragStartObservable=new f.c,this.onDragObservable=new f.c,this.onDragEndObservable=new f.c,this.allowMultiPointer=!0}return Object.defineProperty(a.prototype,"currentDraggingPointerId",{get:function(){return void 0!==this.currentDraggingPointerIds[0]?this.currentDraggingPointerIds[0]:-1},set:function(a){this.currentDraggingPointerIds[0]=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentDraggingPointerID",{get:function(){return this.currentDraggingPointerId},set:function(a){this.currentDraggingPointerId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"name",{get:function(){return"BaseSixDofDrag"},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isMoving",{get:function(){return this._moving},enumerable:!1,configurable:!0}),a.prototype.init=function(){},Object.defineProperty(a.prototype,"_pointerCamera",{get:function(){return this._scene.cameraToUseForPointers?this._scene.cameraToUseForPointers:this._scene.activeCamera},enumerable:!1,configurable:!0}),a.prototype._createVirtualMeshInfo=function(){var b=new cb.a("",a._virtualScene);b.rotationQuaternion=new g.b;var c=new cb.a("",a._virtualScene);c.rotationQuaternion=new g.b;var d=new cb.a("",a._virtualScene);return d.rotationQuaternion=new g.b,{dragging:!1,moving:!1,dragMesh:b,originMesh:c,pivotMesh:d,startingPivotPosition:new g.e,startingPivotOrientation:new g.b,startingPosition:new g.e,startingOrientation:new g.b,lastOriginPosition:new g.e,lastDragPosition:new g.e}},a.prototype._resetVirtualMeshesPosition=function(){for(var a=0;a0)return;!c._pointerCamera||c._pointerCamera.cameraRigMode!==db.a.RIG_MODE_NONE||c._pointerCamera._isLeftCamera||c._pointerCamera._isRightCamera||a.pickInfo.ray.origin.copyFrom(c._pointerCamera.globalPosition),c._ownerNode.computeWorldMatrix(!0);d=c._virtualMeshesInfo[b];f?(c._dragging=a.pickInfo.originMesh?c._dragType.NEAR_DRAG:c._dragType.DRAG_WITH_CONTROLLER,d.originMesh.position.copyFrom(a.pickInfo.aimTransform.position),c._dragging===c._dragType.NEAR_DRAG&&a.pickInfo.gripTransform?d.originMesh.rotationQuaternion.copyFrom(a.pickInfo.gripTransform.rotationQuaternion):d.originMesh.rotationQuaternion.copyFrom(a.pickInfo.aimTransform.rotationQuaternion)):(c._dragging=c._dragType.DRAG,d.originMesh.position.copyFrom(a.pickInfo.ray.origin)),d.lastOriginPosition.copyFrom(d.originMesh.position),d.dragMesh.position.copyFrom(a.pickInfo.pickedPoint),d.lastDragPosition.copyFrom(a.pickInfo.pickedPoint),d.pivotMesh.position.copyFrom(c._ownerNode.getAbsolutePivotPoint()),d.pivotMesh.rotationQuaternion.copyFrom(c._ownerNode.absoluteRotationQuaternion),d.startingPosition.copyFrom(d.dragMesh.position),d.startingPivotPosition.copyFrom(d.pivotMesh.position),d.startingOrientation.copyFrom(d.dragMesh.rotationQuaternion),d.startingPivotOrientation.copyFrom(d.pivotMesh.rotationQuaternion),f?(d.originMesh.addChild(d.dragMesh),d.originMesh.addChild(d.pivotMesh)):d.originMesh.lookAt(d.dragMesh.position),d.dragging=!0,-1===c.currentDraggingPointerIds.indexOf(b)&&c.currentDraggingPointerIds.push(b),c.detachCameraControls&&c._pointerCamera&&!c._pointerCamera.leftCamera&&(c._pointerCamera.inputs&&c._pointerCamera.inputs.attachedToElement?(c._pointerCamera.detachControl(),c._attachedToElement=!0):c._attachedToElement=!1),c._targetDragStart(d.pivotMesh.position,d.pivotMesh.rotationQuaternion,b),c.onDragStartObservable.notifyObservers({position:d.pivotMesh.position})}}else if(a.type==Ua.a.POINTERUP||a.type==Ua.a.POINTERDOUBLETAP){d=c.currentDraggingPointerIds.indexOf(b);e.dragging=!1,-1!==d&&(c.currentDraggingPointerIds.splice(d,1),0===c.currentDraggingPointerIds.length&&(c._moving=!1,c._dragging=c._dragType.NONE,c.detachCameraControls&&c._attachedToElement&&c._pointerCamera&&!c._pointerCamera.leftCamera&&(c._pointerCamera.attachControl(!0),c._attachedToElement=!1)),e.originMesh.removeChild(e.dragMesh),e.originMesh.removeChild(e.pivotMesh),c._targetDragEnd(b),c.onDragEndObservable.notifyObservers({}))}else if(a.type==Ua.a.POINTERMOVE&&(-1!==c.currentDraggingPointerIds.indexOf(b)&&e.dragging&&a.pickInfo&&(a.pickInfo.ray||a.pickInfo.aimTransform))){d=c.zDragFactor;(c.currentDraggingPointerIds.length>1||a.pickInfo.originMesh)&&(d=0),c._ownerNode.computeWorldMatrix(!0),f?c._pointerUpdateXR(a.pickInfo.aimTransform,a.pickInfo.gripTransform,b,d):c._pointerUpdate2D(a.pickInfo.ray,b,d),c._tmpQuaternion.copyFrom(e.startingPivotOrientation),c._tmpQuaternion.x=-c._tmpQuaternion.x,c._tmpQuaternion.y=-c._tmpQuaternion.y,c._tmpQuaternion.z=-c._tmpQuaternion.z,e.pivotMesh.absoluteRotationQuaternion.multiplyToRef(c._tmpQuaternion,c._tmpQuaternion),e.pivotMesh.absolutePosition.subtractToRef(e.startingPivotPosition,c._tmpVector),c.onDragObservable.notifyObservers({delta:c._tmpVector,position:e.pivotMesh.position,pickInfo:a.pickInfo}),c._targetDrag(c._tmpVector,c._tmpQuaternion,b),e.lastDragPosition.copyFrom(e.dragMesh.absolutePosition),c._moving=!0}})},a.prototype._applyZOffset=function(a,b,c){a.position.z-=a.position.z<1?b*c:b*c*a.position.z,a.position.z<0&&(a.position.z=0)},a.prototype._targetDragStart=function(a,b,c){},a.prototype._targetDrag=function(a,b,c){},a.prototype._targetDragEnd=function(a){},a.prototype.detach=function(){for(var a in this._scene&&(this.detachCameraControls&&this._attachedToElement&&this._pointerCamera&&!this._pointerCamera.leftCamera&&(this._pointerCamera.attachControl(!0),this._attachedToElement=!1),this._scene.onPointerObservable.remove(this._pointerObserver)),this._virtualMeshesInfo)this._virtualMeshesInfo[a].originMesh.dispose(),this._virtualMeshesInfo[a].dragMesh.dispose();this.onDragEndObservable.clear(),this.onDragObservable.clear(),this.onDragStartObservable.clear()},a}(),fb=c(46),gb=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._sceneRenderObserver=null,b._targetPosition=new g.e(0,0,0),b._targetOrientation=new g.b,b._targetScaling=new g.e(1,1,1),b._startingPosition=new g.e(0,0,0),b._startingOrientation=new g.b,b._startingScaling=new g.e(1,1,1),b.onPositionChangedObservable=new f.c,b.dragDeltaRatio=.2,b.rotateDraggedObject=!0,b.rotateAroundYOnly=!1,b.rotateWithMotionController=!0,b.disableMovement=!1,b.faceCameraOnDragStart=!1,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"name",{get:function(){return"SixDofDrag"},enumerable:!1,configurable:!0}),b.prototype.attach=function(b){var c=this;a.prototype.attach.call(this,b),b.isNearGrabbable=!0,this._virtualTransformNode=new fb.a("virtual_sixDof",eb._virtualScene),this._virtualTransformNode.rotationQuaternion=g.b.Identity(),this._sceneRenderObserver=b.getScene().onBeforeRenderObservable.add(function(){if(1===c.currentDraggingPointerIds.length&&c._moving&&!c.disableMovement){var a=b.parent;b.setParent(null),b.position.addInPlace(c._targetPosition.subtract(b.position).scale(c.dragDeltaRatio)),c.onPositionChangedObservable.notifyObservers({position:b.absolutePosition}),(!a||a.scaling&&!a.scaling.isNonUniformWithinEpsilon(.001))&&g.b.SlerpToRef(b.rotationQuaternion,c._targetOrientation,c.dragDeltaRatio,b.rotationQuaternion),b.setParent(a)}})},b.prototype._getPositionOffsetAround=function(a,b,c){var d=g.c.Matrix[0],e=g.c.Matrix[1],f=g.c.Matrix[2],h=g.c.Matrix[3],i=g.c.Matrix[4];return g.a.TranslationToRef(a.x,a.y,a.z,d),g.a.TranslationToRef(-a.x,-a.y,-a.z,e),g.a.FromQuaternionToRef(c,f),g.a.ScalingToRef(b,b,b,h),e.multiplyToRef(f,i),i.multiplyToRef(h,i),i.multiplyToRef(d,i),i.getTranslation()},b.prototype._onePointerPositionUpdated=function(a,b){g.c.Vector3[0].setAll(0),this._dragging===this._dragType.DRAG?this.rotateDraggedObject&&(this.rotateAroundYOnly?g.b.RotationYawPitchRollToRef(b.toEulerAngles().y,0,0,g.c.Quaternion[0]):g.c.Quaternion[0].copyFrom(b),g.c.Quaternion[0].multiplyToRef(this._startingOrientation,this._targetOrientation)):(this._dragging===this._dragType.NEAR_DRAG||this._dragging===this._dragType.DRAG_WITH_CONTROLLER&&this.rotateWithMotionController)&&b.multiplyToRef(this._startingOrientation,this._targetOrientation),this._targetPosition.copyFrom(this._startingPosition).addInPlace(a)},b.prototype._twoPointersPositionUpdated=function(){var a=this._virtualMeshesInfo[this.currentDraggingPointerIds[0]].startingPosition,b=this._virtualMeshesInfo[this.currentDraggingPointerIds[1]].startingPosition,c=g.c.Vector3[0];a.addToRef(b,c),c.scaleInPlace(.5);var d=g.c.Vector3[1];b.subtractToRef(a,d);b=this._virtualMeshesInfo[this.currentDraggingPointerIds[0]].dragMesh.absolutePosition;a=this._virtualMeshesInfo[this.currentDraggingPointerIds[1]].dragMesh.absolutePosition;var e=g.c.Vector3[2];b.addToRef(a,e),e.scaleInPlace(.5);var f=g.c.Vector3[3];a.subtractToRef(b,f);a=f.length()/d.length();b=e.subtract(c);e=g.b.FromEulerAngles(0,g.e.GetAngleBetweenVectorsOnPlane(d.normalize(),f.normalize(),g.e.UpReadOnly),0);d=this._ownerNode.parent;this._ownerNode.setParent(null);f=this._getPositionOffsetAround(c.subtract(this._virtualTransformNode.getAbsolutePivotPoint()),a,e);this._virtualTransformNode.rotationQuaternion.multiplyToRef(e,this._ownerNode.rotationQuaternion),this._virtualTransformNode.scaling.scaleToRef(a,this._ownerNode.scaling),this._virtualTransformNode.position.addToRef(b.addInPlace(f),this._ownerNode.position),this.onPositionChangedObservable.notifyObservers({position:this._ownerNode.position}),this._ownerNode.setParent(d)},b.prototype._targetDragStart=function(){var a=this.currentDraggingPointerIds.length,b=this._ownerNode.parent;this._ownerNode.rotationQuaternion||(this._ownerNode.rotationQuaternion=g.b.RotationYawPitchRoll(this._ownerNode.rotation.y,this._ownerNode.rotation.x,this._ownerNode.rotation.z));var c=this._ownerNode.getAbsolutePivotPoint();if(this._ownerNode.setParent(null),1===a){if(this._targetPosition.copyFrom(this._ownerNode.position),this._targetOrientation.copyFrom(this._ownerNode.rotationQuaternion),this._targetScaling.copyFrom(this._ownerNode.scaling),this.faceCameraOnDragStart&&this._scene.activeCamera){var d=g.c.Vector3[0];this._scene.activeCamera.position.subtractToRef(c,d),d.normalize();var e=g.c.Quaternion[0];this._scene.useRightHandedSystem?g.b.FromLookDirectionRHToRef(d,new g.e(0,1,0),e):g.b.FromLookDirectionLHToRef(d,new g.e(0,1,0),e),e.normalize(),g.b.RotationYawPitchRollToRef(e.toEulerAngles().y,0,0,g.c.Quaternion[0]),this._targetOrientation.copyFrom(g.c.Quaternion[0])}this._startingPosition.copyFrom(this._targetPosition),this._startingOrientation.copyFrom(this._targetOrientation),this._startingScaling.copyFrom(this._targetScaling)}else 2===a&&(this._virtualTransformNode.setPivotPoint(new g.e(0,0,0),Q.c.LOCAL),this._virtualTransformNode.position.copyFrom(this._ownerNode.position),this._virtualTransformNode.scaling.copyFrom(this._ownerNode.scaling),this._virtualTransformNode.rotationQuaternion.copyFrom(this._ownerNode.rotationQuaternion),this._virtualTransformNode.setPivotPoint(c,Q.c.WORLD),this._resetVirtualMeshesPosition());this._ownerNode.setParent(b)},b.prototype._targetDrag=function(a,b,c){1===this.currentDraggingPointerIds.length?this._onePointerPositionUpdated(a,b):2===this.currentDraggingPointerIds.length&&this._twoPointersPositionUpdated()},b.prototype._targetDragEnd=function(){if(1===this.currentDraggingPointerIds.length){this._resetVirtualMeshesPosition();var a=this.faceCameraOnDragStart;this.faceCameraOnDragStart=!1,this._targetDragStart(),this.faceCameraOnDragStart=a}},b.prototype.detach=function(){a.prototype.detach.call(this),this._ownerNode&&(this._ownerNode.isNearGrabbable=!1,this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver)),this._virtualTransformNode&&this._virtualTransformNode.dispose()},b}(eb),hb=function(){function a(){this._attachPointLocalOffset=new g.e,this._workingPosition=new g.e,this._workingQuaternion=new g.b,this._lastTick=-1,this._hit=!1,this.hitNormalOffset=.05,this.meshes=[],this.interpolatePose=!0,this.lerpTime=250,this.keepOrientationVertical=!0,this.enabled=!0,this.maxStickingDistance=.8}return Object.defineProperty(a.prototype,"name",{get:function(){return"SurfaceMagnetism"},enumerable:!1,configurable:!0}),a.prototype.init=function(){},a.prototype.attach=function(a,b){this._attachedMesh=a,this._scene=b||a.getScene(),this._attachedMesh.rotationQuaternion||(this._attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this._attachedMesh.rotation.y,this._attachedMesh.rotation.x,this._attachedMesh.rotation.z)),this.updateAttachPoint(),this._workingPosition.copyFrom(this._attachedMesh.position),this._workingQuaternion.copyFrom(this._attachedMesh.rotationQuaternion),this._addObservables()},a.prototype.detach=function(){this._attachedMesh=null,this._removeObservables()},a.prototype._getTargetPose=function(a){if(!this._attachedMesh)return null;if(a&&a.hit){var b=a.getNormal(!0,!0);a=a.pickedPoint;if(!b||!a)return null;b.normalize();var c=g.c.Vector3[0];return c.copyFrom(b),c.scaleInPlace(this.hitNormalOffset),c.addInPlace(a),this._attachedMesh.parent&&(g.c.Matrix[0].copyFrom(this._attachedMesh.parent.getWorldMatrix()).invert(),g.e.TransformNormalToRef(c,g.c.Matrix[0],c)),{position:c,quaternion:g.b.RotationYawPitchRoll(-Math.atan2(b.x,-b.z),this.keepOrientationVertical?0:Math.atan2(b.y,Math.sqrt(b.z*b.z+b.x*b.x)),0)}}return null},a.prototype.updateAttachPoint=function(){this._getAttachPointOffsetToRef(this._attachPointLocalOffset)},a.prototype.findAndUpdateTarget=function(a){if(this._hit=!1,!a.ray)return!1;a=a.ray.intersectsMeshes(this.meshes)[0];if(this._attachedMesh&&a&&a.hit&&a.pickedMesh){a=this._getTargetPose(a);a&&g.e.Distance(this._attachedMesh.position,a.position)c&&(g.b.RotationAxisToRef(d,-h+c,f),b.rotateByQuaternionToRef(f,b),e=!0)}h=this._angleBetweenVectorAndPlane(b,d)*(this._scene.useRightHandedSystem?-1:1);c=this.maxViewHorizontalDegrees*Math.PI/180*.5;return h<-c?(g.b.RotationAxisToRef(a,-h-c,f),b.rotateByQuaternionToRef(f,b),e=!0):h>c&&(g.b.RotationAxisToRef(a,-h+c,f),b.rotateByQuaternionToRef(f,b),e=!0),e},a.prototype._orientationClamp=function(a,b){var c=this._tmpVectors[0];c.copyFrom(a).scaleInPlace(-1).normalize();a=this._tmpVectors[1];var d=this._tmpVectors[2];a.copyFromFloats(0,1,0),g.e.CrossToRef(c,a,d);var e=d.length();ethis.orientToCameraDeadzoneDegrees},a.prototype._updateLeashing=function(a){if(this.attachedNode){var b=this.attachedNode.parent;this.attachedNode.setParent(null);var c=this.attachedNode.getWorldMatrix(),d=this._workingPosition,e=this._workingQuaternion,f=this.attachedNode.getPivotPoint(),h=this._tmpInvertView;h.copyFrom(a.getViewMatrix()),h.invert(),g.e.TransformCoordinatesToRef(f,c,d);var i=this._tmpPosition;i.copyFromFloats(0,0,0),g.e.TransformCoordinatesToRef(i,c,i),i.scaleInPlace(-1).subtractInPlace(f),d.subtractInPlace(a.globalPosition),this.ignoreCameraPitchAndRoll&&this._applyPitchOffset(h);var j=!1,k=this._tmpForward;k.copyFromFloats(0,0,this._scene.useRightHandedSystem?-1:1),g.e.TransformNormalToRef(k,h,k);var l=this._tmpNodeForward;if(l.copyFromFloats(0,0,this._scene.useRightHandedSystem?-1:1),g.e.TransformNormalToRef(l,c,l),this._recenterNextUpdate)d.copyFrom(k).scaleInPlace(this.defaultDistance);else if(this.ignoreAngleClamp){c=d.length();d.copyFrom(k).scaleInPlace(c)}else j=this._angularClamp(h,d);k=!1;this.ignoreDistanceClamp||(k=this._distanceClamp(d,j),this._applyVerticalClamp(d)),this.useFixedVerticalOffset&&(d.y=i.y-a.globalPosition.y+this.fixedVerticalOffset),(j||k||this._passedOrientationDeadzone(d,l)||this._recenterNextUpdate)&&this._orientationClamp(d,e),this._workingPosition.subtractInPlace(f),this._recenterNextUpdate=!1,this.attachedNode.setParent(b)}},a.prototype._updateTransformToGoal=function(a){if(this.attachedNode&&this.followedCamera&&this._enabled){this.attachedNode.rotationQuaternion||(this.attachedNode.rotationQuaternion=g.b.Identity());var b=this.attachedNode.parent;if(this.attachedNode.setParent(null),!this.interpolatePose)return this.attachedNode.position.copyFrom(this.followedCamera.globalPosition).addInPlace(this._workingPosition),void this.attachedNode.rotationQuaternion.copyFrom(this._workingQuaternion);var c=new g.e;c.copyFrom(this.attachedNode.position).subtractInPlace(this.followedCamera.globalPosition),g.e.SmoothToRef(c,this._workingPosition,a,this.lerpTime,c),c.addInPlace(this.followedCamera.globalPosition),this.attachedNode.position.copyFrom(c);c=new g.b;c.copyFrom(this.attachedNode.rotationQuaternion),g.b.SmoothToRef(c,this._workingQuaternion,a,this.lerpTime,this.attachedNode.rotationQuaternion),this.attachedNode.setParent(b)}},a.prototype._addObservables=function(){var a=this;this._lastTick=Date.now(),this._onBeforeRender=this._scene.onBeforeRenderObservable.add(function(){if(a.followedCamera){var b=Date.now();a._updateLeashing(a.followedCamera),a._updateTransformToGoal(b-a._lastTick),a._lastTick=b}})},a.prototype._removeObservables=function(){this._onBeforeRender&&this._scene.onBeforeRenderObservable.remove(this._onBeforeRender)},a}(),kb=function(){function a(){}return a.ANCHOR_SYSTEM="xr-anchor-system",a.BACKGROUND_REMOVER="xr-background-remover",a.HIT_TEST="xr-hit-test",a.MESH_DETECTION="xr-mesh-detection",a.PHYSICS_CONTROLLERS="xr-physics-controller",a.PLANE_DETECTION="xr-plane-detection",a.POINTER_SELECTION="xr-controller-pointer-selection",a.TELEPORTATION="xr-controller-teleportation",a.FEATURE_POINTS="xr-feature-points",a.HAND_TRACKING="xr-hand-tracking",a.IMAGE_TRACKING="xr-image-tracking",a.NEAR_INTERACTION="xr-near-interaction",a.DOM_OVERLAY="xr-dom-overlay",a.MOVEMENT="xr-controller-movement",a.LIGHT_ESTIMATION="xr-light-estimation",a.EYE_TRACKING="xr-eye-tracking",a.WALKING_LOCOMOTION="xr-walking-locomotion",a}(),lb=function(){function a(a){var b=this;this._xrSessionManager=a,this._features={},this._xrSessionManager.onXRSessionInit.add(function(){b.getEnabledFeatures().forEach(function(a){var c=b._features[a];!c.enabled||c.featureImplementation.attached||c.featureImplementation.disableAutoAttach||b.attachFeature(a)})}),this._xrSessionManager.onXRSessionEnded.add(function(){b.getEnabledFeatures().forEach(function(a){var c=b._features[a];c.enabled&&c.featureImplementation.attached&&b.detachFeature(a)})})}return a.AddWebXRFeature=function(a,b,c,d){void 0===c&&(c=1),void 0===d&&(d=!1),this._AvailableFeatures[a]=this._AvailableFeatures[a]||{latest:c},c>this._AvailableFeatures[a].latest&&(this._AvailableFeatures[a].latest=c),d&&(this._AvailableFeatures[a].stable=c),this._AvailableFeatures[a][c]=b},a.ConstructFeature=function(a,b,c,d){void 0===b&&(b=1);a=this._AvailableFeatures[a][b];if(!a)throw new Error("feature not found");return a(c,d)},a.GetAvailableFeatures=function(){return Object.keys(this._AvailableFeatures)},a.GetAvailableVersions=function(a){return Object.keys(this._AvailableFeatures[a])},a.GetLatestVersionOfFeature=function(a){return this._AvailableFeatures[a]&&this._AvailableFeatures[a].latest||-1},a.GetStableVersionOfFeature=function(a){return this._AvailableFeatures[a]&&this._AvailableFeatures[a].stable||-1},a.prototype.attachFeature=function(a){a=this._features[a];a&&a.enabled&&!a.featureImplementation.attached&&a.featureImplementation.attach()},a.prototype.detachFeature=function(a){a=this._features[a];a&&a.featureImplementation.attached&&a.featureImplementation.detach()},a.prototype.disableFeature=function(a){a="string"==typeof a?a:a.Name;var b=this._features[a];return!(!b||!b.enabled)&&(b.enabled=!1,this.detachFeature(a),b.featureImplementation.dispose(),!0)},a.prototype.dispose=function(){var a=this;this.getEnabledFeatures().forEach(function(b){a.disableFeature(b),a._features[b].featureImplementation.dispose()})},a.prototype.enableFeature=function(b,c,d,e,f){var g=this;void 0===c&&(c="latest"),void 0===d&&(d={}),void 0===e&&(e=!0),void 0===f&&(f=!0);b="string"==typeof b?b:b.Name;var h=0;if("string"==typeof c){if(!c)throw new Error("Error in provided version - "+b+" ("+c+")");if(-1===(h="stable"===c?a.GetStableVersionOfFeature(b):"latest"===c?a.GetLatestVersionOfFeature(b):+c)||isNaN(h))throw new Error("feature not found - "+b+" ("+c+")")}else h=c;c=a._ConflictingFeatures[b];if(void 0!==c&&-1!==this.getEnabledFeatures().indexOf(c))throw new Error("Feature "+b+" cannot be enabled while "+c+" is enabled.");c=this._features[b];d=a.ConstructFeature(b,h,this._xrSessionManager,d);if(!d)throw new Error("feature not found - "+b);c&&this.disableFeature(b);c=d();if(c.dependsOn&&!c.dependsOn.every(function(a){return!!g._features[a]}))throw new Error("Dependant features missing. Make sure the following features are enabled - "+c.dependsOn.join(", "));if(c.isCompatible())return this._features[b]={featureImplementation:c,enabled:!0,version:h,required:f},e?this._xrSessionManager.session&&!this._features[b].featureImplementation.attached&&this.attachFeature(b):this._features[b].featureImplementation.disableAutoAttach=!0,this._features[b].featureImplementation;if(f)throw new Error("required feature not compatible");return T.b.Warn("Feature "+b+" not compatible with the current environment/browser and was not enabled."),c},a.prototype.getEnabledFeature=function(a){return this._features[a]&&this._features[a].featureImplementation},a.prototype.getEnabledFeatures=function(){return Object.keys(this._features)},a.prototype._extendXRSessionInitObject=function(a){return Object(l.b)(this,void 0,void 0,function(){var b,c,d,e,f,g,h;return Object(l.e)(this,function(i){switch(i.label){case 0:b=this.getEnabledFeatures(),c=0,d=b,i.label=1;case 1:return c0&&(f.lengthSquared()-h2*this.palmUpStrictness-1&&(a=!0)}}this._node.setEnabled(a&&b)},a.prototype.detach=function(){this._scene.onBeforeRenderObservable.remove(this._sceneRenderObserver)},a.prototype.linkToXRExperience=function(a){this._eyeTracking=a.featuresManager.getEnabledFeature(kb.EYE_TRACKING),this._handTracking=a.featuresManager.getEnabledFeature(kb.HAND_TRACKING)},a}(),nb=function(){function a(a,b,c){if(this.targetPosition=g.e.Zero(),this.poleTargetPosition=g.e.Zero(),this.poleTargetLocalOffset=g.e.Zero(),this.poleAngle=0,this.slerpAmount=1,this._bone1Quat=g.b.Identity(),this._bone1Mat=g.a.Identity(),this._bone2Ang=Math.PI,this._maxAngle=Math.PI,this._rightHandedSystem=!1,this._bendAxis=g.e.Right(),this._slerping=!1,this._adjustRoll=0,this._bone2=b,this._bone1=b.getParent(),this._bone1){this.mesh=a;var d=b.getPosition();if(b.getAbsoluteTransform().determinant()>0&&(this._rightHandedSystem=!0,this._bendAxis.x=0,this._bendAxis.y=0,this._bendAxis.z=-1,d.x>d.y&&d.x>d.z&&(this._adjustRoll=.5*Math.PI,this._bendAxis.z=1)),this._bone1.length){b=this._bone1.getScale();d=this._bone2.getScale();this._bone1Length=this._bone1.length*b.y*this.mesh.scaling.y,this._bone2Length=this._bone2.length*d.y*this.mesh.scaling.y}else if(this._bone1.children[0]){a.computeWorldMatrix(!0);b=this._bone2.children[0].getAbsolutePosition(a);d=this._bone2.getAbsolutePosition(a);var e=this._bone1.getAbsolutePosition(a);this._bone1Length=g.e.Distance(b,d),this._bone2Length=g.e.Distance(d,e)}this._bone1.getRotationMatrixToRef(Q.c.WORLD,a,this._bone1Mat),this.maxAngle=Math.PI,c&&(c.targetMesh&&(this.targetMesh=c.targetMesh,this.targetMesh.computeWorldMatrix(!0)),c.poleTargetMesh?(this.poleTargetMesh=c.poleTargetMesh,this.poleTargetMesh.computeWorldMatrix(!0)):c.poleTargetBone?this.poleTargetBone=c.poleTargetBone:this._bone1.getParent()&&(this.poleTargetBone=this._bone1.getParent()),c.poleTargetLocalOffset&&this.poleTargetLocalOffset.copyFrom(c.poleTargetLocalOffset),c.poleAngle&&(this.poleAngle=c.poleAngle),c.bendAxis&&this._bendAxis.copyFrom(c.bendAxis),c.maxAngle&&(this.maxAngle=c.maxAngle),c.slerpAmount&&(this.slerpAmount=c.slerpAmount))}}return Object.defineProperty(a.prototype,"maxAngle",{get:function(){return this._maxAngle},set:function(a){this._setMaxAngle(a)},enumerable:!1,configurable:!0}),a.prototype._setMaxAngle=function(a){a<0&&(a=0),(a>Math.PI||null==a)&&(a=Math.PI),this._maxAngle=a;var b=this._bone1Length,c=this._bone2Length;this._maxReach=Math.sqrt(b*b+c*c-2*b*c*Math.cos(a))},a.prototype.update=function(){var b=this._bone1;if(b){var c=this.targetPosition,d=this.poleTargetPosition,e=a._tmpMats[0],f=a._tmpMats[1];this.targetMesh&&c.copyFrom(this.targetMesh.getAbsolutePosition()),this.poleTargetBone?this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset,this.mesh,d):this.poleTargetMesh&&g.e.TransformCoordinatesToRef(this.poleTargetLocalOffset,this.poleTargetMesh.getWorldMatrix(),d);var h=a._tmpVecs[0],i=a._tmpVecs[1],j=a._tmpVecs[2],k=a._tmpVecs[3],l=a._tmpVecs[4],n=a._tmpQuat;b.getAbsolutePositionToRef(this.mesh,h),d.subtractToRef(h,l),0==l.x&&0==l.y&&0==l.z?l.y=1:l.normalize(),c.subtractToRef(h,k),k.normalize(),g.e.CrossToRef(k,l,i),i.normalize(),g.e.CrossToRef(k,i,j),j.normalize(),g.a.FromXYZAxesToRef(j,k,i,e);b=this._bone1Length;d=this._bone2Length;l=g.e.Distance(h,c);this._maxReach>0&&(l=Math.min(this._maxReach,l));j=(d*d+l*l-b*b)/(2*d*l);i=(l*l+b*b-d*d)/(2*l*b);j>1&&(j=1),i>1&&(i=1),j<-1&&(j=-1),i<-1&&(i=-1);h=Math.acos(j);c=Math.acos(i);d=-h-c;if(this._rightHandedSystem)g.a.RotationYawPitchRollToRef(0,0,this._adjustRoll,f),f.multiplyToRef(e,e),g.a.RotationAxisToRef(this._bendAxis,c,f),f.multiplyToRef(e,e);else{l=a._tmpVecs[5];l.copyFrom(this._bendAxis),l.x*=-1,g.a.RotationAxisToRef(l,-c,f),f.multiplyToRef(e,e)}this.poleAngle&&(g.a.RotationAxisToRef(k,this.poleAngle,f),e.multiplyToRef(f,e)),this._bone1&&(this.slerpAmount<1?(this._slerping||g.b.FromRotationMatrixToRef(this._bone1Mat,this._bone1Quat),g.b.FromRotationMatrixToRef(e,n),g.b.SlerpToRef(this._bone1Quat,n,this.slerpAmount,this._bone1Quat),d=this._bone2Ang*(1-this.slerpAmount)+d*this.slerpAmount,this._bone1.setRotationQuaternion(this._bone1Quat,Q.c.WORLD,this.mesh),this._slerping=!0):(this._bone1.setRotationMatrix(e,Q.c.WORLD,this.mesh),this._bone1Mat.copyFrom(e),this._slerping=!1),this._updateLinkedTransformRotation(this._bone1)),this._bone2.setAxisAngle(this._bendAxis,d,Q.c.LOCAL),this._updateLinkedTransformRotation(this._bone2),this._bone2Ang=d}},a.prototype._updateLinkedTransformRotation=function(a){a._linkedTransformNode&&(a._linkedTransformNode.rotationQuaternion||(a._linkedTransformNode.rotationQuaternion=new g.b),a.getRotationQuaternionToRef(Q.c.LOCAL,null,a._linkedTransformNode.rotationQuaternion))},a._tmpVecs=[g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero(),g.e.Zero()],a._tmpQuat=g.b.Identity(),a._tmpMats=[g.a.Identity(),g.a.Identity()],a}(),ob=function(){function a(a,b,c,d){if(this.upAxis=g.e.Up(),this.upAxisSpace=Q.c.LOCAL,this.adjustYaw=0,this.adjustPitch=0,this.adjustRoll=0,this.slerpAmount=1,this._boneQuat=g.b.Identity(),this._slerping=!1,this._firstFrameSkipped=!1,this._fowardAxis=g.e.Forward(),this.mesh=a,this.bone=b,this.target=c,d&&(d.adjustYaw&&(this.adjustYaw=d.adjustYaw),d.adjustPitch&&(this.adjustPitch=d.adjustPitch),d.adjustRoll&&(this.adjustRoll=d.adjustRoll),null!=d.maxYaw?this.maxYaw=d.maxYaw:this.maxYaw=Math.PI,null!=d.minYaw?this.minYaw=d.minYaw:this.minYaw=-Math.PI,null!=d.maxPitch?this.maxPitch=d.maxPitch:this.maxPitch=Math.PI,null!=d.minPitch?this.minPitch=d.minPitch:this.minPitch=-Math.PI,null!=d.slerpAmount&&(this.slerpAmount=d.slerpAmount),null!=d.upAxis&&(this.upAxis=d.upAxis),null!=d.upAxisSpace&&(this.upAxisSpace=d.upAxisSpace),null!=d.yawAxis||null!=d.pitchAxis)){a=Q.a.Y;c=Q.a.X;null!=d.yawAxis&&(a=d.yawAxis.clone()).normalize(),null!=d.pitchAxis&&(c=d.pitchAxis.clone()).normalize();d=g.e.Cross(c,a);this._transformYawPitch=g.a.Identity(),g.a.FromXYZAxesToRef(c,a,d,this._transformYawPitch),this._transformYawPitchInv=this._transformYawPitch.clone(),this._transformYawPitch.invert()}b.getParent()||this.upAxisSpace!=Q.c.BONE||(this.upAxisSpace=Q.c.LOCAL)}return Object.defineProperty(a.prototype,"minYaw",{get:function(){return this._minYaw},set:function(a){this._minYaw=a,this._minYawSin=Math.sin(a),this._minYawCos=Math.cos(a),null!=this._maxYaw&&(this._midYawConstraint=.5*this._getAngleDiff(this._minYaw,this._maxYaw)+this._minYaw,this._yawRange=this._maxYaw-this._minYaw)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxYaw",{get:function(){return this._maxYaw},set:function(a){this._maxYaw=a,this._maxYawSin=Math.sin(a),this._maxYawCos=Math.cos(a),null!=this._minYaw&&(this._midYawConstraint=.5*this._getAngleDiff(this._minYaw,this._maxYaw)+this._minYaw,this._yawRange=this._maxYaw-this._minYaw)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"minPitch",{get:function(){return this._minPitch},set:function(a){this._minPitch=a,this._minPitchTan=Math.tan(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxPitch",{get:function(){return this._maxPitch},set:function(a){this._maxPitch=a,this._maxPitchTan=Math.tan(a)},enumerable:!1,configurable:!0}),a.prototype.update=function(){if(this.slerpAmount<1&&!this._firstFrameSkipped)this._firstFrameSkipped=!0;else{var b=this.bone,c=a._tmpVecs[0];b.getAbsolutePositionToRef(this.mesh,c);var d=this.target,e=a._tmpMats[0],f=a._tmpMats[1],h=this.mesh;b=b.getParent();var i=a._tmpVecs[1];i.copyFrom(this.upAxis),this.upAxisSpace==Q.c.BONE&&b?(this._transformYawPitch&&g.e.TransformCoordinatesToRef(i,this._transformYawPitchInv,i),b.getDirectionToRef(i,this.mesh,i)):this.upAxisSpace==Q.c.LOCAL&&(h.getDirectionToRef(i,i),1==h.scaling.x&&1==h.scaling.y&&1==h.scaling.z||i.normalize());var j=!1,k=!1;if(this._maxYaw==Math.PI&&this._minYaw==-Math.PI||(j=!0),this._maxPitch==Math.PI&&this._minPitch==-Math.PI||(k=!0),j||k){var l=a._tmpMats[2],o=a._tmpMats[3];if(this.upAxisSpace==Q.c.BONE&&1==i.y&&b)b.getRotationMatrixToRef(Q.c.WORLD,this.mesh,l);else if(this.upAxisSpace!=Q.c.LOCAL||1!=i.y||b){(q=a._tmpVecs[2]).copyFrom(this._fowardAxis),this._transformYawPitch&&g.e.TransformCoordinatesToRef(q,this._transformYawPitchInv,q),b?b.getDirectionToRef(q,this.mesh,q):h.getDirectionToRef(q,q);b=g.e.Cross(i,q);b.normalize();var q=g.e.Cross(b,i);g.a.FromXYZAxesToRef(b,i,q,l)}else l.copyFrom(h.getWorldMatrix());l.invertToRef(o);b=null;if(k){q=a._tmpVecs[3];d.subtractToRef(c,q),g.e.TransformCoordinatesToRef(q,o,q),b=Math.sqrt(q.x*q.x+q.z*q.z);h=Math.atan2(q.y,b);k=h;h>this._maxPitch?(q.y=this._maxPitchTan*b,k=this._maxPitch):hthis._maxYaw||hMath.PI?this._isAngleBetween(h,this._maxYaw,this._midYawConstraint)?(q.z=this._maxYawCos*b,q.x=this._maxYawSin*b,k=this._maxYaw):this._isAngleBetween(h,this._midYawConstraint,this._minYaw)&&(q.z=this._minYawCos*b,q.x=this._minYawSin*b,k=this._minYaw):h>this._maxYaw?(q.z=this._maxYawCos*b,q.x=this._maxYawSin*b,k=this._maxYaw):hMath.PI){j=a._tmpVecs[8];j.copyFrom(Q.a.Z),this._transformYawPitch&&g.e.TransformCoordinatesToRef(j,this._transformYawPitchInv,j);var r=a._tmpMats[4];this._boneQuat.toRotationMatrix(r),this.mesh.getWorldMatrix().multiplyToRef(r,r),g.e.TransformCoordinatesToRef(j,r,j),g.e.TransformCoordinatesToRef(j,o,j);r=Math.atan2(j.x,j.z);if(this._getAngleBetween(r,h)>this._getAngleBetween(r,this._midYawConstraint)){null==b&&(b=Math.sqrt(q.x*q.x+q.z*q.z));o=this._getAngleBetween(r,this._maxYaw);this._getAngleBetween(r,this._minYaw)Math.PI?b-=2*Math.PI:b<-Math.PI&&(b+=2*Math.PI),b},a.prototype._getAngleBetween=function(a,b){return(a=(a=(a%=2*Math.PI)<0?a+2*Math.PI:a)<(b=(b%=2*Math.PI)<0?b+2*Math.PI:b)?b-a:a-b)>Math.PI&&(a=2*Math.PI-a),a},a.prototype._isAngleBetween=function(a,b,c){if(a=(a%=2*Math.PI)<0?a+2*Math.PI:a,(b=(b%=2*Math.PI)<0?b+2*Math.PI:b)<(c=(c%=2*Math.PI)<0?c+2*Math.PI:c)){if(a>b&&ac&&a>o,A=0;A<6;A++){var B=n[o][A];m&&(B=rb(B,z,z,e)),x.texImage2D(A,o,k,z,z,0,j,f,B)}w._bindTextureDirectly(x.TEXTURE_CUBE_MAP,null)}else w.updateRawCubeTexture(y,a,d,e,l);y.isReady=!0,null==b||b._removePendingData(y),i&&i()}}(a)},void 0,null==b?void 0:b.offlineProvider,!0,function(a,c){null==b||b._removePendingData(y),j&&a&&j(a.status+" "+a.statusText,c)}),y},qb.a.prototype.createRawTexture2DArray=a(!1),qb.a.prototype.createRawTexture3D=a(!0),qb.a.prototype.updateRawTexture2DArray=V(!1),qb.a.prototype.updateRawTexture3D=V(!0);var sb=function(a){function b(b,c,d,e,f,g,h,i,j,k){void 0===g&&(g=!0),void 0===h&&(h=!1),void 0===i&&(i=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT);f=a.call(this,null,f,!g,h,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,k)||this;return f.format=e,f._engine?(f._engine._caps.textureFloatLinearFiltering||j!==r.a.TEXTURETYPE_FLOAT||(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),f._engine._caps.textureHalfFloatLinearFiltering||j!==r.a.TEXTURETYPE_HALF_FLOAT||(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),f._texture=f._engine.createRawTexture(b,c,d,e,g,h,i,null,j,null!=k?k:0),f.wrapU=U.a.CLAMP_ADDRESSMODE,f.wrapV=U.a.CLAMP_ADDRESSMODE,f):f}return Object(l.d)(b,a),b.prototype.update=function(a){this._getEngine().updateRawTexture(this._texture,a,this._texture.format,this._texture.invertY,null,this._texture.type)},b.CreateLuminanceTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_LUMINANCE,e,f,g,h)},b.CreateLuminanceAlphaTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_LUMINANCE_ALPHA,e,f,g,h)},b.CreateAlphaTexture=function(a,c,d,e,f,g,h){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),new b(a,c,d,r.a.TEXTUREFORMAT_ALPHA,e,f,g,h)},b.CreateRGBTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGB,e,f,g,h,i)},b.CreateRGBATexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGBA,e,f,g,h,i)},b.CreateRGBAStorageTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),new b(a,c,d,r.a.TEXTUREFORMAT_RGBA,e,f,g,h,i,r.a.TEXTURE_CREATIONFLAG_STORAGE)},b.CreateRTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=U.a.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_FLOAT),new b(a,c,d,r.a.TEXTUREFORMAT_R,e,f,g,h,i)},b.CreateRStorageTexture=function(a,c,d,e,f,g,h,i){return void 0===f&&(f=!0),void 0===g&&(g=!1),void 0===h&&(h=U.a.TRILINEAR_SAMPLINGMODE),void 0===i&&(i=r.a.TEXTURETYPE_FLOAT),new b(a,c,d,r.a.TEXTUREFORMAT_R,e,f,g,h,i,r.a.TEXTURE_CREATIONFLAG_STORAGE)},b}(U.a),tb=function(){function a(a,b,c){this.name=a,this.id=b,this.bones=new Array,this.needInitialSkinMatrix=!1,this.overrideMesh=null,this._isDirty=!0,this._meshesWithPoseMatrix=new Array,this._identity=g.a.Identity(),this._ranges={},this._lastAbsoluteTransformsUpdateId=-1,this._canUseTextureForBones=!1,this._uniqueId=0,this._numBonesWithLinkedTransformNode=0,this._hasWaitingData=null,this._waitingOverrideMeshId=null,this._parentContainer=null,this.doNotSerialize=!1,this._useTextureToStoreBoneMatrices=!0,this._animationPropertiesOverride=null,this.onBeforeComputeObservable=new f.c,this.bones=[],this._scene=c||C.a.LastCreatedScene,this._uniqueId=this._scene.getUniqueId(),this._scene.addSkeleton(this),this._isDirty=!0;a=this._scene.getEngine().getCaps();this._canUseTextureForBones=a.textureFloat&&a.maxVertexTextureImageUnits>0}return Object.defineProperty(a.prototype,"useTextureToStoreBoneMatrices",{get:function(){return this._useTextureToStoreBoneMatrices},set:function(a){this._useTextureToStoreBoneMatrices=a,this._markAsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationPropertiesOverride",{get:function(){return this._animationPropertiesOverride?this._animationPropertiesOverride:this._scene.animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUsingTextureForMatrices",{get:function(){return this.useTextureToStoreBoneMatrices&&this._canUseTextureForBones},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"Skeleton"},a.prototype.getChildren=function(){return this.bones.filter(function(a){return!a.getParent()})},a.prototype.getTransformMatrices=function(a){return this.needInitialSkinMatrix&&a._bonesTransformMatrices?a._bonesTransformMatrices:(this._transformMatrices||this.prepare(),this._transformMatrices)},a.prototype.getTransformMatrixTexture=function(a){return this.needInitialSkinMatrix&&a._transformMatrixTexture?a._transformMatrixTexture:this._transformMatrixTexture},a.prototype.getScene=function(){return this._scene},a.prototype.toString=function(a){var b="Name: "+this.name+", nBones: "+this.bones.length;if(b+=", nAnimationRanges: "+(this._ranges?Object.keys(this._ranges).length:"none"),a){b+=", Ranges: {";a=!0;for(var c in this._ranges)a&&(b+=", ",a=!1),b+=c;b+="}"}return b},a.prototype.getBoneIndexByName=function(a){for(var b=0,c=this.bones.length;b-1&&this._meshesWithPoseMatrix.splice(a,1)},a.prototype._computeTransformMatrices=function(a,b){this.onBeforeComputeObservable.notifyObservers(this);for(var c=0;c0)for(var a=0,b=this.bones;a-1&&this._parentContainer.skeletons.splice(a,1),this._parentContainer=null}this._transformMatrixTexture&&(this._transformMatrixTexture.dispose(),this._transformMatrixTexture=null)},a.prototype.serialize=function(){var a,b={};b.name=this.name,b.id=this.id,this.dimensionsAtRest&&(b.dimensionsAtRest=this.dimensionsAtRest.asArray()),b.bones=[],b.needInitialSkinMatrix=this.needInitialSkinMatrix,b.overrideMeshId=null===(a=this.overrideMesh)||void 0===a?void 0:a.id;for(a=0;a0&&(d.animation=c.animations[0].serialize()),b.ranges=[],this._ranges){c=this._ranges[d];if(c){var e={};e.name=d,e.from=c.from,e.to=c.to,b.ranges.push(e)}}}return b},a.Parse=function(b,c){var d;c=new a(b.name,b.id,c);for(b.dimensionsAtRest&&(c.dimensionsAtRest=g.e.FromArray(b.dimensionsAtRest)),c.needInitialSkinMatrix=b.needInitialSkinMatrix,b.overrideMeshId&&(c._hasWaitingData=!0,c._waitingOverrideMeshId=b.overrideMeshId),d=0;d-1&&(h=c.bones[e.parentBoneIndex]);var i=e.rest?g.a.FromArray(e.rest):null;h=new ka(e.name,c,h,g.a.FromArray(e.matrix),i,null,f);void 0!==e.id&&null!==e.id&&(h.id=e.id),e.length&&(h.length=e.length),e.metadata&&(h.metadata=e.metadata),e.animation&&h.animations.push(N.Parse(e.animation)),void 0!==e.linkedTransformNodeId&&null!==e.linkedTransformNodeId&&(c._hasWaitingData=!0,h._waitingTransformNodeId=e.linkedTransformNodeId)}if(b.ranges)for(d=0;d0&&(a=this._meshesWithPoseMatrix[0].getPoseMatrix()),a},a.prototype.sortBones=function(){for(var a=new Array,b=new Array(this.bones.length),c=0;c=2&&(this._leftStick={x:this.browserGamepad.axes[this._leftStickAxisX],y:this.browserGamepad.axes[this._leftStickAxisY]}),this.browserGamepad.axes.length>=4&&(this._rightStick={x:this.browserGamepad.axes[this._rightStickAxisX],y:this.browserGamepad.axes[this._rightStickAxisY]})}return Object.defineProperty(a.prototype,"isConnected",{get:function(){return this._isConnected},enumerable:!1,configurable:!0}),a.prototype.onleftstickchanged=function(a){this._onleftstickchanged=a},a.prototype.onrightstickchanged=function(a){this._onrightstickchanged=a},Object.defineProperty(a.prototype,"leftStick",{get:function(){return this._leftStick},set:function(a){!this._onleftstickchanged||this._leftStick.x===a.x&&this._leftStick.y===a.y||this._onleftstickchanged(a),this._leftStick=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rightStick",{get:function(){return this._rightStick},set:function(a){!this._onrightstickchanged||this._rightStick.x===a.x&&this._rightStick.y===a.y||this._onrightstickchanged(a),this._rightStick=a},enumerable:!1,configurable:!0}),a.prototype.update=function(){this._leftStick&&(this.leftStick={x:this.browserGamepad.axes[this._leftStickAxisX],y:this.browserGamepad.axes[this._leftStickAxisY]},this._invertLeftStickY&&(this.leftStick.y*=-1)),this._rightStick&&(this.rightStick={x:this.browserGamepad.axes[this._rightStickAxisX],y:this.browserGamepad.axes[this._rightStickAxisY]})},a.prototype.dispose=function(){},a.GAMEPAD=0,a.GENERIC=1,a.XBOX=2,a.POSE_ENABLED=3,a.DUALSHOCK=4,a}(),Db=function(a){function b(b,c,d){b=a.call(this,b,c,d)||this;return b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.type=Cb.GENERIC,b._buttons=new Array(d.buttons.length),b}return Object(l.d)(b,a),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype.update=function(){a.prototype.update.call(this);for(var b=0;b.005&&(a.inertialAlphaOffset+=c)}if(0!=b.y){c=b.y/this.gamepadRotationSensibility*this._yAxisScale;0!=c&&Math.abs(c)>.005&&(a.inertialBetaOffset+=c)}}b=this.gamepad.leftStick;if(b&&0!=b.y){a=b.y/this.gamepadMoveSensibility;0!=a&&Math.abs(a)>.005&&(this.camera.inertialRadiusOffset-=a)}}},a.prototype.getClassName=function(){return"ArcRotateCameraGamepadInput"},a.prototype.getSimpleName=function(){return"gamepad"},Object(l.c)([Object(I.d)()],a.prototype,"gamepadRotationSensibility",void 0),Object(l.c)([Object(I.d)()],a.prototype,"gamepadMoveSensibility",void 0),a}();zb.ArcRotateCameraGamepadInput=Eb;var Fb=c(73),Gb=function(){function a(){this.keysUp=[38],this.keysDown=[40],this.keysLeft=[37],this.keysRight=[39],this.keysReset=[220],this.panningSensibility=50,this.zoomingSensibility=25,this.useAltToZoom=!0,this.angularSpeed=.01,this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;d.metaKey||(c.type===Fb.a.KEYDOWN?(b._ctrlPressed=d.ctrlKey,b._altPressed=d.altKey,(-1!==b.keysUp.indexOf(d.keyCode)||-1!==b.keysDown.indexOf(d.keyCode)||-1!==b.keysLeft.indexOf(d.keyCode)||-1!==b.keysRight.indexOf(d.keyCode)||-1!==b.keysReset.indexOf(d.keyCode))&&(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),d.preventDefault&&(a||d.preventDefault()))):-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysReset.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),d.preventDefault&&(a||d.preventDefault())))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b0?b/(1+this.wheelDeltaPercentage):b*(1+this.wheelDeltaPercentage)},a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this._wheel=function(c,d){if(c.type===Ua.a.POINTERWHEEL){d=c.event;c=d;var e=0,f=d.deltaMode===wb.b.DOM_DELTA_LINE?40:1;if(e=void 0!==d.deltaY?-d.deltaY*f:void 0!==d.wheelDeltaY?-d.wheelDeltaY*f:c.wheelDelta,b.customComputeDeltaFromMouseWheel)f=b.customComputeDeltaFromMouseWheel(e,b,d);else if(b.wheelDeltaPercentage){if((f=b.computeDeltaFromMouseWheelLegacyEvent(e,b.camera.radius))>0){for(var c=b.camera.radius,g=b.camera.inertialRadiusOffset+f,h=0;h<20&&Math.abs(g)>.001;h++)c-=g,g*=b.camera.inertia;c=H.a.Clamp(c,0,Number.MAX_VALUE),f=b.computeDeltaFromMouseWheelLegacyEvent(e,c)}}else f=e/(40*b.wheelPrecision);f&&(b.zoomToMouseLocation&&b._hitPlane?b._zoomToMouse(f):b.camera.inertialRadiusOffset+=f),d.preventDefault&&(a||d.preventDefault())}},this._observer=this.camera.getScene().onPointerObservable.add(this._wheel,Ua.a.POINTERWHEEL),this.zoomToMouseLocation&&this._inertialPanning.setAll(0)},a.prototype.detachControl=function(a){this._observer&&(this.camera.getScene().onPointerObservable.remove(this._observer),this._observer=null,this._wheel=null)},a.prototype.checkInputs=function(){if(this.zoomToMouseLocation){var a=this.camera;0+a.inertialAlphaOffset+a.inertialBetaOffset+a.inertialRadiusOffset&&(this._updateHitPlane(),a.target.addInPlace(this._inertialPanning),this._inertialPanning.scaleInPlace(a.inertia),this._zeroIfClose(this._inertialPanning))}},a.prototype.getClassName=function(){return"ArcRotateCameraMouseWheelInput"},a.prototype.getSimpleName=function(){return"mousewheel"},a.prototype._updateHitPlane=function(){var a=this.camera;a=a.target.subtract(a.position);this._hitPlane=Hb.a.FromPositionAndNormal(g.e.Zero(),a)},a.prototype._getPosition=function(){var a,b=this.camera,c=b.getScene();c=c.createPickingRay(c.pointerX,c.pointerY,g.a.Identity(),b,!1);b=0;return this._hitPlane&&(b=null!==(a=c.intersectsPlane(this._hitPlane))&&void 0!==a?a:0),c.origin.addInPlace(c.direction.scaleInPlace(b))},a.prototype._zoomToMouse=function(a){var b,c=this.camera,d=1-c.inertia;if(c.lowerRadiusLimit){b=null!==(b=c.lowerRadiusLimit)&&void 0!==b?b:0;c.radius-(c.inertialRadiusOffset+a)/db&&(a=(c.radius-b)*d-c.inertialRadiusOffset)}b=a/d/c.radius;b=this._getPosition().subtract(c.target).scale(b);b.scaleInPlace(d),this._inertialPanning.addInPlace(b),c.inertialRadiusOffset+=a},a.prototype._zeroIfClose=function(a){Math.abs(a.x)this.camera.pinchToPanMaxDistance?(this._computePinchZoom(c,d),this._isPinching=!0):this._computeMultiTouchPanning(e,f)):this.multiTouchPanning?this._computeMultiTouchPanning(e,f):this.pinchZoom&&this._computePinchZoom(c,d))},b.prototype.onButtonDown=function(a){this._isPanClick=a.button===this.camera._panningMouseButton},b.prototype.onButtonUp=function(a){this._twoFingerActivityCount=0,this._isPinching=!1},b.prototype.onLostFocus=function(){this._isPanClick=!1,this._twoFingerActivityCount=0,this._isPinching=!1},b.MinimumRadiusForPinch=.001,Object(l.c)([Object(I.d)()],b.prototype,"buttons",void 0),Object(l.c)([Object(I.d)()],b.prototype,"angularSensibilityX",void 0),Object(l.c)([Object(I.d)()],b.prototype,"angularSensibilityY",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchPrecision",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchDeltaPercentage",void 0),Object(l.c)([Object(I.d)()],b.prototype,"useNaturalPinchZoom",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchZoom",void 0),Object(l.c)([Object(I.d)()],b.prototype,"panningSensibility",void 0),Object(l.c)([Object(I.d)()],b.prototype,"multiTouchPanning",void 0),Object(l.c)([Object(I.d)()],b.prototype,"multiTouchPanAndZoom",void 0),b}(yb);zb.ArcRotateCameraPointersInput=Jb;var Kb=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addMouseWheel=function(){return this.add(new Ib),this},b.prototype.addPointers=function(){return this.add(new Jb),this},b.prototype.addKeyboard=function(){return this.add(new Gb),this},b}(Ab);Kb.prototype.addVRDeviceOrientation=function(){return this.add(new Lb),this};var Lb=function(){function a(){this.alphaCorrection=1,this.gammaCorrection=1,this._alpha=0,this._gamma=0,this._dirty=!1,this._deviceOrientationHandler=this._onOrientationEvent.bind(this)}return a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this.camera.attachControl(a);var c=this.camera.getScene().getEngine().getHostWindow();c&&("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"===a?c.addEventListener("deviceorientation",b._deviceOrientationHandler):T.b.Warn("Permission not granted.")})["catch"](function(a){T.b.Error(a)}):c.addEventListener("deviceorientation",this._deviceOrientationHandler))},a.prototype._onOrientationEvent=function(a){null!==a.alpha&&(this._alpha=(0|+a.alpha)*this.alphaCorrection),null!==a.gamma&&(this._gamma=(0|+a.gamma)*this.gammaCorrection),this._dirty=!0},a.prototype.checkInputs=function(){this._dirty&&(this._dirty=!1,this._gamma<0&&(this._gamma=180+this._gamma),this.camera.alpha=-this._alpha/180*Math.PI%Math.PI*2,this.camera.beta=this._gamma/180*Math.PI)},a.prototype.detachControl=function(a){window.removeEventListener("deviceorientation",this._deviceOrientationHandler)},a.prototype.getClassName=function(){return"ArcRotateCameraVRDeviceOrientationInput"},a.prototype.getSimpleName=function(){return"VRDeviceOrientation"},a}();zb.ArcRotateCameraVRDeviceOrientationInput=Lb;var Mb=function(){function a(){this.keysForward=[87],this.keysBackward=[83],this.keysUp=[69],this.keysDown=[81],this.keysRight=[68],this.keysLeft=[65],this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;c.type===Fb.a.KEYDOWN?-1===b.keysForward.indexOf(d.keyCode)&&-1===b.keysBackward.indexOf(d.keyCode)&&-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)||(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),a||d.preventDefault()):-1===b.keysForward.indexOf(d.keyCode)&&-1===b.keysBackward.indexOf(d.keyCode)&&-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),a||d.preventDefault())}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.getClassName=function(){return"FlyCameraKeyboardInput"},a.prototype._onLostFocus=function(a){this._keys=[]},a.prototype.getSimpleName=function(){return"keyboard"},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b=0&&b._keys.splice(c,1),d.preventDefault&&(a||d.preventDefault())))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){var a=this;this._onKeyboardObserver&&this._keys.forEach(function(b){-1!==a.keysHeightOffsetIncr.indexOf(b)&&a._modifierHeightOffset()?a.camera.heightOffset+=a.heightSensibility:-1!==a.keysHeightOffsetDecr.indexOf(b)&&a._modifierHeightOffset()?a.camera.heightOffset-=a.heightSensibility:-1!==a.keysRotationOffsetIncr.indexOf(b)&&a._modifierRotationOffset()?(a.camera.rotationOffset+=a.rotationSensibility,a.camera.rotationOffset%=360):-1!==a.keysRotationOffsetDecr.indexOf(b)&&a._modifierRotationOffset()?(a.camera.rotationOffset-=a.rotationSensibility,a.camera.rotationOffset%=360):-1!==a.keysRadiusIncr.indexOf(b)&&a._modifierRadius()?a.camera.radius+=a.radiusSensibility:-1!==a.keysRadiusDecr.indexOf(b)&&a._modifierRadius()&&(a.camera.radius-=a.radiusSensibility)})},a.prototype.getClassName=function(){return"FollowCameraKeyboardMoveInput"},a.prototype.getSimpleName=function(){return"keyboard"},a.prototype._modifierHeightOffset=function(){return this.keysHeightOffsetModifierAlt===this._altPressed&&this.keysHeightOffsetModifierCtrl===this._ctrlPressed&&this.keysHeightOffsetModifierShift===this._shiftPressed},a.prototype._modifierRotationOffset=function(){return this.keysRotationOffsetModifierAlt===this._altPressed&&this.keysRotationOffsetModifierCtrl===this._ctrlPressed&&this.keysRotationOffsetModifierShift===this._shiftPressed},a.prototype._modifierRadius=function(){return this.keysRadiusModifierAlt===this._altPressed&&this.keysRadiusModifierCtrl===this._ctrlPressed&&this.keysRadiusModifierShift===this._shiftPressed},Object(l.c)([Object(I.d)()],a.prototype,"keysHeightOffsetIncr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysHeightOffsetDecr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysHeightOffsetModifierAlt",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysHeightOffsetModifierCtrl",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysHeightOffsetModifierShift",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRotationOffsetIncr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRotationOffsetDecr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRotationOffsetModifierAlt",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRotationOffsetModifierCtrl",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRotationOffsetModifierShift",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRadiusIncr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRadiusDecr",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRadiusModifierAlt",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRadiusModifierCtrl",void 0),Object(l.c)([Object(I.d)()],a.prototype,"keysRadiusModifierShift",void 0),Object(l.c)([Object(I.d)()],a.prototype,"heightSensibility",void 0),Object(l.c)([Object(I.d)()],a.prototype,"rotationSensibility",void 0),Object(l.c)([Object(I.d)()],a.prototype,"radiusSensibility",void 0),a}();zb.FollowCameraKeyboardMoveInput=Ob;var Pb=function(){function a(){this.axisControlRadius=!0,this.axisControlHeight=!1,this.axisControlRotation=!1,this.wheelPrecision=3,this.wheelDeltaPercentage=0}return a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this._wheel=function(c,d){if(c.type===Ua.a.POINTERWHEEL){d=c.event;c=0;var e=Math.max(-1,Math.min(1,d.deltaY||d.wheelDelta||-d.detail));b.wheelDeltaPercentage?(console.assert(b.axisControlRadius+b.axisControlHeight+b.axisControlRotation<=1,"wheelDeltaPercentage only usable when mouse wheel controls ONE axis. Currently enabled: axisControlRadius: "+b.axisControlRadius+", axisControlHeightOffset: "+b.axisControlHeight+", axisControlRotationOffset: "+b.axisControlRotation),b.axisControlRadius?c=.01*e*b.wheelDeltaPercentage*b.camera.radius:b.axisControlHeight?c=.01*e*b.wheelDeltaPercentage*b.camera.heightOffset:b.axisControlRotation&&(c=.01*e*b.wheelDeltaPercentage*b.camera.rotationOffset)):c=e*b.wheelPrecision,c&&(b.axisControlRadius?b.camera.radius+=c:b.axisControlHeight?b.camera.heightOffset-=c:b.axisControlRotation&&(b.camera.rotationOffset-=c)),d.preventDefault&&(a||d.preventDefault())}},this._observer=this.camera.getScene().onPointerObservable.add(this._wheel,Ua.a.POINTERWHEEL)},a.prototype.detachControl=function(a){this._observer&&(this.camera.getScene().onPointerObservable.remove(this._observer),this._observer=null,this._wheel=null)},a.prototype.getClassName=function(){return"ArcRotateCameraMouseWheelInput"},a.prototype.getSimpleName=function(){return"mousewheel"},Object(l.c)([Object(I.d)()],a.prototype,"axisControlRadius",void 0),Object(l.c)([Object(I.d)()],a.prototype,"axisControlHeight",void 0),Object(l.c)([Object(I.d)()],a.prototype,"axisControlRotation",void 0),Object(l.c)([Object(I.d)()],a.prototype,"wheelPrecision",void 0),Object(l.c)([Object(I.d)()],a.prototype,"wheelDeltaPercentage",void 0),a}();zb.FollowCameraMouseWheelInput=Pb;var Qb=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.angularSensibilityX=1,b.angularSensibilityY=1,b.pinchPrecision=1e4,b.pinchDeltaPercentage=0,b.axisXControlRadius=!1,b.axisXControlHeight=!1,b.axisXControlRotation=!0,b.axisYControlRadius=!1,b.axisYControlHeight=!0,b.axisYControlRotation=!1,b.axisPinchControlRadius=!0,b.axisPinchControlHeight=!1,b.axisPinchControlRotation=!1,b.warningEnable=!0,b._warningCounter=0,b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FollowCameraPointersInput"},b.prototype.onTouch=function(a,b,c){this._warning(),this.axisXControlRotation?this.camera.rotationOffset+=b/this.angularSensibilityX:this.axisYControlRotation&&(this.camera.rotationOffset+=c/this.angularSensibilityX),this.axisXControlHeight?this.camera.heightOffset+=b/this.angularSensibilityY:this.axisYControlHeight&&(this.camera.heightOffset+=c/this.angularSensibilityY),this.axisXControlRadius?this.camera.radius-=b/this.angularSensibilityY:this.axisYControlRadius&&(this.camera.radius-=c/this.angularSensibilityY)},b.prototype.onMultiTouch=function(a,b,c,d,e,f){if(!(0===c&&null===e||0===d&&null===f)){a=(d-c)/(this.pinchPrecision*(this.angularSensibilityX+this.angularSensibilityY)/2);this.pinchDeltaPercentage?(a*=.01*this.pinchDeltaPercentage,this.axisPinchControlRotation&&(this.camera.rotationOffset+=a*this.camera.rotationOffset),this.axisPinchControlHeight&&(this.camera.heightOffset+=a*this.camera.heightOffset),this.axisPinchControlRadius&&(this.camera.radius-=a*this.camera.radius)):(this.axisPinchControlRotation&&(this.camera.rotationOffset+=a),this.axisPinchControlHeight&&(this.camera.heightOffset+=a),this.axisPinchControlRadius&&(this.camera.radius-=a))}},b.prototype._warning=function(){if(this.warningEnable&&this._warningCounter++%100==0){var a="It probably only makes sense to control ONE camera property with each pointer axis. Set "warningEnable = false" if you are sure. Currently enabled: ";console.assert(this.axisXControlRotation+this.axisXControlHeight+this.axisXControlRadius<=1,a+"axisXControlRotation: "+this.axisXControlRotation+", axisXControlHeight: "+this.axisXControlHeight+", axisXControlRadius: "+this.axisXControlRadius),console.assert(this.axisYControlRotation+this.axisYControlHeight+this.axisYControlRadius<=1,a+"axisYControlRotation: "+this.axisYControlRotation+", axisYControlHeight: "+this.axisYControlHeight+", axisYControlRadius: "+this.axisYControlRadius),console.assert(this.axisPinchControlRotation+this.axisPinchControlHeight+this.axisPinchControlRadius<=1,a+"axisPinchControlRotation: "+this.axisPinchControlRotation+", axisPinchControlHeight: "+this.axisPinchControlHeight+", axisPinchControlRadius: "+this.axisPinchControlRadius)}},Object(l.c)([Object(I.d)()],b.prototype,"angularSensibilityX",void 0),Object(l.c)([Object(I.d)()],b.prototype,"angularSensibilityY",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchPrecision",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchDeltaPercentage",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisXControlRadius",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisXControlHeight",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisXControlRotation",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisYControlRadius",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisYControlHeight",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisYControlRotation",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisPinchControlRadius",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisPinchControlHeight",void 0),Object(l.c)([Object(I.d)()],b.prototype,"axisPinchControlRotation",void 0),b}(yb);zb.FollowCameraPointersInput=Qb;var Rb=function(){function a(){this.keysUp=[38],this.keysUpward=[33],this.keysDown=[40],this.keysDownward=[34],this.keysLeft=[37],this.keysRight=[39],this.rotationSpeed=.5,this.keysRotateLeft=[],this.keysRotateRight=[],this._keys=new Array}return a.prototype.attachControl=function(a){var b=this;a=T.b.BackCompatCameraNoPreventDefault(arguments),this._onCanvasBlurObserver||(this._scene=this.camera.getScene(),this._engine=this._scene.getEngine(),this._onCanvasBlurObserver=this._engine.onCanvasBlurObservable.add(function(){b._keys=[]}),this._onKeyboardObserver=this._scene.onKeyboardObservable.add(function(c){var d=c.event;d.metaKey||(c.type===Fb.a.KEYDOWN?-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysUpward.indexOf(d.keyCode)&&-1===b.keysDownward.indexOf(d.keyCode)&&-1===b.keysRotateLeft.indexOf(d.keyCode)&&-1===b.keysRotateRight.indexOf(d.keyCode)||(-1===b._keys.indexOf(d.keyCode)&&b._keys.push(d.keyCode),a||d.preventDefault()):-1===b.keysUp.indexOf(d.keyCode)&&-1===b.keysDown.indexOf(d.keyCode)&&-1===b.keysLeft.indexOf(d.keyCode)&&-1===b.keysRight.indexOf(d.keyCode)&&-1===b.keysUpward.indexOf(d.keyCode)&&-1===b.keysDownward.indexOf(d.keyCode)&&-1===b.keysRotateLeft.indexOf(d.keyCode)&&-1===b.keysRotateRight.indexOf(d.keyCode)||((c=b._keys.indexOf(d.keyCode))>=0&&b._keys.splice(c,1),a||d.preventDefault()))}))},a.prototype.detachControl=function(a){this._scene&&(this._onKeyboardObserver&&this._scene.onKeyboardObservable.remove(this._onKeyboardObserver),this._onCanvasBlurObserver&&this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver),this._onKeyboardObserver=null,this._onCanvasBlurObserver=null),this._keys=[]},a.prototype.checkInputs=function(){if(this._onKeyboardObserver)for(var a=this.camera,b=0;b1)a.cameraRotation.x=-this._offsetY/this.touchAngularSensibility;else{var b=a._computeLocalCameraSpeed();b=new g.e(0,0,b*this._offsetY/this.touchMoveSensibility);g.a.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,0,a._cameraRotationMatrix),a.cameraDirection.addInPlace(g.e.TransformCoordinates(b,a._cameraRotationMatrix))}}},a.prototype.getClassName=function(){return"FreeCameraTouchInput"},a.prototype.getSimpleName=function(){return"touch"},Object(l.c)([Object(I.d)()],a.prototype,"touchAngularSensibility",void 0),Object(l.c)([Object(I.d)()],a.prototype,"touchMoveSensibility",void 0),a}();zb.FreeCameraTouchInput=Vb;var Wb=function(a){function b(b){b=a.call(this,b)||this;return b._mouseInput=null,b._mouseWheelInput=null,b}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Rb),this},b.prototype.addMouse=function(a){return void 0===a&&(a=!0),this._mouseInput||(this._mouseInput=new Tb(a),this.add(this._mouseInput)),this},b.prototype.removeMouse=function(){return this._mouseInput&&this.remove(this._mouseInput),this},b.prototype.addMouseWheel=function(){return this._mouseWheelInput||(this._mouseWheelInput=new Ub,this.add(this._mouseWheelInput)),this},b.prototype.removeMouseWheel=function(){return this._mouseWheelInput&&this.remove(this._mouseWheelInput),this},b.prototype.addTouch=function(){return this.add(new Vb),this},b.prototype.clear=function(){a.prototype.clear.call(this),this._mouseInput=null},b}(Ab);Wb.prototype.addDeviceOrientation=function(){return this._deviceOrientationInput||(this._deviceOrientationInput=new Xb,this.add(this._deviceOrientationInput)),this};var Xb=function(){function a(){var a=this;this._screenOrientationAngle=0,this._screenQuaternion=new g.b,this._alpha=0,this._beta=0,this._gamma=0,this._onDeviceOrientationChangedObservable=new f.c,this._orientationChanged=function(){a._screenOrientationAngle=void 0!==window.orientation?+window.orientation:window.screen.orientation&&window.screen.orientation.angle?window.screen.orientation.angle:0,a._screenOrientationAngle=-T.b.ToRadians(a._screenOrientationAngle/2),a._screenQuaternion.copyFromFloats(0,Math.sin(a._screenOrientationAngle),0,Math.cos(a._screenOrientationAngle))},this._deviceOrientation=function(b){a._alpha=null!==b.alpha?b.alpha:0,a._beta=null!==b.beta?b.beta:0,a._gamma=null!==b.gamma?b.gamma:0,null!==b.alpha&&a._onDeviceOrientationChangedObservable.notifyObservers()},this._constantTranform=new g.b(-Math.sqrt(.5),0,0,Math.sqrt(.5)),this._orientationChanged()}return a.WaitForOrientationChangeAsync=function(a){return new Promise(function(b,c){var d=!1,e=function(){window.removeEventListener("deviceorientation",e),d=!0,b()};a&&setTimeout(function(){d||(window.removeEventListener("deviceorientation",e),c("WaitForOrientationChangeAsync timed out"))},a),"undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"==a?window.addEventListener("deviceorientation",e):T.b.Warn("Permission not granted.")})["catch"](function(a){T.b.Error(a)}):window.addEventListener("deviceorientation",e)})},Object.defineProperty(a.prototype,"camera",{get:function(){return this._camera},set:function(a){var b=this;this._camera=a,null==this._camera||this._camera.rotationQuaternion||(this._camera.rotationQuaternion=new g.b),this._camera&&this._camera.onDisposeObservable.add(function(){b._onDeviceOrientationChangedObservable.clear()})},enumerable:!1,configurable:!0}),a.prototype.attachControl=function(){var a=this,b=this.camera.getScene().getEngine().getHostWindow();if(b){var c=function(){b.addEventListener("orientationchange",a._orientationChanged),b.addEventListener("deviceorientation",a._deviceOrientation),a._orientationChanged()};"undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"===a?c():T.b.Warn("Permission not granted.")})["catch"](function(a){T.b.Error(a)}):c()}},a.prototype.detachControl=function(a){window.removeEventListener("orientationchange",this._orientationChanged),window.removeEventListener("deviceorientation",this._deviceOrientation),this._alpha=0},a.prototype.checkInputs=function(){this._alpha&&(g.b.RotationYawPitchRollToRef(T.b.ToRadians(this._alpha),T.b.ToRadians(this._beta),-T.b.ToRadians(this._gamma),this.camera.rotationQuaternion),this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion),this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform),this._camera.rotationQuaternion.z*=-1,this._camera.rotationQuaternion.w*=-1)},a.prototype.getClassName=function(){return"FreeCameraDeviceOrientationInput"},a.prototype.getSimpleName=function(){return"deviceOrientation"},a}();zb.FreeCameraDeviceOrientationInput=Xb;var Yb=function(){function a(){this.gamepadAngularSensibility=200,this.gamepadMoveSensibility=40,this.deadzoneDelta=.1,this._yAxisScale=1,this._cameraTransform=g.a.Identity(),this._deltaTransform=g.e.Zero(),this._vector3=g.e.Zero(),this._vector2=g.d.Zero()}return Object.defineProperty(a.prototype,"invertYAxis",{get:function(){return 1!==this._yAxisScale},set:function(a){this._yAxisScale=a?-1:1},enumerable:!1,configurable:!0}),a.prototype.attachControl=function(){var a=this,b=this.camera.getScene().gamepadManager;this._onGamepadConnectedObserver=b.onGamepadConnectedObservable.add(function(b){b.type!==Cb.POSE_ENABLED&&(a.gamepad&&b.type!==Cb.XBOX||(a.gamepad=b))}),this._onGamepadDisconnectedObserver=b.onGamepadDisconnectedObservable.add(function(b){a.gamepad===b&&(a.gamepad=null)}),this.gamepad=b.getGamepadByType(Cb.XBOX),!this.gamepad&&b.gamepads.length&&(this.gamepad=b.gamepads[0])},a.prototype.detachControl=function(a){this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver),this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver),this.gamepad=null},a.prototype.checkInputs=function(){if(this.gamepad&&this.gamepad.leftStick){var a=this.camera,b=this.gamepad.leftStick;0!==this.gamepadMoveSensibility&&(b.x=Math.abs(b.x)>this.deadzoneDelta?b.x/this.gamepadMoveSensibility:0,b.y=Math.abs(b.y)>this.deadzoneDelta?b.y/this.gamepadMoveSensibility:0);var c=this.gamepad.rightStick;c&&0!==this.gamepadAngularSensibility?(c.x=Math.abs(c.x)>this.deadzoneDelta?c.x/this.gamepadAngularSensibility:0,c.y=(Math.abs(c.y)>this.deadzoneDelta?c.y/this.gamepadAngularSensibility:0)*this._yAxisScale):c={x:0,y:0},a.rotationQuaternion?a.rotationQuaternion.toRotationMatrix(this._cameraTransform):g.a.RotationYawPitchRollToRef(a.rotation.y,a.rotation.x,0,this._cameraTransform);var d=50*a._computeLocalCameraSpeed();this._vector3.copyFromFloats(b.x*d,0,-b.y*d),g.e.TransformCoordinatesToRef(this._vector3,this._cameraTransform,this._deltaTransform),a.cameraDirection.addInPlace(this._deltaTransform),this._vector2.copyFromFloats(c.y,c.x),a.cameraRotation.addInPlace(this._vector2)}},a.prototype.getClassName=function(){return"FreeCameraGamepadInput"},a.prototype.getSimpleName=function(){return"gamepad"},Object(l.c)([Object(I.d)()],a.prototype,"gamepadAngularSensibility",void 0),Object(l.c)([Object(I.d)()],a.prototype,"gamepadMoveSensibility",void 0),a}();zb.FreeCameraGamepadInput=Yb;var Zb,$b=c(125);!function(a){a[a.X=0]="X",a[a.Y=1]="Y",a[a.Z=2]="Z"}(Zb||(Zb={}));var ac=function(){function a(b,c){var d=this;c=Object(l.a)(Object(l.a)({},a._GetDefaultOptions()),c);if(this._leftJoystick=!!b,a._globalJoystickIndex++,this._axisTargetedByLeftAndRight=Zb.X,this._axisTargetedByUpAndDown=Zb.Y,this.reverseLeftRight=!1,this.reverseUpDown=!1,this._touches=new $b.a,this.deltaPosition=g.e.Zero(),this._joystickSensibility=25,this._inversedSensibility=1/(this._joystickSensibility/1e3),this._onResize=function(b){a.vjCanvasWidth=window.innerWidth,a.vjCanvasHeight=window.innerHeight,a.Canvas&&(a.Canvas.width=a.vjCanvasWidth,a.Canvas.height=a.vjCanvasHeight),a.halfWidth=a.vjCanvasWidth/2},!a.Canvas){window.addEventListener("resize",this._onResize,!1),a.Canvas=document.createElement("canvas"),a.vjCanvasWidth=window.innerWidth,a.vjCanvasHeight=window.innerHeight,a.Canvas.width=window.innerWidth,a.Canvas.height=window.innerHeight,a.Canvas.style.width="100%",a.Canvas.style.height="100%",a.Canvas.style.position="absolute",a.Canvas.style.backgroundColor="transparent",a.Canvas.style.top="0px",a.Canvas.style.left="0px",a.Canvas.style.zIndex="5",a.Canvas.style.msTouchAction="none",a.Canvas.style.touchAction="none",a.Canvas.setAttribute("touch-action","none");b=a.Canvas.getContext("2d");if(!b)throw new Error("Unable to create canvas for virtual joystick");a.vjCanvasContext=b,a.vjCanvasContext.strokeStyle="#ffffff",a.vjCanvasContext.lineWidth=2,document.body.appendChild(a.Canvas)}a.halfWidth=a.Canvas.width/2,this.pressed=!1,this.limitToContainer=c.limitToContainer,this._joystickColor=c.color,this.containerSize=c.containerSize,this.puckSize=c.puckSize,c.position&&this.setPosition(c.position.x,c.position.y),c.puckImage&&this.setPuckImage(c.puckImage),c.containerImage&&this.setContainerImage(c.containerImage),c.alwaysVisible&&a._alwaysVisibleSticks++,this.alwaysVisible=c.alwaysVisible,this._joystickPointerId=-1,this._joystickPointerPos=new g.d(0,0),this._joystickPreviousPointerPos=new g.d(0,0),this._joystickPointerStartPos=new g.d(0,0),this._deltaJoystickVector=new g.d(0,0),this._onPointerDownHandlerRef=function(a){d._onPointerDown(a)},this._onPointerMoveHandlerRef=function(a){d._onPointerMove(a)},this._onPointerUpHandlerRef=function(a){d._onPointerUp(a)},a.Canvas.addEventListener("pointerdown",this._onPointerDownHandlerRef,!1),a.Canvas.addEventListener("pointermove",this._onPointerMoveHandlerRef,!1),a.Canvas.addEventListener("pointerup",this._onPointerUpHandlerRef,!1),a.Canvas.addEventListener("pointerout",this._onPointerUpHandlerRef,!1),a.Canvas.addEventListener("contextmenu",function(a){a.preventDefault()},!1),requestAnimationFrame(function(){d._drawVirtualJoystick()})}return a._GetDefaultOptions=function(){return{puckSize:40,containerSize:60,color:"cyan",puckImage:void 0,containerImage:void 0,position:void 0,alwaysVisible:!1,limitToContainer:!1}},a.prototype.setJoystickSensibility=function(a){this._joystickSensibility=a,this._inversedSensibility=1/(this._joystickSensibility/1e3)},a.prototype._onPointerDown=function(b){b.preventDefault(),(!0===this._leftJoystick?b.clientXa.halfWidth)&&this._joystickPointerId<0?(this._joystickPointerId=b.pointerId,this._joystickPosition?(this._joystickPointerStartPos=this._joystickPosition.clone(),this._joystickPointerPos=this._joystickPosition.clone(),this._joystickPreviousPointerPos=this._joystickPosition.clone(),this._onPointerMove(b)):(this._joystickPointerStartPos.x=b.clientX,this._joystickPointerStartPos.y=b.clientY,this._joystickPointerPos=this._joystickPointerStartPos.clone(),this._joystickPreviousPointerPos=this._joystickPointerStartPos.clone()),this._deltaJoystickVector.x=0,this._deltaJoystickVector.y=0,this.pressed=!0,this._touches.add(b.pointerId.toString(),b)):a._globalJoystickIndex<2&&this._action&&(this._action(),this._touches.add(b.pointerId.toString(),{x:b.clientX,y:b.clientY,prevX:b.clientX,prevY:b.clientY}))},a.prototype._onPointerMove=function(b){if(this._joystickPointerId==b.pointerId){if(this.limitToContainer){var c=new g.d(b.clientX-this._joystickPointerStartPos.x,b.clientY-this._joystickPointerStartPos.y),d=c.length();d>this.containerSize&&c.scaleInPlace(this.containerSize/d),this._joystickPointerPos.x=this._joystickPointerStartPos.x+c.x,this._joystickPointerPos.y=this._joystickPointerStartPos.y+c.y}else this._joystickPointerPos.x=b.clientX,this._joystickPointerPos.y=b.clientY;this._deltaJoystickVector=this._joystickPointerPos.clone(),this._deltaJoystickVector=this._deltaJoystickVector.subtract(this._joystickPointerStartPos),0=0?this.rotation.y=-Math.atan(a.z/a.x)+Math.PI/2:this.rotation.y=-Math.atan(a.z/a.x)-Math.PI/2,this.rotation.z=0,isNaN(this.rotation.x)&&(this.rotation.x=0),isNaN(this.rotation.y)&&(this.rotation.y=0),isNaN(this.rotation.z)&&(this.rotation.z=0),this.rotationQuaternion&&g.b.RotationYawPitchRollToRef(this.rotation.y,this.rotation.x,this.rotation.z,this.rotationQuaternion)},Object.defineProperty(b.prototype,"target",{get:function(){return this.getTarget()},set:function(a){this.setTarget(a)},enumerable:!1,configurable:!0}),b.prototype.getTarget=function(){return this._currentTarget},b.prototype._decideIfNeedsToMove=function(){return Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){if(this.parent)return this.parent.getWorldMatrix().invertToRef(g.c.Matrix[0]),g.e.TransformNormalToRef(this.cameraDirection,g.c.Matrix[0],g.c.Vector3[0]),void this.position.addInPlace(g.c.Vector3[0]);this.position.addInPlace(this.cameraDirection)},b.prototype._checkInputs=function(){var b=this.invertRotation?-this.inverseRotationSpeed:1,c=this._decideIfNeedsToMove(),d=Math.abs(this.cameraRotation.x)>0||Math.abs(this.cameraRotation.y)>0;(c&&this._updatePosition(),d)&&((this.rotationQuaternion&&this.rotationQuaternion.toEulerAnglesToRef(this.rotation),this.rotation.x+=this.cameraRotation.x*b,this.rotation.y+=this.cameraRotation.y*b,!this.noRotationConstraint)&&(this.rotation.x>1.570796&&(this.rotation.x=1.570796),this.rotation.x<-1.570796&&(this.rotation.x=-1.570796)),this.rotationQuaternion&&(this.rotation.lengthSquared()&&g.b.RotationYawPitchRollToRef(this.rotation.y,this.rotation.x,this.rotation.z,this.rotationQuaternion)));c&&(Math.abs(this.cameraDirection.x)S.a.CollisionsEpsilon&&(f.position.addInPlace(f._diffPosition),f.onCollide&&c&&f.onCollide(c))},f.inputs=new Wb(f),f.inputs.addKeyboard().addMouse(),f}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"angularSensibility",{get:function(){var a=this.inputs.attached.mouse;return a?a.angularSensibility:0},set:function(a){var b=this.inputs.attached.mouse;b&&(b.angularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUpward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUpward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUpward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDownward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDownward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDownward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRotateLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRotateLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRotateLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRotateRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRotateRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRotateRight=a)},enumerable:!1,configurable:!0}),b.prototype.attachControl=function(a,b){b=T.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b)},b.prototype.detachControl=function(a){this.inputs.detachElement(),this.cameraDirection=new g.e(0,0,0),this.cameraRotation=new g.d(0,0)},Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._collisionMask},set:function(a){this._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),b.prototype._collideWithWorld=function(a){(this.parent?g.e.TransformCoordinates(this.position,this.parent.getWorldMatrix()):this.position).subtractFromFloatsToRef(0,this.ellipsoid.y,0,this._oldPosition),this._oldPosition.addInPlace(this.ellipsoidOffset);var b=this.getScene().collisionCoordinator;this._collider||(this._collider=b.createCollider()),this._collider._radius=this.ellipsoid,this._collider.collisionMask=this._collisionMask;var c=a;this.applyGravity&&(c=a.add(this.getScene().gravity)),b.getNewPosition(this._oldPosition,c,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)},b.prototype._checkInputs=function(){this._localDirection||(this._localDirection=g.e.Zero(),this._transformedDirection=g.e.Zero()),this.inputs.checkInputs(),a.prototype._checkInputs.call(this)},b.prototype._decideIfNeedsToMove=function(){return this._needMoveForGravity||Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){this.checkCollisions&&this.getScene().collisionsEnabled?this._collideWithWorld(this.cameraDirection):a.prototype._updatePosition.call(this)},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"FreeCamera"},Object(l.c)([Object(I.p)()],b.prototype,"ellipsoid",void 0),Object(l.c)([Object(I.p)()],b.prototype,"ellipsoidOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"checkCollisions",void 0),Object(l.c)([Object(I.d)()],b.prototype,"applyGravity",void 0),b}(cc);K.a.AddNodeConstructor("TouchCamera",function(a,b){return function(){return new ec(a,g.e.Zero(),b)}});var ec=function(a){function b(b,c,d){b=a.call(this,b,c,d)||this;return b.inputs.addTouch(),b._setupInputs(),b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"touchAngularSensibility",{get:function(){var a=this.inputs.attached.touch;return a?a.touchAngularSensibility:0},set:function(a){var b=this.inputs.attached.touch;b&&(b.touchAngularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"touchMoveSensibility",{get:function(){var a=this.inputs.attached.touch;return a?a.touchMoveSensibility:0},set:function(a){var b=this.inputs.attached.touch;b&&(b.touchMoveSensibility=a)},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"TouchCamera"},b.prototype._setupInputs=function(){var a=this.inputs.attached.touch,b=this.inputs.attached.mouse;b?b.touchEnabled=!1:a.allowMouse=!0},b}(dc);K.a.AddNodeConstructor("ArcRotateCamera",function(a,b){return function(){return new fc(a,0,0,1,g.e.Zero(),b)}});var fc=function(a){function b(b,c,d,e,h,i,j){void 0===j&&(j=!0);var k=a.call(this,b,g.e.Zero(),i,j)||this;return k.inertialAlphaOffset=0,k.inertialBetaOffset=0,k.inertialRadiusOffset=0,k.lowerAlphaLimit=null,k.upperAlphaLimit=null,k.lowerBetaLimit=.01,k.upperBetaLimit=Math.PI-.01,k.lowerRadiusLimit=null,k.upperRadiusLimit=null,k.inertialPanningX=0,k.inertialPanningY=0,k.pinchToPanMaxDistance=20,k.panningDistanceLimit=null,k.panningOriginTarget=g.e.Zero(),k.panningInertia=.9,k.zoomOnFactor=1,k.targetScreenOffset=g.d.Zero(),k.allowUpsideDown=!0,k.useInputToRestoreState=!0,k._viewMatrix=new g.a,k.panningAxis=new g.e(1,1,0),k._transformedDirection=new g.e,k.mapPanning=!1,k.onMeshTargetChangedObservable=new f.c,k.checkCollisions=!1,k.collisionRadius=new g.e(.5,.5,.5),k._previousPosition=g.e.Zero(),k._collisionVelocity=g.e.Zero(),k._newPosition=g.e.Zero(),k._computationVector=g.e.Zero(),k._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null),c?(k.setPosition(b),k.onCollide&&k.onCollide(c)):k._previousPosition.copyFrom(k._position);a=Math.cos(k.alpha);b=Math.sin(k.alpha);c=Math.cos(k.beta);var d=Math.sin(k.beta);0===d&&(d=1e-4);var e=k._getTargetPosition();k._computationVector.copyFromFloats(k.radius*a*d,k.radius*c,k.radius*b*d),e.addToRef(k._computationVector,k._newPosition),k._position.copyFrom(k._newPosition);a=k.upVector;k.allowUpsideDown&&k.beta<0&&(a=a.clone().negate()),k._computeViewMatrix(k._position,e,a),k._viewMatrix.addAtIndex(12,k.targetScreenOffset.x),k._viewMatrix.addAtIndex(13,k.targetScreenOffset.y),k._collisionTriggered=!1},k._target=g.e.Zero(),h&&k.setTarget(h),k.alpha=c,k.beta=d,k.radius=e,k.getViewMatrix(),k.inputs=new Kb(k),k.inputs.addKeyboard().addMouseWheel().addPointers(),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"target",{get:function(){return this._target},set:function(a){this.setTarget(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"targetHost",{get:function(){return this._targetHost},set:function(a){a&&this.setTarget(a)},enumerable:!1,configurable:!0}),b.prototype.getTarget=function(){return this.target},Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this.setPosition(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"upVector",{get:function(){return this._upVector},set:function(a){this._upToYMatrix||(this._YToUpMatrix=new g.a,this._upToYMatrix=new g.a,this._upVector=g.e.Zero()),a.normalize(),this._upVector.copyFrom(a),this.setMatUp()},enumerable:!1,configurable:!0}),b.prototype.setMatUp=function(){g.a.RotationAlignToRef(g.e.UpReadOnly,this._upVector,this._YToUpMatrix),g.a.RotationAlignToRef(this._upVector,g.e.UpReadOnly,this._upToYMatrix)},Object.defineProperty(b.prototype,"angularSensibilityX",{get:function(){var a=this.inputs.attached.pointers;return a?a.angularSensibilityX:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.angularSensibilityX=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"angularSensibilityY",{get:function(){var a=this.inputs.attached.pointers;return a?a.angularSensibilityY:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.angularSensibilityY=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pinchPrecision",{get:function(){var a=this.inputs.attached.pointers;return a?a.pinchPrecision:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.pinchPrecision=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pinchDeltaPercentage",{get:function(){var a=this.inputs.attached.pointers;return a?a.pinchDeltaPercentage:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.pinchDeltaPercentage=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useNaturalPinchZoom",{get:function(){var a=this.inputs.attached.pointers;return!!a&&a.useNaturalPinchZoom},set:function(a){var b=this.inputs.attached.pointers;b&&(b.useNaturalPinchZoom=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"panningSensibility",{get:function(){var a=this.inputs.attached.pointers;return a?a.panningSensibility:0},set:function(a){var b=this.inputs.attached.pointers;b&&(b.panningSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wheelPrecision",{get:function(){var a=this.inputs.attached.mousewheel;return a?a.wheelPrecision:0},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.wheelPrecision=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"zoomToMouseLocation",{get:function(){var a=this.inputs.attached.mousewheel;return!!a&&a.zoomToMouseLocation},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.zoomToMouseLocation=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wheelDeltaPercentage",{get:function(){var a=this.inputs.attached.mousewheel;return a?a.wheelDeltaPercentage:0},set:function(a){var b=this.inputs.attached.mousewheel;b&&(b.wheelDeltaPercentage=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bouncingBehavior",{get:function(){return this._bouncingBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useBouncingBehavior",{get:function(){return null!=this._bouncingBehavior},set:function(a){a!==this.useBouncingBehavior&&(a?(this._bouncingBehavior=new Wa,this.addBehavior(this._bouncingBehavior)):this._bouncingBehavior&&(this.removeBehavior(this._bouncingBehavior),this._bouncingBehavior=null))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"framingBehavior",{get:function(){return this._framingBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useFramingBehavior",{get:function(){return null!=this._framingBehavior},set:function(a){a!==this.useFramingBehavior&&(a?(this._framingBehavior=new Xa,this.addBehavior(this._framingBehavior)):this._framingBehavior&&(this.removeBehavior(this._framingBehavior),this._framingBehavior=null))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"autoRotationBehavior",{get:function(){return this._autoRotationBehavior},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useAutoRotationBehavior",{get:function(){return null!=this._autoRotationBehavior},set:function(a){a!==this.useAutoRotationBehavior&&(a?(this._autoRotationBehavior=new Va,this.addBehavior(this._autoRotationBehavior)):this._autoRotationBehavior&&(this.removeBehavior(this._autoRotationBehavior),this._autoRotationBehavior=null))},enumerable:!1,configurable:!0}),b.prototype._initCache=function(){a.prototype._initCache.call(this),this._cache._target=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._cache.alpha=void 0,this._cache.beta=void 0,this._cache.radius=void 0,this._cache.targetScreenOffset=g.d.Zero()},b.prototype._updateCache=function(b){b||a.prototype._updateCache.call(this),this._cache._target.copyFrom(this._getTargetPosition()),this._cache.alpha=this.alpha,this._cache.beta=this.beta,this._cache.radius=this.radius,this._cache.targetScreenOffset.copyFrom(this.targetScreenOffset)},b.prototype._getTargetPosition=function(){if(this._targetHost&&this._targetHost.getAbsolutePosition){var a=this._targetHost.getAbsolutePosition();this._targetBoundingCenter?a.addToRef(this._targetBoundingCenter,this._target):this._target.copyFrom(a)}a=this._getLockedTargetPosition();return a||this._target},b.prototype.storeState=function(){return this._storedAlpha=this.alpha,this._storedBeta=this.beta,this._storedRadius=this.radius,this._storedTarget=this._getTargetPosition().clone(),this._storedTargetScreenOffset=this.targetScreenOffset.clone(),a.prototype.storeState.call(this)},b.prototype._restoreStateValues=function(){return!!a.prototype._restoreStateValues.call(this)&&(this.setTarget(this._storedTarget.clone()),this.alpha=this._storedAlpha,this.beta=this._storedBeta,this.radius=this._storedRadius,this.targetScreenOffset=this._storedTargetScreenOffset.clone(),this.inertialAlphaOffset=0,this.inertialBetaOffset=0,this.inertialRadiusOffset=0,this.inertialPanningX=0,this.inertialPanningY=0,!0)},b.prototype._isSynchronizedViewMatrix=function(){return!!a.prototype._isSynchronizedViewMatrix.call(this)&&this._cache._target.equals(this._getTargetPosition())&&this._cache.alpha===this.alpha&&this._cache.beta===this.beta&&this._cache.radius===this.radius&&this._cache.targetScreenOffset.equals(this.targetScreenOffset)},b.prototype.attachControl=function(a,b,c,d){var e=this;void 0===c&&(c=!0),void 0===d&&(d=2),b=T.b.BackCompatCameraNoPreventDefault(arguments),this._useCtrlForPanning=c,this._panningMouseButton=d,"boolean"==typeof arguments[0]&&(arguments.length>1&&(this._useCtrlForPanning=arguments[1]),arguments.length>2&&(this._panningMouseButton=arguments[2])),this.inputs.attachElement(b),this._reset=function(){e.inertialAlphaOffset=0,e.inertialBetaOffset=0,e.inertialRadiusOffset=0,e.inertialPanningX=0,e.inertialPanningY=0}},b.prototype.detachControl=function(a){this.inputs.detachElement(),this._reset&&this._reset()},b.prototype._checkInputs=function(){if(!this._collisionTriggered){if(this.inputs.checkInputs(),0!==this.inertialAlphaOffset||0!==this.inertialBetaOffset||0!==this.inertialRadiusOffset){var b=this.inertialAlphaOffset;this.beta<=0&&(b*=-1),this.getScene().useRightHandedSystem&&(b*=-1),this.parent&&this.parent._getWorldMatrixDeterminant()<0&&(b*=-1),this.alpha+=b,this.beta+=this.inertialBetaOffset,this.radius-=this.inertialRadiusOffset,this.inertialAlphaOffset*=this.inertia,this.inertialBetaOffset*=this.inertia,this.inertialRadiusOffset*=this.inertia,Math.abs(this.inertialAlphaOffset)Math.PI&&(this.beta=this.beta-2*Math.PI):this.betathis.upperBetaLimit&&(this.beta=this.upperBetaLimit),null!==this.lowerAlphaLimit&&this.alphathis.upperAlphaLimit&&(this.alpha=this.upperAlphaLimit),null!==this.lowerRadiusLimit&&this.radiusthis.upperRadiusLimit&&(this.radius=this.upperRadiusLimit,this.inertialRadiusOffset=0)},b.prototype.rebuildAnglesAndRadius=function(){this._position.subtractToRef(this._getTargetPosition(),this._computationVector),0===this._upVector.x&&1===this._upVector.y&&0===this._upVector.z||g.e.TransformCoordinatesToRef(this._computationVector,this._upToYMatrix,this._computationVector),this.radius=this._computationVector.length(),0===this.radius&&(this.radius=1e-4);var a=this.alpha;0===this._computationVector.x&&0===this._computationVector.z?this.alpha=Math.PI/2:this.alpha=Math.acos(this._computationVector.x/Math.sqrt(Math.pow(this._computationVector.x,2)+Math.pow(this._computationVector.z,2))),this._computationVector.z<0&&(this.alpha=2*Math.PI-this.alpha);a=Math.round((a-this.alpha)/(2*Math.PI));this.alpha+=2*a*Math.PI,this.beta=Math.acos(this._computationVector.y/this.radius),this._checkLimits()},b.prototype.setPosition=function(a){this._position.equals(a)||(this._position.copyFrom(a),this.rebuildAnglesAndRadius())},b.prototype.setTarget=function(a,b,c){if(void 0===b&&(b=!1),void 0===c&&(c=!1),a.getBoundingInfo)this._targetBoundingCenter=b?a.getBoundingInfo().boundingBox.centerWorld.clone():null,a.computeWorldMatrix(),this._targetHost=a,this._target=this._getTargetPosition(),this.onMeshTargetChangedObservable.notifyObservers(this._targetHost);else{b=a;a=this._getTargetPosition();if(a&&!c&&a.equals(b))return;this._targetHost=null,this._target=b,this._targetBoundingCenter=null,this.onMeshTargetChangedObservable.notifyObservers(null)}this.rebuildAnglesAndRadius()},b.prototype._getViewMatrix=function(){var a=Math.cos(this.alpha),b=Math.sin(this.alpha),c=Math.cos(this.beta),d=Math.sin(this.beta);0===d&&(d=1e-4),0===this.radius&&(this.radius=1e-4);var e=this._getTargetPosition();if(this._computationVector.copyFromFloats(this.radius*a*d,this.radius*c,this.radius*b*d),0===this._upVector.x&&1===this._upVector.y&&0===this._upVector.z||g.e.TransformCoordinatesToRef(this._computationVector,this._YToUpMatrix,this._computationVector),e.addToRef(this._computationVector,this._newPosition),this.getScene().collisionsEnabled&&this.checkCollisions){a=this.getScene().collisionCoordinator;this._collider||(this._collider=a.createCollider()),this._collider._radius=this.collisionRadius,this._newPosition.subtractToRef(this._position,this._collisionVelocity),this._collisionTriggered=!0,a.getNewPosition(this._position,this._collisionVelocity,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)}else{this._position.copyFrom(this._newPosition);c=this.upVector;this.allowUpsideDown&&d<0&&(c=c.negate()),this._computeViewMatrix(this._position,e,c),this._viewMatrix.addAtIndex(12,this.targetScreenOffset.x),this._viewMatrix.addAtIndex(13,this.targetScreenOffset.y)}return this._currentTarget=e,this._viewMatrix},b.prototype.zoomOn=function(a,b){void 0===b&&(b=!1),a=a||this.getScene().meshes;a=R.a.MinMax(a);var c=g.e.Distance(a.min,a.max);this.radius=c*this.zoomOnFactor,this.focusOn({min:a.min,max:a.max,distance:c},b)},b.prototype.focusOn=function(a,b){var c;if(void 0===b&&(b=!1),void 0===a.min){var d=a||this.getScene().meshes;d=R.a.MinMax(d),c=g.e.Distance(d.min,d.max)}else d=a,c=a.distance;this._target=R.a.Center(d),b||(this.maxZ=2*c)},b.prototype.createRigCamera=function(a,c){var d=0;switch(this.cameraRigMode){case db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH:case db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:case db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER:case db.a.RIG_MODE_STEREOSCOPIC_INTERLACED:case db.a.RIG_MODE_VR:d=this._cameraRigParams.stereoHalfAngle*(0===c?1:-1);break;case db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:d=this._cameraRigParams.stereoHalfAngle*(0===c?-1:1)}c=new b(a,this.alpha+d,this.beta,this.radius,this._target,this.getScene());return c._cameraRigParams={},c.isRigCamera=!0,c.rigParent=this,c.upVector=this.upVector,c},b.prototype._updateRigCameras=function(){var b=this._rigCameras[0],c=this._rigCameras[1];switch(b.beta=c.beta=this.beta,this.cameraRigMode){case db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH:case db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:case db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER:case db.a.RIG_MODE_STEREOSCOPIC_INTERLACED:case db.a.RIG_MODE_VR:b.alpha=this.alpha-this._cameraRigParams.stereoHalfAngle,c.alpha=this.alpha+this._cameraRigParams.stereoHalfAngle;break;case db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:b.alpha=this.alpha+this._cameraRigParams.stereoHalfAngle,c.alpha=this.alpha-this._cameraRigParams.stereoHalfAngle}a.prototype._updateRigCameras.call(this)},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"ArcRotateCamera"},Object(l.c)([Object(I.d)()],b.prototype,"alpha",void 0),Object(l.c)([Object(I.d)()],b.prototype,"beta",void 0),Object(l.c)([Object(I.d)()],b.prototype,"radius",void 0),Object(l.c)([Object(I.p)("target")],b.prototype,"_target",void 0),Object(l.c)([Object(I.l)("targetHost")],b.prototype,"_targetHost",void 0),Object(l.c)([Object(I.d)()],b.prototype,"inertialAlphaOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"inertialBetaOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"inertialRadiusOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerAlphaLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperAlphaLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerBetaLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperBetaLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerRadiusLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperRadiusLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"inertialPanningX",void 0),Object(l.c)([Object(I.d)()],b.prototype,"inertialPanningY",void 0),Object(l.c)([Object(I.d)()],b.prototype,"pinchToPanMaxDistance",void 0),Object(l.c)([Object(I.d)()],b.prototype,"panningDistanceLimit",void 0),Object(l.c)([Object(I.p)()],b.prototype,"panningOriginTarget",void 0),Object(l.c)([Object(I.d)()],b.prototype,"panningInertia",void 0),Object(l.c)([Object(I.d)()],b.prototype,"zoomToMouseLocation",null),Object(l.c)([Object(I.d)()],b.prototype,"zoomOnFactor",void 0),Object(l.c)([Object(I.d)()],b.prototype,"targetScreenOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"allowUpsideDown",void 0),Object(l.c)([Object(I.d)()],b.prototype,"useInputToRestoreState",void 0),b}(cc);K.a.AddNodeConstructor("DeviceOrientationCamera",function(a,b){return function(){return new gc(a,g.e.Zero(),b)}});var gc=function(a){function b(b,c,d){var e=a.call(this,b,c,d)||this;return e._tmpDragQuaternion=new g.b,e._disablePointerInputWhenUsingDeviceOrientation=!0,e._dragFactor=0,e._quaternionCache=new g.b,e.inputs.addDeviceOrientation(),e.inputs._deviceOrientationInput&&e.inputs._deviceOrientationInput._onDeviceOrientationChangedObservable.addOnce(function(){e._disablePointerInputWhenUsingDeviceOrientation&&e.inputs._mouseInput&&(e.inputs._mouseInput._allowCameraRotation=!1,e.inputs._mouseInput.onPointerMovedObservable.add(function(a){0!=e._dragFactor&&(e._initialQuaternion||(e._initialQuaternion=new g.b),g.b.FromEulerAnglesToRef(0,a.offsetX*e._dragFactor,0,e._tmpDragQuaternion),e._initialQuaternion.multiplyToRef(e._tmpDragQuaternion,e._initialQuaternion))}))}),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"disablePointerInputWhenUsingDeviceOrientation",{get:function(){return this._disablePointerInputWhenUsingDeviceOrientation},set:function(a){this._disablePointerInputWhenUsingDeviceOrientation=a},enumerable:!1,configurable:!0}),b.prototype.enableHorizontalDragging=function(a){void 0===a&&(a=1/300),this._dragFactor=a},b.prototype.getClassName=function(){return"DeviceOrientationCamera"},b.prototype._checkInputs=function(){a.prototype._checkInputs.call(this),this._quaternionCache.copyFrom(this.rotationQuaternion),this._initialQuaternion&&this._initialQuaternion.multiplyToRef(this.rotationQuaternion,this.rotationQuaternion)},b.prototype.resetToCurrentRotation=function(a){var b=this;void 0===a&&(a=Q.a.Y),this.rotationQuaternion&&(this._initialQuaternion||(this._initialQuaternion=new g.b),this._initialQuaternion.copyFrom(this._quaternionCache||this.rotationQuaternion),["x","y","z"].forEach(function(c){a[c]?b._initialQuaternion[c]*=-1:b._initialQuaternion[c]=0}),this._initialQuaternion.normalize(),this._initialQuaternion.multiplyToRef(this.rotationQuaternion,this.rotationQuaternion))},b}(dc),hc=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Mb),this},b.prototype.addMouse=function(a){return void 0===a&&(a=!0),this.add(new Nb(a)),this},b}(Ab),ic=function(a){function b(b,c,d,e){void 0===e&&(e=!0);var f=a.call(this,b,c,d,e)||this;return f.ellipsoid=new g.e(1,1,1),f.ellipsoidOffset=new g.e(0,0,0),f.checkCollisions=!1,f.applyGravity=!1,f.cameraDirection=g.e.Zero(),f._trackRoll=0,f.rollCorrect=100,f.bankedTurn=!1,f.bankedTurnLimit=Math.PI/2,f.bankedTurnMultiplier=1,f._needMoveForGravity=!1,f._oldPosition=g.e.Zero(),f._diffPosition=g.e.Zero(),f._newPosition=g.e.Zero(),f._collisionMask=-1,f._onCollisionPositionChange=function(a,b,c){void 0===c&&(c=null);a=b,f._newPosition.copyFrom(a),f._newPosition.subtractToRef(f._oldPosition,f._diffPosition),f._diffPosition.length()>S.a.CollisionsEpsilon&&(f.position.addInPlace(f._diffPosition),f.onCollide&&c&&f.onCollide(c))},f.inputs=new hc(f),f.inputs.addKeyboard().addMouse(),f}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"angularSensibility",{get:function(){var a=this.inputs.attached.mouse;return a?a.angularSensibility:0},set:function(a){var b=this.inputs.attached.mouse;b&&(b.angularSensibility=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysForward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysForward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysForward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysBackward",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysBackward:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysBackward=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysUp",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysUp:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysUp=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysDown",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysDown:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysDown=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysLeft",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysLeft:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysLeft=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"keysRight",{get:function(){var a=this.inputs.attached.keyboard;return a?a.keysRight:[]},set:function(a){var b=this.inputs.attached.keyboard;b&&(b.keysRight=a)},enumerable:!1,configurable:!0}),b.prototype.attachControl=function(a,b){b=T.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b)},b.prototype.detachControl=function(){this.inputs.detachElement(),this.cameraDirection=new g.e(0,0,0)},Object.defineProperty(b.prototype,"collisionMask",{get:function(){return this._collisionMask},set:function(a){this._collisionMask=isNaN(a)?-1:a},enumerable:!1,configurable:!0}),b.prototype._collideWithWorld=function(a){(this.parent?g.e.TransformCoordinates(this.position,this.parent.getWorldMatrix()):this.position).subtractFromFloatsToRef(0,this.ellipsoid.y,0,this._oldPosition),this._oldPosition.addInPlace(this.ellipsoidOffset);var b=this.getScene().collisionCoordinator;this._collider||(this._collider=b.createCollider()),this._collider._radius=this.ellipsoid,this._collider.collisionMask=this._collisionMask;var c=a;this.applyGravity&&(c=a.add(this.getScene().gravity)),b.getNewPosition(this._oldPosition,c,this._collider,3,null,this._onCollisionPositionChange,this.uniqueId)},b.prototype._checkInputs=function(){this._localDirection||(this._localDirection=g.e.Zero(),this._transformedDirection=g.e.Zero()),this.inputs.checkInputs(),a.prototype._checkInputs.call(this)},b.prototype._decideIfNeedsToMove=function(){return this._needMoveForGravity||Math.abs(this.cameraDirection.x)>0||Math.abs(this.cameraDirection.y)>0||Math.abs(this.cameraDirection.z)>0},b.prototype._updatePosition=function(){this.checkCollisions&&this.getScene().collisionsEnabled?this._collideWithWorld(this.cameraDirection):a.prototype._updatePosition.call(this)},b.prototype.restoreRoll=function(a){var b=this._trackRoll,c=b-this.rotation.z;Math.abs(c)>=.001&&(this.rotation.z+=c/a,Math.abs(b-this.rotation.z)<=.001&&(this.rotation.z=b))},b.prototype.dispose=function(){this.inputs.clear(),a.prototype.dispose.call(this)},b.prototype.getClassName=function(){return"FlyCamera"},Object(l.c)([Object(I.p)()],b.prototype,"ellipsoid",void 0),Object(l.c)([Object(I.p)()],b.prototype,"ellipsoidOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"checkCollisions",void 0),Object(l.c)([Object(I.d)()],b.prototype,"applyGravity",void 0),b}(cc),jc=function(a){function b(b){return a.call(this,b)||this}return Object(l.d)(b,a),b.prototype.addKeyboard=function(){return this.add(new Ob),this},b.prototype.addMouseWheel=function(){return this.add(new Pb),this},b.prototype.addPointers=function(){return this.add(new Qb),this},b.prototype.addVRDeviceOrientation=function(){return!1,this},b}(Ab);K.a.AddNodeConstructor("FollowCamera",function(a,b){return function(){return new lc(a,g.e.Zero(),b)}}),K.a.AddNodeConstructor("ArcFollowCamera",function(a,b){return function(){return new mc(a,0,0,1,null,b)}});var kc,lc=function(a){function b(b,c,d,e){void 0===e&&(e=null);b=a.call(this,b,c,d)||this;return b.radius=12,b.lowerRadiusLimit=null,b.upperRadiusLimit=null,b.rotationOffset=0,b.lowerRotationOffsetLimit=null,b.upperRotationOffsetLimit=null,b.heightOffset=4,b.lowerHeightOffsetLimit=null,b.upperHeightOffsetLimit=null,b.cameraAcceleration=.05,b.maxCameraSpeed=20,b.lockedTarget=e,b.inputs=new jc(b),b.inputs.addKeyboard().addMouseWheel().addPointers(),b}return Object(l.d)(b,a),b.prototype._follow=function(a){if(a){var b=g.c.Matrix[0];a.absoluteRotationQuaternion.toRotationMatrix(b);b=Math.atan2(b.m[8],b.m[10]);b=T.b.ToRadians(this.rotationOffset)+b;a=a.getAbsolutePosition();var c=a.x+Math.sin(b)*this.radius;b=a.z+Math.cos(b)*this.radius;c=c-this.position.x;var d=a.y+this.heightOffset-this.position.y;b=b-this.position.z;c=c*this.cameraAcceleration*2;d=d*this.cameraAcceleration;b=b*this.cameraAcceleration*2;(c>this.maxCameraSpeed||c<-this.maxCameraSpeed)&&(c=c<1?-this.maxCameraSpeed:this.maxCameraSpeed),(d>this.maxCameraSpeed||d<-this.maxCameraSpeed)&&(d=d<1?-this.maxCameraSpeed:this.maxCameraSpeed),(b>this.maxCameraSpeed||b<-this.maxCameraSpeed)&&(b=b<1?-this.maxCameraSpeed:this.maxCameraSpeed),this.position=new g.e(this.position.x+c,this.position.y+d,this.position.z+b),this.setTarget(a)}},b.prototype.attachControl=function(a,b){b=T.b.BackCompatCameraNoPreventDefault(arguments),this.inputs.attachElement(b),this._reset=function(){}},b.prototype.detachControl=function(a){this.inputs.detachElement(),this._reset&&this._reset()},b.prototype._checkInputs=function(){this.inputs.checkInputs(),this._checkLimits(),a.prototype._checkInputs.call(this),this.lockedTarget&&this._follow(this.lockedTarget)},b.prototype._checkLimits=function(){null!==this.lowerRadiusLimit&&this.radiusthis.upperRadiusLimit&&(this.radius=this.upperRadiusLimit),null!==this.lowerHeightOffsetLimit&&this.heightOffsetthis.upperHeightOffsetLimit&&(this.heightOffset=this.upperHeightOffsetLimit),null!==this.lowerRotationOffsetLimit&&this.rotationOffsetthis.upperRotationOffsetLimit&&(this.rotationOffset=this.upperRotationOffsetLimit)},b.prototype.getClassName=function(){return"FollowCamera"},Object(l.c)([Object(I.d)()],b.prototype,"radius",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerRadiusLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperRadiusLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"rotationOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerRotationOffsetLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperRotationOffsetLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"heightOffset",void 0),Object(l.c)([Object(I.d)()],b.prototype,"lowerHeightOffsetLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"upperHeightOffsetLimit",void 0),Object(l.c)([Object(I.d)()],b.prototype,"cameraAcceleration",void 0),Object(l.c)([Object(I.d)()],b.prototype,"maxCameraSpeed",void 0),Object(l.c)([Object(I.l)("lockedTargetId")],b.prototype,"lockedTarget",void 0),b}(cc),mc=function(a){function b(b,c,d,e,f,h){b=a.call(this,b,g.e.Zero(),h)||this;return b.alpha=c,b.beta=d,b.radius=e,b._cartesianCoordinates=g.e.Zero(),b.setMeshTarget(f),b}return Object(l.d)(b,a),b.prototype.setMeshTarget=function(a){this._meshTarget=a,this._follow()},b.prototype._follow=function(){if(this._meshTarget){this._cartesianCoordinates.x=this.radius*Math.cos(this.alpha)*Math.cos(this.beta),this._cartesianCoordinates.y=this.radius*Math.sin(this.beta),this._cartesianCoordinates.z=this.radius*Math.sin(this.alpha)*Math.cos(this.beta);var a=this._meshTarget.getAbsolutePosition();this.position=a.add(this._cartesianCoordinates),this.setTarget(a)}},b.prototype._checkInputs=function(){a.prototype._checkInputs.call(this),this._follow()},b.prototype.getClassName=function(){return"ArcFollowCamera"},b}(cc),nc=c(42);!function(a){a[a.VIVE=0]="VIVE",a[a.OCULUS=1]="OCULUS",a[a.WINDOWS=2]="WINDOWS",a[a.GEAR_VR=3]="GEAR_VR",a[a.DAYDREAM=4]="DAYDREAM",a[a.GENERIC=5]="GENERIC"}(kc||(kc={}));var oc,pc,qc=function(){function a(){}return a.InitiateController=function(a){for(var b=0,c=this._ControllerFactories;bthis._maxRotationDistFromHeadset){a=a-(a<0?-this._maxRotationDistFromHeadset:this._maxRotationDistFromHeadset);this._draggedRoomRotation+=a;var c=Math.sin(-a);a=Math.cos(-a);this._calculatedPosition.x=this._calculatedPosition.x*a-this._calculatedPosition.z*c,this._calculatedPosition.z=this._calculatedPosition.x*c+this._calculatedPosition.z*a}}g.e.TransformCoordinatesToRef(this._calculatedPosition,this._deviceToWorld,this.devicePosition),this._deviceToWorld.getRotationMatrixToRef(this._workingMatrix),g.b.FromRotationMatrixToRef(this._workingMatrix,this.deviceRotationQuaternion),this.deviceRotationQuaternion.multiplyInPlace(this._calculatedRotation),this._mesh&&(this._mesh.position.copyFrom(this.devicePosition),this._mesh.rotationQuaternion&&this._mesh.rotationQuaternion.copyFrom(this.deviceRotationQuaternion))}},b.prototype.updateFromDevice=function(a){if(!this.isXR&&a){this.rawPose=a,a.position&&(this._deviceRoomPosition.copyFromFloats(a.position[0],a.position[1],-a.position[2]),this._mesh&&this._mesh.getScene().useRightHandedSystem&&(this._deviceRoomPosition.z*=-1),this._trackPosition&&this._deviceRoomPosition.scaleToRef(this.deviceScaleFactor,this._calculatedPosition),this._calculatedPosition.addInPlace(this.position));var b=this.rawPose;a.orientation&&b.orientation&&4===b.orientation.length&&(this._deviceRoomRotationQuaternion.copyFromFloats(b.orientation[0],b.orientation[1],-b.orientation[2],-b.orientation[3]),this._mesh&&(this._mesh.getScene().useRightHandedSystem?(this._deviceRoomRotationQuaternion.z*=-1,this._deviceRoomRotationQuaternion.w*=-1):this._deviceRoomRotationQuaternion.multiplyToRef(this._leftHandSystemQuaternion,this._deviceRoomRotationQuaternion)),this._deviceRoomRotationQuaternion.multiplyToRef(this.rotationQuaternion,this._calculatedRotation))}},b.prototype.attachToMesh=function(a){if(this._mesh&&(this._mesh.parent=null),this._mesh=a,this._poseControlledCamera&&(this._mesh.parent=this._poseControlledCamera),this._mesh.rotationQuaternion||(this._mesh.rotationQuaternion=new g.b),!this.isXR&&(this._updatePoseAndMesh(),this._pointingPoseNode)){for(var b=[],c=this._pointingPoseNode;c.parent;)b.push(c.parent),c=c.parent;b.reverse().forEach(function(a){a.computeWorldMatrix(!0)})}this._meshAttachedObservable.notifyObservers(a)},b.prototype.attachToPoseControlledCamera=function(a){this._poseControlledCamera=a,this._mesh&&(this._mesh.parent=this._poseControlledCamera)},b.prototype.dispose=function(){this._mesh&&this._mesh.dispose(),this._mesh=null,a.prototype.dispose.call(this)},Object.defineProperty(b.prototype,"mesh",{get:function(){return this._mesh},enumerable:!1,configurable:!0}),b.prototype.getForwardRay=function(a){if(void 0===a&&(a=100),!this.mesh)return new nc.a(g.e.Zero(),new g.e(0,0,1),a);var b=this._pointingPoseNode?this._pointingPoseNode.getWorldMatrix():this.mesh.getWorldMatrix(),c=b.getTranslation(),d=new g.e(0,0,-1);d=g.e.TransformNormal(d,b);b=g.e.Normalize(d);return new nc.a(c,b,a)},b.POINTING_POSE="POINTING_POSE",b}(Cb);!function(a){a[a.A=0]="A",a[a.B=1]="B",a[a.X=2]="X",a[a.Y=3]="Y",a[a.LB=4]="LB",a[a.RB=5]="RB",a[a.Back=8]="Back",a[a.Start=9]="Start",a[a.LeftStick=10]="LeftStick",a[a.RightStick=11]="RightStick"}(oc||(oc={})),(function(a){a[a.Up=12]="Up",a[a.Down=13]="Down",a[a.Left=14]="Left",a[a.Right=15]="Right"})(pc||(pc={}));var sc,tc,uc=function(a){function b(b,c,d,e){void 0===e&&(e=!1);b=a.call(this,b,c,d,0,1,2,3)||this;return b._leftTrigger=0,b._rightTrigger=0,b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.onPadDownObservable=new f.c,b.onPadUpObservable=new f.c,b._buttonA=0,b._buttonB=0,b._buttonX=0,b._buttonY=0,b._buttonBack=0,b._buttonStart=0,b._buttonLB=0,b._buttonRB=0,b._buttonLeftStick=0,b._buttonRightStick=0,b._dPadUp=0,b._dPadDown=0,b._dPadLeft=0,b._dPadRight=0,b._isXboxOnePad=!1,b.type=Cb.XBOX,b._isXboxOnePad=e,b}return Object(l.d)(b,a),b.prototype.onlefttriggerchanged=function(a){this._onlefttriggerchanged=a},b.prototype.onrighttriggerchanged=function(a){this._onrighttriggerchanged=a},Object.defineProperty(b.prototype,"leftTrigger",{get:function(){return this._leftTrigger},set:function(a){this._onlefttriggerchanged&&this._leftTrigger!==a&&this._onlefttriggerchanged(a),this._leftTrigger=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightTrigger",{get:function(){return this._rightTrigger},set:function(a){this._onrighttriggerchanged&&this._rightTrigger!==a&&this._onrighttriggerchanged(a),this._rightTrigger=a},enumerable:!1,configurable:!0}),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype.ondpaddown=function(a){this._ondpaddown=a},b.prototype.ondpadup=function(a){this._ondpadup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype._setDPadValue=function(a,b,c){return a!==b&&(1===a&&(this._ondpaddown&&this._ondpaddown(c),this.onPadDownObservable.notifyObservers(c)),0===a&&(this._ondpadup&&this._ondpadup(c),this.onPadUpObservable.notifyObservers(c))),a},Object.defineProperty(b.prototype,"buttonA",{get:function(){return this._buttonA},set:function(a){this._buttonA=this._setButtonValue(a,this._buttonA,oc.A)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonB",{get:function(){return this._buttonB},set:function(a){this._buttonB=this._setButtonValue(a,this._buttonB,oc.B)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonX",{get:function(){return this._buttonX},set:function(a){this._buttonX=this._setButtonValue(a,this._buttonX,oc.X)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonY",{get:function(){return this._buttonY},set:function(a){this._buttonY=this._setButtonValue(a,this._buttonY,oc.Y)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonStart",{get:function(){return this._buttonStart},set:function(a){this._buttonStart=this._setButtonValue(a,this._buttonStart,oc.Start)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonBack",{get:function(){return this._buttonBack},set:function(a){this._buttonBack=this._setButtonValue(a,this._buttonBack,oc.Back)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLB",{get:function(){return this._buttonLB},set:function(a){this._buttonLB=this._setButtonValue(a,this._buttonLB,oc.LB)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRB",{get:function(){return this._buttonRB},set:function(a){this._buttonRB=this._setButtonValue(a,this._buttonRB,oc.RB)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLeftStick",{get:function(){return this._buttonLeftStick},set:function(a){this._buttonLeftStick=this._setButtonValue(a,this._buttonLeftStick,oc.LeftStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRightStick",{get:function(){return this._buttonRightStick},set:function(a){this._buttonRightStick=this._setButtonValue(a,this._buttonRightStick,oc.RightStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadUp",{get:function(){return this._dPadUp},set:function(a){this._dPadUp=this._setDPadValue(a,this._dPadUp,pc.Up)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadDown",{get:function(){return this._dPadDown},set:function(a){this._dPadDown=this._setDPadValue(a,this._dPadDown,pc.Down)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadLeft",{get:function(){return this._dPadLeft},set:function(a){this._dPadLeft=this._setDPadValue(a,this._dPadLeft,pc.Left)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadRight",{get:function(){return this._dPadRight},set:function(a){this._dPadRight=this._setDPadValue(a,this._dPadRight,pc.Right)},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this),this._isXboxOnePad,this.buttonA=this.browserGamepad.buttons[0].value,this.buttonB=this.browserGamepad.buttons[1].value,this.buttonX=this.browserGamepad.buttons[2].value,this.buttonY=this.browserGamepad.buttons[3].value,this.buttonLB=this.browserGamepad.buttons[4].value,this.buttonRB=this.browserGamepad.buttons[5].value,this.leftTrigger=this.browserGamepad.buttons[6].value,this.rightTrigger=this.browserGamepad.buttons[7].value,this.buttonBack=this.browserGamepad.buttons[8].value,this.buttonStart=this.browserGamepad.buttons[9].value,this.buttonLeftStick=this.browserGamepad.buttons[10].value,this.buttonRightStick=this.browserGamepad.buttons[11].value,this.dPadUp=this.browserGamepad.buttons[12].value,this.dPadDown=this.browserGamepad.buttons[13].value,this.dPadLeft=this.browserGamepad.buttons[14].value,this.dPadRight=this.browserGamepad.buttons[15].value},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.onButtonDownObservable.clear(),this.onButtonUpObservable.clear(),this.onPadDownObservable.clear(),this.onPadUpObservable.clear()},b}(Cb);!function(a){a[a.Cross=0]="Cross",a[a.Circle=1]="Circle",a[a.Square=2]="Square",a[a.Triangle=3]="Triangle",a[a.L1=4]="L1",a[a.R1=5]="R1",a[a.Share=8]="Share",a[a.Options=9]="Options",a[a.LeftStick=10]="LeftStick",a[a.RightStick=11]="RightStick"}(sc||(sc={})),(function(a){a[a.Up=12]="Up",a[a.Down=13]="Down",a[a.Left=14]="Left",a[a.Right=15]="Right"})(tc||(tc={}));var vc=function(a){function b(b,c,d){b=a.call(this,b.replace("STANDARD GAMEPAD","SONY PLAYSTATION DUALSHOCK"),c,d,0,1,2,3)||this;return b._leftTrigger=0,b._rightTrigger=0,b.onButtonDownObservable=new f.c,b.onButtonUpObservable=new f.c,b.onPadDownObservable=new f.c,b.onPadUpObservable=new f.c,b._buttonCross=0,b._buttonCircle=0,b._buttonSquare=0,b._buttonTriangle=0,b._buttonShare=0,b._buttonOptions=0,b._buttonL1=0,b._buttonR1=0,b._buttonLeftStick=0,b._buttonRightStick=0,b._dPadUp=0,b._dPadDown=0,b._dPadLeft=0,b._dPadRight=0,b.type=Cb.DUALSHOCK,b}return Object(l.d)(b,a),b.prototype.onlefttriggerchanged=function(a){this._onlefttriggerchanged=a},b.prototype.onrighttriggerchanged=function(a){this._onrighttriggerchanged=a},Object.defineProperty(b.prototype,"leftTrigger",{get:function(){return this._leftTrigger},set:function(a){this._onlefttriggerchanged&&this._leftTrigger!==a&&this._onlefttriggerchanged(a),this._leftTrigger=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rightTrigger",{get:function(){return this._rightTrigger},set:function(a){this._onrighttriggerchanged&&this._rightTrigger!==a&&this._onrighttriggerchanged(a),this._rightTrigger=a},enumerable:!1,configurable:!0}),b.prototype.onbuttondown=function(a){this._onbuttondown=a},b.prototype.onbuttonup=function(a){this._onbuttonup=a},b.prototype.ondpaddown=function(a){this._ondpaddown=a},b.prototype.ondpadup=function(a){this._ondpadup=a},b.prototype._setButtonValue=function(a,b,c){return a!==b&&(1===a&&(this._onbuttondown&&this._onbuttondown(c),this.onButtonDownObservable.notifyObservers(c)),0===a&&(this._onbuttonup&&this._onbuttonup(c),this.onButtonUpObservable.notifyObservers(c))),a},b.prototype._setDPadValue=function(a,b,c){return a!==b&&(1===a&&(this._ondpaddown&&this._ondpaddown(c),this.onPadDownObservable.notifyObservers(c)),0===a&&(this._ondpadup&&this._ondpadup(c),this.onPadUpObservable.notifyObservers(c))),a},Object.defineProperty(b.prototype,"buttonCross",{get:function(){return this._buttonCross},set:function(a){this._buttonCross=this._setButtonValue(a,this._buttonCross,sc.Cross)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonCircle",{get:function(){return this._buttonCircle},set:function(a){this._buttonCircle=this._setButtonValue(a,this._buttonCircle,sc.Circle)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonSquare",{get:function(){return this._buttonSquare},set:function(a){this._buttonSquare=this._setButtonValue(a,this._buttonSquare,sc.Square)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonTriangle",{get:function(){return this._buttonTriangle},set:function(a){this._buttonTriangle=this._setButtonValue(a,this._buttonTriangle,sc.Triangle)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonOptions",{get:function(){return this._buttonOptions},set:function(a){this._buttonOptions=this._setButtonValue(a,this._buttonOptions,sc.Options)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonShare",{get:function(){return this._buttonShare},set:function(a){this._buttonShare=this._setButtonValue(a,this._buttonShare,sc.Share)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonL1",{get:function(){return this._buttonL1},set:function(a){this._buttonL1=this._setButtonValue(a,this._buttonL1,sc.L1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonR1",{get:function(){return this._buttonR1},set:function(a){this._buttonR1=this._setButtonValue(a,this._buttonR1,sc.R1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonLeftStick",{get:function(){return this._buttonLeftStick},set:function(a){this._buttonLeftStick=this._setButtonValue(a,this._buttonLeftStick,sc.LeftStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"buttonRightStick",{get:function(){return this._buttonRightStick},set:function(a){this._buttonRightStick=this._setButtonValue(a,this._buttonRightStick,sc.RightStick)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadUp",{get:function(){return this._dPadUp},set:function(a){this._dPadUp=this._setDPadValue(a,this._dPadUp,tc.Up)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadDown",{get:function(){return this._dPadDown},set:function(a){this._dPadDown=this._setDPadValue(a,this._dPadDown,tc.Down)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadLeft",{get:function(){return this._dPadLeft},set:function(a){this._dPadLeft=this._setDPadValue(a,this._dPadLeft,tc.Left)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dPadRight",{get:function(){return this._dPadRight},set:function(a){this._dPadRight=this._setDPadValue(a,this._dPadRight,tc.Right)},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this),this.buttonCross=this.browserGamepad.buttons[0].value,this.buttonCircle=this.browserGamepad.buttons[1].value,this.buttonSquare=this.browserGamepad.buttons[2].value,this.buttonTriangle=this.browserGamepad.buttons[3].value,this.buttonL1=this.browserGamepad.buttons[4].value,this.buttonR1=this.browserGamepad.buttons[5].value,this.leftTrigger=this.browserGamepad.buttons[6].value,this.rightTrigger=this.browserGamepad.buttons[7].value,this.buttonShare=this.browserGamepad.buttons[8].value,this.buttonOptions=this.browserGamepad.buttons[9].value,this.buttonLeftStick=this.browserGamepad.buttons[10].value,this.buttonRightStick=this.browserGamepad.buttons[11].value,this.dPadUp=this.browserGamepad.buttons[12].value,this.dPadDown=this.browserGamepad.buttons[13].value,this.dPadLeft=this.browserGamepad.buttons[14].value,this.dPadRight=this.browserGamepad.buttons[15].value},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.onButtonDownObservable.clear(),this.onButtonUpObservable.clear(),this.onPadDownObservable.clear(),this.onPadUpObservable.clear()},b}(Cb),wc=function(){function a(a){var b=this;if(this._scene=a,this._babylonGamepads=[],this._oneGamepadConnected=!1,this._isMonitoring=!1,this.onGamepadDisconnectedObservable=new f.c,Object(Ja.e)()?(this._gamepadEventSupported="GamepadEvent"in window,this._gamepadSupport=navigator&&(navigator.getGamepads||navigator.webkitGetGamepads||navigator.msGetGamepads||navigator.webkitGamepads)):this._gamepadEventSupported=!1,this.onGamepadConnectedObservable=new f.c(function(a){for(var c in b._babylonGamepads){var d=b._babylonGamepads[c];d&&d._isConnected&&b.onGamepadConnectedObservable.notifyObserver(a,d)}}),this._onGamepadConnectedEvent=function(a){var c;a=a.gamepad;a.index in b._babylonGamepads&&b._babylonGamepads[a.index].isConnected||(b._babylonGamepads[a.index]?((c=b._babylonGamepads[a.index]).browserGamepad=a,c._isConnected=!0):c=b._addNewGamepad(a),b.onGamepadConnectedObservable.notifyObservers(c),b._startMonitoringGamepads())},this._onGamepadDisconnectedEvent=function(a){a=a.gamepad;for(var c in b._babylonGamepads)if(b._babylonGamepads[c].index===a.index){var d=b._babylonGamepads[c];d._isConnected=!1,b.onGamepadDisconnectedObservable.notifyObservers(d),d.dispose&&d.dispose();break}},this._gamepadSupport)if(this._updateGamepadObjects(),this._babylonGamepads.length&&this._startMonitoringGamepads(),this._gamepadEventSupported){a=this._scene?this._scene.getEngine().getHostWindow():window;a&&(a.addEventListener("gamepadconnected",this._onGamepadConnectedEvent,!1),a.addEventListener("gamepaddisconnected",this._onGamepadDisconnectedEvent,!1))}else this._startMonitoringGamepads()}return Object.defineProperty(a.prototype,"gamepads",{get:function(){return this._babylonGamepads},enumerable:!1,configurable:!0}),a.prototype.getGamepadByType=function(a){void 0===a&&(a=Cb.XBOX);for(var b=0,c=this._babylonGamepads;b1?"COLOR_ATTACHMENT"+b:"COLOR_ATTACHMENT"+b+"_WEBGL"];b=-1!==c?e.TEXTURE_CUBE_MAP_POSITIVE_X+c:e.TEXTURE_2D;e.framebufferTexture2D(e.FRAMEBUFFER,f,b,a._hardwareTexture.underlyingResource,d),this._engine._bindUnboundFramebuffer(g)}},b.prototype.setTexture=function(b,c,d){void 0===c&&(c=0),void 0===d&&(d=!0),a.prototype.setTexture.call(this,b,c,d),this._bindTextureRenderTarget(b,c)},b.prototype.dispose=function(b){void 0===b&&(b=!1);var c=this._context;b||(this._colorTextureArray&&(this._context.deleteTexture(this._colorTextureArray),this._colorTextureArray=null),this._depthStencilTextureArray&&(this._context.deleteTexture(this._depthStencilTextureArray),this._depthStencilTextureArray=null)),this._framebuffer&&(c.deleteFramebuffer(this._framebuffer),this._framebuffer=null),this._depthStencilBuffer&&(c.deleteRenderbuffer(this._depthStencilBuffer),this._depthStencilBuffer=null),this._MSAAFramebuffer&&(c.deleteFramebuffer(this._MSAAFramebuffer),this._MSAAFramebuffer=null),a.prototype.dispose.call(this,b)},b}(Cc.a);qb.a.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new Dc(a,b,c,this,this._gl);return this._renderTargetWrapperCache.push(a),a},qb.a.prototype.createRenderTargetTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!1,a),d=new Bc.b;void 0!==b&&"object"==typeof b?(d.generateDepthBuffer=!!b.generateDepthBuffer,d.generateStencilBuffer=!!b.generateStencilBuffer):(d.generateDepthBuffer=!0,d.generateStencilBuffer=!1);b=this._createInternalTexture(a,b);var e=a.width||a;a=a.height||a;var f=this._currentFramebuffer,g=this._gl,h=g.createFramebuffer();return this._bindUnboundFramebuffer(h),c._depthStencilBuffer=this._setupFramebufferDepthAttachments(!!d.generateStencilBuffer,d.generateDepthBuffer,e,a),b.is2DArray||g.framebufferTexture2D(g.FRAMEBUFFER,g.COLOR_ATTACHMENT0,g.TEXTURE_2D,b._hardwareTexture.underlyingResource,0),this._bindUnboundFramebuffer(f),c._framebuffer=h,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=!!d.generateStencilBuffer,c.setTextures(b),c},qb.a.prototype.createDepthStencilTexture=function(a,b,c){if(b.isCube){var d=a.width||a;return this._createDepthStencilCubeTexture(d,b,c)}return this._createDepthStencilTexture(a,b,c)},qb.a.prototype._createDepthStencilTexture=function(a,b,c){var d=this._gl,e=a.layers||0,f=0!==e?d.TEXTURE_2D_ARRAY:d.TEXTURE_2D,g=new pb.a(this,pb.b.DepthStencil);if(!this._caps.depthTextureExtension)return q.a.Error("Depth texture is not supported by your browser or hardware."),g;b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1},b);this._bindTextureDirectly(f,g,!0),this._setupDepthStencilTexture(g,a,b.generateStencil,0!==b.comparisonFunction&&b.bilinearFiltering,b.comparisonFunction),c._depthStencilTexture=g,c._depthStencilTextureWithStencil=b.generateStencil;a=b.generateStencil?d.UNSIGNED_INT_24_8:d.UNSIGNED_INT;c=b.generateStencil?d.DEPTH_STENCIL:d.DEPTH_COMPONENT;var h=c;return this.webGLVersion>1&&(h=b.generateStencil?d.DEPTH24_STENCIL8:d.DEPTH_COMPONENT24),g.is2DArray?d.texImage3D(f,0,h,g.width,g.height,e,0,c,a,null):d.texImage2D(f,0,h,g.width,g.height,0,c,a,null),this._bindTextureDirectly(f,null),this._internalTexturesCache.push(g),g},qb.a.prototype.updateRenderTargetTextureSampleCount=function(a,b){if(this.webGLVersion<2||!a||!a.texture)return 1;if(a.samples===b)return b;var c=this._gl;b=Math.min(b,this.getCaps().maxMSAASamples),a._depthStencilBuffer&&(c.deleteRenderbuffer(a._depthStencilBuffer),a._depthStencilBuffer=null),a._MSAAFramebuffer&&(c.deleteFramebuffer(a._MSAAFramebuffer),a._MSAAFramebuffer=null);var d=a.texture._hardwareTexture;if(d._MSAARenderBuffer&&(c.deleteRenderbuffer(d._MSAARenderBuffer),d._MSAARenderBuffer=null),b>1&&c.renderbufferStorageMultisample){var e=c.createFramebuffer();if(!e)throw new Error("Unable to create multi sampled framebuffer");a._MSAAFramebuffer=e,this._bindUnboundFramebuffer(a._MSAAFramebuffer);e=this._createRenderBuffer(a.texture.width,a.texture.height,b,-1,this._getRGBAMultiSampleBufferFormat(a.texture.type),c.COLOR_ATTACHMENT0,!1);if(!e)throw new Error("Unable to create multi sampled framebuffer");d._MSAARenderBuffer=e}else this._bindUnboundFramebuffer(a._framebuffer);return a.texture.samples=b,a._depthStencilBuffer=this._setupFramebufferDepthAttachments(a._generateStencilBuffer,a._generateDepthBuffer,a.texture.width,a.texture.height,b),this._bindUnboundFramebuffer(null),b};var Ec=c(37),Fc=function(){function a(a,b,c,d,e,h,i,j,k,l,s,t,u,v,w){void 0===i&&(i=r.a.TEXTURE_NEAREST_SAMPLINGMODE),void 0===l&&(l=null),void 0===s&&(s=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===t&&(t="postprocess"),void 0===v&&(v=!1),void 0===w&&(w=r.a.TEXTUREFORMAT_RGBA),this._parentContainer=null,this.width=-1,this.height=-1,this.nodeMaterialSource=null,this._outputTexture=null,this.autoClear=!0,this.alphaMode=r.a.ALPHA_DISABLE,this.animations=new Array,this.enablePixelPerfectMode=!1,this.forceFullscreenViewport=!0,this.scaleMode=r.a.SCALEMODE_FLOOR,this.alwaysForcePOT=!1,this._samples=1,this.adaptScaleToCurrentViewport=!1,this._reusable=!1,this._renderId=0,this._textures=new Ac.a(2),this._textureCache=[],this._currentRenderTextureInd=0,this._scaleRatio=new g.d(1,1),this._texelSize=g.d.Zero(),this.onActivateObservable=new f.c,this.onSizeChangedObservable=new f.c,this.onApplyObservable=new f.c,this.onBeforeRenderObservable=new f.c,this.onAfterRenderObservable=new f.c,this.name=a,null!=h?(this._camera=h,this._scene=h.getScene(),h.attachPostProcess(this),this._engine=this._scene.getEngine(),this._scene.postProcesses.push(this),this.uniqueId=this._scene.getUniqueId()):j&&(this._engine=j,this._engine.postProcesses.push(this)),this._options=e,this.renderTargetSamplingMode=i||r.a.TEXTURE_NEAREST_SAMPLINGMODE,this._reusable=k||!1,this._textureType=s,this._textureFormat=w,this._samplers=d||[],this._samplers.push("textureSampler"),this._fragmentUrl=b,this._vertexUrl=t,this._parameters=c||[],this._parameters.push("scale"),this._indexParameters=u,this._drawWrapper=new Ec.a(this._engine),v||this.updateEffect(l)}return Object.defineProperty(a.prototype,"samples",{get:function(){return this._samples},set:function(a){var b=this;this._samples=Math.min(a,this._engine.getCaps().maxMSAASamples),this._textures.forEach(function(a){a.samples!==b._samples&&b._engine.updateRenderTargetTextureSampleCount(a,b._samples)})},enumerable:!1,configurable:!0}),a.prototype.getEffectName=function(){return this._fragmentUrl},Object.defineProperty(a.prototype,"onActivate",{set:function(a){this._onActivateObserver&&this.onActivateObservable.remove(this._onActivateObserver),a&&(this._onActivateObserver=this.onActivateObservable.add(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onSizeChanged",{set:function(a){this._onSizeChangedObserver&&this.onSizeChangedObservable.remove(this._onSizeChangedObserver),this._onSizeChangedObserver=this.onSizeChangedObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onApply",{set:function(a){this._onApplyObserver&&this.onApplyObservable.remove(this._onApplyObserver),this._onApplyObserver=this.onApplyObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onBeforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onAfterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=this.onAfterRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"inputTexture",{get:function(){return this._textures.data[this._currentRenderTextureInd]},set:function(a){this._forcedOutputTexture=a},enumerable:!1,configurable:!0}),a.prototype.restoreDefaultInputTexture=function(){this._forcedOutputTexture&&(this._forcedOutputTexture=null,this.markTextureDirty())},a.prototype.getCamera=function(){return this._camera},Object.defineProperty(a.prototype,"texelSize",{get:function(){return this._shareOutputWithPostProcess?this._shareOutputWithPostProcess.texelSize:(this._forcedOutputTexture&&this._texelSize.copyFromFloats(1/this._forcedOutputTexture.width,1/this._forcedOutputTexture.height),this._texelSize)},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"PostProcess"},a.prototype.getEngine=function(){return this._engine},a.prototype.getEffect=function(){return this._drawWrapper.effect},a.prototype.shareOutputWith=function(a){return this._disposeTextures(),this._shareOutputWithPostProcess=a,this},a.prototype.useOwnOutput=function(){0==this._textures.length&&(this._textures=new Ac.a(2)),this._shareOutputWithPostProcess=null},a.prototype.updateEffect=function(a,b,c,d,e,f,g,h){void 0===a&&(a=null),void 0===b&&(b=null),void 0===c&&(c=null),this._postProcessDefines=a,this._drawWrapper.effect=this._engine.createEffect({vertex:null!=g?g:this._vertexUrl,fragment:null!=h?h:this._fragmentUrl},["position"],b||this._parameters,c||this._samplers,null!==a?a:"",void 0,e,f,d||this._indexParameters)},a.prototype.isReusable=function(){return this._reusable},a.prototype.markTextureDirty=function(){this.width=-1},a.prototype._createRenderTargetTexture=function(a,b,c){void 0===c&&(c=0);for(var d=0;d=0;b--)if(a-this._textureCache[b].lastUsedRenderId>100){for(var c=!1,d=0;d0&&this._textures.reset(),this.width=a,this.height=b;for(a=null,b=0;b=0;a--)this._textureCache[a].texture.dispose();this._textureCache.length=0},a.prototype.setPrePassRenderer=function(a){return!!this._prePassEffectConfiguration&&(this._prePassEffectConfiguration=a.addEffectConfiguration(this._prePassEffectConfiguration),this._prePassEffectConfiguration.enabled=!0,!0)},a.prototype.dispose=function(a){var b;if(a=a||this._camera,this._disposeTextures(),this._scene&&-1!==(b=this._scene.postProcesses.indexOf(this))&&this._scene.postProcesses.splice(b,1),this._parentContainer){var c=this._parentContainer.postProcesses.indexOf(this);c>-1&&this._parentContainer.postProcesses.splice(c,1),this._parentContainer=null}if(-1!==(b=this._engine.postProcesses.indexOf(this))&&this._engine.postProcesses.splice(b,1),a){if(a.detachPostProcess(this),0===(b=a._postProcesses.indexOf(this))&&a._postProcesses.length>0){c=this._camera._getFirstPostProcess();c&&c.markTextureDirty()}this.onActivateObservable.clear(),this.onAfterRenderObservable.clear(),this.onApplyObservable.clear(),this.onBeforeRenderObservable.clear(),this.onSizeChangedObservable.clear()}},a.prototype.serialize=function(){var a=I.a.Serialize(this),b=this.getCamera()||this._scene&&this._scene.activeCamera;return a.customType="BABYLON."+this.getClassName(),a.cameraId=b?b.id:null,a.reusable=this._reusable,a.textureType=this._textureType,a.fragmentUrl=this._fragmentUrl,a.parameters=this._parameters,a.samplers=this._samplers,a.options=this._options,a.defines=this._postProcessDefines,a.textureFormat=this._textureFormat,a.vertexUrl=this._vertexUrl,a.indexParameters=this._indexParameters,a},a.prototype.clone=function(){var b=this.serialize();b._engine=this._engine,b.cameraId=null;b=a.Parse(b,this._scene,"");return b?(b.onActivateObservable=this.onActivateObservable.clone(),b.onSizeChangedObservable=this.onSizeChangedObservable.clone(),b.onApplyObservable=this.onApplyObservable.clone(),b.onBeforeRenderObservable=this.onBeforeRenderObservable.clone(),b.onAfterRenderObservable=this.onAfterRenderObservable.clone(),b._prePassEffectConfiguration=this._prePassEffectConfiguration,b):null},a.Parse=function(a,b,c){var d=Object(i.a)(a.customType);if(!d||!d._Parse)return null;var e=b?b.getCameraById(a.cameraId):null;return d._Parse(a,e,b,c)},a._Parse=function(b,c,d,e){return I.a.Parse(function(){return new a(b.name,b.fragmentUrl,b.parameters,b.samplers,b.options,c,b.renderTargetSamplingMode,b._engine,b.reusable,b.defines,b.textureType,b.vertexUrl,b.indexParameters,!1,b.textureFormat)},b,d,e)},Object(l.c)([Object(I.d)()],a.prototype,"uniqueId",void 0),Object(l.c)([Object(I.d)()],a.prototype,"name",void 0),Object(l.c)([Object(I.d)()],a.prototype,"width",void 0),Object(l.c)([Object(I.d)()],a.prototype,"height",void 0),Object(l.c)([Object(I.d)()],a.prototype,"renderTargetSamplingMode",void 0),Object(l.c)([Object(I.g)()],a.prototype,"clearColor",void 0),Object(l.c)([Object(I.d)()],a.prototype,"autoClear",void 0),Object(l.c)([Object(I.d)()],a.prototype,"alphaMode",void 0),Object(l.c)([Object(I.d)()],a.prototype,"alphaConstants",void 0),Object(l.c)([Object(I.d)()],a.prototype,"enablePixelPerfectMode",void 0),Object(l.c)([Object(I.d)()],a.prototype,"forceFullscreenViewport",void 0),Object(l.c)([Object(I.d)()],a.prototype,"scaleMode",void 0),Object(l.c)([Object(I.d)()],a.prototype,"alwaysForcePOT",void 0),Object(l.c)([Object(I.d)("samples")],a.prototype,"_samples",void 0),Object(l.c)([Object(I.d)()],a.prototype,"adaptScaleToCurrentViewport",void 0),a}();Object(i.b)("BABYLON.PostProcess",Fc);V=" varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor=texture2D(textureSampler,vUV); }";X.a.ShadersStore.passPixelShader=V;a=" varying vec2 vUV; uniform samplerCube textureSampler; void main(void) { vec2 uv=vUV*2.0-1.0; #ifdef POSITIVEX gl_FragColor=textureCube(textureSampler,vec3(1.001,uv.y,uv.x)); #endif #ifdef NEGATIVEX gl_FragColor=textureCube(textureSampler,vec3(-1.001,uv.y,uv.x)); #endif #ifdef POSITIVEY gl_FragColor=textureCube(textureSampler,vec3(uv.y,1.001,uv.x)); #endif #ifdef NEGATIVEY gl_FragColor=textureCube(textureSampler,vec3(uv.y,-1.001,uv.x)); #endif #ifdef POSITIVEZ gl_FragColor=textureCube(textureSampler,vec3(uv,1.001)); #endif #ifdef NEGATIVEZ gl_FragColor=textureCube(textureSampler,vec3(uv,-1.001)); #endif }";X.a.ShadersStore.passCubePixelShader=a;var Gc=function(a){function b(b,c,d,e,f,g,h,i){return void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1),a.call(this,b,"pass",null,null,c,d,e,f,g,void 0,h,void 0,null,i)||this}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"PassPostProcess"},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,a._engine,a.reusable)},a,d,e)},b}(Fc);Object(i.b)("BABYLON.PassPostProcess",Gc);var Hc=function(a){function b(b,c,d,e,f,g,h,i){void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1);b=a.call(this,b,"passCube",null,null,c,d,e,f,g,"#define POSITIVEX",h,void 0,null,i)||this;return b._face=0,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"face",{get:function(){return this._face},set:function(a){if(!(a<0||a>5))switch(this._face=a,this._face){case 0:this.updateEffect("#define POSITIVEX");break;case 1:this.updateEffect("#define NEGATIVEX");break;case 2:this.updateEffect("#define POSITIVEY");break;case 3:this.updateEffect("#define NEGATIVEY");break;case 4:this.updateEffect("#define POSITIVEZ");break;case 5:this.updateEffect("#define NEGATIVEZ")}},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PassCubePostProcess"},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,a._engine,a.reusable)},a,d,e)},b}(Fc);S.a._RescalePostProcessFactory=function(a){return new Gc("rescale",1,null,r.a.TEXTURE_BILINEAR_SAMPLINGMODE,a,!1,r.a.TEXTURETYPE_UNSIGNED_INT)};V=" varying vec2 vUV; uniform sampler2D textureSampler; uniform sampler2D leftSampler; void main(void) { vec4 leftFrag=texture2D(leftSampler,vUV); leftFrag=vec4(1.0,leftFrag.g,leftFrag.b,1.0); vec4 rightFrag=texture2D(textureSampler,vUV); rightFrag=vec4(rightFrag.r,1.0,1.0,1.0); gl_FragColor=vec4(rightFrag.rgb*leftFrag.rgb,1.0); }";X.a.ShadersStore.anaglyphPixelShader=V;var Ic=function(a){function b(b,c,d,e,f,g){var h=a.call(this,b,"anaglyph",null,["leftSampler"],c,d[1],e,f,g)||this;return h._passedProcess=d[0]._rigPostProcess,h.onApplyObservable.add(function(a){a.setTextureFromPostProcess("leftSampler",h._passedProcess)}),h}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphPostProcess"},b}(Fc);function Jc(a){a._rigCameras[0]._rigPostProcess=new Gc(a.name+"_passthru",1,a._rigCameras[0]),a._rigCameras[1]._rigPostProcess=new Ic(a.name+"_anaglyph",1,a._rigCameras)}Object(i.b)("BABYLON.AnaglyphPostProcess",Ic),K.a.AddNodeConstructor("AnaglyphArcRotateCamera",function(a,b,c){return function(){return new Kc(a,0,0,1,g.e.Zero(),c.interaxial_distance,b)}});var Kc=function(a){function b(b,c,d,e,f,g,h){b=a.call(this,b,c,d,e,f,h)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=g,b.setCameraRigMode(db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:g}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphArcRotateCamera"},b}(fc);K.a.AddNodeConstructor("AnaglyphFreeCamera",function(a,b,c){return function(){return new Lc(a,g.e.Zero(),c.interaxial_distance,b)}});var Lc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphFreeCamera"},b}(dc);K.a.AddNodeConstructor("AnaglyphGamepadCamera",function(a,b,c){return function(){return new Mc(a,g.e.Zero(),c.interaxial_distance,b)}});var Mc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphGamepadCamera"},b}(zc);K.a.AddNodeConstructor("AnaglyphUniversalCamera",function(a,b,c){return function(){return new Nc(a,g.e.Zero(),c.interaxial_distance,b)}});var Nc=function(a){function b(b,c,d,e){b=a.call(this,b,c,e)||this;return b._setRigMode=Jc.bind(null,b),b.interaxialDistance=d,b.setCameraRigMode(db.a.RIG_MODE_STEREOSCOPIC_ANAGLYPH,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"AnaglyphUniversalCamera"},b}(yc),Oc=c(61);a="const vec3 TWO=vec3(2.0,2.0,2.0); varying vec2 vUV; uniform sampler2D camASampler; uniform sampler2D textureSampler; uniform vec2 stepSize; void main(void) { bool useCamA; bool useCamB; vec2 texCoord1; vec2 texCoord2; vec3 frag1; vec3 frag2; #ifdef IS_STEREOSCOPIC_HORIZ useCamB=vUV.x>0.5; useCamA=!useCamB; texCoord1=vec2(useCamB ? (vUV.x-0.5)*2.0 : vUV.x*2.0,vUV.y); texCoord2=vec2(texCoord1.x+stepSize.x,vUV.y); #else #ifdef IS_STEREOSCOPIC_INTERLACED float rowNum=floor(vUV.y/stepSize.y); useCamA=mod(rowNum,2.0) == 1.0; useCamB=mod(rowNum,2.0) == 0.0; texCoord1=vec2(vUV.x,vUV.y); texCoord2=vec2(vUV.x,vUV.y); #else useCamB=vUV.y>0.5; useCamA=!useCamB; texCoord1=vec2(vUV.x,useCamB ? (vUV.y-0.5)*2.0 : vUV.y*2.0); texCoord2=vec2(vUV.x,texCoord1.y+stepSize.y); #endif #endif if (useCamB){ frag1=texture2D(textureSampler,texCoord1).rgb; frag2=texture2D(textureSampler,texCoord2).rgb; }else if (useCamA){ frag1=texture2D(camASampler ,texCoord1).rgb; frag2=texture2D(camASampler ,texCoord2).rgb; }else { discard; } gl_FragColor=vec4((frag1+frag2)/TWO,1.0); } ";X.a.ShadersStore.stereoscopicInterlacePixelShader=a;var Pc=function(a){function b(b,c,d,e,f,h,i){var j=a.call(this,b,"stereoscopicInterlace",["stepSize"],["camASampler"],1,c[1],f,h,i,e?"#define IS_STEREOSCOPIC_INTERLACED 1":d?"#define IS_STEREOSCOPIC_HORIZ 1":void 0)||this;return j._passedProcess=c[0]._rigPostProcess,j._stepSize=new g.d(1/j.width,1/j.height),j.onSizeChangedObservable.add(function(){j._stepSize=new g.d(1/j.width,1/j.height)}),j.onApplyObservable.add(function(a){a.setTextureFromPostProcess("camASampler",j._passedProcess),a.setFloat2("stepSize",j._stepSize.x,j._stepSize.y)}),j}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicInterlacePostProcessI"},b}(Fc),Qc=function(a){function b(b,c,d,e,f,h){var i=a.call(this,b,"stereoscopicInterlace",["stepSize"],["camASampler"],1,c[1],e,f,h,d?"#define IS_STEREOSCOPIC_HORIZ 1":void 0)||this;return i._passedProcess=c[0]._rigPostProcess,i._stepSize=new g.d(1/i.width,1/i.height),i.onSizeChangedObservable.add(function(){i._stepSize=new g.d(1/i.width,1/i.height)}),i.onApplyObservable.add(function(a){a.setTextureFromPostProcess("camASampler",i._passedProcess),a.setFloat2("stepSize",i._stepSize.x,i._stepSize.y)}),i}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicInterlacePostProcess"},b}(Fc);function Rc(a){var b=a.cameraRigMode===db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL||a.cameraRigMode===db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED,c=a.cameraRigMode===db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;a.cameraRigMode===db.a.RIG_MODE_STEREOSCOPIC_INTERLACED?(a._rigCameras[0]._rigPostProcess=new Gc(a.name+"_passthru",1,a._rigCameras[0]),a._rigCameras[1]._rigPostProcess=new Pc(a.name+"_stereoInterlace",a._rigCameras,!1,!0)):(a._rigCameras[c?1:0].viewport=new Oc.a(0,0,b?.5:1,b?1:.5),a._rigCameras[c?0:1].viewport=new Oc.a(b?.5:0,b?0:.5,b?.5:1,b?1:.5))}K.a.AddNodeConstructor("StereoscopicArcRotateCamera",function(a,b,c){return function(){return new Sc(a,0,0,1,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Sc=function(a){function b(b,c,d,e,f,g,h,i){b=a.call(this,b,c,d,e,f,i)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=g,b.isStereoscopicSideBySide=h,b.setCameraRigMode(h?db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:g}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicArcRotateCamera"},b}(fc);K.a.AddNodeConstructor("StereoscopicFreeCamera",function(a,b,c){return function(){return new Tc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Tc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicFreeCamera"},b}(dc);K.a.AddNodeConstructor("StereoscopicGamepadCamera",function(a,b,c){return function(){return new Uc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Uc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicGamepadCamera"},b}(zc);K.a.AddNodeConstructor("StereoscopicFreeCamera",function(a,b,c){return function(){return new Vc(a,g.e.Zero(),c.interaxial_distance,c.isStereoscopicSideBySide,b)}});var Vc=function(a){function b(b,c,d,e,f){b=a.call(this,b,c,f)||this;return b._setRigMode=Rc.bind(null,b),b.interaxialDistance=d,b.isStereoscopicSideBySide=e,b.setCameraRigMode(e?db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:db.a.RIG_MODE_STEREOSCOPIC_OVERUNDER,{interaxialDistance:d}),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"StereoscopicUniversalCamera"},b}(yc),Wc=function(a){function b(b,c,d,e,f){void 0===e&&(e=1),void 0===f&&(f=.065);b=a.call(this,b,c,d)||this;return b._distanceBetweenEyes=f,b._distanceToProjectionPlane=e,b.setCameraRigMode(db.a.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL,{stereoHalfAngle:0}),b._cameraRigParams.stereoHalfAngle=0,b._cameraRigParams.interaxialDistance=f,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"distanceBetweenEyes",{get:function(){return this._distanceBetweenEyes},set:function(a){this._distanceBetweenEyes=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"distanceToProjectionPlane",{get:function(){return this._distanceToProjectionPlane},set:function(a){this._distanceToProjectionPlane=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"StereoscopicUniversalCamera"},b.prototype.createRigCamera=function(a,b){b=new cc(a,g.e.Zero(),this.getScene());a=new fb.a("tm_"+a,this.getScene());return b.parent=a,a.setPivotMatrix(g.a.Identity(),!1),b.isRigCamera=!0,b.rigParent=this,b},b.prototype._updateRigCameras=function(){for(var a=0;a1.0 || tc.y<0.0 || tc.y>1.0) gl_FragColor=vec4(0.0,0.0,0.0,0.0); else{ gl_FragColor=texture2D(textureSampler,tc); } }";X.a.ShadersStore.vrDistortionCorrectionPixelShader=V;var Zc=function(a){function b(b,c,d,e){var f=a.call(this,b,"vrDistortionCorrection",["LensCenter","Scale","ScaleIn","HmdWarpParam"],null,e.postProcessScaleFactor,c,U.a.BILINEAR_SAMPLINGMODE)||this;return f._isRightEye=d,f._distortionFactors=e.distortionK,f._postProcessScaleFactor=e.postProcessScaleFactor,f._lensCenterOffset=e.lensCenterOffset,f.adaptScaleToCurrentViewport=!0,f.onSizeChangedObservable.add(function(){f._scaleIn=new g.d(2,2/f.aspectRatio),f._scaleFactor=new g.d(1/f._postProcessScaleFactor*.5,1/f._postProcessScaleFactor*.5*f.aspectRatio),f._lensCenter=new g.d(f._isRightEye?.5-.5*f._lensCenterOffset:.5+.5*f._lensCenterOffset,.5)}),f.onApplyObservable.add(function(a){a.setFloat2("LensCenter",f._lensCenter.x,f._lensCenter.y),a.setFloat2("Scale",f._scaleFactor.x,f._scaleFactor.y),a.setFloat2("ScaleIn",f._scaleIn.x,f._scaleIn.y),a.setFloat4("HmdWarpParam",f._distortionFactors[0],f._distortionFactors[1],f._distortionFactors[2],f._distortionFactors[3])}),f}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"VRDistortionCorrectionPostProcess"},b}(Fc);a="precision mediump sampler2DArray; varying vec2 vUV; uniform sampler2DArray multiviewSampler; uniform int imageIndex; void main(void) { gl_FragColor=texture2D(multiviewSampler,vec3(vUV,imageIndex)); }";X.a.ShadersStore.vrMultiviewToSingleviewPixelShader=a;var $c=c(67),ad=c(103),bd=c(104);qb.a.prototype.createRenderTargetCubeTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!0,a);b=Object(l.a)({generateMipMaps:!0,generateDepthBuffer:!0,generateStencilBuffer:!1,type:r.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,format:r.a.TEXTUREFORMAT_RGBA},b);b.generateStencilBuffer=b.generateDepthBuffer&&b.generateStencilBuffer,(b.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(b.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(b.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);var d=this._gl,e=new pb.a(this,pb.b.RenderTarget);this._bindTextureDirectly(d.TEXTURE_CUBE_MAP,e,!0);var f=this._getSamplingParameters(b.samplingMode,b.generateMipMaps);b.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloat||(b.type=r.a.TEXTURETYPE_UNSIGNED_INT,q.a.Warn("Float textures are not supported. Cube render target forced to TEXTURETYPE_UNESIGNED_BYTE type")),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_MAG_FILTER,f.mag),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_MIN_FILTER,f.min),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_WRAP_S,d.CLAMP_TO_EDGE),d.texParameteri(d.TEXTURE_CUBE_MAP,d.TEXTURE_WRAP_T,d.CLAMP_TO_EDGE);for(f=0;f<6;f++)d.texImage2D(d.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,this._getRGBABufferInternalSizedFormat(b.type,b.format),a,a,0,this._getInternalFormat(b.format),this._getWebGLTextureType(b.type),null);f=d.createFramebuffer();return this._bindUnboundFramebuffer(f),c._depthStencilBuffer=this._setupFramebufferDepthAttachments(b.generateStencilBuffer,b.generateDepthBuffer,a,a),b.generateMipMaps&&d.generateMipmap(d.TEXTURE_CUBE_MAP),this._bindTextureDirectly(d.TEXTURE_CUBE_MAP,null),this._bindUnboundFramebuffer(null),c._framebuffer=f,c._generateDepthBuffer=b.generateDepthBuffer,c._generateStencilBuffer=b.generateStencilBuffer,e.width=a,e.height=a,e.isReady=!0,e.isCube=!0,e.samples=1,e.generateMipMaps=b.generateMipMaps,e.samplingMode=b.samplingMode,e.type=b.type,e.format=b.format,this._internalTexturesCache.push(e),c.setTextures(e),c};var cd=function(a){function b(b,c,d,e,h,i,j,k,l,t,u,v,w,x,y){void 0===h&&(h=!0),void 0===i&&(i=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===j&&(j=!1),void 0===k&&(k=U.a.TRILINEAR_SAMPLINGMODE),void 0===l&&(l=!0),void 0===t&&(t=!1),void 0===u&&(u=!1),void 0===v&&(v=r.a.TEXTUREFORMAT_RGBA),void 0===w&&(w=!1);k=a.call(this,null,d,!e,void 0,k,void 0,void 0,void 0,void 0,v)||this;if(k.renderParticles=!0,k.renderSprites=!1,k.ignoreCameraViewport=!1,k.onBeforeBindObservable=new f.c,k.onAfterUnbindObservable=new f.c,k.onBeforeRenderObservable=new f.c,k.onAfterRenderObservable=new f.c,k.onClearObservable=new f.c,k.onResizeObservable=new f.c,k._cleared=!1,k.skipInitialClear=!1,k._currentRefreshId=-1,k._refreshRate=1,k._samples=1,k._canRescale=!0,k._renderTarget=null,k.boundingBoxPosition=g.e.Zero(),!(d=k.getScene()))return k;v=k.getScene().getEngine();return k._coordinatesMode=U.a.PROJECTION_MODE,k.renderList=new Array,k.name=b,k.isRenderTarget=!0,k._initialSizeParameter=c,k._processSizeParameter(c),k._resizeObserver=v.onResizeObservable.add(function(){}),k._generateMipMaps=!!e,k._doNotChangeAspectRatio=h,k._renderingManager=new bd.b(d),k._renderingManager._useSceneAutoClearSetup=!0,u||(k._renderTargetOptions={generateMipMaps:e,type:i,format:null!==(b=k._format)&&void 0!==b?b:void 0,samplingMode:k.samplingMode,generateDepthBuffer:l,generateStencilBuffer:t,samples:x,creationFlags:y},k.samplingMode===U.a.NEAREST_SAMPLINGMODE&&(k.wrapU=U.a.CLAMP_ADDRESSMODE,k.wrapV=U.a.CLAMP_ADDRESSMODE),w||(j?(k._renderTarget=d.getEngine().createRenderTargetCubeTexture(k.getRenderSize(),k._renderTargetOptions),k.coordinatesMode=U.a.INVCUBIC_MODE,k._textureMatrix=g.a.Identity()):k._renderTarget=d.getEngine().createRenderTargetTexture(k._size,k._renderTargetOptions),k._texture=k._renderTarget.texture,void 0!==x&&(k.samples=x))),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"renderList",{get:function(){return this._renderList},set:function(a){this._renderList=a,this._renderList&&this._hookArray(this._renderList)},enumerable:!1,configurable:!0}),b.prototype._hookArray=function(a){var b=this,c=a.push;a.push=function(){for(var d,e=[],f=0;f0&&(this._postProcesses[0].autoClear=!1))}},b.prototype._shouldRender=function(){return-1===this._currentRefreshId||this.refreshRate===this._currentRefreshId?(this._currentRefreshId=1,!0):(this._currentRefreshId++,!1)},b.prototype.getRenderSize=function(){return this.getRenderWidth()},b.prototype.getRenderWidth=function(){return this._size.width?this._size.width:this._size},b.prototype.getRenderHeight=function(){return this._size.width?this._size.height:this._size},b.prototype.getRenderLayers=function(){var a=this._size.layers;return a||0},b.prototype.disableRescaling=function(){this._canRescale=!1},Object.defineProperty(b.prototype,"canRescale",{get:function(){return this._canRescale},enumerable:!1,configurable:!0}),b.prototype.scale=function(a){a=Math.max(1,this.getRenderSize()*a);this.resize(a)},b.prototype.getReflectionTextureMatrix=function(){return this.isCube?this._textureMatrix:a.prototype.getReflectionTextureMatrix.call(this)},b.prototype.resize=function(a){var b,c=this.isCube;null===(b=this._renderTarget)||void 0===b||b.dispose(),this._renderTarget=null;b=this.getScene();b&&(this._processSizeParameter(a),this._renderTarget=c?b.getEngine().createRenderTargetCubeTexture(this.getRenderSize(),this._renderTargetOptions):b.getEngine().createRenderTargetTexture(this._size,this._renderTargetOptions),this._texture=this._renderTarget.texture,void 0!==this._renderTargetOptions.samples&&(this.samples=this._renderTargetOptions.samples),this.onResizeObservable.hasObservers()&&this.onResizeObservable.notifyObservers(this))},b.prototype.render=function(a,b){if(void 0===a&&(a=!1),void 0===b&&(b=!1),f=this.getScene()){var c=f.getEngine();if(void 0!==this.useCameraPostProcesses&&(a=this.useCameraPostProcesses),this._waitingRenderList){this.renderList=[];for(var d=0;d1||this.activeCamera&&this.activeCamera!==f.activeCamera)&&f.setTransformMatrix(f.activeCamera.getViewMatrix(),f.activeCamera.getProjectionMatrix(!0)),c.setViewport(f.activeCamera.viewport)),f.resetCachedMaterial()}},b.prototype._bestReflectionRenderTargetDimension=function(a,b){b=a*b;b=S.a.NearestPOT(b+16384/(128+b));return Math.min(S.a.FloorPOT(a),b)},b.prototype._prepareRenderingManager=function(a,b,c,d){var e=this.getScene();if(e){this._renderingManager.reset();for(var f=e.getRenderId(),g=0;g=0&&this._renderingManager.dispatchParticles(i)}}},b.prototype._bindFrameBuffer=function(a,b){void 0===a&&(a=0),void 0===b&&(b=0);var c=this.getScene();if(c){c=c.getEngine();this._renderTarget&&c.bindFramebuffer(this._renderTarget,this.isCube?a:void 0,void 0,void 0,this.ignoreCameraViewport,0,b)}},b.prototype.unbindFrameBuffer=function(a,b){var c=this;this._renderTarget&&a.unBindFramebuffer(this._renderTarget,this.isCube,function(){c.onAfterRenderObservable.notifyObservers(b)})},b.prototype._prepareFrame=function(a,b,c,d){this._postProcessManager?this._prePassEnabled||this._postProcessManager._prepareFrame(this._texture,this._postProcesses):d&&a.postProcessManager._prepareFrame(this._texture)||this._bindFrameBuffer(b,c)},b.prototype.renderToTarget=function(a,b,c,d,e){var f;void 0===d&&(d=0),void 0===e&&(e=null);var g=this.getScene();if(g){var h=g.getEngine();if(this._texture){if(null===(f=h._debugPushGroup)||void 0===f||f.call(h,"render to face #"+a+" layer #"+d,1),this._prepareFrame(g,a,d,b),this.is2DArray?this.onBeforeRenderObservable.notifyObservers(d):this.onBeforeRenderObservable.notifyObservers(a),h.snapshotRendering&&h.snapshotRenderingMode===r.a.SNAPSHOTRENDERING_FAST)this.onClearObservable.hasObservers()?this.onClearObservable.notifyObservers(h):this.skipInitialClear||h.clear(this.clearColor||g.clearColor,!0,!0,!0);else{f=null;var i=this.renderList?this.renderList:g.getActiveMeshes().data,j=this.renderList?this.renderList.length:g.getActiveMeshes().length;this.getCustomRenderList&&(f=this.getCustomRenderList(this.is2DArray?d:a,i,j)),f?this._prepareRenderingManager(f,f.length,e,!1):(this._defaultRenderListPrepared||(this._prepareRenderingManager(i,j,e,!this.renderList),this._defaultRenderListPrepared=!0),f=i);for(j=0,e=g._beforeRenderTargetClearStage;j=0&&b.customRenderTargets.splice(c,1);for(var d=0,b=b.cameras;d=0&&e.customRenderTargets.splice(c,1)}null===(e=this._renderTarget)||void 0===e||e.dispose(),this._renderTarget=null,this._texture=null,a.prototype.dispose.call(this)}},b.prototype._rebuild=function(){this.refreshRate===b.REFRESHRATE_RENDER_ONCE&&(this.refreshRate=b.REFRESHRATE_RENDER_ONCE),this._postProcessManager&&this._postProcessManager._rebuild()},b.prototype.freeRenderingGroups=function(){this._renderingManager&&this._renderingManager.freeRenderingGroups()},b.prototype.getViewCount=function(){return 1},b.REFRESHRATE_RENDER_ONCE=0,b.REFRESHRATE_RENDER_ONEVERYFRAME=1,b.REFRESHRATE_RENDER_ONEVERYTWOFRAMES=2,b}(U.a);U.a._CreateRenderTargetTexture=function(a,b,c,d,e){return new cd(a,b,c,d)};var dd=function(a){function b(b,c){void 0===c&&(c=512);c=a.call(this,"multiview rtt",c,b,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT,!1,void 0,!1,!1,!0,void 0,!0)||this;b=b.getEngine().createMultiviewRenderTargetTexture(c.getRenderWidth(),c.getRenderHeight());return c._texture=b.texture,c._texture.isMultiview=!0,c._texture.format=r.a.TEXTUREFORMAT_RGBA,c.samples=c._getEngine().getCaps().maxSamples||c.samples,c}return Object(l.d)(b,a),b.prototype._bindFrameBuffer=function(a){void 0===a&&0,this._renderTarget&&this.getScene().getEngine().bindMultiviewFramebuffer(this._renderTarget)},b.prototype.getViewCount=function(){return 2},b}(cd),ed=c(96);S.a.prototype.createMultiviewRenderTargetTexture=function(a,b){var c=this._gl;if(!this.getCaps().multiview)throw"Multiview is not supported";var d=this._createHardwareRenderTargetWrapper(!1,!1,{width:a,height:b});d._framebuffer=c.createFramebuffer();var e=new pb.a(this,pb.b.Unknown,!0);return e.width=a,e.height=b,d._colorTextureArray=c.createTexture(),c.bindTexture(c.TEXTURE_2D_ARRAY,d._colorTextureArray),c.texStorage3D(c.TEXTURE_2D_ARRAY,1,c.RGBA8,a,b,2),d._depthStencilTextureArray=c.createTexture(),c.bindTexture(c.TEXTURE_2D_ARRAY,d._depthStencilTextureArray),c.texStorage3D(c.TEXTURE_2D_ARRAY,1,c.DEPTH32F_STENCIL8,a,b,2),e.isReady=!0,d.setTextures(e),d},S.a.prototype.bindMultiviewFramebuffer=function(a){a=a;var b=this._gl,c=this.getCaps().oculusMultiview||this.getCaps().multiview;if(this.bindFramebuffer(a,void 0,void 0,void 0,!0),b.bindFramebuffer(b.DRAW_FRAMEBUFFER,a._framebuffer),!a._colorTextureArray||!a._depthStencilTextureArray)throw"Invalid multiview frame buffer";this.getCaps().oculusMultiview?(c.framebufferTextureMultisampleMultiviewOVR(b.DRAW_FRAMEBUFFER,b.COLOR_ATTACHMENT0,a._colorTextureArray,0,a.samples,0,2),c.framebufferTextureMultisampleMultiviewOVR(b.DRAW_FRAMEBUFFER,b.DEPTH_STENCIL_ATTACHMENT,a._depthStencilTextureArray,0,a.samples,0,2)):(c.framebufferTextureMultiviewOVR(b.DRAW_FRAMEBUFFER,b.COLOR_ATTACHMENT0,a._colorTextureArray,0,0,2),c.framebufferTextureMultiviewOVR(b.DRAW_FRAMEBUFFER,b.DEPTH_STENCIL_ATTACHMENT,a._depthStencilTextureArray,0,0,2))},db.a.prototype._useMultiviewToSingleView=!1,db.a.prototype._multiviewTexture=null,db.a.prototype._resizeOrCreateMultiviewTexture=function(a,b){this._multiviewTexture?this._multiviewTexture.getRenderWidth()==a&&this._multiviewTexture.getRenderHeight()==b||(this._multiviewTexture.dispose(),this._multiviewTexture=new dd(this.getScene(),{width:a,height:b})):this._multiviewTexture=new dd(this.getScene(),{width:a,height:b})},O.a.prototype._transformMatrixR=g.a.Zero(),O.a.prototype._multiviewSceneUbo=null,O.a.prototype._createMultiviewUbo=function(){this._multiviewSceneUbo=new $c.a(this.getEngine(),void 0,!0,"scene_multiview"),this._multiviewSceneUbo.addUniform("viewProjection",16),this._multiviewSceneUbo.addUniform("viewProjectionR",16),this._multiviewSceneUbo.addUniform("view",16),this._multiviewSceneUbo.addUniform("projection",16),this._multiviewSceneUbo.addUniform("viewPosition",4)},O.a.prototype._updateMultiviewUbo=function(a,b){a&&b&&a.multiplyToRef(b,this._transformMatrixR),a&&b&&(a.multiplyToRef(b,g.c.Matrix[0]),ed.a.GetRightPlaneToRef(g.c.Matrix[0],this._frustumPlanes[3])),this._multiviewSceneUbo&&(this._multiviewSceneUbo.updateMatrix("viewProjection",this.getTransformMatrix()),this._multiviewSceneUbo.updateMatrix("viewProjectionR",this._transformMatrixR),this._multiviewSceneUbo.updateMatrix("view",this._viewMatrix),this._multiviewSceneUbo.updateMatrix("projection",this._projectionMatrix))},O.a.prototype._renderMultiviewToSingleView=function(a){a._resizeOrCreateMultiviewTexture(a._rigPostProcess&&a._rigPostProcess&&a._rigPostProcess.width>0?a._rigPostProcess.width:this.getEngine().getRenderWidth(!0),a._rigPostProcess&&a._rigPostProcess&&a._rigPostProcess.height>0?a._rigPostProcess.height:this.getEngine().getRenderHeight(!0)),this._multiviewSceneUbo||this._createMultiviewUbo(),a.outputRenderTarget=a._multiviewTexture,this._renderForCamera(a),a.outputRenderTarget=null;for(var b=0;b=2&&a.onControllersAttachedObservable.notifyObservers(a.controllers)}}})},b}(dc),nd=function(a){function b(b){var c=a.call(this,b)||this;return c.onTriggerStateChangedObservable=new f.c,c.onMainButtonStateChangedObservable=new f.c,c.onSecondaryButtonStateChangedObservable=new f.c,c.onPadStateChangedObservable=new f.c,c.onPadValuesChangedObservable=new f.c,c.pad={x:0,y:0},c._changes={pressChanged:!1,touchChanged:!1,valueChanged:!1,changed:!1},c._buttons=new Array(b.buttons.length),c.hand=b.hand,c}return Object(l.d)(b,a),b.prototype.onButtonStateChange=function(a){this._onButtonStateChange=a},Object.defineProperty(b.prototype,"defaultModel",{get:function(){return this._defaultModel},enumerable:!1,configurable:!0}),b.prototype.update=function(){a.prototype.update.call(this);for(var b=0;b #include #include void main(void) { vec4 result=texture2D(textureSampler,vUV); #ifdef IMAGEPROCESSING #ifndef FROMLINEARSPACE result.rgb=toLinearSpace(result.rgb); #endif result=applyImageProcessing(result); #else #ifdef FROMLINEARSPACE result=applyImageProcessing(result); #endif #endif gl_FragColor=result; }");X.a.ShadersStore.imageProcessingPixelShader=V;var rd,sd,td=function(a){function b(b,c,d,e,f,g,h,i){void 0===d&&(d=null),void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT);var j=a.call(this,b,"imageProcessing",[],[],c,d,e,f,g,null,h,"postprocess",null,!0)||this;return j._fromLinearSpace=!0,j._defines={IMAGEPROCESSING:!1,VIGNETTE:!1,VIGNETTEBLENDMODEMULTIPLY:!1,VIGNETTEBLENDMODEOPAQUE:!1,TONEMAPPING:!1,TONEMAPPING_ACES:!1,CONTRAST:!1,COLORCURVES:!1,COLORGRADING:!1,COLORGRADING3D:!1,FROMLINEARSPACE:!1,SAMPLER3DGREENDEPTH:!1,SAMPLER3DBGRMAP:!1,IMAGEPROCESSINGPOSTPROCESS:!1,EXPOSURE:!1},i?(i.applyByPostProcess=!0,j._attachImageProcessingConfiguration(i,!0),j.fromLinearSpace=!1):(j._attachImageProcessingConfiguration(null,!0),j.imageProcessingConfiguration.applyByPostProcess=!0),j.onApply=function(a){j.imageProcessingConfiguration.bind(a,j.aspectRatio)},j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){a.applyByPostProcess=!0,this._attachImageProcessingConfiguration(a)},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a,b){var c=this;if(void 0===b&&(b=!1),a!==this._imageProcessingConfiguration){if(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a)this._imageProcessingConfiguration=a;else{a=this.getEngine();var d=this.getCamera();if(d)d=d.getScene();else if(a&&a.scenes){a=a.scenes;d=a[a.length-1]}else d=C.a.LastCreatedScene;this._imageProcessingConfiguration=d?d.imageProcessingConfiguration:new od.a}this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){c._updateParameters()})),b||this._updateParameters()}},Object.defineProperty(b.prototype,"isSupported",{get:function(){var a=this.getEffect();return!a||a.isSupported},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorCurves",{get:function(){return this.imageProcessingConfiguration.colorCurves},set:function(a){this.imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorGradingTexture",{get:function(){return this.imageProcessingConfiguration.colorGradingTexture},set:function(a){this.imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"colorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"exposure",{get:function(){return this.imageProcessingConfiguration.exposure},set:function(a){this.imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toneMappingType",{get:function(){return this._imageProcessingConfiguration.toneMappingType},set:function(a){this._imageProcessingConfiguration.toneMappingType=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"contrast",{get:function(){return this.imageProcessingConfiguration.contrast},set:function(a){this.imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteStretch",{get:function(){return this.imageProcessingConfiguration.vignetteStretch},set:function(a){this.imageProcessingConfiguration.vignetteStretch=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCentreX",{get:function(){return this.imageProcessingConfiguration.vignetteCentreX},set:function(a){this.imageProcessingConfiguration.vignetteCentreX=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCentreY",{get:function(){return this.imageProcessingConfiguration.vignetteCentreY},set:function(a){this.imageProcessingConfiguration.vignetteCentreY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteWeight",{get:function(){return this.imageProcessingConfiguration.vignetteWeight},set:function(a){this.imageProcessingConfiguration.vignetteWeight=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteColor",{get:function(){return this.imageProcessingConfiguration.vignetteColor},set:function(a){this.imageProcessingConfiguration.vignetteColor=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteCameraFov",{get:function(){return this.imageProcessingConfiguration.vignetteCameraFov},set:function(a){this.imageProcessingConfiguration.vignetteCameraFov=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteBlendMode",{get:function(){return this.imageProcessingConfiguration.vignetteBlendMode},set:function(a){this.imageProcessingConfiguration.vignetteBlendMode=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vignetteEnabled",{get:function(){return this.imageProcessingConfiguration.vignetteEnabled},set:function(a){this.imageProcessingConfiguration.vignetteEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fromLinearSpace",{get:function(){return this._fromLinearSpace},set:function(a){this._fromLinearSpace!==a&&(this._fromLinearSpace=a,this._updateParameters())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"ImageProcessingPostProcess"},b.prototype._updateParameters=function(){this._defines.FROMLINEARSPACE=this._fromLinearSpace,this.imageProcessingConfiguration.prepareDefines(this._defines,!0);var a="";for(var b in this._defines)this._defines[b]&&(a+="#define "+b+"; ");b=["textureSampler"];var c=["scale"];od.a&&(od.a.PrepareSamplers(b,this._defines),od.a.PrepareUniforms(c,this._defines)),this.updateEffect(a,c,b)},b.prototype.dispose=function(b){a.prototype.dispose.call(this,b),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration&&(this.imageProcessingConfiguration.applyByPostProcess=!1)},Object(l.c)([Object(I.d)()],b.prototype,"_fromLinearSpace",void 0),b}(Fc),ud=function(){function a(){}return a.GetDefaults=function(b){var c=new a;return c.canvasOptions={antialias:!0,depth:!0,stencil:!b||b.isStencilEnable,alpha:!0,multiview:!1,framebufferScaleFactor:1},c.newCanvasCssStyle="position:absolute; bottom:0px;right:0px;z-index:10;width:90%;height:100%;background-color: #000000;",c},a}(),vd=function(){function a(a,b){var c=this;if(void 0===b&&(b=ud.GetDefaults()),this._options=b,this._canvas=null,this._engine=null,this.xrLayer=null,this.onXRLayerInitObservable=new f.c,this._engine=a.scene.getEngine(),this._engine.onDisposeObservable.addOnce(function(){c._engine=null}),b.canvasElement)this._setManagedOutputCanvas(b.canvasElement);else{b=document.createElement("canvas");b.style.cssText=this._options.newCanvasCssStyle||"position:absolute; bottom:0px;right:0px;",this._setManagedOutputCanvas(b)}a.onXRSessionInit.add(function(){c._addCanvas()}),a.onXRSessionEnded.add(function(){c._removeCanvas()})}return a.prototype.dispose=function(){this._removeCanvas(),this._setManagedOutputCanvas(null)},a.prototype.initializeXRLayerAsync=function(a){var b=this,c=function(){var c=new XRWebGLLayer(a,b.canvasContext,b._options.canvasOptions);return b.onXRLayerInitObservable.notifyObservers(c),c};return this.canvasContext.makeXRCompatible?this.canvasContext.makeXRCompatible().then(function(){},function(){T.b.Warn("Error executing makeXRCompatible. This does not mean that the session will work incorrectly.")}).then(function(){return b.xrLayer=c(),b.xrLayer}):(this.xrLayer=c(),Promise.resolve(this.xrLayer))},a.prototype._addCanvas=function(){var a=this;this._canvas&&this._engine&&this._canvas!==this._engine.getRenderingCanvas()&&document.body.appendChild(this._canvas),this.xrLayer?this._setCanvasSize(!0):this.onXRLayerInitObservable.addOnce(function(b){a._setCanvasSize(!0,b)})},a.prototype._removeCanvas=function(){this._canvas&&this._engine&&document.body.contains(this._canvas)&&this._canvas!==this._engine.getRenderingCanvas()&&document.body.removeChild(this._canvas),this._setCanvasSize(!1)},a.prototype._setCanvasSize=function(a,b){void 0===a&&(a=!0),void 0===b&&(b=this.xrLayer),this._canvas&&this._engine&&(a?b&&(this._canvas!==this._engine.getRenderingCanvas()?(this._canvas.style.width=b.framebufferWidth+"px",this._canvas.style.height=b.framebufferHeight+"px"):this._engine.setSize(b.framebufferWidth,b.framebufferHeight)):this._originalCanvasSize&&(this._canvas!==this._engine.getRenderingCanvas()?(this._canvas.style.width=this._originalCanvasSize.width+"px",this._canvas.style.height=this._originalCanvasSize.height+"px"):this._engine.setSize(this._originalCanvasSize.width,this._originalCanvasSize.height)))},a.prototype._setManagedOutputCanvas=function(a){this._removeCanvas(),a?(this._originalCanvasSize={width:a.offsetWidth,height:a.offsetHeight},this._canvas=a,this.canvasContext=this._canvas.getContext("webgl2"),this.canvasContext||(this.canvasContext=this._canvas.getContext("webgl"))):(this._canvas=null,this.canvasContext=null)},a}(),wd=function(){function a(a){var b=this;this.scene=a,this._sessionEnded=!1,this._baseLayer=null,this._renderTargetTextures=[],this.currentTimestamp=-1,this.defaultHeightCompensation=1.7,this.onXRFrameObservable=new f.c,this.onXRReferenceSpaceChanged=new f.c,this.onXRSessionEnded=new f.c,this.onXRSessionInit=new f.c,this._engine=a.getEngine(),this._engine.onDisposeObservable.addOnce(function(){b._engine=null})}return Object.defineProperty(a.prototype,"referenceSpace",{get:function(){return this._referenceSpace},set:function(a){this._referenceSpace=a,this.onXRReferenceSpaceChanged.notifyObservers(this._referenceSpace)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"sessionMode",{get:function(){return this._sessionMode},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this._sessionEnded||this.exitXRAsync(),this.onXRFrameObservable.clear(),this.onXRSessionEnded.clear(),this.onXRReferenceSpaceChanged.clear(),this.onXRSessionInit.clear()},a.prototype.exitXRAsync=function(){return this.session&&!this._sessionEnded?(this._sessionEnded=!0,this.session.end()["catch"](function(a){q.a.Warn("Could not end XR session.")})):Promise.resolve()},a.prototype.getRenderTargetTextureForEye=function(a){return this._rttProvider.getRenderTargetForEye(a)},a.prototype.getWebXRRenderTarget=function(a){var b=this.scene.getEngine();return this._xrNavigator.xr["native"]?this._xrNavigator.xr.getWebXRRenderTarget(b):((a=a||ud.GetDefaults(b)).canvasElement=a.canvasElement||b.getRenderingCanvas()||void 0,new vd(this,a))},a.prototype.initializeAsync=function(){return this._xrNavigator=navigator,this._xrNavigator.xr?Promise.resolve():Promise.reject("WebXR not available")},a.prototype.initializeSessionAsync=function(a,b){var c=this;return void 0===a&&(a="immersive-vr"),void 0===b&&(b={}),this._xrNavigator.xr.requestSession(a,b).then(function(b){return c.session=b,c._sessionMode=a,c.onXRSessionInit.notifyObservers(b),c._sessionEnded=!1,c.session.addEventListener("end",function(){c._sessionEnded=!0,c.onXRSessionEnded.notifyObservers(null),c._rttProvider=null,c._engine&&(c._engine.framebufferDimensionsObject=null,c._engine.restoreDefaultFramebuffer(),c._engine.customAnimationFrameRequester=null,c._engine._renderLoop()),c.isNative&&(c._renderTargetTextures.forEach(function(a){return a.dispose()}),c._renderTargetTextures.length=0)},{once:!0}),c.session})},a.prototype.isSessionSupportedAsync=function(b){return a.IsSessionSupportedAsync(b)},a.prototype.resetReferenceSpace=function(){this.referenceSpace=this.baseReferenceSpace},a.prototype.runXRRenderLoop=function(){var a=this;if(!this._sessionEnded&&this._engine){var b,c,d,e;(this._engine.customAnimationFrameRequester={requestAnimationFrame:this.session.requestAnimationFrame.bind(this.session),renderFunction:function(b,c){!a._sessionEnded&&a._engine&&(a.currentFrame=c,a.currentTimestamp=b,c&&(a._engine.framebufferDimensionsObject=a._baseLayer,a.onXRFrameObservable.notifyObservers(c),a._engine._renderLoop(),a._engine.framebufferDimensionsObject=null))}},this._xrNavigator.xr["native"])?this._rttProvider=this._xrNavigator.xr.getNativeRenderTargetProvider(this.session,this._createRenderTargetTexture.bind(this),this._destroyRenderTargetTexture.bind(this)):(this._rttProvider={getRenderTargetForEye:function(){var f=a._baseLayer;return f.framebufferWidth===c&&f.framebufferHeight===d&&f.framebuffer===e||(b=a._createRenderTargetTexture(f.framebufferWidth,f.framebufferHeight,f.framebuffer),c=f.framebufferWidth,d=f.framebufferHeight,e=f.framebuffer),b}},this._engine.framebufferDimensionsObject=this._baseLayer);"undefined"!=typeof window&&window.cancelAnimationFrame&&window.cancelAnimationFrame(this._engine._frameHandler),this._engine._renderLoop()}},a.prototype.setReferenceSpaceTypeAsync=function(a){var b=this;return void 0===a&&(a="local-floor"),this.session.requestReferenceSpace(a).then(function(a){return a},function(a){return q.a.Error("XR.requestReferenceSpace failed for the following reason: "),q.a.Error(a),q.a.Log("Defaulting to universally-supported "viewer" reference space type."),b.session.requestReferenceSpace("viewer").then(function(a){var c=new XRRigidTransform({x:0,y:-b.defaultHeightCompensation,z:0});return a.getOffsetReferenceSpace(c)},function(a){throw q.a.Error(a),"XR initialization failed: required "viewer" reference space type not supported."})}).then(function(a){return b.session.requestReferenceSpace("viewer").then(function(c){return b.viewerReferenceSpace=c,a})}).then(function(a){return b.referenceSpace=b.baseReferenceSpace=a,b.referenceSpace})},a.prototype.updateRenderStateAsync=function(a){return a.baseLayer&&(this._baseLayer=a.baseLayer),this.session.updateRenderState(a)},a.IsSessionSupportedAsync=function(a){if(!navigator.xr)return Promise.resolve(!1);var b=navigator.xr.isSessionSupported||navigator.xr.supportsSession;return b?b.call(navigator.xr,a).then(function(a){a=void 0===a||a;return Promise.resolve(a)})["catch"](function(a){return q.a.Warn(a),Promise.resolve(!1)}):Promise.resolve(!1)},Object.defineProperty(a.prototype,"isNative",{get:function(){var a;return null!==(a=this._xrNavigator.xr["native"])&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentFrameRate",{get:function(){var a;return null===(a=this.session)||void 0===a?void 0:a.frameRate},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportedFrameRates",{get:function(){var a;return null===(a=this.session)||void 0===a?void 0:a.supportedFrameRates},enumerable:!1,configurable:!0}),a.prototype.updateTargetFrameRate=function(a){return this.session.updateTargetFrameRate(a)},Object.defineProperty(a.prototype,"isFixedFoveationSupported",{get:function(){var a;return null!==!!(null===(a=this._baseLayer)||void 0===a?void 0:a.fixedFoveation)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"fixedFoveation",{get:function(){var a;return void 0!==(null===(a=this._baseLayer)||void 0===a?void 0:a.fixedFoveation)?this._baseLayer.fixedFoveation:null},set:function(a){var b;a=Math.max(0,Math.min(1,a||0));void 0!==(null===(b=this._baseLayer)||void 0===b?void 0:b.fixedFoveation)&&(this._baseLayer.fixedFoveation=a)},enumerable:!1,configurable:!0}),a.prototype._createRenderTargetTexture=function(a,b,c){if(!this._engine)throw new Error("Engine is disposed");var d=new pb.a(this._engine,pb.b.Unknown,!0);d.width=a,d.height=b;a=new cd("XR renderTargetTexture",{width:a,height:b},this.scene);b=a.renderTarget;return b.setTexture(d,0),b._framebuffer=c,a._texture=d,a.disableRescaling(),"immersive-ar"===this._sessionMode&&(a.skipInitialClear=!0),this._renderTargetTextures.push(a),a},a.prototype._destroyRenderTargetTexture=function(a){this._renderTargetTextures.splice(this._renderTargetTextures.indexOf(a),1),a.dispose()},a}();!function(a){a[a.ENTERING_XR=0]="ENTERING_XR",a[a.EXITING_XR=1]="EXITING_XR",a[a.IN_XR=2]="IN_XR",a[a.NOT_IN_XR=3]="NOT_IN_XR"}(rd||(rd={})),(function(a){a[a.NOT_TRACKING=0]="NOT_TRACKING",a[a.TRACKING_LOST=1]="TRACKING_LOST",a[a.TRACKING=2]="TRACKING"})(sd||(sd={}));var xd=c(56),yd=c(18);function zd(a){for(var b=[],c=[],d=[],e=[],f=a.diameter||1,h=a.thickness||.5,i=a.tessellation||16,j=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,k=i+1,l=0;l<=i;l++)for(var o=l/i,t=l*Math.PI*2/i-Math.PI/2,t=g.a.Translation(f/2,0,0).multiply(g.a.RotationY(t)),u=0;u<=i;u++){var v=1-u/i,w=u*Math.PI*2/i+Math.PI,x=Math.cos(w);w=Math.sin(w);x=new g.e(x,w,0);w=x.scale(h/2);v=new g.d(o,v);w=g.e.TransformCoordinates(w,t),x=g.e.TransformNormal(x,t),c.push(w.x,w.y,w.z),d.push(x.x,x.y,x.z),e.push(v.x,v.y);w=(l+1)%k;x=(u+1)%k;b.push(l*k+u),b.push(l*k+x),b.push(w*k+u),b.push(l*k+x),b.push(w*k+x),b.push(w*k+u)}yd.a._ComputeSides(j,c,b,d,e,a.frontUVs,a.backUVs);v=new yd.a;return v.indices=b,v.positions=c,v.normals=d,v.uvs=e,v}function Ad(a,b,c){a=new R.a(a,c);return b.sideOrientation=R.a._GetDefaultSideOrientation(b.sideOrientation),a._originalBuilderSideOrientation=b.sideOrientation,zd(b).applyToMesh(a,b.updatable),a}var Bd={CreateTorus:Ad};yd.a.CreateTorus=zd,R.a.CreateTorus=function(a,b,c,d,e,f,g){return Ad(a,{diameter:b,thickness:c,tessellation:d,sideOrientation:g,updatable:f},e)},R.a._GroundMeshParser=function(a,b){return Cd.Parse(a,b)};var Cd=function(a){function b(b,c){b=a.call(this,b,c)||this;return b.generateOctree=!1,b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"GroundMesh"},Object.defineProperty(b.prototype,"subdivisions",{get:function(){return Math.min(this._subdivisionsX,this._subdivisionsY)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"subdivisionsX",{get:function(){return this._subdivisionsX},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"subdivisionsY",{get:function(){return this._subdivisionsY},enumerable:!1,configurable:!0}),b.prototype.optimize=function(a,b){(void 0===b&&(b=32),this._subdivisionsX=a,this._subdivisionsY=a,this.subdivide(a)),this.createOrUpdateSubmeshesOctree&&this.createOrUpdateSubmeshesOctree(b)},b.prototype.getHeightAtCoordinates=function(a,b){var c=this.getWorldMatrix(),d=g.c.Matrix[5];c.invertToRef(d);var e=g.c.Vector3[8];if(g.e.TransformCoordinatesFromFloatsToRef(a,0,b,d,e),a=e.x,b=e.z,athis._maxX||bthis._maxZ)return this.position.y;this._heightQuads&&0!=this._heightQuads.length||(this._initHeightQuads(),this._computeHeightQuads());d=this._getFacetAt(a,b);a=-(d.x*a+d.z*b+d.w)/d.y;return g.e.TransformCoordinatesFromFloatsToRef(0,a,0,c,e),e.y},b.prototype.getNormalAtCoordinates=function(a,b){var c=new g.e(0,1,0);return this.getNormalAtCoordinatesToRef(a,b,c),c},b.prototype.getNormalAtCoordinatesToRef=function(a,b,c){var d=this.getWorldMatrix(),e=g.c.Matrix[5];d.invertToRef(e);var f=g.c.Vector3[8];if(g.e.TransformCoordinatesFromFloatsToRef(a,0,b,e,f),a=f.x,b=f.z,athis._maxX||bthis._maxZ)return this;this._heightQuads&&0!=this._heightQuads.length||(this._initHeightQuads(),this._computeHeightQuads());e=this._getFacetAt(a,b);return g.e.TransformNormalFromFloatsToRef(e.x,e.y,e.z,d,c),this},b.prototype.updateCoordinateHeights=function(){return this._heightQuads&&0!=this._heightQuads.length||this._initHeightQuads(),this._computeHeightQuads(),this},b.prototype._getFacetAt=function(a,b){var c=Math.floor((a+this._maxX)*this._subdivisionsX/this._width),d=Math.floor(-(b+this._maxZ)*this._subdivisionsY/this._height+this._subdivisionsY);d=this._heightQuads[d*this._subdivisionsX+c];return ba.maxHeight){k=!0;var l=a.maxHeight;a.maxHeight=a.minHeight,a.minHeight=l}for(l=0;l<=a.subdivisions;l++)for(b=0;b<=a.subdivisions;b++){var s=new g.e(b*a.width/a.subdivisions-a.width/2,0,(a.subdivisions-l)*a.height/a.subdivisions-a.height/2),t=4*(((s.x+a.width/2)/a.width*(a.bufferWidth-1)|0)+((1-(s.z+a.height/2)/a.height)*(a.bufferHeight-1)|0)*a.bufferWidth),u=a.buffer[t]/255,v=a.buffer[t+1]/255,w=a.buffer[t+2]/255;t=a.buffer[t+3]/255;k&&(u=1-u,v=1-v,w=1-w);u=u*i.r+v*i.g+w*i.b;s.y=t>=j?a.minHeight+(a.maxHeight-a.minHeight)*u:a.minHeight-ib.a,d.push(s.x,s.y,s.z),e.push(0,0,0),f.push(b/a.subdivisions,1-l/a.subdivisions)}for(l=0;l=a.minHeight;k=d[3*w+1]>=a.minHeight;j=d[3*t+1]>=a.minHeight;s&&k&&j&&(c.push(v),c.push(w),c.push(t)),d[3*u+1]>=a.minHeight&&s&&j&&(c.push(u),c.push(v),c.push(t))}yd.a.ComputeNormals(d,c,e);i=new yd.a;return i.indices=c,i.positions=d,i.normals=e,i.uvs=f,i}function Gd(a,b,c){a=new Cd(a,c);return a._setReady(!1),a._subdivisionsX=b.subdivisionsX||b.subdivisions||1,a._subdivisionsY=b.subdivisionsY||b.subdivisions||1,a._width=b.width||1,a._height=b.height||1,a._maxX=a._width/2,a._maxZ=a._height/2,a._minX=-a._maxX,a._minZ=-a._maxZ,Dd(b).applyToMesh(a,b.updatable),a._setReady(!0),a}function Hd(a,b,c){void 0===c&&(c=null);a=new R.a(a,c);return Ed(b).applyToMesh(a,b.updatable),a}function Id(a,b,c,d){void 0===d&&(d=null);var e=c.width||10,f=c.height||10,g=c.subdivisions||1,i=c.minHeight||0,j=c.maxHeight||1,k=c.colorFilter||new h.a(.3,.59,.11),l=c.alphaFilter||0,p=c.updatable,q=c.onReady;d=d||C.a.LastCreatedScene;var r=new Cd(a,d);r._subdivisionsX=g,r._subdivisionsY=g,r._width=e,r._height=f,r._maxX=r._width/2,r._maxZ=r._height/2,r._minX=-r._maxX,r._minZ=-r._maxZ,r._setReady(!1);return T.b.LoadImage(b,function(a){var b=a.width,c=a.height;if(!d.isDisposed){a=null==d?void 0:d.getEngine().resizeImageBitmap(a,b,c);Fd({width:e,height:f,subdivisions:g,minHeight:i,maxHeight:j,colorFilter:k,buffer:a,bufferWidth:b,bufferHeight:c,alphaFilter:l}).applyToMesh(r,p),q&&q(r),r._setReady(!0)}},function(){},d.offlineProvider),r}var Jd={CreateGround:Gd,CreateGroundFromHeightMap:Id,CreateTiledGround:Hd};yd.a.CreateGround=Dd,yd.a.CreateTiledGround=Ed,yd.a.CreateGroundFromHeightMap=Fd,R.a.CreateGround=function(a,b,c,d,e,f){return Gd(a,{width:b,height:c,subdivisions:d,updatable:f},e)},R.a.CreateTiledGround=function(a,b,c,d,e,f,g,h,i){return Hd(a,{xmin:b,zmin:c,xmax:d,zmax:e,subdivisions:f,precision:g,updatable:i},h)},R.a.CreateGroundFromHeightMap=function(a,b,c,d,e,f,g,h,i,j,k){return Id(a,b,{width:c,height:d,subdivisions:e,minHeight:f,maxHeight:g,updatable:i,onReady:j,alphaFilter:k},h)};var Kd;a=function(){function a(b,c){if(void 0===c&&(c=null),this.scene=b,this._pointerDownOnMeshAsked=!1,this._isActionableMesh=!1,this._teleportationRequestInitiated=!1,this._teleportationBackRequestInitiated=!1,this._rotationRightAsked=!1,this._rotationLeftAsked=!1,this._dpadPressed=!0,this._activePointer=!1,this._id=a._idCounter++,c)this._gazeTracker=c.clone("gazeTracker");else{this._gazeTracker=Ad("gazeTracker",{diameter:.0035,thickness:.0025,tessellation:20,updatable:!1},b),this._gazeTracker.bakeCurrentTransformIntoVertices(),this._gazeTracker.isPickable=!1,this._gazeTracker.isVisible=!1;c=new pd.a("targetMat",b);c.specularColor=h.a.Black(),c.emissiveColor=new h.a(.7,.7,.7),c.backFaceCulling=!1,this._gazeTracker.material=c}}return a.prototype._getForwardRay=function(a){return new nc.a(g.e.Zero(),new g.e(0,0,a))},a.prototype._selectionPointerDown=function(){this._pointerDownOnMeshAsked=!0,this._currentHit&&this.scene.simulatePointerDown(this._currentHit,{pointerId:this._id})},a.prototype._selectionPointerUp=function(){this._currentHit&&this.scene.simulatePointerUp(this._currentHit,{pointerId:this._id}),this._pointerDownOnMeshAsked=!1},a.prototype._activatePointer=function(){this._activePointer=!0},a.prototype._deactivatePointer=function(){this._activePointer=!1},a.prototype._updatePointerDistance=function(a){void 0===a&&100},a.prototype.dispose=function(){this._interactionsEnabled=!1,this._teleportationEnabled=!1,this._gazeTracker&&this._gazeTracker.dispose()},a._idCounter=0,a}();var Ld=function(a){function b(b,c,d){var e=a.call(this,c,d)||this;e.webVRController=b,e._laserPointer=Object(xd.a)("laserPointer",{updatable:!1,height:1,diameterTop:.004,diameterBottom:2e-4,tessellation:20,subdivisions:1},c);d=new pd.a("laserPointerMat",c);if(d.emissiveColor=new h.a(.7,.7,.7),d.alpha=.6,e._laserPointer.material=d,e._laserPointer.rotation.x=Math.PI/2,e._laserPointer.position.z=-.5,e._laserPointer.isVisible=!1,e._laserPointer.isPickable=!1,!b.mesh){d=new R.a("preloadControllerMesh",c);c=new R.a(rc.POINTING_POSE,c);c.rotation.x=-.7,d.addChild(c),b.attachToMesh(d)}return e._setLaserPointerParent(b.mesh),e._meshAttachedObserver=b._meshAttachedObservable.add(function(a){e._setLaserPointerParent(a)}),e}return Object(l.d)(b,a),b.prototype._getForwardRay=function(a){return this.webVRController.getForwardRay(a)},b.prototype._activatePointer=function(){a.prototype._activatePointer.call(this),this._laserPointer.isVisible=!0},b.prototype._deactivatePointer=function(){a.prototype._deactivatePointer.call(this),this._laserPointer.isVisible=!1},b.prototype._setLaserPointerColor=function(a){this._laserPointer.material.emissiveColor=a},b.prototype._setLaserPointerLightingDisabled=function(a){this._laserPointer.material.disableLighting=a},b.prototype._setLaserPointerParent=function(a){var b=function(a){a.isPickable=!1,a.getChildMeshes().forEach(function(a){b(a)})};b(a);var c=a.getChildren(void 0,!1);a=a;this.webVRController._pointingPoseNode=null;for(var d=0;d=0){a=c[d],this.webVRController._pointingPoseNode=a;break}this._laserPointer.parent=a},b.prototype._updatePointerDistance=function(a){void 0===a&&(a=100),this._laserPointer.scaling.y=a,this._laserPointer.position.z=-a/2},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._laserPointer.dispose(),this._meshAttachedObserver&&this.webVRController._meshAttachedObservable.remove(this._meshAttachedObserver)},b}(a),Md=function(a){function b(b,c){c=a.call(this,c)||this;return c.getCamera=b,c}return Object(l.d)(b,a),b.prototype._getForwardRay=function(a){var b=this.getCamera();return b?b.getForwardRay(a):new nc.a(g.e.Zero(),g.e.Forward())},b}(a),Nd=function(){},Od=function(){function a(b,c){var d=this;if(void 0===c&&(c={}),this.webVROptions=c,this._webVRsupported=!1,this._webVRready=!1,this._webVRrequesting=!1,this._webVRpresenting=!1,this._fullscreenVRpresenting=!1,this.enableGazeEvenWhenNoPointerLock=!1,this.exitVROnDoubleTap=!0,this.onEnteringVRObservable=new f.c,this.onAfterEnteringVRObservable=new f.c,this.onExitingVRObservable=new f.c,this.onControllerMeshLoadedObservable=new f.c,this._useCustomVRButton=!1,this._teleportationRequested=!1,this._teleportActive=!1,this._floorMeshesCollection=[],this._teleportationMode=a.TELEPORTATIONMODE_CONSTANTTIME,this._teleportationTime=122,this._teleportationSpeed=20,this._rotationAllowed=!0,this._teleportBackwardsVector=new g.e(0,-1,-1),this._isDefaultTeleportationTarget=!0,this._teleportationFillColor="#444444",this._teleportationBorderColor="#FFFFFF",this._rotationAngle=0,this._haloCenter=new g.e(0,0,0),this._padSensibilityUp=.65,this._padSensibilityDown=.35,this._leftController=null,this._rightController=null,this._gazeColor=new h.a(.7,.7,.7),this._laserColor=new h.a(.7,.7,.7),this._pickedLaserColor=new h.a(.2,.2,1),this._pickedGazeColor=new h.a(0,0,1),this.onNewMeshSelected=new f.c,this.onMeshSelectedWithController=new f.c,this.onNewMeshPicked=new f.c,this.onBeforeCameraTeleport=new f.c,this.onAfterCameraTeleport=new f.c,this.onSelectedMeshUnselected=new f.c,this.teleportationEnabled=!0,this._teleportationInitialized=!1,this._interactionsEnabled=!1,this._interactionsRequested=!1,this._displayGaze=!0,this._displayLaserPointer=!0,this.updateGazeTrackerScale=!0,this.updateGazeTrackerColor=!0,this.updateControllerLaserColor=!0,this.requestPointerLockOnFullScreen=!0,this.xrTestDone=!1,this._onResize=function(){d.moveButtonToBottomRight(),d._fullscreenVRpresenting&&d._webVRready&&d.exitVR()},this._onFullscreenChange=function(){var a=document;void 0!==a.fullscreen?d._fullscreenVRpresenting=document.fullscreen:void 0!==a.mozFullScreen?d._fullscreenVRpresenting=a.mozFullScreen:void 0!==a.webkitIsFullScreen?d._fullscreenVRpresenting=a.webkitIsFullScreen:void 0!==a.msIsFullScreen?d._fullscreenVRpresenting=a.msIsFullScreen:void 0!==document.msFullscreenElement&&(d._fullscreenVRpresenting=document.msFullscreenElement),!d._fullscreenVRpresenting&&d._inputElement&&(d.exitVR(),!d._useCustomVRButton&&d._btnVR&&(d._btnVR.style.top=d._inputElement.offsetTop+d._inputElement.offsetHeight-70+"px",d._btnVR.style.left=d._inputElement.offsetLeft+d._inputElement.offsetWidth-100+"px",d.updateButtonVisibility()))},this._cachedAngularSensibility={angularSensibilityX:null,angularSensibilityY:null,angularSensibility:null},this.beforeRender=function(){d._leftController&&d._leftController._activePointer&&d._castRayAndSelectObject(d._leftController),d._rightController&&d._rightController._activePointer&&d._castRayAndSelectObject(d._rightController),d._noControllerIsActive&&(d._scene.getEngine().isPointerLock||d.enableGazeEvenWhenNoPointerLock)?d._castRayAndSelectObject(d._cameraGazer):d._cameraGazer._gazeTracker.isVisible=!1},this._onNewGamepadConnected=function(a){if(a.type!==Cb.POSE_ENABLED)a.leftStick&&a.onleftstickchanged(function(a){d._teleportationInitialized&&d.teleportationEnabled&&(!d._leftController&&!d._rightController||d._leftController&&!d._leftController._activePointer&&d._rightController&&!d._rightController._activePointer)&&(d._checkTeleportWithRay(a,d._cameraGazer),d._checkTeleportBackwards(a,d._cameraGazer))}),a.rightStick&&a.onrightstickchanged(function(a){d._teleportationInitialized&&d._checkRotate(a,d._cameraGazer)}),a.type===Cb.XBOX&&(a.onbuttondown(function(a){d._interactionsEnabled&&a===oc.A&&d._cameraGazer._selectionPointerDown()}),a.onbuttonup(function(a){d._interactionsEnabled&&a===oc.A&&d._cameraGazer._selectionPointerUp()}));else{a=a;var b=new Ld(a,d._scene,d._cameraGazer._gazeTracker);"right"===a.hand||d._leftController&&d._leftController.webVRController!=a?d._rightController=b:d._leftController=b,d._tryEnableInteractionOnController(b)}},this._tryEnableInteractionOnController=function(a){d._interactionsRequested&&!a._interactionsEnabled&&d._enableInteractionOnController(a),d._teleportationRequested&&!a._teleportationEnabled&&d._enableTeleportationOnController(a)},this._onNewGamepadDisconnected=function(a){a instanceof nd&&("left"===a.hand&&null!=d._leftController&&(d._leftController.dispose(),d._leftController=null),"right"===a.hand&&null!=d._rightController&&(d._rightController.dispose(),d._rightController=null))},this._workingVector=g.e.Zero(),this._workingQuaternion=g.b.Identity(),this._workingMatrix=g.a.Identity(),q.a.Warn("WebVR is deprecated. Please avoid using this experience helper and use the WebXR experience helper instead"),this._scene=b,this._inputElement=b.getEngine().getInputElement(),"getVRDisplays"in navigator||void 0!==c.useXR||(c.useXR=!0),void 0===c.createFallbackVRDeviceOrientationFreeCamera&&(c.createFallbackVRDeviceOrientationFreeCamera=!0),void 0===c.createDeviceOrientationCamera&&(c.createDeviceOrientationCamera=!0),void 0===c.laserToggle&&(c.laserToggle=!0),void 0===c.defaultHeight&&(c.defaultHeight=1.7),c.useCustomVRButton&&(this._useCustomVRButton=!0,c.customVRButton&&(this._btnVR=c.customVRButton)),c.rayLength&&(this._rayLength=c.rayLength),this._defaultHeight=c.defaultHeight,c.positionScale&&(this._rayLength*=c.positionScale,this._defaultHeight*=c.positionScale),this._hasEnteredVR=!1,this._scene.activeCamera?this._position=this._scene.activeCamera.position.clone():this._position=new g.e(0,this._defaultHeight,0),c.createDeviceOrientationCamera||!this._scene.activeCamera){if(this._deviceOrientationCamera=new gc("deviceOrientationVRHelper",this._position.clone(),b),this._scene.activeCamera&&(this._deviceOrientationCamera.minZ=this._scene.activeCamera.minZ,this._deviceOrientationCamera.maxZ=this._scene.activeCamera.maxZ,this._scene.activeCamera instanceof cc&&this._scene.activeCamera.rotation)){var e=this._scene.activeCamera;e.rotationQuaternion?this._deviceOrientationCamera.rotationQuaternion.copyFrom(e.rotationQuaternion):this._deviceOrientationCamera.rotationQuaternion.copyFrom(g.b.RotationYawPitchRoll(e.rotation.y,e.rotation.x,e.rotation.z)),this._deviceOrientationCamera.rotation=e.rotation.clone()}this._scene.activeCamera=this._deviceOrientationCamera,this._inputElement&&this._scene.activeCamera.attachControl()}else this._existingCamera=this._scene.activeCamera;this.webVROptions.useXR&&navigator.xr?wd.IsSessionSupportedAsync("immersive-vr").then(function(a){a?(q.a.Log("Using WebXR. It is recommended to use the WebXRDefaultExperience directly"),b.createDefaultXRExperienceAsync({floorMeshes:c.floorMeshes||[]}).then(function(a){d.xr=a,d.xrTestDone=!0,d._cameraGazer=new Md(function(){return d.xr.baseExperience.camera},b),d.xr.baseExperience.onStateChangedObservable.add(function(a){switch(a){case rd.ENTERING_XR:d.onEnteringVRObservable.notifyObservers(d),d._interactionsEnabled||d.xr.pointerSelection.detach(),d.xr.pointerSelection.displayLaserPointer=d._displayLaserPointer;break;case rd.EXITING_XR:d.onExitingVRObservable.notifyObservers(d),d._scene.getEngine().resize();break;case rd.IN_XR:d._hasEnteredVR=!0;break;case rd.NOT_IN_XR:d._hasEnteredVR=!1}})})):d.completeVRInit(b,c)}):this.completeVRInit(b,c)}return Object.defineProperty(a.prototype,"onEnteringVR",{get:function(){return this.onEnteringVRObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onExitingVR",{get:function(){return this.onExitingVRObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onControllerMeshLoaded",{get:function(){return this.onControllerMeshLoadedObservable},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"teleportationTarget",{get:function(){return this._teleportationTarget},set:function(a){a&&(a.name="teleportationTarget",this._isDefaultTeleportationTarget=!1,this._teleportationTarget=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"gazeTrackerMesh",{get:function(){return this._cameraGazer._gazeTracker},set:function(a){a&&(this._cameraGazer._gazeTracker&&this._cameraGazer._gazeTracker.dispose(),this._leftController&&this._leftController._gazeTracker&&this._leftController._gazeTracker.dispose(),this._rightController&&this._rightController._gazeTracker&&this._rightController._gazeTracker.dispose(),this._cameraGazer._gazeTracker=a,this._cameraGazer._gazeTracker.bakeCurrentTransformIntoVertices(),this._cameraGazer._gazeTracker.isPickable=!1,this._cameraGazer._gazeTracker.isVisible=!1,this._cameraGazer._gazeTracker.name="gazeTracker",this._leftController&&(this._leftController._gazeTracker=this._cameraGazer._gazeTracker.clone("gazeTracker")),this._rightController&&(this._rightController._gazeTracker=this._cameraGazer._gazeTracker.clone("gazeTracker")))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"leftControllerGazeTrackerMesh",{get:function(){return this._leftController?this._leftController._gazeTracker:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rightControllerGazeTrackerMesh",{get:function(){return this._rightController?this._rightController._gazeTracker:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"displayGaze",{get:function(){return this._displayGaze},set:function(a){this._displayGaze=a,a||(this._cameraGazer._gazeTracker.isVisible=!1,this._leftController&&(this._leftController._gazeTracker.isVisible=!1),this._rightController&&(this._rightController._gazeTracker.isVisible=!1))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"displayLaserPointer",{get:function(){return this._displayLaserPointer},set:function(a){this._displayLaserPointer=a,a?(this._rightController&&this._rightController._activatePointer(),this._leftController&&this._leftController._activatePointer()):(this._rightController&&(this._rightController._deactivatePointer(),this._rightController._gazeTracker.isVisible=!1),this._leftController&&(this._leftController._deactivatePointer(),this._leftController._gazeTracker.isVisible=!1))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"deviceOrientationCamera",{get:function(){return this._deviceOrientationCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"currentVRCamera",{get:function(){return this._webVRready?this._webVRCamera:this._scene.activeCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"webVRCamera",{get:function(){return this._webVRCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vrDeviceOrientationCamera",{get:function(){return this._vrDeviceOrientationCamera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vrButton",{get:function(){return this._btnVR},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"_teleportationRequestInitiated",{get:function(){return this._cameraGazer._teleportationRequestInitiated||null!==this._leftController&&this._leftController._teleportationRequestInitiated||null!==this._rightController&&this._rightController._teleportationRequestInitiated},enumerable:!1,configurable:!0}),a.prototype.completeVRInit=function(a,b){var c=this;if(this.xrTestDone=!0,b.createFallbackVRDeviceOrientationFreeCamera&&(b.useMultiview&&(b.vrDeviceOrientationCameraMetrics||(b.vrDeviceOrientationCameraMetrics=Yc.GetDefault()),b.vrDeviceOrientationCameraMetrics.multiviewEnabled=!0),this._vrDeviceOrientationCamera=new id("VRDeviceOrientationVRHelper",this._position,this._scene,!0,b.vrDeviceOrientationCameraMetrics),this._vrDeviceOrientationCamera.angularSensibility=Number.MAX_VALUE),this._webVRCamera=new md("WebVRHelper",this._position,this._scene,b),this._webVRCamera.useStandingMatrix(),this._cameraGazer=new Md(function(){return c.currentVRCamera},a),!this._useCustomVRButton){this._btnVR=document.createElement("BUTTON"),this._btnVR.className="babylonVRicon",this._btnVR.id="babylonVRiconbtn",this._btnVR.title="Click to switch to VR";var d=".babylonVRicon { position: absolute; right: 20px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-image: url("+(window.SVGSVGElement?"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%222048%22%20height%3D%221152%22%20viewBox%3D%220%200%202048%201152%22%20version%3D%221.1%22%3E%3Cpath%20transform%3D%22rotate%28180%201024%2C576.0000000000001%29%22%20d%3D%22m1109%2C896q17%2C0%2030%2C-12t13%2C-30t-12.5%2C-30.5t-30.5%2C-12.5l-170%2C0q-18%2C0%20-30.5%2C12.5t-12.5%2C30.5t13%2C30t30%2C12l170%2C0zm-85%2C256q59%2C0%20132.5%2C-1.5t154.5%2C-5.5t164.5%2C-11.5t163%2C-20t150%2C-30t124.5%2C-41.5q23%2C-11%2042%2C-24t38%2C-30q27%2C-25%2041%2C-61.5t14%2C-72.5l0%2C-257q0%2C-123%20-47%2C-232t-128%2C-190t-190%2C-128t-232%2C-47l-81%2C0q-37%2C0%20-68.5%2C14t-60.5%2C34.5t-55.5%2C45t-53%2C45t-53%2C34.5t-55.5%2C14t-55.5%2C-14t-53%2C-34.5t-53%2C-45t-55.5%2C-45t-60.5%2C-34.5t-68.5%2C-14l-81%2C0q-123%2C0%20-232%2C47t-190%2C128t-128%2C190t-47%2C232l0%2C257q0%2C68%2038%2C115t97%2C73q54%2C24%20124.5%2C41.5t150%2C30t163%2C20t164.5%2C11.5t154.5%2C5.5t132.5%2C1.5zm939%2C-298q0%2C39%20-24.5%2C67t-58.5%2C42q-54%2C23%20-122%2C39.5t-143.5%2C28t-155.5%2C19t-157%2C11t-148.5%2C5t-129.5%2C1.5q-59%2C0%20-130%2C-1.5t-148%2C-5t-157%2C-11t-155.5%2C-19t-143.5%2C-28t-122%2C-39.5q-34%2C-14%20-58.5%2C-42t-24.5%2C-67l0%2C-257q0%2C-106%2040.5%2C-199t110%2C-162.5t162.5%2C-109.5t199%2C-40l81%2C0q27%2C0%2052%2C14t50%2C34.5t51%2C44.5t55.5%2C44.5t63.5%2C34.5t74%2C14t74%2C-14t63.5%2C-34.5t55.5%2C-44.5t51%2C-44.5t50%2C-34.5t52%2C-14l14%2C0q37%2C0%2070%2C0.5t64.5%2C4.5t63.5%2C12t68%2C23q71%2C30%20128.5%2C78.5t98.5%2C110t63.5%2C133.5t22.5%2C149l0%2C257z%22%20fill%3D%22white%22%20/%3E%3C/svg%3E%0A":"https://cdn.babylonjs.com/Assets/vrButton.png")+"); background-size: 80%; background-repeat:no-repeat; background-position: center; border: none; outline: none; transition: transform 0.125s ease-out } .babylonVRicon:hover { transform: scale(1.05) } .babylonVRicon:active {background-color: rgba(51,51,51,1) } .babylonVRicon:focus {background-color: rgba(51,51,51,1) }";d+=".babylonVRicon.vrdisplaypresenting { display: none; }";var e=document.createElement("style");e.appendChild(document.createTextNode(d)),document.getElementsByTagName("head")[0].appendChild(e),this.moveButtonToBottomRight()}this._btnVR&&this._btnVR.addEventListener("click",function(){c.isInVRMode?c._scene.getEngine().disableVR():c.enterVR()});d=this._scene.getEngine().getHostWindow();d&&(d.addEventListener("resize",this._onResize),document.addEventListener("fullscreenchange",this._onFullscreenChange,!1),document.addEventListener("mozfullscreenchange",this._onFullscreenChange,!1),document.addEventListener("webkitfullscreenchange",this._onFullscreenChange,!1),document.addEventListener("msfullscreenchange",this._onFullscreenChange,!1),document.onmsfullscreenchange=this._onFullscreenChange,b.createFallbackVRDeviceOrientationFreeCamera?this.displayVRButton():this._scene.getEngine().onVRDisplayChangedObservable.add(function(a){a.vrDisplay&&c.displayVRButton()}),this._onKeyDown=function(a){27===a.keyCode&&c.isInVRMode&&c.exitVR()},document.addEventListener("keydown",this._onKeyDown),this._scene.onPrePointerObservable.add(function(){c._hasEnteredVR&&c.exitVROnDoubleTap&&(c.exitVR(),c._fullscreenVRpresenting&&c._scene.getEngine().exitFullscreen())},Ua.a.POINTERDOUBLETAP,!1),this._onVRDisplayChanged=function(a){return c.onVRDisplayChanged(a)},this._onVrDisplayPresentChange=function(){return c.onVrDisplayPresentChange()},this._onVRRequestPresentStart=function(){c._webVRrequesting=!0,c.updateButtonVisibility()},this._onVRRequestPresentComplete=function(){c._webVRrequesting=!1,c.updateButtonVisibility()},a.getEngine().onVRDisplayChangedObservable.add(this._onVRDisplayChanged),a.getEngine().onVRRequestPresentStart.add(this._onVRRequestPresentStart),a.getEngine().onVRRequestPresentComplete.add(this._onVRRequestPresentComplete),d.addEventListener("vrdisplaypresentchange",this._onVrDisplayPresentChange),a.onDisposeObservable.add(function(){c.dispose()}),this._webVRCamera.onControllerMeshLoadedObservable.add(function(a){return c._onDefaultMeshLoaded(a)}),this._scene.gamepadManager.onGamepadConnectedObservable.add(this._onNewGamepadConnected),this._scene.gamepadManager.onGamepadDisconnectedObservable.add(this._onNewGamepadDisconnected),this.updateButtonVisibility(),this._circleEase=new ra,this._circleEase.setEasingMode(qa.EASINGMODE_EASEINOUT),this._teleportationEasing=this._circleEase,a.onPointerObservable.add(function(b){c._interactionsEnabled&&a.activeCamera===c.vrDeviceOrientationCamera&&"mouse"===b.event.pointerType&&(b.type===Ua.a.POINTERDOWN?c._cameraGazer._selectionPointerDown():b.type===Ua.a.POINTERUP&&c._cameraGazer._selectionPointerUp())}),this.webVROptions.floorMeshes&&this.enableTeleportation({floorMeshes:this.webVROptions.floorMeshes}))},a.prototype._onDefaultMeshLoaded=function(a){this._leftController&&this._leftController.webVRController==a&&a.mesh&&this._leftController._setLaserPointerParent(a.mesh),this._rightController&&this._rightController.webVRController==a&&a.mesh&&this._rightController._setLaserPointerParent(a.mesh);try{this.onControllerMeshLoadedObservable.notifyObservers(a)}catch(a){q.a.Warn("Error in your custom logic onControllerMeshLoaded: "+a)}},Object.defineProperty(a.prototype,"isInVRMode",{get:function(){return this.xr&&this.webVROptions.useXR&&this.xr.baseExperience.state===rd.IN_XR||this._webVRpresenting||this._fullscreenVRpresenting},enumerable:!1,configurable:!0}),a.prototype.onVrDisplayPresentChange=function(){var a=this._scene.getEngine().getVRDevice();if(a){var b=this._webVRpresenting;this._webVRpresenting=a.isPresenting,b&&!this._webVRpresenting&&this.exitVR()}else q.a.Warn("Detected VRDisplayPresentChange on an unknown VRDisplay. Did you can enterVR on the vrExperienceHelper?");this.updateButtonVisibility()},a.prototype.onVRDisplayChanged=function(a){this._webVRsupported=a.vrSupported,this._webVRready=!!a.vrDisplay,this._webVRpresenting=a.vrDisplay&&a.vrDisplay.isPresenting,this.updateButtonVisibility()},a.prototype.moveButtonToBottomRight=function(){if(this._inputElement&&!this._useCustomVRButton&&this._btnVR){var a=this._inputElement.getBoundingClientRect();this._btnVR.style.top=a.top+a.height-70+"px",this._btnVR.style.left=a.left+a.width-100+"px"}},a.prototype.displayVRButton=function(){this._useCustomVRButton||this._btnVRDisplayed||!this._btnVR||(document.body.appendChild(this._btnVR),this._btnVRDisplayed=!0)},a.prototype.updateButtonVisibility=function(){this._btnVR&&!this._useCustomVRButton&&(this._btnVR.className="babylonVRicon",this.isInVRMode?this._btnVR.className+=" vrdisplaypresenting":(this._webVRready&&(this._btnVR.className+=" vrdisplayready"),this._webVRsupported&&(this._btnVR.className+=" vrdisplaysupported"),this._webVRrequesting&&(this._btnVR.className+=" vrdisplayrequesting")))},a.prototype.enterVR=function(){var a=this;if(this.xr)this.xr.baseExperience.enterXRAsync("immersive-vr","local-floor",this.xr.renderTarget);else{if(this.onEnteringVRObservable)try{this.onEnteringVRObservable.notifyObservers(this)}catch(a){q.a.Warn("Error in your custom logic onEnteringVR: "+a)}if(this._scene.activeCamera){if(this._position=this._scene.activeCamera.position.clone(),this.vrDeviceOrientationCamera&&(this.vrDeviceOrientationCamera.rotation=g.b.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles(),this.vrDeviceOrientationCamera.angularSensibility=2e3),this.webVRCamera){var b=this.webVRCamera.deviceRotationQuaternion.toEulerAngles().y;b=g.b.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles().y-b;var c=this.webVRCamera.rotationQuaternion.toEulerAngles().y;this.webVRCamera.rotationQuaternion=g.b.FromEulerAngles(0,c+b,0)}this._existingCamera=this._scene.activeCamera,this._existingCamera.angularSensibilityX&&(this._cachedAngularSensibility.angularSensibilityX=this._existingCamera.angularSensibilityX,this._existingCamera.angularSensibilityX=Number.MAX_VALUE),this._existingCamera.angularSensibilityY&&(this._cachedAngularSensibility.angularSensibilityY=this._existingCamera.angularSensibilityY,this._existingCamera.angularSensibilityY=Number.MAX_VALUE),this._existingCamera.angularSensibility&&(this._cachedAngularSensibility.angularSensibility=this._existingCamera.angularSensibility,this._existingCamera.angularSensibility=Number.MAX_VALUE)}this._webVRrequesting||(this._webVRready?this._webVRpresenting||(this._scene.getEngine().onVRRequestPresentComplete.addOnce(function(b){a.onAfterEnteringVRObservable.notifyObservers({success:b})}),this._webVRCamera.position=this._position,this._scene.activeCamera=this._webVRCamera):this._vrDeviceOrientationCamera&&(this._vrDeviceOrientationCamera.position=this._position,this._scene.activeCamera&&(this._vrDeviceOrientationCamera.minZ=this._scene.activeCamera.minZ),this._scene.activeCamera=this._vrDeviceOrientationCamera,this._scene.getEngine().enterFullscreen(this.requestPointerLockOnFullScreen),this.updateButtonVisibility(),this._vrDeviceOrientationCamera.onViewMatrixChangedObservable.addOnce(function(){a.onAfterEnteringVRObservable.notifyObservers({success:!0})})),this._scene.activeCamera&&this._inputElement&&this._scene.activeCamera.attachControl(),this._interactionsEnabled&&this._scene.registerBeforeRender(this.beforeRender),this._displayLaserPointer&&[this._leftController,this._rightController].forEach(function(a){a&&a._activatePointer()}),this._hasEnteredVR=!0)}},a.prototype.exitVR=function(){if(this.xr)this.xr.baseExperience.exitXRAsync();else if(this._hasEnteredVR){if(this.onExitingVRObservable)try{this.onExitingVRObservable.notifyObservers(this)}catch(a){q.a.Warn("Error in your custom logic onExitingVR: "+a)}this._webVRpresenting&&this._scene.getEngine().disableVR(),this._scene.activeCamera&&(this._position=this._scene.activeCamera.position.clone()),this.vrDeviceOrientationCamera&&(this.vrDeviceOrientationCamera.angularSensibility=Number.MAX_VALUE),this._deviceOrientationCamera?(this._deviceOrientationCamera.position=this._position,this._scene.activeCamera=this._deviceOrientationCamera,this._cachedAngularSensibility.angularSensibilityX&&(this._deviceOrientationCamera.angularSensibilityX=this._cachedAngularSensibility.angularSensibilityX,this._cachedAngularSensibility.angularSensibilityX=null),this._cachedAngularSensibility.angularSensibilityY&&(this._deviceOrientationCamera.angularSensibilityY=this._cachedAngularSensibility.angularSensibilityY,this._cachedAngularSensibility.angularSensibilityY=null),this._cachedAngularSensibility.angularSensibility&&(this._deviceOrientationCamera.angularSensibility=this._cachedAngularSensibility.angularSensibility,this._cachedAngularSensibility.angularSensibility=null)):this._existingCamera&&(this._existingCamera.position=this._position,this._scene.activeCamera=this._existingCamera,this._inputElement&&this._scene.activeCamera.attachControl(),this._cachedAngularSensibility.angularSensibilityX&&(this._existingCamera.angularSensibilityX=this._cachedAngularSensibility.angularSensibilityX,this._cachedAngularSensibility.angularSensibilityX=null),this._cachedAngularSensibility.angularSensibilityY&&(this._existingCamera.angularSensibilityY=this._cachedAngularSensibility.angularSensibilityY,this._cachedAngularSensibility.angularSensibilityY=null),this._cachedAngularSensibility.angularSensibility&&(this._existingCamera.angularSensibility=this._cachedAngularSensibility.angularSensibility,this._cachedAngularSensibility.angularSensibility=null)),this.updateButtonVisibility(),this._interactionsEnabled&&(this._scene.unregisterBeforeRender(this.beforeRender),this._cameraGazer._gazeTracker.isVisible=!1,this._leftController&&(this._leftController._gazeTracker.isVisible=!1),this._rightController&&(this._rightController._gazeTracker.isVisible=!1)),this._scene.getEngine().resize(),[this._leftController,this._rightController].forEach(function(a){a&&a._deactivatePointer()}),this._hasEnteredVR=!1;var a=this._scene.getEngine();a._onVrDisplayPresentChange&&a._onVrDisplayPresentChange()}},Object.defineProperty(a.prototype,"position",{get:function(){return this._position},set:function(a){this._position=a,this._scene.activeCamera&&(this._scene.activeCamera.position=a)},enumerable:!1,configurable:!0}),a.prototype.enableInteractions=function(){var a=this;if(!this._interactionsEnabled){if(this._interactionsRequested=!0,this.xr)return void (this.xr.baseExperience.state===rd.IN_XR&&this.xr.pointerSelection.attach());this._leftController&&this._enableInteractionOnController(this._leftController),this._rightController&&this._enableInteractionOnController(this._rightController),this.raySelectionPredicate=function(b){return b.isVisible&&(b.isPickable||b.name===a._floorMeshName)},this.meshSelectionPredicate=function(){return!0},this._raySelectionPredicate=function(b){return!!(a._isTeleportationFloor(b)||-1===b.name.indexOf("gazeTracker")&&-1===b.name.indexOf("teleportationTarget")&&-1===b.name.indexOf("torusTeleportation"))&&a.raySelectionPredicate(b)},this._interactionsEnabled=!0}},Object.defineProperty(a.prototype,"_noControllerIsActive",{get:function(){return!(this._leftController&&this._leftController._activePointer||this._rightController&&this._rightController._activePointer)},enumerable:!1,configurable:!0}),a.prototype._isTeleportationFloor=function(a){for(var b=0;b-1||this._floorMeshesCollection.push(a))},a.prototype.removeFloorMesh=function(a){if(this._floorMeshesCollection){a=this._floorMeshesCollection.indexOf(a);-1!==a&&this._floorMeshesCollection.splice(a,1)}},a.prototype.enableTeleportation=function(a){var b=this;if(void 0===a&&(a={}),!this._teleportationInitialized){if(this._teleportationRequested=!0,this.enableInteractions(),this.webVROptions.useXR&&(a.floorMeshes||a.floorMeshName)){var c=a.floorMeshes||[];if(!c.length){var d=this._scene.getMeshByName(a.floorMeshName);d&&c.push(d)}if(this.xr)return c.forEach(function(a){b.xr.teleportation.addFloorMesh(a)}),void (this.xr.teleportation.attached||this.xr.teleportation.attach());if(!this.xrTestDone){var e=function(){b.xrTestDone&&(b._scene.unregisterBeforeRender(e),b.xr?b.xr.teleportation.attached||b.xr.teleportation.attach():b.enableTeleportation(a))};return void this._scene.registerBeforeRender(e)}}a.floorMeshName&&(this._floorMeshName=a.floorMeshName),a.floorMeshes&&(this._floorMeshesCollection=a.floorMeshes),a.teleportationMode&&(this._teleportationMode=a.teleportationMode),a.teleportationTime&&a.teleportationTime>0&&(this._teleportationTime=a.teleportationTime),a.teleportationSpeed&&a.teleportationSpeed>0&&(this._teleportationSpeed=a.teleportationSpeed),void 0!==a.easingFunction&&(this._teleportationEasing=a.easingFunction),null!=this._leftController&&this._enableTeleportationOnController(this._leftController),null!=this._rightController&&this._enableTeleportationOnController(this._rightController);d=new od.a;d.vignetteColor=new h.b(0,0,0,0),d.vignetteEnabled=!0,this._postProcessMove=new td("postProcessMove",1,this._webVRCamera,void 0,void 0,void 0,void 0,d),this._webVRCamera.detachPostProcess(this._postProcessMove),this._teleportationInitialized=!0,this._isDefaultTeleportationTarget&&(this._createTeleportationCircles(),this._teleportationTarget.scaling.scaleInPlace(this._webVRCamera.deviceScaleFactor))}},a.prototype._enableInteractionOnController=function(a){var b=this;a.webVRController.mesh&&(a._interactionsEnabled=!0,this.isInVRMode&&this._displayLaserPointer&&a._activatePointer(),this.webVROptions.laserToggle&&a.webVRController.onMainButtonStateChangedObservable.add(function(c){b._displayLaserPointer&&1===c.value&&(a._activePointer?a._deactivatePointer():a._activatePointer(),b.displayGaze&&(a._gazeTracker.isVisible=a._activePointer))}),a.webVRController.onTriggerStateChangedObservable.add(function(c){var d=a;b._noControllerIsActive&&(d=b._cameraGazer),d._pointerDownOnMeshAsked?c.valueb._padSensibilityUp&&d._selectionPointerDown()}))},a.prototype._checkTeleportWithRay=function(a,b){this._teleportationRequestInitiated&&!b._teleportationRequestInitiated||(b._teleportationRequestInitiated?Math.sqrt(a.y*a.y+a.x*a.x)-this._padSensibilityDown&&(b._rotationLeftAsked=!1):a.x<-this._padSensibilityUp&&b._dpadPressed&&(b._rotationLeftAsked=!0,this._rotationAllowed&&this._rotateCamera(!1)),b._rotationRightAsked?a.xthis._padSensibilityUp&&b._dpadPressed&&(b._rotationRightAsked=!0,this._rotationAllowed&&this._rotateCamera(!0)))},a.prototype._checkTeleportBackwards=function(a,b){if(!b._teleportationRequestInitiated)if(a.y>this._padSensibilityUp&&b._dpadPressed){if(!b._teleportationBackRequestInitiated){if(!this.currentVRCamera)return;a=g.b.FromRotationMatrix(this.currentVRCamera.getWorldMatrix().getRotationMatrix());var c=this.currentVRCamera.position;this.currentVRCamera.devicePosition&&this.currentVRCamera.deviceRotationQuaternion&&(a=this.currentVRCamera.deviceRotationQuaternion,c=this.currentVRCamera.devicePosition),a.toEulerAnglesToRef(this._workingVector),this._workingVector.z=0,this._workingVector.x=0,g.b.RotationYawPitchRollToRef(this._workingVector.y,this._workingVector.x,this._workingVector.z,this._workingQuaternion),this._workingQuaternion.toRotationMatrix(this._workingMatrix),g.e.TransformCoordinatesToRef(this._teleportBackwardsVector,this._workingMatrix,this._workingVector);a=new nc.a(c,this._workingVector);c=this._scene.pickWithRay(a,this._raySelectionPredicate);c&&c.pickedPoint&&c.pickedMesh&&this._isTeleportationFloor(c.pickedMesh)&&c.distance<5&&this.teleportCamera(c.pickedPoint),b._teleportationBackRequestInitiated=!0}}else b._teleportationBackRequestInitiated=!1},a.prototype._enableTeleportationOnController=function(a){var b=this;a.webVRController.mesh&&(a._interactionsEnabled||this._enableInteractionOnController(a),a._interactionsEnabled=!0,a._teleportationEnabled=!0,a.webVRController.controllerType===kc.VIVE&&(a._dpadPressed=!1,a.webVRController.onPadStateChangedObservable.add(function(b){a._dpadPressed=b.pressed,a._dpadPressed||(a._rotationLeftAsked=!1,a._rotationRightAsked=!1,a._teleportationBackRequestInitiated=!1)})),a.webVRController.onPadValuesChangedObservable.add(function(c){b.teleportationEnabled&&(b._checkTeleportBackwards(c,a),b._checkTeleportWithRay(c,a)),b._checkRotate(c,a)}))},a.prototype._createTeleportationCircles=function(){this._teleportationTarget=Gd("teleportationTarget",{width:2,height:2,subdivisions:2},this._scene),this._teleportationTarget.isPickable=!1;var a=new qd.a("DynamicTexture",512,this._scene,!0);a.hasAlpha=!0;var b=a.getContext();b.beginPath(),b.arc(256,256,200,0,2*Math.PI,!1),b.fillStyle=this._teleportationFillColor,b.fill(),b.lineWidth=10,b.strokeStyle=this._teleportationBorderColor,b.stroke(),b.closePath(),a.update();b=new pd.a("TextPlaneMaterial",this._scene);b.diffuseTexture=a,this._teleportationTarget.material=b;a=Ad("torusTeleportation",{diameter:.75,thickness:.1,tessellation:25,updatable:!1},this._scene);a.isPickable=!1,a.parent=this._teleportationTarget;b=new N("animationInnerCircle","position.y",30,N.ANIMATIONTYPE_FLOAT,N.ANIMATIONLOOPMODE_CYCLE);var c=[];c.push({frame:0,value:0}),c.push({frame:30,value:.4}),c.push({frame:60,value:0}),b.setKeys(c);c=new Ba;c.setEasingMode(qa.EASINGMODE_EASEINOUT),b.setEasingFunction(c),a.animations=[],a.animations.push(b),this._scene.beginAnimation(a,0,60,!0),this._hideTeleportationTarget()},a.prototype._displayTeleportationTarget=function(){this._teleportActive=!0,this._teleportationInitialized&&(this._teleportationTarget.isVisible=!0,this._isDefaultTeleportationTarget&&(this._teleportationTarget.getChildren()[0].isVisible=!0))},a.prototype._hideTeleportationTarget=function(){this._teleportActive=!1,this._teleportationInitialized&&(this._teleportationTarget.isVisible=!1,this._isDefaultTeleportationTarget&&(this._teleportationTarget.getChildren()[0].isVisible=!1))},a.prototype._rotateCamera=function(a){var b=this;if(this.currentVRCamera instanceof dc){a?this._rotationAngle++:this._rotationAngle--,this.currentVRCamera.animations=[];a=g.b.FromRotationMatrix(g.a.RotationY(Math.PI/4*this._rotationAngle));var c=new N("animationRotation","rotationQuaternion",90,N.ANIMATIONTYPE_QUATERNION,N.ANIMATIONLOOPMODE_CONSTANT),d=[];d.push({frame:0,value:this.currentVRCamera.rotationQuaternion}),d.push({frame:6,value:a}),c.setKeys(d),c.setEasingFunction(this._circleEase),this.currentVRCamera.animations.push(c),this._postProcessMove.animations=[];a=new N("animationPP","vignetteWeight",90,N.ANIMATIONTYPE_FLOAT,N.ANIMATIONLOOPMODE_CONSTANT);d=[];d.push({frame:0,value:0}),d.push({frame:3,value:4}),d.push({frame:6,value:0}),a.setKeys(d),a.setEasingFunction(this._circleEase),this._postProcessMove.animations.push(a);c=new N("animationPP2","vignetteStretch",90,N.ANIMATIONTYPE_FLOAT,N.ANIMATIONLOOPMODE_CONSTANT);d=[];d.push({frame:0,value:0}),d.push({frame:3,value:10}),d.push({frame:6,value:0}),c.setKeys(d),c.setEasingFunction(this._circleEase),this._postProcessMove.animations.push(c),this._postProcessMove.imageProcessingConfiguration.vignetteWeight=0,this._postProcessMove.imageProcessingConfiguration.vignetteStretch=0,this._postProcessMove.samples=4,this._webVRCamera.attachPostProcess(this._postProcessMove),this._scene.beginAnimation(this._postProcessMove,0,6,!1,1,function(){b._webVRCamera.detachPostProcess(b._postProcessMove)}),this._scene.beginAnimation(this.currentVRCamera,0,6,!1,1)}},a.prototype._moveTeleportationSelectorTo=function(a,b,c){if(a.pickedPoint){b._teleportationRequestInitiated&&(this._displayTeleportationTarget(),this._haloCenter.copyFrom(a.pickedPoint),this._teleportationTarget.position.copyFrom(a.pickedPoint));b=this._convertNormalToDirectionOfRay(a.getNormal(!0,!1),c);if(b){a=g.e.Cross(Q.a.Y,b);c=g.e.Cross(b,a);g.e.RotationFromAxisToRef(c,b,a,this._teleportationTarget.rotation)}this._teleportationTarget.position.y+=.1}},a.prototype.teleportCamera=function(b){var c=this;if(this.currentVRCamera instanceof dc){this.webVRCamera.leftCamera?(this._workingVector.copyFrom(this.webVRCamera.leftCamera.globalPosition),this._workingVector.subtractInPlace(this.webVRCamera.position),b.subtractToRef(this._workingVector,this._workingVector)):this._workingVector.copyFrom(b),this.isInVRMode?this._workingVector.y+=this.webVRCamera.deviceDistanceToRoomGround()*this._webVRCamera.deviceScaleFactor:this._workingVector.y+=this._defaultHeight,this.onBeforeCameraTeleport.notifyObservers(this._workingVector);if(this._teleportationMode==a.TELEPORTATIONMODE_CONSTANTSPEED){b=90;var d=g.e.Distance(this.currentVRCamera.position,this._workingVector);d=this._teleportationSpeed/d}else b=Math.round(90*this._teleportationTime/1e3),d=1;this.currentVRCamera.animations=[];var e=new N("animationCameraTeleportation","position",90,N.ANIMATIONTYPE_VECTOR3,N.ANIMATIONLOOPMODE_CONSTANT),f=[{frame:0,value:this.currentVRCamera.position},{frame:b,value:this._workingVector}];e.setKeys(f),e.setEasingFunction(this._teleportationEasing),this.currentVRCamera.animations.push(e),this._postProcessMove.animations=[];f=Math.round(b/2);e=new N("animationPP","vignetteWeight",90,N.ANIMATIONTYPE_FLOAT,N.ANIMATIONLOOPMODE_CONSTANT);var h=[];h.push({frame:0,value:0}),h.push({frame:f,value:8}),h.push({frame:b,value:0}),e.setKeys(h),this._postProcessMove.animations.push(e);h=new N("animationPP2","vignetteStretch",90,N.ANIMATIONTYPE_FLOAT,N.ANIMATIONLOOPMODE_CONSTANT);e=[];e.push({frame:0,value:0}),e.push({frame:f,value:10}),e.push({frame:b,value:0}),h.setKeys(e),this._postProcessMove.animations.push(h),this._postProcessMove.imageProcessingConfiguration.vignetteWeight=0,this._postProcessMove.imageProcessingConfiguration.vignetteStretch=0,this._webVRCamera.attachPostProcess(this._postProcessMove),this._scene.beginAnimation(this._postProcessMove,0,b,!1,d,function(){c._webVRCamera.detachPostProcess(c._postProcessMove)}),this._scene.beginAnimation(this.currentVRCamera,0,b,!1,d,function(){c.onAfterCameraTeleport.notifyObservers(c._workingVector)}),this._hideTeleportationTarget()}},a.prototype._convertNormalToDirectionOfRay=function(a,b){a&&Math.acos(g.e.Dot(a,b.direction))b){c=b;b=e,e=c}return e>0&&e0&&b=0))},a.prototype._canDoCollision=function(a,b,c,d){a=g.e.Distance(this._basePointWorld,a);var e=Math.max(this._radius.x,this._radius.y,this._radius.z);return!(a>this._velocityWorldLength+e+b)&&!!function(a,b,c,d){return!(a.x>c.x+d)&&!(c.x-d>b.x)&&!(a.y>c.y+d)&&!(c.y-d>b.y)&&!(a.z>c.z+d)&&!(c.z-d>b.z)}(c,d,this._basePointWorld,this._velocityWorldLength+e)},a.prototype._testTriangle=function(a,b,c,d,e,f,h){var i,j=!1;b||(b=[]),b[a]||(b[a]=new Hb.a(0,0,0,0),b[a].copyFromPoints(c,d,e));b=b[a];if(f||b.isFrontFacingTo(this._normalizedVelocity,0)){a=b.signedDistanceTo(this._basePoint);f=g.e.Dot(b.normal,this._velocity);if(0==f){if(Math.abs(a)>=1)return;j=!0,i=0}else{var k=(1-a)/f;if((i=(-1-a)/f)>k){a=k;k=i,i=a}if(i>1||k<0)return;i<0&&(i=0),i>1&&(i=1)}this._collisionPoint.copyFromFloats(0,0,0);f=!1;a=1;if(j||(this._basePoint.subtractToRef(b.normal,this._planeIntersectionPoint),this._velocity.scaleToRef(i,this._tempVector),this._planeIntersectionPoint.addInPlace(this._tempVector),this._checkPointInTriangle(this._planeIntersectionPoint,c,d,e,b.normal)&&(f=!0,a=i,this._collisionPoint.copyFrom(this._planeIntersectionPoint))),!f){k=this._velocitySquaredLength;this._basePoint.subtractToRef(c,this._tempVector);j=2*g.e.Dot(this._velocity,this._tempVector);b=this._tempVector.lengthSquared()-1;i=Pd(k,j,b,a);i.found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(c)),this._basePoint.subtractToRef(d,this._tempVector),j=2*g.e.Dot(this._velocity,this._tempVector),b=this._tempVector.lengthSquared()-1,(i=Pd(k,j,b,a)).found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(d)),this._basePoint.subtractToRef(e,this._tempVector),j=2*g.e.Dot(this._velocity,this._tempVector),b=this._tempVector.lengthSquared()-1,(i=Pd(k,j,b,a)).found&&(a=i.root,f=!0,this._collisionPoint.copyFrom(e)),d.subtractToRef(c,this._edge),c.subtractToRef(this._basePoint,this._baseToVertex);var l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex);if(k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found){var p=(m*i.root-o)/l;p>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),c.addToRef(this._edge,this._collisionPoint))}e.subtractToRef(d,this._edge),d.subtractToRef(this._basePoint,this._baseToVertex),l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex),k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found&&(p=(m*i.root-o)/l)>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),d.addToRef(this._edge,this._collisionPoint)),c.subtractToRef(e,this._edge),e.subtractToRef(this._basePoint,this._baseToVertex),l=this._edge.lengthSquared(),m=g.e.Dot(this._edge,this._velocity),o=g.e.Dot(this._edge,this._baseToVertex),k=l*-this._velocitySquaredLength+m*m,j=2*(l*g.e.Dot(this._velocity,this._baseToVertex)-m*o),b=l*(1-this._baseToVertex.lengthSquared())+o*o,(i=Pd(k,j,b,a)).found&&(p=(m*i.root-o)/l)>=0&&p<=1&&(a=i.root,f=!0,this._edge.scaleInPlace(p),e.addToRef(this._edge,this._collisionPoint))}if(f){d=a*a*this._velocitySquaredLength;(!this.collisionFound||d=d)e.copyFrom(a);else{var h=f?f.collisionMask:c.collisionMask;c._initialize(a,b,g);for(var i=f&&f.surroundingMeshes||this._scene.meshes,j=0;j-1?a:this._shaderRepository+a,this._engine._loadFile(c+"."+b.toLowerCase()+".fx",d)):d(window.atob(a.substr(7))):d(a.substr(7))},Object.defineProperty(a.prototype,"computeSourceCode",{get:function(){var a;return this._computeSourceCodeOverride?this._computeSourceCodeOverride:null!==(a=null===(a=this._pipelineContext)||void 0===a?void 0:a._getComputeShaderCode())&&void 0!==a?a:this._computeSourceCode},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"rawComputeSourceCode",{get:function(){return this._rawComputeSourceCode},enumerable:!1,configurable:!0}),a.prototype._prepareEffect=function(){var a=this,b=this.defines,c=this._pipelineContext;this._isReady=!1;try{var d=this._engine;this._pipelineContext=d.createComputePipelineContext(),this._pipelineContext._name=this._key,d._prepareComputePipelineContext(this._pipelineContext,this._computeSourceCodeOverride?this._computeSourceCodeOverride:this._computeSourceCode,this._rawComputeSourceCode,this._computeSourceCodeOverride?null:b,this._entryPoint),d._executeWhenComputeStateIsCompiled(this._pipelineContext,function(){a._compilationError="",a._isReady=!0,a.onCompiled&&a.onCompiled(a),a.onCompileObservable.notifyObservers(a),a.onCompileObservable.clear(),c&&a.getEngine()._deleteComputePipelineContext(c)}),this._pipelineContext.isAsync&&this._checkIsReady(c)}catch(a){this._processCompilationErrors(a,c)}},a.prototype._getShaderCodeAndErrorLine=function(a,b){var c=null;if(b&&a){b=b.match(/COMPUTE SHADER ERROR: 0:(d+?):/);if(b&&2===b.length){b=parseInt(b[1]);var d=a.split(" ",-1);d.length>=b&&(c="Offending line ["+b+"] in compute code: "+d[b-1])}}return[a,c]},a.prototype._processCompilationErrors=function(b,c){var d;if(void 0===c&&(c=null),this._compilationError=b.message,q.a.Error("Unable to compile compute effect:"),q.a.Error("Defines: "+this.defines),a.LogShaderCodeOnCompilationError){b=null;var e=null;(null===(d=this._pipelineContext)||void 0===d?void 0:d._getComputeShaderCode())&&(e=(d=this._getShaderCodeAndErrorLine(this._pipelineContext._getComputeShaderCode(),this._compilationError))[0],b=d[1],e&&(q.a.Error("Compute code:"),q.a.Error(e))),b&&q.a.Error(b)}q.a.Error("Error: "+this._compilationError),c&&(this._pipelineContext=c,this._isReady=!0,this.onError&&this.onError(this,this._compilationError),this.onErrorObservable.notifyObservers(this))},a.prototype.dispose=function(){this._pipelineContext&&this._pipelineContext.dispose(),this._engine._releaseComputeEffect(this)},a.RegisterShader=function(a,b){X.a.GetShadersStore(Xd.a.WGSL)[a+"ComputeShader"]=b},a._uniqueIdSeed=0,a.LogShaderCodeOnCompilationError=!0,a}();!function(a){a[a.Texture=0]="Texture",a[a.StorageTexture=1]="StorageTexture",a[a.UniformBuffer=2]="UniformBuffer",a[a.StorageBuffer=3]="StorageBuffer",a[a.TextureWithoutSampler=4]="TextureWithoutSampler",a[a.Sampler=5]="Sampler"}(Sd||(Sd={})),qb.a.prototype.createComputeEffect=function(a,b){throw new Error("createComputeEffect: This engine does not support compute shaders!")},qb.a.prototype.createComputePipelineContext=function(){throw new Error("createComputePipelineContext: This engine does not support compute shaders!")},qb.a.prototype.createComputeContext=function(){},qb.a.prototype.computeDispatch=function(a,b,c,d,e,f,g){throw new Error("computeDispatch: This engine does not support compute shaders!")},qb.a.prototype.areAllComputeEffectsReady=function(){return!0},qb.a.prototype.releaseComputeEffects=function(){},qb.a.prototype._prepareComputePipelineContext=function(a,b,c,d,e){},qb.a.prototype._rebuildComputeEffects=function(){},qb.a.prototype._executeWhenComputeStateIsCompiled=function(a,b){b()},qb.a.prototype._releaseComputeEffect=function(a){},qb.a.prototype._deleteComputePipelineContext=function(a){};var Zd=c(137),$d=c(129),ae=function(){function a(a,b,c,d){void 0===d&&(d={}),this._bindings={},this._samplers={},this._contextIsDirty=!1,this.onCompiled=null,this.onError=null,this.name=a,this._engine=b,this.uniqueId=Zd.a.UniqueId,this._engine.getCaps().supportComputeShaders?d.bindingsMapping?(this._context=b.createComputeContext(),this._shaderPath=c,this._options=Object(l.a)({bindingsMapping:{},defines:[]},d)):q.a.Error("You must provide the binding mappings as browsers don"t support reflection for wgsl shaders yet!"):q.a.Error("This engine does not support compute shaders!")}return Object.defineProperty(a.prototype,"options",{get:function(){return this._options},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderPath",{get:function(){return this._shaderPath},enumerable:!1,configurable:!0}),a.prototype.getClassName=function(){return"ComputeShader"},a.prototype.setTexture=function(a,b,c){void 0===c&&(c=!0);var d=this._bindings[a];this._bindings[a]={type:c?Sd.Texture:Sd.TextureWithoutSampler,object:b,indexInGroupEntries:null==d?void 0:d.indexInGroupEntries},this._contextIsDirty||(this._contextIsDirty=!d||d.object!==b||d.type!==this._bindings[a].type)},a.prototype.setStorageTexture=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.StorageTexture,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setUniformBuffer=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.UniformBuffer,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setStorageBuffer=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||c.object!==b),this._bindings[a]={type:Sd.StorageBuffer,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.setTextureSampler=function(a,b){var c=this._bindings[a];this._contextIsDirty||(this._contextIsDirty=!c||!b.compareSampler(c.object)),this._bindings[a]={type:Sd.Sampler,object:b,indexInGroupEntries:null==c?void 0:c.indexInGroupEntries}},a.prototype.isReady=function(){var a=this._effect;for(var b in this._bindings){var c=this._bindings[b],d=c.type;c=c.object;switch(d){case Sd.Texture:case Sd.TextureWithoutSampler:case Sd.StorageTexture:if(!c.isReady())return!1}}d=[];c=this._shaderPath;if(this._options.defines)for(b=0;bthis.capacity&&this._depth-1&&this.entries.splice(b,1)}},a.prototype.addEntries=function(a){for(var b=0;b-1&&(b._gpuFrameTimeToken=null,b._gpuFrameTime.fetchNewFrame(),b._gpuFrameTime.addCount(a,!0))}})):(this.onBeginFrameObservable.remove(this._onBeginFrameObserver),this._onBeginFrameObserver=null,this.onEndFrameObservable.remove(this._onEndFrameObserver),this._onEndFrameObserver=null))},S.a.prototype._getGlAlgorithmType=function(a){return a===cb.a.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE?this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE:this._gl.ANY_SAMPLES_PASSED},Object.defineProperty(cb.a.prototype,"isOcclusionQueryInProgress",{get:function(){return this._occlusionDataStorage.isOcclusionQueryInProgress},set:function(a){this._occlusionDataStorage.isOcclusionQueryInProgress=a},enumerable:!1,configurable:!0}),Object.defineProperty(cb.a.prototype,"_occlusionDataStorage",{get:function(){return this.__occlusionDataStorage||(this.__occlusionDataStorage=new se),this.__occlusionDataStorage},enumerable:!1,configurable:!0}),Object.defineProperty(cb.a.prototype,"isOccluded",{get:function(){return this._occlusionDataStorage.isOccluded},set:function(a){this._occlusionDataStorage.isOccluded=a},enumerable:!0,configurable:!0}),Object.defineProperty(cb.a.prototype,"occlusionQueryAlgorithmType",{get:function(){return this._occlusionDataStorage.occlusionQueryAlgorithmType},set:function(a){this._occlusionDataStorage.occlusionQueryAlgorithmType=a},enumerable:!0,configurable:!0}),Object.defineProperty(cb.a.prototype,"occlusionType",{get:function(){return this._occlusionDataStorage.occlusionType},set:function(a){this._occlusionDataStorage.occlusionType=a},enumerable:!0,configurable:!0}),Object.defineProperty(cb.a.prototype,"occlusionRetryCount",{get:function(){return this._occlusionDataStorage.occlusionRetryCount},set:function(a){this._occlusionDataStorage.occlusionRetryCount=a},enumerable:!0,configurable:!0}),cb.a.prototype._checkOcclusionQuery=function(){var a=this._occlusionDataStorage;if(a.occlusionType===cb.a.OCCLUSION_TYPE_NONE)return a.isOccluded=!1,!1;var b=this.getEngine();if(!b.getCaps().supportOcclusionQuery)return a.isOccluded=!1,!1;if(!b.isQueryResultAvailable)return a.isOccluded=!1,!1;if(this.isOcclusionQueryInProgress&&this._occlusionQuery)if(b.isQueryResultAvailable(this._occlusionQuery)){var c=b.getQueryResult(this._occlusionQuery);a.isOcclusionQueryInProgress=!1,a.occlusionInternalRetryCounter=0,a.isOccluded=!(c>0)}else{if(a.occlusionInternalRetryCounter++,!(-1!==a.occlusionRetryCount&&a.occlusionInternalRetryCounter>a.occlusionRetryCount))return a.occlusionType!==cb.a.OCCLUSION_TYPE_OPTIMISTIC&&a.isOccluded;a.isOcclusionQueryInProgress=!1,a.occlusionInternalRetryCounter=0,a.isOccluded=a.occlusionType!==cb.a.OCCLUSION_TYPE_OPTIMISTIC&&a.isOccluded}c=this.getScene();if(c.getBoundingBoxRenderer){c=c.getBoundingBoxRenderer();null===this._occlusionQuery&&(this._occlusionQuery=b.createQuery()),b.beginOcclusionQuery(a.occlusionQueryAlgorithmType,this._occlusionQuery)&&(c.renderOcclusionBoundingBox(this),b.endOcclusionQuery(a.occlusionQueryAlgorithmType),this._occlusionDataStorage.isOcclusionQueryInProgress=!0)}return a.isOccluded};var te=!0;S.a.prototype.createTransformFeedback=function(){return this._gl.createTransformFeedback()},S.a.prototype.deleteTransformFeedback=function(a){this._gl.deleteTransformFeedback(a)},S.a.prototype.bindTransformFeedback=function(a){this._gl.bindTransformFeedback(this._gl.TRANSFORM_FEEDBACK,a)},S.a.prototype.beginTransformFeedback=function(a){void 0===a&&(a=!0),this._gl.beginTransformFeedback(a?this._gl.POINTS:this._gl.TRIANGLES)},S.a.prototype.endTransformFeedback=function(){this._gl.endTransformFeedback()},S.a.prototype.setTranformFeedbackVaryings=function(a,b){this._gl.transformFeedbackVaryings(a,b,this._gl.INTERLEAVED_ATTRIBS)},S.a.prototype.bindTransformFeedbackBuffer=function(a){this._gl.bindBufferBase(this._gl.TRANSFORM_FEEDBACK_BUFFER,0,a?a.underlyingResource:null)};c(144);qb.a.prototype.createExternalTexture=function(a){return null},qb.a.prototype.setExternalTexture=function(a,b){throw new Error("setExternalTexture: This engine does not support external textures!")},qb.a.prototype.updateVideoTexture=function(a,b,c){if(a&&!a._isDisabled){var d=this._bindTextureDirectly(this._gl.TEXTURE_2D,a,!0);this._unpackFlipY(!c);try{if(void 0===this._videoTextureSupported&&(this._gl.getError(),this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,b),0!==this._gl.getError()?this._videoTextureSupported=!1:this._videoTextureSupported=!0),this._videoTextureSupported)this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,b);else{if(!a._workingCanvas){a._workingCanvas=this.createCanvas(a.width,a.height);c=a._workingCanvas.getContext("2d");if(!c)throw new Error("Unable to get 2d context");a._workingContext=c,a._workingCanvas.width=a.width,a._workingCanvas.height=a.height}a._workingContext.clearRect(0,0,a.width,a.height),a._workingContext.drawImage(b,0,0,b.videoWidth,b.videoHeight,0,0,a.width,a.height),this._gl.texImage2D(this._gl.TEXTURE_2D,0,this._gl.RGBA,this._gl.RGBA,this._gl.UNSIGNED_BYTE,a._workingCanvas)}a.generateMipMaps&&this._gl.generateMipmap(this._gl.TEXTURE_2D),d||this._bindTextureDirectly(this._gl.TEXTURE_2D,null),a.isReady=!0}catch(b){a._isDisabled=!0}}},qb.a.prototype.restoreSingleAttachment=function(){var a=this._gl;this.bindAttachments([a.BACK])},qb.a.prototype.restoreSingleAttachmentForRenderTarget=function(){var a=this._gl;this.bindAttachments([a.COLOR_ATTACHMENT0])},qb.a.prototype.buildTextureLayout=function(a){for(var b=this._gl,c=[],d=0;d1?"COLOR_ATTACHMENT"+g:"COLOR_ATTACHMENT"+g+"_WEBGL"],d.readBuffer(e[g]),d.drawBuffers(e),d.blitFramebuffer(0,0,h.width,h.height,0,0,h.width,h.height,d.COLOR_BUFFER_BIT,d.NEAREST)}for(g=0;g1?"COLOR_ATTACHMENT"+g:"COLOR_ATTACHMENT"+g+"_WEBGL"];d.drawBuffers(e)}for(g=0;g1&&(b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8||b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24||b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH32_FLOAT)&&(h=b.depthTextureFormat));var y=this._gl,z=y.createFramebuffer();this._bindUnboundFramebuffer(z);var A=a.width||a;a=a.height||a;var B=[],C=[];b=this.webGLVersion>1&&g&&b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8;var E=this._setupFramebufferDepthAttachments(!b&&f,!g&&e,A,a);w._framebuffer=z,w._depthStencilBuffer=E,w._generateDepthBuffer=!g&&e,w._generateStencilBuffer=!b&&f,w._attachments=C;for(z=0;z1?"COLOR_ATTACHMENT"+z:"COLOR_ATTACHMENT"+z+"_WEBGL"];B.push(f),C.push(F),y.activeTexture(y["TEXTURE"+z]),y.bindTexture(y.TEXTURE_2D,f._hardwareTexture.underlyingResource),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_MAG_FILTER,b.mag),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_MIN_FILTER,b.min),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_WRAP_S,y.CLAMP_TO_EDGE),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_WRAP_T,y.CLAMP_TO_EDGE),y.texImage2D(y.TEXTURE_2D,0,this._getRGBABufferInternalSizedFormat(e),A,a,0,y.RGBA,this._getWebGLTextureType(e),null),y.framebufferTexture2D(y.DRAW_FRAMEBUFFER,F,y.TEXTURE_2D,f._hardwareTexture.underlyingResource,0),d&&this._gl.generateMipmap(this._gl.TEXTURE_2D),this._bindTextureDirectly(y.TEXTURE_2D,null),f.baseWidth=A,f.baseHeight=a,f.width=A,f.height=a,f.isReady=!0,f.samples=1,f.generateMipMaps=d,f.samplingMode=E,f.type=e,this._internalTexturesCache.push(f)}if(g&&this._caps.depthTextureExtension){b=new pb.a(this,pb.b.Depth);F=r.a.TEXTURETYPE_UNSIGNED_SHORT;E=y.DEPTH_COMPONENT16;e=y.DEPTH_COMPONENT;f=y.UNSIGNED_SHORT;i=y.DEPTH_ATTACHMENT;this.webGLVersion<2?E=y.DEPTH_COMPONENT:h===r.a.TEXTUREFORMAT_DEPTH32_FLOAT?(F=r.a.TEXTURETYPE_FLOAT,f=y.FLOAT,E=y.DEPTH_COMPONENT32F):h===r.a.TEXTUREFORMAT_DEPTH24?(F=r.a.TEXTURETYPE_UNSIGNED_INT,f=y.UNSIGNED_INT,E=y.DEPTH_COMPONENT24,i=y.DEPTH_ATTACHMENT):h===r.a.TEXTUREFORMAT_DEPTH24_STENCIL8&&(F=r.a.TEXTURETYPE_UNSIGNED_INT_24_8,f=y.UNSIGNED_INT_24_8,E=y.DEPTH24_STENCIL8,e=y.DEPTH_STENCIL,i=y.DEPTH_STENCIL_ATTACHMENT),y.activeTexture(y.TEXTURE0),y.bindTexture(y.TEXTURE_2D,b._hardwareTexture.underlyingResource),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_MAG_FILTER,y.NEAREST),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_MIN_FILTER,y.NEAREST),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_WRAP_S,y.CLAMP_TO_EDGE),y.texParameteri(y.TEXTURE_2D,y.TEXTURE_WRAP_T,y.CLAMP_TO_EDGE),y.texImage2D(y.TEXTURE_2D,0,E,A,a,0,e,f,null),y.framebufferTexture2D(y.FRAMEBUFFER,i,y.TEXTURE_2D,b._hardwareTexture.underlyingResource,0),b.baseWidth=A,b.baseHeight=a,b.width=A,b.height=a,b.isReady=!0,b.samples=1,b.generateMipMaps=d,b.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE,b.format=h,b.type=F,B.push(b),this._internalTexturesCache.push(b)}return w.setTextures(B),c&&y.drawBuffers(C),this._bindUnboundFramebuffer(null),this.resetTextureCache(),w},qb.a.prototype.updateMultipleRenderTargetTextureSampleCount=function(a,b,c){if(void 0===c&&(c=!0),this.webGLVersion<2||!a||!a.texture)return 1;if(a.samples===b)return b;var d=a._attachments.length;if(0===d)return 1;var e=this._gl;b=Math.min(b,this.getCaps().maxMSAASamples),a._depthStencilBuffer&&(e.deleteRenderbuffer(a._depthStencilBuffer),a._depthStencilBuffer=null),a._MSAAFramebuffer&&(e.deleteFramebuffer(a._MSAAFramebuffer),a._MSAAFramebuffer=null);for(var f=0;f1&&e.renderbufferStorageMultisample){var g=e.createFramebuffer();if(!g)throw new Error("Unable to create multi sampled framebuffer");a._MSAAFramebuffer=g,this._bindUnboundFramebuffer(g);g=[];for(f=0;f1?"COLOR_ATTACHMENT"+f:"COLOR_ATTACHMENT"+f+"_WEBGL"],k=this._createRenderBuffer(h.width,h.height,b,-1,this._getRGBAMultiSampleBufferFormat(h.type),j);if(!k)throw new Error("Unable to create multi sampled framebuffer");i._MSAARenderBuffer=k,h.samples=b,g.push(j)}c&&e.drawBuffers(g)}else this._bindUnboundFramebuffer(a._framebuffer);return a._depthStencilBuffer=this._setupFramebufferDepthAttachments(a._generateStencilBuffer,a._generateDepthBuffer,a.texture.width,a.texture.height,b),this._bindUnboundFramebuffer(null),b};var ue=c(54),ve=c(115);qb.a.prototype._createDepthStencilCubeTexture=function(a,b,c){var d=new pb.a(this,pb.b.DepthStencil);if(d.isCube=!0,1===this.webGLVersion)return q.a.Error("Depth cube texture is not supported by WebGL 1."),d;b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1},b);var e=this._gl;this._bindTextureDirectly(e.TEXTURE_CUBE_MAP,d,!0),this._setupDepthStencilTexture(d,a,b.generateStencil,b.bilinearFiltering,b.comparisonFunction),c._depthStencilTexture=d,c._depthStencilTextureWithStencil=b.generateStencil;for(c=0;c<6;c++)b.generateStencil?e.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+c,0,e.DEPTH24_STENCIL8,a,a,0,e.DEPTH_STENCIL,e.UNSIGNED_INT_24_8,null):e.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+c,0,e.DEPTH_COMPONENT24,a,a,0,e.DEPTH_COMPONENT,e.UNSIGNED_INT,null);return this._bindTextureDirectly(e.TEXTURE_CUBE_MAP,null),this._internalTexturesCache.push(d),d},qb.a.prototype._partialLoadFile=function(a,b,c,d,e){void 0===e&&(e=null),this._loadFile(a,function(a){c[b]=a,c._internalCount++,6===c._internalCount&&d(c)},void 0,void 0,!0,function(a,b){e&&a&&e(a.status+" "+a.statusText,b)})},qb.a.prototype._cascadeLoadFiles=function(a,b,c,d){void 0===d&&(d=null);a=[];a._internalCount=0;for(var e=0;e<6;e++)this._partialLoadFile(c[e],e,a,b,d)},qb.a.prototype._cascadeLoadImgs=function(a,b,c,d,e,f){void 0===e&&(e=null);var g=[];g._internalCount=0;for(var h=0;h<6;h++)this._partialLoadImg(d[h],h,g,a,b,c,e,f)},qb.a.prototype._partialLoadImg=function(a,b,c,d,e,f,g,h){void 0===g&&(g=null);var i=Object(ve.a)();Object(ue.i)(a,function(a){c[b]=a,c._internalCount++,d&&d._removePendingData(i),6===c._internalCount&&f&&f(e,c)},function(a,b){d&&d._removePendingData(i),g&&g(a,b)},d?d.offlineProvider:null,h),d&&d._addPendingData(i)},qb.a.prototype._setCubeMapTextureParams=function(a,b){var c=this._gl;c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_MAG_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_MIN_FILTER,b?c.LINEAR_MIPMAP_LINEAR:c.LINEAR),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_CUBE_MAP,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),a.samplingMode=b?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:r.a.TEXTURE_LINEAR_LINEAR,this._bindTextureDirectly(c.TEXTURE_CUBE_MAP,null)},qb.a.prototype.createCubeTextureBase=function(a,b,c,d,e,f,g,h,i,j,k,l,u,A,B){var C=this;void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=!1),void 0===j&&(j=0),void 0===k&&(k=0),void 0===l&&(l=null),void 0===u&&(u=null),void 0===A&&(A=null),void 0===B&&(B=!1);var D=l||new pb.a(this,pb.b.Cube);D.isCube=!0,D.url=a,D.generateMipMaps=!d,D._lodGenerationScale=j,D._lodGenerationOffset=k,D._useSRGBBuffer=!!B&&this._caps.supportSRGBBuffers&&(this.webGLVersion>1||this.isWebGPU||!!d),this._doNotHandleContextLost||(D._extension=h,D._files=c);var E=a;this._transformTextureUrl&&!l&&(a=this._transformTextureUrl(a));for(var l=a.lastIndexOf("."),l=h||(l>-1?a.substring(l).toLowerCase():""),F=null,aa=0,G=qb.a._TextureLoaders;aa-1?a.substring(c,a.length):"";return(b>-1?a.substring(0,b):a)+this._textureFormatInUse+c}qb.a.prototype.createStorageBuffer=function(a,b){throw new Error("createStorageBuffer: Unsupported method in this engine!")},qb.a.prototype.updateStorageBuffer=function(a,b,c,d){},qb.a.prototype.readFromStorageBuffer=function(a,b,c,d){throw new Error("readFromStorageBuffer: Unsupported method in this engine!")},qb.a.prototype.setStorageBuffer=function(a,b){throw new Error("setStorageBuffer: Unsupported method in this engine!")},Object.defineProperty(S.a.prototype,"texturesSupported",{get:function(){var a=new Array;return this._caps.astc&&a.push("-astc.ktx"),this._caps.s3tc&&a.push("-dxt.ktx"),this._caps.pvrtc&&a.push("-pvrtc.ktx"),this._caps.etc2&&a.push("-etc2.ktx"),this._caps.etc1&&a.push("-etc1.ktx"),a},enumerable:!0,configurable:!0}),Object.defineProperty(S.a.prototype,"textureFormatInUse",{get:function(){return this._textureFormatInUse||null},enumerable:!0,configurable:!0}),S.a.prototype.setCompressedTextureExclusions=function(a){this._excludedCompressedTextures=a},S.a.prototype.setTextureFormatToUse=function(a){for(var b=this.texturesSupported,c=0,d=b.length;cthis._length&&this._flush()},a.prototype._flush=function(){this._nativeDataStream.writeBuffer(this._uint32s.buffer,this._position),this._position=0},a.DEFAULT_BUFFER_SIZE=65536,a}(),Ae=function(){function a(a,b){void 0===a&&(a=g.e.Zero()),void 0===b&&(b=g.e.Up()),this.position=a,this.normal=b}return a.prototype.clone=function(){return new a(this.position.clone(),this.normal.clone())},a}(),Be=function(){function a(a,b,c){void 0===a&&(a=g.e.Zero()),void 0===b&&(b=g.e.Up()),void 0===c&&(c=g.d.Zero()),this.position=a,this.normal=b,this.uv=c}return a.prototype.clone=function(){return new a(this.position.clone(),this.normal.clone(),this.uv.clone())},a}(),Ce=[Math.sqrt(1/(4*Math.PI)),-Math.sqrt(3/(4*Math.PI)),Math.sqrt(3/(4*Math.PI)),-Math.sqrt(3/(4*Math.PI)),Math.sqrt(15/(4*Math.PI)),-Math.sqrt(15/(4*Math.PI)),Math.sqrt(5/(16*Math.PI)),-Math.sqrt(15/(4*Math.PI)),Math.sqrt(15/(16*Math.PI))],De=[function(a){return 1},function(a){return a.y},function(a){return a.z},function(a){return a.x},function(a){return a.x*a.y},function(a){return a.y*a.z},function(a){return 3*a.z*a.z-1},function(a){return a.x*a.z},function(a){return a.x*a.x-a.y*a.y}],Ee=function(a,b){return Ce[a]*De[a](b)},Fe=[Math.PI,2*Math.PI/3,2*Math.PI/3,2*Math.PI/3,Math.PI/4,Math.PI/4,Math.PI/4,Math.PI/4,Math.PI/4],Ge=function(){function a(){this.preScaled=!1,this.l00=g.e.Zero(),this.l1_1=g.e.Zero(),this.l10=g.e.Zero(),this.l11=g.e.Zero(),this.l2_2=g.e.Zero(),this.l2_1=g.e.Zero(),this.l20=g.e.Zero(),this.l21=g.e.Zero(),this.l22=g.e.Zero()}return a.prototype.addLight=function(a,b,c){g.c.Vector3[0].set(b.r,b.g,b.b);b=g.c.Vector3[0];var d=g.c.Vector3[1];b.scaleToRef(c,d),d.scaleToRef(Ee(0,a),g.c.Vector3[2]),this.l00.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(1,a),g.c.Vector3[2]),this.l1_1.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(2,a),g.c.Vector3[2]),this.l10.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(3,a),g.c.Vector3[2]),this.l11.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(4,a),g.c.Vector3[2]),this.l2_2.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(5,a),g.c.Vector3[2]),this.l2_1.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(6,a),g.c.Vector3[2]),this.l20.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(7,a),g.c.Vector3[2]),this.l21.addInPlace(g.c.Vector3[2]),d.scaleToRef(Ee(8,a),g.c.Vector3[2]),this.l22.addInPlace(g.c.Vector3[2])},a.prototype.scaleInPlace=function(a){this.l00.scaleInPlace(a),this.l1_1.scaleInPlace(a),this.l10.scaleInPlace(a),this.l11.scaleInPlace(a),this.l2_2.scaleInPlace(a),this.l2_1.scaleInPlace(a),this.l20.scaleInPlace(a),this.l21.scaleInPlace(a),this.l22.scaleInPlace(a)},a.prototype.convertIncidentRadianceToIrradiance=function(){this.l00.scaleInPlace(Fe[0]),this.l1_1.scaleInPlace(Fe[1]),this.l10.scaleInPlace(Fe[2]),this.l11.scaleInPlace(Fe[3]),this.l2_2.scaleInPlace(Fe[4]),this.l2_1.scaleInPlace(Fe[5]),this.l20.scaleInPlace(Fe[6]),this.l21.scaleInPlace(Fe[7]),this.l22.scaleInPlace(Fe[8])},a.prototype.convertIrradianceToLambertianRadiance=function(){this.scaleInPlace(1/Math.PI)},a.prototype.preScaleForRendering=function(){this.preScaled=!0,this.l00.scaleInPlace(Ce[0]),this.l1_1.scaleInPlace(Ce[1]),this.l10.scaleInPlace(Ce[2]),this.l11.scaleInPlace(Ce[3]),this.l2_2.scaleInPlace(Ce[4]),this.l2_1.scaleInPlace(Ce[5]),this.l20.scaleInPlace(Ce[6]),this.l21.scaleInPlace(Ce[7]),this.l22.scaleInPlace(Ce[8])},a.prototype.updateFromArray=function(a){return g.e.FromArrayToRef(a[0],0,this.l00),g.e.FromArrayToRef(a[1],0,this.l1_1),g.e.FromArrayToRef(a[2],0,this.l10),g.e.FromArrayToRef(a[3],0,this.l11),g.e.FromArrayToRef(a[4],0,this.l2_2),g.e.FromArrayToRef(a[5],0,this.l2_1),g.e.FromArrayToRef(a[6],0,this.l20),g.e.FromArrayToRef(a[7],0,this.l21),g.e.FromArrayToRef(a[8],0,this.l22),this},a.prototype.updateFromFloatsArray=function(a){return g.e.FromFloatsToRef(a[0],a[1],a[2],this.l00),g.e.FromFloatsToRef(a[3],a[4],a[5],this.l1_1),g.e.FromFloatsToRef(a[6],a[7],a[8],this.l10),g.e.FromFloatsToRef(a[9],a[10],a[11],this.l11),g.e.FromFloatsToRef(a[12],a[13],a[14],this.l2_2),g.e.FromFloatsToRef(a[15],a[16],a[17],this.l2_1),g.e.FromFloatsToRef(a[18],a[19],a[20],this.l20),g.e.FromFloatsToRef(a[21],a[22],a[23],this.l21),g.e.FromFloatsToRef(a[24],a[25],a[26],this.l22),this},a.FromArray=function(b){return(new a).updateFromArray(b)},a.FromPolynomial=function(b){var c=new a;return c.l00=b.xx.scale(.376127).add(b.yy.scale(.376127)).add(b.zz.scale(.376126)),c.l1_1=b.y.scale(.977204),c.l10=b.z.scale(.977204),c.l11=b.x.scale(.977204),c.l2_2=b.xy.scale(1.16538),c.l2_1=b.yz.scale(1.16538),c.l20=b.zz.scale(1.34567).subtract(b.xx.scale(.672834)).subtract(b.yy.scale(.672834)),c.l21=b.zx.scale(1.16538),c.l22=b.xx.scale(1.16538).subtract(b.yy.scale(1.16538)),c.l1_1.scaleInPlace(-1),c.l11.scaleInPlace(-1),c.l2_1.scaleInPlace(-1),c.l21.scaleInPlace(-1),c.scaleInPlace(Math.PI),c},a}(),He=function(){function a(){this.x=g.e.Zero(),this.y=g.e.Zero(),this.z=g.e.Zero(),this.xx=g.e.Zero(),this.yy=g.e.Zero(),this.zz=g.e.Zero(),this.xy=g.e.Zero(),this.yz=g.e.Zero(),this.zx=g.e.Zero()}return Object.defineProperty(a.prototype,"preScaledHarmonics",{get:function(){return this._harmonics||(this._harmonics=Ge.FromPolynomial(this)),this._harmonics.preScaled||this._harmonics.preScaleForRendering(),this._harmonics},enumerable:!1,configurable:!0}),a.prototype.addAmbient=function(a){g.c.Vector3[0].copyFromFloats(a.r,a.g,a.b);a=g.c.Vector3[0];this.xx.addInPlace(a),this.yy.addInPlace(a),this.zz.addInPlace(a)},a.prototype.scaleInPlace=function(a){this.x.scaleInPlace(a),this.y.scaleInPlace(a),this.z.scaleInPlace(a),this.xx.scaleInPlace(a),this.yy.scaleInPlace(a),this.zz.scaleInPlace(a),this.yz.scaleInPlace(a),this.zx.scaleInPlace(a),this.xy.scaleInPlace(a)},a.prototype.updateFromHarmonics=function(a){return this._harmonics=a,this.x.copyFrom(a.l11),this.x.scaleInPlace(1.02333).scaleInPlace(-1),this.y.copyFrom(a.l1_1),this.y.scaleInPlace(1.02333).scaleInPlace(-1),this.z.copyFrom(a.l10),this.z.scaleInPlace(1.02333),this.xx.copyFrom(a.l00),g.c.Vector3[0].copyFrom(a.l20).scaleInPlace(.247708),g.c.Vector3[1].copyFrom(a.l22).scaleInPlace(.429043),this.xx.scaleInPlace(.886277).subtractInPlace(g.c.Vector3[0]).addInPlace(g.c.Vector3[1]),this.yy.copyFrom(a.l00),this.yy.scaleInPlace(.886277).subtractInPlace(g.c.Vector3[0]).subtractInPlace(g.c.Vector3[1]),this.zz.copyFrom(a.l00),g.c.Vector3[0].copyFrom(a.l20).scaleInPlace(.495417),this.zz.scaleInPlace(.886277).addInPlace(g.c.Vector3[0]),this.yz.copyFrom(a.l2_1),this.yz.scaleInPlace(.858086).scaleInPlace(-1),this.zx.copyFrom(a.l21),this.zx.scaleInPlace(.858086).scaleInPlace(-1),this.xy.copyFrom(a.l2_2),this.xy.scaleInPlace(.858086),this.scaleInPlace(1/Math.PI),this},a.FromHarmonics=function(b){return(new a).updateFromHarmonics(b)},a.FromArray=function(b){var c=new a;return g.e.FromArrayToRef(b[0],0,c.x),g.e.FromArrayToRef(b[1],0,c.y),g.e.FromArrayToRef(b[2],0,c.z),g.e.FromArrayToRef(b[3],0,c.xx),g.e.FromArrayToRef(b[4],0,c.yy),g.e.FromArrayToRef(b[5],0,c.zz),g.e.FromArrayToRef(b[6],0,c.yz),g.e.FromArrayToRef(b[7],0,c.zx),g.e.FromArrayToRef(b[8],0,c.xy),c},a}(),Ie=c(53);V=" varying vec2 vUV; uniform sampler2D textureSampler; #include void main(void) { gl_FragColor=vec4(fromRGBD(texture2D(textureSampler,vUV)),1.0); }";X.a.ShadersStore.rgbdDecodePixelShader=V;var Je,Ke;function Le(a,b,c,d){void 0===d&&(d=!0);var e=a.getScene(),f=e.getEngine(),g=new cd("resized"+a.name,{width:b,height:c},e,!a.noMipmap,!0,a._texture.type,!1,a.samplingMode,!1);g.wrapU=a.wrapU,g.wrapV=a.wrapV,g.uOffset=a.uOffset,g.vOffset=a.vOffset,g.uScale=a.uScale,g.vScale=a.vScale,g.uAng=a.uAng,g.vAng=a.vAng,g.wAng=a.wAng,g.coordinatesIndex=a.coordinatesIndex,g.level=a.level,g.anisotropicFilteringLevel=a.anisotropicFilteringLevel,g._texture.isReady=!1,a.wrapU=U.a.CLAMP_ADDRESSMODE,a.wrapV=U.a.CLAMP_ADDRESSMODE;var h=new Gc("pass",1,null,d?U.a.BILINEAR_SAMPLINGMODE:U.a.NEAREST_SAMPLINGMODE,f,!1,r.a.TEXTURETYPE_UNSIGNED_INT);return h.getEffect().executeWhenCompiled(function(){h.onApply=function(b){b.setTexture("textureSampler",a)};var b=g.renderTarget;b&&(e.postProcessManager.directRender([h],b),f.unBindFramebuffer(b),g.disposeFramebufferObjects(),h.dispose(),g.getInternalTexture().isReady=!0)}),g}function Me(a,b,c,d,e,f){var g=b.getEngine();return b.isReady=!1,e=null!=e?e:b.samplingMode,d=null!=d?d:b.type,f=null!=f?f:b.format,-1===d&&(d=r.a.TEXTURETYPE_UNSIGNED_BYTE),new Promise(function(h){var i=new Fc("postprocess",a,null,null,1,null,e,g,!1,void 0,d,void 0,null,!1,f),j=g.createRenderTargetTexture({width:b.width,height:b.height},{generateDepthBuffer:!1,generateMipMaps:!1,generateStencilBuffer:!1,samplingMode:e,type:d,format:f});i.getEffect().executeWhenCompiled(function(){i.onApply=function(a){a._bindTexture("textureSampler",b),a.setFloat2("scale",1,1)},c.postProcessManager.directRender([i],j,!0),g.restoreDefaultFramebuffer(),g._releaseTexture(b),i&&i.dispose(),j._swapAndDie(b),b.type=d,b.format=r.a.TEXTUREFORMAT_RGBA,b.isReady=!0,h(b)})})}function Ne(a){Je||(Je=new Float32Array(1),Ke=new Int32Array(Je.buffer)),Je[0]=a;a=Ke[0];var b=a>>16&32768,c=a>>12&2047,d=a>>23&255;return d<103?b:d>142?(b|=31744,b|=(255==d?0:1)&&8388607&a):d<113?b|=((c|=2048)>>114-d)+(c>>113-d&1):(b|=d-112<<10|c>>1,b+=1&c)}function Oe(a){var b=(32768&a)>>15,c=(31744&a)>>10;a=1023&a;return 0===c?(b?-1:1)*Math.pow(2,-14)*(a/Math.pow(2,10)):31==c?a?NaN:1/0*(b?-1:1):(b?-1:1)*Math.pow(2,c-15)*(1+a/Math.pow(2,10))}var Pe={CreateResizedCopy:Le,ApplyPostProcess:Me,ToHalfFloat:Ne,FromHalfFloat:Oe},Qe=function(){function a(){}return a.ExpandRGBDTexture=function(a){var b=a._texture;if(b&&a.isRGBD){var c=b.getEngine(),d=c.getCaps(),e=b.isReady,f=!1;d.textureHalfFloatRender&&d.textureHalfFloatLinearFiltering?(f=!0,b.type=r.a.TEXTURETYPE_HALF_FLOAT):d.textureFloatRender&&d.textureFloatLinearFiltering&&(f=!0,b.type=r.a.TEXTURETYPE_FLOAT),f&&(b.isReady=!1,b._isRGBD=!1,b.invertY=!1);d=function(){if(f){var d=new Fc("rgbdDecode","rgbdDecode",null,null,1,null,r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,c,!1,void 0,b.type,void 0,null,!1),e=c.createRenderTargetTexture(b.width,{generateDepthBuffer:!1,generateMipMaps:!1,generateStencilBuffer:!1,samplingMode:b.samplingMode,type:b.type,format:r.a.TEXTUREFORMAT_RGBA});d.getEffect().executeWhenCompiled(function(){d.onApply=function(a){a._bindTexture("textureSampler",b),a.setFloat2("scale",1,1)},a.getScene().postProcessManager.directRender([d],e,!0),c.restoreDefaultFramebuffer(),c._releaseTexture(b),d&&d.dispose(),e._swapAndDie(b),b.isReady=!0})}};e?d():a.onLoadObservable.addOnce(d)}},a.EncodeTextureToRGBD=function(a,b,c){return void 0===c&&(c=r.a.TEXTURETYPE_UNSIGNED_BYTE),Me("rgbdEncode",a,b,c,r.a.TEXTURE_NEAREST_SAMPLINGMODE,r.a.TEXTUREFORMAT_RGBA)},a}(),Re=function(a,b,c,d){this.name=a,this.worldAxisForNormal=b,this.worldAxisForFileX=c,this.worldAxisForFileY=d},Se=function(){function a(){}return a.ConvertCubeMapTextureToSphericalPolynomial=function(a){var b,c=this;if(!a.isCube)return null;null===(b=a.getScene())||void 0===b||b.getEngine().flushFramebuffer();var d,e,f=a.getSize().width,g=a.readPixels(0,void 0,void 0,!1),h=a.readPixels(1,void 0,void 0,!1);a.isRenderTarget?(d=a.readPixels(3,void 0,void 0,!1),e=a.readPixels(2,void 0,void 0,!1)):(d=a.readPixels(2,void 0,void 0,!1),e=a.readPixels(3,void 0,void 0,!1));var i=a.readPixels(4,void 0,void 0,!1),j=a.readPixels(5,void 0,void 0,!1),k=a.gammaSpace,l=r.a.TEXTUREFORMAT_RGBA,n=r.a.TEXTURETYPE_UNSIGNED_INT;return a.textureType!=r.a.TEXTURETYPE_FLOAT&&a.textureType!=r.a.TEXTURETYPE_HALF_FLOAT||(n=r.a.TEXTURETYPE_FLOAT),new Promise(function(a,b){Promise.all([h,g,d,e,i,j]).then(function(b){var d=b[0],e=b[1],g=b[2],h=b[3],i=b[4];b=b[5];e={size:f,right:e,left:d,up:g,down:h,front:i,back:b,format:l,type:n,gammaSpace:k};a(c.ConvertCubeMapToSphericalPolynomial(e))})})},a.ConvertCubeMapToSphericalPolynomial=function(a){for(var b=new Ge,c=0,d=2/a.size,e=d,f=.5*d-1,g=0;g<6;g++)for(var i=this.FileFaces[g],j=a[i.name],k=f,l=a.format===r.a.TEXTUREFORMAT_RGBA?4:3,v=0;v void main(void) { gl_FragColor=toRGBD(texture2D(textureSampler,vUV).rgb); }";X.a.ShadersStore.rgbdEncodePixelShader=a;var Te=[134,22,135,150,246,214,150,54];function Ue(a){for(var a=new DataView(a.buffer,a.byteOffset,a.byteLength),b=0,c=0;c2)throw new Error("Unsupported babylon environment map version ""+a.version+"". Latest supported version is "2".");return 2===a.version?a:a=Object(l.a)(Object(l.a)({},a),{version:2,imageType:"image/png"})}function We(a,b){var c;return void 0===b&&(b={}),Object(l.b)(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k,J,K,ba,L,M,N,ca,da,ea,fa,ga,ha,ia,P,ja,Q,ka,la,ma,na,oa,pa;return Object(l.e)(this,function(l){switch(l.label){case 0:if(!(d=a.getInternalTexture()))return[2,Promise.reject("The cube texture is invalid.")];if(e=null!==(c=b.imageType)&&void 0!==c?c:"image/png",f=d.getEngine(),a.textureType!==r.a.TEXTURETYPE_HALF_FLOAT&&a.textureType!==r.a.TEXTURETYPE_FLOAT&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_BYTE&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_INT&&a.textureType!==r.a.TEXTURETYPE_UNSIGNED_INTEGER&&-1!==a.textureType)return[2,Promise.reject("The cube texture should allow HDR (Full Float or Half Float).")];if(g=r.a.TEXTURETYPE_FLOAT,!f.getCaps().textureFloatRender&&(g=r.a.TEXTURETYPE_HALF_FLOAT,!f.getCaps().textureHalfFloatRender))return[2,Promise.reject("Env texture can only be created when the browser supports half float or full float rendering.")];h=d.width,i=new O.a(f),j={},f.flushFramebuffer(),k=H.a.ILog2(d.width),na=0,l.label=1;case 1:if(!(na<=k))return[3,9];J=Math.pow(2,k-na),oa=0,l.label=2;case 2:return oa<6?[4,a.readPixels(oa,na,void 0,!1)]:[3,8];case 3:if((K=l.sent())&&K.byteLength===K.length){for(ba=new Float32Array(4*K.byteLength),L=0;L=48&&a<=57||a>=65&&a<=90||a>=97&&a<=122||95==a}function hf(a){for(var b=0,c="",d=!1,e=[];b=0&&a.charAt(b)!==c;)b--;return b}var kf=function(){function a(a,b){void 0===b&&(b=20),this.debug=!1,this._sourceCode=a,this._numMaxIterations=b,this._functionDescr=[],this.inlineToken="#define inline"}return Object.defineProperty(a.prototype,"code",{get:function(){return this._sourceCode},enumerable:!1,configurable:!0}),a.prototype.processCode=function(){this.debug&&!1,this._collectFunctions(),this._processInlining(this._numMaxIterations),this.debug&&!1},a.prototype._collectFunctions=function(){for(var b=0;b=0&&i.push(k.substring(l+1))}"void"!==f&&i.push("return"),this._functionDescr.push({name:e,type:f,parameters:i,body:g,callIndex:0}),b=h+1;k=c>0?this._sourceCode.substring(0,c):"";l=h+1=0&&this._replaceFunctionCallsByCode(););return this.debug&&!1,a>=0},a.prototype._replaceFunctionCallsByCode=function(){for(var a=!1,b=0,c=this._functionDescr;b0?this._sourceCode.substring(0,j):"";m=l+1=0){var g=b[c[e]];if(g){var h=g.getBuffer();h&&this._engine.recordVertexBuffer(a,h.nativeVertexBuffer,f,g.byteOffset,g.byteStride,g.getSize(),this._getNativeAttribType(g.type),g.normalized)}}}},b.prototype.bindBuffers=function(a,b,c){this._boundBuffersVertexArray&&this._deleteVertexArray(this._boundBuffersVertexArray),this._boundBuffersVertexArray=this._engine.createVertexArray(),this._recordVertexArrayObject(this._boundBuffersVertexArray,a,b,c),this.bindVertexArrayObject(this._boundBuffersVertexArray)},b.prototype.recordVertexArrayObject=function(a,b,c){var d=this._engine.createVertexArray();return this._recordVertexArrayObject(d,a,b,c),d},b.prototype._deleteVertexArray=function(a){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXARRAY),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.bindVertexArrayObject=function(a){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDVERTEXARRAY),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.releaseVertexArrayObject=function(a){this._deleteVertexArray(a)},b.prototype.getAttributes=function(a,b){a=a;return this._engine.getAttributes(a.nativeProgram,b)},b.prototype.drawElementsType=function(a,b,c,d){this._drawCalls.addCount(1,!1),this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXED),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.drawArraysType=function(a,b,c,d){this._drawCalls.addCount(1,!1),this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAW),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.finishEncodingCommand()},b.prototype.createPipelineContext=function(){return new mf(this)},b.prototype.createMaterialContext=function(){},b.prototype.createDrawContext=function(){},b.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i){e=a;e.nativeProgram=d?this.createRawShaderProgram(a,b,c,void 0,i):this.createShaderProgram(a,b,c,h,void 0,i)},b.prototype._isRenderingStateCompiled=function(a){return!0},b.prototype._executeWhenRenderingStateIsCompiled=function(a,b){b()},b.prototype.createRawShaderProgram=function(a,b,c,d,e){throw void 0===e&&null,new Error("Not Supported")},b.prototype.createShaderProgram=function(a,b,c,d,e,f){void 0===f&&null,this.onBeforeShaderCompilationObservable.notifyObservers(this);a=new kf(b);a.processCode(),b=a.code;e=new kf(c);e.processCode(),c=e.code,b=qb.a._ConcatenateShader(b,d),c=qb.a._ConcatenateShader(c,d);f=this._engine.createProgram(b,c);return this.onAfterShaderCompilationObservable.notifyObservers(this),f},b.prototype.inlineShaderCode=function(a){a=new kf(a);return a.debug=!1,a.processCode(),a.code},b.prototype._setProgram=function(a){this._currentProgram!==a&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETPROGRAM),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.finishEncodingCommand(),this._currentProgram=a)},b.prototype._deletePipelineContext=function(a){a=a;a&&a.nativeProgram&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEPROGRAM),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeProgram),this._commandBufferEncoder.finishEncodingCommand())},b.prototype.getUniforms=function(a,b){a=a;return this._engine.getUniforms(a.nativeProgram,b)},b.prototype.bindUniformBlock=function(a,b,c){throw new Error("Not Implemented")},b.prototype.bindSamplers=function(a){var b=a.getPipelineContext();this._setProgram(b.nativeProgram);for(var b=a.getSamplers(),c=0;c-1?a.substring(J).toLowerCase():""),K=null,ba=0,L=S.a._TextureLoaders;ba-1?a.substring(b).toLowerCase():""))){if(c&&6===c.length)throw new Error("Multi-file loading not allowed on env files.");this._loadFile(a,function(a){return function(a){var b=Ue(a);u.width=b.width,u.height=b.width,bf(u,b);var c=b.specular;if(!c)throw new Error("Nothing else parsed so far");u._lodGenerationScale=c.lodGenerationScale;c=Ye(a,b);u.format=r.a.TEXTUREFORMAT_RGBA,u.type=r.a.TEXTURETYPE_UNSIGNED_INT,u.generateMipMaps=!0,u.getEngine().updateTextureSamplingMode(U.a.TRILINEAR_SAMPLINGMODE,u),u._isRGBD=!0,u.invertY=!0,t._engine.loadCubeTextureWithMips(u._hardwareTexture.underlyingResource,c,!1,s,function(){u.isReady=!0,e&&e()},function(){throw new Error("Could not load a native cube texture.")})}(new Uint8Array(a))},void 0,void 0,!0,function(a,b){f&&a&&f(a.status+" "+a.statusText,b)})}else{if(!c||6!==c.length)throw new Error("Cannot load cubemap because 6 files were not defined");g=[c[0],c[3],c[1],c[4],c[2],c[5]];Promise.all(g.map(function(a){return T.b.LoadFileAsync(a).then(function(a){return new Uint8Array(a)})})).then(function(a){return new Promise(function(b,c){t._engine.loadCubeTexture(u._hardwareTexture.underlyingResource,a,!d,!0,s,b,c)})}).then(function(){u.isReady=!0,e&&e()},function(a){f&&f("Failed to load cubemap: "+a.message,a)})}return this._internalTexturesCache.push(u),u},b.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new nf(a,b,c,this);return this._renderTargetWrapperCache.push(a),a},b.prototype.createRenderTargetTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!1,a),d=new Bc.b;void 0!==b&&"object"==typeof b?(d.generateMipMaps=b.generateMipMaps,d.generateDepthBuffer=void 0===b.generateDepthBuffer||b.generateDepthBuffer,d.generateStencilBuffer=d.generateDepthBuffer&&b.generateStencilBuffer,d.type=void 0===b.type?r.a.TEXTURETYPE_UNSIGNED_INT:b.type,d.samplingMode=void 0===b.samplingMode?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:b.samplingMode,d.format=void 0===b.format?r.a.TEXTUREFORMAT_RGBA:b.format):(d.generateMipMaps=b,d.generateDepthBuffer=!0,d.generateStencilBuffer=!1,d.type=r.a.TEXTURETYPE_UNSIGNED_INT,d.samplingMode=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,d.format=r.a.TEXTUREFORMAT_RGBA),(d.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(d.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(d.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);b=new pb.a(this,pb.b.RenderTarget);var e=a.width||a;a=a.height||a;d.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloat||(d.type=r.a.TEXTURETYPE_UNSIGNED_INT,q.a.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type"));var f=this._engine.createFrameBuffer(b._hardwareTexture.underlyingResource,e,a,this._getNativeTextureFormat(d.format,d.type),!!d.generateStencilBuffer,d.generateDepthBuffer,!!d.generateMipMaps);return c._framebuffer=f,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=!!d.generateStencilBuffer,b.baseWidth=e,b.baseHeight=a,b.width=e,b.height=a,b.isReady=!0,b.samples=1,b.generateMipMaps=!!d.generateMipMaps,b.samplingMode=d.samplingMode,b.type=d.type,b.format=d.format,this._internalTexturesCache.push(b),c.setTextures(b),c},b.prototype.updateTextureSamplingMode=function(a,b){if(b._hardwareTexture){var c=this._getNativeSamplingMode(a);this._setTextureSampling(b._hardwareTexture.underlyingResource,c)}b.samplingMode=a},b.prototype.bindFramebuffer=function(a,b,c,d,e){e=a;if(b)throw new Error("Cuboid frame buffers are not yet supported in NativeEngine.");if(c||d)throw new Error("Required width/height for frame buffers not yet supported in NativeEngine.");e._framebufferDepthStencil?this._bindUnboundFramebuffer(e._framebufferDepthStencil):this._bindUnboundFramebuffer(e._framebuffer)},b.prototype.unBindFramebuffer=function(a,b,c){void 0===b&&!1,c&&c(),this._bindUnboundFramebuffer(null)},b.prototype.createDynamicVertexBuffer=function(a){return this.createVertexBuffer(a,!0)},b.prototype.updateDynamicIndexBuffer=function(a,b,c){void 0===c&&(c=0);a=a;b=this._normalizeIndexData(b);a.is32Bits=4===b.BYTES_PER_ELEMENT,this._engine.updateDynamicIndexBuffer(a.nativeIndexBuffer,b.buffer,b.byteOffset,b.byteLength,c)},b.prototype.updateDynamicVertexBuffer=function(a,b,c,d){a=a;b=ArrayBuffer.isView(b)?b:new Float32Array(b);this._engine.updateDynamicVertexBuffer(a.nativeVertexBuffer,b.buffer,b.byteOffset+(null!=c?c:0),null!=d?d:b.byteLength)},b.prototype._setTexture=function(a,b,c,d){void 0===c&&!1,void 0===d&&(d=!1);c=this._boundUniforms[a];if(!c)return!1;if(!b)return null!=this._boundTexturesCache[a]&&(this._activeChannel=a,this._setTextureCore(c,null)),!1;if(b.video)this._activeChannel=a,b.update();else if(b.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED)return b.delayLoad(),!1;return d=d?b.depthStencilTexture:b.isReady()?b.getInternalTexture():b.isCube?this.emptyCubeTexture:b.is3D?this.emptyTexture3D:b.is2DArray?this.emptyTexture2DArray:this.emptyTexture,this._activeChannel=a,!(!d||!d._hardwareTexture)&&(this._setTextureWrapMode(d._hardwareTexture.underlyingResource,this._getAddressMode(b.wrapU),this._getAddressMode(b.wrapV),this._getAddressMode(b.wrapR)),this._updateAnisotropicLevel(b),this._setTextureCore(c,d._hardwareTexture.underlyingResource),!0)},b.prototype._setTextureSampling=function(a,b){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTURESAMPLING),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._setTextureWrapMode=function(a,b,c,d){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTUREWRAPMODE),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsUInt32(b),this._commandBufferEncoder.encodeCommandArgAsUInt32(c),this._commandBufferEncoder.encodeCommandArgAsUInt32(d),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._setTextureCore=function(a,b){this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTURE),this._commandBufferEncoder.encodeCommandArgAsNativeData(a),this._commandBufferEncoder.encodeCommandArgAsNativeData(b),this._commandBufferEncoder.finishEncodingCommand()},b.prototype._updateAnisotropicLevel=function(a){var b=a.getInternalTexture();a=a.anisotropicFilteringLevel;b&&b._hardwareTexture&&b._cachedAnisotropicFilteringLevel!==a&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETTEXTUREANISOTROPICLEVEL),this._commandBufferEncoder.encodeCommandArgAsNativeData(b._hardwareTexture.underlyingResource),this._commandBufferEncoder.encodeCommandArgAsUInt32(a),this._commandBufferEncoder.finishEncodingCommand(),b._cachedAnisotropicFilteringLevel=a)},b.prototype._getAddressMode=function(a){switch(a){case r.a.TEXTURE_WRAP_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_WRAP;case r.a.TEXTURE_CLAMP_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_CLAMP;case r.a.TEXTURE_MIRROR_ADDRESSMODE:return _native.Engine.ADDRESS_MODE_MIRROR;default:throw new Error("Unexpected wrap mode: "+a+".")}},b.prototype._bindTexture=function(a,b){a=this._boundUniforms[a];if(a&&b&&b._hardwareTexture){b=b._hardwareTexture.underlyingResource;this._setTextureCore(a,b)}},b.prototype._deleteBuffer=function(a){a.nativeIndexBuffer&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEINDEXBUFFER),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeIndexBuffer),this._commandBufferEncoder.finishEncodingCommand(),delete a.nativeIndexBuffer),a.nativeVertexBuffer&&(this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXBUFFER),this._commandBufferEncoder.encodeCommandArgAsNativeData(a.nativeVertexBuffer),this._commandBufferEncoder.finishEncodingCommand(),delete a.nativeVertexBuffer)},b.prototype.createCanvas=function(a,b){if(!_native.Canvas)throw new Error("Native Canvas plugin not available.");var c=new _native.Canvas;return c.width=a,c.height=b,c},b.prototype.createCanvasImage=function(){if(!_native.Canvas)throw new Error("Native Canvas plugin not available.");return new _native.Image},b.prototype._uploadCompressedDataToTextureDirectly=function(a,b,c,d,e,f,g){throw void 0===f&&0,void 0===g&&0,new Error("_uploadCompressedDataToTextureDirectly not implemented.")},b.prototype._uploadDataToTextureDirectly=function(a,b,c,d){throw void 0===c&&0,void 0===d&&0,new Error("_uploadDataToTextureDirectly not implemented.")},b.prototype._uploadArrayBufferViewToTexture=function(a,b,c,d){throw void 0===c&&0,void 0===d&&0,new Error("_uploadArrayBufferViewToTexture not implemented.")},b.prototype._uploadImageToTexture=function(a,b,c,d){throw void 0===c&&0,void 0===d&&0,new Error("_uploadArrayBufferViewToTexture not implemented.")},b.prototype._getNativeSamplingMode=function(a){switch(a){case r.a.TEXTURE_NEAREST_NEAREST:return _native.Engine.TEXTURE_NEAREST_NEAREST;case r.a.TEXTURE_LINEAR_LINEAR:return _native.Engine.TEXTURE_LINEAR_LINEAR;case r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR:return _native.Engine.TEXTURE_LINEAR_LINEAR_MIPLINEAR;case r.a.TEXTURE_NEAREST_NEAREST_MIPNEAREST:return _native.Engine.TEXTURE_NEAREST_NEAREST_MIPNEAREST;case r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST:return _native.Engine.TEXTURE_NEAREST_LINEAR_MIPNEAREST;case r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR:return _native.Engine.TEXTURE_NEAREST_LINEAR_MIPLINEAR;case r.a.TEXTURE_NEAREST_LINEAR:return _native.Engine.TEXTURE_NEAREST_LINEAR;case r.a.TEXTURE_NEAREST_NEAREST_MIPLINEAR:return _native.Engine.TEXTURE_NEAREST_NEAREST_MIPLINEAR;case r.a.TEXTURE_LINEAR_NEAREST_MIPNEAREST:return _native.Engine.TEXTURE_LINEAR_NEAREST_MIPNEAREST;case r.a.TEXTURE_LINEAR_NEAREST_MIPLINEAR:return _native.Engine.TEXTURE_LINEAR_NEAREST_MIPLINEAR;case r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST:return _native.Engine.TEXTURE_LINEAR_LINEAR_MIPNEAREST;case r.a.TEXTURE_LINEAR_NEAREST:return _native.Engine.TEXTURE_LINEAR_NEAREST;default:throw new Error("Unsupported sampling mode: "+a+".")}},b.prototype._getStencilFunc=function(a){switch(a){case r.a.LESS:return _native.Engine.STENCIL_TEST_LESS;case r.a.LEQUAL:return _native.Engine.STENCIL_TEST_LEQUAL;case r.a.EQUAL:return _native.Engine.STENCIL_TEST_EQUAL;case r.a.GEQUAL:return _native.Engine.STENCIL_TEST_GEQUAL;case r.a.GREATER:return _native.Engine.STENCIL_TEST_GREATER;case r.a.NOTEQUAL:return _native.Engine.STENCIL_TEST_NOTEQUAL;case r.a.NEVER:return _native.Engine.STENCIL_TEST_NEVER;case r.a.ALWAYS:return _native.Engine.STENCIL_TEST_ALWAYS;default:throw new Error("Unsupported stencil func mode: "+a+".")}},b.prototype._getStencilOpFail=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_FAIL_S_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_FAIL_S_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_FAIL_S_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_FAIL_S_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_FAIL_S_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_FAIL_S_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_FAIL_S_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_FAIL_S_DECRSAT;default:throw new Error("Unsupported stencil OpFail mode: "+a+".")}},b.prototype._getStencilDepthFail=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_FAIL_Z_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_FAIL_Z_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_FAIL_Z_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_FAIL_Z_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_FAIL_Z_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_FAIL_Z_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_FAIL_Z_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_FAIL_Z_DECRSAT;default:throw new Error("Unsupported stencil depthFail mode: "+a+".")}},b.prototype._getStencilDepthPass=function(a){switch(a){case r.a.KEEP:return _native.Engine.STENCIL_OP_PASS_Z_KEEP;case r.a.ZERO:return _native.Engine.STENCIL_OP_PASS_Z_ZERO;case r.a.REPLACE:return _native.Engine.STENCIL_OP_PASS_Z_REPLACE;case r.a.INCR:return _native.Engine.STENCIL_OP_PASS_Z_INCR;case r.a.DECR:return _native.Engine.STENCIL_OP_PASS_Z_DECR;case r.a.INVERT:return _native.Engine.STENCIL_OP_PASS_Z_INVERT;case r.a.INCR_WRAP:return _native.Engine.STENCIL_OP_PASS_Z_INCRSAT;case r.a.DECR_WRAP:return _native.Engine.STENCIL_OP_PASS_Z_DECRSAT;default:throw new Error("Unsupported stencil opPass mode: "+a+".")}},b.prototype._getNativeTextureFormat=function(a,b){if(a==r.a.TEXTUREFORMAT_RGB&&b==r.a.TEXTURETYPE_UNSIGNED_INT)return _native.Engine.TEXTURE_FORMAT_RGB8;if(a==r.a.TEXTUREFORMAT_RGBA&&b==r.a.TEXTURETYPE_UNSIGNED_INT)return _native.Engine.TEXTURE_FORMAT_RGBA8;if(a==r.a.TEXTUREFORMAT_RGBA&&b==r.a.TEXTURETYPE_FLOAT)return _native.Engine.TEXTURE_FORMAT_RGBA32F;throw new Error("Unsupported texture format or type: format "+a+", type "+b+".")},b.prototype._getNativeAlphaMode=function(a){switch(a){case r.a.ALPHA_DISABLE:return _native.Engine.ALPHA_DISABLE;case r.a.ALPHA_ADD:return _native.Engine.ALPHA_ADD;case r.a.ALPHA_COMBINE:return _native.Engine.ALPHA_COMBINE;case r.a.ALPHA_SUBTRACT:return _native.Engine.ALPHA_SUBTRACT;case r.a.ALPHA_MULTIPLY:return _native.Engine.ALPHA_MULTIPLY;case r.a.ALPHA_MAXIMIZED:return _native.Engine.ALPHA_MAXIMIZED;case r.a.ALPHA_ONEONE:return _native.Engine.ALPHA_ONEONE;case r.a.ALPHA_PREMULTIPLIED:return _native.Engine.ALPHA_PREMULTIPLIED;case r.a.ALPHA_PREMULTIPLIED_PORTERDUFF:return _native.Engine.ALPHA_PREMULTIPLIED_PORTERDUFF;case r.a.ALPHA_INTERPOLATE:return _native.Engine.ALPHA_INTERPOLATE;case r.a.ALPHA_SCREENMODE:return _native.Engine.ALPHA_SCREENMODE;default:throw new Error("Unsupported alpha mode: "+a+".")}},b.prototype._getNativeAttribType=function(a){switch(a){case W.b.BYTE:return _native.Engine.ATTRIB_TYPE_INT8;case W.b.UNSIGNED_BYTE:return _native.Engine.ATTRIB_TYPE_UINT8;case W.b.SHORT:return _native.Engine.ATTRIB_TYPE_INT16;case W.b.UNSIGNED_SHORT:return _native.Engine.ATTRIB_TYPE_UINT16;case W.b.FLOAT:return _native.Engine.ATTRIB_TYPE_FLOAT;default:throw new Error("Unsupported attribute type: "+a+".")}},b.prototype.getFontOffset=function(a){return{ascent:0,height:0,descent:0}},b.PROTOCOL_VERSION=2,b}(S.a);qf._createNativeDataStream=function(){return _native.NativeDataStream.VALIDATION_ENABLED?new Zf:new ze};var rf,sf,tf,uf,vf,wf,xf,yf,Y,zf,Af,Bf,Cf,Df,Ef,Ff,Gf,Hf,If,Jf,Kf,Lf,Mf,Nf,Of,Pf,Qf,Rf,Sf,Tf,Uf,Vf,Wf,Xf,Yf,Zf=function(a){function b(){return a.call(this)||this}return Object(l.d)(b,a),b.prototype.writeUint32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_UINT_32),a.prototype.writeUint32.call(this,b)},b.prototype.writeInt32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_INT_32),a.prototype.writeInt32.call(this,b)},b.prototype.writeFloat32=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_FLOAT_32),a.prototype.writeFloat32.call(this,b)},b.prototype.writeUint32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_UINT_32_ARRAY),a.prototype.writeUint32Array.call(this,b)},b.prototype.writeInt32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_INT_32_ARRAY),a.prototype.writeInt32Array.call(this,b)},b.prototype.writeFloat32Array=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_FLOAT_32_ARRAY),a.prototype.writeFloat32Array.call(this,b)},b.prototype.writeNativeData=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_NATIVE_DATA),a.prototype.writeNativeData.call(this,b)},b.prototype.writeBoolean=function(b){a.prototype.writeUint32.call(this,_native.NativeDataStream.VALIDATION_BOOLEAN),a.prototype.writeBoolean.call(this,b)},b}(ze),$f=c(36);!function(a){a.SRGB="srgb"}(rf||(rf={})),(function(a){a.LowPower="low-power",a.HighPerformance="high-performance"})(rf||(rf={})),(function(a){a.DepthClamping="depth-clamping",a.Depth24UnormStencil8="depth24unorm-stencil8",a.Depth32FloatStencil8="depth32float-stencil8",a.PipelineStatisticsQuery="pipeline-statistics-query",a.TextureCompressionBC="texture-compression-bc",a.TimestampQuery="timestamp-query"})(sf||(sf={})),(function(a){a[a.MapRead=1]="MapRead",a[a.MapWrite=2]="MapWrite",a[a.CopySrc=4]="CopySrc",a[a.CopyDst=8]="CopyDst",a[a.Index=16]="Index",a[a.Vertex=32]="Vertex",a[a.Uniform=64]="Uniform",a[a.Storage=128]="Storage",a[a.Indirect=256]="Indirect",a[a.QueryResolve=512]="QueryResolve"})(tf||(tf={})),(function(a){a[a.Read=1]="Read",a[a.Write=2]="Write"})(uf||(uf={})),(function(a){a.E1d="1d",a.E2d="2d",a.E3d="3d"})(vf||(vf={})),(function(a){a[a.CopySrc=1]="CopySrc",a[a.CopyDst=2]="CopyDst",a[a.TextureBinding=4]="TextureBinding",a[a.StorageBinding=8]="StorageBinding",a[a.RenderAttachment=16]="RenderAttachment"})(wf||(wf={})),(function(a){a.E1d="1d",a.E2d="2d",a.E2dArray="2d-array",a.Cube="cube",a.CubeArray="cube-array",a.E3d="3d"})(xf||(xf={})),(function(a){a.All="all",a.StencilOnly="stencil-only",a.DepthOnly="depth-only"})(yf||(yf={})),(function(a){a.R8Unorm="r8unorm",a.R8Snorm="r8snorm",a.R8Uint="r8uint",a.R8Sint="r8sint",a.R16Uint="r16uint",a.R16Sint="r16sint",a.R16Float="r16float",a.RG8Unorm="rg8unorm",a.RG8Snorm="rg8snorm",a.RG8Uint="rg8uint",a.RG8Sint="rg8sint",a.R32Uint="r32uint",a.R32Sint="r32sint",a.R32Float="r32float",a.RG16Uint="rg16uint",a.RG16Sint="rg16sint",a.RG16Float="rg16float",a.RGBA8Unorm="rgba8unorm",a.RGBA8UnormSRGB="rgba8unorm-srgb",a.RGBA8Snorm="rgba8snorm",a.RGBA8Uint="rgba8uint",a.RGBA8Sint="rgba8sint",a.BGRA8Unorm="bgra8unorm",a.BGRA8UnormSRGB="bgra8unorm-srgb",a.RGB9E5UFloat="rgb9e5ufloat",a.RGB10A2Unorm="rgb10a2unorm",a.RG11B10UFloat="rg11b10ufloat",a.RG32Uint="rg32uint",a.RG32Sint="rg32sint",a.RG32Float="rg32float",a.RGBA16Uint="rgba16uint",a.RGBA16Sint="rgba16sint",a.RGBA16Float="rgba16float",a.RGBA32Uint="rgba32uint",a.RGBA32Sint="rgba32sint",a.RGBA32Float="rgba32float",a.Stencil8="stencil8",a.Depth16Unorm="depth16unorm",a.Depth24Plus="depth24plus",a.Depth24PlusStencil8="depth24plus-stencil8",a.Depth32Float="depth32float",a.BC1RGBAUnorm="bc1-rgba-unorm",a.BC1RGBAUnormSRGB="bc1-rgba-unorm-srgb",a.BC2RGBAUnorm="bc2-rgba-unorm",a.BC2RGBAUnormSRGB="bc2-rgba-unorm-srgb",a.BC3RGBAUnorm="bc3-rgba-unorm",a.BC3RGBAUnormSRGB="bc3-rgba-unorm-srgb",a.BC4RUnorm="bc4-r-unorm",a.BC4RSnorm="bc4-r-snorm",a.BC5RGUnorm="bc5-rg-unorm",a.BC5RGSnorm="bc5-rg-snorm",a.BC6HRGBUFloat="bc6h-rgb-ufloat",a.BC6HRGBFloat="bc6h-rgb-float",a.BC7RGBAUnorm="bc7-rgba-unorm",a.BC7RGBAUnormSRGB="bc7-rgba-unorm-srgb",a.Depth24UnormStencil8="depth24unorm-stencil8",a.Depth32FloatStencil8="depth32float-stencil8"})(Y||(Y={})),(function(a){a.ClampToEdge="clamp-to-edge",a.Repeat="repeat",a.MirrorRepeat="mirror-repeat"})(zf||(zf={})),(function(a){a.Nearest="nearest",a.Linear="linear"})(Af||(Af={})),(function(a){a.Never="never",a.Less="less",a.Equal="equal",a.LessEqual="less-equal",a.Greater="greater",a.NotEqual="not-equal",a.GreaterEqual="greater-equal",a.Always="always"})(Bf||(Bf={})),(function(a){a[a.Vertex=1]="Vertex",a[a.Fragment=2]="Fragment",a[a.Compute=4]="Compute"})(Cf||(Cf={})),(function(a){a.Uniform="uniform",a.Storage="storage",a.ReadOnlyStorage="read-only-storage"})(Df||(Df={})),(function(a){a.Filtering="filtering",a.NonFiltering="non-filtering",a.Comparison="comparison"})(Ef||(Ef={})),(function(a){a.Float="float",a.UnfilterableFloat="unfilterable-float",a.Depth="depth",a.Sint="sint",a.Uint="uint"})(Ff||(Ff={})),(function(a){a.WriteOnly="write-only"})(Gf||(Gf={})),(function(a){a.Error="error",a.Warning="warning",a.Info="info"})(Hf||(Hf={})),(function(a){a.PointList="point-list",a.LineList="line-list",a.LineStrip="line-strip",a.TriangleList="triangle-list",a.TriangleStrip="triangle-strip"})(If||(If={})),(function(a){a.CCW="ccw",a.CW="cw"})(Jf||(Jf={})),(function(a){a.None="none",a.Front="front",a.Back="back"})(Kf||(Kf={})),(function(a){a[a.Red=1]="Red",a[a.Green=2]="Green",a[a.Blue=4]="Blue",a[a.Alpha=8]="Alpha",a[a.All=15]="All"})(Lf||(Lf={})),(function(a){a.Zero="zero",a.One="one",a.Src="src",a.OneMinusSrc="one-minus-src",a.SrcAlpha="src-alpha",a.OneMinusSrcAlpha="one-minus-src-alpha",a.Dst="dst",a.OneMinusDst="one-minus-dst",a.DstAlpha="dst-alpha",a.OneMinusDstAlpha="one-minus-dst-alpha",a.SrcAlphaSaturated="src-alpha-saturated",a.Constant="constant",a.OneMinusConstant="one-minus-constant"})(Mf||(Mf={})),(function(a){a.Add="add",a.Subtract="subtract",a.ReverseSubtract="reverse-subtract",a.Min="min",a.Max="max"})(Nf||(Nf={})),(function(a){a.Keep="keep",a.Zero="zero",a.Replace="replace",a.Invert="invert",a.IncrementClamp="increment-clamp",a.DecrementClamp="decrement-clamp",a.IncrementWrap="increment-wrap",a.DecrementWrap="decrement-wrap"})(Of||(Of={})),(function(a){a.Uint16="uint16",a.Uint32="uint32"})(Pf||(Pf={})),(function(a){a.Uint8x2="uint8x2",a.Uint8x4="uint8x4",a.Sint8x2="sint8x2",a.Sint8x4="sint8x4",a.Unorm8x2="unorm8x2",a.Unorm8x4="unorm8x4",a.Snorm8x2="snorm8x2",a.Snorm8x4="snorm8x4",a.Uint16x2="uint16x2",a.Uint16x4="uint16x4",a.Sint16x2="sint16x2",a.Sint16x4="sint16x4",a.Unorm16x2="unorm16x2",a.Unorm16x4="unorm16x4",a.Snorm16x2="snorm16x2",a.Snorm16x4="snorm16x4",a.Float16x2="float16x2",a.Float16x4="float16x4",a.Float32="float32",a.Float32x2="float32x2",a.Float32x3="float32x3",a.Float32x4="float32x4",a.Uint32="uint32",a.Uint32x2="uint32x2",a.Uint32x3="uint32x3",a.Uint32x4="uint32x4",a.Sint32="sint32",a.Sint32x2="sint32x2",a.Sint32x3="sint32x3",a.Sint32x4="sint32x4"})(Qf||(Qf={})),(function(a){a.Vertex="vertex",a.Instance="instance"})(Rf||(Rf={})),(function(a){a.Load="load"})(Sf||(Sf={})),(function(a){a.Store="store",a.Discard="discard"})(Tf||(Tf={})),(function(a){a.Occlusion="occlusion",a.PipelineStatistics="pipeline-statistics",a.Timestamp="timestamp"})(Uf||(Uf={})),(function(a){a.VertexShaderInvocations="vertex-shader-invocations",a.ClipperInvocations="clipper-invocations",a.ClipperPrimitivesOut="clipper-primitives-out",a.FragmentShaderInvocations="fragment-shader-invocations",a.ComputeShaderInvocations="compute-shader-invocations"})(Vf||(Vf={})),(function(a){a.Opaque="opaque",a.Premultiplied="premultiplied"})(Wf||(Wf={})),(function(a){a.Destroyed="destroyed"})(Xf||(Xf={})),(function(a){a.OutOfMemory="out-of-memory",a.Validation="validation"})(Yf||(Yf={}));var ag=function(){function a(){this.shaderLanguage=Xd.a.GLSL}return a.prototype._addUniformToLeftOverUBO=function(a,b,c){a=(c=this._getArraySize(a,b,c))[0],b=c[1],c=c[2];for(var d=0;d=0&&(f.push(g[d]),e.push(b))}this.shaderProcessingContext.attributeNamesFromEffect=f,this.shaderProcessingContext.attributeLocationsFromEffect=e},a.prototype.buildUniformLayout=function(){if(this.shaderProcessingContext.leftOverUniforms.length){this.uniformBuffer=new $c.a(this.engine,void 0,void 0,"leftOver-"+this._name);for(var a=0,b=this.shaderProcessingContext.leftOverUniforms;a)?$/,"$1");d=ag.UniformSizes[d];this.uniformBuffer.addUniform(c.name,d,c.length),this._leftOverUniformsByName[c.name]=c.type}this.uniformBuffer.create()}},a.prototype.dispose=function(){this.uniformBuffer&&this.uniformBuffer.dispose()},a.prototype.setInt=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt(a,b)},a.prototype.setInt2=function(a,b,c){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt2(a,b,c)},a.prototype.setInt3=function(a,b,c,d){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt3(a,b,c,d)},a.prototype.setInt4=function(a,b,c,d,e){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateInt4(a,b,c,d,e)},a.prototype.setIntArray=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateIntArray(a,b)},a.prototype.setIntArray2=function(a,b){this.setIntArray(a,b)},a.prototype.setIntArray3=function(a,b){this.setIntArray(a,b)},a.prototype.setIntArray4=function(a,b){this.setIntArray(a,b)},a.prototype.setArray=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateArray(a,b)},a.prototype.setArray2=function(a,b){this.setArray(a,b)},a.prototype.setArray3=function(a,b){this.setArray(a,b)},a.prototype.setArray4=function(a,b){this.setArray(a,b)},a.prototype.setMatrices=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrices(a,b)},a.prototype.setMatrix=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix(a,b)},a.prototype.setMatrix3x3=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix3x3(a,b)},a.prototype.setMatrix2x2=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateMatrix2x2(a,b)},a.prototype.setFloat=function(a,b){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat(a,b)},a.prototype.setVector2=function(a,b){this.setFloat2(a,b.x,b.y)},a.prototype.setFloat2=function(a,b,c){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat2(a,b,c)},a.prototype.setVector3=function(a,b){this.setFloat3(a,b.x,b.y,b.z)},a.prototype.setFloat3=function(a,b,c,d){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat3(a,b,c,d)},a.prototype.setVector4=function(a,b){this.setFloat4(a,b.x,b.y,b.z,b.w)},a.prototype.setFloat4=function(a,b,c,d,e){this.uniformBuffer&&this._leftOverUniformsByName[a]&&this.uniformBuffer.updateFloat4(a,b,c,d,e)},a.prototype.setColor3=function(a,b){this.setFloat3(a,b.r,b.g,b.b)},a.prototype.setColor4=function(a,b,c){this.setFloat4(a,b.r,b.g,b.b,c)},a.prototype.setDirectColor4=function(a,b){this.setFloat4(a,b.r,b.g,b.b,b.a)},a.prototype._getVertexShaderCode=function(){var a;return null===(a=this.sources)||void 0===a?void 0:a.vertex},a.prototype._getFragmentShaderCode=function(){var a;return null===(a=this.sources)||void 0===a?void 0:a.fragment},a}(),cg={mat2:2,mat3:3,mat4:4,mat2x2:2,mat3x3:3,mat4x4:4},dg=function(){function a(a){this.shaderLanguage=a,this._attributeNextLocation=0,this._varyingNextLocation=0,this.freeGroupIndex=0,this.freeBindingIndex=0,this.availableVaryings={},this.availableAttributes={},this.availableBuffers={},this.availableTextures={},this.availableSamplers={},this.orderedAttributes=[],this.bindGroupLayoutEntries=[],this.bindGroupLayoutEntryInfo=[],this.bindGroupEntries=[],this.bufferNames=[],this.textureNames=[],this.samplerNames=[],this.leftOverUniforms=[],this._findStartingGroupBinding()}return Object.defineProperty(a,"KnownUBOs",{get:function(){return a._SimplifiedKnownBindings?a._SimplifiedKnownUBOs:a._KnownUBOs},enumerable:!1,configurable:!0}),a.prototype._findStartingGroupBinding=function(){var b=a.KnownUBOs,c=[];for(var d in b){var e=b[d].binding;-1!==e.groupIndex&&(void 0===c[e.groupIndex]?c[e.groupIndex]=e.bindingIndex:c[e.groupIndex]=Math.max(c[e.groupIndex],e.bindingIndex))}this.freeGroupIndex=c.length-1,0===this.freeGroupIndex?(this.freeGroupIndex++,this.freeBindingIndex=0):this.freeBindingIndex=c[c.length-1]+1},a.prototype.getAttributeNextLocation=function(a,b){void 0===b&&(b=0);var c=this._attributeNextLocation;return this._attributeNextLocation+=(null!==(a=cg[a])&&void 0!==a?a:1)*(b||1),c},a.prototype.getVaryingNextLocation=function(a,b){void 0===b&&(b=0);var c=this._varyingNextLocation;return this._varyingNextLocation+=(null!==(a=cg[a])&&void 0!==a?a:1)*(b||1),c},a.prototype.getNextFreeUBOBinding=function(){return this._getNextFreeBinding(1)},a.prototype._getNextFreeBinding=function(a){if(this.freeBindingIndex>65536-a&&(this.freeGroupIndex++,this.freeBindingIndex=0),4===this.freeGroupIndex)throw"Too many textures or UBOs have been declared and it is not supported in WebGPU.";var b={groupIndex:this.freeGroupIndex,bindingIndex:this.freeBindingIndex};return this.freeBindingIndex+=a,b},a._SimplifiedKnownBindings=!0,a._SimplifiedKnownUBOs={Scene:{binding:{groupIndex:0,bindingIndex:0}},Light0:{binding:{groupIndex:-1,bindingIndex:-1}},Light1:{binding:{groupIndex:-1,bindingIndex:-1}},Light2:{binding:{groupIndex:-1,bindingIndex:-1}},Light3:{binding:{groupIndex:-1,bindingIndex:-1}},Light4:{binding:{groupIndex:-1,bindingIndex:-1}},Light5:{binding:{groupIndex:-1,bindingIndex:-1}},Light6:{binding:{groupIndex:-1,bindingIndex:-1}},Light7:{binding:{groupIndex:-1,bindingIndex:-1}},Light8:{binding:{groupIndex:-1,bindingIndex:-1}},Light9:{binding:{groupIndex:-1,bindingIndex:-1}},Light10:{binding:{groupIndex:-1,bindingIndex:-1}},Light11:{binding:{groupIndex:-1,bindingIndex:-1}},Light12:{binding:{groupIndex:-1,bindingIndex:-1}},Light13:{binding:{groupIndex:-1,bindingIndex:-1}},Light14:{binding:{groupIndex:-1,bindingIndex:-1}},Light15:{binding:{groupIndex:-1,bindingIndex:-1}},Light16:{binding:{groupIndex:-1,bindingIndex:-1}},Light17:{binding:{groupIndex:-1,bindingIndex:-1}},Light18:{binding:{groupIndex:-1,bindingIndex:-1}},Light19:{binding:{groupIndex:-1,bindingIndex:-1}},Light20:{binding:{groupIndex:-1,bindingIndex:-1}},Light21:{binding:{groupIndex:-1,bindingIndex:-1}},Light22:{binding:{groupIndex:-1,bindingIndex:-1}},Light23:{binding:{groupIndex:-1,bindingIndex:-1}},Light24:{binding:{groupIndex:-1,bindingIndex:-1}},Light25:{binding:{groupIndex:-1,bindingIndex:-1}},Light26:{binding:{groupIndex:-1,bindingIndex:-1}},Light27:{binding:{groupIndex:-1,bindingIndex:-1}},Light28:{binding:{groupIndex:-1,bindingIndex:-1}},Light29:{binding:{groupIndex:-1,bindingIndex:-1}},Light30:{binding:{groupIndex:-1,bindingIndex:-1}},Light31:{binding:{groupIndex:-1,bindingIndex:-1}},Material:{binding:{groupIndex:-1,bindingIndex:-1}},Mesh:{binding:{groupIndex:-1,bindingIndex:-1}}},a._KnownUBOs={Scene:{binding:{groupIndex:0,bindingIndex:0}},Light0:{binding:{groupIndex:1,bindingIndex:0}},Light1:{binding:{groupIndex:1,bindingIndex:1}},Light2:{binding:{groupIndex:1,bindingIndex:2}},Light3:{binding:{groupIndex:1,bindingIndex:3}},Light4:{binding:{groupIndex:1,bindingIndex:4}},Light5:{binding:{groupIndex:1,bindingIndex:5}},Light6:{binding:{groupIndex:1,bindingIndex:6}},Light7:{binding:{groupIndex:1,bindingIndex:7}},Light8:{binding:{groupIndex:1,bindingIndex:8}},Light9:{binding:{groupIndex:1,bindingIndex:9}},Light10:{binding:{groupIndex:1,bindingIndex:10}},Light11:{binding:{groupIndex:1,bindingIndex:11}},Light12:{binding:{groupIndex:1,bindingIndex:12}},Light13:{binding:{groupIndex:1,bindingIndex:13}},Light14:{binding:{groupIndex:1,bindingIndex:14}},Light15:{binding:{groupIndex:1,bindingIndex:15}},Light16:{binding:{groupIndex:1,bindingIndex:16}},Light17:{binding:{groupIndex:1,bindingIndex:17}},Light18:{binding:{groupIndex:1,bindingIndex:18}},Light19:{binding:{groupIndex:1,bindingIndex:19}},Light20:{binding:{groupIndex:1,bindingIndex:20}},Light21:{binding:{groupIndex:1,bindingIndex:21}},Light22:{binding:{groupIndex:1,bindingIndex:22}},Light23:{binding:{groupIndex:1,bindingIndex:23}},Light24:{binding:{groupIndex:1,bindingIndex:24}},Light25:{binding:{groupIndex:1,bindingIndex:25}},Light26:{binding:{groupIndex:1,bindingIndex:26}},Light27:{binding:{groupIndex:1,bindingIndex:27}},Light28:{binding:{groupIndex:1,bindingIndex:28}},Light29:{binding:{groupIndex:1,bindingIndex:29}},Light30:{binding:{groupIndex:1,bindingIndex:30}},Light31:{binding:{groupIndex:1,bindingIndex:31}},Material:{binding:{groupIndex:2,bindingIndex:0}},Mesh:{binding:{groupIndex:2,bindingIndex:1}}},a}(),eg=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._missingVaryings=[],b._textureArrayProcessing=[],b.shaderLanguage=Xd.a.GLSL,b}return Object(l.d)(b,a),b.prototype._getArraySize=function(a,b,c){var d=0,e=a.indexOf("["),f=a.indexOf("]");if(e>0&&f>0){f=a.substring(e+1,f);d=+f,isNaN(d)&&(d=+c[f.trim()]),a=a.substr(0,e)}return[a,b,d]},b.prototype.initializeShaders=function(a){this.webgpuProcessingContext=a,this._missingVaryings.length=0,this._textureArrayProcessing.length=0},b.prototype.varyingProcessor=function(a,b,c,d){this._preProcessors=c;d=/s*varyings+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/gm.exec(a);if(null!=d){var e,f=d[1],g=d[2];b?(e=this.webgpuProcessingContext.availableVaryings[g],this._missingVaryings[e]="",void 0===e&&q.a.Warn("Invalid fragment shader: The varying named ""+g+"" is not declared in the vertex shader! This declaration will be ignored.")):(e=this.webgpuProcessingContext.getVaryingNextLocation(f,this._getArraySize(g,f,c)[2]),this.webgpuProcessingContext.availableVaryings[g]=e,this._missingVaryings[e]="layout(location = "+e+") in "+f+" "+g+";"),a=a.replace(d[0],void 0===e?"":"layout(location = "+e+") "+(b?"in":"out")+" "+f+" "+g+";")}return a},b.prototype.attributeProcessor=function(a,b,c){this._preProcessors=b;c=/s*attributes+(S+)s+(S+)s*;/gm.exec(a);if(null!=c){var d=c[1],e=c[2];b=this.webgpuProcessingContext.getAttributeNextLocation(d,this._getArraySize(e,d,b)[2]);this.webgpuProcessingContext.availableAttributes[e]=b,this.webgpuProcessingContext.orderedAttributes[b]=e,a=a.replace(c[0],"layout(location = "+b+") in "+d+" "+e+";")}return a},b.prototype.uniformProcessor=function(a,b,c,d){var e,f;this._preProcessors=c;d=/s*uniforms+(?:(?:highp)?|(?:lowp)?)s*(S+)s+(S+)s*;/gm.exec(a);if(null!=d){var g=d[1];d=d[2];if(0===g.indexOf("sampler")||1===g.indexOf("sampler")){var h=0;d=(e=this._getArraySize(d,g,c))[0],g=e[1],h=e[2];e=this.webgpuProcessingContext.availableTextures[d];if(!e){e={autoBindSampler:!0,isTextureArray:h>0,isStorageTexture:!1,textures:[],sampleType:Ff.Float};for(var i=0;i<(h||1);++i)e.textures.push(this.webgpuProcessingContext.getNextFreeUBOBinding())}f=null!==(f=ag._SamplerTypeByWebGLSamplerType[g])&&void 0!==f?f:"sampler";var j=!!ag._IsComparisonSamplerByWebGPUSamplerType[f],k=j?Ef.Comparison:Ef.Filtering,l=d+ag.AutoSamplerSuffix,m=this.webgpuProcessingContext.availableSamplers[l];m||(m={binding:this.webgpuProcessingContext.getNextFreeUBOBinding(),type:k});k="u"===g.charAt(0)?"u":"i"===g.charAt(0)?"i":"";k&&(g=g.substr(1));j=j?Ff.Depth:"u"===k?Ff.Uint:"i"===k?Ff.Sint:Ff.Float;e.sampleType=j;j=h>0;var r=m.binding.groupIndex,s=m.binding.bindingIndex,t=ag._SamplerFunctionByWebGLSamplerType[g],z=ag._TextureTypeByWebGLSamplerType[g],C=ag._GpuTextureViewDimensionByWebGPUTextureType[z];if(j){j=[];j.push("layout(set = "+r+", binding = "+s+") uniform "+k+f+" "+l+";"),a=" ";for(i=0;i0?" ":"")+"#define "+d+i+" "+k+t+"("+d+"Texture"+i+", "+l+")"}a=j.join(" ")+a,this._textureArrayProcessing.push(d)}else h=1,a="layout(set = "+r+", binding = "+s+") uniform "+k+f+" "+l+"; layout(set = "+e.textures[0].groupIndex+", binding = "+e.textures[0].bindingIndex+") uniform "+z+" "+d+"Texture; #define "+d+" "+k+t+"("+d+"Texture, "+l+")";this.webgpuProcessingContext.availableTextures[d]=e,this.webgpuProcessingContext.availableSamplers[l]=m,this._addSamplerBindingDescription(l,m,!b);for(i=0;i0?b+=" "+d.type+" "+d.name+"["+d.length+"]; ":b+=" "+d.type+" "+d.name+"; "}return b+="}; "},b.prototype.finalizeShaders=function(a,b,c){for(c=0;c0&&(b=d+" "+b)}d=this._buildLeftOverUBO();return a=d+a,b=d+b,this._collectBindingNames(),this._preCreateBindGroupEntries(),this._preProcessors=null,{vertexCode:a,fragmentCode:b}},b}(ag);V="#if NUM_BONE_INFLUENCERS>0 #ifdef BONETEXTURE var boneSampler : texture_2d; uniform boneTextureWidth : f32; #else uniform mBones : array; #ifdef BONES_VELOCITY_ENABLED uniform mPreviousBones : array; #endif #endif attribute matricesIndices : vec4; attribute matricesWeights : vec4; #if NUM_BONE_INFLUENCERS>4 attribute matricesIndicesExtra : vec4; attribute matricesWeightsExtra : vec4; #endif #ifdef BONETEXTURE fn readMatrixFromRawSampler(smp : texture_2d,index : f32) -> mat4x4 { let offset=i32(index)*4; let m0=textureLoad(smp,vec2(offset+0,0),0); let m1=textureLoad(smp,vec2(offset+1,0),0); let m2=textureLoad(smp,vec2(offset+2,0),0); let m3=textureLoad(smp,vec2(offset+3,0),0); return mat4x4(m0,m1,m2,m3); } #endif #endif";X.a.IncludesShadersStoreWGSL.bonesDeclaration=V;a="#if NUM_BONE_INFLUENCERS>0 var influence : mat4x4; #ifdef BONETEXTURE influence=readMatrixFromRawSampler(boneSampler,matricesIndices[0])*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[1])*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[2])*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndices[3])*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[0])*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[1])*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[2])*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence=influence+readMatrixFromRawSampler(boneSampler,matricesIndicesExtra[3])*matricesWeightsExtra[3]; #endif #else influence=uniforms.mBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 influence=influence+uniforms.mBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 influence=influence+uniforms.mBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 influence=influence+uniforms.mBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 influence=influence+uniforms.mBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 influence=influence+uniforms.mBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 influence=influence+uniforms.mBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 influence=influence+uniforms.mBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif #endif finalWorld=finalWorld*influence; #endif";X.a.IncludesShadersStoreWGSL.bonesVertex=a;V="#ifdef INSTANCES attribute world0 : vec4; attribute world1 : vec4; attribute world2 : vec4; attribute world3 : vec4; #if defined(THIN_INSTANCES) && !defined(WORLD_UBO) uniform world : mat4x4; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) attribute previousWorld0 : vec4; attribute previousWorld1 : vec4; attribute previousWorld2 : vec4; attribute previousWorld3 : vec4; #ifdef THIN_INSTANCES uniform previousWorld : mat4x4; #endif #endif #else #if !defined(WORLD_UBO) uniform world : mat4x4; #endif #if defined(VELOCITY) || defined(PREPASS_VELOCITY) uniform previousWorld : mat4x4; #endif #endif";X.a.IncludesShadersStoreWGSL.instancesDeclaration=V;a="#ifdef INSTANCES var finalWorld=mat4x4(world0,world1,world2,world3); #if defined(PREPASS_VELOCITY) || defined(VELOCITY) var finalPreviousWorld=mat4x4(previousWorld0,previousWorld1,previousWorld2,previousWorld3); #endif #ifdef THIN_INSTANCES #if !defined(WORLD_UBO) finalWorld=uniforms.world*finalWorld; #else finalWorld=mesh.world*finalWorld; #endif #if defined(PREPASS_VELOCITY) || defined(VELOCITY) finalPreviousWorld=previousWorld*finalPreviousWorld; #endif #endif #else #if !defined(WORLD_UBO) var finalWorld=uniforms.world; #else var finalWorld=mesh.world; #endif #if defined(PREPASS_VELOCITY) || defined(VELOCITY) var finalPreviousWorld=previousWorld; #endif #endif";X.a.IncludesShadersStoreWGSL.instancesVertex=a;V="[[block]] struct Mesh { world : mat4x4; visibility : f32; }; var mesh : Mesh; #define WORLD_UBO ";X.a.IncludesShadersStoreWGSL.meshUboDeclaration=V;a="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE vertexID=f32(gl_VertexID)*uniforms.morphTargetTextureInfo.x; positionUpdated=positionUpdated+(readVector3FromRawSampler({X},vertexID)-position)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #ifdef MORPHTARGETS_NORMAL normalUpdated=normalUpdated+(readVector3FromRawSampler({X},vertexID)-normal)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #endif #ifdef MORPHTARGETS_UV uvUpdated=uvUpdated+(readVector3FromRawSampler({X},vertexID).xy-uv)*uniforms.morphTargetInfluences[{X}]; vertexID=vertexID+1.0; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz=tangentUpdated.xyz+(readVector3FromRawSampler({X},vertexID)-tangent.xyz)*uniforms.morphTargetInfluences[{X}]; #endif #else positionUpdated=positionUpdated+(position{X}-position)*uniforms.morphTargetInfluences[{X}]; #ifdef MORPHTARGETS_NORMAL normalUpdated+=(normal{X}-normal)*uniforms.morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_TANGENT tangentUpdated.xyz=tangentUpdated.xyz+(tangent{X}-tangent.xyz)*uniforms.morphTargetInfluences[{X}]; #endif #ifdef MORPHTARGETS_UV uvUpdated=uvUpdated+(uv_{X}-uv)*uniforms.morphTargetInfluences[{X}]; #endif #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertex=a;V="#ifdef MORPHTARGETS #ifndef MORPHTARGETS_TEXTURE attribute position{X} : vec3; #ifdef MORPHTARGETS_NORMAL attribute normal{X} : vec3; #endif #ifdef MORPHTARGETS_TANGENT attribute tangent{X} : vec3; #endif #ifdef MORPHTARGETS_UV attribute uv_{X} : vec2; #endif #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertexDeclaration=V;X.a.IncludesShadersStoreWGSL.morphTargetsVertexGlobal="#ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE var vertexID : f32; #endif #endif";a="#ifdef MORPHTARGETS uniform morphTargetInfluences : array; #ifdef MORPHTARGETS_TEXTURE uniform morphTargetTextureIndices : array; uniform morphTargetTextureInfo : vec3; var morphTargets : texture_2d_array; var morphTargetsSampler : sampler; fn readVector3FromRawSampler(targetIndex : i32,vertexIndex : f32) -> vec3 { let y=floor(vertexIndex/uniforms.morphTargetTextureInfo.y); let x=vertexIndex-y*uniforms.morphTargetTextureInfo.y; let textureUV=vec2((x+0.5)/uniforms.morphTargetTextureInfo.y,(y+0.5)/uniforms.morphTargetTextureInfo.z); return textureSampleLevel(morphTargets,morphTargetsSampler,textureUV,i32(uniforms.morphTargetTextureIndices[targetIndex]),0.0).xyz; } #endif #endif";X.a.IncludesShadersStoreWGSL.morphTargetsVertexGlobalDeclaration=a;V="[[block]] struct Scene { viewProjection : mat4x4; #ifdef MULTIVIEW viewProjectionR : mat4x4; #endif view : mat4x4; projection : mat4x4; vEyePosition : vec4; }; var scene : Scene; ";X.a.IncludesShadersStoreWGSL.sceneUboDeclaration=V;var fg,gg={texture_1d:xf.E1d,texture_2d:xf.E2d,texture_2d_array:xf.E2dArray,texture_3d:xf.E3d,texture_cube:xf.Cube,texture_cube_array:xf.CubeArray,texture_multisampled_2d:xf.E2d,texture_depth_2d:xf.E2d,texture_depth_2d_array:xf.E2dArray,texture_depth_cube:xf.Cube,texture_depth_cube_array:xf.CubeArray,texture_depth_multisampled_2d:xf.E2d,texture_storage_1d:xf.E1d,texture_storage_2d:xf.E2d,texture_storage_2d_array:xf.E2dArray,texture_storage_3d:xf.E3d,texture_external:null},hg=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.shaderLanguage=Xd.a.WGSL,b.uniformRegexp=/uniforms+(w+)s*:s*(.+)s*;/,b.textureRegexp=/vars+(w+)s*:s*((array)?s*(,s*w+s*>s*)?);/,b.noPrecision=!0,b}return Object(l.d)(b,a),b.prototype._getArraySize=function(a,b,c){var d=0,e=b.lastIndexOf(">");if(b.indexOf("array")>=0&&e>0){for(var f=e;f>0&&" "!==b.charAt(f)&&","!==b.charAt(f);)f--;e=b.substring(f+1,e);for(d=+e,isNaN(d)&&(d=+c[e.trim()]);f>0&&(" "===b.charAt(f)||","===b.charAt(f));)f--;b=b.substring(b.indexOf("<")+1,f+1)}return[a,b,d]},b.prototype.initializeShaders=function(a){this.webgpuProcessingContext=a,this._attributesWGSL=[],this._attributesDeclWGSL=[],this._attributeNamesWGSL=[],this._varyingsWGSL=[],this._varyingsDeclWGSL=[],this._varyingNamesWGSL=[]},b.prototype.preProcessShaderCode=function(a){return hf(a)},b.prototype.varyingProcessor=function(a,b,c,d){d=/s*varyings+(?:(?:highp)?|(?:lowp)?)s*(S+)s*:s*(.+)s*;/gm.exec(a);if(null!==d){var e=d[2];d=d[1];b?void 0===this.webgpuProcessingContext.availableVaryings[d]&&q.a.Warn("Invalid fragment shader: The varying named ""+d+"" is not declared in the vertex shader! This declaration will be ignored."):(b=this.webgpuProcessingContext.getVaryingNextLocation(e,this._getArraySize(d,e,c)[2]),this.webgpuProcessingContext.availableVaryings[d]=b,this._varyingsWGSL.push("[[location("+b+")]] "+d+" : "+e+";"),this._varyingsDeclWGSL.push("var "+d+" : "+e+";"),this._varyingNamesWGSL.push(d)),a=""}return a},b.prototype.attributeProcessor=function(a,b,c){c=/s*attributes+(S+)s*:s*(.+)s*;/gm.exec(a);if(null!==c){var d=c[2];c=c[1];b=this.webgpuProcessingContext.getAttributeNextLocation(d,this._getArraySize(c,d,b)[2]);this.webgpuProcessingContext.availableAttributes[c]=b,this.webgpuProcessingContext.orderedAttributes[b]=c,this._attributesWGSL.push("[[location("+b+")]] "+c+" : "+d+";"),this._attributesDeclWGSL.push("var "+c+" : "+d+";"),this._attributeNamesWGSL.push(c),a=""}return a},b.prototype.uniformProcessor=function(a,b,c,d){b=this.uniformRegexp.exec(a);if(null!==b){d=b[2];b=b[1];this._addUniformToLeftOverUBO(b,d,c),a=""}return a},b.prototype.textureProcessor=function(a,b,c,d){d=this.textureRegexp.exec(a);if(null!==d){var e=d[1],f=d[2],g=!!d[3],h=d[4],i=h.indexOf("storage")>0;d=d[6];var j=i?d.substring(0,d.indexOf(",")).trim():null;g=g?this._getArraySize(e,f,c)[2]:0;f=this.webgpuProcessingContext.availableTextures[e];if(f)g=f.textures.length;else{f={isTextureArray:g>0,isStorageTexture:i,textures:[],sampleType:Ff.Float},g=g||1;for(c=0;c0;var k=gg[h];i=i?Ff.Depth:"u32"===d?Ff.Uint:"i32"===d?Ff.Sint:Ff.Float;if(f.sampleType=i,void 0===k)throw"Can"t get the texture dimension corresponding to the texture function ""+h+""!";for(c=0;c0&&(e+=this._attributesWGSL.join(" ")),e+=" }; ";var f="struct FragmentInputs { [[builtin(position)]] position : vec4; ";this._varyingsWGSL.length>0&&(f+=this._varyingsWGSL.join(" ")),a="var gl_VertexID : u32; var gl_InstanceID : u32; var gl_Position : vec4; "+e+d+(f+=" }; ")+c+a;for(e=" var output : FragmentInputs; gl_VertexID = input.vertexIndex; gl_InstanceID = input.instanceIndex; ",d=0;d0&&(g+=this._varyingsWGSL.join(" ")),g+=" }; ";for(var e="struct FragmentOutputs { [[location(0)]] color : vec4; ",f=!1,h=0;!(f||(h=b.indexOf("gl_FragDepth",h))<0);){var i=h;for(f=!0;h>1&&" "!==b.charAt(h);){if("/"===b.charAt(h)&&"/"===b.charAt(h-1)){f=!1;break}h--}h=i+12}f&&(e+=" [[builtin(frag_depth)]] fragDepth: f32; "),b="var gl_FragCoord : vec4; var gl_FrontFacing : bool; var gl_FragColor : vec4; var gl_FragDepth : f32; "+g+c+(e+="}; ")+b;i=" var output : FragmentOutputs; gl_FragCoord = input.position; gl_FrontFacing = input.frontFacing; ";for(d=0;d)?$/,"$1");g=ag.UniformSizes[g];f.length>0?c+=g<=2?" [[align(16)]] "+f.name+" : [[stride(16)]] array<"+f.type+", "+f.length+">; ":" "+f.name+" : array<"+f.type+", "+f.length+">; ":c+=" "+f.name+" : "+f.type+"; "}return c+="}; ",c+="[[group("+b.binding.groupIndex+"), binding("+b.binding.bindingIndex+")]] var uniforms : "+a+"; "},b.prototype._injectStartingAndEndingCode=function(a,b,c){if(b){var d=a.indexOf("fn main");if(d>=0){for(;d++s+(S+)s*:s*(S+)s*;/gm;;){var d=c.exec(a);if(null===d)break;var e=d[1],f=d[3],g=d[4],h=d[5],i=this.webgpuProcessingContext.availableBuffers[g];if(!i){var j="uniform"===e?dg.KnownUBOs[h]:null;j?(g=h,-1===(h=j.binding).groupIndex&&(h=this.webgpuProcessingContext.getNextFreeUBOBinding())):h=this.webgpuProcessingContext.getNextFreeUBOBinding(),i={binding:h},this.webgpuProcessingContext.availableBuffers[g]=i}this._addBufferBindingDescription(g,this.webgpuProcessingContext.availableBuffers[g],"read_write"===f?Df.Storage:"storage"===e?Df.ReadOnlyStorage:Df.Uniform,b);j=i.binding.groupIndex;h=i.binding.bindingIndex;g=a.substring(0,d.index);f="[[group("+j+"), binding("+h+")]] ";e=a.substring(d.index);a=g+f+e,c.lastIndex+=f.length}return a},b}(ag),ig=function(){function a(a){void 0===a&&(a=null),this.format=Y.RGBA8Unorm,this.textureUsages=0,this.textureAdditionalUsages=0,this._webgpuTexture=a,this._webgpuMSAATexture=null,this.view=null}return Object.defineProperty(a.prototype,"underlyingResource",{get:function(){return this._webgpuTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"msaaTexture",{get:function(){return this._webgpuMSAATexture},set:function(a){this._webgpuMSAATexture=a},enumerable:!1,configurable:!0}),a.prototype.set=function(a){this._webgpuTexture=a},a.prototype.setMSAATexture=function(a){this._webgpuMSAATexture=a},a.prototype.setUsage=function(a,b,c,d,e){b=a!==pb.b.RenderTarget&&b,this.createView({format:this.format,dimension:c?xf.Cube:xf.E2d,mipLevelCount:b?H.a.ILog2(Math.max(d,e))+1:1,baseArrayLayer:0,baseMipLevel:0,arrayLayerCount:c?6:1,aspect:yf.All})},a.prototype.createView=function(a){this.view=this._webgpuTexture.createView(a)},a.prototype.reset=function(){this._webgpuTexture=null,this._webgpuMSAATexture=null,this.view=null},a.prototype.release=function(){var a;null===(a=this._webgpuTexture)||void 0===a||a.destroy(),null===(a=this._webgpuMSAATexture)||void 0===a||a.destroy(),this.reset()},a}();!function(a){a[a.MipMap=0]="MipMap",a[a.InvertYPremultiplyAlpha=1]="InvertYPremultiplyAlpha",a[a.Clear=2]="Clear"}(fg||(fg={}));var jg,kg=[{vertex:" const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f)); layout(location = 0) out vec2 vTex; void main() { vTex = tex[gl_VertexIndex]; gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" layout(set = 0, binding = 0) uniform sampler imgSampler; layout(set = 0, binding = 1) uniform texture2D img; layout(location = 0) in vec2 vTex; layout(location = 0) out vec4 outColor; void main() { outColor = texture(sampler2D(img, imgSampler), vTex); } "},{vertex:" #extension GL_EXT_samplerless_texture_functions : enable const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f)); layout(set = 0, binding = 0) uniform texture2D img; #ifdef INVERTY layout(location = 0) out flat ivec2 vTextureSize; #endif void main() { #ifdef INVERTY vTextureSize = textureSize(img, 0); #endif gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" #extension GL_EXT_samplerless_texture_functions : enable layout(set = 0, binding = 0) uniform texture2D img; #ifdef INVERTY layout(location = 0) in flat ivec2 vTextureSize; #endif layout(location = 0) out vec4 outColor; void main() { #ifdef INVERTY vec4 color = texelFetch(img, ivec2(gl_FragCoord.x, vTextureSize.y - gl_FragCoord.y), 0); #else vec4 color = texelFetch(img, ivec2(gl_FragCoord.xy), 0); #endif #ifdef PREMULTIPLYALPHA color.rgb *= color.a; #endif outColor = color; } "},{vertex:" const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f)); void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } ",fragment:" layout(set = 0, binding = 0) uniform Uniforms { uniform vec4 color; }; layout(location = 0) out vec4 outColor; void main() { outColor = color; } "}],lg=function(){function a(a,b,c,d){this._pipelines={},this._compiledShaders=[],this._deferredReleaseTextures=[],this._device=a,this._glslang=b,this._tintWASM=c,this._bufferManager=d,this._mipmapSampler=a.createSampler({minFilter:Af.Linear}),this._getPipeline(Y.RGBA8Unorm)}return a.ComputeNumMipmapLevels=function(a,b){return H.a.ILog2(Math.max(a,b))+1},a.prototype._getPipeline=function(a,b,c){void 0===b&&(b=fg.MipMap);var d=b===fg.MipMap?1:b===fg.InvertYPremultiplyAlpha?((c.invertY?1:0)<<1)+((c.premultiplyAlpha?1:0)<<2):b===fg.Clear?8:0;this._pipelines[a]||(this._pipelines[a]=[]);var e=this._pipelines[a][d];if(!e){var f="#version 450 ";b===fg.InvertYPremultiplyAlpha&&(c.invertY&&(f+="#define INVERTY "),c.premultiplyAlpha&&(f+="#define PREMULTIPLYALPHA "));c=this._compiledShaders[d];if(!c){var g=this._glslang.compileGLSL(f+kg[b].vertex,"vertex");f=this._glslang.compileGLSL(f+kg[b].fragment,"fragment");this._tintWASM&&(g=this._tintWASM.convertSpirV2WGSL(g),f=this._tintWASM.convertSpirV2WGSL(f));b=this._device.createShaderModule({code:g});g=this._device.createShaderModule({code:f});c=this._compiledShaders[d]=[b,g]}e=this._pipelines[a][d]=this._device.createRenderPipeline({vertex:{module:c[0],entryPoint:"main"},fragment:{module:c[1],entryPoint:"main",targets:[{format:a}]},primitive:{topology:If.TriangleStrip,stripIndexFormat:Pf.Uint16}})}return e},a._GetTextureTypeFromFormat=function(a){switch(a){case Y.R8Unorm:case Y.R8Snorm:case Y.R8Uint:case Y.R8Sint:case Y.RG8Unorm:case Y.RG8Snorm:case Y.RG8Uint:case Y.RG8Sint:case Y.RGBA8Unorm:case Y.RGBA8UnormSRGB:case Y.RGBA8Snorm:case Y.RGBA8Uint:case Y.RGBA8Sint:case Y.BGRA8Unorm:case Y.BGRA8UnormSRGB:case Y.RGB10A2Unorm:case Y.RGB9E5UFloat:case Y.RG11B10UFloat:case Y.Depth24UnormStencil8:case Y.Depth32FloatStencil8:case Y.BC7RGBAUnorm:case Y.BC7RGBAUnormSRGB:case Y.BC6HRGBUFloat:case Y.BC6HRGBFloat:case Y.BC5RGUnorm:case Y.BC5RGSnorm:case Y.BC3RGBAUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC2RGBAUnormSRGB:case Y.BC4RUnorm:case Y.BC4RSnorm:case Y.BC1RGBAUnorm:case Y.BC1RGBAUnormSRGB:return r.a.TEXTURETYPE_UNSIGNED_BYTE;case Y.R16Uint:case Y.R16Sint:case Y.RG16Uint:case Y.RG16Sint:case Y.RGBA16Uint:case Y.RGBA16Sint:case Y.Depth16Unorm:return r.a.TEXTURETYPE_UNSIGNED_SHORT;case Y.R16Float:case Y.RG16Float:case Y.RGBA16Float:return r.a.TEXTURETYPE_HALF_FLOAT;case Y.R32Uint:case Y.R32Sint:case Y.RG32Uint:case Y.RG32Sint:case Y.RGBA32Uint:case Y.RGBA32Sint:return r.a.TEXTURETYPE_UNSIGNED_INTEGER;case Y.R32Float:case Y.RG32Float:case Y.RGBA32Float:case Y.Depth32Float:return r.a.TEXTURETYPE_FLOAT;case Y.Stencil8:throw"No fixed size for Stencil8 format!";case Y.Depth24Plus:throw"No fixed size for Depth24Plus format!";case Y.Depth24PlusStencil8:throw"No fixed size for Depth24PlusStencil8 format!"}return r.a.TEXTURETYPE_UNSIGNED_BYTE},a._GetBlockInformationFromFormat=function(a){switch(a){case Y.R8Unorm:case Y.R8Snorm:case Y.R8Uint:case Y.R8Sint:return{width:1,height:1,length:1};case Y.R16Uint:case Y.R16Sint:case Y.R16Float:case Y.RG8Unorm:case Y.RG8Snorm:case Y.RG8Uint:case Y.RG8Sint:return{width:1,height:1,length:2};case Y.R32Uint:case Y.R32Sint:case Y.R32Float:case Y.RG16Uint:case Y.RG16Sint:case Y.RG16Float:case Y.RGBA8Unorm:case Y.RGBA8UnormSRGB:case Y.RGBA8Snorm:case Y.RGBA8Uint:case Y.RGBA8Sint:case Y.BGRA8Unorm:case Y.BGRA8UnormSRGB:case Y.RGB9E5UFloat:case Y.RGB10A2Unorm:case Y.RG11B10UFloat:return{width:1,height:1,length:4};case Y.RG32Uint:case Y.RG32Sint:case Y.RG32Float:case Y.RGBA16Uint:case Y.RGBA16Sint:case Y.RGBA16Float:return{width:1,height:1,length:8};case Y.RGBA32Uint:case Y.RGBA32Sint:case Y.RGBA32Float:return{width:1,height:1,length:16};case Y.Stencil8:throw"No fixed size for Stencil8 format!";case Y.Depth16Unorm:return{width:1,height:1,length:2};case Y.Depth24Plus:throw"No fixed size for Depth24Plus format!";case Y.Depth24PlusStencil8:throw"No fixed size for Depth24PlusStencil8 format!";case Y.Depth32Float:case Y.Depth24UnormStencil8:return{width:1,height:1,length:4};case Y.Depth32FloatStencil8:return{width:1,height:1,length:5};case Y.BC7RGBAUnorm:case Y.BC7RGBAUnormSRGB:case Y.BC6HRGBUFloat:case Y.BC6HRGBFloat:case Y.BC5RGUnorm:case Y.BC5RGSnorm:case Y.BC3RGBAUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC2RGBAUnormSRGB:return{width:4,height:4,length:16};case Y.BC4RUnorm:case Y.BC4RSnorm:case Y.BC1RGBAUnorm:case Y.BC1RGBAUnormSRGB:return{width:4,height:4,length:8}}return{width:1,height:1,length:4}},a._IsHardwareTexture=function(a){return!!a.release},a._IsInternalTexture=function(a){return!!a.dispose},a.GetCompareFunction=function(a){switch(a){case r.a.ALWAYS:return Bf.Always;case r.a.EQUAL:return Bf.Equal;case r.a.GREATER:return Bf.Greater;case r.a.GEQUAL:return Bf.GreaterEqual;case r.a.LESS:return Bf.Less;case r.a.LEQUAL:return Bf.LessEqual;case r.a.NEVER:return Bf.Never;case r.a.NOTEQUAL:return Bf.NotEqual;default:return Bf.Less}},a.IsImageBitmap=function(a){return void 0!==a.close},a.IsImageBitmapArray=function(a){return Array.isArray(a)&&void 0!==a[0].close},a.prototype.setCommandEncoder=function(a){this._commandEncoderForCreation=a},a.IsCompressedFormat=function(a){switch(a){case Y.BC7RGBAUnormSRGB:case Y.BC7RGBAUnorm:case Y.BC6HRGBFloat:case Y.BC6HRGBUFloat:case Y.BC5RGSnorm:case Y.BC5RGUnorm:case Y.BC4RSnorm:case Y.BC4RUnorm:case Y.BC3RGBAUnormSRGB:case Y.BC3RGBAUnorm:case Y.BC2RGBAUnormSRGB:case Y.BC2RGBAUnorm:case Y.BC1RGBAUnormSRGB:case Y.BC1RGBAUnorm:return!0}return!1},a.GetWebGPUTextureFormat=function(a,b,c){switch(void 0===c&&(c=!1),b){case r.a.TEXTUREFORMAT_DEPTH16:return Y.Depth16Unorm;case r.a.TEXTUREFORMAT_DEPTH24_STENCIL8:return Y.Depth24PlusStencil8;case r.a.TEXTUREFORMAT_DEPTH32_FLOAT:return Y.Depth32Float;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:return c?Y.BC7RGBAUnormSRGB:Y.BC7RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:return Y.BC6HRGBUFloat;case r.a.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:return Y.BC6HRGBFloat;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:return c?Y.BC3RGBAUnormSRGB:Y.BC3RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:return c?Y.BC2RGBAUnormSRGB:Y.BC2RGBAUnorm;case r.a.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:return c?Y.BC1RGBAUnormSRGB:Y.BC1RGBAUnorm}switch(a){case r.a.TEXTURETYPE_BYTE:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R8Snorm;case r.a.TEXTUREFORMAT_RG:return Y.RG8Snorm;case r.a.TEXTUREFORMAT_RGB:throw"RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R8Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG8Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:return Y.RGBA8Sint;default:return Y.RGBA8Snorm}case r.a.TEXTURETYPE_UNSIGNED_BYTE:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R8Unorm;case r.a.TEXTUREFORMAT_RG:return Y.RG8Unorm;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:return c?Y.RGBA8UnormSRGB:Y.RGBA8Unorm;case r.a.TEXTUREFORMAT_BGRA:return c?Y.BGRA8UnormSRGB:Y.BGRA8Unorm;case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R8Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG8Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:return Y.RGBA8Uint;case r.a.TEXTUREFORMAT_ALPHA:throw"TEXTUREFORMAT_ALPHA format not supported in WebGPU";case r.a.TEXTUREFORMAT_LUMINANCE:throw"TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";case r.a.TEXTUREFORMAT_LUMINANCE_ALPHA:throw"TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";default:return Y.RGBA8Unorm}case r.a.TEXTURETYPE_SHORT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R16Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG16Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA16Sint}case r.a.TEXTURETYPE_UNSIGNED_SHORT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R16Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG16Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA16Uint}case r.a.TEXTURETYPE_INT:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R32Sint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG32Sint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA32Sint}case r.a.TEXTURETYPE_UNSIGNED_INTEGER:switch(b){case r.a.TEXTUREFORMAT_RED_INTEGER:return Y.R32Uint;case r.a.TEXTUREFORMAT_RG_INTEGER:return Y.RG32Uint;case r.a.TEXTUREFORMAT_RGB_INTEGER:throw"TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA_INTEGER:default:return Y.RGBA32Uint}case r.a.TEXTURETYPE_FLOAT:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R32Float;case r.a.TEXTUREFORMAT_RG:return Y.RG32Float;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:default:return Y.RGBA32Float}case r.a.TEXTURETYPE_HALF_FLOAT:switch(b){case r.a.TEXTUREFORMAT_RED:return Y.R16Float;case r.a.TEXTUREFORMAT_RG:return Y.RG16Float;case r.a.TEXTUREFORMAT_RGB:throw"TEXTUREFORMAT_RGB format not supported in WebGPU";case r.a.TEXTUREFORMAT_RGBA:default:return Y.RGBA16Float}case r.a.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:throw"TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:throw"TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:throw"TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:throw"TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:throw"TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";case r.a.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:switch(b){case r.a.TEXTUREFORMAT_RGBA:return Y.RGB10A2Unorm;case r.a.TEXTUREFORMAT_RGBA_INTEGER:throw"TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";default:return Y.RGB10A2Unorm}}return c?Y.RGBA8UnormSRGB:Y.RGBA8Unorm},a.prototype.invertYPreMultiplyAlpha=function(a,b,c,d,e,f,g,h,i,j){var k,l;void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=0),void 0===h&&(h=0),void 0===i&&(i=1);var m=void 0===j,n=this._getPipeline(d,fg.InvertYPremultiplyAlpha,{invertY:e,premultiplyAlpha:f}),r=n.getBindGroupLayout(0);m&&(j=this._device.createCommandEncoder({})),null===(l=(k=j).pushDebugGroup)||void 0===l||l.call(k,"internal process texture - invertY="+e+" premultiplyAlpha="+f);l=this.createTexture({width:b,height:c,layers:1},!1,!1,!1,!1,!1,d,1,j,wf.CopySrc|wf.RenderAttachment|wf.TextureBinding);k=j.beginRenderPass({colorAttachments:[{view:l.createView({format:d,dimension:xf.E2d,baseMipLevel:0,mipLevelCount:1,arrayLayerCount:1,baseArrayLayer:0}),loadValue:Sf.Load,storeOp:Tf.Store}]});e=this._device.createBindGroup({layout:r,entries:[{binding:0,resource:a.createView({format:d,dimension:xf.E2d,baseMipLevel:h,mipLevelCount:1,arrayLayerCount:i,baseArrayLayer:Math.max(g,0)})}]});k.setPipeline(n),k.setBindGroup(0,e),k.draw(4,1,0,0),k.endPass(),j.copyTextureToTexture({texture:l},{texture:a,mipLevel:h,origin:{x:0,y:0,z:Math.max(g,0)}},{width:b,height:c,depthOrArrayLayers:1}),this._deferredReleaseTextures.push([l,null]),null===(r=(f=j).popDebugGroup)||void 0===r||r.call(f),m&&(this._device.queue.submit([j.finish()]),j=null)},a.prototype.copyWithInvertY=function(a,b,c,d){var e,f,g=void 0===d;b=this._getPipeline(b,fg.InvertYPremultiplyAlpha,{invertY:!0,premultiplyAlpha:!1});var h=b.getBindGroupLayout(0);g&&(d=this._device.createCommandEncoder({})),null===(f=(e=d).pushDebugGroup)||void 0===f||f.call(e,"internal copy texture with invertY");f=d.beginRenderPass(c);e=this._device.createBindGroup({layout:h,entries:[{binding:0,resource:a}]});f.setPipeline(b),f.setBindGroup(0,e),f.draw(4,1,0,0),f.endPass(),null===(h=(c=d).popDebugGroup)||void 0===h||h.call(c),g&&(this._device.queue.submit([d.finish()]),d=null)},a.prototype.createTexture=function(b,c,d,e,f,g,h,i,j,k,l){void 0===c&&(c=!1),void 0===d&&(d=!1),void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=!1),void 0===h&&(h=Y.RGBA8Unorm),void 0===i&&(i=1),void 0===k&&(k=-1),void 0===l&&(l=0),i>1&&(i=4);var q=b.layers||1,r={width:b.width,height:b.height,depthOrArrayLayers:q},s=a.IsCompressedFormat(h),t=c?a.ComputeNumMipmapLevels(b.width,b.height):1;k=k>=0?k:wf.CopySrc|wf.CopyDst|wf.TextureBinding;l|=c&&!s?wf.CopySrc|wf.RenderAttachment:0,s||(l|=wf.RenderAttachment|wf.CopyDst);s=this._device.createTexture({size:r,dimension:g?vf.E3d:vf.E2d,format:h,usage:k|l,sampleCount:i,mipLevelCount:t});return a.IsImageBitmap(b)&&(this.updateTexture(b,s,b.width,b.height,q,h,0,0,e,f,0,0,j),c&&d&&this.generateMipmaps(s,h,t,0,j)),s},a.prototype.createCubeTexture=function(b,c,d,e,f,g,h,i,j,k){void 0===c&&(c=!1),void 0===d&&(d=!1),void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===g&&(g=Y.RGBA8Unorm),void 0===h&&(h=1),void 0===j&&(j=-1),void 0===k&&(k=0),h>1&&(h=4);var l=a.IsImageBitmapArray(b)?b[0].width:b.width,p=a.IsImageBitmapArray(b)?b[0].height:b.height,q=a.IsCompressedFormat(g),r=c?a.ComputeNumMipmapLevels(l,p):1;j=j>=0?j:wf.CopySrc|wf.CopyDst|wf.TextureBinding;k|=c&&!q?wf.CopySrc|wf.RenderAttachment:0,q||(k|=wf.RenderAttachment|wf.CopyDst);q=this._device.createTexture({size:{width:l,height:p,depthOrArrayLayers:6},dimension:vf.E2d,format:g,usage:j|k,sampleCount:h,mipLevelCount:r});return a.IsImageBitmapArray(b)&&(this.updateCubeTextures(b,q,l,p,g,e,f,0,0,i),c&&d&&this.generateCubeMipmaps(q,g,r,i)),q},a.prototype.generateCubeMipmaps=function(a,b,c,d){var e,f,g=void 0===d;g&&(d=this._device.createCommandEncoder({})),null===(f=(e=d).pushDebugGroup)||void 0===f||f.call(e,"create cube mipmaps - "+c+" levels");for(f=0;f<6;++f)this.generateMipmaps(a,b,c,f,d);null===(a=(e=d).popDebugGroup)||void 0===a||a.call(e),g&&(this._device.queue.submit([d.finish()]),d=null)},a.prototype.generateMipmaps=function(a,b,c,d,e){var f,g;void 0===d&&(d=0);var h=void 0===e,i=this._getPipeline(b),j=i.getBindGroupLayout(0);h&&(e=this._device.createCommandEncoder({})),null===(g=(f=e).pushDebugGroup)||void 0===g||g.call(f,"create mipmaps for face #"+d+" - "+c+" levels");for(g=1;g15728640;)this._device.queue.writeBuffer(f,b+i,c.buffer,a+i,15728640),i+=15728640;this._device.queue.writeBuffer(f,b+i,c.buffer,a+i,e-i)},a.prototype._GetHalfFloatAsFloatRGBAArrayBuffer=function(a,b,c){c||(c=new Float32Array(a));for(b=new Uint16Array(b);a--;)c[a]=Oe(b[a]);return c},a.prototype.readDataFromBuffer=function(a,b,c,d,e,f,g,h,i,j,k){var l=this;void 0===g&&(g=r.a.TEXTURETYPE_UNSIGNED_BYTE),void 0===h&&(h=0),void 0===i&&(i=null),void 0===j&&(j=!0),void 0===k&&(k=!1);var p=g===r.a.TEXTURETYPE_FLOAT?2:g===r.a.TEXTURETYPE_HALF_FLOAT?1:0;return new Promise(function(c,m){a.mapAsync(uf.Read,h,b).then(function(){var m=a.getMappedRange(h,b),u=i;if(k)u=null===u?Object(xe.a)(g,b,!0,m):Object(xe.a)(g,u.buffer,void 0,m);else if(null===u)switch(p){case 0:(u=new Uint8Array(b)).set(new Uint8Array(m));break;case 1:u=l._GetHalfFloatAsFloatRGBAArrayBuffer(b/2,m);break;case 2:(u=new Float32Array(b/4)).set(new Float32Array(m))}else switch(p){case 0:(u=new Uint8Array(u.buffer)).set(new Uint8Array(m));break;case 1:u=l._GetHalfFloatAsFloatRGBAArrayBuffer(b/2,m,i);break;case 2:(u=new Float32Array(u.buffer)).set(new Float32Array(m))}if(e!==f){1!==p||k||(e*=2,f*=2);for(var m=new Uint8Array(u.buffer),v=e,w=0,x=1;x1&&(0!==f||0!==g)?{magFilter:Af.Linear,minFilter:Af.Linear,mipmapFilter:Af.Linear,anisotropyEnabled:!0}:{magFilter:c,minFilter:d,mipmapFilter:e,lodMinClamp:f,lodMaxClamp:g}},a._GetWrappingMode=function(a){switch(a){case r.a.TEXTURE_WRAP_ADDRESSMODE:return zf.Repeat;case r.a.TEXTURE_CLAMP_ADDRESSMODE:return zf.ClampToEdge;case r.a.TEXTURE_MIRROR_ADDRESSMODE:return zf.MirrorRepeat}return zf.Repeat},a._GetSamplerWrappingDescriptor=function(a){return{addressModeU:this._GetWrappingMode(a._cachedWrapU),addressModeV:this._GetWrappingMode(a._cachedWrapV),addressModeW:this._GetWrappingMode(a._cachedWrapR)}},a._GetSamplerDescriptor=function(a){var b,c=a.useMipMaps&&null!==(b=a._cachedAnisotropicFilteringLevel)&&void 0!==b?b:1,d=this._GetSamplerFilterDescriptor(a,c);return Object(l.a)(Object(l.a)(Object(l.a)({},d),this._GetSamplerWrappingDescriptor(a)),{compare:a._comparisonFunction?lg.GetCompareFunction(a._comparisonFunction):void 0,maxAnisotropy:d.anisotropyEnabled?c:1})},a.prototype.getSampler=function(b,c,d){if(void 0===c&&(c=!1),void 0===d&&(d=0),this.disabled)return this._device.createSampler(a._GetSamplerDescriptor(b));c?d=0:0===d&&(d=a.GetSamplerHashCode(b));var e=c?void 0:this._samplers[d];return e||(e=this._device.createSampler(a._GetSamplerDescriptor(b)),c||(this._samplers[d]=e)),e},a}();!function(a){a[a.StencilReadMask=0]="StencilReadMask",a[a.StencilWriteMask=1]="StencilWriteMask",a[a.DepthBias=2]="DepthBias",a[a.DepthBiasSlopeScale=3]="DepthBiasSlopeScale",a[a.MRTAttachments1=4]="MRTAttachments1",a[a.MRTAttachments2=5]="MRTAttachments2",a[a.DepthStencilState=6]="DepthStencilState",a[a.RasterizationState=7]="RasterizationState",a[a.ColorStates=8]="ColorStates",a[a.ShaderStage=9]="ShaderStage",a[a.TextureStage=10]="TextureStage",a[a.VertexState=11]="VertexState",a[a.NumStates=12]="NumStates"}(jg||(jg={}));var tg={"":0,r8unorm:1,r8snorm:2,r8uint:3,r8sint:4,r16uint:5,r16sint:6,r16float:7,rg8unorm:8,rg8snorm:9,rg8uint:10,rg8sint:11,r32uint:12,r32sint:13,r32float:14,rg16uint:15,rg16sint:16,rg16float:17,rgba8unorm:18,"rgba8unorm-srgb":19,rgba8snorm:20,rgba8uint:21,rgba8sint:22,bgra8unorm:23,"bgra8unorm-srgb":24,rgb9e5ufloat:25,rgb10a2unorm:26,rg11b10ufloat:27,rg32uint:28,rg32sint:29,rg32float:30,rgba16uint:31,rgba16sint:32,rgba16float:33,rgba32uint:34,rgba32sint:35,rgba32float:36,stencil8:37,depth16unorm:38,depth24plus:39,"depth24plus-stencil8":40,depth32float:41,"bc1-rgba-unorm":42,"bc1-rgba-unorm-srgb":43,"bc2-rgba-unorm":44,"bc2-rgba-unorm-srgb":45,"bc3-rgba-unorm":46,"bc3-rgba-unorm-srgb":47,"bc4-r-unorm":48,"bc4-r-snorm":49,"bc5-rg-unorm":50,"bc5-rg-snorm":51,"bc6h-rgb-ufloat":52,"bc6h-rgb-float":53,"bc7-rgba-unorm":54,"bc7-rgba-unorm-srgb":55,"depth24unorm-stencil8":56,"depth32float-stencil8":57},ug={0:1,1:2,768:3,769:4,770:5,771:6,772:7,773:8,774:9,775:10,776:11,32769:12,32770:13,32771:12,32772:13},vg={0:0,7680:1,7681:2,7682:3,7683:4,5386:5,34055:6,34056:7},wg=function(){function a(a,b,c){this._device=a,this._useTextureStage=c,this._states=new Array(30),this._statesLength=0,this._stateDirtyLowestIndex=0,this._emptyVertexBuffer=b,this._mrtFormats=[],this._parameter={token:void 0,pipeline:null},this.disabled=!1,this.vertexBuffers=[],this._kMaxVertexBufferStride=2048,this.reset()}return a.prototype.reset=function(){this._isDirty=!0,this.vertexBuffers.length=0,this.setAlphaToCoverage(!1),this.resetDepthCullingState(),this.setClampDepth(!1),this.setDepthBias(0),this._webgpuColorFormat=[Y.BGRA8Unorm],this.setColorFormat(Y.BGRA8Unorm),this.setMRTAttachments([],[]),this.setAlphaBlendEnabled(!1),this.setAlphaBlendFactors([null,null,null,null],[null,null]),this.setWriteMask(15),this.setDepthStencilFormat(Y.Depth24PlusStencil8),this.setStencilEnabled(!1),this.resetStencilState(),this.setBuffers(null,null,null),this._setTextureState(0)},Object.defineProperty(a.prototype,"colorFormats",{get:function(){return this._mrtAttachments1>0?this._mrtFormats:this._webgpuColorFormat},enumerable:!1,configurable:!0}),a.prototype.getRenderPipeline=function(b,c,d,e){if(void 0===e&&(e=0),this.disabled){var f=a._GetTopology(b);return this._setVertexState(c),this._parameter.pipeline=this._createRenderPipeline(c,f,d),a.NumCacheMiss++,a._NumPipelineCreationCurrentFrame++,this._parameter.pipeline}if(this._setShaderStage(c.uniqueId),this._setRasterizationState(b,d),this._setColorStates(),this._setDepthStencilState(),this._setVertexState(c),this._setTextureState(e),this.lastStateDirtyLowestIndex=this._stateDirtyLowestIndex,!this._isDirty&&this._parameter.pipeline)return this._stateDirtyLowestIndex=this._statesLength,a.NumCacheHitWithoutHash++,this._parameter.pipeline;if(this._getRenderPipeline(this._parameter),this._isDirty=!1,this._stateDirtyLowestIndex=this._statesLength,this._parameter.pipeline)return a.NumCacheHitWithHash++,this._parameter.pipeline;f=a._GetTopology(b);return this._parameter.pipeline=this._createRenderPipeline(c,f,d),this._setRenderPipeline(this._parameter),a.NumCacheMiss++,a._NumPipelineCreationCurrentFrame++,this._parameter.pipeline},a.prototype.endFrame=function(){a.NumPipelineCreationLastFrame=a._NumPipelineCreationCurrentFrame,a._NumPipelineCreationCurrentFrame=0},a.prototype.setAlphaToCoverage=function(a){this._alphaToCoverageEnabled=a},a.prototype.setFrontFace=function(a){this._frontFace=a},a.prototype.setCullEnabled=function(a){this._cullEnabled=a},a.prototype.setCullFace=function(a){this._cullFace=a},a.prototype.setClampDepth=function(a){this._clampDepth=a},a.prototype.resetDepthCullingState=function(){this.setDepthCullingState(!1,2,1,0,0,!0,!0,r.a.ALWAYS)},a.prototype.setDepthCullingState=function(a,b,c,d,e,f,g,h){this._depthWriteEnabled=g,this._depthTestEnabled=f,this._depthCompare=(null!=h?h:r.a.ALWAYS)-512,this._cullFace=c,this._cullEnabled=a,this._frontFace=b,this.setDepthBiasSlopeScale(d),this.setDepthBias(e)},a.prototype.setDepthBias=function(a){this._depthBias!==a&&(this._depthBias=a,this._states[jg.DepthBias]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthBias))},a.prototype.setDepthBiasSlopeScale=function(a){this._depthBiasSlopeScale!==a&&(this._depthBiasSlopeScale=a,this._states[jg.DepthBiasSlopeScale]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthBiasSlopeScale))},a.prototype.setColorFormat=function(a){this._webgpuColorFormat[0]=a,this._colorFormat=tg[a]},a.prototype.setMRTAttachments=function(a,b){if(a.length>10)throw"Can"t handle more than 10 attachments for a MRT in cache render pipeline!";this.mrtAttachments=a,this.mrtTextureArray=b;for(var c=[0,0],d=0,e=0,f=0,g=0;g=32&&(e=0,d++)}}this._mrtFormats.length=f,this._mrtAttachments1===c[0]&&this._mrtAttachments2===c[1]||(this._mrtAttachments1=c[0],this._mrtAttachments2=c[1],this._states[jg.MRTAttachments1]=c[0],this._states[jg.MRTAttachments2]=c[1],this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.MRTAttachments1))},a.prototype.setAlphaBlendEnabled=function(a){this._alphaBlendEnabled=a},a.prototype.setAlphaBlendFactors=function(a,b){this._alphaBlendFuncParams=a,this._alphaBlendEqParams=b},a.prototype.setWriteMask=function(a){this._writeMask=a},a.prototype.setDepthStencilFormat=function(a){this._webgpuDepthStencilFormat=a,this._depthStencilFormat=void 0===a?0:tg[a]},a.prototype.setDepthTestEnabled=function(a){this._depthTestEnabled=a},a.prototype.setDepthWriteEnabled=function(a){this._depthWriteEnabled=a},a.prototype.setDepthCompare=function(a){this._depthCompare=(null!=a?a:r.a.ALWAYS)-512},a.prototype.setStencilEnabled=function(a){this._stencilEnabled=a},a.prototype.setStencilCompare=function(a){this._stencilFrontCompare=(null!=a?a:r.a.ALWAYS)-512},a.prototype.setStencilDepthFailOp=function(a){this._stencilFrontDepthFailOp=null===a?1:vg[a]},a.prototype.setStencilPassOp=function(a){this._stencilFrontPassOp=null===a?2:vg[a]},a.prototype.setStencilFailOp=function(a){this._stencilFrontFailOp=null===a?1:vg[a]},a.prototype.setStencilReadMask=function(a){this._stencilReadMask!==a&&(this._stencilReadMask=a,this._states[jg.StencilReadMask]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.StencilReadMask))},a.prototype.setStencilWriteMask=function(a){this._stencilWriteMask!==a&&(this._stencilWriteMask=a,this._states[jg.StencilWriteMask]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.StencilWriteMask))},a.prototype.resetStencilState=function(){this.setStencilState(!1,r.a.ALWAYS,r.a.KEEP,r.a.REPLACE,r.a.KEEP,255,255)},a.prototype.setStencilState=function(a,b,c,d,e,f,g){this._stencilEnabled=a,this._stencilFrontCompare=(null!=b?b:r.a.ALWAYS)-512,this._stencilFrontDepthFailOp=null===c?1:vg[c],this._stencilFrontPassOp=null===d?2:vg[d],this._stencilFrontFailOp=null===e?1:vg[e],this.setStencilReadMask(f),this.setStencilWriteMask(g)},a.prototype.setBuffers=function(a,b,c){this._vertexBuffers=a,this._overrideVertexBuffers=c,this._indexBuffer=b},a._GetTopology=function(a){switch(a){case r.a.MATERIAL_TriangleFillMode:return If.TriangleList;case r.a.MATERIAL_PointFillMode:return If.PointList;case r.a.MATERIAL_WireFrameFillMode:return If.LineList;case r.a.MATERIAL_PointListDrawMode:return If.PointList;case r.a.MATERIAL_LineListDrawMode:return If.LineList;case r.a.MATERIAL_LineLoopDrawMode:throw"LineLoop is an unsupported fillmode in WebGPU";case r.a.MATERIAL_LineStripDrawMode:return If.LineStrip;case r.a.MATERIAL_TriangleStripDrawMode:return If.TriangleStrip;case r.a.MATERIAL_TriangleFanDrawMode:throw"TriangleFan is an unsupported fillmode in WebGPU";default:return If.TriangleList}},a._GetAphaBlendOperation=function(a){switch(a){case 32774:return Nf.Add;case 32778:return Nf.Subtract;case 32779:return Nf.ReverseSubtract;case 32775:return Nf.Min;case 32776:return Nf.Max;default:return Nf.Add}},a._GetAphaBlendFactor=function(a){switch(a){case 0:return Mf.Zero;case 1:return Mf.One;case 768:return Mf.Src;case 769:return Mf.OneMinusSrc;case 770:return Mf.SrcAlpha;case 771:return Mf.OneMinusSrcAlpha;case 772:return Mf.DstAlpha;case 773:return Mf.OneMinusDstAlpha;case 774:return Mf.Dst;case 775:return Mf.OneMinusDst;case 776:return Mf.SrcAlphaSaturated;case 32769:return Mf.Constant;case 32770:return Mf.OneMinusConstant;case 32771:return Mf.Constant;case 32772:return Mf.OneMinusConstant;default:return Mf.One}},a._GetCompareFunction=function(a){switch(a){case 0:return Bf.Never;case 1:return Bf.Less;case 2:return Bf.Equal;case 3:return Bf.LessEqual;case 4:return Bf.Greater;case 5:return Bf.NotEqual;case 6:return Bf.GreaterEqual;case 7:return Bf.Always}return Bf.Never},a._GetStencilOpFunction=function(a){switch(a){case 0:return Of.Zero;case 1:return Of.Keep;case 2:return Of.Replace;case 3:return Of.IncrementClamp;case 4:return Of.DecrementClamp;case 5:return Of.Invert;case 6:return Of.IncrementWrap;case 7:return Of.DecrementWrap}return Of.Keep},a._GetVertexInputDescriptorFormat=function(a){var b=a.type,c=a.normalized,d=a.getSize();switch(b){case W.b.BYTE:switch(d){case 1:case 2:return c?Qf.Snorm8x2:Qf.Sint8x2;case 3:case 4:return c?Qf.Snorm8x4:Qf.Sint8x4}break;case W.b.UNSIGNED_BYTE:switch(d){case 1:case 2:return c?Qf.Unorm8x2:Qf.Uint8x2;case 3:case 4:return c?Qf.Unorm8x4:Qf.Uint8x4}break;case W.b.SHORT:switch(d){case 1:case 2:return c?Qf.Snorm16x2:Qf.Sint16x2;case 3:case 4:return c?Qf.Snorm16x4:Qf.Sint16x4}break;case W.b.UNSIGNED_SHORT:switch(d){case 1:case 2:return c?Qf.Unorm16x2:Qf.Uint16x2;case 3:case 4:return c?Qf.Unorm16x4:Qf.Uint16x4}break;case W.b.INT:switch(d){case 1:return Qf.Sint32;case 2:return Qf.Sint32x2;case 3:return Qf.Sint32x3;case 4:return Qf.Sint32x4}break;case W.b.UNSIGNED_INT:switch(d){case 1:return Qf.Uint32;case 2:return Qf.Uint32x2;case 3:return Qf.Uint32x3;case 4:return Qf.Uint32x4}break;case W.b.FLOAT:switch(d){case 1:return Qf.Float32;case 2:return Qf.Float32x2;case 3:return Qf.Float32x3;case 4:return Qf.Float32x4}}throw new Error("Invalid Format ""+a.getKind()+"" - type="+b+", normalized="+c+", size="+d)},a.prototype._getAphaBlendState=function(){return this._alphaBlendEnabled?{srcFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[2]),dstFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[3]),operation:a._GetAphaBlendOperation(this._alphaBlendEqParams[1])}:null},a.prototype._getColorBlendState=function(){return this._alphaBlendEnabled?{srcFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[0]),dstFactor:a._GetAphaBlendFactor(this._alphaBlendFuncParams[1]),operation:a._GetAphaBlendOperation(this._alphaBlendEqParams[0])}:null},a.prototype._setShaderStage=function(a){this._shaderId!==a&&(this._shaderId=a,this._states[jg.ShaderStage]=a,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.ShaderStage))},a.prototype._setRasterizationState=function(a,b){a=this._frontFace-1+((this._cullEnabled?this._cullFace:0)<<1)+((this._clampDepth?1:0)<<3)+((this._alphaToCoverageEnabled?1:0)<<4)+(a<<5)+(b<<8);this._rasterizationState!==a&&(this._rasterizationState=a,this._states[jg.RasterizationState]=this._rasterizationState,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.RasterizationState))},a.prototype._setColorStates=function(){var a=((this._writeMask?1:0)<<22)+(this._colorFormat<<23)+((this._depthWriteEnabled?1:0)<<29);this._alphaBlendEnabled&&(a+=((null===this._alphaBlendFuncParams[0]?2:ug[this._alphaBlendFuncParams[0]])<<0)+((null===this._alphaBlendFuncParams[1]?2:ug[this._alphaBlendFuncParams[1]])<<4)+((null===this._alphaBlendFuncParams[2]?2:ug[this._alphaBlendFuncParams[2]])<<8)+((null===this._alphaBlendFuncParams[3]?2:ug[this._alphaBlendFuncParams[3]])<<12)+((null===this._alphaBlendEqParams[0]?1:this._alphaBlendEqParams[0]-32773)<<16)+((null===this._alphaBlendEqParams[1]?1:this._alphaBlendEqParams[1]-32773)<<19)),a!==this._colorStates&&(this._colorStates=a,this._states[jg.ColorStates]=this._colorStates,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.ColorStates))},a.prototype._setDepthStencilState=function(){var a=this._stencilEnabled?this._stencilFrontCompare+(this._stencilFrontDepthFailOp<<3)+(this._stencilFrontPassOp<<6)+(this._stencilFrontFailOp<<9):591;a=this._depthStencilFormat+((this._depthTestEnabled?this._depthCompare:7)<<6)+(a<<10);this._depthStencilState!==a&&(this._depthStencilState=a,this._states[jg.DepthStencilState]=this._depthStencilState,this._isDirty=!0,this._stateDirtyLowestIndex=Math.min(this._stateDirtyLowestIndex,jg.DepthStencilState))},a.prototype._setVertexState=function(a){for(var b,c,d,e=this._statesLength,f=jg.VertexState,a=a._pipelineContext,g=a.shaderProcessingContext.attributeNamesFromEffect,a=a.shaderProcessingContext.attributeLocationsFromEffect,h=0,i=0;i0)for(var j=0;j=this._video.HAVE_CURRENT_DATA},a.prototype.dispose=function(){},a}(),Eg=function(){function a(){this.uniqueId=a._Counter++,this.reset()}return Object.defineProperty(a.prototype,"forceBindGroupCreation",{get:function(){return this._numExternalTextures>0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasFloatTextures",{get:function(){return this._numFloatTextures>0},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this.samplers={},this.textures={},this.buffers={},this.isDirty=!0,this._numFloatTextures=0,this._numExternalTextures=0},a.prototype.setSampler=function(a,b){var c=this.samplers[a],d=-1;c?d=c.hashCode:this.samplers[a]=c={sampler:b,hashCode:0},c.sampler=b,c.hashCode=b?sg.GetSamplerHashCode(b):0,this.isDirty||(this.isDirty=d!==c.hashCode)},a.prototype.setTexture=function(a,b){var c,d=this.textures[a],e=-1;d?e=null!==(c=null===(c=d.texture)||void 0===c?void 0:c.uniqueId)&&void 0!==c?c:-1:this.textures[a]=d={texture:b,isFloatTexture:!1,isExternalTexture:!1},d.isExternalTexture&&this._numExternalTextures--,d.isFloatTexture&&this._numFloatTextures--,b?(d.isFloatTexture=b.type===r.a.TEXTURETYPE_FLOAT,d.isExternalTexture=Dg.IsExternalTexture(b),d.isFloatTexture&&this._numFloatTextures++,d.isExternalTexture&&this._numExternalTextures++):(d.isFloatTexture=!1,d.isExternalTexture=!1),d.texture=b,this.isDirty||(this.isDirty=e!==(null!==(c=null==b?void 0:b.uniqueId)&&void 0!==c?c:-1))},a.prototype.setBuffer=function(a,b){var c;this.isDirty||(this.isDirty=(null==b?void 0:b.uniqueId)!==(null===(c=this.buffers[a])||void 0===c?void 0:c.uniqueId)),this.buffers[a]=b},a._Counter=0,a}(),Fg=function(){function a(){this.uniqueId=a._Counter++}return a._Counter=0,a}(),Gg=function(){this.values={}},Hg=function(){function a(a,b,c){this.disabled=!1,this._device=a,this._cacheSampler=b,this._engine=c}return Object.defineProperty(a,"Statistics",{get:function(){return{totalCreated:a.NumBindGroupsCreatedTotal,lastFrameCreated:a.NumBindGroupsCreatedLastFrame,lookupLastFrame:a.NumBindGroupsLookupLastFrame,noLookupLastFrame:a.NumBindGroupsNoLookupLastFrame}},enumerable:!1,configurable:!0}),a.prototype.endFrame=function(){a.NumBindGroupsCreatedLastFrame=a._NumBindGroupsCreatedCurrentFrame,a.NumBindGroupsLookupLastFrame=a._NumBindGroupsLookupCurrentFrame,a.NumBindGroupsNoLookupLastFrame=a._NumBindGroupsNoLookupCurrentFrame,a._NumBindGroupsCreatedCurrentFrame=0,a._NumBindGroupsLookupCurrentFrame=0,a._NumBindGroupsNoLookupCurrentFrame=0},a.prototype.getBindGroups=function(b,c){var d,e,f=void 0,g=a._Cache,h=this.disabled||c.forceBindGroupCreation;if(!h){if(!c.isDirty)return a._NumBindGroupsNoLookupCurrentFrame++,c.bindGroups;for(var i=0,j=b.shaderProcessingContext.bufferNames;i":b}),50);continue}i[h].resource=this._cacheSampler.getSampler(u,!1,m.hashCode)}else q.a.Error("Sampler ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50);else if(g.texture||g.storageTexture)if(m=c.textures[d]){if(this._engine.dbgSanityChecks&&null===m.texture){q.a.Error("Trying to bind a null texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(m,function(a,b){return"texture"===a?"":b}),50);continue}u=m.texture._hardwareTexture;if(this._engine.dbgSanityChecks&&(!u||!u.view)){q.a.Error("Trying to bind a null gpu texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(m,function(a,b){return"texture"===a?"":b})+", isReady="+(null===(e=m.texture)||void 0===e?void 0:e.isReady),50);continue}i[h].resource=u.view}else q.a.Error("Texture ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50);else if(g.externalTexture){if(m=c.textures[d]){if(this._engine.dbgSanityChecks&&null===m.texture){q.a.Error("Trying to bind a null external texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(m,function(a,b){return"texture"===a?"":b}),50);continue}e=m.texture.underlyingResource;if(this._engine.dbgSanityChecks&&!e){q.a.Error("Trying to bind a null gpu external texture! entry="+JSON.stringify(g)+", bindingInfo="+JSON.stringify(m,function(a,b){return"texture"===a?"":b})+", isReady="+(null===(u=m.texture)||void 0===u?void 0:u.isReady),50);continue}i[h].resource=this._device.importExternalTexture({source:e})}else q.a.Error("Texture ""+d+"" could not be bound. entry="+JSON.stringify(g)+", materialContext="+JSON.stringify(c,function(a,b){return"texture"===a||"sampler"===a?"":b}),50)}else if(g.buffer){m=c.buffers[d];if(m){u=m.underlyingResource;i[h].resource.buffer=u,i[h].resource.size=m.capacity}else q.a.Error("Can"t find buffer ""+d+"". entry="+JSON.stringify(g)+", buffers="+JSON.stringify(c.buffers),50)}}e=k[l];f[l]=this._device.createBindGroup({layout:e,entries:i})}return f},a.NumBindGroupsCreatedTotal=0,a.NumBindGroupsCreatedLastFrame=0,a.NumBindGroupsLookupLastFrame=0,a.NumBindGroupsNoLookupLastFrame=0,a._Cache=new Gg,a._NumBindGroupsCreatedCurrentFrame=0,a._NumBindGroupsLookupCurrentFrame=0,a._NumBindGroupsNoLookupCurrentFrame=0,a}();a="uniform float depthValue; const vec2 pos[4]={ vec2(-1.0,1.0), vec2(1.0,1.0), vec2(-1.0,-1.0), vec2(1.0,-1.0) }; void main(void) { gl_Position=vec4(pos[gl_VertexID],depthValue,1.0); } ";X.a.ShadersStore.clearQuadVertexShader=a;X.a.ShadersStore.clearQuadPixelShader="uniform vec4 color; void main() { gl_FragColor=color; } ";var Ig=function(){function a(a,b,c){this._bindGroups={},this._bundleCache={},this._device=a,this._engine=b,this._cacheRenderPipeline=new yg(this._device,c,!b._caps.textureFloatLinearFiltering),this._cacheRenderPipeline.setDepthTestEnabled(!1),this._cacheRenderPipeline.setStencilReadMask(255),this._effect=b.createEffect("clearQuad",[],["color","depthValue"])}return a.prototype.setDepthStencilFormat=function(a){this._depthTextureFormat=a,this._cacheRenderPipeline.setDepthStencilFormat(a)},a.prototype.setColorFormat=function(a){this._cacheRenderPipeline.setColorFormat(a)},a.prototype.setMRTAttachments=function(a,b){this._cacheRenderPipeline.setMRTAttachments(a,b)},a.prototype.clear=function(a,b,c,d,e){var f;void 0===e&&(e=1);var g=null,h=0;if(a)f=a;else{if(h=(b?b.r+256*b.g+256*b.b*256+256*b.a*256*256:0)+(c?Math.pow(2,32):0)+(d?Math.pow(2,33):0)+(this._engine.useReverseDepthBuffer?Math.pow(2,34):0)+e*Math.pow(2,35),g=this._bundleCache[h])return g;f=this._device.createRenderBundleEncoder({colorFormats:this._cacheRenderPipeline.colorFormats,depthStencilFormat:this._depthTextureFormat,sampleCount:e})}this._cacheRenderPipeline.setDepthWriteEnabled(!!c),this._cacheRenderPipeline.setStencilEnabled(!!d),this._cacheRenderPipeline.setStencilWriteMask(d?255:0),this._cacheRenderPipeline.setStencilCompare(d?r.a.ALWAYS:r.a.NEVER),this._cacheRenderPipeline.setStencilPassOp(d?r.a.REPLACE:r.a.KEEP),this._cacheRenderPipeline.setWriteMask(b?15:0);c=this._cacheRenderPipeline.getRenderPipeline(r.a.MATERIAL_TriangleStripDrawMode,this._effect,e);d=this._effect._pipelineContext;b&&this._effect.setDirectColor4("color",b),this._effect.setFloat("depthValue",this._engine.useReverseDepthBuffer?this._engine._clearReverseDepthValue:this._engine._clearDepthValue),null===(e=d.uniformBuffer)||void 0===e||e.update();e=null===(b=d.uniformBuffer)||void 0===b?void 0:b.getBuffer();b=this._bindGroups[e.uniqueId];if(!b){d=d.bindGroupLayouts;(b=this._bindGroups[e.uniqueId]=[]).push(this._device.createBindGroup({layout:d[0],entries:[]})),dg._SimplifiedKnownBindings||b.push(this._device.createBindGroup({layout:d[1],entries:[]})),b.push(this._device.createBindGroup({layout:d[dg._SimplifiedKnownBindings?1:2],entries:[{binding:0,resource:{buffer:e.underlyingResource,size:e.capacity}}]}))}f.setPipeline(c);for(d=0;d=0&&(b._gpuFrameTimeCounter.fetchNewFrame(),b._gpuFrameTimeCounter.addCount(a,!0)),b._measureDurationState=0}))},a}(),Rg=function(){function a(a,b){this._querySet=new Pg(2,Uf.Timestamp,a,b)}return a.prototype.start=function(a){a.writeTimestamp(this._querySet.querySet,0)},a.prototype.stop=function(a){return Object(l.b)(this,void 0,void 0,function(){return Object(l.e)(this,function(b){return a.writeTimestamp(this._querySet.querySet,1),[2,this._querySet.readTwoValuesAndSubtract(0)]})})},a.prototype.dispose=function(){this._querySet.dispose()},a}(),Sg=function(){function a(a,b,c,d,e){void 0===d&&(d=50),void 0===e&&(e=100),this._availableIndices=[],this._engine=a,this._device=b,this._bufferManager=c,this._frameLastBuffer=-1,this._currentTotalIndices=0,this._countIncrement=e,this._allocateNewIndices(d)}return Object.defineProperty(a.prototype,"querySet",{get:function(){return this._querySet.querySet},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasQueries",{get:function(){return this._currentTotalIndices!==this._availableIndices.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"canBeginQuery",{get:function(){switch(this._engine._getCurrentRenderPassIndex()){case 0:return void 0!==this._engine._mainRenderPassWrapper.renderPassDescriptor.occlusionQuerySet;case 1:return void 0!==this._engine._rttRenderPassWrapper.renderPassDescriptor.occlusionQuerySet}return!1},enumerable:!1,configurable:!0}),a.prototype.createQuery=function(){0===this._availableIndices.length&&this._allocateNewIndices();var a=this._availableIndices[this._availableIndices.length-1];return this._availableIndices.length--,a},a.prototype.deleteQuery=function(a){this._availableIndices[this._availableIndices.length-1]=a},a.prototype.isQueryResultAvailable=function(a){return this._retrieveQueryBuffer(),!!this._lastBuffer&&a=0||void 0,pvrtc:null,etc1:null,etc2:null,bptc:this._deviceEnabledExtensions.indexOf(sf.TextureCompressionBC)>=0||void 0,maxAnisotropy:16,uintIndices:!0,fragmentDepthSupported:!0,highPrecisionShaderSupported:!0,colorBufferFloat:!0,textureFloat:!0,textureFloatLinearFiltering:!1,textureFloatRender:!0,textureHalfFloat:!0,textureHalfFloatLinearFiltering:!0,textureHalfFloatRender:!0,textureLOD:!0,drawBuffersExtension:!0,depthTextureExtension:!0,vertexArrayObject:!1,instancedArrays:!0,timerQuery:void 0,supportOcclusionQuery:"undefined"!=typeof BigUint64Array,canUseTimestampForTimerQuery:!0,multiview:!1,oculusMultiview:!1,parallelShaderCompile:void 0,blendMinMax:!0,maxMSAASamples:4,canUseGLInstanceID:!0,canUseGLVertexID:!0,supportComputeShaders:!0,supportSRGBBuffers:!0},this._caps.parallelShaderCompile=null,this._features={forceBitmapOverHTMLImageElement:!0,supportRenderAndCopyToLodForFloatTextures:!0,supportDepthStencilTexture:!0,supportShadowSamplers:!0,uniformBufferHardCheckMatrix:!1,allowTexturePrefiltering:!0,trackUbosInFrame:!0,checkUbosContentBeforeUpload:!0,supportCSM:!0,basisNeedsPOT:!1,support3DTextures:!0,needTypeSuffixInShaderConstants:!0,supportMSAA:!0,supportSSAO2:!0,supportExtendedTextureFormats:!0,supportSwitchCaseInShader:!0,supportSyncTextureRead:!1,needsInvertingBitmap:!1,useUBOBindingCache:!1,needShaderCodeInlining:!0,_collectUbosUpdatedInFrame:!1}},b.prototype._initializeContextAndSwapChain=function(){this._context=this._canvas.getContext("webgpu"),this._configureContext(this._canvas.width,this._canvas.height),this._colorFormat=this._options.swapChainFormat,this._mainRenderPassWrapper.colorAttachmentGPUTextures=[new ig],this._mainRenderPassWrapper.colorAttachmentGPUTextures[0].format=this._colorFormat,this._invertYFinalFramebuffer&&(this._mainRenderPassCopyWrapper.colorAttachmentGPUTextures=[new ig],this._mainRenderPassCopyWrapper.colorAttachmentGPUTextures[0].format=this._colorFormat)},b.prototype._initializeMainAttachments=function(){var a,b;if(this._mainTextureExtends={width:this.getRenderWidth(),height:this.getRenderHeight(),depthOrArrayLayers:1},this._options.antialiasing){var c={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:this._mainPassSampleCount,dimension:vf.E2d,format:this._options.swapChainFormat,usage:wf.RenderAttachment};null===(a=this._mainTexture)||void 0===a||a.destroy(),this._mainTexture=this._device.createTexture(c),a=[{view:this._mainTexture.createView(),loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}]}else a=[{view:void 0,loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}];if(this._invertYFinalFramebuffer){c={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:1,dimension:vf.E2d,format:this._options.swapChainFormat,usage:wf.RenderAttachment|wf.TextureBinding};null===(b=this._mainTextureLastCopy)||void 0===b||b.destroy(),this._mainTextureLastCopy=this._device.createTexture(c),this._options.antialiasing?a[0].resolveTarget=this._mainTextureLastCopy.createView():a[0].view=this._mainTextureLastCopy.createView(),this._mainRenderPassCopyWrapper.renderPassDescriptor={colorAttachments:[{view:void 0,loadValue:new h.b(0,0,0,1),storeOp:Tf.Store}]}}this._mainRenderPassWrapper.depthTextureFormat=this.isStencilEnable?Y.Depth24PlusStencil8:Y.Depth32Float,this._setDepthTextureFormat(this._mainRenderPassWrapper);b={size:this._mainTextureExtends,mipLevelCount:1,sampleCount:this._mainPassSampleCount,dimension:vf.E2d,format:this._mainRenderPassWrapper.depthTextureFormat,usage:wf.RenderAttachment};this._depthTexture&&this._depthTexture.destroy(),this._depthTexture=this._device.createTexture(b);c={view:this._depthTexture.createView(),depthLoadValue:this._clearDepthValue,depthStoreOp:Tf.Store,stencilLoadValue:this._clearStencilValue,stencilStoreOp:Tf.Store};this._mainRenderPassWrapper.renderPassDescriptor={colorAttachments:a,depthStencilAttachment:c},null!==this._mainRenderPassWrapper.renderPass&&this._endMainRenderPass()},b.prototype._configureContext=function(a,b){this._context.configure({device:this._device,format:this._options.swapChainFormat,usage:wf.RenderAttachment|wf.CopySrc,compositingAlphaMode:this.premultipliedAlpha?Wf.Premultiplied:Wf.Opaque,size:{width:a,height:b,depthOrArrayLayers:1}})},b.prototype.setSize=function(b,c,d){return void 0===d&&(d=!1),!!a.prototype.setSize.call(this,b,c,d)&&(this.dbgVerboseLogsForFirstFrames&&(void 0===this._count&&(this._count=0),(!this._count||this._count65535?c=new Uint32Array(a):(c=new Uint16Array(a),b=!1);a=this._bufferManager.createBuffer(c,tf.Index|tf.CopyDst);return a.is32Bits=b,a},b.prototype._createBuffer=function(a,b){a=a instanceof Array?new Float32Array(a):a instanceof ArrayBuffer?new Uint8Array(a):a;var c=0;return b&r.a.BUFFER_CREATIONFLAG_READ&&(c|=tf.CopySrc),b&r.a.BUFFER_CREATIONFLAG_WRITE&&(c|=tf.CopyDst),b&r.a.BUFFER_CREATIONFLAG_UNIFORM&&(c|=tf.Uniform),b&r.a.BUFFER_CREATIONFLAG_VERTEX&&(c|=tf.Vertex),b&r.a.BUFFER_CREATIONFLAG_INDEX&&(c|=tf.Index),b&r.a.BUFFER_CREATIONFLAG_STORAGE&&(c|=tf.Storage),this._bufferManager.createBuffer(a,c)},b.prototype.bindBuffersDirectly=function(a,b,c,d,e){throw"Not implemented on WebGPU"},b.prototype.updateAndBindInstancesBuffer=function(a,b,c){throw"Not implemented on WebGPU"},b.prototype.bindBuffers=function(a,b,c,d){this._currentIndexBuffer=b,this._currentOverrideVertexBuffers=null!=d?d:null,this._cacheRenderPipeline.setBuffers(a,b,this._currentOverrideVertexBuffers)},b.prototype._releaseBuffer=function(a){return this._bufferManager.releaseBuffer(a)},b.prototype.createEffect=function(a,b,c,d,e,f,g,h,i,j){var k;void 0===j&&(j=Xd.a.GLSL);var l=a.vertexElement||a.vertex||a.vertexToken||a.vertexSource||a,o=a.fragmentElement||a.fragment||a.fragmentToken||a.fragmentSource||a,p=this._getGlobalDefines();k=null!==(k=null!=e?e:b.defines)&&void 0!==k?k:"";p&&(k+=" "+p);p=l+"+"+o+"@"+k;if(this._compiledEffects[p]){l=this._compiledEffects[p];return g&&l.isReady()&&g(l),l}o=new $f.a(a,b,c,d,this,e,f,g,h,i,p,j);return this._compiledEffects[p]=o,o},b.prototype._compileRawShaderToSpirV=function(a,b){return this._glslang.compileGLSL(a,b)},b.prototype._compileShaderToSpirV=function(a,b,c,d){return this._compileRawShaderToSpirV(d+(c?c+" ":"")+a,b)},b.prototype._getWGSLShader=function(a,b,c,d){return(c?"//"+c.split(" ").join(" //")+" ":"")+a},b.prototype._createPipelineStageDescriptor=function(a,b,c){return this._tintWASM&&c===Xd.a.GLSL&&(a=this._tintWASM.convertSpirV2WGSL(a),b=this._tintWASM.convertSpirV2WGSL(b)),{vertexStage:{module:this._device.createShaderModule({code:a}),entryPoint:"main"},fragmentStage:{module:this._device.createShaderModule({code:b}),entryPoint:"main"}}},b.prototype._compileRawPipelineStageDescriptor=function(a,b,c){a=c===Xd.a.GLSL?this._compileRawShaderToSpirV(a,"vertex"):a;b=c===Xd.a.GLSL?this._compileRawShaderToSpirV(b,"fragment"):b;return this._createPipelineStageDescriptor(a,b,c)},b.prototype._compilePipelineStageDescriptor=function(a,b,c,d){this.onBeforeShaderCompilationObservable.notifyObservers(this);a=d===Xd.a.GLSL?this._compileShaderToSpirV(a,"vertex",c,"#version 450 "):this._getWGSLShader(a,"vertex",c,"#version 450 ");b=d===Xd.a.GLSL?this._compileShaderToSpirV(b,"fragment",c,"#version 450 "):this._getWGSLShader(b,"fragment",c,"#version 450 ");c=this._createPipelineStageDescriptor(a,b,d);return this.onAfterShaderCompilationObservable.notifyObservers(this),c},b.prototype.createRawShaderProgram=function(a,b,c,d,e){throw void 0===e&&null,"Not available on WebGPU"},b.prototype.createShaderProgram=function(a,b,c,d,e,f){throw void 0===f&&null,"Not available on WebGPU"},b.prototype.inlineShaderCode=function(a){a=new kf(a);return a.debug=!1,a.processCode(),a.code},b.prototype.createPipelineContext=function(a){return new bg(a,this)},b.prototype.createMaterialContext=function(){return new Eg},b.prototype.createDrawContext=function(){return new Fg},b.prototype._preparePipelineContext=function(a,b,c,d,e,f,g,h,i,j){g=a;i=g.shaderProcessingContext.shaderLanguage;this.dbgShowShaderCode&&(!1,!1,!1),g.sources={fragment:c,vertex:b,rawVertex:e,rawFragment:f},g.stages=d?this._compileRawPipelineStageDescriptor(b,c,i):this._compilePipelineStageDescriptor(b,c,h,i)},b.prototype.getAttributes=function(a,b){for(var c=new Array(b.length),a=a,d=0;d0;for(var e in a){var f=a[e],g=c[e],h=g.group;g=g.binding;var i=f.type,j=f.object,k=f.indexInGroupEntries;switch((m=this._bindGroupEntries[h])||(m=this._bindGroupEntries[h]=[]),i){case Sd.Sampler:h=j;void 0!==k&&d?m[k].resource=this._cacheSampler.getSampler(h):(f.indexInGroupEntries=m.length,m.push({binding:g,resource:this._cacheSampler.getSampler(h)}));break;case Sd.Texture:case Sd.TextureWithoutSampler:var l=(h=j)._texture._hardwareTexture;void 0!==k&&d?(i===Sd.Texture&&(m[k++].resource=this._cacheSampler.getSampler(h._texture)),m[k].resource=l.view):(f.indexInGroupEntries=m.length,i===Sd.Texture&&m.push({binding:g-1,resource:this._cacheSampler.getSampler(h._texture)}),m.push({binding:g,resource:l.view}));break;case Sd.StorageTexture:0==((l=(h=j)._texture._hardwareTexture).textureAdditionalUsages&wf.StorageBinding)&&q.a.Error("computeDispatch: The texture (name="+h.name+", uniqueId="+h.uniqueId+") is not a storage texture!",50),void 0!==k&&d?m[k].resource=l.view:(f.indexInGroupEntries=m.length,m.push({binding:g,resource:l.view}));break;case Sd.UniformBuffer:case Sd.StorageBuffer:i=(Sd.UniformBuffer,j).getBuffer();h=i.underlyingResource;void 0!==k&&d?(m[k].resource.buffer=h,m[k].resource.size=i.capacity):(f.indexInGroupEntries=m.length,m.push({binding:g,resource:{buffer:h,offset:0,size:i.capacity}}))}}for(l=0;l1&&(b=4);for(var d=0;d>z,B=[],C=0;C<6;C++){var D=k[z][n[C]];j&&(D=Yg(D,A,A,e)),B.push(new Uint8Array(D.buffer,D.byteOffset,D.byteLength))}x._textureHelper.updateCubeTextures(B,m.underlyingResource,A,A,m.format,l,!1,0,0,x._uploadEncoder)}else{for(B=[],C=0;C<6;C++)B.push(a[f[C]]);x.updateRawCubeTexture(y,B,d,e,l)}y.isReady=!0,null==b||b._removePendingData(y),i&&i()}}(a)},void 0,null==b?void 0:b.offlineProvider,!0,function(a,c){null==b||b._removePendingData(y),j&&a&&j(a.status+" "+a.statusText,c)}),y},Ug.prototype.createRawTexture3D=function(a,b,c,d,e,f,g,h,i,j,k){void 0===i&&(i=null),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=0);var l=pb.b.Raw3D;l=new pb.a(this,l);return l.baseWidth=b,l.baseHeight=c,l.baseDepth=d,l.width=b,l.height=c,l.depth=d,l.format=e,l.type=j,l.generateMipMaps=f,l.samplingMode=h,l.is3D=!0,this._doNotHandleContextLost||(l._bufferView=a),this._textureHelper.createGPUTextureForInternalTexture(l,b,c,void 0,k),this.updateRawTexture3D(l,a,e,g,i,j),this._internalTexturesCache.push(l),l},Ug.prototype.updateRawTexture3D=function(a,b,c,d,e,f){if(void 0===e&&(e=null),void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),this._doNotHandleContextLost||(a._bufferView=b,a.format=c,a.invertY=d,a._compression=e),b){e=a._hardwareTexture;c===r.a.TEXTUREFORMAT_RGB&&(b=Yg(b,a.width,a.height,f));c=new Uint8Array(b.buffer,b.byteOffset,b.byteLength);this._textureHelper.updateTexture(c,a,a.width,a.height,a.depth,e.format,0,0,d,!1,0,0,this._uploadEncoder),a.generateMipMaps&&this._generateMipmaps(a,this._uploadEncoder)}a.isReady=!0},Ug.prototype.createRawTexture2DArray=function(a,b,c,d,e,f,g,h,i,j,k){void 0===i&&(i=null),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=0);var l=pb.b.Raw2DArray;l=new pb.a(this,l);return l.baseWidth=b,l.baseHeight=c,l.baseDepth=d,l.width=b,l.height=c,l.depth=d,l.format=e,l.type=j,l.generateMipMaps=f,l.samplingMode=h,l.is2DArray=!0,this._doNotHandleContextLost||(l._bufferView=a),this._textureHelper.createGPUTextureForInternalTexture(l,b,c,d,k),this.updateRawTexture2DArray(l,a,e,g,i,j),this._internalTexturesCache.push(l),l},Ug.prototype.updateRawTexture2DArray=function(a,b,c,d,e,f){if(void 0===e&&(e=null),void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),this._doNotHandleContextLost||(a._bufferView=b,a.format=c,a.invertY=d,a._compression=e),b){e=a._hardwareTexture;c===r.a.TEXTUREFORMAT_RGB&&(b=Yg(b,a.width,a.height,f));c=new Uint8Array(b.buffer,b.byteOffset,b.byteLength);this._textureHelper.updateTexture(c,a,a.width,a.height,a.depth,e.format,0,0,d,!1,0,0,this._uploadEncoder),a.generateMipMaps&&this._generateMipmaps(a,this._uploadEncoder)}a.isReady=!0},Ug.prototype._readTexturePixels=function(a,b,c,d,e,f,g,h){void 0===d&&(d=-1),void 0===e&&(e=0),void 0===f&&(f=null),void 0===g&&(g=!0),void 0===h&&(h=!1);a=a._hardwareTexture;return g&&this.flushFramebuffer(),this._textureHelper.readPixels(a.underlyingResource,0,0,b,c,a.format,d,e,f,h)},Ug.prototype._readTexturePixelsSync=function(a,b,c,d,e,f,g,h){throw void 0===d&&-1,void 0===e&&0,void 0===f&&null,void 0===g&&!0,void 0===h&&!1,"_readTexturePixelsSync is unsupported in WebGPU!"},Ug.prototype._createHardwareRenderTargetWrapper=function(a,b,c){a=new Cc.a(a,b,c,this);return this._renderTargetWrapperCache.push(a),a},Ug.prototype.createRenderTargetTexture=function(a,b){var c,d=this._createHardwareRenderTargetWrapper(!1,!1,a),e=new Bc.b;void 0!==b&&"object"==typeof b?(e.generateMipMaps=b.generateMipMaps,e.generateDepthBuffer=void 0===b.generateDepthBuffer||b.generateDepthBuffer,e.generateStencilBuffer=e.generateDepthBuffer&&b.generateStencilBuffer,e.type=void 0===b.type?r.a.TEXTURETYPE_UNSIGNED_INT:b.type,e.samplingMode=void 0===b.samplingMode?r.a.TEXTURE_TRILINEAR_SAMPLINGMODE:b.samplingMode,e.format=void 0===b.format?r.a.TEXTUREFORMAT_RGBA:b.format,e.samples=null!==(c=b.samples)&&void 0!==c?c:1,e.creationFlags=null!==(c=b.creationFlags)&&void 0!==c?c:0):(e.generateMipMaps=b,e.generateDepthBuffer=!0,e.generateStencilBuffer=!1,e.type=r.a.TEXTURETYPE_UNSIGNED_INT,e.samplingMode=r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,e.format=r.a.TEXTUREFORMAT_RGBA,e.samples=1,e.creationFlags=0),(e.type!==r.a.TEXTURETYPE_FLOAT||this._caps.textureFloatLinearFiltering)&&(e.type!==r.a.TEXTURETYPE_HALF_FLOAT||this._caps.textureHalfFloatLinearFiltering)||(e.samplingMode=r.a.TEXTURE_NEAREST_SAMPLINGMODE);c=new pb.a(this,pb.b.RenderTarget);var f=a.width||a,g=a.height||a;a=a.layers||0;return d._generateDepthBuffer=e.generateDepthBuffer,d._generateStencilBuffer=!!e.generateStencilBuffer,c.baseWidth=f,c.baseHeight=g,c.width=f,c.height=g,c.depth=a,c.isReady=!0,c.samples=e.samples,c.generateMipMaps=!!e.generateMipMaps,c.samplingMode=e.samplingMode,c.type=e.type,c.format=e.format,c.is2DArray=a>0,c._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,c._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE,this._internalTexturesCache.push(c),d.setTextures(c),(d._generateDepthBuffer||d._generateStencilBuffer)&&d.createDepthStencilTexture(0,void 0===e.samplingMode||e.samplingMode===r.a.TEXTURE_BILINEAR_SAMPLINGMODE||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR||e.samplingMode===r.a.TEXTURE_TRILINEAR_SAMPLINGMODE||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR||e.samplingMode===r.a.TEXTURE_NEAREST_LINEAR||e.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,d._generateStencilBuffer,d.samples),void 0!==b&&"object"==typeof b&&b.createMipMaps&&!e.generateMipMaps&&(c.generateMipMaps=!0),this._textureHelper.createGPUTextureForInternalTexture(c,void 0,void 0,void 0,e.creationFlags),void 0!==b&&"object"==typeof b&&b.createMipMaps&&!e.generateMipMaps&&(c.generateMipMaps=!1),d},Ug.prototype._createDepthStencilTexture=function(a,b,c){c=new pb.a(this,pb.b.DepthStencil);b=Object(l.a)({bilinearFiltering:!1,comparisonFunction:0,generateStencil:!1,samples:1,depthTextureFormat:r.a.TEXTUREFORMAT_DEPTH16},b);return c.format=b.generateStencil?r.a.TEXTUREFORMAT_DEPTH24_STENCIL8:b.depthTextureFormat===r.a.TEXTUREFORMAT_DEPTH16?r.a.TEXTUREFORMAT_DEPTH32_FLOAT:b.depthTextureFormat,this._setupDepthStencilTexture(c,a,b.generateStencil,b.bilinearFiltering,b.comparisonFunction,b.samples),this._textureHelper.createGPUTextureForInternalTexture(c),this._internalTexturesCache.push(c),c},Ug.prototype._setupDepthStencilTexture=function(a,b,c,d,e,f){void 0===f&&(f=1);c=b.width||b;var g=b.height||b;b=b.layers||0;a.baseWidth=c,a.baseHeight=g,a.width=c,a.height=g,a.is2DArray=b>0,a.depth=b,a.isReady=!0,a.samples=f,a.generateMipMaps=!1,a.samplingMode=d?r.a.TEXTURE_BILINEAR_SAMPLINGMODE:r.a.TEXTURE_NEAREST_SAMPLINGMODE,a.type=r.a.TEXTURETYPE_FLOAT,a._comparisonFunction=e,a._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,a._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE},Ug.prototype.updateRenderTargetTextureSampleCount=function(a,b){return a&&a.texture&&a.samples!==b?((b=Math.min(b,this.getCaps().maxMSAASamples))>1&&(b=4),this._textureHelper.createMSAATexture(a.texture,b),a._depthStencilTexture&&(this._textureHelper.createMSAATexture(a._depthStencilTexture,b),a._depthStencilTexture.samples=b),a.texture.samples=b,b):b},Ug.prototype.createRenderTargetCubeTexture=function(a,b){var c=this._createHardwareRenderTargetWrapper(!1,!0,a),d=Object(l.a)({generateMipMaps:!0,generateDepthBuffer:!0,generateStencilBuffer:!1,type:r.a.TEXTURETYPE_UNSIGNED_INT,samplingMode:r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,format:r.a.TEXTUREFORMAT_RGBA,samples:1},b);d.generateStencilBuffer=d.generateDepthBuffer&&d.generateStencilBuffer,c._generateDepthBuffer=d.generateDepthBuffer,c._generateStencilBuffer=d.generateStencilBuffer;var e=new pb.a(this,pb.b.RenderTarget);return e.width=a,e.height=a,e.depth=0,e.isReady=!0,e.isCube=!0,e.samples=d.samples,e.generateMipMaps=d.generateMipMaps,e.samplingMode=d.samplingMode,e.type=d.type,e.format=d.format,this._internalTexturesCache.push(e),c.setTextures(e),(c._generateDepthBuffer||c._generateStencilBuffer)&&c.createDepthStencilTexture(0,void 0===d.samplingMode||d.samplingMode===r.a.TEXTURE_BILINEAR_SAMPLINGMODE||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR||d.samplingMode===r.a.TEXTURE_TRILINEAR_SAMPLINGMODE||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPLINEAR||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPNEAREST||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR_MIPLINEAR||d.samplingMode===r.a.TEXTURE_NEAREST_LINEAR||d.samplingMode===r.a.TEXTURE_LINEAR_LINEAR_MIPNEAREST,c._generateStencilBuffer,c.samples),b&&b.createMipMaps&&!d.generateMipMaps&&(e.generateMipMaps=!0),this._textureHelper.createGPUTextureForInternalTexture(e),b&&b.createMipMaps&&!d.generateMipMaps&&(e.generateMipMaps=!1),c},$f.a.prototype.setTextureSampler=function(a,b){this._engine.setTextureSampler(a,b)},Ug.prototype.setTextureSampler=function(a,b){var c;null===(c=this._currentMaterialContext)||void 0===c||c.setSampler(a,b)},$f.a.prototype.setStorageBuffer=function(a,b){this._engine.setStorageBuffer(a,b)},Ug.prototype.createStorageBuffer=function(a,b){return this._createBuffer(a,b|r.a.BUFFER_CREATIONFLAG_STORAGE)},Ug.prototype.updateStorageBuffer=function(a,b,c,d){var e;a=a;void 0===c&&(c=0),void 0===d?d=(e=b instanceof Array?new Float32Array(b):b instanceof ArrayBuffer?new Uint8Array(b):b).byteLength:e=b instanceof Array?new Float32Array(b):b instanceof ArrayBuffer?new Uint8Array(b):b,this._bufferManager.setSubData(a,c,e,0,d)},Ug.prototype.readFromStorageBuffer=function(a,b,c,d){var e=this;c=c||a.capacity;var f=this._bufferManager.createRawBuffer(c,tf.MapRead|tf.CopyDst);return this._renderTargetEncoder.copyBufferToBuffer(a.underlyingResource,null!=b?b:0,f,0,c),new Promise(function(a,b){e.onEndFrameObservable.addOnce(function(){f.mapAsync(uf.Read,0,c).then(function(){var b=f.getMappedRange(0,c),g=d;if(void 0===g)(g=new Uint8Array(c)).set(new Uint8Array(b));else{var h=g.constructor;(g=new h(g.buffer)).set(new h(b))}f.unmap(),e._bufferManager.releaseBuffer(f),a(g)},function(a){return b(a)})})})},Ug.prototype.setStorageBuffer=function(a,b){var c;null===(c=this._currentMaterialContext)||void 0===c||c.setBuffer(a,null!==(c=null==b?void 0:b.getBuffer())&&void 0!==c?c:null)},Ug.prototype.createUniformBuffer=function(a){return a=a instanceof Array?new Float32Array(a):a,this._bufferManager.createBuffer(a,tf.Uniform|tf.CopyDst)},Ug.prototype.createDynamicUniformBuffer=function(a){return this.createUniformBuffer(a)},Ug.prototype.updateUniformBuffer=function(a,b,c,d){void 0===c&&(c=0);var e;a=a;void 0===d?d=(e=b instanceof Float32Array?b:new Float32Array(b)).byteLength:e=b instanceof Float32Array?b:new Float32Array(b),this._bufferManager.setSubData(a,c,e,0,d)},Ug.prototype.bindUniformBufferBase=function(a,b,c){this._currentMaterialContext.setBuffer(c,a)},Ug.prototype.bindUniformBlock=function(a,b,c){},Ug.prototype.updateVideoTexture=function(a,b,c){var d,e=this;if(a&&!a._isDisabled){void 0===this._videoTextureSupported&&(this._videoTextureSupported=!0);var f=a._hardwareTexture;(null===(d=a._hardwareTexture)||void 0===d?void 0:d.underlyingResource)||(f=this._textureHelper.createGPUTextureForInternalTexture(a)),this.createImageBitmap(b).then(function(b){e._textureHelper.updateTexture(b,a,a.width,a.height,a.depth,f.format,0,0,!c,!1,0,0,e._uploadEncoder),a.generateMipMaps&&e._generateMipmaps(a,e._uploadEncoder),a.isReady=!0})["catch"](function(b){a.isReady=!0})}};var Zg,$g=c(167),ah=c(131),bh=c(81),ch=function(){function a(){}return a.CreateAsync=function(a,b){return Ug.IsSupportedAsync.then(function(c){return c?Ug.CreateAsync(a,b):S.a.IsSupported?new Promise(function(c){c(new S.a(a,void 0,b))}):new Promise(function(a){a(new pe.a(b))})})},a}(),dh=function(){function a(){}return a.COPY=1,a.CUT=2,a.PASTE=3,a}(),eh=function(){function a(a,b){this.type=a,this.event=b}return a.GetTypeFromCharacter=function(a){switch(a){case 67:return dh.COPY;case 86:return dh.PASTE;case 88:return dh.CUT;default:return-1}},a}(),fh=c(77),gh=c(39);!function(a){a[a.Clean=0]="Clean",a[a.Stop=1]="Stop",a[a.Sync=2]="Sync",a[a.NoSync=3]="NoSync"}(Zg||(Zg={}));var hh=function(){function a(){}return Object.defineProperty(a,"ForceFullSceneLoadingForIncremental",{get:function(){return fh.a.ForceFullSceneLoadingForIncremental},set:function(a){fh.a.ForceFullSceneLoadingForIncremental=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"ShowLoadingScreen",{get:function(){return fh.a.ShowLoadingScreen},set:function(a){fh.a.ShowLoadingScreen=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"loggingLevel",{get:function(){return fh.a.loggingLevel},set:function(a){fh.a.loggingLevel=a},enumerable:!1,configurable:!0}),Object.defineProperty(a,"CleanBoneMatrixWeights",{get:function(){return fh.a.CleanBoneMatrixWeights},set:function(a){fh.a.CleanBoneMatrixWeights=a},enumerable:!1,configurable:!0}),a.GetDefaultPlugin=function(){return a._registeredPlugins[".babylon"]},a._GetPluginForExtension=function(b){var c=a._registeredPlugins[b];return c||(q.a.Warn("Unable to find a plugin to load "+b+" files. Trying to use .babylon default plugin. To load from a specific filetype (eg. gltf) see: https://doc.babylonjs.com/how_to/load_from_any_file_type"),a.GetDefaultPlugin())},a._GetPluginForDirectLoad=function(b){for(var c in a._registeredPlugins){var d=a._registeredPlugins[c].plugin;if(d.canDirectLoad&&d.canDirectLoad(b))return a._registeredPlugins[c]}return a.GetDefaultPlugin()},a._GetPluginForFilename=function(b){var c=b.indexOf("?");-1!==c&&(b=b.substring(0,c));c=b.lastIndexOf(".");c=b.substring(c,b.length).toLowerCase();return a._GetPluginForExtension(c)},a._GetDirectLoad=function(a){return"data:"===a.substr(0,5)?a.substr(5):null},a._FormatErrorMessage=function(a,b,c){a="Unable to load from "+a.url;return b?a+=": "+b:c&&(a+=": "+c),a},a._LoadData=function(b,c,d,e,f,g,h){var i,j=a._GetDirectLoad(b.url);h=h?a._GetPluginForExtension(h):j?a._GetPluginForDirectLoad(b.url):a._GetPluginForFilename(b.url);if(!(i=void 0!==h.plugin.createPlugin?h.plugin.createPlugin():h.plugin))throw"The loader plugin corresponding to the file type you are trying to load has not been found. If using es6, please import the plugin you wish to use before.";if(a.OnPluginActivatedObservable.notifyObservers(i),j&&(i.canDirectLoad&&i.canDirectLoad(b.url)||!Object(ue.e)(b.url))){if(i.directLoad){var k=i.directLoad(c,j);k.then?k.then(function(a){d(i,a)})["catch"](function(a){f("Error in directLoad of _loadData: "+a,a)}):d(i,k)}else d(i,j);return i}var l=h.isBinary,q=function(a,b){c.isDisposed?f("Scene has been disposed"):d(i,a,b)},r=null,s=!1;k=i.onDisposeObservable;k&&k.add(function(){s=!0,r&&(r.abort(),r=null),g()});j=function(){if(!s){var a=function(a,b){f(null==a?void 0:a.statusText,b)},d=b.file||b.url;r=i.loadFile?i.loadFile(c,d,q,e,l,a):c._loadFile(d,q,e,!0,l,a)}};h=c.getEngine();k=h.enableOfflineSupport;if(k){for(var t=!1,x=0,y=c.disableOfflineSupportExceptionRules;xk.snapDistance?(d=Math.floor(Math.abs(u)/k.snapDistance),u<0&&(d*=-1),u%=k.snapDistance,z.scaleToRef(k.snapDistance*d,z),c=!0):z.scaleInPlace(0)),g.a.ScalingToRef(1+z.x,1+z.y,1+z.z,k._tmpMatrix2),k._tmpMatrix2.multiplyToRef(k.attachedNode.getWorldMatrix(),k._tmpMatrix),k._tmpMatrix.decompose(k._tmpVector);Math.abs(k._tmpVector.x)<1e5&&Math.abs(k._tmpVector.y)<1e5&&Math.abs(k._tmpVector.z)<1e5&&k.attachedNode.getWorldMatrix().copyFrom(k._tmpMatrix),c&&(E.snapDistance=k.snapDistance*d,k.onSnapObservable.notifyObservers(E)),k._matrixChanged()}}),k.dragBehavior.onDragStartObservable.add(function(){k._dragging=!0}),k.dragBehavior.onDragObservable.add(function(a){return r(a.dragDistance)}),k.dragBehavior.onDragEndObservable.add(i),null===(j=null===(j=null===(j=null==e?void 0:e.uniformScaleGizmo)||void 0===j?void 0:j.dragBehavior)||void 0===j?void 0:j.onDragObservable)||void 0===j||j.add(function(a){return r(a.delta.y)}),null===(j=null===(e=null===(j=null==e?void 0:e.uniformScaleGizmo)||void 0===j?void 0:j.dragBehavior)||void 0===e?void 0:e.onDragEndObservable)||void 0===j||j.add(i);var F={gizmoMeshes:[l,m],colliderMeshes:[c.arrowMesh,c.arrowTail],material:k._coloredMaterial,hoverMaterial:k._hoverMaterial,disableMaterial:k._disableMaterial,active:!1,dragBehavior:k.dragBehavior};null===(e=k._parent)||void 0===e||e.addToAxisCache(k._gizmoMesh,F),k._pointerObserver=d.utilityLayerScene.onPointerObservable.add(function(a){if(!k._customMeshSet&&(k._isHovered=!(-1==F.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!k._parent)){a=k.dragBehavior.enabled?k._isHovered||k._dragging?k._hoverMaterial:k._coloredMaterial:k._disableMaterial;k._setGizmoMeshMaterial(F.gizmoMeshes,a)}}),k.dragBehavior.onEnabledObservable.add(function(a){k._setGizmoMeshMaterial(F.gizmoMeshes,a?k._coloredMaterial:k._disableMaterial)});j=d._getSharedGizmoLight();return j.includedOnlyMeshes=j.includedOnlyMeshes.concat(k._rootMesh.getChildMeshes()),k}return Object(l.d)(b,a),b.prototype._createGizmoMesh=function(a,b,c){void 0===c&&(c=!1);var d=Object(rh.b)("yPosMesh",{size:.4*(1+(b-1)/4)},this.gizmoLayer.utilityLayerScene);b=Object(xd.a)("cylinder",{diameterTop:.005*b,height:.275,diameterBottom:.005*b,tessellation:96},this.gizmoLayer.utilityLayerScene);return d.scaling.scaleInPlace(.1),d.material=this._coloredMaterial,d.rotation.x=Math.PI/2,d.position.z+=.3,b.material=this._coloredMaterial,b.position.z+=.1375,b.rotation.x=Math.PI/2,c&&(d.visibility=0,b.visibility=0),a.addChild(d),a.addChild(b),{arrowMesh:d,arrowTail:b}},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedMesh=this._parent.attachedMesh,this.attachedNode=this._parent.attachedNode):(this.attachedMesh=null,this.attachedNode=null)},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),this._gizmoMesh&&this._gizmoMesh.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()}),a.prototype.dispose.call(this)},b.prototype.setCustomMesh=function(b,c){var d=this;void 0===c&&(c=!1),a.prototype.setCustomMesh.call(this,b),c&&(this._rootMesh.getChildMeshes().forEach(function(a){a.material=d._coloredMaterial,a.color&&(a.color=d._coloredMaterial.diffuseColor)}),this._customMeshSet=!1)},b}(sh.a),vh=c(48),wh=c(38),xh=c(75),yh=function(a){function b(b,c){void 0===b&&(b=h.a.Gray()),void 0===c&&(c=th.a.DefaultKeepDepthUtilityLayer);var d=a.call(this,c)||this;d._boundingDimensions=new g.e(1,1,1),d._renderObserver=null,d._pointerObserver=null,d._scaleDragSpeed=.2,d._tmpQuaternion=new g.b,d._tmpVector=new g.e(0,0,0),d._tmpRotationMatrix=new g.a,d.ignoreChildren=!1,d.includeChildPredicate=null,d.rotationSphereSize=.1,d.scaleBoxSize=.1,d.fixedDragMeshScreenSize=!1,d.fixedDragMeshBoundsSize=!1,d.fixedDragMeshScreenSizeDistanceFactor=10,d.onDragStartObservable=new f.c,d.onScaleBoxDragObservable=new f.c,d.onScaleBoxDragEndObservable=new f.c,d.onRotationSphereDragObservable=new f.c,d.onRotationSphereDragEndObservable=new f.c,d.scalePivot=null,d._axisFactor=new g.e(1,1,1),d._existingMeshScale=new g.e,d._dragMesh=null,d.pointerDragBehavior=new ab.a,d.updateScale=!1,d._anchorMesh=new cb.a("anchor",c.utilityLayerScene),d.coloredMaterial=new pd.a("",c.utilityLayerScene),d.coloredMaterial.disableLighting=!0,d.hoverColoredMaterial=new pd.a("",c.utilityLayerScene),d.hoverColoredMaterial.disableLighting=!0,d._lineBoundingBox=new cb.a("",c.utilityLayerScene),d._lineBoundingBox.rotationQuaternion=new g.b;var e=[];e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(d._boundingDimensions.x,0,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(0,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,0),new g.e(0,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,0,0),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,0,0),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,d._boundingDimensions.y,0),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,d._boundingDimensions.y,0),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(0,0,d._boundingDimensions.z),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(0,d._boundingDimensions.y,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,0,d._boundingDimensions.z)]},c.utilityLayerScene)),e.push(Object(wh.e)("lines",{points:[new g.e(d._boundingDimensions.x,d._boundingDimensions.y,d._boundingDimensions.z),new g.e(d._boundingDimensions.x,d._boundingDimensions.y,0)]},c.utilityLayerScene)),e.forEach(function(a){a.color=b,a.position.addInPlace(new g.e(-d._boundingDimensions.x/2,-d._boundingDimensions.y/2,-d._boundingDimensions.z/2)),a.isPickable=!1,d._lineBoundingBox.addChild(a)}),d._rootMesh.addChild(d._lineBoundingBox),d.setColor(b),d._rotateSpheresParent=new cb.a("",c.utilityLayerScene),d._rotateSpheresParent.rotationQuaternion=new g.b;for(var i=function(a){var b=Object(vh.a)("",{diameter:1},c.utilityLayerScene);b.rotationQuaternion=new g.b,b.material=j.coloredMaterial,b.isNearGrabbable=!0,(e=new ab.a({})).moveAttached=!1,e.updateDragPlane=!1,b.addBehavior(e);var e=new g.e(1,0,0),f=0;e.onDragStartObservable.add(function(){e.copyFrom(b.forward),f=0}),e.onDragObservable.add(function(b){if(d.onRotationSphereDragObservable.notifyObservers({}),d.attachedMesh){var c=d.attachedMesh.parent;if(c&&c.scaling&&c.scaling.isNonUniformWithinEpsilon(.001))return void q.a.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");xh.a._RemoveAndStorePivotPoint(d.attachedMesh);var h=e,i=b.dragPlaneNormal.scale(g.e.Dot(b.dragPlaneNormal,h));h=h.subtract(i).normalizeToNew();i=g.e.Dot(h,b.delta)<0?Math.abs(b.delta.length()):-Math.abs(b.delta.length());i=i/d._boundingDimensions.length()*d._anchorMesh.scaling.length(),d.attachedMesh.rotationQuaternion||(d.attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(d.attachedMesh.rotation.y,d.attachedMesh.rotation.x,d.attachedMesh.rotation.z)),d._anchorMesh.rotationQuaternion||(d._anchorMesh.rotationQuaternion=g.b.RotationYawPitchRoll(d._anchorMesh.rotation.y,d._anchorMesh.rotation.x,d._anchorMesh.rotation.z)),f+=i,Math.abs(f)<=2*Math.PI&&(a>=8?g.b.RotationYawPitchRollToRef(0,0,i,d._tmpQuaternion):a>=4?g.b.RotationYawPitchRollToRef(i,0,0,d._tmpQuaternion):g.b.RotationYawPitchRollToRef(0,i,0,d._tmpQuaternion),d._anchorMesh.addChild(d.attachedMesh),d._anchorMesh.rotationQuaternion.multiplyToRef(d._tmpQuaternion,d._anchorMesh.rotationQuaternion),d._anchorMesh.removeChild(d.attachedMesh),d.attachedMesh.setParent(c)),d.updateBoundingBox(),xh.a._RestorePivotPoint(d.attachedMesh)}d._updateDummy()}),e.onDragStartObservable.add(function(){d.onDragStartObservable.notifyObservers({}),d._selectNode(b)}),e.onDragEndObservable.add(function(){d.onRotationSphereDragEndObservable.notifyObservers({}),d._selectNode(null),d._updateDummy()}),j._rotateSpheresParent.addChild(b)},j=this,k=0;k<12;k++)i(k);d._rootMesh.addChild(d._rotateSpheresParent),d._scaleBoxesParent=new cb.a("",c.utilityLayerScene),d._scaleBoxesParent.rotationQuaternion=new g.b;for(var l=0;l<3;l++)for(var n=0;n<3;n++)for(var e,i=function(){var a=(1===l?1:0)+(1===n?1:0)+(1===t?1:0);if(1===a||3===a)return"continue";var b=Object(rh.b)("",{size:1},c.utilityLayerScene);b.material=o.coloredMaterial,b.metadata=2===a,b.isNearGrabbable=!0;var e=new g.e(l-1,n-1,t-1).normalize();(e=new ab.a({dragAxis:e})).updateDragPlane=!1,e.moveAttached=!1,b.addBehavior(e),e.onDragObservable.add(function(c){if(d.onScaleBoxDragObservable.notifyObservers({}),d.attachedMesh){var f=d.attachedMesh.parent;if(f&&f.scaling&&f.scaling.isNonUniformWithinEpsilon(.001))return void q.a.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");xh.a._RemoveAndStorePivotPoint(d.attachedMesh);c=c.dragDistance/d._boundingDimensions.length()*d._anchorMesh.scaling.length();c=new g.e(c,c,c);2===a&&(c.x*=Math.abs(e.x),c.y*=Math.abs(e.y),c.z*=Math.abs(e.z)),c.scaleInPlace(d._scaleDragSpeed),c.multiplyInPlace(d._axisFactor),d.updateBoundingBox(),d.scalePivot?(d.attachedMesh.getWorldMatrix().getRotationMatrixToRef(d._tmpRotationMatrix),d._boundingDimensions.scaleToRef(.5,d._tmpVector),g.e.TransformCoordinatesToRef(d._tmpVector,d._tmpRotationMatrix,d._tmpVector),d._anchorMesh.position.subtractInPlace(d._tmpVector),d._boundingDimensions.multiplyToRef(d.scalePivot,d._tmpVector),g.e.TransformCoordinatesToRef(d._tmpVector,d._tmpRotationMatrix,d._tmpVector),d._anchorMesh.position.addInPlace(d._tmpVector)):(b.absolutePosition.subtractToRef(d._anchorMesh.position,d._tmpVector),d._anchorMesh.position.subtractInPlace(d._tmpVector)),d._anchorMesh.addChild(d.attachedMesh),d._anchorMesh.scaling.addInPlace(c),(d._anchorMesh.scaling.x<0||d._anchorMesh.scaling.y<0||d._anchorMesh.scaling.z<0)&&d._anchorMesh.scaling.subtractInPlace(c),d._anchorMesh.removeChild(d.attachedMesh),d.attachedMesh.setParent(f),xh.a._RestorePivotPoint(d.attachedMesh)}d._updateDummy()}),e.onDragStartObservable.add(function(){d.onDragStartObservable.notifyObservers({}),d._selectNode(b)}),e.onDragEndObservable.add(function(){d.onScaleBoxDragEndObservable.notifyObservers({}),d._selectNode(null),d._updateDummy()}),o._scaleBoxesParent.addChild(b)},o=this,t=0;t<3;t++)i();d._rootMesh.addChild(d._scaleBoxesParent);var u=new Array;return d._pointerObserver=c.utilityLayerScene.onPointerObservable.add(function(a){u[a.event.pointerId]?a.pickInfo&&a.pickInfo.pickedMesh!=u[a.event.pointerId]&&(u[a.event.pointerId].material=d.coloredMaterial,delete u[a.event.pointerId]):d._rotateSpheresParent.getChildMeshes().concat(d._scaleBoxesParent.getChildMeshes()).forEach(function(b){a.pickInfo&&a.pickInfo.pickedMesh==b&&(u[a.event.pointerId]=b,b.material=d.hoverColoredMaterial)})}),d._renderObserver=d.gizmoLayer.originalScene.onBeforeRenderObservable.add(function(){d.attachedMesh&&!d._existingMeshScale.equals(d.attachedMesh.scaling)?d.updateBoundingBox():(d.fixedDragMeshScreenSize||d.fixedDragMeshBoundsSize)&&(d._updateRotationSpheres(),d._updateScaleBoxes()),d._dragMesh&&d.attachedMesh&&d.pointerDragBehavior.dragging&&(d._lineBoundingBox.position.rotateByQuaternionToRef(d._rootMesh.rotationQuaternion,d._tmpVector),d.attachedMesh.setAbsolutePosition(d._dragMesh.position.add(d._tmpVector.scale(-1))))}),d.updateBoundingBox(),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"axisFactor",{get:function(){return this._axisFactor},set:function(a){this._axisFactor=a;for(var a=this._scaleBoxesParent.getChildMeshes(),b=0,c=0;c<3;c++)for(var d=0;d<3;d++)for(var e=0;e<3;e++){var f=(1===c?1:0)+(1===d?1:0)+(1===e?1:0);if(1!==f&&3!==f){if(a[b]){f=new g.e(c-1,d-1,e-1);f.multiplyInPlace(this._axisFactor),a[b].setEnabled(f.lengthSquared()>ib.a)}b++}}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleDragSpeed",{get:function(){return this._scaleDragSpeed},set:function(a){this._scaleDragSpeed=a},enumerable:!1,configurable:!0}),b.prototype.setColor=function(a){this.coloredMaterial.emissiveColor=a,this.hoverColoredMaterial.emissiveColor=a.clone().add(new h.a(.3,.3,.3)),this._lineBoundingBox.getChildren().forEach(function(b){b.color&&(b.color=a)})},b.prototype._attachedNodeChanged=function(a){var b=this;if(a){this._anchorMesh.scaling.setAll(1),xh.a._RemoveAndStorePivotPoint(a);var c=a.parent;this._anchorMesh.addChild(a),this._anchorMesh.removeChild(a),a.setParent(c),xh.a._RestorePivotPoint(a),this.updateBoundingBox(),a.getChildMeshes(!1).forEach(function(a){a.markAsDirty("scaling")}),this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(function(){b._updateDummy()})}},b.prototype._selectNode=function(a){this._rotateSpheresParent.getChildMeshes().concat(this._scaleBoxesParent.getChildMeshes()).forEach(function(b){b.isVisible=!a||b==a})},b.prototype.updateBoundingBox=function(){if(this.attachedMesh){xh.a._RemoveAndStorePivotPoint(this.attachedMesh);var a=this.attachedMesh.parent;this.attachedMesh.setParent(null);var b=null;this.attachedMesh.skeleton&&(b=this.attachedMesh.skeleton.overrideMesh,this.attachedMesh.skeleton.overrideMesh=null),this._update(),this.attachedMesh.rotationQuaternion||(this.attachedMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this.attachedMesh.rotation.y,this.attachedMesh.rotation.x,this.attachedMesh.rotation.z)),this._anchorMesh.rotationQuaternion||(this._anchorMesh.rotationQuaternion=g.b.RotationYawPitchRoll(this._anchorMesh.rotation.y,this._anchorMesh.rotation.x,this._anchorMesh.rotation.z)),this._anchorMesh.rotationQuaternion.copyFrom(this.attachedMesh.rotationQuaternion),this._tmpQuaternion.copyFrom(this.attachedMesh.rotationQuaternion),this._tmpVector.copyFrom(this.attachedMesh.position),this.attachedMesh.rotationQuaternion.set(0,0,0,1),this.attachedMesh.position.set(0,0,0);var c=this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren,this.includeChildPredicate);c.max.subtractToRef(c.min,this._boundingDimensions),this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions),this._lineBoundingBox.position.set((c.max.x+c.min.x)/2,(c.max.y+c.min.y)/2,(c.max.z+c.min.z)/2),this._rotateSpheresParent.position.copyFrom(this._lineBoundingBox.position),this._scaleBoxesParent.position.copyFrom(this._lineBoundingBox.position),this._lineBoundingBox.computeWorldMatrix(),this._anchorMesh.position.copyFrom(this._lineBoundingBox.absolutePosition),this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion),this.attachedMesh.position.copyFrom(this._tmpVector),this.attachedMesh.setParent(a),this.attachedMesh.skeleton&&(this.attachedMesh.skeleton.overrideMesh=b)}this._updateRotationSpheres(),this._updateScaleBoxes(),this.attachedMesh&&(this._existingMeshScale.copyFrom(this.attachedMesh.scaling),xh.a._RestorePivotPoint(this.attachedMesh))},b.prototype._updateRotationSpheres=function(){for(var a=this._rotateSpheresParent.getChildMeshes(),b=0;b<3;b++)for(var c=0;c<2;c++)for(var d=0;d<2;d++){var e=4*b+2*c+d;if(0==b&&(a[e].position.set(this._boundingDimensions.x/2,this._boundingDimensions.y*c,this._boundingDimensions.z*d),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Right()).normalizeToNew().add(a[e].position))),1==b&&(a[e].position.set(this._boundingDimensions.x*c,this._boundingDimensions.y/2,this._boundingDimensions.z*d),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Up()).normalizeToNew().add(a[e].position))),2==b&&(a[e].position.set(this._boundingDimensions.x*c,this._boundingDimensions.y*d,this._boundingDimensions.z/2),a[e].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),a[e].lookAt(g.e.Cross(a[e].position.normalizeToNew(),g.e.Forward()).normalizeToNew().add(a[e].position))),this.fixedDragMeshScreenSize&&this.gizmoLayer.utilityLayerScene.activeCamera){a[e].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera.position,this._tmpVector);var f=this.rotationSphereSize*this._tmpVector.length()/this.fixedDragMeshScreenSizeDistanceFactor;a[e].scaling.set(f,f,f)}else this.fixedDragMeshBoundsSize?a[e].scaling.set(this.rotationSphereSize*this._boundingDimensions.x,this.rotationSphereSize*this._boundingDimensions.y,this.rotationSphereSize*this._boundingDimensions.z):a[e].scaling.set(this.rotationSphereSize,this.rotationSphereSize,this.rotationSphereSize)}},b.prototype._updateScaleBoxes=function(){for(var a=this._scaleBoxesParent.getChildMeshes(),b=0,c=0;c<3;c++)for(var d=0;d<3;d++)for(var e=0;e<3;e++){var f=(1===c?1:0)+(1===d?1:0)+(1===e?1:0);if(1!==f&&3!==f){if(a[b])if(a[b].position.set(this._boundingDimensions.x*(c/2),this._boundingDimensions.y*(d/2),this._boundingDimensions.z*(e/2)),a[b].position.addInPlace(new g.e(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2)),this.fixedDragMeshScreenSize&&this.gizmoLayer.utilityLayerScene.activeCamera){a[b].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera.position,this._tmpVector);f=this.scaleBoxSize*this._tmpVector.length()/this.fixedDragMeshScreenSizeDistanceFactor;a[b].scaling.set(f,f,f)}else this.fixedDragMeshBoundsSize?a[b].scaling.set(this.scaleBoxSize*this._boundingDimensions.x,this.scaleBoxSize*this._boundingDimensions.y,this.scaleBoxSize*this._boundingDimensions.z):a[b].scaling.set(this.scaleBoxSize,this.scaleBoxSize,this.scaleBoxSize);b++}}},b.prototype.setEnabledRotationAxis=function(a){this._rotateSpheresParent.getChildMeshes().forEach(function(b,c){c<4?b.setEnabled(-1!=a.indexOf("x")):c<8?b.setEnabled(-1!=a.indexOf("y")):b.setEnabled(-1!=a.indexOf("z"))})},b.prototype.setEnabledScaling=function(a,b){void 0===b&&(b=!1),this._scaleBoxesParent.getChildMeshes().forEach(function(c,d){d=a;b&&!0===c.metadata&&(d=!1),c.setEnabled(d)})},b.prototype._updateDummy=function(){this._dragMesh&&(this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition()),this._dragMesh.scaling.copyFrom(this._lineBoundingBox.scaling),this._dragMesh.rotationQuaternion.copyFrom(this._rootMesh.rotationQuaternion))},b.prototype.enableDragBehavior=function(){this._dragMesh=Object(rh.b)("dummy",{size:1},this.gizmoLayer.utilityLayerScene),this._dragMesh.visibility=0,this._dragMesh.rotationQuaternion=new g.b,this.pointerDragBehavior.useObjectOrientationForDragging=!1,this._dragMesh.addBehavior(this.pointerDragBehavior)},b.prototype.dispose=function(){this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.gizmoLayer.originalScene.onBeforeRenderObservable.remove(this._renderObserver),this._lineBoundingBox.dispose(),this._rotateSpheresParent.dispose(),this._scaleBoxesParent.dispose(),this._dragMesh&&this._dragMesh.dispose(),a.prototype.dispose.call(this)},b.MakeNotPickableAndWrapInBoundingBox=function(a){var b=function(a){a.isPickable=!1,a.getChildMeshes().forEach(function(a){b(a)})};b(a),a.rotationQuaternion||(a.rotationQuaternion=g.b.RotationYawPitchRoll(a.rotation.y,a.rotation.x,a.rotation.z));var c=a.position.clone(),d=a.rotationQuaternion.clone();a.rotationQuaternion.set(0,0,0,1),a.position.set(0,0,0);var e=Object(rh.b)("box",{size:1},a.getScene()),f=a.getHierarchyBoundingVectors();return f.max.subtractToRef(f.min,e.scaling),0===e.scaling.y&&(e.scaling.y=ib.a),0===e.scaling.x&&(e.scaling.x=ib.a),0===e.scaling.z&&(e.scaling.z=ib.a),e.position.set((f.max.x+f.min.x)/2,(f.max.y+f.min.y)/2,(f.max.z+f.min.z)/2),a.addChild(e),a.rotationQuaternion.copyFrom(d),a.position.copyFrom(c),a.removeChild(e),e.addChild(a),e.visibility=0,e},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo")},b}(sh.a),zh=c(71),Ah=c(66),Bh=function(a){function b(c,d,e,i,j,k,l){void 0===d&&(d=h.a.Gray()),void 0===e&&(e=th.a.DefaultUtilityLayer),void 0===i&&(i=32),void 0===j&&(j=null),void 0===k&&(k=!1),void 0===l&&(l=1);var m=a.call(this,e)||this;m._pointerObserver=null,m.snapDistance=0,m.onSnapObservable=new f.c,m.angle=0,m._isEnabled=!0,m._parent=null,m._dragging=!1,m._angles=new g.e,m._parent=j,m._coloredMaterial=new pd.a("",e.utilityLayerScene),m._coloredMaterial.diffuseColor=d,m._coloredMaterial.specularColor=d.subtract(new h.a(.1,.1,.1)),m._hoverMaterial=new pd.a("",e.utilityLayerScene),m._hoverMaterial.diffuseColor=h.a.Yellow(),m._disableMaterial=new pd.a("",e.utilityLayerScene),m._disableMaterial.diffuseColor=h.a.Gray(),m._disableMaterial.alpha=.4,m._gizmoMesh=new R.a("",e.utilityLayerScene);k=m._createGizmoMesh(m._gizmoMesh,l,i);j=k.rotationMesh;d=k.collider;m._rotationDisplayPlane=Object(Ah.a)("rotationDisplay",{size:.6,updatable:!1},m.gizmoLayer.utilityLayerScene),m._rotationDisplayPlane.rotation.z=.5*Math.PI,m._rotationDisplayPlane.parent=m._gizmoMesh,m._rotationDisplayPlane.setEnabled(!1),$f.a.ShadersStore.rotationGizmoVertexShader=b._rotationGizmoVertexShader,$f.a.ShadersStore.rotationGizmoFragmentShader=b._rotationGizmoFragmentShader,m._rotationShaderMaterial=new zh.a("shader",m.gizmoLayer.utilityLayerScene,{vertex:"rotationGizmo",fragment:"rotationGizmo"},{attributes:["position","uv"],uniforms:["worldViewProjection","angles"]}),m._rotationShaderMaterial.backFaceCulling=!1,m._rotationDisplayPlane.material=m._rotationShaderMaterial,m._rotationDisplayPlane.visibility=.999,m._gizmoMesh.lookAt(m._rootMesh.position.add(c)),m._rootMesh.addChild(m._gizmoMesh),m._gizmoMesh.scaling.scaleInPlace(1/3),m.dragBehavior=new ab.a({dragPlaneNormal:c}),m.dragBehavior.moveAttached=!1,m.dragBehavior.maxDragAngle=b.MaxDragAngle,m.dragBehavior._useAlternatePickedPointAboveMaxDragAngle=!0,m._rootMesh.addBehavior(m.dragBehavior);var p=new g.e,q=new g.a,z=new g.e,B=new g.e;m.dragBehavior.onDragStartObservable.add(function(a){m.attachedNode&&(p.copyFrom(a.dragPlanePoint),m._rotationDisplayPlane.setEnabled(!0),m._rotationDisplayPlane.getWorldMatrix().invertToRef(q),g.e.TransformCoordinatesToRef(a.dragPlanePoint,q,p),m._angles.x=Math.atan2(p.y,p.x)+Math.PI,m._angles.y=0,m._angles.z=m.updateGizmoRotationToMatchAttachedMesh?1:0,m._dragging=!0,p.copyFrom(a.dragPlanePoint),m._rotationShaderMaterial.setVector3("angles",m._angles),m.angle=0)}),m.dragBehavior.onDragEndObservable.add(function(){m._dragging=!1,m._rotationDisplayPlane.setEnabled(!1)});var C={snapDistance:0},D=0,E=new g.a,F=new g.b;m.dragBehavior.onDragObservable.add(function(a){if(m.attachedNode){var b=new g.e(1,1,1),d=new g.b(0,0,0,1),f=new g.e(0,0,0);m._handlePivot(),m.attachedNode.getWorldMatrix().decompose(b,d,f);var h=a.dragPlanePoint.subtract(f).normalize(),i=p.subtract(f).normalize(),j=g.e.Cross(h,i);h=g.e.Dot(h,i);i=Math.atan2(j.length(),h);z.copyFrom(c),B.copyFrom(c),m.updateGizmoRotationToMatchAttachedMesh&&(d.toRotationMatrix(q),B=g.e.TransformCoordinates(z,q));h=!1;if(e.utilityLayerScene.activeCamera){var k=e.utilityLayerScene.activeCamera.position.subtract(f).normalize();g.e.Dot(k,B)>0&&(z.scaleInPlace(-1),B.scaleInPlace(-1),h=!0)}g.e.Dot(B,j)>0&&(i=-i);k=!1;if(0!=m.snapDistance)if(D+=i,Math.abs(D)>m.snapDistance){j=Math.floor(Math.abs(D)/m.snapDistance);D<0&&(j*=-1),D%=m.snapDistance,i=m.snapDistance*j,k=!0}else i=0;j=Math.sin(i/2);if(F.set(z.x*j,z.y*j,z.z*j,Math.cos(i/2)),E.determinant()>0){j=new g.e;F.toEulerAnglesToRef(j),g.b.RotationYawPitchRollToRef(j.y,-j.x,-j.z,F)}m.updateGizmoRotationToMatchAttachedMesh?d.multiplyToRef(F,d):F.multiplyToRef(d,d),m.attachedNode.getWorldMatrix().copyFrom(g.a.Compose(b,d,f)),p.copyFrom(a.dragPlanePoint),k&&(C.snapDistance=i,m.onSnapObservable.notifyObservers(C)),m._angles.y+=i,m.angle+=h?-i:i,m._rotationShaderMaterial.setVector3("angles",m._angles),m._matrixChanged()}});l=e._getSharedGizmoLight();l.includedOnlyMeshes=l.includedOnlyMeshes.concat(m._rootMesh.getChildMeshes(!1));var aa={colliderMeshes:[d],gizmoMeshes:[j],material:m._coloredMaterial,hoverMaterial:m._hoverMaterial,disableMaterial:m._disableMaterial,active:!1,dragBehavior:m.dragBehavior};return null===(i=m._parent)||void 0===i||i.addToAxisCache(m._gizmoMesh,aa),m._pointerObserver=e.utilityLayerScene.onPointerObservable.add(function(a){if(!m._customMeshSet&&(m.dragBehavior.maxDragAngle=b.MaxDragAngle,m._isHovered=!(-1==aa.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!m._parent)){a=aa.dragBehavior.enabled?m._isHovered||m._dragging?m._hoverMaterial:m._coloredMaterial:m._disableMaterial;m._setGizmoMeshMaterial(aa.gizmoMeshes,a)}}),m.dragBehavior.onEnabledObservable.add(function(a){m._setGizmoMeshMaterial(aa.gizmoMeshes,a?m._coloredMaterial:m._disableMaterial)}),m}return Object(l.d)(b,a),b.prototype._createGizmoMesh=function(a,b,c){var d=Ad("ignore",{diameter:.6,thickness:.03*b,tessellation:c},this.gizmoLayer.utilityLayerScene);d.visibility=0;b=Ad("",{diameter:.6,thickness:.005*b,tessellation:c},this.gizmoLayer.utilityLayerScene);return b.material=this._coloredMaterial,b.rotation.x=Math.PI/2,d.rotation.x=Math.PI/2,a.addChild(b),a.addChild(d),{rotationMesh:b,collider:d}},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedMesh=this._parent.attachedMesh):this.attachedMesh=null},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),this._gizmoMesh&&this._gizmoMesh.dispose(),this._rotationDisplayPlane&&this._rotationDisplayPlane.dispose(),this._rotationShaderMaterial&&this._rotationShaderMaterial.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()}),a.prototype.dispose.call(this)},b.MaxDragAngle=9*Math.PI/20,b._rotationGizmoVertexShader=" precision highp float; attribute vec3 position; attribute vec2 uv; uniform mat4 worldViewProjection; varying vec3 vPosition; varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }",b._rotationGizmoFragmentShader=" precision highp float; varying vec2 vUV; varying vec3 vPosition; uniform vec3 angles; #define twopi 6.283185307 void main(void) { vec2 uv = vUV - vec2(0.5); float angle = atan(uv.y, uv.x) + 3.141592; float delta = gl_FrontFacing ? angles.y : -angles.y; float begin = angles.x - delta * angles.z; float start = (begin < (begin + delta)) ? begin : (begin + delta); float end = (begin > (begin + delta)) ? begin : (begin + delta); float len = sqrt(dot(uv,uv)); float opacity = 1. - step(0.5, len); float base = abs(floor(start / twopi)) * twopi; start += base; end += base; float intensity = 0.; for (int i = 0; i < 5; i++) { intensity += max(step(start, angle) - step(end, angle), 0.); angle += twopi; } gl_FragColor = vec4(1.,1.,0., min(intensity * 0.25, 0.8)) * opacity; }",b}(sh.a),Ch=function(a){function b(b,c,d,e,i,j){void 0===b&&(b=th.a.DefaultUtilityLayer),void 0===c&&(c=32),void 0===d&&(d=!1),void 0===e&&(e=1);var k=a.call(this,b)||this;k.onDragStartObservable=new f.c,k.onDragEndObservable=new f.c,k._observables=[],k._gizmoAxisCache=new Map;var l=j&&j.xOptions&&j.xOptions.color?j.xOptions.color:h.a.Red().scale(.5),o=j&&j.yOptions&&j.yOptions.color?j.yOptions.color:h.a.Green().scale(.5),p=j&&j.zOptions&&j.zOptions.color?j.zOptions.color:h.a.Blue().scale(.5);return k.xGizmo=new Bh(new g.e(1,0,0),l,b,c,k,d,e),k.yGizmo=new Bh(new g.e(0,1,0),o,b,c,k,d,e),k.zGizmo=new Bh(new g.e(0,0,1),p,b,c,k,d,e),[k.xGizmo,k.yGizmo,k.zGizmo].forEach(function(a){j&&null!=j.updateScale&&(a.updateScale=j.updateScale),a.dragBehavior.onDragStartObservable.add(function(){k.onDragStartObservable.notifyObservers({})}),a.dragBehavior.onDragEndObservable.add(function(){k.onDragEndObservable.notifyObservers({})})}),k.attachedMesh=null,k.attachedNode=null,i?i.addToAxisCache(k._gizmoAxisCache):sh.a.GizmoAxisPointerObserver(b,k._gizmoAxisCache),k}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"attachedMesh",{get:function(){return this._meshAttached},set:function(a){this._meshAttached=a,this._nodeAttached=a,this._checkBillboardTransform(),[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){b.isEnabled?b.attachedMesh=a:b.attachedMesh=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"attachedNode",{get:function(){return this._nodeAttached},set:function(a){this._meshAttached=null,this._nodeAttached=a,this._checkBillboardTransform(),[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){b.isEnabled?b.attachedNode=a:b.attachedNode=null})},enumerable:!1,configurable:!0}),b.prototype._checkBillboardTransform=function(){this._nodeAttached&&this._nodeAttached.billboardMode&&!1},Object.defineProperty(b.prototype,"isHovered",{get:function(){var a=!1;return[this.xGizmo,this.yGizmo,this.zGizmo].forEach(function(b){a=a||b.isHovered}),a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"updateGizmoRotationToMatchAttachedMesh",{get:function(){return this.xGizmo.updateGizmoRotationToMatchAttachedMesh},set:function(a){this.xGizmo&&(this.xGizmo.updateGizmoRotationToMatchAttachedMesh=a,this.yGizmo.updateGizmoRotationToMatchAttachedMesh=a,this.zGizmo.updateGizmoRotationToMatchAttachedMesh=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapDistance",{get:function(){return this.xGizmo.snapDistance},set:function(a){this.xGizmo&&(this.xGizmo.snapDistance=a,this.yGizmo.snapDistance=a,this.zGizmo.snapDistance=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleRatio",{get:function(){return this.xGizmo.scaleRatio},set:function(a){this.xGizmo&&(this.xGizmo.scaleRatio=a,this.yGizmo.scaleRatio=a,this.zGizmo.scaleRatio=a)},enumerable:!1,configurable:!0}),b.prototype.addToAxisCache=function(a,b){this._gizmoAxisCache.set(a,b)},b.prototype.dispose=function(){var a=this;this.xGizmo.dispose(),this.yGizmo.dispose(),this.zGizmo.dispose(),this.onDragStartObservable.clear(),this.onDragEndObservable.clear(),this._observables.forEach(function(b){a.gizmoLayer.utilityLayerScene.onPointerObservable.remove(b)})},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)")},b}(sh.a),Dh=function(a){function b(c,d,e,i){void 0===d&&(d=h.a.Gray()),void 0===e&&(e=th.a.DefaultUtilityLayer),void 0===i&&(i=null);var j=a.call(this,e)||this;j._pointerObserver=null,j.snapDistance=0,j.onSnapObservable=new f.c,j._isEnabled=!1,j._parent=null,j._dragging=!1,j._parent=i,j._coloredMaterial=new pd.a("",e.utilityLayerScene),j._coloredMaterial.diffuseColor=d,j._coloredMaterial.specularColor=d.subtract(new h.a(.1,.1,.1)),j._hoverMaterial=new pd.a("",e.utilityLayerScene),j._hoverMaterial.diffuseColor=h.a.Yellow(),j._disableMaterial=new pd.a("",e.utilityLayerScene),j._disableMaterial.diffuseColor=h.a.Gray(),j._disableMaterial.alpha=.4,j._gizmoMesh=b._CreatePlane(e.utilityLayerScene,j._coloredMaterial),j._gizmoMesh.lookAt(j._rootMesh.position.add(c)),j._gizmoMesh.scaling.scaleInPlace(1/3),j._gizmoMesh.parent=j._rootMesh;var k=0,l=new g.e,o={snapDistance:0};j.dragBehavior=new ab.a({dragPlaneNormal:c}),j.dragBehavior.moveAttached=!1,j._rootMesh.addBehavior(j.dragBehavior),j.dragBehavior.onDragObservable.add(function(a){if(j.attachedNode){if(j._handlePivot(),0==j.snapDistance)j.attachedNode.getWorldMatrix().addTranslationFromFloats(a.delta.x,a.delta.y,a.delta.z);else if(k+=a.dragDistance,Math.abs(k)>j.snapDistance){var b=Math.floor(Math.abs(k)/j.snapDistance);k%=j.snapDistance,a.delta.normalizeToRef(l),l.scaleInPlace(j.snapDistance*b),j.attachedNode.getWorldMatrix().addTranslationFromFloats(l.x,l.y,l.z),o.snapDistance=j.snapDistance*b,j.onSnapObservable.notifyObservers(o)}j._matrixChanged()}}),j.dragBehavior.onDragStartObservable.add(function(){j._dragging=!0}),j.dragBehavior.onDragEndObservable.add(function(){j._dragging=!1});i=e._getSharedGizmoLight();i.includedOnlyMeshes=i.includedOnlyMeshes.concat(j._rootMesh.getChildMeshes(!1));var q={gizmoMeshes:j._gizmoMesh.getChildMeshes(),colliderMeshes:j._gizmoMesh.getChildMeshes(),material:j._coloredMaterial,hoverMaterial:j._hoverMaterial,disableMaterial:j._disableMaterial,active:!1,dragBehavior:j.dragBehavior};return null===(d=j._parent)||void 0===d||d.addToAxisCache(j._gizmoMesh,q),j._pointerObserver=e.utilityLayerScene.onPointerObservable.add(function(a){if(!j._customMeshSet&&(j._isHovered=!(-1==q.colliderMeshes.indexOf(null===(a=null==a?void 0:a.pickInfo)||void 0===a?void 0:a.pickedMesh)),!j._parent)){a=q.dragBehavior.enabled?j._isHovered||j._dragging?j._hoverMaterial:j._coloredMaterial:j._disableMaterial;j._setGizmoMeshMaterial(q.gizmoMeshes,a)}}),j.dragBehavior.onEnabledObservable.add(function(a){j._setGizmoMeshMaterial(q.gizmoMeshes,a?j._coloredMaterial:j._disableMaterial)}),j}return Object(l.d)(b,a),b._CreatePlane=function(a,b){var c=new fb.a("plane",a);a=Object(Ah.a)("dragPlane",{width:.1375,height:.1375,sideOrientation:2},a);return a.material=b,a.parent=c,c},b.prototype._attachedNodeChanged=function(a){this.dragBehavior&&(this.dragBehavior.enabled=!!a)},Object.defineProperty(b.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a,a?this._parent&&(this.attachedNode=this._parent.attachedNode):this.attachedNode=null},enumerable:!1,configurable:!0}),b.prototype.dispose=function(){this.onSnapObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this.dragBehavior.detach(),a.prototype.dispose.call(this),this._gizmoMesh&&this._gizmoMesh.dispose(),[this._coloredMaterial,this._hoverMaterial,this._disableMaterial].forEach(function(a){a&&a.dispose()})},b}(sh.a),Eh=function(a){function b(b,c,d){void 0===b&&(b=th.a.DefaultUtilityLayer),void 0===c&&(c=1);var e=a.call(this,b)||this;return e._meshAttached=null,e._nodeAttached=null,e._observables=[],e._gizmoAxisCache=new Map,e.onDragStartObservable=new f.c,e.onDragEndObservable=new f.c,e._planarGizmoEnabled=!1,e.xGizmo=new qh.a(new g.e(1,0,0),h.a.Red().scale(.5),b,e,c),e.yGizmo=new qh.a(new g.e(0,1,0),h.a.Green().scale(.5),b,e,c),e.zGizmo=new qh.a(new g.e(0,0,1),h.a.Blue().scale(.5),b,e,c),e.xPlaneGizmo=new Dh(new g.e(1,0,0),h.a.Red().scale(.5),e.gizmoLayer,e),e.yPlaneGizmo=new Dh(new g.e(0,1,0),h.a.Green().scale(.5),e.gizmoLayer,e),e.zPlaneGizmo=new Dh(new g.e(0,0,1),h.a.Blue().scale(.5),e.gizmoLayer,e),[e.xGizmo,e.yGizmo,e.zGizmo,e.xPlaneGizmo,e.yPlaneGizmo,e.zPlaneGizmo].forEach(function(a){a.dragBehavior.onDragStartObservable.add(function(){e.onDragStartObservable.notifyObservers({})}),a.dragBehavior.onDragEndObservable.add(function(){e.onDragEndObservable.notifyObservers({})})}),e.attachedMesh=null,d?d.addToAxisCache(e._gizmoAxisCache):sh.a.GizmoAxisPointerObserver(b,e._gizmoAxisCache),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"attachedMesh",{get:function(){return this._meshAttached},set:function(a){this._meshAttached=a,this._nodeAttached=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b.isEnabled?b.attachedMesh=a:b.attachedMesh=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"attachedNode",{get:function(){return this._nodeAttached},set:function(a){this._meshAttached=null,this._nodeAttached=null,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b.isEnabled?b.attachedNode=a:b.attachedNode=null})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isHovered",{get:function(){var a=!1;return[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){a=a||b.isHovered}),a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"planarGizmoEnabled",{get:function(){return this._planarGizmoEnabled},set:function(a){var b=this;this._planarGizmoEnabled=a,[this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(c){c&&(c.isEnabled=a,a&&(c.attachedMesh?c.attachedMesh=b.attachedMesh:c.attachedNode=b.attachedNode))},this)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"updateGizmoRotationToMatchAttachedMesh",{get:function(){return this._updateGizmoRotationToMatchAttachedMesh},set:function(a){this._updateGizmoRotationToMatchAttachedMesh=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.updateGizmoRotationToMatchAttachedMesh=a)})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapDistance",{get:function(){return this._snapDistance},set:function(a){this._snapDistance=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.snapDistance=a)})},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"scaleRatio",{get:function(){return this._scaleRatio},set:function(a){this._scaleRatio=a,[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(b){b&&(b.scaleRatio=a)})},enumerable:!1,configurable:!0}),b.prototype.addToAxisCache=function(a,b){this._gizmoAxisCache.set(a,b)},b.prototype.dispose=function(){var a=this;[this.xGizmo,this.yGizmo,this.zGizmo,this.xPlaneGizmo,this.yPlaneGizmo,this.zPlaneGizmo].forEach(function(a){a&&a.dispose()}),this._observables.forEach(function(b){a.gizmoLayer.utilityLayerScene.onPointerObservable.remove(b)}),this.onDragStartObservable.clear(),this.onDragEndObservable.clear()},b.prototype.setCustomMesh=function(a){q.a.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo,gizmo.xPlaneGizmo, gizmo.yPlaneGizmo, gizmo.zPlaneGizmo)")},b}(sh.a);function Fh(a){var b=[];b[0]={vertex:[[0,0,1.732051],[1.632993,0,-.5773503],[-.8164966,1.414214,-.5773503],[-.8164966,-1.414214,-.5773503]],face:[[0,1,2],[0,2,3],[0,3,1],[1,3,2]]},b[1]={vertex:[[0,0,1.414214],[1.414214,0,0],[0,1.414214,0],[-1.414214,0,0],[0,-1.414214,0],[0,0,-1.414214]],face:[[0,1,2],[0,2,3],[0,3,4],[0,4,1],[1,4,5],[1,5,2],[2,5,3],[3,5,4]]},b[2]={vertex:[[0,0,1.070466],[.7136442,0,.7978784],[-.3568221,.618034,.7978784],[-.3568221,-.618034,.7978784],[.7978784,.618034,.3568221],[.7978784,-.618034,.3568221],[-.9341724,.381966,.3568221],[.1362939,1,.3568221],[.1362939,-1,.3568221],[-.9341724,-.381966,.3568221],[.9341724,.381966,-.3568221],[.9341724,-.381966,-.3568221],[-.7978784,.618034,-.3568221],[-.1362939,1,-.3568221],[-.1362939,-1,-.3568221],[-.7978784,-.618034,-.3568221],[.3568221,.618034,-.7978784],[.3568221,-.618034,-.7978784],[-.7136442,0,-.7978784],[0,0,-1.070466]],face:[[0,1,4,7,2],[0,2,6,9,3],[0,3,8,5,1],[1,5,11,10,4],[2,7,13,12,6],[3,9,15,14,8],[4,10,16,13,7],[5,8,14,17,11],[6,12,18,15,9],[10,11,17,19,16],[12,13,16,19,18],[14,15,18,19,17]]},b[3]={vertex:[[0,0,1.175571],[1.051462,0,.5257311],[.3249197,1,.5257311],[-.8506508,.618034,.5257311],[-.8506508,-.618034,.5257311],[.3249197,-1,.5257311],[.8506508,.618034,-.5257311],[.8506508,-.618034,-.5257311],[-.3249197,1,-.5257311],[-1.051462,0,-.5257311],[-.3249197,-1,-.5257311],[0,0,-1.175571]],face:[[0,1,2],[0,2,3],[0,3,4],[0,4,5],[0,5,1],[1,5,7],[1,7,6],[1,6,2],[2,6,8],[2,8,3],[3,8,9],[3,9,4],[4,9,10],[4,10,5],[5,10,7],[6,7,11],[6,11,8],[7,10,11],[8,11,9],[9,11,10]]},b[4]={vertex:[[0,0,1.070722],[.7148135,0,.7971752],[-.104682,.7071068,.7971752],[-.6841528,.2071068,.7971752],[-.104682,-.7071068,.7971752],[.6101315,.7071068,.5236279],[1.04156,.2071068,.1367736],[.6101315,-.7071068,.5236279],[-.3574067,1,.1367736],[-.7888348,-.5,.5236279],[-.9368776,.5,.1367736],[-.3574067,-1,.1367736],[.3574067,1,-.1367736],[.9368776,-.5,-.1367736],[.7888348,.5,-.5236279],[.3574067,-1,-.1367736],[-.6101315,.7071068,-.5236279],[-1.04156,-.2071068,-.1367736],[-.6101315,-.7071068,-.5236279],[.104682,.7071068,-.7971752],[.6841528,-.2071068,-.7971752],[.104682,-.7071068,-.7971752],[-.7148135,0,-.7971752],[0,0,-1.070722]],face:[[0,2,3],[1,6,5],[4,9,11],[7,15,13],[8,16,10],[12,14,19],[17,22,18],[20,21,23],[0,1,5,2],[0,3,9,4],[0,4,7,1],[1,7,13,6],[2,5,12,8],[2,8,10,3],[3,10,17,9],[4,11,15,7],[5,6,14,12],[6,13,20,14],[8,12,19,16],[9,17,18,11],[10,16,22,17],[11,18,21,15],[13,15,21,20],[14,20,23,19],[16,19,23,22],[18,22,23,21]]},b[5]={vertex:[[0,0,1.322876],[1.309307,0,.1889822],[-.9819805,.8660254,.1889822],[.1636634,-1.299038,.1889822],[.3273268,.8660254,-.9449112],[-.8183171,-.4330127,-.9449112]],face:[[0,3,1],[2,4,5],[0,1,4,2],[0,2,5,3],[1,3,5,4]]},b[6]={vertex:[[0,0,1.159953],[1.013464,0,.5642542],[-.3501431,.9510565,.5642542],[-.7715208,-.6571639,.5642542],[.6633206,.9510565,-.03144481],[.8682979,-.6571639,-.3996071],[-1.121664,.2938926,-.03144481],[-.2348831,-1.063314,-.3996071],[.5181548,.2938926,-.9953061],[-.5850262,-.112257,-.9953061]],face:[[0,1,4,2],[0,2,6,3],[1,5,8,4],[3,6,9,7],[5,7,9,8],[0,3,7,5,1],[2,4,8,9,6]]},b[7]={vertex:[[0,0,1.118034],[.8944272,0,.6708204],[-.2236068,.8660254,.6708204],[-.7826238,-.4330127,.6708204],[.6708204,.8660254,.2236068],[1.006231,-.4330127,-.2236068],[-1.006231,.4330127,.2236068],[-.6708204,-.8660254,-.2236068],[.7826238,.4330127,-.6708204],[.2236068,-.8660254,-.6708204],[-.8944272,0,-.6708204],[0,0,-1.118034]],face:[[0,1,4,2],[0,2,6,3],[1,5,8,4],[3,6,10,7],[5,9,11,8],[7,10,11,9],[0,3,7,9,5,1],[2,4,8,11,10,6]]},b[8]={vertex:[[-.729665,.670121,.319155],[-.655235,-.29213,-.754096],[-.093922,-.607123,.537818],[.702196,.595691,.485187],[.776626,-.36656,-.588064]],face:[[1,4,2],[0,1,2],[3,0,2],[4,3,2],[4,1,0,3]]},b[9]={vertex:[[-.868849,-.100041,.61257],[-.329458,.976099,.28078],[-.26629,-.013796,-.477654],[-.13392,-1.034115,.229829],[.738834,.707117,-.307018],[.859683,-.535264,-.338508]],face:[[3,0,2],[5,3,2],[4,5,2],[1,4,2],[0,1,2],[0,3,5,4,1]]},b[10]={vertex:[[-.610389,.243975,.531213],[-.187812,-.48795,-.664016],[-.187812,.9759,-.664016],[.187812,-.9759,.664016],[.798201,.243975,.132803]],face:[[1,3,0],[3,4,0],[3,1,4],[0,2,1],[0,4,2],[2,4,1]]},b[11]={vertex:[[-1.028778,.392027,-.048786],[-.640503,-.646161,.621837],[-.125162,-.395663,-.540059],[.004683,.888447,-.651988],[.125161,.395663,.540059],[.632925,-.791376,.433102],[1.031672,.157063,-.354165]],face:[[3,2,0],[2,1,0],[2,5,1],[0,4,3],[0,1,4],[4,1,5],[2,3,6],[3,4,6],[5,2,6],[4,5,6]]},b[12]={vertex:[[-.669867,.334933,-.529576],[-.669867,.334933,.529577],[-.4043,1.212901,0],[-.334933,-.669867,-.529576],[-.334933,-.669867,.529577],[.334933,.669867,-.529576],[.334933,.669867,.529577],[.4043,-1.212901,0],[.669867,-.334933,-.529576],[.669867,-.334933,.529577]],face:[[8,9,7],[6,5,2],[3,8,7],[5,0,2],[4,3,7],[0,1,2],[9,4,7],[1,6,2],[9,8,5,6],[8,3,0,5],[3,4,1,0],[4,9,6,1]]},b[13]={vertex:[[-.931836,.219976,-.264632],[-.636706,.318353,.692816],[-.613483,-.735083,-.264632],[-.326545,.979634,0],[-.318353,-.636706,.692816],[-.159176,.477529,-.856368],[.159176,-.477529,-.856368],[.318353,.636706,.692816],[.326545,-.979634,0],[.613482,.735082,-.264632],[.636706,-.318353,.692816],[.931835,-.219977,-.264632]],face:[[11,10,8],[7,9,3],[6,11,8],[9,5,3],[2,6,8],[5,0,3],[4,2,8],[0,1,3],[10,4,8],[1,7,3],[10,11,9,7],[11,6,5,9],[6,2,0,5],[2,4,1,0],[4,10,7,1]]},b[14]={vertex:[[-.93465,.300459,-.271185],[-.838689,-.260219,-.516017],[-.711319,.717591,.128359],[-.710334,-.156922,.080946],[-.599799,.556003,-.725148],[-.503838,-.004675,-.969981],[-.487004,.26021,.48049],[-.460089,-.750282,-.512622],[-.376468,.973135,-.325605],[-.331735,-.646985,.084342],[-.254001,.831847,.530001],[-.125239,-.494738,-.966586],[.029622,.027949,.730817],[.056536,-.982543,-.262295],[.08085,1.087391,.076037],[.125583,-.532729,.485984],[.262625,.599586,.780328],[.391387,-.726999,-.716259],[.513854,-.868287,.139347],[.597475,.85513,.326364],[.641224,.109523,.783723],[.737185,-.451155,.538891],[.848705,-.612742,-.314616],[.976075,.365067,.32976],[1.072036,-.19561,.084927]],face:[[15,18,21],[12,20,16],[6,10,2],[3,0,1],[9,7,13],[2,8,4,0],[0,4,5,1],[1,5,11,7],[7,11,17,13],[13,17,22,18],[18,22,24,21],[21,24,23,20],[20,23,19,16],[16,19,14,10],[10,14,8,2],[15,9,13,18],[12,15,21,20],[6,12,16,10],[3,6,2,0],[9,3,1,7],[9,15,12,6,3],[22,17,11,5,4,8,14,19,23,24]]};var c,d,e,f,i,j=a.type&&(a.type<0||a.type>=b.length)?0:a.type||0,k=a.size,l=a.sizeX||k||1,o=a.sizeY||k||1;k=a.sizeZ||k||1;b=a.custom||b[j];j=b.face.length;var p=a.faceUV||new Array(j),q=a.faceColors,aa=void 0===a.flat||a.flat,G=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,H=new Array,I=new Array,J=new Array,K=new Array,ba=new Array,L=0,M=0,N=new Array,ca=0,da=0;if(aa)for(da=0;da0&&a.forEach(function(a,c){b._gizmoAxisCache.set(c,a)})},a.prototype.dispose=function(){var a=this;for(var b in this._pointerObservers.forEach(function(b){a.scene.onPointerObservable.remove(b)}),this.gizmos){var c=this.gizmos[b];c&&c.dispose()}this._defaultKeepDepthUtilityLayer!==th.a._DefaultKeepDepthUtilityLayer&&(null===(c=this._defaultKeepDepthUtilityLayer)||void 0===c||c.dispose()),this._defaultUtilityLayer!==th.a._DefaultUtilityLayer&&(null===(b=this._defaultUtilityLayer)||void 0===b||b.dispose()),this.boundingBoxDragBehavior.detach(),this.onAttachedToMeshObservable.clear()},a}(),Kh=c(52),Lh=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._needProjectionMatrixCompute=!0,b}return Object(l.d)(b,a),b.prototype._setPosition=function(a){this._position=a},Object.defineProperty(b.prototype,"position",{get:function(){return this._position},set:function(a){this._setPosition(a)},enumerable:!1,configurable:!0}),b.prototype._setDirection=function(a){this._direction=a},Object.defineProperty(b.prototype,"direction",{get:function(){return this._direction},set:function(a){this._setDirection(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowMinZ",{get:function(){return this._shadowMinZ},set:function(a){this._shadowMinZ=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowMaxZ",{get:function(){return this._shadowMaxZ},set:function(a){this._shadowMaxZ=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),b.prototype.computeTransformedInformation=function(){return!(!this.parent||!this.parent.getWorldMatrix)&&(this.transformedPosition||(this.transformedPosition=g.e.Zero()),g.e.TransformCoordinatesToRef(this.position,this.parent.getWorldMatrix(),this.transformedPosition),this.direction&&(this.transformedDirection||(this.transformedDirection=g.e.Zero()),g.e.TransformNormalToRef(this.direction,this.parent.getWorldMatrix(),this.transformedDirection)),!0)},b.prototype.getDepthScale=function(){return 50},b.prototype.getShadowDirection=function(a){return this.transformedDirection?this.transformedDirection:this.direction},b.prototype.getAbsolutePosition=function(){return this.transformedPosition?this.transformedPosition:this.position},b.prototype.setDirectionToTarget=function(a){return this.direction=g.e.Normalize(a.subtract(this.position)),this.direction},b.prototype.getRotation=function(){this.direction.normalize();var a=g.e.Cross(this.direction,Q.a.Y),b=g.e.Cross(a,this.direction);return g.e.RotationFromAxis(a,b,this.direction)},b.prototype.needCube=function(){return!1},b.prototype.needProjectionMatrixCompute=function(){return this._needProjectionMatrixCompute},b.prototype.forceProjectionMatrixCompute=function(){this._needProjectionMatrixCompute=!0},b.prototype._initCache=function(){a.prototype._initCache.call(this),this._cache.position=g.e.Zero()},b.prototype._isSynchronized=function(){return!!this._cache.position.equals(this.position)},b.prototype.computeWorldMatrix=function(a){return!a&&this.isSynchronized()?(this._currentRenderId=this.getScene().getRenderId(),this._worldMatrix):(this._updateCache(),this._cache.position.copyFrom(this.position),this._worldMatrix||(this._worldMatrix=g.a.Identity()),g.a.TranslationToRef(this.position.x,this.position.y,this.position.z,this._worldMatrix),this.parent&&this.parent.getWorldMatrix&&(this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(),this._worldMatrix),this._markSyncedWithParent()),this._worldMatrixDeterminantIsDirty=!0,this._worldMatrix)},b.prototype.getDepthMinZ=function(a){return void 0!==this.shadowMinZ?this.shadowMinZ:a.minZ},b.prototype.getDepthMaxZ=function(a){return void 0!==this.shadowMaxZ?this.shadowMaxZ:a.maxZ},b.prototype.setShadowProjectionMatrix=function(a,b,c){return this.customProjectionMatrixBuilder?this.customProjectionMatrixBuilder(b,c,a):this._setDefaultShadowProjectionMatrix(a,b,c),this},Object(l.c)([Object(I.p)()],b.prototype,"position",null),Object(l.c)([Object(I.p)()],b.prototype,"direction",null),Object(l.c)([Object(I.d)()],b.prototype,"shadowMinZ",null),Object(l.c)([Object(I.d)()],b.prototype,"shadowMaxZ",null),b}(Kh.a);K.a.AddNodeConstructor("Light_Type_1",function(a,b){return function(){return new Mh(a,g.e.Zero(),b)}});var Mh=function(a){function b(b,c,d){b=a.call(this,b,d)||this;return b._shadowFrustumSize=0,b._shadowOrthoScale=.1,b.autoUpdateExtends=!0,b.autoCalcShadowZBounds=!1,b._orthoLeft=Number.MAX_VALUE,b._orthoRight=Number.MIN_VALUE,b._orthoTop=Number.MIN_VALUE,b._orthoBottom=Number.MAX_VALUE,b.position=c.scale(-1),b.direction=c,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"shadowFrustumSize",{get:function(){return this._shadowFrustumSize},set:function(a){this._shadowFrustumSize=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"shadowOrthoScale",{get:function(){return this._shadowOrthoScale},set:function(a){this._shadowOrthoScale=a,this.forceProjectionMatrixCompute()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoLeft",{get:function(){return this._orthoLeft},set:function(a){this._orthoLeft=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoRight",{get:function(){return this._orthoRight},set:function(a){this._orthoRight=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoTop",{get:function(){return this._orthoTop},set:function(a){this._orthoTop=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"orthoBottom",{get:function(){return this._orthoBottom},set:function(a){this._orthoBottom=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"DirectionalLight"},b.prototype.getTypeID=function(){return Kh.a.LIGHTTYPEID_DIRECTIONALLIGHT},b.prototype._setDefaultShadowProjectionMatrix=function(a,b,c){this.shadowFrustumSize>0?this._setDefaultFixedFrustumShadowProjectionMatrix(a):this._setDefaultAutoExtendShadowProjectionMatrix(a,b,c)},b.prototype._setDefaultFixedFrustumShadowProjectionMatrix=function(a){var b=this.getScene().activeCamera;b&&g.a.OrthoLHToRef(this.shadowFrustumSize,this.shadowFrustumSize,void 0!==this.shadowMinZ?this.shadowMinZ:b.minZ,void 0!==this.shadowMaxZ?this.shadowMaxZ:b.maxZ,a,this.getScene().getEngine().isNDCHalfZRange)},b.prototype._setDefaultAutoExtendShadowProjectionMatrix=function(a,b,c){var d=this.getScene().activeCamera;if(d){if(this.autoUpdateExtends||this._orthoLeft===Number.MAX_VALUE){var e=g.e.Zero();this._orthoLeft=Number.MAX_VALUE,this._orthoRight=Number.MIN_VALUE,this._orthoTop=Number.MIN_VALUE,this._orthoBottom=Number.MAX_VALUE;for(var f=Number.MAX_VALUE,h=Number.MIN_VALUE,i=0;ithis._orthoRight&&(this._orthoRight=e.x),e.y>this._orthoTop&&(this._orthoTop=e.y),this.autoCalcShadowZBounds&&(e.zh&&(h=e.z))}this.autoCalcShadowZBounds&&(this._shadowMinZ=f,this._shadowMaxZ=h)}k=this._orthoRight-this._orthoLeft;j=this._orthoTop-this._orthoBottom;i=void 0!==this.shadowMinZ?this.shadowMinZ:d.minZ;c=void 0!==this.shadowMaxZ?this.shadowMaxZ:d.maxZ;e=this.getScene().getEngine().useReverseDepthBuffer;g.a.OrthoOffCenterLHToRef(this._orthoLeft-k*this.shadowOrthoScale,this._orthoRight+k*this.shadowOrthoScale,this._orthoBottom-j*this.shadowOrthoScale,this._orthoTop+j*this.shadowOrthoScale,e?c:i,e?i:c,a,this.getScene().getEngine().isNDCHalfZRange)}},b.prototype._buildUniformLayout=function(){this._uniformBuffer.addUniform("vLightData",4),this._uniformBuffer.addUniform("vLightDiffuse",4),this._uniformBuffer.addUniform("vLightSpecular",4),this._uniformBuffer.addUniform("shadowsInfo",3),this._uniformBuffer.addUniform("depthValues",2),this._uniformBuffer.create()},b.prototype.transferToEffect=function(a,b){return this.computeTransformedInformation()?(this._uniformBuffer.updateFloat4("vLightData",this.transformedDirection.x,this.transformedDirection.y,this.transformedDirection.z,1,b),this):(this._uniformBuffer.updateFloat4("vLightData",this.direction.x,this.direction.y,this.direction.z,1,b),this)},b.prototype.transferToNodeMaterialEffect=function(a,b){return this.computeTransformedInformation()?(a.setFloat3(b,this.transformedDirection.x,this.transformedDirection.y,this.transformedDirection.z),this):(a.setFloat3(b,this.direction.x,this.direction.y,this.direction.z),this)},b.prototype.getDepthMinZ=function(a){a=this._scene.getEngine();return!a.useReverseDepthBuffer&&a.isNDCHalfZRange?0:1},b.prototype.getDepthMaxZ=function(a){a=this._scene.getEngine();return a.useReverseDepthBuffer&&a.isNDCHalfZRange?0:1},b.prototype.prepareLightSpecificDefines=function(a,b){a["DIRLIGHT"+b]=!0},Object(l.c)([Object(I.d)()],b.prototype,"shadowFrustumSize",null),Object(l.c)([Object(I.d)()],b.prototype,"shadowOrthoScale",null),Object(l.c)([Object(I.d)()],b.prototype,"autoUpdateExtends",void 0),Object(l.c)([Object(I.d)()],b.prototype,"autoCalcShadowZBounds",void 0),Object(l.c)([Object(I.d)("orthoLeft")],b.prototype,"_orthoLeft",void 0),Object(l.c)([Object(I.d)("orthoRight")],b.prototype,"_orthoRight",void 0),Object(l.c)([Object(I.d)("orthoTop")],b.prototype,"_orthoTop",void 0),Object(l.c)([Object(I.d)("orthoBottom")],b.prototype,"_orthoBottom",void 0),b}(Lh);function Nh(a){var b=new Array,c=new Array,d=new Array,e=new Array,f=a.radius||.5,g=a.tessellation||64,h=a.arc&&(a.arc<=0||a.arc>1)?1:a.arc||1,i=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE;b.push(0,0,0),e.push(.5,.5);for(var j=2*Math.PI*h,j=1===h?j/g:j/(g-1),k=0,l=0;l1e-4){b=this.attachedMesh.forward;this._light.direction=new g.e(b.x,b.y,b.z),this._cachedForward.copyFrom(this.attachedMesh.forward)}else g.e.DistanceSquared(this.attachedMesh.forward,this._light.direction)>1e-4&&(this.attachedMesh.setDirection(this._light.direction),this.attachedMesh.computeWorldMatrix(!0),this._cachedForward.copyFrom(this.attachedMesh.forward))}},b.prototype.dispose=function(){this.onClickedObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this._material.dispose(),a.prototype.dispose.call(this),this._attachedMeshParent.dispose()},b._CreateHemisphericLightMesh=function(a){var c=new R.a("hemisphereLight",a),d=Qh(c.name,{segments:10,diameter:1},a);return d.position.z=-.15,d.rotation.x=Math.PI/2,d.parent=c,this._CreateLightLines(3,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreatePointLightMesh=function(a){var c=new R.a("pointLight",a),d=Object(vh.a)(c.name,{segments:10,diameter:1},a);return d.rotation.x=Math.PI/2,d.parent=c,this._CreateLightLines(5,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreateSpotLightMesh=function(a){var c=new R.a("spotLight",a);Object(vh.a)(c.name,{segments:10,diameter:1},a).parent=c;var d=Qh(c.name,{segments:10,diameter:2},a);return d.parent=c,d.rotation.x=-Math.PI/2,this._CreateLightLines(2,a).parent=c,c.scaling.scaleInPlace(b._Scale),c.rotation.x=Math.PI/2,c},b._CreateDirectionalLightMesh=function(a){var c=new R.a("directionalLight",a),d=new R.a(c.name,a);d.parent=c,Object(vh.a)(c.name,{diameter:1.2,segments:10},a).parent=d;var e=Object(xd.a)(c.name,{updatable:!1,height:6,diameterTop:.3,diameterBottom:.3,tessellation:6,subdivisions:1},a);e.parent=d,(f=e.clone(c.name)).scaling.y=.5,f.position.x+=1.25,(e=e.clone(c.name)).scaling.y=.5,e.position.x+=-1.25;var f;a=Object(xd.a)(c.name,{updatable:!1,height:1,diameterTop:0,diameterBottom:.6,tessellation:6,subdivisions:1},a);return a.position.y+=3,a.parent=d,(f=a.clone(c.name)).position.y=1.5,f.position.x+=1.25,(e=a.clone(c.name)).position.y=1.5,e.position.x+=-1.25,d.scaling.scaleInPlace(b._Scale),d.rotation.z=Math.PI/2,d.rotation.y=Math.PI/2,c},b._Scale=.007,b._CreateLightLines=function(a,b){var c=new R.a("root",b);c.rotation.x=Math.PI/2;var d=new R.a("linePivot",b);d.parent=c;b=Object(xd.a)("line",{updatable:!1,height:2,diameterTop:.2,diameterBottom:.3,tessellation:6,subdivisions:1},b);if(b.position.y=b.scaling.y/2+1.2,b.parent=d,a<2)return d;for(b=0;b<4;b++)(e=d.clone("lineParentClone")).rotation.z=Math.PI/4,e.rotation.y=Math.PI/2+Math.PI/2*b,e.getChildMeshes()[0].scaling.y=.5,e.getChildMeshes()[0].scaling.x=e.getChildMeshes()[0].scaling.z=.8,e.getChildMeshes()[0].position.y=e.getChildMeshes()[0].scaling.y/2+1.2;if(a<3)return c;for(b=0;b<4;b++)(e=d.clone("linePivotClone")).rotation.z=Math.PI/2,e.rotation.y=Math.PI/2*b;if(a<4)return c;for(b=0;b<4;b++){var e;(e=d.clone("linePivotClone")).rotation.z=Math.PI+Math.PI/4,e.rotation.y=Math.PI/2+Math.PI/2*b,e.getChildMeshes()[0].scaling.y=.5,e.getChildMeshes()[0].scaling.x=e.getChildMeshes()[0].scaling.z=.8,e.getChildMeshes()[0].position.y=e.getChildMeshes()[0].scaling.y/2+1.2}return a<5||((e=d.clone("linePivotClone")).rotation.z=Math.PI),c},b}(sh.a),Uh=function(a){function b(b){void 0===b&&(b=th.a.DefaultUtilityLayer);var c=a.call(this,b)||this;return c._pointerObserver=null,c.onClickedObservable=new f.c,c._camera=null,c._invProjection=new g.a,c._material=new pd.a("cameraGizmoMaterial",c.gizmoLayer.utilityLayerScene),c._material.diffuseColor=new h.a(.5,.5,.5),c._material.specularColor=new h.a(.1,.1,.1),c._pointerObserver=b.utilityLayerScene.onPointerObservable.add(function(a){c._camera&&(c._isHovered=!(!a.pickInfo||-1==c._rootMesh.getChildMeshes().indexOf(a.pickInfo.pickedMesh)),c._isHovered&&0===a.event.button&&c.onClickedObservable.notifyObservers(c._camera))},Ua.a.POINTERDOWN),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"displayFrustum",{get:function(){return this._cameraLinesMesh.isEnabled()},set:function(a){this._cameraLinesMesh.setEnabled(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"camera",{get:function(){return this._camera},set:function(a){var c=this;if(this._camera=a,this.attachedNode=a,a){this._cameraMesh&&this._cameraMesh.dispose(),this._cameraLinesMesh&&this._cameraLinesMesh.dispose(),this._cameraMesh=b._CreateCameraMesh(this.gizmoLayer.utilityLayerScene),this._cameraLinesMesh=b._CreateCameraFrustum(this.gizmoLayer.utilityLayerScene),this._cameraMesh.getChildMeshes(!1).forEach(function(a){a.material=c._material}),this._cameraMesh.parent=this._rootMesh,this._cameraLinesMesh.parent=this._rootMesh,this.gizmoLayer.utilityLayerScene.activeCamera&&this.gizmoLayer.utilityLayerScene.activeCamera.maxZ<1.5*a.maxZ&&(this.gizmoLayer.utilityLayerScene.activeCamera.maxZ=1.5*a.maxZ),this.attachedNode.reservedDataStore||(this.attachedNode.reservedDataStore={}),this.attachedNode.reservedDataStore.cameraGizmo=this;a=this.gizmoLayer._getSharedGizmoLight();a.includedOnlyMeshes=a.includedOnlyMeshes.concat(this._cameraMesh.getChildMeshes(!1)),this._update()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"material",{get:function(){return this._material},enumerable:!1,configurable:!0}),b.prototype._update=function(){a.prototype._update.call(this),this._camera&&(this._camera.getProjectionMatrix().invertToRef(this._invProjection),this._cameraLinesMesh.setPivotMatrix(this._invProjection,!1),this._cameraLinesMesh.scaling.x=1/this._rootMesh.scaling.x,this._cameraLinesMesh.scaling.y=1/this._rootMesh.scaling.y,this._cameraLinesMesh.scaling.z=1/this._rootMesh.scaling.z,this._cameraMesh.parent=null,this._cameraMesh.rotation.y=.5*Math.PI*(this._camera.getScene().useRightHandedSystem?1:-1),this._cameraMesh.parent=this._rootMesh)},b.prototype.dispose=function(){this.onClickedObservable.clear(),this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver),this._cameraMesh&&this._cameraMesh.dispose(),this._cameraLinesMesh&&this._cameraLinesMesh.dispose(),this._material.dispose(),a.prototype.dispose.call(this)},b._CreateCameraMesh=function(a){var c=new R.a("rootCameraGizmo",a),d=new R.a(c.name,a);d.parent=c,Object(rh.b)(c.name,{width:1,height:.8,depth:.5},a).parent=d;var e=Object(xd.a)(c.name,{height:.5,diameterTop:.8,diameterBottom:.8},a);e.parent=d,e.position.y=.3,e.position.x=-.6,e.rotation.x=.5*Math.PI;e=Object(xd.a)(c.name,{height:.5,diameterTop:.6,diameterBottom:.6},a);e.parent=d,e.position.y=.5,e.position.x=.4,e.rotation.x=.5*Math.PI;e=Object(xd.a)(c.name,{height:.5,diameterTop:.5,diameterBottom:.5},a);return e.parent=d,e.position.y=0,e.position.x=.6,e.rotation.z=.5*Math.PI,c.scaling.scaleInPlace(b._Scale),d.position.x=-.9,c},b._CreateCameraFrustum=function(a){var b=new R.a("rootCameraGizmo",a),c=new R.a(b.name,a);c.parent=b;for(var d=0;d<4;d+=2)for(var e=0;e<4;e+=2){var f;(f=Object(wh.e)("lines",{points:[new g.e(-1+e,-1+d,-1),new g.e(-1+e,-1+d,1)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1,(f=Object(wh.e)("lines",{points:[new g.e(-1,-1+e,-1+d),new g.e(1,-1+e,-1+d)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1,(f=Object(wh.e)("lines",{points:[new g.e(-1+e,-1,-1+d),new g.e(-1+e,1,-1+d)]},a)).parent=c,f.alwaysSelectAsActiveMesh=!0,f.isPickable=!1}return b},b._Scale=.05,b}(sh.a);X.a.IncludesShadersStore.kernelBlurVaryingDeclaration="varying vec2 sampleCoord{X};";V="vec4 pack(float depth) { const vec4 bit_shift=vec4(255.0*255.0*255.0,255.0*255.0,255.0,1.0); const vec4 bit_mask=vec4(0.0,1.0/255.0,1.0/255.0,1.0/255.0); vec4 res=fract(depth*bit_shift); res-=res.xxyz*bit_mask; return res; } float unpack(vec4 color) { const vec4 bit_shift=vec4(1.0/(255.0*255.0*255.0),1.0/(255.0*255.0),1.0/255.0,1.0); return dot(color,bit_shift); }";X.a.IncludesShadersStore.packingFunctions=V;a="#ifdef DOF factor=sampleCoC(sampleCoord{X}); computedWeight=KERNEL_WEIGHT{X}*factor; sumOfWeights+=computedWeight; #else computedWeight=KERNEL_WEIGHT{X}; #endif #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCoord{X}))*computedWeight; #else blend+=texture2D(textureSampler,sampleCoord{X})*computedWeight; #endif";X.a.IncludesShadersStore.kernelBlurFragment=a;V="#ifdef DOF factor=sampleCoC(sampleCenter+delta*KERNEL_DEP_OFFSET{X}); computedWeight=KERNEL_DEP_WEIGHT{X}*factor; sumOfWeights+=computedWeight; #else computedWeight=KERNEL_DEP_WEIGHT{X}; #endif #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCenter+delta*KERNEL_DEP_OFFSET{X}))*computedWeight; #else blend+=texture2D(textureSampler,sampleCenter+delta*KERNEL_DEP_OFFSET{X})*computedWeight; #endif";X.a.IncludesShadersStore.kernelBlurFragment2=V;a=" uniform sampler2D textureSampler; uniform vec2 delta; varying vec2 sampleCenter; #ifdef DOF uniform sampler2D circleOfConfusionSampler; uniform vec2 cameraMinMaxZ; float sampleDistance(in vec2 offset) { float depth=texture2D(circleOfConfusionSampler,offset).g; return cameraMinMaxZ.x+(cameraMinMaxZ.y-cameraMinMaxZ.x)*depth; } float sampleCoC(in vec2 offset) { float coc=texture2D(circleOfConfusionSampler,offset).r; return coc; } #endif #include[0..varyingCount] #ifdef PACKEDFLOAT #include #endif void main(void) { float computedWeight=0.0; #ifdef PACKEDFLOAT float blend=0.; #else vec4 blend=vec4(0.); #endif #ifdef DOF float sumOfWeights=CENTER_WEIGHT; float factor=0.0; #ifdef PACKEDFLOAT blend+=unpack(texture2D(textureSampler,sampleCenter))*CENTER_WEIGHT; #else blend+=texture2D(textureSampler,sampleCenter)*CENTER_WEIGHT; #endif #endif #include[0..varyingCount] #include[0..depCount] #ifdef PACKEDFLOAT gl_FragColor=pack(blend); #else gl_FragColor=blend; #endif #ifdef DOF gl_FragColor/=sumOfWeights; #endif }";X.a.ShadersStore.kernelBlurPixelShader=a;X.a.IncludesShadersStore.kernelBlurVertex="sampleCoord{X}=sampleCenter+delta*KERNEL_OFFSET{X};";V=" attribute vec2 position; uniform vec2 delta; varying vec2 sampleCenter; #include[0..varyingCount] const vec2 madd=vec2(0.5,0.5); void main(void) { sampleCenter=(position*madd+madd); #include[0..varyingCount] gl_Position=vec4(position,0.0,1.0); }";X.a.ShadersStore.kernelBlurVertexShader=V;var Vh=function(a){function b(b,c,d,e,f,g,h,i,j,k,l){void 0===g&&(g=U.a.BILINEAR_SAMPLINGMODE),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===k&&(k=""),void 0===l&&(l=!1);var n=a.call(this,b,"kernelBlur",["delta","direction","cameraMinMaxZ"],["circleOfConfusionSampler"],e,f,g,h,i,null,j,"kernelBlur",{varyingCount:0,depCount:0},!0)||this;return n.blockCompilation=l,n._packedFloat=!1,n._staticDefines="",n._staticDefines=k,n.direction=c,n.onApplyObservable.add(function(a){n._outputTexture?a.setFloat2("delta",1/n._outputTexture.width*n.direction.x,1/n._outputTexture.height*n.direction.y):a.setFloat2("delta",1/n.width*n.direction.x,1/n.height*n.direction.y)}),n.kernel=d,n}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"kernel",{get:function(){return this._idealKernel},set:function(a){this._idealKernel!==a&&(a=Math.max(a,1),this._idealKernel=a,this._kernel=this._nearestBestKernel(a),this.blockCompilation||this._updateParameters())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"packedFloat",{get:function(){return this._packedFloat},set:function(a){this._packedFloat!==a&&(this._packedFloat=a,this.blockCompilation||this._updateParameters())},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"BlurPostProcess"},b.prototype.updateEffect=function(a,b,c,d,e,f){void 0===a&&null,void 0===b&&null,void 0===c&&null,this._updateParameters(e,f)},b.prototype._updateParameters=function(b,c){for(var d=this._kernel,e=(d-1)/2,f=[],g=[],h=0,i=0;i0)return Math.max(d,3)}return Math.max(a,3)},b.prototype._gaussianWeight=function(a){a=-a*a/(1/3*2*(1/3));return 1/(Math.sqrt(2*Math.PI)*(1/3))*Math.exp(a)},b.prototype._glslFloat=function(a,b){return void 0===b&&(b=8),a.toFixed(b).replace(/0+$/,"")},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.direction,a.kernel,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable,a.textureType,void 0,!1)},a,d,e)},Object(l.c)([Object(I.d)("kernel")],b.prototype,"_kernel",void 0),Object(l.c)([Object(I.d)("packedFloat")],b.prototype,"_packedFloat",void 0),Object(l.c)([Object(I.o)()],b.prototype,"direction",void 0),b}(Fc);Object(i.b)("BABYLON.BlurPostProcess",Vh);var Wh=function(a){function b(b,c,d,e,f,h,i){void 0===f&&(f=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===h&&(h=U.a.BILINEAR_SAMPLINGMODE),void 0===i&&(i=!0);var j=a.call(this,b,c,d,e,!0,f,!1,h,i)||this;j.scene=d,j.mirrorPlane=new Hb.a(0,1,0,1),j._transformMatrix=g.a.Zero(),j._mirrorMatrix=g.a.Zero(),j._adaptiveBlurKernel=0,j._blurKernelX=0,j._blurKernelY=0,j._blurRatio=1,j.ignoreCameraViewport=!0,j._updateGammaSpace(),j._imageProcessingConfigChangeObserver=d.imageProcessingConfiguration.onUpdateParameters.add(function(){j._updateGammaSpace()});var k,l=j.getScene().getEngine();return j.onBeforeBindObservable.add(function(){var a;null===(a=l._debugPushGroup)||void 0===a||a.call(l,"mirror generation for "+b,1)}),j.onAfterUnbindObservable.add(function(){var a;null===(a=l._debugPopGroup)||void 0===a||a.call(l,1)}),j.onBeforeRenderObservable.add(function(){g.a.ReflectionToRef(j.mirrorPlane,j._mirrorMatrix),j._mirrorMatrix.multiplyToRef(d.getViewMatrix(),j._transformMatrix),d.setTransformMatrix(j._transformMatrix,d.getProjectionMatrix()),k=d.clipPlane,d.clipPlane=j.mirrorPlane,d.getEngine().cullBackFaces=!1,d._mirroredCameraPosition=g.e.TransformCoordinates(d.activeCamera.globalPosition,j._mirrorMatrix)}),j.onAfterRenderObservable.add(function(){d.updateTransformMatrix(),d.getEngine().cullBackFaces=null,d._mirroredCameraPosition=null,d.clipPlane=k}),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"blurRatio",{get:function(){return this._blurRatio},set:function(a){this._blurRatio!==a&&(this._blurRatio=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"adaptiveBlurKernel",{set:function(a){this._adaptiveBlurKernel=a,this._autoComputeBlurKernel()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernel",{set:function(a){this.blurKernelX=a,this.blurKernelY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernelX",{get:function(){return this._blurKernelX},set:function(a){this._blurKernelX!==a&&(this._blurKernelX=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurKernelY",{get:function(){return this._blurKernelY},set:function(a){this._blurKernelY!==a&&(this._blurKernelY=a,this._preparePostProcesses())},enumerable:!1,configurable:!0}),b.prototype._autoComputeBlurKernel=function(){var a=this.getScene().getEngine(),b=this.getRenderWidth()/a.getRenderWidth();a=this.getRenderHeight()/a.getRenderHeight();this.blurKernelX=this._adaptiveBlurKernel*b,this.blurKernelY=this._adaptiveBlurKernel*a},b.prototype._onRatioRescale=function(){this._sizeRatio&&(this.resize(this._initialSizeParameter),this._adaptiveBlurKernel||this._preparePostProcesses()),this._adaptiveBlurKernel&&this._autoComputeBlurKernel()},b.prototype._updateGammaSpace=function(){this.gammaSpace=!this.scene.imageProcessingConfiguration.isEnabled||!this.scene.imageProcessingConfiguration.applyByPostProcess},b.prototype._preparePostProcesses=function(){if(this.clearPostProcesses(!0),this._blurKernelX&&this._blurKernelY){var a=this.getScene().getEngine(),b=a.getCaps().textureFloatRender&&a.getCaps().textureFloatLinearFiltering?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_HALF_FLOAT;this._blurX=new Vh("horizontal blur",new g.d(1,0),this._blurKernelX,this._blurRatio,null,U.a.BILINEAR_SAMPLINGMODE,a,!1,b),this._blurX.autoClear=!1,1===this._blurRatio&&this.samples<2&&this._texture?this._blurX.inputTexture=this._renderTarget:this._blurX.alwaysForcePOT=!0,this._blurY=new Vh("vertical blur",new g.d(0,1),this._blurKernelY,this._blurRatio,null,U.a.BILINEAR_SAMPLINGMODE,a,!1,b),this._blurY.autoClear=!1,this._blurY.alwaysForcePOT=1!==this._blurRatio,this.addPostProcess(this._blurX),this.addPostProcess(this._blurY)}else this._blurY&&(this.removePostProcess(this._blurY),this._blurY.dispose(),this._blurY=null),this._blurX&&(this.removePostProcess(this._blurX),this._blurX.dispose(),this._blurX=null)},b.prototype.clone=function(){var a=this.getScene();if(!a)return this;var c=this.getSize();c=new b(this.name,c.width,a,this._renderTargetOptions.generateMipMaps,this._renderTargetOptions.type,this._renderTargetOptions.samplingMode,this._renderTargetOptions.generateDepthBuffer);return c.hasAlpha=this.hasAlpha,c.level=this.level,c.mirrorPlane=this.mirrorPlane.clone(),this.renderList&&(c.renderList=this.renderList.slice(0)),c},b.prototype.serialize=function(){if(!this.name)return null;var b=a.prototype.serialize.call(this);return b.mirrorPlane=this.mirrorPlane.asArray(),b},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.scene.imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingConfigChangeObserver)},b}(cd);U.a._CreateMirror=function(a,b,c,d){return new Wh(a,b,c,d)};var Xh=function(a){function b(b,c,d,e,h,i,j,k,l,t,u,v,w,x,y){void 0===d&&(d=null),void 0===e&&(e=!1),void 0===h&&(h=null),void 0===i&&(i=null),void 0===j&&(j=null),void 0===k&&(k=r.a.TEXTUREFORMAT_RGBA),void 0===l&&(l=!1),void 0===t&&(t=null),void 0===u&&(u=!1),void 0===v&&(v=.8),void 0===w&&(w=0);c=a.call(this,c)||this;return c._lodScale=.8,c._lodOffset=0,c.onLoadObservable=new f.c,c.boundingBoxPosition=g.e.Zero(),c._rotationY=0,c._files=null,c._forcedExtension=null,c._extensions=null,c.name=b,c.url=b,c._noMipmap=e,c.hasAlpha=!1,c._format=k,c.isCube=!0,c._textureMatrix=g.a.Identity(),c._createPolynomials=u,c.coordinatesMode=U.a.CUBIC_MODE,c._extensions=d,c._files=h,c._forcedExtension=t,c._loaderOptions=x,c._useSRGBBuffer=y,c._lodScale=v,c._lodOffset=w,b||h?(c.updateURL(b,t,i,l,j,d,null===(e=c.getScene())||void 0===e?void 0:e.useDelayedTextureLoading,h),c):c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"boundingBoxSize",{get:function(){return this._boundingBoxSize},set:function(a){if(!this._boundingBoxSize||!this._boundingBoxSize.equals(a)){this._boundingBoxSize=a;a=this.getScene();a&&a.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag)}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rotationY",{get:function(){return this._rotationY},set:function(a){this._rotationY=a,this.setReflectionTextureMatrix(g.a.RotationY(this._rotationY))},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"noMipmap",{get:function(){return this._noMipmap},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"forcedExtension",{get:function(){return this._forcedExtension},enumerable:!1,configurable:!0}),b.CreateFromImages=function(a,c,d){var e="";return a.forEach(function(a){return e+=a}),new b(e,c,null,d,a)},b.CreateFromPrefilteredData=function(a,c,d,e){void 0===d&&(d=null),void 0===e&&(e=!0);var f=c.useDelayedTextureLoading;c.useDelayedTextureLoading=!1;a=new b(a,c,null,!1,null,null,null,void 0,!0,d,e);return c.useDelayedTextureLoading=f,a},b.prototype.getClassName=function(){return"CubeTexture"},b.prototype.updateURL=function(a,b,c,d,e,f,g,h){void 0===c&&(c=null),void 0===d&&(d=!1),void 0===e&&(e=null),void 0===f&&(f=null),void 0===g&&(g=!1),void 0===h&&(h=null),this.name&&!Object(gh.g)(this.name,"data:")||(this.name=a),this.url=a;var i=a.lastIndexOf(".");b=b||(i>-1?a.substring(i).toLowerCase():"");i=0===b.indexOf(".dds");b=0===b.indexOf(".env");if(b?(this.gammaSpace=!1,this._prefiltered=!1,this.anisotropicFilteringLevel=1):(this._prefiltered=d,d&&(this.gammaSpace=!1,this.anisotropicFilteringLevel=1)),h)this._files=h;else if(b||i||f||(f=["_px.jpg","_py.jpg","_pz.jpg","_nx.jpg","_ny.jpg","_nz.jpg"]),this._files=this._files||[],this._files.length=0,f){for(d=0;d ";X.a.IncludesShadersStore.backgroundUboDeclaration=V;c(153),c(150),c(151),c(181),c(152),c(122),c(143),c(109),c(157),c(158);a="#ifdef TEXTURELODSUPPORT #extension GL_EXT_shader_texture_lod : enable #endif precision highp float; #include<__decl__backgroundFragment> #include #define RECIPROCAL_PI2 0.15915494 varying vec3 vPositionW; #ifdef MAINUV1 varying vec2 vMainUV1; #endif #ifdef MAINUV2 varying vec2 vMainUV2; #endif #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef DIFFUSE #if DIFFUSEDIRECTUV == 1 #define vDiffuseUV vMainUV1 #elif DIFFUSEDIRECTUV == 2 #define vDiffuseUV vMainUV2 #else varying vec2 vDiffuseUV; #endif uniform sampler2D diffuseSampler; #endif #ifdef REFLECTION #ifdef REFLECTIONMAP_3D #define sampleReflection(s,c) textureCube(s,c) uniform samplerCube reflectionSampler; #ifdef TEXTURELODSUPPORT #define sampleReflectionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #else #define sampleReflection(s,c) texture2D(s,c) uniform sampler2D reflectionSampler; #ifdef TEXTURELODSUPPORT #define sampleReflectionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #include #endif #ifndef FROMLINEARSPACE #define FROMLINEARSPACE; #endif #ifndef SHADOWONLY #define SHADOWONLY; #endif #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include #include #include #ifdef REFLECTIONFRESNEL #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 vec3 fresnelSchlickEnvironmentGGX(float VdotN,vec3 reflectance0,vec3 reflectance90,float smoothness) { float weight=mix(FRESNEL_MAXIMUM_ON_ROUGH,1.0,smoothness); return reflectance0+weight*(reflectance90-reflectance0)*pow5(saturate(1.0-VdotN)); } #endif void main(void) { #include vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=vec3(0.0,1.0,0.0); #endif float shadow=1.; float globalShadow=0.; float shadowLightCount=0.; #include[0..maxSimultaneousLights] #ifdef SHADOWINUSE globalShadow/=shadowLightCount; #else globalShadow=1.0; #endif #ifndef BACKMAT_SHADOWONLY vec4 reflectionColor=vec4(1.,1.,1.,1.); #ifdef REFLECTION vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D vec3 reflectionCoords=reflectionVector; #else vec2 reflectionCoords=reflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION reflectionCoords/=reflectionVector.z; #endif reflectionCoords.y=1.0-reflectionCoords.y; #endif #ifdef REFLECTIONBLUR float reflectionLOD=vReflectionInfos.y; #ifdef TEXTURELODSUPPORT reflectionLOD=reflectionLOD*log2(vReflectionMicrosurfaceInfos.x)*vReflectionMicrosurfaceInfos.y+vReflectionMicrosurfaceInfos.z; reflectionColor=sampleReflectionLod(reflectionSampler,reflectionCoords,reflectionLOD); #else float lodReflectionNormalized=saturate(reflectionLOD); float lodReflectionNormalizedDoubled=lodReflectionNormalized*2.0; vec4 reflectionSpecularMid=sampleReflection(reflectionSampler,reflectionCoords); if(lodReflectionNormalizedDoubled<1.0){ reflectionColor=mix( sampleReflection(reflectionSamplerHigh,reflectionCoords), reflectionSpecularMid, lodReflectionNormalizedDoubled ); } else { reflectionColor=mix( reflectionSpecularMid, sampleReflection(reflectionSamplerLow,reflectionCoords), lodReflectionNormalizedDoubled-1.0 ); } #endif #else vec4 reflectionSample=sampleReflection(reflectionSampler,reflectionCoords); reflectionColor=reflectionSample; #endif #ifdef RGBDREFLECTION reflectionColor.rgb=fromRGBD(reflectionColor); #endif #ifdef GAMMAREFLECTION reflectionColor.rgb=toLinearSpace(reflectionColor.rgb); #endif #ifdef REFLECTIONBGR reflectionColor.rgb=reflectionColor.bgr; #endif reflectionColor.rgb*=vReflectionInfos.x; #endif vec3 diffuseColor=vec3(1.,1.,1.); float finalAlpha=alpha; #ifdef DIFFUSE vec4 diffuseMap=texture2D(diffuseSampler,vDiffuseUV); #ifdef GAMMADIFFUSE diffuseMap.rgb=toLinearSpace(diffuseMap.rgb); #endif diffuseMap.rgb*=vDiffuseInfos.y; #ifdef DIFFUSEHASALPHA finalAlpha*=diffuseMap.a; #endif diffuseColor=diffuseMap.rgb; #endif #ifdef REFLECTIONFRESNEL vec3 colorBase=diffuseColor; #else vec3 colorBase=reflectionColor.rgb*diffuseColor; #endif colorBase=max(colorBase,0.0); #ifdef USERGBCOLOR vec3 finalColor=colorBase; #else #ifdef USEHIGHLIGHTANDSHADOWCOLORS vec3 mainColor=mix(vPrimaryColorShadow.rgb,vPrimaryColor.rgb,colorBase); #else vec3 mainColor=vPrimaryColor.rgb; #endif vec3 finalColor=colorBase*mainColor; #endif #ifdef REFLECTIONFRESNEL vec3 reflectionAmount=vReflectionControl.xxx; vec3 reflectionReflectance0=vReflectionControl.yyy; vec3 reflectionReflectance90=vReflectionControl.zzz; float VdotN=dot(normalize(vEyePosition.xyz),normalW); vec3 planarReflectionFresnel=fresnelSchlickEnvironmentGGX(saturate(VdotN),reflectionReflectance0,reflectionReflectance90,1.0); reflectionAmount*=planarReflectionFresnel; #ifdef REFLECTIONFALLOFF float reflectionDistanceFalloff=1.0-saturate(length(vPositionW.xyz-vBackgroundCenter)*vReflectionControl.w); reflectionDistanceFalloff*=reflectionDistanceFalloff; reflectionAmount*=reflectionDistanceFalloff; #endif finalColor=mix(finalColor,reflectionColor.rgb,saturate(reflectionAmount)); #endif #ifdef OPACITYFRESNEL float viewAngleToFloor=dot(normalW,normalize(vEyePosition.xyz-vBackgroundCenter)); const float startAngle=0.1; float fadeFactor=saturate(viewAngleToFloor/startAngle); finalAlpha*=fadeFactor*fadeFactor; #endif #ifdef SHADOWINUSE finalColor=mix(finalColor*shadowLevel,finalColor,globalShadow); #endif vec4 color=vec4(finalColor,finalAlpha); #else vec4 color=vec4(vPrimaryColor.rgb,(1.0-clamp(globalShadow,0.,1.))*alpha); #endif #include #ifdef IMAGEPROCESSINGPOSTPROCESS color.rgb=clamp(color.rgb,0.,30.0); #else color=applyImageProcessing(color); #endif #ifdef PREMULTIPLYALPHA color.rgb*=color.a; #endif #ifdef NOISE color.rgb+=dither(vPositionW.xy,0.5); color=max(color,0.0); #endif gl_FragColor=color; } ";X.a.ShadersStore.backgroundPixelShader=a;V="uniform mat4 view; uniform mat4 viewProjection; uniform float shadowLevel; #ifdef DIFFUSE uniform mat4 diffuseMatrix; uniform vec2 vDiffuseInfos; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; uniform mat4 reflectionMatrix; uniform vec3 vReflectionMicrosurfaceInfos; uniform float fFovMultiplier; #endif #ifdef POINTSIZE uniform float pointSize; #endif";X.a.IncludesShadersStore.backgroundVertexDeclaration=V;c(87),c(92),c(123),c(159),c(160),c(161),c(88),c(89),c(112),c(185),c(162);a="precision highp float; #include<__decl__backgroundVertex> #include attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #include #include varying vec3 vPositionW; #ifdef NORMAL varying vec3 vNormalW; #endif #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #ifdef MAINUV1 varying vec2 vMainUV1; #endif #ifdef MAINUV2 varying vec2 vMainUV2; #endif #if defined(DIFFUSE) && DIFFUSEDIRECTUV == 0 varying vec2 vDiffuseUV; #endif #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif void main(void) { #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=position; #endif #include #include #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*finalWorld*vec4(position,1.0); } else { gl_Position=viewProjectionR*finalWorld*vec4(position,1.0); } #else gl_Position=viewProjection*finalWorld*vec4(position,1.0); #endif vec4 worldPos=finalWorld*vec4(position,1.0); vPositionW=vec3(worldPos); #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normal); #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(position,0.0))); #ifdef EQUIRECTANGULAR_RELFECTION_FOV mat3 screenToWorld=inverseMat3(mat3(finalWorld*viewProjection)); vec3 segment=mix(vDirectionW,screenToWorld*vec3(0.0,0.0,1.0),abs(fFovMultiplier-1.0)); if (fFovMultiplier<=1.0) { vDirectionW=normalize(segment); } else { vDirectionW=normalize(vDirectionW+(vDirectionW-segment)); } #endif #endif #ifndef UV1 vec2 uv=vec2(0.,0.); #endif #ifndef UV2 vec2 uv2=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uv; #endif #ifdef MAINUV2 vMainUV2=uv2; #endif #if defined(DIFFUSE) && DIFFUSEDIRECTUV == 0 if (vDiffuseInfos.x == 0.) { vDiffuseUV=vec2(diffuseMatrix*vec4(uv,1.0,0.0)); } else { vDiffuseUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); } #endif #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #ifdef POINTSIZE gl_PointSize=pointSize; #endif } ";X.a.ShadersStore.backgroundVertexShader=a;var bi=c(74),ci=function(a){function b(){var b=a.call(this)||this;return b.DIFFUSE=!1,b.DIFFUSEDIRECTUV=0,b.GAMMADIFFUSE=!1,b.DIFFUSEHASALPHA=!1,b.OPACITYFRESNEL=!1,b.REFLECTIONBLUR=!1,b.REFLECTIONFRESNEL=!1,b.REFLECTIONFALLOFF=!1,b.TEXTURELODSUPPORT=!1,b.PREMULTIPLYALPHA=!1,b.USERGBCOLOR=!1,b.USEHIGHLIGHTANDSHADOWCOLORS=!1,b.BACKMAT_SHADOWONLY=!1,b.NOISE=!1,b.REFLECTIONBGR=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.EXPOSURE=!1,b.MULTIVIEW=!1,b.REFLECTION=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.INVERTCUBICMAP=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.LODINREFLECTIONALPHA=!1,b.GAMMAREFLECTION=!1,b.RGBDREFLECTION=!1,b.EQUIRECTANGULAR_RELFECTION_FOV=!1,b.MAINUV1=!1,b.MAINUV2=!1,b.UV1=!1,b.UV2=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.POINTSIZE=!1,b.FOG=!1,b.NORMAL=!1,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.INSTANCES=!1,b.SHADOWFLOAT=!1,b.LOGARITHMICDEPTH=!1,b.NONUNIFORMSCALING=!1,b.ALPHATEST=!1,b.rebuild(),b}return Object(l.d)(b,a),b}(Zh.a),di=function(a){function b(b,c){var d=a.call(this,b,c)||this;return d.primaryColor=h.a.White(),d._primaryColorShadowLevel=0,d._primaryColorHighlightLevel=0,d.reflectionTexture=null,d.reflectionBlur=0,d.diffuseTexture=null,d._shadowLights=null,d.shadowLights=null,d.shadowLevel=0,d.sceneCenter=g.e.Zero(),d.opacityFresnel=!0,d.reflectionFresnel=!1,d.reflectionFalloffDistance=0,d.reflectionAmount=1,d.reflectionReflectance0=.05,d.reflectionReflectance90=.5,d.useRGBColor=!0,d.enableNoise=!1,d._fovMultiplier=1,d.useEquirectangularFOV=!1,d._maxSimultaneousLights=4,d.maxSimultaneousLights=4,d._shadowOnly=!1,d.shadowOnly=!1,d._imageProcessingObserver=null,d.switchToBGR=!1,d._renderTargets=new Ac.a(16),d._reflectionControls=g.f.Zero(),d._white=h.a.White(),d._primaryShadowColor=h.a.Black(),d._primaryHighlightColor=h.a.Black(),d._attachImageProcessingConfiguration(null),d.getRenderTargetTextures=function(){return d._renderTargets.reset(),d._diffuseTexture&&d._diffuseTexture.isRenderTarget&&d._renderTargets.push(d._diffuseTexture),d._reflectionTexture&&d._reflectionTexture.isRenderTarget&&d._renderTargets.push(d._reflectionTexture),d._renderTargets},d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"_perceptualColor",{get:function(){return this.__perceptualColor},set:function(a){this.__perceptualColor=a,this._computePrimaryColorFromPerceptualColor(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"primaryColorShadowLevel",{get:function(){return this._primaryColorShadowLevel},set:function(a){this._primaryColorShadowLevel=a,this._computePrimaryColors(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"primaryColorHighlightLevel",{get:function(){return this._primaryColorHighlightLevel},set:function(a){this._primaryColorHighlightLevel=a,this._computePrimaryColors(),this._markAllSubMeshesAsLightsDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"reflectionStandardFresnelWeight",{set:function(a){a=a;a<.5?(a*=2,this.reflectionReflectance0=b.StandardReflectance0*a,this.reflectionReflectance90=b.StandardReflectance90*a):(a=2*a-1,this.reflectionReflectance0=b.StandardReflectance0+(1-b.StandardReflectance0)*a,this.reflectionReflectance90=b.StandardReflectance90+(1-b.StandardReflectance90)*a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fovMultiplier",{get:function(){return this._fovMultiplier},set:function(a){isNaN(a)&&(a=1),this._fovMultiplier=Math.max(0,Math.min(2,a))},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a){var b=this;a!==this._imageProcessingConfiguration&&(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration=a||this.getScene().imageProcessingConfiguration,this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){b._computePrimaryColorFromPerceptualColor(),b._markAllSubMeshesAsImageProcessingDirty()})))},Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a),this._markAllSubMeshesAsTexturesDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraToneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraExposure",{get:function(){return this._imageProcessingConfiguration.exposure},set:function(a){this._imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraContrast",{get:function(){return this._imageProcessingConfiguration.contrast},set:function(a){this._imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingTexture",{get:function(){return this._imageProcessingConfiguration.colorGradingTexture},set:function(a){this.imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurves",{get:function(){return this.imageProcessingConfiguration.colorCurves},set:function(a){this.imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasRenderTargetTextures",{get:function(){return!(!this._diffuseTexture||!this._diffuseTexture.isRenderTarget)||!(!this._reflectionTexture||!this._reflectionTexture.isRenderTarget)},enumerable:!1,configurable:!0}),b.prototype.needAlphaTesting=function(){return!0},b.prototype.needAlphaBlending=function(){return this.alpha<1||null!=this._diffuseTexture&&this._diffuseTexture.hasAlpha||this._shadowOnly},b.prototype.isReadyForSubMesh=function(a,b,c){var d=this;if(void 0===c&&(c=!1),b.effect&&this.isFrozen&&b.effect._wasPreviouslyReady)return!0;b._materialDefines||(b.materialDefines=new ci);var e=this.getScene(),f=b._materialDefines;if(this._isReadyForSubMesh(b))return!0;var g=e.getEngine();if(Yh.a.PrepareDefinesForLights(e,a,f,!1,this._maxSimultaneousLights),f._needNormals=!0,Yh.a.PrepareDefinesForMultiview(e,f),f._areTexturesDirty){if(f._needUVs=!1,e.texturesEnabled){if(e.getEngine().getCaps().textureLOD&&(f.TEXTURELODSUPPORT=!0),this._diffuseTexture&&ai.a.DiffuseTextureEnabled){if(!this._diffuseTexture.isReadyOrNotBlocking())return!1;Yh.a.PrepareDefinesForMergedUV(this._diffuseTexture,f,"DIFFUSE"),f.DIFFUSEHASALPHA=this._diffuseTexture.hasAlpha,f.GAMMADIFFUSE=this._diffuseTexture.gammaSpace,f.OPACITYFRESNEL=this._opacityFresnel}else f.DIFFUSE=!1,f.DIFFUSEHASALPHA=!1,f.GAMMADIFFUSE=!1,f.OPACITYFRESNEL=!1;var h=this._reflectionTexture;if(h&&ai.a.ReflectionTextureEnabled){if(!h.isReadyOrNotBlocking())return!1;switch(f.REFLECTION=!0,f.GAMMAREFLECTION=h.gammaSpace,f.RGBDREFLECTION=h.isRGBD,f.REFLECTIONBLUR=this._reflectionBlur>0,f.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!h.invertZ:h.invertZ,f.LODINREFLECTIONALPHA=h.lodLevelInAlpha,f.EQUIRECTANGULAR_RELFECTION_FOV=this.useEquirectangularFOV,f.REFLECTIONBGR=this.switchToBGR,h.coordinatesMode===U.a.INVCUBIC_MODE&&(f.INVERTCUBICMAP=!0),f.REFLECTIONMAP_3D=h.isCube,h.coordinatesMode){case U.a.EXPLICIT_MODE:f.REFLECTIONMAP_EXPLICIT=!0;break;case U.a.PLANAR_MODE:f.REFLECTIONMAP_PLANAR=!0;break;case U.a.PROJECTION_MODE:f.REFLECTIONMAP_PROJECTION=!0;break;case U.a.SKYBOX_MODE:f.REFLECTIONMAP_SKYBOX=!0;break;case U.a.SPHERICAL_MODE:f.REFLECTIONMAP_SPHERICAL=!0;break;case U.a.EQUIRECTANGULAR_MODE:f.REFLECTIONMAP_EQUIRECTANGULAR=!0;break;case U.a.FIXED_EQUIRECTANGULAR_MODE:f.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!0;break;case U.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:f.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!0;break;case U.a.CUBIC_MODE:case U.a.INVCUBIC_MODE:default:f.REFLECTIONMAP_CUBIC=!0}this.reflectionFresnel?(f.REFLECTIONFRESNEL=!0,f.REFLECTIONFALLOFF=this.reflectionFalloffDistance>0,this._reflectionControls.x=this.reflectionAmount,this._reflectionControls.y=this.reflectionReflectance0,this._reflectionControls.z=this.reflectionReflectance90,this._reflectionControls.w=1/this.reflectionFalloffDistance):(f.REFLECTIONFRESNEL=!1,f.REFLECTIONFALLOFF=!1)}else f.REFLECTION=!1,f.REFLECTIONFRESNEL=!1,f.REFLECTIONFALLOFF=!1,f.REFLECTIONBLUR=!1,f.REFLECTIONMAP_3D=!1,f.REFLECTIONMAP_SPHERICAL=!1,f.REFLECTIONMAP_PLANAR=!1,f.REFLECTIONMAP_CUBIC=!1,f.REFLECTIONMAP_PROJECTION=!1,f.REFLECTIONMAP_SKYBOX=!1,f.REFLECTIONMAP_EXPLICIT=!1,f.REFLECTIONMAP_EQUIRECTANGULAR=!1,f.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,f.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,f.INVERTCUBICMAP=!1,f.REFLECTIONMAP_OPPOSITEZ=!1,f.LODINREFLECTIONALPHA=!1,f.GAMMAREFLECTION=!1,f.RGBDREFLECTION=!1}f.PREMULTIPLYALPHA=this.alphaMode===r.a.ALPHA_PREMULTIPLIED||this.alphaMode===r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,f.USERGBCOLOR=this._useRGBColor,f.NOISE=this._enableNoise}if(f._areLightsDirty&&(f.USEHIGHLIGHTANDSHADOWCOLORS=!this._useRGBColor&&(0!==this._primaryColorShadowLevel||0!==this._primaryColorHighlightLevel),f.BACKMAT_SHADOWONLY=this._shadowOnly),f._areImageProcessingDirty&&this._imageProcessingConfiguration){if(!this._imageProcessingConfiguration.isReady())return!1;this._imageProcessingConfiguration.prepareDefines(f)}if(Yh.a.PrepareDefinesForMisc(a,e,!1,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a),f),Yh.a.PrepareDefinesForFrameBoundValues(e,g,f,c,null,b.getRenderingMesh().hasThinInstances),Yh.a.PrepareDefinesForAttributes(a,f,!1,!0,!1)&&a&&(e.getEngine().getCaps().standardDerivatives||a.isVerticesDataPresent(W.b.NormalKind)||(a.createNormals(!0),q.a.Warn("BackgroundMaterial: Normals have been created for the mesh: "+a.name))),f.isDirty){f.markAsProcessed(),e.resetCachedMaterial();h=new bi.a;f.FOG&&h.addFallback(0,"FOG"),f.POINTSIZE&&h.addFallback(1,"POINTSIZE"),f.MULTIVIEW&&h.addFallback(0,"MULTIVIEW"),Yh.a.HandleFallbacksForShadows(f,h,this._maxSimultaneousLights);c=[W.b.PositionKind];f.NORMAL&&c.push(W.b.NormalKind),f.UV1&&c.push(W.b.UVKind),f.UV2&&c.push(W.b.UV2Kind),Yh.a.PrepareAttributesForBones(c,a,f,h),Yh.a.PrepareAttributesForInstances(c,f);a=["world","view","viewProjection","vEyePosition","vLightsType","vFogInfos","vFogColor","pointSize","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","mBones","vPrimaryColor","vPrimaryColorShadow","vReflectionInfos","reflectionMatrix","vReflectionMicrosurfaceInfos","fFovMultiplier","shadowLevel","alpha","vBackgroundCenter","vReflectionControl","vDiffuseInfos","diffuseMatrix"];var i=["diffuseSampler","reflectionSampler","reflectionSamplerLow","reflectionSamplerHigh"],j=["Material","Scene"];od.a&&(od.a.PrepareUniforms(a,f),od.a.PrepareSamplers(i,f)),Yh.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:f,maxSimultaneousLights:this._maxSimultaneousLights});var k=f.toString();c=e.getEngine().createEffect("background",{attributes:c,uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:k,fallbacks:h,onCompiled:function(a){d.onCompiled&&d.onCompiled(a),Yh.a.BindSceneUniformBuffer(a,e.getSceneUniformBuffer())},onError:this.onError,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights}},g);b.setEffect(c,f,this._materialContext),this.buildUniformLayout()}return!(!b.effect||!b.effect.isReady())&&(f._renderId=e.getRenderId(),b.effect._wasPreviouslyReady=!0,!0)},b.prototype._computePrimaryColorFromPerceptualColor=function(){this.__perceptualColor&&(this._primaryColor.copyFrom(this.__perceptualColor),this._primaryColor.toLinearSpaceToRef(this._primaryColor),this._imageProcessingConfiguration&&this._primaryColor.scaleToRef(1/this._imageProcessingConfiguration.exposure,this._primaryColor),this._computePrimaryColors())},b.prototype._computePrimaryColors=function(){0===this._primaryColorShadowLevel&&0===this._primaryColorHighlightLevel||(this._primaryColor.scaleToRef(this._primaryColorShadowLevel,this._primaryShadowColor),this._primaryColor.subtractToRef(this._primaryShadowColor,this._primaryShadowColor),this._white.subtractToRef(this._primaryColor,this._primaryHighlightColor),this._primaryHighlightColor.scaleToRef(this._primaryColorHighlightLevel,this._primaryHighlightColor),this._primaryColor.addToRef(this._primaryHighlightColor,this._primaryHighlightColor))},b.prototype.buildUniformLayout=function(){this._uniformBuffer.addUniform("vPrimaryColor",4),this._uniformBuffer.addUniform("vPrimaryColorShadow",4),this._uniformBuffer.addUniform("vDiffuseInfos",2),this._uniformBuffer.addUniform("vReflectionInfos",2),this._uniformBuffer.addUniform("diffuseMatrix",16),this._uniformBuffer.addUniform("reflectionMatrix",16),this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos",3),this._uniformBuffer.addUniform("fFovMultiplier",1),this._uniformBuffer.addUniform("pointSize",1),this._uniformBuffer.addUniform("shadowLevel",1),this._uniformBuffer.addUniform("alpha",1),this._uniformBuffer.addUniform("vBackgroundCenter",3),this._uniformBuffer.addUniform("vReflectionControl",4),this._uniformBuffer.create()},b.prototype.unbind=function(){this._diffuseTexture&&this._diffuseTexture.isRenderTarget&&this._uniformBuffer.setTexture("diffuseSampler",null),this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&this._uniformBuffer.setTexture("reflectionSampler",null),a.prototype.unbind.call(this)},b.prototype.bindOnlyWorldMatrix=function(a){this._activeEffect.setMatrix("world",a)},b.prototype.bindForSubMesh=function(a,b,c){var d=this.getScene(),e=c._materialDefines;if(e){c=c.effect;if(c){this._activeEffect=c,this.bindOnlyWorldMatrix(a),Yh.a.BindBonesParameters(b,this._activeEffect);a=this._mustRebind(d,c,b.visibility);if(a){this._uniformBuffer.bindToEffect(c,"Material"),this.bindViewProjection(c);var f=this._reflectionTexture;this._uniformBuffer.useUbo&&this.isFrozen&&this._uniformBuffer.isSync||(d.texturesEnabled&&(this._diffuseTexture&&ai.a.DiffuseTextureEnabled&&(this._uniformBuffer.updateFloat2("vDiffuseInfos",this._diffuseTexture.coordinatesIndex,this._diffuseTexture.level),Yh.a.BindTextureMatrix(this._diffuseTexture,this._uniformBuffer,"diffuse")),f&&ai.a.ReflectionTextureEnabled&&(this._uniformBuffer.updateMatrix("reflectionMatrix",f.getReflectionTextureMatrix()),this._uniformBuffer.updateFloat2("vReflectionInfos",f.level,this._reflectionBlur),this._uniformBuffer.updateFloat3("vReflectionMicrosurfaceInfos",f.getSize().width,f.lodGenerationScale,f.lodGenerationOffset))),this.shadowLevel>0&&this._uniformBuffer.updateFloat("shadowLevel",this.shadowLevel),this._uniformBuffer.updateFloat("alpha",this.alpha),this.pointsCloud&&this._uniformBuffer.updateFloat("pointSize",this.pointSize),e.USEHIGHLIGHTANDSHADOWCOLORS?(this._uniformBuffer.updateColor4("vPrimaryColor",this._primaryHighlightColor,1),this._uniformBuffer.updateColor4("vPrimaryColorShadow",this._primaryShadowColor,1)):this._uniformBuffer.updateColor4("vPrimaryColor",this._primaryColor,1)),this._uniformBuffer.updateFloat("fFovMultiplier",this._fovMultiplier),d.texturesEnabled&&(this._diffuseTexture&&ai.a.DiffuseTextureEnabled&&this._uniformBuffer.setTexture("diffuseSampler",this._diffuseTexture),f&&ai.a.ReflectionTextureEnabled&&(e.REFLECTIONBLUR&&e.TEXTURELODSUPPORT?this._uniformBuffer.setTexture("reflectionSampler",f):e.REFLECTIONBLUR?(this._uniformBuffer.setTexture("reflectionSampler",f._lodTextureMid||f),this._uniformBuffer.setTexture("reflectionSamplerLow",f._lodTextureLow||f),this._uniformBuffer.setTexture("reflectionSamplerHigh",f._lodTextureHigh||f)):this._uniformBuffer.setTexture("reflectionSampler",f),e.REFLECTIONFRESNEL&&(this._uniformBuffer.updateFloat3("vBackgroundCenter",this.sceneCenter.x,this.sceneCenter.y,this.sceneCenter.z),this._uniformBuffer.updateFloat4("vReflectionControl",this._reflectionControls.x,this._reflectionControls.y,this._reflectionControls.z,this._reflectionControls.w)))),Yh.a.BindClipPlane(this._activeEffect,d),d.bindEyePosition(c)}!a&&this.isFrozen||(d.lightsEnabled&&Yh.a.BindLights(d,b,this._activeEffect,e,this._maxSimultaneousLights),this.bindView(c),Yh.a.BindFogParameters(d,b,this._activeEffect,!0),this._imageProcessingConfiguration&&this._imageProcessingConfiguration.bind(this._activeEffect)),this._afterBind(b,this._activeEffect),this._uniformBuffer.update()}}},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._reflectionTexture===b||this._diffuseTexture===b},b.prototype.dispose=function(b,c){void 0===b&&(b=!1),void 0===c&&(c=!1),c&&(this.diffuseTexture&&this.diffuseTexture.dispose(),this.reflectionTexture&&this.reflectionTexture.dispose()),this._renderTargets.dispose(),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b)},b.prototype.clone=function(a){var c=this;return I.a.Clone(function(){return new b(a,c.getScene())},this)},b.prototype.serialize=function(){var a=I.a.Serialize(this);return a.customType="BABYLON.BackgroundMaterial",a},b.prototype.getClassName=function(){return"BackgroundMaterial"},b.Parse=function(a,c,d){return I.a.Parse(function(){return new b(a.name,c)},a,c,d)},b.StandardReflectance0=.05,b.StandardReflectance90=.5,Object(l.c)([Object(I.f)()],b.prototype,"_primaryColor",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"primaryColor",void 0),Object(l.c)([Object(I.f)()],b.prototype,"__perceptualColor",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_primaryColorShadowLevel",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_primaryColorHighlightLevel",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"primaryColorHighlightLevel",null),Object(l.c)([Object(I.n)()],b.prototype,"_reflectionTexture",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionBlur",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionBlur",void 0),Object(l.c)([Object(I.n)()],b.prototype,"_diffuseTexture",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"diffuseTexture",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"shadowLights",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_shadowLevel",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"shadowLevel",void 0),Object(l.c)([Object(I.p)()],b.prototype,"_sceneCenter",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"sceneCenter",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_opacityFresnel",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"opacityFresnel",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionFresnel",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionFresnel",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionFalloffDistance",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionFalloffDistance",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionAmount",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionAmount",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionReflectance0",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionReflectance0",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_reflectionReflectance90",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionReflectance90",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_useRGBColor",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRGBColor",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_enableNoise",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"enableNoise",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_maxSimultaneousLights",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_shadowOnly",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"shadowOnly",void 0),Object(l.c)([Object(I.j)()],b.prototype,"_imageProcessingConfiguration",void 0),b}($h.a);Object(i.b)("BABYLON.BackgroundMaterial",di);var ei=function(){function a(b,c){var d=this;this._errorHandler=function(a,b){d.onErrorObservable.notifyObservers({message:a,exception:b})},this._options=Object(l.a)(Object(l.a)({},a._getDefaultOptions()),b),this._scene=c,this.onErrorObservable=new f.c,this._setupBackground(),this._setupImageProcessing()}return a._getDefaultOptions=function(){return{createGround:!0,groundSize:15,groundTexture:this._groundTextureCDNUrl,groundColor:new h.a(.2,.2,.3).toLinearSpace().scale(3),groundOpacity:.9,enableGroundShadow:!0,groundShadowLevel:.5,enableGroundMirror:!1,groundMirrorSizeRatio:.3,groundMirrorBlurKernel:64,groundMirrorAmount:1,groundMirrorFresnelWeight:1,groundMirrorFallOffDistance:0,groundMirrorTextureType:r.a.TEXTURETYPE_UNSIGNED_INT,groundYBias:1e-5,createSkybox:!0,skyboxSize:20,skyboxTexture:this._skyboxTextureCDNUrl,skyboxColor:new h.a(.2,.2,.3).toLinearSpace().scale(3),backgroundYRotation:0,sizeAuto:!0,rootPosition:g.e.Zero(),setupImageProcessing:!0,environmentTexture:this._environmentTextureCDNUrl,cameraExposure:.8,cameraContrast:1.2,toneMappingEnabled:!0}},Object.defineProperty(a.prototype,"rootMesh",{get:function(){return this._rootMesh},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skybox",{get:function(){return this._skybox},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skyboxTexture",{get:function(){return this._skyboxTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"skyboxMaterial",{get:function(){return this._skyboxMaterial},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"ground",{get:function(){return this._ground},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundTexture",{get:function(){return this._groundTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMirror",{get:function(){return this._groundMirror},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMirrorRenderList",{get:function(){return this._groundMirror?this._groundMirror.renderList:null},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"groundMaterial",{get:function(){return this._groundMaterial},enumerable:!1,configurable:!0}),a.prototype.updateOptions=function(a){a=Object(l.a)(Object(l.a)({},this._options),a);this._ground&&!a.createGround&&(this._ground.dispose(),this._ground=null),this._groundMaterial&&!a.createGround&&(this._groundMaterial.dispose(),this._groundMaterial=null),this._groundTexture&&this._options.groundTexture!=a.groundTexture&&(this._groundTexture.dispose(),this._groundTexture=null),this._skybox&&!a.createSkybox&&(this._skybox.dispose(),this._skybox=null),this._skyboxMaterial&&!a.createSkybox&&(this._skyboxMaterial.dispose(),this._skyboxMaterial=null),this._skyboxTexture&&this._options.skyboxTexture!=a.skyboxTexture&&(this._skyboxTexture.dispose(),this._skyboxTexture=null),this._groundMirror&&!a.enableGroundMirror&&(this._groundMirror.dispose(),this._groundMirror=null),this._scene.environmentTexture&&this._options.environmentTexture!=a.environmentTexture&&this._scene.environmentTexture.dispose(),this._options=a,this._setupBackground(),this._setupImageProcessing()},a.prototype.setMainColor=function(a){this.groundMaterial&&(this.groundMaterial.primaryColor=a),this.skyboxMaterial&&(this.skyboxMaterial.primaryColor=a),this.groundMirror&&(this.groundMirror.clearColor=new h.b(a.r,a.g,a.b,1))},a.prototype._setupImageProcessing=function(){this._options.setupImageProcessing&&(this._scene.imageProcessingConfiguration.contrast=this._options.cameraContrast,this._scene.imageProcessingConfiguration.exposure=this._options.cameraExposure,this._scene.imageProcessingConfiguration.toneMappingEnabled=this._options.toneMappingEnabled,this._setupEnvironmentTexture())},a.prototype._setupEnvironmentTexture=function(){if(!this._scene.environmentTexture)if(this._options.environmentTexture instanceof Ie.a)this._scene.environmentTexture=this._options.environmentTexture;else{var a=Xh.CreateFromPrefilteredData(this._options.environmentTexture,this._scene);this._scene.environmentTexture=a}},a.prototype._setupBackground=function(){this._rootMesh||(this._rootMesh=new R.a("BackgroundHelper",this._scene)),this._rootMesh.rotation.y=this._options.backgroundYRotation;var a=this._getSceneSize();this._options.createGround&&(this._setupGround(a),this._setupGroundMaterial(),this._setupGroundDiffuseTexture(),this._options.enableGroundMirror&&this._setupGroundMirrorTexture(a),this._setupMirrorInGroundMaterial()),this._options.createSkybox&&(this._setupSkybox(a),this._setupSkyboxMaterial(),this._setupSkyboxReflectionTexture()),this._rootMesh.position.x=a.rootPosition.x,this._rootMesh.position.z=a.rootPosition.z,this._rootMesh.position.y=a.rootPosition.y},a.prototype._getSceneSize=function(){var a=this,b=this._options.groundSize,c=this._options.skyboxSize,d=this._options.rootPosition;if(!this._scene.meshes||1===this._scene.meshes.length)return{groundSize:b,skyboxSize:c,rootPosition:d};var e=this._scene.getWorldExtends(function(b){return b!==a._ground&&b!==a._rootMesh&&b!==a._skybox}),f=e.max.subtract(e.min);if(this._options.sizeAuto){this._scene.activeCamera instanceof fc&&this._scene.activeCamera.upperRadiusLimit&&(c=b=2*this._scene.activeCamera.upperRadiusLimit);var g=f.length();g>b&&(c=b=2*g),b*=1.1,c*=1.5,(d=e.min.add(f.scale(.5))).y=e.min.y-this._options.groundYBias}return{groundSize:b,skyboxSize:c,rootPosition:d}},a.prototype._setupGround=function(a){var b=this;this._ground&&!this._ground.isDisposed()||(this._ground=Object(Ah.a)("BackgroundPlane",{size:a.groundSize},this._scene),this._ground.rotation.x=Math.PI/2,this._ground.parent=this._rootMesh,this._ground.onDisposeObservable.add(function(){b._ground=null})),this._ground.receiveShadows=this._options.enableGroundShadow},a.prototype._setupGroundMaterial=function(){this._groundMaterial||(this._groundMaterial=new di("BackgroundPlaneMaterial",this._scene)),this._groundMaterial.alpha=this._options.groundOpacity,this._groundMaterial.alphaMode=r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,this._groundMaterial.shadowLevel=this._options.groundShadowLevel,this._groundMaterial.primaryColor=this._options.groundColor,this._groundMaterial.useRGBColor=!1,this._groundMaterial.enableNoise=!0,this._ground&&(this._ground.material=this._groundMaterial)},a.prototype._setupGroundDiffuseTexture=function(){this._groundMaterial&&(this._groundTexture||(this._options.groundTexture instanceof Ie.a?this._groundMaterial.diffuseTexture=this._options.groundTexture:(this._groundTexture=new U.a(this._options.groundTexture,this._scene,void 0,void 0,void 0,void 0,this._errorHandler),this._groundTexture.gammaSpace=!1,this._groundTexture.hasAlpha=!0,this._groundMaterial.diffuseTexture=this._groundTexture)))},a.prototype._setupGroundMirrorTexture=function(a){var b=U.a.CLAMP_ADDRESSMODE;if(!this._groundMirror&&(this._groundMirror=new Wh("BackgroundPlaneMirrorTexture",{ratio:this._options.groundMirrorSizeRatio},this._scene,!1,this._options.groundMirrorTextureType,U.a.BILINEAR_SAMPLINGMODE,!0),this._groundMirror.mirrorPlane=new Hb.a(0,-1,0,a.rootPosition.y),this._groundMirror.anisotropicFilteringLevel=1,this._groundMirror.wrapU=b,this._groundMirror.wrapV=b,this._groundMirror.gammaSpace=!1,this._groundMirror.renderList))for(a=0;a0&&a.push(this._texture),this._textureRoughness&&this._textureRoughness.animations&&this._textureRoughness.animations.length>0&&a.push(this._textureRoughness),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._tintTexture&&this._tintTexture.animations&&this._tintTexture.animations.length>0&&a.push(this._tintTexture)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose(),null===(a=this._textureRoughness)||void 0===a||a.dispose(),null===(a=this._bumpTexture)||void 0===a||a.dispose(),null===(a=this._tintTexture)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"PBRClearCoatConfiguration"},a.AddFallbacks=function(a,b,c){return a.CLEARCOAT_BUMP&&b.addFallback(c++,"CLEARCOAT_BUMP"),a.CLEARCOAT_TINT&&b.addFallback(c++,"CLEARCOAT_TINT"),a.CLEARCOAT&&b.addFallback(c++,"CLEARCOAT"),c},a.AddUniforms=function(a){a.push("vClearCoatTangentSpaceParams","vClearCoatParams","vClearCoatRefractionParams","vClearCoatTintParams","clearCoatColorAtDistance","clearCoatMatrix","clearCoatRoughnessMatrix","clearCoatBumpMatrix","clearCoatTintMatrix","vClearCoatInfos","vClearCoatBumpInfos","vClearCoatTintInfos")},a.AddSamplers=function(a){a.push("clearCoatSampler","clearCoatRoughnessSampler","clearCoatBumpSampler","clearCoatTintSampler")},a.PrepareUniformBuffer=function(a){a.addUniform("vClearCoatParams",2),a.addUniform("vClearCoatRefractionParams",4),a.addUniform("vClearCoatInfos",4),a.addUniform("clearCoatMatrix",16),a.addUniform("clearCoatRoughnessMatrix",16),a.addUniform("vClearCoatBumpInfos",2),a.addUniform("vClearCoatTangentSpaceParams",2),a.addUniform("clearCoatBumpMatrix",16),a.addUniform("vClearCoatTintParams",4),a.addUniform("clearCoatColorAtDistance",1),a.addUniform("vClearCoatTintInfos",2),a.addUniform("clearCoatTintMatrix",16)},a.prototype.copyTo=function(a){I.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return I.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;I.a.Parse(function(){return d},a,b,c)},a._DefaultIndexOfRefraction=1.5,Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(I.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(I.d)()],a.prototype,"roughness",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"indexOfRefraction",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useRoughnessFromMainTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"textureRoughness",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"remapF0OnInterfaceChange",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"bumpTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isTintEnabled",void 0),Object(l.c)([Object(I.f)()],a.prototype,"tintColor",void 0),Object(l.c)([Object(I.d)()],a.prototype,"tintColorAtDistance",void 0),Object(l.c)([Object(I.d)()],a.prototype,"tintThickness",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"tintTexture",void 0),a}(),li=function(){function a(a){this._isEnabled=!1,this.isEnabled=!1,this.intensity=1,this.direction=new g.d(1,0),this._texture=null,this.texture=null,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){return!this._isEnabled||!(a._areTexturesDirty&&b.texturesEnabled&&this._texture&&ai.a.AnisotropicTextureEnabled&&!this._texture.isReadyOrNotBlocking())},a.prototype.prepareDefines=function(a,b,c){this._isEnabled?(a.ANISOTROPIC=this._isEnabled,this._isEnabled&&!b.isVerticesDataPresent(W.b.TangentKind)&&(a._needUVs=!0,a.MAINUV1=!0),a._areTexturesDirty&&c.texturesEnabled&&(this._texture&&ai.a.AnisotropicTextureEnabled?Yh.a.PrepareDefinesForMergedUV(this._texture,a,"ANISOTROPIC_TEXTURE"):a.ANISOTROPIC_TEXTURE=!1)):(a.ANISOTROPIC=!1,a.ANISOTROPIC_TEXTURE=!1)},a.prototype.bindForSubMesh=function(a,b,c){this._isEnabled&&(a.useUbo&&c&&a.isSync||(this._texture&&ai.a.AnisotropicTextureEnabled&&(a.updateFloat2("vAnisotropyInfos",this._texture.coordinatesIndex,this._texture.level),Yh.a.BindTextureMatrix(this._texture,a,"anisotropy")),a.updateFloat3("vAnisotropy",this.direction.x,this.direction.y,this.intensity)),b.texturesEnabled&&this._texture&&ai.a.AnisotropicTextureEnabled&&a.setTexture("anisotropySampler",this._texture))},a.prototype.hasTexture=function(a){return this._texture===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture)},a.prototype.dispose=function(a){a&&this._texture&&this._texture.dispose()},a.prototype.getClassName=function(){return"PBRAnisotropicConfiguration"},a.AddFallbacks=function(a,b,c){return a.ANISOTROPIC&&b.addFallback(c++,"ANISOTROPIC"),c},a.AddUniforms=function(a){a.push("vAnisotropy","vAnisotropyInfos","anisotropyMatrix")},a.PrepareUniformBuffer=function(a){a.addUniform("vAnisotropy",3),a.addUniform("vAnisotropyInfos",2),a.addUniform("anisotropyMatrix",16)},a.AddSamplers=function(a){a.push("anisotropySampler")},a.prototype.copyTo=function(a){I.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return I.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;I.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(I.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(I.o)()],a.prototype,"direction",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),a}(),mi=function(){function a(b){this._useEnergyConservation=a.DEFAULT_USE_ENERGY_CONSERVATION,this.useEnergyConservation=a.DEFAULT_USE_ENERGY_CONSERVATION,this._useSmithVisibilityHeightCorrelated=a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED,this.useSmithVisibilityHeightCorrelated=a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED,this._useSphericalHarmonics=a.DEFAULT_USE_SPHERICAL_HARMONICS,this.useSphericalHarmonics=a.DEFAULT_USE_SPHERICAL_HARMONICS,this._useSpecularGlossinessInputEnergyConservation=a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION,this.useSpecularGlossinessInputEnergyConservation=a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION,this._internalMarkAllSubMeshesAsMiscDirty=b}return a.prototype._markAllSubMeshesAsMiscDirty=function(){this._internalMarkAllSubMeshesAsMiscDirty()},a.prototype.prepareDefines=function(a){a.BRDF_V_HEIGHT_CORRELATED=this._useSmithVisibilityHeightCorrelated,a.MS_BRDF_ENERGY_CONSERVATION=this._useEnergyConservation&&this._useSmithVisibilityHeightCorrelated,a.SPHERICAL_HARMONICS=this._useSphericalHarmonics,a.SPECULAR_GLOSSINESS_ENERGY_CONSERVATION=this._useSpecularGlossinessInputEnergyConservation},a.prototype.getClassName=function(){return"PBRBRDFConfiguration"},a.prototype.copyTo=function(a){I.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return I.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;I.a.Parse(function(){return d},a,b,c)},a.DEFAULT_USE_ENERGY_CONSERVATION=!0,a.DEFAULT_USE_SMITH_VISIBILITY_HEIGHT_CORRELATED=!0,a.DEFAULT_USE_SPHERICAL_HARMONICS=!0,a.DEFAULT_USE_SPECULAR_GLOSSINESS_INPUT_ENERGY_CONSERVATION=!0,Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useEnergyConservation",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSmithVisibilityHeightCorrelated",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSphericalHarmonics",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsMiscDirty")],a.prototype,"useSpecularGlossinessInputEnergyConservation",void 0),a}(),ni=function(){function a(a){this._isEnabled=!1,this.isEnabled=!1,this._linkSheenWithAlbedo=!1,this.linkSheenWithAlbedo=!1,this.intensity=1,this.color=h.a.White(),this._texture=null,this.texture=null,this._useRoughnessFromMainTexture=!0,this.useRoughnessFromMainTexture=!0,this._roughness=null,this.roughness=null,this._textureRoughness=null,this.textureRoughness=null,this._albedoScaling=!1,this.albedoScaling=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a}return a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isEnabled)return!0;if(a._areTexturesDirty&&b.texturesEnabled){if(this._texture&&ai.a.SheenTextureEnabled&&!this._texture.isReadyOrNotBlocking())return!1;if(this._textureRoughness&&ai.a.SheenTextureEnabled&&!this._textureRoughness.isReadyOrNotBlocking())return!1}return!0},a.prototype.prepareDefines=function(a,b){var c;this._isEnabled?(a.SHEEN=this._isEnabled,a.SHEEN_LINKWITHALBEDO=this._linkSheenWithAlbedo,a.SHEEN_ROUGHNESS=null!==this._roughness,a.SHEEN_ALBEDOSCALING=this._albedoScaling,a.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=this._useRoughnessFromMainTexture,a.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=null!==this._texture&&this._texture._texture===(null===(c=this._textureRoughness)||void 0===c?void 0:c._texture)&&this._texture.checkTransformsAreIdentical(this._textureRoughness),a._areTexturesDirty&&b.texturesEnabled&&(this._texture&&ai.a.SheenTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._texture,a,"SHEEN_TEXTURE"),a.SHEEN_GAMMATEXTURE=this._texture.gammaSpace):a.SHEEN_TEXTURE=!1,this._textureRoughness&&ai.a.SheenTextureEnabled?Yh.a.PrepareDefinesForMergedUV(this._textureRoughness,a,"SHEEN_TEXTURE_ROUGHNESS"):a.SHEEN_TEXTURE_ROUGHNESS=!1)):(a.SHEEN=!1,a.SHEEN_TEXTURE=!1,a.SHEEN_TEXTURE_ROUGHNESS=!1,a.SHEEN_LINKWITHALBEDO=!1,a.SHEEN_ROUGHNESS=!1,a.SHEEN_ALBEDOSCALING=!1,a.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,a.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=!1)},a.prototype.bindForSubMesh=function(a,b,c,d){if(this._isEnabled){d=d._materialDefines;var e=d.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL;a.useUbo&&c&&a.isSync||(e&&ai.a.SheenTextureEnabled?(a.updateFloat4("vSheenInfos",this._texture.coordinatesIndex,this._texture.level,-1,-1),Yh.a.BindTextureMatrix(this._texture,a,"sheen")):(this._texture||this._textureRoughness)&&ai.a.SheenTextureEnabled&&(a.updateFloat4("vSheenInfos",null!==(c=null===(c=this._texture)||void 0===c?void 0:c.coordinatesIndex)&&void 0!==c?c:0,null!==(c=null===(c=this._texture)||void 0===c?void 0:c.level)&&void 0!==c?c:0,null!==(c=null===(c=this._textureRoughness)||void 0===c?void 0:c.coordinatesIndex)&&void 0!==c?c:0,null!==(c=null===(c=this._textureRoughness)||void 0===c?void 0:c.level)&&void 0!==c?c:0),this._texture&&Yh.a.BindTextureMatrix(this._texture,a,"sheen"),!this._textureRoughness||e||d.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE||Yh.a.BindTextureMatrix(this._textureRoughness,a,"sheenRoughness")),a.updateFloat4("vSheenColor",this.color.r,this.color.g,this.color.b,this.intensity),null!==this._roughness&&a.updateFloat("vSheenRoughness",this._roughness)),b.texturesEnabled&&(this._texture&&ai.a.SheenTextureEnabled&&a.setTexture("sheenSampler",this._texture),this._textureRoughness&&!e&&!d.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE&&ai.a.SheenTextureEnabled&&a.setTexture("sheenRoughnessSampler",this._textureRoughness))}},a.prototype.hasTexture=function(a){return this._texture===a||this._textureRoughness===a},a.prototype.getActiveTextures=function(a){this._texture&&a.push(this._texture),this._textureRoughness&&a.push(this._textureRoughness)},a.prototype.getAnimatables=function(a){this._texture&&this._texture.animations&&this._texture.animations.length>0&&a.push(this._texture),this._textureRoughness&&this._textureRoughness.animations&&this._textureRoughness.animations.length>0&&a.push(this._textureRoughness)},a.prototype.dispose=function(a){a&&(null===(a=this._texture)||void 0===a||a.dispose(),null===(a=this._textureRoughness)||void 0===a||a.dispose())},a.prototype.getClassName=function(){return"PBRSheenConfiguration"},a.AddFallbacks=function(a,b,c){return a.SHEEN&&b.addFallback(c++,"SHEEN"),c},a.AddUniforms=function(a){a.push("vSheenColor","vSheenRoughness","vSheenInfos","sheenMatrix","sheenRoughnessMatrix")},a.PrepareUniformBuffer=function(a){a.addUniform("vSheenColor",4),a.addUniform("vSheenRoughness",1),a.addUniform("vSheenInfos",4),a.addUniform("sheenMatrix",16),a.addUniform("sheenRoughnessMatrix",16)},a.AddSamplers=function(a){a.push("sheenSampler"),a.push("sheenRoughnessSampler")},a.prototype.copyTo=function(a){I.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return I.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;I.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isEnabled",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"linkSheenWithAlbedo",void 0),Object(l.c)([Object(I.d)()],a.prototype,"intensity",void 0),Object(l.c)([Object(I.f)()],a.prototype,"color",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"texture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useRoughnessFromMainTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"roughness",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"textureRoughness",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"albedoScaling",void 0),a}(),oi=function(){function a(a,b,c){this._isRefractionEnabled=!1,this.isRefractionEnabled=!1,this._isTranslucencyEnabled=!1,this.isTranslucencyEnabled=!1,this._isScatteringEnabled=!1,this.isScatteringEnabled=!1,this._scatteringDiffusionProfileIndex=0,this.refractionIntensity=1,this.translucencyIntensity=1,this.useAlbedoToTintRefraction=!1,this.useAlbedoToTintTranslucency=!1,this._thicknessTexture=null,this.thicknessTexture=null,this._refractionTexture=null,this.refractionTexture=null,this._indexOfRefraction=1.5,this.indexOfRefraction=1.5,this._volumeIndexOfRefraction=-1,this._invertRefractionY=!1,this.invertRefractionY=!1,this._linkRefractionWithTransparency=!1,this.linkRefractionWithTransparency=!1,this.minimumThickness=0,this.maximumThickness=1,this.useThicknessAsDepth=!1,this.tintColor=h.a.White(),this.tintColorAtDistance=1,this.diffusionDistance=h.a.White(),this._useMaskFromThicknessTexture=!1,this.useMaskFromThicknessTexture=!1,this._refractionIntensityTexture=null,this.refractionIntensityTexture=null,this._translucencyIntensityTexture=null,this.translucencyIntensityTexture=null,this._useGltfStyleTextures=!1,this.useGltfStyleTextures=!1,this._internalMarkAllSubMeshesAsTexturesDirty=a,this._internalMarkScenePrePassDirty=b,this._scene=c}return Object.defineProperty(a.prototype,"scatteringDiffusionProfile",{get:function(){return this._scene.subSurfaceConfiguration?this._scene.subSurfaceConfiguration.ssDiffusionProfileColors[this._scatteringDiffusionProfileIndex]:null},set:function(a){this._scene.enableSubSurfaceForPrePass()&&a&&(this._scatteringDiffusionProfileIndex=this._scene.subSurfaceConfiguration.addDiffusionProfile(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"volumeIndexOfRefraction",{get:function(){return this._volumeIndexOfRefraction>=1?this._volumeIndexOfRefraction:this._indexOfRefraction},set:function(a){this._volumeIndexOfRefraction=a>=1?a:-1},enumerable:!1,configurable:!0}),a.prototype._markAllSubMeshesAsTexturesDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty()},a.prototype._markScenePrePassDirty=function(){this._internalMarkAllSubMeshesAsTexturesDirty(),this._internalMarkScenePrePassDirty()},a.prototype.isReadyForSubMesh=function(a,b){if(!this._isRefractionEnabled&&!this._isTranslucencyEnabled&&!this._isScatteringEnabled)return!0;if(a._areTexturesDirty&&b.texturesEnabled){if(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&!this._thicknessTexture.isReadyOrNotBlocking())return!1;a=this._getRefractionTexture(b);if(a&&ai.a.RefractionTextureEnabled&&!a.isReadyOrNotBlocking())return!1}return!0},a.prototype.prepareDefines=function(a,b){if(!this._isRefractionEnabled&&!this._isTranslucencyEnabled&&!this._isScatteringEnabled)return a.SUBSURFACE=!1,a.SS_TRANSLUCENCY=!1,a.SS_SCATTERING=!1,void (a.SS_REFRACTION=!1);if(a._areTexturesDirty){if(a.SUBSURFACE=!1,a.SS_TRANSLUCENCY=this._isTranslucencyEnabled,a.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=!1,a.SS_SCATTERING=this._isScatteringEnabled,a.SS_THICKNESSANDMASK_TEXTURE=!1,a.SS_REFRACTIONINTENSITY_TEXTURE=!1,a.SS_TRANSLUCENCYINTENSITY_TEXTURE=!1,a.SS_HAS_THICKNESS=!1,a.SS_MASK_FROM_THICKNESS_TEXTURE=!1,a.SS_USE_GLTF_TEXTURES=!1,a.SS_REFRACTION=!1,a.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=!1,a.SS_REFRACTIONMAP_3D=!1,a.SS_GAMMAREFRACTION=!1,a.SS_RGBDREFRACTION=!1,a.SS_LINEARSPECULARREFRACTION=!1,a.SS_REFRACTIONMAP_OPPOSITEZ=!1,a.SS_LODINREFRACTIONALPHA=!1,a.SS_LINKREFRACTIONTOTRANSPARENCY=!1,a.SS_ALBEDOFORREFRACTIONTINT=!1,a.SS_ALBEDOFORTRANSLUCENCYTINT=!1,a.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=!1,a.SS_USE_THICKNESS_AS_DEPTH=!1,this._isRefractionEnabled||this._isTranslucencyEnabled||this._isScatteringEnabled){a.SUBSURFACE=!0;var c=!!this._thicknessTexture&&!!this._refractionIntensityTexture&&this._refractionIntensityTexture.checkTransformsAreIdentical(this._thicknessTexture)&&this._refractionIntensityTexture._texture===this._thicknessTexture._texture,d=!!this._thicknessTexture&&!!this._translucencyIntensityTexture&&this._translucencyIntensityTexture.checkTransformsAreIdentical(this._thicknessTexture)&&this._translucencyIntensityTexture._texture===this._thicknessTexture._texture;c=(c||!this._refractionIntensityTexture)&&(d||!this._translucencyIntensityTexture);a._areTexturesDirty&&b.texturesEnabled&&(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&Yh.a.PrepareDefinesForMergedUV(this._thicknessTexture,a,"SS_THICKNESSANDMASK_TEXTURE"),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&!c&&Yh.a.PrepareDefinesForMergedUV(this._refractionIntensityTexture,a,"SS_REFRACTIONINTENSITY_TEXTURE"),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&!c&&Yh.a.PrepareDefinesForMergedUV(this._translucencyIntensityTexture,a,"SS_TRANSLUCENCYINTENSITY_TEXTURE")),a.SS_HAS_THICKNESS=this.maximumThickness-this.minimumThickness!=0,a.SS_MASK_FROM_THICKNESS_TEXTURE=(this._useMaskFromThicknessTexture||!!this._refractionIntensityTexture||!!this._translucencyIntensityTexture)&&c,a.SS_USE_GLTF_TEXTURES=this._useGltfStyleTextures,a.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=(this._useMaskFromThicknessTexture||!!this._refractionIntensityTexture)&&c,a.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=(this._useMaskFromThicknessTexture||!!this._translucencyIntensityTexture)&&c}if(this._isRefractionEnabled&&b.texturesEnabled){d=this._getRefractionTexture(b);d&&ai.a.RefractionTextureEnabled&&(a.SS_REFRACTION=!0,a.SS_REFRACTIONMAP_3D=d.isCube,a.SS_GAMMAREFRACTION=d.gammaSpace,a.SS_RGBDREFRACTION=d.isRGBD,a.SS_LINEARSPECULARREFRACTION=d.linearSpecularLOD,a.SS_REFRACTIONMAP_OPPOSITEZ=d.invertZ,a.SS_LODINREFRACTIONALPHA=d.lodLevelInAlpha,a.SS_LINKREFRACTIONTOTRANSPARENCY=this._linkRefractionWithTransparency,a.SS_ALBEDOFORREFRACTIONTINT=this.useAlbedoToTintRefraction,a.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=d.isCube&&d.boundingBoxSize,a.SS_USE_THICKNESS_AS_DEPTH=this.useThicknessAsDepth)}this._isTranslucencyEnabled&&(a.SS_ALBEDOFORTRANSLUCENCYTINT=this.useAlbedoToTintTranslucency)}},a.prototype.bindForSubMesh=function(a,b,c,d,e,f,h){if(this._isRefractionEnabled||this._isTranslucencyEnabled||this._isScatteringEnabled){c=h._materialDefines;var i=this._getRefractionTexture(b);if(!a.useUbo||!d||!a.isSync){this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&(a.updateFloat2("vThicknessInfos",this._thicknessTexture.coordinatesIndex,this._thicknessTexture.level),Yh.a.BindTextureMatrix(this._thicknessTexture,a,"thickness")),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&c.SS_REFRACTIONINTENSITY_TEXTURE&&(a.updateFloat2("vRefractionIntensityInfos",this._refractionIntensityTexture.coordinatesIndex,this._refractionIntensityTexture.level),Yh.a.BindTextureMatrix(this._refractionIntensityTexture,a,"refractionIntensity")),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&c.SS_TRANSLUCENCYINTENSITY_TEXTURE&&(a.updateFloat2("vTranslucencyIntensityInfos",this._translucencyIntensityTexture.coordinatesIndex,this._translucencyIntensityTexture.level),Yh.a.BindTextureMatrix(this._translucencyIntensityTexture,a,"translucencyIntensity")),h.getRenderingMesh().getWorldMatrix().decompose(g.c.Vector3[0]);d=Math.max(Math.abs(g.c.Vector3[0].x),Math.abs(g.c.Vector3[0].y),Math.abs(g.c.Vector3[0].z));if(a.updateFloat2("vThicknessParam",this.minimumThickness*d,(this.maximumThickness-this.minimumThickness)*d),i&&ai.a.RefractionTextureEnabled){a.updateMatrix("refractionMatrix",i.getReflectionTextureMatrix());h=1;i.isCube||i.depth&&(h=i.depth);d=i.getSize().width;var j=this.volumeIndexOfRefraction;if(a.updateFloat4("vRefractionInfos",i.level,1/j,h,this._invertRefractionY?-1:1),a.updateFloat4("vRefractionMicrosurfaceInfos",d,i.lodGenerationScale,i.lodGenerationOffset,1/this.indexOfRefraction),f&&a.updateFloat2("vRefractionFilteringInfo",d,H.a.Log2(d)),i.boundingBoxSize){j=i;a.updateVector3("vRefractionPosition",j.boundingBoxPosition),a.updateVector3("vRefractionSize",j.boundingBoxSize)}}this.isScatteringEnabled&&a.updateFloat("scatteringDiffusionProfile",this._scatteringDiffusionProfileIndex),a.updateColor3("vDiffusionDistance",this.diffusionDistance),a.updateFloat4("vTintColor",this.tintColor.r,this.tintColor.g,this.tintColor.b,Math.max(1e-5,this.tintColorAtDistance)),a.updateFloat3("vSubSurfaceIntensity",this.refractionIntensity,this.translucencyIntensity,0)}b.texturesEnabled&&(this._thicknessTexture&&ai.a.ThicknessTextureEnabled&&a.setTexture("thicknessSampler",this._thicknessTexture),this._refractionIntensityTexture&&ai.a.RefractionIntensityTextureEnabled&&c.SS_REFRACTIONINTENSITY_TEXTURE&&a.setTexture("refractionIntensitySampler",this._refractionIntensityTexture),this._translucencyIntensityTexture&&ai.a.TranslucencyIntensityTextureEnabled&&c.SS_TRANSLUCENCYINTENSITY_TEXTURE&&a.setTexture("translucencyIntensitySampler",this._translucencyIntensityTexture),i&&ai.a.RefractionTextureEnabled&&(e?a.setTexture("refractionSampler",i):(a.setTexture("refractionSampler",i._lodTextureMid||i),a.setTexture("refractionSamplerLow",i._lodTextureLow||i),a.setTexture("refractionSamplerHigh",i._lodTextureHigh||i))))}},a.prototype.unbind=function(a){return!(!this._refractionTexture||!this._refractionTexture.isRenderTarget)&&(a.setTexture("refractionSampler",null),!0)},a.prototype._getRefractionTexture=function(a){return this._refractionTexture?this._refractionTexture:this._isRefractionEnabled?a.environmentTexture:null},Object.defineProperty(a.prototype,"disableAlphaBlending",{get:function(){return this.isRefractionEnabled&&this._linkRefractionWithTransparency},enumerable:!1,configurable:!0}),a.prototype.fillRenderTargetTextures=function(a){ai.a.RefractionTextureEnabled&&this._refractionTexture&&this._refractionTexture.isRenderTarget&&a.push(this._refractionTexture)},a.prototype.hasTexture=function(a){return this._thicknessTexture===a||this._refractionTexture===a},a.prototype.hasRenderTargetTextures=function(){return!!(ai.a.RefractionTextureEnabled&&this._refractionTexture&&this._refractionTexture.isRenderTarget)},a.prototype.getActiveTextures=function(a){this._thicknessTexture&&a.push(this._thicknessTexture),this._refractionTexture&&a.push(this._refractionTexture)},a.prototype.getAnimatables=function(a){this._thicknessTexture&&this._thicknessTexture.animations&&this._thicknessTexture.animations.length>0&&a.push(this._thicknessTexture),this._refractionTexture&&this._refractionTexture.animations&&this._refractionTexture.animations.length>0&&a.push(this._refractionTexture)},a.prototype.dispose=function(a){a&&(this._thicknessTexture&&this._thicknessTexture.dispose(),this._refractionTexture&&this._refractionTexture.dispose())},a.prototype.getClassName=function(){return"PBRSubSurfaceConfiguration"},a.AddFallbacks=function(a,b,c){return a.SS_SCATTERING&&b.addFallback(c++,"SS_SCATTERING"),a.SS_TRANSLUCENCY&&b.addFallback(c++,"SS_TRANSLUCENCY"),c},a.AddUniforms=function(a){a.push("vDiffusionDistance","vTintColor","vSubSurfaceIntensity","vRefractionMicrosurfaceInfos","vRefractionFilteringInfo","vRefractionInfos","vThicknessInfos","vRefractionIntensityInfos","vTranslucencyIntensityInfos","vThicknessParam","vRefractionPosition","vRefractionSize","refractionMatrix","thicknessMatrix","refractionIntensityMatrix","translucencyIntensityMatrix","scatteringDiffusionProfile")},a.AddSamplers=function(a){a.push("thicknessSampler","refractionIntensitySampler","translucencyIntensitySampler","refractionSampler","refractionSamplerLow","refractionSamplerHigh")},a.PrepareUniformBuffer=function(a){a.addUniform("vRefractionMicrosurfaceInfos",4),a.addUniform("vRefractionFilteringInfo",2),a.addUniform("vTranslucencyIntensityInfos",2),a.addUniform("vRefractionInfos",4),a.addUniform("refractionMatrix",16),a.addUniform("vThicknessInfos",2),a.addUniform("vRefractionIntensityInfos",2),a.addUniform("thicknessMatrix",16),a.addUniform("refractionIntensityMatrix",16),a.addUniform("translucencyIntensityMatrix",16),a.addUniform("vThicknessParam",2),a.addUniform("vDiffusionDistance",3),a.addUniform("vTintColor",4),a.addUniform("vSubSurfaceIntensity",3),a.addUniform("vRefractionPosition",3),a.addUniform("vRefractionSize",3),a.addUniform("scatteringDiffusionProfile",1)},a.prototype.copyTo=function(a){I.a.Clone(function(){return a},this)},a.prototype.serialize=function(){return I.a.Serialize(this)},a.prototype.parse=function(a,b,c){var d=this;I.a.Parse(function(){return d},a,b,c)},Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isRefractionEnabled",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"isTranslucencyEnabled",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markScenePrePassDirty")],a.prototype,"isScatteringEnabled",void 0),Object(l.c)([Object(I.d)()],a.prototype,"_scatteringDiffusionProfileIndex",void 0),Object(l.c)([Object(I.d)()],a.prototype,"refractionIntensity",void 0),Object(l.c)([Object(I.d)()],a.prototype,"translucencyIntensity",void 0),Object(l.c)([Object(I.d)()],a.prototype,"useAlbedoToTintRefraction",void 0),Object(l.c)([Object(I.d)()],a.prototype,"useAlbedoToTintTranslucency",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"thicknessTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"refractionTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"indexOfRefraction",void 0),Object(l.c)([Object(I.d)()],a.prototype,"_volumeIndexOfRefraction",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"volumeIndexOfRefraction",null),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"invertRefractionY",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"linkRefractionWithTransparency",void 0),Object(l.c)([Object(I.d)()],a.prototype,"minimumThickness",void 0),Object(l.c)([Object(I.d)()],a.prototype,"maximumThickness",void 0),Object(l.c)([Object(I.d)()],a.prototype,"useThicknessAsDepth",void 0),Object(l.c)([Object(I.f)()],a.prototype,"tintColor",void 0),Object(l.c)([Object(I.d)()],a.prototype,"tintColorAtDistance",void 0),Object(l.c)([Object(I.f)()],a.prototype,"diffusionDistance",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useMaskFromThicknessTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"refractionIntensityTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"translucencyIntensityTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],a.prototype,"useGltfStyleTextures",void 0),a}(),pi=c(119),qi=c(29);V=(c(190),c(191),"uniform vec4 vEyePosition; uniform vec3 vReflectionColor; uniform vec4 vAlbedoColor; uniform vec4 vLightingIntensity; uniform vec4 vReflectivityColor; uniform vec4 vMetallicReflectanceFactors; uniform vec3 vEmissiveColor; uniform float visibility; uniform vec3 vAmbientColor; #ifdef ALBEDO uniform vec2 vAlbedoInfos; #endif #ifdef AMBIENT uniform vec4 vAmbientInfos; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #ifdef OPACITY uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; #endif #ifdef REFLECTIVITY uniform vec3 vReflectivityInfos; #endif #ifdef MICROSURFACEMAP uniform vec2 vMicroSurfaceSamplerInfos; #endif #if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION) || defined(PREPASS) uniform mat4 view; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; #ifdef REALTIME_FILTERING uniform vec2 vReflectionFilteringInfo; #endif uniform mat4 reflectionMatrix; uniform vec3 vReflectionMicrosurfaceInfos; #if defined(USE_LOCAL_REFLECTIONMAP_CUBIC) && defined(REFLECTIONMAP_CUBIC) uniform vec3 vReflectionPosition; uniform vec3 vReflectionSize; #endif #endif #if defined(SS_REFRACTION) && defined(SS_USE_LOCAL_REFRACTIONMAP_CUBIC) uniform vec3 vRefractionPosition; uniform vec3 vRefractionSize; #endif #ifdef CLEARCOAT uniform vec2 vClearCoatParams; uniform vec4 vClearCoatRefractionParams; #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS) uniform vec4 vClearCoatInfos; #endif #ifdef CLEARCOAT_TEXTURE uniform mat4 clearCoatMatrix; #endif #ifdef CLEARCOAT_TEXTURE_ROUGHNESS uniform mat4 clearCoatRoughnessMatrix; #endif #ifdef CLEARCOAT_BUMP uniform vec2 vClearCoatBumpInfos; uniform vec2 vClearCoatTangentSpaceParams; uniform mat4 clearCoatBumpMatrix; #endif #ifdef CLEARCOAT_TINT uniform vec4 vClearCoatTintParams; uniform float clearCoatColorAtDistance; #ifdef CLEARCOAT_TINT_TEXTURE uniform vec2 vClearCoatTintInfos; uniform mat4 clearCoatTintMatrix; #endif #endif #endif #ifdef ANISOTROPIC uniform vec3 vAnisotropy; #ifdef ANISOTROPIC_TEXTURE uniform vec2 vAnisotropyInfos; uniform mat4 anisotropyMatrix; #endif #endif #ifdef SHEEN uniform vec4 vSheenColor; #ifdef SHEEN_ROUGHNESS uniform float vSheenRoughness; #endif #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS) uniform vec4 vSheenInfos; #endif #ifdef SHEEN_TEXTURE uniform mat4 sheenMatrix; #endif #ifdef SHEEN_TEXTURE_ROUGHNESS uniform mat4 sheenRoughnessMatrix; #endif #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION uniform vec4 vRefractionMicrosurfaceInfos; uniform vec4 vRefractionInfos; uniform mat4 refractionMatrix; #ifdef REALTIME_FILTERING uniform vec2 vRefractionFilteringInfo; #endif #endif #ifdef SS_THICKNESSANDMASK_TEXTURE uniform vec2 vThicknessInfos; uniform mat4 thicknessMatrix; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE uniform vec2 vRefractionIntensityInfos; uniform mat4 refractionIntensityMatrix; #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE uniform vec2 vTranslucencyIntensityInfos; uniform mat4 translucencyIntensityMatrix; #endif uniform vec2 vThicknessParam; uniform vec3 vDiffusionDistance; uniform vec4 vTintColor; uniform vec3 vSubSurfaceIntensity; #endif #ifdef PREPASS #ifdef SS_SCATTERING uniform float scatteringDiffusionProfile; #endif #endif #if DEBUGMODE>0 uniform vec2 vDebugMode; #endif #ifdef DETAIL uniform vec4 vDetailInfos; #endif #ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS uniform vec3 vSphericalL00; uniform vec3 vSphericalL1_1; uniform vec3 vSphericalL10; uniform vec3 vSphericalL11; uniform vec3 vSphericalL2_2; uniform vec3 vSphericalL2_1; uniform vec3 vSphericalL20; uniform vec3 vSphericalL21; uniform vec3 vSphericalL22; #else uniform vec3 vSphericalX; uniform vec3 vSphericalY; uniform vec3 vSphericalZ; uniform vec3 vSphericalXX_ZZ; uniform vec3 vSphericalYY_ZZ; uniform vec3 vSphericalZZ; uniform vec3 vSphericalXY; uniform vec3 vSphericalYZ; uniform vec3 vSphericalZX; #endif #endif ");X.a.IncludesShadersStore.pbrFragmentDeclaration=V;c(180);a="layout(std140,column_major) uniform; uniform Material { vec2 vAlbedoInfos; vec4 vAmbientInfos; vec2 vOpacityInfos; vec2 vEmissiveInfos; vec2 vLightmapInfos; vec3 vReflectivityInfos; vec2 vMicroSurfaceSamplerInfos; vec2 vReflectionInfos; vec2 vReflectionFilteringInfo; vec3 vReflectionPosition; vec3 vReflectionSize; vec3 vBumpInfos; mat4 albedoMatrix; mat4 ambientMatrix; mat4 opacityMatrix; mat4 emissiveMatrix; mat4 lightmapMatrix; mat4 reflectivityMatrix; mat4 microSurfaceSamplerMatrix; mat4 bumpMatrix; vec2 vTangentSpaceParams; mat4 reflectionMatrix; vec3 vReflectionColor; vec4 vAlbedoColor; vec4 vLightingIntensity; vec3 vReflectionMicrosurfaceInfos; float pointSize; vec4 vReflectivityColor; vec3 vEmissiveColor; vec3 vAmbientColor; vec2 vDebugMode; vec4 vMetallicReflectanceFactors; vec2 vMetallicReflectanceInfos; mat4 metallicReflectanceMatrix; vec2 vReflectanceInfos; mat4 reflectanceMatrix; vec2 vClearCoatParams; vec4 vClearCoatRefractionParams; vec4 vClearCoatInfos; mat4 clearCoatMatrix; mat4 clearCoatRoughnessMatrix; vec2 vClearCoatBumpInfos; vec2 vClearCoatTangentSpaceParams; mat4 clearCoatBumpMatrix; vec4 vClearCoatTintParams; float clearCoatColorAtDistance; vec2 vClearCoatTintInfos; mat4 clearCoatTintMatrix; vec3 vAnisotropy; vec2 vAnisotropyInfos; mat4 anisotropyMatrix; vec4 vSheenColor; float vSheenRoughness; vec4 vSheenInfos; mat4 sheenMatrix; mat4 sheenRoughnessMatrix; vec4 vRefractionMicrosurfaceInfos; vec2 vRefractionFilteringInfo; vec2 vTranslucencyIntensityInfos; vec4 vRefractionInfos; mat4 refractionMatrix; vec2 vThicknessInfos; vec2 vRefractionIntensityInfos; mat4 thicknessMatrix; mat4 refractionIntensityMatrix; mat4 translucencyIntensityMatrix; vec2 vThicknessParam; vec3 vDiffusionDistance; vec4 vTintColor; vec3 vSubSurfaceIntensity; vec3 vRefractionPosition; vec3 vRefractionSize; float scatteringDiffusionProfile; vec4 vDetailInfos; mat4 detailMatrix; vec3 vSphericalL00; vec3 vSphericalL1_1; vec3 vSphericalL10; vec3 vSphericalL11; vec3 vSphericalL2_2; vec3 vSphericalL2_1; vec3 vSphericalL20; vec3 vSphericalL21; vec3 vSphericalL22; vec3 vSphericalX; vec3 vSphericalY; vec3 vSphericalZ; vec3 vSphericalXX_ZZ; vec3 vSphericalYY_ZZ; vec3 vSphericalZZ; vec3 vSphericalXY; vec3 vSphericalYZ; vec3 vSphericalZX; }; #include #include ";X.a.IncludesShadersStore.pbrUboDeclaration=a;c(149);V=" varying vec3 vPositionW; #if DEBUGMODE>0 varying vec4 vClipSpacePosition; #endif #include[1..7] #ifdef NORMAL varying vec3 vNormalW; #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) varying vec3 vEnvironmentIrradiance; #endif #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif";X.a.IncludesShadersStore.pbrFragmentExtraDeclaration=V;c(182);a="#ifdef _DEFINENAME_ #if _DEFINENAME_DIRECTUV == 1 #define v_VARYINGNAME_UV vMainUV1 #elif _DEFINENAME_DIRECTUV == 2 #define v_VARYINGNAME_UV vMainUV2 #elif _DEFINENAME_DIRECTUV == 3 #define v_VARYINGNAME_UV vMainUV3 #elif _DEFINENAME_DIRECTUV == 4 #define v_VARYINGNAME_UV vMainUV4 #elif _DEFINENAME_DIRECTUV == 5 #define v_VARYINGNAME_UV vMainUV5 #elif _DEFINENAME_DIRECTUV == 6 #define v_VARYINGNAME_UV vMainUV6 #else varying vec2 v_VARYINGNAME_UV; #endif #endif ";X.a.IncludesShadersStore.samplerFragmentAlternateDeclaration=a;V="#include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_SAMPLERNAME_,albedo) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_SAMPLERNAME_,lightmap) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity,_SAMPLERNAME_,reflectivity) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler,_SAMPLERNAME_,microSurface) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance,_SAMPLERNAME_,metallicReflectance) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance,_SAMPLERNAME_,reflectance) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat,_SAMPLERNAME_,clearCoat) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness) #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) uniform sampler2D clearCoatRoughnessSampler; #endif #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump,_SAMPLERNAME_,clearCoatBump) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint,_SAMPLERNAME_,clearCoatTint) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen,_SAMPLERNAME_,sheen) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness) #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) uniform sampler2D sheenRoughnessSampler; #endif #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy,_SAMPLERNAME_,anisotropy) #endif #ifdef REFLECTION #ifdef REFLECTIONMAP_3D #define sampleReflection(s,c) textureCube(s,c) uniform samplerCube reflectionSampler; #ifdef LODBASEDMICROSFURACE #define sampleReflectionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube reflectionSamplerLow; uniform samplerCube reflectionSamplerHigh; #endif #ifdef USEIRRADIANCEMAP uniform samplerCube irradianceSampler; #endif #else #define sampleReflection(s,c) texture2D(s,c) uniform sampler2D reflectionSampler; #ifdef LODBASEDMICROSFURACE #define sampleReflectionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform sampler2D reflectionSamplerLow; uniform sampler2D reflectionSamplerHigh; #endif #ifdef USEIRRADIANCEMAP uniform sampler2D irradianceSampler; #endif #endif #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #else #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #endif #endif #ifdef ENVIRONMENTBRDF uniform sampler2D environmentBrdfSampler; #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION #ifdef SS_REFRACTIONMAP_3D #define sampleRefraction(s,c) textureCube(s,c) uniform samplerCube refractionSampler; #ifdef LODBASEDMICROSFURACE #define sampleRefractionLod(s,c,l) textureCubeLodEXT(s,c,l) #else uniform samplerCube refractionSamplerLow; uniform samplerCube refractionSamplerHigh; #endif #else #define sampleRefraction(s,c) texture2D(s,c) uniform sampler2D refractionSampler; #ifdef LODBASEDMICROSFURACE #define sampleRefractionLod(s,c,l) texture2DLodEXT(s,c,l) #else uniform sampler2D refractionSamplerLow; uniform sampler2D refractionSamplerHigh; #endif #endif #endif #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness,_SAMPLERNAME_,thickness) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity,_SAMPLERNAME_,refractionIntensity) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity,_SAMPLERNAME_,translucencyIntensity) #endif";X.a.IncludesShadersStore.pbrFragmentSamplersDeclaration=V;c(127);X.a.IncludesShadersStore.subSurfaceScatteringFunctions="bool testLightingForSSS(float diffusionProfile) { return diffusionProfile<1.; }";a=" vec3 hemisphereCosSample(vec2 u) { float phi=2.*PI*u.x; float cosTheta2=1.-u.y; float cosTheta=sqrt(cosTheta2); float sinTheta=sqrt(1.-cosTheta2); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); } vec3 hemisphereImportanceSampleDggx(vec2 u,float a) { float phi=2.*PI*u.x; float cosTheta2=(1.-u.y)/(1.+(a+1.)*((a-1.)*u.y)); float cosTheta=sqrt(cosTheta2); float sinTheta=sqrt(1.-cosTheta2); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); } vec3 hemisphereImportanceSampleDCharlie(vec2 u,float a) { float phi=2.*PI*u.x; float sinTheta=pow(u.y,a/(2.*a+1.)); float cosTheta=sqrt(1.-sinTheta*sinTheta); return vec3(sinTheta*cos(phi),sinTheta*sin(phi),cosTheta); }";X.a.IncludesShadersStore.importanceSampling=a;V=" #define RECIPROCAL_PI2 0.15915494 #define RECIPROCAL_PI 0.31830988618 #define MINIMUMVARIANCE 0.0005 float convertRoughnessToAverageSlope(float roughness) { return square(roughness)+MINIMUMVARIANCE; } float fresnelGrazingReflectance(float reflectance0) { float reflectance90=saturate(reflectance0*25.0); return reflectance90; } vec2 getAARoughnessFactors(vec3 normalVector) { #ifdef SPECULARAA vec3 nDfdx=dFdx(normalVector.xyz); vec3 nDfdy=dFdy(normalVector.xyz); float slopeSquare=max(dot(nDfdx,nDfdx),dot(nDfdy,nDfdy)); float geometricRoughnessFactor=pow(saturate(slopeSquare),0.333); float geometricAlphaGFactor=sqrt(slopeSquare); geometricAlphaGFactor*=0.75; return vec2(geometricRoughnessFactor,geometricAlphaGFactor); #else return vec2(0.); #endif } #ifdef ANISOTROPIC vec2 getAnisotropicRoughness(float alphaG,float anisotropy) { float alphaT=max(alphaG*(1.0+anisotropy),MINIMUMVARIANCE); float alphaB=max(alphaG*(1.0-anisotropy),MINIMUMVARIANCE); return vec2(alphaT,alphaB); } vec3 getAnisotropicBentNormals(const vec3 T,const vec3 B,const vec3 N,const vec3 V,float anisotropy) { vec3 anisotropicFrameDirection=anisotropy>=0.0 ? B : T; vec3 anisotropicFrameTangent=cross(normalize(anisotropicFrameDirection),V); vec3 anisotropicFrameNormal=cross(anisotropicFrameTangent,anisotropicFrameDirection); vec3 anisotropicNormal=normalize(mix(N,anisotropicFrameNormal,abs(anisotropy))); return anisotropicNormal; } #endif #if defined(CLEARCOAT) || defined(SS_REFRACTION) vec3 cocaLambert(vec3 alpha,float distance) { return exp(-alpha*distance); } vec3 cocaLambert(float NdotVRefract,float NdotLRefract,vec3 alpha,float thickness) { return cocaLambert(alpha,(thickness*((NdotLRefract+NdotVRefract)/(NdotLRefract*NdotVRefract)))); } vec3 computeColorAtDistanceInMedia(vec3 color,float distance) { return -log(color)/distance; } vec3 computeClearCoatAbsorption(float NdotVRefract,float NdotLRefract,vec3 clearCoatColor,float clearCoatThickness,float clearCoatIntensity) { vec3 clearCoatAbsorption=mix(vec3(1.0), cocaLambert(NdotVRefract,NdotLRefract,clearCoatColor,clearCoatThickness), clearCoatIntensity); return clearCoatAbsorption; } #endif #ifdef MICROSURFACEAUTOMATIC float computeDefaultMicroSurface(float microSurface,vec3 reflectivityColor) { const float kReflectivityNoAlphaWorkflow_SmoothnessMax=0.95; float reflectivityLuminance=getLuminance(reflectivityColor); float reflectivityLuma=sqrt(reflectivityLuminance); microSurface=reflectivityLuma*kReflectivityNoAlphaWorkflow_SmoothnessMax; return microSurface; } #endif";X.a.IncludesShadersStore.pbrHelperFunctions=V;a="#ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS vec3 computeEnvironmentIrradiance(vec3 normal) { return vSphericalL00 +vSphericalL1_1*(normal.y) +vSphericalL10*(normal.z) +vSphericalL11*(normal.x) +vSphericalL2_2*(normal.y*normal.x) +vSphericalL2_1*(normal.y*normal.z) +vSphericalL20*((3.0*normal.z*normal.z)-1.0) +vSphericalL21*(normal.z*normal.x) +vSphericalL22*(normal.x*normal.x-(normal.y*normal.y)); } #else vec3 computeEnvironmentIrradiance(vec3 normal) { float Nx=normal.x; float Ny=normal.y; float Nz=normal.z; vec3 C1=vSphericalZZ.rgb; vec3 Cx=vSphericalX.rgb; vec3 Cy=vSphericalY.rgb; vec3 Cz=vSphericalZ.rgb; vec3 Cxx_zz=vSphericalXX_ZZ.rgb; vec3 Cyy_zz=vSphericalYY_ZZ.rgb; vec3 Cxy=vSphericalXY.rgb; vec3 Cyz=vSphericalYZ.rgb; vec3 Czx=vSphericalZX.rgb; vec3 a1=Cyy_zz*Ny+Cy; vec3 a2=Cyz*Nz+a1; vec3 b1=Czx*Nz+Cx; vec3 b2=Cxy*Ny+b1; vec3 b3=Cxx_zz*Nx+b2; vec3 t1=Cz*Nz+C1; vec3 t2=a2*Ny+t1; vec3 t3=b3*Nx+t2; return t3; } #endif #endif";X.a.IncludesShadersStore.harmonicsFunctions=a;V=" struct preLightingInfo { vec3 lightOffset; float lightDistanceSquared; float lightDistance; float attenuation; vec3 L; vec3 H; float NdotV; float NdotLUnclamped; float NdotL; float VdotH; float roughness; }; preLightingInfo computePointAndSpotPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.lightOffset=lightData.xyz-vPositionW; result.lightDistanceSquared=dot(result.lightOffset,result.lightOffset); result.lightDistance=sqrt(result.lightDistanceSquared); result.L=normalize(result.lightOffset); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); result.NdotLUnclamped=dot(N,result.L); result.NdotL=saturateEps(result.NdotLUnclamped); return result; } preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.lightDistance=length(-lightData.xyz); result.L=normalize(-lightData.xyz); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); result.NdotLUnclamped=dot(N,result.L); result.NdotL=saturateEps(result.NdotLUnclamped); return result; } preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData,vec3 V,vec3 N) { preLightingInfo result; result.NdotL=dot(N,lightData.xyz)*0.5+0.5; result.NdotL=saturateEps(result.NdotL); result.NdotLUnclamped=result.NdotL; #ifdef SPECULARTERM result.L=normalize(lightData.xyz); result.H=normalize(V+result.L); result.VdotH=saturate(dot(V,result.H)); #endif return result; }";X.a.IncludesShadersStore.pbrDirectLightingSetupFunctions=V;a="float computeDistanceLightFalloff_Standard(vec3 lightOffset,float range) { return max(0.,1.0-length(lightOffset)/range); } float computeDistanceLightFalloff_Physical(float lightDistanceSquared) { return 1.0/maxEps(lightDistanceSquared); } float computeDistanceLightFalloff_GLTF(float lightDistanceSquared,float inverseSquaredRange) { float lightDistanceFalloff=1.0/maxEps(lightDistanceSquared); float factor=lightDistanceSquared*inverseSquaredRange; float attenuation=saturate(1.0-factor*factor); attenuation*=attenuation; lightDistanceFalloff*=attenuation; return lightDistanceFalloff; } float computeDistanceLightFalloff(vec3 lightOffset,float lightDistanceSquared,float range,float inverseSquaredRange) { #ifdef USEPHYSICALLIGHTFALLOFF return computeDistanceLightFalloff_Physical(lightDistanceSquared); #elif defined(USEGLTFLIGHTFALLOFF) return computeDistanceLightFalloff_GLTF(lightDistanceSquared,inverseSquaredRange); #else return computeDistanceLightFalloff_Standard(lightOffset,range); #endif } float computeDirectionalLightFalloff_Standard(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle,float exponent) { float falloff=0.0; float cosAngle=maxEps(dot(-lightDirection,directionToLightCenterW)); if (cosAngle>=cosHalfAngle) { falloff=max(0.,pow(cosAngle,exponent)); } return falloff; } float computeDirectionalLightFalloff_Physical(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle) { const float kMinusLog2ConeAngleIntensityRatio=6.64385618977; float concentrationKappa=kMinusLog2ConeAngleIntensityRatio/(1.0-cosHalfAngle); vec4 lightDirectionSpreadSG=vec4(-lightDirection*concentrationKappa,-concentrationKappa); float falloff=exp2(dot(vec4(directionToLightCenterW,1.0),lightDirectionSpreadSG)); return falloff; } float computeDirectionalLightFalloff_GLTF(vec3 lightDirection,vec3 directionToLightCenterW,float lightAngleScale,float lightAngleOffset) { float cd=dot(-lightDirection,directionToLightCenterW); float falloff=saturate(cd*lightAngleScale+lightAngleOffset); falloff*=falloff; return falloff; } float computeDirectionalLightFalloff(vec3 lightDirection,vec3 directionToLightCenterW,float cosHalfAngle,float exponent,float lightAngleScale,float lightAngleOffset) { #ifdef USEPHYSICALLIGHTFALLOFF return computeDirectionalLightFalloff_Physical(lightDirection,directionToLightCenterW,cosHalfAngle); #elif defined(USEGLTFLIGHTFALLOFF) return computeDirectionalLightFalloff_GLTF(lightDirection,directionToLightCenterW,lightAngleScale,lightAngleOffset); #else return computeDirectionalLightFalloff_Standard(lightDirection,directionToLightCenterW,cosHalfAngle,exponent); #endif }";X.a.IncludesShadersStore.pbrDirectLightingFalloffFunctions=a;V=" #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 #ifdef MS_BRDF_ENERGY_CONSERVATION vec3 getEnergyConservationFactor(const vec3 specularEnvironmentR0,const vec3 environmentBrdf) { return 1.0+specularEnvironmentR0*(1.0/environmentBrdf.y-1.0); } #endif #ifdef ENVIRONMENTBRDF vec3 getBRDFLookup(float NdotV,float perceptualRoughness) { vec2 UV=vec2(NdotV,perceptualRoughness); vec4 brdfLookup=texture2D(environmentBrdfSampler,UV); #ifdef ENVIRONMENTBRDF_RGBD brdfLookup.rgb=fromRGBD(brdfLookup.rgba); #endif return brdfLookup.rgb; } vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0,const vec3 specularEnvironmentR90,const vec3 environmentBrdf) { #ifdef BRDF_V_HEIGHT_CORRELATED vec3 reflectance=(specularEnvironmentR90-specularEnvironmentR0)*environmentBrdf.x+specularEnvironmentR0*environmentBrdf.y; #else vec3 reflectance=specularEnvironmentR0*environmentBrdf.x+specularEnvironmentR90*environmentBrdf.y; #endif return reflectance; } vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0,const vec3 environmentBrdf) { #ifdef BRDF_V_HEIGHT_CORRELATED vec3 reflectance=mix(environmentBrdf.xxx,environmentBrdf.yyy,specularEnvironmentR0); #else vec3 reflectance=specularEnvironmentR0*environmentBrdf.x+environmentBrdf.y; #endif return reflectance; } #endif #if !defined(ENVIRONMENTBRDF) || defined(REFLECTIONMAP_SKYBOX) || defined(ALPHAFRESNEL) vec3 getReflectanceFromAnalyticalBRDFLookup_Jones(float VdotN,vec3 reflectance0,vec3 reflectance90,float smoothness) { float weight=mix(FRESNEL_MAXIMUM_ON_ROUGH,1.0,smoothness); return reflectance0+weight*(reflectance90-reflectance0)*pow5(saturate(1.0-VdotN)); } #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) vec3 getSheenReflectanceFromBRDFLookup(const vec3 reflectance0,const vec3 environmentBrdf) { vec3 sheenEnvironmentReflectance=reflectance0*environmentBrdf.b; return sheenEnvironmentReflectance; } #endif vec3 fresnelSchlickGGX(float VdotH,vec3 reflectance0,vec3 reflectance90) { return reflectance0+(reflectance90-reflectance0)*pow5(1.0-VdotH); } float fresnelSchlickGGX(float VdotH,float reflectance0,float reflectance90) { return reflectance0+(reflectance90-reflectance0)*pow5(1.0-VdotH); } #ifdef CLEARCOAT vec3 getR0RemappedForClearCoat(vec3 f0) { #ifdef CLEARCOAT_DEFAULTIOR #ifdef MOBILE return saturate(f0*(f0*0.526868+0.529324)-0.0482256); #else return saturate(f0*(f0*(0.941892-0.263008*f0)+0.346479)-0.0285998); #endif #else vec3 s=sqrt(f0); vec3 t=(vClearCoatRefractionParams.z+vClearCoatRefractionParams.w*s)/(vClearCoatRefractionParams.w+vClearCoatRefractionParams.z*s); return t*t; #endif } #endif float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH,float alphaG) { float a2=square(alphaG); float d=NdotH*NdotH*(a2-1.0)+1.0; return a2/(PI*d*d); } #ifdef SHEEN float normalDistributionFunction_CharlieSheen(float NdotH,float alphaG) { float invR=1./alphaG; float cos2h=NdotH*NdotH; float sin2h=1.-cos2h; return (2.+invR)*pow(sin2h,invR*.5)/(2.*PI); } #endif #ifdef ANISOTROPIC float normalDistributionFunction_BurleyGGX_Anisotropic(float NdotH,float TdotH,float BdotH,const vec2 alphaTB) { float a2=alphaTB.x*alphaTB.y; vec3 v=vec3(alphaTB.y*TdotH,alphaTB.x*BdotH,a2*NdotH); float v2=dot(v,v); float w2=a2/v2; return a2*w2*w2*RECIPROCAL_PI; } #endif #ifdef BRDF_V_HEIGHT_CORRELATED float smithVisibility_GGXCorrelated(float NdotL,float NdotV,float alphaG) { #ifdef MOBILE float GGXV=NdotL*(NdotV*(1.0-alphaG)+alphaG); float GGXL=NdotV*(NdotL*(1.0-alphaG)+alphaG); return 0.5/(GGXV+GGXL); #else float a2=alphaG*alphaG; float GGXV=NdotL*sqrt(NdotV*(NdotV-a2*NdotV)+a2); float GGXL=NdotV*sqrt(NdotL*(NdotL-a2*NdotL)+a2); return 0.5/(GGXV+GGXL); #endif } #else float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot,float alphaG) { #ifdef MOBILE return 1.0/(dot+alphaG+(1.0-alphaG)*dot )); #else float alphaSquared=alphaG*alphaG; return 1.0/(dot+sqrt(alphaSquared+(1.0-alphaSquared)*dot*dot)); #endif } float smithVisibility_TrowbridgeReitzGGXFast(float NdotL,float NdotV,float alphaG) { float visibility=smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL,alphaG)*smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV,alphaG); return visibility; } #endif #ifdef ANISOTROPIC float smithVisibility_GGXCorrelated_Anisotropic(float NdotL,float NdotV,float TdotV,float BdotV,float TdotL,float BdotL,const vec2 alphaTB) { float lambdaV=NdotL*length(vec3(alphaTB.x*TdotV,alphaTB.y*BdotV,NdotV)); float lambdaL=NdotV*length(vec3(alphaTB.x*TdotL,alphaTB.y*BdotL,NdotL)); float v=0.5/(lambdaV+lambdaL); return v; } #endif #ifdef CLEARCOAT float visibility_Kelemen(float VdotH) { return 0.25/(VdotH*VdotH); } #endif #ifdef SHEEN float visibility_Ashikhmin(float NdotL,float NdotV) { return 1./(4.*(NdotL+NdotV-NdotL*NdotV)); } #endif float diffuseBRDF_Burley(float NdotL,float NdotV,float VdotH,float roughness) { float diffuseFresnelNV=pow5(saturateEps(1.0-NdotL)); float diffuseFresnelNL=pow5(saturateEps(1.0-NdotV)); float diffuseFresnel90=0.5+2.0*VdotH*VdotH*roughness; float fresnel = (1.0+(diffuseFresnel90-1.0)*diffuseFresnelNL) * (1.0+(diffuseFresnel90-1.0)*diffuseFresnelNV); return fresnel/PI; } #ifdef SS_TRANSLUCENCY vec3 transmittanceBRDF_Burley(const vec3 tintColor,const vec3 diffusionDistance,float thickness) { vec3 S=1./maxEps(diffusionDistance); vec3 temp=exp((-0.333333333*thickness)*S); return tintColor.rgb*0.25*(temp*temp*temp+3.0*temp); } float computeWrappedDiffuseNdotL(float NdotL,float w) { float t=1.0+w; float invt2=1.0/square(t); return saturate((NdotL+w)*invt2); } #endif ";X.a.IncludesShadersStore.pbrBRDFFunctions=V;a="#ifdef NUM_SAMPLES #if NUM_SAMPLES>0 #if defined(WEBGL2) || defined(WEBGPU) float radicalInverse_VdC(uint bits) { bits=(bits << 16u) | (bits >> 16u); bits=((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits=((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits=((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits=((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits)*2.3283064365386963e-10; } vec2 hammersley(uint i,uint N) { return vec2(float(i)/float(N),radicalInverse_VdC(i)); } #else float vanDerCorpus(int n,int base) { float invBase=1.0/float(base); float denom=1.0; float result=0.0; for(int i=0; i<32; ++i) { if(n>0) { denom=mod(float(n),2.0); result+=denom*invBase; invBase=invBase/2.0; n=int(float(n)/2.0); } } return result; } vec2 hammersley(int i,int N) { return vec2(float(i)/float(N),vanDerCorpus(i,2)); } #endif float log4(float x) { return log2(x)/2.; } const float NUM_SAMPLES_FLOAT=float(NUM_SAMPLES); const float NUM_SAMPLES_FLOAT_INVERSED=1./NUM_SAMPLES_FLOAT; const float K=4.; #define inline vec3 irradiance(samplerCube inputTexture,vec3 inputN,vec2 filteringInfo) { vec3 n=normalize(inputN); vec3 result=vec3(0.0); vec3 tangent=abs(n.z)<0.999 ? vec3(0.,0.,1.) : vec3(1.,0.,0.); tangent=normalize(cross(tangent,n)); vec3 bitangent=cross(n,tangent); mat3 tbn=mat3(tangent,bitangent,n); float maxLevel=filteringInfo.y; float dim0=filteringInfo.x; float omegaP=(4.*PI)/(6.*dim0*dim0); #if defined(WEBGL2) || defined(WEBGPU) for(uint i=0u; i0.) { float pdf_inversed=PI/NoL; float omegaS=NUM_SAMPLES_FLOAT_INVERSED*pdf_inversed; float l=log4(omegaS)-log4(omegaP)+log4(K); float mipLevel=clamp(l,0.0,maxLevel); vec3 c=textureCubeLodEXT(inputTexture,tbn*Ls,mipLevel).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif result+=c; } } result=result*NUM_SAMPLES_FLOAT_INVERSED; return result; } #define inline vec3 radiance(float alphaG,samplerCube inputTexture,vec3 inputN,vec2 filteringInfo) { vec3 n=normalize(inputN); if (alphaG == 0.) { vec3 c=textureCube(inputTexture,n).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif return c; } else { vec3 result=vec3(0.); vec3 tangent=abs(n.z)<0.999 ? vec3(0.,0.,1.) : vec3(1.,0.,0.); tangent=normalize(cross(tangent,n)); vec3 bitangent=cross(n,tangent); mat3 tbn=mat3(tangent,bitangent,n); float maxLevel=filteringInfo.y; float dim0=filteringInfo.x; float omegaP=(4.*PI)/(6.*dim0*dim0); float weight=0.; #if defined(WEBGL2) || defined(WEBGPU) for(uint i=0u; i0.) { float pdf_inversed=4./normalDistributionFunction_TrowbridgeReitzGGX(NoH,alphaG); float omegaS=NUM_SAMPLES_FLOAT_INVERSED*pdf_inversed; float l=log4(omegaS)-log4(omegaP)+log4(K); float mipLevel=clamp(float(l),0.0,maxLevel); weight+=NoL; vec3 c=textureCubeLodEXT(inputTexture,tbn*L,mipLevel).rgb; #ifdef GAMMA_INPUT c=toLinearSpace(c); #endif result+=c*NoL; } } result=result/weight; return result; } } #endif #endif";X.a.IncludesShadersStore.hdrFilteringFunctions=a;V="#define CLEARCOATREFLECTANCE90 1.0 struct lightingInfo { vec3 diffuse; #ifdef SPECULARTERM vec3 specular; #endif #ifdef CLEARCOAT vec4 clearCoat; #endif #ifdef SHEEN vec3 sheen; #endif }; float adjustRoughnessFromLightProperties(float roughness,float lightRadius,float lightDistance) { #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF) float lightRoughness=lightRadius/lightDistance; float totalRoughness=saturate(lightRoughness+roughness); return totalRoughness; #else return roughness; #endif } vec3 computeHemisphericDiffuseLighting(preLightingInfo info,vec3 lightColor,vec3 groundColor) { return mix(groundColor,lightColor,info.NdotL); } vec3 computeDiffuseLighting(preLightingInfo info,vec3 lightColor) { float diffuseTerm=diffuseBRDF_Burley(info.NdotL,info.NdotV,info.VdotH,info.roughness); return diffuseTerm*info.attenuation*info.NdotL*lightColor; } #define inline vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler,mat4 textureProjectionMatrix){ vec4 strq=textureProjectionMatrix*vec4(vPositionW,1.0); strq/=strq.w; vec3 textureColor=texture2D(projectionLightSampler,strq.xy).rgb; return toLinearSpace(textureColor); } #ifdef SS_TRANSLUCENCY vec3 computeDiffuseAndTransmittedLighting(preLightingInfo info,vec3 lightColor,vec3 transmittance) { float NdotL=absEps(info.NdotLUnclamped); float wrapNdotL=computeWrappedDiffuseNdotL(NdotL,0.02); float trAdapt=step(0.,info.NdotLUnclamped); vec3 transmittanceNdotL=mix(transmittance*wrapNdotL,vec3(wrapNdotL),trAdapt); float diffuseTerm=diffuseBRDF_Burley(NdotL,info.NdotV,info.VdotH,info.roughness); return diffuseTerm*transmittanceNdotL*info.attenuation*lightColor; } #endif #ifdef SPECULARTERM vec3 computeSpecularLighting(preLightingInfo info,vec3 N,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float roughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(roughness); vec3 fresnel=fresnelSchlickGGX(info.VdotH,reflectance0,reflectance90); float distribution=normalDistributionFunction_TrowbridgeReitzGGX(NdotH,alphaG); #ifdef BRDF_V_HEIGHT_CORRELATED float smithVisibility=smithVisibility_GGXCorrelated(info.NdotL,info.NdotV,alphaG); #else float smithVisibility=smithVisibility_TrowbridgeReitzGGXFast(info.NdotL,info.NdotV,alphaG); #endif vec3 specTerm=fresnel*distribution*smithVisibility; return specTerm*info.attenuation*info.NdotL*lightColor; } #endif #ifdef ANISOTROPIC vec3 computeAnisotropicSpecularLighting(preLightingInfo info,vec3 V,vec3 N,vec3 T,vec3 B,float anisotropy,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float TdotH=dot(T,info.H); float BdotH=dot(B,info.H); float TdotV=dot(T,V); float BdotV=dot(B,V); float TdotL=dot(T,info.L); float BdotL=dot(B,info.L); float alphaG=convertRoughnessToAverageSlope(info.roughness); vec2 alphaTB=getAnisotropicRoughness(alphaG,anisotropy); alphaTB=max(alphaTB,square(geometricRoughnessFactor)); vec3 fresnel=fresnelSchlickGGX(info.VdotH,reflectance0,reflectance90); float distribution=normalDistributionFunction_BurleyGGX_Anisotropic(NdotH,TdotH,BdotH,alphaTB); float smithVisibility=smithVisibility_GGXCorrelated_Anisotropic(info.NdotL,info.NdotV,TdotV,BdotV,TdotL,BdotL,alphaTB); vec3 specTerm=fresnel*distribution*smithVisibility; return specTerm*info.attenuation*info.NdotL*lightColor; } #endif #ifdef CLEARCOAT vec4 computeClearCoatLighting(preLightingInfo info,vec3 Ncc,float geometricRoughnessFactor,float clearCoatIntensity,vec3 lightColor) { float NccdotL=saturateEps(dot(Ncc,info.L)); float NccdotH=saturateEps(dot(Ncc,info.H)); float clearCoatRoughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(clearCoatRoughness); float fresnel=fresnelSchlickGGX(info.VdotH,vClearCoatRefractionParams.x,CLEARCOATREFLECTANCE90); fresnel*=clearCoatIntensity; float distribution=normalDistributionFunction_TrowbridgeReitzGGX(NccdotH,alphaG); float kelemenVisibility=visibility_Kelemen(info.VdotH); float clearCoatTerm=fresnel*distribution*kelemenVisibility; return vec4( clearCoatTerm*info.attenuation*NccdotL*lightColor, 1.0-fresnel ); } vec3 computeClearCoatLightingAbsorption(float NdotVRefract,vec3 L,vec3 Ncc,vec3 clearCoatColor,float clearCoatThickness,float clearCoatIntensity) { vec3 LRefract=-refract(L,Ncc,vClearCoatRefractionParams.y); float NdotLRefract=saturateEps(dot(Ncc,LRefract)); vec3 absorption=computeClearCoatAbsorption(NdotVRefract,NdotLRefract,clearCoatColor,clearCoatThickness,clearCoatIntensity); return absorption; } #endif #ifdef SHEEN vec3 computeSheenLighting(preLightingInfo info,vec3 N,vec3 reflectance0,vec3 reflectance90,float geometricRoughnessFactor,vec3 lightColor) { float NdotH=saturateEps(dot(N,info.H)); float roughness=max(info.roughness,geometricRoughnessFactor); float alphaG=convertRoughnessToAverageSlope(roughness); float fresnel=1.; float distribution=normalDistributionFunction_CharlieSheen(NdotH,alphaG); float visibility=visibility_Ashikhmin(info.NdotL,info.NdotV); float sheenTerm=fresnel*distribution*visibility; return sheenTerm*info.attenuation*info.NdotL*lightColor; } #endif ";X.a.IncludesShadersStore.pbrDirectLightingFunctions=V;a="#if defined(REFLECTION) || defined(SS_REFRACTION) float getLodFromAlphaG(float cubeMapDimensionPixels,float microsurfaceAverageSlope) { float microsurfaceAverageSlopeTexels=cubeMapDimensionPixels*microsurfaceAverageSlope; float lod=log2(microsurfaceAverageSlopeTexels); return lod; } float getLinearLodFromRoughness(float cubeMapDimensionPixels,float roughness) { float lod=log2(cubeMapDimensionPixels)*roughness; return lod; } #endif #if defined(ENVIRONMENTBRDF) && defined(RADIANCEOCCLUSION) float environmentRadianceOcclusion(float ambientOcclusion,float NdotVUnclamped) { float temp=NdotVUnclamped+ambientOcclusion; return saturate(square(temp)-1.0+ambientOcclusion); } #endif #if defined(ENVIRONMENTBRDF) && defined(HORIZONOCCLUSION) float environmentHorizonOcclusion(vec3 view,vec3 normal,vec3 geometricNormal) { vec3 reflection=reflect(view,normal); float temp=saturate(1.0+1.1*dot(reflection,geometricNormal)); return square(temp); } #endif #if defined(LODINREFLECTIONALPHA) || defined(SS_LODINREFRACTIONALPHA) #define UNPACK_LOD(x) (1.0-x)*255.0 float getLodFromAlphaG(float cubeMapDimensionPixels,float alphaG,float NdotV) { float microsurfaceAverageSlope=alphaG; microsurfaceAverageSlope*=sqrt(abs(NdotV)); return getLodFromAlphaG(cubeMapDimensionPixels,microsurfaceAverageSlope); } #endif";X.a.IncludesShadersStore.pbrIBLFunctions=a;c(154),c(155);V="struct albedoOpacityOutParams { vec3 surfaceAlbedo; float alpha; }; #define pbr_inline void albedoOpacityBlock( in vec4 vAlbedoColor, #ifdef ALBEDO in vec4 albedoTexture, in vec2 albedoInfos, #endif #ifdef OPACITY in vec4 opacityMap, in vec2 vOpacityInfos, #endif #ifdef DETAIL in vec4 detailColor, in vec4 vDetailInfos, #endif out albedoOpacityOutParams outParams ) { vec3 surfaceAlbedo=vAlbedoColor.rgb; float alpha=vAlbedoColor.a; #ifdef ALBEDO #if defined(ALPHAFROMALBEDO) || defined(ALPHATEST) alpha*=albedoTexture.a; #endif #ifdef GAMMAALBEDO surfaceAlbedo*=toLinearSpace(albedoTexture.rgb); #else surfaceAlbedo*=albedoTexture.rgb; #endif surfaceAlbedo*=albedoInfos.y; #endif #ifdef VERTEXCOLOR surfaceAlbedo*=vColor.rgb; #endif #ifdef DETAIL float detailAlbedo=2.0*mix(0.5,detailColor.r,vDetailInfos.y); surfaceAlbedo.rgb=surfaceAlbedo.rgb*detailAlbedo*detailAlbedo; #endif #define CUSTOM_FRAGMENT_UPDATE_ALBEDO #ifdef OPACITY #ifdef OPACITYRGB alpha=getLuminance(opacityMap.rgb); #else alpha*=opacityMap.a; #endif alpha*=vOpacityInfos.y; #endif #ifdef VERTEXALPHA alpha*=vColor.a; #endif #if !defined(SS_LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL) #ifdef ALPHATEST if (alpha0 vec4 surfaceMetallicColorMap; vec4 surfaceReflectivityColorMap; vec2 metallicRoughness; vec3 metallicF0; #endif }; #define pbr_inline void reflectivityBlock( in vec4 vReflectivityColor, #ifdef METALLICWORKFLOW in vec3 surfaceAlbedo, in vec4 metallicReflectanceFactors, #endif #ifdef REFLECTIVITY in vec3 reflectivityInfos, in vec4 surfaceMetallicOrReflectivityColorMap, #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) in vec3 ambientOcclusionColorIn, #endif #ifdef MICROSURFACEMAP in vec4 microSurfaceTexel, #endif #ifdef DETAIL in vec4 detailColor, in vec4 vDetailInfos, #endif out reflectivityOutParams outParams ) { float microSurface=vReflectivityColor.a; vec3 surfaceReflectivityColor=vReflectivityColor.rgb; #ifdef METALLICWORKFLOW vec2 metallicRoughness=surfaceReflectivityColor.rg; #ifdef REFLECTIVITY #if DEBUGMODE>0 outParams.surfaceMetallicColorMap=surfaceMetallicOrReflectivityColorMap; #endif #ifdef AOSTOREINMETALMAPRED vec3 aoStoreInMetalMap=vec3(surfaceMetallicOrReflectivityColorMap.r,surfaceMetallicOrReflectivityColorMap.r,surfaceMetallicOrReflectivityColorMap.r); outParams.ambientOcclusionColor=mix(ambientOcclusionColorIn,aoStoreInMetalMap,reflectivityInfos.z); #endif #ifdef METALLNESSSTOREINMETALMAPBLUE metallicRoughness.r*=surfaceMetallicOrReflectivityColorMap.b; #else metallicRoughness.r*=surfaceMetallicOrReflectivityColorMap.r; #endif #ifdef ROUGHNESSSTOREINMETALMAPALPHA metallicRoughness.g*=surfaceMetallicOrReflectivityColorMap.a; #else #ifdef ROUGHNESSSTOREINMETALMAPGREEN metallicRoughness.g*=surfaceMetallicOrReflectivityColorMap.g; #endif #endif #endif #ifdef DETAIL float detailRoughness=mix(0.5,detailColor.b,vDetailInfos.w); float loLerp=mix(0.,metallicRoughness.g,detailRoughness*2.); float hiLerp=mix(metallicRoughness.g,1.,(detailRoughness-0.5)*2.); metallicRoughness.g=mix(loLerp,hiLerp,step(detailRoughness,0.5)); #endif #ifdef MICROSURFACEMAP metallicRoughness.g*=microSurfaceTexel.r; #endif #if DEBUGMODE>0 outParams.metallicRoughness=metallicRoughness; #endif #define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS microSurface=1.0-metallicRoughness.g; vec3 baseColor=surfaceAlbedo; #ifdef FROSTBITE_REFLECTANCE outParams.surfaceAlbedo=baseColor.rgb*(1.0-metallicRoughness.r); surfaceReflectivityColor=mix(0.16*reflectance*reflectance,baseColor,metallicRoughness.r); #else vec3 metallicF0=metallicReflectanceFactors.rgb; #if DEBUGMODE>0 outParams.metallicF0=metallicF0; #endif outParams.surfaceAlbedo=mix(baseColor.rgb*(1.0-metallicF0),vec3(0.,0.,0.),metallicRoughness.r); surfaceReflectivityColor=mix(metallicF0,baseColor,metallicRoughness.r); #endif #else #ifdef REFLECTIVITY surfaceReflectivityColor*=surfaceMetallicOrReflectivityColorMap.rgb; #if DEBUGMODE>0 outParams.surfaceReflectivityColorMap=surfaceMetallicOrReflectivityColorMap; #endif #ifdef MICROSURFACEFROMREFLECTIVITYMAP microSurface*=surfaceMetallicOrReflectivityColorMap.a; microSurface*=reflectivityInfos.z; #else #ifdef MICROSURFACEAUTOMATIC microSurface*=computeDefaultMicroSurface(microSurface,surfaceReflectivityColor); #endif #ifdef MICROSURFACEMAP microSurface*=microSurfaceTexel.r; #endif #define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE #endif #endif #endif microSurface=saturate(microSurface); float roughness=1.-microSurface; outParams.microSurface=microSurface; outParams.roughness=roughness; outParams.surfaceReflectivityColor=surfaceReflectivityColor; } ";X.a.IncludesShadersStore.pbrBlockReflectivity=a;V="struct ambientOcclusionOutParams { vec3 ambientOcclusionColor; #if DEBUGMODE>0 vec3 ambientOcclusionColorMap; #endif }; #define pbr_inline void ambientOcclusionBlock( #ifdef AMBIENT in vec3 ambientOcclusionColorMap_, in vec4 vAmbientInfos, #endif out ambientOcclusionOutParams outParams ) { vec3 ambientOcclusionColor=vec3(1.,1.,1.); #ifdef AMBIENT vec3 ambientOcclusionColorMap=ambientOcclusionColorMap_*vAmbientInfos.y; #ifdef AMBIENTINGRAYSCALE ambientOcclusionColorMap=vec3(ambientOcclusionColorMap.r,ambientOcclusionColorMap.r,ambientOcclusionColorMap.r); #endif ambientOcclusionColor=mix(ambientOcclusionColor,ambientOcclusionColorMap,vAmbientInfos.z); #if DEBUGMODE>0 outParams.ambientOcclusionColorMap=ambientOcclusionColorMap; #endif #endif outParams.ambientOcclusionColor=ambientOcclusionColor; } ";X.a.IncludesShadersStore.pbrBlockAmbientOcclusion=V;a="#ifdef ALPHAFRESNEL #if defined(ALPHATEST) || defined(ALPHABLEND) struct alphaFresnelOutParams { float alpha; }; #define pbr_inline void alphaFresnelBlock( in vec3 normalW, in vec3 viewDirectionW, in float alpha, in float microSurface, out alphaFresnelOutParams outParams ) { float opacityPerceptual=alpha; #ifdef LINEARALPHAFRESNEL float opacity0=opacityPerceptual; #else float opacity0=opacityPerceptual*opacityPerceptual; #endif float opacity90=fresnelGrazingReflectance(opacity0); vec3 normalForward=faceforward(normalW,-viewDirectionW,normalW); outParams.alpha=getReflectanceFromAnalyticalBRDFLookup_Jones(saturate(dot(viewDirectionW,normalForward)),vec3(opacity0),vec3(opacity90),sqrt(microSurface)).x; #ifdef ALPHATEST if (outParams.alpha0 vec3 anisotropyMapData; #endif }; #define pbr_inline void anisotropicBlock( in vec3 vAnisotropy, #ifdef ANISOTROPIC_TEXTURE in vec3 anisotropyMapData, #endif in mat3 TBN, in vec3 normalW, in vec3 viewDirectionW, out anisotropicOutParams outParams ) { float anisotropy=vAnisotropy.b; vec3 anisotropyDirection=vec3(vAnisotropy.xy,0.); #ifdef ANISOTROPIC_TEXTURE anisotropy*=anisotropyMapData.b; anisotropyDirection.rg*=anisotropyMapData.rg*2.0-1.0; #if DEBUGMODE>0 outParams.anisotropyMapData=anisotropyMapData; #endif #endif mat3 anisoTBN=mat3(normalize(TBN[0]),normalize(TBN[1]),normalize(TBN[2])); vec3 anisotropicTangent=normalize(anisoTBN*anisotropyDirection); vec3 anisotropicBitangent=normalize(cross(anisoTBN[2],anisotropicTangent)); outParams.anisotropy=anisotropy; outParams.anisotropicTangent=anisotropicTangent; outParams.anisotropicBitangent=anisotropicBitangent; outParams.anisotropicNormal=getAnisotropicBentNormals(anisotropicTangent,anisotropicBitangent,normalW,viewDirectionW,anisotropy); } #endif ";X.a.IncludesShadersStore.pbrBlockAnisotropic=V;a="#ifdef REFLECTION struct reflectionOutParams { vec4 environmentRadiance; vec3 environmentIrradiance; #ifdef REFLECTIONMAP_3D vec3 reflectionCoords; #else vec2 reflectionCoords; #endif #ifdef SS_TRANSLUCENCY #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) vec3 irradianceVector; #endif #endif #endif }; #define pbr_inline void createReflectionCoords( in vec3 vPositionW, in vec3 normalW, #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #ifdef REFLECTIONMAP_3D out vec3 reflectionCoords #else out vec2 reflectionCoords #endif ) { #ifdef ANISOTROPIC vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),anisotropicOut.anisotropicNormal); #else vec3 reflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),normalW); #endif #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D reflectionCoords=reflectionVector; #else reflectionCoords=reflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION reflectionCoords/=reflectionVector.z; #endif reflectionCoords.y=1.0-reflectionCoords.y; #endif } #define pbr_inline #define inline void sampleReflectionTexture( in float alphaG, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) in float NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION in float roughness, #endif #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, const vec3 reflectionCoords, #else in sampler2D reflectionSampler, const vec2 reflectionCoords, #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif out vec4 environmentRadiance ) { #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) float reflectionLOD=getLodFromAlphaG(vReflectionMicrosurfaceInfos.x,alphaG,NdotVUnclamped); #elif defined(LINEARSPECULARREFLECTION) float reflectionLOD=getLinearLodFromRoughness(vReflectionMicrosurfaceInfos.x,roughness); #else float reflectionLOD=getLodFromAlphaG(vReflectionMicrosurfaceInfos.x,alphaG); #endif #ifdef LODBASEDMICROSFURACE reflectionLOD=reflectionLOD*vReflectionMicrosurfaceInfos.y+vReflectionMicrosurfaceInfos.z; #ifdef LODINREFLECTIONALPHA float automaticReflectionLOD=UNPACK_LOD(sampleReflection(reflectionSampler,reflectionCoords).a); float requestedReflectionLOD=max(automaticReflectionLOD,reflectionLOD); #else float requestedReflectionLOD=reflectionLOD; #endif #ifdef REALTIME_FILTERING environmentRadiance=vec4(radiance(alphaG,reflectionSampler,reflectionCoords,vReflectionFilteringInfo),1.0); #else environmentRadiance=sampleReflectionLod(reflectionSampler,reflectionCoords,reflectionLOD); #endif #else float lodReflectionNormalized=saturate(reflectionLOD/log2(vReflectionMicrosurfaceInfos.x)); float lodReflectionNormalizedDoubled=lodReflectionNormalized*2.0; vec4 environmentMid=sampleReflection(reflectionSampler,reflectionCoords); if (lodReflectionNormalizedDoubled<1.0){ environmentRadiance=mix( sampleReflection(reflectionSamplerHigh,reflectionCoords), environmentMid, lodReflectionNormalizedDoubled ); } else { environmentRadiance=mix( environmentMid, sampleReflection(reflectionSamplerLow,reflectionCoords), lodReflectionNormalizedDoubled-1.0 ); } #endif #ifdef RGBDREFLECTION environmentRadiance.rgb=fromRGBD(environmentRadiance); #endif #ifdef GAMMAREFLECTION environmentRadiance.rgb=toLinearSpace(environmentRadiance.rgb); #endif environmentRadiance.rgb*=vReflectionInfos.x; environmentRadiance.rgb*=vReflectionColor.rgb; } #define pbr_inline #define inline void reflectionBlock( in vec3 vPositionW, in vec3 normalW, in float alphaG, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) in float NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION in float roughness, #endif #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, #else in sampler2D reflectionSampler, #endif #if defined(NORMAL) && defined(USESPHERICALINVERTEX) in vec3 vEnvironmentIrradiance, #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) in mat4 reflectionMatrix, #endif #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D in samplerCube irradianceSampler, #else in sampler2D irradianceSampler, #endif #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif out reflectionOutParams outParams ) { vec4 environmentRadiance=vec4(0.,0.,0.,0.); #ifdef REFLECTIONMAP_3D vec3 reflectionCoords=vec3(0.); #else vec2 reflectionCoords=vec2(0.); #endif createReflectionCoords( vPositionW, normalW, #ifdef ANISOTROPIC anisotropicOut, #endif reflectionCoords ); sampleReflectionTexture( alphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION roughness, #endif #ifdef REFLECTIONMAP_3D reflectionSampler, reflectionCoords, #else reflectionSampler, reflectionCoords, #endif #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentRadiance ); vec3 environmentIrradiance=vec3(0.,0.,0.); #ifdef USESPHERICALFROMREFLECTIONMAP #if defined(NORMAL) && defined(USESPHERICALINVERTEX) environmentIrradiance=vEnvironmentIrradiance; #else #ifdef ANISOTROPIC vec3 irradianceVector=vec3(reflectionMatrix*vec4(anisotropicOut.anisotropicNormal,0)).xyz; #else vec3 irradianceVector=vec3(reflectionMatrix*vec4(normalW,0)).xyz; #endif #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z*=-1.0; #endif #ifdef INVERTCUBICMAP irradianceVector.y*=-1.0; #endif #if defined(REALTIME_FILTERING) environmentIrradiance=irradiance(reflectionSampler,irradianceVector,vReflectionFilteringInfo); #else environmentIrradiance=computeEnvironmentIrradiance(irradianceVector); #endif #ifdef SS_TRANSLUCENCY outParams.irradianceVector=irradianceVector; #endif #endif #elif defined(USEIRRADIANCEMAP) vec4 environmentIrradiance4=sampleReflection(irradianceSampler,reflectionCoords); environmentIrradiance=environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance.rgb=fromRGBD(environmentIrradiance4); #endif #ifdef GAMMAREFLECTION environmentIrradiance.rgb=toLinearSpace(environmentIrradiance.rgb); #endif #endif environmentIrradiance*=vReflectionColor.rgb; outParams.environmentRadiance=environmentRadiance; outParams.environmentIrradiance=environmentIrradiance; outParams.reflectionCoords=reflectionCoords; } #endif ";X.a.IncludesShadersStore.pbrBlockReflection=a;V="#ifdef SHEEN struct sheenOutParams { float sheenIntensity; vec3 sheenColor; float sheenRoughness; #ifdef SHEEN_LINKWITHALBEDO vec3 surfaceAlbedo; #endif #if defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) float sheenAlbedoScaling; #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) vec3 finalSheenRadianceScaled; #endif #if DEBUGMODE>0 vec4 sheenMapData; vec3 sheenEnvironmentReflectance; #endif }; #define pbr_inline #define inline void sheenBlock( in vec4 vSheenColor, #ifdef SHEEN_ROUGHNESS in float vSheenRoughness, #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) in vec4 sheenMapRoughnessData, #endif #endif in float roughness, #ifdef SHEEN_TEXTURE in vec4 sheenMapData, in float sheenMapLevel, #endif in float reflectance, #ifdef SHEEN_LINKWITHALBEDO in vec3 baseColor, in vec3 surfaceAlbedo, #endif #ifdef ENVIRONMENTBRDF in float NdotV, in vec3 environmentBrdf, #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) in vec2 AARoughnessFactors, in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, in vec4 vLightingIntensity, #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, in vec3 reflectionCoords, #else in sampler2D reflectionSampler, in vec2 reflectionCoords, #endif in float NdotVUnclamped, #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) in float seo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) in float eho, #endif #endif out sheenOutParams outParams ) { float sheenIntensity=vSheenColor.a; #ifdef SHEEN_TEXTURE #if DEBUGMODE>0 outParams.sheenMapData=sheenMapData; #endif #endif #ifdef SHEEN_LINKWITHALBEDO float sheenFactor=pow5(1.0-sheenIntensity); vec3 sheenColor=baseColor.rgb*(1.0-sheenFactor); float sheenRoughness=sheenIntensity; outParams.surfaceAlbedo=surfaceAlbedo*sheenFactor; #ifdef SHEEN_TEXTURE sheenIntensity*=sheenMapData.a; #endif #else vec3 sheenColor=vSheenColor.rgb; #ifdef SHEEN_TEXTURE #ifdef SHEEN_GAMMATEXTURE sheenColor.rgb*=toLinearSpace(sheenMapData.rgb); #else sheenColor.rgb*=sheenMapData.rgb; #endif sheenColor.rgb*=sheenMapLevel; #endif #ifdef SHEEN_ROUGHNESS float sheenRoughness=vSheenRoughness; #ifdef SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE #if defined(SHEEN_TEXTURE) sheenRoughness*=sheenMapData.a; #endif #elif defined(SHEEN_TEXTURE_ROUGHNESS) #ifdef SHEEN_TEXTURE_ROUGHNESS_IDENTICAL sheenRoughness*=sheenMapData.a; #else sheenRoughness*=sheenMapRoughnessData.a; #endif #endif #else float sheenRoughness=roughness; #ifdef SHEEN_TEXTURE sheenIntensity*=sheenMapData.a; #endif #endif #if !defined(SHEEN_ALBEDOSCALING) sheenIntensity*=(1.-reflectance); #endif sheenColor*=sheenIntensity; #endif #ifdef ENVIRONMENTBRDF #ifdef SHEEN_ROUGHNESS vec3 environmentSheenBrdf=getBRDFLookup(NdotV,sheenRoughness); #else vec3 environmentSheenBrdf=environmentBrdf; #endif #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) float sheenAlphaG=convertRoughnessToAverageSlope(sheenRoughness); #ifdef SPECULARAA sheenAlphaG+=AARoughnessFactors.y; #endif vec4 environmentSheenRadiance=vec4(0.,0.,0.,0.); sampleReflectionTexture( sheenAlphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION sheenRoughness, #endif reflectionSampler, reflectionCoords, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentSheenRadiance ); vec3 sheenEnvironmentReflectance=getSheenReflectanceFromBRDFLookup(sheenColor,environmentSheenBrdf); #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) sheenEnvironmentReflectance*=seo; #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) sheenEnvironmentReflectance*=eho; #endif #if DEBUGMODE>0 outParams.sheenEnvironmentReflectance=sheenEnvironmentReflectance; #endif outParams.finalSheenRadianceScaled= environmentSheenRadiance.rgb * sheenEnvironmentReflectance * vLightingIntensity.z; #endif #if defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) outParams.sheenAlbedoScaling=1.0-sheenIntensity*max(max(sheenColor.r,sheenColor.g),sheenColor.b)*environmentSheenBrdf.b; #endif outParams.sheenIntensity=sheenIntensity; outParams.sheenColor=sheenColor; outParams.sheenRoughness=sheenRoughness; } #endif ";X.a.IncludesShadersStore.pbrBlockSheen=V;a="struct clearcoatOutParams { vec3 specularEnvironmentR0; float conservationFactor; vec3 clearCoatNormalW; vec2 clearCoatAARoughnessFactors; float clearCoatIntensity; float clearCoatRoughness; #ifdef REFLECTION vec3 finalClearCoatRadianceScaled; #endif #ifdef CLEARCOAT_TINT vec3 absorption; float clearCoatNdotVRefract; vec3 clearCoatColor; float clearCoatThickness; #endif #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) vec3 energyConservationFactorClearCoat; #endif #if DEBUGMODE>0 mat3 TBNClearCoat; vec2 clearCoatMapData; vec4 clearCoatTintMapData; vec4 environmentClearCoatRadiance; float clearCoatNdotV; vec3 clearCoatEnvironmentReflectance; #endif }; #ifdef CLEARCOAT #define pbr_inline #define inline void clearcoatBlock( in vec3 vPositionW, in vec3 geometricNormalW, in vec3 viewDirectionW, in vec2 vClearCoatParams, #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) in vec4 clearCoatMapRoughnessData, #endif in vec3 specularEnvironmentR0, #ifdef CLEARCOAT_TEXTURE in vec2 clearCoatMapData, #endif #ifdef CLEARCOAT_TINT in vec4 vClearCoatTintParams, in float clearCoatColorAtDistance, in vec4 vClearCoatRefractionParams, #ifdef CLEARCOAT_TINT_TEXTURE in vec4 clearCoatTintMapData, #endif #endif #ifdef CLEARCOAT_BUMP in vec2 vClearCoatBumpInfos, in vec4 clearCoatBumpMapData, in vec2 vClearCoatBumpUV, #if defined(TANGENT) && defined(NORMAL) in mat3 vTBN, #else in vec2 vClearCoatTangentSpaceParams, #endif #ifdef OBJECTSPACE_NORMALMAP in mat4 normalMatrix, #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) in vec3 faceNormal, #endif #ifdef REFLECTION in vec3 vReflectionMicrosurfaceInfos, in vec2 vReflectionInfos, in vec3 vReflectionColor, in vec4 vLightingIntensity, #ifdef REFLECTIONMAP_3D in samplerCube reflectionSampler, #else in sampler2D reflectionSampler, #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D in samplerCube reflectionSamplerLow, in samplerCube reflectionSamplerHigh, #else in sampler2D reflectionSamplerLow, in sampler2D reflectionSamplerHigh, #endif #endif #ifdef REALTIME_FILTERING in vec2 vReflectionFilteringInfo, #endif #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION in float ambientMonochrome, #endif #endif #if defined(CLEARCOAT_BUMP) || defined(TWOSIDEDLIGHTING) in float frontFacingMultiplier, #endif out clearcoatOutParams outParams ) { float clearCoatIntensity=vClearCoatParams.x; float clearCoatRoughness=vClearCoatParams.y; #ifdef CLEARCOAT_TEXTURE clearCoatIntensity*=clearCoatMapData.x; #ifdef CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE clearCoatRoughness*=clearCoatMapData.y; #endif #if DEBUGMODE>0 outParams.clearCoatMapData=clearCoatMapData; #endif #endif #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) #ifdef CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL clearCoatRoughness*=clearCoatMapData.y; #else clearCoatRoughness*=clearCoatMapRoughnessData.y; #endif #endif outParams.clearCoatIntensity=clearCoatIntensity; outParams.clearCoatRoughness=clearCoatRoughness; #ifdef CLEARCOAT_TINT vec3 clearCoatColor=vClearCoatTintParams.rgb; float clearCoatThickness=vClearCoatTintParams.a; #ifdef CLEARCOAT_TINT_TEXTURE #ifdef CLEARCOAT_TINT_GAMMATEXTURE clearCoatColor*=toLinearSpace(clearCoatTintMapData.rgb); #else clearCoatColor*=clearCoatTintMapData.rgb; #endif clearCoatThickness*=clearCoatTintMapData.a; #if DEBUGMODE>0 outParams.clearCoatTintMapData=clearCoatTintMapData; #endif #endif outParams.clearCoatColor=computeColorAtDistanceInMedia(clearCoatColor,clearCoatColorAtDistance); outParams.clearCoatThickness=clearCoatThickness; #endif #ifdef CLEARCOAT_REMAP_F0 vec3 specularEnvironmentR0Updated=getR0RemappedForClearCoat(specularEnvironmentR0); #else vec3 specularEnvironmentR0Updated=specularEnvironmentR0; #endif outParams.specularEnvironmentR0=mix(specularEnvironmentR0,specularEnvironmentR0Updated,clearCoatIntensity); vec3 clearCoatNormalW=geometricNormalW; #ifdef CLEARCOAT_BUMP #ifdef NORMALXYSCALE float clearCoatNormalScale=1.0; #else float clearCoatNormalScale=vClearCoatBumpInfos.y; #endif #if defined(TANGENT) && defined(NORMAL) mat3 TBNClearCoat=vTBN; #else vec2 TBNClearCoatUV=vClearCoatBumpUV*frontFacingMultiplier; mat3 TBNClearCoat=cotangent_frame(clearCoatNormalW*clearCoatNormalScale,vPositionW,TBNClearCoatUV,vClearCoatTangentSpaceParams); #endif #if DEBUGMODE>0 outParams.TBNClearCoat=TBNClearCoat; #endif #ifdef OBJECTSPACE_NORMALMAP clearCoatNormalW=normalize(clearCoatBumpMapData.xyz*2.0-1.0); clearCoatNormalW=normalize(mat3(normalMatrix)*clearCoatNormalW); #else clearCoatNormalW=perturbNormal(TBNClearCoat,clearCoatBumpMapData.xyz,vClearCoatBumpInfos.y); #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) clearCoatNormalW*=sign(dot(clearCoatNormalW,faceNormal)); #endif #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) clearCoatNormalW=clearCoatNormalW*frontFacingMultiplier; #endif outParams.clearCoatNormalW=clearCoatNormalW; outParams.clearCoatAARoughnessFactors=getAARoughnessFactors(clearCoatNormalW.xyz); float clearCoatNdotVUnclamped=dot(clearCoatNormalW,viewDirectionW); float clearCoatNdotV=absEps(clearCoatNdotVUnclamped); #if DEBUGMODE>0 outParams.clearCoatNdotV=clearCoatNdotV; #endif #ifdef CLEARCOAT_TINT vec3 clearCoatVRefract=-refract(vPositionW,clearCoatNormalW,vClearCoatRefractionParams.y); outParams.clearCoatNdotVRefract=absEps(dot(clearCoatNormalW,clearCoatVRefract)); #endif #if defined(ENVIRONMENTBRDF) && (!defined(REFLECTIONMAP_SKYBOX) || defined(MS_BRDF_ENERGY_CONSERVATION)) vec3 environmentClearCoatBrdf=getBRDFLookup(clearCoatNdotV,clearCoatRoughness); #endif #if defined(REFLECTION) float clearCoatAlphaG=convertRoughnessToAverageSlope(clearCoatRoughness); #ifdef SPECULARAA clearCoatAlphaG+=outParams.clearCoatAARoughnessFactors.y; #endif vec4 environmentClearCoatRadiance=vec4(0.,0.,0.,0.); vec3 clearCoatReflectionVector=computeReflectionCoords(vec4(vPositionW,1.0),clearCoatNormalW); #ifdef REFLECTIONMAP_OPPOSITEZ clearCoatReflectionVector.z*=-1.0; #endif #ifdef REFLECTIONMAP_3D vec3 clearCoatReflectionCoords=clearCoatReflectionVector; #else vec2 clearCoatReflectionCoords=clearCoatReflectionVector.xy; #ifdef REFLECTIONMAP_PROJECTION clearCoatReflectionCoords/=clearCoatReflectionVector.z; #endif clearCoatReflectionCoords.y=1.0-clearCoatReflectionCoords.y; #endif sampleReflectionTexture( clearCoatAlphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) clearCoatNdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION clearCoatRoughness, #endif reflectionSampler, clearCoatReflectionCoords, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif environmentClearCoatRadiance ); #if DEBUGMODE>0 outParams.environmentClearCoatRadiance=environmentClearCoatRadiance; #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) vec3 clearCoatEnvironmentReflectance=getReflectanceFromBRDFLookup(vec3(vClearCoatRefractionParams.x),environmentClearCoatBrdf); #ifdef RADIANCEOCCLUSION float clearCoatSeo=environmentRadianceOcclusion(ambientMonochrome,clearCoatNdotVUnclamped); clearCoatEnvironmentReflectance*=clearCoatSeo; #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D float clearCoatEho=environmentHorizonOcclusion(-viewDirectionW,clearCoatNormalW,geometricNormalW); clearCoatEnvironmentReflectance*=clearCoatEho; #endif #endif #endif #else vec3 clearCoatEnvironmentReflectance=getReflectanceFromAnalyticalBRDFLookup_Jones(clearCoatNdotV,vec3(1.),vec3(1.),sqrt(1.-clearCoatRoughness)); #endif clearCoatEnvironmentReflectance*=clearCoatIntensity; #if DEBUGMODE>0 outParams.clearCoatEnvironmentReflectance=clearCoatEnvironmentReflectance; #endif outParams.finalClearCoatRadianceScaled= environmentClearCoatRadiance.rgb * clearCoatEnvironmentReflectance * vLightingIntensity.z; #endif #if defined(CLEARCOAT_TINT) outParams.absorption=computeClearCoatAbsorption(outParams.clearCoatNdotVRefract,outParams.clearCoatNdotVRefract,outParams.clearCoatColor,clearCoatThickness,clearCoatIntensity); #endif float fresnelIBLClearCoat=fresnelSchlickGGX(clearCoatNdotV,vClearCoatRefractionParams.x,CLEARCOATREFLECTANCE90); fresnelIBLClearCoat*=clearCoatIntensity; outParams.conservationFactor=(1.-fresnelIBLClearCoat); #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) outParams.energyConservationFactorClearCoat=getEnergyConservationFactor(outParams.specularEnvironmentR0,environmentClearCoatBrdf); #endif } #endif ";X.a.IncludesShadersStore.pbrBlockClearcoat=a;V="struct subSurfaceOutParams { vec3 specularEnvironmentReflectance; #ifdef SS_REFRACTION vec3 finalRefraction; vec3 surfaceAlbedo; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY float alpha; #endif #ifdef REFLECTION float refractionFactorForIrradiance; #endif #endif #ifdef SS_TRANSLUCENCY vec3 transmittance; float translucencyIntensity; #ifdef REFLECTION vec3 refractionIrradiance; #endif #endif #if DEBUGMODE>0 vec4 thicknessMap; vec4 environmentRefraction; vec3 refractionTransmittance; #endif }; #ifdef SUBSURFACE #define pbr_inline #define inline void subSurfaceBlock( in vec3 vSubSurfaceIntensity, in vec2 vThicknessParam, in vec4 vTintColor, in vec3 normalW, in vec3 specularEnvironmentReflectance, #ifdef SS_THICKNESSANDMASK_TEXTURE in vec4 thicknessMap, #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE in vec4 refractionIntensityMap, #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE in vec4 translucencyIntensityMap, #endif #ifdef REFLECTION #ifdef SS_TRANSLUCENCY in mat4 reflectionMatrix, #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) in vec3 irradianceVector_, #endif #if defined(REALTIME_FILTERING) in samplerCube reflectionSampler, in vec2 vReflectionFilteringInfo, #endif #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D in samplerCube irradianceSampler, #else in sampler2D irradianceSampler, #endif #endif #endif #endif #if defined(SS_REFRACTION) || defined(SS_TRANSLUCENCY) in vec3 surfaceAlbedo, #endif #ifdef SS_REFRACTION in vec3 vPositionW, in vec3 viewDirectionW, in mat4 view, in vec4 vRefractionInfos, in mat4 refractionMatrix, in vec4 vRefractionMicrosurfaceInfos, in vec4 vLightingIntensity, #ifdef SS_LINKREFRACTIONTOTRANSPARENCY in float alpha, #endif #ifdef SS_LODINREFRACTIONALPHA in float NdotVUnclamped, #endif #ifdef SS_LINEARSPECULARREFRACTION in float roughness, #endif in float alphaG, #ifdef SS_REFRACTIONMAP_3D in samplerCube refractionSampler, #ifndef LODBASEDMICROSFURACE in samplerCube refractionSamplerLow, in samplerCube refractionSamplerHigh, #endif #else in sampler2D refractionSampler, #ifndef LODBASEDMICROSFURACE in sampler2D refractionSamplerLow, in sampler2D refractionSamplerHigh, #endif #endif #ifdef ANISOTROPIC in anisotropicOutParams anisotropicOut, #endif #ifdef REALTIME_FILTERING in vec2 vRefractionFilteringInfo, #endif #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC in vec3 refractionPosition, in vec3 refractionSize, #endif #endif #ifdef SS_TRANSLUCENCY in vec3 vDiffusionDistance, #endif out subSurfaceOutParams outParams ) { outParams.specularEnvironmentReflectance=specularEnvironmentReflectance; #ifdef SS_REFRACTION float refractionIntensity=vSubSurfaceIntensity.x; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY refractionIntensity*=(1.0-alpha); outParams.alpha=1.0; #endif #endif #ifdef SS_TRANSLUCENCY float translucencyIntensity=vSubSurfaceIntensity.y; #endif #ifdef SS_THICKNESSANDMASK_TEXTURE #if defined(SS_USE_GLTF_TEXTURES) float thickness=thicknessMap.g*vThicknessParam.y+vThicknessParam.x; #else float thickness=thicknessMap.r*vThicknessParam.y+vThicknessParam.x; #endif #if DEBUGMODE>0 outParams.thicknessMap=thicknessMap; #endif #ifdef SS_MASK_FROM_THICKNESS_TEXTURE #if defined(SS_REFRACTION) && defined(SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE) #if defined(SS_USE_GLTF_TEXTURES) refractionIntensity*=thicknessMap.r; #else refractionIntensity*=thicknessMap.g; #endif #endif #if defined(SS_TRANSLUCENCY) && defined(SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE) translucencyIntensity*=thicknessMap.b; #endif #endif #else float thickness=vThicknessParam.y; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE #ifdef SS_USE_GLTF_TEXTURES refractionIntensity*=refractionIntensityMap.r; #else refractionIntensity*=refractionIntensityMap.g; #endif #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE translucencyIntensity*=translucencyIntensityMap.b; #endif #ifdef SS_TRANSLUCENCY thickness=maxEps(thickness); vec3 transmittance=transmittanceBRDF_Burley(vTintColor.rgb,vDiffusionDistance,thickness); transmittance*=translucencyIntensity; outParams.transmittance=transmittance; outParams.translucencyIntensity=translucencyIntensity; #endif #ifdef SS_REFRACTION vec4 environmentRefraction=vec4(0.,0.,0.,0.); #ifdef ANISOTROPIC vec3 refractionVector=refract(-viewDirectionW,anisotropicOut.anisotropicNormal,vRefractionInfos.y); #else vec3 refractionVector=refract(-viewDirectionW,normalW,vRefractionInfos.y); #endif #ifdef SS_REFRACTIONMAP_OPPOSITEZ refractionVector.z*=-1.0; #endif #ifdef SS_REFRACTIONMAP_3D #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC refractionVector=parallaxCorrectNormal(vPositionW,refractionVector,refractionSize,refractionPosition); #endif refractionVector.y=refractionVector.y*vRefractionInfos.w; vec3 refractionCoords=refractionVector; refractionCoords=vec3(refractionMatrix*vec4(refractionCoords,0)); #else #ifdef SS_USE_THICKNESS_AS_DEPTH vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*thickness,1.0))); #else vec3 vRefractionUVW=vec3(refractionMatrix*(view*vec4(vPositionW+refractionVector*vRefractionInfos.z,1.0))); #endif vec2 refractionCoords=vRefractionUVW.xy/vRefractionUVW.z; refractionCoords.y=1.0-refractionCoords.y; #endif #ifdef SS_HAS_THICKNESS float ior=vRefractionInfos.y; #else float ior=vRefractionMicrosurfaceInfos.w; #endif #ifdef SS_LODINREFRACTIONALPHA float refractionAlphaG=alphaG; refractionAlphaG=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLodFromAlphaG(vRefractionMicrosurfaceInfos.x,refractionAlphaG,NdotVUnclamped); #elif defined(SS_LINEARSPECULARREFRACTION) float refractionRoughness=alphaG; refractionRoughness=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLinearLodFromRoughness(vRefractionMicrosurfaceInfos.x,refractionRoughness); #else float refractionAlphaG=alphaG; refractionAlphaG=mix(alphaG,0.0,clamp(ior*3.0-2.0,0.0,1.0)); float refractionLOD=getLodFromAlphaG(vRefractionMicrosurfaceInfos.x,refractionAlphaG); #endif #ifdef LODBASEDMICROSFURACE refractionLOD=refractionLOD*vRefractionMicrosurfaceInfos.y+vRefractionMicrosurfaceInfos.z; #ifdef SS_LODINREFRACTIONALPHA float automaticRefractionLOD=UNPACK_LOD(sampleRefraction(refractionSampler,refractionCoords).a); float requestedRefractionLOD=max(automaticRefractionLOD,refractionLOD); #else float requestedRefractionLOD=refractionLOD; #endif #ifdef REALTIME_FILTERING environmentRefraction=vec4(radiance(alphaG,refractionSampler,refractionCoords,vRefractionFilteringInfo),1.0); #else environmentRefraction=sampleRefractionLod(refractionSampler,refractionCoords,requestedRefractionLOD); #endif #else float lodRefractionNormalized=saturate(refractionLOD/log2(vRefractionMicrosurfaceInfos.x)); float lodRefractionNormalizedDoubled=lodRefractionNormalized*2.0; vec4 environmentRefractionMid=sampleRefraction(refractionSampler,refractionCoords); if (lodRefractionNormalizedDoubled<1.0){ environmentRefraction=mix( sampleRefraction(refractionSamplerHigh,refractionCoords), environmentRefractionMid, lodRefractionNormalizedDoubled ); } else { environmentRefraction=mix( environmentRefractionMid, sampleRefraction(refractionSamplerLow,refractionCoords), lodRefractionNormalizedDoubled-1.0 ); } #endif #ifdef SS_RGBDREFRACTION environmentRefraction.rgb=fromRGBD(environmentRefraction); #endif #ifdef SS_GAMMAREFRACTION environmentRefraction.rgb=toLinearSpace(environmentRefraction.rgb); #endif environmentRefraction.rgb*=vRefractionInfos.x; #endif #ifdef SS_REFRACTION vec3 refractionTransmittance=vec3(refractionIntensity); #ifdef SS_THICKNESSANDMASK_TEXTURE vec3 volumeAlbedo=computeColorAtDistanceInMedia(vTintColor.rgb,vTintColor.w); refractionTransmittance*=cocaLambert(volumeAlbedo,thickness); #elif defined(SS_LINKREFRACTIONTOTRANSPARENCY) float maxChannel=max(max(surfaceAlbedo.r,surfaceAlbedo.g),surfaceAlbedo.b); vec3 volumeAlbedo=saturate(maxChannel*surfaceAlbedo); environmentRefraction.rgb*=volumeAlbedo; #else vec3 volumeAlbedo=computeColorAtDistanceInMedia(vTintColor.rgb,vTintColor.w); refractionTransmittance*=cocaLambert(volumeAlbedo,vThicknessParam.y); #endif #ifdef SS_ALBEDOFORREFRACTIONTINT environmentRefraction.rgb*=surfaceAlbedo.rgb; #endif outParams.surfaceAlbedo=surfaceAlbedo*(1.-refractionIntensity); #ifdef REFLECTION outParams.refractionFactorForIrradiance=(1.-refractionIntensity); #endif #ifdef UNUSED_MULTIPLEBOUNCES vec3 bounceSpecularEnvironmentReflectance=(2.0*specularEnvironmentReflectance)/(1.0+specularEnvironmentReflectance); outParams.specularEnvironmentReflectance=mix(bounceSpecularEnvironmentReflectance,specularEnvironmentReflectance,refractionIntensity); #endif refractionTransmittance*=1.0-outParams.specularEnvironmentReflectance; #if DEBUGMODE>0 outParams.refractionTransmittance=refractionTransmittance; #endif outParams.finalRefraction=environmentRefraction.rgb*refractionTransmittance*vLightingIntensity.z; #if DEBUGMODE>0 outParams.environmentRefraction=environmentRefraction; #endif #endif #if defined(REFLECTION) && defined(SS_TRANSLUCENCY) #if defined(NORMAL) && defined(USESPHERICALINVERTEX) || !defined(USESPHERICALFROMREFLECTIONMAP) vec3 irradianceVector=vec3(reflectionMatrix*vec4(normalW,0)).xyz; #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z*=-1.0; #endif #ifdef INVERTCUBICMAP irradianceVector.y*=-1.0; #endif #else vec3 irradianceVector=irradianceVector_; #endif #if defined(USESPHERICALFROMREFLECTIONMAP) #if defined(REALTIME_FILTERING) vec3 refractionIrradiance=irradiance(reflectionSampler,-irradianceVector,vReflectionFilteringInfo); #else vec3 refractionIrradiance=computeEnvironmentIrradiance(-irradianceVector); #endif #elif defined(USEIRRADIANCEMAP) #ifdef REFLECTIONMAP_3D vec3 irradianceCoords=irradianceVector; #else vec2 irradianceCoords=irradianceVector.xy; #ifdef REFLECTIONMAP_PROJECTION irradianceCoords/=irradianceVector.z; #endif irradianceCoords.y=1.0-irradianceCoords.y; #endif vec4 refractionIrradiance=sampleReflection(irradianceSampler,-irradianceCoords); #ifdef RGBDREFLECTION refractionIrradiance.rgb=fromRGBD(refractionIrradiance); #endif #ifdef GAMMAREFLECTION refractionIrradiance.rgb=toLinearSpace(refractionIrradiance.rgb); #endif #else vec4 refractionIrradiance=vec4(0.); #endif refractionIrradiance.rgb*=transmittance; #ifdef SS_ALBEDOFORTRANSLUCENCYTINT refractionIrradiance.rgb*=surfaceAlbedo.rgb; #endif outParams.refractionIrradiance=refractionIrradiance.rgb; #endif } #endif ";X.a.IncludesShadersStore.pbrBlockSubSurface=V;c(193);a="vec3 viewDirectionW=normalize(vEyePosition.xyz-vPositionW); #ifdef NORMAL vec3 normalW=normalize(vNormalW); #else vec3 normalW=normalize(cross(dFdx(vPositionW),dFdy(vPositionW)))*vEyePosition.w; #endif vec3 geometricNormalW=normalW; #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) geometricNormalW=gl_FrontFacing ? geometricNormalW : -geometricNormalW; #endif ";X.a.IncludesShadersStore.pbrBlockNormalGeometric=a;c(156);V="#if defined(FORCENORMALFORWARD) && defined(NORMAL) vec3 faceNormal=normalize(cross(dFdx(vPositionW),dFdy(vPositionW)))*vEyePosition.w; #if defined(TWOSIDEDLIGHTING) faceNormal=gl_FrontFacing ? faceNormal : -faceNormal; #endif normalW*=sign(dot(normalW,faceNormal)); #endif #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) normalW=gl_FrontFacing ? normalW : -normalW; #endif ";X.a.IncludesShadersStore.pbrBlockNormalFinal=V;c(194);a="#ifdef LIGHTMAP vec4 lightmapColor=texture2D(lightmapSampler,vLightmapUV+uvOffset); #ifdef RGBDLIGHTMAP lightmapColor.rgb=fromRGBD(lightmapColor); #endif #ifdef GAMMALIGHTMAP lightmapColor.rgb=toLinearSpace(lightmapColor.rgb); #endif lightmapColor.rgb*=vLightmapInfos.y; #endif ";X.a.IncludesShadersStore.pbrBlockLightmapInit=a;V="float NdotVUnclamped=dot(normalW,viewDirectionW); float NdotV=absEps(NdotVUnclamped); float alphaG=convertRoughnessToAverageSlope(roughness); vec2 AARoughnessFactors=getAARoughnessFactors(normalW.xyz); #ifdef SPECULARAA alphaG+=AARoughnessFactors.y; #endif #if defined(ENVIRONMENTBRDF) vec3 environmentBrdf=getBRDFLookup(NdotV,roughness); #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION #ifdef AMBIENTINGRAYSCALE float ambientMonochrome=aoOut.ambientOcclusionColor.r; #else float ambientMonochrome=getLuminance(aoOut.ambientOcclusionColor); #endif float seo=environmentRadianceOcclusion(ambientMonochrome,NdotVUnclamped); #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D float eho=environmentHorizonOcclusion(-viewDirectionW,normalW,geometricNormalW); #endif #endif #endif #endif ";X.a.IncludesShadersStore.pbrBlockGeometryInfo=V;a="float reflectance=max(max(reflectivityOut.surfaceReflectivityColor.r,reflectivityOut.surfaceReflectivityColor.g),reflectivityOut.surfaceReflectivityColor.b); vec3 specularEnvironmentR0=reflectivityOut.surfaceReflectivityColor.rgb; #ifdef METALLICWORKFLOW vec3 specularEnvironmentR90=vec3(metallicReflectanceFactors.a); #else vec3 specularEnvironmentR90=vec3(1.0,1.0,1.0); #endif #ifdef ALPHAFRESNEL float reflectance90=fresnelGrazingReflectance(reflectance); specularEnvironmentR90=specularEnvironmentR90*reflectance90; #endif ";X.a.IncludesShadersStore.pbrBlockReflectance0=a;V="#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) vec3 specularEnvironmentReflectance=getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,environmentBrdf); #ifdef RADIANCEOCCLUSION specularEnvironmentReflectance*=seo; #endif #ifdef HORIZONOCCLUSION #ifdef BUMP #ifdef REFLECTIONMAP_3D specularEnvironmentReflectance*=eho; #endif #endif #endif #else vec3 specularEnvironmentReflectance=getReflectanceFromAnalyticalBRDFLookup_Jones(NdotV,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,sqrt(microSurface)); #endif #ifdef CLEARCOAT specularEnvironmentReflectance*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) specularEnvironmentReflectance*=clearcoatOut.absorption; #endif #endif ";X.a.IncludesShadersStore.pbrBlockReflectance=V;a="vec3 diffuseBase=vec3(0.,0.,0.); #ifdef SPECULARTERM vec3 specularBase=vec3(0.,0.,0.); #endif #ifdef CLEARCOAT vec3 clearCoatBase=vec3(0.,0.,0.); #endif #ifdef SHEEN vec3 sheenBase=vec3(0.,0.,0.); #endif preLightingInfo preInfo; lightingInfo info; float shadow=1.; #if defined(CLEARCOAT) && defined(CLEARCOAT_TINT) vec3 absorption=vec3(0.); #endif ";X.a.IncludesShadersStore.pbrBlockDirectLighting=a;V=" #if defined(ENVIRONMENTBRDF) #ifdef MS_BRDF_ENERGY_CONSERVATION vec3 energyConservationFactor=getEnergyConservationFactor(clearcoatOut.specularEnvironmentR0,environmentBrdf); #endif #endif #ifndef METALLICWORKFLOW #ifdef SPECULAR_GLOSSINESS_ENERGY_CONSERVATION surfaceAlbedo.rgb=(1.-reflectance)*surfaceAlbedo.rgb; #endif #endif #if defined(SHEEN) && defined(SHEEN_ALBEDOSCALING) && defined(ENVIRONMENTBRDF) surfaceAlbedo.rgb=sheenOut.sheenAlbedoScaling*surfaceAlbedo.rgb; #endif #ifdef REFLECTION vec3 finalIrradiance=reflectionOut.environmentIrradiance; #if defined(CLEARCOAT) finalIrradiance*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) finalIrradiance*=clearcoatOut.absorption; #endif #endif #if defined(SS_REFRACTION) finalIrradiance*=subSurfaceOut.refractionFactorForIrradiance; #endif #if defined(SS_TRANSLUCENCY) finalIrradiance*=(1.0-subSurfaceOut.translucencyIntensity); finalIrradiance+=subSurfaceOut.refractionIrradiance; #endif finalIrradiance*=surfaceAlbedo.rgb; finalIrradiance*=vLightingIntensity.z; finalIrradiance*=aoOut.ambientOcclusionColor; #endif #ifdef SPECULARTERM vec3 finalSpecular=specularBase; finalSpecular=max(finalSpecular,0.0); vec3 finalSpecularScaled=finalSpecular*vLightingIntensity.x*vLightingIntensity.w; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalSpecularScaled*=energyConservationFactor; #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) finalSpecularScaled*=sheenOut.sheenAlbedoScaling; #endif #endif #ifdef REFLECTION vec3 finalRadiance=reflectionOut.environmentRadiance.rgb; finalRadiance*=subSurfaceOut.specularEnvironmentReflectance; vec3 finalRadianceScaled=finalRadiance*vLightingIntensity.z; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalRadianceScaled*=energyConservationFactor; #endif #if defined(SHEEN) && defined(ENVIRONMENTBRDF) && defined(SHEEN_ALBEDOSCALING) finalRadianceScaled*=sheenOut.sheenAlbedoScaling; #endif #endif #ifdef SHEEN vec3 finalSheen=sheenBase*sheenOut.sheenColor; finalSheen=max(finalSheen,0.0); vec3 finalSheenScaled=finalSheen*vLightingIntensity.x*vLightingIntensity.w; #if defined(CLEARCOAT) && defined(REFLECTION) && defined(ENVIRONMENTBRDF) sheenOut.finalSheenRadianceScaled*=clearcoatOut.conservationFactor; #if defined(CLEARCOAT_TINT) sheenOut.finalSheenRadianceScaled*=clearcoatOut.absorption; #endif #endif #endif #ifdef CLEARCOAT vec3 finalClearCoat=clearCoatBase; finalClearCoat=max(finalClearCoat,0.0); vec3 finalClearCoatScaled=finalClearCoat*vLightingIntensity.x*vLightingIntensity.w; #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION) finalClearCoatScaled*=clearcoatOut.energyConservationFactorClearCoat; #endif #ifdef SS_REFRACTION subSurfaceOut.finalRefraction*=clearcoatOut.conservationFactor; #ifdef CLEARCOAT_TINT subSurfaceOut.finalRefraction*=clearcoatOut.absorption; #endif #endif #endif #ifdef ALPHABLEND float luminanceOverAlpha=0.0; #if defined(REFLECTION) && defined(RADIANCEOVERALPHA) luminanceOverAlpha+=getLuminance(finalRadianceScaled); #if defined(CLEARCOAT) luminanceOverAlpha+=getLuminance(clearcoatOut.finalClearCoatRadianceScaled); #endif #endif #if defined(SPECULARTERM) && defined(SPECULAROVERALPHA) luminanceOverAlpha+=getLuminance(finalSpecularScaled); #endif #if defined(CLEARCOAT) && defined(CLEARCOATOVERALPHA) luminanceOverAlpha+=getLuminance(finalClearCoatScaled); #endif #if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA) || defined(CLEARCOATOVERALPHA) alpha=saturate(alpha+luminanceOverAlpha*luminanceOverAlpha); #endif #endif ";X.a.IncludesShadersStore.pbrBlockFinalLitComponents=V;a=" vec3 finalDiffuse=diffuseBase; finalDiffuse*=surfaceAlbedo.rgb; finalDiffuse=max(finalDiffuse,0.0); finalDiffuse*=vLightingIntensity.x; vec3 finalAmbient=vAmbientColor; finalAmbient*=surfaceAlbedo.rgb; vec3 finalEmissive=vEmissiveColor; #ifdef EMISSIVE vec3 emissiveColorTex=texture2D(emissiveSampler,vEmissiveUV+uvOffset).rgb; #ifdef GAMMAEMISSIVE finalEmissive*=toLinearSpace(emissiveColorTex.rgb); #else finalEmissive*=emissiveColorTex.rgb; #endif finalEmissive*=vEmissiveInfos.y; #endif finalEmissive*=vLightingIntensity.y; #ifdef AMBIENT vec3 ambientOcclusionForDirectDiffuse=mix(vec3(1.),aoOut.ambientOcclusionColor,vAmbientInfos.w); #else vec3 ambientOcclusionForDirectDiffuse=aoOut.ambientOcclusionColor; #endif finalAmbient*=aoOut.ambientOcclusionColor; finalDiffuse*=ambientOcclusionForDirectDiffuse; ";X.a.IncludesShadersStore.pbrBlockFinalUnlitComponents=a;V="vec4 finalColor=vec4( finalAmbient + finalDiffuse + #ifndef UNLIT #ifdef REFLECTION finalIrradiance + #endif #ifdef SPECULARTERM finalSpecularScaled + #endif #ifdef SHEEN finalSheenScaled + #endif #ifdef CLEARCOAT finalClearCoatScaled + #endif #ifdef REFLECTION finalRadianceScaled + #if defined(SHEEN) && defined(ENVIRONMENTBRDF) sheenOut.finalSheenRadianceScaled + #endif #ifdef CLEARCOAT clearcoatOut.finalClearCoatRadianceScaled + #endif #endif #ifdef SS_REFRACTION subSurfaceOut.finalRefraction + #endif #endif finalEmissive, alpha); #ifdef LIGHTMAP #ifndef LIGHTMAPEXCLUDED #ifdef USELIGHTMAPASSHADOWMAP finalColor.rgb*=lightmapColor.rgb; #else finalColor.rgb+=lightmapColor.rgb; #endif #endif #endif #define CUSTOM_FRAGMENT_BEFORE_FOG finalColor=max(finalColor,0.0); ";X.a.IncludesShadersStore.pbrBlockFinalColorComposition=V;c(183);a="#if defined(IMAGEPROCESSINGPOSTPROCESS) || defined(SS_SCATTERING) finalColor.rgb=clamp(finalColor.rgb,0.,30.0); #else finalColor=applyImageProcessing(finalColor); #endif finalColor.a*=visibility; #ifdef PREMULTIPLYALPHA finalColor.rgb*=finalColor.a; #endif ";X.a.IncludesShadersStore.pbrBlockImageProcessing=a;V="#if DEBUGMODE>0 if (vClipSpacePosition.x/vClipSpacePosition.w>=vDebugMode.x) { #if DEBUGMODE == 1 gl_FragColor.rgb=vPositionW.rgb; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 2 && defined(NORMAL) gl_FragColor.rgb=vNormalW.rgb; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 3 && defined(BUMP) || DEBUGMODE == 3 && defined(PARALLAX) || DEBUGMODE == 3 && defined(ANISOTROPIC) gl_FragColor.rgb=TBN[0]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 4 && defined(BUMP) || DEBUGMODE == 4 && defined(PARALLAX) || DEBUGMODE == 4 && defined(ANISOTROPIC) gl_FragColor.rgb=TBN[1]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 5 gl_FragColor.rgb=normalW; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 6 && defined(MAINUV1) gl_FragColor.rgb=vec3(vMainUV1,0.0); #elif DEBUGMODE == 7 && defined(MAINUV2) gl_FragColor.rgb=vec3(vMainUV2,0.0); #elif DEBUGMODE == 8 && defined(CLEARCOAT) && defined(CLEARCOAT_BUMP) gl_FragColor.rgb=clearcoatOut.TBNClearCoat[0]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 9 && defined(CLEARCOAT) && defined(CLEARCOAT_BUMP) gl_FragColor.rgb=clearcoatOut.TBNClearCoat[1]; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 10 && defined(CLEARCOAT) gl_FragColor.rgb=clearcoatOut.clearCoatNormalW; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 11 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicNormal; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 12 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicTangent; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 13 && defined(ANISOTROPIC) gl_FragColor.rgb=anisotropicOut.anisotropicBitangent; #define DEBUGMODE_NORMALIZE #elif DEBUGMODE == 20 && defined(ALBEDO) gl_FragColor.rgb=albedoTexture.rgb; #elif DEBUGMODE == 21 && defined(AMBIENT) gl_FragColor.rgb=aoOut.ambientOcclusionColorMap.rgb; #elif DEBUGMODE == 22 && defined(OPACITY) gl_FragColor.rgb=opacityMap.rgb; #elif DEBUGMODE == 23 && defined(EMISSIVE) gl_FragColor.rgb=emissiveColorTex.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 24 && defined(LIGHTMAP) gl_FragColor.rgb=lightmapColor.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 25 && defined(REFLECTIVITY) && defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.surfaceMetallicColorMap.rgb; #elif DEBUGMODE == 26 && defined(REFLECTIVITY) && !defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.surfaceReflectivityColorMap.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 27 && defined(CLEARCOAT) && defined(CLEARCOAT_TEXTURE) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatMapData.rg,0.0); #elif DEBUGMODE == 28 && defined(CLEARCOAT) && defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE) gl_FragColor.rgb=clearcoatOut.clearCoatTintMapData.rgb; #elif DEBUGMODE == 29 && defined(SHEEN) && defined(SHEEN_TEXTURE) gl_FragColor.rgb=sheenOut.sheenMapData.rgb; #elif DEBUGMODE == 30 && defined(ANISOTROPIC) && defined(ANISOTROPIC_TEXTURE) gl_FragColor.rgb=anisotropicOut.anisotropyMapData.rgb; #elif DEBUGMODE == 31 && defined(SUBSURFACE) && defined(SS_THICKNESSANDMASK_TEXTURE) gl_FragColor.rgb=subSurfaceOut.thicknessMap.rgb; #elif DEBUGMODE == 40 && defined(SS_REFRACTION) gl_FragColor.rgb=subSurfaceOut.environmentRefraction.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 41 && defined(REFLECTION) gl_FragColor.rgb=reflectionOut.environmentRadiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 42 && defined(CLEARCOAT) && defined(REFLECTION) gl_FragColor.rgb=clearcoatOut.environmentClearCoatRadiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 50 gl_FragColor.rgb=diffuseBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 51 && defined(SPECULARTERM) gl_FragColor.rgb=specularBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 52 && defined(CLEARCOAT) gl_FragColor.rgb=clearCoatBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 53 && defined(SHEEN) gl_FragColor.rgb=sheenBase.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 54 && defined(REFLECTION) gl_FragColor.rgb=reflectionOut.environmentIrradiance.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 60 gl_FragColor.rgb=surfaceAlbedo.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 61 gl_FragColor.rgb=clearcoatOut.specularEnvironmentR0; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 62 && defined(METALLICWORKFLOW) gl_FragColor.rgb=vec3(reflectivityOut.metallicRoughness.r); #elif DEBUGMODE == 71 && defined(METALLICWORKFLOW) gl_FragColor.rgb=reflectivityOut.metallicF0; #elif DEBUGMODE == 63 gl_FragColor.rgb=vec3(roughness); #elif DEBUGMODE == 64 gl_FragColor.rgb=vec3(alphaG); #elif DEBUGMODE == 65 gl_FragColor.rgb=vec3(NdotV); #elif DEBUGMODE == 66 && defined(CLEARCOAT) && defined(CLEARCOAT_TINT) gl_FragColor.rgb=clearcoatOut.clearCoatColor.rgb; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 67 && defined(CLEARCOAT) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatRoughness); #elif DEBUGMODE == 68 && defined(CLEARCOAT) gl_FragColor.rgb=vec3(clearcoatOut.clearCoatNdotV); #elif DEBUGMODE == 69 && defined(SUBSURFACE) && defined(SS_TRANSLUCENCY) gl_FragColor.rgb=subSurfaceOut.transmittance; #elif DEBUGMODE == 70 && defined(SUBSURFACE) && defined(SS_REFRACTION) gl_FragColor.rgb=subSurfaceOut.refractionTransmittance; #elif DEBUGMODE == 80 && defined(RADIANCEOCCLUSION) gl_FragColor.rgb=vec3(seo); #elif DEBUGMODE == 81 && defined(HORIZONOCCLUSION) gl_FragColor.rgb=vec3(eho); #elif DEBUGMODE == 82 && defined(MS_BRDF_ENERGY_CONSERVATION) gl_FragColor.rgb=vec3(energyConservationFactor); #elif DEBUGMODE == 83 && defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) gl_FragColor.rgb=specularEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 84 && defined(CLEARCOAT) && defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) gl_FragColor.rgb=clearcoatOut.clearCoatEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 85 && defined(SHEEN) && defined(REFLECTION) gl_FragColor.rgb=sheenOut.sheenEnvironmentReflectance; #define DEBUGMODE_GAMMA #elif DEBUGMODE == 86 && defined(ALPHABLEND) gl_FragColor.rgb=vec3(luminanceOverAlpha); #elif DEBUGMODE == 87 gl_FragColor.rgb=vec3(alpha); #endif gl_FragColor.rgb*=vDebugMode.y; #ifdef DEBUGMODE_NORMALIZE gl_FragColor.rgb=normalize(gl_FragColor.rgb)*0.5+0.5; #endif #ifdef DEBUGMODE_GAMMA gl_FragColor.rgb=toGammaSpace(gl_FragColor.rgb); #endif gl_FragColor.a=1.0; #ifdef PREPASS gl_FragData[0]=toLinearSpace(gl_FragColor); gl_FragData[1]=vec4(0.,0.,0.,0.); #endif return; } #endif";X.a.IncludesShadersStore.pbrDebug=V;a="#if defined(BUMP) || !defined(NORMAL) || defined(FORCENORMALFORWARD) || defined(SPECULARAA) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) #extension GL_OES_standard_derivatives : enable #endif #ifdef LODBASEDMICROSFURACE #extension GL_EXT_shader_texture_lod : enable #endif #define CUSTOM_FRAGMENT_BEGIN #ifdef LOGARITHMICDEPTH #extension GL_EXT_frag_depth : enable #endif #include[SCENE_MRT_COUNT] precision highp float; #include #ifndef FROMLINEARSPACE #define FROMLINEARSPACE #endif #include<__decl__pbrFragment> #include #include<__decl__lightFragment>[0..maxSimultaneousLights] #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef REFLECTION #include #endif #define CUSTOM_FRAGMENT_DEFINITIONS #include #include #include #include #include #include #include #include #include void main(void) { #define CUSTOM_FRAGMENT_MAIN_BEGIN #include #include #include #include #include albedoOpacityOutParams albedoOpacityOut; #ifdef ALBEDO vec4 albedoTexture=texture2D(albedoSampler,vAlbedoUV+uvOffset); #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vOpacityUV+uvOffset); #endif albedoOpacityBlock( vAlbedoColor, #ifdef ALBEDO albedoTexture, vAlbedoInfos, #endif #ifdef OPACITY opacityMap, vOpacityInfos, #endif #ifdef DETAIL detailColor, vDetailInfos, #endif albedoOpacityOut ); vec3 surfaceAlbedo=albedoOpacityOut.surfaceAlbedo; float alpha=albedoOpacityOut.alpha; #define CUSTOM_FRAGMENT_UPDATE_ALPHA #include #define CUSTOM_FRAGMENT_BEFORE_LIGHTS ambientOcclusionOutParams aoOut; #ifdef AMBIENT vec3 ambientOcclusionColorMap=texture2D(ambientSampler,vAmbientUV+uvOffset).rgb; #endif ambientOcclusionBlock( #ifdef AMBIENT ambientOcclusionColorMap, vAmbientInfos, #endif aoOut ); #include #ifdef UNLIT vec3 diffuseBase=vec3(1.,1.,1.); #else vec3 baseColor=surfaceAlbedo; reflectivityOutParams reflectivityOut; #if defined(REFLECTIVITY) vec4 surfaceMetallicOrReflectivityColorMap=texture2D(reflectivitySampler,vReflectivityUV+uvOffset); vec4 baseReflectivity=surfaceMetallicOrReflectivityColorMap; #ifndef METALLICWORKFLOW #ifdef REFLECTIVITY_GAMMA surfaceMetallicOrReflectivityColorMap=toLinearSpace(surfaceMetallicOrReflectivityColorMap); #endif surfaceMetallicOrReflectivityColorMap.rgb*=vReflectivityInfos.y; #endif #endif #if defined(MICROSURFACEMAP) vec4 microSurfaceTexel=texture2D(microSurfaceSampler,vMicroSurfaceSamplerUV+uvOffset)*vMicroSurfaceSamplerInfos.y; #endif #ifdef METALLICWORKFLOW vec4 metallicReflectanceFactors=vMetallicReflectanceFactors; #ifdef REFLECTANCE vec4 reflectanceFactorsMap=texture2D(reflectanceSampler,vReflectanceUV+uvOffset); #ifdef REFLECTANCE_GAMMA reflectanceFactorsMap=toLinearSpace(reflectanceFactorsMap); #endif metallicReflectanceFactors.rgb*=reflectanceFactorsMap.rgb; #endif #ifdef METALLIC_REFLECTANCE vec4 metallicReflectanceFactorsMap=texture2D(metallicReflectanceSampler,vMetallicReflectanceUV+uvOffset); #ifdef METALLIC_REFLECTANCE_GAMMA metallicReflectanceFactorsMap=toLinearSpace(metallicReflectanceFactorsMap); #endif #ifndef METALLIC_REFLECTANCE_USE_ALPHA_ONLY metallicReflectanceFactors.rgb*=metallicReflectanceFactorsMap.rgb; #endif metallicReflectanceFactors*=metallicReflectanceFactorsMap.a; #endif #endif reflectivityBlock( vReflectivityColor, #ifdef METALLICWORKFLOW surfaceAlbedo, metallicReflectanceFactors, #endif #ifdef REFLECTIVITY vReflectivityInfos, surfaceMetallicOrReflectivityColorMap, #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor, #endif #ifdef MICROSURFACEMAP microSurfaceTexel, #endif #ifdef DETAIL detailColor, vDetailInfos, #endif reflectivityOut ); float microSurface=reflectivityOut.microSurface; float roughness=reflectivityOut.roughness; #ifdef METALLICWORKFLOW surfaceAlbedo=reflectivityOut.surfaceAlbedo; #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor=reflectivityOut.ambientOcclusionColor; #endif #ifdef ALPHAFRESNEL #if defined(ALPHATEST) || defined(ALPHABLEND) alphaFresnelOutParams alphaFresnelOut; alphaFresnelBlock( normalW, viewDirectionW, alpha, microSurface, alphaFresnelOut ); alpha=alphaFresnelOut.alpha; #endif #endif #include #ifdef ANISOTROPIC anisotropicOutParams anisotropicOut; #ifdef ANISOTROPIC_TEXTURE vec3 anisotropyMapData=texture2D(anisotropySampler,vAnisotropyUV+uvOffset).rgb*vAnisotropyInfos.y; #endif anisotropicBlock( vAnisotropy, #ifdef ANISOTROPIC_TEXTURE anisotropyMapData, #endif TBN, normalW, viewDirectionW, anisotropicOut ); #endif #ifdef REFLECTION reflectionOutParams reflectionOut; reflectionBlock( vPositionW, normalW, alphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, #ifdef ANISOTROPIC anisotropicOut, #endif #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) NdotVUnclamped, #endif #ifdef LINEARSPECULARREFLECTION roughness, #endif reflectionSampler, #if defined(NORMAL) && defined(USESPHERICALINVERTEX) vEnvironmentIrradiance, #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) reflectionMatrix, #endif #endif #ifdef USEIRRADIANCEMAP irradianceSampler, #endif #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif reflectionOut ); #endif #include #ifdef SHEEN sheenOutParams sheenOut; #ifdef SHEEN_TEXTURE vec4 sheenMapData=texture2D(sheenSampler,vSheenUV+uvOffset); #endif #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) vec4 sheenMapRoughnessData=texture2D(sheenRoughnessSampler,vSheenRoughnessUV+uvOffset)*vSheenInfos.w; #endif sheenBlock( vSheenColor, #ifdef SHEEN_ROUGHNESS vSheenRoughness, #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) sheenMapRoughnessData, #endif #endif roughness, #ifdef SHEEN_TEXTURE sheenMapData, vSheenInfos.y, #endif reflectance, #ifdef SHEEN_LINKWITHALBEDO baseColor, surfaceAlbedo, #endif #ifdef ENVIRONMENTBRDF NdotV, environmentBrdf, #endif #if defined(REFLECTION) && defined(ENVIRONMENTBRDF) AARoughnessFactors, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, vLightingIntensity, reflectionSampler, reflectionOut.reflectionCoords, NdotVUnclamped, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) seo, #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(HORIZONOCCLUSION) && defined(BUMP) && defined(REFLECTIONMAP_3D) eho, #endif #endif sheenOut ); #ifdef SHEEN_LINKWITHALBEDO surfaceAlbedo=sheenOut.surfaceAlbedo; #endif #endif clearcoatOutParams clearcoatOut; #ifdef CLEARCOAT #ifdef CLEARCOAT_TEXTURE vec2 clearCoatMapData=texture2D(clearCoatSampler,vClearCoatUV+uvOffset).rg*vClearCoatInfos.y; #endif #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) vec4 clearCoatMapRoughnessData=texture2D(clearCoatRoughnessSampler,vClearCoatRoughnessUV+uvOffset)*vClearCoatInfos.w; #endif #if defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE) vec4 clearCoatTintMapData=texture2D(clearCoatTintSampler,vClearCoatTintUV+uvOffset); #endif #ifdef CLEARCOAT_BUMP vec4 clearCoatBumpMapData=texture2D(clearCoatBumpSampler,vClearCoatBumpUV+uvOffset); #endif clearcoatBlock( vPositionW, geometricNormalW, viewDirectionW, vClearCoatParams, #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE) clearCoatMapRoughnessData, #endif specularEnvironmentR0, #ifdef CLEARCOAT_TEXTURE clearCoatMapData, #endif #ifdef CLEARCOAT_TINT vClearCoatTintParams, clearCoatColorAtDistance, vClearCoatRefractionParams, #ifdef CLEARCOAT_TINT_TEXTURE clearCoatTintMapData, #endif #endif #ifdef CLEARCOAT_BUMP vClearCoatBumpInfos, clearCoatBumpMapData, vClearCoatBumpUV, #if defined(TANGENT) && defined(NORMAL) vTBN, #else vClearCoatTangentSpaceParams, #endif #ifdef OBJECTSPACE_NORMALMAP normalMatrix, #endif #endif #if defined(FORCENORMALFORWARD) && defined(NORMAL) faceNormal, #endif #ifdef REFLECTION vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, vLightingIntensity, reflectionSampler, #ifndef LODBASEDMICROSFURACE reflectionSamplerLow, reflectionSamplerHigh, #endif #ifdef REALTIME_FILTERING vReflectionFilteringInfo, #endif #endif #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) #ifdef RADIANCEOCCLUSION ambientMonochrome, #endif #endif #if defined(CLEARCOAT_BUMP) || defined(TWOSIDEDLIGHTING) (gl_FrontFacing ? 1. : -1.), #endif clearcoatOut ); #else clearcoatOut.specularEnvironmentR0=specularEnvironmentR0; #endif #include subSurfaceOutParams subSurfaceOut; #ifdef SUBSURFACE #ifdef SS_THICKNESSANDMASK_TEXTURE vec4 thicknessMap=texture2D(thicknessSampler,vThicknessUV+uvOffset); #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE vec4 refractionIntensityMap=texture2D(refractionIntensitySampler,vRefractionIntensityUV+uvOffset); #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE vec4 translucencyIntensityMap=texture2D(translucencyIntensitySampler,vTranslucencyIntensityUV+uvOffset); #endif subSurfaceBlock( vSubSurfaceIntensity, vThicknessParam, vTintColor, normalW, specularEnvironmentReflectance, #ifdef SS_THICKNESSANDMASK_TEXTURE thicknessMap, #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE refractionIntensityMap, #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE translucencyIntensityMap, #endif #ifdef REFLECTION #ifdef SS_TRANSLUCENCY reflectionMatrix, #ifdef USESPHERICALFROMREFLECTIONMAP #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) reflectionOut.irradianceVector, #endif #if defined(REALTIME_FILTERING) reflectionSampler, vReflectionFilteringInfo, #endif #endif #ifdef USEIRRADIANCEMAP irradianceSampler, #endif #endif #endif #if defined(SS_REFRACTION) || defined(SS_TRANSLUCENCY) surfaceAlbedo, #endif #ifdef SS_REFRACTION vPositionW, viewDirectionW, view, vRefractionInfos, refractionMatrix, vRefractionMicrosurfaceInfos, vLightingIntensity, #ifdef SS_LINKREFRACTIONTOTRANSPARENCY alpha, #endif #ifdef SS_LODINREFRACTIONALPHA NdotVUnclamped, #endif #ifdef SS_LINEARSPECULARREFRACTION roughness, #endif alphaG, refractionSampler, #ifndef LODBASEDMICROSFURACE refractionSamplerLow, refractionSamplerHigh, #endif #ifdef ANISOTROPIC anisotropicOut, #endif #ifdef REALTIME_FILTERING vRefractionFilteringInfo, #endif #ifdef SS_USE_LOCAL_REFRACTIONMAP_CUBIC vRefractionPosition, vRefractionSize, #endif #endif #ifdef SS_TRANSLUCENCY vDiffusionDistance, #endif subSurfaceOut ); #ifdef SS_REFRACTION surfaceAlbedo=subSurfaceOut.surfaceAlbedo; #ifdef SS_LINKREFRACTIONTOTRANSPARENCY alpha=subSurfaceOut.alpha; #endif #endif #else subSurfaceOut.specularEnvironmentReflectance=specularEnvironmentReflectance; #endif #include #include[0..maxSimultaneousLights] #include #endif #include #define CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION #include #include #include(color,finalColor) #include #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR #ifdef PREPASS float writeGeometryInfo=finalColor.a>0.4 ? 1.0 : 0.0; #ifdef PREPASS_POSITION gl_FragData[PREPASS_POSITION_INDEX]=vec4(vPositionW,writeGeometryInfo); #endif #ifdef PREPASS_VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[PREPASS_VELOCITY_INDEX]=vec4(velocity,0.0,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT vec3 sqAlbedo=sqrt(surfaceAlbedo); #endif #ifdef PREPASS_IRRADIANCE vec3 irradiance=finalDiffuse; #ifndef UNLIT #ifdef REFLECTION irradiance+=finalIrradiance; #endif #endif #ifdef SS_SCATTERING gl_FragData[0]=vec4(finalColor.rgb-irradiance,finalColor.a); irradiance/=sqAlbedo; #else gl_FragData[0]=finalColor; float scatteringDiffusionProfile=255.; #endif gl_FragData[PREPASS_IRRADIANCE_INDEX]=vec4(clamp(irradiance,vec3(0.),vec3(1.)),scatteringDiffusionProfile/255.); #else gl_FragData[0]=vec4(finalColor.rgb,finalColor.a); #endif #ifdef PREPASS_DEPTH gl_FragData[PREPASS_DEPTH_INDEX]=vec4(vViewPos.z,0.0,0.0,writeGeometryInfo); #endif #ifdef PREPASS_NORMAL gl_FragData[PREPASS_NORMAL_INDEX]=vec4((view*vec4(normalW,0.0)).rgb,writeGeometryInfo); #endif #ifdef PREPASS_ALBEDO_SQRT gl_FragData[PREPASS_ALBEDO_SQRT_INDEX]=vec4(sqAlbedo,writeGeometryInfo); #endif #ifdef PREPASS_REFLECTIVITY #if defined(REFLECTIVITY) gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(baseReflectivity.rgb,writeGeometryInfo); #else gl_FragData[PREPASS_REFLECTIVITY_INDEX]=vec4(0.0,0.0,0.0,writeGeometryInfo); #endif #endif #endif #if !defined(PREPASS) || defined(WEBGL2) gl_FragColor=finalColor; #endif #if ORDER_INDEPENDENT_TRANSPARENCY if (fragDepth == nearestDepth) { frontColor.rgb+=finalColor.rgb*finalColor.a*alphaMultiplier; frontColor.a=1.0-alphaMultiplier*(1.0-finalColor.a); } else { backColor+=finalColor; } #endif #include } ";X.a.ShadersStore.pbrPixelShader=a;V="uniform mat4 view; uniform mat4 viewProjection; #ifdef ALBEDO uniform mat4 albedoMatrix; uniform vec2 vAlbedoInfos; #endif #ifdef AMBIENT uniform mat4 ambientMatrix; uniform vec4 vAmbientInfos; #endif #ifdef OPACITY uniform mat4 opacityMatrix; uniform vec2 vOpacityInfos; #endif #ifdef EMISSIVE uniform vec2 vEmissiveInfos; uniform mat4 emissiveMatrix; #endif #ifdef LIGHTMAP uniform vec2 vLightmapInfos; uniform mat4 lightmapMatrix; #endif #ifdef REFLECTIVITY uniform vec3 vReflectivityInfos; uniform mat4 reflectivityMatrix; #endif #ifdef METALLIC_REFLECTANCE uniform vec2 vMetallicReflectanceInfos; uniform mat4 metallicReflectanceMatrix; #endif #ifdef REFLECTANCE uniform vec2 vReflectanceInfos; uniform mat4 reflectanceMatrix; #endif #ifdef MICROSURFACEMAP uniform vec2 vMicroSurfaceSamplerInfos; uniform mat4 microSurfaceSamplerMatrix; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform mat4 bumpMatrix; #endif #ifdef POINTSIZE uniform float pointSize; #endif #ifdef REFLECTION uniform vec2 vReflectionInfos; uniform mat4 reflectionMatrix; #endif #ifdef CLEARCOAT #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS) uniform vec4 vClearCoatInfos; #endif #ifdef CLEARCOAT_TEXTURE uniform mat4 clearCoatMatrix; #endif #ifdef CLEARCOAT_TEXTURE_ROUGHNESS uniform mat4 clearCoatRoughnessMatrix; #endif #ifdef CLEARCOAT_BUMP uniform vec2 vClearCoatBumpInfos; uniform mat4 clearCoatBumpMatrix; #endif #ifdef CLEARCOAT_TINT_TEXTURE uniform vec2 vClearCoatTintInfos; uniform mat4 clearCoatTintMatrix; #endif #endif #ifdef ANISOTROPIC #ifdef ANISOTROPIC_TEXTURE uniform vec2 vAnisotropyInfos; uniform mat4 anisotropyMatrix; #endif #endif #ifdef SHEEN #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS) uniform vec4 vSheenInfos; #endif #ifdef SHEEN_TEXTURE uniform mat4 sheenMatrix; #endif #ifdef SHEEN_TEXTURE_ROUGHNESS uniform mat4 sheenRoughnessMatrix; #endif #endif #ifdef SUBSURFACE #ifdef SS_REFRACTION uniform vec4 vRefractionInfos; uniform mat4 refractionMatrix; #endif #ifdef SS_THICKNESSANDMASK_TEXTURE uniform vec2 vThicknessInfos; uniform mat4 thicknessMatrix; #endif #ifdef SS_REFRACTIONINTENSITY_TEXTURE uniform vec2 vRefractionIntensityInfos; uniform mat4 refractionIntensityMatrix; #endif #ifdef SS_TRANSLUCENCYINTENSITY_TEXTURE uniform vec2 vTranslucencyIntensityInfos; uniform mat4 translucencyIntensityMatrix; #endif #endif #ifdef NORMAL #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) #ifdef USESPHERICALFROMREFLECTIONMAP #ifdef SPHERICAL_HARMONICS uniform vec3 vSphericalL00; uniform vec3 vSphericalL1_1; uniform vec3 vSphericalL10; uniform vec3 vSphericalL11; uniform vec3 vSphericalL2_2; uniform vec3 vSphericalL2_1; uniform vec3 vSphericalL20; uniform vec3 vSphericalL21; uniform vec3 vSphericalL22; #else uniform vec3 vSphericalX; uniform vec3 vSphericalY; uniform vec3 vSphericalZ; uniform vec3 vSphericalXX_ZZ; uniform vec3 vSphericalYY_ZZ; uniform vec3 vSphericalZZ; uniform vec3 vSphericalXY; uniform vec3 vSphericalYZ; uniform vec3 vSphericalZX; #endif #endif #endif #endif #ifdef DETAIL uniform vec4 vDetailInfos; uniform mat4 detailMatrix; #endif";X.a.IncludesShadersStore.pbrVertexDeclaration=V;c(195),c(196),c(197),c(198),c(101),c(102),c(110),c(111),c(199),c(200),c(201),c(184),c(186);a="precision highp float; #include<__decl__pbrVertex> #define CUSTOM_VERTEX_BEGIN attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #ifdef TANGENT attribute vec4 tangent; #endif #ifdef UV1 attribute vec2 uv; #endif #include[2..7] #include[1..7] #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include #include #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance) #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness) #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness) #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy) #endif #ifdef SUBSURFACE #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity) #endif varying vec3 vPositionW; #if DEBUGMODE>0 varying vec4 vClipSpacePosition; #endif #ifdef NORMAL varying vec3 vNormalW; #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) varying vec3 vEnvironmentIrradiance; #include #endif #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif #include #include #include #include<__decl__lightVxFragment>[0..maxSimultaneousLights] #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX varying vec3 vPositionUVW; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) varying vec3 vDirectionW; #endif #include #define CUSTOM_VERTEX_DEFINITIONS void main(void) { #define CUSTOM_VERTEX_MAIN_BEGIN vec3 positionUpdated=position; #ifdef NORMAL vec3 normalUpdated=normal; #endif #ifdef TANGENT vec4 tangentUpdated=tangent; #endif #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #ifdef REFLECTIONMAP_SKYBOX vPositionUVW=positionUpdated; #endif #define CUSTOM_VERTEX_UPDATE_POSITION #define CUSTOM_VERTEX_UPDATE_NORMAL #include #if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); vPositionW=vec3(worldPos); #include #ifdef NORMAL mat3 normalWorld=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vNormalW=normalUpdated/vec3(dot(normalWorld[0],normalWorld[0]),dot(normalWorld[1],normalWorld[1]),dot(normalWorld[2],normalWorld[2])); vNormalW=normalize(normalWorld*vNormalW); #else #ifdef NONUNIFORMSCALING normalWorld=transposeMat3(inverseMat3(normalWorld)); #endif vNormalW=normalize(normalWorld*normalUpdated); #endif #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) vec3 reflectionVector=vec3(reflectionMatrix*vec4(vNormalW,0)).xyz; #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z*=-1.0; #endif vEnvironmentIrradiance=computeEnvironmentIrradiance(reflectionVector); #endif #endif #define CUSTOM_VERTEX_UPDATE_WORLDPOS #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif #if DEBUGMODE>0 vClipSpacePosition=gl_Position; #endif #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) vDirectionW=normalize(vec3(finalWorld*vec4(positionUpdated,0.0))); #endif #ifndef UV1 vec2 uvUpdated=vec2(0.,0.); #endif #ifdef MAINUV1 vMainUV1=uvUpdated; #endif #include[2..7] #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_MATRIXNAME_,albedo,_INFONAME_,AlbedoInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_MATRIXNAME_,emissive,_INFONAME_,EmissiveInfos.x) #include(_DEFINENAME_,LIGHTMAP,_VARYINGNAME_,Lightmap,_MATRIXNAME_,lightmap,_INFONAME_,LightmapInfos.x) #include(_DEFINENAME_,REFLECTIVITY,_VARYINGNAME_,Reflectivity,_MATRIXNAME_,reflectivity,_INFONAME_,ReflectivityInfos.x) #include(_DEFINENAME_,MICROSURFACEMAP,_VARYINGNAME_,MicroSurfaceSampler,_MATRIXNAME_,microSurfaceSampler,_INFONAME_,MicroSurfaceSamplerInfos.x) #include(_DEFINENAME_,METALLIC_REFLECTANCE,_VARYINGNAME_,MetallicReflectance,_MATRIXNAME_,metallicReflectance,_INFONAME_,MetallicReflectanceInfos.x) #include(_DEFINENAME_,REFLECTANCE,_VARYINGNAME_,Reflectance,_MATRIXNAME_,reflectance,_INFONAME_,ReflectanceInfos.x) #include(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_MATRIXNAME_,bump,_INFONAME_,BumpInfos.x) #ifdef CLEARCOAT #include(_DEFINENAME_,CLEARCOAT_TEXTURE,_VARYINGNAME_,ClearCoat,_MATRIXNAME_,clearCoat,_INFONAME_,ClearCoatInfos.x) #include(_DEFINENAME_,CLEARCOAT_TEXTURE_ROUGHNESS,_VARYINGNAME_,ClearCoatRoughness,_MATRIXNAME_,clearCoatRoughness,_INFONAME_,ClearCoatInfos.z) #include(_DEFINENAME_,CLEARCOAT_BUMP,_VARYINGNAME_,ClearCoatBump,_MATRIXNAME_,clearCoatBump,_INFONAME_,ClearCoatBumpInfos.x) #include(_DEFINENAME_,CLEARCOAT_TINT_TEXTURE,_VARYINGNAME_,ClearCoatTint,_MATRIXNAME_,clearCoatTint,_INFONAME_,ClearCoatTintInfos.x) #endif #ifdef SHEEN #include(_DEFINENAME_,SHEEN_TEXTURE,_VARYINGNAME_,Sheen,_MATRIXNAME_,sheen,_INFONAME_,SheenInfos.x) #include(_DEFINENAME_,SHEEN_TEXTURE_ROUGHNESS,_VARYINGNAME_,SheenRoughness,_MATRIXNAME_,sheen,_INFONAME_,SheenInfos.z) #endif #ifdef ANISOTROPIC #include(_DEFINENAME_,ANISOTROPIC_TEXTURE,_VARYINGNAME_,Anisotropy,_MATRIXNAME_,anisotropy,_INFONAME_,AnisotropyInfos.x) #endif #ifdef SUBSURFACE #include(_DEFINENAME_,SS_THICKNESSANDMASK_TEXTURE,_VARYINGNAME_,Thickness,_MATRIXNAME_,thickness,_INFONAME_,ThicknessInfos.x) #include(_DEFINENAME_,SS_REFRACTIONINTENSITY_TEXTURE,_VARYINGNAME_,RefractionIntensity,_MATRIXNAME_,refractionIntensity,_INFONAME_,RefractionIntensityInfos.x) #include(_DEFINENAME_,SS_TRANSLUCENCYINTENSITY_TEXTURE,_VARYINGNAME_,TranslucencyIntensity,_MATRIXNAME_,translucencyIntensity,_INFONAME_,TranslucencyIntensityInfos.x) #endif #include #include #include #include[0..maxSimultaneousLights] #ifdef VERTEXCOLOR vColor=color; #endif #ifdef POINTSIZE gl_PointSize=pointSize; #endif #include #define CUSTOM_VERTEX_MAIN_END }";X.a.ShadersStore.pbrVertexShader=a;var ri=c(100),si={effect:null,subMesh:null},ti=function(a){function b(){var b=a.call(this)||this;return b.PBR=!0,b.NUM_SAMPLES="0",b.REALTIME_FILTERING=!1,b.MAINUV1=!1,b.MAINUV2=!1,b.MAINUV3=!1,b.MAINUV4=!1,b.MAINUV5=!1,b.MAINUV6=!1,b.UV1=!1,b.UV2=!1,b.UV3=!1,b.UV4=!1,b.UV5=!1,b.UV6=!1,b.ALBEDO=!1,b.GAMMAALBEDO=!1,b.ALBEDODIRECTUV=0,b.VERTEXCOLOR=!1,b.DETAIL=!1,b.DETAILDIRECTUV=0,b.DETAIL_NORMALBLENDMETHOD=0,b.AMBIENT=!1,b.AMBIENTDIRECTUV=0,b.AMBIENTINGRAYSCALE=!1,b.OPACITY=!1,b.VERTEXALPHA=!1,b.OPACITYDIRECTUV=0,b.OPACITYRGB=!1,b.ALPHATEST=!1,b.DEPTHPREPASS=!1,b.ALPHABLEND=!1,b.ALPHAFROMALBEDO=!1,b.ALPHATESTVALUE="0.5",b.SPECULAROVERALPHA=!1,b.RADIANCEOVERALPHA=!1,b.ALPHAFRESNEL=!1,b.LINEARALPHAFRESNEL=!1,b.PREMULTIPLYALPHA=!1,b.EMISSIVE=!1,b.EMISSIVEDIRECTUV=0,b.GAMMAEMISSIVE=!1,b.REFLECTIVITY=!1,b.REFLECTIVITY_GAMMA=!1,b.REFLECTIVITYDIRECTUV=0,b.SPECULARTERM=!1,b.MICROSURFACEFROMREFLECTIVITYMAP=!1,b.MICROSURFACEAUTOMATIC=!1,b.LODBASEDMICROSFURACE=!1,b.MICROSURFACEMAP=!1,b.MICROSURFACEMAPDIRECTUV=0,b.METALLICWORKFLOW=!1,b.ROUGHNESSSTOREINMETALMAPALPHA=!1,b.ROUGHNESSSTOREINMETALMAPGREEN=!1,b.METALLNESSSTOREINMETALMAPBLUE=!1,b.AOSTOREINMETALMAPRED=!1,b.METALLIC_REFLECTANCE=!1,b.METALLIC_REFLECTANCE_GAMMA=!1,b.METALLIC_REFLECTANCEDIRECTUV=0,b.METALLIC_REFLECTANCE_USE_ALPHA_ONLY=!1,b.REFLECTANCE=!1,b.REFLECTANCE_GAMMA=!1,b.REFLECTANCEDIRECTUV=0,b.ENVIRONMENTBRDF=!1,b.ENVIRONMENTBRDF_RGBD=!1,b.NORMAL=!1,b.TANGENT=!1,b.BUMP=!1,b.BUMPDIRECTUV=0,b.OBJECTSPACE_NORMALMAP=!1,b.PARALLAX=!1,b.PARALLAXOCCLUSION=!1,b.NORMALXYSCALE=!0,b.LIGHTMAP=!1,b.LIGHTMAPDIRECTUV=0,b.USELIGHTMAPASSHADOWMAP=!1,b.GAMMALIGHTMAP=!1,b.RGBDLIGHTMAP=!1,b.REFLECTION=!1,b.REFLECTIONMAP_3D=!1,b.REFLECTIONMAP_SPHERICAL=!1,b.REFLECTIONMAP_PLANAR=!1,b.REFLECTIONMAP_CUBIC=!1,b.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,b.REFLECTIONMAP_PROJECTION=!1,b.REFLECTIONMAP_SKYBOX=!1,b.REFLECTIONMAP_EXPLICIT=!1,b.REFLECTIONMAP_EQUIRECTANGULAR=!1,b.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,b.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,b.INVERTCUBICMAP=!1,b.USESPHERICALFROMREFLECTIONMAP=!1,b.USEIRRADIANCEMAP=!1,b.SPHERICAL_HARMONICS=!1,b.USESPHERICALINVERTEX=!1,b.REFLECTIONMAP_OPPOSITEZ=!1,b.LODINREFLECTIONALPHA=!1,b.GAMMAREFLECTION=!1,b.RGBDREFLECTION=!1,b.LINEARSPECULARREFLECTION=!1,b.RADIANCEOCCLUSION=!1,b.HORIZONOCCLUSION=!1,b.INSTANCES=!1,b.THIN_INSTANCES=!1,b.PREPASS=!1,b.PREPASS_IRRADIANCE=!1,b.PREPASS_IRRADIANCE_INDEX=-1,b.PREPASS_ALBEDO_SQRT=!1,b.PREPASS_ALBEDO_SQRT_INDEX=-1,b.PREPASS_DEPTH=!1,b.PREPASS_DEPTH_INDEX=-1,b.PREPASS_NORMAL=!1,b.PREPASS_NORMAL_INDEX=-1,b.PREPASS_POSITION=!1,b.PREPASS_POSITION_INDEX=-1,b.PREPASS_VELOCITY=!1,b.PREPASS_VELOCITY_INDEX=-1,b.PREPASS_REFLECTIVITY=!1,b.PREPASS_REFLECTIVITY_INDEX=-1,b.SCENE_MRT_COUNT=0,b.NUM_BONE_INFLUENCERS=0,b.BonesPerMesh=0,b.BONETEXTURE=!1,b.BONES_VELOCITY_ENABLED=!1,b.NONUNIFORMSCALING=!1,b.MORPHTARGETS=!1,b.MORPHTARGETS_NORMAL=!1,b.MORPHTARGETS_TANGENT=!1,b.MORPHTARGETS_UV=!1,b.NUM_MORPH_INFLUENCERS=0,b.MORPHTARGETS_TEXTURE=!1,b.IMAGEPROCESSING=!1,b.VIGNETTE=!1,b.VIGNETTEBLENDMODEMULTIPLY=!1,b.VIGNETTEBLENDMODEOPAQUE=!1,b.TONEMAPPING=!1,b.TONEMAPPING_ACES=!1,b.CONTRAST=!1,b.COLORCURVES=!1,b.COLORGRADING=!1,b.COLORGRADING3D=!1,b.SAMPLER3DGREENDEPTH=!1,b.SAMPLER3DBGRMAP=!1,b.IMAGEPROCESSINGPOSTPROCESS=!1,b.EXPOSURE=!1,b.MULTIVIEW=!1,b.ORDER_INDEPENDENT_TRANSPARENCY=!1,b.USEPHYSICALLIGHTFALLOFF=!1,b.USEGLTFLIGHTFALLOFF=!1,b.TWOSIDEDLIGHTING=!1,b.SHADOWFLOAT=!1,b.CLIPPLANE=!1,b.CLIPPLANE2=!1,b.CLIPPLANE3=!1,b.CLIPPLANE4=!1,b.CLIPPLANE5=!1,b.CLIPPLANE6=!1,b.POINTSIZE=!1,b.FOG=!1,b.LOGARITHMICDEPTH=!1,b.FORCENORMALFORWARD=!1,b.SPECULARAA=!1,b.CLEARCOAT=!1,b.CLEARCOAT_DEFAULTIOR=!1,b.CLEARCOAT_TEXTURE=!1,b.CLEARCOAT_TEXTURE_ROUGHNESS=!1,b.CLEARCOAT_TEXTUREDIRECTUV=0,b.CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV=0,b.CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,b.CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL=!1,b.CLEARCOAT_BUMP=!1,b.CLEARCOAT_BUMPDIRECTUV=0,b.CLEARCOAT_REMAP_F0=!0,b.CLEARCOAT_TINT=!1,b.CLEARCOAT_TINT_TEXTURE=!1,b.CLEARCOAT_TINT_GAMMATEXTURE=!1,b.CLEARCOAT_TINT_TEXTUREDIRECTUV=0,b.ANISOTROPIC=!1,b.ANISOTROPIC_TEXTURE=!1,b.ANISOTROPIC_TEXTUREDIRECTUV=0,b.BRDF_V_HEIGHT_CORRELATED=!1,b.MS_BRDF_ENERGY_CONSERVATION=!1,b.SPECULAR_GLOSSINESS_ENERGY_CONSERVATION=!1,b.SHEEN=!1,b.SHEEN_TEXTURE=!1,b.SHEEN_GAMMATEXTURE=!1,b.SHEEN_TEXTURE_ROUGHNESS=!1,b.SHEEN_TEXTUREDIRECTUV=0,b.SHEEN_TEXTURE_ROUGHNESSDIRECTUV=0,b.SHEEN_LINKWITHALBEDO=!1,b.SHEEN_ROUGHNESS=!1,b.SHEEN_ALBEDOSCALING=!1,b.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE=!1,b.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL=!1,b.SUBSURFACE=!1,b.SS_REFRACTION=!1,b.SS_REFRACTION_USE_INTENSITY_FROM_TEXTURE=!1,b.SS_TRANSLUCENCY=!1,b.SS_TRANSLUCENCY_USE_INTENSITY_FROM_TEXTURE=!1,b.SS_SCATTERING=!1,b.SS_THICKNESSANDMASK_TEXTURE=!1,b.SS_THICKNESSANDMASK_TEXTUREDIRECTUV=0,b.SS_HAS_THICKNESS=!1,b.SS_REFRACTIONINTENSITY_TEXTURE=!1,b.SS_REFRACTIONINTENSITY_TEXTUREDIRECTUV=0,b.SS_TRANSLUCENCYINTENSITY_TEXTURE=!1,b.SS_TRANSLUCENCYINTENSITY_TEXTUREDIRECTUV=0,b.SS_REFRACTIONMAP_3D=!1,b.SS_REFRACTIONMAP_OPPOSITEZ=!1,b.SS_LODINREFRACTIONALPHA=!1,b.SS_GAMMAREFRACTION=!1,b.SS_RGBDREFRACTION=!1,b.SS_LINEARSPECULARREFRACTION=!1,b.SS_LINKREFRACTIONTOTRANSPARENCY=!1,b.SS_ALBEDOFORREFRACTIONTINT=!1,b.SS_ALBEDOFORTRANSLUCENCYTINT=!1,b.SS_USE_LOCAL_REFRACTIONMAP_CUBIC=!1,b.SS_USE_THICKNESS_AS_DEPTH=!1,b.SS_MASK_FROM_THICKNESS_TEXTURE=!1,b.SS_USE_GLTF_TEXTURES=!1,b.UNLIT=!1,b.DEBUGMODE=0,b.rebuild(),b}return Object(l.d)(b,a),b.prototype.reset=function(){a.prototype.reset.call(this),this.ALPHATESTVALUE="0.5",this.PBR=!0,this.NORMALXYSCALE=!0},b}(Zh.a),ui=function(a){function b(c,d){var e=a.call(this,c,d)||this;return e._directIntensity=1,e._emissiveIntensity=1,e._environmentIntensity=1,e._specularIntensity=1,e._lightingInfos=new g.f(e._directIntensity,e._emissiveIntensity,e._environmentIntensity,e._specularIntensity),e._disableBumpMap=!1,e._albedoTexture=null,e._ambientTexture=null,e._ambientTextureStrength=1,e._ambientTextureImpactOnAnalyticalLights=b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,e._opacityTexture=null,e._reflectionTexture=null,e._emissiveTexture=null,e._reflectivityTexture=null,e._metallicTexture=null,e._metallic=null,e._roughness=null,e._metallicF0Factor=1,e._metallicReflectanceColor=h.a.White(),e._useOnlyMetallicFromMetallicReflectanceTexture=!1,e._metallicReflectanceTexture=null,e._reflectanceTexture=null,e._microSurfaceTexture=null,e._bumpTexture=null,e._lightmapTexture=null,e._ambientColor=new h.a(0,0,0),e._albedoColor=new h.a(1,1,1),e._reflectivityColor=new h.a(1,1,1),e._reflectionColor=new h.a(1,1,1),e._emissiveColor=new h.a(0,0,0),e._microSurface=.9,e._useLightmapAsShadowmap=!1,e._useHorizonOcclusion=!0,e._useRadianceOcclusion=!0,e._useAlphaFromAlbedoTexture=!1,e._useSpecularOverAlpha=!0,e._useMicroSurfaceFromReflectivityMapAlpha=!1,e._useRoughnessFromMetallicTextureAlpha=!0,e._useRoughnessFromMetallicTextureGreen=!1,e._useMetallnessFromMetallicTextureBlue=!1,e._useAmbientOcclusionFromMetallicTextureRed=!1,e._useAmbientInGrayScale=!1,e._useAutoMicroSurfaceFromReflectivityMap=!1,e._lightFalloff=b.LIGHTFALLOFF_PHYSICAL,e._useRadianceOverAlpha=!0,e._useObjectSpaceNormalMap=!1,e._useParallax=!1,e._useParallaxOcclusion=!1,e._parallaxScaleBias=.05,e._disableLighting=!1,e._maxSimultaneousLights=4,e._invertNormalMapX=!1,e._invertNormalMapY=!1,e._twoSidedLighting=!1,e._alphaCutOff=.4,e._forceAlphaTest=!1,e._useAlphaFresnel=!1,e._useLinearAlphaFresnel=!1,e._environmentBRDFTexture=null,e._forceIrradianceInFragment=!1,e._realTimeFiltering=!1,e._realTimeFilteringQuality=r.a.TEXTURE_FILTERING_QUALITY_LOW,e._forceNormalForward=!1,e._enableSpecularAntiAliasing=!1,e._imageProcessingObserver=null,e._renderTargets=new Ac.a(16),e._globalAmbientColor=new h.a(0,0,0),e._useLogarithmicDepth=!1,e._unlit=!1,e._debugMode=0,e.debugMode=0,e.debugLimit=-1,e.debugFactor=1,e.clearCoat=new ki(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.anisotropy=new li(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.brdf=new mi(e._markAllSubMeshesAsMiscDirty.bind(e)),e.sheen=new ni(e._markAllSubMeshesAsTexturesDirty.bind(e)),e.detailMap=new ri.a(e._markAllSubMeshesAsTexturesDirty.bind(e)),e._attachImageProcessingConfiguration(null),e.getRenderTargetTextures=function(){return e._renderTargets.reset(),ai.a.ReflectionTextureEnabled&&e._reflectionTexture&&e._reflectionTexture.isRenderTarget&&e._renderTargets.push(e._reflectionTexture),e.subSurface.fillRenderTargetTextures(e._renderTargets),e._renderTargets},e._environmentBRDFTexture=ii(d),e.subSurface=new oi(e._markAllSubMeshesAsTexturesDirty.bind(e),e._markScenePrePassDirty.bind(e),d),e.prePassConfiguration=new pi.a,e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"realTimeFiltering",{get:function(){return this._realTimeFiltering},set:function(a){this._realTimeFiltering=a,this.markAsDirty(r.a.MATERIAL_TextureDirtyFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"realTimeFilteringQuality",{get:function(){return this._realTimeFilteringQuality},set:function(a){this._realTimeFilteringQuality=a,this.markAsDirty(r.a.MATERIAL_TextureDirtyFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"canRenderToMRT",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype._attachImageProcessingConfiguration=function(a){var b=this;a!==this._imageProcessingConfiguration&&(this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),this._imageProcessingConfiguration=a||this.getScene().imageProcessingConfiguration,this._imageProcessingConfiguration&&(this._imageProcessingObserver=this._imageProcessingConfiguration.onUpdateParameters.add(function(){b._markAllSubMeshesAsImageProcessingDirty()})))},Object.defineProperty(b.prototype,"hasRenderTargetTextures",{get:function(){return!!(ai.a.ReflectionTextureEnabled&&this._reflectionTexture&&this._reflectionTexture.isRenderTarget)||this.subSurface.hasRenderTargetTextures()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"isPrePassCapable",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PBRBaseMaterial"},Object.defineProperty(b.prototype,"useLogarithmicDepth",{get:function(){return this._useLogarithmicDepth},set:function(a){this._useLogarithmicDepth=a&&this.getScene().getEngine().getCaps().fragmentDepthSupported},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"_disableAlphaBlending",{get:function(){return this.subSurface.disableAlphaBlending||this._transparencyMode===b.PBRMATERIAL_OPAQUE||this._transparencyMode===b.PBRMATERIAL_ALPHATEST},enumerable:!1,configurable:!0}),b.prototype.needAlphaBlending=function(){return!this._disableAlphaBlending&&(this.alpha<1||null!=this._opacityTexture||this._shouldUseAlphaFromAlbedoTexture())},b.prototype.needAlphaTesting=function(){return!!this._forceAlphaTest||!this.subSurface.disableAlphaBlending&&this._hasAlphaChannel()&&(null==this._transparencyMode||this._transparencyMode===b.PBRMATERIAL_ALPHATEST)},b.prototype._shouldUseAlphaFromAlbedoTexture=function(){return null!=this._albedoTexture&&this._albedoTexture.hasAlpha&&this._useAlphaFromAlbedoTexture&&this._transparencyMode!==b.PBRMATERIAL_OPAQUE},b.prototype._hasAlphaChannel=function(){return null!=this._albedoTexture&&this._albedoTexture.hasAlpha||null!=this._opacityTexture},b.prototype.getAlphaTestTexture=function(){return this._albedoTexture},b.prototype.isReadyForSubMesh=function(a,b,c){if(b.effect&&this.isFrozen&&b.effect._wasPreviouslyReady)return!0;b._materialDefines||(b.materialDefines=new ti);var d=b._materialDefines;if(this._isReadyForSubMesh(b))return!0;var e=this.getScene(),f=e.getEngine();if(d._areTexturesDirty&&e.texturesEnabled){if(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&!this._albedoTexture.isReadyOrNotBlocking())return!1;if(this._ambientTexture&&ai.a.AmbientTextureEnabled&&!this._ambientTexture.isReadyOrNotBlocking())return!1;if(this._opacityTexture&&ai.a.OpacityTextureEnabled&&!this._opacityTexture.isReadyOrNotBlocking())return!1;var g=this._getReflectionTexture();if(g&&ai.a.ReflectionTextureEnabled){if(!g.isReadyOrNotBlocking())return!1;if(g.irradianceTexture&&!g.irradianceTexture.isReadyOrNotBlocking())return!1}if(this._lightmapTexture&&ai.a.LightmapTextureEnabled&&!this._lightmapTexture.isReadyOrNotBlocking())return!1;if(this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&!this._emissiveTexture.isReadyOrNotBlocking())return!1;if(ai.a.SpecularTextureEnabled){if(this._metallicTexture){if(!this._metallicTexture.isReadyOrNotBlocking())return!1}else if(this._reflectivityTexture&&!this._reflectivityTexture.isReadyOrNotBlocking())return!1;if(this._metallicReflectanceTexture&&!this._metallicReflectanceTexture.isReadyOrNotBlocking())return!1;if(this._reflectanceTexture&&!this._reflectanceTexture.isReadyOrNotBlocking())return!1;if(this._microSurfaceTexture&&!this._microSurfaceTexture.isReadyOrNotBlocking())return!1}if(f.getCaps().standardDerivatives&&this._bumpTexture&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&!this._bumpTexture.isReady())return!1;if(this._environmentBRDFTexture&&ai.a.ReflectionTextureEnabled&&!this._environmentBRDFTexture.isReady())return!1}if(!(this.subSurface.isReadyForSubMesh(d,e)&&this.clearCoat.isReadyForSubMesh(d,e,f,this._disableBumpMap)&&this.sheen.isReadyForSubMesh(d,e)&&this.anisotropy.isReadyForSubMesh(d,e)&&this.detailMap.isReadyForSubMesh(d,e)))return!1;if(d._areImageProcessingDirty&&this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.isReady())return!1;f.getCaps().standardDerivatives||a.isVerticesDataPresent(W.b.NormalKind)||(a.createNormals(!0),q.a.Warn("PBRMaterial: Normals have been created for the mesh: "+a.name));g=b.effect;f=d._areLightsDisposed;a=this._prepareEffect(a,d,this.onCompiled,this.onError,c,null,b.getRenderingMesh().hasThinInstances);if(a)if(this._onEffectCreatedObservable&&(si.effect=a,si.subMesh=b,this._onEffectCreatedObservable.notifyObservers(si)),this.allowShaderHotSwapping&&g&&!a.isReady()){if(a=g,d.markAsUnprocessed(),f)return d._areLightsDisposed=!0,!1}else e.resetCachedMaterial(),b.setEffect(a,d,this._materialContext),this.buildUniformLayout();return!(!b.effect||!b.effect.isReady())&&(d._renderId=e.getRenderId(),b.effect._wasPreviouslyReady=!0,!0)},b.prototype.isMetallicWorkflow=function(){return!(null==this._metallic&&null==this._roughness&&!this._metallicTexture)},b.prototype._prepareEffect=function(a,b,c,d,e,f,g){if(void 0===c&&(c=null),void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),this._prepareDefines(a,b,e,f,g),!b.isDirty)return null;b.markAsProcessed();e=this.getScene().getEngine();f=new bi.a;g=0;b.USESPHERICALINVERTEX&&f.addFallback(g++,"USESPHERICALINVERTEX"),b.FOG&&f.addFallback(g,"FOG"),b.SPECULARAA&&f.addFallback(g,"SPECULARAA"),b.POINTSIZE&&f.addFallback(g,"POINTSIZE"),b.LOGARITHMICDEPTH&&f.addFallback(g,"LOGARITHMICDEPTH"),b.PARALLAX&&f.addFallback(g,"PARALLAX"),b.PARALLAXOCCLUSION&&f.addFallback(g++,"PARALLAXOCCLUSION"),g=li.AddFallbacks(b,f,g),g=li.AddFallbacks(b,f,g),g=oi.AddFallbacks(b,f,g),g=ni.AddFallbacks(b,f,g),b.ENVIRONMENTBRDF&&f.addFallback(g++,"ENVIRONMENTBRDF"),b.TANGENT&&f.addFallback(g++,"TANGENT"),b.BUMP&&f.addFallback(g++,"BUMP"),g=Yh.a.HandleFallbacksForShadows(b,f,this._maxSimultaneousLights,g++),b.SPECULARTERM&&f.addFallback(g++,"SPECULARTERM"),b.USESPHERICALFROMREFLECTIONMAP&&f.addFallback(g++,"USESPHERICALFROMREFLECTIONMAP"),b.USEIRRADIANCEMAP&&f.addFallback(g++,"USEIRRADIANCEMAP"),b.LIGHTMAP&&f.addFallback(g++,"LIGHTMAP"),b.NORMAL&&f.addFallback(g++,"NORMAL"),b.AMBIENT&&f.addFallback(g++,"AMBIENT"),b.EMISSIVE&&f.addFallback(g++,"EMISSIVE"),b.VERTEXCOLOR&&f.addFallback(g++,"VERTEXCOLOR"),b.MORPHTARGETS&&f.addFallback(g++,"MORPHTARGETS"),b.MULTIVIEW&&f.addFallback(0,"MULTIVIEW");g=[W.b.PositionKind];b.NORMAL&&g.push(W.b.NormalKind),b.TANGENT&&g.push(W.b.TangentKind);for(var h=1;h<=r.a.MAX_SUPPORTED_UV_SETS;++h)b["UV"+h]&&g.push("uv"+(1===h?"":h));b.VERTEXCOLOR&&g.push(W.b.ColorKind),Yh.a.PrepareAttributesForBones(g,a,b,f),Yh.a.PrepareAttributesForInstances(g,b),Yh.a.PrepareAttributesForMorphTargets(g,a,b);h="pbr";a=["world","view","viewProjection","vEyePosition","vLightsType","vAmbientColor","vAlbedoColor","vReflectivityColor","vMetallicReflectanceFactors","vEmissiveColor","visibility","vReflectionColor","vFogInfos","vFogColor","pointSize","vAlbedoInfos","vAmbientInfos","vOpacityInfos","vReflectionInfos","vReflectionPosition","vReflectionSize","vEmissiveInfos","vReflectivityInfos","vReflectionFilteringInfo","vMetallicReflectanceInfos","vReflectanceInfos","vMicroSurfaceSamplerInfos","vBumpInfos","vLightmapInfos","mBones","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","albedoMatrix","ambientMatrix","opacityMatrix","reflectionMatrix","emissiveMatrix","reflectivityMatrix","normalMatrix","microSurfaceSamplerMatrix","bumpMatrix","lightmapMatrix","metallicReflectanceMatrix","reflectanceMatrix","vLightingIntensity","logarithmicDepthConstant","vSphericalX","vSphericalY","vSphericalZ","vSphericalXX_ZZ","vSphericalYY_ZZ","vSphericalZZ","vSphericalXY","vSphericalYZ","vSphericalZX","vSphericalL00","vSphericalL1_1","vSphericalL10","vSphericalL11","vSphericalL2_2","vSphericalL2_1","vSphericalL20","vSphericalL21","vSphericalL22","vReflectionMicrosurfaceInfos","vTangentSpaceParams","boneTextureWidth","vDebugMode","morphTargetTextureInfo","morphTargetTextureIndices"];var i=["albedoSampler","reflectivitySampler","ambientSampler","emissiveSampler","bumpSampler","lightmapSampler","opacitySampler","reflectionSampler","reflectionSamplerLow","reflectionSamplerHigh","irradianceSampler","microSurfaceSampler","environmentBrdfSampler","boneSampler","metallicReflectanceSampler","reflectanceSampler","morphTargets","oitDepthSampler","oitFrontColorSampler"],j=["Material","Scene","Mesh"];ri.a.AddUniforms(a),ri.a.AddSamplers(i),oi.AddUniforms(a),oi.AddSamplers(i),ki.AddUniforms(a),ki.AddSamplers(i),li.AddUniforms(a),li.AddSamplers(i),ni.AddUniforms(a),ni.AddSamplers(i),pi.a.AddUniforms(a),pi.a.AddSamplers(i),od.a&&(od.a.PrepareUniforms(a,b),od.a.PrepareSamplers(i,b)),Yh.a.PrepareUniformsAndSamplersList({uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:b,maxSimultaneousLights:this._maxSimultaneousLights});var k={};this.customShaderNameResolve&&(h=this.customShaderNameResolve(h,a,j,i,b,g,k));var l=b.toString();return e.createEffect(h,{attributes:g,uniformsNames:a,uniformBuffersNames:j,samplers:i,defines:l,fallbacks:f,onCompiled:c,onError:d,indexParameters:{maxSimultaneousLights:this._maxSimultaneousLights,maxSimultaneousMorphTargets:b.NUM_MORPH_INFLUENCERS},processFinalCode:k.processFinalCode,multiTarget:b.PREPASS},e)},b.prototype._prepareDefines=function(a,c,d,e,f){void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=!1);var g=this.getScene(),h=g.getEngine();Yh.a.PrepareDefinesForLights(g,a,c,!0,this._maxSimultaneousLights,this._disableLighting),c._needNormals=!0,Yh.a.PrepareDefinesForMultiview(g,c);var i=this.needAlphaBlendingForMesh(a)&&this.getScene().useOrderIndependentTransparency;if(Yh.a.PrepareDefinesForPrePass(g,c,this.canRenderToMRT&&!i),Yh.a.PrepareDefinesForOIT(g,c,i),c.METALLICWORKFLOW=this.isMetallicWorkflow(),c._areTexturesDirty){if(c._needUVs=!1,g.texturesEnabled){g.getEngine().getCaps().textureLOD&&(c.LODBASEDMICROSFURACE=!0),this._albedoTexture&&ai.a.DiffuseTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._albedoTexture,c,"ALBEDO"),c.GAMMAALBEDO=this._albedoTexture.gammaSpace):c.ALBEDO=!1,this._ambientTexture&&ai.a.AmbientTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._ambientTexture,c,"AMBIENT"),c.AMBIENTINGRAYSCALE=this._useAmbientInGrayScale):c.AMBIENT=!1,this._opacityTexture&&ai.a.OpacityTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._opacityTexture,c,"OPACITY"),c.OPACITYRGB=this._opacityTexture.getAlphaFromRGB):c.OPACITY=!1;i=this._getReflectionTexture();if(i&&ai.a.ReflectionTextureEnabled){switch(c.REFLECTION=!0,c.GAMMAREFLECTION=i.gammaSpace,c.RGBDREFLECTION=i.isRGBD,c.REFLECTIONMAP_OPPOSITEZ=this.getScene().useRightHandedSystem?!i.invertZ:i.invertZ,c.LODINREFLECTIONALPHA=i.lodLevelInAlpha,c.LINEARSPECULARREFLECTION=i.linearSpecularLOD,this.realTimeFiltering&&this.realTimeFilteringQuality>0?(c.NUM_SAMPLES=""+this.realTimeFilteringQuality,h._features.needTypeSuffixInShaderConstants&&(c.NUM_SAMPLES=c.NUM_SAMPLES+"u"),c.REALTIME_FILTERING=!0):c.REALTIME_FILTERING=!1,i.coordinatesMode===U.a.INVCUBIC_MODE&&(c.INVERTCUBICMAP=!0),c.REFLECTIONMAP_3D=i.isCube,c.REFLECTIONMAP_CUBIC=!1,c.REFLECTIONMAP_EXPLICIT=!1,c.REFLECTIONMAP_PLANAR=!1,c.REFLECTIONMAP_PROJECTION=!1,c.REFLECTIONMAP_SKYBOX=!1,c.REFLECTIONMAP_SPHERICAL=!1,c.REFLECTIONMAP_EQUIRECTANGULAR=!1,c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,i.coordinatesMode){case U.a.EXPLICIT_MODE:c.REFLECTIONMAP_EXPLICIT=!0;break;case U.a.PLANAR_MODE:c.REFLECTIONMAP_PLANAR=!0;break;case U.a.PROJECTION_MODE:c.REFLECTIONMAP_PROJECTION=!0;break;case U.a.SKYBOX_MODE:c.REFLECTIONMAP_SKYBOX=!0;break;case U.a.SPHERICAL_MODE:c.REFLECTIONMAP_SPHERICAL=!0;break;case U.a.EQUIRECTANGULAR_MODE:c.REFLECTIONMAP_EQUIRECTANGULAR=!0;break;case U.a.FIXED_EQUIRECTANGULAR_MODE:c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!0;break;case U.a.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!0;break;case U.a.CUBIC_MODE:case U.a.INVCUBIC_MODE:default:c.REFLECTIONMAP_CUBIC=!0,c.USE_LOCAL_REFLECTIONMAP_CUBIC=!!i.boundingBoxSize}i.coordinatesMode!==U.a.SKYBOX_MODE&&(i.irradianceTexture?(c.USEIRRADIANCEMAP=!0,c.USESPHERICALFROMREFLECTIONMAP=!1):i.isCube&&(c.USESPHERICALFROMREFLECTIONMAP=!0,c.USEIRRADIANCEMAP=!1,this._forceIrradianceInFragment||this.realTimeFiltering||g.getEngine().getCaps().maxVaryingVectors<=8?c.USESPHERICALINVERTEX=!1:c.USESPHERICALINVERTEX=!0))}else c.REFLECTION=!1,c.REFLECTIONMAP_3D=!1,c.REFLECTIONMAP_SPHERICAL=!1,c.REFLECTIONMAP_PLANAR=!1,c.REFLECTIONMAP_CUBIC=!1,c.USE_LOCAL_REFLECTIONMAP_CUBIC=!1,c.REFLECTIONMAP_PROJECTION=!1,c.REFLECTIONMAP_SKYBOX=!1,c.REFLECTIONMAP_EXPLICIT=!1,c.REFLECTIONMAP_EQUIRECTANGULAR=!1,c.REFLECTIONMAP_EQUIRECTANGULAR_FIXED=!1,c.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED=!1,c.INVERTCUBICMAP=!1,c.USESPHERICALFROMREFLECTIONMAP=!1,c.USEIRRADIANCEMAP=!1,c.USESPHERICALINVERTEX=!1,c.REFLECTIONMAP_OPPOSITEZ=!1,c.LODINREFLECTIONALPHA=!1,c.GAMMAREFLECTION=!1,c.RGBDREFLECTION=!1,c.LINEARSPECULARREFLECTION=!1;if(this._lightmapTexture&&ai.a.LightmapTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._lightmapTexture,c,"LIGHTMAP"),c.USELIGHTMAPASSHADOWMAP=this._useLightmapAsShadowmap,c.GAMMALIGHTMAP=this._lightmapTexture.gammaSpace,c.RGBDLIGHTMAP=this._lightmapTexture.isRGBD):c.LIGHTMAP=!1,this._emissiveTexture&&ai.a.EmissiveTextureEnabled?(Yh.a.PrepareDefinesForMergedUV(this._emissiveTexture,c,"EMISSIVE"),c.GAMMAEMISSIVE=this._emissiveTexture.gammaSpace):c.EMISSIVE=!1,ai.a.SpecularTextureEnabled){if(this._metallicTexture?(Yh.a.PrepareDefinesForMergedUV(this._metallicTexture,c,"REFLECTIVITY"),c.ROUGHNESSSTOREINMETALMAPALPHA=this._useRoughnessFromMetallicTextureAlpha,c.ROUGHNESSSTOREINMETALMAPGREEN=!this._useRoughnessFromMetallicTextureAlpha&&this._useRoughnessFromMetallicTextureGreen,c.METALLNESSSTOREINMETALMAPBLUE=this._useMetallnessFromMetallicTextureBlue,c.AOSTOREINMETALMAPRED=this._useAmbientOcclusionFromMetallicTextureRed,c.REFLECTIVITY_GAMMA=!1):this._reflectivityTexture?(Yh.a.PrepareDefinesForMergedUV(this._reflectivityTexture,c,"REFLECTIVITY"),c.MICROSURFACEFROMREFLECTIVITYMAP=this._useMicroSurfaceFromReflectivityMapAlpha,c.MICROSURFACEAUTOMATIC=this._useAutoMicroSurfaceFromReflectivityMap,c.REFLECTIVITY_GAMMA=this._reflectivityTexture.gammaSpace):c.REFLECTIVITY=!1,this._metallicReflectanceTexture||this._reflectanceTexture){i=null!==this._metallicReflectanceTexture&&this._metallicReflectanceTexture._texture===(null===(i=this._reflectanceTexture)||void 0===i?void 0:i._texture)&&this._metallicReflectanceTexture.checkTransformsAreIdentical(this._reflectanceTexture);c.METALLIC_REFLECTANCE_USE_ALPHA_ONLY=this._useOnlyMetallicFromMetallicReflectanceTexture&&!i,this._metallicReflectanceTexture?(Yh.a.PrepareDefinesForMergedUV(this._metallicReflectanceTexture,c,"METALLIC_REFLECTANCE"),c.METALLIC_REFLECTANCE_GAMMA=this._metallicReflectanceTexture.gammaSpace):c.METALLIC_REFLECTANCE=!1,this._reflectanceTexture&&!i&&(!this._metallicReflectanceTexture||this._metallicReflectanceTexture&&this._useOnlyMetallicFromMetallicReflectanceTexture)?(Yh.a.PrepareDefinesForMergedUV(this._reflectanceTexture,c,"REFLECTANCE"),c.REFLECTANCE_GAMMA=this._reflectanceTexture.gammaSpace):c.REFLECTANCE=!1}else c.METALLIC_REFLECTANCE=!1,c.REFLECTANCE=!1;this._microSurfaceTexture?Yh.a.PrepareDefinesForMergedUV(this._microSurfaceTexture,c,"MICROSURFACEMAP"):c.MICROSURFACEMAP=!1}else c.REFLECTIVITY=!1,c.MICROSURFACEMAP=!1;g.getEngine().getCaps().standardDerivatives&&this._bumpTexture&&ai.a.BumpTextureEnabled&&!this._disableBumpMap?(Yh.a.PrepareDefinesForMergedUV(this._bumpTexture,c,"BUMP"),this._useParallax&&this._albedoTexture&&ai.a.DiffuseTextureEnabled?(c.PARALLAX=!0,c.PARALLAXOCCLUSION=!!this._useParallaxOcclusion):c.PARALLAX=!1,c.OBJECTSPACE_NORMALMAP=this._useObjectSpaceNormalMap):c.BUMP=!1,this._environmentBRDFTexture&&ai.a.ReflectionTextureEnabled?(c.ENVIRONMENTBRDF=!0,c.ENVIRONMENTBRDF_RGBD=this._environmentBRDFTexture.isRGBD):(c.ENVIRONMENTBRDF=!1,c.ENVIRONMENTBRDF_RGBD=!1),this._shouldUseAlphaFromAlbedoTexture()?c.ALPHAFROMALBEDO=!0:c.ALPHAFROMALBEDO=!1}c.SPECULAROVERALPHA=this._useSpecularOverAlpha,this._lightFalloff===b.LIGHTFALLOFF_STANDARD?(c.USEPHYSICALLIGHTFALLOFF=!1,c.USEGLTFLIGHTFALLOFF=!1):this._lightFalloff===b.LIGHTFALLOFF_GLTF?(c.USEPHYSICALLIGHTFALLOFF=!1,c.USEGLTFLIGHTFALLOFF=!0):(c.USEPHYSICALLIGHTFALLOFF=!0,c.USEGLTFLIGHTFALLOFF=!1),c.RADIANCEOVERALPHA=this._useRadianceOverAlpha,!this.backFaceCulling&&this._twoSidedLighting?c.TWOSIDEDLIGHTING=!0:c.TWOSIDEDLIGHTING=!1,c.SPECULARAA=g.getEngine().getCaps().standardDerivatives&&this._enableSpecularAntiAliasing}(c._areTexturesDirty||c._areMiscDirty)&&(c.ALPHATESTVALUE=this._alphaCutOff+(this._alphaCutOff%1==0?".":""),c.PREMULTIPLYALPHA=this.alphaMode===r.a.ALPHA_PREMULTIPLIED||this.alphaMode===r.a.ALPHA_PREMULTIPLIED_PORTERDUFF,c.ALPHABLEND=this.needAlphaBlendingForMesh(a),c.ALPHAFRESNEL=this._useAlphaFresnel||this._useLinearAlphaFresnel,c.LINEARALPHAFRESNEL=this._useLinearAlphaFresnel),c._areImageProcessingDirty&&this._imageProcessingConfiguration&&this._imageProcessingConfiguration.prepareDefines(c),c.FORCENORMALFORWARD=this._forceNormalForward,c.RADIANCEOCCLUSION=this._useRadianceOcclusion,c.HORIZONOCCLUSION=this._useHorizonOcclusion,c._areMiscDirty&&(Yh.a.PrepareDefinesForMisc(a,g,this._useLogarithmicDepth,this.pointsCloud,this.fogEnabled,this._shouldTurnAlphaTestOn(a)||this._forceAlphaTest,c),c.UNLIT=this._unlit||(this.pointsCloud||this.wireframe)&&!a.isVerticesDataPresent(W.b.NormalKind),c.DEBUGMODE=this._debugMode),this.detailMap.prepareDefines(c,g),this.subSurface.prepareDefines(c,g),this.clearCoat.prepareDefines(c,g),this.anisotropy.prepareDefines(c,a,g),this.brdf.prepareDefines(c),this.sheen.prepareDefines(c,g),Yh.a.PrepareDefinesForFrameBoundValues(g,h,c,!!d,e,f),Yh.a.PrepareDefinesForAttributes(a,c,!0,!0,!0,this._transparencyMode!==b.PBRMATERIAL_OPAQUE)},b.prototype.forceCompilation=function(a,b,c){var d=this;c=Object(l.a)({clipPlane:!1,useInstances:!1},c);var e=new ti;e=this._prepareEffect(a,e,void 0,void 0,c.useInstances,c.clipPlane,a.hasThinInstances);this._onEffectCreatedObservable&&(si.effect=e,si.subMesh=null,this._onEffectCreatedObservable.notifyObservers(si)),e.isReady()?b&&b(this):e.onCompileObservable.add(function(){b&&b(d)})},b.prototype.buildUniformLayout=function(){var a=this._uniformBuffer;a.addUniform("vAlbedoInfos",2),a.addUniform("vAmbientInfos",4),a.addUniform("vOpacityInfos",2),a.addUniform("vEmissiveInfos",2),a.addUniform("vLightmapInfos",2),a.addUniform("vReflectivityInfos",3),a.addUniform("vMicroSurfaceSamplerInfos",2),a.addUniform("vReflectionInfos",2),a.addUniform("vReflectionFilteringInfo",2),a.addUniform("vReflectionPosition",3),a.addUniform("vReflectionSize",3),a.addUniform("vBumpInfos",3),a.addUniform("albedoMatrix",16),a.addUniform("ambientMatrix",16),a.addUniform("opacityMatrix",16),a.addUniform("emissiveMatrix",16),a.addUniform("lightmapMatrix",16),a.addUniform("reflectivityMatrix",16),a.addUniform("microSurfaceSamplerMatrix",16),a.addUniform("bumpMatrix",16),a.addUniform("vTangentSpaceParams",2),a.addUniform("reflectionMatrix",16),a.addUniform("vReflectionColor",3),a.addUniform("vAlbedoColor",4),a.addUniform("vLightingIntensity",4),a.addUniform("vReflectionMicrosurfaceInfos",3),a.addUniform("pointSize",1),a.addUniform("vReflectivityColor",4),a.addUniform("vEmissiveColor",3),a.addUniform("vAmbientColor",3),a.addUniform("vDebugMode",2),a.addUniform("vMetallicReflectanceFactors",4),a.addUniform("vMetallicReflectanceInfos",2),a.addUniform("metallicReflectanceMatrix",16),a.addUniform("vReflectanceInfos",2),a.addUniform("reflectanceMatrix",16),ki.PrepareUniformBuffer(a),li.PrepareUniformBuffer(a),ni.PrepareUniformBuffer(a),oi.PrepareUniformBuffer(a),ri.a.PrepareUniformBuffer(a),a.addUniform("vSphericalL00",3),a.addUniform("vSphericalL1_1",3),a.addUniform("vSphericalL10",3),a.addUniform("vSphericalL11",3),a.addUniform("vSphericalL2_2",3),a.addUniform("vSphericalL2_1",3),a.addUniform("vSphericalL20",3),a.addUniform("vSphericalL21",3),a.addUniform("vSphericalL22",3),a.addUniform("vSphericalX",3),a.addUniform("vSphericalY",3),a.addUniform("vSphericalZ",3),a.addUniform("vSphericalXX_ZZ",3),a.addUniform("vSphericalYY_ZZ",3),a.addUniform("vSphericalZZ",3),a.addUniform("vSphericalXY",3),a.addUniform("vSphericalYZ",3),a.addUniform("vSphericalZX",3),a.create()},b.prototype.unbind=function(){if(this._activeEffect){var b=!1;this._reflectionTexture&&this._reflectionTexture.isRenderTarget&&(this._activeEffect.setTexture("reflection2DSampler",null),b=!0),this.subSurface.unbind(this._activeEffect)&&(b=!0),b&&this._markAllSubMeshesAsTexturesDirty()}a.prototype.unbind.call(this)},b.prototype.bindForSubMesh=function(a,b,c){var d=this.getScene(),e=c._materialDefines;if(e){var f=c.effect;if(f){this._activeEffect=f,b.getMeshUniformBuffer().bindToEffect(f,"Mesh"),b.transferToEffect(a),this.prePassConfiguration.bindForSubMesh(this._activeEffect,d,b,a,this.isFrozen),e.OBJECTSPACE_NORMALMAP&&(a.toNormalMatrix(this._normalMatrix),this.bindOnlyNormalMatrix(this._normalMatrix));a=this._mustRebind(d,f,b.visibility);Yh.a.BindBonesParameters(b,this._activeEffect,this.prePassConfiguration);var g=null,i=this._uniformBuffer;if(a){var j=d.getEngine();if(i.bindToEffect(f,"Material"),this.bindViewProjection(f),g=this._getReflectionTexture(),!i.useUbo||!this.isFrozen||!i.isSync){if(d.texturesEnabled){if(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&(i.updateFloat2("vAlbedoInfos",this._albedoTexture.coordinatesIndex,this._albedoTexture.level),Yh.a.BindTextureMatrix(this._albedoTexture,i,"albedo")),this._ambientTexture&&ai.a.AmbientTextureEnabled&&(i.updateFloat4("vAmbientInfos",this._ambientTexture.coordinatesIndex,this._ambientTexture.level,this._ambientTextureStrength,this._ambientTextureImpactOnAnalyticalLights),Yh.a.BindTextureMatrix(this._ambientTexture,i,"ambient")),this._opacityTexture&&ai.a.OpacityTextureEnabled&&(i.updateFloat2("vOpacityInfos",this._opacityTexture.coordinatesIndex,this._opacityTexture.level),Yh.a.BindTextureMatrix(this._opacityTexture,i,"opacity")),g&&ai.a.ReflectionTextureEnabled){if(i.updateMatrix("reflectionMatrix",g.getReflectionTextureMatrix()),i.updateFloat2("vReflectionInfos",g.level,0),g.boundingBoxSize){var k=g;i.updateVector3("vReflectionPosition",k.boundingBoxPosition),i.updateVector3("vReflectionSize",k.boundingBoxSize)}if(this.realTimeFiltering){k=g.getSize().width;i.updateFloat2("vReflectionFilteringInfo",k,H.a.Log2(k))}if(!e.USEIRRADIANCEMAP){k=g.sphericalPolynomial;if(e.USESPHERICALFROMREFLECTIONMAP&&k)if(e.SPHERICAL_HARMONICS){var l=k.preScaledHarmonics;i.updateVector3("vSphericalL00",l.l00),i.updateVector3("vSphericalL1_1",l.l1_1),i.updateVector3("vSphericalL10",l.l10),i.updateVector3("vSphericalL11",l.l11),i.updateVector3("vSphericalL2_2",l.l2_2),i.updateVector3("vSphericalL2_1",l.l2_1),i.updateVector3("vSphericalL20",l.l20),i.updateVector3("vSphericalL21",l.l21),i.updateVector3("vSphericalL22",l.l22)}else i.updateFloat3("vSphericalX",k.x.x,k.x.y,k.x.z),i.updateFloat3("vSphericalY",k.y.x,k.y.y,k.y.z),i.updateFloat3("vSphericalZ",k.z.x,k.z.y,k.z.z),i.updateFloat3("vSphericalXX_ZZ",k.xx.x-k.zz.x,k.xx.y-k.zz.y,k.xx.z-k.zz.z),i.updateFloat3("vSphericalYY_ZZ",k.yy.x-k.zz.x,k.yy.y-k.zz.y,k.yy.z-k.zz.z),i.updateFloat3("vSphericalZZ",k.zz.x,k.zz.y,k.zz.z),i.updateFloat3("vSphericalXY",k.xy.x,k.xy.y,k.xy.z),i.updateFloat3("vSphericalYZ",k.yz.x,k.yz.y,k.yz.z),i.updateFloat3("vSphericalZX",k.zx.x,k.zx.y,k.zx.z)}i.updateFloat3("vReflectionMicrosurfaceInfos",g.getSize().width,g.lodGenerationScale,g.lodGenerationOffset)}this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&(i.updateFloat2("vEmissiveInfos",this._emissiveTexture.coordinatesIndex,this._emissiveTexture.level),Yh.a.BindTextureMatrix(this._emissiveTexture,i,"emissive")),this._lightmapTexture&&ai.a.LightmapTextureEnabled&&(i.updateFloat2("vLightmapInfos",this._lightmapTexture.coordinatesIndex,this._lightmapTexture.level),Yh.a.BindTextureMatrix(this._lightmapTexture,i,"lightmap")),ai.a.SpecularTextureEnabled&&(this._metallicTexture?(i.updateFloat3("vReflectivityInfos",this._metallicTexture.coordinatesIndex,this._metallicTexture.level,this._ambientTextureStrength),Yh.a.BindTextureMatrix(this._metallicTexture,i,"reflectivity")):this._reflectivityTexture&&(i.updateFloat3("vReflectivityInfos",this._reflectivityTexture.coordinatesIndex,this._reflectivityTexture.level,1),Yh.a.BindTextureMatrix(this._reflectivityTexture,i,"reflectivity")),this._metallicReflectanceTexture&&(i.updateFloat2("vMetallicReflectanceInfos",this._metallicReflectanceTexture.coordinatesIndex,this._metallicReflectanceTexture.level),Yh.a.BindTextureMatrix(this._metallicReflectanceTexture,i,"metallicReflectance")),this._reflectanceTexture&&e.REFLECTANCE&&(i.updateFloat2("vReflectanceInfos",this._reflectanceTexture.coordinatesIndex,this._reflectanceTexture.level),Yh.a.BindTextureMatrix(this._reflectanceTexture,i,"reflectance")),this._microSurfaceTexture&&(i.updateFloat2("vMicroSurfaceSamplerInfos",this._microSurfaceTexture.coordinatesIndex,this._microSurfaceTexture.level),Yh.a.BindTextureMatrix(this._microSurfaceTexture,i,"microSurfaceSampler"))),this._bumpTexture&&j.getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&(i.updateFloat3("vBumpInfos",this._bumpTexture.coordinatesIndex,this._bumpTexture.level,this._parallaxScaleBias),Yh.a.BindTextureMatrix(this._bumpTexture,i,"bump"),d._mirroredCameraPosition?i.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?1:-1,this._invertNormalMapY?1:-1):i.updateFloat2("vTangentSpaceParams",this._invertNormalMapX?-1:1,this._invertNormalMapY?-1:1))}if(this.pointsCloud&&i.updateFloat("pointSize",this.pointSize),e.METALLICWORKFLOW){h.c.Color3[0].r=void 0===this._metallic||null===this._metallic?1:this._metallic,h.c.Color3[0].g=void 0===this._roughness||null===this._roughness?1:this._roughness,i.updateColor4("vReflectivityColor",h.c.Color3[0],1);l=this.subSurface.indexOfRefraction;k=Math.pow((l-1)/(l+1),2);this._metallicReflectanceColor.scaleToRef(k*this._metallicF0Factor,h.c.Color3[0]);l=this._metallicF0Factor;i.updateColor4("vMetallicReflectanceFactors",h.c.Color3[0],l)}else i.updateColor4("vReflectivityColor",this._reflectivityColor,this._microSurface);i.updateColor3("vEmissiveColor",ai.a.EmissiveTextureEnabled?this._emissiveColor:h.a.BlackReadOnly),i.updateColor3("vReflectionColor",this._reflectionColor),!e.SS_REFRACTION&&this.subSurface.linkRefractionWithTransparency?i.updateColor4("vAlbedoColor",this._albedoColor,1):i.updateColor4("vAlbedoColor",this._albedoColor,this.alpha),this._lightingInfos.x=this._directIntensity,this._lightingInfos.y=this._emissiveIntensity,this._lightingInfos.z=this._environmentIntensity*d.environmentIntensity,this._lightingInfos.w=this._specularIntensity,i.updateVector4("vLightingIntensity",this._lightingInfos),d.ambientColor.multiplyToRef(this._ambientColor,this._globalAmbientColor),i.updateColor3("vAmbientColor",this._globalAmbientColor),i.updateFloat2("vDebugMode",this.debugLimit,this.debugFactor)}d.texturesEnabled&&(this._albedoTexture&&ai.a.DiffuseTextureEnabled&&i.setTexture("albedoSampler",this._albedoTexture),this._ambientTexture&&ai.a.AmbientTextureEnabled&&i.setTexture("ambientSampler",this._ambientTexture),this._opacityTexture&&ai.a.OpacityTextureEnabled&&i.setTexture("opacitySampler",this._opacityTexture),g&&ai.a.ReflectionTextureEnabled&&(e.LODBASEDMICROSFURACE?i.setTexture("reflectionSampler",g):(i.setTexture("reflectionSampler",g._lodTextureMid||g),i.setTexture("reflectionSamplerLow",g._lodTextureLow||g),i.setTexture("reflectionSamplerHigh",g._lodTextureHigh||g)),e.USEIRRADIANCEMAP&&i.setTexture("irradianceSampler",g.irradianceTexture)),e.ENVIRONMENTBRDF&&i.setTexture("environmentBrdfSampler",this._environmentBRDFTexture),this._emissiveTexture&&ai.a.EmissiveTextureEnabled&&i.setTexture("emissiveSampler",this._emissiveTexture),this._lightmapTexture&&ai.a.LightmapTextureEnabled&&i.setTexture("lightmapSampler",this._lightmapTexture),ai.a.SpecularTextureEnabled&&(this._metallicTexture?i.setTexture("reflectivitySampler",this._metallicTexture):this._reflectivityTexture&&i.setTexture("reflectivitySampler",this._reflectivityTexture),this._metallicReflectanceTexture&&i.setTexture("metallicReflectanceSampler",this._metallicReflectanceTexture),this._reflectanceTexture&&e.REFLECTANCE&&i.setTexture("reflectanceSampler",this._reflectanceTexture),this._microSurfaceTexture&&i.setTexture("microSurfaceSampler",this._microSurfaceTexture)),this._bumpTexture&&j.getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&!this._disableBumpMap&&i.setTexture("bumpSampler",this._bumpTexture)),this.getScene().useOrderIndependentTransparency&&this.needAlphaBlendingForMesh(b)&&this.getScene().depthPeelingRenderer.bind(f),this.detailMap.bindForSubMesh(i,d,this.isFrozen),this.subSurface.bindForSubMesh(i,d,j,this.isFrozen,e.LODBASEDMICROSFURACE,this.realTimeFiltering,c),this.clearCoat.bindForSubMesh(i,d,j,this._disableBumpMap,this.isFrozen,this._invertNormalMapX,this._invertNormalMapY,c),this.anisotropy.bindForSubMesh(i,d,this.isFrozen),this.sheen.bindForSubMesh(i,d,this.isFrozen,c),Yh.a.BindClipPlane(this._activeEffect,d),this.bindEyePosition(f)}!a&&this.isFrozen||(d.lightsEnabled&&!this._disableLighting&&Yh.a.BindLights(d,b,this._activeEffect,e,this._maxSimultaneousLights),(d.fogEnabled&&b.applyFog&&d.fogMode!==O.a.FOGMODE_NONE||g||b.receiveShadows)&&this.bindView(f),Yh.a.BindFogParameters(d,b,this._activeEffect,!0),e.NUM_MORPH_INFLUENCERS&&Yh.a.BindMorphTargetParameters(b,this._activeEffect),this._imageProcessingConfiguration.bind(this._activeEffect),Yh.a.BindLogDepth(e,this._activeEffect,d)),this._afterBind(b,this._activeEffect),i.update()}}},b.prototype.getAnimatables=function(){var a=[];return this._albedoTexture&&this._albedoTexture.animations&&this._albedoTexture.animations.length>0&&a.push(this._albedoTexture),this._ambientTexture&&this._ambientTexture.animations&&this._ambientTexture.animations.length>0&&a.push(this._ambientTexture),this._opacityTexture&&this._opacityTexture.animations&&this._opacityTexture.animations.length>0&&a.push(this._opacityTexture),this._reflectionTexture&&this._reflectionTexture.animations&&this._reflectionTexture.animations.length>0&&a.push(this._reflectionTexture),this._emissiveTexture&&this._emissiveTexture.animations&&this._emissiveTexture.animations.length>0&&a.push(this._emissiveTexture),this._metallicTexture&&this._metallicTexture.animations&&this._metallicTexture.animations.length>0?a.push(this._metallicTexture):this._reflectivityTexture&&this._reflectivityTexture.animations&&this._reflectivityTexture.animations.length>0&&a.push(this._reflectivityTexture),this._bumpTexture&&this._bumpTexture.animations&&this._bumpTexture.animations.length>0&&a.push(this._bumpTexture),this._lightmapTexture&&this._lightmapTexture.animations&&this._lightmapTexture.animations.length>0&&a.push(this._lightmapTexture),this.detailMap.getAnimatables(a),this.subSurface.getAnimatables(a),this.clearCoat.getAnimatables(a),this.sheen.getAnimatables(a),this.anisotropy.getAnimatables(a),a},b.prototype._getReflectionTexture=function(){return this._reflectionTexture?this._reflectionTexture:this.getScene().environmentTexture},b.prototype.getActiveTextures=function(){var b=a.prototype.getActiveTextures.call(this);return this._albedoTexture&&b.push(this._albedoTexture),this._ambientTexture&&b.push(this._ambientTexture),this._opacityTexture&&b.push(this._opacityTexture),this._reflectionTexture&&b.push(this._reflectionTexture),this._emissiveTexture&&b.push(this._emissiveTexture),this._reflectivityTexture&&b.push(this._reflectivityTexture),this._metallicTexture&&b.push(this._metallicTexture),this._metallicReflectanceTexture&&b.push(this._metallicReflectanceTexture),this._reflectanceTexture&&b.push(this._reflectanceTexture),this._microSurfaceTexture&&b.push(this._microSurfaceTexture),this._bumpTexture&&b.push(this._bumpTexture),this._lightmapTexture&&b.push(this._lightmapTexture),this.detailMap.getActiveTextures(b),this.subSurface.getActiveTextures(b),this.clearCoat.getActiveTextures(b),this.sheen.getActiveTextures(b),this.anisotropy.getActiveTextures(b),b},b.prototype.hasTexture=function(b){return!!a.prototype.hasTexture.call(this,b)||this._albedoTexture===b||this._ambientTexture===b||this._opacityTexture===b||this._reflectionTexture===b||this._reflectivityTexture===b||this._metallicTexture===b||this._metallicReflectanceTexture===b||this._reflectanceTexture===b||this._microSurfaceTexture===b||this._bumpTexture===b||this._lightmapTexture===b||this.detailMap.hasTexture(b)||this.subSurface.hasTexture(b)||this.clearCoat.hasTexture(b)||this.sheen.hasTexture(b)||this.anisotropy.hasTexture(b)},b.prototype.setPrePassRenderer=function(a){if(this.subSurface.isScatteringEnabled){a=this.getScene().enableSubSurfaceForPrePass();return a&&(a.enabled=!0),!0}return!1},b.prototype.dispose=function(b,c){var d;c&&(this._environmentBRDFTexture&&this.getScene().environmentBRDFTexture!==this._environmentBRDFTexture&&this._environmentBRDFTexture.dispose(),null===(d=this._albedoTexture)||void 0===d||d.dispose(),null===(d=this._ambientTexture)||void 0===d||d.dispose(),null===(d=this._opacityTexture)||void 0===d||d.dispose(),null===(d=this._reflectionTexture)||void 0===d||d.dispose(),null===(d=this._emissiveTexture)||void 0===d||d.dispose(),null===(d=this._metallicTexture)||void 0===d||d.dispose(),null===(d=this._reflectivityTexture)||void 0===d||d.dispose(),null===(d=this._bumpTexture)||void 0===d||d.dispose(),null===(d=this._lightmapTexture)||void 0===d||d.dispose(),null===(d=this._metallicReflectanceTexture)||void 0===d||d.dispose(),null===(d=this._reflectanceTexture)||void 0===d||d.dispose(),null===(d=this._microSurfaceTexture)||void 0===d||d.dispose()),this.detailMap.dispose(c),this.subSurface.dispose(c),this.clearCoat.dispose(c),this.sheen.dispose(c),this.anisotropy.dispose(c),this._renderTargets.dispose(),this._imageProcessingConfiguration&&this._imageProcessingObserver&&this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver),a.prototype.dispose.call(this,b,c)},b.PBRMATERIAL_OPAQUE=qi.a.MATERIAL_OPAQUE,b.PBRMATERIAL_ALPHATEST=qi.a.MATERIAL_ALPHATEST,b.PBRMATERIAL_ALPHABLEND=qi.a.MATERIAL_ALPHABLEND,b.PBRMATERIAL_ALPHATESTANDBLEND=qi.a.MATERIAL_ALPHATESTANDBLEND,b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS=0,b.LIGHTFALLOFF_PHYSICAL=0,b.LIGHTFALLOFF_GLTF=1,b.LIGHTFALLOFF_STANDARD=2,Object(l.c)([Object(I.j)()],b.prototype,"_imageProcessingConfiguration",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsMiscDirty")],b.prototype,"debugMode",void 0),Object(l.c)([Object(I.d)()],b.prototype,"useLogarithmicDepth",null),b}($h.a),vi=function(a){function b(c,d){c=a.call(this,c,d)||this;return c.directIntensity=1,c.emissiveIntensity=1,c.environmentIntensity=1,c.specularIntensity=1,c.disableBumpMap=!1,c.ambientTextureStrength=1,c.ambientTextureImpactOnAnalyticalLights=b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,c.metallicF0Factor=1,c.metallicReflectanceColor=h.a.White(),c.useOnlyMetallicFromMetallicReflectanceTexture=!1,c.ambientColor=new h.a(0,0,0),c.albedoColor=new h.a(1,1,1),c.reflectivityColor=new h.a(1,1,1),c.reflectionColor=new h.a(1,1,1),c.emissiveColor=new h.a(0,0,0),c.microSurface=1,c.useLightmapAsShadowmap=!1,c.useAlphaFromAlbedoTexture=!1,c.forceAlphaTest=!1,c.alphaCutOff=.4,c.useSpecularOverAlpha=!0,c.useMicroSurfaceFromReflectivityMapAlpha=!1,c.useRoughnessFromMetallicTextureAlpha=!0,c.useRoughnessFromMetallicTextureGreen=!1,c.useMetallnessFromMetallicTextureBlue=!1,c.useAmbientOcclusionFromMetallicTextureRed=!1,c.useAmbientInGrayScale=!1,c.useAutoMicroSurfaceFromReflectivityMap=!1,c.useRadianceOverAlpha=!0,c.useObjectSpaceNormalMap=!1,c.useParallax=!1,c.useParallaxOcclusion=!1,c.parallaxScaleBias=.05,c.disableLighting=!1,c.forceIrradianceInFragment=!1,c.maxSimultaneousLights=4,c.invertNormalMapX=!1,c.invertNormalMapY=!1,c.twoSidedLighting=!1,c.useAlphaFresnel=!1,c.useLinearAlphaFresnel=!1,c.environmentBRDFTexture=null,c.forceNormalForward=!1,c.enableSpecularAntiAliasing=!1,c.useHorizonOcclusion=!0,c.useRadianceOcclusion=!0,c.unlit=!1,c._environmentBRDFTexture=ii(d),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"refractionTexture",{get:function(){return this.subSurface.refractionTexture},set:function(a){this.subSurface.refractionTexture=a,a?this.subSurface.isRefractionEnabled=!0:this.subSurface.linkRefractionWithTransparency||(this.subSurface.isRefractionEnabled=!1)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"indexOfRefraction",{get:function(){return this.subSurface.indexOfRefraction},set:function(a){this.subSurface.indexOfRefraction=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"invertRefractionY",{get:function(){return this.subSurface.invertRefractionY},set:function(a){this.subSurface.invertRefractionY=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"linkRefractionWithTransparency",{get:function(){return this.subSurface.linkRefractionWithTransparency},set:function(a){this.subSurface.linkRefractionWithTransparency=a,a&&(this.subSurface.isRefractionEnabled=!0)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"usePhysicalLightFalloff",{get:function(){return this._lightFalloff===ui.LIGHTFALLOFF_PHYSICAL},set:function(a){a!==this.usePhysicalLightFalloff&&(this._markAllSubMeshesAsTexturesDirty(),this._lightFalloff=a?ui.LIGHTFALLOFF_PHYSICAL:ui.LIGHTFALLOFF_STANDARD)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"useGLTFLightFalloff",{get:function(){return this._lightFalloff===ui.LIGHTFALLOFF_GLTF},set:function(a){a!==this.useGLTFLightFalloff&&(this._markAllSubMeshesAsTexturesDirty(),this._lightFalloff=a?ui.LIGHTFALLOFF_GLTF:ui.LIGHTFALLOFF_STANDARD)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a),this._markAllSubMeshesAsTexturesDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurvesEnabled",{get:function(){return this.imageProcessingConfiguration.colorCurvesEnabled},set:function(a){this.imageProcessingConfiguration.colorCurvesEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingEnabled",{get:function(){return this.imageProcessingConfiguration.colorGradingEnabled},set:function(a){this.imageProcessingConfiguration.colorGradingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraToneMappingEnabled",{get:function(){return this._imageProcessingConfiguration.toneMappingEnabled},set:function(a){this._imageProcessingConfiguration.toneMappingEnabled=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraExposure",{get:function(){return this._imageProcessingConfiguration.exposure},set:function(a){this._imageProcessingConfiguration.exposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraContrast",{get:function(){return this._imageProcessingConfiguration.contrast},set:function(a){this._imageProcessingConfiguration.contrast=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorGradingTexture",{get:function(){return this._imageProcessingConfiguration.colorGradingTexture},set:function(a){this._imageProcessingConfiguration.colorGradingTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cameraColorCurves",{get:function(){return this._imageProcessingConfiguration.colorCurves},set:function(a){this._imageProcessingConfiguration.colorCurves=a},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"PBRMaterial"},b.prototype.clone=function(a){var c=this,d=I.a.Clone(function(){return new b(a,c.getScene())},this);return d.id=a,d.name=a,this.stencil.copyTo(d.stencil),this.clearCoat.copyTo(d.clearCoat),this.anisotropy.copyTo(d.anisotropy),this.brdf.copyTo(d.brdf),this.sheen.copyTo(d.sheen),this.subSurface.copyTo(d.subSurface),d},b.prototype.serialize=function(){var a=I.a.Serialize(this);return a.customType="BABYLON.PBRMaterial",a.stencil=this.stencil.serialize(),a.clearCoat=this.clearCoat.serialize(),a.anisotropy=this.anisotropy.serialize(),a.brdf=this.brdf.serialize(),a.sheen=this.sheen.serialize(),a.subSurface=this.subSurface.serialize(),a},b.Parse=function(a,c,d){var e=I.a.Parse(function(){return new b(a.name,c)},a,c,d);return a.stencil&&e.stencil.parse(a.stencil,c,d),a.clearCoat&&e.clearCoat.parse(a.clearCoat,c,d),a.anisotropy&&e.anisotropy.parse(a.anisotropy,c,d),a.brdf&&e.brdf.parse(a.brdf,c,d),a.sheen&&e.sheen.parse(a.sheen,c,d),a.subSurface&&e.subSurface.parse(a.subSurface,c,d),e},b.PBRMATERIAL_OPAQUE=ui.PBRMATERIAL_OPAQUE,b.PBRMATERIAL_ALPHATEST=ui.PBRMATERIAL_ALPHATEST,b.PBRMATERIAL_ALPHABLEND=ui.PBRMATERIAL_ALPHABLEND,b.PBRMATERIAL_ALPHATESTANDBLEND=ui.PBRMATERIAL_ALPHATESTANDBLEND,b.DEFAULT_AO_ON_ANALYTICAL_LIGHTS=ui.DEFAULT_AO_ON_ANALYTICAL_LIGHTS,Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"directIntensity",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveIntensity",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"environmentIntensity",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"specularIntensity",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"disableBumpMap",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"albedoTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTextureStrength",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientTextureImpactOnAnalyticalLights",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"opacityTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectivityTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallic",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"roughness",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicF0Factor",void 0),Object(l.c)([Object(I.f)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicReflectanceColor",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useOnlyMetallicFromMetallicReflectanceTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"metallicReflectanceTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectanceTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"microSurfaceTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"bumpTexture",void 0),Object(l.c)([Object(I.n)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty",null)],b.prototype,"lightmapTexture",void 0),Object(l.c)([Object(I.f)("ambient"),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"ambientColor",void 0),Object(l.c)([Object(I.f)("albedo"),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"albedoColor",void 0),Object(l.c)([Object(I.f)("reflectivity"),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectivityColor",void 0),Object(l.c)([Object(I.f)("reflection"),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"reflectionColor",void 0),Object(l.c)([Object(I.f)("emissive"),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"emissiveColor",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"microSurface",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLightmapAsShadowmap",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"useAlphaFromAlbedoTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"forceAlphaTest",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesAndMiscDirty")],b.prototype,"alphaCutOff",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useSpecularOverAlpha",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useMicroSurfaceFromReflectivityMapAlpha",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRoughnessFromMetallicTextureAlpha",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRoughnessFromMetallicTextureGreen",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useMetallnessFromMetallicTextureBlue",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAmbientOcclusionFromMetallicTextureRed",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAmbientInGrayScale",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAutoMicroSurfaceFromReflectivityMap",void 0),Object(l.c)([Object(I.d)()],b.prototype,"usePhysicalLightFalloff",null),Object(l.c)([Object(I.d)()],b.prototype,"useGLTFLightFalloff",null),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRadianceOverAlpha",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useObjectSpaceNormalMap",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallax",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useParallaxOcclusion",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"parallaxScaleBias",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"disableLighting",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"forceIrradianceInFragment",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsLightsDirty")],b.prototype,"maxSimultaneousLights",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapX",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"invertNormalMapY",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"twoSidedLighting",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useAlphaFresnel",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useLinearAlphaFresnel",void 0),Object(l.c)([Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"environmentBRDFTexture",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"forceNormalForward",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"enableSpecularAntiAliasing",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useHorizonOcclusion",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsTexturesDirty")],b.prototype,"useRadianceOcclusion",void 0),Object(l.c)([Object(I.d)(),Object(I.b)("_markAllSubMeshesAsMiscDirty")],b.prototype,"unlit",void 0),b}(ui);Object(i.b)("BABYLON.PBRMaterial",vi);function wi(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var xi=wi("DXT1"),yi=wi("DXT3"),zi=wi("DXT5"),Ai=wi("DX10"),Bi=function(){function a(){}return a.GetDDSInfo=function(a){var b=new Int32Array(a.buffer,a.byteOffset,31);a=new Int32Array(a.buffer,a.byteOffset,35);var c=1;131072&b[2]&&(c=Math.max(1,b[7]));var d=b[21];a=d===Ai?a[32]:0;var e=r.a.TEXTURETYPE_UNSIGNED_INT;switch(d){case 113:e=r.a.TEXTURETYPE_HALF_FLOAT;break;case 116:e=r.a.TEXTURETYPE_FLOAT;break;case Ai:if(10===a){e=r.a.TEXTURETYPE_HALF_FLOAT;break}if(2===a){e=r.a.TEXTURETYPE_FLOAT;break}}return{width:b[4],height:b[3],mipmapCount:c,isFourCC:4==(4&b[20]),isRGB:64==(64&b[20]),isLuminance:131072==(131072&b[20]),isCube:512==(512&b[28]),isCompressed:d===xi||d===yi||d===zi,dxgiFormat:a,textureType:e}},a._GetHalfFloatAsFloatRGBAArrayBuffer=function(b,c,d,e,f,g){for(var e=new Float32Array(e),f=new Uint16Array(f,d),d=0,h=0;h>8)},a._GetRGBArrayBuffer=function(a,b,c,d,e,f,g,h){for(var d=new Uint8Array(d),e=new Uint8Array(e,c),c=0,i=0;i0?e.sphericalPolynomial=Se.ConvertCubeMapToSphericalPolynomial({size:G[4],right:k[0],left:k[1],up:k[2],down:k[3],front:k[4],back:k[5],format:r.a.TEXTUREFORMAT_RGBA,type:r.a.TEXTURETYPE_FLOAT,gammaSpace:!1}):e.sphericalPolynomial=void 0}else q.a.Error("Compressed textures are not supported on this platform.");else q.a.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");else q.a.Error("Invalid magic number in DDS header")},a.StoreLODInAlphaChannel=!1,a}();qb.a.prototype.createPrefilteredCubeTexture=function(a,b,c,d,e,f,g,h,i){var j=this;void 0===e&&(e=null),void 0===f&&(f=null),void 0===h&&(h=null),void 0===i&&(i=!0);return this.createCubeTexture(a,b,null,!1,function(a){if(a){var f=a.texture;if(i?a.info.sphericalPolynomial&&(f._sphericalPolynomial=a.info.sphericalPolynomial):f._sphericalPolynomial=new He,f._source=pb.b.CubePrefiltered,j.getCaps().textureLOD)e&&e(f);else{var g=j._gl,h=a.width;if(h){for(var k=[],l=0;l<3;l++){var p=1-l/2,s=d,t=H.a.Log2(h)*c+d;s=s+(t-s)*p;p=Math.round(Math.min(Math.max(s,0),t));s=new pb.a(j,pb.b.Temp);if(s.type=f.type,s.format=f.format,s.width=Math.pow(2,Math.max(H.a.Log2(h)-p,0)),s.height=s.width,s.isCube=!0,s._cachedWrapU=r.a.TEXTURE_CLAMP_ADDRESSMODE,s._cachedWrapV=r.a.TEXTURE_CLAMP_ADDRESSMODE,j._bindTextureDirectly(g.TEXTURE_CUBE_MAP,s,!0),s.samplingMode=r.a.TEXTURE_LINEAR_LINEAR,g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_MAG_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_MIN_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_WRAP_S,g.CLAMP_TO_EDGE),g.texParameteri(g.TEXTURE_CUBE_MAP,g.TEXTURE_WRAP_T,g.CLAMP_TO_EDGE),a.isDDS){t=a.info;var u=a.data;j._unpackFlipY(t.isCompressed),Bi.UploadDDSLevels(j,s,u,t,!0,6,p)}else q.a.Warn("DDS is the only prefiltered cube map supported so far.");j._bindTextureDirectly(g.TEXTURE_CUBE_MAP,null);u=new Ie.a(b);u.isCube=!0,u._texture=s,s.isReady=!0,k.push(u)}f._lodTextureHigh=k[2],f._lodTextureMid=k[1],f._lodTextureLow=k[0],e&&e(f)}}}else e&&e(null)},f,g,h,i,c,d)};var Ci=function(){function a(){this.supportCascades=!0}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".dds")},a.prototype.loadCubeData=function(a,b,c,d,e){var f;e=b.getEngine();var g=!1;if(Array.isArray(a))for(var h=0;h1)&&b.generateMipMaps,e._unpackFlipY(f.isCompressed),Bi.UploadDDSLevels(e,b,i,f,g,6,-1,h),f.isFourCC||1!==f.mipmapCount||e.generateMipMapsForCubemap(b)}else{i=a;f=Bi.GetDDSInfo(i),b.width=f.width,b.height=f.height,c&&(f.sphericalPolynomial=new He),g=(f.isRGB||f.isLuminance||f.mipmapCount>1)&&b.generateMipMaps,e._unpackFlipY(f.isCompressed),Bi.UploadDDSLevels(e,b,i,f,g,6),f.isFourCC||1!==f.mipmapCount||e.generateMipMapsForCubemap(b,!1)}e._setCubeMapTextureParams(b,g),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d({isDDS:!0,width:b.width,info:f,data:a,texture:b})},a.prototype.loadData=function(a,b,c){var d=Bi.GetDDSInfo(a),e=(d.isRGB||d.isLuminance||d.mipmapCount>1)&&b.generateMipMaps&&d.width>>d.mipmapCount-1==1;c(d.width,d.height,e,d.isFourCC,function(){Bi.UploadDDSLevels(b.getEngine(),b,a,d,e,1)})},a}();S.a._TextureLoaders.push(new Ci);var Di=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".env")},a.prototype.loadCubeData=function(a,b,c,d,e){if(!Array.isArray(a)){c=Ue(a);if(c){b.width=c.width,b.height=c.width;try{bf(b,c),Ze(b,a,c).then(function(){b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()},function(a){null==e||e("Can not upload environment levels",a)})}catch(a){null==e||e("Can not upload environment file",a)}}else e&&e("Can not parse the environment file",null)}},a.prototype.loadData=function(a,b,c){throw".env not supported in 2d."},a}();S.a._TextureLoaders.push(new Di);var Ei=function(){function a(b,c,d,e){if(this.data=b,this.isInvalid=!1,!a.IsValid(b))return this.isInvalid=!0,void q.a.Error("texture missing KTX identifier");d=Uint32Array.BYTES_PER_ELEMENT;e=new DataView(this.data.buffer,this.data.byteOffset+12,13*d);b=67305985===e.getUint32(0,!0);this.glType=e.getUint32(1*d,b),this.glTypeSize=e.getUint32(2*d,b),this.glFormat=e.getUint32(3*d,b),this.glInternalFormat=e.getUint32(4*d,b),this.glBaseInternalFormat=e.getUint32(5*d,b),this.pixelWidth=e.getUint32(6*d,b),this.pixelHeight=e.getUint32(7*d,b),this.pixelDepth=e.getUint32(8*d,b),this.numberOfArrayElements=e.getUint32(9*d,b),this.numberOfFaces=e.getUint32(10*d,b),this.numberOfMipmapLevels=e.getUint32(11*d,b),this.bytesOfKeyValueData=e.getUint32(12*d,b),0===this.glType?(this.numberOfMipmapLevels=Math.max(1,this.numberOfMipmapLevels),0!==this.pixelHeight&&0===this.pixelDepth?0===this.numberOfArrayElements?this.numberOfFaces===c?this.loadType=a.COMPRESSED_2D:q.a.Error("number of faces expected"+c+", but found "+this.numberOfFaces):q.a.Error("texture arrays not currently supported"):q.a.Error("only 2D textures currently supported")):q.a.Error("only compressed formats currently supported")}return a.prototype.uploadLevels=function(b,c){switch(this.loadType){case a.COMPRESSED_2D:this._upload2DCompressedLevels(b,c);break;case a.TEX_2D:case a.COMPRESSED_3D:case a.TEX_3D:}},a.prototype._upload2DCompressedLevels=function(b,c){for(var d=a.HEADER_LEN+this.bytesOfKeyValueData,e=this.pixelWidth,f=this.pixelHeight,c=c?this.numberOfMipmapLevels:1,g=0;g=12){a=new Uint8Array(a.buffer,a.byteOffset,12);if(171===a[0]&&75===a[1]&&84===a[2]&&88===a[3]&&32===a[4]&&49===a[5]&&49===a[6]&&187===a[7]&&13===a[8]&&10===a[9]&&26===a[10]&&10===a[11])return!0}return!1},a.HEADER_LEN=64,a.COMPRESSED_2D=0,a.COMPRESSED_3D=1,a.TEX_2D=2,a.TEX_3D=3,a}(),Fi=function(){function a(a){this._pendingActions=new Array,this._workerInfos=a.map(function(a){return{worker:a,active:!1}})}return a.prototype.dispose=function(){for(var a=0,b=this._workerInfos;a1,a.errors)throw new Error("KTX2 container - could not transcode the data. "+a.errors);for(c=0;c=12){a=new Uint8Array(a.buffer,a.byteOffset,12);if(171===a[0]&&75===a[1]&&84===a[2]&&88===a[3]&&32===a[4]&&50===a[5]&&48===a[6]&&187===a[7]&&13===a[8]&&10===a[9]&&26===a[10]&&10===a[11])return!0}return!1},a.URLConfig={jsDecoderModule:"https://preview.babylonjs.com/babylon.ktx2Decoder.js",wasmUASTCToASTC:null,wasmUASTCToBC7:null,wasmUASTCToRGBA_UNORM:null,wasmUASTCToRGBA_SRGB:null,jsMSCTranscoder:null,wasmMSCTranscoder:null,wasmZSTDDecoder:null},a.DefaultNumWorkers=a.GetDefaultNumWorkers(),a}();function Hi(){var a;onmessage=function(b){if(b.data)switch(b.data.action){case"init":var c=b.data.urls;importScripts(c.jsDecoderModule),null!==c.wasmUASTCToASTC&&(KTX2DECODER.LiteTranscoder_UASTC_ASTC.WasmModuleURL=c.wasmUASTCToASTC),null!==c.wasmUASTCToBC7&&(KTX2DECODER.LiteTranscoder_UASTC_BC7.WasmModuleURL=c.wasmUASTCToBC7),null!==c.wasmUASTCToRGBA_UNORM&&(KTX2DECODER.LiteTranscoder_UASTC_RGBA_UNORM.WasmModuleURL=c.wasmUASTCToRGBA_UNORM),null!==c.wasmUASTCToRGBA_SRGB&&(KTX2DECODER.LiteTranscoder_UASTC_RGBA_SRGB.WasmModuleURL=c.wasmUASTCToRGBA_SRGB),null!==c.jsMSCTranscoder&&(KTX2DECODER.MSCTranscoder.JSModuleURL=c.jsMSCTranscoder),null!==c.wasmMSCTranscoder&&(KTX2DECODER.MSCTranscoder.WasmModuleURL=c.wasmMSCTranscoder),null!==c.wasmZSTDDecoder&&(KTX2DECODER.ZSTDDecoder.WasmModuleURL=c.wasmZSTDDecoder),a=new KTX2DECODER.KTX2Decoder,postMessage({action:"init"});break;case"decode":a.decode(b.data.data,b.data.caps,b.data.options).then(function(a){for(var b=[],c=0;c1&&b.generateMipMaps;c._unpackFlipY(!0),e.uploadLevels(b,b.generateMipMaps),b.width=e.pixelWidth,b.height=e.pixelHeight,c._setCubeMapTextureParams(b,a),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()}},a.prototype.loadData=function(a,b,c,d){if(Ei.IsValid(a)){b._invertVScale=!b.invertY;var e=new Ei(a,1);c(e.pixelWidth,e.pixelHeight,b.generateMipMaps,!0,function(){e.uploadLevels(b,b.generateMipMaps)},e.isInvalid)}else Gi.IsValid(a)?new Gi(b.getEngine()).uploadAsync(a,b,d).then(function(){c(b.width,b.height,b.generateMipMaps,!0,function(){},!1)},function(a){q.a.Warn("Failed to load KTX2 texture data: "+a.message),c(0,0,!1,!1,function(){},!0)}):(q.a.Error("texture missing KTX identifier"),c(0,0,!1,!1,function(){},!0))},a}();S.a._TextureLoaders.unshift(new Ii);var Ji=function(a){function b(b,c,d){var e=a.call(this,b,g.e.Zero(),c)||this;return e._xrSessionManager=d,e._firstFrame=!1,e._referenceQuaternion=g.b.Identity(),e._referencedPosition=new g.e,e._trackingState=sd.NOT_TRACKING,e.onBeforeCameraTeleport=new f.c,e.onAfterCameraTeleport=new f.c,e.onTrackingStateChanged=new f.c,e.compensateOnFirstFrame=!0,e._rotate180=new g.b(0,1,0,0),e.minZ=.1,e.rotationQuaternion=new g.b,e.cameraRigMode=db.a.RIG_MODE_CUSTOM,e.updateUpVectorFromRotation=!0,e._updateNumberOfRigCameras(1),e.freezeProjectionMatrix(),e._xrSessionManager.onXRSessionInit.add(function(){e._referencedPosition.copyFromFloats(0,0,0),e._referenceQuaternion.copyFromFloats(0,0,0,1),e._firstFrame=e.compensateOnFirstFrame}),e._xrSessionManager.onXRFrameObservable.add(function(a){e._firstFrame&&e._updateFromXRSession(),e._updateReferenceSpace(),e._updateFromXRSession()},void 0,!0),e}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"trackingState",{get:function(){return this._trackingState},enumerable:!1,configurable:!0}),b.prototype._setTrackingState=function(a){this._trackingState!==a&&(this._trackingState=a,this.onTrackingStateChanged.notifyObservers(a))},Object.defineProperty(b.prototype,"realWorldHeight",{get:function(){var a=this._xrSessionManager.currentFrame&&this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.baseReferenceSpace);return a&&a.transform?a.transform.position.y:0},enumerable:!1,configurable:!0}),b.prototype._updateForDualEyeDebugging=function(){this._updateNumberOfRigCameras(2),this.rigCameras[0].viewport=new Oc.a(0,0,.5,1),this.rigCameras[0].outputRenderTarget=null,this.rigCameras[1].viewport=new Oc.a(.5,0,.5,1),this.rigCameras[1].outputRenderTarget=null},b.prototype.setTransformationFromNonVRCamera=function(a,b){(void 0===a&&(a=this.getScene().activeCamera),void 0===b&&(b=!0),a&&a!==this)&&(a.computeWorldMatrix().decompose(void 0,this.rotationQuaternion,this.position),this.position.y=0,g.b.FromEulerAnglesToRef(0,this.rotationQuaternion.toEulerAngles().y,0,this.rotationQuaternion),this._firstFrame=!0,b&&this._xrSessionManager.resetReferenceSpace())},b.prototype.getClassName=function(){return"WebXRCamera"},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._lastXRViewerPose=void 0},b.prototype._updateFromXRSession=function(){var a=this,b=this._xrSessionManager.currentFrame&&this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.referenceSpace);if(this._lastXRViewerPose=b||void 0,b){var c=b.emulatedPosition?sd.TRACKING_LOST:sd.TRACKING;if(this._setTrackingState(c),b.transform){c=b.transform.orientation;if(void 0===b.transform.orientation.x)return;var d=b.transform.position;this._referencedPosition.set(d.x,d.y,d.z),this._referenceQuaternion.set(c.x,c.y,c.z,c.w),this._scene.useRightHandedSystem||(this._referencedPosition.z*=-1,this._referenceQuaternion.z*=-1,this._referenceQuaternion.w*=-1),this._firstFrame?(this._firstFrame=!1,this.position.y+=this._referencedPosition.y,this._referenceQuaternion.copyFromFloats(0,0,0,1)):(this.rotationQuaternion.copyFrom(this._referenceQuaternion),this.position.copyFrom(this._referencedPosition))}this.rigCameras.length!==b.views.length&&this._updateNumberOfRigCameras(b.views.length),b.views.forEach(function(b,c){var d=a.rigCameras[c];d.isLeftCamera||d.isRightCamera||("right"===b.eye?d._isRightCamera=!0:"left"===b.eye&&(d._isLeftCamera=!0));var e=b.transform.position,f=b.transform.orientation;if(d.parent=a.parent,d.position.set(e.x,e.y,e.z),d.rotationQuaternion.set(f.x,f.y,f.z,f.w),a._scene.useRightHandedSystem?d.rotationQuaternion.multiplyInPlace(a._rotate180):(d.position.z*=-1,d.rotationQuaternion.z*=-1,d.rotationQuaternion.w*=-1),g.a.FromFloat32ArrayToRefScaled(b.projectionMatrix,0,1,d._projectionMatrix),a._scene.useRightHandedSystem||d._projectionMatrix.toggleProjectionMatrixHandInPlace(),0===c&&a._projectionMatrix.copyFrom(d._projectionMatrix),a._xrSessionManager.session.renderState.baseLayer){e=a._xrSessionManager.session.renderState.baseLayer.getViewport(b);f=a._xrSessionManager.session.renderState.baseLayer.framebufferWidth;c=a._xrSessionManager.session.renderState.baseLayer.framebufferHeight;d.viewport.width=e.width/f,d.viewport.height=e.height/c,d.viewport.x=e.x/f,d.viewport.y=e.y/c}d.outputRenderTarget=a._xrSessionManager.getRenderTargetTextureForEye(b.eye)})}else this._setTrackingState(sd.NOT_TRACKING)},b.prototype._updateNumberOfRigCameras=function(a){for(void 0===a&&(a=1);this.rigCameras.lengtha;){b=this.rigCameras.pop();b&&b.dispose()}},b.prototype._updateReferenceSpace=function(){if(!this.position.equals(this._referencedPosition)||!this.rotationQuaternion.equals(this._referenceQuaternion)){var a=g.c.Matrix[0],c=g.c.Matrix[1],d=g.c.Matrix[2];g.a.ComposeToRef(b._ScaleReadOnly,this._referenceQuaternion,this._referencedPosition,a),g.a.ComposeToRef(b._ScaleReadOnly,this.rotationQuaternion,this.position,c),a.invert().multiplyToRef(c,d),d.invert(),this._scene.useRightHandedSystem||d.toggleModelMatrixHandInPlace(),d.decompose(void 0,this._referenceQuaternion,this._referencedPosition);a=new XRRigidTransform({x:this._referencedPosition.x,y:this._referencedPosition.y,z:this._referencedPosition.z},{x:this._referenceQuaternion.x,y:this._referenceQuaternion.y,z:this._referenceQuaternion.z,w:this._referenceQuaternion.w});this._xrSessionManager.referenceSpace=this._xrSessionManager.referenceSpace.getOffsetReferenceSpace(a)}},b._ScaleReadOnly=g.e.One(),b}(dc),Ki=function(){function a(a){var b=this;this.scene=a,this._nonVRCamera=null,this._attachedToElement=!1,this._spectatorCamera=null,this._originalSceneAutoClear=!0,this._supported=!1,this._spectatorMode=!1,this.onInitialXRPoseSetObservable=new f.c,this.onStateChangedObservable=new f.c,this.state=rd.NOT_IN_XR,this.sessionManager=new wd(a),this.camera=new Ji("webxr",a,this.sessionManager),this.featuresManager=new lb(this.sessionManager),a.onDisposeObservable.add(function(){b.exitXRAsync()})}return a.CreateAsync=function(b){var c=new a(b);return c.sessionManager.initializeAsync().then(function(){return c._supported=!0,c})["catch"](function(a){throw c._setState(rd.NOT_IN_XR),c.dispose(),a})},a.prototype.dispose=function(){var a;this.camera.dispose(),this.onStateChangedObservable.clear(),this.onInitialXRPoseSetObservable.clear(),this.sessionManager.dispose(),null===(a=this._spectatorCamera)||void 0===a||a.dispose(),this._nonVRCamera&&(this.scene.activeCamera=this._nonVRCamera)},a.prototype.enterXRAsync=function(a,b,c,d){var e,f;return void 0===c&&(c=this.sessionManager.getWebXRRenderTarget()),void 0===d&&(d={}),Object(l.b)(this,void 0,void 0,function(){var g,h=this;return Object(l.e)(this,function(i){switch(i.label){case 0:if(!this._supported)throw"WebXR not supported in this browser or environment";return this._setState(rd.ENTERING_XR),"viewer"!==b&&"local"!==b&&(d.optionalFeatures=d.optionalFeatures||[],d.optionalFeatures.push(b)),[4,this.featuresManager._extendXRSessionInitObject(d)];case 1:d=i.sent(),"immersive-ar"===a&&"unbounded"!==b&&q.a.Warn("We recommend using "unbounded" reference space type when using "immersive-ar" session mode"),i.label=2;case 2:return i.trys.push([2,7,,8]),[4,this.sessionManager.initializeSessionAsync(a,d)];case 3:return i.sent(),[4,this.sessionManager.setReferenceSpaceTypeAsync(b)];case 4:return i.sent(),[4,c.initializeXRLayerAsync(this.sessionManager.session)];case 5:return i.sent(),[4,this.sessionManager.updateRenderStateAsync({depthFar:this.camera.maxZ,depthNear:this.camera.minZ,baseLayer:c.xrLayer})];case 6:return i.sent(),this.sessionManager.runXRRenderLoop(),this._originalSceneAutoClear=this.scene.autoClear,this._nonVRCamera=this.scene.activeCamera,this._attachedToElement=!!(null===(e=this._nonVRCamera)||void 0===e?void 0:e.inputs.attachedToElement),null===(f=this._nonVRCamera)||void 0===f||f.detachControl(),this.scene.activeCamera=this.camera,"immersive-ar"!==a?this._nonXRToXRCamera():(this.scene.autoClear=!1,this.camera.compensateOnFirstFrame=!1),this.sessionManager.onXRSessionEnded.addOnce(function(){h.camera.rigCameras.forEach(function(a){a.outputRenderTarget=null}),h.scene.autoClear=h._originalSceneAutoClear,h.scene.activeCamera=h._nonVRCamera,h._attachedToElement&&h._nonVRCamera&&h._nonVRCamera.attachControl(!!h._nonVRCamera.inputs.noPreventDefault),"immersive-ar"!==a&&h.camera.compensateOnFirstFrame&&(h._nonVRCamera.setPosition?h._nonVRCamera.setPosition(h.camera.position):h._nonVRCamera.position.copyFrom(h.camera.position)),h._setState(rd.NOT_IN_XR)}),this.sessionManager.onXRFrameObservable.addOnce(function(){h._setState(rd.IN_XR)}),[2,this.sessionManager];case 7:throw g=i.sent(),!1,!1,this._setState(rd.NOT_IN_XR),g;case 8:return[2]}})})},a.prototype.exitXRAsync=function(){return this.state!==rd.IN_XR?Promise.resolve():(this._setState(rd.EXITING_XR),this.sessionManager.exitXRAsync())},a.prototype.enableSpectatorMode=function(){var a=this;if(!this._spectatorMode){var b=function(){a._spectatorCamera&&(a._spectatorCamera.position.copyFrom(a.camera.rigCameras[0].globalPosition),a._spectatorCamera.rotationQuaternion.copyFrom(a.camera.rigCameras[0].absoluteRotation))},c=function(){a.state===rd.IN_XR?(a._spectatorCamera=new yc("webxr-spectator",g.e.Zero(),a.scene),a._spectatorCamera.rotationQuaternion=new g.b,a.scene.activeCameras=[a.camera,a._spectatorCamera],a.sessionManager.onXRFrameObservable.add(b),a.scene.onAfterRenderCameraObservable.add(function(b){b===a.camera&&(a.scene.getEngine().framebufferDimensionsObject=null)})):a.state===rd.EXITING_XR&&(a.sessionManager.onXRFrameObservable.removeCallback(b),a.scene.activeCameras=null)};this._spectatorMode=!0,this.onStateChangedObservable.add(c),c()}},a.prototype._nonXRToXRCamera=function(){this.camera.setTransformationFromNonVRCamera(this._nonVRCamera),this.onInitialXRPoseSetObservable.notifyObservers(this.camera)},a.prototype._setState=function(a){this.state!==a&&(this.state=a,this.onStateChangedObservable.notifyObservers(this.state))},a}(),Li=function(){function a(a,b,c,d){void 0===c&&(c=-1),void 0===d&&(d=[]),this.id=a,this.type=b,this._buttonIndex=c,this._axesIndices=d,this._axes={x:0,y:0},this._changes={},this._currentValue=0,this._hasChanges=!1,this._pressed=!1,this._touched=!1,this.onAxisValueChangedObservable=new f.c,this.onButtonStateChangedObservable=new f.c}return Object.defineProperty(a.prototype,"axes",{get:function(){return this._axes},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"changes",{get:function(){return this._changes},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasChanges",{get:function(){return this._hasChanges},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"pressed",{get:function(){return this._pressed},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"touched",{get:function(){return this._touched},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"value",{get:function(){return this._currentValue},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.onAxisValueChangedObservable.clear(),this.onButtonStateChangedObservable.clear()},a.prototype.isAxes=function(){return 0!==this._axesIndices.length},a.prototype.isButton=function(){return-1!==this._buttonIndex},a.prototype.update=function(a){var b=!1,c=!1;if(this._hasChanges=!1,this._changes={},this.isButton()){var d=a.buttons[this._buttonIndex];if(!d)return;this._currentValue!==d.value&&(this.changes.value={current:d.value,previous:this._currentValue},b=!0,this._currentValue=d.value),this._touched!==d.touched&&(this.changes.touched={current:d.touched,previous:this._touched},b=!0,this._touched=d.touched),this._pressed!==d.pressed&&(this.changes.pressed={current:d.pressed,previous:this._pressed},b=!0,this._pressed=d.pressed)}this.isAxes()&&(this._axes.x!==a.axes[this._axesIndices[0]]&&(this.changes.axes={current:{x:a.axes[this._axesIndices[0]],y:this._axes.y},previous:{x:this._axes.x,y:this._axes.y}},this._axes.x=a.axes[this._axesIndices[0]],c=!0),this._axes.y!==a.axes[this._axesIndices[1]]&&(this.changes.axes?this.changes.axes.current.y=a.axes[this._axesIndices[1]]:this.changes.axes={current:{x:this._axes.x,y:a.axes[this._axesIndices[1]]},previous:{x:this._axes.x,y:this._axes.y}},this._axes.y=a.axes[this._axesIndices[1]],c=!0)),b&&(this._hasChanges=!0,this.onButtonStateChangedObservable.notifyObservers(this)),c&&(this._hasChanges=!0,this.onAxisValueChangedObservable.notifyObservers(this._axes))},a.BUTTON_TYPE="button",a.SQUEEZE_TYPE="squeeze",a.THUMBSTICK_TYPE="thumbstick",a.TOUCHPAD_TYPE="touchpad",a.TRIGGER_TYPE="trigger",a}(),Mi=function(){function a(a,b,c,d,e,g){var h=this;void 0===e&&(e=!1),this.scene=a,this.layout=b,this.gamepadObject=c,this.handedness=d,this._doNotLoadControllerMesh=e,this._controllerCache=g,this._initComponent=function(a){if(a){var b=h.layout.components[a],c=b.type,d=b.gamepadIndices.button,e=[];void 0!==b.gamepadIndices.xAxis&&void 0!==b.gamepadIndices.yAxis&&e.push(b.gamepadIndices.xAxis,b.gamepadIndices.yAxis),h.components[a]=new Li(a,c,d,e)}},this._modelReady=!1,this.components={},this.disableAnimation=!1,this.onModelLoadedObservable=new f.c,b.components&&Object.keys(b.components).forEach(this._initComponent)}return a.prototype.dispose=function(){var a=this;this.getComponentIds().forEach(function(b){return a.getComponent(b).dispose()}),this.rootMesh&&(this.rootMesh.getChildren(void 0,!0).forEach(function(a){a.setEnabled(!1)}),this.rootMesh.dispose(!!this._controllerCache,!this._controllerCache))},a.prototype.getAllComponentsOfType=function(a){var b=this;return this.getComponentIds().map(function(a){return b.components[a]}).filter(function(b){return b.type===a})},a.prototype.getComponent=function(a){return this.components[a]},a.prototype.getComponentIds=function(){return Object.keys(this.components)},a.prototype.getComponentOfType=function(a){return this.getAllComponentsOfType(a)[0]||null},a.prototype.getMainComponent=function(){return this.getComponent(this.layout.selectComponentId)},a.prototype.loadModel=function(){return Object(l.b)(this,void 0,void 0,function(){var a,b,c=this;return Object(l.e)(this,function(d){return a=!this._getModelLoadingConstraints(),b=this._getGenericFilenameAndPath(),a?q.a.Warn("Falling back to generic models"):b=this._getFilenameAndPath(),[2,new Promise(function(d,e){var f=function(b){a?c._getGenericParentMesh(b):c._setRootMesh(b),c._processLoadedModel(b),c._modelReady=!0,c.onModelLoadedObservable.notifyObservers(c),d(!0)};if(c._controllerCache){var g=c._controllerCache.filter(function(a){return a.filename===b.filename&&a.path===b.path});if(g[0])return g[0].meshes.forEach(function(a){return a.setEnabled(!0)}),void f(g[0].meshes)}hh.ImportMesh("",b.path,b.filename,c.scene,function(a){c._controllerCache&&c._controllerCache.push(Object(l.a)(Object(l.a)({},b),{meshes:a})),f(a)},null,function(a,d){q.a.Log(d),q.a.Warn("Failed to retrieve controller model of type "+c.profileId+" from the remote server: "+b.path+b.filename),e(d)})})]})})},a.prototype.updateFromXRFrame=function(a){var b=this;this.getComponentIds().forEach(function(a){return b.getComponent(a).update(b.gamepadObject)}),this.updateModel(a)},Object.defineProperty(a.prototype,"handness",{get:function(){return this.handedness},enumerable:!1,configurable:!0}),a.prototype.pulse=function(a,b,c){return void 0===c&&(c=0),this.gamepadObject.hapticActuators&&this.gamepadObject.hapticActuators[c]?this.gamepadObject.hapticActuators[c].pulse(a,b):Promise.resolve(!1)},a.prototype._getChildByName=function(a,b){return a.getChildren(function(a){return a.name===b},!1)[0]},a.prototype._getImmediateChildByName=function(a,b){return a.getChildren(function(a){return a.name==b},!0)[0]},a.prototype._lerpTransform=function(a,b,c){if(a.minMesh&&a.maxMesh&&a.valueMesh&&a.minMesh.rotationQuaternion&&a.maxMesh.rotationQuaternion&&a.valueMesh.rotationQuaternion){c=c?.5*b+.5:b;g.b.SlerpToRef(a.minMesh.rotationQuaternion,a.maxMesh.rotationQuaternion,c,a.valueMesh.rotationQuaternion),g.e.LerpToRef(a.minMesh.position,a.maxMesh.position,c,a.valueMesh.position)}},a.prototype.updateModel=function(a){this._modelReady&&this._updateModel(a)},a.prototype._getGenericFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},a.prototype._getGenericParentMesh=function(a){var b=this;this.rootMesh=new R.a(this.profileId+" "+this.handedness,this.scene),a.forEach(function(a){a.parent||(a.isPickable=!1,a.setParent(b.rootMesh))}),this.rootMesh.rotationQuaternion=g.b.FromEulerAngles(0,Math.PI,0)},a}(),Ni=function(a){function b(c,d,e){c=a.call(this,c,Oi[e],d,e)||this;return c.profileId=b.ProfileId,c}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},b.prototype._getModelLoadingConstraints=function(){return!0},b.prototype._processLoadedModel=function(a){},b.prototype._setRootMesh=function(a){var b=this;this.rootMesh=new R.a(this.profileId+" "+this.handedness,this.scene),a.forEach(function(a){a.isPickable=!1,a.parent||a.setParent(b.rootMesh)}),this.rootMesh.rotationQuaternion=g.b.FromEulerAngles(0,Math.PI,0)},b.prototype._updateModel=function(){},b.ProfileId="generic-trigger",b}(Mi),Oi={left:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-left",assetPath:"left.glb"},right:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-right",assetPath:"right.glb"},none:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr_standard_trigger",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-trigger-none",assetPath:"none.glb"}},Pi=function(a){function b(b,c,d,e,f){b=a.call(this,b,d.layouts[c.handedness||"none"],c.gamepad,c.handedness,void 0,f)||this;return b._repositoryUrl=e,b.controllerCache=f,b._buttonMeshMapping={},b._touchDots={},b.profileId=d.profileId,b}return Object(l.d)(b,a),b.prototype.dispose=function(){var b=this;a.prototype.dispose.call(this),this.controllerCache||Object.keys(this._touchDots).forEach(function(a){b._touchDots[a].dispose()})},b.prototype._getFilenameAndPath=function(){return{filename:this.layout.assetPath,path:this._repositoryUrl+"/profiles/"+this.profileId+"/"}},b.prototype._getModelLoadingConstraints=function(){var a=hh.IsPluginForExtensionAvailable(".glb");return a||q.a.Warn("glTF / glb loader was not registered, using generic controller instead"),a},b.prototype._processLoadedModel=function(a){var b=this;this.getComponentIds().forEach(function(a){var c=b.layout.components[a];b._buttonMeshMapping[a]={mainMesh:b._getChildByName(b.rootMesh,c.rootNodeName),states:{}},Object.keys(c.visualResponses).forEach(function(d){var e=c.visualResponses[d];if("transform"===e.valueNodeProperty)b._buttonMeshMapping[a].states[d]={valueMesh:b._getChildByName(b.rootMesh,e.valueNodeName),minMesh:b._getChildByName(b.rootMesh,e.minNodeName),maxMesh:b._getChildByName(b.rootMesh,e.maxNodeName)};else{e=c.type===Li.TOUCHPAD_TYPE&&c.touchPointNodeName?c.touchPointNodeName:e.valueNodeName;if(b._buttonMeshMapping[a].states[d]={valueMesh:b._getChildByName(b.rootMesh,e)},c.type===Li.TOUCHPAD_TYPE&&!b._touchDots[d]){e=Object(vh.a)(d+"dot",{diameter:.0015,segments:8},b.scene);e.material=new pd.a(d+"mat",b.scene),e.material.diffuseColor=h.a.Red(),e.parent=b._buttonMeshMapping[a].states[d].valueMesh||null,e.isVisible=!1,b._touchDots[d]=e}}})})},b.prototype._setRootMesh=function(a){var b;this.rootMesh=new R.a(this.profileId+"-"+this.handedness,this.scene),this.rootMesh.isPickable=!1;for(var c=0;cd/10&&(g.isVisible=!0),(h+=b._scene.getEngine().getDeltaTime())>=d)b._scene.simulatePointerDown(c.pick,j),i=!0,b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,j),g.isVisible=!1;else{var a=1-h/d;g.scaling.set(a,a,a)}else i=!1,h=0;b._scene.simulatePointerMove(c.pick,j),f=c.pick}}),void 0!==this._options.renderingGroupId&&(g.renderingGroupId=this._options.renderingGroupId),a&&a.onDisposeObservable.addOnce(function(){c.pick&&!b._options.disablePointerUpOnTouchOut&&i&&b._scene.simulatePointerUp(c.pick,j),g.dispose()})},b.prototype._attachScreenRayMode=function(a){var b=this,c=this._controllers[a.uniqueId],d=!1,e={pointerId:c.id,pointerType:"xr"};c.onFrameObserver=this._xrSessionManager.onXRFrameObservable.add(function(){!c.pick||b._options.disablePointerUpOnTouchOut&&d||(d?b._scene.simulatePointerMove(c.pick,e):(b._scene.simulatePointerDown(c.pick,e),d=!0,b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,e)))}),a.onDisposeObservable.addOnce(function(){c.pick&&d&&!b._options.disablePointerUpOnTouchOut&&b._scene.simulatePointerUp(c.pick,e)})},b.prototype._attachTrackedPointerRayMode=function(a){var b=this,c=this._controllers[a.uniqueId];if(this._options.forceGazeMode)return this._attachGazeMode(a);var d={pointerId:c.id,pointerType:"xr"};if(c.onFrameObserver=this._xrSessionManager.onXRFrameObservable.add(function(){c.laserPointer.material.disableLighting=b.disablePointerLighting,c.selectionMesh.material.disableLighting=b.disableSelectionMeshLighting,c.pick&&b._scene.simulatePointerMove(c.pick,d)}),a.inputSource.gamepad){var e=function(e){b._options.overrideButtonId&&(c.selectionComponent=e.getComponent(b._options.overrideButtonId)),c.selectionComponent||(c.selectionComponent=e.getMainComponent()),c.onButtonChangedObserver=c.selectionComponent.onButtonStateChangedObservable.add(function(e){if(e.changes.pressed){e=e.changes.pressed.current;c.pick?(b._options.enablePointerSelectionOnAllControllers||a.uniqueId===b._attachedController)&&(e?(b._scene.simulatePointerDown(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshPickedColor,c.laserPointer.material.emissiveColor=b.laserPointerPickedColor):(b._scene.simulatePointerUp(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshDefaultColor,c.laserPointer.material.emissiveColor=b.laserPointerDefaultColor)):!e||b._options.enablePointerSelectionOnAllControllers||b._options.disableSwitchOnClick||(b._attachedController=a.uniqueId)}})};a.motionController?e(a.motionController):a.onMotionControllerInitObservable.add(e)}else{e=function(a){c.xrController&&a.inputSource===c.xrController.inputSource&&c.pick&&(b._scene.simulatePointerDown(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshPickedColor,c.laserPointer.material.emissiveColor=b.laserPointerPickedColor)};var f=function(a){c.xrController&&a.inputSource===c.xrController.inputSource&&c.pick&&(b._scene.simulatePointerUp(c.pick,d),c.selectionMesh.material.emissiveColor=b.selectionMeshDefaultColor,c.laserPointer.material.emissiveColor=b.laserPointerDefaultColor)};c.eventListeners={selectend:f,selectstart:e},this._xrSessionManager.session.addEventListener("selectstart",e),this._xrSessionManager.session.addEventListener("selectend",f)}},b.prototype._convertNormalToDirectionOfRay=function(a,b){a&&Math.acos(g.e.Dot(a,b.direction))a},b.prototype._updatePointerDistance=function(a,b){void 0===b&&(b=100),a.scaling.y=b,this._scene.useRightHandedSystem&&(b*=-1),a.position.z=b/2+.05},Object.defineProperty(b.prototype,"lasterPointerDefaultColor",{get:function(){return this.laserPointerDefaultColor},enumerable:!1,configurable:!0}),b._idCounter=200,b.Name=kb.POINTER_SELECTION,b.Version=1,b}(Vi);lb.AddWebXRFeature(Wi.Name,function(a,b){return function(){return new Wi(a,b)}},Wi.Version,!0);var Xi=function(a){function b(c,d){var e=a.call(this,c)||this;return e._options=d,e._attachController=function(a){if(!e._controllers[a.uniqueId]){var c=e._generateNewHandTipMesh(),d=e._generateVisualCue();switch(e._controllers[a.uniqueId]={xrController:a,meshUnderPointer:null,nearInteractionMesh:null,pick:null,pickIndexMeshTip:c,grabRay:new nc.a(new g.e,new g.e),hoverInteraction:!1,nearInteraction:!1,grabInteraction:!1,id:b._idCounter++,pickedPointVisualCue:d},e._attachedController?!e._options.enableNearInteractionOnAllControllers&&e._options.preferredHandedness&&a.inputSource.handedness===e._options.preferredHandedness&&(e._attachedController=a.uniqueId):e._options.enableNearInteractionOnAllControllers||(e._attachedController=a.uniqueId),a.inputSource.targetRayMode){case"tracked-pointer":return e._attachNearInteractionMode(a);case"gaze":case"screen":return null}}},e._controllers={},e._farInteractionFeature=null,e.selectionMeshDefaultColor=new h.a(.8,.8,.8),e.selectionMeshPickedColor=new h.a(.3,.3,1),e._hoverRadius=.1,e._pickRadius=.02,e._nearGrabLengthScale=5,e._indexTipQuaternion=new g.b,e._indexTipOrientationVector=g.e.Zero(),e._scene=e._xrSessionManager.scene,e._options.farInteractionFeature&&(e._farInteractionFeature=e._options.farInteractionFeature),e}return Object(l.d)(b,a),b.prototype.attach=function(){var b=this;return!!a.prototype.attach.call(this)&&(this._options.xrInput.controllers.forEach(this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable,this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable,function(a){b._detachController(a.uniqueId)}),this._scene.constantlyUpdateMeshUnderPointer=!0,!0)},b.prototype.detach=function(){var b=this;return!!a.prototype.detach.call(this)&&(Object.keys(this._controllers).forEach(function(a){b._detachController(a)}),!0)},b.prototype.getMeshUnderPointer=function(a){return this._controllers[a]?this._controllers[a].meshUnderPointer:null},b.prototype.getXRControllerByPointerId=function(a){for(var b=Object.keys(this._controllers),c=0;ci&&(h=0,f.copyFrom(b.center)),-1!==h&&h=a.timeout&&(a.contextObservable.remove(e),a.onEnded&&a.onEnded(f))},a.observableParameters.mask,a.observableParameters.insertFirst,a.observableParameters.scope);return e}!function(a){a[a.INIT=0]="INIT",a[a.STARTED=1]="STARTED",a[a.ENDED=2]="ENDED"}(Yi||(Yi={}));var cj=function(){function a(a){var b,c=this;this.onEachCountObservable=new f.c,this.onTimerAbortedObservable=new f.c,this.onTimerEndedObservable=new f.c,this.onStateChangedObservable=new f.c,this._observer=null,this._breakOnNextTick=!1,this._tick=function(a){var b=Date.now();c._timer=b-c._startTime;b={startTime:c._startTime,currentTime:b,deltaTime:c._timer,completeRate:c._timer/c._timeToEnd,payload:a};a=c._breakOnNextTick||c._breakCondition(b);a||c._timer>=c._timeToEnd?c._stop(b,a):c.onEachCountObservable.notifyObservers(b)},this._setState(Yi.INIT),this._contextObservable=a.contextObservable,this._observableParameters=null!==(b=a.observableParameters)&&void 0!==b?b:{},this._breakCondition=null!==(b=a.breakCondition)&&void 0!==b?b:function(){return!1},a.onEnded&&this.onTimerEndedObservable.add(a.onEnded),a.onTick&&this.onEachCountObservable.add(a.onTick),a.onAborted&&this.onTimerAbortedObservable.add(a.onAborted)}return Object.defineProperty(a.prototype,"breakCondition",{set:function(a){this._breakCondition=a},enumerable:!1,configurable:!0}),a.prototype.clearObservables=function(){this.onEachCountObservable.clear(),this.onTimerAbortedObservable.clear(),this.onTimerEndedObservable.clear(),this.onStateChangedObservable.clear()},a.prototype.start=function(a){if(void 0===a&&(a=this._timeToEnd),this._state===Yi.STARTED)throw new Error("Timer already started. Please stop it before starting again");this._timeToEnd=a,this._startTime=Date.now(),this._timer=0,this._observer=this._contextObservable.add(this._tick,this._observableParameters.mask,this._observableParameters.insertFirst,this._observableParameters.scope),this._setState(Yi.STARTED)},a.prototype.stop=function(){this._state===Yi.STARTED&&(this._breakOnNextTick=!0)},a.prototype.dispose=function(){this._observer&&this._contextObservable.remove(this._observer),this.clearObservables()},a.prototype._setState=function(a){this._state=a,this.onStateChangedObservable.notifyObservers(this._state)},a.prototype._stop=function(a,b){void 0===b&&(b=!1),this._contextObservable.remove(this._observer),this._setState(Yi.ENDED),b?this.onTimerAbortedObservable.notifyObservers(a):this.onTimerEndedObservable.notifyObservers(a)},a}(),dj=function(a){function b(b,c){var d=a.call(this,b)||this;return d._options=c,d._controllers={},d._snappedToPoint=!1,d._tmpRay=new nc.a(new g.e,new g.e),d._tmpVector=new g.e,d._tmpQuaternion=new g.b,d.skipNextTeleportation=!1,d.backwardsMovementEnabled=!0,d.backwardsTeleportationDistance=.7,d.parabolicCheckRadius=5,d.parabolicRayEnabled=!0,d.straightRayEnabled=!0,d.rotationAngle=Math.PI/8,d.onTargetMeshPositionUpdatedObservable=new f.c,d.teleportationEnabled=!0,d._rotationEnabled=!0,d._attachController=function(a){if(!(d._controllers[a.uniqueId]||d._options.forceHandedness&&a.inputSource.handedness!==d._options.forceHandedness)){d._controllers[a.uniqueId]={xrController:a,teleportationState:{forward:!1,backwards:!1,rotating:!1,currentRotation:0,baseRotation:0}};var b=d._controllers[a.uniqueId];if("tracked-pointer"===b.xrController.inputSource.targetRayMode&&b.xrController.inputSource.gamepad){var c=function(){if(a.motionController){var c=a.motionController.getComponentOfType(Li.THUMBSTICK_TYPE)||a.motionController.getComponentOfType(Li.TOUCHPAD_TYPE);if(!c||d._options.useMainComponentOnly){var e=a.motionController.getMainComponent();if(!e)return;b.teleportationComponent=e,b.onButtonChangedObserver=e.onButtonStateChangedObservable.add(function(){d.teleportationEnabled&&e.changes.pressed&&(e.changes.pressed.current?(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y,b.teleportationState.currentRotation=0,bj({timeout:d._options.timeToTeleport||3e3,contextObservable:d._xrSessionManager.onXRFrameObservable,breakCondition:function(){return!e.pressed},onEnded:function(){d._currentTeleportationControllerId===b.xrController.uniqueId&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)}})):(b.teleportationState.forward=!1,d._currentTeleportationControllerId=""))})}else b.teleportationComponent=c,b.onAxisChangedObserver=c.onAxisValueChangedObservable.add(function(c){if(c.y<=.7&&b.teleportationState.backwards&&(b.teleportationState.backwards=!1),c.y>.7&&!b.teleportationState.forward&&d.backwardsMovementEnabled&&!d.snapPointsOnly&&!b.teleportationState.backwards){b.teleportationState.backwards=!0,d._tmpQuaternion.copyFrom(d._options.xrInput.xrCamera.rotationQuaternion),d._tmpQuaternion.toEulerAnglesToRef(d._tmpVector),d._tmpVector.x=0,d._tmpVector.z=0,g.b.FromEulerVectorToRef(d._tmpVector,d._tmpQuaternion),d._tmpVector.set(0,0,d.backwardsTeleportationDistance*(d._xrSessionManager.scene.useRightHandedSystem?1:-1)),d._tmpVector.rotateByQuaternionToRef(d._tmpQuaternion,d._tmpVector),d._tmpVector.addInPlace(d._options.xrInput.xrCamera.position),d._tmpRay.origin.copyFrom(d._tmpVector),d._tmpRay.length=d._options.xrInput.xrCamera.realWorldHeight+.1,d._tmpRay.direction.set(0,-1,0);var e=d._xrSessionManager.scene.pickWithRay(d._tmpRay,function(a){return-1!==d._floorMeshes.indexOf(a)});e&&e.pickedPoint&&(d._options.xrInput.xrCamera.position.x=e.pickedPoint.x,d._options.xrInput.xrCamera.position.z=e.pickedPoint.z)}if(c.y<-.7&&!d._currentTeleportationControllerId&&!b.teleportationState.rotating&&d.teleportationEnabled&&(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y),c.x){if(b.teleportationState.forward)d._currentTeleportationControllerId===b.xrController.uniqueId&&(d.rotationEnabled?setTimeout(function(){b.teleportationState.currentRotation=Math.atan2(c.x,c.y*(d._xrSessionManager.scene.useRightHandedSystem?1:-1))}):b.teleportationState.currentRotation=0);else if(!b.teleportationState.rotating&&Math.abs(c.x)>.7){b.teleportationState.rotating=!0;e=d.rotationAngle*(c.x>0?1:-1)*(d._xrSessionManager.scene.useRightHandedSystem?-1:1);g.b.FromEulerAngles(0,e,0).multiplyToRef(d._options.xrInput.xrCamera.rotationQuaternion,d._options.xrInput.xrCamera.rotationQuaternion)}}else b.teleportationState.rotating=!1;0===c.x&&0===c.y&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)})}};a.motionController?c():a.onMotionControllerInitObservable.addOnce(function(){c()})}else d._xrSessionManager.scene.onPointerObservable.add(function(c){c.type===Ua.a.POINTERDOWN?(b.teleportationState.forward=!0,d._currentTeleportationControllerId=b.xrController.uniqueId,b.teleportationState.baseRotation=d._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y,b.teleportationState.currentRotation=0,bj({timeout:d._options.timeToTeleport||3e3,contextObservable:d._xrSessionManager.onXRFrameObservable,onEnded:function(){d._currentTeleportationControllerId===b.xrController.uniqueId&&b.teleportationState.forward&&d._teleportForward(a.uniqueId)}})):c.type===Ua.a.POINTERUP&&(b.teleportationState.forward=!1,d._currentTeleportationControllerId="")})}},d._options.teleportationTargetMesh||d._createDefaultTargetMesh(),d._floorMeshes=d._options.floorMeshes||[],d._snapToPositions=d._options.snapPositions||[],d._setTargetMeshVisibility(!1),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"rotationEnabled",{get:function(){return this._rotationEnabled},set:function(a){if(this._rotationEnabled=a,this._options.teleportationTargetMesh){var b=this._options.teleportationTargetMesh.getChildMeshes(!1,function(a){return"rotationCone"===a.name});b[0]&&b[0].setEnabled(a)}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"teleportationTargetMesh",{get:function(){return this._options.teleportationTargetMesh||null},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"snapPointsOnly",{get:function(){return!!this._options.snapPointsOnly},set:function(a){this._options.snapPointsOnly=a},enumerable:!1,configurable:!0}),b.prototype.addFloorMesh=function(a){this._floorMeshes.push(a)},b.prototype.addBlockerMesh=function(a){this._options.pickBlockerMeshes=this._options.pickBlockerMeshes||[],this._options.pickBlockerMeshes.push(a)},b.prototype.addSnapPoint=function(a){this._snapToPositions.push(a)},b.prototype.attach=function(){var b=this;return!!a.prototype.attach.call(this)&&(this._currentTeleportationControllerId="",this._options.xrInput.controllers.forEach(this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable,this._attachController),this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable,function(a){b._detachController(a.uniqueId)}),!0)},b.prototype.detach=function(){var b=this;return!!a.prototype.detach.call(this)&&(Object.keys(this._controllers).forEach(function(a){b._detachController(a)}),this._setTargetMeshVisibility(!1),this._currentTeleportationControllerId="",this._controllers={},!0)},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._options.teleportationTargetMesh&&this._options.teleportationTargetMesh.dispose(!1,!0)},b.prototype.removeFloorMesh=function(a){a=this._floorMeshes.indexOf(a);-1!==a&&this._floorMeshes.splice(a,1)},b.prototype.removeBlockerMesh=function(a){this._options.pickBlockerMeshes=this._options.pickBlockerMeshes||[];a=this._options.pickBlockerMeshes.indexOf(a);-1!==a&&this._options.pickBlockerMeshes.splice(a,1)},b.prototype.removeFloorMeshByName=function(a){a=this._xrSessionManager.scene.getMeshByName(a);a&&this.removeFloorMesh(a)},b.prototype.removeSnapPoint=function(a){var b=this._snapToPositions.indexOf(a);if(-1===b)for(var c=0;c=j.video.HAVE_CURRENT_DATA;return!h.poster||h.autoPlay&&f?f&&j._createInternalTexture():(j._texture=j._getEngine().createTexture(h.poster,!1,!j.invertY,d),j._displayingPosterTexture=!0),j}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"onUserActionRequestedObservable",{get:function(){return this._onUserActionRequestedObservable||(this._onUserActionRequestedObservable=new f.c),this._onUserActionRequestedObservable},enumerable:!1,configurable:!0}),b.prototype._processError=function(a){this._errorFound=!0,this._onError?this._onError(null==a?void 0:a.message):q.a.Error(null==a?void 0:a.message)},b.prototype._handlePlay=function(){var a=this;this._errorFound=!1,this.video.play()["catch"](function(b){if("NotAllowedError"===(null==b?void 0:b.name)){if(a._onUserActionRequestedObservable&&a._onUserActionRequestedObservable.hasObservers())return void a._onUserActionRequestedObservable.notifyObservers(a);if(!a.video.muted)return q.a.Warn("Unable to autoplay a video with sound. Trying again with muted turned true"),a.video.muted=!0,a._errorFound=!1,void a.video.play()["catch"](function(b){a._processError(b)})}a._processError(b)})},b.prototype.getClassName=function(){return"VideoTexture"},b.prototype._getName=function(a){return a instanceof HTMLVideoElement?a.currentSrc:"object"==typeof a?a.toString():a},b.prototype._getVideo=function(a){if(a.isNative)return a;if(a instanceof HTMLVideoElement)return T.b.SetCorsBehavior(a.currentSrc,a),a;var b=document.createElement("video");return"string"==typeof a?(T.b.SetCorsBehavior(a,b),b.src=a):(T.b.SetCorsBehavior(a[0],b),a.forEach(function(a){var c=document.createElement("source");c.src=a,b.appendChild(c)})),b},b.prototype._rebuild=function(){this.update()},b.prototype.update=function(){this.autoUpdateTexture&&this.updateTexture(!0)},b.prototype.updateTexture=function(a){a&&(this.video.paused&&this._stillImageCaptured||(this._stillImageCaptured=!0,this._updateInternalTexture()))},b.prototype.updateURL=function(a){this.video.src=a,this._currentSrc=a},b.prototype.clone=function(){return new b(this.name,this._currentSrc,this.getScene(),this._generateMipMaps,this.invertY,this.samplingMode,this._settings)},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._currentSrc=null,this._onUserActionRequestedObservable&&(this._onUserActionRequestedObservable.clear(),this._onUserActionRequestedObservable=null),this.video.removeEventListener(this._createInternalTextureOnEvent,this._createInternalTexture),this.video.removeEventListener("paused",this._updateInternalTexture),this.video.removeEventListener("seeked",this._updateInternalTexture),this.video.removeEventListener("emptied",this.reset),this.video.pause()},b.CreateFromStreamAsync=function(a,c,d,e){void 0===e&&(e=!0);var f=a.getEngine().createVideoElement(d);return a.getEngine()._badOS&&(document.body.appendChild(f),f.style.transform="scale(0.0001, 0.0001)",f.style.opacity="0",f.style.position="fixed",f.style.bottom="0px",f.style.right="0px"),f.setAttribute("autoplay",""),f.setAttribute("muted","true"),f.setAttribute("playsinline",""),f.muted=!0,void 0!==f.mozSrcObject?f.mozSrcObject=c:"object"==typeof f.srcObject?f.srcObject=c:(window.URL=window.URL||window.webkitURL||window.mozURL||window.msURL,f.src=window.URL&&window.URL.createObjectURL(c)),new Promise(function(c){var d=function(){c(new b("video",f,a,!0,e)),f.removeEventListener("playing",d)};f.addEventListener("playing",d),f.play()})},b.CreateFromWebCamAsync=function(a,b,c,d){var e,f=this;if(void 0===c&&(c=!1),void 0===d&&(d=!0),b&&b.deviceId&&(e={exact:b.deviceId}),navigator.mediaDevices)return navigator.mediaDevices.getUserMedia({video:b,audio:c}).then(function(c){return f.CreateFromStreamAsync(a,c,b,d)});var g=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;return g&&g({video:{deviceId:e,width:{min:b&&b.minWidth||256,max:b&&b.maxWidth||640},height:{min:b&&b.minHeight||256,max:b&&b.maxHeight||480}},audio:c},function(c){return f.CreateFromStreamAsync(a,c,b,d)},function(a){q.a.Error(a.name)}),Promise.reject("No support for userMedia on this device")},b.CreateFromWebCam=function(a,b,c,d,e){void 0===d&&(d=!1),void 0===e&&(e=!0),this.CreateFromWebCamAsync(a,c,d,e).then(function(a){b&&b(a)})["catch"](function(a){q.a.Error(a.name)})},b}(U.a),ij=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"videoTexture",{get:function(){return this._texture},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"videoMode",{get:function(){return this.textureMode},set:function(a){this.textureMode=a},enumerable:!1,configurable:!0}),b.prototype._initTexture=function(a,b,c){var d=this,e={loop:c.loop,autoPlay:c.autoPlay,autoUpdateTexture:!0,poster:c.poster};a=new hj((this.name||"videoDome")+"_texture",a,b,c.generateMipMaps,this._useDirectMapping,U.a.TRILINEAR_SAMPLINGMODE,e);return c.clickToPlay&&(b.onPointerUp=function(){d._texture.video.play()}),a.onLoadObservable.add(function(){d.onLoadObservable.notifyObservers()}),a},b.MODE_MONOSCOPIC=fi.MODE_MONOSCOPIC,b.MODE_TOPBOTTOM=fi.MODE_TOPBOTTOM,b.MODE_SIDEBYSIDE=fi.MODE_SIDEBYSIDE,b}(fi),jj=function(){function a(a){this.engine=a,this._captureGPUFrameTime=!1,this._captureShaderCompilationTime=!1,this._shaderCompilationTime=new re.a,this._onBeginFrameObserver=null,this._onEndFrameObserver=null,this._onBeforeShaderCompilationObserver=null,this._onAfterShaderCompilationObserver=null}return Object.defineProperty(a.prototype,"gpuFrameTimeCounter",{get:function(){return this.engine.getGPUFrameTimeCounter()},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureGPUFrameTime",{get:function(){return this._captureGPUFrameTime},set:function(a){a!==this._captureGPUFrameTime&&(this._captureGPUFrameTime=a,this.engine.captureGPUFrameTime(a))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"shaderCompilationTimeCounter",{get:function(){return this._shaderCompilationTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureShaderCompilationTime",{get:function(){return this._captureShaderCompilationTime},set:function(a){var b=this;a!==this._captureShaderCompilationTime&&(this._captureShaderCompilationTime=a,a?(this._onBeforeShaderCompilationObserver=this.engine.onBeforeShaderCompilationObservable.add(function(){b._shaderCompilationTime.fetchNewFrame(),b._shaderCompilationTime.beginMonitoring()}),this._onAfterShaderCompilationObserver=this.engine.onAfterShaderCompilationObservable.add(function(){b._shaderCompilationTime.endMonitoring()})):(this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver),this._onBeforeShaderCompilationObserver=null,this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver),this._onAfterShaderCompilationObserver=null))},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.engine.onBeginFrameObservable.remove(this._onBeginFrameObserver),this._onBeginFrameObserver=null,this.engine.onEndFrameObservable.remove(this._onEndFrameObserver),this._onEndFrameObserver=null,this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver),this._onBeforeShaderCompilationObserver=null,this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver),this._onAfterShaderCompilationObserver=null,this.engine=null},a}(),kj=function(){function a(a){var b=this;this.scene=a,this._captureActiveMeshesEvaluationTime=!1,this._activeMeshesEvaluationTime=new re.a,this._captureRenderTargetsRenderTime=!1,this._renderTargetsRenderTime=new re.a,this._captureFrameTime=!1,this._frameTime=new re.a,this._captureRenderTime=!1,this._renderTime=new re.a,this._captureInterFrameTime=!1,this._interFrameTime=new re.a,this._captureParticlesRenderTime=!1,this._particlesRenderTime=new re.a,this._captureSpritesRenderTime=!1,this._spritesRenderTime=new re.a,this._capturePhysicsTime=!1,this._physicsTime=new re.a,this._captureAnimationsTime=!1,this._animationsTime=new re.a,this._captureCameraRenderTime=!1,this._cameraRenderTime=new re.a,this._onBeforeActiveMeshesEvaluationObserver=null,this._onAfterActiveMeshesEvaluationObserver=null,this._onBeforeRenderTargetsRenderObserver=null,this._onAfterRenderTargetsRenderObserver=null,this._onAfterRenderObserver=null,this._onBeforeDrawPhaseObserver=null,this._onAfterDrawPhaseObserver=null,this._onBeforeAnimationsObserver=null,this._onBeforeParticlesRenderingObserver=null,this._onAfterParticlesRenderingObserver=null,this._onBeforeSpritesRenderingObserver=null,this._onAfterSpritesRenderingObserver=null,this._onBeforePhysicsObserver=null,this._onAfterPhysicsObserver=null,this._onAfterAnimationsObserver=null,this._onBeforeCameraRenderObserver=null,this._onAfterCameraRenderObserver=null,this._onBeforeAnimationsObserver=a.onBeforeAnimationsObservable.add(function(){b._captureActiveMeshesEvaluationTime&&b._activeMeshesEvaluationTime.fetchNewFrame(),b._captureRenderTargetsRenderTime&&b._renderTargetsRenderTime.fetchNewFrame(),b._captureFrameTime&&(T.b.StartPerformanceCounter("Scene rendering"),b._frameTime.beginMonitoring()),b._captureInterFrameTime&&b._interFrameTime.endMonitoring(),b._captureParticlesRenderTime&&b._particlesRenderTime.fetchNewFrame(),b._captureSpritesRenderTime&&b._spritesRenderTime.fetchNewFrame(),b._captureAnimationsTime&&b._animationsTime.beginMonitoring(),b.scene.getEngine()._drawCalls.fetchNewFrame()}),this._onAfterRenderObserver=a.onAfterRenderObservable.add(function(){b._captureFrameTime&&(T.b.EndPerformanceCounter("Scene rendering"),b._frameTime.endMonitoring()),b._captureRenderTime&&b._renderTime.endMonitoring(!1),b._captureInterFrameTime&&b._interFrameTime.beginMonitoring()})}return Object.defineProperty(a.prototype,"activeMeshesEvaluationTimeCounter",{get:function(){return this._activeMeshesEvaluationTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureActiveMeshesEvaluationTime",{get:function(){return this._captureActiveMeshesEvaluationTime},set:function(a){var b=this;a!==this._captureActiveMeshesEvaluationTime&&(this._captureActiveMeshesEvaluationTime=a,a?(this._onBeforeActiveMeshesEvaluationObserver=this.scene.onBeforeActiveMeshesEvaluationObservable.add(function(){T.b.StartPerformanceCounter("Active meshes evaluation"),b._activeMeshesEvaluationTime.beginMonitoring()}),this._onAfterActiveMeshesEvaluationObserver=this.scene.onAfterActiveMeshesEvaluationObservable.add(function(){T.b.EndPerformanceCounter("Active meshes evaluation"),b._activeMeshesEvaluationTime.endMonitoring()})):(this.scene.onBeforeActiveMeshesEvaluationObservable.remove(this._onBeforeActiveMeshesEvaluationObserver),this._onBeforeActiveMeshesEvaluationObserver=null,this.scene.onAfterActiveMeshesEvaluationObservable.remove(this._onAfterActiveMeshesEvaluationObserver),this._onAfterActiveMeshesEvaluationObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderTargetsRenderTimeCounter",{get:function(){return this._renderTargetsRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureRenderTargetsRenderTime",{get:function(){return this._captureRenderTargetsRenderTime},set:function(a){var b=this;a!==this._captureRenderTargetsRenderTime&&(this._captureRenderTargetsRenderTime=a,a?(this._onBeforeRenderTargetsRenderObserver=this.scene.onBeforeRenderTargetsRenderObservable.add(function(){T.b.StartPerformanceCounter("Render targets rendering"),b._renderTargetsRenderTime.beginMonitoring()}),this._onAfterRenderTargetsRenderObserver=this.scene.onAfterRenderTargetsRenderObservable.add(function(){T.b.EndPerformanceCounter("Render targets rendering"),b._renderTargetsRenderTime.endMonitoring(!1)})):(this.scene.onBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver),this._onBeforeRenderTargetsRenderObserver=null,this.scene.onAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver),this._onAfterRenderTargetsRenderObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"particlesRenderTimeCounter",{get:function(){return this._particlesRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureParticlesRenderTime",{get:function(){return this._captureParticlesRenderTime},set:function(a){var b=this;a!==this._captureParticlesRenderTime&&(this._captureParticlesRenderTime=a,a?(this._onBeforeParticlesRenderingObserver=this.scene.onBeforeParticlesRenderingObservable.add(function(){T.b.StartPerformanceCounter("Particles"),b._particlesRenderTime.beginMonitoring()}),this._onAfterParticlesRenderingObserver=this.scene.onAfterParticlesRenderingObservable.add(function(){T.b.EndPerformanceCounter("Particles"),b._particlesRenderTime.endMonitoring(!1)})):(this.scene.onBeforeParticlesRenderingObservable.remove(this._onBeforeParticlesRenderingObserver),this._onBeforeParticlesRenderingObserver=null,this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver),this._onAfterParticlesRenderingObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"spritesRenderTimeCounter",{get:function(){return this._spritesRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureSpritesRenderTime",{get:function(){return this._captureSpritesRenderTime},set:function(a){var b=this;a!==this._captureSpritesRenderTime&&(this._captureSpritesRenderTime=a,this.scene.spriteManagers&&(a?(this._onBeforeSpritesRenderingObserver=this.scene.onBeforeSpritesRenderingObservable.add(function(){T.b.StartPerformanceCounter("Sprites"),b._spritesRenderTime.beginMonitoring()}),this._onAfterSpritesRenderingObserver=this.scene.onAfterSpritesRenderingObservable.add(function(){T.b.EndPerformanceCounter("Sprites"),b._spritesRenderTime.endMonitoring(!1)})):(this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver),this._onBeforeSpritesRenderingObserver=null,this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver),this._onAfterSpritesRenderingObserver=null)))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"physicsTimeCounter",{get:function(){return this._physicsTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"capturePhysicsTime",{get:function(){return this._capturePhysicsTime},set:function(a){var b=this;a!==this._capturePhysicsTime&&this.scene.onBeforePhysicsObservable&&(this._capturePhysicsTime=a,a?(this._onBeforePhysicsObserver=this.scene.onBeforePhysicsObservable.add(function(){T.b.StartPerformanceCounter("Physics"),b._physicsTime.beginMonitoring()}),this._onAfterPhysicsObserver=this.scene.onAfterPhysicsObservable.add(function(){T.b.EndPerformanceCounter("Physics"),b._physicsTime.endMonitoring()})):(this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver),this._onBeforePhysicsObserver=null,this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver),this._onAfterPhysicsObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationsTimeCounter",{get:function(){return this._animationsTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureAnimationsTime",{get:function(){return this._captureAnimationsTime},set:function(a){var b=this;a!==this._captureAnimationsTime&&(this._captureAnimationsTime=a,a?this._onAfterAnimationsObserver=this.scene.onAfterAnimationsObservable.add(function(){b._animationsTime.endMonitoring()}):(this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver),this._onAfterAnimationsObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"frameTimeCounter",{get:function(){return this._frameTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureFrameTime",{get:function(){return this._captureFrameTime},set:function(a){this._captureFrameTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"interFrameTimeCounter",{get:function(){return this._interFrameTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureInterFrameTime",{get:function(){return this._captureInterFrameTime},set:function(a){this._captureInterFrameTime=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderTimeCounter",{get:function(){return this._renderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureRenderTime",{get:function(){return this._captureRenderTime},set:function(a){var b=this;a!==this._captureRenderTime&&(this._captureRenderTime=a,a?(this._onBeforeDrawPhaseObserver=this.scene.onBeforeDrawPhaseObservable.add(function(){b._renderTime.beginMonitoring(),T.b.StartPerformanceCounter("Main render")}),this._onAfterDrawPhaseObserver=this.scene.onAfterDrawPhaseObservable.add(function(){b._renderTime.endMonitoring(!1),T.b.EndPerformanceCounter("Main render")})):(this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver),this._onBeforeDrawPhaseObserver=null,this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver),this._onAfterDrawPhaseObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"cameraRenderTimeCounter",{get:function(){return this._cameraRenderTime},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"captureCameraRenderTime",{get:function(){return this._captureCameraRenderTime},set:function(a){var b=this;a!==this._captureCameraRenderTime&&(this._captureCameraRenderTime=a,a?(this._onBeforeCameraRenderObserver=this.scene.onBeforeCameraRenderObservable.add(function(a){b._cameraRenderTime.beginMonitoring(),T.b.StartPerformanceCounter("Rendering camera "+a.name)}),this._onAfterCameraRenderObserver=this.scene.onAfterCameraRenderObservable.add(function(a){b._cameraRenderTime.endMonitoring(!1),T.b.EndPerformanceCounter("Rendering camera "+a.name)})):(this.scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=null,this.scene.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=null))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"drawCallsCounter",{get:function(){return this.scene.getEngine()._drawCalls},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=null,this.scene.onBeforeActiveMeshesEvaluationObservable.remove(this._onBeforeActiveMeshesEvaluationObserver),this._onBeforeActiveMeshesEvaluationObserver=null,this.scene.onAfterActiveMeshesEvaluationObservable.remove(this._onAfterActiveMeshesEvaluationObserver),this._onAfterActiveMeshesEvaluationObserver=null,this.scene.onBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver),this._onBeforeRenderTargetsRenderObserver=null,this.scene.onAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver),this._onAfterRenderTargetsRenderObserver=null,this.scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver),this._onBeforeAnimationsObserver=null,this.scene.onBeforeParticlesRenderingObservable.remove(this._onBeforeParticlesRenderingObserver),this._onBeforeParticlesRenderingObserver=null,this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver),this._onAfterParticlesRenderingObserver=null,this._onBeforeSpritesRenderingObserver&&(this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver),this._onBeforeSpritesRenderingObserver=null),this._onAfterSpritesRenderingObserver&&(this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver),this._onAfterSpritesRenderingObserver=null),this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver),this._onBeforeDrawPhaseObserver=null,this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver),this._onAfterDrawPhaseObserver=null,this._onBeforePhysicsObserver&&(this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver),this._onBeforePhysicsObserver=null),this._onAfterPhysicsObserver&&(this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver),this._onAfterPhysicsObserver=null),this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver),this._onAfterAnimationsObserver=null,this.scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver),this._onBeforeCameraRenderObserver=null,this.scene.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver),this._onAfterCameraRenderObserver=null,this.scene=null},a}();V="#if defined(DIFFUSE_ISLINEAR) || defined(EMISSIVE_ISLINEAR) #include #endif #ifdef DIFFUSE varying vec2 vUVDiffuse; uniform sampler2D diffuseSampler; #endif #ifdef OPACITY varying vec2 vUVOpacity; uniform sampler2D opacitySampler; uniform float opacityIntensity; #endif #ifdef EMISSIVE varying vec2 vUVEmissive; uniform sampler2D emissiveSampler; #endif #ifdef VERTEXALPHA varying vec4 vColor; #endif uniform vec4 glowColor; void main(void) { vec4 finalColor=glowColor; #ifdef DIFFUSE vec4 albedoTexture=texture2D(diffuseSampler,vUVDiffuse); #ifdef DIFFUSE_ISLINEAR albedoTexture=toGammaSpace(albedoTexture); #endif #ifdef GLOW finalColor.a*=albedoTexture.a; #endif #ifdef HIGHLIGHT finalColor.a=albedoTexture.a; #endif #endif #ifdef OPACITY vec4 opacityMap=texture2D(opacitySampler,vUVOpacity); #ifdef OPACITYRGB finalColor.a*=getLuminance(opacityMap.rgb); #else finalColor.a*=opacityMap.a; #endif finalColor.a*=opacityIntensity; #endif #ifdef VERTEXALPHA finalColor.a*=vColor.a; #endif #ifdef ALPHATEST if (finalColor.a #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; varying vec4 vPosition; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #ifdef DIFFUSE varying vec2 vUVDiffuse; uniform mat4 diffuseMatrix; #endif #ifdef OPACITY varying vec2 vUVOpacity; uniform mat4 opacityMatrix; #endif #ifdef EMISSIVE varying vec2 vUVEmissive; uniform mat4 emissiveMatrix; #endif #ifdef VERTEXALPHA attribute vec4 color; varying vec4 vColor; #endif void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include #ifdef CUBEMAP vPosition=finalWorld*vec4(positionUpdated,1.0); gl_Position=viewProjection*finalWorld*vec4(position,1.0); #else vPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); gl_Position=vPosition; #endif #ifdef DIFFUSE #ifdef DIFFUSEUV1 vUVDiffuse=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef DIFFUSEUV2 vUVDiffuse=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef OPACITY #ifdef OPACITYUV1 vUVOpacity=vec2(opacityMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef OPACITYUV2 vUVOpacity=vec2(opacityMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef EMISSIVE #ifdef EMISSIVEUV1 vUVEmissive=vec2(emissiveMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef EMISSIVEUV2 vUVEmissive=vec2(emissiveMatrix*vec4(uv2,1.0,0.0)); #endif #endif #ifdef VERTEXALPHA vColor=color; #endif }";X.a.ShadersStore.glowMapGenerationVertexShader=a;var lj=function(){function a(b,c){this._vertexBuffers={},this._maxSize=0,this._mainTextureDesiredSize={width:0,height:0},this._shouldRender=!0,this._postProcesses=[],this._textures=[],this._emissiveTextureAndColor={texture:null,color:new h.b},this.neutralColor=new h.b,this.isEnabled=!0,this.disableBoundingBoxesFromEffectLayer=!1,this.onDisposeObservable=new f.c,this.onBeforeRenderMainTextureObservable=new f.c,this.onBeforeComposeObservable=new f.c,this.onBeforeRenderMeshToEffect=new f.c,this.onAfterRenderMeshToEffect=new f.c,this.onAfterComposeObservable=new f.c,this.onSizeChangedObservable=new f.c,this.name=b,this._scene=c||C.a.LastCreatedScene,a._SceneComponentInitialization(this._scene),this._engine=this._scene.getEngine(),this._maxSize=this._engine.getCaps().maxTextureSize,this._scene.effectLayers.push(this),this._effectLayerMapGenerationDrawWrapper=new Ec.a(this._engine),this._mergeDrawWrapper=new Ec.a(this._engine),this._generateIndexBuffer(),this._generateVertexBuffer()}return Object.defineProperty(a.prototype,"camera",{get:function(){return this._effectLayerOptions.camera},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderingGroupId",{get:function(){return this._effectLayerOptions.renderingGroupId},set:function(a){this._effectLayerOptions.renderingGroupId=a},enumerable:!1,configurable:!0}),a.prototype._init=function(a){this._effectLayerOptions=Object(l.a)({mainTextureRatio:.5,alphaBlendingMode:r.a.ALPHA_COMBINE,camera:null,renderingGroupId:-1},a),this._setMainTextureSize(),this._createMainTexture(),this._createTextureAndPostProcesses(),this._mergeDrawWrapper.setEffect(this._createMergeEffect())},a.prototype._generateIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._engine.createIndexBuffer(a)},a.prototype._generateVertexBuffer=function(){var a=[];a.push(1,1),a.push(-1,1),a.push(-1,-1),a.push(1,-1);a=new W.b(this._engine,a,W.b.PositionKind,!1,!1,2);this._vertexBuffers[W.b.PositionKind]=a},a.prototype._setMainTextureSize=function(){this._effectLayerOptions.mainTextureFixedSize?(this._mainTextureDesiredSize.width=this._effectLayerOptions.mainTextureFixedSize,this._mainTextureDesiredSize.height=this._effectLayerOptions.mainTextureFixedSize):(this._mainTextureDesiredSize.width=this._engine.getRenderWidth()*this._effectLayerOptions.mainTextureRatio,this._mainTextureDesiredSize.height=this._engine.getRenderHeight()*this._effectLayerOptions.mainTextureRatio,this._mainTextureDesiredSize.width=this._engine.needPOTTextures?S.a.GetExponentOfTwo(this._mainTextureDesiredSize.width,this._maxSize):this._mainTextureDesiredSize.width,this._mainTextureDesiredSize.height=this._engine.needPOTTextures?S.a.GetExponentOfTwo(this._mainTextureDesiredSize.height,this._maxSize):this._mainTextureDesiredSize.height),this._mainTextureDesiredSize.width=Math.floor(this._mainTextureDesiredSize.width),this._mainTextureDesiredSize.height=Math.floor(this._mainTextureDesiredSize.height)},a.prototype._createMainTexture=function(){var a=this;if(this._mainTexture=new cd("HighlightLayerMainRTT",{width:this._mainTextureDesiredSize.width,height:this._mainTextureDesiredSize.height},this._scene,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT),this._mainTexture.activeCamera=this._effectLayerOptions.camera,this._mainTexture.wrapU=U.a.CLAMP_ADDRESSMODE,this._mainTexture.wrapV=U.a.CLAMP_ADDRESSMODE,this._mainTexture.anisotropicFilteringLevel=1,this._mainTexture.updateSamplingMode(U.a.BILINEAR_SAMPLINGMODE),this._mainTexture.renderParticles=!1,this._mainTexture.renderList=null,this._mainTexture.ignoreCameraViewport=!0,this._mainTexture.customRenderFunction=function(b,c,d,e){var f;a.onBeforeRenderMainTextureObservable.notifyObservers(a);var g=a._scene.getEngine();if(e.length){for(g.setColorWrite(!1),f=0;f4&&(f.push(W.b.MatricesIndicesExtraKind),f.push(W.b.MatricesWeightsExtraKind)),e.push("#define NUM_BONE_INFLUENCERS "+g.numBoneInfluencers);k=g.skeleton;k&&k.isUsingTextureForMatrices?e.push("#define BONETEXTURE"):e.push("#define BonesPerMesh "+(k?k.bones.length+1:0)),g.numBoneInfluencers>0&&j.addCPUSkinningFallback(0,g)}else e.push("#define NUM_BONE_INFLUENCERS 0");d=g.morphTargetManager;l=0;d&&d.numInfluencers>0&&(e.push("#define MORPHTARGETS"),l=d.numInfluencers,e.push("#define NUM_MORPH_INFLUENCERS "+l),d.isUsingTextureForTargets&&e.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(f,g,l)),b&&(e.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(f),a.getRenderingMesh().hasThinInstances&&e.push("#define THIN_INSTANCES")),this._addCustomEffectDefines(e);c=e.join(" ");return this._cachedDefines!==c&&(this._cachedDefines=c,this._effectLayerMapGenerationDrawWrapper.setEffect(this._scene.getEngine().createEffect("glowMapGeneration",f,["world","mBones","viewProjection","glowColor","morphTargetInfluences","boneTextureWidth","diffuseMatrix","emissiveMatrix","opacityMatrix","opacityIntensity","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","emissiveSampler","opacitySampler","boneSampler","morphTargets"],c,j,void 0,void 0,{maxSimultaneousMorphTargets:l}))),this._effectLayerMapGenerationDrawWrapper.effect.isReady()},a.prototype.render=function(){var a=this._mergeDrawWrapper;if(a.effect.isReady()){for(var b=0;b-1&&this._scene.effectLayers.splice(a,1),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onBeforeRenderMainTextureObservable.clear(),this.onBeforeComposeObservable.clear(),this.onBeforeRenderMeshToEffect.clear(),this.onAfterRenderMeshToEffect.clear(),this.onAfterComposeObservable.clear(),this.onSizeChangedObservable.clear()},a.prototype.getClassName=function(){return"EffectLayer"},a.Parse=function(a,b,c){return T.b.Instantiate(a.customType).Parse(a,b,c)},a._SceneComponentInitialization=function(a){throw Object(La.a)("EffectLayerSceneComponent")},Object(l.c)([Object(I.d)()],a.prototype,"name",void 0),Object(l.c)([Object(I.g)()],a.prototype,"neutralColor",void 0),Object(l.c)([Object(I.d)()],a.prototype,"isEnabled",void 0),Object(l.c)([Object(I.e)()],a.prototype,"camera",null),Object(l.c)([Object(I.d)()],a.prototype,"renderingGroupId",null),Object(l.c)([Object(I.d)()],a.prototype,"disableBoundingBoxesFromEffectLayer",void 0),a}();d.a.AddParser(Oa.a.NAME_EFFECTLAYER,function(a,b,c,d){if(a.effectLayers){c.effectLayers||(c.effectLayers=new Array);for(var e=0;e0){this._previousStencilState=this._engine.getStencilBuffer();for(var d=0,c=c;d-1)){this._renderEffects=!0,this._needStencil=this._needStencil||e.needStencil();e=e._mainTexture;e._shouldRender()&&(this.scene.incrementRenderId(),e.render(!1,!1),b=!0)}}this.scene.incrementRenderId()}return b},a.prototype._setStencil=function(){this._needStencil&&this._engine.setStencilBuffer(!0)},a.prototype._setStencilBack=function(){this._needStencil&&this._engine.setStencilBuffer(this._previousStencilState)},a.prototype._draw=function(a){if(this._renderEffects){this._engine.setDepthBuffer(!1);for(var b=this.scene.effectLayers,c=0;c-1},b.prototype.referenceMeshToUseItsOwnMaterial=function(a){var b=this;this._meshesUsingTheirOwnMaterials.push(a.uniqueId),a.onDisposeObservable.add(function(){b._disposeMesh(a)})},b.prototype.unReferenceMeshFromUsingItsOwnMaterial=function(a){for(var b=this._meshesUsingTheirOwnMaterials.indexOf(a.uniqueId);b>=0;)this._meshesUsingTheirOwnMaterials.splice(b,1),b=this._meshesUsingTheirOwnMaterials.indexOf(a.uniqueId)},b.prototype._disposeMesh=function(a){this.removeIncludedOnlyMesh(a),this.removeExcludedMesh(a)},b.prototype.getClassName=function(){return"GlowLayer"},b.prototype.serialize=function(){var a,b=I.a.Serialize(this);if(b.customType="BABYLON.GlowLayer",b.includedMeshes=[],this._includedOnlyMeshes.length)for(a=0;a0&&a.isBackground===b&&a.renderTargetTextures.indexOf(d)>-1&&0!=(a.layerMask&c)},a.prototype._drawRenderTargetBackground=function(a){var b=this;this._draw(function(c){return b._drawRenderTargetPredicate(c,!0,b.scene.activeCamera.layerMask,a)})},a.prototype._drawRenderTargetForeground=function(a){var b=this;this._draw(function(c){return b._drawRenderTargetPredicate(c,!1,b.scene.activeCamera.layerMask,a)})},a.prototype.addFromContainer=function(a){var b=this;a.layers&&a.layers.forEach(function(a){b.scene.layers.push(a)})},a.prototype.removeFromContainer=function(a,b){var c=this;void 0===b&&(b=!1),a.layers&&a.layers.forEach(function(a){var d=c.scene.layers.indexOf(a);-1!==d&&c.scene.layers.splice(d,1),b&&a.dispose()})},a}();wi=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec4 color; #include void main(void) { vec4 baseColor=texture2D(textureSampler,vUV); #ifdef LINEAR baseColor.rgb=toGammaSpace(baseColor.rgb); #endif #ifdef ALPHATEST if (baseColor.a<0.4) discard; #endif gl_FragColor=baseColor*color; }";X.a.ShadersStore.layerPixelShader=wi;V=" attribute vec2 position; uniform vec2 scale; uniform vec2 offset; uniform mat4 textureMatrix; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vec2 shiftedPosition=position*scale+offset; vUV=vec2(textureMatrix*vec4(shiftedPosition*madd+madd,1.0,0.0)); gl_Position=vec4(shiftedPosition,0.0,1.0); }";X.a.ShadersStore.layerVertexShader=V;var rj=function(){function a(a,b,c,d,e){this.name=a,this.scale=new g.d(1,1),this.offset=new g.d(0,0),this.alphaBlendingMode=r.a.ALPHA_COMBINE,this.layerMask=268435455,this.renderTargetTextures=[],this.renderOnlyInRenderTargetTextures=!1,this.isEnabled=!0,this._vertexBuffers={},this.onDisposeObservable=new f.c,this.onBeforeRenderObservable=new f.c,this.onAfterRenderObservable=new f.c,this.texture=b?new U.a(b,c,!0):null,this.isBackground=void 0===d||d,this.color=void 0===e?new h.b(1,1,1,1):e,this._scene=c||C.a.LastCreatedScene;a=this._scene._getComponent(Oa.a.NAME_LAYER);a||(a=new qj(this._scene),this._scene._addComponent(a)),this._scene.layers.push(this);b=this._scene.getEngine();this._drawWrapper=new Ec.a(b);d=[];d.push(1,1),d.push(-1,1),d.push(-1,-1),d.push(1,-1);e=new W.b(b,d,W.b.PositionKind,!1,!1,2);this._vertexBuffers[W.b.PositionKind]=e,this._createIndexBuffer()}return Object.defineProperty(a.prototype,"onDispose",{set:function(a){this._onDisposeObserver&&this.onDisposeObservable.remove(this._onDisposeObserver),this._onDisposeObserver=this.onDisposeObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onBeforeRender",{set:function(a){this._onBeforeRenderObserver&&this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver),this._onBeforeRenderObserver=this.onBeforeRenderObservable.add(a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"onAfterRender",{set:function(a){this._onAfterRenderObserver&&this.onAfterRenderObservable.remove(this._onAfterRenderObserver),this._onAfterRenderObserver=this.onAfterRenderObservable.add(a)},enumerable:!1,configurable:!0}),a.prototype._createIndexBuffer=function(){var a=this._scene.getEngine(),b=[];b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),this._indexBuffer=a.createIndexBuffer(b)},a.prototype._rebuild=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&a._rebuild(),this._createIndexBuffer()},a.prototype.render=function(){if(this.isEnabled){var a=this._scene.getEngine(),b="";this.alphaTest&&(b="#define ALPHATEST"),this.texture&&!this.texture.gammaSpace&&(b+=" #define LINEAR"),this._previousDefines!==b&&(this._previousDefines=b,this._drawWrapper.effect=a.createEffect("layer",[W.b.PositionKind],["textureMatrix","color","scale","offset"],["textureSampler"],b));b=this._drawWrapper.effect;b&&b.isReady()&&this.texture&&this.texture.isReady()&&(a=this._scene.getEngine(),(this.onBeforeRenderObservable.notifyObservers(this),a.enableEffect(this._drawWrapper),a.setState(!1),b.setTexture("textureSampler",this.texture),b.setMatrix("textureMatrix",this.texture.getTextureMatrix()),b.setFloat4("color",this.color.r,this.color.g,this.color.b,this.color.a),b.setVector2("offset",this.offset),b.setVector2("scale",this.scale),a.bindBuffers(this._vertexBuffers,this._indexBuffer,b),this.alphaTest?a.drawElementsType(qi.a.TriangleFillMode,0,6):(a.setAlphaMode(this.alphaBlendingMode),a.drawElementsType(qi.a.TriangleFillMode,0,6),a.setAlphaMode(r.a.ALPHA_DISABLE)),this.onAfterRenderObservable.notifyObservers(this)))}},a.prototype.dispose=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&(a.dispose(),this._vertexBuffers[W.b.PositionKind]=null),this._indexBuffer&&(this._scene.getEngine()._releaseBuffer(this._indexBuffer),this._indexBuffer=null),this.texture&&(this.texture.dispose(),this.texture=null),this.renderTargetTextures=[];a=this._scene.layers.indexOf(this);this._scene.layers.splice(a,1),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onAfterRenderObservable.clear(),this.onBeforeRenderObservable.clear()},a}(),sj=function(){function a(a,b,c,d,e){this.size=a,this.position=b,this.alphaMode=r.a.ALPHA_ONEONE,this.color=c||new h.a(1,1,1),this.texture=d?new U.a(d,e.getScene(),!0):null,this._system=e,e.lensFlares.push(this)}return a.AddFlare=function(b,c,d,e,f){return new a(b,c,d,e,f)},a.prototype.dispose=function(){this.texture&&this.texture.dispose();var a=this._system.lensFlares.indexOf(this);this._system.lensFlares.splice(a,1)},a}();a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec4 color; void main(void) { vec4 baseColor=texture2D(textureSampler,vUV); gl_FragColor=baseColor*color; }";X.a.ShadersStore.lensFlarePixelShader=a;wi=" attribute vec2 position; uniform mat4 viewportMatrix; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vUV=position*madd+madd; gl_Position=viewportMatrix*vec4(position,0.0,1.0); }";X.a.ShadersStore.lensFlareVertexShader=wi;var tj=function(){function a(b,c,d){this.name=b,this.lensFlares=new Array,this.borderLimit=300,this.viewportBorder=0,this.layerMask=268435455,this._vertexBuffers={},this._isEnabled=!0,this._scene=d||C.a.LastCreatedScene,a._SceneComponentInitialization(this._scene),this._emitter=c,this.id=b,d.lensFlareSystems.push(this),this.meshesSelectionPredicate=function(a){return d.activeCamera&&a.material&&a.isVisible&&a.isEnabled()&&a.isBlocker&&0!=(a.layerMask&d.activeCamera.layerMask)};c=d.getEngine();this._drawWrapper=new Ec.a(c);b=[];b.push(1,1),b.push(-1,1),b.push(-1,-1),b.push(1,-1),this._vertexBuffers[W.b.PositionKind]=new W.b(c,b,W.b.PositionKind,!1,!1,2),this._createIndexBuffer(),this._drawWrapper.effect=c.createEffect("lensFlare",[W.b.PositionKind],["color","viewportMatrix"],["textureSampler"],"")}return a.prototype._createIndexBuffer=function(){var a=[];a.push(0),a.push(1),a.push(2),a.push(0),a.push(2),a.push(3),this._indexBuffer=this._scene.getEngine().createIndexBuffer(a)},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._isEnabled},set:function(a){this._isEnabled=a},enumerable:!1,configurable:!0}),a.prototype.getScene=function(){return this._scene},a.prototype.getEmitter=function(){return this._emitter},a.prototype.setEmitter=function(a){this._emitter=a},a.prototype.getEmitterPosition=function(){return this._emitter.getAbsolutePosition?this._emitter.getAbsolutePosition():this._emitter.position},a.prototype.computeEffectivePosition=function(a){var b=this.getEmitterPosition();b=g.e.Project(b,g.a.Identity(),this._scene.getTransformMatrix(),a),this._positionX=b.x,this._positionY=b.y,b=g.e.TransformCoordinates(this.getEmitterPosition(),this._scene.getViewMatrix()),this.viewportBorder>0&&(a.x-=this.viewportBorder,a.y-=this.viewportBorder,a.width+=2*this.viewportBorder,a.height+=2*this.viewportBorder,b.x+=this.viewportBorder,b.y+=this.viewportBorder,this._positionX+=this.viewportBorder,this._positionY+=this.viewportBorder);var c=this._scene.useRightHandedSystem;return!!(b.z>0&&!c||b.z<0&&c)&&(this._positionX>a.x&&this._positionXa.y&&(this._positionY,a.y,a.height),!0)},a.prototype._isVisible=function(){if(!this._isEnabled||!this._scene.activeCamera)return!1;var a=this.getEmitterPosition().subtract(this._scene.activeCamera.globalPosition),b=a.length();a.normalize();a=new nc.a(this._scene.activeCamera.globalPosition,a);a=this._scene.pickWithRay(a,this.meshesSelectionPredicate,!0);return!a||!a.hit||a.distance>b},a.prototype.render=function(){if(!this._drawWrapper.effect.isReady()||!this._scene.activeCamera)return!1;var a,b,c=this._scene.getEngine(),d=this._scene.activeCamera.viewport.toGlobal(c.getRenderWidth(!0),c.getRenderHeight(!0));if(!this.computeEffectivePosition(d))return!1;if(!this._isVisible())return!1;a=(a=this._positionXd.x+d.width-this.borderLimit?this._positionX-d.x-d.width+this.borderLimit:0)>(b=this._positionYd.y+d.height-this.borderLimit?this._positionY-d.y-d.height+this.borderLimit:0)?a:b;(a-=this.viewportBorder)>this.borderLimit&&(a=this.borderLimit);b=1-H.a.Clamp(a/this.borderLimit,0,1);if(b<0)return!1;b>1&&(b=1),this.viewportBorder>0&&(d.x+=this.viewportBorder,d.y+=this.viewportBorder,d.width-=2*this.viewportBorder,d.height-=2*this.viewportBorder,this._positionX-=this.viewportBorder,this._positionY-=this.viewportBorder);a=d.x+d.width/2;var e=d.y+d.height/2,f=a-this._positionX,h=e-this._positionY;c.enableEffect(this._drawWrapper),c.setState(!1),c.setDepthBuffer(!1),c.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect);for(var i=0;i0);for(var c=0,d=b;c0)}},a}();tj._SceneComponentInitialization=function(a){var b=a._getComponent(Oa.a.NAME_LENSFLARESYSTEM);b||(b=new uj(a),a._addComponent(b))};V=" float bayerDither2(vec2 _P) { return mod(2.0*_P.y+_P.x+1.0,4.0); } float bayerDither4(vec2 _P) { vec2 P1=mod(_P,2.0); vec2 P2=floor(0.5*mod(_P,4.0)); return 4.0*bayerDither2(P1)+bayerDither2(P2); } float bayerDither8(vec2 _P) { vec2 P1=mod(_P,2.0); vec2 P2=floor(0.5*mod(_P,4.0)); vec2 P4=floor(0.25*mod(_P,8.0)); return 4.0*(4.0*bayerDither2(P1)+bayerDither2(P2))+bayerDither2(P4); } ";X.a.IncludesShadersStore.bayerDitherFunctions=V;a="#if SM_FLOAT == 0 #include #endif #if SM_SOFTTRANSPARENTSHADOW == 1 #include uniform float softTransparentShadowSM; #endif varying float vDepthMetricSM; #if SM_USEDISTANCE == 1 uniform vec3 lightDataSM; varying vec3 vPositionWSM; #endif uniform vec3 biasAndScaleSM; uniform vec2 depthValuesSM; #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 varying float zSM; #endif ";X.a.IncludesShadersStore.shadowMapFragmentExtraDeclaration=a;wi=" float depthSM=vDepthMetricSM; #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 #if SM_USEDISTANCE == 1 depthSM=(length(vPositionWSM-lightDataSM)+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else #ifdef USE_REVERSE_DEPTHBUFFER depthSM=(-zSM+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else depthSM=(zSM+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #endif #ifdef USE_REVERSE_DEPTHBUFFER gl_FragDepth=clamp(1.0-depthSM,0.0,1.0); #else gl_FragDepth=clamp(depthSM,0.0,1.0); #endif #elif SM_USEDISTANCE == 1 depthSM=(length(vPositionWSM-lightDataSM)+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #if SM_ESM == 1 depthSM=clamp(exp(-min(87.,biasAndScaleSM.z*depthSM)),0.,1.); #endif #if SM_FLOAT == 1 gl_FragColor=vec4(depthSM,1.0,1.0,1.0); #else gl_FragColor=pack(depthSM); #endif return;";X.a.IncludesShadersStore.shadowMapFragment=wi;V="#include #ifdef ALPHATEST varying vec2 vUV; uniform sampler2D diffuseSampler; #endif #include void main(void) { #include #ifdef ALPHATEST float alphaFromAlphaTexture=texture2D(diffuseSampler,vUV).a; if (alphaFromAlphaTexture<0.4) discard; #endif #if SM_SOFTTRANSPARENTSHADOW == 1 #ifdef ALPHATEST if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM*alphaFromAlphaTexture) discard; #else if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM) discard; #endif #endif #include }";X.a.ShadersStore.shadowMapPixelShader=V;a="uniform mat4 viewProjection; #ifdef MULTIVIEW uniform mat4 viewProjectionR; #endif uniform mat4 view; uniform mat4 projection; uniform vec4 vEyePosition; ";X.a.IncludesShadersStore.sceneVertexDeclaration=a;X.a.IncludesShadersStore.meshVertexDeclaration="uniform mat4 world; uniform float visibility; ";X.a.IncludesShadersStore.shadowMapVertexDeclaration="#include #include ";wi="layout(std140,column_major) uniform; #include #include ";X.a.IncludesShadersStore.shadowMapUboDeclaration=wi;V="#if SM_NORMALBIAS == 1 uniform vec3 lightDataSM; #endif uniform vec3 biasAndScaleSM; uniform vec2 depthValuesSM; varying float vDepthMetricSM; #if SM_USEDISTANCE == 1 varying vec3 vPositionWSM; #endif #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 varying float zSM; #endif ";X.a.IncludesShadersStore.shadowMapVertexExtraDeclaration=V;a=" #if SM_NORMALBIAS == 1 #if SM_DIRECTIONINLIGHTDATA == 1 vec3 worldLightDirSM=normalize(-lightDataSM.xyz); #else vec3 directionToLightSM=lightDataSM.xyz-worldPos.xyz; vec3 worldLightDirSM=normalize(directionToLightSM); #endif float ndlSM=dot(vNormalW,worldLightDirSM); float sinNLSM=sqrt(1.0-ndlSM*ndlSM); float normalBiasSM=biasAndScaleSM.y*sinNLSM; worldPos.xyz-=vNormalW*normalBiasSM; #endif ";X.a.IncludesShadersStore.shadowMapVertexNormalBias=a;wi="#if SM_USEDISTANCE == 1 vPositionWSM=worldPos.xyz; #endif #if SM_DEPTHTEXTURE == 1 #ifdef IS_NDC_HALF_ZRANGE #define BIASFACTOR 0.5 #else #define BIASFACTOR 1.0 #endif #ifdef USE_REVERSE_DEPTHBUFFER gl_Position.z-=biasAndScaleSM.x*gl_Position.w*BIASFACTOR; #else gl_Position.z+=biasAndScaleSM.x*gl_Position.w*BIASFACTOR; #endif #endif #if defined(SM_DEPTHCLAMP) && SM_DEPTHCLAMP == 1 zSM=gl_Position.z; gl_Position.z=0.0; #elif SM_USEDISTANCE == 0 #ifdef USE_REVERSE_DEPTHBUFFER vDepthMetricSM=(-gl_Position.z+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #else vDepthMetricSM=(gl_Position.z+depthValuesSM.x)/depthValuesSM.y+biasAndScaleSM.x; #endif #endif ";X.a.IncludesShadersStore.shadowMapVertexMetric=wi;V=" attribute vec3 position; #ifdef NORMAL attribute vec3 normal; #endif #include #include #include[0..maxSimultaneousMorphTargets] #ifdef INSTANCES attribute vec4 world0; attribute vec4 world1; attribute vec4 world2; attribute vec4 world3; #endif #include #include<__decl__shadowMapVertex> #ifdef ALPHATEST varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #include #include void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #ifdef NORMAL vec3 normalUpdated=normal; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include vec4 worldPos=finalWorld*vec4(positionUpdated,1.0); #ifdef NORMAL mat3 normWorldSM=mat3(finalWorld); #if defined(INSTANCES) && defined(THIN_INSTANCES) vec3 vNormalW=normalUpdated/vec3(dot(normWorldSM[0],normWorldSM[0]),dot(normWorldSM[1],normWorldSM[1]),dot(normWorldSM[2],normWorldSM[2])); vNormalW=normalize(normWorldSM*vNormalW); #else #ifdef NONUNIFORMSCALING normWorldSM=transposeMat3(inverseMat3(normWorldSM)); #endif vec3 vNormalW=normalize(normWorldSM*normalUpdated); #endif #endif #include gl_Position=viewProjection*worldPos; #include #ifdef ALPHATEST #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #include }";X.a.ShadersStore.shadowMapVertexShader=V;a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform vec2 screenSize; void main(void) { vec4 colorDepth=vec4(0.0); for (int x=-OFFSET; x<=OFFSET; x++) for (int y=-OFFSET; y<=OFFSET; y++) colorDepth+=texture2D(textureSampler,vUV+vec2(x,y)/screenSize); gl_FragColor=(colorDepth/float((OFFSET*2+1)*(OFFSET*2+1))); }";X.a.ShadersStore.depthBoxBlurPixelShader=a;wi="#if SM_SOFTTRANSPARENTSHADOW == 1 if ((bayerDither8(floor(mod(gl_FragCoord.xy,8.0))))/64.0>=softTransparentShadowSM*alpha) discard; #endif ";X.a.IncludesShadersStore.shadowMapFragmentSoftTransparentShadow=wi;var vj=function(){function a(b,c,d){if(this.onBeforeShadowMapRenderObservable=new f.c,this.onAfterShadowMapRenderObservable=new f.c,this.onBeforeShadowMapRenderMeshObservable=new f.c,this.onAfterShadowMapRenderMeshObservable=new f.c,this._bias=5e-5,this._normalBias=0,this._blurBoxOffset=1,this._blurScale=2,this._blurKernel=1,this._useKernelBlur=!1,this._filter=a.FILTER_NONE,this._filteringQuality=a.QUALITY_HIGH,this._contactHardeningLightSizeUVRatio=.1,this._darkness=0,this._transparencyShadow=!1,this.enableSoftTransparentShadow=!1,this.frustumEdgeFalloff=0,this.forceBackFacesOnly=!1,this._lightDirection=g.e.Zero(),this._viewMatrix=g.a.Zero(),this._projectionMatrix=g.a.Zero(),this._transformMatrix=g.a.Zero(),this._cachedPosition=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._cachedDirection=new g.e(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),this._currentFaceIndex=0,this._currentFaceIndexCache=0,this._defaultTextureMatrix=g.a.Identity(),this._mapSize=b,this._light=c,this._scene=c.getScene(),c._shadowGenerator=this,this.id=c.id,this._useUBO=this._scene.getEngine().supportsUniformBuffers,this._nameForDrawWrapper=[r.a.SUBMESH_DRAWWRAPPER_SHADOWGENERATOR_PREFIX+a._Counter++],c.needCube())for(b=this._nameForDrawWrapper[0],c=0;c<6;++c)this._nameForDrawWrapper[c]=b+"_"+c;a._SceneComponentInitialization(this._scene);b=this._scene.getEngine().getCaps();d?b.textureFloatRender&&b.textureFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_FLOAT:b.textureHalfFloatRender&&b.textureHalfFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_HALF_FLOAT:this._textureType=r.a.TEXTURETYPE_UNSIGNED_INT:b.textureHalfFloatRender&&b.textureHalfFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_HALF_FLOAT:b.textureFloatRender&&b.textureFloatLinearFiltering?this._textureType=r.a.TEXTURETYPE_FLOAT:this._textureType=r.a.TEXTURETYPE_UNSIGNED_INT,this._initializeGenerator(),this._applyFilterValues(),this._nameForDrawWrapperCurrent=this._nameForDrawWrapper[0]}return Object.defineProperty(a.prototype,"bias",{get:function(){return this._bias},set:function(a){this._bias=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"normalBias",{get:function(){return this._normalBias},set:function(a){this._normalBias=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurBoxOffset",{get:function(){return this._blurBoxOffset},set:function(a){this._blurBoxOffset!==a&&(this._blurBoxOffset=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurScale",{get:function(){return this._blurScale},set:function(a){this._blurScale!==a&&(this._blurScale=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"blurKernel",{get:function(){return this._blurKernel},set:function(a){this._blurKernel!==a&&(this._blurKernel=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useKernelBlur",{get:function(){return this._useKernelBlur},set:function(a){this._useKernelBlur!==a&&(this._useKernelBlur=a,this._disposeBlurPostProcesses())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"depthScale",{get:function(){return void 0!==this._depthScale?this._depthScale:this._light.getDepthScale()},set:function(a){this._depthScale=a},enumerable:!1,configurable:!0}),a.prototype._validateFilter=function(a){return a},Object.defineProperty(a.prototype,"filter",{get:function(){return this._filter},set:function(b){if(b=this._validateFilter(b),this._light.needCube()){if(b===a.FILTER_BLUREXPONENTIALSHADOWMAP)return void (this.useExponentialShadowMap=!0);if(b===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP)return void (this.useCloseExponentialShadowMap=!0);if(b===a.FILTER_PCF||b===a.FILTER_PCSS)return void (this.usePoissonSampling=!0)}b!==a.FILTER_PCF&&b!==a.FILTER_PCSS||this._scene.getEngine()._features.supportShadowSamplers?this._filter!==b&&(this._filter=b,this._disposeBlurPostProcesses(),this._applyFilterValues(),this._light._markMeshesAsLightDirty()):this.usePoissonSampling=!0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"usePoissonSampling",{get:function(){return this.filter===a.FILTER_POISSONSAMPLING},set:function(b){var c=this._validateFilter(a.FILTER_POISSONSAMPLING);(b||this.filter===a.FILTER_POISSONSAMPLING)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useExponentialShadowMap",{get:function(){return this.filter===a.FILTER_EXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_EXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_EXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useBlurExponentialShadowMap",{get:function(){return this.filter===a.FILTER_BLUREXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_BLUREXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_BLUREXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useCloseExponentialShadowMap",{get:function(){return this.filter===a.FILTER_CLOSEEXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_CLOSEEXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_CLOSEEXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useBlurCloseExponentialShadowMap",{get:function(){return this.filter===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP},set:function(b){var c=this._validateFilter(a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP);(b||this.filter===a.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"usePercentageCloserFiltering",{get:function(){return this.filter===a.FILTER_PCF},set:function(b){var c=this._validateFilter(a.FILTER_PCF);(b||this.filter===a.FILTER_PCF)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"filteringQuality",{get:function(){return this._filteringQuality},set:function(a){this._filteringQuality!==a&&(this._filteringQuality=a,this._disposeBlurPostProcesses(),this._applyFilterValues(),this._light._markMeshesAsLightDirty())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useContactHardeningShadow",{get:function(){return this.filter===a.FILTER_PCSS},set:function(b){var c=this._validateFilter(a.FILTER_PCSS);(b||this.filter===a.FILTER_PCSS)&&(this.filter=b?c:a.FILTER_NONE)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"contactHardeningLightSizeUVRatio",{get:function(){return this._contactHardeningLightSizeUVRatio},set:function(a){this._contactHardeningLightSizeUVRatio=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"darkness",{get:function(){return this._darkness},set:function(a){this.setDarkness(a)},enumerable:!1,configurable:!0}),a.prototype.getDarkness=function(){return this._darkness},a.prototype.setDarkness=function(a){return this._darkness=a>=1?1:a<=0?0:a,this},Object.defineProperty(a.prototype,"transparencyShadow",{get:function(){return this._transparencyShadow},set:function(a){this.setTransparencyShadow(a)},enumerable:!1,configurable:!0}),a.prototype.setTransparencyShadow=function(a){return this._transparencyShadow=a,this},a.prototype.getShadowMap=function(){return this._shadowMap},a.prototype.getShadowMapForRendering=function(){return this._shadowMap2?this._shadowMap2:this._shadowMap},a.prototype.getClassName=function(){return a.CLASSNAME},a.prototype.addShadowCaster=function(a,b){if(void 0===b&&(b=!0),!this._shadowMap)return this;if(this._shadowMap.renderList||(this._shadowMap.renderList=[]),-1===this._shadowMap.renderList.indexOf(a)&&this._shadowMap.renderList.push(a),b)for(b=0,a=a.getChildMeshes();b=e.length)return void (a&&a(c));setTimeout(i,16)}};i()}else a&&a(this)}else a&&a(this)}else a&&a(this)},a.prototype.forceCompilationAsync=function(a){var b=this;return new Promise(function(c){b.forceCompilation(function(){c()},a)})},a.prototype._isReadyCustomDefines=function(a,b,c){},a.prototype._prepareShadowDefines=function(a,b,c,d){c.push("#define SM_FLOAT "+(this._textureType!==r.a.TEXTURETYPE_UNSIGNED_INT?"1":"0")),c.push("#define SM_ESM "+(this.useExponentialShadowMap||this.useBlurExponentialShadowMap?"1":"0")),c.push("#define SM_DEPTHTEXTURE "+(this.usePercentageCloserFiltering||this.useContactHardeningShadow?"1":"0"));var e=a.getMesh();return c.push("#define SM_NORMALBIAS "+(this.normalBias&&e.isVerticesDataPresent(W.b.NormalKind)?"1":"0")),c.push("#define SM_DIRECTIONINLIGHTDATA "+(this.getLight().getTypeID()===Kh.a.LIGHTTYPEID_DIRECTIONALLIGHT?"1":"0")),c.push("#define SM_USEDISTANCE "+(this._light.needCube()?"1":"0")),c.push("#define SM_SOFTTRANSPARENTSHADOW "+(this.enableSoftTransparentShadow&&d?"1":"0")),this._isReadyCustomDefines(c,a,b),c},a.prototype.isReady=function(a,b,c){var d=a.getMaterial(),e=null==d?void 0:d.shadowDepthWrapper,f=[];if(this._prepareShadowDefines(a,b,f,c),e){if(!e.isReadyForSubMesh(a,f,this,b,this._nameForDrawWrapperCurrent))return!1}else{c=a._getDrawWrapper(this._nameForDrawWrapperCurrent,!0);e=c.effect;var g=c.defines,h=[W.b.PositionKind],i=a.getMesh();if(this.normalBias&&i.isVerticesDataPresent(W.b.NormalKind)&&(h.push(W.b.NormalKind),f.push("#define NORMAL"),i.nonUniformScaling&&f.push("#define NONUNIFORMSCALING")),d&&d.needAlphaTesting()){d=d.getAlphaTestTexture();if(d){if(!d.isReady())return!1;f.push("#define ALPHATEST"),i.isVerticesDataPresent(W.b.UVKind)&&(h.push(W.b.UVKind),f.push("#define UV1")),i.isVerticesDataPresent(W.b.UV2Kind)&&1===d.coordinatesIndex&&(h.push(W.b.UV2Kind),f.push("#define UV2"))}}d=new bi.a;if(i.useBones&&i.computeBonesUsingShaders&&i.skeleton){h.push(W.b.MatricesIndicesKind),h.push(W.b.MatricesWeightsKind),i.numBoneInfluencers>4&&(h.push(W.b.MatricesIndicesExtraKind),h.push(W.b.MatricesWeightsExtraKind));var j=i.skeleton;f.push("#define NUM_BONE_INFLUENCERS "+i.numBoneInfluencers),i.numBoneInfluencers>0&&d.addCPUSkinningFallback(0,i),j.isUsingTextureForMatrices?f.push("#define BONETEXTURE"):f.push("#define BonesPerMesh "+(j.bones.length+1))}else f.push("#define NUM_BONE_INFLUENCERS 0");j=i.morphTargetManager;var k=0;j&&j.numInfluencers>0&&(f.push("#define MORPHTARGETS"),k=j.numInfluencers,f.push("#define NUM_MORPH_INFLUENCERS "+k),j.isUsingTextureForTargets&&f.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(h,i,k));j=this._scene;if(j.clipPlane&&f.push("#define CLIPPLANE"),j.clipPlane2&&f.push("#define CLIPPLANE2"),j.clipPlane3&&f.push("#define CLIPPLANE3"),j.clipPlane4&&f.push("#define CLIPPLANE4"),j.clipPlane5&&f.push("#define CLIPPLANE5"),j.clipPlane6&&f.push("#define CLIPPLANE6"),b&&(f.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(h),a.getRenderingMesh().hasThinInstances&&f.push("#define THIN_INSTANCES")),this.customShaderOptions&&this.customShaderOptions.defines)for(i=0,j=this.customShaderOptions.defines;i #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; uniform vec2 depthValues; #if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif varying float vDepthMetric; void main(void) { vec3 positionUpdated=position; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #ifdef USE_REVERSE_DEPTHBUFFER vDepthMetric=((-gl_Position.z+depthValues.x)/(depthValues.y)); #else vDepthMetric=((gl_Position.z+depthValues.x)/(depthValues.y)); #endif #if defined(ALPHATEST) || defined(BASIC_RENDER) #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif } ";X.a.ShadersStore.depthVertexShader=a;var wj=function(){function a(b,c,d,e,f){var g=this;void 0===c&&(c=r.a.TEXTURETYPE_FLOAT),void 0===d&&(d=null),void 0===e&&(e=!1),void 0===f&&(f=U.a.TRILINEAR_SAMPLINGMODE),this.enabled=!0,this.forceDepthWriteTransparentMeshes=!1,this.useOnlyInActiveCamera=!1,this._scene=b,this._storeNonLinearDepth=e,this.isPacked=c===r.a.TEXTURETYPE_UNSIGNED_BYTE,this.isPacked?this._clearColor=new h.b(1,1,1,1):this._clearColor=new h.b(1,0,0,1),a._SceneComponentInitialization(this._scene),this._nameForDrawWrapper=r.a.SUBMESH_DRAWWRAPPER_DEPTHRENDERER_PREFIX+a._Counter++,this._camera=d;var i=b.getEngine();f!==U.a.NEAREST_SAMPLINGMODE&&(c!==r.a.TEXTURETYPE_FLOAT||i._caps.textureFloatLinearFiltering||(f=U.a.NEAREST_SAMPLINGMODE),c!==r.a.TEXTURETYPE_HALF_FLOAT||i._caps.textureHalfFloatLinearFiltering||(f=U.a.NEAREST_SAMPLINGMODE));e=this.isPacked||!i._features.supportExtendedTextureFormats?r.a.TEXTUREFORMAT_RGBA:r.a.TEXTUREFORMAT_R;this._depthMap=new cd("depthMap",{width:i.getRenderWidth(),height:i.getRenderHeight()},this._scene,!1,!0,c,!1,f,void 0,void 0,void 0,e),this._depthMap.wrapU=U.a.CLAMP_ADDRESSMODE,this._depthMap.wrapV=U.a.CLAMP_ADDRESSMODE,this._depthMap.refreshRate=1,this._depthMap.renderParticles=!1,this._depthMap.renderList=null,this._depthMap.activeCamera=this._camera,this._depthMap.ignoreCameraViewport=!0,this._depthMap.useCameraPostProcesses=!1,this._depthMap.onClearObservable.add(function(a){a.clear(g._clearColor,!0,!0,!0)}),this._depthMap.onBeforeBindObservable.add(function(){var a;null===(a=i._debugPushGroup)||void 0===a||a.call(i,"depth renderer",1)}),this._depthMap.onAfterUnbindObservable.add(function(){var a;null===(a=i._debugPopGroup)||void 0===a||a.call(i,1)});var j=function(a){var b,c=a.getRenderingMesh(),d=a.getEffectiveMesh(),e=g._scene,f=e.getEngine(),h=a.getMaterial();if(d._internalAbstractMeshDataInfo._isActiveIntermediate=!1,h&&!d.infiniteDistance&&!h.disableDepthWrite&&0!==a.verticesCount&&a._renderId!==e.getRenderId()){var i=d._getWorldMatrixDeterminant()<0;b=null!==(b=c.overrideMaterialSideOrientation)&&void 0!==b?b:h.sideOrientation;(e.useRightHandedSystem&&!i||!e.useRightHandedSystem&&i)&&(b=b===r.a.MATERIAL_ClockWiseSideOrientation?r.a.MATERIAL_CounterClockWiseSideOrientation:r.a.MATERIAL_ClockWiseSideOrientation);i=b===r.a.MATERIAL_ClockWiseSideOrientation;f.setState(h.backFaceCulling,0,!1,i,h.cullBackFaces);b=c._getInstancesRenderList(a._id,!!a.getReplacementMesh());if(!b.mustReturn){i=f.getCaps().instancedArrays&&(null!==b.visibleInstances[a._id]&&void 0!==b.visibleInstances[a._id]||c.hasThinInstances);var j=g._camera||e.activeCamera;if(g.isReady(a,i)&&j){a._renderId=e.getRenderId();var k=a._getDrawWrapper(g._nameForDrawWrapper),l=Ec.a.GetEffect(k),m=j.mode===db.a.ORTHOGRAPHIC_CAMERA;f.enableEffect(k),i||c._bind(a,l,h.fillMode),l.setMatrix("viewProjection",e.getTransformMatrix()),l.setMatrix("world",d.getWorldMatrix());if(m?(k=!f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:1,e=f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:1):(k=f.useReverseDepthBuffer&&f.isNDCHalfZRange?j.minZ:f.isNDCHalfZRange?0:j.minZ,e=f.useReverseDepthBuffer&&f.isNDCHalfZRange?0:j.maxZ),l.setFloat2("depthValues",k,k+e),h&&h.needAlphaTesting()){m=h.getAlphaTestTexture();m&&(l.setTexture("diffuseSampler",m),l.setMatrix("diffuseMatrix",m.getTextureMatrix()))}if(c.useBones&&c.computeBonesUsingShaders&&c.skeleton){f=c.skeleton;if(f.isUsingTextureForMatrices){j=f.getTransformMatrixTexture(c);if(!j)return;l.setTexture("boneSampler",j),l.setFloat("boneTextureWidth",4*(f.bones.length+1))}else l.setMatrices("mBones",f.getTransformMatrices(c))}Yh.a.BindMorphTargetParameters(c,l),c.morphTargetManager&&c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(l),c._processRendering(d,a,l,h.fillMode,b,i,function(a,b){return l.setMatrix("world",b)})}}}};this._depthMap.customRenderFunction=function(a,b,c,d){var e;if(d.length)for(e=0;e4&&(i.push(W.b.MatricesIndicesExtraKind),i.push(W.b.MatricesWeightsExtraKind)),d.push("#define NUM_BONE_INFLUENCERS "+j.numBoneInfluencers),d.push("#define BonesPerMesh "+(j.skeleton?j.skeleton.bones.length+1:0));c=a.getRenderingMesh().skeleton;(null==c?void 0:c.isUsingTextureForMatrices)&&d.push("#define BONETEXTURE")}else d.push("#define NUM_BONE_INFLUENCERS 0");c=j.morphTargetManager;var k=0;c&&c.numInfluencers>0&&(k=c.numInfluencers,d.push("#define MORPHTARGETS"),d.push("#define NUM_MORPH_INFLUENCERS "+k),c.isUsingTextureForTargets&&d.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(i,j,k)),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(i),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES")),this._storeNonLinearDepth&&d.push("#define NONLINEARDEPTH"),this.isPacked&&d.push("#define PACKED");c=d.join(" ");return h!==c&&(h=c,g=f.createEffect("depth",i,["world","mBones","boneTextureWidth","viewProjection","diffuseMatrix","depthValues","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","morphTargets","boneSampler"],c,void 0,void 0,void 0,{maxSimultaneousMorphTargets:k})),e.setEffect(g,h),g.isReady()},a.prototype.getDepthMap=function(){return this._depthMap},a.prototype.dispose=function(){this._depthMap.dispose()},a._Counter=0,a._SceneComponentInitialization=function(a){throw Object(La.a)("DepthRendererSceneComponent")},a}();wi="varying vec2 vUV; uniform sampler2D textureSampler; #if defined(INITIAL) uniform sampler2D sourceTexture; uniform vec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*(texSize-1.0)); float f1=texelFetch(sourceTexture,coord,0).r; float f2=texelFetch(sourceTexture,coord+ivec2(1,0),0).r; float f3=texelFetch(sourceTexture,coord+ivec2(1,1),0).r; float f4=texelFetch(sourceTexture,coord+ivec2(0,1),0).r; float minz=min(min(min(f1,f2),f3),f4); #ifdef DEPTH_REDUX float maxz=max(max(max(sign(1.0-f1)*f1,sign(1.0-f2)*f2),sign(1.0-f3)*f3),sign(1.0-f4)*f4); #else float maxz=max(max(max(f1,f2),f3),f4); #endif glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(MAIN) uniform vec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*(texSize-1.0)); vec2 f1=texelFetch(textureSampler,coord,0).rg; vec2 f2=texelFetch(textureSampler,coord+ivec2(1,0),0).rg; vec2 f3=texelFetch(textureSampler,coord+ivec2(1,1),0).rg; vec2 f4=texelFetch(textureSampler,coord+ivec2(0,1),0).rg; float minz=min(min(min(f1.x,f2.x),f3.x),f4.x); float maxz=max(max(max(f1.y,f2.y),f3.y),f4.y); glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(ONEBEFORELAST) uniform ivec2 texSize; void main(void) { ivec2 coord=ivec2(vUV*vec2(texSize-1)); vec2 f1=texelFetch(textureSampler,coord % texSize,0).rg; vec2 f2=texelFetch(textureSampler,(coord+ivec2(1,0)) % texSize,0).rg; vec2 f3=texelFetch(textureSampler,(coord+ivec2(1,1)) % texSize,0).rg; vec2 f4=texelFetch(textureSampler,(coord+ivec2(0,1)) % texSize,0).rg; float minz=min(f1.x,f2.x); float maxz=max(f1.y,f2.y); glFragColor=vec4(minz,maxz,0.,0.); } #elif defined(LAST) void main(void) { glFragColor=vec4(0.); discard; } #endif ";X.a.ShadersStore.minmaxReduxPixelShader=wi;var xj=function(){function a(a){var b=this;this.onAfterReductionPerformed=new f.c,this._forceFullscreenViewport=!0,this._activated=!1,this._camera=a,this._postProcessManager=new ad.a(a.getScene()),this._onContextRestoredObserver=a.getEngine().onContextRestoredObservable.add(function(){b._postProcessManager._rebuild()})}return Object.defineProperty(a.prototype,"sourceTexture",{get:function(){return this._sourceTexture},enumerable:!1,configurable:!0}),a.prototype.setSourceTexture=function(a,b,c,d){var e=this;if(void 0===c&&(c=r.a.TEXTURETYPE_HALF_FLOAT),void 0===d&&(d=!0),a!==this._sourceTexture){this.dispose(!1),this._sourceTexture=a,this._reductionSteps=[],this._forceFullscreenViewport=d;var f=this._camera.getScene();a=new Fc("Initial reduction phase","minmaxRedux",["texSize"],["sourceTexture"],1,null,r.a.TEXTURE_NEAREST_NEAREST,f.getEngine(),!1,"#define INITIAL"+(b?" #define DEPTH_REDUX":""),c,void 0,void 0,void 0,r.a.TEXTUREFORMAT_RG);a.autoClear=!1,a.forceFullscreenViewport=d;b=this._sourceTexture.getRenderWidth();var g=this._sourceTexture.getRenderHeight();a.onApply=function(a,b){return function(c){c.setTexture("sourceTexture",e._sourceTexture),c.setFloat2("texSize",a,b)}}(b,g),this._reductionSteps.push(a);for(a=1;b>1||g>1;){b=Math.max(Math.round(b/2),1),g=Math.max(Math.round(g/2),1);var h=new Fc("Reduction phase "+a,"minmaxRedux",["texSize"],null,{width:b,height:g},null,r.a.TEXTURE_NEAREST_NEAREST,f.getEngine(),!1,"#define "+(1==b&&1==g?"LAST":1==b||1==g?"ONEBEFORELAST":"MAIN"),c,void 0,void 0,void 0,r.a.TEXTUREFORMAT_RG);(h.autoClear=!1,h.forceFullscreenViewport=d,h.onApply=function(a,b){return function(c){1==a||1==b?c.setInt2("texSize",a,b):c.setFloat2("texSize",a,b)}}(b,g),this._reductionSteps.push(h),a++,1==b&&1==g)&&h.onAfterRenderObservable.add(function(a,b,c){var d=new Float32Array(4*a*b),g={min:0,max:0};return function(){f.getEngine()._readTexturePixels(c.inputTexture.texture,a,b,-1,0,d,!1),g.min=d[0],g.max=d[1],e.onAfterReductionPerformed.notifyObservers(g)}}(b,g,h))}}},Object.defineProperty(a.prototype,"refreshRate",{get:function(){return this._sourceTexture?this._sourceTexture.refreshRate:-1},set:function(a){this._sourceTexture&&(this._sourceTexture.refreshRate=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"activated",{get:function(){return this._activated},enumerable:!1,configurable:!0}),a.prototype.activate=function(){var a=this;!this._onAfterUnbindObserver&&this._sourceTexture&&(this._onAfterUnbindObserver=this._sourceTexture.onAfterUnbindObservable.add(function(){var b,c=a._camera.getScene().getEngine();null===(b=c._debugPushGroup)||void 0===b||b.call(c,"min max reduction",1),a._reductionSteps[0].activate(a._camera),a._postProcessManager.directRender(a._reductionSteps,a._reductionSteps[0].inputTexture,a._forceFullscreenViewport),c.unBindFramebuffer(a._reductionSteps[0].inputTexture,!1),null===(b=c._debugPopGroup)||void 0===b||b.call(c,1)}),this._activated=!0)},a.prototype.deactivate=function(){this._onAfterUnbindObserver&&this._sourceTexture&&(this._sourceTexture.onAfterUnbindObservable.remove(this._onAfterUnbindObserver),this._onAfterUnbindObserver=null,this._activated=!1)},a.prototype.dispose=function(a){if(void 0===a&&(a=!0),a&&(this.onAfterReductionPerformed.clear(),this._onContextRestoredObserver&&(this._camera.getEngine().onContextRestoredObservable.remove(this._onContextRestoredObserver),this._onContextRestoredObserver=null)),this.deactivate(),this._reductionSteps){for(var b=0;bb&&(a=0,b=1),a<0&&(a=0),b>1&&(b=1),this._minDistance=a,this._maxDistance=b,this._breaksAreDirty=!0)},Object.defineProperty(b.prototype,"minDistance",{get:function(){return this._minDistance},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"maxDistance",{get:function(){return this._maxDistance},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return b.CLASSNAME},b.prototype.getCascadeMinExtents=function(a){return a>=0&&a=0&&athis._scene.activeCamera.maxZ||(this._shadowMaxZ=a,this._light._markMeshesAsLightDirty(),this._breaksAreDirty=!0):this._shadowMaxZ=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"debug",{get:function(){return this._debug},set:function(a){this._debug=a,this._light._markMeshesAsLightDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"depthClamp",{get:function(){return this._depthClamp},set:function(a){this._depthClamp=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cascadeBlendPercentage",{get:function(){return this._cascadeBlendPercentage},set:function(a){this._cascadeBlendPercentage=a,this._light._markMeshesAsLightDirty()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lambda",{get:function(){return this._lambda},set:function(a){a=Math.min(Math.max(a,0),1);this._lambda!=a&&(this._lambda=a,this._breaksAreDirty=!0)},enumerable:!1,configurable:!0}),b.prototype.getCascadeViewMatrix=function(a){return a>=0&&a=0&&a=0&&a=a&&(c=0,a=1),c==b._minDistance&&a==b._maxDistance||b.setMinMaxDistance(c,a)}),this._depthReducer.setDepthRenderer(this._depthRenderer)),this._depthReducer.activate()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"autoCalcDepthBoundsRefreshRate",{get:function(){var a;return null!==(a=null===(a=null===(a=this._depthReducer)||void 0===a?void 0:a.depthRenderer)||void 0===a?void 0:a.getDepthMap().refreshRate)&&void 0!==a?a:-1},set:function(a){var b;(null===(b=this._depthReducer)||void 0===b?void 0:b.depthRenderer)&&(this._depthReducer.depthRenderer.getDepthMap().refreshRate=a)},enumerable:!1,configurable:!0}),b.prototype.splitFrustum=function(){this._breaksAreDirty=!0},b.prototype._splitFrustum=function(){var a=this._scene.activeCamera;if(a){for(var b=a.minZ,a=a.maxZ,c=a-b,d=this._minDistance,e=b+d*c,a=b+(this._shadowMaxZ=b?Math.min((this._shadowMaxZ-b)/(a-b),this._maxDistance):this._maxDistance)*c,f=a-e,a=a/e,g=0;gMath.PI;)e-=2*Math.PI;e=e/Math.PI;a=a/Math.PI;e=.5*e+.5;e=Math.round(e*c);e<0?e=0:e>=c&&(e=c-1);a=Math.round(a*d);a<0?a=0:a>=d&&(a=d-1);d=d-a-1;return{r:b[d*c*3+3*e+0],g:b[d*c*3+3*e+1],b:b[d*c*3+3*e+2]}},a.FACE_LEFT=[new g.e(-1,-1,-1),new g.e(1,-1,-1),new g.e(-1,1,-1),new g.e(1,1,-1)],a.FACE_RIGHT=[new g.e(1,-1,1),new g.e(-1,-1,1),new g.e(1,1,1),new g.e(-1,1,1)],a.FACE_FRONT=[new g.e(1,-1,-1),new g.e(1,-1,1),new g.e(1,1,-1),new g.e(1,1,1)],a.FACE_BACK=[new g.e(-1,-1,1),new g.e(-1,-1,-1),new g.e(-1,1,1),new g.e(-1,1,-1)],a.FACE_DOWN=[new g.e(1,1,-1),new g.e(1,1,1),new g.e(-1,1,-1),new g.e(-1,1,1)],a.FACE_UP=[new g.e(-1,-1,-1),new g.e(-1,-1,1),new g.e(1,-1,-1),new g.e(1,-1,1)],a}(),Lj=function(){function a(){}return a.Ldexp=function(a,b){return b>1023?a*Math.pow(2,1023)*Math.pow(2,b-1023):b<-1074?a*Math.pow(2,-1074)*Math.pow(2,b+1074):a*Math.pow(2,b)},a.Rgbe2float=function(a,b,c,d,e,f){e>0?(e=this.Ldexp(1,e-136),a[f+0]=b*e,a[f+1]=c*e,a[f+2]=d*e):(a[f+0]=0,a[f+1]=0,a[f+2]=0)},a.readStringLine=function(a,b){for(var c="",d="",e=b;e32767)throw"HDR Bad header format, unsupported size";return{height:a,width:d,dataPosition:e+=b.length+1}},a.GetCubeMapTextureData=function(a,b){a=new Uint8Array(a);var c=this.RGBE_ReadHeader(a);a=this.RGBE_ReadPixels(a,c);return Kj.ConvertPanoramaToCubemap(a,c.width,c.height,b)},a.RGBE_ReadPixels=function(a,b){return this.RGBE_ReadPixels_RLE(a,b)},a.RGBE_ReadPixels_RLE=function(a,b){for(var c,d,e,f,g,h=b.height,i=b.width,j=b.dataPosition,k=0,l=0,o=0,q=new ArrayBuffer(4*i),q=new Uint8Array(q),s=new ArrayBuffer(b.width*b.height*4*3),s=new Float32Array(s);h>0;){if(c=a[j++],d=a[j++],e=a[j++],f=a[j++],2!=c||2!=d||128&e||b.width<8||b.width>32767)return this.RGBE_ReadPixels_NOT_RLE(a,b);if((e<<8|f)!=i)throw"HDR Bad header format, wrong scan line width";for(k=0,o=0;o<4;o++)for(l=(o+1)*i;k128){if(0==(g=c-128)||g>l-k)throw"HDR Bad Format, bad scanline data (run)";for(;g-->0;)q[k++]=d}else{if(0==(g=c)||g>l-k)throw"HDR Bad Format, bad scanline data (non-run)";if(q[k++]=d,--g>0)for(var t=0;t0;){for(g=0;g #include #include #include uniform float alphaG; uniform samplerCube inputTexture; uniform vec2 vFilteringInfo; uniform float hdrScale; varying vec3 direction; void main() { vec3 color=radiance(alphaG,inputTexture,direction,vFilteringInfo); gl_FragColor=vec4(color*hdrScale,1.0); }";X.a.ShadersStore.hdrFilteringPixelShader=a;var Oj=function(){function a(a,b){void 0===b&&(b={}),this._lodGenerationOffset=0,this._lodGenerationScale=.8,this.quality=r.a.TEXTURE_FILTERING_QUALITY_OFFLINE,this.hdrScale=1,this._engine=a,this.hdrScale=b.hdrScale||this.hdrScale,this.quality=b.hdrScale||this.quality}return a.prototype._createRenderTarget=function(a){var b=r.a.TEXTURETYPE_UNSIGNED_BYTE;this._engine.getCaps().textureHalfFloatRender?b=r.a.TEXTURETYPE_HALF_FLOAT:this._engine.getCaps().textureFloatRender&&(b=r.a.TEXTURETYPE_FLOAT);a=this._engine.createRenderTargetCubeTexture(a,{format:r.a.TEXTUREFORMAT_RGBA,type:b,createMipMaps:!0,generateMipMaps:!1,generateDepthBuffer:!1,generateStencilBuffer:!1,samplingMode:r.a.TEXTURE_NEAREST_SAMPLINGMODE});return this._engine.updateTextureWrappingMode(a.texture,r.a.TEXTURE_CLAMP_ADDRESSMODE,r.a.TEXTURE_CLAMP_ADDRESSMODE,r.a.TEXTURE_CLAMP_ADDRESSMODE),this._engine.updateTextureSamplingMode(r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,a.texture,!0),a},a.prototype._prefilterInternal=function(a){var b=a.getSize().width,c=H.a.ILog2(b)+1,d=this._effectWrapper.effect,e=this._createRenderTarget(b);this._effectRenderer.setViewport();var f=a.getInternalTexture();f&&this._engine.updateTextureSamplingMode(r.a.TEXTURE_TRILINEAR_SAMPLINGMODE,f,!0),this._effectRenderer.applyEffectWrapper(this._effectWrapper);f=[[new g.e(0,0,-1),new g.e(0,-1,0),new g.e(1,0,0)],[new g.e(0,0,1),new g.e(0,-1,0),new g.e(-1,0,0)],[new g.e(1,0,0),new g.e(0,0,1),new g.e(0,1,0)],[new g.e(1,0,0),new g.e(0,0,-1),new g.e(0,-1,0)],[new g.e(1,0,0),new g.e(0,-1,0),new g.e(0,0,1)],[new g.e(-1,0,0),new g.e(0,-1,0),new g.e(0,0,-1)]];d.setFloat("hdrScale",this.hdrScale),d.setFloat2("vFilteringInfo",a.getSize().width,c),d.setTexture("inputTexture",a);for(var h=0;h<6;h++){d.setVector3("up",f[h][0]),d.setVector3("right",f[h][1]),d.setVector3("front",f[h][2]);for(var i=0;i255){s=255/s;k*=s,l*=s,q*=s}f[3*j+0]=k,f[3*j+1]=l,f[3*j+2]=q}g?d.push(g):f?d.push(f):d.push(i)}return d},null,this._onLoad,this._onError)},b.prototype.clone=function(){var a=new b(this.url,this.getScene()||this._getEngine(),this._size,this._noMipmap,this._generateHarmonics,this.gammaSpace);return a.level=this.level,a.wrapU=this.wrapU,a.wrapV=this.wrapV,a.coordinatesIndex=this.coordinatesIndex,a.coordinatesMode=this.coordinatesMode,a},b.prototype.delayLoad=function(){this.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=r.a.DELAYLOADSTATE_LOADED,this._texture=this._getFromCache(this.url,this._noMipmap),this._texture||this.loadTexture())},b.prototype.getReflectionTextureMatrix=function(){return this._textureMatrix},b.prototype.setReflectionTextureMatrix=function(a){var b=this;this._textureMatrix=a,a.updateFlag!==this._textureMatrix.updateFlag&&a.isIdentity()!==this._textureMatrix.isIdentity()&&(null===(a=this.getScene())||void 0===a||a.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return-1!==a.getActiveTextures().indexOf(b)}))},b.Parse=function(a,c,d){var e=null;return a.name&&!a.isRenderTarget&&((e=new b(d+a.name,c,a.size,a.noMipmap,a.generateHarmonics,a.useInGammaSpace)).name=a.name,e.hasAlpha=a.hasAlpha,e.level=a.level,e.coordinatesMode=a.coordinatesMode,e.isBlocking=a.isBlocking),e&&(a.boundingBoxPosition&&(e.boundingBoxPosition=g.e.FromArray(a.boundingBoxPosition)),a.boundingBoxSize&&(e.boundingBoxSize=g.e.FromArray(a.boundingBoxSize)),a.rotationY&&(e.rotationY=a.rotationY)),e},b.prototype.serialize=function(){if(!this.name)return null;var a={};return a.name=this.name,a.hasAlpha=this.hasAlpha,a.isCube=!0,a.level=this.level,a.size=this._size,a.coordinatesMode=this.coordinatesMode,a.useInGammaSpace=this.gammaSpace,a.generateHarmonics=this._generateHarmonics,a.customType="BABYLON.HDRCubeTexture",a.noMipmap=this._noMipmap,a.isBlocking=this._isBlocking,a.rotationY=this._rotationY,a},b._facesMapping=["right","left","up","down","front","back"],b}(Ie.a);Object(i.b)("BABYLON.HDRCubeTexture",Pj);var Qj=function(){function a(a,b,c){void 0===b&&(b=0),void 0===c&&(c=null),this.name=a,this.animations=new Array,this._positions=null,this._normals=null,this._tangents=null,this._uvs=null,this._uniqueId=0,this.onInfluenceChanged=new f.c,this._onDataLayoutChanged=new f.c,this._animationPropertiesOverride=null,this._scene=c||C.a.LastCreatedScene,this.influence=b,this._scene&&(this._uniqueId=this._scene.getUniqueId())}return Object.defineProperty(a.prototype,"influence",{get:function(){return this._influence},set:function(a){if(this._influence!==a){var b=this._influence;this._influence=a,this.onInfluenceChanged.hasObservers()&&this.onInfluenceChanged.notifyObservers(0===b||0===a)}},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"animationPropertiesOverride",{get:function(){return!this._animationPropertiesOverride&&this._scene?this._scene.animationPropertiesOverride:this._animationPropertiesOverride},set:function(a){this._animationPropertiesOverride=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasPositions",{get:function(){return!!this._positions},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasNormals",{get:function(){return!!this._normals},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasTangents",{get:function(){return!!this._tangents},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"hasUVs",{get:function(){return!!this._uvs},enumerable:!1,configurable:!0}),a.prototype.setPositions=function(a){var b=this.hasPositions;this._positions=a,b!==this.hasPositions&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getPositions=function(){return this._positions},a.prototype.setNormals=function(a){var b=this.hasNormals;this._normals=a,b!==this.hasNormals&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getNormals=function(){return this._normals},a.prototype.setTangents=function(a){var b=this.hasTangents;this._tangents=a,b!==this.hasTangents&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getTangents=function(){return this._tangents},a.prototype.setUVs=function(a){var b=this.hasUVs;this._uvs=a,b!==this.hasUVs&&this._onDataLayoutChanged.notifyObservers(void 0)},a.prototype.getUVs=function(){return this._uvs},a.prototype.clone=function(){var b=this,c=I.a.Clone(function(){return new a(b.name,b.influence,b._scene)},this);return c._positions=this._positions,c._normals=this._normals,c._tangents=this._tangents,c._uvs=this._uvs,c},a.prototype.serialize=function(){var a={};return a.name=this.name,a.influence=this.influence,a.positions=Array.prototype.slice.call(this.getPositions()),null!=this.id&&(a.id=this.id),this.hasNormals&&(a.normals=Array.prototype.slice.call(this.getNormals())),this.hasTangents&&(a.tangents=Array.prototype.slice.call(this.getTangents())),this.hasUVs&&(a.uvs=Array.prototype.slice.call(this.getUVs())),I.a.AppendSerializedAnimations(this,a),a},a.prototype.getClassName=function(){return"MorphTarget"},a.Parse=function(b){var c=new a(b.name,b.influence);if(c.setPositions(b.positions),null!=b.id&&(c.id=b.id),b.normals&&c.setNormals(b.normals),b.tangents&&c.setTangents(b.tangents),b.uvs&&c.setUVs(b.uvs),b.animations)for(var d=0;d0}}return Object.defineProperty(a.prototype,"areUpdatesFrozen",{get:function(){return this._blockCounter>0},set:function(a){a?this._blockCounter++:(this._blockCounter--,this._blockCounter<=0&&(this._blockCounter=0,this._syncActiveTargets(!0)))},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"uniqueId",{get:function(){return this._uniqueId},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"vertexCount",{get:function(){return this._vertexCount},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsNormals",{get:function(){return this._supportsNormals&&this.enableNormalMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsTangents",{get:function(){return this._supportsTangents&&this.enableTangentMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"supportsUVs",{get:function(){return this._supportsUVs&&this.enableUVMorphing},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"numTargets",{get:function(){return this._targets.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"numInfluencers",{get:function(){return this._activeTargets.length},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"influences",{get:function(){return this._influences},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"useTextureToStoreTargets",{get:function(){return this._useTextureToStoreTargets},set:function(a){this._useTextureToStoreTargets=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUsingTextureForTargets",{get:function(){return a.EnableTextureStorage&&this.useTextureToStoreTargets&&this._canUseTextureForTargets},enumerable:!1,configurable:!0}),a.prototype.getActiveTarget=function(a){return this._activeTargets.data[a]},a.prototype.getTarget=function(a){return this._targets[a]},a.prototype.addTarget=function(a){var b=this;this._targets.push(a),this._targetInfluenceChangedObservers.push(a.onInfluenceChanged.add(function(a){b._syncActiveTargets(a)})),this._targetDataLayoutChangedObservers.push(a._onDataLayoutChanged.add(function(){b._syncActiveTargets(!0)})),this._syncActiveTargets(!0)},a.prototype.removeTarget=function(a){var b=this._targets.indexOf(a);b>=0&&(this._targets.splice(b,1),a.onInfluenceChanged.remove(this._targetInfluenceChangedObservers.splice(b,1)[0]),a._onDataLayoutChanged.remove(this._targetDataLayoutChangedObservers.splice(b,1)[0]),this._syncActiveTargets(!0))},a.prototype._bind=function(a){a.setFloat3("morphTargetTextureInfo",this._textureVertexStride,this._textureWidth,this._textureHeight),a.setFloatArray("morphTargetTextureIndices",this._morphTargetTextureIndices),a.setTexture("morphTargets",this._targetStoreTexture)},a.prototype.clone=function(){for(var b=new a(this._scene),c=0,d=this._targets;ca&&(this._textureHeight=Math.ceil(this._textureWidth/a),this._textureWidth=a);a=!0;if(this._targetStoreTexture){var b=this._targetStoreTexture.getSize();b.width===this._textureWidth&&b.height===this._textureHeight&&this._targetStoreTexture.depth===this._targets.length&&(a=!1)}if(a){this._targetStoreTexture&&this._targetStoreTexture.dispose();for(var b=this._targets.length,a=new Float32Array(b*this._textureWidth*this._textureHeight*4),c=0,d=0;d-1&&this._parentContainer.morphTargetManagers.splice(a,1),this._parentContainer=null}},a.Parse=function(b,c){c=new a(c);c._uniqueId=b.id;for(var d=0,b=b.targets;d-1&&this._impostors.splice(b,1).length&&this.getPhysicsPlugin().removePhysicsBody(a)},a.prototype.addJoint=function(a,b,c){a={mainImpostor:a,connectedImpostor:b,joint:c};c.physicsPlugin=this._physicsPlugin,this._joints.push(a),this._physicsPlugin.generateJoint(a)},a.prototype.removeJoint=function(a,b,c){var d=this._joints.filter(function(d){return d.connectedImpostor===b&&d.joint===c&&d.mainImpostor===a});d.length&&this._physicsPlugin.removeJoint(d[0])},a.prototype._step=function(a){var b=this;this._impostors.forEach(function(a){a.isBodyInitRequired()&&b._physicsPlugin.generatePhysicsBody(a)}),a>.1?a=.1:a<=0&&(a=1/60),this._physicsPlugin.executeStep(a,this._impostors)},a.prototype.getPhysicsPlugin=function(){return this._physicsPlugin},a.prototype.getImpostors=function(){return this._impostors},a.prototype.getImpostorForPhysicsObject=function(a){for(var b=0;b0&&(this._physicsBodysToRemoveAfterStep.forEach(function(b){"function"==typeof a.world.removeBody?a.world.removeBody(b):a.world.remove(b)}),this._physicsBodysToRemoveAfterStep=[])},a.prototype.applyImpulse=function(a,b,c){c=new this.BJSCANNON.Vec3(c.x,c.y,c.z);b=new this.BJSCANNON.Vec3(b.x,b.y,b.z);a.physicsBody.applyImpulse(b,c)},a.prototype.applyForce=function(a,b,c){c=new this.BJSCANNON.Vec3(c.x,c.y,c.z);b=new this.BJSCANNON.Vec3(b.x,b.y,b.z);a.physicsBody.applyForce(b,c)},a.prototype.generatePhysicsBody=function(a){if(this._removeMarkedPhysicsBodiesFromWorld(),a.parent)a.physicsBody&&(this.removePhysicsBody(a),a.forceUpdate());else{if(a.isBodyInitRequired()){var b=this._createShape(a),c=a.physicsBody;c&&this.removePhysicsBody(a);var d=this._addMaterial("mat-"+a.uniqueId,a.getParam("friction"),a.getParam("restitution"));d={mass:a.getParam("mass"),material:d};var e=a.getParam("nativeOptions");for(var f in e)e.hasOwnProperty(f)&&(d[f]=e[f]);a.physicsBody=new this.BJSCANNON.Body(d),a.physicsBody.addEventListener("collide",a.onCollide),this.world.addEventListener("preStep",a.beforeStep),this.world.addEventListener("postStep",a.afterStep),a.physicsBody.addShape(b),"function"==typeof this.world.addBody?this.world.addBody(a.physicsBody):this.world.add(a.physicsBody),c&&["force","torque","velocity","angularVelocity"].forEach(function(b){var d=c[b];a.physicsBody[b].set(d.x,d.y,d.z)}),this._processChildMeshes(a)}this._updatePhysicsBodyTransformation(a)}},a.prototype._processChildMeshes=function(a){var b=this,c=a.object.getChildMeshes?a.object.getChildMeshes(!0):[],d=a.object.rotationQuaternion;if(d?d.conjugateToRef(this._tmpQuaternion):this._tmpQuaternion.set(0,0,0,1),c.length){var e=function(c){if(c.rotationQuaternion){var d=c.getPhysicsImpostor();if(d&&(d.parent!==a&&c.parent)){var f=c.getAbsolutePosition().subtract(c.parent.getAbsolutePosition()),g=c.rotationQuaternion.multiply(b._tmpQuaternion);d.physicsBody&&(b.removePhysicsBody(d),d.physicsBody=null),d.parent=a,d.resetUpdateFlags(),a.physicsBody.addShape(b._createShape(d),new b.BJSCANNON.Vec3(f.x,f.y,f.z),new b.BJSCANNON.Quaternion(g.x,g.y,g.z,g.w)),a.physicsBody.mass+=d.getParam("mass")}c.getChildMeshes(!0).filter(function(a){return!!a.physicsImpostor}).forEach(e)}};c.filter(function(a){return!!a.physicsImpostor}).forEach(e)}},a.prototype.removePhysicsBody=function(a){a.physicsBody.removeEventListener("collide",a.onCollide),this.world.removeEventListener("preStep",a.beforeStep),this.world.removeEventListener("postStep",a.afterStep),-1===this._physicsBodysToRemoveAfterStep.indexOf(a.physicsBody)&&this._physicsBodysToRemoveAfterStep.push(a.physicsBody)},a.prototype.generateJoint=function(a){var b=a.mainImpostor.physicsBody,c=a.connectedImpostor.physicsBody;if(b&&c){var d,e=a.joint.jointData,f={pivotA:e.mainPivot?(new this.BJSCANNON.Vec3).set(e.mainPivot.x,e.mainPivot.y,e.mainPivot.z):null,pivotB:e.connectedPivot?(new this.BJSCANNON.Vec3).set(e.connectedPivot.x,e.connectedPivot.y,e.connectedPivot.z):null,axisA:e.mainAxis?(new this.BJSCANNON.Vec3).set(e.mainAxis.x,e.mainAxis.y,e.mainAxis.z):null,axisB:e.connectedAxis?(new this.BJSCANNON.Vec3).set(e.connectedAxis.x,e.connectedAxis.y,e.connectedAxis.z):null,maxForce:e.nativeParams.maxForce,collideConnected:!!e.collision};switch(a.joint.type){case Uj.e.HingeJoint:case Uj.e.Hinge2Joint:d=new this.BJSCANNON.HingeConstraint(b,c,f);break;case Uj.e.DistanceJoint:d=new this.BJSCANNON.DistanceConstraint(b,c,e.maxDistance||2);break;case Uj.e.SpringJoint:var g=e;d=new this.BJSCANNON.Spring(b,c,{restLength:g.length,stiffness:g.stiffness,damping:g.damping,localAnchorA:f.pivotA,localAnchorB:f.pivotB});break;case Uj.e.LockJoint:d=new this.BJSCANNON.LockConstraint(b,c,f);break;case Uj.e.PointToPointJoint:case Uj.e.BallAndSocketJoint:default:d=new this.BJSCANNON.PointToPointConstraint(b,f.pivotA,c,f.pivotB,f.maxForce)}d.collideConnected=!!e.collision,a.joint.physicsJoint=d,a.joint.type!==Uj.e.SpringJoint?this.world.addConstraint(d):(a.joint.jointData.forceApplicationCallback=a.joint.jointData.forceApplicationCallback||function(){d.applyForce()},a.mainImpostor.registerAfterPhysicsStep(a.joint.jointData.forceApplicationCallback))}},a.prototype.removeJoint=function(a){a.joint.type!==Uj.e.SpringJoint?this.world.removeConstraint(a.joint.physicsJoint):a.mainImpostor.unregisterAfterPhysicsStep(a.joint.jointData.forceApplicationCallback)},a.prototype._addMaterial=function(a,b,c){var d,e;for(d=0;d1e3*c));g++);this.time+=d;for(e=this.time%c/c,f=a,g=this.bodies,d=0;d!==g.length;d++){c=g[d];c.type!==b.Body.STATIC&&c.sleepState!==b.Body.SLEEPING?(c.position.vsub(c.previousPosition,f),f.scale(e,f),c.position.vadd(f,c.interpolatedPosition)):(c.interpolatedPosition.set(c.position.x,c.position.y,c.position.z),c.interpolatedQuaternion.set(c.quaternion.x,c.quaternion.y,c.quaternion.z,c.quaternion.w))}}}},a.prototype.raycast=function(a,b){return this._cannonRaycastResult.reset(),this.world.raycastClosest(a,b,{},this._cannonRaycastResult),this._raycastResult.reset(a,b),this._cannonRaycastResult.hasHit&&(this._raycastResult.setHitData({x:this._cannonRaycastResult.hitNormalWorld.x,y:this._cannonRaycastResult.hitNormalWorld.y,z:this._cannonRaycastResult.hitNormalWorld.z},{x:this._cannonRaycastResult.hitPointWorld.x,y:this._cannonRaycastResult.hitPointWorld.y,z:this._cannonRaycastResult.hitPointWorld.z}),this._raycastResult.setHitDistance(this._cannonRaycastResult.distance)),this._raycastResult},a}();Vj.DefaultPluginFactory=function(){return new Xj};var Yj=function(){function a(a,b,c){void 0===a&&(a=!0),void 0===c&&(c=OIMO),this._useDeltaForWorldStep=a,this.name="OimoJSPlugin",this._fixedTimeStep=1/60,this._tmpImpostorsArray=[],this._tmpPositionVector=g.e.Zero(),this.BJSOIMO=c,this.world=new this.BJSOIMO.World({iterations:b}),this.world.clear(),this._raycastResult=new Wj}return a.prototype.setGravity=function(a){this.world.gravity.set(a.x,a.y,a.z)},a.prototype.setTimeStep=function(a){this.world.timeStep=a},a.prototype.getTimeStep=function(){return this.world.timeStep},a.prototype.executeStep=function(a,b){var c=this;b.forEach(function(a){a.beforeStep()}),this.world.timeStep=this._useDeltaForWorldStep?a:this._fixedTimeStep,this.world.step(),b.forEach(function(a){a.afterStep(),c._tmpImpostorsArray[a.uniqueId]=a});for(a=this.world.contacts;null!==a;)if(!a.touching||a.body1.sleeping||a.body2.sleeping){b=this._tmpImpostorsArray[+a.body1.name];var d=this._tmpImpostorsArray[+a.body2.name];b&&d?(b.onCollide({body:d.physicsBody,point:null}),d.onCollide({body:b.physicsBody,point:null}),a=a.next):a=a.next}else a=a.next},a.prototype.applyImpulse=function(a,b,c){var d=a.physicsBody.mass;a.physicsBody.applyImpulse(c.scale(this.world.invScale),b.scale(this.world.invScale*d))},a.prototype.applyForce=function(a,b,c){q.a.Warn("Oimo doesn"t support applying force. Using impule instead."),this.applyImpulse(a,b,c)},a.prototype.generatePhysicsBody=function(a){var b=this;if(a.parent)a.physicsBody&&(this.removePhysicsBody(a),a.forceUpdate());else{if(a.isBodyInitRequired()){var c={name:a.uniqueId,config:[a.getParam("mass")||.001,a.getParam("friction"),a.getParam("restitution")],size:[],type:[],pos:[],posShape:[],rot:[],rotShape:[],move:0!==a.getParam("mass"),density:a.getParam("mass"),friction:a.getParam("friction"),restitution:a.getParam("restitution"),world:this.world},d=[a];(h=a.object).getChildMeshes&&h.getChildMeshes().forEach(function(a){a.physicsImpostor&&d.push(a.physicsImpostor)});var e=function(a){return Math.max(a,Vj.Epsilon)},f=new g.b;d.forEach(function(d){if(d.object.rotationQuaternion){var g=d.object.rotationQuaternion;f.copyFrom(g),d.object.rotationQuaternion.set(0,0,0,1),d.object.computeWorldMatrix(!0);var h=f.toEulerAngles(),i=d.getObjectExtendSize();if(d===a){var j=a.getObjectCenter();a.object.getAbsolutePivotPoint().subtractToRef(j,b._tmpPositionVector),b._tmpPositionVector.divideInPlace(a.object.scaling),c.pos.push(j.x),c.pos.push(j.y),c.pos.push(j.z),c.posShape.push(0,0,0),c.rotShape.push(0,0,0)}else{j=d.object.position.clone();c.posShape.push(j.x),c.posShape.push(j.y),c.posShape.push(j.z),c.rotShape.push(57.29577951308232*h.x,57.29577951308232*h.y,57.29577951308232*h.z)}switch(d.object.rotationQuaternion.copyFrom(f),d.type){case Tj.a.ParticleImpostor:q.a.Warn("No Particle support in OIMO.js. using SphereImpostor instead");case Tj.a.SphereImpostor:j=i.x;h=i.y;var k=i.z;j=Math.max(e(j),e(h),e(k))/2;c.type.push("sphere"),c.size.push(j),c.size.push(j),c.size.push(j);break;case Tj.a.CylinderImpostor:h=e(i.x)/2;k=e(i.y);c.type.push("cylinder"),c.size.push(h),c.size.push(k),c.size.push(k);break;case Tj.a.PlaneImpostor:case Tj.a.BoxImpostor:default:h=e(i.x),k=e(i.y);j=e(i.z);c.type.push("box"),c.size.push(h),c.size.push(k),c.size.push(j)}d.object.rotationQuaternion=g}}),a.physicsBody=this.world.add(c),a.physicsBody.resetQuaternion(f),a.physicsBody.updatePosition(0)}else this._tmpPositionVector.copyFromFloats(0,0,0);var h;a.setDeltaPosition(this._tmpPositionVector)}},a.prototype.removePhysicsBody=function(a){this.world.removeRigidBody(a.physicsBody)},a.prototype.generateJoint=function(a){var b=a.mainImpostor.physicsBody,c=a.connectedImpostor.physicsBody;if(b&&c){var d,e=a.joint.jointData,f=e.nativeParams||{};b={body1:b,body2:c,axe1:f.axe1||(e.mainAxis?e.mainAxis.asArray():null),axe2:f.axe2||(e.connectedAxis?e.connectedAxis.asArray():null),pos1:f.pos1||(e.mainPivot?e.mainPivot.asArray():null),pos2:f.pos2||(e.connectedPivot?e.connectedPivot.asArray():null),min:f.min,max:f.max,collision:f.collision||e.collision,spring:f.spring,world:this.world};switch(a.joint.type){case Uj.e.BallAndSocketJoint:d="jointBall";break;case Uj.e.SpringJoint:q.a.Warn("OIMO.js doesn"t support Spring Constraint. Simulating using DistanceJoint instead");c=e;b.min=c.length||b.min,b.max=Math.max(b.min,b.max);case Uj.e.DistanceJoint:d="jointDistance",b.max=e.maxDistance;break;case Uj.e.PrismaticJoint:d="jointPrisme";break;case Uj.e.SliderJoint:d="jointSlide";break;case Uj.e.WheelJoint:d="jointWheel";break;case Uj.e.HingeJoint:default:d="jointHinge"}b.type=d,a.joint.physicsJoint=this.world.add(b)}},a.prototype.removeJoint=function(a){try{this.world.removeJoint(a.joint.physicsJoint)}catch(a){q.a.Warn(a)}},a.prototype.isSupported=function(){return void 0!==this.BJSOIMO},a.prototype.setTransformationFromPhysicsBody=function(a){if(!a.physicsBody.sleeping){if(a.physicsBody.shapes.next){for(var b=a.physicsBody.shapes;b.next;)b=b.next;a.object.position.set(b.position.x,b.position.y,b.position.z)}else{b=a.physicsBody.getPosition();a.object.position.set(b.x,b.y,b.z)}if(a.object.rotationQuaternion){b=a.physicsBody.getQuaternion();a.object.rotationQuaternion.set(b.x,b.y,b.z,b.w)}}},a.prototype.setPhysicsBodyTransformation=function(a,b,c){var d=a.physicsBody;a.physicsBody.shapes.next||(d.position.set(b.x,b.y,b.z),d.orientation.set(c.x,c.y,c.z,c.w),d.syncShapes(),d.awake())},a.prototype.setLinearVelocity=function(a,b){a.physicsBody.linearVelocity.set(b.x,b.y,b.z)},a.prototype.setAngularVelocity=function(a,b){a.physicsBody.angularVelocity.set(b.x,b.y,b.z)},a.prototype.getLinearVelocity=function(a){a=a.physicsBody.linearVelocity;return a?new g.e(a.x,a.y,a.z):null},a.prototype.getAngularVelocity=function(a){a=a.physicsBody.angularVelocity;return a?new g.e(a.x,a.y,a.z):null},a.prototype.setBodyMass=function(a,b){var c=0===b;a.physicsBody.shapes.density=c?1:b,a.physicsBody.setupMass(c?2:1)},a.prototype.getBodyMass=function(a){return a.physicsBody.shapes.density},a.prototype.getBodyFriction=function(a){return a.physicsBody.shapes.friction},a.prototype.setBodyFriction=function(a,b){a.physicsBody.shapes.friction=b},a.prototype.getBodyRestitution=function(a){return a.physicsBody.shapes.restitution},a.prototype.setBodyRestitution=function(a,b){a.physicsBody.shapes.restitution=b},a.prototype.sleepBody=function(a){a.physicsBody.sleep()},a.prototype.wakeUpBody=function(a){a.physicsBody.awake()},a.prototype.updateDistanceJoint=function(a,b,c){a.physicsJoint.limitMotor.upperLimit=b,void 0!==c&&(a.physicsJoint.limitMotor.lowerLimit=c)},a.prototype.setMotor=function(a,b,c,d){void 0!==c?q.a.Warn("OimoJS plugin currently has unexpected behavior when using setMotor with force parameter"):c=1e6,b*=-1;d=d?a.physicsJoint.rotationalLimitMotor2:a.physicsJoint.rotationalLimitMotor1||a.physicsJoint.rotationalLimitMotor||a.physicsJoint.limitMotor;d&&d.setMotor(b,c)},a.prototype.setLimit=function(a,b,c,d){d=d?a.physicsJoint.rotationalLimitMotor2:a.physicsJoint.rotationalLimitMotor1||a.physicsJoint.rotationalLimitMotor||a.physicsJoint.limitMotor;d&&d.setLimit(b,void 0===c?-b:c)},a.prototype.syncMeshWithImpostor=function(a,b){b=b.physicsBody;a.position.x=b.position.x,a.position.y=b.position.y,a.position.z=b.position.z,a.rotationQuaternion&&(a.rotationQuaternion.x=b.orientation.x,a.rotationQuaternion.y=b.orientation.y,a.rotationQuaternion.z=b.orientation.z,a.rotationQuaternion.w=b.orientation.s)},a.prototype.getRadius=function(a){return a.physicsBody.shapes.radius},a.prototype.getBoxSizeToRef=function(a,b){a=a.physicsBody.shapes;b.x=2*a.halfWidth,b.y=2*a.halfHeight,b.z=2*a.halfDepth},a.prototype.dispose=function(){this.world.clear()},a.prototype.raycast=function(a,b){return q.a.Warn("raycast is not currently supported by the Oimo physics plugin"),this._raycastResult.reset(a,b),this._raycastResult},a}(),Zj=c(105),$j=function(){function a(a,b,c){var d=this;void 0===a&&(a=!0),void 0===b&&(b=Ammo),void 0===c&&(c=null),this._useDeltaForWorldStep=a,this.bjsAMMO={},this.name="AmmoJSPlugin",this._timeStep=1/60,this._fixedTimeStep=1/60,this._maxSteps=5,this._tmpQuaternion=new g.b,this._tmpContactCallbackResult=!1,this._tmpContactPoint=new g.e,this._tmpMatrix=new g.a,"function"!=typeof b?(this.bjsAMMO=b,this.isSupported()?(this._collisionConfiguration=new this.bjsAMMO.btSoftBodyRigidBodyCollisionConfiguration,this._dispatcher=new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration),this._overlappingPairCache=c||new this.bjsAMMO.btDbvtBroadphase,this._solver=new this.bjsAMMO.btSequentialImpulseConstraintSolver,this._softBodySolver=new this.bjsAMMO.btDefaultSoftBodySolver,this.world=new this.bjsAMMO.btSoftRigidDynamicsWorld(this._dispatcher,this._overlappingPairCache,this._solver,this._collisionConfiguration,this._softBodySolver),this._tmpAmmoConcreteContactResultCallback=new this.bjsAMMO.ConcreteContactResultCallback,this._tmpAmmoConcreteContactResultCallback.addSingleResult=function(a,b,c,e){b=d.bjsAMMO.wrapPointer(a,d.bjsAMMO.btManifoldPoint).getPositionWorldOnA();d._tmpContactPoint.x=b.x(),d._tmpContactPoint.y=b.y(),d._tmpContactPoint.z=b.z(),d._tmpContactCallbackResult=!0},this._raycastResult=new Wj,this._tmpAmmoTransform=new this.bjsAMMO.btTransform,this._tmpAmmoTransform.setIdentity(),this._tmpAmmoQuaternion=new this.bjsAMMO.btQuaternion(0,0,0,1),this._tmpAmmoVectorA=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorB=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorC=new this.bjsAMMO.btVector3(0,0,0),this._tmpAmmoVectorD=new this.bjsAMMO.btVector3(0,0,0)):q.a.Error("AmmoJS is not available. Please make sure you included the js file.")):q.a.Error("AmmoJS is not ready. Please make sure you await Ammo() before using the plugin.")}return a.prototype.setGravity=function(a){this._tmpAmmoVectorA.setValue(a.x,a.y,a.z),this.world.setGravity(this._tmpAmmoVectorA),this.world.getWorldInfo().set_m_gravity(this._tmpAmmoVectorA)},a.prototype.setTimeStep=function(a){this._timeStep=a},a.prototype.setFixedTimeStep=function(a){this._fixedTimeStep=a},a.prototype.setMaxSteps=function(a){this._maxSteps=a},a.prototype.getTimeStep=function(){return this._timeStep},a.prototype._isImpostorInContact=function(a){return this._tmpContactCallbackResult=!1,this.world.contactTest(a.physicsBody,this._tmpAmmoConcreteContactResultCallback),this._tmpContactCallbackResult},a.prototype._isImpostorPairInContact=function(a,b){return this._tmpContactCallbackResult=!1,this.world.contactPairTest(a.physicsBody,b.physicsBody,this._tmpAmmoConcreteContactResultCallback),this._tmpContactCallbackResult},a.prototype._stepSimulation=function(a,b,c){if(void 0===a&&(a=1/60),void 0===b&&(b=10),void 0===c&&(c=1/60),0==b)this.world.stepSimulation(a,0);else for(;b>0&&a>0;)a-c0&&this._isImpostorInContact(d))for(a=0,b=d._onPhysicsCollideCallbacks;a3?3:c;b=(new this.bjsAMMO.btSoftBodyHelpers).CreateRope(this.world.getWorldInfo(),this._tmpAmmoVectorA,this._tmpAmmoVectorB,d-1,c);return b.get_m_cfg().set_collisions(17),b},a.prototype._createCustom=function(a){var b=null;return this.onCreateCustomShape&&(b=this.onCreateCustomShape(a)),null==b&&(b=new this.bjsAMMO.btCompoundShape),b},a.prototype._addHullVerts=function(a,b,c){var d=this,e=0;if(c&&c.getIndices&&c.getWorldMatrix&&c.getChildMeshes){var f=c.getIndices();f||(f=[]);var h=c.getVerticesData(W.b.PositionKind);h||(h=[]),c.computeWorldMatrix(!1);for(var i=f.length/3,j=0;j0){if(a.type!=Tj.a.NoImpostor){b=this._createShape(a,!0);b&&(this._tmpAmmoTransform.getOrigin().setValue(0,0,0),this._tmpAmmoQuaternion.setValue(0,0,0,1),this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion),d.addChildShape(this._tmpAmmoTransform,b))}return d}this.bjsAMMO.destroy(d),d=null}switch(a.type){case Tj.a.SphereImpostor:if(H.a.WithinEpsilon(f.x,f.y,1e-4)&&H.a.WithinEpsilon(f.x,f.z,1e-4))d=new this.bjsAMMO.btSphereShape(f.x/2);else{b=[new this.bjsAMMO.btVector3(0,0,0)];(d=new this.bjsAMMO.btMultiSphereShape(b,[1],1)).setLocalScaling(new this.bjsAMMO.btVector3(f.x/2,f.y/2,f.z/2))}break;case Tj.a.CapsuleImpostor:b=f.x/2;d=new this.bjsAMMO.btCapsuleShape(b,f.y-2*b);break;case Tj.a.CylinderImpostor:this._tmpAmmoVectorA.setValue(f.x/2,f.y/2,f.z/2),d=new this.bjsAMMO.btCylinderShape(this._tmpAmmoVectorA);break;case Tj.a.PlaneImpostor:case Tj.a.BoxImpostor:this._tmpAmmoVectorA.setValue(f.x/2,f.y/2,f.z/2),d=new this.bjsAMMO.btBoxShape(this._tmpAmmoVectorA);break;case Tj.a.MeshImpostor:if(0==a.getParam("mass")){b=new this.bjsAMMO.btTriangleMesh;a._pluginData.toDispose.push(b);var i=this._addMeshVerts(b,e,e);d=0==i?new this.bjsAMMO.btCompoundShape:new this.bjsAMMO.btBvhTriangleMeshShape(b);break}case Tj.a.ConvexHullImpostor:i=new this.bjsAMMO.btConvexHullShape;0==this._addHullVerts(i,e,e)?(a._pluginData.toDispose.push(i),d=new this.bjsAMMO.btCompoundShape):d=i;break;case Tj.a.NoImpostor:d=new this.bjsAMMO.btSphereShape(f.x/2);break;case Tj.a.CustomImpostor:d=this._createCustom(a);break;case Tj.a.SoftbodyImpostor:d=this._createSoftbody(a);break;case Tj.a.ClothImpostor:d=this._createCloth(a);break;case Tj.a.RopeImpostor:d=this._createRope(a);break;default:q.a.Warn("The impostor type is not currently supported by the ammo plugin.")}return d},a.prototype.setTransformationFromPhysicsBody=function(a){a.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform),a.object.position.set(this._tmpAmmoTransform.getOrigin().x(),this._tmpAmmoTransform.getOrigin().y(),this._tmpAmmoTransform.getOrigin().z()),a.object.rotationQuaternion?a.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(),this._tmpAmmoTransform.getRotation().y(),this._tmpAmmoTransform.getRotation().z(),this._tmpAmmoTransform.getRotation().w()):a.object.rotation&&(this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(),this._tmpAmmoTransform.getRotation().y(),this._tmpAmmoTransform.getRotation().z(),this._tmpAmmoTransform.getRotation().w()),this._tmpQuaternion.toEulerAnglesToRef(a.object.rotation))},a.prototype.setPhysicsBodyTransformation=function(a,b,c){var d=a.physicsBody.getWorldTransform();if(Math.abs(d.getOrigin().x()-b.x)>ib.a||Math.abs(d.getOrigin().y()-b.y)>ib.a||Math.abs(d.getOrigin().z()-b.z)>ib.a||Math.abs(d.getRotation().x()-c.x)>ib.a||Math.abs(d.getRotation().y()-c.y)>ib.a||Math.abs(d.getRotation().z()-c.z)>ib.a||Math.abs(d.getRotation().w()-c.w)>ib.a)if(this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),d.setOrigin(this._tmpAmmoVectorA),this._tmpAmmoQuaternion.setValue(c.x,c.y,c.z,c.w),d.setRotation(this._tmpAmmoQuaternion),a.physicsBody.setWorldTransform(d),0==a.mass){b=a.physicsBody.getMotionState();b&&b.setWorldTransform(d)}else a.physicsBody.activate()},a.prototype.isSupported=function(){return void 0!==this.bjsAMMO},a.prototype.setLinearVelocity=function(a,b){this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),a.soft?a.physicsBody.linearVelocity(this._tmpAmmoVectorA):a.physicsBody.setLinearVelocity(this._tmpAmmoVectorA)},a.prototype.setAngularVelocity=function(a,b){this._tmpAmmoVectorA.setValue(b.x,b.y,b.z),a.soft?a.physicsBody.angularVelocity(this._tmpAmmoVectorA):a.physicsBody.setAngularVelocity(this._tmpAmmoVectorA)},a.prototype.getLinearVelocity=function(a){if(a.soft)var b=a.physicsBody.linearVelocity();else b=a.physicsBody.getLinearVelocity();if(!b)return null;a=new g.e(b.x(),b.y(),b.z());return this.bjsAMMO.destroy(b),a},a.prototype.getAngularVelocity=function(a){if(a.soft)var b=a.physicsBody.angularVelocity();else b=a.physicsBody.getAngularVelocity();if(!b)return null;a=new g.e(b.x(),b.y(),b.z());return this.bjsAMMO.destroy(b),a},a.prototype.setBodyMass=function(a,b){a.soft?a.physicsBody.setTotalMass(b,!1):a.physicsBody.setMassProps(b),a._pluginData.mass=b},a.prototype.getBodyMass=function(a){return a._pluginData.mass||0},a.prototype.getBodyFriction=function(a){return a._pluginData.friction||0},a.prototype.setBodyFriction=function(a,b){a.soft?a.physicsBody.get_m_cfg().set_kDF(b):a.physicsBody.setFriction(b),a._pluginData.friction=b},a.prototype.getBodyRestitution=function(a){return a._pluginData.restitution||0},a.prototype.setBodyRestitution=function(a,b){a.physicsBody.setRestitution(b),a._pluginData.restitution=b},a.prototype.getBodyPressure=function(a){return a.soft?a._pluginData.pressure||0:(q.a.Warn("Pressure is not a property of a rigid body"),0)},a.prototype.setBodyPressure=function(a,b){a.soft?a.type===Tj.a.SoftbodyImpostor?(a.physicsBody.get_m_cfg().set_kPR(b),a._pluginData.pressure=b):(a.physicsBody.get_m_cfg().set_kPR(0),a._pluginData.pressure=0):q.a.Warn("Pressure can only be applied to a softbody")},a.prototype.getBodyStiffness=function(a){return a.soft?a._pluginData.stiffness||0:(q.a.Warn("Stiffness is not a property of a rigid body"),0)},a.prototype.setBodyStiffness=function(a,b){a.soft?(b=(b=b<0?0:b)>1?1:b,a.physicsBody.get_m_materials().at(0).set_m_kLST(b),a._pluginData.stiffness=b):q.a.Warn("Stiffness cannot be applied to a rigid body")},a.prototype.getBodyVelocityIterations=function(a){return a.soft?a._pluginData.velocityIterations||0:(q.a.Warn("Velocity iterations is not a property of a rigid body"),0)},a.prototype.setBodyVelocityIterations=function(a,b){a.soft?(b=b<0?0:b,a.physicsBody.get_m_cfg().set_viterations(b),a._pluginData.velocityIterations=b):q.a.Warn("Velocity iterations cannot be applied to a rigid body")},a.prototype.getBodyPositionIterations=function(a){return a.soft?a._pluginData.positionIterations||0:(q.a.Warn("Position iterations is not a property of a rigid body"),0)},a.prototype.setBodyPositionIterations=function(a,b){a.soft?(b=b<0?0:b,a.physicsBody.get_m_cfg().set_piterations(b),a._pluginData.positionIterations=b):q.a.Warn("Position iterations cannot be applied to a rigid body")},a.prototype.appendAnchor=function(a,b,c,d,e,f){void 0===e&&(e=1),void 0===f&&(f=!1);var g=a.segments;c=Math.round((g-1)*c)+g*(g-1-Math.round((g-1)*d));a.physicsBody.appendAnchor(c,b.physicsBody,f,e)},a.prototype.appendHook=function(a,b,c,d,e){void 0===d&&(d=1),void 0===e&&(e=!1);c=Math.round(a.segments*c);a.physicsBody.appendAnchor(c,b.physicsBody,e,d)},a.prototype.sleepBody=function(a){a.physicsBody.forceActivationState(0)},a.prototype.wakeUpBody=function(a){a.physicsBody.activate()},a.prototype.updateDistanceJoint=function(a,b,c){q.a.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin")},a.prototype.setMotor=function(a,b,c,d){a.physicsJoint.enableAngularMotor(!0,b,c)},a.prototype.setLimit=function(a,b,c){q.a.Warn("setLimit is not currently supported by the Ammo physics plugin")},a.prototype.syncMeshWithImpostor=function(a,b){b.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform),a.position.x=this._tmpAmmoTransform.getOrigin().x(),a.position.y=this._tmpAmmoTransform.getOrigin().y(),a.position.z=this._tmpAmmoTransform.getOrigin().z(),a.rotationQuaternion&&(a.rotationQuaternion.x=this._tmpAmmoTransform.getRotation().x(),a.rotationQuaternion.y=this._tmpAmmoTransform.getRotation().y(),a.rotationQuaternion.z=this._tmpAmmoTransform.getRotation().z(),a.rotationQuaternion.w=this._tmpAmmoTransform.getRotation().w())},a.prototype.getRadius=function(a){return a.getObjectExtendSize().x/2},a.prototype.getBoxSizeToRef=function(a,b){a=a.getObjectExtendSize();b.x=a.x,b.y=a.y,b.z=a.z},a.prototype.dispose=function(){this.bjsAMMO.destroy(this.world),this.bjsAMMO.destroy(this._solver),this.bjsAMMO.destroy(this._overlappingPairCache),this.bjsAMMO.destroy(this._dispatcher),this.bjsAMMO.destroy(this._collisionConfiguration),this.bjsAMMO.destroy(this._tmpAmmoVectorA),this.bjsAMMO.destroy(this._tmpAmmoVectorB),this.bjsAMMO.destroy(this._tmpAmmoVectorC),this.bjsAMMO.destroy(this._tmpAmmoTransform),this.bjsAMMO.destroy(this._tmpAmmoQuaternion),this.bjsAMMO.destroy(this._tmpAmmoConcreteContactResultCallback),this.world=null},a.prototype.raycast=function(a,b){this._tmpAmmoVectorRCA=new this.bjsAMMO.btVector3(a.x,a.y,a.z),this._tmpAmmoVectorRCB=new this.bjsAMMO.btVector3(b.x,b.y,b.z);var c=new this.bjsAMMO.ClosestRayResultCallback(this._tmpAmmoVectorRCA,this._tmpAmmoVectorRCB);return this.world.rayTest(this._tmpAmmoVectorRCA,this._tmpAmmoVectorRCB,c),this._raycastResult.reset(a,b),c.hasHit()&&(this._raycastResult.setHitData({x:c.get_m_hitNormalWorld().x(),y:c.get_m_hitNormalWorld().y(),z:c.get_m_hitNormalWorld().z()},{x:c.get_m_hitPointWorld().x(),y:c.get_m_hitPointWorld().y(),z:c.get_m_hitPointWorld().z()}),this._raycastResult.calculateHitDistance()),this.bjsAMMO.destroy(c),this.bjsAMMO.destroy(this._tmpAmmoVectorRCA),this.bjsAMMO.destroy(this._tmpAmmoVectorRCB),this._raycastResult},a.DISABLE_COLLISION_FLAG=4,a.KINEMATIC_FLAG=2,a.DISABLE_DEACTIVATION_FLAG=4,a}();d.a.prototype.removeReflectionProbe=function(a){if(!this.reflectionProbes)return-1;a=this.reflectionProbes.indexOf(a);return-1!==a&&this.reflectionProbes.splice(a,1),a},d.a.prototype.addReflectionProbe=function(a){this.reflectionProbes||(this.reflectionProbes=[]),this.reflectionProbes.push(a)};var ak=function(){function a(a,b,c,d,e,f){var h=this;void 0===d&&(d=!0),void 0===e&&(e=!1),void 0===f&&(f=!1),this.name=a,this._viewMatrix=g.a.Identity(),this._target=g.e.Zero(),this._add=g.e.Zero(),this._invertYAxis=!1,this.position=g.e.Zero(),this._parentContainer=null,this._scene=c,this._scene.reflectionProbes||(this._scene.reflectionProbes=new Array),this._scene.reflectionProbes.push(this);var i=r.a.TEXTURETYPE_UNSIGNED_BYTE;if(e){e=this._scene.getEngine().getCaps();e.textureHalfFloatRender?i=r.a.TEXTURETYPE_HALF_FLOAT:e.textureFloatRender&&(i=r.a.TEXTURETYPE_FLOAT)}this._renderTargetTexture=new cd(a,b,c,d,!0,i,!0),this._renderTargetTexture.gammaSpace=!f;var j,k=c.getEngine().useReverseDepthBuffer;this._renderTargetTexture.onBeforeRenderObservable.add(function(a){switch(a){case 0:h._add.copyFromFloats(1,0,0);break;case 1:h._add.copyFromFloats(-1,0,0);break;case 2:h._add.copyFromFloats(0,h._invertYAxis?1:-1,0);break;case 3:h._add.copyFromFloats(0,h._invertYAxis?-1:1,0);break;case 4:h._add.copyFromFloats(0,0,c.useRightHandedSystem?-1:1);break;case 5:h._add.copyFromFloats(0,0,c.useRightHandedSystem?1:-1)}h._attachedMesh&&h.position.copyFrom(h._attachedMesh.getAbsolutePosition()),h.position.addToRef(h._add,h._target);a=c.useRightHandedSystem?g.a.LookAtRHToRef:g.a.LookAtLHToRef;var b=c.useRightHandedSystem?g.a.PerspectiveFovRH:g.a.PerspectiveFovLH;a(h.position,h._target,g.e.Up(),h._viewMatrix),c.activeCamera&&(h._projectionMatrix=b(Math.PI/2,1,k?c.activeCamera.maxZ:c.activeCamera.minZ,k?c.activeCamera.minZ:c.activeCamera.maxZ,h._scene.getEngine().isNDCHalfZRange),c.setTransformMatrix(h._viewMatrix,h._projectionMatrix),c.activeCamera.isRigCamera&&!h._renderTargetTexture.activeCamera&&(h._renderTargetTexture.activeCamera=c.activeCamera.rigParent||null)),c._forcedViewPosition=h.position}),this._renderTargetTexture.onBeforeBindObservable.add(function(){var b,d;null===(d=(b=c.getEngine())._debugPushGroup)||void 0===d||d.call(b,"reflection probe generation for "+a,1),j=h._scene.imageProcessingConfiguration.applyByPostProcess,f&&(c.imageProcessingConfiguration.applyByPostProcess=!0)}),this._renderTargetTexture.onAfterUnbindObservable.add(function(){var a,b;c.imageProcessingConfiguration.applyByPostProcess=j,c._forcedViewPosition=null,c.updateTransformMatrix(!0),null===(b=(a=c.getEngine())._debugPopGroup)||void 0===b||b.call(a,1)})}return Object.defineProperty(a.prototype,"samples",{get:function(){return this._renderTargetTexture.samples},set:function(a){this._renderTargetTexture.samples=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"refreshRate",{get:function(){return this._renderTargetTexture.refreshRate},set:function(a){this._renderTargetTexture.refreshRate=a},enumerable:!1,configurable:!0}),a.prototype.getScene=function(){return this._scene},Object.defineProperty(a.prototype,"cubeTexture",{get:function(){return this._renderTargetTexture},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"renderList",{get:function(){return this._renderTargetTexture.renderList},enumerable:!1,configurable:!0}),a.prototype.attachToMesh=function(a){this._attachedMesh=a},a.prototype.setRenderingAutoClearDepthStencil=function(a,b){this._renderTargetTexture.setRenderingAutoClearDepthStencil(a,b)},a.prototype.dispose=function(){var a=this._scene.reflectionProbes.indexOf(this);if(-1!==a&&this._scene.reflectionProbes.splice(a,1),this._parentContainer){a=this._parentContainer.reflectionProbes.indexOf(this);a>-1&&this._parentContainer.reflectionProbes.splice(a,1),this._parentContainer=null}this._renderTargetTexture&&(this._renderTargetTexture.dispose(),this._renderTargetTexture=null)},a.prototype.toString=function(a){var b="Name: "+this.name;return a&&(b+=", position: "+this.position.toString(),this._attachedMesh&&(b+=", attached mesh: "+this._attachedMesh.name)),b},a.prototype.getClassName=function(){return"ReflectionProbe"},a.prototype.serialize=function(){var a=I.a.Serialize(this,this._renderTargetTexture.serialize());return a.isReflectionProbe=!0,a},a.Parse=function(b,c,d){var e=null;if(c.reflectionProbes)for(var f=0;f0){var d=b._waitingData.lods.ids,e=c.isEnabled(!1);if(b._waitingData.lods.distances){var f=b._waitingData.lods.distances;if(f.length>=d.length){var g=f.length>d.length?f[f.length-1]:0;c.setEnabled(!1);for(var h=0;h0&&c.addLODLevel(g,null),!0===e&&c.setEnabled(!0)}else T.b.Warn("Invalid level of detail distances for "+b.name)}}b._waitingData.lods=null}},hk=function(a,b,c,e,f){void 0===f&&(f=!1);var g=new Ha(a),h="importScene has failed JSON parse";try{var j=JSON.parse(b);h="";b=hh.loggingLevel===hh.DETAILED_LOGGING;if(void 0!==j.environmentTexture&&null!==j.environmentTexture){var k=void 0===j.isPBR||j.isPBR;if(j.environmentTextureType&&"BABYLON.HDRCubeTexture"===j.environmentTextureType){var l=j.environmentTextureSize?j.environmentTextureSize:128;l=new Pj((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a,l,!0,!k);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}else if(Object(gh.e)(j.environmentTexture,".env")){l=new Xh((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}else{l=Xh.CreateFromPrefilteredData((j.environmentTexture.match(/https?:///g)?"":c)+j.environmentTexture,a);j.environmentTextureRotationY&&(l.rotationY=j.environmentTextureRotationY),a.environmentTexture=l}if(!0===j.createDefaultSkybox){l=void 0!==a.activeCamera&&null!==a.activeCamera?(a.activeCamera.maxZ-a.activeCamera.minZ)/2:1e3;var m=j.skyboxBlurLevel||0;a.createDefaultSkybox(a.environmentTexture,k,l,m)}g.environmentTexture=a.environmentTexture}if(void 0!==j.environmentIntensity&&null!==j.environmentIntensity&&(a.environmentIntensity=j.environmentIntensity),void 0!==j.lights&&null!==j.lights)for(k=0,l=j.lights.length;k0){for(m=0;m0){for(p=0;p-1&&void 0!==k.skeletons&&null!==k.skeletons&&!1===D.indexOf(F.skeletonId)>-1)for(ba=0,J=k.skeletons.length;ba0&&(v+1)%4==0)g[v]=255;else{w=h[v];g[v]=w/p*255}a.is3D?(a.updateSize(i,i,i),d.updateRawTexture3D(a,g,r.a.TEXTUREFORMAT_RGBA,!1)):(a.updateSize(i*i,i),d.updateRawTexture(a,g,r.a.TEXTUREFORMAT_RGBA,!1)),a.isReady=!0,c._triggerOnLoad()}},f=this.getScene();return f?f._loadFile(this.url,e):d._loadFile(this.url,e),this._texture},b.prototype.loadTexture=function(){this.url&&this.url.toLocaleLowerCase().indexOf(".3dl")==this.url.length-4&&this.load3dlTexture()},b.prototype.clone=function(){var a=new b(this.url,this.getScene()||this._getEngine());return a.level=this.level,a},b.prototype.delayLoad=function(){this.delayLoadState===r.a.DELAYLOADSTATE_NOTLOADED&&(this.delayLoadState=r.a.DELAYLOADSTATE_LOADED,this._texture=this._getFromCache(this.url,!0),this._texture||this.loadTexture())},b.Parse=function(a,c){var d=null;return a.name&&!a.isRenderTarget&&((d=new b(a.name,c)).name=a.name,d.level=a.level),d},b.prototype.serialize=function(){if(!this.name)return null;var a={};return a.name=this.name,a.level=this.level,a.customType="BABYLON.ColorGradingTexture",a},b._noneEmptyLineRegex=/S+/,b}(Ie.a);Object(i.b)("BABYLON.ColorGradingTexture",pk);var qk=function(a){function b(b,c,d,e,f,g,h){void 0===e&&(e=!1),void 0===f&&(f=!0),void 0===g&&(g=null),void 0===h&&(h=null);var i=a.call(this,c)||this;if(i._onLoad=null,i._onError=null,!b)throw new Error("Image url is not set");return i._coordinatesMode=U.a.CUBIC_MODE,i.name=b,i.url=b,i._size=d,i._noMipmap=e,i.gammaSpace=f,i._onLoad=g,i._onError=h,i.hasAlpha=!1,i.isCube=!0,i._texture=i._getFromCache(b,i._noMipmap),i._texture?g&&(i._texture.isReady?T.b.SetImmediate(function(){return g()}):i._texture.onLoadedObservable.add(g)):c.useDelayedTextureLoading?i.delayLoadState=r.a.DELAYLOADSTATE_NOTLOADED:i.loadImage(i.loadTexture.bind(i),i._onError),i}return Object(l.d)(b,a),b.prototype.loadImage=function(a,b){var c=this,d=document.createElement("canvas");Object(ue.i)(this.url,function(b){c._width=b.width,c._height=b.height,d.width=c._width,d.height=c._height;var e=d.getContext("2d");e.drawImage(b,0,0);e=e.getImageData(0,0,b.width,b.height);c._buffer=e.data.buffer,d.remove(),a()},function(a,d){b&&b(c.getClassName()+" could not be loaded",d)},null)},b.prototype.loadTexture=function(){var a=this,c=this.getScene();c&&(this._texture=c.getEngine().createRawCubeTextureFromUrl(this.url,c,this._size,r.a.TEXTUREFORMAT_RGB,c.getEngine().getCaps().textureFloat?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_UNSIGNED_INTEGER,this._noMipmap,function(){for(var c=a.getFloat32ArrayFromArrayBuffer(a._buffer),c=Kj.ConvertPanoramaToCubemap(c,a._width,a._height,a._size),d=[],e=0;e<6;e++){var f=c[b._FacesMapping[e]];d.push(f)}return d},null,this._onLoad,this._onError))},b.prototype.getFloat32ArrayFromArrayBuffer=function(a){for(var b=new DataView(a),c=new Float32Array(3*a.byteLength/4),d=0,e=0;eb.length)q.a.Error("Unable to load TGA file - Not enough data");else{c+=d.id_length;var e=!1,f=!1,g=!1;switch(d.image_type){case 9:e=!0;case 1:f=!0;break;case 10:e=!0;case 2:break;case 11:e=!0;case 3:g=!0}var h,i,j,k,l,u,x,y=d.pixel_size>>3,z=d.width*d.height*y;if(f&&(h=b.subarray(c,c+=d.colormap_length*(d.colormap_size>>3))),e){var A,B;e=new Uint8Array(z);for(var C=0,D=new Uint8Array(y);c>4){default:case 2:i=0,k=1,x=d.width,j=0,l=1,u=d.height;break;case 0:i=0,k=1,x=d.width,j=d.height-1,l=-1,u=-1;break;case 3:i=d.width-1,k=-1,x=-1,j=0,l=1,u=d.height;break;case 1:i=d.width-1,k=-1,x=-1,j=d.height-1,l=-1,u=-1}A="_getImageData"+(g?"Grey":"")+d.pixel_size+"bits";B=uk[A](d,h,e,j,l,u,i,k,x);a.getEngine()._uploadDataToTextureDirectly(a,B)}}}var uk={GetTGAHeader:sk,UploadContent:tk,_getImageData8bits:function(a,b,c,d,e,f,g,h,i){var j,k;c=c;b=b;var l=a.width;a=a.height;var m=0;a=new Uint8Array(l*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,m++)j=c[m],a[4*(k+l*d)+3]=255,a[4*(k+l*d)+2]=b[3*j+0],a[4*(k+l*d)+1]=b[3*j+1],a[4*(k+l*d)+0]=b[3*j+2];return a},_getImageData16bits:function(a,b,c,d,e,f,g,h,i){var j,k;b=c;c=a.width;a=a.height;var l=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,l+=2){var m=255*((31744&(j=b[l+0]+(b[l+1]<<8)))>>10)/31|0,n=255*((992&j)>>5)/31|0,o=255*(31&j)/31|0;a[4*(k+c*d)+0]=m,a[4*(k+c*d)+1]=n,a[4*(k+c*d)+2]=o,a[4*(k+c*d)+3]=32768&j?0:255}return a},_getImageData24bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=3)a[4*(j+c*d)+3]=255,a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+1]=b[k+1],a[4*(j+c*d)+0]=b[k+2];return a},_getImageData32bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=4)a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+1]=b[k+1],a[4*(j+c*d)+0]=b[k+2],a[4*(j+c*d)+3]=b[k+3];return a},_getImageDataGrey8bits:function(a,b,c,d,e,f,g,h,i){var j,k;b=c;c=a.width;a=a.height;var l=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(k=g;k!==i;k+=h,l++)j=b[l],a[4*(k+c*d)+0]=j,a[4*(k+c*d)+1]=j,a[4*(k+c*d)+2]=j,a[4*(k+c*d)+3]=255;return a},_getImageDataGrey16bits:function(a,b,c,d,e,f,g,h,i){var j;b=c;c=a.width;a=a.height;var k=0;a=new Uint8Array(c*a*4);for(d=d;d!==f;d+=e)for(j=g;j!==i;j+=h,k+=2)a[4*(j+c*d)+0]=b[k+0],a[4*(j+c*d)+1]=b[k+0],a[4*(j+c*d)+2]=b[k+0],a[4*(j+c*d)+3]=b[k+1];return a}},vk=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".tga")},a.prototype.loadCubeData=function(a,b,c,d,e){throw".env not supported in Cube."},a.prototype.loadData=function(a,b,c){var d=new Uint8Array(a.buffer,a.byteOffset,a.byteLength);a=sk(d);c(a.width,a.height,b.generateMipMaps,!1,function(){tk(b,d)})},a}();S.a._TextureLoaders.push(new vk);var wk=function(){function a(){this.supportCascades=!1}return a.prototype.canLoad=function(a){return Object(gh.e)(a,".hdr")},a.prototype.loadCubeData=function(a,b,c,d,e){throw".env not supported in Cube."},a.prototype.loadData=function(a,b,c){for(var a=new Uint8Array(a.buffer,a.byteOffset,a.byteLength),d=Lj.RGBE_ReadHeader(a),a=Lj.RGBE_ReadPixels(a,d),e=d.width*d.height,f=new Float32Array(4*e),g=0;g>2&3],f[o++]=e[l>>4&3],f[o++]=e[l>>6&3]}}return f}(f,0,a.getImageWidth(b,c)+3&-4,a.getImageHeight(b,c)+3&-4));return f}onmessage=function(g){if("init"===g.data.action){if(!e){Module={wasmBinary:g.data.wasmBinary};try{importScripts(g.data.url)}catch(a){postMessage({action:"error",error:a})}e=new Promise(function(a){Module.onRuntimeInitialized=function(){Module.initializeBasis(),a()}})}e.then(function(){postMessage({action:"init"})})}else if("transcode"===g.data.action){var h=g.data.config,i=g.data.imageData;i=new Module.BasisFile(i);var j=function(a){for(var b=a.getHasAlpha(),c=a.getNumImages(),d=[],e=0;e1&&b.generateMipMaps;Fk(b,a),b.getEngine()._setCubeMapTextureParams(b,c),b.isReady=!0,b.onLoadedObservable.notifyObservers(b),b.onLoadedObservable.clear(),d&&d()})["catch"](function(a){T.b.Warn("Failed to transcode Basis file, transcoding may not be supported on this device"),b.isReady=!0,e&&e(a)})}},a.prototype.loadData=function(a,b,c){var d=b.getEngine().getCaps();d={supportedCompressionFormats:{etc1:!!d.etc1,s3tc:!!d.s3tc,pvrtc:!!d.pvrtc,etc2:!!d.etc2}};Ek(a,d).then(function(a){var d=a.fileInfo.images[0].levels[0],e=a.fileInfo.images[0].levels.length>1&&b.generateMipMaps;c(d.width,d.height,e,-1!==a.format,function(){Fk(b,a)})})["catch"](function(a){T.b.Warn("Failed to transcode Basis file, transcoding may not be supported on this device"),c(0,0,!1,!1,function(){},!0)})},a}();S.a._TextureLoaders.push(new Ik);var Jk=function(a){function b(b,c,d,e,f,g){var h=this,i=!(!f||!f.generateMipMaps)&&f.generateMipMaps,j=!(!f||!f.generateDepthTexture)&&f.generateDepthTexture,k=f&&f.depthTextureFormat?f.depthTextureFormat:r.a.TEXTUREFORMAT_DEPTH16,l=!f||void 0===f.doNotChangeAspectRatio||f.doNotChangeAspectRatio,n=!(!f||!f.drawOnlyOnFirstAttachmentByDefault)&&f.drawOnlyOnFirstAttachmentByDefault;if((h=a.call(this,b,c,e,i,l,void 0,void 0,void 0,void 0,void 0,void 0,void 0,!0)||this).isSupported){b=[];e=[];h._initTypes(d,b,e,f);l=!f||void 0===f.generateDepthBuffer||f.generateDepthBuffer;f=!(!f||void 0===f.generateStencilBuffer)&&f.generateStencilBuffer;return h._size=c,h._multiRenderTargetOptions={samplingModes:e,generateMipMaps:i,generateDepthBuffer:l,generateStencilBuffer:f,generateDepthTexture:j,depthTextureFormat:k,types:b,textureCount:d},h._count=d,h._drawOnlyOnFirstAttachmentByDefault=n,d>0&&(h._createInternalTextures(),h._createTextures(g)),h}h.dispose()}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"isSupported",{get:function(){var a;return null!==(a=null===(a=this._engine)||void 0===a?void 0:a.getCaps().drawBuffersExtension)&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"textures",{get:function(){return this._textures},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"count",{get:function(){return this._count},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"depthTexture",{get:function(){return this._textures[this._textures.length-1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"wrapU",{set:function(a){if(this._textures)for(var b=0;b=0;a--)this._textures[a]._texture=null;null===(a=this._renderTarget)||void 0===a||a.dispose(),this._renderTarget=null}},b}(cd),Kk=function(a,b,c){this.id=a,this.scale=b,this.offset=c},Lk=function(){function a(b,c,d,e){return this.name=b,this.meshes=c,this.scene=e,this.options=d,this.options.map=null!==(b=this.options.map)&&void 0!==b?b:["ambientTexture","bumpTexture","diffuseTexture","emissiveTexture","lightmapTexture","opacityTexture","reflectionTexture","refractionTexture","specularTexture"],this.options.uvsIn=null!==(c=this.options.uvsIn)&&void 0!==c?c:W.b.UVKind,this.options.uvsOut=null!==(e=this.options.uvsOut)&&void 0!==e?e:W.b.UVKind,this.options.layout=null!==(d=this.options.layout)&&void 0!==d?d:a.LAYOUT_STRIP,this.options.layout===a.LAYOUT_COLNUM&&(this.options.colnum=null!==(b=this.options.colnum)&&void 0!==b?b:8),this.options.updateInputMeshes=null===(c=this.options.updateInputMeshes)||void 0===c||c,this.options.disposeSources=null===(e=this.options.disposeSources)||void 0===e||e,this._expecting=0,this.options.fillBlanks=null===(d=this.options.fillBlanks)||void 0===d||d,!0===this.options.fillBlanks&&(this.options.customFillColor=null!==(b=this.options.customFillColor)&&void 0!==b?b:"black"),this.options.frameSize=null!==(c=this.options.frameSize)&&void 0!==c?c:256,this.options.paddingRatio=null!==(e=this.options.paddingRatio)&&void 0!==e?e:.0115,this._paddingValue=Math.ceil(this.options.frameSize*this.options.paddingRatio),this._paddingValue%2!=0&&this._paddingValue++,this.options.paddingMode=null!==(d=this.options.paddingMode)&&void 0!==d?d:a.SUBUV_WRAP,this.options.paddingMode===a.SUBUV_COLOR&&(this.options.paddingColor=null!==(b=this.options.paddingColor)&&void 0!==b?b:new h.b(0,0,0,1)),this.sets={},this.frames=[],this}return a.prototype._createFrames=function(a){for(var b=this,c=this._calculateSize(),d=new g.d(1,1).divide(c),e=0,f=this._expecting,i=this.meshes.length,j=Object.keys(this.sets),k=0;k0);for(var a=0;a0)}},a}();wi=" attribute vec2 position; varying vec2 vPosition; varying vec2 vUV; const vec2 madd=vec2(0.5,0.5); void main(void) { vPosition=position; vUV=position*madd+madd; gl_Position=vec4(position,0.0,1.0); }";X.a.ShadersStore.proceduralVertexShader=wi;var Nk=function(a){function b(b,c,d,e,g,h,i,j){void 0===g&&(g=null),void 0===h&&(h=!0),void 0===i&&(i=!1),void 0===j&&(j=r.a.TEXTURETYPE_UNSIGNED_INT);var k=a.call(this,null,e,!h)||this;k.isEnabled=!0,k.autoClear=!0,k.onGeneratedObservable=new f.c,k.onBeforeGenerationObservable=new f.c,k.nodeMaterialSource=null,k._textures={},k._currentRefreshId=-1,k._frameId=-1,k._refreshRate=1,k._vertexBuffers={},k._uniforms=new Array,k._samplers=new Array,k._floats={},k._ints={},k._floatsArrays={},k._colors3={},k._colors4={},k._vectors2={},k._vectors3={},k._matrices={},k._fallbackTextureUsed=!1,k._cachedDefines=null,k._contentUpdateId=-1,k._rtWrapper=null;var l=(e=k.getScene()||C.a.LastCreatedScene)._getComponent(Oa.a.NAME_PROCEDURALTEXTURE);l||(l=new Mk(e),e._addComponent(l)),e.proceduralTextures.push(k),k._fullEngine=e.getEngine(),k.name=b,k.isRenderTarget=!0,k._size=c,k._textureType=j,k._generateMipMaps=h,k._drawWrapper=new Ec.a(k._fullEngine),k.setFragment(d),k._fallbackTexture=g,i?(k._rtWrapper=k._fullEngine.createRenderTargetCubeTexture(c,{generateMipMaps:h,generateDepthBuffer:!1,generateStencilBuffer:!1,type:j}),k.setFloat("face",0)):k._rtWrapper=k._fullEngine.createRenderTargetTexture(c,{generateMipMaps:h,generateDepthBuffer:!1,generateStencilBuffer:!1,type:j}),k._texture=k._rtWrapper.texture;l=[];return l.push(1,1),l.push(-1,1),l.push(-1,-1),l.push(1,-1),k._vertexBuffers[W.b.PositionKind]=new W.b(k._fullEngine,l,W.b.PositionKind,!1,!1,2),k._createIndexBuffer(),k}return Object(l.d)(b,a),b.prototype.getEffect=function(){return this._drawWrapper.effect},b.prototype._setEffect=function(a){this._drawWrapper.effect=a},b.prototype.getContent=function(){var a=this;return this._contentData&&this._frameId===this._contentUpdateId||(this._contentData?this._contentData.then(function(b){a._contentData=a.readPixels(0,0,b),a._contentUpdateId=a._frameId}):(this._contentData=this.readPixels(0,0),this._contentUpdateId=this._frameId)),this._contentData},b.prototype._createIndexBuffer=function(){var a=this._fullEngine,b=[];b.push(0),b.push(1),b.push(2),b.push(0),b.push(2),b.push(3),this._indexBuffer=a.createIndexBuffer(b)},b.prototype._rebuild=function(){var a=this._vertexBuffers[W.b.PositionKind];a&&a._rebuild(),this._createIndexBuffer(),this.refreshRate===cd.REFRESHRATE_RENDER_ONCE&&(this.refreshRate=cd.REFRESHRATE_RENDER_ONCE)},b.prototype.reset=function(){var a;null===(a=this._drawWrapper.effect)||void 0===a||a.dispose()},b.prototype._getDefines=function(){return""},b.prototype.isReady=function(){var a,b=this,c=this._fullEngine;if(this.nodeMaterialSource)return this._drawWrapper.effect.isReady();if(!this._fragment)return!1;if(this._fallbackTextureUsed)return!0;var d=this._getDefines();return!(!this._drawWrapper.effect||d!==this._cachedDefines||!this._drawWrapper.effect.isReady())||(a=void 0!==this._fragment.fragmentElement?{vertex:"procedural",fragmentElement:this._fragment.fragmentElement}:{vertex:"procedural",fragment:this._fragment},this._cachedDefines!==d&&(this._cachedDefines=d,this._drawWrapper.effect=c.createEffect(a,[W.b.PositionKind],this._uniforms,this._samplers,d,void 0,void 0,function(){var a;null===(a=b._rtWrapper)||void 0===a||a.dispose(),b._rtWrapper=b._texture=null,b._fallbackTexture&&(b._texture=b._fallbackTexture._texture,b._texture&&b._texture.incrementReferences()),b._fallbackTextureUsed=!0})),this._drawWrapper.effect.isReady())},b.prototype.resetRefreshCounter=function(){this._currentRefreshId=-1},b.prototype.setFragment=function(a){this._fragment=a},Object.defineProperty(b.prototype,"refreshRate",{get:function(){return this._refreshRate},set:function(a){this._refreshRate=a,this.resetRefreshCounter()},enumerable:!1,configurable:!0}),b.prototype._shouldRender=function(){return this.isEnabled&&this.isReady()&&this._texture?!this._fallbackTextureUsed&&(-1===this._currentRefreshId||this.refreshRate===this._currentRefreshId?(this._currentRefreshId=1,this._frameId++,!0):(this._currentRefreshId++,!1)):(this._texture&&(this._texture.isReady=!1),!1)},b.prototype.getRenderSize=function(){return this._size},b.prototype.resize=function(a,b){var c;this._fallbackTextureUsed||(null===(c=this._rtWrapper)||void 0===c||c.dispose(),this._rtWrapper=this._fullEngine.createRenderTargetTexture(a,{generateMipMaps:b,generateDepthBuffer:!1,generateStencilBuffer:!1,type:this._textureType}),this._texture=this._rtWrapper.texture,this._size=a,this._generateMipMaps=b)},b.prototype._checkUniform=function(a){-1===this._uniforms.indexOf(a)&&this._uniforms.push(a)},b.prototype.setTexture=function(a,b){return-1===this._samplers.indexOf(a)&&this._samplers.push(a),this._textures[a]=b,this},b.prototype.setFloat=function(a,b){return this._checkUniform(a),this._floats[a]=b,this},b.prototype.setInt=function(a,b){return this._checkUniform(a),this._ints[a]=b,this},b.prototype.setFloats=function(a,b){return this._checkUniform(a),this._floatsArrays[a]=b,this},b.prototype.setColor3=function(a,b){return this._checkUniform(a),this._colors3[a]=b,this},b.prototype.setColor4=function(a,b){return this._checkUniform(a),this._colors4[a]=b,this},b.prototype.setVector2=function(a,b){return this._checkUniform(a),this._vectors2[a]=b,this},b.prototype.setVector3=function(a,b){return this._checkUniform(a),this._vectors3[a]=b,this},b.prototype.setMatrix=function(a,b){return this._checkUniform(a),this._matrices[a]=b,this},b.prototype.render=function(a){a=this.getScene();if(a){var b=this._fullEngine;if(b.enableEffect(this._drawWrapper),this.onBeforeGenerationObservable.notifyObservers(this),b.setState(!1),!this.nodeMaterialSource){for(var c in this._textures)this._drawWrapper.effect.setTexture(c,this._textures[c]);for(c in this._ints)this._drawWrapper.effect.setInt(c,this._ints[c]);for(c in this._floats)this._drawWrapper.effect.setFloat(c,this._floats[c]);for(c in this._floatsArrays)this._drawWrapper.effect.setArray(c,this._floatsArrays[c]);for(c in this._colors3)this._drawWrapper.effect.setColor3(c,this._colors3[c]);for(c in this._colors4){var d=this._colors4[c];this._drawWrapper.effect.setFloat4(c,d.r,d.g,d.b,d.a)}for(c in this._vectors2)this._drawWrapper.effect.setVector2(c,this._vectors2[c]);for(c in this._vectors3)this._drawWrapper.effect.setVector3(c,this._vectors3[c]);for(c in this._matrices)this._drawWrapper.effect.setMatrix(c,this._matrices[c])}if(this._texture&&this._rtWrapper){if(null===(d=b._debugPushGroup)||void 0===d||d.call(b,"procedural texture generation for "+this.name,1),this.isCube)for(c=0;c<6;c++)b.bindFramebuffer(this._rtWrapper,c,void 0,void 0,!0),b.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect),this._drawWrapper.effect.setFloat("face",c),this.autoClear&&b.clear(a.clearColor,!0,!1,!1),b.drawElementsType(qi.a.TriangleFillMode,0,6);else b.bindFramebuffer(this._rtWrapper,0,void 0,void 0,!0),b.bindBuffers(this._vertexBuffers,this._indexBuffer,this._drawWrapper.effect),this.autoClear&&b.clear(a.clearColor,!0,!1,!1),b.drawElementsType(qi.a.TriangleFillMode,0,6);b.unBindFramebuffer(this._rtWrapper,this.isCube),this.isCube&&b.generateMipMapsForCubemap(this._texture),null===(d=b._debugPopGroup)||void 0===d||d.call(b,1),this.onGenerated&&this.onGenerated(),this.onGeneratedObservable.notifyObservers(this)}}},b.prototype.clone=function(){var a=this.getSize();a=new b(this.name,a.width,this._fragment,this.getScene(),this._fallbackTexture,this._generateMipMaps);return a.hasAlpha=this.hasAlpha,a.level=this.level,a.coordinatesMode=this.coordinatesMode,a},b.prototype.dispose=function(){var b=this.getScene();if(b){var c=b.proceduralTextures.indexOf(this);c>=0&&b.proceduralTextures.splice(c,1);b=this._vertexBuffers[W.b.PositionKind];b&&(b.dispose(),this._vertexBuffers[W.b.PositionKind]=null),this._indexBuffer&&this._fullEngine._releaseBuffer(this._indexBuffer)&&(this._indexBuffer=null),this.onGeneratedObservable.clear(),this.onBeforeGenerationObservable.clear(),a.prototype.dispose.call(this)}},Object(l.c)([Object(I.d)()],b.prototype,"isEnabled",void 0),Object(l.c)([Object(I.d)()],b.prototype,"autoClear",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_generateMipMaps",void 0),Object(l.c)([Object(I.d)()],b.prototype,"_size",void 0),Object(l.c)([Object(I.d)()],b.prototype,"refreshRate",null),b}(U.a);Object(i.b)("BABYLON.ProceduralTexture",Nk);var Ok=function(a){function b(b,c,d,e,f,g){b=a.call(this,b,d,null,e,f,g)||this;return b._animate=!0,b._time=0,b._texturePath=c,b._loadJson(c),b.refreshRate=1,b}return Object(l.d)(b,a),b.prototype._loadJson=function(a){var b=this,c=function(){try{b.setFragment(b._texturePath)}catch(a){q.a.Log("No json or ShaderStore or DOM element found for CustomProceduralTexture")}};a=a+"/config.json";var d=new L.a;d.open("GET",a),d.addEventListener("load",function(){if(200===d.status||d.responseText&&d.responseText.length>0)try{b._config=JSON.parse(d.response),b.updateShaderUniforms(),b.updateTextures(),b.setFragment(b._texturePath+"/custom"),b._animate=b._config.animate,b.refreshRate=b._config.refreshrate}catch(a){c()}else c()},!1),d.addEventListener("error",function(){c()},!1);try{d.send()}catch(a){q.a.Error("CustomProceduralTexture: Error on XHR send request.")}},b.prototype.isReady=function(){if(!a.prototype.isReady.call(this))return!1;for(var b in this._textures)if(!this._textures[b].isReady())return!1;return!0},b.prototype.render=function(b){var c=this.getScene();this._animate&&c&&(this._time+=.03*c.getAnimationRatio(),this.updateShaderUniforms()),a.prototype.render.call(this,b)},b.prototype.updateTextures=function(){for(var a=0;a0},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isDirectlyConnectedToVertexOutput",{get:function(){if(!this.hasEndpoints)return!1;for(var a=0,b=this._endpoints;a=0)&&(b.isExposedOnFrame=!0,b.exposedPortPosition=this.exposedPortPosition),b},a.prototype.dispose=function(){this.onConnectionObservable.clear()},a}(),al=function(){function a(a,b,c,d){void 0===b&&(b=Z.Vertex),void 0===c&&(c=!1),void 0===d&&(d=!1),this._isFinalMerger=!1,this._isInput=!1,this._name="",this._isUnique=!1,this.inputsAreExclusive=!1,this._codeVariableName="",this._inputs=new Array,this._outputs=new Array,this.comments="",this.visibleInInspector=!1,this.visibleOnFrame=!1,this._target=b,this._originalTargetIsNeutral=b===Z.Neutral,this._isFinalMerger=c,this._isInput=d,this._name=a,this.uniqueId=Zd.a.UniqueId}return Object.defineProperty(a.prototype,"name",{get:function(){return this._name},set:function(a){this.validateBlockName(a)&&(this._name=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isUnique",{get:function(){return this._isUnique},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isFinalMerger",{get:function(){return this._isFinalMerger},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isInput",{get:function(){return this._isInput},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"buildId",{get:function(){return this._buildId},set:function(a){this._buildId=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"target",{get:function(){return this._target},set:function(a){0==(this._target&a)&&(this._target=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"inputs",{get:function(){return this._inputs},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"outputs",{get:function(){return this._outputs},enumerable:!1,configurable:!0}),a.prototype.getInputByName=function(a){var b=this._inputs.filter(function(b){return b.name===a});return b.length?b[0]:null},a.prototype.getOutputByName=function(a){var b=this._outputs.filter(function(b){return b.name===a});return b.length?b[0]:null},a.prototype.initialize=function(a){},a.prototype.bind=function(a,b,c,d){},a.prototype._declareOutput=function(a,b){return b._getGLType(a.type)+" "+a.associatedVariableName},a.prototype._writeVariable=function(a){return a.connectedPoint?""+a.associatedVariableName:"0."},a.prototype._writeFloat=function(a){a=a.toString();return-1===a.indexOf(".")&&(a+=".0"),""+a},a.prototype.getClassName=function(){return"NodeMaterialBlock"},a.prototype.registerInput=function(a,b,c,d,e){return void 0===c&&(c=!1),(e=null!=e?e:new $k(a,this,Uk.Input)).type=b,e.isOptional=c,d&&(e.target=d),this._inputs.push(e),this},a.prototype.registerOutput=function(a,b,c,d){return(d=null!=d?d:new $k(a,this,Uk.Output)).type=b,c&&(d.target=c),this._outputs.push(d),this},a.prototype.getFirstAvailableInput=function(a){void 0===a&&(a=null);for(var b=0,c=this._inputs;b=this._outputs.length?null:this._outputs[a+1]},a.prototype.isAnAncestorOf=function(a){for(var b=0,c=this._outputs;b[0.."+c.repeatKey+"] ";a=$f.a.IncludesShadersStore[a]+" ";if(this.sharedData.emitComments&&(a=b+" "+a),!c)return a;if(c.replaceStrings)for(b=0;b[0.."+c.repeatKey+"] ":this.functions[d]="#include<"+a+"> ",void (this.sharedData.emitComments&&(this.functions[d]=b+" "+this.functions[d]));if(this.functions[d]=$f.a.IncludesShadersStore[a],this.sharedData.emitComments&&(this.functions[d]=b+" "+this.functions[d]),c.removeIfDef&&(this.functions[d]=this.functions[d].replace(/^s*?#ifdef.+$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#endif.*$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#else.*$/gm,""),this.functions[d]=this.functions[d].replace(/^s*?#elif.*$/gm,"")),c.removeAttributes&&(this.functions[d]=this.functions[d].replace(/^s*?attribute.+$/gm,"")),c.removeUniforms&&(this.functions[d]=this.functions[d].replace(/^s*?uniform.+$/gm,"")),c.removeVaryings&&(this.functions[d]=this.functions[d].replace(/^s*?varying.+$/gm,"")),c.replaceStrings)for(a=0;a0||this._emitRateGradients&&this._emitRateGradients.length>0||this._lifeTimeGradients&&this._lifeTimeGradients.length>0},a.prototype.getDragGradients=function(){return this._dragGradients},a.prototype.getLimitVelocityGradients=function(){return this._limitVelocityGradients},a.prototype.getColorGradients=function(){return this._colorGradients},a.prototype.getSizeGradients=function(){return this._sizeGradients},a.prototype.getColorRemapGradients=function(){return this._colorRemapGradients},a.prototype.getAlphaRemapGradients=function(){return this._alphaRemapGradients},a.prototype.getLifeTimeGradients=function(){return this._lifeTimeGradients},a.prototype.getAngularSpeedGradients=function(){return this._angularSpeedGradients},a.prototype.getVelocityGradients=function(){return this._velocityGradients},a.prototype.getStartSizeGradients=function(){return this._startSizeGradients},a.prototype.getEmitRateGradients=function(){return this._emitRateGradients},Object.defineProperty(a.prototype,"direction1",{get:function(){return this.particleEmitterType.direction1?this.particleEmitterType.direction1:g.e.Zero()},set:function(a){this.particleEmitterType.direction1&&(this.particleEmitterType.direction1=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"direction2",{get:function(){return this.particleEmitterType.direction2?this.particleEmitterType.direction2:g.e.Zero()},set:function(a){this.particleEmitterType.direction2&&(this.particleEmitterType.direction2=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"minEmitBox",{get:function(){return this.particleEmitterType.minEmitBox?this.particleEmitterType.minEmitBox:g.e.Zero()},set:function(a){this.particleEmitterType.minEmitBox&&(this.particleEmitterType.minEmitBox=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"maxEmitBox",{get:function(){return this.particleEmitterType.maxEmitBox?this.particleEmitterType.maxEmitBox:g.e.Zero()},set:function(a){this.particleEmitterType.maxEmitBox&&(this.particleEmitterType.maxEmitBox=a)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isBillboardBased",{get:function(){return this._isBillboardBased},set:function(a){this._isBillboardBased!==a&&(this._isBillboardBased=a,this._reset())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"imageProcessingConfiguration",{get:function(){return this._imageProcessingConfiguration},set:function(a){this._attachImageProcessingConfiguration(a)},enumerable:!1,configurable:!0}),a.prototype._attachImageProcessingConfiguration=function(a){a!==this._imageProcessingConfiguration&&(!a&&this._scene?this._imageProcessingConfiguration=this._scene.imageProcessingConfiguration:this._imageProcessingConfiguration=a)},a.prototype._reset=function(){},a.prototype._removeGradientAndTexture=function(a,b,c){if(!b)return this;for(var d=0,e=0,f=b;e-1))return this._optimizers.push(a),this},b.prototype.unregisterOptimizer=function(a){a=this._optimizers.indexOf(a);if(-1!==a)return this._optimizers.splice(a,1),this},b.prototype.addOutputNode=function(a){if(null===a.target)throw"This node is not meant to be an output node. You may want to explicitly set its target value.";return 0!=(a.target&Z.Vertex)&&this._addVertexOutputNode(a),0!=(a.target&Z.Fragment)&&this._addFragmentOutputNode(a),this},b.prototype.removeOutputNode=function(a){return null===a.target||(0!=(a.target&Z.Vertex)&&this._removeVertexOutputNode(a),0!=(a.target&Z.Fragment)&&this._removeFragmentOutputNode(a)),this},b.prototype._addVertexOutputNode=function(a){if(-1===this._vertexOutputNodes.indexOf(a))return a.target=Z.Vertex,this._vertexOutputNodes.push(a),this},b.prototype._removeVertexOutputNode=function(a){a=this._vertexOutputNodes.indexOf(a);if(-1!==a)return this._vertexOutputNodes.splice(a,1),this},b.prototype._addFragmentOutputNode=function(a){if(-1===this._fragmentOutputNodes.indexOf(a))return a.target=Z.Fragment,this._fragmentOutputNodes.push(a),this},b.prototype._removeFragmentOutputNode=function(a){a=this._fragmentOutputNodes.indexOf(a);if(-1!==a)return this._fragmentOutputNodes.splice(a,1),this},b.prototype.needAlphaBlending=function(){return!this.ignoreAlpha&&(this.alpha<1||this._sharedData&&this._sharedData.hints.needAlphaBlending)},b.prototype.needAlphaTesting=function(){return this._sharedData&&this._sharedData.hints.needAlphaTesting},b.prototype._initializeBlock=function(a,b,c,d){if(void 0===d&&(d=!0),a.initialize(b),d&&a.autoConfigure(this),a._preparationId=this._buildId,-1===this.attachedBlocks.indexOf(a)){if(a.isUnique)for(var e=a.getClassName(),f=0,g=this.attachedBlocks;f-1&&this.attachedBlocks.splice(b,1),a.isFinalMerger&&this.removeOutputNode(a)},b.prototype.build=function(a,c,d){void 0===a&&(a=!1),void 0===c&&(c=!0),void 0===d&&(d=!0),this._buildWasSuccessful=!1;var e=this.getScene().getEngine(),f=this._mode===Sk.Particle;if(0===this._vertexOutputNodes.length&&!f)throw"You must define at least one vertexOutputNode";if(0===this._fragmentOutputNodes.length)throw"You must define at least one fragmentOutputNode";this._vertexCompilationState=new bl,this._vertexCompilationState.supportUniformBuffers=e.supportsUniformBuffers,this._vertexCompilationState.target=Z.Vertex,this._fragmentCompilationState=new bl,this._fragmentCompilationState.supportUniformBuffers=e.supportsUniformBuffers,this._fragmentCompilationState.target=Z.Fragment,this._sharedData=new cl,this._vertexCompilationState.sharedData=this._sharedData,this._fragmentCompilationState.sharedData=this._sharedData,this._sharedData.buildId=this._buildId,this._sharedData.emitComments=this._options.emitComments,this._sharedData.verbose=a,this._sharedData.scene=this.getScene(),this._sharedData.allowEmptyVertexProgram=f;for(var e=[],f=[],g=0,h=this._vertexOutputNodes;g0&&(Yh.a.BindMorphTargetParameters(c,a),c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(a))},b.prototype.replaceRepeatableContent=function(a,b,c,d){b=this.position;var e=this.normal,f=this.tangent,g=this.uv,h=this.positionOutput,i=this.normalOutput,j=this.tangentOutput,k=this.uvOutput;a=a;var l=d.NUM_MORPH_INFLUENCERS;c=c.morphTargetManager;var m=c&&c.supportsNormals&&d.NORMAL,o=c&&c.supportsTangents&&d.TANGENT;d=c&&c.supportsUVs&&d.UV1;var r="";(null==c?void 0:c.isUsingTextureForTargets)&&l>0&&(r+="float vertexID; ");for(var u=0;u0)for(u=0;u(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_SAMPLERNAME_,bump)/g,replace:""},{search:/uniform sampler2D bumpSampler;/g,replace:""},{search:/vec2 parallaxOcclusion(vec3 vViewDirCoT,vec3 vNormalCoT,vec2 texCoord,float parallaxScale)/g,replace:"#define inline vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale, sampler2D bumpSampler)"},{search:/vec2 parallaxOffset(vec3 viewDir,float heightScale)/g,replace:"vec2 parallaxOffset(vec3 viewDir, float heightScale, float height_)"},{search:/texture2D(bumpSampler,vBumpUV).w/g,replace:"height_"}]});g=i&&h?"texture2D("+h+", "+d.associatedVariableName+" + uvOffset).xyz":this.normalMapColor.associatedVariableName;return b.compilationString+=this._declareOutput(this.output,b)+" = vec4(0.); ",b.compilationString+=b._emitCodeFromInclude("bumpFragment",c,{replaceStrings:[{search:/perturbNormal(TBN,texture2D(bumpSampler,vBumpUV+uvOffset).xyz,vBumpInfos.y)/g,replace:"perturbNormal(TBN, "+g+", vBumpInfos.y)"},{search:/parallaxOcclusion(invTBN*-viewDirectionW,invTBN*normalW,vBumpUV,vBumpInfos.z)/g,replace:"parallaxOcclusion((invTBN * -viewDirectionW), (invTBN * normalW), vBumpUV, vBumpInfos.z, "+(i&&this.useParallaxOcclusion?h:"bumpSampler")+")"},{search:/parallaxOffset(invTBN*viewDirectionW,vBumpInfos.z)/g,replace:"parallaxOffset(invTBN * viewDirectionW, vBumpInfos.z, "+(i?this.parallaxHeight.associatedVariableName:"0.")+")"},{search:/vTangentSpaceParams/g,replace:this._tangentSpaceParameterName},{search:/vBumpInfos.y/g,replace:k},{search:/vBumpInfos.z/g,replace:j},{search:/vBumpUV/g,replace:d.associatedVariableName},{search:/vPositionW/g,replace:e.associatedVariableName+".xyz"},{search:/normalW=/g,replace:this.output.associatedVariableName+".xyz = "},{search:/mat3(normalMatrix)*normalW/g,replace:"mat3(normalMatrix) * "+this.output.associatedVariableName+".xyz"},{search:/normalW/g,replace:f.associatedVariableName+".xyz"},{search:/viewDirectionW/g,replace:i?this.viewDirection.associatedVariableName:"vec3(0.)"},l]}),this},b.prototype._dumpPropertiesCode=function(){var b=a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".invertX = ")+this.invertX+"; ";return b+=this._codeVariableName+".invertY = "+this.invertY+"; ",b+=this._codeVariableName+".useParallaxOcclusion = "+this.useParallaxOcclusion+"; "},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.invertX=this.invertX,b.invertY=this.invertY,b.useParallaxOcclusion=this.useParallaxOcclusion,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.invertX=b.invertX,this.invertY=b.invertY,this.useParallaxOcclusion=!!b.useParallaxOcclusion},Object(l.c)([gl("Invert X axis",el.Boolean,"PROPERTIES",{notifiers:{update:!1}})],b.prototype,"invertX",void 0),Object(l.c)([gl("Invert Y axis",el.Boolean,"PROPERTIES",{notifiers:{update:!1}})],b.prototype,"invertY",void 0),Object(l.c)([gl("Use parallax occlusion",el.Boolean)],b.prototype,"useParallaxOcclusion",void 0),b}(al);Object(i.b)("BABYLON.PerturbNormalBlock",Rl);var Sl=function(a){function b(b){b=a.call(this,b,Z.Fragment,!0)||this;return b.registerInput("value",$.Float,!0),b.registerInput("cutoff",$.Float,!0),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"DiscardBlock"},Object.defineProperty(b.prototype,"value",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"cutoff",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.sharedData.hints.needAlphaTesting=!0,this.cutoff.isConnected&&this.value.isConnected)return b.compilationString+="if ("+this.value.associatedVariableName+" < "+this.cutoff.associatedVariableName+") discard; ",this},b}(al);Object(i.b)("BABYLON.DiscardBlock",Sl);var Tl=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerOutput("output",$.Float,Z.Fragment),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FrontFacingBlock"},Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.target===Z.Vertex)throw"FrontFacingBlock must only be used in a fragment shader";var c=this._outputs[0];return b.compilationString+=this._declareOutput(c,b)+" = gl_FrontFacing ? 1.0 : 0.0; ",this},b}(al);Object(i.b)("BABYLON.FrontFacingBlock",Tl);var Ul=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerInput("input",$.AutoDetect,!1),b.registerOutput("dx",$.BasedOnInput),b.registerOutput("dy",$.BasedOnInput),b._outputs[0]._typeConnectionSource=b._inputs[0],b._outputs[1]._typeConnectionSource=b._inputs[0],b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"DerivativeBlock"},Object.defineProperty(b.prototype,"input",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dx",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dy",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){a.prototype._buildBlock.call(this,b);var c=this._outputs[0],d=this._outputs[1];return b._emitExtension("derivatives","#extension GL_OES_standard_derivatives : enable"),c.hasEndpoints&&(b.compilationString+=this._declareOutput(c,b)+" = dFdx("+this.input.associatedVariableName+"); "),d.hasEndpoints&&(b.compilationString+=this._declareOutput(d,b)+" = dFdy("+this.input.associatedVariableName+"); "),this},b}(al);Object(i.b)("BABYLON.DerivativeBlock",Ul);var Vl=function(a){function b(b){b=a.call(this,b,Z.Fragment)||this;return b.registerOutput("xy",$.Vector2,Z.Fragment),b.registerOutput("xyz",$.Vector3,Z.Fragment),b.registerOutput("xyzw",$.Vector4,Z.Fragment),b.registerOutput("x",$.Float,Z.Fragment),b.registerOutput("y",$.Float,Z.Fragment),b.registerOutput("z",$.Float,Z.Fragment),b.registerOutput("w",$.Float,Z.Fragment),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"FragCoordBlock"},Object.defineProperty(b.prototype,"xy",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"xyz",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"xyzw",{get:function(){return this._outputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"x",{get:function(){return this._outputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"y",{get:function(){return this._outputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"z",{get:function(){return this._outputs[5]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[6]},enumerable:!1,configurable:!0}),b.prototype.writeOutputs=function(a){for(var b="",c=0,d=this._outputs;c=0;Yh.a.PrepareUniformsAndSamplersForLight(e,a.uniforms,a.samplers,c["PROJECTEDLIGHTTEXTURE"+e],d,f)}},b.prototype.bind=function(a,b,c){if(c){var d=c.getScene();this.light?Yh.a.BindLight(this.light,this._lightId,d,a,!0):Yh.a.BindLights(d,c,a,!0,b.maxSimultaneousLights)}},b.prototype._injectVertexCode=function(a){var b=this.worldPosition,c="//"+this.name;this.light?(this._lightId=(void 0!==a.counters.lightCounter?a.counters.lightCounter:-1)+1,a.counters.lightCounter=this._lightId,a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString())):(a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),this._lightId=0,a.sharedData.dynamicUniformBlocks.push(this));var d="v_"+b.associatedVariableName;a._emitVaryingFromString(d,"vec4")&&(a.compilationString+=d+" = "+b.associatedVariableName+"; "),this.light?a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()},{search:/worldPos/g,replace:b.associatedVariableName}]}):(a.compilationString+="vec4 worldPos = "+b.associatedVariableName+"; ",this.view.isConnected&&(a.compilationString+="mat4 view = "+this.view.associatedVariableName+"; "),a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{repeatKey:"maxSimultaneousLights"}))},b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),b.target===Z.Fragment){b.sharedData.forcedBindableBlocks.push(this),b.sharedData.blocksWithDefines.push(this);var c="//"+this.name,d=this.worldPosition;b._emitFunctionFromInclude("helperFunctions",c),b._emitFunctionFromInclude("lightsFragmentFunctions",c,{replaceStrings:[{search:/vPositionW/g,replace:"v_"+d.associatedVariableName+".xyz"}]}),b._emitFunctionFromInclude("shadowsFragmentFunctions",c,{replaceStrings:[{search:/vPositionW/g,replace:"v_"+d.associatedVariableName+".xyz"}]}),this.light?b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString()):b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),0===this._lightId&&(b._registerTempVariable("viewDirectionW")&&(b.compilationString+="vec3 viewDirectionW = normalize("+this.cameraPosition.associatedVariableName+" - v_"+d.associatedVariableName+".xyz); "),b.compilationString+="lightingInfo info; ",b.compilationString+="float shadow = 1.; ",b.compilationString+="float glossiness = "+(this.glossiness.isConnected?this.glossiness.associatedVariableName:"1.0")+" * "+(this.glossPower.isConnected?this.glossPower.associatedVariableName:"1024.0")+"; ",b.compilationString+="vec3 diffuseBase = vec3(0., 0., 0.); ",b.compilationString+="vec3 specularBase = vec3(0., 0., 0.); ",b.compilationString+="vec3 normalW = "+this.worldNormal.associatedVariableName+".xyz; "),this.light?b.compilationString+=b._emitCodeFromInclude("lightFragment",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]}):b.compilationString+=b._emitCodeFromInclude("lightFragment",c,{repeatKey:"maxSimultaneousLights"});d=this.diffuseOutput;c=this.specularOutput;return b.compilationString+=this._declareOutput(d,b)+" = diffuseBase"+(this.diffuseColor.isConnected?" * "+this.diffuseColor.associatedVariableName:"")+"; ",c.hasEndpoints&&(b.compilationString+=this._declareOutput(c,b)+" = specularBase"+(this.specularColor.isConnected?" * "+this.specularColor.associatedVariableName:"")+"; "),this.shadow.hasEndpoints&&(b.compilationString+=this._declareOutput(this.shadow,b)+" = shadow; "),this}this._injectVertexCode(b)},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return this.light&&(b.lightId=this.light.id),b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),b.lightId&&(this.light=c.getLightById(b.lightId))},b}(al);Object(i.b)("BABYLON.LightBlock",Yl);var Zl=function(a){function b(b,c,d,e,f,g){c=a.call(this,b,c,d)||this;return c._blockType=e,c._blockName=f,c._nameForCheking=g,c._nameForCheking||(c._nameForCheking=b),c.needDualDirectionValidation=!0,c}return Object(l.d)(b,a),b.prototype.checkCompatibilityState=function(a){return a instanceof b&&a.name===this._nameForCheking?Tk.Compatible:Tk.TypeIncompatible},b.prototype.createCustomInputBlock=function(){return[new this._blockType(this._blockName),this.name]},b}($k),$l=function(a){function b(c){c=a.call(this,c,Z.VertexAndFragment)||this;return c.registerOutput("source",$.Object,Z.VertexAndFragment,new Zl("source",c,Uk.Output,b,"ImageSourceBlock")),c}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"texture",{get:function(){return this._texture},set:function(a){var b,c=this;if(this._texture!==a){b=null!==(b=null==a?void 0:a.getScene())&&void 0!==b?b:S.a.LastCreatedScene;!a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return a.hasTexture(c._texture)}),this._texture=a,a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(b){return b.hasTexture(a)})}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samplerName",{get:function(){return this._samplerName},enumerable:!1,configurable:!0}),b.prototype.bind=function(a,b,c){this.texture&&a.setTexture(this._samplerName,this.texture)},b.prototype.isReady=function(){return!(this.texture&&!this.texture.isReadyOrNotBlocking())},b.prototype.getClassName=function(){return"ImageSourceBlock"},Object.defineProperty(b.prototype,"source",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){return a.prototype._buildBlock.call(this,b),b.target===Z.Vertex&&(this._samplerName=b._getFreeVariableName(this.name+"Sampler"),b.sharedData.blockingBlocks.push(this),b.sharedData.textureBlocks.push(this),b.sharedData.bindableBlocks.push(this)),b._emit2DSampler(this._samplerName),this},b.prototype._dumpPropertiesCode=function(){var b=a.prototype._dumpPropertiesCode.call(this);return this.texture?(b+=this._codeVariableName+".texture = new BABYLON.Texture(""+this.texture.name+"", null, "+this.texture.noMipmap+", "+this.texture.invertY+", "+this.texture.samplingMode+"); ",b+=this._codeVariableName+".texture.wrapU = "+this.texture.wrapU+"; ",b+=this._codeVariableName+".texture.wrapV = "+this.texture.wrapV+"; ",b+=this._codeVariableName+".texture.uAng = "+this.texture.uAng+"; ",b+=this._codeVariableName+".texture.vAng = "+this.texture.vAng+"; ",b+=this._codeVariableName+".texture.wAng = "+this.texture.wAng+"; ",b+=this._codeVariableName+".texture.uOffset = "+this.texture.uOffset+"; ",b+=this._codeVariableName+".texture.vOffset = "+this.texture.vOffset+"; ",b+=this._codeVariableName+".texture.uScale = "+this.texture.uScale+"; ",b+=this._codeVariableName+".texture.vScale = "+this.texture.vScale+"; ",b+=this._codeVariableName+".texture.coordinatesMode = "+this.texture.coordinatesMode+"; "):b},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return this.texture&&!this.texture.isRenderTarget&&"VideoTexture"!==this.texture.getClassName()&&(b.texture=this.texture.serialize()),b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),b.texture&&!Ll.IgnoreTexturesAtLoadTime&&void 0!==b.texture.url&&(d=0===b.texture.url.indexOf("data:")?"":d,this.texture=U.a.Parse(b.texture,c,d))},b}(al);Object(i.b)("BABYLON.ImageSourceBlock",$l);var am=function(a){function b(b,c){void 0===c&&(c=!1);b=a.call(this,b,c?Z.Fragment:Z.VertexAndFragment)||this;return b.convertToGammaSpace=!1,b.convertToLinearSpace=!1,b.disableLevelMultiplication=!1,b._fragmentOnly=c,b.registerInput("uv",$.Vector2,!1,Z.VertexAndFragment),b.registerInput("source",$.Object,!0,Z.VertexAndFragment,new Zl("source",b,Uk.Input,$l,"ImageSourceBlock")),b.registerOutput("rgba",$.Color4,Z.Neutral),b.registerOutput("rgb",$.Color3,Z.Neutral),b.registerOutput("r",$.Float,Z.Neutral),b.registerOutput("g",$.Float,Z.Neutral),b.registerOutput("b",$.Float,Z.Neutral),b.registerOutput("a",$.Float,Z.Neutral),b.registerOutput("level",$.Float,Z.Neutral),b._inputs[0].acceptedConnectionPointTypes.push($.Vector3),b._inputs[0].acceptedConnectionPointTypes.push($.Vector4),b._inputs[0]._prioritizeVertex=!c,b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"texture",{get:function(){var a;return this.source.isConnected?(null===(a=this.source.connectedPoint)||void 0===a?void 0:a.ownerBlock).texture:this._texture},set:function(a){var b,c=this;if(this._texture!==a){b=null!==(b=null==a?void 0:a.getScene())&&void 0!==b?b:S.a.LastCreatedScene;!a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(a){return a.hasTexture(c._texture)}),this._texture=a,a&&b&&b.markAllMaterialsAsDirty(r.a.MATERIAL_TextureDirtyFlag,function(b){return b.hasTexture(a)})}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samplerName",{get:function(){return this._imageSource?this._imageSource.samplerName:this._samplerName},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hasImageSource",{get:function(){return!!this._imageSource},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"TextureBlock"},Object.defineProperty(b.prototype,"uv",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"source",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rgba",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"rgb",{get:function(){return this._outputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"r",{get:function(){return this._outputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"g",{get:function(){return this._outputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"b",{get:function(){return this._outputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"a",{get:function(){return this._outputs[5]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"level",{get:function(){return this._outputs[6]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"target",{get:function(){if(this._fragmentOnly)return Z.Fragment;if(!this.uv.isConnected)return Z.VertexAndFragment;if(this.uv.sourceBlock.isInput)return Z.VertexAndFragment;for(var a=this.uv.connectedPoint;a;){if(a.target===Z.Fragment)return Z.Fragment;if(a.target===Z.Vertex)return Z.VertexAndFragment;if(a.target===Z.Neutral||a.target===Z.VertexAndFragment){var b=a.ownerBlock;if(b.target===Z.Fragment)return Z.Fragment;a=null;for(var c=0,b=b.inputs;c=0;Yh.a.PrepareUniformsAndSamplersForLight(e,a.uniforms,a.samplers,c["PROJECTEDLIGHTTEXTURE"+e],d,f)}},b.prototype.isReady=function(a,b,c){return!(this._environmentBRDFTexture&&!this._environmentBRDFTexture.isReady())&&!(c._areImageProcessingDirty&&b.imageProcessingConfiguration&&!b.imageProcessingConfiguration.isReady())},b.prototype.bind=function(a,b,c){if(c){var d=c.getScene();this.light?Yh.a.BindLight(this.light,this._lightId,d,a,!0):Yh.a.BindLights(d,c,a,!0,b.maxSimultaneousLights),a.setTexture(this._environmentBrdfSamplerName,this._environmentBRDFTexture),a.setFloat2("vDebugMode",this.debugLimit,this.debugFactor);c=this._scene.ambientColor;c&&a.setColor3("ambientFromScene",c);c=d.useRightHandedSystem===(null!=d._mirroredCameraPosition);a.setFloat(this._invertNormalName,c?-1:1),a.setFloat4("vLightingIntensity",this.directIntensity,1,this.environmentIntensity*this._scene.environmentIntensity,this.specularIntensity);d=null!==(c=null===(d=this.indexOfRefraction.connectInputBlock)||void 0===d?void 0:d.value)&&void 0!==c?c:1.5;c=Math.pow((d-1)/(d+1),2);this._metallicReflectanceColor.scaleToRef(c*this._metallicF0Factor,h.c.Color3[0]);d=this._metallicF0Factor;a.setColor4(this._vMetallicReflectanceFactorsName,h.c.Color3[0],d),b.imageProcessingConfiguration&&b.imageProcessingConfiguration.bind(a)}},b.prototype._injectVertexCode=function(a){var b=this.worldPosition,c="//"+this.name;this.light?(this._lightId=(void 0!==a.counters.lightCounter?a.counters.lightCounter:-1)+1,a.counters.lightCounter=this._lightId,a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString())):(a._emitFunctionFromInclude(a.supportUniformBuffers?"lightVxUboDeclaration":"lightVxFragmentDeclaration",c,{repeatKey:"maxSimultaneousLights"}),this._lightId=0,a.sharedData.dynamicUniformBlocks.push(this));var d="v_"+b.associatedVariableName;a._emitVaryingFromString(d,"vec4")&&(a.compilationString+=d+" = "+b.associatedVariableName+"; ");d=this.reflection.isConnected?null===(d=this.reflection.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;d&&(d.viewConnectionPoint=this.view),a.compilationString+=null!==(d=null==d?void 0:d.handleVertexSide(a))&&void 0!==d?d:"",a._emitUniformFromString("vDebugMode","vec2","defined(IGNORE) || DEBUGMODE > 0"),a._emitUniformFromString("ambientFromScene","vec3"),a._emitVaryingFromString("vClipSpacePosition","vec4","defined(IGNORE) || DEBUGMODE > 0")&&(a._injectAtEnd+="#if DEBUGMODE > 0 ",a._injectAtEnd+="vClipSpacePosition = gl_Position; ",a._injectAtEnd+="#endif "),this.light?a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()},{search:/worldPos/g,replace:b.associatedVariableName}]}):(a.compilationString+="vec4 worldPos = "+b.associatedVariableName+"; ",this.view.isConnected&&(a.compilationString+="mat4 view = "+this.view.associatedVariableName+"; "),a.compilationString+=a._emitCodeFromInclude("shadowsVertex",c,{repeatKey:"maxSimultaneousLights"}))},b.prototype._getAlbedoOpacityCode=function(){var a="albedoOpacityOutParams albedoOpacityOut; ";return a+="albedoOpacityBlock( vec4("+(this.baseColor.isConnected?this.baseColor.associatedVariableName:"vec3(1.)")+", 1.), #ifdef ALBEDO vec4(1.), vec2(1., 1.), #endif #ifdef OPACITY vec4("+(this.opacity.isConnected?this.opacity.associatedVariableName:"1.")+"), vec2(1., 1.), #endif albedoOpacityOut ); vec3 surfaceAlbedo = albedoOpacityOut.surfaceAlbedo; float alpha = albedoOpacityOut.alpha; "},b.prototype._getAmbientOcclusionCode=function(){var a="ambientOcclusionOutParams aoOut; ";return a+="ambientOcclusionBlock( #ifdef AMBIENT vec3("+(this.ambientOcc.isConnected?this.ambientOcc.associatedVariableName:"1.")+"), vec4(0., 1.0, 1.0, 0.), #endif aoOut ); "},b.prototype._getReflectivityCode=function(a){var b="reflectivityOutParams reflectivityOut; ";return this._vMetallicReflectanceFactorsName=a._getFreeVariableName("vMetallicReflectanceFactors"),a._emitUniformFromString(this._vMetallicReflectanceFactorsName,"vec4"),b+="vec3 baseColor = surfaceAlbedo; reflectivityBlock( vec4("+this.metallic.associatedVariableName+", "+this.roughness.associatedVariableName+", 0., 0.), #ifdef METALLICWORKFLOW surfaceAlbedo, "+this._vMetallicReflectanceFactorsName+", #endif #ifdef REFLECTIVITY vec3(0., 0., 1.), vec4(1.), #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor, #endif #ifdef MICROSURFACEMAP microSurfaceTexel, <== not handled! #endif reflectivityOut ); float microSurface = reflectivityOut.microSurface; float roughness = reflectivityOut.roughness; #ifdef METALLICWORKFLOW surfaceAlbedo = reflectivityOut.surfaceAlbedo; #endif #if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED) aoOut.ambientOcclusionColor = reflectivityOut.ambientOcclusionColor; #endif "},b.prototype._buildBlock=function(b){var c,d,e,f;a.prototype._buildBlock.call(this,b),this._scene=b.sharedData.scene,this._environmentBRDFTexture||(this._environmentBRDFTexture=ii(this._scene));c=this.reflection.isConnected?null===(c=this.reflection.connectedPoint)||void 0===c?void 0:c.ownerBlock:null;if(c&&(c.worldPositionConnectionPoint=this.worldPosition,c.cameraPositionConnectionPoint=this.cameraPosition,c.worldNormalConnectionPoint=this.worldNormal),b.target!==Z.Fragment)return this._injectVertexCode(b),this;b.sharedData.forcedBindableBlocks.push(this),b.sharedData.blocksWithDefines.push(this),b.sharedData.blockingBlocks.push(this);var g="//"+this.name,h="v_"+this.worldPosition.associatedVariableName,i=this.perturbedNormal;this._environmentBrdfSamplerName=b._getFreeVariableName("environmentBrdfSampler"),b._emit2DSampler(this._environmentBrdfSamplerName),b.sharedData.hints.needAlphaBlending=b.sharedData.hints.needAlphaBlending||this.useAlphaBlending,b.sharedData.hints.needAlphaTesting=b.sharedData.hints.needAlphaTesting||this.useAlphaTest,b._emitExtension("lod","#extension GL_EXT_shader_texture_lod : enable","defined(LODBASEDMICROSFURACE)"),b._emitExtension("derivatives","#extension GL_OES_standard_derivatives : enable"),b.uniforms.push("exposureLinear"),b.uniforms.push("contrast"),b.uniforms.push("vInverseScreenSize"),b.uniforms.push("vignetteSettings1"),b.uniforms.push("vignetteSettings2"),b.uniforms.push("vCameraColorCurveNegative"),b.uniforms.push("vCameraColorCurveNeutral"),b.uniforms.push("vCameraColorCurvePositive"),b.uniforms.push("txColorTransform"),b.uniforms.push("colorTransformSettings"),this.light?b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",g,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]},this._lightId.toString()):b._emitFunctionFromInclude(b.supportUniformBuffers?"lightUboDeclaration":"lightFragmentDeclaration",g,{repeatKey:"maxSimultaneousLights"}),b._emitFunctionFromInclude("helperFunctions",g),b._emitFunctionFromInclude("importanceSampling",g),b._emitFunctionFromInclude("pbrHelperFunctions",g),b._emitFunctionFromInclude("imageProcessingDeclaration",g),b._emitFunctionFromInclude("imageProcessingFunctions",g),b._emitFunctionFromInclude("shadowsFragmentFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrDirectLightingSetupFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrDirectLightingFalloffFunctions",g),b._emitFunctionFromInclude("pbrBRDFFunctions",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"}]}),b._emitFunctionFromInclude("hdrFilteringFunctions",g),b._emitFunctionFromInclude("pbrDirectLightingFunctions",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"}]}),b._emitFunctionFromInclude("pbrIBLFunctions",g),b._emitFunctionFromInclude("pbrBlockAlbedoOpacity",g),b._emitFunctionFromInclude("pbrBlockReflectivity",g),b._emitFunctionFromInclude("pbrBlockAmbientOcclusion",g),b._emitFunctionFromInclude("pbrBlockAlphaFresnel",g),b._emitFunctionFromInclude("pbrBlockAnisotropic",g),b._emitUniformFromString("vLightingIntensity","vec4"),this._vNormalWName=b._getFreeVariableName("vNormalW"),b.compilationString+="vec4 "+this._vNormalWName+" = normalize("+this.worldNormal.associatedVariableName+"); ",b._registerTempVariable("viewDirectionW")&&(b.compilationString+="vec3 viewDirectionW = normalize("+this.cameraPosition.associatedVariableName+" - "+h+".xyz); "),b.compilationString+="vec3 geometricNormalW = "+this._vNormalWName+".xyz; ",b.compilationString+="vec3 normalW = "+(i.isConnected?"normalize("+i.associatedVariableName+".xyz)":"geometricNormalW")+"; ",this._invertNormalName=b._getFreeVariableName("invertNormal"),b._emitUniformFromString(this._invertNormalName,"float"),b.compilationString+=b._emitCodeFromInclude("pbrBlockNormalFinal",g,{replaceStrings:[{search:/vPositionW/g,replace:h+".xyz"},{search:/vEyePosition.w/g,replace:this._invertNormalName}]}),b.compilationString+=this._getAlbedoOpacityCode(),b.compilationString+=b._emitCodeFromInclude("depthPrePass",g),b.compilationString+=this._getAmbientOcclusionCode(),b.compilationString+=b._emitCodeFromInclude("pbrBlockLightmapInit",g),b.compilationString+="#ifdef UNLIT vec3 diffuseBase = vec3(1., 1., 1.); #else ",b.compilationString+=this._getReflectivityCode(b),b.compilationString+=b._emitCodeFromInclude("pbrBlockGeometryInfo",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"},{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"}]});i=this.anisotropy.isConnected?null===(d=this.anisotropy.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;i&&(i.worldPositionConnectionPoint=this.worldPosition,i.worldNormalConnectionPoint=this.worldNormal,b.compilationString+=i.getCode(b,!this.perturbedNormal.isConnected)),c&&c.hasTexture&&(b.compilationString+=c.getCode(b,i?"anisotropicOut.anisotropicNormal":"normalW")),b._emitFunctionFromInclude("pbrBlockReflection",g,{replaceStrings:[{search:/computeReflectionCoords/g,replace:"computeReflectionCoordsPBR"},{search:/REFLECTIONMAP_3D/g,replace:null!==(d=null==c?void 0:c._define3DName)&&void 0!==d?d:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(i=null==c?void 0:c._defineOppositeZ)&&void 0!==i?i:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(d=null==c?void 0:c._defineProjectionName)&&void 0!==d?d:"REFLECTIONMAP_PROJECTION"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(d=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==d?d:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(i=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==i?i:"LINEARSPECULARREFLECTION"},{search:/vReflectionFilteringInfo/g,replace:null!==(d=null==c?void 0:c._vReflectionFilteringInfoName)&&void 0!==d?d:"vReflectionFilteringInfo"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockReflectance0",g,{replaceStrings:[{search:/metallicReflectanceFactors/g,replace:this._vMetallicReflectanceFactorsName}]});d=this.sheen.isConnected?null===(i=this.sheen.connectedPoint)||void 0===i?void 0:i.ownerBlock:null;d&&(b.compilationString+=d.getCode(c)),b._emitFunctionFromInclude("pbrBlockSheen",g,{replaceStrings:[{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(d=null==c?void 0:c._defineSkyboxName)&&void 0!==d?d:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(i=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==i?i:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(d=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==d?d:"LINEARSPECULARREFLECTION"}]});d=this.clearcoat.isConnected?null===(i=this.clearcoat.connectedPoint)||void 0===i?void 0:i.ownerBlock:null;i=!this.perturbedNormal.isConnected&&!this.anisotropy.isConnected;e=this.perturbedNormal.isConnected&&(null===(e=(null===(e=this.perturbedNormal.connectedPoint)||void 0===e?void 0:e.ownerBlock).worldTangent)||void 0===e?void 0:e.isConnected);f=this.anisotropy.isConnected&&(null===(f=this.anisotropy.connectedPoint)||void 0===f?void 0:f.ownerBlock).worldTangent.isConnected;e=e||!this.perturbedNormal.isConnected&&f;b.compilationString+=Tm.GetCode(b,d,c,h,i,e,this.worldNormal.associatedVariableName),i&&(e=null!==(f=null==d?void 0:d.worldTangent.isConnected)&&void 0!==f&&f),b._emitFunctionFromInclude("pbrBlockClearcoat",g,{replaceStrings:[{search:/computeReflectionCoords/g,replace:"computeReflectionCoordsPBR"},{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(d=null==c?void 0:c._defineOppositeZ)&&void 0!==d?d:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(f=null==c?void 0:c._defineProjectionName)&&void 0!==f?f:"REFLECTIONMAP_PROJECTION"},{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/LODINREFLECTIONALPHA/g,replace:null!==(d=null==c?void 0:c._defineLODReflectionAlpha)&&void 0!==d?d:"LODINREFLECTIONALPHA"},{search:/LINEARSPECULARREFLECTION/g,replace:null!==(f=null==c?void 0:c._defineLinearSpecularReflection)&&void 0!==f?f:"LINEARSPECULARREFLECTION"},{search:/defined(TANGENT)/g,replace:e?"defined(TANGENT)":"defined(IGNORE)"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockReflectance",g,{replaceStrings:[{search:/REFLECTIONMAP_SKYBOX/g,replace:null!==(i=null==c?void 0:c._defineSkyboxName)&&void 0!==i?i:"REFLECTIONMAP_SKYBOX"},{search:/REFLECTIONMAP_3D/g,replace:null!==(d=null==c?void 0:c._define3DName)&&void 0!==d?d:"REFLECTIONMAP_3D"}]});e=this.subsurface.isConnected?null===(f=this.subsurface.connectedPoint)||void 0===f?void 0:f.ownerBlock:null;f=this.subsurface.isConnected?null===(d=(null===(i=this.subsurface.connectedPoint)||void 0===i?void 0:i.ownerBlock).refraction.connectedPoint)||void 0===d?void 0:d.ownerBlock:null;f&&(f.viewConnectionPoint=this.view,f.indexOfRefractionConnectionPoint=this.indexOfRefraction),b.compilationString+=Vm.GetCode(b,e,c,h),b._emitFunctionFromInclude("pbrBlockSubSurface",g,{replaceStrings:[{search:/REFLECTIONMAP_3D/g,replace:null!==(i=null==c?void 0:c._define3DName)&&void 0!==i?i:"REFLECTIONMAP_3D"},{search:/REFLECTIONMAP_OPPOSITEZ/g,replace:null!==(d=null==c?void 0:c._defineOppositeZ)&&void 0!==d?d:"REFLECTIONMAP_OPPOSITEZ"},{search:/REFLECTIONMAP_PROJECTION/g,replace:null!==(e=null==c?void 0:c._defineProjectionName)&&void 0!==e?e:"REFLECTIONMAP_PROJECTION"},{search:/SS_REFRACTIONMAP_3D/g,replace:null!==(i=null==f?void 0:f._define3DName)&&void 0!==i?i:"SS_REFRACTIONMAP_3D"},{search:/SS_LODINREFRACTIONALPHA/g,replace:null!==(d=null==f?void 0:f._defineLODRefractionAlpha)&&void 0!==d?d:"SS_LODINREFRACTIONALPHA"},{search:/SS_LINEARSPECULARREFRACTION/g,replace:null!==(c=null==f?void 0:f._defineLinearSpecularRefraction)&&void 0!==c?c:"SS_LINEARSPECULARREFRACTION"},{search:/SS_REFRACTIONMAP_OPPOSITEZ/g,replace:null!==(e=null==f?void 0:f._defineOppositeZ)&&void 0!==e?e:"SS_REFRACTIONMAP_OPPOSITEZ"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockDirectLighting",g),this.light?b.compilationString+=b._emitCodeFromInclude("lightFragment",g,{replaceStrings:[{search:/{X}/g,replace:this._lightId.toString()}]}):b.compilationString+=b._emitCodeFromInclude("lightFragment",g,{repeatKey:"maxSimultaneousLights"}),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalLitComponents",g),b.compilationString+="#endif ";i=this.ambientColor.isConnected?this.ambientColor.associatedVariableName:"vec3(0., 0., 0.)";d=ui.DEFAULT_AO_ON_ANALYTICAL_LIGHTS.toString();-1===d.indexOf(".")&&(d+="."),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalUnlitComponents",g,{replaceStrings:[{search:/vec3 finalEmissive[sS]*?finalEmissive*=vLightingIntensity.y;/g,replace:""},{search:/vAmbientColor/g,replace:i+" * ambientFromScene"},{search:/vAmbientInfos.w/g,replace:d}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockFinalColorComposition",g,{replaceStrings:[{search:/finalEmissive/g,replace:"vec3(0.)"}]}),b.compilationString+=b._emitCodeFromInclude("pbrBlockImageProcessing",g,{replaceStrings:[{search:/visibility/g,replace:"1."}]}),b.compilationString+=b._emitCodeFromInclude("pbrDebug",g,{replaceStrings:[{search:/vNormalW/g,replace:this._vNormalWName},{search:/vPositionW/g,replace:h},{search:/albedoTexture.rgb;/g,replace:"vec3(1.); gl_FragColor.rgb = toGammaSpace(gl_FragColor.rgb); "}]});for(c=0,f=this._outputs;c "+this.b.associatedVariableName+" ? "+d+" : "+e+"; ";break;case Zm.GreaterOrEqual:b.compilationString+=this._declareOutput(c,b)+" = "+this.a.associatedVariableName+" >= "+this.b.associatedVariableName+" ? "+d+" : "+e+"; ";break;case Zm.Xor:b.compilationString+=this._declareOutput(c,b)+" = (mod("+this.a.associatedVariableName+" + "+this.b.associatedVariableName+", 2.0) > 0.0) ? "+d+" : "+e+"; ";break;case Zm.Or:b.compilationString+=this._declareOutput(c,b)+" = (min("+this.a.associatedVariableName+" + "+this.b.associatedVariableName+", 1.0) > 0.0) ? "+d+" : "+e+"; ";break;case Zm.And:b.compilationString+=this._declareOutput(c,b)+" = ("+this.a.associatedVariableName+" * "+this.b.associatedVariableName+" > 0.0) ? "+d+" : "+e+"; "}return this},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.condition=this.condition,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.condition=b.condition},b.prototype._dumpPropertiesCode=function(){return a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".condition = BABYLON.ConditionalBlockConditions.")+Zm[this.condition]+"; "},b}(al);Object(i.b)("BABYLON.ConditionalBlock",an);var bn=function(a){function b(b){b=a.call(this,b,Z.Neutral)||this;return b.octaves=6,b.registerInput("seed",$.Vector2),b.registerInput("gain",$.Float,!0),b.registerInput("lacunarity",$.Float,!0),b.registerInput("timeX",$.Float,!0),b.registerInput("timeY",$.Float,!0),b.registerOutput("output",$.Vector3),b}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"CloudBlock"},Object.defineProperty(b.prototype,"seed",{get:function(){return this._inputs[0]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"gain",{get:function(){return this._inputs[1]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"lacunarity",{get:function(){return this._inputs[2]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"timeX",{get:function(){return this._inputs[3]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"timeY",{get:function(){return this._inputs[4]},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"output",{get:function(){return this._outputs[0]},enumerable:!1,configurable:!0}),b.prototype._buildBlock=function(b){if(a.prototype._buildBlock.call(this,b),this.seed.isConnected&&this._outputs[0].hasEndpoints){var c="fbm"+this.octaves;b._emitFunction("CloudBlockCode","float cloudRandom (in vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898,78.233)))* 43758.5453123); } // Based on Morgan McGuire @morgan3d // https://www.shadertoy.com/view/4dS3Wd float fbmNoise (in vec2 st) { vec2 i = floor(st); vec2 f = fract(st); // Four corners in 2D of a tile float a = cloudRandom(i); float b = cloudRandom(i + vec2(1.0, 0.0)); float c = cloudRandom(i + vec2(0.0, 1.0)); float d = cloudRandom(i + vec2(1.0, 1.0)); vec2 u = f * f * (3.0 - 2.0 * f); return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; }","// CloudBlockCode"),b._emitFunction("CloudBlockCodeFBM"+this.octaves,"float fbm (in vec2 st, in float gain, in float lacunarity) { // Initial values float value = 0.0; float amplitude = .5; float frequency = 0.; // Loop of octaves for (int i = 0; i < OCTAVES; i++) { value += amplitude * fbmNoise(st); st *= lacunarity; amplitude *= gain; } return value; }".replace("fbm",c).replace("OCTAVES",(0|this.octaves).toString()),"// CloudBlockCode FBM");var d=b._getFreeVariableName("st");return b.compilationString+="vec2 "+d+" = "+this.seed.associatedVariableName+"; ",this.timeX.isConnected&&(b.compilationString+=d+".x += 0.1 * "+this.timeX.associatedVariableName+"; "),this.timeY.isConnected&&(b.compilationString+=d+".y += 0.1 * "+this.timeY.associatedVariableName+"; "),b.compilationString+=this._declareOutput(this._outputs[0],b)+" = vec3(0.0) + "+c+"("+d+", "+(this.gain.isConnected?this.gain.associatedVariableName:"0.5")+", "+(this.lacunarity.isConnected?this.lacunarity.associatedVariableName:"2.0")+"); ",this}},b.prototype._dumpPropertiesCode=function(){return a.prototype._dumpPropertiesCode.call(this)+(this._codeVariableName+".octaves = ")+this.octaves+"; "},b.prototype.serialize=function(){var b=a.prototype.serialize.call(this);return b.octaves=this.octaves,b},b.prototype._deserialize=function(b,c,d){a.prototype._deserialize.call(this,b,c,d),this.octaves=b.octaves},Object(l.c)([gl("Octaves",el.Int)],b.prototype,"octaves",void 0),b}(al);Object(i.b)("BABYLON.CloudBlock",bn);var cn=function(){function a(){}return a.prototype.optimize=function(a,b){},a}(),dn=function(){function a(){this.mm=new Map}return a.prototype.get=function(a,b){a=this.mm.get(a);if(void 0!==a)return a.get(b)},a.prototype.set=function(a,b,c){var d=this.mm.get(a);void 0===d&&this.mm.set(a,d=new Map),d.set(b,c)},a}(),en=function(){function a(a,b,c){var d=this;this._baseMaterial=a,this._scene=null!=b?b:S.a.LastCreatedScene,this._options=c,this._subMeshToEffect=new Map,this._subMeshToDepthWrapper=new dn,this._meshes=new Map,this._onEffectCreatedObserver=this._baseMaterial.onEffectCreatedObservable.add(function(a){var b;b=null===(b=a.subMesh)||void 0===b?void 0:b.getMesh();b&&!d._meshes.has(b)&&d._meshes.set(b,b.onDisposeObservable.add(function(a){for(var b=d._subMeshToEffect.keys(),c=b.next();!0!==c.done;c=b.next()){var e=c.value;(null==e?void 0:e.getMesh())===a&&(d._subMeshToEffect["delete"](e),d._subMeshToDepthWrapper.mm["delete"](e))}})),d._subMeshToEffect.set(a.subMesh,a.effect),d._subMeshToDepthWrapper.mm["delete"](a.subMesh)})}return Object.defineProperty(a.prototype,"standalone",{get:function(){var a;return null!==(a=null===(a=this._options)||void 0===a?void 0:a.standalone)&&void 0!==a&&a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"baseMaterial",{get:function(){return this._baseMaterial},enumerable:!1,configurable:!0}),a.prototype.getEffect=function(a,b,c){a=null===(a=this._subMeshToDepthWrapper.mm.get(a))||void 0===a?void 0:a.get(b);if(!a)return null;b=a.drawWrapper[c];return b||(b=a.drawWrapper[c]=new Ec.a(this._scene.getEngine())).setEffect(a.mainDrawWrapper.effect,a.mainDrawWrapper.defines),b},a.prototype.isReadyForSubMesh=function(a,b,c,d,e){var f;return!(this.standalone&&!this._baseMaterial.isReadyForSubMesh(a.getMesh(),a,d))&&null!==(f=null===(d=this._makeEffect(a,b,c,e))||void 0===d?void 0:d.isReady())&&void 0!==f&&f},a.prototype.dispose=function(){this._baseMaterial.onEffectCreatedObservable.remove(this._onEffectCreatedObserver),this._onEffectCreatedObserver=null;for(var a=this._meshes.entries(),b=a.next();!0!==b.done;b=a.next()){var c=b.value,d=c[0];c=c[1];d.onDisposeObservable.remove(c)}},a.prototype._makeEffect=function(a,b,c,d){var e=this._scene.getEngine(),f=this._subMeshToEffect.get(a);if(!f)return null;var g=this._subMeshToDepthWrapper.get(a,c);if(!g){var h=new Ec.a(e);h.defines=a._materialDefines,(g={drawWrapper:{},mainDrawWrapper:h,depthDefines:"",token:Object(ve.a)()}).drawWrapper[d]=h,this._subMeshToDepthWrapper.set(a,c,g)}h=b.join(" ");if(g.mainDrawWrapper.effect&&h===g.depthDefines)return g.mainDrawWrapper.effect;g.depthDefines=h;a=f.rawVertexSourceCode;c=f.rawFragmentSourceCode;b=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapVertexNormalBias;var i=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapVertexMetric,j=this._options&&this._options.remappedVariables?"#include("+this._options.remappedVariables.join(",")+")":$f.a.IncludesShadersStore.shadowMapFragmentSoftTransparentShadow,k=$f.a.IncludesShadersStore.shadowMapFragment;a=(a=-1!==(a=(a=a.replace(/voids+?main/g,$f.a.IncludesShadersStore.shadowMapVertexExtraDeclaration+" void main")).replace(/#define SHADOWDEPTH_NORMALBIAS|#define CUSTOM_VERTEX_UPDATE_WORLDPOS/g,b)).indexOf("#define SHADOWDEPTH_METRIC")?a.replace(/#define SHADOWDEPTH_METRIC/g,i):a.replace(/}s*$/g,i+" }")).replace(/#define SHADER_NAME.*? |out vec4 glFragColor; /g,"");b=c.indexOf("#define SHADOWDEPTH_SOFTTRANSPARENTSHADOW")>=0||c.indexOf("#define CUSTOM_FRAGMENT_BEFORE_FOG")>=0;i=-1!==c.indexOf("#define SHADOWDEPTH_FRAGMENT");var l="";b?c=c.replace(/#define SHADOWDEPTH_SOFTTRANSPARENTSHADOW|#define CUSTOM_FRAGMENT_BEFORE_FOG/g,j):l=j+" ",c=c.replace(/voids+?main/g,$f.a.IncludesShadersStore.shadowMapFragmentExtraDeclaration+" void main"),i?c=c.replace(/#define SHADOWDEPTH_FRAGMENT/g,k):l+=k+" ",l&&(c=c.replace(/}s*$/g,l+"}")),c=c.replace(/#define SHADER_NAME.*? |out vec4 glFragColor; /g,"");b=f.getUniformNames().slice();for(j in b.push("biasAndScaleSM","depthValuesSM","lightDataSM","softTransparentShadowSM"),g.mainDrawWrapper.effect=e.createEffect({vertexSource:a,fragmentSource:c,vertexToken:g.token,fragmentToken:g.token},{attributes:f.getAttributesNames(),uniformsNames:b,uniformBuffersNames:f.getUniformBuffersNames(),samplers:f.getSamplers(),defines:h+" "+f.defines.replace("#define SHADOWS","").replace(/#define SHADOWd/g,""),indexParameters:f.getIndexParameters()},e),g.drawWrapper)j!==d&&(null===(i=g.drawWrapper[j])||void 0===i||i.setEffect(g.mainDrawWrapper.effect,g.mainDrawWrapper.defines));return g.mainDrawWrapper.effect},a}(),fn=c(114);function gn(a,b,c,d,e){var f=new a.DecoderBuffer;f.Init(b,b.byteLength);var g,h,i=new a.Decoder;try{b=i.GetEncodedGeometryType(f);switch(b){case a.TRIANGULAR_MESH:g=new a.Mesh,h=i.DecodeBufferToMesh(f,g);break;case a.POINT_CLOUD:g=new a.PointCloud,h=i.DecodeBufferToPointCloud(f,g);break;default:throw new Error("Invalid geometry type "+b)}if(!h.ok()||!g.ptr)throw new Error(h.error_msg());if(b===a.TRIANGULAR_MESH){b=3*g.num_faces();var j=4*b,k=a._malloc(j);try{i.GetTrianglesUInt32Array(g,j,k);j=new Uint32Array(b);j.set(new Uint32Array(a.HEAPF32.buffer,k,b)),d(j)}finally{a._free(k)}}b=function(b,c){var d=c.num_components(),f=g.num_points(),h=f*d,j=h*Float32Array.BYTES_PER_ELEMENT,k=a._malloc(j);try{i.GetAttributeDataArrayForAllPoints(g,c,a.DT_FLOAT32,j,k);c=new Float32Array(a.HEAPF32.buffer,k,h);if("color"===b&&3===d){for(var j=new Float32Array(4*f),f=0,l=0;fa.EPSILON?1:0;j|=l,k.push(l)}switch(j){case 0:(g.e.Dot(this.normal,b.plane.normal)>0?c:d).push(b);break;case 1:e.push(b);break;case 2:f.push(b);break;case 3:var n;l=[];j=[];for(h=0;h=3&&(n=new pn(l,b.shared)).plane&&e.push(n),j.length>=3&&(n=new pn(j,b.shared)).plane&&f.push(n)}},a.EPSILON=1e-5,a}(),pn=function(){function a(a,b){this.vertices=a,this.shared=b,this.plane=on.FromPoints(a[0].pos,a[1].pos,a[2].pos)}return a.prototype.clone=function(){return new a(this.vertices.map(function(a){return a.clone()}),this.shared)},a.prototype.flip=function(){this.vertices.reverse().map(function(a){a.flip()}),this.plane.flip()},a}(),qn=function(){function a(a){this.plane=null,this.front=null,this.back=null,this.polygons=new Array,a&&this.build(a)}return a.prototype.clone=function(){var b=new a;return b.plane=this.plane&&this.plane.clone(),b.front=this.front&&this.front.clone(),b.back=this.back&&this.back.clone(),b.polygons=this.polygons.map(function(a){return a.clone()}),b},a.prototype.invert=function(){for(var a=0;a0||i>0){J=-k,ba=-l;L=k,M=l;switch(e){case R.a.CENTER:J-=g/=2,L+=g;break;case R.a.LEFT:L+=g,G=-g/2;break;case R.a.RIGHT:J-=g,G=g/2}switch(f){case R.a.CENTER:ba-=i/=2,M+=i;break;case R.a.BOTTOM:M+=i,H=-i/2;break;case R.a.TOP:ba-=i,H=i/2}}var N=[],ca=[],da=[];da[0]=[0,0,1,0,1,1,0,1],da[1]=[0,0,1,0,1,1,0,1],b!==R.a.ROTATE_TILE&&b!==R.a.ROTATE_ROW||(da[1]=[1,1,0,1,0,0,1,0]),b!==R.a.FLIP_TILE&&b!==R.a.FLIP_ROW||(da[1]=[1,0,0,0,0,1,1,1]),b!==R.a.FLIP_N_ROTATE_TILE&&b!==R.a.FLIP_N_ROTATE_ROW||(da[1]=[0,1,1,1,1,0,0,0]);for(var ea=[],fa=[],ga=[],ha=0,ia=0;ia0||i>0){var P,ja,Q,ka;da=i>0&&(f===R.a.CENTER||f===R.a.TOP);f=i>0&&(f===R.a.CENTER||f===R.a.BOTTOM);var la=g>0&&(e===R.a.CENTER||e===R.a.RIGHT);e=g>0&&(e===R.a.CENTER||e===R.a.LEFT);var ma;if(da&&la&&(N.push(J+G,ba+H,0),N.push(-k+G,ba+H,0),N.push(-k+G,ba+i+H,0),N.push(J+G,ba+i+H,0),ga.push(ha,ha+1,ha+3,ha+1,ha+2,ha+3),ha+=4,ma=[P=1-g/c,ja=1-i/d,Q=1,ja,Q,ka=1,P,ka],b===R.a.ROTATE_ROW&&(ma=[1-P,1-ja,1-Q,1-ja,1-Q,1-ka,1-P,1-ka]),b===R.a.FLIP_ROW&&(ma=[1-P,ja,1-Q,ja,1-Q,ka,1-P,ka]),b===R.a.FLIP_N_ROTATE_ROW&&(ma=[P,1-ja,Q,1-ja,Q,1-ka,P,1-ka]),ea=ea.concat(ma),fa.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ca.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),da&&e&&(N.push(k+G,ba+H,0),N.push(L+G,ba+H,0),N.push(L+G,ba+i+H,0),N.push(k+G,ba+i+H,0),ga.push(ha,ha+1,ha+3,ha+1,ha+2,ha+3),ha+=4,ma=[P=0,ja=1-i/d,Q=g/c,ja,Q,ka=1,P,ka],(b===R.a.ROTATE_ROW||b===R.a.ROTATE_TILE&&h%2==0)&&(ma=[1-P,1-ja,1-Q,1-ja,1-Q,1-ka,1-P,1-ka]),(b===R.a.FLIP_ROW||b===R.a.FLIP_TILE&&h%2==0)&&(ma=[1-P,ja,1-Q,ja,1-Q,ka,1-P,ka]),(b===R.a.FLIP_N_ROTATE_ROW||b===R.a.FLIP_N_ROTATE_TILE&&h%2==0)&&(ma=[P,1-ja,Q,1-ja,Q,1-ka,P,1-ka]),ea=ea.concat(ma),fa.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ca.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),f&&la&&(N.push(J+G,l+H,0),N.push(-k+G,l+H,0),N.push(-k+G,M+H,0),N.push(J+G,M+H,0),ga.push(ha,ha+1,ha+3,ha+1,ha+2,ha+3),ha+=4,ma=[P=1-g/c,ja=0,Q=1,ja,Q,ka=i/d,P,ka],(b===R.a.ROTATE_ROW&&j%2==1||b===R.a.ROTATE_TILE&&j%1==0)&&(ma=[1-P,1-ja,1-Q,1-ja,1-Q,1-ka,1-P,1-ka]),(b===R.a.FLIP_ROW&&j%2==1||b===R.a.FLIP_TILE&&j%2==0)&&(ma=[1-P,ja,1-Q,ja,1-Q,ka,1-P,ka]),(b===R.a.FLIP_N_ROTATE_ROW&&j%2==1||b===R.a.FLIP_N_ROTATE_TILE&&j%2==0)&&(ma=[P,1-ja,Q,1-ja,Q,1-ka,P,1-ka]),ea=ea.concat(ma),fa.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ca.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),f&&e&&(N.push(k+G,l+H,0),N.push(L+G,l+H,0),N.push(L+G,M+H,0),N.push(k+G,M+H,0),ga.push(ha,ha+1,ha+3,ha+1,ha+2,ha+3),ha+=4,ma=[P=0,ja=0,Q=g/c,ja,Q,ka=i/d,P,ka],(b===R.a.ROTATE_ROW&&j%2==1||b===R.a.ROTATE_TILE&&(j+h)%2==1)&&(ma=[1-P,1-ja,1-Q,1-ja,1-Q,1-ka,1-P,1-ka]),(b===R.a.FLIP_ROW&&j%2==1||b===R.a.FLIP_TILE&&(j+h)%2==1)&&(ma=[1-P,ja,1-Q,ja,1-Q,ka,1-P,ka]),(b===R.a.FLIP_N_ROTATE_ROW&&j%2==1||b===R.a.FLIP_N_ROTATE_TILE&&(j+h)%2==1)&&(ma=[P,1-ja,Q,1-ja,Q,1-ka,P,1-ka]),ea=ea.concat(ma),fa.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),ca.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1)),da){ma=[];P=0,ja=1-i/d,Q=1,ka=1,ma[0]=[P,ja,Q,ja,Q,ka,P,ka],ma[1]=[P,ja,Q,ja,Q,ka,P,ka],b!==R.a.ROTATE_TILE&&b!==R.a.ROTATE_ROW||(ma[1]=[1-P,1-ja,1-Q,1-ja,1-Q,1-ka,1-P,1-ka]),b!==R.a.FLIP_TILE&&b!==R.a.FLIP_ROW||(ma[1]=[1-P,ja,1-Q,ja,1-Q,ka,1-P,ka]),b!==R.a.FLIP_N_ROTATE_TILE&&b!==R.a.FLIP_N_ROTATE_ROW||(ma[1]=[P,1-ja,Q,1-ja,Q,1-ka,P,1-ka]);for(O=0;Ob.x&&(b.x=c.x),c.yb.y&&(b.y=c.y)}),{min:a,max:b,width:b.x-a.x,height:b.y-a.y}},a}(),In=function(){function a(){}return a.Rectangle=function(a,b,c,d){return[new g.d(a,b),new g.d(c,b),new g.d(c,d),new g.d(a,d)]},a.Circle=function(a,b,c,d){void 0===b&&(b=0),void 0===c&&(c=0),void 0===d&&(d=32);for(var e=new Array,f=0,h=2*Math.PI/d,i=0;i0){j=f.length/3;this._points.elements.forEach(function(b){e.push(0,-1,0),f.push(b.x,-a,b.y),g.push(1-(b.x-h.min.x)/h.width,1-(b.y-h.min.y)/h.height)});var l=i.length;for(k=0;kj?Fj?F1?1:b.arc:1,f=void 0===b.closed||b.closed,h=b.shape,i=b.radius||1,j=b.tessellation||64,k=b.clip||0,l=b.updatable,t=R.a._GetDefaultSideOrientation(b.sideOrientation),u=b.cap||R.a.NO_CAP,v=2*Math.PI,w=new Array,x=b.invertUV||!1,y=0,z=0;v=v/j*e;e=new Array;for(y=0;y<=j-k;y++){e=[];for(u!=R.a.CAP_START&&u!=R.a.CAP_ALL||(e.push(new g.e(0,h[0].y,0)),e.push(new g.e(Math.cos(y*v)*h[0].x*i,h[0].y,Math.sin(y*v)*h[0].x*i))),z=0;z1)?1:b.arc||1;var x,y,z=function(a,b,c,d,e,f,h,i){for(var j,k,l,p,q=b.getTangents(),r=b.getNormals(),b=b.getDistances(),i=2*Math.PI/e*i,f=f||function(){return d},w=g.c.Matrix[0],x=h===R.a.NO_CAP||h===R.a.CAP_END?0:2,y=0;y3?0:j,b.arc);B=Object(wn.a)(a,{pathArray:y,closePath:!0,closeArray:!1,updatable:l,sideOrientation:w,invertUV:k,frontUVs:b.frontUVs,backUVs:b.backUVs},c);return B._creationDataStorage.pathArray=y,B._creationDataStorage.path3D=x,B._creationDataStorage.tessellation=h,B._creationDataStorage.cap=j,B._creationDataStorage.arc=b.arc,B._creationDataStorage.radius=f,B}R.a.CreateLathe=function(a,b,c,d,e,f,g){return On(a,{shape:b,radius:c,tessellation:d,sideOrientation:g,updatable:f},e)};var Rn={CreateTube:Qn};function Sn(a){var b,c=a.sideOrientation||yd.a.DEFAULTSIDE,d=a.radius||1,e=void 0===a.flat||a.flat,f=a.subdivisions||4,h=a.radiusX||d,i=a.radiusY||d,j=a.radiusZ||d,k=(1+Math.sqrt(5))/2;k=[-1,k,-0,1,k,0,-1,-k,0,1,-k,0,0,-1,-k,0,1,-k,0,-1,k,0,1,k,k,0,1,k,0,-1,-k,0,1,-k,0,-1];var l=[0,11,5,0,5,1,0,1,7,0,7,10,12,22,23,1,5,20,5,11,4,23,22,13,22,18,6,7,1,8,14,21,4,14,4,2,16,13,6,15,6,19,3,8,9,4,21,5,13,17,23,6,13,22,19,6,18,9,8,1],z=[0,1,2,3,4,5,6,7,8,9,10,11,0,2,3,3,3,4,7,8,9,9,10,11],C=[5,1,3,1,6,4,0,0,5,3,4,2,2,2,4,0,2,0,1,1,6,0,6,2,0,4,3,3,4,4,3,1,4,2,4,4,0,2,1,1,2,2,3,3,1,3,2,4],D=[0,0,0,0,1,0,0,1,1,0,0,0,1,1,0,0,1,1,1,0],E=new Array,F=new Array,aa=new Array,G=new Array,H=0,I=new Array(3),J=new Array(3);for(b=0;b<3;b++)I[b]=g.e.Zero(),J[b]=g.d.Zero();for(var K=0;K<20;K++){for(b=0;b<3;b++){var ba=l[3*K+b];I[b].copyFromFloats(k[3*z[ba]],k[3*z[ba]+1],k[3*z[ba]+2]),I[b].normalize().scaleInPlace(d),J[b].copyFromFloats(C[2*ba]*(138/1024)+60/1024+D[K]*(-40/1024),C[2*ba+1]*(239/1024)+26/1024+D[K]*(20/1024))}for(var ba=function(a,b,c,d){var k=g.e.Lerp(I[0],I[2],b/f),l=g.e.Lerp(I[1],I[2],b/f);k=f===b?I[2]:g.e.Lerp(k,l,a/(f-b));if(k.normalize(),e){l=g.e.Lerp(I[0],I[2],d/f);var n=g.e.Lerp(I[1],I[2],d/f);l=g.e.Lerp(l,n,c/(f-d))}else l=new g.e(k.x,k.y,k.z);l.x/=h,l.y/=i,l.z/=j,l.normalize();n=g.d.Lerp(J[0],J[2],b/f);c=g.d.Lerp(J[1],J[2],b/f);d=f===b?J[2]:g.d.Lerp(n,c,a/(f-b));F.push(k.x*h,k.y*i,k.z*j),aa.push(l.x,l.y,l.z),G.push(d.x,d.y),E.push(H),H++},L=0;L0)?1:0)+((i=g.e.Dot(a[f+1].position,b)-c>0)?1:0)+((j=g.e.Dot(a[f+2].position,b)-c>0)?1:0)){case 0:e.push(a[f]),e.push(a[f+1]),e.push(a[f+2]);break;case 1:if(h&&(l=a[f+1],p=a[f+2],q=d(a[f],l),r=d(a[f],p)),i){l=a[f],p=a[f+2],q=d(a[f+1],l),r=d(a[f+1],p),e.push(q),e.push(p.clone()),e.push(l.clone()),e.push(p.clone()),e.push(q.clone()),e.push(r);break}j&&(l=a[f],p=a[f+1],q=d(a[f+2],l),r=d(a[f+2],p)),l&&p&&q&&r&&(e.push(l.clone()),e.push(p.clone()),e.push(q),e.push(r),e.push(q.clone()),e.push(p.clone()));break;case 2:h||(p=d(l=a[f].clone(),a[f+1]),q=d(l,a[f+2]),e.push(l),e.push(p),e.push(q)),i||(p=d(l=a[f+1].clone(),a[f+2]),q=d(l,a[f]),e.push(l),e.push(p),e.push(q)),j||(p=d(l=a[f+2].clone(),a[f]),q=d(l,a[f+1]),e.push(l),e.push(p),e.push(q))}}return e},D=0;D2?I[g[k]]=[-J[z][0],J[z][1],g[k]]:I[g[k]]=[G[J[z][0]],J[z][1],g[k]])}function ba(a,b,c,d){l=b+"|"+d,(k=a+"|"+c)in g||l in g?k in g&&!(l in g)?g[l]=g[k]:l in g&&!(k in g)&&(g[k]=g[l]):(g[k]=f,g[l]=f,f++),J[c][0]>2?I[g[k]]=[-J[c][0],J[c][1],g[k]]:I[g[k]]=[G[J[c][0]],J[c][1],g[k]]}this.closestTo=I,this.vecToIdx=g},a.prototype.calcCoeffs=function(){var a=this.m,b=this.n,c=Math.sqrt(3)/3,d=a*a+b*b+a*b;this.coau=(a+b)/d,this.cobu=-b/d,this.coav=-c*(a-b)/d,this.cobv=c*(2*a+b)/d},a.prototype.createInnerFacets=function(){for(var a=this.m,b=this.n,c=0;c0&&d0){for(var f=H.a.HCF(a,b),e=a/f,d=b/f,g=1;g-1?a[c][1]>0&&b[a[c][0]].push([c,a[c][1]]):b[12].push([c,a[c][0]]);a=[];for(c=0;c<12;c++)a[c]=c;var d=12;for(c=0;c<12;c++){b[c].sort(function(a,b){return a[1]-b[1]});for(var e=0;e0;)e=b[g],this.face[e].indexOf(f)>-1?(a=(this.face[e].indexOf(f)+1)%3,f=this.face[e][a],c.push(f),d.push(e),b.splice(g,1),g=0):g++;return this.adjacentFaces.push(c),d},b.prototype.toGoldbergData=function(){var a=this,b=new $n("GeoDual","Goldberg",[],[]);b.name="GD dual";for(var c=this.vertex.length,d=new Array(c),e=0;ed){var f=e;e=d,d=f,q.a.Warn("n > m therefore m and n swapped")}f=new Zn;return f.build(d,e),Gh(a,{custom:ao.BuildGeodesicData(f),size:b.size,sizeX:b.sizeX,sizeY:b.sizeY,sizeZ:b.sizeZ,faceUV:b.faceUV,faceColors:b.faceColors,flat:b.flat,updatable:b.updatable,sideOrientation:b.sideOrientation,frontUVs:b.frontUVs,backUVs:b.backUVs},c)}var co,eo=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b.faceColors=[],b.faceCenters=[],b.faceZaxis=[],b.faceXaxis=[],b.faceYaxis=[],b}return Object(l.d)(b,a),b.prototype.setMetadata=function(){this.metadata={nbSharedFaces:this.nbSharedFaces,nbUnsharedFaces:this.nbUnsharedFaces,nbFacesAtPole:this.nbFacesAtPole,nbFaces:this.nbFaces,faceCenters:this.faceCenters,faceXaxis:this.faceXaxis,faceYaxis:this.faceYaxis,faceZaxis:this.faceZaxis,adjacentFaces:this.adjacentFaces}},b.prototype.relFace=function(a,b){return void 0===b?(a>this.nbUnsharedFaces-1&&(q.a.Warn("Maximum number of unshared faces used"),a=this.nbUnsharedFaces-1),this.nbUnsharedFaces+a):(a>11&&(q.a.Warn("Last pole used"),a=11),b>this.nbFacesAtPole-1&&(q.a.Warn("Maximum number of faces at a pole used"),b=this.nbFacesAtPole-1),12+a*this.nbFacesAtPole+b)},b.prototype.refreshFaceData=function(){this.nbSharedFaces=this.metadata.nbSharedFaces,this.nbUnsharedFaces=this.metadata.nbUnsharedFaces,this.nbFacesAtPole=this.metadata.nbFacesAtPole,this.adjacentFaces=this.metadata.adjacentFaces,this.nbFaces=this.metadata.nbFaces,this.faceCenters=this.metadata.faceCenters,this.faceXaxis=this.metadata.faceXaxis,this.faceYaxis=this.metadata.faceYaxis,this.faceZaxis=this.metadata.faceZaxis},b.prototype.changeFaceColors=function(a){for(var b=0;b1&&(k=1),i.push(k,l);for(n=0;n<6;n++)k=f.x+g*Math.cos(h+n*Math.PI/3),l=f.y+g*Math.sin(h+n*Math.PI/3),k<0&&(k=0),k>1&&(k=1),j.push(k,l);for(l=d;lc){var e=d;d=c,c=e,q.a.Warn("n > m therefore m and n swapped")}e=new Zn;e.build(c,d);c=ao.BuildGeodesicData(e);d=c.toGoldbergData();e=new eo(a);b.sideOrientation=R.a._GetDefaultSideOrientation(b.sideOrientation),e._originalBuilderSideOrientation=b.sideOrientation,(function(a,b){for(var c=a.size,d=a.sizeX||c||1,e=a.sizeY||c||1,c=a.sizeZ||c||1,a=0===a.sideOrientation?0:a.sideOrientation||yd.a.DEFAULTSIDE,f=new Array,h=new Array,i=new Array,j=new Array,k=1/0,l=-1/0,x=1/0,y=-1/0,z=0;zi||a.deleted||a.isDirty))for(var b=0;b<3;++b)if(a.error[b]>0,function(a){if(e){a=a+h.verticesStart;var b=g.e.FromArray(e,3*a);b=function(a){if(c)for(var b=0;b0&&this._reconstructedMesh.setVerticesData(W.b.NormalKind,g),h.length>0&&this._reconstructedMesh.setVerticesData(W.b.UVKind,h),i.length>0&&this._reconstructedMesh.setVerticesData(W.b.ColorKind,i);b=this._mesh.subMeshes[a];a>0&&(this._reconstructedMesh.subMeshes=[],v.forEach(function(a){ln.a.AddToMesh(a.materialIndex,a.verticesStart,a.verticesCount,a.indexStart,a.indexCount,a.getMesh())}),ln.a.AddToMesh(b.materialIndex,u,o,d,3*e.length,this._reconstructedMesh))},a.prototype.initDecimatedMesh=function(){this._reconstructedMesh=new R.a(this._mesh.name+"Decimated",this._mesh.getScene()),this._reconstructedMesh.material=this._mesh.material,this._reconstructedMesh.parent=this._mesh.parent,this._reconstructedMesh.isVisible=!1,this._reconstructedMesh.renderingGroupId=this._mesh.renderingGroupId},a.prototype.isFlipped=function(a,b,c,d,e){for(var f=0;f.999)return!0;j=g.e.Cross(j,i).normalize();if(d[f]=!1,g.e.Dot(j,h.normal)<.2)return!0}else d[f]=!0,e.push(h)}}return!1},a.prototype.updateTriangles=function(a,b,c,d){for(var d=d,e=0;e=this._thinInstanceDataStorage.instancesCount)return!1;var d=this._thinInstanceDataStorage.matrixData;return b.copyToArray(d,16*a),this._thinInstanceDataStorage.worldMatrices&&(this._thinInstanceDataStorage.worldMatrices[a]=b),c&&(this.thinInstanceBufferUpdated("matrix"),this.doNotSyncBoundingInfo||this.thinInstanceRefreshBoundingInfo(!1)),!0},R.a.prototype.thinInstanceSetAttributeAt=function(a,b,c,d){return void 0===d&&(d=!0),!(!this._userThinInstanceBuffersStorage||!this._userThinInstanceBuffersStorage.data[a]||b>=this._thinInstanceDataStorage.instancesCount)&&(this._thinInstanceUpdateBufferSize(a,0),this._userThinInstanceBuffersStorage.data[a].set(c,b*this._userThinInstanceBuffersStorage.strides[a]),d&&this.thinInstanceBufferUpdated(a),!0)},Object.defineProperty(R.a.prototype,"thinInstanceCount",{get:function(){return this._thinInstanceDataStorage.instancesCount},set:function(a){var b;a<=(null!==(b=null===(b=this._thinInstanceDataStorage.matrixData)||void 0===b?void 0:b.length)&&void 0!==b?b:0)/16&&(this._thinInstanceDataStorage.instancesCount=a)},enumerable:!0,configurable:!0}),R.a.prototype._thinInstanceCreateMatrixBuffer=function(a,b,c){void 0===c&&(c=!1);for(b=new W.a(this.getEngine(),b,!c,16,!1,!0),c=0;c<4;c++)this.setVerticesBuffer(b.createVertexBuffer(a+c,4*c,4));return b},R.a.prototype.thinInstanceSetBuffer=function(a,b,c,d){var e;void 0===c&&(c=0),void 0===d&&(d=!1),c=c||16,"matrix"===a?(null===(e=this._thinInstanceDataStorage.matrixBuffer)||void 0===e||e.dispose(),this._thinInstanceDataStorage.matrixBuffer=null,this._thinInstanceDataStorage.matrixBufferSize=b?b.length:32*c,this._thinInstanceDataStorage.matrixData=b,this._thinInstanceDataStorage.worldMatrices=null,null!==b?(this._thinInstanceDataStorage.instancesCount=b.length/c,this._thinInstanceDataStorage.matrixBuffer=this._thinInstanceCreateMatrixBuffer("world",b,d),this.doNotSyncBoundingInfo||this.thinInstanceRefreshBoundingInfo(!1)):(this._thinInstanceDataStorage.instancesCount=0,this.doNotSyncBoundingInfo||this.refreshBoundingInfo())):"previousMatrix"===a?(null===(e=this._thinInstanceDataStorage.previousMatrixBuffer)||void 0===e||e.dispose(),this._thinInstanceDataStorage.previousMatrixBuffer=null,this._thinInstanceDataStorage.previousMatrixData=b,null!==b&&(this._thinInstanceDataStorage.previousMatrixBuffer=this._thinInstanceCreateMatrixBuffer("previousWorld",b,d))):null===b?(null===(e=this._userThinInstanceBuffersStorage)||void 0===e?void 0:e.data[a])&&(this.removeVerticesData(a),delete this._userThinInstanceBuffersStorage.data[a],delete this._userThinInstanceBuffersStorage.strides[a],delete this._userThinInstanceBuffersStorage.sizes[a],delete this._userThinInstanceBuffersStorage.vertexBuffers[a]):(this._thinInstanceInitializeUserStorage(),this._userThinInstanceBuffersStorage.data[a]=b,this._userThinInstanceBuffersStorage.strides[a]=c,this._userThinInstanceBuffersStorage.sizes[a]=b.length,this._userThinInstanceBuffersStorage.vertexBuffers[a]=new W.b(this.getEngine(),b,a,!d,!1,c,!0),this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[a]))},R.a.prototype.thinInstanceBufferUpdated=function(a){var b;"matrix"===a?this._thinInstanceDataStorage.matrixBuffer&&this._thinInstanceDataStorage.matrixBuffer.updateDirectly(this._thinInstanceDataStorage.matrixData,0,this._thinInstanceDataStorage.instancesCount):(null===(b=this._userThinInstanceBuffersStorage)||void 0===b?void 0:b.vertexBuffers[a])&&this._userThinInstanceBuffersStorage.vertexBuffers[a].updateDirectly(this._userThinInstanceBuffersStorage.data[a],0)},R.a.prototype.thinInstancePartialBufferUpdate=function(a,b,c){var d;"matrix"===a?this._thinInstanceDataStorage.matrixBuffer&&this._thinInstanceDataStorage.matrixBuffer.updateDirectly(b,c):(null===(d=this._userThinInstanceBuffersStorage)||void 0===d?void 0:d.vertexBuffers[a])&&this._userThinInstanceBuffersStorage.vertexBuffers[a].updateDirectly(b,c)},R.a.prototype.thinInstanceGetWorldMatrices=function(){if(!this._thinInstanceDataStorage.matrixData||!this._thinInstanceDataStorage.matrixBuffer)return[];var a=this._thinInstanceDataStorage.matrixData;if(!this._thinInstanceDataStorage.worldMatrices){this._thinInstanceDataStorage.worldMatrices=new Array;for(var b=0;b-1&&(this.agentDestinationArmed[a]=!0,this.agentDestination[a].set(b.x,b.y,b.z))},a.prototype.agentTeleport=function(a,b){this.recastCrowd.agentTeleport(a,new this.bjsRECASTPlugin.bjsRECAST.Vec3(b.x,b.y,b.z))},a.prototype.updateAgentParameters=function(a,b){var c=this.recastCrowd.getAgentParameters(a);void 0!==b.radius&&(c.radius=b.radius),void 0!==b.height&&(c.height=b.height),void 0!==b.maxAcceleration&&(c.maxAcceleration=b.maxAcceleration),void 0!==b.maxSpeed&&(c.maxSpeed=b.maxSpeed),void 0!==b.collisionQueryRange&&(c.collisionQueryRange=b.collisionQueryRange),void 0!==b.pathOptimizationRange&&(c.pathOptimizationRange=b.pathOptimizationRange),void 0!==b.separationWeight&&(c.separationWeight=b.separationWeight),this.recastCrowd.setAgentParameters(a,c)},a.prototype.removeAgent=function(a){this.recastCrowd.removeAgent(a);a=this.agents.indexOf(a);a>-1&&(this.agents.splice(a,1),this.transforms.splice(a,1),this.reachRadii.splice(a,1),this.agentDestinationArmed.splice(a,1),this.agentDestination.splice(a,1))},a.prototype.getAgents=function(){return this.agents},a.prototype.update=function(a){this.bjsRECASTPlugin.navMesh.update();var b=this.bjsRECASTPlugin.getTimeStep(),c=this.bjsRECASTPlugin.getMaximumSubStepCount();if(b<=ib.a)this.recastCrowd.update(a);else{b=Math.floor(a/b);c&&b>c&&(b=c),b<1&&(b=1);for(c=a/b,a=0;ag&&c.y=400&&f?f(j):c()},!1),j.addEventListener("error",function(){q.a.Error("error on XHR request."),c()},!1),j.send()}else q.a.Error("Error: IndexedDB not supported by your browser or Babylon.js database is not open."),c()},a._ValidateXHRData=function(a,b){void 0===b&&(b=7);try{if(1&b){if(a.responseText&&a.responseText.length>0)return!0;if(1===b)return!1}if(2&b){var c=sk(a.response);if(c.width&&c.height&&c.width>0&&c.height>0)return!0;if(2===b)return!1}if(4&b){c=new Uint8Array(a.response,0,3);return 68===c[0]&&68===c[1]&&83===c[2]}}catch(a){}return!1},a.IsUASupportingBlobStorage=!0,a.IDBStorageEnabled=!1,a._ParseURL=function(a){document.createElement("a").href=a;var b=a.substring(0,a.lastIndexOf("#"));b=a.substring(b.lastIndexOf("/")+1,a.length);return a.substring(0,a.indexOf(b,0))},a._ReturnFullUrlLocation=function(b){return-1===b.indexOf("http:/")&&-1===b.indexOf("https:/")&&"undefined"!=typeof window?a._ParseURL(window.location.href)+b:b},a}(),to=function(){function a(a){this._isUbo(a)?(this.setMatrix3x3=a.updateMatrix3x3.bind(a),this.setMatrix2x2=a.updateMatrix2x2.bind(a),this.setFloat=a.updateFloat.bind(a),this.setFloat2=a.updateFloat2.bind(a),this.setFloat3=a.updateFloat3.bind(a),this.setFloat4=a.updateFloat4.bind(a),this.setFloatArray=a.updateFloatArray.bind(a),this.setArray=a.updateArray.bind(a),this.setIntArray=a.updateIntArray.bind(a),this.setMatrix=a.updateMatrix.bind(a),this.setMatrices=a.updateMatrices.bind(a),this.setVector3=a.updateVector3.bind(a),this.setVector4=a.updateVector4.bind(a),this.setColor3=a.updateColor3.bind(a),this.setColor4=a.updateColor4.bind(a),this.setDirectColor4=a.updateDirectColor4.bind(a),this.setInt=a.updateInt.bind(a),this.setInt2=a.updateInt2.bind(a),this.setInt3=a.updateInt3.bind(a),this.setInt4=a.updateInt4.bind(a)):(this.setMatrix3x3=a.setMatrix3x3.bind(a),this.setMatrix2x2=a.setMatrix2x2.bind(a),this.setFloat=a.setFloat.bind(a),this.setFloat2=a.setFloat2.bind(a),this.setFloat3=a.setFloat3.bind(a),this.setFloat4=a.setFloat4.bind(a),this.setFloatArray=a.setFloatArray.bind(a),this.setArray=a.setArray.bind(a),this.setIntArray=a.setIntArray.bind(a),this.setMatrix=a.setMatrix.bind(a),this.setMatrices=a.setMatrices.bind(a),this.setVector3=a.setVector3.bind(a),this.setVector4=a.setVector4.bind(a),this.setColor3=a.setColor3.bind(a),this.setColor4=a.setColor4.bind(a),this.setDirectColor4=a.setDirectColor4.bind(a),this.setInt=a.setInt.bind(a),this.setInt2=a.setInt2.bind(a),this.setInt3=a.setInt3.bind(a),this.setInt4=a.setInt4.bind(a))}return a.prototype._isUbo=function(a){return void 0!==a.addUniform},a}();X.a.ShadersStore.gpuUpdateParticlesPixelShader="#version 300 es void main() { discard; } ";wi="#version 300 es #define PI 3.14159 uniform float currentCount; uniform float timeDelta; uniform float stopFactor; #ifndef LOCAL uniform mat4 emitterWM; #endif uniform vec2 lifeTime; uniform vec2 emitPower; uniform vec2 sizeRange; uniform vec4 scaleRange; #ifndef COLORGRADIENTS uniform vec4 color1; uniform vec4 color2; #endif uniform vec3 gravity; uniform sampler2D randomSampler; uniform sampler2D randomSampler2; uniform vec4 angleRange; #ifdef BOXEMITTER uniform vec3 direction1; uniform vec3 direction2; uniform vec3 minEmitBox; uniform vec3 maxEmitBox; #endif #ifdef POINTEMITTER uniform vec3 direction1; uniform vec3 direction2; #endif #ifdef HEMISPHERICEMITTER uniform float radius; uniform float radiusRange; uniform float directionRandomizer; #endif #ifdef SPHEREEMITTER uniform float radius; uniform float radiusRange; #ifdef DIRECTEDSPHEREEMITTER uniform vec3 direction1; uniform vec3 direction2; #else uniform float directionRandomizer; #endif #endif #ifdef CYLINDEREMITTER uniform float radius; uniform float height; uniform float radiusRange; #ifdef DIRECTEDCYLINDEREMITTER uniform vec3 direction1; uniform vec3 direction2; #else uniform float directionRandomizer; #endif #endif #ifdef CONEEMITTER uniform vec2 radius; uniform float coneAngle; uniform vec2 height; uniform float directionRandomizer; #endif in vec3 position; #ifdef CUSTOMEMITTER in vec3 initialPosition; #endif in float age; in float life; in vec4 seed; in vec3 size; #ifndef COLORGRADIENTS in vec4 color; #endif in vec3 direction; #ifndef BILLBOARD in vec3 initialDirection; #endif #ifdef ANGULARSPEEDGRADIENTS in float angle; #else in vec2 angle; #endif #ifdef ANIMATESHEET in float cellIndex; #ifdef ANIMATESHEETRANDOMSTART in float cellStartOffset; #endif #endif #ifdef NOISE in vec3 noiseCoordinates1; in vec3 noiseCoordinates2; #endif out vec3 outPosition; #ifdef CUSTOMEMITTER out vec3 outInitialPosition; #endif out float outAge; out float outLife; out vec4 outSeed; out vec3 outSize; #ifndef COLORGRADIENTS out vec4 outColor; #endif out vec3 outDirection; #ifndef BILLBOARD out vec3 outInitialDirection; #endif #ifdef ANGULARSPEEDGRADIENTS out float outAngle; #else out vec2 outAngle; #endif #ifdef ANIMATESHEET out float outCellIndex; #ifdef ANIMATESHEETRANDOMSTART out float outCellStartOffset; #endif #endif #ifdef NOISE out vec3 outNoiseCoordinates1; out vec3 outNoiseCoordinates2; #endif #ifdef SIZEGRADIENTS uniform sampler2D sizeGradientSampler; #endif #ifdef ANGULARSPEEDGRADIENTS uniform sampler2D angularSpeedGradientSampler; #endif #ifdef VELOCITYGRADIENTS uniform sampler2D velocityGradientSampler; #endif #ifdef LIMITVELOCITYGRADIENTS uniform sampler2D limitVelocityGradientSampler; uniform float limitVelocityDamping; #endif #ifdef DRAGGRADIENTS uniform sampler2D dragGradientSampler; #endif #ifdef NOISE uniform vec3 noiseStrength; uniform sampler2D noiseSampler; #endif #ifdef ANIMATESHEET uniform vec4 cellInfos; #endif vec3 getRandomVec3(float offset) { return texture(randomSampler2,vec2(float(gl_VertexID)*offset/currentCount,0)).rgb; } vec4 getRandomVec4(float offset) { return texture(randomSampler,vec2(float(gl_VertexID)*offset/currentCount,0)); } void main() { float newAge=age+timeDelta; if (newAge>=life && stopFactor != 0.) { vec3 newPosition; vec3 newDirection; vec4 randoms=getRandomVec4(seed.x); outLife=lifeTime.x+(lifeTime.y-lifeTime.x)*randoms.r; outAge=newAge-life; outSeed=seed; #ifdef SIZEGRADIENTS outSize.x=texture(sizeGradientSampler,vec2(0,0)).r; #else outSize.x=sizeRange.x+(sizeRange.y-sizeRange.x)*randoms.g; #endif outSize.y=scaleRange.x+(scaleRange.y-scaleRange.x)*randoms.b; outSize.z=scaleRange.z+(scaleRange.w-scaleRange.z)*randoms.a; #ifndef COLORGRADIENTS outColor=color1+(color2-color1)*randoms.b; #endif #ifndef ANGULARSPEEDGRADIENTS outAngle.y=angleRange.x+(angleRange.y-angleRange.x)*randoms.a; outAngle.x=angleRange.z+(angleRange.w-angleRange.z)*randoms.r; #else outAngle=angleRange.z+(angleRange.w-angleRange.z)*randoms.r; #endif #ifdef POINTEMITTER vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); newPosition=vec3(0,0,0); newDirection=direction1+(direction2-direction1)*randoms3; #elif defined(BOXEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); newPosition=minEmitBox+(maxEmitBox-minEmitBox)*randoms2; newDirection=direction1+(direction2-direction1)*randoms3; #elif defined(HEMISPHERICEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float phi=2.0*PI*randoms2.x; float theta=acos(2.0*randoms2.y-1.0); float randX=cos(phi)*sin(theta); float randY=cos(theta); float randZ=sin(phi)*sin(theta); newPosition=(radius-(radius*radiusRange*randoms2.z))*vec3(randX,abs(randY),randZ); newDirection=newPosition+directionRandomizer*randoms3; #elif defined(SPHEREEMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float phi=2.0*PI*randoms2.x; float theta=acos(2.0*randoms2.y-1.0); float randX=cos(phi)*sin(theta); float randY=cos(theta); float randZ=sin(phi)*sin(theta); newPosition=(radius-(radius*radiusRange*randoms2.z))*vec3(randX,randY,randZ); #ifdef DIRECTEDSPHEREEMITTER newDirection=normalize(direction1+(direction2-direction1)*randoms3); #else newDirection=normalize(newPosition+directionRandomizer*randoms3); #endif #elif defined(CYLINDEREMITTER) vec3 randoms2=getRandomVec3(seed.y); vec3 randoms3=getRandomVec3(seed.z); float yPos=(randoms2.x-0.5)*height; float angle=randoms2.y*PI*2.; float inverseRadiusRangeSquared=((1.-radiusRange)*(1.-radiusRange)); float positionRadius=radius*sqrt(inverseRadiusRangeSquared+(randoms2.z*(1.-inverseRadiusRangeSquared))); float xPos=positionRadius*cos(angle); float zPos=positionRadius*sin(angle); newPosition=vec3(xPos,yPos,zPos); #ifdef DIRECTEDCYLINDEREMITTER newDirection=direction1+(direction2-direction1)*randoms3; #else angle=angle+((randoms3.x-0.5)*PI)*directionRandomizer; newDirection=vec3(cos(angle),(randoms3.y-0.5)*directionRandomizer,sin(angle)); newDirection=normalize(newDirection); #endif #elif defined(CONEEMITTER) vec3 randoms2=getRandomVec3(seed.y); float s=2.0*PI*randoms2.x; #ifdef CONEEMITTERSPAWNPOINT float h=0.0001; #else float h=randoms2.y*height.y; h=1.-h*h; #endif float lRadius=radius.x-radius.x*randoms2.z*radius.y; lRadius=lRadius*h; float randX=lRadius*sin(s); float randZ=lRadius*cos(s); float randY=h*height.x; newPosition=vec3(randX,randY,randZ); if (abs(cos(coneAngle)) == 1.0) { newDirection=vec3(0.,1.0,0.); } else { vec3 randoms3=getRandomVec3(seed.z); newDirection=normalize(newPosition+directionRandomizer*randoms3); } #elif defined(CUSTOMEMITTER) newPosition=initialPosition; outInitialPosition=initialPosition; #else newPosition=vec3(0.,0.,0.); newDirection=2.0*(getRandomVec3(seed.w)-vec3(0.5,0.5,0.5)); #endif float power=emitPower.x+(emitPower.y-emitPower.x)*randoms.a; #ifdef LOCAL outPosition=newPosition; #else outPosition=(emitterWM*vec4(newPosition,1.)).xyz; #endif #ifdef CUSTOMEMITTER outDirection=direction; #ifndef BILLBOARD outInitialDirection=direction; #endif #else #ifdef LOCAL vec3 initial=newDirection; #else vec3 initial=(emitterWM*vec4(newDirection,0.)).xyz; #endif outDirection=initial*power; #ifndef BILLBOARD outInitialDirection=initial; #endif #endif #ifdef ANIMATESHEET outCellIndex=cellInfos.x; #ifdef ANIMATESHEETRANDOMSTART outCellStartOffset=randoms.a*outLife; #endif #endif #ifdef NOISE outNoiseCoordinates1=noiseCoordinates1; outNoiseCoordinates2=noiseCoordinates2; #endif } else { float directionScale=timeDelta; outAge=newAge; float ageGradient=newAge/life; #ifdef VELOCITYGRADIENTS directionScale*=texture(velocityGradientSampler,vec2(ageGradient,0)).r; #endif #ifdef DRAGGRADIENTS directionScale*=1.0-texture(dragGradientSampler,vec2(ageGradient,0)).r; #endif #if defined(CUSTOMEMITTER) outPosition=position+(direction-position)*ageGradient; outInitialPosition=initialPosition; #else outPosition=position+direction*directionScale; #endif outLife=life; outSeed=seed; #ifndef COLORGRADIENTS outColor=color; #endif #ifdef SIZEGRADIENTS outSize.x=texture(sizeGradientSampler,vec2(ageGradient,0)).r; outSize.yz=size.yz; #else outSize=size; #endif #ifndef BILLBOARD outInitialDirection=initialDirection; #endif #ifdef CUSTOMEMITTER outDirection=direction; #else vec3 updatedDirection=direction+gravity*timeDelta; #ifdef LIMITVELOCITYGRADIENTS float limitVelocity=texture(limitVelocityGradientSampler,vec2(ageGradient,0)).r; float currentVelocity=length(updatedDirection); if (currentVelocity>limitVelocity) { updatedDirection=updatedDirection*limitVelocityDamping; } #endif outDirection=updatedDirection; #ifdef NOISE float fetchedR=texture(noiseSampler,vec2(noiseCoordinates1.x,noiseCoordinates1.y)*vec2(0.5)+vec2(0.5)).r; float fetchedG=texture(noiseSampler,vec2(noiseCoordinates1.z,noiseCoordinates2.x)*vec2(0.5)+vec2(0.5)).r; float fetchedB=texture(noiseSampler,vec2(noiseCoordinates2.y,noiseCoordinates2.z)*vec2(0.5)+vec2(0.5)).r; vec3 force=vec3(2.*fetchedR-1.,2.*fetchedG-1.,2.*fetchedB-1.)*noiseStrength; outDirection=outDirection+force*timeDelta; outNoiseCoordinates1=noiseCoordinates1; outNoiseCoordinates2=noiseCoordinates2; #endif #endif #ifdef ANGULARSPEEDGRADIENTS float angularSpeed=texture(angularSpeedGradientSampler,vec2(ageGradient,0)).r; outAngle=angle+angularSpeed*timeDelta; #else outAngle=vec2(angle.x+angle.y*timeDelta,angle.y); #endif #ifdef ANIMATESHEET float offsetAge=outAge; float dist=cellInfos.y-cellInfos.x; #ifdef ANIMATESHEETRANDOMSTART outCellStartOffset=cellStartOffset; offsetAge+=cellStartOffset; #else float cellStartOffset=0.; #endif float ratio=0.; if (cellInfos.w == 1.0) { ratio=clamp(mod(cellStartOffset+cellInfos.z*offsetAge,life)/life,0.,1.0); } else { ratio=clamp(cellStartOffset+cellInfos.z*offsetAge/life,0.,1.0); } outCellIndex=float(int(cellInfos.x+ratio*dist)); #endif } }";X.a.ShadersStore.gpuUpdateParticlesVertexShader=wi;var uo=function(){function a(a,b){this._renderVAO=[],this._updateVAO=[],this.alignDataInBuffer=!1,this._parent=a,this._engine=b,this._updateEffectOptions={attributes:["position","initialPosition","age","life","seed","size","color","direction","initialDirection","angle","cellIndex","cellStartOffset","noiseCoordinates1","noiseCoordinates2"],uniformsNames:["currentCount","timeDelta","emitterWM","lifeTime","color1","color2","sizeRange","scaleRange","gravity","emitPower","direction1","direction2","minEmitBox","maxEmitBox","radius","directionRandomizer","height","coneAngle","stopFactor","angleRange","radiusRange","cellInfos","noiseStrength","limitVelocityDamping"],uniformBuffersNames:[],samplers:["randomSampler","randomSampler2","sizeGradientSampler","angularSpeedGradientSampler","velocityGradientSampler","limitVelocityGradientSampler","noiseSampler","dragGradientSampler"],defines:"",fallbacks:null,onCompiled:null,onError:null,indexParameters:null,maxSimultaneousLights:0,transformFeedbackVaryings:[]}}return a.prototype.isUpdateBufferCreated=function(){return!!this._updateEffect},a.prototype.isUpdateBufferReady=function(){var a;return null!==(a=null===(a=this._updateEffect)||void 0===a?void 0:a.isReady())&&void 0!==a&&a},a.prototype.createUpdateBuffer=function(a){return this._updateEffectOptions.transformFeedbackVaryings=["outPosition"],this._updateEffectOptions.transformFeedbackVaryings.push("outAge"),this._updateEffectOptions.transformFeedbackVaryings.push("outSize"),this._updateEffectOptions.transformFeedbackVaryings.push("outLife"),this._updateEffectOptions.transformFeedbackVaryings.push("outSeed"),this._updateEffectOptions.transformFeedbackVaryings.push("outDirection"),this._parent.particleEmitterType instanceof Cl&&this._updateEffectOptions.transformFeedbackVaryings.push("outInitialPosition"),this._parent._colorGradientsTexture||this._updateEffectOptions.transformFeedbackVaryings.push("outColor"),this._parent._isBillboardBased||this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection"),this._parent.noiseTexture&&(this._updateEffectOptions.transformFeedbackVaryings.push("outNoiseCoordinates1"),this._updateEffectOptions.transformFeedbackVaryings.push("outNoiseCoordinates2")),this._updateEffectOptions.transformFeedbackVaryings.push("outAngle"),this._parent.isAnimationSheetEnabled&&(this._updateEffectOptions.transformFeedbackVaryings.push("outCellIndex"),this._parent.spriteRandomStartCell&&this._updateEffectOptions.transformFeedbackVaryings.push("outCellStartOffset")),this._updateEffectOptions.defines=a,this._updateEffect=new $f.a("gpuUpdateParticles",this._updateEffectOptions,this._engine),new to(this._updateEffect)},a.prototype.createVertexBuffers=function(a,b){this._updateVAO.push(this._createUpdateVAO(a)),this._renderVAO.push(this._engine.recordVertexArrayObject(b,null,this._parent._getWrapper(this._parent.blendMode).effect)),this._engine.bindArrayBuffer(null)},a.prototype.createParticleBuffer=function(a){return a},a.prototype.bindDrawBuffers=function(a,b){this._engine.bindVertexArrayObject(this._renderVAO[a],null)},a.prototype.preUpdateParticleBuffer=function(){var a=this._engine;if(this._engine.enableEffect(this._updateEffect),!a.setState)throw new Error("GPU particles cannot work without a full Engine. ThinEngine is not supported")},a.prototype.updateParticleBuffer=function(a,b,c){this._updateEffect.setTexture("randomSampler",this._parent._randomTexture),this._updateEffect.setTexture("randomSampler2",this._parent._randomTexture2),this._parent._sizeGradientsTexture&&this._updateEffect.setTexture("sizeGradientSampler",this._parent._sizeGradientsTexture),this._parent._angularSpeedGradientsTexture&&this._updateEffect.setTexture("angularSpeedGradientSampler",this._parent._angularSpeedGradientsTexture),this._parent._velocityGradientsTexture&&this._updateEffect.setTexture("velocityGradientSampler",this._parent._velocityGradientsTexture),this._parent._limitVelocityGradientsTexture&&this._updateEffect.setTexture("limitVelocityGradientSampler",this._parent._limitVelocityGradientsTexture),this._parent._dragGradientsTexture&&this._updateEffect.setTexture("dragGradientSampler",this._parent._dragGradientsTexture),this._parent.noiseTexture&&this._updateEffect.setTexture("noiseSampler",this._parent.noiseTexture),this._engine.bindVertexArrayObject(this._updateVAO[a],null);a=this._engine;a.bindTransformFeedbackBuffer(b.getBuffer()),a.setRasterizerState(!1),a.beginTransformFeedback(!0),a.drawArraysType(r.a.MATERIAL_PointListDrawMode,0,c),a.endTransformFeedback(),a.setRasterizerState(!0),a.bindTransformFeedbackBuffer(null)},a.prototype.releaseBuffers=function(){},a.prototype.releaseVertexBuffers=function(){for(var a=0;aa)c(b[0],b[0],1);else{for(var d=0;d=e.gradient&&a<=f.gradient)return void c(e,f,(a-e.gradient)/(f.gradient-e.gradient))}f=b.length-1;c(b[f],b[f],1)}},a}(),Bo=function(){function a(b){this.particleSystem=b,this.position=g.e.Zero(),this.direction=g.e.Zero(),this.color=new h.b(0,0,0,0),this.colorStep=new h.b(0,0,0,0),this.lifeTime=1,this.age=0,this.size=0,this.scale=new g.d(1,1),this.angle=0,this.angularSpeed=0,this.cellIndex=0,this._attachedSubEmitters=null,this._currentColor1=new h.b(0,0,0,0),this._currentColor2=new h.b(0,0,0,0),this._currentSize1=0,this._currentSize2=0,this._currentAngularSpeed1=0,this._currentAngularSpeed2=0,this._currentVelocity1=0,this._currentVelocity2=0,this._currentLimitVelocity1=0,this._currentLimitVelocity2=0,this._currentDrag1=0,this._currentDrag2=0,this.id=a._Count++,this.particleSystem.isAnimationSheetEnabled&&this.updateCellInfoFromSystem()}return a.prototype.updateCellInfoFromSystem=function(){this.cellIndex=this.particleSystem.startSpriteCellID},a.prototype.updateCellIndex=function(){var a=this.age,b=this.particleSystem.spriteCellChangeSpeed;this.particleSystem.spriteRandomStartCell&&(void 0===this._randomCellOffset&&(this._randomCellOffset=Math.random()*this.lifeTime),0===b?(b=1,a=this._randomCellOffset):a+=this._randomCellOffset);var c=this._initialEndSpriteCellID-this._initialStartSpriteCellID;a=this._initialSpriteCellLoop?H.a.Clamp(a*b%this.lifeTime/this.lifeTime):H.a.Clamp(a*b/this.lifeTime),this.cellIndex=this._initialStartSpriteCellID+a*c|0},a.prototype._inheritParticleInfoToSubEmitter=function(a){if(a.particleSystem.emitter.position){var b=a.particleSystem.emitter;if(b.position.copyFrom(this.position),a.inheritDirection){var c=g.c.Vector3[0];this.direction.normalizeToRef(c),b.setDirection(c,0,Math.PI/2)}}else a.particleSystem.emitter.copyFrom(this.position);this.direction.scaleToRef(a.inheritedVelocityAmount/2,g.c.Vector3[0]),a.particleSystem._inheritedVelocityOffset.copyFrom(g.c.Vector3[0])},a.prototype._inheritParticleInfoToSubEmitters=function(){var a=this;this._attachedSubEmitters&&this._attachedSubEmitters.length>0&&this._attachedSubEmitters.forEach(function(b){a._inheritParticleInfoToSubEmitter(b)})},a.prototype._reset=function(){this.age=0,this.id=a._Count++,this._currentColorGradient=null,this._currentSizeGradient=null,this._currentAngularSpeedGradient=null,this._currentVelocityGradient=null,this._currentLimitVelocityGradient=null,this._currentDragGradient=null,this.cellIndex=this.particleSystem.startSpriteCellID,this._randomCellOffset=void 0},a.prototype.copyTo=function(a){a.position.copyFrom(this.position),this._initialDirection?a._initialDirection?a._initialDirection.copyFrom(this._initialDirection):a._initialDirection=this._initialDirection.clone():a._initialDirection=null,a.direction.copyFrom(this.direction),this._localPosition&&(a._localPosition?a._localPosition.copyFrom(this._localPosition):a._localPosition=this._localPosition.clone()),a.color.copyFrom(this.color),a.colorStep.copyFrom(this.colorStep),a.lifeTime=this.lifeTime,a.age=this.age,a._randomCellOffset=this._randomCellOffset,a.size=this.size,a.scale.copyFrom(this.scale),a.angle=this.angle,a.angularSpeed=this.angularSpeed,a.particleSystem=this.particleSystem,a.cellIndex=this.cellIndex,a.id=this.id,a._attachedSubEmitters=this._attachedSubEmitters,this._currentColorGradient&&(a._currentColorGradient=this._currentColorGradient,a._currentColor1.copyFrom(this._currentColor1),a._currentColor2.copyFrom(this._currentColor2)),this._currentSizeGradient&&(a._currentSizeGradient=this._currentSizeGradient,a._currentSize1=this._currentSize1,a._currentSize2=this._currentSize2),this._currentAngularSpeedGradient&&(a._currentAngularSpeedGradient=this._currentAngularSpeedGradient,a._currentAngularSpeed1=this._currentAngularSpeed1,a._currentAngularSpeed2=this._currentAngularSpeed2),this._currentVelocityGradient&&(a._currentVelocityGradient=this._currentVelocityGradient,a._currentVelocity1=this._currentVelocity1,a._currentVelocity2=this._currentVelocity2),this._currentLimitVelocityGradient&&(a._currentLimitVelocityGradient=this._currentLimitVelocityGradient,a._currentLimitVelocity1=this._currentLimitVelocity1,a._currentLimitVelocity2=this._currentLimitVelocity2),this._currentDragGradient&&(a._currentDragGradient=this._currentDragGradient,a._currentDrag1=this._currentDrag1,a._currentDrag2=this._currentDrag2),this.particleSystem.isAnimationSheetEnabled&&(a._initialStartSpriteCellID=this._initialStartSpriteCellID,a._initialEndSpriteCellID=this._initialEndSpriteCellID,a._initialSpriteCellLoop=this._initialSpriteCellLoop),this.particleSystem.useRampGradients&&(a.remapData&&this.remapData?a.remapData.copyFrom(this.remapData):a.remapData=new g.f(0,0,0,0)),this._randomNoiseCoordinates1&&(a._randomNoiseCoordinates1?(a._randomNoiseCoordinates1.copyFrom(this._randomNoiseCoordinates1),a._randomNoiseCoordinates2.copyFrom(this._randomNoiseCoordinates2)):(a._randomNoiseCoordinates1=this._randomNoiseCoordinates1.clone(),a._randomNoiseCoordinates2=this._randomNoiseCoordinates2.clone()))},a._Count=0,a}();!function(a){a[a.ATTACHED=0]="ATTACHED",a[a.END=1]="END"}(wo||(wo={}));var Co=function(){function a(a){if(this.particleSystem=a,this.type=wo.END,this.inheritDirection=!1,this.inheritedVelocityAmount=0,!a.emitter||!a.emitter.dispose){var b=Object(i.a)("BABYLON.AbstractMesh");a.emitter=new b("SubemitterSystemEmitter",a.getScene())}}return a.prototype.clone=function(){var b=this.particleSystem.emitter;b?b instanceof g.e?b=b.clone():-1!==b.getClassName().indexOf("Mesh")&&((b=new(Object(i.a)("BABYLON.Mesh"))("",b.getScene())).isVisible=!1):b=new g.e;b=new a(this.particleSystem.clone(this.particleSystem.name,b));return b.particleSystem.name+="Clone",b.type=this.type,b.inheritDirection=this.inheritDirection,b.inheritedVelocityAmount=this.inheritedVelocityAmount,b.particleSystem._disposeEmitterOnDispose=!0,b.particleSystem.disposeOnStop=!0,b},a.prototype.serialize=function(a){void 0===a&&(a=!1);var b={};return b.type=this.type,b.inheritDirection=this.inheritDirection,b.inheritedVelocityAmount=this.inheritedVelocityAmount,b.particleSystem=this.particleSystem.serialize(a),b},a._ParseParticleSystem=function(a,b,c,d){throw void 0===d&&!1,Object(La.a)("ParseParticle")},a.Parse=function(b,c,d){var e=b.particleSystem;e=new a(a._ParseParticleSystem(e,c,d,!0));return e.type=b.type,e.inheritDirection=b.inheritDirection,e.inheritedVelocityAmount=b.inheritedVelocityAmount,e.particleSystem._isSubEmitter=!0,e},a.prototype.dispose=function(){this.particleSystem.dispose()},a}();a=" varying vec2 vUV; varying vec4 vColor; uniform vec4 textureMask; uniform sampler2D diffuseSampler; #include #include #include #include #ifdef RAMPGRADIENT varying vec4 remapRanges; uniform sampler2D rampSampler; #endif void main(void) { #include vec4 textureColor=texture2D(diffuseSampler,vUV); vec4 baseColor=(textureColor*textureMask+(vec4(1.,1.,1.,1.)-textureMask))*vColor; #ifdef RAMPGRADIENT float alpha=baseColor.a; float remappedColorIndex=clamp((alpha-remapRanges.x)/remapRanges.y,0.0,1.0); vec4 rampColor=texture2D(rampSampler,vec2(1.0-remappedColorIndex,0.)); baseColor.rgb*=rampColor.rgb; float finalAlpha=baseColor.a; baseColor.a=clamp((alpha*rampColor.a-remapRanges.z)/remapRanges.w,0.0,1.0); #endif #ifdef BLENDMULTIPLYMODE float sourceAlpha=vColor.a*textureColor.a; baseColor.rgb=baseColor.rgb*sourceAlpha+vec3(1.0)*(1.0-sourceAlpha); #endif #ifdef IMAGEPROCESSINGPOSTPROCESS baseColor.rgb=toLinearSpace(baseColor.rgb); #else #ifdef IMAGEPROCESSING baseColor.rgb=toLinearSpace(baseColor.rgb); baseColor=applyImageProcessing(baseColor); #endif #endif gl_FragColor=baseColor; }";X.a.ShadersStore.particlesPixelShader=a;wi=" attribute vec3 position; attribute vec4 color; attribute float angle; attribute vec2 size; #ifdef ANIMATESHEET attribute float cellIndex; #endif #ifndef BILLBOARD attribute vec3 direction; #endif #ifdef BILLBOARDSTRETCHED attribute vec3 direction; #endif #ifdef RAMPGRADIENT attribute vec4 remapData; #endif attribute vec2 offset; uniform mat4 view; uniform mat4 projection; uniform vec2 translationPivot; #ifdef ANIMATESHEET uniform vec3 particlesInfos; #endif varying vec2 vUV; varying vec4 vColor; varying vec3 vPositionW; #ifdef RAMPGRADIENT varying vec4 remapRanges; #endif #if defined(BILLBOARD) && !defined(BILLBOARDY) && !defined(BILLBOARDSTRETCHED) uniform mat4 invView; #endif #include #ifdef BILLBOARD uniform vec3 eyePosition; #endif vec3 rotate(vec3 yaxis,vec3 rotatedCorner) { vec3 xaxis=normalize(cross(vec3(0.,1.0,0.),yaxis)); vec3 zaxis=normalize(cross(yaxis,xaxis)); vec3 row0=vec3(xaxis.x,xaxis.y,xaxis.z); vec3 row1=vec3(yaxis.x,yaxis.y,yaxis.z); vec3 row2=vec3(zaxis.x,zaxis.y,zaxis.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; return position+alignedCorner; } #ifdef BILLBOARDSTRETCHED vec3 rotateAlign(vec3 toCamera,vec3 rotatedCorner) { vec3 normalizedToCamera=normalize(toCamera); vec3 normalizedCrossDirToCamera=normalize(cross(normalize(direction),normalizedToCamera)); vec3 crossProduct=normalize(cross(normalizedToCamera,normalizedCrossDirToCamera)); vec3 row0=vec3(normalizedCrossDirToCamera.x,normalizedCrossDirToCamera.y,normalizedCrossDirToCamera.z); vec3 row1=vec3(crossProduct.x,crossProduct.y,crossProduct.z); vec3 row2=vec3(normalizedToCamera.x,normalizedToCamera.y,normalizedToCamera.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; return position+alignedCorner; } #endif void main(void) { vec2 cornerPos; cornerPos=(vec2(offset.x-0.5,offset.y-0.5)-translationPivot)*size+translationPivot; #ifdef BILLBOARD vec3 rotatedCorner; #ifdef BILLBOARDY rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=position-eyePosition; yaxis.y=0.; vPositionW=rotate(normalize(yaxis),rotatedCorner); vec3 viewPos=(view*vec4(vPositionW,1.0)).xyz; #elif defined(BILLBOARDSTRETCHED) rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 toCamera=position-eyePosition; vPositionW=rotateAlign(toCamera,rotatedCorner); vec3 viewPos=(view*vec4(vPositionW,1.0)).xyz; #else rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 viewPos=(view*vec4(position,1.0)).xyz+rotatedCorner; vPositionW=(invView*vec4(viewPos,1)).xyz; #endif #ifdef RAMPGRADIENT remapRanges=remapData; #endif gl_Position=projection*vec4(viewPos,1.0); #else vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=normalize(direction); vPositionW=rotate(yaxis,rotatedCorner); gl_Position=projection*view*vec4(vPositionW,1.0); #endif vColor=color; #ifdef ANIMATESHEET float rowOffset=floor(cellIndex*particlesInfos.z); float columnOffset=cellIndex-rowOffset/particlesInfos.z; vec2 uvScale=particlesInfos.xy; vec2 uvOffset=vec2(offset.x ,1.0-offset.y); vUV=(uvOffset+vec2(columnOffset,rowOffset))*uvScale; #else vUV=offset; #endif #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=vec4(vPositionW,1.0); #endif #include }";X.a.ShadersStore.particlesVertexShader=wi;var Do=function(a){function b(b,c,d,e,i,j){void 0===e&&(e=null),void 0===i&&(i=!1),void 0===j&&(j=.01);var k=a.call(this,b)||this;k._emitterInverseWorldMatrix=g.a.Identity(),k._inheritedVelocityOffset=new g.e,k.onDisposeObservable=new f.c,k.onStoppedObservable=new f.c,k._particles=new Array,k._stockParticles=new Array,k._newPartsExcess=0,k._vertexBuffers={},k._scaledColorStep=new h.b(0,0,0,0),k._colorDiff=new h.b(0,0,0,0),k._scaledDirection=g.e.Zero(),k._scaledGravity=g.e.Zero(),k._currentRenderId=-1,k._useInstancing=!1,k._started=!1,k._stopped=!1,k._actualFrame=0,k._currentEmitRate1=0,k._currentEmitRate2=0,k._currentStartSize1=0,k._currentStartSize2=0,k._rawTextureWidth=256,k._useRampGradients=!1,k._disposeEmitterOnDispose=!1,k.isLocal=!1,k._onBeforeDrawParticlesObservable=null,k.recycleParticle=function(a){var b=k._particles.pop();b!==a&&b.copyTo(a),k._stockParticles.push(b)},k._createParticle=function(){var a;if(0!==k._stockParticles.length?(a=k._stockParticles.pop())._reset():a=new Bo(k),k._subEmitters&&k._subEmitters.length>0){var b=k._subEmitters[Math.floor(Math.random()*k._subEmitters.length)];a._attachedSubEmitters=[],b.forEach(function(b){if(b.type===wo.ATTACHED){b=b.clone();a._attachedSubEmitters.push(b),b.particleSystem.start()}})}return a},k._emitFromParticle=function(a){if(k._subEmitters&&0!==k._subEmitters.length){var b=Math.floor(Math.random()*k._subEmitters.length);k._subEmitters[b].forEach(function(b){if(b.type===wo.END){b=b.clone();a._inheritParticleInfoToSubEmitter(b),b.particleSystem._rootParticleSystem=k,k.activeSubSystems.push(b.particleSystem),b.particleSystem.start()}})}},k._capacity=c,k._epsilon=j,k._isAnimationSheetEnabled=i,d&&"Scene"!==d.getClassName()?(k._engine=d,k.defaultProjectionMatrix=g.a.PerspectiveFovLH(.8,1,.1,100,k._engine.isNDCHalfZRange)):(k._scene=d||C.a.LastCreatedScene,k._engine=k._scene.getEngine(),k.uniqueId=k._scene.getUniqueId(),k._scene.particleSystems.push(k)),k._engine.getCaps().vertexArrayObject&&(k._vertexArrayObject=null),k._attachImageProcessingConfiguration(null),k._customWrappers={0:new Ec.a(k._engine)},k._customWrappers[0].effect=e,k._drawWrapper=new Ec.a(k._engine),k._useInstancing=k._engine.getCaps().instancedArrays,k._createIndexBuffer(),k._createVertexBuffers(),k.particleEmitterType=new ul;var l=null;return k.updateFunction=function(a){var b,c=null;k.noiseTexture&&(c=k.noiseTexture.getSize(),null===(b=k.noiseTexture.getContent())||void 0===b||b.then(function(a){l=a}));for(var d,b=function(){d=a[e];var b=k._scaledUpdateSpeed,f=d.age;if(d.age+=b,d.age>d.lifeTime){var i=d.age-f;b=(d.lifeTime-f)*b/i,d.age=d.lifeTime}f=d.age/d.lifeTime;k._colorGradients&&k._colorGradients.length>0?Ao.GetCurrentGradient(f,k._colorGradients,function(a,b,c){a!==d._currentColorGradient&&(d._currentColor1.copyFrom(d._currentColor2),b.getColorToRef(d._currentColor2),d._currentColorGradient=a),h.b.LerpToRef(d._currentColor1,d._currentColor2,c,d.color)}):(d.colorStep.scaleToRef(b,k._scaledColorStep),d.color.addInPlace(k._scaledColorStep),d.color.a<0&&(d.color.a=0)),k._angularSpeedGradients&&k._angularSpeedGradients.length>0&&Ao.GetCurrentGradient(f,k._angularSpeedGradients,function(a,b,c){a!==d._currentAngularSpeedGradient&&(d._currentAngularSpeed1=d._currentAngularSpeed2,d._currentAngularSpeed2=b.getFactor(),d._currentAngularSpeedGradient=a),d.angularSpeed=H.a.Lerp(d._currentAngularSpeed1,d._currentAngularSpeed2,c)}),d.angle+=d.angularSpeed*b;var j=b;if(k._velocityGradients&&k._velocityGradients.length>0&&Ao.GetCurrentGradient(f,k._velocityGradients,function(a,b,c){a!==d._currentVelocityGradient&&(d._currentVelocity1=d._currentVelocity2,d._currentVelocity2=b.getFactor(),d._currentVelocityGradient=a),j*=H.a.Lerp(d._currentVelocity1,d._currentVelocity2,c)}),d.direction.scaleToRef(j,k._scaledDirection),k._limitVelocityGradients&&k._limitVelocityGradients.length>0&&Ao.GetCurrentGradient(f,k._limitVelocityGradients,function(a,b,c){a!==d._currentLimitVelocityGradient&&(d._currentLimitVelocity1=d._currentLimitVelocity2,d._currentLimitVelocity2=b.getFactor(),d._currentLimitVelocityGradient=a);b=H.a.Lerp(d._currentLimitVelocity1,d._currentLimitVelocity2,c);d.direction.length()>b&&d.direction.scaleInPlace(k.limitVelocityDamping)}),k._dragGradients&&k._dragGradients.length>0&&Ao.GetCurrentGradient(f,k._dragGradients,function(a,b,c){a!==d._currentDragGradient&&(d._currentDrag1=d._currentDrag2,d._currentDrag2=b.getFactor(),d._currentDragGradient=a);b=H.a.Lerp(d._currentDrag1,d._currentDrag2,c);k._scaledDirection.scaleInPlace(1-b)}),k.isLocal&&d._localPosition?(d._localPosition.addInPlace(k._scaledDirection),g.e.TransformCoordinatesToRef(d._localPosition,k._emitterWorldMatrix,d.position)):d.position.addInPlace(k._scaledDirection),l&&c&&d._randomNoiseCoordinates1){i=k._fetchR(d._randomNoiseCoordinates1.x,d._randomNoiseCoordinates1.y,c.width,c.height,l);var n=k._fetchR(d._randomNoiseCoordinates1.z,d._randomNoiseCoordinates2.x,c.width,c.height,l),s=k._fetchR(d._randomNoiseCoordinates2.y,d._randomNoiseCoordinates2.z,c.width,c.height,l),t=g.c.Vector3[0],u=g.c.Vector3[1];t.copyFromFloats((2*i-1)*k.noiseStrength.x,(2*n-1)*k.noiseStrength.y,(2*s-1)*k.noiseStrength.z),t.scaleToRef(b,u),d.direction.addInPlace(u)}if(k.gravity.scaleToRef(b,k._scaledGravity),d.direction.addInPlace(k._scaledGravity),k._sizeGradients&&k._sizeGradients.length>0&&Ao.GetCurrentGradient(f,k._sizeGradients,function(a,b,c){a!==d._currentSizeGradient&&(d._currentSize1=d._currentSize2,d._currentSize2=b.getFactor(),d._currentSizeGradient=a),d.size=H.a.Lerp(d._currentSize1,d._currentSize2,c)}),k._useRampGradients&&(k._colorRemapGradients&&k._colorRemapGradients.length>0&&Ao.GetCurrentGradient(f,k._colorRemapGradients,function(a,b,c){var e=H.a.Lerp(a.factor1,b.factor1,c);a=H.a.Lerp(a.factor2,b.factor2,c);d.remapData.x=e,d.remapData.y=a-e}),k._alphaRemapGradients&&k._alphaRemapGradients.length>0&&Ao.GetCurrentGradient(f,k._alphaRemapGradients,function(a,b,c){var e=H.a.Lerp(a.factor1,b.factor1,c);a=H.a.Lerp(a.factor2,b.factor2,c);d.remapData.z=e,d.remapData.w=a-e})),k._isAnimationSheetEnabled&&d.updateCellIndex(),d._inheritParticleInfoToSubEmitters(),d.age>=d.lifeTime)return k._emitFromParticle(d),d._attachedSubEmitters&&(d._attachedSubEmitters.forEach(function(a){a.particleSystem.disposeOnStop=!0,a.particleSystem.stop()}),d._attachedSubEmitters=null),k.recycleParticle(d),e--,"continue"},e=0;eb.gradient?1:0})},b.prototype._removeFactorGradient=function(a,b){if(a)for(var c=0,d=0,e=a;db.gradient?1:0}),this._rampGradientsTexture&&(this._rampGradientsTexture.dispose(),this._rampGradientsTexture=null),this._createRampGradientTexture())},b.prototype.addRampGradient=function(a,b){this._rampGradients||(this._rampGradients=[]);a=new yo(a,b);return this._rampGradients.push(a),this._syncRampGradientTexture(),this},b.prototype.removeRampGradient=function(a){return this._removeGradientAndTexture(a,this._rampGradients,this._rampGradientsTexture),this._rampGradientsTexture=null,this._rampGradients&&this._rampGradients.length>0&&this._createRampGradientTexture(),this},b.prototype.addColorGradient=function(a,b,c){this._colorGradients||(this._colorGradients=[]);a=new xo(a,b,c);return this._colorGradients.push(a),this._colorGradients.sort(function(a,b){return a.gradientb.gradient?1:0}),this},b.prototype.removeColorGradient=function(a){if(!this._colorGradients)return this;for(var b=0,c=0,d=this._colorGradients;c0&&(this._currentEmitRateGradient=this._emitRateGradients[0],this._currentEmitRate1=this._currentEmitRateGradient.getFactor(),this._currentEmitRate2=this._currentEmitRate1),this._emitRateGradients.length>1&&(this._currentEmitRate2=this._emitRateGradients[1].getFactor())),this._startSizeGradients&&(this._startSizeGradients.length>0&&(this._currentStartSizeGradient=this._startSizeGradients[0],this._currentStartSize1=this._currentStartSizeGradient.getFactor(),this._currentStartSize2=this._currentStartSize1),this._startSizeGradients.length>1&&(this._currentStartSize2=this._startSizeGradients[1].getFactor())),this.preWarmCycles){-1!==(null===(a=this.emitter)||void 0===a?void 0:a.getClassName().indexOf("Mesh"))&&this.emitter.computeWorldMatrix(!0);var c=this.noiseTexture;if(c&&c.onGeneratedObservable)c.onGeneratedObservable.addOnce(function(){setTimeout(function(){for(var a=0;a0&&this._scene&&this._scene.beginAnimation(this,this.beginAnimationFrom,this.beginAnimationTo,this.beginAnimationLoop)}},b.prototype.stop=function(a){void 0===a&&(a=!0),this._stopped||(this.onStoppedObservable.notifyObservers(this),this._stopped=!0,a&&this._stopSubEmitters())},b.prototype.reset=function(){this._stockParticles=[],this._particles=[]},b.prototype._appendParticleVertex=function(a,c,d,e){a=a*this._vertexBufferSize;if(this._vertexData[a++]=c.position.x+this.worldOffset.x,this._vertexData[a++]=c.position.y+this.worldOffset.y,this._vertexData[a++]=c.position.z+this.worldOffset.z,this._vertexData[a++]=c.color.r,this._vertexData[a++]=c.color.g,this._vertexData[a++]=c.color.b,this._vertexData[a++]=c.color.a,this._vertexData[a++]=c.angle,this._vertexData[a++]=c.scale.x*c.size,this._vertexData[a++]=c.scale.y*c.size,this._isAnimationSheetEnabled&&(this._vertexData[a++]=c.cellIndex),this._isBillboardBased)this.billboardMode===b.BILLBOARDMODE_STRETCHED&&(this._vertexData[a++]=c.direction.x,this._vertexData[a++]=c.direction.y,this._vertexData[a++]=c.direction.z);else if(c._initialDirection){var f=c._initialDirection;this.isLocal&&(g.e.TransformNormalToRef(f,this._emitterWorldMatrix,g.c.Vector3[0]),f=g.c.Vector3[0]),0===f.x&&0===f.z&&(f.x=.001),this._vertexData[a++]=f.x,this._vertexData[a++]=f.y,this._vertexData[a++]=f.z}else{f=c.direction;this.isLocal&&(g.e.TransformNormalToRef(f,this._emitterWorldMatrix,g.c.Vector3[0]),f=g.c.Vector3[0]),0===f.x&&0===f.z&&(f.x=.001),this._vertexData[a++]=f.x,this._vertexData[a++]=f.y,this._vertexData[a++]=f.z}this._useRampGradients&&c.remapData&&(this._vertexData[a++]=c.remapData.x,this._vertexData[a++]=c.remapData.y,this._vertexData[a++]=c.remapData.z,this._vertexData[a++]=c.remapData.w),this._useInstancing||(this._isAnimationSheetEnabled&&(0===d?d=this._epsilon:1===d&&(d=1-this._epsilon),0===e?e=this._epsilon:1===e&&(e=1-this._epsilon)),this._vertexData[a++]=d,this._vertexData[a++]=e)},b.prototype._stopSubEmitters=function(){this.activeSubSystems&&(this.activeSubSystems.forEach(function(a){a.stop(!0)}),this.activeSubSystems=new Array)},b.prototype._removeFromRoot=function(){if(this._rootParticleSystem){var a=this._rootParticleSystem.activeSubSystems.indexOf(this);-1!==a&&this._rootParticleSystem.activeSubSystems.splice(a,1),this._rootParticleSystem=null}},b.prototype._update=function(a){var b,c=this;if(this._alive=this._particles.length>0,this.emitter.position){var d=this.emitter;this._emitterWorldMatrix=d.getWorldMatrix()}else{d=this.emitter;this._emitterWorldMatrix=g.a.Translation(d.x,d.y,d.z)}this._emitterWorldMatrix.invertToRef(this._emitterInverseWorldMatrix),this.updateFunction(this._particles);for(var d,e=function(){if(f._particles.length===f._capacity)return"break";if(b=f._createParticle(),f._particles.push(b),f.targetStopDuration&&f._lifeTimeGradients&&f._lifeTimeGradients.length>0){var a=H.a.Clamp(f._actualFrame/f.targetStopDuration);Ao.GetCurrentGradient(a,f._lifeTimeGradients,function(c,d){c=c;d=d;var e=c.getFactor(),f=d.getFactor();d=(a-c.gradient)/(d.gradient-c.gradient);b.lifeTime=H.a.Lerp(e,f,d)})}else b.lifeTime=H.a.RandomRange(f.minLifeTime,f.maxLifeTime);var d=H.a.RandomRange(f.minEmitPower,f.maxEmitPower);if(f.startPositionFunction?f.startPositionFunction(f._emitterWorldMatrix,b.position,b,f.isLocal):f.particleEmitterType.startPositionFunction(f._emitterWorldMatrix,b.position,b,f.isLocal),f.isLocal&&(b._localPosition?b._localPosition.copyFrom(b.position):b._localPosition=b.position.clone(),g.e.TransformCoordinatesToRef(b._localPosition,f._emitterWorldMatrix,b.position)),f.startDirectionFunction?f.startDirectionFunction(f._emitterWorldMatrix,b.direction,b,f.isLocal):f.particleEmitterType.startDirectionFunction(f._emitterWorldMatrix,b.direction,b,f.isLocal,f._emitterInverseWorldMatrix),0===d?b._initialDirection?b._initialDirection.copyFrom(b.direction):b._initialDirection=b.direction.clone():b._initialDirection=null,b.direction.scaleInPlace(d),f._sizeGradients&&0!==f._sizeGradients.length?(b._currentSizeGradient=f._sizeGradients[0],b._currentSize1=b._currentSizeGradient.getFactor(),b.size=b._currentSize1,f._sizeGradients.length>1?b._currentSize2=f._sizeGradients[1].getFactor():b._currentSize2=b._currentSize1):b.size=H.a.RandomRange(f.minSize,f.maxSize),b.scale.copyFromFloats(H.a.RandomRange(f.minScaleX,f.maxScaleX),H.a.RandomRange(f.minScaleY,f.maxScaleY)),f._startSizeGradients&&f._startSizeGradients[0]&&f.targetStopDuration){d=f._actualFrame/f.targetStopDuration;Ao.GetCurrentGradient(d,f._startSizeGradients,function(a,d,e){a!==c._currentStartSizeGradient&&(c._currentStartSize1=c._currentStartSize2,c._currentStartSize2=d.getFactor(),c._currentStartSizeGradient=a);d=H.a.Lerp(c._currentStartSize1,c._currentStartSize2,e);b.scale.scaleInPlace(d)})}f._angularSpeedGradients&&0!==f._angularSpeedGradients.length?(b._currentAngularSpeedGradient=f._angularSpeedGradients[0],b.angularSpeed=b._currentAngularSpeedGradient.getFactor(),b._currentAngularSpeed1=b.angularSpeed,f._angularSpeedGradients.length>1?b._currentAngularSpeed2=f._angularSpeedGradients[1].getFactor():b._currentAngularSpeed2=b._currentAngularSpeed1):b.angularSpeed=H.a.RandomRange(f.minAngularSpeed,f.maxAngularSpeed),b.angle=H.a.RandomRange(f.minInitialRotation,f.maxInitialRotation),f._velocityGradients&&f._velocityGradients.length>0&&(b._currentVelocityGradient=f._velocityGradients[0],b._currentVelocity1=b._currentVelocityGradient.getFactor(),f._velocityGradients.length>1?b._currentVelocity2=f._velocityGradients[1].getFactor():b._currentVelocity2=b._currentVelocity1),f._limitVelocityGradients&&f._limitVelocityGradients.length>0&&(b._currentLimitVelocityGradient=f._limitVelocityGradients[0],b._currentLimitVelocity1=b._currentLimitVelocityGradient.getFactor(),f._limitVelocityGradients.length>1?b._currentLimitVelocity2=f._limitVelocityGradients[1].getFactor():b._currentLimitVelocity2=b._currentLimitVelocity1),f._dragGradients&&f._dragGradients.length>0&&(b._currentDragGradient=f._dragGradients[0],b._currentDrag1=b._currentDragGradient.getFactor(),f._dragGradients.length>1?b._currentDrag2=f._dragGradients[1].getFactor():b._currentDrag2=b._currentDrag1),f._colorGradients&&0!==f._colorGradients.length?(b._currentColorGradient=f._colorGradients[0],b._currentColorGradient.getColorToRef(b.color),b._currentColor1.copyFrom(b.color),f._colorGradients.length>1?f._colorGradients[1].getColorToRef(b._currentColor2):b._currentColor2.copyFrom(b.color)):(d=H.a.RandomRange(0,1),h.b.LerpToRef(f.color1,f.color2,d,b.color),f.colorDead.subtractToRef(b.color,f._colorDiff),f._colorDiff.scaleToRef(1/b.lifeTime,b.colorStep)),f._isAnimationSheetEnabled&&(b._initialStartSpriteCellID=f.startSpriteCellID,b._initialEndSpriteCellID=f.endSpriteCellID,b._initialSpriteCellLoop=f.spriteCellLoop),b.direction.addInPlace(f._inheritedVelocityOffset),f._useRampGradients&&(b.remapData=new g.f(0,1,0,1)),f.noiseTexture&&(b._randomNoiseCoordinates1?(b._randomNoiseCoordinates1.copyFromFloats(Math.random(),Math.random(),Math.random()),b._randomNoiseCoordinates2.copyFromFloats(Math.random(),Math.random(),Math.random())):(b._randomNoiseCoordinates1=new g.e(Math.random(),Math.random(),Math.random()),b._randomNoiseCoordinates2=new g.e(Math.random(),Math.random(),Math.random()))),b._inheritParticleInfoToSubEmitters()},f=this,i=0;i-1)b=this.manualEmitCount,this._newPartsExcess=0,this.manualEmitCount=0;else{var d=this.emitRate;if(this._emitRateGradients&&this._emitRateGradients.length>0&&this.targetStopDuration){var e=this._actualFrame/this.targetStopDuration;Ao.GetCurrentGradient(e,this._emitRateGradients,function(a,b,e){a!==c._currentEmitRateGradient&&(c._currentEmitRate1=c._currentEmitRate2,c._currentEmitRate2=b.getFactor(),c._currentEmitRateGradient=a),d=H.a.Lerp(c._currentEmitRate1,c._currentEmitRate2,e)})}b=d*this._scaledUpdateSpeed>>0,this._newPartsExcess+=d*this._scaledUpdateSpeed-b}if(this._newPartsExcess>1&&(b+=this._newPartsExcess>>0,this._newPartsExcess-=this._newPartsExcess>>0),this._alive=!1,this._stopped?b=0:(this._actualFrame+=this._scaledUpdateSpeed,this.targetStopDuration&&this._actualFrame>=this.targetStopDuration&&this.stop()),this._update(b),this._stopped&&(this._alive||(this._started=!1,this.onAnimationEnd&&this.onAnimationEnd(),this.disposeOnStop&&this._scene&&this._scene._toBeDisposed.push(this))),!a){for(e=0,b=0;b=0&&(d.invertToRef(g.c.Matrix[0]),e.setMatrix("invView",g.c.Matrix[0])),void 0!==this._vertexArrayObject?(this._vertexArrayObject||(this._vertexArrayObject=this._engine.recordVertexArrayObject(this._vertexBuffers,this._indexBuffer,e)),this._engine.bindVertexArrayObject(this._vertexArrayObject,this._indexBuffer)):f.bindBuffers(this._vertexBuffers,this._indexBuffer,e),this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(e),a){case b.BLENDMODE_ADD:f.setAlphaMode(r.a.ALPHA_ADD);break;case b.BLENDMODE_ONEONE:f.setAlphaMode(r.a.ALPHA_ONEONE);break;case b.BLENDMODE_STANDARD:f.setAlphaMode(r.a.ALPHA_COMBINE);break;case b.BLENDMODE_MULTIPLY:f.setAlphaMode(r.a.ALPHA_MULTIPLY)}return this._onBeforeDrawParticlesObservable&&this._onBeforeDrawParticlesObservable.notifyObservers(e),this._useInstancing?f.drawArraysType(r.a.MATERIAL_TriangleStripDrawMode,0,4,this._particles.length):f.drawElementsType(r.a.MATERIAL_TriangleFillMode,0,6*this._particles.length),this._particles.length},b.prototype.render=function(){if(!this.isReady()||!this._particles.length)return 0;var a=this._engine;a.setState&&(a.setState(!1),this.forceDepthWrite&&a.setDepthWrite(!0));a=0;return a=this.blendMode===b.BLENDMODE_MULTIPLYADD?this._render(b.BLENDMODE_MULTIPLY)+this._render(b.BLENDMODE_ADD):this._render(this.blendMode),this._engine.unbindInstanceAttributes(),this._engine.setAlphaMode(r.a.ALPHA_DISABLE),a},b.prototype.dispose=function(a){if(void 0===a&&(a=!0),this._vertexBuffer&&(this._vertexBuffer.dispose(),this._vertexBuffer=null),this._spriteBuffer&&(this._spriteBuffer.dispose(),this._spriteBuffer=null),this._indexBuffer&&(this._engine._releaseBuffer(this._indexBuffer),this._indexBuffer=null),this._vertexArrayObject&&(this._engine.releaseVertexArrayObject(this._vertexArrayObject),this._vertexArrayObject=null),a&&this.particleTexture&&(this.particleTexture.dispose(),this.particleTexture=null),a&&this.noiseTexture&&(this.noiseTexture.dispose(),this.noiseTexture=null),this._rampGradientsTexture&&(this._rampGradientsTexture.dispose(),this._rampGradientsTexture=null),this._removeFromRoot(),this._subEmitters&&this._subEmitters.length){for(a=0;a-1&&this._scene.particleSystems.splice(a,1),this._scene._activeParticleSystems.dispose());this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear(),this.onStoppedObservable.clear(),this.reset()},b.prototype.clone=function(a,c){var d=Object(l.a)({},this._customWrappers),e=null,f=this._engine;if(f.createEffectForParticles&&null!=this.customShader){var g=(e=this.customShader).shaderOptions.defines.length>0?e.shaderOptions.defines.join(" "):"";d[0]=f.createEffectForParticles(e.shaderPath.fragmentElement,e.shaderOptions.uniforms,e.shaderOptions.samplers,g)}f=this.serialize();g=b.Parse(f,this._scene||this._engine,this._rootUrl);return g.name=a,g.customShader=e,g._customWrappers=d,void 0===c&&(c=this.emitter),this.noiseTexture&&(g.noiseTexture=this.noiseTexture.clone()),g.emitter=c,this.preventAutoStart||g.start(),g},b.prototype.serialize=function(a){void 0===a&&(a=!1);var c={};if(b._Serialize(c,this,a),c.textureMask=this.textureMask.asArray(),c.customShader=this.customShader,c.preventAutoStart=this.preventAutoStart,this.subEmitters){c.subEmitters=[],this._subEmitters||this._prepareSubEmitterInternalArray();for(var d=0,e=this._subEmitters;d0?k.shaderOptions.defines.join(" "):"";j=g.createEffectForParticles(k.shaderPath.fragmentElement,k.shaderOptions.uniforms,k.shaderOptions.samplers,l)}g=new b(i,f||a.capacity,c,j,a.isAnimationSheetEnabled);if(g.customShader=k,g._rootUrl=d,a.id&&(g.id=a.id),a.subEmitters){g.subEmitters=[];for(l=0,i=a.subEmitters;l #include #include #include void main() { #include vec4 textureColor=texture2D(diffuseSampler,vUV); gl_FragColor=textureColor*vColor; #ifdef BLENDMULTIPLYMODE float alpha=vColor.a*textureColor.a; gl_FragColor.rgb=gl_FragColor.rgb*alpha+vec3(1.0)*(1.0-alpha); #endif #ifdef IMAGEPROCESSINGPOSTPROCESS gl_FragColor.rgb=toLinearSpace(gl_FragColor.rgb); #else #ifdef IMAGEPROCESSING gl_FragColor.rgb=toLinearSpace(gl_FragColor.rgb); gl_FragColor=applyImageProcessing(gl_FragColor); #endif #endif } ";X.a.ShadersStore.gpuRenderParticlesPixelShader=a;wi="#ifdef CLIPPLANE uniform vec4 vClipPlane; out float fClipDistance; #endif #ifdef CLIPPLANE2 uniform vec4 vClipPlane2; out float fClipDistance2; #endif #ifdef CLIPPLANE3 uniform vec4 vClipPlane3; out float fClipDistance3; #endif #ifdef CLIPPLANE4 uniform vec4 vClipPlane4; out float fClipDistance4; #endif #ifdef CLIPPLANE5 uniform vec4 vClipPlane5; out float fClipDistance5; #endif #ifdef CLIPPLANE6 uniform vec4 vClipPlane6; out float fClipDistance6; #endif";X.a.IncludesShadersStore.clipPlaneVertexDeclaration2=wi;V="precision highp float; uniform mat4 view; uniform mat4 projection; uniform vec2 translationPivot; uniform vec3 worldOffset; #ifdef LOCAL uniform mat4 emitterWM; #endif attribute vec3 position; attribute float age; attribute float life; attribute vec3 size; #ifndef BILLBOARD attribute vec3 initialDirection; #endif #ifdef BILLBOARDSTRETCHED attribute vec3 direction; #endif attribute float angle; #ifdef ANIMATESHEET attribute float cellIndex; #endif attribute vec2 offset; attribute vec2 uv; varying vec2 vUV; varying vec4 vColor; varying vec3 vPositionW; #if defined(BILLBOARD) && !defined(BILLBOARDY) && !defined(BILLBOARDSTRETCHED) uniform mat4 invView; #endif #include #ifdef COLORGRADIENTS uniform sampler2D colorGradientSampler; #else uniform vec4 colorDead; attribute vec4 color; #endif #ifdef ANIMATESHEET uniform vec3 sheetInfos; #endif #ifdef BILLBOARD uniform vec3 eyePosition; #endif vec3 rotate(vec3 yaxis,vec3 rotatedCorner) { vec3 xaxis=normalize(cross(vec3(0.,1.0,0.),yaxis)); vec3 zaxis=normalize(cross(yaxis,xaxis)); vec3 row0=vec3(xaxis.x,xaxis.y,xaxis.z); vec3 row1=vec3(yaxis.x,yaxis.y,yaxis.z); vec3 row2=vec3(zaxis.x,zaxis.y,zaxis.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; #ifdef LOCAL return ((emitterWM*vec4(position,1.0)).xyz+worldOffset)+alignedCorner; #else return (position+worldOffset)+alignedCorner; #endif } #ifdef BILLBOARDSTRETCHED vec3 rotateAlign(vec3 toCamera,vec3 rotatedCorner) { vec3 normalizedToCamera=normalize(toCamera); vec3 normalizedCrossDirToCamera=normalize(cross(normalize(direction),normalizedToCamera)); vec3 crossProduct=normalize(cross(normalizedToCamera,normalizedCrossDirToCamera)); vec3 row0=vec3(normalizedCrossDirToCamera.x,normalizedCrossDirToCamera.y,normalizedCrossDirToCamera.z); vec3 row1=vec3(crossProduct.x,crossProduct.y,crossProduct.z); vec3 row2=vec3(normalizedToCamera.x,normalizedToCamera.y,normalizedToCamera.z); mat3 rotMatrix=mat3(row0,row1,row2); vec3 alignedCorner=rotMatrix*rotatedCorner; #ifdef LOCAL return ((emitterWM*vec4(position,1.0)).xyz+worldOffset)+alignedCorner; #else return (position+worldOffset)+alignedCorner; #endif } #endif void main() { #ifdef ANIMATESHEET float rowOffset=floor(cellIndex/sheetInfos.z); float columnOffset=cellIndex-rowOffset*sheetInfos.z; vec2 uvScale=sheetInfos.xy; vec2 uvOffset=vec2(uv.x ,1.0-uv.y); vUV=(uvOffset+vec2(columnOffset,rowOffset))*uvScale; #else vUV=uv; #endif float ratio=age/life; #ifdef COLORGRADIENTS vColor=texture2D(colorGradientSampler,vec2(ratio,0)); #else vColor=color*vec4(1.0-ratio)+colorDead*vec4(ratio); #endif vec2 cornerPos=(offset-translationPivot)*size.yz*size.x+translationPivot; #ifdef BILLBOARD vec4 rotatedCorner; rotatedCorner.w=0.; #ifdef BILLBOARDY rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.y=0.; vec3 yaxis=(position+worldOffset)-eyePosition; yaxis.y=0.; vPositionW=rotate(normalize(yaxis),rotatedCorner.xyz); vec4 viewPosition=(view*vec4(vPositionW,1.0)); #elif defined(BILLBOARDSTRETCHED) rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; vec3 toCamera=(position+worldOffset)-eyePosition; vPositionW=rotateAlign(toCamera,rotatedCorner.xyz); vec4 viewPosition=(view*vec4(vPositionW,1.0)); #else rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; #ifdef LOCAL vec4 viewPosition=view*vec4(((emitterWM*vec4(position,1.0)).xyz+worldOffset),1.0)+rotatedCorner; #else vec4 viewPosition=view*vec4((position+worldOffset),1.0)+rotatedCorner; #endif vPositionW=(invView*viewPosition).xyz; #endif #else vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=0.; rotatedCorner.z=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); vec3 yaxis=normalize(initialDirection); vPositionW=rotate(yaxis,rotatedCorner); vec4 viewPosition=view*vec4(vPositionW,1.0); #endif gl_Position=projection*viewPosition; #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=vec4(vPositionW,1.0); #endif #include }";X.a.ShadersStore.gpuRenderParticlesVertexShader=V;var Eo=function(a){function b(b,c,d,e,h){void 0===e&&(e=null),void 0===h&&(h=!1);b=a.call(this,b)||this;if(b.layerMask=268435455,b._accumulatedCount=0,b._targetIndex=0,b._currentRenderId=-1,b._currentRenderingCameraUniqueId=-1,b._started=!1,b._stopped=!1,b._timeDelta=0,b._actualFrame=0,b._rawTextureWidth=256,b.onDisposeObservable=new f.c,b.onStoppedObservable=new f.c,b.forceDepthWrite=!1,b._preWarmDone=!1,b.isLocal=!1,b._onBeforeDrawParticlesObservable=null,d&&"Scene"!==d.getClassName()?(b._engine=d,b.defaultProjectionMatrix=g.a.PerspectiveFovLH(.8,1,.1,100,b._engine.isNDCHalfZRange)):(b._scene=d||C.a.LastCreatedScene,b._engine=b._scene.getEngine(),b.uniqueId=b._scene.getUniqueId(),b._scene.particleSystems.push(b)),b._engine.getCaps().supportComputeShaders){if(!Object(i.a)("BABYLON.ComputeShaderParticleSystem"))throw new Error("The ComputeShaderParticleSystem class is not available! Make sure you have imported it.");b._platform=new(Object(i.a)("BABYLON.ComputeShaderParticleSystem"))(b,b._engine)}else{if(!Object(i.a)("BABYLON.WebGL2ParticleSystem"))throw new Error("The WebGL2ParticleSystem class is not available! Make sure you have imported it.");b._platform=new(Object(i.a)("BABYLON.WebGL2ParticleSystem"))(b,b._engine)}b._customWrappers={0:new Ec.a(b._engine)},b._customWrappers[0].effect=e,b._drawWrapper=new Ec.a(b._engine),b._attachImageProcessingConfiguration(null),(c=null!=c?c:{}).randomTextureSize||delete c.randomTextureSize;e=Object(l.a)({capacity:5e4,randomTextureSize:b._engine.getCaps().maxTextureSize},c);c=c;isFinite(c)&&(e.capacity=c),b._capacity=e.capacity,b._activeCount=e.capacity,b._currentActiveCount=0,b._isAnimationSheetEnabled=h,b.particleEmitterType=new ul;for(c=Math.min(b._engine.getCaps().maxTextureSize,e.randomTextureSize),h=[],e=0;e1||C.a.LastCreatedEngine.getCaps().supportComputeShaders)},enumerable:!1,configurable:!0}),b.prototype.getCapacity=function(){return this._capacity},Object.defineProperty(b.prototype,"activeParticleCount",{get:function(){return this._activeCount},set:function(a){this._activeCount=Math.min(a,this._capacity)},enumerable:!1,configurable:!0}),b.prototype.isReady=function(){if(!this.emitter||this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.isReady()||!this.particleTexture||!this.particleTexture.isReady())return!1;if(this.blendMode!==Do.BLENDMODE_MULTIPLYADD){if(!this._getWrapper(this.blendMode).effect.isReady())return!1}else{if(!this._getWrapper(Do.BLENDMODE_MULTIPLY).effect.isReady())return!1;if(!this._getWrapper(Do.BLENDMODE_ADD).effect.isReady())return!1}return this._platform.isUpdateBufferCreated()?this._platform.isUpdateBufferReady():(this._recreateUpdateEffect(),!1)},b.prototype.isStarted=function(){return this._started},b.prototype.isStopped=function(){return this._stopped},b.prototype.isStopping=function(){return!1},b.prototype.getActiveCount=function(){return this._currentActiveCount},b.prototype.start=function(a){var b=this;if(void 0===a&&(a=this.startDelay),!this.targetStopDuration&&this._hasTargetStopDurationDependantGradient())throw"Particle system started with a targetStopDuration dependant gradient (eg. startSizeGradients) but no targetStopDuration set";a?setTimeout(function(){b.start(0)},a):(this._started=!0,this._stopped=!1,this._preWarmDone=!1,this.beginAnimationOnStart&&this.animations&&this.animations.length>0&&this._scene&&this._scene.beginAnimation(this,this.beginAnimationFrom,this.beginAnimationTo,this.beginAnimationLoop))},b.prototype.stop=function(){this._stopped||(this._stopped=!0)},b.prototype.reset=function(){this._releaseBuffers(),this._platform.releaseVertexBuffers(),this._currentActiveCount=0,this._targetIndex=0},b.prototype.getClassName=function(){return"GPUParticleSystem"},b.prototype.getCustomEffect=function(a){return void 0===a&&(a=0),null!==(a=null===(a=this._customWrappers[a])||void 0===a?void 0:a.effect)&&void 0!==a?a:this._customWrappers[0].effect},b.prototype._getCustomDrawWrapper=function(a){return void 0===a&&(a=0),null!==(a=this._customWrappers[a])&&void 0!==a?a:this._customWrappers[0]},b.prototype.setCustomEffect=function(a,b){void 0===b&&(b=0),this._customWrappers[b]=new Ec.a(this._engine),this._customWrappers[b].effect=a},Object.defineProperty(b.prototype,"onBeforeDrawParticlesObservable",{get:function(){return this._onBeforeDrawParticlesObservable||(this._onBeforeDrawParticlesObservable=new f.c),this._onBeforeDrawParticlesObservable},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"vertexShaderName",{get:function(){return"gpuRenderParticles"},enumerable:!1,configurable:!0}),b.prototype._removeGradientAndTexture=function(b,c,d){return a.prototype._removeGradientAndTexture.call(this,b,c,d),this._releaseBuffers(),this},b.prototype.addColorGradient=function(a,b,c){this._colorGradients||(this._colorGradients=[]);c=new xo(a,b);return this._colorGradients.push(c),this._refreshColorGradient(!0),this._releaseBuffers(),this},b.prototype._refreshColorGradient=function(a){void 0===a&&(a=!1),this._colorGradients&&(a&&this._colorGradients.sort(function(a,b){return a.gradientb.gradient?1:0}),this._colorGradientsTexture&&(this._colorGradientsTexture.dispose(),this._colorGradientsTexture=null))},b.prototype.forceRefreshGradients=function(){this._refreshColorGradient(),this._refreshFactorGradient(this._sizeGradients,"_sizeGradientsTexture"),this._refreshFactorGradient(this._angularSpeedGradients,"_angularSpeedGradientsTexture"),this._refreshFactorGradient(this._velocityGradients,"_velocityGradientsTexture"),this._refreshFactorGradient(this._limitVelocityGradients,"_limitVelocityGradientsTexture"),this._refreshFactorGradient(this._dragGradients,"_dragGradientsTexture"),this.reset()},b.prototype.removeColorGradient=function(a){return this._removeGradientAndTexture(a,this._colorGradients,this._colorGradientsTexture),this._colorGradientsTexture=null,this},b.prototype._addFactorGradient=function(a,b,c){b=new zo(b,c);a.push(b),this._releaseBuffers()},b.prototype.addSizeGradient=function(a,b){return this._sizeGradients||(this._sizeGradients=[]),this._addFactorGradient(this._sizeGradients,a,b),this._refreshFactorGradient(this._sizeGradients,"_sizeGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeSizeGradient=function(a){return this._removeGradientAndTexture(a,this._sizeGradients,this._sizeGradientsTexture),this._sizeGradientsTexture=null,this},b.prototype._refreshFactorGradient=function(a,b,c){(void 0===c&&(c=!1),a)&&(c&&a.sort(function(a,b){return a.gradientb.gradient?1:0}),this[b]&&(this[b].dispose(),this[b]=null))},b.prototype.addAngularSpeedGradient=function(a,b){return this._angularSpeedGradients||(this._angularSpeedGradients=[]),this._addFactorGradient(this._angularSpeedGradients,a,b),this._refreshFactorGradient(this._angularSpeedGradients,"_angularSpeedGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeAngularSpeedGradient=function(a){return this._removeGradientAndTexture(a,this._angularSpeedGradients,this._angularSpeedGradientsTexture),this._angularSpeedGradientsTexture=null,this},b.prototype.addVelocityGradient=function(a,b){return this._velocityGradients||(this._velocityGradients=[]),this._addFactorGradient(this._velocityGradients,a,b),this._refreshFactorGradient(this._velocityGradients,"_velocityGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeVelocityGradient=function(a){return this._removeGradientAndTexture(a,this._velocityGradients,this._velocityGradientsTexture),this._velocityGradientsTexture=null,this},b.prototype.addLimitVelocityGradient=function(a,b){return this._limitVelocityGradients||(this._limitVelocityGradients=[]),this._addFactorGradient(this._limitVelocityGradients,a,b),this._refreshFactorGradient(this._limitVelocityGradients,"_limitVelocityGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeLimitVelocityGradient=function(a){return this._removeGradientAndTexture(a,this._limitVelocityGradients,this._limitVelocityGradientsTexture),this._limitVelocityGradientsTexture=null,this},b.prototype.addDragGradient=function(a,b){return this._dragGradients||(this._dragGradients=[]),this._addFactorGradient(this._dragGradients,a,b),this._refreshFactorGradient(this._dragGradients,"_dragGradientsTexture",!0),this._releaseBuffers(),this},b.prototype.removeDragGradient=function(a){return this._removeGradientAndTexture(a,this._dragGradients,this._dragGradientsTexture),this._dragGradientsTexture=null,this},b.prototype.addEmitRateGradient=function(a,b,c){return this},b.prototype.removeEmitRateGradient=function(a){return this},b.prototype.addStartSizeGradient=function(a,b,c){return this},b.prototype.removeStartSizeGradient=function(a){return this},b.prototype.addColorRemapGradient=function(a,b,c){return this},b.prototype.removeColorRemapGradient=function(){return this},b.prototype.addAlphaRemapGradient=function(a,b,c){return this},b.prototype.removeAlphaRemapGradient=function(){return this},b.prototype.addRampGradient=function(a,b){return this},b.prototype.removeRampGradient=function(){return this},b.prototype.getRampGradients=function(){return null},Object.defineProperty(b.prototype,"useRampGradients",{get:function(){return!1},set:function(a){},enumerable:!1,configurable:!0}),b.prototype.addLifeTimeGradient=function(a,b,c){return this},b.prototype.removeLifeTimeGradient=function(a){return this},b.prototype._reset=function(){this._releaseBuffers()},b.prototype._createVertexBuffers=function(a,b,c){var d={};d.position=b.createVertexBuffer("position",0,3,this._attributesStrideSize,!0);var e=3;d.age=b.createVertexBuffer("age",e,1,this._attributesStrideSize,!0),e+=1,d.size=b.createVertexBuffer("size",e,3,this._attributesStrideSize,!0),e+=3,d.life=b.createVertexBuffer("life",e,1,this._attributesStrideSize,!0),e+=1,e+=4,this.billboardMode===Do.BILLBOARDMODE_STRETCHED&&(d.direction=b.createVertexBuffer("direction",e,3,this._attributesStrideSize,!0)),e+=3,this._platform.alignDataInBuffer&&(e+=1),this.particleEmitterType instanceof Cl&&(e+=3,this._platform.alignDataInBuffer&&(e+=1)),this._colorGradientsTexture||(d.color=b.createVertexBuffer("color",e,4,this._attributesStrideSize,!0),e+=4),this._isBillboardBased||(d.initialDirection=b.createVertexBuffer("initialDirection",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1)),this.noiseTexture&&(d.noiseCoordinates1=b.createVertexBuffer("noiseCoordinates1",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1),d.noiseCoordinates2=b.createVertexBuffer("noiseCoordinates2",e,3,this._attributesStrideSize,!0),e+=3,this._platform.alignDataInBuffer&&(e+=1)),d.angle=b.createVertexBuffer("angle",e,1,this._attributesStrideSize,!0),this._angularSpeedGradientsTexture?e++:e+=2,this._isAnimationSheetEnabled&&(d.cellIndex=b.createVertexBuffer("cellIndex",e,1,this._attributesStrideSize,!0),e+=1,this.spriteRandomStartCell&&(d.cellStartOffset=b.createVertexBuffer("cellStartOffset",e,1,this._attributesStrideSize,!0),e+=1)),d.offset=c.createVertexBuffer("offset",0,2),d.uv=c.createVertexBuffer("uv",2,2),this._platform.createVertexBuffers(a,d)},b.prototype._initialize=function(a){if(void 0===a&&(a=!1),!this._buffer0||a){a=this._engine;var b=new Array;this._attributesStrideSize=21,this._targetIndex=0,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1),this.particleEmitterType instanceof Cl&&(this._attributesStrideSize+=3,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1)),this.isBillboardBased||(this._attributesStrideSize+=3,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=1)),this._colorGradientsTexture&&(this._attributesStrideSize-=4),this._angularSpeedGradientsTexture&&(this._attributesStrideSize-=1),this._isAnimationSheetEnabled&&(this._attributesStrideSize+=1,this.spriteRandomStartCell&&(this._attributesStrideSize+=1)),this.noiseTexture&&(this._attributesStrideSize+=6,this._platform.alignDataInBuffer&&(this._attributesStrideSize+=2)),this._platform.alignDataInBuffer&&(this._attributesStrideSize+=3-(this._attributesStrideSize+3&3));for(var c=this.particleEmitterType instanceof Cl,d=g.c.Vector3[0],e=0,f=0;f0;)b.push(0)}h=new Float32Array([.5,.5,1,1,-.5,.5,0,1,.5,-.5,1,0,-.5,-.5,0,0]);c=this._platform.createParticleBuffer(b);e=this._platform.createParticleBuffer(b);this._buffer0=new W.a(a,c,!1,this._attributesStrideSize),this._buffer1=new W.a(a,e,!1,this._attributesStrideSize),this._spriteBuffer=new W.a(a,h,!1,4),this._createVertexBuffers(this._buffer0,this._buffer1,this._spriteBuffer),this._createVertexBuffers(this._buffer1,this._buffer0,this._spriteBuffer),this._sourceBuffer=this._buffer0,this._targetBuffer=this._buffer1}},b.prototype._recreateUpdateEffect=function(){var a=this.particleEmitterType?this.particleEmitterType.getEffectDefines():"";this._isBillboardBased&&(a+=" #define BILLBOARD"),this._colorGradientsTexture&&(a+=" #define COLORGRADIENTS"),this._sizeGradientsTexture&&(a+=" #define SIZEGRADIENTS"),this._angularSpeedGradientsTexture&&(a+=" #define ANGULARSPEEDGRADIENTS"),this._velocityGradientsTexture&&(a+=" #define VELOCITYGRADIENTS"),this._limitVelocityGradientsTexture&&(a+=" #define LIMITVELOCITYGRADIENTS"),this._dragGradientsTexture&&(a+=" #define DRAGGRADIENTS"),this.isAnimationSheetEnabled&&(a+=" #define ANIMATESHEET",this.spriteRandomStartCell&&(a+=" #define ANIMATESHEETRANDOMSTART")),this.noiseTexture&&(a+=" #define NOISE"),this.isLocal&&(a+=" #define LOCAL"),this._platform.isUpdateBufferCreated()&&this._cachedUpdateDefines===a||(this._cachedUpdateDefines=a,this._updateBuffer=this._platform.createUpdateBuffer(a))},b.prototype._getWrapper=function(a){var b=this._getCustomDrawWrapper(a);if(null==b?void 0:b.effect)return b;b=[];this.fillDefines(b,a);a=b.join(" ");if(this._drawWrapper.defines!==a){this._drawWrapper.defines=a;b=[];var c=[],d=[];this.fillUniformsAttributesAndSamplerNames(c,b,d),this._drawWrapper.effect=this._engine.createEffect("gpuRenderParticles",b,c,d,a)}return this._drawWrapper},b._GetAttributeNamesOrOptions=function(a,b,c,d){void 0===a&&(a=!1),void 0===b&&(b=!1),void 0===c&&(c=!1),void 0===d&&(d=!1);var e=[W.b.PositionKind,"age","life","size","angle"];return a||e.push(W.b.ColorKind),b&&e.push("cellIndex"),c||e.push("initialDirection"),d||e.push("direction"),e.push("offset",W.b.UVKind),e},b._GetEffectCreationOptions=function(a){void 0===a&&(a=!1);var b=["emitterWM","worldOffset","view","projection","colorDead","invView","vClipPlane","vClipPlane2","vClipPlane3","vClipPlane4","vClipPlane5","vClipPlane6","translationPivot","eyePosition"];return a&&b.push("sheetInfos"),b},b.prototype.fillDefines=function(a,b){if(void 0===b&&(b=0),this._scene&&(this._scene.clipPlane&&a.push("#define CLIPPLANE"),this._scene.clipPlane2&&a.push("#define CLIPPLANE2"),this._scene.clipPlane3&&a.push("#define CLIPPLANE3"),this._scene.clipPlane4&&a.push("#define CLIPPLANE4"),this._scene.clipPlane5&&a.push("#define CLIPPLANE5"),this._scene.clipPlane6&&a.push("#define CLIPPLANE6")),b===Do.BLENDMODE_MULTIPLY&&a.push("#define BLENDMULTIPLYMODE"),this.isLocal&&a.push("#define LOCAL"),this._isBillboardBased)switch(a.push("#define BILLBOARD"),this.billboardMode){case Do.BILLBOARDMODE_Y:a.push("#define BILLBOARDY");break;case Do.BILLBOARDMODE_STRETCHED:a.push("#define BILLBOARDSTRETCHED");break;case Do.BILLBOARDMODE_ALL:a.push("#define BILLBOARDMODE_ALL")}this._colorGradientsTexture&&a.push("#define COLORGRADIENTS"),this.isAnimationSheetEnabled&&a.push("#define ANIMATESHEET"),this._imageProcessingConfiguration&&(this._imageProcessingConfiguration.prepareDefines(this._imageProcessingConfigurationDefines),a.push(""+this._imageProcessingConfigurationDefines.toString()))},b.prototype.fillUniformsAttributesAndSamplerNames=function(a,c,d){c.push.apply(c,b._GetAttributeNamesOrOptions(!!this._colorGradientsTexture,this._isAnimationSheetEnabled,this._isBillboardBased,this._isBillboardBased&&this.billboardMode===Do.BILLBOARDMODE_STRETCHED)),a.push.apply(a,b._GetEffectCreationOptions(this._isAnimationSheetEnabled)),d.push("diffuseSampler","colorGradientSampler"),this._imageProcessingConfiguration&&(od.a.PrepareUniforms(a,this._imageProcessingConfigurationDefines),od.a.PrepareSamplers(d,this._imageProcessingConfigurationDefines))},b.prototype.animate=function(a){void 0===a&&(a=!1),this._timeDelta=this.updateSpeed*(a?this.preWarmStepOffset:(null===(a=this._scene)||void 0===a?void 0:a.getAnimationRatio())||1),this._actualFrame+=this._timeDelta,this._stopped||this.targetStopDuration&&this._actualFrame>=this.targetStopDuration&&this.stop()},b.prototype._createFactorGradientTexture=function(a,b){var c=this[b];if(a&&a.length&&!c){for(var d=new Float32Array(this._rawTextureWidth),e=0;e=0){b=d.clone();b.invert(),e.setMatrix("invView",b)}switch(this._imageProcessingConfiguration&&!this._imageProcessingConfiguration.applyByPostProcess&&this._imageProcessingConfiguration.bind(e),a){case Do.BLENDMODE_ADD:this._engine.setAlphaMode(r.a.ALPHA_ADD);break;case Do.BLENDMODE_ONEONE:this._engine.setAlphaMode(r.a.ALPHA_ONEONE);break;case Do.BLENDMODE_STANDARD:this._engine.setAlphaMode(r.a.ALPHA_COMBINE);break;case Do.BLENDMODE_MULTIPLY:this._engine.setAlphaMode(r.a.ALPHA_MULTIPLY)}return this._platform.bindDrawBuffers(this._targetIndex,e),this._onBeforeDrawParticlesObservable&&this._onBeforeDrawParticlesObservable.notifyObservers(e),this._engine.drawArraysType(r.a.MATERIAL_TriangleStripDrawMode,0,4,this._currentActiveCount),this._engine.setAlphaMode(r.a.ALPHA_DISABLE),this._currentActiveCount},b.prototype.render=function(a,b){if(void 0===a&&(a=!1),void 0===b&&(b=!1),!this._started)return 0;if(this._createColorGradientTexture(),this._createSizeGradientTexture(),this._createAngularSpeedGradientTexture(),this._createVelocityGradientTexture(),this._createLimitVelocityGradientTexture(),this._createDragGradientTexture(),this._recreateUpdateEffect(),!this.isReady())return 0;if(!a&&this._scene){if(!this._preWarmDone&&this.preWarmCycles){for(var c=0;c1){c=0|this._accumulatedCount;this._accumulatedCount-=c,this._currentActiveCount=Math.min(this._activeCount,this._currentActiveCount+c)}if(!this._currentActiveCount)return 0;if(this.emitter.position)c=this.emitter.getWorldMatrix();else{var d=this.emitter;c=g.a.Translation(d.x,d.y,d.z)}d=this._engine;this._platform.preUpdateParticleBuffer(),this._updateBuffer.setFloat("currentCount",this._currentActiveCount),this._updateBuffer.setFloat("timeDelta",this._timeDelta),this._updateBuffer.setFloat("stopFactor",this._stopped?0:1),this._updateBuffer.setInt("randomTextureSize",this._randomTextureSize),this._updateBuffer.setFloat2("lifeTime",this.minLifeTime,this.maxLifeTime),this._updateBuffer.setFloat2("emitPower",this.minEmitPower,this.maxEmitPower),this._colorGradientsTexture||(this._updateBuffer.setDirectColor4("color1",this.color1),this._updateBuffer.setDirectColor4("color2",this.color2)),this._updateBuffer.setFloat2("sizeRange",this.minSize,this.maxSize),this._updateBuffer.setFloat4("scaleRange",this.minScaleX,this.maxScaleX,this.minScaleY,this.maxScaleY),this._updateBuffer.setFloat4("angleRange",this.minAngularSpeed,this.maxAngularSpeed,this.minInitialRotation,this.maxInitialRotation),this._updateBuffer.setVector3("gravity",this.gravity),this._limitVelocityGradientsTexture&&this._updateBuffer.setFloat("limitVelocityDamping",this.limitVelocityDamping),this.particleEmitterType&&this.particleEmitterType.applyToShader(this._updateBuffer),this._isAnimationSheetEnabled&&this._updateBuffer.setFloat4("cellInfos",this.startSpriteCellID,this.endSpriteCellID,this.spriteCellChangeSpeed,this.spriteCellLoop?1:0),this.noiseTexture&&this._updateBuffer.setVector3("noiseStrength",this.noiseStrength),this.isLocal||this._updateBuffer.setMatrix("emitterWM",c),this._platform.updateParticleBuffer(this._targetIndex,this._targetBuffer,this._currentActiveCount);var e=0;a||b||(d.setState(!1),this.forceDepthWrite&&d.setDepthWrite(!0),e=this.blendMode===Do.BLENDMODE_MULTIPLYADD?this._render(Do.BLENDMODE_MULTIPLY,c)+this._render(Do.BLENDMODE_ADD,c):this._render(this.blendMode,c),this._engine.setAlphaMode(r.a.ALPHA_DISABLE)),this._targetIndex++,2===this._targetIndex&&(this._targetIndex=0);a=this._sourceBuffer;return this._sourceBuffer=this._targetBuffer,this._targetBuffer=a,e},b.prototype.rebuild=function(){this._initialize(!0)},b.prototype._releaseBuffers=function(){this._buffer0&&(this._buffer0.dispose(),this._buffer0=null),this._buffer1&&(this._buffer1.dispose(),this._buffer1=null),this._spriteBuffer&&(this._spriteBuffer.dispose(),this._spriteBuffer=null),this._platform.releaseBuffers()},b.prototype.dispose=function(a){if(void 0===a&&(a=!0),this._scene){var b=this._scene.particleSystems.indexOf(this);b>-1&&this._scene.particleSystems.splice(b,1)}this._releaseBuffers(),this._platform.releaseVertexBuffers(),this._colorGradientsTexture&&(this._colorGradientsTexture.dispose(),this._colorGradientsTexture=null),this._sizeGradientsTexture&&(this._sizeGradientsTexture.dispose(),this._sizeGradientsTexture=null),this._angularSpeedGradientsTexture&&(this._angularSpeedGradientsTexture.dispose(),this._angularSpeedGradientsTexture=null),this._velocityGradientsTexture&&(this._velocityGradientsTexture.dispose(),this._velocityGradientsTexture=null),this._limitVelocityGradientsTexture&&(this._limitVelocityGradientsTexture.dispose(),this._limitVelocityGradientsTexture=null),this._dragGradientsTexture&&(this._dragGradientsTexture.dispose(),this._dragGradientsTexture=null),this._randomTexture&&(this._randomTexture.dispose(),this._randomTexture=null),this._randomTexture2&&(this._randomTexture2.dispose(),this._randomTexture2=null),a&&this.particleTexture&&(this.particleTexture.dispose(),this.particleTexture=null),a&&this.noiseTexture&&(this.noiseTexture.dispose(),this.noiseTexture=null),this.onStoppedObservable.clear(),this.onDisposeObservable.notifyObservers(this),this.onDisposeObservable.clear()},b.prototype.clone=function(a,c){var d=Object(l.a)({},this._customWrappers),e=null,f=this._engine;if(f.createEffectForParticles&&null!=this.customShader){var g=(e=this.customShader).shaderOptions.defines.length>0?e.shaderOptions.defines.join(" "):"";d[0]=f.createEffectForParticles(e.shaderPath.fragmentElement,e.shaderOptions.uniforms,e.shaderOptions.samplers,g,void 0,void 0,void 0,this)}f=this.serialize();g=b.Parse(f,this._scene||this._engine,this._rootUrl);return g.name=a,g.customShader=e,g._customWrappers=d,void 0===c&&(c=this.emitter),this.noiseTexture&&(g.noiseTexture=this.noiseTexture.clone()),g.emitter=c,g},b.prototype.serialize=function(a){void 0===a&&(a=!1);var b={};return Do._Serialize(b,this,a),b.activeParticleCount=this.activeParticleCount,b.randomTextureSize=this._randomTextureSize,b.customShader=this.customShader,b},b.Parse=function(a,c,d,e,f){void 0===e&&(e=!1);var g,h=a.name;g=c instanceof qb.a?c:c.getEngine();h=new b(h,{capacity:f||a.capacity,randomTextureSize:a.randomTextureSize},c,null,a.isAnimationSheetEnabled);if(h._rootUrl=d,a.customShader&&g.createEffectForParticles){f=a.customShader;var i=f.shaderOptions.defines.length>0?f.shaderOptions.defines.join(" "):"";g=g.createEffectForParticles(f.shaderPath.fragmentElement,f.shaderOptions.uniforms,f.shaderOptions.samplers,i,void 0,void 0,void 0,h);h.setCustomEffect(g,0),h.customShader=f}return a.id&&(h.id=a.id),a.activeParticleCount&&(h.activeParticleCount=a.activeParticleCount),Do._Parse(a,h,c,d),a.preventAutoStart&&(h.preventAutoStart=a.preventAutoStart),e||h.preventAutoStart||h.start(),h},b}(El),Fo=function(){function a(){this._emitterNodeIsOwned=!0,this.systems=new Array}return Object.defineProperty(a.prototype,"emitterNode",{get:function(){return this._emitterNode},set:function(a){this._emitterNodeIsOwned&&this._emitterNode&&(this._emitterNode.dispose&&this._emitterNode.dispose(),this._emitterNodeIsOwned=!1);for(var b=0,c=this.systems;b0&&a.set(this._uvs32,W.b.UVKind),this._colors32.length>0&&a.set(this._colors32,W.b.ColorKind),a.applyToMesh(this.mesh,this._updatable),this.mesh.isPickable=this._pickable,this._pickable)for(var a=0,b=0;bo?o:d,c=Math.round(o/d),e=0):c=c>o?o:c;for(var d=[],z=[],A=[],C=[],D=[],E=g.e.Zero(),F=c;lo-(c=F+Math.floor((1+e)*Math.random()))&&(c=o-l),d.length=0,z.length=0,A.length=0,C.length=0,D.length=0;for(var K=0,ba=3*l;ba<3*(l+c);ba++){A.push(K);var L=h[ba],M=3*L;if(d.push(f[M],f[M+1],f[M+2]),z.push(k[M],k[M+1],k[M+2]),i){M=2*L;C.push(i[M],i[M+1])}if(j){M=4*L;D.push(j[M],j[M+1],j[M+2],j[M+3])}K++}var N;L=this.nbParticles;M=this._posToShape(d);ba=this._uvsToShapeUV(C);K=T.b.Slice(A);var ca=T.b.Slice(D),da=T.b.Slice(z);for(E.copyFromFloats(0,0,0),N=0;N65535&&(this._needs32Bits=!0)}if(this._depthSort||this._multimaterialEnabled){D=null!==L.materialIndex?L.materialIndex:0;this.depthSortedParticles.push(new Ko(A,b,e.length,D))}return L},a.prototype._posToShape=function(a){for(var b=[],c=0;c=this.nbParticles||!this._updatable)return[];var d=this.particles,e=this.nbParticles;if(b=this.nbParticles?this.nbParticles-1:b,this._computeBoundingBox&&(0!=a||b!=this.nbParticles-1)){var Aa=this.mesh.getBoundingInfo();Aa&&(ha.copyFrom(Aa.minimum),O.copyFrom(Aa.maximum))}Aa=(e=this.particles[a]._pos)/3|0;ua=4*Aa,wa=2*Aa;for(Aa=a;Aa<=b;Aa++){var Ba=this.particles[Aa];this.updateParticle(Ba);var Ca=Ba._model._shape,Da=Ba._model._shapeUV,Ea=Ba._rotationMatrix,R=Ba.position,Fa=Ba.rotation,Ga=Ba.scaling,Ha=Ba._globalPosition;if(this._depthSort&&this._depthSortParticles){var S=this.depthSortedParticles[Aa];S.idx=Ba.idx,S.ind=Ba._ind,S.indicesLength=Ba._model._indicesLength,S.sqDistance=g.e.DistanceSquared(Ba.position,la)}if(!Ba.alive||Ba._stillInvisible&&!Ba.isVisible)e+=3*(xa=Ca.length),ua+=4*xa,wa+=2*xa;else{if(Ba.isVisible){Ba._stillInvisible=!1;S=C[12];if(Ba.pivot.multiplyToRef(Ga,S),this.billboard&&(Fa.x=0,Fa.y=0),(this._computeParticleRotation||this.billboard)&&Ba.getRotationMatrix(d),null!==Ba.parentId){Fa=this.getParticleById(Ba.parentId);if(Fa){var T=Fa._rotationMatrix;Fa=Fa._globalPosition;var Ia=R.x*T[1]+R.y*T[4]+R.z*T[7],Ja=R.x*T[0]+R.y*T[3]+R.z*T[6],Ka=R.x*T[2]+R.y*T[5]+R.z*T[8];if(Ha.x=Fa.x+Ja,Ha.y=Fa.y+Ia,Ha.z=Fa.z+Ka,this._computeParticleRotation||this.billboard){Fa=d.m;Ea[0]=Fa[0]*T[0]+Fa[1]*T[3]+Fa[2]*T[6],Ea[1]=Fa[0]*T[1]+Fa[1]*T[4]+Fa[2]*T[7],Ea[2]=Fa[0]*T[2]+Fa[1]*T[5]+Fa[2]*T[8],Ea[3]=Fa[4]*T[0]+Fa[5]*T[3]+Fa[6]*T[6],Ea[4]=Fa[4]*T[1]+Fa[5]*T[4]+Fa[6]*T[7],Ea[5]=Fa[4]*T[2]+Fa[5]*T[5]+Fa[6]*T[8],Ea[6]=Fa[8]*T[0]+Fa[9]*T[3]+Fa[10]*T[6],Ea[7]=Fa[8]*T[1]+Fa[9]*T[4]+Fa[10]*T[7],Ea[8]=Fa[8]*T[2]+Fa[9]*T[5]+Fa[10]*T[8]}}else Ba.parentId=null}else(Ha.x=R.x,Ha.y=R.y,Ha.z=R.z,this._computeParticleRotation||this.billboard)&&(Fa=d.m,(Ea[0]=Fa[0],Ea[1]=Fa[1],Ea[2]=Fa[2],Ea[3]=Fa[4],Ea[4]=Fa[5],Ea[5]=Fa[6],Ea[6]=Fa[8],Ea[7]=Fa[9],Ea[8]=Fa[10]));T=C[11];for(Ba.translateFromPivot?T.setAll(0):T.copyFrom(S),xa=0;xa0)for(var b=0;b0&&b.set(this._uvs32,W.b.UVKind);var c=0;this._colors32.length>0&&(c=1,b.set(this._colors32,W.b.ColorKind));var d=new R.a(this.name,this._scene);b.applyToMesh(d,this._updatable),this.mesh=d,this._positions=null,this._uvs=null,this._colors=null,this._updatable||(this.particles.length=0);b=a;return b||((b=new pd.a("point cloud material",this._scene)).emissiveColor=new h.a(c,c,c),b.disableLighting=!0,b.pointsCloud=!0,b.pointSize=this._size),d.material=b,new Promise(function(a){return a(d)})},a.prototype._addParticle=function(a,b,c,d){a=new No(a,b,c,d,this);return this.particles.push(a),a},a.prototype._randomUnitVector=function(a){a.position=new g.e(Math.random(),Math.random(),Math.random()),a.color=new h.b(1,1,1,1)},a.prototype._getColorIndicesForCoord=function(a,b,c,d){a=a._groupImageData;c=c*(4*d)+4*b;d=[c,c+1,c+2,c+3];b=d[1];c=d[2];var e=d[3];d=a[d[0]];b=a[b];c=a[c];a=a[e];return new h.b(d/255,b/255,c/255,a)},a.prototype._setPointsColorOrUV=function(a,b,c,d,e,f,i){c&&a.updateFacetData();var j=2*a.getBoundingInfo().boundingSphere.radius,k=a.getVerticesData(W.b.PositionKind),l=a.getIndices(),s=a.getVerticesData(W.b.UVKind),t=a.getVerticesData(W.b.ColorKind),u=g.e.Zero();a.computeWorldMatrix();var v=a.getWorldMatrix();if(!v.isIdentity()){k=k.slice(0);for(var L=0;L1&&(M=1),(sb=sb.b+Cb)<0&&(sb=0),sb>1&&(sb=1),h.a.HSVtoRGBToRef(Db,M,sb,Q),tb.set(Q.r,Q.g,Q.b,1)):tb=nb.set(Math.random(),Math.random(),Math.random(),1),eb.color=new h.b(tb.x,tb.y,tb.z,tb.w),this._colors.push(tb.x,tb.y,tb.z,tb.w))}},a.prototype._colorFromTexture=function(a,b,c){var d=this;if(null===a.material)return q.a.Warn(a.name+"has no material."),b._groupImageData=null,void this._setPointsColorOrUV(a,b,c,!0,!1);var e=a.material.getActiveTextures();if(0===e.length)return q.a.Warn(a.name+"has no usable texture."),b._groupImageData=null,void this._setPointsColorOrUV(a,b,c,!0,!1);var f=a.clone();f.setEnabled(!1),this._promises.push(new Promise(function(a){Ie.a.WhenAllReady(e,function(){var g=b._textureNb;g<0&&(g=0),g>e.length-1&&(g=e.length-1);var h=function(){b._groupImgWidth=e[g].getSize().width,b._groupImgHeight=e[g].getSize().height,d._setPointsColorOrUV(f,b,c,!0,!0),f.dispose(),a()};b._groupImageData=null;var i=e[g].readPixels();i?i.then(function(a){b._groupImageData=a,h()}):h()})}))},a.prototype._calculateDensity=function(a,b,c){for(var d,e,f,h,i,d,j,k,e,l,m,f,h,i,d,j,k,p=new Array,q=g.e.Zero(),r=g.e.Zero(),s=g.e.Zero(),t=g.e.Zero(),u=g.e.Zero(),aa=g.e.Zero(),G=new Array,I=0,J=c.length/3,K=0;K0&&(p=p.map(function(a){return a+ba}));for(K=0;K3)&&(c=Ho.Random);var f=a.getVerticesData(W.b.PositionKind),g=a.getIndices();this._groups.push(this._groupCounter);var i=new Oo(this._groupCounter,null);switch(i._groupDensity=this._calculateDensity(b,f,g),c===Ho.Color?i._textureNb=d||0:d=d||new h.b(1,1,1,1),c){case Ho.Color:this._colorFromTexture(a,i,!1);break;case Ho.UV:this._setPointsColorOrUV(a,i,!1,!1,!1);break;case Ho.Random:this._setPointsColorOrUV(a,i,!1);break;case Ho.Stated:this._setPointsColorOrUV(a,i,!1,void 0,void 0,d,e)}return this.nbParticles+=b,this._groupCounter++,this._groupCounter-1},a.prototype.addVolumePoints=function(a,b,c,d,e){c=c||Ho.Random;(isNaN(c)||c<0||c>3)&&(c=Ho.Random);var f=a.getVerticesData(W.b.PositionKind),g=a.getIndices();this._groups.push(this._groupCounter);var i=new Oo(this._groupCounter,null);switch(i._groupDensity=this._calculateDensity(b,f,g),c===Ho.Color?i._textureNb=d||0:d=d||new h.b(1,1,1,1),c){case Ho.Color:this._colorFromTexture(a,i,!0);break;case Ho.UV:this._setPointsColorOrUV(a,i,!0,!1,!1);break;case Ho.Random:this._setPointsColorOrUV(a,i,!0);break;case Ho.Stated:this._setPointsColorOrUV(a,i,!0,void 0,void 0,d,e)}return this.nbParticles+=b,this._groupCounter++,this._groupCounter-1},a.prototype.setParticles=function(a,b,c){if(void 0===a&&(a=0),void 0===b&&(b=this.nbParticles-1),void 0===c&&(c=!0),!this._updatable||!this._isReady)return this;this.beforeUpdateParticles(a,b,c);var d=g.c.Matrix[0],e=this.mesh,f=this._colors32,h=this._positions32,i=this._uvs32,j=g.c.Vector3,k=j[5].copyFromFloats(1,0,0),l=j[6].copyFromFloats(0,1,0),r=j[7].copyFromFloats(0,0,1),w=j[8].setAll(Number.MAX_VALUE),B=j[9].setAll(-Number.MAX_VALUE);g.a.IdentityToRef(d);var E;if(this.mesh.isFacetDataEnabled&&(this._computeBoundingBox=!0),b=b>=this.nbParticles?this.nbParticles-1:b,this._computeBoundingBox&&(0!=a||b!=this.nbParticles-1)){var F=this.mesh.getBoundingInfo();F&&(w.copyFrom(F.minimum),B.copyFrom(F.maximum))}for(var F=0,aa=0,G=0,H=a;H<=b;H++){var I=this.particles[H];F=3*(E=I.idx),aa=4*E,G=2*E,this.updateParticle(I);E=I._rotationMatrix;var J=I.position,K=I._globalPosition;if(this._computeParticleRotation&&I.getRotationMatrix(d),null!==I.parentId){var L=this.particles[I.parentId],M=L._rotationMatrix;L=L._globalPosition;var N=J.x*M[1]+J.y*M[4]+J.z*M[7],ca=J.x*M[0]+J.y*M[3]+J.z*M[6];J=J.x*M[2]+J.y*M[5]+J.z*M[8];if(K.x=L.x+ca,K.y=L.y+N,K.z=L.z+J,this._computeParticleRotation){ca=d.m;E[0]=ca[0]*M[0]+ca[1]*M[3]+ca[2]*M[6],E[1]=ca[0]*M[1]+ca[1]*M[4]+ca[2]*M[7],E[2]=ca[0]*M[2]+ca[1]*M[5]+ca[2]*M[8],E[3]=ca[4]*M[0]+ca[5]*M[3]+ca[6]*M[6],E[4]=ca[4]*M[1]+ca[5]*M[4]+ca[6]*M[7],E[5]=ca[4]*M[2]+ca[5]*M[5]+ca[6]*M[8],E[6]=ca[8]*M[0]+ca[9]*M[3]+ca[10]*M[6],E[7]=ca[8]*M[1]+ca[9]*M[4]+ca[10]*M[7],E[8]=ca[8]*M[2]+ca[9]*M[5]+ca[10]*M[8]}}else(K.x=0,K.y=0,K.z=0,this._computeParticleRotation)&&(ca=d.m,(E[0]=ca[0],E[1]=ca[1],E[2]=ca[2],E[3]=ca[4],E[4]=ca[5],E[5]=ca[6],E[6]=ca[8],E[7]=ca[9],E[8]=ca[10]));N=j[11];I.translateFromPivot?N.setAll(0):N.copyFrom(I.pivot);L=j[0];L.copyFrom(I.position);J=L.x-I.pivot.x;M=L.y-I.pivot.y;ca=L.z-I.pivot.z;L=J*E[0]+M*E[3]+ca*E[6];var da=J*E[1]+M*E[4]+ca*E[7];J=J*E[2]+M*E[5]+ca*E[8];L+=N.x,da+=N.y,J+=N.z;M=h[F]=K.x+k.x*L+l.x*da+r.x*J;ca=h[F+1]=K.y+k.y*L+l.y*da+r.y*J;E=h[F+2]=K.z+k.z*L+l.z*da+r.z*J;if(this._computeBoundingBox&&(w.minimizeInPlaceFromFloats(M,ca,E),B.maximizeInPlaceFromFloats(M,ca,E)),this._computeParticleColor&&I.color){N=I.color;K=this._colors32;K[aa]=N.r,K[aa+1]=N.g,K[aa+2]=N.b,K[aa+3]=N.a}if(this._computeParticleTexture&&I.uv){L=I.uv;da=this._uvs32;da[G]=L.x,da[G+1]=L.y}}return c&&(this._computeParticleColor&&e.updateVerticesData(W.b.ColorKind,f,!1,!1),this._computeParticleTexture&&e.updateVerticesData(W.b.UVKind,i,!1,!1),e.updateVerticesData(W.b.PositionKind,h,!1,!1)),this._computeBoundingBox&&(e.hasBoundingInfo?e.getBoundingInfo().reConstruct(w,B,e._worldMatrix):e.buildBoundingInfo(w,B,e._worldMatrix)),this.afterUpdateParticles(a,b,c),this},a.prototype.dispose=function(){this.mesh.dispose(),this.vars=null,this._positions=null,this._indices=null,this._normals=null,this._uvs=null,this._colors=null,this._indices32=null,this._positions32=null,this._uvs32=null,this._colors32=null},a.prototype.refreshVisibleSize=function(){return this._isVisibilityBoxLocked||this.mesh.refreshBoundingInfo(),this},a.prototype.setVisibilityBox=function(a){a=a/2;this.mesh.buildBoundingInfo(new g.e(-a,-a,-a),new g.e(a,a,a))},Object.defineProperty(a.prototype,"isAlwaysVisible",{get:function(){return this._alwaysVisible},set:function(a){this._alwaysVisible=a,this.mesh.alwaysSelectAsActiveMesh=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleRotation",{set:function(a){this._computeParticleRotation=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleColor",{get:function(){return this._computeParticleColor},set:function(a){this._computeParticleColor=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeParticleTexture",{get:function(){return this._computeParticleTexture},set:function(a){this._computeParticleTexture=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"computeBoundingBox",{get:function(){return this._computeBoundingBox},set:function(a){this._computeBoundingBox=a},enumerable:!1,configurable:!0}),a.prototype.initParticles=function(){},a.prototype.recycleParticle=function(a){return a},a.prototype.updateParticle=function(a){return a},a.prototype.beforeUpdateParticles=function(a,b,c){},a.prototype.afterUpdateParticles=function(a,b,c){},a}();O.a.prototype.getPhysicsEngine=function(){return this._physicsEngine},O.a.prototype.enablePhysics=function(a,b){if(void 0===a&&(a=null),this._physicsEngine)return!0;var c=this._getComponent(Oa.a.NAME_PHYSICSENGINE);c||(c=new So(this),this._addComponent(c));try{return this._physicsEngine=new Vj(a,b),this._physicsTimeAccumulator=0,!0}catch(a){return q.a.Error(a.message),!1}},O.a.prototype.disablePhysicsEngine=function(){this._physicsEngine&&(this._physicsEngine.dispose(),this._physicsEngine=null)},O.a.prototype.isPhysicsEnabled=function(){return void 0!==this._physicsEngine},O.a.prototype.deleteCompoundImpostor=function(a){a=a.parts[0].mesh;a.physicsImpostor&&(a.physicsImpostor.dispose(),a.physicsImpostor=null)},O.a.prototype._advancePhysicsEngineStep=function(a){if(this._physicsEngine){var b=this._physicsEngine.getSubTimeStep();if(b>0)for(this._physicsTimeAccumulator+=a;this._physicsTimeAccumulator>b;)this.onBeforePhysicsObservable.notifyObservers(this),this._physicsEngine._step(b/1e3),this.onAfterPhysicsObservable.notifyObservers(this),this._physicsTimeAccumulator-=b;else this.onBeforePhysicsObservable.notifyObservers(this),this._physicsEngine._step(a/1e3),this.onAfterPhysicsObservable.notifyObservers(this)}},Object.defineProperty(cb.a.prototype,"physicsImpostor",{get:function(){return this._physicsImpostor},set:function(a){var b=this;this._physicsImpostor!==a&&(this._disposePhysicsObserver&&this.onDisposeObservable.remove(this._disposePhysicsObserver),this._physicsImpostor=a,a&&(this._disposePhysicsObserver=this.onDisposeObservable.add(function(){b.physicsImpostor&&(b.physicsImpostor.dispose(),b.physicsImpostor=null)})))},enumerable:!0,configurable:!0}),cb.a.prototype.getPhysicsImpostor=function(){return this.physicsImpostor},cb.a.prototype.applyImpulse=function(a,b){return this.physicsImpostor?(this.physicsImpostor.applyImpulse(a,b),this):this},cb.a.prototype.setPhysicsLinkWith=function(a,b,c,d){return this.physicsImpostor&&a.physicsImpostor?(this.physicsImpostor.createJoint(a.physicsImpostor,Uj.e.HingeJoint,{mainPivot:b,connectedPivot:c,nativeParams:d}),this):this};var Qo,Ro,So=function(){function a(a){var b=this;this.name=Oa.a.NAME_PHYSICSENGINE,this.scene=a,this.scene.onBeforePhysicsObservable=new f.c,this.scene.onAfterPhysicsObservable=new f.c,this.scene.getDeterministicFrameTime=function(){return b.scene._physicsEngine?1e3*b.scene._physicsEngine.getTimeStep():1e3/60}}return a.prototype.register=function(){},a.prototype.rebuild=function(){},a.prototype.dispose=function(){this.scene.onBeforePhysicsObservable.clear(),this.scene.onAfterPhysicsObservable.clear(),this.scene._physicsEngine&&this.scene.disablePhysicsEngine()},a}(),To=function(){function a(a){this._scene=a,this._physicsEngine=this._scene.getPhysicsEngine(),this._physicsEngine||q.a.Warn("Physics engine not enabled. Please enable the physics before you can use the methods.")}return a.prototype.applyRadialExplosionImpulse=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call this method."),null;var e=this._physicsEngine.getImpostors();if(0===e.length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);var f=new Uo(this._scene,b),g=Array();return e.forEach(function(b){var c=f.getImpostorHitData(b,a);c&&(b.applyImpulse(c.force,c.contactPoint),g.push({impostor:b,hitData:c}))}),f.triggerAffectedImpostorsCallback(g),f.dispose(!1),f},a.prototype.applyRadialExplosionForce=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;var e=this._physicsEngine.getImpostors();if(0===e.length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);var f=new Uo(this._scene,b),g=Array();return e.forEach(function(b){var c=f.getImpostorHitData(b,a);c&&(b.applyForce(c.force,c.contactPoint),g.push({impostor:b,hitData:c}))}),f.triggerAffectedImpostorsCallback(g),f.dispose(!1),f},a.prototype.gravitationalField=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new Yo).radius=b,b.strength=c||b.strength,b.falloff=d||b.falloff);c=new Vo(this,this._scene,a,b);return c.dispose(!1),c},a.prototype.updraft=function(a,b,c,d,e){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new Zo).radius=b,b.strength=c||b.strength,b.height=d||b.height,b.updraftMode=e||b.updraftMode);c=new Wo(this._scene,a,b);return c.dispose(!1),c},a.prototype.vortex=function(a,b,c,d){if(!this._physicsEngine)return q.a.Warn("Physics engine not enabled. Please enable the physics before you call the PhysicsHelper."),null;if(0===this._physicsEngine.getImpostors().length)return null;"number"==typeof b&&((b=new $o).radius=b,b.strength=c||b.strength,b.height=d||b.height);c=new Xo(this._scene,a,b);return c.dispose(!1),c},a}(),Uo=function(){function a(a,b){this._scene=a,this._options=b,this._dataFetched=!1,this._options=Object(l.a)(Object(l.a)({},new Yo),this._options)}return a.prototype.getData=function(){return this._dataFetched=!0,{sphere:this._sphere}},a.prototype.getImpostorHitData=function(a,b){if(0===a.mass)return null;if(!this._intersectsWithSphere(a,b,this._options.radius))return null;if("Mesh"!==a.object.getClassName()&&"InstancedMesh"!==a.object.getClassName())return null;var c=a.getObjectCenter().subtract(b);a=new nc.a(b,c,this._options.radius).intersectsMesh(a.object).pickedPoint;if(!a)return null;b=g.e.Distance(b,a);if(b>this._options.radius)return null;var d=this._options.falloff===Qo.Constant?this._options.strength:this._options.strength*(1-b/this._options.radius);return{force:c.multiplyByFloats(d,d,d),contactPoint:a,distanceFromOrigin:b}},a.prototype.triggerAffectedImpostorsCallback=function(a){this._options.affectedImpostorsCallback&&this._options.affectedImpostorsCallback(a)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._sphere.dispose():setTimeout(function(){b._dataFetched||b._sphere.dispose()},0)},a.prototype._prepareSphere=function(){this._sphere||(this._sphere=Object(vh.a)("radialExplosionEventSphere",this._options.sphere,this._scene),this._sphere.isVisible=!1)},a.prototype._intersectsWithSphere=function(a,b,c){a=a.object;return this._prepareSphere(),this._sphere.position=b,this._sphere.scaling=new g.e(2*c,2*c,2*c),this._sphere._updateBoundingInfo(),this._sphere.computeWorldMatrix(!0),this._sphere.intersectsMesh(a,!0)},a}(),Vo=function(){function a(a,b,c,d){this._physicsHelper=a,this._scene=b,this._origin=c,this._options=d,this._dataFetched=!1,this._options=Object(l.a)(Object(l.a)({},new Yo),this._options),this._tickCallback=this._tick.bind(this),this._options.strength=-1*this._options.strength}return a.prototype.getData=function(){return this._dataFetched=!0,{sphere:this._sphere}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._sphere.dispose():setTimeout(function(){b._dataFetched||b._sphere.dispose()},0)},a.prototype._tick=function(){if(this._sphere)this._physicsHelper.applyRadialExplosionForce(this._origin,this._options);else{var a=this._physicsHelper.applyRadialExplosionForce(this._origin,this._options);a&&(this._sphere=a.getData().sphere.clone("radialExplosionEventSphereClone"))}},a}(),Wo=function(){function a(a,b,c){this._scene=a,this._origin=b,this._options=c,this._originTop=g.e.Zero(),this._originDirection=g.e.Zero(),this._cylinderPosition=g.e.Zero(),this._dataFetched=!1,this._physicsEngine=this._scene.getPhysicsEngine(),this._options=Object(l.a)(Object(l.a)({},new Zo),this._options),this._origin.addToRef(new g.e(0,this._options.height/2,0),this._cylinderPosition),this._origin.addToRef(new g.e(0,this._options.height,0),this._originTop),this._options.updraftMode===Ro.Perpendicular&&(this._originDirection=this._origin.subtract(this._originTop).normalize()),this._tickCallback=this._tick.bind(this),this._prepareCylinder()}return a.prototype.getData=function(){return this._dataFetched=!0,{cylinder:this._cylinder}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),this._cylinder&&(a?this._cylinder.dispose():setTimeout(function(){b._dataFetched||b._cylinder.dispose()},0))},a.prototype.getImpostorHitData=function(a){if(0===a.mass)return null;if(!this._intersectsWithCylinder(a))return null;a=a.getObjectCenter();if(this._options.updraftMode===Ro.Perpendicular)var b=this._originDirection;else b=a.subtract(this._originTop);var c=g.e.Distance(this._origin,a),d=-1*this._options.strength;return{force:b.multiplyByFloats(d,d,d),contactPoint:a,distanceFromOrigin:c}},a.prototype._tick=function(){var a=this;this._physicsEngine.getImpostors().forEach(function(b){var c=a.getImpostorHitData(b);c&&b.applyForce(c.force,c.contactPoint)})},a.prototype._prepareCylinder=function(){this._cylinder||(this._cylinder=Object(xd.a)("updraftEventCylinder",{height:this._options.height,diameter:2*this._options.radius},this._scene),this._cylinder.isVisible=!1)},a.prototype._intersectsWithCylinder=function(a){a=a.object;return this._cylinder.position=this._cylinderPosition,this._cylinder.intersectsMesh(a,!0)},a}(),Xo=function(){function a(a,b,c){this._scene=a,this._origin=b,this._options=c,this._originTop=g.e.Zero(),this._cylinderPosition=g.e.Zero(),this._dataFetched=!1,this._physicsEngine=this._scene.getPhysicsEngine(),this._options=Object(l.a)(Object(l.a)({},new $o),this._options),this._origin.addToRef(new g.e(0,this._options.height/2,0),this._cylinderPosition),this._origin.addToRef(new g.e(0,this._options.height,0),this._originTop),this._tickCallback=this._tick.bind(this),this._prepareCylinder()}return a.prototype.getData=function(){return this._dataFetched=!0,{cylinder:this._cylinder}},a.prototype.enable=function(){this._tickCallback.call(this),this._scene.registerBeforeRender(this._tickCallback)},a.prototype.disable=function(){this._scene.unregisterBeforeRender(this._tickCallback)},a.prototype.dispose=function(a){var b=this;void 0===a&&(a=!0),a?this._cylinder.dispose():setTimeout(function(){b._dataFetched||b._cylinder.dispose()},0)},a.prototype.getImpostorHitData=function(a){if(0===a.mass)return null;if(!this._intersectsWithCylinder(a))return null;if("Mesh"!==a.object.getClassName()&&"InstancedMesh"!==a.object.getClassName())return null;var b=a.getObjectCenter(),c=new g.e(this._origin.x,b.y,this._origin.z),d=b.subtract(c);d=new nc.a(c,d,this._options.radius).intersectsMesh(a.object);a=d.pickedPoint;if(!a)return null;d=d.distance/this._options.radius;a=a.normalize();if(d>this._options.centripetalForceThreshold&&(a=a.negate()),d>this._options.centripetalForceThreshold)var e=a.x*this._options.centripetalForceMultiplier,f=a.y*this._options.updraftForceMultiplier,h=a.z*this._options.centripetalForceMultiplier;else{c=g.e.Cross(c,b).normalize();e=(c.x+a.x)*this._options.centrifugalForceMultiplier,f=this._originTop.y*this._options.updraftForceMultiplier,h=(c.z+a.z)*this._options.centrifugalForceMultiplier}c=new g.e(e,f,h);return{force:c.multiplyByFloats(this._options.strength,this._options.strength,this._options.strength),contactPoint:b,distanceFromOrigin:d}},a.prototype._tick=function(){var a=this;this._physicsEngine.getImpostors().forEach(function(b){var c=a.getImpostorHitData(b);c&&b.applyForce(c.force,c.contactPoint)})},a.prototype._prepareCylinder=function(){this._cylinder||(this._cylinder=Object(xd.a)("vortexEventCylinder",{height:this._options.height,diameter:2*this._options.radius},this._scene),this._cylinder.isVisible=!1)},a.prototype._intersectsWithCylinder=function(a){a=a.object;return this._cylinder.position=this._cylinderPosition,this._cylinder.intersectsMesh(a,!0)},a}(),Yo=function(){this.radius=5,this.strength=10,this.falloff=Qo.Constant,this.sphere={segments:32,diameter:1}},Zo=function(){this.radius=5,this.strength=10,this.height=10,this.updraftMode=Ro.Center},$o=function(){this.radius=5,this.strength=10,this.height=10,this.centripetalForceThreshold=.7,this.centripetalForceMultiplier=5,this.centrifugalForceMultiplier=.5,this.updraftForceMultiplier=.02};!function(a){a[a.Constant=0]="Constant",a[a.Linear=1]="Linear"}(Qo||(Qo={})),(function(a){a[a.Center=0]="Center",a[a.Perpendicular=1]="Perpendicular"})(Ro||(Ro={}));a=" varying vec2 vUV; uniform sampler2D textureSampler; uniform float degree; void main(void) { vec3 color=texture2D(textureSampler,vUV).rgb; float luminance=dot(color,vec3(0.3,0.59,0.11)); vec3 blackAndWhite=vec3(luminance,luminance,luminance); gl_FragColor=vec4(color-((color-blackAndWhite)*degree),1.0); }";X.a.ShadersStore.blackAndWhitePixelShader=a;var ap=function(a){function b(b,c,d,e,f,g){var h=a.call(this,b,"blackAndWhite",["degree"],null,c,d,e,f,g)||this;return h.degree=1,h.onApplyObservable.add(function(a){a.setFloat("degree",h.degree)}),h}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"BlackAndWhitePostProcess"},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},Object(l.c)([Object(I.d)()],b.prototype,"degree",void 0),b}(Fc);Object(i.b)("BABYLON.BlackAndWhitePostProcess",ap);var bp=function(){function a(a,b,c,d){this._name=b,this._singleInstance=d||!0,this._getPostProcesses=c,this._cameras={},this._indicesForCamera={},this._postProcesses={}}return Object.defineProperty(a.prototype,"isSupported",{get:function(){for(var a in this._postProcesses)if(this._postProcesses.hasOwnProperty(a))for(var b=this._postProcesses[a],c=0;c-1?"#define MALI 1 ":null},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},b}(Fc);Object(i.b)("BABYLON.FxaaPostProcess",qp);a="#include uniform sampler2D textureSampler; uniform float intensity; uniform float animatedSeed; varying vec2 vUV; void main(void) { gl_FragColor=texture2D(textureSampler,vUV); vec2 seed=vUV*(animatedSeed); float grain=dither(seed,intensity); float lum=getLuminance(gl_FragColor.rgb); float grainAmount=(cos(-PI+(lum*PI*2.))+1.)/2.; gl_FragColor.rgb+=grain*grainAmount; gl_FragColor.rgb=max(gl_FragColor.rgb,0.0); }";X.a.ShadersStore.grainPixelShader=a;var rp=function(a){function b(b,c,d,e,f,g,h,i){void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),void 0===i&&(i=!1);var j=a.call(this,b,"grain",["intensity","animatedSeed"],[],c,d,e,f,g,null,h,void 0,null,i)||this;return j.intensity=30,j.animated=!1,j.onApplyObservable.add(function(a){a.setFloat("intensity",j.intensity),a.setFloat("animatedSeed",j.animated?Math.random()+1:1)}),j}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"GrainPostProcess"},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.reusable)},a,d,e)},Object(l.c)([Object(I.d)()],b.prototype,"intensity",void 0),Object(l.c)([Object(I.d)()],b.prototype,"animated",void 0),b}(Fc);Object(i.b)("BABYLON.GrainPostProcess",rp);wi=" varying vec2 vUV; uniform sampler2D textureSampler; const vec3 RGBLuminanceCoefficients=vec3(0.2126,0.7152,0.0722); void main(void) { vec4 tex=texture2D(textureSampler,vUV); vec3 c=tex.rgb; float luma=dot(c.rgb,RGBLuminanceCoefficients); gl_FragColor=vec4(pow(c,vec3(25.0-luma*15.0)),tex.a); }";X.a.ShadersStore.highlightsPixelShader=wi;var sp=function(a){function b(b,c,d,e,f,g,h){return void 0===h&&(h=r.a.TEXTURETYPE_UNSIGNED_INT),a.call(this,b,"highlights",null,null,c,d,e,f,g,null,h)||this}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"HighlightsPostProcess"},b}(Fc);V="#if defined(WEBGL2) || defined(WEBGPU) layout(location=0) out vec4 glFragData[{X}]; #endif ";X.a.IncludesShadersStore.mrtFragmentDeclaration=V;a="#extension GL_EXT_draw_buffers : require #if defined(BUMP) || !defined(NORMAL) #extension GL_OES_standard_derivatives : enable #endif precision highp float; #ifdef BUMP varying mat4 vWorldView; varying vec3 vNormalW; #else varying vec3 vNormalV; #endif varying vec4 vViewPos; #if defined(POSITION) || defined(BUMP) varying vec3 vPositionW; #endif #ifdef VELOCITY varying vec4 vCurrentPosition; varying vec4 vPreviousPosition; #endif #ifdef NEED_UV varying vec2 vUV; #endif #ifdef BUMP uniform vec3 vBumpInfos; uniform vec2 vTangentSpaceParams; #endif #if defined(REFLECTIVITY) && (defined(HAS_SPECULAR) || defined(HAS_REFLECTIVITY)) varying vec2 vReflectivityUV; uniform sampler2D reflectivitySampler; #endif #ifdef ALPHATEST uniform sampler2D diffuseSampler; #endif #include[RENDER_TARGET_COUNT] #include #include void main() { #ifdef ALPHATEST if (texture2D(diffuseSampler,vUV).a<0.4) discard; #endif vec3 normalOutput; #ifdef BUMP vec3 normalW=normalize(vNormalW); #include normalOutput=normalize(vec3(vWorldView*vec4(normalW,0.0))); #else normalOutput=normalize(vNormalV); #endif #ifdef PREPASS #ifdef PREPASS_DEPTH gl_FragData[DEPTH_INDEX]=vec4(vViewPos.z/vViewPos.w,0.0,0.0,1.0); #endif #ifdef PREPASS_NORMAL gl_FragData[NORMAL_INDEX]=vec4(normalOutput,1.0); #endif #else gl_FragData[0]=vec4(vViewPos.z/vViewPos.w,0.0,0.0,1.0); gl_FragData[1]=vec4(normalOutput,1.0); #endif #ifdef POSITION gl_FragData[POSITION_INDEX]=vec4(vPositionW,1.0); #endif #ifdef VELOCITY vec2 a=(vCurrentPosition.xy/vCurrentPosition.w)*0.5+0.5; vec2 b=(vPreviousPosition.xy/vPreviousPosition.w)*0.5+0.5; vec2 velocity=abs(a-b); velocity=vec2(pow(velocity.x,1.0/3.0),pow(velocity.y,1.0/3.0))*sign(a-b)*0.5+0.5; gl_FragData[VELOCITY_INDEX]=vec4(velocity,0.0,1.0); #endif #ifdef REFLECTIVITY #ifdef HAS_SPECULAR vec4 reflectivity=texture2D(reflectivitySampler,vReflectivityUV); #elif HAS_REFLECTIVITY vec4 reflectivity=vec4(texture2D(reflectivitySampler,vReflectivityUV).rgb,1.0); #else vec4 reflectivity=vec4(0.0,0.0,0.0,1.0); #endif gl_FragData[REFLECTIVITY_INDEX]=reflectivity; #endif } ";X.a.ShadersStore.geometryPixelShader=a;X.a.IncludesShadersStore.geometryVertexDeclaration=" uniform mat4 viewProjection; uniform mat4 view;";X.a.IncludesShadersStore.geometryUboDeclaration="#include";wi="precision highp float; #include #include #include[0..maxSimultaneousMorphTargets] #include #include<__decl__geometryVertex> attribute vec3 position; attribute vec3 normal; #ifdef NEED_UV varying vec2 vUV; #ifdef ALPHATEST uniform mat4 diffuseMatrix; #endif #ifdef BUMP uniform mat4 bumpMatrix; varying vec2 vBumpUV; #endif #ifdef REFLECTIVITY uniform mat4 reflectivityMatrix; varying vec2 vReflectivityUV; #endif #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #ifdef BUMP varying mat4 vWorldView; #endif #ifdef BUMP varying vec3 vNormalW; #else varying vec3 vNormalV; #endif varying vec4 vViewPos; #if defined(POSITION) || defined(BUMP) varying vec3 vPositionW; #endif #ifdef VELOCITY uniform mat4 previousViewProjection; varying vec4 vCurrentPosition; varying vec4 vPreviousPosition; #endif void main(void) { vec3 positionUpdated=position; vec3 normalUpdated=normal; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #if defined(VELOCITY) && !defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #include vec4 pos=vec4(finalWorld*vec4(positionUpdated,1.0)); #ifdef BUMP vWorldView=view*finalWorld; vNormalW=normalUpdated; #else vNormalV=normalize(vec3((view*finalWorld)*vec4(normalUpdated,0.0))); #endif vViewPos=view*pos; #if defined(VELOCITY) && defined(BONES_VELOCITY_ENABLED) vCurrentPosition=viewProjection*finalWorld*vec4(positionUpdated,1.0); #if NUM_BONE_INFLUENCERS>0 mat4 previousInfluence; previousInfluence=mPreviousBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 previousInfluence+=mPreviousBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 previousInfluence+=mPreviousBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 previousInfluence+=mPreviousBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif vPreviousPosition=previousViewProjection*finalPreviousWorld*previousInfluence*vec4(positionUpdated,1.0); #else vPreviousPosition=previousViewProjection*finalPreviousWorld*vec4(positionUpdated,1.0); #endif #endif #if defined(POSITION) || defined(BUMP) vPositionW=pos.xyz/pos.w; #endif gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #ifdef NEED_UV #ifdef UV1 #if defined(ALPHATEST) && defined(ALPHATEST_UV1) vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #else vUV=uv; #endif #ifdef BUMP_UV1 vBumpUV=vec2(bumpMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef REFLECTIVITY_UV1 vReflectivityUV=vec2(reflectivityMatrix*vec4(uvUpdated,1.0,0.0)); #endif #endif #ifdef UV2 #if defined(ALPHATEST) && defined(ALPHATEST_UV2) vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #else vUV=uv2; #endif #ifdef BUMP_UV2 vBumpUV=vec2(bumpMatrix*vec4(uv2,1.0,0.0)); #endif #ifdef REFLECTIVITY_UV2 vReflectivityUV=vec2(reflectivityMatrix*vec4(uv2,1.0,0.0)); #endif #endif #endif #include } ";X.a.ShadersStore.geometryVertexShader=wi;var tp=function(){function a(b,c){void 0===c&&(c=1),this._previousTransformationMatrices={},this._previousBonesTransformationMatrices={},this.excludedSkinnedMeshesFromVelocity=[],this.renderTransparentMeshes=!0,this._resizeObserver=null,this._enablePosition=!1,this._enableVelocity=!1,this._enableReflectivity=!1,this._positionIndex=-1,this._velocityIndex=-1,this._reflectivityIndex=-1,this._depthIndex=-1,this._normalIndex=-1,this._linkedWithPrePass=!1,this._scene=b,this._ratio=c,this._useUbo=b.getEngine().supportsUniformBuffers,this._drawWrapper=new Ec.a(b.getEngine()),a._SceneComponentInitialization(this._scene),this._createRenderTargets()}return a.prototype._linkPrePassRenderer=function(a){this._linkedWithPrePass=!0,this._prePassRenderer=a,this._multiRenderTarget&&(this._multiRenderTarget.onClearObservable.clear(),this._multiRenderTarget.onClearObservable.add(function(a){}))},a.prototype._unlinkPrePassRenderer=function(){this._linkedWithPrePass=!1,this._createRenderTargets()},a.prototype._resetLayout=function(){this._enablePosition=!1,this._enableReflectivity=!1,this._enableVelocity=!1,this._attachments=[]},a.prototype._forceTextureType=function(b,c){b===a.POSITION_TEXTURE_TYPE?(this._positionIndex=c,this._enablePosition=!0):b===a.VELOCITY_TEXTURE_TYPE?(this._velocityIndex=c,this._enableVelocity=!0):b===a.REFLECTIVITY_TEXTURE_TYPE?(this._reflectivityIndex=c,this._enableReflectivity=!0):b===a.DEPTH_TEXTURE_TYPE?this._depthIndex=c:b===a.NORMAL_TEXTURE_TYPE&&(this._normalIndex=c)},a.prototype._setAttachments=function(a){this._attachments=a},a.prototype._linkInternalTexture=function(a){this._multiRenderTarget._texture=a},Object.defineProperty(a.prototype,"renderList",{get:function(){return this._multiRenderTarget.renderList},set:function(a){this._multiRenderTarget.renderList=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSupported",{get:function(){return this._multiRenderTarget.isSupported},enumerable:!1,configurable:!0}),a.prototype.getTextureIndex=function(b){switch(b){case a.POSITION_TEXTURE_TYPE:return this._positionIndex;case a.VELOCITY_TEXTURE_TYPE:return this._velocityIndex;case a.REFLECTIVITY_TEXTURE_TYPE:return this._reflectivityIndex;default:return-1}},Object.defineProperty(a.prototype,"enablePosition",{get:function(){return this._enablePosition},set:function(a){this._enablePosition=a,this._linkedWithPrePass||(this.dispose(),this._createRenderTargets())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enableVelocity",{get:function(){return this._enableVelocity},set:function(a){this._enableVelocity=a,a||(this._previousTransformationMatrices={}),this._linkedWithPrePass||(this.dispose(),this._createRenderTargets()),this._scene.needsPreviousWorldMatrices=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"enableReflectivity",{get:function(){return this._enableReflectivity},set:function(a){this._enableReflectivity=a,this._linkedWithPrePass||(this.dispose(),this._createRenderTargets())},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"ratio",{get:function(){return this._ratio},enumerable:!1,configurable:!0}),a.prototype.isReady=function(a,b){var c=a.getMaterial();if(c&&c.disableDepthWrite)return!1;var d=[],e=[W.b.PositionKind,W.b.NormalKind],f=a.getMesh();if(c){var g=!1;c.needAlphaTesting()&&c.getAlphaTestTexture()&&(d.push("#define ALPHATEST"),d.push("#define ALPHATEST_UV"+(c.getAlphaTestTexture().coordinatesIndex+1)),g=!0),c.bumpTexture&&ai.a.BumpTextureEnabled&&(d.push("#define BUMP"),d.push("#define BUMP_UV"+(c.bumpTexture.coordinatesIndex+1)),g=!0),this._enableReflectivity&&(c.specularTexture?(d.push("#define HAS_SPECULAR"),d.push("#define REFLECTIVITY_UV"+(c.specularTexture.coordinatesIndex+1)),g=!0):c.reflectivityTexture&&(d.push("#define HAS_REFLECTIVITY"),d.push("#define REFLECTIVITY_UV"+(c.reflectivityTexture.coordinatesIndex+1)),g=!0)),g&&(d.push("#define NEED_UV"),f.isVerticesDataPresent(W.b.UVKind)&&(e.push(W.b.UVKind),d.push("#define UV1")),f.isVerticesDataPresent(W.b.UV2Kind)&&(e.push(W.b.UV2Kind),d.push("#define UV2")))}this._linkedWithPrePass&&(d.push("#define PREPASS"),-1!==this._depthIndex&&(d.push("#define DEPTH_INDEX "+this._depthIndex),d.push("#define PREPASS_DEPTH")),-1!==this._normalIndex&&(d.push("#define NORMAL_INDEX "+this._normalIndex),d.push("#define PREPASS_NORMAL"))),this._enablePosition&&(d.push("#define POSITION"),d.push("#define POSITION_INDEX "+this._positionIndex)),this._enableVelocity&&(d.push("#define VELOCITY"),d.push("#define VELOCITY_INDEX "+this._velocityIndex),-1===this.excludedSkinnedMeshesFromVelocity.indexOf(f)&&d.push("#define BONES_VELOCITY_ENABLED")),this._enableReflectivity&&(d.push("#define REFLECTIVITY"),d.push("#define REFLECTIVITY_INDEX "+this._reflectivityIndex)),f.useBones&&f.computeBonesUsingShaders?(e.push(W.b.MatricesIndicesKind),e.push(W.b.MatricesWeightsKind),f.numBoneInfluencers>4&&(e.push(W.b.MatricesIndicesExtraKind),e.push(W.b.MatricesWeightsExtraKind)),d.push("#define NUM_BONE_INFLUENCERS "+f.numBoneInfluencers),d.push("#define BonesPerMesh "+(f.skeleton?f.skeleton.bones.length+1:0))):d.push("#define NUM_BONE_INFLUENCERS 0");c=f.morphTargetManager;g=0;c&&c.numInfluencers>0&&(g=c.numInfluencers,d.push("#define MORPHTARGETS"),d.push("#define NUM_MORPH_INFLUENCERS "+g),c.isUsingTextureForTargets&&d.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(e,f,g)),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(e,this._enableVelocity),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES")),this._linkedWithPrePass?d.push("#define RENDER_TARGET_COUNT "+this._attachments.length):d.push("#define RENDER_TARGET_COUNT "+this._multiRenderTarget.textures.length);c=d.join(" ");return this._cachedDefines!==c&&(this._cachedDefines=c,this._drawWrapper.effect=this._scene.getEngine().createEffect("geometry",{attributes:e,uniformsNames:["world","mBones","viewProjection","diffuseMatrix","view","previousWorld","previousViewProjection","mPreviousBones","bumpMatrix","reflectivityMatrix","vTangentSpaceParams","vBumpInfos","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],samplers:["diffuseSampler","bumpSampler","reflectivitySampler","morphTargets"],defines:c,onCompiled:null,fallbacks:null,onError:null,uniformBuffersNames:["Scene"],indexParameters:{buffersCount:this._multiRenderTarget.textures.length-1,maxSimultaneousMorphTargets:g}},this._scene.getEngine())),this._drawWrapper.effect.isReady()},a.prototype.getGBuffer=function(){return this._multiRenderTarget},Object.defineProperty(a.prototype,"samples",{get:function(){return this._multiRenderTarget.samples},set:function(a){this._multiRenderTarget.samples=a},enumerable:!1,configurable:!0}),a.prototype.dispose=function(){this._resizeObserver&&(this._scene.getEngine().onResizeObservable.remove(this._resizeObserver),this._resizeObserver=null),this.getGBuffer().dispose()},a.prototype._assignRenderTargetIndices=function(){var a=[],b=2;return a.push("gBuffer_Depth","gBuffer_Normal"),this._enablePosition&&(this._positionIndex=b,b++,a.push("gBuffer_Position")),this._enableVelocity&&(this._velocityIndex=b,b++,a.push("gBuffer_Velocity")),this._enableReflectivity&&(this._reflectivityIndex=b,b++,a.push("gBuffer_Reflectivity")),[b,a]},a.prototype._createRenderTargets=function(){var a=this,b=this._scene.getEngine(),c=this._assignRenderTargetIndices(),d=c[0];c=c[1];var e=r.a.TEXTURETYPE_UNSIGNED_BYTE;if(b._caps.textureFloat&&b._caps.textureFloatLinearFiltering?e=r.a.TEXTURETYPE_FLOAT:b._caps.textureHalfFloat&&b._caps.textureHalfFloatLinearFiltering&&(e=r.a.TEXTURETYPE_HALF_FLOAT),this._multiRenderTarget=new Jk("gBuffer",{width:b.getRenderWidth()*this._ratio,height:b.getRenderHeight()*this._ratio},d,this._scene,{generateMipMaps:!1,generateDepthTexture:!0,defaultType:e},c.concat("gBuffer_DepthBuffer")),this.isSupported){this._multiRenderTarget.wrapU=U.a.CLAMP_ADDRESSMODE,this._multiRenderTarget.wrapV=U.a.CLAMP_ADDRESSMODE,this._multiRenderTarget.refreshRate=1,this._multiRenderTarget.renderParticles=!1,this._multiRenderTarget.renderList=null,this._multiRenderTarget.onClearObservable.add(function(a){a.clear(new h.b(0,0,0,0),!0,!0,!0)}),this._resizeObserver=b.onResizeObservable.add(function(){a._multiRenderTarget&&a._multiRenderTarget.resize({width:b.getRenderWidth()*a._ratio,height:b.getRenderHeight()*a._ratio})});var f=function(b){var c=b.getRenderingMesh(),d=b.getEffectiveMesh(),e=a._scene,f=e.getEngine(),h=b.getMaterial();if(h){if(d._internalAbstractMeshDataInfo._isActiveIntermediate=!1,a._enableVelocity&&!a._previousTransformationMatrices[d.uniqueId]&&(a._previousTransformationMatrices[d.uniqueId]={world:g.a.Identity(),viewProjection:e.getTransformMatrix()},c.skeleton)){var i=c.skeleton.getTransformMatrices(c);a._previousBonesTransformationMatrices[c.uniqueId]=a._copyBonesTransformationMatrices(i,new Float32Array(i.length))}i=c._getInstancesRenderList(b._id,!!b.getReplacementMesh());if(!i.mustReturn){var j=f.getCaps().instancedArrays&&(null!==i.visibleInstances[b._id]||c.hasThinInstances),k=d.getWorldMatrix();if(a.isReady(b,j)){var l=a._drawWrapper.effect;if(f.enableEffect(a._drawWrapper),j||c._bind(b,l,h.fillMode),a._useUbo?(a._scene.finalizeSceneUbo(),Yh.a.BindSceneUniformBuffer(l,a._scene.getSceneUniformBuffer())):(l.setMatrix("viewProjection",e.getTransformMatrix()),l.setMatrix("view",e.getViewMatrix())),h){f=c._instanceDataStorage;if(f.isFrozen||!h.backFaceCulling&&null===h.overrideMaterialSideOrientation)f=f.sideOrientation;else{var n=d._getWorldMatrixDeterminant();null==(f=h.overrideMaterialSideOrientation)&&(f=h.sideOrientation),n<0&&(f=f===qi.a.ClockWiseSideOrientation?qi.a.CounterClockWiseSideOrientation:qi.a.ClockWiseSideOrientation)}if(h._preBind(a._drawWrapper,f),h.needAlphaTesting()){n=h.getAlphaTestTexture();n&&(l.setTexture("diffuseSampler",n),l.setMatrix("diffuseMatrix",n.getTextureMatrix()))}h.bumpTexture&&e.getEngine().getCaps().standardDerivatives&&ai.a.BumpTextureEnabled&&(l.setFloat3("vBumpInfos",h.bumpTexture.coordinatesIndex,1/h.bumpTexture.level,h.parallaxScaleBias),l.setMatrix("bumpMatrix",h.bumpTexture.getTextureMatrix()),l.setTexture("bumpSampler",h.bumpTexture),l.setFloat2("vTangentSpaceParams",h.invertNormalMapX?-1:1,h.invertNormalMapY?-1:1)),a._enableReflectivity&&(h.specularTexture?(l.setMatrix("reflectivityMatrix",h.specularTexture.getTextureMatrix()),l.setTexture("reflectivitySampler",h.specularTexture)):h.reflectivityTexture&&(l.setMatrix("reflectivityMatrix",h.reflectivityTexture.getTextureMatrix()),l.setTexture("reflectivitySampler",h.reflectivityTexture)))}c.useBones&&c.computeBonesUsingShaders&&c.skeleton&&(l.setMatrices("mBones",c.skeleton.getTransformMatrices(c)),a._enableVelocity&&l.setMatrices("mPreviousBones",a._previousBonesTransformationMatrices[c.uniqueId])),Yh.a.BindMorphTargetParameters(c,l),c.morphTargetManager&&c.morphTargetManager.isUsingTextureForTargets&&c.morphTargetManager._bind(l),a._enableVelocity&&(l.setMatrix("previousWorld",a._previousTransformationMatrices[d.uniqueId].world),l.setMatrix("previousViewProjection",a._previousTransformationMatrices[d.uniqueId].viewProjection),j&&c.hasThinInstances&&l.setMatrix("world",k)),c._processRendering(d,b,l,h.fillMode,i,j,function(a,b){a||l.setMatrix("world",b)})}a._enableVelocity&&(a._previousTransformationMatrices[d.uniqueId].world=k.clone(),a._previousTransformationMatrices[d.uniqueId].viewProjection=a._scene.getTransformMatrix().clone(),c.skeleton&&a._copyBonesTransformationMatrices(c.skeleton.getTransformMatrices(c),a._previousBonesTransformationMatrices[d.uniqueId]))}}};this._multiRenderTarget.customRenderFunction=function(c,d,e,g){var h;if(a._linkedWithPrePass){if(!a._prePassRenderer.enabled)return;a._scene.getEngine().bindAttachments(a._attachments)}if(g.length){for(b.setColorWrite(!1),h=0;h0){b=this._renderEffects[b[0]].getPostProcesses();b&&(b[0].samples=a)}return!0},a.prototype.setPrePassRenderer=function(a){return!1},a.prototype.dispose=function(){},Object(l.c)([Object(I.d)()],a.prototype,"_name",void 0),a}(),Ap=function(){function a(){this._renderPipelines={}}return Object.defineProperty(a.prototype,"supportedPipelines",{get:function(){var a=[];for(var b in this._renderPipelines)if(this._renderPipelines.hasOwnProperty(b)){var c=this._renderPipelines[b];c.isSupported&&a.push(c)}return a},enumerable:!1,configurable:!0}),a.prototype.addPipeline=function(a){this._renderPipelines[a._name]=a},a.prototype.attachCamerasToRenderPipeline=function(a,b,c){void 0===c&&(c=!1);a=this._renderPipelines[a];a&&a._attachCameras(b,c)},a.prototype.detachCamerasFromRenderPipeline=function(a,b){a=this._renderPipelines[a];a&&a._detachCameras(b)},a.prototype.enableEffectInPipeline=function(a,b,c){a=this._renderPipelines[a];a&&a._enableEffect(b,c)},a.prototype.disableEffectInPipeline=function(a,b,c){a=this._renderPipelines[a];a&&a._disableEffect(b,c)},a.prototype.update=function(){for(var a in this._renderPipelines)if(this._renderPipelines.hasOwnProperty(a)){var b=this._renderPipelines[a];b.isSupported?b._update():(b.dispose(),delete this._renderPipelines[a])}},a.prototype._rebuild=function(){for(var a in this._renderPipelines)this._renderPipelines.hasOwnProperty(a)&&this._renderPipelines[a]._rebuild()},a.prototype.dispose=function(){for(var a in this._renderPipelines)this._renderPipelines.hasOwnProperty(a)&&this._renderPipelines[a].dispose()},a}();Object.defineProperty(O.a.prototype,"postProcessRenderPipelineManager",{get:function(){if(!this._postProcessRenderPipelineManager){var a=this._getComponent(Oa.a.NAME_POSTPROCESSRENDERPIPELINEMANAGER);a||(a=new Bp(this),this._addComponent(a)),this._postProcessRenderPipelineManager=new Ap}return this._postProcessRenderPipelineManager},enumerable:!0,configurable:!0});var Bp=function(){function a(a){this.name=Oa.a.NAME_POSTPROCESSRENDERPIPELINEMANAGER,this.scene=a}return a.prototype.register=function(){this.scene._gatherRenderTargetsStage.registerStep(Oa.a.STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER,this,this._gatherRenderTargets)},a.prototype.rebuild=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager._rebuild()},a.prototype.dispose=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager.dispose()},a.prototype._gatherRenderTargets=function(){this.scene._postProcessRenderPipelineManager&&this.scene._postProcessRenderPipelineManager.update()},a}(),Cp=function(a){function b(b,c,d,e,g){void 0===b&&(b=""),void 0===c&&(c=!0),void 0===d&&(d=C.a.LastCreatedScene),void 0===g&&(g=!0);var h=a.call(this,d.getEngine(),b)||this;h._camerasToBeAttached=[],h.SharpenPostProcessId="SharpenPostProcessEffect",h.ImageProcessingPostProcessId="ImageProcessingPostProcessEffect",h.FxaaPostProcessId="FxaaPostProcessEffect",h.ChromaticAberrationPostProcessId="ChromaticAberrationPostProcessEffect",h.GrainPostProcessId="GrainPostProcessEffect",h._glowLayer=null,h.animations=[],h._imageProcessingConfigurationObserver=null,h._sharpenEnabled=!1,h._bloomEnabled=!1,h._depthOfFieldEnabled=!1,h._depthOfFieldBlurLevel=kp.Low,h._fxaaEnabled=!1,h._imageProcessingEnabled=!0,h._bloomScale=.5,h._chromaticAberrationEnabled=!1,h._grainEnabled=!1,h._buildAllowed=!0,h.onBuildObservable=new f.c,h._resizeObserver=null,h._hardwareScaleLevel=1,h._bloomKernel=64,h._bloomWeight=.15,h._bloomThreshold=.9,h._samples=1,h._hasCleared=!1,h._prevPostProcess=null,h._prevPrevPostProcess=null,h._depthOfFieldSceneObserver=null,h._cameras=e||d.cameras,h._cameras=h._cameras.slice(),h._camerasToBeAttached=h._cameras.slice(),h._buildAllowed=g,h._scene=d;b=h._scene.getEngine().getCaps();h._hdr=c&&(b.textureHalfFloatRender||b.textureFloatRender),h._hdr?b.textureHalfFloatRender?h._defaultPipelineTextureType=r.a.TEXTURETYPE_HALF_FLOAT:b.textureFloatRender&&(h._defaultPipelineTextureType=r.a.TEXTURETYPE_FLOAT):h._defaultPipelineTextureType=r.a.TEXTURETYPE_UNSIGNED_INT,d.postProcessRenderPipelineManager.addPipeline(h);var i=h._scene.getEngine();return h.sharpen=new yp("sharpen",1,null,U.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._sharpenEffect=new bp(i,h.SharpenPostProcessId,function(){return h.sharpen},!0),h.depthOfField=new np(h._scene,null,h._depthOfFieldBlurLevel,h._defaultPipelineTextureType,!0),h.bloom=new ep(h._scene,h._bloomScale,h._bloomWeight,h.bloomKernel,h._defaultPipelineTextureType,!0),h.chromaticAberration=new fp("ChromaticAberration",i.getRenderWidth(),i.getRenderHeight(),1,null,U.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._chromaticAberrationEffect=new bp(i,h.ChromaticAberrationPostProcessId,function(){return h.chromaticAberration},!0),h.grain=new rp("Grain",1,null,U.a.BILINEAR_SAMPLINGMODE,i,!1,h._defaultPipelineTextureType,!0),h._grainEffect=new bp(i,h.GrainPostProcessId,function(){return h.grain},!0),h._resizeObserver=i.onResizeObservable.add(function(){h._hardwareScaleLevel=i.getHardwareScalingLevel(),h.bloomKernel=h.bloomKernel}),h._imageProcessingConfigurationObserver=h._scene.imageProcessingConfiguration.onUpdateParameters.add(function(){h.bloom._downscale._exposure=h._scene.imageProcessingConfiguration.exposure,h.imageProcessingEnabled!==h._scene.imageProcessingConfiguration.isEnabled&&(h._imageProcessingEnabled=h._scene.imageProcessingConfiguration.isEnabled,h._buildPipeline())}),h._buildPipeline(),h}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"sharpenEnabled",{get:function(){return this._sharpenEnabled},set:function(a){this._sharpenEnabled!==a&&(this._sharpenEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomKernel",{get:function(){return this._bloomKernel},set:function(a){this._bloomKernel=a,this.bloom.kernel=a/this._hardwareScaleLevel},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomWeight",{get:function(){return this._bloomWeight},set:function(a){this._bloomWeight!==a&&(this.bloom.weight=a,this._bloomWeight=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomThreshold",{get:function(){return this._bloomThreshold},set:function(a){this._bloomThreshold!==a&&(this.bloom.threshold=a,this._bloomThreshold=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomScale",{get:function(){return this._bloomScale},set:function(a){this._bloomScale!==a&&(this._bloomScale=a,this._rebuildBloom(),this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"bloomEnabled",{get:function(){return this._bloomEnabled},set:function(a){this._bloomEnabled!==a&&(this._bloomEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),b.prototype._rebuildBloom=function(){var a=this.bloom;this.bloom=new ep(this._scene,this.bloomScale,this._bloomWeight,this.bloomKernel,this._defaultPipelineTextureType,!1),this.bloom.threshold=a.threshold;for(var b=0;b1){for(var c=0,d=this._cameras;c-1&&(a.depthOfField.depthTexture=b.enableDepthRenderer(b.activeCamera).getDepthMap())})}else{this._scene.onAfterRenderTargetsRenderObservable.remove(this._depthOfFieldSceneObserver);e=this._scene.enableDepthRenderer(this._cameras[0]);this.depthOfField.depthTexture=e.getDepthMap()}this.depthOfField._isReady()||this.depthOfField._updateEffects(),this.addEffect(this.depthOfField),this._setAutoClearAndTextureSharing(this.depthOfField._effects[0],!0)}else this._scene.onAfterRenderTargetsRenderObservable.remove(this._depthOfFieldSceneObserver);this.bloomEnabled&&(this.bloom._isReady()||this.bloom._updateEffects(),this.addEffect(this.bloom),this._setAutoClearAndTextureSharing(this.bloom._effects[0],!0)),this._imageProcessingEnabled&&(this.imageProcessing=new td("imageProcessing",1,null,U.a.BILINEAR_SAMPLINGMODE,b,!1,this._defaultPipelineTextureType),this._hdr?(this.addEffect(new bp(b,this.ImageProcessingPostProcessId,function(){return a.imageProcessing},!0)),this._setAutoClearAndTextureSharing(this.imageProcessing)):this._scene.imageProcessingConfiguration.applyByPostProcess=!1,this.cameras&&0!==this.cameras.length||(this._scene.imageProcessingConfiguration.applyByPostProcess=!1),this.imageProcessing.getEffect()||this.imageProcessing._updateParameters()),this.sharpenEnabled&&(this.sharpen.isReady()||this.sharpen.updateEffect(),this.addEffect(this._sharpenEffect),this._setAutoClearAndTextureSharing(this.sharpen)),this.grainEnabled&&(this.grain.isReady()||this.grain.updateEffect(),this.addEffect(this._grainEffect),this._setAutoClearAndTextureSharing(this.grain)),this.chromaticAberrationEnabled&&(this.chromaticAberration.isReady()||this.chromaticAberration.updateEffect(),this.addEffect(this._chromaticAberrationEffect),this._setAutoClearAndTextureSharing(this.chromaticAberration)),this.fxaaEnabled&&(this.fxaa=new qp("fxaa",1,null,U.a.BILINEAR_SAMPLINGMODE,b,!1,this._defaultPipelineTextureType),this.addEffect(new bp(b,this.FxaaPostProcessId,function(){return a.fxaa},!0)),this._setAutoClearAndTextureSharing(this.fxaa,!0)),null!==this._cameras&&this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name,this._cameras),this._scene.activeCameras&&this._scene.activeCameras.length>1&&(this._scene.autoClear=!0),!this._enableMSAAOnFirstPostProcess(this.samples)&&this.samples>1&&q.a.Warn("MSAA failed to enable, MSAA is only supported in browsers that support webGL >= 2.0"),this.onBuildObservable.notifyObservers(this)}},b.prototype._disposePostProcesses=function(a){void 0===a&&(a=!1);for(var b=0;b1.0) { total_weight+=sampleScreen(col,vec2(0.01*w,2.25*h),0.70); total_weight+=sampleScreen(col,vec2(-1.62*w,-1.74*h),0.67); total_weight+=sampleScreen(col,vec2(2.49*w,0.20*h),0.65); total_weight+=sampleScreen(col,vec2(-2.07*w,1.61*h),0.63); total_weight+=sampleScreen(col,vec2(0.46*w,-2.70*h),0.61); total_weight+=sampleScreen(col,vec2(1.55*w,2.40*h),0.59); total_weight+=sampleScreen(col,vec2(-2.88*w,-0.75*h),0.56); total_weight+=sampleScreen(col,vec2(2.73*w,-1.44*h),0.54); total_weight+=sampleScreen(col,vec2(-1.08*w,3.02*h),0.52); total_weight+=sampleScreen(col,vec2(-1.28*w,-3.05*h),0.49); } if (blur_level>2.0) { total_weight+=sampleScreen(col,vec2(3.11*w,1.43*h),0.46); total_weight+=sampleScreen(col,vec2(-3.36*w,1.08*h),0.44); total_weight+=sampleScreen(col,vec2(1.80*w,-3.16*h),0.41); total_weight+=sampleScreen(col,vec2(0.83*w,3.65*h),0.38); total_weight+=sampleScreen(col,vec2(-3.16*w,-2.19*h),0.34); total_weight+=sampleScreen(col,vec2(3.92*w,-0.53*h),0.31); total_weight+=sampleScreen(col,vec2(-2.59*w,3.12*h),0.26); total_weight+=sampleScreen(col,vec2(-0.20*w,-4.15*h),0.22); total_weight+=sampleScreen(col,vec2(3.02*w,3.00*h),0.15); } col/=total_weight; if (darken>0.0) { col.rgb*=clamp(0.3,1.0,1.05-size*0.5*darken); } return col; } void main(void) { centered_screen_pos=vec2(vUV.x-0.5,vUV.y-0.5); radius2=centered_screen_pos.x*centered_screen_pos.x+centered_screen_pos.y*centered_screen_pos.y; radius=sqrt(radius2); distorted_coords=getDistortedCoords(vUV); vec2 texels_coords=vec2(vUV.x*screen_width,vUV.y*screen_height); float depth=texture2D(depthSampler,distorted_coords).r; float distance=near+(far-near)*depth; vec4 color=texture2D(textureSampler,vUV); float coc=abs(aperture*(screen_distance*(inverse_focal_length-1.0/distance)-1.0)); if (dof_enabled == false || coc<0.07) { coc=0.0; } float edge_blur_amount=0.0; if (edge_blur>0.0) { edge_blur_amount=clamp((radius*2.0-1.0+0.15*edge_blur)*1.5,0.0,1.0)*1.3; } float blur_amount=max(edge_blur_amount,coc); if (blur_amount == 0.0) { gl_FragColor=texture2D(textureSampler,distorted_coords); } else { gl_FragColor=getBlurColor(blur_amount*1.7); if (highlights) { gl_FragColor.rgb+=clamp(coc,0.0,1.0)*texture2D(highlightsSampler,distorted_coords).rgb; } if (blur_noise) { vec2 noise=rand(distorted_coords)*0.01*blur_amount; vec2 blurred_coord=vec2(distorted_coords.x+noise.x,distorted_coords.y+noise.y); gl_FragColor=0.04*texture2D(textureSampler,blurred_coord)+0.96*gl_FragColor; } } if (grain_amount>0.0) { vec4 grain_color=texture2D(grainSampler,texels_coords*0.003); gl_FragColor.rgb+=(-0.5+grain_color.rgb)*0.30*grain_amount; } } ";X.a.ShadersStore.depthOfFieldPixelShader=a;var Dp=function(a){function b(b,c,d,e,f){void 0===e&&(e=1);var g=a.call(this,d.getEngine(),b)||this;return g.LensChromaticAberrationEffect="LensChromaticAberrationEffect",g.HighlightsEnhancingEffect="HighlightsEnhancingEffect",g.LensDepthOfFieldEffect="LensDepthOfFieldEffect",g._pentagonBokehIsEnabled=!1,g._scene=d,g._depthTexture=d.enableDepthRenderer().getDepthMap(),c.grain_texture?g._grainTexture=c.grain_texture:g._createGrainTexture(),g._edgeBlur=c.edge_blur?c.edge_blur:0,g._grainAmount=c.grain_amount?c.grain_amount:0,g._chromaticAberration=c.chromatic_aberration?c.chromatic_aberration:0,g._distortion=c.distortion?c.distortion:0,g._highlightsGain=void 0!==c.dof_gain?c.dof_gain:-1,g._highlightsThreshold=c.dof_threshold?c.dof_threshold:1,g._dofDistance=void 0!==c.dof_focus_distance?c.dof_focus_distance:-1,g._dofAperture=c.dof_aperture?c.dof_aperture:1,g._dofDarken=c.dof_darken?c.dof_darken:0,g._dofPentagon=void 0===c.dof_pentagon||c.dof_pentagon,g._blurNoise=void 0===c.blur_noise||c.blur_noise,g._createChromaticAberrationPostProcess(e),g._createHighlightsPostProcess(e),g._createDepthOfFieldPostProcess(e/4),g.addEffect(new bp(d.getEngine(),g.LensChromaticAberrationEffect,function(){return g._chromaticAberrationPostProcess},!0)),g.addEffect(new bp(d.getEngine(),g.HighlightsEnhancingEffect,function(){return g._highlightsPostProcess},!0)),g.addEffect(new bp(d.getEngine(),g.LensDepthOfFieldEffect,function(){return g._depthOfFieldPostProcess},!0)),-1===g._highlightsGain&&g._disableEffect(g.HighlightsEnhancingEffect,null),d.postProcessRenderPipelineManager.addPipeline(g),f&&d.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(b,f),g}return Object(l.d)(b,a),b.prototype.getClassName=function(){return"LensRenderingPipeline"},Object.defineProperty(b.prototype,"scene",{get:function(){return this._scene},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"edgeBlur",{get:function(){return this._edgeBlur},set:function(a){this.setEdgeBlur(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"grainAmount",{get:function(){return this._grainAmount},set:function(a){this.setGrainAmount(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"chromaticAberration",{get:function(){return this._chromaticAberration},set:function(a){this.setChromaticAberration(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dofAperture",{get:function(){return this._dofAperture},set:function(a){this.setAperture(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"edgeDistortion",{get:function(){return this._distortion},set:function(a){this.setEdgeDistortion(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"dofDistortion",{get:function(){return this._dofDistance},set:function(a){this.setFocusDistance(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"darkenOutOfFocus",{get:function(){return this._dofDarken},set:function(a){this.setDarkenOutOfFocus(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"blurNoise",{get:function(){return this._blurNoise},set:function(a){this._blurNoise=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"pentagonBokeh",{get:function(){return this._pentagonBokehIsEnabled},set:function(a){a?this.enablePentagonBokeh():this.disablePentagonBokeh()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"highlightsGain",{get:function(){return this._highlightsGain},set:function(a){this.setHighlightsGain(a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"highlightsThreshold",{get:function(){return this._highlightsThreshold},set:function(a){this.setHighlightsThreshold(a)},enumerable:!1,configurable:!0}),b.prototype.setEdgeBlur=function(a){this._edgeBlur=a},b.prototype.disableEdgeBlur=function(){this._edgeBlur=0},b.prototype.setGrainAmount=function(a){this._grainAmount=a},b.prototype.disableGrain=function(){this._grainAmount=0},b.prototype.setChromaticAberration=function(a){this._chromaticAberration=a},b.prototype.disableChromaticAberration=function(){this._chromaticAberration=0},b.prototype.setEdgeDistortion=function(a){this._distortion=a},b.prototype.disableEdgeDistortion=function(){this._distortion=0},b.prototype.setFocusDistance=function(a){this._dofDistance=a},b.prototype.disableDepthOfField=function(){this._dofDistance=-1},b.prototype.setAperture=function(a){this._dofAperture=a},b.prototype.setDarkenOutOfFocus=function(a){this._dofDarken=a},b.prototype.enablePentagonBokeh=function(){this._highlightsPostProcess.updateEffect("#define PENTAGON "),this._pentagonBokehIsEnabled=!0},b.prototype.disablePentagonBokeh=function(){this._pentagonBokehIsEnabled=!1,this._highlightsPostProcess.updateEffect()},b.prototype.enableNoiseBlur=function(){this._blurNoise=!0},b.prototype.disableNoiseBlur=function(){this._blurNoise=!1},b.prototype.setHighlightsGain=function(a){this._highlightsGain=a},b.prototype.setHighlightsThreshold=function(a){-1===this._highlightsGain&&(this._highlightsGain=1),this._highlightsThreshold=a},b.prototype.disableHighlights=function(){this._highlightsGain=-1},b.prototype.dispose=function(a){void 0===a&&(a=!1),this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name,this._scene.cameras),this._chromaticAberrationPostProcess=null,this._highlightsPostProcess=null,this._depthOfFieldPostProcess=null,this._grainTexture.dispose(),a&&this._scene.disableDepthRenderer()},b.prototype._createChromaticAberrationPostProcess=function(a){var b=this;this._chromaticAberrationPostProcess=new Fc("LensChromaticAberration","chromaticAberration",["chromatic_aberration","screen_width","screen_height","direction","radialIntensity","centerPosition"],[],a,null,U.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1),this._chromaticAberrationPostProcess.onApply=function(a){a.setFloat("chromatic_aberration",b._chromaticAberration),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight()),a.setFloat("radialIntensity",1),a.setFloat2("direction",17,17),a.setFloat2("centerPosition",.5,.5)}},b.prototype._createHighlightsPostProcess=function(a){var b=this;this._highlightsPostProcess=new Fc("LensHighlights","lensHighlights",["gain","threshold","screen_width","screen_height"],[],a,null,U.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1,this._dofPentagon?"#define PENTAGON ":""),this._highlightsPostProcess.onApply=function(a){a.setFloat("gain",b._highlightsGain),a.setFloat("threshold",b._highlightsThreshold),a.setTextureFromPostProcess("textureSampler",b._chromaticAberrationPostProcess),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight())}},b.prototype._createDepthOfFieldPostProcess=function(a){var b=this;this._depthOfFieldPostProcess=new Fc("LensDepthOfField","depthOfField",["grain_amount","blur_noise","screen_width","screen_height","distortion","dof_enabled","screen_distance","aperture","darken","edge_blur","highlights","near","far"],["depthSampler","grainSampler","highlightsSampler"],a,null,U.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1),this._depthOfFieldPostProcess.onApply=function(a){a.setTexture("depthSampler",b._depthTexture),a.setTexture("grainSampler",b._grainTexture),a.setTextureFromPostProcess("textureSampler",b._highlightsPostProcess),a.setTextureFromPostProcess("highlightsSampler",b._depthOfFieldPostProcess),a.setFloat("grain_amount",b._grainAmount),a.setBool("blur_noise",b._blurNoise),a.setFloat("screen_width",b._scene.getEngine().getRenderWidth()),a.setFloat("screen_height",b._scene.getEngine().getRenderHeight()),a.setFloat("distortion",b._distortion),a.setBool("dof_enabled",-1!==b._dofDistance),a.setFloat("screen_distance",1/(.1-1/b._dofDistance)),a.setFloat("aperture",b._dofAperture),a.setFloat("darken",b._dofDarken),a.setFloat("edge_blur",b._edgeBlur),a.setBool("highlights",-1!==b._highlightsGain),b._scene.activeCamera&&(a.setFloat("near",b._scene.activeCamera.minZ),a.setFloat("far",b._scene.activeCamera.maxZ))}},b.prototype._createGrainTexture=function(){this._grainTexture=new qd.a("LensNoiseTexture",512,this._scene,!1,U.a.BILINEAR_SAMPLINGMODE),this._grainTexture.wrapU=U.a.WRAP_ADDRESSMODE,this._grainTexture.wrapV=U.a.WRAP_ADDRESSMODE;for(var b,a,b,c=this._grainTexture.getContext(),d=0;d<512;d++)for(var e=0;e<512;e++)b=Math.floor(255*(a=.42,b=.58,Math.random()*(b-a)+a)),c.fillStyle="rgb("+b+", "+b+", "+b+")",c.fillRect(d,e,1,1);this._grainTexture.update(!1)},b}(zp),Ep=function(){this.enabled=!1,this.name="ssao2",this.texturesRequired=[r.a.PREPASS_NORMAL_TEXTURE_TYPE,r.a.PREPASS_DEPTH_TEXTURE_TYPE]};wi=" precision highp float; uniform sampler2D textureSampler; uniform float near; uniform float far; uniform float radius; float scales[16]=float[16]( 0.1, 0.11406250000000001, 0.131640625, 0.15625, 0.187890625, 0.2265625, 0.272265625, 0.325, 0.384765625, 0.4515625, 0.525390625, 0.60625, 0.694140625, 0.7890625, 0.891015625, 1.0 ); varying vec2 vUV; float perspectiveDepthToViewZ(in float invClipZ,in float near,in float far ) { return ( near*far )/( ( far-near )*invClipZ-far ); } float viewZToPerspectiveDepth( in float viewZ,in float near,in float far ) { return ( near*far/viewZ+far)/( far-near ); } float viewZToOrthographicDepth( in float viewZ,in float near,in float far ) { return ( viewZ+near )/( near-far ); } #ifdef SSAO uniform sampler2D randomSampler; uniform sampler2D depthSampler; uniform sampler2D normalSampler; uniform float randTextureTiles; uniform float samplesFactor; uniform vec3 sampleSphere[SAMPLES]; uniform float totalStrength; uniform float base; uniform float xViewport; uniform float yViewport; uniform mat3 depthProjection; uniform float maxZ; uniform float minZAspect; uniform vec2 texelSize; uniform mat4 projection; void main() { vec3 random=texture2D(randomSampler,vUV*randTextureTiles).rgb; float depth=texture2D(depthSampler,vUV).r; float depthSign=depth/abs(depth); depth=depth*depthSign; vec3 normal=texture2D(normalSampler,vUV).rgb; float occlusion=0.0; float correctedRadius=min(radius,minZAspect*depth/near); vec3 vViewRay=vec3((vUV.x*2.0-1.0)*xViewport,(vUV.y*2.0-1.0)*yViewport,depthSign); vec3 vDepthFactor=depthProjection*vec3(1.0,1.0,depth); vec3 origin=vViewRay*vDepthFactor; vec3 rvec=random*2.0-1.0; rvec.z=0.0; float dotProduct=dot(rvec,normal); rvec=1.0-abs(dotProduct)>1e-2 ? rvec : vec3(-rvec.y,0.0,rvec.x); vec3 tangent=normalize(rvec-normal*dot(rvec,normal)); vec3 bitangent=cross(normal,tangent); mat3 tbn=mat3(tangent,bitangent,normal); float difference; for (int i=0; i1.0 || offset.y>1.0) { continue; } float sampleDepth=abs(texture2D(depthSampler,offset.xy).r); difference=depthSign*samplePosition.z-sampleDepth; float rangeCheck=1.0-smoothstep(correctedRadius*0.5,correctedRadius,difference); occlusion+=(difference>=0.0 ? 1.0 : 0.0)*rangeCheck; } occlusion=occlusion*(1.0-smoothstep(maxZ*0.75,maxZ,depth)); float ao=1.0-totalStrength*occlusion*samplesFactor; float result=clamp(ao+base,0.0,1.0); gl_FragColor=vec4(vec3(result),1.0); } #endif #ifdef BILATERAL_BLUR uniform sampler2D depthSampler; uniform float outSize; uniform float samplerOffsets[SAMPLES]; vec4 blur9(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.3846153846)*direction; vec2 off2=vec2(3.2307692308)*direction; color+=texture2D(image,uv)*0.2270270270; color+=texture2D(image,uv+(off1/resolution))*0.3162162162; color+=texture2D(image,uv-(off1/resolution))*0.3162162162; color+=texture2D(image,uv+(off2/resolution))*0.0702702703; color+=texture2D(image,uv-(off2/resolution))*0.0702702703; return color; } vec4 blur13(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.411764705882353)*direction; vec2 off2=vec2(3.2941176470588234)*direction; vec2 off3=vec2(5.176470588235294)*direction; color+=texture2D(image,uv)*0.1964825501511404; color+=texture2D(image,uv+(off1/resolution))*0.2969069646728344; color+=texture2D(image,uv-(off1/resolution))*0.2969069646728344; color+=texture2D(image,uv+(off2/resolution))*0.09447039785044732; color+=texture2D(image,uv-(off2/resolution))*0.09447039785044732; color+=texture2D(image,uv+(off3/resolution))*0.010381362401148057; color+=texture2D(image,uv-(off3/resolution))*0.010381362401148057; return color; } vec4 blur13Bilateral(sampler2D image,vec2 uv,float resolution,vec2 direction) { vec4 color=vec4(0.0); vec2 off1=vec2(1.411764705882353)*direction; vec2 off2=vec2(3.2941176470588234)*direction; vec2 off3=vec2(5.176470588235294)*direction; float compareDepth=abs(texture2D(depthSampler,uv).r); float sampleDepth; float weight; float weightSum=30.0; color+=texture2D(image,uv)*30.0; sampleDepth=abs(texture2D(depthSampler,uv+(off1/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off1/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off1/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off1/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv+(off2/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off2/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off2/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off2/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv+(off3/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv+(off3/resolution))*weight; sampleDepth=abs(texture2D(depthSampler,uv-(off3/resolution)).r); weight=clamp(1.0/( 0.003+abs(compareDepth-sampleDepth)),0.0,30.0); weightSum+=weight; color+=texture2D(image,uv-(off3/resolution))*weight; return color/weightSum; } void main() { #if EXPENSIVE float compareDepth=abs(texture2D(depthSampler,vUV).r); float texelsize=1.0/outSize; float result=0.0; float weightSum=0.0; for (int i=0; i0?d._ssaoCombinePostProcess.width:d._originalColorPostProcess.width),a.setFloat("near",d._scene.activeCamera.minZ),a.setFloat("far",d._scene.activeCamera.maxZ),a.setFloat("radius",d.radius),d._geometryBufferRenderer?a.setTexture("depthSampler",d._geometryBufferRenderer.getGBuffer().textures[0]):d._prePassRenderer&&a.setTexture("depthSampler",d._prePassRenderer.getRenderTarget().textures[d._prePassRenderer.getIndex(r.a.PREPASS_DEPTH_TEXTURE_TYPE)]),a.setArray("samplerOffsets",d._samplerOffsets))},this._blurVPostProcess=new Fc("BlurV","ssao2",["outSize","samplerOffsets","near","far","radius"],["depthSampler"],b,null,U.a.TRILINEAR_SAMPLINGMODE,this._scene.getEngine(),!1,"#define BILATERAL_BLUR #define BILATERAL_BLUR_V #define SAMPLES 16 #define EXPENSIVE "+(e?"1":"0")+" ",c),this._blurVPostProcess.onApply=function(a){d._scene.activeCamera&&(a.setFloat("outSize",d._ssaoCombinePostProcess.height>0?d._ssaoCombinePostProcess.height:d._originalColorPostProcess.height),a.setFloat("near",d._scene.activeCamera.minZ),a.setFloat("far",d._scene.activeCamera.maxZ),a.setFloat("radius",d.radius),d._geometryBufferRenderer?a.setTexture("depthSampler",d._geometryBufferRenderer.getGBuffer().textures[0]):d._prePassRenderer&&a.setTexture("depthSampler",d._prePassRenderer.getRenderTarget().textures[d._prePassRenderer.getIndex(r.a.PREPASS_DEPTH_TEXTURE_TYPE)]),a.setArray("samplerOffsets",d._samplerOffsets))},this._blurHPostProcess.samples=this.textureSamples,this._blurVPostProcess.samples=this.textureSamples},b.prototype._rebuild=function(){a.prototype._rebuild.call(this)},b.prototype._radicalInverse_VdC=function(a){return this._bits[0]=a,this._bits[0]=(this._bits[0]<<16|this._bits[0]>>16)>>>0,this._bits[0]=(1431655765&this._bits[0])<<1|(2863311530&this._bits[0])>>>1>>>0,this._bits[0]=(858993459&this._bits[0])<<2|(3435973836&this._bits[0])>>>2>>>0,this._bits[0]=(252645135&this._bits[0])<<4|(4042322160&this._bits[0])>>>4>>>0,this._bits[0]=(16711935&this._bits[0])<<8|(4278255360&this._bits[0])>>>8>>>0,23283064365386963e-26*this._bits[0]},b.prototype._hammersley=function(a,b){return[a/b,this._radicalInverse_VdC(a)]},b.prototype._hemisphereSample_uniform=function(a,b){b=2*b*Math.PI;a=1-(.85*a+.15);var c=Math.sqrt(1-a*a);return new g.e(Math.cos(b)*c,Math.sin(b)*c,a)},b.prototype._generateHemisphere=function(){for(var a,b=this.samples,c=[],d=0;d0.0) hitCoord-=dir; else hitCoord+=dir; info.color+=texture2D(textureSampler,projectedCoord.xy).rgb; } projectedCoord=projection*vec4(hitCoord,1.0); projectedCoord.xy/=projectedCoord.w; projectedCoord.xy=0.5*projectedCoord.xy+vec2(0.5); info.coords=vec4(projectedCoord.xy,sampledDepth,1.0); info.color+=texture2D(textureSampler,projectedCoord.xy).rgb; info.color/=float(SMOOTH_STEPS+1); return info; } ReflectionInfo getReflectionInfo(vec3 dir,vec3 hitCoord) { ReflectionInfo info; vec4 projectedCoord; float sampledDepth; dir*=step; for(int i=0; i>0)),a.push("#define SMOOTH_STEPS "+(this._smoothSteps>>0)),this.updateEffect(a.join(" "))},b._Parse=function(a,c,d,e){return I.a.Parse(function(){return new b(a.name,d,a.options,c,a.renderTargetSamplingMode,d.getEngine(),a.textureType,a.reusable)},a,d,e)},Object(l.c)([Object(I.d)()],b.prototype,"threshold",void 0),Object(l.c)([Object(I.d)()],b.prototype,"strength",void 0),Object(l.c)([Object(I.d)()],b.prototype,"reflectionSpecularFalloffExponent",void 0),Object(l.c)([Object(I.d)()],b.prototype,"step",void 0),Object(l.c)([Object(I.d)()],b.prototype,"roughnessFactor",void 0),Object(l.c)([Object(I.d)()],b.prototype,"enableSmoothReflections",null),Object(l.c)([Object(I.d)()],b.prototype,"reflectionSamples",null),Object(l.c)([Object(I.d)()],b.prototype,"smoothSteps",null),b}(Fc);Object(i.b)("BABYLON.ScreenSpaceReflectionPostProcess",Ip);V="uniform sampler2D textureSampler; varying vec2 vUV; #if defined(PASS_POST_PROCESS) void main(void) { vec4 color=texture2D(textureSampler,vUV); gl_FragColor=color; } #endif #if defined(DOWN_SAMPLE_X4) uniform vec2 dsOffsets[16]; void main(void) { vec4 average=vec4(0.0,0.0,0.0,0.0); average=texture2D(textureSampler,vUV+dsOffsets[0]); average+=texture2D(textureSampler,vUV+dsOffsets[1]); average+=texture2D(textureSampler,vUV+dsOffsets[2]); average+=texture2D(textureSampler,vUV+dsOffsets[3]); average+=texture2D(textureSampler,vUV+dsOffsets[4]); average+=texture2D(textureSampler,vUV+dsOffsets[5]); average+=texture2D(textureSampler,vUV+dsOffsets[6]); average+=texture2D(textureSampler,vUV+dsOffsets[7]); average+=texture2D(textureSampler,vUV+dsOffsets[8]); average+=texture2D(textureSampler,vUV+dsOffsets[9]); average+=texture2D(textureSampler,vUV+dsOffsets[10]); average+=texture2D(textureSampler,vUV+dsOffsets[11]); average+=texture2D(textureSampler,vUV+dsOffsets[12]); average+=texture2D(textureSampler,vUV+dsOffsets[13]); average+=texture2D(textureSampler,vUV+dsOffsets[14]); average+=texture2D(textureSampler,vUV+dsOffsets[15]); average/=16.0; gl_FragColor=average; } #endif #if defined(BRIGHT_PASS) uniform vec2 dsOffsets[4]; uniform float brightThreshold; void main(void) { vec4 average=vec4(0.0,0.0,0.0,0.0); average=texture2D(textureSampler,vUV+vec2(dsOffsets[0].x,dsOffsets[0].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[1].x,dsOffsets[1].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[2].x,dsOffsets[2].y)); average+=texture2D(textureSampler,vUV+vec2(dsOffsets[3].x,dsOffsets[3].y)); average*=0.25; float luminance=length(average.rgb); if (luminanceshadowPixelDepth) accumFog+=sunColor*computeScattering(dot(rayDirection,sunDirection)); currentPosition+=stepL; } accumFog/=NB_STEPS; vec3 color=accumFog*scatteringPower; gl_FragColor=vec4(color*exp(color) ,1.0); } #endif #if defined(VLSMERGE) uniform sampler2D originalSampler; void main(void) { gl_FragColor=texture2D(originalSampler,vUV)+texture2D(textureSampler,vUV); } #endif #if defined(LUMINANCE) uniform vec2 lumOffsets[4]; void main() { float average=0.0; vec4 color=vec4(0.0); float maximum=-1e20; vec3 weight=vec3(0.299,0.587,0.114); for (int i=0; i<4; i++) { color=texture2D(textureSampler,vUV+ lumOffsets[i]); float GreyValue=dot(color.rgb,vec3(0.33,0.33,0.33)); #ifdef WEIGHTED_AVERAGE float GreyValue=dot(color.rgb,weight); #endif #ifdef BRIGHTNESS float GreyValue=max(color.r,max(color.g,color.b)); #endif #ifdef HSL_COMPONENT float GreyValue=0.5*(max(color.r,max(color.g,color.b))+min(color.r,min(color.g,color.b))); #endif #ifdef MAGNITUDE float GreyValue=length(color.rgb); #endif maximum=max(maximum,GreyValue); average+=(0.25*log(1e-5+GreyValue)); } average=exp(average); gl_FragColor=vec4(average,maximum,0.0,1.0); } #endif #if defined(LUMINANCE_DOWN_SAMPLE) uniform vec2 dsOffsets[9]; uniform float halfDestPixelSize; #ifdef FINAL_DOWN_SAMPLER #include #endif void main() { vec4 color=vec4(0.0); float average=0.0; for (int i=0; i<9; i++) { color=texture2D(textureSampler,vUV+vec2(halfDestPixelSize,halfDestPixelSize)+dsOffsets[i]); average+=color.r; } average/=9.0; #ifdef FINAL_DOWN_SAMPLER gl_FragColor=pack(average); #else gl_FragColor=vec4(average,average,0.0,1.0); #endif } #endif #if defined(HDR) uniform sampler2D textureAdderSampler; uniform float averageLuminance; void main() { vec4 color=texture2D(textureAdderSampler,vUV); #ifndef AUTO_EXPOSURE vec4 adjustedColor=color/averageLuminance; color=adjustedColor; color.a=1.0; #endif gl_FragColor=color; } #endif #if defined(LENS_FLARE) #define GHOSTS 3 uniform sampler2D lensColorSampler; uniform float strength; uniform float ghostDispersal; uniform float haloWidth; uniform vec2 resolution; uniform float distortionStrength; float hash(vec2 p) { float h=dot(p,vec2(127.1,311.7)); return -1.0+2.0*fract(sin(h)*43758.5453123); } float noise(in vec2 p) { vec2 i=floor(p); vec2 f=fract(p); vec2 u=f*f*(3.0-2.0*f); return mix(mix(hash(i+vec2(0.0,0.0)), hash(i+vec2(1.0,0.0)),u.x), mix(hash(i+vec2(0.0,1.0)), hash(i+vec2(1.0,1.0)),u.x),u.y); } float fbm(vec2 p) { float f=0.0; f+=0.5000*noise(p); p*=2.02; f+=0.2500*noise(p); p*=2.03; f+=0.1250*noise(p); p*=2.01; f+=0.0625*noise(p); p*=2.04; f/=0.9375; return f; } vec3 pattern(vec2 uv) { vec2 p=-1.0+2.0*uv; float p2=dot(p,p); float f=fbm(vec2(15.0*p2))/2.0; float r=0.2+0.6*sin(12.5*length(uv-vec2(0.5))); float g=0.2+0.6*sin(20.5*length(uv-vec2(0.5))); float b=0.2+0.6*sin(17.2*length(uv-vec2(0.5))); return (1.0-f)*vec3(r,g,b); } float luminance(vec3 color) { return dot(color.rgb,vec3(0.2126,0.7152,0.0722)); } vec4 textureDistorted(sampler2D tex,vec2 texcoord,vec2 direction,vec3 distortion) { return vec4( texture2D(tex,texcoord+direction*distortion.r).r, texture2D(tex,texcoord+direction*distortion.g).g, texture2D(tex,texcoord+direction*distortion.b).b, 1.0 ); } void main(void) { vec2 uv=-vUV+vec2(1.0); vec2 ghostDir=(vec2(0.5)-uv)*ghostDispersal; vec2 texelSize=1.0/resolution; vec3 distortion=vec3(-texelSize.x*distortionStrength,0.0,texelSize.x*distortionStrength); vec4 result=vec4(0.0); float ghostIndice=1.0; for (int i=0; i=nSamples) break; vec2 offset1=vUV+velocity*(float(i)/float(nSamples-1)-0.5); result+=texture2D(textureSampler,offset1); } gl_FragColor=result/float(nSamples); } #endif ";X.a.ShadersStore.standardPixelShader=V;var Jp=function(a){function b(b,c,d,e,f){void 0===e&&(e=null);b=a.call(this,c.getEngine(),b)||this;return b.downSampleX4PostProcess=null,b.brightPassPostProcess=null,b.blurHPostProcesses=[],b.blurVPostProcesses=[],b.textureAdderPostProcess=null,b.volumetricLightPostProcess=null,b.volumetricLightSmoothXPostProcess=null,b.volumetricLightSmoothYPostProcess=null,b.volumetricLightMergePostProces=null,b.volumetricLightFinalPostProcess=null,b.luminancePostProcess=null,b.luminanceDownSamplePostProcesses=[],b.hdrPostProcess=null,b.textureAdderFinalPostProcess=null,b.lensFlareFinalPostProcess=null,b.hdrFinalPostProcess=null,b.lensFlarePostProcess=null,b.lensFlareComposePostProcess=null,b.motionBlurPostProcess=null,b.depthOfFieldPostProcess=null,b.fxaaPostProcess=null,b.screenSpaceReflectionPostProcess=null,b.brightThreshold=1,b.blurWidth=512,b.horizontalBlur=!1,b.lensTexture=null,b.volumetricLightCoefficient=.2,b.volumetricLightPower=4,b.volumetricLightBlurScale=64,b.sourceLight=null,b.hdrMinimumLuminance=1,b.hdrDecreaseRate=.5,b.hdrIncreaseRate=.5,b.lensColorTexture=null,b.lensFlareStrength=20,b.lensFlareGhostDispersal=1.4,b.lensFlareHaloWidth=.7,b.lensFlareDistortionStrength=16,b.lensFlareBlurWidth=512,b.lensStarTexture=null,b.lensFlareDirtTexture=null,b.depthOfFieldDistance=10,b.depthOfFieldBlurWidth=64,b.animations=[],b._currentDepthOfFieldSource=null,b._fixedExposure=1,b._currentExposure=1,b._hdrAutoExposure=!1,b._hdrCurrentLuminance=1,b._motionStrength=1,b._isObjectBasedMotionBlur=!1,b._camerasToBeAttached=[],b._bloomEnabled=!1,b._depthOfFieldEnabled=!1,b._vlsEnabled=!1,b._lensFlareEnabled=!1,b._hdrEnabled=!1,b._motionBlurEnabled=!1,b._fxaaEnabled=!1,b._screenSpaceReflectionsEnabled=!1,b._motionBlurSamples=64,b._volumetricLightStepsCount=50,b._samples=1,b._cameras=f||c.cameras,b._cameras=b._cameras.slice(),b._camerasToBeAttached=b._cameras.slice(),b._scene=c,b._basePostProcess=e,b._ratio=d,b._floatTextureType=c.getEngine().getCaps().textureFloatRender?r.a.TEXTURETYPE_FLOAT:r.a.TEXTURETYPE_HALF_FLOAT,c.postProcessRenderPipelineManager.addPipeline(b),b._buildPipeline(),b}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"exposure",{get:function(){return this._fixedExposure},set:function(a){this._fixedExposure=a,this._currentExposure=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"hdrAutoExposure",{get:function(){return this._hdrAutoExposure},set:function(a){if(this._hdrAutoExposure=a,this.hdrPostProcess){var b=["#define HDR"];a&&b.push("#define AUTO_EXPOSURE"),this.hdrPostProcess.updateEffect(b.join(" "))}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"motionStrength",{get:function(){return this._motionStrength},set:function(a){this._motionStrength=a,this._isObjectBasedMotionBlur&&this.motionBlurPostProcess&&(this.motionBlurPostProcess.motionStrength=a)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"objectBasedMotionBlur",{get:function(){return this._isObjectBasedMotionBlur},set:function(a){var b=this._isObjectBasedMotionBlur!==a;this._isObjectBasedMotionBlur=a,b&&this._buildPipeline()},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"BloomEnabled",{get:function(){return this._bloomEnabled},set:function(a){this._bloomEnabled!==a&&(this._bloomEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"DepthOfFieldEnabled",{get:function(){return this._depthOfFieldEnabled},set:function(a){this._depthOfFieldEnabled!==a&&(this._depthOfFieldEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"LensFlareEnabled",{get:function(){return this._lensFlareEnabled},set:function(a){this._lensFlareEnabled!==a&&(this._lensFlareEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"HDREnabled",{get:function(){return this._hdrEnabled},set:function(a){this._hdrEnabled!==a&&(this._hdrEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"VLSEnabled",{get:function(){return this._vlsEnabled},set:function(a){if(this._vlsEnabled!==a){if(a&&!this._scene.enableGeometryBufferRenderer())return void q.a.Warn("Geometry renderer is not supported, cannot create volumetric lights in Standard Rendering Pipeline");this._vlsEnabled=a,this._buildPipeline()}},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"MotionBlurEnabled",{get:function(){return this._motionBlurEnabled},set:function(a){this._motionBlurEnabled!==a&&(this._motionBlurEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"fxaaEnabled",{get:function(){return this._fxaaEnabled},set:function(a){this._fxaaEnabled!==a&&(this._fxaaEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"screenSpaceReflectionsEnabled",{get:function(){return this._screenSpaceReflectionsEnabled},set:function(a){this._screenSpaceReflectionsEnabled!==a&&(this._screenSpaceReflectionsEnabled=a,this._buildPipeline())},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"volumetricLightStepsCount",{get:function(){return this._volumetricLightStepsCount},set:function(a){this.volumetricLightPostProcess&&this.volumetricLightPostProcess.updateEffect("#define VLS #define NB_STEPS "+a.toFixed(1)),this._volumetricLightStepsCount=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"motionBlurSamples",{get:function(){return this._motionBlurSamples},set:function(a){this.motionBlurPostProcess&&(this._isObjectBasedMotionBlur?this.motionBlurPostProcess.motionBlurSamples=a:this.motionBlurPostProcess.updateEffect("#define MOTION_BLUR #define MAX_MOTION_SAMPLES "+a.toFixed(1))),this._motionBlurSamples=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"samples",{get:function(){return this._samples},set:function(a){this._samples!==a&&(this._samples=a,this._buildPipeline())},enumerable:!1,configurable:!0}),b.prototype._buildPipeline=function(){var a=this,b=this._ratio,c=this._scene;this._disposePostProcesses(),null!==this._cameras&&(this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name,this._cameras),this._cameras=this._camerasToBeAttached.slice()),this._reset(),this._screenSpaceReflectionsEnabled&&(this.screenSpaceReflectionPostProcess=new Ip("HDRPass",c,b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,this._floatTextureType),this.screenSpaceReflectionPostProcess.onApplyObservable.add(function(){a._currentDepthOfFieldSource=a.screenSpaceReflectionPostProcess}),this.addEffect(new bp(c.getEngine(),"HDRScreenSpaceReflections",function(){return a.screenSpaceReflectionPostProcess},!0))),this._basePostProcess?this.originalPostProcess=this._basePostProcess:this.originalPostProcess=new Fc("HDRPass","standard",[],[],b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",this._floatTextureType),this.originalPostProcess.autoClear=!this.screenSpaceReflectionPostProcess,this.originalPostProcess.onApplyObservable.add(function(){a._currentDepthOfFieldSource=a.originalPostProcess}),this.addEffect(new bp(c.getEngine(),"HDRPassPostProcess",function(){return a.originalPostProcess},!0)),this._bloomEnabled&&(this._createDownSampleX4PostProcess(c,b/4),this._createBrightPassPostProcess(c,b/4),this._createBlurPostProcesses(c,b/4,1),this._createTextureAdderPostProcess(c,b),this.textureAdderFinalPostProcess=new Fc("HDRDepthOfFieldSource","standard",[],[],b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRBaseDepthOfFieldSource",function(){return a.textureAdderFinalPostProcess},!0))),this._vlsEnabled&&(this._createVolumetricLightPostProcess(c,b),this.volumetricLightFinalPostProcess=new Fc("HDRVLSFinal","standard",[],[],b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRVLSFinal",function(){return a.volumetricLightFinalPostProcess},!0))),this._lensFlareEnabled&&(this._createLensFlarePostProcess(c,b),this.lensFlareFinalPostProcess=new Fc("HDRPostLensFlareDepthOfFieldSource","standard",[],[],b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRPostLensFlareDepthOfFieldSource",function(){return a.lensFlareFinalPostProcess},!0))),this._hdrEnabled&&(this._createLuminancePostProcesses(c,this._floatTextureType),this._createHdrPostProcess(c,b),this.hdrFinalPostProcess=new Fc("HDRPostHDReDepthOfFieldSource","standard",[],[],b,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,"#define PASS_POST_PROCESS",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRPostHDReDepthOfFieldSource",function(){return a.hdrFinalPostProcess},!0))),this._depthOfFieldEnabled&&(this._createBlurPostProcesses(c,b/2,3,"depthOfFieldBlurWidth"),this._createDepthOfFieldPostProcess(c,b)),this._motionBlurEnabled&&this._createMotionBlurPostProcess(c,b),this._fxaaEnabled&&(this.fxaaPostProcess=new qp("fxaa",1,null,U.a.BILINEAR_SAMPLINGMODE,c.getEngine(),!1,r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(c.getEngine(),"HDRFxaa",function(){return a.fxaaPostProcess},!0))),null!==this._cameras&&this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name,this._cameras),!this._enableMSAAOnFirstPostProcess(this._samples)&&this._samples>1&&q.a.Warn("MSAA failed to enable, MSAA is only supported in browsers that support webGL >= 2.0")},b.prototype._createDownSampleX4PostProcess=function(a,b){var c=this,d=new Array(32);this.downSampleX4PostProcess=new Fc("HDRDownSampleX4","standard",["dsOffsets"],[],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define DOWN_SAMPLE_X4",this._floatTextureType),this.downSampleX4PostProcess.onApply=function(a){for(var b=0,e=c.downSampleX4PostProcess.width,f=c.downSampleX4PostProcess.height,g=-2;g<2;g++)for(var h=-2;h<2;h++)d[b]=(g+.5)*(1/e),d[b+1]=(h+.5)*(1/f),b+=2;a.setArray2("dsOffsets",d)},this.addEffect(new bp(a.getEngine(),"HDRDownSampleX4",function(){return c.downSampleX4PostProcess},!0))},b.prototype._createBrightPassPostProcess=function(a,b){var c=this,d=new Array(8);this.brightPassPostProcess=new Fc("HDRBrightPass","standard",["dsOffsets","brightThreshold"],[],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define BRIGHT_PASS",this._floatTextureType),this.brightPassPostProcess.onApply=function(a){var b=1/c.brightPassPostProcess.width,e=1/c.brightPassPostProcess.height;d[0]=-.5*b,d[1]=.5*e,d[2]=.5*b,d[3]=.5*e,d[4]=-.5*b,d[5]=-.5*e,d[6]=.5*b,d[7]=-.5*e,a.setArray2("dsOffsets",d),a.setFloat("brightThreshold",c.brightThreshold)},this.addEffect(new bp(a.getEngine(),"HDRBrightPass",function(){return c.brightPassPostProcess},!0))},b.prototype._createBlurPostProcesses=function(a,b,c,d){var e=this;void 0===d&&(d="blurWidth");var f=a.getEngine(),h=new Vh("HDRBlurH_"+c,new g.d(1,0),this[d],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,this._floatTextureType),i=new Vh("HDRBlurV_"+c,new g.d(0,1),this[d],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,this._floatTextureType);h.onActivateObservable.add(function(){var a=h.width/f.getRenderWidth();h.kernel=e[d]*a}),i.onActivateObservable.add(function(){var a=i.height/f.getRenderHeight();i.kernel=e.horizontalBlur?64*a:e[d]*a}),this.addEffect(new bp(a.getEngine(),"HDRBlurH"+c,function(){return h},!0)),this.addEffect(new bp(a.getEngine(),"HDRBlurV"+c,function(){return i},!0)),this.blurHPostProcesses.push(h),this.blurVPostProcesses.push(i)},b.prototype._createTextureAdderPostProcess=function(a,b){var c=this;this.textureAdderPostProcess=new Fc("HDRTextureAdder","standard",["exposure"],["otherSampler","lensSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define TEXTURE_ADDER",this._floatTextureType),this.textureAdderPostProcess.onApply=function(a){a.setTextureFromPostProcess("otherSampler",c._vlsEnabled?c._currentDepthOfFieldSource:c.originalPostProcess),a.setTexture("lensSampler",c.lensTexture),a.setFloat("exposure",c._currentExposure),c._currentDepthOfFieldSource=c.textureAdderFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDRTextureAdder",function(){return c.textureAdderPostProcess},!0))},b.prototype._createVolumetricLightPostProcess=function(a,b){var c=this,d=a.enableGeometryBufferRenderer();d.enablePosition=!0;var e=d.getGBuffer();this.volumetricLightPostProcess=new Fc("HDRVLS","standard",["shadowViewProjection","cameraPosition","sunDirection","sunColor","scatteringCoefficient","scatteringPower","depthValues"],["shadowMapSampler","positionSampler"],b/8,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define VLS #define NB_STEPS "+this._volumetricLightStepsCount.toFixed(1));var f=g.d.Zero();this.volumetricLightPostProcess.onApply=function(a){if(c.sourceLight&&c.sourceLight.getShadowGenerator()&&c._scene.activeCamera){var b=c.sourceLight.getShadowGenerator();a.setTexture("shadowMapSampler",b.getShadowMap()),a.setTexture("positionSampler",e.textures[2]),a.setColor3("sunColor",c.sourceLight.diffuse),a.setVector3("sunDirection",c.sourceLight.getShadowDirection()),a.setVector3("cameraPosition",c._scene.activeCamera.globalPosition),a.setMatrix("shadowViewProjection",b.getTransformMatrix()),a.setFloat("scatteringCoefficient",c.volumetricLightCoefficient),a.setFloat("scatteringPower",c.volumetricLightPower),f.x=c.sourceLight.getDepthMinZ(c._scene.activeCamera),f.y=c.sourceLight.getDepthMaxZ(c._scene.activeCamera),a.setVector2("depthValues",f)}},this.addEffect(new bp(a.getEngine(),"HDRVLS",function(){return c.volumetricLightPostProcess},!0)),this._createBlurPostProcesses(a,b/4,0,"volumetricLightBlurScale"),this.volumetricLightMergePostProces=new Fc("HDRVLSMerge","standard",[],["originalSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define VLSMERGE"),this.volumetricLightMergePostProces.onApply=function(a){a.setTextureFromPostProcess("originalSampler",c._bloomEnabled?c.textureAdderFinalPostProcess:c.originalPostProcess),c._currentDepthOfFieldSource=c.volumetricLightFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDRVLSMerge",function(){return c.volumetricLightMergePostProces},!0))},b.prototype._createLuminancePostProcesses=function(a,c){var d=this,e=Math.pow(3,b.LuminanceSteps);this.luminancePostProcess=new Fc("HDRLuminance","standard",["lumOffsets"],[],{width:e,height:e},null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LUMINANCE",c);var f=[];this.luminancePostProcess.onApply=function(a){var b=1/d.luminancePostProcess.width,c=1/d.luminancePostProcess.height;f[0]=-.5*b,f[1]=.5*c,f[2]=.5*b,f[3]=.5*c,f[4]=-.5*b,f[5]=-.5*c,f[6]=.5*b,f[7]=-.5*c,a.setArray2("lumOffsets",f)},this.addEffect(new bp(a.getEngine(),"HDRLuminance",function(){return d.luminancePostProcess},!0));for(var h=b.LuminanceSteps-1;h>=0;h--){e=Math.pow(3,h);var i="#define LUMINANCE_DOWN_SAMPLE ";0===h&&(i+="#define FINAL_DOWN_SAMPLER");e=new Fc("HDRLuminanceDownSample"+h,"standard",["dsOffsets","halfDestPixelSize"],[],{width:e,height:e},null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,i,c);this.luminanceDownSamplePostProcesses.push(e)}var j=this.luminancePostProcess;this.luminanceDownSamplePostProcesses.forEach(function(b,c){var e=new Array(18);b.onApply=function(a){if(j){for(var f=0,g=-1;g<2;g++)for(var h=-1;h<2;h++)e[f]=g/j.width,e[f+1]=h/j.height,f+=2;a.setArray2("dsOffsets",e),a.setFloat("halfDestPixelSize",.5/j.width),j=c===d.luminanceDownSamplePostProcesses.length-1?d.luminancePostProcess:b}},c===d.luminanceDownSamplePostProcesses.length-1&&(b.onAfterRender=function(){var b=a.getEngine().readPixels(0,0,1,1),c=new g.f(1/16581375,1/65025,1/255,1);b.then(function(a){a=new Uint8Array(a.buffer);d._hdrCurrentLuminance=(a[0]*c.x+a[1]*c.y+a[2]*c.z+a[3]*c.w)/100})}),d.addEffect(new bp(a.getEngine(),"HDRLuminanceDownSample"+c,function(){return b},!0))})},b.prototype._createHdrPostProcess=function(a,b){var c=this,d=["#define HDR"];this._hdrAutoExposure&&d.push("#define AUTO_EXPOSURE"),this.hdrPostProcess=new Fc("HDR","standard",["averageLuminance"],["textureAdderSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,d.join(" "),r.a.TEXTURETYPE_UNSIGNED_INT);var e=1,f=0,g=0;this.hdrPostProcess.onApply=function(b){if(b.setTextureFromPostProcess("textureAdderSampler",c._currentDepthOfFieldSource),f+=a.getEngine().getDeltaTime(),e<0)e=c._hdrCurrentLuminance;else{var d=(g-f)/1e3;c._hdrCurrentLuminancee-c.hdrIncreaseRate*d?e-=c.hdrIncreaseRate*d:e=c._hdrCurrentLuminance}c.hdrAutoExposure?c._currentExposure=c._fixedExposure/e:(e=H.a.Clamp(e,c.hdrMinimumLuminance,1e20),b.setFloat("averageLuminance",e)),g=f,c._currentDepthOfFieldSource=c.hdrFinalPostProcess},this.addEffect(new bp(a.getEngine(),"HDR",function(){return c.hdrPostProcess},!0))},b.prototype._createLensFlarePostProcess=function(a,b){var c=this;this.lensFlarePostProcess=new Fc("HDRLensFlare","standard",["strength","ghostDispersal","haloWidth","resolution","distortionStrength"],["lensColorSampler"],b/2,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LENS_FLARE",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(a.getEngine(),"HDRLensFlare",function(){return c.lensFlarePostProcess},!0)),this._createBlurPostProcesses(a,b/4,2,"lensFlareBlurWidth"),this.lensFlareComposePostProcess=new Fc("HDRLensFlareCompose","standard",["lensStarMatrix"],["otherSampler","lensDirtSampler","lensStarSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define LENS_FLARE_COMPOSE",r.a.TEXTURETYPE_UNSIGNED_INT),this.addEffect(new bp(a.getEngine(),"HDRLensFlareCompose",function(){return c.lensFlareComposePostProcess},!0));var d=new g.d(0,0);this.lensFlarePostProcess.onApply=function(a){a.setTextureFromPostProcess("textureSampler",c._bloomEnabled?c.blurHPostProcesses[0]:c.originalPostProcess),a.setTexture("lensColorSampler",c.lensColorTexture),a.setFloat("strength",c.lensFlareStrength),a.setFloat("ghostDispersal",c.lensFlareGhostDispersal),a.setFloat("haloWidth",c.lensFlareHaloWidth),d.x=c.lensFlarePostProcess.width,d.y=c.lensFlarePostProcess.height,a.setVector2("resolution",d),a.setFloat("distortionStrength",c.lensFlareDistortionStrength)};var e=g.a.FromValues(2,0,-1,0,0,2,-1,0,0,0,1,0,0,0,0,1),f=g.a.FromValues(.5,0,.5,0,0,.5,.5,0,0,0,1,0,0,0,0,1);this.lensFlareComposePostProcess.onApply=function(a){if(c._scene.activeCamera){a.setTextureFromPostProcess("otherSampler",c.lensFlarePostProcess),a.setTexture("lensDirtSampler",c.lensFlareDirtTexture),a.setTexture("lensStarSampler",c.lensStarTexture);var b=c._scene.activeCamera.getViewMatrix().getRow(0),d=c._scene.activeCamera.getViewMatrix().getRow(2);b=g.e.Dot(b.toVector3(),new g.e(1,0,0))+g.e.Dot(d.toVector3(),new g.e(0,0,1));b*=4;d=g.a.FromValues(.5*Math.cos(b),-Math.sin(b),0,0,Math.sin(b),.5*Math.cos(b),0,0,0,0,1,0,0,0,0,1);b=f.multiply(d).multiply(e);a.setMatrix("lensStarMatrix",b),c._currentDepthOfFieldSource=c.lensFlareFinalPostProcess}}},b.prototype._createDepthOfFieldPostProcess=function(a,b){var c=this;this.depthOfFieldPostProcess=new Fc("HDRDepthOfField","standard",["distance"],["otherSampler","depthSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define DEPTH_OF_FIELD",r.a.TEXTURETYPE_UNSIGNED_INT),this.depthOfFieldPostProcess.onApply=function(a){a.setTextureFromPostProcess("otherSampler",c._currentDepthOfFieldSource),a.setTexture("depthSampler",c._getDepthTexture()),a.setFloat("distance",c.depthOfFieldDistance)},this.addEffect(new bp(a.getEngine(),"HDRDepthOfField",function(){return c.depthOfFieldPostProcess},!0))},b.prototype._createMotionBlurPostProcess=function(a,b){var c=this;if(this._isObjectBasedMotionBlur){var d=new wp("HDRMotionBlur",a,b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,r.a.TEXTURETYPE_UNSIGNED_INT);d.motionStrength=this.motionStrength,d.motionBlurSamples=this.motionBlurSamples,this.motionBlurPostProcess=d}else{this.motionBlurPostProcess=new Fc("HDRMotionBlur","standard",["inverseViewProjection","prevViewProjection","screenSize","motionScale","motionStrength"],["depthSampler"],b,null,U.a.BILINEAR_SAMPLINGMODE,a.getEngine(),!1,"#define MOTION_BLUR #define MAX_MOTION_SAMPLES "+this.motionBlurSamples.toFixed(1),r.a.TEXTURETYPE_UNSIGNED_INT);var e=g.a.Identity(),f=g.a.Identity();d=g.a.Identity();var h=g.d.Zero();this.motionBlurPostProcess.onApply=function(b){(d=a.getProjectionMatrix().multiply(a.getViewMatrix())).invertToRef(f),b.setMatrix("inverseViewProjection",f),b.setMatrix("prevViewProjection",e),e=d,h.x=c.motionBlurPostProcess.width,h.y=c.motionBlurPostProcess.height,b.setVector2("screenSize",h),b=a.getEngine().getFps()/60,b.setFloat("motionScale",b),b.setFloat("motionStrength",c.motionStrength),b.setTexture("depthSampler",c._getDepthTexture())}}this.addEffect(new bp(a.getEngine(),"HDRMotionBlur",function(){return c.motionBlurPostProcess},!0))},b.prototype._getDepthTexture=function(){return this._scene.getEngine().getCaps().drawBuffersExtension?this._scene.enableGeometryBufferRenderer().getGBuffer().textures[0]:this._scene.enableDepthRenderer().getDepthMap()},b.prototype._disposePostProcesses=function(){for(var a=0;a #include #include[0..maxSimultaneousMorphTargets] #include uniform mat4 viewProjection; uniform vec2 depthValues; #if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif void main(void) { vec3 positionUpdated=position; #if (defined(ALPHATEST) || defined(NEED_UV)) && defined(UV1) vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] #include #include gl_Position=viewProjection*finalWorld*vec4(positionUpdated,1.0); #if defined(ALPHATEST) || defined(BASIC_RENDER) #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif } ";X.a.ShadersStore.volumetricLightScatteringPassVertexShader=V;a="#if defined(ALPHATEST) || defined(NEED_UV) varying vec2 vUV; #endif #if defined(ALPHATEST) uniform sampler2D diffuseSampler; #endif void main(void) { #if defined(ALPHATEST) vec4 diffuseColor=texture2D(diffuseSampler,vUV); if (diffuseColor.a<0.4) discard; #endif gl_FragColor=vec4(0.0,0.0,0.0,1.0); } ";X.a.ShadersStore.volumetricLightScatteringPassPixelShader=a;var Mp=function(a){function b(c,d,e,f,h,i,j,k,l){void 0===h&&(h=100),void 0===i&&(i=U.a.BILINEAR_SAMPLINGMODE);var m=a.call(this,c,"volumetricLightScattering",["decay","exposure","weight","meshPositionOnScreen","density"],["lightScatteringSampler"],d.postProcessRatio||d,e,i,j,k,"#define NUM_SAMPLES "+h)||this;return m._screenCoordinates=g.d.Zero(),m.customMeshPosition=g.e.Zero(),m.useCustomMeshPosition=!1,m.invert=!0,m.excludedMeshes=new Array,m.exposure=.3,m.decay=.96815,m.weight=.58767,m.density=.926,j=(l=null!==(c=null==e?void 0:e.getScene())&&void 0!==c?c:l).getEngine(),m._viewPort=new Oc.a(0,0,1,1).toGlobal(j.getRenderWidth(),j.getRenderHeight()),m.mesh=null!=f?f:b.CreateDefaultMesh("VolumetricLightScatteringMesh",l),m._volumetricLightScatteringPass=new Ec.a(j),m._createPass(l,d.passRatio||d),m.onActivate=function(a){m.isSupported||m.dispose(a),m.onActivate=null},m.onApplyObservable.add(function(a){m._updateMeshScreenCoordinates(l),a.setTexture("lightScatteringSampler",m._volumetricLightScatteringRTT),a.setFloat("exposure",m.exposure),a.setFloat("decay",m.decay),a.setFloat("weight",m.weight),a.setFloat("density",m.density),a.setVector2("meshPositionOnScreen",m._screenCoordinates)}),m}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"useDiffuseColor",{get:function(){return q.a.Warn("VolumetricLightScatteringPostProcess.useDiffuseColor is no longer used, use the mesh material directly instead"),!1},set:function(a){q.a.Warn("VolumetricLightScatteringPostProcess.useDiffuseColor is no longer used, use the mesh material directly instead")},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"VolumetricLightScatteringPostProcess"},b.prototype._isReady=function(a,b){var c=a.getMesh();if(c===this.mesh&&c.material)return c.material.isReady(c);var d=[],e=[W.b.PositionKind],f=a.getMaterial();f&&(f.needAlphaTesting()&&d.push("#define ALPHATEST"),c.isVerticesDataPresent(W.b.UVKind)&&(e.push(W.b.UVKind),d.push("#define UV1")),c.isVerticesDataPresent(W.b.UV2Kind)&&(e.push(W.b.UV2Kind),d.push("#define UV2"))),c.useBones&&c.computeBonesUsingShaders?(e.push(W.b.MatricesIndicesKind),e.push(W.b.MatricesWeightsKind),d.push("#define NUM_BONE_INFLUENCERS "+c.numBoneInfluencers),d.push("#define BonesPerMesh "+(c.skeleton?c.skeleton.bones.length+1:0))):d.push("#define NUM_BONE_INFLUENCERS 0"),b&&(d.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(e),a.getRenderingMesh().hasThinInstances&&d.push("#define THIN_INSTANCES"));f=d.join(" ");return this._cachedDefines!==f&&(this._cachedDefines=f,this._volumetricLightScatteringPass.effect=c.getScene().getEngine().createEffect("volumetricLightScatteringPass",e,["world","mBones","viewProjection","diffuseMatrix"],["diffuseSampler"],f,void 0,void 0,void 0,{maxSimultaneousMorphTargets:c.numBoneInfluencers})),this._volumetricLightScatteringPass.effect.isReady()},b.prototype.setCustomMeshPosition=function(a){this.customMeshPosition=a},b.prototype.getCustomMeshPosition=function(){return this.customMeshPosition},b.prototype.dispose=function(b){var c=b.getScene().customRenderTargets.indexOf(this._volumetricLightScatteringRTT);-1!==c&&b.getScene().customRenderTargets.splice(c,1),this._volumetricLightScatteringRTT.dispose(),a.prototype.dispose.call(this,b)},b.prototype.getPass=function(){return this._volumetricLightScatteringRTT},b.prototype._meshExcluded=function(a){return this.excludedMeshes.length>0&&-1!==this.excludedMeshes.indexOf(a)},b.prototype._createPass=function(a,b){var c=this,d=a.getEngine();this._volumetricLightScatteringRTT=new cd("volumetricLightScatteringMap",{width:d.getRenderWidth()*b,height:d.getRenderHeight()*b},a,!1,!0,r.a.TEXTURETYPE_UNSIGNED_INT),this._volumetricLightScatteringRTT.wrapU=U.a.CLAMP_ADDRESSMODE,this._volumetricLightScatteringRTT.wrapV=U.a.CLAMP_ADDRESSMODE,this._volumetricLightScatteringRTT.renderList=null,this._volumetricLightScatteringRTT.renderParticles=!1,this._volumetricLightScatteringRTT.ignoreCameraViewport=!0;d=this.getCamera();d?d.customRenderTargets.push(this._volumetricLightScatteringRTT):a.customRenderTargets.push(this._volumetricLightScatteringRTT);var e,f=function(a){var b=a.getRenderingMesh(),d=a.getEffectiveMesh();if(!c._meshExcluded(b)){d._internalAbstractMeshDataInfo._isActiveIntermediate=!1;var e=a.getMaterial();if(e){var f=b.getScene(),g=f.getEngine();g.setState(e.backFaceCulling,void 0,void 0,void 0,e.cullBackFaces);var h=b._getInstancesRenderList(a._id,!!a.getReplacementMesh());if(!h.mustReturn){var i=g.getCaps().instancedArrays&&(null!==h.visibleInstances[a._id]||b.hasThinInstances);if(c._isReady(a,i)){var j=c._volumetricLightScatteringPass;b===c.mesh&&(j=a.effect?a._drawWrapper:e._getDrawWrapper());var k=j.effect;if(g.enableEffect(j),i||b._bind(a,k,e.fillMode),b===c.mesh)e.bind(d.getWorldMatrix(),b);else{if(k.setMatrix("viewProjection",f.getTransformMatrix()),e&&e.needAlphaTesting()){g=e.getAlphaTestTexture();k.setTexture("diffuseSampler",g),g&&k.setMatrix("diffuseMatrix",g.getTextureMatrix())}b.useBones&&b.computeBonesUsingShaders&&b.skeleton&&k.setMatrices("mBones",b.skeleton.getTransformMatrices(b))}b._processRendering(d,a,k,qi.a.TriangleFillMode,h,i,function(a,b){return k.setMatrix("world",b)})}}}}},g=new h.b(0,0,0,1);this._volumetricLightScatteringRTT.onBeforeRenderObservable.add(function(){e=a.clearColor,a.clearColor=g}),this._volumetricLightScatteringRTT.onAfterRenderObservable.add(function(){a.clearColor=e}),this._volumetricLightScatteringRTT.customRenderFunction=function(b,c,d,e){var g,h=a.getEngine();if(e.length){for(h.setColorWrite(!1),g=0;gb._alphaIndex?1:a._alphaIndexb._distanceToCamera?-1:0}),h.setAlphaMode(r.a.ALPHA_COMBINE),g=0;g uniform vec4 color; void main(void) { #include gl_FragColor=color; }";X.a.ShadersStore.linePixelShader=wi;V="#include #include attribute vec3 position; attribute vec4 normal; uniform mat4 viewProjection; uniform float width; uniform float aspectRatio; void main(void) { #include mat4 worldViewProjection=viewProjection*finalWorld; vec4 viewPosition=worldViewProjection*vec4(position,1.0); vec4 viewPositionNext=worldViewProjection*vec4(normal.xyz,1.0); vec2 currentScreen=viewPosition.xy/viewPosition.w; vec2 nextScreen=viewPositionNext.xy/viewPositionNext.w; currentScreen.x*=aspectRatio; nextScreen.x*=aspectRatio; vec2 dir=normalize(nextScreen-currentScreen); vec2 normalDir=vec2(-dir.y,dir.x); normalDir*=width/2.0; normalDir.x/=aspectRatio; vec4 offset=vec4(normalDir*normal.w,0.0,0.0); gl_Position=viewPosition+offset; #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) vec4 worldPos=finalWorld*vec4(position,1.0); #include #endif }";X.a.ShadersStore.lineVertexShader=V;cb.a.prototype.disableEdgesRendering=function(){return this._edgesRenderer&&(this._edgesRenderer.dispose(),this._edgesRenderer=null),this},cb.a.prototype.enableEdgesRendering=function(a,b,c){return void 0===a&&(a=.95),void 0===b&&(b=!1),this.disableEdgesRendering(),this._edgesRenderer=new Up(this,a,b,!0,c),this},Object.defineProperty(cb.a.prototype,"edgesRenderer",{get:function(){return this._edgesRenderer},enumerable:!0,configurable:!0}),un.b.prototype.enableEdgesRendering=function(a,b){return void 0===a&&(a=.95),void 0===b&&(b=!1),this.disableEdgesRendering(),this._edgesRenderer=new Vp(this,a,b),this},un.a.prototype.enableEdgesRendering=function(a,b){return void 0===a&&.95,void 0===b&&!1,un.b.prototype.enableEdgesRendering.apply(this,arguments),this};var Tp=function(){this.edges=new Array,this.edgesConnectedCount=0},Up=function(){function a(a,b,c,d,e){var f=this;void 0===b&&(b=.95),void 0===c&&(c=!1),void 0===d&&(d=!0),this.edgesWidthScalerForOrthographic=1e3,this.edgesWidthScalerForPerspective=50,this._linesPositions=new Array,this._linesNormals=new Array,this._linesIndices=new Array,this._buffers={},this._buffersForInstances={},this._checkVerticesInsteadOfIndices=!1,this.isEnabled=!0,this.customInstances=new Ac.a(32),this._source=a,this._checkVerticesInsteadOfIndices=c,this._options=null!=e?e:null,this._epsilon=b,this._prepareRessources(),d&&(null===(a=null==e?void 0:e.useAlternateEdgeFinder)||void 0===a||a?this._generateEdgesLinesAlternate():this._generateEdgesLines()),this._meshRebuildObserver=this._source.onRebuildObservable.add(function(){f._rebuild()}),this._meshDisposeObserver=this._source.onDisposeObservable.add(function(){f.dispose()})}return Object.defineProperty(a.prototype,"linesPositions",{get:function(){return this._linesPositions},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"linesNormals",{get:function(){return this._linesNormals},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"linesIndices",{get:function(){return this._linesIndices},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"lineShader",{get:function(){return this._lineShader},set:function(a){this._lineShader=a},enumerable:!1,configurable:!0}),a.GetShader=function(a){if(!a._edgeRenderLineShader){var b=new zh.a("lineShader",a,"line",{attributes:["position","normal"],uniforms:["world","viewProjection","color","width","aspectRatio"]});b.disableDepthWrite=!0,b.backFaceCulling=!1,a._edgeRenderLineShader=b}return a._edgeRenderLineShader},a.prototype._prepareRessources=function(){this._lineShader||(this._lineShader=a.GetShader(this._source.getScene()))},a.prototype._rebuild=function(){var a=this._buffers[W.b.PositionKind];a&&a._rebuild(),(a=this._buffers[W.b.NormalKind])&&a._rebuild();a=this._source.getScene().getEngine();this._ib=a.createIndexBuffer(this._linesIndices)},a.prototype.dispose=function(){this._source.onRebuildObservable.remove(this._meshRebuildObserver),this._source.onDisposeObservable.remove(this._meshDisposeObserver);var a=this._buffers[W.b.PositionKind];a&&(a.dispose(),this._buffers[W.b.PositionKind]=null),(a=this._buffers[W.b.NormalKind])&&(a.dispose(),this._buffers[W.b.NormalKind]=null),this._ib&&this._source.getScene().getEngine()._releaseBuffer(this._ib),this._lineShader.dispose()},a.prototype._processEdgeForAdjacencies=function(a,b,c,d,e){return a===c&&b===d||a===d&&b===c?0:a===d&&b===e||a===e&&b===d?1:a===e&&b===c||a===c&&b===e?2:-1},a.prototype._processEdgeForAdjacenciesWithVertices=function(a,b,c,d,e){var f=1e-10;return a.equalsWithEpsilon(c,f)&&b.equalsWithEpsilon(d,f)||a.equalsWithEpsilon(d,f)&&b.equalsWithEpsilon(c,f)?0:a.equalsWithEpsilon(d,f)&&b.equalsWithEpsilon(e,f)||a.equalsWithEpsilon(e,f)&&b.equalsWithEpsilon(d,f)?1:a.equalsWithEpsilon(e,f)&&b.equalsWithEpsilon(c,f)||a.equalsWithEpsilon(c,f)&&b.equalsWithEpsilon(e,f)?2:-1},a.prototype._checkEdge=function(a,b,c,d,e){var f;void 0===b?f=!0:f=g.e.Dot(c[a],c[b])=0&&b.push(c);for(c=0;c=a[0].length&&a[1].length>=a[2].length?f=1:a[2].length>=a[0].length&&a[2].length>=a[1].length&&(f=2);for(var g=0;g<3;++g)g===f?a[g].sort(function(a,b){return a[1]b[1]?1:0}):a[g].sort(function(a,b){return a[1]>b[1]?-1:a[1]=f+1;--j)e(a[j%3],h,j!==f+2?d[c[b+(j+1)%3]]:-1);a=h.length;c.push(d[c[b+f]],g[0],h[0]),c.push(d[c[b+(f+1)%3]],h[a-1],g[i-1]);for(var j=i<=a,e=j?i:a,d=j?a:i,f=j?i-1:a-1,k=j?0:1,i=i+a-2,a=0,l=0,m=j?g:h,j=j?h:g,h=0;i-->0;){k?c.push(m[a],j[l]):c.push(j[l],m[a]);(h+=e)>=d&&aj){r=h;h=j,j=r}(D=G[K=h+"_"+j])?D.done||(g.e.Dot(aa,D.normal)0||this._source.hasThinInstances)},a.prototype.render=function(){var a=this._source.getScene();if(this.isReady()&&a.activeCamera){var b=this._source.hasInstances&&this.customInstances.length>0,c=b||this._source.hasThinInstances,d=0;if(c)if(this._buffersForInstances.world0=this._source.getVertexBuffer("world0"),this._buffersForInstances.world1=this._source.getVertexBuffer("world1"),this._buffersForInstances.world2=this._source.getVertexBuffer("world2"),this._buffersForInstances.world3=this._source.getVertexBuffer("world3"),b){b=this._source._instanceDataStorage;if(d=this.customInstances.length,!b.instancesData)return void (this._source.getScene()._activeMeshesFrozen||this.customInstances.reset());if(!b.isFrozen){for(var e=0,f=0;f0&&(b.push(!0),c.push(!1));this._multiRenderAttachments=this._engine.buildTextureLayout(a),this._clearAttachments=this._engine.buildTextureLayout(b),this._defaultAttachments=this._engine.buildTextureLayout(c)},a.prototype._resetLayout=function(){for(var b=0;b=0;a--)this.renderTargets[a].dispose();for(a=0;a #include #include #include varying vec2 vUV; uniform vec2 texelSize; uniform sampler2D textureSampler; uniform sampler2D irradianceSampler; uniform sampler2D depthSampler; uniform sampler2D albedoSampler; uniform vec2 viewportSize; uniform float metersPerUnit; const float LOG2_E=1.4426950408889634; const float SSS_PIXELS_PER_SAMPLE=4.; const int _SssSampleBudget=40; #define rcp(x) 1./x #define Sq(x) x*x #define SSS_BILATERAL_FILTER true vec3 EvalBurleyDiffusionProfile(float r,vec3 S) { vec3 exp_13=exp2(((LOG2_E*(-1.0/3.0))*r)*S); vec3 expSum=exp_13*(1.+exp_13*exp_13); return (S*rcp(8.*PI))*expSum; } vec2 SampleBurleyDiffusionProfile(float u,float rcpS) { u=1.-u; float g=1.+(4.*u)*(2.*u+sqrt(1.+(4.*u)*u)); float n=exp2(log2(g)*(-1.0/3.0)); float p=(g*n)*n; float c=1.+p+n; float d=(3./LOG2_E*2.)+(3./LOG2_E)*log2(u); float x=(3./LOG2_E)*log2(c)-d; float rcpExp=((c*c)*c)*rcp((4.*u)*((c*c)+(4.*u)*(4.*u))); float r=x*rcpS; float rcpPdf=(8.*PI*rcpS)*rcpExp; return vec2(r,rcpPdf); } vec3 ComputeBilateralWeight(float xy2,float z,float mmPerUnit,vec3 S,float rcpPdf) { #ifndef SSS_BILATERAL_FILTER z=0.; #endif float r=sqrt(xy2+(z*mmPerUnit)*(z*mmPerUnit)); float area=rcpPdf; #if SSS_CLAMP_ARTIFACT return clamp(EvalBurleyDiffusionProfile(r,S)*area,0.0,1.0); #else return EvalBurleyDiffusionProfile(r,S)*area; #endif } void EvaluateSample(int i,int n,vec3 S,float d,vec3 centerPosVS,float mmPerUnit,float pixelsPerMm, float phase,inout vec3 totalIrradiance,inout vec3 totalWeight) { float scale=rcp(float(n)); float offset=rcp(float(n))*0.5; float sinPhase,cosPhase; sinPhase=sin(phase); cosPhase=cos(phase); vec2 bdp=SampleBurleyDiffusionProfile(float(i)*scale+offset,d); float r=bdp.x; float rcpPdf=bdp.y; float phi=SampleDiskGolden(i,n).y; float sinPhi,cosPhi; sinPhi=sin(phi); cosPhi=cos(phi); float sinPsi=cosPhase*sinPhi+sinPhase*cosPhi; float cosPsi=cosPhase*cosPhi-sinPhase*sinPhi; vec2 vec=r*vec2(cosPsi,sinPsi); vec2 position; float xy2; position=vUV+round((pixelsPerMm*r)*vec2(cosPsi,sinPsi))*texelSize; xy2=r*r; vec4 textureSample=texture2D(irradianceSampler,position); float viewZ=texture2D(depthSampler,position).r; vec3 irradiance=textureSample.rgb; if (testLightingForSSS(textureSample.a)) { float relZ=viewZ-centerPosVS.z; vec3 weight=ComputeBilateralWeight(xy2,relZ,mmPerUnit,S,rcpPdf); totalIrradiance+=weight*irradiance; totalWeight+=weight; } else { } } void main(void) { vec4 irradianceAndDiffusionProfile=texture2D(irradianceSampler,vUV); vec3 centerIrradiance=irradianceAndDiffusionProfile.rgb; int diffusionProfileIndex=int(round(irradianceAndDiffusionProfile.a*255.)); float centerDepth=0.; vec4 inputColor=texture2D(textureSampler,vUV); bool passedStencilTest=testLightingForSSS(irradianceAndDiffusionProfile.a); if (passedStencilTest) { centerDepth=texture2D(depthSampler,vUV).r; } if (!passedStencilTest) { gl_FragColor=inputColor; return; } float distScale=1.; vec3 S=diffusionS[diffusionProfileIndex]; float d=diffusionD[diffusionProfileIndex]; float filterRadius=filterRadii[diffusionProfileIndex]; vec2 centerPosNDC=vUV; vec2 cornerPosNDC=vUV+0.5*texelSize; vec3 centerPosVS=vec3(centerPosNDC*viewportSize,1.0)*centerDepth; vec3 cornerPosVS=vec3(cornerPosNDC*viewportSize,1.0)*centerDepth; float mmPerUnit=1000.*(metersPerUnit*rcp(distScale)); float unitsPerMm=rcp(mmPerUnit); float unitsPerPixel=2.*abs(cornerPosVS.x-centerPosVS.x); float pixelsPerMm=rcp(unitsPerPixel)*unitsPerMm; float filterArea=PI*Sq(filterRadius*pixelsPerMm); int sampleCount=int(filterArea*rcp(SSS_PIXELS_PER_SAMPLE)); int sampleBudget=_SssSampleBudget; int texturingMode=0; vec3 albedo=texture2D(albedoSampler,vUV).rgb; if (distScale == 0. || sampleCount<1) { #ifdef DEBUG_SSS_SAMPLES vec3 green=vec3(0.,1.,0.); gl_FragColor=vec4(green,1.0); return; #endif gl_FragColor=vec4(inputColor.rgb+albedo*centerIrradiance,1.0); return; } #ifdef DEBUG_SSS_SAMPLES vec3 red=vec3(1.,0.,0.); vec3 blue=vec3(0.,0.,1.); gl_FragColor=vec4(mix(blue,red,clamp(float(sampleCount)/float(sampleBudget),0.0,1.0)),1.0); return; #endif float phase=0.; int n=min(sampleCount,sampleBudget); vec3 centerWeight=vec3(0.); vec3 totalIrradiance=vec3(0.); vec3 totalWeight=vec3(0.); for (int i=0; i=5)return q.a.Error("You already reached the maximum number of diffusion profiles."),0;for(var b=0;b #include #include[0..maxSimultaneousMorphTargets] uniform float offset; #include uniform mat4 viewProjection; #ifdef ALPHATEST varying vec2 vUV; uniform mat4 diffuseMatrix; #ifdef UV1 attribute vec2 uv; #endif #ifdef UV2 attribute vec2 uv2; #endif #endif #include void main(void) { vec3 positionUpdated=position; vec3 normalUpdated=normal; #ifdef UV1 vec2 uvUpdated=uv; #endif #include #include[0..maxSimultaneousMorphTargets] vec3 offsetPosition=positionUpdated+(normalUpdated*offset); #include #include gl_Position=viewProjection*finalWorld*vec4(offsetPosition,1.0); #ifdef ALPHATEST #ifdef UV1 vUV=vec2(diffuseMatrix*vec4(uvUpdated,1.0,0.0)); #endif #ifdef UV2 vUV=vec2(diffuseMatrix*vec4(uv2,1.0,0.0)); #endif #endif #include } ";X.a.ShadersStore.outlineVertexShader=wi;O.a.prototype.getOutlineRenderer=function(){return this._outlineRenderer||(this._outlineRenderer=new bq(this)),this._outlineRenderer},Object.defineProperty(R.a.prototype,"renderOutline",{get:function(){return this._renderOutline},set:function(a){a&&this.getScene().getOutlineRenderer(),this._renderOutline=a},enumerable:!0,configurable:!0}),Object.defineProperty(R.a.prototype,"renderOverlay",{get:function(){return this._renderOverlay},set:function(a){a&&this.getScene().getOutlineRenderer(),this._renderOverlay=a},enumerable:!0,configurable:!0});var bq=function(){function a(b){this.name=Oa.a.NAME_OUTLINERENDERER,this.zOffset=1,this.zOffsetUnits=4,this.scene=b,this._engine=b.getEngine(),this.scene._addComponent(this),this._nameForDrawWrapper=r.a.SUBMESH_DRAWWRAPPER_OUTLINERENDERER_PREFIX+a._Counter++}return a.prototype.register=function(){this.scene._beforeRenderingMeshStage.registerStep(Oa.a.STEP_BEFORERENDERINGMESH_OUTLINE,this,this._beforeRenderingMesh),this.scene._afterRenderingMeshStage.registerStep(Oa.a.STEP_AFTERRENDERINGMESH_OUTLINE,this,this._afterRenderingMesh)},a.prototype.rebuild=function(){},a.prototype.dispose=function(){},a.prototype.render=function(a,b,c){void 0===c&&(c=!1);var d=this.scene,e=d.getEngine(),f=e.getCaps().instancedArrays&&(null!==b.visibleInstances[a._id]&&void 0!==b.visibleInstances[a._id]||a.getRenderingMesh().hasThinInstances);if(this.isReady(a,f)){var g=a.getMesh();g=g._internalAbstractMeshDataInfo._actAsRegularMesh?g:null;var h=a.getRenderingMesh();g=g||h;var i=a.getMaterial();if(i&&d.activeCamera){var j=a._getDrawWrapper(this._nameForDrawWrapper),k=Ec.a.GetEffect(j);if(e.enableEffect(j),i.useLogarithmicDepth&&k.setFloat("logarithmicDepthConstant",2/(Math.log(d.activeCamera.maxZ+1)/Math.LN2)),k.setFloat("offset",c?0:h.outlineWidth),k.setColor4("color",c?h.overlayColor:h.outlineColor,c?h.overlayAlpha:i.alpha),k.setMatrix("viewProjection",d.getTransformMatrix()),k.setMatrix("world",g.getWorldMatrix()),h.useBones&&h.computeBonesUsingShaders&&h.skeleton&&k.setMatrices("mBones",h.skeleton.getTransformMatrices(h)),h.morphTargetManager&&h.morphTargetManager.isUsingTextureForTargets&&h.morphTargetManager._bind(k),Yh.a.BindMorphTargetParameters(h,k),f||h._bind(a,k,i.fillMode),i&&i.needAlphaTesting()){j=i.getAlphaTestTexture();j&&(k.setTexture("diffuseSampler",j),k.setMatrix("diffuseMatrix",j.getTextureMatrix()))}e.setZOffset(-this.zOffset),e.setZOffsetUnits(-this.zOffsetUnits),h._processRendering(g,a,k,i.fillMode,b,f,function(a,b){k.setMatrix("world",b)}),e.setZOffset(0),e.setZOffsetUnits(0)}}},a.prototype.isReady=function(a,b){var c=[],d=[W.b.PositionKind,W.b.NormalKind],e=a._getDrawWrapper(this._nameForDrawWrapper,!0),f=e.effect,g=e.defines,h=a.getMesh(),i=a.getMaterial();i&&(i.needAlphaTesting()&&(c.push("#define ALPHATEST"),h.isVerticesDataPresent(W.b.UVKind)&&(d.push(W.b.UVKind),c.push("#define UV1")),h.isVerticesDataPresent(W.b.UV2Kind)&&(d.push(W.b.UV2Kind),c.push("#define UV2"))),i.useLogarithmicDepth&&c.push("#define LOGARITHMICDEPTH")),h.useBones&&h.computeBonesUsingShaders?(d.push(W.b.MatricesIndicesKind),d.push(W.b.MatricesWeightsKind),h.numBoneInfluencers>4&&(d.push(W.b.MatricesIndicesExtraKind),d.push(W.b.MatricesWeightsExtraKind)),c.push("#define NUM_BONE_INFLUENCERS "+h.numBoneInfluencers),c.push("#define BonesPerMesh "+(h.skeleton?h.skeleton.bones.length+1:0))):c.push("#define NUM_BONE_INFLUENCERS 0");i=h.morphTargetManager;var j=0;i&&i.numInfluencers>0&&(j=i.numInfluencers,c.push("#define MORPHTARGETS"),c.push("#define NUM_MORPH_INFLUENCERS "+j),i.isUsingTextureForTargets&&c.push("#define MORPHTARGETS_TEXTURE"),Yh.a.PrepareAttributesForMorphTargetsInfluencers(d,h,j)),b&&(c.push("#define INSTANCES"),Yh.a.PushAttributesForInstances(d),a.getRenderingMesh().hasThinInstances&&c.push("#define THIN_INSTANCES"));i=c.join(" ");return g!==i&&(g=i,f=this.scene.getEngine().createEffect("outline",d,["world","mBones","viewProjection","diffuseMatrix","offset","color","logarithmicDepthConstant","morphTargetInfluences","morphTargetTextureInfo","morphTargetTextureIndices"],["diffuseSampler","morphTargets"],i,void 0,void 0,void 0,{maxSimultaneousMorphTargets:j})),e.setEffect(f,g),f.isReady()},a.prototype._beforeRenderingMesh=function(b,c,d){if(this._savedDepthWrite=this._engine.getDepthWrite(),b.renderOutline){var e=c.getMaterial();e&&e.needAlphaBlendingForMesh(b)&&(this._engine.cacheStencilState(),this._engine.setDepthWrite(!1),this._engine.setColorWrite(!1),this._engine.setStencilBuffer(!0),this._engine.setStencilOperationPass(r.a.REPLACE),this._engine.setStencilFunction(r.a.ALWAYS),this._engine.setStencilMask(a._StencilReference),this._engine.setStencilFunctionReference(a._StencilReference),this._engine.stencilStateComposer.useStencilGlobalOnly=!0,this.render(c,d,!0),this._engine.setColorWrite(!0),this._engine.setStencilFunction(r.a.NOTEQUAL)),this._engine.setDepthWrite(!1),this.render(c,d),this._engine.setDepthWrite(this._savedDepthWrite),e&&e.needAlphaBlendingForMesh(b)&&(this._engine.stencilStateComposer.useStencilGlobalOnly=!1,this._engine.restoreStencilState())}},a.prototype._afterRenderingMesh=function(a,b,c){if(a.renderOverlay){var d=this._engine.getAlphaMode(),e=this._engine.alphaState.alphaBlend;this._engine.setAlphaMode(r.a.ALPHA_COMBINE),this.render(b,c,!0),this._engine.setAlphaMode(d),this._engine.setDepthWrite(this._savedDepthWrite),this._engine.alphaState.alphaBlend=e}a.renderOutline&&this._savedDepthWrite&&(this._engine.setDepthWrite(!0),this._engine.setColorWrite(!1),this.render(b,c),this._engine.setColorWrite(!0))},a._Counter=0,a._StencilReference=4,a}(),cq=c(169),dq=c(172),eq=function(a){function b(b,c){var d=a.call(this)||this;return d.name=b,d.animations=new Array,d.isPickable=!1,d.useAlphaForPicking=!1,d.onDisposeObservable=new f.c,d._onAnimationEnd=null,d._endAnimation=function(){d._onAnimationEnd&&d._onAnimationEnd(),d.disposeWhenFinishedAnimating&&d.dispose()},d.color=new h.b(1,1,1,1),d.position=g.e.Zero(),d._manager=c,d._manager.sprites.push(d),d.uniqueId=d._manager.scene.getUniqueId(),d}return Object(l.d)(b,a),Object.defineProperty(b.prototype,"size",{get:function(){return this.width},set:function(a){this.width=a,this.height=a},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"manager",{get:function(){return this._manager},enumerable:!1,configurable:!0}),b.prototype.getClassName=function(){return"Sprite"},Object.defineProperty(b.prototype,"fromIndex",{get:function(){return this._fromIndex},set:function(a){this.playAnimation(a,this._toIndex,this._loopAnimation,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"toIndex",{get:function(){return this._toIndex},set:function(a){this.playAnimation(this._fromIndex,a,this._loopAnimation,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"loopAnimation",{get:function(){return this._loopAnimation},set:function(a){this.playAnimation(this._fromIndex,this._toIndex,a,this._delay,this._onAnimationEnd)},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"delay",{get:function(){return Math.max(this._delay,1)},set:function(a){this.playAnimation(this._fromIndex,this._toIndex,this._loopAnimation,a,this._onAnimationEnd)},enumerable:!1,configurable:!0}),b.prototype.playAnimation=function(b,c,d,e,f){void 0===f&&(f=null),this._onAnimationEnd=f,a.prototype.playAnimation.call(this,b,c,d,e,this._endAnimation)},b.prototype.dispose=function(){for(var a=0;athis._delay&&(this._time=this._time%this._delay,this.cellIndex+=this._direction,(this._direction>0&&this.cellIndex>this._toIndex||this._direction<0&&this.cellIndex0?this._fromIndex:this._toIndex:(this.cellIndex=this._toIndex,this._animationStarted=!1,this._onBaseAnimationEnd&&this._onBaseAnimationEnd()))))},a}());O.a.prototype._internalPickSprites=function(a,b,c,d){if(!Td.a)return null;var e=null;if(!d){if(!this.activeCamera)return null;d=this.activeCamera}if(this.spriteManagers.length>0)for(var f=0;f=e.distance))&&(e=g,c))break}}return e||new Td.a},O.a.prototype._internalMultiPickSprites=function(a,b,c){if(!Td.a)return null;var d=new Array;if(!c){if(!this.activeCamera)return null;c=this.activeCamera}if(this.spriteManagers.length>0)for(var e=0;e0&&(c=e.pickSprite(a,b,this._spritePredicate,!1,e.cameraToUseForPointers||void 0))&&c.hit&&c.pickedSprite&&c.pickedSprite.actionManager){switch(e._pickedDownSprite=c.pickedSprite,d.button){case 0:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnLeftPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d));break;case 1:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnCenterPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d));break;case 2:c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnRightPickTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d))}c.pickedSprite.actionManager&&c.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickDownTrigger,k.a.CreateNewFromSprite(c.pickedSprite,e,d))}return c},a.prototype._pointerUp=function(a,b,c,d){var e=this.scene;if(e.spriteManagers.length>0){a=e.pickSprite(a,b,this._spritePredicate,!1,e.cameraToUseForPointers||void 0);a&&(a.hit&&a.pickedSprite&&a.pickedSprite.actionManager&&(a.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickUpTrigger,k.a.CreateNewFromSprite(a.pickedSprite,e,d)),a.pickedSprite.actionManager&&(this.scene._inputManager._isPointerSwiping()||a.pickedSprite.actionManager.processTrigger(r.a.ACTION_OnPickTrigger,k.a.CreateNewFromSprite(a.pickedSprite,e,d)))),e._pickedDownSprite&&e._pickedDownSprite.actionManager&&e._pickedDownSprite!==a.pickedSprite&&e._pickedDownSprite.actionManager.processTrigger(r.a.ACTION_OnPickOutTrigger,k.a.CreateNewFromSprite(e._pickedDownSprite,e,d)))}return c},a}();X.a.IncludesShadersStore.imageProcessingCompatibility="#ifdef IMAGEPROCESSINGPOSTPROCESS gl_FragColor.rgb=pow(gl_FragColor.rgb,vec3(2.2)); #endif";V="uniform bool alphaTest; varying vec4 vColor; varying vec2 vUV; uniform sampler2D diffuseSampler; #include void main(void) { vec4 color=texture2D(diffuseSampler,vUV); if (alphaTest) { if (color.a<0.95) discard; } color*=vColor; #include gl_FragColor=color; #include }";X.a.ShadersStore.spritesPixelShader=V;a=" attribute vec4 position; attribute vec2 options; attribute vec2 offsets; attribute vec2 inverts; attribute vec4 cellInfo; attribute vec4 color; uniform mat4 view; uniform mat4 projection; varying vec2 vUV; varying vec4 vColor; #include void main(void) { vec3 viewPos=(view*vec4(position.xyz,1.0)).xyz; vec2 cornerPos; float angle=position.w; vec2 size=vec2(options.x,options.y); vec2 offset=offsets.xy; cornerPos=vec2(offset.x-0.5,offset.y-0.5)*size; vec3 rotatedCorner; rotatedCorner.x=cornerPos.x*cos(angle)-cornerPos.y*sin(angle); rotatedCorner.y=cornerPos.x*sin(angle)+cornerPos.y*cos(angle); rotatedCorner.z=0.; viewPos+=rotatedCorner; gl_Position=projection*vec4(viewPos,1.0); vColor=color; vec2 uvOffset=vec2(abs(offset.x-inverts.x),abs(1.0-offset.y-inverts.y)); vec2 uvPlace=cellInfo.xy; vec2 uvSize=cellInfo.zw; vUV.x=uvPlace.x+uvSize.x*uvOffset.x; vUV.y=uvPlace.y+uvSize.y*uvOffset.y; #ifdef FOG vFogDistance=viewPos; #endif }";X.a.ShadersStore.spritesVertexShader=a;var gq=function(){function a(a,b,c,d){void 0===c&&(c=.01),void 0===d&&(d=null),this.blendMode=r.a.ALPHA_COMBINE,this.autoResetAlpha=!0,this.disableDepthWrite=!1,this.fogEnabled=!0,this._useVAO=!1,this._useInstancing=!1,this._vertexBuffers={},this._capacity=b,this._epsilon=c,this._engine=a,this._useInstancing=a.getCaps().instancedArrays,this._useVAO=a.getCaps().vertexArrayObject&&!a.disableVertexArrayObjects,this._scene=d,this._drawWrapperBase=new Ec.a(a),this._drawWrapperFog=new Ec.a(a),this._useInstancing||this._buildIndexBuffer(),this._vertexBufferSize=this._useInstancing?16:18,this._vertexData=new Float32Array(b*this._vertexBufferSize*(this._useInstancing?1:4)),this._buffer=new W.a(a,this._vertexData,!0,this._vertexBufferSize);c=this._buffer.createVertexBuffer(W.b.PositionKind,0,4,this._vertexBufferSize,this._useInstancing);d=this._buffer.createVertexBuffer("options",4,2,this._vertexBufferSize,this._useInstancing);b=6;if(this._useInstancing){var e=new Float32Array([0,0,1,0,0,1,1,1]);this._spriteBuffer=new W.a(a,e,!1,2),a=this._spriteBuffer.createVertexBuffer("offsets",0,2)}else a=this._buffer.createVertexBuffer("offsets",b,2,this._vertexBufferSize,this._useInstancing),b+=2;e=this._buffer.createVertexBuffer("inverts",b,2,this._vertexBufferSize,this._useInstancing);var f=this._buffer.createVertexBuffer("cellInfo",b+2,4,this._vertexBufferSize,this._useInstancing);b=this._buffer.createVertexBuffer(W.b.ColorKind,b+6,4,this._vertexBufferSize,this._useInstancing);this._vertexBuffers[W.b.PositionKind]=c,this._vertexBuffers.options=d,this._vertexBuffers.offsets=a,this._vertexBuffers.inverts=e,this._vertexBuffers.cellInfo=f,this._vertexBuffers[W.b.ColorKind]=b,this._drawWrapperBase.effect=this._engine.createEffect("sprites",[W.b.PositionKind,"options","offsets","inverts","cellInfo",W.b.ColorKind],["view","projection","textureInfos","alphaTest"],["diffuseSampler"],""),this._scene&&(this._drawWrapperFog.effect=this._scene.getEngine().createEffect("sprites",[W.b.PositionKind,"options","offsets","inverts","cellInfo",W.b.ColorKind],["view","projection","textureInfos","alphaTest","vFogInfos","vFogColor"],["diffuseSampler"],"#define FOG"))}return Object.defineProperty(a.prototype,"capacity",{get:function(){return this._capacity},enumerable:!1,configurable:!0}),a.prototype.render=function(a,b,c,d,e){if(void 0===e&&(e=null),this.texture&&this.texture.isReady()&&a.length){var f=this._drawWrapperBase,g=!1;this.fogEnabled&&this._scene&&this._scene.fogEnabled&&0!==this._scene.fogMode&&(f=this._drawWrapperFog,g=!0);var h=f.effect;if(h.isReady()){for(var i=this._engine,j=!(!this._scene||!this._scene.useRightHandedSystem),k=this.texture.getBaseSize(),l=Math.min(this._capacity,a.length),q=0,s=!0,t=0;t>0;b._xOffset=(b.cellIndex-h*g)*this.cellWidth/e.width,b._yOffset=h*this.cellHeight/e.height,b._xSize=this.cellWidth,b._ySize=this.cellHeight}this._vertexData[a]=b.position.x,this._vertexData[a+1]=b.position.y,this._vertexData[a+2]=b.position.z,this._vertexData[a+3]=b.angle,this._vertexData[a+4]=b.width,this._vertexData[a+5]=b.height,this._useInstancing?a-=2:(this._vertexData[a+6]=c,this._vertexData[a+7]=d),this._vertexData[a+8]=f?b.invertU?0:1:b.invertU?1:0,this._vertexData[a+9]=b.invertV?1:0,this._vertexData[a+10]=b._xOffset,this._vertexData[a+11]=b._yOffset,this._vertexData[a+12]=b._xSize/e.width,this._vertexData[a+13]=b._ySize/e.height,this._vertexData[a+14]=b.color.r,this._vertexData[a+15]=b.color.g,this._vertexData[a+16]=b.color.b,this._vertexData[a+17]=b.color.a},a.prototype._buildIndexBuffer=function(){for(var a=[],b=0,c=0;c0);f=a.substring(0,b-1)+".json";var g=new XMLHttpRequest;g.open("GET",f,!0),g.onerror=function(){q.a.Error("JSON ERROR: Unable to load JSON file."),c._fromPacked=!1,c._packedAndReady=!1},g.onload=function(){try{var a=JSON.parse(g.response),b=Reflect.ownKeys(a.frames);c._spriteMap=b,c._packedAndReady=!0,c._cellData=a.frames}catch(a){throw c._fromPacked=!1,c._packedAndReady=!1,new Error("Invalid JSON format. Please check documentation for format specifications.")}},g.send()}},a.prototype._checkTextureAlpha=function(a,b,c,d,e){if(!a.useAlphaForPicking||!this.texture)return!0;var f=this.texture.getSize();this._textureContent||(this._textureContent=new Uint8Array(f.width*f.height*4),this.texture.readPixels(0,0,this._textureContent));var h=g.c.Vector3[0];h.copyFrom(b.direction),h.normalize(),h.scaleInPlace(c),h.addInPlace(b.origin);c=(h.x-d.x)/(e.x-d.x)-.5;b=1-(h.y-d.y)/(e.y-d.y)-.5;h=a.angle;e=c*Math.cos(h)-b*Math.sin(h)+.5;d=c*Math.sin(h)+b*Math.cos(h)+.5;c=a._xOffset*f.width+e*a._xSize|0;b=a._yOffset*f.height+d*a._ySize|0;return this._textureContent[4*(c+b*f.width)+3]>.5},a.prototype.intersects=function(a,b,c,d){for(var e=Math.min(this.capacity,this.sprites.length),f=g.e.Zero(),h=g.e.Zero(),i=Number.MAX_VALUE,j=null,k=g.c.Vector3[0],l=g.c.Vector3[1],b=b.getViewMatrix(),m=a,s=a,t=0;tv){if(!this._checkTextureAlpha(u,m,v,f,h))continue;if(s=m,i=v,j=u,d)break}}}}if(j){v=new Td.a;b.invertToRef(g.c.Matrix[0]),v.hit=!0,v.pickedSprite=j,v.distance=i;u=g.c.Vector3[2];return u.copyFrom(s.direction),u.normalize(),u.scaleInPlace(i),s.origin.addToRef(u,k),v.pickedPoint=g.e.TransformCoordinates(k,g.c.Matrix[0]),v}return null},a.prototype.multiIntersects=function(a,b,c){for(var d,e=Math.min(this.capacity,this.sprites.length),f=g.e.Zero(),h=g.e.Zero(),i=[],j=g.c.Vector3[0].copyFromFloats(0,0,0),k=g.c.Vector3[1].copyFromFloats(0,0,0),b=b.getViewMatrix(),l=0;l0&&(a+=" "),a+=this._tileMaps[b]._texture._bufferView.toString();b=document.createElement("a");b.href="data:octet/stream;charset=utf-8,"+encodeURI(a),b.target="_blank",b.download=this.name+".tilemaps",b.click(),b.remove()},a.prototype.loadTileMaps=function(a){var b=this,c=new XMLHttpRequest;c.open("GET",a);var d=this.options.layerCount||0;c.onload=function(){for(var a=c.response.split(" "),e=0;e-1&&this._tasks.splice(a,1)},a.prototype._decreaseWaitingTasksCount=function(a){this._waitingTasksCount--;try{this.onProgress&&this.onProgress(this._waitingTasksCount,this._totalTasksCount,a),this.onProgressObservable.notifyObservers(new pq(this._waitingTasksCount,this._totalTasksCount,a))}catch(a){q.a.Error("Error running progress callbacks."),!1}if(0===this._waitingTasksCount){try{var b=this._tasks.slice();this.onFinish&&this.onFinish(b);for(var c=0,b=b;c-1&&this._tasks.splice(a,1)}this.onTasksDoneObservable.notifyObservers(this._tasks)}catch(a){q.a.Error("Error running tasks-done callbacks."),!1}this._isLoading=!1,this.autoHideLoadingUI&&this._scene.getEngine().hideLoadingUI()}},a.prototype._runTask=function(a){var b=this,c=function(c,d){a._setErrorObject(c,d),b.onTaskError&&b.onTaskError(a),b.onTaskErrorObservable.notifyObservers(a),b._decreaseWaitingTasksCount(a)};a.run(this._scene,function(){try{b.onTaskSuccess&&b.onTaskSuccess(a),b.onTaskSuccessObservable.notifyObservers(a),b._decreaseWaitingTasksCount(a)}catch(a){c("Error executing task success callbacks",a)}},c)},a.prototype.reset=function(){return this._isLoading=!1,this._tasks=new Array,this},a.prototype.load=function(){if(this._isLoading)return this;if(this._isLoading=!0,this._waitingTasksCount=this._tasks.length,this._totalTasksCount=this._tasks.length,0===this._waitingTasksCount)return this._isLoading=!1,this.onFinish&&this.onFinish(this._tasks),this.onTasksDoneObservable.notifyObservers(this._tasks),this;this.useDefaultLoadingScreen&&this._scene.getEngine().displayLoadingUI();for(var a=0;a=0&&this._meshes.splice(a,1),this._centerPosition=this._centerMesh.getAbsolutePosition().clone();for(b=0;b0&&this._textureLoadingCallback(a)}this._currentScene.render()}},a.prototype.drag=function(a){a.stopPropagation(),a.preventDefault()},a.prototype.drop=function(a){a.stopPropagation(),a.preventDefault(),this.loadFiles(a)},a.prototype._traverseFolder=function(a,b,c,d){var e=this,f=a.createReader(),g=a.fullPath.replace(/^//,"").replace(/(.+?)/?$/,"$1/");f.readEntries(function(a){c.count+=a.length;for(var f=0,a=a;f0)){for(var c=new Array,d=[],a=a.dataTransfer?a.dataTransfer.items:null,e=0;e0&&q.a.ClearLogCache(),this._engine.stopRenderLoop()),hh.ShowLoadingScreen=!1,this._engine.displayLoadingUI(),this.loadAsync(this._sceneFileToLoad,this._progressCallback).then(function(b){a._currentScene&&a._currentScene.dispose(),a._currentScene=b,a._sceneLoadedCallback&&a._sceneLoadedCallback(a._sceneFileToLoad,a._currentScene),a._currentScene.executeWhenReady(function(){a._engine.hideLoadingUI(),a._engine.runRenderLoop(function(){a.renderFunction()})})})["catch"](function(b){a._engine.hideLoadingUI(),a._errorCallback&&a._errorCallback(a._sceneFileToLoad,a._currentScene,b.message)})):q.a.Error("Please provide a valid .babylon file.")},a}();f.c.prototype.runCoroutineAsync=function(a){var b=this;return this.coroutineIterators||(this.coroutineIterators=[],this.add(function(){for(var a=function(a){if(b.coroutineIterators[a].paused)return"continue";var c=b.coroutineIterators[a].iterator.next();if(c.value){var d=b.coroutineIterators[a];d.paused=!0,c.value.then(function(){d.paused=!1})}c.done&&(b.coroutineIterators[a].resolver(),b.coroutineIterators.splice(a,1))},c=b.coroutineIterators.length-1;c>=0;--c)a(c)})),new Promise(function(c,d){var e;null===(e=b.coroutineIterators)||void 0===e||e.push({iterator:a,resolver:c,rejecter:d,paused:!1})})},f.c.prototype.cancelAllCoroutines=function(){this.coroutineIterators.forEach(function(a){a.rejecter()}),this.coroutineIterators=[]};var Eq=c(173),Fq=c(168),Gq=function(){function a(a){void 0===a&&(a=0),this.priority=a}return a.prototype.getDescription=function(){return""},a.prototype.apply=function(a,b){return!0},a}(),Hq=function(a){function b(b,c,d){void 0===b&&(b=0),void 0===c&&(c=1024),void 0===d&&(d=.5);var e=a.call(this,b)||this;return e.priority=b,e.maximumSize=c,e.step=d,e}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Reducing render target texture size to "+this.maximumSize},b.prototype.apply=function(a,b){for(var b=!0,c=0;cthis.maximumSize&&(d.scale(this.step),b=!1)}}return b},b}(Gq),Iq=function(a){function b(b,c,d){void 0===b&&(b=0),void 0===c&&(c=2),void 0===d&&(d=.25);var e=a.call(this,b)||this;return e.priority=b,e.maximumScale=c,e.step=d,e._currentScale=-1,e._directionOffset=1,e}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Setting hardware scaling level to "+this._currentScale},b.prototype.apply=function(a,b){return-1===this._currentScale&&(this._currentScale=a.getEngine().getHardwareScalingLevel(),this._currentScale>this.maximumScale&&(this._directionOffset=-1)),this._currentScale+=this._directionOffset*this.step,a.getEngine().setHardwareScalingLevel(this._currentScale),1===this._directionOffset?this._currentScale>=this.maximumScale:this._currentScale<=this.maximumScale},b}(Gq),Jq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning shadows on/off"},b.prototype.apply=function(a,b){return a.shadowsEnabled=b.isInImprovementMode,!0},b}(Gq),Kq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning post-processes on/off"},b.prototype.apply=function(a,b){return a.postProcessesEnabled=b.isInImprovementMode,!0},b}(Gq),Lq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning lens flares on/off"},b.prototype.apply=function(a,b){return a.lensFlaresEnabled=b.isInImprovementMode,!0},b}(Gq),Mq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return this.onGetDescription?this.onGetDescription():"Running user defined callback"},b.prototype.apply=function(a,b){return!this.onApply||this.onApply(a,b)},b}(Gq),Nq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning particles on/off"},b.prototype.apply=function(a,b){return a.particlesEnabled=b.isInImprovementMode,!0},b}(Gq),Oq=function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return Object(l.d)(b,a),b.prototype.getDescription=function(){return"Turning render targets off"},b.prototype.apply=function(a,b){return a.renderTargetsEnabled=b.isInImprovementMode,!0},b}(Gq),Pq=function(a){function b(){var b=null!==a&&a.apply(this,arguments)||this;return b._canBeMerged=function(a){if(!(a instanceof R.a))return!1;a=a;return!a.isDisposed()&&!(!a.isVisible||!a.isEnabled())&&!(a.instances.length>0)&&!a.skeleton&&!a.hasLODLevels},b}return Object(l.d)(b,a),Object.defineProperty(b,"UpdateSelectionTree",{get:function(){return b._UpdateSelectionTree},set:function(a){b._UpdateSelectionTree=a},enumerable:!1,configurable:!0}),b.prototype.getDescription=function(){return"Merging similar meshes together"},b.prototype.apply=function(a,c,d){for(var c=a.meshes.slice(0),e=c.length,f=0;f=this._targetFrameRate)return this._isRunning=!1,void this.onSuccessObservable.notifyObservers(this);for(var d=!0,e=!0,f=0;f0){d.animationGroups=[];for(c=0;c0)for(d.reflectionProbes=[],f=0;f0&&setTimeout(function(){c.stopRecording()},1e3*b),this._fileName=a,this._recordedChunks=[],this._resolve=null,this._reject=null,this._canvas.isRecording=!0,this._mediaRecorder.start(this._options.recordChunckSize),new Promise(function(a,b){c._resolve=a,c._reject=b})},a.prototype.dispose=function(){this._canvas=null,this._mediaRecorder=null,this._recordedChunks=[],this._fileName=null,this._resolve=null,this._reject=null},a.prototype._handleDataAvailable=function(a){a.data.size>0&&this._recordedChunks.push(a.data)},a.prototype._handleError=function(a){if(this.stopRecording(),!this._reject)throw new a.error;this._reject(a.error)},a.prototype._handleStop=function(){this.stopRecording();var a=new Blob(this._recordedChunks);this._resolve&&this._resolve(a),window.URL.createObjectURL(a),this._fileName&&T.b.Download(a,this._fileName)},a._defaultOptions={mimeType:"video/webm",fps:25,recordChunckSize:3e3},a}();function Yq(a,b,c,d,e,f){void 0===e&&(e="image/png"),void 0===f&&(f=!1);b=cr(a,b,c);c=b.height;b=b.width;if(c&&b){T.b._ScreenshotCanvas||(T.b._ScreenshotCanvas=document.createElement("canvas")),T.b._ScreenshotCanvas.width=b,T.b._ScreenshotCanvas.height=c;var g=T.b._ScreenshotCanvas.getContext("2d"),h=a.getRenderWidth()/a.getRenderHeight(),i=b,j=i/h;j>c&&(i=(j=c)*h);var k=Math.max(0,b-i)/2,l=Math.max(0,c-j)/2;a.onEndFrameObservable.addOnce(function(){var b=a.getRenderingCanvas();g&&b&&g.drawImage(b,k,l,i,j),f?(T.b.EncodeScreenshotCanvasData(void 0,e),d&&d("")):T.b.EncodeScreenshotCanvasData(d,e)})}else q.a.Error("Invalid "size" parameter !")}function Zq(a,b,c,d){return void 0===d&&(d="image/png"),new Promise(function(e,f){Yq(a,b,c,function(a){void 0!==a?e(a):f(new Error("Data is undefined"))},d)})}function $q(a,b,c,d,e){return void 0===e&&(e="image/png"),new Promise(function(f,g){Yq(a,b,{width:c,height:d},function(){f()},e,!0)})}function ar(a,b,c,d,e,f,g,h,i,j){void 0===e&&(e="image/png"),void 0===f&&(f=1),void 0===g&&(g=!1),void 0===i&&(i=!1),void 0===j&&(j=!1);c=cr(a,b,c);var k=c.height,l=c.width;c={width:l,height:k};if(k&&l){var n=b.getScene(),v=null,w=n.activeCameras;n.activeCameras=null,n.activeCamera!==b&&(v=n.activeCamera,n.activeCamera=b),n.render();var x=new cd("screenShot",c,n,!1,!1,r.a.TEXTURETYPE_UNSIGNED_INT,!1,U.a.NEAREST_SAMPLINGMODE,void 0,j,void 0,void 0,void 0,f);x.renderList=null,x.samples=f,x.renderSprites=i,a.onEndFrameObservable.addOnce(function(){x.readPixels(void 0,void 0,void 0,!1).then(function(a){T.b.DumpData(l,k,a,d,e,h,!0),x.dispose()})});var y=function(){n.incrementRenderId(),n.resetCachedMaterial(),x.render(!0),n.incrementRenderId(),n.resetCachedMaterial(),v&&(n.activeCamera=v),n.activeCameras=w,b.getProjectionMatrix(!0),n.render()};if(g){c=new qp("antialiasing",1,n.activeCamera);x.addPostProcess(c),c.getEffect().isReady()?y():c.getEffect().onCompiled=function(){y()}}else y()}else q.a.Error("Invalid "size" parameter !")}function br(a,b,c,d,e,f,g,h){return void 0===d&&(d="image/png"),void 0===e&&(e=1),void 0===f&&(f=!1),void 0===h&&(h=!1),new Promise(function(i,j){ar(a,b,c,function(a){void 0!==a?i(a):j(new Error("Data is undefined"))},d,e,f,g,h)})}function cr(a,b,c){var d=0,e=0;if("object"==typeof c){var f=c.precision?Math.abs(c.precision):1;c.width&&c.height?(d=c.height*f,e=c.width*f):c.width&&!c.height?(e=c.width*f,d=Math.round(e/a.getAspectRatio(b))):c.height&&!c.width?(d=c.height*f,e=Math.round(d*a.getAspectRatio(b))):(e=Math.round(a.getRenderWidth()*f),d=Math.round(e/a.getAspectRatio(b)))}else isNaN(c)||(d=c,e=c);return e&&(e=Math.floor(e)),d&&(d=Math.floor(d)),{height:0|d,width:0|e}}var dr,er={CreateScreenshot:Yq,CreateScreenshotAsync:Zq,CreateScreenshotWithResizeAsync:$q,CreateScreenshotUsingRenderTarget:ar,CreateScreenshotUsingRenderTargetAsync:br};T.b.CreateScreenshot=Yq,T.b.CreateScreenshotAsync=Zq,T.b.CreateScreenshotUsingRenderTarget=ar,T.b.CreateScreenshotUsingRenderTargetAsync=br,(function(a){a[a.Checkbox=0]="Checkbox",a[a.Slider=1]="Slider",a[a.Vector3=2]="Vector3",a[a.Quaternion=3]="Quaternion",a[a.Color3=4]="Color3",a[a.String=5]="String",a[a.Button=6]="Button",a[a.Options=7]="Options",a[a.Tab=8]="Tab"})(dr||(dr={}));var fr,gr=c(164),hr=c(145),ir=function(){function a(a){this.byteOffset=0,this.buffer=a}return a.prototype.loadAsync=function(a){var b=this;return this.buffer.readAsync(this.byteOffset,a).then(function(a){b._dataView=new DataView(a.buffer,a.byteOffset,a.byteLength),b._dataByteOffset=0})},a.prototype.readUint32=function(){var a=this._dataView.getUint32(this._dataByteOffset,!0);return this._dataByteOffset+=4,this.byteOffset+=4,a},a.prototype.readUint8Array=function(a){var b=new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+this._dataByteOffset,a);return this._dataByteOffset+=a,this.byteOffset+=a,b},a.prototype.readString=function(a){return Object(gh.a)(this.readUint8Array(a))},a.prototype.skipBytes=function(a){this._dataByteOffset+=a,this.byteOffset+=a},a}(),jr=function(){function a(){}return a._GetStorage=function(){try{return localStorage.setItem("test",""),localStorage.removeItem("test"),localStorage}catch(b){var a={};return{getItem:function(b){b=a[b];return void 0===b?null:b},setItem:function(b,c){a[b]=c}}}},a.ReadString=function(a,b){a=this._Storage.getItem(a);return null!==a?a:b},a.WriteString=function(a,b){this._Storage.setItem(a,b)},a.ReadBoolean=function(a,b){a=this._Storage.getItem(a);return null!==a?"true"===a:b},a.WriteBoolean=function(a,b){this._Storage.setItem(a,b?"true":"false")},a.ReadNumber=function(a,b){a=this._Storage.getItem(a);return null!==a?parseFloat(a):b},a.WriteNumber=function(a,b){this._Storage.setItem(a,b.toString())},a._Storage=a._GetStorage(),a}(),kr=function(){function a(){this._trackedScene=null}return a.prototype.track=function(a){this._trackedScene=a,I.a.AllowLoadingUniqueId=!0,this._savedJSON=Vq.Serialize(a),I.a.AllowLoadingUniqueId=!1},a.prototype.getDelta=function(){if(!this._trackedScene)return null;var a=U.a.ForceSerializeBuffers;U.a.ForceSerializeBuffers=!1,I.a.AllowLoadingUniqueId=!0;var b=Vq.Serialize(this._trackedScene);I.a.AllowLoadingUniqueId=!1;var c={};for(var d in b)this._compareCollections(d,this._savedJSON[d],b[d],c);return U.a.ForceSerializeBuffers=a,c},a.prototype._compareArray=function(a,b,c,d){if(0===b.length&&0===c.length)return!0;if(b.length&&!isNaN(b[0])||c.length&&!isNaN(c[0])){if(b.length!==c.length)return!1;if(0===b.length)return!0;for(var e=0;ea.MAX_SEQUENCE_LENGTH)throw new Error("Sequences longer than "+a.MAX_SEQUENCE_LENGTH+" not supported.");this._alphabet=c,this._characters=b.map(function(a){return d._alphabet.getCharacterIdx(a)})}return a.prototype.serialize=function(){return JSON.stringify(this._characters)},a.Deserialize=function(b,c){c=new a([],c);return c._characters=JSON.parse(b),c},a.prototype.distance=function(b){return a._distance(this,b)},a._distance=function(b,c){var d=b._alphabet;if(d!==c._alphabet)throw new Error("Cannot Levenshtein compare Sequences built from different alphabets.");b=b._characters;c=c._characters;var e=b.length,f=c.length,g=a._costMatrix;g[0][0]=0;for(var h=0;h.98)&&(g.e.CrossToRef(a._forwardDir,a._inverseFromVec,a._upDir),a._upDir.normalize(),g.a.LookAtLHToRef(b,c,a._upDir,a._lookMatrix),d.subtractToRef(c,a._fromToVec),a._fromToVec.normalize(),g.e.TransformNormalToRef(a._fromToVec,a._lookMatrix,e),!0)},a._tokenizeSegment=function(b,c){a._bestMatch=0,a._score=g.e.Dot(b,c[0]),a._bestScore=a._score;for(var d=1;da._bestScore&&(a._bestMatch=d,a._bestScore=a._score);return a._bestMatch},a._forwardDir=new g.e,a._inverseFromVec=new g.e,a._upDir=new g.e,a._fromToVec=new g.e,a._lookMatrix=new g.a,a}(),mr=function(){function a(a){this.chars=new Array(a)}return a.Generate=function(b,c,d,e,f){void 0===b&&(b=64),void 0===c&&(c=256),void 0===d&&(d=.1),void 0===e&&(e=.001),void 0===f&&(f=[]);for(var h,k,i=new a(b),j=0;j1e-6&&o.scaleAndAddToRef(1/(o.lengthSquared()*k),l)}),l.scaleInPlace(h),i.chars[a].addInPlace(l),i.chars[a].normalize()};for(j=f.length;j4;d=Math.floor(d/2))e.push(b.resampleAtTargetResolution(d).tokenize(c.chars));return e},a.prototype.distance=function(a){for(var b=0,c=0;c0&&(this._averageDistance=Math.max(this._averageDistance/this._descriptors.length,a.MIN_AVERAGE_DISTANCE))},a.MIN_AVERAGE_DISTANCE=1,a}(),pr=function(){function a(){this._maximumAllowableMatchCost=4,this._nameToDescribedTrajectory=new Map}return a.prototype.serialize=function(){var a={};return a.maximumAllowableMatchCost=this._maximumAllowableMatchCost,a.vector3Alphabet=this._vector3Alphabet.serialize(),a.levenshteinAlphabet=this._levenshteinAlphabet.serialize(),a.nameToDescribedTrajectory=[],this._nameToDescribedTrajectory.forEach(function(b,c){a.nameToDescribedTrajectory.push(c),a.nameToDescribedTrajectory.push(b.serialize())}),JSON.stringify(a)},a.Deserialize=function(b){b=JSON.parse(b);var c=new a;c._maximumAllowableMatchCost=b.maximumAllowableMatchCost,c._vector3Alphabet=mr.Deserialize(b.vector3Alphabet),c._levenshteinAlphabet=fr.Alphabet.Deserialize(b.levenshteinAlphabet);for(var d=0;d=this._itemLength?NaN:this._view[a]},a.prototype.subarray=function(a,b){return a>=b||a<0?new Float32Array(0):(b>this._itemLength&&(b=this._itemLength),this._view.subarray(a,b))},a.prototype.push=function(a){this._view[this._itemLength]=a,this._itemLength++,this._itemLength>=this._view.length&&this._growArray()},a.prototype._growArray=function(){var a=Math.floor(1.5*this._view.length);a=new Float32Array(a);a.set(this._view),this._view=a},a}(),ur=/ /g,vr=function(){function a(b,c){var d=this;this._scene=b,this._collectDataAtFrame=function(){var b=P.a.Now-d._startingTimestamp,c=d.datasets.ids.length,e=d.datasets.startingIndices.itemLength,f=0;if(e>0){e=d.datasets.startingIndices.at(e-1);f=e+d.datasets.data.at(e+a.NumberOfPointsOffset)+a.SliceDataOffset}if(d.datasets.startingIndices.push(f),d.datasets.data.push(b),d.datasets.data.push(c),d.datasets.ids.forEach(function(a){a=d._strategies.get(a);a&&d.datasets.data.push(a.getData())}),d.datasetObservable.hasObservers()){for(e=[b,c],b=0;b>c&255).toString(16)).substr(-2);return a},a.prototype.getCurrentSlice=function(){var a=this,b=[P.a.Now-this._startingTimestamp,this.datasets.ids.length];this.datasets.ids.forEach(function(c){c=a._strategies.get(c);c&&a.datasetObservable.hasObservers()&&b.push(c.getData())}),this.datasetObservable.hasObservers()&&this.datasetObservable.notifyObservers(b)},a.prototype.updateMetadata=function(a,b,c){a=this._datasetMeta.get(a);a&&(a[b]=c,this.metadataObservable.notifyObservers(this._datasetMeta))},a.prototype.clear=function(a){this.datasets.data=new tr(1800),this.datasets.ids.length=0,this.datasets.startingIndices=new tr(1800),this._datasetMeta.clear(),this._strategies.forEach(function(a){return a.dispose()}),this._strategies.clear(),a||this._eventRestoreSet.clear(),this._hasLoadedData=!1},Object.defineProperty(a.prototype,"hasLoadedData",{get:function(){return this._hasLoadedData},enumerable:!1,configurable:!0}),a.prototype.loadFromFileData=function(b){b=b.replace(ur,"").split(" ").map(function(a){return a.split(",").filter(function(a){return a.length>0})}).filter(function(a){return a.length>0});var c=a.NumberOfPointsOffset;if(b.length<2)return!1;var d={ids:[],data:new tr(1800),startingIndices:new tr(1800)},e=b[0];b=b.slice(1);if(e.length<2||"timestamp"!==e[0]||"numPoints"!==e[c])return!1;for(var f=a.SliceDataOffset;f0&&this.onFeaturePointsAddedObservable.notifyObservers(d),c.length>0&&this.onFeaturePointsUpdatedObservable.notifyObservers(c)}}},b.prototype._init=function(){this._xrSessionManager.session.trySetFeaturePointCloudEnabled&&this._xrSessionManager.session.trySetFeaturePointCloudEnabled(!0)&&(this._enabled=!0)},b.Name=kb.FEATURE_POINTS,b.Version=1,b}(Vi);lb.AddWebXRFeature(Hr.Name,function(a){return function(){return new Hr(a)}},Hr.Version);var Ir=["wrist","thumb-metacarpal","thumb-phalanx-proximal","thumb-phalanx-distal","thumb-tip","index-finger-metacarpal","index-finger-phalanx-proximal","index-finger-phalanx-intermediate","index-finger-phalanx-distal","index-finger-tip","middle-finger-metacarpal","middle-finger-phalanx-proximal","middle-finger-phalanx-intermediate","middle-finger-phalanx-distal","middle-finger-tip","ring-finger-metacarpal","ring-finger-phalanx-proximal","ring-finger-phalanx-intermediate","ring-finger-phalanx-distal","ring-finger-tip","pinky-finger-metacarpal","pinky-finger-phalanx-proximal","pinky-finger-phalanx-intermediate","pinky-finger-phalanx-distal","pinky-finger-tip"],Jr=((a={}).wrist=["wrist"],a.thumb=["thumb-metacarpal","thumb-phalanx-proximal","thumb-phalanx-distal","thumb-tip"],a.index=["index-finger-metacarpal","index-finger-phalanx-proximal","index-finger-phalanx-intermediate","index-finger-phalanx-distal","index-finger-tip"],a.middle=["middle-finger-metacarpal","middle-finger-phalanx-proximal","middle-finger-phalanx-intermediate","middle-finger-phalanx-distal","middle-finger-tip"],a.ring=["ring-finger-metacarpal","ring-finger-phalanx-proximal","ring-finger-phalanx-intermediate","ring-finger-phalanx-distal","ring-finger-tip"],a.little=["pinky-finger-metacarpal","pinky-finger-phalanx-proximal","pinky-finger-phalanx-intermediate","pinky-finger-phalanx-distal","pinky-finger-tip"],a),Kr=function(){function a(a,b,c,d,e,f,h){void 0===e&&(e=!1),void 0===f&&(f=!1),void 0===h&&(h=1),this.xrController=a,this._jointMeshes=b,this._handMesh=c,this.rigMapping=d,this._leftHandedMeshes=e,this._jointsInvisible=f,this._jointScaleFactor=h,this._jointTransforms=new Array(Ir.length),this._jointTransformMatrices=new Float32Array(16*Ir.length),this._tempJointMatrix=new g.a,this._jointRadii=new Float32Array(Ir.length),this._scene=b[0].getScene();for(a=0;ac.rotationThreshold?a.x:0,b.rotateY=Math.abs(a.y)>c.rotationThreshold?a.y:0}},{allowedComponentTypes:[Li.THUMBSTICK_TYPE,Li.TOUCHPAD_TYPE],forceHandedness:"right",axisChangedHandler:function(a,b,c,d){b.moveX=Math.abs(a.x)>c.movementThreshold?a.x:0,b.moveY=Math.abs(a.y)>c.movementThreshold?a.y:0}}]},b.Version=1,b}(Vi);lb.AddWebXRFeature(Qr.Name,function(a,b){return function(){return new Qr(a,b)}},Qr.Version,!0);var Rr=c(65),Sr=function(a){function b(b,c){var d=a.call(this,b)||this;return d.options=c,d._canvasContext=null,d._reflectionCubeMap=null,d._xrLightEstimate=null,d._xrLightProbe=null,d._xrWebGLBinding=null,d._lightDirection=g.e.Up().negateInPlace(),d._lightColor=h.a.White(),d._intensity=1,d._sphericalHarmonics=new Ge,d._cubeMapPollTime=Date.now(),d._lightEstimationPollTime=Date.now(),d._ReflectionCubeMapTextureSize=16,d.directionalLight=null,d.onReflectionCubeMapUpdatedObservable=new f.c,d._updateReflectionCubeMap=function(){var a;if(d._xrLightProbe){if(d.options.cubeMapPollInterval){var b=Date.now();if(b-d._cubeMapPollTime=this._samples.length)throw new Error("Index out of bounds");return this._samples[(this._idx+a)%this._samples.length]},a}(),Vr=function(){function a(){this._samples=new Ur(60),this._entropy=0,this.onFirstStepDetected=new f.c}return a.prototype.update=function(a,b,c,d){this._samples.push(a,b);a=this._samples.at(0);if(this._entropy*=this._entropyDecayFactor,this._entropy+=g.d.Distance(a,this._samples.at(1)),!(this._entropy>this._entropyThreshold)){for(b=this._samePointCheckStartIdx;be&&(f=i,e=h);if(!(eb*this._squaredProjectionDistanceThreshold)){j=g.c.Vector3[0];j.set(c,d,0);f=g.c.Vector3[1];f.set(e.x,e.y,0);i=g.e.Cross(j,f).z>0;k=a.clone();b=a.clone();h.subtractToRef(a,e),i?(e.scaleAndAddToRef(this._axisToApexShrinkFactor,k),e.scaleAndAddToRef(this._axisToApexExtendFactor,b)):(e.scaleAndAddToRef(this._axisToApexExtendFactor,k),e.scaleAndAddToRef(this._axisToApexShrinkFactor,b)),this.onFirstStepDetected.notifyObservers({leftApex:k,rightApex:b,currentPosition:a,currentStepDirection:i?"right":"left"})}}}}},a.prototype.reset=function(){for(var a=0;athis._maxT&&(this._maxT=this._t,this._maxTPosition.copyFromFloats(a,b)),!(this._vitalityc&&(this.onMovement.notifyObservers({deltaT:this._t-c}),c<.5&&this._t>=.5&&this.onFootfall.notifyObservers({foot:this._steppingLeft?"left":"right"})),this._t<.95*this._maxT&&(this._currentPosition.copyFromFloats(a,b),this._steppingLeft?this._leftApex.copyFrom(this._maxTPosition):this._rightApex.copyFrom(this._maxTPosition),this._reset(this._leftApex,this._rightApex,this._currentPosition,!this._steppingLeft)),!(this._axisLength<.03))},Object.defineProperty(a.prototype,"_vitalityThreshold",{get:function(){return.1},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"forward",{get:function(){return this._forward},enumerable:!1,configurable:!0}),a}(),Xr=function(){function a(){var a=this;this._detector=new Vr,this._walker=null,this._movement=new g.d,this.movementThisFrame=g.e.Zero(),this._detector.onFirstStepDetected.add(function(b){a._walker||(a._walker=new Wr(b.leftApex,b.rightApex,b.currentPosition,b.currentStepDirection),a._walker.onFootfall.add(function(){}),a._walker.onMovement.add(function(b){a._walker.forward.scaleAndAddToRef(.024*b.deltaT,a._movement)}))})}return a.prototype.update=function(a,b){(b.y=0,b.normalize(),this._detector.update(a.x,a.z,b.x,b.z),this._walker)&&(this._walker.update(a.x,a.z)||(this._walker=null)),(this.movementThisFrame.set(this._movement.x,0,this._movement.y),this._movement.scaleInPlace(.96))},a}(),Yr=function(a){function b(b,c){var d=a.call(this,b)||this;return d._up=new g.e,d._forward=new g.e,d._position=new g.e,d._movement=new g.e,d._sessionManager=b,d.locomotionTarget=c.locomotionTarget,d._isLocomotionTargetWebXRCamera&&q.a.Warn("Using walking locomotion directly on a WebXRCamera may have unintended interactions with other XR techniques. Using an XR space parent is highly recommended"),d}return Object(l.d)(b,a),Object.defineProperty(b,"Name",{get:function(){return kb.WALKING_LOCOMOTION},enumerable:!1,configurable:!0}),Object.defineProperty(b,"Version",{get:function(){return 1},enumerable:!1,configurable:!0}),Object.defineProperty(b.prototype,"locomotionTarget",{get:function(){return this._locomotionTarget},set:function(a){this._locomotionTarget=a,this._isLocomotionTargetWebXRCamera="WebXRCamera"===this._locomotionTarget.getClassName()},enumerable:!1,configurable:!0}),b.prototype.isCompatible=function(){return void 0===this._sessionManager.sessionMode||"immersive-vr"===this._sessionManager.sessionMode},b.prototype.attach=function(){return!(!this.isCompatible||!a.prototype.attach.call(this))&&(this._walker=new Xr,!0)},b.prototype.detach=function(){return!!a.prototype.detach.call(this)&&(this._walker=null,!0)},b.prototype._onXRFrame=function(a){a=a.getViewerPose(this._sessionManager.baseReferenceSpace);if(a){var b=this.locomotionTarget.getScene().useRightHandedSystem?1:-1;a=a.transform.matrix;this._up.copyFromFloats(a[4],a[5],b*a[6]),this._forward.copyFromFloats(a[8],a[9],b*a[10]),this._position.copyFromFloats(a[12],a[13],b*a[14]),this._forward.scaleAndAddToRef(.05,this._position),this._up.scaleAndAddToRef(-.05,this._position),this._walker.update(this._position,this._forward),this._movement.copyFrom(this._walker.movementThisFrame),this._isLocomotionTargetWebXRCamera||g.e.TransformNormalToRef(this._movement,this.locomotionTarget.getWorldMatrix(),this._movement),this.locomotionTarget.position.addInPlace(this._movement)}},b}(Vi);lb.AddWebXRFeature(Yr.Name,function(a,b){return function(){return new Yr(a,b)}},Yr.Version,!1);var Zr=function(a){function b(b,c,d){b=a.call(this,b,$r[d],c,d,!0)||this;return b.profileId="generic-hand-select-grasp",b}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"generic.babylon",path:"https://controllers.babylonjs.com/generic/"}},b.prototype._getModelLoadingConstraints=function(){return!0},b.prototype._processLoadedModel=function(a){},b.prototype._setRootMesh=function(a){},b.prototype._updateModel=function(){},b}(Mi);Ri.RegisterController("generic-hand-select-grasp",function(a,b){return new Zr(b,a.gamepad,a.handedness)});var $r={left:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-left",assetPath:"left.glb"},right:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-right",assetPath:"right.glb"},none:{selectComponentId:"xr-standard-trigger",components:{"xr-standard-trigger":{type:"trigger",gamepadIndices:{button:0},rootNodeName:"xr-standard-trigger",visualResponses:{}},grasp:{type:"trigger",gamepadIndices:{button:4},rootNodeName:"grasp",visualResponses:{}}},gamepadMapping:"xr-standard",rootNodeName:"generic-hand-select-grasp-none",assetPath:"none.glb"}},as=function(a){function b(b,c,d){b=a.call(this,b,bs["left-right"],c,d)||this;return b._mapping={defaultButton:{valueNodeName:"VALUE",unpressedNodeName:"UNPRESSED",pressedNodeName:"PRESSED"},defaultAxis:{valueNodeName:"VALUE",minNodeName:"MIN",maxNodeName:"MAX"},buttons:{"xr-standard-trigger":{rootNodeName:"SELECT",componentProperty:"button",states:["default","touched","pressed"]},"xr-standard-squeeze":{rootNodeName:"GRASP",componentProperty:"state",states:["pressed"]},"xr-standard-touchpad":{rootNodeName:"TOUCHPAD_PRESS",labelAnchorNodeName:"squeeze-label",touchPointNodeName:"TOUCH"},"xr-standard-thumbstick":{rootNodeName:"THUMBSTICK_PRESS",componentProperty:"state",states:["pressed"]}},axes:{"xr-standard-touchpad":{"x-axis":{rootNodeName:"TOUCHPAD_TOUCH_X"},"y-axis":{rootNodeName:"TOUCHPAD_TOUCH_Y"}},"xr-standard-thumbstick":{"x-axis":{rootNodeName:"THUMBSTICK_X"},"y-axis":{rootNodeName:"THUMBSTICK_Y"}}}},b.profileId="microsoft-mixed-reality",b}return Object(l.d)(b,a),b.prototype._getFilenameAndPath=function(){return{filename:"left"===this.handedness?b.MODEL_LEFT_FILENAME:b.MODEL_RIGHT_FILENAME,path:b.MODEL_BASE_URL+"default/"}},b.prototype._getModelLoadingConstraints=function(){var a=hh.IsPluginForExtensionAvailable(".glb");return a||q.a.Warn("glTF / glb loaded was not registered, using generic controller instead"),a},b.prototype._processLoadedModel=function(a){var b=this;this.rootMesh&&(this.getComponentIds().forEach(function(a,c){if(!b.disableAnimation&&a&&b.rootMesh){var d=b._mapping.buttons[a],e=d.rootNodeName;if(!e)return void q.a.Log("Skipping unknown button at index: "+c+" with mapped name: "+a);c=b._getChildByName(b.rootMesh,e);if(!c)return void q.a.Warn("Missing button mesh with name: "+e);if(d.valueMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.valueNodeName),d.pressedMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.pressedNodeName),d.unpressedMesh=b._getImmediateChildByName(c,b._mapping.defaultButton.unpressedNodeName),d.valueMesh&&d.pressedMesh&&d.unpressedMesh){c=b.getComponent(a);c&&c.onButtonStateChangedObservable.add(function(a){b._lerpTransform(d,a.value)},void 0,!0)}else q.a.Warn("Missing button submesh under mesh with name: "+e)}}),this.getComponentIds().forEach(function(a,c){var d=b.getComponent(a);d.isAxes()&&["x-axis","y-axis"].forEach(function(c){if(b.rootMesh){var e=b._mapping.axes[a][c],f=b._getChildByName(b.rootMesh,e.rootNodeName);f?(e.valueMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.valueNodeName),e.minMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.minNodeName),e.maxMesh=b._getImmediateChildByName(f,b._mapping.defaultAxis.maxNodeName),e.valueMesh&&e.minMesh&&e.maxMesh?d&&d.onAxisValueChangedObservable.add(function(a){a="x-axis"===c?a.x:a.y;b._lerpTransform(e,a,!0)},void 0,!0):q.a.Warn("Missing axis submesh under mesh with name: "+e.rootNodeName)):q.a.Warn("Missing axis mesh with name: "+e.rootNodeName)}})}))},b.prototype._setRootMesh=function(a){var b;this.rootMesh=new R.a(this.profileId+" "+this.handedness,this.scene),this.rootMesh.isPickable=!1;for(var c=0;cshadow ? darkness : 1.0; } #define inline float computeShadowWithPoissonSamplingCube(vec3 lightPosition,samplerCube shadowSampler,float mapSize,float darkness,vec2 depthValues) { vec3 directionToLight=vPositionW-lightPosition; float depth=length(directionToLight); depth=(depth+depthValues.x)/(depthValues.y); depth=clamp(depth,0.,1.0); directionToLight=normalize(directionToLight); directionToLight.y=-directionToLight.y; float visibility=1.; vec3 poissonDisk[4]; poissonDisk[0]=vec3(-1.0,1.0,-1.0); poissonDisk[1]=vec3(1.0,-1.0,-1.0); poissonDisk[2]=vec3(-1.0,-1.0,-1.0); poissonDisk[3]=vec3(1.0,-1.0,1.0); #ifndef SHADOWFLOAT if (unpack(textureCube(shadowSampler,directionToLight+poissonDisk[0]*mapSize))shadow ? computeFallOff(darkness,clipSpace.xy,frustumEdgeFalloff) : 1.; } #endif #define inline float computeShadow(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadow=unpack(texture2D(shadowSampler,uv)); #else float shadow=texture2D(shadowSampler,uv).x; #endif return shadowPixelDepth>shadow ? computeFallOff(darkness,clipSpace.xy,frustumEdgeFalloff) : 1.; } } #define inline float computeShadowWithPoissonSampling(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float mapSize,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); float visibility=1.; vec2 poissonDisk[4]; poissonDisk[0]=vec2(-0.94201624,-0.39906216); poissonDisk[1]=vec2(0.94558609,-0.76890725); poissonDisk[2]=vec2(-0.094184101,-0.92938870); poissonDisk[3]=vec2(0.34495938,0.29387760); #ifndef SHADOWFLOAT if (unpack(texture2D(shadowSampler,uv+poissonDisk[0]*mapSize))1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadowMapSample=unpack(texture2D(shadowSampler,uv)); #else float shadowMapSample=texture2D(shadowSampler,uv).x; #endif float esm=1.0-clamp(exp(min(87.,depthScale*shadowPixelDepth))*shadowMapSample,0.,1.-darkness); return computeFallOff(esm,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithCloseESM(vec4 vPositionFromLight,float depthMetric,sampler2D shadowSampler,float darkness,float depthScale,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec2 uv=0.5*clipSpace.xy+vec2(0.5); if (uv.x<0. || uv.x>1.0 || uv.y<0. || uv.y>1.0) { return 1.0; } else { float shadowPixelDepth=clamp(depthMetric,0.,1.0); #ifndef SHADOWFLOAT float shadowMapSample=unpack(texture2D(shadowSampler,uv)); #else float shadowMapSample=texture2D(shadowSampler,uv).x; #endif float esm=clamp(exp(min(87.,-depthScale*(shadowPixelDepth-shadowMapSample))),darkness,1.); return computeFallOff(esm,clipSpace.xy,frustumEdgeFalloff); } } #ifdef IS_NDC_HALF_ZRANGE #define ZINCLIP clipSpace.z #else #define ZINCLIP uvDepth.z #endif #if defined(WEBGL2) || defined(WEBGPU) #define GREATEST_LESS_THAN_ONE 0.99999994 #define inline float computeShadowWithCSMPCF1(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec4 uvDepthLayer=vec4(uvDepth.x,uvDepth.y,layer,uvDepth.z); float shadow=texture2D(shadowSampler,uvDepthLayer); shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithCSMPCF3(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=3.-2.*st; vec2 uvw1=1.+2.*st; vec2 u=vec2((2.-st.x)/uvw0.x-1.,st.x/uvw1.x+1.)*shadowMapSizeAndInverse.y; vec2 v=vec2((2.-st.y)/uvw0.y-1.,st.y/uvw1.y+1.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[0]),layer,uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[0]),layer,uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[1]),layer,uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[1]),layer,uvDepth.z)); shadow=shadow/16.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithCSMPCF5(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArrayShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=4.-3.*st; vec2 uvw1=vec2(7.); vec2 uvw2=1.+3.*st; vec3 u=vec3((3.-2.*st.x)/uvw0.x-2.,(3.+st.x)/uvw1.x,st.x/uvw2.x+2.)*shadowMapSizeAndInverse.y; vec3 v=vec3((3.-2.*st.y)/uvw0.y-2.,(3.+st.y)/uvw1.y,st.y/uvw2.y+2.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[0]),layer,uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[0]),layer,uvDepth.z)); shadow+=uvw2.x*uvw0.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[0]),layer,uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[1]),layer,uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[1]),layer,uvDepth.z)); shadow+=uvw2.x*uvw1.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[1]),layer,uvDepth.z)); shadow+=uvw0.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[0],v[2]),layer,uvDepth.z)); shadow+=uvw1.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[1],v[2]),layer,uvDepth.z)); shadow+=uvw2.x*uvw2.y*texture2D(shadowSampler,vec4(base_uv.xy+vec2(u[2],v[2]),layer,uvDepth.z)); shadow=shadow/144.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } #define inline float computeShadowWithPCF1(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; float shadow=texture2D(shadowSampler,uvDepth); shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithPCF3(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=3.-2.*st; vec2 uvw1=1.+2.*st; vec2 u=vec2((2.-st.x)/uvw0.x-1.,st.x/uvw1.x+1.)*shadowMapSizeAndInverse.y; vec2 v=vec2((2.-st.y)/uvw0.y-1.,st.y/uvw1.y+1.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[0]),uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[0]),uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[1]),uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[1]),uvDepth.z)); shadow=shadow/16.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } #define inline float computeShadowWithPCF5(vec4 vPositionFromLight,float depthMetric,sampler2DShadow shadowSampler,vec2 shadowMapSizeAndInverse,float darkness,float frustumEdgeFalloff) { if (depthMetric>1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; vec2 uv=uvDepth.xy*shadowMapSizeAndInverse.x; uv+=0.5; vec2 st=fract(uv); vec2 base_uv=floor(uv)-0.5; base_uv*=shadowMapSizeAndInverse.y; vec2 uvw0=4.-3.*st; vec2 uvw1=vec2(7.); vec2 uvw2=1.+3.*st; vec3 u=vec3((3.-2.*st.x)/uvw0.x-2.,(3.+st.x)/uvw1.x,st.x/uvw2.x+2.)*shadowMapSizeAndInverse.y; vec3 v=vec3((3.-2.*st.y)/uvw0.y-2.,(3.+st.y)/uvw1.y,st.y/uvw2.y+2.)*shadowMapSizeAndInverse.y; float shadow=0.; shadow+=uvw0.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[0]),uvDepth.z)); shadow+=uvw1.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[0]),uvDepth.z)); shadow+=uvw2.x*uvw0.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[0]),uvDepth.z)); shadow+=uvw0.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[1]),uvDepth.z)); shadow+=uvw1.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[1]),uvDepth.z)); shadow+=uvw2.x*uvw1.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[1]),uvDepth.z)); shadow+=uvw0.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[0],v[2]),uvDepth.z)); shadow+=uvw1.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[1],v[2]),uvDepth.z)); shadow+=uvw2.x*uvw2.y*texture2D(shadowSampler,vec3(base_uv.xy+vec2(u[2],v[2]),uvDepth.z)); shadow=shadow/144.; shadow=mix(darkness,1.,shadow); return computeFallOff(shadow,clipSpace.xy,frustumEdgeFalloff); } } const vec3 PoissonSamplers32[64]=vec3[64]( vec3(0.06407013,0.05409927,0.), vec3(0.7366577,0.5789394,0.), vec3(-0.6270542,-0.5320278,0.), vec3(-0.4096107,0.8411095,0.), vec3(0.6849564,-0.4990818,0.), vec3(-0.874181,-0.04579735,0.), vec3(0.9989998,0.0009880066,0.), vec3(-0.004920578,-0.9151649,0.), vec3(0.1805763,0.9747483,0.), vec3(-0.2138451,0.2635818,0.), vec3(0.109845,0.3884785,0.), vec3(0.06876755,-0.3581074,0.), vec3(0.374073,-0.7661266,0.), vec3(0.3079132,-0.1216763,0.), vec3(-0.3794335,-0.8271583,0.), vec3(-0.203878,-0.07715034,0.), vec3(0.5912697,0.1469799,0.), vec3(-0.88069,0.3031784,0.), vec3(0.5040108,0.8283722,0.), vec3(-0.5844124,0.5494877,0.), vec3(0.6017799,-0.1726654,0.), vec3(-0.5554981,0.1559997,0.), vec3(-0.3016369,-0.3900928,0.), vec3(-0.5550632,-0.1723762,0.), vec3(0.925029,0.2995041,0.), vec3(-0.2473137,0.5538505,0.), vec3(0.9183037,-0.2862392,0.), vec3(0.2469421,0.6718712,0.), vec3(0.3916397,-0.4328209,0.), vec3(-0.03576927,-0.6220032,0.), vec3(-0.04661255,0.7995201,0.), vec3(0.4402924,0.3640312,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.), vec3(0.,0.,0.) ); const vec3 PoissonSamplers64[64]=vec3[64]( vec3(-0.613392,0.617481,0.), vec3(0.170019,-0.040254,0.), vec3(-0.299417,0.791925,0.), vec3(0.645680,0.493210,0.), vec3(-0.651784,0.717887,0.), vec3(0.421003,0.027070,0.), vec3(-0.817194,-0.271096,0.), vec3(-0.705374,-0.668203,0.), vec3(0.977050,-0.108615,0.), vec3(0.063326,0.142369,0.), vec3(0.203528,0.214331,0.), vec3(-0.667531,0.326090,0.), vec3(-0.098422,-0.295755,0.), vec3(-0.885922,0.215369,0.), vec3(0.566637,0.605213,0.), vec3(0.039766,-0.396100,0.), vec3(0.751946,0.453352,0.), vec3(0.078707,-0.715323,0.), vec3(-0.075838,-0.529344,0.), vec3(0.724479,-0.580798,0.), vec3(0.222999,-0.215125,0.), vec3(-0.467574,-0.405438,0.), vec3(-0.248268,-0.814753,0.), vec3(0.354411,-0.887570,0.), vec3(0.175817,0.382366,0.), vec3(0.487472,-0.063082,0.), vec3(-0.084078,0.898312,0.), vec3(0.488876,-0.783441,0.), vec3(0.470016,0.217933,0.), vec3(-0.696890,-0.549791,0.), vec3(-0.149693,0.605762,0.), vec3(0.034211,0.979980,0.), vec3(0.503098,-0.308878,0.), vec3(-0.016205,-0.872921,0.), vec3(0.385784,-0.393902,0.), vec3(-0.146886,-0.859249,0.), vec3(0.643361,0.164098,0.), vec3(0.634388,-0.049471,0.), vec3(-0.688894,0.007843,0.), vec3(0.464034,-0.188818,0.), vec3(-0.440840,0.137486,0.), vec3(0.364483,0.511704,0.), vec3(0.034028,0.325968,0.), vec3(0.099094,-0.308023,0.), vec3(0.693960,-0.366253,0.), vec3(0.678884,-0.204688,0.), vec3(0.001801,0.780328,0.), vec3(0.145177,-0.898984,0.), vec3(0.062655,-0.611866,0.), vec3(0.315226,-0.604297,0.), vec3(-0.780145,0.486251,0.), vec3(-0.371868,0.882138,0.), vec3(0.200476,0.494430,0.), vec3(-0.494552,-0.711051,0.), vec3(0.612476,0.705252,0.), vec3(-0.578845,-0.768792,0.), vec3(-0.772454,-0.090976,0.), vec3(0.504440,0.372295,0.), vec3(0.155736,0.065157,0.), vec3(0.391522,0.849605,0.), vec3(-0.620106,-0.328104,0.), vec3(0.789239,-0.419965,0.), vec3(-0.545396,0.538133,0.), vec3(-0.178564,-0.596057,0.) ); #define inline float computeShadowWithCSMPCSS(float layer,vec4 vPositionFromLight,float depthMetric,highp sampler2DArray depthSampler,highp sampler2DArrayShadow shadowSampler,float shadowMapSizeInverse,float lightSizeUV,float darkness,float frustumEdgeFalloff,int searchTapCount,int pcfTapCount,vec3[64] poissonSamplers,vec2 lightSizeUVCorrection,float depthCorrection,float penumbraDarkness) { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=clamp(ZINCLIP,0.,GREATEST_LESS_THAN_ONE); vec4 uvDepthLayer=vec4(uvDepth.x,uvDepth.y,layer,uvDepth.z); float blockerDepth=0.0; float sumBlockerDepth=0.0; float numBlocker=0.0; for (int i=0; i1.0 || depthMetric<0.0) { return 1.0; } else { vec3 clipSpace=vPositionFromLight.xyz/vPositionFromLight.w; vec3 uvDepth=vec3(0.5*clipSpace.xyz+vec3(0.5)); uvDepth.z=ZINCLIP; float blockerDepth=0.0; float sumBlockerDepth=0.0; float numBlocker=0.0; for (int i=0; i(_DEFINENAME_,BUMP,_VARYINGNAME_,Bump,_SAMPLERNAME_,bump) #endif #if defined(DETAIL) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_SAMPLERNAME_,detail) #endif #if defined(BUMP) && defined(PARALLAX) const float minSamples=4.; const float maxSamples=15.; const int iMaxSamples=15; vec2 parallaxOcclusion(vec3 vViewDirCoT,vec3 vNormalCoT,vec2 texCoord,float parallaxScale) { float parallaxLimit=length(vViewDirCoT.xy)/vViewDirCoT.z; parallaxLimit*=parallaxScale; vec2 vOffsetDir=normalize(vViewDirCoT.xy); vec2 vMaxOffset=vOffsetDir*parallaxLimit; float numSamples=maxSamples+(dot(vViewDirCoT,vNormalCoT)*(minSamples-maxSamples)); float stepSize=1.0/numSamples; float currRayHeight=1.0; vec2 vCurrOffset=vec2(0,0); vec2 vLastOffset=vec2(0,0); float lastSampledHeight=1.0; float currSampledHeight=1.0; for (int i=0; icurrRayHeight) { float delta1=currSampledHeight-currRayHeight; float delta2=(currRayHeight+stepSize)-lastSampledHeight; float ratio=delta1/(delta1+delta2); vCurrOffset=(ratio)* vLastOffset+(1.0-ratio)*vCurrOffset; break; } else { currRayHeight-=stepSize; vLastOffset=vCurrOffset; vCurrOffset+=stepSize*vMaxOffset; lastSampledHeight=currSampledHeight; } } return vCurrOffset; } vec2 parallaxOffset(vec3 viewDir,float heightScale) { float height=texture2D(bumpSampler,vBumpUV).w; vec2 texCoordOffset=heightScale*viewDir.xy*height; return -texCoordOffset; } #endif ";a.a.IncludesShadersStore[b]=c},function(a,b,c){a="vec2 uvOffset=vec2(0.0,0.0); #if defined(BUMP) || defined(PARALLAX) || defined(DETAIL) #ifdef NORMALXYSCALE float normalScale=1.0; #elif defined(BUMP) float normalScale=vBumpInfos.y; #else float normalScale=1.0; #endif #if defined(TANGENT) && defined(NORMAL) mat3 TBN=vTBN; #elif defined(BUMP) vec2 TBNUV=gl_FrontFacing ? vBumpUV : -vBumpUV; mat3 TBN=cotangent_frame(normalW*normalScale,vPositionW,TBNUV,vTangentSpaceParams); #else vec2 TBNUV=gl_FrontFacing ? vDetailUV : -vDetailUV; mat3 TBN=cotangent_frame(normalW*normalScale,vPositionW,TBNUV,vec2(1.,1.)); #endif #elif defined(ANISOTROPIC) #if defined(TANGENT) && defined(NORMAL) mat3 TBN=vTBN; #else vec2 TBNUV=gl_FrontFacing ? vMainUV1 : -vMainUV1; mat3 TBN=cotangent_frame(normalW,vPositionW,TBNUV,vec2(1.,1.)); #endif #endif #ifdef PARALLAX mat3 invTBN=transposeMat3(TBN); #ifdef PARALLAXOCCLUSION uvOffset=parallaxOcclusion(invTBN*-viewDirectionW,invTBN*normalW,vBumpUV,vBumpInfos.z); #else uvOffset=parallaxOffset(invTBN*viewDirectionW,vBumpInfos.z); #endif #endif #ifdef DETAIL vec4 detailColor=texture2D(detailSampler,vDetailUV+uvOffset); vec2 detailNormalRG=detailColor.wy*2.0-1.0; float detailNormalB=sqrt(1.-saturate(dot(detailNormalRG,detailNormalRG))); vec3 detailNormal=vec3(detailNormalRG,detailNormalB); #endif #ifdef BUMP #ifdef OBJECTSPACE_NORMALMAP normalW=normalize(texture2D(bumpSampler,vBumpUV).xyz*2.0-1.0); normalW=normalize(mat3(normalMatrix)*normalW); #elif !defined(DETAIL) normalW=perturbNormal(TBN,texture2D(bumpSampler,vBumpUV+uvOffset).xyz,vBumpInfos.y); #else vec3 bumpNormal=texture2D(bumpSampler,vBumpUV+uvOffset).xyz*2.0-1.0; #if DETAIL_NORMALBLENDMETHOD == 0 detailNormal.xy*=vDetailInfos.z; vec3 blendedNormal=normalize(vec3(bumpNormal.xy+detailNormal.xy,bumpNormal.z*detailNormal.z)); #elif DETAIL_NORMALBLENDMETHOD == 1 detailNormal.xy*=vDetailInfos.z; bumpNormal+=vec3(0.0,0.0,1.0); detailNormal*=vec3(-1.0,-1.0,1.0); vec3 blendedNormal=bumpNormal*dot(bumpNormal,detailNormal)/bumpNormal.z-detailNormal; #endif normalW=perturbNormalBase(TBN,blendedNormal,vBumpInfos.y); #endif #elif defined(DETAIL) detailNormal.xy*=vDetailInfos.z; normalW=perturbNormalBase(TBN,detailNormal,vDetailInfos.z); #endif ";c(5).a.IncludesShadersStore.bumpFragment=a},function(a,b,c){a="lightFragment";b="#ifdef LIGHT{X} #if defined(SHADOWONLY) || defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X}) #else #ifdef PBR #ifdef SPOTLIGHT{X} preInfo=computePointAndSpotPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(POINTLIGHT{X}) preInfo=computePointAndSpotPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(HEMILIGHT{X}) preInfo=computeHemisphericPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #elif defined(DIRLIGHT{X}) preInfo=computeDirectionalPreLightingInfo(light{X}.vLightData,viewDirectionW,normalW); #endif preInfo.NdotV=NdotV; #ifdef SPOTLIGHT{X} #ifdef LIGHT_FALLOFF_GLTF{X} preInfo.attenuation=computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared,light{X}.vLightFalloff.y); preInfo.attenuation*=computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightFalloff.z,light{X}.vLightFalloff.w); #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) preInfo.attenuation=computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); preInfo.attenuation*=computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w); #elif defined(LIGHT_FALLOFF_STANDARD{X}) preInfo.attenuation=computeDistanceLightFalloff_Standard(preInfo.lightOffset,light{X}.vLightFalloff.x); preInfo.attenuation*=computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w,light{X}.vLightData.w); #else preInfo.attenuation=computeDistanceLightFalloff(preInfo.lightOffset,preInfo.lightDistanceSquared,light{X}.vLightFalloff.x,light{X}.vLightFalloff.y); preInfo.attenuation*=computeDirectionalLightFalloff(light{X}.vLightDirection.xyz,preInfo.L,light{X}.vLightDirection.w,light{X}.vLightData.w,light{X}.vLightFalloff.z,light{X}.vLightFalloff.w); #endif #elif defined(POINTLIGHT{X}) #ifdef LIGHT_FALLOFF_GLTF{X} preInfo.attenuation=computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared,light{X}.vLightFalloff.y); #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) preInfo.attenuation=computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); #elif defined(LIGHT_FALLOFF_STANDARD{X}) preInfo.attenuation=computeDistanceLightFalloff_Standard(preInfo.lightOffset,light{X}.vLightFalloff.x); #else preInfo.attenuation=computeDistanceLightFalloff(preInfo.lightOffset,preInfo.lightDistanceSquared,light{X}.vLightFalloff.x,light{X}.vLightFalloff.y); #endif #else preInfo.attenuation=1.0; #endif #ifdef HEMILIGHT{X} preInfo.roughness=roughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(roughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif #ifdef HEMILIGHT{X} info.diffuse=computeHemisphericDiffuseLighting(preInfo,light{X}.vLightDiffuse.rgb,light{X}.vLightGround); #elif defined(SS_TRANSLUCENCY) info.diffuse=computeDiffuseAndTransmittedLighting(preInfo,light{X}.vLightDiffuse.rgb,subSurfaceOut.transmittance); #else info.diffuse=computeDiffuseLighting(preInfo,light{X}.vLightDiffuse.rgb); #endif #ifdef SPECULARTERM #ifdef ANISOTROPIC info.specular=computeAnisotropicSpecularLighting(preInfo,viewDirectionW,normalW,anisotropicOut.anisotropicTangent,anisotropicOut.anisotropicBitangent,anisotropicOut.anisotropy,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #else info.specular=computeSpecularLighting(preInfo,normalW,clearcoatOut.specularEnvironmentR0,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #endif #endif #ifdef SHEEN #ifdef SHEEN_LINKWITHALBEDO preInfo.roughness=sheenOut.sheenIntensity; #else #ifdef HEMILIGHT{X} preInfo.roughness=sheenOut.sheenRoughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(sheenOut.sheenRoughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif #endif info.sheen=computeSheenLighting(preInfo,normalW,sheenOut.sheenColor,specularEnvironmentR90,AARoughnessFactors.x,light{X}.vLightDiffuse.rgb); #endif #ifdef CLEARCOAT #ifdef HEMILIGHT{X} preInfo.roughness=clearcoatOut.clearCoatRoughness; #else preInfo.roughness=adjustRoughnessFromLightProperties(clearcoatOut.clearCoatRoughness,light{X}.vLightSpecular.a,preInfo.lightDistance); #endif info.clearCoat=computeClearCoatLighting(preInfo,clearcoatOut.clearCoatNormalW,clearcoatOut.clearCoatAARoughnessFactors.x,clearcoatOut.clearCoatIntensity,light{X}.vLightDiffuse.rgb); #ifdef CLEARCOAT_TINT absorption=computeClearCoatLightingAbsorption(clearcoatOut.clearCoatNdotVRefract,preInfo.L,clearcoatOut.clearCoatNormalW,clearcoatOut.clearCoatColor,clearcoatOut.clearCoatThickness,clearcoatOut.clearCoatIntensity); info.diffuse*=absorption; #ifdef SPECULARTERM info.specular*=absorption; #endif #endif info.diffuse*=info.clearCoat.w; #ifdef SPECULARTERM info.specular*=info.clearCoat.w; #endif #ifdef SHEEN info.sheen*=info.clearCoat.w; #endif #endif #else #ifdef SPOTLIGHT{X} info=computeSpotLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDirection,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightDiffuse.a,glossiness); #elif defined(HEMILIGHT{X}) info=computeHemisphericLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightGround,glossiness); #elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X}) info=computeLighting(viewDirectionW,normalW,light{X}.vLightData,light{X}.vLightDiffuse.rgb,light{X}.vLightSpecular.rgb,light{X}.vLightDiffuse.a,glossiness); #endif #endif #ifdef PROJECTEDLIGHTTEXTURE{X} info.diffuse*=computeProjectionTextureDiffuseLighting(projectionLightSampler{X},textureProjectionMatrix{X}); #endif #endif #ifdef SHADOW{X} #ifdef SHADOWCSM{X} for (int i=0; i=0.) { index{X}=i; break; } } #ifdef SHADOWCSMUSESHADOWMAXZ{X} if (index{X}>=0) #endif { #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithCSMPCF1(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithCSMPCF3(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithCSMPCF5(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithCSMPCSS16(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithCSMPCSS32(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #else shadow=computeShadowWithCSMPCSS64(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #endif #else shadow=computeShadowCSM(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #ifdef SHADOWCSMDEBUG{X} shadowDebug{X}=vec3(shadow)*vCascadeColorsMultiplier{X}[index{X}]; #endif #ifndef SHADOWCSMNOBLEND{X} float frustumLength=frustumLengths{X}[index{X}]; float diffRatio=clamp(diff{X}/frustumLength,0.,1.)*cascadeBlendFactor{X}; if (index{X}<(SHADOWCSMNUM_CASCADES{X}-1) && diffRatio<1.) { index{X}+=1; float nextShadow=0.; #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) nextShadow=computeShadowWithCSMPCF1(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) nextShadow=computeShadowWithCSMPCF3(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else nextShadow=computeShadowWithCSMPCF5(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) nextShadow=computeShadowWithCSMPCSS16(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) nextShadow=computeShadowWithCSMPCSS32(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #else nextShadow=computeShadowWithCSMPCSS64(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w,lightSizeUVCorrection{X}[index{X}],depthCorrection{X}[index{X}],penumbraDarkness{X}); #endif #else nextShadow=computeShadowCSM(float(index{X}),vPositionFromLight{X}[index{X}],vDepthMetric{X}[index{X}],shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif shadow=mix(nextShadow,shadow,diffRatio); #ifdef SHADOWCSMDEBUG{X} shadowDebug{X}=mix(vec3(nextShadow)*vCascadeColorsMultiplier{X}[index{X}],shadowDebug{X},diffRatio); #endif } #endif } #elif defined(SHADOWCLOSEESM{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithCloseESMCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.depthValues); #else shadow=computeShadowWithCloseESM(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWESM{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithESMCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.depthValues); #else shadow=computeShadowWithESM(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.z,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPOISSON{X}) #if defined(SHADOWCUBE{X}) shadow=computeShadowWithPoissonSamplingCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.x,light{X}.depthValues); #else shadow=computeShadowWithPoissonSampling(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithPCF1(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithPCF3(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithPCF5(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.yz,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) shadow=computeShadowWithPCSS16(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) shadow=computeShadowWithPCSS32(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #else shadow=computeShadowWithPCSS64(vPositionFromLight{X},vDepthMetric{X},depthSampler{X},shadowSampler{X},light{X}.shadowsInfo.y,light{X}.shadowsInfo.z,light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #else #if defined(SHADOWCUBE{X}) shadow=computeShadowCube(light{X}.vLightData.xyz,shadowSampler{X},light{X}.shadowsInfo.x,light{X}.depthValues); #else shadow=computeShadow(vPositionFromLight{X},vDepthMetric{X},shadowSampler{X},light{X}.shadowsInfo.x,light{X}.shadowsInfo.w); #endif #endif #ifdef SHADOWONLY #ifndef SHADOWINUSE #define SHADOWINUSE #endif globalShadow+=shadow; shadowLightCount+=1.0; #endif #else shadow=1.; #endif #ifndef SHADOWONLY #ifdef CUSTOMUSERLIGHTING diffuseBase+=computeCustomDiffuseLighting(info,diffuseBase,shadow); #ifdef SPECULARTERM specularBase+=computeCustomSpecularLighting(info,specularBase,shadow); #endif #elif defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) diffuseBase+=lightmapColor.rgb*shadow; #ifdef SPECULARTERM #ifndef LIGHTMAPNOSPECULAR{X} specularBase+=info.specular*shadow*lightmapColor.rgb; #endif #endif #ifdef CLEARCOAT #ifndef LIGHTMAPNOSPECULAR{X} clearCoatBase+=info.clearCoat.rgb*shadow*lightmapColor.rgb; #endif #endif #ifdef SHEEN #ifndef LIGHTMAPNOSPECULAR{X} sheenBase+=info.sheen.rgb*shadow; #endif #endif #else #ifdef SHADOWCSMDEBUG{X} diffuseBase+=info.diffuse*shadowDebug{X}; #else diffuseBase+=info.diffuse*shadow; #endif #ifdef SPECULARTERM specularBase+=info.specular*shadow; #endif #ifdef CLEARCOAT clearCoatBase+=info.clearCoat.rgb*shadow; #endif #ifdef SHEEN sheenBase+=info.sheen.rgb*shadow; #endif #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#ifdef FOG float fog=CalcFogFactor(); #ifdef PBR fog=toLinearSpace(fog); #endif color.rgb=mix(vFogColor,color.rgb,fog); #endif";c(5).a.IncludesShadersStore.fogFragment=a},function(a,b,c){a="fogVertexDeclaration";b="#ifdef FOG varying vec3 vFogDistance; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="lightVxFragmentDeclaration";b="#ifdef LIGHT{X} uniform vec4 vLightData{X}; uniform vec4 vLightDiffuse{X}; #ifdef SPECULARTERM uniform vec4 vLightSpecular{X}; #else vec4 vLightSpecular{X}=vec4(0.); #endif #ifdef SHADOW{X} #ifdef SHADOWCSM{X} uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}]; varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromCamera{X}; #elif defined(SHADOWCUBE{X}) #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; uniform mat4 lightMatrix{X}; #endif uniform vec4 shadowsInfo{X}; uniform vec2 depthValues{X}; #endif #ifdef SPOTLIGHT{X} uniform vec4 vLightDirection{X}; uniform vec4 vLightFalloff{X}; #elif defined(POINTLIGHT{X}) uniform vec4 vLightFalloff{X}; #elif defined(HEMILIGHT{X}) uniform vec3 vLightGround{X}; #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="lightVxUboDeclaration";b="#ifdef LIGHT{X} uniform Light{X} { vec4 vLightData; vec4 vLightDiffuse; vec4 vLightSpecular; #ifdef SPOTLIGHT{X} vec4 vLightDirection; vec4 vLightFalloff; #elif defined(POINTLIGHT{X}) vec4 vLightFalloff; #elif defined(HEMILIGHT{X}) vec3 vLightGround; #endif vec4 shadowsInfo; vec2 depthValues; } light{X}; #ifdef SHADOW{X} #ifdef SHADOWCSM{X} uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}]; varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}]; varying vec4 vPositionFromCamera{X}; #elif defined(SHADOWCUBE{X}) #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; uniform mat4 lightMatrix{X}; #endif #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="shadowsVertex";b="#ifdef SHADOWS #if defined(SHADOWCSM{X}) vPositionFromCamera{X}=view*worldPos; for (int i=0; i1)for(var h=0;h=a||-1!==c.indexOf("file:")?-1:Math.pow(2,e)*b}},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(1),e=function(){function a(){this.reset()}return a.prototype.reset=function(){this.enabled=!1,this.mask=255,this.func=a.ALWAYS,this.funcRef=1,this.funcMask=255,this.opStencilFail=a.KEEP,this.opDepthFail=a.KEEP,this.opStencilDepthPass=a.REPLACE},Object.defineProperty(a.prototype,"stencilFunc",{get:function(){return this.func},set:function(a){this.func=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilFuncRef",{get:function(){return this.funcRef},set:function(a){this.funcRef=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilFuncMask",{get:function(){return this.funcMask},set:function(a){this.funcMask=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpStencilFail",{get:function(){return this.opStencilFail},set:function(a){this.opStencilFail=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpDepthFail",{get:function(){return this.opDepthFail},set:function(a){this.opDepthFail=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilOpStencilDepthPass",{get:function(){return this.opStencilDepthPass},set:function(a){this.opStencilDepthPass=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilMask",{get:function(){return this.mask},set:function(a){this.mask=a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"stencilTest",{get:function(){return this.enabled},set:function(a){this.enabled=a},enumerable:!1,configurable:!0}),a.ALWAYS=d.a.ALWAYS,a.KEEP=d.a.KEEP,a.REPLACE=d.a.REPLACE,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._blendFunctionParameters=new Array(4),this._blendEquationParameters=new Array(2),this._blendConstants=new Array(4),this._isBlendConstantsDirty=!1,this._alphaBlend=!1,this._isAlphaBlendDirty=!1,this._isBlendFunctionParametersDirty=!1,this._isBlendEquationParametersDirty=!1,this.reset()}return Object.defineProperty(a.prototype,"isDirty",{get:function(){return this._isAlphaBlendDirty||this._isBlendFunctionParametersDirty||this._isBlendEquationParametersDirty},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"alphaBlend",{get:function(){return this._alphaBlend},set:function(a){this._alphaBlend!==a&&(this._alphaBlend=a,this._isAlphaBlendDirty=!0)},enumerable:!1,configurable:!0}),a.prototype.setAlphaBlendConstants=function(a,b,c,d){this._blendConstants[0]===a&&this._blendConstants[1]===b&&this._blendConstants[2]===c&&this._blendConstants[3]===d||(this._blendConstants[0]=a,this._blendConstants[1]=b,this._blendConstants[2]=c,this._blendConstants[3]=d,this._isBlendConstantsDirty=!0)},a.prototype.setAlphaBlendFunctionParameters=function(a,b,c,d){this._blendFunctionParameters[0]===a&&this._blendFunctionParameters[1]===b&&this._blendFunctionParameters[2]===c&&this._blendFunctionParameters[3]===d||(this._blendFunctionParameters[0]=a,this._blendFunctionParameters[1]=b,this._blendFunctionParameters[2]=c,this._blendFunctionParameters[3]=d,this._isBlendFunctionParametersDirty=!0)},a.prototype.setAlphaEquationParameters=function(a,b){this._blendEquationParameters[0]===a&&this._blendEquationParameters[1]===b||(this._blendEquationParameters[0]=a,this._blendEquationParameters[1]=b,this._isBlendEquationParametersDirty=!0)},a.prototype.reset=function(){this._alphaBlend=!1,this._blendFunctionParameters[0]=null,this._blendFunctionParameters[1]=null,this._blendFunctionParameters[2]=null,this._blendFunctionParameters[3]=null,this._blendEquationParameters[0]=null,this._blendEquationParameters[1]=null,this._blendConstants[0]=null,this._blendConstants[1]=null,this._blendConstants[2]=null,this._blendConstants[3]=null,this._isAlphaBlendDirty=!0,this._isBlendFunctionParametersDirty=!1,this._isBlendEquationParametersDirty=!1,this._isBlendConstantsDirty=!1},a.prototype.apply=function(a){this.isDirty&&(this._isAlphaBlendDirty&&(this._alphaBlend?a.enable(a.BLEND):a.disable(a.BLEND),this._isAlphaBlendDirty=!1),this._isBlendFunctionParametersDirty&&(a.blendFuncSeparate(this._blendFunctionParameters[0],this._blendFunctionParameters[1],this._blendFunctionParameters[2],this._blendFunctionParameters[3]),this._isBlendFunctionParametersDirty=!1),this._isBlendEquationParametersDirty&&(a.blendEquationSeparate(this._blendEquationParameters[0],this._blendEquationParameters[1]),this._isBlendEquationParametersDirty=!1),this._isBlendConstantsDirty&&(a.blendColor(this._blendConstants[0],this._blendConstants[1],this._blendConstants[2],this._blendConstants[3]),this._isBlendConstantsDirty=!1))},a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(){function a(){this._valueCache={},this.vertexCompilationError=null,this.fragmentCompilationError=null,this.programLinkError=null,this.programValidationError=null}return Object.defineProperty(a.prototype,"isAsync",{get:function(){return this.isParallelCompiled},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isReady",{get:function(){return!!this.program&&(!this.isParallelCompiled||this.engine._isRenderingStateCompiled(this))},enumerable:!1,configurable:!0}),a.prototype._handlesSpectorRebuildCallback=function(a){a&&this.program&&a(this.program)},a.prototype._fillEffectInformation=function(a,b,c,d,e,f,g,h){var i=this.engine;if(i.supportsUniformBuffers)for(var j in b)a.bindUniformBlock(j,b[j]);for(this.engine.getUniforms(this,c).forEach(function(a,b){d[c[b]]=a}),this._uniforms=d,j=0;jc._alphaIndex?1:b._alphaIndexb._distanceToCamera?-1:0},a.frontToBackSortCompare=function(a,b){return a._distanceToCamerab._distanceToCamera?1:0},a.prototype.prepare=function(){this._opaqueSubMeshes.reset(),this._transparentSubMeshes.reset(),this._alphaTestSubMeshes.reset(),this._depthOnlySubMeshes.reset(),this._particleSystems.reset(),this._spriteManagers.reset(),this._edgesRenderers.reset()},a.prototype.dispose=function(){this._opaqueSubMeshes.dispose(),this._transparentSubMeshes.dispose(),this._alphaTestSubMeshes.dispose(),this._depthOnlySubMeshes.dispose(),this._particleSystems.dispose(),this._spriteManagers.dispose(),this._edgesRenderers.dispose()},a.prototype.dispatch=function(a,b,c){void 0===b&&(b=a.getMesh()),void 0===c&&(c=a.getMaterial()),null!=c&&(c.needAlphaBlendingForMesh(b)?this._transparentSubMeshes.push(a):c.needAlphaTesting()?(c.needDepthPrePass&&this._depthOnlySubMeshes.push(a),this._alphaTestSubMeshes.push(a)):(c.needDepthPrePass&&this._depthOnlySubMeshes.push(a),this._opaqueSubMeshes.push(a)),b._renderingGroup=this,b._edgesRenderer&&b._edgesRenderer.isEnabled&&this._edgesRenderers.pushNoDuplicate(b._edgesRenderer))},a.prototype.dispatchSprites=function(a){this._spriteManagers.push(a)},a.prototype.dispatchParticles=function(a){this._particleSystems.push(a)},a.prototype._renderParticles=function(a){if(0!==this._particleSystems.length){var b=this._scene.activeCamera;this._scene.onBeforeParticlesRenderingObservable.notifyObservers(this._scene);for(var c=0;c=g.a.Xbox&&a<=g.a.Switch&&navigator.getGamepads&&this._updateDevice(a,b,c);d=d[c];if(void 0===d)throw"Unable to find input "+c+" for device "+g.a[a]+" in slot "+b;return d},a.prototype.isDeviceAvailable=function(a){return void 0!==this._inputs[a]},a.prototype.dispose=function(){this.onDeviceConnectedObservable.clear(),this.onDeviceDisconnectedObservable.clear(),this.onInputChangedObservable.clear(),this._elementToAttachTo&&(this._removeEvents(),window.removeEventListener("gamepadconnected",this._gamepadConnectedEvent),window.removeEventListener("gamepaddisconnected",this._gamepadDisconnectedEvent))},a.prototype._checkForConnectedDevices=function(){if(navigator.getGamepads)for(var a=0,b=navigator.getGamepads();a=0&&b._elementToAttachTo.hasPointerCapture(b._mouseId)?b._elementToAttachTo.releasePointerCapture(b._mouseId):a.pointerId&&b._elementToAttachTo.hasPointerCapture(a.pointerId)&&b._elementToAttachTo.releasePointerCapture(a.pointerId),b.onInputChangedObservable.notifyObservers(j),d!==g.a.Mouse){f=b._activeTouchIds.indexOf(a.pointerId);delete b._activeTouchIds[f],b._unregisterDevice(d,e)}}},this._wheelEventName="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll";var c=!1,d=function(){};try{var e={passive:{get:function(){c=!0}}};this._elementToAttachTo.addEventListener("test",d,e),this._elementToAttachTo.removeEventListener("test",d,e)}catch(a){}this._pointerBlurEvent=function(a){var c;if(b.isDeviceAvailable(g.a.Mouse)){a=b._inputs[g.a.Mouse][0];b._mouseId>=0&&b._elementToAttachTo.hasPointerCapture(b._mouseId)&&b._elementToAttachTo.releasePointerCapture(b._mouseId);for(var d=0;d<=g.c.BrowserForward;d++)1===a[d+2]&&(a[d+2]=0,(e=f.a.CreateDeviceEvent(g.a.Mouse,0,d+2,1,b,b._elementToAttachTo)).deviceType=g.a.Mouse,e.deviceSlot=0,e.inputIndex=d+2,e.currentState=a[d+2],e.previousState=1,b.onInputChangedObservable.notifyObservers(e))}if(b.isDeviceAvailable(g.a.Touch)){a=b._inputs[g.a.Touch];for(d in Object.keys(b._activeTouchIds)){var e,h=+d,i=b._activeTouchIds[h];(b._elementToAttachTo.hasPointerCapture(i)&&b._elementToAttachTo.releasePointerCapture(i),1===(null===(c=a[h])||void 0===c?void 0:c[g.c.LeftClick]))&&(a[h][g.c.LeftClick]=0,(e=f.a.CreateDeviceEvent(g.a.Touch,i,g.c.LeftClick,1,b,b._elementToAttachTo)).deviceType=g.a.Mouse,e.deviceSlot=h,e.inputIndex=g.c.LeftClick,e.currentState=a[h][g.c.LeftClick],e.previousState=1,b.onInputChangedObservable.notifyObservers(e),b._unregisterDevice(g.a.Touch,h))}for(;void 0!==b._activeTouchIds.pop(););}},this._pointerWheelEvent=function(c){var d=g.a.Mouse;b._inputs[d]||(b._inputs[d]=[]),b._inputs[d][0]||(b._pointerActive=!0,b._registerDevice(d,0,a.MAX_POINTER_INPUTS));var e=b._inputs[d][0];if(e){var f=e[g.c.MouseWheelX],h=e[g.c.MouseWheelY],i=e[g.c.MouseWheelZ];e[g.c.MouseWheelX]=c.deltaX||0,e[g.c.MouseWheelY]=c.deltaY||c.wheelDelta||0,e[g.c.MouseWheelZ]=c.deltaZ||0;c=c;c.deviceType=d,c.deviceSlot=0,0!==e[g.c.MouseWheelX]&&(c.inputIndex=g.c.MouseWheelX,c.previousState=f,c.currentState=e[g.c.MouseWheelX],b.onInputChangedObservable.notifyObservers(c)),0!==e[g.c.MouseWheelY]&&(c.inputIndex=g.c.MouseWheelY,c.previousState=h,c.currentState=e[g.c.MouseWheelY],b.onInputChangedObservable.notifyObservers(c)),0!==e[g.c.MouseWheelZ]&&(c.inputIndex=g.c.MouseWheelZ,c.previousState=i,c.currentState=e[g.c.MouseWheelZ],b.onInputChangedObservable.notifyObservers(c))}},this._elementToAttachTo.addEventListener(this._eventPrefix+"move",this._pointerMoveEvent),this._elementToAttachTo.addEventListener(this._eventPrefix+"down",this._pointerDownEvent),this._elementToAttachTo.addEventListener(this._eventPrefix+"up",this._pointerUpEvent),this._elementToAttachTo.addEventListener("blur",this._pointerBlurEvent),this._elementToAttachTo.addEventListener(this._wheelEventName,this._pointerWheelEvent,!!c&&{passive:!1}),this._pointerInputClearObserver=this._engine.onEndFrameObservable.add(function(){if(b.isDeviceAvailable(g.a.Mouse)){var a=b._inputs[g.a.Mouse][0];a[g.c.MouseWheelX]=0,a[g.c.MouseWheelY]=0,a[g.c.MouseWheelZ]=0,a[g.c.DeltaHorizontal]=0,a[g.c.DeltaVertical]=0}})},a.prototype._handleGamepadActions=function(){var a=this;this._gamepadConnectedEvent=function(b){a._addGamePad(b.gamepad)},this._gamepadDisconnectedEvent=function(b){if(a._gamepads){var c=a._getGamepadDeviceType(b.gamepad.id);b=b.gamepad.index;a._unregisterDevice(c,b),delete a._gamepads[b]}},window.addEventListener("gamepadconnected",this._gamepadConnectedEvent),window.addEventListener("gamepaddisconnected",this._gamepadDisconnectedEvent)},a.prototype._updateDevice=function(a,b,c){var d=navigator.getGamepads()[b];if(d&&a===this._gamepads[b]){a=this._inputs[a][b];c>=d.buttons.length?a[c]=d.axes[c-d.buttons.length].valueOf():a[c]=d.buttons[c].value}},a.prototype._getGamepadDeviceType=function(a){return-1!==a.indexOf("054c")&&-1===a.indexOf("0ce6")?g.a.DualShock:-1!==a.indexOf("Xbox One")||-1!==a.search("Xbox 360")||-1!==a.search("xinput")?g.a.Xbox:-1!==a.indexOf("057e")?g.a.Switch:g.a.Generic},a.prototype._getPointerType=function(a){var b=g.a.Mouse;return("touch"===a.pointerType||"pen"===a.pointerType||a.touches)&&(b=g.a.Touch),b},a.prototype._removeEvents=function(){this._elementToAttachTo.removeEventListener("blur",this._keyboardBlurEvent),this._elementToAttachTo.removeEventListener("blur",this._pointerBlurEvent),this._keyboardActive&&(this._elementToAttachTo.removeEventListener("keydown",this._keyboardDownEvent),this._elementToAttachTo.removeEventListener("keyup",this._keyboardUpEvent)),this._pointerActive&&(this._elementToAttachTo.removeEventListener(this._eventPrefix+"move",this._pointerMoveEvent),this._elementToAttachTo.removeEventListener(this._eventPrefix+"down",this._pointerDownEvent),this._elementToAttachTo.removeEventListener(this._eventPrefix+"up",this._pointerUpEvent),this._elementToAttachTo.removeEventListener(this._wheelEventName,this._pointerWheelEvent),this._pointerInputClearObserver&&this._engine.onEndFrameObservable.remove(this._pointerInputClearObserver))},a.MAX_KEYCODES=255,a.MAX_POINTER_INPUTS=Object.keys(g.c).length/2,a}()},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a){a.prototype.setActiveCameraByID=function(a){return this.setActiveCameraById(a)},a.prototype.getLastMaterialByID=function(a){return this.getLastMaterialById(a)},a.prototype.getMaterialByID=function(a){return this.getMaterialById(a)},a.prototype.getTextureByUniqueID=function(a){return this.getTextureByUniqueId(a)},a.prototype.getCameraByID=function(a){return this.getCameraById(a)},a.prototype.getCameraByUniqueID=function(a){return this.getCameraByUniqueId(a)},a.prototype.getBoneByID=function(a){return this.getBoneById(a)},a.prototype.getLightByID=function(a){return this.getLightById(a)},a.prototype.getLightByUniqueID=function(a){return this.getLightByUniqueId(a)},a.prototype.getParticleSystemByID=function(a){return this.getParticleSystemById(a)},a.prototype.getGeometryByID=function(a){return this.getGeometryById(a)},a.prototype.getMeshByID=function(a){return this.getMeshById(a)},a.prototype.getMeshesByID=function(a){return this.getMeshesById(a)},a.prototype.getTransformNodeByID=function(a){return this.getTransformNodeById(a)},a.prototype.getTransformNodeByUniqueID=function(a){return this.getTransformNodeByUniqueId(a)},a.prototype.getTransformNodesByID=function(a){return this.getTransformNodesById(a)},a.prototype.getMeshByUniqueID=function(a){return this.getMeshByUniqueId(a)},a.prototype.getLastMeshByID=function(a){return this.getLastMeshById(a)},a.prototype.getLastEntryByID=function(a){return this.getLastEntryById(a)},a.prototype.getNodeByID=function(a){return this.getNodeById(a)},a.prototype.getLastSkeletonByID=function(a){return this.getLastSkeletonById(a)}}},function(a,b,c){c.d(b,"a",function(){return e}),c.d(b,"b",function(){return f});var d=c(34),e=function(){function a(a){void 0===a&&(a=30),this._enabled=!0,this._rollingFrameTime=new f(a)}return a.prototype.sampleFrame=function(a){if(void 0===a&&(a=d.a.Now),this._enabled){if(null!=this._lastFrameTimeMs){var b=a-this._lastFrameTimeMs;this._rollingFrameTime.add(b)}this._lastFrameTimeMs=a}},Object.defineProperty(a.prototype,"averageFrameTime",{get:function(){return this._rollingFrameTime.average},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"averageFrameTimeVariance",{get:function(){return this._rollingFrameTime.variance},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"instantaneousFrameTime",{get:function(){return this._rollingFrameTime.history(0)},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"averageFPS",{get:function(){return 1e3/this._rollingFrameTime.average},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"instantaneousFPS",{get:function(){var a=this._rollingFrameTime.history(0);return 0===a?0:1e3/a},enumerable:!1,configurable:!0}),Object.defineProperty(a.prototype,"isSaturated",{get:function(){return this._rollingFrameTime.isSaturated()},enumerable:!1,configurable:!0}),a.prototype.enable=function(){this._enabled=!0},a.prototype.disable=function(){this._enabled=!1,this._lastFrameTimeMs=null},Object.defineProperty(a.prototype,"isEnabled",{get:function(){return this._enabled},enumerable:!1,configurable:!0}),a.prototype.reset=function(){this._lastFrameTimeMs=null,this._rollingFrameTime.reset()},a}(),f=function(){function a(a){this._samples=new Array(a),this.reset()}return a.prototype.add=function(a){var b;if(this.isSaturated()){var c=this._samples[this._pos];b=c-this.average,this.average-=b/(this._sampleCount-1),this._m2-=b*(c-this.average)}else this._sampleCount++;b=a-this.average,this.average+=b/this._sampleCount,this._m2+=b*(a-this.average),this.variance=this._m2/(this._sampleCount-1),this._samples[this._pos]=a,this._pos++,this._pos%=this._samples.length},a.prototype.history=function(a){if(a>=this._sampleCount||a>=this._samples.length)return 0;var b=this._wrapPosition(this._pos-1);return this._samples[this._wrapPosition(b-a)]},a.prototype.isSaturated=function(){return this._sampleCount>=this._samples.length},a.prototype.reset=function(){this.average=0,this.variance=0,this._sampleCount=0,this._pos=0,this._m2=0},a.prototype._wrapPosition=function(a){var b=this._samples.length;return(a%b+b)%b},a}()},function(a,b,c){c.d(b,"a",function(){return e});var d=c(0),e=function(){this._checkCollisions=!1,this._collisionMask=-1,this._collisionGroup=-1,this._surroundingMeshes=null,this._collider=null,this._oldPositionForCollisions=new d.e(0,0,0),this._diffPositionForCollisions=new d.e(0,0,0),this._collisionResponse=!0}},function(a,b,c){c.d(b,"a",function(){return d});var d=function(a,b){this.distanceOrScreenCoverage=a,this.mesh=b}},function(a,b,c){c.d(b,"a",function(){return e});var d=c(26),e=function(a){a.prototype.setMaterialByID=function(a){return this.setMaterialById(a)},a.CreateDisc=a.CreateDisc||function(){throw Object(d.a)("MeshBuilder")},a.CreateBox=a.CreateBox||function(){throw Object(d.a)("MeshBuilder")},a.CreateSphere=a.CreateSphere||function(){throw Object(d.a)("MeshBuilder")},a.CreateCylinder=a.CreateCylinder||function(){throw Object(d.a)("MeshBuilder")},a.CreateTorusKnot=a.CreateTorusKnot||function(){throw Object(d.a)("MeshBuilder")},a.CreateTorus=a.CreateTorus||function(){throw Object(d.a)("MeshBuilder")},a.CreatePlane=a.CreatePlane||function(){throw Object(d.a)("MeshBuilder")},a.CreateGround=a.CreateGround||function(){throw Object(d.a)("MeshBuilder")},a.CreateTiledGround=a.CreateTiledGround||function(){throw Object(d.a)("MeshBuilder")},a.CreateGroundFromHeightMap=a.CreateGroundFromHeightMap||function(){throw Object(d.a)("MeshBuilder")},a.CreateTube=a.CreateTube||function(){throw Object(d.a)("MeshBuilder")},a.CreatePolyhedron=a.CreatePolyhedron||function(){throw Object(d.a)("MeshBuilder")},a.CreateIcoSphere=a.CreateIcoSphere||function(){throw Object(d.a)("MeshBuilder")},a.CreateDecal=a.CreateDecal||function(){throw Object(d.a)("MeshBuilder")},a.CreateCapsule=a.CreateCapsule||function(){throw Object(d.a)("MeshBuilder")},a.ExtendToGoldberg=a.ExtendToGoldberg||function(){throw Object(d.a)("MeshBuilder")}}},function(a,b,c){c.d(b,"a",function(){return k});var d=c(2),e=c(0),f=c(7);a=c(33);b=c(9);var g=c(47),h=c(46),i=c(4),j=c(12);b.a._instancedMeshFactory=function(a,b){a=new k(a,b);if(b.instancedBuffers)for(var c in a.instancedBuffers={},b.instancedBuffers)a.instancedBuffers[c]=b.instancedBuffers[c];return a};var k=function(a){function b(b,c){b=a.call(this,b,c.getScene())||this;b._indexInSourceMeshInstanceArray=-1,b._distanceToCamera=0,c.addInstance(b),b._sourceMesh=c,b._unIndexed=c._unIndexed,b.position.copyFrom(c.position),b.rotation.copyFrom(c.rotation),b.scaling.copyFrom(c.scaling),c.rotationQuaternion&&(b.rotationQuaternion=c.rotationQuaternion.clone()),b.animations=j.b.Slice(c.animations);for(var d=0,e=c.getAnimationRanges();d0!=this._getWorldMatrixDeterminant()>0)return this._internalAbstractMeshDataInfo._actAsRegularMesh=!0,!0;if(this._internalAbstractMeshDataInfo._actAsRegularMesh=!1,this._currentLOD._registerInstanceForRenderId(this,a),b){if(!this._currentLOD._internalAbstractMeshDataInfo._isActiveIntermediate)return this._currentLOD._internalAbstractMeshDataInfo._onlyForInstancesIntermediate=!0,!0}else if(!this._currentLOD._internalAbstractMeshDataInfo._isActive)return this._currentLOD._internalAbstractMeshDataInfo._onlyForInstances=!0,!0}return!1},b.prototype._postActivate=function(){this._sourceMesh.edgesShareWithInstances&&this._sourceMesh._edgesRenderer&&this._sourceMesh._edgesRenderer.isEnabled&&this._sourceMesh._renderingGroup?(this._sourceMesh._renderingGroup._edgesRenderers.pushNoDuplicate(this._sourceMesh._edgesRenderer),this._sourceMesh._edgesRenderer.customInstances.push(this.getWorldMatrix())):this._edgesRenderer&&this._edgesRenderer.isEnabled&&this._sourceMesh._renderingGroup&&this._sourceMesh._renderingGroup._edgesRenderers.push(this._edgesRenderer)},b.prototype.getWorldMatrix=function(){if(this._currentLOD&&this._currentLOD.billboardMode!==h.a.BILLBOARDMODE_NONE&&this._currentLOD._masterMesh!==this){this._billboardWorldMatrix||(this._billboardWorldMatrix=new e.a);var b=this._currentLOD._masterMesh;return this._currentLOD._masterMesh=this,e.c.Vector3[7].copyFrom(this._currentLOD.position),this._currentLOD.position.set(0,0,0),this._billboardWorldMatrix.copyFrom(this._currentLOD.computeWorldMatrix(!0)),this._currentLOD.position.copyFrom(e.c.Vector3[7]),this._currentLOD._masterMesh=b,this._billboardWorldMatrix}return a.prototype.getWorldMatrix.call(this)},Object.defineProperty(b.prototype,"isAnInstance",{get:function(){return!0},enumerable:!1,configurable:!0}),b.prototype.getLOD=function(a){if(!a)return this;var b=this.getBoundingInfo();return this._currentLOD=this.sourceMesh.getLOD(a,b.boundingSphere),this._currentLOD===this.sourceMesh?this.sourceMesh:this._currentLOD},b.prototype._preActivateForIntermediateRendering=function(a){return this.sourceMesh._preActivateForIntermediateRendering(a)},b.prototype._syncSubMeshes=function(){if(this.releaseSubMeshes(),this._sourceMesh.subMeshes)for(var a=0;a=lightDirection.w) { cosAngle=max(0.,pow(cosAngle,lightData.w)); attenuation*=cosAngle; float ndl=max(0.,dot(vNormal,lightVectorW)); #ifdef NDOTL result.ndl=ndl; #endif result.diffuse=ndl*diffuseColor*attenuation; #ifdef SPECULARTERM vec3 angleW=normalize(viewDirectionW+lightVectorW); float specComp=max(0.,dot(vNormal,angleW)); specComp=pow(specComp,max(1.,glossiness)); result.specular=specComp*specularColor*attenuation; #endif return result; } result.diffuse=vec3(0.); #ifdef SPECULARTERM result.specular=vec3(0.); #endif #ifdef NDOTL result.ndl=0.; #endif return result; } lightingInfo computeHemisphericLighting(vec3 viewDirectionW,vec3 vNormal,vec4 lightData,vec3 diffuseColor,vec3 specularColor,vec3 groundColor,float glossiness) { lightingInfo result; float ndl=dot(vNormal,lightData.xyz)*0.5+0.5; #ifdef NDOTL result.ndl=ndl; #endif result.diffuse=mix(groundColor,diffuseColor,ndl); #ifdef SPECULARTERM vec3 angleW=normalize(viewDirectionW+lightData.xyz); float specComp=max(0.,dot(vNormal,angleW)); specComp=pow(specComp,max(1.,glossiness)); result.specular=specComp*specularColor; #endif return result; } #define inline vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler,mat4 textureProjectionMatrix){ vec4 strq=textureProjectionMatrix*vec4(vPositionW,1.0); strq/=strq.w; vec3 textureColor=texture2D(projectionLightSampler,strq.xy).rgb; return textureColor; }";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="samplerFragmentDeclaration";b="#ifdef _DEFINENAME_ #if _DEFINENAME_DIRECTUV == 1 #define v_VARYINGNAME_UV vMainUV1 #elif _DEFINENAME_DIRECTUV == 2 #define v_VARYINGNAME_UV vMainUV2 #elif _DEFINENAME_DIRECTUV == 3 #define v_VARYINGNAME_UV vMainUV3 #elif _DEFINENAME_DIRECTUV == 4 #define v_VARYINGNAME_UV vMainUV4 #elif _DEFINENAME_DIRECTUV == 5 #define v_VARYINGNAME_UV vMainUV5 #elif _DEFINENAME_DIRECTUV == 6 #define v_VARYINGNAME_UV vMainUV6 #else varying vec2 v_VARYINGNAME_UV; #endif uniform sampler2D _SAMPLERNAME_Sampler; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="logDepthFragment";b="#ifdef LOGARITHMICDEPTH gl_FragDepthEXT=log2(vFragmentDepth)*logarithmicDepthConstant*0.5; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#if defined(BUMP) || defined(PARALLAX) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) #if defined(TANGENT) && defined(NORMAL) vec3 tbnNormal=normalize(normalUpdated); vec3 tbnTangent=normalize(tangentUpdated.xyz); vec3 tbnBitangent=cross(tbnNormal,tbnTangent)*tangentUpdated.w; vTBN=mat3(finalWorld)*mat3(tbnTangent,tbnBitangent,tbnNormal); #endif #endif";c(5).a.IncludesShadersStore.bumpVertex=a},function(a,b,c){a="#ifdef FOG vFogDistance=(view*worldPos).xyz; #endif";c(5).a.IncludesShadersStore.fogVertex=a},function(a,b,c){a="logDepthVertex";b="#ifdef LOGARITHMICDEPTH vFragmentDepth=1.0+gl_Position.w; gl_Position.z=log2(max(0.000001,vFragmentDepth))*logarithmicDepthConstant; #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a=c(5);b=(c(122),c(109),"colorPixelShader");c="#ifdef VERTEXCOLOR varying vec4 vColor; #else uniform vec4 color; #endif #include void main(void) { #include #ifdef VERTEXCOLOR gl_FragColor=vColor; #else gl_FragColor=color; #endif }";a.a.ShadersStore[b]=c},function(a,b,c){a=c(5);b=(c(87),c(123),c(92),c(88),c(89),c(112),"colorVertexShader");c=" attribute vec3 position; #ifdef VERTEXCOLOR attribute vec4 color; #endif #include #include #include uniform mat4 viewProjection; #ifdef MULTIVIEW uniform mat4 viewProjectionR; #endif #ifdef VERTEXCOLOR varying vec4 vColor; #endif void main(void) { #include #include vec4 worldPos=finalWorld*vec4(position,1.0); #ifdef MULTIVIEW if (gl_ViewID_OVR == 0u) { gl_Position=viewProjection*worldPos; } else { gl_Position=viewProjectionR*worldPos; } #else gl_Position=viewProjection*worldPos; #endif #include #ifdef VERTEXCOLOR vColor=color; #endif }";a.a.ShadersStore[b]=c},function(a,b){b=function(){return this}();try{b=b||new Function("return this")()}catch(a){"object"==typeof window&&(b=window)}a.exports=b},function(a,b,c){a="prePassDeclaration";b="#ifdef PREPASS #extension GL_EXT_draw_buffers : require layout(location=0) out highp vec4 glFragData[{X}]; highp vec4 gl_FragColor; #ifdef PREPASS_DEPTH varying highp vec3 vViewPos; #endif #ifdef PREPASS_VELOCITY varying highp vec4 vCurrentPosition; varying highp vec4 vPreviousPosition; #endif #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="oitDeclaration";b="#ifdef ORDER_INDEPENDENT_TRANSPARENCY #extension GL_EXT_draw_buffers : require layout(location=0) out vec2 depth; layout(location=1) out vec4 frontColor; layout(location=2) out vec4 backColor; #define MAX_DEPTH 99999.0 highp vec4 gl_FragColor; uniform sampler2D oitDepthSampler; uniform sampler2D oitFrontColorSampler; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="fresnelFunction";b="#ifdef FRESNEL float computeFresnelTerm(vec3 viewDirection,vec3 worldNormal,float bias,float power) { float fresnelTerm=pow(bias+abs(dot(viewDirection,worldNormal)),power); return clamp(fresnelTerm,0.,1.); } #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="#ifdef ORDER_INDEPENDENT_TRANSPARENCY float fragDepth=gl_FragCoord.z; #ifdef USE_REVERSE_DEPTHBUFFER fragDepth=1.0-fragDepth; #endif ivec2 fragCoord=ivec2(gl_FragCoord.xy); vec2 lastDepth=texelFetch(oitDepthSampler,fragCoord,0).rg; vec4 lastFrontColor=texelFetch(oitFrontColorSampler,fragCoord,0); depth.rg=vec2(-MAX_DEPTH); frontColor=lastFrontColor; backColor=vec4(0.0); float nearestDepth=-lastDepth.x; float furthestDepth=lastDepth.y; float alphaMultiplier=1.0-lastFrontColor.a; if (fragDepthfurthestDepth) { return; } if (fragDepth>nearestDepth && fragDepth0 mat4 previousInfluence; previousInfluence=mPreviousBones[int(matricesIndices[0])]*matricesWeights[0]; #if NUM_BONE_INFLUENCERS>1 previousInfluence+=mPreviousBones[int(matricesIndices[1])]*matricesWeights[1]; #endif #if NUM_BONE_INFLUENCERS>2 previousInfluence+=mPreviousBones[int(matricesIndices[2])]*matricesWeights[2]; #endif #if NUM_BONE_INFLUENCERS>3 previousInfluence+=mPreviousBones[int(matricesIndices[3])]*matricesWeights[3]; #endif #if NUM_BONE_INFLUENCERS>4 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[0])]*matricesWeightsExtra[0]; #endif #if NUM_BONE_INFLUENCERS>5 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[1])]*matricesWeightsExtra[1]; #endif #if NUM_BONE_INFLUENCERS>6 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[2])]*matricesWeightsExtra[2]; #endif #if NUM_BONE_INFLUENCERS>7 previousInfluence+=mPreviousBones[int(matricesIndicesExtra[3])]*matricesWeightsExtra[3]; #endif vPreviousPosition=previousViewProjection*previousWorld*previousInfluence*vec4(positionUpdated,1.0); #else vPreviousPosition=previousViewProjection*previousWorld*vec4(positionUpdated,1.0); #endif #endif";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="uvVariableDeclaration";b="#if !defined(UV{X}) && defined(MAINUV{X}) vec2 uv{X}=vec2(0.,0.); #endif #ifdef MAINUV{X} vMainUV{X}=uv{X}; #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){a="samplerVertexImplementation";b="#if defined(_DEFINENAME_) && _DEFINENAME_DIRECTUV == 0 if (v_INFONAME_ == 0.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uvUpdated,1.0,0.0)); } #ifdef UV2 else if (v_INFONAME_ == 1.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv2,1.0,0.0)); } #endif #ifdef UV3 else if (v_INFONAME_ == 2.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv3,1.0,0.0)); } #endif #ifdef UV4 else if (v_INFONAME_ == 3.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv4,1.0,0.0)); } #endif #ifdef UV5 else if (v_INFONAME_ == 4.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv5,1.0,0.0)); } #endif #ifdef UV6 else if (v_INFONAME_ == 5.) { v_VARYINGNAME_UV=vec2(_MATRIXNAME_Matrix*vec4(uv6,1.0,0.0)); } #endif #endif ";c(5).a.IncludesShadersStore[a]=b},function(a,b,c){c.r(b),(function(a){c.d(b,"Debug",function(){return h});var d=c(148),e=c(108);c.d(b,"AbstractScene",function(){return d.AbstractScene}),c.d(b,"AbstractActionManager",function(){return d.AbstractActionManager}),c.d(b,"Action",function(){return d.Action}),c.d(b,"ActionEvent",function(){return d.ActionEvent}),c.d(b,"ActionManager",function(){return d.ActionManager}),c.d(b,"Condition",function(){return d.Condition}),c.d(b,"ValueCondition",function(){return d.ValueCondition}),c.d(b,"PredicateCondition",function(){return d.PredicateCondition}),c.d(b,"StateCondition",function(){return d.StateCondition}),c.d(b,"SwitchBooleanAction",function(){return d.SwitchBooleanAction}),c.d(b,"SetStateAction",function(){return d.SetStateAction}),c.d(b,"SetValueAction",function(){return d.SetValueAction}),c.d(b,"IncrementValueAction",function(){return d.IncrementValueAction}),c.d(b,"PlayAnimationAction",function(){return d.PlayAnimationAction}),c.d(b,"StopAnimationAction",function(){return d.StopAnimationAction}),c.d(b,"DoNothingAction",function(){return d.DoNothingAction}),c.d(b,"CombineAction",function(){return d.CombineAction}),c.d(b,"ExecuteCodeAction",function(){return d.ExecuteCodeAction}),c.d(b,"SetParentAction",function(){return d.SetParentAction}),c.d(b,"PlaySoundAction",function(){return d.PlaySoundAction}),c.d(b,"StopSoundAction",function(){return d.StopSoundAction}),c.d(b,"InterpolateValueAction",function(){return d.InterpolateValueAction}),c.d(b,"Animatable",function(){return d.Animatable}),c.d(b,"_IAnimationState",function(){return d._IAnimationState}),c.d(b,"Animation",function(){return d.Animation}),c.d(b,"TargetedAnimation",function(){return d.TargetedAnimation}),c.d(b,"AnimationGroup",function(){return d.AnimationGroup}),c.d(b,"AnimationPropertiesOverride",function(){return d.AnimationPropertiesOverride}),c.d(b,"EasingFunction",function(){return d.EasingFunction}),c.d(b,"CircleEase",function(){return d.CircleEase}),c.d(b,"BackEase",function(){return d.BackEase}),c.d(b,"BounceEase",function(){return d.BounceEase}),c.d(b,"CubicEase",function(){return d.CubicEase}),c.d(b,"ElasticEase",function(){return d.ElasticEase}),c.d(b,"ExponentialEase",function(){return d.ExponentialEase}),c.d(b,"PowerEase",function(){return d.PowerEase}),c.d(b,"QuadraticEase",function(){return d.QuadraticEase}),c.d(b,"QuarticEase",function(){return d.QuarticEase}),c.d(b,"QuinticEase",function(){return d.QuinticEase}),c.d(b,"SineEase",function(){return d.SineEase}),c.d(b,"BezierCurveEase",function(){return d.BezierCurveEase}),c.d(b,"RuntimeAnimation",function(){return d.RuntimeAnimation}),c.d(b,"AnimationEvent",function(){return d.AnimationEvent}),c.d(b,"AnimationKeyInterpolation",function(){return d.AnimationKeyInterpolation}),c.d(b,"AnimationRange",function(){return d.AnimationRange}),c.d(b,"PathCursor",function(){return d.PathCursor}),c.d(b,"KeepAssets",function(){return d.KeepAssets}),c.d(b,"InstantiatedEntries",function(){return d.InstantiatedEntries}),c.d(b,"AssetContainer",function(){return d.AssetContainer}),c.d(b,"Analyser",function(){return d.Analyser}),c.d(b,"AudioEngine",function(){return d.AudioEngine}),c.d(b,"AudioSceneComponent",function(){return d.AudioSceneComponent}),c.d(b,"Sound",function(){return d.Sound}),c.d(b,"SoundTrack",function(){return d.SoundTrack}),c.d(b,"WeightedSound",function(){return d.WeightedSound}),c.d(b,"AutoRotationBehavior",function(){return d.AutoRotationBehavior}),c.d(b,"BouncingBehavior",function(){return d.BouncingBehavior}),c.d(b,"FramingBehavior",function(){return d.FramingBehavior}),c.d(b,"AttachToBoxBehavior",function(){return d.AttachToBoxBehavior}),c.d(b,"FadeInOutBehavior",function(){return d.FadeInOutBehavior}),c.d(b,"MultiPointerScaleBehavior",function(){return d.MultiPointerScaleBehavior}),c.d(b,"PointerDragBehavior",function(){return d.PointerDragBehavior}),c.d(b,"SixDofDragBehavior",function(){return d.SixDofDragBehavior}),c.d(b,"SurfaceMagnetismBehavior",function(){return d.SurfaceMagnetismBehavior}),c.d(b,"BaseSixDofDragBehavior",function(){return d.BaseSixDofDragBehavior}),c.d(b,"FollowBehavior",function(){return d.FollowBehavior}),c.d(b,"HandConstraintZone",function(){return d.HandConstraintZone}),c.d(b,"HandConstraintOrientation",function(){return d.HandConstraintOrientation}),c.d(b,"HandConstraintVisibility",function(){return d.HandConstraintVisibility}),c.d(b,"HandConstraintBehavior",function(){return d.HandConstraintBehavior}),c.d(b,"Bone",function(){return d.Bone}),c.d(b,"BoneIKController",function(){return d.BoneIKController}),c.d(b,"BoneLookController",function(){return d.BoneLookController}),c.d(b,"Skeleton",function(){return d.Skeleton}),c.d(b,"Buffer",function(){return d.Buffer}),c.d(b,"VertexBuffer",function(){return d.VertexBuffer}),c.d(b,"DataBuffer",function(){return d.DataBuffer}),c.d(b,"StorageBuffer",function(){return d.StorageBuffer}),c.d(b,"BaseCameraMouseWheelInput",function(){return d.BaseCameraMouseWheelInput}),c.d(b,"BaseCameraPointersInput",function(){return d.BaseCameraPointersInput}),c.d(b,"ArcRotateCameraGamepadInput",function(){return d.ArcRotateCameraGamepadInput}),c.d(b,"ArcRotateCameraKeyboardMoveInput",function(){return d.ArcRotateCameraKeyboardMoveInput}),c.d(b,"ArcRotateCameraMouseWheelInput",function(){return d.ArcRotateCameraMouseWheelInput}),c.d(b,"ArcRotateCameraPointersInput",function(){return d.ArcRotateCameraPointersInput}),c.d(b,"ArcRotateCameraVRDeviceOrientationInput",function(){return d.ArcRotateCameraVRDeviceOrientationInput}),c.d(b,"FlyCameraKeyboardInput",function(){return d.FlyCameraKeyboardInput}),c.d(b,"FlyCameraMouseInput",function(){return d.FlyCameraMouseInput}),c.d(b,"FollowCameraKeyboardMoveInput",function(){return d.FollowCameraKeyboardMoveInput}),c.d(b,"FollowCameraMouseWheelInput",function(){return d.FollowCameraMouseWheelInput}),c.d(b,"FollowCameraPointersInput",function(){return d.FollowCameraPointersInput}),c.d(b,"FreeCameraDeviceOrientationInput",function(){return d.FreeCameraDeviceOrientationInput}),c.d(b,"FreeCameraGamepadInput",function(){return d.FreeCameraGamepadInput}),c.d(b,"FreeCameraKeyboardMoveInput",function(){return d.FreeCameraKeyboardMoveInput}),c.d(b,"FreeCameraMouseInput",function(){return d.FreeCameraMouseInput}),c.d(b,"FreeCameraMouseWheelInput",function(){return d.FreeCameraMouseWheelInput}),c.d(b,"FreeCameraTouchInput",function(){return d.FreeCameraTouchInput}),c.d(b,"FreeCameraVirtualJoystickInput",function(){return d.FreeCameraVirtualJoystickInput}),c.d(b,"CameraInputTypes",function(){return d.CameraInputTypes}),c.d(b,"CameraInputsManager",function(){return d.CameraInputsManager}),c.d(b,"Camera",function(){return d.Camera}),c.d(b,"TargetCamera",function(){return d.TargetCamera}),c.d(b,"FreeCamera",function(){return d.FreeCamera}),c.d(b,"FreeCameraInputsManager",function(){return d.FreeCameraInputsManager}),c.d(b,"TouchCamera",function(){return d.TouchCamera}),c.d(b,"ArcRotateCamera",function(){return d.ArcRotateCamera}),c.d(b,"ArcRotateCameraInputsManager",function(){return d.ArcRotateCameraInputsManager}),c.d(b,"DeviceOrientationCamera",function(){return d.DeviceOrientationCamera}),c.d(b,"FlyCamera",function(){return d.FlyCamera}),c.d(b,"FlyCameraInputsManager",function(){return d.FlyCameraInputsManager}),c.d(b,"FollowCamera",function(){return d.FollowCamera}),c.d(b,"ArcFollowCamera",function(){return d.ArcFollowCamera}),c.d(b,"FollowCameraInputsManager",function(){return d.FollowCameraInputsManager}),c.d(b,"GamepadCamera",function(){return d.GamepadCamera}),c.d(b,"AnaglyphArcRotateCamera",function(){return d.AnaglyphArcRotateCamera}),c.d(b,"AnaglyphFreeCamera",function(){return d.AnaglyphFreeCamera}),c.d(b,"AnaglyphGamepadCamera",function(){return d.AnaglyphGamepadCamera}),c.d(b,"AnaglyphUniversalCamera",function(){return d.AnaglyphUniversalCamera}),c.d(b,"StereoscopicArcRotateCamera",function(){return d.StereoscopicArcRotateCamera}),c.d(b,"StereoscopicFreeCamera",function(){return d.StereoscopicFreeCamera}),c.d(b,"StereoscopicGamepadCamera",function(){return d.StereoscopicGamepadCamera}),c.d(b,"StereoscopicUniversalCamera",function(){return d.StereoscopicUniversalCamera}),c.d(b,"StereoscopicScreenUniversalCamera",function(){return d.StereoscopicScreenUniversalCamera}),c.d(b,"UniversalCamera",function(){return d.UniversalCamera}),c.d(b,"VirtualJoysticksCamera",function(){return d.VirtualJoysticksCamera}),c.d(b,"VRCameraMetrics",function(){return d.VRCameraMetrics}),c.d(b,"VRDeviceOrientationArcRotateCamera",function(){return d.VRDeviceOrientationArcRotateCamera}),c.d(b,"VRDeviceOrientationFreeCamera",function(){return d.VRDeviceOrientationFreeCamera}),c.d(b,"VRDeviceOrientationGamepadCamera",function(){return d.VRDeviceOrientationGamepadCamera}),c.d(b,"OnAfterEnteringVRObservableEvent",function(){return d.OnAfterEnteringVRObservableEvent}),c.d(b,"VRExperienceHelper",function(){return d.VRExperienceHelper}),c.d(b,"WebVRFreeCamera",function(){return d.WebVRFreeCamera}),c.d(b,"setStereoscopicAnaglyphRigMode",function(){return d.setStereoscopicAnaglyphRigMode}),c.d(b,"setStereoscopicRigMode",function(){return d.setStereoscopicRigMode}),c.d(b,"setVRRigMode",function(){return d.setVRRigMode}),c.d(b,"setWebVRRigMode",function(){return d.setWebVRRigMode}),c.d(b,"Collider",function(){return d.Collider}),c.d(b,"DefaultCollisionCoordinator",function(){return d.DefaultCollisionCoordinator}),c.d(b,"PickingInfo",function(){return d.PickingInfo}),c.d(b,"IntersectionInfo",function(){return d.IntersectionInfo}),c.d(b,"_MeshCollisionData",function(){return d._MeshCollisionData}),c.d(b,"ComputeEffect",function(){return d.ComputeEffect}),c.d(b,"ComputeShader",function(){return d.ComputeShader}),c.d(b,"BoundingBox",function(){return d.BoundingBox}),c.d(b,"BoundingInfo",function(){return d.BoundingInfo}),c.d(b,"BoundingSphere",function(){return d.BoundingSphere}),c.d(b,"Octree",function(){return d.Octree}),c.d(b,"OctreeBlock",function(){return d.OctreeBlock}),c.d(b,"OctreeSceneComponent",function(){return d.OctreeSceneComponent}),c.d(b,"Ray",function(){return d.Ray}),c.d(b,"AxesViewer",function(){return d.AxesViewer}),c.d(b,"BoneAxesViewer",function(){return d.BoneAxesViewer}),c.d(b,"DebugLayerTab",function(){return d.DebugLayerTab}),c.d(b,"DebugLayer",function(){return d.DebugLayer}),c.d(b,"PhysicsViewer",function(){return d.PhysicsViewer}),c.d(b,"RayHelper",function(){return d.RayHelper}),c.d(b,"SkeletonViewer",function(){return d.SkeletonViewer}),c.d(b,"DirectionalLightFrustumViewer",function(){return d.DirectionalLightFrustumViewer}),c.d(b,"DeviceInputSystem",function(){return d.DeviceInputSystem}),c.d(b,"DeviceType",function(){return d.DeviceType}),c.d(b,"PointerInput",function(){return d.PointerInput}),c.d(b,"DualShockInput",function(){return d.DualShockInput}),c.d(b,"XboxInput",function(){return d.XboxInput}),c.d(b,"SwitchInput",function(){return d.SwitchInput}),c.d(b,"DeviceEventFactory",function(){return d.DeviceEventFactory}),c.d(b,"NativeDeviceInputWrapper",function(){return d.NativeDeviceInputWrapper}),c.d(b,"WebDeviceInputSystem",function(){return d.WebDeviceInputSystem}),c.d(b,"DeviceSource",function(){return d.DeviceSource}),c.d(b,"DeviceSourceManager",function(){return d.DeviceSourceManager}),c.d(b,"Constants",function(){return d.Constants}),c.d(b,"ThinEngine",function(){return d.ThinEngine}),c.d(b,"Engine",function(){return d.Engine}),c.d(b,"EngineStore",function(){return d.EngineStore}),c.d(b,"NullEngineOptions",function(){return d.NullEngineOptions}),c.d(b,"NullEngine",function(){return d.NullEngine}),c.d(b,"_OcclusionDataStorage",function(){return d._OcclusionDataStorage}),c.d(b,"_forceTransformFeedbackToBundle",function(){return d._forceTransformFeedbackToBundle}),c.d(b,"EngineView",function(){return d.EngineView}),c.d(b,"allocateAndCopyTypedBuffer",function(){return d.allocateAndCopyTypedBuffer}),c.d(b,"ComputeBindingType",function(){return d.ComputeBindingType}),c.d(b,"NativeDataStream",function(){return d.NativeDataStream}),c.d(b,"WebGLPipelineContext",function(){return d.WebGLPipelineContext}),c.d(b,"WebGLHardwareTexture",function(){return d.WebGLHardwareTexture}),c.d(b,"PowerPreference",function(){return d.PowerPreference}),c.d(b,"FeatureName",function(){return d.FeatureName}),c.d(b,"BufferUsage",function(){return d.BufferUsage}),c.d(b,"MapMode",function(){return d.MapMode}),c.d(b,"TextureDimension",function(){return d.TextureDimension}),c.d(b,"TextureUsage",function(){return d.TextureUsage}),c.d(b,"TextureViewDimension",function(){return d.TextureViewDimension}),c.d(b,"TextureAspect",function(){return d.TextureAspect}),c.d(b,"TextureFormat",function(){return d.TextureFormat}),c.d(b,"AddressMode",function(){return d.AddressMode}),c.d(b,"FilterMode",function(){return d.FilterMode}),c.d(b,"CompareFunction",function(){return d.CompareFunction}),c.d(b,"ShaderStage",function(){return d.ShaderStage}),c.d(b,"BufferBindingType",function(){return d.BufferBindingType}),c.d(b,"SamplerBindingType",function(){return d.SamplerBindingType}),c.d(b,"TextureSampleType",function(){return d.TextureSampleType}),c.d(b,"StorageTextureAccess",function(){return d.StorageTextureAccess}),c.d(b,"CompilationMessageType",function(){return d.CompilationMessageType}),c.d(b,"PrimitiveTopology",function(){return d.PrimitiveTopology}),c.d(b,"FrontFace",function(){return d.FrontFace}),c.d(b,"CullMode",function(){return d.CullMode}),c.d(b,"ColorWrite",function(){return d.ColorWrite}),c.d(b,"BlendFactor",function(){return d.BlendFactor}),c.d(b,"BlendOperation",function(){return d.BlendOperation}),c.d(b,"StencilOperation",function(){return d.StencilOperation}),c.d(b,"IndexFormat",function(){return d.IndexFormat}),c.d(b,"VertexFormat",function(){return d.VertexFormat}),c.d(b,"InputStepMode",function(){return d.InputStepMode}),c.d(b,"LoadOp",function(){return d.LoadOp}),c.d(b,"StoreOp",function(){return d.StoreOp}),c.d(b,"QueryType",function(){return d.QueryType}),c.d(b,"PipelineStatisticName",function(){return d.PipelineStatisticName}),c.d(b,"CanvasCompositingAlphaMode",function(){return d.CanvasCompositingAlphaMode}),c.d(b,"DeviceLostReason",function(){return d.DeviceLostReason}),c.d(b,"ErrorFilter",function(){return d.ErrorFilter}),c.d(b,"WebGPUEngine",function(){return d.WebGPUEngine}),c.d(b,"WebGPUCacheRenderPipeline",function(){return d.WebGPUCacheRenderPipeline}),c.d(b,"WebGPUCacheRenderPipelineTree",function(){return d.WebGPUCacheRenderPipelineTree}),c.d(b,"WebGPUCacheBindGroups",function(){return d.WebGPUCacheBindGroups}),c.d(b,"WebGPUCacheSampler",function(){return d.WebGPUCacheSampler}),c.d(b,"WebGPUTintWASM",function(){return d.WebGPUTintWASM}),c.d(b,"WebGL2ShaderProcessor",function(){return d.WebGL2ShaderProcessor}),c.d(b,"NativeEngine",function(){return d.NativeEngine}),c.d(b,"ShaderCodeInliner",function(){return d.ShaderCodeInliner}),c.d(b,"PerformanceConfigurator",function(){return d.PerformanceConfigurator}),c.d(b,"EngineFactory",function(){return d.EngineFactory}),c.d(b,"ShaderStore",function(){return d.ShaderStore}),c.d(b,"KeyboardEventTypes",function(){return d.KeyboardEventTypes}),c.d(b,"KeyboardInfo",function(){return d.KeyboardInfo}),c.d(b,"KeyboardInfoPre",function(){return d.KeyboardInfoPre}),c.d(b,"PointerEventTypes",function(){return d.PointerEventTypes}),c.d(b,"PointerInfoBase",function(){return d.PointerInfoBase}),c.d(b,"PointerInfoPre",function(){return d.PointerInfoPre}),c.d(b,"PointerInfo",function(){return d.PointerInfo}),c.d(b,"ClipboardEventTypes",function(){return d.ClipboardEventTypes}),c.d(b,"ClipboardInfo",function(){return d.ClipboardInfo}),c.d(b,"DeviceInputEventType",function(){return d.DeviceInputEventType}),c.d(b,"EventConstants",function(){return d.EventConstants}),c.d(b,"DaydreamController",function(){return d.DaydreamController}),c.d(b,"GearVRController",function(){return d.GearVRController}),c.d(b,"GenericController",function(){return d.GenericController}),c.d(b,"OculusTouchController",function(){return d.OculusTouchController}),c.d(b,"PoseEnabledControllerType",function(){return d.PoseEnabledControllerType}),c.d(b,"PoseEnabledControllerHelper",function(){return d.PoseEnabledControllerHelper}),c.d(b,"PoseEnabledController",function(){return d.PoseEnabledController}),c.d(b,"ViveController",function(){return d.ViveController}),c.d(b,"WebVRController",function(){return d.WebVRController}),c.d(b,"WindowsMotionController",function(){return d.WindowsMotionController}),c.d(b,"XRWindowsMotionController",function(){return d.XRWindowsMotionController}),c.d(b,"StickValues",function(){return d.StickValues}),c.d(b,"Gamepad",function(){return d.Gamepad}),c.d(b,"GenericPad",function(){return d.GenericPad}),c.d(b,"GamepadManager",function(){return d.GamepadManager}),c.d(b,"GamepadSystemSceneComponent",function(){return d.GamepadSystemSceneComponent}),c.d(b,"Xbox360Button",function(){return d.Xbox360Button}),c.d(b,"Xbox360Dpad",function(){return d.Xbox360Dpad}),c.d(b,"Xbox360Pad",function(){return d.Xbox360Pad}),c.d(b,"DualShockButton",function(){return d.DualShockButton}),c.d(b,"DualShockDpad",function(){return d.DualShockDpad}),c.d(b,"DualShockPad",function(){return d.DualShockPad}),c.d(b,"AxisDragGizmo",function(){return d.AxisDragGizmo}),c.d(b,"AxisScaleGizmo",function(){return d.AxisScaleGizmo}),c.d(b,"BoundingBoxGizmo",function(){return d.BoundingBoxGizmo}),c.d(b,"Gizmo",function(){return d.Gizmo}),c.d(b,"GizmoManager",function(){return d.GizmoManager}),c.d(b,"PlaneRotationGizmo",function(){return d.PlaneRotationGizmo}),c.d(b,"PositionGizmo",function(){return d.PositionGizmo}),c.d(b,"RotationGizmo",function(){return d.RotationGizmo}),c.d(b,"ScaleGizmo",function(){return d.ScaleGizmo}),c.d(b,"LightGizmo",function(){return d.LightGizmo}),c.d(b,"CameraGizmo",function(){return d.CameraGizmo}),c.d(b,"PlaneDragGizmo",function(){return d.PlaneDragGizmo}),c.d(b,"EnvironmentHelper",function(){return d.EnvironmentHelper}),c.d(b,"PhotoDome",function(){return d.PhotoDome}),c.d(b,"_forceSceneHelpersToBundle",function(){return d._forceSceneHelpersToBundle}),c.d(b,"VideoDome",function(){return d.VideoDome}),c.d(b,"EngineInstrumentation",function(){return d.EngineInstrumentation}),c.d(b,"SceneInstrumentation",function(){return d.SceneInstrumentation}),c.d(b,"_TimeToken",function(){return d._TimeToken}),c.d(b,"EffectLayer",function(){return d.EffectLayer}),c.d(b,"EffectLayerSceneComponent",function(){return d.EffectLayerSceneComponent}),c.d(b,"GlowLayer",function(){return d.GlowLayer}),c.d(b,"HighlightLayer",function(){return d.HighlightLayer}),c.d(b,"Layer",function(){return d.Layer}),c.d(b,"LayerSceneComponent",function(){return d.LayerSceneComponent}),c.d(b,"LensFlare",function(){return d.LensFlare}),c.d(b,"LensFlareSystem",function(){return d.LensFlareSystem}),c.d(b,"LensFlareSystemSceneComponent",function(){return d.LensFlareSystemSceneComponent}),c.d(b,"Light",function(){return d.Light}),c.d(b,"ShadowLight",function(){return d.ShadowLight}),c.d(b,"ShadowGenerator",function(){return d.ShadowGenerator}),c.d(b,"CascadedShadowGenerator",function(){return d.CascadedShadowGenerator}),c.d(b,"ShadowGeneratorSceneComponent",function(){return d.ShadowGeneratorSceneComponent}),c.d(b,"DirectionalLight",function(){return d.DirectionalLight}),c.d(b,"HemisphericLight",function(){return d.HemisphericLight}),c.d(b,"PointLight",function(){return d.PointLight}),c.d(b,"SpotLight",function(){return d.SpotLight}),c.d(b,"DefaultLoadingScreen",function(){return d.DefaultLoadingScreen}),c.d(b,"_BabylonLoaderRegistered",function(){return d._BabylonLoaderRegistered}),c.d(b,"BabylonFileLoaderConfiguration",function(){return d.BabylonFileLoaderConfiguration}),c.d(b,"SceneLoaderAnimationGroupLoadingMode",function(){return d.SceneLoaderAnimationGroupLoadingMode}),c.d(b,"SceneLoader",function(){return d.SceneLoader}),c.d(b,"SceneLoaderFlags",function(){return d.SceneLoaderFlags}),c.d(b,"BackgroundMaterial",function(){return d.BackgroundMaterial}),c.d(b,"ColorCurves",function(){return d.ColorCurves}),c.d(b,"EffectFallbacks",function(){return d.EffectFallbacks}),c.d(b,"Effect",function(){return d.Effect}),c.d(b,"FresnelParameters",function(){return d.FresnelParameters}),c.d(b,"ImageProcessingConfigurationDefines",function(){return d.ImageProcessingConfigurationDefines}),c.d(b,"ImageProcessingConfiguration",function(){return d.ImageProcessingConfiguration}),c.d(b,"Material",function(){return d.Material}),c.d(b,"MaterialDefines",function(){return d.MaterialDefines}),c.d(b,"ThinMaterialHelper",function(){return d.ThinMaterialHelper}),c.d(b,"MaterialHelper",function(){return d.MaterialHelper}),c.d(b,"MultiMaterial",function(){return d.MultiMaterial}),c.d(b,"OcclusionMaterial",function(){return d.OcclusionMaterial}),c.d(b,"PBRMaterialDefines",function(){return d.PBRMaterialDefines}),c.d(b,"PBRBaseMaterial",function(){return d.PBRBaseMaterial}),c.d(b,"PBRBaseSimpleMaterial",function(){return d.PBRBaseSimpleMaterial}),c.d(b,"PBRMaterial",function(){return d.PBRMaterial}),c.d(b,"PBRMetallicRoughnessMaterial",function(){return d.PBRMetallicRoughnessMaterial}),c.d(b,"PBRSpecularGlossinessMaterial",function(){return d.PBRSpecularGlossinessMaterial}),c.d(b,"PushMaterial",function(){return d.PushMaterial}),c.d(b,"ShaderLanguage",function(){return d.ShaderLanguage}),c.d(b,"ShaderMaterial",function(){return d.ShaderMaterial}),c.d(b,"StandardMaterialDefines",function(){return d.StandardMaterialDefines}),c.d(b,"StandardMaterial",function(){return d.StandardMaterial}),c.d(b,"BaseTexture",function(){return d.BaseTexture}),c.d(b,"ColorGradingTexture",function(){return d.ColorGradingTexture}),c.d(b,"CubeTexture",function(){return d.CubeTexture}),c.d(b,"DynamicTexture",function(){return d.DynamicTexture}),c.d(b,"EquiRectangularCubeTexture",function(){return d.EquiRectangularCubeTexture}),c.d(b,"ExternalTexture",function(){return d.ExternalTexture}),c.d(b,"HDRFiltering",function(){return d.HDRFiltering}),c.d(b,"HDRCubeTexture",function(){return d.HDRCubeTexture}),c.d(b,"HtmlElementTexture",function(){return d.HtmlElementTexture}),c.d(b,"InternalTextureSource",function(){return d.InternalTextureSource}),c.d(b,"InternalTexture",function(){return d.InternalTexture}),c.d(b,"_DDSTextureLoader",function(){return d._DDSTextureLoader}),c.d(b,"_ENVTextureLoader",function(){return d._ENVTextureLoader}),c.d(b,"_KTXTextureLoader",function(){return d._KTXTextureLoader}),c.d(b,"_TGATextureLoader",function(){return d._TGATextureLoader}),c.d(b,"_HDRTextureLoader",function(){return d._HDRTextureLoader}),c.d(b,"_BasisTextureLoader",function(){return d._BasisTextureLoader}),c.d(b,"MirrorTexture",function(){return d.MirrorTexture}),c.d(b,"MultiRenderTarget",function(){return d.MultiRenderTarget}),c.d(b,"TexturePacker",function(){return d.TexturePacker}),c.d(b,"TexturePackerFrame",function(){return d.TexturePackerFrame}),c.d(b,"CustomProceduralTexture",function(){return d.CustomProceduralTexture}),c.d(b,"NoiseProceduralTexture",function(){return d.NoiseProceduralTexture}),c.d(b,"ProceduralTexture",function(){return d.ProceduralTexture}),c.d(b,"ProceduralTextureSceneComponent",function(){return d.ProceduralTextureSceneComponent}),c.d(b,"RawCubeTexture",function(){return d.RawCubeTexture}),c.d(b,"RawTexture",function(){return d.RawTexture}),c.d(b,"RawTexture2DArray",function(){return d.RawTexture2DArray}),c.d(b,"RawTexture3D",function(){return d.RawTexture3D}),c.d(b,"RefractionTexture",function(){return d.RefractionTexture}),c.d(b,"RenderTargetTexture",function(){return d.RenderTargetTexture}),c.d(b,"TextureSampler",function(){return d.TextureSampler}),c.d(b,"Texture",function(){return d.Texture}),c.d(b,"ThinTexture",function(){return d.ThinTexture}),c.d(b,"ThinRenderTargetTexture",function(){return d.ThinRenderTargetTexture}),c.d(b,"VideoTexture",function(){return d.VideoTexture}),c.d(b,"UniformBuffer",function(){return d.UniformBuffer}),c.d(b,"MaterialFlags",function(){return d.MaterialFlags}),c.d(b,"NodeMaterialBlockTargets",function(){return d.NodeMaterialBlockTargets}),c.d(b,"NodeMaterialBlockConnectionPointTypes",function(){return d.NodeMaterialBlockConnectionPointTypes}),c.d(b,"NodeMaterialBlockConnectionPointMode",function(){return d.NodeMaterialBlockConnectionPointMode}),c.d(b,"NodeMaterialSystemValues",function(){return d.NodeMaterialSystemValues}),c.d(b,"NodeMaterialModes",function(){return d.NodeMaterialModes}),c.d(b,"NodeMaterialConnectionPointCompatibilityStates",function(){return d.NodeMaterialConnectionPointCompatibilityStates}),c.d(b,"NodeMaterialConnectionPointDirection",function(){return d.NodeMaterialConnectionPointDirection}),c.d(b,"NodeMaterialConnectionPoint",function(){return d.NodeMaterialConnectionPoint}),c.d(b,"NodeMaterialBlock",function(){return d.NodeMaterialBlock}),c.d(b,"NodeMaterialDefines",function(){return d.NodeMaterialDefines}),c.d(b,"NodeMaterial",function(){return d.NodeMaterial}),c.d(b,"VertexOutputBlock",function(){return d.VertexOutputBlock}),c.d(b,"BonesBlock",function(){return d.BonesBlock}),c.d(b,"InstancesBlock",function(){return d.InstancesBlock}),c.d(b,"MorphTargetsBlock",function(){return d.MorphTargetsBlock}),c.d(b,"LightInformationBlock",function(){return d.LightInformationBlock}),c.d(b,"FragmentOutputBlock",function(){return d.FragmentOutputBlock}),c.d(b,"ImageProcessingBlock",function(){return d.ImageProcessingBlock}),c.d(b,"PerturbNormalBlock",function(){return d.PerturbNormalBlock}),c.d(b,"DiscardBlock",function(){return d.DiscardBlock}),c.d(b,"FrontFacingBlock",function(){return d.FrontFacingBlock}),c.d(b,"DerivativeBlock",function(){return d.DerivativeBlock}),c.d(b,"FragCoordBlock",function(){return d.FragCoordBlock}),c.d(b,"ScreenSizeBlock",function(){return d.ScreenSizeBlock}),c.d(b,"FogBlock",function(){return d.FogBlock}),c.d(b,"LightBlock",function(){return d.LightBlock}),c.d(b,"TextureBlock",function(){return d.TextureBlock}),c.d(b,"ReflectionTextureBlock",function(){return d.ReflectionTextureBlock}),c.d(b,"CurrentScreenBlock",function(){return d.CurrentScreenBlock}),c.d(b,"SceneDepthBlock",function(){return d.SceneDepthBlock}),c.d(b,"ImageSourceBlock",function(){return d.ImageSourceBlock}),c.d(b,"InputBlock",function(){return d.InputBlock}),c.d(b,"AnimatedInputBlockTypes",function(){return d.AnimatedInputBlockTypes}),c.d(b,"MultiplyBlock",function(){return d.MultiplyBlock}),c.d(b,"AddBlock",function(){return d.AddBlock}),c.d(b,"ScaleBlock",function(){return d.ScaleBlock}),c.d(b,"ClampBlock",function(){return d.ClampBlock}),c.d(b,"CrossBlock",function(){return d.CrossBlock}),c.d(b,"DotBlock",function(){return d.DotBlock}),c.d(b,"TransformBlock",function(){return d.TransformBlock}),c.d(b,"RemapBlock",function(){return d.RemapBlock}),c.d(b,"NormalizeBlock",function(){return d.NormalizeBlock}),c.d(b,"TrigonometryBlockOperations",function(){return d.TrigonometryBlockOperations}),c.d(b,"TrigonometryBlock",function(){return d.TrigonometryBlock}),c.d(b,"ColorMergerBlock",function(){return d.ColorMergerBlock}),c.d(b,"VectorMergerBlock",function(){return d.VectorMergerBlock}),c.d(b,"ColorSplitterBlock",function(){return d.ColorSplitterBlock}),c.d(b,"VectorSplitterBlock",function(){return d.VectorSplitterBlock}),c.d(b,"LerpBlock",function(){return d.LerpBlock}),c.d(b,"DivideBlock",function(){return d.DivideBlock}),c.d(b,"SubtractBlock",function(){return d.SubtractBlock}),c.d(b,"StepBlock",function(){return d.StepBlock}),c.d(b,"OneMinusBlock",function(){return d.OneMinusBlock}),c.d(b,"ViewDirectionBlock",function(){return d.ViewDirectionBlock}),c.d(b,"FresnelBlock",function(){return d.FresnelBlock}),c.d(b,"MaxBlock",function(){return d.MaxBlock}),c.d(b,"MinBlock",function(){return d.MinBlock}),c.d(b,"DistanceBlock",function(){return d.DistanceBlock}),c.d(b,"LengthBlock",function(){return d.LengthBlock}),c.d(b,"NegateBlock",function(){return d.NegateBlock}),c.d(b,"PowBlock",function(){return d.PowBlock}),c.d(b,"RandomNumberBlock",function(){return d.RandomNumberBlock}),c.d(b,"ArcTan2Block",function(){return d.ArcTan2Block}),c.d(b,"SmoothStepBlock",function(){return d.SmoothStepBlock}),c.d(b,"ReciprocalBlock",function(){return d.ReciprocalBlock}),c.d(b,"ReplaceColorBlock",function(){return d.ReplaceColorBlock}),c.d(b,"PosterizeBlock",function(){return d.PosterizeBlock}),c.d(b,"WaveBlockKind",function(){return d.WaveBlockKind}),c.d(b,"WaveBlock",function(){return d.WaveBlock}),c.d(b,"GradientBlockColorStep",function(){return d.GradientBlockColorStep}),c.d(b,"GradientBlock",function(){return d.GradientBlock}),c.d(b,"NLerpBlock",function(){return d.NLerpBlock}),c.d(b,"WorleyNoise3DBlock",function(){return d.WorleyNoise3DBlock}),c.d(b,"SimplexPerlin3DBlock",function(){return d.SimplexPerlin3DBlock}),c.d(b,"NormalBlendBlock",function(){return d.NormalBlendBlock}),c.d(b,"Rotate2dBlock",function(){return d.Rotate2dBlock}),c.d(b,"ReflectBlock",function(){return d.ReflectBlock}),c.d(b,"RefractBlock",function(){return d.RefractBlock}),c.d(b,"DesaturateBlock",function(){return d.DesaturateBlock}),c.d(b,"PBRMetallicRoughnessBlock",function(){return d.PBRMetallicRoughnessBlock}),c.d(b,"SheenBlock",function(){return d.SheenBlock}),c.d(b,"AnisotropyBlock",function(){return d.AnisotropyBlock}),c.d(b,"ReflectionBlock",function(){return d.ReflectionBlock}),c.d(b,"ClearCoatBlock",function(){return d.ClearCoatBlock}),c.d(b,"RefractionBlock",function(){return d.RefractionBlock}),c.d(b,"SubSurfaceBlock",function(){return d.SubSurfaceBlock}),c.d(b,"ParticleTextureBlock",function(){return d.ParticleTextureBlock}),c.d(b,"ParticleRampGradientBlock",function(){return d.ParticleRampGradientBlock}),c.d(b,"ParticleBlendMultiplyBlock",function(){return d.ParticleBlendMultiplyBlock}),c.d(b,"ModBlock",function(){return d.ModBlock}),c.d(b,"MatrixBuilderBlock",function(){return d.MatrixBuilderBlock}),c.d(b,"ConditionalBlockConditions",function(){return d.ConditionalBlockConditions}),c.d(b,"ConditionalBlock",function(){return d.ConditionalBlock}),c.d(b,"CloudBlock",function(){return d.CloudBlock}),c.d(b,"NodeMaterialOptimizer",function(){return d.NodeMaterialOptimizer}),c.d(b,"PropertyTypeForEdition",function(){return d.PropertyTypeForEdition}),c.d(b,"editableInPropertyPage",function(){return d.editableInPropertyPage}),c.d(b,"EffectRenderer",function(){return d.EffectRenderer}),c.d(b,"EffectWrapper",function(){return d.EffectWrapper}),c.d(b,"ShadowDepthWrapper",function(){return d.ShadowDepthWrapper}),c.d(b,"DrawWrapper",function(){return d.DrawWrapper}),c.d(b,"Scalar",function(){return d.Scalar}),c.d(b,"extractMinAndMaxIndexed",function(){return d.extractMinAndMaxIndexed}),c.d(b,"extractMinAndMax",function(){return d.extractMinAndMax}),c.d(b,"Space",function(){return d.Space}),c.d(b,"Axis",function(){return d.Axis}),c.d(b,"Coordinate",function(){return d.Coordinate}),c.d(b,"Color3",function(){return d.Color3}),c.d(b,"Color4",function(){return d.Color4}),c.d(b,"TmpColors",function(){return d.TmpColors}),c.d(b,"ToGammaSpace",function(){return d.ToGammaSpace}),c.d(b,"ToLinearSpace",function(){return d.ToLinearSpace}),c.d(b,"PHI",function(){return d.PHI}),c.d(b,"Epsilon",function(){return d.Epsilon}),c.d(b,"Frustum",function(){return d.Frustum}),c.d(b,"Orientation",function(){return d.Orientation}),c.d(b,"BezierCurve",function(){return d.BezierCurve}),c.d(b,"Angle",function(){return d.Angle}),c.d(b,"Arc2",function(){return d.Arc2}),c.d(b,"Path2",function(){return d.Path2}),c.d(b,"Path3D",function(){return d.Path3D}),c.d(b,"Curve3",function(){return d.Curve3}),c.d(b,"Plane",function(){return d.Plane}),c.d(b,"Size",function(){return d.Size}),c.d(b,"Vector2",function(){return d.Vector2}),c.d(b,"Vector3",function(){return d.Vector3}),c.d(b,"Vector4",function(){return d.Vector4}),c.d(b,"Quaternion",function(){return d.Quaternion}),c.d(b,"Matrix",function(){return d.Matrix}),c.d(b,"TmpVectors",function(){return d.TmpVectors}),c.d(b,"PositionNormalVertex",function(){return d.PositionNormalVertex}),c.d(b,"PositionNormalTextureVertex",function(){return d.PositionNormalTextureVertex}),c.d(b,"Viewport",function(){return d.Viewport}),c.d(b,"SphericalHarmonics",function(){return d.SphericalHarmonics}),c.d(b,"SphericalPolynomial",function(){return d.SphericalPolynomial}),c.d(b,"AbstractMesh",function(){return d.AbstractMesh}),c.d(b,"DracoCompression",function(){return d.DracoCompression}),c.d(b,"MeshoptCompression",function(){return d.MeshoptCompression}),c.d(b,"CSG",function(){return d.CSG}),c.d(b,"Geometry",function(){return d.Geometry}),c.d(b,"GroundMesh",function(){return d.GroundMesh}),c.d(b,"TrailMesh",function(){return d.TrailMesh}),c.d(b,"InstancedMesh",function(){return d.InstancedMesh}),c.d(b,"LinesMesh",function(){return d.LinesMesh}),c.d(b,"InstancedLinesMesh",function(){return d.InstancedLinesMesh}),c.d(b,"_CreationDataStorage",function(){return d._CreationDataStorage}),c.d(b,"_InstancesBatch",function(){return d._InstancesBatch}),c.d(b,"Mesh",function(){return d.Mesh}),c.d(b,"_injectLTSMesh",function(){return d._injectLTSMesh}),c.d(b,"VertexData",function(){return d.VertexData}),c.d(b,"MeshBuilder",function(){return d.MeshBuilder}),c.d(b,"SimplificationSettings",function(){return d.SimplificationSettings}),c.d(b,"SimplificationQueue",function(){return d.SimplificationQueue}),c.d(b,"SimplificationType",function(){return d.SimplificationType}),c.d(b,"QuadraticErrorSimplification",function(){return d.QuadraticErrorSimplification}),c.d(b,"SimplicationQueueSceneComponent",function(){return d.SimplicationQueueSceneComponent}),c.d(b,"Polygon",function(){return d.Polygon}),c.d(b,"PolygonMeshBuilder",function(){return d.PolygonMeshBuilder}),c.d(b,"_PrimaryIsoTriangle",function(){return d._PrimaryIsoTriangle}),c.d(b,"PolyhedronData",function(){return d.PolyhedronData}),c.d(b,"GeodesicData",function(){return d.GeodesicData}),c.d(b,"SubMesh",function(){return d.SubMesh}),c.d(b,"MeshLODLevel",function(){return d.MeshLODLevel}),c.d(b,"TransformNode",function(){return d.TransformNode}),c.d(b,"CreateBoxVertexData",function(){return d.CreateBoxVertexData}),c.d(b,"CreateBox",function(){return d.CreateBox}),c.d(b,"BoxBuilder",function(){return d.BoxBuilder}),c.d(b,"CreateTiledBoxVertexData",function(){return d.CreateTiledBoxVertexData}),c.d(b,"CreateTiledBox",function(){return d.CreateTiledBox}),c.d(b,"TiledBoxBuilder",function(){return d.TiledBoxBuilder}),c.d(b,"CreateDisc",function(){return d.CreateDisc}),c.d(b,"DiscBuilder",function(){return d.DiscBuilder}),c.d(b,"CreateRibbonVertexData",function(){return d.CreateRibbonVertexData}),c.d(b,"CreateRibbon",function(){return d.CreateRibbon}),c.d(b,"RibbonBuilder",function(){return d.RibbonBuilder}),c.d(b,"CreateSphereVertexData",function(){return d.CreateSphereVertexData}),c.d(b,"CreateSphere",function(){return d.CreateSphere}),c.d(b,"SphereBuilder",function(){return d.SphereBuilder}),c.d(b,"CreateHemisphere",function(){return d.CreateHemisphere}),c.d(b,"HemisphereBuilder",function(){return d.HemisphereBuilder}),c.d(b,"CreateCylinderVertexData",function(){return d.CreateCylinderVertexData}),c.d(b,"CreateCylinder",function(){return d.CreateCylinder}),c.d(b,"CylinderBuilder",function(){return d.CylinderBuilder}),c.d(b,"CreateTorusVertexData",function(){return d.CreateTorusVertexData}),c.d(b,"CreateTorus",function(){return d.CreateTorus}),c.d(b,"TorusBuilder",function(){return d.TorusBuilder}),c.d(b,"CreateTorusKnotVertexData",function(){return d.CreateTorusKnotVertexData}),c.d(b,"CreateTorusKnot",function(){return d.CreateTorusKnot}),c.d(b,"TorusKnotBuilder",function(){return d.TorusKnotBuilder}),c.d(b,"CreateLineSystemVertexData",function(){return d.CreateLineSystemVertexData}),c.d(b,"CreateDashedLinesVertexData",function(){return d.CreateDashedLinesVertexData}),c.d(b,"CreateLineSystem",function(){return d.CreateLineSystem}),c.d(b,"CreateLines",function(){return d.CreateLines}),c.d(b,"CreateDashedLines",function(){return d.CreateDashedLines}),c.d(b,"LinesBuilder",function(){return d.LinesBuilder}),c.d(b,"CreatePolygonVertexData",function(){return d.CreatePolygonVertexData}),c.d(b,"CreatePolygon",function(){return d.CreatePolygon}),c.d(b,"ExtrudePolygon",function(){return d.ExtrudePolygon}),c.d(b,"PolygonBuilder",function(){return d.PolygonBuilder}),c.d(b,"ExtrudeShape",function(){return d.ExtrudeShape}),c.d(b,"ExtrudeShapeCustom",function(){return d.ExtrudeShapeCustom}),c.d(b,"ShapeBuilder",function(){return d.ShapeBuilder}),c.d(b,"CreateLathe",function(){return d.CreateLathe}),c.d(b,"LatheBuilder",function(){return d.LatheBuilder}),c.d(b,"CreatePlaneVertexData",function(){return d.CreatePlaneVertexData}),c.d(b,"CreatePlane",function(){return d.CreatePlane}),c.d(b,"PlaneBuilder",function(){return d.PlaneBuilder}),c.d(b,"CreateTiledPlaneVertexData",function(){return d.CreateTiledPlaneVertexData}),c.d(b,"CreateTiledPlane",function(){return d.CreateTiledPlane}),c.d(b,"TiledPlaneBuilder",function(){return d.TiledPlaneBuilder}),c.d(b,"CreateGroundVertexData",function(){return d.CreateGroundVertexData}),c.d(b,"CreateTiledGroundVertexData",function(){return d.CreateTiledGroundVertexData}),c.d(b,"CreateGroundFromHeightMapVertexData",function(){return d.CreateGroundFromHeightMapVertexData}),c.d(b,"CreateGround",function(){return d.CreateGround}),c.d(b,"CreateTiledGround",function(){return d.CreateTiledGround}),c.d(b,"CreateGroundFromHeightMap",function(){return d.CreateGroundFromHeightMap}),c.d(b,"GroundBuilder",function(){return d.GroundBuilder}),c.d(b,"CreateTube",function(){return d.CreateTube}),c.d(b,"TubeBuilder",function(){return d.TubeBuilder}),c.d(b,"CreatePolyhedronVertexData",function(){return d.CreatePolyhedronVertexData}),c.d(b,"CreatePolyhedron",function(){return d.CreatePolyhedron}),c.d(b,"PolyhedronBuilder",function(){return d.PolyhedronBuilder}),c.d(b,"CreateIcoSphereVertexData",function(){return d.CreateIcoSphereVertexData}),c.d(b,"CreateIcoSphere",function(){return d.CreateIcoSphere}),c.d(b,"IcoSphereBuilder",function(){return d.IcoSphereBuilder}),c.d(b,"CreateGeodesic",function(){return d.CreateGeodesic}),c.d(b,"CreateDecal",function(){return d.CreateDecal}),c.d(b,"DecalBuilder",function(){return d.DecalBuilder}),c.d(b,"CreateCapsuleVertexData",function(){return d.CreateCapsuleVertexData}),c.d(b,"CreateCapsule",function(){return d.CreateCapsule}),c.d(b,"CapsuleBuilder",function(){return d.CapsuleBuilder}),c.d(b,"WebGLDataBuffer",function(){return d.WebGLDataBuffer}),c.d(b,"WebGPUDataBuffer",function(){return d.WebGPUDataBuffer}),c.d(b,"MorphTarget",function(){return d.MorphTarget}),c.d(b,"MorphTargetManager",function(){return d.MorphTargetManager}),c.d(b,"RecastJSPlugin",function(){return d.RecastJSPlugin}),c.d(b,"RecastJSCrowd",function(){return d.RecastJSCrowd}),c.d(b,"Node",function(){return d.Node}),c.d(b,"Database",function(){return d.Database}),c.d(b,"BaseParticleSystem",function(){return d.BaseParticleSystem}),c.d(b,"BoxParticleEmitter",function(){return d.BoxParticleEmitter}),c.d(b,"ConeParticleEmitter",function(){return d.ConeParticleEmitter}),c.d(b,"CylinderParticleEmitter",function(){return d.CylinderParticleEmitter}),c.d(b,"CylinderDirectedParticleEmitter",function(){return d.CylinderDirectedParticleEmitter}),c.d(b,"HemisphericParticleEmitter",function(){return d.HemisphericParticleEmitter}),c.d(b,"PointParticleEmitter",function(){return d.PointParticleEmitter}),c.d(b,"SphereParticleEmitter",function(){return d.SphereParticleEmitter}),c.d(b,"SphereDirectedParticleEmitter",function(){return d.SphereDirectedParticleEmitter}),c.d(b,"CustomParticleEmitter",function(){return d.CustomParticleEmitter}),c.d(b,"MeshParticleEmitter",function(){return d.MeshParticleEmitter}),c.d(b,"WebGL2ParticleSystem",function(){return d.WebGL2ParticleSystem}),c.d(b,"ComputeShaderParticleSystem",function(){return d.ComputeShaderParticleSystem}),c.d(b,"GPUParticleSystem",function(){return d.GPUParticleSystem}),c.d(b,"Particle",function(){return d.Particle}),c.d(b,"ParticleHelper",function(){return d.ParticleHelper}),c.d(b,"ParticleSystem",function(){return d.ParticleSystem}),c.d(b,"ParticleSystemSet",function(){return d.ParticleSystemSet}),c.d(b,"SolidParticle",function(){return d.SolidParticle}),c.d(b,"ModelShape",function(){return d.ModelShape}),c.d(b,"DepthSortedParticle",function(){return d.DepthSortedParticle}),c.d(b,"SolidParticleVertex",function(){return d.SolidParticleVertex}),c.d(b,"SolidParticleSystem",function(){return d.SolidParticleSystem}),c.d(b,"CloudPoint",function(){return d.CloudPoint}),c.d(b,"PointsGroup",function(){return d.PointsGroup}),c.d(b,"PointColor",function(){return d.PointColor}),c.d(b,"PointsCloudSystem",function(){return d.PointsCloudSystem}),c.d(b,"SubEmitterType",function(){return d.SubEmitterType}),c.d(b,"SubEmitter",function(){return d.SubEmitter}),c.d(b,"PhysicsEngine",function(){return d.PhysicsEngine}),c.d(b,"PhysicsEngineSceneComponent",function(){return d.PhysicsEngineSceneComponent}),c.d(b,"PhysicsHelper",function(){return d.PhysicsHelper}),c.d(b,"PhysicsRadialExplosionEventOptions",function(){return d.PhysicsRadialExplosionEventOptions}),c.d(b,"PhysicsUpdraftEventOptions",function(){return d.PhysicsUpdraftEventOptions}),c.d(b,"PhysicsVortexEventOptions",function(){return d.PhysicsVortexEventOptions}),c.d(b,"PhysicsRadialImpulseFalloff",function(){return d.PhysicsRadialImpulseFalloff}),c.d(b,"PhysicsUpdraftMode",function(){return d.PhysicsUpdraftMode}),c.d(b,"PhysicsImpostor",function(){return d.PhysicsImpostor}),c.d(b,"PhysicsJoint",function(){return d.PhysicsJoint}),c.d(b,"DistanceJoint",function(){return d.DistanceJoint}),c.d(b,"MotorEnabledJoint",function(){return d.MotorEnabledJoint}),c.d(b,"HingeJoint",function(){return d.HingeJoint}),c.d(b,"Hinge2Joint",function(){return d.Hinge2Joint}),c.d(b,"CannonJSPlugin",function(){return d.CannonJSPlugin}),c.d(b,"AmmoJSPlugin",function(){return d.AmmoJSPlugin}),c.d(b,"OimoJSPlugin",function(){return d.OimoJSPlugin}),c.d(b,"AnaglyphPostProcess",function(){return d.AnaglyphPostProcess}),c.d(b,"BlackAndWhitePostProcess",function(){return d.BlackAndWhitePostProcess}),c.d(b,"BloomEffect",function(){return d.BloomEffect}),c.d(b,"BloomMergePostProcess",function(){return d.BloomMergePostProcess}),c.d(b,"BlurPostProcess",function(){return d.BlurPostProcess}),c.d(b,"ChromaticAberrationPostProcess",function(){return d.ChromaticAberrationPostProcess}),c.d(b,"CircleOfConfusionPostProcess",function(){return d.CircleOfConfusionPostProcess}),c.d(b,"ColorCorrectionPostProcess",function(){return d.ColorCorrectionPostProcess}),c.d(b,"ConvolutionPostProcess",function(){return d.ConvolutionPostProcess}),c.d(b,"DepthOfFieldBlurPostProcess",function(){return d.DepthOfFieldBlurPostProcess}),c.d(b,"DepthOfFieldEffectBlurLevel",function(){return d.DepthOfFieldEffectBlurLevel}),c.d(b,"DepthOfFieldEffect",function(){return d.DepthOfFieldEffect}),c.d(b,"DepthOfFieldMergePostProcessOptions",function(){return d.DepthOfFieldMergePostProcessOptions}),c.d(b,"DepthOfFieldMergePostProcess",function(){return d.DepthOfFieldMergePostProcess}),c.d(b,"DisplayPassPostProcess",function(){return d.DisplayPassPostProcess}),c.d(b,"ExtractHighlightsPostProcess",function(){return d.ExtractHighlightsPostProcess}),c.d(b,"FilterPostProcess",function(){return d.FilterPostProcess}),c.d(b,"FxaaPostProcess",function(){return d.FxaaPostProcess}),c.d(b,"GrainPostProcess",function(){return d.GrainPostProcess}),c.d(b,"HighlightsPostProcess",function(){return d.HighlightsPostProcess}),c.d(b,"ImageProcessingPostProcess",function(){return d.ImageProcessingPostProcess}),c.d(b,"MotionBlurPostProcess",function(){return d.MotionBlurPostProcess}),c.d(b,"PassPostProcess",function(){return d.PassPostProcess}),c.d(b,"PassCubePostProcess",function(){return d.PassCubePostProcess}),c.d(b,"PostProcess",function(){return d.PostProcess}),c.d(b,"PostProcessManager",function(){return d.PostProcessManager}),c.d(b,"RefractionPostProcess",function(){return d.RefractionPostProcess}),c.d(b,"DefaultRenderingPipeline",function(){return d.DefaultRenderingPipeline}),c.d(b,"LensRenderingPipeline",function(){return d.LensRenderingPipeline}),c.d(b,"SSAO2RenderingPipeline",function(){return d.SSAO2RenderingPipeline}),c.d(b,"SSAORenderingPipeline",function(){return d.SSAORenderingPipeline}),c.d(b,"StandardRenderingPipeline",function(){return d.StandardRenderingPipeline}),c.d(b,"PostProcessRenderEffect",function(){return d.PostProcessRenderEffect}),c.d(b,"PostProcessRenderPipeline",function(){return d.PostProcessRenderPipeline}),c.d(b,"PostProcessRenderPipelineManager",function(){return d.PostProcessRenderPipelineManager}),c.d(b,"PostProcessRenderPipelineManagerSceneComponent",function(){return d.PostProcessRenderPipelineManagerSceneComponent}),c.d(b,"SharpenPostProcess",function(){return d.SharpenPostProcess}),c.d(b,"StereoscopicInterlacePostProcessI",function(){return d.StereoscopicInterlacePostProcessI}),c.d(b,"StereoscopicInterlacePostProcess",function(){return d.StereoscopicInterlacePostProcess}),c.d(b,"TonemappingOperator",function(){return d.TonemappingOperator}),c.d(b,"TonemapPostProcess",function(){return d.TonemapPostProcess}),c.d(b,"VolumetricLightScatteringPostProcess",function(){return d.VolumetricLightScatteringPostProcess}),c.d(b,"VRDistortionCorrectionPostProcess",function(){return d.VRDistortionCorrectionPostProcess}),c.d(b,"VRMultiviewToSingleviewPostProcess",function(){return d.VRMultiviewToSingleviewPostProcess}),c.d(b,"ScreenSpaceReflectionPostProcess",function(){return d.ScreenSpaceReflectionPostProcess}),c.d(b,"ScreenSpaceCurvaturePostProcess",function(){return d.ScreenSpaceCurvaturePostProcess}),c.d(b,"ReflectionProbe",function(){return d.ReflectionProbe}),c.d(b,"BoundingBoxRenderer",function(){return d.BoundingBoxRenderer}),c.d(b,"DepthRenderer",function(){return d.DepthRenderer}),c.d(b,"DepthRendererSceneComponent",function(){return d.DepthRendererSceneComponent}),c.d(b,"DepthPeelingRenderer",function(){return d.DepthPeelingRenderer}),c.d(b,"DepthPeelingSceneComponent",function(){return d.DepthPeelingSceneComponent}),c.d(b,"EdgesRenderer",function(){return d.EdgesRenderer}),c.d(b,"LineEdgesRenderer",function(){return d.LineEdgesRenderer}),c.d(b,"GeometryBufferRenderer",function(){return d.GeometryBufferRenderer}),c.d(b,"GeometryBufferRendererSceneComponent",function(){return d.GeometryBufferRendererSceneComponent}),c.d(b,"PrePassRenderer",function(){return d.PrePassRenderer}),c.d(b,"PrePassRendererSceneComponent",function(){return d.PrePassRendererSceneComponent}),c.d(b,"SubSurfaceSceneComponent",function(){return d.SubSurfaceSceneComponent}),c.d(b,"OutlineRenderer",function(){return d.OutlineRenderer}),c.d(b,"RenderingGroup",function(){return d.RenderingGroup}),c.d(b,"RenderingGroupInfo",function(){return d.RenderingGroupInfo}),c.d(b,"RenderingManager",function(){return d.RenderingManager}),c.d(b,"UtilityLayerRenderer",function(){return d.UtilityLayerRenderer}),c.d(b,"Scene",function(){return d.Scene}),c.d(b,"_injectLTSScene",function(){return d._injectLTSScene}),c.d(b,"SceneComponentConstants",function(){return d.SceneComponentConstants}),c.d(b,"Stage",function(){return d.Stage}),c.d(b,"Sprite",function(){return d.Sprite}),c.d(b,"SpriteManager",function(){return d.SpriteManager}),c.d(b,"SpriteMap",function(){return d.SpriteMap}),c.d(b,"SpritePackedManager",function(){return d.SpritePackedManager}),c.d(b,"SpriteSceneComponent",function(){return d.SpriteSceneComponent}),c.d(b,"AlphaState",function(){return d.AlphaState}),c.d(b,"DepthCullingState",function(){return d.DepthCullingState}),c.d(b,"StencilState",function(){return d.StencilState}),c.d(b,"StencilStateComposer",function(){return d.StencilStateComposer}),c.d(b,"AndOrNotEvaluator",function(){return d.AndOrNotEvaluator}),c.d(b,"AssetTaskState",function(){return d.AssetTaskState}),c.d(b,"AbstractAssetTask",function(){return d.AbstractAssetTask}),c.d(b,"AssetsProgressEvent",function(){return d.AssetsProgressEvent}),c.d(b,"ContainerAssetTask",function(){return d.ContainerAssetTask}),c.d(b,"MeshAssetTask",function(){return d.MeshAssetTask}),c.d(b,"TextFileAssetTask",function(){return d.TextFileAssetTask}),c.d(b,"BinaryFileAssetTask",function(){return d.BinaryFileAssetTask}),c.d(b,"ImageAssetTask",function(){return d.ImageAssetTask}),c.d(b,"TextureAssetTask",function(){return d.TextureAssetTask}),c.d(b,"CubeTextureAssetTask",function(){return d.CubeTextureAssetTask}),c.d(b,"HDRCubeTextureAssetTask",function(){return d.HDRCubeTextureAssetTask}),c.d(b,"EquiRectangularCubeTextureAssetTask",function(){return d.EquiRectangularCubeTextureAssetTask}),c.d(b,"AssetsManager",function(){return d.AssetsManager}),c.d(b,"BasisTranscodeConfiguration",function(){return d.BasisTranscodeConfiguration}),c.d(b,"BasisToolsOptions",function(){return d.BasisToolsOptions}),c.d(b,"GetInternalFormatFromBasisFormat",function(){return d.GetInternalFormatFromBasisFormat}),c.d(b,"TranscodeAsync",function(){return d.TranscodeAsync}),c.d(b,"LoadTextureFromTranscodeResult",function(){return d.LoadTextureFromTranscodeResult}),c.d(b,"BasisTools",function(){return d.BasisTools}),c.d(b,"DDSTools",function(){return d.DDSTools}),c.d(b,"expandToProperty",function(){return d.expandToProperty}),c.d(b,"serialize",function(){return d.serialize}),c.d(b,"serializeAsTexture",function(){return d.serializeAsTexture}),c.d(b,"serializeAsColor3",function(){return d.serializeAsColor3}),c.d(b,"serializeAsFresnelParameters",function(){return d.serializeAsFresnelParameters}),c.d(b,"serializeAsVector2",function(){return d.serializeAsVector2}),c.d(b,"serializeAsVector3",function(){return d.serializeAsVector3}),c.d(b,"serializeAsMeshReference",function(){return d.serializeAsMeshReference}),c.d(b,"serializeAsColorCurves",function(){return d.serializeAsColorCurves}),c.d(b,"serializeAsColor4",function(){return d.serializeAsColor4}),c.d(b,"serializeAsImageProcessingConfiguration",function(){return d.serializeAsImageProcessingConfiguration}),c.d(b,"serializeAsQuaternion",function(){return d.serializeAsQuaternion}),c.d(b,"serializeAsMatrix",function(){return d.serializeAsMatrix}),c.d(b,"serializeAsCameraReference",function(){return d.serializeAsCameraReference}),c.d(b,"SerializationHelper",function(){return d.SerializationHelper}),c.d(b,"nativeOverride",function(){return d.nativeOverride}),c.d(b,"Deferred",function(){return d.Deferred}),c.d(b,"GetEnvInfo",function(){return d.GetEnvInfo}),c.d(b,"normalizeEnvInfo",function(){return d.normalizeEnvInfo}),c.d(b,"CreateEnvTextureAsync",function(){return d.CreateEnvTextureAsync}),c.d(b,"CreateImageDataArrayBufferViews",function(){return d.CreateImageDataArrayBufferViews}),c.d(b,"UploadEnvLevelsAsync",function(){return d.UploadEnvLevelsAsync}),c.d(b,"UploadLevelsAsync",function(){return d.UploadLevelsAsync}),c.d(b,"UploadEnvSpherical",function(){return d.UploadEnvSpherical}),c.d(b,"_UpdateRGBDAsync",function(){return d._UpdateRGBDAsync}),c.d(b,"EnvironmentTextureTools",function(){return d.EnvironmentTextureTools}),c.d(b,"MeshExploder",function(){return d.MeshExploder}),c.d(b,"FilesInput",function(){return d.FilesInput}),c.d(b,"CubeMapToSphericalPolynomialTools",function(){return d.CubeMapToSphericalPolynomialTools}),c.d(b,"HDRTools",function(){return d.HDRTools}),c.d(b,"PanoramaToCubeMapTools",function(){return d.PanoramaToCubeMapTools}),c.d(b,"KhronosTextureContainer",function(){return d.KhronosTextureContainer}),c.d(b,"EventState",function(){return d.EventState}),c.d(b,"Observer",function(){return d.Observer}),c.d(b,"MultiObserver",function(){return d.MultiObserver}),c.d(b,"Observable",function(){return d.Observable}),c.d(b,"PerformanceMonitor",function(){return d.PerformanceMonitor}),c.d(b,"RollingAverage",function(){return d.RollingAverage}),c.d(b,"PromisePolyfill",function(){return d.PromisePolyfill}),c.d(b,"SceneOptimization",function(){return d.SceneOptimization}),c.d(b,"TextureOptimization",function(){return d.TextureOptimization}),c.d(b,"HardwareScalingOptimization",function(){return d.HardwareScalingOptimization}),c.d(b,"ShadowsOptimization",function(){return d.ShadowsOptimization}),c.d(b,"PostProcessesOptimization",function(){return d.PostProcessesOptimization}),c.d(b,"LensFlaresOptimization",function(){return d.LensFlaresOptimization}),c.d(b,"CustomOptimization",function(){return d.CustomOptimization}),c.d(b,"ParticlesOptimization",function(){return d.ParticlesOptimization}),c.d(b,"RenderTargetsOptimization",function(){return d.RenderTargetsOptimization}),c.d(b,"MergeMeshesOptimization",function(){return d.MergeMeshesOptimization}),c.d(b,"SceneOptimizerOptions",function(){return d.SceneOptimizerOptions});c.d(b,"SceneOptimizer",function(){return d.SceneOptimizer}),c.d(b,"SceneSerializer",function(){return d.SceneSerializer}),c.d(b,"SmartArray",function(){return d.SmartArray}),c.d(b,"SmartArrayNoDuplicate",function(){return d.SmartArrayNoDuplicate}),c.d(b,"StringDictionary",function(){return d.StringDictionary}),c.d(b,"Tags",function(){return d.Tags}),c.d(b,"CreateResizedCopy",function(){return d.CreateResizedCopy}),c.d(b,"ApplyPostProcess",function(){return d.ApplyPostProcess}),c.d(b,"ToHalfFloat",function(){return d.ToHalfFloat}),c.d(b,"FromHalfFloat",function(){return d.FromHalfFloat}),c.d(b,"TextureTools",function(){return d.TextureTools}),c.d(b,"GetTGAHeader",function(){return d.GetTGAHeader}),c.d(b,"UploadContent",function(){return d.UploadContent}),c.d(b,"TGATools",function(){return d.TGATools}),c.d(b,"Tools",function(){return d.Tools}),c.d(b,"className",function(){return d.className}),c.d(b,"AsyncLoop",function(){return d.AsyncLoop}),c.d(b,"VideoRecorder",function(){return d.VideoRecorder}),c.d(b,"JoystickAxis",function(){return d.JoystickAxis}),c.d(b,"VirtualJoystick",function(){return d.VirtualJoystick}),c.d(b,"WorkerPool",function(){return d.WorkerPool}),c.d(b,"Logger",function(){return d.Logger}),c.d(b,"RegisterClass",function(){return d.RegisterClass}),c.d(b,"GetClass",function(){return d.GetClass}),c.d(b,"FilesInputStore",function(){return d.FilesInputStore}),c.d(b,"DeepCopier",function(){return d.DeepCopier}),c.d(b,"PivotTools",function(){return d.PivotTools}),c.d(b,"PrecisionDate",function(){return d.PrecisionDate}),c.d(b,"CreateScreenshot",function(){return d.CreateScreenshot}),c.d(b,"CreateScreenshotAsync",function(){return d.CreateScreenshotAsync}),c.d(b,"CreateScreenshotWithResizeAsync",function(){return d.CreateScreenshotWithResizeAsync}),c.d(b,"CreateScreenshotUsingRenderTarget",function(){return d.CreateScreenshotUsingRenderTarget}),c.d(b,"CreateScreenshotUsingRenderTargetAsync",function(){return d.CreateScreenshotUsingRenderTargetAsync}),c.d(b,"ScreenshotTools",function(){return d.ScreenshotTools}),c.d(b,"WebRequest",function(){return d.WebRequest}),c.d(b,"InspectableType",function(){return d.InspectableType}),c.d(b,"GetEnvironmentBRDFTexture",function(){return d.GetEnvironmentBRDFTexture}),c.d(b,"BRDFTextureTools",function(){return d.BRDFTextureTools}),c.d(b,"RGBDTextureTools",function(){return d.RGBDTextureTools}),c.d(b,"ColorGradient",function(){return d.ColorGradient}),c.d(b,"Color3Gradient",function(){return d.Color3Gradient}),c.d(b,"FactorGradient",function(){return d.FactorGradient}),c.d(b,"GradientHelper",function(){return d.GradientHelper}),c.d(b,"PerfCounter",function(){return d.PerfCounter}),c.d(b,"RetryStrategy",function(){return d.RetryStrategy}),c.d(b,"LoadFileError",function(){return d.LoadFileError}),c.d(b,"RequestFileError",function(){return d.RequestFileError}),c.d(b,"ReadFileError",function(){return d.ReadFileError}),c.d(b,"FileToolsOptions",function(){return d.FileToolsOptions}),c.d(b,"SetCorsBehavior",function(){return d.SetCorsBehavior}),c.d(b,"LoadImage",function(){return d.LoadImage}),c.d(b,"ReadFile",function(){return d.ReadFile}),c.d(b,"LoadFile",function(){return d.LoadFile}),c.d(b,"RequestFile",function(){return d.RequestFile}),c.d(b,"IsFileURL",function(){return d.IsFileURL}),c.d(b,"IsBase64DataUrl",function(){return d.IsBase64DataUrl}),c.d(b,"DecodeBase64UrlToBinary",function(){return d.DecodeBase64UrlToBinary}),c.d(b,"DecodeBase64UrlToString",function(){return d.DecodeBase64UrlToString}),c.d(b,"FileTools",function(){return d.FileTools}),c.d(b,"_injectLTSFileTools",function(){return d._injectLTSFileTools}),c.d(b,"EndsWith",function(){return d.EndsWith}),c.d(b,"StartsWith",function(){return d.StartsWith}),c.d(b,"Decode",function(){return d.Decode}),c.d(b,"EncodeArrayBufferToBase64",function(){return d.EncodeArrayBufferToBase64}),c.d(b,"DecodeBase64ToString",function(){return d.DecodeBase64ToString}),c.d(b,"DecodeBase64ToBinary",function(){return d.DecodeBase64ToBinary}),c.d(b,"PadNumber",function(){return d.PadNumber}),c.d(b,"StringTools",function(){return d.StringTools}),c.d(b,"DataReader",function(){return d.DataReader}),c.d(b,"MinMaxReducer",function(){return d.MinMaxReducer}),c.d(b,"DepthReducer",function(){return d.DepthReducer}),c.d(b,"DataStorage",function(){return d.DataStorage}),c.d(b,"SceneRecorder",function(){return d.SceneRecorder}),c.d(b,"KhronosTextureContainer2",function(){return d.KhronosTextureContainer2}),c.d(b,"Trajectory",function(){return d.Trajectory}),c.d(b,"TrajectoryClassifier",function(){return d.TrajectoryClassifier}),c.d(b,"TimerState",function(){return d.TimerState}),c.d(b,"setAndStartTimer",function(){return d.setAndStartTimer}),c.d(b,"AdvancedTimer",function(){return d.AdvancedTimer}),c.d(b,"GenerateBase64StringFromPixelData",function(){return d.GenerateBase64StringFromPixelData}),c.d(b,"GenerateBase64StringFromTexture",function(){return d.GenerateBase64StringFromTexture}),c.d(b,"GenerateBase64StringFromTextureAsync",function(){return d.GenerateBase64StringFromTextureAsync}),c.d(b,"CopyTools",function(){return d.CopyTools}),c.d(b,"Reflector",function(){return d.Reflector}),c.d(b,"IsWindowObjectExist",function(){return d.IsWindowObjectExist}),c.d(b,"IsNavigatorAvailable",function(){return d.IsNavigatorAvailable}),c.d(b,"IsDocumentAvailable",function(){return d.IsDocumentAvailable}),c.d(b,"GetDOMTextContent",function(){return d.GetDOMTextContent}),c.d(b,"DomManagement",function(){return d.DomManagement}),c.d(b,"ComputePressureObserverWrapper",function(){return d.ComputePressureObserverWrapper}),c.d(b,"PerformanceViewerCollector",function(){return d.PerformanceViewerCollector}),c.d(b,"PerfCollectionStrategy",function(){return d.PerfCollectionStrategy}),c.d(b,"DynamicFloat32Array",function(){return d.DynamicFloat32Array}),c.d(b,"WebXRCamera",function(){return d.WebXRCamera}),c.d(b,"WebXREnterExitUIButton",function(){return d.WebXREnterExitUIButton}),c.d(b,"WebXREnterExitUIOptions",function(){return d.WebXREnterExitUIOptions}),c.d(b,"WebXREnterExitUI",function(){return d.WebXREnterExitUI}),c.d(b,"WebXRExperienceHelper",function(){return d.WebXRExperienceHelper}),c.d(b,"WebXRInput",function(){return d.WebXRInput}),c.d(b,"WebXRInputSource",function(){return d.WebXRInputSource}),c.d(b,"WebXRManagedOutputCanvasOptions",function(){return d.WebXRManagedOutputCanvasOptions}),c.d(b,"WebXRManagedOutputCanvas",function(){return d.WebXRManagedOutputCanvas}),c.d(b,"WebXRState",function(){return d.WebXRState}),c.d(b,"WebXRTrackingState",function(){return d.WebXRTrackingState}),c.d(b,"WebXRSessionManager",function(){return d.WebXRSessionManager}),c.d(b,"WebXRDefaultExperienceOptions",function(){return d.WebXRDefaultExperienceOptions}),c.d(b,"WebXRDefaultExperience",function(){return d.WebXRDefaultExperience}),c.d(b,"WebXRFeatureName",function(){return d.WebXRFeatureName}),c.d(b,"WebXRFeaturesManager",function(){return d.WebXRFeaturesManager}),c.d(b,"WebXRAbstractFeature",function(){return d.WebXRAbstractFeature}),c.d(b,"WebXRHitTestLegacy",function(){return d.WebXRHitTestLegacy}),c.d(b,"WebXRAnchorSystem",function(){return d.WebXRAnchorSystem}),c.d(b,"WebXRPlaneDetector",function(){return d.WebXRPlaneDetector}),c.d(b,"WebXRBackgroundRemover",function(){return d.WebXRBackgroundRemover}),c.d(b,"WebXRMotionControllerTeleportation",function(){return d.WebXRMotionControllerTeleportation}),c.d(b,"WebXRControllerPointerSelection",function(){return d.WebXRControllerPointerSelection}),c.d(b,"IWebXRControllerPhysicsOptions",function(){return d.IWebXRControllerPhysicsOptions}),c.d(b,"WebXRControllerPhysics",function(){return d.WebXRControllerPhysics}),c.d(b,"WebXRHitTest",function(){return d.WebXRHitTest}),c.d(b,"WebXRFeaturePointSystem",function(){return d.WebXRFeaturePointSystem}),c.d(b,"WebXRHand",function(){return d.WebXRHand}),c.d(b,"WebXRHandTracking",function(){return d.WebXRHandTracking}),c.d(b,"WebXRMeshDetector",function(){return d.WebXRMeshDetector}),c.d(b,"WebXRImageTracking",function(){return d.WebXRImageTracking}),c.d(b,"WebXRNearInteraction",function(){return d.WebXRNearInteraction}),c.d(b,"WebXRDomOverlay",function(){return d.WebXRDomOverlay}),c.d(b,"WebXRControllerMovement",function(){return d.WebXRControllerMovement}),c.d(b,"WebXRLightEstimation",function(){return d.WebXRLightEstimation}),c.d(b,"WebXREyeTracking",function(){return d.WebXREyeTracking}),c.d(b,"WebXRWalkingLocomotion",function(){return d.WebXRWalkingLocomotion}),c.d(b,"WebXRAbstractMotionController",function(){return d.WebXRAbstractMotionController}),c.d(b,"WebXRControllerComponent",function(){return d.WebXRControllerComponent}),c.d(b,"WebXRGenericHandController",function(){return d.WebXRGenericHandController}),c.d(b,"WebXRGenericTriggerMotionController",function(){return d.WebXRGenericTriggerMotionController}),c.d(b,"WebXRMicrosoftMixedRealityController",function(){return d.WebXRMicrosoftMixedRealityController}),c.d(b,"WebXRMotionControllerManager",function(){return d.WebXRMotionControllerManager}),c.d(b,"WebXROculusTouchMotionController",function(){return d.WebXROculusTouchMotionController}),c.d(b,"WebXRHTCViveMotionController",function(){return d.WebXRHTCViveMotionController}),c.d(b,"WebXRProfiledMotionController",function(){return d.WebXRProfiledMotionController});a=void 0!==a?a:"undefined"!=typeof window?window:void 0;if(void 0!==a){a.BABYLON=f,a.BABYLON=a.BABYLON||{};var f=a.BABYLON;f.Debug=f.Debug||{};a=[];for(var g in e)f.Debug[g]=e[g],a.push(g);for(var g in d)f[g]=d[g]}var h={AxesViewer:e.AxesViewer,BoneAxesViewer:e.BoneAxesViewer,PhysicsViewer:e.PhysicsViewer,SkeletonViewer:e.SkeletonViewer}}).call(this,c(189))}])})}var k=!1;function l(){k||(k=!0,j());return h.exports}function b(a){switch(a){case void 0:return l()}}e.exports=b}),null);
-----
KFLoader",["BlobFactory","CubicBezier","KFColor","KFComponentNames","KFGradient","KFLoaderUtils","KFManifest","KFPath2d","KFPoint","KFPosition","KFScalar","KFSchema","Promise","flatbuffers"],(function(a,b,c,d,e,f,g){"use strict";var h=a.URL||a.webkitURL||{},i=new Map(),j;function e(a,b,e){j=!1;i.clear();a=new(d("flatbuffers").ByteBuffer)(a);a=c("KFSchema").Document.bufferHasIdentifier(a)?c("KFSchema").Document.getRootAsDocument(a):null;l(a);P(a,e);m(a,null,!0,b,e);k(b)}function k(a){j?c("KFLoaderUtils").maybeGenerateTrackMatteIDs(a):c("KFLoaderUtils").traverseEntitiesAndApply(a,function(b){b=a.getComponent(b,d("KFComponentNames").COMPOSITING);var c=b==null?void 0:b.trackMatteId;if(b==null||c==null||c===0)return;var e=i.get(c);b.trackMatteId=(b=e==null?void 0:e.getID())!=null?b:c})}function l(a,b){var d=[],e=a.manifestLength();for(var f=0;f0?e[e.length-1]:null;switch(b[k]){case c("KFSchema").CommandType.MoveTo:case c("KFSchema").CommandType.LineTo:g[0]=a[d++];g[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]]});break;case c("KFSchema").CommandType.QuadTo:g[0]=a[d++];g[1]=a[d++];h[0]=a[d++];h[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CubicTo:g[0]=a[d++];g[1]=a[d++];h[0]=a[d++];h[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:b[k],point:[g[0],g[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.HorizontalLineTo:g[0]=a[d++];e.push({type:c("KFSchema").CommandType.LineTo,point:[g[0],g[1]]});break;case c("KFSchema").CommandType.VerticalLineTo:g[1]=a[d++];e.push({type:c("KFSchema").CommandType.LineTo,point:[g[0],g[1]]});break;case c("KFSchema").CommandType.CloseLineTo:e.push({type:c("KFSchema").CommandType.LineTo,point:[f[0],f[1]]});break;case c("KFSchema").CommandType.SmoothQuadTo:H(h,j);g[0]=a[d++];g[1]=a[d++];e.push({type:c("KFSchema").CommandType.QuadTo,point:[g[0],g[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CloseQuadTo:h[0]=a[d++];h[1]=a[d++];e.push({type:c("KFSchema").CommandType.QuadTo,point:[f[0],f[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.CloseSmoothQuadTo:H(h,j);e.push({type:c("KFSchema").CommandType.QuadTo,point:[f[0],f[1]],c1:[h[0],h[1]]});break;case c("KFSchema").CommandType.SmoothCubicTo:H(h,j);g[0]=a[d++];g[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[g[0],g[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.CloseCubicTo:h[0]=a[d++];h[1]=a[d++];i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[f[0],f[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break;case c("KFSchema").CommandType.CloseSmoothCubicTo:H(h,j);i[0]=a[d++];i[1]=a[d++];e.push({type:c("KFSchema").CommandType.CubicTo,point:[f[0],f[1]],c1:[h[0],h[1]],c2:[i[0],i[1]]});break}f||(f=[g[0],g[1]])}return e}var G=[0,0];function H(a,b){var d;if(b==null)return;var e=b.point,f;switch(b.type){case c("KFSchema").CommandType.QuadTo:f=(d=b.c1)!=null?d:G;break;case c("KFSchema").CommandType.CubicTo:f=(d=b.c2)!=null?d:G;break;default:f=G;break}a[0]=f[0]+2*(e[0]-f[0]);a[1]=f[1]+2*(e[1]-f[1])}function I(a){return v(a,F)}function J(a){return a?{width:a.width(),height:a.height()}:null}function K(a){return v(a,L)}function L(a){return a?[a.red(),a.green(),a.blue(),a.alpha()]:null}function M(a){if(!a)return null;var b=a.colorsLength(),c=[];for(var d=0;d-----
WebBloksPrimitives",["WebBloksAccessibilityAnnouncement","WebBloksAccessibilityExtensionHandler","WebBloksAddChild","WebBloksAnchorIDExtensionHandler","WebBloksAnimatedCreate","WebBloksAnimatedCreateColor","WebBloksAnimatedCreateCubicBezier","WebBloksAnimatedCreateDimension","WebBloksAnimatedGetCurrentColorValue","WebBloksAnimatedGetCurrentDimensionValue","WebBloksAnimatedGetCurrentValue","WebBloksAnimatedParallel","WebBloksAnimatedStart","WebBloksApply","WebBloksArrayAppend","WebBloksArrayClone","WebBloksArrayConcat","WebBloksArrayFilter","WebBloksArrayGet","WebBloksArrayIndexOf","WebBloksArrayLength","WebBloksArrayMake","WebBloksArrayMap","WebBloksArrayPut","WebBloksArrayPutAndGet","WebBloksArrayRemove","WebBloksArrayRemoveAndGet","WebBloksArraySlice","WebBloksArraySortedArray","WebBloksArrayUpdate","WebBloksAsyncAction","WebBloksAsyncActionWithDataManifest","WebBloksAsyncLoadV2","WebBloksAutomationTestExtensionHandler","WebBloksBindWithArrayV2","WebBloksBoolAnd","WebBloksBoolConst","WebBloksBoolNot","WebBloksBoolOr","WebBloksBottomSheet","WebBloksChildAtIndex","WebBloksClipboardSetString","WebBloksCollection","WebBloksConstNumber","WebBloksCurrentTimeMillis","WebBloksDangerouslyGetTreeFromParseResult","WebBloksDatetimeTextProvider","WebBloksDelay","WebBloksDeviceLog","WebBloksDialog","WebBloksDialogButton","WebBloksDismissBottomSheet","WebBloksDismissToast","WebBloksDummy","WebBloksDummyComponent","WebBloksFindComponentContext","WebBloksFlexbox","WebBloksGestureExtensionHandler","WebBloksGetAttr","WebBloksGetDeserializedEmbeddedPayload","WebBloksGetState","WebBloksGetTemplateArg","WebBloksGetVariable2","WebBloksGetVariableWithScope","WebBloksGlimmer","WebBloksImage","WebBloksIndexOfChild","WebBloksInflateSync","WebBloksInsertChildrenAfter","WebBloksJsonDecode","WebBloksJsonEncode","WebBloksJsonEncodeV3","WebBloksLogEvent","WebBloksMakeFlat","WebBloksMapClone","WebBloksMapFilter","WebBloksMapGet","WebBloksMapKeys","WebBloksMapMake","WebBloksMapRemove","WebBloksMapUpdate","WebBloksMapValues","WebBloksMatch","WebBloksMatchesRegex","WebBloksNavbar","WebBloksNavigationCloseScreen","WebBloksNumberAnd","WebBloksNumberEq","WebBloksNumberGt","WebBloksNumberLt","WebBloksNumberMod","WebBloksNumberOr","WebBloksNumberRand","WebBloksOnMount","WebBloksOnMountExtensionHandler","WebBloksOpenBottomSheet","WebBloksOpenBottomSheetV2","WebBloksOpenDialog","WebBloksOpenFRXAction","WebBloksOpenFullScreenV4","WebBloksOpenScreen","WebBloksOpenScreenByRoute","WebBloksOpenSendMessage","WebBloksOpenUrl","WebBloksOpenUrlV2","WebBloksParseEmbedded","WebBloksParseEmbeddedV2","WebBloksPopBottomSheet","WebBloksPrependChildren","WebBloksPushBottomSheetV2","WebBloksReadPandoField","WebBloksReduce","WebBloksReflow","WebBloksRefresh","WebBloksRemoveChild","WebBloksRemoveChildrenBetween","WebBloksRenderLifecycleExtensionHandler","WebBloksReplaceChild","WebBloksReplaceChildren","WebBloksReplaceEmbeddedChild","WebBloksReplaceEmbeddedChildren","WebBloksRequestFocus","WebBloksRichText","WebBloksRotate3D","WebBloksScale3D","WebBloksScreenWrapperPrimitive","WebBloksSessionStoreGet","WebBloksSetAttr","WebBloksShowToast","WebBloksSpinner","WebBloksStringAsInteger","WebBloksStringAsLong","WebBloksStringConcat","WebBloksStringContains","WebBloksStringGetURLLastPathComponent","WebBloksStringJoin","WebBloksStringLength","WebBloksStringReplace","WebBloksStringSlice","WebBloksStringSplitWithString","WebBloksStringStartsWith","WebBloksStringToLowerCase","WebBloksStringToUpperCase","WebBloksStringValueOfNumber","WebBloksSwitch","WebBloksT3DFromArray","WebBloksTakeLast","WebBloksText","WebBloksTextGetText","WebBloksTextInput","WebBloksTextInputGetText","WebBloksTextInputUnfocus","WebBloksTextIsTruncated","WebBloksTextSpan","WebBloksToastPrimitive","WebBloksTouchExtensionHandler","WebBloksTranslate3D","WebBloksViewTransformExtensionHandler","WebBloksVisibilityContextGetTimeSinceLastImpressionInMS","WebBloksVisibilityContextHasSeenBefore","WebBloksVisibilityExtensionHandler","WebBloksWriteGlobalConsistencyStore","WebBloksWriteLocalState","WebBloksf32Convert","WebBloksf32NumberAdd","WebBloksf32NumberDiv","WebBloksf32NumberMul","WebBloksf32NumberPow","WebBloksf32NumberSub","WebBloksi32Convert","WebBloksi32NumberAdd","WebBloksi32NumberDiv","WebBloksi32NumberMul","WebBloksi32NumberSub","WebBloksi64ConstNumber","WebBloksi64NumberAdd","WebBloksi64NumberDiv","WebBloksi64NumberEq","WebBloksi64NumberGt","WebBloksi64NumberLt","WebBloksi64NumberMod","WebBloksi64NumberSub"],(function(a,b,c,d,e,f,g){a={"bk.components.internal.Action":function(){return null},"bk.components.internal.Merge":c("WebBloksDummyComponent"),"bk.components.internal.Shadow":c("WebBloksDummyComponent"),"bk.data.screen.Navbar":c("WebBloksNavbar"),"bk.components.BottomSheet":c("WebBloksBottomSheet"),"bk.components.dialog.Dialog":c("WebBloksDialog"),"bk.components.dialog.Button":c("WebBloksDialogButton"),"bk.components.Collection":c("WebBloksCollection"),"bk.components.Flexbox":c("WebBloksFlexbox"),"bk.components.Glimmer":c("WebBloksGlimmer"),"bk.components.Image":c("WebBloksImage"),"bk.components.RichText":c("WebBloksRichText"),"bk.components.Spinner":c("WebBloksSpinner"),"bk.components.Switch":c("WebBloksSwitch"),"bk.components.Text":c("WebBloksText"),"bk.components.TextInput":c("WebBloksTextInput"),"bk.components.TextSpan":c("WebBloksTextSpan"),"bk.components.screen.Wrapper":d("WebBloksScreenWrapperPrimitive").BloksScreenWrapperPrimitive,"bk.components.OnMount":c("WebBloksOnMount"),"bk.components.DatetimeTextProvider":c("WebBloksDatetimeTextProvider"),"bk.components.Video":c("WebBloksDummyComponent"),"bk.components.VideoVersion":c("WebBloksDummyComponent")};b={"bk.action.accessibility.Announcement":c("WebBloksAccessibilityAnnouncement"),"bk.action.animated.Create":c("WebBloksAnimatedCreate"),"bk.action.animated.CreateColor":c("WebBloksAnimatedCreateColor"),"bk.action.animated.CreateDimension":c("WebBloksAnimatedCreateDimension"),"bk.action.animated.easing.CreateCubicBezier":c("WebBloksAnimatedCreateCubicBezier"),"bk.action.animated.GetCurrentColorValue":c("WebBloksAnimatedGetCurrentColorValue"),"bk.action.animated.GetCurrentDimensionValue":c("WebBloksAnimatedGetCurrentDimensionValue"),"bk.action.animated.GetCurrentValue":c("WebBloksAnimatedGetCurrentValue"),"bk.action.animated.Parallel":c("WebBloksAnimatedParallel"),"bk.action.animated.Start":c("WebBloksAnimatedStart"),"bk.action.array.Append":c("WebBloksArrayAppend"),"bk.action.array.Clone":c("WebBloksArrayClone"),"bk.action.array.Concat":c("WebBloksArrayConcat"),"bk.action.array.Filter":c("WebBloksArrayFilter"),"bk.action.array.Get":c("WebBloksArrayGet"),"bk.action.array.IndexOf":c("WebBloksArrayIndexOf"),"bk.action.array.Length":c("WebBloksArrayLength"),"bk.action.array.Make":c("WebBloksArrayMake"),"bk.action.array.Map":c("WebBloksArrayMap"),"bk.action.array.Put":c("WebBloksArrayPut"),"bk.action.array.PutAndGet":c("WebBloksArrayPutAndGet"),"bk.action.array.Remove":c("WebBloksArrayRemove"),"bk.action.array.RemoveAndGet":c("WebBloksArrayRemoveAndGet"),"bk.action.array.Slice":c("WebBloksArraySlice"),"bk.action.array.SortedArray":c("WebBloksArraySortedArray"),"bk.action.array.Update":c("WebBloksArrayUpdate"),"bk.action.bloks.AddChild":c("WebBloksAddChild"),"bk.action.bloks.AsyncAction":c("WebBloksAsyncAction"),"bk.action.bloks.AsyncActionWithDataManifest":c("WebBloksAsyncActionWithDataManifest"),"bk.action.bloks.AsyncLoadV2":c("WebBloksAsyncLoadV2"),"bk.action.bloks.ChildAtIndex":c("WebBloksChildAtIndex"),"bk.action.bloks.DangerouslyGetTreeFromParseResult":c("WebBloksDangerouslyGetTreeFromParseResult"),"bk.action.bloks.DismissBottomSheet":c("WebBloksDismissBottomSheet"),"bk.action.bloks.DismissKeyboard":c("WebBloksDummy"),"bk.action.bloks.Find":c("WebBloksFindComponentContext"),"bk.action.bloks.FindContainer":c("WebBloksFindComponentContext"),"bk.action.bloks.GetDeserializedEmbeddedPayload":c("WebBloksGetDeserializedEmbeddedPayload"),"bk.action.bloks.GetState":c("WebBloksGetState"),"bk.action.bloks.GetVariable2":c("WebBloksGetVariable2"),"bk.action.bloks.GetVariableWithScope":c("WebBloksGetVariableWithScope"),"bk.action.bloks.IndexOfChild":c("WebBloksIndexOfChild"),"bk.action.bloks.InflateSync":c("WebBloksInflateSync"),"bk.action.bloks.InsertChildrenAfter":c("WebBloksInsertChildrenAfter"),"bk.action.bloks.OpenBottomSheetV2":c("WebBloksOpenBottomSheetV2"),"bk.action.bloks.OpenBottomSheet":c("WebBloksOpenBottomSheet"),"bk.action.dialog.OpenDialog":c("WebBloksOpenDialog"),"bk.action.bloks.OpenScreen":c("WebBloksOpenScreen"),"bk.action.bloks.OpenFullScreenV4":c("WebBloksOpenFullScreenV4"),"bk.action.bloks.ParseEmbedded":c("WebBloksParseEmbedded"),"bk.action.bloks.ParseEmbeddedV2":c("WebBloksParseEmbeddedV2"),"bk.action.bloks.PopBottomSheet":c("WebBloksPopBottomSheet"),"bk.action.bloks.PushBottomSheetV2":c("WebBloksPushBottomSheetV2"),"bk.action.bloks.ReadPandoField":c("WebBloksReadPandoField"),"bk.action.bloks.Reduce":c("WebBloksReduce"),"bk.action.bloks.Reflow":c("WebBloksReflow"),"bk.action.bloks.RemoveChild":c("WebBloksRemoveChild"),"bk.action.bloks.RemoveChildrenBetween":c("WebBloksRemoveChildrenBetween"),"bk.action.bloks.ReplaceChild":c("WebBloksReplaceChild"),"bk.action.bloks.ReplaceChildren":c("WebBloksReplaceChildren"),"bk.action.bloks.ReplaceEmbeddedChildV2":c("WebBloksReplaceEmbeddedChild"),"bk.action.bloks.ReplaceEmbeddedChildren":c("WebBloksReplaceEmbeddedChildren"),"bk.action.bloks.RequestFocus":c("WebBloksRequestFocus"),"bk.action.bloks.WriteGlobalConsistencyStore":c("WebBloksWriteGlobalConsistencyStore"),"bk.action.bloks.WriteLocalState":c("WebBloksWriteLocalState"),"bk.action.bool.And":c("WebBloksBoolAnd"),"bk.action.bool.Const":c("WebBloksBoolConst"),"bk.action.bool.Not":c("WebBloksBoolNot"),"bk.action.bool.Or":c("WebBloksBoolOr"),"bk.action.component.GetAttr":c("WebBloksGetAttr"),"bk.action.component.SetAttr":c("WebBloksSetAttr"),"bk.action.core.Apply":c("WebBloksApply"),"bk.action.core.Default":c("WebBloksDummy"),"bk.action.core.Delay":c("WebBloksDelay"),"bk.action.core.FuncConst":c("WebBloksDummy"),"bk.action.core.GetArg":c("WebBloksDummy"),"bk.action.core.GetTemplateArg":c("WebBloksGetTemplateArg"),"bk.action.core.Match":c("WebBloksMatch"),"bk.action.core.Pattern":c("WebBloksDummy"),"bk.action.core.SetArg":c("WebBloksDummy"),"bk.action.core.TakeLast":c("WebBloksTakeLast"),"bk.action.core.While":c("WebBloksDummy"),"bk.action.f32.Add":c("WebBloksf32NumberAdd"),"bk.action.f32.Const":c("WebBloksConstNumber"),"bk.action.f32.Convert":c("WebBloksf32Convert"),"bk.action.f32.Div":c("WebBloksf32NumberDiv"),"bk.action.f32.Eq":c("WebBloksNumberEq"),"bk.action.f32.Gt":c("WebBloksNumberGt"),"bk.action.f32.Lt":c("WebBloksNumberLt"),"bk.action.f32.Mul":c("WebBloksf32NumberMul"),"bk.action.f32.Pow":c("WebBloksf32NumberPow"),"bk.action.f32.Sub":c("WebBloksf32NumberSub"),"bk.action.function.BindWithArrayV2":c("WebBloksBindWithArrayV2"),"bk.action.i32.Add":c("WebBloksi32NumberAdd"),"bk.action.i32.And":c("WebBloksNumberAnd"),"bk.action.i32.Const":c("WebBloksConstNumber"),"bk.action.i32.Convert":c("WebBloksi32Convert"),"bk.action.i32.Div":c("WebBloksi32NumberDiv"),"bk.action.i32.Eq":c("WebBloksNumberEq"),"bk.action.i32.Gt":c("WebBloksNumberGt"),"bk.action.i32.Lt":c("WebBloksNumberLt"),"bk.action.i32.Mod":c("WebBloksNumberMod"),"bk.action.i32.Mul":c("WebBloksi32NumberMul"),"bk.action.i32.Or":c("WebBloksNumberOr"),"bk.action.i32.Rand":c("WebBloksNumberRand"),"bk.action.i32.Sub":c("WebBloksi32NumberSub"),"bk.action.i64.Add":c("WebBloksi64NumberAdd"),"bk.action.i64.Const":c("WebBloksi64ConstNumber"),"bk.action.i64.Div":c("WebBloksi64NumberDiv"),"bk.action.i64.Eq":c("WebBloksi64NumberEq"),"bk.action.i64.Gt":c("WebBloksi64NumberGt"),"bk.action.i64.Lt":c("WebBloksi64NumberLt"),"bk.action.i64.Mod":c("WebBloksi64NumberMod"),"bk.action.i64.Sub":c("WebBloksi64NumberSub"),"bk.action.internal.Refresh":c("WebBloksRefresh"),"bk.action.io.clipboard.SetString":c("WebBloksClipboardSetString"),"bk.action.io.CurrentTimeMillis":c("WebBloksCurrentTimeMillis"),"bk.action.io.Toast":c("WebBloksToastPrimitive"),"bk.action.logging.LogEvent":c("WebBloksLogEvent"),"bk.action.map.Append":c("WebBloksDummy"),"bk.action.map.Clone":c("WebBloksMapClone"),"bk.action.map.Filter":c("WebBloksMapFilter"),"bk.action.map.Get":c("WebBloksMapGet"),"bk.action.map.Keys":c("WebBloksMapKeys"),"bk.action.map.Make":c("WebBloksMapMake"),"bk.action.map.MakeFlat":c("WebBloksMakeFlat"),"bk.action.map.Remove":c("WebBloksMapRemove"),"bk.action.map.Update":c("WebBloksMapUpdate"),"bk.action.map.Values":c("WebBloksMapValues"),"bk.action.navigation.CloseScreen":c("WebBloksNavigationCloseScreen"),"bk.action.navigation.OpenSendMessage":c("WebBloksOpenSendMessage"),"bk.action.navigation.OpenUrl":c("WebBloksOpenUrl"),"bk.action.navigation.OpenUrlV2":c("WebBloksOpenUrlV2"),"bk.action.navigation.SetNavBar":c("WebBloksDummy"),"bk.action.OpenFRXAction":c("WebBloksOpenFRXAction"),"bk.action.session_store.Get":c("WebBloksSessionStoreGet"),"bk.action.string.AsInteger":c("WebBloksStringAsInteger"),"bk.action.string.AsLong":c("WebBloksStringAsLong"),"bk.action.string.Concat":c("WebBloksStringConcat"),"bk.action.string.Contains":c("WebBloksStringContains"),"bk.action.string.Join":c("WebBloksStringJoin"),"bk.action.string.JsonDecode":c("WebBloksJsonDecode"),"bk.action.string.JsonEncode":c("WebBloksJsonEncode"),"bk.action.string.JsonEncodeV2":c("WebBloksJsonEncode"),"bk.action.string.JsonEncodeV3":c("WebBloksJsonEncodeV3"),"bk.action.string.Length":c("WebBloksStringLength"),"bk.action.bloks.PrependChildren":c("WebBloksPrependChildren"),"bk.action.string.Replace":c("WebBloksStringReplace"),"bk.action.string.Slice":c("WebBloksStringSlice"),"bk.action.string.SplitWithString":c("WebBloksStringSplitWithString"),"bk.action.string.StartsWith":c("WebBloksStringStartsWith"),"bk.action.string.ToLowerCase":c("WebBloksStringToLowerCase"),"bk.action.string.ToUpperCase":c("WebBloksStringToUpperCase"),"bk.action.string.GetURLLastPathComponent":c("WebBloksStringGetURLLastPathComponent"),"bk.action.string.MatchesRegex":c("WebBloksMatchesRegex"),"bk.action.string.ValueOfNumber":c("WebBloksStringValueOfNumber"),"bk.action.toast.ShowToast":c("WebBloksShowToast"),"bk.action.toast.DismissToast":c("WebBloksDismissToast"),"bk.action.t3d.FromArray":c("WebBloksT3DFromArray"),"bk.action.t3d.Rotate":c("WebBloksRotate3D"),"bk.action.t3d.Scale":c("WebBloksScale3D"),"bk.action.t3d.Translate":c("WebBloksTranslate3D"),"bk.action.text.GetText":c("WebBloksTextGetText"),"bk.action.textinput.GetText":c("WebBloksTextInputGetText"),"bk.action.text_input.Unfocus":c("WebBloksTextInputUnfocus"),"bk.action.visibility_context.GetTimeSinceLastImpressionInMS":c("WebBloksVisibilityContextGetTimeSinceLastImpressionInMS"),"bk.action.visibility_context.HasSeenBefore":c("WebBloksVisibilityContextHasSeenBefore"),"bk.action.text.IsTruncated":c("WebBloksTextIsTruncated"),"bk.action.debug.internal.DeviceLog":c("WebBloksDeviceLog"),"bk.action.webbloks.OpenScreenByRoute":c("WebBloksOpenScreenByRoute"),"bk.versioning.bloks.GlobalStateWithInitialLispy":c("WebBloksDummy"),"bk.versioning.bind.Interleaved":c("WebBloksDummy")};e={"bk.components.AnchorIdExtension":d("WebBloksAnchorIDExtensionHandler").WebBloksAnchorIDExtensionHandler,"bk.components.AutomationTestExtension":d("WebBloksAutomationTestExtensionHandler").WebBloksAutomationTestExtensionHandler,"bk.components.VisibilityExtension":d("WebBloksVisibilityExtensionHandler").WebBloksVisibilityExtensionHandler,"bk.components.AccessibilityExtension":d("WebBloksAccessibilityExtensionHandler").WebBloksAccessibilityExtensionHandler,"bk.components.OnMount":d("WebBloksOnMountExtensionHandler").WebBloksOnMountExtensionHandler,"bk.components.RenderLifecycleExtension":d("WebBloksRenderLifecycleExtensionHandler").WebBloksRenderLifecycleExtensionHandler,"bk.components.ViewTransformsExtension":d("WebBloksViewTransformExtensionHandler").WebBloksViewTransformExtensionHandler,"bk.components.FoaTouchExtension":d("WebBloksTouchExtensionHandler").WebBloksTouchExtensionHandler,"bk.components.FoaGestureExtension":d("WebBloksGestureExtensionHandler").WebBloksGestureExtensionHandler};g.COMPONENTS=a;g.ACTIONS=b;g.EXTENSION_HANDLERS=e}),98);
-----
WebBloksAnimationFunctions",[],(function(a,b,c,d,e,f){function g(a,b,c,d){var e=1e-6,f=3*a-3*c+1,g=3*c-6*a,h=3*a,i=3*b-3*d+1,j=3*d-6*b,k=3*b;function l(a){return(3*f*a+2*g)*a+h}function m(a){return((f*a+g)*a+h)*a}function n(a){return((i*a+j)*a+k)*a}function o(a){var b=a,c,d;for(var f=0;f<8;f++){d=m(b)-a;if(Math.abs(d)f){d=m(b)-a;if(Math.abs(d)0?c=b:f=b;b=(c+f)/2}return b}function p(a){return n(o(a))}return p}function a(){return g(0,0,1,1)}f.cubicBezier=g;f.linearInterpolator=a}),66);
-----
WebBloksAnimatedCreateCubicBezier",["WebBloksAnimationFunctions"],(function(a,b,c,d,e,f,g){function a(a,b,c,e,f){return d("WebBloksAnimationFunctions").cubicBezier(b,c,e,f)}g["default"]=a}),98);
-----
KF2Loader",["BlobFactory","CubicBezier","KFColor","KFComponentNames","KFGradient","KFLoaderUtils","KFManifest","KFPath2d","KFPoint","KFPosition","KFScalar","KeyframesCompatPluginsLoader","KeyframesSchema","Promise","flatbuffers","regeneratorRuntime"],(function(a,b,c,d,e,f,g){"use strict";var h=function(a){return a};function e(a,e,f){var g,h,i;return b("regeneratorRuntime").async(function(l){while(1)switch(l.prev=l.next){case 0:g=new(d("flatbuffers").ByteBuffer)(a);h=c("KeyframesSchema").Document.bufferHasIdentifier(g)?c("KeyframesSchema").Document.getRootAsDocument(g):null;E(h);l.next=5;return b("regeneratorRuntime").awrap(j(h,e));case 5:i=l.sent,e.addComponent(k(h,e,f,i),d("KFComponentNames").ROOT),c("KFLoaderUtils").maybeGenerateTrackMatteIDs(e);case 8:case"end":return l.stop()}},null,this)}function i(a){var b=[];for(var c=a.pluginsLength()-1;c>=0;c--)b.unshift(a.plugins(c));return b}function j(a,d){return new(b("Promise"))(function(b,e){if(a==null)return b(null);e=i(a);return e.length===0?b(null):b(c("KeyframesCompatPluginsLoader").fromPluginTable(e,d))})}function k(a,b,e,f){var g=G(a,e),h=a.subdocumentsLength(),i=[];for(var j=0;j-----
CubicBezier",[],(function(a,b,c,d,e,f){var g=.005;a=function(){function a(a,b){this.cx=3*a[0],this.bx=3*(b[0]-a[0])-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*a[1],this.by=3*(b[1]-a[1])-this.cy,this.ay=1-this.cy-this.by}var b=a.prototype;b.sampleCurveX=function(a){return((this.ax*a+this.bx)*a+this.cx)*a};b.solve=function(a){a=this.solveCurveX(a);return((this.ay*a+this.by)*a+this.cy)*a};b.solveCurveX=function(a){var b,c,d,e;for(d=a,c=0;c<8;c++){e=this.sampleCurveX(d)-a;if(Math.abs(e)c)return c;while(be?b=d:c=d;d=(c-b)/2+b}return d};return a}();f["default"]=a}),66);
-----
KeyframesTween",["invariant","CubicBezier","KeyframesPathUtils","KeyframesSchema"],(function(a,b,c,d,e,f,g){"use strict";var h=b("KeyframesPathUtils").interpolateCubic,i=b("KeyframesPathUtils").interpolateLinear,j=b("KeyframesPathUtils").interpolateQuad;(d=b("KeyframesSchema")).Color;d.ColorAnimation;d.ColorArray;d.ColorArrayAnimation;d.Command;var k=d.CommandType;d.Path;d.PathAnimation;d.Point;d.PointAnimation;d.ScalarAnimation;d.ScalarArray;d.ScalarArrayAnimation;var l=d.TweenType;function m(a){return a}function n(a){return{x:a.x(),y:a.y()}}function o(a){return{red:a.red(),green:a.green(),blue:a.blue(),alpha:a.alpha()}}function p(a){var b=a.valuesLength(),c=[];for(var d=0;d=b.keyframes(b.keyframesLength()-1))return 1;else if(a-----
CometProgressRingUtils",["ix"],(function(a,b,c,d,e,f,g,h){"use strict";function a(a,b,c,d){function e(a,b){return 1-3*b+3*a}function f(a,b){return 3*b-6*a}function g(a){return 3*a}function h(a,b,c){return((e(b,c)*a+f(b,c))*a+g(b))*a}function i(a,b,c){return 3*e(b,c)*a*a+2*f(b,c)*a+g(b)}function j(b){var d=b;for(var e=0;e<4;++e){var f=i(d,a,c);if(f===0)return d;var g=h(d,a,c)-b;d-=g/f}return d}return function(e){return a===b&&c===d?e:h(j(e),b,d)}}function b(a,b,c){switch(b){case"12":switch(c){case"dark":switch(a){case"blue":return h("1876411");case"disabled":return h("1876443");case"dark":return h("1876427");case"light":return h("1876427");default:return h("1876427")}case"light":switch(a){case"blue":return h("1876419");case"disabled":return h("1876451");case"dark":return h("1876435");case"light":return h("1876427");default:return h("1876435")}default:return h("1876435")}case"16":switch(c){case"dark":switch(a){case"blue":return h("1876412");case"disabled":return h("1876444");case"dark":return h("1876428");case"light":return h("1876428");default:return h("1876428")}case"light":switch(a){case"blue":return h("1876420");case"disabled":return h("1876452");case"dark":return h("1876436");case"light":return h("1876428");default:return h("1876436")}default:return h("1876436")}case"20":switch(c){case"dark":switch(a){case"blue":return h("1876413");case"disabled":return h("1876445");case"dark":return h("1876429");case"light":return h("1876429");default:return h("1876429")}case"light":switch(a){case"blue":return h("1876421");case"disabled":return h("1876453");case"dark":return h("1876437");case"light":return h("1876429");default:return h("1876437")}default:return h("1876437")}case"24":switch(c){case"dark":switch(a){case"blue":return h("1876414");case"disabled":return h("1876446");case"dark":return h("1876430");case"light":return h("1876430");default:return h("1876430")}case"light":switch(a){case"blue":return h("1876422");case"disabled":return h("1876454");case"dark":return h("1876438");case"light":return h("1876430");default:return h("1876438")}default:return h("1876438")}case"32":switch(c){case"dark":switch(a){case"blue":return h("1876415");case"disabled":return h("1876447");case"dark":return h("1876431");case"light":return h("1876431");default:return h("1876431")}case"light":switch(a){case"blue":return h("1876423");case"disabled":return h("1876455");case"dark":return h("1876439");case"light":return h("1876431");default:return h("1876439")}default:return h("1876439")}case"48":switch(c){case"dark":switch(a){case"blue":return h("1876416");case"disabled":return h("1876448");case"dark":return h("1876432");case"light":return h("1876432");default:return h("1876432")}case"light":switch(a){case"blue":return h("1876424");case"disabled":return h("1876456");case"dark":return h("1876440");case"light":return h("1876432");default:return h("1876440")}default:return h("1876440")}case"60":switch(c){case"dark":switch(a){case"blue":return h("1940508");case"disabled":return h("1940512");case"dark":return h("1940510");case"light":return h("1940510");default:return h("1940510")}case"light":switch(a){case"blue":return h("1940509");case"disabled":return h("1940513");case"dark":return h("1940511");case"light":return h("1940510");default:return h("1940511")}default:return h("1940511")}case"72":switch(c){case"dark":switch(a){case"blue":return h("1876418");case"disabled":return h("1876450");case"dark":return h("1876434");case"light":return h("1876434");default:return h("1876434")}case"light":switch(a){case"blue":return h("1876426");case"disabled":return h("1876458");case"dark":return h("1876442");case"light":return h("1876434");default:return h("1876442")}default:return h("1876442")}default:return h("1876439")}}function c(a){switch(a){case"dark":return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"};case"light":return{backgroundColor:"var(--progress-ring-on-media-background)",foregroundColor:"var(--progress-ring-on-media-foreground)"};case"blue":return{backgroundColor:"var(--progress-ring-blue-background)",foregroundColor:"var(--progress-ring-blue-foreground)"};case"disabled":return{backgroundColor:"var(--progress-ring-disabled-background)",foregroundColor:"var(--progress-ring-disabled-foreground)"};default:return{backgroundColor:"var(--progress-ring-neutral-background)",foregroundColor:"var(--progress-ring-neutral-foreground)"}}}g.getCubicBezierPercentageFunc=a;g.getRingGifUrl=b;g.getRingColor=c}),98);
-----

Previous searches: